ata-validator
1.61.3 · 29 Oct 2026MIT21.8k / weekRepositoryHomepagenpmAtom feed
1.61.3 is the newest release of 1.x.
In short
2 fixed4 changed
Release note
Faster where ata was slowest, smaller server bundles, and both engines answering alike on objects that inherit keys. Three changes to the generated code take on the SchemaStore schemas where ata lost to the default validator, a server bundle leaves out the tools an application does not import, and the generated code now reads only an object's own keys, as the interpreter does.
Measured on an Apple M4 Pro, Node 25.2.1. On SchemaStore's 446 schemas with sample documents, ata is slower than the default validator on 42 rather than 52, and the median ratio moves from 1.68x to 1.72x; among schemas of 50 to 200 KB it is slower on one rather than five. Against 1.51.0: require and first check within noise (4.95 to 5.06 ms and 4.90 to 4.87 in the two orders), a Fastify app's first validated request the same, hot paths the same with code generation and 6 to 9% faster without it, heap per validator the same. The browser bundle is 1.0 KB larger and the edge bundle 0.1 KB. Standalone modules grow where a schema walks object keys: across SchemaStore's 875, the gzipped total grows 4.0%, the median changed module 3.2%, and the ten-field user schema in the README not at all.
Changed
A
oneOfwhose branches each allow only their own properties ({ properties: {...}, additionalProperties: false }, no property in two branches) is answered by the branch the object's first key names instead of trying every branch. Traefik's file-provider schemas, 25 middleware branches, go from 10.8 to 1.2 µs a document. Values that are not objects, and objects with no keys, still try every branch.A small object definition referenced from many places in a large schema is written as several functions with the same body, each serving at most four references. One function called with objects of dozens of shapes falls off V8's fast property reads; SchemaStore's detekt sends 72 shapes through one rule definition and goes from 6.3 to 1.1 µs a document. Definitions without properties are not copied.
The linear-time pattern engine, which runs the patterns the platform RegExp cannot be trusted with, steps through one flat transition table of 16-bit entries instead of a state object per character: 25 to 35% less per test on SchemaStore's patterns, and less memory than before (17.9 against 21.2 KB per pattern in use). dart-build goes from 2.82 to 1.96 µs a document, popxf from 6.44 to 4.46, apple-app-site-association from 0.73 to 0.50.
A server bundle built with esbuild, Rollup or webpack reads a new ESM entry,
index.bundle.mjs, through themodulecondition, which those bundlers match and Node does not. It imports each tool on its own, as the browser entry does, so the tools an application does not import stay out: an app that imports onlyValidatorgoes from 142.8 to 135.0 KB gzipped with esbuild, 150.0 to 141.5 with Rollup and 134.9 to 127.1 with webpack. Node and Bun at run time keep the entries they had, since loading every tool up front cost 7.6 ms in Node before 22.10 and 1.3 ms in Bun.bun builddoes not matchmoduleunless asked:bun build --conditions=modulegives 134.7 KB against 142.6.
Fixed
The generated code walked an object's keys with
for...in, which also visits enumerable keys its prototype carries, where the interpreter reads own keys only. A class instance or an object made withObject.createwas rejected whereadditionalPropertiesis false, and undernotthat turned into accepting what the interpreter rejects: on the official suite's object data rebuilt over such prototypes, 236 verdicts and 102 error lists differed, two of them accepts. The key loops of both generators now skip keys an object does not own, with the prototype read once per loop; an ordinary object, whose prototype is Object.prototype, takes its keys as before, soJSON.parseoutput pays nothing.tests/test_inherited_keys.jsholds the 2,918 variants. A pollutedObject.prototypeis not covered: the generated code takes an ordinary object's prototype as clean, as the default validator does.The shared runtime (
ata-errors.mjs, schema modules,errorRuntimeSource) called its bundled files through a local function namedrequire. A tool that reads the file without scope tookrequire('./interpreter-core')for an import of a file that is not there: knip reported ten unresolved imports in a project that checks its generated sources. The local function has another name now.