@testsmith/api-spector 0.6.0 → 0.6.2
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/docs/ai/api-spector-scripting-reference.md +149 -0
- package/docs/ai/collection-file-format.md +110 -0
- package/docs/ai/functional-testing-guide.md +145 -0
- package/docs/ai/security-testing-guide.md +253 -0
- package/out/main/agents.js +2 -1
- package/out/main/chunks/{snapshots-Y33M1SEy.js → broker-client-WEymdqAK.js} +119 -10
- package/out/main/chunks/{cli-common-BKYgsbGB.js → cli-common-vASoDfQH.js} +3 -2
- package/out/main/chunks/data-files-Br2h3Dt2.js +85 -0
- package/out/main/chunks/escape-BwPM4Tf6.js +9 -0
- package/out/main/chunks/{handle-BFaeFA0A.js → handle-CL-nWtYA.js} +11 -2
- package/out/main/chunks/{import-D9LH0isB.js → import-DdXhO56X.js} +2 -2
- package/out/main/chunks/{request-collection-BQIo1765.js → request-collection-Hqh3I3rs.js} +75 -15
- package/out/main/chunks/{request-exec-5_3279w1.js → request-exec-DAFA6Fap.js} +19 -1
- package/out/main/chunks/{soap-handler-CWn3o6Py.js → soap-handler-BAPCALkN.js} +2 -2
- package/out/main/compare.js +2 -1
- package/out/main/contract.js +50 -147
- package/out/main/coverage.js +2 -1
- package/out/main/generate-tests.js +2 -1
- package/out/main/index.js +131 -36
- package/out/main/lib.js +2 -2
- package/out/main/mock.js +2 -1
- package/out/main/record.js +2 -1
- package/out/main/runner.js +95 -13
- package/out/main/wsdl.js +5 -4
- package/out/preload/index.js +21 -3
- package/out/renderer/assets/{GraphQLEditor-DITvPhtG.js → GraphQLEditor-DtppmZJO.js} +3 -1
- package/out/renderer/assets/{SoapEditor-Bf-oE-rS.js → SoapEditor-BwSm12wH.js} +4 -2
- package/out/renderer/assets/ajv-2MAg6kGF.js +6153 -0
- package/out/renderer/assets/codemirror-F6m3Hm1M.js +26552 -0
- package/out/renderer/assets/index-B3mtLdU3.css +2 -0
- package/out/renderer/assets/{index-MMIHKSKx.js → index-D6-zmXen.js} +17493 -49301
- package/out/renderer/index.html +4 -2
- package/package.json +4 -2
- package/src/cli/agent-templates/aider/conventions.md +10 -0
- package/src/cli/agent-templates/claude/skills/api-spector-api-audit.md +135 -0
- package/src/cli/agent-templates/claude/skills/api-spector-functional-tests.md +73 -0
- package/src/cli/agent-templates/claude/skills/api-spector-generate-mocks.md +182 -0
- package/src/cli/agent-templates/claude/skills/api-spector-security-tests.md +73 -0
- package/src/cli/agent-templates/copilot/copilot-instructions.md +38 -0
- package/src/cli/agent-templates/cursor/rules/api-spector.mdc +21 -0
- package/src/cli/agent-templates/windsurf/windsurfrules +19 -0
- package/out/renderer/assets/index-CkHujZU3.css +0 -2
package/out/main/contract.js
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
const promises = require("fs/promises");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const
|
|
5
|
+
const brokerClient = require("./chunks/broker-client-WEymdqAK.js");
|
|
6
|
+
const escape = require("./chunks/escape-BwPM4Tf6.js");
|
|
6
7
|
const crypto = require("crypto");
|
|
7
8
|
const undici = require("undici");
|
|
8
9
|
const os = require("os");
|
|
9
|
-
const cliCommon = require("./chunks/cli-common-
|
|
10
|
+
const cliCommon = require("./chunks/cli-common-vASoDfQH.js");
|
|
10
11
|
const child_process = require("child_process");
|
|
11
12
|
const jsYaml = require("js-yaml");
|
|
12
13
|
const environments = require("./chunks/environments-iM3SUM-4.js");
|
|
13
|
-
require("./chunks/request-exec-
|
|
14
|
+
require("./chunks/request-exec-DAFA6Fap.js");
|
|
14
15
|
require("tls");
|
|
15
|
-
require("./chunks/handle-
|
|
16
|
+
require("./chunks/handle-CL-nWtYA.js");
|
|
16
17
|
require("node:fs");
|
|
17
18
|
require("node:os");
|
|
18
19
|
require("node:path");
|
|
@@ -24,19 +25,17 @@ require("jsonpath-plus");
|
|
|
24
25
|
require("@xmldom/xmldom");
|
|
25
26
|
require("http");
|
|
26
27
|
require("ajv");
|
|
27
|
-
|
|
28
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
29
|
-
}
|
|
28
|
+
require("./chunks/data-files-Br2h3Dt2.js");
|
|
30
29
|
function caseXml(r) {
|
|
31
|
-
const name = escapeXml(`${r.method} ${r.requestName}`);
|
|
30
|
+
const name = escape.escapeXml(`${r.method} ${r.requestName}`);
|
|
32
31
|
const time = ((r.durationMs ?? 0) / 1e3).toFixed(3);
|
|
33
|
-
const classnm = escapeXml(r.url);
|
|
32
|
+
const classnm = escape.escapeXml(r.url);
|
|
34
33
|
if (r.passed) {
|
|
35
34
|
return ` <testcase name="${name}" classname="${classnm}" time="${time}"/>`;
|
|
36
35
|
}
|
|
37
36
|
const messages = r.violations.map((v) => `${v.type}: ${v.message}${v.path ? ` (${v.path})` : ""}`);
|
|
38
|
-
const summary = escapeXml(messages[0] ?? "contract failed");
|
|
39
|
-
const detail = escapeXml(messages.join("\n"));
|
|
37
|
+
const summary = escape.escapeXml(messages[0] ?? "contract failed");
|
|
38
|
+
const detail = escape.escapeXml(messages.join("\n"));
|
|
40
39
|
return [
|
|
41
40
|
` <testcase name="${name}" classname="${classnm}" time="${time}">`,
|
|
42
41
|
` <failure message="${summary}">${detail}</failure>`,
|
|
@@ -48,7 +47,7 @@ function toJUnitXml(report, suiteName = "contract") {
|
|
|
48
47
|
const lines = [
|
|
49
48
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
50
49
|
`<testsuites name="api-spector" tests="${report.total}" failures="${report.failed}" time="${time}">`,
|
|
51
|
-
` <testsuite name="${escapeXml(`${suiteName}:${report.mode}`)}" tests="${report.total}" failures="${report.failed}" time="${time}">`,
|
|
50
|
+
` <testsuite name="${escape.escapeXml(`${suiteName}:${report.mode}`)}" tests="${report.total}" failures="${report.failed}" time="${time}">`,
|
|
52
51
|
...report.results.map(caseXml),
|
|
53
52
|
" </testsuite>",
|
|
54
53
|
"</testsuites>"
|
|
@@ -133,102 +132,6 @@ async function fireWebhooks(hooks, payload, env = process.env, log = console.log
|
|
|
133
132
|
}
|
|
134
133
|
}));
|
|
135
134
|
}
|
|
136
|
-
function brokerConfigFromEnv() {
|
|
137
|
-
return {
|
|
138
|
-
endpoint: (process.env["API_SPECTOR_CLOUD_ENDPOINT"] || "https://api-spector.dev").replace(/\/+$/, ""),
|
|
139
|
-
token: process.env["API_SPECTOR_TOKEN"] || ""
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
async function brokerFetch(cfg, path2, method, body) {
|
|
143
|
-
if (!cfg.token) throw new Error("No API token. Set API_SPECTOR_TOKEN (create one in the cloud dashboard under Tokens).");
|
|
144
|
-
const url = cfg.endpoint + path2;
|
|
145
|
-
let res;
|
|
146
|
-
try {
|
|
147
|
-
res = await undici.fetch(url, {
|
|
148
|
-
method,
|
|
149
|
-
headers: {
|
|
150
|
-
"Content-Type": "application/json",
|
|
151
|
-
Accept: "application/json",
|
|
152
|
-
Authorization: `Bearer ${cfg.token}`
|
|
153
|
-
},
|
|
154
|
-
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
155
|
-
});
|
|
156
|
-
} catch (err) {
|
|
157
|
-
throw new Error(`Could not reach ${url}: ${err.message}`);
|
|
158
|
-
}
|
|
159
|
-
const text = await res.text();
|
|
160
|
-
let json;
|
|
161
|
-
try {
|
|
162
|
-
json = text ? JSON.parse(text) : {};
|
|
163
|
-
} catch {
|
|
164
|
-
json = { raw: text };
|
|
165
|
-
}
|
|
166
|
-
return { status: res.status, ok: res.ok, json };
|
|
167
|
-
}
|
|
168
|
-
function fail(r, what) {
|
|
169
|
-
const detail = r.json?.error || r.json?.message || (r.status === 401 ? "Unauthorized (check API_SPECTOR_TOKEN)" : `HTTP ${r.status}`);
|
|
170
|
-
throw new Error(`${what}: ${detail}`);
|
|
171
|
-
}
|
|
172
|
-
async function publishPact(cfg, input) {
|
|
173
|
-
const r = await brokerFetch(cfg, "/api/contracts", "PUT", {
|
|
174
|
-
consumer: input.consumer,
|
|
175
|
-
consumerVersion: input.consumerVersion,
|
|
176
|
-
provider: input.provider,
|
|
177
|
-
content: input.pact
|
|
178
|
-
});
|
|
179
|
-
if (!r.ok) fail(r, "Publish pact failed");
|
|
180
|
-
}
|
|
181
|
-
async function publishSpec(cfg, input) {
|
|
182
|
-
const r = await brokerFetch(cfg, "/api/provider-contracts", "PUT", {
|
|
183
|
-
pacticipant: input.pacticipant,
|
|
184
|
-
version: input.version,
|
|
185
|
-
spec: input.spec,
|
|
186
|
-
// Optional provider self-verification (its own tests run against this spec).
|
|
187
|
-
...input.results ? { results: input.results } : {}
|
|
188
|
-
});
|
|
189
|
-
if (!r.ok) fail(r, "Publish spec failed");
|
|
190
|
-
}
|
|
191
|
-
async function canIDeploy(cfg, input) {
|
|
192
|
-
const q = new URLSearchParams({ pacticipant: input.pacticipant, version: input.version, environment: input.environment }).toString();
|
|
193
|
-
const r = await brokerFetch(cfg, "/can-i-deploy?" + q, "GET");
|
|
194
|
-
if (r.status !== 200 && r.status !== 409) fail(r, "can-i-deploy failed");
|
|
195
|
-
return { deployable: r.status === 200, reason: r.json?.summary?.reason || "" };
|
|
196
|
-
}
|
|
197
|
-
async function deployPreview(cfg, input) {
|
|
198
|
-
const q = new URLSearchParams({ pacticipant: input.pacticipant, version: input.version, environment: input.environment }).toString();
|
|
199
|
-
const r = await brokerFetch(cfg, "/api/deploy-preview?" + q, "GET");
|
|
200
|
-
if (!r.ok) fail(r, "deploy-preview failed");
|
|
201
|
-
return r.json;
|
|
202
|
-
}
|
|
203
|
-
async function recordDeployment(cfg, input) {
|
|
204
|
-
const path2 = `/pacticipants/${encodeURIComponent(input.pacticipant)}/versions/${encodeURIComponent(input.version)}/deployed-versions/environment/${encodeURIComponent(input.environment)}`;
|
|
205
|
-
const r = await brokerFetch(cfg, path2, "POST", {});
|
|
206
|
-
if (!r.ok) fail(r, "record-deployment failed");
|
|
207
|
-
}
|
|
208
|
-
async function checkCompatibility(cfg, input) {
|
|
209
|
-
const q = new URLSearchParams({ consumer: input.consumer, consumerVersion: input.consumerVersion, provider: input.provider, providerVersion: input.providerVersion }).toString();
|
|
210
|
-
const r = await brokerFetch(cfg, "/api/compatibility?" + q, "GET");
|
|
211
|
-
if (r.status !== 200 && r.status !== 409) fail(r, "Compatibility check failed");
|
|
212
|
-
return { compatible: r.json?.compatible === true, checks: r.json?.checks ?? [] };
|
|
213
|
-
}
|
|
214
|
-
async function fetchContracts(cfg, input = {}) {
|
|
215
|
-
const q = new URLSearchParams();
|
|
216
|
-
if (input.consumer) q.set("consumer", input.consumer);
|
|
217
|
-
if (input.provider) q.set("provider", input.provider);
|
|
218
|
-
const suffix = q.toString() ? "?" + q.toString() : "";
|
|
219
|
-
const r = await brokerFetch(cfg, "/api/contracts" + suffix, "GET");
|
|
220
|
-
if (!r.ok) fail(r, "Fetch contracts failed");
|
|
221
|
-
return r.json?.contracts ?? [];
|
|
222
|
-
}
|
|
223
|
-
async function publishVerification(cfg, input) {
|
|
224
|
-
const r = await brokerFetch(cfg, "/api/verifications", "POST", {
|
|
225
|
-
contractId: input.contractId,
|
|
226
|
-
providerVersion: input.providerVersion,
|
|
227
|
-
success: input.success,
|
|
228
|
-
...input.buildUrl ? { buildUrl: input.buildUrl } : {}
|
|
229
|
-
});
|
|
230
|
-
if (!r.ok) fail(r, "Publish verification failed");
|
|
231
|
-
}
|
|
232
135
|
function resolveVersion(override) {
|
|
233
136
|
if (override) return override;
|
|
234
137
|
const env = process.env["GITHUB_SHA"] || process.env["CI_COMMIT_SHA"] || process.env["GIT_COMMIT"] || process.env["CIRCLE_SHA1"] || process.env["BUILD_SOURCEVERSION"] || process.env["BITBUCKET_COMMIT"];
|
|
@@ -285,8 +188,8 @@ async function cmdList(args) {
|
|
|
285
188
|
process.exit(2);
|
|
286
189
|
}
|
|
287
190
|
const { workspace, dir } = await cliCommon.loadWorkspace(wsArg);
|
|
288
|
-
const snapshots
|
|
289
|
-
if (snapshots
|
|
191
|
+
const snapshots = await brokerClient.listSnapshots(dir, workspace.contracts ?? []);
|
|
192
|
+
if (snapshots.length === 0) {
|
|
290
193
|
console.log(" No contract snapshots. Capture one from the app or via:");
|
|
291
194
|
console.log(" api-spector contract pin --workspace <path> --spec-url <url>");
|
|
292
195
|
return;
|
|
@@ -294,7 +197,7 @@ async function cmdList(args) {
|
|
|
294
197
|
console.log("");
|
|
295
198
|
console.log(" ID Name Version Captured");
|
|
296
199
|
console.log(" ──────── ─────────────────────────────────── ─────────── ──────────────────");
|
|
297
|
-
for (const { snapshot } of snapshots
|
|
200
|
+
for (const { snapshot } of snapshots) {
|
|
298
201
|
const id = snapshot.id.slice(0, 8);
|
|
299
202
|
const name = snapshot.name.slice(0, 35).padEnd(35);
|
|
300
203
|
const version = (snapshot.specVersion ?? "-").slice(0, 11).padEnd(11);
|
|
@@ -319,8 +222,8 @@ async function cmdPin(args) {
|
|
|
319
222
|
}
|
|
320
223
|
const name = typeof args["name"] === "string" ? args["name"] : void 0;
|
|
321
224
|
const { workspace, dir, file } = await cliCommon.loadWorkspace(wsArg);
|
|
322
|
-
const snapshot = await
|
|
323
|
-
const relPath =
|
|
225
|
+
const snapshot = await brokerClient.captureSnapshot(dir, { specUrl, specPath, name });
|
|
226
|
+
const relPath = brokerClient.relPathOf(snapshot);
|
|
324
227
|
if (relPath && !(workspace.contracts ?? []).includes(relPath)) {
|
|
325
228
|
workspace.contracts = [...workspace.contracts ?? [], relPath];
|
|
326
229
|
await promises.writeFile(file, JSON.stringify(workspace, null, 2), "utf8");
|
|
@@ -333,7 +236,7 @@ async function cmdPin(args) {
|
|
|
333
236
|
console.log(` api-spector contract run --workspace ${wsArg} --mode provider --snapshot ${snapshot.id.slice(0, 8)}`);
|
|
334
237
|
}
|
|
335
238
|
async function resolveSnapshot(ws, dir, needle) {
|
|
336
|
-
const all = await
|
|
239
|
+
const all = await brokerClient.listSnapshots(dir, ws.contracts ?? []);
|
|
337
240
|
const matches = all.filter(
|
|
338
241
|
({ snapshot }) => snapshot.id === needle || snapshot.id.startsWith(needle) || snapshot.name === needle
|
|
339
242
|
);
|
|
@@ -366,10 +269,10 @@ async function cmdRun(args) {
|
|
|
366
269
|
const collectionVars = {};
|
|
367
270
|
for (const c of collections) Object.assign(collectionVars, c.collectionVariables ?? {});
|
|
368
271
|
const allRequests = collections.flatMap((c) => Object.values(c.requests));
|
|
369
|
-
const collectionContracts = allRequests.filter((r) =>
|
|
272
|
+
const collectionContracts = allRequests.filter((r) => brokerClient.hasContract(r.contract));
|
|
370
273
|
const keyOf = (r) => `${r.method} ${r.url} ${r.name}`;
|
|
371
274
|
const collectionKeys = new Set(collectionContracts.map(keyOf));
|
|
372
|
-
const designContracts = (await
|
|
275
|
+
const designContracts = (await brokerClient.loadDesignContractRequests(workspace, dir)).filter((r) => !collectionKeys.has(keyOf(r)));
|
|
373
276
|
const contractRequests = [...collectionContracts, ...designContracts];
|
|
374
277
|
if (designContracts.length) {
|
|
375
278
|
console.log(` + ${designContracts.length} design-first interaction(s) from the Contract Designer / pacts/`);
|
|
@@ -408,16 +311,16 @@ async function cmdRun(args) {
|
|
|
408
311
|
const modeValue = mode;
|
|
409
312
|
switch (modeValue) {
|
|
410
313
|
case "consumer":
|
|
411
|
-
report = await
|
|
314
|
+
report = await brokerClient.runConsumerContracts(contractRequests, envVars, collectionVars, providerBaseUrl);
|
|
412
315
|
break;
|
|
413
316
|
case "provider":
|
|
414
|
-
report = await
|
|
317
|
+
report = await brokerClient.runProviderVerification(allRequests, envVars, collectionVars, specUrl, specPath, requestBaseUrl);
|
|
415
318
|
break;
|
|
416
319
|
case "provider-live":
|
|
417
|
-
report = await
|
|
320
|
+
report = await brokerClient.runLiveProviderVerification(contractRequests, envVars, collectionVars, providerBaseUrl, stateHandlerUrl);
|
|
418
321
|
break;
|
|
419
322
|
case "bidirectional":
|
|
420
|
-
report = await
|
|
323
|
+
report = await brokerClient.runBidirectional(contractRequests, envVars, collectionVars, specUrl, specPath, requestBaseUrl);
|
|
421
324
|
break;
|
|
422
325
|
}
|
|
423
326
|
if (args["allow-pending"]) {
|
|
@@ -459,7 +362,7 @@ async function cmdRun(args) {
|
|
|
459
362
|
console.log(` JUnit report written to ${args["junit"]}`);
|
|
460
363
|
}
|
|
461
364
|
if (typeof args["html"] === "string") {
|
|
462
|
-
const html =
|
|
365
|
+
const html = brokerClient.reportToHtml(report, {
|
|
463
366
|
consumer: collections[0]?.name,
|
|
464
367
|
provider: providerBaseUrl,
|
|
465
368
|
spec: snapshotLabel ?? specUrl ?? specPath,
|
|
@@ -475,7 +378,7 @@ async function cmdRun(args) {
|
|
|
475
378
|
process.exit(2);
|
|
476
379
|
}
|
|
477
380
|
const pacticipant = typeof args["pacticipant"] === "string" ? args["pacticipant"] : collections[0]?.name ?? "app";
|
|
478
|
-
const file = await
|
|
381
|
+
const file = await brokerClient.recordResult(dir, pacticipant, appVersion, report, (/* @__PURE__ */ new Date()).toISOString());
|
|
479
382
|
console.log(` Recorded result for ${pacticipant}@${appVersion} → ${file}`);
|
|
480
383
|
}
|
|
481
384
|
process.exit(report.failed === 0 ? 0 : 1);
|
|
@@ -498,7 +401,7 @@ async function cmdCanIDeploy(args) {
|
|
|
498
401
|
}
|
|
499
402
|
const toEnv = typeof args["to"] === "string" ? args["to"] : void 0;
|
|
500
403
|
const { dir } = await cliCommon.loadWorkspace(wsArg);
|
|
501
|
-
const verdict = await
|
|
404
|
+
const verdict = await brokerClient.canIDeploy$1(dir, pacticipant, appVersion, toEnv);
|
|
502
405
|
console.log("");
|
|
503
406
|
console.log(verdict.deployable ? ` ✓ Computer says yes - safe to deploy.` : ` ✗ Computer says no.`);
|
|
504
407
|
console.log(` ${verdict.reason}`);
|
|
@@ -537,8 +440,8 @@ async function cmdRecordDeployment(args) {
|
|
|
537
440
|
process.exit(2);
|
|
538
441
|
}
|
|
539
442
|
const { dir } = await cliCommon.loadWorkspace(wsArg);
|
|
540
|
-
const verdict = await
|
|
541
|
-
const { file, previous } = await
|
|
443
|
+
const verdict = await brokerClient.canIDeploy$1(dir, pacticipant, appVersion);
|
|
444
|
+
const { file, previous } = await brokerClient.recordDeployment$1(dir, env, pacticipant, appVersion, (/* @__PURE__ */ new Date()).toISOString());
|
|
542
445
|
console.log(` Recorded: ${pacticipant}@${appVersion} deployed to ${env}`);
|
|
543
446
|
if (previous) console.log(` Replaces: ${pacticipant}@${previous.version} (deployed ${previous.recordedAt})`);
|
|
544
447
|
console.log(` File: ${file}`);
|
|
@@ -555,7 +458,7 @@ async function cmdEnvironments(args) {
|
|
|
555
458
|
process.exit(2);
|
|
556
459
|
}
|
|
557
460
|
const { dir } = await cliCommon.loadWorkspace(wsArg);
|
|
558
|
-
const envs = await
|
|
461
|
+
const envs = await brokerClient.listEnvironments(dir);
|
|
559
462
|
if (envs.length === 0) {
|
|
560
463
|
console.log(" No deployments recorded. After a deploy, run:");
|
|
561
464
|
console.log(" api-spector contract record-deployment --workspace <path> --pacticipant <name> --app-version <ver> --env <name>");
|
|
@@ -607,7 +510,7 @@ async function cmdFuzz(args) {
|
|
|
607
510
|
console.log(` Fuzzing ${allRequests.length} request(s)${specUrl || specPath ? " against the spec" : " from request bodies"} at the ${level} level...`);
|
|
608
511
|
if (level === "aggressive") console.log(" Aggressive: includes injection probes (SQL/NoSQL/…) and header tampering. Only run against systems you own.");
|
|
609
512
|
if (!includeWrites) console.log(" Write methods (POST/PUT/PATCH/DELETE) are skipped. Add --include-writes to fuzz them (sends malformed writes; target staging or a mock).");
|
|
610
|
-
const report = await
|
|
513
|
+
const report = await brokerClient.runFuzz({
|
|
611
514
|
level,
|
|
612
515
|
requests: allRequests,
|
|
613
516
|
envVars,
|
|
@@ -658,7 +561,7 @@ async function cmdFuzz(args) {
|
|
|
658
561
|
Report written to ${args["output"]}`);
|
|
659
562
|
}
|
|
660
563
|
if (typeof args["html"] === "string") {
|
|
661
|
-
await promises.writeFile(args["html"],
|
|
564
|
+
await promises.writeFile(args["html"], brokerClient.fuzzReportToHtml(report, (/* @__PURE__ */ new Date()).toISOString()), "utf8");
|
|
662
565
|
console.log(` HTML report written to ${args["html"]}`);
|
|
663
566
|
}
|
|
664
567
|
process.exit(report.totalFindings === 0 ? 0 : 1);
|
|
@@ -715,9 +618,9 @@ async function cmdReport(args) {
|
|
|
715
618
|
}
|
|
716
619
|
const { dir } = await cliCommon.loadWorkspace(wsArg);
|
|
717
620
|
const out = typeof args["html"] === "string" ? args["html"] : "contract-dashboard.html";
|
|
718
|
-
const records = await
|
|
719
|
-
const environments2 = await
|
|
720
|
-
await promises.writeFile(out,
|
|
621
|
+
const records = await brokerClient.listResults(dir);
|
|
622
|
+
const environments2 = await brokerClient.listEnvironments(dir);
|
|
623
|
+
await promises.writeFile(out, brokerClient.dashboardToHtml(records, (/* @__PURE__ */ new Date()).toISOString(), { environments: environments2 }), "utf8");
|
|
721
624
|
console.log(` Dashboard with ${records.length} recorded result(s) written to ${out}`);
|
|
722
625
|
}
|
|
723
626
|
async function cmdPactImport(args) {
|
|
@@ -726,10 +629,10 @@ async function cmdPactImport(args) {
|
|
|
726
629
|
console.error(" [error] --file <pact.json> is required");
|
|
727
630
|
process.exit(2);
|
|
728
631
|
}
|
|
729
|
-
const result =
|
|
632
|
+
const result = brokerClient.importPact(await promises.readFile(file, "utf8"));
|
|
730
633
|
console.log(` Imported ${result.requests.length} interaction(s): ${result.consumer} → ${result.provider} (spec ${result.specVersion})`);
|
|
731
634
|
if (typeof args["out"] === "string") {
|
|
732
|
-
const collection =
|
|
635
|
+
const collection = brokerClient.pactToCollection(result);
|
|
733
636
|
await promises.writeFile(args["out"], JSON.stringify(collection, null, 2), "utf8");
|
|
734
637
|
console.log(` Collection written to ${args["out"]}`);
|
|
735
638
|
console.log(` Set the "baseUrl" collection variable, then verify with:`);
|
|
@@ -750,14 +653,14 @@ async function cmdPactExport(args) {
|
|
|
750
653
|
const { workspace, dir } = await cliCommon.loadWorkspace(wsArg);
|
|
751
654
|
const collectionName = typeof args["collection"] === "string" ? args["collection"] : void 0;
|
|
752
655
|
const collections = await cliCommon.loadCollections(workspace, dir, { filterName: collectionName });
|
|
753
|
-
const requests = collections.flatMap((c) => Object.values(c.requests)).filter((r) =>
|
|
656
|
+
const requests = collections.flatMap((c) => Object.values(c.requests)).filter((r) => brokerClient.hasContract(r.contract));
|
|
754
657
|
if (requests.length === 0) {
|
|
755
658
|
console.error(" [error] No requests with contracts found to export.");
|
|
756
659
|
process.exit(2);
|
|
757
660
|
}
|
|
758
661
|
const consumer = typeof args["consumer"] === "string" ? args["consumer"] : collections[0]?.name ?? "consumer";
|
|
759
662
|
const provider = typeof args["provider"] === "string" ? args["provider"] : "provider";
|
|
760
|
-
const pact =
|
|
663
|
+
const pact = brokerClient.exportPact(consumer, provider, requests);
|
|
761
664
|
await promises.writeFile(out, JSON.stringify(pact, null, 2), "utf8");
|
|
762
665
|
console.log(` Exported ${requests.length} interaction(s) to ${out} (${consumer} → ${provider})`);
|
|
763
666
|
}
|
|
@@ -797,14 +700,14 @@ async function cmdPublish(args) {
|
|
|
797
700
|
let all = collections.flatMap((c) => Object.values(c.requests));
|
|
798
701
|
const tag = str(args["tag"]);
|
|
799
702
|
if (tag) all = all.filter((r) => (r.meta?.tags ?? []).includes(tag));
|
|
800
|
-
let requests = all.filter((r) =>
|
|
703
|
+
let requests = all.filter((r) => brokerClient.hasContract(r.contract));
|
|
801
704
|
if (args["derive"]) {
|
|
802
|
-
const derived = all.filter((r) => !
|
|
705
|
+
const derived = all.filter((r) => !brokerClient.hasContract(r.contract)).map((r) => ({ ...r, contract: deriveContract(r) })).filter((r) => brokerClient.hasContract(r.contract));
|
|
803
706
|
requests = [...requests, ...derived];
|
|
804
707
|
}
|
|
805
708
|
if (requests.length === 0) die("No requests with contract expectations found to publish (set them on the Contract tab, or pass --derive to use saved example responses).");
|
|
806
|
-
const pact =
|
|
807
|
-
await publishPact(brokerConfigFromEnv(), { consumer, provider, consumerVersion: version, pact });
|
|
709
|
+
const pact = brokerClient.exportPact(consumer, provider, requests);
|
|
710
|
+
await brokerClient.publishPact(brokerClient.brokerConfigFromEnv(), { consumer, provider, consumerVersion: version, pact });
|
|
808
711
|
console.log(` Published ${requests.length} interaction(s): ${consumer}@${version.slice(0, 7)} → ${provider}`);
|
|
809
712
|
}
|
|
810
713
|
async function cmdPublishSpecLocal(args) {
|
|
@@ -838,7 +741,7 @@ async function cmdPublishSpec(args) {
|
|
|
838
741
|
let results;
|
|
839
742
|
const resultsPath = str(args["results"]);
|
|
840
743
|
if (resultsPath) results = parseSelfVerification(await promises.readFile(resultsPath, "utf8"), resultsPath);
|
|
841
|
-
await publishSpec(brokerConfigFromEnv(), { pacticipant, version, spec, results });
|
|
744
|
+
await brokerClient.publishSpec(brokerClient.brokerConfigFromEnv(), { pacticipant, version, spec, results });
|
|
842
745
|
const selfNote = results ? ` (self-verification: ${results.success ? "passed" : "FAILED"})` : "";
|
|
843
746
|
console.log(` Published spec: ${pacticipant}@${version.slice(0, 7)}${selfNote}`);
|
|
844
747
|
}
|
|
@@ -846,7 +749,7 @@ async function cmdCloudCanIDeploy(args) {
|
|
|
846
749
|
const pacticipant = str(args["pacticipant"]) ?? die("--pacticipant <name> is required");
|
|
847
750
|
const version = resolveVersion(str(args["version"]));
|
|
848
751
|
const environment = str(args["environment"]) ?? str(args["to"]) ?? die("--environment <name> is required");
|
|
849
|
-
const { deployable, reason } = await canIDeploy(brokerConfigFromEnv(), { pacticipant, version, environment });
|
|
752
|
+
const { deployable, reason } = await brokerClient.canIDeploy(brokerClient.brokerConfigFromEnv(), { pacticipant, version, environment });
|
|
850
753
|
if (deployable) {
|
|
851
754
|
console.log(` ✓ ${pacticipant}@${version.slice(0, 7)} can deploy to ${environment}`);
|
|
852
755
|
} else {
|
|
@@ -859,7 +762,7 @@ async function cmdPreview(args) {
|
|
|
859
762
|
const pacticipant = str(args["pacticipant"]) ?? die("--pacticipant <name> is required");
|
|
860
763
|
const version = resolveVersion(str(args["version"]));
|
|
861
764
|
const environment = str(args["environment"]) ?? str(args["to"]) ?? die("--environment <name> is required");
|
|
862
|
-
const preview = await deployPreview(brokerConfigFromEnv(), { pacticipant, version, environment });
|
|
765
|
+
const preview = await brokerClient.deployPreview(brokerClient.brokerConfigFromEnv(), { pacticipant, version, environment });
|
|
863
766
|
console.log(`### ${preview.check.title}
|
|
864
767
|
|
|
865
768
|
${preview.check.summary}`);
|
|
@@ -869,7 +772,7 @@ async function cmdCloudRecordDeployment(args) {
|
|
|
869
772
|
const pacticipant = str(args["pacticipant"]) ?? die("--pacticipant <name> is required");
|
|
870
773
|
const version = resolveVersion(str(args["version"]));
|
|
871
774
|
const environment = str(args["environment"]) ?? str(args["env"]) ?? die("--environment <name> is required");
|
|
872
|
-
await recordDeployment(brokerConfigFromEnv(), { pacticipant, version, environment });
|
|
775
|
+
await brokerClient.recordDeployment(brokerClient.brokerConfigFromEnv(), { pacticipant, version, environment });
|
|
873
776
|
console.log(` Recorded ${pacticipant}@${version.slice(0, 7)} deployed to ${environment}`);
|
|
874
777
|
}
|
|
875
778
|
async function cmdCheck(args) {
|
|
@@ -877,7 +780,7 @@ async function cmdCheck(args) {
|
|
|
877
780
|
const consumerVersion = str(args["consumer-version"]) ?? die("--consumer-version <ver> is required");
|
|
878
781
|
const provider = str(args["provider"]) ?? die("--provider <name> is required");
|
|
879
782
|
const providerVersion = str(args["provider-version"]) ?? die("--provider-version <ver> is required");
|
|
880
|
-
const { compatible, checks } = await checkCompatibility(brokerConfigFromEnv(), { consumer, consumerVersion, provider, providerVersion });
|
|
783
|
+
const { compatible, checks } = await brokerClient.checkCompatibility(brokerClient.brokerConfigFromEnv(), { consumer, consumerVersion, provider, providerVersion });
|
|
881
784
|
if (compatible) {
|
|
882
785
|
console.log(` ✓ ${consumer}@${consumerVersion} is compatible with ${provider}@${providerVersion}`);
|
|
883
786
|
return;
|
|
@@ -899,11 +802,11 @@ async function cmdPublishVerification(args) {
|
|
|
899
802
|
const success = successArg === "true" || successArg === "1";
|
|
900
803
|
const buildUrl = str(args["build-url"]);
|
|
901
804
|
const consumerVersion = str(args["consumer-version"]);
|
|
902
|
-
const cfg = brokerConfigFromEnv();
|
|
903
|
-
const contracts = await fetchContracts(cfg, { consumer, provider });
|
|
805
|
+
const cfg = brokerClient.brokerConfigFromEnv();
|
|
806
|
+
const contracts = await brokerClient.fetchContracts(cfg, { consumer, provider });
|
|
904
807
|
const match = consumerVersion ? contracts.find((c) => c.consumerVersion === consumerVersion) : contracts[contracts.length - 1];
|
|
905
808
|
if (!match) die(`No published contract for ${consumer} -> ${provider}${consumerVersion ? "@" + consumerVersion : ""}.`);
|
|
906
|
-
await publishVerification(cfg, { contractId: match.id, providerVersion, success, buildUrl });
|
|
809
|
+
await brokerClient.publishVerification(cfg, { contractId: match.id, providerVersion, success, buildUrl });
|
|
907
810
|
console.log(` Published verification: ${provider}@${providerVersion.slice(0, 7)} -> ${consumer}@${match.consumerVersion} = ${success ? "passed" : "FAILED"}`);
|
|
908
811
|
}
|
|
909
812
|
async function main() {
|
package/out/main/coverage.js
CHANGED
|
@@ -4,8 +4,9 @@ const promises = require("fs/promises");
|
|
|
4
4
|
const jsYaml = require("js-yaml");
|
|
5
5
|
const undici = require("undici");
|
|
6
6
|
const coverage = require("./chunks/coverage-C54mZEh_.js");
|
|
7
|
-
const cliCommon = require("./chunks/cli-common-
|
|
7
|
+
const cliCommon = require("./chunks/cli-common-vASoDfQH.js");
|
|
8
8
|
require("path");
|
|
9
|
+
require("./chunks/data-files-Br2h3Dt2.js");
|
|
9
10
|
require("crypto");
|
|
10
11
|
async function loadObservations(path) {
|
|
11
12
|
const report = JSON.parse(await promises.readFile(path, "utf8"));
|
|
@@ -5,8 +5,9 @@ const jsYaml = require("js-yaml");
|
|
|
5
5
|
const undici = require("undici");
|
|
6
6
|
const uuid = require("uuid");
|
|
7
7
|
const coverage = require("./chunks/coverage-C54mZEh_.js");
|
|
8
|
-
const cliCommon = require("./chunks/cli-common-
|
|
8
|
+
const cliCommon = require("./chunks/cli-common-vASoDfQH.js");
|
|
9
9
|
require("path");
|
|
10
|
+
require("./chunks/data-files-Br2h3Dt2.js");
|
|
10
11
|
require("crypto");
|
|
11
12
|
const HTTP_METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
|
|
12
13
|
function resolveRef(spec, ref) {
|