ata-validator 1.15.0 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -442,12 +442,57 @@ class Plan {
442
442
  this.if = schema.if !== undefined ? child(schema.if) : undefined;
443
443
  this.then = schema.then !== undefined ? child(schema.then) : undefined;
444
444
  this.else = schema.else !== undefined ? child(schema.else) : undefined;
445
- this.hasApplicators = this.allOf !== null || this.anyOf !== null || this.oneOf !== null || this.not !== undefined || this.if !== undefined;
445
+ // Macro keywords: the schema the macro returns is applied in place, the
446
+ // way the reference validator does it, so its errors carry the keyword's
447
+ // path and the keyword adds an error of its own when that schema fails.
448
+ this.macros = null;
449
+ if (interp.keywords !== null) {
450
+ for (const key of Object.keys(schema)) {
451
+ const def = interp.keywords[key];
452
+ if (def === undefined || def.macro === null) continue;
453
+ const expanded = def.macro(schema[key], schema);
454
+ if (expanded === undefined) continue;
455
+ if (this.macros === null) this.macros = [];
456
+ this.macros.push({ keyword: key, node: child(expanded) });
457
+ }
458
+ }
459
+ this.hasApplicators = this.allOf !== null || this.anyOf !== null || this.oneOf !== null || this.not !== undefined || this.if !== undefined || this.macros !== null;
446
460
 
447
461
  this.unevaluatedProperties = schema.unevaluatedProperties !== undefined ? child(schema.unevaluatedProperties) : undefined;
448
462
  this.unevaluatedItems = schema.unevaluatedItems !== undefined ? child(schema.unevaluatedItems) : undefined;
449
463
  this.hasUnevaluated = this.unevaluatedProperties !== undefined || this.unevaluatedItems !== undefined;
450
464
 
465
+ // Custom keywords present on this node, in schema order. Each op holds
466
+ // the per-value check, built once here for the compile form, and the
467
+ // type mask it applies to (T_ANY when the definition names no type).
468
+ this.custom = null;
469
+ if (interp.keywords !== null) {
470
+ for (const key of Object.keys(schema)) {
471
+ const def = interp.keywords[key];
472
+ if (def === undefined || def.macro !== null) continue;
473
+ const value = schema[key];
474
+ let mask = T_ANY;
475
+ if (def.types !== null) {
476
+ mask = 0;
477
+ for (const t of def.types) mask |= typeBit(t);
478
+ }
479
+ let fn;
480
+ if (def.compile !== null) {
481
+ fn = def.compile(value, schema);
482
+ if (typeof fn !== 'function') throw new Error(`keyword "${key}": compile must return a function`);
483
+ } else {
484
+ const validate = def.validate;
485
+ fn = (data) => validate(value, data, schema);
486
+ // Errors written by the validate function land on the function
487
+ // itself, which is where the check reads them from below.
488
+ fn.source = validate;
489
+ }
490
+ if (this.custom === null) this.custom = [];
491
+ this.custom.push({ keyword: key, fn, mask });
492
+ }
493
+ }
494
+ this.hasCustom = this.custom !== null;
495
+
451
496
  // Resource bookkeeping, read once instead of two Map lookups per visit.
452
497
  const nodeBase = interp.state.nodeBase.get(schema);
453
498
  this.nodeBase = nodeBase !== undefined ? nodeBase : null;
@@ -519,9 +564,60 @@ function evalLeafV(P, data) {
519
564
  }
520
565
  }
521
566
  }
567
+ if (P.hasCustom && !runCustomV(P, data, bits)) return false;
568
+ return true;
569
+ }
570
+
571
+ // Custom keyword checks for one node. The verdict variant stops at the first
572
+ // failing keyword; the collecting variant runs them all and reports each,
573
+ // taking the error objects the keyword's function left on itself when it
574
+ // wrote any (`fn.errors`, the same convention as the default validator's
575
+ // custom keywords) and a fixed message otherwise.
576
+ function runCustomV(P, data, bits) {
577
+ const ops = P.custom;
578
+ for (let i = 0; i < ops.length; i++) {
579
+ const op = ops[i];
580
+ if ((bits & op.mask) === 0) continue;
581
+ const fn = op.fn;
582
+ const ok = fn(data);
583
+ const holder = fn.source || fn;
584
+ if (holder.errors) holder.errors = null;
585
+ if (!ok) return false;
586
+ }
522
587
  return true;
523
588
  }
524
589
 
590
+ function runCustom(P, data, bits, errors, instancePath, schemaPath) {
591
+ const ops = P.custom;
592
+ let valid = true;
593
+ for (let i = 0; i < ops.length; i++) {
594
+ const op = ops[i];
595
+ if ((bits & op.mask) === 0) continue;
596
+ const fn = op.fn;
597
+ const ok = fn(data);
598
+ const holder = fn.source || fn;
599
+ const own = holder.errors;
600
+ if (own) holder.errors = null;
601
+ if (ok) continue;
602
+ valid = false;
603
+ if (errors === NOERRORS) return false;
604
+ const kwPath = schemaPath + '/' + op.keyword;
605
+ if (Array.isArray(own) && own.length > 0) {
606
+ for (let j = 0; j < own.length; j++) {
607
+ const e = own[j];
608
+ errors.push(err(op.keyword, e.keyword || op.keyword,
609
+ e.instancePath !== undefined ? instancePath + e.instancePath : instancePath,
610
+ e.schemaPath !== undefined ? e.schemaPath : kwPath,
611
+ e.params !== undefined ? e.params : {},
612
+ e.message !== undefined ? e.message : `must pass "${op.keyword}" keyword validation`));
613
+ }
614
+ } else {
615
+ errors.push(err(op.keyword, op.keyword, instancePath, kwPath, { keyword: op.keyword }, `must pass "${op.keyword}" keyword validation`));
616
+ }
617
+ }
618
+ return valid;
619
+ }
620
+
525
621
  // Value-level checks only; the caller guarantees P.leaf. The bodies mirror
526
622
  // the corresponding sections of eval() exactly, minus the prologue.
527
623
  function evalLeaf(P, data, errors, instancePath, schemaPath) {
@@ -593,6 +689,7 @@ function evalLeaf(P, data, errors, instancePath, schemaPath) {
593
689
  }
594
690
  }
595
691
  }
692
+ if (P.hasCustom && !runCustom(P, data, bits, errors, instancePath, schemaPath)) valid = false;
596
693
  return valid;
597
694
  }
598
695
 
@@ -602,6 +699,8 @@ class Interpreter {
602
699
  this.root = rootSchema;
603
700
  this.state = indexSchemas(rootSchema, opts.schemaMap);
604
701
  this.userFormats = opts.formats || null;
702
+ // Custom keywords, already normalized by lib/keywords.js.
703
+ this.keywords = opts.keywords || null;
605
704
  // 2020-12 requires bookending for `$dynamicRef`; v1 removes the
606
705
  // requirement. See the `$dynamicRef` branch in eval().
607
706
  this.bookending = !opts.v1;
@@ -985,6 +1084,17 @@ class Interpreter {
985
1084
  else if (collect) mergeAnnotations(local, sub);
986
1085
  }
987
1086
  }
1087
+ if (P.macros !== null) {
1088
+ for (let i = 0; i < P.macros.length; i++) {
1089
+ const m = P.macros[i];
1090
+ const sp = schemaPath + '/' + m.keyword;
1091
+ const sub = collect ? { props: null, items: null } : DISCARD;
1092
+ if (!this.eval(m.node, data, base, dynScope, errors, instancePath, sp, stack, sub)) {
1093
+ valid = false;
1094
+ if (errors !== NOERRORS) errors.push(err(m.keyword, m.keyword, instancePath, sp, { keyword: m.keyword }, `must pass "${m.keyword}" keyword validation`));
1095
+ } else if (collect) mergeAnnotations(local, sub);
1096
+ }
1097
+ }
988
1098
  if (P.anyOf !== null) {
989
1099
  let any = false;
990
1100
  const scratch = errors === NOERRORS ? NOERRORS : [];
@@ -1087,7 +1197,7 @@ function err(code, keyword, instancePath, schemaPath, params, message) {
1087
1197
  }
1088
1198
 
1089
1199
  const { compileInterpreter } = require('./plan-compiler').install({
1090
- Plan, NOERRORS, err, evalLeaf, evalLeafV, dataBits, escapePointer,
1200
+ Plan, NOERRORS, err, evalLeaf, evalLeafV, runCustomV, dataBits, escapePointer,
1091
1201
  deepEqual, multipleOfOk, cpAtLeast, cpAtMost,
1092
1202
  T_STRING, T_ARRAY, T_OBJECT, resolveRef, splitFragment, resolveUri,
1093
1203
  });
@@ -3194,6 +3194,9 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
3194
3194
  if (needsBaseTracking(schema, schemaMap, new Set())) return null
3195
3195
 
3196
3196
  const ctx = { varCounter: 0, helperCode: [], rootDefs: eRootDefs, refStack: new Set(), schemaMap: schemaMap || null, anchors: eAnchors, rootSchema: schema, userFormats: userFormats || null,
3197
+ // Custom format checkers referenced by the body, bound as
3198
+ // closure parameters below the way the other entry points do.
3199
+ closureVars: [], closureVals: [],
3197
3200
  sourceMap: (sourceOpts && sourceOpts.sourceMap && sourceOpts.schemaFile)
3198
3201
  ? { file: sourceOpts.schemaFile, map: sourceOpts.sourceMap }
3199
3202
  : null }
@@ -3241,16 +3244,35 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
3241
3244
  }
3242
3245
  try {
3243
3246
  let fn
3247
+ // Custom format checkers come in as leading parameters, bound once here,
3248
+ // so the body's `_uf_<name>(...)` calls resolve. Without a binding the
3249
+ // first invalid document threw a ReferenceError instead of reporting.
3250
+ const cvars = ctx.closureVars
3251
+ const cvals = ctx.closureVals
3244
3252
  if (ctx.usesSafeRe) {
3245
3253
  // Inlined helperCode references __ataSafeRe; bind the safe-regex factory.
3246
3254
  // Standalone keeps the inline _errSource — the embed defines __ataSafeRe.
3247
- const built = new Function('__ataSafeRe', 'd', '_all', body)
3248
- fn = (d, _all) => built(compileSafe, d, _all)
3255
+ const built = new Function(...cvars, '__ataSafeRe', 'd', '_all', body)
3256
+ fn = cvars.length
3257
+ ? (d, _all) => built(...cvals, compileSafe, d, _all)
3258
+ : (d, _all) => built(compileSafe, d, _all)
3259
+ } else if (cvars.length) {
3260
+ const built = new Function(...cvars, 'd', '_all', body)
3261
+ fn = (d, _all) => built(...cvals, d, _all)
3249
3262
  } else {
3250
3263
  fn = new Function('d', '_all', body)
3251
3264
  }
3252
3265
  fn._errSource = body
3253
3266
  fn._usesSafeRe = !!ctx.usesSafeRe
3267
+ if (cvars.length) {
3268
+ fn._formatClosures = cvars.map((name, i) => {
3269
+ let format = null
3270
+ for (const key of Object.keys(ctx.userFormats)) {
3271
+ if (ctx.userFormats[key] === cvals[i]) { format = key; break }
3272
+ }
3273
+ return { name, fn: cvals[i], format }
3274
+ })
3275
+ }
3254
3276
  return fn
3255
3277
  } catch {
3256
3278
  return null
@@ -3538,11 +3560,16 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3538
3560
  const fmtCode = fc(v, isStr, ctx).replace(/return false/g, `{${failPush};break _fmt${ri}}`)
3539
3561
  lines.push(`_fmt${ri}:{${fmtCode}}`)
3540
3562
  } else if (ctx.userFormats && typeof ctx.userFormats[schema.format] === 'function') {
3541
- // User-supplied format checker on the error path. Same closure plumbing
3542
- // as the boolean codegen — but error codegen has no closure factory, so
3543
- // bundle output serializes the function via Function#toString separately.
3563
+ // User-supplied format checker on the error path: the same closure
3564
+ // plumbing as the boolean codegen. The function is bound as a parameter
3565
+ // of the compiled error function; bundle output serializes it via
3566
+ // Function#toString separately.
3544
3567
  const safeName = schema.format.replace(/[^a-zA-Z0-9_]/g, '_')
3545
3568
  const closureName = `_uf_${safeName}`
3569
+ if (ctx.closureVars && !ctx.closureVars.includes(closureName)) {
3570
+ ctx.closureVars.push(closureName)
3571
+ ctx.closureVals.push(ctx.userFormats[schema.format])
3572
+ }
3546
3573
  const guard = isStr ? '' : `typeof ${v}==='string'&&`
3547
3574
  lines.push(`if(${guard}!${closureName}(${v})){${failPush}}`)
3548
3575
  }
@@ -0,0 +1,113 @@
1
+ 'use strict';
2
+
3
+ // Custom keywords for `new Validator(schema, { keywords })`.
4
+ //
5
+ // A definition is one of:
6
+ // fn shorthand for { validate: fn }
7
+ // { validate(value, data, parent) } called per value
8
+ // { compile(value, parent) -> fn } called once per schema node, the
9
+ // returned function per value
10
+ // { macro(value, parent) -> schema } the returned schema is applied in
11
+ // place, and the keyword reports an
12
+ // error of its own when it fails
13
+ // with optional `type`: a JSON Schema type name or a list of them. A typed
14
+ // keyword is skipped for data of any other type, which is what makes
15
+ // `{ type: 'number', ... }` safe next to `type: ['number', 'string']`.
16
+ //
17
+ // Schemas that use a registered keyword run on the interpreted engine, so
18
+ // every applicator around the custom check keeps its specification meaning.
19
+ // A function cannot be carried into a standalone module, so the ahead-of-time
20
+ // paths refuse these options instead of emitting a module that would ignore
21
+ // the keyword.
22
+
23
+ const RESERVED = new Set([
24
+ '$id', '$schema', '$ref', '$defs', '$anchor', '$dynamicRef', '$dynamicAnchor',
25
+ '$vocabulary', '$comment', 'definitions', 'type', 'enum', 'const',
26
+ 'properties', 'patternProperties', 'additionalProperties', 'required',
27
+ 'items', 'prefixItems', 'additionalItems', 'contains', 'allOf', 'anyOf',
28
+ 'oneOf', 'not', 'if', 'then', 'else', 'format', 'pattern',
29
+ 'unevaluatedProperties', 'unevaluatedItems', 'dependentSchemas',
30
+ 'dependentRequired', 'dependencies', 'propertyNames', 'propertyDependencies',
31
+ 'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'multipleOf',
32
+ 'minLength', 'maxLength', 'minItems', 'maxItems', 'uniqueItems',
33
+ 'minContains', 'maxContains', 'minProperties', 'maxProperties',
34
+ ]);
35
+
36
+ // Normalize a caller's `{ name: def }` map into `{ name: { types, validate,
37
+ // compile, macro } }`. Throws on a definition that has no way to run, so a
38
+ // keyword that would otherwise be ignored is never accepted quietly.
39
+ function normalizeKeywords(defs) {
40
+ if (!defs || typeof defs !== 'object') return null;
41
+ const names = Object.keys(defs);
42
+ if (names.length === 0) return null;
43
+ const out = Object.create(null);
44
+ for (const name of names) {
45
+ const raw = defs[name];
46
+ const def = typeof raw === 'function' ? { validate: raw } : raw;
47
+ if (!def || typeof def !== 'object') {
48
+ throw new Error(`keyword "${name}": definition must be a function or an object`);
49
+ }
50
+ if (RESERVED.has(name)) {
51
+ throw new Error(`keyword "${name}" is a JSON Schema keyword and cannot be redefined`);
52
+ }
53
+ const hasValidate = typeof def.validate === 'function';
54
+ const hasCompile = typeof def.compile === 'function';
55
+ const hasMacro = typeof def.macro === 'function';
56
+ if (!hasValidate && !hasCompile && !hasMacro) {
57
+ throw new Error(
58
+ `keyword "${name}": definition needs validate, compile or macro` +
59
+ (typeof def.code === 'function' ? ' (code-generating keywords are not supported)' : ''),
60
+ );
61
+ }
62
+ let types = null;
63
+ if (def.type !== undefined) {
64
+ types = Array.isArray(def.type) ? def.type.slice() : [def.type];
65
+ for (const t of types) {
66
+ if (typeof t !== 'string') throw new Error(`keyword "${name}": type must be a string or a list of strings`);
67
+ }
68
+ }
69
+ out[name] = {
70
+ name,
71
+ types,
72
+ validate: hasValidate ? def.validate : null,
73
+ compile: hasCompile ? def.compile : null,
74
+ macro: hasMacro ? def.macro : null,
75
+ };
76
+ }
77
+ return out;
78
+ }
79
+
80
+ // Does the schema, or any subschema of it, use a registered keyword? Walks
81
+ // every object and array so keywords inside applicators and definitions are
82
+ // found too. Property names under `properties` are not keywords, so that map
83
+ // is descended into without reading its keys as keywords.
84
+ function schemaUsesKeywords(schema, keywords) {
85
+ if (!keywords) return false;
86
+ const seen = new Set();
87
+ const walk = (node, keysAreNames) => {
88
+ if (node === null || typeof node !== 'object') return false;
89
+ if (seen.has(node)) return false;
90
+ seen.add(node);
91
+ if (Array.isArray(node)) {
92
+ for (const item of node) if (walk(item, false)) return true;
93
+ return false;
94
+ }
95
+ for (const key of Object.keys(node)) {
96
+ if (!keysAreNames && keywords[key] !== undefined) return true;
97
+ const v = node[key];
98
+ if (v !== null && typeof v === 'object') {
99
+ if (walk(v, !keysAreNames && NAME_MAPS.has(key))) return true;
100
+ }
101
+ }
102
+ return false;
103
+ };
104
+ return walk(schema, false);
105
+ }
106
+
107
+ // Maps whose keys are user-chosen names, not keywords.
108
+ const NAME_MAPS = new Set([
109
+ 'properties', 'patternProperties', '$defs', 'definitions',
110
+ 'dependentSchemas', 'dependentRequired', 'dependencies', 'propertyDependencies',
111
+ ]);
112
+
113
+ module.exports = { normalizeKeywords, schemaUsesKeywords };
@@ -46,7 +46,7 @@
46
46
  // allocation per child.
47
47
 
48
48
  function install(deps) {
49
- const { Plan, NOERRORS, err, evalLeaf, evalLeafV, dataBits, escapePointer,
49
+ const { Plan, NOERRORS, err, evalLeaf, evalLeafV, runCustomV, dataBits, escapePointer,
50
50
  deepEqual, multipleOfOk, cpAtLeast, cpAtMost,
51
51
  T_STRING, T_ARRAY, T_OBJECT, resolveRef, splitFragment, resolveUri } = deps;
52
52
 
@@ -229,6 +229,10 @@ function install(deps) {
229
229
  if (run !== null) { needsBits = true; checks.push((data, bits) => bits !== T_OBJECT || run(data)); }
230
230
  }
231
231
 
232
+ if (P.hasCustom) {
233
+ needsBits = true;
234
+ checks.push((data, bits) => runCustomV(P, data, bits));
235
+ }
232
236
  if (checks.length === 0) return TRUE_FN;
233
237
  if (!needsBits) {
234
238
  if (checks.length === 1) { const a = checks[0]; return (data) => a(data, 0); }
@@ -368,7 +372,7 @@ function install(deps) {
368
372
  if (P.hasType || P.enum !== null || P.hasConst || P.hasNumber || P.hasString ||
369
373
  P.minItems !== undefined || P.maxItems !== undefined || P.uniqueItems ||
370
374
  P.required !== null || P.minProperties !== undefined || P.maxProperties !== undefined ||
371
- P.dependentRequired !== null) {
375
+ P.dependentRequired !== null || P.hasCustom) {
372
376
  steps.push((data, errors, instancePath, schemaPath) => evalLeaf(P, data, errors, instancePath, schemaPath));
373
377
  vsteps.push(compileLeafV(P));
374
378
  }
@@ -679,6 +683,34 @@ function install(deps) {
679
683
  return true;
680
684
  });
681
685
  }
686
+ if (P.macros !== null) {
687
+ const ms = P.macros.map((m) => ({ keyword: m.keyword, fn: inPlace(m.node, base) }));
688
+ steps.push((data, errors, instancePath, schemaPath, stack, rec) => {
689
+ let ok = true;
690
+ for (let i = 0; i < ms.length; i++) {
691
+ const m = ms[i];
692
+ const sp = schemaPath + '/' + m.keyword;
693
+ let passed;
694
+ if (collect) {
695
+ const pl = pLen(rec), n0 = rec.n, xl = xLen(rec);
696
+ passed = m.fn.c(data, errors, instancePath, sp, stack, rec);
697
+ if (!passed) undo(rec, pl, n0, xl);
698
+ } else {
699
+ passed = m.fn.c(data, errors, instancePath, sp, stack);
700
+ }
701
+ if (!passed) {
702
+ ok = false;
703
+ if (errors === NOERRORS) return false;
704
+ errors.push(err(m.keyword, m.keyword, instancePath, sp, { keyword: m.keyword }, `must pass "${m.keyword}" keyword validation`));
705
+ }
706
+ }
707
+ return ok;
708
+ });
709
+ vsteps.push((data, stack, rec) => {
710
+ for (let i = 0; i < ms.length; i++) if (!ms[i].fn.v(data, stack, rec)) return false;
711
+ return true;
712
+ });
713
+ }
682
714
  if (P.anyOf !== null) {
683
715
  const fns = P.anyOf.map((v) => inPlace(v, base));
684
716
  steps.push((data, errors, instancePath, schemaPath, stack, rec) => {
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.15.0';
10
+ module.exports = '1.16.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.15.0",
3
+ "version": "1.16.1",
4
4
  "description": "JSON Schema validation with first-class TypeScript and zero runtime cost. AOT compile to per-schema ESM modules with zero validator dependency. Generic Validator<T> for TypeBox/Zod/Valibot composition. Optional runtime API. Standard Schema V1 compatible.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -51,7 +51,7 @@
51
51
  "release:check": "node scripts/regen-safe-regex-source.js && node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_source_sync.js && node tests/test_version_sync.js",
52
52
  "build": "cmake-js build --target ata",
53
53
  "rebuild": "cmake-js rebuild --target ata",
54
- "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_buffer_gate.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_aot_parse.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_formats_single_pass.js && node tests/test_format_single_error.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_ajv_errors.js && node tests/test_pattern_properties_errors.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_value_equality.js && node tests/test_vocabulary.js && node tests/test_schema_scan.js && node tests/test_lazy_errors.js && node tests/test_lazy_instance.js && node tests/test_cyclic_input.js && node tests/test_native_error_codes.js && node tests/test_verdict_preprocess.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_additive_fields.js && node tests/test_diagnostic_source.js && node tests/test_diagnose.js && node tests/test_correlate.js && node tests/test_diagnostics_score.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
54
+ "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_buffer_gate.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_aot_parse.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_formats_single_pass.js && node tests/test_format_single_error.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_ajv_parity.js && node tests/test_user_format_error_path.js && node tests/test_pattern_properties_errors.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_value_equality.js && node tests/test_vocabulary.js && node tests/test_schema_scan.js && node tests/test_lazy_errors.js && node tests/test_lazy_instance.js && node tests/test_cyclic_input.js && node tests/test_native_error_codes.js && node tests/test_verdict_preprocess.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_additive_fields.js && node tests/test_diagnostic_source.js && node tests/test_diagnose.js && node tests/test_correlate.js && node tests/test_diagnostics_score.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
55
55
  "bench:size": "node benchmark/bench_aot_size.mjs",
56
56
  "test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
57
57
  "test:compat": "node tests/test_compat.js",
@@ -122,13 +122,13 @@
122
122
  "LICENSE"
123
123
  ],
124
124
  "optionalDependencies": {
125
- "@ata-validator/native-darwin-arm64": "1.15.0",
126
- "@ata-validator/native-darwin-x64": "1.15.0",
127
- "@ata-validator/native-linux-x64-gnu": "1.15.0",
128
- "@ata-validator/native-linux-arm64-gnu": "1.15.0",
129
- "@ata-validator/native-linux-x64-musl": "1.15.0",
130
- "@ata-validator/native-linux-arm64-musl": "1.15.0",
131
- "@ata-validator/native-win32-x64": "1.15.0"
125
+ "@ata-validator/native-darwin-arm64": "1.16.1",
126
+ "@ata-validator/native-darwin-x64": "1.16.1",
127
+ "@ata-validator/native-linux-arm64-gnu": "1.16.1",
128
+ "@ata-validator/native-linux-arm64-musl": "1.16.1",
129
+ "@ata-validator/native-linux-x64-gnu": "1.16.1",
130
+ "@ata-validator/native-linux-x64-musl": "1.16.1",
131
+ "@ata-validator/native-win32-x64": "1.16.1"
132
132
  },
133
133
  "peerDependencies": {
134
134
  "yaml": "^2.0.0"
@@ -141,6 +141,8 @@
141
141
  "devDependencies": {
142
142
  "@sinclair/typebox": "^0.34.49",
143
143
  "@types/node": "^25.8.0",
144
+ "ajv": "^8.20.0",
145
+ "ajv-formats": "^3.0.1",
144
146
  "cmake-js": "^8.0.0",
145
147
  "esbuild": "^0.28.0",
146
148
  "jsonc-parser": "^3.3.1",