specshield 2.0.0 → 3.0.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
@@ -7,7 +7,7 @@
7
7
 
8
8
  ---
9
9
 
10
- > **OpenAPI Diff · Swagger Diff · API Breaking Change Detection · Contract Testing · Pact Alternative · API Governance · CI/CD API Validation · API Drift Detection · Bi-Directional Contract Testing**
10
+ > **OpenAPI Diff · API Breaking Change Detection · Swagger Diff · Bi-Directional Contract Testing · Pact Alternative · can-i-deploy · GitHub PR Checks · CI/CD**
11
11
 
12
12
  ---
13
13
 
@@ -57,7 +57,7 @@ No runtime surprise. No production incident. No 3am page.
57
57
 
58
58
  [![SpecShield Demo — API Breaking Change Detection & Contract Testing](https://img.youtube.com/vi/mugDyQQGqZw/maxresdefault.jpg)](https://www.youtube.com/watch?v=mugDyQQGqZw)
59
59
 
60
- > Watch: Catching a breaking API change before it hits production — compare specs, publish contracts, verify providers, and gate deployments with `can-i-deploy`.
60
+ > Watch: Catching a breaking API change before it hits production — compare specs, publish provider/consumer specs via BDCT, and gate deployments with `can-i-deploy`.
61
61
 
62
62
  **CLI preview:**
63
63
 
@@ -141,8 +141,6 @@ That's it. Works with any OpenAPI 3.x YAML or JSON spec.
141
141
  | JSON / human output | ✅ | ✅ | ✅ |
142
142
  | Fail CI on breaking change | ✅ | ✅ | ✅ |
143
143
  | **Compare history & dashboard** | ❌ | ✅ | ✅ |
144
- | **CDCT contract testing registry** | ❌ | ✅ | ✅ |
145
- | **CDCT can-i-deploy gating** | ❌ | ✅ | ✅ |
146
144
  | **GitHub App PR checks** | ❌ | ✅ | ✅ |
147
145
  | **BDCT bi-directional contracts** | ❌ | ❌ | ✅ |
148
146
  | **BDCT can-i-deploy gating** | ❌ | ❌ | ✅ |
@@ -160,8 +158,9 @@ That's it. Works with any OpenAPI 3.x YAML or JSON spec.
160
158
  > Without SpecShield, this would have reached staging, broken checkout for every user,
161
159
  > and triggered an incident at 2am.
162
160
  >
163
- > With SpecShield, the provider's CI ran `specshield contracts verify` against the
164
- > consumer's published contract. The mismatch was caught immediately:
161
+ > With SpecShield, the provider's CI published the new spec via
162
+ > `specshield bdct publish-provider`. The compatibility engine ran against
163
+ > `checkout-ui`'s published contract and the mismatch was caught immediately:
165
164
  >
166
165
  > ```
167
166
  > ● MISSING_FIELD at $.status
@@ -195,7 +194,6 @@ Track API drift over time across your entire platform. Know what changed, when,
195
194
  |---|---|---|---|
196
195
  | OpenAPI / Swagger native | ✅ | ❌ (code-level) | ✅ |
197
196
  | No broker required | ✅ | ❌ (needs Pact Broker) | ✅ |
198
- | Consumer-driven contract testing | ✅ | ✅ | ❌ |
199
197
  | **Bi-directional contract testing** | ✅ | ✅ (Pactflow paid) | ❌ |
200
198
  | Breaking change detection | ✅ | ❌ | ✅ |
201
199
  | can-i-deploy gating | ✅ | ✅ (via broker) | ❌ |
@@ -212,7 +210,7 @@ Track API drift over time across your entire platform. Know what changed, when,
212
210
 
213
211
  | Plan | Price | What's included |
214
212
  |---|---|---|
215
- | **Free** | $0 forever | Local compare (unlimited) · Compare history & dashboard · CDCT contracts & can-i-deploy · GitHub App PR checks |
213
+ | **Free** | $0 forever | Local compare (unlimited) · Compare history & dashboard · GitHub App PR checks |
216
214
  | **Pro** | Coming soon | Everything in Free + BDCT bi-directional contracts · BDCT can-i-deploy & matrix · Team collaboration · Advanced reporting · Priority support |
217
215
 
218
216
  No credit card ever required for the free plan.
@@ -351,137 +349,16 @@ github:
351
349
 
352
350
  ---
353
351
 
354
- ## Contract Testing (CDCT)
355
-
356
- Consumer-driven contract testing for microservices — without a broker.
357
-
358
- **How it works:**
359
-
360
- 1. Consumer team publishes a contract (what they expect from the provider)
361
- 2. Provider team verifies their service satisfies it by actually calling it
362
- 3. `can-i-deploy` gates the deployment based on verification results
363
-
364
- ### Contract File Format
365
-
366
- ```json
367
- {
368
- "consumer": { "name": "checkout-ui", "version": "2.0.0" },
369
- "provider": { "name": "payment-service" },
370
- "orgKey": "acme-store",
371
- "contractName": "create-payment",
372
- "contractType": "HTTP",
373
- "interactions": [
374
- {
375
- "description": "checkout-ui creates a payment",
376
- "request": {
377
- "method": "POST",
378
- "path": "/payments",
379
- "headers": { "Content-Type": "application/json" },
380
- "body": { "orderId": "ORD-123", "amount": 1299, "currency": "INR" }
381
- },
382
- "expectedResponse": {
383
- "status": 201,
384
- "headers": { "Content-Type": "application/json" },
385
- "body": { "paymentId": "PAY-123", "status": "CREATED" }
386
- }
387
- }
388
- ]
389
- }
390
- ```
391
-
392
- ### Publish a Contract
393
-
394
- ```bash
395
- specshield contracts publish \
396
- --file ./contracts/create-payment.json \
397
- --org acme-store \
398
- --consumer-version 2.0.0 \
399
- --tag main
400
- ```
401
-
402
- ### Verify a Contract
403
-
404
- ```bash
405
- specshield contracts verify \
406
- --contract-id 42 \
407
- --base-url http://localhost:8080 \
408
- --provider-version v2.1.0 \
409
- --env staging
410
- ```
411
-
412
- Pass output:
413
- ```
414
- ✔ Verification PASSED (1/1 interactions)
415
- ```
416
-
417
- Fail output:
418
- ```
419
- ✖ Verification FAILED (0/1 interactions passed, 1 failed)
420
-
421
- Mismatches
422
- ● [create payment] MISSING_FIELD at $.status
423
- expected: "CREATED" → actual: null
424
- ```
425
-
426
- ### Can I Deploy?
427
-
428
- ```bash
429
- specshield contracts can-i-deploy \
430
- --provider payment-service \
431
- --version v2.1.0 \
432
- --env staging
433
- ```
434
-
435
- ```
436
- ✔ PASS: payment-service v2.1.0 is deployable in staging
437
- ```
438
- ```
439
- ✖ FAIL: payment-service v2.1.0 is NOT deployable in staging
440
- ```
441
-
442
- Exit codes: `0` = deployable · `1` = blocked · `2` = error
443
-
444
- ### List and Inspect Contracts
445
-
446
- ```bash
447
- specshield contracts list --provider payment-service
448
- specshield contracts latest --consumer checkout-ui --provider payment-service --json
449
- specshield contracts history --contract-id 42
450
- ```
451
-
452
- ### Full CDCT Workflow
453
-
454
- ```bash
455
- # 1. Consumer publishes contract
456
- specshield contracts publish --file ./contracts/create-payment.json --org acme-store
457
-
458
- # 2. Provider verifies it
459
- specshield contracts verify --contract-id 42 --base-url http://localhost:8080 --provider-version v2.1.0
460
-
461
- # 3. Gate the deployment
462
- specshield contracts can-i-deploy --provider payment-service --version v2.1.0
463
- ```
464
-
465
- ---
466
-
467
352
  ## Bi-Directional Contract Testing (BDCT)
468
353
 
469
354
  **Spec-to-spec contract testing — no running services required.**
470
355
 
471
- BDCT is the static alternative to CDCT. Instead of running the provider server, both sides publish their OpenAPI specs. SpecShield compares them and flags mismatches immediately — ideal for teams that don't run services locally or in CI.
356
+ Both sides publish their OpenAPI specs. SpecShield compares them and flags
357
+ mismatches immediately — ideal for teams that don't run services locally or in
358
+ CI. Pact JSON consumer contracts are also accepted and auto-converted.
472
359
 
473
360
  > BDCT requires a **Pro plan**. [Upgrade at specshield.io/upgrade](https://specshield.io/upgrade)
474
361
 
475
- **CDCT vs BDCT:**
476
-
477
- | | CDCT | BDCT |
478
- |---|---|---|
479
- | How verification works | Replay requests against a live server | Compare OpenAPI specs statically |
480
- | Provider needs to run | Yes | No |
481
- | Feedback speed | After deploy to test env | Immediately on spec publish |
482
- | Pact JSON contracts | Supported | Supported (auto-converted) |
483
- | Best for | Runtime correctness | Early spec-level safety |
484
-
485
362
  ### How BDCT Works
486
363
 
487
364
  1. Consumer team publishes an OpenAPI spec subset (the endpoints they use)
@@ -687,7 +564,7 @@ specshield bdct list-consumers --org acme-store --provider payment-service
687
564
  ### List Verifications
688
565
 
689
566
  ```bash
690
- specshield bdct list-verifications \
567
+ specshield bdct list \
691
568
  --org acme-store \
692
569
  --provider payment-service \
693
570
  --env production \
@@ -780,73 +657,6 @@ jobs:
780
657
  run: specshield compare /tmp/base.yaml api/openapi.yaml --fail-on-breaking
781
658
  ```
782
659
 
783
- ### On Push — Publish consumer contract (CDCT)
784
-
785
- ```yaml
786
- name: Publish Contract
787
-
788
- on:
789
- push:
790
- branches: [main]
791
- paths:
792
- - 'contracts/**'
793
-
794
- jobs:
795
- publish:
796
- runs-on: ubuntu-latest
797
- steps:
798
- - uses: actions/checkout@v4
799
- - uses: actions/setup-node@v4
800
- with:
801
- node-version: '20'
802
- - run: npm install -g specshield
803
- - name: Publish contract
804
- env:
805
- SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
806
- run: |
807
- specshield contracts publish \
808
- --file ./contracts/create-payment.json \
809
- --org acme-store \
810
- --tag ${{ github.ref_name }}
811
- ```
812
-
813
- ### On Push — Verify provider + gate deployment (CDCT)
814
-
815
- ```yaml
816
- name: Contract Verification
817
-
818
- on:
819
- push:
820
- branches: [main]
821
-
822
- jobs:
823
- verify:
824
- runs-on: ubuntu-latest
825
- steps:
826
- - uses: actions/checkout@v4
827
- - uses: actions/setup-node@v4
828
- with:
829
- node-version: '20'
830
- - run: npm install -g specshield
831
- - name: Verify contract
832
- env:
833
- SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
834
- run: |
835
- specshield contracts verify \
836
- --contract-id ${{ vars.CONTRACT_ID }} \
837
- --base-url http://localhost:8080 \
838
- --provider-version ${{ github.sha }} \
839
- --env staging
840
- - name: Can I deploy?
841
- env:
842
- SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
843
- run: |
844
- specshield contracts can-i-deploy \
845
- --provider payment-service \
846
- --version ${{ github.sha }} \
847
- --env staging
848
- ```
849
-
850
660
  ### On Push — Publish provider spec (BDCT)
851
661
 
852
662
  ```yaml
@@ -981,7 +791,7 @@ specshield bdct <subcommand> [options]
981
791
  | `matrix` | View compatibility matrix across all pairs |
982
792
  | `list-providers` | List published provider specs |
983
793
  | `list-consumers` | List published consumer contracts |
984
- | `list-verifications` | List verification history |
794
+ | `list` | List verification history |
985
795
 
986
796
  All `bdct` subcommands support `--json` for machine-readable output.
987
797
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "specshield",
3
- "version": "2.0.0",
4
- "description": "CLI to compare OpenAPI/Swagger specs and detect breaking changes for CI/CD pipelines and local developer workflows.",
3
+ "version": "3.0.0",
4
+ "description": "CLI for OpenAPI breaking change detection and bi-directional contract verification with can-i-deploy gating and GitHub PR checks.",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
7
7
  "specshield": "./bin/specshield.js"
@@ -18,11 +18,8 @@
18
18
  "api-diff",
19
19
  "openapi-diff",
20
20
  "swagger-diff",
21
- "api-diff-tool",
22
21
  "breaking-change-detection",
23
22
  "api-breaking-changes",
24
- "prevent-breaking-api",
25
- "api-regression",
26
23
  "api-compatibility",
27
24
  "contract-testing",
28
25
  "consumer-driven-contract",
@@ -30,7 +27,6 @@
30
27
  "contract-verification",
31
28
  "can-i-deploy",
32
29
  "ci-cd",
33
- "devops",
34
30
  "github-actions",
35
31
  "cli",
36
32
  "developer-tools",
@@ -41,8 +37,11 @@
41
37
  "microservices",
42
38
  "api-quality",
43
39
  "api-governance",
44
- "yaml",
45
- "json"
40
+ "bi-directional-contract-testing",
41
+ "bdct",
42
+ "pact-json",
43
+ "github-app",
44
+ "api-drift"
46
45
  ],
47
46
  "license": "MIT",
48
47
  "files": [
@@ -22,7 +22,10 @@ function apiError(err) {
22
22
  const data = err.response.data;
23
23
  const msg = (data && (data.message || data.error || data.title))
24
24
  || `HTTP ${err.response.status}`;
25
- return new Error(`API error (${err.response.status}): ${msg}`);
25
+ const e = new Error(`API error (${err.response.status}): ${msg}`);
26
+ e.status = err.response.status;
27
+ e.responseData = data;
28
+ return e;
26
29
  }
27
30
  if (err.request) return new Error(`No response from server: ${err.message}`);
28
31
  return new Error(`Request failed: ${err.message}`);
@@ -51,11 +54,10 @@ async function verify(server, apiToken, payload) {
51
54
 
52
55
  async function listVerifications(server, apiToken, { org, consumer, provider, env, page = 0, size = 20 } = {}) {
53
56
  try {
54
- const params = { page, size };
55
- if (org) params.orgKey = org;
56
- if (consumer) params.consumer = consumer;
57
- if (provider) params.provider = provider;
58
- if (env) params.env = env;
57
+ const params = { orgKey: org, page, size };
58
+ if (consumer) params.consumerName = consumer;
59
+ if (provider) params.providerName = provider;
60
+ if (env) params.environment = env;
59
61
  const res = await buildClient(server, apiToken).get('/api/bdct/verifications', { params });
60
62
  return res.data;
61
63
  } catch (err) { throw apiError(err); }
@@ -63,41 +65,44 @@ async function listVerifications(server, apiToken, { org, consumer, provider, en
63
65
 
64
66
  async function canIDeploy(server, apiToken, { org, service, version: ver, env } = {}) {
65
67
  try {
66
- const params = { service, version: ver };
67
- if (org) params.orgKey = org;
68
- if (env) params.env = env;
68
+ const params = { orgKey: org, service, version: ver };
69
+ if (env) params.environment = env;
69
70
  const res = await buildClient(server, apiToken).get('/api/bdct/can-i-deploy', { params });
70
71
  return res.data;
71
- } catch (err) { throw apiError(err); }
72
+ } catch (err) {
73
+ // 409 = NOT DEPLOYABLE — valid result, not an error (backend signals via status)
74
+ if (err.response && err.response.status === 409 && err.response.data
75
+ && typeof err.response.data === 'object'
76
+ && Object.prototype.hasOwnProperty.call(err.response.data, 'deployable')) {
77
+ return err.response.data;
78
+ }
79
+ throw apiError(err);
80
+ }
72
81
  }
73
82
 
74
83
  async function getMatrix(server, apiToken, { org, env } = {}) {
75
84
  try {
76
- const params = {};
77
- if (org) params.orgKey = org;
78
- if (env) params.env = env;
85
+ const params = { orgKey: org };
86
+ if (env) params.environment = env;
79
87
  const res = await buildClient(server, apiToken).get('/api/bdct/matrix', { params });
80
88
  return res.data;
81
89
  } catch (err) { throw apiError(err); }
82
90
  }
83
91
 
84
- async function listProviderSpecs(server, apiToken, { org, provider, env, page = 0, size = 20 } = {}) {
92
+ async function listProviderSpecs(server, apiToken, { org, provider } = {}) {
85
93
  try {
86
- const params = { page, size };
87
- if (org) params.orgKey = org;
88
- if (provider) params.provider = provider;
89
- if (env) params.env = env;
94
+ const params = { orgKey: org };
95
+ if (provider) params.providerName = provider;
90
96
  const res = await buildClient(server, apiToken).get('/api/bdct/provider-specs', { params });
91
97
  return res.data;
92
98
  } catch (err) { throw apiError(err); }
93
99
  }
94
100
 
95
- async function listConsumerContracts(server, apiToken, { org, consumer, provider, page = 0, size = 20 } = {}) {
101
+ async function listConsumerContracts(server, apiToken, { org, consumer, provider } = {}) {
96
102
  try {
97
- const params = { page, size };
98
- if (org) params.orgKey = org;
99
- if (consumer) params.consumer = consumer;
100
- if (provider) params.provider = provider;
103
+ const params = { orgKey: org };
104
+ if (consumer) params.consumerName = consumer;
105
+ if (provider) params.providerName = provider;
101
106
  const res = await buildClient(server, apiToken).get('/api/bdct/consumer-contracts', { params });
102
107
  return res.data;
103
108
  } catch (err) { throw apiError(err); }
package/src/cli.js CHANGED
@@ -5,7 +5,6 @@ const { version } = require('../package.json');
5
5
  const compareCommand = require('./commands/compare');
6
6
  const loginCommand = require('./commands/login');
7
7
  const logoutCommand = require('./commands/logout');
8
- const contractsCommand = require('./commands/contracts');
9
8
  const bdctCommand = require('./commands/bdct');
10
9
 
11
10
  const program = new Command();
@@ -13,12 +12,15 @@ const program = new Command();
13
12
  program
14
13
  .name('specshield')
15
14
  .description('Compare OpenAPI specs and detect breaking changes')
16
- .version(version);
15
+ .version(version)
16
+ // Without this, a subcommand option named `--version` (e.g. on `bdct can-i-deploy`)
17
+ // is greedily consumed by the root `--version` flag and the program prints the
18
+ // CLI version and exits before the subcommand runs.
19
+ .enablePositionalOptions();
17
20
 
18
21
  program.addCommand(compareCommand);
19
22
  program.addCommand(loginCommand);
20
23
  program.addCommand(logoutCommand);
21
- program.addCommand(contractsCommand);
22
24
  program.addCommand(bdctCommand);
23
25
 
24
26
  program.parseAsync(process.argv).catch((err) => {