ata-validator 1.49.0 → 1.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -4
- package/aot.d.ts +40 -0
- package/aot.mjs +1 -1
- package/bin/ata.js +14 -1
- package/build.d.ts +1 -1
- package/error-runtime.d.ts +24 -0
- package/error-runtime.js +40 -0
- package/error-runtime.mjs +3 -0
- package/index.browser.mjs +14 -5
- package/index.edge.js +14 -0
- package/index.edge.mjs +13 -0
- package/index.js +8 -1014
- package/lib/aot-build.js +211 -68
- package/lib/aot-impl.js +207 -7
- package/lib/codegen-engine.js +1024 -0
- package/lib/error-code-of.js +22 -0
- package/lib/error-runtime-entry.js +85 -0
- package/lib/error-runtime.js +152 -0
- package/lib/error-sort-rank.js +61 -0
- package/lib/interpreter-core.js +1356 -0
- package/lib/interpreter.js +8 -1260
- package/lib/public-api.js +80 -0
- package/lib/schema-runtime-entry.js +21 -0
- package/lib/schema-strip.js +115 -0
- package/lib/tools.mjs +21 -0
- package/lib/validator-core.js +17 -5
- package/lib/version.js +1 -1
- package/package.json +28 -8
package/README.md
CHANGED
|
@@ -43,10 +43,12 @@ npm install ata-validator --omit=optional
|
|
|
43
43
|
|
|
44
44
|
or set `ATA_NO_NATIVE=1` at runtime. Typical schemas compile to specialized JS; shapes the compiler cannot represent (some `$dynamicRef`, cyclic `$ref`, unusual keyword interactions) fall back to an interpreted engine, so every schema validates in every environment. The pure-JS setup scores the same on the official suite as the native one, 1301 of 1301 Draft 2020-12 cases. The buffer and parallel APIs (`isValid` on raw buffers, `isValidPrepadded`, `isValidNDJSON`, `isValidParallel`, `countValid`, `batchIsValid`, `validateAndParse`) work without the addon too, answering through the same checks as `isValidJSON()`, slower than the addon: 177 ns against 92 for a small document on Node 25. In a browser, which has no `Buffer`, they throw and name the methods to use instead.
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
In Cloudflare Workers and Vercel Edge, where generated code cannot run, `ata-validator`
|
|
47
|
+
resolves to an entry without the code generator through the `workerd` and `edge-light` export
|
|
48
|
+
conditions: the same names and answers, and a Worker bundled by wrangler goes from 179.1 to
|
|
49
|
+
70.4 KiB gzipped. On a page whose Content-Security-Policy blocks `unsafe-eval`, or in a
|
|
50
|
+
browser extension, no condition tells the bundler, so import `ata-validator/lite` there: the
|
|
51
|
+
same `Validator` on the interpreted engine alone.
|
|
50
52
|
|
|
51
53
|
On Deno the same package is `npm:ata-validator` or `jsr:@ata/validator`; the JSR package
|
|
52
54
|
re-exports the npm one at the same version, and CI runs the official suite under Deno. The
|
|
@@ -574,6 +576,19 @@ npx ata build 'schemas/*.json' --out-dir build/validators --check
|
|
|
574
576
|
|
|
575
577
|
Run with `--watch` during development for incremental rebuilds.
|
|
576
578
|
|
|
579
|
+
In a production build (`--no-source`, or `NODE_ENV=production`), `ata build` can leave the
|
|
580
|
+
error collector out of the modules: each one carries its verdict and its schema, and takes
|
|
581
|
+
its errors from one `ata-errors.mjs` the build writes beside them. The build does this only
|
|
582
|
+
where it makes the output smaller, so a single small schema keeps its self-contained module.
|
|
583
|
+
The errors are the ones `new Validator(schema, { richErrors: false })` reports, in the same
|
|
584
|
+
order. Seven SchemaStore schemas imported into one app, minified with esbuild, go from 148.9 KB
|
|
585
|
+
to 63.3 KB gzipped, and from 35.8 ms to 13.8 ms to import with code generation blocked. A
|
|
586
|
+
rejected document costs more this way, since its errors come from an interpreted walk:
|
|
587
|
+
`--errors inline` keeps every module self-contained. `--mode schema` writes each module as
|
|
588
|
+
its schema and takes the verdict from the runtime too: forty SchemaStore schemas in one app
|
|
589
|
+
come to 101.6 KB gzipped against 212.6 KB as compact modules, and load and check each once in
|
|
590
|
+
23.0 ms against 35.5, while a check once warm costs about five times what generated code costs.
|
|
591
|
+
|
|
577
592
|
Bundle sizes for the 10-field user schema in `tests/fixtures/error-dx/user.schema.json`,
|
|
578
593
|
minified and gzipped, measured with `bun build --minify --target=browser` on ata 1.45.0:
|
|
579
594
|
|
package/aot.d.ts
CHANGED
|
@@ -24,6 +24,46 @@ export function toStandalone(validator: Validator<unknown>): string | null;
|
|
|
24
24
|
/** Emit one standalone module (no runtime dependency) for one validator. */
|
|
25
25
|
export function toStandaloneModule(validator: Validator<unknown>, opts?: StandaloneOptions): string;
|
|
26
26
|
|
|
27
|
+
/** Options of toCompactModule. */
|
|
28
|
+
export interface CompactModuleOptions {
|
|
29
|
+
/** Import specifier of the error runtime the module takes its errors from. */
|
|
30
|
+
errorRuntime: string;
|
|
31
|
+
/** Module format for the emitted source. Default: 'esm'. */
|
|
32
|
+
format?: 'esm' | 'cjs';
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Emit a compact module: the verdict function and the schema without its
|
|
38
|
+
* descriptive keywords, with error detail from the shared runtime at
|
|
39
|
+
* `opts.errorRuntime` (see errorRuntimeSource). The errors are the ones the
|
|
40
|
+
* runtime Validator reports with `richErrors: false`, in its order, and
|
|
41
|
+
* `errorMessage` is not applied, as in toStandaloneModule. Null when the validator depends on
|
|
42
|
+
* something outside its schema (custom keywords or formats, registered
|
|
43
|
+
* schemas, options other than the defaults); write the self-contained module
|
|
44
|
+
* then. `ata build` makes this choice by itself.
|
|
45
|
+
*/
|
|
46
|
+
export function toCompactModule(validator: Validator<unknown>, opts: CompactModuleOptions): string | null;
|
|
47
|
+
|
|
48
|
+
/** Why a validator cannot be written as a compact module, or null when it can. */
|
|
49
|
+
export function compactDecline(validator: Validator<unknown>): string | null;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Emit a schema module: the schema without its descriptive keywords and a few
|
|
53
|
+
* lines; the verdict and the errors come from the runtime at `opts.runtime`
|
|
54
|
+
* (errorRuntimeSource with `schemaMode`). Far smaller and quicker to load than
|
|
55
|
+
* generated code, about five times slower per check once warm. Answers as the
|
|
56
|
+
* runtime Validator does with `richErrors: false`. Null where
|
|
57
|
+
* toCompactModule would be null. `ata build --mode schema` writes these.
|
|
58
|
+
*/
|
|
59
|
+
export function toSchemaModule(validator: Validator<unknown>, opts: { runtime: string; format?: 'esm' | 'cjs' }): string | null;
|
|
60
|
+
|
|
61
|
+
/** Source of the runtime compact modules (and, with `schemaMode`, schema
|
|
62
|
+
* modules) import. With `schemas`, the validators or schemas whose modules
|
|
63
|
+
* will import it, parts none of them needs are left out. Node only: it is put
|
|
64
|
+
* together from the library's files. */
|
|
65
|
+
export function errorRuntimeSource(format?: 'esm' | 'cjs', schemas?: ReadonlyArray<unknown> | null, schemaMode?: boolean): string;
|
|
66
|
+
|
|
27
67
|
/** Emit a module of per-schema compiled validators that `loadBundle` consumes. */
|
|
28
68
|
export function bundle(V: ValidatorClass, schemas: ReadonlyArray<JSONSchema | boolean | object>, opts?: StandaloneOptions): string;
|
|
29
69
|
|
package/aot.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import mod from './lib/aot-impl.js';
|
|
2
|
-
export const { toStandalone, toStandaloneModule, bundle, bundleStandalone, bundleCompact, loadBundle } = mod;
|
|
2
|
+
export const { toStandalone, toStandaloneModule, toCompactModule, toSchemaModule, compactDecline, errorRuntimeSource, bundle, bundleStandalone, bundleCompact, loadBundle } = mod;
|
|
3
3
|
export default mod;
|
package/bin/ata.js
CHANGED
|
@@ -45,6 +45,12 @@ Build options:
|
|
|
45
45
|
--abort-early Use stub errors (smallest bundle)
|
|
46
46
|
--one-pass Report errors with the runtime's one-pass function: faster on
|
|
47
47
|
rejected documents, a module 1.3 to 1.7x the size
|
|
48
|
+
--errors <mode> Where error detail comes from: auto | inline | shared. auto
|
|
49
|
+
(default) has modules share one error runtime file when that
|
|
50
|
+
makes the output smaller; production builds only (--no-source)
|
|
51
|
+
--mode <mode> code (default) | schema. schema writes each module as its
|
|
52
|
+
schema and takes the verdict and errors from one runtime
|
|
53
|
+
file: far smaller and quicker to load, slower per check
|
|
48
54
|
--check Check (don't write); exit 1 if any output is stale
|
|
49
55
|
--cache-file <path> Cache file for incremental builds (default: cache disabled)
|
|
50
56
|
--max-size <bytes> Fail build if any compiled module exceeds this gzipped size
|
|
@@ -94,6 +100,8 @@ function parseArgs(argv) {
|
|
|
94
100
|
if (a === '--no-types') { out.opts.types = false; continue; }
|
|
95
101
|
if (a === '--abort-early') { out.opts.abortEarly = true; continue; }
|
|
96
102
|
if (a === '--one-pass') { out.opts.onePass = true; continue; }
|
|
103
|
+
if (a === '--errors') { out.opts.errors = argv[++i]; continue; }
|
|
104
|
+
if (a === '--mode') { out.opts.mode = argv[++i]; continue; }
|
|
97
105
|
if (a === '--check') { out.opts.check = true; continue; }
|
|
98
106
|
if (a === '--strict') { out.opts.strict = true; continue; }
|
|
99
107
|
if (a === '--strict-schema') { out.opts.strictSchema = true; continue; }
|
|
@@ -296,6 +304,8 @@ function cmdBuild(args) {
|
|
|
296
304
|
suffix: args.opts.suffix,
|
|
297
305
|
abortEarly: !!args.opts.abortEarly,
|
|
298
306
|
onePass: args.opts.onePass === true ? true : undefined,
|
|
307
|
+
errors: args.opts.errors,
|
|
308
|
+
mode: args.opts.mode,
|
|
299
309
|
check: !!args.opts.check,
|
|
300
310
|
maxSize: args.opts.maxSize,
|
|
301
311
|
strict: !!args.opts.strict,
|
|
@@ -312,7 +322,10 @@ function cmdBuild(args) {
|
|
|
312
322
|
return;
|
|
313
323
|
}
|
|
314
324
|
for (const c of report.compiled) {
|
|
315
|
-
process.stdout.write(`ata: ${c.input} -> ${c.output} (${c.bytes.toLocaleString()} bytes)\n`);
|
|
325
|
+
process.stdout.write(`ata: ${c.input} -> ${c.output} (${c.bytes.toLocaleString()} bytes${c.errors === 'compact' ? ', shared errors' : c.errors === 'schema' ? ', schema module' : ''})\n`);
|
|
326
|
+
}
|
|
327
|
+
if (report.runtime) {
|
|
328
|
+
process.stdout.write(`ata: error runtime -> ${report.runtime.output} (${report.runtime.bytes.toLocaleString()} bytes, shared by the modules above)\n`);
|
|
316
329
|
}
|
|
317
330
|
for (const c of report.cached) {
|
|
318
331
|
process.stdout.write(`ata: cached ${c.input}\n`);
|
package/build.d.ts
CHANGED
|
@@ -190,7 +190,7 @@ export function compiledSchemaFor(schema: unknown): object;
|
|
|
190
190
|
* {@link compiledEligible} declines, one the emitter cannot compile, and one
|
|
191
191
|
* whose detailed errors the generator cannot produce.
|
|
192
192
|
*/
|
|
193
|
-
export function compiledModuleFor(schema: unknown, opts?: { format?: 'esm' | 'cjs' }): string | null;
|
|
193
|
+
export function compiledModuleFor(schema: unknown, opts?: { format?: 'esm' | 'cjs'; errorRuntime?: string }): string | null;
|
|
194
194
|
|
|
195
195
|
/**
|
|
196
196
|
* The Validator options `fromCompiled()` reproduces, so a plugin can tell
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** One error as a compact module reports it. */
|
|
2
|
+
export interface CompactError {
|
|
3
|
+
code?: string;
|
|
4
|
+
keyword: string;
|
|
5
|
+
instancePath: string;
|
|
6
|
+
schemaPath: string;
|
|
7
|
+
params: Record<string, unknown>;
|
|
8
|
+
message: string;
|
|
9
|
+
docUrl?: string;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The error function a compact module calls for a document its verdict
|
|
15
|
+
* rejected: the errors the runtime Validator reports with
|
|
16
|
+
* `richErrors: false`, in schema order, or null when it finds none.
|
|
17
|
+
*/
|
|
18
|
+
export function createErrors(schema: object, options?: { v1?: boolean }): (data: unknown) => CompactError[] | null;
|
|
19
|
+
|
|
20
|
+
/** The linear-time pattern engine compact modules share (the RegExp-like object it returns has `test`). */
|
|
21
|
+
export function compileSafe(source: string): { test(input: string): boolean };
|
|
22
|
+
|
|
23
|
+
/** The ata-validator version this runtime belongs to. */
|
|
24
|
+
export const ataVersion: string;
|
package/error-runtime.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ata-validator/error-runtime: the error runtime compact modules import, for a
|
|
4
|
+
// bundler plugin that writes compact modules into the application's own
|
|
5
|
+
// bundle. The bundler then keeps one copy, shared with ata-validator/compiled
|
|
6
|
+
// where both are used. `ata build` writes the same runtime as a file instead
|
|
7
|
+
// (errorRuntimeSource in ata-validator/aot).
|
|
8
|
+
const ataVersion = require('./lib/version.js');
|
|
9
|
+
|
|
10
|
+
// The interpreter and the code table load on the first call, not at import:
|
|
11
|
+
// a compact module calls this only when a document it rejected is asked for
|
|
12
|
+
// its errors.
|
|
13
|
+
function createErrors(schema, options) {
|
|
14
|
+
const { createErrors: create } = require('./lib/error-runtime-entry.js');
|
|
15
|
+
const { codeOf } = require('./lib/error-code-of.js');
|
|
16
|
+
return create(schema, codeOf, options);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// The linear-time pattern engine, which a compact module imports from here
|
|
20
|
+
// instead of embedding its own copy.
|
|
21
|
+
let _safe = null;
|
|
22
|
+
function compileSafe(src) {
|
|
23
|
+
if (_safe === null) _safe = require('./lib/safe-regex.js').compileSafe;
|
|
24
|
+
return _safe(src);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Warm-up, in a browser only: the runtime's code compiles on its first use,
|
|
28
|
+
// which a page would otherwise pay for on its first rejected document, about
|
|
29
|
+
// a millisecond. When the browser is idle, one small error list is built here
|
|
30
|
+
// instead, once, so the first real one finds the code compiled. Nothing it
|
|
31
|
+
// does reaches a caller.
|
|
32
|
+
const WARM_SCHEMA = { type: 'object', properties: { a: { type: 'string' }, b: { type: 'integer', minimum: 0 }, c: { anyOf: [{ type: 'string' }, { type: 'null' }] }, f: { type: 'string', format: 'email' } }, required: ['d'], additionalProperties: false };
|
|
33
|
+
function warm() { try { createErrors(WARM_SCHEMA, { flat: true })({ a: 1, b: -1, c: 2, f: 'x', e: 0 }); } catch (_) {} }
|
|
34
|
+
if (typeof requestIdleCallback === 'function') { try { requestIdleCallback(warm); } catch (_) {} }
|
|
35
|
+
// In Node the same happens once the event loop is first free after start-up,
|
|
36
|
+
// on a timer that does not keep the process alive: a script that exits at
|
|
37
|
+
// once never runs it, and a server has it done before its first request.
|
|
38
|
+
else if (typeof process === 'object' && process !== null && process.versions && process.versions.node && typeof setTimeout === 'function') { try { const t = setTimeout(warm, 0); if (t && typeof t.unref === 'function') t.unref(); } catch (_) {} }
|
|
39
|
+
|
|
40
|
+
module.exports = { createErrors, compileSafe, ataVersion };
|
package/index.browser.mjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
// Browser ESM entry: the
|
|
2
|
-
//
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// Browser ESM entry, for bundlers: the validator core with the code generator
|
|
2
|
+
// registered, as index.js, but with each tool imported on its own (see
|
|
3
|
+
// lib/tools.mjs) so that the tools an application does not import stay out
|
|
4
|
+
// of its bundle. The native addon is stubbed by the package.json "browser"
|
|
5
|
+
// field.
|
|
6
|
+
import core from './lib/validator-core.js';
|
|
7
|
+
import engine from './lib/codegen-engine.js';
|
|
8
|
+
import { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON } from './lib/tools.mjs';
|
|
9
|
+
|
|
10
|
+
core._registerCodegen(engine);
|
|
11
|
+
|
|
12
|
+
export const { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, defineSchema } = core;
|
|
13
|
+
export { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
|
14
|
+
export default { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, toTypeScript, defineSchema, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
package/index.edge.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// The package for runtimes that cannot run generated code: Cloudflare Workers
|
|
4
|
+
// (the `workerd` export condition) and Vercel Edge (`edge-light`) refuse
|
|
5
|
+
// `new Function`, so the code generator in the full package could only ever
|
|
6
|
+
// fail there and fall back. This entry leaves it out and answers through the
|
|
7
|
+
// interpreted engine, as the full package does in those runtimes: the same
|
|
8
|
+
// names, the same verdicts and errors, a bundle about half the size. Modules
|
|
9
|
+
// from `ata build` and Validator.bundle() load as they do in a browser bundle
|
|
10
|
+
// (lib/aot.browser.js); the emitters are not here, as they are not there.
|
|
11
|
+
const core = require('./lib/validator-core.js');
|
|
12
|
+
core._registerAot(() => require('./lib/aot.browser.js'));
|
|
13
|
+
|
|
14
|
+
module.exports = require('./lib/public-api.js');
|
package/index.edge.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// ESM edge entry (see index.edge.js): the core without the code generator,
|
|
2
|
+
// for Cloudflare Workers and Vercel Edge, with each tool imported on its own
|
|
3
|
+
// (lib/tools.mjs) so the ones an application does not import stay out of its
|
|
4
|
+
// bundle.
|
|
5
|
+
import core from './lib/validator-core.js';
|
|
6
|
+
import aotBrowser from './lib/aot.browser.js';
|
|
7
|
+
import { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON } from './lib/tools.mjs';
|
|
8
|
+
|
|
9
|
+
core._registerAot(() => aotBrowser);
|
|
10
|
+
|
|
11
|
+
export const { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, defineSchema } = core;
|
|
12
|
+
export { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
|
13
|
+
export default { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, toTypeScript, defineSchema, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|