@factoidal/core 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/api.mjs CHANGED
@@ -22,66 +22,26 @@
22
22
  // JSDoc note "entry_jsoo.ml's X" below names that lineage. The Lean op of
23
23
  // the same name serves X now (formal/lean4/Wasm/Dispatch.lean lists them).
24
24
 
25
- import { dataFactory, termFromSrj, quadToNQuads } from '../rdfjs.mjs';
25
+ import { dataFactory, termFromSrj, termToJson, quadToJson, _setTextEngine } from '../rdfjs.mjs';
26
26
  import { Dataset, INTERNAL, checkOptions, makeRegistry, WRITER_TAGS } from './dataset.mjs';
27
27
  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
- const DATA_FORMAT_EXT = {
33
- turtle: 'ttl',
34
- ttl: 'ttl',
35
- ntriples: 'nt',
36
- nt: 'nt',
37
- nquads: 'nq',
38
- nq: 'nq',
39
- trig: 'trig',
40
- rdfxml: 'rdf',
41
- 'rdf-xml': 'rdf',
42
- rdf: 'rdf',
43
- jsonld: 'jsonld',
44
- 'json-ld': 'jsonld',
45
- // RDF 1.2 opt-in: these select the engine's RDF 1.2 parsers (triple
46
- // terms <<( s p o )>>, ~ reifiers, {| |} annotations, VERSION,
47
- // directional literals "x"@lang--dir; Wasm/Ops/Parse.lean's
48
- // `Mode.rdf12`); the plain names above stay RDF 1.1.
49
- turtle12: 'ttl12',
50
- ttl12: 'ttl12',
51
- ntriples12: 'nt12',
52
- nt12: 'nt12',
53
- nquads12: 'nq12',
54
- nq12: 'nq12',
55
- trig12: 'trig12',
56
- };
57
-
58
- // Canonical format tag for the engine's parse ops (parseToDatasetJson,
59
- // datasetOpen); the *12 tags select the RDF 1.2 mode.
60
- const DATA_FORMAT_TAG = {
61
- ttl: 'turtle', nt: 'ntriples', nq: 'nquads', trig: 'trig', rdf: 'rdfxml',
62
- jsonld: 'jsonld',
63
- ttl12: 'turtle12', nt12: 'ntriples12', nq12: 'nquads12', trig12: 'trig12',
64
- };
65
-
66
- const ENTAIL_VALUES = new Set(['none', 'RDFS', 'OWL-RL', 'x-rdfscore', 'x-rdfsplus']);
67
-
68
- // queryRaw's options (op queryRaw, Wasm/Ops/QueryExtras.lean).
69
- const RAW_OUTPUT_FORMATS = new Set(['json', 'csv', 'tsv', 'xml', 'table', 'ntriples']);
70
- const RAW_DATA_FORMATS = {
71
- turtle: 'turtle', ttl: 'turtle', ntriples: 'ntriples', nt: 'ntriples',
72
- nquads: 'nquads', nq: 'nquads', trig: 'trig', rdfxml: 'rdfxml',
73
- 'rdf-xml': 'rdfxml', rdf: 'rdfxml',
74
- };
33
+ // The format names, regimes and query outputs are the engine's tables (op
34
+ // engineTables, formal/lean4/Wasm/Ops/Handles.lean), read once per engine:
35
+ // no host copy (https://github.com/danbri/factoidal/issues/894 item 5).
75
36
 
76
- function extForFormat(fmt) {
37
+ // The parse tag a format name selects, from the engine's table.
38
+ // A name outside the table is refused by the engine (op apiCheck,
39
+ // code format.unknown).
40
+ function parseTagOf(e, tables, fmt, who) {
77
41
  const key = String(fmt || 'turtle').toLowerCase();
78
- if (!(key in DATA_FORMAT_EXT)) {
79
- throw new TypeError(
80
- `Unknown format '${fmt}'. Expected one of: ` +
81
- Object.keys(DATA_FORMAT_EXT).join(', ')
82
- );
83
- }
84
- return DATA_FORMAT_EXT[key];
42
+ const tag = tables.parseFormats[key];
43
+ if (tag === undefined) checkOptions(e, who || 'parse', { format: String(fmt) });
44
+ return tag;
85
45
  }
86
46
 
87
47
  // Where a function takes an options object, a bare string names the
@@ -102,28 +62,37 @@ function optionsOf(fn, options) {
102
62
  // package, or a dispatch table that does not serve it). When the entry is
103
63
  // the core engine and the op is one only the full engine serves, the error
104
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.
105
67
  function missingOpError(what, e, op) {
106
- if (e && e.profile === 'core' && op && Array.isArray(e.fullOnlyOps) &&
107
- e.fullOnlyOps.includes(op)) {
108
- return new Error(`${what}: ${fullEngineMessage(`the op '${op}'`)}`);
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
+ }
109
81
  }
110
- return new Error(
82
+ return new FactoidalError(
111
83
  `${what}: the loaded Lean wasm module has no dispatch op for this ` +
112
84
  'operation. The module is older than this package; rebuild it with ' +
113
85
  'formal/lean4/Wasm/build-wasm.sh, or point FACTOIDAL_L4_ASSETS at a ' +
114
- 'current build.');
86
+ 'current build.', { code: 'op.unknown' });
115
87
  }
116
88
 
117
- // Detect the query form for result-shape dispatch. IRIs and comments
118
- // are blanked first so '#' inside a PREFIX IRI cannot hide the verb.
119
- // The engine's SPARQL parser remains the authority — this only picks
120
- // the output shape.
121
- function sniffQueryForm(sparql) {
122
- const cleaned = String(sparql)
123
- .replace(/<[^>]*>/g, ' ')
124
- .replace(/#[^\n]*/g, ' ');
125
- const m = cleaned.match(/\b(select|ask|construct|describe)\b/i);
126
- return m ? m[1].toLowerCase() : 'select';
89
+ // The query form, from the engine's SPARQL parser (op queryForm). A query
90
+ // the parser rejects answers 'select'; the query op then reports the parse
91
+ // error itself.
92
+ function queryFormOf(e, sparql) {
93
+ if (typeof e.queryForm !== 'function') return 'select';
94
+ const r = JSON.parse(e.queryForm(String(sparql)));
95
+ return r.ok ? r.kind : 'select';
127
96
  }
128
97
 
129
98
  // Bytes for an op that reads the byte region (queryHdt, storeOpen): a
@@ -157,85 +126,23 @@ function bindingsFromSrj(srj) {
157
126
  }
158
127
 
159
128
  // ---------------------------------------------------------------------
160
- // Strict parsing (issue #344) -- ParseError, and the shared envelope
161
- // unwrapper every engine call routes through.
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).
162
132
  // ---------------------------------------------------------------------
163
133
 
164
- /**
165
- * A parse failure that carries a position: `line`/`column`/`offset`
166
- * when the failing engine call reported one (a parseToDatasetJson or
167
- * datasetOpen error over Turtle, TriG, N-Triples or N-Quads), else
168
- * `undefined`. RDF/XML and JSON-LD failures, and any non-parse engine
169
- * error, stay plain Error -- entryResult() below decides which one to
170
- * throw.
171
- *
172
- * Units: `line` and `column` are 1-based; `column` counts Unicode scalar
173
- * values, as the engine reports it. `offset` is a UTF-16 code-unit index
174
- * into the input JavaScript string, so `text.slice(err.offset)` starts
175
- * at the error. The engine counts Unicode scalar values;
176
- * utf16OffsetOf() below converts.
177
- */
178
- class ParseError extends Error {
179
- constructor(message, info) {
180
- super(message);
181
- this.name = 'ParseError';
182
- const i = info || {};
183
- this.line = i.line;
184
- this.column = i.column;
185
- this.offset = i.offset;
186
- this.format = i.format;
187
- if (typeof Error.captureStackTrace === 'function') {
188
- Error.captureStackTrace(this, ParseError);
189
- }
190
- }
191
- }
192
-
193
- // Unwrap one dispatch-op JSON envelope ({"ok":true,...} |
194
- // {"ok":false,"error":...}). A `line` or `offset` member on the error
195
- // envelope means the failing call was a parse (Turtle/TriG/N-Triples/
196
- // N-Quads carry a position; RDF/XML and JSON-LD carry a message only)
197
- // -- throw ParseError so a caller can branch on position without
198
- // string-matching the message. Every other failure stays a plain
199
- // Error, as before. Pure (no per-driver state), so every buildApi()
200
- // instance and DatasetHandle share this one function.
201
- function entryResult(jsonText, what) {
202
- const r = JSON.parse(jsonText);
203
- if (!r.ok) {
204
- if (r.line !== undefined || r.offset !== undefined) {
205
- throw new ParseError(`${what}: ${r.error}`, {
206
- line: r.line, column: r.column, offset: r.offset, format: r.format,
207
- });
208
- }
209
- throw new Error(`${what}: ${r.error}`);
210
- }
211
- return r;
134
+ function entryResult(jsonText, _what, text) {
135
+ return envelopeResult(jsonText, text);
212
136
  }
213
137
 
214
- // The Lean engine reports a parse error's `offset` as a count of Unicode
215
- // scalar values from the start of the input. A JavaScript string is
216
- // indexed in UTF-16 code units, where a character outside the Basic
217
- // Multilingual Plane (U+10000 and above) takes two. This converts the
218
- // engine's count into the UTF-16 index of the same position. A count past
219
- // the end of the text gives text.length. Iterating a string with for..of
220
- // yields one element per code point (a lone surrogate is one element, and
221
- // the engine reads it as one replacement character), so both sides count
222
- // the same units.
223
- function utf16OffsetOf(text, scalarOffset) {
224
- if (typeof text !== 'string' || !Number.isInteger(scalarOffset) || scalarOffset < 0) {
225
- return scalarOffset;
226
- }
227
- let units = 0;
228
- let scalars = 0;
229
- for (const ch of text) {
230
- if (scalars >= scalarOffset) break;
231
- units += ch.length;
232
- scalars += 1;
233
- }
234
- 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);
235
142
  }
236
143
 
237
144
  // The lenient parse's diagnostics (https://github.com/danbri/factoidal/issues/716)
238
- // with each `offset` converted as utf16OffsetOf() converts a ParseError's, so
145
+ // with each `offset` converted to a UTF-16 index as a FactoidalError's, so
239
146
  // `text.slice(d.offset)` starts at the error. `line` and `column` are kept as
240
147
  // the engine reports them. One pass over `text` for all diagnostics: the
241
148
  // offsets are visited in increasing order, whatever order the list has.
@@ -263,23 +170,6 @@ function utf16Diagnostics(text, diagnostics) {
263
170
  return diagnostics.map((d, i) => (units.has(i) ? { ...d, offset: units.get(i) } : d));
264
171
  }
265
172
 
266
- // entryResult() for an op that parsed `text`: a positioned ParseError gets
267
- // its offset converted to a UTF-16 index into `text`.
268
- function parseResult(jsonText, what, text) {
269
- try {
270
- return entryResult(jsonText, what);
271
- } catch (err) {
272
- if (err instanceof ParseError && typeof err.offset === 'number') {
273
- err.offset = utf16OffsetOf(text, err.offset);
274
- }
275
- throw err;
276
- }
277
- }
278
-
279
- // DatasetHandle: the name of the engine-resident dataset class before
280
- // 0.10.0. Deprecated alias of Dataset, kept for 0.10.x only.
281
- const DatasetHandle = Dataset;
282
-
283
173
  // ---------------------------------------------------------------------
284
174
  // The Lean persisted store (Shardborough generations: IBK3, IBK4, IBK5
285
175
  // blocks with their index sidecars; skills/shardborough-storage). A
@@ -322,7 +212,8 @@ class Store {
322
212
  get closed() { return this._closed; }
323
213
 
324
214
  _assertOpen(who) {
325
- if (this._closed) throw new Error(`Store.${who}: store handle '${this.handle}' is closed`);
215
+ // The engine answers handle.disposed for a closed store handle.
216
+ if (this._closed) entryResult(this._e.storeHandleClose(this.handle), `Store.${who}`);
326
217
  }
327
218
 
328
219
  /**
@@ -333,7 +224,7 @@ class Store {
333
224
  async query(sparql) {
334
225
  this._assertOpen('query');
335
226
  if (typeof sparql !== 'string') throw new TypeError('Store.query: sparql must be a string');
336
- const run = () => entryResult(this._e.storeHandleQuery(this.handle, sparql), 'Store.query');
227
+ const run = () => entryResult(this._e.storeHandleQuery(this.handle, sparql), 'Store.query', sparql);
337
228
  const r = this._withExtensionRounds ? await this._withExtensionRounds(run) : run();
338
229
  if (r.kind === 'ask') return r.boolean;
339
230
  if (r.kind === 'construct' || r.kind === 'describe') {
@@ -384,16 +275,24 @@ function buildApi(driver) {
384
275
  entryPromise = Promise.resolve()
385
276
  .then(() => driver.loadEntry())
386
277
  .then((e) => {
387
- if (!e) throw new Error('factoidal: the engine loader returned no entry object');
278
+ if (!e) throw new FactoidalError('factoidal: the engine loader returned no entry object', { code: 'host.io' });
388
279
  return e;
389
280
  })
390
- .catch((err) => { entryPromise = undefined; throw 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
+ });
391
287
  }
392
288
  return entryPromise;
393
289
  }
394
290
 
395
- // entryResult() is now module-level (see above ParseError) -- pure,
396
- // shared by every buildApi() instance and by DatasetHandle.
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
+ }
397
296
 
398
297
 
399
298
  // ---------------------------------------------------------------
@@ -525,7 +424,7 @@ function buildApi(driver) {
525
424
  if (typeof e.registerServiceEndpoint !== 'function') {
526
425
  throw missingOpError('registerServiceEndpoint');
527
426
  }
528
- const opts = checkOptions('registerServiceEndpoint', optionsOf('registerServiceEndpoint', options), ['format', 'baseIRI']);
427
+ const opts = await checkedOptions('registerServiceEndpoint', optionsOf('registerServiceEndpoint', options));
529
428
  const nq = await nquadsOf(data, opts, 'registerServiceEndpoint');
530
429
  return entryResult(e.registerServiceEndpoint(iri, nq),
531
430
  'registerServiceEndpoint');
@@ -549,9 +448,9 @@ function buildApi(driver) {
549
448
  const result = runOnce();
550
449
  if (extPending.length === 0) return result;
551
450
  if (round >= EXT_MAX_ROUNDS) {
552
- throw new Error(
451
+ throw new FactoidalError(
553
452
  'extension functions: async resolution did not converge ' +
554
- `within ${EXT_MAX_ROUNDS} evaluation rounds`);
453
+ `within ${EXT_MAX_ROUNDS} evaluation rounds`, { code: 'capacity.exceeded' });
555
454
  }
556
455
  const pend = extPending;
557
456
  extPending = [];
@@ -585,15 +484,16 @@ function buildApi(driver) {
585
484
  return out;
586
485
  }
587
486
 
588
- // A ShEx focus/shape-label argument is either a raw string (an IRI,
589
- // or "_:label" for a blank node -- the entry ABI's own convention)
590
- // or an RDF/JS term (NamedNode/BlankNode); accepting both lets a
591
- // caller pass a term straight out of a query() binding.
487
+ // A ShEx focus/shape-label argument is a string in N-Triples term syntax
488
+ // ("<http://...>" or "_:label"), which the engine reads with its one
489
+ // term-string reader, or an RDF/JS term (NamedNode/BlankNode); accepting
490
+ // both lets a caller pass a term straight out of a query() binding.
592
491
  function shexTermToString(t, who) {
593
- if (typeof t === 'string') return t;
492
+ if (typeof t === 'string') return JSON.stringify(termToJson(t));
594
493
  if (t && typeof t.termType === 'string') {
595
- if (t.termType === 'BlankNode') return `_:${t.value}`;
596
- if (t.termType === 'NamedNode') return t.value;
494
+ if (t.termType === 'BlankNode' || t.termType === 'NamedNode') {
495
+ return JSON.stringify(termToJson(t)); // the engine writes the node
496
+ }
597
497
  throw new TypeError(`${who}: expected an IRI or blank node term`);
598
498
  }
599
499
  throw new TypeError(`${who}: expected a string or an RDF/JS term`);
@@ -613,9 +513,21 @@ function buildApi(driver) {
613
513
  async function ctx() {
614
514
  const e = await entry();
615
515
  if (ctxCache && ctxCache.e === e) return ctxCache;
516
+ let tablesCache = null;
616
517
  const c = {
617
518
  e,
618
519
  registry,
520
+ tables() {
521
+ if (!tablesCache) {
522
+ requireEntryFn(e, 'engineTables', 'format and regime tables');
523
+ const t = entryResult(e.engineTables(), 'engineTables');
524
+ tablesCache = { ...t.tables, errorCodes: t.errorCodes || [],
525
+ errorModelExemptOps: t.errorModelExemptOps || [] };
526
+ for (const k of Object.keys(WRITER_TAGS)) delete WRITER_TAGS[k];
527
+ Object.assign(WRITER_TAGS, tablesCache.writerFormats);
528
+ }
529
+ return tablesCache;
530
+ },
619
531
  missing: (what, op) => missingOpError(what, e, op),
620
532
  wrap(r, prefixes, extra) {
621
533
  const { ok: _ok, handle, count, prefixes: own, ...info } = r;
@@ -638,16 +550,34 @@ function buildApi(driver) {
638
550
  async closure(ds, regime) { return closureOnHandle(c, ds, regime); },
639
551
  };
640
552
  ctxCache = c;
553
+ installTextEngine(e);
641
554
  return c;
642
555
  }
643
556
 
557
+ // rdfjs.mjs's N-Quads text functions (nquadsToQuads, quadsToNQuads,
558
+ // termToNQuads, DatasetCore.fromNQuads/toNQuads) call these engine ops.
559
+ function installTextEngine(e) {
560
+ if (typeof e.parseQuadsJson !== 'function') return;
561
+ _setTextEngine({
562
+ parseQuads(text) {
563
+ return parseResult(e.parseQuadsJson(text, 'nquads12', ''), 'nquadsToQuads', text).quads;
564
+ },
565
+ writeQuads(quadsJson) {
566
+ return entryResult(e.quadsToNQuads(JSON.stringify(quadsJson)), 'quadsToNQuads').nquads;
567
+ },
568
+ termTokens(termsJson) {
569
+ return entryResult(e.termsToNTriples(JSON.stringify(termsJson)), 'termToNQuads').tokens;
570
+ },
571
+ });
572
+ }
573
+
644
574
  // The parse tag of `format`, or the engine's inference when it is omitted.
645
575
  function formatTagOf(c, text, format, who) {
646
576
  if (format === undefined || format === null || format === '') {
647
577
  requireEntryFn(c.e, 'detectFormat', `${who} (format inference)`);
648
578
  return { tag: entryResult(c.e.detectFormat(text), who).format, inferred: true };
649
579
  }
650
- return { tag: DATA_FORMAT_TAG[extForFormat(format)], inferred: false };
580
+ return { tag: parseTagOf(c.e, c.tables(), format, who), inferred: false };
651
581
  }
652
582
 
653
583
  // Open `text` as a Dataset. A parse error names the format and says
@@ -659,11 +589,10 @@ function buildApi(driver) {
659
589
  try {
660
590
  r = parseResult(c.e.datasetOpen(text, tag, baseIRI || ''), who, text);
661
591
  } catch (err) {
662
- if (inferred) {
663
- err.message += ` (format inferred as '${tag}' because none was given; pass {format} to choose)`;
664
- err.formatInferred = true;
592
+ if (err instanceof FactoidalError) {
593
+ err.formatInferred = inferred;
594
+ err.format = err.format || tag;
665
595
  }
666
- err.format = err.format || tag;
667
596
  throw err;
668
597
  }
669
598
  return c.wrap(r, undefined, { format: tag, formatInferred: inferred });
@@ -713,49 +642,29 @@ function buildApi(driver) {
713
642
 
714
643
  async function queryDatasetHandle(c, ds, sparql, opts) {
715
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.
716
647
  const entail = opts.entail || 'none';
717
- if (/^x-ikl/i.test(entail)) {
718
- throw new TypeError(
719
- `query: entail '${entail}' is not implemented. The x-ikl-* ` +
720
- "entailment regimes are the owner's design " +
721
- "(danbri/factoidal#581); the Lean engine's dispatch for them " +
722
- 'was deleted on 2026-08-26 as collateral of the IKL-to-RDF ' +
723
- 'projection purge (danbri/factoidal#626). This is not a ' +
724
- 'policy exclusion.');
725
- }
726
- if (!ENTAIL_VALUES.has(entail)) {
727
- throw new TypeError(`query: entail must be one of ${[...ENTAIL_VALUES].join(', ')}`);
728
- }
729
- if (opts.version !== undefined && !['1.1', '1.2'].includes(String(opts.version))) {
730
- throw new TypeError("query: version must be '1.1' or '1.2'");
731
- }
732
648
  const sparql12 = opts.sparql12 === true || String(opts.version || '') === '1.2';
733
649
  const e = c.e;
734
650
  if (entail !== 'none' || opts.output !== undefined) {
735
651
  // queryRaw over the stored dataset: an entailment regime, or a
736
652
  // results format (SPARQL 1.1 Query Results JSON, XML, CSV, TSV).
737
- if (sparql12) {
738
- throw new TypeError(
739
- "query: entail/output and SPARQL 1.2 cannot be combined; that path reads SPARQL 1.1");
740
- }
741
653
  const output = opts.output || 'json';
742
- if (!RAW_OUTPUT_FORMATS.has(output)) {
743
- throw new TypeError(`query: output must be one of ${[...RAW_OUTPUT_FORMATS].join(', ')}`);
744
- }
745
- const form = sniffQueryForm(sparql);
654
+ const form = queryFormOf(e, sparql);
746
655
  const graphForm = form === 'construct' || form === 'describe';
747
656
  requireEntryFn(e, 'datasetQueryRaw', `query {entail/output}`);
748
657
  if (opts.output === undefined) {
749
658
  // An entailment regime with the default answer shape.
750
659
  const r = entryResult(e.datasetQueryRaw(ds.handle, sparql, JSON.stringify({
751
- entail, output: graphForm ? 'ntriples' : 'json' })), 'query');
660
+ entail, output: graphForm ? 'ntriples' : 'json' })), 'query', sparql);
752
661
  if (graphForm) return c.fromNQuads(r.text, ds.prefixes);
753
662
  const srj = JSON.parse(r.text);
754
663
  if (typeof srj.boolean === 'boolean') return srj.boolean;
755
664
  return bindingsFromSrj(srj);
756
665
  }
757
666
  const r = entryResult(e.datasetQueryRaw(ds.handle, sparql,
758
- JSON.stringify({ entail, output })), 'query');
667
+ JSON.stringify({ entail, output })), 'query', sparql);
759
668
  return output === 'json' && !graphForm ? JSON.parse(r.text) : r.text;
760
669
  }
761
670
  const fn = sparql12 ? e.datasetQuery12 : e.datasetQuery;
@@ -763,7 +672,7 @@ function buildApi(driver) {
763
672
  throw missingOpError(`query (datasetQuery${sparql12 ? '12' : ''})`, e,
764
673
  sparql12 ? 'datasetQuery12' : 'datasetQuery');
765
674
  }
766
- const r = await withExtensionRounds(() => entryResult(fn(ds.handle, sparql), 'query'));
675
+ const r = await withExtensionRounds(() => entryResult(fn(ds.handle, sparql), 'query', sparql));
767
676
  if (r.kind === 'ask') return r.boolean;
768
677
  if (r.kind === 'construct' || r.kind === 'describe') {
769
678
  return c.fromNQuads(r.nquads, ds.prefixes);
@@ -791,18 +700,9 @@ function buildApi(driver) {
791
700
  c.e.datasetShexValidate(ds.handle, schema, focusStr, shapeStr), 'shexValidate').verdict;
792
701
  }
793
702
 
794
- const CLOSURE_REGIMES = {
795
- rdfs: 'rdfs', 'owl-rl': 'owl-rl', owlrl: 'owl-rl', owl_rl: 'owl-rl',
796
- 'rho-df': 'rho-df', rhodf: 'rho-df', 'core-rdfs': 'rho-df',
797
- 'rdfs-plus': 'rdfs-plus', rdfsplus: 'rdfs-plus',
798
- };
799
-
800
703
  async function closureOnHandle(c, ds, regime) {
801
- const r0 = CLOSURE_REGIMES[String(regime || '').toLowerCase()];
802
- if (!r0) {
803
- throw new TypeError(
804
- `closure: regime must be one of rdfs, owl-rl, rho-df, rdfs-plus (got '${regime}')`);
805
- }
704
+ const r0 = c.tables().closureAliases[String(regime || '').toLowerCase()];
705
+ if (!r0) checkOptions(c.e, 'Dataset.closure', { regime: String(regime == null ? '' : regime) });
806
706
  requireEntryFn(c.e, 'datasetClosure', 'closure');
807
707
  const r = entryResult(c.e.datasetClosure(ds.handle, r0), 'closure');
808
708
  return c.wrap(r, ds.prefixes);
@@ -813,7 +713,7 @@ function buildApi(driver) {
813
713
 
814
714
  /**
815
715
  * Parse one RDF document into a Dataset. Strict by default: a syntax
816
- * error rejects with a ParseError (`line`/`column`/`offset` for Turtle,
716
+ * error rejects with a FactoidalError (`code`, `position`, `spec`; for Turtle,
817
717
  * TriG, N-Triples and N-Quads). `{lenient:true}` skips each bad statement
818
718
  * of Turtle, TriG, N-Triples and N-Quads and lists one diagnostic per
819
719
  * skipped statement in `dataset.diagnostics`
@@ -827,8 +727,7 @@ function buildApi(driver) {
827
727
  */
828
728
  async function parse(text, options) {
829
729
  if (typeof text !== 'string') throw new TypeError('parse: text must be a string');
830
- const opts = checkOptions('parse', optionsOf('parse', options),
831
- ['format', 'baseIRI', 'lenient', 'maxErrors']);
730
+ const opts = await checkedOptions('parse', optionsOf('parse', options));
832
731
  const c = await ctx();
833
732
  if (opts.lenient) {
834
733
  const { tag, inferred } = formatTagOf(c, text, opts.format, 'parse');
@@ -856,7 +755,7 @@ function buildApi(driver) {
856
755
  * @returns {Promise<Dataset>}
857
756
  */
858
757
  async function openDataset(data, options) {
859
- const opts = checkOptions('openDataset', optionsOf('openDataset', options), ['format', 'baseIRI']);
758
+ const opts = await checkedOptions('openDataset', optionsOf('openDataset', options));
860
759
  const c = await ctx();
861
760
  if (data instanceof Dataset) return data.clone();
862
761
  return datasetOf(c, data, opts, 'openDataset').ds;
@@ -876,8 +775,7 @@ function buildApi(driver) {
876
775
  */
877
776
  async function query(data, sparql, options) {
878
777
  if (typeof sparql !== 'string') throw new TypeError('query: sparql must be a string');
879
- const opts = checkOptions('query', optionsOf('query', options),
880
- ['format', 'baseIRI', 'entail', 'sparql12', 'version', 'output']);
778
+ const opts = await checkedOptions('query', optionsOf('query', options));
881
779
  return withDataset(data, opts, 'query', (c, ds) => queryDatasetHandle(c, ds, sparql, opts));
882
780
  }
883
781
 
@@ -892,15 +790,11 @@ function buildApi(driver) {
892
790
  if (typeof sparql !== 'string') {
893
791
  throw new TypeError('queryHdt: sparql must be a string');
894
792
  }
895
- const form = sniffQueryForm(sparql);
896
- if (form === 'construct' || form === 'describe') {
897
- throw new TypeError(
898
- `queryHdt: ${form.toUpperCase()} is not supported over an HDT file (SELECT/ASK only)`);
899
- }
900
793
  const e = await entry();
901
794
  requireEntryFn(e, 'queryHdt', 'queryHdt');
902
795
  const r = entryResult(e.queryHdt(bytesToUint8Array(hdtBytes, 'queryHdt'), sparql), 'queryHdt');
903
796
  if (r.kind === 'ask') return r.boolean;
797
+ if (r.kind === 'construct' || r.kind === 'describe') return (await ctx()).fromNQuads(r.nquads, {});
904
798
  return bindingsFromSrj(r.srj);
905
799
  }
906
800
 
@@ -914,7 +808,7 @@ function buildApi(driver) {
914
808
  */
915
809
  async function update(data, updateText, options) {
916
810
  if (typeof updateText !== 'string') throw new TypeError('update: updateText must be a string');
917
- const opts = checkOptions('update', optionsOf('update', options), ['format', 'baseIRI']);
811
+ const opts = await checkedOptions('update', optionsOf('update', options));
918
812
  const c = await ctx();
919
813
  const { ds, temp } = datasetOf(c, data, opts, 'update');
920
814
  const out = temp ? ds : ds.clone();
@@ -938,14 +832,8 @@ function buildApi(driver) {
938
832
  * @returns {Promise<string>}
939
833
  */
940
834
  async function serialize(data, options) {
941
- const opts = checkOptions('serialize', optionsOf('serialize', options),
942
- ['format', 'version', 'prefixes', 'literalShorthand', 'useNativeTypes',
943
- 'useRdfType', 'rdfDirection', 'inputFormat', 'baseIRI']);
835
+ const opts = await checkedOptions('serialize', optionsOf('serialize', options));
944
836
  const { inputFormat, baseIRI, ...writeOpts } = opts;
945
- if (writeOpts.format !== undefined && !WRITER_TAGS[String(writeOpts.format).toLowerCase()]) {
946
- throw new TypeError(`serialize: unknown format '${writeOpts.format}'. Known formats: ` +
947
- [...new Set(Object.values(WRITER_TAGS))].join(', '));
948
- }
949
837
  return withDataset(data, { format: inputFormat, baseIRI }, 'serialize',
950
838
  (_c, ds) => ds.serialize(writeOpts));
951
839
  }
@@ -957,7 +845,7 @@ function buildApi(driver) {
957
845
  * @returns {Promise<string>}
958
846
  */
959
847
  async function canonicalize(data, options) {
960
- const opts = checkOptions('canonicalize', optionsOf('canonicalize', options), ['format', 'baseIRI']);
848
+ const opts = await checkedOptions('canonicalize', optionsOf('canonicalize', options));
961
849
  return withDataset(data, opts, 'canonicalize', (_c, ds) => ds.toString('rdfc'));
962
850
  }
963
851
 
@@ -965,24 +853,24 @@ function buildApi(driver) {
965
853
  * The named graphs of a Dataset, each as a Dataset (default graph
966
854
  * excluded, first-seen order).
967
855
  * @param {Dataset} dataset
968
- * @returns {Array<[iri: string, graph: Dataset]>}
856
+ * @returns {Array<[name: NamedNode|BlankNode, graph: Dataset]>}
969
857
  */
970
858
  function graphs(dataset) {
971
859
  if (!(dataset instanceof Dataset)) throw new TypeError('graphs: expected a Dataset');
972
- const seen = new Map();
973
- for (const q of dataset) {
974
- const g = q.graph;
975
- if (!g || g.termType === 'DefaultGraph') continue;
976
- if (!seen.has(g.value)) seen.set(g.value, g);
977
- }
978
- const out = [];
979
- for (const [iri, gTerm] of seen) out.push([iri, dataset.match(null, null, null, gTerm)]);
980
- return out;
860
+ dataset.size; // throws the disposed error
861
+ const c = dataset._ctx;
862
+ requireEntryFn(c.e, 'datasetGraphNames', 'graphs');
863
+ const names = entryResult(c.e.datasetGraphNames(dataset.handle), 'graphs').graphs;
864
+ return names.map((g) => {
865
+ const term = termFromSrj(g);
866
+ return [term, dataset.match(null, null, null, term)];
867
+ });
981
868
  }
982
869
 
983
870
  /**
984
871
  * RDFC-1.0 canonical N-Quads of one graph: every quad's graph name is
985
- * dropped first. Text, not a digest.
872
+ * dropped first (op datasetGraphAsDefault with the union of the graphs).
873
+ * Text, not a digest.
986
874
  * @param {Dataset} datasetOrGraph
987
875
  * @returns {Promise<string>}
988
876
  */
@@ -990,9 +878,59 @@ function buildApi(driver) {
990
878
  if (!(datasetOrGraph instanceof Dataset)) {
991
879
  throw new TypeError('canonicalHash: expected a Dataset (e.g. one entry of graphs())');
992
880
  }
993
- const asDefault = datasetOrGraph.toArray()
994
- .map((q) => quadToNQuads(dataFactory.quad(q.subject, q.predicate, q.object))).join('');
995
- return canonicalize(asDefault, { format: 'nquads' });
881
+ datasetOrGraph.size; // throws the disposed error
882
+ const c = datasetOrGraph._ctx;
883
+ requireEntryFn(c.e, 'datasetGraphAsDefault', 'canonicalHash');
884
+ const merged = c.wrap(entryResult(c.e.datasetGraphAsDefault(datasetOrGraph.handle,
885
+ JSON.stringify({ type: 'union' })), 'canonicalHash'), {});
886
+ try {
887
+ return merged.toString('rdfc');
888
+ } finally {
889
+ merged.dispose();
890
+ }
891
+ }
892
+
893
+ /**
894
+ * Whether two datasets are isomorphic (RDF 1.1 Concepts 3.6 and 4; blank
895
+ * nodes inside RDF 1.2 triple terms included): the engine's
896
+ * Dataset.isomorphicOutcome (op datasetIsomorphic). Rejects when the
897
+ * blank-node search budget is exceeded.
898
+ * @param {Dataset|string|Array} a
899
+ * @param {Dataset|string|Array} b
900
+ * @param {{format?: string, baseIRI?: string}} [options] read text arguments
901
+ * @returns {Promise<boolean>}
902
+ */
903
+ async function isomorphic(a, b, options) {
904
+ const opts = await checkedOptions('isomorphic', optionsOf('isomorphic', options));
905
+ return withDataset(a, opts, 'isomorphic', (c, da) =>
906
+ withDataset(b, opts, 'isomorphic', (_c, db) => {
907
+ requireEntryFn(c.e, 'datasetIsomorphic', 'isomorphic');
908
+ return entryResult(c.e.datasetIsomorphic(da.handle, db.handle), 'isomorphic').isomorphic;
909
+ }));
910
+ }
911
+
912
+ /**
913
+ * A Dataset holding RDF/JS quads (op datasetFromQuadsJson). Each term is
914
+ * sent as its components; the engine reads no text.
915
+ * @param {Iterable<object>} quads
916
+ * @returns {Promise<Dataset>}
917
+ */
918
+ async function fromQuads(quads) {
919
+ const c = await ctx();
920
+ requireEntryFn(c.e, 'datasetFromQuadsJson', 'fromQuads');
921
+ const arr = [];
922
+ for (const q of quads) arr.push(quadToJson(q));
923
+ return c.wrap(entryResult(c.e.datasetFromQuadsJson(JSON.stringify(arr)), 'fromQuads'), {});
924
+ }
925
+
926
+ /** Load the engine; afterwards the N-Quads text functions of `@factoidal/core/rdfjs` work. */
927
+ async function ready() {
928
+ await ctx();
929
+ }
930
+
931
+ /** The engine's name tables (formats, regimes, query outputs, pack syntaxes). */
932
+ async function engineTables() {
933
+ return (await ctx()).tables();
996
934
  }
997
935
 
998
936
  // A wasm module older than this package can lack an op: `e[fnName]` is
@@ -1009,7 +947,7 @@ function buildApi(driver) {
1009
947
  * @returns {Promise<{conforms: boolean, report: Dataset}>}
1010
948
  */
1011
949
  async function shaclValidate(data, shapes, options) {
1012
- const opts = checkOptions('shaclValidate', optionsOf('shaclValidate', options), ['format', 'baseIRI']);
950
+ const opts = await checkedOptions('shaclValidate', optionsOf('shaclValidate', options));
1013
951
  return withDataset(data, opts, 'shaclValidate(data)', (c, ds) => shaclOnHandle(c, ds, shapes, opts));
1014
952
  }
1015
953
 
@@ -1025,7 +963,7 @@ function buildApi(driver) {
1025
963
  * @returns {Promise<boolean>}
1026
964
  */
1027
965
  async function shexValidate(data, schema, focus, shape, options) {
1028
- const opts = checkOptions('shexValidate', optionsOf('shexValidate', options), ['format', 'baseIRI']);
966
+ const opts = await checkedOptions('shexValidate', optionsOf('shexValidate', options));
1029
967
  if (typeof schema !== 'string') throw new TypeError('shexValidate: schema must be a string');
1030
968
  return withDataset(data, opts, 'shexValidate(data)', (c, ds) => shexOnHandle(c, ds, schema, focus, shape));
1031
969
  }
@@ -1038,7 +976,7 @@ function buildApi(driver) {
1038
976
  * @returns {Promise<Dataset>}
1039
977
  */
1040
978
  async function closure(data, regime, options) {
1041
- const opts = checkOptions('closure', optionsOf('closure', options), ['format', 'baseIRI']);
979
+ const opts = await checkedOptions('closure', optionsOf('closure', options));
1042
980
  return withDataset(data, opts, 'closure', (c, ds) => closureOnHandle(c, ds, regime));
1043
981
  }
1044
982
 
@@ -1051,9 +989,8 @@ function buildApi(driver) {
1051
989
  */
1052
990
  async function owlClosure(data, mode, options) {
1053
991
  const m = String(mode || '').toLowerCase();
1054
- if (!['rdfs', 'owl-rl', 'owlrl', 'owl_rl'].includes(m)) {
1055
- throw new TypeError(`owlClosure: mode must be 'RDFS' or 'OWL-RL' (got '${mode}')`);
1056
- }
992
+ // The engine checks the mode against its regime table (op apiCheck).
993
+ await checkedOptions('owlClosure', { mode: String(mode == null ? '' : mode) });
1057
994
  return closure(data, m, options);
1058
995
  }
1059
996
 
@@ -1077,7 +1014,7 @@ function buildApi(driver) {
1077
1014
  * @returns {Promise<{ok: boolean, fragment: boolean}>}
1078
1015
  */
1079
1016
  async function coreRdfsCheck(data, options) {
1080
- const opts = checkOptions('coreRdfsCheck', optionsOf('coreRdfsCheck', options), ['format', 'baseIRI']);
1017
+ const opts = await checkedOptions('coreRdfsCheck', optionsOf('coreRdfsCheck', options));
1081
1018
  const nq = await nquadsOf(data, opts, 'coreRdfsCheck(data)');
1082
1019
  const e = await entry();
1083
1020
  requireEntryFn(e, 'rhoDfFragmentCheck', 'core-RDFS fragment check');
@@ -1105,7 +1042,7 @@ function buildApi(driver) {
1105
1042
  * @returns {Promise<{dataset: Dataset, addedCount: number, budgetHit?: boolean}>}
1106
1043
  */
1107
1044
  async function tableauMaterialise(data, options) {
1108
- const opts = checkOptions('tableauMaterialise', optionsOf('tableauMaterialise', options), ['format', 'baseIRI']);
1045
+ const opts = await checkedOptions('tableauMaterialise', optionsOf('tableauMaterialise', options));
1109
1046
  const nq = await nquadsOf(data, opts, 'tableauMaterialise(data)');
1110
1047
  const c = await ctx();
1111
1048
  requireEntryFn(c.e, 'tableauMaterialise', 'OWL tableau materialisation');
@@ -1125,7 +1062,7 @@ function buildApi(driver) {
1125
1062
  * @returns {Promise<{inconsistent: boolean, rlAlone: boolean}>}
1126
1063
  */
1127
1064
  async function tableauDlInconsistent(data, options) {
1128
- const opts = checkOptions('tableauDlInconsistent', optionsOf('tableauDlInconsistent', options), ['format', 'baseIRI']);
1065
+ const opts = await checkedOptions('tableauDlInconsistent', optionsOf('tableauDlInconsistent', options));
1129
1066
  const nq = await nquadsOf(data, opts, 'tableauDlInconsistent(data)');
1130
1067
  const e = await entry();
1131
1068
  requireEntryFn(e, 'tableauDlInconsistent', 'OWL tableau DL inconsistency check');
@@ -1141,7 +1078,7 @@ function buildApi(driver) {
1141
1078
  * @returns {Promise<{consistent: boolean|null, reason?: string}>}
1142
1079
  */
1143
1080
  async function owlIsConsistent(data, options) {
1144
- const opts = checkOptions('owlIsConsistent', optionsOf('owlIsConsistent', options), ['format', 'baseIRI', 'fuel']);
1081
+ const opts = await checkedOptions('owlIsConsistent', optionsOf('owlIsConsistent', options));
1145
1082
  const nq = await nquadsOf(data, opts, 'owlIsConsistent(data)');
1146
1083
  const e = await entry();
1147
1084
  requireEntryFn(e, 'owlIsConsistent', 'OWL DL consistency check');
@@ -1161,7 +1098,7 @@ function buildApi(driver) {
1161
1098
  * @returns {Promise<{entailed: boolean|null, via: 'closure'|'refutation', reason?: string}>}
1162
1099
  */
1163
1100
  async function owlEntails(premise, conclusion, options) {
1164
- const opts = checkOptions('owlEntails', optionsOf('owlEntails', options), ['format', 'baseIRI', 'fuel']);
1101
+ const opts = await checkedOptions('owlEntails', optionsOf('owlEntails', options));
1165
1102
  const premiseNq = await nquadsOf(premise, opts, 'owlEntails(premise)');
1166
1103
  const conclusionNq = await nquadsOf(conclusion, opts, 'owlEntails(conclusion)');
1167
1104
  const e = await entry();
@@ -1185,8 +1122,8 @@ function buildApi(driver) {
1185
1122
  async function rmlMap(mapping, sourceData, sourceKind, options) {
1186
1123
  if (typeof sourceData !== 'string') throw new TypeError('rmlMap: sourceData must be a string');
1187
1124
  const kind = String(sourceKind || '').toLowerCase();
1188
- if (kind !== 'json' && kind !== 'csv') throw new TypeError("rmlMap: sourceKind must be 'json' or 'csv'");
1189
- const opts = checkOptions('rmlMap', optionsOf('rmlMap', options), ['format', 'baseIRI']);
1125
+ await checkedOptions('rmlMap', { sourceKind: kind });
1126
+ const opts = await checkedOptions('rmlMap', optionsOf('rmlMap', options));
1190
1127
  return withDataset(mapping, opts, 'rmlMap(mapping)', (c, ds) => {
1191
1128
  requireEntryFn(c.e, 'datasetFromRml', 'RML mapping evaluation');
1192
1129
  return c.wrap(entryResult(c.e.datasetFromRml(ds.handle, sourceData, kind), 'rmlMap'), {});
@@ -1206,7 +1143,7 @@ function buildApi(driver) {
1206
1143
  if (typeof csvText !== 'string') throw new TypeError('csvwToRdf: csvText must be a string');
1207
1144
  const meta = metadataJson == null ? '' : metadataJson;
1208
1145
  if (typeof meta !== 'string') throw new TypeError('csvwToRdf: metadataJson must be a string');
1209
- const opts = checkOptions('csvwToRdf', options, ['mode', 'base', 'url']);
1146
+ const opts = await checkedOptions('csvwToRdf', options);
1210
1147
  const c = await ctx();
1211
1148
  requireEntryFn(c.e, 'datasetFromCsvw', 'CSVW csv2rdf conversion');
1212
1149
  const optionsJson = JSON.stringify({
@@ -1227,7 +1164,7 @@ function buildApi(driver) {
1227
1164
  */
1228
1165
  async function jsonldToRdf(jsonldText, options) {
1229
1166
  if (typeof jsonldText !== 'string') throw new TypeError('jsonldToRdf: jsonldText must be a string');
1230
- const opts = checkOptions('jsonldToRdf', options, ['base', 'rdfDirection', 'expandContext', 'processingMode']);
1167
+ const opts = await checkedOptions('jsonldToRdf', options);
1231
1168
  const c = await ctx();
1232
1169
  requireEntryFn(c.e, 'datasetFromJsonLd', 'jsonldToRdf');
1233
1170
  const optionsJson = JSON.stringify({
@@ -1250,7 +1187,7 @@ function buildApi(driver) {
1250
1187
  */
1251
1188
  async function rifEval(data, rifRulesXml, options) {
1252
1189
  if (typeof rifRulesXml !== 'string') throw new TypeError('rifEval: rifRulesXml must be a string');
1253
- const opts = checkOptions('rifEval', optionsOf('rifEval', options), ['format', 'baseIRI']);
1190
+ const opts = await checkedOptions('rifEval', optionsOf('rifEval', options));
1254
1191
  return withDataset(data, opts, 'rifEval(data)', (c, ds) => {
1255
1192
  requireEntryFn(c.e, 'datasetRifEval', 'RIF Core evaluation');
1256
1193
  return c.wrap(entryResult(c.e.datasetRifEval(rifRulesXml, ds.handle), 'rifEval'), ds.prefixes);
@@ -1266,8 +1203,7 @@ function buildApi(driver) {
1266
1203
  * @returns {Promise<any>}
1267
1204
  */
1268
1205
  async function jsonldFromRdf(data, options) {
1269
- const opts = checkOptions('jsonldFromRdf', optionsOf('jsonldFromRdf', options),
1270
- ['useNativeTypes', 'useRdfType', 'rdfDirection', 'format', 'baseIRI']);
1206
+ const opts = await checkedOptions('jsonldFromRdf', optionsOf('jsonldFromRdf', options));
1271
1207
  const { format, baseIRI, ...w } = opts;
1272
1208
  return withDataset(data, { format, baseIRI }, 'jsonldFromRdf(data)',
1273
1209
  (_c, ds) => JSON.parse(ds.toString({ format: 'jsonld', ...w })));
@@ -1980,28 +1916,16 @@ function buildApi(driver) {
1980
1916
  if (typeof queryString !== 'string') {
1981
1917
  throw new TypeError('queryRaw: queryString must be a string');
1982
1918
  }
1983
- const opts = checkOptions('queryRaw', options, ['dataFormat', 'entail', 'output']);
1919
+ const opts = await checkedOptions('queryRaw', options);
1984
1920
  const dataFormat = String(opts.dataFormat || 'turtle').toLowerCase();
1985
1921
  const entail = opts.entail || 'none';
1986
1922
  const output = opts.output || 'json';
1987
- if (!(dataFormat in RAW_DATA_FORMATS)) {
1988
- throw new TypeError(`queryRaw: unknown dataFormat '${opts.dataFormat}'`);
1989
- }
1990
- if (!ENTAIL_VALUES.has(entail)) {
1991
- throw new TypeError(`queryRaw: entail must be one of ${[...ENTAIL_VALUES].join(', ')}`);
1992
- }
1993
- if (!RAW_OUTPUT_FORMATS.has(output)) {
1994
- throw new TypeError(`queryRaw: output must be one of ${[...RAW_OUTPUT_FORMATS].join(', ')}`);
1995
- }
1923
+ const tables = (await ctx()).tables();
1996
1924
  const e = await entry();
1997
1925
  requireEntryFn(e, 'queryRaw', 'queryRaw');
1998
- const r = JSON.parse(e.queryRaw(dataString, queryString,
1999
- JSON.stringify({ dataFormat: RAW_DATA_FORMATS[dataFormat], entail, output })));
2000
- if (!r.ok) {
2001
- const err = new Error('SPARQL query failed: ' + r.error);
2002
- err.stderr = String(r.error);
2003
- throw err;
2004
- }
1926
+ const r = entryResult(e.queryRaw(dataString, queryString,
1927
+ JSON.stringify({ dataFormat: tables.rawDataFormats[dataFormat], entail, output })),
1928
+ 'queryRaw', queryString);
2005
1929
  return output === 'json' ? JSON.parse(r.text) : r.text;
2006
1930
  }
2007
1931
 
@@ -2280,10 +2204,13 @@ function buildApi(driver) {
2280
2204
  deltaLogDestroy,
2281
2205
  _deltaLogCorruptLastForTest,
2282
2206
  queryRaw,
2207
+ isomorphic,
2208
+ fromQuads,
2209
+ ready,
2210
+ engineTables,
2283
2211
  Dataset,
2284
- DatasetHandle,
2285
2212
  Store,
2286
- ParseError,
2213
+ FactoidalError,
2287
2214
  dataFactory,
2288
2215
  // Metadata (issue #682): the same two fields on every driver's
2289
2216
  // typed surface, so a caller can log/report which package version
@@ -2295,6 +2222,6 @@ function buildApi(driver) {
2295
2222
  }
2296
2223
 
2297
2224
  export {
2298
- buildApi, sniffQueryForm, bindingsFromSrj, entryResult, parseResult,
2299
- utf16OffsetOf, missingOpError, ParseError, DatasetHandle, Store,
2225
+ buildApi, bindingsFromSrj, entryResult, parseResult,
2226
+ missingOpError, FactoidalError, Store,
2300
2227
  };