eyeling 1.28.9 → 1.29.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.
@@ -5842,10 +5842,12 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
5842
5842
 
5843
5843
  let derived = [];
5844
5844
  let outTriples = [];
5845
+ let queryDerived = [];
5845
5846
  if (hasQueries) {
5846
5847
  const res = engine.forwardChainAndCollectLogQueryConclusions(facts, frules, brules, qrules, { prefixes });
5847
5848
  derived = res.derived;
5848
5849
  outTriples = res.queryTriples;
5850
+ queryDerived = res.queryDerived || [];
5849
5851
  } else {
5850
5852
  const skipDerivedCollection = mayAutoRenderOutputStrings;
5851
5853
  derived = engine.forwardChain(facts, frules, brules, null, {
@@ -5859,7 +5861,7 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
5859
5861
  const renderedOutputTriples = hasQueries ? outTriples : facts;
5860
5862
  if (factsContainOutputStrings(renderedOutputTriples)) {
5861
5863
  process.stdout.write(engine.collectOutputStringsFromFacts(renderedOutputTriples, prefixes));
5862
- return;
5864
+ return { facts, derived, outTriples, queryDerived, queryMode: !!hasQueries };
5863
5865
  }
5864
5866
 
5865
5867
  const outPrefixEnv = outputPrefixes || prefixes;
@@ -5871,9 +5873,99 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
5871
5873
  } else {
5872
5874
  for (const df of derived) console.log(engine.tripleToN3(df.fact, outPrefixEnv));
5873
5875
  }
5876
+ return { facts, derived, outTriples, queryDerived, queryMode: !!hasQueries };
5877
+ }
5878
+
5879
+ async function createCliStore({ storeName, storePath = null, storeClear = false } = {}) {
5880
+ if (!storeName) return null;
5881
+ return engine.createFactStore({ name: storeName, path: storePath || undefined, clear: !!storeClear });
5882
+ }
5883
+
5884
+ async function persistRunResultToStore(store, runResult) {
5885
+ if (!store || !runResult) return;
5886
+ await store.batchAdd(runResult.facts || [], 'explicit');
5887
+ await store.batchAdd((runResult.derived || []).map((df) => df.fact), 'inferred');
5888
+ await store.batchAdd((runResult.queryDerived || []).map((df) => df.fact), 'inferred');
5889
+ await store.batchAdd(runResult.outTriples || [], 'inferred');
5890
+ }
5891
+
5892
+ function sourceLooksLikeLineBasedRdf(sourceLabel) {
5893
+ if (typeof sourceLabel !== 'string') return false;
5894
+ const clean = sourceLabel.split(/[?#]/, 1)[0].toLowerCase();
5895
+ return /\.(?:nt|nq)(?:\.gz|\.br|\.deflate)?$/.test(clean);
5896
+ }
5897
+
5898
+ function parseLineBasedRdfLine(line, { sourceLabel, lineNumber, rdfMode }) {
5899
+ const text = String(line || '');
5900
+ if (!text.trim() || /^\s*#/.test(text)) return [];
5901
+ const doc = parseN3Text(text, {
5902
+ baseIri: __sourceLabelToBaseIri(sourceLabel),
5903
+ label: `${sourceLabel}:${lineNumber}`,
5904
+ keepSourceArtifacts: false,
5905
+ sourceLocations: false,
5906
+ rdf: rdfMode,
5907
+ });
5908
+ return doc.triples || [];
5909
+ }
5910
+
5911
+ async function ingestLineBasedRdfSourceToStore(sourceLabel, store, { rdfMode = true } = {}) {
5912
+ let lineNumber = 0;
5913
+ let count = 0;
5914
+ const onLine = async (line) => {
5915
+ lineNumber += 1;
5916
+ let triples;
5917
+ try {
5918
+ triples = parseLineBasedRdfLine(line, { sourceLabel, lineNumber, rdfMode });
5919
+ } catch (e) {
5920
+ if (e && e.name === 'N3SyntaxError') {
5921
+ throw new Error(formatN3SyntaxError(e, line, `${sourceLabel}:${lineNumber}`));
5922
+ }
5923
+ throw e;
5924
+ }
5925
+ count += await store.batchAdd(triples, 'explicit');
5926
+ };
5927
+
5928
+ const filePath = __localPathForSource(sourceLabel);
5929
+ if (filePath) {
5930
+ const fd = fs.openSync(filePath, 'r');
5931
+ const decoder = new TextDecoder('utf8');
5932
+ const buf = Buffer.allocUnsafe(64 * 1024);
5933
+ let carry = '';
5934
+ try {
5935
+ for (;;) {
5936
+ const n = fs.readSync(fd, buf, 0, buf.length, null);
5937
+ if (n === 0) break;
5938
+ carry += decoder.decode(buf.subarray(0, n), { stream: true });
5939
+ for (;;) {
5940
+ const m = /\r\n|\n|\r/.exec(carry);
5941
+ if (!m) break;
5942
+ const end = m.index + m[0].length;
5943
+ await onLine(carry.slice(0, end));
5944
+ carry = carry.slice(end);
5945
+ }
5946
+ }
5947
+ carry += decoder.decode();
5948
+ if (carry) await onLine(carry);
5949
+ } finally {
5950
+ fs.closeSync(fd);
5951
+ }
5952
+ return count;
5953
+ }
5954
+
5955
+ if (__isHttpSource(sourceLabel)) {
5956
+ const body = await __openHttpTextStream(sourceLabel);
5957
+ const rl = readline.createInterface({ input: body, crlfDelay: Infinity });
5958
+ for await (const line of rl) await onLine(line + '\n');
5959
+ return count;
5960
+ }
5961
+
5962
+ const text = __readInputSourceSync(sourceLabel);
5963
+ for (const line of text.match(/.*(?:\r\n|\n|\r)|.+$/g) || []) await onLine(line);
5964
+ return count;
5874
5965
  }
5875
5966
 
5876
- async function runStreamMessagesMode(sourceLabels, { rdfMode }) {
5967
+
5968
+ async function runStreamMessagesMode(sourceLabels, { rdfMode, storeName = null, storePath = null, storeClear = false } = {}) {
5877
5969
  const ordinarySourceLabels = [];
5878
5970
  const messageSourceLabels = [];
5879
5971
 
@@ -5922,40 +6014,49 @@ async function runStreamMessagesMode(sourceLabels, { rdfMode }) {
5922
6014
  }
5923
6015
  }
5924
6016
 
6017
+ const store = await createCliStore({ storeName, storePath, storeClear });
6018
+ let pendingStoreWrites = Promise.resolve();
6019
+
5925
6020
  const fullIriPrefixes = new PrefixEnv({});
5926
- for (const messageSourceLabel of messageSourceLabels) {
5927
- try {
5928
- await __forEachRdfMessageChunk(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
5929
- const messageText = buildSingleMessageReplayDocument({
5930
- sourceLabel: messageSourceLabel,
5931
- messageIndex,
5932
- chunk,
5933
- directives,
5934
- });
5935
- let messageDoc;
5936
- try {
5937
- messageDoc = parseN3Text(messageText, {
5938
- baseIri: `${__sourceLabelToBaseIri(messageSourceLabel)}#message-${messageIndex}`,
5939
- label: `${messageSourceLabel}#message-${messageIndex}`,
5940
- keepSourceArtifacts: false,
5941
- sourceLocations: false,
5942
- rdf: false,
6021
+ try {
6022
+ for (const messageSourceLabel of messageSourceLabels) {
6023
+ try {
6024
+ await __forEachRdfMessageChunk(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
6025
+ const messageText = buildSingleMessageReplayDocument({
6026
+ sourceLabel: messageSourceLabel,
6027
+ messageIndex,
6028
+ chunk,
6029
+ directives,
5943
6030
  });
5944
- } catch (e) {
5945
- if (e && e.name === 'N3SyntaxError') {
5946
- console.error(formatN3SyntaxError(e, messageText, `${messageSourceLabel}#message-${messageIndex}`));
5947
- process.exit(1);
6031
+ let messageDoc;
6032
+ try {
6033
+ messageDoc = parseN3Text(messageText, {
6034
+ baseIri: `${__sourceLabelToBaseIri(messageSourceLabel)}#message-${messageIndex}`,
6035
+ label: `${messageSourceLabel}#message-${messageIndex}`,
6036
+ keepSourceArtifacts: false,
6037
+ sourceLocations: false,
6038
+ rdf: false,
6039
+ });
6040
+ } catch (e) {
6041
+ if (e && e.name === 'N3SyntaxError') {
6042
+ console.error(formatN3SyntaxError(e, messageText, `${messageSourceLabel}#message-${messageIndex}`));
6043
+ process.exit(1);
6044
+ }
6045
+ throw e;
5948
6046
  }
5949
- throw e;
5950
- }
5951
6047
 
5952
- const merged = mergeParsedDocuments(programSources.concat([messageDoc]));
5953
- runParsedDocumentOnce(merged, { rdfMode, outputPrefixes: fullIriPrefixes });
5954
- });
5955
- } catch (e) {
5956
- console.error(`Error streaming RDF Message Log ${JSON.stringify(messageSourceLabel)}: ${e && e.message ? e.message : String(e)}`);
5957
- process.exit(1);
6048
+ const merged = mergeParsedDocuments(programSources.concat([messageDoc]));
6049
+ const result = runParsedDocumentOnce(merged, { rdfMode, outputPrefixes: fullIriPrefixes });
6050
+ if (store) pendingStoreWrites = pendingStoreWrites.then(() => persistRunResultToStore(store, result));
6051
+ });
6052
+ } catch (e) {
6053
+ console.error(`Error streaming RDF Message Log ${JSON.stringify(messageSourceLabel)}: ${e && e.message ? e.message : String(e)}`);
6054
+ process.exit(1);
6055
+ }
5958
6056
  }
6057
+ await pendingStoreWrites;
6058
+ } finally {
6059
+ if (store && typeof store.close === 'function') await store.close();
5959
6060
  }
5960
6061
  }
5961
6062
 
@@ -5990,6 +6091,9 @@ async function main() {
5990
6091
  ` -p, --proof Enable proof explanations.\n` +
5991
6092
  ` -r, --rdf Enable RDF/TriG input/output compatibility.\n` +
5992
6093
  ` --stream-messages Process RDF Message Logs one message at a time under -r.\n` +
6094
+ ` --store <name> Use an optional persistent fact store.\n` +
6095
+ ` --store-clear Clear the named store before this run.\n` +
6096
+ ` --store-path <dir> Node.js persistent store directory.\n` +
5993
6097
  ` -s, --super-restricted Disable all builtins except => and <=.\n` +
5994
6098
  ` -t, --stream Stream derived triples as soon as they are derived.\n` +
5995
6099
  ` -v, --version Print version and exit.\n`;
@@ -6026,6 +6130,35 @@ async function main() {
6026
6130
  builtinModules.push(a.slice('--builtin='.length));
6027
6131
  continue;
6028
6132
  }
6133
+ if (a === '--store') {
6134
+ const next = argv[i + 1];
6135
+ if (!next || next.startsWith('-')) {
6136
+ console.error('Error: --store expects a store name.');
6137
+ process.exit(1);
6138
+ }
6139
+ argv.__storeName = next;
6140
+ i += 1;
6141
+ continue;
6142
+ }
6143
+ if (typeof a === 'string' && a.startsWith('--store=')) {
6144
+ argv.__storeName = a.slice('--store='.length);
6145
+ continue;
6146
+ }
6147
+ if (a === '--store-path') {
6148
+ const next = argv[i + 1];
6149
+ if (!next || next.startsWith('-')) {
6150
+ console.error('Error: --store-path expects a directory path.');
6151
+ process.exit(1);
6152
+ }
6153
+ argv.__storePath = next;
6154
+ i += 1;
6155
+ continue;
6156
+ }
6157
+ if (typeof a === 'string' && a.startsWith('--store-path=')) {
6158
+ argv.__storePath = a.slice('--store-path='.length);
6159
+ continue;
6160
+ }
6161
+ if (a === '--store-clear') continue;
6029
6162
  if (a === '-' || !a.startsWith('-')) positional.push(a);
6030
6163
  }
6031
6164
 
@@ -6033,6 +6166,9 @@ async function main() {
6033
6166
  const streamMode = argv.includes('--stream') || argv.includes('-t');
6034
6167
  const streamMessagesMode = argv.includes('--stream-messages');
6035
6168
  const rdfMode = argv.includes('--rdf') || argv.includes('-r');
6169
+ const storeName = argv.__storeName || null;
6170
+ const storePath = argv.__storePath || null;
6171
+ const storeClear = argv.includes('--store-clear');
6036
6172
 
6037
6173
  // --enforce-https: rewrite http:// -> https:// for log dereferencing builtins
6038
6174
  if (argv.includes('--enforce-https') || argv.includes('-e')) {
@@ -6091,17 +6227,94 @@ async function main() {
6091
6227
  }
6092
6228
  }
6093
6229
 
6094
- const sourceLabels = useImplicitStdin ? ['<stdin>'] : positional.map((item) => (item === '-' ? '<stdin>' : item));
6230
+ let sourceLabels = useImplicitStdin ? ['<stdin>'] : positional.map((item) => (item === '-' ? '<stdin>' : item));
6095
6231
  if (sourceLabels.filter((item) => item === '<stdin>').length > 1) {
6096
6232
  console.error('Error: stdin can only be used once.');
6097
6233
  process.exit(1);
6098
6234
  }
6099
6235
 
6100
6236
  if (streamMessagesMode) {
6101
- await runStreamMessagesMode(sourceLabels, { rdfMode });
6237
+ await runStreamMessagesMode(sourceLabels, { rdfMode, storeName, storePath, storeClear });
6102
6238
  return;
6103
6239
  }
6104
6240
 
6241
+ if (storeName && rdfMode) {
6242
+ const lineBasedSources = sourceLabels.filter(sourceLooksLikeLineBasedRdf);
6243
+ if (lineBasedSources.length) {
6244
+ if (showAst) {
6245
+ console.error('Error: line-based --store ingestion cannot be combined with --ast.');
6246
+ process.exit(1);
6247
+ }
6248
+ if (streamMode) {
6249
+ console.error('Error: line-based --store ingestion cannot be combined with --stream.');
6250
+ process.exit(1);
6251
+ }
6252
+ if (engine.getProofCommentsEnabled()) {
6253
+ console.error('Error: line-based --store ingestion currently does not support proof output.');
6254
+ process.exit(1);
6255
+ }
6256
+
6257
+ const store = await createCliStore({ storeName, storePath, storeClear });
6258
+ try {
6259
+ for (const sourceLabel of lineBasedSources) {
6260
+ try {
6261
+ await ingestLineBasedRdfSourceToStore(sourceLabel, store, { rdfMode });
6262
+ } catch (e) {
6263
+ console.error(`Error streaming RDF source ${JSON.stringify(sourceLabel)}: ${e && e.message ? e.message : String(e)}`);
6264
+ process.exit(1);
6265
+ }
6266
+ }
6267
+
6268
+ sourceLabels = sourceLabels.filter((sourceLabel) => !sourceLooksLikeLineBasedRdf(sourceLabel));
6269
+ if (!sourceLabels.length) return;
6270
+
6271
+ const parsedRuleSources = [];
6272
+ for (const sourceLabel of sourceLabels) {
6273
+ let text;
6274
+ try {
6275
+ text = __readInputSourceSync(sourceLabel);
6276
+ } catch (e) {
6277
+ if (sourceLabel === '<stdin>') console.error(`Error reading stdin: ${e.message}`);
6278
+ else console.error(`Error reading source ${JSON.stringify(sourceLabel)}: ${e.message}`);
6279
+ process.exit(1);
6280
+ }
6281
+
6282
+ try {
6283
+ parsedRuleSources.push(
6284
+ parseN3Text(text, {
6285
+ baseIri: __sourceLabelToBaseIri(sourceLabel),
6286
+ label: sourceLabel,
6287
+ collectUsedPrefixes: false,
6288
+ keepSourceArtifacts: false,
6289
+ sourceLocations: false,
6290
+ rdf: rdfMode,
6291
+ }),
6292
+ );
6293
+ } catch (e) {
6294
+ if (e && e.name === 'N3SyntaxError') {
6295
+ console.error(formatN3SyntaxError(e, text, sourceLabel));
6296
+ process.exit(1);
6297
+ }
6298
+ throw e;
6299
+ }
6300
+ }
6301
+
6302
+ const mergedRuleDocument = mergeParsedDocuments(parsedRuleSources);
6303
+ const result = await engine.runStoreBacked(mergedRuleDocument, store, { rdf: rdfMode });
6304
+ const outTriples = result.queryMode ? result.queryTriples || [] : (result.derived || []).map((df) => df.fact);
6305
+ if (result.queryMode) {
6306
+ const bodyText = engine.prettyPrintQueryTriples(outTriples, result.prefixes);
6307
+ if (bodyText) process.stdout.write(String(bodyText).replace(/\s*$/g, '') + '\n');
6308
+ } else {
6309
+ for (const tr of outTriples) console.log(engine.tripleToRdfCompatible(tr, result.prefixes));
6310
+ }
6311
+ return;
6312
+ } finally {
6313
+ if (store && typeof store.close === 'function') await store.close();
6314
+ }
6315
+ }
6316
+ }
6317
+
6105
6318
  const parsedSources = [];
6106
6319
  for (const sourceLabel of sourceLabels) {
6107
6320
  let text;
@@ -6211,6 +6424,69 @@ function factsContainOutputStrings(triplesForOutput) {
6211
6424
  const hasQueries = Array.isArray(qrules) && qrules.length;
6212
6425
  const mayAutoRenderOutputStrings = programMayProduceOutputStrings(triples, frules, qrules);
6213
6426
 
6427
+ if (storeName) {
6428
+ if (streamMode) {
6429
+ console.error('Error: --store cannot be combined with --stream yet.');
6430
+ process.exit(1);
6431
+ }
6432
+
6433
+ const storeResult = await engine.runAsync(
6434
+ { prefixes, triples, frules, brules, logQueryRules: qrules },
6435
+ {
6436
+ proof: engine.getProofCommentsEnabled(),
6437
+ rdf: rdfMode,
6438
+ store: { name: storeName, clear: storeClear, path: storePath || undefined },
6439
+ },
6440
+ );
6441
+
6442
+ const storeFacts = storeResult.facts || [];
6443
+ const storeDerived = storeResult.derived || [];
6444
+ const storeHasQueries = !!storeResult.queryMode;
6445
+ const storeOutTriples = storeHasQueries ? (storeResult.queryTriples || []) : (mayAutoRenderOutputStrings && !engine.getProofCommentsEnabled() ? [] : storeDerived.map((df) => df.fact));
6446
+ const storeOutDerived = storeHasQueries ? (storeResult.queryDerived || []) : storeDerived;
6447
+ const renderedOutputTriples = storeHasQueries ? storeOutTriples : storeFacts;
6448
+
6449
+ if (factsContainOutputStrings(renderedOutputTriples)) {
6450
+ process.stdout.write(engine.collectOutputStringsFromFacts(renderedOutputTriples, prefixes));
6451
+ if (storeResult.store && typeof storeResult.store.close === 'function') await storeResult.store.close();
6452
+ return;
6453
+ }
6454
+
6455
+ if (engine.getProofCommentsEnabled()) {
6456
+ process.stdout.write(engine.renderProofDocument(storeOutDerived, storeDerived.concat(storeOutDerived || []), triples, prefixes, brules));
6457
+ if (storeResult.store && typeof storeResult.store.close === 'function') await storeResult.store.close();
6458
+ return;
6459
+ }
6460
+
6461
+ let bodyText = '';
6462
+ if (rdfMode) bodyText = storeOutTriples.map((tr) => engine.tripleToRdfCompatible(tr, prefixes)).join('\n');
6463
+ else if (storeHasQueries) bodyText = engine.prettyPrintQueryTriples(storeOutTriples, prefixes);
6464
+
6465
+ let usedPrefixes = prefixes.prefixesUsedForOutput(storeOutTriples);
6466
+ if (rdfMode && bodyText) usedPrefixes = usedPrefixes.filter(([pfx]) => pfx === '' || bodyText.includes(pfx + ':'));
6467
+
6468
+ if (rdfMode && bodyText.includes('<<(')) {
6469
+ console.log('VERSION "1.2"');
6470
+ console.log();
6471
+ }
6472
+
6473
+ for (const [pfx, base] of usedPrefixes) {
6474
+ if (pfx === '') console.log(`@prefix : <${base}> .`);
6475
+ else console.log(`@prefix ${pfx}: <${base}> .`);
6476
+ }
6477
+ if (storeOutTriples.length && usedPrefixes.length) console.log();
6478
+
6479
+ if (bodyText) process.stdout.write(String(bodyText).replace(/\s*$/g, '') + '\n');
6480
+ else {
6481
+ for (const df of storeOutDerived) {
6482
+ console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, prefixes) : engine.tripleToN3(df.fact, prefixes));
6483
+ }
6484
+ }
6485
+
6486
+ if (storeResult.store && typeof storeResult.store.close === 'function') await storeResult.store.close();
6487
+ return;
6488
+ }
6489
+
6214
6490
  if (streamMode && !hasQueries && !mayAutoRenderOutputStrings && !engine.getProofCommentsEnabled()) {
6215
6491
  const usedInInput = mergedDocument.usedPrefixes instanceof Set ? new Set(mergedDocument.usedPrefixes) : new Set();
6216
6492
  const outPrefixes = restrictPrefixEnv(prefixes, usedInInput);
@@ -6886,6 +7162,7 @@ const {
6886
7162
  getDataFactory,
6887
7163
  internalTripleToRdfJsQuads,
6888
7164
  normalizeParsedReasonerInputSync,
7165
+ normalizeParsedReasonerInputAsync,
6889
7166
  normalizeReasonerInputSync,
6890
7167
  normalizeReasonerInputAsync,
6891
7168
  } = require('./rdfjs');
@@ -6894,6 +7171,7 @@ const trace = require('./trace');
6894
7171
  const { deterministicSkolemIdFromKey } = require('./skolem');
6895
7172
 
6896
7173
  const deref = require('./deref');
7174
+ const { createFactStore, collectStore, MemoryFactStore, PersistentFactStore, tripleToStoreKey } = require('./store');
6897
7175
 
6898
7176
  const hasOwn = Object.prototype.hasOwnProperty;
6899
7177
 
@@ -10572,6 +10850,278 @@ function reasonStream(input, opts = {}) {
10572
10850
  return __out;
10573
10851
  }
10574
10852
 
10853
+
10854
+ async function __parseRunAsyncInput(input, opts) {
10855
+ const {
10856
+ baseIri = null,
10857
+ proof = false,
10858
+ rdf = false,
10859
+ sourceLabel = '<input>',
10860
+ } = opts || {};
10861
+
10862
+ const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: !!rdf, sourceLocations: !!proof });
10863
+ if (parsedSourceList) return parsedSourceList;
10864
+
10865
+ const parsedObject = await normalizeParsedReasonerInputAsync(input);
10866
+ if (parsedObject) {
10867
+ if (baseIri && parsedObject.prefixes && typeof parsedObject.prefixes.setBase === 'function') parsedObject.prefixes.setBase(baseIri);
10868
+ return parsedObject;
10869
+ }
10870
+
10871
+ const n3Text = await normalizeReasonerInputAsync(input);
10872
+ return parseN3Text(n3Text, {
10873
+ baseIri: baseIri || '',
10874
+ label: sourceLabel || '<input>',
10875
+ keepSourceArtifacts: false,
10876
+ sourceLocations: !!proof,
10877
+ rdf: !!rdf,
10878
+ });
10879
+ }
10880
+
10881
+ function __storePatternTerm(t, subst) {
10882
+ const applied = applySubstTerm(t, subst || __emptySubst());
10883
+ return containsVarTerm(applied) ? null : applied;
10884
+ }
10885
+
10886
+ function __mergeStoreSubst(base, delta) {
10887
+ let out = base;
10888
+ for (const k of Object.keys(delta || {})) {
10889
+ const v = delta[k];
10890
+ if (Object.prototype.hasOwnProperty.call(out, k)) {
10891
+ if (!termsEqual(out[k], v)) return null;
10892
+ } else {
10893
+ if (out === base) out = __cloneSubst(base);
10894
+ out[k] = v;
10895
+ }
10896
+ }
10897
+ return out;
10898
+ }
10899
+
10900
+ async function __proveGoalsAgainstStore(goals, subst, store, backRules, localFacts, depth, varGen, opts = {}) {
10901
+ if (!Array.isArray(goals) || goals.length === 0) return [subst || __emptySubst()];
10902
+ if (depth > 64) return [];
10903
+
10904
+ const goal0 = applySubstTriple(goals[0], subst || __emptySubst());
10905
+ const rest = goals.slice(1);
10906
+ const out = [];
10907
+
10908
+ if (isBuiltinPred(goal0.p)) {
10909
+ const deltas = evalBuiltin(goal0, __emptySubst(), localFacts || [], backRules || [], depth, varGen, undefined);
10910
+ for (const delta of deltas) {
10911
+ const nextSubst = __mergeStoreSubst(subst || __emptySubst(), delta);
10912
+ if (nextSubst === null) continue;
10913
+ out.push(...await __proveGoalsAgainstStore(rest, nextSubst, store, backRules, localFacts, depth + 1, varGen, opts));
10914
+ if (opts.maxResults && out.length >= opts.maxResults) return out.slice(0, opts.maxResults);
10915
+ }
10916
+ return out;
10917
+ }
10918
+
10919
+ // Backward rules are kept in memory; they may call back into the persistent
10920
+ // fact store for their own premises.
10921
+ if (goal0.p instanceof Iri && Array.isArray(backRules) && backRules.length) {
10922
+ ensureBackRuleIndexes(backRules);
10923
+ const candRules = (backRules.__byHeadPred.get(goal0.p.__tid) || []).concat(backRules.__wildHeadPred || []);
10924
+ for (const r of candRules) {
10925
+ if (!r || !Array.isArray(r.conclusion) || r.conclusion.length !== 1) continue;
10926
+ const rStd = standardizeRule(r, varGen);
10927
+ const head = rStd.conclusion[0];
10928
+ const s2 = unifyTriple(head, goal0, subst || __emptySubst());
10929
+ if (s2 === null) continue;
10930
+ const newGoals = (rStd.premise || []).concat(rest);
10931
+ out.push(...await __proveGoalsAgainstStore(newGoals, s2, store, backRules, localFacts, depth + 1, varGen, opts));
10932
+ if (opts.maxResults && out.length >= opts.maxResults) return out.slice(0, opts.maxResults);
10933
+ }
10934
+ }
10935
+
10936
+ const sPat = __storePatternTerm(goal0.s, subst || __emptySubst());
10937
+ const pPat = __storePatternTerm(goal0.p, subst || __emptySubst());
10938
+ const oPat = __storePatternTerm(goal0.o, subst || __emptySubst());
10939
+
10940
+ for await (const fact of store.match(sPat, pPat, oPat)) {
10941
+ const s2 = unifyTriple(goal0, fact, subst || __emptySubst());
10942
+ if (s2 === null) continue;
10943
+ out.push(...await __proveGoalsAgainstStore(rest, s2, store, backRules, localFacts, depth + 1, varGen, opts));
10944
+ if (opts.maxResults && out.length >= opts.maxResults) return out.slice(0, opts.maxResults);
10945
+ }
10946
+
10947
+ return out;
10948
+ }
10949
+
10950
+ async function runStoreBacked(input, store, opts = {}) {
10951
+ const parsed = parseN3SourceList(input, {
10952
+ baseIri: opts.baseIri || null,
10953
+ rdf: !!opts.rdf,
10954
+ sourceLocations: !!opts.proof,
10955
+ }) || input;
10956
+
10957
+ const prefixes = parsed.prefixes;
10958
+ const triples = parsed.triples || [];
10959
+ const frules = parsed.frules || [];
10960
+ const brules = parsed.brules || [];
10961
+ const qrules = parsed.logQueryRules || [];
10962
+
10963
+ materializeRdfLists(triples, frules.concat(qrules || []), brules);
10964
+ await store.batchAdd(triples, 'explicit');
10965
+
10966
+ const derived = [];
10967
+ const derivedKeys = new Set();
10968
+ const varGen = [0];
10969
+ const maxIterations = Number.isInteger(opts.maxIterations) && opts.maxIterations > 0 ? opts.maxIterations : 1024;
10970
+
10971
+ for (let iteration = 0; iteration < maxIterations; iteration += 1) {
10972
+ let changed = false;
10973
+ for (let ruleIndex = 0; ruleIndex < frules.length; ruleIndex += 1) {
10974
+ const r = frules[ruleIndex];
10975
+ if (!r || r.isFuse || r.__dynamicConclusionTerm) continue;
10976
+ __prepareForwardRule(r);
10977
+ const solutions = await __proveGoalsAgainstStore(r.premise || [], __emptySubst(), store, brules, triples, 0, varGen, {});
10978
+ for (const subst of solutions) {
10979
+ for (const cpat of r.conclusion || []) {
10980
+ const fact = applySubstTriple(cpat, subst);
10981
+ if (!isGroundTriple(fact)) continue;
10982
+ if (await store.add(fact, 'inferred')) {
10983
+ const df = makeDerivedRecord(fact, r, (r.premise || []).map((p) => applySubstTriple(p, subst)), subst, false);
10984
+ const key = tripleToStoreKey(fact);
10985
+ if (!derivedKeys.has(key)) {
10986
+ derivedKeys.add(key);
10987
+ derived.push(df);
10988
+ }
10989
+ changed = true;
10990
+ }
10991
+ }
10992
+ }
10993
+ }
10994
+ if (!changed) break;
10995
+ }
10996
+
10997
+ let queryTriples = [];
10998
+ const queryDerived = [];
10999
+ if (qrules.length) {
11000
+ for (const r of qrules) {
11001
+ const solutions = await __proveGoalsAgainstStore(r.premise || [], __emptySubst(), store, brules, triples, 0, varGen, {});
11002
+ for (const subst of solutions) {
11003
+ for (const cpat of r.conclusion || []) {
11004
+ const fact = applySubstTriple(cpat, subst);
11005
+ if (!isGroundTriple(fact)) continue;
11006
+ queryTriples.push(fact);
11007
+ queryDerived.push(makeDerivedRecord(fact, r, (r.premise || []).map((p) => applySubstTriple(p, subst)), subst, false));
11008
+ }
11009
+ }
11010
+ }
11011
+ }
11012
+
11013
+ if (queryTriples.length) {
11014
+ const seen = new Set();
11015
+ queryTriples = queryTriples.filter((tr) => {
11016
+ const key = tripleToStoreKey(tr);
11017
+ if (seen.has(key)) return false;
11018
+ seen.add(key);
11019
+ return true;
11020
+ });
11021
+ }
11022
+
11023
+ return {
11024
+ prefixes,
11025
+ facts: [],
11026
+ derived,
11027
+ queryMode: !!qrules.length,
11028
+ queryTriples,
11029
+ queryDerived,
11030
+ closureN3: qrules.length
11031
+ ? prettyPrintQueryTriples(queryTriples, prefixes)
11032
+ : derived.map((df) => (opts.rdf ? tripleToRdfCompatible(df.fact, prefixes) : tripleToN3(df.fact, prefixes))).join('\n'),
11033
+ store,
11034
+ storeBacked: true,
11035
+ };
11036
+ }
11037
+
11038
+
11039
+ function __withoutStoreOptions(opts) {
11040
+ const out = { ...(opts || {}) };
11041
+ delete out.store;
11042
+ delete out.storePath;
11043
+ delete out.storeClear;
11044
+ return out;
11045
+ }
11046
+
11047
+ function __normalizeStoreOption(opts) {
11048
+ const cfg = opts && opts.store;
11049
+ if (!cfg) return null;
11050
+ if (typeof cfg === 'string') return { name: cfg, path: opts.storePath || undefined, clear: !!opts.storeClear };
11051
+ if (typeof cfg === 'object') {
11052
+ return {
11053
+ ...cfg,
11054
+ path: cfg.path || opts.storePath || undefined,
11055
+ clear: !!(cfg.clear || opts.storeClear),
11056
+ };
11057
+ }
11058
+ throw new TypeError('runAsync option "store" must be a store name string or a store options object');
11059
+ }
11060
+
11061
+ async function runAsync(input, opts = {}) {
11062
+ const storeConfig = __normalizeStoreOption(opts);
11063
+ const runOpts = __withoutStoreOptions(opts);
11064
+
11065
+ // No store configured: keep ordinary in-memory behavior, but accept async RDF/JS
11066
+ // iterables by normalizing the input before calling the synchronous engine.
11067
+ if (!storeConfig) {
11068
+ const normalizedInput = parseN3SourceList(input, {
11069
+ baseIri: runOpts.baseIri || null,
11070
+ rdf: !!runOpts.rdf,
11071
+ sourceLocations: !!runOpts.proof,
11072
+ }) || (await normalizeReasonerInputAsync(input));
11073
+ return reasonStream(normalizedInput, runOpts);
11074
+ }
11075
+
11076
+ const store = await createFactStore(storeConfig);
11077
+ let closeStore = true;
11078
+ try {
11079
+ const parsed = await __parseRunAsyncInput(input, runOpts);
11080
+
11081
+ // Persist the newly supplied explicit facts first, then include all stored
11082
+ // facts as the starting closure for this run. This lets --store reuse facts
11083
+ // across runs while keeping the current in-memory prover semantics intact.
11084
+ await store.batchAdd(parsed.triples || [], 'explicit');
11085
+ const storedTriples = await collectStore(store);
11086
+
11087
+ const storedKeys = new Set();
11088
+ for (const tr of storedTriples) storedKeys.add(require('./store').tripleToStoreKey(tr));
11089
+ const mergedTriples = storedTriples.slice();
11090
+ for (const tr of parsed.triples || []) {
11091
+ const key = require('./store').tripleToStoreKey(tr);
11092
+ if (!storedKeys.has(key)) {
11093
+ storedKeys.add(key);
11094
+ mergedTriples.push(tr);
11095
+ }
11096
+ }
11097
+
11098
+ const result = reasonStream(
11099
+ {
11100
+ prefixes: parsed.prefixes,
11101
+ triples: mergedTriples,
11102
+ frules: parsed.frules,
11103
+ brules: parsed.brules,
11104
+ logQueryRules: parsed.logQueryRules,
11105
+ },
11106
+ runOpts,
11107
+ );
11108
+
11109
+ await store.batchAdd((result.derived || []).map((df) => df.fact), 'inferred');
11110
+ result.store = store;
11111
+ closeStore = false;
11112
+ return result;
11113
+ } catch (e) {
11114
+ await store.close();
11115
+ throw e;
11116
+ } finally {
11117
+ // Keep result.store usable for callers. On exceptional paths it is closed
11118
+ // above; on success callers may close it when they are done.
11119
+ if (closeStore) {
11120
+ try { await store.close(); } catch {}
11121
+ }
11122
+ }
11123
+ }
11124
+
10575
11125
  function reasonRdfJs(input, opts = {}) {
10576
11126
  const { dataFactory = null, skipUnsupportedRdfJs = false, ...restOpts } = opts || {};
10577
11127
  const rdfFactory = getDataFactory(dataFactory);
@@ -10679,6 +11229,8 @@ function setTracePrefixes(v) {
10679
11229
 
10680
11230
  module.exports = {
10681
11231
  reasonStream,
11232
+ runAsync,
11233
+ runStoreBacked,
10682
11234
  reasonRdfJs,
10683
11235
  collectLogQueryConclusions,
10684
11236
  forwardChainAndCollectLogQueryConclusions,
@@ -10715,6 +11267,9 @@ module.exports = {
10715
11267
  loadBuiltinModule,
10716
11268
  listBuiltinIris,
10717
11269
  INFERENCE_FUSE_EXIT_CODE,
11270
+ createFactStore,
11271
+ MemoryFactStore,
11272
+ PersistentFactStore,
10718
11273
  };
10719
11274
 
10720
11275
  };
@@ -10738,6 +11293,7 @@ const { dataFactory } = require('./rdfjs');
10738
11293
  module.exports = {
10739
11294
  // public
10740
11295
  reasonStream: engine.reasonStream,
11296
+ runAsync: engine.runAsync,
10741
11297
  reasonRdfJs: engine.reasonRdfJs,
10742
11298
  rdfjs: dataFactory,
10743
11299
  main: engine.main,
@@ -10762,6 +11318,9 @@ module.exports = {
10762
11318
  registerBuiltinModule: engine.registerBuiltinModule,
10763
11319
  loadBuiltinModule: engine.loadBuiltinModule,
10764
11320
  listBuiltinIris: engine.listBuiltinIris,
11321
+ createFactStore: engine.createFactStore,
11322
+ MemoryFactStore: engine.MemoryFactStore,
11323
+ PersistentFactStore: engine.PersistentFactStore,
10765
11324
  getEnforceHttpsEnabled: engine.getEnforceHttpsEnabled,
10766
11325
  setEnforceHttpsEnabled: engine.setEnforceHttpsEnabled,
10767
11326
  getProofCommentsEnabled: engine.getProofCommentsEnabled,
@@ -17050,6 +17609,7 @@ module.exports = {
17050
17609
  internalTripleToRdfJsQuads,
17051
17610
  internalTripleToRdfJsQuadInGraph,
17052
17611
  normalizeParsedReasonerInputSync,
17612
+ normalizeParsedReasonerInputAsync,
17053
17613
  normalizeReasonerInputSync,
17054
17614
  normalizeReasonerInputAsync,
17055
17615
  hasEyelingObjectInput,
@@ -17215,6 +17775,697 @@ module.exports = {
17215
17775
  deterministicSkolemIdFromKey,
17216
17776
  };
17217
17777
 
17778
+ };
17779
+ __modules["lib/store.js"] = function(require, module, exports){
17780
+ /**
17781
+ * Eyeling Reasoner — fact stores
17782
+ *
17783
+ * Optional async fact-store abstraction used by runAsync() and by tests. The
17784
+ * in-memory reasoner remains the default; persistent stores are opt-in.
17785
+ */
17786
+
17787
+ 'use strict';
17788
+
17789
+ const {
17790
+ Iri,
17791
+ Literal,
17792
+ Var,
17793
+ Blank,
17794
+ ListTerm,
17795
+ OpenListTerm,
17796
+ GraphTerm,
17797
+ Triple,
17798
+ } = require('./prelude');
17799
+
17800
+ const KIND_EXPLICIT = 1;
17801
+ const KIND_INFERRED = 2;
17802
+
17803
+ function __dynamicRequire(name) {
17804
+ try {
17805
+ // Keep this indirect so the browser bundle can include this module without
17806
+ // eagerly trying to bundle Node-only or optional Level dependencies.
17807
+ const req = typeof require === 'function' ? require : null;
17808
+ return req ? req(name) : null;
17809
+ } catch {
17810
+ return null;
17811
+ }
17812
+ }
17813
+
17814
+ function base64url(s) {
17815
+ const text = String(s);
17816
+ if (typeof Buffer !== 'undefined') return Buffer.from(text, 'utf8').toString('base64url');
17817
+ const encoder = globalThis.TextEncoder ? new globalThis.TextEncoder() : null;
17818
+ if (!encoder || typeof globalThis.btoa !== 'function') {
17819
+ throw new Error('No UTF-8/base64 encoder available for persistent store keys');
17820
+ }
17821
+ const bytes = encoder.encode(text);
17822
+ let bin = '';
17823
+ for (const b of bytes) bin += String.fromCharCode(b);
17824
+ return globalThis.btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
17825
+ }
17826
+
17827
+ function idPart(id) {
17828
+ return String(id).padStart(16, '0');
17829
+ }
17830
+
17831
+ function safeStoreName(name) {
17832
+ const text = String(name || 'default');
17833
+ return encodeURIComponent(text).replace(/%/g, '_');
17834
+ }
17835
+
17836
+ function termToJson(term) {
17837
+ if (term instanceof Iri) return ['Iri', term.value];
17838
+ if (term instanceof Literal) return ['Literal', term.value];
17839
+ if (term instanceof Var) return ['Var', term.name];
17840
+ if (term instanceof Blank) return ['Blank', term.label];
17841
+ if (term instanceof ListTerm) return ['ListTerm', term.elems.map(termToJson)];
17842
+ if (term instanceof OpenListTerm) return ['OpenListTerm', term.prefix.map(termToJson), term.tailVar];
17843
+ if (term instanceof GraphTerm) return ['GraphTerm', term.triples.map(tripleToJson)];
17844
+
17845
+ // RDF/JS terms, for callers that use the store abstraction directly.
17846
+ if (term && typeof term === 'object' && typeof term.termType === 'string') {
17847
+ switch (term.termType) {
17848
+ case 'NamedNode':
17849
+ return ['Iri', term.value];
17850
+ case 'BlankNode':
17851
+ return ['Blank', term.value && term.value.startsWith('_:') ? term.value : `_:${term.value}`];
17852
+ case 'Variable':
17853
+ return ['Var', term.value];
17854
+ case 'Literal': {
17855
+ const dt = term.datatype && term.datatype.termType === 'NamedNode' ? term.datatype.value : '';
17856
+ const lang = typeof term.language === 'string' ? term.language : '';
17857
+ const q = JSON.stringify(String(term.value));
17858
+ if (lang) return ['Literal', `${q}@${lang}`];
17859
+ if (!dt || dt === 'http://www.w3.org/2001/XMLSchema#string') return ['Literal', q];
17860
+ return ['Literal', `${q}^^<${dt}>`];
17861
+ }
17862
+ case 'Quad':
17863
+ return ['GraphTerm', [tripleToJson({ s: term.subject, p: term.predicate, o: term.object })]];
17864
+ case 'DefaultGraph':
17865
+ return ['DefaultGraph'];
17866
+ default:
17867
+ break;
17868
+ }
17869
+ }
17870
+
17871
+ throw new TypeError(`Unsupported fact-store term: ${term && term.constructor ? term.constructor.name : String(term)}`);
17872
+ }
17873
+
17874
+ function termFromJson(json) {
17875
+ if (!Array.isArray(json)) throw new TypeError('Invalid serialized term');
17876
+ switch (json[0]) {
17877
+ case 'Iri':
17878
+ return new Iri(json[1]);
17879
+ case 'Literal':
17880
+ return new Literal(json[1]);
17881
+ case 'Var':
17882
+ return new Var(json[1]);
17883
+ case 'Blank':
17884
+ return new Blank(json[1]);
17885
+ case 'ListTerm':
17886
+ return new ListTerm((json[1] || []).map(termFromJson));
17887
+ case 'OpenListTerm':
17888
+ return new OpenListTerm((json[1] || []).map(termFromJson), json[2]);
17889
+ case 'GraphTerm':
17890
+ return new GraphTerm((json[1] || []).map(tripleFromJson));
17891
+ case 'DefaultGraph':
17892
+ return new Iri('urn:eyeling:default-graph');
17893
+ default:
17894
+ throw new TypeError(`Unsupported serialized term type: ${json[0]}`);
17895
+ }
17896
+ }
17897
+
17898
+ function tripleToJson(triple) {
17899
+ return [termToJson(triple.s || triple.subject), termToJson(triple.p || triple.predicate), termToJson(triple.o || triple.object)];
17900
+ }
17901
+
17902
+ function tripleFromJson(json) {
17903
+ return new Triple(termFromJson(json[0]), termFromJson(json[1]), termFromJson(json[2]));
17904
+ }
17905
+
17906
+ function termToStoreKey(term) {
17907
+ return JSON.stringify(termToJson(term));
17908
+ }
17909
+
17910
+ function tripleToStoreKey(triple) {
17911
+ return JSON.stringify(tripleToJson(triple));
17912
+ }
17913
+
17914
+ function kindMask(kind) {
17915
+ if (kind === 'explicit') return KIND_EXPLICIT;
17916
+ if (kind === 'inferred') return KIND_INFERRED;
17917
+ if (typeof kind === 'number') return kind & (KIND_EXPLICIT | KIND_INFERRED);
17918
+ return KIND_EXPLICIT;
17919
+ }
17920
+
17921
+ class MemoryKv {
17922
+ constructor() {
17923
+ this.map = new Map();
17924
+ }
17925
+
17926
+ async get(key) {
17927
+ return this.map.get(key);
17928
+ }
17929
+
17930
+ async put(key, value) {
17931
+ this.map.set(key, value);
17932
+ }
17933
+
17934
+ async del(key) {
17935
+ this.map.delete(key);
17936
+ }
17937
+
17938
+ async clear() {
17939
+ this.map.clear();
17940
+ }
17941
+
17942
+ async batch(ops) {
17943
+ for (const op of ops || []) {
17944
+ if (!op) continue;
17945
+ if (op.type === 'del') this.map.delete(op.key);
17946
+ else this.map.set(op.key, op.value);
17947
+ }
17948
+ }
17949
+
17950
+ async *entries(prefix = '') {
17951
+ const keys = Array.from(this.map.keys())
17952
+ .filter((key) => key.startsWith(prefix))
17953
+ .sort();
17954
+ for (const key of keys) yield [key, this.map.get(key)];
17955
+ }
17956
+
17957
+ async close() {}
17958
+ }
17959
+
17960
+ class JsonFileKv extends MemoryKv {
17961
+ constructor(filePath) {
17962
+ super();
17963
+ this.filePath = filePath;
17964
+ this.loaded = false;
17965
+ this.dirty = false;
17966
+ }
17967
+
17968
+ async open() {
17969
+ if (this.loaded) return;
17970
+ this.loaded = true;
17971
+ const fs = __dynamicRequire('node:fs');
17972
+ const path = __dynamicRequire('node:path');
17973
+ if (!fs || !path) throw new Error('Persistent stores need node:fs in this runtime');
17974
+ fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
17975
+ if (!fs.existsSync(this.filePath)) return;
17976
+ const text = fs.readFileSync(this.filePath, 'utf8');
17977
+ if (!text.trim()) return;
17978
+ const data = JSON.parse(text);
17979
+ this.map = new Map(Array.isArray(data.entries) ? data.entries : []);
17980
+ }
17981
+
17982
+ async flush() {
17983
+ if (!this.loaded || !this.dirty) return;
17984
+ const fs = __dynamicRequire('node:fs');
17985
+ const path = __dynamicRequire('node:path');
17986
+ fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
17987
+ const tmp = `${this.filePath}.tmp`;
17988
+ fs.writeFileSync(tmp, JSON.stringify({ entries: Array.from(this.map.entries()) }), 'utf8');
17989
+ fs.renameSync(tmp, this.filePath);
17990
+ this.dirty = false;
17991
+ }
17992
+
17993
+ async get(key) {
17994
+ await this.open();
17995
+ return super.get(key);
17996
+ }
17997
+
17998
+ async put(key, value) {
17999
+ await this.open();
18000
+ await super.put(key, value);
18001
+ this.dirty = true;
18002
+ await this.flush();
18003
+ }
18004
+
18005
+ async del(key) {
18006
+ await this.open();
18007
+ await super.del(key);
18008
+ this.dirty = true;
18009
+ await this.flush();
18010
+ }
18011
+
18012
+ async clear() {
18013
+ await this.open();
18014
+ await super.clear();
18015
+ this.dirty = true;
18016
+ await this.flush();
18017
+ }
18018
+
18019
+ async batch(ops) {
18020
+ await this.open();
18021
+ await super.batch(ops);
18022
+ this.dirty = true;
18023
+ await this.flush();
18024
+ }
18025
+
18026
+ async *entries(prefix = '') {
18027
+ await this.open();
18028
+ yield* super.entries(prefix);
18029
+ }
18030
+
18031
+ async close() {
18032
+ await this.flush();
18033
+ }
18034
+ }
18035
+
18036
+ class ClassicLevelKv {
18037
+ constructor(location) {
18038
+ const classic = __dynamicRequire('classic-level');
18039
+ const ClassicLevel = classic && (classic.ClassicLevel || classic.Level || classic.default);
18040
+ if (!ClassicLevel) throw new Error('classic-level is not installed');
18041
+ const fs = __dynamicRequire('node:fs');
18042
+ const path = __dynamicRequire('node:path');
18043
+ if (fs && path) fs.mkdirSync(path.dirname(location), { recursive: true });
18044
+ this.db = new ClassicLevel(location, { valueEncoding: 'json', createIfMissing: true, errorIfExists: false });
18045
+ this.opened = false;
18046
+ }
18047
+
18048
+ async open() {
18049
+ if (this.opened) return;
18050
+ if (typeof this.db.open === 'function') await this.db.open();
18051
+ this.opened = true;
18052
+ }
18053
+
18054
+ async get(key) {
18055
+ await this.open();
18056
+ try {
18057
+ return await this.db.get(key);
18058
+ } catch (e) {
18059
+ if (e && (e.notFound || e.code === 'LEVEL_NOT_FOUND' || e.code === 'NOT_FOUND')) return undefined;
18060
+ throw e;
18061
+ }
18062
+ }
18063
+
18064
+ async put(key, value) {
18065
+ await this.open();
18066
+ await this.db.put(key, value);
18067
+ }
18068
+
18069
+ async del(key) {
18070
+ await this.open();
18071
+ await this.db.del(key);
18072
+ }
18073
+
18074
+ async clear() {
18075
+ await this.open();
18076
+ if (typeof this.db.clear === 'function') await this.db.clear();
18077
+ else {
18078
+ const ops = [];
18079
+ for await (const [key] of this.entries('')) ops.push({ type: 'del', key });
18080
+ if (ops.length) await this.batch(ops);
18081
+ }
18082
+ }
18083
+
18084
+ async batch(ops) {
18085
+ await this.open();
18086
+ if (!ops || !ops.length) return;
18087
+ await this.db.batch(ops);
18088
+ }
18089
+
18090
+ async *entries(prefix = '') {
18091
+ await this.open();
18092
+ const gte = prefix;
18093
+ const lt = prefix + '\uffff';
18094
+ const iterator = this.db.iterator({ gte, lt });
18095
+ for await (const item of iterator) {
18096
+ if (Array.isArray(item)) yield item;
18097
+ else if (item && Array.isArray(item.key)) yield item.key;
18098
+ }
18099
+ }
18100
+
18101
+ async close() {
18102
+ if (this.db && typeof this.db.close === 'function') await this.db.close();
18103
+ }
18104
+ }
18105
+
18106
+ class IndexedDbKv {
18107
+ constructor(name) {
18108
+ this.name = name;
18109
+ this.dbPromise = null;
18110
+ }
18111
+
18112
+ open() {
18113
+ if (this.dbPromise) return this.dbPromise;
18114
+ const idb = typeof globalThis !== 'undefined' ? globalThis.indexedDB : null;
18115
+ if (!idb) throw new Error('IndexedDB is not available in this runtime');
18116
+ this.dbPromise = new Promise((resolve, reject) => {
18117
+ const req = idb.open(`eyeling:${this.name}`, 1);
18118
+ req.onupgradeneeded = () => {
18119
+ const db = req.result;
18120
+ if (!db.objectStoreNames.contains('kv')) db.createObjectStore('kv');
18121
+ };
18122
+ req.onerror = () => reject(req.error || new Error('Failed to open IndexedDB store'));
18123
+ req.onsuccess = () => resolve(req.result);
18124
+ });
18125
+ return this.dbPromise;
18126
+ }
18127
+
18128
+ async __tx(mode, fn) {
18129
+ const db = await this.open();
18130
+ return new Promise((resolve, reject) => {
18131
+ const tx = db.transaction('kv', mode);
18132
+ const store = tx.objectStore('kv');
18133
+ let value;
18134
+ tx.oncomplete = () => resolve(value);
18135
+ tx.onerror = () => reject(tx.error || new Error('IndexedDB transaction failed'));
18136
+ tx.onabort = () => reject(tx.error || new Error('IndexedDB transaction aborted'));
18137
+ try {
18138
+ value = fn(store, resolve, reject);
18139
+ } catch (e) {
18140
+ try { tx.abort(); } catch {}
18141
+ reject(e);
18142
+ }
18143
+ });
18144
+ }
18145
+
18146
+ async get(key) {
18147
+ return this.__tx('readonly', (store, resolve, reject) => {
18148
+ const req = store.get(key);
18149
+ req.onerror = () => reject(req.error);
18150
+ req.onsuccess = () => resolve(req.result);
18151
+ });
18152
+ }
18153
+
18154
+ async put(key, value) {
18155
+ return this.__tx('readwrite', (store) => {
18156
+ store.put(value, key);
18157
+ });
18158
+ }
18159
+
18160
+ async del(key) {
18161
+ return this.__tx('readwrite', (store) => {
18162
+ store.delete(key);
18163
+ });
18164
+ }
18165
+
18166
+ async clear() {
18167
+ return this.__tx('readwrite', (store) => {
18168
+ store.clear();
18169
+ });
18170
+ }
18171
+
18172
+ async batch(ops) {
18173
+ return this.__tx('readwrite', (store) => {
18174
+ for (const op of ops || []) {
18175
+ if (op.type === 'del') store.delete(op.key);
18176
+ else store.put(op.value, op.key);
18177
+ }
18178
+ });
18179
+ }
18180
+
18181
+ async *entries(prefix = '') {
18182
+ const db = await this.open();
18183
+ const rows = await new Promise((resolve, reject) => {
18184
+ const tx = db.transaction('kv', 'readonly');
18185
+ const store = tx.objectStore('kv');
18186
+ const out = [];
18187
+ const range = globalThis.IDBKeyRange.bound(prefix, prefix + '\uffff', false, true);
18188
+ const req = store.openCursor(range);
18189
+ req.onerror = () => reject(req.error);
18190
+ req.onsuccess = () => {
18191
+ const cursor = req.result;
18192
+ if (!cursor) return;
18193
+ out.push([cursor.key, cursor.value]);
18194
+ cursor.continue();
18195
+ };
18196
+ tx.oncomplete = () => resolve(out);
18197
+ tx.onerror = () => reject(tx.error || new Error('IndexedDB transaction failed'));
18198
+ });
18199
+ for (const row of rows) yield row;
18200
+ }
18201
+
18202
+ async close() {
18203
+ if (!this.dbPromise) return;
18204
+ const db = await this.dbPromise;
18205
+ if (db && typeof db.close === 'function') db.close();
18206
+ }
18207
+ }
18208
+
18209
+ class MemoryFactStore {
18210
+ constructor() {
18211
+ this.map = new Map();
18212
+ }
18213
+
18214
+ async add(triple, kind = 'explicit') {
18215
+ const key = tripleToStoreKey(triple);
18216
+ const prev = this.map.get(key);
18217
+ const mask = kindMask(kind);
18218
+ if (prev) {
18219
+ const nextKind = prev.kind | mask;
18220
+ if (nextKind !== prev.kind) prev.kind = nextKind;
18221
+ return false;
18222
+ }
18223
+ this.map.set(key, { triple, kind: mask });
18224
+ return true;
18225
+ }
18226
+
18227
+ async batchAdd(triples, kind = 'explicit') {
18228
+ let n = 0;
18229
+ for (const triple of triples || []) if (await this.add(triple, kind)) n += 1;
18230
+ return n;
18231
+ }
18232
+
18233
+ async has(triple) {
18234
+ return this.map.has(tripleToStoreKey(triple));
18235
+ }
18236
+
18237
+ async kindOf(triple) {
18238
+ const row = this.map.get(tripleToStoreKey(triple));
18239
+ return row ? row.kind : 0;
18240
+ }
18241
+
18242
+ async *match(s, p, o) {
18243
+ const sk = s == null ? null : termToStoreKey(s);
18244
+ const pk = p == null ? null : termToStoreKey(p);
18245
+ const ok = o == null ? null : termToStoreKey(o);
18246
+ for (const { triple } of this.map.values()) {
18247
+ if (sk !== null && termToStoreKey(triple.s) !== sk) continue;
18248
+ if (pk !== null && termToStoreKey(triple.p) !== pk) continue;
18249
+ if (ok !== null && termToStoreKey(triple.o) !== ok) continue;
18250
+ yield triple;
18251
+ }
18252
+ }
18253
+
18254
+ async clear() {
18255
+ this.map.clear();
18256
+ }
18257
+
18258
+ async close() {}
18259
+ }
18260
+
18261
+ class PersistentFactStore {
18262
+ constructor(kv, options = {}) {
18263
+ this.kv = kv;
18264
+ this.termCacheByKey = new Map();
18265
+ this.termCacheById = new Map();
18266
+ this.name = options.name || 'default';
18267
+ }
18268
+
18269
+ async clear() {
18270
+ await this.kv.clear();
18271
+ this.termCacheByKey.clear();
18272
+ this.termCacheById.clear();
18273
+ }
18274
+
18275
+ async __nextTermId() {
18276
+ const key = 'meta/nextTermId';
18277
+ const current = Number((await this.kv.get(key)) || 1);
18278
+ await this.kv.put(key, current + 1);
18279
+ return idPart(current);
18280
+ }
18281
+
18282
+ async __idForTerm(term, create) {
18283
+ const canonical = termToStoreKey(term);
18284
+ if (this.termCacheByKey.has(canonical)) return this.termCacheByKey.get(canonical);
18285
+ const byLexKey = `term/byLex/${base64url(canonical)}`;
18286
+ const found = await this.kv.get(byLexKey);
18287
+ if (found !== undefined) {
18288
+ this.termCacheByKey.set(canonical, found);
18289
+ return found;
18290
+ }
18291
+ if (!create) return null;
18292
+ const id = await this.__nextTermId();
18293
+ const json = termToJson(term);
18294
+ await this.kv.batch([
18295
+ { type: 'put', key: byLexKey, value: id },
18296
+ { type: 'put', key: `term/byId/${id}`, value: json },
18297
+ ]);
18298
+ this.termCacheByKey.set(canonical, id);
18299
+ this.termCacheById.set(id, term);
18300
+ return id;
18301
+ }
18302
+
18303
+ async __termById(id) {
18304
+ if (this.termCacheById.has(id)) return this.termCacheById.get(id);
18305
+ const json = await this.kv.get(`term/byId/${id}`);
18306
+ if (json === undefined) throw new Error(`Corrupt fact store: missing term ${id}`);
18307
+ const term = termFromJson(json);
18308
+ this.termCacheById.set(id, term);
18309
+ return term;
18310
+ }
18311
+
18312
+ async __tripleFromIds(sid, pid, oid) {
18313
+ return new Triple(await this.__termById(sid), await this.__termById(pid), await this.__termById(oid));
18314
+ }
18315
+
18316
+ async add(triple, kind = 'explicit') {
18317
+ const sid = await this.__idForTerm(triple.s, true);
18318
+ const pid = await this.__idForTerm(triple.p, true);
18319
+ const oid = await this.__idForTerm(triple.o, true);
18320
+ const mask = kindMask(kind);
18321
+ const primary = `triple/${sid}/${pid}/${oid}`;
18322
+ const prev = await this.kv.get(primary);
18323
+ if (prev !== undefined) {
18324
+ const nextKind = (typeof prev === 'number' ? prev : prev.kind || 0) | mask;
18325
+ if (nextKind !== prev) await this.kv.put(primary, nextKind);
18326
+ return false;
18327
+ }
18328
+ await this.kv.batch([
18329
+ { type: 'put', key: primary, value: mask },
18330
+ { type: 'put', key: `i/spo/${sid}/${pid}/${oid}`, value: mask },
18331
+ { type: 'put', key: `i/pos/${pid}/${oid}/${sid}`, value: mask },
18332
+ { type: 'put', key: `i/osp/${oid}/${sid}/${pid}`, value: mask },
18333
+ ]);
18334
+ return true;
18335
+ }
18336
+
18337
+ async batchAdd(triples, kind = 'explicit') {
18338
+ let n = 0;
18339
+ for (const triple of triples || []) if (await this.add(triple, kind)) n += 1;
18340
+ return n;
18341
+ }
18342
+
18343
+ async has(triple) {
18344
+ return (await this.kindOf(triple)) !== 0;
18345
+ }
18346
+
18347
+ async kindOf(triple) {
18348
+ const sid = await this.__idForTerm(triple.s, false);
18349
+ if (sid === null) return 0;
18350
+ const pid = await this.__idForTerm(triple.p, false);
18351
+ if (pid === null) return 0;
18352
+ const oid = await this.__idForTerm(triple.o, false);
18353
+ if (oid === null) return 0;
18354
+ const value = await this.kv.get(`triple/${sid}/${pid}/${oid}`);
18355
+ return typeof value === 'number' ? value : value && typeof value.kind === 'number' ? value.kind : 0;
18356
+ }
18357
+
18358
+ async __idsForPattern(s, p, o) {
18359
+ const sid = s == null ? null : await this.__idForTerm(s, false);
18360
+ const pid = p == null ? null : await this.__idForTerm(p, false);
18361
+ const oid = o == null ? null : await this.__idForTerm(o, false);
18362
+ if ((s != null && sid === null) || (p != null && pid === null) || (o != null && oid === null)) return null;
18363
+ return { sid, pid, oid };
18364
+ }
18365
+
18366
+ __scanPlan(sid, pid, oid) {
18367
+ if (sid && pid && oid) return { index: 'triple', prefix: `triple/${sid}/${pid}/${oid}`, order: 'spo' };
18368
+ if (sid && pid) return { index: 'spo', prefix: `i/spo/${sid}/${pid}/`, order: 'spo' };
18369
+ if (pid && oid) return { index: 'pos', prefix: `i/pos/${pid}/${oid}/`, order: 'pos' };
18370
+ if (sid && oid) return { index: 'osp', prefix: `i/osp/${oid}/${sid}/`, order: 'osp' };
18371
+ if (pid) return { index: 'pos', prefix: `i/pos/${pid}/`, order: 'pos' };
18372
+ if (sid) return { index: 'spo', prefix: `i/spo/${sid}/`, order: 'spo' };
18373
+ if (oid) return { index: 'osp', prefix: `i/osp/${oid}/`, order: 'osp' };
18374
+ return { index: 'spo', prefix: 'i/spo/', order: 'spo' };
18375
+ }
18376
+
18377
+ __decodeIndexKey(key, plan) {
18378
+ if (plan.index === 'triple') {
18379
+ const parts = key.split('/');
18380
+ return { sid: parts[1], pid: parts[2], oid: parts[3] };
18381
+ }
18382
+ const rest = key.slice(`i/${plan.index}/`.length).split('/');
18383
+ if (plan.order === 'spo') return { sid: rest[0], pid: rest[1], oid: rest[2] };
18384
+ if (plan.order === 'pos') return { pid: rest[0], oid: rest[1], sid: rest[2] };
18385
+ return { oid: rest[0], sid: rest[1], pid: rest[2] };
18386
+ }
18387
+
18388
+ async *match(s, p, o) {
18389
+ const ids = await this.__idsForPattern(s, p, o);
18390
+ if (!ids) return;
18391
+ const plan = this.__scanPlan(ids.sid, ids.pid, ids.oid);
18392
+ const seen = new Set();
18393
+ for await (const [key] of this.kv.entries(plan.prefix)) {
18394
+ const row = this.__decodeIndexKey(key, plan);
18395
+ if (ids.sid && row.sid !== ids.sid) continue;
18396
+ if (ids.pid && row.pid !== ids.pid) continue;
18397
+ if (ids.oid && row.oid !== ids.oid) continue;
18398
+ const primary = `${row.sid}/${row.pid}/${row.oid}`;
18399
+ if (seen.has(primary)) continue;
18400
+ seen.add(primary);
18401
+ yield this.__tripleFromIds(row.sid, row.pid, row.oid);
18402
+ }
18403
+ }
18404
+
18405
+ async close() {
18406
+ if (this.kv && typeof this.kv.close === 'function') await this.kv.close();
18407
+ }
18408
+ }
18409
+
18410
+ function nodeStoreLocation(name, storePath) {
18411
+ const path = __dynamicRequire('node:path');
18412
+ const os = __dynamicRequire('node:os');
18413
+ if (!path || !os) return null;
18414
+ const base = storePath || path.join(os.homedir ? os.homedir() : '.', '.eyeling-store');
18415
+ return path.join(base, safeStoreName(name));
18416
+ }
18417
+
18418
+ async function createPersistentFactStore(options = {}) {
18419
+ const name = typeof options === 'string' ? options : options.name || 'default';
18420
+ const clear = !!(options && options.clear);
18421
+ let kv;
18422
+
18423
+ if (typeof globalThis !== 'undefined' && globalThis.indexedDB && !__dynamicRequire('node:fs')) {
18424
+ kv = new IndexedDbKv(name);
18425
+ } else {
18426
+ const location = nodeStoreLocation(name, options && options.path);
18427
+ try {
18428
+ kv = new ClassicLevelKv(location);
18429
+ } catch {
18430
+ kv = new JsonFileKv(`${location}.json`);
18431
+ }
18432
+ }
18433
+
18434
+ const store = new PersistentFactStore(kv, { name });
18435
+ if (clear) await store.clear();
18436
+ return store;
18437
+ }
18438
+
18439
+ async function createFactStore(options = null) {
18440
+ if (!options) return new MemoryFactStore();
18441
+ if (typeof options === 'string') return createPersistentFactStore({ name: options });
18442
+ if (options.type === 'memory') return new MemoryFactStore();
18443
+ if (options.backend === 'memory') return new MemoryFactStore();
18444
+ return createPersistentFactStore(options);
18445
+ }
18446
+
18447
+ async function collectStore(store) {
18448
+ const out = [];
18449
+ for await (const tr of store.match(null, null, null)) out.push(tr);
18450
+ return out;
18451
+ }
18452
+
18453
+ module.exports = {
18454
+ KIND_EXPLICIT,
18455
+ KIND_INFERRED,
18456
+ MemoryFactStore,
18457
+ PersistentFactStore,
18458
+ createFactStore,
18459
+ createPersistentFactStore,
18460
+ collectStore,
18461
+ termToStoreKey,
18462
+ tripleToStoreKey,
18463
+ termToJson,
18464
+ termFromJson,
18465
+ tripleToJson,
18466
+ tripleFromJson,
18467
+ };
18468
+
17218
18469
  };
17219
18470
  __modules["lib/time.js"] = function(require, module, exports){
17220
18471
  /**