eyeling 1.28.9 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -1
- package/dist/browser/eyeling.browser.js +915 -0
- package/dist/browser/index.mjs +10 -0
- package/eyeling.js +915 -0
- package/index.d.ts +59 -0
- package/index.js +17 -0
- package/lib/cli.js +102 -0
- package/lib/engine.js +120 -0
- package/lib/entry.js +4 -0
- package/lib/rdfjs.js +1 -0
- package/lib/store.js +685 -0
- package/package.json +8 -3
- package/test/store.test.js +138 -0
- package/tools/bundle.js +10 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
const { pass, failResult, info } = require('./report');
|
|
9
|
+
const { Iri, Triple } = require('../lib/prelude');
|
|
10
|
+
const { createFactStore } = require('../lib/store');
|
|
11
|
+
const { runAsync } = require('../index.js');
|
|
12
|
+
|
|
13
|
+
const EX = 'http://example.org/';
|
|
14
|
+
const a = new Iri(EX + 'a');
|
|
15
|
+
const p = new Iri(EX + 'p');
|
|
16
|
+
const q = new Iri(EX + 'q');
|
|
17
|
+
const b = new Iri(EX + 'b');
|
|
18
|
+
const c = new Iri(EX + 'c');
|
|
19
|
+
const x = new Iri(EX + 'x');
|
|
20
|
+
const y = new Iri(EX + 'y');
|
|
21
|
+
const r = new Iri(EX + 'r');
|
|
22
|
+
const z = new Iri(EX + 'z');
|
|
23
|
+
|
|
24
|
+
const triples = [
|
|
25
|
+
new Triple(a, p, b),
|
|
26
|
+
new Triple(a, p, c),
|
|
27
|
+
new Triple(a, q, b),
|
|
28
|
+
new Triple(x, p, b),
|
|
29
|
+
new Triple(y, r, z),
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
async function collect(iterable) {
|
|
33
|
+
const out = [];
|
|
34
|
+
for await (const item of iterable) out.push(item);
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function withStore(factory, fn) {
|
|
39
|
+
const store = await factory();
|
|
40
|
+
try {
|
|
41
|
+
await store.batchAdd(triples, 'explicit');
|
|
42
|
+
await fn(store);
|
|
43
|
+
} finally {
|
|
44
|
+
if (store && typeof store.close === 'function') await store.close();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function expectCount(store, label, args, expected) {
|
|
49
|
+
const out = await collect(store.match(...args));
|
|
50
|
+
if (out.length !== expected) {
|
|
51
|
+
throw new Error(`${label}: expected ${expected} match(es), got ${out.length}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const tests = [
|
|
56
|
+
{
|
|
57
|
+
name: 'memory store supports all eight triple match shapes',
|
|
58
|
+
fn: async () => withStore(() => createFactStore({ type: 'memory' }), async (store) => {
|
|
59
|
+
await expectCount(store, '(s,p,o)', [a, p, b], 1);
|
|
60
|
+
await expectCount(store, '(s,p,?)', [a, p, null], 2);
|
|
61
|
+
await expectCount(store, '(s,?,o)', [a, null, b], 2);
|
|
62
|
+
await expectCount(store, '(s,?,?)', [a, null, null], 3);
|
|
63
|
+
await expectCount(store, '(?,p,o)', [null, p, b], 2);
|
|
64
|
+
await expectCount(store, '(?,p,?)', [null, p, null], 3);
|
|
65
|
+
await expectCount(store, '(?,?,o)', [null, null, b], 3);
|
|
66
|
+
await expectCount(store, '(?,?,?)', [null, null, null], 5);
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'persistent store supports all eight triple match shapes and duplicate prevention',
|
|
71
|
+
fn: async () => {
|
|
72
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-store-test-'));
|
|
73
|
+
try {
|
|
74
|
+
await withStore(() => createFactStore({ name: 'patterns', path: dir, clear: true }), async (store) => {
|
|
75
|
+
await expectCount(store, '(s,p,o)', [a, p, b], 1);
|
|
76
|
+
await expectCount(store, '(s,p,?)', [a, p, null], 2);
|
|
77
|
+
await expectCount(store, '(s,?,o)', [a, null, b], 2);
|
|
78
|
+
await expectCount(store, '(s,?,?)', [a, null, null], 3);
|
|
79
|
+
await expectCount(store, '(?,p,o)', [null, p, b], 2);
|
|
80
|
+
await expectCount(store, '(?,p,?)', [null, p, null], 3);
|
|
81
|
+
await expectCount(store, '(?,?,o)', [null, null, b], 3);
|
|
82
|
+
await expectCount(store, '(?,?,?)', [null, null, null], 5);
|
|
83
|
+
const added = await store.add(new Triple(a, p, b), 'inferred');
|
|
84
|
+
if (added) throw new Error('duplicate insertion should return false');
|
|
85
|
+
if (typeof store.kindOf === 'function') {
|
|
86
|
+
const mask = await store.kindOf(new Triple(a, p, b));
|
|
87
|
+
if ((mask & 1) === 0 || (mask & 2) === 0) throw new Error('explicit/inferred bitmask should be preserved');
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
} finally {
|
|
91
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'runAsync reuses a named persistent store across runs',
|
|
97
|
+
fn: async () => {
|
|
98
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-runasync-store-'));
|
|
99
|
+
try {
|
|
100
|
+
const first = await runAsync('@prefix : <http://example.org/> .\n:a :p :b .', { store: { name: 'dataset', path: dir, clear: true } });
|
|
101
|
+
if (first.store) await first.store.close();
|
|
102
|
+
|
|
103
|
+
const second = await runAsync('@prefix : <http://example.org/> .\n{ ?s :p ?o } => { ?s :q ?o } .', { store: { name: 'dataset', path: dir } });
|
|
104
|
+
try {
|
|
105
|
+
const text = second.closureN3 || '';
|
|
106
|
+
if (!text.includes(':a :q :b .') && !text.includes(':a :q :b.')) throw new Error(`expected stored fact to feed rule, got:\n${text}`);
|
|
107
|
+
if (!second.store) throw new Error('runAsync should return the opened store');
|
|
108
|
+
const qMatches = await collect(second.store.match(a, q, b));
|
|
109
|
+
if (qMatches.length !== 1) throw new Error('expected inferred fact to be persisted');
|
|
110
|
+
} finally {
|
|
111
|
+
if (second.store) await second.store.close();
|
|
112
|
+
}
|
|
113
|
+
} finally {
|
|
114
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
(async function main() {
|
|
121
|
+
const startAll = Date.now();
|
|
122
|
+
let passed = 0;
|
|
123
|
+
for (let i = 0; i < tests.length; i += 1) {
|
|
124
|
+
const n = i + 1;
|
|
125
|
+
const t0 = Date.now();
|
|
126
|
+
try {
|
|
127
|
+
await tests[i].fn();
|
|
128
|
+
pass(n, tests[i].name, Date.now() - t0);
|
|
129
|
+
passed += 1;
|
|
130
|
+
} catch (e) {
|
|
131
|
+
failResult(n, tests[i].name, Date.now() - t0);
|
|
132
|
+
console.error(e && e.stack ? e.stack : e);
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
info(`Store tests: ${passed}/${tests.length} passed`, Date.now() - startAll);
|
|
138
|
+
})();
|
package/tools/bundle.js
CHANGED
|
@@ -240,6 +240,10 @@ function getBrowserApi() {
|
|
|
240
240
|
|
|
241
241
|
export const INFERENCE_FUSE_EXIT_CODE = 65;
|
|
242
242
|
|
|
243
|
+
export function runAsync(input, opts) {
|
|
244
|
+
return getBrowserApi().runAsync(input, opts);
|
|
245
|
+
}
|
|
246
|
+
|
|
243
247
|
export function reasonStream(input, opts) {
|
|
244
248
|
return getBrowserApi().reasonStream(input, opts);
|
|
245
249
|
}
|
|
@@ -248,6 +252,10 @@ export function reasonRdfJs(input, opts) {
|
|
|
248
252
|
return getBrowserApi().reasonRdfJs(input, opts);
|
|
249
253
|
}
|
|
250
254
|
|
|
255
|
+
export function createFactStore(options) {
|
|
256
|
+
return getBrowserApi().createFactStore(options);
|
|
257
|
+
}
|
|
258
|
+
|
|
251
259
|
export function registerBuiltin(iri, handler) {
|
|
252
260
|
return getBrowserApi().registerBuiltin(iri, handler);
|
|
253
261
|
}
|
|
@@ -295,9 +303,11 @@ const eyeling = {
|
|
|
295
303
|
return getBrowserApi().version;
|
|
296
304
|
},
|
|
297
305
|
INFERENCE_FUSE_EXIT_CODE,
|
|
306
|
+
runAsync,
|
|
298
307
|
reasonStream,
|
|
299
308
|
reasonRdfJs,
|
|
300
309
|
rdfjs,
|
|
310
|
+
createFactStore,
|
|
301
311
|
registerBuiltin,
|
|
302
312
|
unregisterBuiltin,
|
|
303
313
|
registerBuiltinModule,
|