ata-validator
1.61.3 · 29 Oct 2026MIT21.8k / weekRepositoryHomepagenpmAtom feed
1.61.3 is the newest release of 1.x.
In short
3 fixed4 changed1 internal
Release note
Draft 2019-09 and draft-06 are read rather than taken for 2020-12, which accepted documents those schemas reject; the generated code gets cheaper on configuration-style schemas (defaults, enums, patterns); and a few error-path and correctness fixes.
Measured against 1.48.0 on an Apple M4 Pro, Node 25.2.1, pure JS: on 20 real API schemas, validating a valid document 10 to 11% faster and a realistic invalid one 8 to 9%; compiled modules 4.4% smaller gzipped; cold start unchanged (4.87 to 4.56 ms require, inside its noise). On SchemaStore's rustfmt schema, a document that already carries its 82 defaults validates in 52 ns where 1.48.0 took 827.
Changed
More patterns run on the platform RegExp instead of the linear-time engine. The rule in
lib/regex-linear.jsnow also accepts a pattern that is deterministic around its loops, on the Glushkov construction: the characters that can follow a position inside a loop are pairwise disjoint, a position reaches a follower by one route only (so(a+)+and(\w+\s?)*stay out), no loop body matches the empty string, and the pattern is anchored. That takes in dotted identifiers,^[^/]+/[^/]+$, version numbers and(ab)*. Over SchemaStore's 1277 patterns 90 more go native, with no answer difference on 18000 random inputs and no slow run on 20000-character adversarial probes; the engine is 3.5 to 5x the RegExp per character, so a schema whose checks are mostly patterns moves accordingly (SchemaStore's elm from 289 to 89 ns a document and mimetypes from 176 to 65, against the default validator's 161 and 82).An
enumtest on a property binds the value once instead of reading the property for every member: a 32-member enum on a required property read it 32 times. On an array of 32 items in six shapes, 2694 to 1507 ns; SchemaStore's devinit 4650 to 2454.The linear-time engine steps the last character outside its loop, so the loop selects no table per character; a few percent on long strings, no change in answers.
The generated defaults pass reads a key before it asks
Object.hasOwnabout it, and asks only when the read gives undefined, for plain objects and keysObject.prototypedoes not have. On a configuration schema with 82 defaults (SchemaStore's rustfmt),validate()on a document that already carried every key cost 827 ns and now costs 52; withuseDefaults: falseit is 12. What is written is unchanged: a new test pins the generated pass to the closure pass on documents with inherited names, null prototypes, explicitundefinedand__proto__keys. Found by timing SchemaStore's 431 sample-bearing schemas against the default validator, where ata lost on the schemas with many defaults and nowhere else for this reason.
Fixed
A draft-06 or Draft 2019-09 schema was read as 2020-12 as written, so its tuple
itemsandadditionalItems, and under draft-06 itsdependenciesand the reading of$refsiblings, were not applied: documents those schemas reject were accepted. On the official suites that was 30 cases of draft-06 and 38 of 2019-09. Both dialects are now read properly (see Added).The generated code rejected valid documents when
unevaluatedPropertiesorunevaluatedItemssat beside a$refback to the document root, as in a tree whose branches say{ unevaluatedProperties: false, $ref: '#' }: what the root evaluates through its own$refwas not counted. Such a schema now goes to the interpreted engine, which answers it. Found through 2019-09's$recursiveReftests; the shape is plain 2020-12 and the 2020-12 suite does not contain it.A standalone module built without
--one-passreported every error with an extra_ofield, the ordinal the runtime sorts errors by, which a module never reads: the collector is now compiled without it for modules and bundles, so an error carries the documented keys only (tests/test_aot_error_shape.js). Present since the ordinal was introduced; the one-pass module and the runtime were not affected.