ata-validator 1.47.0 → 1.49.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.
@@ -9,7 +9,7 @@ const { compileFunction } = require('./compile-fn')
9
9
  // the first call that compiles errors. Everything here was written as part
10
10
  // of js-compiler.js and reads its helpers from that module's _internal.
11
11
  const core = require('./js-compiler')
12
- const { hasConstraints, assertEmitted, prepareForCodegen, sharedCodegenGate, codegenSafe, isSimplePN, hasUnresolvableRef, needsBaseTracking, _cpLen, sharedRt, DECLINE, bindRt, compileSafe, emitGuardState, withPlain, REF_NEUTRAL_SIBLINGS, resolveCrossSchemaRef, enumCondition, emitDeq, emitConstant, ownGuard, ownKeyExpr, multipleOfBad, compilePatternInline, useSafeEngine, reFlags, FORMAT_CODEGEN, PROTO_NAMES, setOwn, nestedGenCode, compileToJSCodegen, SKIP_VALUE_KEYS, skipString, CYCLE_DEPTH, cyclicDefNames, sharedDefNames, reFlagArg, hoistOnce, PTR_ESC_HELPER, unevalContributions, emitUq, uqSources, safeReClosure, fastPrefixCheck, replaceTopLevel, refTargetFor, genCode, addLocalAnnotations, addTopApplicatorAnnotations, protoIs, ANNOT_APPLICATORS, SIZE_SKIPS, emitRootGuard, emitGuardedRun } = core._internal
12
+ const { hasConstraints, assertEmitted, prepareForCodegen, sharedCodegenGate, codegenSafe, isSimplePN, hasUnresolvableRef, needsBaseTracking, _cpLen, sharedRt, DECLINE, bindRt, compileSafe, emitGuardState, withPlain, REF_NEUTRAL_SIBLINGS, resolveCrossSchemaRef, enumCondition, enumOperand, emitDeq, emitConstant, ownGuard, ownKeyExpr, multipleOfBad, compilePatternInline, useSafeEngine, reFlags, FORMAT_CODEGEN, PROTO_NAMES, setOwn, nestedGenCode, compileToJSCodegen, SKIP_VALUE_KEYS, skipString, CYCLE_DEPTH, cyclicDefNames, sharedDefNames, reFlagArg, hoistOnce, PTR_ESC_HELPER, unevalContributions, emitUq, uqSources, safeReClosure, fastPrefixCheck, replaceTopLevel, refTargetFor, genCode, addLocalAnnotations, addTopApplicatorAnnotations, protoIs, ANNOT_APPLICATORS, SIZE_SKIPS, emitRootGuard, emitGuardedRun } = core._internal
13
13
 
14
14
 
15
15
  function emitPtrEsc(ctx) {
@@ -289,7 +289,7 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
289
289
  if (hasUnresolvableRef(schema, eRootDefs, eAnchors, schemaMap, new Set())) return null
290
290
  if (needsBaseTracking(schema, schemaMap, new Set())) return null
291
291
 
292
- const ctx = { varCounter: 0, helperCode: [], rootDefs: eRootDefs, shared: [], refStack: new Set(), schemaMap: schemaMap || null, anchors: eAnchors, rootE: schema, rootSchema: inputSchema, userFormats: userFormats || null,
292
+ const ctx = { noOrdinal: !!(sourceOpts && sourceOpts.noOrdinal), varCounter: 0, helperCode: [], rootDefs: eRootDefs, shared: [], refStack: new Set(), schemaMap: schemaMap || null, anchors: eAnchors, rootE: schema, rootSchema: inputSchema, userFormats: userFormats || null,
293
293
  // Custom format checkers referenced by the body, bound as
294
294
  // closure parameters below the way the other entry points do.
295
295
  closureVars: [], closureVals: [],
@@ -1143,7 +1143,8 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
1143
1143
 
1144
1144
  // enum
1145
1145
  if (schema.enum) {
1146
- lines.push(`if(!(${enumCondition(ctx, schema.enum, v)})){${fail('enum', 'enum', `{allowedValues:${JSON.stringify(schema.enum)}}`, "'must be equal to one of the allowed values'")}}`)
1146
+ const ev = enumOperand(ctx, schema.enum, v, lines)
1147
+ lines.push(`if(!(${enumCondition(ctx, schema.enum, ev)})){${fail('enum', 'enum', `{allowedValues:${JSON.stringify(schema.enum)}}`, "'must be equal to one of the allowed values'")}}`)
1147
1148
  }
1148
1149
 
1149
1150
  // const — use canonical (sorted-key) comparison for objects
@@ -1351,9 +1352,15 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
1351
1352
  if (schema.properties) {
1352
1353
  for (const [key, prop] of Object.entries(schema.properties)) {
1353
1354
  const childPath = childPathExpr(pathExpr, ptrSeg(key))
1355
+ const sp = schemaPrefix+'/properties/'+ptrSeg(key)
1356
+ const from = lines.length
1354
1357
  lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&${ownKeyExpr(ctx, v, key)}){`)
1355
- genCodeE(prop, `${v}[${JSON.stringify(key)}]`, childPath, lines, ctx, schemaPrefix+'/properties/'+ptrSeg(key))
1358
+ // A large subschema as a function of its own (emitPropFnE); false means inline.
1359
+ if (!propFnCandidateE(prop, ctx, sp) || !emitPropFnE(prop, `${v}[${JSON.stringify(key)}]`, childPath, lines, ctx, sp)) {
1360
+ genCodeE(prop, `${v}[${JSON.stringify(key)}]`, childPath, lines, ctx, sp)
1361
+ }
1356
1362
  lines.push(`}`)
1363
+ countInlineE(lines, from, ctx)
1357
1364
  }
1358
1365
  }
1359
1366
 
@@ -2039,6 +2046,86 @@ function emitSharedDefC(defName, v, pathExpr, lines, ctx, schemaPrefix) {
2039
2046
  return true
2040
2047
  }
2041
2048
 
2049
+ // The same split for the error collector (genCodeE): a large property
2050
+ // subschema becomes `_pe<n>(d,_p,_all,_e)` in the helper code, pushing into
2051
+ // the caller's list as an inlined body would, generated against the real
2052
+ // schema path since it has one call site. Not inside a shared definition's
2053
+ // body, whose placeholder path is substituted after the fact.
2054
+ function propFnCandidateE (prop, ctx, schemaPrefix) {
2055
+ if (!ctx || ctx.nestedBoolean || ctx.condDepth) return false
2056
+ if (typeof schemaPrefix !== 'string' || schemaPrefix.includes(SP_PLACEHOLDER)) return false
2057
+ if (!prop || typeof prop !== 'object' || Array.isArray(prop)) return false
2058
+ let size = 0
2059
+ try { size = JSON.stringify(prop).length } catch { return false }
2060
+ return size >= SPLIT_C_SCHEMA || ((ctx.fnBytesE || 0) > SPLIT_C_BUDGET && size >= SPLIT_C_SMALL)
2061
+ }
2062
+ function emitPropFnE (prop, v, pathExpr, lines, ctx, schemaPrefix) {
2063
+ const outerBytes = ctx.fnBytesE
2064
+ ctx.fnBytesE = 0
2065
+ const bodyLines = []
2066
+ try { genCodeE(prop, 'd', '_p', bodyLines, ctx, schemaPrefix) } finally { ctx.fnBytesE = outerBytes }
2067
+ if (bodyLines.length === 0) return false
2068
+ const body = bodyLines.join('\n ')
2069
+ if (body.includes(SP_PLACEHOLDER)) return false
2070
+ const fnName = '_pe' + (ctx.propFnsE = (ctx.propFnsE || 0) + 1)
2071
+ ctx.helperCode.push(`function ${fnName}(d,_p,_all,_e){${body}}`)
2072
+ lines.push(`${fnName}(${v},${pathExpr || '""'},_all,_e);if(!_all&&_e.length)return{valid:false,errors:_e}`)
2073
+ return true
2074
+ }
2075
+ function countInlineE (lines, from, ctx) {
2076
+ let n = 0
2077
+ for (let i = from; i < lines.length; i++) n += lines[i].length
2078
+ ctx.fnBytesE = (ctx.fnBytesE || 0) + n
2079
+ }
2080
+
2081
+ // A large property subschema as a function of its own, for the reason the
2082
+ // verdict generator has (emitPropFn in js-compiler.js): one function per
2083
+ // schema outgrew what V8 optimizes, and a 167 KB configuration schema's
2084
+ // one-pass function was 3.8 MB of source that ran in the interpreter. The
2085
+ // function is used at one site, so it is generated against the real schema
2086
+ // path and its errors keep their precise ordinal keys; it is not written
2087
+ // inside a shared definition's body, whose placeholder path is substituted
2088
+ // after the fact and would not reach a helper of its own.
2089
+ const SPLIT_C_SCHEMA = 1500
2090
+ const SPLIT_C_SMALL = 160
2091
+ const SPLIT_C_BUDGET = 16 * 1024
2092
+ function propFnCandidateC (prop, ctx, schemaPrefix) {
2093
+ if (!ctx || ctx.inDefC || ctx.nestedBoolean || ctx.condDepth) return false
2094
+ if (typeof schemaPrefix !== 'string' || schemaPrefix.includes(SP_PLACEHOLDER)) return false
2095
+ if (!prop || typeof prop !== 'object' || Array.isArray(prop)) return false
2096
+ let size = 0
2097
+ try { size = JSON.stringify(prop).length } catch { return false }
2098
+ return size >= SPLIT_C_SCHEMA || ((ctx.fnBytesC || 0) > SPLIT_C_BUDGET && size >= SPLIT_C_SMALL)
2099
+ }
2100
+ function emitPropFnC (prop, v, pathExpr, lines, ctx, schemaPrefix) {
2101
+ const outerHoisted = ctx.hoisted
2102
+ ctx.hoisted = []
2103
+ const outerBytes = ctx.fnBytesC
2104
+ ctx.fnBytesC = 0
2105
+ const bodyLines = []
2106
+ let hoisted
2107
+ try {
2108
+ genGuardedC(prop, '_dv', '_p', bodyLines, ctx, schemaPrefix)
2109
+ } finally {
2110
+ hoisted = ctx.hoisted
2111
+ ctx.hoisted = outerHoisted
2112
+ ctx.fnBytesC = outerBytes
2113
+ }
2114
+ if (bodyLines.length === 0) return false
2115
+ const body = bodyLines.join('\n ')
2116
+ if (body.includes(SP_PLACEHOLDER) || /\b_so\b/.test(body)) return false
2117
+ const decl = hoisted.length ? `let ${hoisted.map((n) => n + '=null').join(',')};` : ''
2118
+ const fnName = '_pc' + (ctx.propFnsC = (ctx.propFnsC || 0) + 1)
2119
+ ctx.helperCode.push(`function ${fnName}(_dv,_p,d){const _all=true;${decl}let _e;\n ${body}\n return _e}`)
2120
+ lines.push(`{const _r=${fnName}(${v},${pathExpr || '""'},d);if(_r!==undefined){if(_e===undefined)_e=_r;else for(let _i=0;_i<_r.length;_i++)_e.push(_r[_i])}}`)
2121
+ return true
2122
+ }
2123
+ function countInlineC (lines, from, ctx) {
2124
+ let n = 0
2125
+ for (let i = from; i < lines.length; i++) n += lines[i].length
2126
+ ctx.fnBytesC = (ctx.fnBytesC || 0) + n
2127
+ }
2128
+
2042
2129
  // One schema as a function of its own in the combined function (see
2043
2130
  // emitSharedDefC). The name is registered before the body is written, so a
2044
2131
  // reference back to it inside becomes a call. One that can re-enter itself,
@@ -2244,7 +2331,8 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
2244
2331
  const emitEnumConst = () => {
2245
2332
  // enum
2246
2333
  if (schema.enum) {
2247
- lines.push(`if(!(${enumCondition(ctx, schema.enum, v)})){${fail('enum', 'enum', `{allowedValues:${JSON.stringify(schema.enum)}}`, "'must be equal to one of the allowed values'")}}`)
2334
+ const ev = enumOperand(ctx, schema.enum, v, lines)
2335
+ lines.push(`if(!(${enumCondition(ctx, schema.enum, ev)})){${fail('enum', 'enum', `{allowedValues:${JSON.stringify(schema.enum)}}`, "'must be equal to one of the allowed values'")}}`)
2248
2336
  }
2249
2337
 
2250
2338
  // const
@@ -2507,21 +2595,28 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
2507
2595
  for (const [key, prop] of Object.entries(schema.properties)) {
2508
2596
  const pv = hoisted[key] || `${v}[${JSON.stringify(key)}]`
2509
2597
  const childPath = childPathExpr(pathExpr, ptrSeg(key))
2598
+ const sp = schemaPrefix+'/properties/'+ptrSeg(key)
2599
+ const from = lines.length
2600
+ const split = propFnCandidateC(prop, ctx, sp)
2601
+ // A large subschema as a function of its own (emitPropFnC), called
2602
+ // where the inline code would be; false means it is written inline.
2603
+ const sub = (pvExpr) => { if (!split || !emitPropFnC(prop, pvExpr, childPath, lines, ctx, sp)) genGuardedC(prop, pvExpr, childPath, lines, ctx, sp) }
2510
2604
  if (requiredSet.has(key) && isObj) {
2511
2605
  // A hoisted local was already cleared if the value is only inherited.
2512
2606
  lines.push(`if(${pv}!==undefined${hoisted[key] ? '' : '&&' + ownGuard(ctx, v, key)}){`)
2513
- genGuardedC(prop, pv, childPath, lines, ctx, schemaPrefix+'/properties/'+ptrSeg(key))
2607
+ sub(pv)
2514
2608
  lines.push(`}`)
2515
2609
  } else if (isObj) {
2516
2610
  const oi = ctx.varCounter++
2517
2611
  lines.push(`{const _o${oi}=${v}[${JSON.stringify(key)}];if(_o${oi}!==undefined&&${ownGuard(ctx, v, key)}){`)
2518
- genGuardedC(prop, `_o${oi}`, childPath, lines, ctx, schemaPrefix+'/properties/'+ptrSeg(key))
2612
+ sub(`_o${oi}`)
2519
2613
  lines.push(`}}`)
2520
2614
  } else {
2521
2615
  lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&${ownKeyExpr(ctx, v, key)}){`)
2522
- genGuardedC(prop, `${v}[${JSON.stringify(key)}]`, childPath, lines, ctx, schemaPrefix+'/properties/'+ptrSeg(key))
2616
+ sub(`${v}[${JSON.stringify(key)}]`)
2523
2617
  lines.push(`}`)
2524
2618
  }
2619
+ countInlineC(lines, from, ctx)
2525
2620
  }
2526
2621
  }
2527
2622
 
@@ -135,6 +135,21 @@ function emitConstant(ctx, value) {
135
135
  return name
136
136
  }
137
137
 
138
+ // The operand an `enum` test compares: `v` itself when it is a plain
139
+ // identifier, otherwise a local bound once. The test is a chain of `===`,
140
+ // one per member, and each used to repeat the access expression, so a
141
+ // 32-member enum on a required property read `_e3["tool"]` 32 times; on an
142
+ // array of items in several shapes those reads are polymorphic and cost
143
+ // 59 ns against 20 with the value in a local. The binding goes to `lines`
144
+ // ahead of the test.
145
+ const IDENT_RE = /^[A-Za-z_$][\w$]*$/
146
+ function enumOperand(ctx, vals, v, lines) {
147
+ if (IDENT_RE.test(v) || vals.length < 2) return v
148
+ const name = `_ev${ctx.varCounter++}`
149
+ lines.push(`const ${name}=${v}`)
150
+ return name
151
+ }
152
+
138
153
  // One `enum` test: primitives compare with `===`, which needs no call, and
139
154
  // object members go through structural equality.
140
155
  function enumCondition(ctx, vals, v) {
@@ -1161,6 +1176,60 @@ function _setCodeBudget (n) { codeBudget = n === undefined ? CODE_BUDGET : n }
1161
1176
  // document that reached it, and the second cut the cycle, so the reference
1162
1177
  // checked nothing. While `nestedBoolean` is set, genCode declines at those
1163
1178
  // points instead, and the caller's generator declines the schema.
1179
+ // A property whose subschema is large becomes a function of its own in the
1180
+ // preamble, called where the property is checked, instead of its code inline.
1181
+ // V8 does not optimize a function past its bytecode budget (about 60 KB), and
1182
+ // a configuration schema of a few hundred properties compiled to one function
1183
+ // of a megabyte or more that ran in the interpreter for its whole life: on a
1184
+ // 167 KB schema the verdict took 407 us where the same checks in functions of
1185
+ // their own take 40. The threshold is on the schema's own size, which is
1186
+ // known before any code is written, so nothing is generated twice. A
1187
+ // property moves out on its own only when its schema is plainly large; the
1188
+ // usual case is the budget: once a function holds SPLIT_FN_BUDGET bytes of
1189
+ // inline code, every property past SPLIT_PROP_SMALL moves out, so a schema
1190
+ // of many middling properties splits after its first functions' worth and a
1191
+ // small or medium one, whose whole program fits the budget, never does and
1192
+ // pays no call.
1193
+ const SPLIT_PROP_SCHEMA = 2000
1194
+ const SPLIT_PROP_SMALL = 220
1195
+ const SPLIT_FN_BUDGET = 24 * 1024
1196
+ function propSchemaSize (prop) {
1197
+ try { return JSON.stringify(prop).length } catch { return 0 }
1198
+ }
1199
+ function propFnCandidate (prop, ctx) {
1200
+ if (!ctx.preamble || ctx.nestedBoolean || ctx.condDepth) return false
1201
+ if (!prop || typeof prop !== 'object' || Array.isArray(prop)) return false
1202
+ const size = propSchemaSize(prop)
1203
+ return size >= SPLIT_PROP_SCHEMA || ((ctx.fnBytes || 0) > SPLIT_FN_BUDGET && size >= SPLIT_PROP_SMALL)
1204
+ }
1205
+ // Generated over its own `d`, the way a shared definition is (see the $ref
1206
+ // case in genCodeNode): the deferred checks belong to this body, and the
1207
+ // plain-object reuse state must not leak in or out. Null means "write it
1208
+ // inline after all": an empty body (the inline path then asserts it the
1209
+ // usual way) or one that calls the recursive entry, which is out of scope
1210
+ // in the preamble.
1211
+ function emitPropFn (prop, ctx) {
1212
+ const bodyLines = []
1213
+ const outerDeferred = ctx.deferredChecks
1214
+ ctx.deferredChecks = null
1215
+ const outerReuse = ctx.plainReuse
1216
+ ctx.plainReuse = undefined
1217
+ const outerBytes = ctx.fnBytes
1218
+ ctx.fnBytes = 0
1219
+ try { genCode(prop, 'd', bodyLines, ctx) } finally { ctx.plainReuse = outerReuse; ctx.fnBytes = outerBytes }
1220
+ if (ctx.deferredChecks) for (const dc of ctx.deferredChecks) bodyLines.push(dc)
1221
+ ctx.deferredChecks = outerDeferred
1222
+ if (bodyLines.length === 0 || bodyLines.some((l) => /\b_validate\b/.test(l))) return null
1223
+ const fnName = '_pf' + (ctx.propFns = (ctx.propFns || 0) + 1)
1224
+ hoist(ctx, `function ${fnName}(d){${bodyLines.join('\n ')}\n return true}`)
1225
+ return fnName
1226
+ }
1227
+ function countInline (lines, from, ctx) {
1228
+ let n = 0
1229
+ for (let i = from; i < lines.length; i++) n += lines[i].length
1230
+ ctx.fnBytes = (ctx.fnBytes || 0) + n
1231
+ }
1232
+
1164
1233
  function nestedGenCode(schema, v, lines, ctx) {
1165
1234
  ctx.nestedBoolean = (ctx.nestedBoolean || 0) + 1
1166
1235
  const before = lines.length
@@ -1262,6 +1331,14 @@ function sharedDefNames (root, defs) {
1262
1331
  }
1263
1332
  return out
1264
1333
  }
1334
+ // Raising this to inline small definitions at every site was measured and
1335
+ // rejected: SchemaStore's detekt applies one 200-character rule definition
1336
+ // at 293 sites, and inlining it took the verdict from 5287 ns to 452 but the
1337
+ // standalone module from 168 KB to 1692 KB and its compile to 33 ms. The
1338
+ // slowness of the shared function is megamorphic property reads (474 objects
1339
+ // of 72 shapes through one function); a for-in body for the shared function
1340
+ // was tried too and gave 1.5x, not enough to carry its complexity. Left as a
1341
+ // known trade: on that shape the shared function costs 27 ns a call.
1265
1342
  const SHARED_DEF_MIN_CHARS = 120
1266
1343
 
1267
1344
  function cyclicDefNames (defs) {
@@ -1932,8 +2009,32 @@ function sharedCodegenGate(schema, schemaMap) {
1932
2009
  _gateSchema = schema; _gateMap = schemaMap; _gateOk = ok
1933
2010
  return ok
1934
2011
  }
2012
+ // `unevaluatedProperties` or `unevaluatedItems` beside a `$ref` back to the
2013
+ // document root: the generated evaluated-set tracking does not credit what
2014
+ // the root evaluates through that reference, so a tree whose branches say
2015
+ // `{ unevaluatedProperties: false, $ref: '#' }` rejected valid branches (a
2016
+ // wrong reject, found through the 2019-09 `$recursiveRef` tests, and the same
2017
+ // in 2020-12). The interpreted engine answers it. A text check first: almost
2018
+ // no schema has both.
2019
+ function unevaluatedBesideRootRef(schema) {
2020
+ const text = JSON.stringify(schema)
2021
+ if (!text.includes('"$ref":"#"') || !/"unevaluated(?:Properties|Items)"/.test(text)) return false
2022
+ let found = false
2023
+ const seen = new Set()
2024
+ const walk = (n) => {
2025
+ if (found || n === null || typeof n !== 'object' || seen.has(n)) return
2026
+ seen.add(n)
2027
+ if (Array.isArray(n)) { for (const x of n) walk(x); return }
2028
+ if (n.$ref === '#' && (n.unevaluatedProperties !== undefined || n.unevaluatedItems !== undefined)) { found = true; return }
2029
+ for (const [k, v] of Object.entries(n)) if (k !== 'enum' && k !== 'const' && k !== 'default' && k !== 'examples') walk(v)
2030
+ }
2031
+ walk(schema)
2032
+ return found
2033
+ }
2034
+
1935
2035
  function sharedCodegenGateUncached(schema, schemaMap) {
1936
2036
  if (bothDefsContainers(schema)) return false
2037
+ if (unevaluatedBesideRootRef(schema)) return false
1937
2038
  if (hasSameInstanceRefCycle(schema)) return false
1938
2039
  if (!codegenSafe(schema, schemaMap)) return false
1939
2040
  if (needsBaseTracking(schema, schemaMap, new Set())) return false
@@ -3243,7 +3344,8 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
3243
3344
 
3244
3345
  // enum
3245
3346
  if (schema.enum) {
3246
- lines.push(`if(!(${enumCondition(ctx, schema.enum, v)}))return false`)
3347
+ const ev = enumOperand(ctx, schema.enum, v, lines)
3348
+ lines.push(`if(!(${enumCondition(ctx, schema.enum, ev)}))return false`)
3247
3349
  }
3248
3350
 
3249
3351
  // const
@@ -3498,11 +3600,17 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
3498
3600
  // properties — use hoisted vars for required props, hoist optional to locals too
3499
3601
  if (schema.properties) {
3500
3602
  for (const [key, prop] of Object.entries(schema.properties)) {
3603
+ const from = lines.length
3604
+ // A large subschema as a function of its own (emitPropFn); the call
3605
+ // takes the place of the inline code in each of the three shapes below.
3606
+ const fn = propFnCandidate(prop, ctx) ? emitPropFn(prop, ctx) : null
3501
3607
  if (requiredSet.has(key) && isObj) {
3502
3608
  // Required + type:object — hoist to local to reduce repeated property lookups
3503
3609
  const access = hoisted[key] || `${v}[${JSON.stringify(key)}]`
3504
- const combined = tryGenCombined(prop, access, ctx)
3505
- if (combined) {
3610
+ const combined = fn ? null : tryGenCombined(prop, access, ctx)
3611
+ if (fn) {
3612
+ lines.push(`if(!${fn}(${access}))return false`)
3613
+ } else if (combined) {
3506
3614
  lines.push(combined)
3507
3615
  } else if (needsLocal(prop) || isPlainContainer(prop)) {
3508
3616
  const oi = ctx.varCounter++
@@ -3518,8 +3626,10 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
3518
3626
  const oi = ctx.varCounter++
3519
3627
  const local = `_o${oi}`
3520
3628
  lines.push(`{const ${local}=${v}[${JSON.stringify(key)}];if(${local}!==undefined&&${ownGuard(ctx, v, key)}){`)
3521
- const combined = tryGenCombined(prop, local, ctx)
3522
- if (combined) {
3629
+ const combined = fn ? null : tryGenCombined(prop, local, ctx)
3630
+ if (fn) {
3631
+ lines.push(`if(!${fn}(${local}))return false`)
3632
+ } else if (combined) {
3523
3633
  lines.push(combined)
3524
3634
  } else {
3525
3635
  genCode(prop, local, lines, ctx)
@@ -3527,9 +3637,11 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
3527
3637
  lines.push(`}}`)
3528
3638
  } else {
3529
3639
  lines.push(`if(typeof ${v}==='object'&&${v}!==null&&${ownKeyExpr(ctx, v, key)}){`)
3530
- genCode(prop, `${v}[${JSON.stringify(key)}]`, lines, ctx)
3640
+ if (fn) lines.push(`if(!${fn}(${v}[${JSON.stringify(key)}]))return false`)
3641
+ else genCode(prop, `${v}[${JSON.stringify(key)}]`, lines, ctx)
3531
3642
  lines.push(`}`)
3532
3643
  }
3644
+ countInline(lines, from, ctx)
3533
3645
  }
3534
3646
  }
3535
3647
 
@@ -5307,4 +5419,4 @@ Object.defineProperty(module.exports, '_expandDynamicScopes', { value: expandDyn
5307
5419
  // For tests/test_custom_keyword_codegen.js, which holds these copies to the interpreter's.
5308
5420
  Object.defineProperty(module.exports, '_kwTypeBits', { value: { kwTypeBit, kwDataBits, KW_T_ANY }, enumerable: false })
5309
5421
 
5310
- module.exports._internal = { hasConstraints, assertEmitted, prepareForCodegen, sharedCodegenGate, codegenSafe, isSimplePN, hasUnresolvableRef, needsBaseTracking, _cpLen, sharedRt, DECLINE, bindRt, compileSafe, emitGuardState, withPlain, REF_NEUTRAL_SIBLINGS, resolveCrossSchemaRef, enumCondition, emitDeq, emitConstant, ownGuard, ownKeyExpr, multipleOfBad, compilePatternInline, useSafeEngine, reFlags, FORMAT_CODEGEN, PROTO_NAMES, setOwn, nestedGenCode, compileToJSCodegen, SKIP_VALUE_KEYS, skipString, CYCLE_DEPTH, cyclicDefNames, sharedDefNames, reFlagArg, hoistOnce, PTR_ESC_HELPER, unevalContributions, emitUq, uqSources, safeReClosure, fastPrefixCheck, replaceTopLevel, refTargetFor, genCode, addLocalAnnotations, addTopApplicatorAnnotations, protoIs, ANNOT_APPLICATORS, SIZE_SKIPS, emitRootGuard, emitGuardedRun }
5422
+ module.exports._internal = { hasConstraints, assertEmitted, prepareForCodegen, sharedCodegenGate, codegenSafe, isSimplePN, hasUnresolvableRef, needsBaseTracking, _cpLen, sharedRt, DECLINE, bindRt, compileSafe, emitGuardState, withPlain, REF_NEUTRAL_SIBLINGS, resolveCrossSchemaRef, enumCondition, enumOperand, emitDeq, emitConstant, ownGuard, ownKeyExpr, multipleOfBad, compilePatternInline, useSafeEngine, reFlags, FORMAT_CODEGEN, PROTO_NAMES, setOwn, nestedGenCode, compileToJSCodegen, SKIP_VALUE_KEYS, skipString, CYCLE_DEPTH, cyclicDefNames, sharedDefNames, reFlagArg, hoistOnce, PTR_ESC_HELPER, unevalContributions, emitUq, uqSources, safeReClosure, fastPrefixCheck, replaceTopLevel, refTargetFor, genCode, addLocalAnnotations, addTopApplicatorAnnotations, protoIs, ANNOT_APPLICATORS, SIZE_SKIPS, emitRootGuard, emitGuardedRun }
@@ -2,8 +2,14 @@
2
2
 
3
3
  // The official meta-schemas, vendored so a `$ref` to one of them resolves
4
4
  // without a network. Draft 2020-12 is eight documents joined by $dynamicRef;
5
- // draft-07 is one. Sources: https://json-schema.org/draft/2020-12/schema and
6
- // http://json-schema.org/draft-07/schema, fetched 2026-08-23, unmodified.
5
+ // draft-07 and draft-06 are one each; 2019-09 is seven. Sources:
6
+ // https://json-schema.org/draft/2020-12/schema and
7
+ // http://json-schema.org/draft-07/schema, fetched 2026-08-23, and
8
+ // http://json-schema.org/draft-06/schema and
9
+ // https://json-schema.org/draft/2019-09/schema, fetched 2026-10-10, unmodified.
10
+ // The 2019-09 documents are read through the same 2019-09 rewrite as a
11
+ // caller's schema (lib/draft7.js) once, at load, since they use
12
+ // `$recursiveRef`, which the engines read as `$dynamicRef`.
7
13
  // Keyed by $id; index.js also registers the http/https and trailing-# spellings.
8
14
 
9
15
  const METASCHEMAS = new Map([
@@ -16,6 +22,19 @@ const METASCHEMAS = new Map([
16
22
  ["https://json-schema.org/draft/2020-12/meta/content", {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://json-schema.org/draft/2020-12/meta/content","$dynamicAnchor":"meta","title":"Content vocabulary meta-schema","type":["object","boolean"],"properties":{"contentEncoding":{"type":"string"},"contentMediaType":{"type":"string"},"contentSchema":{"$dynamicRef":"#meta"}}}],
17
23
  ["https://json-schema.org/draft/2020-12/meta/unevaluated", {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://json-schema.org/draft/2020-12/meta/unevaluated","$dynamicAnchor":"meta","title":"Unevaluated applicator vocabulary meta-schema","type":["object","boolean"],"properties":{"unevaluatedItems":{"$dynamicRef":"#meta"},"unevaluatedProperties":{"$dynamicRef":"#meta"}}}],
18
24
  ["http://json-schema.org/draft-07/schema#", {"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"writeOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}],
25
+ ["http://json-schema.org/draft-06/schema#", {"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}],
26
+ ["https://json-schema.org/draft/2019-09/schema", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/schema","$vocabulary":{"https://json-schema.org/draft/2019-09/vocab/core":true,"https://json-schema.org/draft/2019-09/vocab/applicator":true,"https://json-schema.org/draft/2019-09/vocab/validation":true,"https://json-schema.org/draft/2019-09/vocab/meta-data":true,"https://json-schema.org/draft/2019-09/vocab/format":false,"https://json-schema.org/draft/2019-09/vocab/content":true},"$recursiveAnchor":true,"title":"Core and Validation specifications meta-schema","allOf":[{"$ref":"meta/core"},{"$ref":"meta/applicator"},{"$ref":"meta/validation"},{"$ref":"meta/meta-data"},{"$ref":"meta/format"},{"$ref":"meta/content"}],"type":["object","boolean"],"properties":{"definitions":{"$comment":"While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.","type":"object","additionalProperties":{"$recursiveRef":"#"},"default":{}},"dependencies":{"$comment":"\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"","type":"object","additionalProperties":{"anyOf":[{"$recursiveRef":"#"},{"$ref":"meta/validation#/$defs/stringArray"}]}}}}],
27
+ ["https://json-schema.org/draft/2019-09/meta/core", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/meta/core","$recursiveAnchor":true,"title":"Core vocabulary meta-schema","type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference","$comment":"Non-empty fragments not allowed.","pattern":"^[^#]*#?$"},"$schema":{"type":"string","format":"uri"},"$anchor":{"type":"string","pattern":"^[A-Za-z][-A-Za-z0-9.:_]*$"},"$ref":{"type":"string","format":"uri-reference"},"$recursiveRef":{"type":"string","format":"uri-reference"},"$recursiveAnchor":{"type":"boolean","default":false},"$vocabulary":{"type":"object","propertyNames":{"type":"string","format":"uri"},"additionalProperties":{"type":"boolean"}},"$comment":{"type":"string"},"$defs":{"type":"object","additionalProperties":{"$recursiveRef":"#"},"default":{}}}}],
28
+ ["https://json-schema.org/draft/2019-09/meta/applicator", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/meta/applicator","$recursiveAnchor":true,"title":"Applicator vocabulary meta-schema","type":["object","boolean"],"properties":{"additionalItems":{"$recursiveRef":"#"},"unevaluatedItems":{"$recursiveRef":"#"},"items":{"anyOf":[{"$recursiveRef":"#"},{"$ref":"#/$defs/schemaArray"}]},"contains":{"$recursiveRef":"#"},"additionalProperties":{"$recursiveRef":"#"},"unevaluatedProperties":{"$recursiveRef":"#"},"properties":{"type":"object","additionalProperties":{"$recursiveRef":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$recursiveRef":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependentSchemas":{"type":"object","additionalProperties":{"$recursiveRef":"#"}},"propertyNames":{"$recursiveRef":"#"},"if":{"$recursiveRef":"#"},"then":{"$recursiveRef":"#"},"else":{"$recursiveRef":"#"},"allOf":{"$ref":"#/$defs/schemaArray"},"anyOf":{"$ref":"#/$defs/schemaArray"},"oneOf":{"$ref":"#/$defs/schemaArray"},"not":{"$recursiveRef":"#"}},"$defs":{"schemaArray":{"type":"array","minItems":1,"items":{"$recursiveRef":"#"}}}}],
29
+ ["https://json-schema.org/draft/2019-09/meta/validation", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/meta/validation","$recursiveAnchor":true,"title":"Validation vocabulary meta-schema","type":["object","boolean"],"properties":{"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/$defs/nonNegativeInteger"},"minLength":{"$ref":"#/$defs/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"maxItems":{"$ref":"#/$defs/nonNegativeInteger"},"minItems":{"$ref":"#/$defs/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"maxContains":{"$ref":"#/$defs/nonNegativeInteger"},"minContains":{"$ref":"#/$defs/nonNegativeInteger","default":1},"maxProperties":{"$ref":"#/$defs/nonNegativeInteger"},"minProperties":{"$ref":"#/$defs/nonNegativeIntegerDefault0"},"required":{"$ref":"#/$defs/stringArray"},"dependentRequired":{"type":"object","additionalProperties":{"$ref":"#/$defs/stringArray"}},"const":true,"enum":{"type":"array","items":true},"type":{"anyOf":[{"$ref":"#/$defs/simpleTypes"},{"type":"array","items":{"$ref":"#/$defs/simpleTypes"},"minItems":1,"uniqueItems":true}]}},"$defs":{"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"$ref":"#/$defs/nonNegativeInteger","default":0},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}}}],
30
+ ["https://json-schema.org/draft/2019-09/meta/meta-data", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/meta/meta-data","$recursiveAnchor":true,"title":"Meta-data vocabulary meta-schema","type":["object","boolean"],"properties":{"title":{"type":"string"},"description":{"type":"string"},"default":true,"deprecated":{"type":"boolean","default":false},"readOnly":{"type":"boolean","default":false},"writeOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true}}}],
31
+ ["https://json-schema.org/draft/2019-09/meta/format", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/meta/format","$recursiveAnchor":true,"title":"Format vocabulary meta-schema","type":["object","boolean"],"properties":{"format":{"type":"string"}}}],
32
+ ["https://json-schema.org/draft/2019-09/meta/content", {"$schema":"https://json-schema.org/draft/2019-09/schema","$id":"https://json-schema.org/draft/2019-09/meta/content","$recursiveAnchor":true,"title":"Content vocabulary meta-schema","type":["object","boolean"],"properties":{"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"contentSchema":{"$recursiveRef":"#"}}}],
19
33
  ]);
20
34
 
35
+ {
36
+ const { isDraft2019, normalizeDraft2019 } = require('./draft7')
37
+ for (const [id, doc] of METASCHEMAS) if (isDraft2019(doc)) METASCHEMAS.set(id, normalizeDraft2019(JSON.parse(JSON.stringify(doc)), true))
38
+ }
39
+
21
40
  module.exports = { METASCHEMAS };