ata-validator 1.31.2 → 1.31.3

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/index.js CHANGED
@@ -244,26 +244,36 @@ function collectRemovals(schema, actions, path) {
244
244
  // Emit the in-place strip for one schema node and everything under its
245
245
  // `properties`. Scope matches collectRemovals(): object properties only, so
246
246
  // the two paths keep the same answer.
247
+ // Each level's code, its own removal loop and its children's, sits inside the
248
+ // guard for that level. A child used to be guarded only on itself, so its
249
+ // access expression read through a parent that could be absent or null:
250
+ // `{ c: { properties: { d: { additionalProperties: false } } } }` threw a
251
+ // TypeError from validate({}) whenever the optional `c` was missing.
247
252
  function emitRemovals(node, access, lines, depth, seen) {
248
253
  if (!node || typeof node !== 'object' || !node.properties) return;
249
254
  if (seen.has(node)) return;
250
255
  seen.add(node);
251
256
  const keys = Object.keys(node.properties);
252
- if (node.additionalProperties === false && keys.length > 0) {
257
+ const body = [];
258
+ // With no declared property every key is additional, as the interpreted
259
+ // engine's remover has it; this pass used to skip such a node entirely.
260
+ if (node.additionalProperties === false) {
253
261
  const kv = '_k' + depth;
254
262
  const checks = keys.map((k) => `${kv}!==${JSON.stringify(k)}`).join('&&');
255
- const guard = depth === 0
256
- ? ''
257
- : `if(${access}!==null&&typeof ${access}==='object'&&!Array.isArray(${access}))`;
258
- lines.push(`${guard}for(var ${kv} in ${access})if(${checks})delete ${access}[${kv}]`);
263
+ body.push(keys.length > 0
264
+ ? `for(var ${kv} in ${access})if(${checks})delete ${access}[${kv}]`
265
+ : `for(var ${kv} in ${access})delete ${access}[${kv}]`);
259
266
  }
260
267
  for (const key of keys) {
261
268
  const prop = node.properties[key];
262
269
  if (prop && typeof prop === 'object' && prop.properties) {
263
- emitRemovals(prop, `${access}[${JSON.stringify(key)}]`, lines, depth + 1, seen);
270
+ emitRemovals(prop, `${access}[${JSON.stringify(key)}]`, body, depth + 1, seen);
264
271
  }
265
272
  }
266
273
  seen.delete(node);
274
+ if (body.length === 0) return;
275
+ if (depth === 0) lines.push(...body);
276
+ else lines.push(`if(${access}!==null&&typeof ${access}==='object'&&!Array.isArray(${access})){${body.join('\n')}}`);
267
277
  }
268
278
 
269
279
  // Generate a fast preprocess function via codegen instead of closure arrays
@@ -1306,6 +1316,23 @@ class Validator {
1306
1316
  this._mutatesInput = !!(preprocess || options.coerceTypes || options.removeAdditional);
1307
1317
  this._preprocess = preprocess;
1308
1318
 
1319
+ // removeAdditional alone, the common parse-and-strip use: a verdict function
1320
+ // that deletes unknown keys in the walk it already makes, where the pass
1321
+ // above walks every object a second time just to find them. It answers
1322
+ // the documents it accepts; anything it rejects takes the full path below,
1323
+ // which removes, validates and reports exactly as before, so a rejected
1324
+ // document is left as clean as it always was. The generator declines any
1325
+ // schema where deleting during the walk could change an answer.
1326
+ let fusedRemove = null;
1327
+ if (preprocess && options.removeAdditional && !options.coerceTypes && !this._interpretOnly &&
1328
+ !this._userFormats && !this._usesKeywords && !this._schemaStr.includes('"default"')) {
1329
+ try {
1330
+ fusedRemove = compileToJSCodegen(schemaObj, this._schemaMap.size > 0 ? this._schemaMap : null, null, { removeAdditional: true });
1331
+ } catch {
1332
+ fusedRemove = null;
1333
+ }
1334
+ }
1335
+
1309
1336
  // Detect if schema is "selective" -- doesn't recurse into arrays/deep objects.
1310
1337
  const hasArrayTraversal =
1311
1338
  schemaObj &&
@@ -1577,9 +1604,11 @@ class Validator {
1577
1604
  // The verdict methods answer validate()'s question without building the
1578
1605
  // error list, so they run the same preprocess pass. Skipping it made the
1579
1606
  // two disagree on input that coercion or a default would have fixed.
1580
- _bindVerdict(this, preprocess
1581
- ? (data) => { preprocess(data); return jsFn(data) }
1582
- : jsFn);
1607
+ _bindVerdict(this, fusedRemove
1608
+ ? (data) => fusedRemove(data) || (preprocess(data), jsFn(data))
1609
+ : preprocess
1610
+ ? (data) => { preprocess(data); return jsFn(data) }
1611
+ : jsFn);
1583
1612
  // Same preference as the object path: the combined function first, since
1584
1613
  // it validates and collects in one pass, and the error generator behind
1585
1614
  // it. `errPreferCombined` is that order, resolved on the first rejection
@@ -1794,7 +1823,12 @@ class Validator {
1794
1823
  : _validate;
1795
1824
  _bindVerdict(this, this._fastVerdict
1796
1825
  ? this._fastVerdict
1797
- : (data) => _validate(data).valid);
1826
+ // The rewrite runs first here too, as it does for validate() above; a
1827
+ // verdict without it disagreed with validate() on input that coercion,
1828
+ // a default or a removed key would have fixed.
1829
+ : preprocess
1830
+ ? (data) => { preprocess(data); return _validate(data).valid; }
1831
+ : (data) => _validate(data).valid);
1798
1832
  this.validateAndParse = (jsonStr) => this._compiled.validateAndParse(jsonStr);
1799
1833
  {
1800
1834
  const slot = this._fastSlot;
@@ -1927,12 +1961,22 @@ class Validator {
1927
1961
  // paths that only need a boolean.
1928
1962
  if (this.validate) {
1929
1963
  const _bare = this.validate;
1930
- this.validate = (data) => {
1931
- const r = _bare(data);
1932
- return (r.valid === true && r.data === undefined)
1933
- ? { valid: true, data, errors: r.errors }
1934
- : r;
1935
- };
1964
+ this.validate = fusedRemove
1965
+ // A document the removing verdict accepts is answered here, one call
1966
+ // deep; anything else takes the full path, see fusedRemove above.
1967
+ ? (data) => {
1968
+ if (fusedRemove(data)) return { valid: true, data, errors: VALID_RESULT.errors };
1969
+ const r = _bare(data);
1970
+ return (r.valid === true && r.data === undefined)
1971
+ ? { valid: true, data, errors: r.errors }
1972
+ : r;
1973
+ }
1974
+ : (data) => {
1975
+ const r = _bare(data);
1976
+ return (r.valid === true && r.data === undefined)
1977
+ ? { valid: true, data, errors: r.errors }
1978
+ : r;
1979
+ };
1936
1980
  }
1937
1981
 
1938
1982
  // Custom error messages: if any subschema declares an `errorMessage`
@@ -1255,3 +1255,9 @@ function createInterpreter(schema, options) {
1255
1255
  }
1256
1256
 
1257
1257
  module.exports = { createInterpreter };
1258
+ // For lib/plan-source.js, which emits source from the same plans and must call
1259
+ // the same helpers so the two cannot answer differently.
1260
+ Object.defineProperty(module.exports, '_planInternals', {
1261
+ value: { Plan, deepEqual, cpAtLeast, cpAtMost, multipleOfOk, dataBits, T_STRING, T_NUMBER, T_INTEGER, T_BOOLEAN, T_NULL, T_OBJECT, T_ARRAY, T_ANY },
1262
+ enumerable: false,
1263
+ });
@@ -912,17 +912,66 @@ const PROTO_NAMES = new Set([...Object.getOwnPropertyNames(Object.prototype), '_
912
912
  // Declared once per compiled function and called by name: spelled out at each
913
913
  // property, the test grew a 50-property module by a tenth. Both are small
914
914
  // enough for the engine to inline.
915
- const OWN_HELPERS = 'const _hop=Object.prototype.hasOwnProperty;' +
916
- 'function _own(o,k){return o.__proto__===Object.prototype?k in o:_hop.call(o,k)}'
915
+ //
916
+ // The `in` stays at the call site with the key as a constant: inside a shared
917
+ // helper the key is a parameter, the engine cannot keep that `in` monomorphic,
918
+ // and reading the errors of a product document went from 3.9 to 5.8 us. The
919
+ // helper only runs once `in` has said yes, and on an ordinary object it
920
+ // answers from the prototype without looking at the key.
921
+ const OWN_HELPERS = 'const _hop=Object.prototype.hasOwnProperty;function _h(o,k){return _hop.call(o,k)}' +
922
+ 'function _ok(o,k){return o.__proto__===Object.prototype||_hop.call(o,k)}' +
923
+ 'function _hall(o,ks){for(let i=0;i<ks.length;i++)if(!_hop.call(o,ks[i]))return false;return true}'
924
+ //
925
+ // Better still, the prototype is read once per object: a node that asks about
926
+ // presence declares `_pkN`, whether its value is an ordinary object, before
927
+ // anything else, and every key test in that node reads the local. A shared
928
+ // helper reading `__proto__` for each key was megamorphic, since it saw every
929
+ // object shape in the document, and cost the error path about a microsecond
930
+ // on a product document.
917
931
  function ownKeyExpr (ctx, v, key) {
918
932
  hoistOnce(ctx, '_ownHoisted', OWN_HELPERS)
919
933
  const k = JSON.stringify(key)
920
- return PROTO_NAMES.has(key) ? `_hop.call(${v},${k})` : `_own(${v},${k})`
934
+ if (PROTO_NAMES.has(key)) return `_h(${v},${k})`
935
+ const pk = ctx.plainOf ? ctx.plainOf.get(v) : undefined
936
+ return pk !== undefined ? `(${pk}?${k} in ${v}:_h(${v},${k}))` : `(${k} in ${v}&&_ok(${v},${k}))`
921
937
  }
922
938
  // For a value already read as `v[key]` and found not to be undefined: whether
923
- // it came from `v` itself. Same test; `in` is already known to hold.
939
+ // it came from `v` itself. `in` is known to hold, so only the second half runs.
924
940
  function ownGuard (ctx, v, key) {
925
- return ownKeyExpr(ctx, v, key)
941
+ hoistOnce(ctx, '_ownHoisted', OWN_HELPERS)
942
+ const k = JSON.stringify(key)
943
+ if (PROTO_NAMES.has(key)) return `_h(${v},${k})`
944
+ const pk = ctx.plainOf ? ctx.plainOf.get(v) : undefined
945
+ return pk !== undefined ? `(${pk}||_h(${v},${k}))` : `_ok(${v},${k})`
946
+ }
947
+
948
+ // Declares the per-object prototype flag for a node that asks about presence,
949
+ // and records it for the key tests emitted while the node is generated. The
950
+ // record is restored on the way out, so a nested node over the same value
951
+ // expression, an allOf branch say, cannot leave its name behind.
952
+ function withPlain (schema, v, lines, ctx, run) {
953
+ if (typeof schema !== 'object' || schema === null ||
954
+ !(schema.required || schema.properties || schema.dependentRequired || schema.dependentSchemas)) {
955
+ return run()
956
+ }
957
+ if (!ctx.plainOf) ctx.plainOf = new Map()
958
+ const name = '_pk' + ctx.varCounter++
959
+ const at = lines.length
960
+ lines.push(`const ${name}=typeof ${v}==='object'&&${v}!==null&&${v}.__proto__===Object.prototype`)
961
+ const prev = ctx.plainOf.get(v)
962
+ ctx.plainOf.set(v, name)
963
+ try {
964
+ return run()
965
+ } finally {
966
+ if (prev === undefined) ctx.plainOf.delete(v)
967
+ else ctx.plainOf.set(v, prev)
968
+ // A node whose keys are all typed never reads the flag; declaring it
969
+ // anyway cost a strict-object check about five percent. Unused, it goes.
970
+ const re = new RegExp('\\b' + name + '\\b')
971
+ let used = false
972
+ for (let i = at + 1; i < lines.length && !used; i++) if (re.test(lines[i])) used = true
973
+ if (!used && lines[at] === `const ${name}=typeof ${v}==='object'&&${v}!==null&&${v}.__proto__===Object.prototype`) lines.splice(at, 1)
974
+ }
926
975
  }
927
976
  // The same test at run time, for the closure compiler.
928
977
  const _hop = Object.prototype.hasOwnProperty
@@ -934,6 +983,56 @@ function hasOwnKey (d, key) {
934
983
  const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'toString', 'valueOf',
935
984
  'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString'])
936
985
 
986
+ // The nodes the removeAdditional pass deletes from, when deleting them during
987
+ // the verdict gives the answer removing them first would, or null when it may
988
+ // not. It may not whenever a keyword could look at a key before it is deleted
989
+ // and pass where it would fail after: minProperties, a const or enum object
990
+ // that holds the extra key, uniqueItems over objects that differ only in one,
991
+ // a branch that requires it, a `required` naming a key the node does not
992
+ // declare. So the whole schema is held to keywords that only look at declared
993
+ // properties or at scalar values. The node set mirrors emitRemovals in
994
+ // index.js: reached through `properties` from the root, with
995
+ // additionalProperties false and at least one declared property.
996
+ const FUSED_REMOVAL_KEYS = new Set(['type', 'properties', 'required', 'additionalProperties', 'items',
997
+ 'minItems', 'maxItems', 'minLength', 'maxLength', 'pattern', 'format', 'minimum', 'maximum',
998
+ 'exclusiveMinimum', 'exclusiveMaximum', 'multipleOf', 'enum', 'const', 'title', 'description',
999
+ '$comment', '$schema'])
1000
+ function fusedRemovalNodes (root) {
1001
+ const isScalar = (x) => x === null || (typeof x !== 'object' && typeof x !== 'function')
1002
+ const safe = (node) => {
1003
+ if (typeof node === 'boolean') return true
1004
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return false
1005
+ for (const k of Object.keys(node)) if (!FUSED_REMOVAL_KEYS.has(k)) return false
1006
+ if (node.enum !== undefined && !(Array.isArray(node.enum) && node.enum.every(isScalar))) return false
1007
+ if (node.const !== undefined && !isScalar(node.const)) return false
1008
+ if (node.properties !== undefined) {
1009
+ if (!node.properties || typeof node.properties !== 'object' || Array.isArray(node.properties)) return false
1010
+ for (const k of Object.keys(node.properties)) if (!safe(node.properties[k])) return false
1011
+ }
1012
+ if (node.additionalProperties === false) {
1013
+ const declared = node.properties ? Object.keys(node.properties) : []
1014
+ if (declared.length === 0) return false
1015
+ if (Array.isArray(node.required) && node.required.some((r) => !declared.includes(r))) return false
1016
+ } else if (node.additionalProperties !== undefined && node.additionalProperties !== true && !safe(node.additionalProperties)) {
1017
+ return false
1018
+ }
1019
+ if (node.items !== undefined && (Array.isArray(node.items) || !safe(node.items))) return false
1020
+ return true
1021
+ }
1022
+ if (!safe(root)) return null
1023
+ const nodes = new Set()
1024
+ const walk = (node) => {
1025
+ if (!node || typeof node !== 'object' || !node.properties) return
1026
+ if (node.additionalProperties === false && Object.keys(node.properties).length > 0) nodes.add(node)
1027
+ for (const k of Object.keys(node.properties)) {
1028
+ const p = node.properties[k]
1029
+ if (p && typeof p === 'object' && p.properties) walk(p)
1030
+ }
1031
+ }
1032
+ walk(root)
1033
+ return nodes.size > 0 ? nodes : null
1034
+ }
1035
+
937
1036
  // Check if all $dynamicRef in a target schema can be resolved via the calling schema's anchors.
938
1037
  function canResolveDynamicRefs(target, callingSchema, schemaMap) {
939
1038
  // Collect anchors from calling schema
@@ -1546,9 +1645,18 @@ function sharedCodegenGate(schema, schemaMap) {
1546
1645
 
1547
1646
  // --- Codegen mode: generates a single Function (NOT CSP-safe) ---
1548
1647
  // This matches ajv's approach: one monolithic function, V8 JIT fully inlines it
1549
- function compileToJSCodegen(schema, schemaMap, userFormats) {
1648
+ function compileToJSCodegen(schema, schemaMap, userFormats, opts) {
1550
1649
  if (typeof schema === 'boolean') return schema ? () => true : () => false
1551
1650
  if (typeof schema !== 'object' || schema === null) return null
1651
+ // removeAdditional: a verdict function that deletes the keys the remover
1652
+ // would delete, in the walk the verdict already makes, instead of rejecting
1653
+ // them. Only for schemas where that cannot change an answer; see
1654
+ // fusedRemovalNodes.
1655
+ let removeNodes = null
1656
+ if (opts && opts.removeAdditional) {
1657
+ removeNodes = fusedRemovalNodes(schema)
1658
+ if (removeNodes === null) return null
1659
+ }
1552
1660
 
1553
1661
  if (!sharedCodegenGate(schema, schemaMap)) return null
1554
1662
 
@@ -1608,7 +1716,7 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
1608
1716
  if (hasUnresolvableRef(schema, rootDefs, anchors, schemaMap, new Set())) return null
1609
1717
  if (needsBaseTracking(schema, schemaMap, new Set())) return null
1610
1718
 
1611
- const ctx = { varCounter: 0, helpers: [], helperCode: [], preamble: [], shared: [], closureVars: ['_cpLen'], closureVals: [_cpLen], rootDefs, refStack: new Set(), schemaMap: schemaMap || null, anchors, rootSchema: schema, userFormats: userFormats || null }
1719
+ const ctx = { varCounter: 0, helpers: [], helperCode: [], preamble: [], shared: [], closureVars: ['_cpLen'], closureVals: [_cpLen], rootDefs, refStack: new Set(), schemaMap: schemaMap || null, anchors, rootSchema: schema, userFormats: userFormats || null, removeNodes }
1612
1720
  const lines = []
1613
1721
  genCode(schema, 'd', lines, ctx)
1614
1722
 
@@ -1930,6 +2038,9 @@ function _deferOrInline(ctx, lines, v, check) {
1930
2038
  // 'array' = we know v is an array
1931
2039
  // 'string' / 'number' / 'integer' = we know the primitive type
1932
2040
  function genCode(schema, v, lines, ctx, knownType) {
2041
+ return withPlain(schema, v, lines, ctx, () => genCodeNode(schema, v, lines, ctx, knownType))
2042
+ }
2043
+ function genCodeNode(schema, v, lines, ctx, knownType) {
1933
2044
  // A boolean schema is a constant. false rejects whatever reached it; true
1934
2045
  // constrains nothing and emits nothing, which is correct only because the
1935
2046
  // caller's own checks still stand around this call.
@@ -2156,7 +2267,10 @@ function genCode(schema, v, lines, ctx, knownType) {
2156
2267
  // On an object with another prototype the typed ones may be inherited.
2157
2268
  if (typed.length > 0) {
2158
2269
  hoistOnce(ctx, '_ownHoisted', OWN_HELPERS)
2159
- lines.push(`if(${v}.__proto__!==Object.prototype&&!(${typed.map((k) => `_hop.call(${v},${JSON.stringify(k)})`).join('&&')}))return false`)
2270
+ // Read directly: the object type is already established here, and a
2271
+ // flag declared for this alone costs more than it saves. The list is
2272
+ // built only on that path, which an ordinary object never takes.
2273
+ lines.push(`if(${v}.__proto__!==Object.prototype&&!_hall(${v},${JSON.stringify(typed)}))return false`)
2160
2274
  }
2161
2275
  } else if (schema.required && schema.required.length > 0) {
2162
2276
  const checks = schema.required.map(key => `!${ownKeyExpr(ctx, v, key)}`)
@@ -2316,7 +2430,21 @@ function genCode(schema, v, lines, ctx, knownType) {
2316
2430
  // additionalProperties -- deferred to end of function for better V8 optimization
2317
2431
  // (type checks run first in hot path, expensive prop count check last)
2318
2432
  // Skip if patternProperties is present — it will handle additionalProperties in a unified loop
2319
- if (schema.additionalProperties === false && schema.properties && !schema.patternProperties) {
2433
+ if (schema.additionalProperties === false && schema.properties && !schema.patternProperties && ctx.removeNodes && ctx.removeNodes.has(schema)) {
2434
+ const kv = `_rk${ctx.varCounter++}`
2435
+ const declared = Object.keys(schema.properties)
2436
+ let loop = `for(var ${kv} in ${v})if(${declared.map((k) => `${kv}!==${JSON.stringify(k)}`).join('&&')})delete ${v}[${kv}]`
2437
+ // When every declared property is required, the verdict has already
2438
+ // established they are all there, so a key count equal to the declared
2439
+ // count means nothing is extra and the deleting walk, which compares every
2440
+ // key against the declared names, need not run.
2441
+ const req = new Set(schema.required || [])
2442
+ if (declared.length <= 15 && declared.every((k) => req.has(k))) {
2443
+ const n = `_rn${ctx.varCounter++}`
2444
+ loop = `{var ${n}=0;for(var ${kv} in ${v})${n}++;if(${n}!==${declared.length}){${loop}}}`
2445
+ }
2446
+ _deferOrInline(ctx, lines, v, isObj ? loop : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){${loop}}`)
2447
+ } else if (schema.additionalProperties === false && schema.properties && !schema.patternProperties) {
2320
2448
  const propCount = Object.keys(schema.properties).length
2321
2449
  const allRequired = schema.required && schema.required.length === propCount
2322
2450
  const inner = allRequired
@@ -3731,6 +3859,9 @@ function emitBranchCollapse (branches, keyword, v, pathExpr, lines, ctx, schemaP
3731
3859
  // Instead of `return false`, pushes to `_e` array and optionally early-returns.
3732
3860
  // `_all` parameter: if falsy, return after first error.
3733
3861
  function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3862
+ return withPlain(schema, v, lines, ctx, () => genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix))
3863
+ }
3864
+ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3734
3865
  if (!schemaPrefix) schemaPrefix = '#'
3735
3866
  // A false schema reports one error at the value that reached it, with the
3736
3867
  // keyword the interpreter has always used for it, `not` (lib/plan-compiler.js
@@ -4451,6 +4582,9 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
4451
4582
  // After type check passes → use optimizations (destructuring, no guards).
4452
4583
  // If type check fails → push error, skip property checks (they'd crash).
4453
4584
  function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
4585
+ return withPlain(schema, v, lines, ctx, () => genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix))
4586
+ }
4587
+ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
4454
4588
  if (!schemaPrefix) schemaPrefix = '#'
4455
4589
  // Per-node, for the reason spelled out in genCode.
4456
4590
  let ppHandledPropertyNames = false
@@ -0,0 +1,281 @@
1
+ 'use strict';
2
+
3
+ // Generated source from interpreter plans: the verdict function.
4
+ //
5
+ // The interpreted engine is the reference every other engine is tested
6
+ // against, and its Plan is a schema node with every keyword resolved once.
7
+ // lib/plan-compiler.js turns plans into closures; this turns them into source,
8
+ // deciding at compile time exactly what plan-compiler decides, and calling the
9
+ // same helpers (deepEqual, the code point bounds, multipleOfOk, the format
10
+ // functions and compiled patterns the plan already holds), so the answers
11
+ // cannot drift the way three hand-written generators did.
12
+ //
13
+ // The scope is deliberately narrow for now: value-level keywords, properties
14
+ // and additionalProperties, prefixItems and items, and the in-place
15
+ // applicators. Everything else, references, unevaluated*, contains,
16
+ // propertyNames, patternProperties, dependentSchemas, custom keywords, makes
17
+ // the compiler return null. It never emits a function with a keyword missing.
18
+
19
+ const { createInterpreter, _planInternals: I } = require('./interpreter');
20
+ const { Plan, deepEqual, cpAtLeast, cpAtMost, multipleOfOk, T_STRING, T_NUMBER, T_INTEGER, T_BOOLEAN, T_NULL, T_OBJECT, T_ARRAY } = I;
21
+
22
+ const DECLINE = Symbol('plan-source.decline');
23
+ const ALL_TYPES = T_STRING | T_NUMBER | T_INTEGER | T_BOOLEAN | T_NULL | T_OBJECT | T_ARRAY;
24
+
25
+ // The own-property test the generators use; see ownKeyExpr in js-compiler.js.
26
+ const HELPERS = 'const _hop=Object.prototype.hasOwnProperty;';
27
+
28
+ const PROTO_NAMES = new Set([...Object.getOwnPropertyNames(Object.prototype), '__proto__']);
29
+
30
+ function isPrimitive(x) {
31
+ return x === null || typeof x === 'string' || typeof x === 'boolean' || (typeof x === 'number' && isFinite(x));
32
+ }
33
+
34
+ function lit(x) {
35
+ return JSON.stringify(x);
36
+ }
37
+
38
+ function createCtx() {
39
+ return { n: 0, argNames: [], argVals: [], fns: [], fnFor: new Map() };
40
+ }
41
+
42
+ // A value the emitted code needs by reference: a function, a RegExp, a list.
43
+ function arg(ctx, value) {
44
+ const i = ctx.argVals.indexOf(value);
45
+ if (i !== -1) return ctx.argNames[i];
46
+ const name = '_a' + ctx.argVals.length;
47
+ ctx.argNames.push(name);
48
+ ctx.argVals.push(value);
49
+ return name;
50
+ }
51
+
52
+ function local(ctx) {
53
+ return '_v' + ctx.n++;
54
+ }
55
+
56
+ // JSON Schema type bits, exactly as dataBits() assigns them: a non-finite
57
+ // number has no type, so it matches neither number nor integer.
58
+ function typeTest(mask, v) {
59
+ const parts = [];
60
+ if (mask & T_STRING) parts.push(`typeof ${v}==='string'`);
61
+ if (mask & T_NUMBER) parts.push(`(typeof ${v}==='number'&&isFinite(${v}))`);
62
+ else if (mask & T_INTEGER) parts.push(`Number.isInteger(${v})`);
63
+ if (mask & T_BOOLEAN) parts.push(`typeof ${v}==='boolean'`);
64
+ if (mask & T_NULL) parts.push(`${v}===null`);
65
+ if ((mask & T_OBJECT) && (mask & T_ARRAY)) parts.push(`(typeof ${v}==='object'&&${v}!==null)`);
66
+ else if (mask & T_OBJECT) parts.push(`(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v}))`);
67
+ else if (mask & T_ARRAY) parts.push(`Array.isArray(${v})`);
68
+ return parts.length ? parts.join('||') : 'false';
69
+ }
70
+
71
+ // `plain` names a local holding `v.__proto__===Object.prototype`, read once per
72
+ // object. With the key a constant at the `in`, the engine keeps the check
73
+ // monomorphic; through a shared helper the key is a parameter and it is not.
74
+ function ownTest(v, key, plain) {
75
+ const k = lit(key);
76
+ return PROTO_NAMES.has(key) ? `_hop.call(${v},${k})` : `(${plain}?${k} in ${v}:_hop.call(${v},${k}))`;
77
+ }
78
+
79
+ const K_NUM = T_NUMBER | T_INTEGER;
80
+ // The body under a guard for one kind of value, given what the type check
81
+ // already established: unconditional when the value can only be that kind,
82
+ // nothing at all when it cannot be.
83
+ function guarded(known, kind, cond, body) {
84
+ if (!body) return '';
85
+ if ((known & kind) === 0) return '';
86
+ if ((known & ~kind) === 0) return body;
87
+ return `if(${cond}){${body}}`;
88
+ }
89
+
90
+ // Value-level keywords, in compileLeafV's order and with its meaning.
91
+ function emitLeaf(ctx, P, v, known) {
92
+ let out = '';
93
+ if (P.hasType) out += `if(!(${typeTest(P.typeMask, v)}))return false;`;
94
+ if (P.enum !== null) {
95
+ const vals = P.enum;
96
+ if (vals.length === 0) out += 'return false;';
97
+ else if (vals.every(isPrimitive)) out += `if(!(${vals.map((x) => `${v}===${lit(x)}`).join('||')}))return false;`;
98
+ else {
99
+ const a = arg(ctx, vals);
100
+ const de = arg(ctx, deepEqual);
101
+ const i = local(ctx);
102
+ out += `{let ${i}=0;for(;${i}<${a}.length;${i}++)if(${de}(${a}[${i}],${v}))break;if(${i}===${a}.length)return false}`;
103
+ }
104
+ }
105
+ if (P.hasConst) {
106
+ out += isPrimitive(P.const)
107
+ ? `if(${v}!==${lit(P.const)})return false;`
108
+ : `if(!${arg(ctx, deepEqual)}(${arg(ctx, P.const)},${v}))return false;`;
109
+ }
110
+ if (P.hasNumber) {
111
+ let n = '';
112
+ if (P.minimum !== undefined) n += `if(${v}<${lit(P.minimum)})return false;`;
113
+ if (P.maximum !== undefined) n += `if(${v}>${lit(P.maximum)})return false;`;
114
+ if (P.exclusiveMinimum !== undefined) n += `if(${v}<=${lit(P.exclusiveMinimum)})return false;`;
115
+ if (P.exclusiveMaximum !== undefined) n += `if(${v}>=${lit(P.exclusiveMaximum)})return false;`;
116
+ if (P.multipleOf !== undefined) n += `if(!${arg(ctx, multipleOfOk)}(${v},${lit(P.multipleOf)}))return false;`;
117
+ out += guarded(known, K_NUM, `typeof ${v}==='number'&&isFinite(${v})`, n);
118
+ }
119
+ if (P.hasString) {
120
+ let s = '';
121
+ if (P.minLength !== undefined) s += `if(!${arg(ctx, cpAtLeast)}(${v},${lit(P.minLength)}))return false;`;
122
+ if (P.maxLength !== undefined) s += `if(!${arg(ctx, cpAtMost)}(${v},${lit(P.maxLength)}))return false;`;
123
+ if (P.pattern !== null) s += `if(!${arg(ctx, P.pattern)}.test(${v}))return false;`;
124
+ if (P.formatFn !== null) s += `if(!${arg(ctx, P.formatFn)}(${v}))return false;`;
125
+ out += guarded(known, T_STRING, `typeof ${v}==='string'`, s);
126
+ }
127
+ if (P.minItems !== undefined || P.maxItems !== undefined || P.uniqueItems) {
128
+ let a = '';
129
+ if (P.minItems !== undefined) a += `if(${v}.length<${lit(P.minItems)})return false;`;
130
+ if (P.maxItems !== undefined) a += `if(${v}.length>${lit(P.maxItems)})return false;`;
131
+ if (P.uniqueItems) {
132
+ const de = arg(ctx, deepEqual);
133
+ const i = local(ctx), j = local(ctx);
134
+ a += `for(let ${i}=0;${i}<${v}.length;${i}++)for(let ${j}=${i}+1;${j}<${v}.length;${j}++)if(${de}(${v}[${i}],${v}[${j}]))return false;`;
135
+ }
136
+ out += guarded(known, T_ARRAY, `Array.isArray(${v})`, a);
137
+ }
138
+ return out;
139
+ }
140
+
141
+ // The object-level keywords of a node, value-level and structural together, so
142
+ // the prototype of the object is read once for all of them.
143
+ function emitObjectLeaf(ctx, P, v, plain) {
144
+ let o = '';
145
+ if (P.required !== null || P.minProperties !== undefined || P.maxProperties !== undefined || P.dependentRequired !== null) {
146
+ if (P.required !== null) for (const k of P.required) o += `if(!${ownTest(v, k, plain)})return false;`;
147
+ if (P.minProperties !== undefined) o += `if(Object.keys(${v}).length<${lit(P.minProperties)})return false;`;
148
+ if (P.maxProperties !== undefined) o += `if(Object.keys(${v}).length>${lit(P.maxProperties)})return false;`;
149
+ if (P.dependentRequired !== null) {
150
+ for (const [key, deps] of P.dependentRequired) {
151
+ o += `if(${ownTest(v, key, plain)}){${deps.map((dep) => `if(!${ownTest(v, dep, plain)})return false;`).join('')}}`;
152
+ }
153
+ }
154
+ }
155
+ return o;
156
+ }
157
+
158
+ // A subschema asked for a verdict of its own (a branch of anyOf/oneOf, the
159
+ // subject of not/if) becomes a named function, emitted once per plan.
160
+ function branchFn(ctx, node) {
161
+ if (node === true) return '_t';
162
+ if (node === false) return '_f';
163
+ let name = ctx.fnFor.get(node);
164
+ if (name !== undefined) return name;
165
+ name = '_b' + ctx.fnFor.size;
166
+ ctx.fnFor.set(node, name);
167
+ const body = emitNode(ctx, node, 'd');
168
+ ctx.fns.push(`function ${name}(d){${body}return true}`);
169
+ return name;
170
+ }
171
+
172
+ function emitNode(ctx, node, v) {
173
+ if (node === true) return '';
174
+ if (node === false) return 'return false;';
175
+ if (!(node instanceof Plan)) return '';
176
+ const P = node;
177
+ if (P.tracked || P.hasUnevaluated || P.hasCustom || P.macros !== null ||
178
+ P.contains !== undefined || P.propertyNames !== undefined || P.patternProperties !== null ||
179
+ P.dependentSchemas !== null || P.propertyDependencies !== null) {
180
+ throw DECLINE;
181
+ }
182
+ const known = P.hasType ? P.typeMask : ALL_TYPES;
183
+ let out = emitLeaf(ctx, P, v, known);
184
+
185
+ if (P.prefixItems !== null || P.items !== undefined) {
186
+ let a = '';
187
+ if (P.prefixItems !== null) {
188
+ P.prefixItems.forEach((child, i) => {
189
+ const x = local(ctx);
190
+ const body = emitNode(ctx, child, x);
191
+ if (body) a += `if(${v}.length>${i}){const ${x}=${v}[${i}];${body}}`;
192
+ });
193
+ }
194
+ if (P.items !== undefined) {
195
+ const start = P.prefixItems !== null ? P.prefixItems.length : 0;
196
+ const i = local(ctx);
197
+ const x = local(ctx);
198
+ const body = emitNode(ctx, P.items, x);
199
+ if (body) a += `for(let ${i}=${start};${i}<${v}.length;${i}++){const ${x}=${v}[${i}];${body}}`;
200
+ }
201
+ out += guarded(known, T_ARRAY, `Array.isArray(${v})`, a);
202
+ }
203
+
204
+ const plain = local(ctx);
205
+ let o = emitObjectLeaf(ctx, P, v, plain);
206
+ if (P.properties !== null || P.additionalProperties !== undefined) {
207
+ const declared = P.properties !== null ? [...P.properties.keys()] : [];
208
+ if (P.properties !== null) {
209
+ for (const [key, entry] of P.properties) {
210
+ const x = local(ctx);
211
+ const body = emitNode(ctx, entry.node, x);
212
+ if (body) o += `if(${ownTest(v, key, plain)}){const ${x}=${v}[${lit(key)}];${body}}`;
213
+ }
214
+ }
215
+ const ap = P.additionalProperties;
216
+ if (ap !== undefined && ap !== true) {
217
+ const k = local(ctx);
218
+ let isDeclared = 'false';
219
+ if (declared.length > 8) {
220
+ const set = Object.create(null);
221
+ for (const key of declared) set[key] = 1;
222
+ isDeclared = `${arg(ctx, set)}[${k}]===1`;
223
+ } else if (declared.length > 0) {
224
+ isDeclared = declared.map((key) => `${k}===${lit(key)}`).join('||');
225
+ }
226
+ let apBody;
227
+ if (ap === false) apBody = 'return false;';
228
+ else {
229
+ const x = local(ctx);
230
+ const body = emitNode(ctx, ap, x);
231
+ apBody = body ? `const ${x}=${v}[${k}];${body}` : '';
232
+ }
233
+ if (apBody) o += `for(const ${k} in ${v}){if(!${plain}&&!_hop.call(${v},${k}))continue;if(${isDeclared})continue;${apBody}}`;
234
+ }
235
+ }
236
+ if (o) out += guarded(known, T_OBJECT, `typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})`, `const ${plain}=${v}.__proto__===Object.prototype;${o}`);
237
+
238
+ if (P.allOf !== null) for (const child of P.allOf) out += emitNode(ctx, child, v);
239
+ if (P.anyOf !== null) out += `if(!(${P.anyOf.map((c) => `${branchFn(ctx, c)}(${v})`).join('||')}))return false;`;
240
+ if (P.oneOf !== null) {
241
+ const c = local(ctx);
242
+ out += `{let ${c}=0;${P.oneOf.map((b) => `if(${branchFn(ctx, b)}(${v})&&++${c}>1)return false;`).join('')}if(${c}!==1)return false}`;
243
+ }
244
+ if (P.not !== undefined) out += `if(${branchFn(ctx, P.not)}(${v}))return false;`;
245
+ if (P.if !== undefined) {
246
+ const t = P.then !== undefined ? emitNode(ctx, P.then, v) : '';
247
+ const e = P.else !== undefined ? emitNode(ctx, P.else, v) : '';
248
+ if (t || e) out += `if(${branchFn(ctx, P.if)}(${v})){${t}}else{${e}}`;
249
+ }
250
+ return out;
251
+ }
252
+
253
+ // Returns { fn, source } or null when the schema is outside what this emits.
254
+ function compileVerdict(schema, options) {
255
+ let interp;
256
+ try {
257
+ interp = createInterpreter(schema, options || {});
258
+ } catch {
259
+ return null;
260
+ }
261
+ const ctx = createCtx();
262
+ let body;
263
+ try {
264
+ body = emitNode(ctx, interp.rootNode, 'd');
265
+ } catch (e) {
266
+ if (e === DECLINE) return null;
267
+ throw e;
268
+ }
269
+ const source = HELPERS + 'function _t(){return true}function _f(){return false}' +
270
+ ctx.fns.join('') + `return function(d){${body}return true}`;
271
+ let fn;
272
+ try {
273
+ // eslint-disable-next-line no-new-func
274
+ fn = new Function(...ctx.argNames, source)(...ctx.argVals);
275
+ } catch {
276
+ return null;
277
+ }
278
+ return { fn, source };
279
+ }
280
+
281
+ module.exports = { compileVerdict };
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.31.2';
10
+ module.exports = '1.31.3';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.31.2",
3
+ "version": "1.31.3",
4
4
  "description": "JSON Schema validation that compiles for speed and still runs where code generation is blocked. Compiled and interpreted engines answer identically at 100% of the official suite. TypeScript inference, Standard Schema V1, and a build step that emits dependency-free modules.",
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_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_output_format.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_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_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_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_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_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_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_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 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_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_output_format.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_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_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_plan_source.js && node tests/test_fused_remove_additional.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_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_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_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_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 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",
@@ -121,13 +121,13 @@
121
121
  "LICENSE"
122
122
  ],
123
123
  "optionalDependencies": {
124
- "@ata-validator/native-darwin-arm64": "1.31.2",
125
- "@ata-validator/native-darwin-x64": "1.31.2",
126
- "@ata-validator/native-linux-arm64-gnu": "1.31.2",
127
- "@ata-validator/native-linux-arm64-musl": "1.31.2",
128
- "@ata-validator/native-linux-x64-gnu": "1.31.2",
129
- "@ata-validator/native-linux-x64-musl": "1.31.2",
130
- "@ata-validator/native-win32-x64": "1.31.2"
124
+ "@ata-validator/native-darwin-arm64": "1.31.3",
125
+ "@ata-validator/native-darwin-x64": "1.31.3",
126
+ "@ata-validator/native-linux-arm64-gnu": "1.31.3",
127
+ "@ata-validator/native-linux-arm64-musl": "1.31.3",
128
+ "@ata-validator/native-linux-x64-gnu": "1.31.3",
129
+ "@ata-validator/native-linux-x64-musl": "1.31.3",
130
+ "@ata-validator/native-win32-x64": "1.31.3"
131
131
  },
132
132
  "peerDependencies": {
133
133
  "yaml": "^2.0.0"