import { copyFileSync, readFileSync, writeFileSync } from 'node:fs'; import { resolve } from 'node:path'; const packageDir = resolve(import.meta.dirname, '../../../packages/decodal-wasm'); copyFileSync(resolve(packageDir, '../../LICENSE-MIT'), resolve(packageDir, 'LICENSE-MIT')); copyFileSync(resolve(packageDir, '../../LICENSE-APACHE'), resolve(packageDir, 'LICENSE-APACHE')); const declarationPath = resolve(packageDir, 'decodal_wasm.d.ts'); let declarations = readFileSync(declarationPath, 'utf8'); const valueTypes = ` /** A concrete JavaScript value or Decodal range descriptor. */ export type DecodalValue = | string | number | boolean | DecodalValue[] | { [field: string]: DecodalValue } | DecodalPrimitiveDescriptor | DecodalUnknownDescriptor | DecodalArrayDescriptor | DecodalMapDescriptor | DecodalObjectDescriptor | DecodalRangeDescriptor; export type DecodalPrimitiveType = 'String' | 'Int' | 'Float' | 'Bool'; export type DecodalConstraint = | { kind: 'unknown' } | { 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?: DecodalValue; } export interface DecodalUnknownDescriptor { $decodal: 'Unknown'; constraints?: DecodalConstraint[]; default?: DecodalValue; } export interface DecodalArrayDescriptor { $decodal: 'Array'; item: DecodalValue; constraints?: DecodalConstraint[]; default?: DecodalValue; } export interface DecodalMapDescriptor { $decodal: 'Map'; value: DecodalValue; constraints?: DecodalConstraint[]; default?: DecodalValue; } export interface DecodalObjectDescriptor { $decodal: 'Object'; fields: Record; rest: DecodalValue; } export interface DecodalRangeDescriptor { $decodal: 'Range'; constraints?: DecodalConstraint[]; default?: DecodalValue; } export type DecodalLoadedImport = | { kind: 'source'; key: string; name?: string; source: string } | { kind: 'value'; key: string; value: DecodalValue }; export type DecodalImportCandidate = | string | { specifier: string; detail?: string }; /** Host-owned environment shared by evaluation and language tooling. */ export interface DecodalEnvironment { globals?: Record; /** 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${valueTypes}`); } 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', ); const packageJsonPath = resolve(packageDir, 'package.json'); const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); packageJson.version = '0.3.0'; packageJson.files = [ 'README.md', 'LICENSE-MIT', 'LICENSE-APACHE', 'decodal_wasm_bg.wasm', 'decodal_wasm.js', 'decodal_wasm.d.ts', ]; packageJson.exports = { '.': { types: './decodal_wasm.d.ts', import: './decodal_wasm.js', }, './decodal_wasm_bg.wasm': './decodal_wasm_bg.wasm', './package.json': './package.json', }; writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); writeFileSync( resolve(packageDir, 'README.md'), `# decodal-wasm WebAssembly runtime package for Decodal. 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. ## Install \`\`\`sh npm install decodal-wasm \`\`\` JSR: \`\`\`sh deno add jsr:@hare/decodal-wasm \`\`\` ## Usage \`\`\`js import init, { DecodalLanguageService } from 'decodal-wasm'; await init(); const files = { 'main.dcdl': 'let schema = import "./schema.dcdl"; in schema.Server', 'schema.dcdl': 'Server = { port = 8080; } as App.Server;', }; 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)); \`\`\` \`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 values. Use \`{ $decodal: 'String' | 'Int' | 'Float' | 'Bool' }\` for primitive ranges, \`{ $decodal: 'Unknown' }\` for the top range, \`{ $decodal: 'Array', item: value }\` for array ranges, and \`{ $decodal: 'Map', value: value }\` for associative-array ranges. Primitive, unknown, array, and map descriptors also accept \`constraints\` and \`default\`. Use \`{ $decodal: 'Range', constraints, default }\` when constructing a general range directly. An object with named fields and an open rest range uses \`{ $decodal: 'Object', fields, rest }\`. The \`fields\` property is a record of values and \`rest\` is the range applied to every other field. Methods return JSON strings so callers can handle diagnostics and successful results without depending on Rust data structures. `, ); writeFileSync( resolve(packageDir, 'jsr.json'), JSON.stringify( { name: '@hare/decodal-wasm', version: '0.3.0', // JSR's publisher currently accepts a single recognized identifier. // npm package metadata remains dual-licensed and both files are included. license: 'MIT', exports: './mod.ts', publish: { include: [ 'README.md', 'LICENSE-MIT', 'LICENSE-APACHE', 'mod.ts', 'decodal_wasm.js', 'decodal_wasm.d.ts', 'decodal_wasm_bg.wasm', 'package.json', ], }, }, null, 2, ) + '\n', );