@factoidal/core 0.11.0 → 0.13.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 +565 -0
- package/README.md +76 -4
- package/api.d.ts +2 -3
- package/api.mjs +2 -2
- package/authz/server.mjs +331 -0
- package/bin/factoidal.mjs +510 -8
- package/browser.d.ts +3 -2
- package/browser.js +1 -2
- package/fn.js +8 -12
- package/index.d.ts +100 -35
- package/index.mjs +1 -2
- package/l4-assets/l4factoidal.js +1 -1
- package/l4-assets/l4factoidal.wasm +0 -0
- package/l4-assets/version.json +6 -6
- package/lib/api.mjs +96 -205
- package/lib/dataset.mjs +40 -45
- package/lib/delta-log-hosts.mjs +9 -8
- package/lib/errors.mjs +97 -0
- package/lib/lean-entry.mjs +15 -10
- package/lib/store.mjs +4 -5
- package/lib/version.mjs +1 -1
- package/lws/README.md +70 -3
- package/lws/client.mjs +319 -0
- package/lws/persist.mjs +253 -6
- package/lws/server.mjs +294 -10
- package/package.json +10 -1
- package/rdfjs.mjs +4 -2
- package/solid/server/index.mjs +7 -1
- package/xmpp/README.md +82 -4
- package/xmpp/server.mjs +549 -0
package/lib/api.mjs
CHANGED
|
@@ -28,32 +28,22 @@ import { version as packageVersion } from './version.mjs';
|
|
|
28
28
|
import { openGeneration, readRegion, manifestKeys, hexOfBytes } from './store.mjs';
|
|
29
29
|
import { fullEngineMessage } from './lean-entry.mjs';
|
|
30
30
|
import { memoryDeltaLogStore } from './delta-log-hosts.mjs';
|
|
31
|
+
import { FactoidalError, errorFromEnvelope, envelopeResult } from './errors.mjs';
|
|
31
32
|
|
|
32
33
|
// The format names, regimes and query outputs are the engine's tables (op
|
|
33
34
|
// engineTables, formal/lean4/Wasm/Ops/Handles.lean), read once per engine:
|
|
34
35
|
// no host copy (https://github.com/danbri/factoidal/issues/894 item 5).
|
|
35
36
|
|
|
36
37
|
// The parse tag a format name selects, from the engine's table.
|
|
37
|
-
|
|
38
|
+
// A name outside the table is refused by the engine (op apiCheck,
|
|
39
|
+
// code format.unknown).
|
|
40
|
+
function parseTagOf(e, tables, fmt, who) {
|
|
38
41
|
const key = String(fmt || 'turtle').toLowerCase();
|
|
39
42
|
const tag = tables.parseFormats[key];
|
|
40
|
-
if (tag === undefined) {
|
|
41
|
-
throw new TypeError(
|
|
42
|
-
`Unknown format '${fmt}'. Expected one of: ` + Object.keys(tables.parseFormats).join(', '));
|
|
43
|
-
}
|
|
43
|
+
if (tag === undefined) checkOptions(e, who || 'parse', { format: String(fmt) });
|
|
44
44
|
return tag;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// The engine's refusal of a regime name (table refusedRegimePrefixes), or null.
|
|
48
|
-
function iklRefusalOf(tables, who, regime) {
|
|
49
|
-
if (regime == null) return null;
|
|
50
|
-
const r = String(regime);
|
|
51
|
-
for (const [prefix, message] of Object.entries(tables.refusedRegimePrefixes)) {
|
|
52
|
-
if (r.toLowerCase().startsWith(prefix)) return message.replace('WHO', who).replace('REGIME', r);
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
47
|
// Where a function takes an options object, a bare string names the
|
|
58
48
|
// format: serialize(ds, 'turtle') is serialize(ds, { format: 'turtle' }).
|
|
59
49
|
// Until 0.8.0 the string was read as an empty options object, so
|
|
@@ -72,16 +62,28 @@ function optionsOf(fn, options) {
|
|
|
72
62
|
// package, or a dispatch table that does not serve it). When the entry is
|
|
73
63
|
// the core engine and the op is one only the full engine serves, the error
|
|
74
64
|
// names the entry that has it (issue 713 stage 4).
|
|
65
|
+
// The engine's own answer to the op is the error (code op.unknown); an entry
|
|
66
|
+
// with no engine handle to ask gets the same code.
|
|
75
67
|
function missingOpError(what, e, op) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
const eng = e && e.engine;
|
|
69
|
+
if (eng && typeof eng.call === 'function' && op) {
|
|
70
|
+
try {
|
|
71
|
+
eng.call(op, []);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
if (err && err.envelope && err.envelope.code === 'op.unknown') {
|
|
74
|
+
const out = errorFromEnvelope(err.envelope);
|
|
75
|
+
if (e.profile === 'core' && Array.isArray(e.fullOnlyOps) && e.fullOnlyOps.includes(op)) {
|
|
76
|
+
out.message = `${what}: ${fullEngineMessage(`the op '${op}'`)}`;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
79
81
|
}
|
|
80
|
-
return new
|
|
82
|
+
return new FactoidalError(
|
|
81
83
|
`${what}: the loaded Lean wasm module has no dispatch op for this ` +
|
|
82
84
|
'operation. The module is older than this package; rebuild it with ' +
|
|
83
85
|
'formal/lean4/Wasm/build-wasm.sh, or point FACTOIDAL_L4_ASSETS at a ' +
|
|
84
|
-
'current build.');
|
|
86
|
+
'current build.', { code: 'op.unknown' });
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
// The query form, from the engine's SPARQL parser (op queryForm). A query
|
|
@@ -124,85 +126,23 @@ function bindingsFromSrj(srj) {
|
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
// ---------------------------------------------------------------------
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
+
// The envelope unwrapper every engine call routes through. A refusal is a
|
|
130
|
+
// FactoidalError (lib/errors.mjs) whose code, spec section and position the
|
|
131
|
+
// engine decided (formal/lean4/Wasm/Ops/ErrorModel.lean).
|
|
129
132
|
// ---------------------------------------------------------------------
|
|
130
133
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
* when the failing engine call reported one (a parseToDatasetJson or
|
|
134
|
-
* datasetOpen error over Turtle, TriG, N-Triples or N-Quads), else
|
|
135
|
-
* `undefined`. RDF/XML and JSON-LD failures, and any non-parse engine
|
|
136
|
-
* error, stay plain Error -- entryResult() below decides which one to
|
|
137
|
-
* throw.
|
|
138
|
-
*
|
|
139
|
-
* Units: `line` and `column` are 1-based; `column` counts Unicode scalar
|
|
140
|
-
* values, as the engine reports it. `offset` is a UTF-16 code-unit index
|
|
141
|
-
* into the input JavaScript string, so `text.slice(err.offset)` starts
|
|
142
|
-
* at the error. The engine counts Unicode scalar values;
|
|
143
|
-
* utf16OffsetOf() below converts.
|
|
144
|
-
*/
|
|
145
|
-
class ParseError extends Error {
|
|
146
|
-
constructor(message, info) {
|
|
147
|
-
super(message);
|
|
148
|
-
this.name = 'ParseError';
|
|
149
|
-
const i = info || {};
|
|
150
|
-
this.line = i.line;
|
|
151
|
-
this.column = i.column;
|
|
152
|
-
this.offset = i.offset;
|
|
153
|
-
this.format = i.format;
|
|
154
|
-
if (typeof Error.captureStackTrace === 'function') {
|
|
155
|
-
Error.captureStackTrace(this, ParseError);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
134
|
+
function entryResult(jsonText, _what, text) {
|
|
135
|
+
return envelopeResult(jsonText, text);
|
|
158
136
|
}
|
|
159
137
|
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// -- throw ParseError so a caller can branch on position without
|
|
165
|
-
// string-matching the message. Every other failure stays a plain
|
|
166
|
-
// Error, as before. Pure (no per-driver state), so every buildApi()
|
|
167
|
-
// instance and DatasetHandle share this one function.
|
|
168
|
-
function entryResult(jsonText, what) {
|
|
169
|
-
const r = JSON.parse(jsonText);
|
|
170
|
-
if (!r.ok) {
|
|
171
|
-
if (r.line !== undefined || r.offset !== undefined) {
|
|
172
|
-
throw new ParseError(`${what}: ${r.error}`, {
|
|
173
|
-
line: r.line, column: r.column, offset: r.offset, format: r.format,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
throw new Error(`${what}: ${r.error}`);
|
|
177
|
-
}
|
|
178
|
-
return r;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// The Lean engine reports a parse error's `offset` as a count of Unicode
|
|
182
|
-
// scalar values from the start of the input. A JavaScript string is
|
|
183
|
-
// indexed in UTF-16 code units, where a character outside the Basic
|
|
184
|
-
// Multilingual Plane (U+10000 and above) takes two. This converts the
|
|
185
|
-
// engine's count into the UTF-16 index of the same position. A count past
|
|
186
|
-
// the end of the text gives text.length. Iterating a string with for..of
|
|
187
|
-
// yields one element per code point (a lone surrogate is one element, and
|
|
188
|
-
// the engine reads it as one replacement character), so both sides count
|
|
189
|
-
// the same units.
|
|
190
|
-
function utf16OffsetOf(text, scalarOffset) {
|
|
191
|
-
if (typeof text !== 'string' || !Number.isInteger(scalarOffset) || scalarOffset < 0) {
|
|
192
|
-
return scalarOffset;
|
|
193
|
-
}
|
|
194
|
-
let units = 0;
|
|
195
|
-
let scalars = 0;
|
|
196
|
-
for (const ch of text) {
|
|
197
|
-
if (scalars >= scalarOffset) break;
|
|
198
|
-
units += ch.length;
|
|
199
|
-
scalars += 1;
|
|
200
|
-
}
|
|
201
|
-
return units;
|
|
138
|
+
// entryResult() for an op that read `text`: the position's offset becomes a
|
|
139
|
+
// UTF-16 index into `text`.
|
|
140
|
+
function parseResult(jsonText, what, text) {
|
|
141
|
+
return entryResult(jsonText, what, text);
|
|
202
142
|
}
|
|
203
143
|
|
|
204
144
|
// The lenient parse's diagnostics (https://github.com/danbri/factoidal/issues/716)
|
|
205
|
-
// with each `offset` converted
|
|
145
|
+
// with each `offset` converted to a UTF-16 index as a FactoidalError's, so
|
|
206
146
|
// `text.slice(d.offset)` starts at the error. `line` and `column` are kept as
|
|
207
147
|
// the engine reports them. One pass over `text` for all diagnostics: the
|
|
208
148
|
// offsets are visited in increasing order, whatever order the list has.
|
|
@@ -230,23 +170,6 @@ function utf16Diagnostics(text, diagnostics) {
|
|
|
230
170
|
return diagnostics.map((d, i) => (units.has(i) ? { ...d, offset: units.get(i) } : d));
|
|
231
171
|
}
|
|
232
172
|
|
|
233
|
-
// entryResult() for an op that parsed `text`: a positioned ParseError gets
|
|
234
|
-
// its offset converted to a UTF-16 index into `text`.
|
|
235
|
-
function parseResult(jsonText, what, text) {
|
|
236
|
-
try {
|
|
237
|
-
return entryResult(jsonText, what);
|
|
238
|
-
} catch (err) {
|
|
239
|
-
if (err instanceof ParseError && typeof err.offset === 'number') {
|
|
240
|
-
err.offset = utf16OffsetOf(text, err.offset);
|
|
241
|
-
}
|
|
242
|
-
throw err;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// DatasetHandle: the name of the engine-resident dataset class before
|
|
247
|
-
// 0.10.0. Deprecated alias of Dataset, kept for 0.10.x only.
|
|
248
|
-
const DatasetHandle = Dataset;
|
|
249
|
-
|
|
250
173
|
// ---------------------------------------------------------------------
|
|
251
174
|
// The Lean persisted store (Shardborough generations: IBK3, IBK4, IBK5
|
|
252
175
|
// blocks with their index sidecars; skills/shardborough-storage). A
|
|
@@ -289,7 +212,8 @@ class Store {
|
|
|
289
212
|
get closed() { return this._closed; }
|
|
290
213
|
|
|
291
214
|
_assertOpen(who) {
|
|
292
|
-
|
|
215
|
+
// The engine answers handle.disposed for a closed store handle.
|
|
216
|
+
if (this._closed) entryResult(this._e.storeHandleClose(this.handle), `Store.${who}`);
|
|
293
217
|
}
|
|
294
218
|
|
|
295
219
|
/**
|
|
@@ -300,7 +224,7 @@ class Store {
|
|
|
300
224
|
async query(sparql) {
|
|
301
225
|
this._assertOpen('query');
|
|
302
226
|
if (typeof sparql !== 'string') throw new TypeError('Store.query: sparql must be a string');
|
|
303
|
-
const run = () => entryResult(this._e.storeHandleQuery(this.handle, sparql), 'Store.query');
|
|
227
|
+
const run = () => entryResult(this._e.storeHandleQuery(this.handle, sparql), 'Store.query', sparql);
|
|
304
228
|
const r = this._withExtensionRounds ? await this._withExtensionRounds(run) : run();
|
|
305
229
|
if (r.kind === 'ask') return r.boolean;
|
|
306
230
|
if (r.kind === 'construct' || r.kind === 'describe') {
|
|
@@ -351,16 +275,24 @@ function buildApi(driver) {
|
|
|
351
275
|
entryPromise = Promise.resolve()
|
|
352
276
|
.then(() => driver.loadEntry())
|
|
353
277
|
.then((e) => {
|
|
354
|
-
if (!e) throw new
|
|
278
|
+
if (!e) throw new FactoidalError('factoidal: the engine loader returned no entry object', { code: 'host.io' });
|
|
355
279
|
return e;
|
|
356
280
|
})
|
|
357
|
-
.catch((err) => {
|
|
281
|
+
.catch((err) => {
|
|
282
|
+
entryPromise = undefined;
|
|
283
|
+
throw err instanceof FactoidalError ? err
|
|
284
|
+
: new FactoidalError(`factoidal: the engine did not load: ${err && err.message}`,
|
|
285
|
+
{ code: 'host.io', cause: err });
|
|
286
|
+
});
|
|
358
287
|
}
|
|
359
288
|
return entryPromise;
|
|
360
289
|
}
|
|
361
290
|
|
|
362
|
-
//
|
|
363
|
-
//
|
|
291
|
+
// The options object of a public function, checked by the engine (op
|
|
292
|
+
// apiCheck): unknown keys, format names, regimes, outputs and versions.
|
|
293
|
+
async function checkedOptions(who, options) {
|
|
294
|
+
return checkOptions(await entry(), who, options);
|
|
295
|
+
}
|
|
364
296
|
|
|
365
297
|
|
|
366
298
|
// ---------------------------------------------------------------
|
|
@@ -492,7 +424,7 @@ function buildApi(driver) {
|
|
|
492
424
|
if (typeof e.registerServiceEndpoint !== 'function') {
|
|
493
425
|
throw missingOpError('registerServiceEndpoint');
|
|
494
426
|
}
|
|
495
|
-
const opts =
|
|
427
|
+
const opts = await checkedOptions('registerServiceEndpoint', optionsOf('registerServiceEndpoint', options));
|
|
496
428
|
const nq = await nquadsOf(data, opts, 'registerServiceEndpoint');
|
|
497
429
|
return entryResult(e.registerServiceEndpoint(iri, nq),
|
|
498
430
|
'registerServiceEndpoint');
|
|
@@ -516,9 +448,9 @@ function buildApi(driver) {
|
|
|
516
448
|
const result = runOnce();
|
|
517
449
|
if (extPending.length === 0) return result;
|
|
518
450
|
if (round >= EXT_MAX_ROUNDS) {
|
|
519
|
-
throw new
|
|
451
|
+
throw new FactoidalError(
|
|
520
452
|
'extension functions: async resolution did not converge ' +
|
|
521
|
-
`within ${EXT_MAX_ROUNDS} evaluation rounds
|
|
453
|
+
`within ${EXT_MAX_ROUNDS} evaluation rounds`, { code: 'capacity.exceeded' });
|
|
522
454
|
}
|
|
523
455
|
const pend = extPending;
|
|
524
456
|
extPending = [];
|
|
@@ -588,7 +520,9 @@ function buildApi(driver) {
|
|
|
588
520
|
tables() {
|
|
589
521
|
if (!tablesCache) {
|
|
590
522
|
requireEntryFn(e, 'engineTables', 'format and regime tables');
|
|
591
|
-
|
|
523
|
+
const t = entryResult(e.engineTables(), 'engineTables');
|
|
524
|
+
tablesCache = { ...t.tables, errorCodes: t.errorCodes || [],
|
|
525
|
+
errorModelExemptOps: t.errorModelExemptOps || [] };
|
|
592
526
|
for (const k of Object.keys(WRITER_TAGS)) delete WRITER_TAGS[k];
|
|
593
527
|
Object.assign(WRITER_TAGS, tablesCache.writerFormats);
|
|
594
528
|
}
|
|
@@ -643,7 +577,7 @@ function buildApi(driver) {
|
|
|
643
577
|
requireEntryFn(c.e, 'detectFormat', `${who} (format inference)`);
|
|
644
578
|
return { tag: entryResult(c.e.detectFormat(text), who).format, inferred: true };
|
|
645
579
|
}
|
|
646
|
-
return { tag: parseTagOf(c.tables(), format), inferred: false };
|
|
580
|
+
return { tag: parseTagOf(c.e, c.tables(), format, who), inferred: false };
|
|
647
581
|
}
|
|
648
582
|
|
|
649
583
|
// Open `text` as a Dataset. A parse error names the format and says
|
|
@@ -655,11 +589,10 @@ function buildApi(driver) {
|
|
|
655
589
|
try {
|
|
656
590
|
r = parseResult(c.e.datasetOpen(text, tag, baseIRI || ''), who, text);
|
|
657
591
|
} catch (err) {
|
|
658
|
-
if (
|
|
659
|
-
err.
|
|
660
|
-
err.
|
|
592
|
+
if (err instanceof FactoidalError) {
|
|
593
|
+
err.formatInferred = inferred;
|
|
594
|
+
err.format = err.format || tag;
|
|
661
595
|
}
|
|
662
|
-
err.format = err.format || tag;
|
|
663
596
|
throw err;
|
|
664
597
|
}
|
|
665
598
|
return c.wrap(r, undefined, { format: tag, formatInferred: inferred });
|
|
@@ -709,43 +642,29 @@ function buildApi(driver) {
|
|
|
709
642
|
|
|
710
643
|
async function queryDatasetHandle(c, ds, sparql, opts) {
|
|
711
644
|
if (typeof sparql !== 'string') throw new TypeError('query: sparql must be a string');
|
|
645
|
+
// The options were checked by the engine (op apiCheck, as 'query' or
|
|
646
|
+
// 'Dataset.query'): regime, version, output and their combination.
|
|
712
647
|
const entail = opts.entail || 'none';
|
|
713
|
-
const tables = c.tables();
|
|
714
|
-
const ikl = iklRefusalOf(tables, 'query', entail);
|
|
715
|
-
if (ikl) throw new TypeError(ikl);
|
|
716
|
-
if (!tables.entailRegimes.includes(entail)) {
|
|
717
|
-
throw new TypeError(`query: entail must be one of ${tables.entailRegimes.join(', ')}`);
|
|
718
|
-
}
|
|
719
|
-
if (opts.version !== undefined && !['1.1', '1.2'].includes(String(opts.version))) {
|
|
720
|
-
throw new TypeError("query: version must be '1.1' or '1.2'");
|
|
721
|
-
}
|
|
722
648
|
const sparql12 = opts.sparql12 === true || String(opts.version || '') === '1.2';
|
|
723
649
|
const e = c.e;
|
|
724
650
|
if (entail !== 'none' || opts.output !== undefined) {
|
|
725
651
|
// queryRaw over the stored dataset: an entailment regime, or a
|
|
726
652
|
// results format (SPARQL 1.1 Query Results JSON, XML, CSV, TSV).
|
|
727
|
-
if (sparql12) {
|
|
728
|
-
throw new TypeError(
|
|
729
|
-
"query: entail/output and SPARQL 1.2 cannot be combined; that path reads SPARQL 1.1");
|
|
730
|
-
}
|
|
731
653
|
const output = opts.output || 'json';
|
|
732
|
-
if (!tables.queryOutputs.includes(output)) {
|
|
733
|
-
throw new TypeError(`query: output must be one of ${tables.queryOutputs.join(', ')}`);
|
|
734
|
-
}
|
|
735
654
|
const form = queryFormOf(e, sparql);
|
|
736
655
|
const graphForm = form === 'construct' || form === 'describe';
|
|
737
656
|
requireEntryFn(e, 'datasetQueryRaw', `query {entail/output}`);
|
|
738
657
|
if (opts.output === undefined) {
|
|
739
658
|
// An entailment regime with the default answer shape.
|
|
740
659
|
const r = entryResult(e.datasetQueryRaw(ds.handle, sparql, JSON.stringify({
|
|
741
|
-
entail, output: graphForm ? 'ntriples' : 'json' })), 'query');
|
|
660
|
+
entail, output: graphForm ? 'ntriples' : 'json' })), 'query', sparql);
|
|
742
661
|
if (graphForm) return c.fromNQuads(r.text, ds.prefixes);
|
|
743
662
|
const srj = JSON.parse(r.text);
|
|
744
663
|
if (typeof srj.boolean === 'boolean') return srj.boolean;
|
|
745
664
|
return bindingsFromSrj(srj);
|
|
746
665
|
}
|
|
747
666
|
const r = entryResult(e.datasetQueryRaw(ds.handle, sparql,
|
|
748
|
-
JSON.stringify({ entail, output })), 'query');
|
|
667
|
+
JSON.stringify({ entail, output })), 'query', sparql);
|
|
749
668
|
return output === 'json' && !graphForm ? JSON.parse(r.text) : r.text;
|
|
750
669
|
}
|
|
751
670
|
const fn = sparql12 ? e.datasetQuery12 : e.datasetQuery;
|
|
@@ -753,7 +672,7 @@ function buildApi(driver) {
|
|
|
753
672
|
throw missingOpError(`query (datasetQuery${sparql12 ? '12' : ''})`, e,
|
|
754
673
|
sparql12 ? 'datasetQuery12' : 'datasetQuery');
|
|
755
674
|
}
|
|
756
|
-
const r = await withExtensionRounds(() => entryResult(fn(ds.handle, sparql), 'query'));
|
|
675
|
+
const r = await withExtensionRounds(() => entryResult(fn(ds.handle, sparql), 'query', sparql));
|
|
757
676
|
if (r.kind === 'ask') return r.boolean;
|
|
758
677
|
if (r.kind === 'construct' || r.kind === 'describe') {
|
|
759
678
|
return c.fromNQuads(r.nquads, ds.prefixes);
|
|
@@ -783,10 +702,7 @@ function buildApi(driver) {
|
|
|
783
702
|
|
|
784
703
|
async function closureOnHandle(c, ds, regime) {
|
|
785
704
|
const r0 = c.tables().closureAliases[String(regime || '').toLowerCase()];
|
|
786
|
-
if (!r0) {
|
|
787
|
-
throw new TypeError(
|
|
788
|
-
`closure: regime must be one of rdfs, owl-rl, rho-df, rdfs-plus (got '${regime}')`);
|
|
789
|
-
}
|
|
705
|
+
if (!r0) checkOptions(c.e, 'Dataset.closure', { regime: String(regime == null ? '' : regime) });
|
|
790
706
|
requireEntryFn(c.e, 'datasetClosure', 'closure');
|
|
791
707
|
const r = entryResult(c.e.datasetClosure(ds.handle, r0), 'closure');
|
|
792
708
|
return c.wrap(r, ds.prefixes);
|
|
@@ -797,7 +713,7 @@ function buildApi(driver) {
|
|
|
797
713
|
|
|
798
714
|
/**
|
|
799
715
|
* Parse one RDF document into a Dataset. Strict by default: a syntax
|
|
800
|
-
* error rejects with a
|
|
716
|
+
* error rejects with a FactoidalError (`code`, `position`, `spec`; for Turtle,
|
|
801
717
|
* TriG, N-Triples and N-Quads). `{lenient:true}` skips each bad statement
|
|
802
718
|
* of Turtle, TriG, N-Triples and N-Quads and lists one diagnostic per
|
|
803
719
|
* skipped statement in `dataset.diagnostics`
|
|
@@ -811,8 +727,7 @@ function buildApi(driver) {
|
|
|
811
727
|
*/
|
|
812
728
|
async function parse(text, options) {
|
|
813
729
|
if (typeof text !== 'string') throw new TypeError('parse: text must be a string');
|
|
814
|
-
const opts =
|
|
815
|
-
['format', 'baseIRI', 'lenient', 'maxErrors']);
|
|
730
|
+
const opts = await checkedOptions('parse', optionsOf('parse', options));
|
|
816
731
|
const c = await ctx();
|
|
817
732
|
if (opts.lenient) {
|
|
818
733
|
const { tag, inferred } = formatTagOf(c, text, opts.format, 'parse');
|
|
@@ -840,7 +755,7 @@ function buildApi(driver) {
|
|
|
840
755
|
* @returns {Promise<Dataset>}
|
|
841
756
|
*/
|
|
842
757
|
async function openDataset(data, options) {
|
|
843
|
-
const opts =
|
|
758
|
+
const opts = await checkedOptions('openDataset', optionsOf('openDataset', options));
|
|
844
759
|
const c = await ctx();
|
|
845
760
|
if (data instanceof Dataset) return data.clone();
|
|
846
761
|
return datasetOf(c, data, opts, 'openDataset').ds;
|
|
@@ -860,8 +775,7 @@ function buildApi(driver) {
|
|
|
860
775
|
*/
|
|
861
776
|
async function query(data, sparql, options) {
|
|
862
777
|
if (typeof sparql !== 'string') throw new TypeError('query: sparql must be a string');
|
|
863
|
-
const opts =
|
|
864
|
-
['format', 'baseIRI', 'entail', 'sparql12', 'version', 'output']);
|
|
778
|
+
const opts = await checkedOptions('query', optionsOf('query', options));
|
|
865
779
|
return withDataset(data, opts, 'query', (c, ds) => queryDatasetHandle(c, ds, sparql, opts));
|
|
866
780
|
}
|
|
867
781
|
|
|
@@ -894,7 +808,7 @@ function buildApi(driver) {
|
|
|
894
808
|
*/
|
|
895
809
|
async function update(data, updateText, options) {
|
|
896
810
|
if (typeof updateText !== 'string') throw new TypeError('update: updateText must be a string');
|
|
897
|
-
const opts =
|
|
811
|
+
const opts = await checkedOptions('update', optionsOf('update', options));
|
|
898
812
|
const c = await ctx();
|
|
899
813
|
const { ds, temp } = datasetOf(c, data, opts, 'update');
|
|
900
814
|
const out = temp ? ds : ds.clone();
|
|
@@ -918,15 +832,8 @@ function buildApi(driver) {
|
|
|
918
832
|
* @returns {Promise<string>}
|
|
919
833
|
*/
|
|
920
834
|
async function serialize(data, options) {
|
|
921
|
-
const opts =
|
|
922
|
-
['format', 'version', 'prefixes', 'literalShorthand', 'useNativeTypes',
|
|
923
|
-
'useRdfType', 'rdfDirection', 'inputFormat', 'baseIRI']);
|
|
835
|
+
const opts = await checkedOptions('serialize', optionsOf('serialize', options));
|
|
924
836
|
const { inputFormat, baseIRI, ...writeOpts } = opts;
|
|
925
|
-
const writerTags = (await ctx()).tables().writerFormats;
|
|
926
|
-
if (writeOpts.format !== undefined && !writerTags[String(writeOpts.format).toLowerCase()]) {
|
|
927
|
-
throw new TypeError(`serialize: unknown format '${writeOpts.format}'. Known formats: ` +
|
|
928
|
-
[...new Set(Object.values(writerTags))].join(', '));
|
|
929
|
-
}
|
|
930
837
|
return withDataset(data, { format: inputFormat, baseIRI }, 'serialize',
|
|
931
838
|
(_c, ds) => ds.serialize(writeOpts));
|
|
932
839
|
}
|
|
@@ -938,7 +845,7 @@ function buildApi(driver) {
|
|
|
938
845
|
* @returns {Promise<string>}
|
|
939
846
|
*/
|
|
940
847
|
async function canonicalize(data, options) {
|
|
941
|
-
const opts =
|
|
848
|
+
const opts = await checkedOptions('canonicalize', optionsOf('canonicalize', options));
|
|
942
849
|
return withDataset(data, opts, 'canonicalize', (_c, ds) => ds.toString('rdfc'));
|
|
943
850
|
}
|
|
944
851
|
|
|
@@ -994,7 +901,7 @@ function buildApi(driver) {
|
|
|
994
901
|
* @returns {Promise<boolean>}
|
|
995
902
|
*/
|
|
996
903
|
async function isomorphic(a, b, options) {
|
|
997
|
-
const opts =
|
|
904
|
+
const opts = await checkedOptions('isomorphic', optionsOf('isomorphic', options));
|
|
998
905
|
return withDataset(a, opts, 'isomorphic', (c, da) =>
|
|
999
906
|
withDataset(b, opts, 'isomorphic', (_c, db) => {
|
|
1000
907
|
requireEntryFn(c.e, 'datasetIsomorphic', 'isomorphic');
|
|
@@ -1040,7 +947,7 @@ function buildApi(driver) {
|
|
|
1040
947
|
* @returns {Promise<{conforms: boolean, report: Dataset}>}
|
|
1041
948
|
*/
|
|
1042
949
|
async function shaclValidate(data, shapes, options) {
|
|
1043
|
-
const opts =
|
|
950
|
+
const opts = await checkedOptions('shaclValidate', optionsOf('shaclValidate', options));
|
|
1044
951
|
return withDataset(data, opts, 'shaclValidate(data)', (c, ds) => shaclOnHandle(c, ds, shapes, opts));
|
|
1045
952
|
}
|
|
1046
953
|
|
|
@@ -1056,7 +963,7 @@ function buildApi(driver) {
|
|
|
1056
963
|
* @returns {Promise<boolean>}
|
|
1057
964
|
*/
|
|
1058
965
|
async function shexValidate(data, schema, focus, shape, options) {
|
|
1059
|
-
const opts =
|
|
966
|
+
const opts = await checkedOptions('shexValidate', optionsOf('shexValidate', options));
|
|
1060
967
|
if (typeof schema !== 'string') throw new TypeError('shexValidate: schema must be a string');
|
|
1061
968
|
return withDataset(data, opts, 'shexValidate(data)', (c, ds) => shexOnHandle(c, ds, schema, focus, shape));
|
|
1062
969
|
}
|
|
@@ -1069,7 +976,7 @@ function buildApi(driver) {
|
|
|
1069
976
|
* @returns {Promise<Dataset>}
|
|
1070
977
|
*/
|
|
1071
978
|
async function closure(data, regime, options) {
|
|
1072
|
-
const opts =
|
|
979
|
+
const opts = await checkedOptions('closure', optionsOf('closure', options));
|
|
1073
980
|
return withDataset(data, opts, 'closure', (c, ds) => closureOnHandle(c, ds, regime));
|
|
1074
981
|
}
|
|
1075
982
|
|
|
@@ -1082,10 +989,8 @@ function buildApi(driver) {
|
|
|
1082
989
|
*/
|
|
1083
990
|
async function owlClosure(data, mode, options) {
|
|
1084
991
|
const m = String(mode || '').toLowerCase();
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
throw new TypeError(`owlClosure: mode must be 'RDFS' or 'OWL-RL' (got '${mode}')`);
|
|
1088
|
-
}
|
|
992
|
+
// The engine checks the mode against its regime table (op apiCheck).
|
|
993
|
+
await checkedOptions('owlClosure', { mode: String(mode == null ? '' : mode) });
|
|
1089
994
|
return closure(data, m, options);
|
|
1090
995
|
}
|
|
1091
996
|
|
|
@@ -1109,7 +1014,7 @@ function buildApi(driver) {
|
|
|
1109
1014
|
* @returns {Promise<{ok: boolean, fragment: boolean}>}
|
|
1110
1015
|
*/
|
|
1111
1016
|
async function coreRdfsCheck(data, options) {
|
|
1112
|
-
const opts =
|
|
1017
|
+
const opts = await checkedOptions('coreRdfsCheck', optionsOf('coreRdfsCheck', options));
|
|
1113
1018
|
const nq = await nquadsOf(data, opts, 'coreRdfsCheck(data)');
|
|
1114
1019
|
const e = await entry();
|
|
1115
1020
|
requireEntryFn(e, 'rhoDfFragmentCheck', 'core-RDFS fragment check');
|
|
@@ -1137,7 +1042,7 @@ function buildApi(driver) {
|
|
|
1137
1042
|
* @returns {Promise<{dataset: Dataset, addedCount: number, budgetHit?: boolean}>}
|
|
1138
1043
|
*/
|
|
1139
1044
|
async function tableauMaterialise(data, options) {
|
|
1140
|
-
const opts =
|
|
1045
|
+
const opts = await checkedOptions('tableauMaterialise', optionsOf('tableauMaterialise', options));
|
|
1141
1046
|
const nq = await nquadsOf(data, opts, 'tableauMaterialise(data)');
|
|
1142
1047
|
const c = await ctx();
|
|
1143
1048
|
requireEntryFn(c.e, 'tableauMaterialise', 'OWL tableau materialisation');
|
|
@@ -1157,7 +1062,7 @@ function buildApi(driver) {
|
|
|
1157
1062
|
* @returns {Promise<{inconsistent: boolean, rlAlone: boolean}>}
|
|
1158
1063
|
*/
|
|
1159
1064
|
async function tableauDlInconsistent(data, options) {
|
|
1160
|
-
const opts =
|
|
1065
|
+
const opts = await checkedOptions('tableauDlInconsistent', optionsOf('tableauDlInconsistent', options));
|
|
1161
1066
|
const nq = await nquadsOf(data, opts, 'tableauDlInconsistent(data)');
|
|
1162
1067
|
const e = await entry();
|
|
1163
1068
|
requireEntryFn(e, 'tableauDlInconsistent', 'OWL tableau DL inconsistency check');
|
|
@@ -1166,14 +1071,15 @@ function buildApi(driver) {
|
|
|
1166
1071
|
}
|
|
1167
1072
|
|
|
1168
1073
|
/**
|
|
1169
|
-
* OWL
|
|
1170
|
-
*
|
|
1074
|
+
* Checked atomic OWL consistency of the default graph: false is a checked
|
|
1075
|
+
* contradiction; null means unsupported syntax or no accepted refutation.
|
|
1076
|
+
* No model producer exists. fuel does not change the fixed checker bound.
|
|
1171
1077
|
* @param {Dataset|string|Array} data
|
|
1172
1078
|
* @param {{format?: string, baseIRI?: string, fuel?: number|string}} [options]
|
|
1173
1079
|
* @returns {Promise<{consistent: boolean|null, reason?: string}>}
|
|
1174
1080
|
*/
|
|
1175
1081
|
async function owlIsConsistent(data, options) {
|
|
1176
|
-
const opts =
|
|
1082
|
+
const opts = await checkedOptions('owlIsConsistent', optionsOf('owlIsConsistent', options));
|
|
1177
1083
|
const nq = await nquadsOf(data, opts, 'owlIsConsistent(data)');
|
|
1178
1084
|
const e = await entry();
|
|
1179
1085
|
requireEntryFn(e, 'owlIsConsistent', 'OWL DL consistency check');
|
|
@@ -1193,7 +1099,7 @@ function buildApi(driver) {
|
|
|
1193
1099
|
* @returns {Promise<{entailed: boolean|null, via: 'closure'|'refutation', reason?: string}>}
|
|
1194
1100
|
*/
|
|
1195
1101
|
async function owlEntails(premise, conclusion, options) {
|
|
1196
|
-
const opts =
|
|
1102
|
+
const opts = await checkedOptions('owlEntails', optionsOf('owlEntails', options));
|
|
1197
1103
|
const premiseNq = await nquadsOf(premise, opts, 'owlEntails(premise)');
|
|
1198
1104
|
const conclusionNq = await nquadsOf(conclusion, opts, 'owlEntails(conclusion)');
|
|
1199
1105
|
const e = await entry();
|
|
@@ -1217,8 +1123,8 @@ function buildApi(driver) {
|
|
|
1217
1123
|
async function rmlMap(mapping, sourceData, sourceKind, options) {
|
|
1218
1124
|
if (typeof sourceData !== 'string') throw new TypeError('rmlMap: sourceData must be a string');
|
|
1219
1125
|
const kind = String(sourceKind || '').toLowerCase();
|
|
1220
|
-
|
|
1221
|
-
const opts =
|
|
1126
|
+
await checkedOptions('rmlMap', { sourceKind: kind });
|
|
1127
|
+
const opts = await checkedOptions('rmlMap', optionsOf('rmlMap', options));
|
|
1222
1128
|
return withDataset(mapping, opts, 'rmlMap(mapping)', (c, ds) => {
|
|
1223
1129
|
requireEntryFn(c.e, 'datasetFromRml', 'RML mapping evaluation');
|
|
1224
1130
|
return c.wrap(entryResult(c.e.datasetFromRml(ds.handle, sourceData, kind), 'rmlMap'), {});
|
|
@@ -1238,7 +1144,7 @@ function buildApi(driver) {
|
|
|
1238
1144
|
if (typeof csvText !== 'string') throw new TypeError('csvwToRdf: csvText must be a string');
|
|
1239
1145
|
const meta = metadataJson == null ? '' : metadataJson;
|
|
1240
1146
|
if (typeof meta !== 'string') throw new TypeError('csvwToRdf: metadataJson must be a string');
|
|
1241
|
-
const opts =
|
|
1147
|
+
const opts = await checkedOptions('csvwToRdf', options);
|
|
1242
1148
|
const c = await ctx();
|
|
1243
1149
|
requireEntryFn(c.e, 'datasetFromCsvw', 'CSVW csv2rdf conversion');
|
|
1244
1150
|
const optionsJson = JSON.stringify({
|
|
@@ -1259,7 +1165,7 @@ function buildApi(driver) {
|
|
|
1259
1165
|
*/
|
|
1260
1166
|
async function jsonldToRdf(jsonldText, options) {
|
|
1261
1167
|
if (typeof jsonldText !== 'string') throw new TypeError('jsonldToRdf: jsonldText must be a string');
|
|
1262
|
-
const opts =
|
|
1168
|
+
const opts = await checkedOptions('jsonldToRdf', options);
|
|
1263
1169
|
const c = await ctx();
|
|
1264
1170
|
requireEntryFn(c.e, 'datasetFromJsonLd', 'jsonldToRdf');
|
|
1265
1171
|
const optionsJson = JSON.stringify({
|
|
@@ -1282,7 +1188,7 @@ function buildApi(driver) {
|
|
|
1282
1188
|
*/
|
|
1283
1189
|
async function rifEval(data, rifRulesXml, options) {
|
|
1284
1190
|
if (typeof rifRulesXml !== 'string') throw new TypeError('rifEval: rifRulesXml must be a string');
|
|
1285
|
-
const opts =
|
|
1191
|
+
const opts = await checkedOptions('rifEval', optionsOf('rifEval', options));
|
|
1286
1192
|
return withDataset(data, opts, 'rifEval(data)', (c, ds) => {
|
|
1287
1193
|
requireEntryFn(c.e, 'datasetRifEval', 'RIF Core evaluation');
|
|
1288
1194
|
return c.wrap(entryResult(c.e.datasetRifEval(rifRulesXml, ds.handle), 'rifEval'), ds.prefixes);
|
|
@@ -1298,8 +1204,7 @@ function buildApi(driver) {
|
|
|
1298
1204
|
* @returns {Promise<any>}
|
|
1299
1205
|
*/
|
|
1300
1206
|
async function jsonldFromRdf(data, options) {
|
|
1301
|
-
const opts =
|
|
1302
|
-
['useNativeTypes', 'useRdfType', 'rdfDirection', 'format', 'baseIRI']);
|
|
1207
|
+
const opts = await checkedOptions('jsonldFromRdf', optionsOf('jsonldFromRdf', options));
|
|
1303
1208
|
const { format, baseIRI, ...w } = opts;
|
|
1304
1209
|
return withDataset(data, { format, baseIRI }, 'jsonldFromRdf(data)',
|
|
1305
1210
|
(_c, ds) => JSON.parse(ds.toString({ format: 'jsonld', ...w })));
|
|
@@ -2012,29 +1917,16 @@ function buildApi(driver) {
|
|
|
2012
1917
|
if (typeof queryString !== 'string') {
|
|
2013
1918
|
throw new TypeError('queryRaw: queryString must be a string');
|
|
2014
1919
|
}
|
|
2015
|
-
const opts =
|
|
1920
|
+
const opts = await checkedOptions('queryRaw', options);
|
|
2016
1921
|
const dataFormat = String(opts.dataFormat || 'turtle').toLowerCase();
|
|
2017
1922
|
const entail = opts.entail || 'none';
|
|
2018
1923
|
const output = opts.output || 'json';
|
|
2019
1924
|
const tables = (await ctx()).tables();
|
|
2020
|
-
if (!(dataFormat in tables.rawDataFormats)) {
|
|
2021
|
-
throw new TypeError(`queryRaw: unknown dataFormat '${opts.dataFormat}'`);
|
|
2022
|
-
}
|
|
2023
|
-
if (!tables.entailRegimes.includes(entail)) {
|
|
2024
|
-
throw new TypeError(`queryRaw: entail must be one of ${tables.entailRegimes.join(', ')}`);
|
|
2025
|
-
}
|
|
2026
|
-
if (!tables.queryOutputs.includes(output)) {
|
|
2027
|
-
throw new TypeError(`queryRaw: output must be one of ${tables.queryOutputs.join(', ')}`);
|
|
2028
|
-
}
|
|
2029
1925
|
const e = await entry();
|
|
2030
1926
|
requireEntryFn(e, 'queryRaw', 'queryRaw');
|
|
2031
|
-
const r =
|
|
2032
|
-
JSON.stringify({ dataFormat: tables.rawDataFormats[dataFormat], entail, output }))
|
|
2033
|
-
|
|
2034
|
-
const err = new Error('SPARQL query failed: ' + r.error);
|
|
2035
|
-
err.stderr = String(r.error);
|
|
2036
|
-
throw err;
|
|
2037
|
-
}
|
|
1927
|
+
const r = entryResult(e.queryRaw(dataString, queryString,
|
|
1928
|
+
JSON.stringify({ dataFormat: tables.rawDataFormats[dataFormat], entail, output })),
|
|
1929
|
+
'queryRaw', queryString);
|
|
2038
1930
|
return output === 'json' ? JSON.parse(r.text) : r.text;
|
|
2039
1931
|
}
|
|
2040
1932
|
|
|
@@ -2318,9 +2210,8 @@ function buildApi(driver) {
|
|
|
2318
2210
|
ready,
|
|
2319
2211
|
engineTables,
|
|
2320
2212
|
Dataset,
|
|
2321
|
-
DatasetHandle,
|
|
2322
2213
|
Store,
|
|
2323
|
-
|
|
2214
|
+
FactoidalError,
|
|
2324
2215
|
dataFactory,
|
|
2325
2216
|
// Metadata (issue #682): the same two fields on every driver's
|
|
2326
2217
|
// typed surface, so a caller can log/report which package version
|
|
@@ -2333,5 +2224,5 @@ function buildApi(driver) {
|
|
|
2333
2224
|
|
|
2334
2225
|
export {
|
|
2335
2226
|
buildApi, bindingsFromSrj, entryResult, parseResult,
|
|
2336
|
-
|
|
2227
|
+
missingOpError, FactoidalError, Store,
|
|
2337
2228
|
};
|