@subhamxcod/apiguard 1.0.4 → 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 +19 -3
- package/dist/{chunk-RQVNHPHC.js → chunk-EJQ5XOY6.js} +1012 -16
- package/dist/cli/main.cjs +1127 -26
- package/dist/cli/main.js +118 -9
- package/dist/index.cjs +171 -168
- package/dist/index.d.cts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +9 -1
- package/package.json +1 -1
package/dist/cli/main.js
CHANGED
|
@@ -9,10 +9,14 @@ import {
|
|
|
9
9
|
discoverAndLoadConfig,
|
|
10
10
|
errorMessage,
|
|
11
11
|
formatIssues,
|
|
12
|
+
generateGatewayShim,
|
|
12
13
|
loadSpec,
|
|
13
14
|
mergeWithFlags,
|
|
15
|
+
renderChangelog,
|
|
16
|
+
renderHtml,
|
|
17
|
+
renderSarif,
|
|
14
18
|
validateSemantics
|
|
15
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-EJQ5XOY6.js";
|
|
16
20
|
|
|
17
21
|
// src/cli/run.ts
|
|
18
22
|
import { Command } from "commander";
|
|
@@ -45,6 +49,7 @@ function renderError(err, verbose = false) {
|
|
|
45
49
|
|
|
46
50
|
// src/cli/diff-command.ts
|
|
47
51
|
import fs from "fs";
|
|
52
|
+
import { exec } from "child_process";
|
|
48
53
|
|
|
49
54
|
// src/reporters/terminal.ts
|
|
50
55
|
var MARKER = { error: "ERROR", warning: "WARN", info: "INFO" };
|
|
@@ -134,14 +139,18 @@ function renderJson(report) {
|
|
|
134
139
|
function resolveFormat(opts) {
|
|
135
140
|
if (opts.json) return "json";
|
|
136
141
|
if (opts.markdown) return "markdown";
|
|
142
|
+
if (opts.html) return "html";
|
|
143
|
+
if (opts.sarif) return "sarif";
|
|
137
144
|
if (opts.format !== void 0) {
|
|
138
|
-
if (opts.format === "terminal" || opts.format === "json" || opts.format === "markdown") {
|
|
145
|
+
if (opts.format === "terminal" || opts.format === "json" || opts.format === "markdown" || opts.format === "html" || opts.format === "sarif") {
|
|
139
146
|
return opts.format;
|
|
140
147
|
}
|
|
141
|
-
throw new CliUsageError(
|
|
148
|
+
throw new CliUsageError(
|
|
149
|
+
`invalid --format "${opts.format}" (expected terminal|json|markdown|html|sarif)`
|
|
150
|
+
);
|
|
142
151
|
}
|
|
143
152
|
const envFormat = process.env.APIGUARD_FORMAT;
|
|
144
|
-
if (envFormat === "json" || envFormat === "markdown" || envFormat === "terminal")
|
|
153
|
+
if (envFormat === "json" || envFormat === "markdown" || envFormat === "terminal" || envFormat === "html" || envFormat === "sarif")
|
|
145
154
|
return envFormat;
|
|
146
155
|
return "terminal";
|
|
147
156
|
}
|
|
@@ -159,6 +168,17 @@ function colorEnabled(opts, configColor) {
|
|
|
159
168
|
return process.stdout.isTTY === true;
|
|
160
169
|
}
|
|
161
170
|
async function runDiff(oldSpec, newSpec, opts, io) {
|
|
171
|
+
if (opts.gitBase) {
|
|
172
|
+
if (!newSpec && oldSpec) {
|
|
173
|
+
newSpec = oldSpec;
|
|
174
|
+
oldSpec = `git:${opts.gitBase}:${newSpec}`;
|
|
175
|
+
} else if (newSpec && !oldSpec) {
|
|
176
|
+
oldSpec = `git:${opts.gitBase}:${newSpec}`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!oldSpec || !newSpec) {
|
|
180
|
+
throw new CliUsageError("missing required spec arguments (or use --git-base <ref>)");
|
|
181
|
+
}
|
|
162
182
|
if (opts.verbose) io.stderr(`verbose: loading baseline ${oldSpec} and current ${newSpec}`);
|
|
163
183
|
const loaded = discoverAndLoadConfig({ explicit: opts.config, env: io.env.APIGUARD_CONFIG });
|
|
164
184
|
for (const w of loaded.warnings) io.stderr(`warning: ${w}`);
|
|
@@ -167,7 +187,8 @@ async function runDiff(oldSpec, newSpec, opts, io) {
|
|
|
167
187
|
failOn: resolveFailOn(opts),
|
|
168
188
|
includeNonBreaking: opts.strict || opts.includeNonBreaking,
|
|
169
189
|
suggestVersion: opts.suggestVersion,
|
|
170
|
-
noColor: !colorEnabled(opts, loaded.config.output.color)
|
|
190
|
+
noColor: !colorEnabled(opts, loaded.config.output.color),
|
|
191
|
+
profile: opts.profile
|
|
171
192
|
});
|
|
172
193
|
const { report } = await analyze(oldSpec, newSpec, {
|
|
173
194
|
config,
|
|
@@ -182,6 +203,10 @@ async function runDiff(oldSpec, newSpec, opts, io) {
|
|
|
182
203
|
} else if (config.output.format === "markdown") {
|
|
183
204
|
const { renderMarkdown } = await import("../markdown-J2EC3VFS.js");
|
|
184
205
|
body = renderMarkdown(report);
|
|
206
|
+
} else if (config.output.format === "html") {
|
|
207
|
+
body = renderHtml(report);
|
|
208
|
+
} else if (config.output.format === "sarif") {
|
|
209
|
+
body = renderSarif(report);
|
|
185
210
|
} else {
|
|
186
211
|
const lines = renderTerminal(report, {
|
|
187
212
|
includeNonBreaking: config.output.includeNonBreaking,
|
|
@@ -194,9 +219,17 @@ async function runDiff(oldSpec, newSpec, opts, io) {
|
|
|
194
219
|
if (bump) body += "\n\n" + bump;
|
|
195
220
|
}
|
|
196
221
|
io.stdout(body);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
222
|
+
const outputPath = opts.output ?? (opts.open ? "apiguard-report.html" : void 0);
|
|
223
|
+
if (outputPath) {
|
|
224
|
+
fs.writeFileSync(outputPath, body + "\n", "utf8");
|
|
225
|
+
if (!opts.quiet) io.stderr(`report written to ${outputPath}`);
|
|
226
|
+
if (opts.open && (config.output.format === "html" || outputPath.endsWith(".html"))) {
|
|
227
|
+
const openCmd = process.platform === "win32" ? `start "" "${outputPath}"` : process.platform === "darwin" ? `open "${outputPath}"` : `xdg-open "${outputPath}"`;
|
|
228
|
+
try {
|
|
229
|
+
exec(openCmd);
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
200
233
|
}
|
|
201
234
|
if (config.failOn === "never") return ExitCode.Ok;
|
|
202
235
|
const threshold = config.failOn === "error" ? "error" : "warning";
|
|
@@ -354,6 +387,65 @@ use --force to overwrite`
|
|
|
354
387
|
io.stdout('next: run "apiguard diff examples/baseline.yaml examples/current.yaml"');
|
|
355
388
|
}
|
|
356
389
|
|
|
390
|
+
// src/cli/changelog-command.ts
|
|
391
|
+
import fs3 from "fs";
|
|
392
|
+
async function runChangelog(oldSpec, newSpec, opts, io) {
|
|
393
|
+
if (opts.gitBase) {
|
|
394
|
+
if (!newSpec && oldSpec) {
|
|
395
|
+
newSpec = oldSpec;
|
|
396
|
+
oldSpec = `git:${opts.gitBase}:${newSpec}`;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (!oldSpec || !newSpec) {
|
|
400
|
+
throw new CliUsageError("missing required spec arguments (or use --git-base <ref>)");
|
|
401
|
+
}
|
|
402
|
+
const loaded = discoverAndLoadConfig({ explicit: opts.config, env: io.env.APIGUARD_CONFIG });
|
|
403
|
+
const { report } = await analyze(oldSpec, newSpec, { config: loaded.config });
|
|
404
|
+
const body = opts.json ? JSON.stringify(
|
|
405
|
+
{ semver: report.semver, summary: report.summary, changes: report.changes },
|
|
406
|
+
null,
|
|
407
|
+
2
|
|
408
|
+
) : renderChangelog(report);
|
|
409
|
+
io.stdout(body);
|
|
410
|
+
if (opts.output) {
|
|
411
|
+
fs3.writeFileSync(opts.output, body + "\n", "utf8");
|
|
412
|
+
io.stderr(`changelog written to ${opts.output}`);
|
|
413
|
+
}
|
|
414
|
+
return ExitCode.Ok;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/cli/codegen-command.ts
|
|
418
|
+
import fs4 from "fs";
|
|
419
|
+
async function runCodegenShim(oldSpec, newSpec, opts, io) {
|
|
420
|
+
if (opts.gitBase) {
|
|
421
|
+
if (!newSpec && oldSpec) {
|
|
422
|
+
newSpec = oldSpec;
|
|
423
|
+
oldSpec = `git:${opts.gitBase}:${newSpec}`;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (!oldSpec || !newSpec) {
|
|
427
|
+
throw new CliUsageError("missing required spec arguments (or use --git-base <ref>)");
|
|
428
|
+
}
|
|
429
|
+
const rawTarget = opts.target ?? "express";
|
|
430
|
+
if (rawTarget !== "express" && rawTarget !== "cloudflare-worker" && rawTarget !== "envoy-lua") {
|
|
431
|
+
throw new CliUsageError(
|
|
432
|
+
`invalid --target "${rawTarget}" (expected express|cloudflare-worker|envoy-lua)`
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
const target = rawTarget;
|
|
436
|
+
const loaded = discoverAndLoadConfig({ explicit: opts.config, env: io.env.APIGUARD_CONFIG });
|
|
437
|
+
const { report } = await analyze(oldSpec, newSpec, { config: loaded.config });
|
|
438
|
+
const result = generateGatewayShim(report, target);
|
|
439
|
+
io.stdout(result.code);
|
|
440
|
+
if (opts.output) {
|
|
441
|
+
fs4.writeFileSync(opts.output, result.code, "utf8");
|
|
442
|
+
io.stderr(
|
|
443
|
+
`compatibility shim written to ${opts.output} (${result.handledChanges} breaking change(s) addressed)`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
return ExitCode.Ok;
|
|
447
|
+
}
|
|
448
|
+
|
|
357
449
|
// src/cli/run.ts
|
|
358
450
|
var defaultIo = {
|
|
359
451
|
stdout: (s) => process.stdout.write(s + "\n"),
|
|
@@ -369,7 +461,10 @@ function buildProgram(io) {
|
|
|
369
461
|
writeErr: (s) => io.stderr(s.trimEnd())
|
|
370
462
|
});
|
|
371
463
|
program.name("apiguard").version(TOOL_VERSION).description("Detect breaking changes between OpenAPI specifications");
|
|
372
|
-
const diff = program.command("diff").description("Diff two OpenAPI specs and report breaking changes").argument("<old-spec>", "baseline spec path").argument("<new-spec>", "current spec path").option(
|
|
464
|
+
const diff = program.command("diff").description("Diff two OpenAPI specs and report breaking changes").argument("<old-spec>", "baseline spec path").argument("<new-spec>", "current spec path").option(
|
|
465
|
+
"--format <f>",
|
|
466
|
+
"terminal|json|markdown|html|sarif (aliases: --json, --markdown, --html, --sarif)"
|
|
467
|
+
).option("--json", "shorthand for --format json").option("--markdown", "shorthand for --format markdown").option("--html", "shorthand for --format html").option("--sarif", "shorthand for --format sarif").option("--open", "open interactive HTML report in browser").option("--git-base <ref>", "resolve baseline against git branch or commit").option("--profile <p>", "client persona profile (default|strict-sdk|loose-web)").option("--config <path>", "explicit config file (missing file: exit 2)").option("--fail-on <level>", "error|warning|never", "error").option("--strict", "shorthand for --fail-on warning AND --include-non-breaking").option("--quiet", "print only the one-line verdict").option("--suggest-version", "print suggested version bump after the report").option("--include-non-breaking", "also render warning+info changes").option("--output <path>", "also write the full report to a file").option("--no-color", "disable ANSI color (auto-detected for non-TTY)").option("--show-ignored", "render changes suppressed by ignore rules, tagged ignored").option("--verbose", "log parse/config steps to stderr").action(async (oldSpec, newSpec, opts) => {
|
|
373
468
|
const code = await runDiff(oldSpec, newSpec, opts, io);
|
|
374
469
|
if (code !== ExitCode.Ok) {
|
|
375
470
|
process.exitCode = code;
|
|
@@ -377,6 +472,20 @@ function buildProgram(io) {
|
|
|
377
472
|
}
|
|
378
473
|
});
|
|
379
474
|
void diff;
|
|
475
|
+
program.command("changelog").description("Generate a Keep-a-Changelog formatted release summary from spec diff").argument("<old-spec>", "baseline spec path").argument("<new-spec>", "current spec path").option("--config <path>", "explicit config file").option("--output <path>", "write changelog to file").option("--git-base <ref>", "resolve baseline against git branch or commit").option("--json", "output changelog as JSON").action(async (oldSpec, newSpec, opts) => {
|
|
476
|
+
const code = await runChangelog(oldSpec, newSpec, opts, io);
|
|
477
|
+
if (code !== ExitCode.Ok) {
|
|
478
|
+
process.exitCode = code;
|
|
479
|
+
program.__apiguardExitCode = code;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
program.command("codegen-shim").description("Generate backward-compatibility gateway adapter for breaking changes").argument("<old-spec>", "baseline spec path").argument("<new-spec>", "current spec path").option("--target <t>", "express|cloudflare-worker|envoy-lua", "express").option("--config <path>", "explicit config file").option("--output <path>", "write generated shim code to file").option("--git-base <ref>", "resolve baseline against git branch or commit").action(async (oldSpec, newSpec, opts) => {
|
|
483
|
+
const code = await runCodegenShim(oldSpec, newSpec, opts, io);
|
|
484
|
+
if (code !== ExitCode.Ok) {
|
|
485
|
+
process.exitCode = code;
|
|
486
|
+
program.__apiguardExitCode = code;
|
|
487
|
+
}
|
|
488
|
+
});
|
|
380
489
|
program.command("validate").description("Load and validate a spec").argument("<spec>", "spec path").option("--config <path>", "explicit config file").option("--strict", "exit 1 when semantic warnings exist").option("--verbose", "log parse steps to stderr").action(async (spec, opts) => {
|
|
381
490
|
const outcome = await runValidate(spec, opts, io);
|
|
382
491
|
if (outcome.exitCode !== ExitCode.Ok) {
|