Packages

ata-validator

1.61.3 · 29 Oct 2026MIT21.8k / weekRepositoryHomepagenpmAtom feed

Your version

1.49.0

10 Oct 2026Where it was published

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.js now 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 enum test 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.hasOwn about it, and asks only when the read gives undefined, for plain objects and keys Object.prototype does 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; with useDefaults: false it 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, explicit undefined and __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 items and additionalItems, and under draft-06 its dependencies and the reading of $ref siblings, 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 unevaluatedProperties or unevaluatedItems sat beside a $ref back to the document root, as in a tree whose branches say { unevaluatedProperties: false, $ref: '#' }: what the root evaluates through its own $ref was not counted. Such a schema now goes to the interpreted engine, which answers it. Found through 2019-09's $recursiveRef tests; the shape is plain 2020-12 and the 2020-12 suite does not contain it.

  • A standalone module built without --one-pass reported every error with an extra _o field, 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.