ata-validator
1.61.3 · 29 Oct 2026MIT21.8k / weekRepositoryHomepagenpmAtom feed
1.61.3 is the newest release of 1.x.
In short
6 new1 fixed
Release note
Fixed
Emitted standalone modules compiled their regex patterns on every call. The compiler stores each pattern both as a closure variable and as an inline
const _reN = __ataSafeRe(...)in the stored source; in process the inline line never runs (the closure binds the compiled matcher once), but every emitter placed it inside the emitted function, where it shadowed the module-scope binding and rebuilt the matcher per call. A schema with onepatternvalidated at 202 thousand ops/s as a standalone module while the in-process path ran at millions. Patterns are now declared once at load scope in all four emitters (toStandalone,toStandaloneModule,bundleStandalone,bundleCompact); the same document on the same schema now measures 27.0 million ops/s, 134x.bundleCompactneeded care: its deduplication keyed on body text alone, and with the pattern no longer in the body, two schemas differing only in their pattern would have merged, so the key now includes the factory-scope declarations.
Added
Provably local
unevaluated*compiles real error detail on the generated-code path, and therefore in standalone modules. When every occurrence ofunevaluatedProperties/unevaluatedItemssits on a node with no$refand nopatternProperties, and every in-place applicator (allOf,anyOf,oneOf,if/then/else) contributes only property names the node's ownpropertiesalready declares, the keyword is exactlyadditionalProperties/itemsin disguise and both the error and combined generators now emit it as such, keeping the keyword's own identity: one error per stray key withparams.unevaluatedProperty(ATA7003), one error withparams.limitfor extra items.toStandaloneModulewith error detail requested no longer degrades on these schemas; a config schema of the commonif/then/elseplus rootunevaluatedProperties: falseshape ships an AOT module with the same per-field errors the runtime reports. Schemas where the annotations cannot be proven local (unevaluated*next to a$ref, cousin-visible shapes) still decline, loudly, as before. Verified by the error-shape differential (2,816 cases), the engine differential (18,126 cases) and the official suite on all three dialects.toStandaloneModule(schema, { positions: true })also exportsvalidateJSON(text): parse, validate, and on failure attach adataFrame(byteOffset,length,line,col,text) to every error by walking the original text once. The mapping goes throughinstancePath, so a key that appears in several sections frames each error on its own occurrence, which a first-occurrence string search does not. A syntax error is the single ATA9001 error with a frame on the document. The walker is the runtime's ownbuildDataPositionMap, embedded verbatim so the two cannot drift, and it costs about 5 KB in the module (1.7 KB gzipped), which is why it is opt-in.toStandaloneModule(schema, { parse: true })'sparse(data)now fills declareddefaultvalues for absent optional properties (object and array defaults are fresh per call) and is emitted for composed schemas under the same proof theunevaluated*error generators use: in-place applicators are admitted when every property name they mention is already declared in the node's ownproperties. The common config shape,if/then/elseover declared fields with rootunevaluatedProperties: false, now gets aparsethat enforces the conditional, rejects undeclared keys, and returns the ready object. Two default shapes decline because they are whereparseand the runtime would disagree: a required property with a default, and a default the property's own schema rejects.Every emitted module exports
schemaHash, a 16 character content hash of the schema it was compiled from, over canonical JSON so key order does not matter, computed from the schema as the caller wrote it so dialect normalization does not break the comparison.schemaHash(schema)fromata-validator/buildcomputes the same value; a build detects a stale artifact by comparing the two instead of embedding its own fingerprint.The agreement between compiled modules and the runtime is now a stated contract, in
docs/STABILITY.mdand the API reference: same version, same verdict on every document, enforced by the differential test in every CI run, with the emitters declining or degrading loudly where they cannot prove equivalence.The compat shim exports
attachDataFrames(errors, text): the same one-pass mapping for Ajv-shaped errors, for callers that keep the source text around. Not an Ajv API; it exists because hand-written line finders based on a string search get repeated keys wrong.