ata-validator 1.63.1 → 1.63.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/bin/ata.js CHANGED
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  const { env } = require('../lib/env');
4
+ // Text that comes from a schema file (a $ref, a keyword name in a warning) is
5
+ // written with its control characters as escapes, as `ata validate` writes
6
+ // it: a schema could otherwise clear the screen or forge an `ata:` line.
7
+ const term = (t) => require('../lib/diagnose').term(String(t));
4
8
 
5
9
  const fs = require('fs');
6
10
  const path = require('path');
@@ -299,7 +303,7 @@ function cmdCompile(args) {
299
303
  for (const w of compileWarnings) reportCompileError(input, w);
300
304
  process.exit(1);
301
305
  }
302
- for (const w of compileWarnings) process.stderr.write(`ata: warning: ${input}: ${w}\n`);
306
+ for (const w of compileWarnings) process.stderr.write(`ata: warning: ${input}: ${term(w)}\n`);
303
307
  }
304
308
  if (!src) {
305
309
  reportCompileError(input, 'schema is too complex for standalone compilation');
@@ -397,7 +401,7 @@ function cmdBuild(args) {
397
401
  for (const c of report.compiled) {
398
402
  process.stdout.write(`ata: ${c.input} -> ${c.output} (${c.bytes.toLocaleString()} bytes${c.errors === 'compact' ? ', shared errors' : c.errors === 'schema' ? ', schema module' : ''})\n`);
399
403
  for (const t of (report.bunTargets || []).filter((t) => t.input === c.input)) process.stdout.write(`ata: bun target -> ${t.output} (for bun build --compile --aot)\n`);
400
- for (const d of (report.bunDeclined || []).filter((d) => d.input === c.input)) process.stderr.write(`ata: warning: ${d.input}: ${d.reason}\n`);
404
+ for (const d of (report.bunDeclined || []).filter((d) => d.input === c.input)) process.stderr.write(`ata: warning: ${d.input}: ${term(d.reason)}\n`);
401
405
  }
402
406
  if (report.runtime) {
403
407
  process.stdout.write(`ata: error runtime -> ${report.runtime.output} (${report.runtime.bytes.toLocaleString()} bytes, shared by the modules above)\n`);
@@ -412,7 +416,7 @@ function cmdBuild(args) {
412
416
  noDetail++;
413
417
  process.stderr.write(`ata: warning ${w.input}: no error detail, failures report the single ATA9000 error (the verdict is exact)\n`);
414
418
  } else {
415
- process.stderr.write(`ata: warning ${w.input}: ${w.warning}\n`);
419
+ process.stderr.write(`ata: warning ${w.input}: ${term(w.warning)}\n`);
416
420
  }
417
421
  }
418
422
  if (noDetail > 0 && args.opts.mode !== 'schema') {
@@ -422,7 +426,7 @@ function cmdBuild(args) {
422
426
  process.stdout.write(`ata: cached ${c.input}\n`);
423
427
  }
424
428
  for (const s of report.skipped) {
425
- process.stdout.write(`ata: skipped ${s.input}: ${s.reason}\n`);
429
+ process.stdout.write(`ata: skipped ${s.input}: ${term(s.reason)}\n`);
426
430
  }
427
431
  for (const f of report.failed) {
428
432
  reportCompileError(f.input, f.error);
@@ -9,9 +9,12 @@
9
9
  // module imports nothing, so the generated code cannot require this file; it
10
10
  // carries a copy emitted by `embedSource()` from these very functions instead
11
11
  // of a second hand-written one. That is why the internals are written in the
12
- // positional form the generated code calls, and why `__ataCollapse` may close
13
- // over nothing but `__ataScore` and the severity table: whatever it reaches
14
- // has to be in the embed too.
12
+ // positional form the generated code calls, and why each function reaches the
13
+ // others only through properties on itself (`__ataCollapse.F`, `.M`,
14
+ // `__ataScore.S`): a bundler that minifies names renames a function and every
15
+ // call to it, and the text toString() gives then called names the embed did
16
+ // not declare. A function's reference to its own name stays consistent, and
17
+ // property names are not renamed.
15
18
 
16
19
  // How much a failing keyword says about intent. A branch that fails on `type`
17
20
  // was probably not the branch the author meant; one that fails on `minLength`
@@ -35,10 +38,12 @@ const __ATA_SEVERITY = {
35
38
 
36
39
  function __ataScore (errs) {
37
40
  if (!errs || !errs.length) return 0;
41
+ const sev = __ataScore.S;
38
42
  let s = 0;
39
- for (const e of errs) s += __ATA_SEVERITY[e.keyword] || 4;
43
+ for (const e of errs) s += sev[e.keyword] || 4;
40
44
  return errs.length * 100 + s; // primary: count, secondary: severity
41
45
  }
46
+ __ataScore.S = __ATA_SEVERITY;
42
47
 
43
48
  // `br` is one entry per branch in declaration order: {valid, errors} and a
44
49
  // `title`, or the titles in `titles` when given (the generated code passes
@@ -68,7 +73,7 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
68
73
  if (pass.length > 1) {
69
74
  const passIdx = [];
70
75
  for (let i = 0; i < br.length; i++) if (br[i].valid) passIdx.push(i);
71
- return __ataMulti(passIdx, br.length, pp, sp, o);
76
+ return __ataCollapse.M(passIdx, br.length, pp, sp, o);
72
77
  }
73
78
  } else if (pass.length >= 1) {
74
79
  return null;
@@ -76,7 +81,7 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
76
81
  let bi = 0;
77
82
  let bs = Infinity;
78
83
  for (let i = 0; i < br.length; i++) {
79
- const s = __ataScore(br[i].errors);
84
+ const s = __ataCollapse.F(br[i].errors);
80
85
  if (s < bs) { bs = s; bi = i; }
81
86
  }
82
87
  const best = br[bi];
@@ -97,6 +102,9 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
97
102
  return none;
98
103
  }
99
104
 
105
+ __ataCollapse.M = __ataMulti;
106
+ __ataCollapse.F = __ataScore;
107
+
100
108
  // The named-argument form the interpreter and the tests call.
101
109
  function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPath, ordinal }) {
102
110
  return __ataCollapse(keyword, branchResults, parentPath, parentSchemaPath, ordinal === undefined ? null : ordinal);
@@ -107,9 +115,9 @@ function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPat
107
115
  // cannot miss the other. `tests/test_branch_collapse.js` runs both.
108
116
  function embedSource () {
109
117
  return 'const __ATA_SEVERITY=' + JSON.stringify(__ATA_SEVERITY) + ';' +
110
- 'const __ataScore=' + __ataScore.toString() + ';' +
118
+ 'const __ataScore=' + __ataScore.toString() + ';__ataScore.S=__ATA_SEVERITY;' +
111
119
  'const __ataMulti=' + __ataMulti.toString() + ';' +
112
- 'const __ataCollapse=' + __ataCollapse.toString() + ';';
120
+ 'const __ataCollapse=' + __ataCollapse.toString() + ';__ataCollapse.M=__ataMulti;__ataCollapse.F=__ataScore;';
113
121
  }
114
122
 
115
123
  module.exports = { collapseBranches, scoreBranch: __ataScore, SEVERITY: __ATA_SEVERITY, embedSource, _collapse: __ataCollapse, _multi: __ataMulti };
package/lib/draft7.js CHANGED
@@ -20,6 +20,7 @@ function isDraft6(schema) {
20
20
  // of its own, which is how a retrieved schema inherits the root's draft.
21
21
  // `draft6` says the document, or the root it inherits from, is draft-06.
22
22
  // `draft4`: draft-04, read as draft-06 plus its own differences (below).
23
+ const PLAIN_NAME_ID = /^#[A-Za-z][A-Za-z0-9_.:-]*$/
23
24
  function normalizeDraft7(schema, force, draft6, draft4) {
24
25
  if (!force && !isDraft7(schema)) return schema
25
26
  const d4 = draft4 === true || (typeof schema.$schema === 'string' && DRAFT4_SCHEMAS.has(schema.$schema))
@@ -57,7 +58,7 @@ function _normalize(schema, d6, d4) {
57
58
 
58
59
  // A fragment-only `$id` is a plain-name anchor in draft-07; 2020-12 spells
59
60
  // it `$anchor`, which every engine already resolves.
60
- if (typeof schema.$id === 'string' && /^#[A-Za-z][A-Za-z0-9_.:-]*$/.test(schema.$id)) {
61
+ if (typeof schema.$id === 'string' && PLAIN_NAME_ID.test(schema.$id)) {
61
62
  if (schema.$anchor === undefined) schema.$anchor = schema.$id.slice(1)
62
63
  delete schema.$id
63
64
  }
@@ -214,6 +215,18 @@ function _normalizeNullable(schema, st) {
214
215
  }
215
216
  }
216
217
  if ('nullable' in schema) delete schema.nullable
218
+ // A fragment-only `$id` (`#name`) is draft-07's spelling of an anchor, and
219
+ // schemas that declare no dialect are written with it too. normalizeDraft7
220
+ // folds it into `$anchor` under a declared older dialect; without one each
221
+ // engine read it its own way: the code generators took it as an anchor on
222
+ // the root and the root's definitions only, and where such a schema
223
+ // referred to itself they wrote nothing at the cycle, so every constraint
224
+ // past the first level was dropped and the document accepted. Folded here,
225
+ // on the walk every dialect runs, there is one spelling for every engine.
226
+ if (typeof schema.$id === 'string' && PLAIN_NAME_ID.test(schema.$id)) {
227
+ if (schema.$anchor === undefined) schema.$anchor = schema.$id.slice(1)
228
+ delete schema.$id
229
+ }
217
230
  // A JSON Pointer in a URI fragment is percent-encoded, so a definition named
218
231
  // `Node<Row>` is reached by `#/definitions/Node%3CRow%3E` (RFC 6901, section
219
232
  // 6). The interpreter decodes the fragment; the code generator compared the
package/lib/ir/emit.mjs CHANGED
@@ -16,8 +16,16 @@ const FAST = {
16
16
  pattern: (n, x) => compilePatternInline(n, x),
17
17
  // A string has between length/2 and length code points, so the length
18
18
  // alone settles most cases and the count runs only near the bound.
19
- minLength: (n, x) => `${x}.length>=${2 * n}||${x}.length>=${n}&&cpLen(${x})>=${n}`,
20
- maxLength: (n, x) => `${x}.length<=${n}||${x}.length<=${2 * n}&&cpLen(${x})<=${n}`,
19
+ // The bound is written into the module as a number, so it has to be one: a
20
+ // schema that gave a string here had it written as source, and what the
21
+ // string said ran in whatever imported the module. Refused, as the closure
22
+ // writer refuses it.
23
+ minLength: (n, x) => { const b = bound(n, 'minLength'); return `${x}.length>=${2 * b}||${x}.length>=${b}&&cpLen(${x})>=${b}` },
24
+ maxLength: (n, x) => { const b = bound(n, 'maxLength'); return `${x}.length<=${b}||${x}.length<=${2 * b}&&cpLen(${x})<=${b}` },
25
+ }
26
+ function bound (n, k) {
27
+ if (typeof n !== 'number' || !Number.isFinite(n)) throw new Error(`ir: ${k} is not a number`)
28
+ return n
21
29
  }
22
30
  // enum and const with only primitive values need no deep equality: the
23
31
  // value is one of them exactly when it is === one of them (JSON numbers have
@@ -40,7 +48,11 @@ function inline (fn, vExpr, nExpr) {
40
48
  const src = fn.toString()
41
49
  const m = /^\(\s*v\s*(?:,\s*n\s*)?\)\s*=>\s*([\s\S]+)$/.exec(src)
42
50
  if (!m) throw new Error('ir: kernel ok() must be (v, n) => expression: ' + src)
43
- return '(' + m[1].replace(/\bv\b/g, vExpr).replace(/\bn\b/g, nExpr) + ')'
51
+ // One pass, by function: the expressions carry text from the schema (a
52
+ // property name inside `d["..."]`), which a second pass would search for
53
+ // `n`, and which replace() would search for `$&` and its kin if it were
54
+ // given as the replacement itself.
55
+ return '(' + m[1].replace(/\b[vn]\b/g, (x) => x === 'v' ? vExpr : nExpr) + ')'
44
56
  }
45
57
 
46
58
  // How many names an object must list before a module visits the object's own
@@ -677,14 +689,16 @@ export function specialize (schema, opts = {}) {
677
689
  const own = typeof opts.fillSource === 'function' && !opts.schemas ? opts.fillSource(schema) : null
678
690
  if (typeof own === 'string') {
679
691
  return src
680
- .replace('export function isValid(d){return ', `function FILL(d){\n${own}\n}\nexport function isValid(d){FILL(d);return `)
692
+ .replace('export function isValid(d){return ', () => `function FILL(d){\n${own}\n}\nexport function isValid(d){FILL(d);return `)
681
693
  .replace('export function validate(d){', 'export function validate(d){FILL(d);')
682
694
  }
683
695
  // The shared default pass (lib/defaults.js), built from the normalized
684
696
  // schema at load.
685
697
  return src
686
698
  .replace(/^import \{([^}]*)\} from /m, (m, list) => `import {${['buildDefaultsApplier', ...list.split(',')].filter(Boolean).sort().join(',')}} from `)
687
- .replace('export function isValid(d){return ', `const FILL=buildDefaultsApplier(${JSON.stringify(ir.schema)})\nexport function isValid(d){FILL(d);return `)
699
+ // By function: a replacement given as text is searched for `$&`, `` $` ``,
700
+ // `$'` and `$$`, and a schema's descriptions and defaults hold them.
701
+ .replace('export function isValid(d){return ', () => `const FILL=buildDefaultsApplier(${JSON.stringify(ir.schema)})\nexport function isValid(d){FILL(d);return `)
688
702
  .replace('export function validate(d){', 'export function validate(d){FILL(d);')
689
703
  }
690
704
 
@@ -683,7 +683,7 @@ function countingBody (body) {
683
683
  j++
684
684
  }
685
685
  if (depth !== 0) return null
686
- body = body.slice(0, b) + (unknown ? 'return -1' : '(_n++,_s+=4)') + body.slice(j)
686
+ body = body.slice(0, b) + (unknown ? 'return -1' : '(_scn++,_scs+=4)') + body.slice(j)
687
687
  }
688
688
  if (/\b(?:_defE\d|__ataCollapse|__ataMulti|_brf\d|_bsf\d)/.test(body)) return null
689
689
  const SEV = require('./branch-collapse').SEVERITY
@@ -691,7 +691,8 @@ function countingBody (body) {
691
691
  let i = 0
692
692
  const head = 'const _e=[];'
693
693
  if (!body.startsWith(head)) return null
694
- out += 'let _n=0,_s=0;'
694
+ // Not `_n`: the inline format checks declare it as a const.
695
+ out += 'let _scn=0,_scs=0;'
695
696
  i = head.length
696
697
  while (i < body.length) {
697
698
  const ch = body[i]
@@ -711,13 +712,13 @@ function countingBody (body) {
711
712
  // The literal may still carry its `code` (the shape pass strips it).
712
713
  const m = /^\{(?:code:'ATA\d{4}',)?keyword:'([A-Za-z$][\w$]*)',/.exec(arg)
713
714
  if (m === null) return null
714
- out += `(_n++,_s+=${SEV[m[1]] || 4})`
715
+ out += `(_scn++,_scs+=${SEV[m[1]] || 4})`
715
716
  i = j
716
717
  continue
717
718
  }
718
- if (body.startsWith('return{valid:false,errors:_e}', i)) { out += 'return _n*100+_s'; i += 29; continue }
719
- if (body.startsWith('return{valid:_e.length===0,errors:_e}', i)) { out += 'return _n*100+_s'; i += 37; continue }
720
- if (body.startsWith('_e.length', i)) { out += '_n'; i += 9; continue }
719
+ if (body.startsWith('return{valid:false,errors:_e}', i)) { out += 'return _scn*100+_scs'; i += 29; continue }
720
+ if (body.startsWith('return{valid:_e.length===0,errors:_e}', i)) { out += 'return _scn*100+_scs'; i += 37; continue }
721
+ if (body.startsWith('_e.length', i)) { out += '_scn'; i += 9; continue }
721
722
  out += ch
722
723
  i++
723
724
  }
@@ -1147,11 +1148,16 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
1147
1148
  const expected = types.join(', ')
1148
1149
  // As written: a one-element array stays an array, as the interpreted engine
1149
1150
  // reports it.
1150
- const expectedParam = !Array.isArray(schema.type) ? `'${types[0]}'` : JSON.stringify(types)
1151
+ // Escaped like every other schema string written into this source: a
1152
+ // type name is the schema author's text, and a quote in one that is not
1153
+ // a JSON Schema type closed the literal and ran what followed as code,
1154
+ // in the process that compiled the schema and in the module written
1155
+ // from it.
1156
+ const expectedParam = !Array.isArray(schema.type) ? `'${esc(String(types[0]))}'` : JSON.stringify(types)
1151
1157
  {
1152
1158
  const typeSp = `${schemaPrefix}/type`
1153
1159
  const lit = buildErrorLiteral({ keyword: 'type', schemaPath: typeSp, sourceMap: ctx.sourceMap })
1154
- lines.push(`if(!(${conds.join('||')})){_e.push({code:'${lit.codeStr}',keyword:'type',instancePath:${pathExpr||'""'},schemaPath:'${typeSp}'${ordinalField(ctx, `${typeSp}`)},params:{type:${expectedParam}},message:'must be ${expected}',docUrl:'${lit.docUrl}'${lit.frame}});if(!_all)return{valid:false,errors:_e}}`)
1160
+ lines.push(`if(!(${conds.join('||')})){_e.push({code:'${lit.codeStr}',keyword:'type',instancePath:${pathExpr||'""'},schemaPath:'${typeSp}'${ordinalField(ctx, `${typeSp}`)},params:{type:${expectedParam}},message:'must be ${esc(expected)}',docUrl:'${lit.docUrl}'${lit.frame}});if(!_all)return{valid:false,errors:_e}}`)
1155
1161
  }
1156
1162
  }
1157
1163
 
@@ -1270,7 +1276,11 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
1270
1276
  // let every later statement of the same check push the same error again.
1271
1277
  if (fc) {
1272
1278
  const ri = ctx.varCounter++
1273
- const fmtCode = fc(v, isStr, ctx).replace(/return false/g, `{${failPush};break _fmt${ri}}`)
1279
+ // By function: the pushed error carries the property's name in its
1280
+ // paths, and a replacement given as text is searched for `$&` and its
1281
+ // kin (a property named `a$&b` reported `/areturn falseb`).
1282
+ const fmtFail = `{${failPush};break _fmt${ri}}`
1283
+ const fmtCode = fc(v, isStr, ctx).replace(/return false/g, () => fmtFail)
1274
1284
  lines.push(`_fmt${ri}:{${fmtCode}}`)
1275
1285
  } else if (uf !== null) {
1276
1286
  // User-supplied format checker on the error path: the same closure
@@ -2038,7 +2048,9 @@ TYPE_KEYWORDS.integer = TYPE_KEYWORDS.number
2038
2048
  const NOT_VALIDATING = new Set(['$schema', '$id', '$anchor', '$comment', '$defs', 'definitions', 'title', 'description', 'default', 'examples', 'deprecated', 'readOnly', 'writeOnly', '$vocabulary'])
2039
2049
  function typeMismatchRest(schema, types) {
2040
2050
  const drop = new Set(['type', 'enum', 'const', 'anyOf', 'oneOf'])
2041
- for (const t of types) for (const k of (TYPE_KEYWORDS[t] || [])) drop.add(k)
2051
+ // Own names only: a type called `constructor` or `__proto__` read
2052
+ // Object.prototype's member here and threw while compiling.
2053
+ for (const t of types) if (Object.prototype.hasOwnProperty.call(TYPE_KEYWORDS, t)) for (const k of TYPE_KEYWORDS[t]) drop.add(k)
2042
2054
  // A built-in format is written for strings only (`typeof v==='string'&&`
2043
2055
  // off the string path), so for a value that failed `type: string` it can
2044
2056
  // never fire: carried here it was dead code in every module that checks one.
@@ -2435,11 +2447,16 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
2435
2447
  const expected = types.join(', ')
2436
2448
  // As written: a one-element array stays an array, as the interpreted engine
2437
2449
  // reports it.
2438
- const expectedParam = !Array.isArray(schema.type) ? `'${types[0]}'` : JSON.stringify(types)
2450
+ // Escaped like every other schema string written into this source: a
2451
+ // type name is the schema author's text, and a quote in one that is not
2452
+ // a JSON Schema type closed the literal and ran what followed as code,
2453
+ // in the process that compiled the schema and in the module written
2454
+ // from it.
2455
+ const expectedParam = !Array.isArray(schema.type) ? `'${esc(String(types[0]))}'` : JSON.stringify(types)
2439
2456
  // Type check: push error but continue — wrap remaining in type-success block
2440
2457
  const typeOk = `_tok${ctx.varCounter++}`
2441
2458
  lines.push(`const ${typeOk}=${conds.join('||')}`)
2442
- lines.push(`if(!${typeOk}){${fail('type', 'type', `{type:${expectedParam}}`, `'must be ${expected}'`)}}`)
2459
+ lines.push(`if(!${typeOk}){${fail('type', 'type', `{type:${expectedParam}}`, `'must be ${esc(expected)}'`)}}`)
2443
2460
  // Subsequent optimized code runs inside if(typeOk){...}
2444
2461
  if (types.length === 1) {
2445
2462
  isObj = types[0] === 'object'
@@ -2551,7 +2568,8 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
2551
2568
  // Same rule as the error codegen: one violation, one error. The first
2552
2569
  // failing statement pushes and leaves the labeled block.
2553
2570
  const ri = ctx.varCounter++
2554
- const code = fc(v, isStr, ctx).replace(/return false/g, `{${fail('format', 'format', `{format:'${esc(schema.format)}'}`, `'must match format "${esc(schema.format)}"'`)};break _fmt${ri}}`)
2571
+ const fmtFail = `{${fail('format', 'format', `{format:'${esc(schema.format)}'}`, `'must match format "${esc(schema.format)}"'`)};break _fmt${ri}}`
2572
+ const code = fc(v, isStr, ctx).replace(/return false/g, () => fmtFail)
2555
2573
  lines.push(`_fmt${ri}:{${code}}`)
2556
2574
  } else if (uf !== null) {
2557
2575
  const closureName = bindUserFormat(ctx, schema.format, uf)
@@ -20,18 +20,16 @@ const seqTag = () => `/*${++_compileSeq}*/`
20
20
  // forms answers a different question and costs a string per call.
21
21
  const DEQ_HELPER = "function _deq(a,b){if(a===b)return true;if(a===null||b===null||typeof a!=='object'||typeof b!=='object')return false;var aa=Array.isArray(a);if(aa!==Array.isArray(b))return false;var i,k,x,y;if(aa){if(a.length!==b.length)return false;for(i=a.length;i--!==0;)if(!_deq(a[i],b[i]))return false;return true}var ka=Object.keys(a);if(ka.length!==Object.keys(b).length)return false;for(i=ka.length;i--!==0;){k=ka[i];if(!Object.prototype.hasOwnProperty.call(b,k))return false;x=a[k];y=b[k];if(x!==y&&(x===null||y===null||typeof x!=='object'||typeof y!=='object'))return false}for(i=ka.length;i--!==0;){k=ka[i];x=a[k];if(x!==null&&typeof x==='object'&&!_deq(x,b[k]))return false}return true}"
22
22
 
23
- // uniqueItems helpers, emitted from the functions the interpreted engine
23
+ // uniqueItems helpers, the text of the functions the interpreted engine
24
24
  // calls (lib/unique-items.js): `_uq` answers whether every item is distinct.
25
- // Loaded at the first schema with uniqueItems, not on every cold start.
26
- // Comment lines and indentation are dropped: the text lands in every
27
- // generated function and ahead-of-time module that checks uniqueItems.
25
+ // The text is written at build time (lib/unique-items-source.js) rather than
26
+ // read with String(fn) here: in a bundle whose identifiers were minified the
27
+ // functions and their calls to each other had other names, and generated
28
+ // code that called `_uq` threw on every array. Loaded at the first schema
29
+ // with uniqueItems, not on every cold start.
28
30
  let _uqSources = null
29
31
  function uqSources () {
30
- if (_uqSources === null) {
31
- const u = require('./unique-items')
32
- const compact = (fn) => String(fn).replace(/\n\s*\/\/[^\n]*/g, '').replace(/\n\s*/g, '\n')
33
- _uqSources = { uq: compact(u._uqh) + compact(u._uq), uqp: 'var _uqpI=0;' + compact(u._uqp) }
34
- }
32
+ if (_uqSources === null) _uqSources = require('./unique-items-source')
35
33
  return _uqSources
36
34
  }
37
35
 
@@ -832,9 +830,9 @@ function resolveRef(ref, defs, schemaMap) {
832
830
 
833
831
  function buildTypeCheck(types) {
834
832
  if (types.length === 1) {
835
- return TYPE_CHECKS[types[0]] || (() => true)
833
+ return typeCheckOf(types[0]) || (() => true)
836
834
  }
837
- const fns = types.map(t => TYPE_CHECKS[t]).filter(Boolean)
835
+ const fns = types.map(typeCheckOf).filter(Boolean)
838
836
  return (d) => {
839
837
  for (let i = 0; i < fns.length; i++) {
840
838
  if (fns[i](d)) return true
@@ -902,6 +900,10 @@ function multipleOfBad(v, m) {
902
900
  return `!(Number.isInteger(${v}/${m})||Math.abs(${v}/${m}-Math.round(${v}/${m}))<1e-9)`
903
901
  }
904
902
 
903
+ // Own names only: a type called `__proto__` or `valueOf` read
904
+ // Object.prototype's member and was handed out as the check, and the
905
+ // Validator then threw on the first document (`_fast is not a function`).
906
+ const typeCheckOf = (t) => (Object.prototype.hasOwnProperty.call(TYPE_CHECKS, t) ? TYPE_CHECKS[t] : undefined)
905
907
  const TYPE_CHECKS = {
906
908
  string: (d) => typeof d === 'string',
907
909
  number: (d) => Number.isFinite(d),
@@ -3467,7 +3469,7 @@ function tryGenCombined(schema, access, ctx) {
3467
3469
  // or similar), skip the `{const _v = access}` wrapping and use it directly.
3468
3470
  const isIdent = /^_[a-zA-Z]\w*$/.test(access)
3469
3471
  const bind = (conds) => isIdent
3470
- ? `if(${conds.join('||').replace(/\b_v\b/g, access)})return false`
3472
+ ? `if(${conds.join('||').replace(/\b_v\b/g, () => access)})return false`
3471
3473
  : `{const _v=${access};if(${conds.join('||')})return false}`
3472
3474
 
3473
3475
  if (t === 'string') {
@@ -4198,7 +4200,11 @@ function genDynamicRefNode(schema, v, lines, ctx, knownType) {
4198
4200
  function genUniqueItemsNode(schema, v, lines, ctx, knownType, hoisted, isArr, types) {
4199
4201
  const si = ctx.varCounter++
4200
4202
  const itemType = schema.items && typeof schema.items === 'object' && schema.items.type
4201
- const isPrimItems = itemType === 'string' || itemType === 'number' || itemType === 'integer'
4203
+ // `items` speaks for the elements after the tuple only: with `prefixItems`
4204
+ // the first positions may hold objects, and comparing those by identity
4205
+ // took two equal objects for different ones and accepted the array.
4206
+ const noTuple = !Array.isArray(schema.prefixItems) || schema.prefixItems.length === 0
4207
+ const isPrimItems = noTuple && (itemType === 'string' || itemType === 'number' || itemType === 'integer')
4202
4208
  const maxItems = schema.maxItems
4203
4209
  // Small primitive arrays (maxItems <= 16): nested loop is 6x faster than Set
4204
4210
  // No allocation, no hash computation — just direct === comparison
@@ -4339,7 +4345,7 @@ function inlineBranchBody (body, param, label, local) {
4339
4345
  else return null
4340
4346
  if (/\breturn\b(?! false\b)/.test(core)) return null
4341
4347
  const re = new RegExp('\\b' + param + '\\b', 'g')
4342
- return core.replace(/\breturn false\b/g, 'break ' + label).replace(re, local)
4348
+ return core.replace(/\breturn false\b/g, () => 'break ' + label).replace(re, () => local)
4343
4349
  }
4344
4350
 
4345
4351
  // A single-branch anyOf or oneOf holds exactly when its branch does, so the
@@ -5169,7 +5175,8 @@ function genUnevaluatedPropertiesNode(schema, v, lines, ctx, knownType, isObj, h
5169
5175
  for (const [trigger, depSchema] of Object.entries(schema.dependentSchemas)) {
5170
5176
  const depResult = collectEvaluated(depSchema, ctx.schemaMap, ctx.rootDefs)
5171
5177
  if (depResult.props && depResult.props.length > 0) {
5172
- lines.push(`if(${ownKeyExpr(ctx, v, trigger)}){${depResult.props.map(k => `${evVar}[${JSON.stringify(k)}]=1`).join(';')}}`)
5178
+ // Asked of an object only: untyped, the document may be null.
5179
+ lines.push(`if(${isObj ? '' : `typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&`}${ownKeyExpr(ctx, v, trigger)}){${depResult.props.map(k => `${evVar}[${JSON.stringify(k)}]=1`).join(';')}}`)
5173
5180
  }
5174
5181
  }
5175
5182
  const inner = `for(var _k in ${v}){if(!${evVar}[_k]&&${ownFail(ctx, v)})return false}`
@@ -5364,7 +5371,8 @@ function genUnevaluatedItemsNode(schema, v, lines, ctx, knownType, isArr) {
5364
5371
  lines.push(`{let ${evVar}=${baseIdx}`)
5365
5372
  lines.push(`const _bf${bfi}=[${fns.join(',')}]`)
5366
5373
  const maxExprs = branchMaxIdx.map((m, i) => {
5367
- if (branchAllItems[i]) return `_bi===${i}?${v}.length`
5374
+ // An array's length only: `items` passes null too.
5375
+ if (branchAllItems[i]) return isArr ? `_bi===${i}?${v}.length` : `_bi===${i}?(Array.isArray(${v})?${v}.length:0)`
5368
5376
  return `_bi===${i}?${Math.max(m, baseIdx)}`
5369
5377
  }).join(':') + `:${baseIdx}`
5370
5378
  if (branchKeyword === 'oneOf') {
@@ -5543,6 +5551,26 @@ const FORMAT_CODEGEN = Object.assign(Object.create(null), {
5543
5551
  hostname: (...a) => getFormats().hostnameSource(...a),
5544
5552
  })
5545
5553
 
5554
+ // An inline check declares locals (`_n`, `_c2`), and the generator names the
5555
+ // value it reads (`_c2` for a property). Where the two are one name the
5556
+ // check's declaration shadows its own input and throws, so that value is
5557
+ // renamed first. Only then; tests/test_no_throw_on_any_document.js.
5558
+ function declares (code, name) {
5559
+ for (const before of ['const ', 'let ', 'var ', ',']) {
5560
+ const text = before + name + '='
5561
+ for (let i = code.indexOf(text); i !== -1; i = code.indexOf(text, i + 1)) if (code[i + text.length] !== '=') return true
5562
+ }
5563
+ return false
5564
+ }
5565
+ for (const name of Object.keys(FORMAT_CODEGEN)) {
5566
+ const write = FORMAT_CODEGEN[name]
5567
+ FORMAT_CODEGEN[name] = (v, isStr, ctx) => {
5568
+ const code = write(v, isStr, ctx)
5569
+ if (!/^[A-Za-z_$][\w$]*$/.test(v) || !declares(code, v)) return code
5570
+ return `{const _fmv=${v};${write('_fmv', isStr, ctx)}}`
5571
+ }
5572
+ }
5573
+
5546
5574
  // `^[class]+$`, `^[class]{m,n}$` and `^[class]{n}$` for a class of ASCII
5547
5575
  // letters, digits, `_` and `-`: a function in the preamble that reads each
5548
5576
  // character once and looks it up in a 128-entry table. The inline form below
@@ -9,9 +9,12 @@
9
9
  // module imports nothing, so the generated code cannot require this file; it
10
10
  // carries a copy emitted by `embedSource()` from these very functions instead
11
11
  // of a second hand-written one. That is why the internals are written in the
12
- // positional form the generated code calls, and why `__ataCollapse` may close
13
- // over nothing but `__ataScore` and the severity table: whatever it reaches
14
- // has to be in the embed too.
12
+ // positional form the generated code calls, and why each function reaches the
13
+ // others only through properties on itself (`__ataCollapse.F`, `.M`,
14
+ // `__ataScore.S`): a bundler that minifies names renames a function and every
15
+ // call to it, and the text toString() gives then called names the embed did
16
+ // not declare. A function's reference to its own name stays consistent, and
17
+ // property names are not renamed.
15
18
 
16
19
  // How much a failing keyword says about intent. A branch that fails on `type`
17
20
  // was probably not the branch the author meant; one that fails on `minLength`
@@ -35,10 +38,12 @@ const __ATA_SEVERITY = {
35
38
 
36
39
  function __ataScore (errs) {
37
40
  if (!errs || !errs.length) return 0;
41
+ const sev = __ataScore.S;
38
42
  let s = 0;
39
- for (const e of errs) s += __ATA_SEVERITY[e.keyword] || 4;
43
+ for (const e of errs) s += sev[e.keyword] || 4;
40
44
  return errs.length * 100 + s; // primary: count, secondary: severity
41
45
  }
46
+ __ataScore.S = __ATA_SEVERITY;
42
47
 
43
48
  // `br` is one entry per branch in declaration order: {valid, errors} and a
44
49
  // `title`, or the titles in `titles` when given (the generated code passes
@@ -68,7 +73,7 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
68
73
  if (pass.length > 1) {
69
74
  const passIdx = [];
70
75
  for (let i = 0; i < br.length; i++) if (br[i].valid) passIdx.push(i);
71
- return __ataMulti(passIdx, br.length, pp, sp, o);
76
+ return __ataCollapse.M(passIdx, br.length, pp, sp, o);
72
77
  }
73
78
  } else if (pass.length >= 1) {
74
79
  return null;
@@ -76,7 +81,7 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
76
81
  let bi = 0;
77
82
  let bs = Infinity;
78
83
  for (let i = 0; i < br.length; i++) {
79
- const s = __ataScore(br[i].errors);
84
+ const s = __ataCollapse.F(br[i].errors);
80
85
  if (s < bs) { bs = s; bi = i; }
81
86
  }
82
87
  const best = br[bi];
@@ -97,6 +102,9 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
97
102
  return none;
98
103
  }
99
104
 
105
+ __ataCollapse.M = __ataMulti;
106
+ __ataCollapse.F = __ataScore;
107
+
100
108
  // The named-argument form the interpreter and the tests call.
101
109
  function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPath, ordinal }) {
102
110
  return __ataCollapse(keyword, branchResults, parentPath, parentSchemaPath, ordinal === undefined ? null : ordinal);
@@ -107,8 +115,8 @@ function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPat
107
115
  // cannot miss the other. `tests/test_branch_collapse.js` runs both.
108
116
  function embedSource () {
109
117
  return 'const __ATA_SEVERITY=' + JSON.stringify(__ATA_SEVERITY) + ';' +
110
- 'const __ataScore=' + __ataScore.toString() + ';' +
118
+ 'const __ataScore=' + __ataScore.toString() + ';__ataScore.S=__ATA_SEVERITY;' +
111
119
  'const __ataMulti=' + __ataMulti.toString() + ';' +
112
- 'const __ataCollapse=' + __ataCollapse.toString() + ';';
120
+ 'const __ataCollapse=' + __ataCollapse.toString() + ';__ataCollapse.M=__ataMulti;__ataCollapse.F=__ataScore;';
113
121
  }
114
122
  export { __ataCollapse };
@@ -36,6 +36,15 @@ const { parse } = require('./safe-regex')
36
36
  // holds them: every accepted pattern answers as the linear engine does and
37
37
  // stays fast on inputs built to make a backtracking engine retry.
38
38
  const NATIVE_ESCAPES = new Set(['d', 'D', 'w', 'W', 's', 'S', 'n', 'r', 't', 'f', 'v', '.', '\\', '/', '^', '$', '|', '?', '*', '+', '(', ')', '[', ']', '{', '}', '-', 'x', 'u'])
39
+ // How many ways a backtracking engine can split one string over the bounded
40
+ // choices of a pattern. Choices in a row multiply: `(a?){30}` is thirty
41
+ // choices and two to the thirtieth ways, which the engine walks through on a
42
+ // string that does not match. The count used to be a sum capped at 64, so
43
+ // that pattern and `^([a-z]?){64}$` passed as linear and a string of 17
44
+ // characters ran for more than half a minute. Now the product, capped where
45
+ // the worst case stays under a millisecond for a hundred characters.
46
+ const MAX_WAYS = 4096
47
+ // The sum of bounded choices, which both rules below also hold to.
39
48
  const MAX_BOUNDED_CHOICES = 64
40
49
 
41
50
  const atom = (n) => n.t === 'char' || n.t === 'class' || n.t === 'any'
@@ -52,7 +61,9 @@ function anchored (ast) {
52
61
  function simpleRule (ast) {
53
62
  // Per subtree: how many unbounded quantifiers it holds (`u`) and how many
54
63
  // bounded choices a backtracking engine could try (`b`), or null where the
55
- // shape is not provably linear.
64
+ // shape is not provably linear. The choices are held to a sum
65
+ // (MAX_BOUNDED_CHOICES: a long counted run is slow to compile and to
66
+ // unwind) and to a product (MAX_WAYS, waysOf below).
56
67
  const info = (n) => {
57
68
  switch (n.t) {
58
69
  case 'star': case 'plus':
@@ -78,7 +89,7 @@ function simpleRule (ast) {
78
89
  }
79
90
  }
80
91
  const r = info(ast)
81
- if (r === null || r.u > 1 || r.b > MAX_BOUNDED_CHOICES) return false
92
+ if (r === null || r.u > 1 || r.b > MAX_BOUNDED_CHOICES || !(waysOf(ast) <= MAX_WAYS)) return false
82
93
  return r.u === 0 || anchored(ast)
83
94
  }
84
95
 
@@ -207,9 +218,31 @@ function deterministicRule (ast) {
207
218
  for (let p = 0; p < sets.length && ok; p++) disjoint(follow[p])
208
219
  if (!ok) return false
209
220
  if (bounded > MAX_BOUNDED_CHOICES) return false
221
+ // A counted repeat of a body that can end in more than one place is not a
222
+ // loop to the walk above (`(a?){30}`, `(a{1,8}){8}`), and it is where the
223
+ // ways multiply: the same product as the simple rule holds here too.
224
+ if (!(waysOf(ast) <= MAX_WAYS)) return false
210
225
  return unbounded === 0 || anchored(ast)
211
226
  }
212
227
 
228
+ // The product the simple rule counts, over any shape: an unbounded
229
+ // quantifier adds no bounded choice of its own.
230
+ function waysOf (n) {
231
+ switch (n.t) {
232
+ case 'star': case 'plus': return waysOf(n.child)
233
+ case 'quest': return atom(n.child) ? 2 : 1 + waysOf(n.child)
234
+ case 'repeat': {
235
+ if (n.max === Infinity) return waysOf(n.child)
236
+ if (atom(n.child)) return n.max - n.min + 1
237
+ return (n.max - n.min + 1) * Math.pow(waysOf(n.child), n.max)
238
+ }
239
+ case 'group': return waysOf(n.child)
240
+ case 'concat': { let w = 1; for (const part of n.parts) w *= waysOf(part); return w }
241
+ case 'alt': { let w = 0; for (const part of n.opts) w += waysOf(part); return w }
242
+ default: return 1
243
+ }
244
+ }
245
+
213
246
  // Two more shapes the rules above decline, both anchored with `^` and `$`
214
247
  // around one top-level sequence.
215
248
  //
@@ -393,6 +426,11 @@ function nativeIsLinear (src) {
393
426
  }
394
427
  let ast
395
428
  try { ast = parse(src) } catch { return false }
429
+ // The product of the bounded choices holds for every rule below, so it is
430
+ // asked here, once: the one-choice and delimited-loop rules did not ask it,
431
+ // and `^(a*b)*` followed by thirty `a?` reached the platform RegExp, where
432
+ // each `a?` more doubles the time on a string that does not match.
433
+ if (!(waysOf(ast) <= MAX_WAYS)) return false
396
434
  return simpleRule(ast) || deterministicRule(ast) || oneChoiceRule(ast) || delimitedLoopRule(ast)
397
435
  }
398
436
 
@@ -981,6 +981,12 @@ function emitObject(schema, ctx, out, depth) {
981
981
  const reqMasks = new Array(words).fill(0);
982
982
  if (required) for (const k of required) { const x = bit.get(k); reqMasks[x.w] |= x.b; }
983
983
 
984
+ // Many names are dispatched on a hash of the key, and two names with one
985
+ // hash cannot be: the code below then took the other branch, which reads
986
+ // variables only the few-names form declares, and the scanner threw a
987
+ // ReferenceError on every document that carried a declared key. Such a
988
+ // schema is left to the parser.
989
+ if (names.length >= HASH_DISPATCH_MIN && new Set(names.map(spanHash)).size !== names.length) decline('properties:hash-collision');
984
990
  out.push('c=s.charCodeAt(++i);');
985
991
  const useHash = names.length >= HASH_DISPATCH_MIN;
986
992
  if (useHash) {
@@ -118,8 +118,9 @@ function needsNormalization(schema, isDraft7) {
118
118
  // The boolean form of the exclusive bounds is draft-04's, and it is
119
119
  // unambiguous wherever it appears: no later dialect gives a boolean any
120
120
  // meaning there. Percent-encoded fragments mean the same in every dialect
121
- // too. Neither is gated on the declared dialect.
122
- if (bits & (NULLABLE | EXCLUSIVE_BOOL | ENCODED_REF)) return true;
121
+ // too. A fragment-only `$id` is folded into `$anchor` in every dialect, by
122
+ // the walk that folds `nullable`. None is gated on the declared dialect.
123
+ if (bits & (NULLABLE | EXCLUSIVE_BOOL | ENCODED_REF | ANCHOR_ID)) return true;
123
124
  return isDraft7 ? (bits & DRAFT7_WORK) !== 0 : false;
124
125
  }
125
126
 
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+ // Generated by scripts/gen-unique-items-source.js from lib/unique-items.js. Do not edit.
3
+ module.exports = { uq: "function _uqh(x) {\nif (x === null) return 1;\nvar t = typeof x, h, i;\nif (t === 'boolean') return x ? 2 : 3;\nif (t === 'number') {\nif (x === 0) return 4;\nh = x | 0;\nif (h === x) return Math.imul(h, 0x9e3779b1) ^ 5;\nvar f = _uqh.f || (_uqh.f = new Float64Array(1)), w = _uqh.w || (_uqh.w = new Uint32Array(f.buffer));\nf[0] = x;\nreturn Math.imul(w[0] ^ Math.imul(w[1], 0x9e3779b1), 0x85ebca6b) ^ 5;\n}\nif (t === 'string') {\nh = 0x811c9dc5 ^ x.length;\nfor (i = 0; i < x.length; i++) h = Math.imul(h ^ x.charCodeAt(i), 16777619);\nreturn h;\n}\nif (t !== 'object') return 6;\nif (Array.isArray(x)) {\nh = 0x2545f491 ^ x.length;\nfor (i = 0; i < x.length; i++) h = Math.imul(h ^ _uqh(x[i]), 16777619);\nreturn h;\n}\nvar ks = Object.keys(x);\nh = 0x6c8e9cf5 ^ ks.length;\nfor (i = 0; i < ks.length; i++) h = (h + Math.imul(_uqh(ks[i]) ^ Math.imul(_uqh(x[ks[i]]), 0x5bd1e995), 0x27d4eb2d)) | 0;\nreturn h;\n}function _uq(a) {\nvar n = a.length, i, k;\nif (n < 2) return true;\nif (n <= 12) {\nfor (i = 1; i < n; i++) for (k = 0; k < i; k++) if (_deq(a[i], a[k])) return false;\nreturn true;\n}\nvar prim = true;\nfor (i = 0; i < n; i++) { var x = a[i]; if (x !== null && typeof x === 'object') { prim = false; break; } }\nif (prim) {\nvar s = new Set();\nfor (i = 0; i < n; i++) { if (s.has(a[i])) return false; s.add(a[i]); }\nreturn true;\n}\nvar m = new Map();\nfor (i = 0; i < n; i++) {\nvar hh = _uqh(a[i]), b = m.get(hh);\nif (b === undefined) m.set(hh, i);\nelse if (typeof b === 'number') { if (_deq(a[b], a[i])) return false; m.set(hh, [b, i]); }\nelse { for (k = 0; k < b.length; k++) if (_deq(a[b[k]], a[i])) return false; b.push(i); }\n}\nreturn true;\n}", uqp: "var _uqpI=0;function _uqp(a) {\nvar n = a.length, i, k;\nif (n < 2) return -1;\nif (n <= 12) {\nfor (i = 0; i < n - 1; i++) for (k = i + 1; k < n; k++) if (_deq(a[i], a[k])) { _uqpI = i; return k; }\nreturn -1;\n}\nvar prim = true;\nfor (i = 0; i < n; i++) { var x = a[i]; if (x !== null && typeof x === 'object') { prim = false; break; } }\nvar bi = -1, bj = -1, m = new Map();\nif (prim) {\nfor (i = 0; i < n; i++) {\nvar p = m.get(a[i]);\nif (p === undefined) m.set(a[i], i);\nelse if (p >= 0) { if (bi < 0 || p < bi) { bi = p; bj = i; } m.set(a[i], -1); }\n}\n} else {\nvar paired = null;\nfor (i = 0; i < n; i++) {\nvar hh = _uqh(a[i]), b = m.get(hh), r = -1;\nif (b === undefined) { m.set(hh, i); continue; }\nif (typeof b === 'number') { if (_deq(a[b], a[i])) r = b; else m.set(hh, [b, i]); }\nelse { for (k = 0; k < b.length; k++) if (_deq(a[b[k]], a[i])) { r = b[k]; break; } if (r < 0) b.push(i); }\nif (r >= 0) {\nif (paired === null) paired = new Set();\nif (!paired.has(r)) { paired.add(r); if (bi < 0 || r < bi) { bi = r; bj = i; } }\n}\n}\n}\nif (bi < 0) return -1;\n_uqpI = bi;\nreturn bj;\n}" };
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.63.1';
10
+ module.exports = '1.63.3';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.63.1",
3
+ "version": "1.63.3",
4
4
  "description": "JSON Schema validator that compiles to plain JavaScript, still runs where code generation is blocked, and can be compiled away at build time. Passes the full official test suite.",
5
5
  "type": "commonjs",
6
6
  "main": "index.js",
@@ -266,13 +266,13 @@
266
266
  "codegen.d.ts"
267
267
  ],
268
268
  "optionalDependencies": {
269
- "@ata-validator/native-darwin-arm64": "1.63.1",
270
- "@ata-validator/native-darwin-x64": "1.63.1",
271
- "@ata-validator/native-linux-arm64-gnu": "1.63.1",
272
- "@ata-validator/native-linux-arm64-musl": "1.63.1",
273
- "@ata-validator/native-linux-x64-gnu": "1.63.1",
274
- "@ata-validator/native-linux-x64-musl": "1.63.1",
275
- "@ata-validator/native-win32-x64": "1.63.1"
269
+ "@ata-validator/native-darwin-arm64": "1.63.3",
270
+ "@ata-validator/native-darwin-x64": "1.63.3",
271
+ "@ata-validator/native-linux-arm64-gnu": "1.63.3",
272
+ "@ata-validator/native-linux-arm64-musl": "1.63.3",
273
+ "@ata-validator/native-linux-x64-gnu": "1.63.3",
274
+ "@ata-validator/native-linux-x64-musl": "1.63.3",
275
+ "@ata-validator/native-win32-x64": "1.63.3"
276
276
  },
277
277
  "peerDependencies": {
278
278
  "yaml": "^2.0.0"