Packages

ata-validator

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

Upgrading ata-validator from 1.49.0 to 1.50.0

1 release in betweenevery note read

Reading releases, release notes and advisories…

Other targets from 1.49.0Newest 1.x, Latest1.61.3
0
changes to make
0
advisories fixed
4
new
0
fixes

Release by release

  1. 1.50.0

    10 Oct 20264 new
    • new

      ata-validator leaves its code generator out where generated code cannot run. Cloudflare Workers and Vercel Edge refuse new Function, so the generator could only fail there and fall back; a bundler building for them sets the workerd or edge-light export condition, and the package now resolves to an entry without it (index.edge.js). Nothing changes in the code that imports it: the same names, and with code generation blocked the same verdicts and errors as the full package, 2230 cases of the 2020-12 and draft-07 suites compared. A Worker with one validated route, bundled by wrangler 4.149.0, went from 912.8 KiB (179.1 KiB gzipped) to 376.4 KiB (70.4 KiB gzipped), and answered the same requests under wrangler dev. Modules from ata build load through it; Validator.bundle and the other emitters point to ata-validator/aot, as in a browser bundle, where they already did. ata-validator/lite stays for pages with a Content-Security-Policy that blocks unsafe-eval and for browser extensions, which no condition identifies. The package's index.js is split for it: the generator's paths are in lib/codegen-engine.js and the public names in lib/public-api.js; requiring the package takes the same time (4.52 against 4.54 ms, fresh processes).

    • new

      Compact modules for production builds. ata build --no-source (or with NODE_ENV=production) can write each module as its verdict function and its schema without the descriptive keywords, and take error detail from one file the build writes beside the modules, ata-errors.mjs, which every module of the build imports. The runtime walks the schema with the interpreted engine when a document fails, so it only runs on rejections. The build chooses by size, per module, and writes the runtime only when the modules that use it save more than it weighs; one small schema keeps its self-contained module, and --errors inline|shared overrides the choice. On seven SchemaStore schemas (abc-supply-plan 13, airlock-microgateway 3, aspire 8, babelrc, github-workflow, prettierrc, tsconfig) imported into one app and minified, the validation code goes from 148.9 to 63.3 KB gzipped and from 35.8 to 13.8 ms to import with code generation blocked (Apple M4 Pro, Node 25.2.1). Forty SchemaStore schemas in one app go from 513.8 to 211.8 KB gzipped (5792.5 to 1566.3 KB minified) and from 61.7 ms to 32.0 ms to import. The runtime carries only what the build's schemas use: format checks, the linear-time pattern engine and deep equality each stay out when no schema has the keyword that reaches them, so the runtime is 11.3 KB gzipped for schemas with none of them and 18.6 KB with all three; 336 of the 445 SchemaStore schemas with sample documents run on a runtime with at least one left out. A compact module also takes the pattern engine from the runtime instead of embedding its own copy, 2.9 KB gzipped, which 147 of 875 SchemaStore modules carried each. On Microsoft's Accessibility Insights assessment schema, the module and its runtime together are 33.6 KB gzipped against 75.9 for the self-contained module. The cost is on rejections: the errors come from an interpreted walk of the document, 398 ns for a rejected request body against a four-field user schema where the self-contained module takes 52, and 57 µs for a 22 KB assessment file with one wrong field where it takes 21 (with several wrong fields, 63 against 94). A build that cares more about rejection speed than size passes --errors inline. toCompactModule, compactDecline and errorRuntimeSource in ata-validator/aot do the same by hand.

    • new

      ata build --mode schema: schema modules, for pages where the bytes shipped matter more than the time per check. A module is its schema without the descriptive keywords and a few lines, and the verdict comes from the shared runtime too, the interpreted engine with its closure tree for a schema used often. Forty SchemaStore schemas in one app, minified, go to 101.6 KB gzipped (212.6 as compact modules, 513.8 self-contained), and loading them and checking each once takes 23.0 ms against 35.5 for compact modules. Once warm a check costs about five times what generated code costs, 3289 against 590 ns a document on SchemaStore samples. The answers are the runtime Validator's: no verdict differs on the official suites in five dialects or on 13741 SchemaStore documents, and every error list of the 11362 rejected ones is the runtime's. toSchemaModule in ata-validator/aot does it by hand.

    • new

      ata-validator/error-runtime, the same error runtime as a package entry, for bundler plugins: compiledModuleFor(schema, { errorRuntime: 'ata-validator/error-runtime' }) returns a compact module that imports it, and the bundler keeps one copy, sharing its error-code table and schema ordering with ata-validator/compiled. Behind fromCompiled, a compact module answers as new Validator(schema) does, rich errors and defaults included: checked on 563 schemas and 1994 cases of the 2020-12 and draft-07 suites. A schema with a $ref that leaves it (a meta-schema the runtime resolves from its own copies, a pointer to nothing) keeps the self-contained module, since the runtime holds the embedded schema and nothing else.

6 other lines we could not sort, mostly version bumps
  • A browser bundle carries only the tools it imports. The ESM entries bundlers read (index.browser.mjs, index.edge.mjs) import TypeScript generation, the renderers, the output format, the retry message and the schema description one by one, where index.js loads them on first call through require, which a bundler follows but cannot drop. An application that imports only Validator, bundled for the browser with esbuild, went from 130.9 KB gzipped (1.49.0) to 123.5 KB. Node keeps loading them on first call.

    1.50.0
  • The interpreted engine (ata-validator/lite, and the default entry wherever code generation is blocked) compiles its closure tree once a schema has been used 1000 times, and answers the calls before that with its plain walk. Building the tree for a whole schema cost as much as 3000 to 30000 plain walks on SchemaStore schemas, so a page or a short-lived worker paid for it on its first check and never got it back; the compiler's own source is now loaded only when it is needed. In a minified bundle with code generation blocked, the first check of a SchemaStore schema of 3.3 KB went from 2.94 to 2.20 ms, 10.8 KB from 5.15 to 4.24, 33 KB from 6.34 to 5.27, 97 KB from 10.83 to 6.39, and tsconfig's 469 KB from 13.86 to 11.99; importing is unchanged, and so is the cost of a check once the tree is built (432 against 447 ns, 342 against 336, 650 against 653). A server that validates thousands of documents pays at most the walk's extra cost over its first 1000 calls, which on tsconfig is 0.85 ms, once. Two things the plain walk did differently from the tree came out once it answered real calls, and both are fixed: it checked custom keywords only on leaf nodes, so a keyword beside properties was skipped (the tree answered every call before, and compiled every schema of the official suite, so this was not reachable in a release), and data nested about 700 levels deep overflowed its stack where the tree's did not; such data now goes to the tree. tests/test_interpreter_tiering.js holds the walk to the tree on the suite's 3365 cases.

    1.50.0
  • The interpreted engine recognises leaf plans again. Its index records a base URI for every schema node, and the plan read that as a resource boundary on every node, so no plan was ever a leaf and every value check went through the full evaluation prologue. A plan now carries a base only where a resource starts; references already hand their target's base over. On the engine without its plan compiler (the shared error runtime), a 30-field flat object goes from 939 to 916 ns and a four-field request body from 118 to 113 ns, measured twice each with the same order of results.

    1.50.0
  • A compact module sets nothing up at import: its schema is parsed and the runtime prepared on the first rejected document whose errors are read. Importing three compact modules takes 4.00 ms against 4.67 for the self-contained ones, eight take 5.50 against 7.65. The first error list a page reads costs more instead, once: 4.50 against 2.76 ms for three validators, 6.59 against 4.82 for eight, so import and first error read together are 8.50 against 7.43 ms for three and 12.09 against 12.47 for eight; after that a rejection costs the same (about 0.09 ms on these schemas). What is left of the difference is the runtime's own code being compiled on its first use, about 1.1 ms. In a browser the runtime does that when the page is idle (requestIdleCallback, once, on a small schema of its own), so the first real error list finds it compiled: with an idle moment between load and the first rejection, a one-property schema's first error read takes 0.61 ms against 0.74 for a self-contained module, and three SchemaStore schemas after it 2.86 against 2.67. In Node it does the same once the event loop is first free after start-up, on a timer that keeps no process alive, so a server has it done before its first request (0.60 against 0.75 ms, and 2.85 against 2.65 for the three schemas after it); a rejection in the same synchronous step as the import, before the loop turns, still pays it. Workers allow no timer at start-up, and there nothing runs ahead. On data nested deeper than its walk can go (thousands of levels), the runtime reports the module's ATA9000 stub instead of throwing; the verdict still comes from the module. Two things keep that first read down: the runtime sorts errors by walking only the paths they name, where the runtime Validator numbers every node of the schema on its first sort, and a build tells the runtime when a schema has no embedded resource or anchor, so it does not index the schema (1.29 to 0.20 ms on the first schema measured).

    1.50.0
  • The interpreted engine builds error paths only while it collects errors. A verdict walk concatenated an instance path and a schema path for every child it visited and never read them: on the 22 KB assessment file above, 48.1 to 40.7 µs.

    1.50.0
  • A compact module reports the errors the runtime Validator reports with richErrors: false (and, like the self-contained module, without errorMessage), in its order: the order of the keywords in the schema. A self-contained module reports them in the order its collector meets them, which differs (a parent's additionalProperties before the child properties' errors, for instance). Neither order was documented. Checked over the official suites in five dialects and 13741 SchemaStore documents: no verdict differs from the self-contained module, and every error list equals the runtime's. On that corpus the self-contained module differed from the runtime in 23 error lists (the errors inside a oneOf branch) and left allOf: [false] without its ATA4005 code; on 73 documents it could only report ATA9000, where the compact module reports the errors.

    1.50.0