@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/CHANGELOG.md +382 -0
- package/README.md +63 -3
- package/api.d.ts +2 -3
- package/api.mjs +2 -2
- package/bin/factoidal.mjs +139 -3
- package/browser.d.ts +3 -2
- package/browser.js +1 -2
- package/fn.js +8 -12
- package/index.d.ts +83 -28
- package/index.mjs +1 -2
- package/l4-assets/l4factoidal.js +1 -1
- package/l4-assets/l4factoidal.wasm +0 -0
- package/l4-assets/version.json +6 -6
- package/lib/api.mjs +93 -203
- package/lib/dataset.mjs +40 -45
- package/lib/delta-log-hosts.mjs +9 -8
- package/lib/errors.mjs +97 -0
- package/lib/lean-entry.mjs +15 -10
- package/lib/store.mjs +4 -5
- package/lib/version.mjs +1 -1
- package/lws/README.md +70 -3
- package/lws/persist.mjs +237 -6
- package/lws/server.mjs +271 -9
- package/package.json +2 -1
- package/rdfjs.mjs +4 -2
- package/solid/server/index.mjs +7 -1
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
358
|
-
//
|
|
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
|
|
361
|
-
|
|
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
|
|
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
|
-
*
|
|
88
|
-
* `
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
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
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
*
|
|
101
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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()/
|
|
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
|
-
|
|
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
|
|
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;
|
package/l4-assets/l4factoidal.js
CHANGED
|
Binary file
|
package/l4-assets/version.json
CHANGED
|
@@ -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-
|
|
6
|
-
"releaseRun": "https://github.com/danbri/factoidal-builds/actions/runs/
|
|
7
|
-
"gitSha": "
|
|
8
|
-
"builtAt": "2026-10-
|
|
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": "
|
|
15
|
-
"wasmBytes":
|
|
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"
|