@factoidal/core 0.10.0 → 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 +114 -1
- package/README.md +7 -1
- package/bin/ext.mjs +16 -20
- package/bin/factoidal.mjs +172 -69
- package/browser.d.ts +4 -0
- package/browser.js +4 -0
- package/fn.d.ts +1 -1
- package/fn.js +34 -97
- package/index.d.ts +22 -1
- package/index.mjs +4 -0
- package/l4-assets/l4factoidal.js +1 -1
- package/l4-assets/l4factoidal.mjs +1 -1
- package/l4-assets/l4factoidal.wasm +0 -0
- package/l4-assets/version.json +6 -6
- package/lib/api.mjs +155 -118
- package/lib/dataset.mjs +128 -34
- package/lib/lean-entry.mjs +23 -14
- package/lib/version.mjs +1 -1
- package/lws/README.md +46 -6
- package/lws/persist.mjs +78 -0
- package/lws/server.mjs +101 -16
- package/package.json +1 -1
- package/rdfjs.mjs +149 -226
- package/solid/client/index.mjs +25 -9
- package/solid/server/index.mjs +93 -15
package/rdfjs.mjs
CHANGED
|
@@ -6,22 +6,21 @@
|
|
|
6
6
|
// terms with spec-compliant `termType` / `value` / `language` /
|
|
7
7
|
// `datatype` and `.equals()`. Self-contained — no runtime deps.
|
|
8
8
|
//
|
|
9
|
-
// Also
|
|
10
|
-
// N-Quads
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
9
|
+
// Also: the field copy between RDF/JS terms and the engine's term objects
|
|
10
|
+
// (Lean L4Factoidal.RDF.TermJson), and N-Quads text in and out through
|
|
11
|
+
// engine ops. This file reads and writes no RDF syntax itself: no escaping,
|
|
12
|
+
// no tokenising (https://github.com/danbri/factoidal/issues/894). The text
|
|
13
|
+
// functions need the engine, which lib/api.mjs installs when it loads.
|
|
14
14
|
//
|
|
15
|
-
// ES module (0.9.0): the same file serves Node (index.mjs, and
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
// legally contain them.
|
|
21
|
-
|
|
22
|
-
const XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string';
|
|
15
|
+
// ES module (0.9.0): the same file serves Node (index.mjs, and rdfjs.js for
|
|
16
|
+
// require()) and the browser (browser.js), with no build step.
|
|
17
|
+
|
|
18
|
+
// RDF/JS Data Model defaults of DataFactory.literal.
|
|
19
|
+
const XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string'; // purity-lint: allow RDF/JS DataFactory default
|
|
23
20
|
const RDF_LANGSTRING =
|
|
24
|
-
'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString';
|
|
21
|
+
'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString'; // purity-lint: allow RDF/JS DataFactory default
|
|
22
|
+
const RDF_DIRLANGSTRING =
|
|
23
|
+
'http://www.w3.org/1999/02/22-rdf-syntax-ns#dirLangString'; // purity-lint: allow RDF/JS DataFactory default
|
|
25
24
|
|
|
26
25
|
// ---------------------------------------------------------------------
|
|
27
26
|
// Terms
|
|
@@ -52,18 +51,23 @@ class BlankNode {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
class Literal {
|
|
55
|
-
constructor(value, language, datatype) {
|
|
54
|
+
constructor(value, language, datatype, direction) {
|
|
56
55
|
this.termType = 'Literal';
|
|
57
56
|
this.value = value;
|
|
58
57
|
this.language = language || '';
|
|
58
|
+
// RDF/JS Data Model DataFactory.literal: no datatype argument means
|
|
59
|
+
// xsd:string, a language means rdf:langString, a language and a
|
|
60
|
+
// direction mean rdf:dirLangString.
|
|
59
61
|
this.datatype = datatype ||
|
|
60
|
-
new NamedNode(language ? RDF_LANGSTRING : XSD_STRING);
|
|
62
|
+
new NamedNode(language ? (direction ? RDF_DIRLANGSTRING : RDF_LANGSTRING) : XSD_STRING); // purity-lint: allow RDF/JS DataFactory default
|
|
63
|
+
this.direction = direction || '';
|
|
61
64
|
Object.freeze(this);
|
|
62
65
|
}
|
|
63
66
|
equals(other) {
|
|
64
67
|
return !!other && other.termType === 'Literal' &&
|
|
65
68
|
other.value === this.value &&
|
|
66
69
|
other.language === this.language &&
|
|
70
|
+
(other.direction || '') === this.direction &&
|
|
67
71
|
!!other.datatype && other.datatype.value === this.datatype.value;
|
|
68
72
|
}
|
|
69
73
|
}
|
|
@@ -139,6 +143,11 @@ const dataFactory = {
|
|
|
139
143
|
if (typeof languageOrDatatype === 'string') {
|
|
140
144
|
return new Literal(v, languageOrDatatype, null);
|
|
141
145
|
}
|
|
146
|
+
// RDF/JS Data Model 1.1: {language, direction} for a directional
|
|
147
|
+
// language-tagged string.
|
|
148
|
+
if (typeof languageOrDatatype.language === 'string' && languageOrDatatype.termType === undefined) {
|
|
149
|
+
return new Literal(v, languageOrDatatype.language, null, languageOrDatatype.direction || '');
|
|
150
|
+
}
|
|
142
151
|
// A NamedNode datatype.
|
|
143
152
|
if (languageOrDatatype.termType === 'NamedNode') {
|
|
144
153
|
if (languageOrDatatype.value === RDF_LANGSTRING) {
|
|
@@ -174,10 +183,8 @@ const dataFactory = {
|
|
|
174
183
|
return new Literal(
|
|
175
184
|
original.value,
|
|
176
185
|
original.language || '',
|
|
177
|
-
original.
|
|
178
|
-
|
|
179
|
-
: new NamedNode(
|
|
180
|
-
(original.datatype && original.datatype.value) || XSD_STRING)
|
|
186
|
+
original.datatype ? new NamedNode(original.datatype.value) : null,
|
|
187
|
+
original.direction || ''
|
|
181
188
|
);
|
|
182
189
|
case 'Quad': return dataFactory.fromQuad(original);
|
|
183
190
|
default:
|
|
@@ -207,31 +214,38 @@ const dataFactory = {
|
|
|
207
214
|
* datatype}) into an RDF/JS term.
|
|
208
215
|
*/
|
|
209
216
|
function termFromSrj(t) {
|
|
217
|
+
return termFromSrjWith(t, dataFactory);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function termFromSrjWith(t, f) {
|
|
210
221
|
if (!t || typeof t.type !== 'string') {
|
|
211
222
|
throw new TypeError('termFromSrj: not a results-JSON term');
|
|
212
223
|
}
|
|
213
224
|
switch (t.type) {
|
|
214
225
|
case 'uri':
|
|
215
|
-
return
|
|
226
|
+
return f.namedNode(t.value);
|
|
216
227
|
case 'bnode':
|
|
217
|
-
return
|
|
228
|
+
return f.blankNode(t.value);
|
|
218
229
|
case 'literal':
|
|
219
230
|
case 'typed-literal': {
|
|
220
231
|
const lang = t['xml:lang'];
|
|
221
|
-
if (lang)
|
|
222
|
-
|
|
223
|
-
|
|
232
|
+
if (lang) {
|
|
233
|
+
if (t['its:dir'] && f === dataFactory) {
|
|
234
|
+
return new Literal(t.value, lang,
|
|
235
|
+
t.datatype ? new NamedNode(t.datatype) : null, t['its:dir']);
|
|
236
|
+
}
|
|
237
|
+
return f.literal(t.value, lang);
|
|
224
238
|
}
|
|
225
|
-
return
|
|
239
|
+
if (t.datatype) return f.literal(t.value, f.namedNode(t.datatype));
|
|
240
|
+
return f.literal(t.value);
|
|
226
241
|
}
|
|
227
242
|
case 'triple': {
|
|
228
|
-
// RDF 1.2 triple term
|
|
243
|
+
// RDF 1.2 triple term:
|
|
229
244
|
// {"type":"triple","value":{"subject":T,"predicate":T,"object":T}}
|
|
230
|
-
//
|
|
231
|
-
// the object may itself be a triple term.
|
|
245
|
+
// -> an RDF/JS Quad term. The object may itself be a triple term.
|
|
232
246
|
const v = t.value || {};
|
|
233
|
-
return
|
|
234
|
-
|
|
247
|
+
return f.quad(termFromSrjWith(v.subject, f), termFromSrjWith(v.predicate, f),
|
|
248
|
+
termFromSrjWith(v.object, f));
|
|
235
249
|
}
|
|
236
250
|
default:
|
|
237
251
|
throw new TypeError(`termFromSrj: unknown term type '${t.type}'`);
|
|
@@ -239,231 +253,132 @@ function termFromSrj(t) {
|
|
|
239
253
|
}
|
|
240
254
|
|
|
241
255
|
// ---------------------------------------------------------------------
|
|
242
|
-
//
|
|
256
|
+
// RDF/JS term <-> engine term object (Lean `L4Factoidal.RDF.TermJson`).
|
|
257
|
+
//
|
|
258
|
+
// These functions copy fields; they read and write no RDF syntax. Every
|
|
259
|
+
// N-Quads parse and write below is an engine op
|
|
260
|
+
// (https://github.com/danbri/factoidal/issues/894 items 1 and 2).
|
|
243
261
|
// ---------------------------------------------------------------------
|
|
244
262
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
case '\r': out += '\\r'; break;
|
|
253
|
-
case '\t': out += '\\t'; break;
|
|
254
|
-
default: out += ch;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return out;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/** Serialize one RDF/JS term to its N-Quads token. */
|
|
261
|
-
function termToNQuads(term) {
|
|
263
|
+
/** The engine term object of an RDF/JS term (`TermJson.encodeTerm`'s shape). */
|
|
264
|
+
function termToJson(term) {
|
|
265
|
+
if (term === null || term === undefined) return null;
|
|
266
|
+
// A term given as text is one RDF 1.2 N-Triples term (`<IRI>`, `_:label`,
|
|
267
|
+
// a literal, `<<( s p o )>>`). The engine reads it
|
|
268
|
+
// (L4Factoidal.RDF.TermJson.readTermString); this file copies the text.
|
|
269
|
+
if (typeof term === 'string') return { type: 'nt', value: term };
|
|
262
270
|
switch (term.termType) {
|
|
263
|
-
case 'NamedNode':
|
|
264
|
-
|
|
265
|
-
case 'BlankNode':
|
|
266
|
-
return '_:' + term.value;
|
|
271
|
+
case 'NamedNode': return { type: 'uri', value: term.value };
|
|
272
|
+
case 'BlankNode': return { type: 'bnode', value: term.value };
|
|
267
273
|
case 'Literal': {
|
|
268
|
-
const
|
|
269
|
-
if (term.
|
|
270
|
-
if (term.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return body;
|
|
274
|
+
const o = { type: 'literal', value: term.value };
|
|
275
|
+
if (term.datatype) o.datatype = term.datatype.value;
|
|
276
|
+
if (term.language) o['xml:lang'] = term.language;
|
|
277
|
+
if (term.direction) o['its:dir'] = term.direction;
|
|
278
|
+
return o;
|
|
274
279
|
}
|
|
275
|
-
case 'DefaultGraph':
|
|
276
|
-
return '';
|
|
277
280
|
case 'Quad':
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
termToNQuads(term.predicate) + ' ' +
|
|
283
|
-
termToNQuads(term.object) + ' )>>';
|
|
281
|
+
return { type: 'triple', value: {
|
|
282
|
+
subject: termToJson(term.subject), predicate: termToJson(term.predicate),
|
|
283
|
+
object: termToJson(term.object) } };
|
|
284
|
+
case 'DefaultGraph': return { type: 'default' };
|
|
284
285
|
default:
|
|
285
|
-
throw new TypeError(
|
|
286
|
-
`termToNQuads: cannot serialize termType '${term.termType}'`);
|
|
286
|
+
throw new TypeError(`termToJson: cannot send termType '${term.termType}' to the engine`);
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
/**
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
290
|
+
/**
|
|
291
|
+
* The engine term object of a term an application supplies, for example an
|
|
292
|
+
* extension function's answer: an RDF/JS term (duck-typed on termType), an
|
|
293
|
+
* engine term object ({type, value, ...}), or a plain object
|
|
294
|
+
* {value, datatype} or {value, language, direction?}. The components are
|
|
295
|
+
* copied as given; the engine checks them (TermJson.decodeCheckedTerm) and
|
|
296
|
+
* keeps the lexical form and the datatype IRI unchanged.
|
|
297
|
+
*/
|
|
298
|
+
function appTermToJson(out) {
|
|
299
|
+
if (typeof out.termType === 'string') return termToJson(out);
|
|
300
|
+
if (typeof out.type === 'string') return out;
|
|
301
|
+
const o = { type: 'literal', value: out.value };
|
|
302
|
+
if (out.datatype !== undefined && out.datatype !== null) {
|
|
303
|
+
o.datatype = typeof out.datatype === 'string' ? out.datatype : out.datatype.value;
|
|
304
|
+
}
|
|
305
|
+
if (out.language !== undefined && out.language !== null && out.language !== '') o['xml:lang'] = out.language;
|
|
306
|
+
if (out.direction !== undefined && out.direction !== null && out.direction !== '') o['its:dir'] = out.direction;
|
|
307
|
+
return o;
|
|
298
308
|
}
|
|
299
309
|
|
|
300
|
-
/**
|
|
301
|
-
function
|
|
302
|
-
|
|
303
|
-
|
|
310
|
+
/** The engine quad array of an RDF/JS quad: [s, p, o] or [s, p, o, g]. */
|
|
311
|
+
function quadToJson(quad) {
|
|
312
|
+
const out = [termToJson(quad.subject), termToJson(quad.predicate), termToJson(quad.object)];
|
|
313
|
+
if (quad.graph && quad.graph.termType !== 'DefaultGraph') out.push(termToJson(quad.graph));
|
|
304
314
|
return out;
|
|
305
315
|
}
|
|
306
316
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
// reader for a machine-generated regular syntax, not a general RDF
|
|
314
|
-
// parser — user documents in any syntax go through the engine.
|
|
315
|
-
// ---------------------------------------------------------------------
|
|
317
|
+
/** The RDF/JS quad of an engine quad array. */
|
|
318
|
+
function quadFromJson(arr, factory) {
|
|
319
|
+
const f = factory || dataFactory;
|
|
320
|
+
return f.quad(termFromSrjWith(arr[0], f), termFromSrjWith(arr[1], f),
|
|
321
|
+
termFromSrjWith(arr[2], f), arr.length > 3 ? termFromSrjWith(arr[3], f) : f.defaultGraph());
|
|
322
|
+
}
|
|
316
323
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
};
|
|
324
|
+
// The engine the text functions below call. lib/api.mjs installs it when it
|
|
325
|
+
// loads the engine; until then they throw.
|
|
326
|
+
let textEngine = null;
|
|
321
327
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
if (e === 'U') {
|
|
336
|
-
out += String.fromCodePoint(parseInt(s.slice(i + 1, i + 9), 16));
|
|
337
|
-
i += 8;
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
throw new SyntaxError(
|
|
341
|
-
`N-Quads line ${lineNo}: bad escape '\\${e}'`);
|
|
342
|
-
}
|
|
343
|
-
return out;
|
|
328
|
+
/** @private lib/api.mjs: install the engine ops the text functions call. */
|
|
329
|
+
function _setTextEngine(engine) { textEngine = engine; }
|
|
330
|
+
|
|
331
|
+
function needEngine(who) {
|
|
332
|
+
if (!textEngine) {
|
|
333
|
+
throw new Error(
|
|
334
|
+
`${who}: N-Quads text is read and written by the engine, which is not ` +
|
|
335
|
+
'loaded yet. Await any @factoidal/core call first (for example ' +
|
|
336
|
+
'`await ready()`), or use parse() and Dataset.toString().');
|
|
337
|
+
}
|
|
338
|
+
return textEngine;
|
|
344
339
|
}
|
|
345
340
|
|
|
346
|
-
|
|
347
|
-
function
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
// (termType 'Quad'). Checked BEFORE the '<' IRI case since a triple
|
|
351
|
-
// term also opens with '<'. This reads the engine's own Mode_12
|
|
352
|
-
// N-Quads serializer output (RDF.NQuads.Serialize) into RDF/JS
|
|
353
|
-
// objects; it is not a conformance parser (that is the F* --rdf12
|
|
354
|
-
// path) — same interop role the rest of this reader plays.
|
|
355
|
-
if (c === '<' && s[i + 1] === '<' && s[i + 2] === '(') {
|
|
356
|
-
let j = skipWs(s, i + 3);
|
|
357
|
-
const [sub, a] = readTerm(s, j, lineNo, factory); j = skipWs(s, a);
|
|
358
|
-
const [pred, b] = readTerm(s, j, lineNo, factory); j = skipWs(s, b);
|
|
359
|
-
const [obj, d] = readTerm(s, j, lineNo, factory); j = skipWs(s, d);
|
|
360
|
-
if (s[j] === ')' && s[j + 1] === '>' && s[j + 2] === '>') {
|
|
361
|
-
return [factory.quad(sub, pred, obj), j + 3];
|
|
362
|
-
}
|
|
363
|
-
throw new SyntaxError(
|
|
364
|
-
`N-Quads line ${lineNo}: unterminated triple term (expected ')>>')`);
|
|
365
|
-
}
|
|
366
|
-
if (c === '<') {
|
|
367
|
-
const end = s.indexOf('>', i + 1);
|
|
368
|
-
if (end < 0) throw new SyntaxError(`N-Quads line ${lineNo}: unclosed IRI`);
|
|
369
|
-
return [factory.namedNode(s.slice(i + 1, end)), end + 1];
|
|
370
|
-
}
|
|
371
|
-
if (c === '_' && s[i + 1] === ':') {
|
|
372
|
-
let j = i + 2;
|
|
373
|
-
while (j < s.length && !/\s/.test(s[j])) j++;
|
|
374
|
-
return [factory.blankNode(s.slice(i + 2, j)), j];
|
|
375
|
-
}
|
|
376
|
-
if (c === '"') {
|
|
377
|
-
// Scan to the closing unescaped quote.
|
|
378
|
-
let j = i + 1;
|
|
379
|
-
for (;;) {
|
|
380
|
-
if (j >= s.length) {
|
|
381
|
-
throw new SyntaxError(`N-Quads line ${lineNo}: unclosed literal`);
|
|
382
|
-
}
|
|
383
|
-
if (s[j] === '\\') { j += 2; continue; }
|
|
384
|
-
if (s[j] === '"') break;
|
|
385
|
-
j++;
|
|
386
|
-
}
|
|
387
|
-
const lex = unescapeLiteral(s.slice(i + 1, j), lineNo);
|
|
388
|
-
j++; // past closing quote
|
|
389
|
-
if (s[j] === '@') {
|
|
390
|
-
let k = j + 1;
|
|
391
|
-
while (k < s.length && !/\s/.test(s[k])) k++;
|
|
392
|
-
return [factory.literal(lex, s.slice(j + 1, k)), k];
|
|
393
|
-
}
|
|
394
|
-
if (s[j] === '^' && s[j + 1] === '^' && s[j + 2] === '<') {
|
|
395
|
-
const end = s.indexOf('>', j + 3);
|
|
396
|
-
if (end < 0) {
|
|
397
|
-
throw new SyntaxError(`N-Quads line ${lineNo}: unclosed datatype IRI`);
|
|
398
|
-
}
|
|
399
|
-
return [
|
|
400
|
-
factory.literal(lex, factory.namedNode(s.slice(j + 3, end))),
|
|
401
|
-
end + 1,
|
|
402
|
-
];
|
|
403
|
-
}
|
|
404
|
-
return [factory.literal(lex), j];
|
|
405
|
-
}
|
|
406
|
-
throw new SyntaxError(
|
|
407
|
-
`N-Quads line ${lineNo}: unexpected character '${c}' at column ${i + 1}`);
|
|
341
|
+
/** One RDF/JS term as its canonical N-Quads token (the engine writes it). */
|
|
342
|
+
function termToNQuads(term) {
|
|
343
|
+
if (term && term.termType === 'DefaultGraph') return '';
|
|
344
|
+
return needEngine('termToNQuads').termTokens([termToJson(term)])[0];
|
|
408
345
|
}
|
|
409
346
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
return
|
|
347
|
+
/** One RDF/JS quad as its canonical N-Quads line (with trailing newline). */
|
|
348
|
+
function quadToNQuads(quad) {
|
|
349
|
+
return needEngine('quadToNQuads').writeQuads([quadToJson(quad)]);
|
|
413
350
|
}
|
|
414
351
|
|
|
415
|
-
/**
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
const f = factory || dataFactory;
|
|
421
|
-
let i = skipWs(line, 0);
|
|
422
|
-
if (i >= line.length || line[i] === '#') return null;
|
|
423
|
-
const terms = [];
|
|
424
|
-
while (terms.length < 4) {
|
|
425
|
-
const [term, next] = readTerm(line, i, lineNo, f);
|
|
426
|
-
terms.push(term);
|
|
427
|
-
i = skipWs(line, next);
|
|
428
|
-
if (line[i] === '.') break;
|
|
429
|
-
}
|
|
430
|
-
if (line[i] !== '.') {
|
|
431
|
-
throw new SyntaxError(`N-Quads line ${lineNo}: missing terminating '.'`);
|
|
432
|
-
}
|
|
433
|
-
if (terms.length < 3) {
|
|
434
|
-
throw new SyntaxError(`N-Quads line ${lineNo}: fewer than 3 terms`);
|
|
435
|
-
}
|
|
436
|
-
return f.quad(terms[0], terms[1], terms[2], terms[3] || f.defaultGraph());
|
|
352
|
+
/** An iterable of RDF/JS quads as an N-Quads document, one line per quad. */
|
|
353
|
+
function quadsToNQuads(quads) {
|
|
354
|
+
const arr = [];
|
|
355
|
+
for (const q of quads) arr.push(quadToJson(q));
|
|
356
|
+
return needEngine('quadsToNQuads').writeQuads(arr);
|
|
437
357
|
}
|
|
438
358
|
|
|
439
359
|
/**
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
360
|
+
* Parse an N-Quads document (RDF 1.2 grammar) into RDF/JS quads; the engine
|
|
361
|
+
* parses it and rejects any line the grammar rejects.
|
|
362
|
+
* The quads come in the engine's order: the default graph, then each named
|
|
363
|
+
* graph in the order of its first quad.
|
|
364
|
+
* options.blankNodePrefix: prepended to every blank-node label (keeps the
|
|
365
|
+
* labels of separate documents apart); options.factory: an RDF/JS
|
|
366
|
+
* DataFactory.
|
|
447
367
|
*/
|
|
448
368
|
function nquadsToQuads(text, options) {
|
|
449
369
|
const opts = options || {};
|
|
450
370
|
const factory = opts.factory || dataFactory;
|
|
451
371
|
const f = opts.blankNodePrefix
|
|
452
|
-
? {
|
|
453
|
-
...factory,
|
|
454
|
-
blankNode: (label) => factory.blankNode(
|
|
455
|
-
label === undefined || label === null
|
|
456
|
-
? undefined
|
|
457
|
-
: opts.blankNodePrefix + label),
|
|
458
|
-
}
|
|
372
|
+
? { ...factory, blankNode: (label) => factory.blankNode(opts.blankNodePrefix + label) }
|
|
459
373
|
: factory;
|
|
460
|
-
const quads =
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
374
|
+
const quads = needEngine('nquadsToQuads').parseQuads(String(text));
|
|
375
|
+
return quads.map((q) => quadFromJson(q, f));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** One N-Quads statement as an RDF/JS quad; null for a blank or comment line. */
|
|
379
|
+
function nquadsLineToQuad(line, _lineNo, factory) {
|
|
380
|
+
const qs = nquadsToQuads(line, { factory });
|
|
381
|
+
return qs.length ? qs[0] : null;
|
|
467
382
|
}
|
|
468
383
|
|
|
469
384
|
// ---------------------------------------------------------------------
|
|
@@ -481,7 +396,8 @@ function termKey(t) {
|
|
|
481
396
|
if (!t) return 'null';
|
|
482
397
|
switch (t.termType) {
|
|
483
398
|
case 'Literal':
|
|
484
|
-
return JSON.stringify(['Literal', t.value, t.language, t.datatype ? t.datatype.value : null
|
|
399
|
+
return JSON.stringify(['Literal', t.value, t.language, t.datatype ? t.datatype.value : null,
|
|
400
|
+
t.direction || '']);
|
|
485
401
|
case 'DefaultGraph':
|
|
486
402
|
return '["DefaultGraph"]';
|
|
487
403
|
case 'Quad':
|
|
@@ -562,7 +478,7 @@ class DatasetCore {
|
|
|
562
478
|
toArray() {
|
|
563
479
|
return this._quads.slice();
|
|
564
480
|
}
|
|
565
|
-
/** N-Quads text
|
|
481
|
+
/** N-Quads text, written by the engine. */
|
|
566
482
|
toNQuads() {
|
|
567
483
|
return quadsToNQuads(this._quads);
|
|
568
484
|
}
|
|
@@ -584,6 +500,13 @@ export {
|
|
|
584
500
|
Quad,
|
|
585
501
|
DatasetCore,
|
|
586
502
|
termFromSrj,
|
|
503
|
+
termToJson,
|
|
504
|
+
appTermToJson,
|
|
505
|
+
quadToJson,
|
|
506
|
+
quadFromJson,
|
|
507
|
+
quadKey,
|
|
508
|
+
termKey,
|
|
509
|
+
_setTextEngine,
|
|
587
510
|
nquadsLineToQuad,
|
|
588
511
|
termToNQuads,
|
|
589
512
|
quadToNQuads,
|
package/solid/client/index.mjs
CHANGED
|
@@ -158,17 +158,29 @@ export async function createSolidClient (options = {}) {
|
|
|
158
158
|
const maxSteps = typeof options.maxSteps === 'number' ? options.maxSteps : 16
|
|
159
159
|
const baseIri = typeof options.baseIri === 'string' ? options.baseIri : null
|
|
160
160
|
|
|
161
|
-
/** Where to send a request whose target the engine wrote
|
|
162
|
-
*
|
|
163
|
-
*
|
|
161
|
+
/** Where to send a request whose target the engine wrote. The engine
|
|
162
|
+
* resolves the target against baseIri (RFC 3986 section 5.2, op
|
|
163
|
+
* resolveIri) and says whether it was already absolute
|
|
164
|
+
* (https://github.com/danbri/factoidal/issues/894 item 5). */
|
|
164
165
|
function endpoint (target) {
|
|
165
|
-
|
|
166
|
+
let r
|
|
167
|
+
try {
|
|
168
|
+
r = engine.call('resolveIri', [baseIri === null ? '' : baseIri, String(target)])
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (isUnknownOp(error)) {
|
|
171
|
+
throw new SolidClientHostError(
|
|
172
|
+
'the loaded l4factoidal WebAssembly module does not carry the ' +
|
|
173
|
+
'resolveIri operation; rebuild it with formal/lean4/Wasm/build-wasm.sh')
|
|
174
|
+
}
|
|
175
|
+
throw error
|
|
176
|
+
}
|
|
177
|
+
if (r.referenceAbsolute) return String(target)
|
|
166
178
|
if (baseIri === null) {
|
|
167
179
|
throw new SolidClientHostError(
|
|
168
|
-
`the engine answered the
|
|
169
|
-
'baseIri to
|
|
180
|
+
`the engine answered the relative reference "${target}" and this client has no ` +
|
|
181
|
+
'baseIri to resolve it against; pass baseIri to createSolidClient')
|
|
170
182
|
}
|
|
171
|
-
return
|
|
183
|
+
return r.iri
|
|
172
184
|
}
|
|
173
185
|
|
|
174
186
|
function callRequest (kind, args) {
|
|
@@ -244,9 +256,13 @@ export async function createSolidClient (options = {}) {
|
|
|
244
256
|
if (interpretKind === null) return answer
|
|
245
257
|
// The record says what came back; `target` says what was asked. The
|
|
246
258
|
// storage walk needs both, and this is the engine's own target
|
|
247
|
-
// echoed back, not a URL this file composed.
|
|
259
|
+
// echoed back, not a URL this file composed. `requestUri` is the URL
|
|
260
|
+
// the request went to (the `endpoint` call above): the base IRI of
|
|
261
|
+
// the representation (RFC 3986 §5.1.3), which the engine resolves
|
|
262
|
+
// relative references against.
|
|
248
263
|
const interpretation = interpretationOf(
|
|
249
|
-
callResponse(interpretKind, { ...record, target: request.target
|
|
264
|
+
callResponse(interpretKind, { ...record, target: request.target,
|
|
265
|
+
requestUri: endpoint(request.target) }))
|
|
250
266
|
answer.interpretation = interpretation
|
|
251
267
|
if (interpretation.continue !== true) return answer
|
|
252
268
|
state = interpretation.state === undefined ? null : interpretation.state
|