autotel-pact 0.2.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 +385 -0
- package/dist/audit.cjs +412 -0
- package/dist/audit.cjs.map +1 -0
- package/dist/audit.d.cts +25 -0
- package/dist/audit.d.ts +25 -0
- package/dist/audit.js +403 -0
- package/dist/audit.js.map +1 -0
- package/dist/auto-wrap.cjs +255 -0
- package/dist/auto-wrap.cjs.map +1 -0
- package/dist/auto-wrap.d.cts +57 -0
- package/dist/auto-wrap.d.ts +57 -0
- package/dist/auto-wrap.js +248 -0
- package/dist/auto-wrap.js.map +1 -0
- package/dist/broker.cjs +84 -0
- package/dist/broker.cjs.map +1 -0
- package/dist/broker.d.cts +23 -0
- package/dist/broker.d.ts +23 -0
- package/dist/broker.js +80 -0
- package/dist/broker.js.map +1 -0
- package/dist/cli.cjs +662 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +4 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +656 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +967 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +200 -0
- package/dist/index.d.ts +200 -0
- package/dist/index.js +932 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger-BuBmfWNc.d.ts +22 -0
- package/dist/ledger-D88TzN1c.d.cts +22 -0
- package/dist/processor.cjs +200 -0
- package/dist/processor.cjs.map +1 -0
- package/dist/processor.d.cts +58 -0
- package/dist/processor.d.ts +58 -0
- package/dist/processor.js +193 -0
- package/dist/processor.js.map +1 -0
- package/dist/provider.cjs +219 -0
- package/dist/provider.cjs.map +1 -0
- package/dist/provider.d.cts +41 -0
- package/dist/provider.d.ts +41 -0
- package/dist/provider.js +213 -0
- package/dist/provider.js.map +1 -0
- package/dist/tag.cjs +50 -0
- package/dist/tag.cjs.map +1 -0
- package/dist/tag.d.cts +9 -0
- package/dist/tag.d.ts +9 -0
- package/dist/tag.js +48 -0
- package/dist/tag.js.map +1 -0
- package/dist/types-BHGiwqcp.d.cts +157 -0
- package/dist/types-BHGiwqcp.d.ts +157 -0
- package/package.json +108 -0
- package/schemas/README.md +24 -0
- package/schemas/audit-matrix-v0.2.0.json +78 -0
- package/schemas/ledger-entry-v0.2.0.json +77 -0
- package/src/attrs.test.ts +35 -0
- package/src/attrs.ts +53 -0
- package/src/audit.test.ts +189 -0
- package/src/audit.ts +251 -0
- package/src/auto-wrap.test.ts +149 -0
- package/src/auto-wrap.ts +283 -0
- package/src/broker.test.ts +175 -0
- package/src/broker.ts +118 -0
- package/src/cli.test.ts +148 -0
- package/src/cli.ts +287 -0
- package/src/index.ts +94 -0
- package/src/labels.ts +25 -0
- package/src/ledger-normalize.test.ts +141 -0
- package/src/ledger-normalize.ts +82 -0
- package/src/ledger.test.ts +92 -0
- package/src/ledger.ts +156 -0
- package/src/pact-file.test.ts +124 -0
- package/src/pact-file.ts +65 -0
- package/src/processor.test.ts +90 -0
- package/src/processor.ts +191 -0
- package/src/tag.test.ts +72 -0
- package/src/tag.ts +21 -0
- package/src/types.ts +169 -0
- package/src/wrapper-http.test.ts +133 -0
- package/src/wrapper-http.ts +194 -0
- package/src/wrapper-provider.test.ts +132 -0
- package/src/wrapper-provider.ts +163 -0
- package/src/wrapper.test.ts +176 -0
- package/src/wrapper.ts +221 -0
package/dist/provider.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { span, getActiveSpan } from 'autotel';
|
|
3
|
+
import { readFileSync, mkdirSync, appendFileSync } from 'fs';
|
|
4
|
+
import 'fs/promises';
|
|
5
|
+
|
|
6
|
+
// src/wrapper-provider.ts
|
|
7
|
+
|
|
8
|
+
// src/types.ts
|
|
9
|
+
var LEDGER_ENTRY_SPEC = "autotel-pact-ledger-entry/v0.2.0";
|
|
10
|
+
|
|
11
|
+
// src/ledger.ts
|
|
12
|
+
var DEFAULT_DIR = ".autotel-pact";
|
|
13
|
+
function resolveLedgerDir(opts = {}) {
|
|
14
|
+
return path.resolve(process.cwd(), opts.dir ?? process.env.AUTOTEL_PACT_LEDGER_DIR ?? DEFAULT_DIR);
|
|
15
|
+
}
|
|
16
|
+
function resolveRunId(opts = {}) {
|
|
17
|
+
const explicit = opts.runId ?? process.env.AUTOTEL_PACT_RUN_ID;
|
|
18
|
+
if (explicit) return explicit;
|
|
19
|
+
return `local-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-")}`;
|
|
20
|
+
}
|
|
21
|
+
function ledgerPath(opts = {}) {
|
|
22
|
+
const dir = resolveLedgerDir(opts);
|
|
23
|
+
return path.join(dir, `ledger-${resolveRunId(opts)}.jsonl`);
|
|
24
|
+
}
|
|
25
|
+
function writeLine(filePath, entry) {
|
|
26
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
27
|
+
appendFileSync(filePath, JSON.stringify(entry) + "\n", "utf8");
|
|
28
|
+
}
|
|
29
|
+
function appendLedgerEntry(entry, opts = {}) {
|
|
30
|
+
const filePath = ledgerPath(opts);
|
|
31
|
+
const normalized = entry.type === "provider_verification_run" ? entry : { ...entry, spec: LEDGER_ENTRY_SPEC, type: "interaction" };
|
|
32
|
+
writeLine(filePath, normalized);
|
|
33
|
+
}
|
|
34
|
+
function appendProviderVerificationFailure(entry, opts = {}) {
|
|
35
|
+
appendLedgerEntry(
|
|
36
|
+
{
|
|
37
|
+
type: "provider_verification_run",
|
|
38
|
+
spec: LEDGER_ENTRY_SPEC,
|
|
39
|
+
outcome: "failed",
|
|
40
|
+
role: "provider",
|
|
41
|
+
source: entry.source ?? "test",
|
|
42
|
+
consumer: entry.consumer,
|
|
43
|
+
provider: entry.provider,
|
|
44
|
+
observed_at: entry.observed_at,
|
|
45
|
+
error: entry.error,
|
|
46
|
+
run_id: entry.run_id,
|
|
47
|
+
git_sha: entry.git_sha,
|
|
48
|
+
trace_id: entry.trace_id,
|
|
49
|
+
span_id: entry.span_id
|
|
50
|
+
},
|
|
51
|
+
opts
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
Promise.resolve();
|
|
55
|
+
function extractInteractionId(metadata) {
|
|
56
|
+
if (!metadata) return void 0;
|
|
57
|
+
const id = metadata.interactionId ?? metadata.interaction_id;
|
|
58
|
+
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
59
|
+
}
|
|
60
|
+
function interactionsFromPactFile(pact) {
|
|
61
|
+
const consumer = pact.consumer?.name;
|
|
62
|
+
const provider = pact.provider?.name;
|
|
63
|
+
if (!consumer || !provider) return [];
|
|
64
|
+
const keys = [];
|
|
65
|
+
for (const m of pact.messages ?? []) {
|
|
66
|
+
keys.push({
|
|
67
|
+
consumer,
|
|
68
|
+
provider,
|
|
69
|
+
interaction: m.description,
|
|
70
|
+
kind: "message",
|
|
71
|
+
interactionId: extractInteractionId(m.metadata)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
for (const i of pact.interactions ?? []) {
|
|
75
|
+
keys.push({
|
|
76
|
+
consumer,
|
|
77
|
+
provider,
|
|
78
|
+
interaction: i.description,
|
|
79
|
+
kind: "http",
|
|
80
|
+
interactionId: extractInteractionId(i.metadata)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return keys;
|
|
84
|
+
}
|
|
85
|
+
function parsePactFile(filePath) {
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/attrs.ts
|
|
94
|
+
var PACT_ATTRS = {
|
|
95
|
+
CONSUMER: "pact.consumer",
|
|
96
|
+
PROVIDER: "pact.provider",
|
|
97
|
+
KIND: "pact.kind",
|
|
98
|
+
INTERACTION_DESCRIPTION: "pact.interaction.description",
|
|
99
|
+
INTERACTION_ID: "pact.interaction.id",
|
|
100
|
+
INTERACTION_STATES: "pact.interaction.states",
|
|
101
|
+
CONTRACT_FILE: "pact.contract.file",
|
|
102
|
+
OUTCOME: "pact.outcome"
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// src/wrapper-provider.ts
|
|
106
|
+
function resolvePactPaths(opts) {
|
|
107
|
+
const urls = opts.pactUrls ?? [];
|
|
108
|
+
return urls.map((u) => path.resolve(process.cwd(), u));
|
|
109
|
+
}
|
|
110
|
+
function inferConsumerFromPacts(pactPaths, fallback) {
|
|
111
|
+
for (const filePath of pactPaths) {
|
|
112
|
+
const pact = parsePactFile(filePath);
|
|
113
|
+
if (pact?.consumer?.name) return pact.consumer.name;
|
|
114
|
+
}
|
|
115
|
+
if (fallback) return fallback;
|
|
116
|
+
throw new Error(
|
|
117
|
+
"autotel-pact: could not infer consumer from pact files. Pass `consumer` in options."
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
function kindForPactFile(filePath) {
|
|
121
|
+
const pact = parsePactFile(filePath);
|
|
122
|
+
if (!pact) return "message";
|
|
123
|
+
if ((pact.interactions?.length ?? 0) > 0) return "http";
|
|
124
|
+
return "message";
|
|
125
|
+
}
|
|
126
|
+
async function loadVerifier(VerifierClass) {
|
|
127
|
+
if (VerifierClass) return VerifierClass;
|
|
128
|
+
const mod = await import('@pact-foundation/pact');
|
|
129
|
+
const Verifier = mod.Verifier;
|
|
130
|
+
if (!Verifier) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
"autotel-pact: @pact-foundation/pact Verifier not found. Install the peer dependency."
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return Verifier;
|
|
136
|
+
}
|
|
137
|
+
async function withProviderVerification(verifierOpts, wrapOpts = {}) {
|
|
138
|
+
const pactPaths = resolvePactPaths(verifierOpts);
|
|
139
|
+
const provider = verifierOpts.provider;
|
|
140
|
+
const consumer = inferConsumerFromPacts(pactPaths, wrapOpts.consumer);
|
|
141
|
+
const spanName = wrapOpts.spanName ?? "pact.verification";
|
|
142
|
+
const start = process.hrtime.bigint();
|
|
143
|
+
const Verifier = wrapOpts.skipVerifier ? void 0 : await loadVerifier(wrapOpts.Verifier);
|
|
144
|
+
return span(spanName, async () => {
|
|
145
|
+
const span = getActiveSpan();
|
|
146
|
+
span?.setAttributes({
|
|
147
|
+
[PACT_ATTRS.CONSUMER]: consumer,
|
|
148
|
+
[PACT_ATTRS.PROVIDER]: provider,
|
|
149
|
+
[PACT_ATTRS.KIND]: pactPaths.length === 1 ? kindForPactFile(pactPaths[0]) : "message",
|
|
150
|
+
"pact.role": "provider"
|
|
151
|
+
});
|
|
152
|
+
try {
|
|
153
|
+
if (Verifier) {
|
|
154
|
+
await new Verifier(verifierOpts).verifyProvider();
|
|
155
|
+
}
|
|
156
|
+
span?.setAttributes({ [PACT_ATTRS.OUTCOME]: "passed" });
|
|
157
|
+
const ctx = span?.spanContext();
|
|
158
|
+
const base = {
|
|
159
|
+
source: "test",
|
|
160
|
+
role: "provider",
|
|
161
|
+
outcome: "passed",
|
|
162
|
+
observed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
163
|
+
trace_id: ctx?.traceId,
|
|
164
|
+
span_id: ctx?.spanId,
|
|
165
|
+
run_id: process.env.AUTOTEL_PACT_RUN_ID,
|
|
166
|
+
git_sha: process.env.GIT_SHA ?? process.env.GITHUB_SHA
|
|
167
|
+
};
|
|
168
|
+
for (const filePath of pactPaths) {
|
|
169
|
+
const pact = parsePactFile(filePath);
|
|
170
|
+
if (!pact) continue;
|
|
171
|
+
const interactions = interactionsFromPactFile(pact);
|
|
172
|
+
for (const i of interactions) {
|
|
173
|
+
const entry = {
|
|
174
|
+
type: "interaction",
|
|
175
|
+
spec: LEDGER_ENTRY_SPEC,
|
|
176
|
+
consumer: i.consumer,
|
|
177
|
+
provider: i.provider,
|
|
178
|
+
interaction: i.interaction,
|
|
179
|
+
interaction_id: i.interactionId,
|
|
180
|
+
states: [],
|
|
181
|
+
kind: i.kind,
|
|
182
|
+
duration_ms: Number(process.hrtime.bigint() - start) / 1e6,
|
|
183
|
+
...base
|
|
184
|
+
};
|
|
185
|
+
appendLedgerEntry(entry, wrapOpts);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
span?.setAttributes({ [PACT_ATTRS.OUTCOME]: "failed" });
|
|
190
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
191
|
+
const ctx = span?.spanContext();
|
|
192
|
+
appendProviderVerificationFailure(
|
|
193
|
+
{
|
|
194
|
+
consumer,
|
|
195
|
+
provider,
|
|
196
|
+
source: "test",
|
|
197
|
+
observed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
198
|
+
error: message,
|
|
199
|
+
trace_id: ctx?.traceId,
|
|
200
|
+
span_id: ctx?.spanId,
|
|
201
|
+
run_id: process.env.AUTOTEL_PACT_RUN_ID,
|
|
202
|
+
git_sha: process.env.GIT_SHA ?? process.env.GITHUB_SHA
|
|
203
|
+
},
|
|
204
|
+
wrapOpts
|
|
205
|
+
);
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export { withProviderVerification };
|
|
212
|
+
//# sourceMappingURL=provider.js.map
|
|
213
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/ledger.ts","../src/pact-file.ts","../src/attrs.ts","../src/wrapper-provider.ts"],"names":["readFileSync","path","autotelSpan"],"mappings":"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","file":"provider.js","sourcesContent":["/**\n * Kind of contract interaction observed.\n */\nexport type PactKind = 'message' | 'http';\n\nexport type PactOutcome = 'passed' | 'failed';\n\nexport type LedgerSource = 'test' | 'production';\nexport type LedgerRole = 'consumer' | 'provider';\n\nexport const LEDGER_ENTRY_SPEC = 'autotel-pact-ledger-entry/v0.2.0';\nexport const AUDIT_MATRIX_SPEC = 'autotel-pact-audit-matrix/v0.2.0';\n\n/**\n * Metadata about a single Pact interaction, derived from the reified message\n * plus the consumer/provider config. Stamped onto the span and the ledger entry.\n */\nexport interface PactInteractionMeta {\n consumer: string;\n provider: string;\n description: string;\n states: string[];\n kind: PactKind;\n interactionId?: string;\n}\n\n/**\n * Per-interaction ledger evidence (consumer exercise, provider verify, or production tag).\n */\nexport interface InteractionLedgerEntry {\n type?: 'interaction';\n spec: typeof LEDGER_ENTRY_SPEC;\n consumer: string;\n provider: string;\n interaction: string;\n interaction_id?: string;\n states: string[];\n kind: PactKind;\n outcome: PactOutcome;\n source: LedgerSource;\n role: LedgerRole;\n duration_ms: number;\n observed_at: string;\n trace_id?: string;\n span_id?: string;\n run_id?: string;\n git_sha?: string;\n error?: string;\n}\n\n/**\n * Run-level provider verification failure — does not imply per-interaction outcomes.\n */\nexport interface ProviderVerificationRunEntry {\n type: 'provider_verification_run';\n spec: typeof LEDGER_ENTRY_SPEC;\n consumer: string;\n provider: string;\n outcome: 'failed';\n source: LedgerSource;\n role: 'provider';\n observed_at: string;\n error: string;\n run_id?: string;\n git_sha?: string;\n trace_id?: string;\n span_id?: string;\n}\n\nexport type LedgerRecord = InteractionLedgerEntry | ProviderVerificationRunEntry;\n\nexport function isInteractionLedgerEntry(\n entry: LedgerRecord,\n): entry is InteractionLedgerEntry {\n return entry.type !== 'provider_verification_run';\n}\n\nexport function isProviderVerificationRun(\n entry: LedgerRecord,\n): entry is ProviderVerificationRunEntry {\n return entry.type === 'provider_verification_run';\n}\n\n/**\n * Shape of a Pact contract file on disk (subset we read).\n */\nexport interface PactFile {\n consumer: { name: string };\n provider: { name: string };\n messages?: Array<{\n description: string;\n providerStates?: Array<{ name: string }>;\n metadata?: Record<string, unknown>;\n }>;\n interactions?: Array<{\n description: string;\n providerStates?: Array<{ name: string }>;\n metadata?: Record<string, unknown>;\n }>;\n}\n\nexport interface BrokerVerification {\n consumer: string;\n provider: string;\n success: boolean;\n verifiedAt?: string;\n /**\n * Populated when the broker could not be reached or returned a non-2xx\n * response. Distinguishes \"broker said the pact is not verified\" (no error)\n * from \"we could not determine verification status\" (error set).\n */\n error?: string;\n}\n\n/**\n * One row in the audit matrix.\n */\nexport interface AuditRow {\n consumer: string;\n provider: string;\n interaction: string;\n interaction_id?: string;\n kind: PactKind;\n contracted: boolean;\n /** Any interaction-level ledger hit in the window (test or production). */\n observed: boolean;\n test_seen: boolean;\n prod_seen: boolean;\n provider_verified: boolean;\n broker_verified: boolean;\n broker_verified_at?: string;\n /** Set when the broker check failed (network error, non-2xx, parse error). */\n broker_error?: string;\n last_observed_at?: string;\n last_outcome?: PactOutcome;\n}\n\nexport interface AuditMatrix {\n spec: typeof AUDIT_MATRIX_SPEC;\n rows: AuditRow[];\n counts: {\n total: number;\n /** Any contracted row. */\n contracted: number;\n /** Any row with test_seen OR prod_seen. */\n observed: number;\n /** Contracted AND seen in a consumer test. */\n contracted_and_test_seen: number;\n /** Contracted but not seen in a consumer test (stale confidence). */\n contracted_not_test_seen: number;\n /** Seen (test or production) without a matching contract (ungoverned flow). */\n test_or_prod_seen_not_contracted: number;\n test_seen: number;\n prod_seen: number;\n provider_verified: number;\n broker_verified: number;\n };\n window_days: number;\n generated_at: string;\n verification_failures?: ProviderVerificationRunEntry[];\n}\n\nexport interface PactInteractionKey {\n consumer: string;\n provider: string;\n interaction: string;\n kind: PactKind;\n interactionId?: string;\n}\n","import {\n appendFileSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n existsSync,\n} from 'node:fs';\nimport { appendFile, mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport { normalizeLedgerRecord } from './ledger-normalize.js';\nimport { LEDGER_ENTRY_SPEC, type LedgerRecord } from './types.js';\n\nexport interface LedgerOptions {\n dir?: string;\n runId?: string;\n}\n\nconst DEFAULT_DIR = '.autotel-pact';\n\nfunction resolveLedgerDir(opts: LedgerOptions = {}): string {\n return path.resolve(process.cwd(), opts.dir ?? process.env.AUTOTEL_PACT_LEDGER_DIR ?? DEFAULT_DIR);\n}\n\nfunction resolveRunId(opts: LedgerOptions = {}): string {\n const explicit = opts.runId ?? process.env.AUTOTEL_PACT_RUN_ID;\n if (explicit) return explicit;\n return `local-${new Date().toISOString().replaceAll(/[:.]/g, '-')}`;\n}\n\nexport function ledgerPath(opts: LedgerOptions = {}): string {\n const dir = resolveLedgerDir(opts);\n return path.join(dir, `ledger-${resolveRunId(opts)}.jsonl`);\n}\n\nfunction writeLine(filePath: string, entry: LedgerRecord): void {\n mkdirSync(path.dirname(filePath), { recursive: true });\n appendFileSync(filePath, JSON.stringify(entry) + '\\n', 'utf8');\n}\n\n/**\n * Append a ledger record synchronously (tests and consumer wrappers).\n */\nexport function appendLedgerEntry(\n entry: LedgerRecord,\n opts: LedgerOptions = {},\n): void {\n const filePath = ledgerPath(opts);\n const normalized: LedgerRecord =\n entry.type === 'provider_verification_run'\n ? entry\n : { ...entry, spec: LEDGER_ENTRY_SPEC, type: 'interaction' as const };\n writeLine(filePath, normalized);\n}\n\nexport function appendProviderVerificationFailure(\n entry: Omit<\n import('./types.js').ProviderVerificationRunEntry,\n 'type' | 'spec' | 'outcome' | 'role'\n > & { error: string },\n opts: LedgerOptions = {},\n): void {\n appendLedgerEntry(\n {\n type: 'provider_verification_run',\n spec: LEDGER_ENTRY_SPEC,\n outcome: 'failed',\n role: 'provider',\n source: entry.source ?? 'test',\n consumer: entry.consumer,\n provider: entry.provider,\n observed_at: entry.observed_at,\n error: entry.error,\n run_id: entry.run_id,\n git_sha: entry.git_sha,\n trace_id: entry.trace_id,\n span_id: entry.span_id,\n },\n opts,\n );\n}\n\n/**\n * Read all ledger files and return normalized records.\n */\nexport function readLedger(opts: LedgerOptions = {}): LedgerRecord[] {\n const dir = resolveLedgerDir(opts);\n if (!existsSync(dir)) return [];\n const files = readdirSync(dir).filter((f) => f.endsWith('.jsonl'));\n const entries: LedgerRecord[] = [];\n for (const file of files) {\n const text = readFileSync(path.join(dir, file), 'utf8');\n for (const line of text.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const normalized = normalizeLedgerRecord(JSON.parse(line));\n if (normalized) entries.push(normalized);\n } catch {\n // skip malformed lines\n }\n }\n }\n return entries;\n}\n\n/**\n * Serialized async writes for production span processor.\n * Bounded by a producer-side backpressure threshold: once `pendingWrites`\n * reaches `MAX_PENDING_WRITES`, new callers await drainage before queueing,\n * so memory cannot grow unbounded under sustained pressure.\n */\nconst MAX_PENDING_WRITES = 4096;\nlet asyncWriteChain: Promise<void> = Promise.resolve();\nlet pendingWrites = 0;\n\nexport async function appendLedgerEntryAsync(\n entry: LedgerRecord,\n opts: LedgerOptions = {},\n): Promise<void> {\n if (pendingWrites >= MAX_PENDING_WRITES) {\n await asyncWriteChain;\n }\n\n const filePath = ledgerPath(opts);\n const normalized: LedgerRecord =\n entry.type === 'provider_verification_run'\n ? entry\n : { ...entry, spec: LEDGER_ENTRY_SPEC, type: 'interaction' as const };\n const line = JSON.stringify(normalized);\n\n pendingWrites++;\n const run = asyncWriteChain.then(async () => {\n try {\n await mkdir(path.dirname(filePath), { recursive: true });\n await appendFile(filePath, line + '\\n', 'utf8');\n } finally {\n pendingWrites--;\n }\n });\n asyncWriteChain = run.catch(() => {});\n return run;\n}\n\nexport async function flushLedgerWrites(): Promise<void> {\n await asyncWriteChain;\n}\n\n/** @internal Reset async chain between tests. */\nexport function resetLedgerWriteChainForTests(): void {\n asyncWriteChain = Promise.resolve();\n pendingWrites = 0;\n}\n\n/** @internal Expose pending write count for tests. */\nexport function pendingLedgerWriteCount(): number {\n return pendingWrites;\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport path from 'node:path';\nimport type { PactFile, PactInteractionKey } from './types.js';\n\n/**\n * Walk a directory and return absolute paths of all *.json pact files.\n */\nexport function listPactFiles(dir: string): string[] {\n if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];\n const out: string[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n out.push(...listPactFiles(full));\n } else if (entry.isFile() && entry.name.endsWith('.json')) {\n out.push(full);\n }\n }\n return out;\n}\n\nexport function extractInteractionId(\n metadata: Record<string, unknown> | undefined,\n): string | undefined {\n if (!metadata) return undefined;\n const id = metadata.interactionId ?? metadata.interaction_id;\n return typeof id === 'string' && id.length > 0 ? id : undefined;\n}\n\n/**\n * Extract interaction tuples declared in a pact file.\n */\nexport function interactionsFromPactFile(pact: PactFile): PactInteractionKey[] {\n const consumer = pact.consumer?.name;\n const provider = pact.provider?.name;\n if (!consumer || !provider) return [];\n const keys: PactInteractionKey[] = [];\n for (const m of pact.messages ?? []) {\n keys.push({\n consumer,\n provider,\n interaction: m.description,\n kind: 'message',\n interactionId: extractInteractionId(m.metadata),\n });\n }\n for (const i of pact.interactions ?? []) {\n keys.push({\n consumer,\n provider,\n interaction: i.description,\n kind: 'http',\n interactionId: extractInteractionId(i.metadata),\n });\n }\n return keys;\n}\n\nexport function parsePactFile(filePath: string): PactFile | null {\n try {\n return JSON.parse(readFileSync(filePath, 'utf8')) as PactFile;\n } catch {\n return null;\n }\n}\n","import type { PactInteractionMeta, PactOutcome } from './types.js';\n\n/**\n * Attribute keys for Pact interactions. Centralised so the namespace is\n * a single source of truth and is forward-compatible with eventual OTel\n * semantic conventions.\n */\nexport const PACT_ATTRS = {\n CONSUMER: 'pact.consumer',\n PROVIDER: 'pact.provider',\n KIND: 'pact.kind',\n INTERACTION_DESCRIPTION: 'pact.interaction.description',\n INTERACTION_ID: 'pact.interaction.id',\n INTERACTION_STATES: 'pact.interaction.states',\n CONTRACT_FILE: 'pact.contract.file',\n OUTCOME: 'pact.outcome',\n} as const;\n\nexport type PactAttributeKey = (typeof PACT_ATTRS)[keyof typeof PACT_ATTRS];\n\n/**\n * Build the set of attributes to stamp on a span when an interaction is\n * about to be exercised. `outcome` is added later by the wrapper.\n */\nexport function buildPactAttributes(\n meta: PactInteractionMeta,\n opts: { contractFile?: string } = {},\n): Record<string, string | string[]> {\n const attrs: Record<string, string | string[]> = {\n [PACT_ATTRS.CONSUMER]: meta.consumer,\n [PACT_ATTRS.PROVIDER]: meta.provider,\n [PACT_ATTRS.KIND]: meta.kind,\n [PACT_ATTRS.INTERACTION_DESCRIPTION]: meta.description,\n [PACT_ATTRS.INTERACTION_STATES]: meta.states,\n };\n if (opts.contractFile) {\n attrs[PACT_ATTRS.CONTRACT_FILE] = opts.contractFile;\n }\n if (meta.interactionId) {\n attrs[PACT_ATTRS.INTERACTION_ID] = meta.interactionId;\n }\n return attrs;\n}\n\n/**\n * Helper that returns just the outcome attribute — stamped after the\n * handler resolves or rejects.\n */\nexport function outcomeAttribute(\n outcome: PactOutcome,\n): Record<string, string> {\n return { [PACT_ATTRS.OUTCOME]: outcome };\n}\n","import path from 'node:path';\nimport { span as autotelSpan, getActiveSpan } from 'autotel';\nimport { appendLedgerEntry, appendProviderVerificationFailure, type LedgerOptions } from './ledger.js';\nimport { interactionsFromPactFile, parsePactFile } from './pact-file.js';\nimport { LEDGER_ENTRY_SPEC, type InteractionLedgerEntry, type PactKind } from './types.js';\nimport { PACT_ATTRS } from './attrs.js';\n\n/**\n * Minimal Verifier options — structural match for @pact-foundation/pact Verifier.\n */\nexport interface VerifierOptionsLike {\n provider: string;\n providerBaseUrl: string;\n pactUrls?: string[];\n logLevel?: string;\n [key: string]: unknown;\n}\n\nexport interface VerifierLike {\n verifyProvider: () => Promise<unknown>;\n}\n\nexport interface VerifierConstructor {\n new (options: VerifierOptionsLike): VerifierLike;\n}\n\nexport interface WithProviderVerificationOptions extends LedgerOptions {\n /** Consumer name when not inferrable from pact files. */\n consumer?: string;\n spanName?: string;\n /** Custom Verifier class (defaults to dynamic import from @pact-foundation/pact). */\n Verifier?: VerifierConstructor;\n /**\n * Skip loading and calling the Verifier entirely. Emits the same\n * per-interaction ledger rows as a successful verification, parsed from the\n * supplied pact files. Use for demos, smoke tests, and audit-pipeline\n * exercises where running a real provider is impractical. Do not use in\n * production CI: it will mark every interaction in the pact file as\n * provider-verified without any actual verification.\n */\n skipVerifier?: boolean;\n}\n\nfunction resolvePactPaths(opts: VerifierOptionsLike): string[] {\n const urls = opts.pactUrls ?? [];\n return urls.map((u) => path.resolve(process.cwd(), u));\n}\n\nfunction inferConsumerFromPacts(pactPaths: string[], fallback?: string): string {\n for (const filePath of pactPaths) {\n const pact = parsePactFile(filePath);\n if (pact?.consumer?.name) return pact.consumer.name;\n }\n if (fallback) return fallback;\n throw new Error(\n 'autotel-pact: could not infer consumer from pact files. Pass `consumer` in options.',\n );\n}\n\nfunction kindForPactFile(filePath: string): PactKind {\n const pact = parsePactFile(filePath);\n if (!pact) return 'message';\n if ((pact.interactions?.length ?? 0) > 0) return 'http';\n return 'message';\n}\n\nasync function loadVerifier(\n VerifierClass?: VerifierConstructor,\n): Promise<VerifierConstructor> {\n if (VerifierClass) return VerifierClass;\n const mod = await import('@pact-foundation/pact');\n const Verifier = (mod as { Verifier?: VerifierConstructor }).Verifier;\n if (!Verifier) {\n throw new Error(\n 'autotel-pact: @pact-foundation/pact Verifier not found. Install the peer dependency.',\n );\n }\n return Verifier;\n}\n\n/**\n * Wrap provider verification — records per-interaction evidence only on success.\n */\nexport async function withProviderVerification(\n verifierOpts: VerifierOptionsLike,\n wrapOpts: WithProviderVerificationOptions = {},\n): Promise<void> {\n const pactPaths = resolvePactPaths(verifierOpts);\n const provider = verifierOpts.provider;\n const consumer = inferConsumerFromPacts(pactPaths, wrapOpts.consumer);\n const spanName = wrapOpts.spanName ?? 'pact.verification';\n const start = process.hrtime.bigint();\n const Verifier = wrapOpts.skipVerifier ? undefined : await loadVerifier(wrapOpts.Verifier);\n\n return autotelSpan(spanName, async () => {\n const span = getActiveSpan();\n span?.setAttributes({\n [PACT_ATTRS.CONSUMER]: consumer,\n [PACT_ATTRS.PROVIDER]: provider,\n [PACT_ATTRS.KIND]: pactPaths.length === 1 ? kindForPactFile(pactPaths[0]!) : 'message',\n 'pact.role': 'provider',\n });\n\n try {\n if (Verifier) {\n await new Verifier(verifierOpts).verifyProvider();\n }\n span?.setAttributes({ [PACT_ATTRS.OUTCOME]: 'passed' });\n\n const ctx = span?.spanContext();\n const base = {\n source: 'test' as const,\n role: 'provider' as const,\n outcome: 'passed' as const,\n observed_at: new Date().toISOString(),\n trace_id: ctx?.traceId,\n span_id: ctx?.spanId,\n run_id: process.env.AUTOTEL_PACT_RUN_ID,\n git_sha: process.env.GIT_SHA ?? process.env.GITHUB_SHA,\n };\n\n for (const filePath of pactPaths) {\n const pact = parsePactFile(filePath);\n if (!pact) continue;\n const interactions = interactionsFromPactFile(pact);\n for (const i of interactions) {\n const entry: InteractionLedgerEntry = {\n type: 'interaction',\n spec: LEDGER_ENTRY_SPEC,\n consumer: i.consumer,\n provider: i.provider,\n interaction: i.interaction,\n interaction_id: i.interactionId,\n states: [],\n kind: i.kind,\n duration_ms: Number(process.hrtime.bigint() - start) / 1e6,\n ...base,\n };\n appendLedgerEntry(entry, wrapOpts);\n }\n }\n } catch (error) {\n span?.setAttributes({ [PACT_ATTRS.OUTCOME]: 'failed' });\n const message = error instanceof Error ? error.message : String(error);\n const ctx = span?.spanContext();\n appendProviderVerificationFailure(\n {\n consumer,\n provider,\n source: 'test',\n observed_at: new Date().toISOString(),\n error: message,\n trace_id: ctx?.traceId,\n span_id: ctx?.spanId,\n run_id: process.env.AUTOTEL_PACT_RUN_ID,\n git_sha: process.env.GIT_SHA ?? process.env.GITHUB_SHA,\n },\n wrapOpts,\n );\n throw error;\n }\n });\n}\n"]}
|
package/dist/tag.cjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var autotel = require('autotel');
|
|
4
|
+
|
|
5
|
+
// src/tag.ts
|
|
6
|
+
|
|
7
|
+
// src/attrs.ts
|
|
8
|
+
var PACT_ATTRS = {
|
|
9
|
+
CONSUMER: "pact.consumer",
|
|
10
|
+
PROVIDER: "pact.provider",
|
|
11
|
+
KIND: "pact.kind",
|
|
12
|
+
INTERACTION_DESCRIPTION: "pact.interaction.description",
|
|
13
|
+
INTERACTION_ID: "pact.interaction.id",
|
|
14
|
+
INTERACTION_STATES: "pact.interaction.states",
|
|
15
|
+
CONTRACT_FILE: "pact.contract.file"};
|
|
16
|
+
function buildPactAttributes(meta, opts = {}) {
|
|
17
|
+
const attrs = {
|
|
18
|
+
[PACT_ATTRS.CONSUMER]: meta.consumer,
|
|
19
|
+
[PACT_ATTRS.PROVIDER]: meta.provider,
|
|
20
|
+
[PACT_ATTRS.KIND]: meta.kind,
|
|
21
|
+
[PACT_ATTRS.INTERACTION_DESCRIPTION]: meta.description,
|
|
22
|
+
[PACT_ATTRS.INTERACTION_STATES]: meta.states
|
|
23
|
+
};
|
|
24
|
+
if (opts.contractFile) {
|
|
25
|
+
attrs[PACT_ATTRS.CONTRACT_FILE] = opts.contractFile;
|
|
26
|
+
}
|
|
27
|
+
if (meta.interactionId) {
|
|
28
|
+
attrs[PACT_ATTRS.INTERACTION_ID] = meta.interactionId;
|
|
29
|
+
}
|
|
30
|
+
return attrs;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/tag.ts
|
|
34
|
+
function tagPactInteraction(meta) {
|
|
35
|
+
const span = autotel.getActiveSpan();
|
|
36
|
+
if (!span) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
"autotel-pact: tagPactInteraction requires an active span. Wrap the handler with trace() first."
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const attrs = buildPactAttributes(meta);
|
|
42
|
+
span.setAttributes(attrs);
|
|
43
|
+
if (meta.interactionId) {
|
|
44
|
+
span.setAttribute(PACT_ATTRS.INTERACTION_ID, meta.interactionId);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
exports.tagPactInteraction = tagPactInteraction;
|
|
49
|
+
//# sourceMappingURL=tag.cjs.map
|
|
50
|
+
//# sourceMappingURL=tag.cjs.map
|
package/dist/tag.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/attrs.ts","../src/tag.ts"],"names":["getActiveSpan"],"mappings":";;;;;;;AAOO,IAAM,UAAA,GAAa;AAAA,EACxB,QAAA,EAAU,eAAA;AAAA,EACV,QAAA,EAAU,eAAA;AAAA,EACV,IAAA,EAAM,WAAA;AAAA,EACN,uBAAA,EAAyB,8BAAA;AAAA,EACzB,cAAA,EAAgB,qBAAA;AAAA,EAChB,kBAAA,EAAoB,yBAAA;AAAA,EACpB,aAAA,EAAe,oBAEjB,CAAA;AAQO,SAAS,mBAAA,CACd,IAAA,EACA,IAAA,GAAkC,EAAC,EACA;AACnC,EAAA,MAAM,KAAA,GAA2C;AAAA,IAC/C,CAAC,UAAA,CAAW,QAAQ,GAAG,IAAA,CAAK,QAAA;AAAA,IAC5B,CAAC,UAAA,CAAW,QAAQ,GAAG,IAAA,CAAK,QAAA;AAAA,IAC5B,CAAC,UAAA,CAAW,IAAI,GAAG,IAAA,CAAK,IAAA;AAAA,IACxB,CAAC,UAAA,CAAW,uBAAuB,GAAG,IAAA,CAAK,WAAA;AAAA,IAC3C,CAAC,UAAA,CAAW,kBAAkB,GAAG,IAAA,CAAK;AAAA,GACxC;AACA,EAAA,IAAI,KAAK,YAAA,EAAc;AACrB,IAAA,KAAA,CAAM,UAAA,CAAW,aAAa,CAAA,GAAI,IAAA,CAAK,YAAA;AAAA,EACzC;AACA,EAAA,IAAI,KAAK,aAAA,EAAe;AACtB,IAAA,KAAA,CAAM,UAAA,CAAW,cAAc,CAAA,GAAI,IAAA,CAAK,aAAA;AAAA,EAC1C;AACA,EAAA,OAAO,KAAA;AACT;;;AClCO,SAAS,mBAAmB,IAAA,EAAiC;AAClE,EAAA,MAAM,OAAOA,qBAAA,EAAc;AAC3B,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,KAAA,GAAQ,oBAAoB,IAAI,CAAA;AACtC,EAAA,IAAA,CAAK,cAAc,KAAK,CAAA;AACxB,EAAA,IAAI,KAAK,aAAA,EAAe;AACtB,IAAA,IAAA,CAAK,YAAA,CAAa,UAAA,CAAW,cAAA,EAAgB,IAAA,CAAK,aAAa,CAAA;AAAA,EACjE;AACF","file":"tag.cjs","sourcesContent":["import type { PactInteractionMeta, PactOutcome } from './types.js';\n\n/**\n * Attribute keys for Pact interactions. Centralised so the namespace is\n * a single source of truth and is forward-compatible with eventual OTel\n * semantic conventions.\n */\nexport const PACT_ATTRS = {\n CONSUMER: 'pact.consumer',\n PROVIDER: 'pact.provider',\n KIND: 'pact.kind',\n INTERACTION_DESCRIPTION: 'pact.interaction.description',\n INTERACTION_ID: 'pact.interaction.id',\n INTERACTION_STATES: 'pact.interaction.states',\n CONTRACT_FILE: 'pact.contract.file',\n OUTCOME: 'pact.outcome',\n} as const;\n\nexport type PactAttributeKey = (typeof PACT_ATTRS)[keyof typeof PACT_ATTRS];\n\n/**\n * Build the set of attributes to stamp on a span when an interaction is\n * about to be exercised. `outcome` is added later by the wrapper.\n */\nexport function buildPactAttributes(\n meta: PactInteractionMeta,\n opts: { contractFile?: string } = {},\n): Record<string, string | string[]> {\n const attrs: Record<string, string | string[]> = {\n [PACT_ATTRS.CONSUMER]: meta.consumer,\n [PACT_ATTRS.PROVIDER]: meta.provider,\n [PACT_ATTRS.KIND]: meta.kind,\n [PACT_ATTRS.INTERACTION_DESCRIPTION]: meta.description,\n [PACT_ATTRS.INTERACTION_STATES]: meta.states,\n };\n if (opts.contractFile) {\n attrs[PACT_ATTRS.CONTRACT_FILE] = opts.contractFile;\n }\n if (meta.interactionId) {\n attrs[PACT_ATTRS.INTERACTION_ID] = meta.interactionId;\n }\n return attrs;\n}\n\n/**\n * Helper that returns just the outcome attribute — stamped after the\n * handler resolves or rejects.\n */\nexport function outcomeAttribute(\n outcome: PactOutcome,\n): Record<string, string> {\n return { [PACT_ATTRS.OUTCOME]: outcome };\n}\n","import { getActiveSpan } from 'autotel';\nimport { buildPactAttributes, PACT_ATTRS } from './attrs.js';\nimport type { PactInteractionMeta } from './types.js';\n\n/**\n * Stamp `pact.*` attributes on the active span for production observation.\n * Requires an active span (wrap handlers with `trace()` first).\n */\nexport function tagPactInteraction(meta: PactInteractionMeta): void {\n const span = getActiveSpan();\n if (!span) {\n throw new Error(\n 'autotel-pact: tagPactInteraction requires an active span. Wrap the handler with trace() first.',\n );\n }\n const attrs = buildPactAttributes(meta);\n span.setAttributes(attrs);\n if (meta.interactionId) {\n span.setAttribute(PACT_ATTRS.INTERACTION_ID, meta.interactionId);\n }\n}\n"]}
|
package/dist/tag.d.cts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { P as PactInteractionMeta } from './types-BHGiwqcp.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stamp `pact.*` attributes on the active span for production observation.
|
|
5
|
+
* Requires an active span (wrap handlers with `trace()` first).
|
|
6
|
+
*/
|
|
7
|
+
declare function tagPactInteraction(meta: PactInteractionMeta): void;
|
|
8
|
+
|
|
9
|
+
export { tagPactInteraction };
|
package/dist/tag.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { P as PactInteractionMeta } from './types-BHGiwqcp.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stamp `pact.*` attributes on the active span for production observation.
|
|
5
|
+
* Requires an active span (wrap handlers with `trace()` first).
|
|
6
|
+
*/
|
|
7
|
+
declare function tagPactInteraction(meta: PactInteractionMeta): void;
|
|
8
|
+
|
|
9
|
+
export { tagPactInteraction };
|
package/dist/tag.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getActiveSpan } from 'autotel';
|
|
2
|
+
|
|
3
|
+
// src/tag.ts
|
|
4
|
+
|
|
5
|
+
// src/attrs.ts
|
|
6
|
+
var PACT_ATTRS = {
|
|
7
|
+
CONSUMER: "pact.consumer",
|
|
8
|
+
PROVIDER: "pact.provider",
|
|
9
|
+
KIND: "pact.kind",
|
|
10
|
+
INTERACTION_DESCRIPTION: "pact.interaction.description",
|
|
11
|
+
INTERACTION_ID: "pact.interaction.id",
|
|
12
|
+
INTERACTION_STATES: "pact.interaction.states",
|
|
13
|
+
CONTRACT_FILE: "pact.contract.file"};
|
|
14
|
+
function buildPactAttributes(meta, opts = {}) {
|
|
15
|
+
const attrs = {
|
|
16
|
+
[PACT_ATTRS.CONSUMER]: meta.consumer,
|
|
17
|
+
[PACT_ATTRS.PROVIDER]: meta.provider,
|
|
18
|
+
[PACT_ATTRS.KIND]: meta.kind,
|
|
19
|
+
[PACT_ATTRS.INTERACTION_DESCRIPTION]: meta.description,
|
|
20
|
+
[PACT_ATTRS.INTERACTION_STATES]: meta.states
|
|
21
|
+
};
|
|
22
|
+
if (opts.contractFile) {
|
|
23
|
+
attrs[PACT_ATTRS.CONTRACT_FILE] = opts.contractFile;
|
|
24
|
+
}
|
|
25
|
+
if (meta.interactionId) {
|
|
26
|
+
attrs[PACT_ATTRS.INTERACTION_ID] = meta.interactionId;
|
|
27
|
+
}
|
|
28
|
+
return attrs;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/tag.ts
|
|
32
|
+
function tagPactInteraction(meta) {
|
|
33
|
+
const span = getActiveSpan();
|
|
34
|
+
if (!span) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"autotel-pact: tagPactInteraction requires an active span. Wrap the handler with trace() first."
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const attrs = buildPactAttributes(meta);
|
|
40
|
+
span.setAttributes(attrs);
|
|
41
|
+
if (meta.interactionId) {
|
|
42
|
+
span.setAttribute(PACT_ATTRS.INTERACTION_ID, meta.interactionId);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { tagPactInteraction };
|
|
47
|
+
//# sourceMappingURL=tag.js.map
|
|
48
|
+
//# sourceMappingURL=tag.js.map
|
package/dist/tag.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/attrs.ts","../src/tag.ts"],"names":[],"mappings":";;;;;AAOO,IAAM,UAAA,GAAa;AAAA,EACxB,QAAA,EAAU,eAAA;AAAA,EACV,QAAA,EAAU,eAAA;AAAA,EACV,IAAA,EAAM,WAAA;AAAA,EACN,uBAAA,EAAyB,8BAAA;AAAA,EACzB,cAAA,EAAgB,qBAAA;AAAA,EAChB,kBAAA,EAAoB,yBAAA;AAAA,EACpB,aAAA,EAAe,oBAEjB,CAAA;AAQO,SAAS,mBAAA,CACd,IAAA,EACA,IAAA,GAAkC,EAAC,EACA;AACnC,EAAA,MAAM,KAAA,GAA2C;AAAA,IAC/C,CAAC,UAAA,CAAW,QAAQ,GAAG,IAAA,CAAK,QAAA;AAAA,IAC5B,CAAC,UAAA,CAAW,QAAQ,GAAG,IAAA,CAAK,QAAA;AAAA,IAC5B,CAAC,UAAA,CAAW,IAAI,GAAG,IAAA,CAAK,IAAA;AAAA,IACxB,CAAC,UAAA,CAAW,uBAAuB,GAAG,IAAA,CAAK,WAAA;AAAA,IAC3C,CAAC,UAAA,CAAW,kBAAkB,GAAG,IAAA,CAAK;AAAA,GACxC;AACA,EAAA,IAAI,KAAK,YAAA,EAAc;AACrB,IAAA,KAAA,CAAM,UAAA,CAAW,aAAa,CAAA,GAAI,IAAA,CAAK,YAAA;AAAA,EACzC;AACA,EAAA,IAAI,KAAK,aAAA,EAAe;AACtB,IAAA,KAAA,CAAM,UAAA,CAAW,cAAc,CAAA,GAAI,IAAA,CAAK,aAAA;AAAA,EAC1C;AACA,EAAA,OAAO,KAAA;AACT;;;AClCO,SAAS,mBAAmB,IAAA,EAAiC;AAClE,EAAA,MAAM,OAAO,aAAA,EAAc;AAC3B,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,KAAA,GAAQ,oBAAoB,IAAI,CAAA;AACtC,EAAA,IAAA,CAAK,cAAc,KAAK,CAAA;AACxB,EAAA,IAAI,KAAK,aAAA,EAAe;AACtB,IAAA,IAAA,CAAK,YAAA,CAAa,UAAA,CAAW,cAAA,EAAgB,IAAA,CAAK,aAAa,CAAA;AAAA,EACjE;AACF","file":"tag.js","sourcesContent":["import type { PactInteractionMeta, PactOutcome } from './types.js';\n\n/**\n * Attribute keys for Pact interactions. Centralised so the namespace is\n * a single source of truth and is forward-compatible with eventual OTel\n * semantic conventions.\n */\nexport const PACT_ATTRS = {\n CONSUMER: 'pact.consumer',\n PROVIDER: 'pact.provider',\n KIND: 'pact.kind',\n INTERACTION_DESCRIPTION: 'pact.interaction.description',\n INTERACTION_ID: 'pact.interaction.id',\n INTERACTION_STATES: 'pact.interaction.states',\n CONTRACT_FILE: 'pact.contract.file',\n OUTCOME: 'pact.outcome',\n} as const;\n\nexport type PactAttributeKey = (typeof PACT_ATTRS)[keyof typeof PACT_ATTRS];\n\n/**\n * Build the set of attributes to stamp on a span when an interaction is\n * about to be exercised. `outcome` is added later by the wrapper.\n */\nexport function buildPactAttributes(\n meta: PactInteractionMeta,\n opts: { contractFile?: string } = {},\n): Record<string, string | string[]> {\n const attrs: Record<string, string | string[]> = {\n [PACT_ATTRS.CONSUMER]: meta.consumer,\n [PACT_ATTRS.PROVIDER]: meta.provider,\n [PACT_ATTRS.KIND]: meta.kind,\n [PACT_ATTRS.INTERACTION_DESCRIPTION]: meta.description,\n [PACT_ATTRS.INTERACTION_STATES]: meta.states,\n };\n if (opts.contractFile) {\n attrs[PACT_ATTRS.CONTRACT_FILE] = opts.contractFile;\n }\n if (meta.interactionId) {\n attrs[PACT_ATTRS.INTERACTION_ID] = meta.interactionId;\n }\n return attrs;\n}\n\n/**\n * Helper that returns just the outcome attribute — stamped after the\n * handler resolves or rejects.\n */\nexport function outcomeAttribute(\n outcome: PactOutcome,\n): Record<string, string> {\n return { [PACT_ATTRS.OUTCOME]: outcome };\n}\n","import { getActiveSpan } from 'autotel';\nimport { buildPactAttributes, PACT_ATTRS } from './attrs.js';\nimport type { PactInteractionMeta } from './types.js';\n\n/**\n * Stamp `pact.*` attributes on the active span for production observation.\n * Requires an active span (wrap handlers with `trace()` first).\n */\nexport function tagPactInteraction(meta: PactInteractionMeta): void {\n const span = getActiveSpan();\n if (!span) {\n throw new Error(\n 'autotel-pact: tagPactInteraction requires an active span. Wrap the handler with trace() first.',\n );\n }\n const attrs = buildPactAttributes(meta);\n span.setAttributes(attrs);\n if (meta.interactionId) {\n span.setAttribute(PACT_ATTRS.INTERACTION_ID, meta.interactionId);\n }\n}\n"]}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kind of contract interaction observed.
|
|
3
|
+
*/
|
|
4
|
+
type PactKind = 'message' | 'http';
|
|
5
|
+
type PactOutcome = 'passed' | 'failed';
|
|
6
|
+
type LedgerSource = 'test' | 'production';
|
|
7
|
+
type LedgerRole = 'consumer' | 'provider';
|
|
8
|
+
declare const LEDGER_ENTRY_SPEC = "autotel-pact-ledger-entry/v0.2.0";
|
|
9
|
+
declare const AUDIT_MATRIX_SPEC = "autotel-pact-audit-matrix/v0.2.0";
|
|
10
|
+
/**
|
|
11
|
+
* Metadata about a single Pact interaction, derived from the reified message
|
|
12
|
+
* plus the consumer/provider config. Stamped onto the span and the ledger entry.
|
|
13
|
+
*/
|
|
14
|
+
interface PactInteractionMeta {
|
|
15
|
+
consumer: string;
|
|
16
|
+
provider: string;
|
|
17
|
+
description: string;
|
|
18
|
+
states: string[];
|
|
19
|
+
kind: PactKind;
|
|
20
|
+
interactionId?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Per-interaction ledger evidence (consumer exercise, provider verify, or production tag).
|
|
24
|
+
*/
|
|
25
|
+
interface InteractionLedgerEntry {
|
|
26
|
+
type?: 'interaction';
|
|
27
|
+
spec: typeof LEDGER_ENTRY_SPEC;
|
|
28
|
+
consumer: string;
|
|
29
|
+
provider: string;
|
|
30
|
+
interaction: string;
|
|
31
|
+
interaction_id?: string;
|
|
32
|
+
states: string[];
|
|
33
|
+
kind: PactKind;
|
|
34
|
+
outcome: PactOutcome;
|
|
35
|
+
source: LedgerSource;
|
|
36
|
+
role: LedgerRole;
|
|
37
|
+
duration_ms: number;
|
|
38
|
+
observed_at: string;
|
|
39
|
+
trace_id?: string;
|
|
40
|
+
span_id?: string;
|
|
41
|
+
run_id?: string;
|
|
42
|
+
git_sha?: string;
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Run-level provider verification failure — does not imply per-interaction outcomes.
|
|
47
|
+
*/
|
|
48
|
+
interface ProviderVerificationRunEntry {
|
|
49
|
+
type: 'provider_verification_run';
|
|
50
|
+
spec: typeof LEDGER_ENTRY_SPEC;
|
|
51
|
+
consumer: string;
|
|
52
|
+
provider: string;
|
|
53
|
+
outcome: 'failed';
|
|
54
|
+
source: LedgerSource;
|
|
55
|
+
role: 'provider';
|
|
56
|
+
observed_at: string;
|
|
57
|
+
error: string;
|
|
58
|
+
run_id?: string;
|
|
59
|
+
git_sha?: string;
|
|
60
|
+
trace_id?: string;
|
|
61
|
+
span_id?: string;
|
|
62
|
+
}
|
|
63
|
+
type LedgerRecord = InteractionLedgerEntry | ProviderVerificationRunEntry;
|
|
64
|
+
declare function isInteractionLedgerEntry(entry: LedgerRecord): entry is InteractionLedgerEntry;
|
|
65
|
+
declare function isProviderVerificationRun(entry: LedgerRecord): entry is ProviderVerificationRunEntry;
|
|
66
|
+
/**
|
|
67
|
+
* Shape of a Pact contract file on disk (subset we read).
|
|
68
|
+
*/
|
|
69
|
+
interface PactFile {
|
|
70
|
+
consumer: {
|
|
71
|
+
name: string;
|
|
72
|
+
};
|
|
73
|
+
provider: {
|
|
74
|
+
name: string;
|
|
75
|
+
};
|
|
76
|
+
messages?: Array<{
|
|
77
|
+
description: string;
|
|
78
|
+
providerStates?: Array<{
|
|
79
|
+
name: string;
|
|
80
|
+
}>;
|
|
81
|
+
metadata?: Record<string, unknown>;
|
|
82
|
+
}>;
|
|
83
|
+
interactions?: Array<{
|
|
84
|
+
description: string;
|
|
85
|
+
providerStates?: Array<{
|
|
86
|
+
name: string;
|
|
87
|
+
}>;
|
|
88
|
+
metadata?: Record<string, unknown>;
|
|
89
|
+
}>;
|
|
90
|
+
}
|
|
91
|
+
interface BrokerVerification {
|
|
92
|
+
consumer: string;
|
|
93
|
+
provider: string;
|
|
94
|
+
success: boolean;
|
|
95
|
+
verifiedAt?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Populated when the broker could not be reached or returned a non-2xx
|
|
98
|
+
* response. Distinguishes "broker said the pact is not verified" (no error)
|
|
99
|
+
* from "we could not determine verification status" (error set).
|
|
100
|
+
*/
|
|
101
|
+
error?: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* One row in the audit matrix.
|
|
105
|
+
*/
|
|
106
|
+
interface AuditRow {
|
|
107
|
+
consumer: string;
|
|
108
|
+
provider: string;
|
|
109
|
+
interaction: string;
|
|
110
|
+
interaction_id?: string;
|
|
111
|
+
kind: PactKind;
|
|
112
|
+
contracted: boolean;
|
|
113
|
+
/** Any interaction-level ledger hit in the window (test or production). */
|
|
114
|
+
observed: boolean;
|
|
115
|
+
test_seen: boolean;
|
|
116
|
+
prod_seen: boolean;
|
|
117
|
+
provider_verified: boolean;
|
|
118
|
+
broker_verified: boolean;
|
|
119
|
+
broker_verified_at?: string;
|
|
120
|
+
/** Set when the broker check failed (network error, non-2xx, parse error). */
|
|
121
|
+
broker_error?: string;
|
|
122
|
+
last_observed_at?: string;
|
|
123
|
+
last_outcome?: PactOutcome;
|
|
124
|
+
}
|
|
125
|
+
interface AuditMatrix {
|
|
126
|
+
spec: typeof AUDIT_MATRIX_SPEC;
|
|
127
|
+
rows: AuditRow[];
|
|
128
|
+
counts: {
|
|
129
|
+
total: number;
|
|
130
|
+
/** Any contracted row. */
|
|
131
|
+
contracted: number;
|
|
132
|
+
/** Any row with test_seen OR prod_seen. */
|
|
133
|
+
observed: number;
|
|
134
|
+
/** Contracted AND seen in a consumer test. */
|
|
135
|
+
contracted_and_test_seen: number;
|
|
136
|
+
/** Contracted but not seen in a consumer test (stale confidence). */
|
|
137
|
+
contracted_not_test_seen: number;
|
|
138
|
+
/** Seen (test or production) without a matching contract (ungoverned flow). */
|
|
139
|
+
test_or_prod_seen_not_contracted: number;
|
|
140
|
+
test_seen: number;
|
|
141
|
+
prod_seen: number;
|
|
142
|
+
provider_verified: number;
|
|
143
|
+
broker_verified: number;
|
|
144
|
+
};
|
|
145
|
+
window_days: number;
|
|
146
|
+
generated_at: string;
|
|
147
|
+
verification_failures?: ProviderVerificationRunEntry[];
|
|
148
|
+
}
|
|
149
|
+
interface PactInteractionKey {
|
|
150
|
+
consumer: string;
|
|
151
|
+
provider: string;
|
|
152
|
+
interaction: string;
|
|
153
|
+
kind: PactKind;
|
|
154
|
+
interactionId?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { AUDIT_MATRIX_SPEC as A, type BrokerVerification as B, type InteractionLedgerEntry as I, LEDGER_ENTRY_SPEC as L, type PactInteractionMeta as P, type PactOutcome as a, type PactFile as b, type PactInteractionKey as c, type AuditMatrix as d, type AuditRow as e, type LedgerRecord as f, type LedgerRole as g, type LedgerSource as h, type PactKind as i, type ProviderVerificationRunEntry as j, isInteractionLedgerEntry as k, isProviderVerificationRun as l };
|