ata-validator 1.53.0 → 1.54.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.
@@ -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-06 are one each; 2019-09 is seven. Sources:
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, and
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, unmodified.
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":{}}}}],
@@ -213,11 +213,18 @@ function install(deps) {
213
213
  const objs = [];
214
214
  if (P.required !== null) {
215
215
  const req = P.required;
216
- if (req.length === 1) { const k = req[0]; objs.push((d) => Object.hasOwn(d, k)); }
217
- else objs.push((d) => {
216
+ // A required key holding undefined is missing; for a declared
217
+ // property the property step finds it (requiredInProps).
218
+ const undef = P.requiredUndef;
219
+ 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)); }
220
+ else if (undef === null) objs.push((d) => {
218
221
  for (let i = 0; i < req.length; i++) if (!Object.hasOwn(d, req[i])) return false;
219
222
  return true;
220
223
  });
224
+ else objs.push((d) => {
225
+ for (let i = 0; i < req.length; i++) if (!Object.hasOwn(d, req[i]) || (undef[i] && d[req[i]] === undefined)) return false;
226
+ return true;
227
+ });
221
228
  }
222
229
  if (P.minProperties !== undefined) { const m = P.minProperties; objs.push((d) => Object.keys(d).length >= m); }
223
230
  if (P.maxProperties !== undefined) { const m = P.maxProperties; objs.push((d) => Object.keys(d).length <= m); }
@@ -490,8 +497,10 @@ function install(deps) {
490
497
  // Declared properties: a linear scan over a short list beats a Map
491
498
  // lookup, and most schemas declare a handful.
492
499
  const propKeys = props !== null ? [...props.keys()] : null;
500
+ // req: the key is also required, so holding undefined it is missing.
501
+ const reqIn = P.requiredInProps;
493
502
  const propList = props !== null
494
- ? [...props.values()].map((entry) => ({ fn: child(entry.node), seg: entry.seg, schemaSeg: entry.schemaSeg }))
503
+ ? [...props].map(([key, entry]) => ({ fn: child(entry.node), seg: entry.seg, schemaSeg: entry.schemaSeg, req: reqIn !== null && reqIn.has(key) }))
495
504
  : null;
496
505
  const propMap = props !== null && props.size > 8 ? new Map(propKeys.map((k, i) => [k, propList[i]])) : null;
497
506
  const lookup = propMap !== null
@@ -533,12 +542,18 @@ function install(deps) {
533
542
  let evaluated = false;
534
543
  const prop = lookup !== null ? lookup(key) : undefined;
535
544
  // A declared property whose value is undefined is not checked (see
536
- // the interpreter's eval).
545
+ // the interpreter's eval); required, it is missing.
537
546
  if (prop !== undefined) {
538
547
  const value = data[key];
539
- if (value !== undefined && !((prop.fn.leaf && prop.fn.v(value, stack)) || prop.fn.c(value, errors, instancePath + prop.seg, schemaPath + prop.schemaSeg, stack))) {
548
+ if (value !== undefined) {
549
+ if (!((prop.fn.leaf && prop.fn.v(value, stack)) || prop.fn.c(value, errors, instancePath + prop.seg, schemaPath + prop.schemaSeg, stack))) {
550
+ ok = false;
551
+ if (errors === NOERRORS) return false;
552
+ }
553
+ } else if (prop.req) {
540
554
  ok = false;
541
555
  if (errors === NOERRORS) return false;
556
+ errors.push(err('required', 'required', instancePath, schemaPath + '/required', { missingProperty: key }, `must have required property '${key}'`));
542
557
  }
543
558
  evaluated = true;
544
559
  }
@@ -582,7 +597,8 @@ function install(deps) {
582
597
  const key = propKeys[i];
583
598
  if (!Object.hasOwn(data, key)) continue;
584
599
  const value = data[key];
585
- if (value !== undefined && !propList[i].fn.v(value, stack)) return false;
600
+ if (value !== undefined) { if (!propList[i].fn.v(value, stack)) return false; }
601
+ else if (propList[i].req) return false;
586
602
  if (collect) addProp(rec, key);
587
603
  }
588
604
  return true;
@@ -599,7 +615,8 @@ function install(deps) {
599
615
  const prop = lookup !== null ? lookup(key) : undefined;
600
616
  if (prop !== undefined) {
601
617
  const value = data[key];
602
- if (value !== undefined && !prop.fn.v(value, stack)) return false;
618
+ if (value !== undefined) { if (!prop.fn.v(value, stack)) return false; }
619
+ else if (prop.req) return false;
603
620
  evaluated = true;
604
621
  }
605
622
  if (patterns !== null) {
@@ -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
- // The last character is stepped with the end-of-input flag set and is
477
- // handled after the loop, so the loop body selects no table and tests
478
- // no position.
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 (c < 128) {
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
- if ((flags[id] & 2) === 2) return false
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) {
@@ -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 (compat.js), read
34
- // on the same walk so that a document is not walked again for them.
35
- const HAS_REF = 1 << 8; // `$ref` anywhere
36
- const BEYOND_ITEMS = 1 << 9; // a subschema keyword other than `properties` and object `items`
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
- const CACHE = new WeakMap();
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
- for (let i = 0; i < keys.length; i++) {
114
- const b = KEY_BITS.get(keys[i]);
115
- if (b !== undefined) bits |= b;
116
- walk(node[keys[i]]);
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
  }
@@ -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 = 0; i < a.length; i++) if (!_deq(a[i], b[i])) return false;
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
- for (i = 0; i < ka.length; i++) {
34
- var k = ka[i];
35
- if (!Object.prototype.hasOwnProperty.call(b, k) || !_deq(a[k], b[k])) return false;
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
  }
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+
3
+ // RFC 3986 URI and URI-reference, RFC 3987 IRI and IRI-reference, as one walk
4
+ // over the string with no slicing. `mode` bit 1 asks for a scheme (uri, iri),
5
+ // bit 2 lets non-ASCII through (iri, iri-reference). The rules the walk holds
6
+ // that a character-class scan does not: a second "@" in the authority, a port
7
+ // that is not digits, square brackets anywhere but around an IP literal, the
8
+ // literal itself (RFC 3986 IPv6 with an embedded IPv4 of no leading zeros, an
9
+ // RFC 6874 zone id, IPvFuture), a colon in the first segment of a relative
10
+ // path, and a second "#". A reference made only of unreserved characters,
11
+ // sub-delimiters, "/" and "@" (a file name, a path) is one whatever its
12
+ // shape, and is answered by one scan before any of that.
13
+ //
14
+ // A factory, like lib/safe-regex.js: the interpreted engine calls the
15
+ // function it returns, and the code generator emits the factory's source
16
+ // text (`source`) into the program it builds, so a standalone module carries
17
+ // the same walk without importing it. Nothing inside the factory reads a
18
+ // free variable, and no comments inside it: its text is what gets emitted.
19
+ // `ip6` is the IPv6 check for a bracketed host, one string in: lib/formats.js
20
+ // gives its own and the generator emits ipv6Source, so neither carries a
21
+ // second copy. The slice it costs happens only on an IP literal.
22
+ function __ataUri (ip6) {
23
+ const C = new Uint8Array(128);
24
+ for (let i = 48; i < 58; i++) C[i] = 3;
25
+ for (let i = 65; i < 91; i++) C[i] = 3;
26
+ for (let i = 97; i < 123; i++) C[i] = 3;
27
+ C[43] = 3; C[45] = 3; C[46] = 3;
28
+ C[95] = 1; C[126] = 1;
29
+ C[33] = 1; C[36] = 1; C[38] = 1; C[39] = 1; C[40] = 1; C[41] = 1; C[42] = 1; C[44] = 1; C[59] = 1; C[61] = 1;
30
+ const H = new Uint8Array(128);
31
+ for (let i = 48; i < 58; i++) H[i] = 1;
32
+ for (let i = 65; i < 71; i++) H[i] = 1;
33
+ for (let i = 97; i < 103; i++) H[i] = 1;
34
+ function pct (s, i) {
35
+ const a = s.charCodeAt(i + 1), b = s.charCodeAt(i + 2);
36
+ return a < 128 && b < 128 && H[a] === 1 && H[b] === 1;
37
+ }
38
+ const P = new Uint8Array(128);
39
+ for (let i = 0; i < 128; i++) P[i] = C[i] & 1;
40
+ P[58] = 1; P[64] = 1; P[47] = 1;
41
+ const Q = new Uint8Array(P);
42
+ Q[63] = 1;
43
+ const S = new Uint8Array(P);
44
+ S[58] = 0;
45
+ function literal (s, from, to) {
46
+ if (from >= to) return false;
47
+ const f = s.charCodeAt(from);
48
+ if (f === 118 || f === 86) {
49
+ let i = from + 1, h = 0;
50
+ for (; i < to; i++) { const c = s.charCodeAt(i); if (c < 128 && H[c] === 1) h++; else break; }
51
+ if (h === 0 || s.charCodeAt(i) !== 46 || i + 1 >= to) return false;
52
+ for (i++; i < to; i++) { const c = s.charCodeAt(i); if (!(c < 128 && ((C[c] & 1) === 1 || c === 58))) return false; }
53
+ return true;
54
+ }
55
+ let z = -1;
56
+ for (let i = from; i < to; i++) if (s.charCodeAt(i) === 37) { z = i; break; }
57
+ if (z === -1) return ip6(s.slice(from, to));
58
+ if (s.charCodeAt(z + 1) !== 50 || s.charCodeAt(z + 2) !== 53 || z + 3 >= to) return false;
59
+ for (let i = z + 3; i < to; i++) {
60
+ const c = s.charCodeAt(i);
61
+ if (c === 37) { if (!pct(s, i)) return false; i += 2; continue; }
62
+ if (!(c < 128 && (C[c] & 1) === 1)) return false;
63
+ }
64
+ return ip6(s.slice(from, z));
65
+ }
66
+ return function (s, mode) {
67
+ const n = s.length;
68
+ const wide = (mode & 2) !== 0;
69
+ if ((mode & 1) === 0 && !(s.charCodeAt(0) === 47 && s.charCodeAt(1) === 47)) {
70
+ let k = 0;
71
+ for (; k < n; k++) { const c = s.charCodeAt(k); if (c > 127 || S[c] === 0) break; }
72
+ if (k === n) return true;
73
+ }
74
+ let i = 0, c = s.charCodeAt(0), scheme = false;
75
+ if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)) {
76
+ let j = 1;
77
+ while (j < n) { c = s.charCodeAt(j); if (c === 58 || c > 127 || (C[c] & 2) === 0) break; j++; }
78
+ if (j < n && c === 58) { scheme = true; i = j + 1; }
79
+ }
80
+ if (!scheme && (mode & 1) !== 0) return false;
81
+ let auth = false;
82
+ if (s.charCodeAt(i) === 47 && s.charCodeAt(i + 1) === 47) {
83
+ auth = true;
84
+ const st = i + 2;
85
+ let end = n, at = -1, br = false, brAt = false, colon = -1;
86
+ for (let k = st; k < n; k++) {
87
+ c = s.charCodeAt(k);
88
+ if (c === 47 || c === 63 || c === 35) { end = k; break; }
89
+ if (c < 128) {
90
+ if ((C[c] & 1) === 1) continue;
91
+ if (c === 37) { if (!pct(s, k)) return false; k += 2; continue; }
92
+ if (c === 58) { if (colon === -1) colon = k; continue; }
93
+ if (c === 64) { if (at !== -1) return false; at = k; brAt = br; colon = -1; continue; }
94
+ if (c === 91 || c === 93) { br = true; continue; }
95
+ return false;
96
+ }
97
+ if (!wide) return false;
98
+ }
99
+ if (brAt) return false;
100
+ const hs = at === -1 ? st : at + 1;
101
+ if (s.charCodeAt(hs) === 91) {
102
+ let close = -1;
103
+ for (let k = hs + 1; k < end; k++) if (s.charCodeAt(k) === 93) { close = k; break; }
104
+ if (close === -1 || !literal(s, hs + 1, close)) return false;
105
+ if (close + 1 < end) {
106
+ if (s.charCodeAt(close + 1) !== 58) return false;
107
+ for (let k = close + 2; k < end; k++) { c = s.charCodeAt(k); if (c < 48 || c > 57) return false; }
108
+ }
109
+ } else {
110
+ if (br) return false;
111
+ if (colon !== -1) for (let k = colon + 1; k < end; k++) { c = s.charCodeAt(k); if (c < 48 || c > 57) return false; }
112
+ }
113
+ i = end;
114
+ }
115
+ if (!scheme && !auth && s.charCodeAt(i) !== 47) {
116
+ for (; i < n; i++) {
117
+ c = s.charCodeAt(i);
118
+ if (c === 47 || c === 63 || c === 35) break;
119
+ if (c === 58) return false;
120
+ if (c === 37) { if (!pct(s, i)) return false; i += 2; continue; }
121
+ if (c < 128 ? P[c] === 0 : !wide) return false;
122
+ }
123
+ }
124
+ for (; i < n; i++) {
125
+ c = s.charCodeAt(i);
126
+ if (c < 128 ? P[c] === 0 : !wide) {
127
+ if (c === 63 || c === 35) break;
128
+ if (c === 37) { const a = s.charCodeAt(i + 1), b = s.charCodeAt(i + 2); if (!(a < 128 && b < 128 && H[a] === 1 && H[b] === 1)) return false; i += 2; continue; }
129
+ return false;
130
+ }
131
+ }
132
+ if (i >= n) return true;
133
+ if (c === 63) {
134
+ for (i++; i < n; i++) {
135
+ c = s.charCodeAt(i);
136
+ if (c < 128 ? Q[c] === 0 : !wide) {
137
+ if (c === 35) break;
138
+ if (c === 37) { if (!pct(s, i)) return false; i += 2; continue; }
139
+ return false;
140
+ }
141
+ }
142
+ if (i >= n) return true;
143
+ }
144
+ for (i++; i < n; i++) {
145
+ c = s.charCodeAt(i);
146
+ if (c < 128 ? Q[c] === 0 : !wide) {
147
+ if (c === 37) { if (!pct(s, i)) return false; i += 2; continue; }
148
+ return false;
149
+ }
150
+ }
151
+ return true;
152
+ };
153
+ }
154
+
155
+ module.exports = { __ataUri };
@@ -33,12 +33,13 @@ let _aotLoader = null;
33
33
  function _registerAot(load) {
34
34
  _aotLoader = load;
35
35
  }
36
- const { draftCode, normalizeDraft2019, normalizeDraft7, normalizeNullable, normalizeExclusiveBounds, stripFormatAssertions } = require("./draft7");
37
- const { enabledKeywords, stripDisabledKeywords } = require("./vocabularies");
36
+ // draft7.js, vocabularies.js, shape-classifier.js and tier0.js load where
37
+ // they are first used: a fresh process validating a 2020-12 schema runs none
38
+ // of their code, and reading them cost it about a tenth of what it loads.
39
+ const _draft7 = () => require("./draft7");
40
+ const _vocabularies = () => require("./vocabularies");
38
41
  const { needsNormalization } = require("./schema-scan");
39
- const { isV1Dialect } = require("./dialect");
40
- const { classify } = require("./shape-classifier");
41
- const { buildTier0Plan, tier0Validate } = require("./tier0");
42
+ const { isV1Dialect, draftCode } = require("./dialect");
42
43
 
43
44
  // The closure defaults pass. Only a validator without the generated
44
45
  // preprocess pass calls it, so it loads on that first call; loading it with
@@ -470,8 +471,9 @@ function _normalizeCallerSchema(s, inheritDraft7) {
470
471
  function _normalizeCallerSchemaUncached(s, inheritDraft7) {
471
472
  const declared = s && typeof s === 'object' ? s.$schema : undefined
472
473
  const code = declared !== undefined ? draftCode(declared) : (inheritDraft7 || 0)
473
- const needsDraft7 = code === 1 || code === 2
474
- const draft6 = code === 2
474
+ const needsDraft7 = code === 1 || code === 2 || code === 4
475
+ const draft4 = code === 4
476
+ const draft6 = code === 2 || draft4
475
477
  const draft2019 = code === 3
476
478
  // One walk answers whether there is anything to do. Almost always there is
477
479
  // not, and then the serialize, clone, normalize, serialize, compare below is
@@ -482,7 +484,8 @@ function _normalizeCallerSchemaUncached(s, inheritDraft7) {
482
484
  if (!draft6 && !draft2019 && !needsNormalization(s, needsDraft7)) return s
483
485
 
484
486
  const copy = _deepCloneWithSymbols(s)
485
- if (needsDraft7) normalizeDraft7(copy, true, draft6)
487
+ const { normalizeDraft7, normalizeDraft2019, normalizeNullable, normalizeExclusiveBounds } = _draft7()
488
+ if (needsDraft7) normalizeDraft7(copy, true, draft6, draft4)
486
489
  if (draft2019) normalizeDraft2019(copy, true)
487
490
  normalizeNullable(copy)
488
491
  normalizeExclusiveBounds(copy)
@@ -615,7 +618,11 @@ function _applyVocabularies(schemaObj, original, schemaMap) {
615
618
  if (!schemaObj || typeof schemaObj !== 'object') return schemaObj
616
619
  const declared = schemaObj.$schema
617
620
  if (typeof declared !== 'string') return schemaObj
618
- const enabled = enabledKeywords(schemaMap.get(declared))
621
+ // Only a meta-schema with a $vocabulary can disable a keyword.
622
+ const meta = schemaMap.get(declared)
623
+ if (!meta || typeof meta !== 'object' || !meta.$vocabulary || typeof meta.$vocabulary !== 'object') return schemaObj
624
+ const { enabledKeywords, stripDisabledKeywords } = _vocabularies()
625
+ const enabled = enabledKeywords(meta)
619
626
  if (!enabled) return schemaObj
620
627
  // `original` is the caller's own object when it reached here unchanged, and
621
628
  // that one is never mutated.
@@ -724,7 +731,7 @@ function _materializeSchema(self) {
724
731
  // Always on a copy: a normalized schema is shared by every validator built
725
732
  // from the same object (see _normalizeCallerSchema), so stripping in place
726
733
  // would take format checks away from the others.
727
- schemaObj = stripFormatAssertions(_deepCloneWithSymbols(schemaObj));
734
+ schemaObj = _draft7().stripFormatAssertions(_deepCloneWithSymbols(schemaObj));
728
735
  }
729
736
  // A keyword used only in a schema reached through $ref counts too: the
730
737
  // interpreted engine applies it there, and every path that does not know the
@@ -2395,8 +2402,10 @@ _defineLazyMethod('isValidObject', (self) => {
2395
2402
  return self.isValidObject(data);
2396
2403
  }
2397
2404
  // Lazy: classify + build tier 0 plan on first call, not in constructor.
2398
- const _tier = classify(self._schemaObj);
2405
+ const _tier = require("./shape-classifier").classify(self._schemaObj);
2399
2406
  if (_tier.tier === 0) {
2407
+ // Bound once here, so the verdict below reads a local, not the module.
2408
+ const { buildTier0Plan, tier0Validate } = require("./tier0");
2400
2409
  const _plan = buildTier0Plan(self._schemaObj);
2401
2410
  let _n = 0;
2402
2411
  _bindVerdict(self, (d) => {
package/lib/version.js CHANGED
@@ -7,4 +7,4 @@
7
7
  //
8
8
  // Kept in lockstep with package.json by `tests/test_version_sync.js`.
9
9
 
10
- module.exports = '1.53.0';
10
+ module.exports = '1.54.0';