@factoidal/core 0.11.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/bin/factoidal.mjs CHANGED
@@ -332,7 +332,12 @@ https://www.w3.org/TR/sparql11-service-description/`,
332
332
 
333
333
  usage: factoidal lws-serve DIR (--issuer IRI | --auth-config FILE | --insecure-no-auth)
334
334
  [--owner WEBID] [--root-acl FILE] [--audience IRI]
335
- [--port N] [--host ADDRESS] [--base IRI]
335
+ [--port N] [--host ADDRESS] [--base IRI] [--page-size N]
336
+ [--paginate-always] [--moves all|data|off]
337
+ [--no-linkset-put] [--unstable-search-results] [--merkle]
338
+ [--controller-inbox URL] [--delivery-limit N]
339
+ [--delivery-window SECONDS] [--redirects-max N]
340
+ [--storage snapshot|files]
336
341
 
337
342
  Starts a Node HTTP server that answers every request through the engine's
338
343
  lwsStep operation.
@@ -344,10 +349,88 @@ start. The engine decides the snapshot format and refuses a snapshot
344
349
  path with a dot segment or other traversal form
345
350
  (formal/lean4/L4Factoidal/LWS/Snapshot.lean).
346
351
 
352
+ With --storage files (off by default) a write costs the same whatever the
353
+ size of the storage: DIR holds one file per resource body (DIR/bodies/),
354
+ a journal of metadata changes (DIR/lws-journal) and a metadata file
355
+ (DIR/lws-files.json) that the journal is folded into from time to time.
356
+ A journal record is valid only when it is complete and its SHA-256
357
+ matches; a torn last record is ignored at start. An existing
358
+ DIR/lws-state.json is migrated at the first start and renamed to
359
+ lws-state.json.migrated. The engine decides every file name and every
360
+ byte (formal/lean4/L4Factoidal/LWS/FileLayout.lean); the storage is still
361
+ held in memory while the server runs
362
+ (docs/designissues/2026-10-22-lws-storage-on-disk.md).
363
+
364
+ Encryption at rest (off by default): when the environment variable
365
+ FACTOIDAL_LWS_KEY holds a 32-byte key (64 hex digits, or 43 characters of
366
+ base64url), the snapshot is sealed with ChaCha20-Poly1305 (HACL*) under a
367
+ new random nonce for every write, and authenticated when the storage is
368
+ opened. A wrong key, a changed or truncated file, a clear-text file with the
369
+ variable set, or a sealed file without it stops the start with a message.
370
+ It protects the file on disk only (formal/lean4/L4Factoidal/LWS/SnapshotSeal.lean).
371
+
347
372
  --port N the port to bind; 0 takes an ephemeral one (default 3000)
348
373
  --host ADDRESS the address to bind (default 127.0.0.1)
349
374
  --base IRI the absolute IRI the storage root maps to
350
375
  (default: the bound origin)
376
+ --page-size N the engine's container page size (config member pageSize;
377
+ default 100, 0 turns pagination off)
378
+ --paginate-always
379
+ present every container listing as paginated, also one
380
+ that fits on one page (config member paginateAlways;
381
+ default off): rel="first" and rel="last", no rel="next"
382
+ --moves all|data|off
383
+ which resources a client may move by changing their up
384
+ link (config member moves; default all): data resources
385
+ and containers, data resources only (the MoveResource
386
+ capability's appliesTo is [DataResource]), or none (no
387
+ MoveResource capability; a change of up answers 409)
388
+ --no-linkset-put
389
+ a linkset resource does not accept PUT (config member
390
+ linksetPut false): its Allow leaves PUT out and a PUT
391
+ answers 405 Method Not Allowed; PATCH still applies
392
+ --unstable-search-results
393
+ ANTICIPATORY AND UNSTABLE, off by default: a QUERY answer
394
+ of the type search names a results resource with
395
+ Content-Location (RFC 10008 section 2.3). lws10-index
396
+ defines no such resource; this is likely to change.
397
+ https://github.com/danbri/factoidal/issues/922
398
+ --merkle off by default: keep a SHA-256 digest of every resource
399
+ and a Merkle root of every container, and show them: an
400
+ RFC 9530 Repr-Digest field on GET of a resource, a
401
+ Factoidal-Merkle-Root field and listing member on a
402
+ container, and the storage root's digest in the storage
403
+ description (roots to the storage controller only under
404
+ authorization). Entity tags do not change. The hashes are
405
+ rebuilt when the storage is opened, not stored.
406
+ https://github.com/danbri/factoidal/issues/919
407
+ --controller-inbox URL
408
+ the storage controller's inbox (config member
409
+ controllerInbox; default none): each new access request
410
+ is announced there with a Create notification. The LWS
411
+ draft names no such inbox; this is a server choice.
412
+ https://github.com/danbri/factoidal/issues/920
413
+ --delivery-limit N
414
+ the most webhook deliveries one subscription gets in one
415
+ window (config member deliveryLimit; default 10, 0 turns
416
+ the limit off); the changes over it wait and go in one
417
+ batched notification when the window ends
418
+ https://github.com/danbri/factoidal/issues/915
419
+ --delivery-window SECONDS
420
+ the length of that window (config member deliveryWindow;
421
+ default 60)
422
+ --redirects-max N
423
+ the most previous identifiers of moved resources kept to
424
+ answer 301 Moved Permanently (config member redirectsMax;
425
+ default 256)
426
+ https://github.com/danbri/factoidal/issues/914
427
+ --storage snapshot|files
428
+ how DIR keeps the storage (config member storage;
429
+ default the environment variable FACTOIDAL_LWS_STORAGE,
430
+ else snapshot): one snapshot file written whole after
431
+ each change, or one file per body with a journal, so a
432
+ write costs the same whatever the storage size
433
+ https://github.com/danbri/factoidal/issues/918
351
434
 
352
435
  Authorization (LWS editor's draft, section Authorization; one of the three
353
436
  is required):
@@ -365,7 +448,8 @@ is required):
365
448
  that gives this agent Read, Write and Control
366
449
  --root-acl FILE a Turtle root ACL for a new storage instead
367
450
  --audience IRI the aud value tokens must carry (default: the
368
- storage root IRI)
451
+ storage IRI, BASE.well-known/lws-storage, which
452
+ every lws#storage link names)
369
453
 
370
454
  Requests carry an access token as "Authorization: Bearer TOKEN" (RFC 6750).
371
455
  The engine verifies its JWS signature (ES256, RS256 or EdDSA, with the
@@ -397,6 +481,14 @@ start. The engine decides the snapshot format and refuses a snapshot
397
481
  path with a dot segment or other traversal form
398
482
  (formal/lean4/L4Factoidal/LWS/Snapshot.lean).
399
483
 
484
+ Encryption at rest (off by default): when the environment variable
485
+ FACTOIDAL_LWS_KEY holds a 32-byte key (64 hex digits, or 43 characters of
486
+ base64url), the snapshot is sealed with ChaCha20-Poly1305 (HACL*) under a
487
+ new random nonce for every write, and authenticated when the storage is
488
+ opened. A wrong key, a changed or truncated file, a clear-text file with the
489
+ variable set, or a sealed file without it stops the start with a message.
490
+ It protects the file on disk only (formal/lean4/L4Factoidal/LWS/SnapshotSeal.lean).
491
+
400
492
  --port N the port to bind; 0 takes an ephemeral one (default 3000)
401
493
  --host ADDRESS the address to bind (default 127.0.0.1)
402
494
  --base IRI the absolute IRI the storage root maps to
@@ -505,7 +597,8 @@ const VALUE_OPTIONS = {
505
597
  compact: new Set([]),
506
598
  serve: new Set(['port', 'host', 'query-path', 'gsp-path', 'file', 'layout', 'tdb2']),
507
599
  'lws-serve': new Set(['port', 'host', 'base', 'issuer', 'auth-config', 'owner', 'root-acl',
508
- 'audience']),
600
+ 'audience', 'page-size', 'moves', 'controller-inbox', 'delivery-limit', 'delivery-window',
601
+ 'redirects-max', 'storage']),
509
602
  'solid-serve': new Set(['port', 'host', 'base', 'owner', 'issuer', 'auth-config']),
510
603
  'solid-client': new Set(['body', 'file', 'content-type', 'interpret'])
511
604
  }
@@ -1198,6 +1291,20 @@ function servePort (options, fallback) {
1198
1291
  return port
1199
1292
  }
1200
1293
 
1294
+ // --page-size N: a CLI number, passed unchanged as the engine config member
1295
+ // `pageSize`; what a page holds is decided in L4Factoidal/LWS/Pagination.lean.
1296
+ function pageSizeOption (value) {
1297
+ return natOption('page-size', value)
1298
+ }
1299
+
1300
+ function natOption (flag, value) {
1301
+ const n = Number(value)
1302
+ if (!Number.isInteger(n) || n < 0) {
1303
+ throw new UsageError(`--${flag} must be a non-negative integer, not "${value}"`)
1304
+ }
1305
+ return n
1306
+ }
1307
+
1201
1308
  function serveHost (options) {
1202
1309
  if (options.host === undefined) return '127.0.0.1'
1203
1310
  if (typeof options.host !== 'string' || options.host.length === 0) {
@@ -1326,6 +1433,19 @@ async function commandLwsServe (positional, options) {
1326
1433
  port: servePort(options, 3000),
1327
1434
  host: serveHost(options),
1328
1435
  ...(typeof options.base === 'string' ? { baseIri: options.base } : {}),
1436
+ ...(options['page-size'] !== undefined ? { pageSize: pageSizeOption(options['page-size']) } : {}),
1437
+ ...(options['unstable-search-results'] === true ? { searchResults: true } : {}),
1438
+ ...(options.merkle === true ? { merkle: true } : {}),
1439
+ // Passed unread as config members; the engine decides what they mean
1440
+ // and refuses a value it does not know (Wasm/Ops/Lws.lean).
1441
+ ...(options['paginate-always'] === true ? { paginateAlways: true } : {}),
1442
+ ...(typeof options.moves === 'string' ? { moves: options.moves } : {}),
1443
+ ...(options['no-linkset-put'] === true ? { linksetPut: false } : {}),
1444
+ ...(typeof options['controller-inbox'] === 'string' ? { controllerInbox: options['controller-inbox'] } : {}),
1445
+ ...(options['delivery-limit'] !== undefined ? { deliveryLimit: natOption('delivery-limit', options['delivery-limit']) } : {}),
1446
+ ...(options['delivery-window'] !== undefined ? { deliveryWindow: natOption('delivery-window', options['delivery-window']) } : {}),
1447
+ ...(options['redirects-max'] !== undefined ? { redirectsMax: natOption('redirects-max', options['redirects-max']) } : {}),
1448
+ ...(typeof options.storage === 'string' ? { storage: options.storage } : {}),
1329
1449
  ...(insecure ? { insecureNoAuth: true } : { auth }),
1330
1450
  ...(typeof options.owner === 'string' ? { owner: options.owner } : {}),
1331
1451
  ...(typeof options['root-acl'] === 'string'
@@ -1338,6 +1458,14 @@ async function commandLwsServe (positional, options) {
1338
1458
  if (error instanceof LwsHostError && error.unknownOp) {
1339
1459
  return notWired('lws-serve', error.message)
1340
1460
  }
1461
+ // The engine refused to open the storage (a bad configuration, or a
1462
+ // snapshot it could not read or authenticate, issue 921), or the state
1463
+ // file is not JSON: the message is the whole answer.
1464
+ if (error !== null && typeof error === 'object' &&
1465
+ (error.envelope !== undefined || error.stateFile === true)) {
1466
+ err(`factoidal lws-serve: ${error.envelope !== undefined ? error.envelope.error : error.message}`)
1467
+ return EXIT_FAILURE
1468
+ }
1341
1469
  throw error
1342
1470
  }
1343
1471
  if (!quiet) {
@@ -1385,6 +1513,14 @@ async function commandSolidServe (positional, options) {
1385
1513
  if (error instanceof SolidServerHostError && error.unknownOp) {
1386
1514
  return notWired('solid-serve', error.message)
1387
1515
  }
1516
+ // The engine refused to open the storage (a bad configuration, or a
1517
+ // snapshot it could not read or authenticate, issue 921), or the state
1518
+ // file is not JSON: the message is the whole answer.
1519
+ if (error !== null && typeof error === 'object' &&
1520
+ (error.envelope !== undefined || error.stateFile === true)) {
1521
+ err(`factoidal solid-serve: ${error.envelope !== undefined ? error.envelope.error : error.message}`)
1522
+ return EXIT_FAILURE
1523
+ }
1388
1524
  throw error
1389
1525
  }
1390
1526
  if (!quiet) {
package/browser.d.ts CHANGED
@@ -5,6 +5,8 @@
5
5
  // system, so a path string rejects.
6
6
 
7
7
  export type {
8
+ FactoidalErrorCode,
9
+ FactoidalErrorPosition,
8
10
  NamedNode, BlankNode, Literal, Variable, DefaultGraph, Quad, Term, DataFactory,
9
11
  DataFormat, EntailRegime, Bindings, DataInput, QueryInput, ParseOptions,
10
12
  QueryOptions, SerializeOptions, StoreReader, OpenStoreOptions,
@@ -89,9 +91,8 @@ export {
89
91
  didKeyResolve,
90
92
  capabilities,
91
93
  Dataset,
92
- DatasetHandle,
93
94
  Store,
94
- ParseError,
95
+ FactoidalError,
95
96
  dataFactory,
96
97
  version,
97
98
  engine,
package/browser.js CHANGED
@@ -153,9 +153,8 @@ export const didKeyResolve = api.didKeyResolve;
153
153
  // Types, probes and metadata
154
154
  export const capabilities = api.capabilities;
155
155
  export const Dataset = api.Dataset;
156
- export const DatasetHandle = api.DatasetHandle;
157
156
  export const Store = api.Store;
158
- export const ParseError = api.ParseError;
157
+ export const FactoidalError = api.FactoidalError;
159
158
  export const dataFactory = api.dataFactory;
160
159
  export const version = api.version;
161
160
  export const engine = api.engine;
package/fn.js CHANGED
@@ -252,7 +252,7 @@ function builder() {
252
252
  let finished = false;
253
253
  return {
254
254
  addChunk(chunk) {
255
- 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' });
256
256
  if (chunk && typeof chunk.termType === 'string') {
257
257
  pending.push(chunk); // a single Quad
258
258
  } else {
@@ -260,7 +260,7 @@ function builder() {
260
260
  }
261
261
  },
262
262
  finish() {
263
- if (finished) throw new Error('builder: finish() already called');
263
+ if (finished) throw new engineApi.FactoidalError('builder: finish() already called', { code: 'op.failed' });
264
264
  finished = true;
265
265
  const result = makeFnDataset(pending);
266
266
  pending = null; // release; no further mutation is possible anyway
@@ -354,24 +354,20 @@ async function query(ds, sparql, options) {
354
354
  * per-graph closure semantics that the underlying query() does not
355
355
  * define today.
356
356
  */
357
- // The regime names, their case-insensitive aliases and the x-ikl-* refusal
358
- // are the engine's tables (op engineTables): no host copy
357
+ // The regime names and their case-insensitive aliases are the engine's tables
358
+ // (op engineTables): no host copy
359
359
  // (https://github.com/danbri/factoidal/issues/894 item 5). A name with no
360
- // alias passes through unchanged and engineApi.query reports it.
361
- async function regimeOf(regime, who) {
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) {
362
363
  const t = await engineApi.engineTables();
363
364
  const r = regime == null ? 'none' : String(regime);
364
- for (const [prefix, message] of Object.entries(t.refusedRegimePrefixes)) {
365
- if (r.toLowerCase().startsWith(prefix)) {
366
- throw new TypeError(message.replace('WHO', who).replace('REGIME', r));
367
- }
368
- }
369
365
  return t.entailAliases[r.toLowerCase()] || regime;
370
366
  }
371
367
 
372
368
  async function entail(ds, regime) {
373
369
  assertFnDataset(ds, 'entail');
374
- const entailRegime = await regimeOf(regime, 'entail');
370
+ const entailRegime = await regimeOf(regime);
375
371
  const rows = await engineApi.query((await engineOf(ds)), 'SELECT ?s ?p ?o WHERE { ?s ?p ?o }',
376
372
  { entail: entailRegime });
377
373
  return makeFnDataset(
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(
@@ -1257,7 +1313,7 @@ export function capabilities(): Promise<{
1257
1313
  entailment: boolean;
1258
1314
  /** queryHdt() (op queryHdt) is served. */
1259
1315
  hdt: boolean;
1260
- /** query()'s `sparql12` and DatasetHandle.query's (queryDataset12, datasetQuery12). */
1316
+ /** query()'s `sparql12` and Dataset.query's (queryDataset12, datasetQuery12). */
1261
1317
  sparql12: boolean;
1262
1318
  /** openStore() and the Store methods (storeOpen, storeHandleQuery). */
1263
1319
  store: boolean;
@@ -1267,7 +1323,7 @@ export function capabilities(): Promise<{
1267
1323
  profile: 'full' | string;
1268
1324
  /** The dispatch ABI version string of the loaded wasm module. */
1269
1325
  abiVersion: string;
1270
- /** openDataset()/DatasetHandle (datasetOpen/Query/Update/Serialize/Close) are present. */
1326
+ /** openDataset()/Dataset (datasetOpen/Query/Update/Serialize/Close) are present. */
1271
1327
  datasetHandles: boolean;
1272
1328
  /** parse()'s `{lenient:true}` (parseDocument) is present. */
1273
1329
  parseDiagnostics: boolean;
@@ -1401,9 +1457,8 @@ declare const _default: {
1401
1457
  vcCheckRelatedResourceDigests: typeof vcCheckRelatedResourceDigests;
1402
1458
  capabilities: typeof capabilities;
1403
1459
  Dataset: typeof Dataset;
1404
- DatasetHandle: typeof DatasetHandle;
1405
1460
  Store: typeof Store;
1406
- ParseError: typeof ParseError;
1461
+ FactoidalError: typeof FactoidalError;
1407
1462
  dataFactory: DataFactory;
1408
1463
  queryRaw: typeof queryRaw;
1409
1464
  version: string;
package/index.mjs CHANGED
@@ -137,9 +137,8 @@ export const didKeyResolve = api.didKeyResolve;
137
137
  // Types, probes and metadata
138
138
  export const capabilities = api.capabilities;
139
139
  export const Dataset = api.Dataset;
140
- export const DatasetHandle = api.DatasetHandle;
141
140
  export const Store = api.Store;
142
- export const ParseError = api.ParseError;
141
+ export const FactoidalError = api.FactoidalError;
143
142
  export const dataFactory = api.dataFactory;
144
143
  export const version = api.version;
145
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 = "15eb8d770829";
40
+ const WASM_VERSION = "ec99e3524f1a";
41
41
 
42
42
  import createModule from './l4factoidal.mjs';
43
43
 
Binary file
@@ -2,17 +2,17 @@
2
2
  "engine": "lean4",
3
3
  "note": "The Lean 4 (L4Factoidal) engine build provenance and claims: the only engine of @factoidal/core since 0.9.0 (issue 713). This committed file is a placeholder. tools/fetch-builds.sh --set web (through tools/stamp-l4-version.mjs) writes the provenance members from the danbri/factoidal-builds release that built the wasm; formal/lean4/Wasm/build-wasm.sh step 9 writes them for a local build. Do not commit a stamped copy.",
4
4
  "stamped": true,
5
- "release": "danbri/factoidal-builds build-d83100e6ec03-web",
6
- "releaseRun": "https://github.com/danbri/factoidal-builds/actions/runs/39574221615",
7
- "gitSha": "d83100e6ec038328a71a245e9fd10fbd768a38a5",
8
- "builtAt": "2026-10-21T09:44:37Z",
5
+ "release": "danbri/factoidal-builds build-d9cc50253669-web",
6
+ "releaseRun": "https://github.com/danbri/factoidal-builds/actions/runs/39759947727",
7
+ "gitSha": "d9cc502536698eaf40ab74f7fb5de194c81f104f",
8
+ "builtAt": "2026-10-22T10:39:28Z",
9
9
  "leanToolchain": "leanprover/lean4:v4.33.1",
10
10
  "emscripten": "6.0.8",
11
11
  "abiVersion": "1",
12
12
  "engineVersion": "l4factoidal-wasm 0.2.0 (dispatch: RDF parse/serialize, SPARQL query+update, RDFS/OWL-RL closures, RDFC-1.0, CL/IKL)",
13
13
  "version": "l4factoidal-wasm 0.2.0 (dispatch: RDF parse/serialize, SPARQL query+update, RDFS/OWL-RL closures, RDFC-1.0, CL/IKL)",
14
- "wasmSha256": "15eb8d770829f3f07dad4f8eabbbaf4503f55104a1803cb87209d05d093dc642",
15
- "wasmBytes": 12362890,
14
+ "wasmSha256": "ec99e3524f1a78667c2b41801556a9d25d65b01cea7afa60ec14c33523da3978",
15
+ "wasmBytes": 13212135,
16
16
  "claims": {
17
17
  "source": "formal/lean4 (L4Factoidal): no sorry, no user axioms, no native_decide; W3C behaviour pinned by build-time #guard",
18
18
  "suitesAtBuildSha": "see docs/test-results and formal/lean4/PORT_NOTES.md at gitSha"