@adcp/sdk 13.0.0-rc.8 → 13.0.0-rc.9

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 (200) hide show
  1. package/dist/lib/adapters/roster-account-store.d.mts +9 -8
  2. package/dist/lib/adapters/roster-account-store.d.ts +9 -8
  3. package/dist/lib/adapters/roster-account-store.d.ts.map +1 -1
  4. package/dist/lib/adapters/roster-account-store.js +2 -1
  5. package/dist/lib/adapters/roster-account-store.js.map +1 -1
  6. package/dist/lib/adapters/roster-account-store.mjs +2 -1
  7. package/dist/lib/adapters/roster-account-store.mjs.map +1 -1
  8. package/dist/lib/net/address-guards.d.ts.map +1 -1
  9. package/dist/lib/net/address-guards.js +6 -0
  10. package/dist/lib/net/address-guards.js.map +1 -1
  11. package/dist/lib/net/address-guards.mjs +6 -0
  12. package/dist/lib/net/address-guards.mjs.map +1 -1
  13. package/dist/lib/net/ssrf-fetch.d.ts.map +1 -1
  14. package/dist/lib/net/ssrf-fetch.js +66 -53
  15. package/dist/lib/net/ssrf-fetch.js.map +1 -1
  16. package/dist/lib/net/ssrf-fetch.mjs +66 -53
  17. package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
  18. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  19. package/dist/lib/server/account-mode.d.mts +2 -1
  20. package/dist/lib/server/account-mode.d.ts +2 -1
  21. package/dist/lib/server/account-mode.d.ts.map +1 -1
  22. package/dist/lib/server/account-mode.js +1 -0
  23. package/dist/lib/server/account-mode.js.map +1 -1
  24. package/dist/lib/server/account-mode.mjs +1 -0
  25. package/dist/lib/server/account-mode.mjs.map +1 -1
  26. package/dist/lib/server/create-adcp-server.d.mts +10 -6
  27. package/dist/lib/server/create-adcp-server.d.ts +10 -6
  28. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  29. package/dist/lib/server/create-adcp-server.js +2 -1
  30. package/dist/lib/server/create-adcp-server.js.map +1 -1
  31. package/dist/lib/server/create-adcp-server.mjs +2 -1
  32. package/dist/lib/server/create-adcp-server.mjs.map +1 -1
  33. package/dist/lib/server/decisioning/account.d.mts +16 -11
  34. package/dist/lib/server/decisioning/account.d.ts +16 -11
  35. package/dist/lib/server/decisioning/account.d.ts.map +1 -1
  36. package/dist/lib/server/decisioning/account.js.map +1 -1
  37. package/dist/lib/server/decisioning/account.mjs.map +1 -1
  38. package/dist/lib/server/decisioning/pagination.d.mts +2 -0
  39. package/dist/lib/server/decisioning/pagination.d.ts +2 -0
  40. package/dist/lib/server/decisioning/pagination.d.ts.map +1 -1
  41. package/dist/lib/server/decisioning/pagination.js.map +1 -1
  42. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +20 -14
  43. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +20 -14
  44. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
  45. package/dist/lib/server/decisioning/runtime/from-platform.js +32 -6
  46. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  47. package/dist/lib/server/decisioning/runtime/from-platform.mjs +32 -6
  48. package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
  49. package/dist/lib/server/decisioning/specialisms/sales.d.mts +16 -4
  50. package/dist/lib/server/decisioning/specialisms/sales.d.ts +16 -4
  51. package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
  52. package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
  53. package/dist/lib/server/index.d.ts.map +1 -1
  54. package/dist/lib/server/index.js.map +1 -1
  55. package/dist/lib/server/index.mjs.map +1 -1
  56. package/dist/lib/server/legacy/v5/index.d.mts +2 -1
  57. package/dist/lib/server/legacy/v5/index.d.ts +2 -1
  58. package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
  59. package/dist/lib/server/legacy/v5/index.js.map +1 -1
  60. package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
  61. package/dist/lib/testing/compliance/comply.d.mts +2 -0
  62. package/dist/lib/testing/compliance/comply.d.ts +2 -0
  63. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  64. package/dist/lib/testing/compliance/comply.js +45 -14
  65. package/dist/lib/testing/compliance/comply.js.map +1 -1
  66. package/dist/lib/testing/compliance/comply.mjs +45 -14
  67. package/dist/lib/testing/compliance/comply.mjs.map +1 -1
  68. package/dist/lib/testing/compliance/index.d.mts +1 -1
  69. package/dist/lib/testing/compliance/index.d.ts +1 -1
  70. package/dist/lib/testing/compliance/index.d.ts.map +1 -1
  71. package/dist/lib/testing/compliance/index.js.map +1 -1
  72. package/dist/lib/testing/compliance/index.mjs.map +1 -1
  73. package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -1
  74. package/dist/lib/testing/compliance/storyboard-tracks.js +9 -1
  75. package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -1
  76. package/dist/lib/testing/compliance/storyboard-tracks.mjs +9 -1
  77. package/dist/lib/testing/compliance/storyboard-tracks.mjs.map +1 -1
  78. package/dist/lib/testing/compliance/types.d.mts +26 -25
  79. package/dist/lib/testing/compliance/types.d.ts +26 -25
  80. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  81. package/dist/lib/testing/compliance/types.js.map +1 -1
  82. package/dist/lib/testing/index.d.mts +2 -2
  83. package/dist/lib/testing/index.d.ts +2 -2
  84. package/dist/lib/testing/index.d.ts.map +1 -1
  85. package/dist/lib/testing/index.js +16 -0
  86. package/dist/lib/testing/index.js.map +1 -1
  87. package/dist/lib/testing/index.mjs +16 -0
  88. package/dist/lib/testing/index.mjs.map +1 -1
  89. package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
  90. package/dist/lib/testing/scenarios/media-buy.js +20 -12
  91. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  92. package/dist/lib/testing/scenarios/media-buy.mjs +20 -12
  93. package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
  94. package/dist/lib/testing/storyboard/creative-assets.d.mts +23 -0
  95. package/dist/lib/testing/storyboard/creative-assets.d.ts +23 -0
  96. package/dist/lib/testing/storyboard/creative-assets.d.ts.map +1 -1
  97. package/dist/lib/testing/storyboard/creative-assets.js +322 -45
  98. package/dist/lib/testing/storyboard/creative-assets.js.map +1 -1
  99. package/dist/lib/testing/storyboard/creative-assets.mjs +321 -45
  100. package/dist/lib/testing/storyboard/creative-assets.mjs.map +1 -1
  101. package/dist/lib/testing/storyboard/index.d.mts +4 -1
  102. package/dist/lib/testing/storyboard/index.d.ts +4 -1
  103. package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
  104. package/dist/lib/testing/storyboard/index.js +20 -0
  105. package/dist/lib/testing/storyboard/index.js.map +1 -1
  106. package/dist/lib/testing/storyboard/index.mjs +19 -0
  107. package/dist/lib/testing/storyboard/index.mjs.map +1 -1
  108. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.mts +21 -0
  109. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.ts +22 -0
  110. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.ts.map +1 -0
  111. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.js +668 -0
  112. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.js.map +1 -0
  113. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.mjs +638 -0
  114. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.mjs.map +1 -0
  115. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.mts +5 -0
  116. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.ts +6 -0
  117. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.ts.map +1 -0
  118. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.js +44 -0
  119. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.js.map +1 -0
  120. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.mjs +20 -0
  121. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.mjs.map +1 -0
  122. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.mts +38 -0
  123. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.ts +39 -0
  124. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.ts.map +1 -0
  125. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.js +17 -0
  126. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.js.map +1 -0
  127. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.mjs +1 -0
  128. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.mjs.map +1 -0
  129. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.mts +32 -0
  130. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.ts +33 -0
  131. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.ts.map +1 -0
  132. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.js +105 -0
  133. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.js.map +1 -0
  134. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.mjs +80 -0
  135. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.mjs.map +1 -0
  136. package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
  137. package/dist/lib/testing/storyboard/probes.js +2 -1
  138. package/dist/lib/testing/storyboard/probes.js.map +1 -1
  139. package/dist/lib/testing/storyboard/probes.mjs +2 -1
  140. package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
  141. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  142. package/dist/lib/testing/storyboard/runner.js +382 -69
  143. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  144. package/dist/lib/testing/storyboard/runner.mjs +387 -70
  145. package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
  146. package/dist/lib/testing/storyboard/seeding.js +3 -1
  147. package/dist/lib/testing/storyboard/seeding.js.map +1 -1
  148. package/dist/lib/testing/storyboard/seeding.mjs +3 -1
  149. package/dist/lib/testing/storyboard/seeding.mjs.map +1 -1
  150. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  151. package/dist/lib/testing/storyboard/task-map.js +16 -2
  152. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  153. package/dist/lib/testing/storyboard/task-map.mjs +16 -2
  154. package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
  155. package/dist/lib/testing/storyboard/trusted-match-context-replay.d.mts +11 -0
  156. package/dist/lib/testing/storyboard/trusted-match-context-replay.d.ts +12 -0
  157. package/dist/lib/testing/storyboard/trusted-match-context-replay.d.ts.map +1 -0
  158. package/dist/lib/testing/storyboard/trusted-match-context-replay.js +340 -0
  159. package/dist/lib/testing/storyboard/trusted-match-context-replay.js.map +1 -0
  160. package/dist/lib/testing/storyboard/trusted-match-context-replay.mjs +316 -0
  161. package/dist/lib/testing/storyboard/trusted-match-context-replay.mjs.map +1 -0
  162. package/dist/lib/testing/storyboard/types.d.mts +84 -15
  163. package/dist/lib/testing/storyboard/types.d.ts +84 -15
  164. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  165. package/dist/lib/testing/storyboard/types.js +2 -0
  166. package/dist/lib/testing/storyboard/types.js.map +1 -1
  167. package/dist/lib/testing/storyboard/types.mjs +2 -0
  168. package/dist/lib/testing/storyboard/types.mjs.map +1 -1
  169. package/dist/lib/testing/storyboard/validations.d.mts +3 -0
  170. package/dist/lib/testing/storyboard/validations.d.ts +3 -0
  171. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
  172. package/dist/lib/testing/storyboard/validations.js +64 -24
  173. package/dist/lib/testing/storyboard/validations.js.map +1 -1
  174. package/dist/lib/testing/storyboard/validations.mjs +64 -24
  175. package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
  176. package/dist/lib/types/server-payload-aliases.d.mts +8 -2
  177. package/dist/lib/types/server-payload-aliases.d.ts +8 -2
  178. package/dist/lib/types/server-payload-aliases.d.ts.map +1 -1
  179. package/dist/lib/types/server-payload-aliases.js.map +1 -1
  180. package/dist/lib/upstream-recorder/recorder.d.mts +7 -18
  181. package/dist/lib/upstream-recorder/recorder.d.ts +7 -18
  182. package/dist/lib/upstream-recorder/recorder.d.ts.map +1 -1
  183. package/dist/lib/upstream-recorder/recorder.js +17 -10
  184. package/dist/lib/upstream-recorder/recorder.js.map +1 -1
  185. package/dist/lib/upstream-recorder/recorder.mjs +12 -5
  186. package/dist/lib/upstream-recorder/recorder.mjs.map +1 -1
  187. package/dist/lib/utils/redact-secrets.d.ts.map +1 -1
  188. package/dist/lib/utils/redact-secrets.js +7 -1
  189. package/dist/lib/utils/redact-secrets.js.map +1 -1
  190. package/dist/lib/utils/redact-secrets.mjs +7 -1
  191. package/dist/lib/utils/redact-secrets.mjs.map +1 -1
  192. package/dist/lib/version.d.mts +3 -3
  193. package/dist/lib/version.d.ts +3 -3
  194. package/dist/lib/version.js +3 -3
  195. package/dist/lib/version.js.map +1 -1
  196. package/dist/lib/version.mjs +3 -3
  197. package/dist/lib/version.mjs.map +1 -1
  198. package/docs/guides/BUILD-AN-AGENT.md +4 -2
  199. package/docs/llms.txt +1 -1
  200. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/upstream-recorder/recorder.ts"],"sourcesContent":["/**\n * `createUpstreamRecorder` implementation. See `./types.ts` for the public\n * surface and `./README` (file header on `index.ts`) for the why.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { createHash } from 'node:crypto';\nimport {\n normalizeSecretKeyPattern,\n redactSecrets,\n secretKeyPatternMatches,\n SECRET_KEY_PATTERN,\n} from '../utils/redact-secrets';\nimport { globToRegExp } from '../utils/glob';\nimport { canonicalize } from '../utils/jcs';\nimport { MAX_JSON_DEPTH } from '../utils/json-depth';\nimport { IDENTIFIER_DIGEST_LIMIT } from './constants';\nimport {\n UpstreamRecorderScopeError,\n type QueryUpstreamTrafficResponse,\n type RecordInput,\n type RawRecordedCall,\n type RecordedCall,\n type UpstreamRecorder,\n type UpstreamRecorderDebugInfo,\n type UpstreamRecorderErrorEvent,\n type UpstreamRecorderOptions,\n type UpstreamRecorderQueryParams,\n type UpstreamRecorderQueryResult,\n} from './types';\n\nconst DEFAULT_BUFFER_SIZE = 1000;\nconst DEFAULT_TTL_MS = 60 * 60 * 1000; // 1h\nconst DEFAULT_QUERY_LIMIT = 100;\nconst DEFAULT_MAX_PAYLOAD_BYTES = 65_536; // mirrors spec's `recorded_calls[].payload.maxLength`\nconst MAX_BUFFER_SIZE = 100_000;\nconst MAX_TTL_MS = 24 * 60 * 60 * 1000; // 24h\nconst VALID_PURPOSES = new Set([\n 'platform_primary',\n 'measurement',\n 'attribution',\n 'creative_serving',\n 'identity',\n 'other',\n]);\n\nexport class PayloadDigestError extends Error {\n cause: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = 'PayloadDigestError';\n this.cause = cause;\n }\n}\n\n/**\n * Wrapped per-call entry stored in the buffer. Carries the public\n * `RecordedCall` plus the principal it was recorded under (kept off the\n * public type so cross-principal isolation is enforced at query time and\n * the principal never appears in the controller-response payload).\n */\ninterface InternalEntry {\n call: RawRecordedCall;\n principal: string;\n /** ms-since-epoch cached for TTL eviction; computed at insert time. */\n recordedAtMs: number;\n}\n\nfunction clamp(n: number | undefined, lo: number, hi: number, fallback: number): number {\n if (typeof n !== 'number' || !Number.isFinite(n)) return fallback;\n // Out-of-range values (zero / negative / above ceiling) revert to the\n // default rather than silently saturating at the limit — a typo'd\n // `bufferSize: -10` silently saturating to `1` would drop nearly\n // everything; the default is a much safer failure mode.\n if (n < lo || n > hi) return fallback;\n return n;\n}\n\nfunction isPrincipalString(p: unknown): p is string {\n return typeof p === 'string' && p.length > 0;\n}\n\n/**\n * Build the recorder. Producer-side companion to the\n * `runner-output-contract.yaml` v2.0.0 `upstream_traffic` storyboard\n * check (spec PR adcontextprotocol/adcp#3816).\n */\nexport function createUpstreamRecorder(options: UpstreamRecorderOptions = {}): UpstreamRecorder {\n const enabled = options.enabled ?? true;\n // Disabled-recorder fast-path: skip every wrapping cost. Production\n // builds get a no-op object whose methods compile to inlinable returns.\n if (!enabled) return makeNoopRecorder();\n\n // Production-enable footgun guard: when adopters ship with `enabled:\n // true` in `NODE_ENV=production` (forgot the gate, or the gate is\n // mis-evaluated), emit a one-time warning. ADCP_RECORDER_PRODUCTION_ACK=1\n // is the explicit acknowledgment escape hatch for the rare adopters\n // who legitimately want recording in prod.\n warnIfEnabledInProduction();\n\n const redactPattern = normalizeSecretKeyPattern(options.redactPattern ?? SECRET_KEY_PATTERN);\n const bufferSize = clamp(options.bufferSize, 1, MAX_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);\n const ttlMs = clamp(options.ttlMs, 1, MAX_TTL_MS, DEFAULT_TTL_MS);\n const maxPayloadBytes = clamp(options.maxPayloadBytes, 0, 16 * 1024 * 1024, DEFAULT_MAX_PAYLOAD_BYTES);\n const purpose = options.purpose;\n const strict = options.strict === true;\n const onError = options.onError;\n\n // Buffer: ordered by insertion (== timestamp ascending). Eviction is\n // FIFO when full or TTL-prune at record time.\n const buffer: InternalEntry[] = [];\n\n // Active-principal context. Set by `runWithPrincipal`; read by the\n // wrapped fetch and by `record` when no explicit principal is passed.\n const principalStorage = new AsyncLocalStorage<string>();\n\n // ────────────────────────────────────────────────────────────\n // Internal helpers\n // ────────────────────────────────────────────────────────────\n\n function emitError(event: UpstreamRecorderErrorEvent): void {\n if (!onError) return;\n try {\n onError(event);\n } catch {\n // onError MUST NOT crash the recorder — silent.\n }\n }\n\n function evictExpired(nowMs: number): void {\n if (buffer.length === 0) return;\n const cutoff = nowMs - ttlMs;\n let idx = 0;\n while (idx < buffer.length && buffer[idx]!.recordedAtMs < cutoff) idx++;\n if (idx > 0) buffer.splice(0, idx);\n }\n\n function pushEntry(entry: InternalEntry): void {\n buffer.push(entry);\n while (buffer.length > bufferSize) buffer.shift();\n }\n\n function applyRedactionToHeaders(headers: Record<string, string> | undefined): Record<string, string> {\n if (!headers) return {};\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) {\n const lower = k.toLowerCase();\n out[lower] = secretKeyPatternMatches(redactPattern, lower) ? '[redacted]' : v;\n }\n return out;\n }\n\n /**\n * Apply per-key redaction to the body. JSON bodies (objects + arrays)\n * walk through `redactSecrets`. Form-urlencoded strings parse +\n * key-redact + re-stringify so `access_token=xxx` is caught without\n * the adopter pre-redacting. Other string types pass through. Binary\n * types (Buffer / Blob / ArrayBuffer / TypedArray) are replaced with\n * a marker — the buffer should not hold raw bytes whose JSON-shape\n * is misleading downstream.\n */\n function applyRedactionToPayload(payload: unknown, contentType: string): { value: unknown; bytes: number } {\n const value = normalizeRecordedPayload(payload, contentType, redactPattern, maxPayloadBytes);\n return { value, bytes: payloadWireLength(value) };\n }\n\n function classifyPurpose(\n method: string,\n url: string,\n host: string,\n path: string,\n headers: Record<string, string>\n ): string | undefined {\n if (!purpose) return undefined;\n try {\n const value = purpose({ method, url, host, path, headers }) as unknown;\n if (value === undefined) return undefined;\n if (typeof value === 'string' && VALID_PURPOSES.has(value)) return value;\n emitError({ kind: 'classifier_invalid_purpose', purpose: typeof value === 'string' ? value : String(value) });\n return undefined;\n } catch (err) {\n emitError({ kind: 'classifier_threw', err });\n return undefined;\n }\n }\n\n function buildRecordedCall(input: RecordInput, nowMs: number): RawRecordedCall | null {\n try {\n const url = input.url;\n let host = '';\n let pathPart = '';\n try {\n const parsed = new URL(url);\n host = parsed.host;\n pathPart = parsed.pathname;\n } catch {\n emitError({ kind: 'url_parse_failed', url });\n }\n const redactedHeaders = applyRedactionToHeaders(input.headers);\n const purposeTag = classifyPurpose(input.method, url, host, pathPart, redactedHeaders);\n const { value: payload, bytes: payloadLength } = applyRedactionToPayload(input.payload, input.content_type);\n return {\n method: input.method,\n endpoint: `${input.method} ${url}`,\n url,\n host,\n path: pathPart,\n content_type: input.content_type,\n attestation_mode: 'raw',\n payload,\n payload_length: payloadLength,\n timestamp: new Date(nowMs).toISOString(),\n ...(input.status_code !== undefined && { status_code: input.status_code }),\n ...(purposeTag !== undefined && { purpose: purposeTag }),\n };\n } catch (err) {\n // Hostile / buggy payload (throwing getter, etc.). Recording MUST\n // never break the adapter call site.\n emitError({ kind: 'payload_build_failed', err });\n return null;\n }\n }\n\n // ────────────────────────────────────────────────────────────\n // Public methods\n // ────────────────────────────────────────────────────────────\n\n function runWithPrincipal<T>(principal: string, fn: () => T | Promise<T>): Promise<T> {\n if (!isPrincipalString(principal)) {\n return Promise.reject(\n new UpstreamRecorderScopeError(\n 'runWithPrincipal: principal MUST be a non-empty string. Pass the same identifier you receive in your comply_test_controller handler.'\n )\n );\n }\n return Promise.resolve(principalStorage.run(principal, fn));\n }\n\n function record(input: RecordInput, explicitPrincipal?: string): void {\n const principal = explicitPrincipal ?? principalStorage.getStore();\n if (!isPrincipalString(principal)) {\n if (strict) {\n throw new UpstreamRecorderScopeError(\n `record() called outside runWithPrincipal scope (method=${input.method}, url=${input.url}). Wrap the call site or pass an explicit principal.`\n );\n }\n emitError({ kind: 'unscoped_record', method: input.method, url: input.url });\n return;\n }\n const nowMs = Date.now();\n evictExpired(nowMs);\n const call = buildRecordedCall(input, nowMs);\n if (!call) return;\n pushEntry({ call, principal, recordedAtMs: nowMs });\n }\n\n function wrapFetch(originalFetch: typeof globalThis.fetch): typeof globalThis.fetch {\n return async function wrappedFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const principal = principalStorage.getStore();\n // No active principal: pass through unchanged in non-strict mode.\n // In strict mode, a fetch outside scope is the same authoring bug\n // as `record()` outside scope and surfaces the same way.\n if (!principal) {\n if (strict) {\n throw new UpstreamRecorderScopeError(\n `wrapFetch invoked outside runWithPrincipal scope (input=${typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url}). Wrap the call site.`\n );\n }\n return originalFetch(input as Parameters<typeof globalThis.fetch>[0], init);\n }\n\n const method = (init?.method ?? (input instanceof Request ? input.method : 'GET')).toUpperCase();\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n\n // Header normalization — fetch's Headers form vs init.headers Record vs Request body.\n const headerObj: Record<string, string> = {};\n if (init?.headers) {\n const h = init.headers;\n if (h instanceof Headers) {\n h.forEach((v, k) => {\n headerObj[k.toLowerCase()] = v;\n });\n } else if (Array.isArray(h)) {\n for (const [k, v] of h) headerObj[k.toLowerCase()] = v;\n } else {\n for (const [k, v] of Object.entries(h as Record<string, string>)) headerObj[k.toLowerCase()] = v;\n }\n } else if (input instanceof Request) {\n input.headers.forEach((v, k) => {\n headerObj[k.toLowerCase()] = v;\n });\n }\n\n const contentType = headerObj['content-type'] ?? '';\n const payload = await readBody(input, init, contentType);\n\n // Fire the actual call; record after we have a status_code. On\n // throw, we still record the attempt with no status_code so the\n // adopter can see the call was made.\n let response: Response | undefined;\n let thrown: unknown;\n try {\n response = await originalFetch(input as Parameters<typeof globalThis.fetch>[0], init);\n } catch (err) {\n thrown = err;\n }\n\n record(\n {\n method,\n url,\n content_type: contentType,\n headers: headerObj,\n payload,\n ...(response && { status_code: response.status }),\n },\n principal\n );\n\n if (thrown) throw thrown;\n return response!;\n };\n }\n\n function query(params: UpstreamRecorderQueryParams): UpstreamRecorderQueryResult {\n if (!isPrincipalString(params.principal)) {\n throw new UpstreamRecorderScopeError(\n 'query({ principal }): principal MUST be a non-empty string. Cross-principal isolation depends on this — passing an empty / non-string value would silently match no entries and conceal misconfiguration.'\n );\n }\n const nowMs = Date.now();\n evictExpired(nowMs);\n const limit = clamp(params.limit, 1, bufferSize, DEFAULT_QUERY_LIMIT);\n const sinceMs = params.sinceTimestamp ? Date.parse(params.sinceTimestamp) : 0;\n const endpointMatcher = params.endpointPattern ? globToRegExp(params.endpointPattern) : undefined;\n\n const matched = buffer.filter(entry => {\n if (entry.principal !== params.principal) return false; // hard isolation\n if (entry.recordedAtMs < sinceMs) return false;\n if (endpointMatcher && !endpointMatcher.test(entry.call.endpoint)) return false;\n return true;\n });\n\n const items: RecordedCall[] = [];\n let total = 0;\n let dropped_count = 0;\n for (const entry of matched) {\n const projected = safeProjectRecordedCall(entry.call, params, emitError);\n if (!projected) {\n if (params.attestationMode === 'digest') dropped_count++;\n continue;\n }\n total++;\n if (items.length < limit) items.push(projected);\n }\n const since_timestamp =\n params.sinceTimestamp ?? matched[0]?.call.timestamp ?? new Date(nowMs - ttlMs).toISOString();\n return {\n items,\n total,\n truncated: total > items.length,\n since_timestamp,\n dropped_count,\n };\n }\n\n function clear(): void {\n buffer.length = 0;\n }\n\n function debug(): UpstreamRecorderDebugInfo {\n const principals = Array.from(new Set(buffer.map(e => e.principal))).sort();\n const last = buffer[buffer.length - 1];\n return {\n enabled: true,\n bufferSize,\n bufferedEntries: buffer.length,\n principals,\n lastRecordedAt: last ? last.call.timestamp : null,\n activePrincipal: principalStorage.getStore() ?? null,\n strict,\n };\n }\n\n return {\n runWithPrincipal,\n wrapFetch,\n record,\n query,\n clear,\n debug,\n enabled: true,\n };\n}\n\nfunction safeProjectRecordedCall(\n call: RawRecordedCall,\n params: UpstreamRecorderQueryParams,\n emitError?: (event: UpstreamRecorderErrorEvent) => void\n): RecordedCall | null {\n try {\n return projectRecordedCall(call, params, emitError);\n } catch (err) {\n emitError?.({ kind: 'digest_canonicalization_failed', method: call.method, endpoint: call.endpoint, err });\n return null;\n }\n}\n\nfunction projectRecordedCall(\n call: RawRecordedCall,\n params: UpstreamRecorderQueryParams,\n emitError?: (event: UpstreamRecorderErrorEvent) => void\n): RecordedCall {\n if (params.attestationMode !== 'digest') return call;\n const payloadBytes = canonicalPayloadBytes(call.payload, call.content_type);\n const payloadDigest = sha256Hex(payloadBytes);\n const identifierDigests = normalizeIdentifierDigests(params.identifierValueDigests);\n const identifierScanPayload =\n identifierDigests.length > 0\n ? parseJsonStringPayloadForScan(call.payload, call.content_type, emitError)\n : undefined;\n const matchedIdentifierDigests =\n identifierDigests.length > 0 && identifierScanPayload !== undefined\n ? collectMatchedStringLeafDigests(identifierScanPayload, new Set(identifierDigests))\n : undefined;\n const identifier_match_proofs =\n identifierDigests.length > 0 && matchedIdentifierDigests\n ? identifierDigests.map(digest => ({\n identifier_value_sha256: digest,\n found: matchedIdentifierDigests.has(digest),\n }))\n : undefined;\n\n return {\n method: call.method,\n endpoint: call.endpoint,\n url: call.url,\n host: call.host,\n path: call.path,\n content_type: call.content_type,\n attestation_mode: 'digest',\n payload_digest_sha256: payloadDigest,\n payload_length: byteLengthOf(payloadBytes),\n timestamp: call.timestamp,\n ...(call.status_code !== undefined && { status_code: call.status_code }),\n ...(call.purpose !== undefined && { purpose: call.purpose }),\n ...(identifier_match_proofs !== undefined && { identifier_match_proofs }),\n };\n}\n\nfunction normalizeIdentifierDigests(input: string[] | undefined): string[] {\n if (!Array.isArray(input)) return [];\n const out: string[] = [];\n for (const value of input) {\n if (typeof value !== 'string' || !/^[a-f0-9]{64}$/.test(value)) continue;\n if (!out.includes(value)) out.push(value);\n if (out.length >= IDENTIFIER_DIGEST_LIMIT) break;\n }\n return out;\n}\n\nfunction collectMatchedStringLeafDigests(root: unknown, wantedDigests: Set<string>): Set<string> {\n const matched = new Set<string>();\n const stack: Array<{ value: unknown; depth: number }> = [{ value: root, depth: 0 }];\n while (stack.length > 0) {\n const { value, depth } = stack.pop()!;\n if (depth > MAX_JSON_DEPTH) continue;\n if (typeof value === 'string') {\n const digest = sha256Hex(value);\n if (wantedDigests.has(digest)) {\n matched.add(digest);\n if (matched.size === wantedDigests.size) break;\n }\n } else if (Array.isArray(value)) {\n for (const item of value) stack.push({ value: item, depth: depth + 1 });\n } else if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value as Record<string, unknown>)) {\n stack.push({ value: item, depth: depth + 1 });\n }\n }\n }\n return matched;\n}\n\nfunction parseJsonStringPayloadForScan(\n payload: unknown,\n contentType: string,\n emitError?: (event: UpstreamRecorderErrorEvent) => void\n): unknown | undefined {\n if (!isJsonContentType(contentType)) return undefined;\n if (typeof payload !== 'string') return payload;\n try {\n return JSON.parse(payload);\n } catch (err) {\n emitError?.({ kind: 'json_payload_parse_failed', content_type: contentType, err });\n return payload;\n }\n}\n\nfunction canonicalPayloadBytes(payload: unknown, contentType: string): string {\n if (isJsonContentType(contentType)) {\n if (typeof payload === 'string') {\n let parsed: unknown;\n try {\n parsed = JSON.parse(payload);\n } catch {\n return payload;\n }\n return canonicalJsonStringify(parsed);\n }\n return canonicalJsonStringify(payload);\n }\n if (typeof payload === 'string') return payload;\n return safeStringify(payload) ?? '';\n}\n\nfunction canonicalJsonStringify(value: unknown): string {\n assertJsonDepth(value);\n return canonicalize(value);\n}\n\nfunction assertJsonDepth(root: unknown): void {\n const stack: Array<{ value: unknown; depth: number }> = [{ value: root, depth: 0 }];\n while (stack.length > 0) {\n const { value, depth } = stack.pop()!;\n if (depth > MAX_JSON_DEPTH) {\n throw new RangeError(`JSON payload exceeds max canonicalization depth ${MAX_JSON_DEPTH}`);\n }\n if (Array.isArray(value)) {\n for (const item of value) stack.push({ value: item, depth: depth + 1 });\n } else if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value as Record<string, unknown>)) {\n stack.push({ value: item, depth: depth + 1 });\n }\n }\n }\n}\n\nfunction sha256Hex(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex');\n}\n\n/**\n * Compute the `RecordedCall.payload_digest_sha256` value. By default this\n * applies the same body snapshot normalization, payload normalization, and\n * canonical secret-key redaction used by the recorder before hashing; pass a\n * custom redaction pattern and `maxPayloadBytes` when your recorder uses matching\n * options:\n *\n * - `createUpstreamRecorder({ redactPattern })` → `computePayloadDigestSha256(..., { redactPattern })`\n * - `createUpstreamRecorder({ maxPayloadBytes })` → `computePayloadDigestSha256(..., { maxPayloadBytes })`\n *\n * Prefer `{ prenormalized: true }` only when the payload has already been\n * normalized/redacted exactly as the recorder would store it; legacy `false`\n * remains accepted as the same prenormalized sentinel. The prenormalized path\n * skips redaction, so the helper rejects secret-shaped keys whose values are\n * not already the literal `\"[redacted]\"` marker or an explicit null\n * placeholder. For form-encoded bodies, duplicate secret-shaped keys are also\n * rejected because last-wins maps cannot prove that every original value was\n * inspected before hashing.\n *\n * JSON content is serialized with RFC 8785 JCS before hashing. When\n * `contentType` is JSON-shaped and `payload` is a string, the helper parses\n * it first so semantically equivalent JSON strings and objects hash to the\n * same value. On the normalizing path, an unparseable JSON-shaped string is\n * treated as the emitted body bytes and hashed verbatim after best-effort\n * redaction. On the prenormalized path, malformed JSON strings throw because\n * their secret-key structure cannot be proven safe. Unsupported JSON values\n * and payloads deeper than the recorder's depth cap throw rather than\n * producing a non-canonical digest.\n *\n * The returned digest is lowercase hex, matching the 3.1\n * `query_upstream_traffic` wire contract.\n */\nexport function computePayloadDigestSha256(\n payload: unknown,\n contentType?: string,\n options?: PayloadDigestOptions\n): string;\n/**\n * @deprecated Pass `{ redactPattern: /.../ }` instead of the bare `RegExp`\n * form. The legacy form remains accepted for this major.\n */\nexport function computePayloadDigestSha256(payload: unknown, contentType: string | undefined, options: RegExp): string;\n/**\n * @deprecated Pass `{ prenormalized: true }` instead of `false`. The legacy\n * form remains accepted for this major.\n */\nexport function computePayloadDigestSha256(payload: unknown, contentType: string | undefined, options: false): string;\nexport function computePayloadDigestSha256(\n payload: unknown,\n contentType = 'application/json',\n options: RegExp | false | PayloadDigestOptions = SECRET_KEY_PATTERN\n): string {\n const redactPattern =\n options instanceof RegExp || options === false\n ? options === false\n ? false\n : normalizeSecretKeyPattern(options)\n : options?.prenormalized\n ? false\n : options?.redactPattern === false\n ? false\n : normalizeSecretKeyPattern(options?.redactPattern ?? SECRET_KEY_PATTERN);\n const prenormalizedSafetyPattern =\n typeof options === 'object' &&\n options !== null &&\n !(options instanceof RegExp) &&\n options.redactPattern instanceof RegExp\n ? normalizeSecretKeyPattern(options.redactPattern)\n : SECRET_KEY_PATTERN;\n const maxPayloadBytes =\n typeof options === 'object' && options !== null && !(options instanceof RegExp)\n ? clamp(options.maxPayloadBytes, 0, 16 * 1024 * 1024, DEFAULT_MAX_PAYLOAD_BYTES)\n : DEFAULT_MAX_PAYLOAD_BYTES;\n try {\n if (\n typeof options === 'object' &&\n options !== null &&\n !(options instanceof RegExp) &&\n options.redactPattern === false &&\n options.prenormalized !== true\n ) {\n throw new PayloadDigestError('PayloadDigestOptions.redactPattern=false requires prenormalized=true');\n }\n const payloadForDigest =\n redactPattern === false\n ? assertPrenormalizedPayloadSafe(payload, contentType, prenormalizedSafetyPattern)\n : normalizeRecordedPayload(\n normalizeFetchPayloadInput(payload, contentType),\n contentType,\n redactPattern,\n maxPayloadBytes\n );\n return sha256Hex(canonicalPayloadBytes(payloadForDigest, contentType));\n } catch (err) {\n if (err instanceof PayloadDigestError) throw err;\n throw new PayloadDigestError(err instanceof Error ? err.message : 'Payload digest canonicalization failed', err);\n }\n}\n\nexport type PayloadDigestOptions = {\n /**\n * Custom redaction pattern. Passing `false` is accepted only with\n * `prenormalized: true`; otherwise it would silently bypass redaction.\n */\n redactPattern?: RegExp | false;\n /**\n * Match `createUpstreamRecorder({ maxPayloadBytes })` when computing\n * digests outside the recorder.\n */\n maxPayloadBytes?: number;\n /**\n * Set only for payloads that have already been normalized and redacted.\n * The helper skips redaction but still scans secret-shaped keys, rejects\n * malformed JSON strings, and rejects duplicate secret-shaped form keys.\n */\n prenormalized?: boolean;\n};\n\nfunction assertPrenormalizedPayloadSafe(payload: unknown, contentType: string, pattern: RegExp): unknown {\n let scanTarget = payload;\n if (typeof payload === 'string' && isJsonContentType(contentType)) {\n try {\n scanTarget = JSON.parse(payload);\n } catch {\n throw new PayloadDigestError(\n 'Prenormalized JSON payload is malformed; hash the recorder-normalized payload or omit prenormalized.'\n );\n }\n } else if (typeof payload === 'string' && isFormUrlEncoded(contentType)) {\n assertPrenormalizedFormUrlEncodedSafe(payload, pattern);\n return payload;\n }\n const unsafePath = findUnredactedSecretPath(scanTarget, pattern);\n if (unsafePath) {\n throw new PayloadDigestError(\n `Prenormalized payload contains unredacted secret-shaped key \"${unsafePath}\"; hash the recorder-normalized payload or omit prenormalized.`\n );\n }\n return payload;\n}\n\nfunction assertPrenormalizedFormUrlEncodedSafe(body: string, pattern: RegExp): void {\n const seenSecretKeys = new Set<string>();\n const params = Array.from(new URLSearchParams(body));\n for (const [key] of params) {\n if (!secretKeyPatternMatches(pattern, key)) continue;\n const normalizedKey = key.toLowerCase();\n if (seenSecretKeys.has(normalizedKey)) {\n throw new PayloadDigestError(\n `Prenormalized form payload contains duplicate secret-shaped key \"${key}\"; hash the recorder-normalized payload or omit prenormalized.`\n );\n }\n seenSecretKeys.add(normalizedKey);\n }\n for (const [key, value] of params) {\n if (!secretKeyPatternMatches(pattern, key)) continue;\n if (!isSafelyRedactedSecretValue(value)) {\n throw new PayloadDigestError(\n `Prenormalized payload contains unredacted secret-shaped key \"${key}\"; hash the recorder-normalized payload or omit prenormalized.`\n );\n }\n }\n}\n\nfunction findUnredactedSecretPath(value: unknown, pattern: RegExp): string | null {\n const stack: Array<{ value: unknown; path: string }> = [{ value, path: '' }];\n const seen = new WeakSet<object>();\n while (stack.length > 0) {\n const { value: current, path } = stack.pop()!;\n if (!current || typeof current !== 'object') continue;\n if (seen.has(current)) continue;\n seen.add(current);\n\n if (Array.isArray(current)) {\n for (let i = current.length - 1; i >= 0; i--) {\n stack.push({ value: current[i], path: `${path}[${i}]` });\n }\n continue;\n }\n\n const entries = Object.entries(current as Record<string, unknown>);\n for (let i = entries.length - 1; i >= 0; i--) {\n const [key, childValue] = entries[i]!;\n const childPath = path ? `${path}.${key}` : key;\n if (secretKeyPatternMatches(pattern, key) && !isSafelyRedactedSecretValue(childValue)) return childPath;\n stack.push({ value: childValue, path: childPath });\n }\n }\n return null;\n}\n\nfunction isSafelyRedactedSecretValue(value: unknown): boolean {\n return value === '[redacted]' || value === null;\n}\n\nfunction normalizeFetchPayloadInput(payload: unknown, contentType: string): unknown {\n if (payload === undefined) return undefined;\n if (typeof payload === 'string') {\n if (!isJsonContentType(contentType)) return payload;\n try {\n return JSON.parse(payload);\n } catch {\n return payload;\n }\n }\n if (payload instanceof URLSearchParams) return payload.toString();\n if (typeof FormData !== 'undefined' && payload instanceof FormData) {\n const out: Record<string, unknown> = {};\n payload.forEach((v, k) => {\n out[k] = typeof v === 'string' ? v : '[file]';\n });\n return out;\n }\n return payload;\n}\n\nfunction normalizeRecordedPayload(\n payload: unknown,\n contentType: string,\n redactPattern: RegExp,\n maxPayloadBytes: number\n): unknown {\n if (payload === undefined) {\n // Raw-mode `RecordedCall.payload` is required by the 3.1 controller\n // response schema. GET/HEAD-style calls with no body therefore emit an\n // empty object instead of omitting the field.\n return {};\n }\n if (payload === null) return payload;\n\n // Binary-shaped bodies — replace with a marker. Adopters that need\n // the raw bytes recorded for diagnostics can stringify before\n // calling `record()`.\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(payload)) {\n return `[binary ${payload.length} bytes]`;\n }\n if (typeof Blob !== 'undefined' && payload instanceof Blob) {\n return `[binary ${payload.size} bytes]`;\n }\n if (payload instanceof ArrayBuffer) {\n return `[binary ${payload.byteLength} bytes]`;\n }\n if (ArrayBuffer.isView(payload)) {\n return `[binary ${payload.byteLength} bytes]`;\n }\n\n if (typeof payload === 'string') {\n if (isJsonContentType(contentType)) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(payload);\n } catch {\n // Malformed JSON strings remain raw strings so diagnostics keep the\n // original body shape instead of pretending the payload was absent.\n return cap(redactJsonLikeSecretValues(payload, redactPattern), maxPayloadBytes);\n }\n const redacted = redactSecrets(parsed, redactPattern);\n assertJsonDepth(redacted);\n const json = safeStringify(redacted);\n if (json && maxPayloadBytes > 0 && byteLengthOf(json) > maxPayloadBytes) {\n return `[truncated ${byteLengthOf(json)} bytes]`;\n }\n return redacted;\n }\n // Form-urlencoded redaction: parse, redact, re-stringify so\n // `access_token=...` bodies don't slip through unredacted.\n if (isFormUrlEncoded(contentType)) {\n return cap(redactFormUrlEncoded(payload, redactPattern), maxPayloadBytes);\n }\n return cap(payload, maxPayloadBytes);\n }\n\n const redacted = redactSecrets(payload, redactPattern);\n assertJsonDepth(redacted);\n const json = safeStringify(redacted);\n if (json && maxPayloadBytes > 0 && byteLengthOf(json) > maxPayloadBytes) {\n return `[truncated ${byteLengthOf(json)} bytes]`;\n }\n return redacted;\n}\n\n/**\n * Project a `recorder.query()` result onto the spec wire shape returned\n * by `comply_test_controller`'s `query_upstream_traffic` scenario\n * (`UpstreamTrafficSuccess` in `comply-test-controller-response.json`,\n * spec PR adcontextprotocol/adcp#3816). Eliminates the\n * `items → recorded_calls` / `total → total_count` field-rename footgun\n * — adopters return this directly:\n *\n * ```ts\n * scenarios: {\n * query_upstream_traffic: ({ params }, ctx) =>\n * toQueryUpstreamTrafficResponse(\n * recorder.query({\n * principal: resolvePrincipal(ctx),\n * sinceTimestamp: params?.since_timestamp,\n * endpointPattern: params?.endpoint_pattern,\n * limit: params?.limit,\n * })\n * ),\n * }\n * ```\n */\nexport function toQueryUpstreamTrafficResponse(result: UpstreamRecorderQueryResult): QueryUpstreamTrafficResponse {\n return {\n success: true,\n recorded_calls: result.items,\n total_count: result.total,\n truncated: result.truncated,\n since_timestamp: result.since_timestamp,\n };\n}\n\n/**\n * No-op recorder returned when `enabled: false`. Every method is a\n * pass-through / empty-result. Production builds drop in this object\n * with zero per-call overhead beyond the initial factory call.\n */\nfunction makeNoopRecorder(): UpstreamRecorder {\n return {\n runWithPrincipal: <T>(_p: string, fn: () => T | Promise<T>): Promise<T> => Promise.resolve(fn()),\n wrapFetch: (originalFetch: typeof globalThis.fetch): typeof globalThis.fetch => originalFetch,\n record: () => undefined,\n query: (params: UpstreamRecorderQueryParams): UpstreamRecorderQueryResult => ({\n items: [],\n total: 0,\n truncated: false,\n since_timestamp: params.sinceTimestamp ?? new Date(0).toISOString(),\n dropped_count: 0,\n }),\n clear: () => undefined,\n debug: (): UpstreamRecorderDebugInfo => ({\n enabled: false,\n bufferSize: 0,\n bufferedEntries: 0,\n principals: [],\n lastRecordedAt: null,\n activePrincipal: null,\n strict: false,\n }),\n enabled: false,\n };\n}\n\nlet warnedAboutProduction = false;\nfunction warnIfEnabledInProduction(): void {\n if (warnedAboutProduction) return;\n if (typeof process === 'undefined' || !process.env) return;\n if (process.env.NODE_ENV !== 'production') return;\n if (process.env.ADCP_RECORDER_PRODUCTION_ACK === '1') return;\n warnedAboutProduction = true;\n // eslint-disable-next-line no-console\n console.warn(\n '[adcp/upstream-recorder] enabled: true in NODE_ENV=production. ' +\n 'The recorder buffers outbound HTTP calls in-memory — sandbox-only by design. ' +\n 'Set enabled: false (recommended) or ADCP_RECORDER_PRODUCTION_ACK=1 to silence.'\n );\n}\n\n/**\n * Read the request body off a fetch call site without consuming the\n * input (when the input is a `Request`, we'd lock its stream by reading\n * directly). Returns `undefined` for bodies the recorder can't snapshot\n * cheaply.\n */\nasync function readBody(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n contentType: string\n): Promise<unknown> {\n let raw: unknown;\n\n if (init?.body !== undefined && init.body !== null) {\n raw = init.body;\n } else if (input instanceof Request) {\n try {\n const clone = input.clone();\n if (contentTypeBase(contentType) === 'multipart/form-data') {\n raw = await clone.formData();\n } else {\n raw = await clone.text();\n }\n } catch {\n raw = undefined;\n }\n } else {\n raw = undefined;\n }\n\n if (raw === undefined) return undefined;\n if (typeof raw === 'string') {\n if (!isJsonContentType(contentType)) return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n // FormData / URLSearchParams stringify cleanly enough for diagnostic value.\n if (raw instanceof URLSearchParams) return raw.toString();\n if (typeof FormData !== 'undefined' && raw instanceof FormData) {\n const out: Record<string, unknown> = {};\n raw.forEach((v, k) => {\n out[k] = typeof v === 'string' ? v : '[file]';\n });\n return out;\n }\n // Object / array literal already, or a Buffer / Blob / TypedArray /\n // ArrayBuffer the adopter passed in directly. Pass through — the\n // redaction step below detects binary types and substitutes markers\n // before they sit in the buffer.\n if (typeof raw === 'object') return raw;\n return String(raw);\n}\n\nfunction isJsonContentType(contentType: string | undefined): boolean {\n const base = contentTypeBase(contentType);\n return base === 'application/json' || /\\+json$/.test(base);\n}\n\nfunction isFormUrlEncoded(contentType: string | undefined): boolean {\n return contentTypeBase(contentType) === 'application/x-www-form-urlencoded';\n}\n\nfunction contentTypeBase(contentType: string | undefined): string {\n if (!contentType) return '';\n return contentType.split(';')[0]?.trim().toLowerCase() ?? '';\n}\n\nfunction redactFormUrlEncoded(body: string, pattern: RegExp): string {\n try {\n const params = new URLSearchParams(body);\n const out = new URLSearchParams();\n params.forEach((v, k) => {\n out.append(k, secretKeyPatternMatches(pattern, k.toLowerCase()) ? '[redacted]' : v);\n });\n return out.toString();\n } catch {\n return body;\n }\n}\n\nfunction redactJsonLikeSecretValues(body: string, pattern: RegExp): string {\n // Best-effort diagnostic scrub for malformed JSON strings. The output is\n // not guaranteed to be valid JSON; the invariant is that secret-shaped keys\n // with scalar/string values are not stored verbatim. Because malformed JSON\n // has no trustworthy parse tree, object/array-valued secret entries are\n // replaced wholesale rather than traversed.\n let out = '';\n let i = 0;\n while (i < body.length) {\n if (body[i] !== '\"') {\n out += body[i++];\n continue;\n }\n\n const keyToken = readJsonStringToken(body, i);\n if (!keyToken) {\n out += body.slice(i);\n break;\n }\n\n let colon = keyToken.end;\n while (colon < body.length && isJsonWhitespace(body[colon])) colon++;\n if (body[colon] !== ':') {\n out += body.slice(i, keyToken.end);\n i = keyToken.end;\n continue;\n }\n\n let key: unknown;\n try {\n key = JSON.parse(keyToken.literal);\n } catch {\n out += body.slice(i, keyToken.end);\n i = keyToken.end;\n continue;\n }\n\n const valueStart = colon + 1;\n let value = valueStart;\n while (value < body.length && isJsonWhitespace(body[value])) value++;\n out += body.slice(i, value);\n if (typeof key !== 'string' || !secretKeyPatternMatches(pattern, key)) {\n i = value;\n continue;\n }\n\n out += '\"[redacted]\"';\n i = skipJsonLikeValue(body, value);\n }\n return out;\n}\n\nfunction readJsonStringToken(input: string, start: number): { literal: string; end: number } | null {\n for (let i = start + 1; i < input.length; i++) {\n const ch = input[i];\n if (ch === '\"') return { literal: input.slice(start, i + 1), end: i + 1 };\n if (ch === '\\\\') i++;\n }\n return null;\n}\n\nfunction skipJsonLikeValue(input: string, start: number): number {\n if (input[start] === '\"') {\n const token = readJsonStringToken(input, start);\n return token?.end ?? input.length;\n }\n if (input[start] === '[' || input[start] === '{') {\n const opener = input[start];\n const closer = opener === '[' ? ']' : '}';\n let depth = 0;\n for (let i = start; i < input.length; i++) {\n const ch = input[i];\n if (ch === '\"') {\n const token = readJsonStringToken(input, i);\n if (!token) return input.length;\n i = token.end - 1;\n continue;\n }\n if (ch === opener) depth++;\n if (ch === closer) {\n depth--;\n if (depth === 0) return i + 1;\n }\n }\n return input.length;\n }\n let i = start;\n while (i < input.length && input[i] !== ',' && input[i] !== '}' && input[i] !== ']' && !isJsonWhitespace(input[i])) {\n i++;\n }\n return i;\n}\n\nfunction isJsonWhitespace(ch: string | undefined): boolean {\n return ch === ' ' || ch === '\\n' || ch === '\\r' || ch === '\\t';\n}\n\nfunction safeStringify(value: unknown): string | undefined {\n try {\n return JSON.stringify(value);\n } catch {\n return undefined;\n }\n}\n\nfunction byteLengthOf(s: string): number {\n if (typeof Buffer !== 'undefined') return Buffer.byteLength(s, 'utf8');\n // Fallback — rough UTF-16 count.\n return s.length;\n}\n\nfunction payloadWireLength(value: unknown): number {\n if (typeof value === 'string') return byteLengthOf(value);\n const json = safeStringify(value);\n return json ? byteLengthOf(json) : 0;\n}\n\nfunction cap(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return s;\n if (byteLengthOf(s) <= maxBytes) return s;\n return `[truncated ${byteLengthOf(s)} bytes]`;\n}\n"],"mappings":"AAKA,SAAS,yBAAyB;AAClC,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,OAWK;AAEP,MAAM,sBAAsB;AAC5B,MAAM,iBAAiB,KAAK,KAAK;AACjC,MAAM,sBAAsB;AAC5B,MAAM,4BAA4B;AAClC,MAAM,kBAAkB;AACxB,MAAM,aAAa,KAAK,KAAK,KAAK;AAClC,MAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,MAAM,2BAA2B,MAAM;AAAA,EAC5C;AAAA,EAEA,YAAY,SAAiB,OAAiB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;AAeA,SAAS,MAAM,GAAuB,IAAY,IAAY,UAA0B;AACtF,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAKzD,MAAI,IAAI,MAAM,IAAI,GAAI,QAAO;AAC7B,SAAO;AACT;AAEA,SAAS,kBAAkB,GAAyB;AAClD,SAAO,OAAO,MAAM,YAAY,EAAE,SAAS;AAC7C;AAOO,SAAS,uBAAuB,UAAmC,CAAC,GAAqB;AAC9F,QAAM,UAAU,QAAQ,WAAW;AAGnC,MAAI,CAAC,QAAS,QAAO,iBAAiB;AAOtC,4BAA0B;AAE1B,QAAM,gBAAgB,0BAA0B,QAAQ,iBAAiB,kBAAkB;AAC3F,QAAM,aAAa,MAAM,QAAQ,YAAY,GAAG,iBAAiB,mBAAmB;AACpF,QAAM,QAAQ,MAAM,QAAQ,OAAO,GAAG,YAAY,cAAc;AAChE,QAAM,kBAAkB,MAAM,QAAQ,iBAAiB,GAAG,KAAK,OAAO,MAAM,yBAAyB;AACrG,QAAM,UAAU,QAAQ;AACxB,QAAM,SAAS,QAAQ,WAAW;AAClC,QAAM,UAAU,QAAQ;AAIxB,QAAM,SAA0B,CAAC;AAIjC,QAAM,mBAAmB,IAAI,kBAA0B;AAMvD,WAAS,UAAU,OAAyC;AAC1D,QAAI,CAAC,QAAS;AACd,QAAI;AACF,cAAQ,KAAK;AAAA,IACf,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,WAAS,aAAa,OAAqB;AACzC,QAAI,OAAO,WAAW,EAAG;AACzB,UAAM,SAAS,QAAQ;AACvB,QAAI,MAAM;AACV,WAAO,MAAM,OAAO,UAAU,OAAO,GAAG,EAAG,eAAe,OAAQ;AAClE,QAAI,MAAM,EAAG,QAAO,OAAO,GAAG,GAAG;AAAA,EACnC;AAEA,WAAS,UAAU,OAA4B;AAC7C,WAAO,KAAK,KAAK;AACjB,WAAO,OAAO,SAAS,WAAY,QAAO,MAAM;AAAA,EAClD;AAEA,WAAS,wBAAwB,SAAqE;AACpG,QAAI,CAAC,QAAS,QAAO,CAAC;AACtB,UAAM,MAA8B,CAAC;AACrC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC5C,YAAM,QAAQ,EAAE,YAAY;AAC5B,UAAI,KAAK,IAAI,wBAAwB,eAAe,KAAK,IAAI,eAAe;AAAA,IAC9E;AACA,WAAO;AAAA,EACT;AAWA,WAAS,wBAAwB,SAAkB,aAAwD;AACzG,UAAM,QAAQ,yBAAyB,SAAS,aAAa,eAAe,eAAe;AAC3F,WAAO,EAAE,OAAO,OAAO,kBAAkB,KAAK,EAAE;AAAA,EAClD;AAEA,WAAS,gBACP,QACA,KACA,MACA,MACA,SACoB;AACpB,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI;AACF,YAAM,QAAQ,QAAQ,EAAE,QAAQ,KAAK,MAAM,MAAM,QAAQ,CAAC;AAC1D,UAAI,UAAU,OAAW,QAAO;AAChC,UAAI,OAAO,UAAU,YAAY,eAAe,IAAI,KAAK,EAAG,QAAO;AACnE,gBAAU,EAAE,MAAM,8BAA8B,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK,EAAE,CAAC;AAC5G,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,gBAAU,EAAE,MAAM,oBAAoB,IAAI,CAAC;AAC3C,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,kBAAkB,OAAoB,OAAuC;AACpF,QAAI;AACF,YAAM,MAAM,MAAM;AAClB,UAAI,OAAO;AACX,UAAI,WAAW;AACf,UAAI;AACF,cAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,eAAO,OAAO;AACd,mBAAW,OAAO;AAAA,MACpB,QAAQ;AACN,kBAAU,EAAE,MAAM,oBAAoB,IAAI,CAAC;AAAA,MAC7C;AACA,YAAM,kBAAkB,wBAAwB,MAAM,OAAO;AAC7D,YAAM,aAAa,gBAAgB,MAAM,QAAQ,KAAK,MAAM,UAAU,eAAe;AACrF,YAAM,EAAE,OAAO,SAAS,OAAO,cAAc,IAAI,wBAAwB,MAAM,SAAS,MAAM,YAAY;AAC1G,aAAO;AAAA,QACL,QAAQ,MAAM;AAAA,QACd,UAAU,GAAG,MAAM,MAAM,IAAI,GAAG;AAAA,QAChC;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,cAAc,MAAM;AAAA,QACpB,kBAAkB;AAAA,QAClB;AAAA,QACA,gBAAgB;AAAA,QAChB,WAAW,IAAI,KAAK,KAAK,EAAE,YAAY;AAAA,QACvC,GAAI,MAAM,gBAAgB,UAAa,EAAE,aAAa,MAAM,YAAY;AAAA,QACxE,GAAI,eAAe,UAAa,EAAE,SAAS,WAAW;AAAA,MACxD;AAAA,IACF,SAAS,KAAK;AAGZ,gBAAU,EAAE,MAAM,wBAAwB,IAAI,CAAC;AAC/C,aAAO;AAAA,IACT;AAAA,EACF;AAMA,WAAS,iBAAoB,WAAmB,IAAsC;AACpF,QAAI,CAAC,kBAAkB,SAAS,GAAG;AACjC,aAAO,QAAQ;AAAA,QACb,IAAI;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,QAAQ,QAAQ,iBAAiB,IAAI,WAAW,EAAE,CAAC;AAAA,EAC5D;AAEA,WAAS,OAAO,OAAoB,mBAAkC;AACpE,UAAM,YAAY,qBAAqB,iBAAiB,SAAS;AACjE,QAAI,CAAC,kBAAkB,SAAS,GAAG;AACjC,UAAI,QAAQ;AACV,cAAM,IAAI;AAAA,UACR,0DAA0D,MAAM,MAAM,SAAS,MAAM,GAAG;AAAA,QAC1F;AAAA,MACF;AACA,gBAAU,EAAE,MAAM,mBAAmB,QAAQ,MAAM,QAAQ,KAAK,MAAM,IAAI,CAAC;AAC3E;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,IAAI;AACvB,iBAAa,KAAK;AAClB,UAAM,OAAO,kBAAkB,OAAO,KAAK;AAC3C,QAAI,CAAC,KAAM;AACX,cAAU,EAAE,MAAM,WAAW,cAAc,MAAM,CAAC;AAAA,EACpD;AAEA,WAAS,UAAU,eAAiE;AAClF,WAAO,eAAe,aAAa,OAA0B,MAAuC;AAClG,YAAM,YAAY,iBAAiB,SAAS;AAI5C,UAAI,CAAC,WAAW;AACd,YAAI,QAAQ;AACV,gBAAM,IAAI;AAAA,YACR,2DAA2D,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,SAAS,IAAI,MAAM,GAAG;AAAA,UACpJ;AAAA,QACF;AACA,eAAO,cAAc,OAAiD,IAAI;AAAA,MAC5E;AAEA,YAAM,UAAU,MAAM,WAAW,iBAAiB,UAAU,MAAM,SAAS,QAAQ,YAAY;AAC/F,YAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,SAAS,IAAI,MAAM;AAGhG,YAAM,YAAoC,CAAC;AAC3C,UAAI,MAAM,SAAS;AACjB,cAAM,IAAI,KAAK;AACf,YAAI,aAAa,SAAS;AACxB,YAAE,QAAQ,CAAC,GAAG,MAAM;AAClB,sBAAU,EAAE,YAAY,CAAC,IAAI;AAAA,UAC/B,CAAC;AAAA,QACH,WAAW,MAAM,QAAQ,CAAC,GAAG;AAC3B,qBAAW,CAAC,GAAG,CAAC,KAAK,EAAG,WAAU,EAAE,YAAY,CAAC,IAAI;AAAA,QACvD,OAAO;AACL,qBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAA2B,EAAG,WAAU,EAAE,YAAY,CAAC,IAAI;AAAA,QACjG;AAAA,MACF,WAAW,iBAAiB,SAAS;AACnC,cAAM,QAAQ,QAAQ,CAAC,GAAG,MAAM;AAC9B,oBAAU,EAAE,YAAY,CAAC,IAAI;AAAA,QAC/B,CAAC;AAAA,MACH;AAEA,YAAM,cAAc,UAAU,cAAc,KAAK;AACjD,YAAM,UAAU,MAAM,SAAS,OAAO,MAAM,WAAW;AAKvD,UAAI;AACJ,UAAI;AACJ,UAAI;AACF,mBAAW,MAAM,cAAc,OAAiD,IAAI;AAAA,MACtF,SAAS,KAAK;AACZ,iBAAS;AAAA,MACX;AAEA;AAAA,QACE;AAAA,UACE;AAAA,UACA;AAAA,UACA,cAAc;AAAA,UACd,SAAS;AAAA,UACT;AAAA,UACA,GAAI,YAAY,EAAE,aAAa,SAAS,OAAO;AAAA,QACjD;AAAA,QACA;AAAA,MACF;AAEA,UAAI,OAAQ,OAAM;AAClB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,MAAM,QAAkE;AAC/E,QAAI,CAAC,kBAAkB,OAAO,SAAS,GAAG;AACxC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,IAAI;AACvB,iBAAa,KAAK;AAClB,UAAM,QAAQ,MAAM,OAAO,OAAO,GAAG,YAAY,mBAAmB;AACpE,UAAM,UAAU,OAAO,iBAAiB,KAAK,MAAM,OAAO,cAAc,IAAI;AAC5E,UAAM,kBAAkB,OAAO,kBAAkB,aAAa,OAAO,eAAe,IAAI;AAExF,UAAM,UAAU,OAAO,OAAO,WAAS;AACrC,UAAI,MAAM,cAAc,OAAO,UAAW,QAAO;AACjD,UAAI,MAAM,eAAe,QAAS,QAAO;AACzC,UAAI,mBAAmB,CAAC,gBAAgB,KAAK,MAAM,KAAK,QAAQ,EAAG,QAAO;AAC1E,aAAO;AAAA,IACT,CAAC;AAED,UAAM,QAAwB,CAAC;AAC/B,QAAI,QAAQ;AACZ,QAAI,gBAAgB;AACpB,eAAW,SAAS,SAAS;AAC3B,YAAM,YAAY,wBAAwB,MAAM,MAAM,QAAQ,SAAS;AACvE,UAAI,CAAC,WAAW;AACd,YAAI,OAAO,oBAAoB,SAAU;AACzC;AAAA,MACF;AACA;AACA,UAAI,MAAM,SAAS,MAAO,OAAM,KAAK,SAAS;AAAA,IAChD;AACA,UAAM,kBACJ,OAAO,kBAAkB,QAAQ,CAAC,GAAG,KAAK,aAAa,IAAI,KAAK,QAAQ,KAAK,EAAE,YAAY;AAC7F,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,WAAW,QAAQ,MAAM;AAAA,MACzB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,WAAS,QAAc;AACrB,WAAO,SAAS;AAAA,EAClB;AAEA,WAAS,QAAmC;AAC1C,UAAM,aAAa,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,OAAK,EAAE,SAAS,CAAC,CAAC,EAAE,KAAK;AAC1E,UAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,iBAAiB,OAAO;AAAA,MACxB;AAAA,MACA,gBAAgB,OAAO,KAAK,KAAK,YAAY;AAAA,MAC7C,iBAAiB,iBAAiB,SAAS,KAAK;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEA,SAAS,wBACP,MACA,QACA,WACqB;AACrB,MAAI;AACF,WAAO,oBAAoB,MAAM,QAAQ,SAAS;AAAA,EACpD,SAAS,KAAK;AACZ,gBAAY,EAAE,MAAM,kCAAkC,QAAQ,KAAK,QAAQ,UAAU,KAAK,UAAU,IAAI,CAAC;AACzG,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBACP,MACA,QACA,WACc;AACd,MAAI,OAAO,oBAAoB,SAAU,QAAO;AAChD,QAAM,eAAe,sBAAsB,KAAK,SAAS,KAAK,YAAY;AAC1E,QAAM,gBAAgB,UAAU,YAAY;AAC5C,QAAM,oBAAoB,2BAA2B,OAAO,sBAAsB;AAClF,QAAM,wBACJ,kBAAkB,SAAS,IACvB,8BAA8B,KAAK,SAAS,KAAK,cAAc,SAAS,IACxE;AACN,QAAM,2BACJ,kBAAkB,SAAS,KAAK,0BAA0B,SACtD,gCAAgC,uBAAuB,IAAI,IAAI,iBAAiB,CAAC,IACjF;AACN,QAAM,0BACJ,kBAAkB,SAAS,KAAK,2BAC5B,kBAAkB,IAAI,aAAW;AAAA,IAC/B,yBAAyB;AAAA,IACzB,OAAO,yBAAyB,IAAI,MAAM;AAAA,EAC5C,EAAE,IACF;AAEN,SAAO;AAAA,IACL,QAAQ,KAAK;AAAA,IACb,UAAU,KAAK;AAAA,IACf,KAAK,KAAK;AAAA,IACV,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,cAAc,KAAK;AAAA,IACnB,kBAAkB;AAAA,IAClB,uBAAuB;AAAA,IACvB,gBAAgB,aAAa,YAAY;AAAA,IACzC,WAAW,KAAK;AAAA,IAChB,GAAI,KAAK,gBAAgB,UAAa,EAAE,aAAa,KAAK,YAAY;AAAA,IACtE,GAAI,KAAK,YAAY,UAAa,EAAE,SAAS,KAAK,QAAQ;AAAA,IAC1D,GAAI,4BAA4B,UAAa,EAAE,wBAAwB;AAAA,EACzE;AACF;AAEA,SAAS,2BAA2B,OAAuC;AACzE,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,QAAM,MAAgB,CAAC;AACvB,aAAW,SAAS,OAAO;AACzB,QAAI,OAAO,UAAU,YAAY,CAAC,iBAAiB,KAAK,KAAK,EAAG;AAChE,QAAI,CAAC,IAAI,SAAS,KAAK,EAAG,KAAI,KAAK,KAAK;AACxC,QAAI,IAAI,UAAU,wBAAyB;AAAA,EAC7C;AACA,SAAO;AACT;AAEA,SAAS,gCAAgC,MAAe,eAAyC;AAC/F,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,QAAkD,CAAC,EAAE,OAAO,MAAM,OAAO,EAAE,CAAC;AAClF,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,EAAE,OAAO,MAAM,IAAI,MAAM,IAAI;AACnC,QAAI,QAAQ,eAAgB;AAC5B,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,SAAS,UAAU,KAAK;AAC9B,UAAI,cAAc,IAAI,MAAM,GAAG;AAC7B,gBAAQ,IAAI,MAAM;AAClB,YAAI,QAAQ,SAAS,cAAc,KAAM;AAAA,MAC3C;AAAA,IACF,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,iBAAW,QAAQ,MAAO,OAAM,KAAK,EAAE,OAAO,MAAM,OAAO,QAAQ,EAAE,CAAC;AAAA,IACxE,WAAW,UAAU,QAAQ,OAAO,UAAU,UAAU;AACtD,iBAAW,QAAQ,OAAO,OAAO,KAAgC,GAAG;AAClE,cAAM,KAAK,EAAE,OAAO,MAAM,OAAO,QAAQ,EAAE,CAAC;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,8BACP,SACA,aACA,WACqB;AACrB,MAAI,CAAC,kBAAkB,WAAW,EAAG,QAAO;AAC5C,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,SAAS,KAAK;AACZ,gBAAY,EAAE,MAAM,6BAA6B,cAAc,aAAa,IAAI,CAAC;AACjF,WAAO;AAAA,EACT;AACF;AAEA,SAAS,sBAAsB,SAAkB,aAA6B;AAC5E,MAAI,kBAAkB,WAAW,GAAG;AAClC,QAAI,OAAO,YAAY,UAAU;AAC/B,UAAI;AACJ,UAAI;AACF,iBAAS,KAAK,MAAM,OAAO;AAAA,MAC7B,QAAQ;AACN,eAAO;AAAA,MACT;AACA,aAAO,uBAAuB,MAAM;AAAA,IACtC;AACA,WAAO,uBAAuB,OAAO;AAAA,EACvC;AACA,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,SAAO,cAAc,OAAO,KAAK;AACnC;AAEA,SAAS,uBAAuB,OAAwB;AACtD,kBAAgB,KAAK;AACrB,SAAO,aAAa,KAAK;AAC3B;AAEA,SAAS,gBAAgB,MAAqB;AAC5C,QAAM,QAAkD,CAAC,EAAE,OAAO,MAAM,OAAO,EAAE,CAAC;AAClF,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,EAAE,OAAO,MAAM,IAAI,MAAM,IAAI;AACnC,QAAI,QAAQ,gBAAgB;AAC1B,YAAM,IAAI,WAAW,mDAAmD,cAAc,EAAE;AAAA,IAC1F;AACA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,QAAQ,MAAO,OAAM,KAAK,EAAE,OAAO,MAAM,OAAO,QAAQ,EAAE,CAAC;AAAA,IACxE,WAAW,UAAU,QAAQ,OAAO,UAAU,UAAU;AACtD,iBAAW,QAAQ,OAAO,OAAO,KAAgC,GAAG;AAClE,cAAM,KAAK,EAAE,OAAO,MAAM,OAAO,QAAQ,EAAE,CAAC;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,UAAU,OAAuB;AACxC,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,MAAM,EAAE,OAAO,KAAK;AAChE;AAiDO,SAAS,2BACd,SACA,cAAc,oBACd,UAAiD,oBACzC;AACR,QAAM,gBACJ,mBAAmB,UAAU,YAAY,QACrC,YAAY,QACV,QACA,0BAA0B,OAAO,IACnC,SAAS,gBACP,QACA,SAAS,kBAAkB,QACzB,QACA,0BAA0B,SAAS,iBAAiB,kBAAkB;AAChF,QAAM,6BACJ,OAAO,YAAY,YACnB,YAAY,QACZ,EAAE,mBAAmB,WACrB,QAAQ,yBAAyB,SAC7B,0BAA0B,QAAQ,aAAa,IAC/C;AACN,QAAM,kBACJ,OAAO,YAAY,YAAY,YAAY,QAAQ,EAAE,mBAAmB,UACpE,MAAM,QAAQ,iBAAiB,GAAG,KAAK,OAAO,MAAM,yBAAyB,IAC7E;AACN,MAAI;AACF,QACE,OAAO,YAAY,YACnB,YAAY,QACZ,EAAE,mBAAmB,WACrB,QAAQ,kBAAkB,SAC1B,QAAQ,kBAAkB,MAC1B;AACA,YAAM,IAAI,mBAAmB,sEAAsE;AAAA,IACrG;AACA,UAAM,mBACJ,kBAAkB,QACd,+BAA+B,SAAS,aAAa,0BAA0B,IAC/E;AAAA,MACE,2BAA2B,SAAS,WAAW;AAAA,MAC/C;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACN,WAAO,UAAU,sBAAsB,kBAAkB,WAAW,CAAC;AAAA,EACvE,SAAS,KAAK;AACZ,QAAI,eAAe,mBAAoB,OAAM;AAC7C,UAAM,IAAI,mBAAmB,eAAe,QAAQ,IAAI,UAAU,0CAA0C,GAAG;AAAA,EACjH;AACF;AAqBA,SAAS,+BAA+B,SAAkB,aAAqB,SAA0B;AACvG,MAAI,aAAa;AACjB,MAAI,OAAO,YAAY,YAAY,kBAAkB,WAAW,GAAG;AACjE,QAAI;AACF,mBAAa,KAAK,MAAM,OAAO;AAAA,IACjC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF,WAAW,OAAO,YAAY,YAAY,iBAAiB,WAAW,GAAG;AACvE,0CAAsC,SAAS,OAAO;AACtD,WAAO;AAAA,EACT;AACA,QAAM,aAAa,yBAAyB,YAAY,OAAO;AAC/D,MAAI,YAAY;AACd,UAAM,IAAI;AAAA,MACR,gEAAgE,UAAU;AAAA,IAC5E;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,sCAAsC,MAAc,SAAuB;AAClF,QAAM,iBAAiB,oBAAI,IAAY;AACvC,QAAM,SAAS,MAAM,KAAK,IAAI,gBAAgB,IAAI,CAAC;AACnD,aAAW,CAAC,GAAG,KAAK,QAAQ;AAC1B,QAAI,CAAC,wBAAwB,SAAS,GAAG,EAAG;AAC5C,UAAM,gBAAgB,IAAI,YAAY;AACtC,QAAI,eAAe,IAAI,aAAa,GAAG;AACrC,YAAM,IAAI;AAAA,QACR,oEAAoE,GAAG;AAAA,MACzE;AAAA,IACF;AACA,mBAAe,IAAI,aAAa;AAAA,EAClC;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,QAAI,CAAC,wBAAwB,SAAS,GAAG,EAAG;AAC5C,QAAI,CAAC,4BAA4B,KAAK,GAAG;AACvC,YAAM,IAAI;AAAA,QACR,gEAAgE,GAAG;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,yBAAyB,OAAgB,SAAgC;AAChF,QAAM,QAAiD,CAAC,EAAE,OAAO,MAAM,GAAG,CAAC;AAC3E,QAAM,OAAO,oBAAI,QAAgB;AACjC,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,EAAE,OAAO,SAAS,KAAK,IAAI,MAAM,IAAI;AAC3C,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU;AAC7C,QAAI,KAAK,IAAI,OAAO,EAAG;AACvB,SAAK,IAAI,OAAO;AAEhB,QAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,eAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,cAAM,KAAK,EAAE,OAAO,QAAQ,CAAC,GAAG,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC;AAAA,MACzD;AACA;AAAA,IACF;AAEA,UAAM,UAAU,OAAO,QAAQ,OAAkC;AACjE,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,YAAM,CAAC,KAAK,UAAU,IAAI,QAAQ,CAAC;AACnC,YAAM,YAAY,OAAO,GAAG,IAAI,IAAI,GAAG,KAAK;AAC5C,UAAI,wBAAwB,SAAS,GAAG,KAAK,CAAC,4BAA4B,UAAU,EAAG,QAAO;AAC9F,YAAM,KAAK,EAAE,OAAO,YAAY,MAAM,UAAU,CAAC;AAAA,IACnD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,4BAA4B,OAAyB;AAC5D,SAAO,UAAU,gBAAgB,UAAU;AAC7C;AAEA,SAAS,2BAA2B,SAAkB,aAA8B;AAClF,MAAI,YAAY,OAAW,QAAO;AAClC,MAAI,OAAO,YAAY,UAAU;AAC/B,QAAI,CAAC,kBAAkB,WAAW,EAAG,QAAO;AAC5C,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,mBAAmB,gBAAiB,QAAO,QAAQ,SAAS;AAChE,MAAI,OAAO,aAAa,eAAe,mBAAmB,UAAU;AAClE,UAAM,MAA+B,CAAC;AACtC,YAAQ,QAAQ,CAAC,GAAG,MAAM;AACxB,UAAI,CAAC,IAAI,OAAO,MAAM,WAAW,IAAI;AAAA,IACvC,CAAC;AACD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,yBACP,SACA,aACA,eACA,iBACS;AACT,MAAI,YAAY,QAAW;AAIzB,WAAO,CAAC;AAAA,EACV;AACA,MAAI,YAAY,KAAM,QAAO;AAK7B,MAAI,OAAO,WAAW,eAAe,OAAO,SAAS,OAAO,GAAG;AAC7D,WAAO,WAAW,QAAQ,MAAM;AAAA,EAClC;AACA,MAAI,OAAO,SAAS,eAAe,mBAAmB,MAAM;AAC1D,WAAO,WAAW,QAAQ,IAAI;AAAA,EAChC;AACA,MAAI,mBAAmB,aAAa;AAClC,WAAO,WAAW,QAAQ,UAAU;AAAA,EACtC;AACA,MAAI,YAAY,OAAO,OAAO,GAAG;AAC/B,WAAO,WAAW,QAAQ,UAAU;AAAA,EACtC;AAEA,MAAI,OAAO,YAAY,UAAU;AAC/B,QAAI,kBAAkB,WAAW,GAAG;AAClC,UAAI;AACJ,UAAI;AACF,iBAAS,KAAK,MAAM,OAAO;AAAA,MAC7B,QAAQ;AAGN,eAAO,IAAI,2BAA2B,SAAS,aAAa,GAAG,eAAe;AAAA,MAChF;AACA,YAAMA,YAAW,cAAc,QAAQ,aAAa;AACpD,sBAAgBA,SAAQ;AACxB,YAAMC,QAAO,cAAcD,SAAQ;AACnC,UAAIC,SAAQ,kBAAkB,KAAK,aAAaA,KAAI,IAAI,iBAAiB;AACvE,eAAO,cAAc,aAAaA,KAAI,CAAC;AAAA,MACzC;AACA,aAAOD;AAAA,IACT;AAGA,QAAI,iBAAiB,WAAW,GAAG;AACjC,aAAO,IAAI,qBAAqB,SAAS,aAAa,GAAG,eAAe;AAAA,IAC1E;AACA,WAAO,IAAI,SAAS,eAAe;AAAA,EACrC;AAEA,QAAM,WAAW,cAAc,SAAS,aAAa;AACrD,kBAAgB,QAAQ;AACxB,QAAM,OAAO,cAAc,QAAQ;AACnC,MAAI,QAAQ,kBAAkB,KAAK,aAAa,IAAI,IAAI,iBAAiB;AACvE,WAAO,cAAc,aAAa,IAAI,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AAwBO,SAAS,+BAA+B,QAAmE;AAChH,SAAO;AAAA,IACL,SAAS;AAAA,IACT,gBAAgB,OAAO;AAAA,IACvB,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO;AAAA,IAClB,iBAAiB,OAAO;AAAA,EAC1B;AACF;AAOA,SAAS,mBAAqC;AAC5C,SAAO;AAAA,IACL,kBAAkB,CAAI,IAAY,OAAyC,QAAQ,QAAQ,GAAG,CAAC;AAAA,IAC/F,WAAW,CAAC,kBAAoE;AAAA,IAChF,QAAQ,MAAM;AAAA,IACd,OAAO,CAAC,YAAsE;AAAA,MAC5E,OAAO,CAAC;AAAA,MACR,OAAO;AAAA,MACP,WAAW;AAAA,MACX,iBAAiB,OAAO,mBAAkB,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,MAClE,eAAe;AAAA,IACjB;AAAA,IACA,OAAO,MAAM;AAAA,IACb,OAAO,OAAkC;AAAA,MACvC,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,YAAY,CAAC;AAAA,MACb,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,QAAQ;AAAA,IACV;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEA,IAAI,wBAAwB;AAC5B,SAAS,4BAAkC;AACzC,MAAI,sBAAuB;AAC3B,MAAI,OAAO,YAAY,eAAe,CAAC,QAAQ,IAAK;AACpD,MAAI,QAAQ,IAAI,aAAa,aAAc;AAC3C,MAAI,QAAQ,IAAI,iCAAiC,IAAK;AACtD,0BAAwB;AAExB,UAAQ;AAAA,IACN;AAAA,EAGF;AACF;AAQA,eAAe,SACb,OACA,MACA,aACkB;AAClB,MAAI;AAEJ,MAAI,MAAM,SAAS,UAAa,KAAK,SAAS,MAAM;AAClD,UAAM,KAAK;AAAA,EACb,WAAW,iBAAiB,SAAS;AACnC,QAAI;AACF,YAAM,QAAQ,MAAM,MAAM;AAC1B,UAAI,gBAAgB,WAAW,MAAM,uBAAuB;AAC1D,cAAM,MAAM,MAAM,SAAS;AAAA,MAC7B,OAAO;AACL,cAAM,MAAM,MAAM,KAAK;AAAA,MACzB;AAAA,IACF,QAAQ;AACN,YAAM;AAAA,IACR;AAAA,EACF,OAAO;AACL,UAAM;AAAA,EACR;AAEA,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI,CAAC,kBAAkB,WAAW,EAAG,QAAO;AAC5C,QAAI;AACF,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,eAAe,gBAAiB,QAAO,IAAI,SAAS;AACxD,MAAI,OAAO,aAAa,eAAe,eAAe,UAAU;AAC9D,UAAM,MAA+B,CAAC;AACtC,QAAI,QAAQ,CAAC,GAAG,MAAM;AACpB,UAAI,CAAC,IAAI,OAAO,MAAM,WAAW,IAAI;AAAA,IACvC,CAAC;AACD,WAAO;AAAA,EACT;AAKA,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,SAAO,OAAO,GAAG;AACnB;AAEA,SAAS,kBAAkB,aAA0C;AACnE,QAAM,OAAO,gBAAgB,WAAW;AACxC,SAAO,SAAS,sBAAsB,UAAU,KAAK,IAAI;AAC3D;AAEA,SAAS,iBAAiB,aAA0C;AAClE,SAAO,gBAAgB,WAAW,MAAM;AAC1C;AAEA,SAAS,gBAAgB,aAAyC;AAChE,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,YAAY,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,EAAE,YAAY,KAAK;AAC5D;AAEA,SAAS,qBAAqB,MAAc,SAAyB;AACnE,MAAI;AACF,UAAM,SAAS,IAAI,gBAAgB,IAAI;AACvC,UAAM,MAAM,IAAI,gBAAgB;AAChC,WAAO,QAAQ,CAAC,GAAG,MAAM;AACvB,UAAI,OAAO,GAAG,wBAAwB,SAAS,EAAE,YAAY,CAAC,IAAI,eAAe,CAAC;AAAA,IACpF,CAAC;AACD,WAAO,IAAI,SAAS;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,2BAA2B,MAAc,SAAyB;AAMzE,MAAI,MAAM;AACV,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACtB,QAAI,KAAK,CAAC,MAAM,KAAK;AACnB,aAAO,KAAK,GAAG;AACf;AAAA,IACF;AAEA,UAAM,WAAW,oBAAoB,MAAM,CAAC;AAC5C,QAAI,CAAC,UAAU;AACb,aAAO,KAAK,MAAM,CAAC;AACnB;AAAA,IACF;AAEA,QAAI,QAAQ,SAAS;AACrB,WAAO,QAAQ,KAAK,UAAU,iBAAiB,KAAK,KAAK,CAAC,EAAG;AAC7D,QAAI,KAAK,KAAK,MAAM,KAAK;AACvB,aAAO,KAAK,MAAM,GAAG,SAAS,GAAG;AACjC,UAAI,SAAS;AACb;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,YAAM,KAAK,MAAM,SAAS,OAAO;AAAA,IACnC,QAAQ;AACN,aAAO,KAAK,MAAM,GAAG,SAAS,GAAG;AACjC,UAAI,SAAS;AACb;AAAA,IACF;AAEA,UAAM,aAAa,QAAQ;AAC3B,QAAI,QAAQ;AACZ,WAAO,QAAQ,KAAK,UAAU,iBAAiB,KAAK,KAAK,CAAC,EAAG;AAC7D,WAAO,KAAK,MAAM,GAAG,KAAK;AAC1B,QAAI,OAAO,QAAQ,YAAY,CAAC,wBAAwB,SAAS,GAAG,GAAG;AACrE,UAAI;AACJ;AAAA,IACF;AAEA,WAAO;AACP,QAAI,kBAAkB,MAAM,KAAK;AAAA,EACnC;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAe,OAAwD;AAClG,WAAS,IAAI,QAAQ,GAAG,IAAI,MAAM,QAAQ,KAAK;AAC7C,UAAM,KAAK,MAAM,CAAC;AAClB,QAAI,OAAO,IAAK,QAAO,EAAE,SAAS,MAAM,MAAM,OAAO,IAAI,CAAC,GAAG,KAAK,IAAI,EAAE;AACxE,QAAI,OAAO,KAAM;AAAA,EACnB;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,OAAe,OAAuB;AAC/D,MAAI,MAAM,KAAK,MAAM,KAAK;AACxB,UAAM,QAAQ,oBAAoB,OAAO,KAAK;AAC9C,WAAO,OAAO,OAAO,MAAM;AAAA,EAC7B;AACA,MAAI,MAAM,KAAK,MAAM,OAAO,MAAM,KAAK,MAAM,KAAK;AAChD,UAAM,SAAS,MAAM,KAAK;AAC1B,UAAM,SAAS,WAAW,MAAM,MAAM;AACtC,QAAI,QAAQ;AACZ,aAASE,KAAI,OAAOA,KAAI,MAAM,QAAQA,MAAK;AACzC,YAAM,KAAK,MAAMA,EAAC;AAClB,UAAI,OAAO,KAAK;AACd,cAAM,QAAQ,oBAAoB,OAAOA,EAAC;AAC1C,YAAI,CAAC,MAAO,QAAO,MAAM;AACzB,QAAAA,KAAI,MAAM,MAAM;AAChB;AAAA,MACF;AACA,UAAI,OAAO,OAAQ;AACnB,UAAI,OAAO,QAAQ;AACjB;AACA,YAAI,UAAU,EAAG,QAAOA,KAAI;AAAA,MAC9B;AAAA,IACF;AACA,WAAO,MAAM;AAAA,EACf;AACA,MAAI,IAAI;AACR,SAAO,IAAI,MAAM,UAAU,MAAM,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,OAAO,CAAC,iBAAiB,MAAM,CAAC,CAAC,GAAG;AAClH;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,IAAiC;AACzD,SAAO,OAAO,OAAO,OAAO,QAAQ,OAAO,QAAQ,OAAO;AAC5D;AAEA,SAAS,cAAc,OAAoC;AACzD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,GAAmB;AACvC,MAAI,OAAO,WAAW,YAAa,QAAO,OAAO,WAAW,GAAG,MAAM;AAErE,SAAO,EAAE;AACX;AAEA,SAAS,kBAAkB,OAAwB;AACjD,MAAI,OAAO,UAAU,SAAU,QAAO,aAAa,KAAK;AACxD,QAAM,OAAO,cAAc,KAAK;AAChC,SAAO,OAAO,aAAa,IAAI,IAAI;AACrC;AAEA,SAAS,IAAI,GAAW,UAA0B;AAChD,MAAI,YAAY,EAAG,QAAO;AAC1B,MAAI,aAAa,CAAC,KAAK,SAAU,QAAO;AACxC,SAAO,cAAc,aAAa,CAAC,CAAC;AACtC;","names":["redacted","json","i"]}
1
+ {"version":3,"sources":["../../../src/lib/upstream-recorder/recorder.ts"],"sourcesContent":["/**\n * `createUpstreamRecorder` implementation. See `./types.ts` for the public\n * surface and `./README` (file header on `index.ts`) for the why.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { createHash } from 'node:crypto';\nimport { isRegExp } from 'node:util/types';\nimport {\n normalizeSecretKeyPattern,\n redactSecrets,\n secretKeyPatternMatches,\n SECRET_KEY_PATTERN,\n} from '../utils/redact-secrets';\nimport { globToRegExp } from '../utils/glob';\nimport { canonicalize } from '../utils/jcs';\nimport { MAX_JSON_DEPTH } from '../utils/json-depth';\nimport { IDENTIFIER_DIGEST_LIMIT } from './constants';\nimport {\n UpstreamRecorderScopeError,\n type QueryUpstreamTrafficResponse,\n type RecordInput,\n type RawRecordedCall,\n type RecordedCall,\n type UpstreamRecorder,\n type UpstreamRecorderDebugInfo,\n type UpstreamRecorderErrorEvent,\n type UpstreamRecorderOptions,\n type UpstreamRecorderQueryParams,\n type UpstreamRecorderQueryResult,\n} from './types';\n\nconst DEFAULT_BUFFER_SIZE = 1000;\nconst DEFAULT_TTL_MS = 60 * 60 * 1000; // 1h\nconst DEFAULT_QUERY_LIMIT = 100;\nconst DEFAULT_MAX_PAYLOAD_BYTES = 65_536; // mirrors spec's `recorded_calls[].payload.maxLength`\nconst MAX_BUFFER_SIZE = 100_000;\nconst MAX_TTL_MS = 24 * 60 * 60 * 1000; // 24h\nconst VALID_PURPOSES = new Set([\n 'platform_primary',\n 'measurement',\n 'attribution',\n 'creative_serving',\n 'identity',\n 'other',\n]);\n\nexport class PayloadDigestError extends Error {\n cause: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = 'PayloadDigestError';\n this.cause = cause;\n }\n}\n\n/**\n * Wrapped per-call entry stored in the buffer. Carries the public\n * `RecordedCall` plus the principal it was recorded under (kept off the\n * public type so cross-principal isolation is enforced at query time and\n * the principal never appears in the controller-response payload).\n */\ninterface InternalEntry {\n call: RawRecordedCall;\n principal: string;\n /** ms-since-epoch cached for TTL eviction; computed at insert time. */\n recordedAtMs: number;\n}\n\nfunction clamp(n: number | undefined, lo: number, hi: number, fallback: number): number {\n if (typeof n !== 'number' || !Number.isFinite(n)) return fallback;\n // Out-of-range values (zero / negative / above ceiling) revert to the\n // default rather than silently saturating at the limit — a typo'd\n // `bufferSize: -10` silently saturating to `1` would drop nearly\n // everything; the default is a much safer failure mode.\n if (n < lo || n > hi) return fallback;\n return n;\n}\n\nfunction isPrincipalString(p: unknown): p is string {\n return typeof p === 'string' && p.length > 0;\n}\n\n/**\n * Build the recorder. Producer-side companion to the\n * `runner-output-contract.yaml` v2.0.0 `upstream_traffic` storyboard\n * check (spec PR adcontextprotocol/adcp#3816).\n */\nexport function createUpstreamRecorder(options: UpstreamRecorderOptions = {}): UpstreamRecorder {\n const enabled = options.enabled ?? true;\n // Disabled-recorder fast-path: skip every wrapping cost. Production\n // builds get a no-op object whose methods compile to inlinable returns.\n if (!enabled) return makeNoopRecorder();\n\n // Production-enable footgun guard: when adopters ship with `enabled:\n // true` in `NODE_ENV=production` (forgot the gate, or the gate is\n // mis-evaluated), emit a one-time warning. ADCP_RECORDER_PRODUCTION_ACK=1\n // is the explicit acknowledgment escape hatch for the rare adopters\n // who legitimately want recording in prod.\n warnIfEnabledInProduction();\n\n const redactPattern = normalizeSecretKeyPattern(options.redactPattern ?? SECRET_KEY_PATTERN);\n const bufferSize = clamp(options.bufferSize, 1, MAX_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);\n const ttlMs = clamp(options.ttlMs, 1, MAX_TTL_MS, DEFAULT_TTL_MS);\n const maxPayloadBytes = clamp(options.maxPayloadBytes, 0, 16 * 1024 * 1024, DEFAULT_MAX_PAYLOAD_BYTES);\n const purpose = options.purpose;\n const strict = options.strict === true;\n const onError = options.onError;\n\n // Buffer: ordered by insertion (== timestamp ascending). Eviction is\n // FIFO when full or TTL-prune at record time.\n const buffer: InternalEntry[] = [];\n\n // Active-principal context. Set by `runWithPrincipal`; read by the\n // wrapped fetch and by `record` when no explicit principal is passed.\n const principalStorage = new AsyncLocalStorage<string>();\n\n // ────────────────────────────────────────────────────────────\n // Internal helpers\n // ────────────────────────────────────────────────────────────\n\n function emitError(event: UpstreamRecorderErrorEvent): void {\n if (!onError) return;\n try {\n onError(event);\n } catch {\n // onError MUST NOT crash the recorder — silent.\n }\n }\n\n function evictExpired(nowMs: number): void {\n if (buffer.length === 0) return;\n const cutoff = nowMs - ttlMs;\n let idx = 0;\n while (idx < buffer.length && buffer[idx]!.recordedAtMs < cutoff) idx++;\n if (idx > 0) buffer.splice(0, idx);\n }\n\n function pushEntry(entry: InternalEntry): void {\n buffer.push(entry);\n while (buffer.length > bufferSize) buffer.shift();\n }\n\n function applyRedactionToHeaders(headers: Record<string, string> | undefined): Record<string, string> {\n if (!headers) return {};\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) {\n const lower = k.toLowerCase();\n out[lower] = secretKeyPatternMatches(redactPattern, lower) ? '[redacted]' : v;\n }\n return out;\n }\n\n /**\n * Apply per-key redaction to the body. JSON bodies (objects + arrays)\n * walk through `redactSecrets`. Form-urlencoded strings parse +\n * key-redact + re-stringify so `access_token=xxx` is caught without\n * the adopter pre-redacting. Other string types pass through. Binary\n * types (Buffer / Blob / ArrayBuffer / TypedArray) are replaced with\n * a marker — the buffer should not hold raw bytes whose JSON-shape\n * is misleading downstream.\n */\n function applyRedactionToPayload(payload: unknown, contentType: string): { value: unknown; bytes: number } {\n const value = normalizeRecordedPayload(payload, contentType, redactPattern, maxPayloadBytes);\n return { value, bytes: payloadWireLength(value) };\n }\n\n function classifyPurpose(\n method: string,\n url: string,\n host: string,\n path: string,\n headers: Record<string, string>\n ): string | undefined {\n if (!purpose) return undefined;\n try {\n const value = purpose({ method, url, host, path, headers }) as unknown;\n if (value === undefined) return undefined;\n if (typeof value === 'string' && VALID_PURPOSES.has(value)) return value;\n emitError({ kind: 'classifier_invalid_purpose', purpose: typeof value === 'string' ? value : String(value) });\n return undefined;\n } catch (err) {\n emitError({ kind: 'classifier_threw', err });\n return undefined;\n }\n }\n\n function buildRecordedCall(input: RecordInput, nowMs: number): RawRecordedCall | null {\n try {\n const url = input.url;\n let host = '';\n let pathPart = '';\n try {\n const parsed = new URL(url);\n host = parsed.host;\n pathPart = parsed.pathname;\n } catch {\n emitError({ kind: 'url_parse_failed', url });\n }\n const redactedHeaders = applyRedactionToHeaders(input.headers);\n const purposeTag = classifyPurpose(input.method, url, host, pathPart, redactedHeaders);\n const { value: payload, bytes: payloadLength } = applyRedactionToPayload(input.payload, input.content_type);\n return {\n method: input.method,\n endpoint: `${input.method} ${url}`,\n url,\n host,\n path: pathPart,\n content_type: input.content_type,\n attestation_mode: 'raw',\n payload,\n payload_length: payloadLength,\n timestamp: new Date(nowMs).toISOString(),\n ...(input.status_code !== undefined && { status_code: input.status_code }),\n ...(purposeTag !== undefined && { purpose: purposeTag }),\n };\n } catch (err) {\n // Hostile / buggy payload (throwing getter, etc.). Recording MUST\n // never break the adapter call site.\n emitError({ kind: 'payload_build_failed', err });\n return null;\n }\n }\n\n // ────────────────────────────────────────────────────────────\n // Public methods\n // ────────────────────────────────────────────────────────────\n\n function runWithPrincipal<T>(principal: string, fn: () => T | Promise<T>): Promise<T> {\n if (!isPrincipalString(principal)) {\n return Promise.reject(\n new UpstreamRecorderScopeError(\n 'runWithPrincipal: principal MUST be a non-empty string. Pass the same identifier you receive in your comply_test_controller handler.'\n )\n );\n }\n return Promise.resolve(principalStorage.run(principal, fn));\n }\n\n function record(input: RecordInput, explicitPrincipal?: string): void {\n const principal = explicitPrincipal ?? principalStorage.getStore();\n if (!isPrincipalString(principal)) {\n if (strict) {\n throw new UpstreamRecorderScopeError(\n `record() called outside runWithPrincipal scope (method=${input.method}, url=${input.url}). Wrap the call site or pass an explicit principal.`\n );\n }\n emitError({ kind: 'unscoped_record', method: input.method, url: input.url });\n return;\n }\n const nowMs = Date.now();\n evictExpired(nowMs);\n const call = buildRecordedCall(input, nowMs);\n if (!call) return;\n pushEntry({ call, principal, recordedAtMs: nowMs });\n }\n\n function wrapFetch(originalFetch: typeof globalThis.fetch): typeof globalThis.fetch {\n return async function wrappedFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const principal = principalStorage.getStore();\n // No active principal: pass through unchanged in non-strict mode.\n // In strict mode, a fetch outside scope is the same authoring bug\n // as `record()` outside scope and surfaces the same way.\n if (!principal) {\n if (strict) {\n throw new UpstreamRecorderScopeError(\n `wrapFetch invoked outside runWithPrincipal scope (input=${typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url}). Wrap the call site.`\n );\n }\n return originalFetch(input as Parameters<typeof globalThis.fetch>[0], init);\n }\n\n const method = (init?.method ?? (input instanceof Request ? input.method : 'GET')).toUpperCase();\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n\n // Header normalization — fetch's Headers form vs init.headers Record vs Request body.\n const headerObj: Record<string, string> = {};\n if (init?.headers) {\n const h = init.headers;\n if (h instanceof Headers) {\n h.forEach((v, k) => {\n headerObj[k.toLowerCase()] = v;\n });\n } else if (Array.isArray(h)) {\n for (const [k, v] of h) headerObj[k.toLowerCase()] = v;\n } else {\n for (const [k, v] of Object.entries(h as Record<string, string>)) headerObj[k.toLowerCase()] = v;\n }\n } else if (input instanceof Request) {\n input.headers.forEach((v, k) => {\n headerObj[k.toLowerCase()] = v;\n });\n }\n\n const contentType = headerObj['content-type'] ?? '';\n const payload = await readBody(input, init, contentType);\n\n // Fire the actual call; record after we have a status_code. On\n // throw, we still record the attempt with no status_code so the\n // adopter can see the call was made.\n let response: Response | undefined;\n let thrown: unknown;\n try {\n response = await originalFetch(input as Parameters<typeof globalThis.fetch>[0], init);\n } catch (err) {\n thrown = err;\n }\n\n record(\n {\n method,\n url,\n content_type: contentType,\n headers: headerObj,\n payload,\n ...(response && { status_code: response.status }),\n },\n principal\n );\n\n if (thrown) throw thrown;\n return response!;\n };\n }\n\n function query(params: UpstreamRecorderQueryParams): UpstreamRecorderQueryResult {\n if (!isPrincipalString(params.principal)) {\n throw new UpstreamRecorderScopeError(\n 'query({ principal }): principal MUST be a non-empty string. Cross-principal isolation depends on this — passing an empty / non-string value would silently match no entries and conceal misconfiguration.'\n );\n }\n const nowMs = Date.now();\n evictExpired(nowMs);\n const limit = clamp(params.limit, 1, bufferSize, DEFAULT_QUERY_LIMIT);\n const sinceMs = params.sinceTimestamp ? Date.parse(params.sinceTimestamp) : 0;\n const endpointMatcher = params.endpointPattern ? globToRegExp(params.endpointPattern) : undefined;\n\n const matched = buffer.filter(entry => {\n if (entry.principal !== params.principal) return false; // hard isolation\n if (entry.recordedAtMs < sinceMs) return false;\n if (endpointMatcher && !endpointMatcher.test(entry.call.endpoint)) return false;\n return true;\n });\n\n const items: RecordedCall[] = [];\n let total = 0;\n let dropped_count = 0;\n for (const entry of matched) {\n const projected = safeProjectRecordedCall(entry.call, params, emitError);\n if (!projected) {\n if (params.attestationMode === 'digest') dropped_count++;\n continue;\n }\n total++;\n if (items.length < limit) items.push(projected);\n }\n const since_timestamp =\n params.sinceTimestamp ?? matched[0]?.call.timestamp ?? new Date(nowMs - ttlMs).toISOString();\n return {\n items,\n total,\n truncated: total > items.length,\n since_timestamp,\n dropped_count,\n };\n }\n\n function clear(): void {\n buffer.length = 0;\n }\n\n function debug(): UpstreamRecorderDebugInfo {\n const principals = Array.from(new Set(buffer.map(e => e.principal))).sort();\n const last = buffer[buffer.length - 1];\n return {\n enabled: true,\n bufferSize,\n bufferedEntries: buffer.length,\n principals,\n lastRecordedAt: last ? last.call.timestamp : null,\n activePrincipal: principalStorage.getStore() ?? null,\n strict,\n };\n }\n\n return {\n runWithPrincipal,\n wrapFetch,\n record,\n query,\n clear,\n debug,\n enabled: true,\n };\n}\n\nfunction safeProjectRecordedCall(\n call: RawRecordedCall,\n params: UpstreamRecorderQueryParams,\n emitError?: (event: UpstreamRecorderErrorEvent) => void\n): RecordedCall | null {\n try {\n return projectRecordedCall(call, params, emitError);\n } catch (err) {\n emitError?.({ kind: 'digest_canonicalization_failed', method: call.method, endpoint: call.endpoint, err });\n return null;\n }\n}\n\nfunction projectRecordedCall(\n call: RawRecordedCall,\n params: UpstreamRecorderQueryParams,\n emitError?: (event: UpstreamRecorderErrorEvent) => void\n): RecordedCall {\n if (params.attestationMode !== 'digest') return call;\n const payloadBytes = canonicalPayloadBytes(call.payload, call.content_type);\n const payloadDigest = sha256Hex(payloadBytes);\n const identifierDigests = normalizeIdentifierDigests(params.identifierValueDigests);\n const identifierScanPayload =\n identifierDigests.length > 0\n ? parseJsonStringPayloadForScan(call.payload, call.content_type, emitError)\n : undefined;\n const matchedIdentifierDigests =\n identifierDigests.length > 0 && identifierScanPayload !== undefined\n ? collectMatchedStringLeafDigests(identifierScanPayload, new Set(identifierDigests))\n : undefined;\n const identifier_match_proofs =\n identifierDigests.length > 0 && matchedIdentifierDigests\n ? identifierDigests.map(digest => ({\n identifier_value_sha256: digest,\n found: matchedIdentifierDigests.has(digest),\n }))\n : undefined;\n\n return {\n method: call.method,\n endpoint: call.endpoint,\n url: call.url,\n host: call.host,\n path: call.path,\n content_type: call.content_type,\n attestation_mode: 'digest',\n payload_digest_sha256: payloadDigest,\n payload_length: byteLengthOf(payloadBytes),\n timestamp: call.timestamp,\n ...(call.status_code !== undefined && { status_code: call.status_code }),\n ...(call.purpose !== undefined && { purpose: call.purpose }),\n ...(identifier_match_proofs !== undefined && { identifier_match_proofs }),\n };\n}\n\nfunction normalizeIdentifierDigests(input: string[] | undefined): string[] {\n if (!Array.isArray(input)) return [];\n const out: string[] = [];\n for (const value of input) {\n if (typeof value !== 'string' || !/^[a-f0-9]{64}$/.test(value)) continue;\n if (!out.includes(value)) out.push(value);\n if (out.length >= IDENTIFIER_DIGEST_LIMIT) break;\n }\n return out;\n}\n\nfunction collectMatchedStringLeafDigests(root: unknown, wantedDigests: Set<string>): Set<string> {\n const matched = new Set<string>();\n const stack: Array<{ value: unknown; depth: number }> = [{ value: root, depth: 0 }];\n while (stack.length > 0) {\n const { value, depth } = stack.pop()!;\n if (depth > MAX_JSON_DEPTH) continue;\n if (typeof value === 'string') {\n const digest = sha256Hex(value);\n if (wantedDigests.has(digest)) {\n matched.add(digest);\n if (matched.size === wantedDigests.size) break;\n }\n } else if (Array.isArray(value)) {\n for (const item of value) stack.push({ value: item, depth: depth + 1 });\n } else if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value as Record<string, unknown>)) {\n stack.push({ value: item, depth: depth + 1 });\n }\n }\n }\n return matched;\n}\n\nfunction parseJsonStringPayloadForScan(\n payload: unknown,\n contentType: string,\n emitError?: (event: UpstreamRecorderErrorEvent) => void\n): unknown | undefined {\n if (!isJsonContentType(contentType)) return undefined;\n if (typeof payload !== 'string') return payload;\n try {\n return JSON.parse(payload);\n } catch (err) {\n emitError?.({ kind: 'json_payload_parse_failed', content_type: contentType, err });\n return payload;\n }\n}\n\nfunction canonicalPayloadBytes(payload: unknown, contentType: string): string {\n if (isJsonContentType(contentType)) {\n if (typeof payload === 'string') {\n let parsed: unknown;\n try {\n parsed = JSON.parse(payload);\n } catch {\n return payload;\n }\n return canonicalJsonStringify(parsed);\n }\n return canonicalJsonStringify(payload);\n }\n if (typeof payload === 'string') return payload;\n return safeStringify(payload) ?? '';\n}\n\nfunction canonicalJsonStringify(value: unknown): string {\n assertJsonDepth(value);\n return canonicalize(value);\n}\n\nfunction assertJsonDepth(root: unknown): void {\n const stack: Array<{ value: unknown; depth: number }> = [{ value: root, depth: 0 }];\n while (stack.length > 0) {\n const { value, depth } = stack.pop()!;\n if (depth > MAX_JSON_DEPTH) {\n throw new RangeError(`JSON payload exceeds max canonicalization depth ${MAX_JSON_DEPTH}`);\n }\n if (Array.isArray(value)) {\n for (const item of value) stack.push({ value: item, depth: depth + 1 });\n } else if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value as Record<string, unknown>)) {\n stack.push({ value: item, depth: depth + 1 });\n }\n }\n }\n}\n\nfunction sha256Hex(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex');\n}\n\n/**\n * Compute the `RecordedCall.payload_digest_sha256` value. By default this\n * applies the same body snapshot normalization, payload normalization, and\n * canonical secret-key redaction used by the recorder before hashing; pass a\n * custom redaction pattern and `maxPayloadBytes` when your recorder uses matching\n * options:\n *\n * - `createUpstreamRecorder({ redactPattern })` → `computePayloadDigestSha256(..., { redactPattern })`\n * - `createUpstreamRecorder({ maxPayloadBytes })` → `computePayloadDigestSha256(..., { maxPayloadBytes })`\n *\n * Use `{ prenormalized: true }` only when the payload has already been\n * normalized/redacted exactly as the recorder would store it. The\n * prenormalized path skips redaction, so the helper rejects secret-shaped keys\n * whose values are not already the literal `\"[redacted]\"` marker or an\n * explicit null placeholder. For form-encoded bodies, duplicate secret-shaped\n * keys are also rejected because last-wins maps cannot prove that every\n * original value was inspected before hashing.\n *\n * JSON content is serialized with RFC 8785 JCS before hashing. When\n * `contentType` is JSON-shaped and `payload` is a string, the helper parses\n * it first so semantically equivalent JSON strings and objects hash to the\n * same value. On the normalizing path, an unparseable JSON-shaped string is\n * treated as the emitted body bytes and hashed verbatim after best-effort\n * redaction. On the prenormalized path, malformed JSON strings throw because\n * their secret-key structure cannot be proven safe. Unsupported JSON values\n * and payloads deeper than the recorder's depth cap throw rather than\n * producing a non-canonical digest.\n *\n * The returned digest is lowercase hex, matching the 3.1\n * `query_upstream_traffic` wire contract.\n */\nexport function computePayloadDigestSha256(\n payload: unknown,\n contentType = 'application/json',\n options: PayloadDigestOptions = {}\n): string {\n try {\n assertPayloadDigestOptions(options);\n const redactPattern = options.prenormalized\n ? false\n : options.redactPattern === false\n ? false\n : normalizeSecretKeyPattern(options.redactPattern ?? SECRET_KEY_PATTERN);\n const prenormalizedSafetyPattern = isRegExp(options.redactPattern)\n ? normalizeSecretKeyPattern(options.redactPattern)\n : SECRET_KEY_PATTERN;\n const maxPayloadBytes = clamp(options.maxPayloadBytes, 0, 16 * 1024 * 1024, DEFAULT_MAX_PAYLOAD_BYTES);\n if (options.redactPattern === false && options.prenormalized !== true) {\n throw new PayloadDigestError('PayloadDigestOptions.redactPattern=false requires prenormalized=true');\n }\n const payloadForDigest =\n redactPattern === false\n ? assertPrenormalizedPayloadSafe(payload, contentType, prenormalizedSafetyPattern)\n : normalizeRecordedPayload(\n normalizeFetchPayloadInput(payload, contentType),\n contentType,\n redactPattern,\n maxPayloadBytes\n );\n return sha256Hex(canonicalPayloadBytes(payloadForDigest, contentType));\n } catch (err) {\n if (err instanceof PayloadDigestError) throw err;\n throw new PayloadDigestError(err instanceof Error ? err.message : 'Payload digest canonicalization failed', err);\n }\n}\n\nfunction assertPayloadDigestOptions(options: unknown): asserts options is PayloadDigestOptions {\n if (typeof options !== 'object' || options === null || isRegExp(options) || Array.isArray(options)) {\n throw new PayloadDigestError('computePayloadDigestSha256 options must be a PayloadDigestOptions object');\n }\n}\n\nexport type PayloadDigestOptions = {\n /**\n * Custom redaction pattern. Passing `false` is accepted only with\n * `prenormalized: true`; otherwise it would silently bypass redaction.\n */\n redactPattern?: RegExp | false;\n /**\n * Match `createUpstreamRecorder({ maxPayloadBytes })` when computing\n * digests outside the recorder.\n */\n maxPayloadBytes?: number;\n /**\n * Set only for payloads that have already been normalized and redacted.\n * The helper skips redaction but still scans secret-shaped keys, rejects\n * malformed JSON strings, and rejects duplicate secret-shaped form keys.\n */\n prenormalized?: boolean;\n};\n\nfunction assertPrenormalizedPayloadSafe(payload: unknown, contentType: string, pattern: RegExp): unknown {\n let scanTarget = payload;\n if (typeof payload === 'string' && isJsonContentType(contentType)) {\n try {\n scanTarget = JSON.parse(payload);\n } catch {\n throw new PayloadDigestError(\n 'Prenormalized JSON payload is malformed; hash the recorder-normalized payload or omit prenormalized.'\n );\n }\n } else if (typeof payload === 'string' && isFormUrlEncoded(contentType)) {\n assertPrenormalizedFormUrlEncodedSafe(payload, pattern);\n return payload;\n }\n const unsafePath = findUnredactedSecretPath(scanTarget, pattern);\n if (unsafePath) {\n throw new PayloadDigestError(\n `Prenormalized payload contains unredacted secret-shaped key \"${unsafePath}\"; hash the recorder-normalized payload or omit prenormalized.`\n );\n }\n return payload;\n}\n\nfunction assertPrenormalizedFormUrlEncodedSafe(body: string, pattern: RegExp): void {\n const seenSecretKeys = new Set<string>();\n const params = Array.from(new URLSearchParams(body));\n for (const [key] of params) {\n if (!secretKeyPatternMatches(pattern, key)) continue;\n const normalizedKey = key.toLowerCase();\n if (seenSecretKeys.has(normalizedKey)) {\n throw new PayloadDigestError(\n `Prenormalized form payload contains duplicate secret-shaped key \"${key}\"; hash the recorder-normalized payload or omit prenormalized.`\n );\n }\n seenSecretKeys.add(normalizedKey);\n }\n for (const [key, value] of params) {\n if (!secretKeyPatternMatches(pattern, key)) continue;\n if (!isSafelyRedactedSecretValue(value)) {\n throw new PayloadDigestError(\n `Prenormalized payload contains unredacted secret-shaped key \"${key}\"; hash the recorder-normalized payload or omit prenormalized.`\n );\n }\n }\n}\n\nfunction findUnredactedSecretPath(value: unknown, pattern: RegExp): string | null {\n const stack: Array<{ value: unknown; path: string }> = [{ value, path: '' }];\n const seen = new WeakSet<object>();\n while (stack.length > 0) {\n const { value: current, path } = stack.pop()!;\n if (!current || typeof current !== 'object') continue;\n if (seen.has(current)) continue;\n seen.add(current);\n\n if (Array.isArray(current)) {\n for (let i = current.length - 1; i >= 0; i--) {\n stack.push({ value: current[i], path: `${path}[${i}]` });\n }\n continue;\n }\n\n const entries = Object.entries(current as Record<string, unknown>);\n for (let i = entries.length - 1; i >= 0; i--) {\n const [key, childValue] = entries[i]!;\n const childPath = path ? `${path}.${key}` : key;\n if (secretKeyPatternMatches(pattern, key) && !isSafelyRedactedSecretValue(childValue)) return childPath;\n stack.push({ value: childValue, path: childPath });\n }\n }\n return null;\n}\n\nfunction isSafelyRedactedSecretValue(value: unknown): boolean {\n return value === '[redacted]' || value === null;\n}\n\nfunction normalizeFetchPayloadInput(payload: unknown, contentType: string): unknown {\n if (payload === undefined) return undefined;\n if (typeof payload === 'string') {\n if (!isJsonContentType(contentType)) return payload;\n try {\n return JSON.parse(payload);\n } catch {\n return payload;\n }\n }\n if (payload instanceof URLSearchParams) return payload.toString();\n if (typeof FormData !== 'undefined' && payload instanceof FormData) {\n const out: Record<string, unknown> = {};\n payload.forEach((v, k) => {\n out[k] = typeof v === 'string' ? v : '[file]';\n });\n return out;\n }\n return payload;\n}\n\nfunction normalizeRecordedPayload(\n payload: unknown,\n contentType: string,\n redactPattern: RegExp,\n maxPayloadBytes: number\n): unknown {\n if (payload === undefined) {\n // Raw-mode `RecordedCall.payload` is required by the 3.1 controller\n // response schema. GET/HEAD-style calls with no body therefore emit an\n // empty object instead of omitting the field.\n return {};\n }\n if (payload === null) return payload;\n\n // Binary-shaped bodies — replace with a marker. Adopters that need\n // the raw bytes recorded for diagnostics can stringify before\n // calling `record()`.\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(payload)) {\n return `[binary ${payload.length} bytes]`;\n }\n if (typeof Blob !== 'undefined' && payload instanceof Blob) {\n return `[binary ${payload.size} bytes]`;\n }\n if (payload instanceof ArrayBuffer) {\n return `[binary ${payload.byteLength} bytes]`;\n }\n if (ArrayBuffer.isView(payload)) {\n return `[binary ${payload.byteLength} bytes]`;\n }\n\n if (typeof payload === 'string') {\n if (isJsonContentType(contentType)) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(payload);\n } catch {\n // Malformed JSON strings remain raw strings so diagnostics keep the\n // original body shape instead of pretending the payload was absent.\n return cap(redactJsonLikeSecretValues(payload, redactPattern), maxPayloadBytes);\n }\n const redacted = redactSecrets(parsed, redactPattern);\n assertJsonDepth(redacted);\n const json = safeStringify(redacted);\n if (json && maxPayloadBytes > 0 && byteLengthOf(json) > maxPayloadBytes) {\n return `[truncated ${byteLengthOf(json)} bytes]`;\n }\n return redacted;\n }\n // Form-urlencoded redaction: parse, redact, re-stringify so\n // `access_token=...` bodies don't slip through unredacted.\n if (isFormUrlEncoded(contentType)) {\n return cap(redactFormUrlEncoded(payload, redactPattern), maxPayloadBytes);\n }\n return cap(payload, maxPayloadBytes);\n }\n\n const redacted = redactSecrets(payload, redactPattern);\n assertJsonDepth(redacted);\n const json = safeStringify(redacted);\n if (json && maxPayloadBytes > 0 && byteLengthOf(json) > maxPayloadBytes) {\n return `[truncated ${byteLengthOf(json)} bytes]`;\n }\n return redacted;\n}\n\n/**\n * Project a `recorder.query()` result onto the spec wire shape returned\n * by `comply_test_controller`'s `query_upstream_traffic` scenario\n * (`UpstreamTrafficSuccess` in `comply-test-controller-response.json`,\n * spec PR adcontextprotocol/adcp#3816). Eliminates the\n * `items → recorded_calls` / `total → total_count` field-rename footgun\n * — adopters return this directly:\n *\n * ```ts\n * scenarios: {\n * query_upstream_traffic: ({ params }, ctx) =>\n * toQueryUpstreamTrafficResponse(\n * recorder.query({\n * principal: resolvePrincipal(ctx),\n * sinceTimestamp: params?.since_timestamp,\n * endpointPattern: params?.endpoint_pattern,\n * limit: params?.limit,\n * })\n * ),\n * }\n * ```\n */\nexport function toQueryUpstreamTrafficResponse(result: UpstreamRecorderQueryResult): QueryUpstreamTrafficResponse {\n return {\n success: true,\n recorded_calls: result.items,\n total_count: result.total,\n truncated: result.truncated,\n since_timestamp: result.since_timestamp,\n };\n}\n\n/**\n * No-op recorder returned when `enabled: false`. Every method is a\n * pass-through / empty-result. Production builds drop in this object\n * with zero per-call overhead beyond the initial factory call.\n */\nfunction makeNoopRecorder(): UpstreamRecorder {\n return {\n runWithPrincipal: <T>(_p: string, fn: () => T | Promise<T>): Promise<T> => Promise.resolve(fn()),\n wrapFetch: (originalFetch: typeof globalThis.fetch): typeof globalThis.fetch => originalFetch,\n record: () => undefined,\n query: (params: UpstreamRecorderQueryParams): UpstreamRecorderQueryResult => ({\n items: [],\n total: 0,\n truncated: false,\n since_timestamp: params.sinceTimestamp ?? new Date(0).toISOString(),\n dropped_count: 0,\n }),\n clear: () => undefined,\n debug: (): UpstreamRecorderDebugInfo => ({\n enabled: false,\n bufferSize: 0,\n bufferedEntries: 0,\n principals: [],\n lastRecordedAt: null,\n activePrincipal: null,\n strict: false,\n }),\n enabled: false,\n };\n}\n\nlet warnedAboutProduction = false;\nfunction warnIfEnabledInProduction(): void {\n if (warnedAboutProduction) return;\n if (typeof process === 'undefined' || !process.env) return;\n if (process.env.NODE_ENV !== 'production') return;\n if (process.env.ADCP_RECORDER_PRODUCTION_ACK === '1') return;\n warnedAboutProduction = true;\n // eslint-disable-next-line no-console\n console.warn(\n '[adcp/upstream-recorder] enabled: true in NODE_ENV=production. ' +\n 'The recorder buffers outbound HTTP calls in-memory — sandbox-only by design. ' +\n 'Set enabled: false (recommended) or ADCP_RECORDER_PRODUCTION_ACK=1 to silence.'\n );\n}\n\n/**\n * Read the request body off a fetch call site without consuming the\n * input (when the input is a `Request`, we'd lock its stream by reading\n * directly). Returns `undefined` for bodies the recorder can't snapshot\n * cheaply.\n */\nasync function readBody(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n contentType: string\n): Promise<unknown> {\n let raw: unknown;\n\n if (init?.body !== undefined && init.body !== null) {\n raw = init.body;\n } else if (input instanceof Request) {\n try {\n const clone = input.clone();\n if (contentTypeBase(contentType) === 'multipart/form-data') {\n raw = await clone.formData();\n } else {\n raw = await clone.text();\n }\n } catch {\n raw = undefined;\n }\n } else {\n raw = undefined;\n }\n\n if (raw === undefined) return undefined;\n if (typeof raw === 'string') {\n if (!isJsonContentType(contentType)) return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n // FormData / URLSearchParams stringify cleanly enough for diagnostic value.\n if (raw instanceof URLSearchParams) return raw.toString();\n if (typeof FormData !== 'undefined' && raw instanceof FormData) {\n const out: Record<string, unknown> = {};\n raw.forEach((v, k) => {\n out[k] = typeof v === 'string' ? v : '[file]';\n });\n return out;\n }\n // Object / array literal already, or a Buffer / Blob / TypedArray /\n // ArrayBuffer the adopter passed in directly. Pass through — the\n // redaction step below detects binary types and substitutes markers\n // before they sit in the buffer.\n if (typeof raw === 'object') return raw;\n return String(raw);\n}\n\nfunction isJsonContentType(contentType: string | undefined): boolean {\n const base = contentTypeBase(contentType);\n return base === 'application/json' || /\\+json$/.test(base);\n}\n\nfunction isFormUrlEncoded(contentType: string | undefined): boolean {\n return contentTypeBase(contentType) === 'application/x-www-form-urlencoded';\n}\n\nfunction contentTypeBase(contentType: string | undefined): string {\n if (!contentType) return '';\n return contentType.split(';')[0]?.trim().toLowerCase() ?? '';\n}\n\nfunction redactFormUrlEncoded(body: string, pattern: RegExp): string {\n try {\n const params = new URLSearchParams(body);\n const out = new URLSearchParams();\n params.forEach((v, k) => {\n out.append(k, secretKeyPatternMatches(pattern, k.toLowerCase()) ? '[redacted]' : v);\n });\n return out.toString();\n } catch {\n return body;\n }\n}\n\nfunction redactJsonLikeSecretValues(body: string, pattern: RegExp): string {\n // Best-effort diagnostic scrub for malformed JSON strings. The output is\n // not guaranteed to be valid JSON; the invariant is that secret-shaped keys\n // with scalar/string values are not stored verbatim. Because malformed JSON\n // has no trustworthy parse tree, object/array-valued secret entries are\n // replaced wholesale rather than traversed.\n let out = '';\n let i = 0;\n while (i < body.length) {\n if (body[i] !== '\"') {\n out += body[i++];\n continue;\n }\n\n const keyToken = readJsonStringToken(body, i);\n if (!keyToken) {\n out += body.slice(i);\n break;\n }\n\n let colon = keyToken.end;\n while (colon < body.length && isJsonWhitespace(body[colon])) colon++;\n if (body[colon] !== ':') {\n out += body.slice(i, keyToken.end);\n i = keyToken.end;\n continue;\n }\n\n let key: unknown;\n try {\n key = JSON.parse(keyToken.literal);\n } catch {\n out += body.slice(i, keyToken.end);\n i = keyToken.end;\n continue;\n }\n\n const valueStart = colon + 1;\n let value = valueStart;\n while (value < body.length && isJsonWhitespace(body[value])) value++;\n out += body.slice(i, value);\n if (typeof key !== 'string' || !secretKeyPatternMatches(pattern, key)) {\n i = value;\n continue;\n }\n\n out += '\"[redacted]\"';\n i = skipJsonLikeValue(body, value);\n }\n return out;\n}\n\nfunction readJsonStringToken(input: string, start: number): { literal: string; end: number } | null {\n for (let i = start + 1; i < input.length; i++) {\n const ch = input[i];\n if (ch === '\"') return { literal: input.slice(start, i + 1), end: i + 1 };\n if (ch === '\\\\') i++;\n }\n return null;\n}\n\nfunction skipJsonLikeValue(input: string, start: number): number {\n if (input[start] === '\"') {\n const token = readJsonStringToken(input, start);\n return token?.end ?? input.length;\n }\n if (input[start] === '[' || input[start] === '{') {\n const opener = input[start];\n const closer = opener === '[' ? ']' : '}';\n let depth = 0;\n for (let i = start; i < input.length; i++) {\n const ch = input[i];\n if (ch === '\"') {\n const token = readJsonStringToken(input, i);\n if (!token) return input.length;\n i = token.end - 1;\n continue;\n }\n if (ch === opener) depth++;\n if (ch === closer) {\n depth--;\n if (depth === 0) return i + 1;\n }\n }\n return input.length;\n }\n let i = start;\n while (i < input.length && input[i] !== ',' && input[i] !== '}' && input[i] !== ']' && !isJsonWhitespace(input[i])) {\n i++;\n }\n return i;\n}\n\nfunction isJsonWhitespace(ch: string | undefined): boolean {\n return ch === ' ' || ch === '\\n' || ch === '\\r' || ch === '\\t';\n}\n\nfunction safeStringify(value: unknown): string | undefined {\n try {\n return JSON.stringify(value);\n } catch {\n return undefined;\n }\n}\n\nfunction byteLengthOf(s: string): number {\n if (typeof Buffer !== 'undefined') return Buffer.byteLength(s, 'utf8');\n // Fallback — rough UTF-16 count.\n return s.length;\n}\n\nfunction payloadWireLength(value: unknown): number {\n if (typeof value === 'string') return byteLengthOf(value);\n const json = safeStringify(value);\n return json ? byteLengthOf(json) : 0;\n}\n\nfunction cap(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return s;\n if (byteLengthOf(s) <= maxBytes) return s;\n return `[truncated ${byteLengthOf(s)} bytes]`;\n}\n"],"mappings":"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","names":["redacted","json","i"]}
@@ -1 +1 @@
1
- {"version":3,"file":"redact-secrets.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QACqK,CAAC;AAErM,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAM7E;AAED;;;;;GAKG;AACH;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,MAA2B,EACpC,KAAK,SAAI,EACT,IAAI,GAAE,OAAO,CAAC,MAAM,CAAiB,GACpC,OAAO,CAwBT"}
1
+ {"version":3,"file":"redact-secrets.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QACqK,CAAC;AAErM,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAM7E;AAED;;;;;GAKG;AACH;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,MAA2B,EACpC,KAAK,SAAI,EACT,IAAI,GAAE,OAAO,CAAC,MAAM,CAAiB,GACpC,OAAO,CAiCT"}
@@ -52,7 +52,13 @@ function redactSecrets(value, pattern = SECRET_KEY_PATTERN, depth = 0, seen = /*
52
52
  seen.add(value);
53
53
  const out = {};
54
54
  for (const [k, v] of Object.entries(value)) {
55
- out[k] = secretKeyPatternMatches(effectivePattern, k) && (typeof v === "string" || typeof v === "number") ? "[redacted]" : redactSecrets(v, effectivePattern, depth + 1, seen);
55
+ const redacted = secretKeyPatternMatches(effectivePattern, k) && (typeof v === "string" || typeof v === "number") ? "[redacted]" : redactSecrets(v, effectivePattern, depth + 1, seen);
56
+ Object.defineProperty(out, k, {
57
+ value: redacted,
58
+ enumerable: true,
59
+ configurable: true,
60
+ writable: true
61
+ });
56
62
  }
57
63
  seen.delete(value);
58
64
  return out;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/utils/redact-secrets.ts"],"sourcesContent":["/**\n * Recursive secret-key redaction.\n *\n * The runner-output-contract.yaml security block (`payload_redaction`) and\n * the `comply-test-controller-response.json` `recorded_calls[].payload`\n * description both mandate the same recursive walk: any property whose\n * final-path-segment key matches the canonical pattern below has its scalar\n * value replaced with the literal string `\"[redacted]\"`. The walk is capped\n * at the same depth the upstream-recorder accepts for JSON canonicalization;\n * the recorder rejects deeper structured payloads after redaction so it does\n * not store subtrees beyond this cap.\n *\n * Spec: `static/compliance/source/universal/runner-output-contract.yaml`\n * (`payload_redaction.pattern`). The runner uses this to redact request /\n * response payloads on `validation_result.request` / `.response`. The\n * `@adcp/sdk/upstream-recorder` uses this at *recording* time so plaintext\n * secrets never sit in the recorded-calls buffer in memory, even briefly.\n *\n * Matching is case-insensitive and structural — the pattern matches against\n * the property KEY, not the value. Adopters MAY extend the pattern via the\n * recorder's `redactPattern` option, but MUST NOT narrow it.\n */\n\nimport { MAX_JSON_DEPTH } from './json-depth';\n\n/**\n * Canonical secret-key pattern from the AdCP runner-output contract. Mirrors\n * the spec block: `Authorization`, `Credentials`, tokens, API keys,\n * passwords, secrets, OAuth refresh / access bearers, session tokens,\n * cookies. Case-insensitive.\n */\nexport const SECRET_KEY_PATTERN =\n /^(authorization|credentials?|token|api[_-]?key|password|secret|client[_-]secret|refresh[_-]token|access[_-]token|bearer|session[_-]token|session[_-]id|offering[_-]token|cookie|set[_-]cookie)$/i;\n\nexport function normalizeSecretKeyPattern(pattern: RegExp): RegExp {\n if (!/[gy]/.test(pattern.flags)) return pattern;\n return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ''));\n}\n\nexport function secretKeyPatternMatches(pattern: RegExp, key: string): boolean {\n const lastIndex = pattern.lastIndex;\n pattern.lastIndex = 0;\n const matched = pattern.test(key);\n pattern.lastIndex = lastIndex;\n return matched;\n}\n\n/**\n * Maximum recursion depth for the redaction walk. Keep this aligned with the\n * upstream-recorder JSON canonicalization depth; recorder paths also run the\n * depth gate after redaction so structured payloads beyond the cap are\n * rejected instead of stored with unvisited subtrees.\n */\n/**\n * Recursively walk `value`, returning a structurally-identical clone with\n * scalar values at secret-shaped keys replaced by `\"[redacted]\"`. Pass an\n * optional `pattern` to override the canonical pattern (typically by\n * extending it — adopters MAY add internal vendor headers but MUST NOT\n * narrow the contract floor).\n *\n * Non-mutating — the input is never touched.\n */\nexport function redactSecrets(\n value: unknown,\n pattern: RegExp = SECRET_KEY_PATTERN,\n depth = 0,\n seen: WeakSet<object> = new WeakSet()\n): unknown {\n const effectivePattern = normalizeSecretKeyPattern(pattern);\n if (depth > MAX_JSON_DEPTH) return value;\n if (Array.isArray(value)) {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out = value.map(v => redactSecrets(v, effectivePattern, depth + 1, seen));\n seen.delete(value);\n return out;\n }\n if (value && typeof value === 'object') {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] =\n secretKeyPatternMatches(effectivePattern, k) && (typeof v === 'string' || typeof v === 'number')\n ? '[redacted]'\n : redactSecrets(v, effectivePattern, depth + 1, seen);\n }\n seen.delete(value);\n return out;\n }\n return value;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBA,wBAA+B;AAQxB,MAAM,qBACX;AAEK,SAAS,0BAA0B,SAAyB;AACjE,MAAI,CAAC,OAAO,KAAK,QAAQ,KAAK,EAAG,QAAO;AACxC,SAAO,IAAI,OAAO,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,SAAS,EAAE,CAAC;AACtE;AAEO,SAAS,wBAAwB,SAAiB,KAAsB;AAC7E,QAAM,YAAY,QAAQ;AAC1B,UAAQ,YAAY;AACpB,QAAM,UAAU,QAAQ,KAAK,GAAG;AAChC,UAAQ,YAAY;AACpB,SAAO;AACT;AAiBO,SAAS,cACd,OACA,UAAkB,oBAClB,QAAQ,GACR,OAAwB,oBAAI,QAAQ,GAC3B;AACT,QAAM,mBAAmB,0BAA0B,OAAO;AAC1D,MAAI,QAAQ,iCAAgB,QAAO;AACnC,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,QAAI,KAAK,IAAI,KAAK,EAAG,QAAO;AAC5B,SAAK,IAAI,KAAK;AACd,UAAM,MAAM,MAAM,IAAI,OAAK,cAAc,GAAG,kBAAkB,QAAQ,GAAG,IAAI,CAAC;AAC9E,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,QAAI,KAAK,IAAI,KAAK,EAAG,QAAO;AAC5B,SAAK,IAAI,KAAK;AACd,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrE,UAAI,CAAC,IACH,wBAAwB,kBAAkB,CAAC,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,YACnF,eACA,cAAc,GAAG,kBAAkB,QAAQ,GAAG,IAAI;AAAA,IAC1D;AACA,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/utils/redact-secrets.ts"],"sourcesContent":["/**\n * Recursive secret-key redaction.\n *\n * The runner-output-contract.yaml security block (`payload_redaction`) and\n * the `comply-test-controller-response.json` `recorded_calls[].payload`\n * description both mandate the same recursive walk: any property whose\n * final-path-segment key matches the canonical pattern below has its scalar\n * value replaced with the literal string `\"[redacted]\"`. The walk is capped\n * at the same depth the upstream-recorder accepts for JSON canonicalization;\n * the recorder rejects deeper structured payloads after redaction so it does\n * not store subtrees beyond this cap.\n *\n * Spec: `static/compliance/source/universal/runner-output-contract.yaml`\n * (`payload_redaction.pattern`). The runner uses this to redact request /\n * response payloads on `validation_result.request` / `.response`. The\n * `@adcp/sdk/upstream-recorder` uses this at *recording* time so plaintext\n * secrets never sit in the recorded-calls buffer in memory, even briefly.\n *\n * Matching is case-insensitive and structural — the pattern matches against\n * the property KEY, not the value. Adopters MAY extend the pattern via the\n * recorder's `redactPattern` option, but MUST NOT narrow it.\n */\n\nimport { MAX_JSON_DEPTH } from './json-depth';\n\n/**\n * Canonical secret-key pattern from the AdCP runner-output contract. Mirrors\n * the spec block: `Authorization`, `Credentials`, tokens, API keys,\n * passwords, secrets, OAuth refresh / access bearers, session tokens,\n * cookies. Case-insensitive.\n */\nexport const SECRET_KEY_PATTERN =\n /^(authorization|credentials?|token|api[_-]?key|password|secret|client[_-]secret|refresh[_-]token|access[_-]token|bearer|session[_-]token|session[_-]id|offering[_-]token|cookie|set[_-]cookie)$/i;\n\nexport function normalizeSecretKeyPattern(pattern: RegExp): RegExp {\n if (!/[gy]/.test(pattern.flags)) return pattern;\n return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ''));\n}\n\nexport function secretKeyPatternMatches(pattern: RegExp, key: string): boolean {\n const lastIndex = pattern.lastIndex;\n pattern.lastIndex = 0;\n const matched = pattern.test(key);\n pattern.lastIndex = lastIndex;\n return matched;\n}\n\n/**\n * Maximum recursion depth for the redaction walk. Keep this aligned with the\n * upstream-recorder JSON canonicalization depth; recorder paths also run the\n * depth gate after redaction so structured payloads beyond the cap are\n * rejected instead of stored with unvisited subtrees.\n */\n/**\n * Recursively walk `value`, returning a structurally-identical clone with\n * scalar values at secret-shaped keys replaced by `\"[redacted]\"`. Pass an\n * optional `pattern` to override the canonical pattern (typically by\n * extending it — adopters MAY add internal vendor headers but MUST NOT\n * narrow the contract floor).\n *\n * Non-mutating — the input is never touched.\n */\nexport function redactSecrets(\n value: unknown,\n pattern: RegExp = SECRET_KEY_PATTERN,\n depth = 0,\n seen: WeakSet<object> = new WeakSet()\n): unknown {\n const effectivePattern = normalizeSecretKeyPattern(pattern);\n if (depth > MAX_JSON_DEPTH) return value;\n if (Array.isArray(value)) {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out = value.map(v => redactSecrets(v, effectivePattern, depth + 1, seen));\n seen.delete(value);\n return out;\n }\n if (value && typeof value === 'object') {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n const redacted =\n secretKeyPatternMatches(effectivePattern, k) && (typeof v === 'string' || typeof v === 'number')\n ? '[redacted]'\n : redactSecrets(v, effectivePattern, depth + 1, seen);\n // Define rather than assign so an own JSON key named `__proto__`\n // remains data instead of invoking Object.prototype's legacy setter.\n // Trusted Match provider ids explicitly exercise this boundary.\n Object.defineProperty(out, k, {\n value: redacted,\n enumerable: true,\n configurable: true,\n writable: true,\n });\n }\n seen.delete(value);\n return out;\n }\n return value;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBA,wBAA+B;AAQxB,MAAM,qBACX;AAEK,SAAS,0BAA0B,SAAyB;AACjE,MAAI,CAAC,OAAO,KAAK,QAAQ,KAAK,EAAG,QAAO;AACxC,SAAO,IAAI,OAAO,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,SAAS,EAAE,CAAC;AACtE;AAEO,SAAS,wBAAwB,SAAiB,KAAsB;AAC7E,QAAM,YAAY,QAAQ;AAC1B,UAAQ,YAAY;AACpB,QAAM,UAAU,QAAQ,KAAK,GAAG;AAChC,UAAQ,YAAY;AACpB,SAAO;AACT;AAiBO,SAAS,cACd,OACA,UAAkB,oBAClB,QAAQ,GACR,OAAwB,oBAAI,QAAQ,GAC3B;AACT,QAAM,mBAAmB,0BAA0B,OAAO;AAC1D,MAAI,QAAQ,iCAAgB,QAAO;AACnC,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,QAAI,KAAK,IAAI,KAAK,EAAG,QAAO;AAC5B,SAAK,IAAI,KAAK;AACd,UAAM,MAAM,MAAM,IAAI,OAAK,cAAc,GAAG,kBAAkB,QAAQ,GAAG,IAAI,CAAC;AAC9E,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,QAAI,KAAK,IAAI,KAAK,EAAG,QAAO;AAC5B,SAAK,IAAI,KAAK;AACd,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrE,YAAM,WACJ,wBAAwB,kBAAkB,CAAC,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,YACnF,eACA,cAAc,GAAG,kBAAkB,QAAQ,GAAG,IAAI;AAIxD,aAAO,eAAe,KAAK,GAAG;AAAA,QAC5B,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,cAAc;AAAA,QACd,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AACA,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
@@ -26,7 +26,13 @@ function redactSecrets(value, pattern = SECRET_KEY_PATTERN, depth = 0, seen = /*
26
26
  seen.add(value);
27
27
  const out = {};
28
28
  for (const [k, v] of Object.entries(value)) {
29
- out[k] = secretKeyPatternMatches(effectivePattern, k) && (typeof v === "string" || typeof v === "number") ? "[redacted]" : redactSecrets(v, effectivePattern, depth + 1, seen);
29
+ const redacted = secretKeyPatternMatches(effectivePattern, k) && (typeof v === "string" || typeof v === "number") ? "[redacted]" : redactSecrets(v, effectivePattern, depth + 1, seen);
30
+ Object.defineProperty(out, k, {
31
+ value: redacted,
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true
35
+ });
30
36
  }
31
37
  seen.delete(value);
32
38
  return out;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/utils/redact-secrets.ts"],"sourcesContent":["/**\n * Recursive secret-key redaction.\n *\n * The runner-output-contract.yaml security block (`payload_redaction`) and\n * the `comply-test-controller-response.json` `recorded_calls[].payload`\n * description both mandate the same recursive walk: any property whose\n * final-path-segment key matches the canonical pattern below has its scalar\n * value replaced with the literal string `\"[redacted]\"`. The walk is capped\n * at the same depth the upstream-recorder accepts for JSON canonicalization;\n * the recorder rejects deeper structured payloads after redaction so it does\n * not store subtrees beyond this cap.\n *\n * Spec: `static/compliance/source/universal/runner-output-contract.yaml`\n * (`payload_redaction.pattern`). The runner uses this to redact request /\n * response payloads on `validation_result.request` / `.response`. The\n * `@adcp/sdk/upstream-recorder` uses this at *recording* time so plaintext\n * secrets never sit in the recorded-calls buffer in memory, even briefly.\n *\n * Matching is case-insensitive and structural — the pattern matches against\n * the property KEY, not the value. Adopters MAY extend the pattern via the\n * recorder's `redactPattern` option, but MUST NOT narrow it.\n */\n\nimport { MAX_JSON_DEPTH } from './json-depth';\n\n/**\n * Canonical secret-key pattern from the AdCP runner-output contract. Mirrors\n * the spec block: `Authorization`, `Credentials`, tokens, API keys,\n * passwords, secrets, OAuth refresh / access bearers, session tokens,\n * cookies. Case-insensitive.\n */\nexport const SECRET_KEY_PATTERN =\n /^(authorization|credentials?|token|api[_-]?key|password|secret|client[_-]secret|refresh[_-]token|access[_-]token|bearer|session[_-]token|session[_-]id|offering[_-]token|cookie|set[_-]cookie)$/i;\n\nexport function normalizeSecretKeyPattern(pattern: RegExp): RegExp {\n if (!/[gy]/.test(pattern.flags)) return pattern;\n return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ''));\n}\n\nexport function secretKeyPatternMatches(pattern: RegExp, key: string): boolean {\n const lastIndex = pattern.lastIndex;\n pattern.lastIndex = 0;\n const matched = pattern.test(key);\n pattern.lastIndex = lastIndex;\n return matched;\n}\n\n/**\n * Maximum recursion depth for the redaction walk. Keep this aligned with the\n * upstream-recorder JSON canonicalization depth; recorder paths also run the\n * depth gate after redaction so structured payloads beyond the cap are\n * rejected instead of stored with unvisited subtrees.\n */\n/**\n * Recursively walk `value`, returning a structurally-identical clone with\n * scalar values at secret-shaped keys replaced by `\"[redacted]\"`. Pass an\n * optional `pattern` to override the canonical pattern (typically by\n * extending it — adopters MAY add internal vendor headers but MUST NOT\n * narrow the contract floor).\n *\n * Non-mutating — the input is never touched.\n */\nexport function redactSecrets(\n value: unknown,\n pattern: RegExp = SECRET_KEY_PATTERN,\n depth = 0,\n seen: WeakSet<object> = new WeakSet()\n): unknown {\n const effectivePattern = normalizeSecretKeyPattern(pattern);\n if (depth > MAX_JSON_DEPTH) return value;\n if (Array.isArray(value)) {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out = value.map(v => redactSecrets(v, effectivePattern, depth + 1, seen));\n seen.delete(value);\n return out;\n }\n if (value && typeof value === 'object') {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] =\n secretKeyPatternMatches(effectivePattern, k) && (typeof v === 'string' || typeof v === 'number')\n ? '[redacted]'\n : redactSecrets(v, effectivePattern, depth + 1, seen);\n }\n seen.delete(value);\n return out;\n }\n return value;\n}\n"],"mappings":"AAuBA,SAAS,sBAAsB;AAQxB,MAAM,qBACX;AAEK,SAAS,0BAA0B,SAAyB;AACjE,MAAI,CAAC,OAAO,KAAK,QAAQ,KAAK,EAAG,QAAO;AACxC,SAAO,IAAI,OAAO,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,SAAS,EAAE,CAAC;AACtE;AAEO,SAAS,wBAAwB,SAAiB,KAAsB;AAC7E,QAAM,YAAY,QAAQ;AAC1B,UAAQ,YAAY;AACpB,QAAM,UAAU,QAAQ,KAAK,GAAG;AAChC,UAAQ,YAAY;AACpB,SAAO;AACT;AAiBO,SAAS,cACd,OACA,UAAkB,oBAClB,QAAQ,GACR,OAAwB,oBAAI,QAAQ,GAC3B;AACT,QAAM,mBAAmB,0BAA0B,OAAO;AAC1D,MAAI,QAAQ,eAAgB,QAAO;AACnC,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,QAAI,KAAK,IAAI,KAAK,EAAG,QAAO;AAC5B,SAAK,IAAI,KAAK;AACd,UAAM,MAAM,MAAM,IAAI,OAAK,cAAc,GAAG,kBAAkB,QAAQ,GAAG,IAAI,CAAC;AAC9E,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,QAAI,KAAK,IAAI,KAAK,EAAG,QAAO;AAC5B,SAAK,IAAI,KAAK;AACd,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrE,UAAI,CAAC,IACH,wBAAwB,kBAAkB,CAAC,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,YACnF,eACA,cAAc,GAAG,kBAAkB,QAAQ,GAAG,IAAI;AAAA,IAC1D;AACA,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/utils/redact-secrets.ts"],"sourcesContent":["/**\n * Recursive secret-key redaction.\n *\n * The runner-output-contract.yaml security block (`payload_redaction`) and\n * the `comply-test-controller-response.json` `recorded_calls[].payload`\n * description both mandate the same recursive walk: any property whose\n * final-path-segment key matches the canonical pattern below has its scalar\n * value replaced with the literal string `\"[redacted]\"`. The walk is capped\n * at the same depth the upstream-recorder accepts for JSON canonicalization;\n * the recorder rejects deeper structured payloads after redaction so it does\n * not store subtrees beyond this cap.\n *\n * Spec: `static/compliance/source/universal/runner-output-contract.yaml`\n * (`payload_redaction.pattern`). The runner uses this to redact request /\n * response payloads on `validation_result.request` / `.response`. The\n * `@adcp/sdk/upstream-recorder` uses this at *recording* time so plaintext\n * secrets never sit in the recorded-calls buffer in memory, even briefly.\n *\n * Matching is case-insensitive and structural — the pattern matches against\n * the property KEY, not the value. Adopters MAY extend the pattern via the\n * recorder's `redactPattern` option, but MUST NOT narrow it.\n */\n\nimport { MAX_JSON_DEPTH } from './json-depth';\n\n/**\n * Canonical secret-key pattern from the AdCP runner-output contract. Mirrors\n * the spec block: `Authorization`, `Credentials`, tokens, API keys,\n * passwords, secrets, OAuth refresh / access bearers, session tokens,\n * cookies. Case-insensitive.\n */\nexport const SECRET_KEY_PATTERN =\n /^(authorization|credentials?|token|api[_-]?key|password|secret|client[_-]secret|refresh[_-]token|access[_-]token|bearer|session[_-]token|session[_-]id|offering[_-]token|cookie|set[_-]cookie)$/i;\n\nexport function normalizeSecretKeyPattern(pattern: RegExp): RegExp {\n if (!/[gy]/.test(pattern.flags)) return pattern;\n return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ''));\n}\n\nexport function secretKeyPatternMatches(pattern: RegExp, key: string): boolean {\n const lastIndex = pattern.lastIndex;\n pattern.lastIndex = 0;\n const matched = pattern.test(key);\n pattern.lastIndex = lastIndex;\n return matched;\n}\n\n/**\n * Maximum recursion depth for the redaction walk. Keep this aligned with the\n * upstream-recorder JSON canonicalization depth; recorder paths also run the\n * depth gate after redaction so structured payloads beyond the cap are\n * rejected instead of stored with unvisited subtrees.\n */\n/**\n * Recursively walk `value`, returning a structurally-identical clone with\n * scalar values at secret-shaped keys replaced by `\"[redacted]\"`. Pass an\n * optional `pattern` to override the canonical pattern (typically by\n * extending it — adopters MAY add internal vendor headers but MUST NOT\n * narrow the contract floor).\n *\n * Non-mutating — the input is never touched.\n */\nexport function redactSecrets(\n value: unknown,\n pattern: RegExp = SECRET_KEY_PATTERN,\n depth = 0,\n seen: WeakSet<object> = new WeakSet()\n): unknown {\n const effectivePattern = normalizeSecretKeyPattern(pattern);\n if (depth > MAX_JSON_DEPTH) return value;\n if (Array.isArray(value)) {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out = value.map(v => redactSecrets(v, effectivePattern, depth + 1, seen));\n seen.delete(value);\n return out;\n }\n if (value && typeof value === 'object') {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n const redacted =\n secretKeyPatternMatches(effectivePattern, k) && (typeof v === 'string' || typeof v === 'number')\n ? '[redacted]'\n : redactSecrets(v, effectivePattern, depth + 1, seen);\n // Define rather than assign so an own JSON key named `__proto__`\n // remains data instead of invoking Object.prototype's legacy setter.\n // Trusted Match provider ids explicitly exercise this boundary.\n Object.defineProperty(out, k, {\n value: redacted,\n enumerable: true,\n configurable: true,\n writable: true,\n });\n }\n seen.delete(value);\n return out;\n }\n return value;\n}\n"],"mappings":"AAuBA,SAAS,sBAAsB;AAQxB,MAAM,qBACX;AAEK,SAAS,0BAA0B,SAAyB;AACjE,MAAI,CAAC,OAAO,KAAK,QAAQ,KAAK,EAAG,QAAO;AACxC,SAAO,IAAI,OAAO,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,SAAS,EAAE,CAAC;AACtE;AAEO,SAAS,wBAAwB,SAAiB,KAAsB;AAC7E,QAAM,YAAY,QAAQ;AAC1B,UAAQ,YAAY;AACpB,QAAM,UAAU,QAAQ,KAAK,GAAG;AAChC,UAAQ,YAAY;AACpB,SAAO;AACT;AAiBO,SAAS,cACd,OACA,UAAkB,oBAClB,QAAQ,GACR,OAAwB,oBAAI,QAAQ,GAC3B;AACT,QAAM,mBAAmB,0BAA0B,OAAO;AAC1D,MAAI,QAAQ,eAAgB,QAAO;AACnC,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,QAAI,KAAK,IAAI,KAAK,EAAG,QAAO;AAC5B,SAAK,IAAI,KAAK;AACd,UAAM,MAAM,MAAM,IAAI,OAAK,cAAc,GAAG,kBAAkB,QAAQ,GAAG,IAAI,CAAC;AAC9E,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,QAAI,KAAK,IAAI,KAAK,EAAG,QAAO;AAC5B,SAAK,IAAI,KAAK;AACd,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrE,YAAM,WACJ,wBAAwB,kBAAkB,CAAC,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,YACnF,eACA,cAAc,GAAG,kBAAkB,QAAQ,GAAG,IAAI;AAIxD,aAAO,eAAe,KAAK,GAAG;AAAA,QAC5B,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,cAAc;AAAA,QACd,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AACA,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AdCP SDK library version
3
3
  */
4
- export declare const LIBRARY_VERSION = "13.0.0-rc.8";
4
+ export declare const LIBRARY_VERSION = "13.0.0-rc.9";
5
5
  /**
6
6
  * AdCP specification version this library is built for
7
7
  */
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
33
33
  * Full version information
34
34
  */
35
35
  export declare const VERSION_INFO: {
36
- readonly library: "13.0.0-rc.8";
36
+ readonly library: "13.0.0-rc.9";
37
37
  readonly adcp: "3.1.11";
38
38
  readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0-beta.1", "3.0-beta.1", "3.0-beta", "3.0.0-beta.3", "3.0-beta.3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11"];
39
- readonly generatedAt: "2026-08-07T09:34:50.924Z";
39
+ readonly generatedAt: "2026-08-08T09:38:19.413Z";
40
40
  };
41
41
  /**
42
42
  * Get the AdCP specification version this library is built for
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AdCP SDK library version
3
3
  */
4
- export declare const LIBRARY_VERSION = "13.0.0-rc.8";
4
+ export declare const LIBRARY_VERSION = "13.0.0-rc.9";
5
5
  /**
6
6
  * AdCP specification version this library is built for
7
7
  */
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
33
33
  * Full version information
34
34
  */
35
35
  export declare const VERSION_INFO: {
36
- readonly library: "13.0.0-rc.8";
36
+ readonly library: "13.0.0-rc.9";
37
37
  readonly adcp: "3.1.11";
38
38
  readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0-beta.1", "3.0-beta.1", "3.0-beta", "3.0.0-beta.3", "3.0-beta.3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11"];
39
- readonly generatedAt: "2026-08-07T09:34:50.924Z";
39
+ readonly generatedAt: "2026-08-08T09:38:19.413Z";
40
40
  };
41
41
  /**
42
42
  * Get the AdCP specification version this library is built for
@@ -31,7 +31,7 @@ __export(version_exports, {
31
31
  toReleasePrecisionVersion: () => toReleasePrecisionVersion
32
32
  });
33
33
  module.exports = __toCommonJS(version_exports);
34
- const LIBRARY_VERSION = "13.0.0-rc.8";
34
+ const LIBRARY_VERSION = "13.0.0-rc.9";
35
35
  const ADCP_VERSION = "3.1.11";
36
36
  const ADCP_MAJOR_VERSION = 3;
37
37
  const COMPATIBLE_ADCP_VERSIONS = [
@@ -72,10 +72,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
72
72
  "3.1.11"
73
73
  ];
74
74
  const VERSION_INFO = {
75
- library: "13.0.0-rc.8",
75
+ library: "13.0.0-rc.9",
76
76
  adcp: "3.1.11",
77
77
  compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
78
- generatedAt: "2026-08-07T09:34:50.924Z"
78
+ generatedAt: "2026-08-08T09:38:19.413Z"
79
79
  };
80
80
  function getAdcpVersion() {
81
81
  return ADCP_VERSION;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.8';\n\n/**\n * AdCP specification version this library is built for\n */\nexport const ADCP_VERSION = '3.1.11';\n\n/**\n * AdCP major version sent with every request (adcp_major_version field).\n * Sellers validate this against their supported versions and return\n * VERSION_UNSUPPORTED if the version is not in range.\n */\nexport const ADCP_MAJOR_VERSION = 3;\n\n/**\n * AdCP versions this library maintains backward compatibility with.\n *\n * Auto-derived from `ADCP_VERSION` by scripts/sync-version.ts. Do not edit\n * this list by hand; bumping the AdCP pin via `npm run sync-version`\n * extends it.\n */\nexport const COMPATIBLE_ADCP_VERSIONS = [\n 'v2.5',\n 'v2.6',\n 'v3',\n '3.0.0-beta.1',\n '3.0-beta.1',\n '3.0-beta',\n '3.0.0-beta.3',\n '3.0-beta.3',\n '3.0.0',\n '3.0',\n '3.0.1',\n '3.0.2',\n '3.0.3',\n '3.0.4',\n '3.0.5',\n '3.0.6',\n '3.0.7',\n '3.0.8',\n '3.0.9',\n '3.0.10',\n '3.0.11',\n '3.0.12',\n '3.1.0',\n '3.1',\n '3.1.1',\n '3.1.2',\n '3.1.3',\n '3.1.4',\n '3.1.5',\n '3.1.6',\n '3.1.7',\n '3.1.8',\n '3.1.9',\n '3.1.10',\n '3.1.11',\n] as const;\n\n/**\n * String literal union of every AdCP version the SDK formally supports.\n *\n * Used by the per-instance `adcpVersion` constructor option to give callers\n * autocomplete in editors. The intersection with `(string & {})` in the\n * config type preserves the escape hatch — any string is still accepted at\n * the type level — while the literal union surfaces canonical values first.\n */\nexport type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];\n\n/**\n * Full version information\n */\nexport const VERSION_INFO = {\n library: '13.0.0-rc.8',\n adcp: '3.1.11',\n compatibleVersions: COMPATIBLE_ADCP_VERSIONS,\n generatedAt: '2026-08-07T09:34:50.924Z',\n} as const;\n\n/**\n * Get the AdCP specification version this library is built for\n */\nexport function getAdcpVersion(): string {\n return ADCP_VERSION;\n}\n\n/**\n * Get the library version\n */\nexport function getLibraryVersion(): string {\n return LIBRARY_VERSION;\n}\n\n/**\n * Check if this library version is compatible with a given AdCP version\n */\nexport function isCompatibleWith(adcpVersion: string): boolean {\n return (COMPATIBLE_ADCP_VERSIONS as readonly string[]).includes(adcpVersion);\n}\n\n/**\n * Get all AdCP versions this library is compatible with\n */\nexport function getCompatibleVersions(): readonly string[] {\n return COMPATIBLE_ADCP_VERSIONS;\n}\n\n/**\n * Extract the major version number from an AdCP version string.\n *\n * Accepts:\n * - Semver: '3.0.0', '3.0.1', '3.1.0-beta.1' → 3\n * - Legacy aliases: 'v3' → 3, 'v2.5' / 'v2.6' → 2\n *\n * Returns NaN for unrecognized strings — callers should validate before passing.\n */\nexport function parseAdcpMajorVersion(version: string): number {\n const trimmed = version.trim();\n const semverLike = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const major = parseInt(semverLike.split('.')[0] ?? '', 10);\n return Number.isFinite(major) ? major : NaN;\n}\n\n/**\n * Normalize a full-semver AdCP version (`MAJOR.MINOR.PATCH[-prerelease]`) to\n * the release-precision form that AdCP 3.1+ requires on the wire:\n * `MAJOR.MINOR[-prerelease]` — the patch digit is dropped.\n *\n * Per the spec note on `adcp_version`: \"SDKs that read full-semver values\n * from bundle metadata (e.g. `ComplianceIndex.published_version =\n * \"3.1.0-beta.1\"`) MUST normalize to release-precision (`\"3.1-beta.1\"`)\n * before emitting on the wire — meta-field values are NOT valid wire\n * values.\" The wire regex (`^\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$`) rejects strings\n * with a patch digit.\n *\n * Behavior:\n * - `\"3.1.0-beta.7\"` → `\"3.1-beta.7\"`\n * - `\"3.1.0\"` → `\"3.1\"`\n * - `\"3.0.12\"` → `\"3.0\"`\n * - Already-release-precision input (`\"3.1\"`, `\"3.1-beta.7\"`) passes through\n * - Legacy aliases (`\"v2.5\"`, `\"v3\"`) pass through unchanged — the wire\n * regex doesn't accept them anyway; the v2.5 path uses\n * `adcp_major_version` instead of `adcp_version` for transport.\n * - Unrecognized strings pass through unchanged so callers can detect drift\n * via the wire validator rather than have it masked by this helper.\n */\nexport function toReleasePrecisionVersion(version: string): string {\n const trimmed = version.trim();\n // Pre-release form `MAJOR.MINOR.PATCH-prerelease` → `MAJOR.MINOR-prerelease`\n const semverMatch = trimmed.match(/^(\\d+)\\.(\\d+)\\.\\d+(-[A-Za-z0-9.-]+)?$/);\n if (semverMatch) {\n const [, major, minor, pre = ''] = semverMatch;\n return `${major}.${minor}${pre}`;\n }\n // Already release-precision (no patch digit). Includes `3.1`, `3.1-beta.7`.\n if (/^\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$/.test(trimmed)) return trimmed;\n // Legacy aliases (`v3`, `v2.5`, `v2.6`) and anything we don't recognize —\n // pass through so the wire validator can flag genuine drift.\n return version;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,MAAM,kBAAkB;AAKxB,MAAM,eAAe;AAOrB,MAAM,qBAAqB;AAS3B,MAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,MAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,oBAAoB;AAAA,EACpB,aAAa;AACf;AAKO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,iBAAiB,aAA8B;AAC7D,SAAQ,yBAA+C,SAAS,WAAW;AAC7E;AAKO,SAAS,wBAA2C;AACzD,SAAO;AACT;AAWO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,QAAM,QAAQ,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACzD,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;AAyBO,SAAS,0BAA0B,SAAyB;AACjE,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,cAAc,QAAQ,MAAM,uCAAuC;AACzE,MAAI,aAAa;AACf,UAAM,CAAC,EAAE,OAAO,OAAO,MAAM,EAAE,IAAI;AACnC,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,GAAG;AAAA,EAChC;AAEA,MAAI,+BAA+B,KAAK,OAAO,EAAG,QAAO;AAGzD,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.9';\n\n/**\n * AdCP specification version this library is built for\n */\nexport const ADCP_VERSION = '3.1.11';\n\n/**\n * AdCP major version sent with every request (adcp_major_version field).\n * Sellers validate this against their supported versions and return\n * VERSION_UNSUPPORTED if the version is not in range.\n */\nexport const ADCP_MAJOR_VERSION = 3;\n\n/**\n * AdCP versions this library maintains backward compatibility with.\n *\n * Auto-derived from `ADCP_VERSION` by scripts/sync-version.ts. Do not edit\n * this list by hand; bumping the AdCP pin via `npm run sync-version`\n * extends it.\n */\nexport const COMPATIBLE_ADCP_VERSIONS = [\n 'v2.5',\n 'v2.6',\n 'v3',\n '3.0.0-beta.1',\n '3.0-beta.1',\n '3.0-beta',\n '3.0.0-beta.3',\n '3.0-beta.3',\n '3.0.0',\n '3.0',\n '3.0.1',\n '3.0.2',\n '3.0.3',\n '3.0.4',\n '3.0.5',\n '3.0.6',\n '3.0.7',\n '3.0.8',\n '3.0.9',\n '3.0.10',\n '3.0.11',\n '3.0.12',\n '3.1.0',\n '3.1',\n '3.1.1',\n '3.1.2',\n '3.1.3',\n '3.1.4',\n '3.1.5',\n '3.1.6',\n '3.1.7',\n '3.1.8',\n '3.1.9',\n '3.1.10',\n '3.1.11',\n] as const;\n\n/**\n * String literal union of every AdCP version the SDK formally supports.\n *\n * Used by the per-instance `adcpVersion` constructor option to give callers\n * autocomplete in editors. The intersection with `(string & {})` in the\n * config type preserves the escape hatch — any string is still accepted at\n * the type level — while the literal union surfaces canonical values first.\n */\nexport type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];\n\n/**\n * Full version information\n */\nexport const VERSION_INFO = {\n library: '13.0.0-rc.9',\n adcp: '3.1.11',\n compatibleVersions: COMPATIBLE_ADCP_VERSIONS,\n generatedAt: '2026-08-08T09:38:19.413Z',\n} as const;\n\n/**\n * Get the AdCP specification version this library is built for\n */\nexport function getAdcpVersion(): string {\n return ADCP_VERSION;\n}\n\n/**\n * Get the library version\n */\nexport function getLibraryVersion(): string {\n return LIBRARY_VERSION;\n}\n\n/**\n * Check if this library version is compatible with a given AdCP version\n */\nexport function isCompatibleWith(adcpVersion: string): boolean {\n return (COMPATIBLE_ADCP_VERSIONS as readonly string[]).includes(adcpVersion);\n}\n\n/**\n * Get all AdCP versions this library is compatible with\n */\nexport function getCompatibleVersions(): readonly string[] {\n return COMPATIBLE_ADCP_VERSIONS;\n}\n\n/**\n * Extract the major version number from an AdCP version string.\n *\n * Accepts:\n * - Semver: '3.0.0', '3.0.1', '3.1.0-beta.1' → 3\n * - Legacy aliases: 'v3' → 3, 'v2.5' / 'v2.6' → 2\n *\n * Returns NaN for unrecognized strings — callers should validate before passing.\n */\nexport function parseAdcpMajorVersion(version: string): number {\n const trimmed = version.trim();\n const semverLike = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const major = parseInt(semverLike.split('.')[0] ?? '', 10);\n return Number.isFinite(major) ? major : NaN;\n}\n\n/**\n * Normalize a full-semver AdCP version (`MAJOR.MINOR.PATCH[-prerelease]`) to\n * the release-precision form that AdCP 3.1+ requires on the wire:\n * `MAJOR.MINOR[-prerelease]` — the patch digit is dropped.\n *\n * Per the spec note on `adcp_version`: \"SDKs that read full-semver values\n * from bundle metadata (e.g. `ComplianceIndex.published_version =\n * \"3.1.0-beta.1\"`) MUST normalize to release-precision (`\"3.1-beta.1\"`)\n * before emitting on the wire — meta-field values are NOT valid wire\n * values.\" The wire regex (`^\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$`) rejects strings\n * with a patch digit.\n *\n * Behavior:\n * - `\"3.1.0-beta.7\"` → `\"3.1-beta.7\"`\n * - `\"3.1.0\"` → `\"3.1\"`\n * - `\"3.0.12\"` → `\"3.0\"`\n * - Already-release-precision input (`\"3.1\"`, `\"3.1-beta.7\"`) passes through\n * - Legacy aliases (`\"v2.5\"`, `\"v3\"`) pass through unchanged — the wire\n * regex doesn't accept them anyway; the v2.5 path uses\n * `adcp_major_version` instead of `adcp_version` for transport.\n * - Unrecognized strings pass through unchanged so callers can detect drift\n * via the wire validator rather than have it masked by this helper.\n */\nexport function toReleasePrecisionVersion(version: string): string {\n const trimmed = version.trim();\n // Pre-release form `MAJOR.MINOR.PATCH-prerelease` → `MAJOR.MINOR-prerelease`\n const semverMatch = trimmed.match(/^(\\d+)\\.(\\d+)\\.\\d+(-[A-Za-z0-9.-]+)?$/);\n if (semverMatch) {\n const [, major, minor, pre = ''] = semverMatch;\n return `${major}.${minor}${pre}`;\n }\n // Already release-precision (no patch digit). Includes `3.1`, `3.1-beta.7`.\n if (/^\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$/.test(trimmed)) return trimmed;\n // Legacy aliases (`v3`, `v2.5`, `v2.6`) and anything we don't recognize —\n // pass through so the wire validator can flag genuine drift.\n return version;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,MAAM,kBAAkB;AAKxB,MAAM,eAAe;AAOrB,MAAM,qBAAqB;AAS3B,MAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,MAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,oBAAoB;AAAA,EACpB,aAAa;AACf;AAKO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,iBAAiB,aAA8B;AAC7D,SAAQ,yBAA+C,SAAS,WAAW;AAC7E;AAKO,SAAS,wBAA2C;AACzD,SAAO;AACT;AAWO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,QAAM,QAAQ,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACzD,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;AAyBO,SAAS,0BAA0B,SAAyB;AACjE,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,cAAc,QAAQ,MAAM,uCAAuC;AACzE,MAAI,aAAa;AACf,UAAM,CAAC,EAAE,OAAO,OAAO,MAAM,EAAE,IAAI;AACnC,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,GAAG;AAAA,EAChC;AAEA,MAAI,+BAA+B,KAAK,OAAO,EAAG,QAAO;AAGzD,SAAO;AACT;","names":[]}
@@ -1,4 +1,4 @@
1
- const LIBRARY_VERSION = "13.0.0-rc.8";
1
+ const LIBRARY_VERSION = "13.0.0-rc.9";
2
2
  const ADCP_VERSION = "3.1.11";
3
3
  const ADCP_MAJOR_VERSION = 3;
4
4
  const COMPATIBLE_ADCP_VERSIONS = [
@@ -39,10 +39,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
39
39
  "3.1.11"
40
40
  ];
41
41
  const VERSION_INFO = {
42
- library: "13.0.0-rc.8",
42
+ library: "13.0.0-rc.9",
43
43
  adcp: "3.1.11",
44
44
  compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
45
- generatedAt: "2026-08-07T09:34:50.924Z"
45
+ generatedAt: "2026-08-08T09:38:19.413Z"
46
46
  };
47
47
  function getAdcpVersion() {
48
48
  return ADCP_VERSION;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.8';\n\n/**\n * AdCP specification version this library is built for\n */\nexport const ADCP_VERSION = '3.1.11';\n\n/**\n * AdCP major version sent with every request (adcp_major_version field).\n * Sellers validate this against their supported versions and return\n * VERSION_UNSUPPORTED if the version is not in range.\n */\nexport const ADCP_MAJOR_VERSION = 3;\n\n/**\n * AdCP versions this library maintains backward compatibility with.\n *\n * Auto-derived from `ADCP_VERSION` by scripts/sync-version.ts. Do not edit\n * this list by hand; bumping the AdCP pin via `npm run sync-version`\n * extends it.\n */\nexport const COMPATIBLE_ADCP_VERSIONS = [\n 'v2.5',\n 'v2.6',\n 'v3',\n '3.0.0-beta.1',\n '3.0-beta.1',\n '3.0-beta',\n '3.0.0-beta.3',\n '3.0-beta.3',\n '3.0.0',\n '3.0',\n '3.0.1',\n '3.0.2',\n '3.0.3',\n '3.0.4',\n '3.0.5',\n '3.0.6',\n '3.0.7',\n '3.0.8',\n '3.0.9',\n '3.0.10',\n '3.0.11',\n '3.0.12',\n '3.1.0',\n '3.1',\n '3.1.1',\n '3.1.2',\n '3.1.3',\n '3.1.4',\n '3.1.5',\n '3.1.6',\n '3.1.7',\n '3.1.8',\n '3.1.9',\n '3.1.10',\n '3.1.11',\n] as const;\n\n/**\n * String literal union of every AdCP version the SDK formally supports.\n *\n * Used by the per-instance `adcpVersion` constructor option to give callers\n * autocomplete in editors. The intersection with `(string & {})` in the\n * config type preserves the escape hatch — any string is still accepted at\n * the type level — while the literal union surfaces canonical values first.\n */\nexport type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];\n\n/**\n * Full version information\n */\nexport const VERSION_INFO = {\n library: '13.0.0-rc.8',\n adcp: '3.1.11',\n compatibleVersions: COMPATIBLE_ADCP_VERSIONS,\n generatedAt: '2026-08-07T09:34:50.924Z',\n} as const;\n\n/**\n * Get the AdCP specification version this library is built for\n */\nexport function getAdcpVersion(): string {\n return ADCP_VERSION;\n}\n\n/**\n * Get the library version\n */\nexport function getLibraryVersion(): string {\n return LIBRARY_VERSION;\n}\n\n/**\n * Check if this library version is compatible with a given AdCP version\n */\nexport function isCompatibleWith(adcpVersion: string): boolean {\n return (COMPATIBLE_ADCP_VERSIONS as readonly string[]).includes(adcpVersion);\n}\n\n/**\n * Get all AdCP versions this library is compatible with\n */\nexport function getCompatibleVersions(): readonly string[] {\n return COMPATIBLE_ADCP_VERSIONS;\n}\n\n/**\n * Extract the major version number from an AdCP version string.\n *\n * Accepts:\n * - Semver: '3.0.0', '3.0.1', '3.1.0-beta.1' → 3\n * - Legacy aliases: 'v3' → 3, 'v2.5' / 'v2.6' → 2\n *\n * Returns NaN for unrecognized strings — callers should validate before passing.\n */\nexport function parseAdcpMajorVersion(version: string): number {\n const trimmed = version.trim();\n const semverLike = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const major = parseInt(semverLike.split('.')[0] ?? '', 10);\n return Number.isFinite(major) ? major : NaN;\n}\n\n/**\n * Normalize a full-semver AdCP version (`MAJOR.MINOR.PATCH[-prerelease]`) to\n * the release-precision form that AdCP 3.1+ requires on the wire:\n * `MAJOR.MINOR[-prerelease]` — the patch digit is dropped.\n *\n * Per the spec note on `adcp_version`: \"SDKs that read full-semver values\n * from bundle metadata (e.g. `ComplianceIndex.published_version =\n * \"3.1.0-beta.1\"`) MUST normalize to release-precision (`\"3.1-beta.1\"`)\n * before emitting on the wire — meta-field values are NOT valid wire\n * values.\" The wire regex (`^\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$`) rejects strings\n * with a patch digit.\n *\n * Behavior:\n * - `\"3.1.0-beta.7\"` → `\"3.1-beta.7\"`\n * - `\"3.1.0\"` → `\"3.1\"`\n * - `\"3.0.12\"` → `\"3.0\"`\n * - Already-release-precision input (`\"3.1\"`, `\"3.1-beta.7\"`) passes through\n * - Legacy aliases (`\"v2.5\"`, `\"v3\"`) pass through unchanged — the wire\n * regex doesn't accept them anyway; the v2.5 path uses\n * `adcp_major_version` instead of `adcp_version` for transport.\n * - Unrecognized strings pass through unchanged so callers can detect drift\n * via the wire validator rather than have it masked by this helper.\n */\nexport function toReleasePrecisionVersion(version: string): string {\n const trimmed = version.trim();\n // Pre-release form `MAJOR.MINOR.PATCH-prerelease` → `MAJOR.MINOR-prerelease`\n const semverMatch = trimmed.match(/^(\\d+)\\.(\\d+)\\.\\d+(-[A-Za-z0-9.-]+)?$/);\n if (semverMatch) {\n const [, major, minor, pre = ''] = semverMatch;\n return `${major}.${minor}${pre}`;\n }\n // Already release-precision (no patch digit). Includes `3.1`, `3.1-beta.7`.\n if (/^\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$/.test(trimmed)) return trimmed;\n // Legacy aliases (`v3`, `v2.5`, `v2.6`) and anything we don't recognize —\n // pass through so the wire validator can flag genuine drift.\n return version;\n}\n"],"mappings":"AAMO,MAAM,kBAAkB;AAKxB,MAAM,eAAe;AAOrB,MAAM,qBAAqB;AAS3B,MAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,MAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,oBAAoB;AAAA,EACpB,aAAa;AACf;AAKO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,iBAAiB,aAA8B;AAC7D,SAAQ,yBAA+C,SAAS,WAAW;AAC7E;AAKO,SAAS,wBAA2C;AACzD,SAAO;AACT;AAWO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,QAAM,QAAQ,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACzD,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;AAyBO,SAAS,0BAA0B,SAAyB;AACjE,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,cAAc,QAAQ,MAAM,uCAAuC;AACzE,MAAI,aAAa;AACf,UAAM,CAAC,EAAE,OAAO,OAAO,MAAM,EAAE,IAAI;AACnC,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,GAAG;AAAA,EAChC;AAEA,MAAI,+BAA+B,KAAK,OAAO,EAAG,QAAO;AAGzD,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.9';\n\n/**\n * AdCP specification version this library is built for\n */\nexport const ADCP_VERSION = '3.1.11';\n\n/**\n * AdCP major version sent with every request (adcp_major_version field).\n * Sellers validate this against their supported versions and return\n * VERSION_UNSUPPORTED if the version is not in range.\n */\nexport const ADCP_MAJOR_VERSION = 3;\n\n/**\n * AdCP versions this library maintains backward compatibility with.\n *\n * Auto-derived from `ADCP_VERSION` by scripts/sync-version.ts. Do not edit\n * this list by hand; bumping the AdCP pin via `npm run sync-version`\n * extends it.\n */\nexport const COMPATIBLE_ADCP_VERSIONS = [\n 'v2.5',\n 'v2.6',\n 'v3',\n '3.0.0-beta.1',\n '3.0-beta.1',\n '3.0-beta',\n '3.0.0-beta.3',\n '3.0-beta.3',\n '3.0.0',\n '3.0',\n '3.0.1',\n '3.0.2',\n '3.0.3',\n '3.0.4',\n '3.0.5',\n '3.0.6',\n '3.0.7',\n '3.0.8',\n '3.0.9',\n '3.0.10',\n '3.0.11',\n '3.0.12',\n '3.1.0',\n '3.1',\n '3.1.1',\n '3.1.2',\n '3.1.3',\n '3.1.4',\n '3.1.5',\n '3.1.6',\n '3.1.7',\n '3.1.8',\n '3.1.9',\n '3.1.10',\n '3.1.11',\n] as const;\n\n/**\n * String literal union of every AdCP version the SDK formally supports.\n *\n * Used by the per-instance `adcpVersion` constructor option to give callers\n * autocomplete in editors. The intersection with `(string & {})` in the\n * config type preserves the escape hatch — any string is still accepted at\n * the type level — while the literal union surfaces canonical values first.\n */\nexport type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];\n\n/**\n * Full version information\n */\nexport const VERSION_INFO = {\n library: '13.0.0-rc.9',\n adcp: '3.1.11',\n compatibleVersions: COMPATIBLE_ADCP_VERSIONS,\n generatedAt: '2026-08-08T09:38:19.413Z',\n} as const;\n\n/**\n * Get the AdCP specification version this library is built for\n */\nexport function getAdcpVersion(): string {\n return ADCP_VERSION;\n}\n\n/**\n * Get the library version\n */\nexport function getLibraryVersion(): string {\n return LIBRARY_VERSION;\n}\n\n/**\n * Check if this library version is compatible with a given AdCP version\n */\nexport function isCompatibleWith(adcpVersion: string): boolean {\n return (COMPATIBLE_ADCP_VERSIONS as readonly string[]).includes(adcpVersion);\n}\n\n/**\n * Get all AdCP versions this library is compatible with\n */\nexport function getCompatibleVersions(): readonly string[] {\n return COMPATIBLE_ADCP_VERSIONS;\n}\n\n/**\n * Extract the major version number from an AdCP version string.\n *\n * Accepts:\n * - Semver: '3.0.0', '3.0.1', '3.1.0-beta.1' → 3\n * - Legacy aliases: 'v3' → 3, 'v2.5' / 'v2.6' → 2\n *\n * Returns NaN for unrecognized strings — callers should validate before passing.\n */\nexport function parseAdcpMajorVersion(version: string): number {\n const trimmed = version.trim();\n const semverLike = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const major = parseInt(semverLike.split('.')[0] ?? '', 10);\n return Number.isFinite(major) ? major : NaN;\n}\n\n/**\n * Normalize a full-semver AdCP version (`MAJOR.MINOR.PATCH[-prerelease]`) to\n * the release-precision form that AdCP 3.1+ requires on the wire:\n * `MAJOR.MINOR[-prerelease]` — the patch digit is dropped.\n *\n * Per the spec note on `adcp_version`: \"SDKs that read full-semver values\n * from bundle metadata (e.g. `ComplianceIndex.published_version =\n * \"3.1.0-beta.1\"`) MUST normalize to release-precision (`\"3.1-beta.1\"`)\n * before emitting on the wire — meta-field values are NOT valid wire\n * values.\" The wire regex (`^\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$`) rejects strings\n * with a patch digit.\n *\n * Behavior:\n * - `\"3.1.0-beta.7\"` → `\"3.1-beta.7\"`\n * - `\"3.1.0\"` → `\"3.1\"`\n * - `\"3.0.12\"` → `\"3.0\"`\n * - Already-release-precision input (`\"3.1\"`, `\"3.1-beta.7\"`) passes through\n * - Legacy aliases (`\"v2.5\"`, `\"v3\"`) pass through unchanged — the wire\n * regex doesn't accept them anyway; the v2.5 path uses\n * `adcp_major_version` instead of `adcp_version` for transport.\n * - Unrecognized strings pass through unchanged so callers can detect drift\n * via the wire validator rather than have it masked by this helper.\n */\nexport function toReleasePrecisionVersion(version: string): string {\n const trimmed = version.trim();\n // Pre-release form `MAJOR.MINOR.PATCH-prerelease` → `MAJOR.MINOR-prerelease`\n const semverMatch = trimmed.match(/^(\\d+)\\.(\\d+)\\.\\d+(-[A-Za-z0-9.-]+)?$/);\n if (semverMatch) {\n const [, major, minor, pre = ''] = semverMatch;\n return `${major}.${minor}${pre}`;\n }\n // Already release-precision (no patch digit). Includes `3.1`, `3.1-beta.7`.\n if (/^\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$/.test(trimmed)) return trimmed;\n // Legacy aliases (`v3`, `v2.5`, `v2.6`) and anything we don't recognize —\n // pass through so the wire validator can flag genuine drift.\n return version;\n}\n"],"mappings":"AAMO,MAAM,kBAAkB;AAKxB,MAAM,eAAe;AAOrB,MAAM,qBAAqB;AAS3B,MAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,MAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,oBAAoB;AAAA,EACpB,aAAa;AACf;AAKO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,iBAAiB,aAA8B;AAC7D,SAAQ,yBAA+C,SAAS,WAAW;AAC7E;AAKO,SAAS,wBAA2C;AACzD,SAAO;AACT;AAWO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,QAAM,QAAQ,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACzD,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;AAyBO,SAAS,0BAA0B,SAAyB;AACjE,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,cAAc,QAAQ,MAAM,uCAAuC;AACzE,MAAI,aAAa;AACf,UAAM,CAAC,EAAE,OAAO,OAAO,MAAM,EAAE,IAAI;AACnC,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,GAAG;AAAA,EAChC;AAEA,MAAI,+BAA+B,KAAK,OAAO,EAAG,QAAO;AAGzD,SAAO;AACT;","names":[]}
@@ -21,7 +21,9 @@ interfaces: `SalesCorePlatform` + `SalesIngestionPlatform`,
21
21
  `CampaignGovernancePlatform`, `BrandRightsPlatform`, etc.) and the
22
22
  framework wires capability projection, idempotency, RFC 9421 signing,
23
23
  async tasks, status normalization, lifecycle state, multi-tenant
24
- routing, and webhook auto-emit on sync mutations. Compile-time
24
+ routing, and async task completion webhooks. Synchronous terminal
25
+ responses stay inline unless an adopter explicitly enables the
26
+ non-conformant `autoEmitCompletionWebhooks` compatibility option. Compile-time
25
27
  enforcement via `RequiredPlatformsFor<S>` catches missing methods
26
28
  before runtime. The `definePlatform` / `defineSalesCorePlatform` /
27
29
  sibling helpers let you write inline platform literals without
@@ -161,7 +163,7 @@ serve(() => createAdcpServerFromPlatform(platform, { name: 'My Publisher', versi
161
163
  - **Auto-generates `get_adcp_capabilities`** from registered platform methods — no manual capability declaration.
162
164
  - **Auto-applies response builders** — return raw data, the framework wraps them in MCP `CallToolResult` with `structuredContent`.
163
165
  - **Resolves accounts** — `accounts.resolve(ref, ctx)` runs before your platform method, the resolved account lands at `ctx.account`. Returns `ACCOUNT_NOT_FOUND` envelope if resolution returns null. `accounts.resolution: 'implicit'` enforces inline-`{account_id}` refusal at the framework boundary (post-6.7 — pre-6.7 the docstring was aspirational).
164
- - **Idempotency, signing, async tasks, status normalization, lifecycle state** are framework-owned. Adopters write the business decisions.
166
+ - **Idempotency, signing, async tasks, status normalization, lifecycle state** are framework-owned. Synchronous terminal responses do not emit completion webhooks by default; the inline result is authoritative. Adopters write the business decisions.
165
167
  - **Catches handler errors** — unhandled exceptions return `SERVICE_UNAVAILABLE` instead of crashing. Throw a typed error class (see § "Returning errors from handlers") to surface a structured envelope.
166
168
 
167
169
  ### Identity, multi-tenant, and lifecycle helpers (6.7)
package/docs/llms.txt CHANGED
@@ -44,7 +44,7 @@ serve(() => createAdcpServerFromPlatform(platform, {
44
44
  })); // http://localhost:3001/mcp
45
45
  ```
46
46
 
47
- Compile-time enforcement: `RequiredPlatformsFor<S>` catches missing specialism methods. Capability projection auto-derives `get_adcp_capabilities` blocks (`audience_targeting`, `conversion_tracking`, `compliance_testing.scenarios`, etc.). Idempotency, RFC 9421 signing, async tasks, status normalization, and sync-completion webhook auto-emit are framework-owned.
47
+ Compile-time enforcement: `RequiredPlatformsFor<S>` catches missing specialism methods. Capability projection auto-derives `get_adcp_capabilities` blocks (`audience_targeting`, `conversion_tracking`, `compliance_testing.scenarios`, etc.). Idempotency, RFC 9421 signing, async tasks, and status normalization are framework-owned. Synchronous terminal responses do not emit completion webhooks by default; `autoEmitCompletionWebhooks: true` is available only as a non-conformant compatibility extension.
48
48
 
49
49
  Lower-level option: `createAdcpServer({ signals: { getSignals: ... } })` from `@adcp/sdk/server/legacy/v5` — handler-bag API. Still fully supported, the substrate the platform path calls into. Use when you need fine control over individual handlers, mid-migration from a v5 codebase, or custom-shaped tools the platform interface doesn't yet model. `wrapEnvelope(inner, { replayed, context, operationId })` from `@adcp/sdk/server` attaches protocol envelope fields with the per-error-code allowlist (IDEMPOTENCY_CONFLICT drops `replayed`).
50
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcp/sdk",
3
- "version": "13.0.0-rc.8",
3
+ "version": "13.0.0-rc.9",
4
4
  "description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)",
5
5
  "workspaces": [
6
6
  ".",
@@ -577,7 +577,7 @@
577
577
  "fast-check": "^3.23.2",
578
578
  "jose": "^6.2.2",
579
579
  "secure-json-parse": "^4.1.0",
580
- "structured-headers": "^2.0.2",
580
+ "structured-headers": "2.0.2",
581
581
  "tldts": "^7.0.29",
582
582
  "undici": "^6.27.0",
583
583
  "ws": "^8.21.0",