@factoidal/core 0.7.1 → 0.10.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.
Files changed (193) hide show
  1. package/CHANGELOG.md +503 -1
  2. package/NOTICE +2 -21
  3. package/README.md +117 -993
  4. package/api.d.ts +53 -0
  5. package/api.js +5 -0
  6. package/api.mjs +56 -0
  7. package/bin/engine.mjs +38 -19
  8. package/bin/factoidal.mjs +512 -66
  9. package/bin/pack.mjs +37 -0
  10. package/bin/serve-worker.mjs +32 -0
  11. package/bin/serve.mjs +423 -0
  12. package/bin/store-worker-host.mjs +1 -1
  13. package/bin/store.mjs +1 -1
  14. package/browser.d.ts +110 -507
  15. package/browser.js +151 -2257
  16. package/docs/fuseki-replacement.md +108 -0
  17. package/fn.d.ts +24 -95
  18. package/fn.js +104 -150
  19. package/index.d.ts +598 -259
  20. package/index.js +4 -234
  21. package/index.mjs +176 -84
  22. package/l4-assets/l4factoidal.js +47 -7
  23. package/l4-assets/l4factoidal.mjs +1 -1
  24. package/l4-assets/l4factoidal.wasm +0 -0
  25. package/l4-assets/version.json +11 -7
  26. package/lib/api.mjs +2300 -0
  27. package/lib/browser-engine.mjs +69 -0
  28. package/lib/dataset.mjs +377 -0
  29. package/lib/delta-log-hosts.mjs +191 -0
  30. package/lib/lean-entry.mjs +204 -0
  31. package/lib/pure.mjs +98 -0
  32. package/lib/store.mjs +115 -0
  33. package/lib/version.mjs +5 -0
  34. package/lws/README.md +97 -0
  35. package/lws/server.mjs +331 -0
  36. package/package.json +59 -61
  37. package/rdfjs.js +5 -536
  38. package/rdfjs.mjs +594 -0
  39. package/sample-store.d.ts +8 -16
  40. package/sample-store.mjs +39 -29
  41. package/solid/client/README.md +100 -0
  42. package/solid/client/index.mjs +277 -0
  43. package/solid/server/README.md +96 -0
  44. package/solid/server/index.mjs +317 -0
  45. package/store-host/deno.mjs +8 -0
  46. package/store-host/index.mjs +12 -0
  47. package/store-host/node.mjs +8 -0
  48. package/xmpp/README.md +107 -0
  49. package/xmpp/index.mjs +229 -0
  50. package/browser-wasm.js +0 -872
  51. package/factoidal-npm-entry.js +0 -15477
  52. package/factoidal-npm-entry.wasm.assets/code-5a7fc68f2ab1323718b8.wasm +0 -0
  53. package/factoidal-npm-entry.wasm.js +0 -455
  54. package/factoidal.js +0 -13118
  55. package/factoidal.wasm.assets/code-bbe6099bfb5b10c4c3ab.wasm +0 -0
  56. package/factoidal.wasm.js +0 -457
  57. package/hacl-init.js +0 -92
  58. package/hacl-wasm/FStar.wasm +0 -0
  59. package/hacl-wasm/Hacl_Bignum.wasm +0 -0
  60. package/hacl-wasm/Hacl_Bignum25519_51.wasm +0 -0
  61. package/hacl-wasm/Hacl_Bignum_Base.wasm +0 -0
  62. package/hacl-wasm/Hacl_Curve25519_51.wasm +0 -0
  63. package/hacl-wasm/Hacl_Ed25519.wasm +0 -0
  64. package/hacl-wasm/Hacl_Ed25519_PrecompTable.wasm +0 -0
  65. package/hacl-wasm/Hacl_Hash_Base.wasm +0 -0
  66. package/hacl-wasm/Hacl_Hash_SHA2.wasm +0 -0
  67. package/hacl-wasm/Hacl_IntTypes_Intrinsics.wasm +0 -0
  68. package/hacl-wasm/LowStar_Endianness.wasm +0 -0
  69. package/hacl-wasm/WasmSupport.wasm +0 -0
  70. package/hacl-wasm/api.js +0 -775
  71. package/hacl-wasm/api.json +0 -3787
  72. package/hacl-wasm/layouts.json +0 -1
  73. package/hacl-wasm/loader.js +0 -568
  74. package/hacl-wasm/shell.js +0 -12
  75. package/l4-core.d.ts +0 -159
  76. package/l4-core.js +0 -188
  77. package/l4.d.ts +0 -50
  78. package/l4.js +0 -104
  79. package/lib/api.js +0 -2300
  80. package/lib/engine-js.js +0 -165
  81. package/lib/engine-wasm.js +0 -300
  82. package/sample-store/CURRENT +0 -1
  83. package/sample-store/gen-1/manifest.sbm2 +0 -0
  84. package/sample-store/gen-1/manifest.tsv +0 -14
  85. package/sample-store/gen-1/predicate-0.ibk3 +0 -0
  86. package/sample-store/gen-1/predicate-0.ibk3.merkle +0 -1
  87. package/sample-store/gen-1/predicate-0.ibk3.oli2 +0 -0
  88. package/sample-store/gen-1/predicate-0.ibk3.oli2.merkle +0 -1
  89. package/sample-store/gen-1/predicate-0.ibk3.sri2 +0 -0
  90. package/sample-store/gen-1/predicate-0.ibk3.sri2.merkle +0 -1
  91. package/sample-store/gen-1/predicate-0.ibk3.tli1 +0 -0
  92. package/sample-store/gen-1/predicate-0.ibk3.tli1.merkle +0 -0
  93. package/sample-store/gen-1/predicate-1.ibk3 +0 -0
  94. package/sample-store/gen-1/predicate-1.ibk3.merkle +0 -0
  95. package/sample-store/gen-1/predicate-1.ibk3.oli2 +0 -0
  96. package/sample-store/gen-1/predicate-1.ibk3.oli2.merkle +0 -0
  97. package/sample-store/gen-1/predicate-1.ibk3.sri2 +0 -0
  98. package/sample-store/gen-1/predicate-1.ibk3.sri2.merkle +0 -1
  99. package/sample-store/gen-1/predicate-1.ibk3.tli1 +0 -0
  100. package/sample-store/gen-1/predicate-1.ibk3.tli1.merkle +0 -1
  101. package/sample-store/gen-1/predicate-10.ibk3 +0 -0
  102. package/sample-store/gen-1/predicate-10.ibk3.merkle +0 -1
  103. package/sample-store/gen-1/predicate-10.ibk3.oli2 +0 -0
  104. package/sample-store/gen-1/predicate-10.ibk3.oli2.merkle +0 -0
  105. package/sample-store/gen-1/predicate-10.ibk3.sri2 +0 -0
  106. package/sample-store/gen-1/predicate-10.ibk3.sri2.merkle +0 -1
  107. package/sample-store/gen-1/predicate-10.ibk3.tli1 +0 -0
  108. package/sample-store/gen-1/predicate-10.ibk3.tli1.merkle +0 -1
  109. package/sample-store/gen-1/predicate-11.ibk3 +0 -0
  110. package/sample-store/gen-1/predicate-11.ibk3.merkle +0 -0
  111. package/sample-store/gen-1/predicate-11.ibk3.oli2 +0 -0
  112. package/sample-store/gen-1/predicate-11.ibk3.oli2.merkle +0 -1
  113. package/sample-store/gen-1/predicate-11.ibk3.sri2 +0 -0
  114. package/sample-store/gen-1/predicate-11.ibk3.sri2.merkle +0 -1
  115. package/sample-store/gen-1/predicate-11.ibk3.tli1 +0 -0
  116. package/sample-store/gen-1/predicate-11.ibk3.tli1.merkle +0 -1
  117. package/sample-store/gen-1/predicate-12.ibk3 +0 -0
  118. package/sample-store/gen-1/predicate-12.ibk3.merkle +0 -1
  119. package/sample-store/gen-1/predicate-12.ibk3.oli2 +0 -0
  120. package/sample-store/gen-1/predicate-12.ibk3.oli2.merkle +0 -1
  121. package/sample-store/gen-1/predicate-12.ibk3.sri2 +0 -0
  122. package/sample-store/gen-1/predicate-12.ibk3.sri2.merkle +0 -1
  123. package/sample-store/gen-1/predicate-12.ibk3.tli1 +0 -0
  124. package/sample-store/gen-1/predicate-12.ibk3.tli1.merkle +0 -1
  125. package/sample-store/gen-1/predicate-2.ibk3 +0 -0
  126. package/sample-store/gen-1/predicate-2.ibk3.merkle +0 -1
  127. package/sample-store/gen-1/predicate-2.ibk3.oli2 +0 -0
  128. package/sample-store/gen-1/predicate-2.ibk3.oli2.merkle +0 -1
  129. package/sample-store/gen-1/predicate-2.ibk3.sri2 +0 -0
  130. package/sample-store/gen-1/predicate-2.ibk3.sri2.merkle +0 -0
  131. package/sample-store/gen-1/predicate-2.ibk3.tli1 +0 -0
  132. package/sample-store/gen-1/predicate-2.ibk3.tli1.merkle +0 -0
  133. package/sample-store/gen-1/predicate-3.ibk3 +0 -0
  134. package/sample-store/gen-1/predicate-3.ibk3.merkle +0 -1
  135. package/sample-store/gen-1/predicate-3.ibk3.oli2 +0 -0
  136. package/sample-store/gen-1/predicate-3.ibk3.oli2.merkle +0 -1
  137. package/sample-store/gen-1/predicate-3.ibk3.sri2 +0 -0
  138. package/sample-store/gen-1/predicate-3.ibk3.sri2.merkle +0 -1
  139. package/sample-store/gen-1/predicate-3.ibk3.tli1 +0 -0
  140. package/sample-store/gen-1/predicate-3.ibk3.tli1.merkle +0 -1
  141. package/sample-store/gen-1/predicate-4.ibk3 +0 -0
  142. package/sample-store/gen-1/predicate-4.ibk3.merkle +0 -0
  143. package/sample-store/gen-1/predicate-4.ibk3.oli2 +0 -0
  144. package/sample-store/gen-1/predicate-4.ibk3.oli2.merkle +0 -1
  145. package/sample-store/gen-1/predicate-4.ibk3.sri2 +0 -0
  146. package/sample-store/gen-1/predicate-4.ibk3.sri2.merkle +0 -1
  147. package/sample-store/gen-1/predicate-4.ibk3.tli1 +0 -0
  148. package/sample-store/gen-1/predicate-4.ibk3.tli1.merkle +0 -2
  149. package/sample-store/gen-1/predicate-5.ibk3 +0 -0
  150. package/sample-store/gen-1/predicate-5.ibk3.merkle +0 -1
  151. package/sample-store/gen-1/predicate-5.ibk3.oli2 +0 -0
  152. package/sample-store/gen-1/predicate-5.ibk3.oli2.merkle +0 -1
  153. package/sample-store/gen-1/predicate-5.ibk3.sri2 +0 -0
  154. package/sample-store/gen-1/predicate-5.ibk3.sri2.merkle +0 -1
  155. package/sample-store/gen-1/predicate-5.ibk3.tli1 +0 -0
  156. package/sample-store/gen-1/predicate-5.ibk3.tli1.merkle +0 -1
  157. package/sample-store/gen-1/predicate-6.ibk3 +0 -0
  158. package/sample-store/gen-1/predicate-6.ibk3.merkle +0 -1
  159. package/sample-store/gen-1/predicate-6.ibk3.oli2 +0 -0
  160. package/sample-store/gen-1/predicate-6.ibk3.oli2.merkle +0 -1
  161. package/sample-store/gen-1/predicate-6.ibk3.sri2 +0 -0
  162. package/sample-store/gen-1/predicate-6.ibk3.sri2.merkle +0 -1
  163. package/sample-store/gen-1/predicate-6.ibk3.tli1 +0 -0
  164. package/sample-store/gen-1/predicate-6.ibk3.tli1.merkle +0 -0
  165. package/sample-store/gen-1/predicate-7.ibk3 +0 -0
  166. package/sample-store/gen-1/predicate-7.ibk3.merkle +0 -0
  167. package/sample-store/gen-1/predicate-7.ibk3.oli2 +0 -0
  168. package/sample-store/gen-1/predicate-7.ibk3.oli2.merkle +0 -2
  169. package/sample-store/gen-1/predicate-7.ibk3.sri2 +0 -0
  170. package/sample-store/gen-1/predicate-7.ibk3.sri2.merkle +0 -1
  171. package/sample-store/gen-1/predicate-7.ibk3.tli1 +0 -0
  172. package/sample-store/gen-1/predicate-7.ibk3.tli1.merkle +0 -1
  173. package/sample-store/gen-1/predicate-8.ibk3 +0 -0
  174. package/sample-store/gen-1/predicate-8.ibk3.merkle +0 -1
  175. package/sample-store/gen-1/predicate-8.ibk3.oli2 +0 -0
  176. package/sample-store/gen-1/predicate-8.ibk3.oli2.merkle +0 -1
  177. package/sample-store/gen-1/predicate-8.ibk3.sri2 +0 -0
  178. package/sample-store/gen-1/predicate-8.ibk3.sri2.merkle +0 -1
  179. package/sample-store/gen-1/predicate-8.ibk3.tli1 +0 -0
  180. package/sample-store/gen-1/predicate-8.ibk3.tli1.merkle +0 -1
  181. package/sample-store/gen-1/predicate-9.ibk3 +0 -0
  182. package/sample-store/gen-1/predicate-9.ibk3.merkle +0 -1
  183. package/sample-store/gen-1/predicate-9.ibk3.oli2 +0 -0
  184. package/sample-store/gen-1/predicate-9.ibk3.oli2.merkle +0 -1
  185. package/sample-store/gen-1/predicate-9.ibk3.sri2 +0 -0
  186. package/sample-store/gen-1/predicate-9.ibk3.sri2.merkle +0 -3
  187. package/sample-store/gen-1/predicate-9.ibk3.tli1 +0 -0
  188. package/sample-store/gen-1/predicate-9.ibk3.tli1.merkle +0 -2
  189. package/select.d.ts +0 -116
  190. package/select.js +0 -492
  191. package/version.json +0 -102
  192. package/wasm.d.ts +0 -130
  193. package/wasm.js +0 -158
@@ -0,0 +1,69 @@
1
+ // Loading a Lean engine in the browser: one of the two modules of
2
+ // l4-assets/ (issue 713 stage 4), named by its basename:
3
+ //
4
+ // 'l4factoidal' the full engine (browser.js)
5
+ // 'l4factoidal-core' the core engine (core-browser.js)
6
+ //
7
+ // Each engine is three files that keep their names together: the loader
8
+ // <name>.js, Emscripten's glue <name>.mjs and the module <name>.wasm. The
9
+ // loader is imported from the assets directory; the wasm is fetched and
10
+ // compiled with WebAssembly.instantiateStreaming (a server that does not
11
+ // send `Content-Type: application/wasm` gets WebAssembly.instantiate over
12
+ // the fetched bytes instead).
13
+ //
14
+ // No Node built-ins: the browser imports this file as it is.
15
+
16
+ async function instantiate(url, imports) {
17
+ try {
18
+ return await WebAssembly.instantiateStreaming(fetch(url), imports);
19
+ } catch (err) {
20
+ // instantiateStreaming refuses a response without the application/wasm
21
+ // media type with a TypeError; compile from the bytes then. Any other
22
+ // failure (network, a corrupt module) is the caller's.
23
+ if (!(err instanceof TypeError)) throw err;
24
+ const response = await fetch(url);
25
+ if (!response.ok) {
26
+ throw new Error(`factoidal: fetching ${url} failed: ${response.status} ${response.statusText}`);
27
+ }
28
+ return WebAssembly.instantiate(await response.arrayBuffer(), imports);
29
+ }
30
+ }
31
+
32
+ /**
33
+ * The asset-directory setters and the engine loader of one engine.
34
+ * @param {string} name 'l4factoidal' or 'l4factoidal-core'
35
+ * @param {string} defaultAssetsUrl the directory URL to load from until
36
+ * setAssetsUrl() names another
37
+ */
38
+ export function browserEngine(name, defaultAssetsUrl) {
39
+ let assetsUrl = defaultAssetsUrl;
40
+ let enginePromise = null;
41
+
42
+ function setAssetsUrl(url) {
43
+ if (enginePromise) {
44
+ throw new Error('setAssetsUrl: the engine is already loaded from ' + assetsUrl);
45
+ }
46
+ const text = String(url);
47
+ assetsUrl = new URL(text.endsWith('/') ? text : text + '/', globalThis.location?.href).href;
48
+ }
49
+
50
+ function getAssetsUrl() {
51
+ return assetsUrl;
52
+ }
53
+
54
+ function loadEngine() {
55
+ if (enginePromise) return enginePromise;
56
+ enginePromise = (async () => {
57
+ const loader = await import(new URL(`${name}.js`, assetsUrl).href);
58
+ // The content-hash version keeps a cached wasm from pairing with a
59
+ // newer loader (the loader's own locateFile does the same).
60
+ const version = loader.wasmVersion ? `?v=${loader.wasmVersion}` : '';
61
+ const wasmUrl = new URL(`${name}.wasm`, assetsUrl).href + version;
62
+ return loader.loadL4({ instantiateWasm: (imports) => instantiate(wasmUrl, imports) });
63
+ })();
64
+ enginePromise.catch(() => { enginePromise = null; });
65
+ return enginePromise;
66
+ }
67
+
68
+ return { setAssetsUrl, getAssetsUrl, loadEngine };
69
+ }
@@ -0,0 +1,377 @@
1
+ // factoidal — Dataset: an RDF dataset held inside the Lean engine.
2
+ //
3
+ // https://github.com/danbri/factoidal/issues/882 item 1. A Dataset is a
4
+ // handle into the engine's dataset store (formal/lean4/Wasm/Ops/Handles.lean
5
+ // and Wasm/Ops/DatasetFull.lean). The parsed, indexed dataset stays in the
6
+ // wasm memory; a query, an update, a validation, a closure or a
7
+ // serialisation sends the handle id and the query text, not the data. An op
8
+ // that makes a dataset (parse, CSVW, RML, JSON-LD, closures, RIF) answers a
9
+ // new handle, so a pipeline of steps moves no N-Quads text between them.
10
+ //
11
+ // This file only marshals: every decision about RDF is the engine's.
12
+ //
13
+ // Lifetime: dispose() (or `using ds = ...`, Symbol.dispose) releases the
14
+ // engine copy. A Dataset that is garbage collected without dispose() is
15
+ // released by a FinalizationRegistry callback. Every method of a disposed
16
+ // Dataset throws a TypeError that names the method.
17
+
18
+ import { nquadsLineToQuad, quadToNQuads, termToNQuads, termFromSrj } from '../rdfjs.mjs';
19
+
20
+ const INTERNAL = Symbol('factoidal.Dataset.internal');
21
+
22
+ // Writer names (lower case) -> the engine's writer tag. JSON-LD is served
23
+ // by the op datasetToJsonLd; the SPARQL results formats are query outputs.
24
+ const WRITER_TAGS = {
25
+ nquads: 'nquads', nq: 'nquads', 'n-quads': 'nquads',
26
+ ntriples: 'ntriples', nt: 'ntriples', 'n-triples': 'ntriples',
27
+ turtle: 'turtle', ttl: 'turtle',
28
+ trig: 'trig',
29
+ rdfxml: 'rdfxml', 'rdf-xml': 'rdfxml', rdf: 'rdfxml',
30
+ jsonld: 'jsonld', 'json-ld': 'jsonld',
31
+ rdfc: 'rdfc', canonical: 'rdfc', 'rdfc-1.0': 'rdfc',
32
+ };
33
+
34
+ const VERSIONS = { '1.1': '11', '1.2': '12' };
35
+
36
+ /** Throw a TypeError when `options` has a key outside `allowed`. */
37
+ function checkOptions(who, options, allowed) {
38
+ if (options == null) return {};
39
+ if (typeof options !== 'object' || Array.isArray(options)) {
40
+ throw new TypeError(`${who}: options must be an object, got ${Array.isArray(options) ? 'array' : typeof options}`);
41
+ }
42
+ for (const key of Object.keys(options)) {
43
+ if (!allowed.includes(key)) {
44
+ throw new TypeError(
45
+ `${who}: unknown option '${key}'. Known options: ${allowed.join(', ') || '(none)'}`);
46
+ }
47
+ }
48
+ return options;
49
+ }
50
+
51
+ function envelope(jsonText, what) {
52
+ const r = JSON.parse(jsonText);
53
+ if (!r.ok) throw new Error(`${what}: ${r.error}`);
54
+ return r;
55
+ }
56
+
57
+ const SERIALIZE_OPTIONS = ['format', 'version', 'prefixes', 'literalShorthand',
58
+ 'useNativeTypes', 'useRdfType', 'rdfDirection'];
59
+
60
+ class Dataset {
61
+ /** @private Datasets come from parse(), csvwToRdf() and the other producers. */
62
+ constructor(token, ctx, handle, size, prefixes, extra) {
63
+ if (token !== INTERNAL) {
64
+ throw new TypeError(
65
+ 'Dataset: use parse(), openDataset() or another producer; the constructor is internal');
66
+ }
67
+ this._ctx = ctx;
68
+ this._handle = handle;
69
+ this._size = size;
70
+ this._prefixes = Object.freeze({ ...(prefixes || {}) });
71
+ const x = extra || {};
72
+ this._diagnostics = Object.freeze((x.diagnostics || []).slice());
73
+ this._format = x.format;
74
+ this._formatInferred = !!x.formatInferred;
75
+ this._info = Object.freeze({ ...(x.info || {}) });
76
+ this._disposed = false;
77
+ this._nquads = null; // cached N-Quads text, dropped on update
78
+ this._quads = null; // cached quad array, dropped on update
79
+ if (ctx.registry) ctx.registry.register(this, { e: ctx.e, handle }, this);
80
+ }
81
+
82
+ /** The engine's handle id (opaque, e.g. "h7"). */
83
+ get handle() { return this._handle; }
84
+ /** Number of quads. */
85
+ get size() { this._open('size'); return this._size; }
86
+ /** Prefixes the source document declared (label -> namespace IRI). */
87
+ get prefixes() { return this._prefixes; }
88
+ /** Diagnostics of a lenient parse ({lenient:true}); else empty. */
89
+ get diagnostics() { return this._diagnostics; }
90
+ /** The syntax the data was read from, when it was read from text. */
91
+ get format() { return this._format; }
92
+ /** True when `format` was inferred because the caller gave none. */
93
+ get formatInferred() { return this._formatInferred; }
94
+ /** True after dispose(). */
95
+ get disposed() { return this._disposed; }
96
+ /**
97
+ * What the producing op reported beside the data: `rounds` for the rho-df
98
+ * and RDFS-Plus closures; `inputCount`, `derivedCount`, `fuel` for rifEval.
99
+ */
100
+ get info() { return this._info; }
101
+
102
+ _open(who) {
103
+ if (this._disposed) {
104
+ throw new TypeError(`Dataset.${who}: the Dataset was disposed (handle '${this._handle}')`);
105
+ }
106
+ }
107
+
108
+ _changed(size) {
109
+ this._size = size;
110
+ this._nquads = null;
111
+ this._quads = null;
112
+ }
113
+
114
+ /**
115
+ * SPARQL 1.1 (or 1.2) query.
116
+ * @param {string} sparql
117
+ * @param {{sparql12?: boolean, version?: '1.1'|'1.2', entail?: string,
118
+ * output?: 'json'|'xml'|'csv'|'tsv'|'table'|'ntriples'}} [options]
119
+ * With `output` (or an `entail` regime other than 'none') the answer
120
+ * is text (`output` 'json': the parsed results JSON object).
121
+ * @returns {Promise<Array<Map<string, object>>|boolean|Dataset|string|object>}
122
+ */
123
+ async query(sparql, options) {
124
+ this._open('query');
125
+ return this._ctx.queryHandle(this, sparql,
126
+ checkOptions('Dataset.query', options, ['sparql12', 'version', 'entail', 'output']));
127
+ }
128
+
129
+ /**
130
+ * SPARQL 1.1 Update, applied to this Dataset in place.
131
+ * @param {string} updateText
132
+ * @param {{}} [options] none today
133
+ * @returns {Promise<Dataset>} this Dataset
134
+ */
135
+ async update(updateText, options) {
136
+ this._open('update');
137
+ checkOptions('Dataset.update', options, []);
138
+ if (typeof updateText !== 'string') {
139
+ throw new TypeError('Dataset.update: updateText must be a string');
140
+ }
141
+ const r = envelope(this._ctx.e.datasetUpdate(this._handle, updateText), 'update');
142
+ this._changed(r.count);
143
+ return this;
144
+ }
145
+
146
+ /**
147
+ * SHACL validation with this Dataset as the data graph.
148
+ * @param {Dataset|string} shapes a Dataset or text
149
+ * @param {{format?: string, baseIRI?: string}} [options] read `shapes` text
150
+ * @returns {Promise<{conforms: boolean, report: Dataset}>}
151
+ */
152
+ async validateShacl(shapes, options) {
153
+ this._open('validateShacl');
154
+ return this._ctx.shacl(this, shapes,
155
+ checkOptions('Dataset.validateShacl', options, ['format', 'baseIRI']));
156
+ }
157
+
158
+ /**
159
+ * ShEx validation of one focus node with this Dataset as the data graph.
160
+ * @param {string} schema ShExC or ShExJ text
161
+ * @param {{focus: string|object, shape?: string|object}} options
162
+ * @returns {Promise<boolean>}
163
+ */
164
+ async validateShex(schema, options) {
165
+ this._open('validateShex');
166
+ return this._ctx.shex(this, schema,
167
+ checkOptions('Dataset.validateShex', options, ['focus', 'shape']));
168
+ }
169
+
170
+ /**
171
+ * Closure of the default graph under a regime, as a new Dataset.
172
+ * @param {'rdfs'|'owl-rl'|'rho-df'|'rdfs-plus'} regime
173
+ * @returns {Promise<Dataset>}
174
+ */
175
+ async closure(regime) {
176
+ this._open('closure');
177
+ return this._ctx.closure(this, regime);
178
+ }
179
+
180
+ /** RDFC-1.0 canonical N-Quads. @returns {Promise<string>} */
181
+ async canonicalize() {
182
+ this._open('canonicalize');
183
+ return this.toString('rdfc');
184
+ }
185
+
186
+ /**
187
+ * The dataset as a document.
188
+ * @param {{format?: string, version?: '1.1'|'1.2', prefixes?: object,
189
+ * literalShorthand?: boolean, useNativeTypes?: boolean,
190
+ * useRdfType?: boolean, rdfDirection?: string}} [options]
191
+ * format: nquads (default), ntriples, turtle, trig, rdfxml, jsonld,
192
+ * rdfc (RDFC-1.0 canonical N-Quads).
193
+ * @returns {Promise<string>}
194
+ */
195
+ async serialize(options) {
196
+ this._open('serialize');
197
+ if (typeof options === 'string') return this._write({ format: options });
198
+ return this._write(checkOptions('Dataset.serialize', options, SERIALIZE_OPTIONS));
199
+ }
200
+
201
+ /**
202
+ * Synchronous serialize(): `toString('turtle')` or `toString({format})`.
203
+ * @param {string|object} [formatOrOptions]
204
+ * @returns {string}
205
+ */
206
+ toString(formatOrOptions) {
207
+ this._open('toString');
208
+ const opts = typeof formatOrOptions === 'string'
209
+ ? { format: formatOrOptions }
210
+ : checkOptions('Dataset.toString', formatOrOptions, SERIALIZE_OPTIONS);
211
+ return this._write(opts);
212
+ }
213
+
214
+ _write(opts) {
215
+ const e = this._ctx.e;
216
+ const name = String(opts.format || 'nquads').toLowerCase();
217
+ const tag = WRITER_TAGS[name];
218
+ if (!tag) {
219
+ throw new TypeError(
220
+ `Dataset.serialize: unknown format '${opts.format}'. Known formats: ` +
221
+ [...new Set(Object.values(WRITER_TAGS))].join(', '));
222
+ }
223
+ let suffix = '';
224
+ if (opts.version !== undefined) {
225
+ suffix = VERSIONS[String(opts.version)];
226
+ if (!suffix) throw new TypeError("Dataset.serialize: version must be '1.1' or '1.2'");
227
+ }
228
+ if (tag !== 'turtle' && (opts.prefixes !== undefined || opts.literalShorthand !== undefined)) {
229
+ throw new TypeError("Dataset.serialize: 'prefixes' and 'literalShorthand' apply to format 'turtle' only");
230
+ }
231
+ const jsonldKeys = ['useNativeTypes', 'useRdfType', 'rdfDirection'];
232
+ if (tag !== 'jsonld' && jsonldKeys.some((k) => opts[k] !== undefined)) {
233
+ throw new TypeError(`Dataset.serialize: ${jsonldKeys.join(', ')} apply to format 'jsonld' only`);
234
+ }
235
+ if (tag === 'jsonld') {
236
+ if (typeof e.datasetToJsonLd !== 'function') throw this._ctx.missing('serialize {format:jsonld}', 'datasetToJsonLd');
237
+ const r = envelope(e.datasetToJsonLd(this._handle, JSON.stringify({
238
+ ...(opts.useNativeTypes ? { useNativeTypes: true } : {}),
239
+ ...(opts.useRdfType ? { useRdfType: true } : {}),
240
+ ...(opts.rdfDirection ? { rdfDirection: String(opts.rdfDirection) } : {}),
241
+ ...(suffix ? { version: String(opts.version) } : {}),
242
+ })), 'serialize');
243
+ return r.jsonld;
244
+ }
245
+ if (tag === 'nquads' && !suffix && this._nquads !== null) return this._nquads;
246
+ const fullTag = tag + suffix;
247
+ let r;
248
+ if (tag === 'turtle' && (opts.prefixes !== undefined || opts.literalShorthand !== undefined ||
249
+ Object.keys(this._prefixes).length > 0)) {
250
+ r = envelope(e.datasetSerializeWith(this._handle, fullTag, JSON.stringify({
251
+ prefixes: { ...this._prefixes, ...(opts.prefixes || {}) },
252
+ ...(opts.literalShorthand !== undefined ? { literalShorthand: !!opts.literalShorthand } : {}),
253
+ })), 'serialize');
254
+ } else {
255
+ r = envelope(e.datasetSerialize(this._handle, fullTag), 'serialize');
256
+ }
257
+ const text = r.text !== undefined ? r.text : (tag === 'turtle' ? r.turtle : r.nquads);
258
+ if (tag === 'nquads' && !suffix) this._nquads = text;
259
+ return text;
260
+ }
261
+
262
+ /** N-Quads text (sorted, the engine's canonical line order). */
263
+ toNQuads() {
264
+ this._open('toNQuads');
265
+ return this._write({ format: 'nquads' });
266
+ }
267
+
268
+ /** A new Dataset with the same quads; updates of one leave the other unchanged. */
269
+ clone() {
270
+ this._open('clone');
271
+ const r = envelope(this._ctx.e.datasetClone(this._handle), 'clone');
272
+ return this._ctx.wrap(r, this._prefixes);
273
+ }
274
+
275
+ /**
276
+ * RDF/JS quads, read from the engine's N-Quads text one line at a time.
277
+ * @returns {Iterator<object>}
278
+ */
279
+ *[Symbol.iterator]() {
280
+ this._open('iterate');
281
+ if (this._quads) { yield* this._quads; return; }
282
+ const text = this.toNQuads();
283
+ let start = 0;
284
+ let lineNo = 0;
285
+ while (start < text.length) {
286
+ let end = text.indexOf('\n', start);
287
+ if (end < 0) end = text.length;
288
+ lineNo += 1;
289
+ const line = text.slice(start, end);
290
+ start = end + 1;
291
+ const q = nquadsLineToQuad(line, lineNo);
292
+ if (q) yield q;
293
+ }
294
+ }
295
+
296
+ /** Every quad as an array. */
297
+ toArray() {
298
+ this._open('toArray');
299
+ if (!this._quads) this._quads = Object.freeze([...this]);
300
+ return this._quads.slice();
301
+ }
302
+
303
+ // A quad pattern for the ops datasetMatch and datasetMatchCount: four
304
+ // N-Triples term tokens, '' for an unbound position, 'DEFAULT' for the
305
+ // default graph.
306
+ _pattern(subject, predicate, object, graph) {
307
+ const tok = (t) => (t == null ? '' : termToNQuads(t));
308
+ const g = graph == null ? '' : (graph.termType === 'DefaultGraph' ? 'DEFAULT' : termToNQuads(graph));
309
+ return JSON.stringify([tok(subject), tok(predicate), tok(object), g]);
310
+ }
311
+
312
+ /** RDF/JS DatasetCore.has: one indexed lookup in the engine. */
313
+ has(quad) {
314
+ this._open('has');
315
+ const r = envelope(this._ctx.e.datasetMatchCount(this._handle,
316
+ this._pattern(quad.subject, quad.predicate, quad.object, quad.graph || { termType: 'DefaultGraph' })), 'has');
317
+ return r.count > 0;
318
+ }
319
+
320
+ /**
321
+ * RDF/JS DatasetCore.match: the matching quads as a new Dataset, found
322
+ * through the engine's indexes. `null`/`undefined` matches any term.
323
+ */
324
+ match(subject, predicate, object, graph) {
325
+ this._open('match');
326
+ const r = envelope(this._ctx.e.datasetMatch(this._handle,
327
+ this._pattern(subject, predicate, object, graph)), 'match');
328
+ return this._ctx.wrap(r, this._prefixes);
329
+ }
330
+
331
+ /**
332
+ * RDF/JS DatasetCore.add: add one quad (set semantics). A blank node is
333
+ * the node of that label in this Dataset. Each call changes the engine
334
+ * copy and costs time in the dataset size; for many quads use update()
335
+ * or parse().
336
+ */
337
+ add(quad) {
338
+ this._open('add');
339
+ const r = envelope(this._ctx.e.datasetAddQuads(this._handle, quadToNQuads(quad)), 'add');
340
+ this._changed(r.count);
341
+ return this;
342
+ }
343
+
344
+ /** RDF/JS DatasetCore.delete: remove one quad. */
345
+ delete(quad) {
346
+ this._open('delete');
347
+ const r = envelope(this._ctx.e.datasetDeleteQuads(this._handle, quadToNQuads(quad)), 'delete');
348
+ this._changed(r.count);
349
+ return this;
350
+ }
351
+
352
+ /** Release the engine copy. Idempotent. */
353
+ dispose() {
354
+ if (this._disposed) return;
355
+ this._disposed = true;
356
+ this._nquads = null;
357
+ this._quads = null;
358
+ if (this._ctx.registry) this._ctx.registry.unregister(this);
359
+ try { this._ctx.e.datasetClose(this._handle); } catch (_err) { /* engine gone */ }
360
+ }
361
+
362
+ /** `using ds = await parse(...)` disposes at the end of the block. */
363
+ [Symbol.dispose]() { this.dispose(); }
364
+ }
365
+
366
+ /**
367
+ * A FinalizationRegistry that closes the engine handle of a Dataset that is
368
+ * collected without dispose(). Null where the platform has none.
369
+ */
370
+ function makeRegistry() {
371
+ if (typeof FinalizationRegistry !== 'function') return null;
372
+ return new FinalizationRegistry(({ e, handle }) => {
373
+ try { e.datasetClose(handle); } catch (_err) { /* engine gone */ }
374
+ });
375
+ }
376
+
377
+ export { Dataset, INTERNAL, checkOptions, makeRegistry, WRITER_TAGS, termFromSrj };
@@ -0,0 +1,191 @@
1
+ // Host stores for the durable update log (deltaLog* in lib/api.mjs; hub
2
+ // post 18; https://github.com/danbri/factoidal/issues/728).
3
+ //
4
+ // A host store keeps, for each log name, the records in commit order. A
5
+ // record is { seq, epoch, hex }: `hex` is the hexadecimal text of one DLB1
6
+ // record as the engine wrote it (op deltaLogEncode,
7
+ // formal/lean4/Wasm/Ops/DeltaLog.lean). The store does not read the hex.
8
+ // The byte format, the checksums, the torn-record rule and the merge are in
9
+ // Lean (formal/lean4/L4Factoidal/Storage/BrowserLog.lean); a host store
10
+ // only stores and loads text.
11
+ //
12
+ // open(name) create the log if it is absent
13
+ // append(name, makeRecord) seq := the number of records; makeRecord(seq)
14
+ // (synchronous) gives {hex, epoch}; the record
15
+ // is committed with that seq, atomically
16
+ // readAll(name) the hex texts, in seq order
17
+ // destroy(name) remove the log
18
+ // corruptLast(name) TEST HOOK: cut the last 8 hex digits (4
19
+ // bytes) of the last record, as a torn write
20
+ // would; false when the log is empty
21
+ //
22
+ // No Node built-ins: the browser imports this file as it is.
23
+
24
+ const DELTA_STORE = 'deltaBatches';
25
+
26
+ /**
27
+ * The records in the memory of this process. The default store of the Node
28
+ * entry: the records live as long as the process. Pass another store to
29
+ * buildApi() (driver.deltaLogStore) to keep them elsewhere.
30
+ */
31
+ export function memoryDeltaLogStore() {
32
+ const logs = new Map();
33
+ const recordsOf = (name) => {
34
+ if (!logs.has(name)) logs.set(name, []);
35
+ return logs.get(name);
36
+ };
37
+ return {
38
+ kind: 'memory',
39
+ async open(name) {
40
+ recordsOf(name);
41
+ },
42
+ async append(name, makeRecord) {
43
+ const records = recordsOf(name);
44
+ const seq = records.length;
45
+ const rec = makeRecord(seq);
46
+ records.push({ seq, epoch: rec.epoch, hex: rec.hex });
47
+ return { seq };
48
+ },
49
+ async readAll(name) {
50
+ return (logs.get(name) || []).map((r) => r.hex);
51
+ },
52
+ async destroy(name) {
53
+ logs.delete(name);
54
+ },
55
+ async corruptLast(name) {
56
+ const records = logs.get(name);
57
+ if (!records || records.length === 0) return false;
58
+ const last = records[records.length - 1];
59
+ last.hex = last.hex.slice(0, Math.max(0, last.hex.length - 8));
60
+ return true;
61
+ },
62
+ };
63
+ }
64
+
65
+ function indexedDbFactory() {
66
+ const factory = globalThis.indexedDB;
67
+ if (!factory) {
68
+ throw new Error('deltaLog: this runtime has no indexedDB; the browser entry keeps the log in IndexedDB');
69
+ }
70
+ return factory;
71
+ }
72
+
73
+ function idbOpen(dbName) {
74
+ return new Promise((resolve, reject) => {
75
+ const req = indexedDbFactory().open(dbName, 1);
76
+ req.onupgradeneeded = () => {
77
+ const db = req.result;
78
+ if (!db.objectStoreNames.contains(DELTA_STORE)) {
79
+ db.createObjectStore(DELTA_STORE, { keyPath: 'seq' });
80
+ }
81
+ };
82
+ req.onsuccess = () => resolve(req.result);
83
+ req.onerror = () => reject(req.error || new Error('indexedDB.open failed'));
84
+ req.onblocked = () => reject(new Error('indexedDB.open blocked (another tab holds an open connection at an older version)'));
85
+ });
86
+ }
87
+
88
+ function requestDone(req) {
89
+ return new Promise((resolve, reject) => {
90
+ req.onsuccess = () => resolve(req.result);
91
+ req.onerror = () => reject(req.error || new Error('IndexedDB request failed'));
92
+ });
93
+ }
94
+
95
+ function transactionDone(tx) {
96
+ return new Promise((resolve, reject) => {
97
+ tx.oncomplete = () => resolve();
98
+ tx.onerror = () => reject(tx.error || new Error('IndexedDB transaction failed'));
99
+ tx.onabort = () => reject(tx.error || new Error('IndexedDB transaction aborted'));
100
+ });
101
+ }
102
+
103
+ /**
104
+ * The records in IndexedDB: one database for each log name, one object
105
+ * store 'deltaBatches' keyed by seq, one object { seq, epoch, hex } for each
106
+ * committed update. The default store of the browser entries.
107
+ *
108
+ * append() counts the records, calls makeRecord and adds the record in ONE
109
+ * readwrite transaction. IndexedDB runs the readwrite transactions of one
110
+ * object store one after another, so two tabs that append at the same time
111
+ * get different seq values; `add` (not `put`) refuses a seq that exists. The
112
+ * transaction asks for `durability: 'strict'`: Chrome 121 and later default
113
+ * to 'relaxed', where `complete` can fire before the data is on disk.
114
+ */
115
+ export function indexedDbDeltaLogStore() {
116
+ return {
117
+ kind: 'indexedDB',
118
+ async open(name) {
119
+ const db = await idbOpen(name);
120
+ db.close();
121
+ },
122
+ async append(name, makeRecord) {
123
+ const db = await idbOpen(name);
124
+ try {
125
+ const tx = db.transaction(DELTA_STORE, 'readwrite', { durability: 'strict' });
126
+ const done = transactionDone(tx);
127
+ const store = tx.objectStore(DELTA_STORE);
128
+ let seq;
129
+ let failure = null;
130
+ const countReq = store.count();
131
+ countReq.onsuccess = () => {
132
+ seq = countReq.result;
133
+ try {
134
+ // Synchronous: the transaction stays active inside this callback.
135
+ const rec = makeRecord(seq);
136
+ store.add({ seq, epoch: rec.epoch, hex: rec.hex });
137
+ } catch (err) {
138
+ failure = err;
139
+ tx.abort();
140
+ }
141
+ };
142
+ try {
143
+ await done;
144
+ } catch (err) {
145
+ throw failure || err;
146
+ }
147
+ return { seq };
148
+ } finally {
149
+ db.close();
150
+ }
151
+ },
152
+ async readAll(name) {
153
+ const db = await idbOpen(name);
154
+ try {
155
+ const all = await requestDone(
156
+ db.transaction(DELTA_STORE, 'readonly').objectStore(DELTA_STORE).getAll());
157
+ all.sort((a, b) => a.seq - b.seq);
158
+ return all.map((r) => r.hex);
159
+ } finally {
160
+ db.close();
161
+ }
162
+ },
163
+ async destroy(name) {
164
+ await new Promise((resolve, reject) => {
165
+ const req = indexedDbFactory().deleteDatabase(name);
166
+ req.onsuccess = () => resolve();
167
+ req.onerror = () => reject(req.error || new Error('indexedDB.deleteDatabase failed'));
168
+ req.onblocked = () => reject(new Error('indexedDB.deleteDatabase blocked (another open connection)'));
169
+ });
170
+ },
171
+ async corruptLast(name) {
172
+ const db = await idbOpen(name);
173
+ try {
174
+ const all = await requestDone(
175
+ db.transaction(DELTA_STORE, 'readonly').objectStore(DELTA_STORE).getAll());
176
+ if (all.length === 0) return false;
177
+ all.sort((a, b) => a.seq - b.seq);
178
+ const last = all[all.length - 1];
179
+ const tx = db.transaction(DELTA_STORE, 'readwrite');
180
+ const done = transactionDone(tx);
181
+ tx.objectStore(DELTA_STORE).put({
182
+ seq: last.seq, epoch: last.epoch, hex: last.hex.slice(0, Math.max(0, last.hex.length - 8)),
183
+ });
184
+ await done;
185
+ return true;
186
+ } finally {
187
+ db.close();
188
+ }
189
+ },
190
+ };
191
+ }