@gmb/bitmark-parser 7.1.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +155 -23
  2. package/config/bitmark-json.tsp +330 -0
  3. package/config/bitmark.json +83394 -0
  4. package/dist/browser/bitmark-parser.min.js +6 -4
  5. package/dist/browser/bitmark-parser.min.js.map +1 -1
  6. package/dist/browser/cjs/index.cjs +140 -72
  7. package/dist/browser/cjs/index.cjs.map +1 -1
  8. package/dist/browser/cjs/index.d.cts +6283 -6798
  9. package/dist/browser/esm/index.d.ts +6283 -6798
  10. package/dist/browser/esm/index.js +139 -72
  11. package/dist/browser/esm/index.js.map +1 -1
  12. package/dist/browser/esm/worker-entry.js +86 -57
  13. package/dist/browser/esm/worker-entry.js.map +1 -1
  14. package/dist/browser/wasm/bitmark_browser_full_wasm_bg.wasm +0 -0
  15. package/dist/browser/wasm/bitmark_json_wasm_bg.wasm +0 -0
  16. package/dist/browser/wasm/bitmark_wasm_bg.wasm +0 -0
  17. package/dist/index.cjs +76 -29
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +6283 -6798
  20. package/dist/index.d.ts +6283 -6798
  21. package/dist/index.js +75 -29
  22. package/dist/index.js.map +1 -1
  23. package/dist/legacy.cjs +4 -3
  24. package/dist/legacy.cjs.map +1 -1
  25. package/dist/legacy.d.cts +2 -1
  26. package/dist/legacy.d.ts +2 -1
  27. package/dist/legacy.js +4 -3
  28. package/dist/legacy.js.map +1 -1
  29. package/dist/worker-entry.cjs +23 -15
  30. package/dist/worker-entry.cjs.map +1 -1
  31. package/package.json +11 -7
  32. package/schema/bitmark.schema.json +10427 -14036
  33. package/wasm/bitmark_wasm.d.ts +12 -3
  34. package/wasm/bitmark_wasm.js +34 -15
  35. package/wasm/bitmark_wasm_bg.wasm +0 -0
  36. package/wasm/bitmark_wasm_bg.wasm.d.ts +2 -2
  37. package/wasm/package.json +1 -1
  38. package/wasm-bitmark-json/bitmark_json_wasm.d.ts +12 -3
  39. package/wasm-bitmark-json/bitmark_json_wasm.js +34 -15
  40. package/wasm-bitmark-json/bitmark_json_wasm_bg.wasm +0 -0
  41. package/wasm-bitmark-json/bitmark_json_wasm_bg.wasm.d.ts +2 -2
  42. package/wasm-bitmark-json/package.json +1 -1
  43. package/wasm-browser-full/bitmark_browser_full_wasm.d.ts +12 -3
  44. package/wasm-browser-full/bitmark_browser_full_wasm.js +34 -15
  45. package/wasm-browser-full/bitmark_browser_full_wasm_bg.wasm +0 -0
  46. package/wasm-browser-full/bitmark_browser_full_wasm_bg.wasm.d.ts +2 -2
  47. package/wasm-browser-full/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../node_modules/tsup/assets/cjs_shims.js","../src/core/facade.ts","../src/node/worker-entry.ts"],"names":["require","createRequire","workerData","parentPort"],"mappings":";;;;;;AAKA,IAAM,gBAAA,GAAmB,MACvB,OAAO,QAAA,KAAa,WAAA,GAChB,IAAI,GAAA,CAAI,CAAA,KAAA,EAAQ,UAAU,CAAA,CAAE,CAAA,CAAE,IAAA,GAC7B,QAAA,CAAS,aAAA,IAAiB,QAAA,CAAS,aAAA,CAAc,OAAA,CAAQ,WAAA,EAAY,KAAM,QAAA,GAC1E,QAAA,CAAS,aAAA,CAAc,GAAA,GACvB,IAAI,GAAA,CAAI,SAAA,EAAW,QAAA,CAAS,OAAO,CAAA,CAAE,IAAA;AAEtC,IAAM,gCAAgC,gBAAA,EAAiB;;;ACevD,IAAM,oBAAA,GAAgC,MAAA;;;ACf7C,IAAMA,QAAAA,GAAUC,uBAAc,aAAe,CAAA;AAI7C,IAAM,YAAA,GAAwC;AAAA,EAC5C,IAAA,EAAM,yBAAA;AAAA,EACN,cAAA,EAAgB,mDAAA;AAAA,EAChB,cAAA,EAAgB;AAClB,CAAA;AAEA,IAAM,OAAA,GAAYC,2BAAkD,OAAA,IAClE,oBAAA;AACF,IAAM,IAAA,GAAOF,QAAAA,CAAQ,YAAA,CAAa,OAAO,CAAC,CAAA;AAkB1C,SAAS,WAAW,GAAA,EAAqB;AACvC,EAAA,MAAM,CAAA,GAAI,IAAI,IAAA,EAAK;AACnB,EAAA,OAAO,CAAA,CAAE,UAAA,CAAW,GAAG,CAAA,IAAK,CAAA,CAAE,QAAA,CAAS,GAAG,CAAA,GAAI,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,GAAA;AACjE;AAEAG,yBAAA,EAAY,EAAA,CAAG,SAAA,EAAW,CAAC,GAAA,KAAoB;AAC7C,EAAA,MAAM,EAAE,EAAA,EAAI,GAAA,EAAI,GAAI,GAAA;AACpB,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,IAAA,CAAK,SAAA;AAAA,MACf,GAAA,CAAI,MAAA;AAAA,MACJ,GAAA,CAAI,WAAA;AAAA,MACJ,GAAA,CAAI,YAAA;AAAA,MACJ,GAAA,CAAI,IAAA;AAAA,MACJ,GAAA,CAAI,SAAA;AAAA,MACJ,KAAA;AAAA,MACA,CAAA;AAAA,MACA,GAAA,CAAI;AAAA,KACN;AACA,IAAA,MAAM,UAAU,GAAA,CAAI,YAAA,KAAiB,MAAA,GAAS,UAAA,CAAW,GAAG,CAAA,GAAI,GAAA;AAChE,IAAAA,yBAAA,EAAY,YAAY,EAAE,EAAA,EAAI,EAAA,EAAI,IAAA,EAAM,SAAS,CAAA;AAAA,EACnD,SAAS,CAAA,EAAG;AAGV,IAAAA,yBAAA,EAAY,WAAA,CAAY,EAAE,EAAA,EAAI,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAA,CAAO,CAAC,GAAG,CAAA;AAAA,EAC9F;AACF,CAAC,CAAA","file":"worker-entry.cjs","sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","// @zen-component: TS-VariantFacade\n//\n// Runtime variant selection (PLAN-166). The package ships one API surface\n// backed by a mutable reference to the ACTIVE wasm module; `init({ feature })`\n// selects which wasm variant backs it, and a later re-init swaps the module\n// atomically between calls (every wasm call is stateless string → string).\n// Platform entries (Node / browser) own the loading; this module owns the\n// reference, the feature vocabulary, and the error types.\n\n/**\n * A deployable wasm feature set (PLAN-174).\n *\n * - `full` — everything, including the `info` META layer (tag descriptions,\n * group provenance, raw mappingKeys patterns). Native-CLI parity.\n * - `browser-full` — the same conversion capabilities as `full`, minus the\n * meta strings the browser should not pay to download.\n * - `bitmark-json` — bitmark ↔ JSON only.\n *\n * Size gating follows the DELIVERY CHANNEL, not the compilation target: a Node\n * backend loading wasm from disk has native-CLI requirements, while any entry\n * can be bundled for the browser. So every variant is buildable for both\n * targets and richness is always chosen explicitly. The per-entry defaults\n * below are conveniences; the contract is \"you get the variant you selected\".\n */\nexport type Feature = \"full\" | \"browser-full\" | \"bitmark-json\";\n\n/** Default variant for the Node entry — disk-loaded wasm, no download budget. */\nexport const NODE_DEFAULT_FEATURE: Feature = \"full\";\n\n/** Default variant for the browser entry — download latency is the budget. */\nexport const BROWSER_DEFAULT_FEATURE: Feature = \"browser-full\";\n\n/**\n * The wasm surface the shared wrappers drive. The optional members are the\n * capabilities a lean variant compiles out (calling their wrapper there\n * throws {@link UnsupportedFeatureError}).\n *\n * Every fallible member THROWS a JavaScript `Error` on failure (PLAN-192 D1);\n * its `message` is the core's error contract, `<kind> at <path>: <message>`,\n * with `<kind>` a stable kebab-case identifier. A returned string is always a\n * result — there is no in-band error prefix to test for.\n */\nexport interface WasmModule {\n convert(\n input: string,\n inputFormat: string,\n outputFormat: string,\n mode: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n includeUnknownProperties: boolean,\n mappingReport: boolean,\n /** `semantic-tokens` output only: `\"utf-16\"` | `\"utf-8\"`, `\"\"` = default (PLAN-193). */\n positionEncoding: string,\n /** `semantic-tokens` output only: `\"lsp\"` | `\"absolute\"`, `\"\"` = default. */\n tokensLayout: string,\n ): string;\n canonicalize(\n input: string,\n inputFormat: string,\n mode: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n ): string;\n transform(\n input: string,\n inputFormat: string,\n outputFormat: string,\n mode: string,\n patchJson: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n ): string;\n info(\n infoType: string,\n format: string,\n bit: string,\n pretty: boolean,\n indent: number,\n includeDeprecated: boolean,\n onlyDeprecated: boolean,\n full: boolean,\n language: string,\n ): string;\n /** Throws when the data is not valid for `dataType`. */\n register(dataType: string, data: string): void;\n count_bits(input: string): string;\n split_bits(input: string): string;\n split_json_bits(input: string): string;\n is_bitmark(input: string): boolean;\n prettify(json: string, indent: number): string;\n convert_text(input: string, direction: string, format: string, location: string): string;\n breakscape_text(input: string, format: string, location: string): string;\n unbreakscape_text(input: string, format: string, location: string): string;\n /**\n * Resolve a patch document against a document into ordered items\n * (PLAN-179), splitting it under `inputFormat` (`\"auto\"` | `\"bitmark\"` |\n * `\"json\"`) — the selector the caller already resolved, never a re-sniff.\n */\n resolve_patch_document(input: string, inputFormat: string, patchJson: string): string;\n /**\n * Present only where this build can render `info` as TEXT (PLAN-192 D6) —\n * the same \"presence is the capability\" signal as `diff`, so no caller\n * has to test for a variant name. Absent on `bitmark-json`, where `info`\n * serves JSON only.\n */\n info_text_supported?(): boolean;\n /** Completion at a position (PLAN-196); absent on a variant built without `editor`. */\n complete?(\n input: string,\n line: number,\n character: number,\n positionEncoding: string,\n includeDeprecated: boolean,\n triggerCharacter: string,\n ): string;\n /** One completion item with its documentation (PLAN-202); absent on a variant built without `editor`. */\n resolve?(\n input: string,\n line: number,\n character: number,\n positionEncoding: string,\n includeDeprecated: boolean,\n label: string,\n kind: number,\n ): string;\n /** Hover at a position (PLAN-196); absent on a variant built without `editor`. */\n hover?(input: string, line: number, character: number, positionEncoding: string): string;\n /** Semantic diff (PLAN-179); absent on a variant built without `diff`. */\n diff?(\n a: string,\n b: string,\n inputFormat: string,\n outputFormat: string,\n context: number,\n locate: boolean,\n similarity: number,\n bboxTolerance: number,\n spacesAroundValues: number,\n ): string;\n}\n\n/** Options accepted by `init` / `initSync` on every platform entry. */\nexport interface InitOptions {\n /** Which wasm variant to load. Default: `\"full\"`. */\n feature?: Feature;\n}\n\n/** A call arrived before any wasm module was initialised (browser only). */\nexport class NotInitializedError extends Error {\n constructor() {\n super(\"wasm not initialised: call init() (or init({ feature: … })) first\");\n this.name = \"NotInitializedError\";\n }\n}\n\n/** `register` rejected the supplied data. */\nexport class RegisterError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"RegisterError\";\n }\n}\n\n/** A call needs a capability the ACTIVE wasm variant compiled out. */\nexport class UnsupportedFeatureError extends Error {\n constructor(what: string, active: Feature | undefined) {\n super(\n `${what} is not available in the loaded wasm module` +\n `${active ? ` (feature: \"${active}\")` : \"\"}; init({ feature: \"full\" }) to load it`,\n );\n this.name = \"UnsupportedFeatureError\";\n }\n}\n\nlet active: WasmModule | undefined;\nlet activeFeature: Feature | undefined;\nlet lazyLoader: (() => void) | undefined;\n\n// Data registered through `register`, kept so it can be REPLAYED onto each\n// newly activated module. Each variant is a separate wasm instance with its\n// own memory, so a registration made against one is invisible to the next —\n// and PLAN-166's promise is that swapping variants is invisible to the\n// caller. Keyed by type, because `register` replaces per type.\nconst registrations = new Map<string, string>();\n\n/**\n * The active wasm module. On Node the entry installs a lazy loader, so the\n * first call without an explicit `init` loads the default (`full`) variant\n * synchronously; in the browser an uninitialised call throws\n * {@link NotInitializedError} (wasm instantiation is async there).\n */\nexport function getActive(): WasmModule {\n if (!active && lazyLoader) lazyLoader();\n if (!active) throw new NotInitializedError();\n return active;\n}\n\n/** Swap the active module — the single atomic reference assignment. */\nexport function setActive(feature: Feature, module: WasmModule): void {\n // Replay first: a module that cannot take the registered data must not\n // become active half-configured. The data was accepted once already, so a\n // failure here is a bug worth surfacing rather than swallowing.\n for (const [type, data] of registrations) {\n try {\n module.register(type, data);\n } catch (e) {\n throw new RegisterError(e instanceof Error ? e.message : String(e));\n }\n }\n active = module;\n activeFeature = feature;\n}\n\n/**\n * Remember what `register` accepted, so it survives an `init` swap. Called\n * only after the ACTIVE module has accepted the same data.\n */\nexport function rememberRegistration(type: string, data: string): void {\n registrations.set(type, data);\n}\n\n/**\n * Install the Node lazy default loader (runs on the first call when no\n * explicit `init` happened; must call {@link setActive}).\n */\nexport function setLazyLoader(loader: () => void): void {\n lazyLoader = loader;\n}\n\n/** The active wasm feature, or `undefined` before the first init. */\nexport function variant(): Feature | undefined {\n return activeFeature;\n}\n","// @zen-component: TS-WorkerEntry\n//\n// Node worker_threads entry for the parallel transform pool. Each worker loads\n// its own WASM instance — the VARIANT the spawning pool passes via workerData\n// (PLAN-166) — and computes one bit at a time from a data-only job. Hooks (JS\n// closures) never reach here — they run on the main thread.\n\nimport { createRequire } from \"node:module\";\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport type { BitJob } from \"../core/hooked-transform.js\";\nimport { NODE_DEFAULT_FEATURE, type Feature } from \"../core/facade.js\";\n\nconst require = createRequire(import.meta.url);\n\n// Bundled to dist/worker-entry.cjs; the wasm glue dirs sit at the package\n// root. Mirrors the entry's variant map (facade.ts).\nconst VARIANT_GLUE: Record<Feature, string> = {\n full: \"../wasm/bitmark_wasm.js\",\n \"browser-full\": \"../wasm-browser-full/bitmark_browser_full_wasm.js\",\n \"bitmark-json\": \"../wasm-bitmark-json/bitmark_json_wasm.js\",\n};\n\nconst feature = ((workerData as { feature?: Feature } | undefined)?.feature ??\n NODE_DEFAULT_FEATURE) as Feature;\nconst wasm = require(VARIANT_GLUE[feature]) as {\n transform(\n input: string,\n inputFormat: string,\n outputFormat: string,\n mode: string,\n patchJson: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n ): string;\n};\n\ninterface JobMessage {\n id: number;\n job: BitJob;\n}\n\nfunction stripArray(doc: string): string {\n const t = doc.trim();\n return t.startsWith(\"[\") && t.endsWith(\"]\") ? t.slice(1, -1) : doc;\n}\n\nparentPort?.on(\"message\", (msg: JobMessage) => {\n const { id, job } = msg;\n try {\n const out = wasm.transform(\n job.source,\n job.inputFormat,\n job.outputFormat,\n job.mode,\n job.patchJson,\n false,\n 2,\n job.spacesAroundValues,\n );\n const element = job.outputFormat === \"json\" ? stripArray(out) : out;\n parentPort?.postMessage({ id, ok: true, element });\n } catch (e) {\n // The wasm export throws on failure (PLAN-192 D1); the pool turns this\n // message back into a rejected promise on the main thread.\n parentPort?.postMessage({ id, ok: false, error: e instanceof Error ? e.message : String(e) });\n }\n});\n"]}
1
+ {"version":3,"sources":["../../../node_modules/tsup/assets/cjs_shims.js","../src/core/facade.ts","../src/core/hooked-transform.ts","../src/node/worker-entry.ts"],"names":["require","createRequire","workerData","parentPort"],"mappings":";;;;;;AAKA,IAAM,gBAAA,GAAmB,MACvB,OAAO,QAAA,KAAa,WAAA,GAChB,IAAI,GAAA,CAAI,CAAA,KAAA,EAAQ,UAAU,CAAA,CAAE,CAAA,CAAE,IAAA,GAC7B,QAAA,CAAS,aAAA,IAAiB,QAAA,CAAS,aAAA,CAAc,OAAA,CAAQ,WAAA,EAAY,KAAM,QAAA,GAC1E,QAAA,CAAS,aAAA,CAAc,GAAA,GACvB,IAAI,GAAA,CAAI,SAAA,EAAW,QAAA,CAAS,OAAO,CAAA,CAAE,IAAA;AAEtC,IAAM,gCAAgC,gBAAA,EAAiB;;;ACiBvD,IAAM,oBAAA,GAAgC,MAAA;;;ACqCtC,SAAS,WAAW,GAAA,EAAqB;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAI,IAAA,EAAK;AACnB,EAAA,OAAO,CAAA,CAAE,UAAA,CAAW,GAAG,CAAA,IAAK,CAAA,CAAE,QAAA,CAAS,GAAG,CAAA,GAAI,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,GAAA;AACjE;AA2JO,SAAS,SAAA,CAAU,cAAsB,YAAA,EAAuC;AACrF,EAAA,MAAM,EAAE,IAAA,EAAM,WAAA,EAAY,GAAI,IAAA,CAAK,MAAM,YAAY,CAAA;AACrD,EAAA,OAAO,EAAE,SAAS,YAAA,KAAiB,MAAA,GAAS,WAAW,IAAI,CAAA,GAAI,MAAM,WAAA,EAAY;AACnF;;;ACvNA,IAAMA,QAAAA,GAAUC,uBAAc,aAAe,CAAA;AAI7C,IAAM,YAAA,GAAwC;AAAA,EAC5C,IAAA,EAAM,yBAAA;AAAA,EACN,cAAA,EAAgB,mDAAA;AAAA,EAChB,cAAA,EAAgB;AAClB,CAAA;AAEA,IAAM,OAAA,GAAYC,2BAAkD,OAAA,IAClE,oBAAA;AACF,IAAM,IAAA,GAAOF,QAAAA,CAAQ,YAAA,CAAa,OAAO,CAAC,CAAA;AAkB1CG,yBAAA,EAAY,EAAA,CAAG,SAAA,EAAW,CAAC,GAAA,KAAoB;AAC7C,EAAA,MAAM,EAAE,EAAA,EAAI,GAAA,EAAI,GAAI,GAAA;AACpB,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,OAAA,EAAS,WAAA,EAAY,GAAI,SAAA;AAAA,MAC/B,IAAA,CAAK,SAAA;AAAA,QACH,GAAA,CAAI,MAAA;AAAA,QACJ,GAAA,CAAI,WAAA;AAAA,QACJ,GAAA,CAAI,YAAA;AAAA,QACJ,GAAA,CAAI,IAAA;AAAA,QACJ,GAAA,CAAI,SAAA;AAAA,QACJ,KAAA;AAAA,QACA,CAAA;AAAA,QACA,GAAA,CAAI;AAAA,OACN;AAAA,MACA,GAAA,CAAI;AAAA,KACN;AACA,IAAAA,yBAAA,EAAY,YAAY,EAAE,EAAA,EAAI,IAAI,IAAA,EAAM,OAAA,EAAS,aAAa,CAAA;AAAA,EAChE,SAAS,CAAA,EAAG;AAGV,IAAAA,yBAAA,EAAY,WAAA,CAAY,EAAE,EAAA,EAAI,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAA,CAAO,CAAC,GAAG,CAAA;AAAA,EAC9F;AACF,CAAC,CAAA","file":"worker-entry.cjs","sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","// @zen-component: TS-VariantFacade\n//\n// Runtime variant selection (PLAN-166). The package ships one API surface\n// backed by a mutable reference to the ACTIVE wasm module; `init({ feature })`\n// selects which wasm variant backs it, and a later re-init swaps the module\n// atomically between calls (every wasm call is stateless string → string).\n// Platform entries (Node / browser) own the loading; this module owns the\n// reference, the feature vocabulary, and the error types.\n\nimport type { PatchDiagnostic } from \"../types.js\";\n\n/**\n * A deployable wasm feature set (PLAN-174).\n *\n * - `full` — everything, including the `info` META layer (tag descriptions,\n * group provenance, raw mappingKeys patterns). Native-CLI parity.\n * - `browser-full` — the same conversion capabilities as `full`, minus the\n * meta strings the browser should not pay to download.\n * - `bitmark-json` — bitmark ↔ JSON only.\n *\n * Size gating follows the DELIVERY CHANNEL, not the compilation target: a Node\n * backend loading wasm from disk has native-CLI requirements, while any entry\n * can be bundled for the browser. So every variant is buildable for both\n * targets and richness is always chosen explicitly. The per-entry defaults\n * below are conveniences; the contract is \"you get the variant you selected\".\n */\nexport type Feature = \"full\" | \"browser-full\" | \"bitmark-json\";\n\n/** Default variant for the Node entry — disk-loaded wasm, no download budget. */\nexport const NODE_DEFAULT_FEATURE: Feature = \"full\";\n\n/** Default variant for the browser entry — download latency is the budget. */\nexport const BROWSER_DEFAULT_FEATURE: Feature = \"browser-full\";\n\n/**\n * The wasm surface the shared wrappers drive. The optional members are the\n * capabilities a lean variant compiles out (calling their wrapper there\n * throws {@link UnsupportedFeatureError}).\n *\n * Every fallible member THROWS a JavaScript `Error` on failure (PLAN-192 D1);\n * its `message` is the core's error contract, `<kind> at <path>: <message>`,\n * with `<kind>` a stable kebab-case identifier. A returned string is always a\n * result — there is no in-band error prefix to test for.\n */\nexport interface WasmModule {\n convert(\n input: string,\n inputFormat: string,\n outputFormat: string,\n mode: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n includeUnknownProperties: boolean,\n mappingReport: boolean,\n /** `semantic-tokens` output only: `\"utf-16\"` | `\"utf-8\"`, `\"\"` = default (PLAN-193). */\n positionEncoding: string,\n /** `semantic-tokens` output only: `\"lsp\"` | `\"absolute\"`, `\"\"` = default. */\n tokensLayout: string,\n ): string;\n canonicalize(\n input: string,\n inputFormat: string,\n mode: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n ): string;\n transform(\n input: string,\n inputFormat: string,\n outputFormat: string,\n mode: string,\n patchJson: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n ): string;\n info(\n infoType: string,\n format: string,\n bit: string,\n pretty: boolean,\n indent: number,\n includeDeprecated: boolean,\n onlyDeprecated: boolean,\n full: boolean,\n language: string,\n ): string;\n /** Throws when the data is not valid for `dataType`. */\n register(dataType: string, data: string): void;\n count_bits(input: string): string;\n split_bits(input: string): string;\n split_json_bits(input: string): string;\n is_bitmark(input: string): boolean;\n prettify(json: string, indent: number): string;\n convert_text(input: string, direction: string, format: string, location: string): string;\n breakscape_text(input: string, format: string, location: string, subLocation: string): string;\n unbreakscape_text(input: string, format: string, location: string, subLocation: string): string;\n /**\n * Resolve a patch document against a document into ordered items\n * (PLAN-179), splitting it under `inputFormat` (`\"auto\"` | `\"bitmark\"` |\n * `\"json\"`) — the selector the caller already resolved, never a re-sniff.\n */\n resolve_patch_document(input: string, inputFormat: string, patchJson: string): string;\n /**\n * Present only where this build can render `info` as TEXT (PLAN-192 D6) —\n * the same \"presence is the capability\" signal as `diff`, so no caller\n * has to test for a variant name. Absent on `bitmark-json`, where `info`\n * serves JSON only.\n */\n info_text_supported?(): boolean;\n /** Completion at a position (PLAN-196); absent on a variant built without `editor`. */\n complete?(\n input: string,\n line: number,\n character: number,\n positionEncoding: string,\n includeDeprecated: boolean,\n triggerCharacter: string,\n ): string;\n /** One completion item with its documentation (PLAN-202); absent on a variant built without `editor`. */\n resolve?(\n input: string,\n line: number,\n character: number,\n positionEncoding: string,\n includeDeprecated: boolean,\n label: string,\n kind: number,\n ): string;\n /** Hover at a position (PLAN-196); absent on a variant built without `editor`. */\n hover?(input: string, line: number, character: number, positionEncoding: string): string;\n /** Semantic diff (PLAN-179); absent on a variant built without `diff`. */\n diff?(\n a: string,\n b: string,\n inputFormat: string,\n outputFormat: string,\n context: number,\n locate: boolean,\n similarity: number,\n bboxTolerance: number,\n spacesAroundValues: number,\n ): string;\n}\n\n/** Options accepted by `init` / `initSync` on every platform entry. */\nexport interface InitOptions {\n /** Which wasm variant to load. Default: `\"full\"`. */\n feature?: Feature;\n}\n\n/** A call arrived before any wasm module was initialised (browser only). */\nexport class NotInitializedError extends Error {\n constructor() {\n super(\"wasm not initialised: call init() (or init({ feature: … })) first\");\n this.name = \"NotInitializedError\";\n }\n}\n\n/** `register` rejected the supplied data. */\nexport class RegisterError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"RegisterError\";\n }\n}\n\n/**\n * `transform` skipped one or more patch entries (PLAN-209 T9). The document\n * was NOT returned: a silently ignored edit is worse than an error. Each\n * diagnostic names the entry's `path`, the error contract's `kind`\n * (`patch-parse-error`, `patch-index-out-of-range`, `patch-type-mismatch`)\n * and a `message`.\n */\nexport class PatchError extends Error {\n readonly diagnostics: PatchDiagnostic[];\n constructor(diagnostics: PatchDiagnostic[]) {\n const lines = diagnostics.map((d) => `${d.kind} at ${d.path}: ${d.message}`);\n super(\n `${diagnostics.length} patch ${diagnostics.length === 1 ? \"entry was\" : \"entries were\"} skipped:\\n` +\n lines.join(\"\\n\"),\n );\n this.name = \"PatchError\";\n this.diagnostics = diagnostics;\n }\n}\n\n/** A call needs a capability the ACTIVE wasm variant compiled out. */\nexport class UnsupportedFeatureError extends Error {\n constructor(what: string, active: Feature | undefined) {\n super(\n `${what} is not available in the loaded wasm module` +\n `${active ? ` (feature: \"${active}\")` : \"\"}; init({ feature: \"full\" }) to load it`,\n );\n this.name = \"UnsupportedFeatureError\";\n }\n}\n\nlet active: WasmModule | undefined;\nlet activeFeature: Feature | undefined;\nlet lazyLoader: (() => void) | undefined;\n\n// Data registered through `register`, kept so it can be REPLAYED onto each\n// newly activated module. Each variant is a separate wasm instance with its\n// own memory, so a registration made against one is invisible to the next —\n// and PLAN-166's promise is that swapping variants is invisible to the\n// caller. Keyed by type, because `register` replaces per type.\nconst registrations = new Map<string, string>();\n\n/**\n * The active wasm module. On Node the entry installs a lazy loader, so the\n * first call without an explicit `init` loads the default (`full`) variant\n * synchronously; in the browser an uninitialised call throws\n * {@link NotInitializedError} (wasm instantiation is async there).\n */\nexport function getActive(): WasmModule {\n if (!active && lazyLoader) lazyLoader();\n if (!active) throw new NotInitializedError();\n return active;\n}\n\n/** Swap the active module — the single atomic reference assignment. */\nexport function setActive(feature: Feature, module: WasmModule): void {\n // Replay first: a module that cannot take the registered data must not\n // become active half-configured. The data was accepted once already, so a\n // failure here is a bug worth surfacing rather than swallowing.\n for (const [type, data] of registrations) {\n try {\n module.register(type, data);\n } catch (e) {\n throw new RegisterError(e instanceof Error ? e.message : String(e));\n }\n }\n active = module;\n activeFeature = feature;\n}\n\n/**\n * Remember what `register` accepted, so it survives an `init` swap. Called\n * only after the ACTIVE module has accepted the same data.\n */\nexport function rememberRegistration(type: string, data: string): void {\n registrations.set(type, data);\n}\n\n/**\n * Install the Node lazy default loader (runs on the first call when no\n * explicit `init` happened; must call {@link setActive}).\n */\nexport function setLazyLoader(loader: () => void): void {\n lazyLoader = loader;\n}\n\n/** The active wasm feature, or `undefined` before the first init. */\nexport function variant(): Feature | undefined {\n return activeFeature;\n}\n","// @zen-component: TS-HookedTransform\n\nimport type { BitView, OutputFormat, PatchDiagnostic, PatchEntry, TransformOptions } from \"../types.js\";\nimport { PatchError } from \"./facade.js\";\n\n/** What the WASM `transform` export returns (PLAN-209 T9). */\nexport interface TransformEnvelope {\n data: string;\n diagnostics: PatchDiagnostic[];\n}\n\n/** One bit's rendered element plus the entries skipped on it. */\nexport interface BitOutput {\n element: string;\n diagnostics: PatchDiagnostic[];\n}\n\n/**\n * The minimal WASM surface the per-bit hook driver needs. Both the Node and\n * browser entry points satisfy this with their loaded module.\n */\nexport interface HookedWasm {\n is_bitmark(input: string): boolean;\n split_bits(input: string): string;\n split_json_bits(input: string): string;\n prettify(json: string, indent: number): string;\n resolve_patch_document(input: string, inputFormat: string, patchJson: string): string;\n /** Returns the {@link TransformEnvelope} as JSON text. */\n transform(\n input: string,\n inputFormat: string,\n outputFormat: string,\n mode: string,\n patchJson: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n ): string;\n}\n\n/** A single bit's compute job: data only, safe to ship to a worker thread. */\nexport interface BitJob {\n index: number;\n source: string;\n inputFormat: \"bitmark\" | \"json\";\n outputFormat: OutputFormat;\n mode: string;\n patchJson: string;\n spacesAroundValues: number;\n}\n\n/**\n * The result of running `preHook` over a document's bits: the compute `jobs`\n * for the bits that survived (in document order) and the matching `views`\n * (so `postHook` can be called per surviving bit), plus the document-level\n * recombination settings.\n */\nexport interface TransformPlan {\n jobs: BitJob[];\n views: BitView[];\n output: OutputFormat;\n pretty: boolean;\n indent: number;\n}\n\n/** Strip the outer `[ … ]` of a compact one-element JSON array. */\nexport function stripArray(doc: string): string {\n const t = doc.trim();\n return t.startsWith(\"[\") && t.endsWith(\"]\") ? t.slice(1, -1) : doc;\n}\n\n/**\n * The `patch` option as the JSON text the WASM resolver takes — a patch\n * document or the bare-array every-bit shorthand — or `\"\"` when absent.\n */\nfunction patchJsonOf(patch: TransformOptions[\"patch\"]): string {\n if (patch === undefined) return \"\";\n if (typeof patch === \"string\") return patch.trim();\n return JSON.stringify(patch);\n}\n\n/**\n * One slot of the resolved patch document (see `resolve_patch_document`).\n * An inserted `bit` is a JSON bit or, as a string, one bit of bitmark\n * source (PLAN-209 D15).\n */\ninterface ResolvedItem {\n source: number | null;\n bit: Record<string, unknown> | string | null;\n patch: PatchEntry[];\n}\n\nfunction buildUnits(\n wasm: HookedWasm,\n input: string,\n inputIsBitmark: boolean,\n): { view: BitView; source: string }[] {\n if (inputIsBitmark) {\n const slices = JSON.parse(wasm.split_bits(input)) as { index: number; source: string }[];\n return slices.map((s) => ({\n view: { format: \"bitmark\", index: s.index, total: slices.length, source: s.source },\n source: s.source,\n }));\n }\n const envelopes = JSON.parse(wasm.split_json_bits(input)) as {\n bit?: import(\"../generated/bit-types.js\").AnyBit;\n }[];\n return envelopes.map((env, index) => ({\n view: { format: \"json\", index, total: envelopes.length, bit: env.bit ?? {} },\n // Per-bit transform consumes a one-bit JSON document.\n source: JSON.stringify([env]),\n }));\n}\n\n/**\n * Split the document and run `preHook` (on the calling thread) for each bit,\n * producing the surviving compute jobs + their views. This is the main-thread\n * half shared by the sync and worker-pool drivers — only the resulting\n * data-only {@link BitJob}s ever cross to a worker.\n */\nexport function planTransform(\n wasm: HookedWasm,\n input: string,\n options: TransformOptions,\n): TransformPlan {\n const mode = options.mode ?? \"optimized\";\n const output: OutputFormat = options.outputFormat ?? \"json\";\n const spacesAroundValues = options.spacesAroundValues ?? 1;\n const indent = options.indent ?? 2;\n const pretty = options.pretty ?? false;\n\n const sel = options.inputFormat ?? \"auto\";\n const inputIsBitmark = sel === \"bitmark\" || (sel !== \"json\" && wasm.is_bitmark(input));\n const inputFormat = inputIsBitmark ? \"bitmark\" : \"json\";\n\n // The engine decides which output formats are per-bit (the whole-document\n // `lex` / `semantic-tokens` / `diagnostics` are not, and it refuses them\n // before any hook runs). Ask it the same way, once, with an empty document:\n // a document that splits into no bits would otherwise never carry that\n // refusal, and the hooked path would answer `\"\"` where the hookless one\n // throws (PLAN-192 D1 parity).\n wasm.transform(\"\", \"bitmark\", output, mode, \"\", false, indent, spacesAroundValues);\n\n const units = buildUnits(wasm, input, inputIsBitmark);\n const total = units.length;\n\n // Hooks see the ORIGINAL bits, in order, before the patch document\n // reshapes anything (same as the Rust pipeline's Phase A).\n const hooked: ({ patches: PatchEntry[]; outputFormat: OutputFormat } | null)[] = [];\n for (let index = 0; index < total; index++) {\n const { view } = units[index];\n view.index = index;\n view.total = total;\n const result = options.preHook ? options.preHook(view) || {} : {};\n hooked.push(result.drop ? null : { patches: result.patches ?? [], outputFormat: result.outputFormat ?? output });\n }\n\n // The patch document (or the every-bit shorthand) is resolved by the one\n // Rust implementation into the ordered output slots; without a patch the\n // slots are simply the original bits. The resolver splits under the SAME\n // input format resolved above — an explicit selection is never re-sniffed.\n const patchJson = patchJsonOf(options.patch);\n let resolved: ResolvedItem[];\n if (patchJson === \"\") {\n resolved = units.map((_, index) => ({ source: index, bit: null, patch: [] }));\n } else {\n resolved = JSON.parse(\n wasm.resolve_patch_document(input, inputFormat, patchJson),\n ) as ResolvedItem[];\n }\n\n const jobs: BitJob[] = [];\n const views: BitView[] = [];\n for (const item of resolved) {\n if (item.source !== null) {\n const hook = hooked[item.source];\n if (!hook) continue; // dropped by the pre-hook\n jobs.push({\n index: item.source,\n source: units[item.source].source,\n inputFormat,\n outputFormat: hook.outputFormat,\n mode,\n // The document's patches first, the hook's after (the hook contract).\n patchJson: JSON.stringify([...item.patch, ...hook.patches]),\n spacesAroundValues,\n });\n views.push(units[item.source].view);\n } else if (typeof item.bit === \"string\") {\n // An inserted bitmark bit (PLAN-209 D15), seen by `postHook` only.\n const index = jobs.length;\n jobs.push({\n index,\n source: item.bit,\n inputFormat: \"bitmark\",\n outputFormat: output,\n mode,\n patchJson: JSON.stringify(item.patch),\n spacesAroundValues,\n });\n views.push({ format: \"bitmark\", index, total, source: item.bit });\n } else {\n // An inserted bit: a one-bit JSON document, seen by `postHook` only.\n const bit = (item.bit ?? {}) as import(\"../generated/bit-types.js\").AnyBit;\n const index = jobs.length;\n jobs.push({\n index,\n source: JSON.stringify([{ bit }]),\n inputFormat: \"json\",\n outputFormat: output,\n mode,\n patchJson: JSON.stringify(item.patch),\n spacesAroundValues,\n });\n views.push({ format: \"json\", index, total, bit });\n }\n }\n return { jobs, views, output, pretty, indent };\n}\n\n/**\n * Read the WASM `transform` envelope for one bit: the element (compact JSON\n * stripped of its one-element array) and the entries skipped on it.\n */\nexport function bitOutput(envelopeJson: string, outputFormat: OutputFormat): BitOutput {\n const { data, diagnostics } = JSON.parse(envelopeJson) as TransformEnvelope;\n return { element: outputFormat === \"json\" ? stripArray(data) : data, diagnostics };\n}\n\n/**\n * Compute one bit's rendered element synchronously via WASM. The per-bit\n * element is always compact JSON (the document is prettified once on\n * recombination). Throws on failure; skipped entries come back as\n * diagnostics for the driver to raise.\n */\nexport function computeBit(wasm: HookedWasm, job: BitJob): BitOutput {\n return bitOutput(\n wasm.transform(\n job.source,\n job.inputFormat,\n job.outputFormat,\n job.mode,\n job.patchJson,\n false,\n 2,\n job.spacesAroundValues,\n ),\n job.outputFormat,\n );\n}\n\n/** Recombine per-bit elements into the final document, in document order. */\nexport function recombine(\n wasm: HookedWasm,\n elements: string[],\n output: OutputFormat,\n pretty: boolean,\n indent: number,\n): string {\n // Only JSON wraps as an array; every other per-bit output (bitmark, plain\n // text, a mapping such as html) is text and joins with newlines — the same\n // rule as the Rust pipeline's recombine.\n if (output !== \"json\") return elements.join(\"\\n\");\n const arr = `[${elements.join(\",\")}]`;\n return pretty ? wasm.prettify(arr, indent) : arr;\n}\n\n/**\n * Per-bit transform driver (synchronous, in-process): split the document, run\n * `preHook` for each bit on the calling thread, apply the static patch + any\n * hook patches, re-emit the bit, run `postHook`, then recombine in document\n * order.\n *\n * Hooks run synchronously on the calling thread; only data crosses into WASM.\n * Used when a `preHook`/`postHook` is supplied; the hookless path stays a\n * single `wasm.transform` call. Throws on failure (PLAN-192 D1), and a\n * `PatchError` when any entry was skipped (PLAN-209 T9).\n */\nexport function runHookedTransform(\n wasm: HookedWasm,\n input: string,\n options: TransformOptions,\n): string {\n const plan: TransformPlan = planTransform(wasm, input, options);\n\n const rendered: string[] = [];\n const skipped: PatchDiagnostic[] = [];\n for (let i = 0; i < plan.jobs.length; i++) {\n const { element, diagnostics } = computeBit(wasm, plan.jobs[i]);\n skipped.push(...diagnostics);\n options.postHook?.(plan.views[i], element);\n rendered.push(element);\n }\n // A skipped entry is never silent (PLAN-209 T9).\n if (skipped.length > 0) throw new PatchError(skipped);\n return recombine(wasm, rendered, plan.output, plan.pretty, plan.indent);\n}\n","// @zen-component: TS-WorkerEntry\n//\n// Node worker_threads entry for the parallel transform pool. Each worker loads\n// its own WASM instance — the VARIANT the spawning pool passes via workerData\n// (PLAN-166) — and computes one bit at a time from a data-only job. Hooks (JS\n// closures) never reach here — they run on the main thread.\n\nimport { createRequire } from \"node:module\";\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport { type BitJob, bitOutput } from \"../core/hooked-transform.js\";\nimport { NODE_DEFAULT_FEATURE, type Feature } from \"../core/facade.js\";\n\nconst require = createRequire(import.meta.url);\n\n// Bundled to dist/worker-entry.cjs; the wasm glue dirs sit at the package\n// root. Mirrors the entry's variant map (facade.ts).\nconst VARIANT_GLUE: Record<Feature, string> = {\n full: \"../wasm/bitmark_wasm.js\",\n \"browser-full\": \"../wasm-browser-full/bitmark_browser_full_wasm.js\",\n \"bitmark-json\": \"../wasm-bitmark-json/bitmark_json_wasm.js\",\n};\n\nconst feature = ((workerData as { feature?: Feature } | undefined)?.feature ??\n NODE_DEFAULT_FEATURE) as Feature;\nconst wasm = require(VARIANT_GLUE[feature]) as {\n transform(\n input: string,\n inputFormat: string,\n outputFormat: string,\n mode: string,\n patchJson: string,\n pretty: boolean,\n indent: number,\n spacesAroundValues: number,\n ): string;\n};\n\ninterface JobMessage {\n id: number;\n job: BitJob;\n}\n\nparentPort?.on(\"message\", (msg: JobMessage) => {\n const { id, job } = msg;\n try {\n const { element, diagnostics } = bitOutput(\n wasm.transform(\n job.source,\n job.inputFormat,\n job.outputFormat,\n job.mode,\n job.patchJson,\n false,\n 2,\n job.spacesAroundValues,\n ),\n job.outputFormat,\n );\n parentPort?.postMessage({ id, ok: true, element, diagnostics });\n } catch (e) {\n // The wasm export throws on failure (PLAN-192 D1); the pool turns this\n // message back into a rejected promise on the main thread.\n parentPort?.postMessage({ id, ok: false, error: e instanceof Error ? e.message : String(e) });\n }\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmb/bitmark-parser",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "description": "A parser for bitmark text, powered by WebAssembly.",
5
5
  "author": "Get More Brain Ltd <info@getmorebrain.com>",
6
6
  "license": "ISC",
@@ -46,6 +46,8 @@
46
46
  "./schema.json": "./schema/bitmark.schema.json",
47
47
  "./translations": "./translations/translations.json",
48
48
  "./translations.json": "./translations/translations.json",
49
+ "./bitmark.json": "./config/bitmark.json",
50
+ "./bitmark-json.tsp": "./config/bitmark-json.tsp",
49
51
  ".": {
50
52
  "types": "./dist/index.d.ts",
51
53
  "import": "./dist/index.js",
@@ -61,13 +63,15 @@
61
63
  "bin",
62
64
  "schema",
63
65
  "translations",
66
+ "config",
64
67
  "README.md",
65
68
  "LICENSE"
66
69
  ],
67
70
  "scripts": {
68
- "init": "npx tsx scripts/create-package-info.ts && npm run schema:ts && npm run schema:json && npm run translations:copy && node scripts/gen-legacy-resource-keys.mjs",
71
+ "init": "npx tsx scripts/create-package-info.ts && npm run schema:ts && npm run schema:json && npm run translations:copy && npm run config:copy && node scripts/gen-legacy-resource-keys.mjs",
69
72
  "schema:ts": "cargo run -q -p bitmark-schemagen --manifest-path ../../Cargo.toml -- ../../resources/bitmark-configurator/bitmark.json --ts src/generated/bit-types.ts",
70
73
  "translations:copy": "node scripts/copy-translations.mjs",
74
+ "config:copy": "node scripts/copy-config.mjs",
71
75
  "schema:json": "cargo run -q -p bitmark-schemagen --manifest-path ../../Cargo.toml -- ../../resources/bitmark-configurator/bitmark.json --out schema/bitmark.schema.json",
72
76
  "version:check": "bash scripts/version-check.sh",
73
77
  "build:wasm": "bash scripts/build-wasm.sh",
@@ -85,11 +89,11 @@
85
89
  "prepublishOnly": "npm run build && npm test"
86
90
  },
87
91
  "optionalDependencies": {
88
- "@gmb/bitmark-parser-darwin-arm64": "7.1.0",
89
- "@gmb/bitmark-parser-darwin-x64": "7.1.0",
90
- "@gmb/bitmark-parser-linux-arm64": "7.1.0",
91
- "@gmb/bitmark-parser-linux-x64": "7.1.0",
92
- "@gmb/bitmark-parser-win32-x64": "7.1.0"
92
+ "@gmb/bitmark-parser-darwin-arm64": "7.3.0",
93
+ "@gmb/bitmark-parser-darwin-x64": "7.3.0",
94
+ "@gmb/bitmark-parser-linux-arm64": "7.3.0",
95
+ "@gmb/bitmark-parser-linux-x64": "7.3.0",
96
+ "@gmb/bitmark-parser-win32-x64": "7.3.0"
93
97
  },
94
98
  "devDependencies": {
95
99
  "@types/node": "^25.2.2",