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.
Files changed (53) hide show
  1. package/README.md +63 -8
  2. package/build.mjs +1 -0
  3. package/index.browser.mjs +23 -4
  4. package/index.bundle.mjs +14 -4
  5. package/index.edge.mjs +14 -4
  6. package/index.js +7 -2
  7. package/lib/aot-build.js +18 -0
  8. package/lib/codegen-engine.js +72 -3
  9. package/lib/compiled-common.js +2 -1
  10. package/lib/email-literal.js +4 -1
  11. package/lib/formats-source.js +39 -40
  12. package/lib/interpreter-core.js +148 -70
  13. package/lib/ir/emit-deps.mjs +131 -0
  14. package/lib/ir/emit.mjs +707 -0
  15. package/lib/ir/front.mjs +865 -0
  16. package/lib/ir/index.mjs +10 -0
  17. package/lib/ir/refs.mjs +108 -0
  18. package/lib/ir/run.mjs +520 -0
  19. package/lib/ir/table.mjs +233 -0
  20. package/lib/js-compiler-errors.js +9 -4
  21. package/lib/kernels/collapse.mjs +114 -0
  22. package/lib/kernels/custom.mjs +70 -0
  23. package/lib/kernels/fill.mjs +6 -0
  24. package/lib/kernels/formats-small.mjs +40 -0
  25. package/lib/kernels/formats.mjs +29 -0
  26. package/lib/kernels/index.mjs +11 -0
  27. package/lib/kernels/kernels.mjs +285 -0
  28. package/lib/kernels/leaves.mjs +28 -0
  29. package/lib/kernels/order.mjs +25 -0
  30. package/lib/kernels/plan-rich.mjs +17 -0
  31. package/lib/kernels/plan.d.ts +9 -0
  32. package/lib/kernels/plan.mjs +128 -0
  33. package/lib/kernels/preprocess.mjs +223 -0
  34. package/lib/kernels/rank.mjs +97 -0
  35. package/lib/kernels/rt.mjs +249 -0
  36. package/lib/kernels/table.mjs +182 -0
  37. package/lib/kernels/unique.mjs +157 -0
  38. package/lib/native-positions.js +1 -1
  39. package/lib/openapi.d.ts +2 -0
  40. package/lib/openapi.js +128 -0
  41. package/lib/plan-compiler.js +113 -14
  42. package/lib/public-api.js +7 -2
  43. package/lib/rejections.js +3 -1
  44. package/lib/schema-helpers.js +12 -0
  45. package/lib/schema-helpers.mjs +20 -0
  46. package/lib/schema-order.js +4 -2
  47. package/lib/shape-classifier.js +13 -1
  48. package/lib/standard-schema.js +1 -1
  49. package/lib/tier0.js +38 -8
  50. package/lib/validator-core.js +59 -36
  51. package/lib/version.js +1 -1
  52. package/lite.js +7 -2
  53. package/package.json +26 -8
@@ -89,8 +89,6 @@ function indexSchemas(rootSchema, schemaMap, flat) {
89
89
  state.rootBase = rootBase;
90
90
  if (flat === true && !schemaMap && typeof rootSchema === 'object' && rootSchema !== null) {
91
91
  state.resources.set(rootBase, rootSchema);
92
- state.anchors.set(rootBase, new Map());
93
- state.dynamicAnchors.set(rootBase, new Map());
94
92
  state.nodeBase.set(rootSchema, rootBase);
95
93
  return state;
96
94
  }
@@ -107,11 +105,15 @@ function indexSchemas(rootSchema, schemaMap, flat) {
107
105
  return state;
108
106
  }
109
107
 
108
+ function anchorMap(maps, baseUri) {
109
+ let m = maps.get(baseUri);
110
+ if (m === undefined) maps.set(baseUri, (m = new Map()));
111
+ return m;
112
+ }
113
+
110
114
  function indexResource(node, baseUri, state) {
111
115
  if (typeof node !== 'object' || node === null) return;
112
116
  if (!state.resources.has(baseUri)) state.resources.set(baseUri, node);
113
- if (!state.anchors.has(baseUri)) state.anchors.set(baseUri, new Map());
114
- if (!state.dynamicAnchors.has(baseUri)) state.dynamicAnchors.set(baseUri, new Map());
115
117
  walkSchema(node, baseUri, state, true);
116
118
  }
117
119
 
@@ -128,11 +130,14 @@ function walkSchema(node, baseUri, state, isResourceRoot) {
128
130
  return;
129
131
  }
130
132
 
131
- if (typeof node.$anchor === 'string') state.anchors.get(baseUri).set(node.$anchor, node);
133
+ // A resource's anchor maps are made at its first anchor: most schemas have
134
+ // none, and two empty maps per resource were a sixth of a small schema's
135
+ // interpreter. Every reader takes a missing map as no anchor.
136
+ if (typeof node.$anchor === 'string') anchorMap(state.anchors, baseUri).set(node.$anchor, node);
132
137
  if (typeof node.$dynamicAnchor === 'string') {
133
- state.dynamicAnchors.get(baseUri).set(node.$dynamicAnchor, node);
138
+ anchorMap(state.dynamicAnchors, baseUri).set(node.$dynamicAnchor, node);
134
139
  // A $dynamicAnchor is also addressable as a plain anchor.
135
- state.anchors.get(baseUri).set(node.$dynamicAnchor, node);
140
+ anchorMap(state.anchors, baseUri).set(node.$dynamicAnchor, node);
136
141
  }
137
142
 
138
143
  for (const kw of SCHEMA_KEYWORDS.single) {
@@ -242,6 +247,7 @@ function enumMember(P, data) {
242
247
  // JSON equality: the same function the code generator emits as `_deq`, so the
243
248
  // engines agree on what equal means (an object's own keys, in any order).
244
249
  const deepEqual = _deq;
250
+ const propEnum = Object.prototype.propertyIsEnumerable;
245
251
 
246
252
  function codePointLength(s) {
247
253
  let n = 0;
@@ -364,6 +370,60 @@ function isObjectMap(v) {
364
370
  // per section so eval() can skip whole blocks without touching the schema
365
371
  // object. Plans have a fixed shape, which keeps eval() monomorphic where the
366
372
  // raw schema objects are not.
373
+ // The keyword values of a schema node, from one pass over its enumerable
374
+ // names (its own, as JSON gives them), in a record of one shape for link().
375
+ const KEYWORD_RECORD = () => ({ '$ref': undefined, '$dynamicRef': undefined, type: undefined, enum: undefined, const: undefined, minimum: undefined, maximum: undefined, exclusiveMinimum: undefined, exclusiveMaximum: undefined, multipleOf: undefined, minLength: undefined, maxLength: undefined, pattern: undefined, format: undefined, minItems: undefined, maxItems: undefined, uniqueItems: undefined, prefixItems: undefined, items: undefined, contains: undefined, minContains: undefined, maxContains: undefined, required: undefined, minProperties: undefined, maxProperties: undefined, dependentRequired: undefined, propertyNames: undefined, properties: undefined, patternProperties: undefined, additionalProperties: undefined, dependentSchemas: undefined, propertyDependencies: undefined, allOf: undefined, anyOf: undefined, oneOf: undefined, not: undefined, if: undefined, then: undefined, else: undefined, unevaluatedProperties: undefined, unevaluatedItems: undefined });
376
+ function readKeywords (schema) {
377
+ const S = KEYWORD_RECORD();
378
+ for (const k in schema) {
379
+ const v = schema[k];
380
+ switch (k) {
381
+ case '$ref': S['$ref'] = v; break;
382
+ case '$dynamicRef': S['$dynamicRef'] = v; break;
383
+ case 'type': S.type = v; break;
384
+ case 'enum': S.enum = v; break;
385
+ case 'const': S.const = v; break;
386
+ case 'minimum': S.minimum = v; break;
387
+ case 'maximum': S.maximum = v; break;
388
+ case 'exclusiveMinimum': S.exclusiveMinimum = v; break;
389
+ case 'exclusiveMaximum': S.exclusiveMaximum = v; break;
390
+ case 'multipleOf': S.multipleOf = v; break;
391
+ case 'minLength': S.minLength = v; break;
392
+ case 'maxLength': S.maxLength = v; break;
393
+ case 'pattern': S.pattern = v; break;
394
+ case 'format': S.format = v; break;
395
+ case 'minItems': S.minItems = v; break;
396
+ case 'maxItems': S.maxItems = v; break;
397
+ case 'uniqueItems': S.uniqueItems = v; break;
398
+ case 'prefixItems': S.prefixItems = v; break;
399
+ case 'items': S.items = v; break;
400
+ case 'contains': S.contains = v; break;
401
+ case 'minContains': S.minContains = v; break;
402
+ case 'maxContains': S.maxContains = v; break;
403
+ case 'required': S.required = v; break;
404
+ case 'minProperties': S.minProperties = v; break;
405
+ case 'maxProperties': S.maxProperties = v; break;
406
+ case 'dependentRequired': S.dependentRequired = v; break;
407
+ case 'propertyNames': S.propertyNames = v; break;
408
+ case 'properties': S.properties = v; break;
409
+ case 'patternProperties': S.patternProperties = v; break;
410
+ case 'additionalProperties': S.additionalProperties = v; break;
411
+ case 'dependentSchemas': S.dependentSchemas = v; break;
412
+ case 'propertyDependencies': S.propertyDependencies = v; break;
413
+ case 'allOf': S.allOf = v; break;
414
+ case 'anyOf': S.anyOf = v; break;
415
+ case 'oneOf': S.oneOf = v; break;
416
+ case 'not': S.not = v; break;
417
+ case 'if': S.if = v; break;
418
+ case 'then': S.then = v; break;
419
+ case 'else': S.else = v; break;
420
+ case 'unevaluatedProperties': S.unevaluatedProperties = v; break;
421
+ case 'unevaluatedItems': S.unevaluatedItems = v; break;
422
+ }
423
+ }
424
+ return S;
425
+ }
426
+
367
427
  class Plan {
368
428
  // The plan is registered in the cache before `link` runs, so a schema
369
429
  // object that contains itself structurally resolves to the same plan
@@ -374,6 +434,13 @@ class Plan {
374
434
 
375
435
  link(interp) {
376
436
  const schema = this.schema;
437
+ // The keywords read once, by one pass over the schema's own names, into
438
+ // a record of one shape: read by name on the schema, each absent keyword
439
+ // was a lookup that missed on an object of yet another shape, and with
440
+ // many schemas in a process the 41 reads cost 3.7 us a node against 8 ns
441
+ // for the pass (measured on 64 shapes). Reads on the record hit one
442
+ // shape every time.
443
+ const S = readKeywords(schema);
377
444
  // Children are linked as plans (or booleans) up front, so a visit never
378
445
  // goes back through the plan cache. Reference targets are resolved at
379
446
  // evaluation time and cached per base URI.
@@ -382,21 +449,21 @@ class Plan {
382
449
  this.refBase0 = undefined;
383
450
  this.refRes0 = null;
384
451
  this.dynCache = null;
385
- this.ref = typeof schema.$ref === 'string' ? schema.$ref : null;
386
- this.dynamicRef = typeof schema.$dynamicRef === 'string' ? schema.$dynamicRef : null;
452
+ this.ref = typeof S.$ref === 'string' ? S.$ref : null;
453
+ this.dynamicRef = typeof S.$dynamicRef === 'string' ? S.$dynamicRef : null;
387
454
  this.tracked = this.ref !== null || this.dynamicRef !== null;
388
455
 
389
456
  this.typeMask = 0;
390
457
  this.typeNames = null;
391
- if (schema.type !== undefined) {
392
- const types = Array.isArray(schema.type) ? schema.type : [schema.type];
458
+ if (S.type !== undefined) {
459
+ const types = Array.isArray(S.type) ? S.type : [S.type];
393
460
  let mask = 0;
394
461
  for (const t of types) mask |= typeBit(t);
395
462
  this.typeMask = mask;
396
463
  this.typeNames = types;
397
464
  }
398
- this.hasType = schema.type !== undefined;
399
- this.enum = schema.enum !== undefined ? schema.enum : null;
465
+ this.hasType = S.type !== undefined;
466
+ this.enum = S.enum !== undefined ? S.enum : null;
400
467
  // Membership for enumMember(): the primitive values in a Set, so a long
401
468
  // enum answers in one lookup, and the object and array values, which only
402
469
  // deepEqual can compare, in a list. Scanning every value with deepEqual
@@ -412,28 +479,28 @@ class Plan {
412
479
  else if (!(typeof v === 'number' && v !== v)) this.enumPrims.add(v);
413
480
  }
414
481
  }
415
- this.hasConst = schema.const !== undefined;
416
- this.const = schema.const;
417
-
418
- this.minimum = typeof schema.minimum === 'number' ? schema.minimum : undefined;
419
- this.maximum = typeof schema.maximum === 'number' ? schema.maximum : undefined;
420
- this.exclusiveMinimum = typeof schema.exclusiveMinimum === 'number' ? schema.exclusiveMinimum : undefined;
421
- this.exclusiveMaximum = typeof schema.exclusiveMaximum === 'number' ? schema.exclusiveMaximum : undefined;
422
- this.multipleOf = typeof schema.multipleOf === 'number' ? schema.multipleOf : undefined;
482
+ this.hasConst = S.const !== undefined;
483
+ this.const = S.const;
484
+
485
+ this.minimum = typeof S.minimum === 'number' ? S.minimum : undefined;
486
+ this.maximum = typeof S.maximum === 'number' ? S.maximum : undefined;
487
+ this.exclusiveMinimum = typeof S.exclusiveMinimum === 'number' ? S.exclusiveMinimum : undefined;
488
+ this.exclusiveMaximum = typeof S.exclusiveMaximum === 'number' ? S.exclusiveMaximum : undefined;
489
+ this.multipleOf = typeof S.multipleOf === 'number' ? S.multipleOf : undefined;
423
490
  this.hasNumber = this.minimum !== undefined || this.maximum !== undefined || this.exclusiveMinimum !== undefined || this.exclusiveMaximum !== undefined || this.multipleOf !== undefined;
424
491
 
425
- this.minLength = schema.minLength;
426
- this.maxLength = schema.maxLength;
427
- this.pattern = schema.pattern !== undefined ? interp.pattern(schema.pattern) : null;
428
- this.patternSource = schema.pattern;
429
- this.format = schema.format;
492
+ this.minLength = S.minLength;
493
+ this.maxLength = S.maxLength;
494
+ this.pattern = S.pattern !== undefined ? interp.pattern(S.pattern) : null;
495
+ this.patternSource = S.pattern;
496
+ this.format = S.format;
430
497
  this.formatFn = null;
431
- if (schema.format !== undefined) {
498
+ if (S.format !== undefined) {
432
499
  // Own properties only, as the generated code reads them: an inherited
433
500
  // Object member ('hasOwnProperty', 'constructor') is not a check.
434
501
  const own = Object.prototype.hasOwnProperty;
435
502
  const uf = interp.userFormats;
436
- const f = schema.format;
503
+ const f = S.format;
437
504
  const table = require('./formats').formatChecks();
438
505
  const fc = (uf && typeof f === 'string' && own.call(uf, f) && typeof uf[f] === 'function') ? uf[f]
439
506
  : (typeof f === 'string' && own.call(table, f) && typeof table[f] === 'function' ? table[f] : null);
@@ -443,31 +510,31 @@ class Plan {
443
510
  // A format compat registered for numbers (`type: 'number'`), checked on
444
511
  // numbers as the reference checks it.
445
512
  const nf = interp.numberFormats;
446
- this.numFormatFn = schema.format !== undefined && nf !== null && typeof nf[schema.format] === 'function' ? nf[schema.format] : null;
513
+ this.numFormatFn = S.format !== undefined && nf !== null && typeof nf[S.format] === 'function' ? nf[S.format] : null;
447
514
  if (this.numFormatFn !== null) this.hasNumber = true;
448
515
 
449
- this.minItems = schema.minItems;
450
- this.maxItems = schema.maxItems;
451
- this.uniqueItems = schema.uniqueItems === true;
452
- this.prefixItems = Array.isArray(schema.prefixItems) ? schema.prefixItems.map(child) : null;
453
- this.items = schema.items !== undefined ? child(schema.items) : undefined;
454
- this.contains = schema.contains !== undefined ? child(schema.contains) : undefined;
455
- this.minContains = schema.minContains;
456
- this.maxContains = schema.maxContains;
516
+ this.minItems = S.minItems;
517
+ this.maxItems = S.maxItems;
518
+ this.uniqueItems = S.uniqueItems === true;
519
+ this.prefixItems = Array.isArray(S.prefixItems) ? S.prefixItems.map(child) : null;
520
+ this.items = S.items !== undefined ? child(S.items) : undefined;
521
+ this.contains = S.contains !== undefined ? child(S.contains) : undefined;
522
+ this.minContains = S.minContains;
523
+ this.maxContains = S.maxContains;
457
524
  this.hasArray = this.minItems !== undefined || this.maxItems !== undefined || this.uniqueItems || this.prefixItems !== null || this.items !== undefined || this.contains !== undefined;
458
525
 
459
- this.required = Array.isArray(schema.required) ? schema.required : null;
460
- this.minProperties = schema.minProperties;
461
- this.maxProperties = schema.maxProperties;
462
- this.dependentRequired = isObjectMap(schema.dependentRequired) ? Object.entries(schema.dependentRequired) : null;
463
- this.propertyNames = schema.propertyNames !== undefined ? child(schema.propertyNames) : undefined;
526
+ this.required = Array.isArray(S.required) ? S.required : null;
527
+ this.minProperties = S.minProperties;
528
+ this.maxProperties = S.maxProperties;
529
+ this.dependentRequired = isObjectMap(S.dependentRequired) ? Object.entries(S.dependentRequired) : null;
530
+ this.propertyNames = S.propertyNames !== undefined ? child(S.propertyNames) : undefined;
464
531
  this.properties = null;
465
- if (isObjectMap(schema.properties)) {
532
+ if (isObjectMap(S.properties)) {
466
533
  this.properties = new Map();
467
- for (const key of Object.keys(schema.properties)) {
534
+ for (const key of Object.keys(S.properties)) {
468
535
  const ek = escapePointer(key);
469
536
  // Escaped once here; the walk only concatenates.
470
- this.properties.set(key, { node: child(schema.properties[key]), seg: '/' + ek, schemaSeg: '/properties/' + ek });
537
+ this.properties.set(key, { node: child(S.properties[key]), seg: '/' + ek, schemaSeg: '/properties/' + ek });
471
538
  }
472
539
  }
473
540
  // A required key holding undefined is missing. Where the key is also a
@@ -486,26 +553,26 @@ class Plan {
486
553
  }
487
554
  }
488
555
  this.patternProperties = null;
489
- if (isObjectMap(schema.patternProperties)) {
490
- this.patternProperties = Object.keys(schema.patternProperties).map((src) => ({ src, re: interp.pattern(src), node: child(schema.patternProperties[src]) }));
556
+ if (isObjectMap(S.patternProperties)) {
557
+ this.patternProperties = Object.keys(S.patternProperties).map((src) => ({ src, re: interp.pattern(src), node: child(S.patternProperties[src]) }));
491
558
  }
492
- this.additionalProperties = schema.additionalProperties !== undefined ? child(schema.additionalProperties) : undefined;
493
- this.dependentSchemas = isObjectMap(schema.dependentSchemas) ? Object.entries(schema.dependentSchemas).map(([k, v]) => [k, child(v)]) : null;
559
+ this.additionalProperties = S.additionalProperties !== undefined ? child(S.additionalProperties) : undefined;
560
+ this.dependentSchemas = isObjectMap(S.dependentSchemas) ? Object.entries(S.dependentSchemas).map(([k, v]) => [k, child(v)]) : null;
494
561
  this.propertyDependencies = null;
495
- if (isObjectMap(schema.propertyDependencies)) {
496
- this.propertyDependencies = Object.entries(schema.propertyDependencies)
562
+ if (isObjectMap(S.propertyDependencies)) {
563
+ this.propertyDependencies = Object.entries(S.propertyDependencies)
497
564
  .filter(([, choices]) => isObjectMap(choices))
498
565
  .map(([k, choices]) => [k, new Map(Object.keys(choices).map((v) => [v, child(choices[v])]))]);
499
566
  }
500
567
  this.hasObject = this.required !== null || this.minProperties !== undefined || this.maxProperties !== undefined || this.dependentRequired !== null || this.propertyNames !== undefined || this.properties !== null || this.patternProperties !== null || this.additionalProperties !== undefined || this.dependentSchemas !== null || this.propertyDependencies !== null;
501
568
 
502
- this.allOf = Array.isArray(schema.allOf) ? schema.allOf.map(child) : null;
503
- this.anyOf = Array.isArray(schema.anyOf) ? schema.anyOf.map(child) : null;
504
- this.oneOf = Array.isArray(schema.oneOf) ? schema.oneOf.map(child) : null;
505
- this.not = schema.not !== undefined ? child(schema.not) : undefined;
506
- this.if = schema.if !== undefined ? child(schema.if) : undefined;
507
- this.then = schema.then !== undefined ? child(schema.then) : undefined;
508
- this.else = schema.else !== undefined ? child(schema.else) : undefined;
569
+ this.allOf = Array.isArray(S.allOf) ? S.allOf.map(child) : null;
570
+ this.anyOf = Array.isArray(S.anyOf) ? S.anyOf.map(child) : null;
571
+ this.oneOf = Array.isArray(S.oneOf) ? S.oneOf.map(child) : null;
572
+ this.not = S.not !== undefined ? child(S.not) : undefined;
573
+ this.if = S.if !== undefined ? child(S.if) : undefined;
574
+ this.then = S.then !== undefined ? child(S.then) : undefined;
575
+ this.else = S.else !== undefined ? child(S.else) : undefined;
509
576
  // Macro keywords: the schema the macro returns is applied in place, the
510
577
  // way the reference validator does it, so its errors carry the keyword's
511
578
  // path and the keyword adds an error of its own when that schema fails.
@@ -522,13 +589,13 @@ class Plan {
522
589
  }
523
590
  this.hasApplicators = this.allOf !== null || this.anyOf !== null || this.oneOf !== null || this.not !== undefined || this.if !== undefined || this.macros !== null;
524
591
 
525
- this.unevaluatedProperties = schema.unevaluatedProperties !== undefined ? child(schema.unevaluatedProperties) : undefined;
526
- this.unevaluatedItems = schema.unevaluatedItems !== undefined ? child(schema.unevaluatedItems) : undefined;
592
+ this.unevaluatedProperties = S.unevaluatedProperties !== undefined ? child(S.unevaluatedProperties) : undefined;
593
+ this.unevaluatedItems = S.unevaluatedItems !== undefined ? child(S.unevaluatedItems) : undefined;
527
594
  // A `false` here rejects the member outright, and the error belongs to this
528
595
  // keyword on this object, not to a boolean schema that happens to sit
529
596
  // under it. Recorded at compile time so the check below is one test.
530
- this.unevaluatedPropertiesFalse = schema.unevaluatedProperties === false;
531
- this.unevaluatedItemsFalse = schema.unevaluatedItems === false;
597
+ this.unevaluatedPropertiesFalse = S.unevaluatedProperties === false;
598
+ this.unevaluatedItemsFalse = S.unevaluatedItems === false;
532
599
  this.hasUnevaluated = this.unevaluatedProperties !== undefined || this.unevaluatedItems !== undefined;
533
600
 
534
601
  // Custom keywords present on this node, in schema order. Each op holds
@@ -847,7 +914,8 @@ class Interpreter {
847
914
  // 2020-12 requires bookending for `$dynamicRef`; v1 removes the
848
915
  // requirement. See the `$dynamicRef` branch in eval().
849
916
  this.bookending = !opts.v1;
850
- this.patternCache = new Map();
917
+ // Made at the first pattern.
918
+ this.patternCache = null;
851
919
  // Plans are built on first visit; a schema node is never re-read after.
852
920
  this.plans = new Map();
853
921
  this.rootNode = this.node(rootSchema);
@@ -919,6 +987,7 @@ class Interpreter {
919
987
  }
920
988
 
921
989
  pattern(src) {
990
+ if (this.patternCache === null) this.patternCache = new Map();
922
991
  let re = this.patternCache.get(src);
923
992
  if (!re) {
924
993
  if (/\\[pP]\{/.test(src)) {
@@ -1244,9 +1313,15 @@ class Interpreter {
1244
1313
  }
1245
1314
  }
1246
1315
 
1247
- // objects
1316
+ // objects. The object's own names, enumerable or not: a declared
1317
+ // property is read by name, as the generated code reads it, so one
1318
+ // hidden under its name is checked; patternProperties, propertyNames
1319
+ // and additionalProperties see the names for-in sees, as there too, and
1320
+ // minProperties and maxProperties count those.
1248
1321
  if (P.hasObject && bits === T_OBJECT) {
1249
- const keys = Object.keys(data);
1322
+ const keys = Object.getOwnPropertyNames(data);
1323
+ let hidden = 0;
1324
+ if (P.minProperties !== undefined || P.maxProperties !== undefined) for (let k = 0; k < keys.length; k++) if (!propEnum.call(data, keys[k])) hidden++;
1250
1325
  if (P.required !== null) {
1251
1326
  const req = P.required;
1252
1327
  for (let i = 0; i < req.length; i++) {
@@ -1254,8 +1329,8 @@ class Interpreter {
1254
1329
  if (!Object.hasOwn(data, key) || (P.requiredUndef !== null && P.requiredUndef[i] && data[key] === undefined)) { errors === NOERRORS || errors.push(err('required', 'required', instancePath, schemaPath + '/required', { missingProperty: key }, `must have required property '${key}'`)); valid = false; }
1255
1330
  }
1256
1331
  }
1257
- if (P.minProperties !== undefined && keys.length < P.minProperties) { errors === NOERRORS || errors.push(err('minProperties', 'minProperties', instancePath, schemaPath + '/minProperties', { limit: P.minProperties }, `must NOT have fewer than ${P.minProperties} properties`)); valid = false; }
1258
- if (P.maxProperties !== undefined && keys.length > P.maxProperties) { errors === NOERRORS || errors.push(err('maxProperties', 'maxProperties', instancePath, schemaPath + '/maxProperties', { limit: P.maxProperties }, `must NOT have more than ${P.maxProperties} properties`)); valid = false; }
1332
+ if (P.minProperties !== undefined && keys.length - hidden < P.minProperties) { errors === NOERRORS || errors.push(err('minProperties', 'minProperties', instancePath, schemaPath + '/minProperties', { limit: P.minProperties }, `must NOT have fewer than ${P.minProperties} properties`)); valid = false; }
1333
+ if (P.maxProperties !== undefined && keys.length - hidden > P.maxProperties) { errors === NOERRORS || errors.push(err('maxProperties', 'maxProperties', instancePath, schemaPath + '/maxProperties', { limit: P.maxProperties }, `must NOT have more than ${P.maxProperties} properties`)); valid = false; }
1259
1334
  if (P.dependentRequired !== null) {
1260
1335
  for (const [key, deps] of P.dependentRequired) {
1261
1336
  if (Object.hasOwn(data, key)) {
@@ -1267,6 +1342,7 @@ class Interpreter {
1267
1342
  }
1268
1343
  if (P.propertyNames !== undefined) {
1269
1344
  for (const key of keys) {
1345
+ if (!propEnum.call(data, key)) continue;
1270
1346
  if (!this.eval(P.propertyNames, key, base, dynScope, errors, instancePath, E ? schemaPath + '/propertyNames' : '', stack, DISCARD)) valid = false;
1271
1347
  }
1272
1348
  }
@@ -1276,6 +1352,7 @@ class Interpreter {
1276
1352
  const key = keys[k];
1277
1353
  let evaluated = false;
1278
1354
  const prop = props !== null ? props.get(key) : undefined;
1355
+ const seen = prop !== undefined && patterns === null && P.additionalProperties === undefined || propEnum.call(data, key);
1279
1356
  if (prop !== undefined) {
1280
1357
  // A declared property whose value is undefined is not checked, as the
1281
1358
  // generated code does not check it: no JSON document carries one, and
@@ -1287,7 +1364,7 @@ class Interpreter {
1287
1364
  } else if (P.requiredInProps !== null && P.requiredInProps.has(key)) { errors === NOERRORS || errors.push(err('required', 'required', instancePath, schemaPath + '/required', { missingProperty: key }, `must have required property '${key}'`)); valid = false; }
1288
1365
  evaluated = true;
1289
1366
  }
1290
- if (patterns !== null) {
1367
+ if (patterns !== null && seen) {
1291
1368
  for (let pi = 0; pi < patterns.length; pi++) {
1292
1369
  const pp = patterns[pi];
1293
1370
  if (pp.re.test(key)) {
@@ -1296,7 +1373,7 @@ class Interpreter {
1296
1373
  }
1297
1374
  }
1298
1375
  }
1299
- if (!evaluated && P.additionalProperties !== undefined) {
1376
+ if (!evaluated && seen && P.additionalProperties !== undefined) {
1300
1377
  // `additionalProperties: false` keeps its own keyword and names the
1301
1378
  // property it turned away. Running it as a plain false subschema
1302
1379
  // reported `not` at the key's path, which drops both.
@@ -1549,7 +1626,8 @@ function planCompiler() {
1549
1626
  let installCompat = null;
1550
1627
  let compatInstalled = null;
1551
1628
  function setCompat(install) {
1552
- installCompat = install;
1629
+ // esm.sh hands over a module of one function as { default: fn }.
1630
+ installCompat = typeof install === 'function' ? install : install && install.default;
1553
1631
  compatInstalled = null;
1554
1632
  }
1555
1633
  function compatExt() {
@@ -0,0 +1,131 @@
1
+ // Generated by scripts/gen-ir-emit-deps.js from lib/js-compiler.js and lib/suggestions.js. Do not edit.
2
+ const FIXED_LITERAL = /[A-Za-z0-9 _\-:@#%&=,;'"<>!~`\/]/
3
+ function compilePatternInline(pattern, varName) {
4
+ let m = pattern.match(/^\^(\[[\w\-]+\])\{(\d+)\}\$$/)
5
+ if (m) {
6
+ const len = parseInt(m[2])
7
+ if (len <= 16) {
8
+ const checks = []
9
+ for (let i = 0; i < len; i++) {
10
+ const ck = charClassToCheck(m[1], `${varName}.charCodeAt(${i})`)
11
+ if (!ck) return null
12
+ checks.push(ck)
13
+ }
14
+ return `${varName}.length===${len}&&${checks.join('&&')}`
15
+ }
16
+ const rangeCheck = charClassToCheck(m[1], `${varName}.charCodeAt(_pi)`)
17
+ if (!rangeCheck) return null
18
+ return `${varName}.length===${len}&&(()=>{for(let _pi=0;_pi<${len};_pi++){if(!(${rangeCheck}))return false}return true})()`
19
+ }
20
+ m = pattern.match(/^\^(\[[\w\-]+\])\+\$$/)
21
+ if (m) {
22
+ const rangeCheck = charClassToCheck(m[1], `${varName}.charCodeAt(_pi)`)
23
+ if (!rangeCheck) return null
24
+ return `${varName}.length>0&&(()=>{for(let _pi=0;_pi<${varName}.length;_pi++){if(!(${rangeCheck}))return false}return true})()`
25
+ }
26
+ m = pattern.match(/^\^(\[[\w\-]+\])\{(\d+),(\d+)\}\$$/)
27
+ if (m) {
28
+ const rangeCheck = charClassToCheck(m[1], `${varName}.charCodeAt(_pi)`)
29
+ if (!rangeCheck) return null
30
+ const min = parseInt(m[2]), max = parseInt(m[3])
31
+ return `${varName}.length>=${min}&&${varName}.length<=${max}&&(()=>{for(let _pi=0;_pi<${varName}.length;_pi++){if(!(${rangeCheck}))return false}return true})()`
32
+ }
33
+ return fixedSequenceInline(pattern, varName)
34
+ }
35
+ function fixedSequenceInline(pattern, varName) {
36
+ if (pattern.length < 3 || pattern[0] !== '^' || pattern[pattern.length - 1] !== '$' || pattern[pattern.length - 2] === '\\') return null
37
+ const end = pattern.length - 1
38
+ const pieces = []
39
+ let i = 1
40
+ while (i < end) {
41
+ let piece
42
+ const c = pattern[i]
43
+ if (c === '[') {
44
+ const j = pattern.indexOf(']', i)
45
+ if (j < 0 || j >= end) return null
46
+ const cls = pattern.slice(i, j + 1)
47
+ if (!/^\[[\w-]+\]$/.test(cls)) return null
48
+ piece = { cls }
49
+ i = j + 1
50
+ } else if (c === '\\') {
51
+ const e = pattern[i + 1]
52
+ if (e === 'd') piece = { cls: '[0-9]' }
53
+ else if (e !== undefined && './()[]{}+*?^$|\\'.includes(e)) piece = { lit: e }
54
+ else return null
55
+ i += 2
56
+ } else if (FIXED_LITERAL.test(c)) {
57
+ piece = { lit: c }
58
+ i += 1
59
+ } else {
60
+ return null
61
+ }
62
+ let count = 1
63
+ if (pattern[i] === '{') {
64
+ const q = /^\{(\d+)\}/.exec(pattern.slice(i))
65
+ if (!q) return null
66
+ count = Number(q[1])
67
+ if (!Number.isSafeInteger(count) || count > 32 - pieces.length) return null
68
+ i += q[0].length
69
+ } else if (i < end && '*+?'.includes(pattern[i])) {
70
+ return null
71
+ }
72
+ for (let k = 0; k < count; k++) pieces.push(piece)
73
+ if (pieces.length > 32) return null
74
+ }
75
+ if (pieces.length === 0) return null
76
+ const checks = []
77
+ for (let k = 0; k < pieces.length; k++) {
78
+ const at = `${varName}.charCodeAt(${k})`
79
+ if (pieces[k].lit !== undefined) checks.push(`${at}===${pieces[k].lit.charCodeAt(0)}`)
80
+ else {
81
+ const ck = charClassToCheck(pieces[k].cls, at)
82
+ if (!ck) return null
83
+ checks.push(`(${ck})`)
84
+ }
85
+ }
86
+ return `${varName}.length===${pieces.length}&&${checks.join('&&')}`
87
+ }
88
+ function charClassToCheck(charClass, codeExpr) {
89
+ const inner = charClass.slice(1, -1)
90
+ const ranges = []
91
+ let i = 0
92
+ while (i < inner.length) {
93
+ if (i + 2 < inner.length && inner[i + 1] === '-') {
94
+ ranges.push([inner.charCodeAt(i), inner.charCodeAt(i + 2)])
95
+ i += 3
96
+ } else {
97
+ ranges.push([inner.charCodeAt(i), inner.charCodeAt(i)])
98
+ i++
99
+ }
100
+ }
101
+ if (ranges.length === 0) return null
102
+ const checks = ranges.map(([lo, hi]) =>
103
+ lo === hi ? `${codeExpr}===${lo}` : `(${codeExpr}>=${lo}&&${codeExpr}<=${hi})`
104
+ )
105
+ return `(${checks.join('||')})`
106
+ }
107
+ const FORMAT_HINTS = {
108
+ email: (val) => {
109
+ if (typeof val !== 'string') return null;
110
+ if (!val.includes('@')) return "missing '@' and domain part";
111
+ if (val.split('@').length > 2) return "multiple '@' characters";
112
+ const [, dom] = val.split('@');
113
+ if (!dom || !dom.includes('.')) return 'domain part missing dot';
114
+ return null;
115
+ },
116
+ date: (val) => {
117
+ if (typeof val !== 'string') return null;
118
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(val);
119
+ if (!m) return 'expected YYYY-MM-DD layout';
120
+ const mo = +m[2]; if (mo < 1 || mo > 12) return 'month must be 01-12';
121
+ const d = +m[3]; if (d < 1 || d > 31) return 'day must be 01-31';
122
+ return null;
123
+ },
124
+ uuid: (val) => {
125
+ if (typeof val !== 'string') return null;
126
+ if (!/^[0-9a-fA-F-]+$/.test(val)) return 'expected hex digits and dashes';
127
+ return 'expected 8-4-4-4-12 hex layout';
128
+ },
129
+ ipv4: (val) => typeof val === 'string' ? 'expected four 0-255 octets separated by dots' : null,
130
+ };
131
+ export { compilePatternInline, FORMAT_HINTS };