Packages

ata-validator

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

Your version

1.7.2

24 Aug 2026Where it was published

1.61.3 is the newest release of 1.x.

In short

2 fixed5 changed

Release note

Fixed

  • enum compared object members by their serialized form, so {"b": 2, "a": 1} was rejected against enum: [{"a": 1, "b": 2}]. JSON Schema compares instances by value: two objects with the same members are equal whatever order their keys were written in. All three code generation paths did this, so they agreed with each other and disagreed with the interpreted engine, which has always compared structurally. const was already correct but reached the answer by building a canonical string on every call. Both now use one structural comparison hoisted per compiled function. The official suite does not cover reordered keys for enum, and the entry-point agreement test compares the generators against each other, so neither would have caught this; tests/test_value_equality.js pins the answer through every engine that can produce one and runs as part of npm test.

  • A draft-07 document passed through the schemas option was rejected with "Schema in schemas option must have $id" whenever it carried $ref next to $id. Draft-07 ignores every keyword sitting beside $ref, so normalization drops them and $id goes with them, which is the right reading for evaluation: the reference resolves against the URI the document was retrieved from, not the $id it declares. It is the wrong reading for registration, where $id is the only name the caller gave the document. The identity is now read from what the caller passed when normalization has dropped it, so such a document registers under its $id in the array form and is addressable by both its retrieval URI and its $id in the map form. A document that declares no $id at all is still refused, and a fragment-only $id is still an anchor rather than a document name. This is what made all 23 remote-reference cases of the draft-07 suite error out in the Bowtie compliance report; they now answer, and answer correctly.

Changed

  • The closure-tree compiler now covers every schema the interpreted engine accepts. It previously declined unevaluatedProperties/unevaluatedItems and $dynamicRef in any schema with more than one resource, which left 192 of the 987 suite schemas walking the generic evaluator; that count is now zero. Annotations flow through the compiled tree: the compiler decides at compile time which nodes anyone reads annotations from, and those nodes carry a record that in-place children write into directly, with a failed child rolled back by truncating the record rather than by allocating a fresh one per child. $dynamicRef resolves against the dynamic scope computed at compile time, so multi-resource schemas no longer search it per call. A schema whose compiled graph has no cycle drops the recursion guard and the (schema, data) stack entirely. An unresolvable $ref compiles into the same runtime rejection the evaluator produces instead of declining the schema.

  • Measured on the official suite with prebuilt validators, several runs each on the same machine: draft 2020-12 went from 94 ns to 77-80 ns per case overall, and the cases that route to the interpreted engine from about 240 ns to about 125 ns when they accept and 235 ns to 160 ns when they reject. Draft 7 went from 59 ns to 49 ns overall. The metaschema-validation case, the worst outlier in the suite, went from 5.5 µs to about 1.4 µs. On a longer steady-state loop over the same suite, where construction is not part of what is timed, draft 2020-12 went from 46 ns to 43 ns per case and draft 7 from 33 ns to 29 ns.

  • The value-level keywords of a schema node are compiled into closures over their own operands instead of being answered by a generic evaluator that re-reads a dozen presence flags off the plan on every call. The type of the value is computed once per node rather than once per keyword group, and a node whose keywords never need it does not compute it at all.

  • uniqueItems on arrays whose item type is not known ahead of time no longer builds a canonicalizing closure and a Set on every call. The helpers are hoisted once per compiled function; arrays of twelve items or fewer compare pairwise with a structural equality that ignores key order and allocates nothing, and longer arrays keep the Set, using canonical strings only once an object actually appears. Over the draft 7 suite the uniqueItems cases went from 125 ns to 24 ns each.

  • ARCHITECTURE.md documents the interpreted engine and the closure-tree compiler, and no longer claims $dynamicRef falls back to the native engine, which stopped being true in 1.7.1.