specshield 3.2.5 → 3.2.6
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 +65 -5
- package/package.json +1 -1
- package/src/commands/compare.js +15 -0
- package/src/commands/history.js +38 -0
- package/src/commands/share.js +27 -0
package/README.md
CHANGED
|
@@ -5,13 +5,40 @@
|
|
|
5
5
|
[](#license)
|
|
6
6
|
[](https://nodejs.org)
|
|
7
7
|
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
**Get started**
|
|
11
|
+
- [Quick Start](#quick-start) · [`specshield init` wizard](#specshield-init--first-run-setup-wizard) · [Login & Authentication](#login--authentication)
|
|
12
|
+
|
|
13
|
+
**Why SpecShield**
|
|
14
|
+
- [The Problem](#the-problem) · [The Solution](#the-solution) · [See It in Action](#see-it-in-action) · [Use Cases](#use-cases)
|
|
15
|
+
- [Local vs Cloud](#local-vs-cloud) · [vs. Alternatives](#vs-alternatives) · [Pricing](#pricing)
|
|
16
|
+
|
|
17
|
+
**Commands — local & hosted compare**
|
|
18
|
+
- [Local Compare](#local-compare) · [Remote Compare](#remote-compare) · [Comparison History](#comparison-history) · [Share a Comparison](#share-a-comparison) · [GitHub App PR Checks](#github-app--pr-checks)
|
|
19
|
+
|
|
20
|
+
**Bi-Directional Contract Testing (BDCT)**
|
|
21
|
+
- [BDCT overview & full command reference](#bi-directional-contract-testing-bdct)
|
|
22
|
+
- [`bdct capture from-har` — HAR → consumer contract](#bdct-capture-from-har--turn-real-traffic-into-a-consumer-contract)
|
|
23
|
+
- [Step 1 — record a HAR](#step-1--record-a-har-file) · [Step 2 — generate contract](#step-2--turn-the-har-into-a-consumer-contract) · [Step 3 — publish + gate](#step-3--publish--gate) · [Operational concerns](#operational-concerns-read-this-before-going-live)
|
|
24
|
+
- [`bdct verify-provider` — spec-vs-production conformance](#bdct-verify-provider--does-your-live-provider-actually-match-its-spec)
|
|
25
|
+
|
|
26
|
+
**CI/CD**
|
|
27
|
+
- [GitHub Actions workflows](#cicd--github-actions)
|
|
28
|
+
|
|
29
|
+
**Reference**
|
|
30
|
+
- [Config File](#config-file) · [All Options](#all-options) · [Exit Codes](#exit-codes)
|
|
31
|
+
|
|
32
|
+
**Project**
|
|
33
|
+
- [License](#license) · [Support](#support)
|
|
34
|
+
|
|
8
35
|
---
|
|
9
36
|
|
|
10
37
|
> **OpenAPI Diff · Breaking-Change Detection · Bi-Directional Contract Testing · `can-i-deploy` Gate · Pact-File Ingest · Live-Traffic Capture · Spec-vs-Production Conformance · GitHub PR Checks**
|
|
11
38
|
|
|
12
39
|
---
|
|
13
40
|
|
|
14
|
-
## Never ship a breaking change to your API consumers
|
|
41
|
+
## Never ship a breaking change to your API consumers
|
|
15
42
|
|
|
16
43
|
**SpecShield** is the one CLI that does four things to keep your API safe:
|
|
17
44
|
|
|
@@ -215,7 +242,9 @@ specshield compare base.yaml target.yaml --ignore "DELETE /admin removed" --fail
|
|
|
215
242
|
|
|
216
243
|
## Remote Compare
|
|
217
244
|
|
|
218
|
-
Sends your specs to SpecShield
|
|
245
|
+
Sends your specs to SpecShield's hosted diff engine. Useful for keeping the
|
|
246
|
+
heavy comparison off your CI runner and for cross-validating against the
|
|
247
|
+
exact engine the dashboard uses. Requires a free account.
|
|
219
248
|
|
|
220
249
|
```bash
|
|
221
250
|
specshield compare base.yaml target.yaml --remote
|
|
@@ -223,9 +252,29 @@ specshield compare base.yaml target.yaml --remote --fail-on-breaking
|
|
|
223
252
|
specshield compare base.yaml target.yaml --remote --json --output result.json
|
|
224
253
|
```
|
|
225
254
|
|
|
255
|
+
> **History note:** `--remote` runs the diff on our servers but currently
|
|
256
|
+
> does **not** persist the result to your comparison history (saving is
|
|
257
|
+
> UI-only right now — see the
|
|
258
|
+
> [Comparison History](#comparison-history) section below). To save a
|
|
259
|
+
> comparison, run it from the dashboard at
|
|
260
|
+
> [specshield.io/account/compare](https://specshield.io/account/compare).
|
|
261
|
+
> A CLI-driven save is on the roadmap.
|
|
262
|
+
|
|
226
263
|
## Comparison History
|
|
227
264
|
|
|
228
|
-
|
|
265
|
+
> **Status: currently UI-only.** Comparison history is available in the
|
|
266
|
+
> dashboard at [specshield.io/account/history](https://specshield.io/account/history).
|
|
267
|
+
> The `specshield history` CLI command exists but currently exits with a
|
|
268
|
+
> "use the dashboard" message — a CLI-callable API endpoint is on the
|
|
269
|
+
> roadmap (the backend's `/me/*` routes require browser-session JWT auth,
|
|
270
|
+
> not the CLI's API key). The dashboard shows the same data and adds
|
|
271
|
+
> per-comparison drill-down.
|
|
272
|
+
|
|
273
|
+
Every comparison you run **in the dashboard's `/account/compare` page** is
|
|
274
|
+
saved to your account. Useful for tracking API drift over time across team
|
|
275
|
+
members + machines.
|
|
276
|
+
|
|
277
|
+
The CLI command (once the backend endpoint ships):
|
|
229
278
|
|
|
230
279
|
```bash
|
|
231
280
|
specshield history # last 20 comparisons
|
|
@@ -233,6 +282,8 @@ specshield history --limit 50
|
|
|
233
282
|
specshield history --json
|
|
234
283
|
```
|
|
235
284
|
|
|
285
|
+
Expected output (once enabled):
|
|
286
|
+
|
|
236
287
|
```
|
|
237
288
|
Your recent comparisons
|
|
238
289
|
─────────────────────────────────────────────────────
|
|
@@ -243,7 +294,15 @@ specshield history --json
|
|
|
243
294
|
|
|
244
295
|
## Share a Comparison
|
|
245
296
|
|
|
246
|
-
|
|
297
|
+
> **Status: currently UI-only.** Share links are generated from the
|
|
298
|
+
> dashboard — open any comparison at
|
|
299
|
+
> [specshield.io/account/history](https://specshield.io/account/history)
|
|
300
|
+
> and click the "Share" button. Same `/r/<token>` URL shape. The
|
|
301
|
+
> `specshield share` CLI command currently exits with a "use the dashboard"
|
|
302
|
+
> message; CLI integration ships when the backend exposes a CLI-callable
|
|
303
|
+
> share-link endpoint.
|
|
304
|
+
|
|
305
|
+
Once enabled, the CLI usage will be:
|
|
247
306
|
|
|
248
307
|
```bash
|
|
249
308
|
# Share an existing report by ID (from `specshield history`)
|
|
@@ -262,7 +321,8 @@ specshield share 482 --expires 30
|
|
|
262
321
|
Expires: 2026-06-16T12:34:56Z
|
|
263
322
|
```
|
|
264
323
|
|
|
265
|
-
Links use a 256-bit random token (unguessable by enumeration). Revoke any
|
|
324
|
+
Links use a 256-bit random token (unguessable by enumeration). Revoke any
|
|
325
|
+
time from the dashboard.
|
|
266
326
|
|
|
267
327
|
## GitHub App — PR Checks
|
|
268
328
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specshield",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.6",
|
|
4
4
|
"description": "CLI for OpenAPI breaking change detection and bi-directional contract verification — with can-i-deploy gating, GitHub PR checks, and a first-run setup wizard.",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
package/src/commands/compare.js
CHANGED
|
@@ -96,6 +96,21 @@ compare
|
|
|
96
96
|
await fsExtra.outputFile(options.output, JSON.stringify(jsonOutput, null, 2));
|
|
97
97
|
logger.info(`Results saved to ${options.output}`);
|
|
98
98
|
}
|
|
99
|
+
|
|
100
|
+
// Honest note for --remote when the backend didn't return a history
|
|
101
|
+
// ID. The `/compare` endpoint runs the diff but does NOT persist to
|
|
102
|
+
// the user's comparison history when called with X-Api-Key auth —
|
|
103
|
+
// that's a UI-only path right now (see the share/history commands).
|
|
104
|
+
// When/if the backend starts returning historyId, this note auto-hides.
|
|
105
|
+
if ((options.remote || options.remoteUrl)
|
|
106
|
+
&& !result.historyId && !result.reportId && !result.id) {
|
|
107
|
+
const chalk = require('chalk');
|
|
108
|
+
process.stdout.write('\n' + chalk.gray(
|
|
109
|
+
' Note: --remote ran the diff on our servers but did NOT save it to your\n' +
|
|
110
|
+
' comparison history (history save is currently UI-only). For history, run\n' +
|
|
111
|
+
' the compare in the dashboard at https://specshield.io/account/compare.\n'
|
|
112
|
+
));
|
|
113
|
+
}
|
|
99
114
|
}
|
|
100
115
|
|
|
101
116
|
// Contextual signup nudge — runs only when the user isn't logged in,
|
package/src/commands/history.js
CHANGED
|
@@ -49,6 +49,17 @@ history
|
|
|
49
49
|
});
|
|
50
50
|
if (spinner) spinner.stop();
|
|
51
51
|
|
|
52
|
+
// The /me/* routes currently require JWT (browser session) auth — the
|
|
53
|
+
// CLI's X-Api-Key isn't accepted, so the backend redirects to /login
|
|
54
|
+
// and axios follows the 302 to the SPA's HTML shell. Detect that and
|
|
55
|
+
// tell the user where the feature really lives, instead of silently
|
|
56
|
+
// returning an empty list.
|
|
57
|
+
if (typeof response.data === 'string'
|
|
58
|
+
&& (response.data.includes('<!doctype') || response.data.includes('<html'))) {
|
|
59
|
+
printWebOnlyMessage(opts);
|
|
60
|
+
process.exit(2);
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
const items = response.data?.content
|
|
53
64
|
|| response.data?.items
|
|
54
65
|
|| (Array.isArray(response.data) ? response.data : []);
|
|
@@ -107,4 +118,31 @@ function printSignupNudge() {
|
|
|
107
118
|
console.log('');
|
|
108
119
|
}
|
|
109
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Surfaced when the backend redirects our API-key request to /login (HTML
|
|
123
|
+
* response). The /me/compare-history endpoint is currently JWT-only — the
|
|
124
|
+
* web UI signs in via OAuth and uses a browser-session JWT. A CLI-callable
|
|
125
|
+
* API endpoint is on the roadmap; until then, the dashboard is the way.
|
|
126
|
+
*/
|
|
127
|
+
function printWebOnlyMessage(opts) {
|
|
128
|
+
if (opts.json) {
|
|
129
|
+
process.stdout.write(JSON.stringify({
|
|
130
|
+
error: 'CLI_UNAVAILABLE',
|
|
131
|
+
message: 'Comparison history is currently UI-only. The CLI API endpoint is on the roadmap.',
|
|
132
|
+
dashboard: 'https://specshield.io/account/history',
|
|
133
|
+
}, null, 2) + '\n');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
console.log('');
|
|
137
|
+
console.log(chalk.bold(' Comparison history is currently UI-only.'));
|
|
138
|
+
console.log('');
|
|
139
|
+
console.log(' View your comparisons at:');
|
|
140
|
+
console.log(' ' + chalk.cyan('https://specshield.io/account/history'));
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log(chalk.gray(' CLI access is on the roadmap — needs a backend API endpoint that'));
|
|
143
|
+
console.log(chalk.gray(' accepts the CLI\'s X-Api-Key auth (currently `/me/*` routes require'));
|
|
144
|
+
console.log(chalk.gray(' the browser-session JWT). Until then, use the dashboard.'));
|
|
145
|
+
console.log('');
|
|
146
|
+
}
|
|
147
|
+
|
|
110
148
|
module.exports = history;
|
package/src/commands/share.js
CHANGED
|
@@ -50,6 +50,14 @@ share
|
|
|
50
50
|
process.exit(2);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// The share-link endpoint lives on `/me/*` which currently requires
|
|
54
|
+
// browser-session JWT auth — the CLI's X-Api-Key gets redirected to
|
|
55
|
+
// /login. Until the backend exposes a CLI-callable endpoint, point
|
|
56
|
+
// users at the dashboard rather than letting the request fail opaquely.
|
|
57
|
+
printWebOnlyMessage();
|
|
58
|
+
process.exit(2);
|
|
59
|
+
|
|
60
|
+
// eslint-disable-next-line no-unreachable
|
|
53
61
|
const spinner = ora('Generating share link...').start();
|
|
54
62
|
try {
|
|
55
63
|
const headers = { 'X-Api-Key': apiKey, 'X-SpecShield-Client': 'cli' };
|
|
@@ -118,4 +126,23 @@ function printSignupNudge() {
|
|
|
118
126
|
console.log('');
|
|
119
127
|
}
|
|
120
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Surfaced because `/me/share-links` currently requires JWT (web sign-in).
|
|
131
|
+
* The CLI X-Api-Key gets redirected to /login. Until a CLI-callable endpoint
|
|
132
|
+
* exists, the dashboard is the way.
|
|
133
|
+
*/
|
|
134
|
+
function printWebOnlyMessage() {
|
|
135
|
+
console.log('');
|
|
136
|
+
console.log(chalk.bold(' Share links are currently UI-only.'));
|
|
137
|
+
console.log('');
|
|
138
|
+
console.log(' Generate a share link at:');
|
|
139
|
+
console.log(' ' + chalk.cyan('https://specshield.io/account/history'));
|
|
140
|
+
console.log(' (open any comparison → click "Share").');
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log(chalk.gray(' CLI access is on the roadmap — needs a backend API endpoint that'));
|
|
143
|
+
console.log(chalk.gray(' accepts the CLI\'s X-Api-Key auth (currently `/me/*` routes require'));
|
|
144
|
+
console.log(chalk.gray(' the browser-session JWT). Until then, use the dashboard.'));
|
|
145
|
+
console.log('');
|
|
146
|
+
}
|
|
147
|
+
|
|
121
148
|
module.exports = share;
|