Repository navigation
feat(validation)!: ship precompiled JSON Schema validators - #78
Merged
Merged
Conversation
The JSON Schema validation providers validate with functions that Ajv's standalone code generator writes at build time, instead of compiling the embedded schemas with Ajv at runtime. The package no longer installs ajv, ajv-errors, ajv-formats or their dependencies, among them require-from-string, which a dependency policy scan reports as end of life. Validation compiles no schema and evaluates no generated code at runtime. scripts/generate-json-schema-validators.mjs, run with npm run generate:validators, compiles the 15 embedded schemas with the options the runtime used and writes json-schema/validators, which is committed. A new CI job regenerates it and fails on any difference. The generated code imports Ajv's equal and ucs2length helpers from copies in json-schema/runtime. ajv, ajv-errors, ajv-formats and fast-deep-equal become exact-pinned devDependencies, and NOTICE and the browser bundle's notices credit Ajv and fast-deep-equal; the retired-identifier allowlist follows the ApiDOM attribution to its new NOTICE lines. Babel compiles the generated code compactly, which keeps the published modules near the size of the generated sources. Diagnostics are unchanged. Every recorded baseline passes as before, and a new test runs every fixture through each generated validator and through Ajv at runtime, expecting the same verdict and the same errors. BREAKING CHANGE: the JSON Schema validation providers no longer accept a schema of their own. The OpenAPI 2.0, 3.0 and 3.1, Arazzo 1.0 and 1.1 and Overlay providers take no constructor arguments, and JsonSchemaValidationProvider takes a validation function instead of a schema and no longer exposes its ajv, ajv2020 and jsonSchema fields. To validate against a schema of your own, compile it with your own Ajv and pass the function to a subclass of JsonSchemaValidationProvider, as the README's "Migrating to 3.0" section shows.
There was a problem hiding this comment.
🔵 Needs a closer look
The breaking provider API and extensive generated-code replacement warrant human confirmation of diagnostic parity and published-package behavior.
0 open findings
What changed in this PR
Replaces runtime Ajv compilation with precompiled JSON Schema validators, removing Ajv from the published dependency tree as a breaking 3.0 change.
Changes:
- Switches providers to generated validation functions and documents custom-schema migration.
- Adds deterministic generation, CI drift checks, and validator parity tests.
- Updates dependency classification, bundled helpers, and attribution.
| File | Description |
|---|---|
| scripts/retired-identifiers-allow.txt | Updates NOTICE attribution locations. |
| packages/api-languageservice/test/validate.ts | Migrates custom-schema provider testing. |
| packages/api-languageservice/test/test-utils.ts | Shares fixture parsing. |
| packages/api-languageservice/test/overlay-yaml.ts | Removes obsolete custom-schema test. |
| packages/api-languageservice/test/json-schema-validators.ts | Checks generated/runtime validator parity. |
| packages/api-languageservice/test/fixtures/json-schema-validators/asyncapi-2-3-valid.yaml | Adds valid AsyncAPI 2.3 coverage. |
| packages/api-languageservice/test/fixtures/json-schema-validators/arazzo-1-1-valid.yaml | Adds valid Arazzo 1.1 coverage. |
| packages/api-languageservice/test/better-errors.ts | Uses generated validators for formatter tests. |
| packages/api-languageservice/src/services/validation/providers/overlay-12-json-schema-validation-provider.ts | Uses precompiled Overlay 1.2 validation. |
| packages/api-languageservice/src/services/validation/providers/overlay-11-json-schema-validation-provider.ts | Uses precompiled Overlay 1.1 validation. |
| packages/api-languageservice/src/services/validation/providers/overlay-10-json-schema-validation-provider.ts | Uses precompiled Overlay 1.0 validation. |
| packages/api-languageservice/src/services/validation/providers/overlay-1-json-schema-validation-provider.ts | Selects version-specific precompiled delegates. |
| packages/api-languageservice/src/services/validation/providers/openapi-31-json-schema-validation-provider.ts | Uses precompiled OpenAPI 3.1 validation. |
| packages/api-languageservice/src/services/validation/providers/openapi-30-json-schema-validation-provider.ts | Uses precompiled OpenAPI 3.0 validation. |
| packages/api-languageservice/src/services/validation/providers/openapi-20-json-schema-validation-provider.ts | Uses precompiled OpenAPI 2.0 validation. |
| packages/api-languageservice/src/services/validation/providers/json-schema-validation-provider.ts | Accepts validation functions instead of schemas. |
| packages/api-languageservice/src/services/validation/providers/better-errors/index.ts | Marks the error interface public. |
| packages/api-languageservice/src/services/validation/providers/asyncapi-26-json-schema-validation-provider.ts | Uses precompiled AsyncAPI 2.6 validation. |
| packages/api-languageservice/src/services/validation/providers/asyncapi-25-json-schema-validation-provider.ts | Uses precompiled AsyncAPI 2.5 validation. |
| packages/api-languageservice/src/services/validation/providers/asyncapi-24-json-schema-validation-provider.ts | Uses precompiled AsyncAPI 2.4 validation. |
| packages/api-languageservice/src/services/validation/providers/asyncapi-23-json-schema-validation-provider.ts | Uses precompiled AsyncAPI 2.3 validation. |
| packages/api-languageservice/src/services/validation/providers/asyncapi-22-json-schema-validation-provider.ts | Uses precompiled AsyncAPI 2.2 validation. |
| packages/api-languageservice/src/services/validation/providers/asyncapi-21-json-schema-validation-provider.ts | Uses precompiled AsyncAPI 2.1 validation. |
| packages/api-languageservice/src/services/validation/providers/asyncapi-20-json-schema-validation-provider.ts | Uses precompiled AsyncAPI 2.0 validation. |
| packages/api-languageservice/src/services/validation/providers/arazzo-11-json-schema-validation-provider.ts | Uses precompiled Arazzo 1.1 validation. |
| packages/api-languageservice/src/services/validation/providers/arazzo-1-json-schema-validation-provider.ts | Uses precompiled Arazzo 1.0 validation. |
| packages/api-languageservice/src/services/validation/providers/ajv-utils.ts | Removes runtime Ajv compilation. |
| packages/api-languageservice/src/services/validation/json-schema/validators/overlay-1-2.ts | Adds generated Overlay 1.2 validator. |
| packages/api-languageservice/src/services/validation/json-schema/validators/overlay-1-1.ts | Adds generated Overlay 1.1 validator. |
| packages/api-languageservice/src/services/validation/json-schema/validators/overlay-1-0.ts | Adds generated Overlay 1.0 validator. |
| packages/api-languageservice/src/services/validation/json-schema/runtime/ucs2length.ts | Includes attributed string-length helper. |
| packages/api-languageservice/src/services/validation/json-schema/runtime/equal.ts | Includes attributed deep-equality helper. |
| packages/api-languageservice/src/index.ts | Exports validation function and error types. |
| packages/api-languageservice/scripts/generate-json-schema-validators.mjs | Generates standalone validators with local helpers. |
| packages/api-languageservice/scripts/generate-json-schema-validators.d.mts | Types generator exports for tests. |
| packages/api-languageservice/README.md | Documents migration to 3.0. |
| packages/api-languageservice/package.json | Adds generation command and development-only dependencies. |
| packages/api-languageservice/config/webpack/browser.config.js | Preserves bundled-code attribution. |
| packages/api-languageservice/.babelrc | Compacts generated validator output. |
| package-lock.json | Records development-only dependency classification. |
| NOTICE | Credits copied helpers and generated code. |
| eslint.config.js | Excludes generated validators and removes obsolete exception. |
| CLAUDE.md | Documents regeneration and helper maintenance. |
| .github/workflows/build.yml | Adds generated-validator drift check. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The removed runtime compiler set `unicodeRegExp: false` on every Ajv instance, so a schema of one's own was compiled without Unicode mode, as the generated validators still are. The README's migration example left the option out. With its options a pattern holding an identity escape such as `\_` no longer compiles, and `.` matches a whole emoji where it matched half of one, so documents the 2.x providers reported pass. The example and its copy in `test/validate.ts` set the option now, and the README says why it matters. A new case validates a document against a schema of one's own with both kinds of pattern, in both formatting modes, and expects the diagnostics the 2.x compiler gives; it fails in Unicode mode on either pattern. Compiled with the README's options, every embedded 2020-12 schema reaches the same verdicts and errors on all fixtures as its generated validator, apart from `schemaPath` values that no diagnostic shows.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the fifth step of the plan to remove Ajv and the components flagged by the dependency policy scan from the published dependency tree, and the one that removes Ajv itself. The package no longer installs
ajv,ajv-errors,ajv-formatsor their dependencies:require-from-string@2.0.2, which the scan reports as a critical end-of-life component, andfast-deep-equal,fast-uriandjson-schema-traverse. It is a breaking change and releases as 3.0.0.The change
The JSON Schema validation providers validate with functions that Ajv's standalone code generator writes ahead of time, instead of compiling the embedded schemas with Ajv at runtime.
scripts/generate-json-schema-validators.mjs(npm run generate:validators) gives each of the 15 embedded schemas its own Ajv instance with the optionsajv-utils.tsused, including the instance without the built-in draft-07 meta-schema that the AsyncAPI 2.3 to 2.6 schemas need since #73, and writessrc/services/validation/json-schema/validators/<name>.ts. The output is committed and deterministic; the files are@ts-nocheckand ignored by ESLint, and their default export is typed asJsonSchemaValidateFunction. A newcheck-generated-validatorsCI job runs the generator and fails when a committed file differs or is missing, so a schema change or anajv,ajv-errorsorajv-formatsbump has to regenerate them in the same PR;CLAUDE.mdrecords this.The generated code needs two of Ajv's runtime helpers,
equal(a re-export offast-deep-equal) anducs2length. They are copied intojson-schema/runtime/with their licence headers, the generator rewrites Ajv'srequirecalls into imports of the copies, and it fails if the code references anything else of Ajv.ajv,ajv-errors,ajv-formatsandfast-deep-equalbecome exact-pinned devDependencies, which also keeps the generation reproducible. NOTICE credits Ajv and fast-deep-equal for the copied helpers, the browser bundle'sTHIRD-PARTY-NOTICES.txtkeeps listing both throughadditionalModules, and the rule inscripts/retired-identifiers-allow.txtfollows the ApiDOM attribution to its new NOTICE lines.ajv-utils.tsis deleted andoptionalDependenciesis gone.Babel compiles the generated directory with
compact: true. Its default output reprints every inlined schema constant expanded and indented, which made the published.mjsand.cjs40 % larger than the generated sources. The committed.tsfiles stay readable, because the generator keeps Ajv's line breaks.Breaking change
The providers no longer accept a schema of their own.
JsonSchemaValidationProvidertakes a validation function instead of a schema and no longer exposes itsajv,ajv2020andjsonSchemafields; the function is typed with the structuralJsonSchemaValidateFunction, which Ajv's compiled functions satisfy, and which is exported from the package entry point together withAjvErrorObject. The OpenAPI 2.0, 3.0 and 3.1, Arazzo 1.0 and 1.1 and Overlay providers take no constructor arguments, the deprecatedOverlay1JsonSchemaValidationProviderincluded, which picks among the three precompiled Overlay validators by document version. Nothing reads the schema any more since the formatter port in #77 dropped its unused schema argument, so the providers no longer import the schema JSON, which Babel used to inline into every provider module.A "Migrating to 3.0" section in the README shows the replacement: compile your schema with your own Ajv and pass the function to a subclass of
JsonSchemaValidationProvider, or, when a modified schema only added organisation-specific checks, write a linting rule instead. The modified-schema case intest/validate.tsis rewritten as that subclass with its expected diagnostics unchanged, and the Overlay 1 custom-schema test inoverlay-yaml.tsgoes with the feature. The first commit carries aBREAKING CHANGE:footer, so lerna releases 3.0.0.The example's Ajv options are the ones the providers compiled a schema of your own with before 3.0. The second commit, after review, adds the one the first left out,
unicodeRegExp: false: without it Ajv compiles patterns in Unicode mode, so a pattern with an identity escape such as\_no longer compiles, and.matches a whole emoji where it matched half of one, which let documents the 2.x providers reported pass. The README now says why the option matters, and a new case intest/validate.tsvalidates a document against a schema of one's own holding both kinds of pattern, in both formatting modes, and expects the diagnostics the removed 2.x compiler gives for it; it fails in Unicode mode on either pattern. Compiled with the README's options, every embedded 2020-12 schema reaches the same verdicts and errors as its generated validator on all 596 fixture documents, apart fromschemaPathvalues that no diagnostic shows.Diagnostics
Diagnostics are unchanged. All recorded baselines pass as before, and so does the formatter parity test from #77. A new test,
test/json-schema-validators.ts, runs every fixture document through each generated validator and through the same schema compiled by Ajv at runtime, and expects the same verdict and the same errors. It checks each document against every schema, not only its own version's, and requires valid and invalid documents for every schema; two valid fixtures were added for AsyncAPI 2.3 and Arazzo 1.1, which no fixture covered. Beforeajv-utils.tswas deleted, a one-off comparison of the old runtime path with the generated validators over 594 documents and the 15 schemas (8,910 comparisons, 348 valid and 8,562 invalid) found no difference in verdict or errors.Validation no longer compiles schemas or evaluates generated code at runtime. Loading Ajv and compiling one embedded schema with it, which the first validation of each version used to cost, takes 91 to 146 ms in a fresh process (OpenAPI 2.0, 3.0 and 3.1, AsyncAPI 2.0 and 2.6, Arazzo 1.1 and Overlay 1.2, median of seven runs); loading the precompiled validator takes 2 to 6 ms. The browser bundle still contains one
new Function("return this"), a global-object fallback in a dependency, and oneeval(, so this PR makes no claim about strict content security policies.Sizes
The generated code ships in both module formats, so the package grows while the install tree shrinks. Measured against the #77 code:
Testing
npm testpasses with 570 tests. Lint (no new warnings), type-check,npm run build(declarations, ESM, CommonJS and the browser bundle), the generator drift check,./scripts/check-retired-identifiers.shand./scripts/check-package-contract.shpass. In a scratch project that installs the packed package, nothing from the Ajv family remains (ajv,ajv-errors,ajv-formats,require-from-string,fast-deep-equal,fast-uri,json-schema-traverse), nor any package the earlier steps removed. The README's migration example, copied into a project of its own withajvinstalled, type-checks and reports its schema's errors through the language service, the two patterns above included.This PR touches the same
devDependenciesblock and lockfile as #76, so whichever of the two lands second needs a small rebase.