parsanol 0.10.3 → 0.10.5

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.
package/README.md CHANGED
@@ -23,6 +23,47 @@ const text = rt.renderString("ISO 5537:2025"); // F6 render
23
23
 
24
24
  Construction throws unless the artifact's embedded checksum verifies.
25
25
 
26
+ ## Provenance
27
+
28
+ Every artifact embeds its provenance; the runtime exposes and verifies
29
+ it (parsanol-ts#5):
30
+
31
+ ```ts
32
+ rt.sourceHash; // SHA-256 of the .parg source (bare hex)
33
+ rt.compilerVersion; // the compiler that produced the artifact
34
+ rt.source; // the embedded .parg source text
35
+ rt.shape; // wire-format family, "parsanol-tree/v2"
36
+ rt.compiledFrom(currentSource); // false = the artifact is stale
37
+ ```
38
+
39
+ A stale artifact (`compiledFrom` false against the current grammar text)
40
+ means the source moved since compilation — recompile or fall back.
41
+ `shape` moves independently of `compilerVersion`: the same
42
+ `parsanol-tree/v2` artifact parses identically on every engine, forever;
43
+ a shape bump means a new engine family.
44
+
45
+ ## Recovery (best-effort partial parses)
46
+
47
+ `parseShapeRecovering` never throws: on success it returns
48
+ `{ ok: true, shape }`; on failure it returns the "what did match"
49
+ answer alongside the failure —
50
+
51
+ ```ts
52
+ const r = rt.parseShapeRecovering("ab 4");
53
+ // { ok: false,
54
+ // error: { offset: 4, expected: [...] },
55
+ // captures: { text: [{ value: "ab", offset: 0, ... }] },
56
+ // consumed_upto: 4 }
57
+ ```
58
+
59
+ - `captures` carries every capture observed before the failure
60
+ (best-effort: it may include captures from branches that later
61
+ failed — same semantics as the rs recovery journal).
62
+ - `consumed_upto` is the furthest position any successful match ended.
63
+
64
+ The strict `parseShape` contract is unchanged; recovery is the
65
+ editors-and-linters answer.
66
+
26
67
  ## Vendored engine freshness
27
68
 
28
69
  `vendor/parsanol-wasm/BUILT_FROM` records the parsanol-rs commit the
package/dist/runtime.d.ts CHANGED
@@ -25,8 +25,21 @@ export declare class PargRuntime {
25
25
  readonly renderSpec: Record<string, RenderSegment[]>;
26
26
  readonly entry: string;
27
27
  readonly defaultEntry: string | null;
28
+ /** parsanol-ts#5 provenance: SHA-256 of the .parg source text and the
29
+ * compiler version. Null for artifacts compiled before these fields
30
+ * existed (unverifiable, not stale). */
31
+ readonly sourceHash: string | null;
32
+ readonly compilerVersion: string | null;
33
+ readonly source: string | null;
28
34
  readonly envelope: Record<string, unknown>;
29
35
  constructor(artifactJson: string, entry?: string);
36
+ /** The wire-format family ("parsanol-tree/v2"): a shape bump requires a
37
+ * new engine family; same-shape artifacts parse identically forever. */
38
+ get shape(): string | null;
39
+ /** True when this artifact was compiled from `source` (the current .parg
40
+ * revision). The compiler bakes a bare hex digest; artifacts without the
41
+ * field answer false — unverifiable rather than fresh. */
42
+ compiledFrom(source: string): boolean;
30
43
  /** Load an artifact JSON file and verify its checksum. */
31
44
  static fromFile(path: string, entry?: string): PargRuntime;
32
45
  /** The artifacts directory: PARG_ARTIFACT_DIR when set. */
@@ -35,6 +48,13 @@ export declare class PargRuntime {
35
48
  static load(grammar: string, entry?: string, dir?: string): PargRuntime;
36
49
  /** Parse with the native wasm engine; returns the parsanol-shape tree. */
37
50
  parseShape(input: string): unknown;
51
+ /**
52
+ * Best-effort recovery (TODO.sota/1): never throws. Success returns
53
+ * `{ ok: true, shape }`; failure returns
54
+ * `{ ok: false, captures, error, consumed_upto }` — the captures
55
+ * observed before the failure plus the failure wire.
56
+ */
57
+ parseShapeRecovering(input: string): RecoveryEnvelope;
38
58
  /** Apply the entry's bindings to a parsanol-shape tree. */
39
59
  applyBindings(shape: unknown): Record<string, unknown>;
40
60
  /** Parse and bind in one step. */
@@ -73,6 +93,21 @@ export type RenderSegment = {
73
93
  /** F6 v1 generic renderer: field / literal / cond-presence segments. */
74
94
  export declare function renderSegments(segments: RenderSegment[] | null, bound: Record<string, unknown>): string;
75
95
  /** A schema-materialized identifier: typed fields plus provenance. */
96
+ export interface RecoveryError {
97
+ offset: number;
98
+ message?: string;
99
+ expected?: string[];
100
+ line?: number;
101
+ column?: number;
102
+ }
103
+ /** The parseShapeRecovering envelope (TODO.sota/1). */
104
+ export interface RecoveryEnvelope {
105
+ ok: boolean;
106
+ shape?: unknown;
107
+ captures?: Record<string, unknown[]>;
108
+ error?: RecoveryError;
109
+ consumed_upto?: number;
110
+ }
76
111
  export interface Materialized {
77
112
  entry: string;
78
113
  attributes: Record<string, unknown>;
package/dist/runtime.js CHANGED
@@ -3,6 +3,7 @@
3
3
  //! materialization. The engine is the vendored parsanol wasm build
4
4
  //! (PargArtifactJs) — no grammar code lives in this package; the baked
5
5
  //! artifact is the contract.
6
+ import { createHash } from "node:crypto";
6
7
  import { createRequire } from "node:module";
7
8
  import { existsSync, readFileSync } from "node:fs";
8
9
  import { fileURLToPath } from "node:url";
@@ -42,6 +43,12 @@ export class PargRuntime {
42
43
  renderSpec;
43
44
  entry;
44
45
  defaultEntry;
46
+ /** parsanol-ts#5 provenance: SHA-256 of the .parg source text and the
47
+ * compiler version. Null for artifacts compiled before these fields
48
+ * existed (unverifiable, not stale). */
49
+ sourceHash;
50
+ compilerVersion;
51
+ source;
45
52
  envelope;
46
53
  constructor(artifactJson, entry) {
47
54
  const parsed = JSON.parse(artifactJson);
@@ -62,8 +69,24 @@ export class PargRuntime {
62
69
  // The compiler bakes default_entry: JSON key order is not preserved
63
70
  // across engines, so the sorted entry list cannot rederive it.
64
71
  this.defaultEntry = parsed["default_entry"] ?? null;
72
+ this.sourceHash = parsed["source_hash"] ?? null;
73
+ this.compilerVersion = parsed["compiler_version"] ?? null;
74
+ this.source = parsed["source"] ?? null;
65
75
  this.entry = entry ?? this.defaultEntry ?? this.artifact.entryNames()[0];
66
76
  }
77
+ /** The wire-format family ("parsanol-tree/v2"): a shape bump requires a
78
+ * new engine family; same-shape artifacts parse identically forever. */
79
+ get shape() {
80
+ return this.envelope["shape"] ?? null;
81
+ }
82
+ /** True when this artifact was compiled from `source` (the current .parg
83
+ * revision). The compiler bakes a bare hex digest; artifacts without the
84
+ * field answer false — unverifiable rather than fresh. */
85
+ compiledFrom(source) {
86
+ if (this.sourceHash === null)
87
+ return false;
88
+ return this.sourceHash === createHash("sha256").update(source).digest("hex");
89
+ }
67
90
  /** Load an artifact JSON file and verify its checksum. */
68
91
  static fromFile(path, entry) {
69
92
  return new PargRuntime(readFileSync(path, "utf8"), entry);
@@ -84,6 +107,15 @@ export class PargRuntime {
84
107
  parseShape(input) {
85
108
  return JSON.parse(this.artifact.parseShape(this.entry, input));
86
109
  }
110
+ /**
111
+ * Best-effort recovery (TODO.sota/1): never throws. Success returns
112
+ * `{ ok: true, shape }`; failure returns
113
+ * `{ ok: false, captures, error, consumed_upto }` — the captures
114
+ * observed before the failure plus the failure wire.
115
+ */
116
+ parseShapeRecovering(input) {
117
+ return JSON.parse(this.artifact.parseShapeRecovering(this.entry, input));
118
+ }
87
119
  /** Apply the entry's bindings to a parsanol-shape tree. */
88
120
  applyBindings(shape) {
89
121
  return JSON.parse(this.artifact.applyBindings(this.entry, JSON.stringify(shape)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "parsanol",
3
- "version": "0.10.3",
3
+ "version": "0.10.5",
4
4
  "description": "PARG artifact runtime for TypeScript \u2014 checksum-verified artifact load, wasm parse, bindings, schema-driven materialization",
5
5
  "type": "module",
6
6
  "main": "dist/runtime.js",
@@ -1,2 +1,2 @@
1
- 68d8f05b92379bd78365ca29fae05fc58b76f235
1
+ 32937dcf6f5d7998a1f543292c88fe86b7a5a812
2
2
  built with: wasm-pack build --target=nodejs --features wasm (from parsanol-rs/parsanol)
@@ -39,7 +39,7 @@ class PargArtifactJs {
39
39
  */
40
40
  entryNames() {
41
41
  const ret = wasm.pargartifactjs_entryNames(this.__wbg_ptr);
42
- var v1 = getArrayJsValueFromWasm0(ret[0], ret[1]).slice();
42
+ var v1 = getArrayJsValueFromWasm0(ret[0], ret[1]);
43
43
  wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
44
44
  return v1;
45
45
  }
@@ -54,7 +54,7 @@ class PargArtifactJs {
54
54
  if (ret[2]) {
55
55
  throw takeFromExternrefTable0(ret[1]);
56
56
  }
57
- this.__wbg_ptr = ret[0] >>> 0;
57
+ this.__wbg_ptr = ret[0];
58
58
  PargArtifactJsFinalization.register(this, this.__wbg_ptr, this);
59
59
  return this;
60
60
  }
@@ -75,6 +75,25 @@ class PargArtifactJs {
75
75
  }
76
76
  return takeFromExternrefTable0(ret[0]);
77
77
  }
78
+ /**
79
+ * Best-effort recovery (TODO.sota/1): success returns
80
+ * `{"ok": true, "shape": ...}`; failure returns
81
+ * `{"ok": false, "captures": ..., "error": ..., "consumed_upto"}`.
82
+ * @param {string} entry
83
+ * @param {string} input
84
+ * @returns {string}
85
+ */
86
+ parseShapeRecovering(entry, input) {
87
+ const ptr0 = passStringToWasm0(entry, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
88
+ const len0 = WASM_VECTOR_LEN;
89
+ const ptr1 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
90
+ const len1 = WASM_VECTOR_LEN;
91
+ const ret = wasm.pargartifactjs_parseShapeRecovering(this.__wbg_ptr, ptr0, len0, ptr1, len1);
92
+ if (ret[2]) {
93
+ throw takeFromExternrefTable0(ret[1]);
94
+ }
95
+ return takeFromExternrefTable0(ret[0]);
96
+ }
78
97
  /**
79
98
  * Parse an entry with the native engine; returns the parsanol-shape
80
99
  * tree as JSON text.
@@ -106,22 +125,22 @@ class PargArtifactJs {
106
125
  return takeFromExternrefTable0(ret[0]);
107
126
  }
108
127
  /**
109
- * The binding-requirements schema as JSON text.
128
+ * The binding-requirements schema as TypeScript source.
110
129
  * @returns {string}
111
130
  */
112
- schema() {
113
- const ret = wasm.pargartifactjs_schema(this.__wbg_ptr);
131
+ schemaTypescript() {
132
+ const ret = wasm.pargartifactjs_schemaTypescript(this.__wbg_ptr);
114
133
  if (ret[2]) {
115
134
  throw takeFromExternrefTable0(ret[1]);
116
135
  }
117
136
  return takeFromExternrefTable0(ret[0]);
118
137
  }
119
138
  /**
120
- * The binding-requirements schema as TypeScript source.
139
+ * The binding-requirements schema as JSON text.
121
140
  * @returns {string}
122
141
  */
123
- schemaTypescript() {
124
- const ret = wasm.pargartifactjs_schemaTypescript(this.__wbg_ptr);
142
+ schema() {
143
+ const ret = wasm.pargartifactjs_schema(this.__wbg_ptr);
125
144
  if (ret[2]) {
126
145
  throw takeFromExternrefTable0(ret[1]);
127
146
  }
@@ -138,7 +157,6 @@ exports.PargArtifactJs = PargArtifactJs;
138
157
  */
139
158
  class WasmParser {
140
159
  static __wrap(ptr) {
141
- ptr = ptr >>> 0;
142
160
  const obj = Object.create(WasmParser.prototype);
143
161
  obj.__wbg_ptr = ptr;
144
162
  WasmParserFinalization.register(obj, obj.__wbg_ptr, obj);
@@ -198,7 +216,7 @@ class WasmParser {
198
216
  if (ret[2]) {
199
217
  throw takeFromExternrefTable0(ret[1]);
200
218
  }
201
- this.__wbg_ptr = ret[0] >>> 0;
219
+ this.__wbg_ptr = ret[0];
202
220
  WasmParserFinalization.register(this, this.__wbg_ptr, this);
203
221
  return this;
204
222
  }
@@ -295,11 +313,10 @@ function init() {
295
313
  wasm.init();
296
314
  }
297
315
  exports.init = init;
298
-
299
316
  function __wbg_get_imports() {
300
317
  const import0 = {
301
318
  __proto__: null,
302
- __wbg___wbindgen_string_get_395e606bd0ee4427: function(arg0, arg1) {
319
+ __wbg___wbindgen_string_get_0380ccaa2f57f0d9: function(arg0, arg1) {
303
320
  const obj = arg1;
304
321
  const ret = typeof(obj) === 'string' ? obj : undefined;
305
322
  var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -307,10 +324,10 @@ function __wbg_get_imports() {
307
324
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
308
325
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
309
326
  },
310
- __wbg___wbindgen_throw_6ddd609b62940d55: function(arg0, arg1) {
327
+ __wbg___wbindgen_throw_41e9ee4f547fc59a: function(arg0, arg1) {
311
328
  throw new Error(getStringFromWasm0(arg0, arg1));
312
329
  },
313
- __wbg_error_a6fa202b58aa1cd3: function(arg0, arg1) {
330
+ __wbg_error_757e9472f8410341: function(arg0, arg1) {
314
331
  let deferred0_0;
315
332
  let deferred0_1;
316
333
  try {
@@ -321,26 +338,26 @@ function __wbg_get_imports() {
321
338
  wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
322
339
  }
323
340
  },
324
- __wbg_getRandomValues_3f44b700395062e5: function() { return handleError(function (arg0, arg1) {
341
+ __wbg_getRandomValues_a608c4436c19407a: function() { return handleError(function (arg0, arg1) {
325
342
  globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
326
343
  }, arguments); },
327
344
  __wbg_new_227d7c05414eb861: function() {
328
345
  const ret = new Error();
329
346
  return ret;
330
347
  },
331
- __wbg_new_a70fbab9066b301f: function() {
332
- const ret = new Array();
348
+ __wbg_new_617a8cdb8bb1130e: function() {
349
+ const ret = new Object();
333
350
  return ret;
334
351
  },
335
- __wbg_new_ab79df5bd7c26067: function() {
336
- const ret = new Object();
352
+ __wbg_new_ee2291f50781bf1d: function() {
353
+ const ret = new Array();
337
354
  return ret;
338
355
  },
339
- __wbg_push_e87b0e732085a946: function(arg0, arg1) {
356
+ __wbg_push_2baf45db356cf468: function(arg0, arg1) {
340
357
  const ret = arg0.push(arg1);
341
358
  return ret;
342
359
  },
343
- __wbg_set_7eaa4f96924fd6b3: function() { return handleError(function (arg0, arg1, arg2) {
360
+ __wbg_set_145a351398b48c65: function() { return handleError(function (arg0, arg1, arg2) {
344
361
  const ret = Reflect.set(arg0, arg1, arg2);
345
362
  return ret;
346
363
  }, arguments); },
@@ -351,16 +368,16 @@ function __wbg_get_imports() {
351
368
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
352
369
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
353
370
  },
354
- __wbg_stringify_5ae93966a84901ac: function() { return handleError(function (arg0) {
371
+ __wbg_stringify_52ff602c1cc4fbb6: function() { return handleError(function (arg0) {
355
372
  const ret = JSON.stringify(arg0);
356
373
  return ret;
357
374
  }, arguments); },
358
- __wbindgen_cast_0000000000000001: function(arg0) {
375
+ __wbindgen_generic_0000000000000001: function(arg0) {
359
376
  // Cast intrinsic for `F64 -> Externref`.
360
377
  const ret = arg0;
361
378
  return ret;
362
379
  },
363
- __wbindgen_cast_0000000000000002: function(arg0, arg1) {
380
+ __wbindgen_generic_0000000000000002: function(arg0, arg1) {
364
381
  // Cast intrinsic for `Ref(String) -> Externref`.
365
382
  const ret = getStringFromWasm0(arg0, arg1);
366
383
  return ret;
@@ -383,10 +400,10 @@ function __wbg_get_imports() {
383
400
 
384
401
  const PargArtifactJsFinalization = (typeof FinalizationRegistry === 'undefined')
385
402
  ? { register: () => {}, unregister: () => {} }
386
- : new FinalizationRegistry(ptr => wasm.__wbg_pargartifactjs_free(ptr >>> 0, 1));
403
+ : new FinalizationRegistry(ptr => wasm.__wbg_pargartifactjs_free(ptr, 1));
387
404
  const WasmParserFinalization = (typeof FinalizationRegistry === 'undefined')
388
405
  ? { register: () => {}, unregister: () => {} }
389
- : new FinalizationRegistry(ptr => wasm.__wbg_wasmparser_free(ptr >>> 0, 1));
406
+ : new FinalizationRegistry(ptr => wasm.__wbg_wasmparser_free(ptr, 1));
390
407
 
391
408
  function addToExternrefTable0(obj) {
392
409
  const idx = wasm.__externref_table_alloc();
@@ -432,8 +449,7 @@ function getDataViewMemory0() {
432
449
  }
433
450
 
434
451
  function getStringFromWasm0(ptr, len) {
435
- ptr = ptr >>> 0;
436
- return decodeText(ptr, len);
452
+ return decodeText(ptr >>> 0, len);
437
453
  }
438
454
 
439
455
  let cachedUint8ArrayMemory0 = null;
@@ -524,5 +540,6 @@ let WASM_VECTOR_LEN = 0;
524
540
  const wasmPath = `${__dirname}/parsanol_bg.wasm`;
525
541
  const wasmBytes = require('fs').readFileSync(wasmPath);
526
542
  const wasmModule = new WebAssembly.Module(wasmBytes);
527
- let wasm = new WebAssembly.Instance(wasmModule, __wbg_get_imports()).exports;
543
+ let wasmInstance = new WebAssembly.Instance(wasmModule, __wbg_get_imports());
544
+ let wasm = wasmInstance.exports;
528
545
  wasm.__wbindgen_start();
@@ -25,6 +25,12 @@ export class PargArtifactJs {
25
25
  * Parse and bind in one step.
26
26
  */
27
27
  parseAndBind(entry: string, input: string): string;
28
+ /**
29
+ * Best-effort recovery (TODO.sota/1): success returns
30
+ * `{"ok": true, "shape": ...}`; failure returns
31
+ * `{"ok": false, "captures": ..., "error": ..., "consumed_upto"}`.
32
+ */
33
+ parseShapeRecovering(entry: string, input: string): string;
28
34
  /**
29
35
  * Parse an entry with the native engine; returns the parsanol-shape
30
36
  * tree as JSON text.
@@ -35,14 +41,14 @@ export class PargArtifactJs {
35
41
  * JSON (empty array means green).
36
42
  */
37
43
  runTests(): string;
38
- /**
39
- * The binding-requirements schema as JSON text.
40
- */
41
- schema(): string;
42
44
  /**
43
45
  * The binding-requirements schema as TypeScript source.
44
46
  */
45
47
  schemaTypescript(): string;
48
+ /**
49
+ * The binding-requirements schema as JSON text.
50
+ */
51
+ schema(): string;
46
52
  }
47
53
 
48
54
  /**
@@ -3,14 +3,15 @@
3
3
  export const memory: WebAssembly.Memory;
4
4
  export const __wbg_pargartifactjs_free: (a: number, b: number) => void;
5
5
  export const __wbg_wasmparser_free: (a: number, b: number) => void;
6
+ export const init: () => void;
6
7
  export const pargartifactjs_applyBindings: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
7
8
  export const pargartifactjs_entryNames: (a: number) => [number, number];
8
9
  export const pargartifactjs_new: (a: number, b: number) => [number, number, number];
9
10
  export const pargartifactjs_parseAndBind: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
10
11
  export const pargartifactjs_parseShape: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
12
+ export const pargartifactjs_parseShapeRecovering: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
11
13
  export const pargartifactjs_runTests: (a: number) => [number, number, number];
12
14
  export const pargartifactjs_schema: (a: number) => [number, number, number];
13
- export const pargartifactjs_schemaTypescript: (a: number) => [number, number, number];
14
15
  export const parsanol_c_last_error: () => number;
15
16
  export const parsanol_c_parse: (a: bigint, b: number, c: number, d: number) => number;
16
17
  export const parsanol_c_parse_len: (a: bigint, b: number, c: number, d: number, e: number) => number;
@@ -33,12 +34,12 @@ export const parsanol_result_error: (a: number) => number;
33
34
  export const parsanol_result_free: (a: number) => void;
34
35
  export const parsanol_result_success: (a: number) => number;
35
36
  export const parsanol_version: () => number;
37
+ export const pargartifactjs_schemaTypescript: (a: number) => [number, number, number];
36
38
  export const wasmparser_fromArtifact: (a: number, b: number, c: number, d: number) => [number, number, number];
37
39
  export const wasmparser_new: (a: number, b: number) => [number, number, number];
38
40
  export const wasmparser_parse: (a: number, b: number, c: number) => [number, number, number];
39
41
  export const wasmparser_parse_flat: (a: number, b: number, c: number) => [number, number, number, number];
40
42
  export const wasmparser_parse_json: (a: number, b: number, c: number) => [number, number, number, number];
41
- export const init: () => void;
42
43
  export const parsanol_string_free: (a: number) => void;
43
44
  export const __wbindgen_malloc: (a: number, b: number) => number;
44
45
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;