Summary
Replace the vendored, unmaintained Gavel.js bundle (packages/dredd/lib/vendor/gavel.js, a ~10.3k-line minified build artifact of gavel@9.1.5) with a small in-house TypeScript validator, then delete the bundle.
This completes the direction already documented in packages/dredd/lib/vendor/README.md ("incrementally replace Gavel with in-house ajv-based validation and eventually drop this bundle") and fits the TS migration tracked in #28.
Why
- Upstream Gavel is unmaintained (last release 2021-12-09) and pulls in abandoned
@apiaryio deps; it is vendored as a frozen, minified artifact that cannot be refactored in place.
- Dredd has already replaced most of Gavel's job: response bodies that carry a JSON Schema (both OpenAPI 3.1 and 3.0, the latter now stamped with the OAS-3.1 dialect in
dredd-transactions/compile/openapi30Schema.js:160) are validated by the in-house ajv path validateBodySchemaWithAjv (lib/TransactionRunner.js:154), not Gavel.
- A native TS module removes the
/** @type {any} */ (gavel) casts at the call site (lib/TransactionRunner.js:954) and collapses today's split validation (ajv for schema'd bodies + Gavel for everything else) into one coherent module.
Current consumption contract (narrow)
Gavel is imported in exactly one place — import gavel from './vendor/gavel' (lib/TransactionRunner.js:7) — and called once:
gavel.validate(expected, real) // -> { valid: boolean, fields: { statusCode?, headers?, body? } }
- Dredd itself reads only
result.valid and result.fields[name].errors[].message (lib/TransactionRunner.js:994-1035).
- The full structured result is forwarded opaquely (JSON-serialized) to the Apiary remote reporter (
lib/reporters/ApiaryReporter.js:411); nothing else in-repo destructures the rich shape.
- The target field shape already exists in-house:
createJSONSchemaValidationResult → { valid, kind, values, errors: [{ message, location: { pointer, property } }] } (lib/TransactionRunner.js:122).
Residual surface to reimplement
With schema'd bodies already on ajv, Gavel is only still invoked (the else branch at lib/TransactionRunner.js:976) for:
- statusCode — exact equality.
- headers — expected ⊆ real, case-insensitive, content-type aware.
- body — only when there is no ajv-dialect schema (API Blueprint inputs, example-only / text bodies, schemaless JSON).
Main risk: behavioral parity on exact strings
Integration/unit tests assert Gavel's exact error messages and field order. A drop-in replacement must reproduce these (or the tests are updated deliberately):
Expected status code '%s', but got '%s'. — test/integration/response-test.js:261
Actual and expected data do not match. — test/integration/response-test.js:300
Value of the ‘content-type’ must be application/json. / No validator found for real data media type 'text/plain' and expected data media type 'application/json'. / Real and expected data does not match. — test/unit/reporters/ApiaryReporter-test.js:51
- Logged field order is asserted exactly as
['headers', 'body', 'statusCode'] — lib/TransactionRunner.js:1027.
Proposed plan (prioritized, incremental — each step ships green)
Acceptance criteria
import gavel from './vendor/gavel' removed; bundle files deleted.
- Full
yarn test (unit + integration + smoke) green with no test weakened solely to pass.
- No new runtime dependency on any
@apiaryio package.
- Apiary reporter payload (
validationResult) shape unchanged for consumers.
Open questions to resolve during P0/P4
- Exact semantics of Gavel's schemaless-JSON body validation (example → generated schema, comparison strictness) — to be read from
gavel@9.1.5 source before reimplementing.
- Whether any non-JSON (text) body comparison paths are exercised by real fixtures, or only by the Apiary unit test.
Relates to #28.
Summary
Replace the vendored, unmaintained Gavel.js bundle (
packages/dredd/lib/vendor/gavel.js, a ~10.3k-line minified build artifact ofgavel@9.1.5) with a small in-house TypeScript validator, then delete the bundle.This completes the direction already documented in
packages/dredd/lib/vendor/README.md("incrementally replace Gavel with in-houseajv-based validation and eventually drop this bundle") and fits the TS migration tracked in #28.Why
@apiaryiodeps; it is vendored as a frozen, minified artifact that cannot be refactored in place.dredd-transactions/compile/openapi30Schema.js:160) are validated by the in-house ajv pathvalidateBodySchemaWithAjv(lib/TransactionRunner.js:154), not Gavel./** @type {any} */ (gavel)casts at the call site (lib/TransactionRunner.js:954) and collapses today's split validation (ajv for schema'd bodies + Gavel for everything else) into one coherent module.Current consumption contract (narrow)
Gavel is imported in exactly one place —
import gavel from './vendor/gavel'(lib/TransactionRunner.js:7) — and called once:result.validandresult.fields[name].errors[].message(lib/TransactionRunner.js:994-1035).lib/reporters/ApiaryReporter.js:411); nothing else in-repo destructures the rich shape.createJSONSchemaValidationResult→{ valid, kind, values, errors: [{ message, location: { pointer, property } }] }(lib/TransactionRunner.js:122).Residual surface to reimplement
With schema'd bodies already on ajv, Gavel is only still invoked (the
elsebranch atlib/TransactionRunner.js:976) for:Main risk: behavioral parity on exact strings
Integration/unit tests assert Gavel's exact error messages and field order. A drop-in replacement must reproduce these (or the tests are updated deliberately):
Expected status code '%s', but got '%s'.—test/integration/response-test.js:261Actual and expected data do not match.—test/integration/response-test.js:300Value of the ‘content-type’ must be application/json./No validator found for real data media type 'text/plain' and expected data media type 'application/json'./Real and expected data does not match.—test/unit/reporters/ApiaryReporter-test.js:51['headers', 'body', 'statusCode']—lib/TransactionRunner.js:1027.Proposed plan (prioritized, incremental — each step ships green)
kind/ field-shape the test suite pins (grep + run suite against a stub). Capture as a parity checklist. No behavior change.lib/validation/(TS) with the public surfacevalidate(expected, real)returning the existing field-result shape; wire it behind the existing call site but keep delegating to Gavel (no-op switch).content-typemessage).gavel@9.1.5body behavior — example→generated-schema and the "no validator found" media-type path).validateTransactionto the in-house module for all paths; unify withvalidateBodySchemaWithAjv.lib/vendor/gavel.js+gavel-license.js, updatelib/vendor/README.md, drop coverage excludes.Acceptance criteria
import gavel from './vendor/gavel'removed; bundle files deleted.yarn test(unit + integration + smoke) green with no test weakened solely to pass.@apiaryiopackage.validationResult) shape unchanged for consumers.Open questions to resolve during P0/P4
gavel@9.1.5source before reimplementing.Relates to #28.