autotel-pact 2.0.0 → 4.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/dist/attrs-DbCs1ou0.cjs +61 -0
- package/dist/attrs-DbCs1ou0.cjs.map +1 -0
- package/dist/attrs-ElwK54Ym.js +43 -0
- package/dist/attrs-ElwK54Ym.js.map +1 -0
- package/dist/audit.cjs +155 -394
- package/dist/audit.cjs.map +1 -1
- package/dist/audit.d.cts +15 -13
- package/dist/audit.d.cts.map +1 -0
- package/dist/audit.d.ts +15 -13
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +153 -388
- package/dist/audit.js.map +1 -1
- package/dist/auto-wrap.cjs +206 -240
- package/dist/auto-wrap.cjs.map +1 -1
- package/dist/auto-wrap.d.cts +9 -7
- package/dist/auto-wrap.d.cts.map +1 -0
- package/dist/auto-wrap.d.ts +9 -7
- package/dist/auto-wrap.d.ts.map +1 -0
- package/dist/auto-wrap.js +205 -233
- package/dist/auto-wrap.js.map +1 -1
- package/dist/broker.cjs +65 -68
- package/dist/broker.cjs.map +1 -1
- package/dist/broker.d.cts +11 -9
- package/dist/broker.d.cts.map +1 -0
- package/dist/broker.d.ts +11 -9
- package/dist/broker.d.ts.map +1 -0
- package/dist/broker.js +64 -67
- package/dist/broker.js.map +1 -1
- package/dist/cli.cjs +166 -620
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +3 -2
- package/dist/cli.d.cts.map +1 -0
- package/dist/cli.d.ts +3 -2
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +165 -614
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +245 -952
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -86
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +90 -86
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +216 -918
- package/dist/index.js.map +1 -1
- package/dist/labels-0Cwk5E-8.cjs +41 -0
- package/dist/labels-0Cwk5E-8.cjs.map +1 -0
- package/dist/labels-BQjT_Zrv.js +23 -0
- package/dist/labels-BQjT_Zrv.js.map +1 -0
- package/dist/{ledger-D88TzN1c.d.cts → ledger-BAFsRX6y.d.cts} +8 -6
- package/dist/ledger-BAFsRX6y.d.cts.map +1 -0
- package/dist/ledger-BayQwemz.cjs +267 -0
- package/dist/ledger-BayQwemz.cjs.map +1 -0
- package/dist/{ledger-BuBmfWNc.d.ts → ledger-Bzh_6tbV.d.ts} +8 -6
- package/dist/ledger-Bzh_6tbV.d.ts.map +1 -0
- package/dist/ledger-sII3Ig3Y.js +174 -0
- package/dist/ledger-sII3Ig3Y.js.map +1 -0
- package/dist/pact-file-B8QjVrC7.cjs +76 -0
- package/dist/pact-file-B8QjVrC7.cjs.map +1 -0
- package/dist/pact-file-CkE4NFZ1.js +57 -0
- package/dist/pact-file-CkE4NFZ1.js.map +1 -0
- package/dist/processor.cjs +112 -185
- package/dist/processor.cjs.map +1 -1
- package/dist/processor.d.cts +41 -40
- package/dist/processor.d.cts.map +1 -0
- package/dist/processor.d.ts +41 -40
- package/dist/processor.d.ts.map +1 -0
- package/dist/processor.js +111 -179
- package/dist/processor.js.map +1 -1
- package/dist/provider.cjs +90 -205
- package/dist/provider.cjs.map +1 -1
- package/dist/provider.d.cts +26 -25
- package/dist/provider.d.cts.map +1 -0
- package/dist/provider.d.ts +26 -25
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +88 -199
- package/dist/provider.js.map +1 -1
- package/dist/tag.cjs +14 -44
- package/dist/tag.cjs.map +1 -1
- package/dist/tag.d.cts +4 -2
- package/dist/tag.d.cts.map +1 -0
- package/dist/tag.d.ts +4 -2
- package/dist/tag.d.ts.map +1 -0
- package/dist/tag.js +13 -42
- package/dist/tag.js.map +1 -1
- package/dist/types-D_0kgK2e.d.cts +154 -0
- package/dist/types-D_0kgK2e.d.cts.map +1 -0
- package/dist/types-D_0kgK2e.d.ts +154 -0
- package/dist/types-D_0kgK2e.d.ts.map +1 -0
- package/package.json +6 -6
- package/dist/types-BHGiwqcp.d.cts +0 -157
- package/dist/types-BHGiwqcp.d.ts +0 -157
package/dist/cli.cjs
CHANGED
|
@@ -1,422 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
3
|
+
const require_broker = require('./broker.cjs');
|
|
4
|
+
const require_audit = require('./audit.cjs');
|
|
5
|
+
const require_labels = require('./labels-0Cwk5E-8.cjs');
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
require('fs/promises');
|
|
7
|
-
|
|
8
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
-
|
|
10
|
-
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
11
|
-
|
|
12
|
-
// src/broker.ts
|
|
13
|
-
function authHeaders(config) {
|
|
14
|
-
const headers = {
|
|
15
|
-
Accept: "application/json"
|
|
16
|
-
};
|
|
17
|
-
if (config.token) {
|
|
18
|
-
headers.Authorization = `Bearer ${config.token}`;
|
|
19
|
-
} else if (config.username && config.password) {
|
|
20
|
-
const encoded = Buffer.from(`${config.username}:${config.password}`).toString("base64");
|
|
21
|
-
headers.Authorization = `Basic ${encoded}`;
|
|
22
|
-
}
|
|
23
|
-
return headers;
|
|
24
|
-
}
|
|
25
|
-
function trimBaseUrl(url) {
|
|
26
|
-
return url.replace(/\/$/, "");
|
|
27
|
-
}
|
|
28
|
-
function parseBrokerVerificationResult(consumer, provider, json) {
|
|
29
|
-
if (!json || typeof json !== "object") return null;
|
|
30
|
-
const body = json;
|
|
31
|
-
const success = body.success === true || body.success === void 0 && body.result === "success";
|
|
32
|
-
const verifiedAt = typeof body.verifiedAt === "string" ? body.verifiedAt : typeof body.verified_at === "string" ? body.verified_at : typeof body.createdAt === "string" ? body.createdAt : void 0;
|
|
33
|
-
return {
|
|
34
|
-
consumer,
|
|
35
|
-
provider,
|
|
36
|
-
success: !!success,
|
|
37
|
-
verifiedAt
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
async function fetchBrokerVerifications(config, pairs) {
|
|
41
|
-
const base = trimBaseUrl(config.baseUrl);
|
|
42
|
-
const headers = authHeaders(config);
|
|
43
|
-
const results = [];
|
|
44
|
-
for (const { consumer, provider } of pairs) {
|
|
45
|
-
const url = `${base}/pacts/provider/${encodeURIComponent(provider)}/consumer/${encodeURIComponent(consumer)}/latest/verification-results`;
|
|
46
|
-
try {
|
|
47
|
-
const res = await fetch(url, { headers });
|
|
48
|
-
if (!res.ok) {
|
|
49
|
-
results.push({
|
|
50
|
-
consumer,
|
|
51
|
-
provider,
|
|
52
|
-
success: false,
|
|
53
|
-
error: `HTTP ${res.status} ${res.statusText}`.trim()
|
|
54
|
-
});
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const json = await res.json();
|
|
58
|
-
const parsed = parseBrokerVerificationResult(consumer, provider, json);
|
|
59
|
-
results.push(
|
|
60
|
-
parsed ?? {
|
|
61
|
-
consumer,
|
|
62
|
-
provider,
|
|
63
|
-
success: false,
|
|
64
|
-
error: "Unparseable broker response"
|
|
65
|
-
}
|
|
66
|
-
);
|
|
67
|
-
} catch (error) {
|
|
68
|
-
results.push({
|
|
69
|
-
consumer,
|
|
70
|
-
provider,
|
|
71
|
-
success: false,
|
|
72
|
-
error: error instanceof Error ? error.message : String(error)
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return results;
|
|
77
|
-
}
|
|
78
|
-
function brokerConfigFromEnv() {
|
|
79
|
-
const baseUrl = process.env.PACT_BROKER_BASE_URL;
|
|
80
|
-
if (!baseUrl) return void 0;
|
|
81
|
-
return {
|
|
82
|
-
baseUrl,
|
|
83
|
-
token: process.env.PACT_BROKER_TOKEN,
|
|
84
|
-
username: process.env.PACT_BROKER_USERNAME,
|
|
85
|
-
password: process.env.PACT_BROKER_PASSWORD
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// src/types.ts
|
|
90
|
-
var LEDGER_ENTRY_SPEC = "autotel-pact-ledger-entry/v0.2.0";
|
|
91
|
-
var AUDIT_MATRIX_SPEC = "autotel-pact-audit-matrix/v0.2.0";
|
|
92
|
-
function isInteractionLedgerEntry(entry) {
|
|
93
|
-
return entry.type !== "provider_verification_run";
|
|
94
|
-
}
|
|
95
|
-
function isProviderVerificationRun(entry) {
|
|
96
|
-
return entry.type === "provider_verification_run";
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// src/ledger-normalize.ts
|
|
100
|
-
function isRecord(value) {
|
|
101
|
-
return typeof value === "object" && value !== null;
|
|
102
|
-
}
|
|
103
|
-
function normalizeLedgerRecord(parsed) {
|
|
104
|
-
if (!isRecord(parsed) || parsed.spec !== LEDGER_ENTRY_SPEC) return null;
|
|
105
|
-
const { consumer, provider, observed_at } = parsed;
|
|
106
|
-
if (typeof consumer !== "string" || typeof provider !== "string" || typeof observed_at !== "string") {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
if (parsed.type === "provider_verification_run") {
|
|
110
|
-
if (typeof parsed.error !== "string") return null;
|
|
111
|
-
const entry2 = {
|
|
112
|
-
type: "provider_verification_run",
|
|
113
|
-
spec: LEDGER_ENTRY_SPEC,
|
|
114
|
-
consumer,
|
|
115
|
-
provider,
|
|
116
|
-
outcome: "failed",
|
|
117
|
-
source: parsed.source === "production" ? "production" : "test",
|
|
118
|
-
role: "provider",
|
|
119
|
-
observed_at,
|
|
120
|
-
error: parsed.error
|
|
121
|
-
};
|
|
122
|
-
if (typeof parsed.run_id === "string") entry2.run_id = parsed.run_id;
|
|
123
|
-
if (typeof parsed.git_sha === "string") entry2.git_sha = parsed.git_sha;
|
|
124
|
-
if (typeof parsed.trace_id === "string") entry2.trace_id = parsed.trace_id;
|
|
125
|
-
if (typeof parsed.span_id === "string") entry2.span_id = parsed.span_id;
|
|
126
|
-
return entry2;
|
|
127
|
-
}
|
|
128
|
-
if (typeof parsed.interaction !== "string") return null;
|
|
129
|
-
const states = Array.isArray(parsed.states) ? parsed.states.filter((s) => typeof s === "string") : [];
|
|
130
|
-
const entry = {
|
|
131
|
-
type: "interaction",
|
|
132
|
-
spec: LEDGER_ENTRY_SPEC,
|
|
133
|
-
consumer,
|
|
134
|
-
provider,
|
|
135
|
-
interaction: parsed.interaction,
|
|
136
|
-
states,
|
|
137
|
-
kind: parsed.kind === "http" ? "http" : "message",
|
|
138
|
-
outcome: parsed.outcome === "failed" ? "failed" : "passed",
|
|
139
|
-
source: parsed.source === "production" ? "production" : "test",
|
|
140
|
-
role: parsed.role === "provider" ? "provider" : "consumer",
|
|
141
|
-
duration_ms: typeof parsed.duration_ms === "number" && parsed.duration_ms >= 0 ? parsed.duration_ms : 0,
|
|
142
|
-
observed_at
|
|
143
|
-
};
|
|
144
|
-
if (typeof parsed.interaction_id === "string" && parsed.interaction_id.length > 0) {
|
|
145
|
-
entry.interaction_id = parsed.interaction_id;
|
|
146
|
-
}
|
|
147
|
-
if (typeof parsed.trace_id === "string") entry.trace_id = parsed.trace_id;
|
|
148
|
-
if (typeof parsed.span_id === "string") entry.span_id = parsed.span_id;
|
|
149
|
-
if (typeof parsed.run_id === "string") entry.run_id = parsed.run_id;
|
|
150
|
-
if (typeof parsed.git_sha === "string") entry.git_sha = parsed.git_sha;
|
|
151
|
-
if (typeof parsed.error === "string") entry.error = parsed.error;
|
|
152
|
-
return entry;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// src/ledger.ts
|
|
156
|
-
var DEFAULT_DIR = ".autotel-pact";
|
|
157
|
-
function resolveLedgerDir(opts = {}) {
|
|
158
|
-
return path__default.default.resolve(process.cwd(), opts.dir ?? process.env.AUTOTEL_PACT_LEDGER_DIR ?? DEFAULT_DIR);
|
|
159
|
-
}
|
|
160
|
-
function readLedger(opts = {}) {
|
|
161
|
-
const dir = resolveLedgerDir(opts);
|
|
162
|
-
if (!fs.existsSync(dir)) return [];
|
|
163
|
-
const files = fs.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
|
|
164
|
-
const entries = [];
|
|
165
|
-
for (const file of files) {
|
|
166
|
-
const text = fs.readFileSync(path__default.default.join(dir, file), "utf8");
|
|
167
|
-
for (const line of text.split("\n")) {
|
|
168
|
-
if (!line.trim()) continue;
|
|
169
|
-
try {
|
|
170
|
-
const normalized = normalizeLedgerRecord(JSON.parse(line));
|
|
171
|
-
if (normalized) entries.push(normalized);
|
|
172
|
-
} catch {
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return entries;
|
|
177
|
-
}
|
|
178
|
-
Promise.resolve();
|
|
179
|
-
function listPactFiles(dir) {
|
|
180
|
-
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return [];
|
|
181
|
-
const out = [];
|
|
182
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
183
|
-
const full = path__default.default.join(dir, entry.name);
|
|
184
|
-
if (entry.isDirectory()) {
|
|
185
|
-
out.push(...listPactFiles(full));
|
|
186
|
-
} else if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
187
|
-
out.push(full);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return out;
|
|
191
|
-
}
|
|
192
|
-
function extractInteractionId(metadata) {
|
|
193
|
-
if (!metadata) return void 0;
|
|
194
|
-
const id = metadata.interactionId ?? metadata.interaction_id;
|
|
195
|
-
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
196
|
-
}
|
|
197
|
-
function interactionsFromPactFile(pact) {
|
|
198
|
-
const consumer = pact.consumer?.name;
|
|
199
|
-
const provider = pact.provider?.name;
|
|
200
|
-
if (!consumer || !provider) return [];
|
|
201
|
-
const keys = [];
|
|
202
|
-
for (const m of pact.messages ?? []) {
|
|
203
|
-
keys.push({
|
|
204
|
-
consumer,
|
|
205
|
-
provider,
|
|
206
|
-
interaction: m.description,
|
|
207
|
-
kind: "message",
|
|
208
|
-
interactionId: extractInteractionId(m.metadata)
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
for (const i of pact.interactions ?? []) {
|
|
212
|
-
keys.push({
|
|
213
|
-
consumer,
|
|
214
|
-
provider,
|
|
215
|
-
interaction: i.description,
|
|
216
|
-
kind: "http",
|
|
217
|
-
interactionId: extractInteractionId(i.metadata)
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
return keys;
|
|
221
|
-
}
|
|
222
|
-
function parsePactFile(filePath) {
|
|
223
|
-
try {
|
|
224
|
-
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
225
|
-
} catch {
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// src/audit.ts
|
|
231
|
-
var DEFAULT_PACTS_DIR = "./pacts";
|
|
232
|
-
var DEFAULT_WINDOW_DAYS = 14;
|
|
233
|
-
function keyOf(k) {
|
|
234
|
-
const identity = k.interactionId ?? k.interaction;
|
|
235
|
-
return `${k.consumer}::${k.provider}::${k.kind}::${identity}`;
|
|
236
|
-
}
|
|
237
|
-
function pairKey(consumer, provider) {
|
|
238
|
-
return `${consumer}::${provider}`;
|
|
239
|
-
}
|
|
240
|
-
function inWindow(observedAt, cutoff) {
|
|
241
|
-
const t = Date.parse(observedAt);
|
|
242
|
-
return Number.isFinite(t) && t >= cutoff;
|
|
243
|
-
}
|
|
244
|
-
function computeAuditMatrix(input) {
|
|
245
|
-
const windowDays = input.windowDays ?? DEFAULT_WINDOW_DAYS;
|
|
246
|
-
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
247
|
-
const cutoff = now.getTime() - windowDays * 24 * 60 * 60 * 1e3;
|
|
248
|
-
const verificationFailures = [];
|
|
249
|
-
const recentInteractions = [];
|
|
250
|
-
for (const entry of input.ledger) {
|
|
251
|
-
if (!inWindow(entry.observed_at, cutoff)) continue;
|
|
252
|
-
if (isProviderVerificationRun(entry)) {
|
|
253
|
-
verificationFailures.push(entry);
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
if (isInteractionLedgerEntry(entry)) {
|
|
257
|
-
recentInteractions.push(entry);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
const brokerByPair = /* @__PURE__ */ new Map();
|
|
261
|
-
for (const b of input.brokerVerifications ?? []) {
|
|
262
|
-
brokerByPair.set(pairKey(b.consumer, b.provider), b);
|
|
263
|
-
}
|
|
264
|
-
const testSeenByKey = /* @__PURE__ */ new Map();
|
|
265
|
-
const prodSeenByKey = /* @__PURE__ */ new Map();
|
|
266
|
-
const providerVerifiedByKey = /* @__PURE__ */ new Map();
|
|
267
|
-
const anyObservedByKey = /* @__PURE__ */ new Map();
|
|
268
|
-
for (const entry of recentInteractions) {
|
|
269
|
-
const k = keyOf({
|
|
270
|
-
consumer: entry.consumer,
|
|
271
|
-
provider: entry.provider,
|
|
272
|
-
interaction: entry.interaction,
|
|
273
|
-
kind: entry.kind,
|
|
274
|
-
interactionId: entry.interaction_id
|
|
275
|
-
});
|
|
276
|
-
const push = (map) => {
|
|
277
|
-
const arr = map.get(k) ?? [];
|
|
278
|
-
arr.push(entry);
|
|
279
|
-
map.set(k, arr);
|
|
280
|
-
};
|
|
281
|
-
push(anyObservedByKey);
|
|
282
|
-
if (entry.source === "test" && entry.role === "consumer") {
|
|
283
|
-
push(testSeenByKey);
|
|
284
|
-
}
|
|
285
|
-
if (entry.source === "production") {
|
|
286
|
-
push(prodSeenByKey);
|
|
287
|
-
}
|
|
288
|
-
if (entry.role === "provider" && entry.outcome === "passed") {
|
|
289
|
-
push(providerVerifiedByKey);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
const contractedByKey = /* @__PURE__ */ new Map();
|
|
293
|
-
for (const c of input.contracted) {
|
|
294
|
-
contractedByKey.set(keyOf(c), c);
|
|
295
|
-
}
|
|
296
|
-
const rows = [];
|
|
297
|
-
function pushRow(parts, k, isContracted) {
|
|
298
|
-
const testObs = testSeenByKey.get(k) ?? [];
|
|
299
|
-
const prodObs = prodSeenByKey.get(k) ?? [];
|
|
300
|
-
const providerObs = providerVerifiedByKey.get(k) ?? [];
|
|
301
|
-
const allObs = anyObservedByKey.get(k) ?? [];
|
|
302
|
-
const latest = allObs.toSorted(
|
|
303
|
-
(a, b) => b.observed_at.localeCompare(a.observed_at)
|
|
304
|
-
)[0];
|
|
305
|
-
const broker = brokerByPair.get(pairKey(parts.consumer, parts.provider));
|
|
306
|
-
rows.push({
|
|
307
|
-
consumer: parts.consumer,
|
|
308
|
-
provider: parts.provider,
|
|
309
|
-
interaction: parts.interaction,
|
|
310
|
-
interaction_id: parts.interactionId ?? latest?.interaction_id,
|
|
311
|
-
kind: parts.kind,
|
|
312
|
-
contracted: isContracted,
|
|
313
|
-
observed: testObs.length > 0 || prodObs.length > 0,
|
|
314
|
-
test_seen: testObs.length > 0,
|
|
315
|
-
prod_seen: prodObs.length > 0,
|
|
316
|
-
provider_verified: providerObs.length > 0,
|
|
317
|
-
broker_verified: broker?.success === true,
|
|
318
|
-
broker_verified_at: broker?.verifiedAt,
|
|
319
|
-
broker_error: broker?.error,
|
|
320
|
-
last_observed_at: latest?.observed_at,
|
|
321
|
-
last_outcome: latest?.outcome
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
for (const [k, contracted] of contractedByKey) {
|
|
325
|
-
pushRow(contracted, k, true);
|
|
326
|
-
}
|
|
327
|
-
for (const [k, observations] of anyObservedByKey) {
|
|
328
|
-
if (contractedByKey.has(k)) continue;
|
|
329
|
-
const first = observations[0];
|
|
330
|
-
pushRow(
|
|
331
|
-
{
|
|
332
|
-
consumer: first.consumer,
|
|
333
|
-
provider: first.provider,
|
|
334
|
-
interaction: first.interaction,
|
|
335
|
-
kind: first.kind,
|
|
336
|
-
interactionId: first.interaction_id
|
|
337
|
-
},
|
|
338
|
-
k,
|
|
339
|
-
false
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
rows.sort(
|
|
343
|
-
(a, b) => a.consumer.localeCompare(b.consumer) || a.provider.localeCompare(b.provider) || a.interaction.localeCompare(b.interaction)
|
|
344
|
-
);
|
|
345
|
-
const counts = {
|
|
346
|
-
total: rows.length,
|
|
347
|
-
contracted: rows.filter((r) => r.contracted).length,
|
|
348
|
-
observed: rows.filter((r) => r.observed).length,
|
|
349
|
-
contracted_and_test_seen: rows.filter((r) => r.contracted && r.test_seen).length,
|
|
350
|
-
contracted_not_test_seen: rows.filter((r) => r.contracted && !r.test_seen).length,
|
|
351
|
-
test_or_prod_seen_not_contracted: rows.filter(
|
|
352
|
-
(r) => !r.contracted && (r.test_seen || r.prod_seen)
|
|
353
|
-
).length,
|
|
354
|
-
test_seen: rows.filter((r) => r.test_seen).length,
|
|
355
|
-
prod_seen: rows.filter((r) => r.prod_seen).length,
|
|
356
|
-
provider_verified: rows.filter((r) => r.provider_verified).length,
|
|
357
|
-
broker_verified: rows.filter((r) => r.broker_verified).length
|
|
358
|
-
};
|
|
359
|
-
const matrix = {
|
|
360
|
-
spec: AUDIT_MATRIX_SPEC,
|
|
361
|
-
rows,
|
|
362
|
-
counts,
|
|
363
|
-
window_days: windowDays,
|
|
364
|
-
generated_at: now.toISOString()
|
|
365
|
-
};
|
|
366
|
-
if (verificationFailures.length > 0) {
|
|
367
|
-
matrix.verification_failures = verificationFailures;
|
|
368
|
-
}
|
|
369
|
-
return matrix;
|
|
370
|
-
}
|
|
371
|
-
async function runAudit(opts = {}) {
|
|
372
|
-
const pactsDir = path__default.default.resolve(process.cwd(), opts.pactsDir ?? DEFAULT_PACTS_DIR);
|
|
373
|
-
const contracted = [];
|
|
374
|
-
const pairs = /* @__PURE__ */ new Set();
|
|
375
|
-
for (const file of listPactFiles(pactsDir)) {
|
|
376
|
-
const pact = parsePactFile(file);
|
|
377
|
-
if (!pact) continue;
|
|
378
|
-
const interactions = interactionsFromPactFile(pact);
|
|
379
|
-
contracted.push(...interactions);
|
|
380
|
-
const consumer = pact.consumer?.name;
|
|
381
|
-
const provider = pact.provider?.name;
|
|
382
|
-
if (consumer && provider) {
|
|
383
|
-
pairs.add(pairKey(consumer, provider));
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
const ledger = readLedger(opts);
|
|
387
|
-
let brokerVerifications;
|
|
388
|
-
if (opts.broker) {
|
|
389
|
-
brokerVerifications = await fetchBrokerVerifications(opts.broker, [...pairs].map((p) => {
|
|
390
|
-
const [consumer, provider] = p.split("::");
|
|
391
|
-
return { consumer, provider };
|
|
392
|
-
}));
|
|
393
|
-
}
|
|
394
|
-
return computeAuditMatrix({
|
|
395
|
-
contracted,
|
|
396
|
-
ledger,
|
|
397
|
-
brokerVerifications,
|
|
398
|
-
windowDays: opts.windowDays
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// src/labels.ts
|
|
403
|
-
var CLI_COLUMNS = {
|
|
404
|
-
STATUS: "STATUS",
|
|
405
|
-
CONTRACTED: "CONTRACTED",
|
|
406
|
-
TEST_SEEN: "TEST_SEEN",
|
|
407
|
-
PROD_SEEN: "PROD_SEEN",
|
|
408
|
-
PROVIDER_VERIFIED: "PROVIDER_VERIFIED",
|
|
409
|
-
BROKER_VERIFIED: "BROKER_VERIFIED",
|
|
410
|
-
PAIR: "CONSUMER \u2192 PROVIDER",
|
|
411
|
-
KIND: "KIND",
|
|
412
|
-
INTERACTION: "INTERACTION"
|
|
413
|
-
};
|
|
414
|
-
function formatYesNo(value) {
|
|
415
|
-
return value ? "yes" : "no";
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// src/cli.ts
|
|
419
|
-
var HELP_TEXT = `autotel-pact audit \u2014 runtime evidence for Pact contracts
|
|
7
|
+
//#region src/cli.ts
|
|
8
|
+
const HELP_TEXT = `autotel-pact audit — runtime evidence for Pact contracts
|
|
420
9
|
|
|
421
10
|
USAGE
|
|
422
11
|
autotel-pact audit [options]
|
|
@@ -442,221 +31,178 @@ ENVIRONMENT
|
|
|
442
31
|
PACT_BROKER_PASSWORD Broker basic auth password
|
|
443
32
|
`;
|
|
444
33
|
function parseDuration(value) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
34
|
+
const m = /^(\d+)(d?)$/.exec(value);
|
|
35
|
+
if (!m) throw new Error(`Invalid --window value: ${value}`);
|
|
36
|
+
return Number.parseInt(m[1], 10);
|
|
448
37
|
}
|
|
449
38
|
function requireValue(argv, i, flag) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
return value;
|
|
39
|
+
const value = argv[i];
|
|
40
|
+
if (value === void 0 || value.startsWith("--")) throw new Error(`Missing value for ${flag}`);
|
|
41
|
+
return value;
|
|
455
42
|
}
|
|
456
43
|
function parseArgs(argv) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if (!value) throw new Error("Missing value for --broker-token");
|
|
522
|
-
args.brokerToken = value;
|
|
523
|
-
} else if (a === "audit") continue;
|
|
524
|
-
else throw new Error(`Unknown argument: ${a}`);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return args;
|
|
44
|
+
const args = {
|
|
45
|
+
gate: "off",
|
|
46
|
+
json: false,
|
|
47
|
+
help: false
|
|
48
|
+
};
|
|
49
|
+
for (let i = 0; i < argv.length; i++) {
|
|
50
|
+
const a = argv[i];
|
|
51
|
+
switch (a) {
|
|
52
|
+
case "--help":
|
|
53
|
+
case "-h":
|
|
54
|
+
args.help = true;
|
|
55
|
+
break;
|
|
56
|
+
case "--json":
|
|
57
|
+
args.json = true;
|
|
58
|
+
break;
|
|
59
|
+
case "--gate":
|
|
60
|
+
args.gate = "on";
|
|
61
|
+
break;
|
|
62
|
+
case "--gate=strict":
|
|
63
|
+
args.gate = "strict";
|
|
64
|
+
break;
|
|
65
|
+
case "--gate=broker":
|
|
66
|
+
args.gate = "broker";
|
|
67
|
+
break;
|
|
68
|
+
case "--pacts":
|
|
69
|
+
args.pactsDir = requireValue(argv, ++i, "--pacts");
|
|
70
|
+
break;
|
|
71
|
+
case "--ledger":
|
|
72
|
+
args.ledgerDir = requireValue(argv, ++i, "--ledger");
|
|
73
|
+
break;
|
|
74
|
+
case "--window":
|
|
75
|
+
args.windowDays = parseDuration(requireValue(argv, ++i, "--window"));
|
|
76
|
+
break;
|
|
77
|
+
case "--broker-url":
|
|
78
|
+
args.brokerUrl = requireValue(argv, ++i, "--broker-url");
|
|
79
|
+
break;
|
|
80
|
+
case "--broker-token":
|
|
81
|
+
args.brokerToken = requireValue(argv, ++i, "--broker-token");
|
|
82
|
+
break;
|
|
83
|
+
default: if (a.startsWith("--pacts=")) {
|
|
84
|
+
const value = a.slice(8);
|
|
85
|
+
if (!value) throw new Error("Missing value for --pacts");
|
|
86
|
+
args.pactsDir = value;
|
|
87
|
+
} else if (a.startsWith("--ledger=")) {
|
|
88
|
+
const value = a.slice(9);
|
|
89
|
+
if (!value) throw new Error("Missing value for --ledger");
|
|
90
|
+
args.ledgerDir = value;
|
|
91
|
+
} else if (a.startsWith("--window=")) {
|
|
92
|
+
const value = a.slice(9);
|
|
93
|
+
if (!value) throw new Error("Missing value for --window");
|
|
94
|
+
args.windowDays = parseDuration(value);
|
|
95
|
+
} else if (a.startsWith("--broker-url=")) {
|
|
96
|
+
const value = a.slice(13);
|
|
97
|
+
if (!value) throw new Error("Missing value for --broker-url");
|
|
98
|
+
args.brokerUrl = value;
|
|
99
|
+
} else if (a.startsWith("--broker-token=")) {
|
|
100
|
+
const value = a.slice(15);
|
|
101
|
+
if (!value) throw new Error("Missing value for --broker-token");
|
|
102
|
+
args.brokerToken = value;
|
|
103
|
+
} else if (a === "audit") continue;
|
|
104
|
+
else throw new Error(`Unknown argument: ${a}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return args;
|
|
529
108
|
}
|
|
530
109
|
function statusLabel(r) {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
110
|
+
if (r.contracted && r.test_seen) return "OK";
|
|
111
|
+
if (r.contracted && !r.test_seen) return "STALE";
|
|
112
|
+
return "SHADOW";
|
|
534
113
|
}
|
|
535
114
|
function formatTable(matrix) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
);
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
` Provider verified (interaction): ${matrix.counts.provider_verified}`,
|
|
582
|
-
` Broker verified (pact-pair): ${matrix.counts.broker_verified}`,
|
|
583
|
-
` Contracted AND seen in test: ${matrix.counts.contracted_and_test_seen}`,
|
|
584
|
-
` Contracted, NOT seen in test: ${matrix.counts.contracted_not_test_seen} \u2190 stale confidence`,
|
|
585
|
-
` Seen, NOT contracted: ${matrix.counts.test_or_prod_seen_not_contracted} \u2190 ungoverned flow`
|
|
586
|
-
);
|
|
587
|
-
if (matrix.verification_failures && matrix.verification_failures.length > 0) {
|
|
588
|
-
lines.push("", "Provider verification failures (run-level):");
|
|
589
|
-
for (const f of matrix.verification_failures) {
|
|
590
|
-
lines.push(` ${f.consumer} \u2192 ${f.provider}: ${f.error}`);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
const brokerErrors = /* @__PURE__ */ new Map();
|
|
594
|
-
for (const r of matrix.rows) {
|
|
595
|
-
if (r.broker_error) {
|
|
596
|
-
brokerErrors.set(`${r.consumer} \u2192 ${r.provider}`, r.broker_error);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
if (brokerErrors.size > 0) {
|
|
600
|
-
lines.push("", "Broker unreachable / errored (verification status unknown):");
|
|
601
|
-
for (const [pair, err] of brokerErrors) {
|
|
602
|
-
lines.push(` ${pair}: ${err}`);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
return lines.join("\n");
|
|
115
|
+
const lines = [
|
|
116
|
+
`Window: last ${matrix.window_days} day(s)`,
|
|
117
|
+
`Generated: ${matrix.generated_at}`,
|
|
118
|
+
""
|
|
119
|
+
];
|
|
120
|
+
if (matrix.rows.length === 0) {
|
|
121
|
+
lines.push("No contracts or observations found.");
|
|
122
|
+
return lines.join("\n");
|
|
123
|
+
}
|
|
124
|
+
const header = [
|
|
125
|
+
require_labels.CLI_COLUMNS.STATUS,
|
|
126
|
+
require_labels.CLI_COLUMNS.CONTRACTED,
|
|
127
|
+
require_labels.CLI_COLUMNS.TEST_SEEN,
|
|
128
|
+
require_labels.CLI_COLUMNS.PROD_SEEN,
|
|
129
|
+
require_labels.CLI_COLUMNS.PROVIDER_VERIFIED,
|
|
130
|
+
require_labels.CLI_COLUMNS.BROKER_VERIFIED,
|
|
131
|
+
require_labels.CLI_COLUMNS.PAIR,
|
|
132
|
+
require_labels.CLI_COLUMNS.KIND,
|
|
133
|
+
require_labels.CLI_COLUMNS.INTERACTION
|
|
134
|
+
];
|
|
135
|
+
const rows = matrix.rows.map((r) => [
|
|
136
|
+
statusLabel(r),
|
|
137
|
+
require_labels.formatYesNo(r.contracted),
|
|
138
|
+
require_labels.formatYesNo(r.test_seen),
|
|
139
|
+
require_labels.formatYesNo(r.prod_seen),
|
|
140
|
+
require_labels.formatYesNo(r.provider_verified),
|
|
141
|
+
require_labels.formatYesNo(r.broker_verified),
|
|
142
|
+
`${r.consumer} → ${r.provider}`,
|
|
143
|
+
r.kind,
|
|
144
|
+
r.interaction
|
|
145
|
+
]);
|
|
146
|
+
const widths = header.map((h, i) => Math.max(h.length, ...rows.map((row) => row[i].length)));
|
|
147
|
+
const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
148
|
+
lines.push(fmt(header), widths.map((w) => "─".repeat(w)).join(" "), ...rows.map((r) => fmt(r)), "", "Summary", ` Total interactions: ${matrix.counts.total}`, ` Contracted: ${matrix.counts.contracted}`, ` Seen in test: ${matrix.counts.test_seen}`, ` Seen in production: ${matrix.counts.prod_seen}`, ` Provider verified (interaction): ${matrix.counts.provider_verified}`, ` Broker verified (pact-pair): ${matrix.counts.broker_verified}`, ` Contracted AND seen in test: ${matrix.counts.contracted_and_test_seen}`, ` Contracted, NOT seen in test: ${matrix.counts.contracted_not_test_seen} ← stale confidence`, ` Seen, NOT contracted: ${matrix.counts.test_or_prod_seen_not_contracted} ← ungoverned flow`);
|
|
149
|
+
if (matrix.verification_failures && matrix.verification_failures.length > 0) {
|
|
150
|
+
lines.push("", "Provider verification failures (run-level):");
|
|
151
|
+
for (const f of matrix.verification_failures) lines.push(` ${f.consumer} → ${f.provider}: ${f.error}`);
|
|
152
|
+
}
|
|
153
|
+
const brokerErrors = /* @__PURE__ */ new Map();
|
|
154
|
+
for (const r of matrix.rows) if (r.broker_error) brokerErrors.set(`${r.consumer} → ${r.provider}`, r.broker_error);
|
|
155
|
+
if (brokerErrors.size > 0) {
|
|
156
|
+
lines.push("", "Broker unreachable / errored (verification status unknown):");
|
|
157
|
+
for (const [pair, err] of brokerErrors) lines.push(` ${pair}: ${err}`);
|
|
158
|
+
}
|
|
159
|
+
return lines.join("\n");
|
|
606
160
|
}
|
|
607
161
|
function shouldFail(matrix, gate) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
162
|
+
if (gate === "off") return false;
|
|
163
|
+
if (matrix.counts.contracted_not_test_seen > 0) return true;
|
|
164
|
+
if (gate === "strict" && matrix.counts.test_or_prod_seen_not_contracted > 0) return true;
|
|
165
|
+
if (gate === "broker") {
|
|
166
|
+
const contractedRows = matrix.rows.filter((r) => r.contracted);
|
|
167
|
+
if (contractedRows.some((r) => r.broker_error)) return true;
|
|
168
|
+
if (contractedRows.some((r) => !r.broker_verified)) return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
617
171
|
}
|
|
618
172
|
async function main(argv) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
}
|
|
652
|
-
var isDirectInvocation = typeof process !== "undefined" && process.argv[1] && (process.argv[1].endsWith("cli.js") || process.argv[1].endsWith("autotel-pact"));
|
|
653
|
-
if (isDirectInvocation) {
|
|
654
|
-
main(process.argv.slice(2)).then((code) => process.exit(code)).catch((error) => {
|
|
655
|
-
console.error(error);
|
|
656
|
-
process.exit(1);
|
|
657
|
-
});
|
|
658
|
-
}
|
|
173
|
+
let args;
|
|
174
|
+
try {
|
|
175
|
+
args = parseArgs(argv);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
process.stderr.write(`${error.message}\n\n${HELP_TEXT}`);
|
|
178
|
+
return 2;
|
|
179
|
+
}
|
|
180
|
+
if (args.help) {
|
|
181
|
+
process.stdout.write(HELP_TEXT);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
const envBroker = require_broker.brokerConfigFromEnv();
|
|
185
|
+
const broker = args.brokerUrl || envBroker ? {
|
|
186
|
+
baseUrl: args.brokerUrl ?? envBroker.baseUrl,
|
|
187
|
+
token: args.brokerToken ?? envBroker?.token,
|
|
188
|
+
username: envBroker?.username,
|
|
189
|
+
password: envBroker?.password
|
|
190
|
+
} : void 0;
|
|
191
|
+
const matrix = await require_audit.runAudit({
|
|
192
|
+
pactsDir: args.pactsDir,
|
|
193
|
+
dir: args.ledgerDir,
|
|
194
|
+
windowDays: args.windowDays,
|
|
195
|
+
broker
|
|
196
|
+
});
|
|
197
|
+
if (args.json) process.stdout.write(JSON.stringify(matrix, null, 2) + "\n");
|
|
198
|
+
else process.stdout.write(formatTable(matrix) + "\n");
|
|
199
|
+
return shouldFail(matrix, args.gate) ? 1 : 0;
|
|
200
|
+
}
|
|
201
|
+
if (typeof process !== "undefined" && process.argv[1] && (process.argv[1].endsWith("cli.js") || process.argv[1].endsWith("autotel-pact"))) main(process.argv.slice(2)).then((code) => process.exit(code)).catch((error) => {
|
|
202
|
+
console.error(error);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
});
|
|
659
205
|
|
|
206
|
+
//#endregion
|
|
660
207
|
exports.main = main;
|
|
661
|
-
//# sourceMappingURL=cli.cjs.map
|
|
662
208
|
//# sourceMappingURL=cli.cjs.map
|