ata-validator 1.45.0 → 1.46.1
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 +9 -9
- package/bin/ata.js +16 -0
- package/index.js +73 -12
- package/lib/branch-collapse.js +1 -1
- package/lib/combined-runtime.js +149 -0
- package/lib/interpreter.js +1 -1
- package/lib/js-compiler.js +1096 -78
- package/lib/migrate.js +253 -0
- package/lib/regex-linear.js +35 -26
- package/lib/validator-core.js +26 -4
- package/lib/version.js +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -244,15 +244,15 @@ is the most common way to get a misleading number out of this library.
|
|
|
244
244
|
|
|
245
245
|
| | compiled with `ata build` | runtime `new Validator(schema)` |
|
|
246
246
|
|---|---|---|
|
|
247
|
-
| In a bundle, gzipped | **2.1 KB** |
|
|
248
|
-
| Time to a served request | **3.5 ms** | 8.
|
|
247
|
+
| In a bundle, gzipped | **2.1 KB** | 113.3 KB |
|
|
248
|
+
| Time to a served request | **3.5 ms** | 8.5 ms |
|
|
249
249
|
| Schema known when | build time | any time |
|
|
250
250
|
|
|
251
251
|
The bundle row is the ten-field user schema in
|
|
252
252
|
`tests/fixtures/error-dx/user.schema.json`, every export of the compiled module against
|
|
253
|
-
`new Validator(schema)`, built with `bun build --minify --target=browser` on ata 1.
|
|
253
|
+
`new Validator(schema)`, built with `bun build --minify --target=browser` on ata 1.45.0.
|
|
254
254
|
The startup row is a Hono route on Bun 1.4, the median of three rounds of best-of-seven, from
|
|
255
|
-
`benchmark/bundle`, against 3.
|
|
255
|
+
`benchmark/bundle`, against 3.7 ms for the same app doing no validation at all, so the
|
|
256
256
|
compiled path costs nothing measurable to start. The runtime figure is what it is because
|
|
257
257
|
a schema that arrives at run time can use any keyword, so the whole engine has to be
|
|
258
258
|
there. The compiled module imports nothing and contains only the checks your schema asks
|
|
@@ -278,7 +278,7 @@ const v = new Validator(schema)
|
|
|
278
278
|
const result = v.validate(data)
|
|
279
279
|
```
|
|
280
280
|
|
|
281
|
-
The runtime API is unchanged from previous releases. Code written against the default validator's class keeps working through `ata-validator/compat`, which covers `compile`, `addSchema`, `addFormat`, `addKeyword`, `errorsText` and the rest of that surface, and reports errors in the same shape and order. See [docs/migration-from-ajv.md](docs/migration-from-ajv.md).
|
|
281
|
+
The runtime API is unchanged from previous releases. Code written against the default validator's class keeps working through `ata-validator/compat`, which covers `compile`, `addSchema`, `addFormat`, `addKeyword`, `errorsText` and the rest of that surface, and reports errors in the same shape and order. `npx ata migrate` reports what the switch would change in a project, and what it cannot translate, before anything is written. See [docs/migration-from-ajv.md](docs/migration-from-ajv.md).
|
|
282
282
|
|
|
283
283
|
## Usage
|
|
284
284
|
|
|
@@ -491,7 +491,7 @@ const v = new Validator(schema, {
|
|
|
491
491
|
|
|
492
492
|
### Build-time compile (`ata compile`)
|
|
493
493
|
|
|
494
|
-
The `ata` CLI turns a JSON Schema file into a self-contained JavaScript module. No runtime dependency on `ata-validator`, so only the generated validator ships to the browser. For the ten-field user schema in `tests/fixtures/error-dx/user.schema.json` the module is 2.1 KB gzipped, full error detail included, against
|
|
494
|
+
The `ata` CLI turns a JSON Schema file into a self-contained JavaScript module. No runtime dependency on `ata-validator`, so only the generated validator ships to the browser. For the ten-field user schema in `tests/fixtures/error-dx/user.schema.json` the module is 2.1 KB gzipped, full error detail included, against 113.3 KB for the runtime bundled for the browser (ata 1.45.0).
|
|
495
495
|
|
|
496
496
|
```bash
|
|
497
497
|
npx ata compile schemas/user.json -o src/generated/user.validator.mjs
|
|
@@ -533,11 +533,11 @@ npx ata build 'schemas/*.json' --out-dir build/validators --check
|
|
|
533
533
|
Run with `--watch` during development for incremental rebuilds.
|
|
534
534
|
|
|
535
535
|
Bundle sizes for the 10-field user schema in `tests/fixtures/error-dx/user.schema.json`,
|
|
536
|
-
minified and gzipped, measured with `bun build --minify --target=browser` on ata 1.
|
|
536
|
+
minified and gzipped, measured with `bun build --minify --target=browser` on ata 1.45.0:
|
|
537
537
|
|
|
538
538
|
| What the app imports | Size | Notes |
|
|
539
539
|
|---|---|---|
|
|
540
|
-
| `Validator` from `ata-validator` |
|
|
540
|
+
| `Validator` from `ata-validator` | 113.3 KB | The compiler ships with it, because a runtime schema can use any keyword |
|
|
541
541
|
| `isValid` from the compiled module | **1.3 KB** | Nothing else is reachable, so the error collector is dropped |
|
|
542
542
|
| `validate` from the compiled module | **1.9 KB** | Adds the detailed error collector |
|
|
543
543
|
|
|
@@ -575,7 +575,7 @@ through a `setFormats()` export the module carries. `docs/API.md` has the
|
|
|
575
575
|
details.
|
|
576
576
|
|
|
577
577
|
**Fastify startup, 10 route schemas, from a cold process to the first validated request:
|
|
578
|
-
ajv 19.
|
|
578
|
+
ajv 19.7 ms, ata 2.75 ms, no build step required.** ata registers in 0.25 ms of that and
|
|
579
579
|
compiles on the first request, so counting only registration would overstate the gap.
|
|
580
580
|
Reproduce with `node benchmark/bench_fastify_boot.mjs`.
|
|
581
581
|
|
package/bin/ata.js
CHANGED
|
@@ -11,6 +11,7 @@ Usage:
|
|
|
11
11
|
ata compile <schema-file> [options] Compile one schema to a standalone module.
|
|
12
12
|
ata build <glob>... [options] Compile a project's schemas (glob pattern) per file.
|
|
13
13
|
ata validate <schema> <data> [options] Validate a JSON data file against a schema.
|
|
14
|
+
ata migrate [dir] [options] Report what switching from ajv would change; --write applies it.
|
|
14
15
|
|
|
15
16
|
Validate options:
|
|
16
17
|
--pretty Render errors with source frames (default on TTY)
|
|
@@ -29,6 +30,11 @@ Compile options:
|
|
|
29
30
|
--source Embed schema source map (default in development)
|
|
30
31
|
--no-source Omit source map (default in production, NODE_ENV=production)
|
|
31
32
|
|
|
33
|
+
Migrate options:
|
|
34
|
+
--write Apply the mechanical changes (import specifiers, addFormats calls).
|
|
35
|
+
Without it the command only reports. Exit code 1 when a site
|
|
36
|
+
needs a reader, so it can run as a readiness check in CI.
|
|
37
|
+
|
|
32
38
|
Build options:
|
|
33
39
|
--out-dir <dir> Write outputs into this directory instead of alongside sources
|
|
34
40
|
--suffix <str> Output filename suffix (default: ".compiled")
|
|
@@ -98,6 +104,7 @@ function parseArgs(argv) {
|
|
|
98
104
|
continue;
|
|
99
105
|
}
|
|
100
106
|
if (a === '--watch') { out.opts.watch = true; continue; }
|
|
107
|
+
if (a === '--write') { out.opts.write = true; continue; }
|
|
101
108
|
if (a === '--source') { out.opts.source = true; continue; }
|
|
102
109
|
if (a === '--no-source') { out.opts.source = false; continue; }
|
|
103
110
|
if (a === '--dual') { out.opts.dual = true; continue; }
|
|
@@ -451,6 +458,15 @@ function main() {
|
|
|
451
458
|
return;
|
|
452
459
|
}
|
|
453
460
|
|
|
461
|
+
if (cmd === 'migrate') {
|
|
462
|
+
const { migrate, formatReport } = require('../lib/migrate');
|
|
463
|
+
const result = migrate(args._[0] || '.', { write: !!args.opts.write });
|
|
464
|
+
process.stdout.write(formatReport(result, { write: !!args.opts.write }) + '\n');
|
|
465
|
+
// Non-zero when something needs a reader, so the command works as a
|
|
466
|
+
// readiness check in CI.
|
|
467
|
+
process.exit(result.flagged > 0 ? 1 : 0);
|
|
468
|
+
}
|
|
469
|
+
|
|
454
470
|
process.stderr.write(`error: unknown command "${cmd}"\n\n`);
|
|
455
471
|
usage();
|
|
456
472
|
process.exit(1);
|
package/index.js
CHANGED
|
@@ -243,14 +243,15 @@ function installCodegenPaths (ctx) {
|
|
|
243
243
|
this.validate = preprocess
|
|
244
244
|
? (data) => { preprocess(data); return _fn(data) ? VALID_RESULT : ABORT_EARLY_RESULT; }
|
|
245
245
|
: (data) => (_fn(data) ? VALID_RESULT : ABORT_EARLY_RESULT);
|
|
246
|
-
} else if (hasDynRef && _isCodegen && jsFn) {
|
|
247
|
-
// $dynamicRef
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
//
|
|
246
|
+
} else if (hasDynRef && !(_isCodegen && jsFn)) {
|
|
247
|
+
// $dynamicRef without codegen: the interpreted engine. A schema the
|
|
248
|
+
// generators unroll by scope (expandDynamicScopes) takes the assembly
|
|
249
|
+
// below like any other, with the one-pass function once its errors are
|
|
250
|
+
// read; a direct verdict-then-error-function path kept for these from
|
|
251
|
+
// before the unrolling cost the suite's dynamicRef groups 5229 ns a pass
|
|
252
|
+
// where the assembly answers in 3136. (A first attempt dropped them into
|
|
253
|
+
// this interpreter branch instead and measured slower; the condition
|
|
254
|
+
// here is what makes them fall through.) It scores the
|
|
254
255
|
// same on the suite's $dynamicRef cases as the native walker since the
|
|
255
256
|
// dynamic-scope fix, needs no addon, and gets the verdict-only mode.
|
|
256
257
|
if (!_interp) {
|
|
@@ -545,7 +546,8 @@ installCodegenPaths.compileVerdict = function compileVerdict () {
|
|
|
545
546
|
// validateJSON and isValidJSON over a schema-directed scanner, which answers
|
|
546
547
|
// from the JSON text without building the document.
|
|
547
548
|
installCodegenPaths.installScanner = function installScanner (schemaObj, options) {
|
|
548
|
-
const { ABORT_EARLY_RESULT, VALID_RESULT, _bindEntry } = core._internals;
|
|
549
|
+
const { ABORT_EARLY_RESULT, VALID_RESULT, _bindEntry, _jsonSyntaxRejection } = core._internals;
|
|
550
|
+
let textRejects = 0;
|
|
549
551
|
const self = this;
|
|
550
552
|
// Generating a scanner costs about 20 microseconds, measured, and it
|
|
551
553
|
// saves from around 85 nanoseconds on a small accepted document to
|
|
@@ -619,16 +621,58 @@ installCodegenPaths.installScanner = function installScanner (schemaObj, options
|
|
|
619
621
|
{
|
|
620
622
|
const validateByParsing = this.validateJSON;
|
|
621
623
|
const abortEarly = !!options.abortEarly;
|
|
624
|
+
// A validator whose errors are being read answers validate() with
|
|
625
|
+
// the one-pass function (the switch in lib/validator-core.js), and
|
|
626
|
+
// without richErrors nothing it hands out depends on the text. A
|
|
627
|
+
// rejected text is then parsed and given to that function directly,
|
|
628
|
+
// instead of to the layers below, which parsed it, decided again and
|
|
629
|
+
// wrapped a rejection whose errors are built on first read: a 1.7 KB
|
|
630
|
+
// rejected body took 9.2 microseconds through those layers.
|
|
631
|
+
// The scanner itself is the other half of that cost on a rejected
|
|
632
|
+
// text (2.7 of the 9.2 on that body) and all the saving on an accepted
|
|
633
|
+
// one, which it answers without parsing. So the scan runs while the
|
|
634
|
+
// texts a validator sees are mostly accepted, and is skipped while
|
|
635
|
+
// they are mostly rejected: `bias` counts rejections against
|
|
636
|
+
// acceptances, from either path, and the mode follows its sign with
|
|
637
|
+
// some room, so a mixed stream does not flip on every document.
|
|
638
|
+
const onePassText = (text) => {
|
|
639
|
+
let op = self._onePass;
|
|
640
|
+
// A validator rejecting text only never reads through the object
|
|
641
|
+
// path's switch; the second rejected text makes it.
|
|
642
|
+
if (op === undefined && self._tierOnePass !== undefined && ++textRejects >= 2) { op = self._tierOnePass() || undefined; if (op === undefined) self._tierOnePass = undefined; }
|
|
643
|
+
if (op === undefined) return undefined;
|
|
644
|
+
let obj;
|
|
645
|
+
try { obj = JSON.parse(text); } catch (e) { if (!(e instanceof SyntaxError)) throw e; return _jsonSyntaxRejection(e); }
|
|
646
|
+
return op(obj);
|
|
647
|
+
};
|
|
622
648
|
this.validateJSON = (jsonStr) => {
|
|
623
649
|
const scan = self._ensureScanner();
|
|
624
650
|
if (scan === undefined) return validateByParsing(jsonStr);
|
|
625
|
-
if (scan === null) {
|
|
651
|
+
if (scan === null) {
|
|
652
|
+
// No scanner for this schema: the one-pass function still answers a
|
|
653
|
+
// lean validator's texts directly once it is installed.
|
|
654
|
+
_bindEntry(self, 'validateJSON', self._richErrors ? validateByParsing : (text) => {
|
|
655
|
+
if (typeof text === 'string') { const o = onePassText(text); if (o !== undefined) return o; }
|
|
656
|
+
return validateByParsing(text);
|
|
657
|
+
});
|
|
658
|
+
return self.validateJSON(jsonStr);
|
|
659
|
+
}
|
|
660
|
+
let bias = 0;
|
|
626
661
|
_bindEntry(self, 'validateJSON', (text) => {
|
|
627
662
|
if (typeof text === 'string') {
|
|
663
|
+
if (bias > 16 && !self._richErrors && self._onePass !== undefined) {
|
|
664
|
+
const r = onePassText(text);
|
|
665
|
+
if (r !== undefined) { if (r.valid) { if (--bias < -32) bias = -32; } else if (bias < 64) bias++; return r; }
|
|
666
|
+
}
|
|
628
667
|
const r = scan(text);
|
|
629
|
-
if (r === 1) return VALID_RESULT;
|
|
668
|
+
if (r === 1) { if (--bias < -32) bias = -32; return VALID_RESULT; }
|
|
630
669
|
if (r === 0) {
|
|
670
|
+
if (bias < 64) bias++;
|
|
631
671
|
if (abortEarly) return ABORT_EARLY_RESULT;
|
|
672
|
+
if (!self._richErrors) {
|
|
673
|
+
const o = onePassText(text);
|
|
674
|
+
if (o !== undefined) return o;
|
|
675
|
+
}
|
|
632
676
|
self._skipNativeFast = true;
|
|
633
677
|
try {
|
|
634
678
|
return validateByParsing(text);
|
|
@@ -637,6 +681,9 @@ installCodegenPaths.installScanner = function installScanner (schemaObj, options
|
|
|
637
681
|
}
|
|
638
682
|
}
|
|
639
683
|
}
|
|
684
|
+
// The scanner could not decide (a shape it does not read): the
|
|
685
|
+
// one-pass function answers a lean validator here too.
|
|
686
|
+
if (!self._richErrors && typeof text === 'string') { const o = onePassText(text); if (o !== undefined) return o; }
|
|
640
687
|
return validateByParsing(text);
|
|
641
688
|
});
|
|
642
689
|
return self.validateJSON(jsonStr);
|
|
@@ -870,7 +917,21 @@ function buildParse (self, decline, extended) {
|
|
|
870
917
|
if (self._mutatesInput) {
|
|
871
918
|
try { target = structuredClone(data); } catch { target = data; }
|
|
872
919
|
}
|
|
873
|
-
|
|
920
|
+
// The errors are worked out when first read, as validate() works them
|
|
921
|
+
// out: building every error with its enrichment before throwing made a
|
|
922
|
+
// rejected parse() cost about 12 µs on a document with 16 violations,
|
|
923
|
+
// most of it for callers that only catch. Read once, the list stays.
|
|
924
|
+
const rejection = self.validate(target);
|
|
925
|
+
Object.defineProperty(e, 'errors', {
|
|
926
|
+
configurable: true,
|
|
927
|
+
enumerable: true,
|
|
928
|
+
get () {
|
|
929
|
+
const errors = rejection.errors;
|
|
930
|
+
Object.defineProperty(e, 'errors', { value: errors, writable: true, configurable: true, enumerable: true });
|
|
931
|
+
return errors;
|
|
932
|
+
},
|
|
933
|
+
set (v) { Object.defineProperty(e, 'errors', { value: v, writable: true, configurable: true, enumerable: true }); },
|
|
934
|
+
});
|
|
874
935
|
throw e;
|
|
875
936
|
}
|
|
876
937
|
return copy(data);
|
package/lib/branch-collapse.js
CHANGED
|
@@ -112,4 +112,4 @@ function embedSource () {
|
|
|
112
112
|
'const __ataCollapse=' + __ataCollapse.toString() + ';';
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
module.exports = { collapseBranches, scoreBranch: __ataScore, SEVERITY: __ATA_SEVERITY, embedSource };
|
|
115
|
+
module.exports = { collapseBranches, scoreBranch: __ataScore, SEVERITY: __ATA_SEVERITY, embedSource, _collapse: __ataCollapse, _multi: __ataMulti };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// What the one-pass (combined) function needs at run time and does not need
|
|
4
|
+
// a copy of: one module-level implementation each, handed to every compiled
|
|
5
|
+
// program as a closure value. Each program used to carry its own copy of
|
|
6
|
+
// these in its source, so a thousand validators meant a thousand `_ap`s, a
|
|
7
|
+
// thousand `__ataCollapse`s and so on, each parsed, each compiled by the
|
|
8
|
+
// baseline compiler, each queued for the optimizing compiler on its own.
|
|
9
|
+
// On the official suite, with about a thousand validators in one process,
|
|
10
|
+
// the optimizing compiler got to 728 functions in five seconds, and the
|
|
11
|
+
// validators themselves (`_vC`) were 242 of those. Shared, a helper is
|
|
12
|
+
// compiled once and is hot from the second validator on. The generated
|
|
13
|
+
// standalone modules (lib/aot-impl.js) are not built from this generator and
|
|
14
|
+
// keep their own embedded copies.
|
|
15
|
+
|
|
16
|
+
// The error list's order, kept as it grows (see apOrd in lib/js-compiler.js):
|
|
17
|
+
// `os` the ordinal of each error in push order, `mo` whether they are still in
|
|
18
|
+
// order, `lo` the last ordinal. One state per compiled program, reset at the
|
|
19
|
+
// top of each call.
|
|
20
|
+
function apState () { return { mo: true, lo: -1, on: 0, os: [] } }
|
|
21
|
+
|
|
22
|
+
// Whether a collapsed error holds, at any depth, a branch list of more than
|
|
23
|
+
// one error, which only the reader puts in schema order.
|
|
24
|
+
function multiBranch (x) {
|
|
25
|
+
for (let b = x.branchErrors; b !== undefined; b = b[0].branchErrors) {
|
|
26
|
+
if (b.length > 1) return true
|
|
27
|
+
if (b.length === 0) return false
|
|
28
|
+
}
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Append one error, keeping whether the list is still in the reader's order.
|
|
33
|
+
function ap (S, e, x, o) {
|
|
34
|
+
// Keys are dense integers in the reader's order (ordinal, then the rank of
|
|
35
|
+
// the path), given per program when it is assembled (assignOrdKeys in
|
|
36
|
+
// lib/js-compiler.js): two errors with one exact key are ones the reader
|
|
37
|
+
// keeps as they came. -1 is a key the program does not know. Below -1 is a
|
|
38
|
+
// coarse key, -(k+2): every error behind a reference into a shared
|
|
39
|
+
// definition takes the key k of the reference that reached it, so two of
|
|
40
|
+
// them with one key and different paths are ordered by the reader.
|
|
41
|
+
if (o < 0) {
|
|
42
|
+
if (o === -1) S.mo = false
|
|
43
|
+
else {
|
|
44
|
+
const k = -o - 2
|
|
45
|
+
if (k < S.lo || (k === S.lo && (e === undefined || x.schemaPath !== e[e.length - 1].schemaPath))) S.mo = false
|
|
46
|
+
else S.lo = k
|
|
47
|
+
}
|
|
48
|
+
} else if (o < S.lo) S.mo = false
|
|
49
|
+
else S.lo = o
|
|
50
|
+
// A collapsed branch error's own lists, at any depth, are ordered by the
|
|
51
|
+
// reader (sr hands such a list to it); an in-order outer list must still
|
|
52
|
+
// get there.
|
|
53
|
+
if (x.branchErrors !== undefined && multiBranch(x)) S.mo = false
|
|
54
|
+
S.os[S.on++] = o
|
|
55
|
+
if (e === undefined) return [x]
|
|
56
|
+
e.push(x)
|
|
57
|
+
return e
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Put a list that did not come in order into order by the ordinals collected
|
|
61
|
+
// beside it: a stable insertion sort over a few integers. The reader's sort
|
|
62
|
+
// (`srt`) still answers where a key is unknown (-1), where two errors share a
|
|
63
|
+
// coarse key but not a path, where a collapsed error holds a branch list of
|
|
64
|
+
// more than one error, and for long lists.
|
|
65
|
+
function sr (S, e, srt) {
|
|
66
|
+
const n = e.length
|
|
67
|
+
const os = S.os
|
|
68
|
+
if (n > 16 || n !== S.on) return srt(e)
|
|
69
|
+
let coarse = 0
|
|
70
|
+
for (let i = 0; i < n; i++) {
|
|
71
|
+
let k = os[i]
|
|
72
|
+
if (k < 0) {
|
|
73
|
+
if (k === -1) return srt(e)
|
|
74
|
+
k = -k - 2
|
|
75
|
+
os[i] = k
|
|
76
|
+
coarse |= 1 << i
|
|
77
|
+
}
|
|
78
|
+
// A collapsed branch error carries the closest branch's errors, which
|
|
79
|
+
// the reader puts in schema order too; one error needs no ordering.
|
|
80
|
+
if (e[i].branchErrors !== undefined && multiBranch(e[i])) return srt(e)
|
|
81
|
+
}
|
|
82
|
+
// Two errors with one key and different paths, one of them coarse: the
|
|
83
|
+
// reader ranks their paths.
|
|
84
|
+
if (coarse !== 0) {
|
|
85
|
+
for (let i = 1; i < n; i++) {
|
|
86
|
+
for (let j = 0; j < i; j++) if (os[j] === os[i] && ((coarse >> i | coarse >> j) & 1) && e[j].schemaPath !== e[i].schemaPath) return srt(e)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (let i = 1; i < n; i++) {
|
|
90
|
+
const k = os[i]
|
|
91
|
+
const x = e[i]
|
|
92
|
+
let j = i - 1
|
|
93
|
+
while (j >= 0 && os[j] > k) { os[j + 1] = os[j]; e[j + 1] = e[j]; j-- }
|
|
94
|
+
os[j + 1] = k
|
|
95
|
+
e[j + 1] = x
|
|
96
|
+
}
|
|
97
|
+
return e
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// `sr` checked against the reader's order on every list; the test suites run
|
|
101
|
+
// the generator with ATA_CHECK_SORT set, which routes here.
|
|
102
|
+
function srX (S, e, srt) {
|
|
103
|
+
const c = e.slice()
|
|
104
|
+
const k = S.os.slice(0, S.on)
|
|
105
|
+
const a = sr(S, e, srt)
|
|
106
|
+
const b = srt(c)
|
|
107
|
+
if (a.length !== b.length || a.some((x, i) => x.schemaPath !== b[i].schemaPath || x.instancePath !== b[i].instancePath)) {
|
|
108
|
+
throw new Error('ata: local error sort disagrees with the reader ' + JSON.stringify(a.map((x) => x.schemaPath)) + ' vs ' + JSON.stringify(b.map((x) => x.schemaPath)) + ' keys ' + JSON.stringify(k))
|
|
109
|
+
}
|
|
110
|
+
return a
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Presence helpers (see OWN_HELPER_CODE in lib/js-compiler.js for the text
|
|
114
|
+
// the standalone modules carry). The prototype test itself stays inline in
|
|
115
|
+
// the generated code, read once per object: a shared helper reading the
|
|
116
|
+
// prototype of every object it was asked about was megamorphic.
|
|
117
|
+
const _PN = ({}).__proto__ === Object.prototype // eslint-disable-line no-proto
|
|
118
|
+
const _OP = Object.prototype
|
|
119
|
+
const _gp = Object.getPrototypeOf
|
|
120
|
+
const _hop = Object.prototype.hasOwnProperty
|
|
121
|
+
function _h (o, k) { return _hop.call(o, k) }
|
|
122
|
+
function _ok (o, k) { return (_PN ? o.__proto__ : _gp(o)) === _OP || _hop.call(o, k) } // eslint-disable-line no-proto
|
|
123
|
+
function _hall (o, ks) { for (let i = 0; i < ks.length; i++) if (!_hop.call(o, ks[i])) return false; return true }
|
|
124
|
+
|
|
125
|
+
// RFC 6901 escaping for a key only known at run time (the static ones are
|
|
126
|
+
// escaped when the code is written).
|
|
127
|
+
function _pe (s) {
|
|
128
|
+
for (let i = 0; i < s.length; i++) {
|
|
129
|
+
const c = s.charCodeAt(i)
|
|
130
|
+
if (c === 126 || c === 47) return s.replace(/~/g, '~0').replace(/\//g, '~1')
|
|
131
|
+
}
|
|
132
|
+
return s
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// The annotation functions' constants (emitAnnot): the boolean schemas, and
|
|
136
|
+
// the rollback of the evaluated-name list when a branch fails.
|
|
137
|
+
function _anT () { return true }
|
|
138
|
+
function _anF () { return false }
|
|
139
|
+
function _rb (S, m) { if (S.length !== m) S.length = m }
|
|
140
|
+
|
|
141
|
+
module.exports = {
|
|
142
|
+
apState, ap, sr, srX,
|
|
143
|
+
_PN, _OP, _gp, _hop, _h, _ok, _hall, _pe, _anT, _anF, _rb,
|
|
144
|
+
}
|
|
145
|
+
// Loaded when a program first asks for them: a schema without const, enum,
|
|
146
|
+
// uniqueItems, oneOf or anyOf never needs these modules on its cold path.
|
|
147
|
+
Object.defineProperty(module.exports, '_deq', { enumerable: true, get () { return require('./unique-items')._deq } })
|
|
148
|
+
Object.defineProperty(module.exports, '__ataCollapse', { enumerable: true, get () { return require('./branch-collapse')._collapse } })
|
|
149
|
+
Object.defineProperty(module.exports, '__ataMulti', { enumerable: true, get () { return require('./branch-collapse')._multi } })
|
package/lib/interpreter.js
CHANGED
|
@@ -1251,7 +1251,7 @@ module.exports = { createInterpreter };
|
|
|
1251
1251
|
// The resolver, for the code generator: it resolves references at compile
|
|
1252
1252
|
// time the way this engine does at run time (normalizeRefs in js-compiler).
|
|
1253
1253
|
Object.defineProperty(module.exports, '_refInternals', {
|
|
1254
|
-
value: { indexSchemas, resolveRef, FALLBACK_BASE },
|
|
1254
|
+
value: { indexSchemas, resolveRef, splitFragment, resolveUri, FALLBACK_BASE },
|
|
1255
1255
|
enumerable: false,
|
|
1256
1256
|
});
|
|
1257
1257
|
// For lib/plan-source.js, which emits source from the same plans and must call
|