@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
|
@@ -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
|
+
}
|
package/lib/dataset.mjs
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
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 { dataFactory, quadFromJson, quadToJson, termToJson, termFromSrj, Literal, NamedNode } from '../rdfjs.mjs';
|
|
19
|
+
|
|
20
|
+
const INTERNAL = Symbol('factoidal.Dataset.internal');
|
|
21
|
+
|
|
22
|
+
// Writer names (lower case) -> the engine's writer tag: the engine's table
|
|
23
|
+
// (op engineTables, `writerFormats`), read once per engine. JSON-LD is served
|
|
24
|
+
// by the op datasetToJsonLd; the SPARQL results formats are query outputs.
|
|
25
|
+
// `WRITER_TAGS` stays exported for callers that read it; it is filled from
|
|
26
|
+
// the engine on the first Dataset.
|
|
27
|
+
const WRITER_TAGS = {};
|
|
28
|
+
|
|
29
|
+
const VERSIONS = { '1.1': '11', '1.2': '12' };
|
|
30
|
+
|
|
31
|
+
/** Throw a TypeError when `options` has a key outside `allowed`. */
|
|
32
|
+
function checkOptions(who, options, allowed) {
|
|
33
|
+
if (options == null) return {};
|
|
34
|
+
if (typeof options !== 'object' || Array.isArray(options)) {
|
|
35
|
+
throw new TypeError(`${who}: options must be an object, got ${Array.isArray(options) ? 'array' : typeof options}`);
|
|
36
|
+
}
|
|
37
|
+
for (const key of Object.keys(options)) {
|
|
38
|
+
if (!allowed.includes(key)) {
|
|
39
|
+
throw new TypeError(
|
|
40
|
+
`${who}: unknown option '${key}'. Known options: ${allowed.join(', ') || '(none)'}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return options;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function envelope(jsonText, what) {
|
|
47
|
+
const r = JSON.parse(jsonText);
|
|
48
|
+
if (!r.ok) throw new Error(`${what}: ${r.error}`);
|
|
49
|
+
return r;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const SERIALIZE_OPTIONS = ['format', 'version', 'prefixes', 'literalShorthand',
|
|
53
|
+
'useNativeTypes', 'useRdfType', 'rdfDirection'];
|
|
54
|
+
|
|
55
|
+
// The quads of the compact answer of datasetQuadsJson: each distinct term is
|
|
56
|
+
// built once and shared (TermJson.compactCertified).
|
|
57
|
+
function quadsFromCompact(terms, ixs) {
|
|
58
|
+
const ts = terms.map(termFromSrj);
|
|
59
|
+
const dg = dataFactory.defaultGraph();
|
|
60
|
+
return ixs.map((ix) => dataFactory.quad(ts[ix[0]], ts[ix[1]], ts[ix[2]],
|
|
61
|
+
ix.length > 3 ? ts[ix[3]] : dg));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The region of op datasetQuadsBin, read as the engine wrote it
|
|
65
|
+
// (formal/lean4/L4Factoidal/RDF/QuadsBin.lean: `binSpec` is the layout,
|
|
66
|
+
// `decodeBin` the reader this function follows, `decodeBin_quadsBin` the
|
|
67
|
+
// theorem that the region reads back as exactly the quads). Every number is a
|
|
68
|
+
// u32 little-endian; each term is in the term codec of DLOG version 2, which
|
|
69
|
+
// names the term kind, the datatype IRI, the language tag and the base
|
|
70
|
+
// direction explicitly, so this function copies fields and decides nothing.
|
|
71
|
+
const UTF8 = new TextDecoder('utf-8', { fatal: true });
|
|
72
|
+
const DIRECTION = ['', 'ltr', 'rtl'];
|
|
73
|
+
|
|
74
|
+
function quadsFromRegion(bytes, out) {
|
|
75
|
+
const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
76
|
+
if (bytes.length < 12 || v.getUint32(0, true) !== 0x31425146) {
|
|
77
|
+
throw new Error('iterate: the engine answered no datasetQuadsBin region');
|
|
78
|
+
}
|
|
79
|
+
const nTerms = v.getUint32(4, true);
|
|
80
|
+
const nQuads = v.getUint32(8, true);
|
|
81
|
+
let p = 12;
|
|
82
|
+
// UTF-8 bytes to a string; a short run of bytes below 0x80 (one UTF-16
|
|
83
|
+
// unit each) is copied without a TextDecoder call.
|
|
84
|
+
const str = () => {
|
|
85
|
+
const n = v.getUint32(p, true);
|
|
86
|
+
const a = p + 4;
|
|
87
|
+
p = a + n;
|
|
88
|
+
if (n <= 64) {
|
|
89
|
+
let i = a;
|
|
90
|
+
while (i < p && bytes[i] < 0x80) i++;
|
|
91
|
+
if (i === p) return String.fromCharCode.apply(null, bytes.subarray(a, p));
|
|
92
|
+
}
|
|
93
|
+
return UTF8.decode(bytes.subarray(a, p));
|
|
94
|
+
};
|
|
95
|
+
const datatypes = new Map();
|
|
96
|
+
const term = () => {
|
|
97
|
+
switch (bytes[p++]) {
|
|
98
|
+
case 0: return new NamedNode(str());
|
|
99
|
+
case 1: return dataFactory.blankNode(str());
|
|
100
|
+
case 2: {
|
|
101
|
+
const lexical = str();
|
|
102
|
+
const dt = str();
|
|
103
|
+
let datatype = datatypes.get(dt);
|
|
104
|
+
if (datatype === undefined) { datatype = new NamedNode(dt); datatypes.set(dt, datatype); }
|
|
105
|
+
const flag = bytes[p++];
|
|
106
|
+
const language = flag === 0 ? '' : str();
|
|
107
|
+
const direction = flag === 2 ? DIRECTION[bytes[p++]] : '';
|
|
108
|
+
return new Literal(lexical, language, datatype, direction);
|
|
109
|
+
}
|
|
110
|
+
case 3: {
|
|
111
|
+
const s = term();
|
|
112
|
+
const pr = term();
|
|
113
|
+
return dataFactory.quad(s, pr, term());
|
|
114
|
+
}
|
|
115
|
+
default: throw new Error('iterate: a term tag outside the DLOG version 2 codec');
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const ts = new Array(nTerms);
|
|
119
|
+
for (let i = 0; i < nTerms; i++) ts[i] = term();
|
|
120
|
+
const dg = dataFactory.defaultGraph();
|
|
121
|
+
for (let i = 0; i < nQuads; i++, p += 16) {
|
|
122
|
+
const g = v.getUint32(p + 12, true);
|
|
123
|
+
out.push(dataFactory.quad(ts[v.getUint32(p, true)], ts[v.getUint32(p + 4, true)],
|
|
124
|
+
ts[v.getUint32(p + 8, true)], g === 0 ? dg : ts[g - 1]));
|
|
125
|
+
}
|
|
126
|
+
return nQuads;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Quads of the first datasetQuadsBin call while iterating: an iteration that
|
|
130
|
+
// stops early reads this many; the rest come in one more call.
|
|
131
|
+
const FIRST_PAGE = 4096;
|
|
132
|
+
|
|
133
|
+
class Dataset {
|
|
134
|
+
/** @private Datasets come from parse(), csvwToRdf() and the other producers. */
|
|
135
|
+
constructor(token, ctx, handle, size, prefixes, extra) {
|
|
136
|
+
if (token !== INTERNAL) {
|
|
137
|
+
throw new TypeError(
|
|
138
|
+
'Dataset: use parse(), openDataset() or another producer; the constructor is internal');
|
|
139
|
+
}
|
|
140
|
+
this._ctx = ctx;
|
|
141
|
+
this._handle = handle;
|
|
142
|
+
this._size = size;
|
|
143
|
+
this._prefixes = Object.freeze({ ...(prefixes || {}) });
|
|
144
|
+
const x = extra || {};
|
|
145
|
+
this._diagnostics = Object.freeze((x.diagnostics || []).slice());
|
|
146
|
+
this._format = x.format;
|
|
147
|
+
this._formatInferred = !!x.formatInferred;
|
|
148
|
+
this._info = Object.freeze({ ...(x.info || {}) });
|
|
149
|
+
this._disposed = false;
|
|
150
|
+
this._nquads = null; // cached N-Quads text, dropped on update
|
|
151
|
+
this._quads = null; // cached quad array, dropped on update
|
|
152
|
+
if (ctx.registry) ctx.registry.register(this, { e: ctx.e, handle }, this);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The engine's handle id (opaque, e.g. "h7"). */
|
|
156
|
+
get handle() { return this._handle; }
|
|
157
|
+
/** Number of quads. */
|
|
158
|
+
get size() { this._open('size'); return this._size; }
|
|
159
|
+
/** Prefixes the source document declared (label -> namespace IRI). */
|
|
160
|
+
get prefixes() { return this._prefixes; }
|
|
161
|
+
/** Diagnostics of a lenient parse ({lenient:true}); else empty. */
|
|
162
|
+
get diagnostics() { return this._diagnostics; }
|
|
163
|
+
/** The syntax the data was read from, when it was read from text. */
|
|
164
|
+
get format() { return this._format; }
|
|
165
|
+
/** True when `format` was inferred because the caller gave none. */
|
|
166
|
+
get formatInferred() { return this._formatInferred; }
|
|
167
|
+
/** True after dispose(). */
|
|
168
|
+
get disposed() { return this._disposed; }
|
|
169
|
+
/**
|
|
170
|
+
* What the producing op reported beside the data: `rounds` for the rho-df
|
|
171
|
+
* and RDFS-Plus closures; `inputCount`, `derivedCount`, `fuel` for rifEval.
|
|
172
|
+
*/
|
|
173
|
+
get info() { return this._info; }
|
|
174
|
+
|
|
175
|
+
_open(who) {
|
|
176
|
+
if (this._disposed) {
|
|
177
|
+
throw new TypeError(`Dataset.${who}: the Dataset was disposed (handle '${this._handle}')`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
_changed(size) {
|
|
182
|
+
this._size = size;
|
|
183
|
+
this._nquads = null;
|
|
184
|
+
this._quads = null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* SPARQL 1.1 (or 1.2) query.
|
|
189
|
+
* @param {string} sparql
|
|
190
|
+
* @param {{sparql12?: boolean, version?: '1.1'|'1.2', entail?: string,
|
|
191
|
+
* output?: 'json'|'xml'|'csv'|'tsv'|'table'|'ntriples'}} [options]
|
|
192
|
+
* With `output` (or an `entail` regime other than 'none') the answer
|
|
193
|
+
* is text (`output` 'json': the parsed results JSON object).
|
|
194
|
+
* @returns {Promise<Array<Map<string, object>>|boolean|Dataset|string|object>}
|
|
195
|
+
*/
|
|
196
|
+
async query(sparql, options) {
|
|
197
|
+
this._open('query');
|
|
198
|
+
return this._ctx.queryHandle(this, sparql,
|
|
199
|
+
checkOptions('Dataset.query', options, ['sparql12', 'version', 'entail', 'output']));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* SPARQL 1.1 Update, applied to this Dataset in place.
|
|
204
|
+
* @param {string} updateText
|
|
205
|
+
* @param {{}} [options] none today
|
|
206
|
+
* @returns {Promise<Dataset>} this Dataset
|
|
207
|
+
*/
|
|
208
|
+
async update(updateText, options) {
|
|
209
|
+
this._open('update');
|
|
210
|
+
checkOptions('Dataset.update', options, []);
|
|
211
|
+
if (typeof updateText !== 'string') {
|
|
212
|
+
throw new TypeError('Dataset.update: updateText must be a string');
|
|
213
|
+
}
|
|
214
|
+
const r = envelope(this._ctx.e.datasetUpdate(this._handle, updateText), 'update');
|
|
215
|
+
this._changed(r.count);
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* SHACL validation with this Dataset as the data graph.
|
|
221
|
+
* @param {Dataset|string} shapes a Dataset or text
|
|
222
|
+
* @param {{format?: string, baseIRI?: string}} [options] read `shapes` text
|
|
223
|
+
* @returns {Promise<{conforms: boolean, report: Dataset}>}
|
|
224
|
+
*/
|
|
225
|
+
async validateShacl(shapes, options) {
|
|
226
|
+
this._open('validateShacl');
|
|
227
|
+
return this._ctx.shacl(this, shapes,
|
|
228
|
+
checkOptions('Dataset.validateShacl', options, ['format', 'baseIRI']));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* ShEx validation of one focus node with this Dataset as the data graph.
|
|
233
|
+
* @param {string} schema ShExC or ShExJ text
|
|
234
|
+
* @param {{focus: string|object, shape?: string|object}} options
|
|
235
|
+
* @returns {Promise<boolean>}
|
|
236
|
+
*/
|
|
237
|
+
async validateShex(schema, options) {
|
|
238
|
+
this._open('validateShex');
|
|
239
|
+
return this._ctx.shex(this, schema,
|
|
240
|
+
checkOptions('Dataset.validateShex', options, ['focus', 'shape']));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Closure of the default graph under a regime, as a new Dataset.
|
|
245
|
+
* @param {'rdfs'|'owl-rl'|'rho-df'|'rdfs-plus'} regime
|
|
246
|
+
* @returns {Promise<Dataset>}
|
|
247
|
+
*/
|
|
248
|
+
async closure(regime) {
|
|
249
|
+
this._open('closure');
|
|
250
|
+
return this._ctx.closure(this, regime);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** RDFC-1.0 canonical N-Quads. @returns {Promise<string>} */
|
|
254
|
+
async canonicalize() {
|
|
255
|
+
this._open('canonicalize');
|
|
256
|
+
return this.toString('rdfc');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* The dataset as a document.
|
|
261
|
+
* @param {{format?: string, version?: '1.1'|'1.2', prefixes?: object,
|
|
262
|
+
* literalShorthand?: boolean, useNativeTypes?: boolean,
|
|
263
|
+
* useRdfType?: boolean, rdfDirection?: string}} [options]
|
|
264
|
+
* format: nquads (default), ntriples, turtle, trig, rdfxml, jsonld,
|
|
265
|
+
* rdfc (RDFC-1.0 canonical N-Quads).
|
|
266
|
+
* @returns {Promise<string>}
|
|
267
|
+
*/
|
|
268
|
+
async serialize(options) {
|
|
269
|
+
this._open('serialize');
|
|
270
|
+
if (typeof options === 'string') return this._write({ format: options });
|
|
271
|
+
return this._write(checkOptions('Dataset.serialize', options, SERIALIZE_OPTIONS));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Synchronous serialize(): `toString('turtle')` or `toString({format})`.
|
|
276
|
+
* @param {string|object} [formatOrOptions]
|
|
277
|
+
* @returns {string}
|
|
278
|
+
*/
|
|
279
|
+
toString(formatOrOptions) {
|
|
280
|
+
this._open('toString');
|
|
281
|
+
const opts = typeof formatOrOptions === 'string'
|
|
282
|
+
? { format: formatOrOptions }
|
|
283
|
+
: checkOptions('Dataset.toString', formatOrOptions, SERIALIZE_OPTIONS);
|
|
284
|
+
return this._write(opts);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
_write(opts) {
|
|
288
|
+
const e = this._ctx.e;
|
|
289
|
+
const name = String(opts.format || 'nquads').toLowerCase();
|
|
290
|
+
const writerTags = this._ctx.tables().writerFormats;
|
|
291
|
+
const tag = writerTags[name];
|
|
292
|
+
if (!tag) {
|
|
293
|
+
throw new TypeError(
|
|
294
|
+
`Dataset.serialize: unknown format '${opts.format}'. Known formats: ` +
|
|
295
|
+
[...new Set(Object.values(writerTags))].join(', '));
|
|
296
|
+
}
|
|
297
|
+
let suffix = '';
|
|
298
|
+
if (opts.version !== undefined) {
|
|
299
|
+
suffix = VERSIONS[String(opts.version)];
|
|
300
|
+
if (!suffix) throw new TypeError("Dataset.serialize: version must be '1.1' or '1.2'");
|
|
301
|
+
}
|
|
302
|
+
if (tag !== 'turtle' && (opts.prefixes !== undefined || opts.literalShorthand !== undefined)) {
|
|
303
|
+
throw new TypeError("Dataset.serialize: 'prefixes' and 'literalShorthand' apply to format 'turtle' only");
|
|
304
|
+
}
|
|
305
|
+
const jsonldKeys = ['useNativeTypes', 'useRdfType', 'rdfDirection'];
|
|
306
|
+
if (tag !== 'jsonld' && jsonldKeys.some((k) => opts[k] !== undefined)) {
|
|
307
|
+
throw new TypeError(`Dataset.serialize: ${jsonldKeys.join(', ')} apply to format 'jsonld' only`);
|
|
308
|
+
}
|
|
309
|
+
if (tag === 'jsonld') {
|
|
310
|
+
if (typeof e.datasetToJsonLd !== 'function') throw this._ctx.missing('serialize {format:jsonld}', 'datasetToJsonLd');
|
|
311
|
+
const r = envelope(e.datasetToJsonLd(this._handle, JSON.stringify({
|
|
312
|
+
...(opts.useNativeTypes ? { useNativeTypes: true } : {}),
|
|
313
|
+
...(opts.useRdfType ? { useRdfType: true } : {}),
|
|
314
|
+
...(opts.rdfDirection ? { rdfDirection: String(opts.rdfDirection) } : {}),
|
|
315
|
+
...(suffix ? { version: String(opts.version) } : {}),
|
|
316
|
+
})), 'serialize');
|
|
317
|
+
return r.jsonld;
|
|
318
|
+
}
|
|
319
|
+
if (tag === 'nquads' && !suffix && this._nquads !== null) return this._nquads;
|
|
320
|
+
const fullTag = tag + suffix;
|
|
321
|
+
let r;
|
|
322
|
+
if (tag === 'turtle' && (opts.prefixes !== undefined || opts.literalShorthand !== undefined ||
|
|
323
|
+
Object.keys(this._prefixes).length > 0)) {
|
|
324
|
+
r = envelope(e.datasetSerializeWith(this._handle, fullTag, JSON.stringify({
|
|
325
|
+
prefixes: { ...this._prefixes, ...(opts.prefixes || {}) },
|
|
326
|
+
...(opts.literalShorthand !== undefined ? { literalShorthand: !!opts.literalShorthand } : {}),
|
|
327
|
+
})), 'serialize');
|
|
328
|
+
} else {
|
|
329
|
+
r = envelope(e.datasetSerialize(this._handle, fullTag), 'serialize');
|
|
330
|
+
}
|
|
331
|
+
const text = r.text !== undefined ? r.text : (tag === 'turtle' ? r.turtle : r.nquads);
|
|
332
|
+
if (tag === 'nquads' && !suffix) this._nquads = text;
|
|
333
|
+
return text;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** N-Quads text (sorted, the engine's canonical line order). */
|
|
337
|
+
toNQuads() {
|
|
338
|
+
this._open('toNQuads');
|
|
339
|
+
return this._write({ format: 'nquads' });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** A new Dataset with the same quads; updates of one leave the other unchanged. */
|
|
343
|
+
clone() {
|
|
344
|
+
this._open('clone');
|
|
345
|
+
const r = envelope(this._ctx.e.datasetClone(this._handle), 'clone');
|
|
346
|
+
return this._ctx.wrap(r, this._prefixes);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* RDF/JS quads, built from the engine's term objects (op datasetQuadsJson),
|
|
351
|
+
* in the line order of toNQuads(). No N-Quads text is read here.
|
|
352
|
+
* @returns {Iterator<object>}
|
|
353
|
+
*/
|
|
354
|
+
*[Symbol.iterator]() {
|
|
355
|
+
this._open('iterate');
|
|
356
|
+
if (this._quads) { yield* this._quads; return; }
|
|
357
|
+
const e = this._ctx.e;
|
|
358
|
+
if (typeof e.datasetQuadsBin !== 'function') { yield* this._allQuads(); return; }
|
|
359
|
+
// The engine's region (op datasetQuadsBin) in two calls, a first page and
|
|
360
|
+
// the rest; the whole list is cached when the rest is read.
|
|
361
|
+
const acc = [];
|
|
362
|
+
const first = e.datasetQuadsBin(this._handle, `{"offset":0,"limit":${FIRST_PAGE}}`);
|
|
363
|
+
quadsFromRegion(first.bytes, acc);
|
|
364
|
+
for (let i = 0; i < acc.length; i++) yield acc[i];
|
|
365
|
+
if (acc.length < first.envelope.total) {
|
|
366
|
+
const rest = e.datasetQuadsBin(this._handle, `{"offset":${acc.length}}`);
|
|
367
|
+
const from = acc.length;
|
|
368
|
+
quadsFromRegion(rest.bytes, acc);
|
|
369
|
+
for (let i = from; i < acc.length; i++) yield acc[i];
|
|
370
|
+
}
|
|
371
|
+
if (!this._quads) this._quads = Object.freeze(acc);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Every quad, read once and cached.
|
|
375
|
+
_allQuads() {
|
|
376
|
+
if (!this._quads) {
|
|
377
|
+
const e = this._ctx.e;
|
|
378
|
+
if (typeof e.datasetQuadsBin === 'function') {
|
|
379
|
+
const acc = [];
|
|
380
|
+
quadsFromRegion(e.datasetQuadsBin(this._handle, '').bytes, acc);
|
|
381
|
+
this._quads = Object.freeze(acc);
|
|
382
|
+
} else {
|
|
383
|
+
const r = envelope(e.datasetQuadsJson(this._handle, '{"compact":true}'), 'iterate');
|
|
384
|
+
this._quads = Object.freeze(r.ixs ? quadsFromCompact(r.terms, r.ixs) : r.quads.map((q) => quadFromJson(q)));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return this._quads;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Every quad as an array. */
|
|
391
|
+
toArray() {
|
|
392
|
+
this._open('toArray');
|
|
393
|
+
return this._allQuads().slice();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// A quad pattern for the ops datasetMatch and datasetMatchCount: four
|
|
397
|
+
// engine term objects, null for an unbound position, {type:'default'} for
|
|
398
|
+
// the default graph.
|
|
399
|
+
_pattern(subject, predicate, object, graph) {
|
|
400
|
+
return JSON.stringify([termToJson(subject), termToJson(predicate), termToJson(object),
|
|
401
|
+
termToJson(graph)]);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** RDF/JS DatasetCore.has: one indexed lookup in the engine. */
|
|
405
|
+
has(quad) {
|
|
406
|
+
this._open('has');
|
|
407
|
+
const r = envelope(this._ctx.e.datasetMatchCount(this._handle,
|
|
408
|
+
this._pattern(quad.subject, quad.predicate, quad.object, quad.graph || { termType: 'DefaultGraph' })), 'has');
|
|
409
|
+
return r.count > 0;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* RDF/JS DatasetCore.match: the matching quads as a new Dataset, found
|
|
414
|
+
* through the engine's indexes. `null`/`undefined` matches any term.
|
|
415
|
+
*/
|
|
416
|
+
match(subject, predicate, object, graph) {
|
|
417
|
+
this._open('match');
|
|
418
|
+
const r = envelope(this._ctx.e.datasetMatch(this._handle,
|
|
419
|
+
this._pattern(subject, predicate, object, graph)), 'match');
|
|
420
|
+
return this._ctx.wrap(r, this._prefixes);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* RDF/JS DatasetCore.add: add one quad (set semantics). A blank node is
|
|
425
|
+
* the node of that label in this Dataset. Each call changes the engine
|
|
426
|
+
* copy and costs time in the dataset size; for many quads use update()
|
|
427
|
+
* or parse().
|
|
428
|
+
*/
|
|
429
|
+
add(quad) {
|
|
430
|
+
this._open('add');
|
|
431
|
+
const r = envelope(this._ctx.e.datasetAddQuadsJson(this._handle,
|
|
432
|
+
JSON.stringify([quadToJson(quad)])), 'add');
|
|
433
|
+
this._changed(r.count);
|
|
434
|
+
return this;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** RDF/JS DatasetCore.delete: remove one quad. */
|
|
438
|
+
delete(quad) {
|
|
439
|
+
this._open('delete');
|
|
440
|
+
const r = envelope(this._ctx.e.datasetDeleteQuadsJson(this._handle,
|
|
441
|
+
JSON.stringify([quadToJson(quad)])), 'delete');
|
|
442
|
+
this._changed(r.count);
|
|
443
|
+
return this;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** Release the engine copy. Idempotent. */
|
|
447
|
+
dispose() {
|
|
448
|
+
if (this._disposed) return;
|
|
449
|
+
this._disposed = true;
|
|
450
|
+
this._nquads = null;
|
|
451
|
+
this._quads = null;
|
|
452
|
+
if (this._ctx.registry) this._ctx.registry.unregister(this);
|
|
453
|
+
try { this._ctx.e.datasetClose(this._handle); } catch (_err) { /* engine gone */ }
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** `using ds = await parse(...)` disposes at the end of the block. */
|
|
457
|
+
[Symbol.dispose]() { this.dispose(); }
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* A FinalizationRegistry that closes the engine handle of a Dataset that is
|
|
462
|
+
* collected without dispose(). Null where the platform has none.
|
|
463
|
+
*/
|
|
464
|
+
function makeRegistry() {
|
|
465
|
+
if (typeof FinalizationRegistry !== 'function') return null;
|
|
466
|
+
return new FinalizationRegistry(({ e, handle }) => {
|
|
467
|
+
try { e.datasetClose(handle); } catch (_err) { /* engine gone */ }
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export { Dataset, INTERNAL, checkOptions, makeRegistry, WRITER_TAGS, termFromSrj };
|