ata-validator 1.63.1 → 1.63.3
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/bin/ata.js +8 -4
- package/lib/branch-collapse.js +16 -8
- package/lib/draft7.js +14 -1
- package/lib/ir/emit.mjs +19 -5
- package/lib/js-compiler-errors.js +31 -13
- package/lib/js-compiler.js +44 -16
- package/lib/kernels/collapse.mjs +16 -8
- package/lib/regex-linear.js +40 -2
- package/lib/scan-compiler.js +6 -0
- package/lib/schema-scan.js +3 -2
- package/lib/unique-items-source.js +3 -0
- package/lib/version.js +1 -1
- package/package.json +8 -8
package/bin/ata.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
const { env } = require('../lib/env');
|
|
4
|
+
// Text that comes from a schema file (a $ref, a keyword name in a warning) is
|
|
5
|
+
// written with its control characters as escapes, as `ata validate` writes
|
|
6
|
+
// it: a schema could otherwise clear the screen or forge an `ata:` line.
|
|
7
|
+
const term = (t) => require('../lib/diagnose').term(String(t));
|
|
4
8
|
|
|
5
9
|
const fs = require('fs');
|
|
6
10
|
const path = require('path');
|
|
@@ -299,7 +303,7 @@ function cmdCompile(args) {
|
|
|
299
303
|
for (const w of compileWarnings) reportCompileError(input, w);
|
|
300
304
|
process.exit(1);
|
|
301
305
|
}
|
|
302
|
-
for (const w of compileWarnings) process.stderr.write(`ata: warning: ${input}: ${w}\n`);
|
|
306
|
+
for (const w of compileWarnings) process.stderr.write(`ata: warning: ${input}: ${term(w)}\n`);
|
|
303
307
|
}
|
|
304
308
|
if (!src) {
|
|
305
309
|
reportCompileError(input, 'schema is too complex for standalone compilation');
|
|
@@ -397,7 +401,7 @@ function cmdBuild(args) {
|
|
|
397
401
|
for (const c of report.compiled) {
|
|
398
402
|
process.stdout.write(`ata: ${c.input} -> ${c.output} (${c.bytes.toLocaleString()} bytes${c.errors === 'compact' ? ', shared errors' : c.errors === 'schema' ? ', schema module' : ''})\n`);
|
|
399
403
|
for (const t of (report.bunTargets || []).filter((t) => t.input === c.input)) process.stdout.write(`ata: bun target -> ${t.output} (for bun build --compile --aot)\n`);
|
|
400
|
-
for (const d of (report.bunDeclined || []).filter((d) => d.input === c.input)) process.stderr.write(`ata: warning: ${d.input}: ${d.reason}\n`);
|
|
404
|
+
for (const d of (report.bunDeclined || []).filter((d) => d.input === c.input)) process.stderr.write(`ata: warning: ${d.input}: ${term(d.reason)}\n`);
|
|
401
405
|
}
|
|
402
406
|
if (report.runtime) {
|
|
403
407
|
process.stdout.write(`ata: error runtime -> ${report.runtime.output} (${report.runtime.bytes.toLocaleString()} bytes, shared by the modules above)\n`);
|
|
@@ -412,7 +416,7 @@ function cmdBuild(args) {
|
|
|
412
416
|
noDetail++;
|
|
413
417
|
process.stderr.write(`ata: warning ${w.input}: no error detail, failures report the single ATA9000 error (the verdict is exact)\n`);
|
|
414
418
|
} else {
|
|
415
|
-
process.stderr.write(`ata: warning ${w.input}: ${w.warning}\n`);
|
|
419
|
+
process.stderr.write(`ata: warning ${w.input}: ${term(w.warning)}\n`);
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
if (noDetail > 0 && args.opts.mode !== 'schema') {
|
|
@@ -422,7 +426,7 @@ function cmdBuild(args) {
|
|
|
422
426
|
process.stdout.write(`ata: cached ${c.input}\n`);
|
|
423
427
|
}
|
|
424
428
|
for (const s of report.skipped) {
|
|
425
|
-
process.stdout.write(`ata: skipped ${s.input}: ${s.reason}\n`);
|
|
429
|
+
process.stdout.write(`ata: skipped ${s.input}: ${term(s.reason)}\n`);
|
|
426
430
|
}
|
|
427
431
|
for (const f of report.failed) {
|
|
428
432
|
reportCompileError(f.input, f.error);
|
package/lib/branch-collapse.js
CHANGED
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
// module imports nothing, so the generated code cannot require this file; it
|
|
10
10
|
// carries a copy emitted by `embedSource()` from these very functions instead
|
|
11
11
|
// of a second hand-written one. That is why the internals are written in the
|
|
12
|
-
// positional form the generated code calls, and why
|
|
13
|
-
//
|
|
14
|
-
//
|
|
12
|
+
// positional form the generated code calls, and why each function reaches the
|
|
13
|
+
// others only through properties on itself (`__ataCollapse.F`, `.M`,
|
|
14
|
+
// `__ataScore.S`): a bundler that minifies names renames a function and every
|
|
15
|
+
// call to it, and the text toString() gives then called names the embed did
|
|
16
|
+
// not declare. A function's reference to its own name stays consistent, and
|
|
17
|
+
// property names are not renamed.
|
|
15
18
|
|
|
16
19
|
// How much a failing keyword says about intent. A branch that fails on `type`
|
|
17
20
|
// was probably not the branch the author meant; one that fails on `minLength`
|
|
@@ -35,10 +38,12 @@ const __ATA_SEVERITY = {
|
|
|
35
38
|
|
|
36
39
|
function __ataScore (errs) {
|
|
37
40
|
if (!errs || !errs.length) return 0;
|
|
41
|
+
const sev = __ataScore.S;
|
|
38
42
|
let s = 0;
|
|
39
|
-
for (const e of errs) s +=
|
|
43
|
+
for (const e of errs) s += sev[e.keyword] || 4;
|
|
40
44
|
return errs.length * 100 + s; // primary: count, secondary: severity
|
|
41
45
|
}
|
|
46
|
+
__ataScore.S = __ATA_SEVERITY;
|
|
42
47
|
|
|
43
48
|
// `br` is one entry per branch in declaration order: {valid, errors} and a
|
|
44
49
|
// `title`, or the titles in `titles` when given (the generated code passes
|
|
@@ -68,7 +73,7 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
|
|
|
68
73
|
if (pass.length > 1) {
|
|
69
74
|
const passIdx = [];
|
|
70
75
|
for (let i = 0; i < br.length; i++) if (br[i].valid) passIdx.push(i);
|
|
71
|
-
return
|
|
76
|
+
return __ataCollapse.M(passIdx, br.length, pp, sp, o);
|
|
72
77
|
}
|
|
73
78
|
} else if (pass.length >= 1) {
|
|
74
79
|
return null;
|
|
@@ -76,7 +81,7 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
|
|
|
76
81
|
let bi = 0;
|
|
77
82
|
let bs = Infinity;
|
|
78
83
|
for (let i = 0; i < br.length; i++) {
|
|
79
|
-
const s =
|
|
84
|
+
const s = __ataCollapse.F(br[i].errors);
|
|
80
85
|
if (s < bs) { bs = s; bi = i; }
|
|
81
86
|
}
|
|
82
87
|
const best = br[bi];
|
|
@@ -97,6 +102,9 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
|
|
|
97
102
|
return none;
|
|
98
103
|
}
|
|
99
104
|
|
|
105
|
+
__ataCollapse.M = __ataMulti;
|
|
106
|
+
__ataCollapse.F = __ataScore;
|
|
107
|
+
|
|
100
108
|
// The named-argument form the interpreter and the tests call.
|
|
101
109
|
function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPath, ordinal }) {
|
|
102
110
|
return __ataCollapse(keyword, branchResults, parentPath, parentSchemaPath, ordinal === undefined ? null : ordinal);
|
|
@@ -107,9 +115,9 @@ function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPat
|
|
|
107
115
|
// cannot miss the other. `tests/test_branch_collapse.js` runs both.
|
|
108
116
|
function embedSource () {
|
|
109
117
|
return 'const __ATA_SEVERITY=' + JSON.stringify(__ATA_SEVERITY) + ';' +
|
|
110
|
-
'const __ataScore=' + __ataScore.toString() + ';' +
|
|
118
|
+
'const __ataScore=' + __ataScore.toString() + ';__ataScore.S=__ATA_SEVERITY;' +
|
|
111
119
|
'const __ataMulti=' + __ataMulti.toString() + ';' +
|
|
112
|
-
'const __ataCollapse=' + __ataCollapse.toString() + ';';
|
|
120
|
+
'const __ataCollapse=' + __ataCollapse.toString() + ';__ataCollapse.M=__ataMulti;__ataCollapse.F=__ataScore;';
|
|
113
121
|
}
|
|
114
122
|
|
|
115
123
|
module.exports = { collapseBranches, scoreBranch: __ataScore, SEVERITY: __ATA_SEVERITY, embedSource, _collapse: __ataCollapse, _multi: __ataMulti };
|
package/lib/draft7.js
CHANGED
|
@@ -20,6 +20,7 @@ function isDraft6(schema) {
|
|
|
20
20
|
// of its own, which is how a retrieved schema inherits the root's draft.
|
|
21
21
|
// `draft6` says the document, or the root it inherits from, is draft-06.
|
|
22
22
|
// `draft4`: draft-04, read as draft-06 plus its own differences (below).
|
|
23
|
+
const PLAIN_NAME_ID = /^#[A-Za-z][A-Za-z0-9_.:-]*$/
|
|
23
24
|
function normalizeDraft7(schema, force, draft6, draft4) {
|
|
24
25
|
if (!force && !isDraft7(schema)) return schema
|
|
25
26
|
const d4 = draft4 === true || (typeof schema.$schema === 'string' && DRAFT4_SCHEMAS.has(schema.$schema))
|
|
@@ -57,7 +58,7 @@ function _normalize(schema, d6, d4) {
|
|
|
57
58
|
|
|
58
59
|
// A fragment-only `$id` is a plain-name anchor in draft-07; 2020-12 spells
|
|
59
60
|
// it `$anchor`, which every engine already resolves.
|
|
60
|
-
if (typeof schema.$id === 'string' &&
|
|
61
|
+
if (typeof schema.$id === 'string' && PLAIN_NAME_ID.test(schema.$id)) {
|
|
61
62
|
if (schema.$anchor === undefined) schema.$anchor = schema.$id.slice(1)
|
|
62
63
|
delete schema.$id
|
|
63
64
|
}
|
|
@@ -214,6 +215,18 @@ function _normalizeNullable(schema, st) {
|
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
if ('nullable' in schema) delete schema.nullable
|
|
218
|
+
// A fragment-only `$id` (`#name`) is draft-07's spelling of an anchor, and
|
|
219
|
+
// schemas that declare no dialect are written with it too. normalizeDraft7
|
|
220
|
+
// folds it into `$anchor` under a declared older dialect; without one each
|
|
221
|
+
// engine read it its own way: the code generators took it as an anchor on
|
|
222
|
+
// the root and the root's definitions only, and where such a schema
|
|
223
|
+
// referred to itself they wrote nothing at the cycle, so every constraint
|
|
224
|
+
// past the first level was dropped and the document accepted. Folded here,
|
|
225
|
+
// on the walk every dialect runs, there is one spelling for every engine.
|
|
226
|
+
if (typeof schema.$id === 'string' && PLAIN_NAME_ID.test(schema.$id)) {
|
|
227
|
+
if (schema.$anchor === undefined) schema.$anchor = schema.$id.slice(1)
|
|
228
|
+
delete schema.$id
|
|
229
|
+
}
|
|
217
230
|
// A JSON Pointer in a URI fragment is percent-encoded, so a definition named
|
|
218
231
|
// `Node<Row>` is reached by `#/definitions/Node%3CRow%3E` (RFC 6901, section
|
|
219
232
|
// 6). The interpreter decodes the fragment; the code generator compared the
|
package/lib/ir/emit.mjs
CHANGED
|
@@ -16,8 +16,16 @@ const FAST = {
|
|
|
16
16
|
pattern: (n, x) => compilePatternInline(n, x),
|
|
17
17
|
// A string has between length/2 and length code points, so the length
|
|
18
18
|
// alone settles most cases and the count runs only near the bound.
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// The bound is written into the module as a number, so it has to be one: a
|
|
20
|
+
// schema that gave a string here had it written as source, and what the
|
|
21
|
+
// string said ran in whatever imported the module. Refused, as the closure
|
|
22
|
+
// writer refuses it.
|
|
23
|
+
minLength: (n, x) => { const b = bound(n, 'minLength'); return `${x}.length>=${2 * b}||${x}.length>=${b}&&cpLen(${x})>=${b}` },
|
|
24
|
+
maxLength: (n, x) => { const b = bound(n, 'maxLength'); return `${x}.length<=${b}||${x}.length<=${2 * b}&&cpLen(${x})<=${b}` },
|
|
25
|
+
}
|
|
26
|
+
function bound (n, k) {
|
|
27
|
+
if (typeof n !== 'number' || !Number.isFinite(n)) throw new Error(`ir: ${k} is not a number`)
|
|
28
|
+
return n
|
|
21
29
|
}
|
|
22
30
|
// enum and const with only primitive values need no deep equality: the
|
|
23
31
|
// value is one of them exactly when it is === one of them (JSON numbers have
|
|
@@ -40,7 +48,11 @@ function inline (fn, vExpr, nExpr) {
|
|
|
40
48
|
const src = fn.toString()
|
|
41
49
|
const m = /^\(\s*v\s*(?:,\s*n\s*)?\)\s*=>\s*([\s\S]+)$/.exec(src)
|
|
42
50
|
if (!m) throw new Error('ir: kernel ok() must be (v, n) => expression: ' + src)
|
|
43
|
-
|
|
51
|
+
// One pass, by function: the expressions carry text from the schema (a
|
|
52
|
+
// property name inside `d["..."]`), which a second pass would search for
|
|
53
|
+
// `n`, and which replace() would search for `$&` and its kin if it were
|
|
54
|
+
// given as the replacement itself.
|
|
55
|
+
return '(' + m[1].replace(/\b[vn]\b/g, (x) => x === 'v' ? vExpr : nExpr) + ')'
|
|
44
56
|
}
|
|
45
57
|
|
|
46
58
|
// How many names an object must list before a module visits the object's own
|
|
@@ -677,14 +689,16 @@ export function specialize (schema, opts = {}) {
|
|
|
677
689
|
const own = typeof opts.fillSource === 'function' && !opts.schemas ? opts.fillSource(schema) : null
|
|
678
690
|
if (typeof own === 'string') {
|
|
679
691
|
return src
|
|
680
|
-
.replace('export function isValid(d){return ', `function FILL(d){\n${own}\n}\nexport function isValid(d){FILL(d);return `)
|
|
692
|
+
.replace('export function isValid(d){return ', () => `function FILL(d){\n${own}\n}\nexport function isValid(d){FILL(d);return `)
|
|
681
693
|
.replace('export function validate(d){', 'export function validate(d){FILL(d);')
|
|
682
694
|
}
|
|
683
695
|
// The shared default pass (lib/defaults.js), built from the normalized
|
|
684
696
|
// schema at load.
|
|
685
697
|
return src
|
|
686
698
|
.replace(/^import \{([^}]*)\} from /m, (m, list) => `import {${['buildDefaultsApplier', ...list.split(',')].filter(Boolean).sort().join(',')}} from `)
|
|
687
|
-
|
|
699
|
+
// By function: a replacement given as text is searched for `$&`, `` $` ``,
|
|
700
|
+
// `$'` and `$$`, and a schema's descriptions and defaults hold them.
|
|
701
|
+
.replace('export function isValid(d){return ', () => `const FILL=buildDefaultsApplier(${JSON.stringify(ir.schema)})\nexport function isValid(d){FILL(d);return `)
|
|
688
702
|
.replace('export function validate(d){', 'export function validate(d){FILL(d);')
|
|
689
703
|
}
|
|
690
704
|
|
|
@@ -683,7 +683,7 @@ function countingBody (body) {
|
|
|
683
683
|
j++
|
|
684
684
|
}
|
|
685
685
|
if (depth !== 0) return null
|
|
686
|
-
body = body.slice(0, b) + (unknown ? 'return -1' : '(
|
|
686
|
+
body = body.slice(0, b) + (unknown ? 'return -1' : '(_scn++,_scs+=4)') + body.slice(j)
|
|
687
687
|
}
|
|
688
688
|
if (/\b(?:_defE\d|__ataCollapse|__ataMulti|_brf\d|_bsf\d)/.test(body)) return null
|
|
689
689
|
const SEV = require('./branch-collapse').SEVERITY
|
|
@@ -691,7 +691,8 @@ function countingBody (body) {
|
|
|
691
691
|
let i = 0
|
|
692
692
|
const head = 'const _e=[];'
|
|
693
693
|
if (!body.startsWith(head)) return null
|
|
694
|
-
|
|
694
|
+
// Not `_n`: the inline format checks declare it as a const.
|
|
695
|
+
out += 'let _scn=0,_scs=0;'
|
|
695
696
|
i = head.length
|
|
696
697
|
while (i < body.length) {
|
|
697
698
|
const ch = body[i]
|
|
@@ -711,13 +712,13 @@ function countingBody (body) {
|
|
|
711
712
|
// The literal may still carry its `code` (the shape pass strips it).
|
|
712
713
|
const m = /^\{(?:code:'ATA\d{4}',)?keyword:'([A-Za-z$][\w$]*)',/.exec(arg)
|
|
713
714
|
if (m === null) return null
|
|
714
|
-
out += `(
|
|
715
|
+
out += `(_scn++,_scs+=${SEV[m[1]] || 4})`
|
|
715
716
|
i = j
|
|
716
717
|
continue
|
|
717
718
|
}
|
|
718
|
-
if (body.startsWith('return{valid:false,errors:_e}', i)) { out += 'return
|
|
719
|
-
if (body.startsWith('return{valid:_e.length===0,errors:_e}', i)) { out += 'return
|
|
720
|
-
if (body.startsWith('_e.length', i)) { out += '
|
|
719
|
+
if (body.startsWith('return{valid:false,errors:_e}', i)) { out += 'return _scn*100+_scs'; i += 29; continue }
|
|
720
|
+
if (body.startsWith('return{valid:_e.length===0,errors:_e}', i)) { out += 'return _scn*100+_scs'; i += 37; continue }
|
|
721
|
+
if (body.startsWith('_e.length', i)) { out += '_scn'; i += 9; continue }
|
|
721
722
|
out += ch
|
|
722
723
|
i++
|
|
723
724
|
}
|
|
@@ -1147,11 +1148,16 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1147
1148
|
const expected = types.join(', ')
|
|
1148
1149
|
// As written: a one-element array stays an array, as the interpreted engine
|
|
1149
1150
|
// reports it.
|
|
1150
|
-
|
|
1151
|
+
// Escaped like every other schema string written into this source: a
|
|
1152
|
+
// type name is the schema author's text, and a quote in one that is not
|
|
1153
|
+
// a JSON Schema type closed the literal and ran what followed as code,
|
|
1154
|
+
// in the process that compiled the schema and in the module written
|
|
1155
|
+
// from it.
|
|
1156
|
+
const expectedParam = !Array.isArray(schema.type) ? `'${esc(String(types[0]))}'` : JSON.stringify(types)
|
|
1151
1157
|
{
|
|
1152
1158
|
const typeSp = `${schemaPrefix}/type`
|
|
1153
1159
|
const lit = buildErrorLiteral({ keyword: 'type', schemaPath: typeSp, sourceMap: ctx.sourceMap })
|
|
1154
|
-
lines.push(`if(!(${conds.join('||')})){_e.push({code:'${lit.codeStr}',keyword:'type',instancePath:${pathExpr||'""'},schemaPath:'${typeSp}'${ordinalField(ctx, `${typeSp}`)},params:{type:${expectedParam}},message:'must be ${expected}',docUrl:'${lit.docUrl}'${lit.frame}});if(!_all)return{valid:false,errors:_e}}`)
|
|
1160
|
+
lines.push(`if(!(${conds.join('||')})){_e.push({code:'${lit.codeStr}',keyword:'type',instancePath:${pathExpr||'""'},schemaPath:'${typeSp}'${ordinalField(ctx, `${typeSp}`)},params:{type:${expectedParam}},message:'must be ${esc(expected)}',docUrl:'${lit.docUrl}'${lit.frame}});if(!_all)return{valid:false,errors:_e}}`)
|
|
1155
1161
|
}
|
|
1156
1162
|
}
|
|
1157
1163
|
|
|
@@ -1270,7 +1276,11 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1270
1276
|
// let every later statement of the same check push the same error again.
|
|
1271
1277
|
if (fc) {
|
|
1272
1278
|
const ri = ctx.varCounter++
|
|
1273
|
-
|
|
1279
|
+
// By function: the pushed error carries the property's name in its
|
|
1280
|
+
// paths, and a replacement given as text is searched for `$&` and its
|
|
1281
|
+
// kin (a property named `a$&b` reported `/areturn falseb`).
|
|
1282
|
+
const fmtFail = `{${failPush};break _fmt${ri}}`
|
|
1283
|
+
const fmtCode = fc(v, isStr, ctx).replace(/return false/g, () => fmtFail)
|
|
1274
1284
|
lines.push(`_fmt${ri}:{${fmtCode}}`)
|
|
1275
1285
|
} else if (uf !== null) {
|
|
1276
1286
|
// User-supplied format checker on the error path: the same closure
|
|
@@ -2038,7 +2048,9 @@ TYPE_KEYWORDS.integer = TYPE_KEYWORDS.number
|
|
|
2038
2048
|
const NOT_VALIDATING = new Set(['$schema', '$id', '$anchor', '$comment', '$defs', 'definitions', 'title', 'description', 'default', 'examples', 'deprecated', 'readOnly', 'writeOnly', '$vocabulary'])
|
|
2039
2049
|
function typeMismatchRest(schema, types) {
|
|
2040
2050
|
const drop = new Set(['type', 'enum', 'const', 'anyOf', 'oneOf'])
|
|
2041
|
-
|
|
2051
|
+
// Own names only: a type called `constructor` or `__proto__` read
|
|
2052
|
+
// Object.prototype's member here and threw while compiling.
|
|
2053
|
+
for (const t of types) if (Object.prototype.hasOwnProperty.call(TYPE_KEYWORDS, t)) for (const k of TYPE_KEYWORDS[t]) drop.add(k)
|
|
2042
2054
|
// A built-in format is written for strings only (`typeof v==='string'&&`
|
|
2043
2055
|
// off the string path), so for a value that failed `type: string` it can
|
|
2044
2056
|
// never fire: carried here it was dead code in every module that checks one.
|
|
@@ -2435,11 +2447,16 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
2435
2447
|
const expected = types.join(', ')
|
|
2436
2448
|
// As written: a one-element array stays an array, as the interpreted engine
|
|
2437
2449
|
// reports it.
|
|
2438
|
-
|
|
2450
|
+
// Escaped like every other schema string written into this source: a
|
|
2451
|
+
// type name is the schema author's text, and a quote in one that is not
|
|
2452
|
+
// a JSON Schema type closed the literal and ran what followed as code,
|
|
2453
|
+
// in the process that compiled the schema and in the module written
|
|
2454
|
+
// from it.
|
|
2455
|
+
const expectedParam = !Array.isArray(schema.type) ? `'${esc(String(types[0]))}'` : JSON.stringify(types)
|
|
2439
2456
|
// Type check: push error but continue — wrap remaining in type-success block
|
|
2440
2457
|
const typeOk = `_tok${ctx.varCounter++}`
|
|
2441
2458
|
lines.push(`const ${typeOk}=${conds.join('||')}`)
|
|
2442
|
-
lines.push(`if(!${typeOk}){${fail('type', 'type', `{type:${expectedParam}}`, `'must be ${expected}'`)}}`)
|
|
2459
|
+
lines.push(`if(!${typeOk}){${fail('type', 'type', `{type:${expectedParam}}`, `'must be ${esc(expected)}'`)}}`)
|
|
2443
2460
|
// Subsequent optimized code runs inside if(typeOk){...}
|
|
2444
2461
|
if (types.length === 1) {
|
|
2445
2462
|
isObj = types[0] === 'object'
|
|
@@ -2551,7 +2568,8 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
2551
2568
|
// Same rule as the error codegen: one violation, one error. The first
|
|
2552
2569
|
// failing statement pushes and leaves the labeled block.
|
|
2553
2570
|
const ri = ctx.varCounter++
|
|
2554
|
-
const
|
|
2571
|
+
const fmtFail = `{${fail('format', 'format', `{format:'${esc(schema.format)}'}`, `'must match format "${esc(schema.format)}"'`)};break _fmt${ri}}`
|
|
2572
|
+
const code = fc(v, isStr, ctx).replace(/return false/g, () => fmtFail)
|
|
2555
2573
|
lines.push(`_fmt${ri}:{${code}}`)
|
|
2556
2574
|
} else if (uf !== null) {
|
|
2557
2575
|
const closureName = bindUserFormat(ctx, schema.format, uf)
|
package/lib/js-compiler.js
CHANGED
|
@@ -20,18 +20,16 @@ const seqTag = () => `/*${++_compileSeq}*/`
|
|
|
20
20
|
// forms answers a different question and costs a string per call.
|
|
21
21
|
const DEQ_HELPER = "function _deq(a,b){if(a===b)return true;if(a===null||b===null||typeof a!=='object'||typeof b!=='object')return false;var aa=Array.isArray(a);if(aa!==Array.isArray(b))return false;var i,k,x,y;if(aa){if(a.length!==b.length)return false;for(i=a.length;i--!==0;)if(!_deq(a[i],b[i]))return false;return true}var ka=Object.keys(a);if(ka.length!==Object.keys(b).length)return false;for(i=ka.length;i--!==0;){k=ka[i];if(!Object.prototype.hasOwnProperty.call(b,k))return false;x=a[k];y=b[k];if(x!==y&&(x===null||y===null||typeof x!=='object'||typeof y!=='object'))return false}for(i=ka.length;i--!==0;){k=ka[i];x=a[k];if(x!==null&&typeof x==='object'&&!_deq(x,b[k]))return false}return true}"
|
|
22
22
|
|
|
23
|
-
// uniqueItems helpers,
|
|
23
|
+
// uniqueItems helpers, the text of the functions the interpreted engine
|
|
24
24
|
// calls (lib/unique-items.js): `_uq` answers whether every item is distinct.
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
25
|
+
// The text is written at build time (lib/unique-items-source.js) rather than
|
|
26
|
+
// read with String(fn) here: in a bundle whose identifiers were minified the
|
|
27
|
+
// functions and their calls to each other had other names, and generated
|
|
28
|
+
// code that called `_uq` threw on every array. Loaded at the first schema
|
|
29
|
+
// with uniqueItems, not on every cold start.
|
|
28
30
|
let _uqSources = null
|
|
29
31
|
function uqSources () {
|
|
30
|
-
if (_uqSources === null)
|
|
31
|
-
const u = require('./unique-items')
|
|
32
|
-
const compact = (fn) => String(fn).replace(/\n\s*\/\/[^\n]*/g, '').replace(/\n\s*/g, '\n')
|
|
33
|
-
_uqSources = { uq: compact(u._uqh) + compact(u._uq), uqp: 'var _uqpI=0;' + compact(u._uqp) }
|
|
34
|
-
}
|
|
32
|
+
if (_uqSources === null) _uqSources = require('./unique-items-source')
|
|
35
33
|
return _uqSources
|
|
36
34
|
}
|
|
37
35
|
|
|
@@ -832,9 +830,9 @@ function resolveRef(ref, defs, schemaMap) {
|
|
|
832
830
|
|
|
833
831
|
function buildTypeCheck(types) {
|
|
834
832
|
if (types.length === 1) {
|
|
835
|
-
return
|
|
833
|
+
return typeCheckOf(types[0]) || (() => true)
|
|
836
834
|
}
|
|
837
|
-
const fns = types.map(
|
|
835
|
+
const fns = types.map(typeCheckOf).filter(Boolean)
|
|
838
836
|
return (d) => {
|
|
839
837
|
for (let i = 0; i < fns.length; i++) {
|
|
840
838
|
if (fns[i](d)) return true
|
|
@@ -902,6 +900,10 @@ function multipleOfBad(v, m) {
|
|
|
902
900
|
return `!(Number.isInteger(${v}/${m})||Math.abs(${v}/${m}-Math.round(${v}/${m}))<1e-9)`
|
|
903
901
|
}
|
|
904
902
|
|
|
903
|
+
// Own names only: a type called `__proto__` or `valueOf` read
|
|
904
|
+
// Object.prototype's member and was handed out as the check, and the
|
|
905
|
+
// Validator then threw on the first document (`_fast is not a function`).
|
|
906
|
+
const typeCheckOf = (t) => (Object.prototype.hasOwnProperty.call(TYPE_CHECKS, t) ? TYPE_CHECKS[t] : undefined)
|
|
905
907
|
const TYPE_CHECKS = {
|
|
906
908
|
string: (d) => typeof d === 'string',
|
|
907
909
|
number: (d) => Number.isFinite(d),
|
|
@@ -3467,7 +3469,7 @@ function tryGenCombined(schema, access, ctx) {
|
|
|
3467
3469
|
// or similar), skip the `{const _v = access}` wrapping and use it directly.
|
|
3468
3470
|
const isIdent = /^_[a-zA-Z]\w*$/.test(access)
|
|
3469
3471
|
const bind = (conds) => isIdent
|
|
3470
|
-
? `if(${conds.join('||').replace(/\b_v\b/g, access)})return false`
|
|
3472
|
+
? `if(${conds.join('||').replace(/\b_v\b/g, () => access)})return false`
|
|
3471
3473
|
: `{const _v=${access};if(${conds.join('||')})return false}`
|
|
3472
3474
|
|
|
3473
3475
|
if (t === 'string') {
|
|
@@ -4198,7 +4200,11 @@ function genDynamicRefNode(schema, v, lines, ctx, knownType) {
|
|
|
4198
4200
|
function genUniqueItemsNode(schema, v, lines, ctx, knownType, hoisted, isArr, types) {
|
|
4199
4201
|
const si = ctx.varCounter++
|
|
4200
4202
|
const itemType = schema.items && typeof schema.items === 'object' && schema.items.type
|
|
4201
|
-
|
|
4203
|
+
// `items` speaks for the elements after the tuple only: with `prefixItems`
|
|
4204
|
+
// the first positions may hold objects, and comparing those by identity
|
|
4205
|
+
// took two equal objects for different ones and accepted the array.
|
|
4206
|
+
const noTuple = !Array.isArray(schema.prefixItems) || schema.prefixItems.length === 0
|
|
4207
|
+
const isPrimItems = noTuple && (itemType === 'string' || itemType === 'number' || itemType === 'integer')
|
|
4202
4208
|
const maxItems = schema.maxItems
|
|
4203
4209
|
// Small primitive arrays (maxItems <= 16): nested loop is 6x faster than Set
|
|
4204
4210
|
// No allocation, no hash computation — just direct === comparison
|
|
@@ -4339,7 +4345,7 @@ function inlineBranchBody (body, param, label, local) {
|
|
|
4339
4345
|
else return null
|
|
4340
4346
|
if (/\breturn\b(?! false\b)/.test(core)) return null
|
|
4341
4347
|
const re = new RegExp('\\b' + param + '\\b', 'g')
|
|
4342
|
-
return core.replace(/\breturn false\b/g, 'break ' + label).replace(re, local)
|
|
4348
|
+
return core.replace(/\breturn false\b/g, () => 'break ' + label).replace(re, () => local)
|
|
4343
4349
|
}
|
|
4344
4350
|
|
|
4345
4351
|
// A single-branch anyOf or oneOf holds exactly when its branch does, so the
|
|
@@ -5169,7 +5175,8 @@ function genUnevaluatedPropertiesNode(schema, v, lines, ctx, knownType, isObj, h
|
|
|
5169
5175
|
for (const [trigger, depSchema] of Object.entries(schema.dependentSchemas)) {
|
|
5170
5176
|
const depResult = collectEvaluated(depSchema, ctx.schemaMap, ctx.rootDefs)
|
|
5171
5177
|
if (depResult.props && depResult.props.length > 0) {
|
|
5172
|
-
|
|
5178
|
+
// Asked of an object only: untyped, the document may be null.
|
|
5179
|
+
lines.push(`if(${isObj ? '' : `typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})&&`}${ownKeyExpr(ctx, v, trigger)}){${depResult.props.map(k => `${evVar}[${JSON.stringify(k)}]=1`).join(';')}}`)
|
|
5173
5180
|
}
|
|
5174
5181
|
}
|
|
5175
5182
|
const inner = `for(var _k in ${v}){if(!${evVar}[_k]&&${ownFail(ctx, v)})return false}`
|
|
@@ -5364,7 +5371,8 @@ function genUnevaluatedItemsNode(schema, v, lines, ctx, knownType, isArr) {
|
|
|
5364
5371
|
lines.push(`{let ${evVar}=${baseIdx}`)
|
|
5365
5372
|
lines.push(`const _bf${bfi}=[${fns.join(',')}]`)
|
|
5366
5373
|
const maxExprs = branchMaxIdx.map((m, i) => {
|
|
5367
|
-
|
|
5374
|
+
// An array's length only: `items` passes null too.
|
|
5375
|
+
if (branchAllItems[i]) return isArr ? `_bi===${i}?${v}.length` : `_bi===${i}?(Array.isArray(${v})?${v}.length:0)`
|
|
5368
5376
|
return `_bi===${i}?${Math.max(m, baseIdx)}`
|
|
5369
5377
|
}).join(':') + `:${baseIdx}`
|
|
5370
5378
|
if (branchKeyword === 'oneOf') {
|
|
@@ -5543,6 +5551,26 @@ const FORMAT_CODEGEN = Object.assign(Object.create(null), {
|
|
|
5543
5551
|
hostname: (...a) => getFormats().hostnameSource(...a),
|
|
5544
5552
|
})
|
|
5545
5553
|
|
|
5554
|
+
// An inline check declares locals (`_n`, `_c2`), and the generator names the
|
|
5555
|
+
// value it reads (`_c2` for a property). Where the two are one name the
|
|
5556
|
+
// check's declaration shadows its own input and throws, so that value is
|
|
5557
|
+
// renamed first. Only then; tests/test_no_throw_on_any_document.js.
|
|
5558
|
+
function declares (code, name) {
|
|
5559
|
+
for (const before of ['const ', 'let ', 'var ', ',']) {
|
|
5560
|
+
const text = before + name + '='
|
|
5561
|
+
for (let i = code.indexOf(text); i !== -1; i = code.indexOf(text, i + 1)) if (code[i + text.length] !== '=') return true
|
|
5562
|
+
}
|
|
5563
|
+
return false
|
|
5564
|
+
}
|
|
5565
|
+
for (const name of Object.keys(FORMAT_CODEGEN)) {
|
|
5566
|
+
const write = FORMAT_CODEGEN[name]
|
|
5567
|
+
FORMAT_CODEGEN[name] = (v, isStr, ctx) => {
|
|
5568
|
+
const code = write(v, isStr, ctx)
|
|
5569
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(v) || !declares(code, v)) return code
|
|
5570
|
+
return `{const _fmv=${v};${write('_fmv', isStr, ctx)}}`
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
|
|
5546
5574
|
// `^[class]+$`, `^[class]{m,n}$` and `^[class]{n}$` for a class of ASCII
|
|
5547
5575
|
// letters, digits, `_` and `-`: a function in the preamble that reads each
|
|
5548
5576
|
// character once and looks it up in a 128-entry table. The inline form below
|
package/lib/kernels/collapse.mjs
CHANGED
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
// module imports nothing, so the generated code cannot require this file; it
|
|
10
10
|
// carries a copy emitted by `embedSource()` from these very functions instead
|
|
11
11
|
// of a second hand-written one. That is why the internals are written in the
|
|
12
|
-
// positional form the generated code calls, and why
|
|
13
|
-
//
|
|
14
|
-
//
|
|
12
|
+
// positional form the generated code calls, and why each function reaches the
|
|
13
|
+
// others only through properties on itself (`__ataCollapse.F`, `.M`,
|
|
14
|
+
// `__ataScore.S`): a bundler that minifies names renames a function and every
|
|
15
|
+
// call to it, and the text toString() gives then called names the embed did
|
|
16
|
+
// not declare. A function's reference to its own name stays consistent, and
|
|
17
|
+
// property names are not renamed.
|
|
15
18
|
|
|
16
19
|
// How much a failing keyword says about intent. A branch that fails on `type`
|
|
17
20
|
// was probably not the branch the author meant; one that fails on `minLength`
|
|
@@ -35,10 +38,12 @@ const __ATA_SEVERITY = {
|
|
|
35
38
|
|
|
36
39
|
function __ataScore (errs) {
|
|
37
40
|
if (!errs || !errs.length) return 0;
|
|
41
|
+
const sev = __ataScore.S;
|
|
38
42
|
let s = 0;
|
|
39
|
-
for (const e of errs) s +=
|
|
43
|
+
for (const e of errs) s += sev[e.keyword] || 4;
|
|
40
44
|
return errs.length * 100 + s; // primary: count, secondary: severity
|
|
41
45
|
}
|
|
46
|
+
__ataScore.S = __ATA_SEVERITY;
|
|
42
47
|
|
|
43
48
|
// `br` is one entry per branch in declaration order: {valid, errors} and a
|
|
44
49
|
// `title`, or the titles in `titles` when given (the generated code passes
|
|
@@ -68,7 +73,7 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
|
|
|
68
73
|
if (pass.length > 1) {
|
|
69
74
|
const passIdx = [];
|
|
70
75
|
for (let i = 0; i < br.length; i++) if (br[i].valid) passIdx.push(i);
|
|
71
|
-
return
|
|
76
|
+
return __ataCollapse.M(passIdx, br.length, pp, sp, o);
|
|
72
77
|
}
|
|
73
78
|
} else if (pass.length >= 1) {
|
|
74
79
|
return null;
|
|
@@ -76,7 +81,7 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
|
|
|
76
81
|
let bi = 0;
|
|
77
82
|
let bs = Infinity;
|
|
78
83
|
for (let i = 0; i < br.length; i++) {
|
|
79
|
-
const s =
|
|
84
|
+
const s = __ataCollapse.F(br[i].errors);
|
|
80
85
|
if (s < bs) { bs = s; bi = i; }
|
|
81
86
|
}
|
|
82
87
|
const best = br[bi];
|
|
@@ -97,6 +102,9 @@ function __ataCollapse (kw, br, pp, sp, o, titles) {
|
|
|
97
102
|
return none;
|
|
98
103
|
}
|
|
99
104
|
|
|
105
|
+
__ataCollapse.M = __ataMulti;
|
|
106
|
+
__ataCollapse.F = __ataScore;
|
|
107
|
+
|
|
100
108
|
// The named-argument form the interpreter and the tests call.
|
|
101
109
|
function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPath, ordinal }) {
|
|
102
110
|
return __ataCollapse(keyword, branchResults, parentPath, parentSchemaPath, ordinal === undefined ? null : ordinal);
|
|
@@ -107,8 +115,8 @@ function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPat
|
|
|
107
115
|
// cannot miss the other. `tests/test_branch_collapse.js` runs both.
|
|
108
116
|
function embedSource () {
|
|
109
117
|
return 'const __ATA_SEVERITY=' + JSON.stringify(__ATA_SEVERITY) + ';' +
|
|
110
|
-
'const __ataScore=' + __ataScore.toString() + ';' +
|
|
118
|
+
'const __ataScore=' + __ataScore.toString() + ';__ataScore.S=__ATA_SEVERITY;' +
|
|
111
119
|
'const __ataMulti=' + __ataMulti.toString() + ';' +
|
|
112
|
-
'const __ataCollapse=' + __ataCollapse.toString() + ';';
|
|
120
|
+
'const __ataCollapse=' + __ataCollapse.toString() + ';__ataCollapse.M=__ataMulti;__ataCollapse.F=__ataScore;';
|
|
113
121
|
}
|
|
114
122
|
export { __ataCollapse };
|
package/lib/regex-linear.js
CHANGED
|
@@ -36,6 +36,15 @@ const { parse } = require('./safe-regex')
|
|
|
36
36
|
// holds them: every accepted pattern answers as the linear engine does and
|
|
37
37
|
// stays fast on inputs built to make a backtracking engine retry.
|
|
38
38
|
const NATIVE_ESCAPES = new Set(['d', 'D', 'w', 'W', 's', 'S', 'n', 'r', 't', 'f', 'v', '.', '\\', '/', '^', '$', '|', '?', '*', '+', '(', ')', '[', ']', '{', '}', '-', 'x', 'u'])
|
|
39
|
+
// How many ways a backtracking engine can split one string over the bounded
|
|
40
|
+
// choices of a pattern. Choices in a row multiply: `(a?){30}` is thirty
|
|
41
|
+
// choices and two to the thirtieth ways, which the engine walks through on a
|
|
42
|
+
// string that does not match. The count used to be a sum capped at 64, so
|
|
43
|
+
// that pattern and `^([a-z]?){64}$` passed as linear and a string of 17
|
|
44
|
+
// characters ran for more than half a minute. Now the product, capped where
|
|
45
|
+
// the worst case stays under a millisecond for a hundred characters.
|
|
46
|
+
const MAX_WAYS = 4096
|
|
47
|
+
// The sum of bounded choices, which both rules below also hold to.
|
|
39
48
|
const MAX_BOUNDED_CHOICES = 64
|
|
40
49
|
|
|
41
50
|
const atom = (n) => n.t === 'char' || n.t === 'class' || n.t === 'any'
|
|
@@ -52,7 +61,9 @@ function anchored (ast) {
|
|
|
52
61
|
function simpleRule (ast) {
|
|
53
62
|
// Per subtree: how many unbounded quantifiers it holds (`u`) and how many
|
|
54
63
|
// bounded choices a backtracking engine could try (`b`), or null where the
|
|
55
|
-
// shape is not provably linear.
|
|
64
|
+
// shape is not provably linear. The choices are held to a sum
|
|
65
|
+
// (MAX_BOUNDED_CHOICES: a long counted run is slow to compile and to
|
|
66
|
+
// unwind) and to a product (MAX_WAYS, waysOf below).
|
|
56
67
|
const info = (n) => {
|
|
57
68
|
switch (n.t) {
|
|
58
69
|
case 'star': case 'plus':
|
|
@@ -78,7 +89,7 @@ function simpleRule (ast) {
|
|
|
78
89
|
}
|
|
79
90
|
}
|
|
80
91
|
const r = info(ast)
|
|
81
|
-
if (r === null || r.u > 1 || r.b > MAX_BOUNDED_CHOICES) return false
|
|
92
|
+
if (r === null || r.u > 1 || r.b > MAX_BOUNDED_CHOICES || !(waysOf(ast) <= MAX_WAYS)) return false
|
|
82
93
|
return r.u === 0 || anchored(ast)
|
|
83
94
|
}
|
|
84
95
|
|
|
@@ -207,9 +218,31 @@ function deterministicRule (ast) {
|
|
|
207
218
|
for (let p = 0; p < sets.length && ok; p++) disjoint(follow[p])
|
|
208
219
|
if (!ok) return false
|
|
209
220
|
if (bounded > MAX_BOUNDED_CHOICES) return false
|
|
221
|
+
// A counted repeat of a body that can end in more than one place is not a
|
|
222
|
+
// loop to the walk above (`(a?){30}`, `(a{1,8}){8}`), and it is where the
|
|
223
|
+
// ways multiply: the same product as the simple rule holds here too.
|
|
224
|
+
if (!(waysOf(ast) <= MAX_WAYS)) return false
|
|
210
225
|
return unbounded === 0 || anchored(ast)
|
|
211
226
|
}
|
|
212
227
|
|
|
228
|
+
// The product the simple rule counts, over any shape: an unbounded
|
|
229
|
+
// quantifier adds no bounded choice of its own.
|
|
230
|
+
function waysOf (n) {
|
|
231
|
+
switch (n.t) {
|
|
232
|
+
case 'star': case 'plus': return waysOf(n.child)
|
|
233
|
+
case 'quest': return atom(n.child) ? 2 : 1 + waysOf(n.child)
|
|
234
|
+
case 'repeat': {
|
|
235
|
+
if (n.max === Infinity) return waysOf(n.child)
|
|
236
|
+
if (atom(n.child)) return n.max - n.min + 1
|
|
237
|
+
return (n.max - n.min + 1) * Math.pow(waysOf(n.child), n.max)
|
|
238
|
+
}
|
|
239
|
+
case 'group': return waysOf(n.child)
|
|
240
|
+
case 'concat': { let w = 1; for (const part of n.parts) w *= waysOf(part); return w }
|
|
241
|
+
case 'alt': { let w = 0; for (const part of n.opts) w += waysOf(part); return w }
|
|
242
|
+
default: return 1
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
213
246
|
// Two more shapes the rules above decline, both anchored with `^` and `$`
|
|
214
247
|
// around one top-level sequence.
|
|
215
248
|
//
|
|
@@ -393,6 +426,11 @@ function nativeIsLinear (src) {
|
|
|
393
426
|
}
|
|
394
427
|
let ast
|
|
395
428
|
try { ast = parse(src) } catch { return false }
|
|
429
|
+
// The product of the bounded choices holds for every rule below, so it is
|
|
430
|
+
// asked here, once: the one-choice and delimited-loop rules did not ask it,
|
|
431
|
+
// and `^(a*b)*` followed by thirty `a?` reached the platform RegExp, where
|
|
432
|
+
// each `a?` more doubles the time on a string that does not match.
|
|
433
|
+
if (!(waysOf(ast) <= MAX_WAYS)) return false
|
|
396
434
|
return simpleRule(ast) || deterministicRule(ast) || oneChoiceRule(ast) || delimitedLoopRule(ast)
|
|
397
435
|
}
|
|
398
436
|
|
package/lib/scan-compiler.js
CHANGED
|
@@ -981,6 +981,12 @@ function emitObject(schema, ctx, out, depth) {
|
|
|
981
981
|
const reqMasks = new Array(words).fill(0);
|
|
982
982
|
if (required) for (const k of required) { const x = bit.get(k); reqMasks[x.w] |= x.b; }
|
|
983
983
|
|
|
984
|
+
// Many names are dispatched on a hash of the key, and two names with one
|
|
985
|
+
// hash cannot be: the code below then took the other branch, which reads
|
|
986
|
+
// variables only the few-names form declares, and the scanner threw a
|
|
987
|
+
// ReferenceError on every document that carried a declared key. Such a
|
|
988
|
+
// schema is left to the parser.
|
|
989
|
+
if (names.length >= HASH_DISPATCH_MIN && new Set(names.map(spanHash)).size !== names.length) decline('properties:hash-collision');
|
|
984
990
|
out.push('c=s.charCodeAt(++i);');
|
|
985
991
|
const useHash = names.length >= HASH_DISPATCH_MIN;
|
|
986
992
|
if (useHash) {
|
package/lib/schema-scan.js
CHANGED
|
@@ -118,8 +118,9 @@ function needsNormalization(schema, isDraft7) {
|
|
|
118
118
|
// The boolean form of the exclusive bounds is draft-04's, and it is
|
|
119
119
|
// unambiguous wherever it appears: no later dialect gives a boolean any
|
|
120
120
|
// meaning there. Percent-encoded fragments mean the same in every dialect
|
|
121
|
-
// too.
|
|
122
|
-
|
|
121
|
+
// too. A fragment-only `$id` is folded into `$anchor` in every dialect, by
|
|
122
|
+
// the walk that folds `nullable`. None is gated on the declared dialect.
|
|
123
|
+
if (bits & (NULLABLE | EXCLUSIVE_BOOL | ENCODED_REF | ANCHOR_ID)) return true;
|
|
123
124
|
return isDraft7 ? (bits & DRAFT7_WORK) !== 0 : false;
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Generated by scripts/gen-unique-items-source.js from lib/unique-items.js. Do not edit.
|
|
3
|
+
module.exports = { uq: "function _uqh(x) {\nif (x === null) return 1;\nvar t = typeof x, h, i;\nif (t === 'boolean') return x ? 2 : 3;\nif (t === 'number') {\nif (x === 0) return 4;\nh = x | 0;\nif (h === x) return Math.imul(h, 0x9e3779b1) ^ 5;\nvar f = _uqh.f || (_uqh.f = new Float64Array(1)), w = _uqh.w || (_uqh.w = new Uint32Array(f.buffer));\nf[0] = x;\nreturn Math.imul(w[0] ^ Math.imul(w[1], 0x9e3779b1), 0x85ebca6b) ^ 5;\n}\nif (t === 'string') {\nh = 0x811c9dc5 ^ x.length;\nfor (i = 0; i < x.length; i++) h = Math.imul(h ^ x.charCodeAt(i), 16777619);\nreturn h;\n}\nif (t !== 'object') return 6;\nif (Array.isArray(x)) {\nh = 0x2545f491 ^ x.length;\nfor (i = 0; i < x.length; i++) h = Math.imul(h ^ _uqh(x[i]), 16777619);\nreturn h;\n}\nvar ks = Object.keys(x);\nh = 0x6c8e9cf5 ^ ks.length;\nfor (i = 0; i < ks.length; i++) h = (h + Math.imul(_uqh(ks[i]) ^ Math.imul(_uqh(x[ks[i]]), 0x5bd1e995), 0x27d4eb2d)) | 0;\nreturn h;\n}function _uq(a) {\nvar n = a.length, i, k;\nif (n < 2) return true;\nif (n <= 12) {\nfor (i = 1; i < n; i++) for (k = 0; k < i; k++) if (_deq(a[i], a[k])) return false;\nreturn true;\n}\nvar prim = true;\nfor (i = 0; i < n; i++) { var x = a[i]; if (x !== null && typeof x === 'object') { prim = false; break; } }\nif (prim) {\nvar s = new Set();\nfor (i = 0; i < n; i++) { if (s.has(a[i])) return false; s.add(a[i]); }\nreturn true;\n}\nvar m = new Map();\nfor (i = 0; i < n; i++) {\nvar hh = _uqh(a[i]), b = m.get(hh);\nif (b === undefined) m.set(hh, i);\nelse if (typeof b === 'number') { if (_deq(a[b], a[i])) return false; m.set(hh, [b, i]); }\nelse { for (k = 0; k < b.length; k++) if (_deq(a[b[k]], a[i])) return false; b.push(i); }\n}\nreturn true;\n}", uqp: "var _uqpI=0;function _uqp(a) {\nvar n = a.length, i, k;\nif (n < 2) return -1;\nif (n <= 12) {\nfor (i = 0; i < n - 1; i++) for (k = i + 1; k < n; k++) if (_deq(a[i], a[k])) { _uqpI = i; return k; }\nreturn -1;\n}\nvar prim = true;\nfor (i = 0; i < n; i++) { var x = a[i]; if (x !== null && typeof x === 'object') { prim = false; break; } }\nvar bi = -1, bj = -1, m = new Map();\nif (prim) {\nfor (i = 0; i < n; i++) {\nvar p = m.get(a[i]);\nif (p === undefined) m.set(a[i], i);\nelse if (p >= 0) { if (bi < 0 || p < bi) { bi = p; bj = i; } m.set(a[i], -1); }\n}\n} else {\nvar paired = null;\nfor (i = 0; i < n; i++) {\nvar hh = _uqh(a[i]), b = m.get(hh), r = -1;\nif (b === undefined) { m.set(hh, i); continue; }\nif (typeof b === 'number') { if (_deq(a[b], a[i])) r = b; else m.set(hh, [b, i]); }\nelse { for (k = 0; k < b.length; k++) if (_deq(a[b[k]], a[i])) { r = b[k]; break; } if (r < 0) b.push(i); }\nif (r >= 0) {\nif (paired === null) paired = new Set();\nif (!paired.has(r)) { paired.add(r); if (bi < 0 || r < bi) { bi = r; bj = i; } }\n}\n}\n}\nif (bi < 0) return -1;\n_uqpI = bi;\nreturn bj;\n}" };
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.63.
|
|
3
|
+
"version": "1.63.3",
|
|
4
4
|
"description": "JSON Schema validator that compiles to plain JavaScript, still runs where code generation is blocked, and can be compiled away at build time. Passes the full official test suite.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "index.js",
|
|
@@ -266,13 +266,13 @@
|
|
|
266
266
|
"codegen.d.ts"
|
|
267
267
|
],
|
|
268
268
|
"optionalDependencies": {
|
|
269
|
-
"@ata-validator/native-darwin-arm64": "1.63.
|
|
270
|
-
"@ata-validator/native-darwin-x64": "1.63.
|
|
271
|
-
"@ata-validator/native-linux-arm64-gnu": "1.63.
|
|
272
|
-
"@ata-validator/native-linux-arm64-musl": "1.63.
|
|
273
|
-
"@ata-validator/native-linux-x64-gnu": "1.63.
|
|
274
|
-
"@ata-validator/native-linux-x64-musl": "1.63.
|
|
275
|
-
"@ata-validator/native-win32-x64": "1.63.
|
|
269
|
+
"@ata-validator/native-darwin-arm64": "1.63.3",
|
|
270
|
+
"@ata-validator/native-darwin-x64": "1.63.3",
|
|
271
|
+
"@ata-validator/native-linux-arm64-gnu": "1.63.3",
|
|
272
|
+
"@ata-validator/native-linux-arm64-musl": "1.63.3",
|
|
273
|
+
"@ata-validator/native-linux-x64-gnu": "1.63.3",
|
|
274
|
+
"@ata-validator/native-linux-x64-musl": "1.63.3",
|
|
275
|
+
"@ata-validator/native-win32-x64": "1.63.3"
|
|
276
276
|
},
|
|
277
277
|
"peerDependencies": {
|
|
278
278
|
"yaml": "^2.0.0"
|