autotel-pact 8.0.0 → 9.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.
Files changed (62) hide show
  1. package/README.md +37 -40
  2. package/dist/attrs-DbCs1ou0.cjs.map +1 -1
  3. package/dist/attrs-ElwK54Ym.js.map +1 -1
  4. package/dist/audit.cjs.map +1 -1
  5. package/dist/audit.d.cts +2 -3
  6. package/dist/audit.d.cts.map +1 -1
  7. package/dist/audit.d.ts +2 -3
  8. package/dist/audit.d.ts.map +1 -1
  9. package/dist/audit.js.map +1 -1
  10. package/dist/auto-wrap.cjs.map +1 -1
  11. package/dist/auto-wrap.d.cts.map +1 -1
  12. package/dist/auto-wrap.d.ts.map +1 -1
  13. package/dist/auto-wrap.js.map +1 -1
  14. package/dist/broker.cjs.map +1 -1
  15. package/dist/broker.d.cts +1 -2
  16. package/dist/broker.d.cts.map +1 -1
  17. package/dist/broker.d.ts +1 -2
  18. package/dist/broker.d.ts.map +1 -1
  19. package/dist/broker.js.map +1 -1
  20. package/dist/cli.cjs.map +1 -1
  21. package/dist/cli.d.cts.map +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js.map +1 -1
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +2 -3
  26. package/dist/index.d.cts.map +1 -1
  27. package/dist/index.d.ts +2 -3
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/labels-0Cwk5E-8.cjs.map +1 -1
  31. package/dist/labels-BQjT_Zrv.js.map +1 -1
  32. package/dist/ledger-BayQwemz.cjs.map +1 -1
  33. package/dist/{ledger-Bzh_6tbV.d.ts → ledger-CBUbpQr-.d.ts} +2 -3
  34. package/dist/ledger-CBUbpQr-.d.ts.map +1 -0
  35. package/dist/ledger-sII3Ig3Y.js.map +1 -1
  36. package/dist/{ledger-BAFsRX6y.d.cts → ledger-yzVOA9iE.d.cts} +2 -3
  37. package/dist/ledger-yzVOA9iE.d.cts.map +1 -0
  38. package/dist/processor.cjs.map +1 -1
  39. package/dist/processor.d.cts +1 -2
  40. package/dist/processor.d.cts.map +1 -1
  41. package/dist/processor.d.ts +1 -2
  42. package/dist/processor.d.ts.map +1 -1
  43. package/dist/processor.js.map +1 -1
  44. package/dist/provider.cjs.map +1 -1
  45. package/dist/provider.d.cts +1 -2
  46. package/dist/provider.d.cts.map +1 -1
  47. package/dist/provider.d.ts +1 -2
  48. package/dist/provider.d.ts.map +1 -1
  49. package/dist/provider.js.map +1 -1
  50. package/dist/tag.d.cts +1 -2
  51. package/dist/tag.d.cts.map +1 -1
  52. package/dist/tag.d.ts +1 -2
  53. package/dist/tag.d.ts.map +1 -1
  54. package/dist/{types-D_0kgK2e.d.ts → types-DpHqSzxb.d.cts} +11 -6
  55. package/dist/types-DpHqSzxb.d.cts.map +1 -0
  56. package/dist/{types-D_0kgK2e.d.cts → types-DpHqSzxb.d.ts} +11 -6
  57. package/dist/types-DpHqSzxb.d.ts.map +1 -0
  58. package/package.json +5 -5
  59. package/dist/ledger-BAFsRX6y.d.cts.map +0 -1
  60. package/dist/ledger-Bzh_6tbV.d.ts.map +0 -1
  61. package/dist/types-D_0kgK2e.d.cts.map +0 -1
  62. package/dist/types-D_0kgK2e.d.ts.map +0 -1
package/README.md CHANGED
@@ -14,12 +14,12 @@ A green Pact suite is evidence of **compatibility**. An observed verified contra
14
14
 
15
15
  Every feature states what was observed or verified. We never claim precision the data cannot support.
16
16
 
17
- | Evidence | What it means | What you must configure |
18
- |----------|---------------|-------------------------|
19
- | **Seen in test** | Consumer exercised the interaction in CI (`source=test`, `role=consumer`) | `withPactInteraction` or `auto-wrap` |
20
- | **Seen in production** | A span tagged with `pact.*` was recorded at runtime | `tagPactInteraction()` + `PactLedgerSpanProcessor` |
21
- | **Provider verified** | Provider `verifyProvider()` succeeded and we enumerated interactions from the pact file | `withProviderVerification` |
22
- | **Broker verified** | Latest broker verification for the consumer–provider **pact pair** succeeded | Broker URL/token at **audit** time |
17
+ | Evidence | What it means | What you must configure |
18
+ | ---------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------- |
19
+ | **Seen in test** | Consumer exercised the interaction in CI (`source=test`, `role=consumer`) | `withPactInteraction` or `auto-wrap` |
20
+ | **Seen in production** | A span tagged with `pact.*` was recorded at runtime | `tagPactInteraction()` + `PactLedgerSpanProcessor` |
21
+ | **Provider verified** | Provider `verifyProvider()` succeeded and we enumerated interactions from the pact file | `withProviderVerification` |
22
+ | **Broker verified** | Latest broker verification for the consumer–provider **pact pair** succeeded | Broker URL/token at **audit** time |
23
23
 
24
24
  > **Broker limitation:** Broker verification proves the latest pact between a consumer and provider passed. It does **not** prove autotel-pact observed each interaction.
25
25
 
@@ -218,19 +218,16 @@ The wrapper parses the pact files, fans out one ledger row per interaction with
218
218
  Tag business spans, then register the processor:
219
219
 
220
220
  ```ts
221
- import { init } from 'autotel';
222
- import { trace } from 'autotel';
221
+ import { init, span } from 'autotel';
223
222
  import { createPactLedgerProcessor } from 'autotel-pact/processor';
224
223
  import { tagPactInteraction } from 'autotel-pact/tag';
225
224
 
226
225
  init({
227
226
  service: 'order-service',
228
- spanProcessors: [
229
- createPactLedgerProcessor({ dir: '.autotel-pact-prod' }),
230
- ],
227
+ spanProcessors: [createPactLedgerProcessor({ dir: '.autotel-pact-prod' })],
231
228
  });
232
229
 
233
- trace('handleOrderCreated', (ctx) => {
230
+ span('handleOrderCreated', () => {
234
231
  tagPactInteraction({
235
232
  consumer: 'OrderShipper',
236
233
  provider: 'OrderService',
@@ -280,26 +277,26 @@ STATUS CONTRACTED TEST_SEEN PROD_SEEN PROVIDER_VERIFIED BROKER_VERIFIED CO
280
277
 
281
278
  ### Status meanings
282
279
 
283
- | Status | Contracted in `pacts/` | In ledger window | Meaning |
284
- |--------|------------------------|------------------|-----------------------------------------------|
285
- | OK | yes | yes | Trusted path, exercised this window |
286
- | STALE | yes | no | Pact exists but no test exercised it |
287
- | SHADOW | no | yes | A wrapped call ran with no matching contract |
280
+ | Status | Contracted in `pacts/` | In ledger window | Meaning |
281
+ | ------ | ---------------------- | ---------------- | -------------------------------------------- |
282
+ | OK | yes | yes | Trusted path, exercised this window |
283
+ | STALE | yes | no | Pact exists but no test exercised it |
284
+ | SHADOW | no | yes | A wrapped call ran with no matching contract |
288
285
 
289
286
  ### Flags
290
287
 
291
- | Flag | Default | Description |
292
- |---------------------|----------------|----------------------------------------------------------------------------|
293
- | `--pacts <dir>` | `./pacts` | Directory containing pact files |
294
- | `--ledger <dir>` | `.autotel-pact`| Directory containing ledger files |
295
- | `--window <days>` | `14` | Ledger lookback window |
296
- | `--gate` | off | Exit 1 if any contracted interaction was not **seen in test** |
297
- | `--gate=strict` | off | Also exit 1 on observations with no matching contract |
298
- | `--gate=broker` | off | Exit 1 if broker configured and any contracted row lacks broker proof |
299
- | `--broker-url` | env | Pact Broker base URL (`PACT_BROKER_BASE_URL`) |
300
- | `--broker-token` | env | Bearer token (`PACT_BROKER_TOKEN`) |
301
- | `--json` | off | Machine-readable JSON (`test_seen`, `prod_seen`, …) |
302
- | `--help` | | Show help |
288
+ | Flag | Default | Description |
289
+ | ----------------- | --------------- | --------------------------------------------------------------------- |
290
+ | `--pacts <dir>` | `./pacts` | Directory containing pact files |
291
+ | `--ledger <dir>` | `.autotel-pact` | Directory containing ledger files |
292
+ | `--window <days>` | `14` | Ledger lookback window |
293
+ | `--gate` | off | Exit 1 if any contracted interaction was not **seen in test** |
294
+ | `--gate=strict` | off | Also exit 1 on observations with no matching contract |
295
+ | `--gate=broker` | off | Exit 1 if broker configured and any contracted row lacks broker proof |
296
+ | `--broker-url` | env | Pact Broker base URL (`PACT_BROKER_BASE_URL`) |
297
+ | `--broker-token` | env | Bearer token (`PACT_BROKER_TOKEN`) |
298
+ | `--json` | off | Machine-readable JSON (`test_seen`, `prod_seen`, …) |
299
+ | `--help` | | Show help |
303
300
 
304
301
  ### CI gating
305
302
 
@@ -323,7 +320,7 @@ A few things to know before you turn `--gate` on for the whole org.
323
320
 
324
321
  A STALE row does **not** mean "never fires in production". It means no consumer test exercised this interaction in the window. Check **PROD_SEEN** for production evidence (requires explicit span tags + processor).
325
322
 
326
- Honest one-liner: *"verified by Pact, exercised by our tests, observed in production only when we tag spans."*
323
+ Honest one-liner: _"verified by Pact, exercised by our tests, observed in production only when we tag spans."_
327
324
 
328
325
  ### Coverage equals wrapped tests
329
326
 
@@ -351,21 +348,21 @@ A contract that should not run anymore is a contract you should delete, not gate
351
348
 
352
349
  ## Attribute schema
353
350
 
354
- | Attribute | Type | Description |
355
- |---------------------------------|----------|---------------------------------------------------|
356
- | `pact.consumer` | string | Consumer name from Pact config |
357
- | `pact.provider` | string | Provider name from Pact config |
358
- | `pact.kind` | string | `"message"` (v1) or `"http"` (v2) |
359
- | `pact.interaction.description` | string | The `expectsToReceive` or `uponReceiving` text |
360
- | `pact.interaction.states` | string[] | Provider state names from `.given()` |
361
- | `pact.contract.file` | string? | Path to the pact file (when supplied) |
362
- | `pact.outcome` | string | `"passed"` or `"failed"` |
351
+ | Attribute | Type | Description |
352
+ | ------------------------------ | -------- | ---------------------------------------------- |
353
+ | `pact.consumer` | string | Consumer name from Pact config |
354
+ | `pact.provider` | string | Provider name from Pact config |
355
+ | `pact.kind` | string | `"message"` (v1) or `"http"` (v2) |
356
+ | `pact.interaction.description` | string | The `expectsToReceive` or `uponReceiving` text |
357
+ | `pact.interaction.states` | string[] | Provider state names from `.given()` |
358
+ | `pact.contract.file` | string? | Path to the pact file (when supplied) |
359
+ | `pact.outcome` | string | `"passed"` or `"failed"` |
363
360
 
364
361
  The `pact.*` namespace is currently unclaimed in OTel semantic conventions; we plan to propose it upstream once the package has traction. Pin to a specific `autotel-pact` version if you build dashboards against these attributes.
365
362
 
366
363
  ## How this is different from…
367
364
 
368
- - **Pact's own `--enable-otel`** flags emit telemetry about the *Pact tooling itself* (broker calls, verifier execution). `autotel-pact` operates at the **interaction wrapper layer in your code**, capturing whether the contract interactions ran.
365
+ - **Pact's own `--enable-otel`** flags emit telemetry about the _Pact tooling itself_ (broker calls, verifier execution). `autotel-pact` operates at the **interaction wrapper layer in your code**, capturing whether the contract interactions ran.
369
366
  - **Tracetest** lets you write tests that assert against traces. `autotel-pact` does not change how you write tests. It audits which of your existing Pact contracts have evidence of being exercised.
370
367
  - **Pact Broker / PactFlow `can-i-deploy`** verifies version compatibility. `autotel-pact` adds the orthogonal question: have these verified contracts run recently?
371
368
 
@@ -1 +1 @@
1
- {"version":3,"file":"attrs-DbCs1ou0.cjs","names":[],"sources":["../src/attrs.ts"],"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"],"mappings":";;;;;;;AAOA,MAAa,aAAa;CACxB,UAAU;CACV,UAAU;CACV,MAAM;CACN,yBAAyB;CACzB,gBAAgB;CAChB,oBAAoB;CACpB,eAAe;CACf,SAAS;AACX;;;;;AAQA,SAAgB,oBACd,MACA,OAAkC,CAAC,GACA;CACnC,MAAM,QAA2C;GAC9C,WAAW,WAAW,KAAK;GAC3B,WAAW,WAAW,KAAK;GAC3B,WAAW,OAAO,KAAK;GACvB,WAAW,0BAA0B,KAAK;GAC1C,WAAW,qBAAqB,KAAK;CACxC;CACA,IAAI,KAAK,cACP,MAAM,WAAW,iBAAiB,KAAK;CAEzC,IAAI,KAAK,eACP,MAAM,WAAW,kBAAkB,KAAK;CAE1C,OAAO;AACT;;;;;AAMA,SAAgB,iBACd,SACwB;CACxB,OAAO,GAAG,WAAW,UAAU,QAAQ;AACzC"}
1
+ {"version":3,"file":"attrs-DbCs1ou0.cjs","names":[],"sources":["../src/attrs.ts"],"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(outcome: PactOutcome): Record<string, string> {\n return { [PACT_ATTRS.OUTCOME]: outcome };\n}\n"],"mappings":";;;;;;;AAOA,MAAa,aAAa;CACxB,UAAU;CACV,UAAU;CACV,MAAM;CACN,yBAAyB;CACzB,gBAAgB;CAChB,oBAAoB;CACpB,eAAe;CACf,SAAS;AACX;;;;;AAQA,SAAgB,oBACd,MACA,OAAkC,CAAC,GACA;CACnC,MAAM,QAA2C;GAC9C,WAAW,WAAW,KAAK;GAC3B,WAAW,WAAW,KAAK;GAC3B,WAAW,OAAO,KAAK;GACvB,WAAW,0BAA0B,KAAK;GAC1C,WAAW,qBAAqB,KAAK;CACxC;CACA,IAAI,KAAK,cACP,MAAM,WAAW,iBAAiB,KAAK;CAEzC,IAAI,KAAK,eACP,MAAM,WAAW,kBAAkB,KAAK;CAE1C,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,SAA8C;CAC7E,OAAO,GAAG,WAAW,UAAU,QAAQ;AACzC"}
@@ -1 +1 @@
1
- {"version":3,"file":"attrs-ElwK54Ym.js","names":[],"sources":["../src/attrs.ts"],"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"],"mappings":";;;;;;AAOA,MAAa,aAAa;CACxB,UAAU;CACV,UAAU;CACV,MAAM;CACN,yBAAyB;CACzB,gBAAgB;CAChB,oBAAoB;CACpB,eAAe;CACf,SAAS;AACX;;;;;AAQA,SAAgB,oBACd,MACA,OAAkC,CAAC,GACA;CACnC,MAAM,QAA2C;GAC9C,WAAW,WAAW,KAAK;GAC3B,WAAW,WAAW,KAAK;GAC3B,WAAW,OAAO,KAAK;GACvB,WAAW,0BAA0B,KAAK;GAC1C,WAAW,qBAAqB,KAAK;CACxC;CACA,IAAI,KAAK,cACP,MAAM,WAAW,iBAAiB,KAAK;CAEzC,IAAI,KAAK,eACP,MAAM,WAAW,kBAAkB,KAAK;CAE1C,OAAO;AACT;;;;;AAMA,SAAgB,iBACd,SACwB;CACxB,OAAO,GAAG,WAAW,UAAU,QAAQ;AACzC"}
1
+ {"version":3,"file":"attrs-ElwK54Ym.js","names":[],"sources":["../src/attrs.ts"],"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(outcome: PactOutcome): Record<string, string> {\n return { [PACT_ATTRS.OUTCOME]: outcome };\n}\n"],"mappings":";;;;;;AAOA,MAAa,aAAa;CACxB,UAAU;CACV,UAAU;CACV,MAAM;CACN,yBAAyB;CACzB,gBAAgB;CAChB,oBAAoB;CACpB,eAAe;CACf,SAAS;AACX;;;;;AAQA,SAAgB,oBACd,MACA,OAAkC,CAAC,GACA;CACnC,MAAM,QAA2C;GAC9C,WAAW,WAAW,KAAK;GAC3B,WAAW,WAAW,KAAK;GAC3B,WAAW,OAAO,KAAK;GACvB,WAAW,0BAA0B,KAAK;GAC1C,WAAW,qBAAqB,KAAK;CACxC;CACA,IAAI,KAAK,cACP,MAAM,WAAW,iBAAiB,KAAK;CAEzC,IAAI,KAAK,eACP,MAAM,WAAW,kBAAkB,KAAK;CAE1C,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,SAA8C;CAC7E,OAAO,GAAG,WAAW,UAAU,QAAQ;AACzC"}
@@ -1 +1 @@
1
- {"version":3,"file":"audit.cjs","names":["isProviderVerificationRun","isInteractionLedgerEntry","AUDIT_MATRIX_SPEC","path","listPactFiles","parsePactFile","interactionsFromPactFile","readLedger","fetchBrokerVerifications"],"sources":["../src/audit.ts"],"sourcesContent":["import path from 'node:path';\nimport { fetchBrokerVerifications, type BrokerConfig } from './broker.js';\nimport { readLedger, type LedgerOptions } from './ledger.js';\nimport {\n interactionsFromPactFile,\n listPactFiles,\n parsePactFile,\n} from './pact-file.js';\nimport {\n AUDIT_MATRIX_SPEC,\n isInteractionLedgerEntry,\n isProviderVerificationRun,\n type AuditMatrix,\n type AuditRow,\n type BrokerVerification,\n type InteractionLedgerEntry,\n type LedgerRecord,\n type PactInteractionKey,\n type ProviderVerificationRunEntry,\n} from './types.js';\n\nexport interface AuditOptions extends LedgerOptions {\n pactsDir?: string;\n windowDays?: number;\n broker?: BrokerConfig;\n}\n\nconst DEFAULT_PACTS_DIR = './pacts';\nconst DEFAULT_WINDOW_DAYS = 14;\n\nexport function keyOf(k: PactInteractionKey): string {\n const identity = k.interactionId ?? k.interaction;\n return `${k.consumer}::${k.provider}::${k.kind}::${identity}`;\n}\n\nfunction pairKey(consumer: string, provider: string): string {\n return `${consumer}::${provider}`;\n}\n\nfunction inWindow(observedAt: string, cutoff: number): boolean {\n const t = Date.parse(observedAt);\n return Number.isFinite(t) && t >= cutoff;\n}\n\n/**\n * Compute the audit matrix from pact files, ledger, and optional broker data.\n */\nexport function computeAuditMatrix(input: {\n contracted: PactInteractionKey[];\n ledger: LedgerRecord[];\n brokerVerifications?: BrokerVerification[];\n windowDays?: number;\n now?: Date;\n}): AuditMatrix {\n const windowDays = input.windowDays ?? DEFAULT_WINDOW_DAYS;\n const now = input.now ?? new Date();\n const cutoff = now.getTime() - windowDays * 24 * 60 * 60 * 1000;\n\n const verificationFailures: ProviderVerificationRunEntry[] = [];\n const recentInteractions: InteractionLedgerEntry[] = [];\n\n for (const entry of input.ledger) {\n if (!inWindow(entry.observed_at, cutoff)) continue;\n if (isProviderVerificationRun(entry)) {\n verificationFailures.push(entry);\n continue;\n }\n if (isInteractionLedgerEntry(entry)) {\n recentInteractions.push(entry);\n }\n }\n\n const brokerByPair = new Map<string, BrokerVerification>();\n for (const b of input.brokerVerifications ?? []) {\n brokerByPair.set(pairKey(b.consumer, b.provider), b);\n }\n\n const testSeenByKey = new Map<string, InteractionLedgerEntry[]>();\n const prodSeenByKey = new Map<string, InteractionLedgerEntry[]>();\n const providerVerifiedByKey = new Map<string, InteractionLedgerEntry[]>();\n const anyObservedByKey = new Map<string, InteractionLedgerEntry[]>();\n\n for (const entry of recentInteractions) {\n const k = keyOf({\n consumer: entry.consumer,\n provider: entry.provider,\n interaction: entry.interaction,\n kind: entry.kind,\n interactionId: entry.interaction_id,\n });\n\n const push = (map: Map<string, InteractionLedgerEntry[]>) => {\n const arr = map.get(k) ?? [];\n arr.push(entry);\n map.set(k, arr);\n };\n\n push(anyObservedByKey);\n\n if (entry.source === 'test' && entry.role === 'consumer') {\n push(testSeenByKey);\n }\n if (entry.source === 'production') {\n push(prodSeenByKey);\n }\n if (entry.role === 'provider' && entry.outcome === 'passed') {\n push(providerVerifiedByKey);\n }\n }\n\n const contractedByKey = new Map<string, PactInteractionKey>();\n for (const c of input.contracted) {\n contractedByKey.set(keyOf(c), c);\n }\n\n const rows: AuditRow[] = [];\n\n function pushRow(parts: PactInteractionKey, k: string, isContracted: boolean): void {\n const testObs = testSeenByKey.get(k) ?? [];\n const prodObs = prodSeenByKey.get(k) ?? [];\n const providerObs = providerVerifiedByKey.get(k) ?? [];\n const allObs = anyObservedByKey.get(k) ?? [];\n const latest = allObs.toSorted((a, b) =>\n b.observed_at.localeCompare(a.observed_at),\n )[0];\n const broker = brokerByPair.get(pairKey(parts.consumer, parts.provider));\n\n rows.push({\n consumer: parts.consumer,\n provider: parts.provider,\n interaction: parts.interaction,\n interaction_id: parts.interactionId ?? latest?.interaction_id,\n kind: parts.kind,\n contracted: isContracted,\n observed: testObs.length > 0 || prodObs.length > 0,\n test_seen: testObs.length > 0,\n prod_seen: prodObs.length > 0,\n provider_verified: providerObs.length > 0,\n broker_verified: broker?.success === true,\n broker_verified_at: broker?.verifiedAt,\n broker_error: broker?.error,\n last_observed_at: latest?.observed_at,\n last_outcome: latest?.outcome,\n });\n }\n\n for (const [k, contracted] of contractedByKey) {\n pushRow(contracted, k, true);\n }\n for (const [k, observations] of anyObservedByKey) {\n if (contractedByKey.has(k)) continue;\n const first = observations[0]!;\n pushRow(\n {\n consumer: first.consumer,\n provider: first.provider,\n interaction: first.interaction,\n kind: first.kind,\n interactionId: first.interaction_id,\n },\n k,\n false,\n );\n }\n\n rows.sort((a, b) =>\n a.consumer.localeCompare(b.consumer) ||\n a.provider.localeCompare(b.provider) ||\n a.interaction.localeCompare(b.interaction),\n );\n\n const counts = {\n total: rows.length,\n contracted: rows.filter((r) => r.contracted).length,\n observed: rows.filter((r) => r.observed).length,\n contracted_and_test_seen: rows.filter((r) => r.contracted && r.test_seen).length,\n contracted_not_test_seen: rows.filter((r) => r.contracted && !r.test_seen).length,\n test_or_prod_seen_not_contracted: rows.filter(\n (r) => !r.contracted && (r.test_seen || r.prod_seen),\n ).length,\n test_seen: rows.filter((r) => r.test_seen).length,\n prod_seen: rows.filter((r) => r.prod_seen).length,\n provider_verified: rows.filter((r) => r.provider_verified).length,\n broker_verified: rows.filter((r) => r.broker_verified).length,\n };\n\n const matrix: AuditMatrix = {\n spec: AUDIT_MATRIX_SPEC,\n rows,\n counts,\n window_days: windowDays,\n generated_at: now.toISOString(),\n };\n\n if (verificationFailures.length > 0) {\n matrix.verification_failures = verificationFailures;\n }\n\n return matrix;\n}\n\nexport async function runAudit(opts: AuditOptions = {}): Promise<AuditMatrix> {\n const pactsDir = path.resolve(process.cwd(), opts.pactsDir ?? DEFAULT_PACTS_DIR);\n const contracted: PactInteractionKey[] = [];\n const pairs = new Set<string>();\n\n for (const file of listPactFiles(pactsDir)) {\n const pact = parsePactFile(file);\n if (!pact) continue;\n const interactions = interactionsFromPactFile(pact);\n contracted.push(...interactions);\n const consumer = pact.consumer?.name;\n const provider = pact.provider?.name;\n if (consumer && provider) {\n pairs.add(pairKey(consumer, provider));\n }\n }\n\n const ledger = readLedger(opts);\n\n let brokerVerifications: BrokerVerification[] | undefined;\n if (opts.broker) {\n brokerVerifications = await fetchBrokerVerifications(opts.broker, [...pairs].map((p) => {\n const [consumer, provider] = p.split('::');\n return { consumer: consumer!, provider: provider! };\n }));\n }\n\n return computeAuditMatrix({\n contracted,\n ledger,\n brokerVerifications,\n windowDays: opts.windowDays,\n });\n}\n\n/** Sync audit without broker (backward compatible for tests). */\nexport function runAuditSync(opts: Omit<AuditOptions, 'broker'> = {}): AuditMatrix {\n const pactsDir = path.resolve(process.cwd(), opts.pactsDir ?? DEFAULT_PACTS_DIR);\n const contracted: PactInteractionKey[] = [];\n for (const file of listPactFiles(pactsDir)) {\n const pact = parsePactFile(file);\n if (pact) contracted.push(...interactionsFromPactFile(pact));\n }\n const ledger = readLedger(opts);\n return computeAuditMatrix({\n contracted,\n ledger,\n windowDays: opts.windowDays,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"audit.cjs","names":["isProviderVerificationRun","isInteractionLedgerEntry","AUDIT_MATRIX_SPEC","path","listPactFiles","parsePactFile","interactionsFromPactFile","readLedger","fetchBrokerVerifications"],"sources":["../src/audit.ts"],"sourcesContent":["import path from 'node:path';\nimport { fetchBrokerVerifications, type BrokerConfig } from './broker.js';\nimport { readLedger, type LedgerOptions } from './ledger.js';\nimport {\n interactionsFromPactFile,\n listPactFiles,\n parsePactFile,\n} from './pact-file.js';\nimport {\n AUDIT_MATRIX_SPEC,\n isInteractionLedgerEntry,\n isProviderVerificationRun,\n type AuditMatrix,\n type AuditRow,\n type BrokerVerification,\n type InteractionLedgerEntry,\n type LedgerRecord,\n type PactInteractionKey,\n type ProviderVerificationRunEntry,\n} from './types.js';\n\nexport interface AuditOptions extends LedgerOptions {\n pactsDir?: string;\n windowDays?: number;\n broker?: BrokerConfig;\n}\n\nconst DEFAULT_PACTS_DIR = './pacts';\nconst DEFAULT_WINDOW_DAYS = 14;\n\nexport function keyOf(k: PactInteractionKey): string {\n const identity = k.interactionId ?? k.interaction;\n return `${k.consumer}::${k.provider}::${k.kind}::${identity}`;\n}\n\nfunction pairKey(consumer: string, provider: string): string {\n return `${consumer}::${provider}`;\n}\n\nfunction inWindow(observedAt: string, cutoff: number): boolean {\n const t = Date.parse(observedAt);\n return Number.isFinite(t) && t >= cutoff;\n}\n\n/**\n * Compute the audit matrix from pact files, ledger, and optional broker data.\n */\nexport function computeAuditMatrix(input: {\n contracted: PactInteractionKey[];\n ledger: LedgerRecord[];\n brokerVerifications?: BrokerVerification[];\n windowDays?: number;\n now?: Date;\n}): AuditMatrix {\n const windowDays = input.windowDays ?? DEFAULT_WINDOW_DAYS;\n const now = input.now ?? new Date();\n const cutoff = now.getTime() - windowDays * 24 * 60 * 60 * 1000;\n\n const verificationFailures: ProviderVerificationRunEntry[] = [];\n const recentInteractions: InteractionLedgerEntry[] = [];\n\n for (const entry of input.ledger) {\n if (!inWindow(entry.observed_at, cutoff)) continue;\n if (isProviderVerificationRun(entry)) {\n verificationFailures.push(entry);\n continue;\n }\n if (isInteractionLedgerEntry(entry)) {\n recentInteractions.push(entry);\n }\n }\n\n const brokerByPair = new Map<string, BrokerVerification>();\n for (const b of input.brokerVerifications ?? []) {\n brokerByPair.set(pairKey(b.consumer, b.provider), b);\n }\n\n const testSeenByKey = new Map<string, InteractionLedgerEntry[]>();\n const prodSeenByKey = new Map<string, InteractionLedgerEntry[]>();\n const providerVerifiedByKey = new Map<string, InteractionLedgerEntry[]>();\n const anyObservedByKey = new Map<string, InteractionLedgerEntry[]>();\n\n for (const entry of recentInteractions) {\n const k = keyOf({\n consumer: entry.consumer,\n provider: entry.provider,\n interaction: entry.interaction,\n kind: entry.kind,\n interactionId: entry.interaction_id,\n });\n\n const push = (map: Map<string, InteractionLedgerEntry[]>) => {\n const arr = map.get(k) ?? [];\n arr.push(entry);\n map.set(k, arr);\n };\n\n push(anyObservedByKey);\n\n if (entry.source === 'test' && entry.role === 'consumer') {\n push(testSeenByKey);\n }\n if (entry.source === 'production') {\n push(prodSeenByKey);\n }\n if (entry.role === 'provider' && entry.outcome === 'passed') {\n push(providerVerifiedByKey);\n }\n }\n\n const contractedByKey = new Map<string, PactInteractionKey>();\n for (const c of input.contracted) {\n contractedByKey.set(keyOf(c), c);\n }\n\n const rows: AuditRow[] = [];\n\n function pushRow(\n parts: PactInteractionKey,\n k: string,\n isContracted: boolean,\n ): void {\n const testObs = testSeenByKey.get(k) ?? [];\n const prodObs = prodSeenByKey.get(k) ?? [];\n const providerObs = providerVerifiedByKey.get(k) ?? [];\n const allObs = anyObservedByKey.get(k) ?? [];\n const latest = allObs.toSorted((a, b) =>\n b.observed_at.localeCompare(a.observed_at),\n )[0];\n const broker = brokerByPair.get(pairKey(parts.consumer, parts.provider));\n\n rows.push({\n consumer: parts.consumer,\n provider: parts.provider,\n interaction: parts.interaction,\n interaction_id: parts.interactionId ?? latest?.interaction_id,\n kind: parts.kind,\n contracted: isContracted,\n observed: testObs.length > 0 || prodObs.length > 0,\n test_seen: testObs.length > 0,\n prod_seen: prodObs.length > 0,\n provider_verified: providerObs.length > 0,\n broker_verified: broker?.success === true,\n broker_verified_at: broker?.verifiedAt,\n broker_error: broker?.error,\n last_observed_at: latest?.observed_at,\n last_outcome: latest?.outcome,\n });\n }\n\n for (const [k, contracted] of contractedByKey) {\n pushRow(contracted, k, true);\n }\n for (const [k, observations] of anyObservedByKey) {\n if (contractedByKey.has(k)) continue;\n const first = observations[0]!;\n pushRow(\n {\n consumer: first.consumer,\n provider: first.provider,\n interaction: first.interaction,\n kind: first.kind,\n interactionId: first.interaction_id,\n },\n k,\n false,\n );\n }\n\n rows.sort(\n (a, b) =>\n a.consumer.localeCompare(b.consumer) ||\n a.provider.localeCompare(b.provider) ||\n a.interaction.localeCompare(b.interaction),\n );\n\n const counts = {\n total: rows.length,\n contracted: rows.filter((r) => r.contracted).length,\n observed: rows.filter((r) => r.observed).length,\n contracted_and_test_seen: rows.filter((r) => r.contracted && r.test_seen)\n .length,\n contracted_not_test_seen: rows.filter((r) => r.contracted && !r.test_seen)\n .length,\n test_or_prod_seen_not_contracted: rows.filter(\n (r) => !r.contracted && (r.test_seen || r.prod_seen),\n ).length,\n test_seen: rows.filter((r) => r.test_seen).length,\n prod_seen: rows.filter((r) => r.prod_seen).length,\n provider_verified: rows.filter((r) => r.provider_verified).length,\n broker_verified: rows.filter((r) => r.broker_verified).length,\n };\n\n const matrix: AuditMatrix = {\n spec: AUDIT_MATRIX_SPEC,\n rows,\n counts,\n window_days: windowDays,\n generated_at: now.toISOString(),\n };\n\n if (verificationFailures.length > 0) {\n matrix.verification_failures = verificationFailures;\n }\n\n return matrix;\n}\n\nexport async function runAudit(opts: AuditOptions = {}): Promise<AuditMatrix> {\n const pactsDir = path.resolve(\n process.cwd(),\n opts.pactsDir ?? DEFAULT_PACTS_DIR,\n );\n const contracted: PactInteractionKey[] = [];\n const pairs = new Set<string>();\n\n for (const file of listPactFiles(pactsDir)) {\n const pact = parsePactFile(file);\n if (!pact) continue;\n const interactions = interactionsFromPactFile(pact);\n contracted.push(...interactions);\n const consumer = pact.consumer?.name;\n const provider = pact.provider?.name;\n if (consumer && provider) {\n pairs.add(pairKey(consumer, provider));\n }\n }\n\n const ledger = readLedger(opts);\n\n let brokerVerifications: BrokerVerification[] | undefined;\n if (opts.broker) {\n brokerVerifications = await fetchBrokerVerifications(\n opts.broker,\n [...pairs].map((p) => {\n const [consumer, provider] = p.split('::');\n return { consumer: consumer!, provider: provider! };\n }),\n );\n }\n\n return computeAuditMatrix({\n contracted,\n ledger,\n brokerVerifications,\n windowDays: opts.windowDays,\n });\n}\n\n/** Sync audit without broker (backward compatible for tests). */\nexport function runAuditSync(\n opts: Omit<AuditOptions, 'broker'> = {},\n): AuditMatrix {\n const pactsDir = path.resolve(\n process.cwd(),\n opts.pactsDir ?? DEFAULT_PACTS_DIR,\n );\n const contracted: PactInteractionKey[] = [];\n for (const file of listPactFiles(pactsDir)) {\n const pact = parsePactFile(file);\n if (pact) contracted.push(...interactionsFromPactFile(pact));\n }\n const ledger = readLedger(opts);\n return computeAuditMatrix({\n contracted,\n ledger,\n windowDays: opts.windowDays,\n });\n}\n"],"mappings":";;;;;;;;AA2BA,MAAM,oBAAoB;AAC1B,MAAM,sBAAsB;AAE5B,SAAgB,MAAM,GAA+B;CACnD,MAAM,WAAW,EAAE,iBAAiB,EAAE;CACtC,OAAO,GAAG,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI;AACrD;AAEA,SAAS,QAAQ,UAAkB,UAA0B;CAC3D,OAAO,GAAG,SAAS,IAAI;AACzB;AAEA,SAAS,SAAS,YAAoB,QAAyB;CAC7D,MAAM,IAAI,KAAK,MAAM,UAAU;CAC/B,OAAO,OAAO,SAAS,CAAC,KAAK,KAAK;AACpC;;;;AAKA,SAAgB,mBAAmB,OAMnB;CACd,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,MAAM,MAAM,uBAAO,IAAI,KAAK;CAClC,MAAM,SAAS,IAAI,QAAQ,IAAI,aAAa,KAAK,KAAK,KAAK;CAE3D,MAAM,uBAAuD,CAAC;CAC9D,MAAM,qBAA+C,CAAC;CAEtD,KAAK,MAAM,SAAS,MAAM,QAAQ;EAChC,IAAI,CAAC,SAAS,MAAM,aAAa,MAAM,GAAG;EAC1C,IAAIA,yCAA0B,KAAK,GAAG;GACpC,qBAAqB,KAAK,KAAK;GAC/B;EACF;EACA,IAAIC,wCAAyB,KAAK,GAChC,mBAAmB,KAAK,KAAK;CAEjC;CAEA,MAAM,+BAAe,IAAI,IAAgC;CACzD,KAAK,MAAM,KAAK,MAAM,uBAAuB,CAAC,GAC5C,aAAa,IAAI,QAAQ,EAAE,UAAU,EAAE,QAAQ,GAAG,CAAC;CAGrD,MAAM,gCAAgB,IAAI,IAAsC;CAChE,MAAM,gCAAgB,IAAI,IAAsC;CAChE,MAAM,wCAAwB,IAAI,IAAsC;CACxE,MAAM,mCAAmB,IAAI,IAAsC;CAEnE,KAAK,MAAM,SAAS,oBAAoB;EACtC,MAAM,IAAI,MAAM;GACd,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,MAAM,MAAM;GACZ,eAAe,MAAM;EACvB,CAAC;EAED,MAAM,QAAQ,QAA+C;GAC3D,MAAM,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC;GAC3B,IAAI,KAAK,KAAK;GACd,IAAI,IAAI,GAAG,GAAG;EAChB;EAEA,KAAK,gBAAgB;EAErB,IAAI,MAAM,WAAW,UAAU,MAAM,SAAS,YAC5C,KAAK,aAAa;EAEpB,IAAI,MAAM,WAAW,cACnB,KAAK,aAAa;EAEpB,IAAI,MAAM,SAAS,cAAc,MAAM,YAAY,UACjD,KAAK,qBAAqB;CAE9B;CAEA,MAAM,kCAAkB,IAAI,IAAgC;CAC5D,KAAK,MAAM,KAAK,MAAM,YACpB,gBAAgB,IAAI,MAAM,CAAC,GAAG,CAAC;CAGjC,MAAM,OAAmB,CAAC;CAE1B,SAAS,QACP,OACA,GACA,cACM;EACN,MAAM,UAAU,cAAc,IAAI,CAAC,KAAK,CAAC;EACzC,MAAM,UAAU,cAAc,IAAI,CAAC,KAAK,CAAC;EACzC,MAAM,cAAc,sBAAsB,IAAI,CAAC,KAAK,CAAC;EAErD,MAAM,UADS,iBAAiB,IAAI,CAAC,KAAK,CAAC,EACtB,CAAC,UAAU,GAAG,MACjC,EAAE,YAAY,cAAc,EAAE,WAAW,CAC3C,CAAC,CAAC;EACF,MAAM,SAAS,aAAa,IAAI,QAAQ,MAAM,UAAU,MAAM,QAAQ,CAAC;EAEvE,KAAK,KAAK;GACR,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,gBAAgB,MAAM,iBAAiB,QAAQ;GAC/C,MAAM,MAAM;GACZ,YAAY;GACZ,UAAU,QAAQ,SAAS,KAAK,QAAQ,SAAS;GACjD,WAAW,QAAQ,SAAS;GAC5B,WAAW,QAAQ,SAAS;GAC5B,mBAAmB,YAAY,SAAS;GACxC,iBAAiB,QAAQ,YAAY;GACrC,oBAAoB,QAAQ;GAC5B,cAAc,QAAQ;GACtB,kBAAkB,QAAQ;GAC1B,cAAc,QAAQ;EACxB,CAAC;CACH;CAEA,KAAK,MAAM,CAAC,GAAG,eAAe,iBAC5B,QAAQ,YAAY,GAAG,IAAI;CAE7B,KAAK,MAAM,CAAC,GAAG,iBAAiB,kBAAkB;EAChD,IAAI,gBAAgB,IAAI,CAAC,GAAG;EAC5B,MAAM,QAAQ,aAAa;EAC3B,QACE;GACE,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,MAAM,MAAM;GACZ,eAAe,MAAM;EACvB,GACA,GACA,KACF;CACF;CAEA,KAAK,MACF,GAAG,MACF,EAAE,SAAS,cAAc,EAAE,QAAQ,KACnC,EAAE,SAAS,cAAc,EAAE,QAAQ,KACnC,EAAE,YAAY,cAAc,EAAE,WAAW,CAC7C;CAmBA,MAAM,SAAsB;EAC1B,MAAMC;EACN;EACA;GAnBA,OAAO,KAAK;GACZ,YAAY,KAAK,QAAQ,MAAM,EAAE,UAAU,CAAC,CAAC;GAC7C,UAAU,KAAK,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC;GACzC,0BAA0B,KAAK,QAAQ,MAAM,EAAE,cAAc,EAAE,SAAS,CAAC,CACtE;GACH,0BAA0B,KAAK,QAAQ,MAAM,EAAE,cAAc,CAAC,EAAE,SAAS,CAAC,CACvE;GACH,kCAAkC,KAAK,QACpC,MAAM,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,UAC5C,CAAC,CAAC;GACF,WAAW,KAAK,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;GAC3C,WAAW,KAAK,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;GAC3C,mBAAmB,KAAK,QAAQ,MAAM,EAAE,iBAAiB,CAAC,CAAC;GAC3D,iBAAiB,KAAK,QAAQ,MAAM,EAAE,eAAe,CAAC,CAAC;EAMlD;EACL,aAAa;EACb,cAAc,IAAI,YAAY;CAChC;CAEA,IAAI,qBAAqB,SAAS,GAChC,OAAO,wBAAwB;CAGjC,OAAO;AACT;AAEA,eAAsB,SAAS,OAAqB,CAAC,GAAyB;CAC5E,MAAM,WAAWC,kBAAK,QACpB,QAAQ,IAAI,GACZ,KAAK,YAAY,iBACnB;CACA,MAAM,aAAmC,CAAC;CAC1C,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,MAAM,QAAQC,gCAAc,QAAQ,GAAG;EAC1C,MAAM,OAAOC,gCAAc,IAAI;EAC/B,IAAI,CAAC,MAAM;EACX,MAAM,eAAeC,2CAAyB,IAAI;EAClD,WAAW,KAAK,GAAG,YAAY;EAC/B,MAAM,WAAW,KAAK,UAAU;EAChC,MAAM,WAAW,KAAK,UAAU;EAChC,IAAI,YAAY,UACd,MAAM,IAAI,QAAQ,UAAU,QAAQ,CAAC;CAEzC;CAEA,MAAM,SAASC,0BAAW,IAAI;CAE9B,IAAI;CACJ,IAAI,KAAK,QACP,sBAAsB,MAAMC,wCAC1B,KAAK,QACL,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,MAAM;EACpB,MAAM,CAAC,UAAU,YAAY,EAAE,MAAM,IAAI;EACzC,OAAO;GAAY;GAAqB;EAAU;CACpD,CAAC,CACH;CAGF,OAAO,mBAAmB;EACxB;EACA;EACA;EACA,YAAY,KAAK;CACnB,CAAC;AACH;;AAGA,SAAgB,aACd,OAAqC,CAAC,GACzB;CACb,MAAM,WAAWL,kBAAK,QACpB,QAAQ,IAAI,GACZ,KAAK,YAAY,iBACnB;CACA,MAAM,aAAmC,CAAC;CAC1C,KAAK,MAAM,QAAQC,gCAAc,QAAQ,GAAG;EAC1C,MAAM,OAAOC,gCAAc,IAAI;EAC/B,IAAI,MAAM,WAAW,KAAK,GAAGC,2CAAyB,IAAI,CAAC;CAC7D;CAEA,OAAO,mBAAmB;EACxB;EACA,QAHaC,0BAAW,IAGnB;EACL,YAAY,KAAK;CACnB,CAAC;AACH"}
package/dist/audit.d.cts CHANGED
@@ -1,7 +1,6 @@
1
- import { d as PactInteractionKey, i as BrokerVerification, n as AuditMatrix, s as LedgerRecord } from "./types-D_0kgK2e.cjs";
1
+ import { d as PactInteractionKey, i as BrokerVerification, n as AuditMatrix, s as LedgerRecord } from "./types-DpHqSzxb.cjs";
2
2
  import { BrokerConfig } from "./broker.cjs";
3
- import { t as LedgerOptions } from "./ledger-BAFsRX6y.cjs";
4
-
3
+ import { t as LedgerOptions } from "./ledger-yzVOA9iE.cjs";
5
4
  //#region src/audit.d.ts
6
5
  interface AuditOptions extends LedgerOptions {
7
6
  pactsDir?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.cts","names":[],"sources":["../src/audit.ts"],"mappings":";;;;;UAqBiB,YAAA,SAAqB,aAAa;EACjD,QAAA;EACA,UAAA;EACA,MAAA,GAAS,YAAA;AAAA;AAAA,iBAMK,KAAA,CAAM,CAAqB,EAAlB,kBAAkB;;;;iBAiB3B,kBAAA,CAAmB,KAAA;EACjC,UAAA,EAAY,kBAAA;EACZ,MAAA,EAAQ,YAAA;EACR,mBAAA,GAAsB,kBAAA;EACtB,UAAA;EACA,GAAA,GAAM,IAAA;AAAA,IACJ,WAAA;AAAA,iBAoJkB,QAAA,CAAS,IAAA,GAAM,YAAA,GAAoB,OAAA,CAAQ,WAAA;AA3KtB;AAAA,iBA+M3B,YAAA,CAAa,IAAA,GAAM,IAAA,CAAK,YAAA,cAA+B,WAAA"}
1
+ {"version":3,"file":"audit.d.cts","names":[],"sources":["../src/audit.ts"],"mappings":";;;;UAqBiB,qBAAqB;EACpC;EACA;EACA,SAAS;;iBAMK,MAAM,GAAG;;;;iBAiBT,mBAAmB;EACjC,YAAY;EACZ,QAAQ;EACR,sBAAsB;EACtB;EACA,MAAM;IACJ;iBA2JkB,SAAS,OAAM,eAAoB,QAAQ;;iBA0CjD,aACd,OAAM,KAAK,0BACV"}
package/dist/audit.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { d as PactInteractionKey, i as BrokerVerification, n as AuditMatrix, s as LedgerRecord } from "./types-D_0kgK2e.js";
1
+ import { d as PactInteractionKey, i as BrokerVerification, n as AuditMatrix, s as LedgerRecord } from "./types-DpHqSzxb.js";
2
2
  import { BrokerConfig } from "./broker.js";
3
- import { t as LedgerOptions } from "./ledger-Bzh_6tbV.js";
4
-
3
+ import { t as LedgerOptions } from "./ledger-CBUbpQr-.js";
5
4
  //#region src/audit.d.ts
6
5
  interface AuditOptions extends LedgerOptions {
7
6
  pactsDir?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","names":[],"sources":["../src/audit.ts"],"mappings":";;;;;UAqBiB,YAAA,SAAqB,aAAa;EACjD,QAAA;EACA,UAAA;EACA,MAAA,GAAS,YAAA;AAAA;AAAA,iBAMK,KAAA,CAAM,CAAqB,EAAlB,kBAAkB;;;;iBAiB3B,kBAAA,CAAmB,KAAA;EACjC,UAAA,EAAY,kBAAA;EACZ,MAAA,EAAQ,YAAA;EACR,mBAAA,GAAsB,kBAAA;EACtB,UAAA;EACA,GAAA,GAAM,IAAA;AAAA,IACJ,WAAA;AAAA,iBAoJkB,QAAA,CAAS,IAAA,GAAM,YAAA,GAAoB,OAAA,CAAQ,WAAA;AA3KtB;AAAA,iBA+M3B,YAAA,CAAa,IAAA,GAAM,IAAA,CAAK,YAAA,cAA+B,WAAA"}
1
+ {"version":3,"file":"audit.d.ts","names":[],"sources":["../src/audit.ts"],"mappings":";;;;UAqBiB,qBAAqB;EACpC;EACA;EACA,SAAS;;iBAMK,MAAM,GAAG;;;;iBAiBT,mBAAmB;EACjC,YAAY;EACZ,QAAQ;EACR,sBAAsB;EACtB;EACA,MAAM;IACJ;iBA2JkB,SAAS,OAAM,eAAoB,QAAQ;;iBA0CjD,aACd,OAAM,KAAK,0BACV"}
package/dist/audit.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","names":[],"sources":["../src/audit.ts"],"sourcesContent":["import path from 'node:path';\nimport { fetchBrokerVerifications, type BrokerConfig } from './broker.js';\nimport { readLedger, type LedgerOptions } from './ledger.js';\nimport {\n interactionsFromPactFile,\n listPactFiles,\n parsePactFile,\n} from './pact-file.js';\nimport {\n AUDIT_MATRIX_SPEC,\n isInteractionLedgerEntry,\n isProviderVerificationRun,\n type AuditMatrix,\n type AuditRow,\n type BrokerVerification,\n type InteractionLedgerEntry,\n type LedgerRecord,\n type PactInteractionKey,\n type ProviderVerificationRunEntry,\n} from './types.js';\n\nexport interface AuditOptions extends LedgerOptions {\n pactsDir?: string;\n windowDays?: number;\n broker?: BrokerConfig;\n}\n\nconst DEFAULT_PACTS_DIR = './pacts';\nconst DEFAULT_WINDOW_DAYS = 14;\n\nexport function keyOf(k: PactInteractionKey): string {\n const identity = k.interactionId ?? k.interaction;\n return `${k.consumer}::${k.provider}::${k.kind}::${identity}`;\n}\n\nfunction pairKey(consumer: string, provider: string): string {\n return `${consumer}::${provider}`;\n}\n\nfunction inWindow(observedAt: string, cutoff: number): boolean {\n const t = Date.parse(observedAt);\n return Number.isFinite(t) && t >= cutoff;\n}\n\n/**\n * Compute the audit matrix from pact files, ledger, and optional broker data.\n */\nexport function computeAuditMatrix(input: {\n contracted: PactInteractionKey[];\n ledger: LedgerRecord[];\n brokerVerifications?: BrokerVerification[];\n windowDays?: number;\n now?: Date;\n}): AuditMatrix {\n const windowDays = input.windowDays ?? DEFAULT_WINDOW_DAYS;\n const now = input.now ?? new Date();\n const cutoff = now.getTime() - windowDays * 24 * 60 * 60 * 1000;\n\n const verificationFailures: ProviderVerificationRunEntry[] = [];\n const recentInteractions: InteractionLedgerEntry[] = [];\n\n for (const entry of input.ledger) {\n if (!inWindow(entry.observed_at, cutoff)) continue;\n if (isProviderVerificationRun(entry)) {\n verificationFailures.push(entry);\n continue;\n }\n if (isInteractionLedgerEntry(entry)) {\n recentInteractions.push(entry);\n }\n }\n\n const brokerByPair = new Map<string, BrokerVerification>();\n for (const b of input.brokerVerifications ?? []) {\n brokerByPair.set(pairKey(b.consumer, b.provider), b);\n }\n\n const testSeenByKey = new Map<string, InteractionLedgerEntry[]>();\n const prodSeenByKey = new Map<string, InteractionLedgerEntry[]>();\n const providerVerifiedByKey = new Map<string, InteractionLedgerEntry[]>();\n const anyObservedByKey = new Map<string, InteractionLedgerEntry[]>();\n\n for (const entry of recentInteractions) {\n const k = keyOf({\n consumer: entry.consumer,\n provider: entry.provider,\n interaction: entry.interaction,\n kind: entry.kind,\n interactionId: entry.interaction_id,\n });\n\n const push = (map: Map<string, InteractionLedgerEntry[]>) => {\n const arr = map.get(k) ?? [];\n arr.push(entry);\n map.set(k, arr);\n };\n\n push(anyObservedByKey);\n\n if (entry.source === 'test' && entry.role === 'consumer') {\n push(testSeenByKey);\n }\n if (entry.source === 'production') {\n push(prodSeenByKey);\n }\n if (entry.role === 'provider' && entry.outcome === 'passed') {\n push(providerVerifiedByKey);\n }\n }\n\n const contractedByKey = new Map<string, PactInteractionKey>();\n for (const c of input.contracted) {\n contractedByKey.set(keyOf(c), c);\n }\n\n const rows: AuditRow[] = [];\n\n function pushRow(parts: PactInteractionKey, k: string, isContracted: boolean): void {\n const testObs = testSeenByKey.get(k) ?? [];\n const prodObs = prodSeenByKey.get(k) ?? [];\n const providerObs = providerVerifiedByKey.get(k) ?? [];\n const allObs = anyObservedByKey.get(k) ?? [];\n const latest = allObs.toSorted((a, b) =>\n b.observed_at.localeCompare(a.observed_at),\n )[0];\n const broker = brokerByPair.get(pairKey(parts.consumer, parts.provider));\n\n rows.push({\n consumer: parts.consumer,\n provider: parts.provider,\n interaction: parts.interaction,\n interaction_id: parts.interactionId ?? latest?.interaction_id,\n kind: parts.kind,\n contracted: isContracted,\n observed: testObs.length > 0 || prodObs.length > 0,\n test_seen: testObs.length > 0,\n prod_seen: prodObs.length > 0,\n provider_verified: providerObs.length > 0,\n broker_verified: broker?.success === true,\n broker_verified_at: broker?.verifiedAt,\n broker_error: broker?.error,\n last_observed_at: latest?.observed_at,\n last_outcome: latest?.outcome,\n });\n }\n\n for (const [k, contracted] of contractedByKey) {\n pushRow(contracted, k, true);\n }\n for (const [k, observations] of anyObservedByKey) {\n if (contractedByKey.has(k)) continue;\n const first = observations[0]!;\n pushRow(\n {\n consumer: first.consumer,\n provider: first.provider,\n interaction: first.interaction,\n kind: first.kind,\n interactionId: first.interaction_id,\n },\n k,\n false,\n );\n }\n\n rows.sort((a, b) =>\n a.consumer.localeCompare(b.consumer) ||\n a.provider.localeCompare(b.provider) ||\n a.interaction.localeCompare(b.interaction),\n );\n\n const counts = {\n total: rows.length,\n contracted: rows.filter((r) => r.contracted).length,\n observed: rows.filter((r) => r.observed).length,\n contracted_and_test_seen: rows.filter((r) => r.contracted && r.test_seen).length,\n contracted_not_test_seen: rows.filter((r) => r.contracted && !r.test_seen).length,\n test_or_prod_seen_not_contracted: rows.filter(\n (r) => !r.contracted && (r.test_seen || r.prod_seen),\n ).length,\n test_seen: rows.filter((r) => r.test_seen).length,\n prod_seen: rows.filter((r) => r.prod_seen).length,\n provider_verified: rows.filter((r) => r.provider_verified).length,\n broker_verified: rows.filter((r) => r.broker_verified).length,\n };\n\n const matrix: AuditMatrix = {\n spec: AUDIT_MATRIX_SPEC,\n rows,\n counts,\n window_days: windowDays,\n generated_at: now.toISOString(),\n };\n\n if (verificationFailures.length > 0) {\n matrix.verification_failures = verificationFailures;\n }\n\n return matrix;\n}\n\nexport async function runAudit(opts: AuditOptions = {}): Promise<AuditMatrix> {\n const pactsDir = path.resolve(process.cwd(), opts.pactsDir ?? DEFAULT_PACTS_DIR);\n const contracted: PactInteractionKey[] = [];\n const pairs = new Set<string>();\n\n for (const file of listPactFiles(pactsDir)) {\n const pact = parsePactFile(file);\n if (!pact) continue;\n const interactions = interactionsFromPactFile(pact);\n contracted.push(...interactions);\n const consumer = pact.consumer?.name;\n const provider = pact.provider?.name;\n if (consumer && provider) {\n pairs.add(pairKey(consumer, provider));\n }\n }\n\n const ledger = readLedger(opts);\n\n let brokerVerifications: BrokerVerification[] | undefined;\n if (opts.broker) {\n brokerVerifications = await fetchBrokerVerifications(opts.broker, [...pairs].map((p) => {\n const [consumer, provider] = p.split('::');\n return { consumer: consumer!, provider: provider! };\n }));\n }\n\n return computeAuditMatrix({\n contracted,\n ledger,\n brokerVerifications,\n windowDays: opts.windowDays,\n });\n}\n\n/** Sync audit without broker (backward compatible for tests). */\nexport function runAuditSync(opts: Omit<AuditOptions, 'broker'> = {}): AuditMatrix {\n const pactsDir = path.resolve(process.cwd(), opts.pactsDir ?? DEFAULT_PACTS_DIR);\n const contracted: PactInteractionKey[] = [];\n for (const file of listPactFiles(pactsDir)) {\n const pact = parsePactFile(file);\n if (pact) contracted.push(...interactionsFromPactFile(pact));\n }\n const ledger = readLedger(opts);\n return computeAuditMatrix({\n contracted,\n ledger,\n windowDays: opts.windowDays,\n });\n}\n"],"mappings":";;;;;;AA2BA,MAAM,oBAAoB;AAC1B,MAAM,sBAAsB;AAE5B,SAAgB,MAAM,GAA+B;CACnD,MAAM,WAAW,EAAE,iBAAiB,EAAE;CACtC,OAAO,GAAG,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI;AACrD;AAEA,SAAS,QAAQ,UAAkB,UAA0B;CAC3D,OAAO,GAAG,SAAS,IAAI;AACzB;AAEA,SAAS,SAAS,YAAoB,QAAyB;CAC7D,MAAM,IAAI,KAAK,MAAM,UAAU;CAC/B,OAAO,OAAO,SAAS,CAAC,KAAK,KAAK;AACpC;;;;AAKA,SAAgB,mBAAmB,OAMnB;CACd,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,MAAM,MAAM,uBAAO,IAAI,KAAK;CAClC,MAAM,SAAS,IAAI,QAAQ,IAAI,aAAa,KAAK,KAAK,KAAK;CAE3D,MAAM,uBAAuD,CAAC;CAC9D,MAAM,qBAA+C,CAAC;CAEtD,KAAK,MAAM,SAAS,MAAM,QAAQ;EAChC,IAAI,CAAC,SAAS,MAAM,aAAa,MAAM,GAAG;EAC1C,IAAI,0BAA0B,KAAK,GAAG;GACpC,qBAAqB,KAAK,KAAK;GAC/B;EACF;EACA,IAAI,yBAAyB,KAAK,GAChC,mBAAmB,KAAK,KAAK;CAEjC;CAEA,MAAM,+BAAe,IAAI,IAAgC;CACzD,KAAK,MAAM,KAAK,MAAM,uBAAuB,CAAC,GAC5C,aAAa,IAAI,QAAQ,EAAE,UAAU,EAAE,QAAQ,GAAG,CAAC;CAGrD,MAAM,gCAAgB,IAAI,IAAsC;CAChE,MAAM,gCAAgB,IAAI,IAAsC;CAChE,MAAM,wCAAwB,IAAI,IAAsC;CACxE,MAAM,mCAAmB,IAAI,IAAsC;CAEnE,KAAK,MAAM,SAAS,oBAAoB;EACtC,MAAM,IAAI,MAAM;GACd,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,MAAM,MAAM;GACZ,eAAe,MAAM;EACvB,CAAC;EAED,MAAM,QAAQ,QAA+C;GAC3D,MAAM,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC;GAC3B,IAAI,KAAK,KAAK;GACd,IAAI,IAAI,GAAG,GAAG;EAChB;EAEA,KAAK,gBAAgB;EAErB,IAAI,MAAM,WAAW,UAAU,MAAM,SAAS,YAC5C,KAAK,aAAa;EAEpB,IAAI,MAAM,WAAW,cACnB,KAAK,aAAa;EAEpB,IAAI,MAAM,SAAS,cAAc,MAAM,YAAY,UACjD,KAAK,qBAAqB;CAE9B;CAEA,MAAM,kCAAkB,IAAI,IAAgC;CAC5D,KAAK,MAAM,KAAK,MAAM,YACpB,gBAAgB,IAAI,MAAM,CAAC,GAAG,CAAC;CAGjC,MAAM,OAAmB,CAAC;CAE1B,SAAS,QAAQ,OAA2B,GAAW,cAA6B;EAClF,MAAM,UAAU,cAAc,IAAI,CAAC,KAAK,CAAC;EACzC,MAAM,UAAU,cAAc,IAAI,CAAC,KAAK,CAAC;EACzC,MAAM,cAAc,sBAAsB,IAAI,CAAC,KAAK,CAAC;EAErD,MAAM,UADS,iBAAiB,IAAI,CAAC,KAAK,CAAC,EACtB,CAAC,UAAU,GAAG,MACjC,EAAE,YAAY,cAAc,EAAE,WAAW,CAC3C,CAAC,CAAC;EACF,MAAM,SAAS,aAAa,IAAI,QAAQ,MAAM,UAAU,MAAM,QAAQ,CAAC;EAEvE,KAAK,KAAK;GACR,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,gBAAgB,MAAM,iBAAiB,QAAQ;GAC/C,MAAM,MAAM;GACZ,YAAY;GACZ,UAAU,QAAQ,SAAS,KAAK,QAAQ,SAAS;GACjD,WAAW,QAAQ,SAAS;GAC5B,WAAW,QAAQ,SAAS;GAC5B,mBAAmB,YAAY,SAAS;GACxC,iBAAiB,QAAQ,YAAY;GACrC,oBAAoB,QAAQ;GAC5B,cAAc,QAAQ;GACtB,kBAAkB,QAAQ;GAC1B,cAAc,QAAQ;EACxB,CAAC;CACH;CAEA,KAAK,MAAM,CAAC,GAAG,eAAe,iBAC5B,QAAQ,YAAY,GAAG,IAAI;CAE7B,KAAK,MAAM,CAAC,GAAG,iBAAiB,kBAAkB;EAChD,IAAI,gBAAgB,IAAI,CAAC,GAAG;EAC5B,MAAM,QAAQ,aAAa;EAC3B,QACE;GACE,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,MAAM,MAAM;GACZ,eAAe,MAAM;EACvB,GACA,GACA,KACF;CACF;CAEA,KAAK,MAAM,GAAG,MACZ,EAAE,SAAS,cAAc,EAAE,QAAQ,KACnC,EAAE,SAAS,cAAc,EAAE,QAAQ,KACnC,EAAE,YAAY,cAAc,EAAE,WAAW,CAC3C;CAiBA,MAAM,SAAsB;EAC1B,MAAM;EACN;EACA;GAjBA,OAAO,KAAK;GACZ,YAAY,KAAK,QAAQ,MAAM,EAAE,UAAU,CAAC,CAAC;GAC7C,UAAU,KAAK,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC;GACzC,0BAA0B,KAAK,QAAQ,MAAM,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;GAC1E,0BAA0B,KAAK,QAAQ,MAAM,EAAE,cAAc,CAAC,EAAE,SAAS,CAAC,CAAC;GAC3E,kCAAkC,KAAK,QACpC,MAAM,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,UAC5C,CAAC,CAAC;GACF,WAAW,KAAK,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;GAC3C,WAAW,KAAK,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;GAC3C,mBAAmB,KAAK,QAAQ,MAAM,EAAE,iBAAiB,CAAC,CAAC;GAC3D,iBAAiB,KAAK,QAAQ,MAAM,EAAE,eAAe,CAAC,CAAC;EAMlD;EACL,aAAa;EACb,cAAc,IAAI,YAAY;CAChC;CAEA,IAAI,qBAAqB,SAAS,GAChC,OAAO,wBAAwB;CAGjC,OAAO;AACT;AAEA,eAAsB,SAAS,OAAqB,CAAC,GAAyB;CAC5E,MAAM,WAAW,KAAK,QAAQ,QAAQ,IAAI,GAAG,KAAK,YAAY,iBAAiB;CAC/E,MAAM,aAAmC,CAAC;CAC1C,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,MAAM,QAAQ,cAAc,QAAQ,GAAG;EAC1C,MAAM,OAAO,cAAc,IAAI;EAC/B,IAAI,CAAC,MAAM;EACX,MAAM,eAAe,yBAAyB,IAAI;EAClD,WAAW,KAAK,GAAG,YAAY;EAC/B,MAAM,WAAW,KAAK,UAAU;EAChC,MAAM,WAAW,KAAK,UAAU;EAChC,IAAI,YAAY,UACd,MAAM,IAAI,QAAQ,UAAU,QAAQ,CAAC;CAEzC;CAEA,MAAM,SAAS,WAAW,IAAI;CAE9B,IAAI;CACJ,IAAI,KAAK,QACP,sBAAsB,MAAM,yBAAyB,KAAK,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,MAAM;EACtF,MAAM,CAAC,UAAU,YAAY,EAAE,MAAM,IAAI;EACzC,OAAO;GAAY;GAAqB;EAAU;CACpD,CAAC,CAAC;CAGJ,OAAO,mBAAmB;EACxB;EACA;EACA;EACA,YAAY,KAAK;CACnB,CAAC;AACH;;AAGA,SAAgB,aAAa,OAAqC,CAAC,GAAgB;CACjF,MAAM,WAAW,KAAK,QAAQ,QAAQ,IAAI,GAAG,KAAK,YAAY,iBAAiB;CAC/E,MAAM,aAAmC,CAAC;CAC1C,KAAK,MAAM,QAAQ,cAAc,QAAQ,GAAG;EAC1C,MAAM,OAAO,cAAc,IAAI;EAC/B,IAAI,MAAM,WAAW,KAAK,GAAG,yBAAyB,IAAI,CAAC;CAC7D;CAEA,OAAO,mBAAmB;EACxB;EACA,QAHa,WAAW,IAGnB;EACL,YAAY,KAAK;CACnB,CAAC;AACH"}
1
+ {"version":3,"file":"audit.js","names":[],"sources":["../src/audit.ts"],"sourcesContent":["import path from 'node:path';\nimport { fetchBrokerVerifications, type BrokerConfig } from './broker.js';\nimport { readLedger, type LedgerOptions } from './ledger.js';\nimport {\n interactionsFromPactFile,\n listPactFiles,\n parsePactFile,\n} from './pact-file.js';\nimport {\n AUDIT_MATRIX_SPEC,\n isInteractionLedgerEntry,\n isProviderVerificationRun,\n type AuditMatrix,\n type AuditRow,\n type BrokerVerification,\n type InteractionLedgerEntry,\n type LedgerRecord,\n type PactInteractionKey,\n type ProviderVerificationRunEntry,\n} from './types.js';\n\nexport interface AuditOptions extends LedgerOptions {\n pactsDir?: string;\n windowDays?: number;\n broker?: BrokerConfig;\n}\n\nconst DEFAULT_PACTS_DIR = './pacts';\nconst DEFAULT_WINDOW_DAYS = 14;\n\nexport function keyOf(k: PactInteractionKey): string {\n const identity = k.interactionId ?? k.interaction;\n return `${k.consumer}::${k.provider}::${k.kind}::${identity}`;\n}\n\nfunction pairKey(consumer: string, provider: string): string {\n return `${consumer}::${provider}`;\n}\n\nfunction inWindow(observedAt: string, cutoff: number): boolean {\n const t = Date.parse(observedAt);\n return Number.isFinite(t) && t >= cutoff;\n}\n\n/**\n * Compute the audit matrix from pact files, ledger, and optional broker data.\n */\nexport function computeAuditMatrix(input: {\n contracted: PactInteractionKey[];\n ledger: LedgerRecord[];\n brokerVerifications?: BrokerVerification[];\n windowDays?: number;\n now?: Date;\n}): AuditMatrix {\n const windowDays = input.windowDays ?? DEFAULT_WINDOW_DAYS;\n const now = input.now ?? new Date();\n const cutoff = now.getTime() - windowDays * 24 * 60 * 60 * 1000;\n\n const verificationFailures: ProviderVerificationRunEntry[] = [];\n const recentInteractions: InteractionLedgerEntry[] = [];\n\n for (const entry of input.ledger) {\n if (!inWindow(entry.observed_at, cutoff)) continue;\n if (isProviderVerificationRun(entry)) {\n verificationFailures.push(entry);\n continue;\n }\n if (isInteractionLedgerEntry(entry)) {\n recentInteractions.push(entry);\n }\n }\n\n const brokerByPair = new Map<string, BrokerVerification>();\n for (const b of input.brokerVerifications ?? []) {\n brokerByPair.set(pairKey(b.consumer, b.provider), b);\n }\n\n const testSeenByKey = new Map<string, InteractionLedgerEntry[]>();\n const prodSeenByKey = new Map<string, InteractionLedgerEntry[]>();\n const providerVerifiedByKey = new Map<string, InteractionLedgerEntry[]>();\n const anyObservedByKey = new Map<string, InteractionLedgerEntry[]>();\n\n for (const entry of recentInteractions) {\n const k = keyOf({\n consumer: entry.consumer,\n provider: entry.provider,\n interaction: entry.interaction,\n kind: entry.kind,\n interactionId: entry.interaction_id,\n });\n\n const push = (map: Map<string, InteractionLedgerEntry[]>) => {\n const arr = map.get(k) ?? [];\n arr.push(entry);\n map.set(k, arr);\n };\n\n push(anyObservedByKey);\n\n if (entry.source === 'test' && entry.role === 'consumer') {\n push(testSeenByKey);\n }\n if (entry.source === 'production') {\n push(prodSeenByKey);\n }\n if (entry.role === 'provider' && entry.outcome === 'passed') {\n push(providerVerifiedByKey);\n }\n }\n\n const contractedByKey = new Map<string, PactInteractionKey>();\n for (const c of input.contracted) {\n contractedByKey.set(keyOf(c), c);\n }\n\n const rows: AuditRow[] = [];\n\n function pushRow(\n parts: PactInteractionKey,\n k: string,\n isContracted: boolean,\n ): void {\n const testObs = testSeenByKey.get(k) ?? [];\n const prodObs = prodSeenByKey.get(k) ?? [];\n const providerObs = providerVerifiedByKey.get(k) ?? [];\n const allObs = anyObservedByKey.get(k) ?? [];\n const latest = allObs.toSorted((a, b) =>\n b.observed_at.localeCompare(a.observed_at),\n )[0];\n const broker = brokerByPair.get(pairKey(parts.consumer, parts.provider));\n\n rows.push({\n consumer: parts.consumer,\n provider: parts.provider,\n interaction: parts.interaction,\n interaction_id: parts.interactionId ?? latest?.interaction_id,\n kind: parts.kind,\n contracted: isContracted,\n observed: testObs.length > 0 || prodObs.length > 0,\n test_seen: testObs.length > 0,\n prod_seen: prodObs.length > 0,\n provider_verified: providerObs.length > 0,\n broker_verified: broker?.success === true,\n broker_verified_at: broker?.verifiedAt,\n broker_error: broker?.error,\n last_observed_at: latest?.observed_at,\n last_outcome: latest?.outcome,\n });\n }\n\n for (const [k, contracted] of contractedByKey) {\n pushRow(contracted, k, true);\n }\n for (const [k, observations] of anyObservedByKey) {\n if (contractedByKey.has(k)) continue;\n const first = observations[0]!;\n pushRow(\n {\n consumer: first.consumer,\n provider: first.provider,\n interaction: first.interaction,\n kind: first.kind,\n interactionId: first.interaction_id,\n },\n k,\n false,\n );\n }\n\n rows.sort(\n (a, b) =>\n a.consumer.localeCompare(b.consumer) ||\n a.provider.localeCompare(b.provider) ||\n a.interaction.localeCompare(b.interaction),\n );\n\n const counts = {\n total: rows.length,\n contracted: rows.filter((r) => r.contracted).length,\n observed: rows.filter((r) => r.observed).length,\n contracted_and_test_seen: rows.filter((r) => r.contracted && r.test_seen)\n .length,\n contracted_not_test_seen: rows.filter((r) => r.contracted && !r.test_seen)\n .length,\n test_or_prod_seen_not_contracted: rows.filter(\n (r) => !r.contracted && (r.test_seen || r.prod_seen),\n ).length,\n test_seen: rows.filter((r) => r.test_seen).length,\n prod_seen: rows.filter((r) => r.prod_seen).length,\n provider_verified: rows.filter((r) => r.provider_verified).length,\n broker_verified: rows.filter((r) => r.broker_verified).length,\n };\n\n const matrix: AuditMatrix = {\n spec: AUDIT_MATRIX_SPEC,\n rows,\n counts,\n window_days: windowDays,\n generated_at: now.toISOString(),\n };\n\n if (verificationFailures.length > 0) {\n matrix.verification_failures = verificationFailures;\n }\n\n return matrix;\n}\n\nexport async function runAudit(opts: AuditOptions = {}): Promise<AuditMatrix> {\n const pactsDir = path.resolve(\n process.cwd(),\n opts.pactsDir ?? DEFAULT_PACTS_DIR,\n );\n const contracted: PactInteractionKey[] = [];\n const pairs = new Set<string>();\n\n for (const file of listPactFiles(pactsDir)) {\n const pact = parsePactFile(file);\n if (!pact) continue;\n const interactions = interactionsFromPactFile(pact);\n contracted.push(...interactions);\n const consumer = pact.consumer?.name;\n const provider = pact.provider?.name;\n if (consumer && provider) {\n pairs.add(pairKey(consumer, provider));\n }\n }\n\n const ledger = readLedger(opts);\n\n let brokerVerifications: BrokerVerification[] | undefined;\n if (opts.broker) {\n brokerVerifications = await fetchBrokerVerifications(\n opts.broker,\n [...pairs].map((p) => {\n const [consumer, provider] = p.split('::');\n return { consumer: consumer!, provider: provider! };\n }),\n );\n }\n\n return computeAuditMatrix({\n contracted,\n ledger,\n brokerVerifications,\n windowDays: opts.windowDays,\n });\n}\n\n/** Sync audit without broker (backward compatible for tests). */\nexport function runAuditSync(\n opts: Omit<AuditOptions, 'broker'> = {},\n): AuditMatrix {\n const pactsDir = path.resolve(\n process.cwd(),\n opts.pactsDir ?? DEFAULT_PACTS_DIR,\n );\n const contracted: PactInteractionKey[] = [];\n for (const file of listPactFiles(pactsDir)) {\n const pact = parsePactFile(file);\n if (pact) contracted.push(...interactionsFromPactFile(pact));\n }\n const ledger = readLedger(opts);\n return computeAuditMatrix({\n contracted,\n ledger,\n windowDays: opts.windowDays,\n });\n}\n"],"mappings":";;;;;;AA2BA,MAAM,oBAAoB;AAC1B,MAAM,sBAAsB;AAE5B,SAAgB,MAAM,GAA+B;CACnD,MAAM,WAAW,EAAE,iBAAiB,EAAE;CACtC,OAAO,GAAG,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI;AACrD;AAEA,SAAS,QAAQ,UAAkB,UAA0B;CAC3D,OAAO,GAAG,SAAS,IAAI;AACzB;AAEA,SAAS,SAAS,YAAoB,QAAyB;CAC7D,MAAM,IAAI,KAAK,MAAM,UAAU;CAC/B,OAAO,OAAO,SAAS,CAAC,KAAK,KAAK;AACpC;;;;AAKA,SAAgB,mBAAmB,OAMnB;CACd,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,MAAM,MAAM,uBAAO,IAAI,KAAK;CAClC,MAAM,SAAS,IAAI,QAAQ,IAAI,aAAa,KAAK,KAAK,KAAK;CAE3D,MAAM,uBAAuD,CAAC;CAC9D,MAAM,qBAA+C,CAAC;CAEtD,KAAK,MAAM,SAAS,MAAM,QAAQ;EAChC,IAAI,CAAC,SAAS,MAAM,aAAa,MAAM,GAAG;EAC1C,IAAI,0BAA0B,KAAK,GAAG;GACpC,qBAAqB,KAAK,KAAK;GAC/B;EACF;EACA,IAAI,yBAAyB,KAAK,GAChC,mBAAmB,KAAK,KAAK;CAEjC;CAEA,MAAM,+BAAe,IAAI,IAAgC;CACzD,KAAK,MAAM,KAAK,MAAM,uBAAuB,CAAC,GAC5C,aAAa,IAAI,QAAQ,EAAE,UAAU,EAAE,QAAQ,GAAG,CAAC;CAGrD,MAAM,gCAAgB,IAAI,IAAsC;CAChE,MAAM,gCAAgB,IAAI,IAAsC;CAChE,MAAM,wCAAwB,IAAI,IAAsC;CACxE,MAAM,mCAAmB,IAAI,IAAsC;CAEnE,KAAK,MAAM,SAAS,oBAAoB;EACtC,MAAM,IAAI,MAAM;GACd,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,MAAM,MAAM;GACZ,eAAe,MAAM;EACvB,CAAC;EAED,MAAM,QAAQ,QAA+C;GAC3D,MAAM,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC;GAC3B,IAAI,KAAK,KAAK;GACd,IAAI,IAAI,GAAG,GAAG;EAChB;EAEA,KAAK,gBAAgB;EAErB,IAAI,MAAM,WAAW,UAAU,MAAM,SAAS,YAC5C,KAAK,aAAa;EAEpB,IAAI,MAAM,WAAW,cACnB,KAAK,aAAa;EAEpB,IAAI,MAAM,SAAS,cAAc,MAAM,YAAY,UACjD,KAAK,qBAAqB;CAE9B;CAEA,MAAM,kCAAkB,IAAI,IAAgC;CAC5D,KAAK,MAAM,KAAK,MAAM,YACpB,gBAAgB,IAAI,MAAM,CAAC,GAAG,CAAC;CAGjC,MAAM,OAAmB,CAAC;CAE1B,SAAS,QACP,OACA,GACA,cACM;EACN,MAAM,UAAU,cAAc,IAAI,CAAC,KAAK,CAAC;EACzC,MAAM,UAAU,cAAc,IAAI,CAAC,KAAK,CAAC;EACzC,MAAM,cAAc,sBAAsB,IAAI,CAAC,KAAK,CAAC;EAErD,MAAM,UADS,iBAAiB,IAAI,CAAC,KAAK,CAAC,EACtB,CAAC,UAAU,GAAG,MACjC,EAAE,YAAY,cAAc,EAAE,WAAW,CAC3C,CAAC,CAAC;EACF,MAAM,SAAS,aAAa,IAAI,QAAQ,MAAM,UAAU,MAAM,QAAQ,CAAC;EAEvE,KAAK,KAAK;GACR,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,gBAAgB,MAAM,iBAAiB,QAAQ;GAC/C,MAAM,MAAM;GACZ,YAAY;GACZ,UAAU,QAAQ,SAAS,KAAK,QAAQ,SAAS;GACjD,WAAW,QAAQ,SAAS;GAC5B,WAAW,QAAQ,SAAS;GAC5B,mBAAmB,YAAY,SAAS;GACxC,iBAAiB,QAAQ,YAAY;GACrC,oBAAoB,QAAQ;GAC5B,cAAc,QAAQ;GACtB,kBAAkB,QAAQ;GAC1B,cAAc,QAAQ;EACxB,CAAC;CACH;CAEA,KAAK,MAAM,CAAC,GAAG,eAAe,iBAC5B,QAAQ,YAAY,GAAG,IAAI;CAE7B,KAAK,MAAM,CAAC,GAAG,iBAAiB,kBAAkB;EAChD,IAAI,gBAAgB,IAAI,CAAC,GAAG;EAC5B,MAAM,QAAQ,aAAa;EAC3B,QACE;GACE,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,aAAa,MAAM;GACnB,MAAM,MAAM;GACZ,eAAe,MAAM;EACvB,GACA,GACA,KACF;CACF;CAEA,KAAK,MACF,GAAG,MACF,EAAE,SAAS,cAAc,EAAE,QAAQ,KACnC,EAAE,SAAS,cAAc,EAAE,QAAQ,KACnC,EAAE,YAAY,cAAc,EAAE,WAAW,CAC7C;CAmBA,MAAM,SAAsB;EAC1B,MAAM;EACN;EACA;GAnBA,OAAO,KAAK;GACZ,YAAY,KAAK,QAAQ,MAAM,EAAE,UAAU,CAAC,CAAC;GAC7C,UAAU,KAAK,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC;GACzC,0BAA0B,KAAK,QAAQ,MAAM,EAAE,cAAc,EAAE,SAAS,CAAC,CACtE;GACH,0BAA0B,KAAK,QAAQ,MAAM,EAAE,cAAc,CAAC,EAAE,SAAS,CAAC,CACvE;GACH,kCAAkC,KAAK,QACpC,MAAM,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,UAC5C,CAAC,CAAC;GACF,WAAW,KAAK,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;GAC3C,WAAW,KAAK,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;GAC3C,mBAAmB,KAAK,QAAQ,MAAM,EAAE,iBAAiB,CAAC,CAAC;GAC3D,iBAAiB,KAAK,QAAQ,MAAM,EAAE,eAAe,CAAC,CAAC;EAMlD;EACL,aAAa;EACb,cAAc,IAAI,YAAY;CAChC;CAEA,IAAI,qBAAqB,SAAS,GAChC,OAAO,wBAAwB;CAGjC,OAAO;AACT;AAEA,eAAsB,SAAS,OAAqB,CAAC,GAAyB;CAC5E,MAAM,WAAW,KAAK,QACpB,QAAQ,IAAI,GACZ,KAAK,YAAY,iBACnB;CACA,MAAM,aAAmC,CAAC;CAC1C,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,MAAM,QAAQ,cAAc,QAAQ,GAAG;EAC1C,MAAM,OAAO,cAAc,IAAI;EAC/B,IAAI,CAAC,MAAM;EACX,MAAM,eAAe,yBAAyB,IAAI;EAClD,WAAW,KAAK,GAAG,YAAY;EAC/B,MAAM,WAAW,KAAK,UAAU;EAChC,MAAM,WAAW,KAAK,UAAU;EAChC,IAAI,YAAY,UACd,MAAM,IAAI,QAAQ,UAAU,QAAQ,CAAC;CAEzC;CAEA,MAAM,SAAS,WAAW,IAAI;CAE9B,IAAI;CACJ,IAAI,KAAK,QACP,sBAAsB,MAAM,yBAC1B,KAAK,QACL,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,MAAM;EACpB,MAAM,CAAC,UAAU,YAAY,EAAE,MAAM,IAAI;EACzC,OAAO;GAAY;GAAqB;EAAU;CACpD,CAAC,CACH;CAGF,OAAO,mBAAmB;EACxB;EACA;EACA;EACA,YAAY,KAAK;CACnB,CAAC;AACH;;AAGA,SAAgB,aACd,OAAqC,CAAC,GACzB;CACb,MAAM,WAAW,KAAK,QACpB,QAAQ,IAAI,GACZ,KAAK,YAAY,iBACnB;CACA,MAAM,aAAmC,CAAC;CAC1C,KAAK,MAAM,QAAQ,cAAc,QAAQ,GAAG;EAC1C,MAAM,OAAO,cAAc,IAAI;EAC/B,IAAI,MAAM,WAAW,KAAK,GAAG,yBAAyB,IAAI,CAAC;CAC7D;CAEA,OAAO,mBAAmB;EACxB;EACA,QAHa,WAAW,IAGnB;EACL,YAAY,KAAK;CACnB,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"auto-wrap.cjs","names":["buildPactAttributes","outcomeAttribute","LEDGER_ENTRY_SPEC"],"sources":["../src/auto-wrap.ts"],"sourcesContent":["/**\n * Auto-wrap entry for vitest / jest setup files.\n *\n * Importing this module monkey-patches `@pact-foundation/pact`'s\n * `MessageConsumerPact.prototype.verify` and `PactV3.prototype.executeTest`\n * so every contract test in the project records a ledger entry without\n * users having to wrap each call by hand.\n *\n * Usage (vitest):\n *\n * ```ts\n * // vitest.config.ts\n * import { defineConfig } from 'vitest/config';\n * export default defineConfig({\n * test: { setupFiles: ['autotel-pact/auto-wrap'] },\n * });\n * ```\n *\n * Usage (jest):\n *\n * ```js\n * // jest.config.js\n * module.exports = { setupFilesAfterEach: ['autotel-pact/auto-wrap'] };\n * ```\n *\n * Configuration is via environment variables — there's no API surface,\n * because setup files don't get parameters:\n *\n * - `AUTOTEL_PACT_RUN_ID` — tag every ledger entry with this run id\n * - `AUTOTEL_PACT_LEDGER_DIR` — override the default `.autotel-pact/` dir\n *\n * Idempotent: importing twice is a no-op.\n *\n * Safe when Pact-JS isn't installed: logs a single warning and exits\n * cleanly. The same setup file can stay in place for a repo whose\n * non-contract test packages don't have Pact as a dependency.\n */\n\nimport { span as autotelSpan, getActiveSpan } from 'autotel';\nimport { createRequire } from 'node:module';\nimport { buildPactAttributes, outcomeAttribute } from './attrs.js';\nimport { appendLedgerEntry } from './ledger.js';\nimport { LEDGER_ENTRY_SPEC, type InteractionLedgerEntry, type PactInteractionMeta } from './types.js';\nimport type { HttpInteraction } from './wrapper-http.js';\nimport type { ReifiedMessage } from './wrapper.js';\n\nconst INSTALLED = Symbol.for('autotel-pact:auto-wrap-installed');\n\ninterface PactJsModule {\n MessageConsumerPact?: { prototype: Record<string | symbol, unknown> };\n PactV3?: { prototype: Record<string | symbol, unknown> };\n}\n\n/**\n * Install the auto-wrap. Called at module load when this file is imported.\n * Exposed for tests + explicit-invocation users.\n *\n * @param pactModule Override Pact-JS resolution. When omitted, requires\n * `@pact-foundation/pact` from disk. Tests can pass synthetic classes.\n *\n * Returns `true` if Pact-JS was found and patched, `false` otherwise.\n */\nexport function installAutoWrap(pactModule?: PactJsModule): boolean {\n const pactJs = pactModule ?? loadPactJs();\n if (!pactJs) return false;\n\n patchMessagePact(pactJs);\n patchHttpPact(pactJs);\n return true;\n}\n\nfunction loadPactJs(): PactJsModule | undefined {\n try {\n // Use createRequire so we work from both ESM and CJS without\n // pulling Pact-JS into the dynamic import graph.\n const require = createRequire(import.meta.url);\n return require('@pact-foundation/pact') as PactJsModule;\n } catch {\n process.stderr.write(\n 'autotel-pact/auto-wrap: @pact-foundation/pact not installed — skipping.\\n',\n );\n return undefined;\n }\n}\n\nfunction patchMessagePact(mod: PactJsModule): void {\n const ctor = mod.MessageConsumerPact;\n if (!ctor) return;\n const proto = ctor.prototype;\n if (proto[INSTALLED]) return;\n\n const originalVerify = proto.verify as (\n handler: (m: ReifiedMessage) => Promise<unknown>,\n ) => Promise<unknown>;\n\n proto.verify = async function patchedVerify(\n this: { config: { consumer: string; provider: string } },\n handler: (m: ReifiedMessage) => Promise<unknown>,\n ): Promise<unknown> {\n const start = process.hrtime.bigint();\n let captured: ReifiedMessage | undefined;\n const consumer = this.config?.consumer ?? '<unknown>';\n const provider = this.config?.provider ?? '<unknown>';\n\n return autotelSpan('pact.interaction', async (span) => {\n span.setAttributes({\n 'pact.consumer': consumer,\n 'pact.provider': provider,\n 'pact.kind': 'message',\n });\n\n try {\n const result = await originalVerify.call(this, async (reified) => {\n captured = reified;\n const meta: PactInteractionMeta = {\n consumer,\n provider,\n description: reified.description ?? '<unknown>',\n states: (reified.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n };\n span.setAttributes(buildPactAttributes(meta));\n return handler(reified);\n });\n span.setAttributes(outcomeAttribute('passed'));\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: captured?.description ?? '<unknown>',\n states: (captured?.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n outcome: 'passed',\n start,\n });\n return result;\n } catch (error) {\n span.setAttributes(outcomeAttribute('failed'));\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: captured?.description ?? '<unknown>',\n states: (captured?.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n outcome: 'failed',\n start,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n });\n };\n\n proto[INSTALLED] = true;\n}\n\nfunction patchHttpPact(mod: PactJsModule): void {\n const ctor = mod.PactV3;\n if (!ctor) return;\n const proto = ctor.prototype;\n if (proto[INSTALLED]) return;\n\n // Track interactions added to each PactV3 instance. Cleared after\n // executeTest emits ledger entries for them, so the same instance can\n // be reused across multiple test cases.\n const tracked = new WeakMap<object, HttpInteraction[]>();\n\n const originalAdd = proto.addInteraction as (i: HttpInteraction) => unknown;\n proto.addInteraction = function patchedAddInteraction(this: object, interaction: HttpInteraction) {\n const list = tracked.get(this) ?? [];\n list.push(interaction);\n tracked.set(this, list);\n return originalAdd.call(this, interaction);\n };\n\n const originalExecute = proto.executeTest as <T>(\n fn: (server: { url: string; port: number }) => Promise<T>,\n ) => Promise<T | undefined>;\n\n proto.executeTest = async function patchedExecuteTest<T>(\n this: { opts?: { consumer?: string; provider?: string } },\n fn: (server: { url: string; port: number }) => Promise<T>,\n ): Promise<T | undefined> {\n const start = process.hrtime.bigint();\n const consumer = this.opts?.consumer ?? '<unknown>';\n const provider = this.opts?.provider ?? '<unknown>';\n const interactions = tracked.get(this) ?? [];\n tracked.set(this, []);\n\n return autotelSpan('pact.interaction', async (span) => {\n span.setAttributes({\n 'pact.consumer': consumer,\n 'pact.provider': provider,\n 'pact.kind': 'http',\n });\n // If multiple interactions were added before executeTest, the span\n // attributes describe the first; ledger entries are still written\n // for each. Most real tests use one interaction per executeTest.\n const first = interactions[0];\n if (first) {\n span.setAttributes(\n buildPactAttributes({\n consumer,\n provider,\n description: first.uponReceiving,\n states: (first.states ?? []).map((s) => s.description),\n kind: 'http',\n }),\n );\n }\n\n try {\n const result = (await originalExecute.call(this, fn)) as T | undefined;\n span.setAttributes(outcomeAttribute('passed'));\n for (const i of interactions) {\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: i.uponReceiving,\n states: (i.states ?? []).map((s) => s.description),\n kind: 'http',\n outcome: 'passed',\n start,\n });\n }\n return result;\n } catch (error_) {\n span.setAttributes(outcomeAttribute('failed'));\n const error = error_ instanceof Error ? error_.message : String(error_);\n for (const i of interactions) {\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: i.uponReceiving,\n states: (i.states ?? []).map((s) => s.description),\n kind: 'http',\n outcome: 'failed',\n start,\n error,\n });\n }\n throw error_;\n }\n });\n };\n\n proto[INSTALLED] = true;\n}\n\nfunction writeAutoLedgerEntry(args: {\n consumer: string;\n provider: string;\n interaction: string;\n states: string[];\n kind: 'message' | 'http';\n outcome: 'passed' | 'failed';\n start: bigint;\n error?: string;\n}): void {\n const ctx = getActiveSpan()?.spanContext();\n const entry: InteractionLedgerEntry = {\n type: 'interaction',\n spec: LEDGER_ENTRY_SPEC,\n consumer: args.consumer,\n provider: args.provider,\n interaction: args.interaction,\n states: args.states,\n kind: args.kind,\n source: 'test',\n role: 'consumer',\n outcome: args.outcome,\n duration_ms: Number(process.hrtime.bigint() - args.start) / 1e6,\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 error: args.error,\n };\n appendLedgerEntry(entry);\n}\n\n// Install on import — this file is meant to be loaded once as a setup file.\ninstallAutoWrap();\n"],"mappings":"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"}
1
+ {"version":3,"file":"auto-wrap.cjs","names":["buildPactAttributes","outcomeAttribute","LEDGER_ENTRY_SPEC"],"sources":["../src/auto-wrap.ts"],"sourcesContent":["/**\n * Auto-wrap entry for vitest / jest setup files.\n *\n * Importing this module monkey-patches `@pact-foundation/pact`'s\n * `MessageConsumerPact.prototype.verify` and `PactV3.prototype.executeTest`\n * so every contract test in the project records a ledger entry without\n * users having to wrap each call by hand.\n *\n * Usage (vitest):\n *\n * ```ts\n * // vitest.config.ts\n * import { defineConfig } from 'vitest/config';\n * export default defineConfig({\n * test: { setupFiles: ['autotel-pact/auto-wrap'] },\n * });\n * ```\n *\n * Usage (jest):\n *\n * ```js\n * // jest.config.js\n * module.exports = { setupFilesAfterEach: ['autotel-pact/auto-wrap'] };\n * ```\n *\n * Configuration is via environment variables — there's no API surface,\n * because setup files don't get parameters:\n *\n * - `AUTOTEL_PACT_RUN_ID` — tag every ledger entry with this run id\n * - `AUTOTEL_PACT_LEDGER_DIR` — override the default `.autotel-pact/` dir\n *\n * Idempotent: importing twice is a no-op.\n *\n * Safe when Pact-JS isn't installed: logs a single warning and exits\n * cleanly. The same setup file can stay in place for a repo whose\n * non-contract test packages don't have Pact as a dependency.\n */\n\nimport { span as autotelSpan, getActiveSpan } from 'autotel';\nimport { createRequire } from 'node:module';\nimport { buildPactAttributes, outcomeAttribute } from './attrs.js';\nimport { appendLedgerEntry } from './ledger.js';\nimport {\n LEDGER_ENTRY_SPEC,\n type InteractionLedgerEntry,\n type PactInteractionMeta,\n} from './types.js';\nimport type { HttpInteraction } from './wrapper-http.js';\nimport type { ReifiedMessage } from './wrapper.js';\n\nconst INSTALLED = Symbol.for('autotel-pact:auto-wrap-installed');\n\ninterface PactJsModule {\n MessageConsumerPact?: { prototype: Record<string | symbol, unknown> };\n PactV3?: { prototype: Record<string | symbol, unknown> };\n}\n\n/**\n * Install the auto-wrap. Called at module load when this file is imported.\n * Exposed for tests + explicit-invocation users.\n *\n * @param pactModule Override Pact-JS resolution. When omitted, requires\n * `@pact-foundation/pact` from disk. Tests can pass synthetic classes.\n *\n * Returns `true` if Pact-JS was found and patched, `false` otherwise.\n */\nexport function installAutoWrap(pactModule?: PactJsModule): boolean {\n const pactJs = pactModule ?? loadPactJs();\n if (!pactJs) return false;\n\n patchMessagePact(pactJs);\n patchHttpPact(pactJs);\n return true;\n}\n\nfunction loadPactJs(): PactJsModule | undefined {\n try {\n // Use createRequire so we work from both ESM and CJS without\n // pulling Pact-JS into the dynamic import graph.\n const require = createRequire(import.meta.url);\n return require('@pact-foundation/pact') as PactJsModule;\n } catch {\n process.stderr.write(\n 'autotel-pact/auto-wrap: @pact-foundation/pact not installed — skipping.\\n',\n );\n return undefined;\n }\n}\n\nfunction patchMessagePact(mod: PactJsModule): void {\n const ctor = mod.MessageConsumerPact;\n if (!ctor) return;\n const proto = ctor.prototype;\n if (proto[INSTALLED]) return;\n\n const originalVerify = proto.verify as (\n handler: (m: ReifiedMessage) => Promise<unknown>,\n ) => Promise<unknown>;\n\n proto.verify = async function patchedVerify(\n this: { config: { consumer: string; provider: string } },\n handler: (m: ReifiedMessage) => Promise<unknown>,\n ): Promise<unknown> {\n const start = process.hrtime.bigint();\n let captured: ReifiedMessage | undefined;\n const consumer = this.config?.consumer ?? '<unknown>';\n const provider = this.config?.provider ?? '<unknown>';\n\n return autotelSpan('pact.interaction', async (span) => {\n span.setAttributes({\n 'pact.consumer': consumer,\n 'pact.provider': provider,\n 'pact.kind': 'message',\n });\n\n try {\n const result = await originalVerify.call(this, async (reified) => {\n captured = reified;\n const meta: PactInteractionMeta = {\n consumer,\n provider,\n description: reified.description ?? '<unknown>',\n states: (reified.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n };\n span.setAttributes(buildPactAttributes(meta));\n return handler(reified);\n });\n span.setAttributes(outcomeAttribute('passed'));\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: captured?.description ?? '<unknown>',\n states: (captured?.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n outcome: 'passed',\n start,\n });\n return result;\n } catch (error) {\n span.setAttributes(outcomeAttribute('failed'));\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: captured?.description ?? '<unknown>',\n states: (captured?.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n outcome: 'failed',\n start,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n });\n };\n\n proto[INSTALLED] = true;\n}\n\nfunction patchHttpPact(mod: PactJsModule): void {\n const ctor = mod.PactV3;\n if (!ctor) return;\n const proto = ctor.prototype;\n if (proto[INSTALLED]) return;\n\n // Track interactions added to each PactV3 instance. Cleared after\n // executeTest emits ledger entries for them, so the same instance can\n // be reused across multiple test cases.\n const tracked = new WeakMap<object, HttpInteraction[]>();\n\n const originalAdd = proto.addInteraction as (i: HttpInteraction) => unknown;\n proto.addInteraction = function patchedAddInteraction(\n this: object,\n interaction: HttpInteraction,\n ) {\n const list = tracked.get(this) ?? [];\n list.push(interaction);\n tracked.set(this, list);\n return originalAdd.call(this, interaction);\n };\n\n const originalExecute = proto.executeTest as <T>(\n fn: (server: { url: string; port: number }) => Promise<T>,\n ) => Promise<T | undefined>;\n\n proto.executeTest = async function patchedExecuteTest<T>(\n this: { opts?: { consumer?: string; provider?: string } },\n fn: (server: { url: string; port: number }) => Promise<T>,\n ): Promise<T | undefined> {\n const start = process.hrtime.bigint();\n const consumer = this.opts?.consumer ?? '<unknown>';\n const provider = this.opts?.provider ?? '<unknown>';\n const interactions = tracked.get(this) ?? [];\n tracked.set(this, []);\n\n return autotelSpan('pact.interaction', async (span) => {\n span.setAttributes({\n 'pact.consumer': consumer,\n 'pact.provider': provider,\n 'pact.kind': 'http',\n });\n // If multiple interactions were added before executeTest, the span\n // attributes describe the first; ledger entries are still written\n // for each. Most real tests use one interaction per executeTest.\n const first = interactions[0];\n if (first) {\n span.setAttributes(\n buildPactAttributes({\n consumer,\n provider,\n description: first.uponReceiving,\n states: (first.states ?? []).map((s) => s.description),\n kind: 'http',\n }),\n );\n }\n\n try {\n const result = (await originalExecute.call(this, fn)) as T | undefined;\n span.setAttributes(outcomeAttribute('passed'));\n for (const i of interactions) {\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: i.uponReceiving,\n states: (i.states ?? []).map((s) => s.description),\n kind: 'http',\n outcome: 'passed',\n start,\n });\n }\n return result;\n } catch (error_) {\n span.setAttributes(outcomeAttribute('failed'));\n const error = error_ instanceof Error ? error_.message : String(error_);\n for (const i of interactions) {\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: i.uponReceiving,\n states: (i.states ?? []).map((s) => s.description),\n kind: 'http',\n outcome: 'failed',\n start,\n error,\n });\n }\n throw error_;\n }\n });\n };\n\n proto[INSTALLED] = true;\n}\n\nfunction writeAutoLedgerEntry(args: {\n consumer: string;\n provider: string;\n interaction: string;\n states: string[];\n kind: 'message' | 'http';\n outcome: 'passed' | 'failed';\n start: bigint;\n error?: string;\n}): void {\n const ctx = getActiveSpan()?.spanContext();\n const entry: InteractionLedgerEntry = {\n type: 'interaction',\n spec: LEDGER_ENTRY_SPEC,\n consumer: args.consumer,\n provider: args.provider,\n interaction: args.interaction,\n states: args.states,\n kind: args.kind,\n source: 'test',\n role: 'consumer',\n outcome: args.outcome,\n duration_ms: Number(process.hrtime.bigint() - args.start) / 1e6,\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 error: args.error,\n };\n appendLedgerEntry(entry);\n}\n\n// Install on import — this file is meant to be loaded once as a setup file.\ninstallAutoWrap();\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"auto-wrap.d.cts","names":[],"sources":["../src/auto-wrap.ts"],"mappings":";;;;;;;;;;;;;AAkD8B;AAY9B;;;;AAAyD;;;;;;;;;;;;;;;;;;;;UAd/C,YAAA;EACR,mBAAA;IAAwB,SAAA,EAAW,MAAA;EAAA;EACnC,MAAA;IAAW,SAAA,EAAW,MAAM;EAAA;AAAA;;;;;;;;;;iBAYd,eAAA,CAAgB,UAAyB,GAAZ,YAAY"}
1
+ {"version":3,"file":"auto-wrap.d.cts","names":[],"sources":["../src/auto-wrap.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAoDU;EACR;IAAwB,WAAW;;EACnC;IAAW,WAAW;;;;;;;;;;;;iBAYR,gBAAgB,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"auto-wrap.d.ts","names":[],"sources":["../src/auto-wrap.ts"],"mappings":";;;;;;;;;;;;;AAkD8B;AAY9B;;;;AAAyD;;;;;;;;;;;;;;;;;;;;UAd/C,YAAA;EACR,mBAAA;IAAwB,SAAA,EAAW,MAAA;EAAA;EACnC,MAAA;IAAW,SAAA,EAAW,MAAM;EAAA;AAAA;;;;;;;;;;iBAYd,eAAA,CAAgB,UAAyB,GAAZ,YAAY"}
1
+ {"version":3,"file":"auto-wrap.d.ts","names":[],"sources":["../src/auto-wrap.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAoDU;EACR;IAAwB,WAAW;;EACnC;IAAW,WAAW;;;;;;;;;;;;iBAYR,gBAAgB,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"auto-wrap.js","names":["autotelSpan"],"sources":["../src/auto-wrap.ts"],"sourcesContent":["/**\n * Auto-wrap entry for vitest / jest setup files.\n *\n * Importing this module monkey-patches `@pact-foundation/pact`'s\n * `MessageConsumerPact.prototype.verify` and `PactV3.prototype.executeTest`\n * so every contract test in the project records a ledger entry without\n * users having to wrap each call by hand.\n *\n * Usage (vitest):\n *\n * ```ts\n * // vitest.config.ts\n * import { defineConfig } from 'vitest/config';\n * export default defineConfig({\n * test: { setupFiles: ['autotel-pact/auto-wrap'] },\n * });\n * ```\n *\n * Usage (jest):\n *\n * ```js\n * // jest.config.js\n * module.exports = { setupFilesAfterEach: ['autotel-pact/auto-wrap'] };\n * ```\n *\n * Configuration is via environment variables — there's no API surface,\n * because setup files don't get parameters:\n *\n * - `AUTOTEL_PACT_RUN_ID` — tag every ledger entry with this run id\n * - `AUTOTEL_PACT_LEDGER_DIR` — override the default `.autotel-pact/` dir\n *\n * Idempotent: importing twice is a no-op.\n *\n * Safe when Pact-JS isn't installed: logs a single warning and exits\n * cleanly. The same setup file can stay in place for a repo whose\n * non-contract test packages don't have Pact as a dependency.\n */\n\nimport { span as autotelSpan, getActiveSpan } from 'autotel';\nimport { createRequire } from 'node:module';\nimport { buildPactAttributes, outcomeAttribute } from './attrs.js';\nimport { appendLedgerEntry } from './ledger.js';\nimport { LEDGER_ENTRY_SPEC, type InteractionLedgerEntry, type PactInteractionMeta } from './types.js';\nimport type { HttpInteraction } from './wrapper-http.js';\nimport type { ReifiedMessage } from './wrapper.js';\n\nconst INSTALLED = Symbol.for('autotel-pact:auto-wrap-installed');\n\ninterface PactJsModule {\n MessageConsumerPact?: { prototype: Record<string | symbol, unknown> };\n PactV3?: { prototype: Record<string | symbol, unknown> };\n}\n\n/**\n * Install the auto-wrap. Called at module load when this file is imported.\n * Exposed for tests + explicit-invocation users.\n *\n * @param pactModule Override Pact-JS resolution. When omitted, requires\n * `@pact-foundation/pact` from disk. Tests can pass synthetic classes.\n *\n * Returns `true` if Pact-JS was found and patched, `false` otherwise.\n */\nexport function installAutoWrap(pactModule?: PactJsModule): boolean {\n const pactJs = pactModule ?? loadPactJs();\n if (!pactJs) return false;\n\n patchMessagePact(pactJs);\n patchHttpPact(pactJs);\n return true;\n}\n\nfunction loadPactJs(): PactJsModule | undefined {\n try {\n // Use createRequire so we work from both ESM and CJS without\n // pulling Pact-JS into the dynamic import graph.\n const require = createRequire(import.meta.url);\n return require('@pact-foundation/pact') as PactJsModule;\n } catch {\n process.stderr.write(\n 'autotel-pact/auto-wrap: @pact-foundation/pact not installed — skipping.\\n',\n );\n return undefined;\n }\n}\n\nfunction patchMessagePact(mod: PactJsModule): void {\n const ctor = mod.MessageConsumerPact;\n if (!ctor) return;\n const proto = ctor.prototype;\n if (proto[INSTALLED]) return;\n\n const originalVerify = proto.verify as (\n handler: (m: ReifiedMessage) => Promise<unknown>,\n ) => Promise<unknown>;\n\n proto.verify = async function patchedVerify(\n this: { config: { consumer: string; provider: string } },\n handler: (m: ReifiedMessage) => Promise<unknown>,\n ): Promise<unknown> {\n const start = process.hrtime.bigint();\n let captured: ReifiedMessage | undefined;\n const consumer = this.config?.consumer ?? '<unknown>';\n const provider = this.config?.provider ?? '<unknown>';\n\n return autotelSpan('pact.interaction', async (span) => {\n span.setAttributes({\n 'pact.consumer': consumer,\n 'pact.provider': provider,\n 'pact.kind': 'message',\n });\n\n try {\n const result = await originalVerify.call(this, async (reified) => {\n captured = reified;\n const meta: PactInteractionMeta = {\n consumer,\n provider,\n description: reified.description ?? '<unknown>',\n states: (reified.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n };\n span.setAttributes(buildPactAttributes(meta));\n return handler(reified);\n });\n span.setAttributes(outcomeAttribute('passed'));\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: captured?.description ?? '<unknown>',\n states: (captured?.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n outcome: 'passed',\n start,\n });\n return result;\n } catch (error) {\n span.setAttributes(outcomeAttribute('failed'));\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: captured?.description ?? '<unknown>',\n states: (captured?.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n outcome: 'failed',\n start,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n });\n };\n\n proto[INSTALLED] = true;\n}\n\nfunction patchHttpPact(mod: PactJsModule): void {\n const ctor = mod.PactV3;\n if (!ctor) return;\n const proto = ctor.prototype;\n if (proto[INSTALLED]) return;\n\n // Track interactions added to each PactV3 instance. Cleared after\n // executeTest emits ledger entries for them, so the same instance can\n // be reused across multiple test cases.\n const tracked = new WeakMap<object, HttpInteraction[]>();\n\n const originalAdd = proto.addInteraction as (i: HttpInteraction) => unknown;\n proto.addInteraction = function patchedAddInteraction(this: object, interaction: HttpInteraction) {\n const list = tracked.get(this) ?? [];\n list.push(interaction);\n tracked.set(this, list);\n return originalAdd.call(this, interaction);\n };\n\n const originalExecute = proto.executeTest as <T>(\n fn: (server: { url: string; port: number }) => Promise<T>,\n ) => Promise<T | undefined>;\n\n proto.executeTest = async function patchedExecuteTest<T>(\n this: { opts?: { consumer?: string; provider?: string } },\n fn: (server: { url: string; port: number }) => Promise<T>,\n ): Promise<T | undefined> {\n const start = process.hrtime.bigint();\n const consumer = this.opts?.consumer ?? '<unknown>';\n const provider = this.opts?.provider ?? '<unknown>';\n const interactions = tracked.get(this) ?? [];\n tracked.set(this, []);\n\n return autotelSpan('pact.interaction', async (span) => {\n span.setAttributes({\n 'pact.consumer': consumer,\n 'pact.provider': provider,\n 'pact.kind': 'http',\n });\n // If multiple interactions were added before executeTest, the span\n // attributes describe the first; ledger entries are still written\n // for each. Most real tests use one interaction per executeTest.\n const first = interactions[0];\n if (first) {\n span.setAttributes(\n buildPactAttributes({\n consumer,\n provider,\n description: first.uponReceiving,\n states: (first.states ?? []).map((s) => s.description),\n kind: 'http',\n }),\n );\n }\n\n try {\n const result = (await originalExecute.call(this, fn)) as T | undefined;\n span.setAttributes(outcomeAttribute('passed'));\n for (const i of interactions) {\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: i.uponReceiving,\n states: (i.states ?? []).map((s) => s.description),\n kind: 'http',\n outcome: 'passed',\n start,\n });\n }\n return result;\n } catch (error_) {\n span.setAttributes(outcomeAttribute('failed'));\n const error = error_ instanceof Error ? error_.message : String(error_);\n for (const i of interactions) {\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: i.uponReceiving,\n states: (i.states ?? []).map((s) => s.description),\n kind: 'http',\n outcome: 'failed',\n start,\n error,\n });\n }\n throw error_;\n }\n });\n };\n\n proto[INSTALLED] = true;\n}\n\nfunction writeAutoLedgerEntry(args: {\n consumer: string;\n provider: string;\n interaction: string;\n states: string[];\n kind: 'message' | 'http';\n outcome: 'passed' | 'failed';\n start: bigint;\n error?: string;\n}): void {\n const ctx = getActiveSpan()?.spanContext();\n const entry: InteractionLedgerEntry = {\n type: 'interaction',\n spec: LEDGER_ENTRY_SPEC,\n consumer: args.consumer,\n provider: args.provider,\n interaction: args.interaction,\n states: args.states,\n kind: args.kind,\n source: 'test',\n role: 'consumer',\n outcome: args.outcome,\n duration_ms: Number(process.hrtime.bigint() - args.start) / 1e6,\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 error: args.error,\n };\n appendLedgerEntry(entry);\n}\n\n// Install on import — this file is meant to be loaded once as a setup file.\ninstallAutoWrap();\n"],"mappings":"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"}
1
+ {"version":3,"file":"auto-wrap.js","names":["autotelSpan"],"sources":["../src/auto-wrap.ts"],"sourcesContent":["/**\n * Auto-wrap entry for vitest / jest setup files.\n *\n * Importing this module monkey-patches `@pact-foundation/pact`'s\n * `MessageConsumerPact.prototype.verify` and `PactV3.prototype.executeTest`\n * so every contract test in the project records a ledger entry without\n * users having to wrap each call by hand.\n *\n * Usage (vitest):\n *\n * ```ts\n * // vitest.config.ts\n * import { defineConfig } from 'vitest/config';\n * export default defineConfig({\n * test: { setupFiles: ['autotel-pact/auto-wrap'] },\n * });\n * ```\n *\n * Usage (jest):\n *\n * ```js\n * // jest.config.js\n * module.exports = { setupFilesAfterEach: ['autotel-pact/auto-wrap'] };\n * ```\n *\n * Configuration is via environment variables — there's no API surface,\n * because setup files don't get parameters:\n *\n * - `AUTOTEL_PACT_RUN_ID` — tag every ledger entry with this run id\n * - `AUTOTEL_PACT_LEDGER_DIR` — override the default `.autotel-pact/` dir\n *\n * Idempotent: importing twice is a no-op.\n *\n * Safe when Pact-JS isn't installed: logs a single warning and exits\n * cleanly. The same setup file can stay in place for a repo whose\n * non-contract test packages don't have Pact as a dependency.\n */\n\nimport { span as autotelSpan, getActiveSpan } from 'autotel';\nimport { createRequire } from 'node:module';\nimport { buildPactAttributes, outcomeAttribute } from './attrs.js';\nimport { appendLedgerEntry } from './ledger.js';\nimport {\n LEDGER_ENTRY_SPEC,\n type InteractionLedgerEntry,\n type PactInteractionMeta,\n} from './types.js';\nimport type { HttpInteraction } from './wrapper-http.js';\nimport type { ReifiedMessage } from './wrapper.js';\n\nconst INSTALLED = Symbol.for('autotel-pact:auto-wrap-installed');\n\ninterface PactJsModule {\n MessageConsumerPact?: { prototype: Record<string | symbol, unknown> };\n PactV3?: { prototype: Record<string | symbol, unknown> };\n}\n\n/**\n * Install the auto-wrap. Called at module load when this file is imported.\n * Exposed for tests + explicit-invocation users.\n *\n * @param pactModule Override Pact-JS resolution. When omitted, requires\n * `@pact-foundation/pact` from disk. Tests can pass synthetic classes.\n *\n * Returns `true` if Pact-JS was found and patched, `false` otherwise.\n */\nexport function installAutoWrap(pactModule?: PactJsModule): boolean {\n const pactJs = pactModule ?? loadPactJs();\n if (!pactJs) return false;\n\n patchMessagePact(pactJs);\n patchHttpPact(pactJs);\n return true;\n}\n\nfunction loadPactJs(): PactJsModule | undefined {\n try {\n // Use createRequire so we work from both ESM and CJS without\n // pulling Pact-JS into the dynamic import graph.\n const require = createRequire(import.meta.url);\n return require('@pact-foundation/pact') as PactJsModule;\n } catch {\n process.stderr.write(\n 'autotel-pact/auto-wrap: @pact-foundation/pact not installed — skipping.\\n',\n );\n return undefined;\n }\n}\n\nfunction patchMessagePact(mod: PactJsModule): void {\n const ctor = mod.MessageConsumerPact;\n if (!ctor) return;\n const proto = ctor.prototype;\n if (proto[INSTALLED]) return;\n\n const originalVerify = proto.verify as (\n handler: (m: ReifiedMessage) => Promise<unknown>,\n ) => Promise<unknown>;\n\n proto.verify = async function patchedVerify(\n this: { config: { consumer: string; provider: string } },\n handler: (m: ReifiedMessage) => Promise<unknown>,\n ): Promise<unknown> {\n const start = process.hrtime.bigint();\n let captured: ReifiedMessage | undefined;\n const consumer = this.config?.consumer ?? '<unknown>';\n const provider = this.config?.provider ?? '<unknown>';\n\n return autotelSpan('pact.interaction', async (span) => {\n span.setAttributes({\n 'pact.consumer': consumer,\n 'pact.provider': provider,\n 'pact.kind': 'message',\n });\n\n try {\n const result = await originalVerify.call(this, async (reified) => {\n captured = reified;\n const meta: PactInteractionMeta = {\n consumer,\n provider,\n description: reified.description ?? '<unknown>',\n states: (reified.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n };\n span.setAttributes(buildPactAttributes(meta));\n return handler(reified);\n });\n span.setAttributes(outcomeAttribute('passed'));\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: captured?.description ?? '<unknown>',\n states: (captured?.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n outcome: 'passed',\n start,\n });\n return result;\n } catch (error) {\n span.setAttributes(outcomeAttribute('failed'));\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: captured?.description ?? '<unknown>',\n states: (captured?.providerStates ?? []).map((s) => s.name),\n kind: 'message',\n outcome: 'failed',\n start,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n });\n };\n\n proto[INSTALLED] = true;\n}\n\nfunction patchHttpPact(mod: PactJsModule): void {\n const ctor = mod.PactV3;\n if (!ctor) return;\n const proto = ctor.prototype;\n if (proto[INSTALLED]) return;\n\n // Track interactions added to each PactV3 instance. Cleared after\n // executeTest emits ledger entries for them, so the same instance can\n // be reused across multiple test cases.\n const tracked = new WeakMap<object, HttpInteraction[]>();\n\n const originalAdd = proto.addInteraction as (i: HttpInteraction) => unknown;\n proto.addInteraction = function patchedAddInteraction(\n this: object,\n interaction: HttpInteraction,\n ) {\n const list = tracked.get(this) ?? [];\n list.push(interaction);\n tracked.set(this, list);\n return originalAdd.call(this, interaction);\n };\n\n const originalExecute = proto.executeTest as <T>(\n fn: (server: { url: string; port: number }) => Promise<T>,\n ) => Promise<T | undefined>;\n\n proto.executeTest = async function patchedExecuteTest<T>(\n this: { opts?: { consumer?: string; provider?: string } },\n fn: (server: { url: string; port: number }) => Promise<T>,\n ): Promise<T | undefined> {\n const start = process.hrtime.bigint();\n const consumer = this.opts?.consumer ?? '<unknown>';\n const provider = this.opts?.provider ?? '<unknown>';\n const interactions = tracked.get(this) ?? [];\n tracked.set(this, []);\n\n return autotelSpan('pact.interaction', async (span) => {\n span.setAttributes({\n 'pact.consumer': consumer,\n 'pact.provider': provider,\n 'pact.kind': 'http',\n });\n // If multiple interactions were added before executeTest, the span\n // attributes describe the first; ledger entries are still written\n // for each. Most real tests use one interaction per executeTest.\n const first = interactions[0];\n if (first) {\n span.setAttributes(\n buildPactAttributes({\n consumer,\n provider,\n description: first.uponReceiving,\n states: (first.states ?? []).map((s) => s.description),\n kind: 'http',\n }),\n );\n }\n\n try {\n const result = (await originalExecute.call(this, fn)) as T | undefined;\n span.setAttributes(outcomeAttribute('passed'));\n for (const i of interactions) {\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: i.uponReceiving,\n states: (i.states ?? []).map((s) => s.description),\n kind: 'http',\n outcome: 'passed',\n start,\n });\n }\n return result;\n } catch (error_) {\n span.setAttributes(outcomeAttribute('failed'));\n const error = error_ instanceof Error ? error_.message : String(error_);\n for (const i of interactions) {\n writeAutoLedgerEntry({\n consumer,\n provider,\n interaction: i.uponReceiving,\n states: (i.states ?? []).map((s) => s.description),\n kind: 'http',\n outcome: 'failed',\n start,\n error,\n });\n }\n throw error_;\n }\n });\n };\n\n proto[INSTALLED] = true;\n}\n\nfunction writeAutoLedgerEntry(args: {\n consumer: string;\n provider: string;\n interaction: string;\n states: string[];\n kind: 'message' | 'http';\n outcome: 'passed' | 'failed';\n start: bigint;\n error?: string;\n}): void {\n const ctx = getActiveSpan()?.spanContext();\n const entry: InteractionLedgerEntry = {\n type: 'interaction',\n spec: LEDGER_ENTRY_SPEC,\n consumer: args.consumer,\n provider: args.provider,\n interaction: args.interaction,\n states: args.states,\n kind: args.kind,\n source: 'test',\n role: 'consumer',\n outcome: args.outcome,\n duration_ms: Number(process.hrtime.bigint() - args.start) / 1e6,\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 error: args.error,\n };\n appendLedgerEntry(entry);\n}\n\n// Install on import — this file is meant to be loaded once as a setup file.\ninstallAutoWrap();\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"broker.cjs","names":[],"sources":["../src/broker.ts"],"sourcesContent":["import type { BrokerVerification } from './types.js';\n\nexport interface BrokerConfig {\n baseUrl: string;\n token?: string;\n username?: string;\n password?: string;\n}\n\nexport interface ConsumerProviderPair {\n consumer: string;\n provider: string;\n}\n\nfunction authHeaders(config: BrokerConfig): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: 'application/json',\n };\n if (config.token) {\n headers.Authorization = `Bearer ${config.token}`;\n } else if (config.username && config.password) {\n const encoded = Buffer.from(`${config.username}:${config.password}`).toString('base64');\n headers.Authorization = `Basic ${encoded}`;\n }\n return headers;\n}\n\nfunction trimBaseUrl(url: string): string {\n return url.replace(/\\/$/, '');\n}\n\n/**\n * Parse Pact Broker latest verification result payload.\n */\nexport function parseBrokerVerificationResult(\n consumer: string,\n provider: string,\n json: unknown,\n): BrokerVerification | null {\n if (!json || typeof json !== 'object') return null;\n const body = json as Record<string, unknown>;\n const success =\n body.success === true ||\n (body.success === undefined && body.result === 'success');\n const verifiedAt =\n typeof body.verifiedAt === 'string'\n ? body.verifiedAt\n : typeof body.verified_at === 'string'\n ? body.verified_at\n : typeof body.createdAt === 'string'\n ? body.createdAt\n : undefined;\n\n return {\n consumer,\n provider,\n success: !!success,\n verifiedAt,\n };\n}\n\n/**\n * Fetch latest verification results for each consumer–provider pair.\n */\nexport async function fetchBrokerVerifications(\n config: BrokerConfig,\n pairs: ConsumerProviderPair[],\n): Promise<BrokerVerification[]> {\n const base = trimBaseUrl(config.baseUrl);\n const headers = authHeaders(config);\n const results: BrokerVerification[] = [];\n\n for (const { consumer, provider } of pairs) {\n const url = `${base}/pacts/provider/${encodeURIComponent(provider)}/consumer/${encodeURIComponent(consumer)}/latest/verification-results`;\n try {\n const res = await fetch(url, { headers });\n if (!res.ok) {\n results.push({\n consumer,\n provider,\n success: false,\n error: `HTTP ${res.status} ${res.statusText}`.trim(),\n });\n continue;\n }\n const json: unknown = await res.json();\n const parsed = parseBrokerVerificationResult(consumer, provider, json);\n results.push(\n parsed ?? {\n consumer,\n provider,\n success: false,\n error: 'Unparseable broker response',\n },\n );\n } catch (error) {\n results.push({\n consumer,\n provider,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return results;\n}\n\nexport function brokerConfigFromEnv(): BrokerConfig | undefined {\n const baseUrl = process.env.PACT_BROKER_BASE_URL;\n if (!baseUrl) return undefined;\n return {\n baseUrl,\n token: process.env.PACT_BROKER_TOKEN,\n username: process.env.PACT_BROKER_USERNAME,\n password: process.env.PACT_BROKER_PASSWORD,\n };\n}\n"],"mappings":";;;AAcA,SAAS,YAAY,QAA8C;CACjE,MAAM,UAAkC,EACtC,QAAQ,mBACV;CACA,IAAI,OAAO,OACT,QAAQ,gBAAgB,UAAU,OAAO;MACpC,IAAI,OAAO,YAAY,OAAO,UAEnC,QAAQ,gBAAgB,SADR,OAAO,KAAK,GAAG,OAAO,SAAS,GAAG,OAAO,UAAU,CAAC,CAAC,SAAS,QACvC;CAEzC,OAAO;AACT;AAEA,SAAS,YAAY,KAAqB;CACxC,OAAO,IAAI,QAAQ,OAAO,EAAE;AAC9B;;;;AAKA,SAAgB,8BACd,UACA,UACA,MAC2B;CAC3B,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,OAAO;CACb,MAAM,UACJ,KAAK,YAAY,QAChB,KAAK,YAAY,UAAa,KAAK,WAAW;CACjD,MAAM,aACJ,OAAO,KAAK,eAAe,WACvB,KAAK,aACL,OAAO,KAAK,gBAAgB,WAC1B,KAAK,cACL,OAAO,KAAK,cAAc,WACxB,KAAK,YACL;CAEV,OAAO;EACL;EACA;EACA,SAAS,CAAC,CAAC;EACX;CACF;AACF;;;;AAKA,eAAsB,yBACpB,QACA,OAC+B;CAC/B,MAAM,OAAO,YAAY,OAAO,OAAO;CACvC,MAAM,UAAU,YAAY,MAAM;CAClC,MAAM,UAAgC,CAAC;CAEvC,KAAK,MAAM,EAAE,UAAU,cAAc,OAAO;EAC1C,MAAM,MAAM,GAAG,KAAK,kBAAkB,mBAAmB,QAAQ,EAAE,YAAY,mBAAmB,QAAQ,EAAE;EAC5G,IAAI;GACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;GACxC,IAAI,CAAC,IAAI,IAAI;IACX,QAAQ,KAAK;KACX;KACA;KACA,SAAS;KACT,OAAO,QAAQ,IAAI,OAAO,GAAG,IAAI,aAAa,KAAK;IACrD,CAAC;IACD;GACF;GAEA,MAAM,SAAS,8BAA8B,UAAU,UAAU,MADrC,IAAI,KAAK,CACgC;GACrE,QAAQ,KACN,UAAU;IACR;IACA;IACA,SAAS;IACT,OAAO;GACT,CACF;EACF,SAAS,OAAO;GACd,QAAQ,KAAK;IACX;IACA;IACA,SAAS;IACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC9D,CAAC;EACH;CACF;CAEA,OAAO;AACT;AAEA,SAAgB,sBAAgD;CAC9D,MAAM,UAAU,QAAQ,IAAI;CAC5B,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL;EACA,OAAO,QAAQ,IAAI;EACnB,UAAU,QAAQ,IAAI;EACtB,UAAU,QAAQ,IAAI;CACxB;AACF"}
1
+ {"version":3,"file":"broker.cjs","names":[],"sources":["../src/broker.ts"],"sourcesContent":["import type { BrokerVerification } from './types.js';\n\nexport interface BrokerConfig {\n baseUrl: string;\n token?: string;\n username?: string;\n password?: string;\n}\n\nexport interface ConsumerProviderPair {\n consumer: string;\n provider: string;\n}\n\nfunction authHeaders(config: BrokerConfig): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: 'application/json',\n };\n if (config.token) {\n headers.Authorization = `Bearer ${config.token}`;\n } else if (config.username && config.password) {\n const encoded = Buffer.from(\n `${config.username}:${config.password}`,\n ).toString('base64');\n headers.Authorization = `Basic ${encoded}`;\n }\n return headers;\n}\n\nfunction trimBaseUrl(url: string): string {\n return url.replace(/\\/$/, '');\n}\n\n/**\n * Parse Pact Broker latest verification result payload.\n */\nexport function parseBrokerVerificationResult(\n consumer: string,\n provider: string,\n json: unknown,\n): BrokerVerification | null {\n if (!json || typeof json !== 'object') return null;\n const body = json as Record<string, unknown>;\n const success =\n body.success === true ||\n (body.success === undefined && body.result === 'success');\n const verifiedAt =\n typeof body.verifiedAt === 'string'\n ? body.verifiedAt\n : typeof body.verified_at === 'string'\n ? body.verified_at\n : typeof body.createdAt === 'string'\n ? body.createdAt\n : undefined;\n\n return {\n consumer,\n provider,\n success: !!success,\n verifiedAt,\n };\n}\n\n/**\n * Fetch latest verification results for each consumer–provider pair.\n */\nexport async function fetchBrokerVerifications(\n config: BrokerConfig,\n pairs: ConsumerProviderPair[],\n): Promise<BrokerVerification[]> {\n const base = trimBaseUrl(config.baseUrl);\n const headers = authHeaders(config);\n const results: BrokerVerification[] = [];\n\n for (const { consumer, provider } of pairs) {\n const url = `${base}/pacts/provider/${encodeURIComponent(provider)}/consumer/${encodeURIComponent(consumer)}/latest/verification-results`;\n try {\n const res = await fetch(url, { headers });\n if (!res.ok) {\n results.push({\n consumer,\n provider,\n success: false,\n error: `HTTP ${res.status} ${res.statusText}`.trim(),\n });\n continue;\n }\n const json: unknown = await res.json();\n const parsed = parseBrokerVerificationResult(consumer, provider, json);\n results.push(\n parsed ?? {\n consumer,\n provider,\n success: false,\n error: 'Unparseable broker response',\n },\n );\n } catch (error) {\n results.push({\n consumer,\n provider,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return results;\n}\n\nexport function brokerConfigFromEnv(): BrokerConfig | undefined {\n const baseUrl = process.env.PACT_BROKER_BASE_URL;\n if (!baseUrl) return undefined;\n return {\n baseUrl,\n token: process.env.PACT_BROKER_TOKEN,\n username: process.env.PACT_BROKER_USERNAME,\n password: process.env.PACT_BROKER_PASSWORD,\n };\n}\n"],"mappings":";;;AAcA,SAAS,YAAY,QAA8C;CACjE,MAAM,UAAkC,EACtC,QAAQ,mBACV;CACA,IAAI,OAAO,OACT,QAAQ,gBAAgB,UAAU,OAAO;MACpC,IAAI,OAAO,YAAY,OAAO,UAInC,QAAQ,gBAAgB,SAHR,OAAO,KACrB,GAAG,OAAO,SAAS,GAAG,OAAO,UAC/B,CAAC,CAAC,SAAS,QAC4B;CAEzC,OAAO;AACT;AAEA,SAAS,YAAY,KAAqB;CACxC,OAAO,IAAI,QAAQ,OAAO,EAAE;AAC9B;;;;AAKA,SAAgB,8BACd,UACA,UACA,MAC2B;CAC3B,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,OAAO;CACb,MAAM,UACJ,KAAK,YAAY,QAChB,KAAK,YAAY,UAAa,KAAK,WAAW;CACjD,MAAM,aACJ,OAAO,KAAK,eAAe,WACvB,KAAK,aACL,OAAO,KAAK,gBAAgB,WAC1B,KAAK,cACL,OAAO,KAAK,cAAc,WACxB,KAAK,YACL;CAEV,OAAO;EACL;EACA;EACA,SAAS,CAAC,CAAC;EACX;CACF;AACF;;;;AAKA,eAAsB,yBACpB,QACA,OAC+B;CAC/B,MAAM,OAAO,YAAY,OAAO,OAAO;CACvC,MAAM,UAAU,YAAY,MAAM;CAClC,MAAM,UAAgC,CAAC;CAEvC,KAAK,MAAM,EAAE,UAAU,cAAc,OAAO;EAC1C,MAAM,MAAM,GAAG,KAAK,kBAAkB,mBAAmB,QAAQ,EAAE,YAAY,mBAAmB,QAAQ,EAAE;EAC5G,IAAI;GACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;GACxC,IAAI,CAAC,IAAI,IAAI;IACX,QAAQ,KAAK;KACX;KACA;KACA,SAAS;KACT,OAAO,QAAQ,IAAI,OAAO,GAAG,IAAI,aAAa,KAAK;IACrD,CAAC;IACD;GACF;GAEA,MAAM,SAAS,8BAA8B,UAAU,UAAU,MADrC,IAAI,KAAK,CACgC;GACrE,QAAQ,KACN,UAAU;IACR;IACA;IACA,SAAS;IACT,OAAO;GACT,CACF;EACF,SAAS,OAAO;GACd,QAAQ,KAAK;IACX;IACA;IACA,SAAS;IACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC9D,CAAC;EACH;CACF;CAEA,OAAO;AACT;AAEA,SAAgB,sBAAgD;CAC9D,MAAM,UAAU,QAAQ,IAAI;CAC5B,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL;EACA,OAAO,QAAQ,IAAI;EACnB,UAAU,QAAQ,IAAI;EACtB,UAAU,QAAQ,IAAI;CACxB;AACF"}
package/dist/broker.d.cts CHANGED
@@ -1,5 +1,4 @@
1
- import { i as BrokerVerification } from "./types-D_0kgK2e.cjs";
2
-
1
+ import { i as BrokerVerification } from "./types-DpHqSzxb.cjs";
3
2
  //#region src/broker.d.ts
4
3
  interface BrokerConfig {
5
4
  baseUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"broker.d.cts","names":[],"sources":["../src/broker.ts"],"mappings":";;;UAEiB,YAAA;EACf,OAAA;EACA,KAAA;EACA,QAAA;EACA,QAAA;AAAA;AAAA,UAGe,oBAAA;EACf,QAAA;EACA,QAAQ;AAAA;;AALA;AAGV;iBAyBgB,6BAAA,CACd,QAAA,UACA,QAAA,UACA,IAAA,YACC,kBAAkB;;;AA3BX;iBAqDY,wBAAA,CACpB,MAAA,EAAQ,YAAA,EACR,KAAA,EAAO,oBAAA,KACN,OAAA,CAAQ,kBAAA;AAAA,iBAyCK,mBAAA,IAAuB,YAAY"}
1
+ {"version":3,"file":"broker.d.cts","names":[],"sources":["../src/broker.ts"],"mappings":";;UAEiB;EACf;EACA;EACA;EACA;;UAGe;EACf;EACA;;;;;iBAyBc,8BACd,kBACA,kBACA,gBACC;;;;iBA0BmB,yBACpB,QAAQ,cACR,OAAO,yBACN,QAAQ;iBAyCK,uBAAuB"}
package/dist/broker.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { i as BrokerVerification } from "./types-D_0kgK2e.js";
2
-
1
+ import { i as BrokerVerification } from "./types-DpHqSzxb.js";
3
2
  //#region src/broker.d.ts
4
3
  interface BrokerConfig {
5
4
  baseUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"broker.d.ts","names":[],"sources":["../src/broker.ts"],"mappings":";;;UAEiB,YAAA;EACf,OAAA;EACA,KAAA;EACA,QAAA;EACA,QAAA;AAAA;AAAA,UAGe,oBAAA;EACf,QAAA;EACA,QAAQ;AAAA;;AALA;AAGV;iBAyBgB,6BAAA,CACd,QAAA,UACA,QAAA,UACA,IAAA,YACC,kBAAkB;;;AA3BX;iBAqDY,wBAAA,CACpB,MAAA,EAAQ,YAAA,EACR,KAAA,EAAO,oBAAA,KACN,OAAA,CAAQ,kBAAA;AAAA,iBAyCK,mBAAA,IAAuB,YAAY"}
1
+ {"version":3,"file":"broker.d.ts","names":[],"sources":["../src/broker.ts"],"mappings":";;UAEiB;EACf;EACA;EACA;EACA;;UAGe;EACf;EACA;;;;;iBAyBc,8BACd,kBACA,kBACA,gBACC;;;;iBA0BmB,yBACpB,QAAQ,cACR,OAAO,yBACN,QAAQ;iBAyCK,uBAAuB"}
@@ -1 +1 @@
1
- {"version":3,"file":"broker.js","names":[],"sources":["../src/broker.ts"],"sourcesContent":["import type { BrokerVerification } from './types.js';\n\nexport interface BrokerConfig {\n baseUrl: string;\n token?: string;\n username?: string;\n password?: string;\n}\n\nexport interface ConsumerProviderPair {\n consumer: string;\n provider: string;\n}\n\nfunction authHeaders(config: BrokerConfig): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: 'application/json',\n };\n if (config.token) {\n headers.Authorization = `Bearer ${config.token}`;\n } else if (config.username && config.password) {\n const encoded = Buffer.from(`${config.username}:${config.password}`).toString('base64');\n headers.Authorization = `Basic ${encoded}`;\n }\n return headers;\n}\n\nfunction trimBaseUrl(url: string): string {\n return url.replace(/\\/$/, '');\n}\n\n/**\n * Parse Pact Broker latest verification result payload.\n */\nexport function parseBrokerVerificationResult(\n consumer: string,\n provider: string,\n json: unknown,\n): BrokerVerification | null {\n if (!json || typeof json !== 'object') return null;\n const body = json as Record<string, unknown>;\n const success =\n body.success === true ||\n (body.success === undefined && body.result === 'success');\n const verifiedAt =\n typeof body.verifiedAt === 'string'\n ? body.verifiedAt\n : typeof body.verified_at === 'string'\n ? body.verified_at\n : typeof body.createdAt === 'string'\n ? body.createdAt\n : undefined;\n\n return {\n consumer,\n provider,\n success: !!success,\n verifiedAt,\n };\n}\n\n/**\n * Fetch latest verification results for each consumer–provider pair.\n */\nexport async function fetchBrokerVerifications(\n config: BrokerConfig,\n pairs: ConsumerProviderPair[],\n): Promise<BrokerVerification[]> {\n const base = trimBaseUrl(config.baseUrl);\n const headers = authHeaders(config);\n const results: BrokerVerification[] = [];\n\n for (const { consumer, provider } of pairs) {\n const url = `${base}/pacts/provider/${encodeURIComponent(provider)}/consumer/${encodeURIComponent(consumer)}/latest/verification-results`;\n try {\n const res = await fetch(url, { headers });\n if (!res.ok) {\n results.push({\n consumer,\n provider,\n success: false,\n error: `HTTP ${res.status} ${res.statusText}`.trim(),\n });\n continue;\n }\n const json: unknown = await res.json();\n const parsed = parseBrokerVerificationResult(consumer, provider, json);\n results.push(\n parsed ?? {\n consumer,\n provider,\n success: false,\n error: 'Unparseable broker response',\n },\n );\n } catch (error) {\n results.push({\n consumer,\n provider,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return results;\n}\n\nexport function brokerConfigFromEnv(): BrokerConfig | undefined {\n const baseUrl = process.env.PACT_BROKER_BASE_URL;\n if (!baseUrl) return undefined;\n return {\n baseUrl,\n token: process.env.PACT_BROKER_TOKEN,\n username: process.env.PACT_BROKER_USERNAME,\n password: process.env.PACT_BROKER_PASSWORD,\n };\n}\n"],"mappings":";AAcA,SAAS,YAAY,QAA8C;CACjE,MAAM,UAAkC,EACtC,QAAQ,mBACV;CACA,IAAI,OAAO,OACT,QAAQ,gBAAgB,UAAU,OAAO;MACpC,IAAI,OAAO,YAAY,OAAO,UAEnC,QAAQ,gBAAgB,SADR,OAAO,KAAK,GAAG,OAAO,SAAS,GAAG,OAAO,UAAU,CAAC,CAAC,SAAS,QACvC;CAEzC,OAAO;AACT;AAEA,SAAS,YAAY,KAAqB;CACxC,OAAO,IAAI,QAAQ,OAAO,EAAE;AAC9B;;;;AAKA,SAAgB,8BACd,UACA,UACA,MAC2B;CAC3B,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,OAAO;CACb,MAAM,UACJ,KAAK,YAAY,QAChB,KAAK,YAAY,UAAa,KAAK,WAAW;CACjD,MAAM,aACJ,OAAO,KAAK,eAAe,WACvB,KAAK,aACL,OAAO,KAAK,gBAAgB,WAC1B,KAAK,cACL,OAAO,KAAK,cAAc,WACxB,KAAK,YACL;CAEV,OAAO;EACL;EACA;EACA,SAAS,CAAC,CAAC;EACX;CACF;AACF;;;;AAKA,eAAsB,yBACpB,QACA,OAC+B;CAC/B,MAAM,OAAO,YAAY,OAAO,OAAO;CACvC,MAAM,UAAU,YAAY,MAAM;CAClC,MAAM,UAAgC,CAAC;CAEvC,KAAK,MAAM,EAAE,UAAU,cAAc,OAAO;EAC1C,MAAM,MAAM,GAAG,KAAK,kBAAkB,mBAAmB,QAAQ,EAAE,YAAY,mBAAmB,QAAQ,EAAE;EAC5G,IAAI;GACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;GACxC,IAAI,CAAC,IAAI,IAAI;IACX,QAAQ,KAAK;KACX;KACA;KACA,SAAS;KACT,OAAO,QAAQ,IAAI,OAAO,GAAG,IAAI,aAAa,KAAK;IACrD,CAAC;IACD;GACF;GAEA,MAAM,SAAS,8BAA8B,UAAU,UAAU,MADrC,IAAI,KAAK,CACgC;GACrE,QAAQ,KACN,UAAU;IACR;IACA;IACA,SAAS;IACT,OAAO;GACT,CACF;EACF,SAAS,OAAO;GACd,QAAQ,KAAK;IACX;IACA;IACA,SAAS;IACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC9D,CAAC;EACH;CACF;CAEA,OAAO;AACT;AAEA,SAAgB,sBAAgD;CAC9D,MAAM,UAAU,QAAQ,IAAI;CAC5B,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL;EACA,OAAO,QAAQ,IAAI;EACnB,UAAU,QAAQ,IAAI;EACtB,UAAU,QAAQ,IAAI;CACxB;AACF"}
1
+ {"version":3,"file":"broker.js","names":[],"sources":["../src/broker.ts"],"sourcesContent":["import type { BrokerVerification } from './types.js';\n\nexport interface BrokerConfig {\n baseUrl: string;\n token?: string;\n username?: string;\n password?: string;\n}\n\nexport interface ConsumerProviderPair {\n consumer: string;\n provider: string;\n}\n\nfunction authHeaders(config: BrokerConfig): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: 'application/json',\n };\n if (config.token) {\n headers.Authorization = `Bearer ${config.token}`;\n } else if (config.username && config.password) {\n const encoded = Buffer.from(\n `${config.username}:${config.password}`,\n ).toString('base64');\n headers.Authorization = `Basic ${encoded}`;\n }\n return headers;\n}\n\nfunction trimBaseUrl(url: string): string {\n return url.replace(/\\/$/, '');\n}\n\n/**\n * Parse Pact Broker latest verification result payload.\n */\nexport function parseBrokerVerificationResult(\n consumer: string,\n provider: string,\n json: unknown,\n): BrokerVerification | null {\n if (!json || typeof json !== 'object') return null;\n const body = json as Record<string, unknown>;\n const success =\n body.success === true ||\n (body.success === undefined && body.result === 'success');\n const verifiedAt =\n typeof body.verifiedAt === 'string'\n ? body.verifiedAt\n : typeof body.verified_at === 'string'\n ? body.verified_at\n : typeof body.createdAt === 'string'\n ? body.createdAt\n : undefined;\n\n return {\n consumer,\n provider,\n success: !!success,\n verifiedAt,\n };\n}\n\n/**\n * Fetch latest verification results for each consumer–provider pair.\n */\nexport async function fetchBrokerVerifications(\n config: BrokerConfig,\n pairs: ConsumerProviderPair[],\n): Promise<BrokerVerification[]> {\n const base = trimBaseUrl(config.baseUrl);\n const headers = authHeaders(config);\n const results: BrokerVerification[] = [];\n\n for (const { consumer, provider } of pairs) {\n const url = `${base}/pacts/provider/${encodeURIComponent(provider)}/consumer/${encodeURIComponent(consumer)}/latest/verification-results`;\n try {\n const res = await fetch(url, { headers });\n if (!res.ok) {\n results.push({\n consumer,\n provider,\n success: false,\n error: `HTTP ${res.status} ${res.statusText}`.trim(),\n });\n continue;\n }\n const json: unknown = await res.json();\n const parsed = parseBrokerVerificationResult(consumer, provider, json);\n results.push(\n parsed ?? {\n consumer,\n provider,\n success: false,\n error: 'Unparseable broker response',\n },\n );\n } catch (error) {\n results.push({\n consumer,\n provider,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return results;\n}\n\nexport function brokerConfigFromEnv(): BrokerConfig | undefined {\n const baseUrl = process.env.PACT_BROKER_BASE_URL;\n if (!baseUrl) return undefined;\n return {\n baseUrl,\n token: process.env.PACT_BROKER_TOKEN,\n username: process.env.PACT_BROKER_USERNAME,\n password: process.env.PACT_BROKER_PASSWORD,\n };\n}\n"],"mappings":";AAcA,SAAS,YAAY,QAA8C;CACjE,MAAM,UAAkC,EACtC,QAAQ,mBACV;CACA,IAAI,OAAO,OACT,QAAQ,gBAAgB,UAAU,OAAO;MACpC,IAAI,OAAO,YAAY,OAAO,UAInC,QAAQ,gBAAgB,SAHR,OAAO,KACrB,GAAG,OAAO,SAAS,GAAG,OAAO,UAC/B,CAAC,CAAC,SAAS,QAC4B;CAEzC,OAAO;AACT;AAEA,SAAS,YAAY,KAAqB;CACxC,OAAO,IAAI,QAAQ,OAAO,EAAE;AAC9B;;;;AAKA,SAAgB,8BACd,UACA,UACA,MAC2B;CAC3B,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,OAAO;CACb,MAAM,UACJ,KAAK,YAAY,QAChB,KAAK,YAAY,UAAa,KAAK,WAAW;CACjD,MAAM,aACJ,OAAO,KAAK,eAAe,WACvB,KAAK,aACL,OAAO,KAAK,gBAAgB,WAC1B,KAAK,cACL,OAAO,KAAK,cAAc,WACxB,KAAK,YACL;CAEV,OAAO;EACL;EACA;EACA,SAAS,CAAC,CAAC;EACX;CACF;AACF;;;;AAKA,eAAsB,yBACpB,QACA,OAC+B;CAC/B,MAAM,OAAO,YAAY,OAAO,OAAO;CACvC,MAAM,UAAU,YAAY,MAAM;CAClC,MAAM,UAAgC,CAAC;CAEvC,KAAK,MAAM,EAAE,UAAU,cAAc,OAAO;EAC1C,MAAM,MAAM,GAAG,KAAK,kBAAkB,mBAAmB,QAAQ,EAAE,YAAY,mBAAmB,QAAQ,EAAE;EAC5G,IAAI;GACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;GACxC,IAAI,CAAC,IAAI,IAAI;IACX,QAAQ,KAAK;KACX;KACA;KACA,SAAS;KACT,OAAO,QAAQ,IAAI,OAAO,GAAG,IAAI,aAAa,KAAK;IACrD,CAAC;IACD;GACF;GAEA,MAAM,SAAS,8BAA8B,UAAU,UAAU,MADrC,IAAI,KAAK,CACgC;GACrE,QAAQ,KACN,UAAU;IACR;IACA;IACA,SAAS;IACT,OAAO;GACT,CACF;EACF,SAAS,OAAO;GACd,QAAQ,KAAK;IACX;IACA;IACA,SAAS;IACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC9D,CAAC;EACH;CACF;CAEA,OAAO;AACT;AAEA,SAAgB,sBAAgD;CAC9D,MAAM,UAAU,QAAQ,IAAI;CAC5B,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL;EACA,OAAO,QAAQ,IAAI;EACnB,UAAU,QAAQ,IAAI;EACtB,UAAU,QAAQ,IAAI;CACxB;AACF"}