ata-validator 1.33.0 → 1.33.2
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 +22 -21
- package/index.js +16 -7
- package/lib/clone-emit.js +5 -0
- package/lib/js-compiler.js +6 -2
- package/lib/version.js +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -98,20 +98,21 @@ file.
|
|
|
98
98
|
|
|
99
99
|
| Dimension | Schema | ata-AOT | runtime validator | Difference |
|
|
100
100
|
|---|---|---|---|---|
|
|
101
|
-
| Bundle (gzipped) | simple | 1.3 KB |
|
|
102
|
-
| Bundle (gzipped) | complex | 7.8 KB |
|
|
103
|
-
| Bundle (gzipped) | nested | 4.5 KB |
|
|
104
|
-
| Cold start | simple | 22 ms |
|
|
105
|
-
| Throughput (1M ops) | simple |
|
|
106
|
-
| Compile time | simple |
|
|
101
|
+
| Bundle (gzipped) | simple | 1.3 KB | 58.1 KB | 43.9x smaller |
|
|
102
|
+
| Bundle (gzipped) | complex | 7.8 KB | 58.1 KB | 7.4x smaller |
|
|
103
|
+
| Bundle (gzipped) | nested | 4.5 KB | 58.1 KB | 12.9x smaller |
|
|
104
|
+
| Cold start | simple | 22 ms | 44 ms | 2.0x faster |
|
|
105
|
+
| Throughput (1M ops) | simple | 266 Mops/s | 109 Mops/s | 2.4x faster |
|
|
106
|
+
| Compile time | simple | 19 µs | 1.67 ms | 87x faster |
|
|
107
107
|
|
|
108
108
|
The runtime column is the default validator most frameworks ship. Reproduce on your machine
|
|
109
|
-
with `npm run bench:aot-vs-ajv`. Numbers from one run on Apple M4 Pro, Node 25.2.1, 2026-09-
|
|
110
|
-
on ata-validator 1.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
with `npm run bench:aot-vs-ajv`. Numbers from one run on Apple M4 Pro, Node 25.2.1, 2026-09-28,
|
|
110
|
+
on ata-validator 1.33.1. The runtime column's bundle measured 52.7 KB on the previous run and
|
|
111
|
+
58.1 KB now, with ata's modules unchanged in size. Across three runs throughput moved
|
|
112
|
+
between 231 and 280 Mops/s against 97 to 109, cold start between 21 and 22 ms against 40 to 44,
|
|
113
|
+
and the compile ratio between 80x and 87x. The throughput row times a single one-million-call
|
|
114
|
+
loop of a few milliseconds, so it moves the most from run to run; treat the last three rows as
|
|
115
|
+
an order of magnitude rather than a constant.
|
|
115
116
|
|
|
116
117
|
The wins are largest on bundle size and compile time because AOT moves work from runtime to
|
|
117
118
|
build time. Throughput and cold start are also faster because the compiled validator is a
|
|
@@ -239,20 +240,20 @@ is the most common way to get a misleading number out of this library.
|
|
|
239
240
|
|
|
240
241
|
| | compiled with `ata build` | runtime `new Validator(schema)` |
|
|
241
242
|
|---|---|---|
|
|
242
|
-
| In a bundle, gzipped | **2.
|
|
243
|
-
| Time to a served request | **3.
|
|
243
|
+
| In a bundle, gzipped | **2.2 KB** | 92.3 KB |
|
|
244
|
+
| Time to a served request | **3.5 ms** | 11.2 ms |
|
|
244
245
|
| Schema known when | build time | any time |
|
|
245
246
|
|
|
246
247
|
The bundle row is the ten-field user schema in `tests/fixtures/error-dx/user.schema.json`,
|
|
247
248
|
every export of the compiled module against `new Validator(schema)`, built with
|
|
248
|
-
`bun build --minify --target=browser` on ata 1.
|
|
249
|
+
`bun build --minify --target=browser` on ata 1.33.1. The startup row is a Hono route on
|
|
249
250
|
Bun 1.4, best of seven, from `benchmark/bundle`, against 3.5 ms for the same app doing no
|
|
250
251
|
validation at all, so the compiled path costs nothing measurable to start. The runtime
|
|
251
252
|
figure is what it is because a schema that arrives at run time can use any keyword, so
|
|
252
253
|
the whole engine has to be there. The compiled module imports nothing and contains only
|
|
253
254
|
the checks your schema asks for.
|
|
254
255
|
|
|
255
|
-
**On a server, use whichever fits your schemas.**
|
|
256
|
+
**On a server, use whichever fits your schemas.** 92 KB of JavaScript on a Node or Bun
|
|
256
257
|
process is not a cost anyone notices, and the runtime API is the simpler thing to reach
|
|
257
258
|
for. Speed is the same either way once warm.
|
|
258
259
|
|
|
@@ -526,13 +527,13 @@ npx ata build 'schemas/*.json' --out-dir build/validators --check
|
|
|
526
527
|
Run with `--watch` during development for incremental rebuilds.
|
|
527
528
|
|
|
528
529
|
Bundle sizes for the 10-field user schema in `tests/fixtures/error-dx/user.schema.json`,
|
|
529
|
-
minified and gzipped, measured with `bun build --minify --target=browser` on ata 1.
|
|
530
|
+
minified and gzipped, measured with `bun build --minify --target=browser` on ata 1.33.1:
|
|
530
531
|
|
|
531
532
|
| What the app imports | Size | Notes |
|
|
532
533
|
|---|---|---|
|
|
533
|
-
| `Validator` from `ata-validator` |
|
|
534
|
-
| `isValid` from the compiled module | **1.
|
|
535
|
-
| `validate` from the compiled module | **2.
|
|
534
|
+
| `Validator` from `ata-validator` | 92.3 KB | The compiler ships with it, because a runtime schema can use any keyword |
|
|
535
|
+
| `isValid` from the compiled module | **1.3 KB** | Nothing else is reachable, so the error collector is dropped |
|
|
536
|
+
| `validate` from the compiled module | **2.0 KB** | Adds the detailed error collector |
|
|
536
537
|
|
|
537
538
|
`--abort-early` makes the generated source about three times smaller, and after
|
|
538
539
|
bundling it makes no difference: importing only `isValid` already leaves the error
|
|
@@ -555,7 +556,7 @@ through a `setFormats()` export the module carries. `docs/API.md` has the
|
|
|
555
556
|
details.
|
|
556
557
|
|
|
557
558
|
**Fastify startup, 10 route schemas, from a cold process to the first validated request:
|
|
558
|
-
ajv 20.2 ms, ata
|
|
559
|
+
ajv 20.2 ms, ata 2.7 ms, no build step required.** ata registers in 0.2 ms of that and
|
|
559
560
|
compiles on the first request, so counting only registration would overstate the gap.
|
|
560
561
|
Reproduce with `node benchmark/bench_fastify_boot.mjs`.
|
|
561
562
|
|
package/index.js
CHANGED
|
@@ -2943,13 +2943,22 @@ Validator.prototype.parse = function (data) {
|
|
|
2943
2943
|
};
|
|
2944
2944
|
|
|
2945
2945
|
function _buildParse(self) {
|
|
2946
|
-
const decline = (why) => () => {
|
|
2947
|
-
throw new TypeError(`parse() is not available for this validator: ${why}.
|
|
2946
|
+
const decline = (why, instead = 'Use validate() with removeAdditional instead.') => () => {
|
|
2947
|
+
throw new TypeError(`parse() is not available for this validator: ${why}. ${instead}`);
|
|
2948
2948
|
};
|
|
2949
2949
|
const o = self._options;
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2950
|
+
// Each refusal names its own reason: a caller who hit the combined one could
|
|
2951
|
+
// not tell a coercion option from a keyword package, and read a deliberate
|
|
2952
|
+
// refusal as a failure of valid data.
|
|
2953
|
+
if (o.coerceTypes) return decline('coerceTypes rewrites the input before it is checked');
|
|
2954
|
+
if (o.removeAdditional === 'all') return decline("removeAdditional: 'all' decides the kept keys at check time");
|
|
2955
|
+
if (self._usesKeywords) return decline('custom keywords are in use, and what they accept is not known to the copy');
|
|
2956
|
+
// Checks added to the validator (withKeywords from @ata-project/keywords
|
|
2957
|
+
// adds instanceof and typeof) only narrow what passes; they do not change
|
|
2958
|
+
// which keys the schema declares. The verdict then goes through
|
|
2959
|
+
// isValidObject, which runs them, and a property they check with instanceof
|
|
2960
|
+
// is carried over as it is (see clone-emit).
|
|
2961
|
+
const extended = self._verdictTail !== null || self._validateTail !== null;
|
|
2953
2962
|
const { cloneExprFor } = require('./lib/clone-emit');
|
|
2954
2963
|
const expr = cloneExprFor(self._schemaObj);
|
|
2955
2964
|
if (!expr) return decline('the set of keys to keep cannot be proven from the schema');
|
|
@@ -2961,8 +2970,8 @@ function _buildParse(self) {
|
|
|
2961
2970
|
return decline('code generation is not allowed here');
|
|
2962
2971
|
}
|
|
2963
2972
|
self._ensureCompiled();
|
|
2964
|
-
const verdict = self._jsFn;
|
|
2965
|
-
if (typeof
|
|
2973
|
+
const verdict = extended ? (d) => self.isValidObject(d) : self._jsFn;
|
|
2974
|
+
if (typeof self._jsFn !== 'function') return decline('the schema has no generated verdict function');
|
|
2966
2975
|
return (data) => {
|
|
2967
2976
|
if (!verdict(data)) {
|
|
2968
2977
|
const e = new Error('validation failed');
|
package/lib/clone-emit.js
CHANGED
|
@@ -293,6 +293,11 @@ function emitValueExpr(prop, read, depth) {
|
|
|
293
293
|
if (!prop || typeof prop !== 'object') return null;
|
|
294
294
|
if (prop.$ref) return null;
|
|
295
295
|
if (isPrimitiveOnly(prop)) return read;
|
|
296
|
+
// A value checked with `instanceof` (the keyword @ata-project/keywords adds)
|
|
297
|
+
// is an instance of a class, such as a Date, not a JSON object whose keys
|
|
298
|
+
// the schema declares. It is carried over as it is, the way zod hands back
|
|
299
|
+
// a Date; copying its declared keys would turn a Date into {}.
|
|
300
|
+
if (prop.instanceof !== undefined) return read;
|
|
296
301
|
const isRecordable = (s) => s.properties ||
|
|
297
302
|
(s.additionalProperties && typeof s.additionalProperties === 'object');
|
|
298
303
|
if (prop.type === 'object' && isRecordable(prop)) {
|
package/lib/js-compiler.js
CHANGED
|
@@ -1002,9 +1002,13 @@ function withPlain (schema, v, lines, ctx, run) {
|
|
|
1002
1002
|
// The same test at run time, for the closure compiler.
|
|
1003
1003
|
const _hop = Object.prototype.hasOwnProperty
|
|
1004
1004
|
const _PN = ({}).__proto__ === Object.prototype
|
|
1005
|
+
// One hasOwnProperty call. From 1.31.2 to 1.33.0 this first tested the name
|
|
1006
|
+
// against Object.prototype's names, then `in`, then read the prototype through
|
|
1007
|
+
// the __proto__ accessor, on every property of every object the eval-free
|
|
1008
|
+
// engine checked: a schema of 14 properties went from 641 to 828 ns a verdict.
|
|
1009
|
+
// hasOwnProperty gives the same answer and V8 inlines it.
|
|
1005
1010
|
function hasOwnKey (d, key) {
|
|
1006
|
-
|
|
1007
|
-
return key in d && ((_PN ? d.__proto__ : Object.getPrototypeOf(d)) === Object.prototype || _hop.call(d, key))
|
|
1011
|
+
return _hop.call(d, key)
|
|
1008
1012
|
}
|
|
1009
1013
|
|
|
1010
1014
|
const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'toString', 'valueOf',
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.33.
|
|
3
|
+
"version": "1.33.2",
|
|
4
4
|
"description": "JSON Schema validation that compiles for speed and still runs where code generation is blocked. Compiled and interpreted engines answer identically at 100% of the official suite. TypeScript inference, Standard Schema V1, and a build step that emits dependency-free modules.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -121,13 +121,13 @@
|
|
|
121
121
|
"LICENSE"
|
|
122
122
|
],
|
|
123
123
|
"optionalDependencies": {
|
|
124
|
-
"@ata-validator/native-darwin-arm64": "1.33.
|
|
125
|
-
"@ata-validator/native-darwin-x64": "1.33.
|
|
126
|
-
"@ata-validator/native-linux-arm64-gnu": "1.33.
|
|
127
|
-
"@ata-validator/native-linux-arm64-musl": "1.33.
|
|
128
|
-
"@ata-validator/native-linux-x64-gnu": "1.33.
|
|
129
|
-
"@ata-validator/native-linux-x64-musl": "1.33.
|
|
130
|
-
"@ata-validator/native-win32-x64": "1.33.
|
|
124
|
+
"@ata-validator/native-darwin-arm64": "1.33.2",
|
|
125
|
+
"@ata-validator/native-darwin-x64": "1.33.2",
|
|
126
|
+
"@ata-validator/native-linux-arm64-gnu": "1.33.2",
|
|
127
|
+
"@ata-validator/native-linux-arm64-musl": "1.33.2",
|
|
128
|
+
"@ata-validator/native-linux-x64-gnu": "1.33.2",
|
|
129
|
+
"@ata-validator/native-linux-x64-musl": "1.33.2",
|
|
130
|
+
"@ata-validator/native-win32-x64": "1.33.2"
|
|
131
131
|
},
|
|
132
132
|
"peerDependencies": {
|
|
133
133
|
"yaml": "^2.0.0"
|