ata-validator 1.61.2 → 1.62.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 +159 -37
- package/bin/ata.js +35 -2
- package/compat.js +15 -2
- package/compiled-standard.d.ts +6 -0
- package/compiled-standard.mjs +66 -0
- package/index.d.ts +7 -0
- package/index.js +10 -0
- package/lib/aot-build.js +27 -1
- package/lib/aot-impl.js +108 -17
- package/lib/buffer-gate.js +25 -14
- package/lib/bun-target.js +21 -5
- package/lib/clone-emit.js +115 -10
- package/lib/codegen-engine.js +94 -18
- package/lib/compiled-common.js +11 -4
- package/lib/compiled-standard.js +33 -0
- package/lib/compiled-verdict.js +2 -2
- package/lib/compiled.js +1 -1
- package/lib/data-positions.js +18 -0
- package/lib/defaults.js +101 -23
- package/lib/describe-schema.js +38 -5
- package/lib/diagnose.js +51 -29
- package/lib/enrich-error.js +23 -1
- package/lib/error-sort-rank.js +12 -0
- package/lib/formats-source.js +12 -1
- package/lib/formats.js +19 -19
- package/lib/interpreter-core.js +23 -4
- package/lib/js-compiler-errors.js +70 -72
- package/lib/js-compiler.js +212 -72
- package/lib/json-rejections.js +1 -1
- package/lib/migrate.js +24 -5
- package/lib/native-attach.browser.js +7 -0
- package/lib/native-attach.js +76 -0
- package/lib/native-positions.js +7 -0
- package/lib/plan-compiler.js +7 -1
- package/lib/regex-linear.js +88 -0
- package/lib/rejections.js +13 -3
- package/lib/render-compact.js +5 -5
- package/lib/render-pretty.js +23 -10
- package/lib/safe-regex.js +29 -4
- package/lib/scan-compiler.js +16 -4
- package/lib/schema-diff.js +103 -0
- package/lib/stream.js +293 -0
- package/lib/tier0.js +6 -2
- package/lib/ts-gen.js +25 -12
- package/lib/unique-items.js +11 -3
- package/lib/validator-core.js +61 -66
- package/lib/verify.js +25 -6
- package/lib/version.js +1 -1
- package/package.json +24 -8
- package/stream.d.ts +51 -0
- package/stream.js +3 -0
- package/stream.mjs +3 -0
|
@@ -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 { ownHead, plainFlag, 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, keyLoopEligible, SAME_KEYS_HELPER, seqTag } = core._internal
|
|
12
|
+
const { ownHead, plainFlag, 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, keyLoopEligible, SAME_KEYS_HELPER, seqTag, numLit, userFormatFn, bindUserFormat, formatNameOf } = core._internal
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
function emitPtrEsc(ctx) {
|
|
@@ -105,9 +105,15 @@ function buildErrorLiteral (opts) {
|
|
|
105
105
|
if (hit) {
|
|
106
106
|
// Support both {line,col,text} (raw output of buildPositionMap) and
|
|
107
107
|
// [line,col,text] (compact form passed by callers that pre-flattened).
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
// The row comes from the caller's `sourceMap` option and is written
|
|
109
|
+
// into source, so a line or column that is not an integer is written
|
|
110
|
+
// as 0 and the text only as a JSON string: a string line ran as code.
|
|
111
|
+
const rawLine = Array.isArray(hit) ? hit[0] : hit.line
|
|
112
|
+
const rawCol = Array.isArray(hit) ? hit[1] : hit.col
|
|
113
|
+
const rawText = Array.isArray(hit) ? hit[2] : hit.text
|
|
114
|
+
const line = Number.isInteger(rawLine) ? rawLine : 0
|
|
115
|
+
const col = Number.isInteger(rawCol) ? rawCol : 0
|
|
116
|
+
const text = typeof rawText === 'string' ? rawText : ''
|
|
111
117
|
if (sourceMap.frames && text.length > FRAME_INLINE_MAX) {
|
|
112
118
|
// A long source line is written once at module scope (see
|
|
113
119
|
// sourceFrameDecls in aot-impl.js) and the literal names it. Inlined,
|
|
@@ -419,13 +425,7 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
|
|
|
419
425
|
fn._sharedHelpers = ctx.shared ? ctx.shared.slice() : []
|
|
420
426
|
fn._usesSafeRe = !!ctx.usesSafeRe
|
|
421
427
|
if (cvars.length) {
|
|
422
|
-
fn._formatClosures = cvars.map((name, i) => {
|
|
423
|
-
let format = null
|
|
424
|
-
for (const key of Object.keys(ctx.userFormats)) {
|
|
425
|
-
if (ctx.userFormats[key] === cvals[i]) { format = key; break }
|
|
426
|
-
}
|
|
427
|
-
return { name, fn: cvals[i], format }
|
|
428
|
-
})
|
|
428
|
+
fn._formatClosures = cvars.map((name, i) => ({ name, fn: cvals[i], format: formatNameOf(ctx, name) }))
|
|
429
429
|
}
|
|
430
430
|
return fn
|
|
431
431
|
} catch (e) {
|
|
@@ -1183,26 +1183,26 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1183
1183
|
|
|
1184
1184
|
// numeric
|
|
1185
1185
|
if (schema.minimum !== undefined) {
|
|
1186
|
-
const c = isNum ? `${v}<${schema.minimum}` : `typeof ${v}==='number'&&${v}<${schema.minimum}`
|
|
1187
|
-
lines.push(`if(${c}){${fail('minimum', 'minimum', `{comparison:'>=',limit:${schema.minimum}}`, `'must be >= ${schema.minimum}'`)}}`)
|
|
1186
|
+
const c = isNum ? `${v}<${numLit(schema.minimum)}` : `typeof ${v}==='number'&&${v}<${numLit(schema.minimum)}`
|
|
1187
|
+
lines.push(`if(${c}){${fail('minimum', 'minimum', `{comparison:'>=',limit:${numLit(schema.minimum)}}`, `'must be >= ${numLit(schema.minimum)}'`)}}`)
|
|
1188
1188
|
}
|
|
1189
1189
|
if (schema.maximum !== undefined) {
|
|
1190
|
-
const c = isNum ? `${v}>${schema.maximum}` : `typeof ${v}==='number'&&${v}>${schema.maximum}`
|
|
1191
|
-
lines.push(`if(${c}){${fail('maximum', 'maximum', `{comparison:'<=',limit:${schema.maximum}}`, `'must be <= ${schema.maximum}'`)}}`)
|
|
1190
|
+
const c = isNum ? `${v}>${numLit(schema.maximum)}` : `typeof ${v}==='number'&&${v}>${numLit(schema.maximum)}`
|
|
1191
|
+
lines.push(`if(${c}){${fail('maximum', 'maximum', `{comparison:'<=',limit:${numLit(schema.maximum)}}`, `'must be <= ${numLit(schema.maximum)}'`)}}`)
|
|
1192
1192
|
}
|
|
1193
1193
|
if (schema.exclusiveMinimum !== undefined) {
|
|
1194
|
-
const c = isNum ? `${v}<=${schema.exclusiveMinimum}` : `typeof ${v}==='number'&&${v}<=${schema.exclusiveMinimum}`
|
|
1195
|
-
lines.push(`if(${c}){${fail('exclusiveMinimum', 'exclusiveMinimum', `{comparison:'>',limit:${schema.exclusiveMinimum}}`, `'must be > ${schema.exclusiveMinimum}'`)}}`)
|
|
1194
|
+
const c = isNum ? `${v}<=${numLit(schema.exclusiveMinimum)}` : `typeof ${v}==='number'&&${v}<=${numLit(schema.exclusiveMinimum)}`
|
|
1195
|
+
lines.push(`if(${c}){${fail('exclusiveMinimum', 'exclusiveMinimum', `{comparison:'>',limit:${numLit(schema.exclusiveMinimum)}}`, `'must be > ${numLit(schema.exclusiveMinimum)}'`)}}`)
|
|
1196
1196
|
}
|
|
1197
1197
|
if (schema.exclusiveMaximum !== undefined) {
|
|
1198
|
-
const c = isNum ? `${v}>=${schema.exclusiveMaximum}` : `typeof ${v}==='number'&&${v}>=${schema.exclusiveMaximum}`
|
|
1199
|
-
lines.push(`if(${c}){${fail('exclusiveMaximum', 'exclusiveMaximum', `{comparison:'<',limit:${schema.exclusiveMaximum}}`, `'must be < ${schema.exclusiveMaximum}'`)}}`)
|
|
1198
|
+
const c = isNum ? `${v}>=${numLit(schema.exclusiveMaximum)}` : `typeof ${v}==='number'&&${v}>=${numLit(schema.exclusiveMaximum)}`
|
|
1199
|
+
lines.push(`if(${c}){${fail('exclusiveMaximum', 'exclusiveMaximum', `{comparison:'<',limit:${numLit(schema.exclusiveMaximum)}}`, `'must be < ${numLit(schema.exclusiveMaximum)}'`)}}`)
|
|
1200
1200
|
}
|
|
1201
1201
|
if (schema.multipleOf !== undefined) {
|
|
1202
|
-
const m = schema.multipleOf
|
|
1202
|
+
const m = numLit(schema.multipleOf)
|
|
1203
1203
|
const ci = ctx.varCounter++
|
|
1204
1204
|
// Use tolerance-based check for floating point (matches C++ behavior)
|
|
1205
|
-
lines.push(`{if(typeof ${v}==='number'&&${multipleOfBad(v,
|
|
1205
|
+
lines.push(`{if(typeof ${v}==='number'&&${multipleOfBad(v, schema.multipleOf)}){${fail('multipleOf', 'multipleOf', `{multipleOf:${m}}`, `'must be multiple of ${m}'`)}}}`)
|
|
1206
1206
|
}
|
|
1207
1207
|
|
|
1208
1208
|
// string length — same s.length fast paths as the boolean codegen above.
|
|
@@ -1210,13 +1210,13 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1210
1210
|
// 2*M <= length <= X passes without scanning. Only call _cpLen in the
|
|
1211
1211
|
// uncertain band (caches it so it isn't called twice when both bounds are set).
|
|
1212
1212
|
if (schema.minLength !== undefined) {
|
|
1213
|
-
const M = schema.minLength
|
|
1213
|
+
const M = +numLit(schema.minLength)
|
|
1214
1214
|
const inner = `${v}.length<${M}||(${v}.length<${M * 2}&&_cpLen(${v})<${M})`
|
|
1215
1215
|
const c = isStr ? inner : `typeof ${v}==='string'&&(${inner})`
|
|
1216
1216
|
lines.push(`if(${c}){${fail('minLength', 'minLength', `{limit:${M}}`, `'must NOT have fewer than ${M} characters'`)}}`)
|
|
1217
1217
|
}
|
|
1218
1218
|
if (schema.maxLength !== undefined) {
|
|
1219
|
-
const X = schema.maxLength
|
|
1219
|
+
const X = +numLit(schema.maxLength)
|
|
1220
1220
|
const inner = `${v}.length>${X * 2}||(${v}.length>${X}&&_cpLen(${v})>${X})`
|
|
1221
1221
|
const c = isStr ? inner : `typeof ${v}==='string'&&(${inner})`
|
|
1222
1222
|
lines.push(`if(${c}){${fail('maxLength', 'maxLength', `{limit:${X}}`, `'must NOT have more than ${X} characters'`)}}`)
|
|
@@ -1243,8 +1243,11 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1243
1243
|
lines.push(`if(${c}){${fail('pattern', 'pattern', `{pattern:${JSON.stringify(schema.pattern)}}`, JSON.stringify(`must match pattern "${schema.pattern}"`))}}`)
|
|
1244
1244
|
}
|
|
1245
1245
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1246
|
+
// The caller's function for the format wins over the built-in check, as
|
|
1247
|
+
// the interpreted engine reads the option; a format named '' is a name.
|
|
1248
|
+
if (typeof schema.format === 'string') {
|
|
1249
|
+
const uf = userFormatFn(ctx.userFormats, schema.format)
|
|
1250
|
+
const fc = uf === null && Object.hasOwn(FORMAT_CODEGEN, schema.format) ? FORMAT_CODEGEN[schema.format] : null
|
|
1248
1251
|
const fmtSp = `${schemaPrefix}/format`
|
|
1249
1252
|
const fmtLit = buildErrorLiteral({ keyword: 'format', format: schema.format, schemaPath: fmtSp, sourceMap: ctx.sourceMap })
|
|
1250
1253
|
const failPush = `_e.push({code:'${fmtLit.codeStr}',keyword:'format',instancePath:${pathExpr||'""'},schemaPath:'${fmtSp}'${ordinalField(ctx, `${fmtSp}`)},params:{format:'${esc(schema.format)}'},message:'must match format "${esc(schema.format)}"',docUrl:'${fmtLit.docUrl}'${fmtLit.frame}});if(!_all)return{valid:false,errors:_e}`
|
|
@@ -1256,17 +1259,12 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1256
1259
|
const ri = ctx.varCounter++
|
|
1257
1260
|
const fmtCode = fc(v, isStr, ctx).replace(/return false/g, `{${failPush};break _fmt${ri}}`)
|
|
1258
1261
|
lines.push(`_fmt${ri}:{${fmtCode}}`)
|
|
1259
|
-
} else if (
|
|
1262
|
+
} else if (uf !== null) {
|
|
1260
1263
|
// User-supplied format checker on the error path: the same closure
|
|
1261
1264
|
// plumbing as the boolean codegen. The function is bound as a parameter
|
|
1262
1265
|
// of the compiled error function; bundle output serializes it via
|
|
1263
1266
|
// Function#toString separately.
|
|
1264
|
-
const
|
|
1265
|
-
const closureName = `_uf_${safeName}`
|
|
1266
|
-
if (ctx.closureVars && !ctx.closureVars.includes(closureName)) {
|
|
1267
|
-
ctx.closureVars.push(closureName)
|
|
1268
|
-
ctx.closureVals.push(ctx.userFormats[schema.format])
|
|
1269
|
-
}
|
|
1267
|
+
const closureName = bindUserFormat(ctx, schema.format, uf)
|
|
1270
1268
|
const guard = isStr ? '' : `typeof ${v}==='string'&&`
|
|
1271
1269
|
lines.push(`if(${guard}!${closureName}(${v})){${failPush}}`)
|
|
1272
1270
|
}
|
|
@@ -1274,12 +1272,12 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1274
1272
|
|
|
1275
1273
|
// array size
|
|
1276
1274
|
if (schema.minItems !== undefined) {
|
|
1277
|
-
const c = isArr ? `${v}.length<${schema.minItems}` : `Array.isArray(${v})&&${v}.length<${schema.minItems}`
|
|
1278
|
-
lines.push(`if(${c}){${fail('minItems', 'minItems', `{limit:${schema.minItems}}`, `'must NOT have fewer than ${schema.minItems} items'`)}}`)
|
|
1275
|
+
const c = isArr ? `${v}.length<${numLit(schema.minItems)}` : `Array.isArray(${v})&&${v}.length<${numLit(schema.minItems)}`
|
|
1276
|
+
lines.push(`if(${c}){${fail('minItems', 'minItems', `{limit:${numLit(schema.minItems)}}`, `'must NOT have fewer than ${numLit(schema.minItems)} items'`)}}`)
|
|
1279
1277
|
}
|
|
1280
1278
|
if (schema.maxItems !== undefined) {
|
|
1281
|
-
const c = isArr ? `${v}.length>${schema.maxItems}` : `Array.isArray(${v})&&${v}.length>${schema.maxItems}`
|
|
1282
|
-
lines.push(`if(${c}){${fail('maxItems', 'maxItems', `{limit:${schema.maxItems}}`, `'must NOT have more than ${schema.maxItems} items'`)}}`)
|
|
1279
|
+
const c = isArr ? `${v}.length>${numLit(schema.maxItems)}` : `Array.isArray(${v})&&${v}.length>${numLit(schema.maxItems)}`
|
|
1280
|
+
lines.push(`if(${c}){${fail('maxItems', 'maxItems', `{limit:${numLit(schema.maxItems)}}`, `'must NOT have more than ${numLit(schema.maxItems)} items'`)}}`)
|
|
1283
1281
|
}
|
|
1284
1282
|
|
|
1285
1283
|
// uniqueItems — tiered: small primitive arrays use nested loop (no allocation)
|
|
@@ -1287,10 +1285,10 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1287
1285
|
|
|
1288
1286
|
// object size
|
|
1289
1287
|
if (schema.minProperties !== undefined) {
|
|
1290
|
-
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&Object.keys(${v}).length<${schema.minProperties}){${fail('minProperties', 'minProperties', `{limit:${schema.minProperties}}`, `'must NOT have fewer than ${schema.minProperties} properties'`)}}`)
|
|
1288
|
+
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&Object.keys(${v}).length<${numLit(schema.minProperties)}){${fail('minProperties', 'minProperties', `{limit:${numLit(schema.minProperties)}}`, `'must NOT have fewer than ${numLit(schema.minProperties)} properties'`)}}`)
|
|
1291
1289
|
}
|
|
1292
1290
|
if (schema.maxProperties !== undefined) {
|
|
1293
|
-
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&Object.keys(${v}).length>${schema.maxProperties}){${fail('maxProperties', 'maxProperties', `{limit:${schema.maxProperties}}`, `'must NOT have more than ${schema.maxProperties} properties'`)}}`)
|
|
1291
|
+
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&Object.keys(${v}).length>${numLit(schema.maxProperties)}){${fail('maxProperties', 'maxProperties', `{limit:${numLit(schema.maxProperties)}}`, `'must NOT have more than ${numLit(schema.maxProperties)} properties'`)}}`)
|
|
1294
1292
|
}
|
|
1295
1293
|
|
|
1296
1294
|
// additionalProperties: false. A key matched by any patternProperties
|
|
@@ -1608,10 +1606,10 @@ function genPropertyNamesE(schema, v, pathExpr, lines, ctx, schemaPrefix, fail)
|
|
|
1608
1606
|
return
|
|
1609
1607
|
}
|
|
1610
1608
|
if (pn.minLength !== undefined) {
|
|
1611
|
-
lines.push(`if(_k${ki}.length<${pn.minLength}){${fail('minLength', 'propertyNames/minLength', `{limit:${pn.minLength}}`, `'must NOT have fewer than ${pn.minLength} characters'`)}}`)
|
|
1609
|
+
lines.push(`if(_k${ki}.length<${numLit(pn.minLength)}){${fail('minLength', 'propertyNames/minLength', `{limit:${numLit(pn.minLength)}}`, `'must NOT have fewer than ${numLit(pn.minLength)} characters'`)}}`)
|
|
1612
1610
|
}
|
|
1613
1611
|
if (pn.maxLength !== undefined) {
|
|
1614
|
-
lines.push(`if(_k${ki}.length>${pn.maxLength}){${fail('maxLength', 'propertyNames/maxLength', `{limit:${pn.maxLength}}`, `'must NOT have more than ${pn.maxLength} characters'`)}}`)
|
|
1612
|
+
lines.push(`if(_k${ki}.length>${numLit(pn.maxLength)}){${fail('maxLength', 'propertyNames/maxLength', `{limit:${numLit(pn.maxLength)}}`, `'must NOT have more than ${numLit(pn.maxLength)} characters'`)}}`)
|
|
1615
1613
|
}
|
|
1616
1614
|
if (pn.pattern) {
|
|
1617
1615
|
const pattern = JSON.stringify(pn.pattern);
|
|
@@ -1653,8 +1651,8 @@ function genContainsE(schema, v, pathExpr, lines, ctx, schemaPrefix, fail) {
|
|
|
1653
1651
|
const subLines = []
|
|
1654
1652
|
nestedGenCode(schema.contains, `_cv`, subLines, ctx)
|
|
1655
1653
|
const fnBody = subLines.length === 0 ? `return true` : `${subLines.join(';')};return true`
|
|
1656
|
-
const minC = schema.minContains !== undefined ? schema.minContains : 1
|
|
1657
|
-
const maxC = schema.maxContains
|
|
1654
|
+
const minC = schema.minContains !== undefined ? numLit(schema.minContains) : 1
|
|
1655
|
+
const maxC = schema.maxContains !== undefined ? numLit(schema.maxContains) : undefined
|
|
1658
1656
|
lines.push(`if(Array.isArray(${v})){const _cf${ci}=function(_cv){${fnBody}};let _cc${ci}=0;for(let _ci${ci}=0;_ci${ci}<${v}.length;_ci${ci}++){if(_cf${ci}(${v}[_ci${ci}]))_cc${ci}++}`)
|
|
1659
1657
|
lines.push(`if(_cc${ci}<${minC}){${fail('contains', 'contains', `{minContains:${minC}}`, `'must contain at least ${minC} valid item(s)'`)}}`)
|
|
1660
1658
|
if (maxC !== undefined) {
|
|
@@ -2494,25 +2492,25 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
2494
2492
|
}
|
|
2495
2493
|
|
|
2496
2494
|
// numeric — skip type guard if known
|
|
2497
|
-
if (schema.minimum !== undefined) { const c = isNum ? `${v}<${schema.minimum}` : `typeof ${v}==='number'&&${v}<${schema.minimum}`; lines.push(`if(${c}){${fail('minimum', 'minimum', `{comparison:'>=',limit:${schema.minimum}}`, `'must be >= ${schema.minimum}'`)}}`) }
|
|
2498
|
-
if (schema.maximum !== undefined) { const c = isNum ? `${v}>${schema.maximum}` : `typeof ${v}==='number'&&${v}>${schema.maximum}`; lines.push(`if(${c}){${fail('maximum', 'maximum', `{comparison:'<=',limit:${schema.maximum}}`, `'must be <= ${schema.maximum}'`)}}`) }
|
|
2499
|
-
if (schema.exclusiveMinimum !== undefined) { const c = isNum ? `${v}<=${schema.exclusiveMinimum}` : `typeof ${v}==='number'&&${v}<=${schema.exclusiveMinimum}`; lines.push(`if(${c}){${fail('exclusiveMinimum', 'exclusiveMinimum', `{comparison:'>',limit:${schema.exclusiveMinimum}}`, `'must be > ${schema.exclusiveMinimum}'`)}}`) }
|
|
2500
|
-
if (schema.exclusiveMaximum !== undefined) { const c = isNum ? `${v}>=${schema.exclusiveMaximum}` : `typeof ${v}==='number'&&${v}>=${schema.exclusiveMaximum}`; lines.push(`if(${c}){${fail('exclusiveMaximum', 'exclusiveMaximum', `{comparison:'<',limit:${schema.exclusiveMaximum}}`, `'must be < ${schema.exclusiveMaximum}'`)}}`) }
|
|
2495
|
+
if (schema.minimum !== undefined) { const c = isNum ? `${v}<${numLit(schema.minimum)}` : `typeof ${v}==='number'&&${v}<${numLit(schema.minimum)}`; lines.push(`if(${c}){${fail('minimum', 'minimum', `{comparison:'>=',limit:${numLit(schema.minimum)}}`, `'must be >= ${numLit(schema.minimum)}'`)}}`) }
|
|
2496
|
+
if (schema.maximum !== undefined) { const c = isNum ? `${v}>${numLit(schema.maximum)}` : `typeof ${v}==='number'&&${v}>${numLit(schema.maximum)}`; lines.push(`if(${c}){${fail('maximum', 'maximum', `{comparison:'<=',limit:${numLit(schema.maximum)}}`, `'must be <= ${numLit(schema.maximum)}'`)}}`) }
|
|
2497
|
+
if (schema.exclusiveMinimum !== undefined) { const c = isNum ? `${v}<=${numLit(schema.exclusiveMinimum)}` : `typeof ${v}==='number'&&${v}<=${numLit(schema.exclusiveMinimum)}`; lines.push(`if(${c}){${fail('exclusiveMinimum', 'exclusiveMinimum', `{comparison:'>',limit:${numLit(schema.exclusiveMinimum)}}`, `'must be > ${numLit(schema.exclusiveMinimum)}'`)}}`) }
|
|
2498
|
+
if (schema.exclusiveMaximum !== undefined) { const c = isNum ? `${v}>=${numLit(schema.exclusiveMaximum)}` : `typeof ${v}==='number'&&${v}>=${numLit(schema.exclusiveMaximum)}`; lines.push(`if(${c}){${fail('exclusiveMaximum', 'exclusiveMaximum', `{comparison:'<',limit:${numLit(schema.exclusiveMaximum)}}`, `'must be < ${numLit(schema.exclusiveMaximum)}'`)}}`) }
|
|
2501
2499
|
if (schema.multipleOf !== undefined) {
|
|
2502
|
-
const m = schema.multipleOf
|
|
2500
|
+
const m = numLit(schema.multipleOf)
|
|
2503
2501
|
const ci = ctx.varCounter++
|
|
2504
|
-
lines.push(`{if(typeof ${v}==='number'&&${multipleOfBad(v,
|
|
2502
|
+
lines.push(`{if(typeof ${v}==='number'&&${multipleOfBad(v, schema.multipleOf)}){${fail('multipleOf', 'multipleOf', `{multipleOf:${m}}`, `'must be multiple of ${m}'`)}}}`)
|
|
2505
2503
|
}
|
|
2506
2504
|
|
|
2507
2505
|
// string length — s.length fast paths, _cpLen only in the uncertain band.
|
|
2508
2506
|
if (schema.minLength !== undefined) {
|
|
2509
|
-
const M = schema.minLength
|
|
2507
|
+
const M = +numLit(schema.minLength)
|
|
2510
2508
|
const inner = `${v}.length<${M}||(${v}.length<${M * 2}&&_cpLen(${v})<${M})`
|
|
2511
2509
|
const c = isStr ? inner : `typeof ${v}==='string'&&(${inner})`
|
|
2512
2510
|
lines.push(`if(${c}){${fail('minLength', 'minLength', `{limit:${M}}`, `'must NOT have fewer than ${M} characters'`)}}`)
|
|
2513
2511
|
}
|
|
2514
2512
|
if (schema.maxLength !== undefined) {
|
|
2515
|
-
const X = schema.maxLength
|
|
2513
|
+
const X = +numLit(schema.maxLength)
|
|
2516
2514
|
const inner = `${v}.length>${X * 2}||(${v}.length>${X}&&_cpLen(${v})>${X})`
|
|
2517
2515
|
const c = isStr ? inner : `typeof ${v}==='string'&&(${inner})`
|
|
2518
2516
|
lines.push(`if(${c}){${fail('maxLength', 'maxLength', `{limit:${X}}`, `'must NOT have more than ${X} characters'`)}}`)
|
|
@@ -2531,36 +2529,32 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
2531
2529
|
lines.push(`if(${c}){${fail('pattern', 'pattern', `{pattern:${JSON.stringify(schema.pattern)}}`, JSON.stringify(`must match pattern "${schema.pattern}"`))}}`)
|
|
2532
2530
|
}
|
|
2533
2531
|
}
|
|
2534
|
-
if (schema.format) {
|
|
2535
|
-
const
|
|
2532
|
+
if (typeof schema.format === 'string') {
|
|
2533
|
+
const uf = userFormatFn(ctx.userFormats, schema.format)
|
|
2534
|
+
const fc = uf === null && Object.hasOwn(FORMAT_CODEGEN, schema.format) ? FORMAT_CODEGEN[schema.format] : null
|
|
2536
2535
|
if (fc) {
|
|
2537
2536
|
// Same rule as the error codegen: one violation, one error. The first
|
|
2538
2537
|
// failing statement pushes and leaves the labeled block.
|
|
2539
2538
|
const ri = ctx.varCounter++
|
|
2540
2539
|
const code = fc(v, isStr, ctx).replace(/return false/g, `{${fail('format', 'format', `{format:'${esc(schema.format)}'}`, `'must match format "${esc(schema.format)}"'`)};break _fmt${ri}}`)
|
|
2541
2540
|
lines.push(`_fmt${ri}:{${code}}`)
|
|
2542
|
-
} else if (
|
|
2543
|
-
const
|
|
2544
|
-
const closureName = `_uf_${safeName}`
|
|
2545
|
-
if (!ctx.closureVars.includes(closureName)) {
|
|
2546
|
-
ctx.closureVars.push(closureName)
|
|
2547
|
-
ctx.closureVals.push(ctx.userFormats[schema.format])
|
|
2548
|
-
}
|
|
2541
|
+
} else if (uf !== null) {
|
|
2542
|
+
const closureName = bindUserFormat(ctx, schema.format, uf)
|
|
2549
2543
|
const guard = isStr ? '' : `typeof ${v}==='string'&&`
|
|
2550
2544
|
lines.push(`if(${guard}!${closureName}(${v})){${fail('format', 'format', `{format:'${esc(schema.format)}'}`, `'must match format "${esc(schema.format)}"'`)}}`)
|
|
2551
2545
|
}
|
|
2552
2546
|
}
|
|
2553
2547
|
|
|
2554
2548
|
// array size
|
|
2555
|
-
if (schema.minItems !== undefined) { const c = isArr ? `${v}.length<${schema.minItems}` : `Array.isArray(${v})&&${v}.length<${schema.minItems}`; lines.push(`if(${c}){${fail('minItems', 'minItems', `{limit:${schema.minItems}}`, `'must NOT have fewer than ${schema.minItems} items'`)}}`) }
|
|
2556
|
-
if (schema.maxItems !== undefined) { const c = isArr ? `${v}.length>${schema.maxItems}` : `Array.isArray(${v})&&${v}.length>${schema.maxItems}`; lines.push(`if(${c}){${fail('maxItems', 'maxItems', `{limit:${schema.maxItems}}`, `'must NOT have more than ${schema.maxItems} items'`)}}`) }
|
|
2549
|
+
if (schema.minItems !== undefined) { const c = isArr ? `${v}.length<${numLit(schema.minItems)}` : `Array.isArray(${v})&&${v}.length<${numLit(schema.minItems)}`; lines.push(`if(${c}){${fail('minItems', 'minItems', `{limit:${numLit(schema.minItems)}}`, `'must NOT have fewer than ${numLit(schema.minItems)} items'`)}}`) }
|
|
2550
|
+
if (schema.maxItems !== undefined) { const c = isArr ? `${v}.length>${numLit(schema.maxItems)}` : `Array.isArray(${v})&&${v}.length>${numLit(schema.maxItems)}`; lines.push(`if(${c}){${fail('maxItems', 'maxItems', `{limit:${numLit(schema.maxItems)}}`, `'must NOT have more than ${numLit(schema.maxItems)} items'`)}}`) }
|
|
2557
2551
|
|
|
2558
2552
|
// uniqueItems — tiered: small primitive arrays use nested loop (no allocation)
|
|
2559
2553
|
if (schema.uniqueItems) genUniqueItemsC(schema, v, pathExpr, lines, ctx, schemaPrefix, fail, isArr)
|
|
2560
2554
|
|
|
2561
2555
|
// object size
|
|
2562
|
-
if (schema.minProperties !== undefined) lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&Object.keys(${v}).length<${schema.minProperties}){${fail('minProperties', 'minProperties', `{limit:${schema.minProperties}}`, `'must NOT have fewer than ${schema.minProperties} properties'`)}}`)
|
|
2563
|
-
if (schema.maxProperties !== undefined) lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&Object.keys(${v}).length>${schema.maxProperties}){${fail('maxProperties', 'maxProperties', `{limit:${schema.maxProperties}}`, `'must NOT have more than ${schema.maxProperties} properties'`)}}`)
|
|
2556
|
+
if (schema.minProperties !== undefined) lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&Object.keys(${v}).length<${numLit(schema.minProperties)}){${fail('minProperties', 'minProperties', `{limit:${numLit(schema.minProperties)}}`, `'must NOT have fewer than ${numLit(schema.minProperties)} properties'`)}}`)
|
|
2557
|
+
if (schema.maxProperties !== undefined) lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&Object.keys(${v}).length>${numLit(schema.maxProperties)}){${fail('maxProperties', 'maxProperties', `{limit:${numLit(schema.maxProperties)}}`, `'must NOT have more than ${numLit(schema.maxProperties)} properties'`)}}`)
|
|
2564
2558
|
|
|
2565
2559
|
// additionalProperties — skip if patternProperties present (handled in unified loop below)
|
|
2566
2560
|
// Small property sets: direct === chain (no Set allocation)
|
|
@@ -2846,7 +2840,11 @@ function annotNames (node, ctx, names, seen, top) {
|
|
|
2846
2840
|
for (const k of ANNOT_BLOCKERS) if (node[k] !== undefined) return false
|
|
2847
2841
|
if (!top && node.unevaluatedProperties !== undefined && node.unevaluatedProperties !== true) return false
|
|
2848
2842
|
const add = (key) => { if (!names.has(key)) { if (names.size >= ANNOT_NAME_MAX) return false; names.set(key, names.size) } return true }
|
|
2849
|
-
|
|
2843
|
+
// One table holds the declared names, the patterns as '/' + pattern
|
|
2844
|
+
// and the all bit: a declared name that starts with '/' or equals the
|
|
2845
|
+
// all bit's key would be read as one of those (a property named '/x'
|
|
2846
|
+
// let 'axb' through as if it matched /x/), so the inline form declines.
|
|
2847
|
+
if (node.properties && typeof node.properties === 'object') for (const k of Object.keys(node.properties)) if (k[0] === '/' || k === ALL_BIT_KEY || !add(k)) return false
|
|
2850
2848
|
if (node.patternProperties && typeof node.patternProperties === 'object') for (const pat of Object.keys(node.patternProperties)) if (!add('/' + pat)) return false
|
|
2851
2849
|
if ((!top && node.additionalProperties !== undefined) || (!top && node.unevaluatedProperties === true)) if (!add(ALL_BIT_KEY)) return false
|
|
2852
2850
|
let subs
|
|
@@ -3475,8 +3473,8 @@ function genPatternPropertiesC(schema, v, pathExpr, lines, ctx, schemaPrefix, fa
|
|
|
3475
3473
|
lines.push(`${guard}{${ownHead(ctx, v, kVar, 'const')}{`)
|
|
3476
3474
|
// propertyNames checks (merged)
|
|
3477
3475
|
if (pn) {
|
|
3478
|
-
if (pn.minLength !== undefined) lines.push(`if(${kVar}.length<${pn.minLength}){${fail('minLength', 'propertyNames/minLength', `{limit:${pn.minLength}}`, `'must NOT have fewer than ${pn.minLength} characters'`)}}`)
|
|
3479
|
-
if (pn.maxLength !== undefined) lines.push(`if(${kVar}.length>${pn.maxLength}){${fail('maxLength', 'propertyNames/maxLength', `{limit:${pn.maxLength}}`, `'must NOT have more than ${pn.maxLength} characters'`)}}`)
|
|
3476
|
+
if (pn.minLength !== undefined) lines.push(`if(${kVar}.length<${numLit(pn.minLength)}){${fail('minLength', 'propertyNames/minLength', `{limit:${numLit(pn.minLength)}}`, `'must NOT have fewer than ${numLit(pn.minLength)} characters'`)}}`)
|
|
3477
|
+
if (pn.maxLength !== undefined) lines.push(`if(${kVar}.length>${numLit(pn.maxLength)}){${fail('maxLength', 'propertyNames/maxLength', `{limit:${numLit(pn.maxLength)}}`, `'must NOT have more than ${numLit(pn.maxLength)} characters'`)}}`)
|
|
3480
3478
|
if (pn.pattern) {
|
|
3481
3479
|
const fast = fastPrefixCheck(pn.pattern, kVar)
|
|
3482
3480
|
if (fast) {
|
|
@@ -3511,8 +3509,8 @@ function genPatternPropertiesC(schema, v, pathExpr, lines, ctx, schemaPrefix, fa
|
|
|
3511
3509
|
handledPropertyNames = !!pn
|
|
3512
3510
|
lines.push(`${guard}{${ownHead(ctx, v, kVar, 'const')}{`)
|
|
3513
3511
|
if (pn) {
|
|
3514
|
-
if (pn.minLength !== undefined) lines.push(`if(${kVar}.length<${pn.minLength}){${fail('minLength', 'propertyNames/minLength', `{limit:${pn.minLength}}`, `'must NOT have fewer than ${pn.minLength} characters'`)}}`)
|
|
3515
|
-
if (pn.maxLength !== undefined) lines.push(`if(${kVar}.length>${pn.maxLength}){${fail('maxLength', 'propertyNames/maxLength', `{limit:${pn.maxLength}}`, `'must NOT have more than ${pn.maxLength} characters'`)}}`)
|
|
3512
|
+
if (pn.minLength !== undefined) lines.push(`if(${kVar}.length<${numLit(pn.minLength)}){${fail('minLength', 'propertyNames/minLength', `{limit:${numLit(pn.minLength)}}`, `'must NOT have fewer than ${numLit(pn.minLength)} characters'`)}}`)
|
|
3513
|
+
if (pn.maxLength !== undefined) lines.push(`if(${kVar}.length>${numLit(pn.maxLength)}){${fail('maxLength', 'propertyNames/maxLength', `{limit:${numLit(pn.maxLength)}}`, `'must NOT have more than ${numLit(pn.maxLength)} characters'`)}}`)
|
|
3516
3514
|
if (pn.pattern) {
|
|
3517
3515
|
const fast = fastPrefixCheck(pn.pattern, kVar)
|
|
3518
3516
|
if (fast) {
|
|
@@ -3570,10 +3568,10 @@ function genPropertyNamesC(schema, v, pathExpr, lines, ctx, schemaPrefix, fail)
|
|
|
3570
3568
|
return
|
|
3571
3569
|
}
|
|
3572
3570
|
if (pn.minLength !== undefined) {
|
|
3573
|
-
lines.push(`if(_k${ki}.length<${pn.minLength}){${fail('minLength', 'propertyNames/minLength', `{limit:${pn.minLength}}`, `'must NOT have fewer than ${pn.minLength} characters'`)}}`)
|
|
3571
|
+
lines.push(`if(_k${ki}.length<${numLit(pn.minLength)}){${fail('minLength', 'propertyNames/minLength', `{limit:${numLit(pn.minLength)}}`, `'must NOT have fewer than ${numLit(pn.minLength)} characters'`)}}`)
|
|
3574
3572
|
}
|
|
3575
3573
|
if (pn.maxLength !== undefined) {
|
|
3576
|
-
lines.push(`if(_k${ki}.length>${pn.maxLength}){${fail('maxLength', 'propertyNames/maxLength', `{limit:${pn.maxLength}}`, `'must NOT have more than ${pn.maxLength} characters'`)}}`)
|
|
3574
|
+
lines.push(`if(_k${ki}.length>${numLit(pn.maxLength)}){${fail('maxLength', 'propertyNames/maxLength', `{limit:${numLit(pn.maxLength)}}`, `'must NOT have more than ${numLit(pn.maxLength)} characters'`)}}`)
|
|
3577
3575
|
}
|
|
3578
3576
|
if (pn.pattern) {
|
|
3579
3577
|
const ri = ctx.varCounter++
|
|
@@ -3607,8 +3605,8 @@ function genContainsC(schema, v, pathExpr, lines, ctx, schemaPrefix, fail) {
|
|
|
3607
3605
|
const subLines = []
|
|
3608
3606
|
nestedGenCode(schema.contains, `_cv`, subLines, ctx)
|
|
3609
3607
|
const fnBody = subLines.length === 0 ? `return true` : `${subLines.join(';')};return true`
|
|
3610
|
-
const minC = schema.minContains !== undefined ? schema.minContains : 1
|
|
3611
|
-
const maxC = schema.maxContains
|
|
3608
|
+
const minC = schema.minContains !== undefined ? numLit(schema.minContains) : 1
|
|
3609
|
+
const maxC = schema.maxContains !== undefined ? numLit(schema.maxContains) : undefined
|
|
3612
3610
|
lines.push(`if(Array.isArray(${v})){const _cf${ci}=function(_cv){${fnBody}};let _cc${ci}=0;for(let _ci${ci}=0;_ci${ci}<${v}.length;_ci${ci}++){if(_cf${ci}(${v}[_ci${ci}]))_cc${ci}++}`)
|
|
3613
3611
|
lines.push(`if(_cc${ci}<${minC}){${fail('contains', 'contains', `{minContains:${minC}}`, `'must contain at least ${minC} valid item(s)'`)}}`)
|
|
3614
3612
|
if (maxC !== undefined) lines.push(`if(_cc${ci}>${maxC}){${fail('contains', 'contains', `{minContains:${minC},maxContains:${maxC}}`, `'must NOT contain more than ${maxC} valid item(s)'`)}}`)
|