ata-validator 1.43.0 → 1.45.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/README.md +10 -10
- package/index.js +54 -0
- package/lib/clone-emit.js +5 -2
- package/lib/data-position-cache.js +12 -6
- package/lib/draft7.js +5 -7
- package/lib/formats.js +8 -2
- package/lib/interpreter.js +44 -51
- package/lib/js-compiler.js +197 -76
- package/lib/levenshtein.js +34 -22
- package/lib/native-positions.js +100 -0
- package/lib/plan-compiler.js +23 -25
- package/lib/rejections.js +14 -7
- package/lib/suggestions.js +27 -20
- package/lib/unique-items.js +139 -0
- package/lib/validator-core.js +71 -10
- package/lib/version.js +1 -1
- package/package.json +9 -9
package/lib/js-compiler.js
CHANGED
|
@@ -10,12 +10,20 @@
|
|
|
10
10
|
// forms answers a different question and costs a string per call.
|
|
11
11
|
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;if(aa){if(a.length!==b.length)return false;for(i=0;i<a.length;i++)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=0;i<ka.length;i++){var k=ka[i];if(!Object.prototype.hasOwnProperty.call(b,k)||!_deq(a[k],b[k]))return false}return true}"
|
|
12
12
|
|
|
13
|
-
// uniqueItems helpers
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
// uniqueItems helpers, emitted from the functions the interpreted engine
|
|
14
|
+
// calls (lib/unique-items.js): `_uq` answers whether every item is distinct.
|
|
15
|
+
// Loaded at the first schema with uniqueItems, not on every cold start.
|
|
16
|
+
// Comment lines and indentation are dropped: the text lands in every
|
|
17
|
+
// generated function and ahead-of-time module that checks uniqueItems.
|
|
18
|
+
let _uqSources = null
|
|
19
|
+
function uqSources () {
|
|
20
|
+
if (_uqSources === null) {
|
|
21
|
+
const u = require('./unique-items')
|
|
22
|
+
const compact = (fn) => String(fn).replace(/\n\s*\/\/[^\n]*/g, '').replace(/\n\s*/g, '\n')
|
|
23
|
+
_uqSources = { uq: compact(u._uqh) + compact(u._uq), uqp: 'var _uqpI=0;' + compact(u._uqp) }
|
|
24
|
+
}
|
|
25
|
+
return _uqSources
|
|
26
|
+
}
|
|
19
27
|
|
|
20
28
|
// Where a generator hoists a declaration so it runs once per compiled
|
|
21
29
|
// function rather than once per call. The boolean generator has a preamble
|
|
@@ -97,7 +105,7 @@ function emitPtrEsc(ctx) {
|
|
|
97
105
|
// `_uq` compares items with `_deq`, so it can never be hoisted alone.
|
|
98
106
|
function emitUq(ctx) {
|
|
99
107
|
emitDeq(ctx)
|
|
100
|
-
hoistOnce(ctx, '_uqHoisted',
|
|
108
|
+
hoistOnce(ctx, '_uqHoisted', uqSources().uq)
|
|
101
109
|
return '_uq'
|
|
102
110
|
}
|
|
103
111
|
|
|
@@ -107,15 +115,11 @@ function emitUq(ctx) {
|
|
|
107
115
|
// a later equal, paired with its first later equal. That is the pair the
|
|
108
116
|
// interpreted engine reports, and the generators used to report a different
|
|
109
117
|
// one (the earliest repeat, paired with what it repeats), so the same document
|
|
110
|
-
// named a different pair depending on the engine that answered.
|
|
111
|
-
//
|
|
112
|
-
// canonical string once an object appears. Building a canonical string for
|
|
113
|
-
// every item on every call, as the generators did, made reading a uniqueItems
|
|
114
|
-
// error up to ten times the cost of the verdict.
|
|
115
|
-
const UQP_HELPER = "var _uqpI=0;function _uqp(a){var n=a.length,i,k;if(n<2)return -1;if(n<=12){for(i=0;i<n-1;i++)for(k=i+1;k<n;k++)if(_deq(a[i],a[k])){_uqpI=i;return k}return -1}var prim=true;for(i=0;i<n;i++){var x=a[i];if(x!==null&&typeof x==='object'){prim=false;break}}var m=new Map(),bi=-1,bj=-1;for(i=0;i<n;i++){var c=prim?a[i]:_cn(a[i]);var p=m.get(c);if(p===undefined)m.set(c,i);else if(p>=0){if(bi<0||p<bi){bi=p;bj=i}m.set(c,-1)}}if(bi<0)return -1;_uqpI=bi;return bj}"
|
|
118
|
+
// named a different pair depending on the engine that answered. See
|
|
119
|
+
// lib/unique-items.js for how it finds the pair.
|
|
116
120
|
function emitUqp(ctx) {
|
|
117
121
|
emitUq(ctx)
|
|
118
|
-
hoistOnce(ctx, '_uqpHoisted',
|
|
122
|
+
hoistOnce(ctx, '_uqpHoisted', uqSources().uqp)
|
|
119
123
|
return '_uqp'
|
|
120
124
|
}
|
|
121
125
|
|
|
@@ -1065,6 +1069,16 @@ function hasOwnKey (d, key) {
|
|
|
1065
1069
|
return _hop.call(d, key)
|
|
1066
1070
|
}
|
|
1067
1071
|
|
|
1072
|
+
// Writes an own key. A schema can name a property, a definition or even a
|
|
1073
|
+
// keyword "__proto__", and assigning that key sets the copy's prototype
|
|
1074
|
+
// instead: the property was gone from the copy, so a constraint on it was
|
|
1075
|
+
// generated as nothing and accepted anything (a schema with such a property
|
|
1076
|
+
// and a reference the resolver rewrites, 1.40.1 to 1.43.0).
|
|
1077
|
+
function setOwn (o, k, v) {
|
|
1078
|
+
if (k === '__proto__') Object.defineProperty(o, k, { value: v, writable: true, enumerable: true, configurable: true })
|
|
1079
|
+
else o[k] = v
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1068
1082
|
const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'toString', 'valueOf',
|
|
1069
1083
|
'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString'])
|
|
1070
1084
|
|
|
@@ -1380,7 +1394,7 @@ function aliasChainEnds(def, defs) {
|
|
|
1380
1394
|
// source of evaluated properties the schema has; when it does not, the
|
|
1381
1395
|
// schema is not generated and the interpreter answers.
|
|
1382
1396
|
function unevalPropsModeled(schema, schemaMap) {
|
|
1383
|
-
const own =
|
|
1397
|
+
const own = { ...schema }
|
|
1384
1398
|
delete own.$defs
|
|
1385
1399
|
delete own.definitions
|
|
1386
1400
|
if (/"\$(?:ref|dynamicRef|recursiveRef)"/.test(JSON.stringify(own))) return false
|
|
@@ -1399,7 +1413,7 @@ function unevalPropsModeled(schema, schemaMap) {
|
|
|
1399
1413
|
if (typeof sub !== 'object' || sub === null) return false
|
|
1400
1414
|
const r = collectEvaluated(sub, schemaMap)
|
|
1401
1415
|
if (r.dynamic || r.allProps) return false
|
|
1402
|
-
const rest =
|
|
1416
|
+
const rest = { ...sub }
|
|
1403
1417
|
delete rest.patternProperties
|
|
1404
1418
|
const j = JSON.stringify(rest)
|
|
1405
1419
|
if (j.includes('"patternProperties"') || j.includes('"unevaluatedProperties"')) return false
|
|
@@ -1432,7 +1446,7 @@ function unevalPropsModeled(schema, schemaMap) {
|
|
|
1432
1446
|
if (typeof schema.if !== 'object' || schema.if === null) return false
|
|
1433
1447
|
const r = collectEvaluated(schema.if, schemaMap)
|
|
1434
1448
|
if (r.dynamic || r.allProps || r.props.length) return false
|
|
1435
|
-
const rest =
|
|
1449
|
+
const rest = { ...schema.if }
|
|
1436
1450
|
delete rest.patternProperties
|
|
1437
1451
|
const j = JSON.stringify(rest)
|
|
1438
1452
|
if (j.includes('"patternProperties"') || j.includes('"unevaluatedProperties"')) return false
|
|
@@ -1486,17 +1500,7 @@ function codegenSafe(schema, schemaMap) {
|
|
|
1486
1500
|
}
|
|
1487
1501
|
|
|
1488
1502
|
// Keys that collide with Object.prototype
|
|
1489
|
-
|
|
1490
|
-
for (const k of schema.required) {
|
|
1491
|
-
if (UNSAFE_KEYS.has(k)) return false
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
if (schema.properties) {
|
|
1495
|
-
for (const k of Object.keys(schema.properties)) {
|
|
1496
|
-
if (UNSAFE_KEYS.has(k)) return false
|
|
1497
|
-
if (k === '$ref') return false // property named "$ref" — confusing
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1503
|
+
|
|
1500
1504
|
|
|
1501
1505
|
// Unicode property escapes in pattern need 'u' flag — codegen uses RegExp without it
|
|
1502
1506
|
|
|
@@ -1627,10 +1631,8 @@ function codegenSafe(schema, schemaMap) {
|
|
|
1627
1631
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return false
|
|
1628
1632
|
}
|
|
1629
1633
|
}
|
|
1630
|
-
if (typeof schema.propertyNames === 'object' && schema.propertyNames !== null
|
|
1631
|
-
|
|
1632
|
-
if (Object.keys(schema.propertyNames).some(k => k !== '$schema' && !supported.includes(k))) return false
|
|
1633
|
-
}
|
|
1634
|
+
if (typeof schema.propertyNames === 'object' && schema.propertyNames !== null &&
|
|
1635
|
+
!isSimplePN(schema.propertyNames) && !codegenSafe(schema.propertyNames, schemaMap)) return false
|
|
1634
1636
|
|
|
1635
1637
|
// Recurse into sub-schemas — bail on boolean schemas in any position
|
|
1636
1638
|
// A boolean subschema is a constant that every generator emits at its
|
|
@@ -1968,8 +1970,21 @@ function bothDefsContainers(schema) {
|
|
|
1968
1970
|
schema.definitions !== null && typeof schema.definitions === 'object'
|
|
1969
1971
|
}
|
|
1970
1972
|
|
|
1973
|
+
// The answer depends on the schema and the map alone, and the three
|
|
1974
|
+
// generators ask it about the same prepared schema in turn, so the last
|
|
1975
|
+
// answer is kept: each check walks the whole schema, and the second and third
|
|
1976
|
+
// asked again what the first had found. One entry, compared by identity,
|
|
1977
|
+
// because a WeakMap entry per schema cost more than the walk it saved on a
|
|
1978
|
+
// small schema that only ever gets its verdict generated.
|
|
1979
|
+
let _gateSchema = null, _gateMap = null, _gateOk = false
|
|
1971
1980
|
function sharedCodegenGate(schema, schemaMap) {
|
|
1972
1981
|
if (typeof schema !== 'object' || schema === null) return true
|
|
1982
|
+
if (schema === _gateSchema && schemaMap === _gateMap) return _gateOk
|
|
1983
|
+
const ok = sharedCodegenGateUncached(schema, schemaMap)
|
|
1984
|
+
_gateSchema = schema; _gateMap = schemaMap; _gateOk = ok
|
|
1985
|
+
return ok
|
|
1986
|
+
}
|
|
1987
|
+
function sharedCodegenGateUncached(schema, schemaMap) {
|
|
1973
1988
|
if (bothDefsContainers(schema)) return false
|
|
1974
1989
|
if (hasSameInstanceRefCycle(schema)) return false
|
|
1975
1990
|
if (!codegenSafe(schema, schemaMap)) return false
|
|
@@ -1985,6 +2000,15 @@ function sharedCodegenGate(schema, schemaMap) {
|
|
|
1985
2000
|
// `type: 'string'` and annotations go, on a copy. Anything else is left as
|
|
1986
2001
|
// written and the gate declines. Errors keep the interpreted engine's path.
|
|
1987
2002
|
const PN_SUPPORTED = new Set(['maxLength', 'minLength', 'pattern', 'const', 'enum'])
|
|
2003
|
+
// Whether the generators can write `propertyNames` as checks on the key in the
|
|
2004
|
+
// object's own key loop. Any other subschema is applied to each key as a
|
|
2005
|
+
// value of its own (genPropertyNamesNode and its error and combined
|
|
2006
|
+
// counterparts), which is what the interpreter does.
|
|
2007
|
+
function isSimplePN (pn) {
|
|
2008
|
+
if (pn === null || typeof pn !== 'object' || Array.isArray(pn)) return false
|
|
2009
|
+
for (const k of Object.keys(pn)) if (k !== '$schema' && !PN_SUPPORTED.has(k)) return false
|
|
2010
|
+
return true
|
|
2011
|
+
}
|
|
1988
2012
|
const PN_ANNOTATIONS = new Set(['$schema', '$comment', 'title', 'description', 'examples', 'default', 'deprecated', 'readOnly', 'writeOnly'])
|
|
1989
2013
|
function simplePropertyNames(pn, defs) {
|
|
1990
2014
|
const seen = new Set()
|
|
@@ -2006,7 +2030,7 @@ function simplePropertyNames(pn, defs) {
|
|
|
2006
2030
|
return null
|
|
2007
2031
|
}
|
|
2008
2032
|
if (!PN_SUPPORTED.has(k)) return null
|
|
2009
|
-
out
|
|
2033
|
+
setOwn(out, k, val)
|
|
2010
2034
|
}
|
|
2011
2035
|
return out
|
|
2012
2036
|
}
|
|
@@ -2037,14 +2061,14 @@ function normalizePropertyNamesWalk(root) {
|
|
|
2037
2061
|
return out
|
|
2038
2062
|
}
|
|
2039
2063
|
let out = node
|
|
2040
|
-
const set = (k, v) => { if (out === node) out = { ...node }; out
|
|
2064
|
+
const set = (k, v) => { if (out === node) out = { ...node }; setOwn(out, k, v) }
|
|
2041
2065
|
for (const k of SUBSCHEMA_MAPS) {
|
|
2042
2066
|
const map = node[k]
|
|
2043
2067
|
if (!map || typeof map !== 'object') continue
|
|
2044
2068
|
let copy = map
|
|
2045
2069
|
for (const [name, sub] of Object.entries(map)) {
|
|
2046
2070
|
const w = walk(sub)
|
|
2047
|
-
if (w !== sub) { if (copy === map) copy = { ...map }; copy
|
|
2071
|
+
if (w !== sub) { if (copy === map) copy = { ...map }; setOwn(copy, name, w) }
|
|
2048
2072
|
}
|
|
2049
2073
|
if (copy !== map) set(k, copy)
|
|
2050
2074
|
}
|
|
@@ -2162,7 +2186,7 @@ function normalizeDeepRefsWalk(root) {
|
|
|
2162
2186
|
if (inner) return node
|
|
2163
2187
|
let out = node
|
|
2164
2188
|
memo.set(node, out)
|
|
2165
|
-
const own = (k, v) => { if (out === node) { out = { ...node }; memo.set(node, out) } out
|
|
2189
|
+
const own = (k, v) => { if (out === node) { out = { ...node }; memo.set(node, out) } setOwn(out, k, v) }
|
|
2166
2190
|
if (typeof node.$ref === 'string' && names.has(node.$ref)) own('$ref', '#/' + defsKey + '/' + names.get(node.$ref))
|
|
2167
2191
|
for (const k of Object.keys(node)) {
|
|
2168
2192
|
if (k === '$ref' || k === 'enum' || k === 'const' || k === 'default' || k === 'examples') continue
|
|
@@ -2211,10 +2235,39 @@ function normalizeRefs(root, schemaMap) {
|
|
|
2211
2235
|
return out
|
|
2212
2236
|
}
|
|
2213
2237
|
const SKIP_VALUE_KEYS = new Set(['enum', 'const', 'default', 'examples'])
|
|
2238
|
+
// A schema whose every reference is `#` or a plain `#/$defs/name` (or
|
|
2239
|
+
// `definitions`), with no `$id` or `$anchor` below the root to move the base
|
|
2240
|
+
// they resolve against, needs nothing from the resolver: each reference
|
|
2241
|
+
// already reads as the generators read it. Telling that from the text skips
|
|
2242
|
+
// loading the interpreted engine and indexing the schema, which was most of
|
|
2243
|
+
// what preparing a small schema for code generation cost.
|
|
2244
|
+
const LOCAL_ONLY_REF = /^#(?:\/(?:\$defs|definitions)\/[^/~%]+)?$/
|
|
2245
|
+
function refsAllPlainLocal (root, text) {
|
|
2246
|
+
const ids = text.split('"$id":').length - 1
|
|
2247
|
+
if (ids > (typeof root.$id === 'string' ? 1 : 0) || text.includes('"$anchor":')) return false
|
|
2248
|
+
const re = /"\$ref":("(?:[^"\\]|\\.)*")/g
|
|
2249
|
+
let m
|
|
2250
|
+
while ((m = re.exec(text)) !== null) {
|
|
2251
|
+
let ref
|
|
2252
|
+
try { ref = JSON.parse(m[1]) } catch { return false }
|
|
2253
|
+
if (!LOCAL_ONLY_REF.test(ref)) return false
|
|
2254
|
+
}
|
|
2255
|
+
return true
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// What the text of a schema prepared for code generation says about its size,
|
|
2259
|
+
// kept from the stringify normalizeRefsWalk makes anyway: its length when it
|
|
2260
|
+
// holds no `$ref`, -1 when it does. expandedChars counts at most 8 for each
|
|
2261
|
+
// character of a schema without references, so a short one is known not to
|
|
2262
|
+
// reach EXPANDED_SHARE without walking it again at every compile.
|
|
2263
|
+
const _refFreeLength = new WeakMap()
|
|
2214
2264
|
function normalizeRefsWalk(root, schemaMap) {
|
|
2215
2265
|
const text = JSON.stringify(root)
|
|
2216
|
-
|
|
2266
|
+
const hasRef = text.includes('"$ref"')
|
|
2267
|
+
_refFreeLength.set(root, hasRef ? -1 : text.length)
|
|
2268
|
+
if (!hasRef) return root
|
|
2217
2269
|
if (/"\$(?:dynamicRef|recursiveRef|dynamicAnchor|recursiveAnchor)"/.test(text)) return null
|
|
2270
|
+
if (refsAllPlainLocal(root, text)) return root
|
|
2218
2271
|
const { indexSchemas, resolveRef } = require('./interpreter')._refInternals
|
|
2219
2272
|
const state = indexSchemas(root, schemaMap && schemaMap.size ? schemaMap : null)
|
|
2220
2273
|
const rootDialect = typeof root.$schema === 'string' ? root.$schema.replace(/#$/, '') : null
|
|
@@ -2280,7 +2333,7 @@ function normalizeRefsWalk(root, schemaMap) {
|
|
|
2280
2333
|
out.$ref = t === root ? '#' : '#/' + defsKey + '/' + names.get(t)
|
|
2281
2334
|
continue
|
|
2282
2335
|
}
|
|
2283
|
-
out
|
|
2336
|
+
setOwn(out, k, SKIP_VALUE_KEYS.has(k) ? node[k] : walk(node[k]))
|
|
2284
2337
|
}
|
|
2285
2338
|
return out
|
|
2286
2339
|
}
|
|
@@ -2299,6 +2352,46 @@ function prepareForCodegen(schema, schemaMap) {
|
|
|
2299
2352
|
}
|
|
2300
2353
|
|
|
2301
2354
|
const VERDICT_SPLIT = 64 * 1024
|
|
2355
|
+
// The schema's size with every local reference followed, each definition
|
|
2356
|
+
// sized once, annotations left out. A rough measure of the expanded verdict
|
|
2357
|
+
// source: on SchemaStore that runs about 1.4 times it at the median, and far
|
|
2358
|
+
// less where allOf merges. Past EXPANDED_SHARE the verdict is generated with
|
|
2359
|
+
// shared definitions from the start, instead of expanded first and thrown
|
|
2360
|
+
// away: SARIF comes to 2.1 million, and generating its 2.8 MB of expanded
|
|
2361
|
+
// source only to discard it was most of a cold first validate() (70 ms of the
|
|
2362
|
+
// 87 a command line run took to report one error). The bound is set where
|
|
2363
|
+
// only schemas that split anyway reach it: SARIF 2.1 measures about 706
|
|
2364
|
+
// thousand, while utcm-monitor (370 thousand) and accelerator (262 thousand)
|
|
2365
|
+
// stay expanded and keep their speed.
|
|
2366
|
+
const EXPANDED_SHARE = 400 * 1024
|
|
2367
|
+
const SIZE_SKIPS = new Set(['$defs', 'definitions', 'description', 'title', 'examples', 'default', '$comment', 'markdownDescription', 'deprecated', 'readOnly', 'writeOnly'])
|
|
2368
|
+
function expandedChars (root, limit) {
|
|
2369
|
+
const defs = (root && (root.$defs || root.definitions)) || {}
|
|
2370
|
+
const memo = new Map()
|
|
2371
|
+
const size = (n, stack) => {
|
|
2372
|
+
if (n === null || typeof n !== 'object') return 8
|
|
2373
|
+
let t = 2
|
|
2374
|
+
if (Array.isArray(n)) { for (const x of n) { t += size(x, stack) + 1; if (t > limit) return t } return t }
|
|
2375
|
+
for (const k of Object.keys(n)) {
|
|
2376
|
+
if (SIZE_SKIPS.has(k)) continue
|
|
2377
|
+
const v = n[k]
|
|
2378
|
+
if (k === '$ref' && typeof v === 'string') {
|
|
2379
|
+
const m = /^#\/(?:\$defs|definitions)\/([^/]+)$/.exec(v)
|
|
2380
|
+
if (m && defs[m[1]] !== undefined && !stack.has(m[1])) {
|
|
2381
|
+
let d = memo.get(m[1])
|
|
2382
|
+
if (d === undefined) { stack.add(m[1]); d = size(defs[m[1]], stack); stack.delete(m[1]); memo.set(m[1], d) }
|
|
2383
|
+
t += d
|
|
2384
|
+
if (t > limit) return t
|
|
2385
|
+
continue
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
t += k.length + 3 + size(v, stack)
|
|
2389
|
+
if (t > limit) return t
|
|
2390
|
+
}
|
|
2391
|
+
return t
|
|
2392
|
+
}
|
|
2393
|
+
return size(root, new Set())
|
|
2394
|
+
}
|
|
2302
2395
|
function sourceSize (lines, ctx) {
|
|
2303
2396
|
let n = 0
|
|
2304
2397
|
for (const l of lines) n += l.length
|
|
@@ -2340,13 +2433,7 @@ function compileToJSCodegen(schema, schemaMap, userFormats, opts) {
|
|
|
2340
2433
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
2341
2434
|
}
|
|
2342
2435
|
}
|
|
2343
|
-
|
|
2344
|
-
if (schema.propertyNames) {
|
|
2345
|
-
const pn = schema.propertyNames
|
|
2346
|
-
const supported = ['maxLength', 'minLength', 'pattern', 'const', 'enum']
|
|
2347
|
-
const keys = Object.keys(pn).filter(k => k !== '$schema')
|
|
2348
|
-
if (keys.some(k => !supported.includes(k))) return null
|
|
2349
|
-
}
|
|
2436
|
+
if (schema.propertyNames && typeof schema.propertyNames === 'object' && !isSimplePN(schema.propertyNames) && !codegenSafe(schema.propertyNames, schemaMap)) return null
|
|
2350
2437
|
|
|
2351
2438
|
// Build anchors map for $ref/#anchor and $dynamicRef resolution
|
|
2352
2439
|
const anchors = {}
|
|
@@ -2389,7 +2476,11 @@ function compileToJSCodegen(schema, schemaMap, userFormats, opts) {
|
|
|
2389
2476
|
// (79 KB of schema) came to 2.8 MB of verdict source, and compiling it was
|
|
2390
2477
|
// half of the first validate() call.
|
|
2391
2478
|
let ctx, lines
|
|
2392
|
-
|
|
2479
|
+
const refFree = _refFreeLength.get(inputSchema)
|
|
2480
|
+
const shareFirst = refFree !== undefined && refFree >= 0 && refFree * 8 <= EXPANDED_SHARE
|
|
2481
|
+
? false
|
|
2482
|
+
: expandedChars(schema, EXPANDED_SHARE) > EXPANDED_SHARE
|
|
2483
|
+
for (const share of shareFirst ? [true] : [false, true]) {
|
|
2393
2484
|
ctx = { varCounter: 0, helpers: [], helperCode: [], preamble: [], shared: [], closureVars: ['_cpLen'], closureVals: [_cpLen], rootDefs, refStack: new Set(), schemaMap: schemaMap || null, anchors, rootSchema: inputSchema, userFormats: userFormats || null, removeNodes,
|
|
2394
2485
|
keywords: (opts && opts.keywords) || null, kwEmitted: null, shareDefs: share }
|
|
2395
2486
|
if (ctx.keywords) ctx.kwEmitted = new Set()
|
|
@@ -3394,6 +3485,10 @@ function genUniqueItemsNode(schema, v, lines, ctx, knownType, hoisted, isArr, ty
|
|
|
3394
3485
|
// up. The previous form built the canonical closure and a Set on
|
|
3395
3486
|
// every call, even for [1, 2, 3].
|
|
3396
3487
|
inner = `if(!${emitUq(ctx)}(${v}))return false`
|
|
3488
|
+
} else if (ctx.helperCode) {
|
|
3489
|
+
// Inside the error or combined function, whose helper code is emitted
|
|
3490
|
+
// into its source, the same hoisted helper.
|
|
3491
|
+
inner = `if(!${emitUq(ctx)}(${v}))return false`
|
|
3397
3492
|
} else {
|
|
3398
3493
|
inner = `const _cn${si}=function(x){if(x===null||typeof x!=='object')return typeof x+':'+x;if(Array.isArray(x))return'['+x.map(_cn${si}).join(',')+']';return'{'+Object.keys(x).sort().map(function(k){return JSON.stringify(k)+':'+_cn${si}(x[k])}).join(',')+'}'};const _s${si}=new Set();for(let _i=0;_i<${v}.length;_i++){const _k=_cn${si}(${v}[_i]);if(_s${si}.has(_k))return false;_s${si}.add(_k)}`
|
|
3399
3494
|
}
|
|
@@ -3438,6 +3533,13 @@ function genPropertyNamesNode(schema, v, lines, ctx, knownType, isObj) {
|
|
|
3438
3533
|
const pn = schema.propertyNames
|
|
3439
3534
|
const ki = ctx.varCounter++
|
|
3440
3535
|
const guard = isObj ? '' : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v}))`
|
|
3536
|
+
if (!isSimplePN(pn)) {
|
|
3537
|
+
const sub = []
|
|
3538
|
+
genCode(pn, '_pv', sub, ctx)
|
|
3539
|
+
if (sub.length === 0) return
|
|
3540
|
+
lines.push(`${guard}{const _pnf${ki}=function(_pv){${sub.join(';')};return true};for(const _k${ki} in ${v}){if(!_pnf${ki}(_k${ki}))return false}}`)
|
|
3541
|
+
return
|
|
3542
|
+
}
|
|
3441
3543
|
lines.push(`${guard}{for(const _k${ki} in ${v}){`)
|
|
3442
3544
|
if (pn.minLength !== undefined) lines.push(`if(_k${ki}.length<${pn.minLength})return false`)
|
|
3443
3545
|
if (pn.maxLength !== undefined) lines.push(`if(_k${ki}.length>${pn.maxLength})return false`)
|
|
@@ -3760,7 +3862,7 @@ function genRefNode(schema, v, lines, ctx, knownType, hasSiblings) {
|
|
|
3760
3862
|
function genPatternPropertiesNode(schema, v, lines, ctx, knownType, isObj) {
|
|
3761
3863
|
let handledPropertyNames = false
|
|
3762
3864
|
const ppEntries = Object.entries(schema.patternProperties)
|
|
3763
|
-
const pn = schema.propertyNames
|
|
3865
|
+
const pn = isSimplePN(schema.propertyNames) ? schema.propertyNames : null
|
|
3764
3866
|
const pi = ctx.varCounter++
|
|
3765
3867
|
const kVar = `_ppk${pi}`
|
|
3766
3868
|
|
|
@@ -4821,12 +4923,7 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
|
|
|
4821
4923
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
4822
4924
|
}
|
|
4823
4925
|
}
|
|
4824
|
-
if (schema.propertyNames)
|
|
4825
|
-
const pn = schema.propertyNames
|
|
4826
|
-
const supported = ['maxLength', 'minLength', 'pattern', 'const', 'enum']
|
|
4827
|
-
const keys = Object.keys(pn).filter(k => k !== '$schema')
|
|
4828
|
-
if (keys.some(k => !supported.includes(k))) return null
|
|
4829
|
-
}
|
|
4926
|
+
if (schema.propertyNames && typeof schema.propertyNames === 'object' && !isSimplePN(schema.propertyNames) && !codegenSafe(schema.propertyNames, schemaMap)) return null
|
|
4830
4927
|
|
|
4831
4928
|
// Build anchors map for $ref/#anchor and $dynamicRef resolution
|
|
4832
4929
|
const eRootDefs = schema.$defs || schema.definitions || null
|
|
@@ -5114,7 +5211,7 @@ function emitBranchCollapse (branches, keyword, v, pathExpr, lines, ctx, schemaP
|
|
|
5114
5211
|
// In the combined function `_e` is created on the first error and every
|
|
5115
5212
|
// error is collected (`_all` is true there), so the report only pushes.
|
|
5116
5213
|
const report = ctx.inCombined && !ctx.inBranchFn
|
|
5117
|
-
? `const ${collapsed}=__ataCollapse('${branchKw}',${resArr},_pp${fi},'${branchSp}',${ord},_bt${fi});if(${collapsed})(_e
|
|
5214
|
+
? `const ${collapsed}=__ataCollapse('${branchKw}',${resArr},_pp${fi},'${branchSp}',${ord},_bt${fi});if(${collapsed})(_e=_ap(_e,${collapsed}))`
|
|
5118
5215
|
: `const ${collapsed}=__ataCollapse('${branchKw}',${resArr},_pp${fi},'${branchSp}',${ord},_bt${fi});if(${collapsed}){_e.push(${collapsed});if(!_all)return{valid:false,errors:_e}}`
|
|
5119
5216
|
// The node's runtime unevaluatedProperties already counted how many of
|
|
5120
5217
|
// these branches pass, through the annotation functions, which answer the
|
|
@@ -5130,7 +5227,7 @@ function emitBranchCollapse (branches, keyword, v, pathExpr, lines, ctx, schemaP
|
|
|
5130
5227
|
// branch errors, so it is built from the bits the count left.
|
|
5131
5228
|
if (guard !== '' && keyword === 'oneOf' && counted.mask && ctx.inCombined && !ctx.inBranchFn) {
|
|
5132
5229
|
const ord0 = ctx.noOrdinal ? 'null' : (() => { const o = ordinalFor(ctx.rootSchema, unescapeSp(branchSp)); return o === null ? 'null' : o })()
|
|
5133
|
-
guard = `if(${same}&&${counted.count}>1){const _pi${fi}=[];for(let _k=0;_k<${branches.length};_k++)if((${counted.mask}>>_k)&1)_pi${fi}.push(_k);(_e
|
|
5230
|
+
guard = `if(${same}&&${counted.count}>1){const _pi${fi}=[];for(let _k=0;_k<${branches.length};_k++)if((${counted.mask}>>_k)&1)_pi${fi}.push(_k);(_e=_ap(_e,__ataMulti(_pi${fi},${branches.length},${pp},'${branchSp}',${ord0})))}else if(!(${same}&&${counted.count}===1))`
|
|
5134
5231
|
}
|
|
5135
5232
|
// Without such a count, each branch also gets a verdict function, and the
|
|
5136
5233
|
// passing branches are counted with those: a oneOf with exactly one and an
|
|
@@ -5191,7 +5288,7 @@ function emitBranchCollapse (branches, keyword, v, pathExpr, lines, ctx, schemaP
|
|
|
5191
5288
|
? `const _pi${fi}=[];for(let _k=0;_k<${L};_k++)if((_m${fi}>>_k)&1)_pi${fi}.push(_k);`
|
|
5192
5289
|
: ''
|
|
5193
5290
|
const multi = `__ataMulti(_pi${fi},${L},_pp${fi},'${branchSp}',${ord})`
|
|
5194
|
-
const pushMulti = top ? `(_e
|
|
5291
|
+
const pushMulti = top ? `(_e=_ap(_e,${multi}))` : `_e.push(${multi});if(!_all)return{valid:false,errors:_e}`
|
|
5195
5292
|
const count = tests.map((t, i) => `if(${t}){_n${fi}++;${mk ? `_m${fi}|=${1 << i}` : `_pi${fi}.push(${i})`}}`).join('')
|
|
5196
5293
|
lines.push(`{const _pp${fi}=${pp};let _n${fi}=0${mk ? `,_m${fi}=0` : `,_pi${fi}=[]`};${count}if(_n${fi}!==1){if(_all===0){_e.push(null);return{valid:false,errors:_e}}if(_n${fi}>1){${collect}${pushMulti}}else{const ${resArr}=[];for(let _bi=0;_bi<${L};_bi++)${resArr}.push(${call});${report}}}}`)
|
|
5197
5294
|
}
|
|
@@ -5791,6 +5888,13 @@ function genPropertyNamesE(schema, v, pathExpr, lines, ctx, schemaPrefix, fail)
|
|
|
5791
5888
|
const pn = schema.propertyNames
|
|
5792
5889
|
const ki = ctx.varCounter++
|
|
5793
5890
|
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k${ki} in ${v}){`)
|
|
5891
|
+
// The subschema checks the key as a value, and its errors are reported at
|
|
5892
|
+
// the object, as the interpreter reports them.
|
|
5893
|
+
if (!isSimplePN(pn)) {
|
|
5894
|
+
genCodeE(pn, `_k${ki}`, pathExpr, lines, ctx, schemaPrefix + '/propertyNames')
|
|
5895
|
+
lines.push(`}}`)
|
|
5896
|
+
return
|
|
5897
|
+
}
|
|
5794
5898
|
if (pn.minLength !== undefined) {
|
|
5795
5899
|
lines.push(`if(_k${ki}.length<${pn.minLength}){${fail('minLength', 'propertyNames/minLength', `{limit:${pn.minLength}}`, `'must NOT have fewer than ${pn.minLength} characters'`)}}`)
|
|
5796
5900
|
}
|
|
@@ -5916,7 +6020,7 @@ function sharedErr (lit, rootSchema) {
|
|
|
5916
6020
|
// the document. `paramsShared` names a frozen params object hoisted for a
|
|
5917
6021
|
// static site, so a static error's params stay one shared object as before.
|
|
5918
6022
|
function errPushC(ctx, keyword, sp, pathExpr, paramsCode, msgCode, valueExpr, lean, paramsShared) {
|
|
5919
|
-
if (!ctx.rich) return `(_e
|
|
6023
|
+
if (!ctx.rich) return `(_e=_ap(_e,${lean}))`
|
|
5920
6024
|
if (!ctx.richMk) {
|
|
5921
6025
|
ctx.richMk = true
|
|
5922
6026
|
const { makeRich } = require('./enrich-site')
|
|
@@ -5932,7 +6036,7 @@ function errPushC(ctx, keyword, sp, pathExpr, paramsCode, msgCode, valueExpr, le
|
|
|
5932
6036
|
// the definition and the reference's own comes in as `_sp`.
|
|
5933
6037
|
const rel = sp.startsWith(SP_PLACEHOLDER)
|
|
5934
6038
|
ctx.closureVals.push(errorSite(keyword, unescapeSp(rel ? sp.slice(SP_PLACEHOLDER.length) : sp), paramsVal, fmt))
|
|
5935
|
-
return `(_e
|
|
6039
|
+
return `(_e=_ap(_e,_mk(${siteVar},${pathExpr || '""'},${paramsShared || paramsCode},${msgCode},${valueExpr},d${rel ? ',_sp' : ''})))`
|
|
5936
6040
|
}
|
|
5937
6041
|
|
|
5938
6042
|
function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats, opts) {
|
|
@@ -5978,12 +6082,7 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats, opts)
|
|
|
5978
6082
|
if (typeof sub === 'object' && sub !== null && !codegenSafe(sub, schemaMap)) return null
|
|
5979
6083
|
}
|
|
5980
6084
|
}
|
|
5981
|
-
if (schema.propertyNames)
|
|
5982
|
-
const pn = schema.propertyNames
|
|
5983
|
-
const supported = ['maxLength', 'minLength', 'pattern', 'const', 'enum']
|
|
5984
|
-
const keys = Object.keys(pn).filter(k => k !== '$schema')
|
|
5985
|
-
if (keys.some(k => !supported.includes(k))) return null
|
|
5986
|
-
}
|
|
6085
|
+
if (schema.propertyNames && typeof schema.propertyNames === 'object' && !isSimplePN(schema.propertyNames) && !codegenSafe(schema.propertyNames, schemaMap)) return null
|
|
5987
6086
|
|
|
5988
6087
|
// Build anchors map for $ref/#anchor and $dynamicRef resolution
|
|
5989
6088
|
const cRootDefs = schema.$defs || schema.definitions || null
|
|
@@ -6019,7 +6118,11 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats, opts)
|
|
|
6019
6118
|
if (needsBaseTracking(schema, schemaMap, new Set())) return null
|
|
6020
6119
|
|
|
6021
6120
|
// runtimeShape: errors without the ordinal, see compileToJSCodegenWithErrors.
|
|
6022
|
-
|
|
6121
|
+
// `_ap` adds an error to a list that may not exist yet: the first one
|
|
6122
|
+
// creates it as a one-element literal. Pushing onto an empty `[]` grew its
|
|
6123
|
+
// backing store on every rejection, about 7% of reading errors across the
|
|
6124
|
+
// official suite.
|
|
6125
|
+
const ctx = { noOrdinal: !!(opts && opts.runtimeShape), rich: !!(opts && opts.rich), inCombined: true, hoisted: [], rootC: schema, varCounter: 0, helperCode: ['function _ap(e,x){if(e===undefined)return[x];e.push(x);return e}'], shared: [], closureVars: ['_cpLen'], closureVals: [_cpLen],
|
|
6023
6126
|
rootDefs: cRootDefs, refStack: new Set(), schemaMap: schemaMap || null, anchors: cAnchors, rootSchema: inputSchema, userFormats: userFormats || null }
|
|
6024
6127
|
const lines = []
|
|
6025
6128
|
try {
|
|
@@ -6135,7 +6238,7 @@ function typeMismatchRest(schema, types) {
|
|
|
6135
6238
|
for (const k of Object.keys(schema)) {
|
|
6136
6239
|
if (drop.has(k)) continue
|
|
6137
6240
|
if (rest === null) rest = {}
|
|
6138
|
-
rest
|
|
6241
|
+
setOwn(rest, k, schema[k])
|
|
6139
6242
|
}
|
|
6140
6243
|
if (rest === null) return null
|
|
6141
6244
|
// A node whose reference was already written (genCodeCNode) keeps that
|
|
@@ -6182,6 +6285,14 @@ function emitDefFnC(key, target, cyclic, v, pathExpr, lines, ctx, schemaPrefix)
|
|
|
6182
6285
|
ctx.defFnsC.set(key, fnName)
|
|
6183
6286
|
const outerHoisted = ctx.hoisted
|
|
6184
6287
|
ctx.hoisted = []
|
|
6288
|
+
// The body is a function of its own: a reference the caller was in the
|
|
6289
|
+
// middle of expanding is not on the stack inside it, where reaching it
|
|
6290
|
+
// again is a call (this function, or another one) rather than a loop.
|
|
6291
|
+
// With the caller's stack, a definition expanded once on the way into
|
|
6292
|
+
// the root's function was taken for a cycle inside it, and the schema
|
|
6293
|
+
// (a tree whose nodes hold trees) lost its one-pass function.
|
|
6294
|
+
const outerRefs = ctx.refStack
|
|
6295
|
+
ctx.refStack = new Set()
|
|
6185
6296
|
const bodyLines = []
|
|
6186
6297
|
let hoisted
|
|
6187
6298
|
try {
|
|
@@ -6189,6 +6300,7 @@ function emitDefFnC(key, target, cyclic, v, pathExpr, lines, ctx, schemaPrefix)
|
|
|
6189
6300
|
} finally {
|
|
6190
6301
|
hoisted = ctx.hoisted
|
|
6191
6302
|
ctx.hoisted = outerHoisted
|
|
6303
|
+
ctx.refStack = outerRefs
|
|
6192
6304
|
}
|
|
6193
6305
|
let body = bodyLines.join('\n ')
|
|
6194
6306
|
body = body.split(`'${SP_PLACEHOLDER}`).join(`_sp+'`).split(`"${SP_PLACEHOLDER}`).join(`_sp+"`)
|
|
@@ -6236,7 +6348,7 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
6236
6348
|
if (Object.keys(schema).some((k) => !REF_NEUTRAL_SIBLINGS.has(k) && !k.startsWith('x-'))) {
|
|
6237
6349
|
if (!ctx.inCombined || ctx.inBranchFn) throw DECLINE
|
|
6238
6350
|
genCodeCNode({ $ref: schema.$ref }, v, pathExpr, lines, ctx, schemaPrefix)
|
|
6239
|
-
const rest =
|
|
6351
|
+
const rest = { ...schema }
|
|
6240
6352
|
Object.defineProperty(rest, REF_DONE, { value: true })
|
|
6241
6353
|
genCodeCNode(rest, v, pathExpr, lines, ctx, schemaPrefix)
|
|
6242
6354
|
return
|
|
@@ -6347,12 +6459,12 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
6347
6459
|
const lit = {keyword, instancePath: pathVal, schemaPath: spVal, params: Object.freeze(paramsVal), message: msgVal}
|
|
6348
6460
|
if (o !== null) lit._o = o
|
|
6349
6461
|
ctx.closureVals.push(sharedErr(lit, ctx.rootSchema))
|
|
6350
|
-
return `(_e
|
|
6462
|
+
return `(_e=_ap(_e,${errVar}))`
|
|
6351
6463
|
}
|
|
6352
6464
|
}
|
|
6353
6465
|
}
|
|
6354
6466
|
// Dynamic path (e.g., array index): inline as before
|
|
6355
|
-
return `(_e
|
|
6467
|
+
return `(_e=_ap(_e,{keyword:'${keyword}',instancePath:${pathExpr||'""'},schemaPath:'${sp}',params:${paramsCode},message:${msgCode}${ordinalField(ctx, sp)}}))`
|
|
6356
6468
|
}
|
|
6357
6469
|
|
|
6358
6470
|
// enum and const apply to a value of any type, so a value that fails
|
|
@@ -6442,7 +6554,7 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
6442
6554
|
const pathVal = pathExpr ? literalValue(pathExpr) : ''
|
|
6443
6555
|
ctx.closureVars.push(errVar)
|
|
6444
6556
|
ctx.closureVals.push(sharedErr({keyword: 'required', instancePath: pathVal, schemaPath: unescapeSp(`${schemaPrefix}/required`), params: Object.freeze({missingProperty: key}), message: `must have required property '${key}'`}, ctx.rootSchema))
|
|
6445
|
-
lines.push(`if(${check}){(_e
|
|
6557
|
+
lines.push(`if(${check}){(_e=_ap(_e,${errVar}))}`)
|
|
6446
6558
|
} else {
|
|
6447
6559
|
lines.push(`if(${check}){${errPushC(ctx, 'required', `${schemaPrefix}/required`, pathExpr, `{missingProperty:'${esc(key)}'}`, `"must have required property '${esc(key)}'"`, v, `{keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/required'${ordinalField(ctx, `${schemaPrefix}/required`)},params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'"}`)}}`)
|
|
6448
6560
|
}
|
|
@@ -6456,7 +6568,7 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
6456
6568
|
const pathVal = pathExpr ? literalValue(pathExpr) : ''
|
|
6457
6569
|
ctx.closureVars.push(errVar)
|
|
6458
6570
|
ctx.closureVals.push(sharedErr({keyword: 'required', instancePath: pathVal, schemaPath: unescapeSp(`${schemaPrefix}/required`), params: Object.freeze({missingProperty: key}), message: `must have required property '${key}'`}, ctx.rootSchema))
|
|
6459
|
-
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&!${ownKeyExpr(ctx, v, key)}){(_e
|
|
6571
|
+
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&!${ownKeyExpr(ctx, v, key)}){(_e=_ap(_e,${errVar}))}`)
|
|
6460
6572
|
} else {
|
|
6461
6573
|
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&!${ownKeyExpr(ctx, v, key)}){${errPushC(ctx, 'required', `${schemaPrefix}/required`, pathExpr, `{missingProperty:'${esc(key)}'}`, `"must have required property '${esc(key)}'"`, v, `{keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/required'${ordinalField(ctx, `${schemaPrefix}/required`)},params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'"}`)}}`)
|
|
6462
6574
|
}
|
|
@@ -6608,7 +6720,7 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
6608
6720
|
const pathVal = pathExpr ? literalValue(pathExpr) : ''
|
|
6609
6721
|
ctx.closureVars.push(errVar)
|
|
6610
6722
|
ctx.closureVals.push(sharedErr({keyword: 'required', instancePath: pathVal, schemaPath: unescapeSp(`${schemaPrefix}/dependentRequired`), params: Object.freeze({missingProperty: dep}), message: `must have required property '${dep}'`}, ctx.rootSchema))
|
|
6611
|
-
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${ownKeyExpr(ctx, v, key)}&&!${ownKeyExpr(ctx, v, dep)}){(_e
|
|
6723
|
+
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${ownKeyExpr(ctx, v, key)}&&!${ownKeyExpr(ctx, v, dep)}){(_e=_ap(_e,${errVar}))}`)
|
|
6612
6724
|
} else {
|
|
6613
6725
|
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${ownKeyExpr(ctx, v, key)}&&!${ownKeyExpr(ctx, v, dep)}){${errPushC(ctx, 'required', `${schemaPrefix}/dependentRequired`, pathExpr, `{missingProperty:'${esc(dep)}'}`, `"must have required property '${esc(dep)}'"`, v, `{keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/dependentRequired'${ordinalField(ctx, `${schemaPrefix}/dependentRequired`)},params:{missingProperty:'${esc(dep)}'},message:"must have required property '${esc(dep)}'"}`)}}`)
|
|
6614
6726
|
}
|
|
@@ -6905,7 +7017,7 @@ function emitAnnot(sub, ctx) {
|
|
|
6905
7017
|
ctx.annFns.set(sub, null)
|
|
6906
7018
|
const name = `_an${ctx.varCounter++}`
|
|
6907
7019
|
const local = {}
|
|
6908
|
-
for (const k of Object.keys(sub)) if (!ANNOT_APPLICATORS.has(k)) local
|
|
7020
|
+
for (const k of Object.keys(sub)) if (!ANNOT_APPLICATORS.has(k)) setOwn(local, k, sub[k])
|
|
6909
7021
|
const vl = []
|
|
6910
7022
|
nestedGenCode(local, '_av', vl, ctx)
|
|
6911
7023
|
const decls = [`function ${name}_v(_av){${vl.join('\n ')}\n return true}`]
|
|
@@ -7053,7 +7165,7 @@ function emitAnnotI(sub, ctx) {
|
|
|
7053
7165
|
annItemHelpers(ctx)
|
|
7054
7166
|
const name = `_ai${ctx.varCounter++}`
|
|
7055
7167
|
const local = {}
|
|
7056
|
-
for (const k of Object.keys(sub)) if (!ANNOT_ITEM_APPLICATORS.has(k)) local
|
|
7168
|
+
for (const k of Object.keys(sub)) if (!ANNOT_ITEM_APPLICATORS.has(k)) setOwn(local, k, sub[k])
|
|
7057
7169
|
const vl = []
|
|
7058
7170
|
nestedGenCode(local, '_av', vl, ctx)
|
|
7059
7171
|
const decls = [`function ${name}_v(_av){${vl.join('\n ')}\n return true}`]
|
|
@@ -7169,7 +7281,7 @@ function genAdditionalSchemaC(schema, v, pathExpr, lines, ctx, schemaPrefix, isO
|
|
|
7169
7281
|
function genPatternPropertiesC(schema, v, pathExpr, lines, ctx, schemaPrefix, fail, isObj) {
|
|
7170
7282
|
let handledPropertyNames = false
|
|
7171
7283
|
const ppEntries = Object.entries(schema.patternProperties)
|
|
7172
|
-
const pn = schema.propertyNames
|
|
7284
|
+
const pn = isSimplePN(schema.propertyNames) ? schema.propertyNames : null
|
|
7173
7285
|
const pi = ctx.varCounter++
|
|
7174
7286
|
|
|
7175
7287
|
// Build pattern matchers: prefer charCodeAt for simple prefixes
|
|
@@ -7290,7 +7402,16 @@ function genPropertyNamesFalseC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
7290
7402
|
function genPropertyNamesC(schema, v, pathExpr, lines, ctx, schemaPrefix, fail) {
|
|
7291
7403
|
const pn = schema.propertyNames
|
|
7292
7404
|
const ki = ctx.varCounter++
|
|
7405
|
+
// In the rich function an error carries the value it was raised on, and
|
|
7406
|
+
// here that would be the key where the interpreter's error points at the
|
|
7407
|
+
// object, so that one is left to the plain function and enrichment.
|
|
7408
|
+
if (!isSimplePN(pn) && ctx.rich) throw DECLINE
|
|
7293
7409
|
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){for(const _k${ki} in ${v}){`)
|
|
7410
|
+
if (!isSimplePN(pn)) {
|
|
7411
|
+
genCodeC(pn, `_k${ki}`, pathExpr, lines, ctx, schemaPrefix + '/propertyNames')
|
|
7412
|
+
lines.push(`}}`)
|
|
7413
|
+
return
|
|
7414
|
+
}
|
|
7294
7415
|
if (pn.minLength !== undefined) {
|
|
7295
7416
|
lines.push(`if(_k${ki}.length<${pn.minLength}){${fail('minLength', 'propertyNames/minLength', `{limit:${pn.minLength}}`, `'must NOT have fewer than ${pn.minLength} characters'`)}}`)
|
|
7296
7417
|
}
|
|
@@ -7508,6 +7629,6 @@ function _collectEval(schema, result, defs, schemaMap, refStack, isRoot) {
|
|
|
7508
7629
|
// not → contributes nothing (spec: annotations from not are discarded)
|
|
7509
7630
|
}
|
|
7510
7631
|
|
|
7511
|
-
module.exports = { compileToJS, compileToJSCodegen, compileToJSCodegenWithErrors, compileToJSCombined, collectEvaluated, unevalContributions, AJV_MESSAGES, _setCodeBudget }
|
|
7632
|
+
module.exports = { compileToJS, compileToJSCodegen, compileToJSCodegenWithErrors, compileToJSCombined, collectEvaluated, unevalContributions, expandedChars, AJV_MESSAGES, _setCodeBudget }
|
|
7512
7633
|
// For tests/test_custom_keyword_codegen.js, which holds these copies to the interpreter's.
|
|
7513
7634
|
Object.defineProperty(module.exports, '_kwTypeBits', { value: { kwTypeBit, kwDataBits, KW_T_ANY }, enumerable: false })
|