ata-validator 1.62.1 → 1.63.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -8
- package/build.mjs +1 -0
- package/index.browser.mjs +23 -4
- package/index.bundle.mjs +14 -4
- package/index.edge.mjs +14 -4
- package/index.js +7 -2
- package/lib/aot-build.js +18 -0
- package/lib/codegen-engine.js +72 -3
- package/lib/compiled-common.js +2 -1
- package/lib/email-literal.js +4 -1
- package/lib/formats-source.js +39 -40
- package/lib/interpreter-core.js +148 -70
- package/lib/ir/emit-deps.mjs +131 -0
- package/lib/ir/emit.mjs +707 -0
- package/lib/ir/front.mjs +865 -0
- package/lib/ir/index.mjs +10 -0
- package/lib/ir/refs.mjs +108 -0
- package/lib/ir/run.mjs +520 -0
- package/lib/ir/table.mjs +233 -0
- package/lib/js-compiler-errors.js +9 -4
- package/lib/kernels/collapse.mjs +114 -0
- package/lib/kernels/custom.mjs +70 -0
- package/lib/kernels/fill.mjs +6 -0
- package/lib/kernels/formats-small.mjs +40 -0
- package/lib/kernels/formats.mjs +29 -0
- package/lib/kernels/index.mjs +11 -0
- package/lib/kernels/kernels.mjs +285 -0
- package/lib/kernels/leaves.mjs +28 -0
- package/lib/kernels/order.mjs +25 -0
- package/lib/kernels/plan-rich.mjs +17 -0
- package/lib/kernels/plan.d.ts +9 -0
- package/lib/kernels/plan.mjs +128 -0
- package/lib/kernels/preprocess.mjs +223 -0
- package/lib/kernels/rank.mjs +97 -0
- package/lib/kernels/rt.mjs +249 -0
- package/lib/kernels/table.mjs +182 -0
- package/lib/kernels/unique.mjs +157 -0
- package/lib/native-positions.js +1 -1
- package/lib/openapi.d.ts +2 -0
- package/lib/openapi.js +128 -0
- package/lib/plan-compiler.js +113 -14
- package/lib/public-api.js +7 -2
- package/lib/rejections.js +3 -1
- package/lib/schema-helpers.js +12 -0
- package/lib/schema-helpers.mjs +20 -0
- package/lib/schema-order.js +4 -2
- package/lib/shape-classifier.js +13 -1
- package/lib/standard-schema.js +1 -1
- package/lib/tier0.js +38 -8
- package/lib/validator-core.js +59 -36
- package/lib/version.js +1 -1
- package/lite.js +7 -2
- package/package.json +26 -8
package/lib/plan-compiler.js
CHANGED
|
@@ -56,6 +56,21 @@ function install(deps) {
|
|
|
56
56
|
T_STRING, T_NUMBER, T_INTEGER, T_BOOLEAN, T_NULL, T_ARRAY, T_OBJECT, resolveRef, splitFragment, resolveUri } = deps;
|
|
57
57
|
|
|
58
58
|
const TRUE_FN = () => true;
|
|
59
|
+
// Leaf codes (leafCode in compileLeafV): a string with lengths and a
|
|
60
|
+
// pattern, an integer or a number with minimum and maximum, a boolean, an
|
|
61
|
+
// enum of primitives, a primitive const. Each answers as the closure it
|
|
62
|
+
// stands for.
|
|
63
|
+
const LC_STR = 1, LC_INT = 2, LC_NUM = 3, LC_BOOL = 4, LC_ENUM = 5, LC_CONST = 6;
|
|
64
|
+
function leafOk(c, d) {
|
|
65
|
+
switch (c[0]) {
|
|
66
|
+
case LC_STR: return typeof d === 'string' && (c[1] === -1 || cpAtLeast(d, c[1])) && (c[2] === -1 || cpAtMost(d, c[2])) && (c[3] === null || c[3].test(d));
|
|
67
|
+
case LC_INT: case LC_NUM: return (c[0] === LC_INT ? Number.isInteger(d) : typeof d === 'number' && isFinite(d)) && d >= c[1] && d <= c[2];
|
|
68
|
+
case LC_BOOL: return typeof d === 'boolean';
|
|
69
|
+
case LC_ENUM: return c[1].has(d);
|
|
70
|
+
default: return d === c[1];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
59
74
|
// `leaf`: the node compiled no subschema of its own (see compileNode). A
|
|
60
75
|
// collecting caller asks a leaf for its verdict first and builds the paths
|
|
61
76
|
// for its errors only when it fails.
|
|
@@ -92,6 +107,7 @@ function install(deps) {
|
|
|
92
107
|
function fresh() { return { props: null, n: 0, x: null, keys: null }; }
|
|
93
108
|
// Object.keys once per record: every node that shares a record sees the
|
|
94
109
|
// same data object.
|
|
110
|
+
const propEnum = Object.prototype.propertyIsEnumerable;
|
|
95
111
|
function keysOf(rec, data) {
|
|
96
112
|
if (rec.keys === null) rec.keys = Object.keys(data);
|
|
97
113
|
return rec.keys;
|
|
@@ -183,11 +199,17 @@ function install(deps) {
|
|
|
183
199
|
// chain of guarded checks: the interpreted engine spent a fifth of a
|
|
184
200
|
// document in those calls.
|
|
185
201
|
const reqFolded = P.required !== null && P.requiredUndef === null && P.properties !== null;
|
|
202
|
+
// The operands of the commonest leaves, for an object walk that checks
|
|
203
|
+
// them in place (leafCode below): bounds that are only minimum and
|
|
204
|
+
// maximum, and string checks that are only lengths and a pattern.
|
|
205
|
+
let nLo = -Infinity, nHi = Infinity, nPlain = true;
|
|
206
|
+
let sLo = -1, sHi = -1, sRe = null, sPlain = true;
|
|
186
207
|
let numsRun = null;
|
|
187
208
|
if (P.hasNumber) {
|
|
188
209
|
const nums = [];
|
|
189
|
-
if (P.minimum !== undefined) { const m = P.minimum; nums.push((d) => d >= m); }
|
|
190
|
-
if (P.maximum !== undefined) { const m = P.maximum; nums.push((d) => d <= m); }
|
|
210
|
+
if (P.minimum !== undefined) { const m = P.minimum; nLo = m; nums.push((d) => d >= m); }
|
|
211
|
+
if (P.maximum !== undefined) { const m = P.maximum; nHi = m; nums.push((d) => d <= m); }
|
|
212
|
+
if (P.exclusiveMinimum !== undefined || P.exclusiveMaximum !== undefined || P.multipleOf !== undefined) nPlain = false;
|
|
191
213
|
if (P.exclusiveMinimum !== undefined) { const m = P.exclusiveMinimum; nums.push((d) => d > m); }
|
|
192
214
|
if (P.exclusiveMaximum !== undefined) { const m = P.exclusiveMaximum; nums.push((d) => d < m); }
|
|
193
215
|
if (P.multipleOf !== undefined) { const m = P.multipleOf; nums.push((d) => multipleOfOk(d, m)); }
|
|
@@ -196,6 +218,10 @@ function install(deps) {
|
|
|
196
218
|
let strsRun = null;
|
|
197
219
|
if (P.hasString) {
|
|
198
220
|
const strs = [];
|
|
221
|
+
if (P.minLength !== undefined) sLo = P.minLength;
|
|
222
|
+
if (P.maxLength !== undefined) sHi = P.maxLength;
|
|
223
|
+
if (P.pattern !== null) sRe = P.pattern;
|
|
224
|
+
if (P.formatFn !== null) sPlain = false;
|
|
199
225
|
if (P.minLength !== undefined && P.maxLength !== undefined) {
|
|
200
226
|
const lo = P.minLength, hi = P.maxLength;
|
|
201
227
|
strs.push((d) => { const n = d.length; return (n >= 2 * lo && n <= hi) || (cpAtLeast(d, lo) && cpAtMost(d, hi)); });
|
|
@@ -254,10 +280,10 @@ function install(deps) {
|
|
|
254
280
|
if (!foreign) {
|
|
255
281
|
const r = own;
|
|
256
282
|
switch (m) {
|
|
257
|
-
case T_STRING: return r === null ? (d) => typeof d === 'string' : (d) => typeof d === 'string' && r(d);
|
|
258
|
-
case T_INTEGER: return r === null ? (d) => Number.isInteger(d) : (d) => Number.isInteger(d) && r(d);
|
|
259
|
-
case T_NUMBER: return r === null ? (d) => typeof d === 'number' && isFinite(d) : (d) => typeof d === 'number' && isFinite(d) && r(d);
|
|
260
|
-
case T_BOOLEAN: return (d) => typeof d === 'boolean';
|
|
283
|
+
case T_STRING: return leafCode(r === null ? (d) => typeof d === 'string' : (d) => typeof d === 'string' && r(d), sPlain ? [LC_STR, sLo, sHi, sRe] : null);
|
|
284
|
+
case T_INTEGER: return leafCode(r === null ? (d) => Number.isInteger(d) : (d) => Number.isInteger(d) && r(d), nPlain ? [LC_INT, nLo, nHi] : null);
|
|
285
|
+
case T_NUMBER: return leafCode(r === null ? (d) => typeof d === 'number' && isFinite(d) : (d) => typeof d === 'number' && isFinite(d) && r(d), nPlain ? [LC_NUM, nLo, nHi] : null);
|
|
286
|
+
case T_BOOLEAN: return leafCode((d) => typeof d === 'boolean', [LC_BOOL]);
|
|
261
287
|
case T_NULL: return (d) => d === null;
|
|
262
288
|
case T_OBJECT: return r === null ? (d) => typeof d === 'object' && d !== null && !Array.isArray(d) : (d) => typeof d === 'object' && d !== null && !Array.isArray(d) && r(d);
|
|
263
289
|
case T_ARRAY: return r === null ? (d) => Array.isArray(d) : (d) => Array.isArray(d) && r(d);
|
|
@@ -279,7 +305,16 @@ function install(deps) {
|
|
|
279
305
|
}
|
|
280
306
|
if (checks.length === 0) return TRUE_FN;
|
|
281
307
|
if (!needsBits) {
|
|
282
|
-
if (checks.length === 1) {
|
|
308
|
+
if (checks.length === 1) {
|
|
309
|
+
const a = checks[0];
|
|
310
|
+
const fn = (data) => a(data, 0);
|
|
311
|
+
// An enum of primitives alone, or a primitive const alone.
|
|
312
|
+
if (!P.hasType && !P.hasCustom && numsRun === null && strsRun === null && arrsRun === null && objsRun === null) {
|
|
313
|
+
if (P.enum !== null && P.enum.length > 0 && P.enumObjs.length === 0) return leafCode(fn, [LC_ENUM, P.enumPrims]);
|
|
314
|
+
if (P.hasConst && P.enum === null && (P.const === null || typeof P.const !== 'object')) return leafCode(fn, [LC_CONST, P.const]);
|
|
315
|
+
}
|
|
316
|
+
return fn;
|
|
317
|
+
}
|
|
283
318
|
return (data) => {
|
|
284
319
|
for (let i = 0; i < checks.length; i++) if (!checks[i](data, 0)) return false;
|
|
285
320
|
return true;
|
|
@@ -297,6 +332,16 @@ function install(deps) {
|
|
|
297
332
|
};
|
|
298
333
|
}
|
|
299
334
|
|
|
335
|
+
// A leaf check that an object walk may run in place, by its code and
|
|
336
|
+
// operands, instead of calling it: one call site calling every property's
|
|
337
|
+
// check cannot be inlined, and on a form the calls were most of the
|
|
338
|
+
// verdict. leafOk must answer as the function does; tests/test_plan_leaf_codes.js
|
|
339
|
+
// holds the two to each other.
|
|
340
|
+
function leafCode(fn, code) {
|
|
341
|
+
if (code !== null) fn.lc = code;
|
|
342
|
+
return fn;
|
|
343
|
+
}
|
|
344
|
+
|
|
300
345
|
// Folds a group's checks into one call, or null when the group is empty.
|
|
301
346
|
function combineValue(fns) {
|
|
302
347
|
if (fns.length === 0) return null;
|
|
@@ -565,9 +610,10 @@ function install(deps) {
|
|
|
565
610
|
steps.push((data, errors, instancePath, schemaPath, stack, rec) => {
|
|
566
611
|
if (dataBits(data) !== T_OBJECT) return true;
|
|
567
612
|
let ok = true;
|
|
568
|
-
const keys = collect ? keysOf(rec, data) : Object.
|
|
613
|
+
const keys = collect ? keysOf(rec, data) : Object.getOwnPropertyNames(data);
|
|
569
614
|
if (pnFn !== null) {
|
|
570
615
|
for (const k of keys) {
|
|
616
|
+
if (!collect && !propEnum.call(data, k)) continue;
|
|
571
617
|
// propertyNames applies its subschema to the property NAME, not to
|
|
572
618
|
// the value sitting at that key, so the failure is reported against
|
|
573
619
|
// the object that carries the name rather than against `/<key>`.
|
|
@@ -599,7 +645,11 @@ function install(deps) {
|
|
|
599
645
|
}
|
|
600
646
|
evaluated = true;
|
|
601
647
|
}
|
|
602
|
-
|
|
648
|
+
// A name only properties lists is read whether enumerable or not,
|
|
649
|
+
// as the short walk above and the generated code read it by name;
|
|
650
|
+
// patterns and additionalProperties see the names for-in sees.
|
|
651
|
+
const seen = collect || prop !== undefined && patterns === null && apFn === null || propEnum.call(data, key);
|
|
652
|
+
if (patterns !== null && seen) {
|
|
603
653
|
for (let pi = 0; pi < patterns.length; pi++) {
|
|
604
654
|
const pp = patterns[pi];
|
|
605
655
|
if (pp.re.test(key)) {
|
|
@@ -611,7 +661,7 @@ function install(deps) {
|
|
|
611
661
|
}
|
|
612
662
|
}
|
|
613
663
|
}
|
|
614
|
-
if (!evaluated && apFn !== null) {
|
|
664
|
+
if (!evaluated && apFn !== null && seen) {
|
|
615
665
|
if (apFalse) {
|
|
616
666
|
ok = false;
|
|
617
667
|
if (errors === NOERRORS) return false;
|
|
@@ -671,9 +721,9 @@ function install(deps) {
|
|
|
671
721
|
vsteps.push((data, stack, rec) => {
|
|
672
722
|
if (fusedLeaf !== null && !fusedLeaf(data)) return false;
|
|
673
723
|
if (dataBits(data) !== T_OBJECT) return true;
|
|
674
|
-
const keys = collect ? keysOf(rec, data) : Object.
|
|
724
|
+
const keys = collect ? keysOf(rec, data) : Object.getOwnPropertyNames(data);
|
|
675
725
|
if (pnFn !== null) {
|
|
676
|
-
for (const k of keys) if (!pnFn.v(k, stack)) return false;
|
|
726
|
+
for (const k of keys) if ((collect || propEnum.call(data, k)) && !pnFn.v(k, stack)) return false;
|
|
677
727
|
}
|
|
678
728
|
let seenReq = 0;
|
|
679
729
|
for (let k = 0; k < keys.length; k++) {
|
|
@@ -687,7 +737,8 @@ function install(deps) {
|
|
|
687
737
|
if (prop.req) seenReq++;
|
|
688
738
|
evaluated = true;
|
|
689
739
|
}
|
|
690
|
-
|
|
740
|
+
const seen = collect || prop !== undefined && patterns === null && apFn === null || propEnum.call(data, key);
|
|
741
|
+
if (patterns !== null && seen) {
|
|
691
742
|
for (let pi = 0; pi < patterns.length; pi++) {
|
|
692
743
|
const pp = patterns[pi];
|
|
693
744
|
if (pp.re.test(key)) {
|
|
@@ -696,7 +747,7 @@ function install(deps) {
|
|
|
696
747
|
}
|
|
697
748
|
}
|
|
698
749
|
}
|
|
699
|
-
if (!evaluated && apFn !== null) {
|
|
750
|
+
if (!evaluated && apFn !== null && seen) {
|
|
700
751
|
if (!apFn.v(data[key], stack)) return false;
|
|
701
752
|
evaluated = true;
|
|
702
753
|
}
|
|
@@ -704,6 +755,54 @@ function install(deps) {
|
|
|
704
755
|
}
|
|
705
756
|
return seenReq === reqN;
|
|
706
757
|
});
|
|
758
|
+
// From three names to 32: below three the key array costs less than
|
|
759
|
+
// the for-in (1.7 ns on one name), past 32 the scan costs more.
|
|
760
|
+
if (apFalse && patterns === null && pnFn === null && !collect && propKeys !== null && propKeys.length >= 3 && propKeys.length <= 32) {
|
|
761
|
+
// A closed object with a short list of names, on an ordinary
|
|
762
|
+
// object: walk its keys with for-in and find each in the list, no
|
|
763
|
+
// key array and no Map. A key the list lacks is extra when it is
|
|
764
|
+
// the object's own (an enumerable name on Object.prototype is not).
|
|
765
|
+
// A listed key is read directly: for-in meets Object.prototype's
|
|
766
|
+
// own names (`constructor`) only on the object itself, so this is
|
|
767
|
+
// the short walk's rule. Other objects take the walk above.
|
|
768
|
+
const keyed = vsteps.pop();
|
|
769
|
+
const n = propKeys.length;
|
|
770
|
+
const fns = propList.map((e) => e.fn);
|
|
771
|
+
const reqs = propList.map((e) => e.req);
|
|
772
|
+
// Names met, as bits: the required ones must all be set, and a
|
|
773
|
+
// listed name not met may still be the object's own, hidden from
|
|
774
|
+
// for-in (defineProperty, enumerable: false), which the generated
|
|
775
|
+
// code and this engine's error path read by name: those are read
|
|
776
|
+
// after the walk, which on an ordinary document is the absent ones.
|
|
777
|
+
const reqMask = reqs.reduce((m, r, i) => r ? m | (1 << i) : m, 0);
|
|
778
|
+
const allMask = n === 32 ? -1 : (1 << n) - 1;
|
|
779
|
+
// A property whose check is a coded leaf is checked in place.
|
|
780
|
+
const codes = propList.map((e) => (e.fn.leaf && e.fn.v.lc !== undefined ? e.fn.v.lc : null));
|
|
781
|
+
vsteps.push((data, stack, rec) => {
|
|
782
|
+
if (dataBits(data) !== T_OBJECT || Object.getPrototypeOf(data) !== Object.prototype) return keyed(data, stack, rec);
|
|
783
|
+
if (fusedLeaf !== null && !fusedLeaf(data)) return false;
|
|
784
|
+
let seen = 0;
|
|
785
|
+
for (const key in data) {
|
|
786
|
+
let i = 0;
|
|
787
|
+
while (i < n && propKeys[i] !== key) i++;
|
|
788
|
+
if (i === n) { if (Object.hasOwn(data, key)) return false; continue; }
|
|
789
|
+
seen |= 1 << i;
|
|
790
|
+
const value = data[key];
|
|
791
|
+
if (value === undefined) { if (reqs[i]) return false; continue; }
|
|
792
|
+
const lc = codes[i];
|
|
793
|
+
if (lc !== null ? !leafOk(lc, value) : !fns[i].v(value, stack)) return false;
|
|
794
|
+
}
|
|
795
|
+
if ((seen & reqMask) !== reqMask) return false;
|
|
796
|
+
for (let miss = seen ^ allMask; miss !== 0; miss &= miss - 1) {
|
|
797
|
+
const i = 31 - Math.clz32(miss & -miss);
|
|
798
|
+
const value = data[propKeys[i]];
|
|
799
|
+
if (value === undefined || !Object.hasOwn(data, propKeys[i])) continue;
|
|
800
|
+
const lc = codes[i];
|
|
801
|
+
if (lc !== null ? !leafOk(lc, value) : !fns[i].v(value, stack)) return false;
|
|
802
|
+
}
|
|
803
|
+
return true;
|
|
804
|
+
});
|
|
805
|
+
}
|
|
707
806
|
}
|
|
708
807
|
}
|
|
709
808
|
if (P.dependentSchemas !== null) {
|
package/lib/public-api.js
CHANGED
|
@@ -58,6 +58,11 @@ function attachSuggestions (errors, data) {
|
|
|
58
58
|
return errors;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// Loaded on first call, like the tools: a process that never calls them does
|
|
62
|
+
// not read the file.
|
|
63
|
+
function defineSchema (schema) { return require('./schema-helpers.js').defineSchema(schema); }
|
|
64
|
+
function schemaOf (...a) { return require('./schema-helpers.js').schemaOf(...a); }
|
|
65
|
+
|
|
61
66
|
module.exports = {
|
|
62
67
|
Validator: core.Validator,
|
|
63
68
|
compile: core.compile,
|
|
@@ -69,8 +74,8 @@ module.exports = {
|
|
|
69
74
|
SIMDJSON_PADDING: core.SIMDJSON_PADDING,
|
|
70
75
|
parseJSON: core.parseJSON,
|
|
71
76
|
toTypeScript,
|
|
72
|
-
defineSchema
|
|
73
|
-
schemaOf
|
|
77
|
+
defineSchema,
|
|
78
|
+
schemaOf,
|
|
74
79
|
renderPretty,
|
|
75
80
|
renderCompact,
|
|
76
81
|
toOutput,
|
package/lib/rejections.js
CHANGED
|
@@ -159,7 +159,9 @@ function stripOrdinal(e) {
|
|
|
159
159
|
// lib/branch-collapse.js builds, the only place these codes come from. Its
|
|
160
160
|
// fields are copied by name, in that order, without the ordinal: walking
|
|
161
161
|
// its keys with for-in was most of the cost of reading such an error.
|
|
162
|
-
|
|
162
|
+
// One that carries verbose's fields takes the copy of every key below,
|
|
163
|
+
// which keeps them; this one dropped them.
|
|
164
|
+
if (e.path !== undefined && e.docUrl === undefined && COLLAPSE_CODES.has(e.code) && !('parentSchema' in e)) {
|
|
163
165
|
const out = { code: e.code, keyword: e.keyword, instancePath: e.instancePath, path: e.path, schemaPath: e.schemaPath, message: e.message, params: e.params };
|
|
164
166
|
if ('branchErrors' in e) out.branchErrors = Array.isArray(e.branchErrors) ? e.branchErrors.map(stripOrdinal) : e.branchErrors;
|
|
165
167
|
return out;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// defineSchema and schemaOf outside the core; lib/schema-helpers.mjs says why.
|
|
4
|
+
function defineSchema (schema) {
|
|
5
|
+
return schema;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function schemaOf () {
|
|
9
|
+
throw new Error('schemaOf<T>() is replaced at build time by @ata-project/unplugin (with ts-json-schema-generator installed); it has no run-time form. Write the JSON Schema, or build with the plugin.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = { defineSchema, schemaOf };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The ES form of lib/schema-helpers.js, for the ES entries. Those entries read
|
|
2
|
+
// each core name through a call marked pure, so a module that imports only
|
|
3
|
+
// `defineSchema` leaves the validator core out of its bundle; a destructuring
|
|
4
|
+
// `export const { ... } = core` was kept whole, and a form whose validator the
|
|
5
|
+
// build plugin had compiled away still shipped the runtime through it. The core
|
|
6
|
+
// holds no copy, so a bundle carries the helpers once.
|
|
7
|
+
// tests/test_define_schema_bundle.js covers both forms.
|
|
8
|
+
|
|
9
|
+
// Identity at run time: it gives an inline schema its JSONSchema type
|
|
10
|
+
// (index.d.ts) for autocomplete and value checks while authoring.
|
|
11
|
+
export function defineSchema (schema) {
|
|
12
|
+
return schema;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Written in by @ata-project/unplugin from a TypeScript type at build time. A
|
|
16
|
+
// call that reaches run time was not replaced, and a loud error beats an
|
|
17
|
+
// empty schema that accepts everything.
|
|
18
|
+
export function schemaOf () {
|
|
19
|
+
throw new Error('schemaOf<T>() is replaced at build time by @ata-project/unplugin (with ts-json-schema-generator installed); it has no run-time form. Write the JSON Schema, or build with the plugin.');
|
|
20
|
+
}
|
package/lib/schema-order.js
CHANGED
|
@@ -140,8 +140,10 @@ function computeRank(rootSchema, schemaPath) {
|
|
|
140
140
|
function remoteTarget(docs, base, node) {
|
|
141
141
|
const ref = node.$ref;
|
|
142
142
|
if (typeof ref !== 'string' || ref.charCodeAt(0) === 35) return null;
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
// A name with no base to resolve against (a root without $id referencing
|
|
144
|
+
// `shared#`) is looked up as written, the way such documents are registered
|
|
145
|
+
// (schemas: [{ $id: 'shared' }]); it used to stop the rank there.
|
|
146
|
+
const abs = resolveUri(ref, base) || ref;
|
|
145
147
|
const hash = abs.indexOf('#');
|
|
146
148
|
const uri = hash < 0 ? abs : abs.slice(0, hash);
|
|
147
149
|
let target = docs.get(uri);
|
package/lib/shape-classifier.js
CHANGED
|
@@ -40,9 +40,21 @@ function isPrimitiveEnumValue(v) {
|
|
|
40
40
|
return v === null || t === 'string' || t === 'number' || t === 'boolean';
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// A node of `enum` or `const` alone, no type: the values decide, as the full
|
|
44
|
+
// engines decide it (enumMember, deepEqual), and every value is a primitive,
|
|
45
|
+
// so a Set lookup or a === is the whole check. Form fields are written this
|
|
46
|
+
// way often (`{ enum: ['TR', 'DE'] }`, `{ const: true }`), and one of them
|
|
47
|
+
// sent the whole object to the slower engines.
|
|
48
|
+
const TIER0_UNTYPED_ALLOWED = new Set(['enum', 'const', ...META_KEYS]);
|
|
49
|
+
|
|
43
50
|
function isTier0Primitive(schema) {
|
|
44
51
|
if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) return false;
|
|
45
|
-
if (!isPrimitiveType(schema.type))
|
|
52
|
+
if (!isPrimitiveType(schema.type)) {
|
|
53
|
+
if (schema.type !== undefined || (schema.enum === undefined && schema.const === undefined)) return false;
|
|
54
|
+
for (const k of Object.keys(schema)) {
|
|
55
|
+
if (!TIER0_UNTYPED_ALLOWED.has(k)) return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
46
58
|
for (const k of Object.keys(schema)) {
|
|
47
59
|
if (!TIER0_PRIMITIVE_ALLOWED.has(k)) return false;
|
|
48
60
|
}
|
package/lib/standard-schema.js
CHANGED
|
@@ -117,7 +117,7 @@ function standardSchemaFor(self, getSchema, validate) {
|
|
|
117
117
|
if (!message) {
|
|
118
118
|
// The native engine reports numeric codes without a message; the
|
|
119
119
|
// enrich pass knows how to word those. Rare, so required lazily.
|
|
120
|
-
|
|
120
|
+
{ const m = require('./native-message'); message = (typeof m === 'function' ? m : m.default)(err); }
|
|
121
121
|
}
|
|
122
122
|
issues[i] = { message, path: parsePointerPath(path) };
|
|
123
123
|
}
|
package/lib/tier0.js
CHANGED
|
@@ -166,9 +166,10 @@ function checkPrimitive(c, v) {
|
|
|
166
166
|
}
|
|
167
167
|
} else if (m === T_BOOLEAN) {
|
|
168
168
|
if (typeof v !== 'boolean') return false;
|
|
169
|
-
} else {
|
|
169
|
+
} else if (m !== 0) {
|
|
170
170
|
return false;
|
|
171
171
|
}
|
|
172
|
+
// m === 0: a node of enum or const alone; the checks below decide.
|
|
172
173
|
if (c.hasEnum && !c.enumSet.has(v)) return false;
|
|
173
174
|
if (c.hasConst && v !== c.constVal) return false;
|
|
174
175
|
return true;
|
|
@@ -176,18 +177,32 @@ function checkPrimitive(c, v) {
|
|
|
176
177
|
|
|
177
178
|
// Inlined object validator. Separating the primitive path removes a dead
|
|
178
179
|
// branch from the hot object path.
|
|
179
|
-
|
|
180
|
+
// The object verdict as one closure over the plan's fields, read once: the
|
|
181
|
+
// bound isValidObject() is then one call into a function whose loop reads
|
|
182
|
+
// captured constants, where a plan passed each call cost two calls and a
|
|
183
|
+
// field load per use (an empty object 17 ns against 7 for a validator built
|
|
184
|
+
// the same way by hand; a number 9 against 4).
|
|
185
|
+
function makeObjectVerdict(plan) {
|
|
186
|
+
const P_additionalAllowed = plan.additionalAllowed;
|
|
187
|
+
const P_constraints = plan.constraints;
|
|
188
|
+
const P_knownKeys = plan.knownKeys;
|
|
189
|
+
const P_requiredMask = plan.requiredMask;
|
|
190
|
+
return function tier0ObjectVerdict(data) {
|
|
180
191
|
if (typeof data !== 'object' || data === null || Array.isArray(data)) return false;
|
|
181
|
-
const cs =
|
|
192
|
+
const cs = P_constraints;
|
|
182
193
|
const n = cs.length;
|
|
183
|
-
const reqMask =
|
|
194
|
+
const reqMask = P_requiredMask;
|
|
195
|
+
// Whether the object is an ordinary one, read once: then a defined value
|
|
196
|
+
// under a name Object.prototype lacks is the object's own. Read for each
|
|
197
|
+
// present field, it was one test per field on a valid document.
|
|
198
|
+
const plain = (_PN ? data.__proto__ : Object.getPrototypeOf(data)) === Object.prototype;
|
|
184
199
|
let seenMask = 0;
|
|
185
200
|
for (let i = 0; i < n; i++) {
|
|
186
201
|
const c = cs[i];
|
|
187
202
|
let v = data[c.key];
|
|
188
203
|
// Presence is an own-property question: a value the object inherits is
|
|
189
204
|
// treated as absent, as the interpreted engine treats it.
|
|
190
|
-
if (v !== undefined && (c.protoName ||
|
|
205
|
+
if (v !== undefined && (c.protoName || !plain) && !_hop.call(data, c.key)) v = undefined;
|
|
191
206
|
if (v === undefined) {
|
|
192
207
|
// A required key holding undefined is missing, as in every engine.
|
|
193
208
|
if (reqMask & (1 << i)) return false;
|
|
@@ -225,21 +240,35 @@ function tier0ValidateObject(plan, data) {
|
|
|
225
240
|
}
|
|
226
241
|
} else if (m === T_BOOLEAN) {
|
|
227
242
|
if (typeof v !== 'boolean') return false;
|
|
228
|
-
} else {
|
|
243
|
+
} else if (m !== 0) {
|
|
229
244
|
return false;
|
|
230
245
|
}
|
|
246
|
+
// m === 0: a node of enum or const alone; the checks below decide.
|
|
231
247
|
if (c.hasEnum && !c.enumSet.has(v)) return false;
|
|
232
248
|
if (c.hasConst && v !== c.constVal) return false;
|
|
233
249
|
}
|
|
234
250
|
if ((seenMask & reqMask) !== reqMask) return false;
|
|
235
|
-
if (!
|
|
236
|
-
const known =
|
|
251
|
+
if (!P_additionalAllowed) {
|
|
252
|
+
const known = P_knownKeys;
|
|
237
253
|
for (const k in data) {
|
|
238
254
|
if (!Object.prototype.hasOwnProperty.call(data, k)) continue;
|
|
239
255
|
if (!known.has(k)) return false;
|
|
240
256
|
}
|
|
241
257
|
}
|
|
242
258
|
return true;
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function tier0ValidateObject(plan, data) {
|
|
262
|
+
let f = plan._objectVerdict;
|
|
263
|
+
if (f === undefined) f = plan._objectVerdict = makeObjectVerdict(plan);
|
|
264
|
+
return f(data);
|
|
265
|
+
}
|
|
266
|
+
// The verdict for a plan as one closure, for a validator to bind directly.
|
|
267
|
+
function tier0Verdict(plan) {
|
|
268
|
+
if (plan.isPrimitive) { const c = plan.constraints[0]; return (data) => checkPrimitive(c, data); }
|
|
269
|
+
let f = plan._objectVerdict;
|
|
270
|
+
if (f === undefined) f = plan._objectVerdict = makeObjectVerdict(plan);
|
|
271
|
+
return f;
|
|
243
272
|
}
|
|
244
273
|
|
|
245
274
|
function tier0Validate(plan, data) {
|
|
@@ -250,6 +279,7 @@ function tier0Validate(plan, data) {
|
|
|
250
279
|
module.exports = {
|
|
251
280
|
buildTier0Plan,
|
|
252
281
|
tier0Validate,
|
|
282
|
+
tier0Verdict,
|
|
253
283
|
tier0ValidateObject,
|
|
254
284
|
checkPrimitive,
|
|
255
285
|
TYPE_MASK,
|
package/lib/validator-core.js
CHANGED
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
// threshold, version(). Loading it at require cost about 1.9 ms of a 9.7 ms
|
|
7
7
|
// serverless start, for every process that never calls any of those.
|
|
8
8
|
let _nativeMod;
|
|
9
|
+
// A module whose exports are one function, as a function. A CDN that turns
|
|
10
|
+
// each CommonJS file into an ES module (esm.sh) hands require() an object
|
|
11
|
+
// with the function on `default`, and calling it threw on the first
|
|
12
|
+
// validator. Node and bundlers hand over the function itself.
|
|
13
|
+
function callable(m) { return typeof m === 'function' ? m : m.default; }
|
|
14
|
+
|
|
9
15
|
function getNative() {
|
|
10
|
-
if (_nativeMod === undefined) _nativeMod = require("./native-load")();
|
|
16
|
+
if (_nativeMod === undefined) _nativeMod = callable(require("./native-load"))();
|
|
11
17
|
return _nativeMod;
|
|
12
18
|
}
|
|
13
19
|
const { env } = require('./env');
|
|
@@ -83,9 +89,9 @@ const _hasOwn = Object.prototype.hasOwnProperty;
|
|
|
83
89
|
|
|
84
90
|
// Build a function that coerces property values to match schema types in-place.
|
|
85
91
|
// Handles string→number, string→integer, string→boolean, number→string, boolean→string.
|
|
86
|
-
function buildCoercer(schema) {
|
|
92
|
+
function buildCoercer(schema, arrayMode) {
|
|
87
93
|
if (typeof schema !== "object" || schema === null) return null;
|
|
88
|
-
const node = buildNodeCoercer(schema, new Set());
|
|
94
|
+
const node = buildNodeCoercer(schema, new Set(), arrayMode === true);
|
|
89
95
|
if (node === null) return null;
|
|
90
96
|
return (data) => { if (typeof data === "object" && data !== null) node(data); };
|
|
91
97
|
}
|
|
@@ -101,7 +107,7 @@ function coercionFor(schema, seen) {
|
|
|
101
107
|
|
|
102
108
|
// The steps for the object or array at `schema`. `seen` stops a schema that
|
|
103
109
|
// contains itself through an inline cycle.
|
|
104
|
-
function buildNodeCoercer(schema, seen) {
|
|
110
|
+
function buildNodeCoercer(schema, seen, arrayMode) {
|
|
105
111
|
if (seen.has(schema)) return null;
|
|
106
112
|
seen.add(schema);
|
|
107
113
|
const steps = [];
|
|
@@ -110,13 +116,17 @@ function buildNodeCoercer(schema, seen) {
|
|
|
110
116
|
// Assignment to `__proto__` would replace the prototype; the generated
|
|
111
117
|
// pass skips it for the same reason.
|
|
112
118
|
if (key === "__proto__") continue;
|
|
119
|
+
// coerceTypes: 'array' wraps a lone value of a top-level property typed
|
|
120
|
+
// array, as the generated pass does; this pass used to leave it.
|
|
121
|
+
const wrap = arrayMode === true && prop && prop.type === "array";
|
|
113
122
|
const c = coercionFor(prop, seen);
|
|
114
|
-
if (!c) continue;
|
|
115
|
-
const { scalar, inner } = c;
|
|
123
|
+
if (!c && !wrap) continue;
|
|
124
|
+
const { scalar, inner } = c || {};
|
|
116
125
|
steps.push((o) => {
|
|
117
126
|
// Own properties only: `key in o` found values the object inherits,
|
|
118
127
|
// and the pass then rewrote the shared object behind them.
|
|
119
128
|
if (!_hasOwn.call(o, key)) return;
|
|
129
|
+
if (wrap && o[key] !== undefined && !Array.isArray(o[key])) o[key] = [o[key]];
|
|
120
130
|
if (scalar) {
|
|
121
131
|
const v = scalar(o[key]);
|
|
122
132
|
if (v !== undefined) o[key] = v;
|
|
@@ -964,6 +974,14 @@ class Validator {
|
|
|
964
974
|
|
|
965
975
|
_ensureCompiled() {
|
|
966
976
|
if (this._initialized) return;
|
|
977
|
+
// A compile that throws (a custom keyword's compile returning no
|
|
978
|
+
// function, say) leaves the validator uncompiled, so the next call throws
|
|
979
|
+
// the same error. The flag stayed set, and the next call's lazy method
|
|
980
|
+
// called itself until the stack ran out.
|
|
981
|
+
try { this._compile(); } catch (e) { this._initialized = false; throw e; }
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
_compile() {
|
|
967
985
|
if (LazyRejection === undefined) _loadRejections();
|
|
968
986
|
this._ensureVocabularies();
|
|
969
987
|
this._initialized = true;
|
|
@@ -1002,7 +1020,7 @@ class Validator {
|
|
|
1002
1020
|
// The generated code takes the form of the target engine (js-compiler's
|
|
1003
1021
|
// setTargetEngine), so a function written for one engine is never handed
|
|
1004
1022
|
// to a compile asked for the other in the same process.
|
|
1005
|
-
const mapKey = compileCacheKey(this._schemaStr, this._schemaMap) + '\u0000' + (_codegen && _codegen.jsCompiler && typeof _codegen.jsCompiler.targetEngine === 'function' ? _codegen.jsCompiler.targetEngine() : 'v8');
|
|
1023
|
+
const mapKey = (_codegen !== null && _codegen.keyFor !== undefined && _codegen.keyFor(this)) || compileCacheKey(this._schemaStr, this._schemaMap) + '\u0000' + (_codegen && _codegen.jsCompiler && typeof _codegen.jsCompiler.targetEngine === 'function' ? _codegen.jsCompiler.targetEngine() : 'v8');
|
|
1006
1024
|
var _forceNapi = this._interpretOnly || env('ATA_FORCE_NAPI');
|
|
1007
1025
|
// Custom formats are JS functions: bypass the compile cache since they can
|
|
1008
1026
|
// differ between validators that share the same schema string. An
|
|
@@ -1112,7 +1130,7 @@ class Validator {
|
|
|
1112
1130
|
if (!preprocess) {
|
|
1113
1131
|
// Without a `default` in the text the defaults module stays unloaded.
|
|
1114
1132
|
const applyDefaults = options.useDefaults === false || !mayHaveDefaults ? null : buildDefaultsApplier(preprocessSchema);
|
|
1115
|
-
const applyCoerce = options.coerceTypes ? buildCoercer(preprocessSchema) : null;
|
|
1133
|
+
const applyCoerce = options.coerceTypes ? buildCoercer(preprocessSchema, options.coerceTypes === 'array') : null;
|
|
1116
1134
|
const applyRemove = options.removeAdditional
|
|
1117
1135
|
? buildRemover(preprocessSchema)
|
|
1118
1136
|
: null;
|
|
@@ -1626,7 +1644,7 @@ class Validator {
|
|
|
1626
1644
|
_ensureNative() {
|
|
1627
1645
|
if (this._nativeReady) return;
|
|
1628
1646
|
this._nativeReady = true;
|
|
1629
|
-
require('./native-attach.js')(this, getNative());
|
|
1647
|
+
callable(require('./native-attach.js'))(this, getNative());
|
|
1630
1648
|
}
|
|
1631
1649
|
|
|
1632
1650
|
// The buffer APIs, installed on the first call to any of them. The schema
|
|
@@ -1647,12 +1665,17 @@ class Validator {
|
|
|
1647
1665
|
if (!native && typeof Buffer === 'undefined') throw new Error(`Native addon required for ${name}(). Use validate(), isValidObject() or validateJSON(), which do not need it.`);
|
|
1648
1666
|
const { bufferNeedsSlowPath, installSlowBufferApis } = require('./buffer-gate.js');
|
|
1649
1667
|
if (!native) return installSlowBufferApis(this);
|
|
1650
|
-
require('./native-attach.js').installFast(this, native, bufferNeedsSlowPath, installSlowBufferApis);
|
|
1668
|
+
callable(require('./native-attach.js')).installFast(this, native, bufferNeedsSlowPath, installSlowBufferApis);
|
|
1651
1669
|
}
|
|
1652
1670
|
|
|
1653
1671
|
addSchema(schema) {
|
|
1654
|
-
|
|
1655
|
-
|
|
1672
|
+
// After a compile of either kind: the verdict-only one isValidObject()
|
|
1673
|
+
// makes (_jsFn) binds a function over the registry as it was, and a
|
|
1674
|
+
// schema added after it reached validate() but not isValidObject(),
|
|
1675
|
+
// which kept answering against the old definition. A call the
|
|
1676
|
+
// interpreted twin answered compiled nothing, and the twin is rebuilt.
|
|
1677
|
+
if (this._initialized || this._jsFn) {
|
|
1678
|
+
throw new Error('Cannot add schema after compilation: call addSchema() before validate(), isValidObject(), validateJSON() or isValidJSON()')
|
|
1656
1679
|
}
|
|
1657
1680
|
if (!schema || !schema.$id) {
|
|
1658
1681
|
throw new Error('Schema must have $id')
|
|
@@ -1688,7 +1711,12 @@ class Validator {
|
|
|
1688
1711
|
return;
|
|
1689
1712
|
}
|
|
1690
1713
|
this._ensureVocabularies();
|
|
1691
|
-
|
|
1714
|
+
// Where the realm refuses generated code (a strict CSP,
|
|
1715
|
+
// --disallow-code-generation-from-strings) the verdict-only compile could
|
|
1716
|
+
// only reach the closure compiler, which answers in about twice the time
|
|
1717
|
+
// of the interpreted engine the full compile binds there; the callers
|
|
1718
|
+
// fall through to that compile when this leaves no function.
|
|
1719
|
+
if (this._interpretOnly || env('ATA_FORCE_NAPI') || _loadCodegen() === null || !codegenAvailable()) return;
|
|
1692
1720
|
_codegen.compileVerdict.call(this);
|
|
1693
1721
|
}
|
|
1694
1722
|
|
|
@@ -1710,7 +1738,7 @@ class Validator {
|
|
|
1710
1738
|
|
|
1711
1739
|
// Mutators
|
|
1712
1740
|
const applyDefaults = buildDefaultsApplier(schemaObj);
|
|
1713
|
-
const applyCoerce = options.coerceTypes ? buildCoercer(schemaObj) : null;
|
|
1741
|
+
const applyCoerce = options.coerceTypes ? buildCoercer(schemaObj, options.coerceTypes === 'array') : null;
|
|
1714
1742
|
const applyRemove = options.removeAdditional
|
|
1715
1743
|
? buildRemover(schemaObj)
|
|
1716
1744
|
: null;
|
|
@@ -1941,22 +1969,6 @@ function compile(schema, opts) {
|
|
|
1941
1969
|
// and the suggestion helper are exported by index.js. None of them is called
|
|
1942
1970
|
// by a Validator, so ata-validator/lite leaves them out.
|
|
1943
1971
|
|
|
1944
|
-
// Authoring helper: identity at runtime. Its only job is to attach the
|
|
1945
|
-
// JSONSchema type (see index.d.ts) to an inline schema object so TypeScript
|
|
1946
|
-
// gives autocomplete and value checking while authoring. Returns the schema
|
|
1947
|
-
// untouched so it can be passed straight to Validator, toStandaloneModule, etc.
|
|
1948
|
-
// `schemaOf<T>()`: the JSON Schema of a TypeScript type, written in by
|
|
1949
|
-
// @ata-project/unplugin at build time from the type and its JSDoc tags. It
|
|
1950
|
-
// has no run-time form: a call that reaches here was not replaced, and the
|
|
1951
|
-
// loud error is better than an empty schema that accepts everything.
|
|
1952
|
-
function schemaOf () {
|
|
1953
|
-
throw new Error('schemaOf<T>() is replaced at build time by @ata-project/unplugin (with ts-json-schema-generator installed); it has no run-time form. Write the JSON Schema, or build with the plugin.');
|
|
1954
|
-
}
|
|
1955
|
-
|
|
1956
|
-
function defineSchema (schema) {
|
|
1957
|
-
return schema;
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
1972
|
// Public methods start as memoized accessors on the prototype. A fresh
|
|
1961
1973
|
// Validator allocates none of them; the first read of a method builds the
|
|
1962
1974
|
// bound closure, stores it on the instance as an ordinary writable property
|
|
@@ -2349,16 +2361,22 @@ function _ticks(self) {
|
|
|
2349
2361
|
// _ticks says so.
|
|
2350
2362
|
function _tier0Verdict(self) {
|
|
2351
2363
|
if (require('./shape-classifier').classify(self._schemaObj).tier !== 0) return null;
|
|
2352
|
-
const { buildTier0Plan,
|
|
2364
|
+
const { buildTier0Plan, tier0Verdict } = require('./tier0');
|
|
2353
2365
|
const plan = buildTier0Plan(self._schemaObj, self._userFormats);
|
|
2354
|
-
|
|
2366
|
+
const verdict = tier0Verdict(plan);
|
|
2367
|
+
if (!_ticks(self)) return verdict;
|
|
2355
2368
|
const tick = _tiering.counter(self);
|
|
2356
|
-
return (d) => { tick(); return
|
|
2369
|
+
return (d) => { tick(); return verdict(d); };
|
|
2357
2370
|
}
|
|
2358
2371
|
|
|
2359
2372
|
Validator.prototype._coldTwin = function () {
|
|
2360
2373
|
const cg = _loadCodegen();
|
|
2361
|
-
|
|
2374
|
+
// Where no code can be generated (a strict CSP), the interpreted engine is
|
|
2375
|
+
// the compile itself: a twin would only delay it, and its size test read
|
|
2376
|
+
// the schema's text for nothing (6.5% of a schema's first check there).
|
|
2377
|
+
// The probe of the realm (codegenAvailable) is not run for a validator
|
|
2378
|
+
// asked for the interpreted engine, which never generates code.
|
|
2379
|
+
return cg === null || cg.coldTwin === undefined || this._interpretOnly || !codegenAvailable() ? null : cg.coldTwin(this, Validator);
|
|
2362
2380
|
};
|
|
2363
2381
|
|
|
2364
2382
|
_defineLazyMethod('validate', (self) => (data) => {
|
|
@@ -2395,7 +2413,14 @@ _defineLazyMethod('isValidObject', (self) => {
|
|
|
2395
2413
|
_bindVerdict(self, _ticks(self) ? _t0 : (d) => {
|
|
2396
2414
|
const r = _t0(d);
|
|
2397
2415
|
if (++_n === 2) {
|
|
2416
|
+
const before = self.isValidObject;
|
|
2398
2417
|
try { self._ensureCodegen(); } catch {}
|
|
2418
|
+
// Where the generated code did not take the method over (no code
|
|
2419
|
+
// generation in this realm, or the generator declined), the plan's
|
|
2420
|
+
// closure answers directly from now on: this wrapper cost every
|
|
2421
|
+
// later call a count and a call, 13.5 against 4.7 ns on an empty
|
|
2422
|
+
// object, on exactly the pages and workers that have no generator.
|
|
2423
|
+
if (self.isValidObject === before) _bindVerdict(self, _t0);
|
|
2399
2424
|
}
|
|
2400
2425
|
return r;
|
|
2401
2426
|
});
|
|
@@ -2457,8 +2482,6 @@ module.exports = {
|
|
|
2457
2482
|
createPaddedBuffer,
|
|
2458
2483
|
SIMDJSON_PADDING,
|
|
2459
2484
|
parseJSON,
|
|
2460
|
-
defineSchema,
|
|
2461
|
-
schemaOf,
|
|
2462
2485
|
};
|
|
2463
2486
|
// How an entry registers the code generator. Not enumerable, so the exported
|
|
2464
2487
|
// surface of `require('ata-validator')` is what it was.
|