ata-validator 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
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.2.0 - 2026-07-18
6
+
7
+ ### Added
8
+
9
+ - An interpreted engine (`lib/interpreter.js`) now backs schemas the JS compiler cannot represent when the native addon is absent (browser, edge workers, `ATA_NO_NATIVE=1`). These schemas validated only with the native engine before, and 1.1.0 made them throw a clear error in native-less environments; they now just validate. Full draft 2020-12 semantics: `$id`/`$anchor` resolution, `$dynamicRef` dynamic scoping, annotation tracking for `unevaluatedProperties`/`unevaluatedItems`. The pure-JS configuration now passes 1184 of 1190 applicable cases (99.5%) in the official test suite, up from 974, and the six remaining failures are shared with the native engine. Error results on the native-less path also carry full per-keyword detail now instead of a single generic message.
10
+
11
+ ### Changed
12
+
13
+ - Validation errors now follow the schema's keyword declaration order instead of a fixed required-first order: a schema declaring `properties` before `required` reports the property errors first, matching what schema authors read top to bottom and what the previous default validator emitted. Order within one keyword is unchanged (`required` errors still follow the array). Single-error and `abortEarly` results are untouched. With this change ata passes every applicable test in Fastify's validation suite (181 of 187; the remaining 6 test the incumbent validator's own extension API rather than validation behavior).
14
+
5
15
  ## 1.1.0 - 2026-07-18
6
16
 
7
17
  ### Added
package/README.md CHANGED
@@ -20,7 +20,7 @@ The `ata-validator` package itself is pure JavaScript. The native accelerator (s
20
20
  npm install ata-validator --omit=optional
21
21
  ```
22
22
 
23
- or set `ATA_NO_NATIVE=1` at runtime. Validation behavior is identical for the schema shapes the JS engine compiles, which covers typical API payload schemas. The buffer and parallel APIs, plus a small set of complex shapes (some `$dynamicRef`, cyclic `$ref`, and unusual keyword interactions), currently need the native engine and throw a clear error without it rather than validating incorrectly.
23
+ or set `ATA_NO_NATIVE=1` at runtime. Typical schemas compile to specialized JS; shapes the compiler cannot represent (some `$dynamicRef`, cyclic `$ref`, unusual keyword interactions) fall back to an interpreted engine, so every schema validates in every environment. The pure-JS setup passes 99.5% of the official draft 2020-12 test suite. Only the buffer and parallel APIs (`isValid` on raw buffers, `countValid`, `batchIsValid`, `validateAndParse`) need the native engine and say so with a clear error.
24
24
 
25
25
  In your code:
26
26
 
package/index.js CHANGED
@@ -319,6 +319,47 @@ function resolveSchemaByPath(rootSchema, schemaPath) {
319
319
  return target;
320
320
  }
321
321
 
322
+ // Rank an error by walking its schemaPath through the schema object: at each
323
+ // level the segment's index among the node's declared keys. Comparing ranks
324
+ // lexicographically orders errors by keyword declaration order, which is the
325
+ // order AJV emits and what schema authors read top to bottom. Segments that
326
+ // cannot be resolved (cross-schema refs, normalized keys) end the walk; the
327
+ // stable sort then keeps such errors in engine emission order.
328
+ function schemaOrderRank(rootSchema, schemaPath) {
329
+ if (!schemaPath || typeof schemaPath !== 'string' || !schemaPath.startsWith('#')) return null;
330
+ const parts = schemaPath.slice(1).split('/').filter(Boolean).map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
331
+ const rank = [];
332
+ let node = rootSchema;
333
+ for (const seg of parts) {
334
+ if (node == null || typeof node !== 'object') break;
335
+ if (Array.isArray(node)) {
336
+ const idx = Number(seg);
337
+ if (!Number.isInteger(idx) || idx < 0 || idx >= node.length) break;
338
+ rank.push(idx);
339
+ node = node[idx];
340
+ } else {
341
+ const idx = Object.keys(node).indexOf(seg);
342
+ if (idx < 0) break;
343
+ rank.push(idx);
344
+ node = node[seg];
345
+ }
346
+ }
347
+ return rank;
348
+ }
349
+
350
+ function sortErrorsBySchemaOrder(rootSchema, errors) {
351
+ const ranked = errors.map((e, i) => ({ e, i, rank: schemaOrderRank(rootSchema, e.schemaPath) }));
352
+ ranked.sort((a, b) => {
353
+ if (!a.rank || !b.rank) return a.i - b.i;
354
+ const n = Math.min(a.rank.length, b.rank.length);
355
+ for (let k = 0; k < n; k++) {
356
+ if (a.rank[k] !== b.rank[k]) return a.rank[k] - b.rank[k];
357
+ }
358
+ return a.i - b.i;
359
+ });
360
+ return ranked.map((r) => r.e);
361
+ }
362
+
322
363
  function parsePointerPath(path) {
323
364
  if (!path) return [];
324
365
  return path
@@ -725,16 +766,32 @@ class Validator {
725
766
  // invalid path doesn't dereference a null _compiled.
726
767
  const hasUnevaluated = schemaObj && (schemaObj.unevaluatedProperties !== undefined || schemaObj.unevaluatedItems !== undefined || this._schemaStr.includes('unevaluatedProperties') || this._schemaStr.includes('unevaluatedItems'))
727
768
  const hasDynRef = this._schemaStr.includes('"$dynamicRef"') || this._schemaStr.includes('"$dynamicAnchor"')
728
- const jsOnlyFallback = (d) => ({
729
- valid: jsFn(d),
730
- errors: jsFn(d) ? [] : [{
731
- keyword: 'validation',
732
- instancePath: '',
733
- schemaPath: '',
734
- params: {},
735
- message: 'schema validation failed (detailed errors unavailable without native addon)'
736
- }]
737
- });
769
+ // Native-less error path: the interpreted engine re-validates failing
770
+ // data to produce full errors. If it disagrees with the codegen verdict
771
+ // (it should not), a generic error keeps the result consistent.
772
+ let _interp = null;
773
+ const jsOnlyFallback = (d) => {
774
+ if (jsFn(d)) return { valid: true, data: d, errors: [] };
775
+ if (!_interp) {
776
+ const { createInterpreter } = require('./lib/interpreter');
777
+ _interp = createInterpreter(schemaObj, {
778
+ schemaMap: this._schemaMap.size > 0 ? this._schemaMap : null,
779
+ formats: this._userFormats,
780
+ });
781
+ }
782
+ const r = _interp.validate(d);
783
+ if (!r.valid) return r;
784
+ return {
785
+ valid: false,
786
+ errors: [{
787
+ keyword: 'validation',
788
+ instancePath: '',
789
+ schemaPath: '',
790
+ params: {},
791
+ message: 'schema validation failed'
792
+ }]
793
+ };
794
+ };
738
795
  const errFn =
739
796
  safeErrFn ||
740
797
  (hasUnevaluated
@@ -1009,17 +1066,42 @@ class Validator {
1009
1066
  };
1010
1067
  }
1011
1068
  } else {
1012
- // No JS codegen and no native engine. Leaving the lazy stubs in place
1013
- // makes them re-dispatch to themselves forever, so bind a clear error.
1014
- const unsupported = () => {
1015
- throw new Error(
1016
- 'This schema is not supported by the pure-JS engine and the native addon is unavailable in this environment'
1017
- );
1069
+ // No JS codegen and no native engine: fall back to the interpreted
1070
+ // engine. Slow but correct, and strictly better than the previous
1071
+ // behavior (the lazy stubs re-dispatched to themselves forever).
1072
+ const { createInterpreter } = require('./lib/interpreter');
1073
+ const interp = createInterpreter(schemaObj, {
1074
+ schemaMap: this._schemaMap.size > 0 ? this._schemaMap : null,
1075
+ formats: this._userFormats,
1076
+ });
1077
+ const run = preprocess
1078
+ ? (data) => { preprocess(data); return interp.validate(data); }
1079
+ : (data) => interp.validate(data);
1080
+ this.validate = run;
1081
+ this.isValidObject = (data) => run(data).valid;
1082
+ this.validateJSON = (jsonStr) => {
1083
+ try {
1084
+ return run(JSON.parse(jsonStr));
1085
+ } catch (e) {
1086
+ return { valid: false, errors: [{ keyword: 'syntax', instancePath: '', schemaPath: '#', params: {}, message: e.message }] };
1087
+ }
1088
+ };
1089
+ this.isValidJSON = (jsonStr) => this.validateJSON(jsonStr).valid;
1090
+ }
1091
+
1092
+ // Declaration-order errors: whichever engine produced them, multi-error
1093
+ // results are sorted by the schema's keyword declaration order before
1094
+ // enrichment. Single-error and abortEarly results pass through untouched.
1095
+ if (this.validate) {
1096
+ const inner = this.validate;
1097
+ const root = this._schemaObj;
1098
+ this.validate = (data) => {
1099
+ const result = inner(data);
1100
+ if (result && !result.valid && result.errors && result.errors.length > 1 && result !== ABORT_EARLY_RESULT) {
1101
+ return { valid: false, errors: sortErrorsBySchemaOrder(root, result.errors) };
1102
+ }
1103
+ return result;
1018
1104
  };
1019
- this.validate = unsupported;
1020
- this.isValidObject = unsupported;
1021
- this.validateJSON = unsupported;
1022
- this.isValidJSON = unsupported;
1023
1105
  }
1024
1106
 
1025
1107
  // richErrors enrichment: layered on top of whichever validate path was
@@ -0,0 +1,617 @@
1
+ 'use strict';
2
+
3
+ // Interpreted draft 2020-12 engine. This is the correctness fallback for
4
+ // schemas the JS codegen cannot represent when the native engine is absent
5
+ // (browser, edge workers, ATA_NO_NATIVE). It walks the schema directly with
6
+ // full annotation tracking (unevaluatedProperties/Items), $id/$anchor
7
+ // resolution, and $dynamicRef dynamic-scope semantics. It optimizes for
8
+ // correctness and clarity, not speed: hot paths belong to codegen and the
9
+ // native engine.
10
+
11
+ const { compileSafe } = require('./safe-regex');
12
+
13
+ const SCHEMA_KEYWORDS = {
14
+ single: ['additionalProperties', 'contains', 'propertyNames', 'if', 'then', 'else', 'not', 'items', 'unevaluatedItems', 'unevaluatedProperties'],
15
+ maps: ['$defs', 'definitions', 'properties', 'patternProperties', 'dependentSchemas'],
16
+ lists: ['allOf', 'anyOf', 'oneOf', 'prefixItems'],
17
+ };
18
+
19
+ const FALLBACK_BASE = 'ata://root';
20
+
21
+ function resolveUri(base, ref) {
22
+ try {
23
+ return new URL(ref, base || FALLBACK_BASE).href;
24
+ } catch {
25
+ return ref;
26
+ }
27
+ }
28
+
29
+ function splitFragment(uri) {
30
+ const hash = uri.indexOf('#');
31
+ if (hash < 0) return [uri, ''];
32
+ return [uri.slice(0, hash), decodeURIComponent(uri.slice(hash + 1))];
33
+ }
34
+
35
+ // ---------- resource index ----------
36
+
37
+ function indexSchemas(rootSchema, schemaMap) {
38
+ const state = {
39
+ resources: new Map(), // baseUri -> schema node of the resource root
40
+ anchors: new Map(), // baseUri -> Map(anchorName -> node)
41
+ dynamicAnchors: new Map(), // baseUri -> Map(anchorName -> node)
42
+ nodeBase: new Map(), // schema node -> baseUri of its enclosing resource
43
+ rootBase: FALLBACK_BASE,
44
+ };
45
+
46
+ const rootBase =
47
+ typeof rootSchema === 'object' && rootSchema !== null && typeof rootSchema.$id === 'string'
48
+ ? resolveUri(FALLBACK_BASE, splitFragment(rootSchema.$id)[0])
49
+ : FALLBACK_BASE;
50
+ state.rootBase = rootBase;
51
+ indexResource(rootSchema, rootBase, state);
52
+
53
+ if (schemaMap) {
54
+ for (const [id, schema] of schemaMap) {
55
+ const base = resolveUri(FALLBACK_BASE, splitFragment(id)[0]);
56
+ if (!state.resources.has(base)) indexResource(schema, base, state);
57
+ // Also register under the raw key so non-URI ids stay addressable.
58
+ if (!state.resources.has(id)) state.resources.set(id, schema);
59
+ }
60
+ }
61
+ return state;
62
+ }
63
+
64
+ function indexResource(node, baseUri, state) {
65
+ if (typeof node !== 'object' || node === null) return;
66
+ if (!state.resources.has(baseUri)) state.resources.set(baseUri, node);
67
+ if (!state.anchors.has(baseUri)) state.anchors.set(baseUri, new Map());
68
+ if (!state.dynamicAnchors.has(baseUri)) state.dynamicAnchors.set(baseUri, new Map());
69
+ walkSchema(node, baseUri, state, true);
70
+ }
71
+
72
+ function walkSchema(node, baseUri, state, isResourceRoot) {
73
+ if (typeof node !== 'object' || node === null) return;
74
+ if (state.nodeBase.has(node)) return; // cycle guard
75
+ state.nodeBase.set(node, baseUri);
76
+
77
+ if (!isResourceRoot && typeof node.$id === 'string') {
78
+ // Embedded resource: new base URI, indexed as its own resource.
79
+ const newBase = splitFragment(resolveUri(baseUri, node.$id))[0];
80
+ state.nodeBase.delete(node); // re-register under the new base
81
+ indexResource(node, newBase, state);
82
+ return;
83
+ }
84
+
85
+ if (typeof node.$anchor === 'string') state.anchors.get(baseUri).set(node.$anchor, node);
86
+ if (typeof node.$dynamicAnchor === 'string') {
87
+ state.dynamicAnchors.get(baseUri).set(node.$dynamicAnchor, node);
88
+ // A $dynamicAnchor is also addressable as a plain anchor.
89
+ state.anchors.get(baseUri).set(node.$dynamicAnchor, node);
90
+ }
91
+
92
+ for (const kw of SCHEMA_KEYWORDS.single) {
93
+ if (node[kw] !== undefined) walkSchema(node[kw], baseUri, state, false);
94
+ }
95
+ for (const kw of SCHEMA_KEYWORDS.maps) {
96
+ const map = node[kw];
97
+ if (map && typeof map === 'object' && !Array.isArray(map)) {
98
+ for (const key of Object.keys(map)) walkSchema(map[key], baseUri, state, false);
99
+ }
100
+ }
101
+ for (const kw of SCHEMA_KEYWORDS.lists) {
102
+ const list = node[kw];
103
+ if (Array.isArray(list)) for (const sub of list) walkSchema(sub, baseUri, state, false);
104
+ }
105
+ }
106
+
107
+ // ---------- ref resolution ----------
108
+
109
+ function walkPointer(root, pointer) {
110
+ if (pointer === '' || pointer === '/') return root;
111
+ const parts = pointer
112
+ .split('/')
113
+ .slice(1)
114
+ .map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
115
+ let node = root;
116
+ for (const part of parts) {
117
+ if (node === null || typeof node !== 'object') return undefined;
118
+ if (Array.isArray(node)) {
119
+ node = node[Number(part)];
120
+ } else if (part in node) {
121
+ node = node[part];
122
+ } else if (part === 'definitions' && node.$defs) {
123
+ // draft-07 normalization renames definitions to $defs; keep old
124
+ // pointers working (mirrors the 1.0.2 alias fix).
125
+ node = node.$defs;
126
+ } else if (part === '$defs' && node.definitions) {
127
+ node = node.definitions;
128
+ } else {
129
+ return undefined;
130
+ }
131
+ }
132
+ return node;
133
+ }
134
+
135
+ function resolveRef(ref, fromBase, state) {
136
+ const abs = resolveUri(fromBase, ref);
137
+ const [uri, fragment] = splitFragment(abs);
138
+ let resource = state.resources.get(uri);
139
+ let resourceBase = uri;
140
+ if (resource === undefined && (uri === FALLBACK_BASE || uri === '')) {
141
+ resource = state.resources.get(state.rootBase);
142
+ resourceBase = state.rootBase;
143
+ }
144
+ if (resource === undefined) {
145
+ // Raw-id registration (schemaMap keys that are not URIs).
146
+ const [rawUri, rawFragment] = splitFragment(ref);
147
+ if (state.resources.has(rawUri)) {
148
+ resource = state.resources.get(rawUri);
149
+ resourceBase = rawUri;
150
+ if (rawFragment === '') return { node: resource, base: resourceBase };
151
+ if (rawFragment.startsWith('/')) return { node: walkPointer(resource, rawFragment), base: resourceBase };
152
+ const anchored = state.anchors.get(resourceBase);
153
+ return { node: anchored ? anchored.get(rawFragment) : undefined, base: resourceBase };
154
+ }
155
+ return { node: undefined, base: resourceBase };
156
+ }
157
+ if (fragment === '') return { node: resource, base: resourceBase };
158
+ if (fragment.startsWith('/')) {
159
+ const node = walkPointer(resource, fragment);
160
+ // A pointer can land inside an embedded resource; its recorded base wins.
161
+ const base = node !== null && typeof node === 'object' && state.nodeBase.has(node) ? state.nodeBase.get(node) : resourceBase;
162
+ return { node, base };
163
+ }
164
+ const anchored = state.anchors.get(resourceBase);
165
+ return { node: anchored ? anchored.get(fragment) : undefined, base: resourceBase };
166
+ }
167
+
168
+ // ---------- value helpers ----------
169
+
170
+ function deepEqual(a, b) {
171
+ if (a === b) return true;
172
+ if (typeof a === 'number' && typeof b === 'number') return a === b;
173
+ if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;
174
+ const aArr = Array.isArray(a);
175
+ if (aArr !== Array.isArray(b)) return false;
176
+ if (aArr) {
177
+ if (a.length !== b.length) return false;
178
+ for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false;
179
+ return true;
180
+ }
181
+ const ak = Object.keys(a);
182
+ const bk = Object.keys(b);
183
+ if (ak.length !== bk.length) return false;
184
+ for (const k of ak) {
185
+ if (!(k in b) || !deepEqual(a[k], b[k])) return false;
186
+ }
187
+ return true;
188
+ }
189
+
190
+ function codePointLength(s) {
191
+ let n = 0;
192
+ for (let i = 0; i < s.length; i++) {
193
+ const c = s.charCodeAt(i);
194
+ if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) i++;
195
+ n++;
196
+ }
197
+ return n;
198
+ }
199
+
200
+ function typeMatches(type, d) {
201
+ switch (type) {
202
+ case 'string': return typeof d === 'string';
203
+ case 'number': return typeof d === 'number' && isFinite(d);
204
+ case 'integer': return typeof d === 'number' && Number.isInteger(d);
205
+ case 'boolean': return typeof d === 'boolean';
206
+ case 'null': return d === null;
207
+ case 'array': return Array.isArray(d);
208
+ case 'object': return typeof d === 'object' && d !== null && !Array.isArray(d);
209
+ default: return true;
210
+ }
211
+ }
212
+
213
+ function multipleOfOk(d, m) {
214
+ if (m === 0) return false;
215
+ const q = d / m;
216
+ if (Number.isInteger(q)) return true;
217
+ // Float remainder tolerance (0.0075 % 0.0001 style cases)
218
+ return Math.abs(q - Math.round(q)) < 1e-9;
219
+ }
220
+
221
+ const FORMAT_CHECKS = {
222
+ email: (s) => { const at = s.indexOf('@'); return at > 0 && at < s.length - 1 && s.indexOf('.', at) > at + 1; },
223
+ date: (s) => { if (s.length !== 10 || !/^\d{4}-\d{2}-\d{2}$/.test(s)) return false; const m = +s.slice(5, 7), d = +s.slice(8, 10); return m >= 1 && m <= 12 && d >= 1 && d <= 31; },
224
+ 'date-time': (s) => /^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$/.test(s) && !isNaN(Date.parse(s)),
225
+ time: (s) => /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?([Zz]|[+-]\d{2}:\d{2})?$/.test(s),
226
+ duration: (s) => /^P(?:\d+Y)?(?:\d+M)?(?:\d+W)?(?:\d+D)?(?:T(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?)?$/.test(s) && s !== 'P' && !s.endsWith('T'),
227
+ uuid: (s) => s.length === 36 && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s),
228
+ uri: (s) => /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(s),
229
+ 'uri-reference': (s) => s === '' || !/\s/.test(s),
230
+ ipv4: (s) => { const p = s.split('.'); return p.length === 4 && p.every((n) => { const v = +n; return n !== '' && v >= 0 && v <= 255 && String(v) === n; }); },
231
+ ipv6: (s) => s !== '' && /^[0-9a-fA-F:.]+$/.test(s) && s.split(':').length >= 3 && s.split(':').length <= 8,
232
+ hostname: (s) => s.length > 0 && s.length <= 253 && /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(s),
233
+ regex: (s) => { try { new RegExp(s, 'u'); return true; } catch { try { new RegExp(s); return true; } catch { return false; } } },
234
+ 'json-pointer': (s) => s === '' || (/^\//.test(s) && !/~(?![01])/.test(s)),
235
+ };
236
+
237
+ // ---------- evaluation ----------
238
+
239
+ const NO_ANNOTATIONS = { props: null, items: null };
240
+
241
+ function mergeAnnotations(target, from) {
242
+ if (from.props && from.props.size) {
243
+ if (!target.props) target.props = new Set();
244
+ for (const p of from.props) target.props.add(p);
245
+ }
246
+ if (from.items && from.items.size) {
247
+ if (!target.items) target.items = new Set();
248
+ for (const i of from.items) target.items.add(i);
249
+ }
250
+ }
251
+
252
+ class Interpreter {
253
+ constructor(rootSchema, options) {
254
+ const opts = options || {};
255
+ this.root = rootSchema;
256
+ this.state = indexSchemas(rootSchema, opts.schemaMap);
257
+ this.userFormats = opts.formats || null;
258
+ this.patternCache = new Map();
259
+ }
260
+
261
+ pattern(src) {
262
+ let re = this.patternCache.get(src);
263
+ if (!re) {
264
+ if (/\\[pP]\{/.test(src)) {
265
+ // Unicode property escapes need a real RegExp in unicode mode; the
266
+ // linear engine does not understand them.
267
+ try { re = new RegExp(src, 'u'); } catch { re = new RegExp(src); }
268
+ } else {
269
+ try {
270
+ re = compileSafe(src);
271
+ } catch {
272
+ try { re = new RegExp(src, 'u'); } catch { re = new RegExp(src); }
273
+ }
274
+ }
275
+ this.patternCache.set(src, re);
276
+ }
277
+ return re;
278
+ }
279
+
280
+ validate(data) {
281
+ const errors = [];
282
+ const dynScope = [this.state.rootBase];
283
+ const valid = this.eval(this.root, data, this.state.rootBase, dynScope, errors, '', '#', [], NO_ANNOTATIONS_SINK());
284
+ return valid ? { valid: true, data, errors: [] } : { valid: false, errors };
285
+ }
286
+
287
+ // Evaluates `schema` against `data`. Collected annotations for data are
288
+ // written into `sink` ({props, items}); errors append to `errors`.
289
+ // `stack` breaks infinite $ref recursion on identical (schema, data) pairs.
290
+ eval(schema, data, base, dynScope, errors, instancePath, schemaPath, stack, sink) {
291
+ if (schema === true) return true;
292
+ if (schema === false) {
293
+ errors.push(err('false schema', 'not', instancePath, schemaPath, {}, 'boolean schema is false'));
294
+ return false;
295
+ }
296
+ if (typeof schema !== 'object' || schema === null) return true;
297
+
298
+ for (const frame of stack) {
299
+ if (frame[0] === schema && frame[1] === data) return true; // fixed point
300
+ }
301
+ stack = stack.concat([[schema, data]]);
302
+
303
+ const nodeBase = this.state.nodeBase.get(schema);
304
+ if (nodeBase !== undefined && nodeBase !== base) base = nodeBase;
305
+ // Entering a schema resource extends the dynamic scope.
306
+ if (this.state.resources.get(base) === schema && dynScope[dynScope.length - 1] !== base) {
307
+ dynScope = dynScope.concat(base);
308
+ }
309
+
310
+ let valid = true;
311
+ const local = { props: null, items: null };
312
+
313
+ // $ref / $dynamicRef: in-place applicators, annotations flow through
314
+ if (typeof schema.$ref === 'string') {
315
+ const { node, base: refBase } = resolveRef(schema.$ref, base, this.state);
316
+ if (node === undefined) {
317
+ errors.push(err('$ref', '$ref', instancePath, schemaPath + '/$ref', { ref: schema.$ref }, `cannot resolve $ref ${schema.$ref}`));
318
+ valid = false;
319
+ } else {
320
+ const sub = { props: null, items: null };
321
+ if (!this.eval(node, data, refBase, dynScope, errors, instancePath, schemaPath + '/$ref', stack, sub)) valid = false;
322
+ else mergeAnnotations(local, sub);
323
+ }
324
+ }
325
+
326
+ if (typeof schema.$dynamicRef === 'string') {
327
+ const ref = schema.$dynamicRef;
328
+ let { node, base: refBase } = resolveRef(ref, base, this.state);
329
+ const [, fragment] = splitFragment(resolveUri(base, ref));
330
+ if (node !== undefined && fragment && !fragment.startsWith('/')) {
331
+ // Initial target must itself carry the matching $dynamicAnchor for
332
+ // dynamic resolution; otherwise it behaves like $ref.
333
+ const initialDyn = this.state.dynamicAnchors.get(refBase);
334
+ if (initialDyn && initialDyn.get(fragment) === node) {
335
+ for (const scopeBase of dynScope) {
336
+ const dyn = this.state.dynamicAnchors.get(scopeBase);
337
+ if (dyn && dyn.has(fragment)) {
338
+ node = dyn.get(fragment);
339
+ refBase = scopeBase;
340
+ break;
341
+ }
342
+ }
343
+ }
344
+ }
345
+ if (node === undefined) {
346
+ errors.push(err('$dynamicRef', '$dynamicRef', instancePath, schemaPath + '/$dynamicRef', { ref }, `cannot resolve $dynamicRef ${ref}`));
347
+ valid = false;
348
+ } else {
349
+ const sub = { props: null, items: null };
350
+ if (!this.eval(node, data, refBase, dynScope, errors, instancePath, schemaPath + '/$dynamicRef', stack, sub)) valid = false;
351
+ else mergeAnnotations(local, sub);
352
+ }
353
+ }
354
+
355
+ // type / enum / const
356
+ if (schema.type !== undefined) {
357
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type];
358
+ if (!types.some((t) => typeMatches(t, data))) {
359
+ errors.push(err('type', 'type', instancePath, schemaPath + '/type', { type: schema.type }, `must be ${types.join(' or ')}`));
360
+ valid = false;
361
+ }
362
+ }
363
+ if (schema.enum !== undefined) {
364
+ if (!schema.enum.some((v) => deepEqual(v, data))) {
365
+ errors.push(err('enum', 'enum', instancePath, schemaPath + '/enum', { allowedValues: schema.enum }, 'must be equal to one of the allowed values'));
366
+ valid = false;
367
+ }
368
+ }
369
+ if (schema.const !== undefined) {
370
+ if (!deepEqual(schema.const, data)) {
371
+ errors.push(err('const', 'const', instancePath, schemaPath + '/const', { allowedValue: schema.const }, 'must be equal to constant'));
372
+ valid = false;
373
+ }
374
+ }
375
+
376
+ // numbers
377
+ if (typeof data === 'number') {
378
+ if (typeof schema.minimum === 'number' && !(data >= schema.minimum)) { errors.push(err('minimum', 'minimum', instancePath, schemaPath + '/minimum', { comparison: '>=', limit: schema.minimum }, `must be >= ${schema.minimum}`)); valid = false; }
379
+ if (typeof schema.maximum === 'number' && !(data <= schema.maximum)) { errors.push(err('maximum', 'maximum', instancePath, schemaPath + '/maximum', { comparison: '<=', limit: schema.maximum }, `must be <= ${schema.maximum}`)); valid = false; }
380
+ if (typeof schema.exclusiveMinimum === 'number' && !(data > schema.exclusiveMinimum)) { errors.push(err('exclusiveMinimum', 'exclusiveMinimum', instancePath, schemaPath + '/exclusiveMinimum', { comparison: '>', limit: schema.exclusiveMinimum }, `must be > ${schema.exclusiveMinimum}`)); valid = false; }
381
+ if (typeof schema.exclusiveMaximum === 'number' && !(data < schema.exclusiveMaximum)) { errors.push(err('exclusiveMaximum', 'exclusiveMaximum', instancePath, schemaPath + '/exclusiveMaximum', { comparison: '<', limit: schema.exclusiveMaximum }, `must be < ${schema.exclusiveMaximum}`)); valid = false; }
382
+ if (typeof schema.multipleOf === 'number' && !multipleOfOk(data, schema.multipleOf)) { errors.push(err('multipleOf', 'multipleOf', instancePath, schemaPath + '/multipleOf', { multipleOf: schema.multipleOf }, `must be multiple of ${schema.multipleOf}`)); valid = false; }
383
+ }
384
+
385
+ // strings
386
+ if (typeof data === 'string') {
387
+ if (schema.minLength !== undefined && codePointLength(data) < schema.minLength) { errors.push(err('minLength', 'minLength', instancePath, schemaPath + '/minLength', { limit: schema.minLength }, `must NOT have fewer than ${schema.minLength} characters`)); valid = false; }
388
+ if (schema.maxLength !== undefined && codePointLength(data) > schema.maxLength) { errors.push(err('maxLength', 'maxLength', instancePath, schemaPath + '/maxLength', { limit: schema.maxLength }, `must NOT have more than ${schema.maxLength} characters`)); valid = false; }
389
+ if (schema.pattern !== undefined && !this.pattern(schema.pattern).test(data)) { errors.push(err('pattern', 'pattern', instancePath, schemaPath + '/pattern', { pattern: schema.pattern }, `must match pattern "${schema.pattern}"`)); valid = false; }
390
+ if (schema.format !== undefined) {
391
+ const fc = (this.userFormats && typeof this.userFormats[schema.format] === 'function') ? this.userFormats[schema.format] : FORMAT_CHECKS[schema.format];
392
+ if (fc && !fc(data)) { errors.push(err('format', 'format', instancePath, schemaPath + '/format', { format: schema.format }, `must match format "${schema.format}"`)); valid = false; }
393
+ }
394
+ }
395
+
396
+ // arrays
397
+ if (Array.isArray(data)) {
398
+ if (schema.minItems !== undefined && data.length < schema.minItems) { errors.push(err('minItems', 'minItems', instancePath, schemaPath + '/minItems', { limit: schema.minItems }, `must NOT have fewer than ${schema.minItems} items`)); valid = false; }
399
+ if (schema.maxItems !== undefined && data.length > schema.maxItems) { errors.push(err('maxItems', 'maxItems', instancePath, schemaPath + '/maxItems', { limit: schema.maxItems }, `must NOT have more than ${schema.maxItems} items`)); valid = false; }
400
+ if (schema.uniqueItems === true) {
401
+ outer: for (let i = 0; i < data.length; i++) {
402
+ for (let j = i + 1; j < data.length; j++) {
403
+ if (deepEqual(data[i], data[j])) {
404
+ errors.push(err('uniqueItems', 'uniqueItems', instancePath, schemaPath + '/uniqueItems', { i, j }, 'must NOT have duplicate items'));
405
+ valid = false;
406
+ break outer;
407
+ }
408
+ }
409
+ }
410
+ }
411
+ const prefix = Array.isArray(schema.prefixItems) ? schema.prefixItems : null;
412
+ if (prefix) {
413
+ for (let i = 0; i < Math.min(prefix.length, data.length); i++) {
414
+ const sub = { props: null, items: null };
415
+ if (!this.eval(prefix[i], data[i], base, dynScope, errors, instancePath + '/' + i, schemaPath + '/prefixItems/' + i, stack, sub)) valid = false;
416
+ if (!local.items) local.items = new Set();
417
+ local.items.add(i);
418
+ }
419
+ }
420
+ if (schema.items !== undefined) {
421
+ const start = prefix ? prefix.length : 0;
422
+ for (let i = start; i < data.length; i++) {
423
+ const sub = { props: null, items: null };
424
+ if (!this.eval(schema.items, data[i], base, dynScope, errors, instancePath + '/' + i, schemaPath + '/items', stack, sub)) valid = false;
425
+ if (!local.items) local.items = new Set();
426
+ local.items.add(i);
427
+ }
428
+ }
429
+ if (schema.contains !== undefined) {
430
+ const matched = [];
431
+ for (let i = 0; i < data.length; i++) {
432
+ const scratch = [];
433
+ const sub = { props: null, items: null };
434
+ if (this.eval(schema.contains, data[i], base, dynScope, scratch, instancePath + '/' + i, schemaPath + '/contains', stack, sub)) matched.push(i);
435
+ }
436
+ const minC = schema.minContains !== undefined ? schema.minContains : 1;
437
+ if (matched.length < minC) { errors.push(err('contains', 'contains', instancePath, schemaPath + '/contains', { minContains: minC }, `must contain at least ${minC} valid item(s)`)); valid = false; }
438
+ if (schema.maxContains !== undefined && matched.length > schema.maxContains) { errors.push(err('maxContains', 'maxContains', instancePath, schemaPath + '/maxContains', { limit: schema.maxContains }, `must NOT contain more than ${schema.maxContains} valid item(s)`)); valid = false; }
439
+ if (matched.length) {
440
+ if (!local.items) local.items = new Set();
441
+ for (const i of matched) local.items.add(i);
442
+ }
443
+ }
444
+ }
445
+
446
+ // objects
447
+ if (typeof data === 'object' && data !== null && !Array.isArray(data)) {
448
+ const keys = Object.keys(data);
449
+ if (Array.isArray(schema.required)) {
450
+ for (const key of schema.required) {
451
+ if (!Object.prototype.hasOwnProperty.call(data, key)) { errors.push(err('required', 'required', instancePath, schemaPath + '/required', { missingProperty: key }, `must have required property '${key}'`)); valid = false; }
452
+ }
453
+ }
454
+ if (schema.minProperties !== undefined && keys.length < schema.minProperties) { errors.push(err('minProperties', 'minProperties', instancePath, schemaPath + '/minProperties', { limit: schema.minProperties }, `must NOT have fewer than ${schema.minProperties} properties`)); valid = false; }
455
+ if (schema.maxProperties !== undefined && keys.length > schema.maxProperties) { errors.push(err('maxProperties', 'maxProperties', instancePath, schemaPath + '/maxProperties', { limit: schema.maxProperties }, `must NOT have more than ${schema.maxProperties} properties`)); valid = false; }
456
+ if (schema.dependentRequired && typeof schema.dependentRequired === 'object') {
457
+ for (const [key, deps] of Object.entries(schema.dependentRequired)) {
458
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
459
+ for (const dep of deps) {
460
+ if (!Object.prototype.hasOwnProperty.call(data, dep)) { errors.push(err('required', 'required', instancePath, schemaPath + '/dependentRequired', { missingProperty: dep }, `must have required property '${dep}'`)); valid = false; }
461
+ }
462
+ }
463
+ }
464
+ }
465
+ if (schema.propertyNames !== undefined) {
466
+ for (const key of keys) {
467
+ if (!this.eval(schema.propertyNames, key, base, dynScope, errors, instancePath + '/' + escapePointer(key), schemaPath + '/propertyNames', stack, { props: null, items: null })) valid = false;
468
+ }
469
+ }
470
+ const props = schema.properties && typeof schema.properties === 'object' ? schema.properties : null;
471
+ const patterns = schema.patternProperties && typeof schema.patternProperties === 'object' ? Object.keys(schema.patternProperties) : null;
472
+ for (const key of keys) {
473
+ let evaluated = false;
474
+ if (props && Object.prototype.hasOwnProperty.call(props, key)) {
475
+ const sub = { props: null, items: null };
476
+ if (!this.eval(props[key], data[key], base, dynScope, errors, instancePath + '/' + escapePointer(key), schemaPath + '/properties/' + escapePointer(key), stack, sub)) valid = false;
477
+ evaluated = true;
478
+ }
479
+ if (patterns) {
480
+ for (const src of patterns) {
481
+ if (this.pattern(src).test(key)) {
482
+ const sub = { props: null, items: null };
483
+ if (!this.eval(schema.patternProperties[src], data[key], base, dynScope, errors, instancePath + '/' + escapePointer(key), schemaPath + '/patternProperties/' + escapePointer(src), stack, sub)) valid = false;
484
+ evaluated = true;
485
+ }
486
+ }
487
+ }
488
+ if (!evaluated && schema.additionalProperties !== undefined) {
489
+ const sub = { props: null, items: null };
490
+ if (!this.eval(schema.additionalProperties, data[key], base, dynScope, errors, instancePath + '/' + escapePointer(key), schemaPath + '/additionalProperties', stack, sub)) valid = false;
491
+ evaluated = true;
492
+ }
493
+ if (evaluated) {
494
+ if (!local.props) local.props = new Set();
495
+ local.props.add(key);
496
+ }
497
+ }
498
+ if (schema.dependentSchemas && typeof schema.dependentSchemas === 'object') {
499
+ for (const [key, dep] of Object.entries(schema.dependentSchemas)) {
500
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
501
+ const sub = { props: null, items: null };
502
+ if (!this.eval(dep, data, base, dynScope, errors, instancePath, schemaPath + '/dependentSchemas/' + escapePointer(key), stack, sub)) valid = false;
503
+ else mergeAnnotations(local, sub);
504
+ }
505
+ }
506
+ }
507
+ }
508
+
509
+ // in-place applicators
510
+ if (Array.isArray(schema.allOf)) {
511
+ for (let i = 0; i < schema.allOf.length; i++) {
512
+ const sub = { props: null, items: null };
513
+ if (!this.eval(schema.allOf[i], data, base, dynScope, errors, instancePath, schemaPath + '/allOf/' + i, stack, sub)) valid = false;
514
+ else mergeAnnotations(local, sub);
515
+ }
516
+ }
517
+ if (Array.isArray(schema.anyOf)) {
518
+ let any = false;
519
+ const scratch = [];
520
+ for (let i = 0; i < schema.anyOf.length; i++) {
521
+ const sub = { props: null, items: null };
522
+ if (this.eval(schema.anyOf[i], data, base, dynScope, scratch, instancePath, schemaPath + '/anyOf/' + i, stack, sub)) {
523
+ any = true;
524
+ mergeAnnotations(local, sub);
525
+ }
526
+ }
527
+ if (!any) {
528
+ for (const e of scratch) errors.push(e);
529
+ errors.push(err('anyOf', 'anyOf', instancePath, schemaPath + '/anyOf', {}, 'must match a schema in anyOf'));
530
+ valid = false;
531
+ }
532
+ }
533
+ if (Array.isArray(schema.oneOf)) {
534
+ let count = 0;
535
+ let winner = null;
536
+ const scratch = [];
537
+ for (let i = 0; i < schema.oneOf.length; i++) {
538
+ const sub = { props: null, items: null };
539
+ if (this.eval(schema.oneOf[i], data, base, dynScope, scratch, instancePath, schemaPath + '/oneOf/' + i, stack, sub)) {
540
+ count++;
541
+ winner = sub;
542
+ }
543
+ }
544
+ if (count === 1) mergeAnnotations(local, winner);
545
+ else {
546
+ if (count === 0) for (const e of scratch) errors.push(e);
547
+ errors.push(err('oneOf', 'oneOf', instancePath, schemaPath + '/oneOf', { passingSchemas: count }, 'must match exactly one schema in oneOf'));
548
+ valid = false;
549
+ }
550
+ }
551
+ if (schema.not !== undefined) {
552
+ const scratch = [];
553
+ if (this.eval(schema.not, data, base, dynScope, scratch, instancePath, schemaPath + '/not', stack, { props: null, items: null })) {
554
+ errors.push(err('not', 'not', instancePath, schemaPath + '/not', {}, 'must NOT be valid'));
555
+ valid = false;
556
+ }
557
+ }
558
+ if (schema.if !== undefined) {
559
+ const ifSub = { props: null, items: null };
560
+ const ifScratch = [];
561
+ const ifOk = this.eval(schema.if, data, base, dynScope, ifScratch, instancePath, schemaPath + '/if', stack, ifSub);
562
+ if (ifOk) {
563
+ mergeAnnotations(local, ifSub);
564
+ if (schema.then !== undefined) {
565
+ const sub = { props: null, items: null };
566
+ if (!this.eval(schema.then, data, base, dynScope, errors, instancePath, schemaPath + '/then', stack, sub)) valid = false;
567
+ else mergeAnnotations(local, sub);
568
+ }
569
+ } else if (schema.else !== undefined) {
570
+ const sub = { props: null, items: null };
571
+ if (!this.eval(schema.else, data, base, dynScope, errors, instancePath, schemaPath + '/else', stack, sub)) valid = false;
572
+ else mergeAnnotations(local, sub);
573
+ }
574
+ }
575
+
576
+ // unevaluated*: run last, against annotations from everything above
577
+ if (schema.unevaluatedProperties !== undefined && typeof data === 'object' && data !== null && !Array.isArray(data)) {
578
+ for (const key of Object.keys(data)) {
579
+ if (local.props && local.props.has(key)) continue;
580
+ const sub = { props: null, items: null };
581
+ if (!this.eval(schema.unevaluatedProperties, data[key], base, dynScope, errors, instancePath + '/' + escapePointer(key), schemaPath + '/unevaluatedProperties', stack, sub)) valid = false;
582
+ if (!local.props) local.props = new Set();
583
+ local.props.add(key);
584
+ }
585
+ }
586
+ if (schema.unevaluatedItems !== undefined && Array.isArray(data)) {
587
+ for (let i = 0; i < data.length; i++) {
588
+ if (local.items && local.items.has(i)) continue;
589
+ const sub = { props: null, items: null };
590
+ if (!this.eval(schema.unevaluatedItems, data[i], base, dynScope, errors, instancePath + '/' + i, schemaPath + '/unevaluatedItems', stack, sub)) valid = false;
591
+ if (!local.items) local.items = new Set();
592
+ local.items.add(i);
593
+ }
594
+ }
595
+
596
+ if (valid) mergeAnnotations(sink, local);
597
+ return valid;
598
+ }
599
+ }
600
+
601
+ function NO_ANNOTATIONS_SINK() {
602
+ return { props: null, items: null };
603
+ }
604
+
605
+ function escapePointer(s) {
606
+ return String(s).replace(/~/g, '~0').replace(/\//g, '~1');
607
+ }
608
+
609
+ function err(code, keyword, instancePath, schemaPath, params, message) {
610
+ return { keyword, instancePath, schemaPath, params, message };
611
+ }
612
+
613
+ function createInterpreter(schema, options) {
614
+ return new Interpreter(schema, options);
615
+ }
616
+
617
+ module.exports = { createInterpreter };
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.1.0';
10
+ module.exports = '1.2.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
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",
@@ -44,7 +44,7 @@
44
44
  "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",
45
45
  "build": "cmake-js build --target ata",
46
46
  "rebuild": "cmake-js rebuild --target ata",
47
- "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.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_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_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_defs_pointer_alias.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_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.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_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",
47
+ "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.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_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_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_defs_pointer_alias.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_order.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_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_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",
48
48
  "bench:size": "node benchmark/bench_aot_size.mjs",
49
49
  "test:suite": "node tests/run_suite.js",
50
50
  "test:compat": "node tests/test_compat.js",
@@ -112,12 +112,12 @@
112
112
  "LICENSE"
113
113
  ],
114
114
  "optionalDependencies": {
115
- "@ata-validator/native-darwin-arm64": "1.1.0",
116
- "@ata-validator/native-linux-x64-gnu": "1.1.0",
117
- "@ata-validator/native-linux-arm64-gnu": "1.1.0",
118
- "@ata-validator/native-linux-x64-musl": "1.1.0",
119
- "@ata-validator/native-linux-arm64-musl": "1.1.0",
120
- "@ata-validator/native-win32-x64": "1.1.0"
115
+ "@ata-validator/native-darwin-arm64": "1.2.0",
116
+ "@ata-validator/native-linux-x64-gnu": "1.2.0",
117
+ "@ata-validator/native-linux-arm64-gnu": "1.2.0",
118
+ "@ata-validator/native-linux-x64-musl": "1.2.0",
119
+ "@ata-validator/native-linux-arm64-musl": "1.2.0",
120
+ "@ata-validator/native-win32-x64": "1.2.0"
121
121
  },
122
122
  "peerDependencies": {
123
123
  "yaml": "^2.0.0"