ata-validator
1.61.3 · 29 Oct 2026MIT21.8k / weekRepositoryHomepagenpmAtom feed
Upgrading ata-validator from 1.58.1 to 1.61.3
6 releases in betweennotes for 3
Reading releases, release notes and advisories…
Release by release
1.61.1 – 1.61.33 releases, no release note
- new
A schema with a
defaultanywhere (ata fills defaults without being asked, so it has a preprocess pass) now builds its rich errors where they fail, as other schemas do: the path was closed to schemas with a preprocess pass, though it reads the document validate() has already processed. Onerequirederror on a four-field user with a defaultednewsletterwent from 313 to 248 ns with its error list read; only the one-pass swap, which replaces validate() itself, stays off for such schemas. Found through the fastest-validator comparison in ata-core/validator-benchmarks (issue #74). - new
The Node entry warms the engine once the event loop is free: where a validator was built and is still untouched by then (not compiled, no method read off it), one check of a small schema runs, accepted and rejected, so that V8 compiles the generator's functions and the error builders before the first request instead of on it. On a four-field schema with the generator already loaded, a server's first verdict went from 3.1 to 3.4 ms to 0.5 to 0.6, the first error list from 6.7 ms to 0.8, at 1.5 to 2 ms of CPU once. A script that builds and checks in the same tick never runs it, the timer is unref'd, and
ATA_NO_WARM=1turns it off. The browser build has done the same since 1.57.0 (lib/tiering.js); the cold-start benchmarks, which check in the same tick as they build, are unchanged by it.tests/test_node_warm.jsholds the conditions. - new
ata-validator/buildexportsbunTarget(the schema as a TypeScript type and the.bun.tsmoduleata build --target bunwrites; withtags: truethe split into a type with Bun'sSchematags and a residual schema, made for the typed validator the compiler's pull request carried until 2026-10-25) andverify(verifyOne,verifySource,generate), for a build plugin that replacesnew Validator(schema)inside aBun.build({ compile: { aot: true } })and holds what it wrote against the runtime. - new
ata verify <glob>holds each schema's compiled module against a runtimeValidatorbuilt from the schema now: documents made from the schema (--generate, 200 by default, half with one rule broken), the project's sample documents (--samples, JSON or JSON Lines files or a directory), verdicts and error lists compared, a module whose schema hash no longer matches the schema reported stale, a non-zero exit on any disagreement, stale or missing module,--jsonfor machines. The check behind the fixes of 1.59.0 and 1.60.0, as a command a project runs in CI.
- new
- new
A validator the build compiled away (
fromCompiledaround a module written ahead of time, what@ata-project/unpluginputs in place ofnew Validator(schema)) carries Standard Schema V1 as the runtime Validator does: the same~standardobject, built bylib/standard-schema.jsfor both, with the same issues, value and published schema. A compiled validator can now be handed to a form library, a router or an RPC layer that reads~standard.tests/test_compiled_standard.jscompares the two. - fixed
toStandaloneModule(schema, { coerceTypes })andtoStandaloneModule(validator)for a validator built withcoerceTypesorremoveAdditionalwrote a module that checked the data as it was, so it rejected{ id: "5" }where the validator accepts it. The module now runs the validator's pass over the data before the check, asbundleStandalonedoes since 1.59.0, and refuses where that pass cannot be written into a module.fastify-ata/standalonebuilds its files through this call.
- new
- new
bundleStandalone(schemas, { order: true })hands each validator's errors out in the runtime's order, astoStandaloneModuledoes with the same option. rjsf's precompiled validator writes its module withbundleStandalone, and its error list showed anadditionalPropertieserror before thepropertieserrors where the runtime validator shows it after.tests/test_aot_order_defaults.jsholds 553 bundled groups of the 2020-12 and draft-07 suites to the runtime's errors, in order. - new
Each validator of a bundle whose schema has a default carries
applyDefaults(data), the runtime's defaults pass, as a property, as standalone modules export it. rjsf's precompiled validator can call it to match its runtime validator.tests/test_bundle_preprocess.jscompares 8400 generated documents with the runtime: the data afterwards, the verdict and the errors in order. - fixed
bundleStandaloneleftcoerceTypesandremoveAdditionalout without a word, so a bundle built withcoerceTypes: truerejected{ age: "5" }, which the runtime accepts. Each bundled validator now runs the runtime's pass over the data for those options before it checks, and withuseDefaults: truefills defaults the same way. Where the generated pass cannot carry what the runtime would do (coercing the items of an array at the root, for one),bundleStandalonethrows instead of writing a module that answers differently. Without these options a bundle checks the data as it is, as before.
- new
4 other lines we could not sort, mostly version bumps
- 1.61.0
The standalone module's error collector (the default form, with schema-source frames) reads each property once into a local, names a closed object's declared keys outright instead of a Set lookup per key when there are at most eight, and reuses the node's plain-object flag in that walk instead of reading the prototype again. A rejected ten-item order with its error list built went from 378 to 244 ns on Node (the one-pass form, 102, is unchanged and still opt-in for its size); the module's size does not move. Found by profiling line by line: one closedness walk held 31% of the samples.
- 1.61.0
ata build --target bunwrites the one-pass error function by default and no schema-source frames, as an executable compiled ahead of time pays no bundle size: a rejected ten-item order with the error list built went from 724 to 189 ns under Bun's compiler (on Node, 378 to 105 against 99 for the runtime). The default for every other build stays the older collector with frames, since the one-pass form is 20% larger gzipped over 60 SchemaStore schemas (53 of them larger by more than 5%, the worst 1.6x);--one-passand--no-sourcechoose it by hand. The plugin's ahead-of-time path does the same. - 1.61.0
Generated code walks and counts a closed object's keys in the form of the engine it is written for: for...in on V8, where it is the cheaper form, an indexed loop over
Object.keyson JavaScriptCore, where for...in is a slow path. The runtime writes for the engine it runs on;ata build --target bunand the plugin's Bun entry write for JavaScriptCore. One form is written, so neither engine pays a branch per object (an earlier form with a load-time flag cost V8 5 to 7% on small schemas). A third form,jsc-aot, is what--target bunand the plugin's ahead-of-time path write: a closed object whose declared keys are all required is checked by readingObject.keysonce and comparing it with the declared list in order, the orderJSON.parsegives, with a membership walk when the order differs. Under Bun's compiler that put the plain module level with Bun's ownvalidate<T>on a ten-item order (105 to 107 ns against 96 to 105, from 108 to 112); on Bun's JIT the same form loses 5 to 15 ns, which is why it is its own target. The prototype read picks__proto__on V8 andObject.getPrototypeOfon JavaScriptCore at generation. Measured on a ten-item order: Bun 1.4.4's JIT 65 to 53 ns a document, Bun's ahead-of-time compiler (pull request build) 227 to 108, Node 25 unchanged. - 1.59.0
Requiring the package no longer loads the code generator. It loads when a validator first compiles, or once the event loop is free, so a server reaches its first request without waiting for it. Importing the package went from 4.55 to 1.64 ms (Node 25, the median of 21 interleaved runs), and a server building 20 validators at startup from 4.71 to 1.83 ms. Validators still compile at their first call, and a script that validates as soon as it starts takes as long as before.
benchmark/bench_fastify_boot.mjsnow counts loading the library on both sides, as "from a cold process" says; the README's Fastify figure moves from 18.3 against 2.67 ms (loading left out) to 30.3 against 9.7 ms.