eyeling 1.19.1 → 1.19.3
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/HANDBOOK.md +67 -12
- package/eyeling.js +37 -6
- package/lib/builtins.js +37 -6
- package/package.json +3 -2
- package/test/builtin-contract.test.js +155 -0
- package/test/fixtures/builtins/bad-export.js +3 -0
- package/test/fixtures/builtins/bad-return.js +5 -0
- package/test/fixtures/builtins/ok-builtins.js +7 -0
- package/test/fixtures/builtins/ok-default-map.js +7 -0
- package/test/fixtures/builtins/ok-map.js +5 -0
- package/test/fixtures/builtins/ok-register.js +7 -0
package/HANDBOOK.md
CHANGED
|
@@ -2085,9 +2085,24 @@ const { reason } = require('eyeling');
|
|
|
2085
2085
|
const out = reason({ builtinModules: ['./hello-builtin.js'] }, n3Text);
|
|
2086
2086
|
```
|
|
2087
2087
|
|
|
2088
|
+
### 16.2.1 Stability rule for `--builtin`
|
|
2089
|
+
|
|
2090
|
+
Eyeling keeps `--builtin` simple.
|
|
2091
|
+
|
|
2092
|
+
There is one small helper API passed into builtin modules. That helper object is frozen, its key set is regression-tested, and builtin modules must use one of the documented export forms.
|
|
2093
|
+
|
|
2094
|
+
In practice, this means:
|
|
2095
|
+
|
|
2096
|
+
- builtin module loading accepts only the documented export forms
|
|
2097
|
+
- the helper API exposed by `__buildBuiltinRegistrationApi()` has a fixed key set
|
|
2098
|
+
- builtin handlers should return an array of substitution objects
|
|
2099
|
+
- accidental helper drift is caught by `test/builtin-contract.test.js`
|
|
2100
|
+
|
|
2101
|
+
This is only meant to stop silent breakage. It is **not** a promise that Eyeling can never change the builtin API. If the helper surface ever needs to change, that change should be deliberate, documented, and called out in release notes.
|
|
2102
|
+
|
|
2088
2103
|
### 16.3 What a builtin module may export
|
|
2089
2104
|
|
|
2090
|
-
Eyeling accepts
|
|
2105
|
+
Eyeling accepts these stable module shapes.
|
|
2091
2106
|
|
|
2092
2107
|
#### A function export
|
|
2093
2108
|
|
|
@@ -2124,23 +2139,52 @@ module.exports = {
|
|
|
2124
2139
|
};
|
|
2125
2140
|
```
|
|
2126
2141
|
|
|
2142
|
+
#### An object with `.builtins`
|
|
2143
|
+
|
|
2144
|
+
```js
|
|
2145
|
+
module.exports = {
|
|
2146
|
+
builtins: {
|
|
2147
|
+
'http://example.org/custom#ok': ({ subst }) => [subst],
|
|
2148
|
+
},
|
|
2149
|
+
};
|
|
2150
|
+
```
|
|
2151
|
+
|
|
2152
|
+
#### An object with `.default` as a plain object map
|
|
2153
|
+
|
|
2154
|
+
This is mainly an ESM/transpiler compatibility form.
|
|
2155
|
+
|
|
2156
|
+
```js
|
|
2157
|
+
module.exports = {
|
|
2158
|
+
default: {
|
|
2159
|
+
'http://example.org/custom#ok': ({ subst }) => [subst],
|
|
2160
|
+
},
|
|
2161
|
+
};
|
|
2162
|
+
```
|
|
2163
|
+
|
|
2127
2164
|
If none of those shapes match, Eyeling rejects the module with a descriptive error.
|
|
2128
2165
|
|
|
2129
2166
|
### 16.4 The handler contract
|
|
2130
2167
|
|
|
2131
|
-
Builtin handlers are called with a context object
|
|
2168
|
+
Builtin handlers are called with a context object containing:
|
|
2132
2169
|
|
|
2133
2170
|
- `iri` — the predicate IRI string
|
|
2134
2171
|
- `goal` — the current triple goal
|
|
2135
2172
|
- `subst` — the current substitution
|
|
2136
|
-
- `facts
|
|
2173
|
+
- `facts` — the active fact store
|
|
2174
|
+
- `backRules` — the backward-rule set
|
|
2175
|
+
- `depth` — current proof depth
|
|
2176
|
+
- `varGen` — the variable generator state
|
|
2177
|
+
- `maxResults` — current result cap
|
|
2137
2178
|
- `api` — the same registration/helper API used by modules
|
|
2138
2179
|
|
|
2139
|
-
A handler
|
|
2180
|
+
A handler should return an **array of substitution objects**:
|
|
2140
2181
|
|
|
2141
2182
|
- `[]` means failure / no solutions
|
|
2142
|
-
- `[
|
|
2143
|
-
-
|
|
2183
|
+
- `[{}]` means success with no new bindings
|
|
2184
|
+
- `[{ ...delta }]` means one successful continuation with bindings
|
|
2185
|
+
- multiple objects mean a generator builtin
|
|
2186
|
+
|
|
2187
|
+
Returning something else is rejected at runtime.
|
|
2144
2188
|
|
|
2145
2189
|
In practice:
|
|
2146
2190
|
|
|
@@ -2153,13 +2197,24 @@ Custom builtin failures are wrapped so the predicate IRI appears in the thrown e
|
|
|
2153
2197
|
|
|
2154
2198
|
### 16.5 The helper API exposed to builtin modules
|
|
2155
2199
|
|
|
2156
|
-
Builtin modules do not need to import internal engine files directly. Eyeling passes a helper API into module registration,
|
|
2200
|
+
Builtin modules do not need to import internal engine files directly. Eyeling passes a helper API into module registration, and that helper surface is kept intentionally small.
|
|
2201
|
+
|
|
2202
|
+
The current helper function set is:
|
|
2203
|
+
|
|
2204
|
+
- `registerBuiltin`, `unregisterBuiltin`, `listBuiltinIris`
|
|
2205
|
+
- `internIri`, `internLiteral`, `literalParts`
|
|
2206
|
+
- `termToJsString`, `termToJsStringDecoded`, `termToN3`, `iriValue`
|
|
2207
|
+
- `unifyTerm`, `applySubstTerm`, `applySubstTriple`, `proveGoals`, `isGroundTerm`
|
|
2208
|
+
- `computeConclusionFromFormula`, `skolemIriFromGroundTerm`
|
|
2209
|
+
- `parseBooleanLiteralInfo`, `parseNumericLiteralInfo`, `parseXsdDecimalToBigIntScale`, `pow10n`
|
|
2210
|
+
- `normalizeLiteralForFastKey`, `literalsEquivalentAsXsdString`, `materializeRdfLists`
|
|
2211
|
+
|
|
2212
|
+
The stable namespace bags are:
|
|
2213
|
+
|
|
2214
|
+
- `terms`: `Literal`, `Iri`, `Var`, `Blank`, `ListTerm`, `OpenListTerm`, `GraphTerm`, `Triple`, `Rule`
|
|
2215
|
+
- `ns`: `RDF_NS`, `XSD_NS`, `CRYPTO_NS`, `MATH_NS`, `TIME_NS`, `LIST_NS`, `LOG_NS`, `STRING_NS`
|
|
2157
2216
|
|
|
2158
|
-
-
|
|
2159
|
-
- term constructors via `terms` (`Literal`, `Iri`, `Var`, `Blank`, `ListTerm`, `GraphTerm`, `Triple`, `Rule`, ...)
|
|
2160
|
-
- literal/term helpers such as `internLiteral`, `internIri`, `literalParts`, `termToN3`, `termToJsString`
|
|
2161
|
-
- reasoning helpers such as `unifyTerm`, `applySubstTerm`, `applySubstTriple`, `proveGoals`
|
|
2162
|
-
- namespace constants via `ns`
|
|
2217
|
+
The helper object is frozen and regression-tested so helper additions, removals, and renames do not slip in silently.
|
|
2163
2218
|
|
|
2164
2219
|
That API keeps the extension boundary explicit: custom builtins get the operations they need without reaching into Eyeling’s private module graph.
|
|
2165
2220
|
|
package/eyeling.js
CHANGED
|
@@ -558,8 +558,15 @@ function registerBuiltin(iri, handler) {
|
|
|
558
558
|
if (typeof handler !== 'function') {
|
|
559
559
|
throw new TypeError(`Custom builtin ${iri} must be registered with a function handler`);
|
|
560
560
|
}
|
|
561
|
-
|
|
562
|
-
|
|
561
|
+
|
|
562
|
+
const wrapped = function builtinResultWrapper(ctx) {
|
|
563
|
+
const out = handler(ctx);
|
|
564
|
+
__assertBuiltinHandlerResult(iri, out);
|
|
565
|
+
return out;
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
__customBuiltinHandlers.set(iri, wrapped);
|
|
569
|
+
return wrapped;
|
|
563
570
|
}
|
|
564
571
|
|
|
565
572
|
function unregisterBuiltin(iri) {
|
|
@@ -570,8 +577,30 @@ function listBuiltinIris() {
|
|
|
570
577
|
return Array.from(__customBuiltinHandlers.keys()).sort();
|
|
571
578
|
}
|
|
572
579
|
|
|
580
|
+
let __builtinApiSingleton = null;
|
|
581
|
+
|
|
582
|
+
function __freezeBuiltinApi(api) {
|
|
583
|
+
Object.freeze(api.terms);
|
|
584
|
+
Object.freeze(api.ns);
|
|
585
|
+
return Object.freeze(api);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function __assertBuiltinHandlerResult(iri, out) {
|
|
589
|
+
if (out == null) return;
|
|
590
|
+
if (!Array.isArray(out)) {
|
|
591
|
+
throw new TypeError(`Custom builtin ${iri} must return an array of substitution deltas`);
|
|
592
|
+
}
|
|
593
|
+
for (const delta of out) {
|
|
594
|
+
if (!delta || typeof delta !== 'object' || Array.isArray(delta)) {
|
|
595
|
+
throw new TypeError(`Custom builtin ${iri} must return an array of substitution deltas`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
573
600
|
function __buildBuiltinRegistrationApi() {
|
|
574
|
-
return
|
|
601
|
+
if (__builtinApiSingleton) return __builtinApiSingleton;
|
|
602
|
+
|
|
603
|
+
const api = {
|
|
575
604
|
registerBuiltin,
|
|
576
605
|
unregisterBuiltin,
|
|
577
606
|
listBuiltinIris,
|
|
@@ -599,6 +628,9 @@ function __buildBuiltinRegistrationApi() {
|
|
|
599
628
|
terms: { Literal, Iri, Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple, Rule },
|
|
600
629
|
ns: { RDF_NS, XSD_NS, CRYPTO_NS, MATH_NS, TIME_NS, LIST_NS, LOG_NS, STRING_NS },
|
|
601
630
|
};
|
|
631
|
+
|
|
632
|
+
__builtinApiSingleton = __freezeBuiltinApi(api);
|
|
633
|
+
return __builtinApiSingleton;
|
|
602
634
|
}
|
|
603
635
|
|
|
604
636
|
function registerBuiltinModule(mod, origin = '<builtin-module>') {
|
|
@@ -675,9 +707,7 @@ function __evalRegisteredBuiltin(pv, goal, subst, facts, backRules, depth, varGe
|
|
|
675
707
|
try {
|
|
676
708
|
const out = handler(ctx);
|
|
677
709
|
if (out == null) return [];
|
|
678
|
-
|
|
679
|
-
throw new TypeError(`Custom builtin ${pv} must return an array of substitution deltas`);
|
|
680
|
-
}
|
|
710
|
+
__assertBuiltinHandlerResult(pv, out);
|
|
681
711
|
return out;
|
|
682
712
|
} catch (err) {
|
|
683
713
|
if (err && typeof err === 'object' && typeof err.message === 'string') {
|
|
@@ -4472,6 +4502,7 @@ module.exports = {
|
|
|
4472
4502
|
registerBuiltinModule,
|
|
4473
4503
|
loadBuiltinModule,
|
|
4474
4504
|
listBuiltinIris,
|
|
4505
|
+
__testBuildBuiltinApi: __buildBuiltinRegistrationApi,
|
|
4475
4506
|
// shared helpers used by engine/explain
|
|
4476
4507
|
parseBooleanLiteralInfo,
|
|
4477
4508
|
parseNumericLiteralInfo,
|
package/lib/builtins.js
CHANGED
|
@@ -78,8 +78,15 @@ function registerBuiltin(iri, handler) {
|
|
|
78
78
|
if (typeof handler !== 'function') {
|
|
79
79
|
throw new TypeError(`Custom builtin ${iri} must be registered with a function handler`);
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
|
|
82
|
+
const wrapped = function builtinResultWrapper(ctx) {
|
|
83
|
+
const out = handler(ctx);
|
|
84
|
+
__assertBuiltinHandlerResult(iri, out);
|
|
85
|
+
return out;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
__customBuiltinHandlers.set(iri, wrapped);
|
|
89
|
+
return wrapped;
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
function unregisterBuiltin(iri) {
|
|
@@ -90,8 +97,30 @@ function listBuiltinIris() {
|
|
|
90
97
|
return Array.from(__customBuiltinHandlers.keys()).sort();
|
|
91
98
|
}
|
|
92
99
|
|
|
100
|
+
let __builtinApiSingleton = null;
|
|
101
|
+
|
|
102
|
+
function __freezeBuiltinApi(api) {
|
|
103
|
+
Object.freeze(api.terms);
|
|
104
|
+
Object.freeze(api.ns);
|
|
105
|
+
return Object.freeze(api);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function __assertBuiltinHandlerResult(iri, out) {
|
|
109
|
+
if (out == null) return;
|
|
110
|
+
if (!Array.isArray(out)) {
|
|
111
|
+
throw new TypeError(`Custom builtin ${iri} must return an array of substitution deltas`);
|
|
112
|
+
}
|
|
113
|
+
for (const delta of out) {
|
|
114
|
+
if (!delta || typeof delta !== 'object' || Array.isArray(delta)) {
|
|
115
|
+
throw new TypeError(`Custom builtin ${iri} must return an array of substitution deltas`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
93
120
|
function __buildBuiltinRegistrationApi() {
|
|
94
|
-
return
|
|
121
|
+
if (__builtinApiSingleton) return __builtinApiSingleton;
|
|
122
|
+
|
|
123
|
+
const api = {
|
|
95
124
|
registerBuiltin,
|
|
96
125
|
unregisterBuiltin,
|
|
97
126
|
listBuiltinIris,
|
|
@@ -119,6 +148,9 @@ function __buildBuiltinRegistrationApi() {
|
|
|
119
148
|
terms: { Literal, Iri, Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple, Rule },
|
|
120
149
|
ns: { RDF_NS, XSD_NS, CRYPTO_NS, MATH_NS, TIME_NS, LIST_NS, LOG_NS, STRING_NS },
|
|
121
150
|
};
|
|
151
|
+
|
|
152
|
+
__builtinApiSingleton = __freezeBuiltinApi(api);
|
|
153
|
+
return __builtinApiSingleton;
|
|
122
154
|
}
|
|
123
155
|
|
|
124
156
|
function registerBuiltinModule(mod, origin = '<builtin-module>') {
|
|
@@ -195,9 +227,7 @@ function __evalRegisteredBuiltin(pv, goal, subst, facts, backRules, depth, varGe
|
|
|
195
227
|
try {
|
|
196
228
|
const out = handler(ctx);
|
|
197
229
|
if (out == null) return [];
|
|
198
|
-
|
|
199
|
-
throw new TypeError(`Custom builtin ${pv} must return an array of substitution deltas`);
|
|
200
|
-
}
|
|
230
|
+
__assertBuiltinHandlerResult(pv, out);
|
|
201
231
|
return out;
|
|
202
232
|
} catch (err) {
|
|
203
233
|
if (err && typeof err === 'object' && typeof err.message === 'string') {
|
|
@@ -3992,6 +4022,7 @@ module.exports = {
|
|
|
3992
4022
|
registerBuiltinModule,
|
|
3993
4023
|
loadBuiltinModule,
|
|
3994
4024
|
listBuiltinIris,
|
|
4025
|
+
__testBuildBuiltinApi: __buildBuiltinRegistrationApi,
|
|
3995
4026
|
// shared helpers used by engine/explain
|
|
3996
4027
|
parseBooleanLiteralInfo,
|
|
3997
4028
|
parseNumericLiteralInfo,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyeling",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.3",
|
|
4
4
|
"description": "A minimal Notation3 (N3) reasoner in JavaScript.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -40,12 +40,13 @@
|
|
|
40
40
|
"build": "node tools/bundle.js",
|
|
41
41
|
"test:packlist": "node test/packlist.test.js",
|
|
42
42
|
"test:api": "node test/api.test.js",
|
|
43
|
+
"test:builtin-contract": "node test/builtin-contract.test.js",
|
|
43
44
|
"test:n3gen": "node test/n3gen.test.js",
|
|
44
45
|
"test:examples": "node test/examples.test.js",
|
|
45
46
|
"test:manifest": "node test/manifest.test.js",
|
|
46
47
|
"test:playground": "node test/playground.test.js",
|
|
47
48
|
"test:package": "node test/package.test.js",
|
|
48
|
-
"test:all": "npm run test:api && npm run test:n3gen && npm run test:examples && npm run test:manifest && npm run test:playground",
|
|
49
|
+
"test:all": "npm run test:api && npm run test:builtin-contract && npm run test:n3gen && npm run test:examples && npm run test:manifest && npm run test:playground",
|
|
49
50
|
"pretest": "npm run build && npm run test:packlist",
|
|
50
51
|
"test": "npm run test:all",
|
|
51
52
|
"posttest": "npm run test:package",
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const TTY = process.stdout.isTTY;
|
|
7
|
+
const C = TTY
|
|
8
|
+
? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
|
|
9
|
+
: { g: '', r: '', y: '', dim: '', n: '' };
|
|
10
|
+
|
|
11
|
+
function ok(msg) {
|
|
12
|
+
console.log(`${C.g}OK ${C.n} ${msg}`);
|
|
13
|
+
}
|
|
14
|
+
function info(msg) {
|
|
15
|
+
console.log(`${C.y}==${C.n} ${msg}`);
|
|
16
|
+
}
|
|
17
|
+
function fail(msg) {
|
|
18
|
+
console.error(`${C.r}FAIL${C.n} ${msg}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const fixtures = path.join(__dirname, 'fixtures', 'builtins');
|
|
22
|
+
const builtins = require('../lib/builtins');
|
|
23
|
+
require('../lib/engine');
|
|
24
|
+
|
|
25
|
+
const expectedApiKeys = [
|
|
26
|
+
'registerBuiltin',
|
|
27
|
+
'unregisterBuiltin',
|
|
28
|
+
'listBuiltinIris',
|
|
29
|
+
'internIri',
|
|
30
|
+
'internLiteral',
|
|
31
|
+
'literalParts',
|
|
32
|
+
'termToJsString',
|
|
33
|
+
'termToJsStringDecoded',
|
|
34
|
+
'termToN3',
|
|
35
|
+
'iriValue',
|
|
36
|
+
'unifyTerm',
|
|
37
|
+
'applySubstTerm',
|
|
38
|
+
'applySubstTriple',
|
|
39
|
+
'proveGoals',
|
|
40
|
+
'isGroundTerm',
|
|
41
|
+
'computeConclusionFromFormula',
|
|
42
|
+
'skolemIriFromGroundTerm',
|
|
43
|
+
'parseBooleanLiteralInfo',
|
|
44
|
+
'parseNumericLiteralInfo',
|
|
45
|
+
'parseXsdDecimalToBigIntScale',
|
|
46
|
+
'pow10n',
|
|
47
|
+
'normalizeLiteralForFastKey',
|
|
48
|
+
'literalsEquivalentAsXsdString',
|
|
49
|
+
'materializeRdfLists',
|
|
50
|
+
'terms',
|
|
51
|
+
'ns',
|
|
52
|
+
].sort();
|
|
53
|
+
|
|
54
|
+
const expectedTermsKeys = [
|
|
55
|
+
'Literal',
|
|
56
|
+
'Iri',
|
|
57
|
+
'Var',
|
|
58
|
+
'Blank',
|
|
59
|
+
'ListTerm',
|
|
60
|
+
'OpenListTerm',
|
|
61
|
+
'GraphTerm',
|
|
62
|
+
'Triple',
|
|
63
|
+
'Rule',
|
|
64
|
+
].sort();
|
|
65
|
+
const expectedNsKeys = ['RDF_NS', 'XSD_NS', 'CRYPTO_NS', 'MATH_NS', 'TIME_NS', 'LIST_NS', 'LOG_NS', 'STRING_NS'].sort();
|
|
66
|
+
|
|
67
|
+
const cases = [
|
|
68
|
+
{
|
|
69
|
+
name: 'builtin helper API stays stable and frozen',
|
|
70
|
+
run() {
|
|
71
|
+
const api = builtins.__testBuildBuiltinApi();
|
|
72
|
+
assert.deepEqual(Object.keys(api).sort(), expectedApiKeys);
|
|
73
|
+
assert.equal(Object.isFrozen(api), true);
|
|
74
|
+
assert.equal(Object.isFrozen(api.terms), true);
|
|
75
|
+
assert.equal(Object.isFrozen(api.ns), true);
|
|
76
|
+
assert.deepEqual(Object.keys(api.terms).sort(), expectedTermsKeys);
|
|
77
|
+
assert.deepEqual(Object.keys(api.ns).sort(), expectedNsKeys);
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'registerBuiltinModule accepts supported module export forms',
|
|
82
|
+
run() {
|
|
83
|
+
assert.doesNotThrow(() => builtins.registerBuiltinModule(require(path.join(fixtures, 'ok-map.js')), 'ok-map'));
|
|
84
|
+
assert.doesNotThrow(() =>
|
|
85
|
+
builtins.registerBuiltinModule(require(path.join(fixtures, 'ok-register.js')), 'ok-register'),
|
|
86
|
+
);
|
|
87
|
+
assert.doesNotThrow(() =>
|
|
88
|
+
builtins.registerBuiltinModule(require(path.join(fixtures, 'ok-builtins.js')), 'ok-builtins'),
|
|
89
|
+
);
|
|
90
|
+
assert.doesNotThrow(() =>
|
|
91
|
+
builtins.registerBuiltinModule(require(path.join(fixtures, 'ok-default-map.js')), 'ok-default-map'),
|
|
92
|
+
);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'registerBuiltinModule rejects unsupported module exports',
|
|
97
|
+
run() {
|
|
98
|
+
assert.throws(
|
|
99
|
+
() => builtins.registerBuiltinModule(require(path.join(fixtures, 'bad-export.js')), 'bad-export'),
|
|
100
|
+
/must export a function, a \{ register\(\) \} object, or an object mapping predicate IRIs to handlers/,
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'registered builtin handlers must return substitution arrays',
|
|
106
|
+
run() {
|
|
107
|
+
const wrapped = builtins.registerBuiltin('http://example.org/test#shape-check', () => ({ nope: true }));
|
|
108
|
+
assert.throws(
|
|
109
|
+
() =>
|
|
110
|
+
wrapped({
|
|
111
|
+
iri: 'http://example.org/test#shape-check',
|
|
112
|
+
goal: {},
|
|
113
|
+
subst: {},
|
|
114
|
+
facts: [],
|
|
115
|
+
backRules: [],
|
|
116
|
+
depth: 0,
|
|
117
|
+
varGen: 0,
|
|
118
|
+
maxResults: 1,
|
|
119
|
+
api: builtins.__testBuildBuiltinApi(),
|
|
120
|
+
}),
|
|
121
|
+
/must return an array of substitution deltas/,
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
let passed = 0;
|
|
128
|
+
let failed = 0;
|
|
129
|
+
|
|
130
|
+
(function main() {
|
|
131
|
+
const suiteStart = Date.now();
|
|
132
|
+
info(`Running ${cases.length} builtin contract tests`);
|
|
133
|
+
|
|
134
|
+
for (const tc of cases) {
|
|
135
|
+
const start = Date.now();
|
|
136
|
+
try {
|
|
137
|
+
tc.run();
|
|
138
|
+
ok(`${tc.name} ${C.dim}(${Date.now() - start} ms)${C.n}`);
|
|
139
|
+
passed++;
|
|
140
|
+
} catch (e) {
|
|
141
|
+
fail(`${tc.name} ${C.dim}(${Date.now() - start} ms)${C.n}`);
|
|
142
|
+
fail(e && e.stack ? e.stack : String(e));
|
|
143
|
+
failed++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log('');
|
|
148
|
+
console.log(`${C.y}==${C.n} Total elapsed: ${Date.now() - suiteStart} ms`);
|
|
149
|
+
if (failed === 0) {
|
|
150
|
+
ok(`All builtin contract tests passed (${passed}/${cases.length})`);
|
|
151
|
+
process.exit(0);
|
|
152
|
+
}
|
|
153
|
+
fail(`Some builtin contract tests failed (${passed}/${cases.length})`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
})();
|