@factoidal/core 0.10.0 → 0.12.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.
package/fn.js CHANGED
@@ -27,7 +27,8 @@ const engineApi = require('./index.js');
27
27
  const {
28
28
  DatasetCore,
29
29
  dataFactory,
30
- quadToNQuads,
30
+ quadKey,
31
+ termKey,
31
32
  quadsToNQuads,
32
33
  } = require('./rdfjs.js');
33
34
 
@@ -132,7 +133,7 @@ function makeFnDataset(quadsIterable) {
132
133
  const out = [];
133
134
  for (const q of quadsIterable) {
134
135
  const frozen = dataFactory.fromQuad(q); // normalizes + (re)freezes
135
- const key = quadToNQuads(frozen);
136
+ const key = quadKey(frozen); // RDF/JS Term.equals identity, no syntax
136
137
  if (seen.has(key)) continue;
137
138
  seen.add(key);
138
139
  out.push(frozen);
@@ -210,7 +211,7 @@ async function engineOf(ds) {
210
211
  if (ds._backend.engine) return ds._backend.engine;
211
212
  let eds = ENGINE_CACHE.get(ds);
212
213
  if (!eds) {
213
- eds = await engineApi.openDataset({ text: quadsToNQuads(ds.toArray()), format: 'nquads' });
214
+ eds = await engineApi.fromQuads(ds.toArray());
214
215
  ENGINE_CACHE.set(ds, eds);
215
216
  }
216
217
  return eds;
@@ -251,7 +252,7 @@ function builder() {
251
252
  let finished = false;
252
253
  return {
253
254
  addChunk(chunk) {
254
- if (finished) throw new Error('builder: addChunk() called after finish()');
255
+ if (finished) throw new engineApi.FactoidalError('builder: addChunk() called after finish()', { code: 'op.failed' });
255
256
  if (chunk && typeof chunk.termType === 'string') {
256
257
  pending.push(chunk); // a single Quad
257
258
  } else {
@@ -259,7 +260,7 @@ function builder() {
259
260
  }
260
261
  },
261
262
  finish() {
262
- if (finished) throw new Error('builder: finish() already called');
263
+ if (finished) throw new engineApi.FactoidalError('builder: finish() already called', { code: 'op.failed' });
263
264
  finished = true;
264
265
  const result = makeFnDataset(pending);
265
266
  pending = null; // release; no further mutation is possible anyway
@@ -297,8 +298,8 @@ function union(a, b) {
297
298
  /** Quads in a that are not in b. */
298
299
  function difference(a, b) {
299
300
  assertFnDataset(a, 'difference'); assertFnDataset(b, 'difference');
300
- const bKeys = new Set(b.toArray().map(quadToNQuads));
301
- return makeFnDataset(a.toArray().filter((q) => !bKeys.has(quadToNQuads(q))));
301
+ const bKeys = new Set(b.toArray().map(quadKey));
302
+ return makeFnDataset(a.toArray().filter((q) => !bKeys.has(quadKey(q))));
302
303
  }
303
304
 
304
305
  /** Keep quads matching quadPred(quad) => boolean. */
@@ -353,64 +354,22 @@ async function query(ds, sparql, options) {
353
354
  * per-graph closure semantics that the underlying query() does not
354
355
  * define today.
355
356
  */
356
- // Case-insensitive regime aliases ('rdfs', 'owl-rl', 'owlrl', 'owl_rl')
357
- // normalized to the engine's exact-case ENTAIL_VALUES spelling ('RDFS',
358
- // 'OWL-RL'); anything else (including already-correct casing, or a value
359
- // that names no regime) passes through unchanged, so engineApi.query's own
360
- // validation reports the error and this function does not guess what the
361
- // caller meant.
362
- const ENTAIL_REGIME_ALIASES = {
363
- none: 'none', rdfs: 'RDFS', 'owl-rl': 'OWL-RL', owlrl: 'OWL-RL', owl_rl: 'OWL-RL',
364
- };
365
-
366
- function normalizeEntailRegime(regime) {
367
- const key = String(regime == null ? 'none' : regime).toLowerCase();
368
- return ENTAIL_REGIME_ALIASES[key] || regime;
369
- }
370
-
371
- // x-ikl-* is NOT IMPLEMENTED. It is not withheld by policy.
372
- //
373
- // CORRECTION 2026-08-26: an earlier version of this comment said the
374
- // family was withheld by owner decision, citing the direction-B ruling
375
- // of https://github.com/danbri/factoidal/issues/618. That ruling was
376
- // about the IKL-to-RDF projection, not about this regime family, and
377
- // attributing it here misstated a decision the owner did not make.
378
- //
379
- // The x-ikl-* family is the OWNER'S design
380
- // (https://github.com/danbri/factoidal/issues/581). Its Lean dispatch
381
- // was deleted on 2026-08-26 as collateral of the projection purge
382
- // (https://github.com/danbri/factoidal/issues/626), not as its target.
383
- // The SEMANTICS survived: Unified/ClBridge.lean's asserted_merge_sound
384
- // is this regime's soundness statement -- lifting asserted named-graph
385
- // content into the default graph -- and it carries no naming scheme.
386
- // Restoring the family is a dispatch branch plus flipping this guard.
387
- //
388
- // The owner's own statement of what IKL does not supply, 2026-08-26:
389
- // no profiles, no entailment procedures, no mapping from RDF datasets.
390
- // Checked the same day against the IKL GUIDE and Hayes 2009: correct.
391
- //
392
- // engineApi.query() (lib/api.js) already rejects this, but
393
- // normalizeEntailRegime() above passes any unrecognised string through
394
- // UNCHANGED before it gets there — check explicitly here too, so this
395
- // function's own contract states the rule rather than depending on a
396
- // downstream check someone could relax without noticing this call
397
- // site. Case-insensitive, any suffix; see test/select.test.js.
398
- function rejectIklRegime(regime, who) {
399
- if (regime != null && /^x-ikl/i.test(String(regime))) {
400
- throw new TypeError(
401
- `${who}: entail '${regime}' is not implemented. The x-ikl-* ` +
402
- 'entailment regimes are the owner\'s design ' +
403
- '(danbri/factoidal#581); the Lean engine\'s dispatch for them was ' +
404
- 'deleted on 2026-08-26 as collateral of the IKL-to-RDF projection ' +
405
- 'purge (danbri/factoidal#626). This is not a policy exclusion.');
406
- }
357
+ // The regime names and their case-insensitive aliases are the engine's tables
358
+ // (op engineTables): no host copy
359
+ // (https://github.com/danbri/factoidal/issues/894 item 5). A name with no
360
+ // alias passes through unchanged and engineApi.query refuses it with the
361
+ // engine's code (regime.refused for x-ikl-*, option.invalid-value otherwise).
362
+ async function regimeOf(regime) {
363
+ const t = await engineApi.engineTables();
364
+ const r = regime == null ? 'none' : String(regime);
365
+ return t.entailAliases[r.toLowerCase()] || regime;
407
366
  }
408
367
 
409
368
  async function entail(ds, regime) {
410
369
  assertFnDataset(ds, 'entail');
411
- rejectIklRegime(regime, 'entail');
370
+ const entailRegime = await regimeOf(regime);
412
371
  const rows = await engineApi.query((await engineOf(ds)), 'SELECT ?s ?p ?o WHERE { ?s ?p ?o }',
413
- { entail: normalizeEntailRegime(regime) });
372
+ { entail: entailRegime });
414
373
  return makeFnDataset(
415
374
  rows.map((row) => dataFactory.quad(row.get('s'), row.get('p'), row.get('o'))));
416
375
  }
@@ -696,60 +655,34 @@ function peekHash(ds) {
696
655
  return typeof c === 'string' ? c : null;
697
656
  }
698
657
 
699
- function hasBlankNode(ds) {
700
- for (const q of ds) {
701
- if (q.subject.termType === 'BlankNode') return true;
702
- if (q.object.termType === 'BlankNode') return true;
703
- if (q.graph.termType === 'BlankNode') return true;
704
- }
705
- return false;
706
- }
707
-
708
658
  /**
709
- * Structural equality, cheapest-correct-path first (see the design
710
- * doc's cost model section for the full argument):
711
- *
712
- * 1. Different sizes -> not equal, O(1).
713
- * 2. Both sides already have a memoized hash -> compare those, O(1).
714
- * 3. Exact quad-set match (same terms, same labels) -> equal, O(n).
715
- * This is sound regardless of blank nodes: identical tokens can
716
- * never be a false positive.
717
- * 4. No blank nodes anywhere -> the step-3 negative is authoritative
718
- * (ground quads have no relabeling to hide behind) -> not equal.
719
- * 5. Otherwise (blank nodes present, step 3 was inconclusive) ->
720
- * the only sound test left is RDFC-1.0 canonical-hash equality,
721
- * which is exactly the O(n log n) canonicalize() cost this module
722
- * tries to avoid paying twice.
659
+ * Dataset isomorphism (RDF 1.1 Concepts 3.6 and 4, blank nodes inside RDF
660
+ * 1.2 triple terms included), decided by the engine
661
+ * (Dataset.isomorphicOutcome, op datasetIsomorphic). The same FnDataset is
662
+ * equal to itself without a call.
723
663
  */
724
664
  async function equals(a, b) {
725
665
  assertFnDataset(a, 'equals'); assertFnDataset(b, 'equals');
726
666
  if (a === b) return true;
727
- if (a.size !== b.size) return false;
728
- const ha = peekHash(a);
729
- const hb = peekHash(b);
730
- if (ha !== null && hb !== null) return ha === hb;
731
- const aKeys = new Set(a.toArray().map(quadToNQuads));
732
- const bTokens = b.toArray().map(quadToNQuads);
733
- const exact = bTokens.length === aKeys.size &&
734
- bTokens.every((t) => aKeys.has(t));
735
- if (exact) return true;
736
- if (!hasBlankNode(a) && !hasBlankNode(b)) return false;
737
- const [h1, h2] = await Promise.all([hash(a), hash(b)]);
738
- return h1 === h2;
667
+ return engineApi.isomorphic(await engineOf(a), await engineOf(b));
739
668
  }
740
669
 
741
670
  /**
742
- * Enumerate named graphs (default graph excluded), first-seen order.
743
- * Reuses index.js's graphs() enumeration; wraps each per-graph
744
- * Dataset back into an FnDataset.
671
+ * Enumerate named graphs (default graph excluded), first-seen order. An
672
+ * engine-backed dataset asks the engine (op datasetGraphNames); an array of
673
+ * JS quads groups by RDF/JS Term.equals identity, so an IRI and a blank node
674
+ * with the same string are two graphs.
745
675
  */
746
676
  function graphs(ds) {
747
677
  assertFnDataset(ds, 'graphs');
678
+ if (ds._backend.engine) {
679
+ return engineApi.graphs(ds._backend.engine).map(([name, g]) => [name, wrapEngine(g)]);
680
+ }
748
681
  const seen = new Map();
749
682
  for (const q of ds) {
750
- if (q.graph.termType !== 'DefaultGraph' && !seen.has(q.graph.value)) seen.set(q.graph.value, q.graph);
683
+ if (q.graph.termType !== 'DefaultGraph' && !seen.has(termKey(q.graph))) seen.set(termKey(q.graph), q.graph);
751
684
  }
752
- return [...seen].map(([iri, g]) => [iri, ds.match(null, null, null, g)]);
685
+ return [...seen.values()].map((g) => [g, ds.match(null, null, null, g)]);
753
686
  }
754
687
 
755
688
  // ---------------------------------------------------------------------
package/index.d.ts CHANGED
@@ -84,27 +84,85 @@ export interface DataFactory {
84
84
  export const dataFactory: DataFactory;
85
85
 
86
86
  /**
87
- * A parse failure that carries a position (issue #344): `line`/
88
- * `column`/`offset` are set when the failing engine call reported one
89
- * (Turtle, TriG, N-Triples, N-Quads); `undefined` for an RDF/XML or
90
- * JSON-LD failure, or any non-parse engine error, which stay a plain
91
- * `Error`. `name` is `'ParseError'`.
87
+ * The stable error codes. The engine decides the code of every refusal
88
+ * (formal/lean4/Wasm/Ops/ErrorModel.lean); `engineTables().errorCodes` lists
89
+ * each with its specification section and summary, and the README table
90
+ * "Errors" is generated from that list. `host.io` is a failure of
91
+ * the host (file system, IndexedDB), not of the engine.
92
92
  */
93
- export class ParseError extends Error {
94
- readonly name: 'ParseError';
95
- /** 1-based line of the error. */
96
- readonly line?: number;
97
- /** 1-based column of the error, counted in Unicode scalar values. */
98
- readonly column?: number;
93
+ export type FactoidalErrorCode =
94
+ | 'parse.syntax'
95
+ | 'parse.rdf12-required'
96
+ | 'term.invalid-iri'
97
+ | 'term.invalid-langtag'
98
+ | 'term.invalid'
99
+ | 'query.syntax'
100
+ | 'query.failed'
101
+ | 'update.syntax'
102
+ | 'update.failed'
103
+ | 'format.unknown'
104
+ | 'serialize.unsupported-rdf12'
105
+ | 'serialize.failed'
106
+ | 'canon.unsupported-rdf12'
107
+ | 'canon.failed'
108
+ | 'owl.unsupported-rdf12'
109
+ | 'reason.failed'
110
+ | 'regime.refused'
111
+ | 'validate.failed'
112
+ | 'xml.failed'
113
+ | 'crypto.failed'
114
+ | 'store.failed'
115
+ | 'registry.failed'
116
+ | 'handle.unknown'
117
+ | 'handle.disposed'
118
+ | 'option.unknown'
119
+ | 'option.invalid-value'
120
+ | 'option.conflict'
121
+ | 'capacity.exceeded'
122
+ | 'call.arity'
123
+ | 'call.arguments'
124
+ | 'op.unknown'
125
+ | 'op.failed'
126
+ | 'host.io';
127
+
128
+ /** Where in the input a FactoidalError happened. */
129
+ export interface FactoidalErrorPosition {
130
+ /** 1-based line. */
131
+ line: number;
132
+ /** 1-based column, counted in Unicode scalar values. */
133
+ column: number;
99
134
  /**
100
- * The position of the error as a UTF-16 code-unit index into the input
101
- * JavaScript string: `text.slice(err.offset)` starts at the error. A
102
- * character outside the Basic Multilingual Plane (U+10000 and above)
103
- * counts 2, as in every JavaScript string index. (The engine reports a
104
- * count of Unicode scalar values; the wrapper converts.)
135
+ * A UTF-16 code-unit index into the input JavaScript string:
136
+ * `text.slice(err.position.offset)` starts at the error.
105
137
  */
106
- readonly offset?: number;
138
+ offset: number;
139
+ /** The same position as a count of UTF-8 bytes from the start of the input. */
140
+ byteOffset: number;
141
+ }
142
+
143
+ /**
144
+ * The one error class of the package. Every function rejects (or throws)
145
+ * with it; a `TypeError` is thrown only when an argument has the wrong
146
+ * JavaScript type. The engine decides `code`, `spec` and `position`.
147
+ */
148
+ export class FactoidalError extends Error {
149
+ constructor(message: string, info?: {
150
+ code?: FactoidalErrorCode; position?: FactoidalErrorPosition | null;
151
+ spec?: string | null; cause?: unknown; format?: string; formatInferred?: boolean;
152
+ });
153
+ readonly name: 'FactoidalError';
154
+ /** The stable machine code. */
155
+ readonly code: FactoidalErrorCode;
156
+ /** Where the input defines a position (a parse, a query, an update), else null. */
157
+ readonly position: FactoidalErrorPosition | null;
158
+ /** The URL of the specification section that defines the condition, else null. */
159
+ readonly spec: string | null;
160
+ /** The underlying error, when there is one (host.io). */
161
+ readonly cause?: unknown;
162
+ /** For a parse failure of text: the syntax it was read as. */
107
163
  readonly format?: string;
164
+ /** For a parse failure of text: true when the syntax was inferred. */
165
+ readonly formatInferred?: boolean;
108
166
  }
109
167
 
110
168
  /** One recoverable-parse diagnostic from a `{lenient:true}` parse() call. */
@@ -112,7 +170,7 @@ export interface ParseDiagnostic {
112
170
  message: string;
113
171
  /**
114
172
  * The position of the error as a UTF-16 code-unit index into the input
115
- * JavaScript string, as ParseError.offset: `text.slice(d.offset)` starts
173
+ * JavaScript string, as FactoidalError.position.offset: `text.slice(d.offset)` starts
116
174
  * at the error.
117
175
  */
118
176
  offset: number;
@@ -222,8 +280,6 @@ export class Dataset implements Iterable<Quad> {
222
280
  [Symbol.dispose](): void;
223
281
  }
224
282
 
225
- /** @deprecated The name of Dataset before 0.10.0; removed in 0.11.0. */
226
- export const DatasetHandle: typeof Dataset;
227
283
 
228
284
  export type ClosureRegime = 'rdfs' | 'owl-rl' | 'rho-df' | 'rdfs-plus';
229
285
 
@@ -302,7 +358,7 @@ export interface ParseOptions {
302
358
  * entry per skipped statement. RDF/XML and JSON-LD parse strictly. A
303
359
  * document with no error gives the strict result and no diagnostics.
304
360
  * Default: false (strict; a syntax error, an undeclared prefix, or an
305
- * unresolvable relative IRI rejects the whole parse with a ParseError).
361
+ * unresolvable relative IRI rejects the whole parse with a FactoidalError).
306
362
  */
307
363
  lenient?: boolean;
308
364
  /**
@@ -323,7 +379,7 @@ export interface QueryOptions extends DatasetQueryOptions {
323
379
  * order is sorted (canonical N-Quads order), not document order;
324
380
  * anonymous blank-node labels (`_anonN`) DO reflect document order.
325
381
  * See README.md's "Blank nodes, labels and statement order". Strict
326
- * by default (issue #344): a syntax error rejects with a ParseError
382
+ * by default (issue #344): a syntax error rejects with a FactoidalError
327
383
  * (`line`/`column`/`offset` for Turtle/TriG/N-Triples/N-Quads; a
328
384
  * message only for RDF/XML and JSON-LD). `options.lenient` recovers
329
385
  * instead; see ParseOptions.
@@ -337,7 +393,7 @@ export function parse(text: string, options?: ParseOptions | DataFormat): Promis
337
393
  * Concise Bounded Description of each resource named). A `SERVICE <iri>`
338
394
  * whose IRI no registerServiceEndpoint() call named answers no
339
395
  * solutions even without SILENT
340
- * (https://github.com/danbri/factoidal/issues/717). `data` may be a DatasetHandle
396
+ * (https://github.com/danbri/factoidal/issues/717). `data` may be a Dataset
341
397
  * from openDataset(), in which case this routes to `data.query()` and
342
398
  * `options.entail` must be 'none' or omitted (a handle carries no
343
399
  * entailment-closure step).
@@ -447,7 +503,7 @@ export function serialize(
447
503
  /**
448
504
  * RDFC-1.0 dataset canonicalization: canonical blank-node labels plus
449
505
  * sorted canonical N-Quads. Two isomorphic inputs canonicalize to the
450
- * same string. `data` may be a DatasetHandle, in which case this
506
+ * same string. `data` may be a Dataset, in which case this
451
507
  * routes to `data.canonicalize()` (options are ignored for a handle).
452
508
  */
453
509
  export function canonicalize(
@@ -462,7 +518,7 @@ export function canonicalize(
462
518
  * `graph` is the same Dataset filtered to that one graph name (what
463
519
  * `dataset.match(null, null, null, graphTerm)` would return).
464
520
  */
465
- export function graphs(dataset: Dataset): Array<[iri: string, graph: Dataset]>;
521
+ export function graphs(dataset: Dataset): Array<[name: NamedNode | BlankNode, graph: Dataset]>;
466
522
 
467
523
  /**
468
524
  * RDFC-1.0 canonical form of a single graph -- the graph-scoped
@@ -476,6 +532,23 @@ export function graphs(dataset: Dataset): Array<[iri: string, graph: Dataset]>;
476
532
  */
477
533
  export function canonicalHash(datasetOrGraph: Dataset): Promise<string>;
478
534
 
535
+ /**
536
+ * Whether two datasets are isomorphic (RDF 1.1 Concepts 3.6 and 4; blank
537
+ * nodes inside RDF 1.2 triple terms included). The engine decides
538
+ * (op datasetIsomorphic); rejects when its blank-node search budget is
539
+ * exceeded.
540
+ */
541
+ export function isomorphic(a: DataInput, b: DataInput, options?: { format?: string; baseIRI?: string }): Promise<boolean>;
542
+
543
+ /** A Dataset holding RDF/JS quads; each term is sent to the engine as its components. */
544
+ export function fromQuads(quads: Iterable<Quad>): Promise<Dataset>;
545
+
546
+ /** Load the engine. Afterwards the N-Quads text functions of `@factoidal/core/rdfjs` work. */
547
+ export function ready(): Promise<void>;
548
+
549
+ /** The engine's name tables: format names, entailment regimes, query outputs, pack syntaxes. */
550
+ export function engineTables(): Promise<Record<string, unknown>>;
551
+
479
552
  /** A SHACL validation-report term (see shaclValidate). */
480
553
  export interface ShaclValidateResult {
481
554
  conforms: boolean;
@@ -1240,7 +1313,7 @@ export function capabilities(): Promise<{
1240
1313
  entailment: boolean;
1241
1314
  /** queryHdt() (op queryHdt) is served. */
1242
1315
  hdt: boolean;
1243
- /** query()'s `sparql12` and DatasetHandle.query's (queryDataset12, datasetQuery12). */
1316
+ /** query()'s `sparql12` and Dataset.query's (queryDataset12, datasetQuery12). */
1244
1317
  sparql12: boolean;
1245
1318
  /** openStore() and the Store methods (storeOpen, storeHandleQuery). */
1246
1319
  store: boolean;
@@ -1250,7 +1323,7 @@ export function capabilities(): Promise<{
1250
1323
  profile: 'full' | string;
1251
1324
  /** The dispatch ABI version string of the loaded wasm module. */
1252
1325
  abiVersion: string;
1253
- /** openDataset()/DatasetHandle (datasetOpen/Query/Update/Serialize/Close) are present. */
1326
+ /** openDataset()/Dataset (datasetOpen/Query/Update/Serialize/Close) are present. */
1254
1327
  datasetHandles: boolean;
1255
1328
  /** parse()'s `{lenient:true}` (parseDocument) is present. */
1256
1329
  parseDiagnostics: boolean;
@@ -1329,6 +1402,10 @@ declare const _default: {
1329
1402
  canonicalize: typeof canonicalize;
1330
1403
  graphs: typeof graphs;
1331
1404
  canonicalHash: typeof canonicalHash;
1405
+ isomorphic: typeof isomorphic;
1406
+ fromQuads: typeof fromQuads;
1407
+ ready: typeof ready;
1408
+ engineTables: typeof engineTables;
1332
1409
  shaclValidate: typeof shaclValidate;
1333
1410
  shexValidate: typeof shexValidate;
1334
1411
  owlClosure: typeof owlClosure;
@@ -1380,9 +1457,8 @@ declare const _default: {
1380
1457
  vcCheckRelatedResourceDigests: typeof vcCheckRelatedResourceDigests;
1381
1458
  capabilities: typeof capabilities;
1382
1459
  Dataset: typeof Dataset;
1383
- DatasetHandle: typeof DatasetHandle;
1384
1460
  Store: typeof Store;
1385
- ParseError: typeof ParseError;
1461
+ FactoidalError: typeof FactoidalError;
1386
1462
  dataFactory: DataFactory;
1387
1463
  queryRaw: typeof queryRaw;
1388
1464
  version: string;
package/index.mjs CHANGED
@@ -51,6 +51,10 @@ export const serialize = api.serialize;
51
51
  export const canonicalize = api.canonicalize;
52
52
  export const graphs = api.graphs;
53
53
  export const canonicalHash = api.canonicalHash;
54
+ export const isomorphic = api.isomorphic;
55
+ export const fromQuads = api.fromQuads;
56
+ export const ready = api.ready;
57
+ export const engineTables = api.engineTables;
54
58
  export const openDataset = api.openDataset;
55
59
  export const closure = api.closure;
56
60
  export const formats = api.formats;
@@ -133,9 +137,8 @@ export const didKeyResolve = api.didKeyResolve;
133
137
  // Types, probes and metadata
134
138
  export const capabilities = api.capabilities;
135
139
  export const Dataset = api.Dataset;
136
- export const DatasetHandle = api.DatasetHandle;
137
140
  export const Store = api.Store;
138
- export const ParseError = api.ParseError;
141
+ export const FactoidalError = api.FactoidalError;
139
142
  export const dataFactory = api.dataFactory;
140
143
  export const version = api.version;
141
144
  export const engine = api.engine;
@@ -37,7 +37,7 @@
37
37
  // bytes change.
38
38
 
39
39
  // Stamped by formal/lean4/Wasm/build-wasm.sh step 9 -- do not hand-edit.
40
- const WASM_VERSION = "2347d124e551";
40
+ const WASM_VERSION = "ec99e3524f1a";
41
41
 
42
42
  import createModule from './l4factoidal.mjs';
43
43