ata-validator 1.62.0 → 1.63.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 +68 -13
- package/build.mjs +1 -0
- package/index.browser.mjs +23 -4
- package/index.bundle.mjs +14 -4
- package/index.edge.mjs +14 -4
- package/index.js +7 -2
- package/lib/aot-build.js +18 -0
- package/lib/aot-impl.js +20 -0
- package/lib/codegen-engine.js +72 -3
- package/lib/compiled-common.js +2 -1
- package/lib/email-literal.js +4 -1
- package/lib/error-runtime-features.js +41 -0
- package/lib/error-runtime.browser.js +15 -0
- package/lib/error-runtime.js +1 -32
- package/lib/formats-source.js +39 -40
- package/lib/interpreter-core.js +17 -9
- package/lib/ir/emit-deps.mjs +131 -0
- package/lib/ir/emit.mjs +560 -0
- package/lib/ir/front.mjs +834 -0
- package/lib/ir/index.mjs +10 -0
- package/lib/ir/refs.mjs +108 -0
- package/lib/ir/run.mjs +520 -0
- package/lib/ir/table.mjs +233 -0
- package/lib/js-compiler-errors.js +25 -7
- package/lib/js-compiler.js +6 -2
- package/lib/kernels/collapse.mjs +114 -0
- package/lib/kernels/custom.mjs +70 -0
- package/lib/kernels/fill.mjs +6 -0
- package/lib/kernels/formats-small.mjs +40 -0
- package/lib/kernels/formats.mjs +29 -0
- package/lib/kernels/index.mjs +11 -0
- package/lib/kernels/kernels.mjs +285 -0
- package/lib/kernels/leaves.mjs +28 -0
- package/lib/kernels/order.mjs +25 -0
- package/lib/kernels/plan-rich.mjs +17 -0
- package/lib/kernels/plan.d.ts +9 -0
- package/lib/kernels/plan.mjs +128 -0
- package/lib/kernels/preprocess.mjs +223 -0
- package/lib/kernels/rank.mjs +97 -0
- package/lib/kernels/rt.mjs +249 -0
- package/lib/kernels/table.mjs +182 -0
- package/lib/kernels/unique.mjs +157 -0
- package/lib/native-positions.js +1 -1
- package/lib/openapi.d.ts +2 -0
- package/lib/openapi.js +128 -0
- package/lib/plan-compiler.js +84 -7
- package/lib/public-api.js +7 -2
- package/lib/rejections.js +3 -1
- package/lib/schema-helpers.js +12 -0
- package/lib/schema-helpers.mjs +20 -0
- package/lib/schema-order.js +4 -2
- package/lib/standard-schema.js +1 -1
- package/lib/validator-core.js +35 -30
- package/lib/version.js +1 -1
- package/lite.js +7 -2
- package/package.json +32 -13
package/README.md
CHANGED
|
@@ -101,19 +101,14 @@ The `ata-validator` package itself is pure JavaScript. The native accelerator (s
|
|
|
101
101
|
npm install ata-validator --omit=optional
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
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,
|
|
104
|
+
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, 1302 of 1302 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.
|
|
105
105
|
|
|
106
106
|
In Cloudflare Workers and Vercel Edge, where generated code cannot run, `ata-validator`
|
|
107
107
|
resolves to an entry without the code generator through the `workerd` and `edge-light` export
|
|
108
108
|
conditions: the same names and answers, and a Worker bundled by wrangler goes from 179.1 to
|
|
109
109
|
70.4 KiB gzipped.
|
|
110
110
|
|
|
111
|
-
In a browser, compile with the bundler plugin
|
|
112
|
-
turns each `new Validator(schema)` whose schema it can read at build time into a compiled
|
|
113
|
-
validator that gives the same answers, and the runtime stays out of the page. A form with two
|
|
114
|
-
validators, one reading errors and one asking only for a verdict, bundles to 10.7 KB gzipped,
|
|
115
|
-
or 3.1 KB with `richErrors: false` on the one that reads errors, against 53.2 KB for the same
|
|
116
|
-
code on the runtime (esbuild, minified, the runtime with code splitting).
|
|
111
|
+
In a browser, compile with the bundler plugin: see [In the browser](#in-the-browser).
|
|
117
112
|
|
|
118
113
|
Without the plugin, the code generator is still not part of the page. Every schema starts on the
|
|
119
114
|
interpreted engine, which gives the same answers, and a validator called a thousand times within
|
|
@@ -133,9 +128,9 @@ re-exports the npm one at the same version, and CI runs the official suite under
|
|
|
133
128
|
details, and what Deno Deploy's refusal of dynamic code means for it, are in
|
|
134
129
|
[docs/edge-runtimes.md](docs/edge-runtimes.md).
|
|
135
130
|
|
|
136
|
-
Those four now agree with `validate()` on every case of the official suite,
|
|
131
|
+
Those four now agree with `validate()` on every case of the official suite, 3366 across three dialects. The native walker behind them does not handle every shape (`contains`, `unevaluatedProperties`, `patternProperties`, tuple `items`, cross-document `$ref`, a few formats), so for schemas using one of those the buffer APIs parse the bytes and answer through `validate()`; the list is in `lib/buffer-gate.js`. Typical request schemas stay on the zero-copy path. `npm test` holds the disagreement count at zero.
|
|
137
132
|
|
|
138
|
-
Where `new Function` is refused altogether, on Cloudflare Workers, Deno Deploy or under a strict Content-Security-Policy, ata drops to the interpreted engine and scores the same
|
|
133
|
+
Where `new Function` is refused altogether, on Cloudflare Workers, Deno Deploy or under a strict Content-Security-Policy, ata drops to the interpreted engine and scores the same 1302 of 1302 with code generation blocked. No flags, and on Workers no `nodejs_compat` either. See [docs/edge-runtimes.md](docs/edge-runtimes.md). Where generated code runs but slowly, as in an ahead-of-time compiled Bun executable, a module from `ata build` is plain JavaScript that imports nothing and calls no `new Function`: on Bun 1.4.0, import plus a first validation takes 0.66 ms with such a module against 10.70 ms with the runtime compiler (medians of 21 runs). [docs/proof-no-code-generation.md](docs/proof-no-code-generation.md) runs one schema in five of these places, Workers and a CSP page included, and shows the same answers from each.
|
|
139
134
|
|
|
140
135
|
Node has a switch for exactly that environment, so this takes thirty seconds to check for yourself, on ata or on whatever you use today:
|
|
141
136
|
|
|
@@ -162,7 +157,67 @@ if (isValid(req.body)) {
|
|
|
162
157
|
|
|
163
158
|
The `.compiled.mjs` modules are self-contained: zero runtime dependency on ata-validator, fully tree-shakeable, with TypeScript types emitted alongside.
|
|
164
159
|
|
|
165
|
-
Code already written with `new Validator(schema)` gets the same result from the bundler plugin, without changes: [`@ata-project/unplugin`](https://github.com/ata-core/unplugin-ata) replaces each call whose schema is known at build time with a compiled validator that answers the same, and the runtime leaves the bundle.
|
|
160
|
+
Code already written with `new Validator(schema)` gets the same result from the bundler plugin, without changes: [`@ata-project/unplugin`](https://github.com/ata-core/unplugin-ata) replaces each call whose schema is known at build time with a compiled validator that answers the same, and the runtime leaves the bundle. The sizes are in [In the browser](#in-the-browser). Schemas that arrive at runtime keep the runtime, which is the right tool for them.
|
|
161
|
+
|
|
162
|
+
## In the browser
|
|
163
|
+
|
|
164
|
+
Add the bundler plugin. [`@ata-project/unplugin`](https://github.com/ata-core/unplugin-ata) compiles each `new Validator(schema)` whose schema it can read at build time, so the page carries that schema's checks and not the compiler. Your code does not change, and the answers, the errors and the filled defaults are the same.
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
// vite.config.js
|
|
168
|
+
import ata from '@ata-project/unplugin/vite'
|
|
169
|
+
|
|
170
|
+
export default { plugins: [ata()] }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
// next.config.mjs, for Turbopack (the default since Next.js 16) and webpack
|
|
175
|
+
import withAta from '@ata-project/unplugin/next'
|
|
176
|
+
|
|
177
|
+
export default withAta({ /* your config */ })
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Webpack, Rspack, Rollup, Rolldown, esbuild and Bun take the same plugin; see its [setup](https://github.com/ata-core/unplugin-ata#setup).
|
|
181
|
+
|
|
182
|
+
An order form with six fields, an email format, a pattern and a default, its errors read on submit:
|
|
183
|
+
|
|
184
|
+
| | gzipped | ready, phone profile | first submit |
|
|
185
|
+
|---|---|---|---|
|
|
186
|
+
| without the plugin (code split) | 55.3 KB | 779 ms | 13.9 ms |
|
|
187
|
+
| with the plugin | 4.5 KB | 372 ms | 1.6 ms |
|
|
188
|
+
|
|
189
|
+
The sizes are an esbuild build, minified, gzip level 9. The times are Chrome with Lighthouse's mobile throttling (CPU 4x slower, 1.6 Mbps, 150 ms round trip), the files served gzipped, medians of 11 runs; the submit is made as soon as the page has loaded, before the runtime warms itself up in idle time, so a later submit on the runtime takes less than that.
|
|
190
|
+
|
|
191
|
+
What a page's validators add to its first load, gzipped, in applications built the usual way (Vite + React, Next.js 16), forms of six fields reading their errors on submit:
|
|
192
|
+
|
|
193
|
+
| | ata 1.62.1, plugin 0.11.3 | ata 1.63.0, plugin 0.12.0 |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| Vite, one form | 2.33 KB | 1.41 KB |
|
|
196
|
+
| Vite, ten forms | 8.02 KB | 2.02 KB |
|
|
197
|
+
| Vite, ten forms with email and uri fields | 12.07 KB | 2.70 KB |
|
|
198
|
+
| Next.js with webpack, one form with rich errors | 10.71 KB | 4.63 KB |
|
|
199
|
+
| Next.js with Turbopack, one form with rich errors | 67.52 KB (the plugin could not run) | 4.23 KB |
|
|
200
|
+
|
|
201
|
+
Many forms go out as data for one shared interpreter instead of code, where that makes the bundle smaller; the plugin decides for the whole application, and keeps written code for a single form. Needs ata-validator 1.63.0 and the plugin's 0.12.0.
|
|
202
|
+
|
|
203
|
+
With an OpenAPI document the plugin writes the validators from it (`ata({ openapi: 'openapi.json' })`, request bodies by operation, see [its README](https://github.com/ata-core/unplugin-ata#one-openapi-document-for-the-server-and-the-page)); a server reads the same document through `operationSchemas` in `ata-validator/openapi`.
|
|
204
|
+
|
|
205
|
+
The form and the server decide alike. A validator the plugin compiles answers exactly as `new Validator(schema)` does on the server, the same schema read the same way, when the page is built with the same ata-validator version the server runs: on a nine-field signup schema (email, uri, a pattern, lengths counted in characters, a fixed set of fields) the compiled form and the server's validator disagreed on 0 of 50,000 generated documents, so a submit the page accepts is one the server accepts.
|
|
206
|
+
|
|
207
|
+
A schema that arrives at run time (a form builder, a CMS, a schema served by the backend) can arrive as a plan instead. The server writes it with `tablePlan` from `ata-validator/ir`, which does the heavy work (references, dialects, normalization); the page reads it with `fromPlan` from `ata-validator/plan`, which carries the table interpreter and loads the format checks a plan names when it names them. Verdicts, errors and filled defaults are the server's for the same schema.
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
// server
|
|
211
|
+
import { tablePlan } from 'ata-validator/ir'
|
|
212
|
+
app.get('/forms/signup', () => tablePlan(signupSchema, { richErrors: false }))
|
|
213
|
+
|
|
214
|
+
// page
|
|
215
|
+
import { fromPlan } from 'ata-validator/plan'
|
|
216
|
+
const v = await fromPlan(await (await fetch('/forms/signup')).json())
|
|
217
|
+
v.validate(data)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
For the nine-field signup schema above, a Vite page that receives its schema at run time loads 53.0 KB of JavaScript gzipped with `new Validator(schema)` and 6.2 KB with `fromPlan`, the errors on the page the same. `ata-validator/plan-rich` reads plans with ata's rich errors as well. A plan is as trusted as a schema, so serve it from your own server: `fromPlan` refuses one that is not well formed before it runs, and says so once when a plan was written by another ata-validator version, whose answers may differ. A plan takes the keywords forms use (types, properties, required, additionalProperties, the leaf checks and formats, items, allOf, `$ref`, not, if, dependentRequired) and defaults; `tablePlan` returns `null` for anything else, and such a schema keeps the runtime.
|
|
166
221
|
|
|
167
222
|
## Measured by others
|
|
168
223
|
|
|
@@ -994,7 +1049,7 @@ Copy-paste recipes for the common frameworks. Most need 10-20 lines of glue. See
|
|
|
994
1049
|
| Fastify | dedicated plugin | [`fastify-ata`](https://github.com/ata-core/fastify-ata) |
|
|
995
1050
|
| Vite, Webpack, Rollup, Rolldown, esbuild, Rspack (build-time compile) | dedicated plugin | [`@ata-project/unplugin`](https://github.com/ata-core/unplugin-ata) |
|
|
996
1051
|
| Vite (build-time compile) | dedicated plugin | [`ata-vite`](https://github.com/ata-core/ata-vite), the Vite entry of the plugin above |
|
|
997
|
-
| Next.js (build-time compile) |
|
|
1052
|
+
| Next.js (build-time compile, Turbopack and webpack) | config wrapper of the plugin above | `withAta` from `@ata-project/unplugin/next` in `next.config.mjs` |
|
|
998
1053
|
| Hono | async middleware | [docs/integrations/hono.md](docs/integrations/hono.md) |
|
|
999
1054
|
| Elysia | Standard Schema V1 in the `body` slot | [docs/integrations/elysia.md](docs/integrations/elysia.md) |
|
|
1000
1055
|
| tRPC | Standard Schema V1 input | [docs/integrations/trpc.md](docs/integrations/trpc.md) |
|
|
@@ -1024,7 +1079,7 @@ Copy-paste recipes for the common frameworks. Most need 10-20 lines of glue. See
|
|
|
1024
1079
|
|
|
1025
1080
|
### Dialects
|
|
1026
1081
|
|
|
1027
|
-
`$schema` selects the dialect. Draft 2020-12 is the default; draft-07, draft-06, draft-04 and Draft 2019-09 are rewritten to the 2020-12 spelling on the way in (draft-06 as draft-07 without `if`/`then`/`else`, draft-04 as draft-06 with `id` for `$id` and without `const`, `contains` and `propertyNames`, 2019-09's tuple `items` and `$recursiveRef` as `prefixItems` and `$dynamicRef`); and `https://json-schema.org/v1` (or the dated `https://json-schema.org/v1/2026`) selects JSON Schema v1. The official suite passes in full on all five:
|
|
1082
|
+
`$schema` selects the dialect. Draft 2020-12 is the default; draft-07, draft-06, draft-04 and Draft 2019-09 are rewritten to the 2020-12 spelling on the way in (draft-06 as draft-07 without `if`/`then`/`else`, draft-04 as draft-06 with `id` for `$id` and without `const`, `contains` and `propertyNames`, 2019-09's tuple `items` and `$recursiveRef` as `prefixItems` and `$dynamicRef`); and `https://json-schema.org/v1` (or the dated `https://json-schema.org/v1/2026`) selects JSON Schema v1. The official suite passes in full on all five: 1302 of 1302 on 2020-12, 1261 of 1261 on 2019-09, 929 of 929 on draft-07, 841 of 841 on draft-06 and 1135 of 1135 on v1, with code generation allowed and blocked. Draft-04 and earlier are not supported; draft-04's boolean `exclusiveMinimum`/`exclusiveMaximum` is read wherever it appears.
|
|
1028
1083
|
|
|
1029
1084
|
Two things differ under v1. `propertyDependencies` selects a subschema by the value of a property rather than by its presence, which is what `dependentSchemas` does. And `$dynamicRef` no longer requires bookending: the reference resolves through the dynamic scope whether or not the schema it first lands on carries a matching `$dynamicAnchor`, so the outermost matching anchor still in scope wins. Everything else ata implements is identical under both dialects, so a schema that declares no `$schema` behaves exactly as before.
|
|
1030
1085
|
|
|
@@ -1036,7 +1091,7 @@ Both are implemented in the interpreted engine, so a v1 schema that uses `$dynam
|
|
|
1036
1091
|
|
|
1037
1092
|
### Known limitations
|
|
1038
1093
|
|
|
1039
|
-
Running the whole Draft 2020-12 suite with nothing excluded, `format` and `default` under specification semantics (`assertFormat: false`, `useDefaults: false`), gives
|
|
1094
|
+
Running the whole Draft 2020-12 suite with nothing excluded, `format` and `default` under specification semantics (`assertFormat: false`, `useDefaults: false`), gives 1302 of 1302 cases. Draft 2019-09 gives 1261 of 1261, draft 7 929 of 929, draft-06 841 of 841, draft-04 618 of 618. The v1 dialect gives 1135 of 1135. `npm run test:suite` reproduces all three.
|
|
1040
1095
|
|
|
1041
1096
|
Areas that remain deliberate scope decisions for 1.x:
|
|
1042
1097
|
|
package/build.mjs
CHANGED
|
@@ -12,6 +12,7 @@ export const schemaHash = mod.schemaHash;
|
|
|
12
12
|
export const compiledEligible = mod.compiledEligible;
|
|
13
13
|
export const compiledSchemaFor = mod.compiledSchemaFor;
|
|
14
14
|
export const compiledModuleFor = mod.compiledModuleFor;
|
|
15
|
+
export const defaultsSource = mod.defaultsSource;
|
|
15
16
|
export const bunTarget = mod.bunTarget;
|
|
16
17
|
export const verify = mod.verify;
|
|
17
18
|
export const compiledOptions = mod.compiledOptions;
|
package/index.browser.mjs
CHANGED
|
@@ -12,9 +12,28 @@
|
|
|
12
12
|
import core from './lib/validator-core.js';
|
|
13
13
|
import tiering from './lib/tiering.js';
|
|
14
14
|
import { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON } from './lib/tools.mjs';
|
|
15
|
+
import { defineSchema, schemaOf } from './lib/schema-helpers.mjs';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// Pure-marked reads, so a bundler drops what goes unused (lib/schema-helpers.mjs).
|
|
18
|
+
// In development, once: a page that loads this entry compiles its schemas in
|
|
19
|
+
// the browser, and a schema the build can read could be compiled by the build
|
|
20
|
+
// instead. A production build replaces process.env.NODE_ENV and drops the
|
|
21
|
+
// whole statement, the try included; where nothing defines process, the
|
|
22
|
+
// ReferenceError is caught. Not in Node (no document).
|
|
23
|
+
const api = /* @__PURE__ */ (() => {
|
|
24
|
+
core._registerTiering(tiering.createTiering(() => import('./lib/codegen-engine.js')));
|
|
25
|
+
try { if (process.env.NODE_ENV !== 'production' && typeof document !== 'undefined') console.info('ata-validator: this page compiles its schemas in the browser, which takes the validator runtime (about 51 KB gzipped). A schema the build can read can be compiled by the build instead, a few KB for a form: https://github.com/ata-core/unplugin-ata') } catch {}
|
|
26
|
+
return core;
|
|
27
|
+
})();
|
|
28
|
+
export const Validator = /* @__PURE__ */ (() => api.Validator)();
|
|
29
|
+
export const compile = /* @__PURE__ */ (() => api.compile)();
|
|
30
|
+
export const validate = /* @__PURE__ */ (() => api.validate)();
|
|
31
|
+
export const validateAsync = /* @__PURE__ */ (() => api.validateAsync)();
|
|
32
|
+
export const parseAsync = /* @__PURE__ */ (() => api.parseAsync)();
|
|
33
|
+
export const version = /* @__PURE__ */ (() => api.version)();
|
|
34
|
+
export const createPaddedBuffer = /* @__PURE__ */ (() => api.createPaddedBuffer)();
|
|
35
|
+
export const SIMDJSON_PADDING = /* @__PURE__ */ (() => api.SIMDJSON_PADDING)();
|
|
36
|
+
export const parseJSON = /* @__PURE__ */ (() => api.parseJSON)();
|
|
37
|
+
export { defineSchema, schemaOf };
|
|
19
38
|
export { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
|
20
|
-
export default { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, toTypeScript, defineSchema, schemaOf, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
|
39
|
+
export default /* @__PURE__ */ (() => ({ Validator: api.Validator, compile: api.compile, validate: api.validate, validateAsync: api.validateAsync, parseAsync: api.parseAsync, version: api.version, createPaddedBuffer: api.createPaddedBuffer, SIMDJSON_PADDING: api.SIMDJSON_PADDING, parseJSON: api.parseJSON, toTypeScript, defineSchema, schemaOf, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON }))();
|
package/index.bundle.mjs
CHANGED
|
@@ -7,9 +7,19 @@
|
|
|
7
7
|
import core from './lib/validator-core.js';
|
|
8
8
|
import engine from './lib/codegen-engine.js';
|
|
9
9
|
import { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON } from './lib/tools.mjs';
|
|
10
|
+
import { defineSchema, schemaOf } from './lib/schema-helpers.mjs';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export const
|
|
12
|
+
// Pure-marked reads, so a bundler drops what goes unused (lib/schema-helpers.mjs).
|
|
13
|
+
const api = /* @__PURE__ */ (() => { core._registerCodegen(engine); return core; })();
|
|
14
|
+
export const Validator = /* @__PURE__ */ (() => api.Validator)();
|
|
15
|
+
export const compile = /* @__PURE__ */ (() => api.compile)();
|
|
16
|
+
export const validate = /* @__PURE__ */ (() => api.validate)();
|
|
17
|
+
export const validateAsync = /* @__PURE__ */ (() => api.validateAsync)();
|
|
18
|
+
export const parseAsync = /* @__PURE__ */ (() => api.parseAsync)();
|
|
19
|
+
export const version = /* @__PURE__ */ (() => api.version)();
|
|
20
|
+
export const createPaddedBuffer = /* @__PURE__ */ (() => api.createPaddedBuffer)();
|
|
21
|
+
export const SIMDJSON_PADDING = /* @__PURE__ */ (() => api.SIMDJSON_PADDING)();
|
|
22
|
+
export const parseJSON = /* @__PURE__ */ (() => api.parseJSON)();
|
|
23
|
+
export { defineSchema, schemaOf };
|
|
14
24
|
export { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
|
15
|
-
export default { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, toTypeScript, defineSchema, schemaOf, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
|
25
|
+
export default /* @__PURE__ */ (() => ({ Validator: api.Validator, compile: api.compile, validate: api.validate, validateAsync: api.validateAsync, parseAsync: api.parseAsync, version: api.version, createPaddedBuffer: api.createPaddedBuffer, SIMDJSON_PADDING: api.SIMDJSON_PADDING, parseJSON: api.parseJSON, toTypeScript, defineSchema, schemaOf, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON }))();
|
package/index.edge.mjs
CHANGED
|
@@ -5,9 +5,19 @@
|
|
|
5
5
|
import core from './lib/validator-core.js';
|
|
6
6
|
import aotBrowser from './lib/aot.browser.js';
|
|
7
7
|
import { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON } from './lib/tools.mjs';
|
|
8
|
+
import { defineSchema, schemaOf } from './lib/schema-helpers.mjs';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export const
|
|
10
|
+
// Pure-marked reads, so a bundler drops what goes unused (lib/schema-helpers.mjs).
|
|
11
|
+
const api = /* @__PURE__ */ (() => { core._registerAot(() => aotBrowser); return core; })();
|
|
12
|
+
export const Validator = /* @__PURE__ */ (() => api.Validator)();
|
|
13
|
+
export const compile = /* @__PURE__ */ (() => api.compile)();
|
|
14
|
+
export const validate = /* @__PURE__ */ (() => api.validate)();
|
|
15
|
+
export const validateAsync = /* @__PURE__ */ (() => api.validateAsync)();
|
|
16
|
+
export const parseAsync = /* @__PURE__ */ (() => api.parseAsync)();
|
|
17
|
+
export const version = /* @__PURE__ */ (() => api.version)();
|
|
18
|
+
export const createPaddedBuffer = /* @__PURE__ */ (() => api.createPaddedBuffer)();
|
|
19
|
+
export const SIMDJSON_PADDING = /* @__PURE__ */ (() => api.SIMDJSON_PADDING)();
|
|
20
|
+
export const parseJSON = /* @__PURE__ */ (() => api.parseJSON)();
|
|
21
|
+
export { defineSchema, schemaOf };
|
|
12
22
|
export { toTypeScript, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
|
13
|
-
export default { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, toTypeScript, defineSchema, schemaOf, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON };
|
|
23
|
+
export default /* @__PURE__ */ (() => ({ Validator: api.Validator, compile: api.compile, validate: api.validate, validateAsync: api.validateAsync, parseAsync: api.parseAsync, version: api.version, createPaddedBuffer: api.createPaddedBuffer, SIMDJSON_PADDING: api.SIMDJSON_PADDING, parseJSON: api.parseJSON, toTypeScript, defineSchema, schemaOf, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON }))();
|
package/index.js
CHANGED
|
@@ -136,6 +136,11 @@ function attachSuggestions (errors, data) {
|
|
|
136
136
|
return errors;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
// Loaded on first call, like the tools: a process that never calls them does
|
|
140
|
+
// not read the file.
|
|
141
|
+
function defineSchema (schema) { return require('./lib/schema-helpers.js').defineSchema(schema); }
|
|
142
|
+
function schemaOf (...a) { return require('./lib/schema-helpers.js').schemaOf(...a); }
|
|
143
|
+
|
|
139
144
|
module.exports = {
|
|
140
145
|
Validator: core.Validator,
|
|
141
146
|
compile: core.compile,
|
|
@@ -147,8 +152,8 @@ module.exports = {
|
|
|
147
152
|
SIMDJSON_PADDING: core.SIMDJSON_PADDING,
|
|
148
153
|
parseJSON: core.parseJSON,
|
|
149
154
|
toTypeScript,
|
|
150
|
-
defineSchema
|
|
151
|
-
schemaOf
|
|
155
|
+
defineSchema,
|
|
156
|
+
schemaOf,
|
|
152
157
|
renderPretty,
|
|
153
158
|
renderCompact,
|
|
154
159
|
toOutput,
|
package/lib/aot-build.js
CHANGED
|
@@ -737,6 +737,23 @@ function compiledModuleFor(schema, opts) {
|
|
|
737
737
|
return src && !degraded ? src : null;
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
+
// The runtime's defaults pass for a schema as the body of a function of `d`
|
|
741
|
+
// (preprocessSource in lib/codegen-engine.js, over the schema the runtime
|
|
742
|
+
// builds it from, as the modules compiledModuleFor writes carry it), or null
|
|
743
|
+
// where the generated pass declines the schema. For a writer of modules that
|
|
744
|
+
// has no generated pass of its own for that schema (ata-validator/ir's
|
|
745
|
+
// specialize, through its fillSource option).
|
|
746
|
+
function defaultsSource(schema) {
|
|
747
|
+
if (!compiledEligible(schema)) return null;
|
|
748
|
+
try {
|
|
749
|
+
const v = validatorFor(schema);
|
|
750
|
+
const { resolveSchemaForPreprocess } = require('./validator-core')._internals;
|
|
751
|
+
return require('./codegen-engine').preprocessSource(resolveSchemaForPreprocess(v._schemaObj, v._schemaMap), { useDefaults: true });
|
|
752
|
+
} catch {
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
740
757
|
// The Validator options fromCompiled() reproduces, for a plugin deciding whether
|
|
741
758
|
// `new Validator(schema, options)` can be replaced. An older ata-validator does
|
|
742
759
|
// not export this, and a plugin should then replace only calls without options.
|
|
@@ -768,6 +785,7 @@ module.exports = {
|
|
|
768
785
|
compiledEligible,
|
|
769
786
|
compiledSchemaFor,
|
|
770
787
|
compiledModuleFor,
|
|
788
|
+
defaultsSource,
|
|
771
789
|
build,
|
|
772
790
|
expandGlobs,
|
|
773
791
|
parseSchemaFile,
|
package/lib/aot-impl.js
CHANGED
|
@@ -19,6 +19,20 @@ const VALID_RESULT = Object.freeze({ valid: true, errors: Object.freeze([]) });
|
|
|
19
19
|
const { buildTargetedPositionMap } = require('./data-positions');
|
|
20
20
|
const { schemaHash } = require('./schema-hash');
|
|
21
21
|
const ATA_VERSION = require('./version');
|
|
22
|
+
|
|
23
|
+
// The emitters write what the code generator produces, so they register it.
|
|
24
|
+
// The browser build loads the generator on demand, once a validator is hot,
|
|
25
|
+
// and until it had, bundleStandalone() compiled every schema to a null
|
|
26
|
+
// validator without an error: a page calling it got an empty module. Done at
|
|
27
|
+
// the first call rather than at load, where a loader that evaluates every
|
|
28
|
+
// require first (esm.sh) would hand over the generator half built.
|
|
29
|
+
let _codegenReady = false;
|
|
30
|
+
function needCodegen() {
|
|
31
|
+
if (_codegenReady) return;
|
|
32
|
+
const core = require('./validator-core.js');
|
|
33
|
+
if (!core._codegenRegistered()) core._registerCodegen(require('./codegen-engine.js'));
|
|
34
|
+
_codegenReady = true;
|
|
35
|
+
}
|
|
22
36
|
const safeRegexEngineSource = () => require('./safe-regex').engineSource();
|
|
23
37
|
|
|
24
38
|
// Embedded verbatim in standalone modules so the output file has no runtime
|
|
@@ -1378,5 +1392,11 @@ module.exports = {
|
|
|
1378
1392
|
bundleCompact: htmlSafeResult(bundleCompact),
|
|
1379
1393
|
loadBundle,
|
|
1380
1394
|
};
|
|
1395
|
+
|
|
1396
|
+
// Every emitter registers the code generator before it runs (needCodegen).
|
|
1397
|
+
for (const k of Object.keys(module.exports)) {
|
|
1398
|
+
const f = module.exports[k];
|
|
1399
|
+
if (typeof f === 'function') module.exports[k] = function (...a) { needCodegen(); return f.apply(this, a); };
|
|
1400
|
+
}
|
|
1381
1401
|
// For the build only (lib/aot-build.js), not part of ata-validator/aot.
|
|
1382
1402
|
Object.defineProperty(module.exports, '_refsResolveWithin', { value: refsResolveWithin, enumerable: false });
|
package/lib/codegen-engine.js
CHANGED
|
@@ -740,7 +740,10 @@ function emitRemovals(node, access, lines, depth, seen, own) {
|
|
|
740
740
|
}
|
|
741
741
|
seen.delete(node);
|
|
742
742
|
if (body.length === 0) return;
|
|
743
|
-
|
|
743
|
+
// At the root too, an array is not the object this node describes: its
|
|
744
|
+
// indices are not additional properties. The pass's own guard lets arrays
|
|
745
|
+
// through for the coercions of a schema whose root is an array.
|
|
746
|
+
if (depth === 0) lines.push(`if(!Array.isArray(${access})){${body.join('\n')}}`);
|
|
744
747
|
else lines.push(`if(${own}${access}!==null&&typeof ${access}==='object'&&!Array.isArray(${access})){${body.join('\n')}}`);
|
|
745
748
|
}
|
|
746
749
|
|
|
@@ -1194,6 +1197,63 @@ function buildParse (self, decline, extended) {
|
|
|
1194
1197
|
// carry. Returns the twin to answer this call, or null to compile.
|
|
1195
1198
|
const COLD_CALLS = 64;
|
|
1196
1199
|
const COLD_MIN = 16 * 1024;
|
|
1200
|
+
const MANY_SCHEMAS = 16;
|
|
1201
|
+
const SMALL_TWIN_OFF = typeof process !== 'undefined' && process.env !== undefined && process.env.ATA_NO_SMALL_TWIN === '1';
|
|
1202
|
+
let _firstCalls = 0;
|
|
1203
|
+
let _canGenerate = null;
|
|
1204
|
+
function canGenerate () {
|
|
1205
|
+
if (_canGenerate === null) { try { _canGenerate = require('./compile-fn').compileFunction('return 1')() === 1; } catch { _canGenerate = false; } }
|
|
1206
|
+
return _canGenerate;
|
|
1207
|
+
}
|
|
1208
|
+
let _core = null;
|
|
1209
|
+
let _keySelf = null; let _key = null;
|
|
1210
|
+
// The compile cache key compileCached just built for this validator, once.
|
|
1211
|
+
function keyFor (self) { if (_keySelf !== self) return null; const k = _key; _keySelf = null; _key = null; return k; }
|
|
1212
|
+
const core_ = () => _core || (_core = require('./validator-core')._internals);
|
|
1213
|
+
function compileCached (self) {
|
|
1214
|
+
if (self._userFormats || self._usesKeywords) return false;
|
|
1215
|
+
const { _compileCache, compileCacheKey } = core_();
|
|
1216
|
+
const str = self._schemaStr || (self._schemaStr = JSON.stringify(self._schemaObj));
|
|
1217
|
+
const key = compileCacheKey(str, self._schemaMap) + '\u0000' + (typeof jsCompiler.targetEngine === 'function' ? jsCompiler.targetEngine() : 'v8');
|
|
1218
|
+
// Kept for the compile that follows in the same call (keyFor), which would
|
|
1219
|
+
// build and hash the same key again.
|
|
1220
|
+
_keySelf = self; _key = key;
|
|
1221
|
+
const c = _compileCache.get(key);
|
|
1222
|
+
return c !== undefined && c.jsFn !== undefined && c.jsFn !== null;
|
|
1223
|
+
}
|
|
1224
|
+
// A small schema's twin, once the process has met MANY_SCHEMAS schemas (an
|
|
1225
|
+
// agent's tools, a registry of thousands): the interpreted engine alone, which
|
|
1226
|
+
// answers a verdict and a valid document's validate(); a document that fails,
|
|
1227
|
+
// or a JSON-text call, compiles the validator, which answers as always. A new
|
|
1228
|
+
// five-property schema's first check took 72 us compiled and 13 on it, and
|
|
1229
|
+
// its memory is about the compiled validator's (8.37 against 8.57 KB). A
|
|
1230
|
+
// server with a few routes compiles them at their first call as before, and
|
|
1231
|
+
// neither a cached schema nor a realm without code generation takes it.
|
|
1232
|
+
// ATA_NO_SMALL_TWIN=1 turns it off, for tests of the generated code.
|
|
1233
|
+
class SmallTwin {
|
|
1234
|
+
constructor (self, interp) { this.v = self; this.i = interp; }
|
|
1235
|
+
// A document that fails, or a call the interpreted verdict does not answer,
|
|
1236
|
+
// compiles the validator, which answers it as always.
|
|
1237
|
+
compile () { this.v._coldCalls = COLD_CALLS; this.v._twin = null; return this.v; }
|
|
1238
|
+
isValidObject (d) { return this.i.isValid(d); }
|
|
1239
|
+
validate (d) { return this.i.isValid(d) ? { valid: true, data: d, errors: [] } : this.compile().validate(d); }
|
|
1240
|
+
validateJSON (t) { return this.compile().validateJSON(t); }
|
|
1241
|
+
isValidJSON (t) { return this.compile().isValidJSON(t); }
|
|
1242
|
+
}
|
|
1243
|
+
// The twin is made before the validator compiles, so it has none of what the
|
|
1244
|
+
// compile prepares: the vendored meta-schemas a reference to json-schema.org
|
|
1245
|
+
// resolves against, vocabularies, and the reading of an `$id` fragment as an
|
|
1246
|
+
// anchor where the dialect is not named. A schema that needs any of these
|
|
1247
|
+
// compiles at its first call as before: a reference other than a JSON pointer
|
|
1248
|
+
// into the document itself, an identifier or anchor, or a `$schema` outside
|
|
1249
|
+
// json-schema.org. With them the twin answered false where the compiled
|
|
1250
|
+
// validator answers true, so isValidObject and validate() disagreed after a
|
|
1251
|
+
// process had met sixteen schemas.
|
|
1252
|
+
const TWIN_DECLINES = /"\$(?:id|anchor|dynamicAnchor|recursiveAnchor|dynamicRef|recursiveRef|vocabulary)"|"\$ref":"(?!#\/|#")|"\$schema":"(?!https?:\/\/json-schema\.org\/)/;
|
|
1253
|
+
function smallTwin (self) {
|
|
1254
|
+
const { _interpArgs } = core_();
|
|
1255
|
+
return new SmallTwin(self, require('./interpreter').createInterpreter(self._schemaObj, _interpArgs(self, self._schemaObj)));
|
|
1256
|
+
}
|
|
1197
1257
|
// Whether the schema may expand past `limit`, answered from its text where
|
|
1198
1258
|
// that settles it. Without a `$ref` nothing expands, and expandedChars counts
|
|
1199
1259
|
// at most 8 for each character of the text, so a short schema is decided
|
|
@@ -1208,11 +1268,16 @@ function mayExpandPast (self, limit) {
|
|
|
1208
1268
|
|
|
1209
1269
|
function coldTwin (self, Validator) {
|
|
1210
1270
|
const n = self._coldCalls === undefined ? 0 : self._coldCalls;
|
|
1211
|
-
|
|
1271
|
+
// Past COLD_CALLS the validator compiles, and the twin is let go: it was
|
|
1272
|
+
// kept for the life of the validator, an interpreted copy of every large
|
|
1273
|
+
// schema in memory beside its generated code.
|
|
1274
|
+
if (n >= COLD_CALLS || self._validateTail !== null || self._verdictTail !== null) { self._twin = null; return null; }
|
|
1212
1275
|
let t = self._twin;
|
|
1213
1276
|
if (t === undefined) {
|
|
1214
1277
|
t = null;
|
|
1215
|
-
if (!self._interpretOnly && !self._initialized && mayExpandPast(self, COLD_MIN)) {
|
|
1278
|
+
if (!self._interpretOnly && !self._initialized && !SMALL_TWIN_OFF && ++_firstCalls > MANY_SCHEMAS && !self._usesKeywords && !self._userFormats && !self._verbose && !self._options.abortEarly && canGenerate() && !compileCached(self) && !self._needsPreprocess() && !mayExpandPast(self, COLD_MIN) && !TWIN_DECLINES.test(self._schemaStr)) {
|
|
1279
|
+
try { t = smallTwin(self); } catch { t = null; }
|
|
1280
|
+
} else if (!self._interpretOnly && !self._initialized && mayExpandPast(self, COLD_MIN)) {
|
|
1216
1281
|
try {
|
|
1217
1282
|
t = new Validator(self._rawSchema, Object.assign({}, self._options, { engine: 'interpreter' }));
|
|
1218
1283
|
// The registry as it is now, not as the constructor options had it:
|
|
@@ -1226,6 +1291,9 @@ function coldTwin (self, Validator) {
|
|
|
1226
1291
|
} catch { t = null; }
|
|
1227
1292
|
}
|
|
1228
1293
|
self._twin = t;
|
|
1294
|
+
// A key kept for a compile that does not follow now would go stale (a
|
|
1295
|
+
// registry changed before the validator compiles).
|
|
1296
|
+
if (t !== null) { _keySelf = null; _key = null; }
|
|
1229
1297
|
}
|
|
1230
1298
|
if (t === null) return null;
|
|
1231
1299
|
self._coldCalls = n + 1;
|
|
@@ -1234,6 +1302,7 @@ function coldTwin (self, Validator) {
|
|
|
1234
1302
|
|
|
1235
1303
|
module.exports = {
|
|
1236
1304
|
jsCompiler,
|
|
1305
|
+
keyFor,
|
|
1237
1306
|
coldTwin,
|
|
1238
1307
|
// The native locator for error frames, loaded with the first frame.
|
|
1239
1308
|
nativePositions: () => require('./native-positions').nativeTargeted,
|
package/lib/compiled-common.js
CHANGED
|
@@ -19,7 +19,8 @@ function carriesDefaults (mod) {
|
|
|
19
19
|
}
|
|
20
20
|
function defaultsFor (mod, schema) {
|
|
21
21
|
if (carriesDefaults(mod)) return typeof mod.applyDefaults === 'function' ? mod.applyDefaults : null;
|
|
22
|
-
|
|
22
|
+
const d = require('./compiled-defaults');
|
|
23
|
+
return (typeof d === 'function' ? d : d.default)(schema);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
// The Validator options the wrapper answers the same way as the runtime.
|
package/lib/email-literal.js
CHANGED
|
@@ -24,6 +24,9 @@ const IPV6_RE = (() => {
|
|
|
24
24
|
// function it is written into when the literal is not an address.
|
|
25
25
|
let _src = null;
|
|
26
26
|
module.exports = () => _src || (_src =
|
|
27
|
-
|
|
27
|
+
// The tag is matched without regard to case; outside the unicode flag /i
|
|
28
|
+
// folds no character above 127 onto an ASCII one, so this is the same test
|
|
29
|
+
// as comparing each code unit with `|32`.
|
|
30
|
+
`if(/^ipv6:/i.test(_in)){const _i6=_in.slice(5);if(_i6.length>45||!${IPV6_RE}.test(_i6))return false}` +
|
|
28
31
|
`else if(_in.length>15||!${require('./formats').IPV4}.test(_in))return false`);
|
|
29
32
|
module.exports.IPV6_RE = IPV6_RE;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// The parts of the shared error runtime a set of schemas needs, and the
|
|
4
|
+
// table of error codes it carries. Pure functions over schemas, kept apart
|
|
5
|
+
// from lib/error-runtime.js, which reads the library's files with fs, so a
|
|
6
|
+
// browser bundle can use these without it (see lib/error-runtime.browser.js).
|
|
7
|
+
|
|
8
|
+
// The parts a build can leave out when none of its schemas needs them. The
|
|
9
|
+
// decision is made on the schemas the runtime walks (as embedded, see
|
|
10
|
+
// runtimeFeatures) and errs toward keeping a part: a key with that name
|
|
11
|
+
// anywhere, even inside an enum value, keeps it. A part left out is replaced
|
|
12
|
+
// by a stub that throws, which a schema without that keyword cannot reach.
|
|
13
|
+
const OPTIONAL = {
|
|
14
|
+
formats: { keys: ['format'], stub: 'module.exports = {};' },
|
|
15
|
+
'safe-regex': { keys: ['pattern', 'patternProperties'], stub: "module.exports = { compileSafe() { throw new Error('ata error runtime: built without the pattern engine'); } };" },
|
|
16
|
+
'unique-items': { keys: ['uniqueItems', 'enum', 'const'], stub: "function no() { throw new Error('ata error runtime: built without deep equality'); }\nmodule.exports = { allDistinct: no, duplicatePair: no, _deq: no };" },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Which optional parts these schemas need: { formats, 'safe-regex',
|
|
20
|
+
// 'unique-items' } as booleans.
|
|
21
|
+
function runtimeFeatures(schemas) {
|
|
22
|
+
const keys = new Set();
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
const walk = (n) => {
|
|
25
|
+
if (n === null || typeof n !== 'object' || seen.has(n)) return;
|
|
26
|
+
seen.add(n);
|
|
27
|
+
if (Array.isArray(n)) { for (const x of n) walk(x); return; }
|
|
28
|
+
for (const k of Object.keys(n)) { keys.add(k); walk(n[k]); }
|
|
29
|
+
};
|
|
30
|
+
for (const s of schemas) walk(s);
|
|
31
|
+
const out = {};
|
|
32
|
+
for (const [part, { keys: ks }] of Object.entries(OPTIONAL)) out[part] = ks.some((k) => keys.has(k));
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function codeTable() {
|
|
37
|
+
const { byKw, byFmt } = require('./error-code-of');
|
|
38
|
+
return { byKw, byFmt };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = { OPTIONAL, runtimeFeatures, codeTable };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Browser twin of lib/error-runtime.js, wired by the package.json `browser`
|
|
4
|
+
// field. The shared error runtime is put together from the library's own
|
|
5
|
+
// files with fs, which a build tool does and a page does not; the emitters a
|
|
6
|
+
// page can call (bundleStandalone, toStandaloneModule) never need it. Pulling
|
|
7
|
+
// fs into a browser bundle stopped esbuild outright and left Vite with an
|
|
8
|
+
// empty stand-in.
|
|
9
|
+
const { runtimeFeatures, codeTable } = require('./error-runtime-features');
|
|
10
|
+
|
|
11
|
+
function errorRuntimeSource() {
|
|
12
|
+
throw new Error('errorRuntimeSource() reads the library files and runs at build time, not in a browser');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { errorRuntimeSource, runtimeFeatures, codeTable };
|
package/lib/error-runtime.js
CHANGED
|
@@ -26,38 +26,7 @@ const FILES = [
|
|
|
26
26
|
// The URI walk formats.js loads; left out with formats (see OPTIONAL).
|
|
27
27
|
'uri-core',
|
|
28
28
|
];
|
|
29
|
-
|
|
30
|
-
// decision is made on the schemas the runtime walks (as embedded, see
|
|
31
|
-
// runtimeFeatures) and errs toward keeping a part: a key with that name
|
|
32
|
-
// anywhere, even inside an enum value, keeps it. A part left out is replaced
|
|
33
|
-
// by a stub that throws, which a schema without that keyword cannot reach.
|
|
34
|
-
const OPTIONAL = {
|
|
35
|
-
formats: { keys: ['format'], stub: 'module.exports = {};' },
|
|
36
|
-
'safe-regex': { keys: ['pattern', 'patternProperties'], stub: "module.exports = { compileSafe() { throw new Error('ata error runtime: built without the pattern engine'); } };" },
|
|
37
|
-
'unique-items': { keys: ['uniqueItems', 'enum', 'const'], stub: "function no() { throw new Error('ata error runtime: built without deep equality'); }\nmodule.exports = { allDistinct: no, duplicatePair: no, _deq: no };" },
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Which optional parts these schemas need: { formats, 'safe-regex',
|
|
41
|
-
// 'unique-items' } as booleans.
|
|
42
|
-
function runtimeFeatures(schemas) {
|
|
43
|
-
const keys = new Set();
|
|
44
|
-
const seen = new Set();
|
|
45
|
-
const walk = (n) => {
|
|
46
|
-
if (n === null || typeof n !== 'object' || seen.has(n)) return;
|
|
47
|
-
seen.add(n);
|
|
48
|
-
if (Array.isArray(n)) { for (const x of n) walk(x); return; }
|
|
49
|
-
for (const k of Object.keys(n)) { keys.add(k); walk(n[k]); }
|
|
50
|
-
};
|
|
51
|
-
for (const s of schemas) walk(s);
|
|
52
|
-
const out = {};
|
|
53
|
-
for (const [part, { keys: ks }] of Object.entries(OPTIONAL)) out[part] = ks.some((k) => keys.has(k));
|
|
54
|
-
return out;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function codeTable() {
|
|
58
|
-
const { byKw, byFmt } = require('./error-code-of');
|
|
59
|
-
return { byKw, byFmt };
|
|
60
|
-
}
|
|
29
|
+
const { OPTIONAL, runtimeFeatures, codeTable } = require('./error-runtime-features');
|
|
61
30
|
|
|
62
31
|
// Lines that hold nothing but a // comment are about half of these files.
|
|
63
32
|
// They go, unless the line sits inside a template literal (tracked by
|