ata-validator 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/lib/formats.js +57 -0
- package/lib/interpreter.js +3 -2
- package/lib/js-compiler.js +244 -126
- package/lib/version.js +1 -1
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to ata-validator are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/), and this project adheres to semantic versioning.
|
|
4
4
|
|
|
5
|
+
## 1.10.0 - 2026-08-30
|
|
6
|
+
|
|
7
|
+
### Performance
|
|
8
|
+
|
|
9
|
+
- The code generator takes shapes it used to decline for no correctness reason: boolean subschemas in `items`, `properties`, `patternProperties`, `dependentSchemas`, `propertyNames`, `allOf`, `anyOf`, `not`, `contains` and `if`/`then`/`else`, recursive `#/$defs/` references as named functions, and `additionalProperties` as a schema alongside composition or `patternProperties`. Each lands in all three generators and the closure path, held to the interpreter by `tests/test_codegen_edge_shapes.js` and the entry-point agreement test over the whole official suite. Every suite group that moved off the interpreter got faster, 31 of 31 on draft 2020-12 and 28 of 28 on draft 7, summed per-group time down 70 percent. The suite-wide figure, measured interleaved against the previous release in one process, did not move outside that measurement's noise; `benchmark/verdict-bench.md` has the numbers and says why.
|
|
10
|
+
- `date` and `ipv4` format checks read the string once with no regular expression and no allocation: 45.7 to 14.2 ns and 54.5 to 27.1 ns on a valid value, interleaved medians. Fuzzed against the previous forms with 0 mismatches; `tests/test_formats_single_pass.js` keeps it that way.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- A key matched only by the second of two `patternProperties` entries, alongside `additionalProperties: false`, was rejected: the generated key loop returned at the first pattern that missed. Found while rewriting that loop; covered by the edge-shape test.
|
|
15
|
+
- A declared property that also matched a `patternProperties` entry skipped the pattern's schema in the generated code when `additionalProperties` was a schema. The suite's own interaction case caught it the moment the shape was allowed to compile.
|
|
16
|
+
|
|
5
17
|
## 1.9.0 - 2026-08-28
|
|
6
18
|
|
|
7
19
|
### Errors
|
package/lib/formats.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Format predicates that read the string once, with no regular expression
|
|
4
|
+
// and no allocation. Each has a twin that emits the same check as source for
|
|
5
|
+
// the code generator; tests/test_formats_single_pass.js holds the two
|
|
6
|
+
// together and fuzzes both against the regular-expression forms they replaced.
|
|
7
|
+
// Measured, interleaved medians: date 45.7 to 14.2 ns, ipv4 54.5 to 27.1 ns.
|
|
8
|
+
// uuid was tried the same way and measured 57.3 against 59.4 ns: V8's regular
|
|
9
|
+
// expression for a fixed-length hex pattern is already that fast, so it stays.
|
|
10
|
+
|
|
11
|
+
function isDigit (c) { return c >= 48 && c <= 57; }
|
|
12
|
+
|
|
13
|
+
function date (s) {
|
|
14
|
+
if (s.length !== 10) return false;
|
|
15
|
+
for (let i = 0; i < 10; i++) {
|
|
16
|
+
const c = s.charCodeAt(i);
|
|
17
|
+
if (i === 4 || i === 7) { if (c !== 45) return false; } else if (!isDigit(c)) return false;
|
|
18
|
+
}
|
|
19
|
+
const m = (s.charCodeAt(5) - 48) * 10 + (s.charCodeAt(6) - 48);
|
|
20
|
+
const d = (s.charCodeAt(8) - 48) * 10 + (s.charCodeAt(9) - 48);
|
|
21
|
+
return m >= 1 && m <= 12 && d >= 1 && d <= 31;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Four decimal octets, no leading zeros, no empty octets, nothing else.
|
|
25
|
+
function ipv4 (s) {
|
|
26
|
+
const n = s.length;
|
|
27
|
+
if (n < 7 || n > 15) return false;
|
|
28
|
+
let octets = 0, value = 0, digits = 0;
|
|
29
|
+
for (let i = 0; i <= n; i++) {
|
|
30
|
+
const c = i < n ? s.charCodeAt(i) : 46;
|
|
31
|
+
if (c === 46) {
|
|
32
|
+
if (digits === 0 || value > 255) return false;
|
|
33
|
+
octets++; value = 0; digits = 0;
|
|
34
|
+
if (octets > 4) return false;
|
|
35
|
+
} else if (isDigit(c)) {
|
|
36
|
+
if (digits === 1 && value === 0) return false;
|
|
37
|
+
value = value * 10 + (c - 48); digits++;
|
|
38
|
+
if (digits > 3) return false;
|
|
39
|
+
} else return false;
|
|
40
|
+
}
|
|
41
|
+
return octets === 4;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Source twins. `v` is the expression holding the string; when `isStr` is
|
|
45
|
+
// false the check is wrapped in a typeof guard, matching FORMAT_CODEGEN's
|
|
46
|
+
// convention. Each returns a statement that `return false`s on mismatch.
|
|
47
|
+
function guard (v, isStr, body) { return isStr ? `{${body}}` : `if(typeof ${v}==='string'){${body}}`; }
|
|
48
|
+
|
|
49
|
+
function dateSource (v, isStr) {
|
|
50
|
+
return guard(v, isStr, `if(${v}.length!==10)return false;for(let _i=0;_i<10;_i++){const _c=${v}.charCodeAt(_i);if(_i===4||_i===7){if(_c!==45)return false}else if(_c<48||_c>57)return false}const _m=(${v}.charCodeAt(5)-48)*10+(${v}.charCodeAt(6)-48),_d=(${v}.charCodeAt(8)-48)*10+(${v}.charCodeAt(9)-48);if(_m<1||_m>12||_d<1||_d>31)return false`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function ipv4Source (v, isStr) {
|
|
54
|
+
return guard(v, isStr, `const _n=${v}.length;if(_n<7||_n>15)return false;let _o=0,_val=0,_dg=0;for(let _i=0;_i<=_n;_i++){const _c=_i<_n?${v}.charCodeAt(_i):46;if(_c===46){if(_dg===0||_val>255)return false;_o++;_val=0;_dg=0;if(_o>4)return false}else if(_c>=48&&_c<=57){if(_dg===1&&_val===0)return false;_val=_val*10+(_c-48);_dg++;if(_dg>3)return false}else return false}if(_o!==4)return false`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { date, ipv4, dateSource, ipv4Source };
|
package/lib/interpreter.js
CHANGED
|
@@ -247,16 +247,17 @@ function multipleOfOk(d, m) {
|
|
|
247
247
|
return Math.abs(q - Math.round(q)) < 1e-9;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
const _formats = require('./formats');
|
|
250
251
|
const FORMAT_CHECKS = {
|
|
251
252
|
email: (s) => { const at = s.indexOf('@'); return at > 0 && at < s.length - 1 && s.indexOf('.', at) > at + 1; },
|
|
252
|
-
date:
|
|
253
|
+
date: _formats.date,
|
|
253
254
|
'date-time': (s) => /^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$/.test(s) && !isNaN(Date.parse(s)),
|
|
254
255
|
time: (s) => /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?([Zz]|[+-]\d{2}:\d{2})?$/.test(s),
|
|
255
256
|
duration: (s) => /^P(?:\d+Y)?(?:\d+M)?(?:\d+W)?(?:\d+D)?(?:T(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?)?$/.test(s) && s !== 'P' && !s.endsWith('T'),
|
|
256
257
|
uuid: (s) => s.length === 36 && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s),
|
|
257
258
|
uri: (s) => /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(s) && !/[\s\u0000-\u001f\u007f]/.test(s),
|
|
258
259
|
'uri-reference': (s) => !/[\s\u0000-\u001f\u007f]/.test(s),
|
|
259
|
-
ipv4:
|
|
260
|
+
ipv4: _formats.ipv4,
|
|
260
261
|
ipv6: (s) => s !== '' && /^[0-9a-fA-F:.]+$/.test(s) && s.split(':').length >= 3 && s.split(':').length <= 8,
|
|
261
262
|
hostname: (s) => s.length > 0 && s.length <= 253 && /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(s),
|
|
262
263
|
regex: (s) => { try { new RegExp(s, 'u'); return true; } catch { try { new RegExp(s); return true; } catch { return false; } } },
|
package/lib/js-compiler.js
CHANGED
|
@@ -193,11 +193,13 @@ function compileToJS(schema, defs, schemaMap) {
|
|
|
193
193
|
// Collect $defs early so sub-schemas can resolve $ref
|
|
194
194
|
const rootDefs = defs || collectDefs(schema)
|
|
195
195
|
|
|
196
|
-
// Bail on features
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
schema.
|
|
196
|
+
// Bail on features this path has no emitter for. Tested for presence, not
|
|
197
|
+
// truthiness: `propertyNames: false` is falsy, passed this bail, and then
|
|
198
|
+
// validated nothing, which the entry-point agreement test caught.
|
|
199
|
+
if (schema.patternProperties !== undefined ||
|
|
200
|
+
schema.dependentSchemas !== undefined ||
|
|
201
|
+
schema.propertyDependencies !== undefined ||
|
|
202
|
+
schema.propertyNames !== undefined) {
|
|
201
203
|
return null
|
|
202
204
|
}
|
|
203
205
|
|
|
@@ -328,7 +330,7 @@ function compileToJS(schema, defs, schemaMap) {
|
|
|
328
330
|
}
|
|
329
331
|
|
|
330
332
|
// items, applied after the prefixItems positions
|
|
331
|
-
if (schema.items) {
|
|
333
|
+
if (schema.items !== undefined && schema.items !== true) {
|
|
332
334
|
const itemCheck = compileToJS(schema.items, rootDefs)
|
|
333
335
|
if (!itemCheck) return null
|
|
334
336
|
const start = Array.isArray(schema.prefixItems) ? schema.prefixItems.length : 0
|
|
@@ -359,7 +361,7 @@ function compileToJS(schema, defs, schemaMap) {
|
|
|
359
361
|
}
|
|
360
362
|
|
|
361
363
|
// contains
|
|
362
|
-
if (schema.contains) {
|
|
364
|
+
if (schema.contains !== undefined) {
|
|
363
365
|
const containsCheck = compileToJS(schema.contains, rootDefs)
|
|
364
366
|
if (!containsCheck) return null
|
|
365
367
|
const minC = schema.minContains !== undefined ? schema.minContains : 1
|
|
@@ -517,20 +519,20 @@ function compileToJS(schema, defs, schemaMap) {
|
|
|
517
519
|
}
|
|
518
520
|
|
|
519
521
|
// not
|
|
520
|
-
if (schema.not) {
|
|
522
|
+
if (schema.not !== undefined) {
|
|
521
523
|
const notFn = compileToJS(schema.not, rootDefs)
|
|
522
524
|
if (!notFn) return null
|
|
523
525
|
checks.push((d) => !notFn(d))
|
|
524
526
|
}
|
|
525
527
|
|
|
526
528
|
// if/then/else
|
|
527
|
-
if (schema.if) {
|
|
529
|
+
if (schema.if !== undefined) {
|
|
528
530
|
const ifFn = compileToJS(schema.if, rootDefs)
|
|
529
531
|
if (!ifFn) return null
|
|
530
|
-
const thenFn = schema.then ? compileToJS(schema.then, rootDefs) : null
|
|
531
|
-
const elseFn = schema.else ? compileToJS(schema.else, rootDefs) : null
|
|
532
|
-
if (schema.then && !thenFn) return null
|
|
533
|
-
if (schema.else && !elseFn) return null
|
|
532
|
+
const thenFn = schema.then !== undefined ? compileToJS(schema.then, rootDefs) : null
|
|
533
|
+
const elseFn = schema.else !== undefined ? compileToJS(schema.else, rootDefs) : null
|
|
534
|
+
if (schema.then !== undefined && !thenFn) return null
|
|
535
|
+
if (schema.else !== undefined && !elseFn) return null
|
|
534
536
|
checks.push((d) => {
|
|
535
537
|
if (ifFn(d)) {
|
|
536
538
|
return thenFn ? thenFn(d) : true
|
|
@@ -756,11 +758,12 @@ function _canonical(x) {
|
|
|
756
758
|
return '{' + Object.keys(x).sort().map((k) => JSON.stringify(k) + ':' + _canonical(x[k])).join(',') + '}'
|
|
757
759
|
}
|
|
758
760
|
|
|
761
|
+
const _formats = require('./formats')
|
|
759
762
|
const FORMAT_CHECKS = {
|
|
760
763
|
email: (s) => { const at = s.indexOf('@'); return at > 0 && at < s.length - 1 && s.indexOf('.', at) > at + 1 },
|
|
761
|
-
date:
|
|
764
|
+
date: _formats.date,
|
|
762
765
|
uuid: (s) => s.length === 36 && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s),
|
|
763
|
-
ipv4:
|
|
766
|
+
ipv4: _formats.ipv4,
|
|
764
767
|
hostname: (s) => s.length > 0 && s.length <= 253 && /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i.test(s),
|
|
765
768
|
}
|
|
766
769
|
|
|
@@ -842,6 +845,51 @@ function hasNestedIdScope(node, isRoot = true) {
|
|
|
842
845
|
|
|
843
846
|
// Recursively check if a schema can be safely compiled to JS codegen.
|
|
844
847
|
// Returns false if any sub-schema contains features codegen gets wrong.
|
|
848
|
+
// Names of the defs that sit on a $ref cycle among defs. Such a def compiles
|
|
849
|
+
// as a named function rather than being inlined, so recursion terminates in
|
|
850
|
+
// the generated code the same way `$ref: "#"` does at the root. A plain
|
|
851
|
+
// cross-reference (A -> B where B does not refer back) is not on a cycle and
|
|
852
|
+
// still inlines.
|
|
853
|
+
function cyclicDefNames (defs) {
|
|
854
|
+
const out = new Set()
|
|
855
|
+
if (!defs || typeof defs !== 'object') return out
|
|
856
|
+
const defNames = new Set(Object.keys(defs))
|
|
857
|
+
const defRefs = {}
|
|
858
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
859
|
+
const refs = []
|
|
860
|
+
const seen = new WeakSet()
|
|
861
|
+
const collectRefs = (node) => {
|
|
862
|
+
if (typeof node !== 'object' || node === null) return
|
|
863
|
+
if (seen.has(node)) return
|
|
864
|
+
seen.add(node)
|
|
865
|
+
if (node.$ref) {
|
|
866
|
+
const m = /^#\/(?:\$defs|definitions)\/([^/]+)$/.exec(node.$ref)
|
|
867
|
+
if (m && defNames.has(m[1])) refs.push(m[1])
|
|
868
|
+
}
|
|
869
|
+
for (const val of Object.values(node)) {
|
|
870
|
+
if (typeof val === 'object' && val !== null) {
|
|
871
|
+
if (Array.isArray(val)) { for (const item of val) collectRefs(item) } else collectRefs(val)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
collectRefs(def)
|
|
876
|
+
defRefs[name] = refs
|
|
877
|
+
}
|
|
878
|
+
const visited = new Set()
|
|
879
|
+
const inStack = []
|
|
880
|
+
const dfs = (node) => {
|
|
881
|
+
const at = inStack.indexOf(node)
|
|
882
|
+
if (at !== -1) { for (let i = at; i < inStack.length; i++) out.add(inStack[i]); return }
|
|
883
|
+
if (visited.has(node)) return
|
|
884
|
+
visited.add(node)
|
|
885
|
+
inStack.push(node)
|
|
886
|
+
for (const neighbor of (defRefs[node] || [])) dfs(neighbor)
|
|
887
|
+
inStack.pop()
|
|
888
|
+
}
|
|
889
|
+
for (const name of Object.keys(defRefs)) dfs(name)
|
|
890
|
+
return out
|
|
891
|
+
}
|
|
892
|
+
|
|
845
893
|
function codegenSafe(schema, schemaMap) {
|
|
846
894
|
if (typeof schema === 'boolean') return true
|
|
847
895
|
if (typeof schema !== 'object' || schema === null) return true
|
|
@@ -855,13 +903,12 @@ function codegenSafe(schema, schemaMap) {
|
|
|
855
903
|
if (schema.$dynamicRef && !schema.$dynamicRef.startsWith('#')) return false
|
|
856
904
|
|
|
857
905
|
// Boolean sub-schemas anywhere cause bail — codegen doesn't handle schema=false correctly
|
|
858
|
-
|
|
859
|
-
|
|
906
|
+
// items: false and items: true are constants; every generator handles a
|
|
907
|
+
// boolean child at its head, so neither falls through as "nothing".
|
|
860
908
|
if (schema.additionalProperties === true) return true // permissive — fine
|
|
861
909
|
if (schema.properties) {
|
|
862
910
|
for (const v of Object.values(schema.properties)) {
|
|
863
|
-
if (v ===
|
|
864
|
-
if (v === true) continue // property: true is always valid
|
|
911
|
+
if (typeof v === 'boolean') continue // constants; every generator handles them at its head
|
|
865
912
|
if (!codegenSafe(v, schemaMap)) return false
|
|
866
913
|
}
|
|
867
914
|
}
|
|
@@ -926,15 +973,17 @@ function codegenSafe(schema, schemaMap) {
|
|
|
926
973
|
// additionalProperties as schema — supported when no composition (allOf/oneOf/anyOf)
|
|
927
974
|
// is in play, and no patternProperties (unified loop does not emit the
|
|
928
975
|
// per-key sub-schema walk yet). Closure path handles those cases.
|
|
976
|
+
// additionalProperties as a schema: emitted by the per-key walk, or by the
|
|
977
|
+
// unified key loop when patternProperties is present. Composition alongside
|
|
978
|
+
// it is fine, since additionalProperties only looks at this schema's own
|
|
979
|
+
// properties and patternProperties.
|
|
929
980
|
if (typeof schema.additionalProperties === 'object' && schema.additionalProperties !== null) {
|
|
930
|
-
if (schema.allOf || schema.oneOf || schema.anyOf) return false
|
|
931
|
-
if (schema.patternProperties) return false
|
|
932
981
|
if (!codegenSafe(schema.additionalProperties, schemaMap)) return false
|
|
933
982
|
}
|
|
934
983
|
if (schema.additionalProperties === false && !schema.properties) return false
|
|
935
984
|
|
|
936
985
|
// propertyNames: false — codegen doesn't handle this
|
|
937
|
-
|
|
986
|
+
// propertyNames: false is emitted as a per-key rejection by every generator.
|
|
938
987
|
|
|
939
988
|
// unevaluatedProperties: allow boolean and schema values
|
|
940
989
|
if (schema.unevaluatedProperties !== undefined) {
|
|
@@ -967,53 +1016,8 @@ function codegenSafe(schema, schemaMap) {
|
|
|
967
1016
|
const defs = schema.$defs || schema.definitions
|
|
968
1017
|
if (defs) {
|
|
969
1018
|
const defNames = new Set(Object.keys(defs))
|
|
970
|
-
//
|
|
971
|
-
//
|
|
972
|
-
// refer back to A) is fine; only true cycles (A -> B -> A) must be rejected.
|
|
973
|
-
// The JS codegen returns (() => true) on cycle (permissive), so cyclic schemas
|
|
974
|
-
// must route to the interpreted engine instead.
|
|
975
|
-
const defRefs = {}
|
|
976
|
-
for (const [name, def] of Object.entries(defs)) {
|
|
977
|
-
defRefs[name] = []
|
|
978
|
-
if (typeof def === 'object' && def !== null) {
|
|
979
|
-
const refs = []
|
|
980
|
-
const seen = new WeakSet()
|
|
981
|
-
const collectRefs = (node) => {
|
|
982
|
-
if (typeof node !== 'object' || node === null) return
|
|
983
|
-
if (seen.has(node)) return
|
|
984
|
-
seen.add(node)
|
|
985
|
-
if (node.$ref) {
|
|
986
|
-
const m = /^#\/(?:\$defs|definitions)\/([^/]+)$/.exec(node.$ref)
|
|
987
|
-
if (m && defNames.has(m[1])) refs.push(m[1])
|
|
988
|
-
}
|
|
989
|
-
for (const val of Object.values(node)) {
|
|
990
|
-
if (typeof val === 'object' && val !== null) {
|
|
991
|
-
if (Array.isArray(val)) { for (const item of val) collectRefs(item) }
|
|
992
|
-
else collectRefs(val)
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
collectRefs(def)
|
|
997
|
-
defRefs[name] = refs
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
const hasCycle = (() => {
|
|
1001
|
-
const visited = new Set()
|
|
1002
|
-
const inStack = new Set()
|
|
1003
|
-
const dfs = (node) => {
|
|
1004
|
-
if (inStack.has(node)) return true
|
|
1005
|
-
if (visited.has(node)) return false
|
|
1006
|
-
visited.add(node)
|
|
1007
|
-
inStack.add(node)
|
|
1008
|
-
for (const neighbor of (defRefs[node] || [])) {
|
|
1009
|
-
if (dfs(neighbor)) return true
|
|
1010
|
-
}
|
|
1011
|
-
inStack.delete(node)
|
|
1012
|
-
return false
|
|
1013
|
-
}
|
|
1014
|
-
return Object.keys(defRefs).some(dfs)
|
|
1015
|
-
})()
|
|
1016
|
-
if (hasCycle) return false
|
|
1019
|
+
// Defs on a $ref cycle compile as named functions (see genCode); nothing
|
|
1020
|
+
// to decline here. cyclicDefNames() is the walk that used to bail.
|
|
1017
1021
|
for (const [name, def] of Object.entries(defs)) {
|
|
1018
1022
|
if (/[~/"']/.test(name)) return false // special chars in def name
|
|
1019
1023
|
if (typeof def === 'boolean') return false
|
|
@@ -1030,6 +1034,10 @@ function codegenSafe(schema, schemaMap) {
|
|
|
1030
1034
|
}
|
|
1031
1035
|
|
|
1032
1036
|
// Recurse into sub-schemas — bail on boolean schemas in any position
|
|
1037
|
+
// A boolean subschema is a constant that every generator emits at its
|
|
1038
|
+
// head with the child's own path. The emitters below test for presence,
|
|
1039
|
+
// not truthiness, because `false` is falsy and a truthiness test would
|
|
1040
|
+
// skip it and validate nothing.
|
|
1033
1041
|
const subs = [
|
|
1034
1042
|
schema.items, schema.contains, schema.not,
|
|
1035
1043
|
schema.if, schema.then, schema.else,
|
|
@@ -1040,9 +1048,7 @@ function codegenSafe(schema, schemaMap) {
|
|
|
1040
1048
|
]
|
|
1041
1049
|
if (typeof schema.additionalProperties === 'object') subs.push(schema.additionalProperties)
|
|
1042
1050
|
for (const s of subs) {
|
|
1043
|
-
if (s === undefined || s === null) continue
|
|
1044
|
-
if (s === false) return false // boolean false sub-schema — complex
|
|
1045
|
-
if (s === true) continue // boolean true sub-schema — always valid, fine
|
|
1051
|
+
if (s === undefined || s === null || typeof s === 'boolean') continue
|
|
1046
1052
|
if (!codegenSafe(s, schemaMap)) return false
|
|
1047
1053
|
}
|
|
1048
1054
|
|
|
@@ -1330,7 +1336,6 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
|
|
|
1330
1336
|
// patternProperties: bail only on boolean sub-schemas or unicode property escapes
|
|
1331
1337
|
if (schema.patternProperties) {
|
|
1332
1338
|
for (const [pat, sub] of Object.entries(schema.patternProperties)) {
|
|
1333
|
-
if (typeof sub === 'boolean') return null
|
|
1334
1339
|
if (/\\[pP]\{/.test(pat)) return null
|
|
1335
1340
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
1336
1341
|
}
|
|
@@ -1338,13 +1343,11 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
|
|
|
1338
1343
|
// dependentSchemas: bail on boolean sub-schemas
|
|
1339
1344
|
if (schema.dependentSchemas) {
|
|
1340
1345
|
for (const sub of Object.values(schema.dependentSchemas)) {
|
|
1341
|
-
if (typeof sub === 'boolean') return null
|
|
1342
1346
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
1343
1347
|
}
|
|
1344
1348
|
}
|
|
1345
1349
|
// propertyNames: only codegen simple string constraints
|
|
1346
1350
|
if (schema.propertyNames) {
|
|
1347
|
-
if (typeof schema.propertyNames === 'boolean') return null
|
|
1348
1351
|
const pn = schema.propertyNames
|
|
1349
1352
|
const supported = ['maxLength', 'minLength', 'pattern', 'const', 'enum']
|
|
1350
1353
|
const keys = Object.keys(pn).filter(k => k !== '$schema')
|
|
@@ -1654,6 +1657,11 @@ function _deferOrInline(ctx, lines, v, check) {
|
|
|
1654
1657
|
// 'array' = we know v is an array
|
|
1655
1658
|
// 'string' / 'number' / 'integer' = we know the primitive type
|
|
1656
1659
|
function genCode(schema, v, lines, ctx, knownType) {
|
|
1660
|
+
// A boolean schema is a constant. false rejects whatever reached it; true
|
|
1661
|
+
// constrains nothing and emits nothing, which is correct only because the
|
|
1662
|
+
// caller's own checks still stand around this call.
|
|
1663
|
+
if (schema === false) { lines.push('return false'); return }
|
|
1664
|
+
if (schema === true) return
|
|
1657
1665
|
if (typeof schema !== 'object' || schema === null) return
|
|
1658
1666
|
if (!ctx.regExpMap) {
|
|
1659
1667
|
ctx.regExpMap = new Map();
|
|
@@ -1673,10 +1681,28 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
1673
1681
|
// 1. Local ref
|
|
1674
1682
|
const m = schema.$ref !== '#' && schema.$ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/)
|
|
1675
1683
|
if (m && ctx.rootDefs && ctx.rootDefs[m[1]]) {
|
|
1676
|
-
|
|
1684
|
+
const defName = m[1]
|
|
1685
|
+
if (!ctx.cyclicDefs) ctx.cyclicDefs = cyclicDefNames(ctx.rootDefs)
|
|
1686
|
+
if (ctx.cyclicDefs.has(defName) && ctx.preamble) {
|
|
1687
|
+
// A def on a cycle is a named function in the preamble, emitted once
|
|
1688
|
+
// and called by name, so recursion terminates the way `$ref: "#"`
|
|
1689
|
+
// does at the root. The name is registered before the body is
|
|
1690
|
+
// generated so a self-reference inside it becomes a call.
|
|
1691
|
+
if (!ctx.defFns) ctx.defFns = new Map()
|
|
1692
|
+
let fnName = ctx.defFns.get(defName)
|
|
1693
|
+
if (!fnName) {
|
|
1694
|
+
fnName = '_def' + ctx.defFns.size + '_' + defName.replace(/[^A-Za-z0-9_]/g, '_')
|
|
1695
|
+
ctx.defFns.set(defName, fnName)
|
|
1696
|
+
const bodyLines = []
|
|
1697
|
+
genCode(ctx.rootDefs[defName], 'd', bodyLines, ctx)
|
|
1698
|
+
ctx.preamble.push(`function ${fnName}(d){${bodyLines.join('\n ')}\n return true}`)
|
|
1699
|
+
}
|
|
1700
|
+
lines.push(`if(!${fnName}(${v}))return false`)
|
|
1701
|
+
if (!hasSiblings) return
|
|
1702
|
+
} else if (ctx.refStack.has(schema.$ref)) { if (!hasSiblings) return }
|
|
1677
1703
|
else {
|
|
1678
1704
|
ctx.refStack.add(schema.$ref)
|
|
1679
|
-
genCode(ctx.rootDefs[
|
|
1705
|
+
genCode(ctx.rootDefs[defName], v, lines, ctx, knownType)
|
|
1680
1706
|
ctx.refStack.delete(schema.$ref)
|
|
1681
1707
|
if (!hasSiblings) return
|
|
1682
1708
|
}
|
|
@@ -2048,11 +2074,23 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
2048
2074
|
|
|
2049
2075
|
const guard = isObj ? '' : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v}))`
|
|
2050
2076
|
|
|
2051
|
-
|
|
2052
|
-
|
|
2077
|
+
const apSchema = typeof schema.additionalProperties === 'object' && schema.additionalProperties !== null ? schema.additionalProperties : null
|
|
2078
|
+
if ((schema.additionalProperties === false && schema.properties) || apSchema) {
|
|
2079
|
+
// Unified loop: properties + patterns + propertyNames + additionalProperties.
|
|
2080
|
+
// A key that is neither declared nor matched by any pattern is
|
|
2081
|
+
// additional: rejected outright, or run through the additionalProperties
|
|
2082
|
+
// schema when there is one.
|
|
2053
2083
|
ctx._ppHandledAdditional = true
|
|
2054
2084
|
ctx._ppHandledPropertyNames = !!pn
|
|
2055
|
-
const propKeys = Object.keys(schema.properties)
|
|
2085
|
+
const propKeys = Object.keys(schema.properties || {})
|
|
2086
|
+
let apFn = null
|
|
2087
|
+
if (apSchema) {
|
|
2088
|
+
const apLines = []
|
|
2089
|
+
genCode(apSchema, '_apv', apLines, ctx)
|
|
2090
|
+
apFn = `_apf${pi}`
|
|
2091
|
+
ctx.closureVars.push(apFn)
|
|
2092
|
+
ctx.closureVals.push(new Function('_apv', apLines.length === 0 ? 'return true' : `${apLines.join(';')};return true`))
|
|
2093
|
+
}
|
|
2056
2094
|
lines.push(`${guard}{for(const ${kVar} in ${v}){`)
|
|
2057
2095
|
// propertyNames checks (merged into same loop)
|
|
2058
2096
|
if (pn) {
|
|
@@ -2077,21 +2115,29 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
2077
2115
|
lines.push(`if(!_es${ei}.has(${kVar}))return false`)
|
|
2078
2116
|
}
|
|
2079
2117
|
}
|
|
2080
|
-
//
|
|
2081
|
-
//
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
//
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2118
|
+
// Every matching pattern applies to every key, declared or not: a
|
|
2119
|
+
// declared key that also matches a pattern is validated by both (the
|
|
2120
|
+
// properties emitter handles its own subschema). A flag rather than an
|
|
2121
|
+
// if/else chain, because a chain returned false at the first pattern
|
|
2122
|
+
// that missed and wrongly rejected a key only a later pattern matched.
|
|
2123
|
+
if (ppEntries.length > 0) {
|
|
2124
|
+
lines.push(`let _pm${pi}=false`)
|
|
2125
|
+
for (let i = 0; i < ppEntries.length; i++) {
|
|
2126
|
+
lines.push(`if(${matchers[i].check}){_pm${pi}=true;if(!_ppf${pi}_${i}(${v}[${kVar}]))return false}`)
|
|
2127
|
+
}
|
|
2088
2128
|
}
|
|
2089
|
-
|
|
2090
|
-
|
|
2129
|
+
// A key that is neither declared nor matched is additional. switch on
|
|
2130
|
+
// the declared names (V8 compiles string cases to a jump table); no
|
|
2131
|
+
// switch at all when nothing is declared, since a switch with no case
|
|
2132
|
+
// clause is a syntax error.
|
|
2133
|
+
const additional = apFn ? `if(!${apFn}(${v}[${kVar}]))return false` : `return false`
|
|
2134
|
+
const notMatched = ppEntries.length > 0 ? `if(!_pm${pi}){${additional}}` : additional
|
|
2135
|
+
if (propKeys.length) {
|
|
2136
|
+
const switchCases = propKeys.map(k => `case ${JSON.stringify(k)}:`).join('')
|
|
2137
|
+
lines.push(`switch(${kVar}){${switchCases}break;default:{${notMatched}}}`)
|
|
2091
2138
|
} else {
|
|
2092
|
-
lines.push(
|
|
2139
|
+
lines.push(notMatched)
|
|
2093
2140
|
}
|
|
2094
|
-
lines.push(`}`) // end switch
|
|
2095
2141
|
lines.push(`}}`)
|
|
2096
2142
|
} else {
|
|
2097
2143
|
// No additionalProperties: validate matching keys + propertyNames
|
|
@@ -2139,6 +2185,13 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
2139
2185
|
}
|
|
2140
2186
|
}
|
|
2141
2187
|
|
|
2188
|
+
// propertyNames: false rejects any key at all; true constrains nothing.
|
|
2189
|
+
if (schema.propertyNames === false) {
|
|
2190
|
+
lines.push(isObj
|
|
2191
|
+
? `for(const _k in ${v})return false`
|
|
2192
|
+
: `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k in ${v})return false}`)
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2142
2195
|
// propertyNames — only emit if not already merged into patternProperties loop
|
|
2143
2196
|
if (schema.propertyNames && typeof schema.propertyNames === 'object' && !ctx._ppHandledPropertyNames) {
|
|
2144
2197
|
const pn = schema.propertyNames
|
|
@@ -2207,7 +2260,7 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
2207
2260
|
}
|
|
2208
2261
|
|
|
2209
2262
|
// items — pass known type info to children
|
|
2210
|
-
if (schema.items) {
|
|
2263
|
+
if (schema.items !== undefined && schema.items !== true) {
|
|
2211
2264
|
const idx = `_j${ctx.varCounter}`
|
|
2212
2265
|
const elem = `_e${ctx.varCounter}`
|
|
2213
2266
|
ctx.varCounter++
|
|
@@ -2234,7 +2287,7 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
2234
2287
|
}
|
|
2235
2288
|
|
|
2236
2289
|
// contains — use helper function to avoid try/catch overhead
|
|
2237
|
-
if (schema.contains) {
|
|
2290
|
+
if (schema.contains !== undefined) {
|
|
2238
2291
|
const ci = ctx.varCounter++
|
|
2239
2292
|
const minC = schema.minContains !== undefined ? schema.minContains : 1
|
|
2240
2293
|
const maxC = schema.maxContains !== undefined ? schema.maxContains : Infinity
|
|
@@ -2314,7 +2367,7 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
2314
2367
|
}
|
|
2315
2368
|
|
|
2316
2369
|
// not
|
|
2317
|
-
if (schema.not) {
|
|
2370
|
+
if (schema.not !== undefined) {
|
|
2318
2371
|
const subLines = []
|
|
2319
2372
|
genCode(schema.not, '_nv', subLines, ctx)
|
|
2320
2373
|
if (subLines.length === 0) {
|
|
@@ -2326,7 +2379,7 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
2326
2379
|
}
|
|
2327
2380
|
|
|
2328
2381
|
// if/then/else
|
|
2329
|
-
if (schema.if) {
|
|
2382
|
+
if (schema.if !== undefined) {
|
|
2330
2383
|
const ifLines = []
|
|
2331
2384
|
genCode(schema.if, '_iv', ifLines, ctx)
|
|
2332
2385
|
const fi = ctx.varCounter++
|
|
@@ -2335,14 +2388,14 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
2335
2388
|
: `function(_iv){${ifLines.join(';')};return true}`
|
|
2336
2389
|
|
|
2337
2390
|
let thenFn = 'null', elseFn = 'null'
|
|
2338
|
-
if (schema.then) {
|
|
2391
|
+
if (schema.then !== undefined) {
|
|
2339
2392
|
const thenLines = []
|
|
2340
2393
|
genCode(schema.then, '_tv', thenLines, ctx)
|
|
2341
2394
|
thenFn = thenLines.length === 0
|
|
2342
2395
|
? `function(_tv){return true}`
|
|
2343
2396
|
: `function(_tv){${thenLines.join(';')};return true}`
|
|
2344
2397
|
}
|
|
2345
|
-
if (schema.else) {
|
|
2398
|
+
if (schema.else !== undefined) {
|
|
2346
2399
|
const elseLines = []
|
|
2347
2400
|
genCode(schema.else, '_ev', elseLines, ctx)
|
|
2348
2401
|
elseFn = elseLines.length === 0
|
|
@@ -2846,9 +2899,7 @@ const FORMAT_CODEGEN = {
|
|
|
2846
2899
|
? `{const _at=${v}.indexOf('@');if(_at<=0||_at>=${v}.length-1||${v}.indexOf('.',_at)<=_at+1)return false}`
|
|
2847
2900
|
: `if(typeof ${v}==='string'){const _at=${v}.indexOf('@');if(_at<=0||_at>=${v}.length-1||${v}.indexOf('.',_at)<=_at+1)return false}`
|
|
2848
2901
|
},
|
|
2849
|
-
date:
|
|
2850
|
-
? `{if(${v}.length!==10||!/^\\d{4}-\\d{2}-\\d{2}$/.test(${v}))return false;const _dm=+${v}.slice(5,7),_dd=+${v}.slice(8,10);if(_dm<1||_dm>12||_dd<1||_dd>31)return false}`
|
|
2851
|
-
: `if(typeof ${v}==='string'){if(${v}.length!==10||!/^\\d{4}-\\d{2}-\\d{2}$/.test(${v}))return false;const _dm=+${v}.slice(5,7),_dd=+${v}.slice(8,10);if(_dm<1||_dm>12||_dd<1||_dd>31)return false}`,
|
|
2902
|
+
date: _formats.dateSource,
|
|
2852
2903
|
uuid: (v, isStr) => isStr
|
|
2853
2904
|
? `if(${v}.length!==36||!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(${v}))return false`
|
|
2854
2905
|
: `if(typeof ${v}==='string'&&(${v}.length!==36||!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(${v})))return false`,
|
|
@@ -2867,9 +2918,7 @@ const FORMAT_CODEGEN = {
|
|
|
2867
2918
|
'uri-reference': (v, isStr) => isStr
|
|
2868
2919
|
? `if(/[\\s\\u0000-\\u001f\\u007f]/.test(${v}))return false`
|
|
2869
2920
|
: `if(typeof ${v}==='string'&&/[\\s\\u0000-\\u001f\\u007f]/.test(${v}))return false`,
|
|
2870
|
-
ipv4:
|
|
2871
|
-
? `{const _p=${v}.split('.');if(_p.length!==4||!_p.every(function(n){var x=+n;return x>=0&&x<=255&&String(x)===n}))return false}`
|
|
2872
|
-
: `if(typeof ${v}==='string'){const _p=${v}.split('.');if(_p.length!==4||!_p.every(function(n){var x=+n;return x>=0&&x<=255&&String(x)===n}))return false}`,
|
|
2921
|
+
ipv4: _formats.ipv4Source,
|
|
2873
2922
|
ipv6: (v, isStr) => isStr
|
|
2874
2923
|
? `{const _s=${v};if(_s===''||!/^[0-9a-fA-F:]+$/.test(_s)||_s.split(':').length<3||_s.split(':').length>8)return false}`
|
|
2875
2924
|
: `if(typeof ${v}==='string'){const _s=${v};if(_s===''||!/^[0-9a-fA-F:]+$/.test(_s)||_s.split(':').length<3||_s.split(':').length>8)return false}`,
|
|
@@ -3032,19 +3081,16 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
|
|
|
3032
3081
|
if (!sharedCodegenGate(schema, schemaMap)) return null
|
|
3033
3082
|
if (schema.patternProperties) {
|
|
3034
3083
|
for (const [pat, sub] of Object.entries(schema.patternProperties)) {
|
|
3035
|
-
if (typeof sub === 'boolean') return null
|
|
3036
3084
|
if (/\\[pP]\{/.test(pat)) return null
|
|
3037
3085
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
3038
3086
|
}
|
|
3039
3087
|
}
|
|
3040
3088
|
if (schema.dependentSchemas) {
|
|
3041
3089
|
for (const sub of Object.values(schema.dependentSchemas)) {
|
|
3042
|
-
if (typeof sub === 'boolean') return null
|
|
3043
3090
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
3044
3091
|
}
|
|
3045
3092
|
}
|
|
3046
3093
|
if (schema.propertyNames) {
|
|
3047
|
-
if (typeof schema.propertyNames === 'boolean') return null
|
|
3048
3094
|
const pn = schema.propertyNames
|
|
3049
3095
|
const supported = ['maxLength', 'minLength', 'pattern', 'const', 'enum']
|
|
3050
3096
|
const keys = Object.keys(pn).filter(k => k !== '$schema')
|
|
@@ -3145,6 +3191,10 @@ function emitBranchCollapse (branches, keyword, v, pathExpr, lines, ctx, schemaP
|
|
|
3145
3191
|
const subSp = branchSp + '/' + i
|
|
3146
3192
|
if (typeof sub === 'object' && sub !== null) {
|
|
3147
3193
|
genCodeE(sub, '_bv', pathExpr, subLines, ctx, subSp)
|
|
3194
|
+
} else if (sub === false) {
|
|
3195
|
+
// A false branch never matches; it reports the same error the
|
|
3196
|
+
// interpreter attaches to a false schema.
|
|
3197
|
+
subLines.push(`_e.push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${subSp}',params:{},message:'boolean schema is false'})`)
|
|
3148
3198
|
}
|
|
3149
3199
|
const title = (sub && typeof sub === 'object' && typeof sub.title === 'string') ? sub.title : ''
|
|
3150
3200
|
titles.push(title)
|
|
@@ -3166,6 +3216,16 @@ function emitBranchCollapse (branches, keyword, v, pathExpr, lines, ctx, schemaP
|
|
|
3166
3216
|
// `_all` parameter: if falsy, return after first error.
|
|
3167
3217
|
function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
3168
3218
|
if (!schemaPrefix) schemaPrefix = '#'
|
|
3219
|
+
// A false schema reports one error at the value that reached it, with the
|
|
3220
|
+
// keyword the interpreter has always used for it, `not` (lib/plan-compiler.js
|
|
3221
|
+
// line 58; its `err()` discards the 'false schema' label). Other validators
|
|
3222
|
+
// name this keyword `false schema`; changing it here would change output
|
|
3223
|
+
// the interpreter path already produces, so both engines say `not`.
|
|
3224
|
+
if (schema === false) {
|
|
3225
|
+
lines.push(`;_e.push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}',params:{},message:'boolean schema is false'});if(!_all)return{valid:false,errors:_e}`)
|
|
3226
|
+
return
|
|
3227
|
+
}
|
|
3228
|
+
if (schema === true) return
|
|
3169
3229
|
if (typeof schema !== 'object' || schema === null) return
|
|
3170
3230
|
if (!ctx.regExpMap) {
|
|
3171
3231
|
ctx.regExpMap = new Map();
|
|
@@ -3176,9 +3236,26 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3176
3236
|
if (schema.$ref === '#') return
|
|
3177
3237
|
const m = schema.$ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/)
|
|
3178
3238
|
if (m && ctx.rootDefs && ctx.rootDefs[m[1]]) {
|
|
3239
|
+
const defName = m[1]
|
|
3240
|
+
if (!ctx.cyclicDefs) ctx.cyclicDefs = cyclicDefNames(ctx.rootDefs)
|
|
3241
|
+
if (ctx.cyclicDefs.has(defName)) {
|
|
3242
|
+
// Named helper for a def on a cycle; the path comes in as an
|
|
3243
|
+
// argument because every call site has its own.
|
|
3244
|
+
if (!ctx.defFns) ctx.defFns = new Map()
|
|
3245
|
+
let fnName = ctx.defFns.get(defName)
|
|
3246
|
+
if (!fnName) {
|
|
3247
|
+
fnName = '_defE' + ctx.defFns.size + '_' + defName.replace(/[^A-Za-z0-9_]/g, '_')
|
|
3248
|
+
ctx.defFns.set(defName, fnName)
|
|
3249
|
+
const bodyLines = []
|
|
3250
|
+
genCodeE(ctx.rootDefs[defName], 'd', '_p', bodyLines, ctx, '#/$defs/' + defName)
|
|
3251
|
+
ctx.helperCode.push(`function ${fnName}(d,_p,_all,_e){${bodyLines.join('\n ')}}`)
|
|
3252
|
+
}
|
|
3253
|
+
lines.push(`${fnName}(${v},${pathExpr || '""'},_all,_e);if(!_all&&_e.length)return{valid:false,errors:_e}`)
|
|
3254
|
+
return
|
|
3255
|
+
}
|
|
3179
3256
|
if (ctx.refStack.has(schema.$ref)) return
|
|
3180
3257
|
ctx.refStack.add(schema.$ref)
|
|
3181
|
-
genCodeE(ctx.rootDefs[
|
|
3258
|
+
genCodeE(ctx.rootDefs[defName], v, pathExpr, lines, ctx, schemaPrefix)
|
|
3182
3259
|
ctx.refStack.delete(schema.$ref)
|
|
3183
3260
|
return
|
|
3184
3261
|
}
|
|
@@ -3496,7 +3573,26 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3496
3573
|
const ki = ctx.varCounter++
|
|
3497
3574
|
const known = Object.keys(schema.properties || {})
|
|
3498
3575
|
const guard = known.length ? `const _ak${ki}=new Set([${known.map((k) => JSON.stringify(k)).join(',')}]);` : ''
|
|
3499
|
-
|
|
3576
|
+
// A key matched by any patternProperties entry is not additional.
|
|
3577
|
+
const patTests = []
|
|
3578
|
+
for (const pat of Object.keys(schema.patternProperties || {})) {
|
|
3579
|
+
const pattern = JSON.stringify(pat)
|
|
3580
|
+
if (!ctx.regExpMap.has(pattern)) {
|
|
3581
|
+
const ri = ctx.varCounter++
|
|
3582
|
+
ctx.regExpMap.set(pattern, ri)
|
|
3583
|
+
if (patternIsSafe(pat)) {
|
|
3584
|
+
ctx.helperCode.push(`const _re${ri}=__ataSafeRe(${pattern})`)
|
|
3585
|
+
ctx.usesSafeRe = true
|
|
3586
|
+
} else {
|
|
3587
|
+
ctx.helperCode.push(`const _re${ri}=new RegExp(${pattern})`)
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
patTests.push(`_re${ctx.regExpMap.get(pattern)}.test(_k${ki})`)
|
|
3591
|
+
}
|
|
3592
|
+
const conds = []
|
|
3593
|
+
if (known.length) conds.push(`!_ak${ki}.has(_k${ki})`)
|
|
3594
|
+
if (patTests.length) conds.push(`!(${patTests.join('||')})`)
|
|
3595
|
+
const keep = conds.length ? `if(${conds.join('&&')}){` : `{`
|
|
3500
3596
|
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){${guard}for(const _k${ki} in ${v}){${keep}`)
|
|
3501
3597
|
const p = pathExpr ? `${pathExpr}+'/'+_k${ki}` : `'/'+_k${ki}`
|
|
3502
3598
|
genCodeE(schema.additionalProperties, `${v}[_k${ki}]`, p, lines, ctx, schemaPrefix + '/additionalProperties')
|
|
@@ -3512,6 +3608,14 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3512
3608
|
}
|
|
3513
3609
|
}
|
|
3514
3610
|
|
|
3611
|
+
// propertyNames: false reports every key, at the key's own path, the way the
|
|
3612
|
+
// interpreter does (plan-compiler: instancePath + '/' + key).
|
|
3613
|
+
if (schema.propertyNames === false) {
|
|
3614
|
+
const ki = ctx.varCounter++
|
|
3615
|
+
const p = pathExpr ? `${pathExpr}+'/'+_k${ki}` : `'/'+_k${ki}`
|
|
3616
|
+
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k${ki} in ${v}){_e.push({keyword:'not',instancePath:${p},schemaPath:'${schemaPrefix}/propertyNames',params:{},message:'boolean schema is false'});if(!_all)return{valid:false,errors:_e}}}`)
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3515
3619
|
// propertyNames
|
|
3516
3620
|
if (schema.propertyNames && typeof schema.propertyNames === 'object') {
|
|
3517
3621
|
const pn = schema.propertyNames
|
|
@@ -3550,7 +3654,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3550
3654
|
}
|
|
3551
3655
|
|
|
3552
3656
|
// items — starts after prefixItems (Draft 2020-12 semantics)
|
|
3553
|
-
if (schema.items) {
|
|
3657
|
+
if (schema.items !== undefined && schema.items !== true) {
|
|
3554
3658
|
const startIdx = schema.prefixItems ? schema.prefixItems.length : 0
|
|
3555
3659
|
const idx = `_j${ctx.varCounter}`
|
|
3556
3660
|
const elem = `_ei${ctx.varCounter}`
|
|
@@ -3572,7 +3676,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3572
3676
|
}
|
|
3573
3677
|
|
|
3574
3678
|
// contains
|
|
3575
|
-
if (schema.contains) {
|
|
3679
|
+
if (schema.contains !== undefined) {
|
|
3576
3680
|
const ci = ctx.varCounter++
|
|
3577
3681
|
const subLines = []
|
|
3578
3682
|
genCode(schema.contains, `_cv`, subLines, ctx)
|
|
@@ -3605,7 +3709,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3605
3709
|
}
|
|
3606
3710
|
|
|
3607
3711
|
// not
|
|
3608
|
-
if (schema.not) {
|
|
3712
|
+
if (schema.not !== undefined) {
|
|
3609
3713
|
const subLines = []
|
|
3610
3714
|
genCode(schema.not, '_nv', subLines, ctx)
|
|
3611
3715
|
const nfn = subLines.length === 0 ? `function(_nv){return true}` : `function(_nv){${subLines.join(';')};return true}`
|
|
@@ -3614,7 +3718,7 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3614
3718
|
}
|
|
3615
3719
|
|
|
3616
3720
|
// if/then/else
|
|
3617
|
-
if (schema.if) {
|
|
3721
|
+
if (schema.if !== undefined) {
|
|
3618
3722
|
const ifLines = []
|
|
3619
3723
|
genCode(schema.if, '_iv', ifLines, ctx)
|
|
3620
3724
|
const fi = ctx.varCounter++
|
|
@@ -3622,13 +3726,13 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3622
3726
|
? `function(_iv){return true}`
|
|
3623
3727
|
: `function(_iv){${ifLines.join(';')};return true}`
|
|
3624
3728
|
lines.push(`{const _if${fi}=${ifFn}`)
|
|
3625
|
-
if (schema.then) {
|
|
3729
|
+
if (schema.then !== undefined) {
|
|
3626
3730
|
lines.push(`if(_if${fi}(${v})){`)
|
|
3627
3731
|
genCodeE(schema.then, v, pathExpr, lines, ctx, schemaPrefix+'/then')
|
|
3628
3732
|
lines.push(`}`)
|
|
3629
3733
|
}
|
|
3630
|
-
if (schema.else) {
|
|
3631
|
-
lines.push(`${schema.then ? 'else' : `if(!_if${fi}(${v}))`}{`)
|
|
3734
|
+
if (schema.else !== undefined) {
|
|
3735
|
+
lines.push(`${schema.then !== undefined ? 'else' : `if(!_if${fi}(${v}))`}{`)
|
|
3632
3736
|
genCodeE(schema.else, v, pathExpr, lines, ctx, schemaPrefix+'/else')
|
|
3633
3737
|
lines.push(`}`)
|
|
3634
3738
|
}
|
|
@@ -3647,6 +3751,10 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
|
|
|
3647
3751
|
if (s.includes('unevaluatedProperties') || s.includes('unevaluatedItems')) return null
|
|
3648
3752
|
// Bail on self-referencing schemas — combined codegen doesn't support recursion
|
|
3649
3753
|
if (s.includes('"$ref":"#"')) return null
|
|
3754
|
+
// Defs on a $ref cycle compile as named functions in the boolean and
|
|
3755
|
+
// errors generators; this generator has no preamble for them, so it
|
|
3756
|
+
// declines and the router uses the errors generator.
|
|
3757
|
+
if (cyclicDefNames(schema.$defs || schema.definitions).size) return null
|
|
3650
3758
|
// Bail on additionalProperties as a schema — combined path doesn't emit the
|
|
3651
3759
|
// per-key sub-schema loop yet. The jsFn + errFn fallback handles it correctly.
|
|
3652
3760
|
if (hasAdditionalPropertiesSchema(schema)) return null
|
|
@@ -3665,19 +3773,16 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
|
|
|
3665
3773
|
if (!sharedCodegenGate(schema, schemaMap)) return null
|
|
3666
3774
|
if (schema.patternProperties) {
|
|
3667
3775
|
for (const [pat, sub] of Object.entries(schema.patternProperties)) {
|
|
3668
|
-
if (typeof sub === 'boolean') return null
|
|
3669
3776
|
if (/\\[pP]\{/.test(pat)) return null
|
|
3670
3777
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
3671
3778
|
}
|
|
3672
3779
|
}
|
|
3673
3780
|
if (schema.dependentSchemas) {
|
|
3674
3781
|
for (const sub of Object.values(schema.dependentSchemas)) {
|
|
3675
|
-
if (typeof sub === 'boolean') return null
|
|
3676
3782
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
3677
3783
|
}
|
|
3678
3784
|
}
|
|
3679
3785
|
if (schema.propertyNames) {
|
|
3680
|
-
if (typeof schema.propertyNames === 'boolean') return null
|
|
3681
3786
|
const pn = schema.propertyNames
|
|
3682
3787
|
const supported = ['maxLength', 'minLength', 'pattern', 'const', 'enum']
|
|
3683
3788
|
const keys = Object.keys(pn).filter(k => k !== '$schema')
|
|
@@ -3747,6 +3852,13 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
|
|
|
3747
3852
|
// If type check fails → push error, skip property checks (they'd crash).
|
|
3748
3853
|
function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
3749
3854
|
if (!schemaPrefix) schemaPrefix = '#'
|
|
3855
|
+
if (schema === false) {
|
|
3856
|
+
// Leading semicolon: the previous generated line may end in an
|
|
3857
|
+
// expression, and a line starting with `(` would be parsed as a call.
|
|
3858
|
+
lines.push(`;(_e||(_e=[])).push({keyword:'not',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}',params:{},message:'boolean schema is false'})`)
|
|
3859
|
+
return
|
|
3860
|
+
}
|
|
3861
|
+
if (schema === true) return
|
|
3750
3862
|
if (typeof schema !== 'object' || schema === null) return
|
|
3751
3863
|
|
|
3752
3864
|
// $ref — resolve local, anchor, and cross-schema refs
|
|
@@ -4181,6 +4293,12 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
4181
4293
|
}
|
|
4182
4294
|
}
|
|
4183
4295
|
|
|
4296
|
+
if (schema.propertyNames === false) {
|
|
4297
|
+
const ki = ctx.varCounter++
|
|
4298
|
+
const p = pathExpr ? `${pathExpr}+'/'+_k${ki}` : `'/'+_k${ki}`
|
|
4299
|
+
lines.push(`;if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k${ki} in ${v}){(_e||(_e=[])).push({keyword:'not',instancePath:${p},schemaPath:'${schemaPrefix}/propertyNames',params:{},message:'boolean schema is false'})}}`)
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4184
4302
|
// propertyNames — skip if already merged into patternProperties loop
|
|
4185
4303
|
if (schema.propertyNames && typeof schema.propertyNames === 'object' && !ctx._ppHandledPropertyNamesC) {
|
|
4186
4304
|
const pn = schema.propertyNames
|
|
@@ -4211,7 +4329,7 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
4211
4329
|
}
|
|
4212
4330
|
|
|
4213
4331
|
// items
|
|
4214
|
-
if (schema.items) {
|
|
4332
|
+
if (schema.items !== undefined && schema.items !== true) {
|
|
4215
4333
|
const startIdx = schema.prefixItems ? schema.prefixItems.length : 0
|
|
4216
4334
|
const idx = `_j${ctx.varCounter}`, elem = `_ei${ctx.varCounter}`
|
|
4217
4335
|
ctx.varCounter++
|
|
@@ -4232,7 +4350,7 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
4232
4350
|
}
|
|
4233
4351
|
|
|
4234
4352
|
// contains
|
|
4235
|
-
if (schema.contains) {
|
|
4353
|
+
if (schema.contains !== undefined) {
|
|
4236
4354
|
const ci = ctx.varCounter++
|
|
4237
4355
|
const subLines = []
|
|
4238
4356
|
genCode(schema.contains, `_cv`, subLines, ctx)
|
|
@@ -4263,7 +4381,7 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
4263
4381
|
}
|
|
4264
4382
|
|
|
4265
4383
|
// not
|
|
4266
|
-
if (schema.not) {
|
|
4384
|
+
if (schema.not !== undefined) {
|
|
4267
4385
|
const sl = []; genCode(schema.not, '_nv', sl, ctx)
|
|
4268
4386
|
const nfn = sl.length === 0 ? `function(_nv){return true}` : `function(_nv){${sl.join(';')};return true}`
|
|
4269
4387
|
const fi = ctx.varCounter++
|
|
@@ -4271,13 +4389,13 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
4271
4389
|
}
|
|
4272
4390
|
|
|
4273
4391
|
// if/then/else
|
|
4274
|
-
if (schema.if) {
|
|
4392
|
+
if (schema.if !== undefined) {
|
|
4275
4393
|
const sl = []; genCode(schema.if, '_iv', sl, ctx)
|
|
4276
4394
|
const fi = ctx.varCounter++
|
|
4277
4395
|
const ifFn = sl.length === 0 ? `function(_iv){return true}` : `function(_iv){${sl.join(';')};return true}`
|
|
4278
4396
|
lines.push(`{const _if${fi}=${ifFn}`)
|
|
4279
|
-
if (schema.then) { lines.push(`if(_if${fi}(${v})){`); genCodeC(schema.then, v, pathExpr, lines, ctx, schemaPrefix+'/then'); lines.push(`}`) }
|
|
4280
|
-
if (schema.else) { lines.push(`${schema.then ? 'else' : `if(!_if${fi}(${v}))`}{`); genCodeC(schema.else, v, pathExpr, lines, ctx, schemaPrefix+'/else'); lines.push(`}`) }
|
|
4397
|
+
if (schema.then !== undefined) { lines.push(`if(_if${fi}(${v})){`); genCodeC(schema.then, v, pathExpr, lines, ctx, schemaPrefix+'/then'); lines.push(`}`) }
|
|
4398
|
+
if (schema.else !== undefined) { lines.push(`${schema.then !== undefined ? 'else' : `if(!_if${fi}(${v}))`}{`); genCodeC(schema.else, v, pathExpr, lines, ctx, schemaPrefix+'/else'); lines.push(`}`) }
|
|
4281
4399
|
lines.push(`}`)
|
|
4282
4400
|
}
|
|
4283
4401
|
|
|
@@ -4362,7 +4480,7 @@ function _collectEval(schema, result, defs, schemaMap, refStack, isRoot) {
|
|
|
4362
4480
|
|
|
4363
4481
|
// contains: marks matching items as evaluated (not ALL items)
|
|
4364
4482
|
// Always set dynamic since which items match depends on the data
|
|
4365
|
-
if (schema.contains) {
|
|
4483
|
+
if (schema.contains !== undefined) {
|
|
4366
4484
|
result.dynamic = true
|
|
4367
4485
|
}
|
|
4368
4486
|
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "JSON Schema validation with first-class TypeScript and zero runtime cost. AOT compile to per-schema ESM modules with zero validator dependency. Generic Validator<T> for TypeBox/Zod/Valibot composition. Optional runtime API. Standard Schema V1 compatible.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"release:check": "node scripts/regen-safe-regex-source.js && node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_source_sync.js && node tests/test_version_sync.js",
|
|
45
45
|
"build": "cmake-js build --target ata",
|
|
46
46
|
"rebuild": "cmake-js rebuild --target ata",
|
|
47
|
-
"test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_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_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_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_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_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",
|
|
47
|
+
"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_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_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_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",
|
|
48
48
|
"bench:size": "node benchmark/bench_aot_size.mjs",
|
|
49
49
|
"test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
|
|
50
50
|
"test:compat": "node tests/test_compat.js",
|
|
@@ -112,13 +112,13 @@
|
|
|
112
112
|
"LICENSE"
|
|
113
113
|
],
|
|
114
114
|
"optionalDependencies": {
|
|
115
|
-
"@ata-validator/native-darwin-arm64": "1.
|
|
116
|
-
"@ata-validator/native-darwin-x64": "1.
|
|
117
|
-
"@ata-validator/native-linux-x64-gnu": "1.
|
|
118
|
-
"@ata-validator/native-linux-arm64-gnu": "1.
|
|
119
|
-
"@ata-validator/native-linux-x64-musl": "1.
|
|
120
|
-
"@ata-validator/native-linux-arm64-musl": "1.
|
|
121
|
-
"@ata-validator/native-win32-x64": "1.
|
|
115
|
+
"@ata-validator/native-darwin-arm64": "1.10.0",
|
|
116
|
+
"@ata-validator/native-darwin-x64": "1.10.0",
|
|
117
|
+
"@ata-validator/native-linux-x64-gnu": "1.10.0",
|
|
118
|
+
"@ata-validator/native-linux-arm64-gnu": "1.10.0",
|
|
119
|
+
"@ata-validator/native-linux-x64-musl": "1.10.0",
|
|
120
|
+
"@ata-validator/native-linux-arm64-musl": "1.10.0",
|
|
121
|
+
"@ata-validator/native-win32-x64": "1.10.0"
|
|
122
122
|
},
|
|
123
123
|
"peerDependencies": {
|
|
124
124
|
"yaml": "^2.0.0"
|