ata-validator 1.55.0 → 1.57.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 +16 -3
- package/bin/ata.js +11 -2
- package/build.d.ts +7 -1
- package/codegen.d.ts +3 -0
- package/codegen.js +10 -0
- package/codegen.mjs +2 -0
- package/index.browser.mjs +13 -7
- package/lib/aot-build.js +6 -1
- package/lib/aot-impl.js +48 -11
- package/lib/aot-order.js +115 -0
- package/lib/codegen-engine.js +12 -3
- package/lib/interpreter-core.js +31 -0
- package/lib/tiering.js +236 -0
- package/lib/ts-gen.js +6 -2
- package/lib/validator-core.js +59 -16
- package/lib/version.js +1 -1
- package/lite.d.ts +3 -1
- package/lite.js +6 -1
- package/package.json +23 -12
package/README.md
CHANGED
|
@@ -46,9 +46,19 @@ or set `ATA_NO_NATIVE=1` at runtime. Typical schemas compile to specialized JS;
|
|
|
46
46
|
In Cloudflare Workers and Vercel Edge, where generated code cannot run, `ata-validator`
|
|
47
47
|
resolves to an entry without the code generator through the `workerd` and `edge-light` export
|
|
48
48
|
conditions: the same names and answers, and a Worker bundled by wrangler goes from 179.1 to
|
|
49
|
-
70.4 KiB gzipped.
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
70.4 KiB gzipped.
|
|
50
|
+
|
|
51
|
+
In a browser bundle the code generator is not part of the page. Every schema starts on the
|
|
52
|
+
interpreted engine, which gives the same answers, and a validator called a thousand times within
|
|
53
|
+
a second loads the generator in the background, through a dynamic import that Vite, webpack and
|
|
54
|
+
Rollup put in a chunk of their own, and is rebuilt on generated code when the page is idle. A
|
|
55
|
+
form, checked as the user types, never loads it. On a react-jsonschema-form page this takes the validator's
|
|
56
|
+
share of the first load from 134.7 KB to about 57 KB gzipped; the generator chunk is about 78 KB
|
|
57
|
+
more when it does load. Where `unsafe-eval` is blocked, on a page with a strict
|
|
58
|
+
Content-Security-Policy or in a Manifest V3 extension, it is never fetched and every schema
|
|
59
|
+
stays on the interpreted engine. To have generated code from the first call, import
|
|
60
|
+
`ata-validator/codegen` once; it also makes `parse()` available in the browser, which otherwise
|
|
61
|
+
waits for the generator.
|
|
52
62
|
|
|
53
63
|
On Deno the same package is `npm:ata-validator` or `jsr:@ata/validator`; the JSR package
|
|
54
64
|
re-exports the npm one at the same version, and CI runs the official suite under Deno. The
|
|
@@ -574,8 +584,11 @@ CLI options:
|
|
|
574
584
|
| `-f, --format <fmt>` | `esm` | `esm` or `cjs` |
|
|
575
585
|
| `--name <TypeName>` | from filename | Root type name in the `.d.ts` |
|
|
576
586
|
| `--abort-early` | off | Generate the stub-error variant, about a third of the source |
|
|
587
|
+
| `--order` | off | Hand errors out in the runtime's order (schema declaration order), not the order the collector found them in |
|
|
577
588
|
| `--no-types` | off | Skip the `.d.mts` / `.d.cts` output |
|
|
578
589
|
|
|
590
|
+
A module answers `validate()` as the runtime does, with two differences a caller reading the module directly should know. The runtime fills in `default` values before it checks (`useDefaults` is on unless turned off), and the module does not: where the schema declares a default the module exports `applyDefaults(data)`, the runtime's own pass, to call first. And the module returns its errors in the order its collector found them, which can put a field's `format` error before its `pattern` error, or behind a `$ref` a definition's `required` before its `properties`; `--order` sorts them as the runtime does. It is off by default because it costs bytes: 225 more gzipped for an eight-field form, about 1 KB for a small schema with references, whose key skeleton the module then carries, and 12.6% over 267 SchemaStore schemas. With both, a module matched the runtime's verdict, filled data and error list, in order, on every case of the official suites it compiles (1,500 groups, 5,324 cases across six dialects) and on 1,952 of SchemaStore's sample documents. `fromCompiled()` and modules that share the error runtime sort already and need neither.
|
|
591
|
+
|
|
579
592
|
For a project with many schemas, `ata build <glob>` compiles them all in one command:
|
|
580
593
|
|
|
581
594
|
```bash
|
package/bin/ata.js
CHANGED
|
@@ -30,6 +30,9 @@ Compile options:
|
|
|
30
30
|
--abort-early Use stub errors (smallest bundle)
|
|
31
31
|
--one-pass Report errors with the runtime's one-pass function: faster on
|
|
32
32
|
rejected documents, a module 1.3 to 1.7x the size
|
|
33
|
+
--order Hand errors out in the runtime's order (schema declaration
|
|
34
|
+
order) rather than the order they were found in; adds a sort,
|
|
35
|
+
and for a schema with $ref its key skeleton
|
|
33
36
|
--source Embed schema source map (default in development)
|
|
34
37
|
--no-source Omit source map (default in production, NODE_ENV=production)
|
|
35
38
|
|
|
@@ -45,6 +48,10 @@ Build options:
|
|
|
45
48
|
--abort-early Use stub errors (smallest bundle)
|
|
46
49
|
--one-pass Report errors with the runtime's one-pass function: faster on
|
|
47
50
|
rejected documents, a module 1.3 to 1.7x the size
|
|
51
|
+
--order Hand errors out in the runtime's order (schema declaration
|
|
52
|
+
order) rather than the order they were found in; adds a sort,
|
|
53
|
+
and for a schema with $ref its key skeleton (modules that share
|
|
54
|
+
the error runtime are in that order already)
|
|
48
55
|
--errors <mode> Where error detail comes from: auto | inline | shared. auto
|
|
49
56
|
(default) has modules share one error runtime file when that
|
|
50
57
|
makes the output smaller; production builds only (--no-source)
|
|
@@ -103,6 +110,7 @@ function parseArgs(argv) {
|
|
|
103
110
|
if (a === '--no-types') { out.opts.types = false; continue; }
|
|
104
111
|
if (a === '--abort-early') { out.opts.abortEarly = true; continue; }
|
|
105
112
|
if (a === '--one-pass') { out.opts.onePass = true; continue; }
|
|
113
|
+
if (a === '--order') { out.opts.order = true; continue; }
|
|
106
114
|
if (a === '--errors') { out.opts.errors = argv[++i]; continue; }
|
|
107
115
|
if (a === '--mode') { out.opts.mode = argv[++i]; continue; }
|
|
108
116
|
if (a === '--base-uri') { out.opts.baseUri = argv[++i]; continue; }
|
|
@@ -250,7 +258,7 @@ function cmdCompile(args) {
|
|
|
250
258
|
}
|
|
251
259
|
const schemaFile = path.relative(process.cwd(), input) || input;
|
|
252
260
|
const compileWarnings = [];
|
|
253
|
-
const src = aot.toStandaloneModule(v, { format, abortEarly, source, sourceMap, schemaFile, onePass: args.opts.onePass === true ? true : undefined, onWarning: (w) => compileWarnings.push(w) });
|
|
261
|
+
const src = aot.toStandaloneModule(v, { format, abortEarly, source, sourceMap, schemaFile, onePass: args.opts.onePass === true ? true : undefined, order: args.opts.order === true ? true : undefined, onWarning: (w) => compileWarnings.push(w) });
|
|
254
262
|
if (compileWarnings.length > 0) {
|
|
255
263
|
if (args.opts.strict) {
|
|
256
264
|
for (const w of compileWarnings) reportCompileError(input, w);
|
|
@@ -277,7 +285,7 @@ function cmdCompile(args) {
|
|
|
277
285
|
const typeName = args.opts.name || path.basename(input, path.extname(input))
|
|
278
286
|
.replace(/[^A-Za-z0-9_]/g, '_')
|
|
279
287
|
.replace(/^([a-z])/, (m) => m.toUpperCase()) || 'Data';
|
|
280
|
-
const dts = toTypeScript(schema, { name: typeName });
|
|
288
|
+
const dts = toTypeScript(schema, { name: typeName, applyDefaults: /\nfunction applyDefaults\(/.test(src) });
|
|
281
289
|
const ext = path.extname(output);
|
|
282
290
|
const dtsExt = ext === '.mjs' ? '.d.mts'
|
|
283
291
|
: ext === '.cjs' ? '.d.cts'
|
|
@@ -308,6 +316,7 @@ function cmdBuild(args) {
|
|
|
308
316
|
suffix: args.opts.suffix,
|
|
309
317
|
abortEarly: !!args.opts.abortEarly,
|
|
310
318
|
onePass: args.opts.onePass === true ? true : undefined,
|
|
319
|
+
order: args.opts.order === true ? true : undefined,
|
|
311
320
|
errors: args.opts.errors,
|
|
312
321
|
mode: args.opts.mode,
|
|
313
322
|
baseUri: args.opts.baseUri,
|
package/build.d.ts
CHANGED
|
@@ -116,6 +116,12 @@ export interface ToStandaloneModuleOptions {
|
|
|
116
116
|
* older collector either way.
|
|
117
117
|
*/
|
|
118
118
|
onePass?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Hand errors out in the runtime's order, schema declaration order, rather
|
|
121
|
+
* than the order the collector found them in (`ata compile --order`). Adds a
|
|
122
|
+
* sort, and for a schema with `$ref` its key skeleton; off by default.
|
|
123
|
+
*/
|
|
124
|
+
order?: boolean;
|
|
119
125
|
source?: boolean;
|
|
120
126
|
sourceMap?: unknown;
|
|
121
127
|
schemaFile?: string;
|
|
@@ -190,7 +196,7 @@ export function compiledSchemaFor(schema: unknown): object;
|
|
|
190
196
|
* {@link compiledEligible} declines, one the emitter cannot compile, and one
|
|
191
197
|
* whose detailed errors the generator cannot produce.
|
|
192
198
|
*/
|
|
193
|
-
export function compiledModuleFor(schema: unknown, opts?: { format?: 'esm' | 'cjs'; errorRuntime?: string }): string | null;
|
|
199
|
+
export function compiledModuleFor(schema: unknown, opts?: { format?: 'esm' | 'cjs'; errorRuntime?: string; order?: boolean }): string | null;
|
|
194
200
|
|
|
195
201
|
/**
|
|
196
202
|
* The Validator options `fromCompiled()` reproduces, so a plugin can tell
|
package/codegen.d.ts
ADDED
package/codegen.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ata-validator/codegen: registers the code generator now. The browser build
|
|
4
|
+
// loads it on demand, once a validator gets hot; importing this once at
|
|
5
|
+
// startup has every schema compiled to generated code from its first call,
|
|
6
|
+
// and makes parse() and the ahead-of-time bundle methods available in the
|
|
7
|
+
// browser. On Node, ata-validator already includes it and this does nothing.
|
|
8
|
+
const core = require('./lib/validator-core.js');
|
|
9
|
+
if (!core._codegenRegistered()) core._registerCodegen(require('./lib/codegen-engine.js'));
|
|
10
|
+
module.exports = {};
|
package/codegen.mjs
ADDED
package/index.browser.mjs
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
// Browser ESM entry, for bundlers: the validator core with
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
// Browser ESM entry, for bundlers: the validator core, with each tool imported
|
|
2
|
+
// on its own (see lib/tools.mjs) so that the tools an application does not
|
|
3
|
+
// import stay out of its bundle. The native addon is stubbed by the
|
|
4
|
+
// package.json "browser" field.
|
|
5
|
+
//
|
|
6
|
+
// The code generator is not in the page's bundle. Every schema starts on the
|
|
7
|
+
// interpreted engine, which gives the same answers; a validator that gets hot
|
|
8
|
+
// (lib/validator-core.js, TIER_CALLS) loads the generator through the dynamic
|
|
9
|
+
// import below, which bundlers split into a chunk of its own, and rebuilds
|
|
10
|
+
// itself on generated code. A page that wants generated code from the first
|
|
11
|
+
// call imports 'ata-validator/codegen' once.
|
|
6
12
|
import core from './lib/validator-core.js';
|
|
7
|
-
import
|
|
13
|
+
import tiering from './lib/tiering.js';
|
|
8
14
|
import { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON } from './lib/tools.mjs';
|
|
9
15
|
|
|
10
|
-
core.
|
|
16
|
+
core._registerTiering(tiering.createTiering(() => import('./lib/codegen-engine.js')));
|
|
11
17
|
|
|
12
18
|
export const { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, defineSchema } = core;
|
|
13
19
|
export { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
package/lib/aot-build.js
CHANGED
|
@@ -288,6 +288,7 @@ async function build(opts) {
|
|
|
288
288
|
format,
|
|
289
289
|
abortEarly: !!opts.abortEarly,
|
|
290
290
|
onePass: opts.onePass === true ? true : undefined,
|
|
291
|
+
order: opts.order === true ? true : undefined,
|
|
291
292
|
source,
|
|
292
293
|
sourceMap,
|
|
293
294
|
schemaFile,
|
|
@@ -465,7 +466,7 @@ async function build(opts) {
|
|
|
465
466
|
const typeName = stem
|
|
466
467
|
.replace(/[^A-Za-z0-9_]/g, '_')
|
|
467
468
|
.replace(/^([a-z])/, (m) => m.toUpperCase()) || 'Data';
|
|
468
|
-
const dts = toTypeScript(e.schema, { name: typeName });
|
|
469
|
+
const dts = toTypeScript(e.schema, { name: typeName, applyDefaults: /\nfunction applyDefaults\(/.test(src) });
|
|
469
470
|
const ext = path.extname(output);
|
|
470
471
|
const dtsExt = ext === '.mjs' ? '.d.mts'
|
|
471
472
|
: ext === '.cjs' ? '.d.cts'
|
|
@@ -646,6 +647,10 @@ function compiledModuleFor(schema, opts) {
|
|
|
646
647
|
try {
|
|
647
648
|
src = aot.toStandaloneModule(validatorFor(schema), {
|
|
648
649
|
format: (opts && opts.format) || 'esm',
|
|
650
|
+
// Errors in the runtime's order, for a reader that does not sort them
|
|
651
|
+
// itself (a form resolver); fromCompiled sorts, so a plugin putting
|
|
652
|
+
// that wrapper around the module leaves it off.
|
|
653
|
+
order: !!(opts && opts.order === true),
|
|
649
654
|
onWarning: (msg, info) => { if (info && info.kind === 'error-detail') degraded = true; },
|
|
650
655
|
});
|
|
651
656
|
} catch {
|
package/lib/aot-impl.js
CHANGED
|
@@ -346,6 +346,20 @@ function codeTableSource(keywords, formats) {
|
|
|
346
346
|
|
|
347
347
|
const ABORT_SHAPE = Object.freeze({ valid: false, errors: Object.freeze([Object.freeze({ code: 'ATA9000', message: 'validation failed', keyword: '__abort_early__', path: '' })]) });
|
|
348
348
|
|
|
349
|
+
// The runtime's defaults pass (preprocessSource in lib/codegen-engine.js),
|
|
350
|
+
// over the schema the runtime builds it from, as a function the module
|
|
351
|
+
// exports as applyDefaults. Not called by validate(): the runtime fills
|
|
352
|
+
// defaults before checking because useDefaults is on unless turned off, and
|
|
353
|
+
// a module cannot see that option, so the caller who wants the runtime's
|
|
354
|
+
// answer calls applyDefaults(data) first. Empty where the schema has no
|
|
355
|
+
// default, or where the pass declines it (a schema object containing itself).
|
|
356
|
+
function defaultsDecl(validator) {
|
|
357
|
+
const { resolveSchemaForPreprocess } = require('./validator-core')._internals;
|
|
358
|
+
let src = null;
|
|
359
|
+
try { src = require('./codegen-engine').preprocessSource(resolveSchemaForPreprocess(validator._schemaObj, validator._schemaMap), { useDefaults: true }); } catch {}
|
|
360
|
+
return src === null ? '' : `function applyDefaults(d) {\n${src}\n}\n`;
|
|
361
|
+
}
|
|
362
|
+
|
|
349
363
|
function toStandaloneModule(validator, opts) {
|
|
350
364
|
assertEmittable(validator, 'toStandaloneModule');
|
|
351
365
|
validator._ensureCompiled();
|
|
@@ -374,6 +388,7 @@ function toStandaloneModule(validator, opts) {
|
|
|
374
388
|
// notices a rejected document's cost. A build that validates large
|
|
375
389
|
// documents against large schemas asks for it.
|
|
376
390
|
let onePass = null;
|
|
391
|
+
let ordered = false;
|
|
377
392
|
const wantOnePass = opts && opts.onePass === true ? 'always' : (opts && (opts.onePass === false || opts.noOnePass)) ? 'never' : 'auto';
|
|
378
393
|
// Asked for explicitly, the one-pass function wins over schema-source
|
|
379
394
|
// frames, which `ata compile` writes by default outside production; the
|
|
@@ -394,14 +409,20 @@ function toStandaloneModule(validator, opts) {
|
|
|
394
409
|
`const _ER = function(e) { _ataStamp(e); this.valid = false; this.errors = e; };\n` +
|
|
395
410
|
`${codeTableSource(onePass.keywords, onePass.formats)}const _vC = (function() {\n${onePass.core}\n})();\n`;
|
|
396
411
|
} else if (!abortEarly) {
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
412
|
+
// With `order: true` the module hands its errors out in the runtime's
|
|
413
|
+
// order (lib/aot-order.js); without it, in the collector's, as it always
|
|
414
|
+
// did, since the sort costs every module bytes (12.6% over SchemaStore's,
|
|
415
|
+
// gzipped, most of it the key skeleton of schemas with references) and a
|
|
416
|
+
// reader that sorts (fromCompiled) does not need it. Where the module
|
|
417
|
+
// sorts by each error site's ordinal, the collector writes it (`_o`).
|
|
418
|
+
const schemaForErrors = typeof validator._schemaObj === 'object' ? validator._schemaObj : {};
|
|
419
|
+
ordered = !!(opts && opts.order === true);
|
|
420
|
+
const noOrdinal = !ordered || !require(AOT_ORDER_MODULE).wantsOrdinals(schemaForErrors);
|
|
400
421
|
jsErrFn = compileToJSCodegenWithErrors(
|
|
401
|
-
|
|
422
|
+
schemaForErrors,
|
|
402
423
|
null,
|
|
403
424
|
validator._userFormats,
|
|
404
|
-
(source && sourceMap && schemaFile) ? { sourceMap, schemaFile, frames: sourceFrames, noOrdinal
|
|
425
|
+
(source && sourceMap && schemaFile) ? { sourceMap, schemaFile, frames: sourceFrames, noOrdinal } : { noOrdinal },
|
|
405
426
|
);
|
|
406
427
|
const errSrc = jsErrFn && jsErrFn._errSource ? jsErrFn._errSource : '';
|
|
407
428
|
if (errSrc && jsErrFn._errFactory) {
|
|
@@ -537,12 +558,22 @@ function toStandaloneModule(validator, opts) {
|
|
|
537
558
|
// module that only accepts costs no more to load than its verdict.
|
|
538
559
|
compactCore = `let _ataErrs = null;\nfunction _ataErrors(d) { if (_ataErrs === null) _ataErrs = _ataCreateErrors(JSON.parse(${text})${rtOpts ? ', { ' + rtOpts + ' }' : ''}); return _ataErrs(d); }\n`;
|
|
539
560
|
}
|
|
561
|
+
const orderDecl = errCore && ordered && errorRuntime === null && onePass === null
|
|
562
|
+
? require(AOT_ORDER_MODULE).orderDecl(validator._schemaObj)
|
|
563
|
+
: '';
|
|
564
|
+
// Asked for and not given: stub errors, the one-pass function, or no error
|
|
565
|
+
// code generated. The caller is told rather than handed the collector's
|
|
566
|
+
// order as if it were the runtime's. Errors from the shared runtime come in
|
|
567
|
+
// the runtime's order already (errorsOf in lib/error-runtime-entry.js).
|
|
568
|
+
if (opts && opts.order === true && orderDecl === '' && errorRuntime === null && typeof opts.onWarning === 'function') {
|
|
569
|
+
opts.onWarning('errors are not in the runtime order: order applies to modules with generated error code, not to stub errors or the one-pass function', { kind: 'order' });
|
|
570
|
+
}
|
|
540
571
|
const validBody = errorRuntime !== null
|
|
541
572
|
? 'if (_fn(data)) return VALID; const e = _ataErrors(data); return e === null ? ABORT : { valid: false, errors: e }'
|
|
542
573
|
: onePass !== null
|
|
543
574
|
? 'return _vC(data)'
|
|
544
575
|
: errCore
|
|
545
|
-
? 'return _fn(data) ? VALID : { valid: false, errors: errFn(data, true).errors }'
|
|
576
|
+
? (orderDecl !== '' ? 'return _fn(data) ? VALID : { valid: false, errors: _ataOrder(errFn(data, true).errors) }' : 'return _fn(data) ? VALID : { valid: false, errors: errFn(data, true).errors }')
|
|
546
577
|
: 'return _fn(data) ? VALID : ABORT';
|
|
547
578
|
|
|
548
579
|
// parse(): validate, then hand back a copy holding only the properties the
|
|
@@ -625,6 +656,8 @@ function validateJSON(text) {
|
|
|
625
656
|
const hashDecl = `const schemaHash = ${JSON.stringify(hashSrc)};\nconst ataVersion = ${JSON.stringify(ATA_VERSION)};\n`;
|
|
626
657
|
|
|
627
658
|
let names = fmt.exportsSetFormats ? baseNames + ', setFormats' : baseNames;
|
|
659
|
+
const fillDecl = defaultsDecl(validator);
|
|
660
|
+
if (fillDecl) names += ', applyDefaults';
|
|
628
661
|
names += ', schemaHash, ataVersion';
|
|
629
662
|
if (positionsCore) names += ', validateJSON';
|
|
630
663
|
const parseAlias = cloneExpr ? ', _ataParse as parse' : '';
|
|
@@ -657,9 +690,9 @@ const ABORT = Object.freeze({
|
|
|
657
690
|
${closureDecls}${sharedBlock}${preambleDecls}${formatDecls}const _fn = function(d) {
|
|
658
691
|
${src}
|
|
659
692
|
};
|
|
660
|
-
${errCore}${compactCore}function isValid(data) { return _fn(data); }
|
|
693
|
+
${errCore}${orderDecl}${compactCore}function isValid(data) { return _fn(data); }
|
|
661
694
|
function validate(data) { ${validBody}; }
|
|
662
|
-
${parseCore}${positionsCore}${hashDecl}${exports}`;
|
|
695
|
+
${parseCore}${positionsCore}${fillDecl}${hashDecl}${exports}`;
|
|
663
696
|
}
|
|
664
697
|
|
|
665
698
|
// Why a validator cannot be written as a compact module, or null when it can.
|
|
@@ -673,6 +706,8 @@ ${parseCore}${positionsCore}${hashDecl}${exports}`;
|
|
|
673
706
|
// along: 1.49.0 bundled for such runtimes, and the generator must not break
|
|
674
707
|
// that.
|
|
675
708
|
const ERROR_RUNTIME_MODULE = './error-runtime';
|
|
709
|
+
// Reads the library's files too, at build time (see lib/aot-order.js).
|
|
710
|
+
const AOT_ORDER_MODULE = './aot-order';
|
|
676
711
|
function loadErrorRuntime() { return require(ERROR_RUNTIME_MODULE); }
|
|
677
712
|
|
|
678
713
|
// The schema hash a module stamps, once per validator: a plugin writes both
|
|
@@ -806,9 +841,11 @@ function toSchemaModule(validator, opts) {
|
|
|
806
841
|
? `import { createValidator as _ataCreateValidator } from ${spec};\n`
|
|
807
842
|
: `const { createValidator: _ataCreateValidator } = require(${spec});\n`)
|
|
808
843
|
+ (regSpec === null ? '' : format === 'esm' ? `import _ataRegistry from ${regSpec};\n` : `const _ataRegistry = require(${regSpec});\n`);
|
|
844
|
+
const fillDecl = defaultsDecl(validator);
|
|
845
|
+
const expNames = 'validate, isValid' + (fillDecl ? ', applyDefaults' : '') + ', schemaHash, ataVersion';
|
|
809
846
|
const exp = format === 'esm'
|
|
810
|
-
?
|
|
811
|
-
:
|
|
847
|
+
? `export { ${expNames} };\nexport default { ${expNames} };\n`
|
|
848
|
+
: `module.exports = { ${expNames} };\nmodule.exports.default = module.exports;\n`;
|
|
812
849
|
return `// Auto-generated by ata-validator ${ATA_VERSION}, do not edit.
|
|
813
850
|
// Schema module: the schema; the verdict and the errors come from the runtime it imports.
|
|
814
851
|
// Re-run the build after upgrading ata: the version above is what wrote this.
|
|
@@ -819,7 +856,7 @@ let _ataV = null;
|
|
|
819
856
|
function _ataGet() { return _ataV !== null ? _ataV : (_ataV = _ataCreateValidator(${ownId !== null ? 'null' : 'JSON.parse(' + text + ')'}${rtOpts ? ', { ' + rtOpts + ' }' : ''})); }
|
|
820
857
|
function isValid(data) { return _ataGet().isValid(data); }
|
|
821
858
|
function validate(data) { const v = _ataGet(); if (v.isValid(data)) return VALID; const e = v.errors(data); return e === null ? ABORT : { valid: false, errors: e }; }
|
|
822
|
-
const schemaHash = ${JSON.stringify(hashSrc)};
|
|
859
|
+
${fillDecl}const schemaHash = ${JSON.stringify(hashSrc)};
|
|
823
860
|
const ataVersion = ${JSON.stringify(ATA_VERSION)};
|
|
824
861
|
${exp}`;
|
|
825
862
|
}
|
package/lib/aot-order.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// How a module written ahead of time hands out its errors in the order the
|
|
4
|
+
// runtime does: schema declaration order (sortErrorsBySchemaOrder in
|
|
5
|
+
// lib/rejections.js). A module used to return them in the order its collector
|
|
6
|
+
// found them, which put `additionalProperties` before `properties` and
|
|
7
|
+
// `format` before `pattern`, and behind a reference a definition's `required`
|
|
8
|
+
// before its `properties`; the first error of a field could differ from the
|
|
9
|
+
// runtime's.
|
|
10
|
+
//
|
|
11
|
+
// The runtime orders by the ordinal of each error's schema path and breaks
|
|
12
|
+
// ties by the path's rank, and comparing ranks alone gives the same order
|
|
13
|
+
// (lib/schema-order.js). Two ways to know them in a module:
|
|
14
|
+
//
|
|
15
|
+
// - Without a reference, the collector writes each error site's ordinal into
|
|
16
|
+
// the error (`_o`, noteOrdinal in lib/js-compiler-errors.js) and no two
|
|
17
|
+
// paths share one, so a stable sort by it is the runtime's order.
|
|
18
|
+
// - With one, an error behind a shared definition has its path built when the
|
|
19
|
+
// definition runs and carries no ordinal, and paths through a `$ref` share
|
|
20
|
+
// the ordinal of the `$ref`. The module ranks each path itself with
|
|
21
|
+
// lib/schema-order.js's own functions, embedded as their text so module and
|
|
22
|
+
// runtime rank with one source, over the schema's skeleton: its schema
|
|
23
|
+
// objects with every key, subschemas kept, every other value 0, which ranks
|
|
24
|
+
// every path an error can name exactly as the schema does.
|
|
25
|
+
//
|
|
26
|
+
// Node only, at build time: it reads the library's own files.
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
|
|
30
|
+
const _files = new Map();
|
|
31
|
+
function take(file, names) {
|
|
32
|
+
let src = _files.get(file);
|
|
33
|
+
if (src === undefined) { src = fs.readFileSync(path.join(__dirname, file), 'utf8'); _files.set(file, src); }
|
|
34
|
+
return names.map((n) => {
|
|
35
|
+
const m = new RegExp('^function ' + n + ' ?\\([^]*?^}$', 'm').exec(src);
|
|
36
|
+
if (m === null) throw new Error('aot-order: lib/' + file + ' has no top-level function ' + n);
|
|
37
|
+
return m[0].replace(/^\s*\/\/.*\n/gm, '');
|
|
38
|
+
}).join('\n');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Keywords whose value is a schema, an array of schemas, or an object of them:
|
|
42
|
+
// the only places an error's schema path goes below a schema object.
|
|
43
|
+
const ONE = new Set(['additionalProperties', 'additionalItems', 'items', 'contains', 'propertyNames', 'if', 'then', 'else', 'not', 'unevaluatedItems', 'unevaluatedProperties']);
|
|
44
|
+
const LIST = new Set(['allOf', 'anyOf', 'oneOf', 'prefixItems', 'items']);
|
|
45
|
+
const MAP = new Set(['properties', 'patternProperties', '$defs', 'definitions', 'dependentSchemas', 'dependencies']);
|
|
46
|
+
|
|
47
|
+
function skeleton(node) {
|
|
48
|
+
if (node === null || typeof node !== 'object' || Array.isArray(node)) return 0;
|
|
49
|
+
const out = {};
|
|
50
|
+
for (const k of Object.keys(node)) {
|
|
51
|
+
const v = node[k];
|
|
52
|
+
let s = 0;
|
|
53
|
+
if (k === '$ref' && typeof v === 'string') s = v;
|
|
54
|
+
else if (Array.isArray(v) && LIST.has(k)) s = v.map(skeleton);
|
|
55
|
+
else if (MAP.has(k) && v !== null && typeof v === 'object' && !Array.isArray(v)) {
|
|
56
|
+
s = {};
|
|
57
|
+
for (const name of Object.keys(v)) Object.defineProperty(s, name, { value: Array.isArray(v[name]) ? 0 : skeleton(v[name]), enumerable: true, writable: true, configurable: true });
|
|
58
|
+
} else if (ONE.has(k) && v !== null && typeof v === 'object') s = skeleton(v);
|
|
59
|
+
Object.defineProperty(out, k, { value: s, enumerable: true, writable: true, configurable: true });
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function hasReference(schemaObj) {
|
|
65
|
+
const text = JSON.stringify(schemaObj);
|
|
66
|
+
return text.includes('"$ref"') || text.includes('"$dynamicRef"') || text.includes('"$recursiveRef"');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Whether the collector should write ordinals into the errors: only where the
|
|
70
|
+
// module sorts by them.
|
|
71
|
+
function wantsOrdinals(schemaObj) { return !hasReference(schemaObj); }
|
|
72
|
+
|
|
73
|
+
// The sort, by a key per error, stable as the runtime's insertion sort is, and
|
|
74
|
+
// the copies the caller gets, without the ordinal; a collapsed error's branch
|
|
75
|
+
// errors are ordered and copied the same way.
|
|
76
|
+
const ORDER = (key, cmp) => `function _ataOrder(list) {
|
|
77
|
+
const n = list.length, out = new Array(n), keys = new Array(n);
|
|
78
|
+
let prev = ${key === '_o' ? '0' : 'null'};
|
|
79
|
+
for (let i = 0; i < n; i++) {
|
|
80
|
+
const src = list[i];
|
|
81
|
+
const { _o, ...e } = src;
|
|
82
|
+
if (Array.isArray(e.branchErrors)) e.branchErrors = _ataOrder(e.branchErrors);
|
|
83
|
+
let k = ${key === '_o' ? "typeof src._o === 'number' ? src._o : prev" : '_ataRank(src.schemaPath)'};
|
|
84
|
+
prev = k;
|
|
85
|
+
let j = i - 1;
|
|
86
|
+
while (j >= 0 && ${cmp}) { keys[j + 1] = keys[j]; out[j + 1] = out[j]; j--; }
|
|
87
|
+
keys[j + 1] = k;
|
|
88
|
+
out[j + 1] = e;
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
// Source declaring `_ataOrder(errors)`: the errors sorted as the runtime sorts
|
|
95
|
+
// them, without their ordinals.
|
|
96
|
+
function orderDecl(schemaObj) {
|
|
97
|
+
if (!hasReference(schemaObj)) return ORDER('_o', 'keys[j] > k');
|
|
98
|
+
const sk = JSON.stringify(skeleton(schemaObj));
|
|
99
|
+
return `const _ataSkeleton = JSON.parse(${JSON.stringify(sk).replace(/</g, '\\u003c')});
|
|
100
|
+
const _keyIndexCache = new WeakMap();
|
|
101
|
+
const _rankCache = new WeakMap();
|
|
102
|
+
const _documents = new WeakMap();
|
|
103
|
+
const CONFLICT = null;
|
|
104
|
+
${take('schema-order.js', ['keyIndex', 'unescapePointerSegment', 'rankFor', 'computeRank', 'localTarget'])}
|
|
105
|
+
function _ataRank(p) { return rankFor(_ataSkeleton, p); }
|
|
106
|
+
function _ataAfter(a, b) {
|
|
107
|
+
if (a === null || b === null) return false;
|
|
108
|
+
const m = Math.min(a.length, b.length);
|
|
109
|
+
for (let i = 0; i < m; i++) if (a[i] !== b[i]) return a[i] > b[i];
|
|
110
|
+
return a.length > b.length;
|
|
111
|
+
}
|
|
112
|
+
${ORDER('rank', '_ataAfter(keys[j], k)')}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { orderDecl, wantsOrdinals };
|
package/lib/codegen-engine.js
CHANGED
|
@@ -857,8 +857,10 @@ function emitDefaults(node, ov, lines, st) {
|
|
|
857
857
|
st.seen.delete(node);
|
|
858
858
|
}
|
|
859
859
|
|
|
860
|
-
//
|
|
861
|
-
|
|
860
|
+
// The source of the preprocess pass, or null when there is nothing to do or
|
|
861
|
+
// the closure passes must take the schema. A module written ahead of time
|
|
862
|
+
// embeds it as applyDefaults (lib/aot-impl.js), so it runs the runtime's code.
|
|
863
|
+
function preprocessSource(schema, options) {
|
|
862
864
|
if (typeof schema !== 'object' || schema === null || !schema.properties) return null;
|
|
863
865
|
const lines = [];
|
|
864
866
|
|
|
@@ -889,13 +891,19 @@ function buildPreprocessCodegen(schema, options) {
|
|
|
889
891
|
// Data may legitimately be null or a non-object (e.g. a `['object','null']`
|
|
890
892
|
// schema), so the per-property mutations must not run on it.
|
|
891
893
|
lines.unshift(`if(d===null||typeof d!=='object')return`);
|
|
894
|
+
return lines.join('\n');
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// Generate a fast preprocess function via codegen instead of closure arrays
|
|
898
|
+
function buildPreprocessCodegen(schema, options) {
|
|
899
|
+
const src = preprocessSource(schema, options);
|
|
900
|
+
if (src === null) return null;
|
|
892
901
|
// The pass depends on property names, types, defaults and which objects
|
|
893
902
|
// are closed, not on the constraints the verdict checks, so routes that
|
|
894
903
|
// take the same shape with different limits (a page/limit querystring, an
|
|
895
904
|
// id param) emit the same source. Compiling it is most of what building the
|
|
896
905
|
// pass costs, so the function is kept by its source and compiled once. It
|
|
897
906
|
// holds no state: it rewrites the object it is given and nothing else.
|
|
898
|
-
const src = lines.join('\n');
|
|
899
907
|
let fn = _preprocessBySource.get(src);
|
|
900
908
|
if (fn === undefined) {
|
|
901
909
|
try {
|
|
@@ -1020,6 +1028,7 @@ module.exports = {
|
|
|
1020
1028
|
compileVerdict: installCodegenPaths.compileVerdict,
|
|
1021
1029
|
installScanner: installCodegenPaths.installScanner,
|
|
1022
1030
|
buildPreprocess: buildPreprocessCodegen,
|
|
1031
|
+
preprocessSource,
|
|
1023
1032
|
buildParse,
|
|
1024
1033
|
aot: () => require('./aot.js'),
|
|
1025
1034
|
};
|
package/lib/interpreter-core.js
CHANGED
|
@@ -13,6 +13,34 @@
|
|
|
13
13
|
let _safeRe = null;
|
|
14
14
|
const compileSafe = (src) => (_safeRe || (_safeRe = require('./safe-regex'))).compileSafe(src);
|
|
15
15
|
|
|
16
|
+
// A pattern with no group or alternation, few bounded choices in all, and at
|
|
17
|
+
// most one unbounded repeat, on one character or class and only when the
|
|
18
|
+
// pattern starts with `^`, runs in linear time on a backtracking RegExp, so it
|
|
19
|
+
// takes the platform's: built in about a microsecond where the linear engine's
|
|
20
|
+
// automaton took 40 to 100, and faster to test. Every pattern accepted here
|
|
21
|
+
// also gets a RegExp from the code generator (lib/regex-linear.js), so both
|
|
22
|
+
// engines answer with the same matcher; tests/test_regex_plain_linear.js holds
|
|
23
|
+
// that and the answers.
|
|
24
|
+
const PLAIN_ATOM = /(?:\\[dDwWsS\\/.^$|?*+()[\]{}-]|\[\^?(?:[^\\\][^\ud800-\udfff]|\\[dws\\/.^$[\]-])(?:[^\\\][\ud800-\udfff]|\\[dws\\/.^$[\]-])*\]|[^\\[\](){}|*+?^$\ud800-\udfff])(\?|[*+]|\{(\d{1,3})(?:,(\d{1,3}))?\})?/y;
|
|
25
|
+
function plainLinear(src) {
|
|
26
|
+
const n = src.length, anchored = src[0] === '^';
|
|
27
|
+
if (n > 256) return false;
|
|
28
|
+
let i = anchored ? 1 : 0, choices = 1, loops = 0;
|
|
29
|
+
while (i < n) {
|
|
30
|
+
if (src[i] === '$' && i === n - 1) return true;
|
|
31
|
+
PLAIN_ATOM.lastIndex = i;
|
|
32
|
+
const m = PLAIN_ATOM.exec(src);
|
|
33
|
+
if (m === null) return false;
|
|
34
|
+
i = PLAIN_ATOM.lastIndex;
|
|
35
|
+
const q = m[1];
|
|
36
|
+
if (q === '?') choices *= 2;
|
|
37
|
+
else if (q === '*' || q === '+') { if (!anchored || ++loops > 1) return false; }
|
|
38
|
+
else if (q !== undefined) { const lo = +m[2], hi = m[3] === undefined ? lo : +m[3]; if (hi < lo) return false; choices *= hi - lo + 1; }
|
|
39
|
+
if (choices > 64) return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
16
44
|
const SCHEMA_KEYWORDS = {
|
|
17
45
|
single: ['additionalProperties', 'contains', 'propertyNames', 'if', 'then', 'else', 'not', 'items', 'unevaluatedItems', 'unevaluatedProperties'],
|
|
18
46
|
maps: ['$defs', 'definitions', 'properties', 'patternProperties', 'dependentSchemas'],
|
|
@@ -912,6 +940,8 @@ class Interpreter {
|
|
|
912
940
|
// Unicode property escapes need a real RegExp in unicode mode; the
|
|
913
941
|
// linear engine does not understand them.
|
|
914
942
|
try { re = new RegExp(src, 'u'); } catch { re = new RegExp(src); }
|
|
943
|
+
} else if (plainLinear(src)) {
|
|
944
|
+
re = new RegExp(src);
|
|
915
945
|
} else {
|
|
916
946
|
try {
|
|
917
947
|
re = compileSafe(src);
|
|
@@ -1534,6 +1564,7 @@ function createInterpreter(schema, options) {
|
|
|
1534
1564
|
}
|
|
1535
1565
|
|
|
1536
1566
|
module.exports = { createInterpreter };
|
|
1567
|
+
Object.defineProperty(module.exports, '_plainLinear', { value: plainLinear, enumerable: false });
|
|
1537
1568
|
Object.defineProperty(module.exports, '_setPlanCompiler', { value: setPlanCompiler, enumerable: false });
|
|
1538
1569
|
Object.defineProperty(module.exports, '_setCompat', { value: setCompat, enumerable: false });
|
|
1539
1570
|
// The resolver, for the code generator: it resolves references at compile
|
package/lib/tiering.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// The browser build's code generator, loaded on demand. index.browser.mjs
|
|
4
|
+
// registers this with the core and hands it a loader (a dynamic import, which
|
|
5
|
+
// bundlers split into a chunk of its own), so a page starts with the
|
|
6
|
+
// interpreted engine alone. A validator counts its calls; one that reaches
|
|
7
|
+
// TIER_CALLS asks for the generator and, once it has arrived, rebuilds itself
|
|
8
|
+
// on generated code. Answers are the same on either engine; what changes is
|
|
9
|
+
// the speed and what the page downloads. Validators that stay cold, a form
|
|
10
|
+
// checked as the user types, never cause the download.
|
|
11
|
+
//
|
|
12
|
+
// Kept out of lib/validator-core.js so the entries that never load the
|
|
13
|
+
// generator on demand (lite, edge, Node) carry none of this.
|
|
14
|
+
const core = require('./validator-core.js');
|
|
15
|
+
const { compileFunction } = require('./compile-fn.js');
|
|
16
|
+
|
|
17
|
+
// Hot means TIER_CALLS calls within WINDOW_MS. A count alone is not enough:
|
|
18
|
+
// a form checked as the user types reaches a thousand calls over a long
|
|
19
|
+
// session, and generated code saves it nothing it would notice (tens of
|
|
20
|
+
// nanoseconds a check) while the page pays for the download and the compile.
|
|
21
|
+
// Measured on a form schema: interpreted 58 ns, generated 18 ns, loading the
|
|
22
|
+
// generator about 3.5 ms and compiling the schema 2.4 ms.
|
|
23
|
+
const TIER_CALLS = 1000;
|
|
24
|
+
const WINDOW_MS = 1000;
|
|
25
|
+
const now = typeof performance !== 'undefined' && typeof performance.now === 'function' ? () => performance.now() : Date.now;
|
|
26
|
+
// Recompiling takes a few milliseconds a validator. Each runs as its own
|
|
27
|
+
// task, when the page is idle where the browser says so, so that several
|
|
28
|
+
// validators turning hot together do not hold the main thread for all of
|
|
29
|
+
// them at once, and none compiles inside the call that crossed the line.
|
|
30
|
+
const later = typeof requestIdleCallback === 'function' ? (f) => requestIdleCallback(f, { timeout: 1000 }) : (f) => setTimeout(f, 0);
|
|
31
|
+
|
|
32
|
+
function createTiering (load) {
|
|
33
|
+
let loading = null;
|
|
34
|
+
// Validators that turned hot, waiting for the generator or for their turn.
|
|
35
|
+
const waiting = new Set();
|
|
36
|
+
let draining = null;
|
|
37
|
+
|
|
38
|
+
function drain () {
|
|
39
|
+
if (draining !== null) return draining;
|
|
40
|
+
draining = new Promise((resolve) => {
|
|
41
|
+
const step = () => {
|
|
42
|
+
const next = waiting.values().next();
|
|
43
|
+
if (next.done) { draining = null; resolve(); return; }
|
|
44
|
+
waiting.delete(next.value);
|
|
45
|
+
tierUp(next.value);
|
|
46
|
+
later(step);
|
|
47
|
+
};
|
|
48
|
+
later(step);
|
|
49
|
+
});
|
|
50
|
+
return draining;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function loadCodegen () {
|
|
54
|
+
if (loading !== null) return loading;
|
|
55
|
+
let allowed;
|
|
56
|
+
try { allowed = compileFunction('return 1')() === 1; } catch { allowed = false; }
|
|
57
|
+
loading = !allowed
|
|
58
|
+
// Code generation is blocked here (a strict Content-Security-Policy):
|
|
59
|
+
// the generator would decline every schema, so it is not downloaded.
|
|
60
|
+
? Promise.resolve(false)
|
|
61
|
+
: Promise.resolve().then(load).then((mod) => {
|
|
62
|
+
if (!core._codegenRegistered()) core._registerCodegen(mod && mod.default ? mod.default : mod);
|
|
63
|
+
return true;
|
|
64
|
+
}, () => false);
|
|
65
|
+
loading.then((ok) => { if (ok) drain(); else waiting.clear(); });
|
|
66
|
+
return loading;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The fields the constructor sets, the same for every validator; anything
|
|
70
|
+
// else on an instance was put there by compiling it, or by a method's first
|
|
71
|
+
// call, and is dropped when it is compiled again.
|
|
72
|
+
let ctorKeys = null;
|
|
73
|
+
const SCHEMA_KEYS = new Set(['_schemaObj', '_usesKeywords', '_schemaIsCallers']);
|
|
74
|
+
|
|
75
|
+
// Rebuilt on generated code, in place: what compiling put on the validator
|
|
76
|
+
// is dropped and it compiles again, now with the generator. In place rather
|
|
77
|
+
// than by copying from a second validator because the compiled methods
|
|
78
|
+
// close over the instance they were built for and swap themselves on it
|
|
79
|
+
// after their first calls (the one-pass and result-factory stages); copied
|
|
80
|
+
// across, those swaps would land on the copy. A caller holding a method
|
|
81
|
+
// taken earlier (`const check = v.isValidObject`) keeps the interpreted
|
|
82
|
+
// one, which answers the same.
|
|
83
|
+
function tierUp (v) {
|
|
84
|
+
if (!core._codegenRegistered() || v._tieredUp) return;
|
|
85
|
+
if (ctorKeys === null) ctorKeys = new Set(Reflect.ownKeys(new core.Validator(true, {})));
|
|
86
|
+
// Reset without `delete`: deleting properties turns a V8 object into a
|
|
87
|
+
// slow dictionary, and every method read on the validator got slower,
|
|
88
|
+
// 20 ns a check against 17 for one compiled directly (in any order; V8
|
|
89
|
+
// did not step back even for the newest property). Values are swapped
|
|
90
|
+
// instead, which keeps the object's shape: a method the prototype makes
|
|
91
|
+
// on first use gets a fresh one, as a new validator would; any other
|
|
92
|
+
// field the compile put there reads as undefined, as if absent. What the
|
|
93
|
+
// schema itself resolved to (_schemaObj and the flags read with it) does
|
|
94
|
+
// not change and stays.
|
|
95
|
+
const proto = Object.getPrototypeOf(v);
|
|
96
|
+
for (const k of Reflect.ownKeys(v)) {
|
|
97
|
+
if (ctorKeys.has(k) || SCHEMA_KEYS.has(k)) continue;
|
|
98
|
+
let d = null;
|
|
99
|
+
for (let p = proto; p !== null && d === null; p = Object.getPrototypeOf(p)) d = Object.getOwnPropertyDescriptor(p, k) || null;
|
|
100
|
+
if (d !== null && typeof d.get === 'function') d.get.call(v);
|
|
101
|
+
else v[k] = d !== null ? d.value : undefined;
|
|
102
|
+
}
|
|
103
|
+
v._initialized = false;
|
|
104
|
+
v._engine = undefined;
|
|
105
|
+
v._preprocess = null;
|
|
106
|
+
v._applyDefaults = null;
|
|
107
|
+
Object.defineProperty(v, '_tieredUp', { value: true, writable: true, configurable: true, enumerable: false });
|
|
108
|
+
v._ensureCompiled();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// A call counter for one validator: the TIER_CALLS-th call asks for the
|
|
112
|
+
// generator and has the validator rebuilt once it is in.
|
|
113
|
+
function counter (v) {
|
|
114
|
+
if (warm === 1) warm = 2;
|
|
115
|
+
let calls = 0, start = 0;
|
|
116
|
+
return () => {
|
|
117
|
+
if (++calls < TIER_CALLS) { if (calls === 1) start = now(); return; }
|
|
118
|
+
if (calls !== TIER_CALLS) return;
|
|
119
|
+
// Too slow to be hot: start a new window.
|
|
120
|
+
if (now() - start > WINDOW_MS) { calls = 0; return; }
|
|
121
|
+
waiting.add(v);
|
|
122
|
+
if (core._codegenRegistered()) drain();
|
|
123
|
+
else loadCodegen();
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// A page's first check pays for V8 compiling the engine and the error
|
|
128
|
+
// builders (3.1 ms of an eight-field form's first submit, 0.4 ms once any
|
|
129
|
+
// other schema had been checked) and for building its own schema's engine.
|
|
130
|
+
// Both move to when the page is idle, or 200 ms later where there is no
|
|
131
|
+
// requestIdleCallback (Safari, workers): one check of a small schema, once
|
|
132
|
+
// per page and dropped when a validator compiles first, then the engine for
|
|
133
|
+
// each validator built since, at most eight, kept aside. A validator adopts
|
|
134
|
+
// it at its first check only if every input is still the same, its schema
|
|
135
|
+
// text included, so addSchema() or a change to the schema in between is
|
|
136
|
+
// honored as before.
|
|
137
|
+
// warm: 0 none, 1 scheduled, 2 done or dropped.
|
|
138
|
+
let warm = 0, scheduled = false, warming = false, adopted = 0;
|
|
139
|
+
const pending = [];
|
|
140
|
+
const shadows = new WeakMap();
|
|
141
|
+
const idle = (f) => {
|
|
142
|
+
if (typeof requestIdleCallback === 'function') return requestIdleCallback(f, { timeout: 2000 });
|
|
143
|
+
const t = setTimeout(f, 200);
|
|
144
|
+
if (t && typeof t.unref === 'function') t.unref();
|
|
145
|
+
};
|
|
146
|
+
function built (v) {
|
|
147
|
+
if (warming) return;
|
|
148
|
+
if (warm === 0) warm = 1;
|
|
149
|
+
if (pending.length < 8 && typeof WeakRef === 'function') pending.push(new WeakRef(v));
|
|
150
|
+
if (!scheduled) { scheduled = true; idle(run); }
|
|
151
|
+
}
|
|
152
|
+
function run (deadline) {
|
|
153
|
+
scheduled = false;
|
|
154
|
+
if (warm === 1) {
|
|
155
|
+
warm = 2;
|
|
156
|
+
warming = true;
|
|
157
|
+
try { warmUp(); } catch { /* a warm-up never reaches the page */ }
|
|
158
|
+
warming = false;
|
|
159
|
+
}
|
|
160
|
+
while (pending.length > 0) {
|
|
161
|
+
if (deadline !== undefined && deadline.timeRemaining() < 1) { scheduled = true; idle(run); return; }
|
|
162
|
+
const v = pending.shift().deref();
|
|
163
|
+
if (v === undefined || core._codegenRegistered()) continue;
|
|
164
|
+
let s = null;
|
|
165
|
+
try { s = prebuild(v); } catch { /* built as usual at the first check */ }
|
|
166
|
+
if (s !== null) shadows.set(v, s);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// One check of a small schema, rejected and accepted, compiles the engine's
|
|
171
|
+
// code for the page: the plan builder, the walk, the regex and format
|
|
172
|
+
// checks, the error builders.
|
|
173
|
+
function warmUp () {
|
|
174
|
+
const v = new core.Validator({
|
|
175
|
+
type: 'object',
|
|
176
|
+
properties: {
|
|
177
|
+
a: { type: 'string', minLength: 2, maxLength: 9, pattern: '^[a-z]+$', format: 'email' },
|
|
178
|
+
b: { type: 'integer', minimum: 1, maximum: 9 },
|
|
179
|
+
c: { type: 'array', items: { type: 'string' }, maxItems: 3 },
|
|
180
|
+
d: { const: true },
|
|
181
|
+
e: { type: 'object', properties: { f: { type: 'string' } }, required: ['f'] },
|
|
182
|
+
},
|
|
183
|
+
required: ['a', 'b'],
|
|
184
|
+
});
|
|
185
|
+
void v.validate({ a: '1', b: 0, c: [1], d: false, e: {} }).errors.length;
|
|
186
|
+
v.validate({ a: 'ab@c.de', b: 2, c: ['x'], d: true, e: { f: 'x' } });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// The engine a validator will build at its first check, built now from the
|
|
190
|
+
// same inputs and kept aside; null where something besides the schema object
|
|
191
|
+
// decides the build: custom keywords (their compile functions are the
|
|
192
|
+
// caller's code), registered schemas, a schema that needs rewriting.
|
|
193
|
+
function prebuild (v) {
|
|
194
|
+
if (v._initialized || v._keywords !== null || v._schemaMap.size !== 0) return null;
|
|
195
|
+
const raw = v._rawSchema;
|
|
196
|
+
const { _interpArgs, _normalizeCallerSchemaUncached } = core._internals;
|
|
197
|
+
if (raw === null || typeof raw !== 'object' || _normalizeCallerSchemaUncached(raw) !== raw) return null;
|
|
198
|
+
const args = _interpArgs(v, raw);
|
|
199
|
+
return { schema: raw, text: JSON.stringify(raw), args, interp: require('./interpreter').createInterpreter(raw, args) };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function adopt (v, schemaObj, args) {
|
|
203
|
+
const s = shadows.get(v);
|
|
204
|
+
if (s === undefined) return null;
|
|
205
|
+
shadows.delete(v);
|
|
206
|
+
if (s.schema !== schemaObj || s.text !== v._schemaStr) return null;
|
|
207
|
+
for (const k of Object.keys(args)) if (args[k] !== s.args[k]) return null;
|
|
208
|
+
adopted++;
|
|
209
|
+
return s.interp;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Count the interpreted engine's calls for this validator.
|
|
213
|
+
function watch (v, interp) {
|
|
214
|
+
if (warm === 1) warm = 2;
|
|
215
|
+
const v0 = interp.validate, i0 = interp.isValid;
|
|
216
|
+
const tick = counter(v);
|
|
217
|
+
interp.validate = (d) => { tick(); return v0.call(interp, d); };
|
|
218
|
+
interp.isValid = (d) => { tick(); return i0.call(interp, d); };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// The emitters stay out of the page, as they did when the generator was in
|
|
222
|
+
// it (the package.json browser field maps lib/aot.js to this stub); loading
|
|
223
|
+
// a built module works.
|
|
224
|
+
const aotStub = () => require('./aot.browser.js');
|
|
225
|
+
|
|
226
|
+
// Resolves once the generator has loaded (or failed to) and every validator
|
|
227
|
+
// waiting for it has been rebuilt. For tests, and a page that wants to wait.
|
|
228
|
+
async function settled () {
|
|
229
|
+
if (loading !== null) await loading;
|
|
230
|
+
while (draining !== null) await draining;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return { watch, counter, built, aotStub, loadCodegen, settled, TIER_CALLS, WINDOW_MS, get loading () { return loading; }, get warm () { return warm; }, get adopted () { return adopted; }, adopt };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = { createTiering, TIER_CALLS };
|
package/lib/ts-gen.js
CHANGED
|
@@ -197,6 +197,8 @@ function renderJsDoc(schema, indent) {
|
|
|
197
197
|
function toTypeScript(schema, opts) {
|
|
198
198
|
const options = opts || {};
|
|
199
199
|
const rootName = toTypeName(options.name || 'Data');
|
|
200
|
+
// Whether the module beside these types exports applyDefaults (lib/aot-impl.js).
|
|
201
|
+
const fill = options.applyDefaults === true;
|
|
200
202
|
const defs = schema && (schema.$defs || schema.definitions);
|
|
201
203
|
|
|
202
204
|
const defLines = [];
|
|
@@ -240,9 +242,11 @@ export type Result = ValidResult | InvalidResult;
|
|
|
240
242
|
|
|
241
243
|
export declare function isValid(data: unknown): data is ${rootName};
|
|
242
244
|
export declare function validate(data: unknown): Result;
|
|
243
|
-
|
|
245
|
+
${fill ? `/** Fills in the schema's \`default\` values where a key is missing, as the runtime Validator does before checking. Call it before validate() for the runtime's answer. */
|
|
246
|
+
export declare function applyDefaults(data: unknown): void;
|
|
247
|
+
` : ''}export declare const schemaHash: string;
|
|
244
248
|
export declare const ataVersion: string;
|
|
245
|
-
declare const _default: { validate: typeof validate; isValid: typeof isValid; schemaHash: typeof schemaHash; ataVersion: typeof ataVersion };
|
|
249
|
+
declare const _default: { validate: typeof validate; isValid: typeof isValid; ${fill ? 'applyDefaults: typeof applyDefaults; ' : ''}schemaHash: typeof schemaHash; ataVersion: typeof ataVersion };
|
|
246
250
|
export default _default;
|
|
247
251
|
`;
|
|
248
252
|
}
|
package/lib/validator-core.js
CHANGED
|
@@ -30,6 +30,10 @@ function _registerCodegen(engine) {
|
|
|
30
30
|
// `ata build` or Validator.bundle() load where generated code cannot run.
|
|
31
31
|
// The emitters stay out, as they do in a browser bundle of the full package.
|
|
32
32
|
let _aotLoader = null;
|
|
33
|
+
// The browser build's on-demand code generator (lib/tiering.js): set by
|
|
34
|
+
// index.browser.mjs, null in every other entry, which then carry none of it.
|
|
35
|
+
let _tiering = null;
|
|
36
|
+
function _registerTiering(t) { _tiering = t; }
|
|
33
37
|
function _registerAot(load) {
|
|
34
38
|
_aotLoader = load;
|
|
35
39
|
}
|
|
@@ -329,7 +333,13 @@ function resolveSchemaByPath(rootSchema, schemaPath) {
|
|
|
329
333
|
//
|
|
330
334
|
// A failing route sees the same handful of schemaPaths over and over, which is
|
|
331
335
|
// what makes the first one worth having.
|
|
332
|
-
|
|
336
|
+
// Loaded at the first compile, where every use below is, rather than with the
|
|
337
|
+
// core: with it came the schema order and diagnostic modules, run at import
|
|
338
|
+
// by every page that loads the package, validating or not.
|
|
339
|
+
let LazyRejection, RichRejection, LazyJsonRejection, _enrichLazy, presentErrors, needsOrdering, sortErrorsBySchemaOrder, stripOrdinal;
|
|
340
|
+
function _loadRejections() {
|
|
341
|
+
({ LazyRejection, RichRejection, LazyJsonRejection, _enrichLazy, presentErrors, needsOrdering, sortErrorsBySchemaOrder, stripOrdinal } = require('./rejections'));
|
|
342
|
+
}
|
|
333
343
|
|
|
334
344
|
// Text that does not parse, reported the same way on every path and platform:
|
|
335
345
|
// ATA9001 with the parser's own message. The native addon used to say only
|
|
@@ -712,6 +722,21 @@ function resolveSchemaForPreprocess(schema, schemaMap) {
|
|
|
712
722
|
// touched), `format` stripped under `assertFormat: false`, and the custom
|
|
713
723
|
// keyword scan. The accessors then step aside for own data properties, so
|
|
714
724
|
// every later read is a plain field.
|
|
725
|
+
// What the interpreted engine is built from, besides the schema.
|
|
726
|
+
function _interpArgs(self, schemaObj) {
|
|
727
|
+
const options = self._options;
|
|
728
|
+
return {
|
|
729
|
+
schemaMap: self._schemaMap.size > 0 ? self._schemaMap : null,
|
|
730
|
+
formats: self._userFormats,
|
|
731
|
+
v1: isV1Dialect(schemaObj),
|
|
732
|
+
keywords: self._keywords,
|
|
733
|
+
// compat's coercion where each type is checked (see the interpreter).
|
|
734
|
+
coerce: self._interpretOnly ? options._coerceInWalk : undefined,
|
|
735
|
+
discriminator: self._interpretOnly && options._discriminator === true,
|
|
736
|
+
numberFormats: self._interpretOnly ? options._numberFormats : undefined,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
|
|
715
740
|
function _materializeSchema(self) {
|
|
716
741
|
const raw = self._rawSchema;
|
|
717
742
|
const options = self._options;
|
|
@@ -891,6 +916,8 @@ class Validator {
|
|
|
891
916
|
this._verdictTail = null;
|
|
892
917
|
this._validateTail = null;
|
|
893
918
|
this._entryExt = null;
|
|
919
|
+
// The browser build warms the engine once, when the page is idle (lib/tiering.js).
|
|
920
|
+
if (_tiering !== null) _tiering.built(this);
|
|
894
921
|
|
|
895
922
|
// Public methods start as memoized accessors on the prototype; nothing is
|
|
896
923
|
// allocated per instance until one is first read. See _defineLazyMethod
|
|
@@ -943,6 +970,7 @@ class Validator {
|
|
|
943
970
|
|
|
944
971
|
_ensureCompiled() {
|
|
945
972
|
if (this._initialized) return;
|
|
973
|
+
if (LazyRejection === undefined) _loadRejections();
|
|
946
974
|
this._ensureVocabularies();
|
|
947
975
|
this._initialized = true;
|
|
948
976
|
// Errors behind a reference into a registered document are ordered as
|
|
@@ -1197,18 +1225,12 @@ class Validator {
|
|
|
1197
1225
|
// No JS codegen: the interpreted engine, with or without the addon. The
|
|
1198
1226
|
// buffer APIs come from _installBufferApis on first use, as they do on
|
|
1199
1227
|
// every other path.
|
|
1200
|
-
const
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
v1: isV1Dialect(schemaObj),
|
|
1205
|
-
keywords: this._keywords,
|
|
1206
|
-
// compat's coercion where each type is checked (see the interpreter).
|
|
1207
|
-
coerce: this._interpretOnly ? options._coerceInWalk : undefined,
|
|
1208
|
-
discriminator: this._interpretOnly && options._discriminator === true,
|
|
1209
|
-
numberFormats: this._interpretOnly ? options._numberFormats : undefined,
|
|
1210
|
-
});
|
|
1228
|
+
const iargs = _interpArgs(this, schemaObj);
|
|
1229
|
+
// The browser build may have built it while the page was idle, from
|
|
1230
|
+
// the same inputs (lib/tiering.js).
|
|
1231
|
+
const interp = (_tiering !== null && _tiering.adopt(this, schemaObj, iargs)) || require('./interpreter').createInterpreter(schemaObj, iargs);
|
|
1211
1232
|
this._engine = 'interpreter';
|
|
1233
|
+
if (_codegen === null && _tiering !== null && !_forceNapi && !this._v1Dynamic) _tiering.watch(this, interp);
|
|
1212
1234
|
// A keyword that reads its data's context may also change the data (a
|
|
1213
1235
|
// request validator deserializing a date, say). Such a schema is run
|
|
1214
1236
|
// once per call: deciding first and walking again for the errors would
|
|
@@ -1703,7 +1725,7 @@ class Validator {
|
|
|
1703
1725
|
static fromStandalone(mod, schema, opts) {
|
|
1704
1726
|
// It loads what Validator.bundleStandalone() writes, and compiles an error
|
|
1705
1727
|
// function when the module carries none, so it goes with those methods.
|
|
1706
|
-
if (_codegen === null && _aotLoader === null) throw new TypeError('Validator.fromStandalone is not part of ata-validator/lite, like the bundle methods it loads for. Import it from ata-validator, or import a module from `ata build` directly.');
|
|
1728
|
+
if (_codegen === null && _aotLoader === null && _tiering === null) throw new TypeError('Validator.fromStandalone is not part of ata-validator/lite, like the bundle methods it loads for. Import it from ata-validator, or import a module from `ata build` directly.');
|
|
1707
1729
|
const options = opts || {};
|
|
1708
1730
|
const schemaObj = typeof schema === "string" ? JSON.parse(schema) : schema;
|
|
1709
1731
|
|
|
@@ -1891,6 +1913,10 @@ function version() {
|
|
|
1891
1913
|
function _aot() {
|
|
1892
1914
|
if (_codegen !== null) return _codegen.aot();
|
|
1893
1915
|
if (_aotLoader !== null) return _aotLoader();
|
|
1916
|
+
// The browser build: emitters stay out of the page, as they did when the
|
|
1917
|
+
// generator was in it (the package.json browser field maps lib/aot.js to
|
|
1918
|
+
// this file); loading a built module works.
|
|
1919
|
+
if (_tiering !== null) return _tiering.aotStub();
|
|
1894
1920
|
throw new TypeError('The ahead-of-time bundle methods are not part of ata-validator/lite. Import them from ata-validator.');
|
|
1895
1921
|
}
|
|
1896
1922
|
Validator.bundle = function (schemas, opts) {
|
|
@@ -2280,7 +2306,9 @@ Validator.prototype.parse = function (data) {
|
|
|
2280
2306
|
let fn = this._parseFn;
|
|
2281
2307
|
if (fn === undefined) {
|
|
2282
2308
|
fn = _buildParse(this);
|
|
2283
|
-
|
|
2309
|
+
// A refusal only because the generator has not loaded yet is not kept:
|
|
2310
|
+
// the next call, after it has, builds the copy.
|
|
2311
|
+
if (!fn._untilCodegen) Object.defineProperty(this, '_parseFn', { value: fn, writable: true, configurable: true, enumerable: false });
|
|
2284
2312
|
}
|
|
2285
2313
|
return fn(data);
|
|
2286
2314
|
};
|
|
@@ -2302,6 +2330,11 @@ function _buildParse(self) {
|
|
|
2302
2330
|
// isValidObject, which runs them, and a property they check with instanceof
|
|
2303
2331
|
// is carried over as it is (see clone-emit).
|
|
2304
2332
|
const extended = self._verdictTail !== null || self._validateTail !== null;
|
|
2333
|
+
if (_codegen === null && _tiering !== null) {
|
|
2334
|
+
const later = decline('in the browser build the code generator loads on demand', "Import 'ata-validator/codegen' once at startup to have it from the first call.");
|
|
2335
|
+
later._untilCodegen = true;
|
|
2336
|
+
return later;
|
|
2337
|
+
}
|
|
2305
2338
|
if (_codegen === null) return decline('this entry has no code generator to build the copy with', 'Use validate(); a valid result carries the input as it is.');
|
|
2306
2339
|
return _codegen.buildParse(self, decline, extended);
|
|
2307
2340
|
}
|
|
@@ -2408,7 +2441,14 @@ _defineLazyMethod('isValidObject', (self) => {
|
|
|
2408
2441
|
const { buildTier0Plan, tier0Validate } = require("./tier0");
|
|
2409
2442
|
const _plan = buildTier0Plan(self._schemaObj);
|
|
2410
2443
|
let _n = 0;
|
|
2411
|
-
|
|
2444
|
+
// In the browser build the generator is not there yet: count the calls
|
|
2445
|
+
// like the interpreted engine does, or a schema checked only through
|
|
2446
|
+
// this method stays on the plan walk, four times slower, for good.
|
|
2447
|
+
const _tick = _codegen === null && _tiering !== null && !self._interpretOnly && !env('ATA_FORCE_NAPI') ? _tiering.counter(self) : null;
|
|
2448
|
+
_bindVerdict(self, _tick !== null ? (d) => {
|
|
2449
|
+
_tick();
|
|
2450
|
+
return tier0Validate(_plan, d);
|
|
2451
|
+
} : (d) => {
|
|
2412
2452
|
const r = tier0Validate(_plan, d);
|
|
2413
2453
|
if (++_n === 2) {
|
|
2414
2454
|
try { self._ensureCodegen(); } catch {}
|
|
@@ -2478,6 +2518,9 @@ module.exports = {
|
|
|
2478
2518
|
// How an entry registers the code generator. Not enumerable, so the exported
|
|
2479
2519
|
// surface of `require('ata-validator')` is what it was.
|
|
2480
2520
|
Object.defineProperty(module.exports, '_registerCodegen', { value: _registerCodegen, enumerable: false });
|
|
2521
|
+
Object.defineProperty(module.exports, '_registerTiering', { value: _registerTiering, enumerable: false });
|
|
2522
|
+
Object.defineProperty(module.exports, '_codegenRegistered', { value: () => _codegen !== null, enumerable: false });
|
|
2523
|
+
Object.defineProperty(module.exports, '_tiering', { get: () => _tiering, enumerable: false });
|
|
2481
2524
|
Object.defineProperty(module.exports, '_registerAot', { value: _registerAot, enumerable: false });
|
|
2482
2525
|
// What the compiled paths in index.js need from this module.
|
|
2483
|
-
Object.defineProperty(module.exports, '_internals', { value: { _jsonSyntaxRejection, ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindEntry, _bindVerdict, _compileCache, _compileCacheSet, _mustReject, _rememberInstance, _verboseWrap, compileCacheKey, getNative, isV1Dialect, resolveSchemaByPath }, enumerable: false });
|
|
2526
|
+
Object.defineProperty(module.exports, '_internals', { value: { _interpArgs, _normalizeCallerSchemaUncached, resolveSchemaForPreprocess, _jsonSyntaxRejection, ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindEntry, _bindVerdict, _compileCache, _compileCacheSet, _mustReject, _rememberInstance, _verboseWrap, compileCacheKey, getNative, isV1Dialect, resolveSchemaByPath }, enumerable: false });
|
package/lib/version.js
CHANGED
package/lite.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// ata-validator/lite
|
|
1
|
+
// ata-validator/lite, kept for code that already imports it; new code imports
|
|
2
|
+
// ata-validator, whose browser build loads the code generator on demand.
|
|
3
|
+
// The same Validator and one-shot functions as
|
|
2
4
|
// ata-validator, on the interpreted engine and without the code generator.
|
|
3
5
|
// Validator#parse and the static bundle methods throw a TypeError here.
|
|
4
6
|
export {
|
package/lite.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// ata-validator/lite
|
|
3
|
+
// ata-validator/lite, kept for code that already imports it. The browser build
|
|
4
|
+
// of ata-validator now starts on the interpreted engine and loads the code
|
|
5
|
+
// generator only on demand, so a page needs nothing else; new code imports
|
|
6
|
+
// ata-validator.
|
|
7
|
+
//
|
|
8
|
+
// The Validator without the code generator. Every schema
|
|
4
9
|
// runs on the interpreted engine, which passes the same official test suite as
|
|
5
10
|
// the compiled path and never calls `new Function`, so it also runs under a
|
|
6
11
|
// strict Content-Security-Policy. The results are the ones `ata-validator`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.57.0",
|
|
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",
|
|
@@ -70,9 +70,17 @@
|
|
|
70
70
|
"import": "./error-runtime.mjs",
|
|
71
71
|
"require": "./error-runtime.js"
|
|
72
72
|
},
|
|
73
|
-
"./package.json": "./package.json"
|
|
73
|
+
"./package.json": "./package.json",
|
|
74
|
+
"./codegen": {
|
|
75
|
+
"types": "./codegen.d.ts",
|
|
76
|
+
"import": "./codegen.mjs",
|
|
77
|
+
"require": "./codegen.js"
|
|
78
|
+
}
|
|
74
79
|
},
|
|
75
|
-
"sideEffects":
|
|
80
|
+
"sideEffects": [
|
|
81
|
+
"./codegen.js",
|
|
82
|
+
"./codegen.mjs"
|
|
83
|
+
],
|
|
76
84
|
"bin": {
|
|
77
85
|
"ata": "bin/ata.js"
|
|
78
86
|
},
|
|
@@ -171,16 +179,19 @@
|
|
|
171
179
|
"error-runtime.d.ts",
|
|
172
180
|
"bin/",
|
|
173
181
|
"README.md",
|
|
174
|
-
"LICENSE"
|
|
182
|
+
"LICENSE",
|
|
183
|
+
"codegen.js",
|
|
184
|
+
"codegen.mjs",
|
|
185
|
+
"codegen.d.ts"
|
|
175
186
|
],
|
|
176
187
|
"optionalDependencies": {
|
|
177
|
-
"@ata-validator/native-darwin-arm64": "1.
|
|
178
|
-
"@ata-validator/native-darwin-x64": "1.
|
|
179
|
-
"@ata-validator/native-linux-arm64-gnu": "1.
|
|
180
|
-
"@ata-validator/native-linux-arm64-musl": "1.
|
|
181
|
-
"@ata-validator/native-linux-x64-gnu": "1.
|
|
182
|
-
"@ata-validator/native-linux-x64-musl": "1.
|
|
183
|
-
"@ata-validator/native-win32-x64": "1.
|
|
188
|
+
"@ata-validator/native-darwin-arm64": "1.57.0",
|
|
189
|
+
"@ata-validator/native-darwin-x64": "1.57.0",
|
|
190
|
+
"@ata-validator/native-linux-arm64-gnu": "1.57.0",
|
|
191
|
+
"@ata-validator/native-linux-arm64-musl": "1.57.0",
|
|
192
|
+
"@ata-validator/native-linux-x64-gnu": "1.57.0",
|
|
193
|
+
"@ata-validator/native-linux-x64-musl": "1.57.0",
|
|
194
|
+
"@ata-validator/native-win32-x64": "1.57.0"
|
|
184
195
|
},
|
|
185
196
|
"peerDependencies": {
|
|
186
197
|
"yaml": "^2.0.0"
|
|
@@ -192,7 +203,7 @@
|
|
|
192
203
|
},
|
|
193
204
|
"devDependencies": {
|
|
194
205
|
"@sinclair/typebox": "^0.34.49",
|
|
195
|
-
"@types/node": "^
|
|
206
|
+
"@types/node": "^26.6.5",
|
|
196
207
|
"ajv": "^8.20.0",
|
|
197
208
|
"ajv-formats": "^3.0.1",
|
|
198
209
|
"cmake-js": "^8.0.0",
|