Share host-aware completion across runtimes
This commit is contained in:
@@ -3,6 +3,73 @@ import { resolve } from 'node:path';
|
||||
|
||||
const packageDir = resolve(import.meta.dirname, '../../../packages/decodal-wasm');
|
||||
|
||||
const declarationPath = resolve(packageDir, 'decodal_wasm.d.ts');
|
||||
let declarations = readFileSync(declarationPath, 'utf8');
|
||||
const hostTypes = `
|
||||
/** A concrete JavaScript value or Decodal schema descriptor supplied by the host. */
|
||||
export type DecodalHostValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| DecodalHostValue[]
|
||||
| { [field: string]: DecodalHostValue }
|
||||
| DecodalPrimitiveDescriptor
|
||||
| DecodalArrayDescriptor
|
||||
| DecodalAbstractDescriptor;
|
||||
|
||||
export type DecodalPrimitiveType = 'String' | 'Int' | 'Float' | 'Bool';
|
||||
|
||||
export type DecodalConstraint =
|
||||
| { kind: 'type'; value: DecodalPrimitiveType }
|
||||
| { kind: 'compare'; op: '>' | '>=' | '<' | '<='; value: string | number | boolean }
|
||||
| { kind: 'regex'; value: string }
|
||||
| { kind: 'predicate'; value: string };
|
||||
|
||||
export interface DecodalPrimitiveDescriptor {
|
||||
$decodal: DecodalPrimitiveType;
|
||||
constraints?: DecodalConstraint[];
|
||||
default?: DecodalHostValue;
|
||||
}
|
||||
|
||||
export interface DecodalArrayDescriptor {
|
||||
$decodal: 'Array';
|
||||
item: DecodalHostValue;
|
||||
constraints?: DecodalConstraint[];
|
||||
default?: DecodalHostValue;
|
||||
}
|
||||
|
||||
export interface DecodalAbstractDescriptor {
|
||||
$decodal: 'Abstract';
|
||||
constraints?: DecodalConstraint[];
|
||||
default?: DecodalHostValue;
|
||||
}
|
||||
|
||||
export type DecodalLoadedImport =
|
||||
| { kind: 'source'; key: string; name?: string; source: string }
|
||||
| { kind: 'value'; key: string; value: DecodalHostValue };
|
||||
|
||||
export type DecodalImportCandidate =
|
||||
| string
|
||||
| { specifier: string; detail?: string };
|
||||
|
||||
/** Host-owned environment shared by evaluation and language tooling. */
|
||||
export interface DecodalEnvironment {
|
||||
globals?: Record<string, DecodalHostValue>;
|
||||
/** Synchronous import callback. Preload or cache asynchronous resources first. */
|
||||
loadImport?: (currentKey: string | null, specifier: string) => DecodalLoadedImport;
|
||||
/** Synchronous import completion callback. */
|
||||
completeImport?: (currentKey: string | null, prefix: string) => DecodalImportCandidate[];
|
||||
}
|
||||
`;
|
||||
if (!declarations.includes('export interface DecodalEnvironment')) {
|
||||
declarations = declarations.replace('/* eslint-disable */\n', `/* eslint-disable */\n${hostTypes}`);
|
||||
}
|
||||
declarations = declarations.replace(
|
||||
'constructor(options: any);',
|
||||
'constructor(options?: DecodalEnvironment);',
|
||||
);
|
||||
writeFileSync(declarationPath, declarations);
|
||||
|
||||
writeFileSync(
|
||||
resolve(packageDir, '.gitignore'),
|
||||
'# wasm-pack output is committed for the browser runtime package.\n',
|
||||
@@ -19,7 +86,7 @@ writeFileSync(
|
||||
|
||||
WebAssembly runtime package for Decodal.
|
||||
|
||||
This package exposes the Decodal evaluator to browsers and other JavaScript runtimes that can load WebAssembly modules.
|
||||
This package exposes the Decodal evaluator and shared language service to browsers and other JavaScript runtimes that can load WebAssembly modules.
|
||||
It is generated from the Rust crate in \`crates/decodal-wasm\` with \`wasm-pack\` and is used by the official playground.
|
||||
|
||||
Use \`decodal-codemirror\` separately when you need CodeMirror 6 language support.
|
||||
@@ -39,18 +106,51 @@ deno add jsr:@hare/decodal-wasm
|
||||
## Usage
|
||||
|
||||
\`\`\`js
|
||||
import init, { evaluateProject } from 'decodal-wasm';
|
||||
import init, { DecodalLanguageService } from 'decodal-wasm';
|
||||
|
||||
await init();
|
||||
|
||||
const result = evaluateProject('main.dcdl', JSON.stringify({
|
||||
'main.dcdl': 'Server = { port = Int default 8080; };',
|
||||
}));
|
||||
const files = {
|
||||
'main.dcdl': 'let schema = import "./schema.dcdl"; in schema.Server',
|
||||
'schema.dcdl': 'Server = App.Server & { port = 8080; };',
|
||||
};
|
||||
|
||||
const service = new DecodalLanguageService({
|
||||
globals: {
|
||||
App: {
|
||||
Server: {
|
||||
port: { $decodal: 'Int' },
|
||||
},
|
||||
},
|
||||
},
|
||||
loadImport(_currentKey, specifier) {
|
||||
const key = specifier.startsWith('./') ? specifier.slice(2) : specifier;
|
||||
return { kind: 'source', key, name: key, source: files[key] };
|
||||
},
|
||||
completeImport() {
|
||||
return ['./schema.dcdl'];
|
||||
},
|
||||
});
|
||||
|
||||
const result = service.evaluate('main.dcdl', 'main.dcdl', files['main.dcdl']);
|
||||
|
||||
console.log(JSON.parse(result));
|
||||
|
||||
const completion = service.complete(
|
||||
'main.dcdl',
|
||||
'App.Server.po',
|
||||
'App.Server.po'.length,
|
||||
false,
|
||||
);
|
||||
|
||||
console.log(JSON.parse(completion));
|
||||
\`\`\`
|
||||
|
||||
The exported functions return JSON strings so callers can handle diagnostics and successful results without depending on Rust data structures.
|
||||
\`globals\`, \`loadImport\`, and \`completeImport\` are host-owned. The package does not assume a filesystem or virtual project model. Import callbacks are synchronous; preload or cache remote content before evaluation.
|
||||
|
||||
Plain strings, numbers, booleans, arrays, and objects are concrete host values. Use \`{ $decodal: 'String' | 'Int' | 'Float' | 'Bool' }\` for primitive schemas and \`{ $decodal: 'Array', item: value }\` for array schemas. Descriptors also accept \`constraints\` and \`default\`.
|
||||
|
||||
Methods return JSON strings so callers can handle diagnostics and successful results without depending on Rust data structures.
|
||||
`,
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user