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 CHANGED
@@ -5,13 +5,40 @@
5
5
  [![license](https://img.shields.io/badge/license-MIT-blue)](#license)
6
6
  [![node](https://img.shields.io/badge/node-%3E%3D20-green)](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 and stores results in your dashboard. Requires a free account.
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
- Every `specshield compare --remote` is saved to your account. List recent comparisons from any machine — useful for tracking API drift over time across CI pipelines + local runs.
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
- Generate a public, tokenized URL for any comparison report. Great for Slack threads, PR comments, Jira tickets.
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 time from the dashboard.
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.5",
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": {
@@ -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,
@@ -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;
@@ -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;