@hello-bill/node 2.1.3-beta.0 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/express/index.d.cts +1 -1
- package/dist/express/index.d.ts +1 -1
- package/dist/express/index.js +8 -35
- package/dist/express/index.js.map +1 -1
- package/dist/express/index.mjs +8 -35
- package/dist/express/index.mjs.map +1 -1
- package/dist/hono/index.d.cts +1 -1
- package/dist/hono/index.d.ts +1 -1
- package/dist/hono/index.js +8 -35
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/index.mjs +8 -35
- package/dist/hono/index.mjs.map +1 -1
- package/dist/{index-C_gIXrzG.d.cts → index-D5WlUYVZ.d.cts} +0 -7
- package/dist/{index-DnEk4Y5R.d.ts → index-jK9cEnEY.d.ts} +0 -7
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -32
- package/dist/index.mjs.map +1 -1
- package/dist/koa/index.d.cts +1 -1
- package/dist/koa/index.d.ts +1 -1
- package/dist/koa/index.js +8 -35
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/index.mjs +8 -35
- package/dist/koa/index.mjs.map +1 -1
- package/dist/next/index.d.cts +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +8 -38
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +8 -38
- package/dist/next/index.mjs.map +1 -1
- package/dist/webhook/index.d.cts +1 -1
- package/dist/webhook/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/koa/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types/errors.ts","../../src/core/retry.ts","../../src/core/client.ts","../../src/core/idempotency.ts","../../src/core/errors.ts","../../src/core/token-manager.ts","../../src/webhook/verify.ts","../../src/webhook/dedupe-store.ts","../../src/webhook/handler.ts","../../src/core/handler.ts","../../src/_internal/normalise-headers.ts","../../src/koa/index.ts"],"names":["delay","randomUUID","createHmac","timingSafeEqual","result","createRequire"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Spec error envelope.\n * Returned with non-2xx responses from the Partner API and surfaced verbatim by the SDK.\n */\n\nimport type { RequestId } from './common.js';\n\n/**\n * Error codes follow `<category>.<specific>` naming and match the v15 §12 catalog.\n */\nexport type ErrorCode =\n // validation.*\n | 'validation.missing_required_field'\n | 'validation.invalid_field_value'\n | 'validation.invalid_enum_value'\n // consent.*\n | 'consent.required'\n // product.*\n | 'product.id_expired'\n // loa.*\n | 'loa.coverage_missing'\n | 'loa.coverage_excess'\n | 'loa.session_mismatch'\n | 'loa.template_changed'\n | 'loa.selection_changed'\n // signature_image.*\n | 'signature_image.invalid'\n | 'signature_image.too_large'\n | 'signature_image.dimensions_invalid'\n // address.*\n | 'address.line_too_long'\n | 'address.postcode_invalid'\n // move_out.*\n | 'move_out.requires_previous_property'\n | 'move_out.invalid_date'\n // final_read.*\n | 'final_read.implausible'\n // customer.*\n | 'customer.address_invalid'\n | 'customer.not_found'\n // bank_details.*\n | 'bank_details.invalid_sort_code'\n | 'bank_details.invalid_account_number'\n | 'bank_details.modulus_check_failed'\n | 'bank_details.session_locked'\n // auth.*\n | 'auth.invalid_credentials'\n | 'auth.token_expired'\n | 'auth.token_malformed'\n | 'auth.insufficient_scope'\n // session.*\n | 'session.not_found'\n // rate.*\n | 'rate.limited'\n // internal.*\n | 'internal.error'\n // discovery.*\n | 'discovery.unavailable';\n\nexport type ErrorType =\n | 'authentication_error'\n | 'validation_error'\n | 'rate_limit_error'\n | 'api_error';\n\nexport interface ErrorEnvelope {\n error: {\n type: ErrorType;\n code: ErrorCode;\n message: string;\n /** Path of the offending field (e.g. \"addresses.current.postcode\"). */\n param?: string;\n /** For validation.missing_required_field — dot-notation list of missing fields. */\n missing_fields?: string[];\n /** Echo of `X-HelloBill-Request-Id` for support correlation. */\n request_id: RequestId;\n };\n}\n\n/** HTTP statuses the SDK retries automatically with exponential backoff. */\nexport const RETRYABLE_HTTP_STATUSES = new Set<number>([429, 500, 502, 503, 504]);\n","import { RETRYABLE_HTTP_STATUSES } from '../types/index.js';\n\nexport interface RetryOptions {\n /** Max attempts including the first. Default 5. */\n maxAttempts?: number;\n /** Base delay in ms for exponential backoff. Default 1000. */\n baseDelayMs?: number;\n /** Cap on delay (ms). Default 16000. */\n maxDelayMs?: number;\n /** Override fetch (for tests). Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch;\n /** Override sleep (for tests). */\n sleep?: (ms: number) => Promise<void>;\n /** Random source for jitter (0..1). */\n random?: () => number;\n /**\n * Optional pre-attempt hook. Resolves with `true` to swap in a refreshed token.\n * Called once per attempt when the previous response indicated `auth.token_expired`.\n */\n onTokenExpired?: () => Promise<void>;\n /**\n * Per-attempt request timeout in ms. If the fetch does not resolve within this\n * window, it is aborted and treated as a retryable network error. Default 30_000.\n */\n timeoutMs?: number;\n}\n\n/** Sentinel error class for network/timeout failures the caller can recognise. */\nexport class NetworkError extends Error {\n constructor(\n message: string,\n public readonly cause?: unknown,\n public readonly kind: 'timeout' | 'network' = 'network',\n ) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\nconst defaultSleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;\n return null;\n}\n\nfunction backoff(attempt: number, base: number, cap: number, random: () => number): number {\n const exp = Math.min(cap, base * 2 ** attempt);\n // full jitter\n return Math.floor(random() * exp);\n}\n\nasync function readErrorCode(res: Response): Promise<string | undefined> {\n try {\n const clone = res.clone();\n const body = (await clone.json()) as { error?: { code?: string } };\n return body?.error?.code;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Fetch with retry. Retries on retryable HTTP statuses honoring `Retry-After`,\n * and invokes `onTokenExpired` once when the upstream returns `auth.token_expired`.\n */\nexport async function retryFetch(\n input: string | URL,\n init: RequestInit,\n opts: RetryOptions = {},\n): Promise<Response> {\n const {\n maxAttempts = 5,\n baseDelayMs = 1000,\n maxDelayMs = 16000,\n fetch: fetchImpl = globalThis.fetch,\n sleep = defaultSleep,\n random = Math.random,\n onTokenExpired,\n timeoutMs = 30_000,\n } = opts;\n\n let tokenRefreshed = false;\n let lastResponse: Response | undefined;\n let lastError: unknown;\n\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n let res: Response;\n try {\n res = await fetchWithTimeout(fetchImpl, input, init, timeoutMs);\n } catch (err) {\n lastError = err;\n // Treat any fetch rejection (network failure, abort/timeout) as retryable.\n if (attempt === maxAttempts - 1) {\n if (err instanceof NetworkError) throw err;\n throw new NetworkError(\n 'upstream request failed',\n err,\n isAbortLike(err) ? 'timeout' : 'network',\n );\n }\n const delay = backoff(attempt, baseDelayMs, maxDelayMs, random);\n await sleep(delay);\n continue;\n }\n lastResponse = res;\n\n if (res.ok) return res;\n\n // auth.token_expired triggers one-shot token refresh + immediate retry.\n if (!tokenRefreshed && onTokenExpired && res.status === 401) {\n const code = await readErrorCode(res);\n if (code === 'auth.token_expired') {\n tokenRefreshed = true;\n await onTokenExpired();\n continue;\n }\n }\n\n if (!RETRYABLE_HTTP_STATUSES.has(res.status)) return res;\n if (attempt === maxAttempts - 1) return res;\n\n const retryAfterMs = parseRetryAfter(res.headers.get('retry-after'));\n const delay = retryAfterMs ?? backoff(attempt, baseDelayMs, maxDelayMs, random);\n await sleep(delay);\n }\n\n // Should be unreachable; satisfies the type checker.\n if (lastResponse) return lastResponse;\n throw new NetworkError('upstream request failed', lastError);\n}\n\nfunction isAbortLike(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const name = (err as { name?: string }).name;\n return name === 'AbortError' || name === 'TimeoutError';\n}\n\nasync function fetchWithTimeout(\n fetchImpl: typeof globalThis.fetch,\n input: string | URL,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n return fetchImpl(input as RequestInfo, init);\n }\n // Compose the caller's signal (if any) with our timeout signal.\n const controller = new AbortController();\n const userSignal = init.signal;\n if (userSignal) {\n if (userSignal.aborted) controller.abort(userSignal.reason);\n else userSignal.addEventListener('abort', () => controller.abort(userSignal.reason), { once: true });\n }\n const timer = setTimeout(() => controller.abort(new Error('upstream timeout')), timeoutMs);\n try {\n return await fetchImpl(input as RequestInfo, { ...init, signal: controller.signal });\n } catch (err) {\n if (controller.signal.aborted && !userSignal?.aborted) {\n throw new NetworkError(`upstream request timed out after ${timeoutMs}ms`, err, 'timeout');\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n","import type { Logger } from './token-manager.js';\nimport type { TokenManager } from './token-manager.js';\nimport { retryFetch } from './retry.js';\n\nexport interface UpstreamCallOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n path: string;\n query?: Record<string, string | string[] | undefined>;\n body?: unknown;\n idempotencyKey?: string;\n logger?: Logger;\n /** Per-attempt request timeout in ms. */\n timeoutMs?: number;\n /**\n * When set, this bearer is used as the upstream Authorization header instead\n * of the M2M token from the TokenManager. The M2M refresh-and-retry is also\n * skipped — a 401 on a forwarded session token is terminal and must not\n * trigger an M2M exchange.\n */\n sessionBearer?: string;\n}\n\nexport interface UpstreamCallResult {\n status: number;\n headers: Headers;\n /** Parsed JSON body, or null when the body isn't JSON. */\n body: unknown;\n /** X-HelloBill-Request-Id, if present. */\n requestId: string | undefined;\n}\n\nfunction buildQueryString(query: Record<string, string | string[] | undefined> | undefined): string {\n if (!query) return '';\n const sp = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue;\n if (Array.isArray(v)) {\n for (const item of v) sp.append(k, item);\n } else {\n sp.append(k, v);\n }\n }\n const qs = sp.toString();\n return qs ? `?${qs}` : '';\n}\n\n/**\n * Internal helper: calls the Partner API with a Bearer token, optional\n * Idempotency-Key, retries, and parses the response (success or error envelope).\n */\nexport async function callUpstream(\n apiBaseUrl: string,\n tokenManager: TokenManager,\n opts: UpstreamCallOptions,\n fetchImpl: typeof globalThis.fetch,\n): Promise<UpstreamCallResult> {\n const base = apiBaseUrl.replace(/\\/$/, '');\n const url = `${base}${opts.path}${buildQueryString(opts.query)}`;\n\n // Always prime the TokenManager so the M2M credential is ready for other routes.\n // When a per-call session bearer is provided, override the Authorization header\n // with that bearer instead of the M2M token.\n const m2mToken = await tokenManager.getAccessToken();\n const bearerToken = opts.sessionBearer ?? m2mToken;\n const headers: Record<string, string> = {\n Authorization: `Bearer ${bearerToken}`,\n Accept: 'application/json',\n };\n if (opts.body !== undefined) headers['Content-Type'] = 'application/json';\n if (opts.idempotencyKey) headers['Idempotency-Key'] = opts.idempotencyKey;\n\n const init: RequestInit = {\n method: opts.method,\n headers,\n };\n if (opts.body !== undefined) init.body = JSON.stringify(opts.body);\n\n // A 401 on an overridden session-token call is terminal — refreshing the M2M\n // token would only swap in a token the route also rejects, masking the real error.\n const onTokenExpired = opts.sessionBearer\n ? undefined\n : async () => {\n await tokenManager.invalidate();\n const fresh = await tokenManager.getAccessToken();\n headers.Authorization = `Bearer ${fresh}`;\n };\n\n const res = await retryFetch(url, init, {\n fetch: fetchImpl,\n timeoutMs: opts.timeoutMs,\n onTokenExpired,\n });\n\n let body: unknown = null;\n const text = await res.text();\n if (text.length > 0) {\n try {\n body = JSON.parse(text);\n } catch {\n body = text;\n }\n }\n\n return {\n status: res.status,\n headers: res.headers,\n body,\n requestId: res.headers.get('x-hellobill-request-id') ?? undefined,\n };\n}\n","import { randomUUID } from 'node:crypto';\n\n/** Default idempotency key generator — UUID v4 via Node crypto. */\nexport function defaultIdempotencyKey(): string {\n return randomUUID();\n}\n","import type { ErrorEnvelope, RequestId } from '../types/index.js';\n\n/**\n * Thrown when an upstream call returns a non-2xx response.\n * Handlers catch and pass through the envelope verbatim to the caller.\n *\n * `headers` carries the upstream response headers so callers can read\n * rate-limit budget (X-RateLimit-Remaining) and back-off hints (Retry-After)\n * after retries are exhausted.\n */\nexport class HellobillApiError extends Error {\n readonly status: number;\n readonly envelope: ErrorEnvelope | null;\n readonly requestId: RequestId | undefined;\n readonly headers: Headers | undefined;\n\n constructor(\n status: number,\n envelope: ErrorEnvelope | null,\n requestId?: RequestId,\n headers?: Headers,\n ) {\n super(envelope?.error.message ?? `HelloBill API error (${status})`);\n this.name = 'HellobillApiError';\n this.status = status;\n this.envelope = envelope;\n this.requestId = requestId;\n this.headers = headers;\n }\n\n get code(): string | undefined {\n return this.envelope?.error.code;\n }\n}\n","import { HellobillApiError } from './errors.js';\n\nexport interface TokenStorage {\n get(clientId: string): Promise<{ accessToken: string; expiresAt: number } | null>;\n set(clientId: string, value: { accessToken: string; expiresAt: number }): Promise<void>;\n}\n\nexport interface Logger {\n debug(msg: string, meta?: Record<string, unknown>): void;\n /**\n * Optional — added for unhandled-variant and dedup-hit logs in the webhook\n * receiver. Existing `Logger` literals (without `info`) continue to satisfy\n * the interface because `info` is optional.\n */\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\nexport interface TokenManagerOptions {\n clientId: string;\n clientSecret: string;\n apiBaseUrl: string;\n fetch?: typeof globalThis.fetch;\n storage?: TokenStorage;\n logger?: Logger;\n /** Renew tokens this many ms before expiry. Default 30_000. */\n refreshLeewayMs?: number;\n /** Override `Date.now()` for tests. */\n now?: () => number;\n}\n\ninterface TokenResponse {\n access_token: string;\n token_type: 'Bearer';\n expires_in: number;\n}\n\n/** Default in-memory single-instance storage. */\nexport class InMemoryTokenStorage implements TokenStorage {\n private readonly map = new Map<string, { accessToken: string; expiresAt: number }>();\n\n async get(clientId: string) {\n return this.map.get(clientId) ?? null;\n }\n\n async set(clientId: string, value: { accessToken: string; expiresAt: number }) {\n this.map.set(clientId, value);\n }\n}\n\n/**\n * Manages partner client_credentials tokens with caching, leeway-based renewal,\n * and single-flight refresh. Pluggable storage for multi-instance deployments.\n */\nexport class TokenManager {\n private readonly clientId: string;\n private readonly clientSecret: string;\n private readonly apiBaseUrl: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n private readonly storage: TokenStorage;\n private readonly logger: Logger | undefined;\n private readonly refreshLeewayMs: number;\n private readonly now: () => number;\n private inflight: Promise<string> | null = null;\n\n constructor(opts: TokenManagerOptions) {\n this.clientId = opts.clientId;\n this.clientSecret = opts.clientSecret;\n this.apiBaseUrl = opts.apiBaseUrl.replace(/\\/$/, '');\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n this.storage = opts.storage ?? new InMemoryTokenStorage();\n this.logger = opts.logger;\n this.refreshLeewayMs = opts.refreshLeewayMs ?? 30_000;\n this.now = opts.now ?? Date.now;\n }\n\n /** Returns a valid bearer token, refreshing if necessary. Single-flight on refresh. */\n async getAccessToken(): Promise<string> {\n if (this.inflight) return this.inflight;\n\n const cached = await this.storage.get(this.clientId);\n if (cached && cached.accessToken && cached.expiresAt - this.refreshLeewayMs > this.now()) {\n return cached.accessToken;\n }\n\n if (this.inflight) return this.inflight;\n\n this.inflight = this.fetchToken().finally(() => {\n this.inflight = null;\n });\n\n return this.inflight;\n }\n\n /** Forces invalidation of the cached token. Next `getAccessToken` will refresh. */\n async invalidate(): Promise<void> {\n await this.storage.set(this.clientId, { accessToken: '', expiresAt: 0 });\n }\n\n private async fetchToken(): Promise<string> {\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n });\n\n const res = await this.fetchImpl(`${this.apiBaseUrl}/auth/partner/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n },\n body: body.toString(),\n });\n\n if (!res.ok) {\n let envelope = null;\n try {\n envelope = await res.json();\n } catch {\n // ignore parse errors\n }\n const requestId = res.headers.get('x-hellobill-request-id') ?? undefined;\n this.logger?.error('hellobill.token_refresh_failed', { status: res.status });\n throw new HellobillApiError(res.status, envelope, requestId, res.headers);\n }\n\n const json = (await res.json()) as TokenResponse;\n const expiresAt = this.now() + json.expires_in * 1000;\n await this.storage.set(this.clientId, {\n accessToken: json.access_token,\n expiresAt,\n });\n this.logger?.debug('hellobill.token_refreshed', { expires_in: json.expires_in });\n return json.access_token;\n }\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { WebhookEvent } from '../types/index.js';\n\nexport interface VerifyWebhookOptions {\n /** Replay window in seconds. Default 300. */\n tolerance?: number;\n /** Signature versions to accept. Default ['v1']. Use ['v1', 'v2'] during key rotation. */\n supportedVersions?: ('v1' | 'v2')[];\n /** Override `Date.now()` for tests. */\n now?: () => number;\n}\n\nexport class WebhookVerificationError extends Error {\n constructor(\n message: string,\n public code: string,\n ) {\n super(message);\n this.name = 'WebhookVerificationError';\n }\n}\n\ninterface ParsedHeader {\n t: number;\n signatures: Record<string, string>;\n}\n\nfunction parseHeader(signatureHeader: string): ParsedHeader {\n if (!signatureHeader || typeof signatureHeader !== 'string') {\n throw new WebhookVerificationError(\n 'Missing signature header',\n 'signature.missing_header',\n );\n }\n\n const parts = signatureHeader.split(',').map((p) => p.trim()).filter(Boolean);\n let t: number | undefined;\n const signatures: Record<string, string> = {};\n\n for (const part of parts) {\n const eq = part.indexOf('=');\n if (eq === -1) {\n throw new WebhookVerificationError(\n 'Malformed signature header element',\n 'signature.malformed',\n );\n }\n const key = part.slice(0, eq);\n const value = part.slice(eq + 1);\n if (key === 't') {\n t = Number(value);\n if (!Number.isFinite(t)) {\n throw new WebhookVerificationError(\n 'Malformed timestamp in signature header',\n 'signature.malformed',\n );\n }\n } else if (key.length > 0 && value.length > 0) {\n signatures[key] = value;\n }\n }\n\n if (t === undefined) {\n throw new WebhookVerificationError(\n 'Missing timestamp in signature header',\n 'signature.malformed',\n );\n }\n if (Object.keys(signatures).length === 0) {\n throw new WebhookVerificationError(\n 'No signature values in header',\n 'signature.malformed',\n );\n }\n\n return { t, signatures };\n}\n\nfunction computeHmacHex(secret: string, payload: string): string {\n return createHmac('sha256', secret).update(payload).digest('hex');\n}\n\nfunction constantTimeHexEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n try {\n return timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'));\n } catch {\n return false;\n }\n}\n\n/**\n * Verifies a HelloBill webhook delivery.\n * Header format: `t=<unix>,v1=<hex>,v2=<hex>`.\n * HMAC computed over `${t}.${rawBody}` (raw bytes — do not re-serialise).\n */\nexport function verifyWebhook(\n rawBody: Buffer | string,\n signatureHeader: string,\n secret: string,\n opts: VerifyWebhookOptions = {},\n): WebhookEvent {\n const tolerance = opts.tolerance ?? 300;\n const supportedVersions = opts.supportedVersions ?? ['v1'];\n const now = opts.now ?? Date.now;\n\n const { t, signatures } = parseHeader(signatureHeader);\n\n const nowSeconds = Math.floor(now() / 1000);\n const skew = Math.abs(nowSeconds - t);\n if (skew > tolerance) {\n throw new WebhookVerificationError(\n `Signature timestamp outside tolerance (${skew}s > ${tolerance}s)`,\n 'signature.expired',\n );\n }\n\n const hasAnyKnownVersion = supportedVersions.some((v) => v in signatures);\n if (!hasAnyKnownVersion) {\n throw new WebhookVerificationError(\n 'No supported signature version present in header',\n 'signature.unsupported_version',\n );\n }\n\n const bodyStr =\n typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');\n const signingInput = `${t}.${bodyStr}`;\n\n for (const version of supportedVersions) {\n const provided = signatures[version];\n if (!provided) continue;\n const expected = computeHmacHex(secret, signingInput);\n if (constantTimeHexEqual(expected, provided)) {\n try {\n return JSON.parse(bodyStr) as WebhookEvent;\n } catch {\n throw new WebhookVerificationError(\n 'Verified payload is not valid JSON',\n 'signature.malformed',\n );\n }\n }\n }\n\n throw new WebhookVerificationError(\n 'Signature does not match expected value',\n 'signature.invalid',\n );\n}\n\n/**\n * Verifies a HelloBill webhook delivery without parsing the payload.\n * Returns `true` only if the timestamp is within tolerance AND at least one\n * supported scheme verifies. Returns `false` for every other case (missing/\n * malformed header, expired timestamp, no supported version, signature\n * mismatch). Never throws on verification failure — callers route on the\n * boolean. Re-throws non-verification errors (e.g. unexpected crypto failures).\n *\n * Unlike the throwing `verifyWebhook`, this function does NOT attempt to parse\n * the body as JSON — signature verification is purely over the raw bytes.\n * Callers that need the parsed event should call `verifyWebhook` directly or\n * parse separately after calling this function.\n *\n * @param rawBody Raw request body bytes (NOT re-serialised JSON).\n * @param signatureHeader Value of `X-HelloBill-Signature`.\n * @param secret Partner-issued webhook secret (env `HELLOBILL_WEBHOOK_SECRET`).\n * @param opts Optional `tolerance` (default 300s), `supportedVersions`\n * (default `['v1']`), `now` (test injection).\n */\nexport function verifyWebhookSignature(\n rawBody: Buffer | string,\n signatureHeader: string,\n secret: string,\n opts: VerifyWebhookOptions = {},\n): boolean {\n const tolerance = opts.tolerance ?? 300;\n const supportedVersions = opts.supportedVersions ?? ['v1'];\n const now = opts.now ?? Date.now;\n\n let parsed: ParsedHeader;\n try {\n parsed = parseHeader(signatureHeader);\n } catch (err) {\n if (err instanceof WebhookVerificationError) return false;\n throw err;\n }\n\n const { t, signatures } = parsed;\n\n const nowSeconds = Math.floor(now() / 1000);\n const skew = Math.abs(nowSeconds - t);\n if (skew > tolerance) return false;\n\n const hasAnyKnownVersion = supportedVersions.some((v) => v in signatures);\n if (!hasAnyKnownVersion) return false;\n\n const bodyStr =\n typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');\n const signingInput = `${t}.${bodyStr}`;\n\n for (const version of supportedVersions) {\n const provided = signatures[version];\n if (!provided) continue;\n const expected = computeHmacHex(secret, signingInput);\n if (constantTimeHexEqual(expected, provided)) {\n return true;\n }\n }\n\n return false;\n}\n","/**\n * Webhook idempotency dedupe store. Keyed on `WebhookEnvelope.id`.\n *\n * Default `InMemoryWebhookDedupeStore` is fine for single-process deployments\n * (and unit tests). Multi-replica deployments MUST inject a shared backend\n * (e.g. Redis SETEX, RDS `INSERT ... ON CONFLICT DO NOTHING`). Per spec §11.1\n * line 2270: \"Use the `id` field to deduplicate.\"\n */\nexport interface WebhookDedupeStore {\n /** Returns true if `id` has been recorded within its TTL. Never throws on miss. */\n has(id: string): Promise<boolean> | boolean;\n /** Records `id` with the given TTL in seconds. Idempotent on repeat calls. */\n record(id: string, ttlSeconds: number): Promise<void> | void;\n}\n\n/** In-memory implementation. NOT durable across process restarts. Tests + single-proc deploys. */\nexport class InMemoryWebhookDedupeStore implements WebhookDedupeStore {\n private readonly store = new Map<string, number>();\n\n // Optional test injection. Defaults to Date.now.\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n async has(id: string): Promise<boolean> {\n const expiresAt = this.store.get(id);\n if (expiresAt === undefined) return false;\n // Inclusive boundary: at exactly expiresAt, treat as expired. Tested via handler test 6.5.\n if (expiresAt <= this.now()) {\n this.store.delete(id);\n return false;\n }\n return true;\n }\n\n async record(id: string, ttlSeconds: number): Promise<void> {\n this.store.set(id, this.now() + ttlSeconds * 1000);\n }\n}\n","import type {\n BankDetailsCollected,\n MoveOutNotificationFailed,\n MoveOutNotificationSent,\n SessionAccountCreated,\n SessionAppDeferred,\n SessionAppInstalled,\n SessionDetailsConfirmed,\n SessionEmbedOpened,\n SessionLoaSigned,\n SessionProductsSelected,\n SetupStatusChanged,\n SubscriptionChanged,\n WebhookEvent,\n} from '../types/index.js';\nimport type { Logger } from '../core/token-manager.js';\nimport { verifyWebhookSignature, type VerifyWebhookOptions } from './verify.js';\nimport {\n InMemoryWebhookDedupeStore,\n type WebhookDedupeStore,\n} from './dedupe-store.js';\nimport type { HellobillRequest, HellobillResponse } from '../core/handler.js';\n\n/** Per-event runtime context passed to every typed handler. */\nexport interface WebhookHandlerContext {\n /**\n * Logger guaranteed to have `info` defined — sourced from `withInfoFallback`\n * at handler construction. Partner handlers may call `ctx.logger.info(...)`\n * without a null-check regardless of what Logger literal was supplied.\n */\n logger: Logger & { info: (msg: string, meta?: Record<string, unknown>) => void };\n headers: Record<string, string | string[] | undefined>;\n deprecation: string | null;\n}\n\n/**\n * Typed handlers — one per `WebhookEvent` variant. All optional; an\n * unprovided variant logs at info (`unhandled_variant`) and the dispatcher\n * still responds 2xx so HelloBill stops retrying.\n *\n * Handlers run async AFTER the route has already responded; throwing\n * from a handler does NOT cause HelloBill to retry (a 2xx was already\n * written). Errors are logged via `opts.logger?.error`.\n */\nexport interface WebhookHandlers {\n onSessionEmbedOpened?(event: SessionEmbedOpened, ctx: WebhookHandlerContext): Promise<void>;\n onSessionDetailsConfirmed?(event: SessionDetailsConfirmed, ctx: WebhookHandlerContext): Promise<void>;\n onSessionProductsSelected?(event: SessionProductsSelected, ctx: WebhookHandlerContext): Promise<void>;\n onSessionLoaSigned?(event: SessionLoaSigned, ctx: WebhookHandlerContext): Promise<void>;\n onSessionAccountCreated?(event: SessionAccountCreated, ctx: WebhookHandlerContext): Promise<void>;\n onSessionAppInstalled?(event: SessionAppInstalled, ctx: WebhookHandlerContext): Promise<void>;\n onSessionAppDeferred?(event: SessionAppDeferred, ctx: WebhookHandlerContext): Promise<void>;\n onSubscriptionChanged?(event: SubscriptionChanged, ctx: WebhookHandlerContext): Promise<void>;\n onSetupStatusChanged?(event: SetupStatusChanged, ctx: WebhookHandlerContext): Promise<void>;\n onMoveOutNotificationSent?(event: MoveOutNotificationSent, ctx: WebhookHandlerContext): Promise<void>;\n onMoveOutNotificationFailed?(event: MoveOutNotificationFailed, ctx: WebhookHandlerContext): Promise<void>;\n onBankDetailsCollected?(event: BankDetailsCollected, ctx: WebhookHandlerContext): Promise<void>;\n}\n\n/** Options for the webhook route. Lives alongside `CreateHellobillHandlerOptions`. */\nexport interface WebhookHandlerOptions {\n /**\n * Partner webhook secret. From `process.env.HELLOBILL_WEBHOOK_SECRET`.\n * MUST be a non-empty string at `createWebhookHandler` call time — the\n * factory throws synchronously on `null`/`undefined`/`''` so that an\n * unset env var is caught immediately rather than silently 401ing every\n * incoming webhook.\n */\n webhookSecret: string;\n /**\n * Optional dedupe-store. Default: in-memory (single-process). Multi-replica\n * deployments MUST supply a shared backend (Redis, RDS, etc.) — see README.\n */\n dedupeStore?: WebhookDedupeStore;\n /**\n * Idempotency window in seconds. Default 86_400 (24h). Per spec §11.1 line 2270.\n */\n dedupeTtlSeconds?: number;\n /** Signature versions accepted. Default `['v1']`. Set to `['v1','v2']` during rotation. */\n supportedVersions?: VerifyWebhookOptions['supportedVersions'];\n /** Timestamp tolerance in seconds. Default 300. */\n tolerance?: number;\n /** Optional logger. Defaults to no-op. */\n logger?: Logger;\n /** Typed handlers — provide as many as you care about; missing ones log \"unhandled\". */\n handlers?: WebhookHandlers;\n /**\n * Test injection for `Date.now()`. Applied to the signature timestamp AND,\n * only when using the default `InMemoryWebhookDedupeStore`, the dedupe TTL\n * clock. Partner-supplied `dedupeStore` instances MUST own their own clock\n * injection — `opts.now` is NOT forwarded to a partner-supplied store.\n */\n now?: () => number;\n}\n\n/**\n * Thrown by `assertNever` when an event arrives whose `type` is not in the\n * known union — typically a newer platform event that this SDK version does\n * not yet handle. The `code` discriminator lets partners identify this class\n * of error in log aggregation without an `instanceof` check.\n */\nexport class UnknownWebhookEventError extends Error {\n readonly code = 'webhook.unknown_event_type' as const;\n\n constructor(eventType: string) {\n super(`Unhandled webhook event type: ${eventType}`);\n this.name = 'UnknownWebhookEventError';\n // Restore correct prototype chain in environments where extending Error\n // loses the prototype (pre-ES2015 transpilation targets).\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Sentinel — placed on the receiver's `default:` branch to surface compile-time exhaustiveness. */\nfunction assertNever(x: never): never {\n throw new UnknownWebhookEventError((x as { type?: string }).type ?? JSON.stringify(x));\n}\n\nconst noopLogger: Logger & { info: () => void } = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Coalesces a partner-supplied Logger literal that may omit the optional `info`\n * method. Without this, `ctx.logger.info(...)` inside `logUnhandled` raises a\n * runtime TypeError when the partner's Logger only provides debug/warn/error.\n */\nfunction withInfoFallback(\n logger: Logger | undefined,\n): Logger & { info: (msg: string, meta?: Record<string, unknown>) => void } {\n return {\n debug: logger?.debug?.bind(logger) ?? (() => {}),\n info: logger?.info?.bind(logger) ?? (() => {}),\n warn: logger?.warn?.bind(logger) ?? (() => {}),\n error: logger?.error?.bind(logger) ?? (() => {}),\n };\n}\n\nfunction headerValue(\n headers: Record<string, string | string[] | undefined>,\n name: string,\n): string | undefined {\n const lower = name.toLowerCase();\n for (const [k, v] of Object.entries(headers)) {\n if (k.toLowerCase() === lower) return Array.isArray(v) ? v[0] : v;\n }\n return undefined;\n}\n\n/**\n * Dispatch a verified-and-deduped event to its typed handler. Exhaustive over\n * the union. Missing handler slots fall through to `logUnhandled` so a\n * partner-only-cares-about-bank-details deployment never 500s on an\n * unrecognised event type.\n */\nasync function dispatch(\n event: WebhookEvent,\n ctx: WebhookHandlerContext,\n handlers: WebhookHandlers,\n): Promise<void> {\n const logUnhandled = (name: string): void => {\n ctx.logger.info('hellobill.webhook.unhandled_variant', {\n event_id: event.id,\n event_type: event.type,\n handler: name,\n });\n };\n\n switch (event.type) {\n case 'session.embed_opened':\n if (handlers.onSessionEmbedOpened) await handlers.onSessionEmbedOpened(event, ctx);\n else logUnhandled('onSessionEmbedOpened');\n return;\n case 'session.details_confirmed':\n if (handlers.onSessionDetailsConfirmed) await handlers.onSessionDetailsConfirmed(event, ctx);\n else logUnhandled('onSessionDetailsConfirmed');\n return;\n case 'session.products_selected':\n if (handlers.onSessionProductsSelected) await handlers.onSessionProductsSelected(event, ctx);\n else logUnhandled('onSessionProductsSelected');\n return;\n case 'session.loa_signed':\n if (handlers.onSessionLoaSigned) await handlers.onSessionLoaSigned(event, ctx);\n else logUnhandled('onSessionLoaSigned');\n return;\n case 'session.account_created':\n if (handlers.onSessionAccountCreated) await handlers.onSessionAccountCreated(event, ctx);\n else logUnhandled('onSessionAccountCreated');\n return;\n case 'session.app_installed':\n if (handlers.onSessionAppInstalled) await handlers.onSessionAppInstalled(event, ctx);\n else logUnhandled('onSessionAppInstalled');\n return;\n case 'session.app_deferred':\n if (handlers.onSessionAppDeferred) await handlers.onSessionAppDeferred(event, ctx);\n else logUnhandled('onSessionAppDeferred');\n return;\n case 'subscription.changed':\n if (handlers.onSubscriptionChanged) await handlers.onSubscriptionChanged(event, ctx);\n else logUnhandled('onSubscriptionChanged');\n return;\n case 'setup_status.changed':\n if (handlers.onSetupStatusChanged) await handlers.onSetupStatusChanged(event, ctx);\n else logUnhandled('onSetupStatusChanged');\n return;\n case 'move_out_notification.sent':\n if (handlers.onMoveOutNotificationSent) await handlers.onMoveOutNotificationSent(event, ctx);\n else logUnhandled('onMoveOutNotificationSent');\n return;\n case 'move_out_notification.failed':\n if (handlers.onMoveOutNotificationFailed) await handlers.onMoveOutNotificationFailed(event, ctx);\n else logUnhandled('onMoveOutNotificationFailed');\n return;\n case 'bank_details.collected':\n if (handlers.onBankDetailsCollected) await handlers.onBankDetailsCollected(event, ctx);\n else logUnhandled('onBankDetailsCollected');\n return;\n default:\n // Exhaustiveness — if Ticket #1 adds a 13th variant, this fails to compile.\n return assertNever(event);\n }\n}\n\n/**\n * Creates the framework-agnostic webhook receive handler. Returns a function\n * matching the same `HellobillRequest`/`HellobillResponse` shape used by the\n * other routes on `HellobillHandlerSet` (see `core/handler.ts`).\n *\n * Throws synchronously if `opts.webhookSecret` is falsy — a missing or\n * empty secret is caught early so that every incoming webhook does not\n * silently 401 with no visible cause.\n *\n * The Express adapter wires this in alongside `session`/`loa`/etc.\n */\nexport function createWebhookHandler(\n opts: WebhookHandlerOptions,\n): (req: HellobillRequest, res: HellobillResponse) => Promise<void> {\n // Runtime guard: `process.env.HELLOBILL_WEBHOOK_SECRET!` bang-assertion satisfies\n // the TypeScript compiler but does not catch the case where the env var is unset at\n // runtime. Without this guard, every webhook would 401 silently because\n // verifyWebhookSignature would always return false for an undefined secret.\n if (opts.webhookSecret == null || opts.webhookSecret === '') {\n throw new Error(\n 'HELLOBILL_WEBHOOK_SECRET must be set; see spec §13.2 and ' +\n 'the @hello-bill/node README \"Webhook receiver\" section.',\n );\n }\n\n const now = opts.now ?? (() => Date.now());\n const dedupeStore = opts.dedupeStore ?? new InMemoryWebhookDedupeStore(now);\n const dedupeTtlSeconds = opts.dedupeTtlSeconds ?? 86_400;\n const tolerance = opts.tolerance ?? 300;\n const supportedVersions = opts.supportedVersions ?? ['v1'];\n const handlers = opts.handlers ?? {};\n // Coalesce partner-supplied Logger that may omit the optional `info` method.\n // Without this, `ctx.logger.info(...)` inside logUnhandled raises a runtime\n // TypeError when the partner Logger only provides debug/warn/error.\n const safeLogger = withInfoFallback(opts.logger ?? noopLogger);\n\n return async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const rawBody = req.rawBody;\n if (!rawBody) {\n safeLogger.error('hellobill.webhook.missing_raw_body', {});\n res.status(500).json({\n error: {\n type: 'api_error',\n code: 'server.internal',\n message: 'Webhook receiver requires raw body — adapter not wired correctly',\n },\n });\n return;\n }\n\n const sigHeader = headerValue(req.headers, 'x-hellobill-signature');\n if (!sigHeader) {\n res.status(401).json({ error: 'invalid_signature' });\n return;\n }\n\n const valid = verifyWebhookSignature(rawBody, sigHeader, opts.webhookSecret, {\n tolerance,\n supportedVersions,\n now,\n });\n if (!valid) {\n res.status(401).json({ error: 'invalid_signature' });\n return;\n }\n\n // Parse AFTER verification — verify operates on raw bytes by contract per spec §11.2.\n let event: WebhookEvent;\n try {\n event = JSON.parse(rawBody.toString('utf8')) as WebhookEvent;\n } catch {\n res.status(400).json({ error: 'invalid_payload' });\n return;\n }\n\n // Dedupe — record-before-dispatch so multi-replica double-delivery cannot\n // double-process. The crash-window caveat (rare loss) is documented and is\n // the at-most-once-after-2xx contract.\n const seen = await dedupeStore.has(event.id);\n if (seen) {\n safeLogger.info('hellobill.webhook.dedup_hit', { event_id: event.id, event_type: event.type });\n res.status(200).json({ received: true, deduped: true });\n return;\n }\n await dedupeStore.record(event.id, dedupeTtlSeconds);\n\n const deprecation = headerValue(req.headers, 'x-hellobill-deprecation') ?? null;\n if (deprecation) {\n safeLogger.warn('hellobill.webhook.deprecation_notice', {\n deprecation,\n event_id: event.id,\n event_type: event.type,\n });\n }\n\n // Respond 2xx FIRST. Per spec §11.1 line 2272: respond within 10s, process async.\n res.status(200).json({ received: true });\n\n const ctx: WebhookHandlerContext = {\n // safeLogger guarantees `info` is defined regardless of partner Logger shape.\n logger: safeLogger,\n headers: req.headers,\n deprecation,\n };\n\n // Fire-and-forget. Errors logged; HelloBill's retry layer is the durability mechanism.\n void dispatch(event, ctx, handlers).catch((err) => {\n safeLogger.error('hellobill.webhook.handler_error', {\n event_id: event.id,\n event_type: event.type,\n message: (err as Error).message,\n // Include the typed error code when present so partners can identify\n // known error classes (e.g. unknown event type) in log aggregation.\n ...(err != null && typeof err === 'object' && 'code' in err\n ? { code: (err as { code: unknown }).code }\n : {}),\n });\n });\n };\n}\n","import type { SessionPayload } from '../types/index.js';\nimport { callUpstream, type UpstreamCallOptions, type UpstreamCallResult } from './client.js';\nimport { defaultIdempotencyKey } from './idempotency.js';\nimport { NetworkError } from './retry.js';\nimport {\n InMemoryTokenStorage,\n TokenManager,\n type Logger,\n type TokenStorage,\n} from './token-manager.js';\nimport { createWebhookHandler, type WebhookHandlerOptions } from '../webhook/handler.js';\n\nexport interface HellobillRequest {\n method: string;\n url: string;\n query: Record<string, string | string[] | undefined>;\n body: unknown;\n headers: Record<string, string | string[] | undefined>;\n /**\n * Path parameters extracted by the adapter's router and populated on the\n * way in. Example: Express maps `:id` on `router.get('/sessions/:id', ...)`\n * into `req.params.id`; the SDK Express adapter forwards that verbatim via\n * `params: req.params`. Adapters that do not expose path parameters leave\n * this field `undefined` for the 8 contractual routes and synthesise\n * `params: { id }` only for the `/sessions/:id` branch.\n *\n * Optional. Backwards-compatible — adapters that pre-date this field\n * type-check unchanged.\n */\n params?: Record<string, string | undefined>;\n /** Optional raw request body for webhook verification — populated by adapter. */\n rawBody?: Buffer;\n}\n\nexport interface HellobillResponse {\n status(code: number): HellobillResponse;\n header(name: string, value: string): HellobillResponse;\n json(body: unknown): void;\n}\n\nexport interface CreateHellobillHandlerOptions {\n clientId: string;\n clientSecret: string;\n /** Partner API base URL, e.g. https://api.hellobill.app/api/v1 or http://localhost:4100. */\n apiBaseUrl: string;\n /**\n * Called on POST /session to assemble the canonical SessionPayload from whatever\n * the partner's host page sent. Receives the request unchanged and the resolved\n * partner session (if configured, or null if `partnerSession` is absent).\n *\n * When `partnerSession` is configured, this function MUST source\n * `customer.email` from the second argument — never from `req.body` —\n * to prevent the browser from spoofing the email address.\n */\n buildSessionPayload: (\n req: HellobillRequest,\n partnerSession: unknown | null,\n ) => Promise<SessionPayload> | SessionPayload;\n /**\n * Optional. Resolves the partner's authenticated server-side session from the\n * incoming request. Result (if non-null) is passed to `buildSessionPayload`\n * as the second argument.\n *\n * Returning `null` causes the middleware to return `401 auth.invalid_credentials`\n * before invoking `buildSessionPayload` or calling the upstream API.\n * Throwing causes a `500 internal.error` response and a warn-level log.\n * When absent, `buildSessionPayload` is called with `null` as the second arg;\n * the partner's `buildSessionPayload` is then solely responsible for any\n * CSRF / origin / session checks.\n */\n partnerSession?: (req: HellobillRequest) => Promise<unknown | null> | unknown | null;\n /** Optional override for idempotency key generation. Defaults to crypto.randomUUID(). */\n idempotencyKeyFactory?: () => string;\n /** Optional token storage adapter for multi-instance deployments. Default: in-memory. */\n tokenStorage?: TokenStorage;\n /** Custom fetch implementation (for tests). Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch;\n /** Optional logger. Defaults to no-op. Receives safe (no-secret) log lines. */\n logger?: Logger;\n /**\n * Per-attempt upstream request timeout in ms. Default 30_000 (30s). Each retry\n * starts a fresh timeout. Network failures and timeouts are surfaced as a\n * clean `internal.error` envelope.\n */\n upstreamTimeoutMs?: number;\n /**\n * Optional webhook receiver configuration. When provided, exposes a\n * `webhook` handler on `HellobillHandlerSet` that the Express adapter\n * mounts at POST /webhooks. Omit to skip the route entirely.\n */\n webhook?: import('../webhook/handler.js').WebhookHandlerOptions;\n}\n\nexport interface HellobillHandlerSet {\n session: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n loa: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n bankDetails: {\n submit: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n get: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n };\n customers: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n status: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n products: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n property: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n /**\n * Partner-side session recovery: list or fetch a single session by id.\n * Both proxy the bearer-gated upstream endpoints without requiring a\n * session_token — they are scoped to the partner's access_token.\n *\n * Query params on `list` pass through verbatim (including non-spec keys;\n * upstream owns 400-on-unknown-filter semantics). The `get` handler reads\n * the session id from `req.params.id`.\n *\n * Additive: not part of the v1.15 contract (spec §9.2.1). The reference SDK\n * always includes them; partners may opt out by substituting a handler that\n * returns 404.\n */\n sessions: {\n list: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n get: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n };\n /**\n * Proxies GET /partner/customers/:id/savings-summary.\n * Reads customer_id from req.query.customer_id or req.params.id.\n * Scoped to the partner's access_token — no session_token required.\n */\n savingsSummary: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n /**\n * Proxies GET /partner/sessions/:id/service-slots.\n * Reads session_id from req.query.session_id or req.params.id.\n * Reads service (required) and provider (optional, defaults to 'any_van') from req.query.\n * Returns 400 if service is missing.\n * Scoped to the partner's access_token — no session_token required.\n */\n serviceSlots: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n /**\n * Proxies GET /partner/loa/:id/document.\n * Reads the loa id from req.query.loa_id or req.params.id.\n * Forwards the inbound session_token bearer upstream: the document route is\n * session-token-scoped, so a missing bearer returns 401 with no upstream call.\n */\n loaDocument: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n /**\n * Optional webhook receiver. Present only when `opts.webhook` is supplied to\n * `createHellobillHandler`. The Express adapter mounts POST /webhooks when\n * this slot is defined, and omits the route when it is `undefined`.\n */\n webhook?: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n}\n\n// Re-export for the public barrel.\nexport type { TokenStorage, Logger } from './token-manager.js';\n\ninterface SessionTokenClaims {\n session_id: string;\n exp?: number;\n [key: string]: unknown;\n}\n\n/** Base64url decode a JWT segment without verification. Exported for tests. */\nexport function decodeSessionToken(token: string): SessionTokenClaims {\n const parts = token.split('.');\n if (parts.length < 2) {\n throw new SessionTokenError('auth.token_malformed', 'Session token is malformed');\n }\n let payload: SessionTokenClaims;\n try {\n const raw = Buffer.from(parts[1] as string, 'base64url').toString('utf8');\n payload = JSON.parse(raw) as SessionTokenClaims;\n } catch {\n throw new SessionTokenError('auth.token_malformed', 'Session token payload unparseable');\n }\n // Partner-api tokens carry the session identifier as `sub` (RFC 7519 standard claim).\n // Older internal tokens used `session_id`. Accept either; normalise to session_id.\n const sessionId =\n typeof payload.session_id === 'string' && payload.session_id.length > 0\n ? payload.session_id\n : typeof payload.sub === 'string' && (payload.sub as string).length > 0\n ? (payload.sub as string)\n : '';\n if (sessionId === '') {\n throw new SessionTokenError('auth.token_malformed', 'Session token missing session_id (or sub)');\n }\n payload.session_id = sessionId;\n return payload;\n}\n\nclass SessionTokenError extends Error {\n constructor(public code: string, message: string) {\n super(message);\n }\n}\n\nfunction getHeader(req: HellobillRequest, name: string): string | undefined {\n const lower = name.toLowerCase();\n for (const [k, v] of Object.entries(req.headers)) {\n if (k.toLowerCase() === lower) {\n return Array.isArray(v) ? v[0] : v;\n }\n }\n return undefined;\n}\n\nfunction extractBearer(req: HellobillRequest): string | undefined {\n const auth = getHeader(req, 'authorization');\n if (!auth) return undefined;\n const match = /^Bearer\\s+(.+)$/i.exec(auth);\n return match?.[1]?.trim();\n}\n\nfunction sendError(\n res: HellobillResponse,\n status: number,\n code: string,\n message: string,\n requestId?: string,\n): void {\n if (requestId) res.header('X-HelloBill-Request-Id', requestId);\n const type =\n status === 401 || status === 403\n ? 'authentication_error'\n : status === 429\n ? 'rate_limit_error'\n : status >= 500\n ? 'api_error'\n : 'validation_error';\n res.status(status).json({\n error: {\n type,\n code,\n message,\n request_id: requestId ?? '',\n },\n });\n}\n\n// Hard-coded allowlist: lowercase upstream header name → RFC-7230 title-case to emit.\n// No algorithmic casing — exactly these five entries cross the boundary.\n// Set-Cookie, Authorization, and WWW-Authenticate are deliberately excluded.\nconst PASSTHROUGH_HEADER_MAP: Record<string, string> = {\n 'retry-after': 'Retry-After',\n 'x-ratelimit-limit': 'X-RateLimit-Limit',\n 'x-ratelimit-remaining': 'X-RateLimit-Remaining',\n 'x-ratelimit-reset': 'X-RateLimit-Reset',\n};\n\nfunction passthroughUpstream(\n res: HellobillResponse,\n status: number,\n body: unknown,\n requestId: string | undefined,\n upstreamHeaders?: Headers,\n): void {\n if (requestId) res.header('X-HelloBill-Request-Id', requestId);\n if (upstreamHeaders) {\n for (const [lower, emit] of Object.entries(PASSTHROUGH_HEADER_MAP)) {\n const v = upstreamHeaders.get(lower);\n if (v !== null && v !== undefined && v !== '') {\n res.header(emit, v);\n }\n }\n }\n res.status(status).json(body);\n}\n\nfunction ensureSessionContext(\n req: HellobillRequest,\n res: HellobillResponse,\n): { sessionId: string } | null {\n const token = extractBearer(req);\n if (!token) {\n sendError(res, 401, 'auth.token_malformed', 'Missing Authorization bearer token');\n return null;\n }\n try {\n const claims = decodeSessionToken(token);\n return { sessionId: claims.session_id };\n } catch (err) {\n const e = err as SessionTokenError;\n sendError(res, 401, e.code ?? 'auth.token_malformed', e.message ?? 'Invalid session token');\n return null;\n }\n}\n\n/**\n * Reads the inbound `Idempotency-Key` header, trims whitespace, and returns\n * `undefined` when absent or empty after trim. The SDK does not validate\n * length, character set, or UUID shape — upstream owns those constraints.\n */\nfunction inboundIdempotencyKey(req: HellobillRequest): string | undefined {\n const v = getHeader(req, 'idempotency-key');\n return v && v.trim().length > 0 ? v.trim() : undefined;\n}\n\nexport function createHellobillHandler(opts: CreateHellobillHandlerOptions): HellobillHandlerSet {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n const tokenStorage = opts.tokenStorage ?? new InMemoryTokenStorage();\n const idempotencyKeyFactory = opts.idempotencyKeyFactory ?? defaultIdempotencyKey;\n const logger = opts.logger;\n const upstreamTimeoutMs = opts.upstreamTimeoutMs ?? 30_000;\n\n const tokenManager = new TokenManager({\n clientId: opts.clientId,\n clientSecret: opts.clientSecret,\n apiBaseUrl: opts.apiBaseUrl,\n fetch: fetchImpl,\n storage: tokenStorage,\n logger,\n });\n\n /**\n * Wraps callUpstream with timeout + clean error envelope shaping. Returns null\n * if the caller's response has already been written (network/timeout/server\n * error path). Otherwise returns the upstream result.\n */\n async function safeCallUpstream(\n res: HellobillResponse,\n upstreamOpts: UpstreamCallOptions,\n ): Promise<UpstreamCallResult | null> {\n try {\n const result = await callUpstream(\n opts.apiBaseUrl,\n tokenManager,\n { ...upstreamOpts, timeoutMs: upstreamOpts.timeoutMs ?? upstreamTimeoutMs },\n fetchImpl,\n );\n // Upstream returned a 2xx with a non-JSON / unparseable body.\n if (\n result.status >= 200 &&\n result.status < 300 &&\n result.body !== null &&\n typeof result.body !== 'object'\n ) {\n logger?.warn('hellobill.upstream_invalid_response', {\n status: result.status,\n path: upstreamOpts.path,\n });\n sendError(\n res,\n 502,\n 'internal.error',\n 'Upstream returned a malformed response',\n result.requestId,\n );\n return null;\n }\n return result;\n } catch (err) {\n if (err instanceof NetworkError) {\n logger?.error('hellobill.upstream_unreachable', {\n kind: err.kind,\n path: upstreamOpts.path,\n });\n if (err.kind === 'timeout') {\n sendError(res, 504, 'internal.error', 'Upstream request timed out');\n } else {\n sendError(res, 502, 'internal.error', 'Upstream request failed');\n }\n return null;\n }\n throw err;\n }\n }\n\n const session = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n // Resolve the partner's authenticated server-side session when a resolver is configured.\n // A null result → 401 before any upstream call; a thrown error → 500 + warn log.\n let partnerSessionResult: unknown | null = null;\n if (opts.partnerSession) {\n try {\n partnerSessionResult = await opts.partnerSession(req);\n } catch (err) {\n logger?.warn('hellobill.partner_session_resolver_failed', {\n message: (err as Error).message,\n });\n sendError(res, 500, 'internal.error', 'Partner session resolver failed');\n return;\n }\n if (partnerSessionResult === null) {\n sendError(res, 401, 'auth.invalid_credentials', 'Partner session is not established');\n return;\n }\n }\n\n let payload: SessionPayload;\n try {\n payload = await opts.buildSessionPayload(req, partnerSessionResult);\n } catch (err) {\n logger?.warn('hellobill.build_session_payload_failed', {\n message: (err as Error).message,\n });\n sendError(res, 400, 'validation.invalid_field_value', (err as Error).message);\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'POST',\n path: '/partner/sessions',\n body: payload,\n idempotencyKey: inboundIdempotencyKey(req) ?? idempotencyKeyFactory(),\n logger,\n });\n if (!result) return;\n\n // Forward the full upstream JSON on every status (2xx and 4xx). The embed\n // needs embed_base_url, provided_fields, discovery_status, estimated_ready_seconds,\n // and expires_at — stripping them would break adaptive flow (spec §3.5:534-565).\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const loa = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const rawProductIds = req.query.product_ids;\n const productIds: string[] = Array.isArray(rawProductIds)\n ? rawProductIds\n : typeof rawProductIds === 'string'\n ? rawProductIds.split(',').map((s) => s.trim()).filter(Boolean)\n : [];\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/loa`,\n query: { product_ids: productIds.join(',') || undefined },\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const customers = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const result = await safeCallUpstream(res, {\n method: 'POST',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/customers`,\n body: req.body,\n idempotencyKey: inboundIdempotencyKey(req) ?? idempotencyKeyFactory(),\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const bankDetailsSubmit = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const result = await safeCallUpstream(res, {\n method: 'POST',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/bank-details`,\n body: req.body,\n idempotencyKey: inboundIdempotencyKey(req) ?? idempotencyKeyFactory(),\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const bankDetailsGet = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/bank-details`,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const status = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const rawCustomer = req.query.customer_id;\n const customerId = Array.isArray(rawCustomer) ? rawCustomer[0] : rawCustomer;\n if (!customerId) {\n sendError(res, 400, 'validation.missing_required_field', 'customer_id query parameter required');\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/customers/${encodeURIComponent(customerId)}/status`,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const products = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n // Pass through whitelisted query params verbatim. The Partner API treats\n // `cursor`, `limit`, `categories`, `include_previous` and `force_refresh`\n // as the canonical pagination/filter knobs; everything else is ignored.\n const query: Record<string, string | undefined> = {};\n const keys = ['cursor', 'limit', 'categories', 'include_previous', 'force_refresh'];\n for (const k of keys) {\n const v = req.query[k];\n if (Array.isArray(v)) query[k] = v[0];\n else if (typeof v === 'string') query[k] = v;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/products`,\n query,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const property = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n // Forward force_refresh verbatim; arrays coerce to first element.\n // Empty strings are dropped (treated as absent). Upstream owns validity.\n const query: Record<string, string | undefined> = {};\n const rawForce = req.query.force_refresh;\n if (typeof rawForce === 'string' && rawForce.length > 0) {\n query.force_refresh = rawForce;\n } else if (Array.isArray(rawForce) && typeof rawForce[0] === 'string' && rawForce[0].length > 0) {\n query.force_refresh = rawForce[0];\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/property`,\n query,\n logger,\n });\n if (!result) return;\n\n // 425 property.discovery_in_progress carries Retry-After; the allowlist forwards it.\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const sessionsList = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n // No ensureSessionContext — this endpoint is partner-credential-scoped (Bearer\n // access_token managed internally), not session_token-scoped. Upstream owns\n // the 401 boundary for the partner credential.\n //\n // POLICY: forwards every non-empty query param verbatim, including params\n // not enumerated in spec §3.6. Upstream owns 400-on-unknown-filter semantics;\n // this is intentionally wider than the spec param list so partners gain new\n // upstream filters without an SDK release.\n const query: Record<string, string | undefined> = {};\n for (const [k, v] of Object.entries(req.query)) {\n if (typeof v === 'string' && v.length > 0) {\n query[k] = v;\n } else if (Array.isArray(v) && typeof v[0] === 'string' && v[0].length > 0) {\n query[k] = v[0];\n }\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: '/partner/sessions',\n query,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const sessionsGet = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n // The session id arrives as a path param. Express populates req.params.id;\n // Next demo dispatcher synthesises params: { id } from the catch-all segment.\n const id = typeof req.params?.id === 'string' ? req.params.id : undefined;\n if (!id || id.length === 0) {\n sendError(res, 400, 'validation.missing_required_field', 'sessions/:id requires a non-empty id path param');\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(id)}`,\n logger,\n });\n if (!result) return;\n\n // 404 session.not_found forwarded verbatim — upstream owns the 404-vs-403\n // cross-partner-isolation choice per spec §2.0.1.\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const savingsSummary = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const rawCustomer = req.query.customer_id ?? req.params?.id;\n const customerId = Array.isArray(rawCustomer) ? rawCustomer[0] : rawCustomer;\n if (!customerId) {\n sendError(res, 400, 'validation.missing_required_field', 'customer_id query parameter required');\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/customers/${encodeURIComponent(customerId)}/savings-summary`,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const serviceSlots = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const rawSession = req.query.session_id ?? req.params?.id;\n const sessionId = Array.isArray(rawSession) ? rawSession[0] : rawSession;\n if (!sessionId) {\n sendError(res, 400, 'validation.missing_required_field', 'session_id query parameter required');\n return;\n }\n\n const rawService = req.query.service;\n const service = Array.isArray(rawService) ? rawService[0] : rawService;\n if (!service) {\n sendError(res, 400, 'validation.missing_required_field', 'service query parameter required');\n return;\n }\n\n const rawProvider = req.query.provider;\n const providerRaw = Array.isArray(rawProvider) ? rawProvider[0] : rawProvider;\n const provider = (typeof providerRaw === 'string' && providerRaw.trim().length > 0)\n ? providerRaw.trim()\n : 'any_van';\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(sessionId)}/service-slots?service=${encodeURIComponent(service)}&provider=${encodeURIComponent(provider)}`,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const loaDocument = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const rawLoa = req.query.loa_id ?? req.params?.id;\n const loaId = Array.isArray(rawLoa) ? rawLoa[0] : rawLoa;\n if (!loaId) {\n sendError(res, 400, 'validation.missing_required_field', 'loa_id query parameter required');\n return;\n }\n\n // The /document route is session-token-gated, not M2M-gated. The inbound bearer\n // (the embed session_token) must be present and is forwarded raw — the partner-api\n // route owns the authoritative verifySandboxJwt + loa:read scope check.\n const sessionBearer = extractBearer(req);\n if (!sessionBearer) {\n sendError(res, 401, 'auth.token_malformed', 'Missing Authorization bearer token');\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/loa/${encodeURIComponent(loaId)}/document`,\n sessionBearer,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const webhookHandler = opts.webhook\n ? createWebhookHandler({ ...opts.webhook, logger: opts.webhook.logger ?? opts.logger })\n : undefined;\n\n const result: HellobillHandlerSet = {\n session,\n loa,\n bankDetails: { submit: bankDetailsSubmit, get: bankDetailsGet },\n customers,\n status,\n products,\n property,\n sessions: { list: sessionsList, get: sessionsGet },\n savingsSummary,\n serviceSlots,\n loaDocument,\n };\n\n if (webhookHandler !== undefined) {\n result.webhook = webhookHandler;\n }\n\n return result;\n}\n","/**\n * Shared header normalisation for all framework adapters.\n *\n * Partners sometimes send `hellobill-signature` without the `x-` prefix.\n * The core webhook handler only reads `x-hellobill-signature`. This utility\n * copies all headers unchanged and, when a `hellobill-signature` key is\n * present, writes its value under the canonical `x-hellobill-signature` key —\n * unless `x-hellobill-signature` was already set (canonical form wins).\n *\n * The broad value type (`string | string[] | undefined`) covers both Express\n * IncomingHeaders and Koa's header map; adapters that use narrower types\n * (`Record<string, string>`) are assignment-compatible with this signature.\n */\nexport function normaliseHeaders(\n raw: Record<string, string | string[] | undefined>,\n): Record<string, string | string[] | undefined> {\n const out: Record<string, string | string[] | undefined> = { ...raw };\n\n for (const [k, v] of Object.entries(raw)) {\n if (k.toLowerCase() === 'hellobill-signature') {\n // Prefer the canonical `x-hellobill-signature` if both arrive together.\n if (out['x-hellobill-signature'] === undefined) {\n out['x-hellobill-signature'] = v;\n }\n }\n }\n\n return out;\n}\n","/**\n * Koa adapter for the framework-agnostic SDK core.\n *\n * Usage:\n * import Koa from 'koa';\n * import Router from '@koa/router';\n * import { createHellobillRouter } from '@hello-bill/node/koa';\n *\n * const app = new Koa();\n * const router = new Router();\n * router.use('/api/hellobill', createHellobillRouter({ ... }).routes());\n * app.use(router.routes());\n *\n * Requires a body parser (e.g. `@koa/bodyparser`) registered BEFORE the router.\n * For webhooks, the body parser must be configured with `rawBody: true`.\n *\n * Both `koa` and `@koa/router` are OPTIONAL peer dependencies — loaded lazily.\n */\n\nimport {\n createHellobillHandler,\n type CreateHellobillHandlerOptions,\n type HellobillRequest,\n type HellobillResponse,\n} from '../core/index.js';\nimport { createRequire } from 'node:module';\nimport { normaliseHeaders } from '../_internal/normalise-headers.js';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KoaContext = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KoaRouterInstance = any;\n\n/** Lazy-load @koa/router; throw a clear error if the peer dep isn't installed. */\nfunction loadKoaRouter(): { Router: new (...args: unknown[]) => KoaRouterInstance } {\n try {\n const req = createRequire(import.meta.url);\n const mod = req('@koa/router');\n // @koa/router via CJS createRequire returns the constructor as the module itself.\n // The ESM interop wrapper would put it under `.default` — handle both.\n const Router = typeof mod === 'function' ? mod : mod.default;\n if (typeof Router !== 'function') {\n throw new Error('@koa/router did not export a constructor.');\n }\n return { Router };\n } catch (err) {\n if (err instanceof Error && err.message.includes('did not export')) throw err;\n throw new Error(\n \"@hello-bill/node/koa requires the 'koa' and '@koa/router' peer dependencies. \" +\n \"Install them with `bun add koa @koa/router @koa/bodyparser` or the npm equivalent.\",\n );\n }\n}\n\n/** Bridge a Koa Context → HellobillRequest. */\nfunction toHellobillRequest(ctx: KoaContext): HellobillRequest {\n const rawHeaders = ctx.request.headers as Record<string, string | string[] | undefined>;\n const headers = normaliseHeaders(rawHeaders);\n\n return {\n method: ctx.method as string,\n url: ctx.url as string,\n query: ctx.query as Record<string, string | string[] | undefined>,\n body: (ctx.request as { body?: unknown }).body,\n headers,\n params: ctx.params as Record<string, string | undefined> | undefined,\n // Populated by @koa/bodyparser when rawBody: true is set.\n rawBody: (ctx.request as { rawBody?: Buffer }).rawBody,\n };\n}\n\n/** Bridge a Koa Context → HellobillResponse (mutations set on ctx). */\nfunction toHellobillResponse(ctx: KoaContext): HellobillResponse {\n const wrapped: HellobillResponse = {\n status(code: number) {\n ctx.status = code;\n return wrapped;\n },\n header(name: string, value: string) {\n ctx.set(name, value);\n return wrapped;\n },\n json(body: unknown) {\n ctx.body = body;\n ctx.type = 'application/json';\n },\n };\n return wrapped;\n}\n\n/** Wrap an async handler so uncaught errors produce a 500 envelope. */\nfunction asyncWrap(\n fn: (req: HellobillRequest, res: HellobillResponse) => Promise<void>,\n requiresBody = true,\n) {\n return async (ctx: KoaContext, next: () => Promise<void>) => {\n try {\n // Detect missing body parser: non-webhook routes require ctx.request.body to be\n // defined (not `undefined`). A missing parser leaves body as undefined.\n if (requiresBody && (ctx.request as { body?: unknown }).body === undefined) {\n ctx.status = 500;\n ctx.type = 'application/json';\n ctx.body = {\n error: {\n type: 'api_error',\n code: 'internal.error',\n message: 'Koa adapter requires a body parser — install @koa/bodyparser and register it before the router.',\n },\n };\n return;\n }\n\n await fn(toHellobillRequest(ctx), toHellobillResponse(ctx));\n } catch (err) {\n ctx.status = 500;\n ctx.type = 'application/json';\n ctx.body = {\n error: {\n type: 'api_error',\n code: 'internal.error',\n message: 'Internal server error',\n request_id: 'unknown',\n },\n };\n }\n await next();\n };\n}\n\n/**\n * Creates a @koa/router instance mounting the HelloBill endpoints:\n * POST /webhooks (optional — only when opts.webhook is provided)\n * POST /session\n * GET /loa\n * POST /customers\n * GET /status\n * GET /products\n * GET /property\n * POST /bank-details\n * GET /bank-details\n * GET /sessions\n * GET /sessions/:id\n *\n * Partners mount via `router.use('/api/hellobill', sdkRouter.routes())`.\n */\nexport function createHellobillRouter(opts: CreateHellobillHandlerOptions): KoaRouterInstance {\n const { Router } = loadKoaRouter();\n const handlers = createHellobillHandler(opts);\n\n const router = new Router();\n\n // Webhook route does not require body parser — uses rawBody populated by @koa/bodyparser.\n if (handlers.webhook) {\n router.post('/webhooks', asyncWrap(handlers.webhook, false));\n }\n\n // All other routes require ctx.request.body to be populated by a body parser.\n router.post('/session', asyncWrap(handlers.session));\n router.get('/loa', asyncWrap(handlers.loa, false));\n router.post('/bank-details', asyncWrap(handlers.bankDetails.submit));\n router.get('/bank-details', asyncWrap(handlers.bankDetails.get, false));\n router.post('/customers', asyncWrap(handlers.customers));\n router.get('/status', asyncWrap(handlers.status, false));\n router.get('/products', asyncWrap(handlers.products, false));\n router.get('/property', asyncWrap(handlers.property, false));\n // /sessions must be registered before /sessions/:id to avoid catch-all match.\n router.get('/sessions', asyncWrap(handlers.sessions.list, false));\n router.get('/sessions/:id', asyncWrap(handlers.sessions.get, false));\n router.get('/service-slots', asyncWrap(handlers.serviceSlots, false));\n router.get('/savings-summary', asyncWrap(handlers.savingsSummary, false));\n router.get('/loa-document', asyncWrap(handlers.loaDocument, false));\n\n return router;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/types/errors.ts","../../src/core/retry.ts","../../src/core/client.ts","../../src/core/idempotency.ts","../../src/core/errors.ts","../../src/core/token-manager.ts","../../src/webhook/verify.ts","../../src/webhook/dedupe-store.ts","../../src/webhook/handler.ts","../../src/core/handler.ts","../../src/_internal/normalise-headers.ts","../../src/koa/index.ts"],"names":["delay","randomUUID","createHmac","timingSafeEqual","result","createRequire"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Spec error envelope.\n * Returned with non-2xx responses from the Partner API and surfaced verbatim by the SDK.\n */\n\nimport type { RequestId } from './common.js';\n\n/**\n * Error codes follow `<category>.<specific>` naming and match the v15 §12 catalog.\n */\nexport type ErrorCode =\n // validation.*\n | 'validation.missing_required_field'\n | 'validation.invalid_field_value'\n | 'validation.invalid_enum_value'\n // consent.*\n | 'consent.required'\n // product.*\n | 'product.id_expired'\n // loa.*\n | 'loa.coverage_missing'\n | 'loa.coverage_excess'\n | 'loa.session_mismatch'\n | 'loa.template_changed'\n | 'loa.selection_changed'\n // signature_image.*\n | 'signature_image.invalid'\n | 'signature_image.too_large'\n | 'signature_image.dimensions_invalid'\n // address.*\n | 'address.line_too_long'\n | 'address.postcode_invalid'\n // move_out.*\n | 'move_out.requires_previous_property'\n | 'move_out.invalid_date'\n // final_read.*\n | 'final_read.implausible'\n // customer.*\n | 'customer.address_invalid'\n | 'customer.not_found'\n // bank_details.*\n | 'bank_details.invalid_sort_code'\n | 'bank_details.invalid_account_number'\n | 'bank_details.modulus_check_failed'\n | 'bank_details.session_locked'\n // auth.*\n | 'auth.invalid_credentials'\n | 'auth.token_expired'\n | 'auth.token_malformed'\n | 'auth.insufficient_scope'\n // session.*\n | 'session.not_found'\n // rate.*\n | 'rate.limited'\n // internal.*\n | 'internal.error'\n // discovery.*\n | 'discovery.unavailable';\n\nexport type ErrorType =\n | 'authentication_error'\n | 'validation_error'\n | 'rate_limit_error'\n | 'api_error';\n\nexport interface ErrorEnvelope {\n error: {\n type: ErrorType;\n code: ErrorCode;\n message: string;\n /** Path of the offending field (e.g. \"addresses.current.postcode\"). */\n param?: string;\n /** For validation.missing_required_field — dot-notation list of missing fields. */\n missing_fields?: string[];\n /** Echo of `X-HelloBill-Request-Id` for support correlation. */\n request_id: RequestId;\n };\n}\n\n/** HTTP statuses the SDK retries automatically with exponential backoff. */\nexport const RETRYABLE_HTTP_STATUSES = new Set<number>([429, 500, 502, 503, 504]);\n","import { RETRYABLE_HTTP_STATUSES } from '../types/index.js';\n\nexport interface RetryOptions {\n /** Max attempts including the first. Default 5. */\n maxAttempts?: number;\n /** Base delay in ms for exponential backoff. Default 1000. */\n baseDelayMs?: number;\n /** Cap on delay (ms). Default 16000. */\n maxDelayMs?: number;\n /** Override fetch (for tests). Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch;\n /** Override sleep (for tests). */\n sleep?: (ms: number) => Promise<void>;\n /** Random source for jitter (0..1). */\n random?: () => number;\n /**\n * Optional pre-attempt hook. Resolves with `true` to swap in a refreshed token.\n * Called once per attempt when the previous response indicated `auth.token_expired`.\n */\n onTokenExpired?: () => Promise<void>;\n /**\n * Per-attempt request timeout in ms. If the fetch does not resolve within this\n * window, it is aborted and treated as a retryable network error. Default 30_000.\n */\n timeoutMs?: number;\n}\n\n/** Sentinel error class for network/timeout failures the caller can recognise. */\nexport class NetworkError extends Error {\n constructor(\n message: string,\n public readonly cause?: unknown,\n public readonly kind: 'timeout' | 'network' = 'network',\n ) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\nconst defaultSleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;\n return null;\n}\n\nfunction backoff(attempt: number, base: number, cap: number, random: () => number): number {\n const exp = Math.min(cap, base * 2 ** attempt);\n // full jitter\n return Math.floor(random() * exp);\n}\n\nasync function readErrorCode(res: Response): Promise<string | undefined> {\n try {\n const clone = res.clone();\n const body = (await clone.json()) as { error?: { code?: string } };\n return body?.error?.code;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Fetch with retry. Retries on retryable HTTP statuses honoring `Retry-After`,\n * and invokes `onTokenExpired` once when the upstream returns `auth.token_expired`.\n */\nexport async function retryFetch(\n input: string | URL,\n init: RequestInit,\n opts: RetryOptions = {},\n): Promise<Response> {\n const {\n maxAttempts = 5,\n baseDelayMs = 1000,\n maxDelayMs = 16000,\n fetch: fetchImpl = globalThis.fetch,\n sleep = defaultSleep,\n random = Math.random,\n onTokenExpired,\n timeoutMs = 30_000,\n } = opts;\n\n let tokenRefreshed = false;\n let lastResponse: Response | undefined;\n let lastError: unknown;\n\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n let res: Response;\n try {\n res = await fetchWithTimeout(fetchImpl, input, init, timeoutMs);\n } catch (err) {\n lastError = err;\n // Treat any fetch rejection (network failure, abort/timeout) as retryable.\n if (attempt === maxAttempts - 1) {\n if (err instanceof NetworkError) throw err;\n throw new NetworkError(\n 'upstream request failed',\n err,\n isAbortLike(err) ? 'timeout' : 'network',\n );\n }\n const delay = backoff(attempt, baseDelayMs, maxDelayMs, random);\n await sleep(delay);\n continue;\n }\n lastResponse = res;\n\n if (res.ok) return res;\n\n // auth.token_expired triggers one-shot token refresh + immediate retry.\n if (!tokenRefreshed && onTokenExpired && res.status === 401) {\n const code = await readErrorCode(res);\n if (code === 'auth.token_expired') {\n tokenRefreshed = true;\n await onTokenExpired();\n continue;\n }\n }\n\n if (!RETRYABLE_HTTP_STATUSES.has(res.status)) return res;\n if (attempt === maxAttempts - 1) return res;\n\n const retryAfterMs = parseRetryAfter(res.headers.get('retry-after'));\n const delay = retryAfterMs ?? backoff(attempt, baseDelayMs, maxDelayMs, random);\n await sleep(delay);\n }\n\n // Should be unreachable; satisfies the type checker.\n if (lastResponse) return lastResponse;\n throw new NetworkError('upstream request failed', lastError);\n}\n\nfunction isAbortLike(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const name = (err as { name?: string }).name;\n return name === 'AbortError' || name === 'TimeoutError';\n}\n\nasync function fetchWithTimeout(\n fetchImpl: typeof globalThis.fetch,\n input: string | URL,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n return fetchImpl(input as RequestInfo, init);\n }\n // Compose the caller's signal (if any) with our timeout signal.\n const controller = new AbortController();\n const userSignal = init.signal;\n if (userSignal) {\n if (userSignal.aborted) controller.abort(userSignal.reason);\n else userSignal.addEventListener('abort', () => controller.abort(userSignal.reason), { once: true });\n }\n const timer = setTimeout(() => controller.abort(new Error('upstream timeout')), timeoutMs);\n try {\n return await fetchImpl(input as RequestInfo, { ...init, signal: controller.signal });\n } catch (err) {\n if (controller.signal.aborted && !userSignal?.aborted) {\n throw new NetworkError(`upstream request timed out after ${timeoutMs}ms`, err, 'timeout');\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n","import type { Logger } from './token-manager.js';\nimport type { TokenManager } from './token-manager.js';\nimport { retryFetch } from './retry.js';\n\nexport interface UpstreamCallOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n path: string;\n query?: Record<string, string | string[] | undefined>;\n body?: unknown;\n idempotencyKey?: string;\n logger?: Logger;\n /** Per-attempt request timeout in ms. */\n timeoutMs?: number;\n}\n\nexport interface UpstreamCallResult {\n status: number;\n headers: Headers;\n /** Parsed JSON body, or null when the body isn't JSON. */\n body: unknown;\n /** X-HelloBill-Request-Id, if present. */\n requestId: string | undefined;\n}\n\nfunction buildQueryString(query: Record<string, string | string[] | undefined> | undefined): string {\n if (!query) return '';\n const sp = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue;\n if (Array.isArray(v)) {\n for (const item of v) sp.append(k, item);\n } else {\n sp.append(k, v);\n }\n }\n const qs = sp.toString();\n return qs ? `?${qs}` : '';\n}\n\n/**\n * Internal helper: calls the Partner API with a Bearer token, optional\n * Idempotency-Key, retries, and parses the response (success or error envelope).\n */\nexport async function callUpstream(\n apiBaseUrl: string,\n tokenManager: TokenManager,\n opts: UpstreamCallOptions,\n fetchImpl: typeof globalThis.fetch,\n): Promise<UpstreamCallResult> {\n const base = apiBaseUrl.replace(/\\/$/, '');\n const url = `${base}${opts.path}${buildQueryString(opts.query)}`;\n\n const token = await tokenManager.getAccessToken();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n };\n if (opts.body !== undefined) headers['Content-Type'] = 'application/json';\n if (opts.idempotencyKey) headers['Idempotency-Key'] = opts.idempotencyKey;\n\n const init: RequestInit = {\n method: opts.method,\n headers,\n };\n if (opts.body !== undefined) init.body = JSON.stringify(opts.body);\n\n const res = await retryFetch(url, init, {\n fetch: fetchImpl,\n timeoutMs: opts.timeoutMs,\n onTokenExpired: async () => {\n await tokenManager.invalidate();\n const fresh = await tokenManager.getAccessToken();\n headers.Authorization = `Bearer ${fresh}`;\n },\n });\n\n let body: unknown = null;\n const text = await res.text();\n if (text.length > 0) {\n try {\n body = JSON.parse(text);\n } catch {\n body = text;\n }\n }\n\n return {\n status: res.status,\n headers: res.headers,\n body,\n requestId: res.headers.get('x-hellobill-request-id') ?? undefined,\n };\n}\n","import { randomUUID } from 'node:crypto';\n\n/** Default idempotency key generator — UUID v4 via Node crypto. */\nexport function defaultIdempotencyKey(): string {\n return randomUUID();\n}\n","import type { ErrorEnvelope, RequestId } from '../types/index.js';\n\n/**\n * Thrown when an upstream call returns a non-2xx response.\n * Handlers catch and pass through the envelope verbatim to the caller.\n *\n * `headers` carries the upstream response headers so callers can read\n * rate-limit budget (X-RateLimit-Remaining) and back-off hints (Retry-After)\n * after retries are exhausted.\n */\nexport class HellobillApiError extends Error {\n readonly status: number;\n readonly envelope: ErrorEnvelope | null;\n readonly requestId: RequestId | undefined;\n readonly headers: Headers | undefined;\n\n constructor(\n status: number,\n envelope: ErrorEnvelope | null,\n requestId?: RequestId,\n headers?: Headers,\n ) {\n super(envelope?.error.message ?? `HelloBill API error (${status})`);\n this.name = 'HellobillApiError';\n this.status = status;\n this.envelope = envelope;\n this.requestId = requestId;\n this.headers = headers;\n }\n\n get code(): string | undefined {\n return this.envelope?.error.code;\n }\n}\n","import { HellobillApiError } from './errors.js';\n\nexport interface TokenStorage {\n get(clientId: string): Promise<{ accessToken: string; expiresAt: number } | null>;\n set(clientId: string, value: { accessToken: string; expiresAt: number }): Promise<void>;\n}\n\nexport interface Logger {\n debug(msg: string, meta?: Record<string, unknown>): void;\n /**\n * Optional — added for unhandled-variant and dedup-hit logs in the webhook\n * receiver. Existing `Logger` literals (without `info`) continue to satisfy\n * the interface because `info` is optional.\n */\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\nexport interface TokenManagerOptions {\n clientId: string;\n clientSecret: string;\n apiBaseUrl: string;\n fetch?: typeof globalThis.fetch;\n storage?: TokenStorage;\n logger?: Logger;\n /** Renew tokens this many ms before expiry. Default 30_000. */\n refreshLeewayMs?: number;\n /** Override `Date.now()` for tests. */\n now?: () => number;\n}\n\ninterface TokenResponse {\n access_token: string;\n token_type: 'Bearer';\n expires_in: number;\n}\n\n/** Default in-memory single-instance storage. */\nexport class InMemoryTokenStorage implements TokenStorage {\n private readonly map = new Map<string, { accessToken: string; expiresAt: number }>();\n\n async get(clientId: string) {\n return this.map.get(clientId) ?? null;\n }\n\n async set(clientId: string, value: { accessToken: string; expiresAt: number }) {\n this.map.set(clientId, value);\n }\n}\n\n/**\n * Manages partner client_credentials tokens with caching, leeway-based renewal,\n * and single-flight refresh. Pluggable storage for multi-instance deployments.\n */\nexport class TokenManager {\n private readonly clientId: string;\n private readonly clientSecret: string;\n private readonly apiBaseUrl: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n private readonly storage: TokenStorage;\n private readonly logger: Logger | undefined;\n private readonly refreshLeewayMs: number;\n private readonly now: () => number;\n private inflight: Promise<string> | null = null;\n\n constructor(opts: TokenManagerOptions) {\n this.clientId = opts.clientId;\n this.clientSecret = opts.clientSecret;\n this.apiBaseUrl = opts.apiBaseUrl.replace(/\\/$/, '');\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n this.storage = opts.storage ?? new InMemoryTokenStorage();\n this.logger = opts.logger;\n this.refreshLeewayMs = opts.refreshLeewayMs ?? 30_000;\n this.now = opts.now ?? Date.now;\n }\n\n /** Returns a valid bearer token, refreshing if necessary. Single-flight on refresh. */\n async getAccessToken(): Promise<string> {\n if (this.inflight) return this.inflight;\n\n const cached = await this.storage.get(this.clientId);\n if (cached && cached.accessToken && cached.expiresAt - this.refreshLeewayMs > this.now()) {\n return cached.accessToken;\n }\n\n if (this.inflight) return this.inflight;\n\n this.inflight = this.fetchToken().finally(() => {\n this.inflight = null;\n });\n\n return this.inflight;\n }\n\n /** Forces invalidation of the cached token. Next `getAccessToken` will refresh. */\n async invalidate(): Promise<void> {\n await this.storage.set(this.clientId, { accessToken: '', expiresAt: 0 });\n }\n\n private async fetchToken(): Promise<string> {\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n });\n\n const res = await this.fetchImpl(`${this.apiBaseUrl}/auth/partner/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n },\n body: body.toString(),\n });\n\n if (!res.ok) {\n let envelope = null;\n try {\n envelope = await res.json();\n } catch {\n // ignore parse errors\n }\n const requestId = res.headers.get('x-hellobill-request-id') ?? undefined;\n this.logger?.error('hellobill.token_refresh_failed', { status: res.status });\n throw new HellobillApiError(res.status, envelope, requestId, res.headers);\n }\n\n const json = (await res.json()) as TokenResponse;\n const expiresAt = this.now() + json.expires_in * 1000;\n await this.storage.set(this.clientId, {\n accessToken: json.access_token,\n expiresAt,\n });\n this.logger?.debug('hellobill.token_refreshed', { expires_in: json.expires_in });\n return json.access_token;\n }\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { WebhookEvent } from '../types/index.js';\n\nexport interface VerifyWebhookOptions {\n /** Replay window in seconds. Default 300. */\n tolerance?: number;\n /** Signature versions to accept. Default ['v1']. Use ['v1', 'v2'] during key rotation. */\n supportedVersions?: ('v1' | 'v2')[];\n /** Override `Date.now()` for tests. */\n now?: () => number;\n}\n\nexport class WebhookVerificationError extends Error {\n constructor(\n message: string,\n public code: string,\n ) {\n super(message);\n this.name = 'WebhookVerificationError';\n }\n}\n\ninterface ParsedHeader {\n t: number;\n signatures: Record<string, string>;\n}\n\nfunction parseHeader(signatureHeader: string): ParsedHeader {\n if (!signatureHeader || typeof signatureHeader !== 'string') {\n throw new WebhookVerificationError(\n 'Missing signature header',\n 'signature.missing_header',\n );\n }\n\n const parts = signatureHeader.split(',').map((p) => p.trim()).filter(Boolean);\n let t: number | undefined;\n const signatures: Record<string, string> = {};\n\n for (const part of parts) {\n const eq = part.indexOf('=');\n if (eq === -1) {\n throw new WebhookVerificationError(\n 'Malformed signature header element',\n 'signature.malformed',\n );\n }\n const key = part.slice(0, eq);\n const value = part.slice(eq + 1);\n if (key === 't') {\n t = Number(value);\n if (!Number.isFinite(t)) {\n throw new WebhookVerificationError(\n 'Malformed timestamp in signature header',\n 'signature.malformed',\n );\n }\n } else if (key.length > 0 && value.length > 0) {\n signatures[key] = value;\n }\n }\n\n if (t === undefined) {\n throw new WebhookVerificationError(\n 'Missing timestamp in signature header',\n 'signature.malformed',\n );\n }\n if (Object.keys(signatures).length === 0) {\n throw new WebhookVerificationError(\n 'No signature values in header',\n 'signature.malformed',\n );\n }\n\n return { t, signatures };\n}\n\nfunction computeHmacHex(secret: string, payload: string): string {\n return createHmac('sha256', secret).update(payload).digest('hex');\n}\n\nfunction constantTimeHexEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n try {\n return timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'));\n } catch {\n return false;\n }\n}\n\n/**\n * Verifies a HelloBill webhook delivery.\n * Header format: `t=<unix>,v1=<hex>,v2=<hex>`.\n * HMAC computed over `${t}.${rawBody}` (raw bytes — do not re-serialise).\n */\nexport function verifyWebhook(\n rawBody: Buffer | string,\n signatureHeader: string,\n secret: string,\n opts: VerifyWebhookOptions = {},\n): WebhookEvent {\n const tolerance = opts.tolerance ?? 300;\n const supportedVersions = opts.supportedVersions ?? ['v1'];\n const now = opts.now ?? Date.now;\n\n const { t, signatures } = parseHeader(signatureHeader);\n\n const nowSeconds = Math.floor(now() / 1000);\n const skew = Math.abs(nowSeconds - t);\n if (skew > tolerance) {\n throw new WebhookVerificationError(\n `Signature timestamp outside tolerance (${skew}s > ${tolerance}s)`,\n 'signature.expired',\n );\n }\n\n const hasAnyKnownVersion = supportedVersions.some((v) => v in signatures);\n if (!hasAnyKnownVersion) {\n throw new WebhookVerificationError(\n 'No supported signature version present in header',\n 'signature.unsupported_version',\n );\n }\n\n const bodyStr =\n typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');\n const signingInput = `${t}.${bodyStr}`;\n\n for (const version of supportedVersions) {\n const provided = signatures[version];\n if (!provided) continue;\n const expected = computeHmacHex(secret, signingInput);\n if (constantTimeHexEqual(expected, provided)) {\n try {\n return JSON.parse(bodyStr) as WebhookEvent;\n } catch {\n throw new WebhookVerificationError(\n 'Verified payload is not valid JSON',\n 'signature.malformed',\n );\n }\n }\n }\n\n throw new WebhookVerificationError(\n 'Signature does not match expected value',\n 'signature.invalid',\n );\n}\n\n/**\n * Verifies a HelloBill webhook delivery without parsing the payload.\n * Returns `true` only if the timestamp is within tolerance AND at least one\n * supported scheme verifies. Returns `false` for every other case (missing/\n * malformed header, expired timestamp, no supported version, signature\n * mismatch). Never throws on verification failure — callers route on the\n * boolean. Re-throws non-verification errors (e.g. unexpected crypto failures).\n *\n * Unlike the throwing `verifyWebhook`, this function does NOT attempt to parse\n * the body as JSON — signature verification is purely over the raw bytes.\n * Callers that need the parsed event should call `verifyWebhook` directly or\n * parse separately after calling this function.\n *\n * @param rawBody Raw request body bytes (NOT re-serialised JSON).\n * @param signatureHeader Value of `X-HelloBill-Signature`.\n * @param secret Partner-issued webhook secret (env `HELLOBILL_WEBHOOK_SECRET`).\n * @param opts Optional `tolerance` (default 300s), `supportedVersions`\n * (default `['v1']`), `now` (test injection).\n */\nexport function verifyWebhookSignature(\n rawBody: Buffer | string,\n signatureHeader: string,\n secret: string,\n opts: VerifyWebhookOptions = {},\n): boolean {\n const tolerance = opts.tolerance ?? 300;\n const supportedVersions = opts.supportedVersions ?? ['v1'];\n const now = opts.now ?? Date.now;\n\n let parsed: ParsedHeader;\n try {\n parsed = parseHeader(signatureHeader);\n } catch (err) {\n if (err instanceof WebhookVerificationError) return false;\n throw err;\n }\n\n const { t, signatures } = parsed;\n\n const nowSeconds = Math.floor(now() / 1000);\n const skew = Math.abs(nowSeconds - t);\n if (skew > tolerance) return false;\n\n const hasAnyKnownVersion = supportedVersions.some((v) => v in signatures);\n if (!hasAnyKnownVersion) return false;\n\n const bodyStr =\n typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');\n const signingInput = `${t}.${bodyStr}`;\n\n for (const version of supportedVersions) {\n const provided = signatures[version];\n if (!provided) continue;\n const expected = computeHmacHex(secret, signingInput);\n if (constantTimeHexEqual(expected, provided)) {\n return true;\n }\n }\n\n return false;\n}\n","/**\n * Webhook idempotency dedupe store. Keyed on `WebhookEnvelope.id`.\n *\n * Default `InMemoryWebhookDedupeStore` is fine for single-process deployments\n * (and unit tests). Multi-replica deployments MUST inject a shared backend\n * (e.g. Redis SETEX, RDS `INSERT ... ON CONFLICT DO NOTHING`). Per spec §11.1\n * line 2270: \"Use the `id` field to deduplicate.\"\n */\nexport interface WebhookDedupeStore {\n /** Returns true if `id` has been recorded within its TTL. Never throws on miss. */\n has(id: string): Promise<boolean> | boolean;\n /** Records `id` with the given TTL in seconds. Idempotent on repeat calls. */\n record(id: string, ttlSeconds: number): Promise<void> | void;\n}\n\n/** In-memory implementation. NOT durable across process restarts. Tests + single-proc deploys. */\nexport class InMemoryWebhookDedupeStore implements WebhookDedupeStore {\n private readonly store = new Map<string, number>();\n\n // Optional test injection. Defaults to Date.now.\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n async has(id: string): Promise<boolean> {\n const expiresAt = this.store.get(id);\n if (expiresAt === undefined) return false;\n // Inclusive boundary: at exactly expiresAt, treat as expired. Tested via handler test 6.5.\n if (expiresAt <= this.now()) {\n this.store.delete(id);\n return false;\n }\n return true;\n }\n\n async record(id: string, ttlSeconds: number): Promise<void> {\n this.store.set(id, this.now() + ttlSeconds * 1000);\n }\n}\n","import type {\n BankDetailsCollected,\n MoveOutNotificationFailed,\n MoveOutNotificationSent,\n SessionAccountCreated,\n SessionAppDeferred,\n SessionAppInstalled,\n SessionDetailsConfirmed,\n SessionEmbedOpened,\n SessionLoaSigned,\n SessionProductsSelected,\n SetupStatusChanged,\n SubscriptionChanged,\n WebhookEvent,\n} from '../types/index.js';\nimport type { Logger } from '../core/token-manager.js';\nimport { verifyWebhookSignature, type VerifyWebhookOptions } from './verify.js';\nimport {\n InMemoryWebhookDedupeStore,\n type WebhookDedupeStore,\n} from './dedupe-store.js';\nimport type { HellobillRequest, HellobillResponse } from '../core/handler.js';\n\n/** Per-event runtime context passed to every typed handler. */\nexport interface WebhookHandlerContext {\n /**\n * Logger guaranteed to have `info` defined — sourced from `withInfoFallback`\n * at handler construction. Partner handlers may call `ctx.logger.info(...)`\n * without a null-check regardless of what Logger literal was supplied.\n */\n logger: Logger & { info: (msg: string, meta?: Record<string, unknown>) => void };\n headers: Record<string, string | string[] | undefined>;\n deprecation: string | null;\n}\n\n/**\n * Typed handlers — one per `WebhookEvent` variant. All optional; an\n * unprovided variant logs at info (`unhandled_variant`) and the dispatcher\n * still responds 2xx so HelloBill stops retrying.\n *\n * Handlers run async AFTER the route has already responded; throwing\n * from a handler does NOT cause HelloBill to retry (a 2xx was already\n * written). Errors are logged via `opts.logger?.error`.\n */\nexport interface WebhookHandlers {\n onSessionEmbedOpened?(event: SessionEmbedOpened, ctx: WebhookHandlerContext): Promise<void>;\n onSessionDetailsConfirmed?(event: SessionDetailsConfirmed, ctx: WebhookHandlerContext): Promise<void>;\n onSessionProductsSelected?(event: SessionProductsSelected, ctx: WebhookHandlerContext): Promise<void>;\n onSessionLoaSigned?(event: SessionLoaSigned, ctx: WebhookHandlerContext): Promise<void>;\n onSessionAccountCreated?(event: SessionAccountCreated, ctx: WebhookHandlerContext): Promise<void>;\n onSessionAppInstalled?(event: SessionAppInstalled, ctx: WebhookHandlerContext): Promise<void>;\n onSessionAppDeferred?(event: SessionAppDeferred, ctx: WebhookHandlerContext): Promise<void>;\n onSubscriptionChanged?(event: SubscriptionChanged, ctx: WebhookHandlerContext): Promise<void>;\n onSetupStatusChanged?(event: SetupStatusChanged, ctx: WebhookHandlerContext): Promise<void>;\n onMoveOutNotificationSent?(event: MoveOutNotificationSent, ctx: WebhookHandlerContext): Promise<void>;\n onMoveOutNotificationFailed?(event: MoveOutNotificationFailed, ctx: WebhookHandlerContext): Promise<void>;\n onBankDetailsCollected?(event: BankDetailsCollected, ctx: WebhookHandlerContext): Promise<void>;\n}\n\n/** Options for the webhook route. Lives alongside `CreateHellobillHandlerOptions`. */\nexport interface WebhookHandlerOptions {\n /**\n * Partner webhook secret. From `process.env.HELLOBILL_WEBHOOK_SECRET`.\n * MUST be a non-empty string at `createWebhookHandler` call time — the\n * factory throws synchronously on `null`/`undefined`/`''` so that an\n * unset env var is caught immediately rather than silently 401ing every\n * incoming webhook.\n */\n webhookSecret: string;\n /**\n * Optional dedupe-store. Default: in-memory (single-process). Multi-replica\n * deployments MUST supply a shared backend (Redis, RDS, etc.) — see README.\n */\n dedupeStore?: WebhookDedupeStore;\n /**\n * Idempotency window in seconds. Default 86_400 (24h). Per spec §11.1 line 2270.\n */\n dedupeTtlSeconds?: number;\n /** Signature versions accepted. Default `['v1']`. Set to `['v1','v2']` during rotation. */\n supportedVersions?: VerifyWebhookOptions['supportedVersions'];\n /** Timestamp tolerance in seconds. Default 300. */\n tolerance?: number;\n /** Optional logger. Defaults to no-op. */\n logger?: Logger;\n /** Typed handlers — provide as many as you care about; missing ones log \"unhandled\". */\n handlers?: WebhookHandlers;\n /**\n * Test injection for `Date.now()`. Applied to the signature timestamp AND,\n * only when using the default `InMemoryWebhookDedupeStore`, the dedupe TTL\n * clock. Partner-supplied `dedupeStore` instances MUST own their own clock\n * injection — `opts.now` is NOT forwarded to a partner-supplied store.\n */\n now?: () => number;\n}\n\n/**\n * Thrown by `assertNever` when an event arrives whose `type` is not in the\n * known union — typically a newer platform event that this SDK version does\n * not yet handle. The `code` discriminator lets partners identify this class\n * of error in log aggregation without an `instanceof` check.\n */\nexport class UnknownWebhookEventError extends Error {\n readonly code = 'webhook.unknown_event_type' as const;\n\n constructor(eventType: string) {\n super(`Unhandled webhook event type: ${eventType}`);\n this.name = 'UnknownWebhookEventError';\n // Restore correct prototype chain in environments where extending Error\n // loses the prototype (pre-ES2015 transpilation targets).\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Sentinel — placed on the receiver's `default:` branch to surface compile-time exhaustiveness. */\nfunction assertNever(x: never): never {\n throw new UnknownWebhookEventError((x as { type?: string }).type ?? JSON.stringify(x));\n}\n\nconst noopLogger: Logger & { info: () => void } = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Coalesces a partner-supplied Logger literal that may omit the optional `info`\n * method. Without this, `ctx.logger.info(...)` inside `logUnhandled` raises a\n * runtime TypeError when the partner's Logger only provides debug/warn/error.\n */\nfunction withInfoFallback(\n logger: Logger | undefined,\n): Logger & { info: (msg: string, meta?: Record<string, unknown>) => void } {\n return {\n debug: logger?.debug?.bind(logger) ?? (() => {}),\n info: logger?.info?.bind(logger) ?? (() => {}),\n warn: logger?.warn?.bind(logger) ?? (() => {}),\n error: logger?.error?.bind(logger) ?? (() => {}),\n };\n}\n\nfunction headerValue(\n headers: Record<string, string | string[] | undefined>,\n name: string,\n): string | undefined {\n const lower = name.toLowerCase();\n for (const [k, v] of Object.entries(headers)) {\n if (k.toLowerCase() === lower) return Array.isArray(v) ? v[0] : v;\n }\n return undefined;\n}\n\n/**\n * Dispatch a verified-and-deduped event to its typed handler. Exhaustive over\n * the union. Missing handler slots fall through to `logUnhandled` so a\n * partner-only-cares-about-bank-details deployment never 500s on an\n * unrecognised event type.\n */\nasync function dispatch(\n event: WebhookEvent,\n ctx: WebhookHandlerContext,\n handlers: WebhookHandlers,\n): Promise<void> {\n const logUnhandled = (name: string): void => {\n ctx.logger.info('hellobill.webhook.unhandled_variant', {\n event_id: event.id,\n event_type: event.type,\n handler: name,\n });\n };\n\n switch (event.type) {\n case 'session.embed_opened':\n if (handlers.onSessionEmbedOpened) await handlers.onSessionEmbedOpened(event, ctx);\n else logUnhandled('onSessionEmbedOpened');\n return;\n case 'session.details_confirmed':\n if (handlers.onSessionDetailsConfirmed) await handlers.onSessionDetailsConfirmed(event, ctx);\n else logUnhandled('onSessionDetailsConfirmed');\n return;\n case 'session.products_selected':\n if (handlers.onSessionProductsSelected) await handlers.onSessionProductsSelected(event, ctx);\n else logUnhandled('onSessionProductsSelected');\n return;\n case 'session.loa_signed':\n if (handlers.onSessionLoaSigned) await handlers.onSessionLoaSigned(event, ctx);\n else logUnhandled('onSessionLoaSigned');\n return;\n case 'session.account_created':\n if (handlers.onSessionAccountCreated) await handlers.onSessionAccountCreated(event, ctx);\n else logUnhandled('onSessionAccountCreated');\n return;\n case 'session.app_installed':\n if (handlers.onSessionAppInstalled) await handlers.onSessionAppInstalled(event, ctx);\n else logUnhandled('onSessionAppInstalled');\n return;\n case 'session.app_deferred':\n if (handlers.onSessionAppDeferred) await handlers.onSessionAppDeferred(event, ctx);\n else logUnhandled('onSessionAppDeferred');\n return;\n case 'subscription.changed':\n if (handlers.onSubscriptionChanged) await handlers.onSubscriptionChanged(event, ctx);\n else logUnhandled('onSubscriptionChanged');\n return;\n case 'setup_status.changed':\n if (handlers.onSetupStatusChanged) await handlers.onSetupStatusChanged(event, ctx);\n else logUnhandled('onSetupStatusChanged');\n return;\n case 'move_out_notification.sent':\n if (handlers.onMoveOutNotificationSent) await handlers.onMoveOutNotificationSent(event, ctx);\n else logUnhandled('onMoveOutNotificationSent');\n return;\n case 'move_out_notification.failed':\n if (handlers.onMoveOutNotificationFailed) await handlers.onMoveOutNotificationFailed(event, ctx);\n else logUnhandled('onMoveOutNotificationFailed');\n return;\n case 'bank_details.collected':\n if (handlers.onBankDetailsCollected) await handlers.onBankDetailsCollected(event, ctx);\n else logUnhandled('onBankDetailsCollected');\n return;\n default:\n // Exhaustiveness — if Ticket #1 adds a 13th variant, this fails to compile.\n return assertNever(event);\n }\n}\n\n/**\n * Creates the framework-agnostic webhook receive handler. Returns a function\n * matching the same `HellobillRequest`/`HellobillResponse` shape used by the\n * other routes on `HellobillHandlerSet` (see `core/handler.ts`).\n *\n * Throws synchronously if `opts.webhookSecret` is falsy — a missing or\n * empty secret is caught early so that every incoming webhook does not\n * silently 401 with no visible cause.\n *\n * The Express adapter wires this in alongside `session`/`loa`/etc.\n */\nexport function createWebhookHandler(\n opts: WebhookHandlerOptions,\n): (req: HellobillRequest, res: HellobillResponse) => Promise<void> {\n // Runtime guard: `process.env.HELLOBILL_WEBHOOK_SECRET!` bang-assertion satisfies\n // the TypeScript compiler but does not catch the case where the env var is unset at\n // runtime. Without this guard, every webhook would 401 silently because\n // verifyWebhookSignature would always return false for an undefined secret.\n if (opts.webhookSecret == null || opts.webhookSecret === '') {\n throw new Error(\n 'HELLOBILL_WEBHOOK_SECRET must be set; see spec §13.2 and ' +\n 'the @hello-bill/node README \"Webhook receiver\" section.',\n );\n }\n\n const now = opts.now ?? (() => Date.now());\n const dedupeStore = opts.dedupeStore ?? new InMemoryWebhookDedupeStore(now);\n const dedupeTtlSeconds = opts.dedupeTtlSeconds ?? 86_400;\n const tolerance = opts.tolerance ?? 300;\n const supportedVersions = opts.supportedVersions ?? ['v1'];\n const handlers = opts.handlers ?? {};\n // Coalesce partner-supplied Logger that may omit the optional `info` method.\n // Without this, `ctx.logger.info(...)` inside logUnhandled raises a runtime\n // TypeError when the partner Logger only provides debug/warn/error.\n const safeLogger = withInfoFallback(opts.logger ?? noopLogger);\n\n return async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const rawBody = req.rawBody;\n if (!rawBody) {\n safeLogger.error('hellobill.webhook.missing_raw_body', {});\n res.status(500).json({\n error: {\n type: 'api_error',\n code: 'server.internal',\n message: 'Webhook receiver requires raw body — adapter not wired correctly',\n },\n });\n return;\n }\n\n const sigHeader = headerValue(req.headers, 'x-hellobill-signature');\n if (!sigHeader) {\n res.status(401).json({ error: 'invalid_signature' });\n return;\n }\n\n const valid = verifyWebhookSignature(rawBody, sigHeader, opts.webhookSecret, {\n tolerance,\n supportedVersions,\n now,\n });\n if (!valid) {\n res.status(401).json({ error: 'invalid_signature' });\n return;\n }\n\n // Parse AFTER verification — verify operates on raw bytes by contract per spec §11.2.\n let event: WebhookEvent;\n try {\n event = JSON.parse(rawBody.toString('utf8')) as WebhookEvent;\n } catch {\n res.status(400).json({ error: 'invalid_payload' });\n return;\n }\n\n // Dedupe — record-before-dispatch so multi-replica double-delivery cannot\n // double-process. The crash-window caveat (rare loss) is documented and is\n // the at-most-once-after-2xx contract.\n const seen = await dedupeStore.has(event.id);\n if (seen) {\n safeLogger.info('hellobill.webhook.dedup_hit', { event_id: event.id, event_type: event.type });\n res.status(200).json({ received: true, deduped: true });\n return;\n }\n await dedupeStore.record(event.id, dedupeTtlSeconds);\n\n const deprecation = headerValue(req.headers, 'x-hellobill-deprecation') ?? null;\n if (deprecation) {\n safeLogger.warn('hellobill.webhook.deprecation_notice', {\n deprecation,\n event_id: event.id,\n event_type: event.type,\n });\n }\n\n // Respond 2xx FIRST. Per spec §11.1 line 2272: respond within 10s, process async.\n res.status(200).json({ received: true });\n\n const ctx: WebhookHandlerContext = {\n // safeLogger guarantees `info` is defined regardless of partner Logger shape.\n logger: safeLogger,\n headers: req.headers,\n deprecation,\n };\n\n // Fire-and-forget. Errors logged; HelloBill's retry layer is the durability mechanism.\n void dispatch(event, ctx, handlers).catch((err) => {\n safeLogger.error('hellobill.webhook.handler_error', {\n event_id: event.id,\n event_type: event.type,\n message: (err as Error).message,\n // Include the typed error code when present so partners can identify\n // known error classes (e.g. unknown event type) in log aggregation.\n ...(err != null && typeof err === 'object' && 'code' in err\n ? { code: (err as { code: unknown }).code }\n : {}),\n });\n });\n };\n}\n","import type { SessionPayload } from '../types/index.js';\nimport { callUpstream, type UpstreamCallOptions, type UpstreamCallResult } from './client.js';\nimport { defaultIdempotencyKey } from './idempotency.js';\nimport { NetworkError } from './retry.js';\nimport {\n InMemoryTokenStorage,\n TokenManager,\n type Logger,\n type TokenStorage,\n} from './token-manager.js';\nimport { createWebhookHandler, type WebhookHandlerOptions } from '../webhook/handler.js';\n\nexport interface HellobillRequest {\n method: string;\n url: string;\n query: Record<string, string | string[] | undefined>;\n body: unknown;\n headers: Record<string, string | string[] | undefined>;\n /**\n * Path parameters extracted by the adapter's router and populated on the\n * way in. Example: Express maps `:id` on `router.get('/sessions/:id', ...)`\n * into `req.params.id`; the SDK Express adapter forwards that verbatim via\n * `params: req.params`. Adapters that do not expose path parameters leave\n * this field `undefined` for the 8 contractual routes and synthesise\n * `params: { id }` only for the `/sessions/:id` branch.\n *\n * Optional. Backwards-compatible — adapters that pre-date this field\n * type-check unchanged.\n */\n params?: Record<string, string | undefined>;\n /** Optional raw request body for webhook verification — populated by adapter. */\n rawBody?: Buffer;\n}\n\nexport interface HellobillResponse {\n status(code: number): HellobillResponse;\n header(name: string, value: string): HellobillResponse;\n json(body: unknown): void;\n}\n\nexport interface CreateHellobillHandlerOptions {\n clientId: string;\n clientSecret: string;\n /** Partner API base URL, e.g. https://api.hellobill.app/api/v1 or http://localhost:4100. */\n apiBaseUrl: string;\n /**\n * Called on POST /session to assemble the canonical SessionPayload from whatever\n * the partner's host page sent. Receives the request unchanged and the resolved\n * partner session (if configured, or null if `partnerSession` is absent).\n *\n * When `partnerSession` is configured, this function MUST source\n * `customer.email` from the second argument — never from `req.body` —\n * to prevent the browser from spoofing the email address.\n */\n buildSessionPayload: (\n req: HellobillRequest,\n partnerSession: unknown | null,\n ) => Promise<SessionPayload> | SessionPayload;\n /**\n * Optional. Resolves the partner's authenticated server-side session from the\n * incoming request. Result (if non-null) is passed to `buildSessionPayload`\n * as the second argument.\n *\n * Returning `null` causes the middleware to return `401 auth.invalid_credentials`\n * before invoking `buildSessionPayload` or calling the upstream API.\n * Throwing causes a `500 internal.error` response and a warn-level log.\n * When absent, `buildSessionPayload` is called with `null` as the second arg;\n * the partner's `buildSessionPayload` is then solely responsible for any\n * CSRF / origin / session checks.\n */\n partnerSession?: (req: HellobillRequest) => Promise<unknown | null> | unknown | null;\n /** Optional override for idempotency key generation. Defaults to crypto.randomUUID(). */\n idempotencyKeyFactory?: () => string;\n /** Optional token storage adapter for multi-instance deployments. Default: in-memory. */\n tokenStorage?: TokenStorage;\n /** Custom fetch implementation (for tests). Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch;\n /** Optional logger. Defaults to no-op. Receives safe (no-secret) log lines. */\n logger?: Logger;\n /**\n * Per-attempt upstream request timeout in ms. Default 30_000 (30s). Each retry\n * starts a fresh timeout. Network failures and timeouts are surfaced as a\n * clean `internal.error` envelope.\n */\n upstreamTimeoutMs?: number;\n /**\n * Optional webhook receiver configuration. When provided, exposes a\n * `webhook` handler on `HellobillHandlerSet` that the Express adapter\n * mounts at POST /webhooks. Omit to skip the route entirely.\n */\n webhook?: import('../webhook/handler.js').WebhookHandlerOptions;\n}\n\nexport interface HellobillHandlerSet {\n session: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n loa: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n bankDetails: {\n submit: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n get: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n };\n customers: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n status: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n products: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n property: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n /**\n * Partner-side session recovery: list or fetch a single session by id.\n * Both proxy the bearer-gated upstream endpoints without requiring a\n * session_token — they are scoped to the partner's access_token.\n *\n * Query params on `list` pass through verbatim (including non-spec keys;\n * upstream owns 400-on-unknown-filter semantics). The `get` handler reads\n * the session id from `req.params.id`.\n *\n * Additive: not part of the v1.15 contract (spec §9.2.1). The reference SDK\n * always includes them; partners may opt out by substituting a handler that\n * returns 404.\n */\n sessions: {\n list: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n get: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n };\n /**\n * Proxies GET /partner/customers/:id/savings-summary.\n * Reads customer_id from req.query.customer_id or req.params.id.\n * Scoped to the partner's access_token — no session_token required.\n */\n savingsSummary: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n /**\n * Proxies GET /partner/sessions/:id/service-slots.\n * Reads session_id from req.query.session_id or req.params.id.\n * Reads service (required) and provider (optional, defaults to 'any_van') from req.query.\n * Returns 400 if service is missing.\n * Scoped to the partner's access_token — no session_token required.\n */\n serviceSlots: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n /**\n * Optional webhook receiver. Present only when `opts.webhook` is supplied to\n * `createHellobillHandler`. The Express adapter mounts POST /webhooks when\n * this slot is defined, and omits the route when it is `undefined`.\n */\n webhook?: (req: HellobillRequest, res: HellobillResponse) => Promise<void>;\n}\n\n// Re-export for the public barrel.\nexport type { TokenStorage, Logger } from './token-manager.js';\n\ninterface SessionTokenClaims {\n session_id: string;\n exp?: number;\n [key: string]: unknown;\n}\n\n/** Base64url decode a JWT segment without verification. Exported for tests. */\nexport function decodeSessionToken(token: string): SessionTokenClaims {\n const parts = token.split('.');\n if (parts.length < 2) {\n throw new SessionTokenError('auth.token_malformed', 'Session token is malformed');\n }\n let payload: SessionTokenClaims;\n try {\n const raw = Buffer.from(parts[1] as string, 'base64url').toString('utf8');\n payload = JSON.parse(raw) as SessionTokenClaims;\n } catch {\n throw new SessionTokenError('auth.token_malformed', 'Session token payload unparseable');\n }\n // Partner-api tokens carry the session identifier as `sub` (RFC 7519 standard claim).\n // Older internal tokens used `session_id`. Accept either; normalise to session_id.\n const sessionId =\n typeof payload.session_id === 'string' && payload.session_id.length > 0\n ? payload.session_id\n : typeof payload.sub === 'string' && (payload.sub as string).length > 0\n ? (payload.sub as string)\n : '';\n if (sessionId === '') {\n throw new SessionTokenError('auth.token_malformed', 'Session token missing session_id (or sub)');\n }\n payload.session_id = sessionId;\n return payload;\n}\n\nclass SessionTokenError extends Error {\n constructor(public code: string, message: string) {\n super(message);\n }\n}\n\nfunction getHeader(req: HellobillRequest, name: string): string | undefined {\n const lower = name.toLowerCase();\n for (const [k, v] of Object.entries(req.headers)) {\n if (k.toLowerCase() === lower) {\n return Array.isArray(v) ? v[0] : v;\n }\n }\n return undefined;\n}\n\nfunction extractBearer(req: HellobillRequest): string | undefined {\n const auth = getHeader(req, 'authorization');\n if (!auth) return undefined;\n const match = /^Bearer\\s+(.+)$/i.exec(auth);\n return match?.[1]?.trim();\n}\n\nfunction sendError(\n res: HellobillResponse,\n status: number,\n code: string,\n message: string,\n requestId?: string,\n): void {\n if (requestId) res.header('X-HelloBill-Request-Id', requestId);\n const type =\n status === 401 || status === 403\n ? 'authentication_error'\n : status === 429\n ? 'rate_limit_error'\n : status >= 500\n ? 'api_error'\n : 'validation_error';\n res.status(status).json({\n error: {\n type,\n code,\n message,\n request_id: requestId ?? '',\n },\n });\n}\n\n// Hard-coded allowlist: lowercase upstream header name → RFC-7230 title-case to emit.\n// No algorithmic casing — exactly these five entries cross the boundary.\n// Set-Cookie, Authorization, and WWW-Authenticate are deliberately excluded.\nconst PASSTHROUGH_HEADER_MAP: Record<string, string> = {\n 'retry-after': 'Retry-After',\n 'x-ratelimit-limit': 'X-RateLimit-Limit',\n 'x-ratelimit-remaining': 'X-RateLimit-Remaining',\n 'x-ratelimit-reset': 'X-RateLimit-Reset',\n};\n\nfunction passthroughUpstream(\n res: HellobillResponse,\n status: number,\n body: unknown,\n requestId: string | undefined,\n upstreamHeaders?: Headers,\n): void {\n if (requestId) res.header('X-HelloBill-Request-Id', requestId);\n if (upstreamHeaders) {\n for (const [lower, emit] of Object.entries(PASSTHROUGH_HEADER_MAP)) {\n const v = upstreamHeaders.get(lower);\n if (v !== null && v !== undefined && v !== '') {\n res.header(emit, v);\n }\n }\n }\n res.status(status).json(body);\n}\n\nfunction ensureSessionContext(\n req: HellobillRequest,\n res: HellobillResponse,\n): { sessionId: string } | null {\n const token = extractBearer(req);\n if (!token) {\n sendError(res, 401, 'auth.token_malformed', 'Missing Authorization bearer token');\n return null;\n }\n try {\n const claims = decodeSessionToken(token);\n return { sessionId: claims.session_id };\n } catch (err) {\n const e = err as SessionTokenError;\n sendError(res, 401, e.code ?? 'auth.token_malformed', e.message ?? 'Invalid session token');\n return null;\n }\n}\n\n/**\n * Reads the inbound `Idempotency-Key` header, trims whitespace, and returns\n * `undefined` when absent or empty after trim. The SDK does not validate\n * length, character set, or UUID shape — upstream owns those constraints.\n */\nfunction inboundIdempotencyKey(req: HellobillRequest): string | undefined {\n const v = getHeader(req, 'idempotency-key');\n return v && v.trim().length > 0 ? v.trim() : undefined;\n}\n\nexport function createHellobillHandler(opts: CreateHellobillHandlerOptions): HellobillHandlerSet {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n const tokenStorage = opts.tokenStorage ?? new InMemoryTokenStorage();\n const idempotencyKeyFactory = opts.idempotencyKeyFactory ?? defaultIdempotencyKey;\n const logger = opts.logger;\n const upstreamTimeoutMs = opts.upstreamTimeoutMs ?? 30_000;\n\n const tokenManager = new TokenManager({\n clientId: opts.clientId,\n clientSecret: opts.clientSecret,\n apiBaseUrl: opts.apiBaseUrl,\n fetch: fetchImpl,\n storage: tokenStorage,\n logger,\n });\n\n /**\n * Wraps callUpstream with timeout + clean error envelope shaping. Returns null\n * if the caller's response has already been written (network/timeout/server\n * error path). Otherwise returns the upstream result.\n */\n async function safeCallUpstream(\n res: HellobillResponse,\n upstreamOpts: UpstreamCallOptions,\n ): Promise<UpstreamCallResult | null> {\n try {\n const result = await callUpstream(\n opts.apiBaseUrl,\n tokenManager,\n { ...upstreamOpts, timeoutMs: upstreamOpts.timeoutMs ?? upstreamTimeoutMs },\n fetchImpl,\n );\n // Upstream returned a 2xx with a non-JSON / unparseable body.\n if (\n result.status >= 200 &&\n result.status < 300 &&\n result.body !== null &&\n typeof result.body !== 'object'\n ) {\n logger?.warn('hellobill.upstream_invalid_response', {\n status: result.status,\n path: upstreamOpts.path,\n });\n sendError(\n res,\n 502,\n 'internal.error',\n 'Upstream returned a malformed response',\n result.requestId,\n );\n return null;\n }\n return result;\n } catch (err) {\n if (err instanceof NetworkError) {\n logger?.error('hellobill.upstream_unreachable', {\n kind: err.kind,\n path: upstreamOpts.path,\n });\n if (err.kind === 'timeout') {\n sendError(res, 504, 'internal.error', 'Upstream request timed out');\n } else {\n sendError(res, 502, 'internal.error', 'Upstream request failed');\n }\n return null;\n }\n throw err;\n }\n }\n\n const session = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n // Resolve the partner's authenticated server-side session when a resolver is configured.\n // A null result → 401 before any upstream call; a thrown error → 500 + warn log.\n let partnerSessionResult: unknown | null = null;\n if (opts.partnerSession) {\n try {\n partnerSessionResult = await opts.partnerSession(req);\n } catch (err) {\n logger?.warn('hellobill.partner_session_resolver_failed', {\n message: (err as Error).message,\n });\n sendError(res, 500, 'internal.error', 'Partner session resolver failed');\n return;\n }\n if (partnerSessionResult === null) {\n sendError(res, 401, 'auth.invalid_credentials', 'Partner session is not established');\n return;\n }\n }\n\n let payload: SessionPayload;\n try {\n payload = await opts.buildSessionPayload(req, partnerSessionResult);\n } catch (err) {\n logger?.warn('hellobill.build_session_payload_failed', {\n message: (err as Error).message,\n });\n sendError(res, 400, 'validation.invalid_field_value', (err as Error).message);\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'POST',\n path: '/partner/sessions',\n body: payload,\n idempotencyKey: inboundIdempotencyKey(req) ?? idempotencyKeyFactory(),\n logger,\n });\n if (!result) return;\n\n // Forward the full upstream JSON on every status (2xx and 4xx). The embed\n // needs embed_base_url, provided_fields, discovery_status, estimated_ready_seconds,\n // and expires_at — stripping them would break adaptive flow (spec §3.5:534-565).\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const loa = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const rawProductIds = req.query.product_ids;\n const productIds: string[] = Array.isArray(rawProductIds)\n ? rawProductIds\n : typeof rawProductIds === 'string'\n ? rawProductIds.split(',').map((s) => s.trim()).filter(Boolean)\n : [];\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/loa`,\n query: { product_ids: productIds.join(',') || undefined },\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const customers = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const result = await safeCallUpstream(res, {\n method: 'POST',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/customers`,\n body: req.body,\n idempotencyKey: inboundIdempotencyKey(req) ?? idempotencyKeyFactory(),\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const bankDetailsSubmit = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const result = await safeCallUpstream(res, {\n method: 'POST',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/bank-details`,\n body: req.body,\n idempotencyKey: inboundIdempotencyKey(req) ?? idempotencyKeyFactory(),\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const bankDetailsGet = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/bank-details`,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const status = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n const rawCustomer = req.query.customer_id;\n const customerId = Array.isArray(rawCustomer) ? rawCustomer[0] : rawCustomer;\n if (!customerId) {\n sendError(res, 400, 'validation.missing_required_field', 'customer_id query parameter required');\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/customers/${encodeURIComponent(customerId)}/status`,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const products = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n // Pass through whitelisted query params verbatim. The Partner API treats\n // `cursor`, `limit`, `categories`, `include_previous` and `force_refresh`\n // as the canonical pagination/filter knobs; everything else is ignored.\n const query: Record<string, string | undefined> = {};\n const keys = ['cursor', 'limit', 'categories', 'include_previous', 'force_refresh'];\n for (const k of keys) {\n const v = req.query[k];\n if (Array.isArray(v)) query[k] = v[0];\n else if (typeof v === 'string') query[k] = v;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/products`,\n query,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const property = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const ctx = ensureSessionContext(req, res);\n if (!ctx) return;\n\n // Forward force_refresh verbatim; arrays coerce to first element.\n // Empty strings are dropped (treated as absent). Upstream owns validity.\n const query: Record<string, string | undefined> = {};\n const rawForce = req.query.force_refresh;\n if (typeof rawForce === 'string' && rawForce.length > 0) {\n query.force_refresh = rawForce;\n } else if (Array.isArray(rawForce) && typeof rawForce[0] === 'string' && rawForce[0].length > 0) {\n query.force_refresh = rawForce[0];\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(ctx.sessionId)}/property`,\n query,\n logger,\n });\n if (!result) return;\n\n // 425 property.discovery_in_progress carries Retry-After; the allowlist forwards it.\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const sessionsList = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n // No ensureSessionContext — this endpoint is partner-credential-scoped (Bearer\n // access_token managed internally), not session_token-scoped. Upstream owns\n // the 401 boundary for the partner credential.\n //\n // POLICY: forwards every non-empty query param verbatim, including params\n // not enumerated in spec §3.6. Upstream owns 400-on-unknown-filter semantics;\n // this is intentionally wider than the spec param list so partners gain new\n // upstream filters without an SDK release.\n const query: Record<string, string | undefined> = {};\n for (const [k, v] of Object.entries(req.query)) {\n if (typeof v === 'string' && v.length > 0) {\n query[k] = v;\n } else if (Array.isArray(v) && typeof v[0] === 'string' && v[0].length > 0) {\n query[k] = v[0];\n }\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: '/partner/sessions',\n query,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const sessionsGet = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n // The session id arrives as a path param. Express populates req.params.id;\n // Next demo dispatcher synthesises params: { id } from the catch-all segment.\n const id = typeof req.params?.id === 'string' ? req.params.id : undefined;\n if (!id || id.length === 0) {\n sendError(res, 400, 'validation.missing_required_field', 'sessions/:id requires a non-empty id path param');\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(id)}`,\n logger,\n });\n if (!result) return;\n\n // 404 session.not_found forwarded verbatim — upstream owns the 404-vs-403\n // cross-partner-isolation choice per spec §2.0.1.\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const savingsSummary = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const rawCustomer = req.query.customer_id ?? req.params?.id;\n const customerId = Array.isArray(rawCustomer) ? rawCustomer[0] : rawCustomer;\n if (!customerId) {\n sendError(res, 400, 'validation.missing_required_field', 'customer_id query parameter required');\n return;\n }\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/customers/${encodeURIComponent(customerId)}/savings-summary`,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const serviceSlots = async (req: HellobillRequest, res: HellobillResponse): Promise<void> => {\n const rawSession = req.query.session_id ?? req.params?.id;\n const sessionId = Array.isArray(rawSession) ? rawSession[0] : rawSession;\n if (!sessionId) {\n sendError(res, 400, 'validation.missing_required_field', 'session_id query parameter required');\n return;\n }\n\n const rawService = req.query.service;\n const service = Array.isArray(rawService) ? rawService[0] : rawService;\n if (!service) {\n sendError(res, 400, 'validation.missing_required_field', 'service query parameter required');\n return;\n }\n\n const rawProvider = req.query.provider;\n const providerRaw = Array.isArray(rawProvider) ? rawProvider[0] : rawProvider;\n const provider = (typeof providerRaw === 'string' && providerRaw.trim().length > 0)\n ? providerRaw.trim()\n : 'any_van';\n\n const result = await safeCallUpstream(res, {\n method: 'GET',\n path: `/partner/sessions/${encodeURIComponent(sessionId)}/service-slots?service=${encodeURIComponent(service)}&provider=${encodeURIComponent(provider)}`,\n logger,\n });\n if (!result) return;\n\n passthroughUpstream(res, result.status, result.body, result.requestId, result.headers);\n };\n\n const webhookHandler = opts.webhook\n ? createWebhookHandler({ ...opts.webhook, logger: opts.webhook.logger ?? opts.logger })\n : undefined;\n\n const result: HellobillHandlerSet = {\n session,\n loa,\n bankDetails: { submit: bankDetailsSubmit, get: bankDetailsGet },\n customers,\n status,\n products,\n property,\n sessions: { list: sessionsList, get: sessionsGet },\n savingsSummary,\n serviceSlots,\n };\n\n if (webhookHandler !== undefined) {\n result.webhook = webhookHandler;\n }\n\n return result;\n}\n","/**\n * Shared header normalisation for all framework adapters.\n *\n * Partners sometimes send `hellobill-signature` without the `x-` prefix.\n * The core webhook handler only reads `x-hellobill-signature`. This utility\n * copies all headers unchanged and, when a `hellobill-signature` key is\n * present, writes its value under the canonical `x-hellobill-signature` key —\n * unless `x-hellobill-signature` was already set (canonical form wins).\n *\n * The broad value type (`string | string[] | undefined`) covers both Express\n * IncomingHeaders and Koa's header map; adapters that use narrower types\n * (`Record<string, string>`) are assignment-compatible with this signature.\n */\nexport function normaliseHeaders(\n raw: Record<string, string | string[] | undefined>,\n): Record<string, string | string[] | undefined> {\n const out: Record<string, string | string[] | undefined> = { ...raw };\n\n for (const [k, v] of Object.entries(raw)) {\n if (k.toLowerCase() === 'hellobill-signature') {\n // Prefer the canonical `x-hellobill-signature` if both arrive together.\n if (out['x-hellobill-signature'] === undefined) {\n out['x-hellobill-signature'] = v;\n }\n }\n }\n\n return out;\n}\n","/**\n * Koa adapter for the framework-agnostic SDK core.\n *\n * Usage:\n * import Koa from 'koa';\n * import Router from '@koa/router';\n * import { createHellobillRouter } from '@hello-bill/node/koa';\n *\n * const app = new Koa();\n * const router = new Router();\n * router.use('/api/hellobill', createHellobillRouter({ ... }).routes());\n * app.use(router.routes());\n *\n * Requires a body parser (e.g. `@koa/bodyparser`) registered BEFORE the router.\n * For webhooks, the body parser must be configured with `rawBody: true`.\n *\n * Both `koa` and `@koa/router` are OPTIONAL peer dependencies — loaded lazily.\n */\n\nimport {\n createHellobillHandler,\n type CreateHellobillHandlerOptions,\n type HellobillRequest,\n type HellobillResponse,\n} from '../core/index.js';\nimport { createRequire } from 'node:module';\nimport { normaliseHeaders } from '../_internal/normalise-headers.js';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KoaContext = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KoaRouterInstance = any;\n\n/** Lazy-load @koa/router; throw a clear error if the peer dep isn't installed. */\nfunction loadKoaRouter(): { Router: new (...args: unknown[]) => KoaRouterInstance } {\n try {\n const req = createRequire(import.meta.url);\n const mod = req('@koa/router');\n // @koa/router via CJS createRequire returns the constructor as the module itself.\n // The ESM interop wrapper would put it under `.default` — handle both.\n const Router = typeof mod === 'function' ? mod : mod.default;\n if (typeof Router !== 'function') {\n throw new Error('@koa/router did not export a constructor.');\n }\n return { Router };\n } catch (err) {\n if (err instanceof Error && err.message.includes('did not export')) throw err;\n throw new Error(\n \"@hello-bill/node/koa requires the 'koa' and '@koa/router' peer dependencies. \" +\n \"Install them with `bun add koa @koa/router @koa/bodyparser` or the npm equivalent.\",\n );\n }\n}\n\n/** Bridge a Koa Context → HellobillRequest. */\nfunction toHellobillRequest(ctx: KoaContext): HellobillRequest {\n const rawHeaders = ctx.request.headers as Record<string, string | string[] | undefined>;\n const headers = normaliseHeaders(rawHeaders);\n\n return {\n method: ctx.method as string,\n url: ctx.url as string,\n query: ctx.query as Record<string, string | string[] | undefined>,\n body: (ctx.request as { body?: unknown }).body,\n headers,\n params: ctx.params as Record<string, string | undefined> | undefined,\n // Populated by @koa/bodyparser when rawBody: true is set.\n rawBody: (ctx.request as { rawBody?: Buffer }).rawBody,\n };\n}\n\n/** Bridge a Koa Context → HellobillResponse (mutations set on ctx). */\nfunction toHellobillResponse(ctx: KoaContext): HellobillResponse {\n const wrapped: HellobillResponse = {\n status(code: number) {\n ctx.status = code;\n return wrapped;\n },\n header(name: string, value: string) {\n ctx.set(name, value);\n return wrapped;\n },\n json(body: unknown) {\n ctx.body = body;\n ctx.type = 'application/json';\n },\n };\n return wrapped;\n}\n\n/** Wrap an async handler so uncaught errors produce a 500 envelope. */\nfunction asyncWrap(\n fn: (req: HellobillRequest, res: HellobillResponse) => Promise<void>,\n requiresBody = true,\n) {\n return async (ctx: KoaContext, next: () => Promise<void>) => {\n try {\n // Detect missing body parser: non-webhook routes require ctx.request.body to be\n // defined (not `undefined`). A missing parser leaves body as undefined.\n if (requiresBody && (ctx.request as { body?: unknown }).body === undefined) {\n ctx.status = 500;\n ctx.type = 'application/json';\n ctx.body = {\n error: {\n type: 'api_error',\n code: 'internal.error',\n message: 'Koa adapter requires a body parser — install @koa/bodyparser and register it before the router.',\n },\n };\n return;\n }\n\n await fn(toHellobillRequest(ctx), toHellobillResponse(ctx));\n } catch (err) {\n ctx.status = 500;\n ctx.type = 'application/json';\n ctx.body = {\n error: {\n type: 'api_error',\n code: 'internal.error',\n message: 'Internal server error',\n request_id: 'unknown',\n },\n };\n }\n await next();\n };\n}\n\n/**\n * Creates a @koa/router instance mounting the HelloBill endpoints:\n * POST /webhooks (optional — only when opts.webhook is provided)\n * POST /session\n * GET /loa\n * POST /customers\n * GET /status\n * GET /products\n * GET /property\n * POST /bank-details\n * GET /bank-details\n * GET /sessions\n * GET /sessions/:id\n *\n * Partners mount via `router.use('/api/hellobill', sdkRouter.routes())`.\n */\nexport function createHellobillRouter(opts: CreateHellobillHandlerOptions): KoaRouterInstance {\n const { Router } = loadKoaRouter();\n const handlers = createHellobillHandler(opts);\n\n const router = new Router();\n\n // Webhook route does not require body parser — uses rawBody populated by @koa/bodyparser.\n if (handlers.webhook) {\n router.post('/webhooks', asyncWrap(handlers.webhook, false));\n }\n\n // All other routes require ctx.request.body to be populated by a body parser.\n router.post('/session', asyncWrap(handlers.session));\n router.get('/loa', asyncWrap(handlers.loa, false));\n router.post('/bank-details', asyncWrap(handlers.bankDetails.submit));\n router.get('/bank-details', asyncWrap(handlers.bankDetails.get, false));\n router.post('/customers', asyncWrap(handlers.customers));\n router.get('/status', asyncWrap(handlers.status, false));\n router.get('/products', asyncWrap(handlers.products, false));\n router.get('/property', asyncWrap(handlers.property, false));\n // /sessions must be registered before /sessions/:id to avoid catch-all match.\n router.get('/sessions', asyncWrap(handlers.sessions.list, false));\n router.get('/sessions/:id', asyncWrap(handlers.sessions.get, false));\n\n return router;\n}\n"]}
|