@subhamxcod/apiguard 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,48 +1,156 @@
1
1
  # API Guard
2
2
 
3
- > Detect breaking changes between OpenAPI specifications (3.0 & 3.1) so API contracts never
4
- > silently break downstream consumers.
3
+ > Production-grade OpenAPI breaking-change detection for CI/CD.
5
4
 
6
5
  [![Test](https://github.com/subhamxaura/apiguard/actions/workflows/test.yml/badge.svg)](./.github/workflows/test.yml)
7
- [![Lint](https://github.com/subhamxaura/apiguard/actions/workflows/lint.yml/badge.svg)](./.github/workflows/lint.yml)
6
+ [![Release](https://github.com/subhamxaura/apiguard/actions/workflows/release.yml/badge.svg)](./.github/workflows/release.yml)
7
+ [![npm version](https://img.shields.io/npm/v/@subhamxcod/apiguard.svg)](https://www.npmjs.com/package/@subhamxcod/apiguard)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
8
9
 
9
- **API Guard** diffs two OpenAPI specs — a _baseline_ and a _current_ — and classifies every
10
- difference as **breaking** (an existing conformant client will fail), **non-breaking but
10
+ **API Guard** diffs two OpenAPI specifications — a _baseline_ and a _current_ — and classifies
11
+ every difference as **breaking** (an existing conformant client will fail), **non-breaking but
11
12
  notable**, or **informational**. It ships as a CLI, a TypeScript library, and a GitHub Action.
12
13
 
14
+ ## Why API Guard?
15
+
16
+ APIs evolve, and small spec changes can silently break the consumers that depend on them:
17
+ frontend applications, generated SDKs, partner integrations, and downstream services. A removed
18
+ response field or a changed property type doesn't fail your build — it fails your users, in
19
+ production, at runtime.
20
+
21
+ API Guard moves that failure left. It compares your OpenAPI documents **semantically** — not
22
+ textually — and reports every change that would break an existing client, before the code ships:
23
+
24
+ ```
25
+ old spec + new spec → API Guard → breaking changes (or a clean pass)
26
+ ```
27
+
13
28
  ## Features
14
29
 
15
30
  - **81 detection rules** across operations, parameters, request/response bodies, headers,
16
31
  schemas, security, callbacks, and webhooks — see the [generated rule reference](./docs/rules.md)
17
- - **Semantic, not textual**: YAML formatting, key order, enum order, and `$ref` aliasing are
18
- never false positives; `nullable: true` (3.0) equals `type: ["string","null"]` (3.1)
19
- - **Deterministic**: same inputs → byte-identical output, on any machine, any locale
20
- - **Configurable**: per-rule severities, per-context overrides (`ruleId@context`), path/
21
- operation/schema/rule ignore lists in `apiguard.yaml`
22
- - **Semver advice**: suggests major/minor/patch from the detected change kinds
23
- - **Three output formats**: terminal (color), JSON (machine contract), markdown (PR-friendly)
24
- - **GitHub Action**: merge-base baseline by default, PR summary, `fail-on` policy
32
+ - **OpenAPI 3.0 and 3.1** — unified normalization: `nullable: true` (3.0) equals
33
+ `type: ["string", "null"]` (3.1)
34
+ - **AST source mapping** — pinpoint exact source file line and column numbers for every change
35
+ - **Multiple output formats** — terminal, JSON (machine contract), markdown, interactive standalone HTML dashboard, and OASIS SARIF 2.1.0 (for GitHub Code Scanning)
36
+ - **Git & remote spec support** — diff directly against git branches (`--git-base main`, `git://rev:file`) or remote URLs (`https://...`) with built-in SSRF protection
37
+ - **Client impact personas** — evaluate breaking impact against `--profile strict-sdk` or `--profile loose-web`
38
+ - **Changelog & shim generators** — automated Keep-a-Changelog release notes and backward-compatibility gateway shims (Express, Cloudflare Worker, Envoy Lua)
39
+ - **Semantic, not textual** — YAML formatting, key order, enum order, and `$ref` aliasing are
40
+ never false positives
41
+ - **Deterministic** — same inputs produce byte-identical output, on any machine, any locale
42
+ - **Configurable** — per-rule severities, per-context overrides (`ruleId@context`), and
43
+ path/operation/schema/rule ignore lists in `apiguard.yaml`
44
+ - **Semver advice** — suggests major/minor/patch from the detected change kinds
45
+ - **GitHub Action** — merge-base baseline, PR job summary, idempotent sticky PR comments, and inline diff annotations
46
+ - **TypeScript library** — typed, pure API for embedding in your own tooling
25
47
 
26
48
  ## Quick start
27
49
 
28
50
  ```bash
29
- pnpm add -D @subhamxcod/apiguard
30
- apiguard init # scaffold apiguard.yaml
31
- apiguard diff openapi.v1.yaml openapi.yaml
51
+ npm install --save-dev @subhamxcod/apiguard
52
+ # or: pnpm add -D @subhamxcod/apiguard
53
+ # or: yarn add -D @subhamxcod/apiguard
54
+
55
+ apiguard init . --examples # scaffold apiguard.yaml + two sample specs
56
+ apiguard diff examples/baseline.yaml examples/current.yaml
57
+ ```
58
+
59
+ Or run it directly without installing:
60
+
61
+ ```bash
62
+ npx @subhamxcod/apiguard diff old.yaml new.yaml
32
63
  ```
33
64
 
34
- Example output:
65
+ ## What gets detected
66
+
67
+ Remove a required response property between two releases:
35
68
 
69
+ ```yaml
70
+ # before # after
71
+ responses: responses:
72
+ '200': '200':
73
+ content: content:
74
+ application/json: application/json:
75
+ schema: schema:
76
+ type: object type: object
77
+ required: [id, name, email] required: [id, name]
78
+ properties: properties:
79
+ id: { type: string } id: { type: string }
80
+ name: { type: string } name: { type: string }
81
+ email: { type: string }
36
82
  ```
37
- ERROR /users/{id} removed-method
38
- operation DELETE was removed; existing clients calling it will get 404s
39
- → keep the operation and mark it deprecated instead
40
83
 
41
- 2 breaking, 3 non-breaking, 12 info — suggest major
42
- verdict: FAIL (exit 1)
84
+ Running `apiguard diff old.yaml new.yaml` exits non-zero and prints:
85
+
86
+ ```text
87
+ GET /users/{id}
88
+ ERROR Response property `email` was removed
89
+ Suggestion: stop relying on `email`; it will no longer be returned
90
+ ❌ Breaking API changes found
91
+
92
+ Suggested version bump: MAJOR
43
93
  ```
44
94
 
45
- ## The GitHub Action (recommended)
95
+ The `property-removed` rule fired for the response body; removals are resolved per the §10.5
96
+ context table (request/response/parameter/component), so the same rule can downgrade to a
97
+ warning where the context allows it. The full catalog of 81 rules with severities and trigger
98
+ conditions lives in the [rule reference](./docs/rules.md).
99
+
100
+ ## CLI
101
+
102
+ ### `apiguard diff <old-spec> <new-spec>`
103
+
104
+ Diff two OpenAPI specs and report breaking changes. This is the core command.
105
+
106
+ | Flag | Effect |
107
+ | ------------------------ | -------------------------------------------------------- |
108
+ | `--json`, `--markdown` | Output format shorthands |
109
+ | `--html`, `--sarif` | Interactive standalone HTML dashboard or SARIF 2.1.0 |
110
+ | `--open` | Open interactive HTML report in browser automatically |
111
+ | `--git-base <ref>` | Diff against baseline from git revision or branch |
112
+ | `--profile <p>` | `default`, `strict-sdk`, or `loose-web` |
113
+ | `--fail-on <level>` | `error` (default) \| `warning` \| `never` |
114
+ | `--strict` | Shorthand for `--fail-on warning --include-non-breaking` |
115
+ | `--include-non-breaking` | Also render warning + info changes |
116
+ | `--show-ignored` | Show changes suppressed by ignore rules, tagged |
117
+ | `--suggest-version` | Print the suggested semver bump after the report |
118
+ | `--config <path>` | Explicit config file (missing file: exit 2) |
119
+ | `--output <path>` | Also write the full report to a file |
120
+ | `--quiet` | Print only the one-line verdict |
121
+ | `--no-color` | Disable ANSI color (auto-detected for non-TTY) |
122
+ | `--verbose` | Log parse/config steps to stderr |
123
+
124
+ **Exit codes:** `0` pass · `1` changes met the fail-on policy · `2` usage/config error ·
125
+ `3` invalid spec · `4` internal error (please report).
126
+
127
+ Machine contract: `--json` emits a versioned report (`schemaVersion: "1.0"`) with
128
+ `summary.bySeverity`, per-change `ruleId`/`severity`/`kind`, and stable change ids.
129
+
130
+ ### `apiguard changelog <old-spec> <new-spec>`
131
+
132
+ Generate Keep-a-Changelog formatted release notes grouped by breaking changes, additions, and updates.
133
+
134
+ ### `apiguard codegen-shim <old-spec> <new-spec>`
135
+
136
+ Synthesize backward-compatibility gateway middleware (`express`, `cloudflare-worker`, `envoy-lua`) for breaking changes.
137
+
138
+ ### `apiguard validate <spec>`
139
+
140
+ Load and semantically validate one spec. `--strict` exits 1 when semantic warnings exist.
141
+
142
+ ### `apiguard init [dir]`
143
+
144
+ Scaffold an `apiguard.yaml` config template. `--examples` also writes two sample specs for a
145
+ first diff run; `--force` overwrites existing files.
146
+
147
+ Full reference: [CLI guide](./docs/cli.md).
148
+
149
+ ## GitHub Action
150
+
151
+ The Action wraps the same engine for pull requests: it resolves the baseline automatically from
152
+ the merge-base with your default branch, posts the report as a job summary, and fails the step
153
+ when breaking changes exist.
46
154
 
47
155
  ```yaml
48
156
  # .github/workflows/api-guard.yml
@@ -54,33 +162,18 @@ jobs:
54
162
  steps:
55
163
  - uses: actions/checkout@v4
56
164
  with: { fetch-depth: 0 } # merge-base needs history
57
- - uses: apiguard/action@v1
165
+ - uses: subhamxaura/apiguard@v1 # tracks the latest v1.x release
58
166
  with:
59
- current: openapi.yaml # default; the PR's spec
167
+ current: openapi.yaml # the PR's spec
60
168
  # baseline: omitted → the spec at the merge-base with origin/main
61
169
  ```
62
170
 
63
- The Action posts a job summary table, emits `breaking` / `warnings` / `verdict` /
64
- `suggested-version` outputs, and fails the step when breaking changes exist
65
- (`fail-on: never` to only report). Full input/output reference:
171
+ The Action emits `breaking`, `warnings`, `verdict`, and `suggested-version` outputs, and writes
172
+ a deterministic job summary. To report without blocking (e.g. during a migration), set
173
+ `fail-on: never` and branch on the outputs. Full input/output reference:
66
174
  [GitHub Action guide](./docs/github-action.md).
67
175
 
68
- ## CLI
69
-
70
- ```
71
- apiguard diff <old-spec> <new-spec> # the core command
72
- apiguard validate <spec> # load + semantic validation
73
- apiguard init [dir] # scaffold a config
74
- ```
75
-
76
- Useful `diff` flags: `--json`, `--markdown`, `--fail-on warning|never`, `--strict`,
77
- `--include-non-breaking`, `--show-ignored`, `--suggest-version`, `--output <file>`,
78
- `--quiet`, `--no-color`. Full flag semantics: [CLI guide](./docs/cli.md).
79
-
80
- **Exit codes:** `0` pass · `1` changes met the fail-on policy · `2` usage/config error ·
81
- `3` invalid spec · `4` internal error (please report).
82
-
83
- ## Library
176
+ ## TypeScript library
84
177
 
85
178
  ```ts
86
179
  import { analyze } from '@subhamxcod/apiguard';
@@ -91,11 +184,15 @@ if (report.summary.bySeverity.error > 0) {
91
184
  }
92
185
  ```
93
186
 
94
- The engine is pure: no stdout, no process.exit, no timestamps in report bodies.
95
- [Library guide](./docs/library.md).
187
+ The library also exports `suggestVersion` for standalone semver advice and `validateSpec` for
188
+ load + semantic validation. The engine is pure: no stdout, no `process.exit`, no timestamps in
189
+ report bodies. [Library guide](./docs/library.md).
96
190
 
97
191
  ## Configuration
98
192
 
193
+ API Guard reads `apiguard.yaml` (discovered from the working directory, or passed with
194
+ `--config` / the `APIGUARD_CONFIG` environment variable):
195
+
99
196
  ```yaml
100
197
  # apiguard.yaml
101
198
  schemaVersion: 1
@@ -111,31 +208,80 @@ ignore:
111
208
 
112
209
  [Configuration guide](./docs/configuration.md) · [Ignoring changes](./docs/ignoring.md)
113
210
 
114
- ## How it works
211
+ ## OpenAPI support
212
+
213
+ | Version | Status |
214
+ | ------- | --------------- |
215
+ | 3.0.x | ✅ Full support |
216
+ | 3.1.x | ✅ Full support |
115
217
 
116
- 1. **Load** — `@apidevtools/swagger-parser` behind a swappable `SpecLoader`; remote `$ref`s
117
- denied by default
118
- 2. **Normalize** — 3.0/3.1 unified: type arrays, nullable, exclusive bounds (§11.2)
119
- 3. **Index** — paths/methods/parameters/components, sorted
120
- 4. **Diff** — ref-aware pairwise schema comparison with cycle protection (10k-deep chains OK)
121
- 5. **Classify** — rules map facts → severities with per-context doctrine
122
- 6. **Report** — dedupe by stable id, deterministic sort, render
218
+ Documents are normalized into one internal model before diffing, so 3.0 and 3.1 idioms compare
219
+ correctly: type arrays vs `nullable`, exclusive bounds, and `$ref` aliasing. Remote `$ref`
220
+ fetching is **denied by default** (pass an explicit loader option to allow it) so untrusted
221
+ specs cannot trigger network access. Swagger 2.0 documents are not supported.
222
+
223
+ ## CI/CD
224
+
225
+ The intended workflow — run API Guard on every pull request that touches your API:
226
+
227
+ ```text
228
+ OpenAPI change → API Guard (merge-base diff) → breaking changes?
229
+ ├─ none → CI passes, merge with confidence
230
+ └─ breaking → step fails, changes are blocked before release
231
+ ```
232
+
233
+ 1. Add the [GitHub Action](#github-action) to your PR workflow, **or** run the CLI in any CI:
234
+ `apiguard diff "$BASELINE" "$CURRENT" --fail-on error`
235
+ 2. If you ship an SDK, gate version bumps on `--suggest-version` output
236
+ 3. Encode your team's policy in `apiguard.yaml` (downgrades, ignores) and commit it
123
237
 
124
238
  ## Docs
125
239
 
126
240
  - [Getting started](./docs/getting-started.md)
127
- - [Configuration](./docs/configuration.md)
128
241
  - [CLI reference](./docs/cli.md)
129
- - [GitHub Action](./docs/github-action.md)
242
+ - [Configuration](./docs/configuration.md)
130
243
  - [Ignoring changes](./docs/ignoring.md)
131
- - [Rule reference (generated)](./docs/rules.md)
244
+ - [Rule reference (81 rules, generated)](./docs/rules.md)
245
+ - [GitHub Action](./docs/github-action.md)
246
+ - [Library API](./docs/library.md)
247
+ - [Changelog](./CHANGELOG.md)
248
+
249
+ ## Development
250
+
251
+ Requires Node ≥ 20. pnpm is pinned via the `packageManager` field (use corepack).
252
+
253
+ ```bash
254
+ pnpm install # install dependencies
255
+ pnpm test # unit tests (vitest)
256
+ pnpm test:e2e # CLI + Action bundle e2e tests
257
+ pnpm coverage # coverage with 90/90/90 lines, 85 branches thresholds
258
+ pnpm lint # eslint
259
+ pnpm format:check # prettier
260
+ pnpm typecheck # tsc --noEmit
261
+ pnpm build # tsup (ESM + CJS + dts)
262
+ pnpm build:action # esbuild bundle for the GitHub Action
263
+ pnpm docs:rules # regenerate docs/rules.md from the rule registry
264
+ ```
265
+
266
+ Rule parity is enforced: if you add a rule and forget `pnpm docs:rules`, CI fails.
132
267
 
133
268
  ## Contributing
134
269
 
135
- See [CONTRIBUTING.md](./CONTRIBUTING.md). Milestone-by-milestone build decisions live in
136
- [DECISIONS.md](./DECISIONS.md). Security issues: [SECURITY.md](./SECURITY.md) — do not open
137
- public issues.
270
+ Contributions are welcome — see [CONTRIBUTING.md](./CONTRIBUTING.md) for the workflow, the
271
+ milestone gate (`lint && typecheck && test`), and how to add a rule. Design decisions and their
272
+ reasons live in [DECISIONS.md](./DECISIONS.md). Please note the
273
+ [Code of Conduct](./CODE_OF_CONDUCT.md).
274
+
275
+ ## Security
276
+
277
+ API Guard processes untrusted spec files; its threat model is documented in
278
+ [SECURITY.md](./SECURITY.md). Please report vulnerabilities through GitHub's private
279
+ vulnerability reporting — do not open public issues.
138
280
 
139
281
  ## License
140
282
 
141
- [MIT](./LICENSE)
283
+ [MIT](./LICENSE) © API Guard contributors
284
+
285
+ ---
286
+
287
+ **Catch breaking OpenAPI changes before they reach production.**