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/README.md CHANGED
@@ -582,8 +582,11 @@ See the included examples:
582
582
  eyeling -r examples/rdf-messages.n3 examples/input/rdf-messages.trig
583
583
  eyeling -r examples/rdf-message-flow.n3 examples/input/rdf-message-flow.trig
584
584
  eyeling -r --stream-messages examples/rdf-message-flow.n3 examples/input/rdf-message-flow.trig
585
+ eyeling -r --stream-messages examples/alma-rdf-messages.n3 https://ugent-lib-opendata-prd.s3.ugent.be/alma-rdf/rdf-messages.20260404.nt
585
586
  ```
586
587
 
588
+ The Alma RDF Message Log example intentionally keeps the message log as a URL, because the source `.nt` file is larger than 9 GB.
589
+
587
590
  ---
588
591
 
589
592
  ## Built-ins
@@ -4623,10 +4623,13 @@ module.exports = {
4623
4623
  'use strict';
4624
4624
 
4625
4625
  const fs = require('node:fs');
4626
- const os = require('node:os');
4627
4626
  const path = require('node:path');
4628
- const { pathToFileURL, fileURLToPath } = require('node:url');
4627
+ const { pathToFileURL, fileURLToPath, URL } = require('node:url');
4629
4628
  const { TextDecoder } = require('node:util');
4629
+ const http = require('node:http');
4630
+ const https = require('node:https');
4631
+ const readline = require('node:readline');
4632
+ const zlib = require('node:zlib');
4630
4633
 
4631
4634
  const engine = require('./engine');
4632
4635
  const deref = require('./deref');
@@ -4760,7 +4763,8 @@ function __httpFetchScriptBody({ prefixOnly = false } = {}) {
4760
4763
  if (finished) return;
4761
4764
  chunks.push(chunk);
4762
4765
  bytes += chunk.length;
4763
- if (bytes >= limit) finish();
4766
+ const text = Buffer.concat(chunks, bytes).toString('utf8');
4767
+ if (bytes >= limit || /^\\s*(?:@version|VERSION)\\s+(["'])(?:1\\.1|1\\.2|1\\.2-basic)-messages\\1\\s*\\.?\\s*(?:#.*)?$/im.test(text)) finish();
4764
4768
  });
4765
4769
  body.on('end', finish);
4766
4770
  body.on('error', (e) => { console.error(e && e.message ? e.message : String(e)); process.exit(5); });
@@ -4789,22 +4793,95 @@ function __readHttpPrefixSync(sourceLabel, byteLimit = 64 * 1024) {
4789
4793
  return r.stdout;
4790
4794
  }
4791
4795
 
4792
- function __downloadHttpSourceToTempFileSync(sourceLabel) {
4793
- const cp = require('node:child_process');
4794
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-rdf-message-log-'));
4795
- const file = path.join(dir, 'source.txt');
4796
- const r = cp.spawnSync(process.execPath, ['-e', __httpFetchScriptBody({ prefixOnly: false }), sourceLabel, file], {
4797
- encoding: 'utf8',
4798
- maxBuffer: 1024 * 1024,
4799
- stdio: ['ignore', 'ignore', 'pipe'],
4796
+ function __openHttpTextStream(sourceLabel, redirects = 0) {
4797
+ const maxRedirects = 10;
4798
+ return new Promise((resolve, reject) => {
4799
+ if (redirects > maxRedirects) {
4800
+ reject(new Error('Too many redirects'));
4801
+ return;
4802
+ }
4803
+
4804
+ let parsed;
4805
+ try {
4806
+ parsed = new URL(sourceLabel);
4807
+ } catch (e) {
4808
+ reject(e);
4809
+ return;
4810
+ }
4811
+
4812
+ const mod = parsed.protocol === 'https:' ? https : parsed.protocol === 'http:' ? http : null;
4813
+ if (!mod) {
4814
+ reject(new Error(`Unsupported protocol ${parsed.protocol}`));
4815
+ return;
4816
+ }
4817
+
4818
+ const req = mod.request(
4819
+ {
4820
+ protocol: parsed.protocol,
4821
+ hostname: parsed.hostname,
4822
+ port: parsed.port || undefined,
4823
+ path: parsed.pathname + parsed.search,
4824
+ headers: {
4825
+ accept: 'text/n3, text/turtle, application/trig, application/n-triples, application/n-quads, text/plain;q=0.8, */*;q=0.01',
4826
+ 'accept-encoding': 'identity',
4827
+ 'user-agent': 'eyeling-rdf-message-stream',
4828
+ },
4829
+ },
4830
+ (res) => {
4831
+ const sc = res.statusCode || 0;
4832
+ if (sc >= 300 && sc < 400 && res.headers && res.headers.location) {
4833
+ const next = new URL(res.headers.location, sourceLabel).toString();
4834
+ res.resume();
4835
+ resolve(__openHttpTextStream(next, redirects + 1));
4836
+ return;
4837
+ }
4838
+ if (sc < 200 || sc >= 300) {
4839
+ res.resume();
4840
+ reject(new Error(`HTTP status ${sc}`));
4841
+ return;
4842
+ }
4843
+
4844
+ const enc = String((res.headers && res.headers['content-encoding']) || '').toLowerCase();
4845
+ let body = res;
4846
+ if (enc.includes('gzip')) body = res.pipe(zlib.createGunzip());
4847
+ else if (enc.includes('deflate')) body = res.pipe(zlib.createInflate());
4848
+ else if (enc.includes('br')) body = res.pipe(zlib.createBrotliDecompress());
4849
+ resolve(body);
4850
+ },
4851
+ );
4852
+ req.on('error', reject);
4853
+ req.end();
4800
4854
  });
4801
- if (r.status !== 0) {
4802
- try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
4803
- throw new Error(`Failed to dereference ${sourceLabel}${r.stderr ? ': ' + String(r.stderr).trim() : ''}`);
4804
- }
4805
- return { file, cleanup: () => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch {} } };
4806
4855
  }
4807
4856
 
4857
+ async function forEachLineInHttpSource(sourceLabel, onLine) {
4858
+ const body = await __openHttpTextStream(sourceLabel);
4859
+ await new Promise((resolve, reject) => {
4860
+ let settled = false;
4861
+ function done(err) {
4862
+ if (settled) return;
4863
+ settled = true;
4864
+ if (err) reject(err);
4865
+ else resolve();
4866
+ }
4867
+
4868
+ const rl = readline.createInterface({ input: body, crlfDelay: Infinity });
4869
+ rl.on('line', (line) => {
4870
+ try {
4871
+ onLine(line + '\n');
4872
+ } catch (e) {
4873
+ try { rl.close(); } catch {}
4874
+ if (body && typeof body.destroy === 'function') {
4875
+ try { body.destroy(); } catch {}
4876
+ }
4877
+ done(e);
4878
+ }
4879
+ });
4880
+ rl.on('close', () => done());
4881
+ rl.on('error', done);
4882
+ body.on('error', done);
4883
+ });
4884
+ }
4808
4885
 
4809
4886
  const RDF_MESSAGE_VERSION_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im;
4810
4887
  const RDF_MESSAGE_VERSION_LINE_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/i;
@@ -5022,15 +5099,50 @@ function __forEachRdfMessageChunkSync(sourceLabel, onMessage) {
5022
5099
  return;
5023
5100
  }
5024
5101
  if (__isHttpSource(sourceLabel)) {
5025
- const tmp = __downloadHttpSourceToTempFileSync(sourceLabel);
5026
- try {
5027
- forEachRdfMessageChunkInFileSync(tmp.file, onMessage);
5028
- } finally {
5029
- tmp.cleanup();
5102
+ throw new Error('internal error: HTTP RDF Message Logs must be streamed asynchronously');
5103
+ }
5104
+ forEachRdfMessageChunkInText(__readInputSourceSync(sourceLabel), onMessage);
5105
+ }
5106
+
5107
+
5108
+ async function forEachRdfMessageChunkInHttpSource(sourceLabel, onMessage) {
5109
+ const directives = [];
5110
+ const seenDirectives = new Set();
5111
+ let chunk = '';
5112
+ let messageIndex = 1;
5113
+ let sawVersion = false;
5114
+ let sawDelimiter = false;
5115
+
5116
+ function emit() {
5117
+ onMessage({ messageIndex, chunk, directives: directives.slice() });
5118
+ messageIndex += 1;
5119
+ chunk = '';
5120
+ }
5121
+
5122
+ await forEachLineInHttpSource(sourceLabel, (line) => {
5123
+ if (RDF_MESSAGE_VERSION_LINE_RE.test(line)) {
5124
+ sawVersion = true;
5125
+ return;
5126
+ }
5127
+ if (RDF_MESSAGE_DELIMITER_LINE_RE.test(line)) {
5128
+ emit();
5129
+ sawDelimiter = true;
5130
+ return;
5030
5131
  }
5132
+ addRdfDirective(directives, seenDirectives, line);
5133
+ chunk += line;
5134
+ });
5135
+
5136
+ if (!sawVersion) throw new Error('not an RDF Message Log: missing VERSION "*-messages" directive');
5137
+ if (sawDelimiter || hasRdfPayload(chunk)) emit();
5138
+ }
5139
+
5140
+ async function __forEachRdfMessageChunk(sourceLabel, onMessage) {
5141
+ if (__isHttpSource(sourceLabel)) {
5142
+ await forEachRdfMessageChunkInHttpSource(sourceLabel, onMessage);
5031
5143
  return;
5032
5144
  }
5033
- forEachRdfMessageChunkInText(__readInputSourceSync(sourceLabel), onMessage);
5145
+ __forEachRdfMessageChunkSync(sourceLabel, onMessage);
5034
5146
  }
5035
5147
 
5036
5148
  function factsContainOutputStrings(triplesForOutput) {
@@ -5096,7 +5208,7 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
5096
5208
  }
5097
5209
  }
5098
5210
 
5099
- function runStreamMessagesMode(sourceLabels, { rdfMode }) {
5211
+ async function runStreamMessagesMode(sourceLabels, { rdfMode }) {
5100
5212
  const ordinarySourceLabels = [];
5101
5213
  const messageSourceLabels = [];
5102
5214
 
@@ -5148,7 +5260,7 @@ function runStreamMessagesMode(sourceLabels, { rdfMode }) {
5148
5260
  const fullIriPrefixes = new PrefixEnv({});
5149
5261
  for (const messageSourceLabel of messageSourceLabels) {
5150
5262
  try {
5151
- __forEachRdfMessageChunkSync(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
5263
+ await __forEachRdfMessageChunk(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
5152
5264
  const messageText = buildSingleMessageReplayDocument({
5153
5265
  sourceLabel: messageSourceLabel,
5154
5266
  messageIndex,
@@ -5182,7 +5294,7 @@ function runStreamMessagesMode(sourceLabels, { rdfMode }) {
5182
5294
  }
5183
5295
  }
5184
5296
 
5185
- function main() {
5297
+ async function main() {
5186
5298
  // Drop "node" and script name; keep only user-provided args
5187
5299
  // Expand combined short options: -pt == -p -t
5188
5300
  const argvRaw = process.argv.slice(2);
@@ -5321,7 +5433,7 @@ function main() {
5321
5433
  }
5322
5434
 
5323
5435
  if (streamMessagesMode) {
5324
- runStreamMessagesMode(sourceLabels, { rdfMode });
5436
+ await runStreamMessagesMode(sourceLabels, { rdfMode });
5325
5437
  return;
5326
5438
  }
5327
5439
 
@@ -5405,7 +5517,8 @@ function main() {
5405
5517
  return false;
5406
5518
  }
5407
5519
 
5408
- function factsContainOutputStrings(triplesForOutput) {
5520
+
5521
+ function factsContainOutputStrings(triplesForOutput) {
5409
5522
  return (
5410
5523
  Array.isArray(triplesForOutput) &&
5411
5524
  triplesForOutput.some(
@@ -6106,7 +6219,7 @@ const {
6106
6219
  } = require('./printing');
6107
6220
  const {
6108
6221
  getDataFactory,
6109
- internalTripleToRdfJsQuad,
6222
+ internalTripleToRdfJsQuads,
6110
6223
  normalizeParsedReasonerInputSync,
6111
6224
  normalizeReasonerInputSync,
6112
6225
  normalizeReasonerInputAsync,
@@ -9604,15 +9717,22 @@ function isUnsupportedRdfJsConversionError(err) {
9604
9717
  );
9605
9718
  }
9606
9719
 
9607
- function maybeTripleToRdfJsQuad(triple, rdfFactory, skipUnsupportedRdfJs) {
9720
+ function maybeTripleToRdfJsQuads(triple, rdfFactory, skipUnsupportedRdfJs) {
9608
9721
  try {
9609
- return internalTripleToRdfJsQuad(triple, rdfFactory);
9722
+ return internalTripleToRdfJsQuads(triple, rdfFactory);
9610
9723
  } catch (err) {
9611
- if (skipUnsupportedRdfJs && isUnsupportedRdfJsConversionError(err)) return null;
9724
+ if (skipUnsupportedRdfJs && isUnsupportedRdfJsConversionError(err)) return [];
9612
9725
  throw err;
9613
9726
  }
9614
9727
  }
9615
9728
 
9729
+ function addRdfJsPayloadQuads(payload, quads) {
9730
+ if (!Array.isArray(quads) || quads.length === 0) return payload;
9731
+ payload.quads = quads;
9732
+ if (quads.length === 1) payload.quad = quads[0];
9733
+ return payload;
9734
+ }
9735
+
9616
9736
  function reasonStream(input, opts = {}) {
9617
9737
  const {
9618
9738
  baseIri = null,
@@ -9640,7 +9760,8 @@ function reasonStream(input, opts = {}) {
9640
9760
  rdf: useRdfCompatibility,
9641
9761
  })
9642
9762
  : null;
9643
- const parsedInput = parsedSourceList || parsedTextInput || normalizeParsedReasonerInputSync(input);
9763
+ const hasInlineN3 = input && typeof input === 'object' && !Array.isArray(input) && typeof input.n3 === 'string';
9764
+ const parsedInput = parsedSourceList || parsedTextInput || (hasInlineN3 ? null : normalizeParsedReasonerInputSync(input));
9644
9765
  const rdfFactory = rdfjs ? getDataFactory(dataFactory) : null;
9645
9766
 
9646
9767
  const __oldEnforceHttps = deref.getEnforceHttpsEnabled();
@@ -9711,8 +9832,7 @@ function reasonStream(input, opts = {}) {
9711
9832
  for (const qdf of queryDerived) {
9712
9833
  const payload = { triple: useRdfCompatibility ? tripleToRdfCompatible(qdf.fact, prefixes) : tripleToN3(qdf.fact, prefixes), df: qdf };
9713
9834
  if (rdfFactory) {
9714
- const quad = maybeTripleToRdfJsQuad(qdf.fact, rdfFactory, skipUnsupportedRdfJs);
9715
- if (quad) payload.quad = quad;
9835
+ addRdfJsPayloadQuads(payload, maybeTripleToRdfJsQuads(qdf.fact, rdfFactory, skipUnsupportedRdfJs));
9716
9836
  }
9717
9837
  onDerived(payload);
9718
9838
  }
@@ -9730,8 +9850,7 @@ function reasonStream(input, opts = {}) {
9730
9850
  df,
9731
9851
  };
9732
9852
  if (rdfFactory) {
9733
- const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
9734
- if (quad) payload.quad = quad;
9853
+ addRdfJsPayloadQuads(payload, maybeTripleToRdfJsQuads(df.fact, rdfFactory, skipUnsupportedRdfJs));
9735
9854
  }
9736
9855
  onDerived(payload);
9737
9856
  }
@@ -9772,12 +9891,10 @@ function reasonStream(input, opts = {}) {
9772
9891
  };
9773
9892
 
9774
9893
  if (rdfFactory) {
9775
- __out.closureQuads = closureTriples
9776
- .map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
9777
- .filter(Boolean);
9778
- __out.queryQuads = queryTriples
9779
- .map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
9780
- .filter(Boolean);
9894
+ __out.closureQuads = closureTriples.flatMap((t) =>
9895
+ maybeTripleToRdfJsQuads(t, rdfFactory, skipUnsupportedRdfJs),
9896
+ );
9897
+ __out.queryQuads = queryTriples.flatMap((t) => maybeTripleToRdfJsQuads(t, rdfFactory, skipUnsupportedRdfJs));
9781
9898
  }
9782
9899
  deref.setEnforceHttpsEnabled(__oldEnforceHttps);
9783
9900
  return __out;
@@ -9808,9 +9925,9 @@ function reasonRdfJs(input, opts = {}) {
9808
9925
  ...restOpts,
9809
9926
  rdfjs: false,
9810
9927
  onDerived: ({ df }) => {
9811
- const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
9812
- if (quad) {
9813
- queue.push(quad);
9928
+ const quads = maybeTripleToRdfJsQuads(df.fact, rdfFactory, skipUnsupportedRdfJs);
9929
+ if (quads.length) {
9930
+ queue.push(...quads);
9814
9931
  flush();
9815
9932
  }
9816
9933
  },
@@ -9839,7 +9956,15 @@ function reasonRdfJs(input, opts = {}) {
9839
9956
  // Minimal export surface for Node + browser/worker
9840
9957
  function main() {
9841
9958
  // Lazily require to avoid hard cycles in the module graph.
9842
- return require('./cli').main();
9959
+ const result = require('./cli').main();
9960
+ if (result && typeof result.then === 'function') {
9961
+ result.catch((e) => {
9962
+ const msg = e && e.stack ? e.stack : e && e.message ? e.message : String(e);
9963
+ console.error(msg);
9964
+ process.exit(1);
9965
+ });
9966
+ }
9967
+ return result;
9843
9968
  }
9844
9969
 
9845
9970
  // ---------------------------------------------------------------------------
@@ -14708,6 +14833,7 @@ module.exports = {
14708
14833
 
14709
14834
  const {
14710
14835
  XSD_NS,
14836
+ LOG_NS,
14711
14837
  Literal: InternalLiteral,
14712
14838
  Iri,
14713
14839
  Blank,
@@ -14718,6 +14844,7 @@ const {
14718
14844
  Triple,
14719
14845
  Rule,
14720
14846
  PrefixEnv,
14847
+ annotateQuotedGraphTerm,
14721
14848
  literalParts,
14722
14849
  } = require('./prelude');
14723
14850
  const { termToN3, tripleToN3 } = require('./printing');
@@ -15021,6 +15148,49 @@ function internalRdf12TermToRdfJs(term, factory, position) {
15021
15148
  return internalTermToRdfJs(term, rdfFactory, position);
15022
15149
  }
15023
15150
 
15151
+ function isDefaultGraphTerm(term) {
15152
+ return isRdfJsTerm(term) && term.termType === 'DefaultGraph';
15153
+ }
15154
+
15155
+ function assertDefaultGraphForRdfJsQuadTerm(term, position) {
15156
+ if (!isDefaultGraphTerm(term.graph)) {
15157
+ throw new TypeError(`RDF/JS Quad terms with named graphs are not supported in ${position}`);
15158
+ }
15159
+ }
15160
+
15161
+ function rdfJsGraphNameToInternal(term, position = 'quad.graph') {
15162
+ assertSupportedRdfJsTerm(term, position);
15163
+ switch (term.termType) {
15164
+ case 'NamedNode':
15165
+ return new Iri(term.value);
15166
+ case 'BlankNode':
15167
+ return new Blank(`_:${term.value}`);
15168
+ case 'DefaultGraph':
15169
+ return null;
15170
+ default:
15171
+ throw new TypeError(`Invalid RDF graph termType ${term.termType}`);
15172
+ }
15173
+ }
15174
+
15175
+ function internalGraphNameToRdfJs(term, factory, position = 'graph') {
15176
+ if (term instanceof Iri || term instanceof Blank) return internalTermToRdfJs(term, factory, position);
15177
+ return unsupportedRdfJsTerm(term, position);
15178
+ }
15179
+
15180
+ function graphKey(term) {
15181
+ return `${term.termType}:${term.value}`;
15182
+ }
15183
+
15184
+ function isInternalLogNameOfTriple(triple) {
15185
+ return (
15186
+ triple instanceof Triple &&
15187
+ (triple.s instanceof Iri || triple.s instanceof Blank) &&
15188
+ triple.p instanceof Iri &&
15189
+ triple.p.value === LOG_NS + 'nameOf' &&
15190
+ triple.o instanceof GraphTerm
15191
+ );
15192
+ }
15193
+
15024
15194
  function assertRdfJsQuadShape(subject, predicate, object, graph) {
15025
15195
  if (!['NamedNode', 'BlankNode', 'Quad'].includes(subject.termType)) {
15026
15196
  throw new TypeError(`Invalid RDF subject termType ${subject.termType}`);
@@ -15049,13 +15219,22 @@ function internalTripleToRdfJsQuadInGraph(triple, graph, factory) {
15049
15219
  function internalTripleToRdfJsQuad(triple, factory) {
15050
15220
  const rdfFactory = getDataFactory(factory);
15051
15221
  return rdfFactory.quad(
15052
- internalTermToRdfJs(triple.s, rdfFactory, 'subject'),
15222
+ internalRdf12TermToRdfJs(triple.s, rdfFactory, 'subject'),
15053
15223
  internalTermToRdfJs(triple.p, rdfFactory, 'predicate'),
15054
- internalTermToRdfJs(triple.o, rdfFactory, 'object'),
15224
+ internalRdf12TermToRdfJs(triple.o, rdfFactory, 'object'),
15055
15225
  rdfFactory.defaultGraph(),
15056
15226
  );
15057
15227
  }
15058
15228
 
15229
+ function internalTripleToRdfJsQuads(triple, factory) {
15230
+ const rdfFactory = getDataFactory(factory);
15231
+ if (isInternalLogNameOfTriple(triple)) {
15232
+ const graph = internalGraphNameToRdfJs(triple.s, rdfFactory, 'graph');
15233
+ return (triple.o.triples || []).map((inner) => internalTripleToRdfJsQuadInGraph(inner, graph, rdfFactory));
15234
+ }
15235
+ return [internalTripleToRdfJsQuad(triple, rdfFactory)];
15236
+ }
15237
+
15059
15238
  function escapeStringForN3(value) {
15060
15239
  return JSON.stringify(String(value));
15061
15240
  }
@@ -15087,7 +15266,14 @@ function rdfJsTermToN3(term, position = 'term') {
15087
15266
  return `${lexical}^^<${datatype}>`;
15088
15267
  }
15089
15268
  case 'Quad':
15090
- throw new TypeError(`Quoted triple terms are not supported in ${position}`);
15269
+ if (String(position).endsWith('.predicate') || position === 'quad.predicate' || position === 'predicate') {
15270
+ throw new TypeError(`Quoted triple terms are not valid RDF predicates in ${position}`);
15271
+ }
15272
+ assertDefaultGraphForRdfJsQuadTerm(term, position);
15273
+ return `{ ${rdfJsTermToN3(term.subject, `${position}.subject`)} ${rdfJsTermToN3(
15274
+ term.predicate,
15275
+ `${position}.predicate`,
15276
+ )} ${rdfJsTermToN3(term.object, `${position}.object`)} . }`;
15091
15277
  default:
15092
15278
  throw new TypeError(`Unsupported RDF/JS termType ${JSON.stringify(term.termType)} in ${position}`);
15093
15279
  }
@@ -15107,8 +15293,21 @@ function rdfJsTermToInternal(term, position = 'term') {
15107
15293
  return new InternalLiteral(rdfJsTermToN3(term, position));
15108
15294
  case 'DefaultGraph':
15109
15295
  throw new TypeError(`DefaultGraph is not a valid standalone N3 term in ${position}`);
15110
- case 'Quad':
15111
- throw new TypeError(`Quoted triple terms are not supported in ${position}`);
15296
+ case 'Quad': {
15297
+ if (String(position).endsWith('.predicate') || position === 'quad.predicate' || position === 'predicate') {
15298
+ throw new TypeError(`Quoted triple terms are not valid RDF predicates in ${position}`);
15299
+ }
15300
+ assertDefaultGraphForRdfJsQuadTerm(term, position);
15301
+ return annotateQuotedGraphTerm(
15302
+ new GraphTerm([
15303
+ new Triple(
15304
+ rdfJsTermToInternal(term.subject, `${position}.subject`),
15305
+ rdfJsTermToInternal(term.predicate, `${position}.predicate`),
15306
+ rdfJsTermToInternal(term.object, `${position}.object`),
15307
+ ),
15308
+ ]),
15309
+ );
15310
+ }
15112
15311
  default:
15113
15312
  throw new TypeError(`Unsupported RDF/JS termType ${JSON.stringify(term.termType)} in ${position}`);
15114
15313
  }
@@ -15116,22 +15315,58 @@ function rdfJsTermToInternal(term, position = 'term') {
15116
15315
 
15117
15316
  function rdfJsQuadToInternalTriple(quad) {
15118
15317
  if (!isRdfJsQuad(quad)) throw new TypeError('Expected an RDF/JS Quad');
15119
- if (quad.graph.termType !== 'DefaultGraph') {
15120
- throw new TypeError('Named graph quads are not supported by Eyeling input; use the default graph only');
15121
- }
15122
- return new Triple(
15318
+ const inner = new Triple(
15123
15319
  rdfJsTermToInternal(quad.subject, 'quad.subject'),
15124
15320
  rdfJsTermToInternal(quad.predicate, 'quad.predicate'),
15125
15321
  rdfJsTermToInternal(quad.object, 'quad.object'),
15126
15322
  );
15323
+ const graph = rdfJsGraphNameToInternal(quad.graph, 'quad.graph');
15324
+ if (!graph) return inner;
15325
+ return new Triple(graph, new Iri(LOG_NS + 'nameOf'), annotateQuotedGraphTerm(new GraphTerm([inner])));
15326
+ }
15327
+
15328
+ function rdfJsQuadsToInternalTriples(quads) {
15329
+ const out = [];
15330
+ const namedGraphs = new Map();
15331
+
15332
+ for (const quad of quads) {
15333
+ if (!isRdfJsQuad(quad)) throw new TypeError('Expected an RDF/JS Quad');
15334
+ const inner = new Triple(
15335
+ rdfJsTermToInternal(quad.subject, 'quad.subject'),
15336
+ rdfJsTermToInternal(quad.predicate, 'quad.predicate'),
15337
+ rdfJsTermToInternal(quad.object, 'quad.object'),
15338
+ );
15339
+ const graph = rdfJsGraphNameToInternal(quad.graph, 'quad.graph');
15340
+ if (!graph) {
15341
+ out.push(inner);
15342
+ continue;
15343
+ }
15344
+
15345
+ const key = graphKey(quad.graph);
15346
+ let group = namedGraphs.get(key);
15347
+ if (!group) {
15348
+ group = { graph, triples: [] };
15349
+ namedGraphs.set(key, group);
15350
+ }
15351
+ group.triples.push(inner);
15352
+ }
15353
+
15354
+ for (const { graph, triples } of namedGraphs.values()) {
15355
+ out.push(new Triple(graph, new Iri(LOG_NS + 'nameOf'), annotateQuotedGraphTerm(new GraphTerm(triples))));
15356
+ }
15357
+
15358
+ return out;
15127
15359
  }
15128
15360
 
15129
15361
  function rdfJsQuadToN3(quad) {
15130
15362
  if (!isRdfJsQuad(quad)) throw new TypeError('Expected an RDF/JS Quad');
15131
- if (quad.graph.termType !== 'DefaultGraph') {
15132
- throw new TypeError('Named graph quads are not supported by Eyeling input; use the default graph only');
15133
- }
15134
- return `${rdfJsTermToN3(quad.subject, 'quad.subject')} ${rdfJsTermToN3(quad.predicate, 'quad.predicate')} ${rdfJsTermToN3(quad.object, 'quad.object')}.`;
15363
+ return tripleToN3(rdfJsQuadToInternalTriple(quad), PrefixEnv.newDefault());
15364
+ }
15365
+
15366
+ function rdfJsQuadsToN3(quads) {
15367
+ return rdfJsQuadsToInternalTriples(quads)
15368
+ .map((triple) => tripleToN3(triple, PrefixEnv.newDefault()))
15369
+ .join('\n');
15135
15370
  }
15136
15371
 
15137
15372
  function collectIterableToArray(iterable, label) {
@@ -15176,6 +15411,11 @@ function getPrefixesText(input) {
15176
15411
  return '';
15177
15412
  }
15178
15413
 
15414
+ function getN3Text(input) {
15415
+ if (!isObject(input)) return '';
15416
+ return typeof input.n3 === 'string' ? input.n3 : '';
15417
+ }
15418
+
15179
15419
  function joinN3Sections(parts) {
15180
15420
  return parts
15181
15421
  .filter((part) => typeof part === 'string' && part.length > 0)
@@ -15444,7 +15684,7 @@ function appendSyncQuadFacts(doc, input) {
15444
15684
  const quads = collectIterableToArray(quadsInfo.value, quadsInfo.label);
15445
15685
  return {
15446
15686
  ...doc,
15447
- triples: doc.triples.concat(quads.map((quad) => rdfJsQuadToInternalTriple(quad))),
15687
+ triples: doc.triples.concat(rdfJsQuadsToInternalTriples(quads)),
15448
15688
  };
15449
15689
  }
15450
15690
 
@@ -15454,7 +15694,7 @@ async function appendAsyncQuadFacts(doc, input) {
15454
15694
  const quads = await collectAsyncIterableToArray(quadsInfo.value, quadsInfo.label);
15455
15695
  return {
15456
15696
  ...doc,
15457
- triples: doc.triples.concat(quads.map((quad) => rdfJsQuadToInternalTriple(quad))),
15697
+ triples: doc.triples.concat(rdfJsQuadsToInternalTriples(quads)),
15458
15698
  };
15459
15699
  }
15460
15700
 
@@ -15473,13 +15713,13 @@ async function normalizeParsedReasonerInputAsync(input) {
15473
15713
  function normalizeReasonerInputSync(input) {
15474
15714
  if (typeof input === 'string') return input;
15475
15715
  const parsed = normalizeParsedReasonerInputSync(input);
15476
- if (parsed) return serializeEyelingDocument(parsed);
15716
+ const n3Text = getN3Text(input);
15717
+ if (parsed) return joinN3Sections([n3Text, serializeEyelingDocument(parsed)]);
15477
15718
  if (!isObject(input)) {
15478
15719
  throw new TypeError(
15479
15720
  'Reasoner input must be an N3 string, an Eyeling AST/rule object, or an object containing RDF/JS quads plus optional rules',
15480
15721
  );
15481
15722
  }
15482
- if (typeof input.n3 === 'string') return input.n3;
15483
15723
 
15484
15724
  const quadsInfo = pickInputQuadIterable(input);
15485
15725
  const rulesText = getRulesText(input);
@@ -15487,25 +15727,25 @@ function normalizeReasonerInputSync(input) {
15487
15727
  const prefixesText = getPrefixesText(input);
15488
15728
 
15489
15729
  if (!quadsInfo) {
15490
- if (rulesText || factsText || prefixesText) return joinN3Sections([prefixesText, factsText, rulesText]);
15730
+ if (n3Text || rulesText || factsText || prefixesText) return joinN3Sections([prefixesText, n3Text, factsText, rulesText]);
15491
15731
  throw new TypeError('Input object must provide n3 text, Eyeling AST/rule objects, or RDF/JS quads/facts/dataset');
15492
15732
  }
15493
15733
 
15494
15734
  const quads = collectIterableToArray(quadsInfo.value, quadsInfo.label);
15495
- const quadText = quads.map((quad) => rdfJsQuadToN3(quad)).join('\n');
15496
- return joinN3Sections([prefixesText, factsText, quadText, rulesText]);
15735
+ const quadText = rdfJsQuadsToN3(quads);
15736
+ return joinN3Sections([prefixesText, n3Text, factsText, quadText, rulesText]);
15497
15737
  }
15498
15738
 
15499
15739
  async function normalizeReasonerInputAsync(input) {
15500
15740
  if (typeof input === 'string') return input;
15501
15741
  const parsed = await normalizeParsedReasonerInputAsync(input);
15502
- if (parsed) return serializeEyelingDocument(parsed);
15742
+ const n3Text = getN3Text(input);
15743
+ if (parsed) return joinN3Sections([n3Text, serializeEyelingDocument(parsed)]);
15503
15744
  if (!isObject(input)) {
15504
15745
  throw new TypeError(
15505
15746
  'Reasoner input must be an N3 string, an Eyeling AST/rule object, or an object containing RDF/JS quads plus optional rules',
15506
15747
  );
15507
15748
  }
15508
- if (typeof input.n3 === 'string') return input.n3;
15509
15749
 
15510
15750
  const quadsInfo = pickInputQuadIterable(input);
15511
15751
  const rulesText = getRulesText(input);
@@ -15513,13 +15753,13 @@ async function normalizeReasonerInputAsync(input) {
15513
15753
  const prefixesText = getPrefixesText(input);
15514
15754
 
15515
15755
  if (!quadsInfo) {
15516
- if (rulesText || factsText || prefixesText) return joinN3Sections([prefixesText, factsText, rulesText]);
15756
+ if (n3Text || rulesText || factsText || prefixesText) return joinN3Sections([prefixesText, n3Text, factsText, rulesText]);
15517
15757
  throw new TypeError('Input object must provide n3 text, Eyeling AST/rule objects, or RDF/JS quads/facts/dataset');
15518
15758
  }
15519
15759
 
15520
15760
  const quads = await collectAsyncIterableToArray(quadsInfo.value, quadsInfo.label);
15521
- const quadText = quads.map((quad) => rdfJsQuadToN3(quad)).join('\n');
15522
- return joinN3Sections([prefixesText, factsText, quadText, rulesText]);
15761
+ const quadText = rdfJsQuadsToN3(quads);
15762
+ return joinN3Sections([prefixesText, n3Text, factsText, quadText, rulesText]);
15523
15763
  }
15524
15764
 
15525
15765
  module.exports = {
@@ -15530,8 +15770,10 @@ module.exports = {
15530
15770
  rdfJsTermToN3,
15531
15771
  rdfJsQuadToN3,
15532
15772
  rdfJsQuadToInternalTriple,
15773
+ rdfJsQuadsToInternalTriples,
15533
15774
  internalTermToRdfJs,
15534
15775
  internalTripleToRdfJsQuad,
15776
+ internalTripleToRdfJsQuads,
15535
15777
  internalTripleToRdfJsQuadInGraph,
15536
15778
  normalizeParsedReasonerInputSync,
15537
15779
  normalizeReasonerInputSync,