ata-validator 1.61.3 → 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 +67 -10
- package/bin/ata.js +3 -2
- package/compat.js +15 -2
- 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-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/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 +17 -8
- package/stream.d.ts +51 -0
- package/stream.js +3 -0
- package/stream.mjs +3 -0
package/lib/js-compiler.js
CHANGED
|
@@ -230,6 +230,17 @@ const useSafeEngine = (src) => {
|
|
|
230
230
|
if (_nativeIsLinear(src)) return false
|
|
231
231
|
return patternIsSafe(src)
|
|
232
232
|
}
|
|
233
|
+
// Whether the generators may carry a pattern at all. A pattern with a
|
|
234
|
+
// Unicode property escape cannot take the linear engine, so it is written
|
|
235
|
+
// for the platform RegExp only when that is shown to run it in linear time
|
|
236
|
+
// (nativeIsLinear reads the escape as one class); otherwise the schema is
|
|
237
|
+
// declined to the interpreted engine. `^(\p{L}+)+$` went to the RegExp
|
|
238
|
+
// unchecked and took a second on 28 letters and a '!'.
|
|
239
|
+
function patternEngineOk (src) {
|
|
240
|
+
if (typeof src !== 'string' || !PROP_ESCAPE.test(src)) return true
|
|
241
|
+
if (_nativeIsLinear === null) _nativeIsLinear = require('./regex-linear').nativeIsLinear
|
|
242
|
+
return _nativeIsLinear(src)
|
|
243
|
+
}
|
|
233
244
|
|
|
234
245
|
// Closure value for a patternProperties/propertyNames regex: the linear-time
|
|
235
246
|
// safe matcher when the pattern is in the supported subset (and flag the ctx so
|
|
@@ -568,10 +579,11 @@ function compileToJS(schema, defs, schemaMap) {
|
|
|
568
579
|
|
|
569
580
|
// format — hand-written fast checks. A format the code generator asserts
|
|
570
581
|
// but this path cannot is a decline, not a pass.
|
|
571
|
-
if (schema.format) {
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
582
|
+
if (typeof schema.format === 'string') {
|
|
583
|
+
const table = formatChecks()
|
|
584
|
+
const fc = Object.hasOwn(table, schema.format) ? table[schema.format] : undefined
|
|
585
|
+
if (typeof fc === 'function') checks.push((d) => typeof d !== 'string' || fc(d))
|
|
586
|
+
else if (Object.hasOwn(FORMAT_CODEGEN, schema.format)) return null
|
|
575
587
|
}
|
|
576
588
|
|
|
577
589
|
// array size
|
|
@@ -836,8 +848,52 @@ function buildTypeCheck(types) {
|
|
|
836
848
|
// 0.1 with -0.09999999999999995, which is neither near zero nor near the
|
|
837
849
|
// divisor, so it called -1 not a multiple of 0.1 when -1 / 0.1 is exactly -10.
|
|
838
850
|
// Emitted from one place because the expression stood in six.
|
|
851
|
+
// Whether a value of `known` type (proven by a parent) also satisfies
|
|
852
|
+
// `types`, so the child's own type check can be skipped: the same type, or
|
|
853
|
+
// an integer where a number is asked for.
|
|
854
|
+
function typeImplied (known, types) {
|
|
855
|
+
for (let i = 0; i < types.length; i++) {
|
|
856
|
+
if (types[i] === known) return true
|
|
857
|
+
if (types[i] === 'number' && known === 'integer') return true
|
|
858
|
+
}
|
|
859
|
+
return false
|
|
860
|
+
}
|
|
861
|
+
// The helper name a user format function is bound as. One name per format
|
|
862
|
+
// name, whatever the characters: 'foo-bar' and 'foo_bar' both came out as
|
|
863
|
+
// `_uf_foo_bar`, and the first function bound answered for both. Characters
|
|
864
|
+
// outside [A-Za-z0-9_] are written as `$` and four hex digits, so the map
|
|
865
|
+
// is one to one and both generators and the ahead-of-time output agree on it.
|
|
866
|
+
function ufName (format) {
|
|
867
|
+
return '_uf_' + String(format).replace(/[^A-Za-z0-9_]/g, (c) => '$' + c.charCodeAt(0).toString(16).padStart(4, '0'))
|
|
868
|
+
}
|
|
869
|
+
// The caller's own format function by that name, or null. An inherited
|
|
870
|
+
// value (`constructor`) is not one, as tier 0 and the interpreted engine
|
|
871
|
+
// read the option.
|
|
872
|
+
// Binds the function as a closure parameter of the generated function, once
|
|
873
|
+
// per format name, and remembers the name for the emitters.
|
|
874
|
+
function bindUserFormat (ctx, format, fn) {
|
|
875
|
+
const closureName = ufName(format)
|
|
876
|
+
if (!ctx.closureVars) { ctx.closureVars = []; ctx.closureVals = [] }
|
|
877
|
+
if (!ctx.closureVars.includes(closureName)) {
|
|
878
|
+
ctx.closureVars.push(closureName)
|
|
879
|
+
ctx.closureVals.push(fn)
|
|
880
|
+
}
|
|
881
|
+
if (!ctx.formatNames) ctx.formatNames = new Map()
|
|
882
|
+
ctx.formatNames.set(closureName, format)
|
|
883
|
+
return closureName
|
|
884
|
+
}
|
|
885
|
+
function formatNameOf (ctx, closureName) {
|
|
886
|
+
const name = ctx.formatNames ? ctx.formatNames.get(closureName) : undefined
|
|
887
|
+
return name === undefined ? null : name
|
|
888
|
+
}
|
|
889
|
+
function userFormatFn (userFormats, format) {
|
|
890
|
+
if (!userFormats || typeof format !== 'string' || !Object.prototype.hasOwnProperty.call(userFormats, format)) return null
|
|
891
|
+
const fn = userFormats[format]
|
|
892
|
+
return typeof fn === 'function' ? fn : null
|
|
893
|
+
}
|
|
839
894
|
function multipleOfBad(v, m) {
|
|
840
|
-
|
|
895
|
+
m = numLit(m)
|
|
896
|
+
if (+m === 0) return 'true'
|
|
841
897
|
// Negated rather than written as "the distance is too large": a quotient
|
|
842
898
|
// that overflows to Infinity makes the distance NaN, and every comparison
|
|
843
899
|
// with NaN is false, so the direct form called 1e308 a multiple of
|
|
@@ -1444,6 +1500,36 @@ function sharedDefNames (root, defs) {
|
|
|
1444
1500
|
const def = defs[name]
|
|
1445
1501
|
if (n >= 2 && def && typeof def === 'object' && JSON.stringify(def).length >= SHARED_DEF_MIN_CHARS) out.add(name)
|
|
1446
1502
|
}
|
|
1503
|
+
// A definition small as written can still be large once its references
|
|
1504
|
+
// are followed: one that applies the next one twice, layer after layer, is
|
|
1505
|
+
// 2^n expanded. The $dynamicRef unrolling makes exactly that, and hands
|
|
1506
|
+
// the same copy to both places, so the count above (by node, from the
|
|
1507
|
+
// schema as given, whose references name the original definitions) saw
|
|
1508
|
+
// none of it, and sixteen layers came to a verdict function too large for
|
|
1509
|
+
// the stack. Here references are counted at every place they are reached
|
|
1510
|
+
// from in the definitions the generators read, and a definition applied
|
|
1511
|
+
// twice whose expansion passes SHARED_DEF_EXPANDED_MIN is shared as well.
|
|
1512
|
+
// The bound is high so ordinary definitions stay inline: at 256 KB the set
|
|
1513
|
+
// is unchanged on every SchemaStore schema (at 8 KB six changed, and
|
|
1514
|
+
// web-types measured about 3% slower).
|
|
1515
|
+
const edges = new Map()
|
|
1516
|
+
const seen2 = new WeakSet()
|
|
1517
|
+
const walk2 = (node) => {
|
|
1518
|
+
if (node === null || typeof node !== 'object') return
|
|
1519
|
+
if (typeof node.$ref === 'string') {
|
|
1520
|
+
const m = /^#\/(?:\$defs|definitions)\/([^/]+)$/.exec(node.$ref)
|
|
1521
|
+
if (m) edges.set(m[1], (edges.get(m[1]) || 0) + 1)
|
|
1522
|
+
}
|
|
1523
|
+
if (seen2.has(node)) return
|
|
1524
|
+
seen2.add(node)
|
|
1525
|
+
for (const v of Object.values(node)) walk2(v)
|
|
1526
|
+
}
|
|
1527
|
+
walk2(defs)
|
|
1528
|
+
for (const [name, n] of edges) {
|
|
1529
|
+
if (n < 2 || out.has(name)) continue
|
|
1530
|
+
const def = defs[name]
|
|
1531
|
+
if (def && typeof def === 'object' && expandedChars(def, SHARED_DEF_EXPANDED_MIN, defs) >= SHARED_DEF_EXPANDED_MIN) out.add(name)
|
|
1532
|
+
}
|
|
1447
1533
|
return out
|
|
1448
1534
|
}
|
|
1449
1535
|
// Raising this to inline small definitions at every site was measured and
|
|
@@ -1455,6 +1541,7 @@ function sharedDefNames (root, defs) {
|
|
|
1455
1541
|
// was tried too and gave 1.5x, not enough to carry its complexity. Left as a
|
|
1456
1542
|
// known trade: on that shape the shared function costs 27 ns a call.
|
|
1457
1543
|
const SHARED_DEF_MIN_CHARS = 120
|
|
1544
|
+
const SHARED_DEF_EXPANDED_MIN = 256 * 1024
|
|
1458
1545
|
|
|
1459
1546
|
// A small definition applied at many places is called with objects of many
|
|
1460
1547
|
// shapes, one or two per place, and V8 gives a function's property reads up
|
|
@@ -1661,6 +1748,27 @@ function refStringsOf(node) {
|
|
|
1661
1748
|
return set
|
|
1662
1749
|
}
|
|
1663
1750
|
|
|
1751
|
+
// The keywords whose values the generators write into source as numbers. A
|
|
1752
|
+
// value that is not a finite number is not written at all: the gate hands
|
|
1753
|
+
// the schema to the interpreted engine. Written as it came, a string value
|
|
1754
|
+
// was source, so `minimum: '0||(process.exitCode=7)'` ran.
|
|
1755
|
+
const NUMERIC_KEYWORDS = ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'multipleOf', 'minLength', 'maxLength', 'minItems', 'maxItems', 'minProperties', 'maxProperties', 'minContains', 'maxContains']
|
|
1756
|
+
function hasNonNumericBound (node) {
|
|
1757
|
+
for (let i = 0; i < NUMERIC_KEYWORDS.length; i++) {
|
|
1758
|
+
const v = node[NUMERIC_KEYWORDS[i]]
|
|
1759
|
+
// v - v is 0 for a finite number and NaN for NaN and the infinities.
|
|
1760
|
+
if (v !== undefined && (typeof v !== 'number' || v - v !== 0)) return true
|
|
1761
|
+
}
|
|
1762
|
+
return false
|
|
1763
|
+
}
|
|
1764
|
+
// Every number the generators write into source goes through here. The gate
|
|
1765
|
+
// has already turned away a value that is not a finite number; should one
|
|
1766
|
+
// reach this anyway, the schema declines rather than becoming code.
|
|
1767
|
+
function numLit (x) {
|
|
1768
|
+
if (typeof x !== 'number' || !Number.isFinite(x)) throw DECLINE
|
|
1769
|
+
return String(x)
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1664
1772
|
function codegenSafe(schema, schemaMap) {
|
|
1665
1773
|
if (typeof schema === 'boolean') return true
|
|
1666
1774
|
if (typeof schema !== 'object' || schema === null) return true
|
|
@@ -1670,6 +1778,17 @@ function codegenSafe(schema, schemaMap) {
|
|
|
1670
1778
|
// vacuous, so hand the schema over rather than silently ignore a constraint.
|
|
1671
1779
|
if (schema.propertyDependencies !== undefined) return false
|
|
1672
1780
|
|
|
1781
|
+
if (hasNonNumericBound(schema)) return false
|
|
1782
|
+
|
|
1783
|
+
// A pattern the linear engine cannot take (a Unicode property escape) runs
|
|
1784
|
+
// on the platform RegExp only when that is shown to be linear too; any
|
|
1785
|
+
// other goes to the interpreted engine, like a pattern with a backreference.
|
|
1786
|
+
if (typeof schema.pattern === 'string' && !patternEngineOk(schema.pattern)) return false
|
|
1787
|
+
if (schema.patternProperties && typeof schema.patternProperties === 'object') {
|
|
1788
|
+
for (const pat of Object.keys(schema.patternProperties)) if (!patternEngineOk(pat)) return false
|
|
1789
|
+
}
|
|
1790
|
+
if (schema.propertyNames && typeof schema.propertyNames === 'object' && typeof schema.propertyNames.pattern === 'string' && !patternEngineOk(schema.propertyNames.pattern)) return false
|
|
1791
|
+
|
|
1673
1792
|
// Only bail on $dynamicRef if it can't be resolved at compile time
|
|
1674
1793
|
if (schema.$dynamicRef && !schema.$dynamicRef.startsWith('#')) return false
|
|
1675
1794
|
|
|
@@ -2572,6 +2691,7 @@ const _refFreeLength = new WeakMap()
|
|
|
2572
2691
|
const DYN_DROP = new Set(['$id', '$anchor', '$dynamicAnchor', '$defs', 'definitions'])
|
|
2573
2692
|
const BOOL_TRUE = { bool: true }
|
|
2574
2693
|
const BOOL_FALSE = { bool: false }
|
|
2694
|
+
const DYN_COPY_BUDGET = 4096
|
|
2575
2695
|
function expandDynamicScopes(root, schemaMap) {
|
|
2576
2696
|
if (root === null || typeof root !== 'object' || Array.isArray(root)) return null
|
|
2577
2697
|
const { indexSchemas, resolveRef, splitFragment, resolveUri } = require('./interpreter')._refInternals
|
|
@@ -2634,6 +2754,11 @@ function expandDynamicScopes(root, schemaMap) {
|
|
|
2634
2754
|
const e = { copy: Array.isArray(node) ? [] : {}, sig, name: null, referenced: false }
|
|
2635
2755
|
bySig.set(sig, e)
|
|
2636
2756
|
order.push(e)
|
|
2757
|
+
// One copy per node and scope chain: two embedded resources per layer
|
|
2758
|
+
// that both reference the next layer make 2^i copies of layer i, and
|
|
2759
|
+
// sixteen layers ran out of stack after a second. Past the budget the
|
|
2760
|
+
// unrolling declines and the interpreted engine answers.
|
|
2761
|
+
if (order.length > DYN_COPY_BUDGET) { fail = true; return e }
|
|
2637
2762
|
if (Array.isArray(node)) { for (const x of node) e.copy.push(copyValue(x, base, chain)); return e }
|
|
2638
2763
|
const out = e.copy
|
|
2639
2764
|
// Only a schema node reads `$ref` as a reference and carries an `$id`; in
|
|
@@ -2664,7 +2789,8 @@ function expandDynamicScopes(root, schemaMap) {
|
|
|
2664
2789
|
// (a type name, a limit, a required name) is kept as it is.
|
|
2665
2790
|
const copyValue = (v, base, chain) => (v !== null && typeof v === 'object') || typeof v === 'boolean' ? entryFor(v, base, chain).copy : v
|
|
2666
2791
|
const rootBase = state.nodeBase.get(root)
|
|
2667
|
-
|
|
2792
|
+
let rootEntry
|
|
2793
|
+
try { rootEntry = entryFor(root, rootBase, [rootBase]) } catch (e) { if (e instanceof RangeError) return null; throw e }
|
|
2668
2794
|
if (fail) return null
|
|
2669
2795
|
const rootCopy = rootEntry.copy
|
|
2670
2796
|
const defsKey = root.$defs && typeof root.$defs === 'object' ? '$defs' : (root.definitions && typeof root.definitions === 'object' ? 'definitions' : '$defs')
|
|
@@ -2815,8 +2941,8 @@ const VERDICT_SPLIT = 64 * 1024
|
|
|
2815
2941
|
// stay expanded and keep their speed.
|
|
2816
2942
|
const EXPANDED_SHARE = 400 * 1024
|
|
2817
2943
|
const SIZE_SKIPS = new Set(['$defs', 'definitions', 'description', 'title', 'examples', 'default', '$comment', 'markdownDescription', 'deprecated', 'readOnly', 'writeOnly'])
|
|
2818
|
-
function expandedChars (root, limit) {
|
|
2819
|
-
const defs = (root && (root.$defs || root.definitions)) || {}
|
|
2944
|
+
function expandedChars (root, limit, rootDefs) {
|
|
2945
|
+
const defs = rootDefs || (root && (root.$defs || root.definitions)) || {}
|
|
2820
2946
|
const memo = new Map()
|
|
2821
2947
|
const size = (n, stack) => {
|
|
2822
2948
|
if (n === null || typeof n !== 'object') return 8
|
|
@@ -3185,11 +3311,7 @@ function attachHeavy (boolFn, P) {
|
|
|
3185
3311
|
if (closureNames[i].startsWith('_uf_')) {
|
|
3186
3312
|
// The format's own name, so an emitter can look it up at runtime
|
|
3187
3313
|
// instead of embedding the function.
|
|
3188
|
-
|
|
3189
|
-
for (const key of Object.keys(ctx.userFormats)) {
|
|
3190
|
-
if (ctx.userFormats[key] === closureValues[i]) { format = key; break }
|
|
3191
|
-
}
|
|
3192
|
-
fmtEntries.push({ name: closureNames[i], fn: closureValues[i], format })
|
|
3314
|
+
fmtEntries.push({ name: closureNames[i], fn: closureValues[i], format: formatNameOf(ctx, closureNames[i]) })
|
|
3193
3315
|
}
|
|
3194
3316
|
}
|
|
3195
3317
|
if (fmtEntries.length) boolFn._formatClosures = fmtEntries
|
|
@@ -3312,7 +3434,7 @@ function needsLocal(schema) {
|
|
|
3312
3434
|
if (schema.minLength !== undefined) checkCount++
|
|
3313
3435
|
if (schema.maxLength !== undefined) checkCount++
|
|
3314
3436
|
if (schema.pattern) checkCount++
|
|
3315
|
-
if (schema.format) checkCount++
|
|
3437
|
+
if (schema.format !== undefined) checkCount++
|
|
3316
3438
|
} else if (t === 'integer' || t === 'number') {
|
|
3317
3439
|
if (schema.minimum !== undefined) checkCount++
|
|
3318
3440
|
if (schema.maximum !== undefined) checkCount++
|
|
@@ -3345,26 +3467,26 @@ function tryGenCombined(schema, access, ctx) {
|
|
|
3345
3467
|
: `{const _v=${access};if(${conds.join('||')})return false}`
|
|
3346
3468
|
|
|
3347
3469
|
if (t === 'string') {
|
|
3348
|
-
if (schema.pattern || schema.format) return null
|
|
3470
|
+
if (schema.pattern || schema.format !== undefined) return null
|
|
3349
3471
|
// s.length is an upper bound on cpLen and at least cpLen / 2 (worst case
|
|
3350
3472
|
// all-surrogate). Use s.length fast paths and only call _cpLen in the
|
|
3351
3473
|
// uncertain band; ASCII strings (>99% of real data) skip _cpLen entirely.
|
|
3352
3474
|
if (schema.minLength !== undefined && schema.maxLength !== undefined) {
|
|
3353
|
-
const M = schema.minLength
|
|
3354
|
-
const X = schema.maxLength
|
|
3475
|
+
const M = +numLit(schema.minLength)
|
|
3476
|
+
const X = +numLit(schema.maxLength)
|
|
3355
3477
|
const v2 = isIdent ? access : '_v'
|
|
3356
3478
|
const prelude = isIdent ? '' : `const _v=${access};`
|
|
3357
3479
|
return `{${prelude}if(typeof ${v2}!=='string')return false;const _lv=${v2}.length;if(_lv<${M}||_lv>${X * 2})return false;if(_lv<${M * 2}||_lv>${X}){const _cp=_cpLen(${v2});if(_cp<${M}||_cp>${X})return false}}`
|
|
3358
3480
|
}
|
|
3359
3481
|
const conds = [`typeof _v!=='string'`]
|
|
3360
3482
|
if (schema.minLength !== undefined && schema.minLength > 0) {
|
|
3361
|
-
const M = schema.minLength
|
|
3483
|
+
const M = +numLit(schema.minLength)
|
|
3362
3484
|
conds.push(`_v.length<${M}`)
|
|
3363
3485
|
// For M=1, length<1 already catches all failures (any non-empty string has cpLen>=1).
|
|
3364
3486
|
if (M > 1) conds.push(`_v.length<${M * 2}&&_cpLen(_v)<${M}`)
|
|
3365
3487
|
}
|
|
3366
3488
|
if (schema.maxLength !== undefined) {
|
|
3367
|
-
const X = schema.maxLength
|
|
3489
|
+
const X = +numLit(schema.maxLength)
|
|
3368
3490
|
// For X=0, only empty string passes, length>0 fails — no cpLen needed.
|
|
3369
3491
|
if (X === 0) {
|
|
3370
3492
|
conds.push(`_v.length>0`)
|
|
@@ -3379,10 +3501,10 @@ function tryGenCombined(schema, access, ctx) {
|
|
|
3379
3501
|
|
|
3380
3502
|
if (t === 'integer') {
|
|
3381
3503
|
const conds = [`!Number.isInteger(_v)`]
|
|
3382
|
-
if (schema.minimum !== undefined) conds.push(`_v<${schema.minimum}`)
|
|
3383
|
-
if (schema.maximum !== undefined) conds.push(`_v>${schema.maximum}`)
|
|
3384
|
-
if (schema.exclusiveMinimum !== undefined) conds.push(`_v<=${schema.exclusiveMinimum}`)
|
|
3385
|
-
if (schema.exclusiveMaximum !== undefined) conds.push(`_v>=${schema.exclusiveMaximum}`)
|
|
3504
|
+
if (schema.minimum !== undefined) conds.push(`_v<${numLit(schema.minimum)}`)
|
|
3505
|
+
if (schema.maximum !== undefined) conds.push(`_v>${numLit(schema.maximum)}`)
|
|
3506
|
+
if (schema.exclusiveMinimum !== undefined) conds.push(`_v<=${numLit(schema.exclusiveMinimum)}`)
|
|
3507
|
+
if (schema.exclusiveMaximum !== undefined) conds.push(`_v>=${numLit(schema.exclusiveMaximum)}`)
|
|
3386
3508
|
if (schema.multipleOf !== undefined) conds.push(`(${multipleOfBad('_v', schema.multipleOf)})`)
|
|
3387
3509
|
if (conds.length < 2) return null
|
|
3388
3510
|
return bind(conds)
|
|
@@ -3390,10 +3512,10 @@ function tryGenCombined(schema, access, ctx) {
|
|
|
3390
3512
|
|
|
3391
3513
|
if (t === 'number') {
|
|
3392
3514
|
const conds = [`!Number.isFinite(_v)`]
|
|
3393
|
-
if (schema.minimum !== undefined) conds.push(`_v<${schema.minimum}`)
|
|
3394
|
-
if (schema.maximum !== undefined) conds.push(`_v>${schema.maximum}`)
|
|
3395
|
-
if (schema.exclusiveMinimum !== undefined) conds.push(`_v<=${schema.exclusiveMinimum}`)
|
|
3396
|
-
if (schema.exclusiveMaximum !== undefined) conds.push(`_v>=${schema.exclusiveMaximum}`)
|
|
3515
|
+
if (schema.minimum !== undefined) conds.push(`_v<${numLit(schema.minimum)}`)
|
|
3516
|
+
if (schema.maximum !== undefined) conds.push(`_v>${numLit(schema.maximum)}`)
|
|
3517
|
+
if (schema.exclusiveMinimum !== undefined) conds.push(`_v<=${numLit(schema.exclusiveMinimum)}`)
|
|
3518
|
+
if (schema.exclusiveMaximum !== undefined) conds.push(`_v>=${numLit(schema.exclusiveMaximum)}`)
|
|
3397
3519
|
if (schema.multipleOf !== undefined) conds.push(`(${multipleOfBad('_v', schema.multipleOf)})`)
|
|
3398
3520
|
if (conds.length < 2) return null
|
|
3399
3521
|
return bind(conds)
|
|
@@ -3575,7 +3697,11 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3575
3697
|
const types = schema.type ? (Array.isArray(schema.type) ? schema.type : [schema.type]) : null
|
|
3576
3698
|
let effectiveType = knownType
|
|
3577
3699
|
if (types) {
|
|
3578
|
-
|
|
3700
|
+
// The parent's proven type stands in for this node's check only when it
|
|
3701
|
+
// implies it. A narrower or different type (`type: number` over `allOf:
|
|
3702
|
+
// [{type: integer}]`, a dependentSchemas branch of another type) keeps
|
|
3703
|
+
// its own check: it used to be dropped, and 1.5 passed as an integer.
|
|
3704
|
+
if (!knownType || !typeImplied(knownType, types)) {
|
|
3579
3705
|
// Emit the type check — use direct negation for single types (avoids !() wrapper)
|
|
3580
3706
|
if (types.length === 1) {
|
|
3581
3707
|
switch (types[0]) {
|
|
@@ -3708,13 +3834,12 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3708
3834
|
// and the interpreted engine reads it the same way. With `type: number` the
|
|
3709
3835
|
// type check has already turned it away, so the guard is only needed here.
|
|
3710
3836
|
const numGuard = isNum ? '' : `Number.isFinite(${v})&&`
|
|
3711
|
-
if (schema.minimum !== undefined) lines.push(`if(${numGuard}${v}<${schema.minimum})return false`)
|
|
3712
|
-
if (schema.maximum !== undefined) lines.push(`if(${numGuard}${v}>${schema.maximum})return false`)
|
|
3713
|
-
if (schema.exclusiveMinimum !== undefined) lines.push(`if(${numGuard}${v}<=${schema.exclusiveMinimum})return false`)
|
|
3714
|
-
if (schema.exclusiveMaximum !== undefined) lines.push(`if(${numGuard}${v}>=${schema.exclusiveMaximum})return false`)
|
|
3837
|
+
if (schema.minimum !== undefined) lines.push(`if(${numGuard}${v}<${numLit(schema.minimum)})return false`)
|
|
3838
|
+
if (schema.maximum !== undefined) lines.push(`if(${numGuard}${v}>${numLit(schema.maximum)})return false`)
|
|
3839
|
+
if (schema.exclusiveMinimum !== undefined) lines.push(`if(${numGuard}${v}<=${numLit(schema.exclusiveMinimum)})return false`)
|
|
3840
|
+
if (schema.exclusiveMaximum !== undefined) lines.push(`if(${numGuard}${v}>=${numLit(schema.exclusiveMaximum)})return false`)
|
|
3715
3841
|
if (schema.multipleOf !== undefined) {
|
|
3716
|
-
const
|
|
3717
|
-
const bad = `(${multipleOfBad(v, m)})`
|
|
3842
|
+
const bad = `(${multipleOfBad(v, schema.multipleOf)})`
|
|
3718
3843
|
lines.push(`if(${numGuard}${bad})return false`)
|
|
3719
3844
|
}
|
|
3720
3845
|
|
|
@@ -3727,15 +3852,15 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3727
3852
|
// Only call _cpLen in the uncertain band. ASCII strings (>99% of real data)
|
|
3728
3853
|
// never enter the band.
|
|
3729
3854
|
if (schema.minLength !== undefined && schema.maxLength !== undefined) {
|
|
3730
|
-
const M = schema.minLength
|
|
3731
|
-
const X = schema.maxLength
|
|
3855
|
+
const M = +numLit(schema.minLength)
|
|
3856
|
+
const X = +numLit(schema.maxLength)
|
|
3732
3857
|
const li = ctx.varCounter++
|
|
3733
3858
|
const lv = `_l${li}`
|
|
3734
3859
|
const body = `{const ${lv}=${v}.length;if(${lv}<${M}||${lv}>${X * 2})return false;if(${lv}<${M * 2}||${lv}>${X}){const _cp=_cpLen(${v});if(_cp<${M}||_cp>${X})return false}}`
|
|
3735
3860
|
lines.push(isStr ? body : `if(typeof ${v}==='string')${body}`)
|
|
3736
3861
|
} else {
|
|
3737
3862
|
if (schema.minLength !== undefined && schema.minLength > 0) {
|
|
3738
|
-
const M = schema.minLength
|
|
3863
|
+
const M = +numLit(schema.minLength)
|
|
3739
3864
|
// M==1: length<1 already catches empty strings (any non-empty string has cpLen>=1).
|
|
3740
3865
|
const body = M === 1
|
|
3741
3866
|
? `if(${v}.length<1)return false`
|
|
@@ -3743,7 +3868,7 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3743
3868
|
lines.push(isStr ? body : `if(typeof ${v}==='string'){${body}}`)
|
|
3744
3869
|
}
|
|
3745
3870
|
if (schema.maxLength !== undefined) {
|
|
3746
|
-
const X = schema.maxLength
|
|
3871
|
+
const X = +numLit(schema.maxLength)
|
|
3747
3872
|
// X==0: only empty string passes, no cpLen needed.
|
|
3748
3873
|
const body = X === 0
|
|
3749
3874
|
? `if(${v}.length>0)return false`
|
|
@@ -3753,12 +3878,12 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3753
3878
|
}
|
|
3754
3879
|
|
|
3755
3880
|
// array size — skip guard if known array
|
|
3756
|
-
if (schema.minItems !== undefined) lines.push(isArr ? `if(${v}.length<${schema.minItems})return false` : `if(Array.isArray(${v})&&${v}.length<${schema.minItems})return false`)
|
|
3757
|
-
if (schema.maxItems !== undefined) lines.push(isArr ? `if(${v}.length>${schema.maxItems})return false` : `if(Array.isArray(${v})&&${v}.length>${schema.maxItems})return false`)
|
|
3881
|
+
if (schema.minItems !== undefined) lines.push(isArr ? `if(${v}.length<${numLit(schema.minItems)})return false` : `if(Array.isArray(${v})&&${v}.length<${numLit(schema.minItems)})return false`)
|
|
3882
|
+
if (schema.maxItems !== undefined) lines.push(isArr ? `if(${v}.length>${numLit(schema.maxItems)})return false` : `if(Array.isArray(${v})&&${v}.length>${numLit(schema.maxItems)})return false`)
|
|
3758
3883
|
|
|
3759
3884
|
// object size
|
|
3760
|
-
if (schema.minProperties !== undefined) lines.push(`if(${objGuard}Object.keys(${v}).length<${schema.minProperties})return false`)
|
|
3761
|
-
if (schema.maxProperties !== undefined) lines.push(`if(${objGuard}Object.keys(${v}).length>${schema.maxProperties})return false`)
|
|
3885
|
+
if (schema.minProperties !== undefined) lines.push(`if(${objGuard}Object.keys(${v}).length<${numLit(schema.minProperties)})return false`)
|
|
3886
|
+
if (schema.maxProperties !== undefined) lines.push(`if(${objGuard}Object.keys(${v}).length>${numLit(schema.maxProperties)})return false`)
|
|
3762
3887
|
|
|
3763
3888
|
if (schema.pattern) {
|
|
3764
3889
|
// Try inline charCode compilation for simple patterns (avoids RegExp engine)
|
|
@@ -3782,20 +3907,18 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3782
3907
|
}
|
|
3783
3908
|
}
|
|
3784
3909
|
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3910
|
+
// The caller's function for the format wins over the built-in check, as
|
|
3911
|
+
// the interpreted engine and tier 0 read the option; a format named ''
|
|
3912
|
+
// is a name like any other.
|
|
3913
|
+
if (typeof schema.format === 'string') {
|
|
3914
|
+
const uf = userFormatFn(ctx.userFormats, schema.format)
|
|
3915
|
+
if (uf !== null) {
|
|
3790
3916
|
// User-supplied format checker: thread the function via closure and call at runtime.
|
|
3791
|
-
const
|
|
3792
|
-
const closureName = `_uf_${safeName}`
|
|
3793
|
-
if (!ctx.closureVars.includes(closureName)) {
|
|
3794
|
-
ctx.closureVars.push(closureName)
|
|
3795
|
-
ctx.closureVals.push(ctx.userFormats[schema.format])
|
|
3796
|
-
}
|
|
3917
|
+
const closureName = bindUserFormat(ctx, schema.format, uf)
|
|
3797
3918
|
const guard = isStr ? '' : `typeof ${v}==='string'&&`
|
|
3798
3919
|
lines.push(`if(${guard}!${closureName}(${v}))return false`)
|
|
3920
|
+
} else if (Object.hasOwn(FORMAT_CODEGEN, schema.format)) {
|
|
3921
|
+
lines.push(FORMAT_CODEGEN[schema.format](v, isStr, ctx))
|
|
3799
3922
|
}
|
|
3800
3923
|
}
|
|
3801
3924
|
|
|
@@ -4144,8 +4267,8 @@ function genPropertyNamesNode(schema, v, lines, ctx, knownType, isObj) {
|
|
|
4144
4267
|
return
|
|
4145
4268
|
}
|
|
4146
4269
|
lines.push(`${guard}{${ownHead(ctx, v, `_k${ki}`, 'const')}{`)
|
|
4147
|
-
if (pn.minLength !== undefined) lines.push(`if(_k${ki}.length<${pn.minLength})return false`)
|
|
4148
|
-
if (pn.maxLength !== undefined) lines.push(`if(_k${ki}.length>${pn.maxLength})return false`)
|
|
4270
|
+
if (pn.minLength !== undefined) lines.push(`if(_k${ki}.length<${numLit(pn.minLength)})return false`)
|
|
4271
|
+
if (pn.maxLength !== undefined) lines.push(`if(_k${ki}.length>${numLit(pn.maxLength)})return false`)
|
|
4149
4272
|
if (pn.pattern) {
|
|
4150
4273
|
const fast = fastPrefixCheck(pn.pattern, `_k${ki}`)
|
|
4151
4274
|
if (fast) {
|
|
@@ -4181,8 +4304,8 @@ function genPrefixItemsNode(schema, v, lines, ctx, knownType, isArr) {
|
|
|
4181
4304
|
|
|
4182
4305
|
function genContainsNode(schema, v, lines, ctx, knownType, isArr) {
|
|
4183
4306
|
const ci = ctx.varCounter++
|
|
4184
|
-
const minC = schema.minContains !== undefined ? schema.minContains : 1
|
|
4185
|
-
const maxC = schema.maxContains !== undefined ? schema.maxContains : Infinity
|
|
4307
|
+
const minC = schema.minContains !== undefined ? numLit(schema.minContains) : 1
|
|
4308
|
+
const maxC = schema.maxContains !== undefined ? numLit(schema.maxContains) : Infinity
|
|
4186
4309
|
const subLines = []
|
|
4187
4310
|
genCode(schema.contains, `_cv`, subLines, ctx)
|
|
4188
4311
|
const fnBody = subLines.length === 0 ? `return true` : `${subLines.join(';')};return true`
|
|
@@ -4601,11 +4724,15 @@ function genRefNode(schema, v, lines, ctx, knownType, hasSiblings) {
|
|
|
4601
4724
|
ctx.refStack.add(schema.$ref)
|
|
4602
4725
|
const outerReuse = ctx.plainReuse
|
|
4603
4726
|
ctx.plainReuse = undefined
|
|
4727
|
+
// Hoisted like the cyclic ones, so a `$ref: '#'` inside reaches the
|
|
4728
|
+
// root through the forwarder rather than naming `_validate`.
|
|
4729
|
+
ctx.inDefFn = (ctx.inDefFn || 0) + 1
|
|
4604
4730
|
try {
|
|
4605
4731
|
genIsolated(ctx, () => genCode(ctx.rootDefs[defName], 'd', bodyLines, ctx))
|
|
4606
4732
|
} finally {
|
|
4607
4733
|
ctx.refStack.delete(schema.$ref)
|
|
4608
4734
|
ctx.plainReuse = outerReuse
|
|
4735
|
+
ctx.inDefFn--
|
|
4609
4736
|
}
|
|
4610
4737
|
if (ctx.deferredChecks) for (const dc of ctx.deferredChecks) bodyLines.push(dc)
|
|
4611
4738
|
ctx.deferredChecks = outerDeferred
|
|
@@ -4715,8 +4842,8 @@ function genPatternPropertiesNode(schema, v, lines, ctx, knownType, isObj) {
|
|
|
4715
4842
|
lines.push(`${guard}{${ownHead(ctx, v, kVar, 'const')}{`)
|
|
4716
4843
|
// propertyNames checks (merged into same loop)
|
|
4717
4844
|
if (pn) {
|
|
4718
|
-
if (pn.minLength !== undefined) lines.push(`if(${kVar}.length<${pn.minLength})return false`)
|
|
4719
|
-
if (pn.maxLength !== undefined) lines.push(`if(${kVar}.length>${pn.maxLength})return false`)
|
|
4845
|
+
if (pn.minLength !== undefined) lines.push(`if(${kVar}.length<${numLit(pn.minLength)})return false`)
|
|
4846
|
+
if (pn.maxLength !== undefined) lines.push(`if(${kVar}.length>${numLit(pn.maxLength)})return false`)
|
|
4720
4847
|
if (pn.pattern) {
|
|
4721
4848
|
const fast = fastPrefixCheck(pn.pattern, kVar)
|
|
4722
4849
|
if (fast) {
|
|
@@ -4773,8 +4900,8 @@ function genPatternPropertiesNode(schema, v, lines, ctx, knownType, isObj) {
|
|
|
4773
4900
|
lines.push(`${guard}{${ownHead(ctx, v, kVar, 'const')}{`)
|
|
4774
4901
|
// propertyNames checks (merged)
|
|
4775
4902
|
if (pn) {
|
|
4776
|
-
if (pn.minLength !== undefined) lines.push(`if(${kVar}.length<${pn.minLength})return false`)
|
|
4777
|
-
if (pn.maxLength !== undefined) lines.push(`if(${kVar}.length>${pn.maxLength})return false`)
|
|
4903
|
+
if (pn.minLength !== undefined) lines.push(`if(${kVar}.length<${numLit(pn.minLength)})return false`)
|
|
4904
|
+
if (pn.maxLength !== undefined) lines.push(`if(${kVar}.length>${numLit(pn.maxLength)})return false`)
|
|
4778
4905
|
if (pn.pattern) {
|
|
4779
4906
|
const fast = fastPrefixCheck(pn.pattern, kVar)
|
|
4780
4907
|
if (fast) {
|
|
@@ -5016,7 +5143,7 @@ function genUnevaluatedPropertiesNode(schema, v, lines, ctx, knownType, isObj, h
|
|
|
5016
5143
|
const bfi = ctx.varCounter++
|
|
5017
5144
|
ctx.closureVars.push(`_bk${bfi}`)
|
|
5018
5145
|
ctx.closureVals.push(branchProps)
|
|
5019
|
-
lines.push(`{const ${evVar}=
|
|
5146
|
+
lines.push(`{const ${evVar}=Object.create(null)`)
|
|
5020
5147
|
for (const k of baseProps) lines.push(`${evVar}[${JSON.stringify(k)}]=1`)
|
|
5021
5148
|
lines.push(`const _bf${bfi}=[${fns.join(',')}]`)
|
|
5022
5149
|
if (branchKeyword === 'oneOf') {
|
|
@@ -5033,7 +5160,7 @@ function genUnevaluatedPropertiesNode(schema, v, lines, ctx, knownType, isObj, h
|
|
|
5033
5160
|
// dependentSchemas: conditional merge at runtime
|
|
5034
5161
|
const ei = ctx.varCounter++
|
|
5035
5162
|
const evVar = `_ev${ei}`
|
|
5036
|
-
lines.push(`{const ${evVar}=
|
|
5163
|
+
lines.push(`{const ${evVar}=Object.create(null)`)
|
|
5037
5164
|
for (const k of baseProps) lines.push(`${evVar}[${JSON.stringify(k)}]=1`)
|
|
5038
5165
|
for (const [trigger, depSchema] of Object.entries(schema.dependentSchemas)) {
|
|
5039
5166
|
const depResult = collectEvaluated(depSchema, ctx.schemaMap, ctx.rootDefs)
|
|
@@ -5064,7 +5191,7 @@ function genUnevaluatedPropertiesNode(schema, v, lines, ctx, knownType, isObj, h
|
|
|
5064
5191
|
if (allPatterns.length > 0) {
|
|
5065
5192
|
const ei = ctx.varCounter++
|
|
5066
5193
|
const evVar = `_ev${ei}`
|
|
5067
|
-
lines.push(`{const ${evVar}=
|
|
5194
|
+
lines.push(`{const ${evVar}=Object.create(null)`)
|
|
5068
5195
|
for (const k of baseProps) lines.push(`${evVar}[${JSON.stringify(k)}]=1`)
|
|
5069
5196
|
const reVars = []
|
|
5070
5197
|
for (const pat of allPatterns) {
|
|
@@ -5113,7 +5240,7 @@ function genUnevaluatedPropertiesNode(schema, v, lines, ctx, knownType, isObj, h
|
|
|
5113
5240
|
// used to emit nothing, and nothing read as valid.
|
|
5114
5241
|
const ei = ctx.varCounter++
|
|
5115
5242
|
const evVar = `_ev${ei}`
|
|
5116
|
-
lines.push(`{const ${evVar}=
|
|
5243
|
+
lines.push(`{const ${evVar}=Object.create(null)`)
|
|
5117
5244
|
for (const k of baseProps) lines.push(`${evVar}[${JSON.stringify(k)}]=1`)
|
|
5118
5245
|
const inner = `for(var _k in ${v}){if(!${evVar}[_k]&&${ownFail(ctx, v)})return false}`
|
|
5119
5246
|
_deferOrInline(ctx, lines, v, isObj ? inner + '}' : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){${inner}}}`)
|
|
@@ -5124,7 +5251,7 @@ function genUnevaluatedPropertiesNode(schema, v, lines, ctx, knownType, isObj, h
|
|
|
5124
5251
|
const ei = ctx.varCounter++
|
|
5125
5252
|
const evVar = `_ev${ei}`
|
|
5126
5253
|
const ukVar = `_uk${ei}`
|
|
5127
|
-
lines.push(`{const ${evVar}=
|
|
5254
|
+
lines.push(`{const ${evVar}=Object.create(null)`)
|
|
5128
5255
|
for (const k of baseProps) lines.push(`${evVar}[${JSON.stringify(k)}]=1`)
|
|
5129
5256
|
|
|
5130
5257
|
if (branchKeyword) {
|
|
@@ -5346,7 +5473,10 @@ const emailHelper = () => `function _em(_s){${getFormats().emailSource('_s', tru
|
|
|
5346
5473
|
// each call site, where the tables would have to be rebuilt.
|
|
5347
5474
|
const uriHelper = () => getFormats().uriHelperSource('_uri')
|
|
5348
5475
|
|
|
5349
|
-
|
|
5476
|
+
// A table with no prototype, read with Object.hasOwn: `format: 'constructor'`
|
|
5477
|
+
// used to find Object here and `format: 'hasOwnProperty'` threw while
|
|
5478
|
+
// compiling.
|
|
5479
|
+
const FORMAT_CODEGEN = Object.assign(Object.create(null), {
|
|
5350
5480
|
email: (v, isStr, ctx) => {
|
|
5351
5481
|
if (!ctx) return getFormats().emailSource(v, isStr)
|
|
5352
5482
|
hoistOnce(ctx, '_emHoisted', emailHelper())
|
|
@@ -5374,7 +5504,11 @@ const FORMAT_CODEGEN = {
|
|
|
5374
5504
|
return isStr ? `{${inner}}` : `if(typeof ${v}==='string'){${inner}}`
|
|
5375
5505
|
},
|
|
5376
5506
|
date: (...a) => getFormats().dateSource(...a),
|
|
5377
|
-
uuid: (
|
|
5507
|
+
uuid: (v, isStr, ctx) => {
|
|
5508
|
+
if (!ctx) return getFormats().uuidSource(v, isStr)
|
|
5509
|
+
hoistOnce(ctx, '_uuHoisted', getFormats().uuidHelperSource('_uu'))
|
|
5510
|
+
return isStr ? `if(!_uu(${v}))return false` : `if(typeof ${v}==='string'&&!_uu(${v}))return false`
|
|
5511
|
+
},
|
|
5378
5512
|
'date-time': (...a) => getFormats().dateTimeSource(...a),
|
|
5379
5513
|
time: (...a) => getFormats().timeSource(...a),
|
|
5380
5514
|
duration: (...a) => getFormats().durationSource(...a),
|
|
@@ -5403,7 +5537,7 @@ const FORMAT_CODEGEN = {
|
|
|
5403
5537
|
return isStr ? body : `if(typeof ${v}==='string'){${body}}`
|
|
5404
5538
|
},
|
|
5405
5539
|
hostname: (...a) => getFormats().hostnameSource(...a),
|
|
5406
|
-
}
|
|
5540
|
+
})
|
|
5407
5541
|
|
|
5408
5542
|
// `^[class]+$`, `^[class]{m,n}$` and `^[class]{n}$` for a class of ASCII
|
|
5409
5543
|
// letters, digits, `_` and `-`: a function in the preamble that reads each
|
|
@@ -5525,6 +5659,10 @@ function fixedSequenceInline(pattern, varName) {
|
|
|
5525
5659
|
const q = /^\{(\d+)\}/.exec(pattern.slice(i))
|
|
5526
5660
|
if (!q) return null
|
|
5527
5661
|
count = Number(q[1])
|
|
5662
|
+
// Checked against the limit before the pieces are pushed: a{100000000}
|
|
5663
|
+
// pushed a hundred million entries first, and a{2000000000} threw a
|
|
5664
|
+
// RangeError out of validate().
|
|
5665
|
+
if (!Number.isSafeInteger(count) || count > 32 - pieces.length) return null
|
|
5528
5666
|
i += q[0].length
|
|
5529
5667
|
} else if (i < end && '*+?'.includes(pattern[i])) {
|
|
5530
5668
|
return null
|
|
@@ -5631,8 +5769,10 @@ function fastPrefixCheck(pattern, keyVar) {
|
|
|
5631
5769
|
if (alts.length <= 8) return `(${alts.map((a) => `${keyVar}===${JSON.stringify(a)}`).join('||')})`
|
|
5632
5770
|
}
|
|
5633
5771
|
if (pattern === '.*') return 'true'
|
|
5634
|
-
// Match patterns like ^literal (no regex metacharacters after ^)
|
|
5635
|
-
|
|
5772
|
+
// Match patterns like ^literal (no regex metacharacters after ^). A dot is
|
|
5773
|
+
// a wildcard, so it is not literal: `^a.b` read as the prefix 'a.b' let
|
|
5774
|
+
// 'axb' skip its value schema.
|
|
5775
|
+
const m = pattern.match(/^\^([a-zA-Z0-9_\-/]+)$/)
|
|
5636
5776
|
if (!m) return null
|
|
5637
5777
|
const prefix = m[1]
|
|
5638
5778
|
if (prefix.length === 0 || prefix.length > 8) return null // too long = diminishing returns
|
|
@@ -6121,4 +6261,4 @@ Object.defineProperty(module.exports, '_expandDynamicScopes', { value: expandDyn
|
|
|
6121
6261
|
// For tests/test_custom_keyword_codegen.js, which holds these copies to the interpreter's.
|
|
6122
6262
|
Object.defineProperty(module.exports, '_kwTypeBits', { value: { kwTypeBit, kwDataBits, KW_T_ANY }, enumerable: false })
|
|
6123
6263
|
|
|
6124
|
-
module.exports._internal = { 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 }
|
|
6264
|
+
module.exports._internal = { 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, hasNonNumericBound, patternEngineOk, ufName, userFormatFn, bindUserFormat, formatNameOf }
|
package/lib/json-rejections.js
CHANGED
|
@@ -97,7 +97,7 @@ Object.defineProperty(LazyJsonRejection.prototype, 'errors', {
|
|
|
97
97
|
if (e && !e.dataFrame) {
|
|
98
98
|
const path = e.path != null ? e.path : (e.instancePath || '');
|
|
99
99
|
const p = positions[path];
|
|
100
|
-
if (p) e.dataFrame = { byteOffset: p.byteOffset, length: p.length, line: p.line, col: p.col, text: p.text };
|
|
100
|
+
if (p) { e.dataFrame = { byteOffset: p.byteOffset, length: p.length, line: p.line, col: p.col, text: p.text }; if (p.textCol !== undefined) e.dataFrame.textCol = p.textCol; }
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
self._posCache.reset();
|
package/lib/migrate.js
CHANGED
|
@@ -68,10 +68,29 @@ function mask (src) {
|
|
|
68
68
|
return out.join('');
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// The line of `index` in `src`. The callers ask for ascending indexes of one
|
|
72
|
+
// file, so the count continues from the last answer rather than from the
|
|
73
|
+
// start: counted from the start for each match, 40000 requires took 16 s.
|
|
74
|
+
let lineSrc = null;
|
|
75
|
+
let lineIdx = 0;
|
|
76
|
+
let lineNo = 1;
|
|
71
77
|
function lineOf (src, index) {
|
|
72
|
-
|
|
73
|
-
for (let i =
|
|
74
|
-
|
|
78
|
+
if (src !== lineSrc || index < lineIdx) { lineSrc = src; lineIdx = 0; lineNo = 1; }
|
|
79
|
+
for (let i = lineIdx; i < index; i++) if (src.charCodeAt(i) === 10) lineNo++;
|
|
80
|
+
lineIdx = index;
|
|
81
|
+
return lineNo;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// A file name or a source line as the report prints it: control characters
|
|
85
|
+
// and the Unicode line separators written as escapes, so a name cannot
|
|
86
|
+
// forge a line or a colour on the terminal.
|
|
87
|
+
function plain (s) {
|
|
88
|
+
return String(s).replace(/[\x00-\x1f\x7f\u2028\u2029]/g, (c) => {
|
|
89
|
+
if (c === '\n') return '\\n';
|
|
90
|
+
if (c === '\r') return '\\r';
|
|
91
|
+
if (c === '\t') return '\\t';
|
|
92
|
+
return '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0');
|
|
93
|
+
});
|
|
75
94
|
}
|
|
76
95
|
|
|
77
96
|
// Every module specifier in `src`: the string following `from`, inside
|
|
@@ -238,8 +257,8 @@ function formatReport (result, opts = {}) {
|
|
|
238
257
|
}
|
|
239
258
|
const label = { rewrite: 'rewrite', delete: 'delete ', flag: 'flag ' };
|
|
240
259
|
for (const r of result.report) {
|
|
241
|
-
lines.push(r.file);
|
|
242
|
-
for (const f of r.findings) lines.push(` ${label[f.kind]} line ${String(f.line).padStart(4)} ${f.text}`);
|
|
260
|
+
lines.push(plain(r.file));
|
|
261
|
+
for (const f of r.findings) lines.push(` ${label[f.kind]} line ${String(f.line).padStart(4)} ${plain(f.text)}`);
|
|
243
262
|
}
|
|
244
263
|
const rewrites = result.report.reduce((n, r) => n + r.findings.filter((f) => f.kind !== 'flag').length, 0);
|
|
245
264
|
lines.push('');
|