{"version":3,"sources":["../src/index.ts","../src/core/shared.ts","../src/core/errors.ts","../src/core/encoding.ts","../src/core/signing.ts","../src/core/cipher.ts","../src/core/retry.ts","../src/core/state.ts","../src/core/ssrf.ts","../src/core/config.ts","../src/core/payload.ts","../src/id.ts","../src/store/encrypted-store.ts","../src/store/endpoint-cache.ts","../src/delivery/http.ts","../src/delivery/_error.ts","../src/delivery/critical.ts","../src/delivery/deliver.ts","../src/dispatcher/dispatcher.ts","../src/store/sql/constants.ts","../src/admin/admin.ts","../src/relay.ts"],"sourcesContent":["/**\n * `commitcourier` main entry.\n *\n * Re-exports the pure core public API and the store port/DTO types. Driver adapters are\n * published under the subpath exports `commitcourier/store/pg` and `commitcourier/store/knex` so that\n * importing this entry never pulls a specific DB driver into scope.\n */\nexport * from \"./core/index\";\nexport type {\n  // The composed persistence port, plus its capability roles (interface segregation): an adapter\n  // implements all of them via `Store`; a consumer can depend on just the role(s) it needs.\n  Store,\n  OutboxEnqueueStore,\n  DispatchStore,\n  EndpointStore,\n  OutboxQueryStore,\n  ReplayStore,\n  MaintenanceStore,\n  SchemaStore,\n  NewOutboxRow,\n  NewDeliveryAttempt,\n  NewEndpointRow,\n  EndpointPatch,\n  OutboxStats,\n  ReplayFilter,\n  OutboxListItem,\n  OutboxListFilter,\n  EndpointSummary,\n  EndpointListFilter,\n  Page,\n} from \"./store/store\";\n\nexport { createRelay } from \"./relay\";\nexport type { Relay, RelayInit, EndpointAdmin, RegisterEndpointInput } from \"./relay\";\nexport type { PruneOptions } from \"./admin/admin\";\nexport type {\n  DeliveryHooks,\n  DeliveryEvent,\n  DeliveryStart,\n  DeliveryInstrument,\n} from \"./delivery/deliver\";\nexport type { Dispatcher, DispatcherOptions, RunOnceOptions } from \"./dispatcher/dispatcher\";\nexport type { Accelerator, WakeSignal } from \"./accelerator/accelerator\";\n// `sink` transport port abstraction; referenced by `RelayInit.sink`. The Svix sample\n// adapter ships under the `commitcourier/forward/svix` subpath so its optional peer never loads here.\nexport type { Sink, SinkEvent, SinkResult } from \"./forward/index\";\n","/**\n * Cross-cutting primitive types shared across all modules.\n *\n * Kept in a separate file from {@link \"./types\"} so that lower-level helpers can\n * depend on these primitives without creating an import cycle.\n */\n\n/** Injectable clock. Can be fixed in tests. Defaults to `() => new Date()`. */\nexport type Clock = () => Date;\n\n/**\n * Injectable logger. Defaults to a no-op, so routine operational logs (delivery failures, retries) are\n * silent unless one is provided. The two critical categories — security (SSRF blocks) and data loss\n * (DLQ transitions) — fall back to the `console` even with no logger configured, so they are never\n * silent; pass a logger (e.g. {@link createConsoleLogger}) to capture everything.\n *\n * Independent of the delivery instrumentation seam: tracing/metrics go through `RelayInit.instrument`\n * / `hooks` and the optional `commitcourier/otel` adapter, not this logger.\n */\nexport interface Logger {\n  debug(msg: string, meta?: Record<string, unknown>): void;\n  info(msg: string, meta?: Record<string, unknown>): void;\n  warn(msg: string, meta?: Record<string, unknown>): void;\n  error(msg: string, meta?: Record<string, unknown>): void;\n}\n\n/**\n * A ready-made {@link Logger} backed by the `console` global — a safe, copy-paste default so a relay\n * is not silent in production. Because delivery is fail-open, an unset logger swallows routine delivery\n * failures and retries (DLQ transitions and SSRF blocks still fall back to the console); passing this\n * (or any `Logger`) makes every operational log observable.\n *\n * Messages are prefixed with `commitcourier`; `debug`/`info`/`warn`/`error` map to the matching console\n * method (`debug` falls back to `console.log`). The library never logs signing secrets, so this is safe\n * to wire directly. For structured logging, adapt your own logger to the {@link Logger} interface instead.\n */\nexport function createConsoleLogger(prefix = \"commitcourier\"): Logger {\n  const emit =\n    (method: \"log\" | \"info\" | \"warn\" | \"error\") =>\n    (msg: string, meta?: Record<string, unknown>): void => {\n      if (meta === undefined) console[method](`[${prefix}] ${msg}`);\n      else console[method](`[${prefix}] ${msg}`, meta);\n    };\n  return {\n    debug: emit(\"log\"),\n    info: emit(\"info\"),\n    warn: emit(\"warn\"),\n    error: emit(\"error\"),\n  };\n}\n\n/** Lifecycle status of an outbox row. */\nexport type Status = \"pending\" | \"in_flight\" | \"delivered\" | \"dead\" | \"observed\" | \"cancelled\";\n\n/** Delivery mode. `observe` records without ever sending (staged rollout). */\nexport type Mode = \"observe\" | \"active\";\n","/**\n * Error model.\n *\n * Every error thrown by the library extends {@link RelayError} and carries a stable,\n * machine-readable {@link RelayErrorCode}.\n */\n\n/** Stable, machine-readable error codes. */\nexport type RelayErrorCode =\n  | \"CONFIG_INVALID\" // config validation failed (fail-fast at startup)\n  | \"MISSING_TABLES\" // tables missing per diagnose\n  | \"SSRF_BLOCKED\" // destination is in a blocked range\n  | \"ENDPOINT_NOT_FOUND\" // endpointId not registered\n  | \"ENDPOINT_DISABLED\" // endpoint is disabled\n  | \"MISSING_SECRET\" // inline target has no secret snapshot to sign with\n  | \"ENQUEUE_NO_TARGET\" // neither url nor endpointId provided\n  | \"ENQUEUE_INVALID_PAYLOAD\" // payload is not JSON-serializable, or exceeds maxPayloadBytes\n  | \"INVALID_ARGUMENT\"; // an admin-call argument failed validation (e.g. a malformed list cursor)\n\n/** Base error for all library failures. */\nexport class RelayError extends Error {\n  constructor(\n    readonly code: RelayErrorCode,\n    message: string,\n    override readonly cause?: unknown,\n  ) {\n    super(message);\n    this.name = \"RelayError\";\n  }\n}\n","/**\n * Pure UTF-8 / Base64 conversion.\n *\n * Uses only Web standard globals (`TextEncoder` / `atob` / `btoa`) so that core stays\n * cross-runtime and import-free. `Buffer` is intentionally not used.\n */\n\nconst _enc = /* @__PURE__ */ new TextEncoder();\nconst _dec = /* @__PURE__ */ new TextDecoder();\n\n/**\n * Encode a string to its UTF-8 bytes.\n *\n * Typed as `Uint8Array<ArrayBuffer>` (not the generic `ArrayBufferLike` default) so the result\n * is assignable to WebCrypto's `BufferSource`, which requires an `ArrayBuffer`-backed view.\n */\nexport function utf8ToBytes(s: string): Uint8Array<ArrayBuffer> {\n  return _enc.encode(s);\n}\n\n/** Decode UTF-8 bytes back to a string. */\nexport function bytesToUtf8(bytes: Uint8Array): string {\n  return _dec.decode(bytes);\n}\n\n/** Decode a standard (non-URL-safe) Base64 string to bytes. */\nexport function base64ToBytes(b64: string): Uint8Array<ArrayBuffer> {\n  const bin = atob(b64);\n  const out = new Uint8Array(bin.length);\n  for (let i = 0; i < bin.length; i++) {\n    out[i] = bin.charCodeAt(i);\n  }\n  return out;\n}\n\n/** Encode bytes to a standard (non-URL-safe) Base64 string. */\nexport function bytesToBase64(bytes: Uint8Array): string {\n  let bin = \"\";\n  for (const b of bytes) {\n    bin += String.fromCharCode(b);\n  }\n  return btoa(bin);\n}\n","/**\n * Standard Webhooks signing.\n *\n * Signed content is `{id}.{timestamp}.{body}`, HMAC-SHA256, Base64-encoded.\n * Implemented with the WebCrypto global (`crypto.subtle`) so core stays cross-runtime\n * and import-free; `node:crypto` and `Buffer` are intentionally not used. As a result\n * {@link sign} is async (delivery is already async, so the impact is minimal).\n */\nimport { base64ToBytes, bytesToBase64, utf8ToBytes } from \"./encoding\";\nimport { RelayError } from \"./errors\";\n\n/** Standard Webhooks signature headers. */\nexport interface SignatureHeaders {\n  \"webhook-id\": string;\n  /** Unix seconds, as a string. */\n  \"webhook-timestamp\": string;\n  /** One or more space-separated `\"v1,<base64>\"` signatures (more than one during key rotation). */\n  \"webhook-signature\": string;\n}\n\nconst WHSEC_PREFIX = \"whsec_\";\n\n/**\n * Derive the raw HMAC key bytes from the secret. A `whsec_`-prefixed secret is Base64-decoded;\n * otherwise the raw UTF-8 bytes are used. A malformed Base64 key is a misconfiguration, so the\n * underlying decode error is normalised to a {@link RelayError} rather than a raw `DOMException`.\n */\nfunction decodeSecret(secret: string): Uint8Array<ArrayBuffer> {\n  if (!secret.startsWith(WHSEC_PREFIX)) {\n    return utf8ToBytes(secret);\n  }\n  try {\n    return base64ToBytes(secret.slice(WHSEC_PREFIX.length));\n  } catch (cause) {\n    throw new RelayError(\n      \"CONFIG_INVALID\",\n      \"signing secret is not valid base64 (expected a whsec_-prefixed base64 key)\",\n      cause,\n    );\n  }\n}\n\n/**\n * Process-wide, bounded LRU memo of `secret -> Promise<CryptoKey>`. Importing the HMAC key is the\n * costly part of signing and is pure for a given secret, so caching it removes a `crypto.subtle.importKey`\n * from every delivery (and from each key during a dual-signing rotation) — a CPU win at high throughput.\n * This is a referentially-transparent memo, not behavioural state: the signature output is unchanged.\n * Keys are non-extractable (`extractable: false`), so the cache never exposes key material beyond what is\n * already in memory, and the size bound keeps it from growing without limit as endpoints/rotations churn.\n */\nconst KEY_CACHE_MAX = 1024;\nconst keyCache = new Map<string, Promise<CryptoKey>>();\n\n/** Resolve the HMAC `CryptoKey` for a secret, importing once and memoising (LRU, rejection-safe). */\nfunction importHmacKey(secret: string): Promise<CryptoKey> {\n  const hit = keyCache.get(secret);\n  if (hit) {\n    // LRU touch: re-insert so this secret is the most-recently used (Map preserves insertion order).\n    keyCache.delete(secret);\n    keyCache.set(secret, hit);\n    return hit;\n  }\n  // decodeSecret is synchronous and may throw CONFIG_INVALID for a malformed whsec_ key; let that\n  // propagate before anything is cached (a bad secret is never memoised).\n  const keyBytes = decodeSecret(secret);\n  const promise = crypto.subtle\n    .importKey(\"raw\", keyBytes, { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"])\n    .catch((err: unknown) => {\n      // Never poison the cache with a rejected import; a later call retries from scratch.\n      keyCache.delete(secret);\n      throw err;\n    });\n  keyCache.set(secret, promise);\n  if (keyCache.size > KEY_CACHE_MAX) {\n    const oldest = keyCache.keys().next().value;\n    if (oldest !== undefined) keyCache.delete(oldest);\n  }\n  return promise;\n}\n\n/** Compute a single `v1,<base64>` HMAC-SHA256 signature over the signed content. */\nasync function signOne(secret: string, signedContent: Uint8Array<ArrayBuffer>): Promise<string> {\n  const key = await importHmacKey(secret);\n  const mac = await crypto.subtle.sign(\"HMAC\", key, signedContent);\n  return `v1,${bytesToBase64(new Uint8Array(mac))}`;\n}\n\n/**\n * Sign a webhook per the Standard Webhooks convention.\n *\n * `secrets` carries one or more keys (more than one during a key rotation): each produces a\n * `v1,<base64>` signature and they are joined with a space in `webhook-signature`, so a receiver\n * configured with any of the keys can verify. Order is preserved (the current key first).\n *\n * Per convention each secret is `\"whsec_\" + base64` (decoded by the internal `decodeSecret`).\n * Rejects with `RelayError(\"CONFIG_INVALID\")` when `secrets` is empty or a `whsec_` secret is not\n * valid Base64.\n */\nexport async function sign(opts: {\n  id: string;\n  timestampSec: number;\n  body: string;\n  secrets: string[];\n}): Promise<SignatureHeaders> {\n  if (opts.secrets.length === 0) {\n    throw new RelayError(\"CONFIG_INVALID\", \"sign requires at least one signing secret\");\n  }\n  const signedContent = utf8ToBytes(`${opts.id}.${String(opts.timestampSec)}.${opts.body}`);\n  const signatures = await Promise.all(opts.secrets.map((s) => signOne(s, signedContent)));\n  return {\n    \"webhook-id\": opts.id,\n    \"webhook-timestamp\": String(opts.timestampSec),\n    \"webhook-signature\": signatures.join(\" \"),\n  };\n}\n\n/** Default `webhook-timestamp` tolerance (seconds) — matches the Standard Webhooks recommendation. */\nconst DEFAULT_TOLERANCE_SEC = 300;\n\n/**\n * Length-checked, content-constant-time string compare. Returns early only on a length mismatch\n * (which leaks nothing secret-dependent here, since every `v1,<base64>` MAC is the same length);\n * for equal lengths it folds every char so the timing does not reveal where a forgery diverges.\n */\nfunction timingSafeEqual(a: string, b: string): boolean {\n  if (a.length !== b.length) return false;\n  let diff = 0;\n  for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n  return diff === 0;\n}\n\n/**\n * Verify an inbound Standard Webhooks request — the receiver-side counterpart of {@link sign}.\n *\n * Recomputes the `v1,<base64>` HMAC over `{id}.{timestamp}.{payload}` for each candidate secret and\n * constant-time compares it against every signature token in the `webhook-signature` header (which may\n * carry several space-separated tokens during a key rotation). Passing more than one secret lets a\n * receiver accept either key across a rotation window. Returns `false` (never throws) for a stale\n * timestamp, a missing/garbled signature, or no match, so a caller can treat any non-`true` as a reject.\n *\n * `nowSec` defaults to the current wall clock; pass it explicitly to keep verification deterministic in\n * tests. A `whsec_` secret that is not valid Base64 still rejects with `RelayError(\"CONFIG_INVALID\")`,\n * since that is a receiver misconfiguration rather than a forged request.\n */\nexport async function verifySignature(input: {\n  id: string;\n  /** The `webhook-timestamp` header value (Unix seconds), as sent. */\n  timestamp: string | number;\n  /** The raw request body exactly as received (verified before JSON parsing). */\n  payload: string;\n  /** The full `webhook-signature` header value (one or more space-separated `v1,<base64>` tokens). */\n  header: string;\n  secrets: string[];\n  /** Allowed clock skew in seconds. Default `300`. */\n  toleranceSec?: number;\n  /** Current time in Unix seconds. Default `Math.floor(Date.now() / 1000)`. */\n  nowSec?: number;\n}): Promise<boolean> {\n  if (input.secrets.length === 0) {\n    throw new RelayError(\"CONFIG_INVALID\", \"verifySignature requires at least one signing secret\");\n  }\n  const ts = String(input.timestamp);\n  const tsSec = Number(ts);\n  if (!Number.isFinite(tsSec)) return false;\n  const tolerance = input.toleranceSec ?? DEFAULT_TOLERANCE_SEC;\n  const nowSec = input.nowSec ?? Math.floor(Date.now() / 1000);\n  if (Math.abs(nowSec - tsSec) > tolerance) return false;\n\n  const provided = input.header.split(\" \").filter((t) => t.length > 0);\n  if (provided.length === 0) return false;\n\n  const signedContent = utf8ToBytes(`${input.id}.${ts}.${input.payload}`);\n  const expected = await Promise.all(input.secrets.map((s) => signOne(s, signedContent)));\n  // Fold over every pair (no early exit on a match) so timing does not reveal which key/token matched.\n  let ok = false;\n  for (const e of expected) {\n    for (const p of provided) ok = timingSafeEqual(e, p) || ok;\n  }\n  return ok;\n}\n","/**\n * At-rest secret encryption.\n *\n * Signing secrets (`webhook_outbox.secret_snapshot`, `webhook_endpoints.secret`) are encrypted\n * before they reach the store and decrypted at the point of use, so the value at rest in the DB\n * is always ciphertext. Like {@link \"./signing\"}, this is implemented with the WebCrypto global\n * (`crypto.subtle` / `crypto.getRandomValues`) so core stays cross-runtime and import-free;\n * `node:crypto` and `Buffer` are intentionally not used.\n *\n * The cipher is injected at the store boundary (see {@link \"../store/encrypted-store\"}); a\n * {@link SecretCipher} can also be implemented over an external KMS/Vault instead of the built-in\n * AES-GCM helper.\n */\nimport { base64ToBytes, bytesToBase64, bytesToUtf8, utf8ToBytes } from \"./encoding\";\nimport { RelayError } from \"./errors\";\n\n/**\n * Reversible string cipher for secrets at rest. Implementations must round-trip\n * (`decrypt(encrypt(x)) === x`) and should be authenticated so tampering is detectable.\n */\nexport interface SecretCipher {\n  encrypt(plaintext: string): Promise<string>;\n  decrypt(ciphertext: string): Promise<string>;\n}\n\n/** Envelope tag prefixing every ciphertext: versioned so a future key rotation can coexist. */\nconst ENVELOPE_PREFIX = \"ccsec.v1.\";\n/** AES-GCM nonce length in bytes (96-bit, the recommended size). */\nconst IV_BYTES = 12;\n/** AES-256 key length in bytes. */\nconst KEY_BYTES = 32;\n\n/**\n * Accept a raw 32-byte key or a base64 string that decodes to 32 bytes.\n *\n * Returns an `ArrayBuffer`-backed view (a fresh copy for the raw-key case) so the result is\n * assignable to WebCrypto's `BufferSource`, which rejects the generic `ArrayBufferLike` default.\n */\nfunction coerceKey(key: Uint8Array | string): Uint8Array<ArrayBuffer> {\n  let bytes: Uint8Array<ArrayBuffer>;\n  if (typeof key === \"string\") {\n    try {\n      bytes = base64ToBytes(key);\n    } catch (cause) {\n      throw new RelayError(\"CONFIG_INVALID\", \"cipher key string must be valid base64\", cause);\n    }\n  } else {\n    bytes = new Uint8Array(key);\n  }\n  if (bytes.length !== KEY_BYTES) {\n    throw new RelayError(\n      \"CONFIG_INVALID\",\n      `cipher key must be ${String(KEY_BYTES)} bytes (AES-256), got ${String(bytes.length)}`,\n    );\n  }\n  return bytes;\n}\n\n/**\n * Build an AES-256-GCM {@link SecretCipher} from a 32-byte key (raw bytes or base64 string).\n *\n * Each {@link SecretCipher.encrypt} draws a fresh random 96-bit IV; the output is\n * `\"ccsec.v1.\" + base64(iv ‖ ciphertext+tag)`. Decryption is authenticated, so a tampered or\n * wrong-key ciphertext rejects with `RelayError(\"CONFIG_INVALID\")` rather than returning garbage.\n *\n * @throws RelayError CONFIG_INVALID when the key is not 32 bytes (or not valid base64).\n */\nexport function createAesGcmCipher(key: Uint8Array | string): SecretCipher {\n  const keyBytes = coerceKey(key);\n  // Import once and reuse; the CryptoKey is non-extractable.\n  const cryptoKey = crypto.subtle.importKey(\"raw\", keyBytes, { name: \"AES-GCM\" }, false, [\n    \"encrypt\",\n    \"decrypt\",\n  ]);\n\n  return {\n    async encrypt(plaintext) {\n      const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));\n      const ct = await crypto.subtle.encrypt(\n        { name: \"AES-GCM\", iv },\n        await cryptoKey,\n        utf8ToBytes(plaintext),\n      );\n      const ctBytes = new Uint8Array(ct);\n      const envelope = new Uint8Array(IV_BYTES + ctBytes.length);\n      envelope.set(iv, 0);\n      envelope.set(ctBytes, IV_BYTES);\n      return ENVELOPE_PREFIX + bytesToBase64(envelope);\n    },\n    async decrypt(ciphertext) {\n      if (!ciphertext.startsWith(ENVELOPE_PREFIX)) {\n        throw new RelayError(\"CONFIG_INVALID\", `ciphertext is not a ${ENVELOPE_PREFIX} envelope`);\n      }\n      let envelope: Uint8Array<ArrayBuffer>;\n      try {\n        envelope = base64ToBytes(ciphertext.slice(ENVELOPE_PREFIX.length));\n      } catch (cause) {\n        throw new RelayError(\"CONFIG_INVALID\", \"ciphertext envelope is not valid base64\", cause);\n      }\n      if (envelope.length <= IV_BYTES) {\n        throw new RelayError(\"CONFIG_INVALID\", \"ciphertext envelope is truncated\");\n      }\n      const iv = envelope.subarray(0, IV_BYTES);\n      const body = envelope.subarray(IV_BYTES);\n      let plain: ArrayBuffer;\n      try {\n        plain = await crypto.subtle.decrypt({ name: \"AES-GCM\", iv }, await cryptoKey, body);\n      } catch (cause) {\n        // Authentication failure: tampered ciphertext or a wrong key.\n        throw new RelayError(\n          \"CONFIG_INVALID\",\n          \"secret decryption failed (bad key or tampering)\",\n          cause,\n        );\n      }\n      return bytesToUtf8(new Uint8Array(plain));\n    },\n  };\n}\n\n/** Generate a fresh random AES-256 key as a base64 string, for {@link createAesGcmCipher}. */\nexport function generateSecretKey(): string {\n  return bytesToBase64(crypto.getRandomValues(new Uint8Array(KEY_BYTES)));\n}\n","/**\n * Exponential backoff with jitter.\n */\nimport type { RetryConfig } from \"./types\";\n\n/**\n * Compute the backoff delay in milliseconds before the next attempt.\n *\n * @param attempts - How many attempts this failure makes it (1-based).\n * @param cfg - Retry policy.\n * @param rnd - Injectable RNG in `[0, 1)`; defaults to `Math.random` for deterministic tests.\n * @returns A non-negative integer delay in milliseconds.\n */\nexport function backoffMs(\n  attempts: number,\n  cfg: RetryConfig,\n  rnd: () => number = Math.random,\n): number {\n  const raw = cfg.baseMs * 2 ** (attempts - 1); // exponential\n  const capped = Math.min(raw, cfg.capMs); // cap\n  const span = capped * cfg.jitter; // +/- jitter\n  const delta = (rnd() * 2 - 1) * span; // [-span, +span]\n  return Math.max(0, Math.round(capped + delta));\n}\n\n/**\n * Parse an HTTP `Retry-After` header into a delay in milliseconds, or null when absent/invalid.\n *\n * Both forms (RFC 9110) are accepted: a non-negative delta-seconds integer, or an HTTP-date whose\n * delay is `date - now`. A past date, a negative delta, or an unparseable value yields null (caller\n * falls back to its own backoff). Pure: uses only `Date.parse` (a Web standard), no Node globals.\n *\n * @param value - The raw header value, or null when the response carried none.\n * @param nowMs - Current time in epoch ms, used to turn an HTTP-date into a relative delay.\n */\nexport function parseRetryAfter(value: string | null | undefined, nowMs: number): number | null {\n  if (value == null) return null;\n  const trimmed = value.trim();\n  if (trimmed === \"\") return null;\n  // delta-seconds: a bare non-negative integer.\n  if (/^\\d+$/.test(trimmed)) {\n    return Number(trimmed) * 1000;\n  }\n  // HTTP-date: anything Date.parse understands; keep only a non-negative future delay.\n  const dateMs = Date.parse(trimmed);\n  if (Number.isNaN(dateMs)) return null;\n  const delta = dateMs - nowMs;\n  return delta > 0 ? delta : null;\n}\n","/**\n * State machine as pure functions.\n *\n * Each function returns the field delta to persist; the actual DB update is done by the\n * store. Invariants: `delivered` / `dead` / `cancelled` are terminal (never claimed);\n * `observed` is ignored by the dispatcher; only `in_flight` rows with a stale `lockedAt`\n * are reclaimed.\n */\nimport type { OutboxRow } from \"./types\";\nimport type { RetryConfig } from \"./types\";\nimport type { Mode, Status } from \"./shared\";\n\n/** Persisted-field delta produced by a transition. */\nexport interface Transition {\n  status: Status;\n  attempts?: number;\n  availableAt?: Date;\n  lockedAt?: Date | null;\n  lockedBy?: string | null;\n  lastError?: string | null;\n  dispatchedAt?: Date | null;\n}\n\n/** Initial state at enqueue. In observe mode it is `observed` and never actually sent. */\nexport function initialState(\n  mode: Mode,\n  now: Date,\n): Pick<OutboxRow, \"status\" | \"attempts\" | \"availableAt\"> {\n  return {\n    status: mode === \"observe\" ? \"observed\" : \"pending\",\n    attempts: 0,\n    availableAt: now,\n  };\n}\n\n/** claim: `pending` -&gt; `in_flight` (applied by the store together with SKIP LOCKED). */\nexport function onClaim(now: Date, lockedBy: string): Transition {\n  return { status: \"in_flight\", lockedAt: now, lockedBy };\n}\n\n/** Delivery success (2xx): `in_flight` -&gt; `delivered`. */\nexport function onSuccess(now: Date): Transition {\n  return { status: \"delivered\", dispatchedAt: now, lockedAt: null, lockedBy: null };\n}\n\n/**\n * Delivery failure: increments `attempts`. If still below `maxAttempts` the row goes back\n * to `pending` with a backoff delay; otherwise it moves to the dead-letter state `dead`.\n */\n// eslint-disable-next-line max-params -- the state-transition signature is intentionally wide (one argument per transition input)\nexport function onFailure(\n  row: Pick<OutboxRow, \"attempts\">,\n  cfg: RetryConfig,\n  now: Date,\n  errorSummary: string,\n  backoffMs: number,\n): Transition {\n  const attempts = row.attempts + 1;\n  if (attempts < cfg.maxAttempts) {\n    return {\n      status: \"pending\",\n      attempts,\n      availableAt: new Date(now.getTime() + backoffMs),\n      lastError: errorSummary,\n      lockedAt: null,\n      lockedBy: null,\n    };\n  }\n  return { status: \"dead\", attempts, lastError: errorSummary, lockedAt: null, lockedBy: null };\n}\n\n/**\n * Permanent delivery failure (e.g. HTTP 410 Gone): `in_flight` -&gt; `dead` immediately, without\n * consuming the remaining retry budget. `attempts` is still incremented so the ledger/last attempt\n * line up. Used when the receiver signals the destination is gone for good.\n */\nexport function onPermanentFailure(\n  row: Pick<OutboxRow, \"attempts\">,\n  errorSummary: string,\n): Transition {\n  return {\n    status: \"dead\",\n    attempts: row.attempts + 1,\n    lastError: errorSummary,\n    lockedAt: null,\n    lockedBy: null,\n  };\n}\n\n/**\n * Reclaim a stuck row: `in_flight` with a stale `lockedAt` -&gt; `pending` (at-least-once).\n * The store applies this with a conditional UPDATE on `lockedAt`.\n */\nexport function onReclaim(): Transition {\n  return { status: \"pending\", lockedAt: null, lockedBy: null };\n}\n\n/**\n * Cancel a not-yet-sent row: `pending` -&gt; `cancelled` (terminal). The store applies this with a\n * conditional UPDATE guarded on `status = 'pending'`, so an already-claimed (`in_flight`) or\n * terminal row is never cancelled out from under a delivery. `attempts` is left unchanged.\n */\nexport function onCancel(): Transition {\n  return { status: \"cancelled\", lockedAt: null, lockedBy: null };\n}\n","/**\n * SSRF range evaluation as pure matchers.\n *\n * Real DNS resolution happens in the delivery layer; core only decides whether a given\n * IP / host is allowed. Allowlist has top priority, then blocklist, then private ranges.\n * CIDR comparison is done with BigInt for both IPv4 (32-bit) and IPv6 (128-bit) so no\n * third-party dependency is needed.\n */\nimport type { SsrfConfig } from \"./types\";\n\n/** Result of an SSRF evaluation. */\nexport type SsrfDecision =\n  | { allowed: true }\n  | {\n      allowed: false;\n      reason:\n        | \"private\"\n        | \"loopback\"\n        | \"link-local\"\n        | \"metadata\"\n        | \"shared\"\n        | \"multicast\"\n        | \"reserved\"\n        | \"blocklist\";\n    };\n\ninterface ParsedIp {\n  version: 4 | 6;\n  value: bigint;\n  bits: 32 | 128;\n}\n\nconst V4_MAX = (1n << 32n) - 1n;\nconst V6_MAX = (1n << 128n) - 1n;\n\n/**\n * Blocked CIDR ranges with their reason, in priority order (most specific first).\n * Unspecified/reserved addresses (`0.0.0.0/8`, `::/128`) route to the local host and are\n * reported as `loopback`, the closest category in {@link SsrfDecision}. Beyond loopback/private/\n * link-local/metadata, we also block the non-global (\"not public unicast\") ranges — shared/CGNAT,\n * multicast, broadcast, and the benchmark/documentation/reserved blocks — since none of them is a\n * legitimate public webhook target and several are routable into internal or carrier networks.\n */\nconst BLOCKED: ReadonlyArray<\n  readonly [string, Exclude<SsrfDecision, { allowed: true }>[\"reason\"]]\n> = [\n  [\"169.254.169.254/32\", \"metadata\"],\n  [\"fd00:ec2::254/128\", \"metadata\"],\n  [\"127.0.0.0/8\", \"loopback\"],\n  [\"::1/128\", \"loopback\"],\n  [\"169.254.0.0/16\", \"link-local\"],\n  [\"fe80::/10\", \"link-local\"],\n  [\"10.0.0.0/8\", \"private\"],\n  [\"172.16.0.0/12\", \"private\"],\n  [\"192.168.0.0/16\", \"private\"],\n  [\"fc00::/7\", \"private\"],\n  [\"0.0.0.0/8\", \"loopback\"],\n  [\"::/128\", \"loopback\"],\n  // Shared Address Space / carrier-grade NAT (RFC 6598).\n  [\"100.64.0.0/10\", \"shared\"],\n  // Multicast (RFC 5771 / RFC 4291 §2.7).\n  [\"224.0.0.0/4\", \"multicast\"],\n  [\"ff00::/8\", \"multicast\"],\n  // Limited broadcast; matched before the 240.0.0.0/4 reserved block for an accurate reason.\n  [\"255.255.255.255/32\", \"reserved\"],\n  // Benchmarking (RFC 2544).\n  [\"198.18.0.0/15\", \"reserved\"],\n  // Documentation/example ranges (RFC 5737 / RFC 3849) — must never carry real traffic.\n  [\"192.0.2.0/24\", \"reserved\"],\n  [\"198.51.100.0/24\", \"reserved\"],\n  [\"203.0.113.0/24\", \"reserved\"],\n  [\"2001:db8::/32\", \"reserved\"],\n  // Reserved for future use (RFC 1112 §4); 255.255.255.255 already handled above.\n  [\"240.0.0.0/4\", \"reserved\"],\n];\n\nfunction parseIpv4(s: string): bigint | null {\n  const parts = s.split(\".\");\n  if (parts.length !== 4) {\n    return null;\n  }\n  let value = 0n;\n  for (const part of parts) {\n    if (!/^\\d{1,3}$/.test(part)) {\n      return null;\n    }\n    const n = Number(part);\n    if (n > 255) {\n      return null;\n    }\n    value = (value << 8n) | BigInt(n);\n  }\n  return value;\n}\n\n/**\n * Rewrite a trailing dotted-quad (e.g. \"::ffff:1.2.3.4\") into two hex groups so the IPv6\n * group math stays purely 16-bit based. Returns the input unchanged when there is no\n * embedded IPv4, or `null` when the dotted-quad tail is malformed.\n */\nfunction rewriteEmbeddedV4Tail(s: string): string | null {\n  if (!s.includes(\".\")) {\n    return s;\n  }\n  const lastColon = s.lastIndexOf(\":\");\n  const v4 = parseIpv4(s.slice(lastColon + 1));\n  if (v4 === null) {\n    return null;\n  }\n  const hi = ((v4 >> 16n) & 0xffffn).toString(16);\n  const lo = (v4 & 0xffffn).toString(16);\n  return `${s.slice(0, lastColon + 1)}${hi}:${lo}`;\n}\n\n/**\n * Parse an IPv6 literal into its 128-bit value, or `null` if malformed. Steps: (1) rewrite a\n * trailing embedded IPv4 (`::ffff:1.2.3.4`) into two hex groups; (2) split on `::` (at most one,\n * which stands in for a run of zero groups); (3) expand head/tail around `::` to exactly eight\n * 16-bit groups; (4) fold the hex groups into a single bigint.\n */\nfunction parseIpv6(input: string): bigint | null {\n  const s = rewriteEmbeddedV4Tail(input);\n  if (s === null) {\n    return null;\n  }\n  const halves = s.split(\"::\");\n  if (halves.length > 2) {\n    return null;\n  }\n  const head = halves[0] === \"\" ? [] : (halves[0] ?? \"\").split(\":\");\n  const tail = halves.length === 2 ? (halves[1] === \"\" ? [] : (halves[1] ?? \"\").split(\":\")) : null;\n  const groups = expandV6Groups(head, tail);\n  if (!groups || groups.length !== 8) {\n    return null;\n  }\n  let value = 0n;\n  for (const g of groups) {\n    if (!/^[0-9a-fA-F]{1,4}$/.test(g)) {\n      return null;\n    }\n    value = (value << 16n) | BigInt(parseInt(g, 16));\n  }\n  return value;\n}\n\n/** Combine head/tail groups around `::`, filling the gap with zero groups. */\nfunction expandV6Groups(head: string[], tail: string[] | null): string[] | null {\n  if (tail === null) {\n    return head.length === 8 ? head : null;\n  }\n  const missing = 8 - head.length - tail.length;\n  if (missing < 1) {\n    return null;\n  }\n  return [...head, ...Array<string>(missing).fill(\"0\"), ...tail];\n}\n\nfunction parseIp(s: string): ParsedIp | null {\n  const v4 = parseIpv4(s);\n  if (v4 !== null) {\n    return { version: 4, value: v4, bits: 32 };\n  }\n  const v6 = parseIpv6(s);\n  if (v6 !== null) {\n    return { version: 6, value: v6, bits: 128 };\n  }\n  return null;\n}\n\n/** IPv6 prefixes that embed an IPv4 destination in their low 32 bits. */\nconst V6_EMBEDDED_V4: ReadonlyArray<string> = [\n  \"::ffff:0:0/96\", // IPv4-mapped (e.g. ::ffff:169.254.169.254)\n  \"64:ff9b::/96\", // NAT64 well-known prefix\n];\n\n/**\n * A CIDR range folded into its masked base value and mask, so membership is a single BigInt\n * AND + compare with no per-call string parsing. {@link BLOCKED} and {@link V6_EMBEDDED_V4} are\n * module constants that were previously re-split/re-parsed on every {@link evaluateIp} call\n * (once per entry, per resolved IP, per delivery); precomputing them once at load removes that.\n */\ninterface CidrRange {\n  version: 4 | 6;\n  value: bigint; // base address already masked to the prefix\n  mask: bigint;\n}\n\n/** Fold a constant CIDR string into a {@link CidrRange}. Only called on the trusted tables below. */\nfunction parseCidr(cidr: string): CidrRange {\n  const [addr, prefixStr] = cidr.split(\"/\");\n  const base = parseIp(addr ?? \"\");\n  if (!base) {\n    throw new Error(`invalid CIDR literal in SSRF table: \"${cidr}\"`);\n  }\n  const prefix = prefixStr === undefined ? base.bits : Number(prefixStr);\n  const full = base.bits === 32 ? V4_MAX : V6_MAX;\n  const mask = prefix === 0 ? 0n : (full << BigInt(base.bits - prefix)) & full;\n  return { version: base.version, value: base.value & mask, mask };\n}\n\n/** Membership test against a precomputed {@link CidrRange} (fast path for the constant tables). */\nfunction rangeContains(ip: ParsedIp, range: CidrRange): boolean {\n  return ip.version === range.version && (ip.value & range.mask) === range.value;\n}\n\n/** {@link BLOCKED} folded once at module load (paired with its reason), removing per-call reparse. */\nconst BLOCKED_PARSED: ReadonlyArray<\n  readonly [CidrRange, Exclude<SsrfDecision, { allowed: true }>[\"reason\"]]\n> = BLOCKED.map(([cidr, reason]) => [parseCidr(cidr), reason] as const);\n\n/** {@link V6_EMBEDDED_V4} folded once at module load. */\nconst V6_EMBEDDED_V4_PARSED: ReadonlyArray<CidrRange> = V6_EMBEDDED_V4.map(parseCidr);\n\n/**\n * If `parsed` is an IPv4-mapped or NAT64-translated IPv6 address, return the embedded\n * IPv4 so the SSRF range rules apply to the real destination; otherwise return `parsed`\n * unchanged. Without this, `::ffff:127.0.0.1` / `64:ff9b::a9fe:a9fe` would slip past the\n * IPv4 blocked ranges and reach loopback / metadata endpoints.\n */\nfunction unwrapEmbeddedV4(parsed: ParsedIp): ParsedIp {\n  if (parsed.version !== 6) {\n    return parsed;\n  }\n  for (const range of V6_EMBEDDED_V4_PARSED) {\n    if (rangeContains(parsed, range)) {\n      return { version: 4, value: parsed.value & V4_MAX, bits: 32 };\n    }\n  }\n  return parsed;\n}\n\nfunction cidrContains(ip: ParsedIp, cidr: string): boolean {\n  const [addr, prefixStr] = cidr.split(\"/\");\n  const base = parseIp(addr ?? \"\");\n  if (!base || base.version !== ip.version) {\n    return false;\n  }\n  if (prefixStr === \"\") {\n    return false; // malformed \"addr/\" with an empty prefix must not match everything\n  }\n  const prefix = prefixStr === undefined ? base.bits : Number(prefixStr);\n  if (!Number.isInteger(prefix) || prefix < 0 || prefix > base.bits) {\n    return false;\n  }\n  const full = ip.bits === 32 ? V4_MAX : V6_MAX;\n  const mask = prefix === 0 ? 0n : (full << BigInt(base.bits - prefix)) & full;\n  return (ip.value & mask) === (base.value & mask);\n}\n\nfunction matchEntry(host: string, entry: string): boolean {\n  if (entry.includes(\"/\")) {\n    const ip = parseIp(host);\n    return ip ? cidrContains(ip, entry) : false;\n  }\n  const h = host.toLowerCase();\n  const e = entry.toLowerCase();\n  if (h === e) {\n    return true;\n  }\n  if (e.startsWith(\".\")) {\n    return h === e.slice(1) || h.endsWith(e);\n  }\n  const hi = parseIp(host);\n  const ei = parseIp(entry);\n  return hi !== null && ei !== null && hi.version === ei.version && hi.value === ei.value;\n}\n\n/**\n * Host-level allowlist/blocklist match. Entries may be exact hosts/IPs, CIDR ranges, or\n * leading-dot domain suffixes (e.g. `\".example.com\"`). Also used for early pre-resolution\n * checks before DNS in the delivery layer.\n */\nexport function matchHostList(host: string, list: readonly string[]): boolean {\n  return list.some((entry) => matchEntry(host, entry));\n}\n\n/** Evaluate an IP against the SSRF policy. Allowlist wins, then blocklist, then ranges. */\nexport function evaluateIp(ip: string, cfg: SsrfConfig): SsrfDecision {\n  if (matchHostList(ip, cfg.allowlist)) {\n    return { allowed: true };\n  }\n  if (matchHostList(ip, cfg.blocklist)) {\n    return { allowed: false, reason: \"blocklist\" };\n  }\n  if (cfg.blockPrivateRanges) {\n    const parsed = parseIp(ip);\n    if (parsed) {\n      const effective = unwrapEmbeddedV4(parsed);\n      for (const [range, reason] of BLOCKED_PARSED) {\n        if (rangeContains(effective, range)) {\n          return { allowed: false, reason };\n        }\n      }\n    }\n  }\n  return { allowed: true };\n}\n","/**\n * Config resolution, defaulting and startup validation.\n *\n * Invalid configuration is rejected fail-fast with `RelayError(\"CONFIG_INVALID\")`.\n * Dangerous-but-valid settings (e.g. disabling SSRF protection) are allowed but warned\n * through the logger. Defaults come from a single source of truth (see {@link DEFAULTS}).\n */\nimport { RelayError } from \"./errors\";\nimport type { Logger } from \"./shared\";\nimport type { RelayConfig } from \"./types\";\n\n/** Recursive partial that keeps arrays and functions (e.g. `Clock`) assignable as a whole. */\nexport type DeepPartial<T> = T extends readonly unknown[]\n  ? T\n  : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    T extends (...args: any[]) => unknown\n    ? T\n    : T extends object\n      ? { [K in keyof T]?: DeepPartial<T[K]> }\n      : T;\n\nconst NOOP_LOGGER: Logger = {\n  debug() {},\n  info() {},\n  warn() {},\n  error() {},\n};\n\n/**\n * Wrap a {@link Logger} so a throwing method can never escape into a caller. Every log site in the\n * library sits on a fail-open path — delivery, the dispatcher loop, and the critical safety net all\n * call `logger.error`/`logger.warn` from inside the catch blocks that ARE the fail-open guarantee — so\n * a misbehaving logger (one whose method throws, e.g. a transport that fails under backpressure) must\n * degrade to a no-op rather than reject a delivery promise and stop the dispatcher. Unlike hooks /\n * instrument / sink (each already wrapped fail-open), the logger was the one injected component still\n * called unguarded. core forbids `console`, so a thrown error is simply swallowed here.\n */\nfunction safeLogger(inner: Logger): Logger {\n  const wrap =\n    (level: keyof Logger) =>\n    (msg: string, meta?: Record<string, unknown>): void => {\n      try {\n        // Preserve the call arity (pass meta only when present), so a wrapped logger is indistinguishable\n        // from the raw one to a caller/spy — mirrors createConsoleLogger.\n        if (meta === undefined) inner[level](msg);\n        else inner[level](msg, meta);\n      } catch {\n        // fail-open: a throwing logger must never propagate into the delivery/dispatch path.\n      }\n    };\n  return { debug: wrap(\"debug\"), info: wrap(\"info\"), warn: wrap(\"warn\"), error: wrap(\"error\") };\n}\n\n/** Default configuration values (the single source of truth for defaults). */\nconst DEFAULTS = {\n  mode: \"active\",\n  signing: { scheme: \"standard-webhooks\" },\n  retry: { maxAttempts: 12, backoff: \"exponential\", baseMs: 1_000, capMs: 3_600_000, jitter: 0.2 },\n  delivery: {\n    transport: \"http\",\n    timeoutMs: 15_000,\n    bodySnippetBytes: 4_096,\n    keepAliveTimeoutMs: 10_000,\n  },\n  ssrf: {\n    blockPrivateRanges: true,\n    allowlist: [] as readonly string[],\n    blocklist: [] as readonly string[],\n  },\n  circuitBreaker: { failureThreshold: 0, cooldownMs: 0 },\n} as const satisfies Omit<RelayConfig, \"clock\" | \"logger\">;\n\nfunction fail(message: string): never {\n  throw new RelayError(\"CONFIG_INVALID\", message);\n}\n\n/**\n * Reject a numeric setting unless it is a finite positive integer. `Number.isInteger` already rejects\n * `NaN`, `Infinity` and fractions (so a typo'd `1500.5` or a stray `Infinity` cannot slip through a\n * bare `> 0` check), and the `> 0` guard rejects `0` and negatives.\n */\nfunction requirePositiveInt(name: string, value: number): void {\n  if (!(Number.isInteger(value) && value > 0)) {\n    fail(`${name} must be a finite positive integer, got ${String(value)}`);\n  }\n}\n\n/** Validate the retry policy; `backoff` is typed to a literal but may be untyped at runtime. */\nfunction validateRetry(retry: RelayConfig[\"retry\"]): void {\n  const { maxAttempts, backoff, baseMs, capMs, jitter } = retry;\n  if ((backoff as string) !== \"exponential\") {\n    fail(`retry.backoff must be \"exponential\", got ${backoff}`);\n  }\n  if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {\n    fail(`retry.maxAttempts must be an integer >= 1, got ${String(maxAttempts)}`);\n  }\n  requirePositiveInt(\"retry.baseMs\", baseMs);\n  requirePositiveInt(\"retry.capMs\", capMs);\n  if (!(capMs >= baseMs)) {\n    fail(\n      `retry.capMs must be >= retry.baseMs, got capMs=${String(capMs)}, baseMs=${String(baseMs)}`,\n    );\n  }\n  if (!(jitter >= 0 && jitter <= 1)) {\n    fail(`retry.jitter must be within 0..1, got ${String(jitter)}`);\n  }\n}\n\n/** Validate the delivery policy; values are typed but may be untyped at runtime. */\nfunction validateDelivery(delivery: RelayConfig[\"delivery\"]): void {\n  // transport is typed to a literal but can be anything from untyped runtime input.\n  if ((delivery.transport as string) !== \"http\" && (delivery.transport as string) !== \"sink\") {\n    fail(`delivery.transport must be \"http\" or \"sink\", got ${delivery.transport}`);\n  }\n  requirePositiveInt(\"delivery.timeoutMs\", delivery.timeoutMs);\n  requirePositiveInt(\"delivery.bodySnippetBytes\", delivery.bodySnippetBytes);\n  requirePositiveInt(\"delivery.keepAliveTimeoutMs\", delivery.keepAliveTimeoutMs);\n  if (\n    delivery.connections !== undefined &&\n    !(Number.isInteger(delivery.connections) && delivery.connections >= 1)\n  ) {\n    fail(`delivery.connections must be an integer >= 1, got ${String(delivery.connections)}`);\n  }\n}\n\n/** Validate the circuit-breaker policy; values are typed but may be untyped at runtime. */\nfunction validateCircuitBreaker(cb: RelayConfig[\"circuitBreaker\"]): void {\n  if (!(Number.isInteger(cb.failureThreshold) && cb.failureThreshold >= 0)) {\n    fail(\n      `circuitBreaker.failureThreshold must be an integer >= 0 (0 disables), got ${String(cb.failureThreshold)}`,\n    );\n  }\n  if (!(Number.isInteger(cb.cooldownMs) && cb.cooldownMs >= 0)) {\n    fail(\n      `circuitBreaker.cooldownMs must be an integer >= 0 (0 disables auto-recovery), got ${String(cb.cooldownMs)}`,\n    );\n  }\n}\n\n/** Validate hard constraints; throws `RelayError(\"CONFIG_INVALID\")` on violation. */\nfunction validate(cfg: Omit<RelayConfig, \"clock\" | \"logger\">): void {\n  // mode/scheme are typed to literals but can be anything from untyped runtime input.\n  if ((cfg.mode as string) !== \"observe\" && (cfg.mode as string) !== \"active\") {\n    fail(`mode must be \"observe\" or \"active\", got ${cfg.mode}`);\n  }\n  if ((cfg.signing.scheme as string) !== \"standard-webhooks\") {\n    fail(`Unsupported signing scheme: ${cfg.signing.scheme}`);\n  }\n  validateRetry(cfg.retry);\n  validateDelivery(cfg.delivery);\n  validateCircuitBreaker(cfg.circuitBreaker);\n  // Optional: only validate when the caller opted in (omitted = no limit).\n  if (cfg.maxPayloadBytes !== undefined) {\n    requirePositiveInt(\"maxPayloadBytes\", cfg.maxPayloadBytes);\n  }\n}\n\n/** Emit non-fatal warnings for dangerous-but-valid settings. */\nfunction warnIfRisky(cfg: Omit<RelayConfig, \"clock\" | \"logger\">, logger: Logger): void {\n  if (!cfg.ssrf.blockPrivateRanges) {\n    logger.warn(\n      \"ssrf.blockPrivateRanges is disabled; private/loopback/metadata destinations are reachable\",\n    );\n  }\n  if (cfg.retry.baseMs * 2 ** (cfg.retry.maxAttempts - 1) > cfg.retry.capMs) {\n    logger.warn(\"retry backoff saturates retry.capMs before reaching retry.maxAttempts\", {\n      baseMs: cfg.retry.baseMs,\n      maxAttempts: cfg.retry.maxAttempts,\n      capMs: cfg.retry.capMs,\n    });\n  }\n}\n\n/** Merge the SSRF policy with its defaults (extracted to keep resolveConfig's complexity in budget). */\nfunction mergeSsrf(input: DeepPartial<RelayConfig>[\"ssrf\"]): RelayConfig[\"ssrf\"] {\n  return {\n    blockPrivateRanges: input?.blockPrivateRanges ?? DEFAULTS.ssrf.blockPrivateRanges,\n    allowlist: input?.allowlist ?? [],\n    blocklist: input?.blocklist ?? [],\n  };\n}\n\n/** Fill defaults into a partial config, validate, and return a deeply frozen config. */\nexport function resolveConfig(input: DeepPartial<RelayConfig>): RelayConfig {\n  const merged: Omit<RelayConfig, \"clock\" | \"logger\"> = {\n    mode: input.mode ?? DEFAULTS.mode,\n    signing: { scheme: input.signing?.scheme ?? DEFAULTS.signing.scheme },\n    retry: { ...DEFAULTS.retry, ...input.retry },\n    delivery: { ...DEFAULTS.delivery, ...input.delivery },\n    ssrf: mergeSsrf(input.ssrf),\n    circuitBreaker: {\n      failureThreshold:\n        input.circuitBreaker?.failureThreshold ?? DEFAULTS.circuitBreaker.failureThreshold,\n      cooldownMs: input.circuitBreaker?.cooldownMs ?? DEFAULTS.circuitBreaker.cooldownMs,\n    },\n    // Optional, off by default: left undefined unless the caller sets it.\n    maxPayloadBytes: input.maxPayloadBytes,\n  };\n  validate(merged);\n\n  // Merge over the no-op so any methods the caller omits still exist, then wrap fail-open so a\n  // throwing logger can never escape into the delivery/dispatch path (see safeLogger).\n  const logger: Logger = safeLogger({ ...NOOP_LOGGER, ...input.logger });\n  warnIfRisky(merged, logger);\n\n  return Object.freeze({\n    mode: merged.mode,\n    signing: Object.freeze(merged.signing),\n    retry: Object.freeze(merged.retry),\n    delivery: Object.freeze(merged.delivery),\n    ssrf: Object.freeze({\n      blockPrivateRanges: merged.ssrf.blockPrivateRanges,\n      allowlist: Object.freeze([...merged.ssrf.allowlist]),\n      blocklist: Object.freeze([...merged.ssrf.blocklist]),\n    }),\n    circuitBreaker: Object.freeze(merged.circuitBreaker),\n    maxPayloadBytes: merged.maxPayloadBytes,\n    clock: input.clock ?? (() => new Date()),\n    logger,\n  });\n}\n","/**\n * Enqueue-time payload validation (pure, cross-runtime).\n *\n * `enqueue` rides the business transaction and is fail-closed, so an unserializable payload would\n * otherwise surface as a raw driver error from the jsonb serialization step rather than a typed\n * {@link RelayError}. This guard catches that case (and an optional size ceiling) up front and wraps\n * it in a stable `ENQUEUE_INVALID_PAYLOAD`, symmetric to how the endpoint shape is validated into\n * `ENQUEUE_NO_TARGET`. Uses only Web-standard globals (`JSON`, `TextEncoder`).\n */\nimport { utf8ToBytes } from \"./encoding\";\nimport { RelayError } from \"./errors\";\n\n/**\n * Validate that `payload` can be stored as jsonb and, when `maxBytes` is set, that its serialized\n * byte length is within the limit. Throws `RelayError(\"ENQUEUE_INVALID_PAYLOAD\")` otherwise; the\n * original serialization error (e.g. for a circular reference or a BigInt) is attached as `cause`.\n *\n * @param payload - The enqueue payload (stored as jsonb).\n * @param maxBytes - Optional ceiling on the UTF-8 byte length of the JSON serialization. Omitted/undefined = no limit.\n */\nexport function validatePayload(payload: unknown, maxBytes?: number): void {\n  let json: string | undefined;\n  try {\n    json = JSON.stringify(payload);\n  } catch (err) {\n    // Circular reference, BigInt, or a custom toJSON that throws — none can be stored as jsonb.\n    throw new RelayError(\n      \"ENQUEUE_INVALID_PAYLOAD\",\n      `payload is not JSON-serializable: ${err instanceof Error ? err.message : String(err)}`,\n      err,\n    );\n  }\n  // The lib types `JSON.stringify` as returning `string`, but it returns `undefined` for a top-level\n  // undefined/function/symbol, so this guard is real despite what the types imply.\n  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n  if (json === undefined) {\n    // Top-level undefined, a function, or a symbol stringifies to undefined and cannot be stored in\n    // the NOT NULL jsonb column.\n    throw new RelayError(\n      \"ENQUEUE_INVALID_PAYLOAD\",\n      \"payload is not JSON-serializable: value serializes to undefined (e.g. undefined, function, symbol)\",\n    );\n  }\n  if (maxBytes !== undefined) {\n    // Reuse the shared module-level encoder (see ./encoding) instead of allocating a TextEncoder\n    // per enqueue; the full byte array is still materialized because an exact UTF-8 byte count needs it.\n    const bytes = utf8ToBytes(json).length;\n    if (bytes > maxBytes) {\n      throw new RelayError(\n        \"ENQUEUE_INVALID_PAYLOAD\",\n        `payload exceeds maxPayloadBytes: ${String(bytes)} > ${String(maxBytes)}`,\n      );\n    }\n  }\n}\n","/**\n * Single source for application-assigned uuids.\n *\n * Outbox rows, replay copies, and delivery-ledger rows get their ids here so the generation\n * strategy lives in one place. Lives outside `core/` because it uses the `node:crypto` builtin,\n * which the dependency-free, cross-runtime core must not import.\n */\nimport { randomUUID } from \"node:crypto\";\n\n/** Generate a fresh uuid for an application-assigned id. */\nexport function newId(): string {\n  return randomUUID();\n}\n","/**\n * Transparent at-rest encryption for signing secrets, as a {@link Store} decorator.\n *\n * Wrapping a store with {@link createEncryptedStore} encrypts the secret columns\n * (`secretSnapshot`, endpoint `secret`) on the way to the backend and decrypts them on the way\n * back, so every other layer (relay, delivery, admin) keeps seeing plaintext while the value at\n * rest is always ciphertext. All encryption is confined to this one module — the underlying\n * adapter and the rest of the codebase are unchanged.\n *\n * Only the secret-bearing methods carry logic; everything else passes straight through.\n *\n * Decryption happens at the store boundary (`claimDue` / `selectForReplay` / `findEndpoint`). A bad\n * key or corrupted ciphertext is isolated per row, never batch-wide: on the dispatch path\n * (`claimDue`) an undecryptable row is quarantined straight to `dead` (the DLQ) and dropped from the\n * batch, so one bad row can never wedge the queue — the remaining rows deliver normally and the bad\n * one is visible in the DLQ (replayable once the key is fixed). `selectForReplay` skips undecryptable\n * rows (logging each) so an admin replay is not aborted by a single bad row. `findEndpoint` still\n * throws on decryption failure, which the delivery path catches and routes to a failure for that one\n * row. This only arises under genuine key misconfiguration or data corruption, never in normal use.\n */\nimport { RelayError } from \"../core/index\";\nimport type { SecretCipher, Logger, Transition } from \"../core/index\";\nimport type { OutboxRow, EndpointRow } from \"../core/index\";\nimport type { NewOutboxRow, NewEndpointRow, EndpointPatch, Store } from \"./store\";\n\nconst NO_OP_LOGGER: Logger = {\n  debug() {},\n  info() {},\n  warn() {},\n  error() {},\n};\n\n/** Transition that quarantines an undecryptable row to the DLQ (matches `core/state.onPermanentFailure`). */\nfunction quarantineTransition(row: OutboxRow): Transition {\n  return {\n    status: \"dead\",\n    attempts: row.attempts + 1,\n    lastError: \"secret decryption failed\",\n    lockedAt: null,\n    lockedBy: null,\n  };\n}\n\n/** Encrypt the `secretSnapshot` of a new outbox row (null is left as-is). */\nasync function encryptOutbox(row: NewOutboxRow, cipher: SecretCipher): Promise<NewOutboxRow> {\n  if (row.secretSnapshot == null) return row;\n  return { ...row, secretSnapshot: await cipher.encrypt(row.secretSnapshot) };\n}\n\n/** Decrypt the `secretSnapshot` of a fetched outbox row (null is left as-is). */\nasync function decryptOutbox(row: OutboxRow, cipher: SecretCipher): Promise<OutboxRow> {\n  if (row.secretSnapshot == null) return row;\n  return { ...row, secretSnapshot: await cipher.decrypt(row.secretSnapshot) };\n}\n\n/**\n * Data-loss sink for the claim-path quarantine. A quarantined row reaches the terminal `dead`\n * state (the DLQ), which is one of the two critical categories that must stay visible even with no\n * logger configured; `createRelay` wires its critical logger's `dataLoss` here. Optional so a\n * directly-constructed store degrades to plain `logger` output.\n */\nexport type DataLossSink = (msg: string, meta?: Record<string, unknown>) => void;\n\n/**\n * Wrap a store so signing secrets are encrypted at rest with `cipher`. The returned store is a\n * drop-in `Store<TTx>`; secrets are plaintext at this boundary and ciphertext in the backend.\n */\nexport function createEncryptedStore<TTx>(\n  inner: Store<TTx>,\n  cipher: SecretCipher,\n  logger: Logger = NO_OP_LOGGER,\n  dataLoss?: DataLossSink,\n): Store<TTx> {\n  return {\n    // --- writes: encrypt secrets before they reach the backend ---\n    async insertOutbox(trx, row) {\n      await inner.insertOutbox(trx, await encryptOutbox(row, cipher));\n    },\n    async insertOutboxMany(trx, rows) {\n      await inner.insertOutboxMany(\n        trx,\n        await Promise.all(rows.map((r) => encryptOutbox(r, cipher))),\n      );\n    },\n    async insertOutboxAutonomous(row) {\n      await inner.insertOutboxAutonomous(await encryptOutbox(row, cipher));\n    },\n    async insertReplayCopies(rows) {\n      return inner.insertReplayCopies(await Promise.all(rows.map((r) => encryptOutbox(r, cipher))));\n    },\n    async insertEndpoint(ep) {\n      const enc: NewEndpointRow = { ...ep, secret: await cipher.encrypt(ep.secret) };\n      await inner.insertEndpoint(enc);\n    },\n    async updateEndpoint(id, patch) {\n      // Encrypt whichever secret column the patch sets. `secretSecondary: null` (rotation finalize)\n      // is left as-is; only a string value is encrypted.\n      const enc: EndpointPatch = { ...patch };\n      if (typeof patch.secret === \"string\") enc.secret = await cipher.encrypt(patch.secret);\n      if (typeof patch.secretSecondary === \"string\") {\n        enc.secretSecondary = await cipher.encrypt(patch.secretSecondary);\n      }\n      await inner.updateEndpoint(id, enc);\n    },\n\n    // --- reads: decrypt secrets coming back from the backend (per-row isolation) ---\n    async claimDue(opts) {\n      const rows = await inner.claimDue(opts);\n      const out: OutboxRow[] = [];\n      for (const r of rows) {\n        try {\n          out.push(await decryptOutbox(r, cipher));\n        } catch (err) {\n          // Isolate the failure: quarantine just this row to the DLQ so one bad ciphertext can never\n          // poison the batch and wedge the (oldest-first) queue. The row is still `in_flight` here,\n          // so applyTransition's guard applies; it then leaves the claimable set for good.\n          logger.warn(\"encrypted-store: quarantining undecryptable outbox row to dead\", {\n            id: r.id,\n            error: String(err),\n          });\n          let quarantined = false;\n          try {\n            await inner.applyTransition(r.id, quarantineTransition(r));\n            quarantined = true;\n          } catch (qErr) {\n            logger.error(\"encrypted-store: failed to quarantine undecryptable row\", {\n              id: r.id,\n              error: String(qErr),\n            });\n          }\n          // The row is now terminal `dead` (the DLQ): a data-loss event, surfaced through the\n          // critical sink so it stays visible even when no logger is configured (mirrors the\n          // delivery path's dead-letter alarm).\n          if (quarantined) {\n            dataLoss?.(\n              \"message moved to the DLQ and is permanently lost (secret decryption failed)\",\n              {\n                id: r.id,\n                endpointId: r.endpointId,\n                eventType: r.eventType,\n                attempts: r.attempts + 1,\n                error: String(err),\n              },\n            );\n          }\n        }\n      }\n      return out;\n    },\n    async selectForReplay(filter) {\n      const rows = await inner.selectForReplay(filter);\n      const out: OutboxRow[] = [];\n      for (const r of rows) {\n        try {\n          out.push(await decryptOutbox(r, cipher));\n        } catch (err) {\n          // Skip undecryptable rows so a single bad row does not abort the whole admin replay.\n          logger.warn(\"encrypted-store: skipping undecryptable row during replay\", {\n            id: r.id,\n            error: String(err),\n          });\n        }\n      }\n      return out;\n    },\n    async findEndpoint(id) {\n      const ep = await inner.findEndpoint(id);\n      if (!ep) return ep;\n      try {\n        const decrypted: EndpointRow = {\n          ...ep,\n          secret: await cipher.decrypt(ep.secret),\n          secretSecondary:\n            ep.secretSecondary == null ? null : await cipher.decrypt(ep.secretSecondary),\n        };\n        return decrypted;\n      } catch (cause) {\n        // Normalise to CONFIG_INVALID so the delivery path routes an undecryptable registered-endpoint\n        // secret straight to `dead` (matching the inline `claimDue` quarantine), regardless of what a\n        // custom SecretCipher throws — the built-in cipher already throws CONFIG_INVALID.\n        throw new RelayError(\"CONFIG_INVALID\", \"endpoint secret decryption failed\", cause);\n      }\n    },\n\n    // --- pass-through: no secret columns involved ---\n    // listOutbox/listEndpoints/getOutbox are secret-free by construction (the store never selects the\n    // secret columns), so they pass straight through with no decryption. cancel and the circuit-breaker\n    // counters touch no secret column either.\n    applyTransition: (id, t) => inner.applyTransition(id, t),\n    cancel: (id) => inner.cancel(id),\n    reclaimStuck: (opts) => inner.reclaimStuck(opts),\n    recordAttempt: (attempt) => inner.recordAttempt(attempt),\n    completeAttempt: (attempt, transition, expectedLockedBy) =>\n      inner.completeAttempt(attempt, transition, expectedLockedBy),\n    queryAttempts: (opts) => inner.queryAttempts(opts),\n    getOutbox: (id) => inner.getOutbox(id),\n    listOutbox: (filter) => inner.listOutbox(filter),\n    listEndpoints: (filter) => inner.listEndpoints(filter),\n    disableEndpoint: (id, now) => inner.disableEndpoint(id, now),\n    noteEndpointSuccess: (id) => inner.noteEndpointSuccess(id),\n    noteEndpointFailure: (id, now, threshold) => inner.noteEndpointFailure(id, now, threshold),\n    reactivateEndpoint: (id) => inner.reactivateEndpoint(id),\n    prune: (opts) => inner.prune(opts),\n    stats: () => inner.stats(),\n    diagnose: () => inner.diagnose(),\n    migrate: () => inner.migrate(),\n  };\n}\n","/**\n * In-process TTL cache for registered-endpoint lookups, as a {@link Store} decorator.\n *\n * The registered-endpoint workflow calls {@link Store.findEndpoint} once per delivery (see\n * `resolveTarget` in `../delivery/deliver`), which is a DB round trip for data that changes only\n * on an admin action. Wrapping the store memoises found rows for a short TTL, cutting the per-\n * delivery round trips on the hot path. v1 has no auto-disable, so an endpoint only changes via\n * `updateEndpoint` / `disableEndpoint`; those invalidate the entry immediately within the process,\n * and the TTL bounds staleness from changes made by other processes.\n *\n * Only the endpoint methods carry logic; everything else passes straight through. Place this OUTSIDE\n * the encrypted-store decorator so it caches already-decrypted {@link EndpointRow}s.\n */\nimport type { EndpointRow } from \"../core/index\";\nimport type { EndpointPatch, Store } from \"./store\";\n\ninterface Entry {\n  row: EndpointRow;\n  expiresAt: number;\n}\n\n/**\n * Wrap a store so {@link Store.findEndpoint} results are cached for `ttlMs`. Found rows are cached;\n * misses are not (so a later `insertEndpoint` is visible at once). `updateEndpoint` /\n * `disableEndpoint` evict the affected id.\n */\nexport function createEndpointCache<TTx>(inner: Store<TTx>, opts: { ttlMs: number }): Store<TTx> {\n  const { ttlMs } = opts;\n  const cache = new Map<string, Entry>();\n  // Bumped on every write; a read captures it before fetching and only caches if it is unchanged\n  // afterwards, so a value read concurrently with an update/disable is never cached (stale-read guard).\n  let generation = 0;\n\n  /**\n   * Run an endpoint write with the stale-read guard on BOTH sides. The bump+evict before the write\n   * invalidates reads already in flight; the bump+evict after it covers reads that STARTED during the\n   * write — those captured the already-bumped generation, may have fetched the pre-commit row, and\n   * would otherwise cache it for the full TTL. The trailing pair runs in `finally` because a failed\n   * write may still have been applied (e.g. a timeout after commit).\n   */\n  const invalidating = async (id: string, write: () => Promise<void>): Promise<void> => {\n    generation++;\n    cache.delete(id);\n    try {\n      await write();\n    } finally {\n      generation++;\n      cache.delete(id);\n    }\n  };\n\n  return {\n    async findEndpoint(id) {\n      const hit = cache.get(id);\n      if (hit && hit.expiresAt > Date.now()) return hit.row;\n      const gen = generation;\n      const row = await inner.findEndpoint(id);\n      // Only cache hits (caching a miss would hide a subsequent register), and only when no write\n      // raced during the read — otherwise this fetch may have seen the pre-write row.\n      if (row && gen === generation) cache.set(id, { row, expiresAt: Date.now() + ttlMs });\n      return row;\n    },\n    updateEndpoint(id, patch: EndpointPatch) {\n      return invalidating(id, () => inner.updateEndpoint(id, patch));\n    },\n    disableEndpoint(id, now) {\n      return invalidating(id, () => inner.disableEndpoint(id, now));\n    },\n\n    noteEndpointFailure(id, now, threshold) {\n      // A failure may trip the circuit breaker and flip the endpoint to `disabled`, which the\n      // per-delivery resolveTarget reads — so evict so the next findEndpoint reflects it promptly.\n      return invalidating(id, () => inner.noteEndpointFailure(id, now, threshold));\n    },\n    // noteEndpointSuccess runs on every successful delivery and only resets the failure counter\n    // (status unchanged); evicting here would defeat the cache, and the stale counter is never read\n    // on the hot path, so pass it straight through without invalidating.\n    noteEndpointSuccess: (id) => inner.noteEndpointSuccess(id),\n\n    reactivateEndpoint(id) {\n      // Half-open recovery flips the endpoint back to `active` and clears disabled_at, which the\n      // per-delivery resolveTarget reads — evict so the next findEndpoint reflects it promptly.\n      return invalidating(id, () => inner.reactivateEndpoint(id));\n    },\n\n    // --- pass-through ---\n    insertOutbox: (trx, row) => inner.insertOutbox(trx, row),\n    insertOutboxMany: (trx, rows) => inner.insertOutboxMany(trx, rows),\n    insertOutboxAutonomous: (row) => inner.insertOutboxAutonomous(row),\n    insertReplayCopies: (rows) => inner.insertReplayCopies(rows),\n    insertEndpoint: (ep) => inner.insertEndpoint(ep),\n    claimDue: (o) => inner.claimDue(o),\n    selectForReplay: (f) => inner.selectForReplay(f),\n    // List surfaces are not cached (they are admin/monitoring reads, not the per-delivery hot path).\n    listOutbox: (f) => inner.listOutbox(f),\n    listEndpoints: (f) => inner.listEndpoints(f),\n    getOutbox: (id) => inner.getOutbox(id),\n    applyTransition: (id, t) => inner.applyTransition(id, t),\n    cancel: (id) => inner.cancel(id),\n    reclaimStuck: (o) => inner.reclaimStuck(o),\n    recordAttempt: (a) => inner.recordAttempt(a),\n    completeAttempt: (a, t, expectedLockedBy) => inner.completeAttempt(a, t, expectedLockedBy),\n    queryAttempts: (o) => inner.queryAttempts(o),\n    prune: (o) => inner.prune(o),\n    stats: () => inner.stats(),\n    diagnose: () => inner.diagnose(),\n    migrate: () => inner.migrate(),\n  };\n}\n","/**\n * HTTP client with SSRF-pinned connections.\n *\n * native `fetch` cannot control the connected IP, so a DNS-rebinding window stays open between\n * resolution and connection. Instead an undici `Agent` is given a custom `connect.lookup` that\n * resolves every candidate IP, validates each with the pure `core/ssrf` matchers, and only lets\n * undici connect to a vetted address — while the `Host` header and TLS SNI keep the original\n * hostname (so virtual hosting and certificate validation are unaffected).\n */\nimport { Agent, request } from \"undici\";\nimport { lookup as dnsLookup } from \"node:dns\";\nimport type { LookupFunction } from \"node:net\";\nimport { evaluateIp, matchHostList, bytesToUtf8 } from \"../core/index\";\nimport type { SsrfConfig, DeliveryConfig } from \"../core/index\";\nimport { errorCode, secretFreeSummary } from \"./_error\";\n\n/** Outcome of one POST attempt. `status === null` means no response (network/timeout/SSRF). */\nexport interface HttpResult {\n  status: number | null;\n  /** Response body up to `bodySnippetBytes`, trimmed at a UTF-8 code-point boundary. */\n  bodySnippet: string | null;\n  durationMs: number;\n  /** Exception summary, if any (e.g. `\"SSRF_BLOCKED:metadata\"`, `\"TIMEOUT\"`). */\n  error: string | null;\n  /** Raw `Retry-After` response header (null when absent); parsed by the caller via `parseRetryAfter`. */\n  retryAfter: string | null;\n}\n\n/** Resolve a hostname to all candidate addresses. Injectable so tests can simulate rebinding. */\nexport type ResolveAll = (\n  hostname: string,\n  callback: (\n    err: NodeJS.ErrnoException | null,\n    addresses: { address: string; family: number }[],\n  ) => void,\n) => void;\n\n/** Thrown from the guarded lookup when a resolved IP is in a blocked range. */\nclass SsrfBlockedError extends Error {\n  constructor(readonly reason: string) {\n    super(`SSRF_BLOCKED:${reason}`);\n    this.name = \"SsrfBlockedError\";\n  }\n}\n\nconst defaultResolveAll: ResolveAll = (hostname, callback) => {\n  dnsLookup(hostname, { all: true }, callback);\n};\n\n/** Normalise an undici header value (string | string[] | undefined) to a single string or null. */\nfunction headerValue(value: string | string[] | undefined): string | null {\n  if (value == null) return null;\n  return Array.isArray(value) ? (value[0] ?? null) : value;\n}\n\n/** Drop the surrounding brackets of an IPv6 literal host (`[::1]` becomes `::1`). */\nfunction stripBrackets(host: string): string {\n  return host.startsWith(\"[\") && host.endsWith(\"]\") ? host.slice(1, -1) : host;\n}\n\n/**\n * Trim `bytes` to at most `maxBytes`, cutting on a UTF-8 code-point boundary so a multi-byte\n * sequence is never split. Trailing continuation bytes (0x80-0xBF) at the cut are rewound.\n */\nexport function truncateUtf8(bytes: Uint8Array, maxBytes: number): string {\n  // bytesToUtf8 reuses the shared module-level TextDecoder (see core/encoding) rather than\n  // allocating one per response snippet read.\n  if (bytes.length <= maxBytes) {\n    return bytesToUtf8(bytes);\n  }\n  let end = maxBytes;\n  while (end > 0 && ((bytes[end] ?? 0) & 0xc0) === 0x80) {\n    end--;\n  }\n  return bytesToUtf8(bytes.subarray(0, end));\n}\n\n/** Pick the address matching the requested family, else the first candidate. */\nfunction chooseAddress(\n  addresses: { address: string; family: number }[],\n  family: number,\n): { address: string; family: number } | undefined {\n  if (family === 4 || family === 6) {\n    const match = addresses.find((a) => a.family === family);\n    if (match) return match;\n  }\n  return addresses[0];\n}\n\n/** Walk the error and its `cause` chain looking for an {@link SsrfBlockedError}. */\nfunction findSsrf(err: unknown): SsrfBlockedError | null {\n  let cur: unknown = err;\n  for (let i = 0; i < 5 && cur != null; i++) {\n    if (cur instanceof SsrfBlockedError) return cur;\n    cur = (cur as { cause?: unknown }).cause;\n  }\n  return null;\n}\n\n/**\n * Reduce a thrown value to a stable, secret-free summary. SSRF blocks and timeouts get specific\n * labels; everything else falls back to the shared {@link secretFreeSummary}.\n */\nfunction summarize(err: unknown): string {\n  const ssrf = findSsrf(err);\n  if (ssrf) return ssrf.message;\n  if (err instanceof Error) {\n    const code = errorCode(err);\n    if (err.name === \"AbortError\" || code === \"UND_ERR_ABORTED\" || code?.includes(\"TIMEOUT\")) {\n      return \"TIMEOUT\";\n    }\n  }\n  return secretFreeSummary(err);\n}\n\n/** Read up to `maxBytes` of the response body, discarding the rest, trimmed UTF-8-safe. */\nasync function readSnippet(\n  body: AsyncIterable<unknown> & { destroy?: () => void },\n  maxBytes: number,\n): Promise<string> {\n  const chunks: Uint8Array[] = [];\n  let total = 0;\n  for await (const chunk of body) {\n    const buf = chunk as Uint8Array;\n    chunks.push(buf);\n    total += buf.length;\n    if (total >= maxBytes) break;\n  }\n  // Free the socket: if we stopped early, the remaining body must be discarded.\n  body.destroy?.();\n  const joined = new Uint8Array(total);\n  let offset = 0;\n  for (const c of chunks) {\n    joined.set(c, offset);\n    offset += c.length;\n  }\n  return truncateUtf8(joined, maxBytes);\n}\n\n/**\n * Build the SSRF-validating `connect.lookup`: resolve all candidate IPs, reject the whole resolution\n * if any address is in a blocked range (unless the host is allowlisted), then hand undici the vetted\n * result. Node's lookup contract is honoured in both shapes: `options.all === true` (the default on\n * Node 20+ with network-family autoselection) expects an ADDRESS ARRAY, while the legacy path expects\n * a single family-matched address.\n */\nexport function makeGuardedLookup(ssrf: SsrfConfig, resolveAll: ResolveAll): LookupFunction {\n  return (hostname, options, callback) => {\n    resolveAll(hostname, (err, addresses) => {\n      if (err) {\n        callback(err, \"\", 0);\n        return;\n      }\n      // A host-level allowlist match is a full bypass (internal/self-host delivery).\n      if (!matchHostList(hostname, ssrf.allowlist)) {\n        for (const a of addresses) {\n          const decision = evaluateIp(a.address, ssrf);\n          if (!decision.allowed) {\n            callback(new SsrfBlockedError(decision.reason), \"\", 0);\n            return;\n          }\n        }\n      }\n      const family = typeof options.family === \"number\" ? options.family : 0;\n      // Node 20+ enables network-family autoselection by default, which calls this lookup with\n      // `options.all === true` and expects an ARRAY of vetted {address, family} entries — the legacy\n      // single-address form makes Node throw ERR_INVALID_IP_ADDRESS. Return all vetted candidates\n      // (family-filtered when a specific family was requested) so happy-eyeballs can pick.\n      if (options.all === true) {\n        const all =\n          family === 4 || family === 6 ? addresses.filter((a) => a.family === family) : addresses;\n        if (all.length === 0) {\n          callback(new Error(\"ENOTFOUND\"), \"\", 0);\n          return;\n        }\n        callback(null, all);\n        return;\n      }\n      const chosen = chooseAddress(addresses, family);\n      if (!chosen) {\n        callback(new Error(\"ENOTFOUND\"), \"\", 0);\n        return;\n      }\n      callback(null, chosen.address, chosen.family);\n    });\n  };\n}\n\n/**\n * POST with SSRF validation. Never throws: any failure (blocked range, network error, timeout)\n * is captured in {@link HttpResult}. A single shared {@link Agent} pins connections per request.\n */\nexport function createHttpClient(\n  cfg: { ssrf: SsrfConfig; delivery: DeliveryConfig },\n  deps: { resolveAll?: ResolveAll } = {},\n): {\n  post(opts: { url: string; headers: Record<string, string>; body: string }): Promise<HttpResult>;\n} {\n  const { ssrf, delivery } = cfg;\n  const resolveAll = deps.resolveAll ?? defaultResolveAll;\n\n  // Pin Happy-Eyeballs ON regardless of the ambient Node default or `--no-network-family-autoselection`,\n  // so delivery reliability (v4/v6 fallback) never depends on how the host process was launched. Node\n  // 20+ defaults this to true; a deployment that disabled it would otherwise lose the fallback and could\n  // fail on a dual-stack host whose DNS returns an unreachable family first (verbatim order, Node 17+).\n  const connect: { lookup: LookupFunction; autoSelectFamily: boolean } = {\n    lookup: makeGuardedLookup(ssrf, resolveAll),\n    autoSelectFamily: true,\n  };\n  // Tune connection reuse for delivery throughput: a longer keep-alive window reuses TCP/TLS across\n  // bursts to the same host; `connections` (when set) caps per-origin sockets. `pipelining` stays at\n  // the undici default (1) since POST is not safe to pipeline.\n  const agent = new Agent({\n    connect,\n    keepAliveTimeout: delivery.keepAliveTimeoutMs,\n    ...(delivery.connections != null ? { connections: delivery.connections } : {}),\n  });\n\n  return {\n    async post({ url, headers, body }) {\n      const start = Date.now();\n      const fail = (error: string): HttpResult => ({\n        status: null,\n        bodySnippet: null,\n        durationMs: Date.now() - start,\n        error,\n        retryAfter: null,\n      });\n\n      let parsed: URL;\n      try {\n        parsed = new URL(url);\n      } catch {\n        return fail(\"INVALID_URL\");\n      }\n      if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n        return fail(\"SSRF_BLOCKED:scheme\");\n      }\n      // Early reject: IP-literal ranges, blocklisted hosts (allowlist wins inside evaluateIp).\n      const decision = evaluateIp(stripBrackets(parsed.hostname), ssrf);\n      if (!decision.allowed) {\n        return fail(`SSRF_BLOCKED:${decision.reason}`);\n      }\n\n      const controller = new AbortController();\n      const timer = setTimeout(() => {\n        controller.abort();\n      }, delivery.timeoutMs);\n      try {\n        const res = await request(url, {\n          method: \"POST\",\n          headers,\n          body,\n          signal: controller.signal,\n          dispatcher: agent,\n        });\n        // The status is authoritative once the response headers arrive: a slow/stalled body (or the\n        // timeout firing mid-stream) must not discard a known 2xx and force a needless redelivery.\n        // Read the snippet best-effort; on failure keep the status and drop the body.\n        let bodySnippet: string | null = null;\n        try {\n          bodySnippet = await readSnippet(res.body, delivery.bodySnippetBytes);\n        } catch {\n          res.body.destroy();\n        }\n        return {\n          status: res.statusCode,\n          bodySnippet,\n          durationMs: Date.now() - start,\n          error: null,\n          retryAfter: headerValue(res.headers[\"retry-after\"]),\n        };\n      } catch (err) {\n        return fail(summarize(err));\n      } finally {\n        clearTimeout(timer);\n      }\n    },\n  };\n}\n","/**\n * Shared, secret-free error reduction for the delivery path.\n *\n * Thrown values from undici / DNS / signing may carry secrets in their messages, so the ledger\n * and logs only ever store a short, stable summary. Both the per-row orchestrator\n * ({@link \"./deliver\"}) and the HTTP client ({@link \"./http\"}) reduce errors the same way; the\n * HTTP client layers SSRF/timeout specialisation on top of {@link secretFreeSummary}.\n */\n\n/** Extract a string `code` property (e.g. undici/Node system errors) if present. */\nexport function errorCode(err: unknown): string | undefined {\n  const code = (err as { code?: unknown } | null | undefined)?.code;\n  return typeof code === \"string\" ? code : undefined;\n}\n\n/** Reduce a thrown value to a short, secret-free summary: error code, else message, else String. */\nexport function secretFreeSummary(err: unknown): string {\n  if (err instanceof Error) {\n    return errorCode(err) ?? err.message;\n  }\n  return String(err);\n}\n","/**\n * Critical-event surfacing for the delivery path.\n *\n * Routine operational logs (delivery failures, retries) go through the injected {@link Logger} and\n * stay silent when no logger is configured (the default is a no-op). Two categories are different:\n * a **security event** (an SSRF block) and **data loss** (a message reaching the terminal `dead`\n * state, i.e. the DLQ) must not vanish just because the logger was not wired. This logger always\n * forwards to the configured logger and, only when no logger was configured, also falls back to the\n * `console` so the event is visible — and says so in the message.\n *\n * Lives in the delivery layer (not `core`) because it uses the `console` global, which `core` forbids.\n */\nimport type { Logger } from \"../core/index\";\n\n/** Surfaces security/data-loss events even when no {@link Logger} was configured (internal). */\nexport interface CriticalLogger {\n  /** A delivery was refused because the destination resolved to a blocked address (SSRF). */\n  securityBlocked(msg: string, meta?: Record<string, unknown>): void;\n  /** A message reached the terminal `dead` state and is permanently lost (the DLQ). */\n  dataLoss(msg: string, meta?: Record<string, unknown>): void;\n}\n\n/**\n * Build a {@link CriticalLogger}. When `loggerConfigured` is false the injected `logger` is the\n * no-op default, so the event would otherwise be silent; in that case we also emit to the `console`\n * and note that no logger is configured. When a logger is configured we trust it and never duplicate\n * to the console.\n */\nexport function createCriticalLogger(logger: Logger, loggerConfigured: boolean): CriticalLogger {\n  const dual =\n    (level: \"warn\" | \"error\") =>\n    (msg: string, meta?: Record<string, unknown>): void => {\n      logger[level](msg, meta); // the configured logger always sees it (a no-op if unset)\n      if (!loggerConfigured) {\n        // Misconfiguration safety net: a security/data-loss event must not be silent.\n        const note = { ...meta, loggerConfigured: false };\n        console[level](\n          `[commitcourier] ${msg} (no logger configured; pass \\`logger\\` to capture this)`,\n          note,\n        );\n      }\n    };\n  return { securityBlocked: dual(\"warn\"), dataLoss: dual(\"error\") };\n}\n","/**\n * Single-row delivery orchestration.\n *\n * Resolves the destination/key, signs, POSTs, records the ledger, and applies the state\n * transition for one claimed row. {@link deliverOne} is strictly fail-open: it never throws, so\n * a delivery-side failure cannot stop the dispatcher loop or reach the user's business path.\n */\nimport {\n  sign,\n  backoffMs,\n  parseRetryAfter,\n  onSuccess,\n  onFailure,\n  onPermanentFailure,\n  RelayError,\n} from \"../core/index\";\nimport type { OutboxRow, RelayConfig, Clock, SignatureHeaders } from \"../core/index\";\nimport type { DispatchStore, EndpointStore, NewDeliveryAttempt } from \"../store/store\";\nimport type { createHttpClient } from \"./http\";\nimport type { Sink, SinkResult } from \"../forward/index\";\nimport { secretFreeSummary } from \"./_error\";\nimport { createCriticalLogger } from \"./critical\";\nimport type { CriticalLogger } from \"./critical\";\n\n/** A single delivery outcome reported to {@link DeliveryHooks}. Carries no secret. */\nexport interface DeliveryEvent {\n  /** Outbox row id (also the signature `webhook-id`). */\n  id: string;\n  eventType: string;\n  /** 1-based attempt number this outcome corresponds to. */\n  attempt: number;\n  /** Registered endpoint id, or null for an inline `{ url, secret }` destination. */\n  endpointId: string | null;\n  /** Destination host only (no path/query/secret), or null when not resolved (e.g. bad target). */\n  host: string | null;\n  /** HTTP status, or null when no response was received (network/timeout/SSRF/pre-HTTP failure). */\n  status: number | null;\n  /** Secret-free failure summary, or null on success. */\n  error: string | null;\n  /** Wall-clock duration of the HTTP attempt in ms (0 for pre-HTTP failures). */\n  durationMs: number;\n}\n\n/**\n * Optional, fail-open delivery-outcome callbacks, each passed a secret-free {@link DeliveryEvent}\n * (never the payload or signing secret). Contract:\n *\n * - A hook fires only when the row's state transition actually committed. A worker that lost its\n *   lease to a visibility-timeout reclaim still records its ledger attempt but fires NO hook — the\n *   worker that owns the row does. So at most one hook fires per attempt, and a stale attempt fires\n *   none.\n * - At-least-once, not exactly-once: a retry fires `onRetry` again, and a redelivery after a crash\n *   can fire `onDelivered` more than once. Treat them as notifications keyed by id + attempt, not as\n *   the ledger (use the delivery-attempts ledger for that).\n * - Fail-open: an exception thrown by a hook is logged and swallowed so it can neither roll back the\n *   delivery state nor stall the dispatcher loop. Hooks run inline on the dispatch path, so keep them\n *   fast and offload slow work.\n */\nexport interface DeliveryHooks {\n  /** A 2xx response moved the row to `delivered`. */\n  onDelivered?: (event: DeliveryEvent) => void | Promise<void>;\n  /** The attempt failed but more remain; the row is back to `pending` with a backoff. */\n  onRetry?: (event: DeliveryEvent) => void | Promise<void>;\n  /** The attempt failed and exhausted `maxAttempts` (or hit a permanent failure); the row moved to `dead`. */\n  onDead?: (event: DeliveryEvent) => void | Promise<void>;\n}\n\n/** Secret-free context for the start of a delivery attempt, passed to a {@link DeliveryInstrument}. */\nexport interface DeliveryStart {\n  /** Outbox row id (also the signature `webhook-id`). */\n  id: string;\n  eventType: string;\n  /** 1-based attempt number. */\n  attempt: number;\n  endpointId: string | null;\n  /** Destination host if already known (inline rows), else null (registered endpoint, resolved later). */\n  host: string | null;\n}\n\n/**\n * Optional, fail-open instrumentation seam for tracing/metrics (the OpenTelemetry seam). Called once\n * just before a delivery attempt with secret-free start info; the returned finaliser, if any, is\n * called exactly once with the terminal {@link DeliveryEvent}. Designed so an implementation can\n * start a span on call and end it in the finaliser. Both the factory and the finaliser are wrapped\n * fail-open: a throw is logged and swallowed so it cannot stall the dispatcher loop.\n */\nexport type DeliveryInstrument = (\n  start: DeliveryStart,\n) => ((event: DeliveryEvent) => void) | undefined;\n\nexport interface DeliverDeps {\n  store: DispatchStore & EndpointStore;\n  http: ReturnType<typeof createHttpClient>;\n  config: RelayConfig;\n  clock: Clock;\n  /**\n   * Surfaces security (SSRF block) and data-loss (DLQ) events even when no logger is configured.\n   * Optional: when omitted it routes to `config.logger` as if a logger were configured (no console\n   * fallback). `createRelay` always supplies the misconfiguration-aware instance.\n   */\n  critical?: CriticalLogger;\n  hooks?: DeliveryHooks;\n  /** Optional tracing/metrics seam; see {@link DeliveryInstrument}. */\n  instrument?: DeliveryInstrument;\n  /**\n   * Delivery sink for `sink` transport. Required when\n   * `config.delivery.transport === \"sink\"`; `createRelay` fails fast if it is missing. Unused for the\n   * default `http` transport. Each handoff is bounded by `config.delivery.timeoutMs`.\n   */\n  sink?: Sink;\n}\n\ninterface Ctx {\n  row: OutboxRow;\n  deps: DeliverDeps;\n  /** Resolved critical-event logger (from `deps.critical`, else a configured-logger fallback). */\n  critical: CriticalLogger;\n  now: Date;\n  /** Resolved destination host (no path/secret), refined once the target URL is known. */\n  host: string | null;\n  /** Instrumentation finaliser for this delivery (from `deps.instrument`), or null. Called once. */\n  finish: ((event: DeliveryEvent) => void) | null;\n  /** One-shot guard so the instrumentation finaliser runs exactly once. */\n  settled: { done: boolean };\n}\n\n/** Parse the host from a URL without throwing (returns null on a malformed/absent URL). */\nfunction hostOf(url: string | null): string | null {\n  if (url == null) return null;\n  try {\n    return new URL(url).host;\n  } catch {\n    return null;\n  }\n}\n\ntype Resolved =\n  { ok: true; url: string; secrets: string[]; halfOpen: boolean } | { ok: false; error: string };\n\n/** Prefix the http client uses for an SSRF-blocked failure summary (e.g. `\"SSRF_BLOCKED:metadata\"`). */\nconst SSRF_BLOCKED_PREFIX = \"SSRF_BLOCKED:\";\n\n/** Only a 2xx response is a success; everything else (including no response) is a retry. */\nfunction isSuccess(status: number | null): boolean {\n  return status !== null && status >= 200 && status < 300;\n}\n\n/**\n * A permanent failure stops retries immediately. Only `410 Gone` qualifies in v1.1: the receiver is\n * telling us the destination no longer exists, so retrying wastes attempts and a registered endpoint\n * should be disabled.\n */\nfunction isPermanentFailure(status: number | null): boolean {\n  return status === 410;\n}\n\n/**\n * Pre-HTTP resolve errors that retrying can never fix, so the row goes straight to the DLQ instead of\n * burning the whole retry budget. `ENDPOINT_NOT_FOUND` / `ENDPOINT_DISABLED` are deliberately NOT here:\n * an admin can create or re-enable the endpoint, so those stay retryable.\n */\nfunction isPermanentResolveError(error: string): boolean {\n  return error === \"MISSING_SECRET\" || error === \"ENQUEUE_NO_TARGET\";\n}\n\n/**\n * Map a {@link SinkResult} onto success. A present `error` is always a\n * failure; otherwise a present `status` defers to the HTTP 2xx rule, and a bare result (no error, no\n * status) is a success.\n */\nfunction isSinkSuccess(r: SinkResult): boolean {\n  if (r.error != null) return false;\n  return r.status != null ? isSuccess(r.status) : true;\n}\n\n/**\n * Map a failed {@link SinkResult} onto a permanent failure. A present\n * `status` defers to the HTTP permanent rule (410); otherwise an explicit `retryable === false` sends\n * the row straight to the DLQ. Everything else stays retryable.\n */\nfunction isSinkPermanent(r: SinkResult): boolean {\n  return r.status != null ? isPermanentFailure(r.status) : r.retryable === false;\n}\n\n/**\n * Coerce a sink's resolved value to a {@link SinkResult}. A contract-violating adapter that resolves to\n * a non-object (undefined/null/string/number) is mapped to a retryable failure, so a stray value is\n * never silently read as success (data loss) and `.status`/`.error` are never read off a non-object.\n */\nfunction normalizeSinkResult(raw: unknown): SinkResult {\n  return raw != null && typeof raw === \"object\"\n    ? raw\n    : { error: \"SINK_BAD_RESULT\", retryable: true };\n}\n\n/**\n * Race a promise against a timeout (used to bound a sink handoff). On timeout the returned promise\n * rejects with a `SINK_TIMEOUT` error; the underlying promise is left to settle in the background, but\n * `Promise.race` keeps a reaction attached to it so a late rejection is never unhandled.\n */\nfunction withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n  let timer: ReturnType<typeof setTimeout>;\n  const timeout = new Promise<never>((_resolve, reject) => {\n    timer = setTimeout(() => {\n      reject(new Error(\"SINK_TIMEOUT\"));\n    }, ms);\n  });\n  return Promise.race([p, timeout]).finally(() => {\n    clearTimeout(timer);\n  });\n}\n\n/**\n * Resolve the destination URL and signing secret for a row (registered endpoint or inline).\n *\n * A disabled registered endpoint normally fails as `ENDPOINT_DISABLED`. With circuit-breaker\n * auto-recovery (`cooldownMs > 0`), once it has been disabled for at least `cooldownMs` (from\n * `disabled_at`) a delivery is let through as a half-open trial (`halfOpen: true`) so the caller can\n * re-activate it on success or re-arm the cooldown on failure. Note this is evaluated per row: under\n * the default `ordering: \"none\"` every currently-due `pending` row for the endpoint resolves\n * `halfOpen: true` in the same claim, so the trial is single only under `ordering: \"per-endpoint\"`.\n */\nasync function resolveTarget(\n  row: OutboxRow,\n  store: EndpointStore,\n  recovery: { cooldownMs: number; now: Date },\n): Promise<Resolved> {\n  if (row.endpointId != null) {\n    const ep = await store.findEndpoint(row.endpointId);\n    if (!ep) return { ok: false, error: \"ENDPOINT_NOT_FOUND\" };\n    // Dual-sign with the secondary key too during a rotation window (current key first).\n    const secrets = ep.secretSecondary == null ? [ep.secret] : [ep.secret, ep.secretSecondary];\n    if (ep.status === \"disabled\") {\n      const { cooldownMs, now } = recovery;\n      const dueForTrial =\n        cooldownMs > 0 &&\n        ep.disabledAt != null &&\n        ep.disabledAt.getTime() + cooldownMs <= now.getTime();\n      if (!dueForTrial) return { ok: false, error: \"ENDPOINT_DISABLED\" };\n      return { ok: true, url: ep.url, secrets, halfOpen: true };\n    }\n    return { ok: true, url: ep.url, secrets, halfOpen: false };\n  }\n  if (row.targetUrl != null) {\n    if (row.secretSnapshot == null) return { ok: false, error: \"MISSING_SECRET\" };\n    return { ok: true, url: row.targetUrl, secrets: [row.secretSnapshot], halfOpen: false };\n  }\n  return { ok: false, error: \"ENQUEUE_NO_TARGET\" };\n}\n\n/** Signature headers plus content-type and the optional idempotency key (never the secret). */\nfunction buildHeaders(sig: SignatureHeaders, row: OutboxRow): Record<string, string> {\n  const headers: Record<string, string> = { ...sig, \"content-type\": \"application/json\" };\n  if (row.idempotencyKey != null) headers[\"idempotency-key\"] = row.idempotencyKey;\n  return headers;\n}\n\n/** A ledger row for a failure that never reached HTTP (e.g. endpoint missing, signing error). */\nfunction noHttpAttempt(row: OutboxRow, error: string): NewDeliveryAttempt {\n  return {\n    outboxId: row.id,\n    attemptNo: row.attempts + 1,\n    requestHeaders: {},\n    responseStatus: null,\n    responseBodySnippet: null,\n    durationMs: 0,\n    error,\n  };\n}\n\n/** Build the secret-free {@link DeliveryEvent} for this attempt. */\nfunction eventFor(\n  ctx: Ctx,\n  status: number | null,\n  error: string | null,\n  durationMs: number,\n): DeliveryEvent {\n  return {\n    id: ctx.row.id,\n    eventType: ctx.row.eventType,\n    attempt: ctx.row.attempts + 1,\n    endpointId: ctx.row.endpointId,\n    host: ctx.host,\n    status,\n    error,\n    durationMs,\n  };\n}\n\n/**\n * Finalise the instrumentation seam exactly once with the terminal event (fail-open). A finaliser\n * error is logged and swallowed so it cannot stall the dispatcher loop.\n */\nfunction settle(ctx: Ctx, event: DeliveryEvent): void {\n  if (ctx.settled.done) return;\n  ctx.settled.done = true;\n  if (!ctx.finish) return;\n  try {\n    ctx.finish(event);\n  } catch (err) {\n    ctx.deps.config.logger.error(\"delivery instrumentation failed\", {\n      id: ctx.row.id,\n      error: secretFreeSummary(err),\n    });\n  }\n}\n\n/**\n * Invoke a delivery hook fail-open: a hook error is logged and swallowed, never propagated. Also\n * finalises the instrumentation seam with this terminal `event` — `fireHook` is reached exactly once\n * per delivery on every normal path, so the span/metric is settled with the real outcome here.\n */\nasync function fireHook(\n  ctx: Ctx,\n  hook: ((event: DeliveryEvent) => void | Promise<void>) | undefined,\n  event: DeliveryEvent,\n): Promise<void> {\n  settle(ctx, event);\n  if (!hook) return;\n  try {\n    await hook(event);\n  } catch (err) {\n    ctx.deps.config.logger.error(\"delivery hook failed\", {\n      id: ctx.row.id,\n      error: secretFreeSummary(err),\n    });\n  }\n}\n\n/**\n * A guarded transition matched no row: this worker lost its lease to a visibility-timeout reclaim and\n * another worker now owns the row. The ledger attempt is still recorded, but we must NOT fire\n * success/retry/dead hooks, endpoint-health/breaker updates, or dead-letter alarms — the winning\n * worker will. We still settle the instrumentation with the real attempt outcome (the HTTP/sink call\n * did happen) and log the skip so the race is observable rather than silent.\n */\nfunction noteStaleTransition(ctx: Ctx, event: DeliveryEvent): void {\n  settle(ctx, event);\n  ctx.deps.config.logger.info(\n    \"delivery transition skipped: row reclaimed by another worker (stale lease)\",\n    { id: ctx.row.id, endpointId: ctx.row.endpointId, attempt: ctx.row.attempts + 1 },\n  );\n}\n\n/**\n * Success: write the ledger row and move to `delivered` in one round trip (store guards on\n * `status = 'in_flight'` and on `locked_by`), then — only when the transition actually applied —\n * notify onDelivered. Returns whether the transition applied so the caller can gate endpoint-health /\n * half-open recovery the same way. A stale worker (lease reclaimed) records its ledger row but fires\n * no success side effects.\n */\nasync function applySuccess(\n  ctx: Ctx,\n  attempt: NewDeliveryAttempt,\n  event: DeliveryEvent,\n): Promise<boolean> {\n  const { transitionApplied } = await ctx.deps.store.completeAttempt(\n    attempt,\n    onSuccess(ctx.now),\n    ctx.row.lockedBy,\n  );\n  if (!transitionApplied) {\n    noteStaleTransition(ctx, event);\n    return false;\n  }\n  await fireHook(ctx, ctx.deps.hooks?.onDelivered, event);\n  return true;\n}\n\n/**\n * Surface a DLQ transition as a data-loss event: a message has reached the terminal `dead` state and\n * will never be delivered. Always routed through the critical logger so it is visible even when no\n * logger is configured (see {@link CriticalLogger}). The meta carries no secret.\n */\nfunction noteDeadLetter(ctx: Ctx, summary: string): void {\n  ctx.critical.dataLoss(\"message moved to the DLQ and is permanently lost\", {\n    id: ctx.row.id,\n    endpointId: ctx.row.endpointId,\n    eventType: ctx.row.eventType,\n    attempts: ctx.row.attempts + 1,\n    error: summary,\n  });\n}\n\n/**\n * Failure: write the ledger row and schedule the next retry or move to `dead` in one round trip\n * (guarded on `status = 'in_flight'` and `locked_by`), then — only when the transition actually\n * applied — fire the dead-letter alarm and notify onRetry/onDead. Returns whether the transition\n * applied so the caller can gate endpoint-health. A stale worker (lease reclaimed) records its ledger\n * row but fires no retry/dead side effects.\n */\n// eslint-disable-next-line max-params -- one optional Retry-After hint kept inline with the failure path\nasync function applyFailure(\n  ctx: Ctx,\n  attempt: NewDeliveryAttempt,\n  summary: string,\n  event: DeliveryEvent,\n  retryAfterMs?: number | null,\n): Promise<boolean> {\n  const { row, deps, now } = ctx;\n  const { retry } = deps.config;\n  const dead = row.attempts + 1 >= retry.maxAttempts;\n  // Honour a server-sent Retry-After when it exceeds our own backoff, clamped to retry.capMs so a\n  // hostile/buggy header cannot park a row indefinitely.\n  const base = backoffMs(row.attempts + 1, retry);\n  const backoff = Math.min(Math.max(base, retryAfterMs ?? 0), retry.capMs);\n  const { transitionApplied } = await deps.store.completeAttempt(\n    attempt,\n    onFailure(row, retry, now, summary, backoff),\n    row.lockedBy,\n  );\n  if (!transitionApplied) {\n    noteStaleTransition(ctx, event);\n    return false;\n  }\n  if (dead) noteDeadLetter(ctx, summary);\n  await fireHook(ctx, dead ? deps.hooks?.onDead : deps.hooks?.onRetry, event);\n  return true;\n}\n\n/**\n * Permanent failure: write the ledger row and move straight to `dead` without consuming the retry\n * budget, then notify onDead. `opts.disableEndpoint` (default true, the HTTP 410 Gone path) also\n * disables the registered endpoint (fail-open — a disable error is logged, never propagated). It is\n * passed `false` for a deterministic pre-HTTP failure (missing/invalid signing secret), where the\n * endpoint itself is not the problem and should stay enabled.\n */\n// eslint-disable-next-line max-params -- one optional endpoint-disable flag kept inline, mirroring applyFailure\nasync function applyPermanentFailure(\n  ctx: Ctx,\n  attempt: NewDeliveryAttempt,\n  summary: string,\n  event: DeliveryEvent,\n  opts: { disableEndpoint?: boolean } = {},\n): Promise<boolean> {\n  const { row, deps, now } = ctx;\n  const { transitionApplied } = await deps.store.completeAttempt(\n    attempt,\n    onPermanentFailure(row, summary),\n    row.lockedBy,\n  );\n  if (!transitionApplied) {\n    noteStaleTransition(ctx, event);\n    return false;\n  }\n  noteDeadLetter(ctx, summary);\n  if ((opts.disableEndpoint ?? true) && row.endpointId != null) {\n    try {\n      await deps.store.disableEndpoint(row.endpointId, now);\n    } catch (err) {\n      deps.config.logger.error(\"disableEndpoint on permanent failure failed\", {\n        id: row.id,\n        endpointId: row.endpointId,\n        error: secretFreeSummary(err),\n      });\n    }\n  }\n  await fireHook(ctx, deps.hooks?.onDead, event);\n  return true;\n}\n\n/**\n * Circuit breaker (fail-open): on a real HTTP outcome to a registered endpoint, reset or increment\n * its consecutive-failure counter; the store auto-disables the endpoint when the count reaches the\n * configured threshold. No-op for inline destinations, or when the feature is off\n * (`failureThreshold === 0`). A counter-update error is logged and swallowed so it can never stall a\n * delivery — the row's own transition has already been applied.\n */\nasync function noteEndpointHealth(ctx: Ctx, success: boolean): Promise<void> {\n  const { row, deps, now } = ctx;\n  const threshold = deps.config.circuitBreaker.failureThreshold;\n  if (row.endpointId == null || threshold <= 0) return;\n  try {\n    if (success) await deps.store.noteEndpointSuccess(row.endpointId);\n    else await deps.store.noteEndpointFailure(row.endpointId, now, threshold);\n  } catch (err) {\n    deps.config.logger.error(\"circuit-breaker endpoint health update failed\", {\n      id: row.id,\n      endpointId: row.endpointId,\n      error: secretFreeSummary(err),\n    });\n  }\n}\n\n/**\n * Half-open recovery outcome (fail-open). A registered endpoint disabled past its cooldown was let\n * through as a trial delivery: on success re-activate it (`status = 'active'`, counter reset); on\n * failure re-arm the cooldown (refresh `disabled_at = now`) so it stays disabled and the next trial\n * waits another `cooldownMs` instead of retrying every attempt. Under `ordering: \"none\"` several of\n * the endpoint's due rows may run this concurrently (each call is idempotent: reactivate/disable\n * converge), so the redundant writes are harmless. Any store error is logged and swallowed — the\n * row's own transition has already been applied, so recovery never stalls a delivery.\n */\nasync function noteHalfOpenOutcome(ctx: Ctx, success: boolean): Promise<void> {\n  const { row, deps, now } = ctx;\n  if (row.endpointId == null) return;\n  try {\n    if (success) await deps.store.reactivateEndpoint(row.endpointId);\n    else await deps.store.disableEndpoint(row.endpointId, now);\n  } catch (err) {\n    deps.config.logger.error(\"circuit-breaker half-open recovery update failed\", {\n      id: row.id,\n      endpointId: row.endpointId,\n      error: secretFreeSummary(err),\n    });\n  }\n}\n\n/** Run the POST, then write the ledger row and apply the success/failure transition in one round trip. */\nasync function deliverHttp(\n  ctx: Ctx,\n  url: string,\n  secrets: string[],\n  halfOpen: boolean,\n): Promise<void> {\n  const { row, now } = ctx;\n  // Refine the instrumentation host now that the destination is resolved (registered endpoints\n  // only have their URL after findEndpoint).\n  ctx.host = hostOf(url);\n  const body = JSON.stringify(row.payload);\n  const sig = await sign({\n    id: row.id,\n    timestampSec: Math.floor(now.getTime() / 1000),\n    body,\n    secrets,\n  });\n  const headers = buildHeaders(sig, row);\n  const res = await ctx.deps.http.post({ url, headers, body });\n  const success = isSuccess(res.status);\n  const failure = res.error ?? `HTTP ${String(res.status)}`;\n  const attempt: NewDeliveryAttempt = {\n    outboxId: row.id,\n    attemptNo: row.attempts + 1,\n    requestHeaders: headers,\n    responseStatus: res.status,\n    responseBodySnippet: res.bodySnippet,\n    durationMs: res.durationMs,\n    error: success ? null : failure,\n  };\n  if (success) {\n    const applied = await applySuccess(\n      ctx,\n      attempt,\n      eventFor(ctx, res.status, null, res.durationMs),\n    );\n    // Only when this worker actually owned the transition: a successful half-open trial recovers the\n    // endpoint, otherwise this is the normal health reset. A stale worker touches neither.\n    if (applied) {\n      if (halfOpen) await noteHalfOpenOutcome(ctx, true);\n      else await noteEndpointHealth(ctx, true);\n    }\n    return;\n  }\n  const event = eventFor(ctx, res.status, failure, res.durationMs);\n  // An SSRF block is a security event: the destination resolved to a blocked address and the POST was\n  // refused. Surface it every attempt (it stays retryable) so it is never silent on a misconfig.\n  if (res.error?.startsWith(SSRF_BLOCKED_PREFIX)) {\n    ctx.critical.securityBlocked(\n      \"delivery refused: destination resolved to a blocked address (SSRF)\",\n      {\n        id: row.id,\n        endpointId: row.endpointId,\n        host: ctx.host,\n        reason: res.error.slice(SSRF_BLOCKED_PREFIX.length),\n      },\n    );\n  }\n  if (isPermanentFailure(res.status)) {\n    // 410 Gone already disables the endpoint via applyPermanentFailure (which also re-arms the\n    // cooldown by refreshing disabled_at); the breaker / half-open re-arm would be redundant.\n    await applyPermanentFailure(ctx, attempt, failure, event);\n    return;\n  }\n  const retryAfterMs = parseRetryAfter(res.retryAfter, now.getTime());\n  const applied = await applyFailure(ctx, attempt, failure, event, retryAfterMs);\n  // Only when this worker actually owned the transition: a failed half-open trial re-arms the cooldown\n  // (keep it disabled), otherwise note the failure so the breaker can trip. They are mutually\n  // exclusive: a half-open row's endpoint is already disabled. A stale worker touches neither.\n  if (applied) {\n    if (halfOpen) await noteHalfOpenOutcome(ctx, false);\n    else await noteEndpointHealth(ctx, false);\n  }\n}\n\n/**\n * `sink` transport: hand the row to the configured sink, then write the\n * ledger and apply the transition atomically. No signing, SSRF or circuit breaker — those are delegated\n * to the sink/SaaS. A throwing adapter is normalised to a retryable failure (fail-open). Signing/SSRF\n * are skipped entirely so a sink delivery never resolves a target URL or secret. The handoff is bounded\n * by `delivery.timeoutMs` so a hung adapter cannot pin a dispatcher slot, but the adapter is still\n * responsible for its own latency/timeout.\n */\nasync function deliverSink(ctx: Ctx, sink: Sink | undefined): Promise<void> {\n  const { row } = ctx;\n  if (sink == null) {\n    // createRelay fails fast on a missing sink, so this is unreachable in practice. Stay never-throw:\n    // record a deterministic permanent failure rather than retrying a config error to exhaustion.\n    const attempt = noHttpAttempt(row, \"SINK_NOT_CONFIGURED\");\n    const event = eventFor(ctx, null, \"SINK_NOT_CONFIGURED\", 0);\n    await applyPermanentFailure(ctx, attempt, \"SINK_NOT_CONFIGURED\", event, {\n      disableEndpoint: false,\n    });\n    return;\n  }\n  const started = ctx.now.getTime();\n  let result: SinkResult;\n  try {\n    // Bound the handoff by delivery.timeoutMs so a hung adapter cannot pin a dispatcher slot; on\n    // timeout this rejects with SINK_TIMEOUT and is normalised to a retryable failure. The adapter is\n    // still responsible for its own latency/timeout (the SaaS call may keep running). A non-SinkResult\n    // return is coerced to a retryable failure (never silently read as success).\n    result = normalizeSinkResult(\n      await withTimeout(\n        sink.deliver({\n          id: row.id,\n          eventType: row.eventType,\n          payload: row.payload,\n          idempotencyKey: row.idempotencyKey ?? undefined,\n          endpointId: row.endpointId,\n        }),\n        ctx.deps.config.delivery.timeoutMs,\n      ),\n    );\n  } catch (err) {\n    // A throwing/timed-out adapter is normalised to a retryable failure (fail-open, never crash the loop).\n    result = { error: secretFreeSummary(err), retryable: true };\n  }\n  const durationMs = ctx.deps.clock().getTime() - started;\n  const status = result.status ?? null;\n  const success = isSinkSuccess(result);\n  const attempt: NewDeliveryAttempt = {\n    outboxId: row.id,\n    attemptNo: row.attempts + 1,\n    // No request headers / destination URL in sink mode; record the provider id for correlation only.\n    requestHeaders:\n      result.providerMessageId != null ? { \"provider-message-id\": result.providerMessageId } : {},\n    responseStatus: status,\n    responseBodySnippet: null,\n    durationMs,\n    error: success ? null : (result.error ?? \"SINK_FAILED\"),\n  };\n  if (success) {\n    await applySuccess(ctx, attempt, eventFor(ctx, status, null, durationMs));\n    return;\n  }\n  const summary = result.error ?? \"SINK_FAILED\";\n  const event = eventFor(ctx, status, summary, durationMs);\n  // No circuit breaker / endpoint health in sink mode: the destination is a single SaaS, not a\n  // per-endpoint URL, so disableEndpoint is always false.\n  if (isSinkPermanent(result)) {\n    await applyPermanentFailure(ctx, attempt, summary, event, { disableEndpoint: false });\n    return;\n  }\n  await applyFailure(ctx, attempt, summary, event);\n}\n\n/**\n * Deliver one claimed row, writing the ledger and applying the transition. Never throws: a\n * delivery-side failure is logged and persisted as a retryable failure (fail-open).\n *\n * The ledger write and the transition are a single atomic `completeAttempt`, so a failure leaves\n * neither written — normally exactly one ledger row is produced per invocation with no partial state.\n * The one exception is a post-commit driver error: if `completeAttempt` COMMITS but the driver then\n * throws while returning the affected-row count (a connection blip after commit), the outer catch's\n * failover write records a second ledger row for the same `attempt_no` (the transition is a no-op by\n * then, since the row already left `in_flight`). That is harmless and consistent with at-least-once,\n * but the ledger can hold a duplicate attempt row in that rare window.\n */\nexport async function deliverOne(row: OutboxRow, deps: DeliverDeps): Promise<void> {\n  const host = hostOf(row.targetUrl);\n  let finish: ((event: DeliveryEvent) => void) | null = null;\n  if (deps.instrument) {\n    try {\n      finish =\n        deps.instrument({\n          id: row.id,\n          eventType: row.eventType,\n          attempt: row.attempts + 1,\n          endpointId: row.endpointId,\n          host,\n        }) ?? null;\n    } catch (err) {\n      deps.config.logger.error(\"delivery instrument start failed\", {\n        id: row.id,\n        error: secretFreeSummary(err),\n      });\n    }\n  }\n  // Without an explicit critical logger, route to the configured logger (no console fallback);\n  // createRelay always injects the misconfiguration-aware instance.\n  const critical = deps.critical ?? createCriticalLogger(deps.config.logger, true);\n  const ctx: Ctx = {\n    row,\n    deps,\n    critical,\n    now: deps.clock(),\n    host,\n    finish,\n    settled: { done: false },\n  };\n  try {\n    // `sink` transport bypasses target/secret resolution, signing and SSRF entirely.\n    if (deps.config.delivery.transport === \"sink\") {\n      await deliverSink(ctx, deps.sink);\n      return;\n    }\n    const resolved = await resolveTarget(row, deps.store, {\n      cooldownMs: deps.config.circuitBreaker.cooldownMs,\n      now: ctx.now,\n    });\n    if (!resolved.ok) {\n      const attempt = noHttpAttempt(row, resolved.error);\n      const event = eventFor(ctx, null, resolved.error, 0);\n      // A row that can never resolve a target/secret will never succeed: send it straight to the DLQ\n      // instead of retrying maxAttempts times. The endpoint is not at fault, so do not disable it.\n      if (isPermanentResolveError(resolved.error)) {\n        await applyPermanentFailure(ctx, attempt, resolved.error, event, {\n          disableEndpoint: false,\n        });\n      } else {\n        await applyFailure(ctx, attempt, resolved.error, event);\n      }\n      return;\n    }\n    await deliverHttp(ctx, resolved.url, resolved.secrets, resolved.halfOpen);\n  } catch (err) {\n    const summary = secretFreeSummary(err);\n    deps.config.logger.error(\"deliverOne failed\", { id: row.id, error: summary });\n    // A CONFIG_INVALID here is a deterministic signing/secret problem (empty or malformed secret, an\n    // undecryptable registered-endpoint secret): retrying cannot fix it, so go straight to dead. Any\n    // other error (a transient DB failure, etc.) stays retryable.\n    const permanent = err instanceof RelayError && err.code === \"CONFIG_INVALID\";\n    try {\n      const attempt = noHttpAttempt(row, summary);\n      const event = eventFor(ctx, null, summary, 0);\n      if (permanent) {\n        await applyPermanentFailure(ctx, attempt, summary, event, { disableEndpoint: false });\n      } else {\n        await applyFailure(ctx, attempt, summary, event);\n      }\n    } catch (inner) {\n      // Even the failover write failed; swallow so the dispatcher loop keeps running.\n      deps.config.logger.error(\"deliverOne failover record failed\", {\n        id: row.id,\n        error: secretFreeSummary(inner),\n      });\n    }\n  } finally {\n    // Safety net: every normal path settles via fireHook; only an unrecoverable double-write failure\n    // reaches here unsettled. Finalise so an instrumentation span is never leaked.\n    settle(ctx, eventFor(ctx, null, \"delivery did not settle\", 0));\n  }\n}\n","/**\n * Background delivery loop.\n *\n * The loop keeps the p-limit queue continuously filled rather than draining one batch at a time:\n * it claims due rows up to the free capacity, dispatches them without waiting, and claims again as\n * soon as slots free. So a single slow delivery never stalls the others (no batch barrier). Running\n * many loops (same or different processes) stays safe: single delivery is guaranteed by SKIP LOCKED\n * and at-least-once by visibility-timeout reclaim. The loop is fail-open — a delivery or DB error is\n * logged and never stops iteration.\n */\nimport { hostname } from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport pLimit, { type LimitFunction } from \"p-limit\";\nimport { RelayError } from \"../core/index\";\nimport type { OutboxRow, RelayConfig } from \"../core/index\";\nimport type { DispatchStore } from \"../store/store\";\nimport type { WakeSignal } from \"../accelerator/accelerator\";\n\nexport interface DispatcherOptions {\n  /** Max concurrent deliveries. Default 8. */\n  concurrency?: number;\n  /** Idle poll interval in ms when there is no backlog. Default 1000. */\n  pollIntervalMs?: number;\n  /** Visibility timeout: reclaim in_flight rows older than this. Default 300000 (5 min). */\n  reclaimAfterMs?: number;\n  /** How often to run the reclaim sweep. Default min(reclaimAfterMs, 30000). */\n  reclaimIntervalMs?: number;\n  /** Upper bound on rows claimed-but-not-finished at once (in-flight buffer). Default concurrency * 2. */\n  batchSize?: number;\n  /**\n   * Delivery ordering. `\"none\"` (default) claims globally oldest-first with full concurrency.\n   * `\"per-endpoint\"` opts into per-endpoint FIFO: at most one in-flight delivery per registered\n   * endpoint, strictly in arrival order (a failed head row holds the line until it is delivered or\n   * dead). Inline (`{ url, secret }`) deliveries are unaffected.\n   */\n  ordering?: \"none\" | \"per-endpoint\";\n}\n\n/** Options for a single {@link Dispatcher.runOnce} drain. */\nexport interface RunOnceOptions {\n  /**\n   * Sweep stale `in_flight` locks back to `pending` before draining (visibility-timeout reclaim), so\n   * rows orphaned by a previous crashed invocation are picked up. Default true.\n   */\n  reclaim?: boolean;\n  /** Upper bound on rows processed in this drain. Default unbounded (drain everything currently due). */\n  maxRows?: number;\n}\n\nexport interface Dispatcher {\n  /** Start the loop. Resolves immediately; iteration continues in the background. */\n  start(): Promise<void>;\n  /** Graceful stop: halt new claims and wait for in-flight deliveries to finish. */\n  stop(): Promise<void>;\n  isRunning(): boolean;\n  /**\n   * Drain the queue once and return, instead of running the continuous loop — for serverless/cron\n   * deployments that cannot host a long-lived process. Claims due rows in waves (up to `batchSize`,\n   * honouring the configured `ordering`), delivers them with the configured `concurrency`, and\n   * resolves once the queue is empty (or `maxRows` is reached) and every dispatched delivery has\n   * settled. Fail-open like the loop: a claim/delivery error is logged, never thrown. Rejects only if\n   * called while the continuous loop is running (use one or the other). Returns the number of rows\n   * dispatched this run (regardless of per-row outcome).\n   */\n  runOnce(options?: RunOnceOptions): Promise<{ processed: number }>;\n}\n\ninterface ResolvedOptions {\n  concurrency: number;\n  pollIntervalMs: number;\n  reclaimAfterMs: number;\n  reclaimIntervalMs: number;\n  batchSize: number;\n  ordering: \"none\" | \"per-endpoint\";\n}\n\nfunction resolveOptions(o: DispatcherOptions = {}): ResolvedOptions {\n  const concurrency = o.concurrency ?? 8;\n  const reclaimAfterMs = o.reclaimAfterMs ?? 300_000;\n  const resolved: ResolvedOptions = {\n    concurrency,\n    pollIntervalMs: o.pollIntervalMs ?? 1000,\n    reclaimAfterMs,\n    reclaimIntervalMs: o.reclaimIntervalMs ?? Math.min(reclaimAfterMs, 30_000),\n    batchSize: o.batchSize ?? concurrency * 2,\n    ordering: o.ordering ?? \"none\",\n  };\n  validateOptions(resolved);\n  return resolved;\n}\n\n/**\n * Fail-fast on misconfiguration. In particular a non-positive batchSize would silently deliver\n * nothing (claimDue with limit 0 always returns []), so reject it loudly rather than stall forever.\n */\nfunction validateOptions(resolved: ResolvedOptions): void {\n  // `Number.isInteger` rejects NaN/Infinity/fractions; the bound rejects out-of-range values. A bare\n  // `>= 0` comparison would have let Infinity and fractional millisecond counts through.\n  requireInt(\"dispatcher concurrency\", resolved.concurrency, 1);\n  requireInt(\"dispatcher batchSize\", resolved.batchSize, 1);\n  requireInt(\"dispatcher pollIntervalMs\", resolved.pollIntervalMs, 0);\n  requireInt(\"dispatcher reclaimAfterMs\", resolved.reclaimAfterMs, 0);\n  requireInt(\"dispatcher reclaimIntervalMs\", resolved.reclaimIntervalMs, 0);\n}\n\nfunction fail(message: string): never {\n  throw new RelayError(\"CONFIG_INVALID\", message);\n}\n\n/** Reject a setting unless it is a finite integer at or above `min`. */\nfunction requireInt(name: string, value: number, min: number): void {\n  if (!(Number.isInteger(value) && value >= min)) {\n    fail(`${name} must be a finite integer >= ${String(min)}, got ${String(value)}`);\n  }\n}\n\n/**\n * At-least-once correctness rests on an unenforced invariant: the visibility timeout\n * (`reclaimAfterMs`) must comfortably exceed the worst-case time a claimed row stays `in_flight`. A\n * row is claimed (and its `locked_at` stamped) up front, but with a claim buffer (`batchSize` above\n * `concurrency`) it can wait in the p-limit queue for up to `ceil(batchSize/concurrency)` delivery\n * waves before its HTTP even starts — so the worst case is `timeoutMs * ceil(batchSize/concurrency)`,\n * not `timeoutMs`. If `reclaimAfterMs` does not exceed that (×1.5 margin), a still-queued row can be\n * reclaimed and double-delivered. Dangerous-but-valid config (like the SSRF warnings in core), so\n * warn rather than fail.\n */\nfunction warnIfReclaimTooTight(opts: ResolvedOptions, config: RelayConfig): void {\n  const { timeoutMs } = config.delivery;\n  const bufferWaves = Math.ceil(opts.batchSize / opts.concurrency);\n  const safeFloor = timeoutMs * 1.5 * bufferWaves;\n  if (opts.reclaimAfterMs <= safeFloor) {\n    config.logger.warn(\n      \"dispatcher reclaimAfterMs is not safely above the worst-case in-flight time (delivery.timeoutMs scaled by the claim buffer batchSize/concurrency); an in-flight delivery may be reclaimed and double-delivered\",\n      {\n        reclaimAfterMs: opts.reclaimAfterMs,\n        timeoutMs,\n        batchSize: opts.batchSize,\n        concurrency: opts.concurrency,\n      },\n    );\n  }\n}\n\n/** Worker identity for `locked_by`: helps identify stuck locks across instances. */\nfunction makeLockedBy(): string {\n  return `${hostname()}:${String(process.pid)}:${randomBytes(4).toString(\"hex\")}`;\n}\n\n/** Everything the continuous dispatch loop needs, bundled so it can live at module scope. */\ninterface LoopCtx {\n  store: DispatchStore;\n  deliver: (row: OutboxRow) => Promise<void>;\n  config: RelayConfig;\n  opts: ResolvedOptions;\n  limit: LimitFunction;\n  lockedBy: string;\n  active: () => boolean;\n  inFlight: Set<Promise<void>>;\n  sleep: (ms: number) => Promise<void>;\n  /** Wake-aware idle sleep: returns early when an accelerator wake arrives (see createDispatcher). */\n  idleSleep: (ms: number) => Promise<void>;\n}\n\n/** One-shot visibility-timeout reclaim (fail-open): used by runOnce before draining. */\nasync function reclaimQuietly(\n  store: DispatchStore,\n  opts: ResolvedOptions,\n  config: RelayConfig,\n): Promise<void> {\n  try {\n    await store.reclaimStuck({ reclaimAfterMs: opts.reclaimAfterMs, now: config.clock() });\n  } catch (err) {\n    config.logger.error(\"dispatcher reclaim failed\", { error: String(err) });\n  }\n}\n\n/**\n * The continuous dispatch loop: keep the in-flight buffer filled (claim up to free capacity,\n * dispatch without awaiting, claim again as slots free), reclaiming on a throttle. Drains all\n * in-flight deliveries before returning so stop() is graceful. Fail-open throughout.\n */\nasync function runLoop(ctx: LoopCtx): Promise<void> {\n  const { store, deliver, config, opts, limit, lockedBy, active, inFlight, sleep, idleSleep } = ctx;\n  const { logger, clock } = config;\n  let lastReclaimAt = 0;\n  // Adaptive idle backoff: when the queue is empty, start near-immediate and double up to\n  // pollIntervalMs, so the first row after an idle period is picked up with low latency without\n  // busy-spinning while quiet. Reset to the floor whenever work is found.\n  const minIdleMs = Math.min(50, opts.pollIntervalMs);\n  let idleMs = minIdleMs;\n\n  const freeCapacity = (): number => opts.batchSize - (limit.activeCount + limit.pendingCount);\n  const schedule = (row: OutboxRow): void => {\n    // deliver is contracted not to throw; swallow any violation so a rejected promise can never\n    // poison `Promise.race`/`allSettled` over inFlight and stop the loop (fail-open).\n    const p = limit(() => deliver(row))\n      .catch((err: unknown) => {\n        logger.error(\"dispatcher delivery threw\", { id: row.id, error: String(err) });\n      })\n      .finally(() => inFlight.delete(p));\n    inFlight.add(p);\n  };\n  const maybeReclaim = async (): Promise<void> => {\n    const now = clock();\n    if (now.getTime() - lastReclaimAt < opts.reclaimIntervalMs) return;\n    lastReclaimAt = now.getTime();\n    try {\n      await store.reclaimStuck({ reclaimAfterMs: opts.reclaimAfterMs, now });\n    } catch (err) {\n      logger.error(\"dispatcher reclaim failed\", { error: String(err) });\n    }\n  };\n\n  while (active()) {\n    await maybeReclaim();\n    if (!active()) break;\n    const capacity = freeCapacity();\n    if (capacity <= 0) {\n      // Buffer full: wait for any in-flight delivery to finish (inFlight is non-empty here; the\n      // sleep is a defensive fallback only), then re-evaluate.\n      if (inFlight.size > 0) await Promise.race([...inFlight]);\n      else await sleep(opts.pollIntervalMs);\n      continue;\n    }\n    let rows: OutboxRow[];\n    try {\n      rows = await store.claimDue({\n        limit: capacity,\n        lockedBy,\n        now: clock(),\n        ordering: opts.ordering,\n      });\n    } catch (err) {\n      logger.error(\"dispatcher claim failed\", { error: String(err) });\n      await sleep(opts.pollIntervalMs);\n      continue;\n    }\n    if (rows.length === 0) {\n      // Nothing due: wait the current backoff, then lengthen it up to the poll-interval cap. The\n      // wait is wake-aware — an accelerator NOTIFY cuts it short so a freshly enqueued row is picked\n      // up at once instead of after the backoff.\n      await idleSleep(idleMs);\n      idleMs = Math.min(idleMs * 2, opts.pollIntervalMs);\n      continue;\n    }\n    idleMs = minIdleMs; // work found: reset the idle backoff to its floor.\n    // deliver never throws; schedule without awaiting and loop to claim more as slots free.\n    for (const row of rows) schedule(row);\n  }\n  await Promise.allSettled([...inFlight]); // graceful drain\n}\n\nexport function createDispatcher(deps: {\n  store: DispatchStore;\n  deliver: (row: OutboxRow) => Promise<void>;\n  config: RelayConfig;\n  options?: DispatcherOptions;\n  /**\n   * Optional accelerator subscription. When present, the dispatcher subscribes on\n   * `start()` and unsubscribes on `stop()`; an incoming wake cuts the idle backoff short so a\n   * freshly enqueued row is dispatched with low latency. Omitted keeps pure adaptive polling.\n   */\n  wakeSignal?: WakeSignal;\n}): Dispatcher {\n  const { store, deliver, config, wakeSignal } = deps;\n  const opts = resolveOptions(deps.options);\n  warnIfReclaimTooTight(opts, config);\n  const limit = pLimit(opts.concurrency);\n  const lockedBy = makeLockedBy();\n\n  const state = { running: false };\n  // Read through a function so control-flow analysis cannot narrow the flag across `await`\n  // (stop() flips it from another closure, which the analyzer cannot see).\n  const active = (): boolean => state.running;\n  const inFlight = new Set<Promise<void>>();\n  let loopPromise: Promise<void> | null = null;\n  let sleepAbort: AbortController | null = null;\n\n  /** Interruptible sleep so stop() can cut a poll-interval wait short. */\n  const sleep = (ms: number): Promise<void> =>\n    new Promise((resolve) => {\n      const ac = new AbortController();\n      sleepAbort = ac;\n      const timer = setTimeout(() => {\n        sleepAbort = null;\n        resolve();\n      }, ms);\n      ac.signal.addEventListener(\n        \"abort\",\n        () => {\n          clearTimeout(timer);\n          sleepAbort = null;\n          resolve();\n        },\n        { once: true },\n      );\n    });\n\n  // --- Accelerator wake. `wake()` latches and aborts the active idle sleep so the next iteration\n  // claims at once. The latch closes the race where a wake arrives between an empty claim and the\n  // start of the next idle sleep: idleSleep consumes the latch synchronously before it waits. stop()\n  // also aborts the idle sleep so a graceful stop is not delayed by the backoff.\n  let wakePending = false;\n  let idleAbort: AbortController | null = null;\n  const wake = (): void => {\n    wakePending = true;\n    idleAbort?.abort();\n  };\n  const idleSleep = (ms: number): Promise<void> =>\n    new Promise((resolve) => {\n      if (wakePending) {\n        wakePending = false;\n        resolve();\n        return;\n      }\n      const ac = new AbortController();\n      idleAbort = ac;\n      const finish = (): void => {\n        if (idleAbort === ac) idleAbort = null;\n        clearTimeout(timer);\n        resolve();\n      };\n      const timer = setTimeout(finish, ms);\n      ac.signal.addEventListener(\n        \"abort\",\n        () => {\n          wakePending = false;\n          finish();\n        },\n        { once: true },\n      );\n    });\n\n  let unsubscribe: Promise<() => void> | null = null;\n\n  return {\n    start() {\n      if (!state.running) {\n        state.running = true;\n        loopPromise = runLoop({\n          store,\n          deliver,\n          config,\n          opts,\n          limit,\n          lockedBy,\n          active,\n          inFlight,\n          sleep,\n          idleSleep,\n        });\n        if (wakeSignal) {\n          // Subscribe in the background; a failed subscription is fail-open (polling still runs).\n          unsubscribe = wakeSignal(wake).catch((err: unknown) => {\n            config.logger.error(\"dispatcher wake subscribe failed\", { error: String(err) });\n            return () => {};\n          });\n        }\n      }\n      return Promise.resolve();\n    },\n\n    async stop() {\n      state.running = false;\n      sleepAbort?.abort();\n      idleAbort?.abort();\n      await loopPromise;\n      loopPromise = null;\n      if (unsubscribe) {\n        const off = await unsubscribe;\n        unsubscribe = null;\n        try {\n          off();\n        } catch (err) {\n          config.logger.error(\"dispatcher wake unsubscribe failed\", { error: String(err) });\n        }\n      }\n    },\n\n    isRunning() {\n      return state.running;\n    },\n\n    async runOnce(runOptions) {\n      // This guard is per-INSTANCE only: it catches the common mistake of driving one dispatcher with\n      // both start() and runOnce(). It cannot see a resident loop on another dispatcher instance or\n      // process (e.g. relay.dispatchOnce() builds a fresh dispatcher per call, and two of them can\n      // overlap), and it need not — overlapping drains stay safe because claimDue's FOR UPDATE SKIP\n      // LOCKED guarantees single-claim. So the throw prevents a self-inflicted footgun, not all overlap.\n      if (state.running) {\n        throw new RelayError(\n          \"CONFIG_INVALID\",\n          \"dispatcher.runOnce cannot run while the continuous loop is active; use start()/stop() or runOnce(), not both\",\n        );\n      }\n      const { logger, clock } = config;\n      const cap = runOptions?.maxRows ?? Infinity;\n      if (runOptions?.reclaim ?? true) await reclaimQuietly(store, opts, config);\n      const inFlightRun = new Set<Promise<void>>();\n      // deliver never throws; swallow any contract violation so one rejection cannot poison the\n      // Promise.race/allSettled below (fail-open, mirrors the continuous loop's schedule()).\n      const schedule = (row: OutboxRow): void => {\n        const p = limit(() => deliver(row))\n          .catch((err: unknown) => {\n            logger.error(\"dispatcher delivery threw\", { id: row.id, error: String(err) });\n          })\n          .finally(() => inFlightRun.delete(p));\n        inFlightRun.add(p);\n      };\n      let processed = 0;\n      while (processed < cap) {\n        // Subtract the rows still in flight so claimed-but-unfinished never exceeds batchSize —\n        // matching the continuous loop's freeCapacity bound. Claiming a full batchSize regardless\n        // would let the buffer reach ~2x batchSize, stretching the worst-case in-flight time past\n        // what warnIfReclaimTooTight models (reclaim + double-delivery risk under a tight\n        // reclaimAfterMs). The wait loop below guarantees at least one free slot here.\n        const want = Math.min(opts.batchSize - inFlightRun.size, cap - processed);\n        let rows: OutboxRow[];\n        try {\n          rows = await store.claimDue({\n            limit: want,\n            lockedBy,\n            now: clock(),\n            ordering: opts.ordering,\n          });\n        } catch (err) {\n          logger.error(\"dispatcher claim failed\", { error: String(err) });\n          break;\n        }\n        if (rows.length === 0) break; // queue drained\n        for (const row of rows) {\n          schedule(row);\n          processed++;\n        }\n        // Bound the claim buffer: wait for a slot before claiming the next wave.\n        while (inFlightRun.size >= opts.batchSize) await Promise.race([...inFlightRun]);\n      }\n      await Promise.allSettled([...inFlightRun]); // drain before returning\n      return { processed };\n    },\n  };\n}\n","/**\n * Table/status names, list limits, and their clamps — the leaf constants the rest of the SQL\n * plumbing builds on. No dependency on other store modules.\n */\nimport type { Status } from \"../../core/index\";\nimport initSql from \"./001_init.sql\";\n\nexport const OUTBOX_TABLE = \"webhook_outbox\";\nexport const ATTEMPTS_TABLE = \"webhook_delivery_attempts\";\nexport const ENDPOINTS_TABLE = \"webhook_endpoints\";\n\n/** Tables whose absence makes the store non-functional (diagnose reports ok:false). */\nexport const CORE_TABLES = [OUTBOX_TABLE, ATTEMPTS_TABLE] as const;\n/** All tables, including the optional registered-endpoint table. */\nexport const ALL_TABLES = [OUTBOX_TABLE, ATTEMPTS_TABLE, ENDPOINTS_TABLE] as const;\n\n/**\n * The canonical DDL. Embedded into the bundle as a string at build time (esbuild `text` loader),\n * so there is no runtime file I/O — this survives both ESM/CJS output and downstream re-bundling.\n */\nexport function loadInitSql(): string {\n  return initSql;\n}\n\n/** Every lifecycle status, used to zero-fill {@link \"./query-builders\".countsFromRows} (mirrors the DDL CHECK). */\nexport const ALL_STATUSES = [\n  \"pending\",\n  \"in_flight\",\n  \"delivered\",\n  \"dead\",\n  \"observed\",\n  \"cancelled\",\n] as const satisfies readonly Status[];\n\n/** Statuses a `prune` is allowed to delete (never `pending`/`in_flight`). */\nexport const PRUNABLE_STATUSES = [\"delivered\", \"dead\", \"cancelled\", \"observed\"] as const;\n/** Default statuses pruned when the caller does not specify (keeps `observed` for audit). */\nexport const DEFAULT_PRUNE_STATUSES: Status[] = [\"delivered\", \"dead\", \"cancelled\"];\n\n/** Default cap on a single `replay` call; the admin layer applies it when no explicit limit is set. */\nexport const REPLAY_DEFAULT_LIMIT = 1_000;\n/** Hard ceiling on one `replay` call so a filter can never fan out into an unbounded mass re-send. */\nexport const REPLAY_MAX_LIMIT = 10_000;\n\n/** Clamp a requested replay limit into `(0, REPLAY_MAX_LIMIT]`, defaulting when absent/invalid. */\nexport function clampReplayLimit(limit: number | undefined): number {\n  if (limit === undefined || !Number.isFinite(limit) || limit <= 0) return REPLAY_DEFAULT_LIMIT;\n  return Math.min(Math.floor(limit), REPLAY_MAX_LIMIT);\n}\n\n/** Default rows deleted per `prune` call when the caller omits `limit`. */\nexport const PRUNE_DEFAULT_LIMIT = 10_000;\n/** Hard ceiling on one `prune` batch so a single call cannot delete (and lock) an unbounded set. */\nexport const PRUNE_MAX_LIMIT = 100_000;\n\n/** Clamp a requested prune limit into `(0, PRUNE_MAX_LIMIT]`, defaulting when absent/invalid. */\nexport function clampPruneLimit(limit: number | undefined): number {\n  if (limit === undefined || !Number.isFinite(limit) || limit <= 0) return PRUNE_DEFAULT_LIMIT;\n  return Math.min(Math.floor(limit), PRUNE_MAX_LIMIT);\n}\n\n/** Default page size for the list APIs when the caller omits `limit`. */\nexport const LIST_DEFAULT_LIMIT = 50;\n/** Hard ceiling on a list page so a caller cannot ask for an unbounded scan. */\nexport const LIST_MAX_LIMIT = 500;\n\n/** Clamp a requested page size into `(0, LIST_MAX_LIMIT]`, defaulting when absent/invalid. */\nexport function clampListLimit(limit: number | undefined): number {\n  if (limit === undefined || !Number.isFinite(limit) || limit <= 0) return LIST_DEFAULT_LIMIT;\n  return Math.min(Math.floor(limit), LIST_MAX_LIMIT);\n}\n","/**\n * Admin operations: ledger queries, replay, and endpoint\n * disabling. These are thin, side-effect-light wrappers over the {@link Store}; the root\n * {@link \"../relay\".createRelay} composes them into the public `Relay`.\n */\nimport { newId } from \"../id\";\nimport { RelayError } from \"../core/index\";\nimport type { DeliveryAttempt, EndpointRow, Status } from \"../core/index\";\nimport {\n  ALL_STATUSES,\n  clampReplayLimit,\n  clampPruneLimit,\n  PRUNABLE_STATUSES,\n  DEFAULT_PRUNE_STATUSES,\n} from \"../store/_shared\";\nimport type {\n  OutboxQueryStore,\n  EndpointStore,\n  ReplayStore,\n  MaintenanceStore,\n  ReplayFilter,\n  NewOutboxRow,\n  NewEndpointRow,\n  EndpointPatch,\n  OutboxListFilter,\n  OutboxListItem,\n  EndpointListFilter,\n  EndpointSummary,\n  Page,\n} from \"../store/store\";\n\n/** Read the delivery ledger for one outbox row. */\nexport function attempts(store: OutboxQueryStore, outboxId: string): Promise<DeliveryAttempt[]> {\n  return store.queryAttempts({ outboxId });\n}\n\n/** A bigint `seq` cursor: one or more decimal digits. */\nconst SEQ_CURSOR_RE = /^\\d+$/;\n/** Postgres `bigint` upper bound; a `seq` cursor above this overflows the `::bigint` cast. */\nconst INT64_MAX = 9223372036854775807n;\n/** A uuid (`id`/`endpointId`) value. */\nconst UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\n/**\n * List outbox rows (read-only, secret-free) for DLQ inspection/monitoring. Newest-first by the\n * monotonic `seq`; pass the returned `nextCursor` to page. A common DLQ query is `{ status: \"dead\" }`.\n *\n * Validates the filter up front so a malformed `status`/`endpointId`/`cursor` fails as a clean,\n * awaitable `INVALID_ARGUMENT` rejection rather than a raw Postgres cast error from\n * `endpoint_id = $n` (uuid) or `seq < ($n)::bigint`. (`limit` is clamped in the store.) `async` so\n * the rejection is delivered through the returned Promise, never thrown synchronously.\n */\nexport async function listOutbox(\n  store: OutboxQueryStore,\n  filter: OutboxListFilter = {},\n): Promise<Page<OutboxListItem>> {\n  if (filter.status !== undefined && !(ALL_STATUSES as readonly string[]).includes(filter.status)) {\n    throw new RelayError(\"INVALID_ARGUMENT\", `list: unknown status \"${filter.status}\"`);\n  }\n  if (filter.endpointId !== undefined && !UUID_RE.test(filter.endpointId)) {\n    throw new RelayError(\n      \"INVALID_ARGUMENT\",\n      `list: endpointId must be a uuid, got \"${filter.endpointId}\"`,\n    );\n  }\n  if (\n    filter.cursor !== undefined &&\n    (!SEQ_CURSOR_RE.test(filter.cursor) || BigInt(filter.cursor) > INT64_MAX)\n  ) {\n    throw new RelayError(\n      \"INVALID_ARGUMENT\",\n      `list: cursor must be a numeric seq within int64, got \"${filter.cursor}\"`,\n    );\n  }\n  return store.listOutbox(filter);\n}\n\n/**\n * List registered endpoints (read-only, secret-free); pass the returned `nextCursor` to page.\n * Validates `status`/`cursor` so a malformed value fails as a clean, awaitable `INVALID_ARGUMENT`\n * rejection rather than a raw Postgres uuid-cast error from `id > $n`. `async` so the rejection is\n * delivered through the returned Promise, never thrown synchronously.\n */\nexport async function listEndpoints(\n  store: EndpointStore,\n  filter: EndpointListFilter = {},\n): Promise<Page<EndpointSummary>> {\n  if (\n    filter.status !== undefined &&\n    !([\"active\", \"disabled\"] as readonly string[]).includes(filter.status)\n  ) {\n    throw new RelayError(\"INVALID_ARGUMENT\", `endpoints.list: unknown status \"${filter.status}\"`);\n  }\n  if (filter.cursor !== undefined && !UUID_RE.test(filter.cursor)) {\n    throw new RelayError(\n      \"INVALID_ARGUMENT\",\n      `endpoints.list: cursor must be a uuid, got \"${filter.cursor}\"`,\n    );\n  }\n  return store.listEndpoints(filter);\n}\n\n/**\n * Cancel a not-yet-sent outbox row: moves it `pending` -&gt; `cancelled` only while it is\n * still pending. Returns `{ cancelled }` — false when the row was already claimed/sent or the id is\n * unknown, so a caller can tell \"stopped in time\" from \"too late\". Validates the id up front so a\n * malformed value fails as a clean `INVALID_ARGUMENT` rather than a raw uuid-cast error.\n */\nexport async function cancel(\n  store: OutboxQueryStore,\n  outboxId: string,\n): Promise<{ cancelled: boolean }> {\n  if (!UUID_RE.test(outboxId)) {\n    throw new RelayError(\"INVALID_ARGUMENT\", `cancel: outboxId must be a uuid, got \"${outboxId}\"`);\n  }\n  return { cancelled: await store.cancel(outboxId) };\n}\n\n/**\n * Fetch one outbox row by id (read-only, secret-free), or null when unknown. Validates the id so a\n * malformed value fails as a clean `INVALID_ARGUMENT` rather than a raw uuid-cast error.\n */\nexport async function getOutbox(\n  store: OutboxQueryStore,\n  outboxId: string,\n): Promise<OutboxListItem | null> {\n  if (!UUID_RE.test(outboxId)) {\n    throw new RelayError(\"INVALID_ARGUMENT\", `get: outboxId must be a uuid, got \"${outboxId}\"`);\n  }\n  return store.getOutbox(outboxId);\n}\n\n/**\n * Replay matching rows by inserting fresh `pending` copies. The original (e.g.\n * dead) rows are kept as history; each copy inherits the destination, payload, eventType and\n * `idempotencyKey` so the receiver can dedupe a re-send. The selection is always clamped to a safe\n * ceiling so a broad filter can never fan out into an unbounded mass re-send.\n *\n * Returns the new ids plus `capped`: true when the selection hit the (clamped) limit, so not every\n * matching row was replayed. To replay more, NARROW the filter (e.g. by `endpointId` / a tighter\n * `since`) or raise `filter.limit` — do NOT re-call with the same filter: replay leaves the source\n * rows untouched, so an identical call re-selects the same head rows and re-sends them (duplicates).\n */\nexport async function replay(\n  store: ReplayStore,\n  now: Date,\n  opts: { outboxId: string } | { filter: ReplayFilter },\n): Promise<{ ids: string[]; capped: boolean }> {\n  const base: ReplayFilter = \"filter\" in opts ? opts.filter : { outboxId: opts.outboxId };\n  // Replay re-activates rows as fresh pending copies, so a still-active row would be duplicated by\n  // the dispatcher's own delivery. Reject an explicit active-status filter up front (mirrors prune's\n  // refusal to touch live rows); the store also guards so a broad/no-status filter excludes them.\n  if (base.status === \"pending\" || base.status === \"in_flight\") {\n    throw new RelayError(\n      \"INVALID_ARGUMENT\",\n      `replay: status \"${base.status}\" is not replayable (only non-active rows can be replayed)`,\n    );\n  }\n  const limit = clampReplayLimit(base.limit);\n  const rows = await store.selectForReplay({ ...base, limit });\n  const copies: NewOutboxRow[] = rows.map((src) => ({\n    id: newId(),\n    eventType: src.eventType,\n    payload: src.payload,\n    endpointId: src.endpointId,\n    targetUrl: src.targetUrl,\n    secretSnapshot: src.secretSnapshot,\n    status: \"pending\",\n    attempts: 0,\n    availableAt: now,\n    idempotencyKey: src.idempotencyKey,\n  }));\n  const ids = await store.insertReplayCopies(copies);\n  // A full page means the limit may have truncated the match set; signal the caller to page on.\n  return { ids, capped: rows.length >= limit };\n}\n\n/** Options for `relay.prune`. `olderThan` is required; `statuses`/`limit` default and are clamped. */\nexport interface PruneOptions {\n  /** Delete only rows whose `created_at` is strictly before this. Required (no implicit \"all time\"). */\n  olderThan: Date;\n  /**\n   * Which statuses to delete. Defaults to `['delivered','dead','cancelled']`. Every value must be a\n   * non-active status (`delivered`/`dead`/`cancelled`/`observed`); passing `pending`/`in_flight`\n   * fails as `INVALID_ARGUMENT` so a live row can never be pruned out from under a delivery.\n   */\n  statuses?: Status[];\n  /** Max rows to delete in this call; clamped to a safe ceiling. Defaults to a batch size. */\n  limit?: number;\n}\n\n/**\n * Retention: delete terminal rows older than `opts.olderThan`, oldest first, in a bounded batch\n * (ledger attempts cascade). Returns `{ deleted }`; `deleted === limit` (the clamped limit) means more\n * may remain, so call again to keep pruning. Validates that every requested status is prunable\n * (non-active) up front, so a `pending`/`in_flight` row is never deleted.\n */\nexport async function prune(\n  store: MaintenanceStore,\n  opts: PruneOptions,\n): Promise<{ deleted: number }> {\n  if (!(opts.olderThan instanceof Date) || Number.isNaN(opts.olderThan.getTime())) {\n    throw new RelayError(\"INVALID_ARGUMENT\", \"prune: olderThan must be a valid Date\");\n  }\n  const statuses = opts.statuses ?? DEFAULT_PRUNE_STATUSES;\n  const allowed = PRUNABLE_STATUSES as readonly string[];\n  for (const s of statuses) {\n    if (!allowed.includes(s)) {\n      throw new RelayError(\n        \"INVALID_ARGUMENT\",\n        `prune: status \"${s}\" is not prunable (allowed: ${PRUNABLE_STATUSES.join(\", \")})`,\n      );\n    }\n  }\n  return store.prune({ olderThan: opts.olderThan, statuses, limit: clampPruneLimit(opts.limit) });\n}\n\n/** Register a new endpoint (status defaults to `active`). Returns the generated id. */\nexport async function registerEndpoint(\n  store: EndpointStore,\n  input: {\n    url: string;\n    secret: string;\n    description?: string | null;\n    metadata?: Record<string, unknown> | null;\n  },\n): Promise<{ id: string }> {\n  const ep: NewEndpointRow = {\n    id: newId(),\n    url: input.url,\n    secret: input.secret,\n    description: input.description ?? null,\n    metadata: input.metadata ?? null,\n  };\n  await store.insertEndpoint(ep);\n  return { id: ep.id };\n}\n\n/** Patch a registered endpoint; only the provided fields change. */\nexport function updateEndpoint(\n  store: EndpointStore,\n  id: string,\n  patch: EndpointPatch,\n): Promise<void> {\n  return store.updateEndpoint(id, patch);\n}\n\n/**\n * Begin a key rotation: promote the current secret to the secondary slot and set `newSecret` as the\n * primary, so deliveries are dual-signed with both keys until {@link finalizeRotation} drops the old\n * one. Throws `ENDPOINT_NOT_FOUND` when the id is unknown.\n */\nexport async function rotateEndpointSecret(\n  store: EndpointStore,\n  id: string,\n  newSecret: string,\n): Promise<void> {\n  const ep = await store.findEndpoint(id);\n  if (!ep) throw new RelayError(\"ENDPOINT_NOT_FOUND\", `endpoint not found: ${id}`);\n  await store.updateEndpoint(id, { secret: newSecret, secretSecondary: ep.secret });\n}\n\n/** Finish a key rotation: drop the secondary secret so deliveries sign with the new key only. */\nexport function finalizeRotation(store: EndpointStore, id: string): Promise<void> {\n  return store.updateEndpoint(id, { secretSecondary: null });\n}\n\n/**\n * Re-enable a disabled endpoint: clears the disabled marker AND resets the circuit-breaker\n * `consecutive_failures` counter, so a manually re-enabled endpoint gets a full `failureThreshold`\n * budget again (otherwise a breaker-disabled endpoint, whose counter is already at the threshold,\n * would be auto-disabled by the very next failed delivery). Reuses `reactivateEndpoint`, the same\n * single-UPDATE the circuit breaker's half-open recovery uses (`status='active'`,\n * `consecutive_failures=0`, `disabled_at=NULL`), so manual and automatic recovery behave identically.\n */\nexport function enableEndpoint(store: EndpointStore, id: string): Promise<void> {\n  return store.reactivateEndpoint(id);\n}\n\n/** Look up a registered endpoint. */\nexport function getEndpoint(store: EndpointStore, id: string): Promise<EndpointRow | null> {\n  return store.findEndpoint(id);\n}\n\n/**\n * Disable a registered endpoint so no further deliveries target it. A deliberate admin disable is\n * sticky: it marks the endpoint `disabled` and clears `disabled_at` (the circuit-breaker auto-recovery\n * cooldown anchor), so — unlike a breaker / `410 Gone` auto-disable, which stamps `disabled_at = now` —\n * it is never re-tried by half-open recovery. It stays disabled until {@link enableEndpoint}.\n */\nexport function disableEndpoint(store: EndpointStore, endpointId: string): Promise<void> {\n  return store.updateEndpoint(endpointId, { status: \"disabled\", disabledAt: null });\n}\n","/**\n * Root public API: `createRelay` and the `Relay<TTx>` it returns.\n *\n * `createRelay` is the single entry point. It resolves/validates config, fails fast if the core\n * tables are missing, wires the HTTP client and bound `deliver`, and exposes enqueue (TX-riding,\n * fail-closed), dispatcher creation, ledger queries, replay, and endpoint disabling. The store's\n * generic flows through so `enqueue`'s `trx` type is the driver's transaction handle.\n */\nimport { newId } from \"./id\";\nimport { resolveConfig, initialState, validatePayload, RelayError } from \"./core/index\";\nimport type {\n  EnqueueInput,\n  OutboxRow,\n  DeliveryAttempt,\n  EndpointRow,\n  Mode,\n  SigningConfig,\n  RetryConfig,\n  DeliveryConfig,\n  SsrfConfig,\n  CircuitBreakerConfig,\n  SecretCipher,\n  Clock,\n  Logger,\n} from \"./core/index\";\nimport type {\n  Store,\n  NewOutboxRow,\n  ReplayFilter,\n  EndpointPatch,\n  OutboxStats,\n  OutboxListFilter,\n  OutboxListItem,\n  EndpointListFilter,\n  EndpointSummary,\n  Page,\n} from \"./store/store\";\nimport { createEncryptedStore } from \"./store/encrypted-store\";\nimport { createEndpointCache } from \"./store/endpoint-cache\";\nimport { createHttpClient } from \"./delivery/http\";\nimport { deliverOne } from \"./delivery/deliver\";\nimport type { DeliveryHooks, DeliveryInstrument } from \"./delivery/deliver\";\nimport { createCriticalLogger } from \"./delivery/critical\";\nimport type { CriticalLogger } from \"./delivery/critical\";\nimport { createDispatcher as makeDispatcher } from \"./dispatcher/dispatcher\";\nimport type { Dispatcher, DispatcherOptions, RunOnceOptions } from \"./dispatcher/dispatcher\";\nimport type { Accelerator } from \"./accelerator/accelerator\";\nimport type { Sink } from \"./forward/index\";\nimport {\n  attempts as adminAttempts,\n  replay as adminReplay,\n  prune as adminPrune,\n  cancel as adminCancel,\n  getOutbox as adminGetOutbox,\n  listOutbox as adminListOutbox,\n  listEndpoints as adminListEndpoints,\n  registerEndpoint as adminRegister,\n  updateEndpoint as adminUpdate,\n  enableEndpoint as adminEnable,\n  getEndpoint as adminGet,\n  disableEndpoint as adminDisable,\n  rotateEndpointSecret as adminRotate,\n  finalizeRotation as adminFinalizeRotation,\n} from \"./admin/admin\";\nimport type { PruneOptions } from \"./admin/admin\";\n\n/** Input to {@link EndpointAdmin.register}. */\nexport interface RegisterEndpointInput {\n  url: string;\n  secret: string;\n  description?: string | null;\n  metadata?: Record<string, unknown> | null;\n}\n\n/** The registered-endpoint admin surface exposed by {@link Relay.endpoints}. */\nexport interface EndpointAdmin {\n  /** Register a new endpoint; returns the generated id. */\n  register(input: RegisterEndpointInput): Promise<{ id: string }>;\n  /** Patch a registered endpoint; only the provided fields change. */\n  update(endpointId: string, patch: EndpointPatch): Promise<void>;\n  /**\n   * Re-enable a disabled endpoint. Also resets the circuit-breaker failure counter, so the endpoint\n   * gets a full `failureThreshold` budget again rather than being re-disabled by the next failure.\n   */\n  enable(endpointId: string): Promise<void>;\n  /**\n   * Disable an endpoint so no further deliveries target it. Sticky: unlike a circuit-breaker / `410`\n   * auto-disable, a deliberate manual disable is never auto-recovered by the half-open cooldown — it\n   * stays disabled until {@link EndpointAdmin.enable}.\n   */\n  disable(endpointId: string): Promise<void>;\n  /** Look up a registered endpoint, or null when absent. */\n  get(endpointId: string): Promise<EndpointRow | null>;\n  /**\n   * List registered endpoints (read-only, secret-free). Ordered by id; pass the returned\n   * `nextCursor` for the next page (null when the last page was returned).\n   */\n  list(filter?: EndpointListFilter): Promise<Page<EndpointSummary>>;\n  /**\n   * Begin a signing-key rotation: promote the current secret to the secondary slot and set\n   * `newSecret` as primary, so deliveries are dual-signed with both keys. Call\n   * {@link EndpointAdmin.finalizeRotation} once receivers have migrated to drop the old key.\n   */\n  rotateSecret(endpointId: string, newSecret: string): Promise<void>;\n  /** Finish a rotation: drop the secondary secret so deliveries sign with the new key only. */\n  finalizeRotation(endpointId: string): Promise<void>;\n}\n\n/** Arguments to {@link createRelay}: the store plus a partial relay configuration. */\nexport interface RelayInit<TTx> {\n  store: Store<TTx>;\n  /**\n   * Optional cipher for encrypting signing secrets at rest (`secretSnapshot`, endpoint `secret`).\n   * When provided, the store is transparently wrapped so secrets are ciphertext in the backend.\n   * See {@link createAesGcmCipher}. When omitted, secrets are stored as-is (plaintext), so at-rest\n   * encryption becomes the database's responsibility — `createRelay` warns about this at startup\n   * unless you acknowledge it with {@link RelayInit.unsafeAllowPlaintextSecrets}.\n   */\n  cipher?: SecretCipher;\n  /**\n   * Acknowledge that signing secrets are intentionally stored in plaintext (no `cipher`) because\n   * at-rest encryption is handled elsewhere (DB disk encryption, column encryption, etc.). Silences\n   * the startup plaintext-secret warning. Has no effect when `cipher` is set. Default false.\n   */\n  unsafeAllowPlaintextSecrets?: boolean;\n  /**\n   * Optional TTL (ms) for an in-process registered-endpoint lookup cache. Cuts the per-delivery\n   * `findEndpoint` DB round trip on the registered-endpoint hot path; `updateEndpoint`/`disable`\n   * evict in-process, and the TTL bounds cross-process staleness. Omitted/0 disables caching\n   * (default). Has no effect on the inline `{ url, secret }` workflow.\n   */\n  endpointCacheTtlMs?: number;\n  mode?: Mode;\n  signing?: Partial<SigningConfig>;\n  retry?: Partial<RetryConfig>;\n  delivery?: Partial<DeliveryConfig>;\n  ssrf?: Partial<SsrfConfig>;\n  /**\n   * Registered-endpoint circuit breaker: after `failureThreshold` consecutive failed deliveries an\n   * endpoint is auto-disabled (a success resets the count). Default `{ failureThreshold: 0 }` = off.\n   */\n  circuitBreaker?: Partial<CircuitBreakerConfig>;\n  /**\n   * Optional ceiling on the UTF-8 byte length of an enqueued payload's JSON serialization. When set,\n   * `enqueue`/`enqueueMany`/`enqueueUnsafe` reject an over-size payload with\n   * `RelayError(\"ENQUEUE_INVALID_PAYLOAD\")`. Omitted (default) = no limit; payload serializability is\n   * validated either way.\n   */\n  maxPayloadBytes?: number;\n  clock?: Clock;\n  logger?: Logger;\n  /** Optional delivery-outcome callbacks (fail-open) applied to every dispatcher from this relay. */\n  hooks?: DeliveryHooks;\n  /**\n   * Optional, fail-open tracing/metrics seam applied to every delivery from this relay (the\n   * OpenTelemetry seam). See {@link DeliveryInstrument}; wire it with `createOtelInstrumentation`\n   * from `commitcourier/otel`.\n   */\n  instrument?: DeliveryInstrument;\n  /**\n   * Optional low-latency wake accelerator (fail-open). When provided, each enqueue signals it (so a\n   * listening dispatcher wakes at once instead of after the poll interval) and every dispatcher this\n   * relay creates subscribes to it. The outbox row stays the source of truth, so a missed wake only\n   * delays delivery. Wire it with `createPgAccelerator` from `commitcourier/accelerator/pg`.\n   */\n  accelerator?: Accelerator<TTx>;\n  /**\n   * Delivery sink for `sink` transport. Required when\n   * `delivery.transport === \"sink\"` — each event is handed to it instead of being delivered over HTTP,\n   * and CommitCourier's signing/SSRF/circuit breaker are delegated. Ignored for the default `http`\n   * transport. Each handoff is bounded by `delivery.timeoutMs`, but the sink is also responsible for\n   * its own latency/timeout. Wire it with `svixSink` from `commitcourier/forward/svix` or your own `Sink`.\n   *\n   * Experimental — this API may change in a minor release.\n   */\n  sink?: Sink;\n}\n\n/** The public surface returned by {@link createRelay}. */\nexport interface Relay<TTx> {\n  /** Ride the business TX (fail-closed). `trx` is required. */\n  enqueue(trx: TTx, input: EnqueueInput): Promise<{ id: string }>;\n  /** Bulk enqueue inside the business TX (fail-closed): one round trip, returns ids in input order. */\n  enqueueMany(trx: TTx, inputs: EnqueueInput[]): Promise<{ ids: string[] }>;\n  /** Non-TX enqueue. Loses the atomicity guarantee; use only when there is no business TX. */\n  enqueueUnsafe(input: EnqueueInput): Promise<{ id: string }>;\n  /** Create a background dispatcher bound to this relay's store/deliver/config. */\n  createDispatcher(options?: DispatcherOptions): Dispatcher;\n  /**\n   * Drain the queue once and return (no long-lived loop), for serverless/cron deployments. A\n   * one-shot convenience over `createDispatcher(options).runOnce(runOptions)`; it never subscribes\n   * the accelerator (the loop's wake seam is irrelevant to a single drain). Returns the number of\n   * rows dispatched this run.\n   *\n   * Pick one delivery model per process: the running-loop guard is per dispatcher instance, and\n   * this creates a fresh dispatcher each call, so a loop started via `createDispatcher().start()`\n   * is not detected. Overlap stays safe (`SKIP LOCKED` keeps rows single-claim); it just competes\n   * for the same rows.\n   */\n  dispatchOnce(\n    options?: DispatcherOptions,\n    runOptions?: RunOnceOptions,\n  ): Promise<{ processed: number }>;\n  /** Read the delivery ledger for one outbox row. */\n  attempts(opts: { outboxId: string }): Promise<DeliveryAttempt[]>;\n  /**\n   * Replay matching rows as fresh pending copies; returns the new ids and `capped` (true when the\n   * selection hit the safety limit, so not everything matched was replayed). To replay more, narrow\n   * the filter or raise `filter.limit` — re-calling with the same filter re-sends the same rows\n   * (replay does not change the source rows), so do not loop on `capped`. The limit is always clamped.\n   */\n  replay(\n    opts: { outboxId: string } | { filter: ReplayFilter },\n  ): Promise<{ ids: string[]; capped: boolean }>;\n  /** Cancel a not-yet-sent row (`pending` -&gt; `cancelled`); `cancelled` is false when it was too late. */\n  cancel(outboxId: string): Promise<{ cancelled: boolean }>;\n  /**\n   * Retention: delete terminal rows older than `olderThan` in a bounded batch (ledger attempts\n   * cascade). Only non-active statuses are eligible (default `delivered`/`dead`/`cancelled`); a\n   * `pending`/`in_flight` row is never deleted. Returns `{ deleted }` — when it equals the (clamped)\n   * limit, more may remain, so call again to keep pruning.\n   */\n  prune(opts: PruneOptions): Promise<{ deleted: number }>;\n  /** Fetch one outbox row by id (read-only, secret-free), or null when unknown. */\n  get(outboxId: string): Promise<OutboxListItem | null>;\n  /**\n   * List outbox rows (read-only, secret-free) for DLQ inspection/monitoring; newest-first by `seq`.\n   * Pass `{ status: \"dead\" }` to inspect the DLQ; page with the returned `nextCursor`.\n   */\n  list(filter?: OutboxListFilter): Promise<Page<OutboxListItem>>;\n  /** Registered-endpoint admin: register / update / enable / disable / get / list. */\n  endpoints: EndpointAdmin;\n  /** Aggregate queue statistics (status counts and oldest-pending age) for monitoring. */\n  stats(): Promise<OutboxStats>;\n}\n\ntype InlineEndpoint = { url: string; secret: string };\n\n// `ep` is typed but may be anything at runtime (JS callers can omit `endpoint`), so guard that it\n// is a non-null object before using the `in` operator, which would otherwise throw a TypeError\n// instead of the intended ENQUEUE_NO_TARGET.\nfunction asInline(ep: unknown): InlineEndpoint | null {\n  return typeof ep === \"object\" &&\n    ep !== null &&\n    \"url\" in ep &&\n    \"secret\" in ep &&\n    typeof ep.url === \"string\" &&\n    typeof ep.secret === \"string\"\n    ? { url: ep.url, secret: ep.secret }\n    : null;\n}\n\nfunction asRegistered(ep: unknown): { endpointId: string } | null {\n  return typeof ep === \"object\" &&\n    ep !== null &&\n    \"endpointId\" in ep &&\n    typeof ep.endpointId === \"string\"\n    ? { endpointId: ep.endpointId }\n    : null;\n}\n\n/**\n * Compose the store decorators: encrypt secrets at rest, then (optionally) cache decrypted endpoint\n * lookups outermost so every layer below keeps seeing plaintext rows. Validates the cache TTL.\n */\nfunction wrapStore<TTx>(\n  rawStore: Store<TTx>,\n  opts: {\n    cipher: SecretCipher | undefined;\n    endpointCacheTtlMs: number | undefined;\n    logger: Logger;\n    critical: CriticalLogger;\n  },\n): Store<TTx> {\n  const { cipher, endpointCacheTtlMs, logger, critical } = opts;\n  if (\n    endpointCacheTtlMs !== undefined &&\n    !(Number.isFinite(endpointCacheTtlMs) && endpointCacheTtlMs >= 0)\n  ) {\n    throw new RelayError(\n      \"CONFIG_INVALID\",\n      `endpointCacheTtlMs must be a number >= 0, got ${String(endpointCacheTtlMs)}`,\n    );\n  }\n  // The encrypted store logs (and quarantines) undecryptable rows, so it needs the relay's logger;\n  // a quarantine is a DLQ transition (data loss), so it also gets the critical sink — visible even\n  // with no logger configured, like the delivery path's dead-letter alarm.\n  let store = cipher\n    ? createEncryptedStore(rawStore, cipher, logger, (msg, meta) => {\n        critical.dataLoss(msg, meta);\n      })\n    : rawStore;\n  if (endpointCacheTtlMs && endpointCacheTtlMs > 0) {\n    store = createEndpointCache(store, { ttlMs: endpointCacheTtlMs });\n  }\n  return store;\n}\n\n/**\n * Fail fast when the runtime does not expose the WebCrypto global (`crypto.subtle`) that webhook\n * signing needs — a standard global on Node 20+ (this package targets 22.19+). Without this, every\n * signed delivery would fail deep in `sign()` with a cryptic \"crypto is not defined\" ReferenceError\n * and, because delivery is fail-open, silently pile into the DLQ. The `sink` transport delegates\n * signing to the SaaS, so it is exempt.\n */\nfunction assertWebCryptoAvailable(transport: DeliveryConfig[\"transport\"]): void {\n  const webcrypto = (globalThis as { crypto?: { subtle?: unknown } }).crypto;\n  if (transport !== \"sink\" && webcrypto?.subtle === undefined) {\n    throw new RelayError(\n      \"CONFIG_INVALID\",\n      \"the WebCrypto API (globalThis.crypto.subtle) is required for webhook signing but is not available \" +\n        \"in this runtime. CommitCourier targets Node >=22.19, where it is a standard global; upgrade the \" +\n        'runtime, or set delivery.transport to \"sink\" to delegate signing to a SaaS.',\n    );\n  }\n}\n\nexport async function createRelay<TTx>(config: RelayInit<TTx>): Promise<Relay<TTx>> {\n  const {\n    store: rawStore,\n    cipher,\n    endpointCacheTtlMs,\n    unsafeAllowPlaintextSecrets,\n    sink,\n    ...rest\n  } = config;\n  // Delivery is fail-open, so without a logger routine failures/retries are swallowed silently. The\n  // two critical categories — security (SSRF blocks) and data loss (DLQ transitions) — fall back to\n  // the console even with no logger (see createCriticalLogger), but everything else stays silent, so\n  // warn once at startup; pass `logger` (e.g. `createConsoleLogger()`) to capture all operational logs.\n  const loggerConfigured = config.logger !== undefined;\n  if (!loggerConfigured) {\n    console.warn(\n      \"[commitcourier] no logger configured: routine delivery failures and retries will be silent. \" +\n        \"Security events (SSRF blocks) and data loss (DLQ transitions) will fall back to console. \" +\n        \"Pass `logger` (e.g. createConsoleLogger() from commitcourier/core) to capture all operational logs.\",\n    );\n  }\n  // Resolve config first so the store decorators (encrypted store) can use the resolved logger.\n  const resolved = resolveConfig(rest);\n  assertWebCryptoAvailable(resolved.delivery.transport);\n  // Signing secrets are written in plaintext without a cipher. This is a security footgun, so warn at\n  // startup unless the caller acknowledges that at-rest encryption is handled elsewhere — symmetric to\n  // the no-logger warning above. Non-fatal: never breaks an existing deployment. Skipped in `sink`\n  // transport, where signing is delegated and no signing secret is used (the warning would be noise).\n  if (\n    cipher === undefined &&\n    unsafeAllowPlaintextSecrets !== true &&\n    resolved.delivery.transport !== \"sink\"\n  ) {\n    const msg =\n      \"no cipher configured: signing secrets (secret_snapshot, endpoint secret) are stored in PLAINTEXT \" +\n      \"in your database. At-rest encryption is then a precondition you must meet elsewhere (DB disk \" +\n      \"encryption, column encryption, or pass `cipher` e.g. createAesGcmCipher). Set \" +\n      \"`unsafeAllowPlaintextSecrets: true` to acknowledge and silence this.\";\n    if (loggerConfigured) resolved.logger.warn(msg);\n    else console.warn(`[commitcourier] ${msg}`);\n  }\n  // In `sink` transport the destination is delegated to the sink/SaaS: `sink` is required, and CC-side\n  // signing/SSRF/circuit-breaker settings do not apply. Fail fast on a missing sink; warn once that the\n  // delegated settings are ignored (non-fatal — they are naturally left over during a staged migration).\n  if (resolved.delivery.transport === \"sink\") {\n    if (sink === undefined) {\n      throw new RelayError(\"CONFIG_INVALID\", 'delivery.transport \"sink\" requires a sink');\n    }\n    const msg =\n      'delivery.transport is \"sink\": CommitCourier delegates signing, SSRF protection and the circuit ' +\n      \"breaker to the sink/SaaS, so any signing/ssrf/circuitBreaker settings are ignored. The sink is \" +\n      \"responsible for final delivery and customer-facing retries.\";\n    if (loggerConfigured) resolved.logger.warn(msg);\n    else console.warn(`[commitcourier] ${msg}`);\n  }\n  // Surfaces security/data-loss events to the console when no logger was configured; otherwise routes\n  // to the configured logger only (see createCriticalLogger). Built before the store decorators so the\n  // encrypted store's quarantine (a DLQ transition = data loss) reports through the same instance.\n  const critical = createCriticalLogger(resolved.logger, loggerConfigured);\n  const store = wrapStore(rawStore, {\n    cipher,\n    endpointCacheTtlMs,\n    logger: resolved.logger,\n    critical,\n  });\n\n  const diag = await store.diagnose();\n  if (!diag.ok) {\n    throw new RelayError(\n      \"MISSING_TABLES\",\n      `required tables missing: ${diag.missingTables.join(\", \")}`,\n    );\n  }\n\n  const http = createHttpClient({ ssrf: resolved.ssrf, delivery: resolved.delivery });\n  const hooks = config.hooks;\n  const instrument = config.instrument;\n  const accelerator = config.accelerator;\n  const deliver = (row: OutboxRow): Promise<void> =>\n    deliverOne(row, {\n      store,\n      http,\n      config: resolved,\n      clock: resolved.clock,\n      critical,\n      hooks,\n      instrument,\n      sink,\n    });\n\n  /** Build the outbox row from enqueue input, snapshotting the inline secret at enqueue time. */\n  function buildRow(input: EnqueueInput): NewOutboxRow {\n    // Validate the payload up front so an unserializable/over-size payload fails with a stable\n    // ENQUEUE_INVALID_PAYLOAD rather than a raw driver error from the jsonb serialization step\n    // (symmetric to the endpoint-shape check below). Covers enqueue/enqueueMany/enqueueUnsafe.\n    validatePayload(input.payload, resolved.maxPayloadBytes);\n    const init = initialState(resolved.mode, resolved.clock());\n    const inline = asInline(input.endpoint);\n    const registered = inline ? null : asRegistered(input.endpoint);\n    // `sink` transport delivers to the configured sink/SaaS, not a per-event URL, so a target is not\n    // required (the row is target-less). `http` still requires one.\n    if (!inline && !registered && resolved.delivery.transport !== \"sink\") {\n      throw new RelayError(\n        \"ENQUEUE_NO_TARGET\",\n        \"enqueue requires endpoint { url, secret } or { endpointId }\",\n      );\n    }\n    return {\n      id: newId(),\n      eventType: input.eventType,\n      payload: input.payload,\n      endpointId: registered ? registered.endpointId : null,\n      targetUrl: inline ? inline.url : null,\n      secretSnapshot: inline ? inline.secret : null,\n      status: init.status,\n      attempts: init.attempts,\n      availableAt: init.availableAt,\n      idempotencyKey: input.idempotencyKey ?? null,\n    };\n  }\n\n  return {\n    async enqueue(trx, input) {\n      // Errors propagate so the user's TX rolls back with the business write (fail-closed). The\n      // accelerator wake rides the same TX (NOTIFY on COMMIT), so a freshly enqueued row wakes a\n      // listening dispatcher at once; its failure consistently rolls back with the enqueue.\n      const row = buildRow(input);\n      await store.insertOutbox(trx, row);\n      if (accelerator) await accelerator.signal(trx);\n      return { id: row.id };\n    },\n    async enqueueMany(trx, inputs) {\n      // One multi-row INSERT on the caller's TX; errors propagate (fail-closed). A single wake\n      // suffices for the whole batch.\n      const rows = inputs.map(buildRow);\n      await store.insertOutboxMany(trx, rows);\n      if (rows.length > 0 && accelerator) await accelerator.signal(trx);\n      return { ids: rows.map((r) => r.id) };\n    },\n    async enqueueUnsafe(input) {\n      const row = buildRow(input);\n      await store.insertOutboxAutonomous(row);\n      // Best-effort wake outside any TX; never let a signal failure break the (already non-atomic)\n      // enqueueUnsafe — the poller is the safety net.\n      if (accelerator) {\n        try {\n          await accelerator.signalAutonomous();\n        } catch (err) {\n          resolved.logger.warn(\"accelerator signalAutonomous failed\", { error: String(err) });\n        }\n      }\n      return { id: row.id };\n    },\n    createDispatcher(options) {\n      return makeDispatcher({\n        store,\n        deliver,\n        config: resolved,\n        options,\n        wakeSignal: accelerator ? (onWake) => accelerator.subscribe(onWake) : undefined,\n      });\n    },\n    dispatchOnce(options, runOptions) {\n      // No wakeSignal: runOnce never subscribes, so the accelerator seam is irrelevant to one drain.\n      return makeDispatcher({ store, deliver, config: resolved, options }).runOnce(runOptions);\n    },\n    attempts(opts) {\n      return adminAttempts(store, opts.outboxId);\n    },\n    async replay(opts) {\n      const result = await adminReplay(store, resolved.clock(), opts);\n      // Replayed rows are fresh `pending` copies; wake a listening dispatcher just like enqueue so\n      // they are delivered without waiting for the next idle poll. Best-effort (the poller is the\n      // net), so a signal failure never fails the replay.\n      if (result.ids.length > 0 && accelerator) {\n        try {\n          await accelerator.signalAutonomous();\n        } catch (err) {\n          resolved.logger.warn(\"accelerator signal after replay failed\", { error: String(err) });\n        }\n      }\n      return result;\n    },\n    cancel(outboxId) {\n      return adminCancel(store, outboxId);\n    },\n    prune(opts) {\n      return adminPrune(store, opts);\n    },\n    get(outboxId) {\n      return adminGetOutbox(store, outboxId);\n    },\n    list(filter) {\n      return adminListOutbox(store, filter);\n    },\n    endpoints: endpointAdmin(store),\n    stats() {\n      return store.stats();\n    },\n  };\n}\n\n/** Compose the registered-endpoint admin surface over the store. */\nfunction endpointAdmin(store: Store): EndpointAdmin {\n  return {\n    register: (input) => adminRegister(store, input),\n    update: (id, patch) => adminUpdate(store, id, patch),\n    enable: (id) => adminEnable(store, id),\n    disable: (id) => adminDisable(store, id),\n    get: (id) => adminGet(store, id),\n    list: (filter) => adminListEndpoints(store, filter),\n    rotateSecret: (id, newSecret) => adminRotate(store, id, newSecret),\n    finalizeRotation: (id) => adminFinalizeRotation(store, id),\n  };\n}\n"],"mappings":"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","names":["attempts","backoffMs","attempts","hostname","dnsLookup","fail","applied","attempt","event","import_node_crypto","fail","pLimit"]}