ata-validator
1.61.3 · 29 Oct 2026MIT21.8k / weekRepositoryHomepagenpmAtom feed
1.61.3 is the newest release of 1.x.
In short
3 new5 fixed1 internal
Release note
OpenAPI validators can run on ata-validator/compat. express-openapi-validator's own test suite passes 470 of 470 with ata injected, as it does on the default validator, and loads GitHub's and Stripe's API descriptions, which the default validator cannot load. Custom keywords can read where their value sits, and compat now coerces, fills defaults and reads discriminator where the reference does. Two engine disagreements are fixed.
Measured on an Apple M4 Pro, Node 25.2.1, against 1.52.0 with both sides in worktrees, interleaved: cold require, compile and first validate 8.31 to 8.18 ms, Fastify's ten routes to first validated request 2.83 to 2.92 ms, warm compile 60.7 to 63.0 µs a schema, request cost within 3%, hot validation the same (that run moves between 19.5 and 24.5 ns a document on both versions). The browser bundle grows from 125.6 to 128.5 KB gzipped: the interpreted engine carries what compat needs from it (coercion where each type is checked, the discriminator, number formats, post keywords). Standalone modules are byte for byte the same size. On SchemaStore's 31,822 sample documents the engines give the same answers.
Added
Custom keywords can ask for their data's context with
dataContext: true: the per-value function is also given{ instancePath, parentData, parentDataProperty, rootData }, as the default validator's keywords are, so it can read or remove the value where it sits. A schema using one runs on the interpreted engine's collecting walk, which knows where every value is; other schemas are untouched.ata-validator/compatgives every keyword this context andit.schemaPath, which is what express-openapi-validator'sreadOnlyandwriteOnlykeywords use to strip read-only properties from a request.ata-validator/compattakes what a library sets up on it in its own code: the formats plugin'saddFormats()is accepted and registers its formats as the reference does, a keyword registered under a list of names is registered under each, and a draft-04 schema (OpenAPI 3.0's meta-schema) is read as draft-07 with itsidas$idand checked against draft-07's meta-schema after its boolean bounds are normalized. A keyword defined only as generated code is now refused when a schema uses it rather than when it is registered, so a library that registers such a keyword for an option it may not use can take the class. With these, express-openapi-validator runs oncompatwith one change of its own (a portablex-eov-type) and validates requests against GitHub's and Stripe's API descriptions, which the default validator cannot load.ata-validator/compatdoes what the reference does while it validates, for the schemas that need it.coerceTypesconverts a value where eachtypeis checked, so it reaches values behindanyOf,oneOf,allOfand$ref;useDefaultsfills defaults behindallOfand$ref; thediscriminatoroption applies theoneOfbranch the tag names; a schema registered under a fragment-only key such as#/components/schemas/Petis found by a$refof that string; a format registered withtype: 'number'is checked on numbers; and custom keywords takebeforeandpostto order keywords that change the data. Errors name the schema a$refpoints to, andoneOfandanyOfwith no passing branch use the reference's wording. express-openapi-validator's own suite, run with ata injected through a small factory option, passes 470 of 470, as it does on the default validator; it passed 429 before these. On 2400 generated documents with composition andcoerceTypes: true, verdicts and coerced data match the reference in every case. A schema keeps its compat copy across compiles, so the work done once per shared schema is not done again: with express-openapi-validator on Stripe's API description, the first request on a second route went from 280 to 200 ms, and the first request overall stays at 1.1 s on both Stripe's and GitHub's.
Fixed
A declared property whose value is
undefinedgave different answers on different engines: the generated code skipped it, as the default validator does, and the interpreted engine and the closures used where code generation is blocked checked it and rejected. All of them now skip it. No JSON document carries such a value, but an object about to be serialized often does ({ id: req.params.id }with no such parameter).tests/test_undefined_property.jsholds the engines together.OpenAPI's
nullable: truewas not applied in a schema reached only through a$refinto a place that is not a subschema keyword, such as an OpenAPIcomponentssection, sonullwas rejected there. The normalization now follows local references.ata-validator/compatreads a rejection about 37% faster and compiles its first schemas about 30% faster. Its errors are reshaped into the reference's own, so the validator underneath no longer builds the rich fields that are then dropped, and each schema path is split and classified once rather than on every rejection. The meta-schema that checks every compiled schema runs on the interpreter, which has nothing to compile: ten route schemas throughexpress-json-validator-middlewarewent from 7.7 to 5.5 ms to compile and answer once, and a rejected body with every error collected from 4.1 to 2.6 µs (the default validator takes 1.9).tests/test_ajv_parity.jspasses unchanged.A schema that needs normalizing (OpenAPI's
nullable, draft-04 booleanexclusiveMinimum, draft-07 and 2019-09 spellings) compiles about twice as fast when it is large. The copy is built as ordinary objects rather than prototype-less ones reattached at the end, which V8 kept in its slow dictionary mode, and the check for "did normalization change anything" compares the copy in place instead of serializing the whole document twice. GitHub's 11 MB of OpenAPI components went from 728 to 320 ms to construct five times; openapi-backend loading Stripe's API description throughcustomizeAjvwithata-validator/compatwent from 14.6 to 6.6 s. The default validator cannot load either document: it stops at"nullable" cannot be used without "type", and with those removed it ran ten minutes on Stripe's and stopped when the process ran out of memory at 3.3 GB.A normalized schema is kept per schema object, so a registry of hundreds of schemas is not normalized again for every validator built over it. express-openapi-validator registers each of an OpenAPI document's components; its first request on GitHub's API description went from 10.5 to 1.1 s, and on Stripe's from 3.7 to 1.1 s. A validator built with
assertFormat: falsestripsformatfrom a copy of its own, so the shared schema keeps its checks for every other validator;tests/test_normalized_cache.jsholds that, and the whole test suite and the SchemaStore differential were run once with the shared copies frozen, with no write to one.