@factoidal/core 0.7.1 → 0.11.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 +616 -1
- package/NOTICE +2 -21
- package/README.md +123 -993
- package/api.d.ts +53 -0
- package/api.js +5 -0
- package/api.mjs +56 -0
- package/bin/engine.mjs +38 -19
- package/bin/ext.mjs +16 -20
- package/bin/factoidal.mjs +667 -118
- package/bin/pack.mjs +37 -0
- package/bin/serve-worker.mjs +32 -0
- package/bin/serve.mjs +423 -0
- package/bin/store-worker-host.mjs +1 -1
- package/bin/store.mjs +1 -1
- package/browser.d.ts +114 -507
- package/browser.js +155 -2257
- package/docs/fuseki-replacement.md +108 -0
- package/fn.d.ts +25 -96
- package/fn.js +131 -240
- package/index.d.ts +620 -260
- package/index.js +4 -234
- package/index.mjs +180 -84
- package/l4-assets/l4factoidal.js +47 -7
- package/l4-assets/l4factoidal.mjs +1 -1
- package/l4-assets/l4factoidal.wasm +0 -0
- package/l4-assets/version.json +11 -7
- package/lib/api.mjs +2337 -0
- package/lib/browser-engine.mjs +69 -0
- package/lib/dataset.mjs +471 -0
- package/lib/delta-log-hosts.mjs +191 -0
- package/lib/lean-entry.mjs +213 -0
- package/lib/pure.mjs +98 -0
- package/lib/store.mjs +115 -0
- package/lib/version.mjs +5 -0
- package/lws/README.md +137 -0
- package/lws/persist.mjs +78 -0
- package/lws/server.mjs +416 -0
- package/package.json +59 -61
- package/rdfjs.js +5 -536
- package/rdfjs.mjs +517 -0
- package/sample-store.d.ts +8 -16
- package/sample-store.mjs +39 -29
- package/solid/client/README.md +100 -0
- package/solid/client/index.mjs +293 -0
- package/solid/server/README.md +96 -0
- package/solid/server/index.mjs +395 -0
- package/store-host/deno.mjs +8 -0
- package/store-host/index.mjs +12 -0
- package/store-host/node.mjs +8 -0
- package/xmpp/README.md +107 -0
- package/xmpp/index.mjs +229 -0
- package/browser-wasm.js +0 -872
- package/factoidal-npm-entry.js +0 -15477
- package/factoidal-npm-entry.wasm.assets/code-5a7fc68f2ab1323718b8.wasm +0 -0
- package/factoidal-npm-entry.wasm.js +0 -455
- package/factoidal.js +0 -13118
- package/factoidal.wasm.assets/code-bbe6099bfb5b10c4c3ab.wasm +0 -0
- package/factoidal.wasm.js +0 -457
- package/hacl-init.js +0 -92
- package/hacl-wasm/FStar.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum25519_51.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum_Base.wasm +0 -0
- package/hacl-wasm/Hacl_Curve25519_51.wasm +0 -0
- package/hacl-wasm/Hacl_Ed25519.wasm +0 -0
- package/hacl-wasm/Hacl_Ed25519_PrecompTable.wasm +0 -0
- package/hacl-wasm/Hacl_Hash_Base.wasm +0 -0
- package/hacl-wasm/Hacl_Hash_SHA2.wasm +0 -0
- package/hacl-wasm/Hacl_IntTypes_Intrinsics.wasm +0 -0
- package/hacl-wasm/LowStar_Endianness.wasm +0 -0
- package/hacl-wasm/WasmSupport.wasm +0 -0
- package/hacl-wasm/api.js +0 -775
- package/hacl-wasm/api.json +0 -3787
- package/hacl-wasm/layouts.json +0 -1
- package/hacl-wasm/loader.js +0 -568
- package/hacl-wasm/shell.js +0 -12
- package/l4-core.d.ts +0 -159
- package/l4-core.js +0 -188
- package/l4.d.ts +0 -50
- package/l4.js +0 -104
- package/lib/api.js +0 -2300
- package/lib/engine-js.js +0 -165
- package/lib/engine-wasm.js +0 -300
- package/sample-store/CURRENT +0 -1
- package/sample-store/gen-1/manifest.sbm2 +0 -0
- package/sample-store/gen-1/manifest.tsv +0 -14
- package/sample-store/gen-1/predicate-0.ibk3 +0 -0
- package/sample-store/gen-1/predicate-0.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-0.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-0.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-0.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-0.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-0.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-0.ibk3.tli1.merkle +0 -0
- package/sample-store/gen-1/predicate-1.ibk3 +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.merkle +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.oli2.merkle +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-1.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-1.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-10.ibk3 +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-10.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.oli2.merkle +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-10.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-10.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-11.ibk3 +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.merkle +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-11.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-11.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-11.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-12.ibk3 +0 -0
- package/sample-store/gen-1/predicate-12.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-12.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-12.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-12.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-12.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-12.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-12.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-2.ibk3 +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-2.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-2.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.sri2.merkle +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-2.ibk3.tli1.merkle +0 -0
- package/sample-store/gen-1/predicate-3.ibk3 +0 -0
- package/sample-store/gen-1/predicate-3.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-3.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-3.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-3.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-3.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-3.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-3.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-4.ibk3 +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.merkle +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-4.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-4.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-4.ibk3.tli1.merkle +0 -2
- package/sample-store/gen-1/predicate-5.ibk3 +0 -0
- package/sample-store/gen-1/predicate-5.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-5.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-5.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-5.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-5.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-5.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-5.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-6.ibk3 +0 -0
- package/sample-store/gen-1/predicate-6.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-6.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-6.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-6.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-6.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-6.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-6.ibk3.tli1.merkle +0 -0
- package/sample-store/gen-1/predicate-7.ibk3 +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.merkle +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.oli2.merkle +0 -2
- package/sample-store/gen-1/predicate-7.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-7.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-7.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-8.ibk3 +0 -0
- package/sample-store/gen-1/predicate-8.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-8.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-8.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-8.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-8.ibk3.sri2.merkle +0 -1
- package/sample-store/gen-1/predicate-8.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-8.ibk3.tli1.merkle +0 -1
- package/sample-store/gen-1/predicate-9.ibk3 +0 -0
- package/sample-store/gen-1/predicate-9.ibk3.merkle +0 -1
- package/sample-store/gen-1/predicate-9.ibk3.oli2 +0 -0
- package/sample-store/gen-1/predicate-9.ibk3.oli2.merkle +0 -1
- package/sample-store/gen-1/predicate-9.ibk3.sri2 +0 -0
- package/sample-store/gen-1/predicate-9.ibk3.sri2.merkle +0 -3
- package/sample-store/gen-1/predicate-9.ibk3.tli1 +0 -0
- package/sample-store/gen-1/predicate-9.ibk3.tli1.merkle +0 -2
- package/select.d.ts +0 -116
- package/select.js +0 -492
- package/version.json +0 -102
- package/wasm.d.ts +0 -130
- package/wasm.js +0 -158
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,620 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0 — 2026-10-21
|
|
4
|
+
|
|
5
|
+
No RDF, SPARQL, datatype or IRI decision is made in JavaScript any more
|
|
6
|
+
(https://github.com/danbri/factoidal/issues/894; owner, 2026-10-21: "The ja
|
|
7
|
+
leak fix is p0"). The engine parses, writes, escapes, compares and chooses
|
|
8
|
+
datatypes; the JavaScript copies term components and calls ops.
|
|
9
|
+
`tools/host-purity-lint.sh` group 3 scans the npm and hub JavaScript for RDF
|
|
10
|
+
syntax and runs in `npm test`.
|
|
11
|
+
|
|
12
|
+
### Breaking changes
|
|
13
|
+
|
|
14
|
+
These shapes could not be kept:
|
|
15
|
+
|
|
16
|
+
- `shexValidate` and `Dataset.validateShex`: a focus or shape given as a
|
|
17
|
+
string is read as one RDF 1.2 N-Triples term (`'<http://…>'` or `'_:b'`);
|
|
18
|
+
a bare IRI string is refused. Every API that takes a term as a string
|
|
19
|
+
now reads it through one engine reader (`TermJson.readTermString`), so
|
|
20
|
+
`_:x` is always a blank node and `<…>` always an IRI.
|
|
21
|
+
- `graphs()` returns `[NamedNode|BlankNode, Dataset]` pairs, not
|
|
22
|
+
`[string, Dataset]`, so an IRI graph and a blank-node graph with the same
|
|
23
|
+
text are never confused.
|
|
24
|
+
- A literal with a language and a direction has datatype
|
|
25
|
+
`rdf:dirLangString` (RDF 1.2 Concepts; RDF/JS Data Model); before, the
|
|
26
|
+
RDF/JS default gave `rdf:langString`.
|
|
27
|
+
|
|
28
|
+
- `factoidal lws-serve` and `@factoidal/core/lws` enforce storage-server
|
|
29
|
+
authorization from the LWS editor's draft (401 with
|
|
30
|
+
`WWW-Authenticate: Bearer resource_metadata`, the RFC 9728 metadata
|
|
31
|
+
document, Bearer JWT access tokens checked for signature, `iss`, `aud`,
|
|
32
|
+
`exp`, `nbf` and `iat`, then Web Access Control). `lws-serve` needs
|
|
33
|
+
`--issuer IRI`, `--auth-config FILE` or, for development only,
|
|
34
|
+
`--insecure-no-auth`; `listen()` with neither `auth` nor
|
|
35
|
+
`insecureNoAuth` serves only what the ACLs give to the public. New op
|
|
36
|
+
`lwsFetched`. https://github.com/danbri/factoidal/issues/886
|
|
37
|
+
|
|
38
|
+
- The N-Quads text functions of `@factoidal/core/rdfjs` (`nquadsToQuads`,
|
|
39
|
+
`nquadsLineToQuad`, `quadsToNQuads`, `quadToNQuads`, `termToNQuads`,
|
|
40
|
+
`DatasetCore.fromNQuads`, `DatasetCore.prototype.toNQuads`) call the engine.
|
|
41
|
+
They throw until the engine is loaded: `await ready()` (new) or any other
|
|
42
|
+
`@factoidal/core` call first. `nquadsToQuads` now rejects a line the
|
|
43
|
+
N-Quads grammar rejects (before, `<http://e/a b>` was read as an IRI), and
|
|
44
|
+
the writers escape as the engine's canonical N-Triples form does (before,
|
|
45
|
+
`\b`, `\f` and other control characters were written raw).
|
|
46
|
+
- A JavaScript number answered by a registered extension function becomes
|
|
47
|
+
`xsd:integer` only for an integer within ±(2^53 − 1); every other number is
|
|
48
|
+
an `xsd:double` in the XSD 1.1 canonical form (`1e21` is `"1.0E21"`, before
|
|
49
|
+
`"1e+21"^^xsd:integer`; `0.1` is `"1.0E-1"`, before `"0.1"`). `NaN`,
|
|
50
|
+
`Infinity` and `-Infinity` answer `"NaN"`, `"INF"`, `"-INF"`; before they
|
|
51
|
+
were the §17.6 error. A `bigint` answers `xsd:integer`
|
|
52
|
+
(`L4Factoidal.XSD.JsValue.jsLiteral`).
|
|
53
|
+
- `queryHdt` answers a Dataset for CONSTRUCT and DESCRIBE; before it threw a
|
|
54
|
+
TypeError chosen by a regular expression over the query text.
|
|
55
|
+
- `Literal` has a `direction` member (`''`, `'ltr'`, `'rtl'`), and a
|
|
56
|
+
directional language-tagged string from the engine keeps its direction and
|
|
57
|
+
its `rdf:dirLangString` datatype; `equals` compares it.
|
|
58
|
+
|
|
59
|
+
### New
|
|
60
|
+
|
|
61
|
+
- `isomorphic(a, b)`: dataset isomorphism by the engine
|
|
62
|
+
(`Dataset.isomorphicOutcome`, blank nodes in triple terms included).
|
|
63
|
+
`@factoidal/core/fn`'s `equals` uses it.
|
|
64
|
+
- `fromQuads(quads)`: a Dataset from RDF/JS quads, sent as term components.
|
|
65
|
+
- `ready()`: load the engine. `engineTables()`: the engine's format,
|
|
66
|
+
regime, query-output and pack-syntax name tables.
|
|
67
|
+
- Ops (formal/lean4/Wasm/Ops/Handles.lean): `datasetQuadsJson`,
|
|
68
|
+
`datasetAddQuadsJson`, `datasetDeleteQuadsJson`, `datasetFromQuadsJson`,
|
|
69
|
+
`datasetGraphNames`, `datasetGraphAsDefault`, `datasetIsomorphic`,
|
|
70
|
+
`parseQuadsJson`, `quadsToNQuads`, `termsToNTriples`, `resolveIri`,
|
|
71
|
+
`queryForm`, `engineTables`; `datasetMatch`/`datasetMatchCount` take term
|
|
72
|
+
objects. Theorems: `TermJson.decodeTerm_encodeTerm`,
|
|
73
|
+
`TermJson.decodeQuad_encodeQuad`, `TermJson.mem_quadList_default`,
|
|
74
|
+
`TermJson.mem_quadList_named`, `TermJson.toCanonicalNQuads_eq_quadList`,
|
|
75
|
+
`TermJson.quadPage_append`, `XSD.JsValue.doubleLexical_inLexicalSpace`,
|
|
76
|
+
`XSD.JsValue.integerLexical_inLexicalSpace`,
|
|
77
|
+
`XSD.JsValue.jsLiteral_number_double`, `XSD.JsValue.jsLiteral_number_integer`.
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- An extension function may answer an exact term: an RDF/JS term
|
|
82
|
+
(Literal, NamedNode, BlankNode, or a Quad as an RDF 1.2 triple term), or
|
|
83
|
+
`{value, datatype}`, or `{value, language, direction?}`. The lexical form
|
|
84
|
+
and the datatype IRI are kept as given, ill-typed literals included (RDF
|
|
85
|
+
1.1 Concepts §3.3). The engine checks only what RDF requires: absolute
|
|
86
|
+
IRIs, a well-formed BCP 47 language tag, direction `ltr` or `rtl`; a term
|
|
87
|
+
that fails is the §17.6 error for that call. A plain JS number still gets
|
|
88
|
+
its datatype from `jsLiteral`. Theorem `TermJson.extAnswerJson_encodeTerm`:
|
|
89
|
+
an app-supplied term is the answer, unchanged.
|
|
90
|
+
|
|
91
|
+
### Performance
|
|
92
|
+
|
|
93
|
+
- Dataset iteration reads the quads from the engine as one byte region (op
|
|
94
|
+
`datasetQuadsBin`: a term table in the delta-log v2 term codec and four
|
|
95
|
+
u32 indices per quad), decoded in JavaScript with `DataView` and
|
|
96
|
+
`TextDecoder`. 60,000 quads: 178–205 ms under load, against 214–280 ms in
|
|
97
|
+
0.10.0 and about the time of the N-Quads writer op in the same run.
|
|
98
|
+
Theorems `decodeBin_quadsBin` and `quadsBin_binSpec`
|
|
99
|
+
(`formal/lean4/L4Factoidal/RDF/QuadsBinTheorems.lean`) prove the region
|
|
100
|
+
reads back as exactly the dataset's quads, so no run-time check is made.
|
|
101
|
+
https://github.com/danbri/factoidal/issues/900
|
|
102
|
+
|
|
103
|
+
### Fixed
|
|
104
|
+
|
|
105
|
+
- `graphs()` (both APIs) no longer merges an IRI graph and a blank-node graph
|
|
106
|
+
whose strings are equal.
|
|
107
|
+
- `Dataset` iteration builds its quads from the engine's term objects; it no
|
|
108
|
+
longer parses N-Quads text in JavaScript.
|
|
109
|
+
- The Solid client resolves a relative target against `baseIri` by RFC 3986
|
|
110
|
+
§5.2 (op `resolveIri`); before it concatenated strings.
|
|
111
|
+
- `factoidal query` table cells and `factoidal inspect` graph names are the
|
|
112
|
+
engine's canonical N-Triples forms (escaped); `factoidal pack` reads its
|
|
113
|
+
suffix table from the engine and detects the syntax of a file with an
|
|
114
|
+
unknown suffix from its content.
|
|
115
|
+
|
|
116
|
+
## 0.10.0 — 2026-10-21
|
|
117
|
+
|
|
118
|
+
### Breaking changes
|
|
119
|
+
|
|
120
|
+
One wasm carries every capability (owner, 2026-10-20: "all this stuff goes
|
|
121
|
+
into a single mod we call Factoidal/core and a dingle wasm"):
|
|
122
|
+
|
|
123
|
+
- The duplicate smaller wasm (`l4-assets/l4factoidal-core.{wasm,mjs,js}`) and
|
|
124
|
+
its `./core` and `./core-browser` subpaths are gone. `l4factoidal.wasm`
|
|
125
|
+
serves every function of the package; import `@factoidal/core`.
|
|
126
|
+
`l4-assets/version.json` has no `coreWasmSha256`/`coreWasmBytes`.
|
|
127
|
+
- No demonstration triples ship (owner: "All the CLIs are still bundled i
|
|
128
|
+
just didn't want any actual demo triples cluttering the file"): the
|
|
129
|
+
4,434-triple `sample-store/` directory is gone. `factoidal sample-store
|
|
130
|
+
[DIR]` and `sampleStorePath([dir])` now pack 15 triples held in
|
|
131
|
+
`sample-store.mjs` into a store under DIR or the temporary directory and
|
|
132
|
+
return its path; `sampleStoreFacts` describes that store.
|
|
133
|
+
- The demonstration functions `matrixDeterminant`, `matrixScalarProduct`,
|
|
134
|
+
`matrixVectorProduct`, `matrixOuterProduct`, `sigmoidPoints` and
|
|
135
|
+
`sigmoidFormulaMathml` (and `capabilities().matrix`/`.sigmoid`) left the
|
|
136
|
+
JavaScript API, `@factoidal/core/fn` and the types. The Lean ops stay and
|
|
137
|
+
answer through `loadEngine().call(op, args)`.
|
|
138
|
+
|
|
139
|
+
One `Dataset` class whose data stays in the engine
|
|
140
|
+
(https://github.com/danbri/factoidal/issues/882 item 1):
|
|
141
|
+
|
|
142
|
+
- `Dataset` is a handle into the engine's dataset store; it no longer holds
|
|
143
|
+
N-Quads text. `new Dataset(...)` and `Dataset.fromNQuads(...)` are gone:
|
|
144
|
+
make one with `parse`, `openDataset` or another producer. It still implements RDF/JS DatasetCore:
|
|
145
|
+
`add`, `delete`, `has` and `match` run in the engine (`match` and `has` use
|
|
146
|
+
its indexes; `match` answers a new Dataset). The in-memory RDF/JS DatasetCore is
|
|
147
|
+
`DatasetCore` in `@factoidal/core/rdfjs` (that subpath no longer exports
|
|
148
|
+
`Dataset`).
|
|
149
|
+
- `DatasetHandle` is a deprecated alias of `Dataset` (removed in 0.11.0).
|
|
150
|
+
`handle.close()`, `handle.closed` and `handle.toDataset()` are replaced by
|
|
151
|
+
`dispose()`, `disposed` and the Dataset itself. Using a disposed Dataset
|
|
152
|
+
throws a `TypeError`.
|
|
153
|
+
- `openDataset` answers a `Dataset` (a Dataset argument is cloned).
|
|
154
|
+
- `coreRdfsClosure`/`rhoDfClosure` and `rdfsPlusClosure` answer a `Dataset`
|
|
155
|
+
(before: `{ok, ntriples}` and `{ok, ntriples, rounds}`; `rounds` is gone).
|
|
156
|
+
`fn.coreRdfsClosure` and `fn.rdfsPlusClosure` drop `rounds` too.
|
|
157
|
+
- Text input with no `format` is no longer read as Turtle: the engine infers
|
|
158
|
+
the format (op `detectFormat`: JSON-LD, RDF/XML, TriG or Turtle, in RDF
|
|
159
|
+
1.2 mode) and reports it in `dataset.format` / `dataset.formatInferred`, or
|
|
160
|
+
in the error message. N-Quads is never inferred; name it.
|
|
161
|
+
- Every function throws a `TypeError` for an unknown option key.
|
|
162
|
+
- `update(dataset, text)` answers a new Dataset and leaves its argument
|
|
163
|
+
unchanged (as before); `dataset.update(text)` changes it in place.
|
|
164
|
+
- `fn.toDataset` answers a DatasetCore; `fn.fromDataset` takes a `Dataset`
|
|
165
|
+
or a DatasetCore.
|
|
166
|
+
|
|
167
|
+
### New
|
|
168
|
+
|
|
169
|
+
- `Dataset` methods: `query` (with `entail` and `output`: SPARQL results
|
|
170
|
+
JSON, XML, CSV, TSV), `update`, `validateShacl`, `validateShex`,
|
|
171
|
+
`closure(regime)`, `canonicalize`, `serialize({format, version})`,
|
|
172
|
+
`toString(format)`, `clone`, `has`, `match`, lazy RDF/JS quad iteration,
|
|
173
|
+
`dispose`, `[Symbol.dispose]`; a `FinalizationRegistry` releases a
|
|
174
|
+
collected Dataset.
|
|
175
|
+
- `serialize` writes every writer the engine has: N-Quads, N-Triples,
|
|
176
|
+
Turtle, TriG, RDF/XML, JSON-LD, RDFC-1.0 canonical N-Quads, each with
|
|
177
|
+
`version: '1.1'` (refuses RDF 1.2 terms) or `'1.2'`. `formats()` gives the
|
|
178
|
+
engine's list; the README table is generated from it. Writers the engine
|
|
179
|
+
lacks: https://github.com/danbri/factoidal/issues/883.
|
|
180
|
+
- `closure(data, regime)`.
|
|
181
|
+
- Engine ops (Lean, `Wasm/Ops/Handles.lean`, `Wasm/Ops/DatasetFull.lean`):
|
|
182
|
+
`datasetClone`, `datasetCanonicalize`, `datasetClosure`,
|
|
183
|
+
`datasetShaclValidate`, `datasetQueryRaw`, `datasetFormats`,
|
|
184
|
+
`detectFormat`, `datasetFromCsvw`, `datasetFromRml`, `datasetFromJsonLd`,
|
|
185
|
+
`datasetToJsonLd`, `datasetShexValidate`, `datasetRifEval`; and
|
|
186
|
+
`datasetSerialize` writes N-Triples, TriG, RDF/XML and RDFC-1.0.
|
|
187
|
+
- Every example in README.md and API.md is executed by
|
|
188
|
+
`test/doc-examples.test.mjs`.
|
|
189
|
+
|
|
190
|
+
### Other changes
|
|
191
|
+
|
|
192
|
+
- The README is a short summary: what the package is, a usage example, the
|
|
193
|
+
JavaScript API and command list, the specifications covered with test
|
|
194
|
+
scores and their sources, and measured strengths and gaps. The previous
|
|
195
|
+
README is `API.md` in the repository (not in the tarball).
|
|
196
|
+
- New pure functions over engine ops that had no JavaScript function:
|
|
197
|
+
`aeadSeal`, `aeadOpen` (ChaCha20-Poly1305, RFC 8439), `hpkeSeal`,
|
|
198
|
+
`hpkeOpen` (RFC 9180 base mode), `proofCheck`, `proofInspect` (FPP0 proof
|
|
199
|
+
bundles), `canonicalizeNQuads` and `normalizeNQuads` (N-Quads text in, text
|
|
200
|
+
out). Tests: `test/pure-and-serve.test.mjs`.
|
|
201
|
+
- New `serveStore(root, {port, host})`: the `factoidal serve` SPARQL 1.1
|
|
202
|
+
Protocol server as a function, `{url, port, close()}`. Node only.
|
|
203
|
+
- `factoidal serve --file DATA /ds`, the `fuseki-server --file` form, and
|
|
204
|
+
the Fuseki service names `/ds/query`, `/ds/update` and `/ds/get` beside
|
|
205
|
+
`/ds/sparql` (decided in `StoreEndpoint.lean`). Record and gaps:
|
|
206
|
+
`docs/fuseki-replacement.md`; tests `test/fuseki-compat.test.mjs`. W3C live
|
|
207
|
+
protocol run: 50 pass, 0 fail (out of 50).
|
|
208
|
+
- The wasm is rebuilt from `main`; the package carries the Lean 4 engine only.
|
|
209
|
+
Package suite: 432 pass, 0 fail (out of 432); 4 of them are expected
|
|
210
|
+
failures naming issues 829, 830 and 831.
|
|
211
|
+
- Removed: the 0.9.x subpath aliases `@factoidal/core/l4` and
|
|
212
|
+
`@factoidal/core/l4-core` (files `l4.js`, `l4.d.ts`, `l4-core.js`,
|
|
213
|
+
`l4-core.d.ts`), as announced in 0.9.0. Import `@factoidal/core`; the
|
|
214
|
+
low-level engine handle is `loadEngine()`.
|
|
215
|
+
- `l4-assets/version.json` is written at release from the
|
|
216
|
+
`danbri/factoidal-builds` release that built the wasm
|
|
217
|
+
(`tools/fetch-builds.sh --set web`): source commit, build time, sha256 and
|
|
218
|
+
size of both wasm files, and the engine's own version. The committed file
|
|
219
|
+
is a placeholder. `factoidal version` prints the stamp and the loaded
|
|
220
|
+
engine's `version()`, and exits 1 when the stamped sha256 differs from the
|
|
221
|
+
installed wasm.
|
|
222
|
+
- RDF 1.2 format tags `turtle12`, `ntriples12`, `nquads12`, `trig12` are in
|
|
223
|
+
the `DataFormat` type.
|
|
224
|
+
- Includes the entries listed under "Unreleased" below.
|
|
225
|
+
|
|
226
|
+
## Unreleased (included in 0.10.0)
|
|
227
|
+
|
|
228
|
+
- **The durable update log in the browser, on the Lean engine**
|
|
229
|
+
([issue 728](https://github.com/danbri/factoidal/issues/728); hub post 18).
|
|
230
|
+
`deltaLogOpen`, `deltaLogAppend`, `deltaLogReadAllHex`, `deltaLogMerge`,
|
|
231
|
+
`deltaLogDestroy` and `_deltaLogCorruptLastForTest` are back, with the names
|
|
232
|
+
and result shapes of the F\* browser entry before 0.9.0, and
|
|
233
|
+
`deltaLogInspect` and the capability `deltaLog` are new. One record per
|
|
234
|
+
update, in the DLB1 format of the store's delta log; the engine ops
|
|
235
|
+
`deltaLogEncode`, `deltaLogDecode` and `deltaLogMerge` make every decision
|
|
236
|
+
about the bytes (checksums, a torn last record, merge on read), with
|
|
237
|
+
theorems in `formal/lean4/L4Factoidal/Storage/BrowserLogTheorems.lean`.
|
|
238
|
+
The browser entries keep the records in IndexedDB; the Node entries keep
|
|
239
|
+
them in the memory of the process. A record that does not verify, and
|
|
240
|
+
every record after it, is not applied.
|
|
241
|
+
|
|
242
|
+
- **SPARQL `SERVICE` failures inside `EXISTS` and under an outer `SILENT`**
|
|
243
|
+
([issue 735](https://github.com/danbri/factoidal/issues/735); SPARQL 1.1
|
|
244
|
+
Federated Query §2.3 and §3.2). A failed `SERVICE` call without `SILENT`
|
|
245
|
+
inside `EXISTS` or `NOT EXISTS` (in `FILTER`, `BIND`, an `OPTIONAL`
|
|
246
|
+
condition, `HAVING`, `ORDER BY` or a `SELECT` expression) now fails the
|
|
247
|
+
query, as the same call in a graph pattern does. A failed `SERVICE` call
|
|
248
|
+
nested in the body of an outer `SERVICE SILENT` is a failure of the outer
|
|
249
|
+
call, so the outer call gives one solution with no bindings; without
|
|
250
|
+
`SILENT` on the outer call the query fails. The two expected-failure cases
|
|
251
|
+
in `test/l4-parity.test.js` are now assertions.
|
|
252
|
+
|
|
253
|
+
- **`factoidal serve STORE --port N`: a SPARQL 1.1 endpoint over an activated
|
|
254
|
+
store** ([issue 669](https://github.com/danbri/factoidal/issues/669), step
|
|
255
|
+
1). SPARQL 1.1 Protocol query and update at `/sparql`, the Service
|
|
256
|
+
Description on a GET there with no query string, and the Graph Store HTTP
|
|
257
|
+
Protocol at `/data`. An update or a Graph Store write is appended to the
|
|
258
|
+
generation's delta log as one batch, synced, before it is answered. The
|
|
259
|
+
request framing, routing, status codes, negotiation and the delta-log batch
|
|
260
|
+
are decided by the engine (`storeServeKeys`, `storeServeOpen`,
|
|
261
|
+
`storeServeRequest`, `storeServeCommit`, `storeServeClose`); `bin/serve.mjs`
|
|
262
|
+
moves bytes. W3C protocol, graph-store and service-description tests over
|
|
263
|
+
a live socket: 50 pass, 0 fail (out of 50). Open gap: a quad generation's
|
|
264
|
+
delta log is not replayed by the native quad reader
|
|
265
|
+
([issue 770](https://github.com/danbri/factoidal/issues/770)).
|
|
266
|
+
- **`@factoidal/core/core`: the core engine** ([issue 713](https://github.com/danbri/factoidal/issues/713),
|
|
267
|
+
stage 4). `l4factoidal-core.wasm` (5.68 MB, 1.07 MB with gzip; the full
|
|
268
|
+
engine is 10.54 MB, 2.07 MB) serves parse and serialize (Turtle, TriG,
|
|
269
|
+
N-Triples, N-Quads, RDF/XML, lenient), SPARQL 1.1 and 1.2 query and update
|
|
270
|
+
over text and dataset handles, canonicalization, the RDFS and OWL 2 RL
|
|
271
|
+
closures and SHACL. The entry has the same exports as `.`; a function
|
|
272
|
+
outside that subset rejects naming `@factoidal/core`, and
|
|
273
|
+
`capabilities().profile` is `'core'` (`'full'` on the other entries).
|
|
274
|
+
Files: `core.mjs`, `core.js`, `core-browser.js`, `core.d.ts`,
|
|
275
|
+
`core-browser.d.ts`, `l4-assets/l4factoidal-core.{js,mjs,wasm}`.
|
|
276
|
+
- A SELECT of more than about 11,000 solutions, and `canonicalize()` of more
|
|
277
|
+
than about 20,000 blank-node quads, no longer fail under Node's default
|
|
278
|
+
stack with "Maximum call stack size exceeded". Four functions of the Lean
|
|
279
|
+
library recursed once per row or per quad (`SPARQL.numberRows`,
|
|
280
|
+
`SPARQL.rowsJoined`, `RDF.Canonical.collapseKeyedQQuads`, `dedupAdj`);
|
|
281
|
+
each now compiles to an accumulator form proved equal to it. The Results
|
|
282
|
+
JSON writer had also copied the rest of the document once per row.
|
|
283
|
+
- `NOW()` is bound ([issue 743](https://github.com/danbri/factoidal/issues/743)).
|
|
284
|
+
Through `query()`, `update()`, dataset handles, the entailment path,
|
|
285
|
+
`queryHdt()` and the store queries, SPARQL `NOW()` answered an error and left
|
|
286
|
+
its variable unbound (W3C `functions/now01.rq` answered `false`). The engine
|
|
287
|
+
now reads the system clock once per query or update and gives every `NOW()` of
|
|
288
|
+
that execution the same `xsd:dateTime` value in UTC (SPARQL 1.1 §17.4.5.1).
|
|
289
|
+
The wasm modules grow by about 82 KB (full) and 89 KB (core).
|
|
290
|
+
- The pure dispatch entries take the instant of `NOW()` as an optional last
|
|
291
|
+
argument ([issue 748](https://github.com/danbri/factoidal/issues/748)).
|
|
292
|
+
`call` and `callBlob` read no clock, so a host that uses them got an error
|
|
293
|
+
for `NOW()`. `queryDataset` and `updateDataset` (fourth, after the base IRI),
|
|
294
|
+
`queryRaw` and `storeQuery` (fourth) and `queryHdt` (second) now accept an
|
|
295
|
+
`xsd:dateTime` lexical form such as `2026-10-10T12:34:56Z`; an empty string
|
|
296
|
+
means none, and a text that is not a dateTime lexical form is an error. Hosts
|
|
297
|
+
that pass fewer arguments do not change. `callIO` and `callBlobIO` still read
|
|
298
|
+
the clock themselves.
|
|
299
|
+
- `Dataset` (`./rdfjs`) adds and looks up a quad in constant time. It
|
|
300
|
+
scanned every quad it held, so `parse()` of 12,000 quads spent 3.1 s
|
|
301
|
+
building the result; it now takes 0.35 s.
|
|
302
|
+
|
|
303
|
+
## 0.9.0 — 2026-10-10
|
|
304
|
+
|
|
305
|
+
**One engine: the Lean 4 build.** ([issue 713](https://github.com/danbri/factoidal/issues/713),
|
|
306
|
+
stage 3.) Owner, 2026-10-10: "We should make the main module Lean4-based.
|
|
307
|
+
Private builds might compare this with F\* (or indeed different wasms from
|
|
308
|
+
different toolchains)." The main entry (`.`: `index.js`, `index.mjs`),
|
|
309
|
+
`./api`, `./fn` and `./browser` now load the Lean 4 library `L4Factoidal`
|
|
310
|
+
compiled to WebAssembly (`l4-assets/`). Every function is a dispatch op of
|
|
311
|
+
that module behind the typed wrapper the F\* engine had, with the same
|
|
312
|
+
signatures and result shapes except where the list below says otherwise.
|
|
313
|
+
|
|
314
|
+
Removed, with no replacement shim (there are no users):
|
|
315
|
+
|
|
316
|
+
- the F\* bundles (`factoidal.js`, `factoidal.wasm.js`,
|
|
317
|
+
`factoidal-npm-entry*.js` and their wasm assets) and the entry points
|
|
318
|
+
that loaded them: `./wasm`, `./lite`, `./browser-wasm`, `./select`, and the
|
|
319
|
+
`./factoidal*.js` subpaths. The F\* bundles stay build artifacts of
|
|
320
|
+
`danbri/factoidal-builds`, for private comparison builds:
|
|
321
|
+
`tests/engine-compare/run.sh PATH/TO/factoidal-npm-entry.js`.
|
|
322
|
+
- `hacl-wasm/` and `hacl-init.js`: Ed25519 is HACL\*'s C code compiled into
|
|
323
|
+
the Lean wasm module, SHA-256 is the Lean hash, and there is no
|
|
324
|
+
initialisation step.
|
|
325
|
+
- the COTTAS functions `openCottas`, `queryCottas`, `closeCottas` and
|
|
326
|
+
`toCottas` (on the main entry and on `./fn`), and the browser delta log
|
|
327
|
+
(`deltaLogOpen` ... `deltaLogDestroy`), which wrote COTTAS batches. The
|
|
328
|
+
owner ruled the F\* COTTAS store essentially unused
|
|
329
|
+
([issue 566](https://github.com/danbri/factoidal/issues/566)). The
|
|
330
|
+
Lean persisted store replaces them (below); its update path in JavaScript
|
|
331
|
+
is [issue 641](https://github.com/danbri/factoidal/issues/641).
|
|
332
|
+
- the F\* `version.json` with its claims block. `l4-assets/version.json` is
|
|
333
|
+
the engine's provenance.
|
|
334
|
+
|
|
335
|
+
New:
|
|
336
|
+
|
|
337
|
+
- **The persisted store on the main entry**: `openStore(source, options)`
|
|
338
|
+
answers a `Store` (a store handle inside the engine: every artifact
|
|
339
|
+
verified against its manifest's SHA-256 once, then queried from the
|
|
340
|
+
retained blocks), `queryStore(source, sparql)`, `listStores()`, and
|
|
341
|
+
`sampleStorePath()` / `sampleStoreFacts`. `source` is a collection root
|
|
342
|
+
path (Node) or a reader function `(path) => bytes`, which is how a page
|
|
343
|
+
reads a store over `fetch()`.
|
|
344
|
+
- `shaclValidate` and `shexValidate` on the Lean engine (ops of
|
|
345
|
+
`formal/lean4/Wasm/Ops/Validate.lean`): SHACL Core, SHACL-SPARQL and SHACL
|
|
346
|
+
1.2 node expressions; ShExJ or ShExC schemas. `shexValidate` answers a
|
|
347
|
+
boolean (the F\* engine answered `null` outside its decidable fragment).
|
|
348
|
+
- SPARQL 1.2 queries: `query(data, q, {sparql12: true})` and
|
|
349
|
+
`handle.query(q, {sparql12: true})` (ops `queryDataset12`,
|
|
350
|
+
`datasetQuery12`): triple-term patterns, reified triples, annotations,
|
|
351
|
+
`TRIPLE`/`isTRIPLE`/`SUBJECT`/`PREDICATE`/`OBJECT`.
|
|
352
|
+
- `query(data, q, {entail})` with `RDFS`, `OWL-RL`, `x-rdfscore` or
|
|
353
|
+
`x-rdfsplus` runs in the engine (op `queryRaw`): the default graph is
|
|
354
|
+
closed under the regime, then the query is answered. SELECT, ASK,
|
|
355
|
+
CONSTRUCT and DESCRIBE.
|
|
356
|
+
- `loadEngine()`: the low-level engine handle (`call(op, args)`), for the
|
|
357
|
+
dispatch ops the typed layer does not wrap.
|
|
358
|
+
- Common Logic: `clParse`, `clSerialize`, `clAlphaNorm`, `clNormalize` on the
|
|
359
|
+
main entry (they were on `./l4-core` only).
|
|
360
|
+
- `browser.js` loads the wasm with `fetch` and
|
|
361
|
+
`WebAssembly.instantiateStreaming`; `setAssetsUrl()` names another
|
|
362
|
+
directory for the engine's three files.
|
|
363
|
+
- `parse(text, {lenient: true})` (op `parseDocument`,
|
|
364
|
+
[issue 716](https://github.com/danbri/factoidal/issues/716)): the Turtle,
|
|
365
|
+
TriG, N-Triples and N-Quads parsers skip each bad statement, keep the good
|
|
366
|
+
ones and list one diagnostic per skipped statement in
|
|
367
|
+
`dataset.diagnostics`: `message`, `line`, `column`, `offset` (a UTF-16
|
|
368
|
+
index, as `ParseError.offset`) and a stable `class` (for example
|
|
369
|
+
`undefined-prefix`, `bad-escape`, `unexpected-token`). The new option
|
|
370
|
+
`maxErrors` stops the parse after that many diagnostics with one
|
|
371
|
+
`too-many-errors` diagnostic. `capabilities().parseDiagnostics` is `true`.
|
|
372
|
+
A document with no error gives the strict result and no diagnostics (a
|
|
373
|
+
theorem in the Lean source). RDF/XML and JSON-LD parse strictly.
|
|
374
|
+
|
|
375
|
+
Changed:
|
|
376
|
+
|
|
377
|
+
- **`ParseError.offset` is a UTF-16 code-unit index into the input string**,
|
|
378
|
+
so `text.slice(err.offset)` starts at the error. The F\* engine counted
|
|
379
|
+
UTF-8 bytes; the Lean engine counts Unicode scalar values and the wrapper
|
|
380
|
+
converts. (`line` is 1-based; `column` counts Unicode scalar values.) This
|
|
381
|
+
unit is a coordinator default, recorded in
|
|
382
|
+
`docs/designissues/2026-10-10-npm-lean-only.md`.
|
|
383
|
+
- Each SELECT solution `Map` iterates in the order of the SELECT clause
|
|
384
|
+
(`head.vars`); it followed the member order of the engine's JSON binding
|
|
385
|
+
objects, which carries no meaning.
|
|
386
|
+
- DESCRIBE answers the Concise Bounded Description of each named resource
|
|
387
|
+
(the F\* engine answered the empty graph,
|
|
388
|
+
[issue 702](https://github.com/danbri/factoidal/issues/702)).
|
|
389
|
+
- `serialize(data, {format: 'ntriples'})` answers the engine's N-Quads text
|
|
390
|
+
and refuses data with a named graph (N-Triples has no graph term).
|
|
391
|
+
- `xmlWellformed` reads a DOCTYPE with an internal subset (the F\* parser
|
|
392
|
+
answered `false` for any DOCTYPE); `xpathEval` is the full XPath 1.0
|
|
393
|
+
evaluator and its node kinds include `root` and `namespace`;
|
|
394
|
+
`mathmlEval` also evaluates `vector` and `matrix`; `rifEval`'s envelope
|
|
395
|
+
has no `rounds`/`engineMs`; `tableauMaterialise` adds `budgetHit`;
|
|
396
|
+
`rmlMap` reads the iteration format from the mapping's
|
|
397
|
+
`rml:referenceFormulation`. Record:
|
|
398
|
+
`docs/designissues/2026-10-10-npm-lean-only.md` section 6.
|
|
399
|
+
- `capabilities()` reports what the loaded wasm serves: new members
|
|
400
|
+
`entailment`, `hdt`, `sparql12`, `store`; `cottasBytesStore` is gone.
|
|
401
|
+
- `engines.node` is `>=22.12`: `index.js` hands the ES module `index.mjs`
|
|
402
|
+
to `require()`, so CommonJS and ES module callers share one engine and
|
|
403
|
+
one `Dataset` class. (Node 20 reached end of life on 2026-04-30.)
|
|
404
|
+
|
|
405
|
+
Open, each with an expected-failure test:
|
|
406
|
+
|
|
407
|
+
- A failed `SERVICE` call without `SILENT` inside `EXISTS`, and a failed
|
|
408
|
+
nested `SERVICE` under an outer `SILENT`
|
|
409
|
+
([issue 735](https://github.com/danbri/factoidal/issues/735)). A failed
|
|
410
|
+
`SERVICE` call without `SILENT` elsewhere now fails the query
|
|
411
|
+
([issue 717](https://github.com/danbri/factoidal/issues/717), fixed).
|
|
412
|
+
|
|
413
|
+
Deprecated: `./l4` and `./l4-core` are aliases of `.` in 0.9.x and go in
|
|
414
|
+
0.10.0. (`./l4` was the low-level loader; `loadEngine()` replaces it.)
|
|
415
|
+
|
|
416
|
+
### Earlier in 0.9.0 (before the switch; `./l4-core` was the Lean entry then)
|
|
417
|
+
|
|
418
|
+
**Lean engine (`factoidal/l4-core`): 26 more functions of the main
|
|
419
|
+
entry** ([issue 713](https://github.com/danbri/factoidal/issues/713),
|
|
420
|
+
stage 1 of making the package Lean-only). `jsonldToRdf`,
|
|
421
|
+
`jsonldFromRdf`, `rmlMap`, `csvwToRdf`, `xmlWellformed`, `xpathEval`,
|
|
422
|
+
`xsltTransform`, `schematronValidate`, `jsonSchemaValidate`,
|
|
423
|
+
`mathmlEval`, `xformsRecalc`, `rifEval`, `queryHdt`,
|
|
424
|
+
`tableauMaterialise`, `tableauDlInconsistent`, `queryRaw`, the three
|
|
425
|
+
extension-function and two SERVICE-endpoint registry functions, and the
|
|
426
|
+
five `toan*` functions now run over the Lean wasm. Signatures and
|
|
427
|
+
result shapes are the F\* engine's. `parse(text, {format: 'jsonld'})`
|
|
428
|
+
now works on this engine. `tableauMaterialise` also returns `budgetHit`
|
|
429
|
+
on the Lean engine. The main entry (`index.js`) is unchanged.
|
|
430
|
+
|
|
431
|
+
**`factoidal/l4-core` serves the maths, VC and DID functions, and matches the
|
|
432
|
+
F\* engine's envelopes** (stage 2 of
|
|
433
|
+
[issue 713](https://github.com/danbri/factoidal/issues/713)). New on the Lean
|
|
434
|
+
engine, with the signatures and result shapes `index.d.ts` declares:
|
|
435
|
+
`matrixDeterminant`, `matrixScalarProduct`, `matrixVectorProduct`,
|
|
436
|
+
`matrixOuterProduct`, `sigmoidPoints`, `sigmoidFormulaMathml`, `vcSha256Hex`,
|
|
437
|
+
`vcEd25519SecretToPublic`, `vcEd25519Sign`, `vcEd25519Verify`,
|
|
438
|
+
`vcEddsaCreateFromCanonical`, `vcEddsaVerifyFromCanonical`, `vcCheckCredential`,
|
|
439
|
+
`vcCheckCredentialSubject`, `vcCheckNoDataLoss`,
|
|
440
|
+
`vcCheckRelatedResourceDigests` and `didKeyResolve`. The Ed25519 operations run
|
|
441
|
+
HACL\* compiled into the wasm module; they need no `initHacl()` call. Envelope
|
|
442
|
+
parity ([issue 685](https://github.com/danbri/factoidal/issues/685)): a Turtle,
|
|
443
|
+
TriG, N-Triples or N-Quads syntax error rejects with a `ParseError` carrying
|
|
444
|
+
`line`, `column`, `offset` and `format` (the Lean `offset` counts Unicode scalar
|
|
445
|
+
values, the F\* `offset` counts UTF-8 bytes); `parse()` and `openDataset()`
|
|
446
|
+
record the prefixes of a Turtle or TriG document; Turtle output accepts the
|
|
447
|
+
`prefixes` and `literalShorthand` options and prints bare numeric and boolean
|
|
448
|
+
literals by default; and each document that is parsed gets its own blank-node
|
|
449
|
+
scope, so merging two documents that both write `_:b0` keeps two nodes. The
|
|
450
|
+
Lean engine still has no `{lenient: true}` parse and no SPARQL 1.2 handle
|
|
451
|
+
query.
|
|
452
|
+
|
|
453
|
+
**Store format: the literal index is renamed LGI3 and LGI4; re-pack older
|
|
454
|
+
generations.** The literal search index folds each lexical form before it takes
|
|
455
|
+
character 3-grams. On 2026-10-10 the fold changed from ASCII-only to the Unicode
|
|
456
|
+
case mapping that SPARQL `LCASE` uses
|
|
457
|
+
([issue 706](https://github.com/danbri/factoidal/issues/706)), so the grams of a
|
|
458
|
+
non-ASCII literal differ and an index written under the old fold lacks rows the
|
|
459
|
+
Unicode `LCASE` keeps. The two layouts are now named LGI3 (was LGI1, sidecar
|
|
460
|
+
`.lgi3`) and LGI4 (was LGI2, sidecar `.lgi4`), and the manifest layout labels
|
|
461
|
+
changed with them. A generation packed earlier names LGI1 or LGI2; `storeOpen`,
|
|
462
|
+
`storeManifestInspect`, `storeQueryPlan`, `storeQuery`, `l4block-shard-activate`
|
|
463
|
+
and `l4block-quad-query` refuse it with the cause and ask for a re-pack.
|
|
464
|
+
`storeOpen` and `storeHandleList` also report `literalIndexes` and
|
|
465
|
+
`geoIndexes`, the number of retained blocks that hold a decoded index of each
|
|
466
|
+
kind. Record: `docs/designissues/2026-10-10-literal-index-lgi3.md`.
|
|
467
|
+
|
|
468
|
+
## 0.8.0 — 2026-09-16
|
|
469
|
+
|
|
470
|
+
**Behaviour change: parsing is strict by default.** `parse()` (and
|
|
471
|
+
every function that parses text input) now rejects on a syntax error,
|
|
472
|
+
an undeclared prefix, or a relative IRI with no `baseIRI` in effect,
|
|
473
|
+
instead of silently dropping the affected statements. The error is a
|
|
474
|
+
new `class ParseError extends Error` (`name`, `line`, `column`,
|
|
475
|
+
`offset`, `format` — position fields present for Turtle/TriG/
|
|
476
|
+
N-Triples/N-Quads, absent for RDF/XML and JSON-LD, which carry a
|
|
477
|
+
message only), exported from every entry point (`index.js`/`.mjs`,
|
|
478
|
+
`wasm.js`, `l4-core.js`, `api.js`/`.mjs`, `lite.js`/`.mjs`, and
|
|
479
|
+
`browser.js`, which declares its own copy since it has no imports).
|
|
480
|
+
Pass `{lenient: true}` to `parse()` to recover instead: the returned
|
|
481
|
+
Dataset holds whatever the parser recovered, and the new
|
|
482
|
+
`dataset.diagnostics` array lists what was skipped (needs the
|
|
483
|
+
npm-entry engine bundle's `parseDocument` export). Issues
|
|
484
|
+
[344](https://github.com/danbri/factoidal/issues/344),
|
|
485
|
+
[680](https://github.com/danbri/factoidal/issues/680),
|
|
486
|
+
[681](https://github.com/danbri/factoidal/issues/681).
|
|
487
|
+
|
|
488
|
+
**Dataset handles.** `openDataset(data, {format?, baseIRI?})` parses
|
|
489
|
+
once and keeps the result indexed in the engine, so repeated
|
|
490
|
+
`query()`/`update()`/`serialize()` calls against the returned
|
|
491
|
+
`DatasetHandle` skip both the re-parse and the SPARQL-index rebuild a
|
|
492
|
+
fresh `query(ds, ...)` call pays every time (measured ~2x faster per
|
|
493
|
+
call on a 2,000-triple two-pattern join in this session — see
|
|
494
|
+
`tests/perf/npm_handle_vs_stateless.mjs`). `query(data, sparql,
|
|
495
|
+
options)`, `serialize(data, options)` and `canonicalize(data)` also
|
|
496
|
+
accept a `DatasetHandle` directly; `update(handle, ...)` rejects with
|
|
497
|
+
a `TypeError`, since `update()` always returns a fresh Dataset while
|
|
498
|
+
`handle.update()` mutates the handle in place. `select.js` (the F*/
|
|
499
|
+
Lean backend selector) deliberately does not route `openDataset()` —
|
|
500
|
+
a handle is opened against one engine's own entry object, so there is
|
|
501
|
+
no "same handle on the other engine" to fall through to.
|
|
502
|
+
|
|
503
|
+
**Prefixes and Turtle literal shorthand.** `Dataset` gains `.prefixes`
|
|
504
|
+
(the declared map from parsing, `{}` for N-Triples/N-Quads) and
|
|
505
|
+
`.diagnostics` (own, non-enumerable properties, so `JSON.stringify()`/
|
|
506
|
+
`toArray()` are unaffected either way). `serialize(data, {format:
|
|
507
|
+
'turtle', prefixes?, literalShorthand?})` uses caller prefix pairs
|
|
508
|
+
(dropping unused caller namespaces, skipping caller labels when
|
|
509
|
+
auto-generating `nsN:` ones) when given, or automatically when `data`
|
|
510
|
+
is a Dataset with non-empty `.prefixes`; `literalShorthand` (default
|
|
511
|
+
`true`) controls whether `xsd:integer`/`decimal`/`double`/`boolean`
|
|
512
|
+
literals with a Turtle-grammar-matching lexical form print bare.
|
|
513
|
+
`DatasetHandle.serialize()` has the same options.
|
|
514
|
+
|
|
515
|
+
**New entry point `@factoidal/core/lite`** (`lite.js`/`.mjs`/`.d.ts`):
|
|
516
|
+
a smaller npm-entry ABI bundle (`factoidal-npm-entry-lite.js`, about
|
|
517
|
+
half the gzip size of the full bundle — see README.md's "Choosing a
|
|
518
|
+
bundle: full or lite") implementing the core surface (`parse`,
|
|
519
|
+
`query`, `update`, `serialize`, `canonicalize`, `graphs`,
|
|
520
|
+
`canonicalHash`, `openDataset`/`DatasetHandle`, the extension-function
|
|
521
|
+
and SERVICE-endpoint registration functions, `capabilities`) with no
|
|
522
|
+
SHACL, ShEx, OWL closure, JSON-LD, RDF/XML, CSVW, RML, RIF, XML,
|
|
523
|
+
XPath, VC crypto, COTTAS, or CLI bundle (no entailment regimes, no
|
|
524
|
+
`queryHdt`). `capabilities()` gains `profile` (`'full'` | `'lite'`),
|
|
525
|
+
`abiVersion`, `datasetHandles`, `parseDiagnostics`, `turtlePrefixes`.
|
|
526
|
+
Issue [684](https://github.com/danbri/factoidal/issues/684).
|
|
527
|
+
|
|
528
|
+
**`browser.js`: `query()` and `toRdf()` route through the npm-entry
|
|
529
|
+
ABI.** Whenever the persistent ABI bundle is available (already on
|
|
530
|
+
`globalThis`, or fetched by `loadNpmEntry()`), `query()` with `entail:
|
|
531
|
+
'none'` and `output: 'json'` (both defaults), and every `toRdf()`
|
|
532
|
+
call, now go through it instead of the eval-per-call CLI bundle —
|
|
533
|
+
falling back to the CLI bundle only when loading the ABI fails. This
|
|
534
|
+
makes the classic-`<script>` route (README.md's "Bundlers and Content
|
|
535
|
+
Security Policy" §b) CSP-safe for parsing and SELECT/ASK too, not only
|
|
536
|
+
for the operations already ABI-routed. New `openDataset(text,
|
|
537
|
+
{format?, baseIRI?})` on `browser.js` returns a handle with `query`/
|
|
538
|
+
`update`/`serialize`/`close` over the same ABI. Issue
|
|
539
|
+
[682](https://github.com/danbri/factoidal/issues/682) (second half).
|
|
540
|
+
|
|
541
|
+
Three new entry points, for two storage protocols:
|
|
542
|
+
|
|
543
|
+
* `@factoidal/core/lws` — a Node `http` server for the
|
|
544
|
+
[Linked Web Storage Protocol 1.0 core](https://w3c.github.io/lws-protocol/lws10-core/);
|
|
545
|
+
* `@factoidal/core/solid/server` — the same shape for the
|
|
546
|
+
[Solid Protocol v0.11.0](https://solidproject.org/TR/protocol) server
|
|
547
|
+
conformance class, including the CORS preflight and the storage root;
|
|
548
|
+
* `@factoidal/core/solid/client` — the client conformance class over
|
|
549
|
+
`fetch`, whose every request is built by the engine and whose every
|
|
550
|
+
response is read by it.
|
|
551
|
+
|
|
552
|
+
Three commands: `factoidal lws-serve DIR`, `factoidal solid-serve DIR`
|
|
553
|
+
and `factoidal solid-client <get|put|post|delete|discover> URL`.
|
|
554
|
+
|
|
555
|
+
Every protocol decision is the Lean engine's; the hosts move bytes. The
|
|
556
|
+
operations are `lwsOpen`/`lwsStep`/`lwsClose`,
|
|
557
|
+
`solidOpen`/`solidStep`/`solidClose` and
|
|
558
|
+
`solidClientRequest`/`solidClientResponse` on the WebAssembly dispatch
|
|
559
|
+
ABI. **A module built before they landed answers `unknown op`**: the
|
|
560
|
+
entry points report it through their `…OpsAvailable(engine)` probe and
|
|
561
|
+
the commands exit 3. `DIR` is not read or written yet — the resource tree
|
|
562
|
+
lives in the engine handle — and Solid-OIDC token verification is not
|
|
563
|
+
built, so the first slice serves public resources and unauthenticated
|
|
564
|
+
writes. Issue
|
|
565
|
+
[659](https://github.com/danbri/factoidal/issues/659).
|
|
566
|
+
|
|
567
|
+
New entry point `@factoidal/core/api`: `createApi(entry, options?)`
|
|
568
|
+
wires the typed `parse`/`query`/`serialize`/... surface around an
|
|
569
|
+
already-loaded npm-entry ABI object, with no `fetch` and no `new
|
|
570
|
+
Function(src)` eval — the route for a bundler (esbuild, webpack,
|
|
571
|
+
Rollup) or a page under `Content-Security-Policy: script-src 'self'`.
|
|
572
|
+
`entry` accepts the `factoidalNpmEntry` object itself, a Promise of
|
|
573
|
+
it, or a zero-argument function returning either. `browser.js`'s
|
|
574
|
+
`loadNpmEntry()` now checks `globalThis.factoidalNpmEntry` before
|
|
575
|
+
fetching anything, so a classic `<script src="factoidal-npm-entry.js">`
|
|
576
|
+
tag (or the new `setNpmEntry(abi)`) makes every `browser.js` operation
|
|
577
|
+
already routed through that ABI CSP-safe too. `package.json`'s
|
|
578
|
+
`exports` map gains `./api` and direct subpaths for
|
|
579
|
+
`factoidal-npm-entry.js`/`.wasm.js`/`.wasm.assets/*` (already shipped
|
|
580
|
+
in `files`, not previously importable as package subpaths). Every
|
|
581
|
+
typed entry point (`factoidal`, `factoidal/wasm`, `factoidal/api`,
|
|
582
|
+
`factoidal/l4-core`) now reports `version` and `engine`. See
|
|
583
|
+
README.md's "Bundlers and Content Security Policy" section. Proved
|
|
584
|
+
against a real headless-Chromium page under that CSP header
|
|
585
|
+
(`tests/web-demos/bundler_csp_smoke.sh`) and against esbuild's bundler
|
|
586
|
+
(`test/bundler-esbuild.test.mjs`). Issue
|
|
587
|
+
[682](https://github.com/danbri/factoidal/issues/682).
|
|
588
|
+
|
|
589
|
+
README.md documents `parse()`'s output order (canonical/sorted
|
|
590
|
+
N-Quads, not document order) and the blank-node label scheme
|
|
591
|
+
(`_anonN` in document order for anonymous nodes, `p<k>_d<n>_` scope
|
|
592
|
+
prefixes, no RDF meaning beyond within-dataset identity), pinned by
|
|
593
|
+
`test/parse-order.test.js`. No behavior changed. Issue
|
|
594
|
+
[683](https://github.com/danbri/factoidal/issues/683).
|
|
595
|
+
|
|
596
|
+
A bare string where `parse`, `query`, `update`, `openDataset`,
|
|
597
|
+
`serialize`, `canonicalize` or `DatasetHandle.serialize` take an
|
|
598
|
+
options object now means `{ format: <string> }`, so
|
|
599
|
+
`serialize(ds, 'turtle')` is `serialize(ds, { format: 'turtle' })`.
|
|
600
|
+
Until now the string was read as an empty options object and
|
|
601
|
+
`serialize(ds, 'turtle')` returned N-Quads with no error (found by the
|
|
602
|
+
0.8.0 release probe). Any other non-object value throws a `TypeError`
|
|
603
|
+
naming the function. Pinned by `test/options-format-string.test.js`.
|
|
604
|
+
|
|
605
|
+
The shipped bundles are built from a fresh extraction of the F\* OWL
|
|
606
|
+
closure source. The committed OCaml for `OWL.Closure` and
|
|
607
|
+
`OWL.DirectMapping.Filter` had lagged the `.fst` files since the
|
|
608
|
+
2026-09-07 OWL soundness audit (commit 709f3c4; the "Check F\* Extraction"
|
|
609
|
+
workflow was red on `claude/main` from that date, runs 200 and 201),
|
|
610
|
+
so 0.7.1 was built from the pre-audit rules. Observable in this
|
|
611
|
+
release: the rule that derived `owl:sameAs` between named classes
|
|
612
|
+
from `owl:equivalentClass` is removed, datatype-range clashes are
|
|
613
|
+
decided by XSD value-space disjointness (`xsd_value_spaces_disjoint`),
|
|
614
|
+
and the direct-mapping filter also excludes the built-in OWL annotation
|
|
615
|
+
properties, not only declared ones. The npm-publish workflow re-extracts and refuses drift,
|
|
616
|
+
so this correction is a precondition of the release.
|
|
617
|
+
|
|
3
618
|
## 0.7.1 — 2026-09-06
|
|
4
619
|
|
|
5
620
|
`factoidal pack --batch-bytes N` was documented in 0.7.0 and refused by the
|
|
@@ -567,7 +1182,7 @@ Known limits, measured:
|
|
|
567
1182
|
format-compatibility gate rejected stores written by the current js
|
|
568
1183
|
bundle (caught by the wasm/js parity test in `test/`).
|
|
569
1184
|
|
|
570
|
-
##
|
|
1185
|
+
## Between 0.1.0-alpha.0 and 0.1.0 — unpublished
|
|
571
1186
|
|
|
572
1187
|
- Package renamed from the placeholder `@danbri/foafos` to `factoidal`
|
|
573
1188
|
(issue #403). The package was never published under the old name, so
|
package/NOTICE
CHANGED
|
@@ -3,27 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
The code in this package is Apache-2.0; see LICENSE.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
The bundled sample Shardborough store holds five IPTC NewsCodes
|
|
10
|
-
vocabularies, converted from the publisher's RDF/XML and packed into
|
|
11
|
-
IBK3 predicate blocks:
|
|
12
|
-
|
|
13
|
-
spamfstat, videoqualifier, subjectqualifier, videocodec, spct
|
|
14
|
-
|
|
15
|
-
Publisher: International Press Telecommunications Council
|
|
16
|
-
https://iptc.org/
|
|
17
|
-
Licence: Creative Commons Attribution 4.0 International (CC BY 4.0)
|
|
18
|
-
https://creativecommons.org/licenses/by/4.0/
|
|
19
|
-
Obtained through: https://github.com/danbri/skosdex third_party/skos
|
|
20
|
-
|
|
21
|
-
The IRIs, labels and structure are the IPTC's. The block layout, the
|
|
22
|
-
dictionary, the indexes and the manifest are this package's encoding of
|
|
23
|
-
them. Redistribution of the vocabulary content stays under CC BY 4.0.
|
|
24
|
-
|
|
25
|
-
hacl-wasm/
|
|
26
|
-
----------
|
|
6
|
+
HACL* (compiled into l4factoidal.wasm)
|
|
7
|
+
--------------------------------------
|
|
27
8
|
|
|
28
9
|
HACL* verified cryptographic primitives, from
|
|
29
10
|
https://github.com/hacl-star/hacl-star, Apache-2.0 / MIT. See the
|