@continuoussecuritytooling/ajv-cli is a CLI wrapper around Ajv JSON Schema validator. It compiles TypeScript sources to dist/ and ships a Docker image (node:24-slim base). Commands: validate, compile, test, migrate, help.
npm run build # tsc → dist/
npm run test-spec # mocha via ts-node (no lint, no coverage)
npm run test-cov # mocha + nyc coverage
npm run test # lint + test-cov
npm run lint # eslint src/**/*.ts test/**/*.js- Entry point:
src/index.ts(CLI arg parsing via minimist) - Commands:
src/commands/{validate,compile,test,migrate,help}.ts - Shared utilities:
src/commands/util.ts,src/commands/options.ts,src/commands/ajv.ts - Tests are integration-only (run compiled
dist/index.jsviaexec()) EXCEPT for new unit tests intest/util.spec.tsandtest/options.spec.ts
import X = require(...)(TypeScript CommonJS compat syntax) is not supported by Node.js v22+ native strip-only mode. All test files useimport X from "..."orimport * as X from "..."instead.String.prototype.substris deprecated — usesubstring(src/commands/util.tsalready fixed).
Required — do not downgrade. Reason: @types/node v24 uses typesVersions to route TypeScript ≤5.6 to ts5.6/index.d.ts, which requires lib: esnext.disposable (added in TypeScript 5.2). On TypeScript 4.x this produces dozens of TS1165/TS2339 errors across @types/node declarations.
Required for Node.js 24 runtime types. Requires TypeScript ≥5.2 (see above).
Gotcha — assert.strictEqual narrowing: @types/node v24 gave assert.strictEqual an assertion signature:
function strictEqual<T>(actual: unknown, expected: T, message?): asserts actual is TThis means TypeScript 5 narrows actual after a strictEqual call. If you call assert.strictEqual(err.keyword, "typeof") where err is typed as DefinedError (an ajv discriminated union that has no "typeof" member), TypeScript narrows err to never. Subsequent property accesses then fail with TS2339.
Fix already applied: assertErrors() in test/validate.spec.ts returns ErrorObject[][] instead of DefinedError[][]. Use ErrorObject (base interface, keyword: string) for tests involving custom keywords; DefinedError only covers built-in ajv keywords.
The project upgraded from a mismatched stack (eslint v6, eslint-plugin-prettier v3, prettier v3) that broke because prettier.resolveConfig.sync was removed in prettier v3. Current working stack:
| Package | Version |
|---|---|
| eslint | ^8.0.0 |
| eslint-plugin-prettier | ^5.0.0 |
| eslint-config-prettier | ^10.0.0 |
| @typescript-eslint/eslint-plugin | ^5.0.0 |
| @typescript-eslint/parser | ^5.0.0 |
| prettier | ^3.8.1 |
Gotcha — @ajv-validator/config: The shared ESLint config (@ajv-validator/config/.eslintrc) still extends "prettier/@typescript-eslint", which was merged into "prettier" in eslint-config-prettier v8. The .eslintrc.js override filters this out at runtime:
extends: [...(tsConfig.extends || []).filter((e) => e !== "prettier/@typescript-eslint"), "prettier"],Do not remove this filter until @ajv-validator/config is updated upstream.
Disabled rules (intentional — the codebase uses any for dynamic CLI/JSON data):
@typescript-eslint/no-unsafe-argument@typescript-eslint/no-unsafe-assignment@typescript-eslint/no-unsafe-call@typescript-eslint/no-unsafe-member-access@typescript-eslint/no-unsafe-return@typescript-eslint/no-var-requires
- Integration tests use
dist/index.js— always runnpm run buildbeforenpm run test-spec. - Unit tests (
test/util.spec.ts,test/options.spec.ts) import source modules directly via ts-node; no build step needed. process.exit()is called on errors inutil.ts(openFile,compile) — only test happy paths in unit tests.- Coverage is healthy: ~97% statements, 100% functions.
ESLint-related packages are grouped in renovate.json5 under packageRules (groupName: "eslint") so they're always upgraded together, preventing the version mismatch that broke lint.
actions/checkoutandactions/setup-nodemust be v4 — v5/v6 do not exist.- Build matrix tests Node.js 22.x and 24.x.
build-resultsjob depends on bothbuildandpackagejobs so Docker failures block the branch.- Docker base image:
node:24.14.0-slim. Thedist/folder must be built beforedocker build(thepackagejob doesnpm run clean && npm run buildfirst).
--errors=code-climate emits a JSON array of CodeClimate issues to stdout (for easy pipe/redirect to a file). Stderr still receives the <file> invalid message.
Each issue shape:
{
"description": "[schema] #/path/to/field must be ...",
"check_name": "json-schema",
"fingerprint": "<sha1 of filepath+instancePath+message>",
"severity": "major",
"location": { "path": "<filepath>", "lines": { "begin": 1 } }
}GitLab CI usage: redirect stdout to gl-code-quality-report.json and declare it as a Code Quality artifact.
Note: The fork at
jirutka/ajv-clialso tracks exact line/column positions by parsing JSON/YAML with position metadata. Our implementation omits this (alwayslines.begin: 1) to avoid heavy dependencies — the GitLab Code Quality widget works without positions.
The project uses js-yaml v3 (^3.14.0). yaml.safeLoad is still the primary API in v3 and is not deprecated there. If js-yaml is ever upgraded to v4, safeLoad must be replaced with load (same arguments, different function name).