ata-validator 1.7.0 → 1.7.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/CHANGELOG.md +14 -0
- package/README.md +9 -0
- package/index.d.ts +3 -3
- package/index.js +121 -45
- package/lib/aot.js +1 -1
- package/lib/interpreter.js +249 -45
- package/lib/js-compiler.js +3 -2
- package/lib/plan-compiler.js +545 -0
- package/lib/version.js +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Compiles interpreter Plans into a tree of specialized closures. No source
|
|
4
|
+
// generation, no `new Function`: this is plain closure composition, so it
|
|
5
|
+
// runs wherever the interpreter runs, CSP included. The payoff over eval()
|
|
6
|
+
// is that every keyword branch is decided once at compile time and every
|
|
7
|
+
// child call is a direct monomorphic call, which is the same advantage the
|
|
8
|
+
// code generator has, minus the codegen.
|
|
9
|
+
//
|
|
10
|
+
// Scope, deliberately narrow:
|
|
11
|
+
// - single schema resource only (no embedded `$id`, no external documents),
|
|
12
|
+
// so the base URI and the dynamic scope are compile-time constants and
|
|
13
|
+
// `$ref` / `$dynamicRef` targets resolve once, here;
|
|
14
|
+
// - no `unevaluatedProperties` / `unevaluatedItems` anywhere (annotation
|
|
15
|
+
// flow stays eval()'s job).
|
|
16
|
+
// Anything outside that gate keeps the generic evaluator. Error output must
|
|
17
|
+
// be byte-for-byte what eval() produces; tests/test_plan_compiler.js diffs
|
|
18
|
+
// the two over the official suite.
|
|
19
|
+
//
|
|
20
|
+
// Compiled signature: fn(data, errors, instancePath, schemaPath, stack) ->
|
|
21
|
+
// boolean. `errors` may be the NOERRORS sentinel for verdict-only runs, in
|
|
22
|
+
// which case a failed step returns immediately; when collecting, every step
|
|
23
|
+
// runs, exactly like eval().
|
|
24
|
+
|
|
25
|
+
function install(deps) {
|
|
26
|
+
const { Plan, NOERRORS, err, evalLeaf, evalLeafV, deepEqual, dataBits, escapePointer,
|
|
27
|
+
T_STRING, T_ARRAY, T_OBJECT, resolveRef, splitFragment, resolveUri } = deps;
|
|
28
|
+
|
|
29
|
+
const TRUE_FN = () => true;
|
|
30
|
+
const TRUE_PAIR = { v: TRUE_FN, c: TRUE_FN };
|
|
31
|
+
const FALSE_PAIR = {
|
|
32
|
+
v: () => false,
|
|
33
|
+
c: (data, errors, instancePath, schemaPath) => {
|
|
34
|
+
if (errors !== NOERRORS) errors.push(err('false schema', 'not', instancePath, schemaPath, {}, 'boolean schema is false'));
|
|
35
|
+
return false;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Whether the whole tree reachable from `node` fits the compiler's scope.
|
|
40
|
+
// `base` is the base URI in effect, updated at compile time exactly the way
|
|
41
|
+
// eval() updates it at run time: the schema path is static, so it can be.
|
|
42
|
+
function compilable(interp, node, base, seen) {
|
|
43
|
+
if (node === true || node === false) return true;
|
|
44
|
+
if (!(node instanceof Plan)) return true;
|
|
45
|
+
const P = node;
|
|
46
|
+
if (P.nodeBase !== null && P.nodeBase !== base) base = P.nodeBase;
|
|
47
|
+
const key = P;
|
|
48
|
+
const prev = seen.get(key);
|
|
49
|
+
if (prev !== undefined) { if (prev === base || prev === true) return true; return false; }
|
|
50
|
+
seen.set(key, base);
|
|
51
|
+
if (P.hasUnevaluated) return false;
|
|
52
|
+
if (P.ref !== null) {
|
|
53
|
+
const t = resolveRef(P.ref, base, interp.state);
|
|
54
|
+
if (t.node === undefined || !compilable(interp, interp.node(t.node), t.base, seen)) return false;
|
|
55
|
+
}
|
|
56
|
+
if (P.dynamicRef !== null) {
|
|
57
|
+
// $dynamicRef stays compiled only in a single-resource schema, where
|
|
58
|
+
// the dynamic scope is a constant. Anything larger keeps eval().
|
|
59
|
+
if (interp.state.resources.size !== 1) return false;
|
|
60
|
+
const t = dynTarget(interp, P.dynamicRef);
|
|
61
|
+
if (t === undefined || !compilable(interp, interp.node(t), base, seen)) return false;
|
|
62
|
+
}
|
|
63
|
+
const kids = [];
|
|
64
|
+
if (P.properties !== null) for (const v of P.properties.values()) kids.push(v.node);
|
|
65
|
+
if (P.patternProperties !== null) for (const e of P.patternProperties) kids.push(e.node);
|
|
66
|
+
if (P.additionalProperties !== undefined) kids.push(P.additionalProperties);
|
|
67
|
+
if (P.propertyNames !== undefined) kids.push(P.propertyNames);
|
|
68
|
+
if (P.dependentSchemas !== null) for (const [, v] of P.dependentSchemas) kids.push(v);
|
|
69
|
+
if (P.propertyDependencies !== null) for (const [, m] of P.propertyDependencies) for (const v of m.values()) kids.push(v);
|
|
70
|
+
if (P.prefixItems !== null) for (const v of P.prefixItems) kids.push(v);
|
|
71
|
+
if (P.items !== undefined) kids.push(P.items);
|
|
72
|
+
if (P.contains !== undefined) kids.push(P.contains);
|
|
73
|
+
if (P.allOf !== null) for (const v of P.allOf) kids.push(v);
|
|
74
|
+
if (P.anyOf !== null) for (const v of P.anyOf) kids.push(v);
|
|
75
|
+
if (P.oneOf !== null) for (const v of P.oneOf) kids.push(v);
|
|
76
|
+
if (P.not !== undefined) kids.push(P.not);
|
|
77
|
+
if (P.if !== undefined) kids.push(P.if);
|
|
78
|
+
if (P.then !== undefined) kids.push(P.then);
|
|
79
|
+
if (P.else !== undefined) kids.push(P.else);
|
|
80
|
+
for (const k of kids) if (!compilable(interp, k, base, seen)) return false;
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Static $dynamicRef target: with a single resource the dynamic scope is
|
|
85
|
+
// always [rootBase], so the outermost-scope search collapses to a lookup.
|
|
86
|
+
function dynTarget(interp, ref) {
|
|
87
|
+
const base = interp.state.rootBase;
|
|
88
|
+
let { node } = resolveRef(ref, base, interp.state);
|
|
89
|
+
const [, fragment] = splitFragment(resolveUri(base, ref));
|
|
90
|
+
if (fragment && !fragment.startsWith('/')) {
|
|
91
|
+
const dyn = interp.state.dynamicAnchors.get(base);
|
|
92
|
+
const bookended = node !== undefined && dyn && dyn.get(fragment) === node;
|
|
93
|
+
if (bookended || !interp.bookending) {
|
|
94
|
+
if (dyn && dyn.has(fragment)) node = dyn.get(fragment);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return node;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function compileNode(interp, node, base, memo) {
|
|
101
|
+
if (node === true) return TRUE_PAIR;
|
|
102
|
+
if (node === false) return FALSE_PAIR;
|
|
103
|
+
if (!(node instanceof Plan)) return TRUE_PAIR;
|
|
104
|
+
const P = node;
|
|
105
|
+
if (P.nodeBase !== null && P.nodeBase !== base) base = P.nodeBase;
|
|
106
|
+
// A plan reached under two bases compiles once per base; nearly every
|
|
107
|
+
// plan only ever has one.
|
|
108
|
+
let perBase = memo.get(P);
|
|
109
|
+
if (perBase === undefined) { perBase = new Map(); memo.set(P, perBase); }
|
|
110
|
+
const cached = perBase.get(base);
|
|
111
|
+
if (cached !== undefined) return cached;
|
|
112
|
+
// Cycles: register trampolines before compiling children.
|
|
113
|
+
const box = { v: null, c: null };
|
|
114
|
+
const pair = {
|
|
115
|
+
v: (d, st) => box.v(d, st),
|
|
116
|
+
c: (d, e, ip, sp, st) => box.c(d, e, ip, sp, st),
|
|
117
|
+
};
|
|
118
|
+
perBase.set(base, pair);
|
|
119
|
+
|
|
120
|
+
const steps = []; // collect variant: (data, errors, ip, sp, stack)
|
|
121
|
+
const vsteps = []; // verdict variant: (data, stack), no strings at all
|
|
122
|
+
|
|
123
|
+
// $ref / $dynamicRef, resolved at compile time. The cycle guard mirrors
|
|
124
|
+
// eval(): a (schema, data) pair already on the stack is a fixed point.
|
|
125
|
+
if (P.ref !== null) {
|
|
126
|
+
const t = resolveRef(P.ref, base, interp.state);
|
|
127
|
+
const target = compileNode(interp, interp.node(t.node), t.base, memo);
|
|
128
|
+
const schema = P.schema;
|
|
129
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
130
|
+
for (let i = stack.length - 2; i >= 0; i -= 2) {
|
|
131
|
+
if (stack[i] === schema && stack[i + 1] === data) return true;
|
|
132
|
+
}
|
|
133
|
+
stack.push(schema, data);
|
|
134
|
+
const ok = target.c(data, errors, instancePath, schemaPath + '/$ref', stack);
|
|
135
|
+
stack.length -= 2;
|
|
136
|
+
return ok;
|
|
137
|
+
});
|
|
138
|
+
vsteps.push((data, stack) => {
|
|
139
|
+
for (let i = stack.length - 2; i >= 0; i -= 2) {
|
|
140
|
+
if (stack[i] === schema && stack[i + 1] === data) return true;
|
|
141
|
+
}
|
|
142
|
+
stack.push(schema, data);
|
|
143
|
+
const ok = target.v(data, stack);
|
|
144
|
+
stack.length -= 2;
|
|
145
|
+
return ok;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (P.dynamicRef !== null) {
|
|
149
|
+
const target = compileNode(interp, interp.node(dynTarget(interp, P.dynamicRef)), base, memo);
|
|
150
|
+
const schema = P.schema;
|
|
151
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
152
|
+
for (let i = stack.length - 2; i >= 0; i -= 2) {
|
|
153
|
+
if (stack[i] === schema && stack[i + 1] === data) return true;
|
|
154
|
+
}
|
|
155
|
+
stack.push(schema, data);
|
|
156
|
+
const ok = target.c(data, errors, instancePath, schemaPath + '/$dynamicRef', stack);
|
|
157
|
+
stack.length -= 2;
|
|
158
|
+
return ok;
|
|
159
|
+
});
|
|
160
|
+
vsteps.push((data, stack) => {
|
|
161
|
+
for (let i = stack.length - 2; i >= 0; i -= 2) {
|
|
162
|
+
if (stack[i] === schema && stack[i + 1] === data) return true;
|
|
163
|
+
}
|
|
164
|
+
stack.push(schema, data);
|
|
165
|
+
const ok = target.v(data, stack);
|
|
166
|
+
stack.length -= 2;
|
|
167
|
+
return ok;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Every value-level keyword in one step, reusing the leaf evaluator so
|
|
172
|
+
// the two paths cannot drift.
|
|
173
|
+
if (P.hasType || P.enum !== null || P.hasConst || P.hasNumber || P.hasString ||
|
|
174
|
+
P.minItems !== undefined || P.maxItems !== undefined || P.uniqueItems ||
|
|
175
|
+
P.required !== null || P.minProperties !== undefined || P.maxProperties !== undefined ||
|
|
176
|
+
P.dependentRequired !== null) {
|
|
177
|
+
steps.push((data, errors, instancePath, schemaPath) => evalLeaf(P, data, errors, instancePath, schemaPath));
|
|
178
|
+
vsteps.push((data) => evalLeafV(P, data));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Arrays
|
|
182
|
+
if (P.prefixItems !== null) {
|
|
183
|
+
const fns = P.prefixItems.map((v) => compileNode(interp, v, base, memo));
|
|
184
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
185
|
+
if (dataBits(data) !== T_ARRAY) return true;
|
|
186
|
+
let ok = true;
|
|
187
|
+
const n = Math.min(fns.length, data.length);
|
|
188
|
+
for (let i = 0; i < n; i++) {
|
|
189
|
+
if (!fns[i].c(data[i], errors, instancePath + '/' + i, schemaPath + '/prefixItems/' + i, stack)) {
|
|
190
|
+
ok = false;
|
|
191
|
+
if (errors === NOERRORS) return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return ok;
|
|
195
|
+
});
|
|
196
|
+
vsteps.push((data, stack) => {
|
|
197
|
+
if (dataBits(data) !== T_ARRAY) return true;
|
|
198
|
+
const n = Math.min(fns.length, data.length);
|
|
199
|
+
for (let i = 0; i < n; i++) if (!fns[i].v(data[i], stack)) return false;
|
|
200
|
+
return true;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (P.items !== undefined) {
|
|
204
|
+
const fn = compileNode(interp, P.items, base, memo);
|
|
205
|
+
const start = P.prefixItems !== null ? P.prefixItems.length : 0;
|
|
206
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
207
|
+
if (dataBits(data) !== T_ARRAY) return true;
|
|
208
|
+
let ok = true;
|
|
209
|
+
for (let i = start; i < data.length; i++) {
|
|
210
|
+
if (!fn.c(data[i], errors, instancePath + '/' + i, schemaPath + '/items', stack)) {
|
|
211
|
+
ok = false;
|
|
212
|
+
if (errors === NOERRORS) return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return ok;
|
|
216
|
+
});
|
|
217
|
+
const fv = fn.v;
|
|
218
|
+
vsteps.push((data, stack) => {
|
|
219
|
+
if (dataBits(data) !== T_ARRAY) return true;
|
|
220
|
+
for (let i = start; i < data.length; i++) if (!fv(data[i], stack)) return false;
|
|
221
|
+
return true;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (P.contains !== undefined) {
|
|
225
|
+
const fn = compileNode(interp, P.contains, base, memo);
|
|
226
|
+
const minC = P.minContains !== undefined ? P.minContains : 1;
|
|
227
|
+
const maxC = P.maxContains;
|
|
228
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
229
|
+
if (dataBits(data) !== T_ARRAY) return true;
|
|
230
|
+
let matched = 0;
|
|
231
|
+
for (let i = 0; i < data.length; i++) {
|
|
232
|
+
if (fn.v(data[i], stack)) matched++;
|
|
233
|
+
}
|
|
234
|
+
let ok = true;
|
|
235
|
+
if (matched < minC) {
|
|
236
|
+
if (errors !== NOERRORS) errors.push(err('contains', 'contains', instancePath, schemaPath + '/contains', { minContains: minC }, `must contain at least ${minC} valid item(s)`));
|
|
237
|
+
ok = false;
|
|
238
|
+
}
|
|
239
|
+
if (maxC !== undefined && matched > maxC) {
|
|
240
|
+
if (errors !== NOERRORS) errors.push(err('maxContains', 'maxContains', instancePath, schemaPath + '/maxContains', { limit: maxC }, `must NOT contain more than ${maxC} valid item(s)`));
|
|
241
|
+
ok = false;
|
|
242
|
+
}
|
|
243
|
+
return ok;
|
|
244
|
+
});
|
|
245
|
+
vsteps.push((data, stack) => {
|
|
246
|
+
if (dataBits(data) !== T_ARRAY) return true;
|
|
247
|
+
let matched = 0;
|
|
248
|
+
for (let i = 0; i < data.length; i++) {
|
|
249
|
+
if (fn.v(data[i], stack)) { matched++; if (maxC === undefined && matched >= minC) return true; }
|
|
250
|
+
}
|
|
251
|
+
return matched >= minC && (maxC === undefined || matched <= maxC);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Objects
|
|
256
|
+
if (P.properties !== null || P.patternProperties !== null || P.additionalProperties !== undefined || P.propertyNames !== undefined) {
|
|
257
|
+
const props = P.properties;
|
|
258
|
+
const propFns = props !== null ? new Map() : null;
|
|
259
|
+
if (props !== null) {
|
|
260
|
+
for (const [key, entry] of props) propFns.set(key, { fn: compileNode(interp, entry.node, base, memo), seg: entry.seg, schemaSeg: entry.schemaSeg });
|
|
261
|
+
}
|
|
262
|
+
const patterns = P.patternProperties !== null
|
|
263
|
+
? P.patternProperties.map((e) => ({ re: e.re, src: e.src, fn: compileNode(interp, e.node, base, memo) }))
|
|
264
|
+
: null;
|
|
265
|
+
const apFn = P.additionalProperties !== undefined ? compileNode(interp, P.additionalProperties, base, memo) : null;
|
|
266
|
+
const pnFn = P.propertyNames !== undefined ? compileNode(interp, P.propertyNames, base, memo) : null;
|
|
267
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
268
|
+
if (dataBits(data) !== T_OBJECT) return true;
|
|
269
|
+
let ok = true;
|
|
270
|
+
const keys = Object.keys(data);
|
|
271
|
+
if (pnFn !== null) {
|
|
272
|
+
for (const key of keys) {
|
|
273
|
+
if (!pnFn.c(key, errors, instancePath + '/' + escapePointer(key), schemaPath + '/propertyNames', stack)) {
|
|
274
|
+
ok = false;
|
|
275
|
+
if (errors === NOERRORS) return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
for (let k = 0; k < keys.length; k++) {
|
|
280
|
+
const key = keys[k];
|
|
281
|
+
let evaluated = false;
|
|
282
|
+
const prop = propFns !== null ? propFns.get(key) : undefined;
|
|
283
|
+
if (prop !== undefined) {
|
|
284
|
+
if (!prop.fn.c(data[key], errors, instancePath + prop.seg, schemaPath + prop.schemaSeg, stack)) {
|
|
285
|
+
ok = false;
|
|
286
|
+
if (errors === NOERRORS) return false;
|
|
287
|
+
}
|
|
288
|
+
evaluated = true;
|
|
289
|
+
}
|
|
290
|
+
if (patterns !== null) {
|
|
291
|
+
for (let pi = 0; pi < patterns.length; pi++) {
|
|
292
|
+
const pp = patterns[pi];
|
|
293
|
+
if (pp.re.test(key)) {
|
|
294
|
+
if (!pp.fn.c(data[key], errors, instancePath + '/' + escapePointer(key), schemaPath + '/patternProperties/' + escapePointer(pp.src), stack)) {
|
|
295
|
+
ok = false;
|
|
296
|
+
if (errors === NOERRORS) return false;
|
|
297
|
+
}
|
|
298
|
+
evaluated = true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (!evaluated && apFn !== null) {
|
|
303
|
+
if (!apFn.c(data[key], errors, instancePath + '/' + escapePointer(key), schemaPath + '/additionalProperties', stack)) {
|
|
304
|
+
ok = false;
|
|
305
|
+
if (errors === NOERRORS) return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return ok;
|
|
310
|
+
});
|
|
311
|
+
vsteps.push((data, stack) => {
|
|
312
|
+
if (dataBits(data) !== T_OBJECT) return true;
|
|
313
|
+
const keys = Object.keys(data);
|
|
314
|
+
if (pnFn !== null) {
|
|
315
|
+
for (const key of keys) if (!pnFn.v(key, stack)) return false;
|
|
316
|
+
}
|
|
317
|
+
for (let k = 0; k < keys.length; k++) {
|
|
318
|
+
const key = keys[k];
|
|
319
|
+
let evaluated = false;
|
|
320
|
+
const prop = propFns !== null ? propFns.get(key) : undefined;
|
|
321
|
+
if (prop !== undefined) {
|
|
322
|
+
if (!prop.fn.v(data[key], stack)) return false;
|
|
323
|
+
evaluated = true;
|
|
324
|
+
}
|
|
325
|
+
if (patterns !== null) {
|
|
326
|
+
for (let pi = 0; pi < patterns.length; pi++) {
|
|
327
|
+
const pp = patterns[pi];
|
|
328
|
+
if (pp.re.test(key)) {
|
|
329
|
+
if (!pp.fn.v(data[key], stack)) return false;
|
|
330
|
+
evaluated = true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (!evaluated && apFn !== null) {
|
|
335
|
+
if (!apFn.v(data[key], stack)) return false;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return true;
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
if (P.dependentSchemas !== null) {
|
|
342
|
+
const entries = P.dependentSchemas.map(([key, v]) => [key, compileNode(interp, v, base, memo), escapePointer(key)]);
|
|
343
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
344
|
+
if (dataBits(data) !== T_OBJECT) return true;
|
|
345
|
+
let ok = true;
|
|
346
|
+
for (const [key, fn, ek] of entries) {
|
|
347
|
+
if (Object.hasOwn(data, key)) {
|
|
348
|
+
if (!fn.c(data, errors, instancePath, schemaPath + '/dependentSchemas/' + ek, stack)) {
|
|
349
|
+
ok = false;
|
|
350
|
+
if (errors === NOERRORS) return false;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return ok;
|
|
355
|
+
});
|
|
356
|
+
vsteps.push((data, stack) => {
|
|
357
|
+
if (dataBits(data) !== T_OBJECT) return true;
|
|
358
|
+
for (const [key, fn] of entries) {
|
|
359
|
+
if (Object.hasOwn(data, key) && !fn.v(data, stack)) return false;
|
|
360
|
+
}
|
|
361
|
+
return true;
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
if (P.propertyDependencies !== null) {
|
|
365
|
+
const entries = P.propertyDependencies.map(([key, choices]) => {
|
|
366
|
+
const m = new Map();
|
|
367
|
+
for (const [value, v] of choices) m.set(value, compileNode(interp, v, base, memo));
|
|
368
|
+
return [key, m];
|
|
369
|
+
});
|
|
370
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
371
|
+
if (dataBits(data) !== T_OBJECT) return true;
|
|
372
|
+
let ok = true;
|
|
373
|
+
for (const [key, choices] of entries) {
|
|
374
|
+
if (!Object.hasOwn(data, key)) continue;
|
|
375
|
+
const value = data[key];
|
|
376
|
+
if (typeof value !== 'string') continue;
|
|
377
|
+
const fn = choices.get(value);
|
|
378
|
+
if (fn === undefined) continue;
|
|
379
|
+
const branchPath = schemaPath + '/propertyDependencies/' + escapePointer(key) + '/' + escapePointer(value);
|
|
380
|
+
if (!fn.c(data, errors, instancePath, branchPath, stack)) {
|
|
381
|
+
ok = false;
|
|
382
|
+
if (errors === NOERRORS) return false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return ok;
|
|
386
|
+
});
|
|
387
|
+
vsteps.push((data, stack) => {
|
|
388
|
+
if (dataBits(data) !== T_OBJECT) return true;
|
|
389
|
+
for (const [key, choices] of entries) {
|
|
390
|
+
if (!Object.hasOwn(data, key)) continue;
|
|
391
|
+
const value = data[key];
|
|
392
|
+
if (typeof value !== 'string') continue;
|
|
393
|
+
const fn = choices.get(value);
|
|
394
|
+
if (fn !== undefined && !fn.v(data, stack)) return false;
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// In-place applicators
|
|
401
|
+
if (P.allOf !== null) {
|
|
402
|
+
const fns = P.allOf.map((v) => compileNode(interp, v, base, memo));
|
|
403
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
404
|
+
let ok = true;
|
|
405
|
+
for (let i = 0; i < fns.length; i++) {
|
|
406
|
+
if (!fns[i].c(data, errors, instancePath, schemaPath + '/allOf/' + i, stack)) {
|
|
407
|
+
ok = false;
|
|
408
|
+
if (errors === NOERRORS) return false;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return ok;
|
|
412
|
+
});
|
|
413
|
+
vsteps.push((data, stack) => {
|
|
414
|
+
for (let i = 0; i < fns.length; i++) if (!fns[i].v(data, stack)) return false;
|
|
415
|
+
return true;
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
if (P.anyOf !== null) {
|
|
419
|
+
const fns = P.anyOf.map((v) => compileNode(interp, v, base, memo));
|
|
420
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
421
|
+
const scratch = errors === NOERRORS ? NOERRORS : [];
|
|
422
|
+
let any = false;
|
|
423
|
+
for (let i = 0; i < fns.length; i++) {
|
|
424
|
+
if (fns[i].c(data, scratch, instancePath, schemaPath + '/anyOf/' + i, stack)) any = true;
|
|
425
|
+
}
|
|
426
|
+
if (!any) {
|
|
427
|
+
if (errors !== NOERRORS) {
|
|
428
|
+
for (const e of scratch) errors.push(e);
|
|
429
|
+
errors.push(err('anyOf', 'anyOf', instancePath, schemaPath + '/anyOf', {}, 'must match a schema in anyOf'));
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
});
|
|
435
|
+
vsteps.push((data, stack) => {
|
|
436
|
+
for (let i = 0; i < fns.length; i++) if (fns[i].v(data, stack)) return true;
|
|
437
|
+
return false;
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
if (P.oneOf !== null) {
|
|
441
|
+
const fns = P.oneOf.map((v) => compileNode(interp, v, base, memo));
|
|
442
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
443
|
+
const scratch = errors === NOERRORS ? NOERRORS : [];
|
|
444
|
+
let count = 0;
|
|
445
|
+
for (let i = 0; i < fns.length; i++) {
|
|
446
|
+
if (fns[i].c(data, scratch, instancePath, schemaPath + '/oneOf/' + i, stack)) count++;
|
|
447
|
+
}
|
|
448
|
+
if (count !== 1) {
|
|
449
|
+
if (errors !== NOERRORS) {
|
|
450
|
+
if (count === 0) for (const e of scratch) errors.push(e);
|
|
451
|
+
errors.push(err('oneOf', 'oneOf', instancePath, schemaPath + '/oneOf', { passingSchemas: count }, 'must match exactly one schema in oneOf'));
|
|
452
|
+
}
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
return true;
|
|
456
|
+
});
|
|
457
|
+
vsteps.push((data, stack) => {
|
|
458
|
+
let count = 0;
|
|
459
|
+
for (let i = 0; i < fns.length; i++) {
|
|
460
|
+
if (fns[i].v(data, stack)) { count++; if (count > 1) return false; }
|
|
461
|
+
}
|
|
462
|
+
return count === 1;
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
if (P.not !== undefined) {
|
|
466
|
+
const fn = compileNode(interp, P.not, base, memo);
|
|
467
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
468
|
+
if (fn.v(data, stack)) {
|
|
469
|
+
if (errors !== NOERRORS) errors.push(err('not', 'not', instancePath, schemaPath + '/not', {}, 'must NOT be valid'));
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
return true;
|
|
473
|
+
});
|
|
474
|
+
vsteps.push((data, stack) => !fn.v(data, stack));
|
|
475
|
+
}
|
|
476
|
+
if (P.if !== undefined) {
|
|
477
|
+
const ifFn = compileNode(interp, P.if, base, memo);
|
|
478
|
+
const thenFn = P.then !== undefined ? compileNode(interp, P.then, base, memo) : null;
|
|
479
|
+
const elseFn = P.else !== undefined ? compileNode(interp, P.else, base, memo) : null;
|
|
480
|
+
steps.push((data, errors, instancePath, schemaPath, stack) => {
|
|
481
|
+
if (ifFn.v(data, stack)) {
|
|
482
|
+
if (thenFn !== null) return thenFn.c(data, errors, instancePath, schemaPath + '/then', stack);
|
|
483
|
+
} else if (elseFn !== null) {
|
|
484
|
+
return elseFn.c(data, errors, instancePath, schemaPath + '/else', stack);
|
|
485
|
+
}
|
|
486
|
+
return true;
|
|
487
|
+
});
|
|
488
|
+
vsteps.push((data, stack) => {
|
|
489
|
+
if (ifFn.v(data, stack)) {
|
|
490
|
+
if (thenFn !== null) return thenFn.v(data, stack);
|
|
491
|
+
} else if (elseFn !== null) {
|
|
492
|
+
return elseFn.v(data, stack);
|
|
493
|
+
}
|
|
494
|
+
return true;
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
let cfn;
|
|
499
|
+
if (steps.length === 0) cfn = TRUE_FN;
|
|
500
|
+
else if (steps.length === 1) cfn = steps[0];
|
|
501
|
+
else {
|
|
502
|
+
const arr = steps;
|
|
503
|
+
cfn = (d, e, ip, sp, st) => {
|
|
504
|
+
let ok = true;
|
|
505
|
+
for (let i = 0; i < arr.length; i++) {
|
|
506
|
+
if (!arr[i](d, e, ip, sp, st)) {
|
|
507
|
+
if (e === NOERRORS) return false;
|
|
508
|
+
ok = false;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return ok;
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
let vfn;
|
|
515
|
+
if (vsteps.length === 0) vfn = TRUE_FN;
|
|
516
|
+
else if (vsteps.length === 1) vfn = vsteps[0];
|
|
517
|
+
else if (vsteps.length === 2) {
|
|
518
|
+
const [a, b] = vsteps;
|
|
519
|
+
vfn = (d, st) => a(d, st) && b(d, st);
|
|
520
|
+
} else {
|
|
521
|
+
const arr = vsteps;
|
|
522
|
+
vfn = (d, st) => {
|
|
523
|
+
for (let i = 0; i < arr.length; i++) if (!arr[i](d, st)) return false;
|
|
524
|
+
return true;
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
box.c = cfn;
|
|
528
|
+
box.v = vfn;
|
|
529
|
+
pair.c = cfn;
|
|
530
|
+
pair.v = vfn;
|
|
531
|
+
perBase.set(base, pair);
|
|
532
|
+
return pair;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Entry point: returns a compiled root function or null when the schema is
|
|
536
|
+
// outside the compiler's scope.
|
|
537
|
+
function compileInterpreter(interp) {
|
|
538
|
+
if (!compilable(interp, interp.rootNode, interp.state.rootBase, new Map())) return null;
|
|
539
|
+
return compileNode(interp, interp.rootNode, interp.state.rootBase, new Map());
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return { compileInterpreter };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
module.exports = { install };
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "JSON Schema validation with first-class TypeScript and zero runtime cost. AOT compile to per-schema ESM modules with zero validator dependency. Generic Validator<T> for TypeBox/Zod/Valibot composition. Optional runtime API. Standard Schema V1 compatible.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"release:check": "node scripts/regen-safe-regex-source.js && node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_source_sync.js && node tests/test_version_sync.js",
|
|
45
45
|
"build": "cmake-js build --target ata",
|
|
46
46
|
"rebuild": "cmake-js rebuild --target ata",
|
|
47
|
-
"test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_buffer_gate.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_pattern_properties_errors.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
|
|
47
|
+
"test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_buffer_gate.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_pattern_properties_errors.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_lazy_errors.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
|
|
48
48
|
"bench:size": "node benchmark/bench_aot_size.mjs",
|
|
49
49
|
"test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
|
|
50
50
|
"test:compat": "node tests/test_compat.js",
|
|
@@ -112,12 +112,12 @@
|
|
|
112
112
|
"LICENSE"
|
|
113
113
|
],
|
|
114
114
|
"optionalDependencies": {
|
|
115
|
-
"@ata-validator/native-darwin-arm64": "1.7.
|
|
116
|
-
"@ata-validator/native-linux-x64-gnu": "1.7.
|
|
117
|
-
"@ata-validator/native-linux-arm64-gnu": "1.7.
|
|
118
|
-
"@ata-validator/native-linux-x64-musl": "1.7.
|
|
119
|
-
"@ata-validator/native-linux-arm64-musl": "1.7.
|
|
120
|
-
"@ata-validator/native-win32-x64": "1.7.
|
|
115
|
+
"@ata-validator/native-darwin-arm64": "1.7.1",
|
|
116
|
+
"@ata-validator/native-linux-x64-gnu": "1.7.1",
|
|
117
|
+
"@ata-validator/native-linux-arm64-gnu": "1.7.1",
|
|
118
|
+
"@ata-validator/native-linux-x64-musl": "1.7.1",
|
|
119
|
+
"@ata-validator/native-linux-arm64-musl": "1.7.1",
|
|
120
|
+
"@ata-validator/native-win32-x64": "1.7.1"
|
|
121
121
|
},
|
|
122
122
|
"peerDependencies": {
|
|
123
123
|
"yaml": "^2.0.0"
|