ata-validator 1.39.1 → 1.39.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,7 @@ JSON Schema validation that compiles for speed and still runs where code generat
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/ata-validator)](https://www.npmjs.com/package/ata-validator)
6
6
  [![License](https://img.shields.io/npm/l/ata-validator)](LICENSE)
7
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ata-core/ata-validator/badge)](https://scorecard.dev/viewer/?uri=github.com/ata-core/ata-validator)
7
8
 
8
9
  1.0 is a stability commitment: see [docs/STABILITY.md](docs/STABILITY.md) for the semver, deprecation, and error-code guarantees.
9
10
 
@@ -187,16 +188,16 @@ const v = new Validator(schema, { source: { path: 'schemas/user.json', content:
187
188
  const r = v.validateJSON(input)
188
189
  if (!r.valid) {
189
190
  console.error(renderPretty(r.errors))
190
- // error[ATA3001]: value does not match format "email"
191
- // --> schemas/user.json:5:7
191
+ // error[ATA3001]: not a valid email: "not-an-email"
192
+ // --> schemas/user.json:5:44
192
193
  // |
193
- // 5 | "email": { "type": "string", "format": "email" }
194
- // | ^^^^^^^ expected format 'email'
194
+ // 5 | "email": { "type": "string", "format": "email" },
195
+ // | ^ expected format 'email'
195
196
  // |
196
- // --> input, byte 23
197
+ // --> input:1:24 (body.email)
197
198
  // |
198
- // 1 | {"name":"M","email":"not-an-email","age":-3}
199
- // | ^^^^^^^^^^^^^^ got "not-an-email"
199
+ // 1 | {"name":"Mert","email":"not-an-email","age":26}
200
+ // | ^^^^^^^^^^^^^^ found "not-an-email"
200
201
  // |
201
202
  // = help: missing '@' and domain part
202
203
  // = note: see https://ata-validator.com/e/ATA3001
package/index.js CHANGED
@@ -36,7 +36,7 @@ class TextRejection {
36
36
  }
37
37
 
38
38
  function installCodegenPaths (ctx) {
39
- const { ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindVerdict, _jsonSyntaxRejection, _mustReject, getNative, isV1Dialect, resolveSchemaByPath } = core._internals;
39
+ const { ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindVerdict, _jsonSyntaxRejection, _mustReject, _verboseWrap, getNative, isV1Dialect, resolveSchemaByPath } = core._internals;
40
40
  const { jsFn, _isCodegen, preprocess, fusedRemove, options, schemaObj, useSimdjsonForLarge, _buildCombined, _buildErr } = ctx;
41
41
  // errFn: the generated error function when it is safe, else the
42
42
  // interpreted engine, on every platform alike.
@@ -222,6 +222,11 @@ function installCodegenPaths (ctx) {
222
222
  this.validate = preprocess
223
223
  ? (data) => { preprocess(data); return run(data); }
224
224
  : run;
225
+ // What validate() returns for a document already known to fail, without
226
+ // deciding again: the lazy layer in lib/validator-core.js has its verdict
227
+ // and used to call validate() to get the errors, which ran the verdict a
228
+ // second time and built a second rejection around it.
229
+ if (!preprocess) ctx.rejectBase = onReject;
225
230
  } else {
226
231
  // No hybrid factory, so the assembly needs the function itself rather
227
232
  // than a reference it can call later: build it now.
@@ -230,6 +235,7 @@ function installCodegenPaths (ctx) {
230
235
  this.validate = preprocess
231
236
  ? (data) => { preprocess(data); return safeCombinedFn(data); }
232
237
  : safeCombinedFn;
238
+ if (!preprocess) ctx.rejectBase = (data) => _mustReject(safeCombinedFn(data));
233
239
  } else {
234
240
  this.validate = preprocess
235
241
  ? (data) => {
@@ -237,6 +243,7 @@ function installCodegenPaths (ctx) {
237
243
  return jsFn(data) ? VALID_RESULT : errOnly(data);
238
244
  }
239
245
  : (data) => (jsFn(data) ? VALID_RESULT : errOnly(data));
246
+ if (!preprocess) ctx.rejectBase = errOnly;
240
247
  }
241
248
  }
242
249
  // Verbose mode: populate parentSchema, schema and data on each error, the
@@ -246,33 +253,10 @@ function installCodegenPaths (ctx) {
246
253
  // migration ended up writing by hand. Errors may be frozen, so clone
247
254
  // them with the extra fields.
248
255
  if (this._verbose) {
249
- const inner = this.validate;
250
- const root = this._schemaObj;
251
- const { resolvePointer } = require('./lib/pointer.js');
252
- this.validate = (data) => {
253
- const result = inner(data);
254
- if (result && !result.valid && result.errors) {
255
- const enriched = result.errors.map((err) => {
256
- if (!err || err.parentSchema !== undefined) return err;
257
- const parentSchema = resolveSchemaByPath(root, err.schemaPath);
258
- // The last segment of the schema path is the keyword that
259
- // failed, so its value on the parent is that keyword's schema.
260
- const sp = typeof err.schemaPath === 'string' ? err.schemaPath : '';
261
- const last = sp.slice(sp.lastIndexOf('/') + 1).replace(/~1/g, '/').replace(/~0/g, '~');
262
- const keywordSchema = (parentSchema !== null && typeof parentSchema === 'object' && last)
263
- ? parentSchema[last]
264
- : undefined;
265
- return {
266
- ...err,
267
- parentSchema,
268
- schema: keywordSchema,
269
- data: resolvePointer(data, err.instancePath, undefined),
270
- };
271
- });
272
- return { valid: false, errors: enriched };
273
- }
274
- return result;
275
- };
256
+ // The verbose fields are added here, so a path around this layer would
257
+ // miss them.
258
+ ctx.rejectBase = null;
259
+ this.validate = _verboseWrap(this.validate, this._schemaObj);
276
260
  }
277
261
  // The verdict methods answer validate()'s question without building the
278
262
  // error list, so they run the same preprocess pass. Skipping it made the
package/lib/aot-impl.js CHANGED
@@ -186,6 +186,24 @@ function emitFormatDecls(closures, mode, declKW) {
186
186
 
187
187
  const { emitClone, inlineRefsForClone } = require('./clone-emit');
188
188
 
189
+ // The schema-source frames the error function names, as `__ataSS[i]`. Each
190
+ // is built once, frozen, from a [line, col, text index] row, with every source
191
+ // line's text written once. Frozen, so sharing one object between reads is safe.
192
+ function sourceFrameDecls(frames, schemaFile) {
193
+ if (!frames || frames.size === 0) return '';
194
+ const texts = [];
195
+ const textIndex = new Map();
196
+ const rows = [];
197
+ for (const f of frames.values()) {
198
+ let i = textIndex.get(f.text);
199
+ if (i === undefined) { i = texts.length; texts.push(f.text); textIndex.set(f.text, i); }
200
+ rows[f.index] = `[${f.line},${f.col},${i}]`;
201
+ }
202
+ return `const __ataSF = ${JSON.stringify(schemaFile)};\n` +
203
+ `const __ataSL = ${JSON.stringify(texts)};\n` +
204
+ `const __ataSS = [${rows.join(',')}].map((r) => Object.freeze({ file: __ataSF, line: r[0], col: r[1], text: __ataSL[r[2]] }));\n`;
205
+ }
206
+
189
207
  function toStandaloneModule(validator, opts) {
190
208
  assertEmittable(validator, 'toStandaloneModule');
191
209
  validator._ensureCompiled();
@@ -200,12 +218,13 @@ function toStandaloneModule(validator, opts) {
200
218
 
201
219
  let errCore = '';
202
220
  let jsErrFn = null;
221
+ const sourceFrames = new Map();
203
222
  if (!abortEarly) {
204
223
  jsErrFn = compileToJSCodegenWithErrors(
205
224
  typeof validator._schemaObj === 'object' ? validator._schemaObj : {},
206
225
  null,
207
226
  validator._userFormats,
208
- (source && sourceMap && schemaFile) ? { sourceMap, schemaFile } : null,
227
+ (source && sourceMap && schemaFile) ? { sourceMap, schemaFile, frames: sourceFrames } : null,
209
228
  );
210
229
  const errSrc = jsErrFn && jsErrFn._errSource ? jsErrFn._errSource : '';
211
230
  if (errSrc && jsErrFn._errFactory) {
@@ -245,7 +264,7 @@ function toStandaloneModule(validator, opts) {
245
264
  // entirely so size budgets and grep-based "is this source-mapped?" checks
246
265
  // both work.
247
266
  const schemaSourceConst = (source && schemaFile)
248
- ? `const __ATA_SCHEMA_SOURCE__ = ${JSON.stringify({ file: schemaFile })};\n`
267
+ ? `const __ATA_SCHEMA_SOURCE__ = ${JSON.stringify({ file: schemaFile })};\n` + sourceFrameDecls(sourceFrames, schemaFile)
249
268
  : '';
250
269
 
251
270
  // Serialize closure vars referenced in _fn body: regex, sub-validators, sets.
@@ -173,6 +173,7 @@ const DOC_BASE = 'https://ata-validator.com/e/'
173
173
  // reader sorts by without deriving it from the path. Empty when the path is
174
174
  // not into the root document.
175
175
  function ordinalField (ctx, schemaPath) {
176
+ if (ctx && ctx.noOrdinal) return ''
176
177
  const o = ctx && ctx.rootSchema ? ordinalFor(ctx.rootSchema, unescapeSp(schemaPath)) : null
177
178
  return o === null ? '' : `,_o:${o}`
178
179
  }
@@ -198,6 +199,8 @@ function unescapeSp (sp) {
198
199
  return sp.indexOf('\\') === -1 ? sp : sp.replace(/\\(.)/g, '$1')
199
200
  }
200
201
 
202
+ const FRAME_INLINE_MAX = 120
203
+
201
204
  function buildErrorLiteral (opts) {
202
205
  const { keyword, format, schemaPath, sourceMap } = opts
203
206
  let code = (keyword === 'format' && format) ? codeFor('format', format) : codeFor(keyword)
@@ -214,10 +217,24 @@ function buildErrorLiteral (opts) {
214
217
  const line = Array.isArray(hit) ? hit[0] : hit.line
215
218
  const col = Array.isArray(hit) ? hit[1] : hit.col
216
219
  const text = Array.isArray(hit) ? hit[2] : hit.text
217
- frame = ',schemaSource:Object.freeze({file:' + JSON.stringify(sourceMap.file) +
218
- ',line:' + line +
219
- ',col:' + col +
220
- ',text:' + JSON.stringify(text) + '})'
220
+ if (sourceMap.frames && text.length > FRAME_INLINE_MAX) {
221
+ // A long source line is written once at module scope (see
222
+ // sourceFrameDecls in aot-impl.js) and the literal names it. Inlined,
223
+ // every error site carried the whole line, and a schema kept on one
224
+ // line is one line of all of it: 181 KB minified for a 2 KB schema.
225
+ // Short lines stay inline: gzip folds the repeats, and measured on
226
+ // the 50-field fixture the table cost 1206 gzipped bytes where the
227
+ // inline frames cost 696.
228
+ const key = line + ':' + col
229
+ let f = sourceMap.frames.get(key)
230
+ if (!f) { f = { line, col, text, index: sourceMap.frames.size }; sourceMap.frames.set(key, f) }
231
+ frame = ',schemaSource:__ataSS[' + f.index + ']'
232
+ } else {
233
+ frame = ',schemaSource:Object.freeze({file:' + JSON.stringify(sourceMap.file) +
234
+ ',line:' + line +
235
+ ',col:' + col +
236
+ ',text:' + JSON.stringify(text) + '})'
237
+ }
221
238
  }
222
239
  }
223
240
  return { codeStr: code, docUrl, frame }
@@ -4315,7 +4332,7 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
4315
4332
  // closure parameters below the way the other entry points do.
4316
4333
  closureVars: [], closureVals: [],
4317
4334
  sourceMap: (sourceOpts && sourceOpts.sourceMap && sourceOpts.schemaFile)
4318
- ? { file: sourceOpts.schemaFile, map: sourceOpts.sourceMap }
4335
+ ? { file: sourceOpts.schemaFile, map: sourceOpts.sourceMap, frames: sourceOpts.frames || null }
4319
4336
  : null }
4320
4337
  ctx.helperCode.push('const _cpLen=s=>{let n=0;for(const _ of s)n++;return n}')
4321
4338
  const lines = []
@@ -4393,6 +4410,18 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
4393
4410
  factoryBody = helpersE + 'return function(d,_all){const _e=[];\n ' + checkStr +
4394
4411
  '\n return{valid:_e.length===0,errors:_e}}'
4395
4412
  }
4413
+ // For the runtime, the literals take the legacy shape directly. `code` and
4414
+ // `docUrl` are there for standalone modules, which return errors as they
4415
+ // are; the runtime derives both from the keyword when it enriches, and
4416
+ // orders errors by schemaPath, so the literals carried three fields that
4417
+ // every read then copied the error to remove. The patterns are this
4418
+ // generator's own spelling; the collapse helper writes its fields with a
4419
+ // space and keeps them.
4420
+ if (sourceOpts && sourceOpts.runtimeShape) {
4421
+ const shape = (src) => src.replace(/code:'ATA\d{4}',/g, '').replace(/,docUrl:'https:\/\/ata-validator\.com\/e\/ATA\d{4}'/g, '').replace(/,_o:\d+/g, '')
4422
+ body = shape(body)
4423
+ factoryBody = shape(factoryBody)
4424
+ }
4396
4425
  const hoistable = !ctx.usesRecursion && defSetsE.length === 0 && helpersE !== ''
4397
4426
  if (hoistable || needsGuardE) {
4398
4427
  const params = [...cvars, '__ataSafeRe']
@@ -5177,7 +5206,7 @@ function genIfE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
5177
5206
  // Returns VALID_RESULT for valid data, {valid:false, errors} for invalid.
5178
5207
  // Avoids double-pass (jsFn → false → errFn runs same checks again).
5179
5208
  // Uses type-aware optimizations: after type check passes, skip guards.
5180
- function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
5209
+ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats, opts) {
5181
5210
  const inputSchema = schema
5182
5211
  schema = normalizePropertyNames(schema)
5183
5212
  // Same provably-local rule as the error generator above.
@@ -5257,7 +5286,8 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
5257
5286
  if (hasUnresolvableRef(schema, cRootDefs, cAnchors, schemaMap, new Set())) return null
5258
5287
  if (needsBaseTracking(schema, schemaMap, new Set())) return null
5259
5288
 
5260
- const ctx = { varCounter: 0, helperCode: [], shared: [], closureVars: ['_cpLen'], closureVals: [_cpLen],
5289
+ // runtimeShape: errors without the ordinal, see compileToJSCodegenWithErrors.
5290
+ const ctx = { noOrdinal: !!(opts && opts.runtimeShape), varCounter: 0, helperCode: [], shared: [], closureVars: ['_cpLen'], closureVals: [_cpLen],
5261
5291
  rootDefs: cRootDefs, refStack: new Set(), schemaMap: schemaMap || null, anchors: cAnchors, rootSchema: inputSchema, userFormats: userFormats || null }
5262
5292
  const lines = []
5263
5293
  try {
@@ -5392,7 +5422,7 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
5392
5422
  if (pathVal !== undefined) {
5393
5423
  const spVal = unescapeSp(sp)
5394
5424
  ctx.closureVars.push(errVar)
5395
- const o = ordinalFor(ctx.rootSchema, spVal)
5425
+ const o = ctx.noOrdinal ? null : ordinalFor(ctx.rootSchema, spVal)
5396
5426
  const lit = {keyword, instancePath: pathVal, schemaPath: spVal, params: Object.freeze(paramsVal), message: msgVal}
5397
5427
  if (o !== null) lit._o = o
5398
5428
  ctx.closureVals.push(Object.freeze(lit))
package/lib/rejections.js CHANGED
@@ -80,49 +80,53 @@ class RichRejection {
80
80
  return raw;
81
81
  }
82
82
  }
83
+ // Orders a raw error list and gives it the shape the caller asked for: the
84
+ // legacy key set, or enriched. Shared by both rejection classes below.
85
+ function presentErrors(raw, data, rawInput, self, root, enrich) {
86
+ if (needsOrdering(raw)) raw = sortErrorsBySchemaOrder(root, raw);
87
+ // The position map, resolved now that an error is actually being read.
88
+ let positions = null;
89
+ if (enrich && raw.length && rawInput != null) {
90
+ positions = self._pos().targeted(rawInput, wantedPointersFor(raw));
91
+ if (positions) self._posCache.reset();
92
+ }
93
+ // One options object for the whole list, not one per error.
94
+ const opts = enrich && raw.length
95
+ ? {
96
+ data,
97
+ positions,
98
+ schemaPositions: self._schemaPositions,
99
+ schemaFile: self._source ? self._source.path : undefined,
100
+ }
101
+ : null;
102
+ const cached = opts
103
+ ? raw.map((e) => enrich(e, opts))
104
+ // The v0.14 shape is a fixed key set; the ordering key the
105
+ // generated code carries is dropped from it here.
106
+ : raw.map(stripOrdinal);
107
+ // Correlation is published, never applied. Both halves of a typo pair
108
+ // stay in the array; `related` only says they are one mistake, so a
109
+ // wrong pairing costs a sentence rather than a hidden violation.
110
+ if (enrich && cached.length > 1) attachRelated(cached);
111
+ // No diagnostic payload here. validate(data) is the library hot path,
112
+ // and attaching one cost about 100 ns per rejection for a consumer
113
+ // that never renders. The text path attaches it, and a renderer given
114
+ // `{ data }` builds frames for object input on request.
115
+ return cached;
116
+ }
117
+
83
118
  Object.defineProperty(RichRejection.prototype, 'errors', {
84
119
  enumerable: true,
85
120
  configurable: true,
86
121
  get() {
87
122
  if (this._cached === null) {
88
- const self = this._self;
89
- const enrich = this._enrich;
90
- let raw = this._result.errors || [];
91
- if (needsOrdering(raw)) raw = sortErrorsBySchemaOrder(this._root, raw);
92
- // The position map, resolved now that an error is actually being read.
93
- let positions = null;
94
- if (enrich && raw.length && this._rawInput != null) {
95
- positions = self._pos().targeted(this._rawInput, wantedPointersFor(raw));
96
- if (positions) self._posCache.reset();
97
- }
98
- // One options object for the whole list, not one per error.
99
- const opts = enrich && raw.length
100
- ? {
101
- data: this._data,
102
- positions,
103
- schemaPositions: self._schemaPositions,
104
- schemaFile: self._source ? self._source.path : undefined,
105
- }
106
- : null;
107
- const cached = opts
108
- ? raw.map((e) => enrich(e, opts))
109
- // The v0.14 shape is a fixed key set; the ordering key the
110
- // generated code carries is dropped from it here.
111
- : raw.map(stripOrdinal);
112
- // Correlation is published, never applied. Both halves of a typo pair
113
- // stay in the array; `related` only says they are one mistake, so a
114
- // wrong pairing costs a sentence rather than a hidden violation.
115
- if (enrich && cached.length > 1) attachRelated(cached);
116
- // No diagnostic payload here. validate(data) is the library hot path,
117
- // and attaching one cost about 100 ns per rejection for a consumer
118
- // that never renders. The text path attaches it, and a renderer given
119
- // `{ data }` builds frames for object input on request.
120
- this._cached = cached;
123
+ this._cached = presentErrors(this._result.errors || [], this._data, this._rawInput, this._self, this._root, this._enrich);
121
124
  }
122
125
  return this._cached;
123
126
  },
124
127
  });
125
128
 
129
+
126
130
  // The rejection validateJSON returns. Everything the text path adds over
127
131
  // validate(data), the value tree enrichment needs, the position map, the
128
132
  // diagnostic payload, happens on first access to `.errors`. Reading `.valid`
@@ -256,7 +260,10 @@ function wantedPointersFor (errors) {
256
260
  const COLLAPSE_CODES = new Set(['ATA4001', 'ATA4002', 'ATA4003']);
257
261
  function stripOrdinal(e) {
258
262
  if (e === null || typeof e !== 'object') return e;
259
- if (e._o === undefined && e.docUrl === undefined && (e.code === undefined || COLLAPSE_CODES.has(e.code)) && !e.branchErrors) return e;
263
+ // Generated code keeps errors at a fixed path as frozen literals shared by
264
+ // every call. The caller gets its own copy, as it always has, so an error it
265
+ // edits (a translated message) is its own and editing it does not throw.
266
+ if (e._o === undefined && e.docUrl === undefined && (e.code === undefined || COLLAPSE_CODES.has(e.code)) && !e.branchErrors) return Object.isFrozen(e) ? { ...e } : e;
260
267
  const out = {};
261
268
  for (const k in e) {
262
269
  if (k === '_o' || k === 'docUrl') continue;
@@ -364,4 +371,4 @@ function attachRelated (errors) {
364
371
  return errors;
365
372
  }
366
373
 
367
- module.exports = { LazyRejection, RichRejection, LazyJsonRejection, _enrichLazy, attachRelated, sortErrorsBySchemaOrder, stripOrdinal, wantedPointersFor };
374
+ module.exports = { LazyRejection, RichRejection, presentErrors, needsOrdering, LazyJsonRejection, _enrichLazy, attachRelated, sortErrorsBySchemaOrder, stripOrdinal, wantedPointersFor };
@@ -42,7 +42,11 @@ function caretSuffix (d, raw, useColor) {
42
42
  function renderOne (d, useColor, opts) {
43
43
  const lines = [];
44
44
  const width = terminalWidth();
45
- const gutter = 3;
45
+ // The bar sits one column past the widest line number, so the source line,
46
+ // the blank rows and the caret row share it and a caret lands on the first
47
+ // character of the value, not one column to its left.
48
+ const bar = (line) => ' '.repeat(Math.max(3, String(line).length + 1)) + '|';
49
+ const src = (line, text) => String(line).padStart(Math.max(2, String(line).length)) + ' | ' + text;
46
50
 
47
51
  // Headline carries the code when there is one. A code-less error, which the
48
52
  // LazyRejection fallback can produce, must not render as "error[undefined]".
@@ -57,12 +61,12 @@ function renderOne (d, useColor, opts) {
57
61
  if (raw && raw.schemaSource) {
58
62
  const f = trimCwd(raw.schemaSource.file, opts.cwd);
59
63
  lines.push(` --> ${color(useColor, ANSI.cyan, `${f}:${raw.schemaSource.line}:${raw.schemaSource.col}`)}`);
60
- lines.push(' |');
61
- const sln = String(raw.schemaSource.line).padStart(2, ' ');
62
- lines.push(` ${sln} | ${truncateLine(raw.schemaSource.text, width - 8)}`);
64
+ const sl = raw.schemaSource.line;
65
+ lines.push(bar(sl));
66
+ lines.push(src(sl, truncateLine(raw.schemaSource.text, width - 8)));
63
67
  const inlineHint = raw.expected ? ' ' + color(useColor, ANSI.dim, `expected ${raw.expected}`) : '';
64
- lines.push(caretLine(raw.schemaSource.col, 1, gutter) + inlineHint);
65
- lines.push(' |');
68
+ lines.push(caretLine(raw.schemaSource.col, 1, bar(sl).length - 1) + inlineHint);
69
+ lines.push(bar(sl));
66
70
  }
67
71
 
68
72
  // Data location, always, even without a frame. This is the single most
@@ -70,11 +74,11 @@ function renderOne (d, useColor, opts) {
70
74
  if (d.frame) {
71
75
  const where = `input:${d.frame.line}:${d.frame.col}`;
72
76
  lines.push(` --> ${color(useColor, ANSI.cyan, where)} ${color(useColor, ANSI.dim, '(' + d.dotted + ')')}`);
73
- lines.push(' |');
74
- const ln = String(d.frame.line).padStart(2, ' ');
75
- lines.push(` ${ln} | ${truncateLine(d.frame.text, width - 8)}`);
76
- lines.push(caretLine(d.frame.col, d.frame.length, gutter) + caretSuffix(d, raw, useColor));
77
- lines.push(' |');
77
+ const fl = d.frame.line;
78
+ lines.push(bar(fl));
79
+ lines.push(src(fl, truncateLine(d.frame.text, width - 8)));
80
+ lines.push(caretLine(d.frame.col, d.frame.length, bar(fl).length - 1) + caretSuffix(d, raw, useColor));
81
+ lines.push(bar(fl));
78
82
  } else {
79
83
  lines.push(` --> ${color(useColor, ANSI.cyan, 'at ' + d.dotted)}`);
80
84
  }
@@ -156,6 +156,14 @@ function ordinalFor(rootSchema, schemaPath) {
156
156
  const map = ordinals(rootSchema);
157
157
  const hit = map.get(schemaPath);
158
158
  if (hit !== undefined) return hit;
159
+ // A path that is not a node is answered from the cache below, filled by the
160
+ // walk after it: every error behind a `$ref`, which is every error of a
161
+ // schema the interpreter answers through references, used to walk its path
162
+ // back segment by segment on every read.
163
+ let misses = _missCache.get(rootSchema);
164
+ if (misses === undefined) { misses = new Map(); _missCache.set(rootSchema, misses); }
165
+ const known = misses.get(schemaPath);
166
+ if (known !== undefined) return known;
159
167
  // Walk back to the longest prefix that exists. Paths through a keyword's
160
168
  // value that is a primitive, or through a key the schema does not have,
161
169
  // land on the nearest enclosing node.
@@ -163,16 +171,24 @@ function ordinalFor(rootSchema, schemaPath) {
163
171
  // behind a reference sort where the `$ref` is declared among its siblings;
164
172
  // rankFor orders them among themselves.
165
173
  let p = schemaPath;
174
+ let found = 0;
166
175
  while (true) {
167
176
  const cut = p.lastIndexOf('/');
168
- if (cut < 0) return 0;
177
+ if (cut < 0) break;
169
178
  p = p.slice(0, cut);
170
179
  const o = map.get(p);
171
180
  if (o !== undefined) {
172
181
  const r = map.get(p + '/$ref');
173
- return r !== undefined ? r : o;
182
+ found = r !== undefined ? r : o;
183
+ break;
174
184
  }
175
185
  }
186
+ // Bounded: behind a recursive reference the paths grow with the document's
187
+ // depth, and the cache must not grow with them.
188
+ if (misses.size < MISS_CACHE_LIMIT) misses.set(schemaPath, found);
189
+ return found;
176
190
  }
191
+ const _missCache = new WeakMap();
192
+ const MISS_CACHE_LIMIT = 4096;
177
193
 
178
194
  module.exports = { rankFor, ordinalFor, unescapePointerSegment };
@@ -292,7 +292,7 @@ function resolveSchemaByPath(rootSchema, schemaPath) {
292
292
  //
293
293
  // A failing route sees the same handful of schemaPaths over and over, which is
294
294
  // what makes the first one worth having.
295
- const { LazyRejection, RichRejection, LazyJsonRejection, _enrichLazy } = require('./rejections');
295
+ const { LazyRejection, RichRejection, LazyJsonRejection, _enrichLazy, presentErrors, needsOrdering, sortErrorsBySchemaOrder } = require('./rejections');
296
296
 
297
297
  // Text that does not parse, reported the same way on every path and platform:
298
298
  // ATA9001 with the parser's own message. The native addon used to say only
@@ -991,20 +991,28 @@ class Validator {
991
991
  };
992
992
  const _buildCombined = () => {
993
993
  if (jsCombinedFn !== undefined) return;
994
- jsCombinedFn = compileToJSCombined(schemaObj, VALID_RESULT, sm, this._userFormats) || null;
994
+ jsCombinedFn = compileToJSCombined(schemaObj, VALID_RESULT, sm, this._userFormats, { runtimeShape: true }) || null;
995
995
  _upgradeCacheEntry();
996
996
  };
997
997
  const _buildErr = () => {
998
998
  if (jsErrFn !== undefined) return;
999
- jsErrFn = compileToJSCodegenWithErrors(schemaObj, sm, this._userFormats) || null;
999
+ jsErrFn = compileToJSCodegenWithErrors(schemaObj, sm, this._userFormats, { runtimeShape: true }) || null;
1000
1000
  _upgradeCacheEntry();
1001
1001
  };
1002
1002
 
1003
+ // What validate() returns for a document already known to fail, without
1004
+ // deciding again, when the codegen paths can say (see index.js). Kept in
1005
+ // locals and on this object rather than on the instance: every call reads
1006
+ // `validate` off the instance, and three more own properties made that
1007
+ // read slower across the suite's validators.
1008
+ let _rejectBase = null, _rejectPresented = null, _rejectPlain = false;
1003
1009
  if (jsFn) {
1004
- _codegen.installPaths.call(this, {
1010
+ const _cg = {
1005
1011
  jsFn, _isCodegen, preprocess, fusedRemove, options, schemaObj, useSimdjsonForLarge, _buildCombined, _buildErr,
1006
- combined: () => jsCombinedFn, err: () => jsErrFn,
1007
- });
1012
+ combined: () => jsCombinedFn, err: () => jsErrFn, rejectBase: null,
1013
+ };
1014
+ _codegen.installPaths.call(this, _cg);
1015
+ _rejectBase = _cg.rejectBase;
1008
1016
  } else {
1009
1017
  // No JS codegen: the interpreted engine, with or without the addon. The
1010
1018
  // buffer APIs come from _installBufferApis on first use, as they do on
@@ -1030,7 +1038,9 @@ class Validator {
1030
1038
  : (preprocess
1031
1039
  ? (data) => { preprocess(data); return interp.validate(data); }
1032
1040
  : (data) => interp.validate(data));
1033
- this.validate = run;
1041
+ this.validate = this._verbose ? _verboseWrap(run, schemaObj) : run;
1042
+ // Reading errors runs the interpreter once, directly; see _rejectBase.
1043
+ if (!preprocess && !options.abortEarly && !this._verbose) _rejectBase = (data) => interp.validate(data);
1034
1044
  _bindVerdict(this, this._fastVerdict
1035
1045
  ? this._fastVerdict
1036
1046
  : (data) => run(data).valid);
@@ -1059,8 +1069,7 @@ class Validator {
1059
1069
  const enrich = this._richErrors ? _enrichLazy : null;
1060
1070
  const root = this._schemaObj;
1061
1071
  const self = this;
1062
- this.validate = (data) => {
1063
- const result = inner(data);
1072
+ const present = (result, data) => {
1064
1073
  // abortEarly returns the shared ATA9000 stub; preserve it as-is so the
1065
1074
  // perf fast path stays allocation-free and the documented code stays stable.
1066
1075
  if (result && result.valid === false && result !== ABORT_EARLY_RESULT) {
@@ -1076,6 +1085,13 @@ class Validator {
1076
1085
  }
1077
1086
  return result;
1078
1087
  };
1088
+ this.validate = (data) => present(inner(data), data);
1089
+ // The same presentation for a document already known to fail.
1090
+ const rejectBase = _rejectBase;
1091
+ _rejectPresented = rejectBase ? (data) => present(rejectBase(data), data) : null;
1092
+ // True while nothing but this layer stands between a rejection and the
1093
+ // caller; the lazy layer below can then build the rejection itself.
1094
+ _rejectPlain = !!rejectBase;
1079
1095
 
1080
1096
  // validateJSON also enriches: set _lastRawInput so the position cache
1081
1097
  // can lazily build a map for dataFrame attachment. Only validateJSON
@@ -1149,6 +1165,8 @@ class Validator {
1149
1165
  return result;
1150
1166
  };
1151
1167
  if (this.validate) this.validate = wrap(this.validate);
1168
+ if (_rejectPresented) _rejectPresented = wrap(_rejectPresented);
1169
+ _rejectPlain = false;
1152
1170
  if (this.validateJSON) this.validateJSON = wrap(this.validateJSON);
1153
1171
  // validateAndParse routes through self.validate on the codegen path, but
1154
1172
  // the native-only path returns directly from the addon — wrap it so both
@@ -1185,7 +1203,13 @@ class Validator {
1185
1203
  const _vx = this._validateTail !== null ? this._validateTail() : null;
1186
1204
  let _vxApplied = false;
1187
1205
  if (this._fastVerdict && !preprocess && !options.abortEarly && this.validate) {
1188
- const _full = this.validate;
1206
+ // The errors of a document the verdict rejected come from the rejection
1207
+ // path directly, not from validate() deciding a second time. Not with a
1208
+ // check from _extendValidate: that check can reject what the schema
1209
+ // accepts, and then the schema's own errors must come from a verdict.
1210
+ // Not with removeAdditional's fused pass either, which edits the
1211
+ // document before the errors are read.
1212
+ const _full = (_vx === null && !fusedRemove && _rejectPresented) ? _rejectPresented : this.validate;
1189
1213
  const _fast = _vx ? _fuseTail(this._fastVerdict, _vx.check) : this._fastVerdict;
1190
1214
  const _extra = _vx ? _vx.errors : null;
1191
1215
  _vxApplied = true;
@@ -1198,11 +1222,19 @@ class Validator {
1198
1222
  // verdict and say so rather than inventing a specific error.
1199
1223
  return own || _verdictFallback;
1200
1224
  };
1201
- const _buildErrors = (data) => {
1225
+ // The common case: the verdict decides, and reading `errors` runs the
1226
+ // error function and presents its list directly. Through the layers it
1227
+ // took a second verdict and a second rejection object, about 75 ns of a
1228
+ // 96 ns read on a two-error document. validate() below is the same code
1229
+ // either way, so reading only `.valid` costs what it did.
1230
+ const _plain = (_vx === null && !fusedRemove && _rejectPlain)
1231
+ ? _plainBuilders(_rejectBase, this._richErrors ? _enrichLazy : null, this._schemaObj, this, _verdictFallback)
1232
+ : null;
1233
+ const _buildErrors = _plain !== null ? _plain.errors : (data) => {
1202
1234
  const r = _full(data);
1203
1235
  return _withExtra((r && r.valid === false && r.errors && r.errors.length) ? r.errors : null, data);
1204
1236
  };
1205
- const _buildRawErrors = (data) => {
1237
+ const _buildRawErrors = _plain !== null ? _plain.raw : (data) => {
1206
1238
  const r = _full(data);
1207
1239
  let raw = null;
1208
1240
  if (r && r.valid === false) {
@@ -1700,6 +1732,60 @@ const _VERDICT_DISAGREES = Object.freeze({
1700
1732
  valid: false,
1701
1733
  errors: Object.freeze([Object.freeze({ keyword: 'validation', instancePath: '', schemaPath: '#', params: Object.freeze({}), message: 'schema validation failed' })]),
1702
1734
  });
1735
+ // The error builders of the common rejection path (see _ensureCompiled): run
1736
+ // the error function behind `reject` for a document the verdict rejected, and
1737
+ // order and present its list. Out here so the closures capture only this.
1738
+ function _plainBuilders(reject, enrich, root, self, fallback) {
1739
+ const rawOf = (data) => {
1740
+ const r = reject(data);
1741
+ const errors = r && r.errors;
1742
+ return errors && errors.length ? errors : fallback;
1743
+ };
1744
+ return {
1745
+ errors: (data) => presentErrors(rawOf(data), data, null, self, root, enrich),
1746
+ raw: (data) => {
1747
+ const raw = rawOf(data);
1748
+ return needsOrdering(raw) ? sortErrorsBySchemaOrder(root, raw) : raw;
1749
+ },
1750
+ };
1751
+ }
1752
+
1753
+ // Verbose mode: populate parentSchema, schema and data on each error, the
1754
+ // three fields the default error shape carries under the same option. `data`
1755
+ // is the value the error points at; without it a caller has to walk the
1756
+ // document by the instance path itself, which is what one migration ended up
1757
+ // writing by hand. Errors may be frozen, so they are cloned with the extra
1758
+ // fields. Both engines go through this; the interpreted one used to skip it,
1759
+ // so the same validator reported different fields depending on which engine
1760
+ // answered.
1761
+ function _verboseWrap(inner, root) {
1762
+ const { resolvePointer } = require('./pointer.js');
1763
+ return (data) => {
1764
+ const result = inner(data);
1765
+ if (result && !result.valid && result.errors) {
1766
+ const enriched = result.errors.map((err) => {
1767
+ if (!err || err.parentSchema !== undefined) return err;
1768
+ const parentSchema = resolveSchemaByPath(root, err.schemaPath);
1769
+ // The last segment of the schema path is the keyword that
1770
+ // failed, so its value on the parent is that keyword's schema.
1771
+ const sp = typeof err.schemaPath === 'string' ? err.schemaPath : '';
1772
+ const last = sp.slice(sp.lastIndexOf('/') + 1).replace(/~1/g, '/').replace(/~0/g, '~');
1773
+ const keywordSchema = (parentSchema !== null && typeof parentSchema === 'object' && last)
1774
+ ? parentSchema[last]
1775
+ : undefined;
1776
+ return {
1777
+ ...err,
1778
+ parentSchema,
1779
+ schema: keywordSchema,
1780
+ data: resolvePointer(data, err.instancePath, undefined),
1781
+ };
1782
+ });
1783
+ return { valid: false, errors: enriched };
1784
+ }
1785
+ return result;
1786
+ };
1787
+ }
1788
+
1703
1789
  function _mustReject(r) {
1704
1790
  return r && r.valid === false ? r : _VERDICT_DISAGREES;
1705
1791
  }
@@ -2063,4 +2149,4 @@ module.exports = {
2063
2149
  // surface of `require('ata-validator')` is what it was.
2064
2150
  Object.defineProperty(module.exports, '_registerCodegen', { value: _registerCodegen, enumerable: false });
2065
2151
  // What the compiled paths in index.js need from this module.
2066
- Object.defineProperty(module.exports, '_internals', { value: { _jsonSyntaxRejection, ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindEntry, _bindVerdict, _compileCache, _mustReject, _rememberInstance, compileCacheKey, getNative, isV1Dialect, resolveSchemaByPath }, enumerable: false });
2152
+ Object.defineProperty(module.exports, '_internals', { value: { _jsonSyntaxRejection, ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindEntry, _bindVerdict, _compileCache, _mustReject, _rememberInstance, _verboseWrap, compileCacheKey, getNative, isV1Dialect, resolveSchemaByPath }, enumerable: false });
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.39.1';
10
+ module.exports = '1.39.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.39.1",
3
+ "version": "1.39.2",
4
4
  "description": "JSON Schema validator that compiles to plain JavaScript, still runs where code generation is blocked, and can be compiled away at build time. Passes the full official test suite.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -59,7 +59,7 @@
59
59
  "release:check": "node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_embed.js && node tests/test_version_sync.js",
60
60
  "build": "cmake-js build --target ata",
61
61
  "rebuild": "cmake-js rebuild --target ata",
62
- "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_text_path_errors.js && node tests/test_compiled_parity.js && node tests/test_buffer_gate.js && node tests/test_scanner_differential.js && node tests/test_validate_json_scanner_cost.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_exclusive_bounds.js && node tests/test_engine_differential.js && node tests/test_error_shape_differential.js && node tests/test_error_list_differential.js && node tests/test_output_format.js && node tests/test_standard_json_schema.js && node tests/test_retry_message.js && node tests/test_describe_schema.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.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_esm_exports.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_native_lazy.js && node tests/test_buffer_apis_without_native.js && node tests/test_cold_start_modules.js && node tests/test_safe_regex_embed.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_regex_ecma_whitespace.js && node tests/test_regex_native_linear.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_option.js && node tests/test_compile_cache_order.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_error_accumulator_collision.js && node tests/test_uri_helper_parity.js && node tests/test_uri_fast_path.js && node tests/test_extend_verdict.js && node tests/test_ipv4_regex.js && node tests/test_ipv6_fast_path.js && node tests/test_nested_recursion_decline.js && node tests/test_generator_error_content.js && node tests/test_inherited_key_presence.js && node tests/test_proto_accessor.js && node tests/test_hybrid_tier.js && node tests/test_first_rejection_builds_one.js && node tests/test_container_local.js && node tests/test_one_shot_validate.js && node tests/test_fixed_pattern_inline.js && node tests/test_plan_source.js && node tests/test_fused_remove_additional.js && node tests/test_runtime_parse.js && node tests/test_formats_single_pass.js && node tests/test_email_format_cost.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_ref_annotation_siblings.js && node tests/test_gate_wrapped_schemas.js && node tests/test_pattern_message_escape.js && node tests/test_nested_defaults_engines.js && node tests/test_nested_coercion_parity.js && node tests/test_preprocess_cache.js && node tests/test_large_schema_codegen.js && node tests/test_base_uri.js && node tests/test_hoisted_def_recursion.js && node tests/test_alias_definitions.js && node tests/test_property_names_ref.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_deferred_def_scope.js && node tests/test_node_scoped_ctx_flags.js && node tests/test_fast_slot_exhaustion.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_strict_schema.js && node tests/test_aot_external_checks.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_unevaluated_error_shape.js && node tests/test_unevaluated_differential.js && node tests/fuzz_differential.js && node tests/test_pattern_properties_errors.js && node tests/test_additional_properties_scaling.js && node tests/test_additional_props_combined_cost.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_ref_error_order.js && node tests/test_rejection_shape.js && node tests/test_lazy_normalization.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_single_pass_errors.js && node tests/test_lazy_json_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_lite_parity.js && node tests/test_lite_bundle_size.js && node tests/test_defaults_own_keys.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_malformed_json_termination.js && node tests/test_data_positions.js && node tests/test_targeted_positions.js && node tests/test_position_map_cost.js && node tests/test_targeted_positions_cost.js && node tests/test_aot_staleness.js && node tests/test_file_positions.js && node tests/test_aot_positions_cost.js && node tests/test_schema_hash.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 tests/test_collect_evaluated.js && node tests/test_differential_tiers.js && node tests/test_draft7.js && node tests/test_dual_path.js && node tests/test_keywords_codegen.js && node tests/test_ref_cross.js && node tests/test_shape_classifier.js && node tests/test_tier0.js && node benchmark/bench_aot_size.mjs",
62
+ "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_text_path_errors.js && node tests/test_compiled_parity.js && node tests/test_buffer_gate.js && node tests/test_scanner_differential.js && node tests/test_validate_json_scanner_cost.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_exclusive_bounds.js && node tests/test_engine_differential.js && node tests/test_error_shape_differential.js && node tests/test_error_list_differential.js && node tests/test_output_format.js && node tests/test_standard_json_schema.js && node tests/test_retry_message.js && node tests/test_describe_schema.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.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_esm_exports.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_native_lazy.js && node tests/test_buffer_apis_without_native.js && node tests/test_cold_start_modules.js && node tests/test_safe_regex_embed.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_regex_ecma_whitespace.js && node tests/test_regex_native_linear.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_option.js && node tests/test_compile_cache_order.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_error_accumulator_collision.js && node tests/test_uri_helper_parity.js && node tests/test_uri_fast_path.js && node tests/test_extend_verdict.js && node tests/test_ipv4_regex.js && node tests/test_ipv6_fast_path.js && node tests/test_nested_recursion_decline.js && node tests/test_generator_error_content.js && node tests/test_inherited_key_presence.js && node tests/test_proto_accessor.js && node tests/test_hybrid_tier.js && node tests/test_first_rejection_builds_one.js && node tests/test_container_local.js && node tests/test_one_shot_validate.js && node tests/test_fixed_pattern_inline.js && node tests/test_plan_source.js && node tests/test_fused_remove_additional.js && node tests/test_runtime_parse.js && node tests/test_formats_single_pass.js && node tests/test_email_format_cost.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_ref_annotation_siblings.js && node tests/test_gate_wrapped_schemas.js && node tests/test_pattern_message_escape.js && node tests/test_nested_defaults_engines.js && node tests/test_nested_coercion_parity.js && node tests/test_preprocess_cache.js && node tests/test_large_schema_codegen.js && node tests/test_base_uri.js && node tests/test_hoisted_def_recursion.js && node tests/test_alias_definitions.js && node tests/test_property_names_ref.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_deferred_def_scope.js && node tests/test_node_scoped_ctx_flags.js && node tests/test_fast_slot_exhaustion.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_strict_schema.js && node tests/test_aot_external_checks.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_unevaluated_error_shape.js && node tests/test_unevaluated_differential.js && node tests/fuzz_differential.js && node tests/test_pattern_properties_errors.js && node tests/test_additional_properties_scaling.js && node tests/test_additional_props_combined_cost.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_ref_error_order.js && node tests/test_rejection_shape.js && node tests/test_lazy_normalization.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_single_pass_errors.js && node tests/test_lazy_json_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_lite_parity.js && node tests/test_lite_bundle_size.js && node tests/test_defaults_own_keys.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_malformed_json_termination.js && node tests/test_data_positions.js && node tests/test_targeted_positions.js && node tests/test_position_map_cost.js && node tests/test_targeted_positions_cost.js && node tests/test_aot_staleness.js && node tests/test_file_positions.js && node tests/test_aot_positions_cost.js && node tests/test_schema_hash.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_aot_source_frames.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 tests/test_collect_evaluated.js && node tests/test_differential_tiers.js && node tests/test_draft7.js && node tests/test_dual_path.js && node tests/test_keywords_codegen.js && node tests/test_ref_cross.js && node tests/test_shape_classifier.js && node tests/test_tier0.js && node benchmark/bench_aot_size.mjs",
63
63
  "bench:size": "node benchmark/bench_aot_size.mjs",
64
64
  "test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
65
65
  "test:compat": "node tests/test_compat.js",
@@ -135,13 +135,13 @@
135
135
  "LICENSE"
136
136
  ],
137
137
  "optionalDependencies": {
138
- "@ata-validator/native-darwin-arm64": "1.39.1",
139
- "@ata-validator/native-darwin-x64": "1.39.1",
140
- "@ata-validator/native-linux-arm64-gnu": "1.39.1",
141
- "@ata-validator/native-linux-arm64-musl": "1.39.1",
142
- "@ata-validator/native-linux-x64-gnu": "1.39.1",
143
- "@ata-validator/native-linux-x64-musl": "1.39.1",
144
- "@ata-validator/native-win32-x64": "1.39.1"
138
+ "@ata-validator/native-darwin-arm64": "1.39.2",
139
+ "@ata-validator/native-darwin-x64": "1.39.2",
140
+ "@ata-validator/native-linux-arm64-gnu": "1.39.2",
141
+ "@ata-validator/native-linux-arm64-musl": "1.39.2",
142
+ "@ata-validator/native-linux-x64-gnu": "1.39.2",
143
+ "@ata-validator/native-linux-x64-musl": "1.39.2",
144
+ "@ata-validator/native-win32-x64": "1.39.2"
145
145
  },
146
146
  "peerDependencies": {
147
147
  "yaml": "^2.0.0"