eyeling 1.27.9 → 1.28.1

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/cli.js CHANGED
@@ -8,10 +8,13 @@
8
8
  'use strict';
9
9
 
10
10
  const fs = require('node:fs');
11
- const os = require('node:os');
12
11
  const path = require('node:path');
13
- const { pathToFileURL, fileURLToPath } = require('node:url');
12
+ const { pathToFileURL, fileURLToPath, URL } = require('node:url');
14
13
  const { TextDecoder } = require('node:util');
14
+ const http = require('node:http');
15
+ const https = require('node:https');
16
+ const readline = require('node:readline');
17
+ const zlib = require('node:zlib');
15
18
 
16
19
  const engine = require('./engine');
17
20
  const deref = require('./deref');
@@ -145,7 +148,8 @@ function __httpFetchScriptBody({ prefixOnly = false } = {}) {
145
148
  if (finished) return;
146
149
  chunks.push(chunk);
147
150
  bytes += chunk.length;
148
- if (bytes >= limit) finish();
151
+ const text = Buffer.concat(chunks, bytes).toString('utf8');
152
+ if (bytes >= limit || /^\\s*(?:@version|VERSION)\\s+(["'])(?:1\\.1|1\\.2|1\\.2-basic)-messages\\1\\s*\\.?\\s*(?:#.*)?$/im.test(text)) finish();
149
153
  });
150
154
  body.on('end', finish);
151
155
  body.on('error', (e) => { console.error(e && e.message ? e.message : String(e)); process.exit(5); });
@@ -174,22 +178,95 @@ function __readHttpPrefixSync(sourceLabel, byteLimit = 64 * 1024) {
174
178
  return r.stdout;
175
179
  }
176
180
 
177
- function __downloadHttpSourceToTempFileSync(sourceLabel) {
178
- const cp = require('node:child_process');
179
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-rdf-message-log-'));
180
- const file = path.join(dir, 'source.txt');
181
- const r = cp.spawnSync(process.execPath, ['-e', __httpFetchScriptBody({ prefixOnly: false }), sourceLabel, file], {
182
- encoding: 'utf8',
183
- maxBuffer: 1024 * 1024,
184
- stdio: ['ignore', 'ignore', 'pipe'],
181
+ function __openHttpTextStream(sourceLabel, redirects = 0) {
182
+ const maxRedirects = 10;
183
+ return new Promise((resolve, reject) => {
184
+ if (redirects > maxRedirects) {
185
+ reject(new Error('Too many redirects'));
186
+ return;
187
+ }
188
+
189
+ let parsed;
190
+ try {
191
+ parsed = new URL(sourceLabel);
192
+ } catch (e) {
193
+ reject(e);
194
+ return;
195
+ }
196
+
197
+ const mod = parsed.protocol === 'https:' ? https : parsed.protocol === 'http:' ? http : null;
198
+ if (!mod) {
199
+ reject(new Error(`Unsupported protocol ${parsed.protocol}`));
200
+ return;
201
+ }
202
+
203
+ const req = mod.request(
204
+ {
205
+ protocol: parsed.protocol,
206
+ hostname: parsed.hostname,
207
+ port: parsed.port || undefined,
208
+ path: parsed.pathname + parsed.search,
209
+ headers: {
210
+ accept: 'text/n3, text/turtle, application/trig, application/n-triples, application/n-quads, text/plain;q=0.8, */*;q=0.01',
211
+ 'accept-encoding': 'identity',
212
+ 'user-agent': 'eyeling-rdf-message-stream',
213
+ },
214
+ },
215
+ (res) => {
216
+ const sc = res.statusCode || 0;
217
+ if (sc >= 300 && sc < 400 && res.headers && res.headers.location) {
218
+ const next = new URL(res.headers.location, sourceLabel).toString();
219
+ res.resume();
220
+ resolve(__openHttpTextStream(next, redirects + 1));
221
+ return;
222
+ }
223
+ if (sc < 200 || sc >= 300) {
224
+ res.resume();
225
+ reject(new Error(`HTTP status ${sc}`));
226
+ return;
227
+ }
228
+
229
+ const enc = String((res.headers && res.headers['content-encoding']) || '').toLowerCase();
230
+ let body = res;
231
+ if (enc.includes('gzip')) body = res.pipe(zlib.createGunzip());
232
+ else if (enc.includes('deflate')) body = res.pipe(zlib.createInflate());
233
+ else if (enc.includes('br')) body = res.pipe(zlib.createBrotliDecompress());
234
+ resolve(body);
235
+ },
236
+ );
237
+ req.on('error', reject);
238
+ req.end();
185
239
  });
186
- if (r.status !== 0) {
187
- try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
188
- throw new Error(`Failed to dereference ${sourceLabel}${r.stderr ? ': ' + String(r.stderr).trim() : ''}`);
189
- }
190
- return { file, cleanup: () => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch {} } };
191
240
  }
192
241
 
242
+ async function forEachLineInHttpSource(sourceLabel, onLine) {
243
+ const body = await __openHttpTextStream(sourceLabel);
244
+ await new Promise((resolve, reject) => {
245
+ let settled = false;
246
+ function done(err) {
247
+ if (settled) return;
248
+ settled = true;
249
+ if (err) reject(err);
250
+ else resolve();
251
+ }
252
+
253
+ const rl = readline.createInterface({ input: body, crlfDelay: Infinity });
254
+ rl.on('line', (line) => {
255
+ try {
256
+ onLine(line + '\n');
257
+ } catch (e) {
258
+ try { rl.close(); } catch {}
259
+ if (body && typeof body.destroy === 'function') {
260
+ try { body.destroy(); } catch {}
261
+ }
262
+ done(e);
263
+ }
264
+ });
265
+ rl.on('close', () => done());
266
+ rl.on('error', done);
267
+ body.on('error', done);
268
+ });
269
+ }
193
270
 
194
271
  const RDF_MESSAGE_VERSION_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im;
195
272
  const RDF_MESSAGE_VERSION_LINE_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/i;
@@ -407,15 +484,50 @@ function __forEachRdfMessageChunkSync(sourceLabel, onMessage) {
407
484
  return;
408
485
  }
409
486
  if (__isHttpSource(sourceLabel)) {
410
- const tmp = __downloadHttpSourceToTempFileSync(sourceLabel);
411
- try {
412
- forEachRdfMessageChunkInFileSync(tmp.file, onMessage);
413
- } finally {
414
- tmp.cleanup();
487
+ throw new Error('internal error: HTTP RDF Message Logs must be streamed asynchronously');
488
+ }
489
+ forEachRdfMessageChunkInText(__readInputSourceSync(sourceLabel), onMessage);
490
+ }
491
+
492
+
493
+ async function forEachRdfMessageChunkInHttpSource(sourceLabel, onMessage) {
494
+ const directives = [];
495
+ const seenDirectives = new Set();
496
+ let chunk = '';
497
+ let messageIndex = 1;
498
+ let sawVersion = false;
499
+ let sawDelimiter = false;
500
+
501
+ function emit() {
502
+ onMessage({ messageIndex, chunk, directives: directives.slice() });
503
+ messageIndex += 1;
504
+ chunk = '';
505
+ }
506
+
507
+ await forEachLineInHttpSource(sourceLabel, (line) => {
508
+ if (RDF_MESSAGE_VERSION_LINE_RE.test(line)) {
509
+ sawVersion = true;
510
+ return;
511
+ }
512
+ if (RDF_MESSAGE_DELIMITER_LINE_RE.test(line)) {
513
+ emit();
514
+ sawDelimiter = true;
515
+ return;
415
516
  }
517
+ addRdfDirective(directives, seenDirectives, line);
518
+ chunk += line;
519
+ });
520
+
521
+ if (!sawVersion) throw new Error('not an RDF Message Log: missing VERSION "*-messages" directive');
522
+ if (sawDelimiter || hasRdfPayload(chunk)) emit();
523
+ }
524
+
525
+ async function __forEachRdfMessageChunk(sourceLabel, onMessage) {
526
+ if (__isHttpSource(sourceLabel)) {
527
+ await forEachRdfMessageChunkInHttpSource(sourceLabel, onMessage);
416
528
  return;
417
529
  }
418
- forEachRdfMessageChunkInText(__readInputSourceSync(sourceLabel), onMessage);
530
+ __forEachRdfMessageChunkSync(sourceLabel, onMessage);
419
531
  }
420
532
 
421
533
  function factsContainOutputStrings(triplesForOutput) {
@@ -481,7 +593,7 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
481
593
  }
482
594
  }
483
595
 
484
- function runStreamMessagesMode(sourceLabels, { rdfMode }) {
596
+ async function runStreamMessagesMode(sourceLabels, { rdfMode }) {
485
597
  const ordinarySourceLabels = [];
486
598
  const messageSourceLabels = [];
487
599
 
@@ -533,7 +645,7 @@ function runStreamMessagesMode(sourceLabels, { rdfMode }) {
533
645
  const fullIriPrefixes = new PrefixEnv({});
534
646
  for (const messageSourceLabel of messageSourceLabels) {
535
647
  try {
536
- __forEachRdfMessageChunkSync(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
648
+ await __forEachRdfMessageChunk(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
537
649
  const messageText = buildSingleMessageReplayDocument({
538
650
  sourceLabel: messageSourceLabel,
539
651
  messageIndex,
@@ -567,7 +679,7 @@ function runStreamMessagesMode(sourceLabels, { rdfMode }) {
567
679
  }
568
680
  }
569
681
 
570
- function main() {
682
+ async function main() {
571
683
  // Drop "node" and script name; keep only user-provided args
572
684
  // Expand combined short options: -pt == -p -t
573
685
  const argvRaw = process.argv.slice(2);
@@ -706,7 +818,7 @@ function main() {
706
818
  }
707
819
 
708
820
  if (streamMessagesMode) {
709
- runStreamMessagesMode(sourceLabels, { rdfMode });
821
+ await runStreamMessagesMode(sourceLabels, { rdfMode });
710
822
  return;
711
823
  }
712
824
 
@@ -790,7 +902,8 @@ function main() {
790
902
  return false;
791
903
  }
792
904
 
793
- function factsContainOutputStrings(triplesForOutput) {
905
+
906
+ function factsContainOutputStrings(triplesForOutput) {
794
907
  return (
795
908
  Array.isArray(triplesForOutput) &&
796
909
  triplesForOutput.some(
package/lib/engine.js CHANGED
@@ -95,7 +95,7 @@ const {
95
95
  } = require('./printing');
96
96
  const {
97
97
  getDataFactory,
98
- internalTripleToRdfJsQuad,
98
+ internalTripleToRdfJsQuads,
99
99
  normalizeParsedReasonerInputSync,
100
100
  normalizeReasonerInputSync,
101
101
  normalizeReasonerInputAsync,
@@ -3593,15 +3593,22 @@ function isUnsupportedRdfJsConversionError(err) {
3593
3593
  );
3594
3594
  }
3595
3595
 
3596
- function maybeTripleToRdfJsQuad(triple, rdfFactory, skipUnsupportedRdfJs) {
3596
+ function maybeTripleToRdfJsQuads(triple, rdfFactory, skipUnsupportedRdfJs) {
3597
3597
  try {
3598
- return internalTripleToRdfJsQuad(triple, rdfFactory);
3598
+ return internalTripleToRdfJsQuads(triple, rdfFactory);
3599
3599
  } catch (err) {
3600
- if (skipUnsupportedRdfJs && isUnsupportedRdfJsConversionError(err)) return null;
3600
+ if (skipUnsupportedRdfJs && isUnsupportedRdfJsConversionError(err)) return [];
3601
3601
  throw err;
3602
3602
  }
3603
3603
  }
3604
3604
 
3605
+ function addRdfJsPayloadQuads(payload, quads) {
3606
+ if (!Array.isArray(quads) || quads.length === 0) return payload;
3607
+ payload.quads = quads;
3608
+ if (quads.length === 1) payload.quad = quads[0];
3609
+ return payload;
3610
+ }
3611
+
3605
3612
  function reasonStream(input, opts = {}) {
3606
3613
  const {
3607
3614
  baseIri = null,
@@ -3629,7 +3636,8 @@ function reasonStream(input, opts = {}) {
3629
3636
  rdf: useRdfCompatibility,
3630
3637
  })
3631
3638
  : null;
3632
- const parsedInput = parsedSourceList || parsedTextInput || normalizeParsedReasonerInputSync(input);
3639
+ const hasInlineN3 = input && typeof input === 'object' && !Array.isArray(input) && typeof input.n3 === 'string';
3640
+ const parsedInput = parsedSourceList || parsedTextInput || (hasInlineN3 ? null : normalizeParsedReasonerInputSync(input));
3633
3641
  const rdfFactory = rdfjs ? getDataFactory(dataFactory) : null;
3634
3642
 
3635
3643
  const __oldEnforceHttps = deref.getEnforceHttpsEnabled();
@@ -3700,8 +3708,7 @@ function reasonStream(input, opts = {}) {
3700
3708
  for (const qdf of queryDerived) {
3701
3709
  const payload = { triple: useRdfCompatibility ? tripleToRdfCompatible(qdf.fact, prefixes) : tripleToN3(qdf.fact, prefixes), df: qdf };
3702
3710
  if (rdfFactory) {
3703
- const quad = maybeTripleToRdfJsQuad(qdf.fact, rdfFactory, skipUnsupportedRdfJs);
3704
- if (quad) payload.quad = quad;
3711
+ addRdfJsPayloadQuads(payload, maybeTripleToRdfJsQuads(qdf.fact, rdfFactory, skipUnsupportedRdfJs));
3705
3712
  }
3706
3713
  onDerived(payload);
3707
3714
  }
@@ -3719,8 +3726,7 @@ function reasonStream(input, opts = {}) {
3719
3726
  df,
3720
3727
  };
3721
3728
  if (rdfFactory) {
3722
- const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
3723
- if (quad) payload.quad = quad;
3729
+ addRdfJsPayloadQuads(payload, maybeTripleToRdfJsQuads(df.fact, rdfFactory, skipUnsupportedRdfJs));
3724
3730
  }
3725
3731
  onDerived(payload);
3726
3732
  }
@@ -3761,12 +3767,10 @@ function reasonStream(input, opts = {}) {
3761
3767
  };
3762
3768
 
3763
3769
  if (rdfFactory) {
3764
- __out.closureQuads = closureTriples
3765
- .map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
3766
- .filter(Boolean);
3767
- __out.queryQuads = queryTriples
3768
- .map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
3769
- .filter(Boolean);
3770
+ __out.closureQuads = closureTriples.flatMap((t) =>
3771
+ maybeTripleToRdfJsQuads(t, rdfFactory, skipUnsupportedRdfJs),
3772
+ );
3773
+ __out.queryQuads = queryTriples.flatMap((t) => maybeTripleToRdfJsQuads(t, rdfFactory, skipUnsupportedRdfJs));
3770
3774
  }
3771
3775
  deref.setEnforceHttpsEnabled(__oldEnforceHttps);
3772
3776
  return __out;
@@ -3797,9 +3801,9 @@ function reasonRdfJs(input, opts = {}) {
3797
3801
  ...restOpts,
3798
3802
  rdfjs: false,
3799
3803
  onDerived: ({ df }) => {
3800
- const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
3801
- if (quad) {
3802
- queue.push(quad);
3804
+ const quads = maybeTripleToRdfJsQuads(df.fact, rdfFactory, skipUnsupportedRdfJs);
3805
+ if (quads.length) {
3806
+ queue.push(...quads);
3803
3807
  flush();
3804
3808
  }
3805
3809
  },
@@ -3828,7 +3832,15 @@ function reasonRdfJs(input, opts = {}) {
3828
3832
  // Minimal export surface for Node + browser/worker
3829
3833
  function main() {
3830
3834
  // Lazily require to avoid hard cycles in the module graph.
3831
- return require('./cli').main();
3835
+ const result = require('./cli').main();
3836
+ if (result && typeof result.then === 'function') {
3837
+ result.catch((e) => {
3838
+ const msg = e && e.stack ? e.stack : e && e.message ? e.message : String(e);
3839
+ console.error(msg);
3840
+ process.exit(1);
3841
+ });
3842
+ }
3843
+ return result;
3832
3844
  }
3833
3845
 
3834
3846
  // ---------------------------------------------------------------------------
package/lib/rdfjs.js CHANGED
@@ -9,6 +9,7 @@
9
9
 
10
10
  const {
11
11
  XSD_NS,
12
+ LOG_NS,
12
13
  Literal: InternalLiteral,
13
14
  Iri,
14
15
  Blank,
@@ -19,6 +20,7 @@ const {
19
20
  Triple,
20
21
  Rule,
21
22
  PrefixEnv,
23
+ annotateQuotedGraphTerm,
22
24
  literalParts,
23
25
  } = require('./prelude');
24
26
  const { termToN3, tripleToN3 } = require('./printing');
@@ -322,6 +324,49 @@ function internalRdf12TermToRdfJs(term, factory, position) {
322
324
  return internalTermToRdfJs(term, rdfFactory, position);
323
325
  }
324
326
 
327
+ function isDefaultGraphTerm(term) {
328
+ return isRdfJsTerm(term) && term.termType === 'DefaultGraph';
329
+ }
330
+
331
+ function assertDefaultGraphForRdfJsQuadTerm(term, position) {
332
+ if (!isDefaultGraphTerm(term.graph)) {
333
+ throw new TypeError(`RDF/JS Quad terms with named graphs are not supported in ${position}`);
334
+ }
335
+ }
336
+
337
+ function rdfJsGraphNameToInternal(term, position = 'quad.graph') {
338
+ assertSupportedRdfJsTerm(term, position);
339
+ switch (term.termType) {
340
+ case 'NamedNode':
341
+ return new Iri(term.value);
342
+ case 'BlankNode':
343
+ return new Blank(`_:${term.value}`);
344
+ case 'DefaultGraph':
345
+ return null;
346
+ default:
347
+ throw new TypeError(`Invalid RDF graph termType ${term.termType}`);
348
+ }
349
+ }
350
+
351
+ function internalGraphNameToRdfJs(term, factory, position = 'graph') {
352
+ if (term instanceof Iri || term instanceof Blank) return internalTermToRdfJs(term, factory, position);
353
+ return unsupportedRdfJsTerm(term, position);
354
+ }
355
+
356
+ function graphKey(term) {
357
+ return `${term.termType}:${term.value}`;
358
+ }
359
+
360
+ function isInternalLogNameOfTriple(triple) {
361
+ return (
362
+ triple instanceof Triple &&
363
+ (triple.s instanceof Iri || triple.s instanceof Blank) &&
364
+ triple.p instanceof Iri &&
365
+ triple.p.value === LOG_NS + 'nameOf' &&
366
+ triple.o instanceof GraphTerm
367
+ );
368
+ }
369
+
325
370
  function assertRdfJsQuadShape(subject, predicate, object, graph) {
326
371
  if (!['NamedNode', 'BlankNode', 'Quad'].includes(subject.termType)) {
327
372
  throw new TypeError(`Invalid RDF subject termType ${subject.termType}`);
@@ -350,13 +395,22 @@ function internalTripleToRdfJsQuadInGraph(triple, graph, factory) {
350
395
  function internalTripleToRdfJsQuad(triple, factory) {
351
396
  const rdfFactory = getDataFactory(factory);
352
397
  return rdfFactory.quad(
353
- internalTermToRdfJs(triple.s, rdfFactory, 'subject'),
398
+ internalRdf12TermToRdfJs(triple.s, rdfFactory, 'subject'),
354
399
  internalTermToRdfJs(triple.p, rdfFactory, 'predicate'),
355
- internalTermToRdfJs(triple.o, rdfFactory, 'object'),
400
+ internalRdf12TermToRdfJs(triple.o, rdfFactory, 'object'),
356
401
  rdfFactory.defaultGraph(),
357
402
  );
358
403
  }
359
404
 
405
+ function internalTripleToRdfJsQuads(triple, factory) {
406
+ const rdfFactory = getDataFactory(factory);
407
+ if (isInternalLogNameOfTriple(triple)) {
408
+ const graph = internalGraphNameToRdfJs(triple.s, rdfFactory, 'graph');
409
+ return (triple.o.triples || []).map((inner) => internalTripleToRdfJsQuadInGraph(inner, graph, rdfFactory));
410
+ }
411
+ return [internalTripleToRdfJsQuad(triple, rdfFactory)];
412
+ }
413
+
360
414
  function escapeStringForN3(value) {
361
415
  return JSON.stringify(String(value));
362
416
  }
@@ -388,7 +442,14 @@ function rdfJsTermToN3(term, position = 'term') {
388
442
  return `${lexical}^^<${datatype}>`;
389
443
  }
390
444
  case 'Quad':
391
- throw new TypeError(`Quoted triple terms are not supported in ${position}`);
445
+ if (String(position).endsWith('.predicate') || position === 'quad.predicate' || position === 'predicate') {
446
+ throw new TypeError(`Quoted triple terms are not valid RDF predicates in ${position}`);
447
+ }
448
+ assertDefaultGraphForRdfJsQuadTerm(term, position);
449
+ return `{ ${rdfJsTermToN3(term.subject, `${position}.subject`)} ${rdfJsTermToN3(
450
+ term.predicate,
451
+ `${position}.predicate`,
452
+ )} ${rdfJsTermToN3(term.object, `${position}.object`)} . }`;
392
453
  default:
393
454
  throw new TypeError(`Unsupported RDF/JS termType ${JSON.stringify(term.termType)} in ${position}`);
394
455
  }
@@ -408,8 +469,21 @@ function rdfJsTermToInternal(term, position = 'term') {
408
469
  return new InternalLiteral(rdfJsTermToN3(term, position));
409
470
  case 'DefaultGraph':
410
471
  throw new TypeError(`DefaultGraph is not a valid standalone N3 term in ${position}`);
411
- case 'Quad':
412
- throw new TypeError(`Quoted triple terms are not supported in ${position}`);
472
+ case 'Quad': {
473
+ if (String(position).endsWith('.predicate') || position === 'quad.predicate' || position === 'predicate') {
474
+ throw new TypeError(`Quoted triple terms are not valid RDF predicates in ${position}`);
475
+ }
476
+ assertDefaultGraphForRdfJsQuadTerm(term, position);
477
+ return annotateQuotedGraphTerm(
478
+ new GraphTerm([
479
+ new Triple(
480
+ rdfJsTermToInternal(term.subject, `${position}.subject`),
481
+ rdfJsTermToInternal(term.predicate, `${position}.predicate`),
482
+ rdfJsTermToInternal(term.object, `${position}.object`),
483
+ ),
484
+ ]),
485
+ );
486
+ }
413
487
  default:
414
488
  throw new TypeError(`Unsupported RDF/JS termType ${JSON.stringify(term.termType)} in ${position}`);
415
489
  }
@@ -417,22 +491,58 @@ function rdfJsTermToInternal(term, position = 'term') {
417
491
 
418
492
  function rdfJsQuadToInternalTriple(quad) {
419
493
  if (!isRdfJsQuad(quad)) throw new TypeError('Expected an RDF/JS Quad');
420
- if (quad.graph.termType !== 'DefaultGraph') {
421
- throw new TypeError('Named graph quads are not supported by Eyeling input; use the default graph only');
422
- }
423
- return new Triple(
494
+ const inner = new Triple(
424
495
  rdfJsTermToInternal(quad.subject, 'quad.subject'),
425
496
  rdfJsTermToInternal(quad.predicate, 'quad.predicate'),
426
497
  rdfJsTermToInternal(quad.object, 'quad.object'),
427
498
  );
499
+ const graph = rdfJsGraphNameToInternal(quad.graph, 'quad.graph');
500
+ if (!graph) return inner;
501
+ return new Triple(graph, new Iri(LOG_NS + 'nameOf'), annotateQuotedGraphTerm(new GraphTerm([inner])));
502
+ }
503
+
504
+ function rdfJsQuadsToInternalTriples(quads) {
505
+ const out = [];
506
+ const namedGraphs = new Map();
507
+
508
+ for (const quad of quads) {
509
+ if (!isRdfJsQuad(quad)) throw new TypeError('Expected an RDF/JS Quad');
510
+ const inner = new Triple(
511
+ rdfJsTermToInternal(quad.subject, 'quad.subject'),
512
+ rdfJsTermToInternal(quad.predicate, 'quad.predicate'),
513
+ rdfJsTermToInternal(quad.object, 'quad.object'),
514
+ );
515
+ const graph = rdfJsGraphNameToInternal(quad.graph, 'quad.graph');
516
+ if (!graph) {
517
+ out.push(inner);
518
+ continue;
519
+ }
520
+
521
+ const key = graphKey(quad.graph);
522
+ let group = namedGraphs.get(key);
523
+ if (!group) {
524
+ group = { graph, triples: [] };
525
+ namedGraphs.set(key, group);
526
+ }
527
+ group.triples.push(inner);
528
+ }
529
+
530
+ for (const { graph, triples } of namedGraphs.values()) {
531
+ out.push(new Triple(graph, new Iri(LOG_NS + 'nameOf'), annotateQuotedGraphTerm(new GraphTerm(triples))));
532
+ }
533
+
534
+ return out;
428
535
  }
429
536
 
430
537
  function rdfJsQuadToN3(quad) {
431
538
  if (!isRdfJsQuad(quad)) throw new TypeError('Expected an RDF/JS Quad');
432
- if (quad.graph.termType !== 'DefaultGraph') {
433
- throw new TypeError('Named graph quads are not supported by Eyeling input; use the default graph only');
434
- }
435
- return `${rdfJsTermToN3(quad.subject, 'quad.subject')} ${rdfJsTermToN3(quad.predicate, 'quad.predicate')} ${rdfJsTermToN3(quad.object, 'quad.object')}.`;
539
+ return tripleToN3(rdfJsQuadToInternalTriple(quad), PrefixEnv.newDefault());
540
+ }
541
+
542
+ function rdfJsQuadsToN3(quads) {
543
+ return rdfJsQuadsToInternalTriples(quads)
544
+ .map((triple) => tripleToN3(triple, PrefixEnv.newDefault()))
545
+ .join('\n');
436
546
  }
437
547
 
438
548
  function collectIterableToArray(iterable, label) {
@@ -477,6 +587,11 @@ function getPrefixesText(input) {
477
587
  return '';
478
588
  }
479
589
 
590
+ function getN3Text(input) {
591
+ if (!isObject(input)) return '';
592
+ return typeof input.n3 === 'string' ? input.n3 : '';
593
+ }
594
+
480
595
  function joinN3Sections(parts) {
481
596
  return parts
482
597
  .filter((part) => typeof part === 'string' && part.length > 0)
@@ -745,7 +860,7 @@ function appendSyncQuadFacts(doc, input) {
745
860
  const quads = collectIterableToArray(quadsInfo.value, quadsInfo.label);
746
861
  return {
747
862
  ...doc,
748
- triples: doc.triples.concat(quads.map((quad) => rdfJsQuadToInternalTriple(quad))),
863
+ triples: doc.triples.concat(rdfJsQuadsToInternalTriples(quads)),
749
864
  };
750
865
  }
751
866
 
@@ -755,7 +870,7 @@ async function appendAsyncQuadFacts(doc, input) {
755
870
  const quads = await collectAsyncIterableToArray(quadsInfo.value, quadsInfo.label);
756
871
  return {
757
872
  ...doc,
758
- triples: doc.triples.concat(quads.map((quad) => rdfJsQuadToInternalTriple(quad))),
873
+ triples: doc.triples.concat(rdfJsQuadsToInternalTriples(quads)),
759
874
  };
760
875
  }
761
876
 
@@ -774,13 +889,13 @@ async function normalizeParsedReasonerInputAsync(input) {
774
889
  function normalizeReasonerInputSync(input) {
775
890
  if (typeof input === 'string') return input;
776
891
  const parsed = normalizeParsedReasonerInputSync(input);
777
- if (parsed) return serializeEyelingDocument(parsed);
892
+ const n3Text = getN3Text(input);
893
+ if (parsed) return joinN3Sections([n3Text, serializeEyelingDocument(parsed)]);
778
894
  if (!isObject(input)) {
779
895
  throw new TypeError(
780
896
  'Reasoner input must be an N3 string, an Eyeling AST/rule object, or an object containing RDF/JS quads plus optional rules',
781
897
  );
782
898
  }
783
- if (typeof input.n3 === 'string') return input.n3;
784
899
 
785
900
  const quadsInfo = pickInputQuadIterable(input);
786
901
  const rulesText = getRulesText(input);
@@ -788,25 +903,25 @@ function normalizeReasonerInputSync(input) {
788
903
  const prefixesText = getPrefixesText(input);
789
904
 
790
905
  if (!quadsInfo) {
791
- if (rulesText || factsText || prefixesText) return joinN3Sections([prefixesText, factsText, rulesText]);
906
+ if (n3Text || rulesText || factsText || prefixesText) return joinN3Sections([prefixesText, n3Text, factsText, rulesText]);
792
907
  throw new TypeError('Input object must provide n3 text, Eyeling AST/rule objects, or RDF/JS quads/facts/dataset');
793
908
  }
794
909
 
795
910
  const quads = collectIterableToArray(quadsInfo.value, quadsInfo.label);
796
- const quadText = quads.map((quad) => rdfJsQuadToN3(quad)).join('\n');
797
- return joinN3Sections([prefixesText, factsText, quadText, rulesText]);
911
+ const quadText = rdfJsQuadsToN3(quads);
912
+ return joinN3Sections([prefixesText, n3Text, factsText, quadText, rulesText]);
798
913
  }
799
914
 
800
915
  async function normalizeReasonerInputAsync(input) {
801
916
  if (typeof input === 'string') return input;
802
917
  const parsed = await normalizeParsedReasonerInputAsync(input);
803
- if (parsed) return serializeEyelingDocument(parsed);
918
+ const n3Text = getN3Text(input);
919
+ if (parsed) return joinN3Sections([n3Text, serializeEyelingDocument(parsed)]);
804
920
  if (!isObject(input)) {
805
921
  throw new TypeError(
806
922
  'Reasoner input must be an N3 string, an Eyeling AST/rule object, or an object containing RDF/JS quads plus optional rules',
807
923
  );
808
924
  }
809
- if (typeof input.n3 === 'string') return input.n3;
810
925
 
811
926
  const quadsInfo = pickInputQuadIterable(input);
812
927
  const rulesText = getRulesText(input);
@@ -814,13 +929,13 @@ async function normalizeReasonerInputAsync(input) {
814
929
  const prefixesText = getPrefixesText(input);
815
930
 
816
931
  if (!quadsInfo) {
817
- if (rulesText || factsText || prefixesText) return joinN3Sections([prefixesText, factsText, rulesText]);
932
+ if (n3Text || rulesText || factsText || prefixesText) return joinN3Sections([prefixesText, n3Text, factsText, rulesText]);
818
933
  throw new TypeError('Input object must provide n3 text, Eyeling AST/rule objects, or RDF/JS quads/facts/dataset');
819
934
  }
820
935
 
821
936
  const quads = await collectAsyncIterableToArray(quadsInfo.value, quadsInfo.label);
822
- const quadText = quads.map((quad) => rdfJsQuadToN3(quad)).join('\n');
823
- return joinN3Sections([prefixesText, factsText, quadText, rulesText]);
937
+ const quadText = rdfJsQuadsToN3(quads);
938
+ return joinN3Sections([prefixesText, n3Text, factsText, quadText, rulesText]);
824
939
  }
825
940
 
826
941
  module.exports = {
@@ -831,8 +946,10 @@ module.exports = {
831
946
  rdfJsTermToN3,
832
947
  rdfJsQuadToN3,
833
948
  rdfJsQuadToInternalTriple,
949
+ rdfJsQuadsToInternalTriples,
834
950
  internalTermToRdfJs,
835
951
  internalTripleToRdfJsQuad,
952
+ internalTripleToRdfJsQuads,
836
953
  internalTripleToRdfJsQuadInGraph,
837
954
  normalizeParsedReasonerInputSync,
838
955
  normalizeReasonerInputSync,