diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 255e74b..34bfca4 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -13,4 +13,15 @@ updates:
directory: "/"
schedule:
interval: "weekly"
- cooldown-period: 15
+ cooldown:
+ default-days: 15
+ groups:
+ dev-dependencies:
+ dependency-type: "development"
+ update-types:
+ - "minor"
+ - "patch"
+ prod-dependencies:
+ dependency-type: "production"
+ update-types:
+ - "patch"
diff --git a/.github/package.json b/.github/package.json
index ea1bd2f..1b05f5b 100644
--- a/.github/package.json
+++ b/.github/package.json
@@ -1,6 +1,7 @@
{
"name": "@willfarrell/ajv-cmd-github-workflows",
"version": "0.0.1",
+ "description": "Workspace for CI-only dev tools (e.g. lockfile-lint) that should not be installed by users or bloat the top-level dependency graph.",
"private": true,
"engines": {
"node": ">=24.0"
diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml
index d1805b5..12bd2da 100644
--- a/.github/workflows/ossf-scorecard.yml
+++ b/.github/workflows/ossf-scorecard.yml
@@ -18,7 +18,7 @@ jobs:
id-token: write
steps:
- name: 'Checkout code'
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: 'Run analysis'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f4f5326..04fa033 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -16,7 +16,7 @@ permissions:
jobs:
build:
name: Build
- if: ${{ github.event.pull_request.merged }}
+ if: ${{ github.event.pull_request.merged && github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
permissions:
contents: read
@@ -25,6 +25,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
diff --git a/.github/workflows/test-dast.yml b/.github/workflows/test-dast.yml
index b770c56..8fdc97b 100644
--- a/.github/workflows/test-dast.yml
+++ b/.github/workflows/test-dast.yml
@@ -17,6 +17,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
diff --git a/.github/workflows/test-lint.yml b/.github/workflows/test-lint.yml
index 000a357..8fce92b 100644
--- a/.github/workflows/test-lint.yml
+++ b/.github/workflows/test-lint.yml
@@ -16,9 +16,11 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js ${{ env.NODE_VERSION }}
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+ uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: https://registry.npmjs.org
@@ -27,4 +29,4 @@ jobs:
npm ci --ignore-scripts
- name: Linting
run: |
- ./node_modules/.bin/biome ci --no-errors-on-unmatched
+ npm run test:lint
diff --git a/.github/workflows/test-perf.yml b/.github/workflows/test-perf.yml
index 2ae31e6..1f54db8 100644
--- a/.github/workflows/test-perf.yml
+++ b/.github/workflows/test-perf.yml
@@ -17,6 +17,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
diff --git a/.github/workflows/test-sast.yml b/.github/workflows/test-sast.yml
index 7a74cdc..df285f7 100644
--- a/.github/workflows/test-sast.yml
+++ b/.github/workflows/test-sast.yml
@@ -20,6 +20,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Trivy
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
@@ -38,6 +40,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
@@ -57,6 +61,28 @@ jobs:
exit-code: 0
format: table
+ license:
+ name: "License headers"
+ runs-on: ubuntu-latest
+ if: (github.actor != 'dependabot[bot]')
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - name: Setup Node.js ${{ env.NODE_VERSION }}
+ uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
+ with:
+ node-version: ${{ env.NODE_VERSION }}
+ registry-url: https://registry.npmjs.org
+ cache: npm
+ - name: Install dependencies
+ run: |
+ npm ci --ignore-scripts
+ - name: License check
+ run: |
+ npm run test:sast:license
+
lockfile:
name: "lockfile-lint: SAST package-lock.json"
runs-on: ubuntu-latest
@@ -64,6 +90,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
@@ -91,6 +119,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@babb554ede22fd5605947329c4d04d8e7a0b8155 # v2.27.7
with:
@@ -112,5 +142,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: semgrep
run: semgrep ci
diff --git a/.github/workflows/test-types.yml b/.github/workflows/test-types.yml
index 192df75..02d66e8 100644
--- a/.github/workflows/test-types.yml
+++ b/.github/workflows/test-types.yml
@@ -17,6 +17,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml
index dd1c647..9dac669 100644
--- a/.github/workflows/test-unit.yml
+++ b/.github/workflows/test-unit.yml
@@ -17,6 +17,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
@@ -32,3 +34,6 @@ jobs:
- name: Unit tests
run: |
npm run test:unit
+ - name: E2E tests
+ run: |
+ npm run test:e2e
diff --git a/license.json b/.license.config.json
similarity index 87%
rename from license.json
rename to .license.config.json
index 21ff47e..1e1220b 100644
--- a/license.json
+++ b/.license.config.json
@@ -1,5 +1,5 @@
{
- "license": "license.template",
+ "license": ".license.template",
"licenseFormats": {
"js|ts": {
"eachLine": {
@@ -14,7 +14,7 @@
"fixtures/*",
"commitlint.config.cjs",
"LICENSE",
- "license.template",
+ ".license.template",
"**/.gitignore",
"**/*.fuzz.js",
"**/*.perf.js",
diff --git a/license.template b/.license.template
similarity index 100%
rename from license.template
rename to .license.template
diff --git a/LICENSE b/LICENSE
index 660d68c..7bd429e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2022 will Farrell
+Copyright (c) 2026 will Farrell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/__test__/e2e.sh b/__test__/e2e.sh
index ffeba25..821d070 100755
--- a/__test__/e2e.sh
+++ b/__test__/e2e.sh
@@ -1,23 +1,32 @@
#!/usr/bin/env bash
+set -euo pipefail
-function bundle {
- echo "validate ${1}"
- node cli.js validate ${1} --valid \
- --strict true --coerce-types array --all-errors true --use-defaults empty
-
- echo "sast audit ${1}"
- node cli.js sast ${1}
-
- echo "transpile ${1}"
- node cli.js transpile ${1} \
- --strict true --coerce-types array --all-errors true --use-defaults empty \
- -o ${1%.json}.js
-
- #cat ${1/schema/data}
-
- echo "test ${1}"
- node --input-type=module -e "import validate from '${1%.json}.js'; import data from '${1/schema/data}' assert {type:'json'}; const valid = validate(data); console.log(valid, JSON.stringify(validate.errors))"
+bundle() {
+ local schema="$1"
+ local data="${schema/schema/data}"
+ local out="${schema%.json}.js"
+
+ echo "validate ${schema}"
+ node cli.js validate "${schema}" --valid \
+ --strict true --coerce-types array --all-errors --use-defaults empty
+
+ echo "sast ${schema}"
+ node cli.js sast "${schema}"
+
+ echo "transpile ${schema}"
+ node cli.js transpile "${schema}" \
+ --strict true --coerce-types array --all-errors --use-defaults empty \
+ -o "${out}"
+
+ echo "test ${schema}"
+ node --input-type=module -e "
+ import validate from '${out}';
+ import data from '${data}' with { type: 'json' };
+ const valid = validate(data);
+ console.log(valid, JSON.stringify(validate.errors));
+ "
+
+ rm -f "${out}"
}
bundle ./__test__/formats.schema.json
-
diff --git a/__test__/formats.schema.json b/__test__/formats.schema.json
index 311771d..b8474ff 100644
--- a/__test__/formats.schema.json
+++ b/__test__/formats.schema.json
@@ -1,4 +1,6 @@
{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://example.com/schemas/formats",
"type": "object",
"additionalProperties": true,
"properties": {
diff --git a/__test__/large-enum.schema.json b/__test__/large-enum.schema.json
index 18a5fd2..62761bb 100644
--- a/__test__/large-enum.schema.json
+++ b/__test__/large-enum.schema.json
@@ -1,4 +1,6 @@
{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://example.com/schemas/large-enum",
"type": "object",
"properties": {
"status": {
diff --git a/__test__/secure.schema.json b/__test__/secure.schema.json
index 260b004..e59d0c5 100644
--- a/__test__/secure.schema.json
+++ b/__test__/secure.schema.json
@@ -1,4 +1,6 @@
{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://example.com/schemas/secure",
"type": "object",
"properties": {
"name": {
diff --git a/biome.json b/biome.json
index c3e7534..09cd887 100644
--- a/biome.json
+++ b/biome.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://biomejs.dev/schemas/2.4.11/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.4.12/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
diff --git a/cli.js b/cli.js
index 1fbbc02..5b701d9 100755
--- a/cli.js
+++ b/cli.js
@@ -1,9 +1,9 @@
#!/usr/bin/env -S node --disable-warning=DEP0040
// Copyright 2026 will Farrell, and ajv-cmd contributors.
// SPDX-License-Identifier: MIT
-// --disable-warning=DEP0040 [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
-// #!/usr/bin/env -S node --experimental-json-modules --no-warnings --no-deprecation
+// --disable-warning=DEP0040 suppresses: [DEP0040] DeprecationWarning: The `punycode` module is deprecated.
+import { createRequire } from "node:module";
import { Command, Option } from "commander";
import deref from "./commands/deref.js";
import ftl from "./commands/ftl.js";
@@ -11,13 +11,13 @@ import sast from "./commands/sast.js";
import transpile from "./commands/transpile.js";
import validate from "./commands/validate.js";
-//import metadata from './package.json' assert { type: 'json' }
+const { version } = createRequire(import.meta.url)("./package.json");
const program = new Command()
.name("ajv")
- //.version(metadata.version)
+ .version(version)
.description(
- "Transpile JSON-Schema (.json) files to JavaScript (.js or .mjs) using ajv",
+ "Validate, transpile, dereference, and audit JSON-Schema files using AJV",
);
program
@@ -71,8 +71,6 @@ program
program
.command("transpile")
.argument("", "Path to the JSON-Schema file to transpile")
- //.addOption(new Option('--ftl ', 'Path to ftl file')
-
// Docs: https://ajv.js.org/packages/ajv-cli.html
.addOption(
new Option(
@@ -165,6 +163,30 @@ program
"Override the max properties limit (default 1024). Removes maxProperties errors when the property count is within this limit. Values <= 1024 are a no-op.",
),
)
+ .addOption(
+ new Option(
+ "--ignore ",
+ "Suppress errors by `instancePath` or `instancePath:keyword` (exact match).",
+ ),
+ )
+ .addOption(
+ new Option(
+ "--offline",
+ "Skip DNS lookups for remote $ref URLs (disables SSRF resolution).",
+ ).preset(true),
+ )
+ .addOption(
+ new Option(
+ "--dns-timeout-ms ",
+ "Per-hostname DNS lookup timeout in ms for SSRF checks (default 5000).",
+ ),
+ )
+ .addOption(
+ new Option(
+ "--dns-concurrency ",
+ "Max concurrent DNS lookups for SSRF checks (default 10).",
+ ),
+ )
.addOption(
new Option(
"-o, --output