ata-validator 1.61.1 → 1.61.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/README.md +98 -31
- package/bin/ata.js +32 -0
- package/compiled-standard.d.ts +6 -0
- package/compiled-standard.mjs +66 -0
- package/index.aot.js +8 -0
- package/index.aot.mjs +5 -0
- package/lib/aot-mark.js +7 -0
- package/lib/compiled-standard.js +33 -0
- package/lib/formats.js +28 -1
- package/lib/interpreter-core.js +4 -28
- package/lib/schema-diff.js +103 -0
- package/lib/shape-classifier.js +6 -1
- package/lib/tier0.js +17 -4
- package/lib/validator-core.js +40 -18
- package/lib/version.js +1 -1
- package/package.json +26 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ata-validator
|
|
2
2
|
|
|
3
|
-
JSON Schema validation
|
|
3
|
+
JSON Schema validation where the TypeScript type comes from the schema, or the schema from the type. Write the schema once and `validate` narrows to it; write the type once and the bundler plugin writes its schema in at build time. The compiled and interpreted engines answer identically, at 100% of the official suite in both modes, across Draft 2020-12, 2019-09, drafts 7, 6 and 4 and the [JSON Schema v1 dialect](#dialects), and `ata build` emits a standalone module that imports nothing, with its `.d.mts` beside it.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/ata-validator)
|
|
6
6
|
[](LICENSE)
|
|
@@ -10,6 +10,60 @@ JSON Schema validation that compiles for speed and still runs where code generat
|
|
|
10
10
|
|
|
11
11
|
1.0 is a stability commitment: see [docs/STABILITY.md](docs/STABILITY.md) for the semver, deprecation, and error-code guarantees.
|
|
12
12
|
|
|
13
|
+
## The type and the schema
|
|
14
|
+
|
|
15
|
+
One source of truth, in either direction. A schema written with `defineSchema` carries its
|
|
16
|
+
type: `validate` narrows `data` to it, and `Infer` pulls the type out for a signature.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { defineSchema, Validator, type Infer } from 'ata-validator'
|
|
20
|
+
|
|
21
|
+
const User = defineSchema({
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
id: { type: 'integer', minimum: 1 },
|
|
25
|
+
email: { type: 'string', format: 'email' },
|
|
26
|
+
role: { enum: ['admin', 'user'] },
|
|
27
|
+
},
|
|
28
|
+
required: ['id', 'email'],
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const users = new Validator(User)
|
|
32
|
+
const r = users.validate(JSON.parse(body))
|
|
33
|
+
if (r.valid) {
|
|
34
|
+
r.data.id // number
|
|
35
|
+
r.data.role // 'admin' | 'user' | undefined
|
|
36
|
+
r.data.name // a type error: the schema has no name
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type User = Infer<typeof User>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or start from the type. `schemaOf<T>()` has no run-time form; the bundler plugin reads `T`
|
|
43
|
+
with the TypeScript checker and writes its JSON Schema in place of the call, with the JSDoc
|
|
44
|
+
tags carrying what a type cannot say, and the `new Validator(...)` then compiles away like any
|
|
45
|
+
static schema.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { Validator, schemaOf } from 'ata-validator'
|
|
49
|
+
|
|
50
|
+
interface Account {
|
|
51
|
+
/** @minimum 1 */
|
|
52
|
+
id: number
|
|
53
|
+
/** @format email */
|
|
54
|
+
email: string
|
|
55
|
+
role?: 'admin' | 'user'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const accounts = new Validator(schemaOf<Account>())
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`ata build` writes a `.d.mts` next to every compiled module, with the inferred type and an
|
|
62
|
+
`isValid` predicate on it, so a module that imports nothing still narrows. The chainable
|
|
63
|
+
builder in `ata-validator/t`, `Infer` on `anyOf`, `allOf`, tuples and recursive `$ref`, and
|
|
64
|
+
the async `t.refine` are in [Type-safe schemas](#type-safe-schemas); the plugin's type reading
|
|
65
|
+
is in [its README](https://github.com/ata-core/unplugin-ata#a-schema-from-a-typescript-type-schemaofuser).
|
|
66
|
+
|
|
13
67
|
## Who uses ata
|
|
14
68
|
|
|
15
69
|
Projects whose code depends on ata, from GitHub's dependency graph, a framework that lists its
|
|
@@ -399,31 +453,7 @@ v.countValid(ndjson); // number
|
|
|
399
453
|
|
|
400
454
|
### Type-safe schemas
|
|
401
455
|
|
|
402
|
-
ata infers TypeScript types straight from plain JSON Schema
|
|
403
|
-
|
|
404
|
-
```ts
|
|
405
|
-
import { defineSchema, Validator } from 'ata-validator'
|
|
406
|
-
|
|
407
|
-
const userSchema = defineSchema({
|
|
408
|
-
type: 'object',
|
|
409
|
-
properties: {
|
|
410
|
-
id: { type: 'integer', minimum: 1 },
|
|
411
|
-
role: { type: 'string', enum: ['admin', 'user'] },
|
|
412
|
-
},
|
|
413
|
-
required: ['id'],
|
|
414
|
-
})
|
|
415
|
-
|
|
416
|
-
const v = new Validator(userSchema)
|
|
417
|
-
const result = v.validate(data)
|
|
418
|
-
if (result.valid) {
|
|
419
|
-
result.data.id // number
|
|
420
|
-
result.data.role // 'admin' | 'user' | undefined
|
|
421
|
-
} else {
|
|
422
|
-
// result.errors: ValidationError[]
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
`defineSchema` returns the schema untouched at runtime; in TypeScript it gives keyword autocomplete and an error when a value has the wrong shape, with no `as const` needed. `new Validator(schema)` carries the inferred type, so a successful `validate` narrows `result.data` with no manual annotation.
|
|
456
|
+
ata infers TypeScript types straight from plain JSON Schema, as in [the opening example](#the-type-and-the-schema): `defineSchema` returns the schema untouched at runtime and, in TypeScript, gives keyword autocomplete and an error when a value has the wrong shape, with no `as const` needed. `new Validator(schema)` carries the inferred type, so a successful `validate` narrows `result.data` with no manual annotation.
|
|
427
457
|
|
|
428
458
|
#### Extracting the type: `Infer`
|
|
429
459
|
|
|
@@ -671,7 +701,11 @@ validator only ever called (`v.validate(x)`, `v.isValidObject(x)`), never handed
|
|
|
671
701
|
it uses the bare wrappers of `ata-validator/compiled-bare` instead, which leave out Standard
|
|
672
702
|
Schema and the extension hook nothing can reach there: 0.3 KB gzipped for both, and
|
|
673
703
|
`ata-validator/compiled-bare-rich` for one that reads ata's own errors. The same form app
|
|
674
|
-
bundles to 10.7 KB gzipped, or 3.1 KB with `richErrors: false` on the validator that reads errors.
|
|
704
|
+
bundles to 10.7 KB gzipped, or 3.1 KB with `richErrors: false` on the validator that reads errors.
|
|
705
|
+
A validator handed only to a Standard Schema consumer the plugin knows (Hono's
|
|
706
|
+
`sValidator`, `hono-openapi`, TanStack Form, oRPC, tRPC, Elysia) takes `ata-validator/compiled-standard`, which
|
|
707
|
+
carries `~standard` alone on a module with plain errors: a Hono route on Cloudflare Workers goes
|
|
708
|
+
from 21.0 to 12.2 KB gzipped, 4.4 KB over Hono by itself. The errors
|
|
675
709
|
then carry `keyword`, `instancePath`, `schemaPath`, `params` and `message`, as that option has
|
|
676
710
|
always given them, without `received`, `suggestion` or `docUrl`.
|
|
677
711
|
|
|
@@ -728,6 +762,37 @@ works as a CI step. This is the check that found the two silent divergences
|
|
|
728
762
|
fixed in 1.59.0 and 1.60.0, now in a form a project can run on every change.
|
|
729
763
|
It does not run the `.bun.ts` target, which needs Bun's compiler.
|
|
730
764
|
|
|
765
|
+
### What a schema change does (`ata diff`)
|
|
766
|
+
|
|
767
|
+
Reading two versions of a schema side by side says which keywords changed. It
|
|
768
|
+
does not say which documents change verdict, and that is the question before
|
|
769
|
+
a deploy: which requests that pass today get a 400 tomorrow. `ata diff` answers
|
|
770
|
+
it with documents:
|
|
771
|
+
|
|
772
|
+
```bash
|
|
773
|
+
npx ata diff schemas/user.json schemas/user-v2.json --samples test/fixtures
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
```
|
|
777
|
+
401 documents: 102 accepted by both, 293 rejected by both, 6 rejected only by user-v2.json, 0 accepted only by user-v2.json
|
|
778
|
+
|
|
779
|
+
Rejected by user-v2.json, accepted by user.json (a client sending these gets a rejection after the change):
|
|
780
|
+
minimum@/age: 4 documents, 1 of them the project's own, must be >= 18
|
|
781
|
+
{"email":"kid@example.com","name":"Kid","age":15} (test/fixtures/signup-teen.json)
|
|
782
|
+
maxLength@/name: 2 documents, must NOT have more than 40 characters
|
|
783
|
+
{"email":"a@b.co","name":"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij...
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
The documents are the ones `ata verify` makes, from both schemas (200 each by
|
|
787
|
+
default, half with one rule broken, then a value on either side of every
|
|
788
|
+
bound), plus the project's own samples. Each one is checked by a `Validator`
|
|
789
|
+
built from each schema, and the ones that change verdict are grouped by the
|
|
790
|
+
rule that decides them, with one example each, the project's own document
|
|
791
|
+
when there is one. The exit code is 1 when a document the old schema accepts
|
|
792
|
+
is rejected by the new one, so a pull request that tightens a schema fails
|
|
793
|
+
the check until someone looks. Generated documents are a sample, not a proof:
|
|
794
|
+
a change this reports is real, one it does not report may still exist.
|
|
795
|
+
|
|
731
796
|
### Bun, compiled ahead of time
|
|
732
797
|
|
|
733
798
|
Bun's ahead-of-time compiler (`bun build --compile --aot`, an open pull
|
|
@@ -748,10 +813,12 @@ So, in order of preference:
|
|
|
748
813
|
module by hand. The module is plain JavaScript and compiles with the
|
|
749
814
|
program: 24 ns, the same errors as the runtime.
|
|
750
815
|
2. Where the runtime `Validator` stays, let it take the interpreted engine.
|
|
751
|
-
It does so by itself
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
816
|
+
It does so by itself under `bun build --compile --aot` and `bun run --aot`,
|
|
817
|
+
where Bun resolves the package's `"aot"` export condition to an entry that
|
|
818
|
+
says so; also when the executable is built with
|
|
819
|
+
`--disallow-code-generation-from-strings` (the program then counts as
|
|
820
|
+
closed for the compiler, too), or when `ATA_ENGINE=interpreter` is set:
|
|
821
|
+
78 ns against 487 for the generated code there.
|
|
755
822
|
3. Write the module for the compiler. `ata build schema.json --target bun`
|
|
756
823
|
writes it with a closed object's keys read once and compared with the
|
|
757
824
|
declared list in order (on a ten-item order with 1024 distinct documents,
|
package/bin/ata.js
CHANGED
|
@@ -15,6 +15,8 @@ Usage:
|
|
|
15
15
|
ata migrate [dir] [options] Report what switching from ajv would change; --write applies it.
|
|
16
16
|
ata verify <glob>... [options] Check that each schema's compiled module answers as the runtime
|
|
17
17
|
does, on generated documents and the project's samples.
|
|
18
|
+
ata diff <old> <new> [options] What a change to a schema does: the documents the old schema
|
|
19
|
+
accepts and the new one rejects, and the reverse, by rule.
|
|
18
20
|
|
|
19
21
|
Verify options:
|
|
20
22
|
--generate <n> Documents made from each schema, half with one rule broken. Default: 200
|
|
@@ -23,6 +25,13 @@ Verify options:
|
|
|
23
25
|
--json Report as JSON
|
|
24
26
|
--out-dir, --suffix, --format Where the modules are, as given to ata build
|
|
25
27
|
|
|
28
|
+
Diff options:
|
|
29
|
+
--generate <n> Documents made from each schema. Default: 200
|
|
30
|
+
--samples <path> The project's own documents, checked too (repeatable)
|
|
31
|
+
--seed <n> Seed for the generated documents. Default: 1
|
|
32
|
+
--json Report as JSON
|
|
33
|
+
Exit status 1 when a document the old schema accepts is rejected by the new one.
|
|
34
|
+
|
|
26
35
|
Validate options:
|
|
27
36
|
--pretty Render errors with source frames (default on TTY)
|
|
28
37
|
--compact Render errors as one line each (default when piped)
|
|
@@ -487,6 +496,24 @@ function cmdVerify (args) {
|
|
|
487
496
|
})().catch((e) => { process.stderr.write(`error: ${e.message}\n`); process.exit(1); });
|
|
488
497
|
}
|
|
489
498
|
|
|
499
|
+
function cmdDiff (args) {
|
|
500
|
+
if (args._.length < 2) {
|
|
501
|
+
process.stderr.write('error: ata diff <old-schema> <new-schema>\n');
|
|
502
|
+
process.exit(2);
|
|
503
|
+
}
|
|
504
|
+
const buildLib = require('../lib/aot-build');
|
|
505
|
+
const { diffSchemas, renderDiff, breaking } = require('../lib/schema-diff');
|
|
506
|
+
let oldSchema, newSchema;
|
|
507
|
+
try { oldSchema = buildLib.parseSchemaFile(args._[0]); newSchema = buildLib.parseSchemaFile(args._[1]); } catch (e) { process.stderr.write(`error: ${e.message}\n`); process.exit(2); }
|
|
508
|
+
const samples = args.opts.samples === undefined ? [] : [].concat(args.opts.samples);
|
|
509
|
+
let report;
|
|
510
|
+
try {
|
|
511
|
+
report = diffSchemas(oldSchema, newSchema, { generate: Number.isFinite(args.opts.generate) ? args.opts.generate : 200, seed: Number.isFinite(args.opts.seed) ? args.opts.seed : 1, samples });
|
|
512
|
+
} catch (e) { process.stderr.write(`error: ${e.message}\n`); process.exit(2); }
|
|
513
|
+
process.stdout.write(renderDiff(report, { json: !!args.opts.json, oldName: path.basename(args._[0]), newName: path.basename(args._[1]) }) + '\n');
|
|
514
|
+
process.exit(breaking(report) ? 1 : 0);
|
|
515
|
+
}
|
|
516
|
+
|
|
490
517
|
function cmdValidate (args) {
|
|
491
518
|
if (args._.length < 2) {
|
|
492
519
|
process.stderr.write('error: ata validate <schema> <data-file>\n');
|
|
@@ -592,6 +619,11 @@ function main() {
|
|
|
592
619
|
return;
|
|
593
620
|
}
|
|
594
621
|
|
|
622
|
+
if (cmd === 'diff') {
|
|
623
|
+
cmdDiff(args);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
|
|
595
627
|
if (cmd === 'migrate') {
|
|
596
628
|
const { migrate, formatReport } = require('../lib/migrate');
|
|
597
629
|
const result = migrate(args._[0] || '.', { write: !!args.opts.write });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ata-validator/compiled-standard: the compiled-module wrapper for a validator
|
|
2
|
+
// a bundler plugin sees handed to Standard Schema consumers only.
|
|
3
|
+
import type { StandardSchemaV1Props } from './index.js';
|
|
4
|
+
import type { CompiledModule } from './compiled.js';
|
|
5
|
+
|
|
6
|
+
export function bareStandard<T = unknown>(mod: CompiledModule, schema: object | boolean, useDefaults?: boolean): { '~standard': StandardSchemaV1Props<T> };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// ata-validator/compiled-standard as an ES module of its own, not a re-export
|
|
2
|
+
// of the CommonJS file, so a bundler adds no interop code (see
|
|
3
|
+
// compiled-bare.mjs). The same `~standard` as lib/compiled-standard.js builds
|
|
4
|
+
// through lib/standard-schema.js; tests/test_compiled_standard.js holds the
|
|
5
|
+
// two to the same issues and the same schema documents.
|
|
6
|
+
|
|
7
|
+
// When the module's error function answers valid where its verdict said no,
|
|
8
|
+
// the disagreement is reported rather than turned into an acceptance.
|
|
9
|
+
const DISAGREES = [{ message: 'schema validation failed', instancePath: '' }];
|
|
10
|
+
const EMPTY_PATH = Object.freeze([]);
|
|
11
|
+
|
|
12
|
+
// A JSON pointer as a Standard Schema path: an all-digit segment without a
|
|
13
|
+
// leading zero is an array index and comes out as a number.
|
|
14
|
+
function pathOf(pointer) {
|
|
15
|
+
if (!pointer) return EMPTY_PATH;
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const raw of pointer.split('/').slice(1)) {
|
|
18
|
+
const seg = raw.indexOf('~') >= 0 ? raw.replace(/~1/g, '/').replace(/~0/g, '~') : raw;
|
|
19
|
+
out.push({ key: /^(?:0|[1-9][0-9]*)$/.test(seg) ? Number(seg) : seg });
|
|
20
|
+
}
|
|
21
|
+
return Object.freeze(out);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Standard JSON Schema: the document, as a copy, for its own dialect.
|
|
25
|
+
function jsonSchemaOf(schema) {
|
|
26
|
+
const convert = (options) => {
|
|
27
|
+
const target = options && options.target;
|
|
28
|
+
if (schema === true) return {};
|
|
29
|
+
if (schema === false) return { not: {} };
|
|
30
|
+
const declared = typeof schema.$schema === 'string' ? schema.$schema : '';
|
|
31
|
+
const dialect = declared === '' || declared.includes('draft/2020-12/schema') ? 'draft-2020-12'
|
|
32
|
+
: declared.includes('draft-07/schema') ? 'draft-07' : declared;
|
|
33
|
+
if (target !== dialect) {
|
|
34
|
+
throw new TypeError(`ata-validator cannot provide a ${dialect} schema as ${String(target)}; it does not convert between dialects`);
|
|
35
|
+
}
|
|
36
|
+
return JSON.parse(JSON.stringify(schema));
|
|
37
|
+
};
|
|
38
|
+
return Object.freeze({ input: convert, output: convert });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function bareStandard(mod, schema, useDefaults) {
|
|
42
|
+
const fill = useDefaults !== false && typeof mod.applyDefaults === 'function' ? mod.applyDefaults : null;
|
|
43
|
+
const isValid = mod.isValid;
|
|
44
|
+
const errFn = mod.validate;
|
|
45
|
+
return {
|
|
46
|
+
'~standard': Object.freeze({
|
|
47
|
+
version: 1,
|
|
48
|
+
vendor: 'ata-validator',
|
|
49
|
+
jsonSchema: jsonSchemaOf(schema),
|
|
50
|
+
validate(value) {
|
|
51
|
+
if (fill !== null) fill(value);
|
|
52
|
+
if (isValid(value)) return { value };
|
|
53
|
+
const r = errFn(value);
|
|
54
|
+
const errors = r && r.valid === false ? r.errors : DISAGREES;
|
|
55
|
+
const issues = new Array(errors.length);
|
|
56
|
+
for (let i = 0; i < errors.length; i++) {
|
|
57
|
+
const e = errors[i];
|
|
58
|
+
issues[i] = { message: e.message || 'validation failed', path: pathOf(e.instancePath != null ? e.instancePath : (e.path || '')) };
|
|
59
|
+
}
|
|
60
|
+
return { issues };
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default { bareStandard };
|
package/index.aot.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ata-validator under Bun's ahead-of-time compiler, where Bun resolves the
|
|
4
|
+
// `"aot"` export condition: the same package, with validators on the
|
|
5
|
+
// interpreted engine by default, which is compiled with the program, rather
|
|
6
|
+
// than on code made at run time, which is not (lib/aot-mark.js).
|
|
7
|
+
require('./lib/aot-mark.js');
|
|
8
|
+
module.exports = require('./index.js');
|
package/index.aot.mjs
ADDED
package/lib/aot-mark.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Loaded first by index.aot.js and index.aot.mjs, the entries Bun resolves
|
|
4
|
+
// through the `"aot"` export condition when it compiles a program ahead of
|
|
5
|
+
// time: every Validator made after this takes the interpreted engine by
|
|
6
|
+
// default (see aotHost in lib/validator-core.js).
|
|
7
|
+
require('./validator-core')._markAotHost();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// bareStandard: the compiled-module wrapper for a validator that is only ever
|
|
4
|
+
// handed to a Standard Schema consumer (a router's validator middleware, a
|
|
5
|
+
// form library, an RPC layer) and never called by its own code. Such a
|
|
6
|
+
// consumer reads `~standard` and nothing else, and an issue carries a message
|
|
7
|
+
// and a path, so the wrapper is built on a module written with
|
|
8
|
+
// `plainErrors: true`, whose errors are those two things in schema order, and
|
|
9
|
+
// carries no error pipeline, no Standard Schema install and no extension hook.
|
|
10
|
+
// The issues are the runtime's (tests/test_compiled_standard.js). A bundler
|
|
11
|
+
// plugin puts this in place of `new Validator(schema)` where it sees that use.
|
|
12
|
+
|
|
13
|
+
const { standardSchemaFor } = require('./standard-schema');
|
|
14
|
+
|
|
15
|
+
const VALID = Object.freeze({ valid: true, errors: Object.freeze([]) });
|
|
16
|
+
// When the module's error function answers valid where its verdict said no,
|
|
17
|
+
// the disagreement is reported rather than turned into an acceptance.
|
|
18
|
+
const VERDICT_DISAGREES = Object.freeze({ valid: false, errors: Object.freeze([Object.freeze({ keyword: 'validation', instancePath: '', schemaPath: '#', params: Object.freeze({}), message: 'schema validation failed' })]) });
|
|
19
|
+
|
|
20
|
+
function bareStandard(mod, schema, useDefaults) {
|
|
21
|
+
const fill = useDefaults !== false && typeof mod.applyDefaults === 'function' ? mod.applyDefaults : null;
|
|
22
|
+
const isValid = mod.isValid;
|
|
23
|
+
const errFn = mod.validate;
|
|
24
|
+
const validate = (data) => {
|
|
25
|
+
if (fill !== null) fill(data);
|
|
26
|
+
if (isValid(data)) return VALID;
|
|
27
|
+
const r = errFn(data);
|
|
28
|
+
return r && r.valid === false ? r : VERDICT_DISAGREES;
|
|
29
|
+
};
|
|
30
|
+
return { '~standard': standardSchemaFor(null, () => schema, (_, value) => validate(value)) };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = { bareStandard };
|
package/lib/formats.js
CHANGED
|
@@ -538,4 +538,31 @@ function email(s) {
|
|
|
538
538
|
const DURATION_RE = /^P(?:\d+W|(?:\d+Y(?:\d+M(?:\d+D)?)?|\d+M(?:\d+D)?|\d+D)(?:T(?:\d+H(?:\d+M(?:\d+S)?)?|\d+M(?:\d+S)?|\d+S))?|T(?:\d+H(?:\d+M(?:\d+S)?)?|\d+M(?:\d+S)?|\d+S))$/;
|
|
539
539
|
function duration (s) { return DURATION_RE.test(s); }
|
|
540
540
|
|
|
541
|
-
|
|
541
|
+
// The built-in format checks by name, built on first use. The interpreted
|
|
542
|
+
// engine and the tier-0 verdict (lib/tier0.js) read the same table, so the two
|
|
543
|
+
// cannot answer differently for a format.
|
|
544
|
+
let _checks = null;
|
|
545
|
+
function formatChecks () {
|
|
546
|
+
return _checks || (_checks = {
|
|
547
|
+
email,
|
|
548
|
+
date,
|
|
549
|
+
'date-time': dateTime,
|
|
550
|
+
time,
|
|
551
|
+
duration,
|
|
552
|
+
uuid,
|
|
553
|
+
uri,
|
|
554
|
+
'uri-reference': uriReference,
|
|
555
|
+
ipv4,
|
|
556
|
+
ipv6,
|
|
557
|
+
hostname,
|
|
558
|
+
regex: (s) => { try { new RegExp(s, 'u'); return true; } catch { return false; } },
|
|
559
|
+
'json-pointer': jsonPointer,
|
|
560
|
+
'relative-json-pointer': relativeJsonPointer,
|
|
561
|
+
'uri-template': uriTemplate,
|
|
562
|
+
iri,
|
|
563
|
+
'iri-reference': iriReference,
|
|
564
|
+
'idn-email': idnEmail,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
module.exports = { formatChecks, IPV4, DURATION_RE, IPV6_FULL, ipv4Range, URI_FAST, date, ipv4, dateTime, ipv6, hostname, uri, uriReference, uriWalk: _uriWalk, uuid, time, noReserved, jsonPointer, relativeJsonPointer, uriTemplate, email, duration, iri, iriReference, idnEmail, DURATION_RE, URI_TEMPLATE_EXPR };
|
package/lib/interpreter-core.js
CHANGED
|
@@ -288,33 +288,9 @@ function multipleOfOk(d, m) {
|
|
|
288
288
|
return Math.abs(q - Math.round(q)) < 1e-9;
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
// The built-in format checks
|
|
292
|
-
// the engine loads without
|
|
293
|
-
// never need.
|
|
294
|
-
let _formatChecks = null;
|
|
295
|
-
function buildFormatChecks() {
|
|
296
|
-
const _formats = require('./formats');
|
|
297
|
-
return {
|
|
298
|
-
email: _formats.email,
|
|
299
|
-
date: _formats.date,
|
|
300
|
-
'date-time': _formats.dateTime,
|
|
301
|
-
time: _formats.time,
|
|
302
|
-
duration: _formats.duration,
|
|
303
|
-
uuid: _formats.uuid,
|
|
304
|
-
uri: _formats.uri,
|
|
305
|
-
'uri-reference': _formats.uriReference,
|
|
306
|
-
ipv4: _formats.ipv4,
|
|
307
|
-
ipv6: _formats.ipv6,
|
|
308
|
-
hostname: _formats.hostname,
|
|
309
|
-
regex: (s) => { try { new RegExp(s, 'u'); return true; } catch { return false; } },
|
|
310
|
-
'json-pointer': _formats.jsonPointer,
|
|
311
|
-
'relative-json-pointer': _formats.relativeJsonPointer,
|
|
312
|
-
'uri-template': _formats.uriTemplate,
|
|
313
|
-
iri: _formats.iri,
|
|
314
|
-
'iri-reference': _formats.iriReference,
|
|
315
|
-
'idn-email': _formats.idnEmail,
|
|
316
|
-
};
|
|
317
|
-
}
|
|
291
|
+
// The built-in format checks come from lib/formats.js (formatChecks), loaded
|
|
292
|
+
// on the first schema that names a format: the engine loads without it, which
|
|
293
|
+
// schemas without `format` never need.
|
|
318
294
|
|
|
319
295
|
// ---------- evaluation ----------
|
|
320
296
|
|
|
@@ -454,7 +430,7 @@ class Plan {
|
|
|
454
430
|
this.formatFn = null;
|
|
455
431
|
if (schema.format !== undefined) {
|
|
456
432
|
const uf = interp.userFormats;
|
|
457
|
-
const fc = (uf && typeof uf[schema.format] === 'function') ? uf[schema.format] : (
|
|
433
|
+
const fc = (uf && typeof uf[schema.format] === 'function') ? uf[schema.format] : require('./formats').formatChecks()[schema.format];
|
|
458
434
|
this.formatFn = fc || null;
|
|
459
435
|
}
|
|
460
436
|
this.hasString = this.minLength !== undefined || this.maxLength !== undefined || this.pattern !== null || this.formatFn !== null;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// What a change to a schema does to documents: which ones the old schema
|
|
4
|
+
// accepted and the new one rejects (a client sending what used to pass gets a
|
|
5
|
+
// 400), and which ones the new one accepts and the old one did not (a rule
|
|
6
|
+
// was loosened). Reading two schemas side by side tells you which keywords
|
|
7
|
+
// changed; it does not tell you which documents change verdict, and that is
|
|
8
|
+
// the question before a deploy. The documents come from `generate` in
|
|
9
|
+
// lib/verify.js, made from both schemas, half of them with one rule broken,
|
|
10
|
+
// plus the values on either side of every bound, plus the project's own
|
|
11
|
+
// samples. Generated documents are a sample, not a proof: a change this
|
|
12
|
+
// finds is real, one it does not find may still exist.
|
|
13
|
+
|
|
14
|
+
const { Validator } = require('../index.js');
|
|
15
|
+
const { generate, readSamples } = require('./verify');
|
|
16
|
+
|
|
17
|
+
const clone = (v) => (v === undefined ? v : JSON.parse(JSON.stringify(v)));
|
|
18
|
+
const errorKey = (e) => `${e.keyword || ''}@${e.instancePath || ''}`;
|
|
19
|
+
const first = (errors) => (errors && errors.length ? errors[0] : { keyword: 'validation', instancePath: '', message: 'validation failed' });
|
|
20
|
+
|
|
21
|
+
// `samples`: paths of JSON or JSON Lines files, or directories of them.
|
|
22
|
+
function diffSchemas (oldSchema, newSchema, { generate: count = 200, seed = 1, samples = [] } = {}) {
|
|
23
|
+
const before = new Validator(oldSchema);
|
|
24
|
+
const after = new Validator(newSchema);
|
|
25
|
+
const docs = [];
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
const add = (doc, source) => {
|
|
28
|
+
if (doc === undefined) return;
|
|
29
|
+
const key = JSON.stringify(doc);
|
|
30
|
+
if (seen.has(key)) return;
|
|
31
|
+
seen.add(key);
|
|
32
|
+
docs.push({ doc, source });
|
|
33
|
+
};
|
|
34
|
+
for (const d of generate(oldSchema, count, seed, (x) => before.isValidObject(clone(x)))) add(d, 'generated from the old schema');
|
|
35
|
+
for (const d of generate(newSchema, count, seed + 1, (x) => after.isValidObject(clone(x)))) add(d, 'generated from the new schema');
|
|
36
|
+
for (const s of readSamples(samples)) if (!s.bad) add(s.doc, s.source);
|
|
37
|
+
|
|
38
|
+
const nowRejected = [];
|
|
39
|
+
const nowAccepted = [];
|
|
40
|
+
let bothAccept = 0;
|
|
41
|
+
let bothReject = 0;
|
|
42
|
+
for (const { doc, source } of docs) {
|
|
43
|
+
const a = before.validate(clone(doc));
|
|
44
|
+
const b = after.validate(clone(doc));
|
|
45
|
+
if (a.valid && b.valid) bothAccept++;
|
|
46
|
+
else if (!a.valid && !b.valid) bothReject++;
|
|
47
|
+
else if (a.valid) nowRejected.push({ doc, source, error: first(b.errors) });
|
|
48
|
+
else nowAccepted.push({ doc, source, error: first(a.errors) });
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
documents: docs.length,
|
|
52
|
+
bothAccept,
|
|
53
|
+
bothReject,
|
|
54
|
+
nowRejected: grouped(nowRejected),
|
|
55
|
+
nowAccepted: grouped(nowAccepted),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The documents of one kind, grouped by the rule that decides them (the
|
|
60
|
+
// first error's keyword and place), each group with one example: one of the
|
|
61
|
+
// project's own documents when the group holds one, since that is a document
|
|
62
|
+
// somebody actually sends, else the first generated one.
|
|
63
|
+
const generated = (source) => source.startsWith('generated');
|
|
64
|
+
function grouped (list) {
|
|
65
|
+
const groups = new Map();
|
|
66
|
+
for (const item of list) {
|
|
67
|
+
const key = errorKey(item.error);
|
|
68
|
+
let g = groups.get(key);
|
|
69
|
+
if (g === undefined) groups.set(key, (g = { rule: key, message: item.error.message, count: 0, samples: 0, example: item.doc, source: item.source }));
|
|
70
|
+
g.count++;
|
|
71
|
+
if (!generated(item.source)) { g.samples++; if (generated(g.source)) { g.example = item.doc; g.source = item.source; } }
|
|
72
|
+
}
|
|
73
|
+
return { count: list.length, rules: [...groups.values()].sort((x, y) => y.count - x.count) };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function breaking (report) { return report.nowRejected.count > 0; }
|
|
77
|
+
|
|
78
|
+
function renderDiff (report, { json = false, oldName = 'old', newName = 'new' } = {}) {
|
|
79
|
+
if (json) return JSON.stringify(report, null, 2);
|
|
80
|
+
const lines = [];
|
|
81
|
+
const show = (doc) => { const s = JSON.stringify(doc); return s.length > 120 ? s.slice(0, 117) + '...' : s; };
|
|
82
|
+
lines.push(`${report.documents} documents: ${report.bothAccept} accepted by both, ${report.bothReject} rejected by both, ${report.nowRejected.count} rejected only by ${newName}, ${report.nowAccepted.count} accepted only by ${newName}`);
|
|
83
|
+
if (report.nowRejected.count) {
|
|
84
|
+
lines.push('');
|
|
85
|
+
lines.push(`Rejected by ${newName}, accepted by ${oldName} (a client sending these gets a rejection after the change):`);
|
|
86
|
+
for (const g of report.nowRejected.rules) {
|
|
87
|
+
lines.push(` ${g.rule}: ${g.count} ${g.count === 1 ? 'document' : 'documents'}${g.samples ? `, ${g.samples} of them the project's own` : ''}, ${g.message}`);
|
|
88
|
+
lines.push(` ${show(g.example)}${generated(g.source) ? '' : ` (${g.source})`}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (report.nowAccepted.count) {
|
|
92
|
+
lines.push('');
|
|
93
|
+
lines.push(`Accepted by ${newName}, rejected by ${oldName} (what ${oldName} refused with):`);
|
|
94
|
+
for (const g of report.nowAccepted.rules) {
|
|
95
|
+
lines.push(` ${g.rule}: ${g.count} ${g.count === 1 ? 'document' : 'documents'}${g.samples ? `, ${g.samples} of them the project's own` : ''}, ${g.message}`);
|
|
96
|
+
lines.push(` ${show(g.example)}${generated(g.source) ? '' : ` (${g.source})`}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!report.nowRejected.count && !report.nowAccepted.count) lines.push('No document changed verdict.');
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = { diffSchemas, renderDiff, breaking };
|
package/lib/shape-classifier.js
CHANGED
|
@@ -24,7 +24,7 @@ const TIER0_PRIMITIVE_ALLOWED = new Set([
|
|
|
24
24
|
'type', 'enum', 'const',
|
|
25
25
|
'minLength', 'maxLength',
|
|
26
26
|
'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum',
|
|
27
|
-
'multipleOf',
|
|
27
|
+
'multipleOf', 'format',
|
|
28
28
|
...META_KEYS,
|
|
29
29
|
]);
|
|
30
30
|
|
|
@@ -54,6 +54,11 @@ function isTier0Primitive(schema) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
if (schema.const !== undefined && !isPrimitiveEnumValue(schema.const)) return false;
|
|
57
|
+
// `format` on a string, by name: tier 0 checks it with the table the
|
|
58
|
+
// interpreted engine reads (formats.formatChecks) or the caller's own
|
|
59
|
+
// function. A format on another type is a compat check for numbers, which
|
|
60
|
+
// the full engines handle.
|
|
61
|
+
if (schema.format !== undefined && (schema.type !== 'string' || typeof schema.format !== 'string')) return false;
|
|
57
62
|
return true;
|
|
58
63
|
}
|
|
59
64
|
|
package/lib/tier0.js
CHANGED
|
@@ -49,7 +49,17 @@ const _hop = Object.prototype.hasOwnProperty;
|
|
|
49
49
|
// Deno removes the __proto__ accessor; read the prototype another way there.
|
|
50
50
|
const _PN = typeof Bun === 'undefined' && ({}).__proto__ === Object.prototype; // JavaScriptCore goes through Object.getPrototypeOf, see js-compiler's _PN
|
|
51
51
|
|
|
52
|
-
function
|
|
52
|
+
// The check for a string's `format`: the caller's own function by that name,
|
|
53
|
+
// else the built-in one, else none (an unknown format is an annotation), as
|
|
54
|
+
// the interpreted engine picks it.
|
|
55
|
+
function formatFnOf(propSchema, userFormats) {
|
|
56
|
+
const f = propSchema.format;
|
|
57
|
+
if (typeof f !== 'string') return null;
|
|
58
|
+
if (userFormats && typeof userFormats[f] === 'function') return userFormats[f];
|
|
59
|
+
return require('./formats').formatChecks()[f] || null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function primConstraint(key, propSchema, userFormats) {
|
|
53
63
|
const t = propSchema.type;
|
|
54
64
|
const hasEnum = Array.isArray(propSchema.enum);
|
|
55
65
|
const hasConst = propSchema.const !== undefined;
|
|
@@ -75,14 +85,15 @@ function primConstraint(key, propSchema) {
|
|
|
75
85
|
exclMin: typeof propSchema.exclusiveMinimum === 'number' ? propSchema.exclusiveMinimum : 0,
|
|
76
86
|
exclMax: typeof propSchema.exclusiveMaximum === 'number' ? propSchema.exclusiveMaximum : 0,
|
|
77
87
|
multipleOf: typeof propSchema.multipleOf === 'number' ? propSchema.multipleOf : 0,
|
|
88
|
+
formatFn: propSchema.type === 'string' ? formatFnOf(propSchema, userFormats) : null,
|
|
78
89
|
};
|
|
79
90
|
}
|
|
80
91
|
|
|
81
|
-
function buildTier0Plan(schema) {
|
|
92
|
+
function buildTier0Plan(schema, userFormats) {
|
|
82
93
|
if (schema.type !== 'object') {
|
|
83
94
|
return {
|
|
84
95
|
isPrimitive: true,
|
|
85
|
-
constraints: [primConstraint('__root__', schema)],
|
|
96
|
+
constraints: [primConstraint('__root__', schema, userFormats)],
|
|
86
97
|
requiredMask: 0,
|
|
87
98
|
additionalAllowed: true,
|
|
88
99
|
knownKeys: null,
|
|
@@ -96,7 +107,7 @@ function buildTier0Plan(schema) {
|
|
|
96
107
|
let requiredMask = 0;
|
|
97
108
|
for (let i = 0; i < keys.length; i++) {
|
|
98
109
|
const k = keys[i];
|
|
99
|
-
constraints[i] = primConstraint(k, props[k]);
|
|
110
|
+
constraints[i] = primConstraint(k, props[k], userFormats);
|
|
100
111
|
knownKeys.add(k);
|
|
101
112
|
if (required && required.has(k)) requiredMask |= (1 << i);
|
|
102
113
|
}
|
|
@@ -128,6 +139,7 @@ function checkPrimitive(c, v) {
|
|
|
128
139
|
const maxLen = c.maxLen;
|
|
129
140
|
if (minLen >= 0) { const l = v.length; if (l < minLen) return false; if (l < minLen * 2 && codePointLength(v) < minLen) return false; }
|
|
130
141
|
if (maxLen >= 0) { const l = v.length; if (l > maxLen && codePointLength(v) > maxLen) return false; }
|
|
142
|
+
if (c.formatFn !== null && !c.formatFn(v)) return false;
|
|
131
143
|
} else if (m === T_INTEGER) {
|
|
132
144
|
if (typeof v !== 'number' || !Number.isInteger(v)) return false;
|
|
133
145
|
const f = c.numFlags;
|
|
@@ -186,6 +198,7 @@ function tier0ValidateObject(plan, data) {
|
|
|
186
198
|
const maxLen = c.maxLen;
|
|
187
199
|
if (minLen >= 0) { const l = v.length; if (l < minLen) return false; if (l < minLen * 2 && codePointLength(v) < minLen) return false; }
|
|
188
200
|
if (maxLen >= 0) { const l = v.length; if (l > maxLen && codePointLength(v) > maxLen) return false; }
|
|
201
|
+
if (c.formatFn !== null && !c.formatFn(v)) return false;
|
|
189
202
|
} else if (m === T_INTEGER) {
|
|
190
203
|
if (typeof v !== 'number' || !Number.isInteger(v)) return false;
|
|
191
204
|
const f = c.numFlags;
|
package/lib/validator-core.js
CHANGED
|
@@ -19,10 +19,17 @@ const { normalizeKeywords, schemaUsesKeywords } = require('./keywords');
|
|
|
19
19
|
// bundle built from lite.js never contains it. Until something registers, the
|
|
20
20
|
// core behaves exactly as it does where `new Function` is blocked: every
|
|
21
21
|
// schema runs on the interpreted engine, which passes the same test suite.
|
|
22
|
-
// Whether this process runs code compiled ahead of time, where
|
|
23
|
-
//
|
|
24
|
-
//
|
|
22
|
+
// Whether this process runs code compiled ahead of time, where code made at
|
|
23
|
+
// run time with `new Function` falls to an interpreter and ata's interpreted
|
|
24
|
+
// engine, compiled with the program, is six times faster (481 against 78 ns
|
|
25
|
+
// a document under bun build --compile --aot). Bun resolves the package's
|
|
26
|
+
// `"aot"` export condition there, which loads index.aot.js and marks the
|
|
27
|
+
// process (_markAotHost); `Bun.isAOT` was the earlier signal, and
|
|
28
|
+
// ATA_ENGINE=interpreter says so from outside.
|
|
29
|
+
let _aotEntry = false;
|
|
30
|
+
function _markAotHost() { _aotEntry = true; }
|
|
25
31
|
function aotHost() {
|
|
32
|
+
if (_aotEntry) return true;
|
|
26
33
|
if (env('ATA_ENGINE') === 'interpreter') return true;
|
|
27
34
|
const b = typeof Bun !== 'undefined' ? Bun : undefined;
|
|
28
35
|
return !!(b && b.isAOT === true);
|
|
@@ -1213,7 +1220,13 @@ class Validator {
|
|
|
1213
1220
|
// once per call: deciding first and walking again for the errors would
|
|
1214
1221
|
// run the keyword twice, the second time on what the first left.
|
|
1215
1222
|
const once = interp.dataContext === true;
|
|
1216
|
-
|
|
1223
|
+
// A schema tier 0 takes answers its verdict there, with or without a
|
|
1224
|
+
// call to validate() first: the shared walk over a flat plan beats the
|
|
1225
|
+
// closure tree, 76 to 60 ns on Node and 85 to 49 under Bun's compiler,
|
|
1226
|
+
// where the closures are not typed. Not for custom keywords, which
|
|
1227
|
+
// tier 0 does not know.
|
|
1228
|
+
const t0 = !preprocess && !once && !this._usesKeywords ? _tier0Verdict(this) : null;
|
|
1229
|
+
if (!preprocess && !once) this._fastVerdict = t0 !== null ? t0 : (d) => interp.isValid(d);
|
|
1217
1230
|
if (preprocess && !once) _preVerdict = (d) => { preprocess(d); return interp.isValid(d); };
|
|
1218
1231
|
// abortEarly is a documented contract, not a property of whichever engine
|
|
1219
1232
|
// answered: it promises the frozen ATA9000 stub instead of a detailed
|
|
@@ -2332,6 +2345,22 @@ for (const [name, pick] of [
|
|
|
2332
2345
|
|
|
2333
2346
|
// Cold start: an interpreted twin answers a large schema's first calls where
|
|
2334
2347
|
// the code generator is registered (see coldTwin in index.js). Lite has none.
|
|
2348
|
+
// Whether this validator, in the browser build before the generator has
|
|
2349
|
+
// come, counts its calls so a busy one moves to generated code (lib/tiering.js).
|
|
2350
|
+
function _ticks(self) {
|
|
2351
|
+
return _codegen === null && _tiering !== null && !self._interpretOnly && !env('ATA_FORCE_NAPI');
|
|
2352
|
+
}
|
|
2353
|
+
// The tier-0 verdict for a schema it takes, or null: counting calls where
|
|
2354
|
+
// _ticks says so.
|
|
2355
|
+
function _tier0Verdict(self) {
|
|
2356
|
+
if (require('./shape-classifier').classify(self._schemaObj).tier !== 0) return null;
|
|
2357
|
+
const { buildTier0Plan, tier0Validate } = require('./tier0');
|
|
2358
|
+
const plan = buildTier0Plan(self._schemaObj, self._userFormats);
|
|
2359
|
+
if (!_ticks(self)) return (d) => tier0Validate(plan, d);
|
|
2360
|
+
const tick = _tiering.counter(self);
|
|
2361
|
+
return (d) => { tick(); return tier0Validate(plan, d); };
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2335
2364
|
Validator.prototype._coldTwin = function () {
|
|
2336
2365
|
const cg = _loadCodegen();
|
|
2337
2366
|
return cg === null || cg.coldTwin === undefined ? null : cg.coldTwin(this, Validator);
|
|
@@ -2363,21 +2392,13 @@ _defineLazyMethod('isValidObject', (self) => {
|
|
|
2363
2392
|
return self.isValidObject(data);
|
|
2364
2393
|
}
|
|
2365
2394
|
// Lazy: classify + build tier 0 plan on first call, not in constructor.
|
|
2366
|
-
const
|
|
2367
|
-
if (
|
|
2368
|
-
//
|
|
2369
|
-
|
|
2370
|
-
const _plan = buildTier0Plan(self._schemaObj);
|
|
2395
|
+
const _t0 = _tier0Verdict(self);
|
|
2396
|
+
if (_t0 !== null) {
|
|
2397
|
+
// In the browser build the verdict counts its calls (see _tier0Verdict);
|
|
2398
|
+
// elsewhere the second call builds the generated code behind it.
|
|
2371
2399
|
let _n = 0;
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
// this method stays on the plan walk, four times slower, for good.
|
|
2375
|
-
const _tick = _codegen === null && _tiering !== null && !self._interpretOnly && !env('ATA_FORCE_NAPI') ? _tiering.counter(self) : null;
|
|
2376
|
-
_bindVerdict(self, _tick !== null ? (d) => {
|
|
2377
|
-
_tick();
|
|
2378
|
-
return tier0Validate(_plan, d);
|
|
2379
|
-
} : (d) => {
|
|
2380
|
-
const r = tier0Validate(_plan, d);
|
|
2400
|
+
_bindVerdict(self, _ticks(self) ? _t0 : (d) => {
|
|
2401
|
+
const r = _t0(d);
|
|
2381
2402
|
if (++_n === 2) {
|
|
2382
2403
|
try { self._ensureCodegen(); } catch {}
|
|
2383
2404
|
}
|
|
@@ -2450,6 +2471,7 @@ Object.defineProperty(module.exports, '_registerCodegen', { value: _registerCode
|
|
|
2450
2471
|
Object.defineProperty(module.exports, '_setCodegenLoader', { value: (load) => { _loadCodegen = load; }, enumerable: false });
|
|
2451
2472
|
Object.defineProperty(module.exports, '_registerTiering', { value: _registerTiering, enumerable: false });
|
|
2452
2473
|
Object.defineProperty(module.exports, '_registerBuiltHook', { value: _registerBuiltHook, enumerable: false });
|
|
2474
|
+
Object.defineProperty(module.exports, '_markAotHost', { value: _markAotHost, enumerable: false });
|
|
2453
2475
|
Object.defineProperty(module.exports, '_codegenRegistered', { value: () => _codegen !== null, enumerable: false });
|
|
2454
2476
|
Object.defineProperty(module.exports, '_tiering', { get: () => _tiering, enumerable: false });
|
|
2455
2477
|
Object.defineProperty(module.exports, '_registerAot', { value: _registerAot, enumerable: false });
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.61.
|
|
3
|
+
"version": "1.61.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",
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
"types": "index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
+
"aot": {
|
|
12
|
+
"types": "./index.d.ts",
|
|
13
|
+
"import": "./index.aot.mjs",
|
|
14
|
+
"require": "./index.aot.js"
|
|
15
|
+
},
|
|
11
16
|
"workerd": {
|
|
12
17
|
"types": "./index.d.ts",
|
|
13
18
|
"import": "./index.edge.mjs",
|
|
@@ -79,6 +84,11 @@
|
|
|
79
84
|
"types": "./compiled-bare-rich.d.ts",
|
|
80
85
|
"default": "./lib/compiled-bare-rich.js"
|
|
81
86
|
},
|
|
87
|
+
"./compiled-standard": {
|
|
88
|
+
"types": "./compiled-standard.d.ts",
|
|
89
|
+
"import": "./compiled-standard.mjs",
|
|
90
|
+
"default": "./lib/compiled-standard.js"
|
|
91
|
+
},
|
|
82
92
|
"./lite": {
|
|
83
93
|
"types": "./lite.d.ts",
|
|
84
94
|
"import": "./lite.mjs",
|
|
@@ -98,7 +108,10 @@
|
|
|
98
108
|
},
|
|
99
109
|
"sideEffects": [
|
|
100
110
|
"./codegen.js",
|
|
101
|
-
"./codegen.mjs"
|
|
111
|
+
"./codegen.mjs",
|
|
112
|
+
"./lib/aot-mark.js",
|
|
113
|
+
"./index.aot.js",
|
|
114
|
+
"./index.aot.mjs"
|
|
102
115
|
],
|
|
103
116
|
"bin": {
|
|
104
117
|
"ata": "bin/ata.js"
|
|
@@ -170,6 +183,8 @@
|
|
|
170
183
|
},
|
|
171
184
|
"files": [
|
|
172
185
|
"index.js",
|
|
186
|
+
"index.aot.js",
|
|
187
|
+
"index.aot.mjs",
|
|
173
188
|
"index.mjs",
|
|
174
189
|
"index.node.mjs",
|
|
175
190
|
"index.bundle.mjs",
|
|
@@ -207,6 +222,8 @@
|
|
|
207
222
|
"compiled-bare.mjs",
|
|
208
223
|
"compiled-bare.d.ts",
|
|
209
224
|
"compiled-bare-rich.d.ts",
|
|
225
|
+
"compiled-standard.mjs",
|
|
226
|
+
"compiled-standard.d.ts",
|
|
210
227
|
"lite.js",
|
|
211
228
|
"lite.mjs",
|
|
212
229
|
"lite.d.ts",
|
|
@@ -221,13 +238,13 @@
|
|
|
221
238
|
"codegen.d.ts"
|
|
222
239
|
],
|
|
223
240
|
"optionalDependencies": {
|
|
224
|
-
"@ata-validator/native-darwin-arm64": "1.61.
|
|
225
|
-
"@ata-validator/native-darwin-x64": "1.61.
|
|
226
|
-
"@ata-validator/native-linux-arm64-gnu": "1.61.
|
|
227
|
-
"@ata-validator/native-linux-arm64-musl": "1.61.
|
|
228
|
-
"@ata-validator/native-linux-x64-gnu": "1.61.
|
|
229
|
-
"@ata-validator/native-linux-x64-musl": "1.61.
|
|
230
|
-
"@ata-validator/native-win32-x64": "1.61.
|
|
241
|
+
"@ata-validator/native-darwin-arm64": "1.61.3",
|
|
242
|
+
"@ata-validator/native-darwin-x64": "1.61.3",
|
|
243
|
+
"@ata-validator/native-linux-arm64-gnu": "1.61.3",
|
|
244
|
+
"@ata-validator/native-linux-arm64-musl": "1.61.3",
|
|
245
|
+
"@ata-validator/native-linux-x64-gnu": "1.61.3",
|
|
246
|
+
"@ata-validator/native-linux-x64-musl": "1.61.3",
|
|
247
|
+
"@ata-validator/native-win32-x64": "1.61.3"
|
|
231
248
|
},
|
|
232
249
|
"peerDependencies": {
|
|
233
250
|
"yaml": "^2.0.0"
|