ata-validator 1.16.2 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to ata-validator are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/), and this project adheres to semantic versioning.
4
4
 
5
+ ## 1.17.1 - 2026-09-13
6
+
7
+ ### Changed
8
+
9
+ - The rejection object behind a failing `validate()` is an instance of a class with prototype accessors rather than an object literal that defined a getter and a closure on every rejection. Same shape, same laziness, same caching; `JSON.stringify` still sees the errors.
10
+ - Declaration-order sorting of short error lists is a stable in-place insertion sort over the integer keys, with no comparator and no index array. Measured on the raw error list of the schema-benchmarks product schema, 15 errors: 1.49 to 1.10 µs; through the Standard Schema bridge 2.02 to 1.63 µs. Since 1.16.2 that row has gone from 3.30 to 1.63 µs on the same machine, and in the schema-benchmarks harness itself standard invalid went from 3.57 to 1.44 µs (60 percent less), every other row within noise.
11
+
12
+ ### Fixed
13
+
14
+ - `validateJSON()` reports errors in schema declaration order, as `validate()` does. The text path enriched errors in emission order, so the first error could differ between the two entry points for the same document.
15
+ - An instance built with options no longer answers a later `new Validator(sameSchema)` without options. The identity cache was seeded by every instance, so a validator created with `richErrors: false` or `coerceTypes` could be handed back to a caller that asked for the defaults.
16
+
17
+ ## 1.17.0 - 2026-09-13
18
+
19
+ ### Changed
20
+
21
+ - Errors are put in schema declaration order by an integer computed when the schema is compiled, not by walking each error's schemaPath at read time. The rank of a path is its pre-order position in the schema tree, which is exactly the order the old array-of-indexes comparison produced; the code generator now writes that position into each error literal (`_o`, an internal field), the reader compares integers, and a list that already arrives in order is returned without sorting or allocating. Errors without a position (an appended custom-keyword error, a path into another document) keep their place next to the error before them. The ordinal logic lives in `lib/schema-order.js`; `tests/test_error_order_ordinal.js` holds the ordinal to the array rank on a set of paths and holds the public error order unchanged.
22
+ - The Standard Schema bridge caches parsed issue paths per instancePath string (bounded, entries frozen), instead of splitting the string for every issue of every rejection.
23
+ - Measured on the schema-benchmarks harness locally, before and after on the same machine: standard invalid 3.57 to 2.83 µs (21 percent less); every other row within noise. On the raw error list of that schema, 15 errors: 1.88 to 1.50 µs; through the Standard Schema bridge 3.30 to 1.99 µs. Verdict paths are untouched.
24
+ - `richErrors: false` keeps the v0.14 key set: the ordering field is dropped from that shape.
25
+
5
26
  ## 1.16.2 - 2026-09-12
6
27
 
7
28
  ### Fixed
package/index.js CHANGED
@@ -423,83 +423,127 @@ function resolveSchemaByPath(rootSchema, schemaPath) {
423
423
  // A failing route sees the same handful of schemaPaths over and over, which is
424
424
  // what makes the first one worth having.
425
425
  const _rankCache = new WeakMap();
426
- const _keyIndexCache = new WeakMap();
427
-
428
- function keyIndex(node, seg) {
429
- let index = _keyIndexCache.get(node);
430
- if (index === undefined) {
431
- index = new Map();
432
- const keys = Object.keys(node);
433
- for (let i = 0; i < keys.length; i++) index.set(keys[i], i);
434
- _keyIndexCache.set(node, index);
426
+ const { rankFor: schemaOrderRank, ordinalFor: schemaOrdinal } = require('./lib/schema-order');
427
+
428
+ // The rejection the rich-errors wrapper returns. `errors` is built on first
429
+ // read and cached; `_ataRaw()` is the raw, schema-ordered list for consumers
430
+ // that carry only message and path, such as the Standard Schema bridge.
431
+ // Prototype accessors, not per-instance ones: see LazyRejection.
432
+ class RichRejection {
433
+ constructor(result, data, positions, self, root, enrich) {
434
+ this.valid = false;
435
+ this._result = result;
436
+ this._data = data;
437
+ this._positions = positions;
438
+ this._self = self;
439
+ this._root = root;
440
+ this._enrich = enrich;
441
+ this._cached = null;
442
+ }
443
+ toJSON() {
444
+ return { valid: false, errors: this.errors };
445
+ }
446
+ _ataRaw() {
447
+ let raw = this._result.errors || [];
448
+ if (raw.length > 1) raw = sortErrorsBySchemaOrder(this._root, raw);
449
+ return raw;
435
450
  }
436
- const at = index.get(seg);
437
- return at === undefined ? -1 : at;
438
- }
439
-
440
- // `~1` and `~0` are the only escapes a JSON pointer has, and almost no schema
441
- // key contains a tilde. Looking for one is far cheaper than two regex passes
442
- // over every segment of every path.
443
- function unescapePointerSegment(seg) {
444
- return seg.indexOf('~') < 0 ? seg : seg.replace(/~1/g, '/').replace(/~0/g, '~');
445
451
  }
452
+ Object.defineProperty(RichRejection.prototype, 'errors', {
453
+ enumerable: true,
454
+ configurable: true,
455
+ get() {
456
+ if (this._cached === null) {
457
+ const self = this._self;
458
+ const enrich = this._enrich;
459
+ let raw = this._result.errors || [];
460
+ if (raw.length > 1) raw = sortErrorsBySchemaOrder(this._root, raw);
461
+ const cached = (enrich && raw.length)
462
+ ? raw.map((e) => enrich(e, {
463
+ data: this._data,
464
+ positions: this._positions,
465
+ schemaPositions: self._schemaPositions,
466
+ schemaFile: self._source ? self._source.path : undefined,
467
+ }))
468
+ // The v0.14 shape is a fixed key set; the ordering key the
469
+ // generated code carries is dropped from it here.
470
+ : raw.map(stripOrdinal);
471
+ // Correlation is published, never applied. Both halves of a typo pair
472
+ // stay in the array; `related` only says they are one mistake, so a
473
+ // wrong pairing costs a sentence rather than a hidden violation.
474
+ if (enrich && cached.length > 1) attachRelated(cached);
475
+ // No diagnostic payload here. validate(data) is the library hot path,
476
+ // and attaching one cost about 100 ns per rejection for a consumer
477
+ // that never renders. The text path attaches it, and a renderer given
478
+ // `{ data }` builds frames for object input on request.
479
+ this._cached = cached;
480
+ }
481
+ return this._cached;
482
+ },
483
+ });
446
484
 
447
- function schemaOrderRank(rootSchema, schemaPath) {
448
- if (!schemaPath || typeof schemaPath !== 'string' || !schemaPath.startsWith('#')) return null;
449
- if (rootSchema === null || typeof rootSchema !== 'object') return null;
450
-
451
- let byPath = _rankCache.get(rootSchema);
452
- if (byPath === undefined) { byPath = new Map(); _rankCache.set(rootSchema, byPath); }
453
- const hit = byPath.get(schemaPath);
454
- if (hit !== undefined) return hit;
455
-
456
- const rank = _computeRank(rootSchema, schemaPath);
457
- byPath.set(schemaPath, rank);
458
- return rank;
485
+ // A raw error without the `_o` ordering key, for the legacy error shape.
486
+ function stripOrdinal(e) {
487
+ if (e === null || typeof e !== 'object' || e._o === undefined) return e;
488
+ const out = {};
489
+ for (const k in e) if (k !== '_o') out[k] = e[k];
490
+ return out;
459
491
  }
460
492
 
461
- function _computeRank(rootSchema, schemaPath) {
462
- const rank = [];
463
- let node = rootSchema;
464
- let start = 1;
465
- while (start <= schemaPath.length) {
466
- let end = schemaPath.indexOf('/', start);
467
- if (end < 0) end = schemaPath.length;
468
- if (end === start) { start = end + 1; continue; } // what filter(Boolean) dropped
469
- const seg = unescapePointerSegment(schemaPath.slice(start, end));
470
- start = end + 1;
471
-
472
- if (node == null || typeof node !== 'object') break;
473
- if (Array.isArray(node)) {
474
- const idx = Number(seg);
475
- if (!Number.isInteger(idx) || idx < 0 || idx >= node.length) break;
476
- rank.push(idx);
477
- node = node[idx];
478
- } else {
479
- const idx = keyIndex(node, seg);
480
- if (idx < 0) break;
481
- rank.push(idx);
482
- node = node[seg];
483
- }
493
+ // Errors in schema declaration order. Each error's key is its schemaPath's
494
+ // pre-order ordinal in the root schema: written into the literal by the code
495
+ // generator (`_o`), looked up once per path otherwise. Most rejections come
496
+ // out already ordered, and those return without sorting or allocating.
497
+ function sortErrorsBySchemaOrder(rootSchema, errors) {
498
+ const n = errors.length;
499
+ const keys = new Array(n);
500
+ let sorted = true;
501
+ let prev = -1;
502
+ for (let i = 0; i < n; i++) {
503
+ const e = errors[i];
504
+ let o = typeof e._o === 'number' ? e._o : schemaOrdinal(rootSchema, e.schemaPath);
505
+ // An error with no place in this document (an appended custom-keyword
506
+ // error, a path into another schema) stays next to the error before it,
507
+ // which is where the rank comparison left it too.
508
+ if (o === null) o = prev < 0 ? 0 : prev;
509
+ keys[i] = o;
510
+ if (o < prev) sorted = false;
511
+ prev = o;
484
512
  }
485
- return rank;
513
+ if (sorted) return errors;
514
+ // Error lists are short. A stable insertion sort over the integer keys
515
+ // moves the errors in tandem with no comparator calls and no index array.
516
+ const out = errors.slice();
517
+ for (let i = 1; i < n; i++) {
518
+ const k = keys[i];
519
+ const e = out[i];
520
+ let j = i - 1;
521
+ while (j >= 0 && keys[j] > k) { keys[j + 1] = keys[j]; out[j + 1] = out[j]; j--; }
522
+ keys[j + 1] = k;
523
+ out[j + 1] = e;
524
+ }
525
+ return out;
486
526
  }
487
527
 
488
- function sortErrorsBySchemaOrder(rootSchema, errors) {
489
- const ranked = errors.map((e, i) => ({ e, i, rank: schemaOrderRank(rootSchema, e.schemaPath) }));
490
- ranked.sort((a, b) => {
491
- if (!a.rank || !b.rank) return a.i - b.i;
492
- const n = Math.min(a.rank.length, b.rank.length);
493
- for (let k = 0; k < n; k++) {
494
- if (a.rank[k] !== b.rank[k]) return a.rank[k] - b.rank[k];
495
- }
496
- return a.i - b.i;
497
- });
498
- return ranked.map((r) => r.e);
499
- }
500
528
 
529
+ // Paths repeat across rejections of the same shape, so the parsed segment
530
+ // list is cached per path string. Entries are frozen: the same array is
531
+ // handed to every issue that names the path. The cache is bounded so a
532
+ // stream of array indexes cannot grow it without limit.
533
+ const _pathCache = new Map();
534
+ const PATH_CACHE_MAX = 4096;
501
535
  function parsePointerPath(path) {
502
- if (!path) return [];
536
+ if (!path) return EMPTY_PATH;
537
+ const hit = _pathCache.get(path);
538
+ if (hit !== undefined) return hit;
539
+ const segs = Object.freeze(parsePointerPathUncached(path));
540
+ if (_pathCache.size >= PATH_CACHE_MAX) _pathCache.clear();
541
+ _pathCache.set(path, segs);
542
+ return segs;
543
+ }
544
+ const EMPTY_PATH = Object.freeze([]);
545
+
546
+ function parsePointerPathUncached(path) {
503
547
  // One pass, no intermediate arrays. Per Standard Schema V1 an array index
504
548
  // is emitted as a number and an object key as a string; a segment is an
505
549
  // index when it is all digits with no leading zero.
@@ -804,6 +848,7 @@ class Validator {
804
848
  this._schemaStr = null; // lazy: computed on first use
805
849
  this._schemaObj = schemaObj;
806
850
  this._options = options;
851
+ this._noOpts = !opts;
807
852
  this._initialized = false;
808
853
  this._nativeReady = false;
809
854
  this._compiled = null;
@@ -1441,43 +1486,11 @@ class Validator {
1441
1486
  // resolved eagerly since the cache is reset per call.
1442
1487
  const positions = (enrich && self._lastRawInput != null) ? self._pos().get(self._lastRawInput) : null;
1443
1488
  if (positions) self._posCache.reset();
1444
- let cached = null;
1445
- return {
1446
- valid: false,
1447
- // Raw shape for consumers that carry only message and path, such
1448
- // as the Standard Schema bridge: schema order, no enrichment.
1449
- // Reading `errors` afterwards still enriches from the same build.
1450
- _ataRaw() {
1451
- let raw = result.errors || [];
1452
- if (raw.length > 1) raw = sortErrorsBySchemaOrder(root, raw);
1453
- return raw;
1454
- },
1455
- get errors() {
1456
- if (cached === null) {
1457
- let raw = result.errors || [];
1458
- if (raw.length > 1) raw = sortErrorsBySchemaOrder(root, raw);
1459
- cached = (enrich && raw.length)
1460
- ? raw.map((e) => enrich(e, {
1461
- data,
1462
- positions,
1463
- schemaPositions: self._schemaPositions,
1464
- schemaFile: self._source ? self._source.path : undefined,
1465
- }))
1466
- : raw;
1467
- // Correlation is published, never applied. Both halves of a
1468
- // typo pair stay in the array; `related` only says they are
1469
- // one mistake, so a wrong pairing costs a sentence rather
1470
- // than a hidden violation.
1471
- if (enrich && cached.length > 1) attachRelated(cached);
1472
- // No diagnostic payload here. validate(data) is the library
1473
- // hot path, and attaching one cost about 100 ns per rejection
1474
- // for a consumer that never renders. The text path attaches
1475
- // it below, and a renderer given `{ data }` builds frames for
1476
- // object input on request.
1477
- }
1478
- return cached;
1479
- },
1480
- };
1489
+ // One instance of a class with prototype accessors. An object
1490
+ // literal with a getter here cost a closure plus an accessor
1491
+ // definition on every rejection, several hundred nanoseconds
1492
+ // before any error was read.
1493
+ return new RichRejection(result, data, positions, self, root, enrich);
1481
1494
  }
1482
1495
  return result;
1483
1496
  };
@@ -1513,7 +1526,10 @@ class Validator {
1513
1526
  try { parsedData = JSON.parse(jsonStr); } catch { parsedData = undefined; }
1514
1527
  if (!first || !first.docUrl) {
1515
1528
  const positions = (this._lastRawInput != null) ? this._pos().get(this._lastRawInput) : null;
1516
- const enriched = result.errors.map((e) => enrich(e, {
1529
+ // Declaration order, as validate() applies it; the text path
1530
+ // used to enrich in emission order.
1531
+ const ordered = result.errors.length > 1 ? sortErrorsBySchemaOrder(this._schemaObj, result.errors) : result.errors;
1532
+ const enriched = ordered.map((e) => enrich(e, {
1517
1533
  data: parsedData,
1518
1534
  positions,
1519
1535
  schemaPositions: this._schemaPositions,
@@ -1652,8 +1668,12 @@ class Validator {
1652
1668
  if (bufferNeedsSlowPath(schemaObj, this._schemaMap, this._keywords)) installSlowBufferApis(this);
1653
1669
  }
1654
1670
 
1655
- // Save to identity cache for ultra-fast reuse with same schema object
1656
- if (this._schemaObj && typeof this._schemaObj === 'object') {
1671
+ // Save to identity cache for ultra-fast reuse with same schema object.
1672
+ // Only an instance built without options may answer a later
1673
+ // `new Validator(schema)`: one built with options (richErrors: false,
1674
+ // coerceTypes, formats, ...) would hand its options to a caller that
1675
+ // asked for none.
1676
+ if (this._noOpts && this._schemaObj && typeof this._schemaObj === 'object') {
1657
1677
  _identityCache.set(this._schemaObj, this);
1658
1678
  }
1659
1679
  }
@@ -73,6 +73,7 @@ function enumCondition(ctx, vals, v) {
73
73
 
74
74
 
75
75
  const { codeFor } = require('./error-codes')
76
+ const { ordinalFor } = require('./schema-order')
76
77
  const { compileSafe, patternIsSafe } = require('./safe-regex')
77
78
 
78
79
  // Closure value for a patternProperties/propertyNames regex: the linear-time
@@ -94,6 +95,14 @@ const DOC_BASE = 'https://ata-validator.com/e/'
94
95
  // like ",schemaSource:Object.freeze({...})" or "" if no source map hit.
95
96
  // The fragment always starts with a leading comma when non-empty so callers
96
97
  // can splice it inside an existing object literal.
98
+ // The error's position in schema declaration order, as a literal field the
99
+ // reader sorts by without deriving it from the path. Empty when the path is
100
+ // not into the root document.
101
+ function ordinalField (ctx, schemaPath) {
102
+ const o = ctx && ctx.rootSchema ? ordinalFor(ctx.rootSchema, schemaPath) : null
103
+ return o === null ? '' : `,_o:${o}`
104
+ }
105
+
97
106
  function buildErrorLiteral (opts) {
98
107
  const { keyword, format, schemaPath, sourceMap } = opts
99
108
  let code = (keyword === 'format' && format) ? codeFor('format', format) : codeFor(keyword)
@@ -3213,7 +3222,7 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
3213
3222
  // AOT output small for schemas that don't compose. Standalone modules can't
3214
3223
  // require('./branch-collapse'), so the helper body is embedded verbatim.
3215
3224
  if (ctx.usesBranchCollapse) {
3216
- ctx.helperCode.push("const __ATA_SEVERITY={type:10,const:8,enum:8,required:5,format:3,minLength:3,maxLength:3,minimum:3,maximum:3,pattern:3,additionalProperties:2,unevaluatedProperties:2,unevaluatedItems:2};function __ataScore(errs){if(!errs||!errs.length)return 0;let s=0;for(const e of errs)s+=__ATA_SEVERITY[e.keyword]||4;return errs.length*100+s}function __ataCollapse(kw,br,pp,sp){const pass=br.filter(b=>b.valid);if(kw==='oneOf'){if(pass.length===1)return null;if(pass.length>1)return{code:'ATA4002',keyword:'oneOf',instancePath:pp||'',path:pp||'',schemaPath:sp,message:'value matched '+pass.length+' of '+br.length+' oneOf variants, expected exactly one',params:{matched:pass.length,total:br.length}}}else{if(pass.length>=1)return null}let bi=0,bs=Infinity;for(let i=0;i<br.length;i++){const s=__ataScore(br[i].errors);if(s<bs){bs=s;bi=i}}const code=kw==='oneOf'?'ATA4001':'ATA4003';const best=br[bi];return{code,keyword:kw,instancePath:pp||'',path:pp||'',schemaPath:sp,message:'value matched 0 of '+br.length+' '+kw+' variants',params:{variants:br.length,closest:bi,closestName:best.title||('variant '+(bi+1))},branchErrors:best.errors}}")
3225
+ ctx.helperCode.push("const __ATA_SEVERITY={type:10,const:8,enum:8,required:5,format:3,minLength:3,maxLength:3,minimum:3,maximum:3,pattern:3,additionalProperties:2,unevaluatedProperties:2,unevaluatedItems:2};function __ataScore(errs){if(!errs||!errs.length)return 0;let s=0;for(const e of errs)s+=__ATA_SEVERITY[e.keyword]||4;return errs.length*100+s}function __ataCollapse(kw,br,pp,sp,o){const pass=br.filter(b=>b.valid);if(kw==='oneOf'){if(pass.length===1)return null;if(pass.length>1)return{code:'ATA4002',keyword:'oneOf',instancePath:pp||'',path:pp||'',schemaPath:sp,_o:o,message:'value matched '+pass.length+' of '+br.length+' oneOf variants, expected exactly one',params:{matched:pass.length,total:br.length}}}else{if(pass.length>=1)return null}let bi=0,bs=Infinity;for(let i=0;i<br.length;i++){const s=__ataScore(br[i].errors);if(s<bs){bs=s;bi=i}}const code=kw==='oneOf'?'ATA4001':'ATA4003';const best=br[bi];return{code,keyword:kw,instancePath:pp||'',path:pp||'',schemaPath:sp,_o:o,message:'value matched 0 of '+br.length+' '+kw+' variants',params:{variants:br.length,closest:bi,closestName:best.title||('variant '+(bi+1))},branchErrors:best.errors}}")
3217
3226
  }
3218
3227
  if (lines.length === 0) return (d) => ({ valid: true, errors: [] })
3219
3228
 
@@ -3299,7 +3308,7 @@ function emitBranchCollapse (branches, keyword, v, pathExpr, lines, ctx, schemaP
3299
3308
  } else if (sub === false) {
3300
3309
  // A false branch never matches; it reports the same error the
3301
3310
  // interpreter attaches to a false schema.
3302
- subLines.push(`_e.push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${subSp}',params:{},message:'boolean schema is false'})`)
3311
+ subLines.push(`_e.push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${subSp}'${ordinalField(ctx, `${subSp}`)},params:{},message:'boolean schema is false'})`)
3303
3312
  }
3304
3313
  const title = (sub && typeof sub === 'object' && typeof sub.title === 'string') ? sub.title : ''
3305
3314
  titles.push(title)
@@ -3313,7 +3322,7 @@ function emitBranchCollapse (branches, keyword, v, pathExpr, lines, ctx, schemaP
3313
3322
  const titleArr = JSON.stringify(titles)
3314
3323
  const collapsed = `_brc${fi}`
3315
3324
  const pp = pathExpr || '""'
3316
- lines.push(`{const ${fnArr}=[${fns.join(',')}];const _bt${fi}=${titleArr};const ${resArr}=[];for(let _bi=0;_bi<${fnArr}.length;_bi++){const _br=${fnArr}[_bi](${v});${resArr}.push({valid:_br.valid,errors:_br.errors,title:_bt${fi}[_bi]})}const ${collapsed}=__ataCollapse('${branchKw}',${resArr},${pp},'${branchSp}');if(${collapsed}){_e.push(${collapsed});if(!_all)return{valid:false,errors:_e}}}`)
3325
+ lines.push(`{const ${fnArr}=[${fns.join(',')}];const _bt${fi}=${titleArr};const ${resArr}=[];for(let _bi=0;_bi<${fnArr}.length;_bi++){const _br=${fnArr}[_bi](${v});${resArr}.push({valid:_br.valid,errors:_br.errors,title:_bt${fi}[_bi]})}const ${collapsed}=__ataCollapse('${branchKw}',${resArr},${pp},'${branchSp}',${(() => { const o = ordinalFor(ctx.rootSchema, branchSp); return o === null ? 'null' : o })()});if(${collapsed}){_e.push(${collapsed});if(!_all)return{valid:false,errors:_e}}}`)
3317
3326
  }
3318
3327
 
3319
3328
  // Error-collecting code generator.
@@ -3327,7 +3336,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3327
3336
  // name this keyword `false schema`; changing it here would change output
3328
3337
  // the interpreter path already produces, so both engines say `not`.
3329
3338
  if (schema === false) {
3330
- lines.push(`;_e.push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}',params:{},message:'boolean schema is false'});if(!_all)return{valid:false,errors:_e}`)
3339
+ lines.push(`;_e.push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}'${ordinalField(ctx, `${schemaPrefix}`)},params:{},message:'boolean schema is false'});if(!_all)return{valid:false,errors:_e}`)
3331
3340
  return
3332
3341
  }
3333
3342
  if (schema === true) return
@@ -3442,7 +3451,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3442
3451
  {
3443
3452
  const typeSp = `${schemaPrefix}/type`
3444
3453
  const lit = buildErrorLiteral({ keyword: 'type', schemaPath: typeSp, sourceMap: ctx.sourceMap })
3445
- lines.push(`if(!(${conds.join('||')})){_e.push({code:'${lit.codeStr}',keyword:'type',instancePath:${pathExpr||'""'},schemaPath:'${typeSp}',params:{type:'${expected}'},message:'must be ${expected}',docUrl:'${lit.docUrl}'${lit.frame}});if(!_all)return{valid:false,errors:_e}}`)
3454
+ lines.push(`if(!(${conds.join('||')})){_e.push({code:'${lit.codeStr}',keyword:'type',instancePath:${pathExpr||'""'},schemaPath:'${typeSp}'${ordinalField(ctx, `${typeSp}`)},params:{type:'${expected}'},message:'must be ${expected}',docUrl:'${lit.docUrl}'${lit.frame}});if(!_all)return{valid:false,errors:_e}}`)
3446
3455
  }
3447
3456
  }
3448
3457
 
@@ -3455,7 +3464,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3455
3464
  const fail = (keyword, schemaSuffix, paramsCode, msgCode, fmt) => {
3456
3465
  const sp = schemaPrefix + '/' + schemaSuffix
3457
3466
  const lit = buildErrorLiteral({ keyword, format: fmt, schemaPath: sp, sourceMap: ctx.sourceMap })
3458
- return `_e.push({code:'${lit.codeStr}',keyword:'${keyword}',instancePath:${pathExpr||'""'},schemaPath:'${sp}',params:${paramsCode},message:${msgCode},docUrl:'${lit.docUrl}'${lit.frame}});if(!_all)return{valid:false,errors:_e}`
3467
+ return `_e.push({code:'${lit.codeStr}',keyword:'${keyword}',instancePath:${pathExpr||'""'},schemaPath:'${sp}',params:${paramsCode},message:${msgCode},docUrl:'${lit.docUrl}'${lit.frame}${ordinalField(ctx, sp)}});if(!_all)return{valid:false,errors:_e}`
3459
3468
  }
3460
3469
 
3461
3470
  // enum
@@ -3480,7 +3489,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3480
3489
  const reqSp = `${schemaPrefix}/required`
3481
3490
  const reqLit = buildErrorLiteral({ keyword: 'required', schemaPath: reqSp, sourceMap: ctx.sourceMap })
3482
3491
  for (const key of schema.required) {
3483
- lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&!(${JSON.stringify(key)} in ${v})){_e.push({code:'${reqLit.codeStr}',keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${reqSp}',params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'",docUrl:'${reqLit.docUrl}'${reqLit.frame}});if(!_all)return{valid:false,errors:_e}}`)
3492
+ lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&!(${JSON.stringify(key)} in ${v})){_e.push({code:'${reqLit.codeStr}',keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${reqSp}'${ordinalField(ctx, `${reqSp}`)},params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'",docUrl:'${reqLit.docUrl}'${reqLit.frame}});if(!_all)return{valid:false,errors:_e}}`)
3484
3493
  }
3485
3494
  }
3486
3495
 
@@ -3550,7 +3559,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3550
3559
  const fc = FORMAT_CODEGEN[schema.format]
3551
3560
  const fmtSp = `${schemaPrefix}/format`
3552
3561
  const fmtLit = buildErrorLiteral({ keyword: 'format', format: schema.format, schemaPath: fmtSp, sourceMap: ctx.sourceMap })
3553
- const failPush = `_e.push({code:'${fmtLit.codeStr}',keyword:'format',instancePath:${pathExpr||'""'},schemaPath:'${fmtSp}',params:{format:'${esc(schema.format)}'},message:'must match format "${esc(schema.format)}"',docUrl:'${fmtLit.docUrl}'${fmtLit.frame}});if(!_all)return{valid:false,errors:_e}`
3562
+ const failPush = `_e.push({code:'${fmtLit.codeStr}',keyword:'format',instancePath:${pathExpr||'""'},schemaPath:'${fmtSp}'${ordinalField(ctx, `${fmtSp}`)},params:{format:'${esc(schema.format)}'},message:'must match format "${esc(schema.format)}"',docUrl:'${fmtLit.docUrl}'${fmtLit.frame}});if(!_all)return{valid:false,errors:_e}`
3554
3563
  // Format errors use the boolean codegen, each `return false` becoming an
3555
3564
  // error push. One violation reports once: the first failing statement
3556
3565
  // pushes and leaves the labeled block, where it used to fall through and
@@ -3634,7 +3643,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3634
3643
  patChecks.push(`_re${ctx.regExpMap.get(pattern)}.test(_k${ci}[_i])`)
3635
3644
  }
3636
3645
  const isAdditional = patChecks.length ? `!_a${ci}.has(_k${ci}[_i])&&!(${patChecks.join('||')})` : `!_a${ci}.has(_k${ci}[_i])`
3637
- const inner = `const _k${ci}=Object.keys(${v});const _a${ci}=new Set([${allowed}]);for(let _i=0;_i<_k${ci}.length;_i++){if(${isAdditional}){_e.push({code:'${apLit.codeStr}',keyword:'additionalProperties',instancePath:${pathExpr||'""'},schemaPath:'${apSp}',params:{additionalProperty:_k${ci}[_i]},message:'must NOT have additional properties',docUrl:'${apLit.docUrl}'${apLit.frame}});if(!_all)return{valid:false,errors:_e}}}`
3646
+ const inner = `const _k${ci}=Object.keys(${v});const _a${ci}=new Set([${allowed}]);for(let _i=0;_i<_k${ci}.length;_i++){if(${isAdditional}){_e.push({code:'${apLit.codeStr}',keyword:'additionalProperties',instancePath:${pathExpr||'""'},schemaPath:'${apSp}'${ordinalField(ctx, `${apSp}`)},params:{additionalProperty:_k${ci}[_i]},message:'must NOT have additional properties',docUrl:'${apLit.docUrl}'${apLit.frame}});if(!_all)return{valid:false,errors:_e}}}`
3638
3647
  lines.push(isObj ? `{${inner}}` : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){${inner}}`)
3639
3648
  }
3640
3649
 
@@ -3644,7 +3653,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3644
3653
  const drLit = buildErrorLiteral({ keyword: 'dependentRequired', schemaPath: drSp, sourceMap: ctx.sourceMap })
3645
3654
  for (const [key, deps] of Object.entries(schema.dependentRequired)) {
3646
3655
  for (const dep of deps) {
3647
- lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${JSON.stringify(key)} in ${v}&&!(${JSON.stringify(dep)} in ${v})){_e.push({code:'${drLit.codeStr}',keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${drSp}',params:{missingProperty:'${esc(dep)}'},message:"must have required property '${esc(dep)}'",docUrl:'${drLit.docUrl}'${drLit.frame}});if(!_all)return{valid:false,errors:_e}}`)
3656
+ lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${JSON.stringify(key)} in ${v}&&!(${JSON.stringify(dep)} in ${v})){_e.push({code:'${drLit.codeStr}',keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${drSp}'${ordinalField(ctx, `${drSp}`)},params:{missingProperty:'${esc(dep)}'},message:"must have required property '${esc(dep)}'",docUrl:'${drLit.docUrl}'${drLit.frame}});if(!_all)return{valid:false,errors:_e}}`)
3648
3657
  }
3649
3658
  }
3650
3659
  }
@@ -3731,7 +3740,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3731
3740
  if (schema.propertyNames === false) {
3732
3741
  const ki = ctx.varCounter++
3733
3742
  const p = pathExpr ? `${pathExpr}+'/'+_k${ki}` : `'/'+_k${ki}`
3734
- lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k${ki} in ${v}){_e.push({keyword:'not',instancePath:${p},schemaPath:'${schemaPrefix}/propertyNames',params:{},message:'boolean schema is false'});if(!_all)return{valid:false,errors:_e}}}`)
3743
+ lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k${ki} in ${v}){_e.push({keyword:'not',instancePath:${p},schemaPath:'${schemaPrefix}/propertyNames'${ordinalField(ctx, `${schemaPrefix}/propertyNames`)},params:{},message:'boolean schema is false'});if(!_all)return{valid:false,errors:_e}}}`)
3735
3744
  }
3736
3745
 
3737
3746
  // propertyNames
@@ -3973,7 +3982,7 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3973
3982
  if (schema === false) {
3974
3983
  // Leading semicolon: the previous generated line may end in an
3975
3984
  // expression, and a line starting with `(` would be parsed as a call.
3976
- lines.push(`;(_e||(_e=[])).push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}',params:{},message:'boolean schema is false'})`)
3985
+ lines.push(`;(_e||(_e=[])).push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}'${ordinalField(ctx, `${schemaPrefix}`)},params:{},message:'boolean schema is false'})`)
3977
3986
  return
3978
3987
  }
3979
3988
  if (schema === true) return
@@ -4061,12 +4070,15 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
4061
4070
  const pathVal = pathExpr ? pathExpr.slice(1, -1) : ''
4062
4071
  const msgVal = msgCode.slice(1, -1)
4063
4072
  ctx.closureVars.push(errVar)
4064
- ctx.closureVals.push(Object.freeze({keyword, instancePath: pathVal, schemaPath: sp, params: Object.freeze(paramsVal), message: msgVal}))
4073
+ const o = ordinalFor(ctx.rootSchema, sp)
4074
+ const lit = {keyword, instancePath: pathVal, schemaPath: sp, params: Object.freeze(paramsVal), message: msgVal}
4075
+ if (o !== null) lit._o = o
4076
+ ctx.closureVals.push(Object.freeze(lit))
4065
4077
  return `(_e||(_e=[])).push(${errVar})`
4066
4078
  }
4067
4079
  }
4068
4080
  // Dynamic path (e.g., array index): inline as before
4069
- return `(_e||(_e=[])).push({keyword:'${keyword}',instancePath:${pathExpr||'""'},schemaPath:'${sp}',params:${paramsCode},message:${msgCode}})`
4081
+ return `(_e||(_e=[])).push({keyword:'${keyword}',instancePath:${pathExpr||'""'},schemaPath:'${sp}',params:${paramsCode},message:${msgCode}${ordinalField(ctx, sp)}})`
4070
4082
  }
4071
4083
 
4072
4084
  if (types) {
@@ -4135,7 +4147,7 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
4135
4147
  ctx.closureVals.push(Object.freeze({keyword: 'required', instancePath: pathVal, schemaPath: `${schemaPrefix}/required`, params: Object.freeze({missingProperty: key}), message: `must have required property '${key}'`}))
4136
4148
  lines.push(`if(${check}){(_e||(_e=[])).push(${errVar})}`)
4137
4149
  } else {
4138
- lines.push(`if(${check}){(_e||(_e=[])).push({keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/required',params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'"})}`)
4150
+ lines.push(`if(${check}){(_e||(_e=[])).push({keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/required'${ordinalField(ctx, `${schemaPrefix}/required`)},params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'"})}`)
4139
4151
  }
4140
4152
  }
4141
4153
  } else if (schema.required) {
@@ -4149,7 +4161,7 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
4149
4161
  ctx.closureVals.push(Object.freeze({keyword: 'required', instancePath: pathVal, schemaPath: `${schemaPrefix}/required`, params: Object.freeze({missingProperty: key}), message: `must have required property '${key}'`}))
4150
4162
  lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&!(${JSON.stringify(key)} in ${v})){(_e||(_e=[])).push(${errVar})}`)
4151
4163
  } else {
4152
- lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&!(${JSON.stringify(key)} in ${v})){(_e||(_e=[])).push({keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/required',params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'"})}`)
4164
+ lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&!(${JSON.stringify(key)} in ${v})){(_e||(_e=[])).push({keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/required'${ordinalField(ctx, `${schemaPrefix}/required`)},params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'"})}`)
4153
4165
  }
4154
4166
  }
4155
4167
  }
@@ -4271,7 +4283,7 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
4271
4283
  ctx.closureVals.push(Object.freeze({keyword: 'required', instancePath: pathVal, schemaPath: `${schemaPrefix}/dependentRequired`, params: Object.freeze({missingProperty: dep}), message: `must have required property '${dep}'`}))
4272
4284
  lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${JSON.stringify(key)} in ${v}&&!(${JSON.stringify(dep)} in ${v})){(_e||(_e=[])).push(${errVar})}`)
4273
4285
  } else {
4274
- lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${JSON.stringify(key)} in ${v}&&!(${JSON.stringify(dep)} in ${v})){(_e||(_e=[])).push({keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/dependentRequired',params:{missingProperty:'${esc(dep)}'},message:"must have required property '${esc(dep)}'"})}`)
4286
+ lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${JSON.stringify(key)} in ${v}&&!(${JSON.stringify(dep)} in ${v})){(_e||(_e=[])).push({keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/dependentRequired'${ordinalField(ctx, `${schemaPrefix}/dependentRequired`)},params:{missingProperty:'${esc(dep)}'},message:"must have required property '${esc(dep)}'"})}`)
4275
4287
  }
4276
4288
  }
4277
4289
  }
@@ -4417,7 +4429,7 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
4417
4429
  if (schema.propertyNames === false) {
4418
4430
  const ki = ctx.varCounter++
4419
4431
  const p = pathExpr ? `${pathExpr}+'/'+_k${ki}` : `'/'+_k${ki}`
4420
- lines.push(`;if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k${ki} in ${v}){(_e||(_e=[])).push({keyword:'not',instancePath:${p},schemaPath:'${schemaPrefix}/propertyNames',params:{},message:'boolean schema is false'})}}`)
4432
+ lines.push(`;if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k${ki} in ${v}){(_e||(_e=[])).push({keyword:'not',instancePath:${p},schemaPath:'${schemaPrefix}/propertyNames'${ordinalField(ctx, `${schemaPrefix}/propertyNames`)},params:{},message:'boolean schema is false'})}}`)
4421
4433
  }
4422
4434
 
4423
4435
  // propertyNames — skip if already merged into patternProperties loop
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ // Declaration order of a schemaPath within its root schema.
4
+ //
5
+ // `rankFor` is the path of child indexes from the root to the node the
6
+ // schemaPath names (or to the deepest node it can reach), which sorts errors
7
+ // in the order their keywords appear in the schema. `ordinalFor` is the same
8
+ // order as one integer: the node's pre-order position in the schema tree.
9
+ // Comparing two ranks segment by segment gives exactly the pre-order
10
+ // relation, so the integer can stand in for the array, and the code
11
+ // generator can compute it once per error site instead of the reader
12
+ // deriving it from the path string on every rejection.
13
+ //
14
+ // Both are memoized per root schema; a validator's root never changes.
15
+
16
+ const _keyIndexCache = new WeakMap();
17
+ const _rankCache = new WeakMap();
18
+ const _ordinalCache = new WeakMap();
19
+
20
+ function keyIndex(node, seg) {
21
+ let index = _keyIndexCache.get(node);
22
+ if (index === undefined) {
23
+ index = new Map();
24
+ const keys = Object.keys(node);
25
+ for (let i = 0; i < keys.length; i++) index.set(keys[i], i);
26
+ _keyIndexCache.set(node, index);
27
+ }
28
+ const at = index.get(seg);
29
+ return at === undefined ? -1 : at;
30
+ }
31
+
32
+ // `~1` and `~0` are the only escapes a JSON pointer has, and almost no schema
33
+ // key contains a tilde. Looking for one is far cheaper than two regex passes
34
+ // over every segment of every path.
35
+ function unescapePointerSegment(seg) {
36
+ return seg.indexOf('~') < 0 ? seg : seg.replace(/~1/g, '/').replace(/~0/g, '~');
37
+ }
38
+
39
+ function escapePointerSegment(seg) {
40
+ return seg.indexOf('~') < 0 && seg.indexOf('/') < 0 ? seg : seg.replace(/~/g, '~0').replace(/\//g, '~1');
41
+ }
42
+
43
+ function rankFor(rootSchema, schemaPath) {
44
+ if (!schemaPath || typeof schemaPath !== 'string' || !schemaPath.startsWith('#')) return null;
45
+ if (rootSchema === null || typeof rootSchema !== 'object') return null;
46
+ let byPath = _rankCache.get(rootSchema);
47
+ if (byPath === undefined) { byPath = new Map(); _rankCache.set(rootSchema, byPath); }
48
+ const hit = byPath.get(schemaPath);
49
+ if (hit !== undefined) return hit;
50
+ const rank = computeRank(rootSchema, schemaPath);
51
+ byPath.set(schemaPath, rank);
52
+ return rank;
53
+ }
54
+
55
+ function computeRank(rootSchema, schemaPath) {
56
+ const rank = [];
57
+ let node = rootSchema;
58
+ let start = 1;
59
+ while (start <= schemaPath.length) {
60
+ let end = schemaPath.indexOf('/', start);
61
+ if (end < 0) end = schemaPath.length;
62
+ if (end === start) { start = end + 1; continue; }
63
+ const seg = unescapePointerSegment(schemaPath.slice(start, end));
64
+ start = end + 1;
65
+ if (node == null || typeof node !== 'object') break;
66
+ if (Array.isArray(node)) {
67
+ const idx = Number(seg);
68
+ if (!Number.isInteger(idx) || idx < 0 || idx >= node.length) break;
69
+ rank.push(idx);
70
+ node = node[idx];
71
+ } else {
72
+ const idx = keyIndex(node, seg);
73
+ if (idx < 0) break;
74
+ rank.push(idx);
75
+ node = node[seg];
76
+ }
77
+ }
78
+ return rank;
79
+ }
80
+
81
+ // Pre-order numbering of every node in the schema, keyed by its pointer as
82
+ // it appears in a schemaPath (segments escaped). Built once per root, on the
83
+ // first ask. Cyclic structures are guarded; a schema object can contain
84
+ // itself only through references, which are strings.
85
+ function ordinals(rootSchema) {
86
+ let map = _ordinalCache.get(rootSchema);
87
+ if (map !== undefined) return map;
88
+ map = new Map();
89
+ const seen = new Set();
90
+ let next = 0;
91
+ const walk = (node, pointer) => {
92
+ map.set(pointer, next++);
93
+ if (node === null || typeof node !== 'object') return;
94
+ if (seen.has(node)) return;
95
+ seen.add(node);
96
+ if (Array.isArray(node)) {
97
+ for (let i = 0; i < node.length; i++) walk(node[i], pointer + '/' + i);
98
+ } else {
99
+ for (const key of Object.keys(node)) walk(node[key], pointer + '/' + escapePointerSegment(key));
100
+ }
101
+ };
102
+ walk(rootSchema, '#');
103
+ _ordinalCache.set(rootSchema, map);
104
+ return map;
105
+ }
106
+
107
+ // The ordinal of the deepest node the schemaPath reaches; null when the path
108
+ // is not into this document.
109
+ function ordinalFor(rootSchema, schemaPath) {
110
+ if (!schemaPath || typeof schemaPath !== 'string' || schemaPath.charCodeAt(0) !== 35) return null;
111
+ if (rootSchema === null || typeof rootSchema !== 'object') return null;
112
+ const map = ordinals(rootSchema);
113
+ const hit = map.get(schemaPath);
114
+ if (hit !== undefined) return hit;
115
+ // Walk back to the longest prefix that exists. Paths through a keyword's
116
+ // value that is a primitive, or through a key the schema does not have,
117
+ // land on the nearest enclosing node.
118
+ let p = schemaPath;
119
+ while (true) {
120
+ const cut = p.lastIndexOf('/');
121
+ if (cut < 0) return 0;
122
+ p = p.slice(0, cut);
123
+ const o = map.get(p);
124
+ if (o !== undefined) return o;
125
+ }
126
+ }
127
+
128
+ module.exports = { rankFor, ordinalFor, unescapePointerSegment };
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.16.2';
10
+ module.exports = '1.17.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.16.2",
3
+ "version": "1.17.1",
4
4
  "description": "JSON Schema validation with first-class TypeScript and zero runtime cost. AOT compile to per-schema ESM modules with zero validator dependency. Generic Validator<T> for TypeBox/Zod/Valibot composition. Optional runtime API. Standard Schema V1 compatible.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -51,7 +51,7 @@
51
51
  "release:check": "node scripts/regen-safe-regex-source.js && node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_source_sync.js && node tests/test_version_sync.js",
52
52
  "build": "cmake-js build --target ata",
53
53
  "rebuild": "cmake-js rebuild --target ata",
54
- "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_buffer_gate.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_aot_parse.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_formats_single_pass.js && node tests/test_format_single_error.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_ajv_parity.js && node tests/test_user_format_error_path.js && node tests/test_unevaluated_error_path.js && node tests/test_pattern_properties_errors.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_value_equality.js && node tests/test_vocabulary.js && node tests/test_schema_scan.js && node tests/test_lazy_errors.js && node tests/test_lazy_instance.js && node tests/test_cyclic_input.js && node tests/test_native_error_codes.js && node tests/test_verdict_preprocess.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_additive_fields.js && node tests/test_diagnostic_source.js && node tests/test_diagnose.js && node tests/test_correlate.js && node tests/test_diagnostics_score.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
54
+ "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_buffer_gate.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_aot_parse.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_formats_single_pass.js && node tests/test_format_single_error.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_ajv_parity.js && node tests/test_user_format_error_path.js && node tests/test_unevaluated_error_path.js && node tests/test_pattern_properties_errors.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_error_order_ordinal.js && node tests/test_rejection_shape.js && node tests/test_value_equality.js && node tests/test_vocabulary.js && node tests/test_schema_scan.js && node tests/test_lazy_errors.js && node tests/test_lazy_instance.js && node tests/test_cyclic_input.js && node tests/test_native_error_codes.js && node tests/test_verdict_preprocess.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_additive_fields.js && node tests/test_diagnostic_source.js && node tests/test_diagnose.js && node tests/test_correlate.js && node tests/test_diagnostics_score.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
55
55
  "bench:size": "node benchmark/bench_aot_size.mjs",
56
56
  "test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
57
57
  "test:compat": "node tests/test_compat.js",
@@ -122,13 +122,13 @@
122
122
  "LICENSE"
123
123
  ],
124
124
  "optionalDependencies": {
125
- "@ata-validator/native-darwin-arm64": "1.16.2",
126
- "@ata-validator/native-darwin-x64": "1.16.2",
127
- "@ata-validator/native-linux-arm64-gnu": "1.16.2",
128
- "@ata-validator/native-linux-arm64-musl": "1.16.2",
129
- "@ata-validator/native-linux-x64-gnu": "1.16.2",
130
- "@ata-validator/native-linux-x64-musl": "1.16.2",
131
- "@ata-validator/native-win32-x64": "1.16.2"
125
+ "@ata-validator/native-darwin-arm64": "1.17.1",
126
+ "@ata-validator/native-darwin-x64": "1.17.1",
127
+ "@ata-validator/native-linux-arm64-gnu": "1.17.1",
128
+ "@ata-validator/native-linux-arm64-musl": "1.17.1",
129
+ "@ata-validator/native-linux-x64-gnu": "1.17.1",
130
+ "@ata-validator/native-linux-x64-musl": "1.17.1",
131
+ "@ata-validator/native-win32-x64": "1.17.1"
132
132
  },
133
133
  "peerDependencies": {
134
134
  "yaml": "^2.0.0"