ata-validator 1.53.0 → 1.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -26
- package/compat.js +13 -2
- package/error-runtime.d.ts +7 -0
- package/error-runtime.js +10 -1
- package/error-runtime.mjs +1 -1
- package/lib/aot-impl.js +26 -3
- package/lib/codegen-engine.js +2 -1
- package/lib/dialect.js +36 -1
- package/lib/draft7.js +17 -28
- package/lib/enrich-site.js +29 -2
- package/lib/error-runtime.js +9 -4
- package/lib/formats-source.js +50 -66
- package/lib/formats.js +37 -231
- package/lib/interpreter-compat.js +144 -0
- package/lib/interpreter-core.js +55 -144
- package/lib/js-compiler-errors.js +52 -16
- package/lib/js-compiler.js +537 -65
- package/lib/metaschemas.js +5 -3
- package/lib/plan-compiler.js +113 -33
- package/lib/regex-linear.js +81 -1
- package/lib/safe-regex.js +14 -11
- package/lib/schema-scan.js +19 -28
- package/lib/tier0.js +1 -0
- package/lib/unique-items.js +16 -5
- package/lib/uri-core.js +155 -0
- package/lib/validator-core.js +20 -11
- package/lib/version.js +1 -1
- package/package.json +9 -9
package/lib/metaschemas.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// The official meta-schemas, vendored so a `$ref` to one of them resolves
|
|
4
4
|
// without a network. Draft 2020-12 is eight documents joined by $dynamicRef;
|
|
5
|
-
// draft-07 and draft-
|
|
5
|
+
// draft-07, draft-06 and draft-04 are one each; 2019-09 is seven. Sources:
|
|
6
6
|
// https://json-schema.org/draft/2020-12/schema and
|
|
7
|
-
// http://json-schema.org/draft-07/schema, fetched 2026-08-23,
|
|
7
|
+
// http://json-schema.org/draft-07/schema, fetched 2026-08-23,
|
|
8
8
|
// http://json-schema.org/draft-06/schema and
|
|
9
|
-
// https://json-schema.org/draft/2019-09/schema, fetched 2026-10-10,
|
|
9
|
+
// https://json-schema.org/draft/2019-09/schema, fetched 2026-10-10, and
|
|
10
|
+
// http://json-schema.org/draft-04/schema, fetched 2026-10-14, unmodified.
|
|
10
11
|
// The 2019-09 documents are read through the same 2019-09 rewrite as a
|
|
11
12
|
// caller's schema (lib/draft7.js) once, at load, since they use
|
|
12
13
|
// `$recursiveRef`, which the engines read as `$dynamicRef`.
|
|
@@ -22,6 +23,7 @@ const METASCHEMAS = new Map([
|
|
|
22
23
|
["https://json-schema.org/draft/2020-12/meta/content", {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://json-schema.org/draft/2020-12/meta/content","$dynamicAnchor":"meta","title":"Content vocabulary meta-schema","type":["object","boolean"],"properties":{"contentEncoding":{"type":"string"},"contentMediaType":{"type":"string"},"contentSchema":{"$dynamicRef":"#meta"}}}],
|
|
23
24
|
["https://json-schema.org/draft/2020-12/meta/unevaluated", {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://json-schema.org/draft/2020-12/meta/unevaluated","$dynamicAnchor":"meta","title":"Unevaluated applicator vocabulary meta-schema","type":["object","boolean"],"properties":{"unevaluatedItems":{"$dynamicRef":"#meta"},"unevaluatedProperties":{"$dynamicRef":"#meta"}}}],
|
|
24
25
|
["http://json-schema.org/draft-07/schema#", {"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"writeOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}],
|
|
26
|
+
["http://json-schema.org/draft-04/schema#", {"id":"http://json-schema.org/draft-04/schema#","$schema":"http://json-schema.org/draft-04/schema#","description":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"positiveInteger":{"type":"integer","minimum":0},"positiveIntegerDefault0":{"allOf":[{"$ref":"#/definitions/positiveInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"minItems":1,"uniqueItems":true}},"type":"object","properties":{"id":{"type":"string"},"$schema":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"multipleOf":{"type":"number","minimum":0,"exclusiveMinimum":true},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"boolean","default":false},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"boolean","default":false},"maxLength":{"$ref":"#/definitions/positiveInteger"},"minLength":{"$ref":"#/definitions/positiveIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/positiveInteger"},"minItems":{"$ref":"#/definitions/positiveIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"maxProperties":{"$ref":"#/definitions/positiveInteger"},"minProperties":{"$ref":"#/definitions/positiveIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"dependencies":{"exclusiveMaximum":["maximum"],"exclusiveMinimum":["minimum"]},"default":{}}],
|
|
25
27
|
["http://json-schema.org/draft-06/schema#", {"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}],
|
|
26
28
|
["https://json-schema.org/draft/2019-09/schema", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/schema","$vocabulary":{"https://json-schema.org/draft/2019-09/vocab/core":true,"https://json-schema.org/draft/2019-09/vocab/applicator":true,"https://json-schema.org/draft/2019-09/vocab/validation":true,"https://json-schema.org/draft/2019-09/vocab/meta-data":true,"https://json-schema.org/draft/2019-09/vocab/format":false,"https://json-schema.org/draft/2019-09/vocab/content":true},"$recursiveAnchor":true,"title":"Core and Validation specifications meta-schema","allOf":[{"$ref":"meta/core"},{"$ref":"meta/applicator"},{"$ref":"meta/validation"},{"$ref":"meta/meta-data"},{"$ref":"meta/format"},{"$ref":"meta/content"}],"type":["object","boolean"],"properties":{"definitions":{"$comment":"While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.","type":"object","additionalProperties":{"$recursiveRef":"#"},"default":{}},"dependencies":{"$comment":"\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"","type":"object","additionalProperties":{"anyOf":[{"$recursiveRef":"#"},{"$ref":"meta/validation#/$defs/stringArray"}]}}}}],
|
|
27
29
|
["https://json-schema.org/draft/2019-09/meta/core", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/meta/core","$recursiveAnchor":true,"title":"Core vocabulary meta-schema","type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference","$comment":"Non-empty fragments not allowed.","pattern":"^[^#]*#?$"},"$schema":{"type":"string","format":"uri"},"$anchor":{"type":"string","pattern":"^[A-Za-z][-A-Za-z0-9.:_]*$"},"$ref":{"type":"string","format":"uri-reference"},"$recursiveRef":{"type":"string","format":"uri-reference"},"$recursiveAnchor":{"type":"boolean","default":false},"$vocabulary":{"type":"object","propertyNames":{"type":"string","format":"uri"},"additionalProperties":{"type":"boolean"}},"$comment":{"type":"string"},"$defs":{"type":"object","additionalProperties":{"$recursiveRef":"#"},"default":{}}}}],
|
package/lib/plan-compiler.js
CHANGED
|
@@ -53,7 +53,7 @@ const branchTitle = (n) => (n && n.schema && typeof n.schema.title === 'string'
|
|
|
53
53
|
function install(deps) {
|
|
54
54
|
const { Plan, NOERRORS, err, evalLeaf, evalLeafV, runCustomV, dataBits, escapePointer,
|
|
55
55
|
deepEqual, enumMember, allDistinct, multipleOfOk, cpAtLeast, cpAtMost,
|
|
56
|
-
T_STRING, T_ARRAY, T_OBJECT, resolveRef, splitFragment, resolveUri } = deps;
|
|
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
59
|
// `leaf`: the node compiled no subschema of its own (see compileNode). A
|
|
@@ -169,12 +169,21 @@ function install(deps) {
|
|
|
169
169
|
}
|
|
170
170
|
if (P.enum !== null) {
|
|
171
171
|
if (P.enum.length === 0) checks.push(() => false);
|
|
172
|
+
else if (P.enumObjs.length === 0) { const prims = P.enumPrims; checks.push((data) => prims.has(data)); }
|
|
172
173
|
else checks.push((data) => enumMember(P, data));
|
|
173
174
|
}
|
|
174
175
|
if (P.hasConst) {
|
|
175
176
|
const c = P.const;
|
|
176
177
|
checks.push((data) => deepEqual(c, data));
|
|
177
178
|
}
|
|
179
|
+
// The value checks of each type, folded to one call or null. A node of
|
|
180
|
+
// one type and nothing but that type's keywords (the common leaf: a
|
|
181
|
+
// string with a length, an integer with a bound) is answered by one
|
|
182
|
+
// closure over typeof and that call, with no type bits computed and no
|
|
183
|
+
// chain of guarded checks: the interpreted engine spent a fifth of a
|
|
184
|
+
// document in those calls.
|
|
185
|
+
const reqFolded = P.required !== null && P.requiredUndef === null && P.properties !== null;
|
|
186
|
+
let numsRun = null;
|
|
178
187
|
if (P.hasNumber) {
|
|
179
188
|
const nums = [];
|
|
180
189
|
if (P.minimum !== undefined) { const m = P.minimum; nums.push((d) => d >= m); }
|
|
@@ -182,42 +191,45 @@ function install(deps) {
|
|
|
182
191
|
if (P.exclusiveMinimum !== undefined) { const m = P.exclusiveMinimum; nums.push((d) => d > m); }
|
|
183
192
|
if (P.exclusiveMaximum !== undefined) { const m = P.exclusiveMaximum; nums.push((d) => d < m); }
|
|
184
193
|
if (P.multipleOf !== undefined) { const m = P.multipleOf; nums.push((d) => multipleOfOk(d, m)); }
|
|
185
|
-
|
|
186
|
-
// A non-finite number is not of type `number` here: dataBits() gives it
|
|
187
|
-
// no type bit, and an empty schema accepts it. So the numeric keywords
|
|
188
|
-
// must not apply to it either, or `{ minimum: 3 }` rejects a value that
|
|
189
|
-
// `{}` accepts and the compiled engine admits.
|
|
190
|
-
if (run !== null) checks.push((data) => !Number.isFinite(data) || run(data));
|
|
194
|
+
numsRun = combineValue(nums);
|
|
191
195
|
}
|
|
196
|
+
let strsRun = null;
|
|
192
197
|
if (P.hasString) {
|
|
193
198
|
const strs = [];
|
|
194
|
-
if (P.minLength !== undefined
|
|
195
|
-
|
|
199
|
+
if (P.minLength !== undefined && P.maxLength !== undefined) {
|
|
200
|
+
const lo = P.minLength, hi = P.maxLength;
|
|
201
|
+
strs.push((d) => { const n = d.length; return (n >= 2 * lo && n <= hi) || (cpAtLeast(d, lo) && cpAtMost(d, hi)); });
|
|
202
|
+
} else if (P.minLength !== undefined) { const m = P.minLength; strs.push((d) => cpAtLeast(d, m)); }
|
|
203
|
+
else if (P.maxLength !== undefined) { const m = P.maxLength; strs.push((d) => cpAtMost(d, m)); }
|
|
196
204
|
if (P.pattern !== null) { const re = P.pattern; strs.push((d) => re.test(d)); }
|
|
197
205
|
if (P.formatFn !== null) { const f = P.formatFn; strs.push((d) => f(d)); }
|
|
198
|
-
|
|
199
|
-
if (run !== null) { needsBits = true; checks.push((data, bits) => bits !== T_STRING || run(data)); }
|
|
206
|
+
strsRun = combineValue(strs);
|
|
200
207
|
}
|
|
201
|
-
|
|
202
|
-
// keywords; only the three value-level ones belong here, exactly as
|
|
203
|
-
// evalLeafV() reads them.
|
|
208
|
+
let arrsRun = null;
|
|
204
209
|
if (P.minItems !== undefined || P.maxItems !== undefined || P.uniqueItems) {
|
|
205
210
|
const arrs = [];
|
|
206
211
|
if (P.minItems !== undefined) { const m = P.minItems; arrs.push((d) => d.length >= m); }
|
|
207
212
|
if (P.maxItems !== undefined) { const m = P.maxItems; arrs.push((d) => d.length <= m); }
|
|
208
213
|
if (P.uniqueItems) arrs.push(allDistinct);
|
|
209
|
-
|
|
210
|
-
if (run !== null) { needsBits = true; checks.push((data, bits) => bits !== T_ARRAY || run(data)); }
|
|
214
|
+
arrsRun = combineValue(arrs);
|
|
211
215
|
}
|
|
212
|
-
|
|
216
|
+
let objsRun = null;
|
|
217
|
+
if ((P.required !== null && !reqFolded) || P.minProperties !== undefined || P.maxProperties !== undefined || P.dependentRequired !== null) {
|
|
213
218
|
const objs = [];
|
|
214
|
-
if (P.required !== null) {
|
|
219
|
+
if (P.required !== null && !reqFolded) {
|
|
215
220
|
const req = P.required;
|
|
216
|
-
|
|
217
|
-
|
|
221
|
+
// A required key holding undefined is missing; for a declared
|
|
222
|
+
// property the property step finds it (requiredInProps).
|
|
223
|
+
const undef = P.requiredUndef;
|
|
224
|
+
if (req.length === 1) { const k = req[0]; objs.push(undef !== null && undef[0] ? (d) => Object.hasOwn(d, k) && d[k] !== undefined : (d) => Object.hasOwn(d, k)); }
|
|
225
|
+
else if (undef === null) objs.push((d) => {
|
|
218
226
|
for (let i = 0; i < req.length; i++) if (!Object.hasOwn(d, req[i])) return false;
|
|
219
227
|
return true;
|
|
220
228
|
});
|
|
229
|
+
else objs.push((d) => {
|
|
230
|
+
for (let i = 0; i < req.length; i++) if (!Object.hasOwn(d, req[i]) || (undef[i] && d[req[i]] === undefined)) return false;
|
|
231
|
+
return true;
|
|
232
|
+
});
|
|
221
233
|
}
|
|
222
234
|
if (P.minProperties !== undefined) { const m = P.minProperties; objs.push((d) => Object.keys(d).length >= m); }
|
|
223
235
|
if (P.maxProperties !== undefined) { const m = P.maxProperties; objs.push((d) => Object.keys(d).length <= m); }
|
|
@@ -232,9 +244,34 @@ function install(deps) {
|
|
|
232
244
|
return true;
|
|
233
245
|
});
|
|
234
246
|
}
|
|
235
|
-
|
|
236
|
-
if (run !== null) { needsBits = true; checks.push((data, bits) => bits !== T_OBJECT || run(data)); }
|
|
247
|
+
objsRun = combineValue(objs);
|
|
237
248
|
}
|
|
249
|
+
if (P.hasType && P.enum === null && !P.hasConst && !P.hasCustom && checks.length === 1) {
|
|
250
|
+
const m = P.typeMask;
|
|
251
|
+
// The run of the type's own keywords, and whether any other type's are present.
|
|
252
|
+
const own = m === T_STRING ? strsRun : m === T_INTEGER || m === T_NUMBER ? numsRun : m === T_OBJECT ? objsRun : m === T_ARRAY ? arrsRun : null;
|
|
253
|
+
const foreign = (numsRun !== null && m !== T_INTEGER && m !== T_NUMBER) || (strsRun !== null && m !== T_STRING) || (arrsRun !== null && m !== T_ARRAY) || (objsRun !== null && m !== T_OBJECT);
|
|
254
|
+
if (!foreign) {
|
|
255
|
+
const r = own;
|
|
256
|
+
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';
|
|
261
|
+
case T_NULL: return (d) => d === null;
|
|
262
|
+
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
|
+
case T_ARRAY: return r === null ? (d) => Array.isArray(d) : (d) => Array.isArray(d) && r(d);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// A non-finite number is not of type `number` here: dataBits() gives it
|
|
268
|
+
// no type bit, and an empty schema accepts it. So the numeric keywords
|
|
269
|
+
// must not apply to it either, or `{ minimum: 3 }` rejects a value that
|
|
270
|
+
// `{}` accepts and the compiled engine admits.
|
|
271
|
+
if (numsRun !== null) { const run = numsRun; checks.push((data) => !Number.isFinite(data) || run(data)); }
|
|
272
|
+
if (strsRun !== null) { const run = strsRun; needsBits = true; checks.push((data, bits) => bits !== T_STRING || run(data)); }
|
|
273
|
+
if (arrsRun !== null) { const run = arrsRun; needsBits = true; checks.push((data, bits) => bits !== T_ARRAY || run(data)); }
|
|
274
|
+
if (objsRun !== null) { const run = objsRun; needsBits = true; checks.push((data, bits) => bits !== T_OBJECT || run(data)); }
|
|
238
275
|
|
|
239
276
|
if (P.hasCustom) {
|
|
240
277
|
needsBits = true;
|
|
@@ -310,6 +347,7 @@ function install(deps) {
|
|
|
310
347
|
|
|
311
348
|
const steps = []; // collect variant: (data, errors, ip, sp, stack, rec)
|
|
312
349
|
const vsteps = []; // verdict variant: (data, stack, rec)
|
|
350
|
+
let leafV = TRUE_FN;
|
|
313
351
|
|
|
314
352
|
// $ref / $dynamicRef, resolved at compile time.
|
|
315
353
|
for (let which = 0; which < 2; which++) {
|
|
@@ -385,7 +423,8 @@ function install(deps) {
|
|
|
385
423
|
P.required !== null || P.minProperties !== undefined || P.maxProperties !== undefined ||
|
|
386
424
|
P.dependentRequired !== null || P.hasCustom) {
|
|
387
425
|
steps.push((data, errors, instancePath, schemaPath) => evalLeaf(P, data, errors, instancePath, schemaPath));
|
|
388
|
-
|
|
426
|
+
leafV = compileLeafV(P);
|
|
427
|
+
if (leafV !== TRUE_FN) vsteps.push(leafV);
|
|
389
428
|
}
|
|
390
429
|
|
|
391
430
|
// Arrays
|
|
@@ -490,8 +529,10 @@ function install(deps) {
|
|
|
490
529
|
// Declared properties: a linear scan over a short list beats a Map
|
|
491
530
|
// lookup, and most schemas declare a handful.
|
|
492
531
|
const propKeys = props !== null ? [...props.keys()] : null;
|
|
532
|
+
// req: the key is also required, so holding undefined it is missing.
|
|
533
|
+
const reqIn = P.requiredInProps;
|
|
493
534
|
const propList = props !== null
|
|
494
|
-
? [...props
|
|
535
|
+
? [...props].map(([key, entry]) => ({ fn: child(entry.node), seg: entry.seg, schemaSeg: entry.schemaSeg, req: reqIn !== null && reqIn.has(key) }))
|
|
495
536
|
: null;
|
|
496
537
|
const propMap = props !== null && props.size > 8 ? new Map(propKeys.map((k, i) => [k, propList[i]])) : null;
|
|
497
538
|
const lookup = propMap !== null
|
|
@@ -503,6 +544,10 @@ function install(deps) {
|
|
|
503
544
|
? P.patternProperties.map((e) => ({ re: e.re, src: e.src, fn: child(e.node) }))
|
|
504
545
|
: null;
|
|
505
546
|
const apFn = P.additionalProperties !== undefined ? child(P.additionalProperties) : null;
|
|
547
|
+
// The node's value checks, when they are the step before this one,
|
|
548
|
+
// run inside it: one closure call per object instead of two, and the
|
|
549
|
+
// composition above it goes away.
|
|
550
|
+
const fusedLeaf = vsteps.length > 0 && vsteps[vsteps.length - 1] === leafV && leafV !== TRUE_FN ? vsteps.pop() : null;
|
|
506
551
|
// `additionalProperties: false` is a boolean subschema, but its error is
|
|
507
552
|
// not the generic "boolean schema is false": every consumer switches on
|
|
508
553
|
// keyword === 'additionalProperties' and reads params.additionalProperty
|
|
@@ -533,12 +578,18 @@ function install(deps) {
|
|
|
533
578
|
let evaluated = false;
|
|
534
579
|
const prop = lookup !== null ? lookup(key) : undefined;
|
|
535
580
|
// A declared property whose value is undefined is not checked (see
|
|
536
|
-
// the interpreter's eval).
|
|
581
|
+
// the interpreter's eval); required, it is missing.
|
|
537
582
|
if (prop !== undefined) {
|
|
538
583
|
const value = data[key];
|
|
539
|
-
if (value !== undefined
|
|
584
|
+
if (value !== undefined) {
|
|
585
|
+
if (!((prop.fn.leaf && prop.fn.v(value, stack)) || prop.fn.c(value, errors, instancePath + prop.seg, schemaPath + prop.schemaSeg, stack))) {
|
|
586
|
+
ok = false;
|
|
587
|
+
if (errors === NOERRORS) return false;
|
|
588
|
+
}
|
|
589
|
+
} else if (prop.req) {
|
|
540
590
|
ok = false;
|
|
541
591
|
if (errors === NOERRORS) return false;
|
|
592
|
+
errors.push(err('required', 'required', instancePath, schemaPath + '/required', { missingProperty: key }, `must have required property '${key}'`));
|
|
542
593
|
}
|
|
543
594
|
evaluated = true;
|
|
544
595
|
}
|
|
@@ -576,30 +627,58 @@ function install(deps) {
|
|
|
576
627
|
// not mistaken for data. A long list walks the data's keys instead,
|
|
577
628
|
// since the data usually carries far fewer.
|
|
578
629
|
const n = propKeys.length;
|
|
630
|
+
// On an ordinary object (its prototype is Object.prototype) a key
|
|
631
|
+
// that names nothing on that prototype is read directly: a value
|
|
632
|
+
// means the key is the object's own, undefined means absent or held
|
|
633
|
+
// undefined, which the schema treats alike. Other objects, and keys
|
|
634
|
+
// such as `constructor` or `toString`, keep the own-property test.
|
|
635
|
+
const direct = propKeys.map((k) => !(k in Object.prototype));
|
|
636
|
+
const assertsObject = fusedLeaf !== null && P.hasType && P.typeMask === T_OBJECT;
|
|
579
637
|
vsteps.push((data, stack, rec) => {
|
|
580
|
-
if (
|
|
638
|
+
if (fusedLeaf !== null && !fusedLeaf(data)) return false;
|
|
639
|
+
if (!assertsObject && dataBits(data) !== T_OBJECT) return true;
|
|
640
|
+
const plain = Object.getPrototypeOf(data) === Object.prototype;
|
|
581
641
|
for (let i = 0; i < n; i++) {
|
|
582
642
|
const key = propKeys[i];
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
643
|
+
let value;
|
|
644
|
+
if (plain && direct[i]) {
|
|
645
|
+
value = data[key];
|
|
646
|
+
// Absent or held undefined: missing when required (the leaf
|
|
647
|
+
// step no longer checks it, reqFolded in compileLeafV). A key
|
|
648
|
+
// held undefined still counts as evaluated for the record.
|
|
649
|
+
if (value === undefined) { if (propList[i].req) return false; if (collect && Object.hasOwn(data, key)) addProp(rec, key); continue; }
|
|
650
|
+
} else {
|
|
651
|
+
if (!Object.hasOwn(data, key)) { if (propList[i].req) return false; continue; }
|
|
652
|
+
value = data[key];
|
|
653
|
+
if (value === undefined) { if (propList[i].req) return false; if (collect) addProp(rec, key); continue; }
|
|
654
|
+
}
|
|
655
|
+
if (!propList[i].fn.v(value, stack)) return false;
|
|
586
656
|
if (collect) addProp(rec, key);
|
|
587
657
|
}
|
|
588
658
|
return true;
|
|
589
659
|
});
|
|
590
|
-
} else
|
|
660
|
+
} else {
|
|
661
|
+
// Required keys that are declared properties are counted as the walk
|
|
662
|
+
// meets them (reqFolded in compileLeafV); one short at the end means
|
|
663
|
+
// one is missing.
|
|
664
|
+
const reqN = P.required !== null && P.requiredUndef === null && P.requiredInProps !== null ? P.requiredInProps.size : 0;
|
|
665
|
+
vsteps.push((data, stack, rec) => {
|
|
666
|
+
if (fusedLeaf !== null && !fusedLeaf(data)) return false;
|
|
591
667
|
if (dataBits(data) !== T_OBJECT) return true;
|
|
592
668
|
const keys = collect ? keysOf(rec, data) : Object.keys(data);
|
|
593
669
|
if (pnFn !== null) {
|
|
594
670
|
for (const k of keys) if (!pnFn.v(k, stack)) return false;
|
|
595
671
|
}
|
|
672
|
+
let seenReq = 0;
|
|
596
673
|
for (let k = 0; k < keys.length; k++) {
|
|
597
674
|
const key = keys[k];
|
|
598
675
|
let evaluated = false;
|
|
599
676
|
const prop = lookup !== null ? lookup(key) : undefined;
|
|
600
677
|
if (prop !== undefined) {
|
|
601
678
|
const value = data[key];
|
|
602
|
-
if (value !== undefined
|
|
679
|
+
if (value !== undefined) { if (!prop.fn.v(value, stack)) return false; }
|
|
680
|
+
else if (prop.req) return false;
|
|
681
|
+
if (prop.req) seenReq++;
|
|
603
682
|
evaluated = true;
|
|
604
683
|
}
|
|
605
684
|
if (patterns !== null) {
|
|
@@ -617,8 +696,9 @@ function install(deps) {
|
|
|
617
696
|
}
|
|
618
697
|
if (evaluated && collect) addProp(rec, key);
|
|
619
698
|
}
|
|
620
|
-
return
|
|
699
|
+
return seenReq === reqN;
|
|
621
700
|
});
|
|
701
|
+
}
|
|
622
702
|
}
|
|
623
703
|
if (P.dependentSchemas !== null) {
|
|
624
704
|
const entries = P.dependentSchemas.map(([k, v]) => [k, inPlace(v, base), escapePointer(k)]);
|
package/lib/regex-linear.js
CHANGED
|
@@ -210,6 +210,86 @@ function deterministicRule (ast) {
|
|
|
210
210
|
return unbounded === 0 || anchored(ast)
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
// Two more shapes the rules above decline, both anchored with `^` and `$`
|
|
214
|
+
// around one top-level sequence.
|
|
215
|
+
//
|
|
216
|
+
// One choice: characters and classes, each repeated a fixed or bounded number
|
|
217
|
+
// of times, with only one of the repeats able to vary, by at most 256
|
|
218
|
+
// (`^[a-z0-9_-]{1,128}$`).
|
|
219
|
+
// The engine backtracks through that one repeat and nowhere else, at most its
|
|
220
|
+
// width per attempt, and the anchor allows one attempt.
|
|
221
|
+
//
|
|
222
|
+
// A delimited loop: `^P(Xd)*Q$` or with `+`, where d is one character or
|
|
223
|
+
// class, X and Q match nothing d matches (Q may end with d, or an optional
|
|
224
|
+
// d), and P holds no unbounded quantifier. Dotted identifiers and paths are
|
|
225
|
+
// written so: `^([A-Za-z_$][\w$]*\.)*[A-Za-z_$][\w$]*$`, `^/?([^/]+/)*[^/]+/?$`.
|
|
226
|
+
// Every iteration and Q's match end at a d or at the end, so each wrong
|
|
227
|
+
// choice between another iteration and Q fails within one segment, and the
|
|
228
|
+
// segments together are the input once. X and Q each hold at most one
|
|
229
|
+
// unbounded quantifier, over a character or class, so a segment is not split
|
|
230
|
+
// two ways either.
|
|
231
|
+
function sequenceOf (ast) {
|
|
232
|
+
if (ast.t !== 'concat') return null
|
|
233
|
+
const parts = ast.parts
|
|
234
|
+
if (parts.length < 3 || parts[0].t !== 'bol' || parts[parts.length - 1].t !== 'eol') return null
|
|
235
|
+
return parts.slice(1, -1)
|
|
236
|
+
}
|
|
237
|
+
const repeatOfAtom = (n) => (n.t === 'star' || n.t === 'plus' || n.t === 'quest' || n.t === 'repeat') && atom(n.child)
|
|
238
|
+
const unboundedNode = (n) => n.t === 'star' || n.t === 'plus' || (n.t === 'repeat' && n.max === Infinity)
|
|
239
|
+
|
|
240
|
+
function oneChoiceRule (ast) {
|
|
241
|
+
const seq = sequenceOf(ast)
|
|
242
|
+
if (seq === null) return false
|
|
243
|
+
let varying = 0
|
|
244
|
+
for (const n of seq) {
|
|
245
|
+
if (atom(n)) continue
|
|
246
|
+
if (n.t === 'repeat' && atom(n.child) && n.max !== Infinity) {
|
|
247
|
+
if (n.max > 256) return false
|
|
248
|
+
if (n.max > n.min) varying++
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
251
|
+
return false
|
|
252
|
+
}
|
|
253
|
+
return varying <= 1
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function delimitedLoopRule (ast) {
|
|
257
|
+
const seq = sequenceOf(ast)
|
|
258
|
+
if (seq === null) return false
|
|
259
|
+
const loopAt = seq.findIndex((n) => unboundedNode(n) && !atom(n.child))
|
|
260
|
+
if (loopAt < 0) return false
|
|
261
|
+
let body = seq[loopAt].child
|
|
262
|
+
while (body.t === 'group') body = body.child
|
|
263
|
+
if (body.t !== 'concat' || body.parts.length < 2) return false
|
|
264
|
+
const d = body.parts[body.parts.length - 1]
|
|
265
|
+
if (!atom(d)) return false
|
|
266
|
+
const D = charSet(d)
|
|
267
|
+
// A run of characters, classes and their repeats that matches nothing in
|
|
268
|
+
// D, with at most one unbounded quantifier.
|
|
269
|
+
const run = (nodes) => {
|
|
270
|
+
let unbounded = 0
|
|
271
|
+
for (const n of nodes) {
|
|
272
|
+
const a = atom(n) ? n : repeatOfAtom(n) ? n.child : null
|
|
273
|
+
if (a === null || overlap(charSet(a), D)) return false
|
|
274
|
+
if (unboundedNode(n)) unbounded++
|
|
275
|
+
if (n.t === 'repeat' && n.max !== Infinity && n.max - n.min > 64) return false
|
|
276
|
+
}
|
|
277
|
+
return unbounded <= 1
|
|
278
|
+
}
|
|
279
|
+
if (!run(body.parts.slice(0, -1))) return false
|
|
280
|
+
const prefix = seq.slice(0, loopAt)
|
|
281
|
+
if (prefix.some(hasUnbounded)) return false
|
|
282
|
+
const pb = simpleRule({ t: 'concat', parts: prefix })
|
|
283
|
+
if (!pb && prefix.length > 0) return false
|
|
284
|
+
let suffix = seq.slice(loopAt + 1)
|
|
285
|
+
const last = suffix[suffix.length - 1]
|
|
286
|
+
if (last !== undefined) {
|
|
287
|
+
const tail = atom(last) ? last : last.t === 'quest' && atom(last.child) ? last.child : null
|
|
288
|
+
if (tail !== null && !overlap(charSet(tail), { bits: D.bits.map((b) => b ^ 1), high: D.high ? 0 : 1 })) suffix = suffix.slice(0, -1)
|
|
289
|
+
}
|
|
290
|
+
return run(suffix)
|
|
291
|
+
}
|
|
292
|
+
|
|
213
293
|
function nativeIsLinear (src) {
|
|
214
294
|
if (typeof src !== 'string' || src.length > 512) return false
|
|
215
295
|
for (let i = 0; i < src.length; i++) {
|
|
@@ -225,7 +305,7 @@ function nativeIsLinear (src) {
|
|
|
225
305
|
}
|
|
226
306
|
let ast
|
|
227
307
|
try { ast = parse(src) } catch { return false }
|
|
228
|
-
return simpleRule(ast) || deterministicRule(ast)
|
|
308
|
+
return simpleRule(ast) || deterministicRule(ast) || oneChoiceRule(ast) || delimitedLoopRule(ast)
|
|
229
309
|
}
|
|
230
310
|
|
|
231
311
|
module.exports = { nativeIsLinear }
|
package/lib/safe-regex.js
CHANGED
|
@@ -473,25 +473,28 @@ function __ataSafeRegex () {
|
|
|
473
473
|
}
|
|
474
474
|
if (id < 0) return testNFA(s)
|
|
475
475
|
if (len === 0) return (flags[id] & 1) === 1
|
|
476
|
-
|
|
477
|
-
//
|
|
478
|
-
//
|
|
476
|
+
if ((flags[id] & 1) === 1) return true
|
|
477
|
+
// The last character, with the end flag, is stepped after the loop.
|
|
478
|
+
// Tables are locals, reread after `step` (which may grow them).
|
|
479
479
|
const last = len - 1
|
|
480
|
+
let T = trans
|
|
481
|
+
let F = flags
|
|
480
482
|
for (let pos = 0; pos < last; pos++) {
|
|
481
|
-
if ((flags[id] & 1) === 1) return true
|
|
482
483
|
const c = s.charCodeAt(pos)
|
|
483
|
-
let nid
|
|
484
|
-
if (
|
|
485
|
-
nid = trans[(id << 7) | c]
|
|
486
|
-
if (nid === -2) { nid = step(states[id], c, false); if (nid >= 0 || overflow) trans[(id << 7) | c] = nid }
|
|
487
|
-
} else {
|
|
484
|
+
let nid = c < 128 ? T[(id << 7) | c] : -2
|
|
485
|
+
if (nid === -2) {
|
|
488
486
|
nid = step(states[id], c, false)
|
|
487
|
+
T = trans; F = flags
|
|
488
|
+
if (c < 128 && (nid >= 0 || overflow)) T[(id << 7) | c] = nid
|
|
489
489
|
}
|
|
490
490
|
if (nid < 0) return testNFA(s)
|
|
491
491
|
id = nid
|
|
492
|
-
|
|
492
|
+
const f = F[id]
|
|
493
|
+
if (f !== 0) {
|
|
494
|
+
if ((f & 2) === 2) return false
|
|
495
|
+
return true
|
|
496
|
+
}
|
|
493
497
|
}
|
|
494
|
-
if ((flags[id] & 1) === 1) return true
|
|
495
498
|
const c = s.charCodeAt(last)
|
|
496
499
|
let nid
|
|
497
500
|
if (c < 128) {
|
package/lib/schema-scan.js
CHANGED
|
@@ -30,23 +30,10 @@ const TUPLE_ITEMS = 1 << 5; // array-valued `items`, split into `prefixItem
|
|
|
30
30
|
const EXCLUSIVE_BOOL = 1 << 6; // draft-04's boolean `exclusiveMinimum`/`exclusiveMaximum`
|
|
31
31
|
const ENCODED_REF = 1 << 7; // a fragment `$ref` holding a percent escape, decoded by normalizeNullable's walk
|
|
32
32
|
|
|
33
|
-
// Questions compat asks of a schema it is about to compile
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const DISCRIMINATOR = 1 << 10; // `discriminator` anywhere
|
|
38
|
-
const HAS_DEFAULT = 1 << 11; // `default` anywhere
|
|
39
|
-
const HAS_ALLOF = 1 << 12; // `allOf` anywhere
|
|
40
|
-
const HAS_FORMAT = 1 << 13; // `format` anywhere
|
|
41
|
-
const KEY_BITS = new Map([
|
|
42
|
-
['$ref', HAS_REF | BEYOND_ITEMS], ['$dynamicRef', BEYOND_ITEMS], ['allOf', HAS_ALLOF | BEYOND_ITEMS],
|
|
43
|
-
['anyOf', BEYOND_ITEMS], ['oneOf', BEYOND_ITEMS], ['not', BEYOND_ITEMS], ['if', BEYOND_ITEMS],
|
|
44
|
-
['then', BEYOND_ITEMS], ['else', BEYOND_ITEMS], ['additionalProperties', BEYOND_ITEMS],
|
|
45
|
-
['patternProperties', BEYOND_ITEMS], ['prefixItems', BEYOND_ITEMS], ['additionalItems', BEYOND_ITEMS],
|
|
46
|
-
['contains', BEYOND_ITEMS], ['dependentSchemas', BEYOND_ITEMS], ['dependencies', BEYOND_ITEMS],
|
|
47
|
-
['propertyNames', BEYOND_ITEMS], ['unevaluatedProperties', BEYOND_ITEMS], ['unevaluatedItems', BEYOND_ITEMS],
|
|
48
|
-
['discriminator', DISCRIMINATOR], ['default', HAS_DEFAULT], ['format', HAS_FORMAT],
|
|
49
|
-
]);
|
|
33
|
+
// Questions compat asks of a schema it is about to compile, read on the same
|
|
34
|
+
// walk so that a document is not walked again for them. compat sets the keys
|
|
35
|
+
// and their bits (setExtraKeys); without compat the walk reads none.
|
|
36
|
+
let EXTRA = null;
|
|
50
37
|
|
|
51
38
|
// Everything only draft-07 normalization acts on. A schema of another dialect
|
|
52
39
|
// can carry these without them meaning anything, so they are read together
|
|
@@ -71,7 +58,7 @@ const ANCHOR_ID_RE = /^#[A-Za-z][A-Za-z0-9_.:-]*$/;
|
|
|
71
58
|
// This assumes a schema is not mutated after being handed to a Validator,
|
|
72
59
|
// which is already true of everything else here: the compile cache, the
|
|
73
60
|
// identity cache and the schema map would all be stale too.
|
|
74
|
-
|
|
61
|
+
let CACHE = new WeakMap();
|
|
75
62
|
|
|
76
63
|
function scan(schema) {
|
|
77
64
|
if (typeof schema !== 'object' || schema === null) return 0;
|
|
@@ -110,11 +97,13 @@ function scan(schema) {
|
|
|
110
97
|
}
|
|
111
98
|
// Every key, not just the subschema keywords, so this cannot miss a place
|
|
112
99
|
// the normalizers would reach.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
100
|
+
if (EXTRA !== null) {
|
|
101
|
+
for (let i = 0; i < keys.length; i++) {
|
|
102
|
+
const b = EXTRA.get(keys[i]);
|
|
103
|
+
if (b !== undefined) bits |= b;
|
|
104
|
+
}
|
|
117
105
|
}
|
|
106
|
+
for (let i = 0; i < keys.length; i++) walk(node[keys[i]]);
|
|
118
107
|
};
|
|
119
108
|
|
|
120
109
|
walk(schema);
|
|
@@ -146,10 +135,12 @@ module.exports = {
|
|
|
146
135
|
EXCLUSIVE_BOOL,
|
|
147
136
|
ENCODED_REF,
|
|
148
137
|
DRAFT7_WORK,
|
|
149
|
-
HAS_REF,
|
|
150
|
-
BEYOND_ITEMS,
|
|
151
|
-
DISCRIMINATOR,
|
|
152
|
-
HAS_DEFAULT,
|
|
153
|
-
HAS_ALLOF,
|
|
154
|
-
HAS_FORMAT,
|
|
155
138
|
};
|
|
139
|
+
|
|
140
|
+
// A schema scanned before the keys were set is scanned again: what was
|
|
141
|
+
// remembered for it does not answer the new questions.
|
|
142
|
+
function setExtraKeys(map) {
|
|
143
|
+
EXTRA = map;
|
|
144
|
+
CACHE = new WeakMap();
|
|
145
|
+
}
|
|
146
|
+
Object.defineProperty(module.exports, '_setExtraKeys', { value: setExtraKeys, enumerable: false });
|
package/lib/tier0.js
CHANGED
|
@@ -173,6 +173,7 @@ function tier0ValidateObject(plan, data) {
|
|
|
173
173
|
// treated as absent, as the interpreted engine treats it.
|
|
174
174
|
if (v !== undefined && (c.protoName || (_PN ? data.__proto__ : Object.getPrototypeOf(data)) !== Object.prototype) && !_hop.call(data, c.key)) v = undefined;
|
|
175
175
|
if (v === undefined) {
|
|
176
|
+
// A required key holding undefined is missing, as in every engine.
|
|
176
177
|
if (reqMask & (1 << i)) return false;
|
|
177
178
|
continue;
|
|
178
179
|
}
|
package/lib/unique-items.js
CHANGED
|
@@ -22,17 +22,28 @@ function _deq(a, b) {
|
|
|
22
22
|
if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;
|
|
23
23
|
var aa = Array.isArray(a);
|
|
24
24
|
if (aa !== Array.isArray(b)) return false;
|
|
25
|
-
var i;
|
|
25
|
+
var i, k, x, y;
|
|
26
26
|
if (aa) {
|
|
27
27
|
if (a.length !== b.length) return false;
|
|
28
|
-
for (i =
|
|
28
|
+
for (i = a.length; i-- !== 0;) if (!_deq(a[i], b[i])) return false;
|
|
29
29
|
return true;
|
|
30
30
|
}
|
|
31
31
|
var ka = Object.keys(a);
|
|
32
32
|
if (ka.length !== Object.keys(b).length) return false;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
// Keys and scalar values first, nested values after, from the last key back:
|
|
34
|
+
// two items of a list most often differ in a name, a path or an id, and
|
|
35
|
+
// walking a nested list first made each comparison of rust-project's crates
|
|
36
|
+
// three times what it needed (250 against 78 ns for the three; the default
|
|
37
|
+
// validator's deep equal takes 115).
|
|
38
|
+
for (i = ka.length; i-- !== 0;) {
|
|
39
|
+
k = ka[i];
|
|
40
|
+
if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
|
|
41
|
+
x = a[k]; y = b[k];
|
|
42
|
+
if (x !== y && (x === null || y === null || typeof x !== 'object' || typeof y !== 'object')) return false;
|
|
43
|
+
}
|
|
44
|
+
for (i = ka.length; i-- !== 0;) {
|
|
45
|
+
k = ka[i]; x = a[k];
|
|
46
|
+
if (x !== null && typeof x === 'object' && !_deq(x, b[k])) return false;
|
|
36
47
|
}
|
|
37
48
|
return true;
|
|
38
49
|
}
|