@mesub/node 0.1.4 → 0.1.5

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cache/access-cache.ts","../src/version.ts","../src/options.ts","../src/cache/memory-store.ts","../src/customer.ts","../src/errors.ts","../src/validate.ts","../src/webhooks.ts","../src/transport.ts","../src/subscriptions.ts","../src/events.ts","../src/plans.ts","../src/client.ts"],"sourcesContent":["import type { CacheEntry, CacheStore } from './store.js';\n\n/** How long a stale answer is kept for the outage fallback, then dropped. */\nexport const DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1000;\n\n/** The one field the cache reads from an answer: seconds it stays true. */\nexport interface Revalidating {\n revalidate_after: number;\n}\n\nexport interface AccessCacheOptions {\n maxStaleMs?: number;\n now?: () => number;\n}\n\n/** What a read found: the answer, and whether it can be served without calling Mesub. */\nexport interface CachedAnswer<T> {\n value: T;\n fresh: boolean;\n}\n\n/**\n * The `/v1/access` answers, by customer and plan, over any store.\n *\n * A store that fails never breaks a request: a read that throws is a miss,\n * a write that throws is dropped. A merchant whose Redis is down still gets\n * answers, straight from Mesub. Nor does one that hangs hold a guard: given a\n * deadline, a read not answered by then is a miss too (#132).\n */\nexport class AccessCache<T extends Revalidating> {\n private readonly maxStaleMs: number;\n private readonly now: () => number;\n\n constructor(\n private readonly store: CacheStore<T>,\n options: AccessCacheOptions = {},\n ) {\n this.maxStaleMs = options.maxStaleMs ?? DEFAULT_MAX_STALE_MS;\n this.now = options.now ?? Date.now;\n }\n\n /**\n * `mesub:access:<scope>:<plan>:<who>`, prefixed so it can share a Redis\n * with anything. The scope (the client passes a hash of its API key) keeps\n * two projects sharing one store apart; without one, `mesub:access:<plan>:<who>`.\n *\n * `who` is whatever names the customer, the client's being\n * `<kind>:<wallet or hash>`. A null plan is the list of every plan they\n * have, under its own prefix, `mesub:access-list:`: no plan slug can reach it.\n */\n static key(who: string, plan: string | null, scope?: string): string {\n const prefix = plan === null ? 'mesub:access-list' : 'mesub:access';\n const path = plan === null ? who : `${plan}:${who}`;\n\n return scope === undefined ? `${prefix}:${path}` : `${prefix}:${scope}:${path}`;\n }\n\n /** With a `deadline` (epoch ms), a store that has not answered by then is a miss. */\n async read(\n who: string,\n plan: string | null,\n scope?: string,\n deadline?: number,\n ): Promise<CachedAnswer<T> | undefined> {\n let entry: CacheEntry<T> | undefined;\n\n try {\n const pending = this.store.get(AccessCache.key(who, plan, scope));\n\n entry = deadline === undefined ? await pending : await this.within(pending, deadline);\n } catch {\n return undefined;\n }\n\n const now = this.now();\n\n // Checked here too: a store like Redis may keep a key a little longer.\n if (!entry || now > entry.keepUntil) return undefined;\n\n return { value: entry.value, fresh: now < entry.freshUntil };\n }\n\n /** What `pending` gives by `deadline`, or undefined, as a miss, when it has not answered. */\n private async within<V>(pending: V | Promise<V>, deadline: number): Promise<V | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const late = new Promise<undefined>((resolve) => {\n timer = setTimeout(resolve, Math.max(0, deadline - this.now()), undefined);\n });\n\n try {\n // Promise.race handles a late rejection of `pending`: never unhandled.\n return await Promise.race([pending, late]);\n } finally {\n clearTimeout(timer);\n }\n }\n\n async write(who: string, plan: string | null, value: T, scope?: string): Promise<void> {\n // NaN or Infinity would make an entry that never goes stale, and a TTL\n // Redis refuses. The client checks every answer first; this is the floor.\n if (!Number.isFinite(value.revalidate_after)) return;\n\n const now = this.now();\n const freshUntil = now + Math.max(0, value.revalidate_after) * 1000;\n const keepUntil = freshUntil + this.maxStaleMs;\n\n try {\n await this.store.set(\n AccessCache.key(who, plan, scope),\n { value, freshUntil, keepUntil },\n keepUntil - now,\n );\n } catch {\n // A store that cannot write costs a cached answer, never the request.\n }\n }\n\n /**\n * Drops the cached answer when `outdated` says it no longer holds, so the\n * next call asks Mesub. A store without `delete` gets it back as stale\n * instead: never served as is again, only as the outage fallback.\n */\n async forget(\n who: string,\n plan: string | null,\n scope: string | undefined,\n outdated: (value: T) => boolean,\n ): Promise<void> {\n const key = AccessCache.key(who, plan, scope);\n\n try {\n const entry = await this.store.get(key);\n\n if (!entry || !outdated(entry.value)) return;\n if (this.store.delete) {\n await this.store.delete(key);\n return;\n }\n\n const now = this.now();\n\n if (now < entry.keepUntil) {\n await this.store.set(key, { ...entry, freshUntil: now }, entry.keepUntil - now);\n }\n } catch {\n // A store that fails costs a fresh answer, never the request.\n }\n }\n}\n","/** Kept in step with package.json by `test/version.spec.ts`. */\nexport const VERSION = '0.1.4';\n\n/**\n * The version of the Mesub API this release was written against, sent in\n * the `Mesub-Version` header of every call, so the API can change its\n * answers for newer releases without breaking this one. Moved only with a\n * release, never by a merchant.\n */\nexport const API_VERSION = '2026-10-02';\n\n/** The header `API_VERSION` is sent in. */\nexport const API_VERSION_HEADER = 'Mesub-Version';\n","import { API_VERSION_HEADER } from './version.js';\n\n/**\n * The client's options, checked once at `new Mesub()`: a wrong one throws a\n * TypeError there, never a 401 or a 1 ms timeout on every call later.\n */\n\n/** The longest delay `setTimeout` keeps: beyond it, it fires at once. */\nconst MAX_DELAY = 2_147_483_647;\n\n/** Hosts reached over plain http without sending the key across a network. */\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\n/**\n * The API key: the one given, else `MESUB_API_KEY`. Refuses none. A wrong one\n * is not guessed at here: Mesub answers it 401, thrown as `unauthorized`.\n */\nexport function apiKeyOf(given: unknown): string {\n if (given !== undefined && typeof given !== 'string') {\n throw new TypeError('apiKey must be a string.');\n }\n\n const apiKey = given || envKey();\n\n if (!apiKey) {\n throw new TypeError(\n typeof process === 'undefined'\n ? 'Missing Mesub API key: there is no process.env here (an edge runtime such as ' +\n 'Cloudflare Workers), so pass it: `new Mesub({ apiKey: env.MESUB_API_KEY })`.'\n : 'Missing Mesub API key: pass `new Mesub({ apiKey })` or set MESUB_API_KEY.',\n );\n }\n\n return apiKey;\n}\n\n/** `MESUB_API_KEY`, where there is a `process.env` to read it from. */\nfunction envKey(): string | undefined {\n return typeof process === 'undefined' ? undefined : process.env?.['MESUB_API_KEY'];\n}\n\n/**\n * The base URL, without its trailing slashes: the one given, else\n * `MESUB_BASE_URL`, else `fallback`. https, or http to this machine only,\n * since every call carries the API key.\n */\nexport function baseUrlOf(given: unknown, fallback: string): string {\n if (given !== undefined) return checkedBaseUrl('baseUrl', given);\n\n const fromEnv = envBaseUrl();\n\n return fromEnv ? checkedBaseUrl('MESUB_BASE_URL', fromEnv) : fallback;\n}\n\n/** `MESUB_BASE_URL`, where there is a `process.env` to read it from. */\nfunction envBaseUrl(): string | undefined {\n return typeof process === 'undefined' ? undefined : process.env?.['MESUB_BASE_URL'];\n}\n\n/** `given` checked as a base URL, the errors naming where it came from. */\nfunction checkedBaseUrl(name: string, given: unknown): string {\n if (typeof given !== 'string') throw new TypeError(`${name} must be a string.`);\n\n let url: URL;\n try {\n url = new URL(given);\n } catch {\n throw new TypeError(`${name} must be an absolute URL, not ${JSON.stringify(given)}.`);\n }\n\n // Every path is appended to it: a query or a fragment would swallow them.\n if (url.search !== '' || url.hash !== '' || given.includes('?') || given.includes('#')) {\n throw new TypeError(\n `${name} must have no query nor fragment, not ${JSON.stringify(given)}.`,\n );\n }\n\n const local = url.protocol === 'http:' && LOOPBACK.has(url.hostname);\n if (url.protocol !== 'https:' && !local) {\n throw new TypeError(\n `${name} must be https (http only to localhost or 127.0.0.1), not ${url.protocol}//${url.host}: ` +\n 'every call carries the API key.',\n );\n }\n\n return given.replace(/\\/+$/, '');\n}\n\n/**\n * A number of milliseconds or of retries, when given: finite, and positive,\n * or at least 0 when 0 means something (no retry, no fallback).\n */\nexport function numberOf(\n name: string,\n given: unknown,\n fallback: number,\n rule: { zero?: boolean; integer?: boolean; delay?: boolean } = {},\n): number {\n if (given === undefined) return fallback;\n\n const ok =\n typeof given === 'number' &&\n Number.isFinite(given) &&\n (rule.zero ? given >= 0 : given > 0) &&\n (!rule.integer || Number.isInteger(given)) &&\n (!rule.delay || given <= MAX_DELAY);\n\n if (!ok) {\n const what = rule.integer ? 'an integer' : 'a number of milliseconds';\n const range = rule.zero ? '0 or more' : 'above 0';\n const cap = rule.delay ? `, ${MAX_DELAY} at most` : '';\n const shown = typeof given === 'string' ? JSON.stringify(given) : String(given);\n throw new TypeError(`${name} must be ${what}, ${range}${cap}, not ${shown}.`);\n }\n\n return given;\n}\n\n/** Headers the SDK sets itself, and no extra header may replace. */\nconst OWN_HEADERS = new Set([\n 'authorization',\n 'user-agent',\n 'accept',\n 'content-type',\n API_VERSION_HEADER.toLowerCase(),\n]);\n\n/**\n * Extra headers for every call: for a proxy or an access gateway in front of\n * Mesub. Valid names and values only, and none of the\n * SDK's own, so the API key can never be swapped for another.\n */\nexport function headersOf(given: unknown): Record<string, string> {\n if (given === undefined) return {};\n if (typeof given !== 'object' || given === null || Array.isArray(given)) {\n throw new TypeError('headers must be an object of header names to string values.');\n }\n\n const headers: Record<string, string> = {};\n\n for (const [name, value] of Object.entries(given)) {\n if (typeof value !== 'string') {\n throw new TypeError(`headers.${name} must be a string.`);\n }\n if (OWN_HEADERS.has(name.toLowerCase())) {\n throw new TypeError(`headers cannot set ${name}: the SDK sets it itself.`);\n }\n try {\n new Headers([[name, value]]);\n } catch {\n throw new TypeError(`headers.${name} is not a valid header name and value.`);\n }\n headers[name] = value;\n }\n\n return headers;\n}\n","import { numberOf } from '../options.js';\nimport type { CacheEntry, CacheStore } from './store.js';\n\n/** How many answers the memory store keeps before dropping the least recently used. */\nexport const DEFAULT_MAX_ENTRIES = 10_000;\n\nexport interface MemoryStoreOptions {\n /** A whole number above 0: 10 000 by default. */\n maxEntries?: number;\n /** The clock, replaced in tests. */\n now?: () => number;\n}\n\n/**\n * The default store: an LRU in a `Map`, which keeps insertion order. Reading\n * an entry moves it to the end, so the first key is always the least recently\n * used one. It lives in this process only: a restart empties it.\n */\nexport class MemoryStore<T = unknown> implements CacheStore<T> {\n private readonly entries = new Map<string, CacheEntry<T>>();\n private readonly maxEntries: number;\n private readonly now: () => number;\n\n constructor(options: MemoryStoreOptions = {}) {\n // NaN would never drop an entry: the store would grow without bound.\n this.maxEntries = numberOf('maxEntries', options.maxEntries, DEFAULT_MAX_ENTRIES, {\n integer: true,\n });\n this.now = options.now ?? Date.now;\n }\n\n /** How many entries are held, expired ones included until they are read. */\n get size(): number {\n return this.entries.size;\n }\n\n get(key: string): CacheEntry<T> | undefined {\n const result = this.entries.get(key);\n if (!result) return undefined;\n if (result.keepUntil < this.now()) {\n this.entries.delete(key);\n return undefined;\n }\n // Deleted then set again: the Map moves it to the end, the most recently used place.\n this.entries.delete(key);\n this.entries.set(key, result);\n\n return result;\n }\n\n set(key: string, entry: CacheEntry<T>, _ttlMs: number): void {\n // `_ttlMs` stays unread: `keepUntil` in the entry already says it.\n this.entries.delete(key);\n this.entries.set(key, entry);\n\n // The first key of the Map is the least recently used one.\n while (this.entries.size > this.maxEntries) {\n const oldest = this.entries.keys().next().value;\n\n if (oldest === undefined) break;\n this.entries.delete(oldest);\n }\n }\n\n delete(key: string): void {\n this.entries.delete(key);\n }\n}\n","import type { Customer } from './answer.js';\n\n/** Who a call is about, normalised as Mesub reads it. */\nexport interface Asked {\n kind: 'wallet' | 'external_id' | 'email';\n value: string;\n}\n\nconst CUSTOMER_KINDS = ['wallet', 'external_id', 'email'] as const;\n\n/**\n * The one customer a call names, normalised the way Mesub normalises the\n * query (#157), so `Ada@Example.com ` and `ada@example.com` share an answer and\n * a cache key: an email trimmed and lowercased, an external id trimmed. A\n * wallet is left as given: Mesub refuses one that is not an address.\n *\n * None or two of them, or one that is not a string, throws a TypeError: a\n * broken integration, never a question to send. What the value holds (an\n * email that is not one, an external id too long) is Mesub's to refuse, with\n * a MesubError `invalid_request`.\n */\nexport function customerOf(customer: Customer | string): Asked {\n if (typeof customer === 'string') return { kind: 'wallet', value: customer };\n\n const named = CUSTOMER_KINDS.filter(\n (kind) => (customer as Record<string, unknown> | null)?.[kind] !== undefined,\n );\n const kind = named[0];\n const value: unknown = kind && (customer as Record<string, unknown>)[kind];\n\n if (named.length !== 1 || !kind || typeof value !== 'string') {\n throw new TypeError('Ask about exactly one of wallet, external_id or email, as a string.');\n }\n\n if (kind === 'email') return { kind, value: value.trim().toLowerCase() };\n if (kind === 'external_id') return { kind, value: value.trim() };\n\n return { kind, value };\n}\n","import type { ServerSubscription } from './subscriptions.js';\n\n/**\n * Stable codes to branch on, one per kind of failure; `apiCode` tells the\n * causes within one apart. `unexpected` covers any status without its own\n * code (408, 413, a 2xx whose body is not JSON or not of the shape the SDK\n * reads, a 404 whose body is not a Mesub error, so a wrong `baseUrl`, ...).\n * `invalid_webhook` is a webhook that fails verification: a header missing,\n * no signature matching the secret, a timestamp too far from now.\n */\nexport type MesubErrorCode =\n | 'invalid_request'\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'plan_not_found'\n | 'conflict'\n | 'rate_limited'\n | 'unavailable'\n | 'invalid_webhook'\n | 'unexpected';\n\nexport interface MesubErrorOptions {\n status: number | null;\n code: MesubErrorCode;\n /** The back's own code, from the error body. Defaults to null. */\n apiCode?: string | null;\n /** Defaults to true for `unavailable` and `rate_limited`, false otherwise. */\n retryable?: boolean;\n /** What Mesub answered, when it answered. */\n body?: unknown;\n /** Milliseconds, from the response's `Retry-After`. Defaults to null. */\n retryAfter?: number | null;\n /** The response's `Mesub-Request-Id`, or the body's `request_id`. Defaults to null. */\n requestId?: string | null;\n cause?: unknown;\n}\n\n/** Every failure of a call to the Mesub API, after retries. */\nexport class MesubError extends Error {\n override readonly name = 'MesubError';\n /**\n * The HTTP status, or `null` when no response came back (network error,\n * timeout). On the copy an Express middleware hands to `next(err)` for a\n * 4xx of Mesub's, 500: Mesub's own is on `cause`.\n */\n readonly status: number | null;\n readonly code: MesubErrorCode;\n /**\n * The code Mesub's error body names (`subscription_not_found`,\n * `pending_cap_reached`, ...), finer than `code` and as stable: never\n * renamed nor reused, though new ones get added. Null when no Mesub error\n * came back: a network error, a timeout, a body that is not Mesub's.\n */\n readonly apiCode: string | null;\n /**\n * Whether the same call, sent again unchanged, may succeed later without\n * anybody doing anything: Mesub's own `retryable` when it sent one. Not\n * \"at once\": the SDK has already retried what it retries.\n */\n readonly retryable: boolean;\n /**\n * The body Mesub answered, parsed when it is JSON, the text otherwise;\n * undefined when no response came back.\n */\n readonly body: unknown;\n /**\n * How long Mesub asked to wait before the same call is sent again, in\n * milliseconds, from the response's `Retry-After`, as sent. It comes on\n * a 429 (`rate_limited`, or `pending_cap_reached` on create: a quota,\n * not a rate, and the wait may be most of an hour), on every 503 of a\n * route that builds, sends or queues (`network_unavailable`, about 10 s;\n * `unavailable` on `submit`, about 2 s, and on `retry`, about 20 s),\n * and on two 409s:\n * `retry_too_soon` on `retry`, and `comeback_period_rolling` on\n * `create`, `transaction` and `submit`. Null when the response had none,\n * or none came back.\n */\n readonly retryAfter: number | null;\n /**\n * The id Mesub gave the request (`req_` and 24 letters or digits), from\n * the response's `Mesub-Request-Id` header, or the error body's\n * `request_id` when a proxy dropped the header (Mesub-io/backend#472).\n * Log it, and quote it to Mesub support: it finds the request in Mesub's\n * logs. Null when no response came back, or one that is not Mesub's.\n */\n readonly requestId: string | null;\n\n constructor(message: string, options: MesubErrorOptions) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.status = options.status;\n this.code = options.code;\n this.apiCode = options.apiCode ?? null;\n this.retryable =\n options.retryable ??\n (options.code === 'unavailable' || options.code === 'rate_limited');\n this.body = options.body;\n this.retryAfter = options.retryAfter ?? null;\n this.requestId = options.requestId ?? null;\n }\n}\n\n/** What a `MesubSubmitError` carries beyond a `MesubError`'s. */\nexport interface MesubSubmitErrorOptions extends MesubErrorOptions {\n subscription: ServerSubscription | null;\n sends: number;\n}\n\n/**\n * A submit whose outcome Mesub never told, thrown with the subscription read\n * back. The wallet may have paid: `subscription` is never `active` nor\n * `cancelled` (those are returned), and null when that read failed too. Read\n * it again with `retrieve` before creating anew; a `pending` one may still\n * land, and Mesub settles it on its own within the hour. Three cases:\n *\n * - no send got an answer that says (a timeout, a network error, a 5xx,\n * after the replays): `code` is `unavailable` and `retryable` true, since\n * the same submit, sent again with the same transaction and terms\n * signature, co-signs nothing more and is answered from the chain\n * (Mesub-io/backend#190). `status`, `apiCode`, `body`, `retryAfter` and `requestId` are\n * those of the last send that got a response (a 503, a 429), or all null\n * when none did (timeouts, network errors);\n * - a replay refused with `not_awaiting_signature` after a send that got no\n * answer: the row moved on (`failed`, `expired`), and the error is that\n * refusal's, `conflict`, not retryable;\n * - a 2xx this SDK cannot read: `unexpected`, not retryable, its fields.\n *\n * `cause` is always the last send's own error.\n */\nexport class MesubSubmitError extends MesubError {\n /** The subscription as read back after the last send, or null if that read failed. */\n readonly subscription: ServerSubscription | null;\n /** How many times the submit was sent, the same request each time. */\n readonly sends: number;\n\n constructor(message: string, options: MesubSubmitErrorOptions) {\n super(message, options);\n this.subscription = options.subscription;\n this.sends = options.sends;\n }\n}\n\n/**\n * The SDK's code for an error Mesub answered: its status, but for\n * `plan_not_found`, which keeps its own code from before the back had codes.\n * Every other 404 (`subscription_not_found`, a route Mesub does not have)\n * is `not_found`.\n */\nexport function codeFor(status: number, apiCode: string | null): MesubErrorCode {\n if (status === 404 && apiCode === 'plan_not_found') return 'plan_not_found';\n if (status === 400) return 'invalid_request';\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not_found';\n if (status === 409) return 'conflict';\n if (status === 429) return 'rate_limited';\n if (status >= 500) return 'unavailable';\n return 'unexpected';\n}\n","import type { AccessAnswer, AccessList, ServedAttempt } from './answer.js';\nimport type { EventList } from './events.js';\nimport { MesubError } from './errors.js';\nimport type { Plan, PlanToken } from './plans.js';\nimport type {\n ConfirmResult,\n ServerSubscription,\n ServerSubscriptionList,\n SubmitResult,\n SubscribeCosts,\n SubscribeTransaction,\n SubscriptionAttempt,\n SubscriptionAttemptList,\n WalletTransaction,\n} from './subscriptions.js';\nimport type {\n AccessChangedDetail,\n CreatedDetail,\n EndedDetail,\n MesubEvent,\n PaymentFailedDetail,\n RenewalUpcomingDetail,\n RenewedDetail,\n StoppedDetail,\n WebhookEvent,\n} from './webhooks.js';\n\n/** One field of an answer: how to tell it is right, and what it should have been. */\ninterface Field {\n check: (value: unknown) => boolean;\n expected: string;\n /**\n * A field newer than some backs still serving: absent, it is set to this\n * value rather than refused, so the answer still holds its type. A\n * function builds a fresh one for each answer.\n */\n absent?: null | false | (() => unknown);\n /** A field the back leaves out when it has nothing to say: absent is fine, as is. */\n optional?: true;\n}\n\nconst STRING: Field = { check: (value) => typeof value === 'string', expected: 'a string' };\nconst STRING_OR_NULL: Field = {\n check: (value) => value === null || typeof value === 'string',\n expected: 'a string or null',\n};\nconst OPTIONAL_STRING: Field = { ...STRING, optional: true };\nconst COUNT: Field = {\n check: (value) => typeof value === 'number' && Number.isInteger(value) && value >= 0,\n expected: 'a whole number',\n};\nconst BOOLEAN: Field = { check: (value) => typeof value === 'boolean', expected: 'a boolean' };\nconst OBJECT: Field = { check: isObject, expected: 'an object' };\nconst LIST_OF: Field = { check: Array.isArray, expected: 'a list' };\nconst DATE: Field = { check: isDate, expected: 'a date' };\nconst DATE_OR_NULL: Field = {\n check: (value) => value === null || isDate(value),\n expected: 'a date or null',\n};\n/** `retry_deadline`, served since Mesub-io/backend#191: null when a back predates it. */\nconst RETRY_DEADLINE: Field = { ...DATE_OR_NULL, absent: null };\n/** `paused` and `end_reason`, served since Mesub-io/backend#236: false and null before it. */\nconst PAUSED: Field = { ...BOOLEAN, absent: false };\nconst END_REASON: Field = { ...STRING_OR_NULL, absent: null };\n/** `blocked_since` and `blocked_reason`, served since Mesub-io/backend#398: null before it. */\nconst BLOCKED_SINCE: Field = { ...DATE_OR_NULL, absent: null };\nconst BLOCKED_REASON: Field = { ...STRING_OR_NULL, absent: null };\n/**\n * `subscription_id` (Mesub-io/backend#469), `stop_reason`, `closable` and\n * `closable_from` (Mesub-io/backend#402), and `transaction_expires_at`\n * (Mesub-io/backend#471): null, and false, before them.\n */\nconst SUBSCRIPTION_ID: Field = { ...STRING_OR_NULL, absent: null };\nconst STOP_REASON: Field = { ...STRING_OR_NULL, absent: null };\nconst CLOSABLE: Field = { ...BOOLEAN, absent: false };\nconst CLOSABLE_FROM: Field = { ...DATE_OR_NULL, absent: null };\nconst TRANSACTION_EXPIRES_AT: Field = { ...DATE_OR_NULL, absent: null };\n/**\n * A plan's `token` and `amount_usd`, and an attempt's `amount_usd`, served\n * since Mesub-io/backend#433: an unverified token with nothing known, and\n * null, before it.\n */\nconst AMOUNT_USD: Field = { ...STRING_OR_NULL, absent: null };\n/** An attempt's `amount_received`, served since Mesub-io/backend#396: null before it. */\nconst AMOUNT_RECEIVED: Field = { ...STRING_OR_NULL, absent: null };\nconst TOKEN: Field = {\n ...OBJECT,\n absent: (): PlanToken => ({ symbol: null, name: null, logo_url: null, verified: false }),\n};\n/**\n * `next_retry_number`, `retry_number` and `retries_allowed`, served since\n * Mesub-io/backend#289: null when a back predates them.\n */\nconst RETRY_COUNT: Field = {\n check: (value) => value === null || COUNT.check(value),\n expected: 'a whole number or null',\n absent: null,\n};\n/**\n * What the cache turns into its dates: NaN or Infinity would never go stale.\n * A negative one is read as 0 there, so it is let through.\n */\nconst SECONDS: Field = {\n check: (value) => typeof value === 'number' && Number.isFinite(value),\n expected: 'a finite number of seconds',\n};\n\n/**\n * Every field of an `AccessAnswer` but `attempts`, which is checked apart: a\n * field added to the type and not here fails to compile. A status, an end\n * or blocked reason or an outcome is only checked to be a string, never against the\n * list: one the back adds later must not turn every guard into an error.\n */\nconst ANSWER = {\n /** Null asked by external id or email, for a customer with nothing on that plan. */\n wallet: STRING_OR_NULL,\n plan: STRING,\n subscription_id: SUBSCRIPTION_ID,\n access: BOOLEAN,\n status: STRING,\n paused: PAUSED,\n end_reason: END_REASON,\n late_reason: STRING_OR_NULL,\n stop_reason: STOP_REASON,\n payment_status: STRING,\n blocked_since: BLOCKED_SINCE,\n blocked_reason: BLOCKED_REASON,\n subscribed_since: DATE_OR_NULL,\n first_subscribed_at: DATE_OR_NULL,\n current_period_end: DATE_OR_NULL,\n cancelled_at: DATE_OR_NULL,\n access_until: DATE_OR_NULL,\n next_charge_at: DATE_OR_NULL,\n next_retry_at: DATE_OR_NULL,\n retry_deadline: RETRY_DEADLINE,\n closable: CLOSABLE,\n closable_from: CLOSABLE_FROM,\n revalidate_after: SECONDS,\n} satisfies Record<Exclude<keyof AccessAnswer, 'attempts'>, Field>;\n\nconst ATTEMPT = {\n outcome: STRING,\n reason: STRING_OR_NULL,\n amount: STRING,\n attempted_at: DATE,\n signature: STRING_OR_NULL,\n} satisfies Record<keyof ServedAttempt, Field>;\n\n/**\n * What `/v1/access` answered for one plan, checked before anything reads or\n * caches it (#31). Without `revalidate_after` an entry's dates were NaN and it\n * never went stale, `access: true` served for good; a Redis store refused\n * `PX NaN`.\n *\n * Throws a MesubError `unexpected` on any answer of another shape: a wrong\n * `baseUrl` answering JSON, or a back the SDK no longer agrees with. Never\n * cached, so the next call asks again.\n */\nexport function accessAnswerFrom(body: unknown): AccessAnswer {\n const problem = answerProblem(body);\n\n if (problem !== null) throw unreadable(problem, body);\n\n return body as AccessAnswer;\n}\n\n/**\n * What `/v1/access` answered without a plan, for `accessList`: a list of\n * answers, each checked like `accessAnswerFrom`'s, and a finite\n * `revalidate_after` for the list itself. Throws and is never cached the same way.\n */\nexport function accessListFrom(body: unknown): AccessList {\n const problem = problemWith(body, LIST) ?? plansProblem(body);\n\n if (problem !== null) throw unreadable(problem, body);\n\n return body as AccessList;\n}\n\nconst LIST = {\n plans: LIST_OF,\n revalidate_after: SECONDS,\n} satisfies Record<keyof AccessList, Field>;\n\nfunction answerProblem(body: unknown, label?: string): string | null {\n return problemWith(body, ANSWER, label) ?? attemptsProblem(body, label);\n}\n\nfunction plansProblem(body: unknown): string | null {\n const { plans } = body as { plans: unknown[] };\n\n for (const [index, answer] of plans.entries()) {\n const problem = answerProblem(answer, `plans[${index}]`);\n\n if (problem !== null) return problem;\n }\n\n return null;\n}\n\n/**\n * Every field of a `ServerSubscription`, as `GET /v1/subscriptions/:id`\n * answers it and as `submit` and `list` embed it. Its status is only checked\n * to be a string, like an access answer's.\n */\nconst SUBSCRIPTION = {\n id: STRING,\n status: STRING,\n paused: PAUSED,\n end_reason: END_REASON,\n late_reason: STRING_OR_NULL,\n stop_reason: STOP_REASON,\n access: BOOLEAN,\n payment_status: STRING,\n blocked_since: BLOCKED_SINCE,\n blocked_reason: BLOCKED_REASON,\n plan: STRING_OR_NULL,\n wallet: STRING,\n email: STRING_OR_NULL,\n external_id: STRING_OR_NULL,\n current_period_start: DATE_OR_NULL,\n current_period_end: DATE_OR_NULL,\n next_charge_at: DATE_OR_NULL,\n next_retry_at: DATE_OR_NULL,\n retry_deadline: RETRY_DEADLINE,\n next_retry_number: RETRY_COUNT,\n retries_allowed: RETRY_COUNT,\n access_until: DATE_OR_NULL,\n closable: CLOSABLE,\n closable_from: CLOSABLE_FROM,\n created_at: DATE,\n confirmed_at: DATE_OR_NULL,\n} satisfies Record<keyof ServerSubscription, Field>;\n\nconst SUBSCRIPTION_LIST = {\n data: LIST_OF,\n has_more: BOOLEAN,\n} satisfies Record<keyof ServerSubscriptionList, Field>;\n\n/** An outcome is only checked to be a string: one the back adds later is handed on. */\nconst SUBSCRIPTION_ATTEMPT = {\n id: STRING,\n attempted_at: DATE,\n outcome: STRING,\n reason: STRING_OR_NULL,\n amount: STRING,\n amount_usd: AMOUNT_USD,\n amount_received: AMOUNT_RECEIVED,\n signature: STRING_OR_NULL,\n retry: BOOLEAN,\n retry_number: RETRY_COUNT,\n retries_allowed: RETRY_COUNT,\n period_start: DATE_OR_NULL,\n} satisfies Record<keyof SubscriptionAttempt, Field>;\n\nconst SUBSCRIPTION_ATTEMPT_LIST = {\n data: LIST_OF,\n has_more: BOOLEAN,\n paid: OBJECT,\n} satisfies Record<keyof SubscriptionAttemptList, Field>;\n\n/** A sum in base units: digits only, so it can be shown or added without a float. */\nconst BASE_UNITS: Field = {\n check: (value) => typeof value === 'string' && /^\\d+$/.test(value),\n expected: 'a whole number as a string',\n};\n\n/** What the front needs to have the wallet sign; the costs are only shown. */\nconst SUBSCRIBE_TRANSACTION = {\n subscription: OBJECT,\n transaction: STRING,\n last_valid_block_height: STRING,\n transaction_expires_at: TRANSACTION_EXPIRES_AT,\n costs: OBJECT,\n terms: OBJECT,\n} satisfies Record<keyof SubscribeTransaction, Field>;\n\n/** Lamports, as the back writes a u64. */\nconst COSTS = {\n rent: OBJECT,\n fee: OBJECT,\n total: BASE_UNITS,\n} satisfies Record<keyof SubscribeCosts, Field>;\n\nconst RENT = {\n subscription: BASE_UNITS,\n // Null when the authority exists already: no rent for it.\n authority: {\n check: (value) => value === null || BASE_UNITS.check(value),\n expected: 'a whole number as a string or null',\n },\n total: BASE_UNITS,\n} satisfies Record<keyof SubscribeCosts['rent'], Field>;\n\nconst FEE = {\n signatures: COUNT,\n per_signature: BASE_UNITS,\n priority: BASE_UNITS,\n total: BASE_UNITS,\n} satisfies Record<keyof SubscribeCosts['fee'], Field>;\n\nconst PLAN = {\n slug: STRING,\n name: STRING,\n description: STRING_OR_NULL,\n project_name: STRING,\n logo_url: STRING_OR_NULL,\n amount: STRING,\n amount_display: STRING,\n amount_usd: AMOUNT_USD,\n decimals: COUNT,\n symbol: STRING_OR_NULL,\n token: TOKEN,\n mint: STRING,\n period_hours: COUNT,\n network: STRING,\n // A string, not one of two: a status added later is handed back, not refused.\n status: STRING,\n available: BOOLEAN,\n ends_at: DATE_OR_NULL,\n} satisfies Record<keyof Plan, Field>;\n\n/** A served `token` is whole: one field missing or of another type is refused. */\nconst PLAN_TOKEN = {\n symbol: STRING_OR_NULL,\n name: STRING_OR_NULL,\n logo_url: STRING_OR_NULL,\n verified: BOOLEAN,\n} satisfies Record<keyof PlanToken, Field>;\n\n/** A plan's own fields, then its token's. */\nfunction planProblem(body: unknown, label?: string): string | null {\n return (\n problemWith(body, PLAN, label) ??\n problemWith(\n (body as Plan).token,\n PLAN_TOKEN,\n label === undefined ? 'token' : `${label}.token`,\n )\n );\n}\n\n/** One plan, from `plans.retrieve`. */\nexport function planFrom(body: unknown): Plan {\n const problem = planProblem(body);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/plans/:slug');\n\n return body as Plan;\n}\n\n/** `plans.list`: each plan checked like `retrieve`'s. */\nexport function planListFrom(body: unknown): Plan[] {\n const problem =\n problemWith(body, { plans: LIST_OF }) ??\n firstProblem((body as { plans: unknown[] }).plans, 'plans', planProblem);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/plans');\n\n return (body as { plans: Plan[] }).plans;\n}\n\n/** One subscription, from `retrieve`, `retry`, or read back after a submit. */\nexport function serverSubscriptionFrom(\n body: unknown,\n route = 'GET /v1/subscriptions/:id',\n status = 200,\n): ServerSubscription {\n const problem = problemWith(body, SUBSCRIPTION);\n\n if (problem !== null) throw unreadable(problem, body, route, status);\n\n return body as ServerSubscription;\n}\n\n/** One page of `list`: each subscription checked like `retrieve`'s. */\nexport function serverSubscriptionListFrom(body: unknown): ServerSubscriptionList {\n const problem =\n problemWith(body, SUBSCRIPTION_LIST) ??\n firstProblem((body as { data: unknown[] }).data, 'data', SUBSCRIPTION);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/subscriptions');\n\n return body as ServerSubscriptionList;\n}\n\n/** One page of `attempts`: each attempt checked, and the paid total. */\nexport function subscriptionAttemptListFrom(body: unknown): SubscriptionAttemptList {\n const problem =\n problemWith(body, SUBSCRIPTION_ATTEMPT_LIST) ??\n firstProblem((body as { data: unknown[] }).data, 'data', SUBSCRIPTION_ATTEMPT) ??\n problemWith(\n (body as SubscriptionAttemptList).paid,\n { count: COUNT, amount: BASE_UNITS },\n 'paid',\n );\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/subscriptions/:id/attempts');\n\n return body as SubscriptionAttemptList;\n}\n\n/** What `create` or `transaction` answered: what the wallet signs, and the subscription's id. */\nexport function subscribeTransactionFrom(\n body: unknown,\n route = 'POST /v1/subscriptions',\n): SubscribeTransaction {\n const problem =\n problemWith(body, SUBSCRIBE_TRANSACTION) ??\n problemWith(\n (body as SubscribeTransaction).subscription,\n { id: STRING, status: STRING },\n 'subscription',\n ) ??\n problemWith((body as SubscribeTransaction).costs, COSTS, 'costs') ??\n problemWith((body as SubscribeTransaction).costs.rent, RENT, 'costs.rent') ??\n problemWith((body as SubscribeTransaction).costs.fee, FEE, 'costs.fee') ??\n problemWith(\n (body as SubscribeTransaction).terms,\n { message: STRING, expires_at: DATE },\n 'terms',\n );\n\n if (problem !== null) throw unreadable(problem, body, route, 201);\n\n return body as SubscribeTransaction;\n}\n\n/** What `submit` answered: the subscription as it settled, and a reason when one came. */\nexport function submitResultFrom(body: unknown): SubmitResult {\n return confirmResultFrom(body, 'POST /v1/subscriptions/:id/submit');\n}\n\n/** What a confirm of `route` answered: the same shape as `submit`'s. */\nexport function confirmResultFrom(body: unknown, route: string): ConfirmResult {\n const problem =\n problemWith(body, { subscription: OBJECT }) ??\n problemWith((body as ConfirmResult).subscription, SUBSCRIPTION, 'subscription') ??\n reasonProblem(body as ConfirmResult);\n\n if (problem !== null) throw unreadable(problem, body, route, 201);\n\n return body as ConfirmResult;\n}\n\nconst WALLET_TRANSACTION = {\n transaction: STRING,\n last_valid_block_height: STRING,\n transaction_expires_at: TRANSACTION_EXPIRES_AT,\n} satisfies Record<keyof WalletTransaction, Field>;\n\n/** What `cancel`, `resume` or `close` answered: the transaction the wallet signs and sends. */\nexport function walletTransactionFrom(body: unknown, route: string): WalletTransaction {\n const problem = problemWith(body, WALLET_TRANSACTION);\n\n if (problem !== null) throw unreadable(problem, body, route, 201);\n\n return body as WalletTransaction;\n}\n\n/**\n * `reason` and `reason_code` (Mesub-io/backend#470), each a string when\n * present. One without the other is let through: a back that predates\n * `reason_code` sends `reason` alone.\n */\nfunction reasonProblem({ reason, reason_code }: ConfirmResult): string | null {\n if (reason !== undefined && typeof reason !== 'string') return 'reason is not a string';\n if (reason_code !== undefined && typeof reason_code !== 'string') {\n return 'reason_code is not a string';\n }\n return null;\n}\n\n/**\n * `sequence` (Mesub-io/backend#502): a whole number from 1, left out on a\n * body written before it and on an event Mesub could not number.\n */\nconst SEQUENCE: Field = {\n check: (value) => typeof value === 'number' && Number.isInteger(value) && value >= 1,\n expected: 'a whole number from 1',\n optional: true,\n};\n\n/**\n * An event (Mesub-io/backend#482), as `GET /v1/events` serves it and a\n * webhook carries it: its id, type and date, the network, the version it is\n * written in, the subscription as `retrieve` answers it, and the event's\n * detail, checked field by field for the types this release knows. A type,\n * a network or a version it does not know is only checked to be a string,\n * like a status: one the back adds later must not turn every delivery into\n * an error.\n */\nconst EVENT = {\n id: STRING,\n object: { check: (value) => value === 'event', expected: '\"event\"' },\n type: STRING,\n created_at: DATE,\n network: STRING,\n api_version: STRING,\n sequence: SEQUENCE,\n data: OBJECT,\n detail: OBJECT,\n} satisfies Record<keyof MesubEvent, Field>;\n\n/** Only on a body sent by hand from the dashboard, and then true. */\nconst TEST: Field = { check: (value) => value === true, expected: 'true', optional: true };\n\n/**\n * A webhook body written before the event shape (Mesub-io/backend#144), and\n * kept as it was sent: no id, and the detail inside `data`.\n */\nconst OLDER_WEBHOOK = { type: STRING, created_at: DATE, data: OBJECT, test: TEST };\n\n/**\n * `first_payment` on `subscription.created` (Mesub-io/backend#509): null, or\n * what the first payment paid; left out by an event written before it.\n */\nconst FIRST_PAYMENT: Field = {\n check: (value) =>\n value === null ||\n problemWith(value, { amount: STRING, mint: STRING_OR_NULL, signature: STRING_OR_NULL }) ===\n null,\n expected: 'null or a payment (amount, mint, signature)',\n optional: true,\n};\n\n/**\n * An access event's detail (Mesub-io/backend#507), and since #511 why and\n * until when: both left out by an event written before it.\n */\nconst ACCESS_CHANGED = {\n changed_at: DATE,\n blocked_reason: { ...STRING_OR_NULL, optional: true },\n access_until: { ...DATE_OR_NULL, optional: true },\n} satisfies Record<keyof AccessChangedDetail, Field>;\n\nconst DETAILS: Partial<Record<string, Record<string, Field>>> = {\n 'subscription.created': {\n previous_id: OPTIONAL_STRING,\n first_payment: FIRST_PAYMENT,\n } satisfies Record<keyof CreatedDetail, Field>,\n 'subscription.ended': {\n changed_at: { ...DATE, optional: true },\n // Only `cancelled` today: a newer word is let through, as a status is.\n end_reason: OPTIONAL_STRING,\n } satisfies Record<keyof EndedDetail, Field>,\n 'subscription.renewal_upcoming': {\n can_pay: BOOLEAN,\n renewal_issue: STRING_OR_NULL,\n amount: STRING,\n mint: STRING,\n due_at: DATE,\n } satisfies Record<keyof RenewalUpcomingDetail, Field>,\n 'subscription.renewed': {\n amount: STRING,\n mint: STRING,\n period_start: DATE,\n period_end: DATE,\n signature: STRING,\n } satisfies Record<keyof RenewedDetail, Field>,\n 'subscription.payment_failed': {\n reason: STRING,\n amount: STRING,\n mint: STRING,\n period_start: DATE_OR_NULL,\n period_end: DATE_OR_NULL,\n next_retry_at: DATE_OR_NULL,\n retry_deadline: DATE_OR_NULL,\n retries_left: COUNT,\n retry_mode: STRING_OR_NULL,\n } satisfies Record<keyof PaymentFailedDetail, Field>,\n 'subscription.stopped': {\n reason: STRING,\n } satisfies Record<keyof StoppedDetail, Field>,\n 'subscription.access_granted': ACCESS_CHANGED,\n 'subscription.access_revoked': ACCESS_CHANGED,\n};\n\n/** An event's own fields, its subscription's, then its detail's for a type this release knows. */\nfunction eventProblem(body: unknown, label?: string): string | null {\n const prefix = label === undefined ? '' : `${label}.`;\n const { type, data, detail } = body as { type: string; data: unknown; detail: unknown };\n const fields = DETAILS[type];\n\n return (\n problemWith(body, EVENT, label) ??\n problemWith(data, SUBSCRIPTION, `${prefix}data`) ??\n (fields ? problemWith(detail, fields, `${prefix}detail`) : null)\n );\n}\n\n/** One event, from `events.retrieve`. */\nexport function eventFrom(body: unknown): MesubEvent {\n const problem = eventProblem(body);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/events/:id');\n\n return body as MesubEvent;\n}\n\n/** One page of `events.list`: each event checked like `retrieve`'s, and `available_since`. */\nexport function eventListFrom(body: unknown): EventList {\n const problem =\n problemWith(body, EVENT_LIST) ??\n firstProblem((body as EventList).data, 'data', eventProblem);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/events');\n\n return body as EventList;\n}\n\nconst EVENT_LIST = {\n data: LIST_OF,\n has_more: BOOLEAN,\n available_since: DATE,\n} satisfies Record<keyof EventList, Field>;\n\n/**\n * A webhook whose signature verified, and `delivery_id`, its `webhook-id`\n * header. The event shape (Mesub-io/backend#482) is handed back as it came;\n * a body written before it (no `id`, no `object`, the detail inside `data`)\n * in the same shape, with its detail at the top and what it never carried\n * null. Throws a MesubError `unexpected` on a body of another shape: Mesub\n * signed it, so it is a back this release no longer agrees with.\n */\nexport function webhookEventFrom(body: unknown, deliveryId: string): WebhookEvent {\n const older = isObject(body) && !('id' in (body as object)) && !('object' in (body as object));\n const problem = older\n ? olderWebhookProblem(body)\n : (eventProblem(body) ?? problemWith(body, { test: TEST }));\n\n if (problem !== null) {\n throw new MesubError(`Mesub sent a webhook this SDK cannot read: ${problem}.`, {\n status: null,\n code: 'unexpected',\n body,\n });\n }\n\n if (!older) return { ...(body as MesubEvent), delivery_id: deliveryId } as WebhookEvent;\n\n const { type, created_at, data, test } = body as {\n type: string;\n created_at: string;\n data: ServerSubscription & { detail: Record<string, unknown> };\n test?: true;\n };\n const { detail, ...subscription } = data;\n\n return {\n id: null,\n delivery_id: deliveryId,\n object: 'event',\n type,\n created_at,\n network: null,\n api_version: null,\n data: subscription,\n detail,\n ...(test !== undefined && { test }),\n } as WebhookEvent;\n}\n\n/** A body of the shape webhooks had before the event's: the detail inside `data`. */\nfunction olderWebhookProblem(body: unknown): string | null {\n const { type, data } = body as { type: string; data: unknown };\n const fields = DETAILS[type];\n\n return (\n problemWith(body, OLDER_WEBHOOK) ??\n problemWith(data, { ...SUBSCRIPTION, detail: OBJECT }, 'data') ??\n (fields ? problemWith((data as { detail: unknown }).detail, fields, 'data.detail') : null)\n );\n}\n\n/** The first item of a list that is not of those fields, or null when all are. */\nfunction firstProblem(\n items: unknown[],\n label: string,\n fields: Record<string, Field> | ((item: unknown, label: string) => string | null),\n): string | null {\n for (const [index, item] of items.entries()) {\n const at = `${label}[${index}]`;\n const problem =\n typeof fields === 'function' ? fields(item, at) : problemWith(item, fields, at);\n\n if (problem !== null) return problem;\n }\n\n return null;\n}\n\n/**\n * The transport hands back 2xx bodies only: /v1/access and the reads answer\n * 200, the POSTs 201.\n */\nfunction unreadable(\n problem: string,\n body: unknown,\n route = '/v1/access',\n status = 200,\n): MesubError {\n return new MesubError(\n `Mesub answered ${route} with an answer this SDK cannot read: ${problem}.`,\n { status, code: 'unexpected', body },\n );\n}\n\n/**\n * The first field that is missing or of the wrong type, or null when all are\n * right. A missing field with an `absent` value is set to it on the body,\n * which is the answer just parsed, never one the caller handed in.\n */\nfunction problemWith(body: unknown, fields: Record<string, Field>, label?: string): string | null {\n if (!isObject(body)) {\n return `${label ?? 'the body'} is not an object`;\n }\n\n const prefix = label === undefined ? '' : `${label}.`;\n\n for (const [name, field] of Object.entries(fields)) {\n const value = (body as Record<string, unknown>)[name];\n\n if (value === undefined && field.optional) continue;\n if (value === undefined && 'absent' in field) {\n (body as Record<string, unknown>)[name] =\n typeof field.absent === 'function' ? field.absent() : field.absent;\n continue;\n }\n if (value === undefined) return `${prefix}${name} is missing`;\n if (!field.check(value)) return `${prefix}${name} is not ${field.expected}`;\n }\n\n return null;\n}\n\n/** `attempts`, only there when asked for: a list of attempts when it is. */\nfunction attemptsProblem(body: unknown, label?: string): string | null {\n const { attempts } = body as { attempts?: unknown };\n const prefix = label === undefined ? '' : `${label}.`;\n\n if (attempts === undefined) return null;\n if (!Array.isArray(attempts)) return `${prefix}attempts is not a list`;\n\n for (const [index, attempt] of attempts.entries()) {\n const problem = problemWith(attempt, ATTEMPT, `${prefix}attempts[${index}]`);\n\n if (problem !== null) return problem;\n }\n\n return null;\n}\n\nfunction isDate(value: unknown): boolean {\n return typeof value === 'string' && !Number.isNaN(Date.parse(value));\n}\n\nfunction isObject(value: unknown): boolean {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { BlockedReason } from './answer.js';\nimport { MesubError } from './errors.js';\nimport { numberOf } from './options.js';\nimport type { ServerSubscription } from './subscriptions.js';\nimport { webhookEventFrom } from './validate.js';\n\n/**\n * Webhooks, as Mesub-io/backend#144 sends them: Standard Webhooks\n * (https://www.standardwebhooks.com), a POST whose body is signed with the\n * endpoint's secret. Since Mesub-io/backend#482 the body is the event, as\n * `GET /v1/events` serves it. The types copy the back's `event-body.ts`,\n * `webhook-payload.ts`, `webhook-events.ts` and `pull-events.ts`, which stay\n * the source of truth. Snake case, as the API serves it.\n */\n\n/** A Fetch `Headers` (Next, route handlers) or Node's plain object (Express). */\nexport type HeaderSource = Headers | Record<string, string | string[] | undefined>;\n\n/** What every signing secret starts with, as the endpoint's dashboard page shows it. */\nconst SECRET_PREFIX = 'whsec_';\n\n/** Standard Webhooks' default: how far `webhook-timestamp` may be from now, either way. */\nconst DEFAULT_TOLERANCE = 5 * 60 * 1000;\n\n/** `webhook-timestamp`: Unix seconds, digits only. */\nconst TIMESTAMP = /^[0-9]{1,15}$/;\n\n/** The v1 events an endpoint subscribes to, and `test`, sent from the dashboard on request. */\nexport type WebhookEventType = WebhookEvent['type'];\n\n/** The v1 events `GET /v1/events` serves: every type but `test`. */\nexport type EventType = MesubEvent['type'];\n\n/** The Solana network a subscription is on, as `/v1` spells it. */\nexport type EventNetwork = 'devnet' | 'mainnet-beta';\n\n/**\n * One event, as `GET /v1/events` serves it and its webhooks carry it\n * (Mesub-io/backend#482): written once, the same bytes for every endpoint,\n * every retry and every read.\n */\ninterface EventOf<Type extends string, Detail> {\n /**\n * The event's own id, the same for every endpoint and every delivery:\n * what `events.retrieve` takes.\n */\n id: string;\n object: 'event';\n type: Type;\n /** When the event happened (ISO 8601), not when it was sent. */\n created_at: string;\n /** The Solana network the subscription is on. */\n network: EventNetwork;\n /** The `Mesub-Version` `data` and `detail` are written in, kept with the body. */\n api_version: string;\n /**\n * Its place among this subscription's events (Mesub-io/backend#502): 1,\n * 2, 3... in the order they happened, two events of one change included.\n * Deliveries arrive in any order: apply one subscription's events by\n * `sequence`, and skip one lower than the last you applied for it.\n * Absent on a body written before Mesub numbered events, and on one it\n * could not number: such an event cannot be placed, so read the\n * subscription now (`subscriptions.retrieve` or `access`) rather than\n * applying its `data`. Always 1 on a test delivery.\n */\n sequence?: number;\n /**\n * The subscription as it stood right after the change that raised the\n * event, as `GET /v1/subscriptions/:id` would have answered it then,\n * however late the delivery. Events raised by one change share it:\n * `detail` says what each is about.\n */\n data: ServerSubscription;\n /** What the event is about, beyond its type. */\n detail: Detail;\n}\n\n/**\n * One event as `verify` hands it back: the body Mesub signed, and\n * `delivery_id`, its `webhook-id` header.\n *\n * A body written before the event shape (before `available_since`, see\n * `events.list`) keeps the shape it was sent in, `{ type, created_at, data }`\n * with the detail inside `data`. It is handed back in this shape all the\n * same: `detail` moved to the top, and `id`, `network` and `api_version`\n * null, since it carried none. Its `data` is the subscription as it stood at\n * the delivery's first attempt.\n */\ninterface WebhookEventOf<Type extends string, Detail> extends Omit<\n EventOf<Type, Detail>,\n 'id' | 'network' | 'api_version'\n> {\n /** The event's id, as `EventOf`'s; null on a body written in the older shape. */\n id: string | null;\n /**\n * The delivery's id, from the `webhook-id` header: one per event and per\n * endpoint, the same on every retry and resend. Drop a delivery you\n * already handled by it; across endpoints, by `id`.\n */\n delivery_id: string;\n /** Null on a body written in the older shape. */\n network: EventNetwork | null;\n /** Null on a body written in the older shape. */\n api_version: string | null;\n /**\n * True on a body sent by hand from the dashboard: the `test` event, or\n * a sample of a real type on a made-up subscription (`sub_test`,\n * `user_123`), with the id `evt_test`. Absent on a real event. Change\n * nothing for it.\n */\n test?: true;\n}\n\n/** No detail: the subscription says it all. */\nexport type NoDetail = Record<string, never>;\n\n/** `subscription.created`: the first payment landed. */\nexport interface CreatedDetail {\n /** On a wallet coming back after a stop, the subscription it replaces. */\n previous_id?: string;\n /**\n * What the first payment paid (Mesub-io/backend#509). Null when Mesub\n * recorded none at the confirmation (a period paid only in part, which\n * the next charge collects); absent on an event written before it.\n */\n first_payment?: FirstPayment | null;\n}\n\n/** The first payment of a subscription: the transfer its own transaction carried. */\nexport interface FirstPayment {\n /** In the token's smallest unit, as a string. */\n amount: string;\n mint: string | null;\n /** The transaction that paid it. */\n signature: string | null;\n /**\n * What the receiver's account got of it: `amount` less the fee a\n * Token-2022 mint withholds. Null when not known, absent from a Mesub that\n * predates it.\n */\n amount_received?: string | null;\n}\n\n/**\n * `subscription.ended`: `changed_at` only on a cancellation that reached its\n * end (Mesub-io/backend#509), its expiry to the millisecond, the moment\n * `data` describes. Absent on any other end.\n */\nexport interface EndedDetail {\n changed_at?: string;\n /**\n * `cancelled` on a cancellation that reached its end, as `data.end_reason`\n * says it (Mesub-io/backend#511); absent otherwise, and on an older event.\n */\n end_reason?: 'cancelled';\n}\n\n/**\n * `subscription.renewed`: a period paid. Amounts in the mint's base units, as\n * strings. It can come late: a transfer whose send failed may land later, and\n * Mesub sends `renewed` then. By that time the subscription may no longer be\n * `active`. Ask `hasAccess` before granting access.\n */\nexport interface RenewedDetail {\n amount: string;\n mint: string;\n period_start: string;\n period_end: string;\n /** The transaction that paid it. */\n signature: string;\n}\n\n/** `subscription.payment_failed`: a pull missed, and what comes next. */\nexport interface PaymentFailedDetail {\n /** Why, e.g. `insufficient_balance`: an attempt's reason (`ServedAttempt`). */\n reason: string;\n amount: string;\n mint: string;\n /** The period it was for; null when unknown. */\n period_start: string | null;\n period_end: string | null;\n /**\n * When Mesub retries; null when it will not (the plan ends before the\n * retry, among others), or on Free, where retries are by hand.\n */\n next_retry_at: string | null;\n /** Free only: when hand retries close, the plan's end when that comes first. */\n retry_deadline: string | null;\n /**\n * Pulls still to come before the subscription stops, the next one\n * included; 0 with `next_retry_at` null when the plan ends before any.\n */\n retries_left: number;\n /** Who retries: Mesub on its schedule, or you by hand (Free). Null once none is left. */\n retry_mode: 'scheduled' | 'manual' | null;\n}\n\n/**\n * `subscription.stopped`: no more pulls. `data.stop_reason` says why it\n * stopped, the reading a program branches on.\n */\nexport interface StoppedDetail {\n /** Why, e.g. `insufficient_balance` or, on Free, `retry_deadline_passed`. */\n reason: string;\n}\n\n/**\n * Why the coming charge would not pay: the wallet holds too little or has no\n * account of the token (`balance`), or Mesub's approval can no longer move\n * the tokens (`authority`). One newer than this release is handed back as it is.\n */\nexport type RenewalIssue = 'balance' | 'authority';\n\n/**\n * `subscription.renewal_upcoming`: the charge due next, and whether the wallet\n * can pay it as it stood at the event's `created_at`. A reading, not a promise.\n */\nexport interface RenewalUpcomingDetail {\n can_pay: boolean;\n /** Why not; null when it can. */\n renewal_issue: RenewalIssue | null;\n amount: string;\n mint: string;\n /** When the charge is due. */\n due_at: string;\n}\n\n/**\n * `subscription.access_granted` and `subscription.access_revoked`: when\n * access actually changed. A date that passed with nothing else happening\n * (the end of a paid period, of a cancellation, of the plan) is that date to\n * the millisecond, even when Mesub wrote the event later; `created_at` is\n * when it was written.\n */\nexport interface AccessChangedDetail {\n changed_at: string;\n /**\n * Why the subscription's charges were held when access changed, in the\n * words of `data.blocked_reason`; null when they were not. Absent on an\n * event written before Mesub-io/backend#511. Keep a default branch: Mesub\n * may add a reason.\n */\n blocked_reason?: BlockedReason | null;\n /**\n * On `access_revoked`, the date access ended: the end of a paid period,\n * of a cancellation, of the plan, or of the access a held subscription\n * keeps; never after `changed_at`, and null when access ended with no\n * date of its own (a stop). On `access_granted`, on a held subscription,\n * the date access ends if nothing is collected by then; null otherwise.\n * Absent on an event written before Mesub-io/backend#511.\n */\n access_until?: string | null;\n}\n\n/** Each event type and its `detail`. */\ninterface EventDetails {\n 'subscription.created': CreatedDetail;\n 'subscription.renewal_upcoming': RenewalUpcomingDetail;\n 'subscription.renewed': RenewedDetail;\n 'subscription.payment_failed': PaymentFailedDetail;\n 'subscription.stopped': StoppedDetail;\n 'subscription.cancelled': NoDetail;\n 'subscription.resumed': NoDetail;\n 'subscription.ended': EndedDetail;\n 'subscription.expired': NoDetail;\n 'subscription.access_granted': AccessChangedDetail;\n 'subscription.access_revoked': AccessChangedDetail;\n 'subscription.paused': NoDetail;\n 'subscription.unpaused': NoDetail;\n}\n\n/**\n * Every event `GET /v1/events` serves. Narrow it with `switch (event.type)`,\n * and keep a default branch: a type newer than this release is handed back\n * too, its subscription checked.\n */\nexport type MesubEvent = {\n [Type in keyof EventDetails]: EventOf<Type, EventDetails[Type]>;\n}[keyof EventDetails];\n\nexport type SubscriptionCreatedEvent = WebhookEventOf<'subscription.created', CreatedDetail>;\n/** Once per period, in its last quarter and three days before the charge at most. */\nexport type SubscriptionRenewalUpcomingEvent = WebhookEventOf<\n 'subscription.renewal_upcoming',\n RenewalUpcomingDetail\n>;\nexport type SubscriptionRenewedEvent = WebhookEventOf<'subscription.renewed', RenewedDetail>;\nexport type SubscriptionPaymentFailedEvent = WebhookEventOf<\n 'subscription.payment_failed',\n PaymentFailedDetail\n>;\nexport type SubscriptionStoppedEvent = WebhookEventOf<'subscription.stopped', StoppedDetail>;\nexport type SubscriptionCancelledEvent = WebhookEventOf<'subscription.cancelled', NoDetail>;\nexport type SubscriptionResumedEvent = WebhookEventOf<'subscription.resumed', NoDetail>;\n/**\n * Sent when the plan ends, is removed or replaced, and when a subscription is\n * closed. Once Mesub turns the access events on, also when a cancellation\n * reaches its end (Mesub-io/backend#509): `end_reason` `cancelled`,\n * `detail.changed_at` its expiry, usually within half an hour of it, after\n * that expiry's `subscription.access_revoked`. Closing it later sends it\n * again, `end_reason` `closed`: one subscription can end twice.\n */\nexport type SubscriptionEndedEvent = WebhookEventOf<'subscription.ended', EndedDetail>;\n/** A checkout nobody signed in time. */\nexport type SubscriptionExpiredEvent = WebhookEventOf<'subscription.expired', NoDetail>;\n/**\n * Access started (Mesub-io/backend#507): the subscription grants access now\n * and did not just before. Its first access, a payment that gives access\n * back, a resume after a cancellation ended. A renewal paid on time, or a\n * resume while the period runs, sends none. `data.access` is true.\n *\n * With `subscription.access_revoked`, the one pair to drive access from:\n * sent exactly when access changes, and every change sends one. Apply them\n * by `sequence`; the last one applied says whether the customer has access.\n * The other events say why. Off until Mesub turns them on (`ACCESS_EVENTS`).\n */\nexport type SubscriptionAccessGrantedEvent = WebhookEventOf<\n 'subscription.access_granted',\n AccessChangedDetail\n>;\n/**\n * Access ended (Mesub-io/backend#507): the subscription grants no access now\n * and did just before. It stopped or ended, a cancellation or the plan\n * reached its end, or a blocked collection reached its `access_until`. For a\n * date that passed, sent a few seconds after it. `data.access` is false.\n * Off until Mesub turns it on.\n */\nexport type SubscriptionAccessRevokedEvent = WebhookEventOf<\n 'subscription.access_revoked',\n AccessChangedDetail\n>;\n/**\n * A seat parked (Mesub-io/backend#509): the project holds more subscribers\n * than its tier allows, and Mesub stopped charging the newest. Not a pause\n * the subscriber asked for; the status stays. `data.paused` is true,\n * `data.next_charge_at` null; access runs to the end of the paid period,\n * and its `subscription.access_revoked` comes then. Off until Mesub turns\n * the access events on.\n */\nexport type SubscriptionPausedEvent = WebhookEventOf<'subscription.paused', NoDetail>;\n/**\n * A parked seat back (Mesub-io/backend#509): the tier has room again, and\n * Mesub charges it again. `data.paused` is false; a\n * `subscription.access_granted` follows when its access had ended. Off until\n * Mesub turns the access events on.\n */\nexport type SubscriptionUnpausedEvent = WebhookEventOf<'subscription.unpaused', NoDetail>;\n/**\n * Sent from the dashboard on request, with a made-up subscription (`sub_test`).\n * The dashboard also sends samples of the other types, with `test: true`.\n */\nexport type TestEvent = WebhookEventOf<'test', NoDetail>;\n\n/**\n * Every event Mesub sends. Narrow it with `switch (event.type)`, and keep a\n * default branch: an event type newer than this release is handed back too,\n * its subscription checked, typed as one of these.\n */\nexport type WebhookEvent =\n | SubscriptionCreatedEvent\n | SubscriptionRenewalUpcomingEvent\n | SubscriptionRenewedEvent\n | SubscriptionPaymentFailedEvent\n | SubscriptionStoppedEvent\n | SubscriptionCancelledEvent\n | SubscriptionResumedEvent\n | SubscriptionEndedEvent\n | SubscriptionExpiredEvent\n | SubscriptionAccessGrantedEvent\n | SubscriptionAccessRevokedEvent\n | SubscriptionPausedEvent\n | SubscriptionUnpausedEvent\n | TestEvent;\n\n/** The body as received, never parsed: a string, a Buffer or any Uint8Array, an ArrayBuffer. */\nexport type WebhookBody = string | Uint8Array | ArrayBuffer;\n\nexport interface VerifyWebhookOptions {\n /**\n * The endpoint's signing secret, `whsec_...`. Defaults to the client's\n * `webhookSecret`, then to `process.env.MESUB_WEBHOOK_SECRET`.\n */\n secret?: string;\n /**\n * How far `webhook-timestamp` may be from now, either way, in\n * milliseconds, above 0: older is refused as a replay. Defaults to 5 minutes.\n */\n tolerance?: number;\n}\n\n/**\n * Checks a webhook Mesub sent and hands back its event: the signature over\n * the raw body, against the endpoint's secret, then the timestamp, then the\n * body's shape.\n *\n * Throws a MesubError `invalid_webhook` when it is not Mesub's, or not now:\n * a header missing, no signature matching, a timestamp outside `tolerance`.\n * Answer it with a 400.\n *\n * Throws `unexpected` for a body Mesub signed that this release cannot read.\n * Never answer it with a 400. Log it, update `@mesub/node`, and answer a\n * 5xx: Mesub retries the same body for 3 days, so the event gets through\n * once the update is out. Past 3 days of failures, Mesub disables the\n * endpoint.\n *\n * A TypeError is an integration error: a body already parsed, no secret,\n * one that is not `whsec_...`.\n */\nexport async function verifyWebhook(\n body: WebhookBody,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<WebhookEvent> {\n return verify(body, headers, options, undefined);\n}\n\n/** `mesub.webhooks`: `verifyWebhook` with the client's own secret. */\nexport class Webhooks {\n readonly #secret: string | undefined;\n readonly #received: (event: WebhookEvent) => Promise<void>;\n\n /** @internal */\n constructor(secret: string | undefined, received: (event: WebhookEvent) => Promise<void>) {\n this.#secret = secret;\n this.#received = received;\n }\n\n /**\n * `verifyWebhook`, with `webhookSecret` unless `options.secret` says\n * otherwise. A verified event also drops every access answer cached for\n * its customer, the yes and the no, so the next check asks Mesub.\n * Three drop nothing: `subscription.renewal_upcoming`, which moves no\n * access, the `test` event, and any body with `test: true`, whose\n * subscription is made up.\n */\n async verify(\n body: WebhookBody,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n ): Promise<WebhookEvent> {\n const event = await verify(body, headers, options, this.#secret);\n\n // A charge to come changes no answer: dropping it would cost the outage fallback.\n // A sample from the dashboard names a made-up customer: it moves nobody's access.\n const moves =\n event.test !== true &&\n event.type !== 'test' &&\n event.type !== 'subscription.renewal_upcoming';\n\n if (moves) await this.#received(event);\n\n return event;\n }\n}\n\nasync function verify(\n body: WebhookBody,\n headers: HeaderSource,\n options: VerifyWebhookOptions,\n fallback: string | undefined,\n): Promise<WebhookEvent> {\n const key =\n options.secret !== undefined || fallback !== undefined\n ? keyOf(options.secret ?? fallback, 'secret')\n : keyOf(envSecret(), 'MESUB_WEBHOOK_SECRET');\n const tolerance = numberOf('tolerance', options.tolerance, DEFAULT_TOLERANCE);\n const bytes = bytesOf(body);\n\n if (typeof headers !== 'object' || headers === null) {\n throw new TypeError('headers must be the request headers: a Headers or a plain object.');\n }\n\n const id = headerIn(headers, 'webhook-id');\n const timestamp = headerIn(headers, 'webhook-timestamp');\n const signatures = headerIn(headers, 'webhook-signature');\n\n if (!id || !timestamp || !signatures) {\n const missing = [\n !id && 'webhook-id',\n !timestamp && 'webhook-timestamp',\n !signatures && 'webhook-signature',\n ].filter(Boolean);\n\n throw invalid(`The webhook has no ${missing.join(', ')} header: it is not one of Mesub's.`);\n }\n if (!TIMESTAMP.test(timestamp)) {\n throw invalid('The webhook-timestamp header is not a number of seconds.');\n }\n\n const expected = await signatureOf(key, id, timestamp, bytes);\n // Every candidate is compared, whichever matches: the time taken says nothing.\n let matched = false;\n for (const candidate of signatures.split(' ')) {\n const [version, encoded] = candidate.split(',');\n const given = version === 'v1' && encoded !== undefined ? fromBase64(encoded) : null;\n\n if (given !== null && sameBytes(given, expected)) matched = true;\n }\n\n if (!matched) {\n throw invalid(\n \"No signature of the webhook matches the secret: not Mesub's, another endpoint's \" +\n 'secret, or a body changed on its way (parsed then serialized again?). ' +\n 'Verify the raw body.',\n );\n }\n\n // After the signature: a stale one is Mesub's, replayed or late on a clock.\n const skew = Date.now() - Number(timestamp) * 1000;\n if (Math.abs(skew) > tolerance) {\n throw invalid(\n `The webhook was signed ${Math.round(Math.abs(skew) / 1000)} s ` +\n `${skew > 0 ? 'ago' : 'ahead of this clock'}, beyond the ${tolerance / 1000} s ` +\n \"tolerance: a replay, or this server's clock is off.\",\n );\n }\n\n return webhookEventFrom(parsed(body, bytes), id);\n}\n\n/** The body as signed: the bytes received, or a string's UTF-8. */\nfunction bytesOf(body: WebhookBody): Uint8Array<ArrayBuffer> {\n if (typeof body === 'string') return new TextEncoder().encode(body);\n if (body instanceof Uint8Array) return new Uint8Array(body);\n if (body instanceof ArrayBuffer) return new Uint8Array(body.slice(0));\n\n throw new TypeError(\n 'body must be the raw body as received, a string or a Buffer, never parsed: the ' +\n 'signature is over its exact bytes. With Express, read it with ' +\n \"express.raw({ type: 'application/json' }); in a Next route, await request.text().\",\n );\n}\n\n/** The JSON Mesub signed. Not JSON, or not UTF-8, is a body this release cannot read. */\nfunction parsed(body: WebhookBody, bytes: Uint8Array): unknown {\n try {\n const text =\n typeof body === 'string'\n ? body\n : new TextDecoder('utf-8', { fatal: true }).decode(bytes);\n\n return JSON.parse(text);\n } catch (cause) {\n throw new MesubError('Mesub sent a webhook whose body is not JSON.', {\n status: null,\n code: 'unexpected',\n cause,\n });\n }\n}\n\n/** `whsec_` then base64: the bytes after the prefix are the HMAC key. */\nfunction keyOf(secret: unknown, name: string): Uint8Array<ArrayBuffer> {\n if (secret === undefined || secret === '') {\n throw new TypeError(\n typeof process === 'undefined'\n ? 'Missing Mesub webhook secret: there is no process.env here, so pass it: ' +\n '`{ secret: env.MESUB_WEBHOOK_SECRET }` or `new Mesub({ webhookSecret })`.'\n : 'Missing Mesub webhook secret: pass `{ secret }` or ' +\n '`new Mesub({ webhookSecret })`, or set MESUB_WEBHOOK_SECRET.',\n );\n }\n\n const key =\n typeof secret === 'string' && secret.startsWith(SECRET_PREFIX)\n ? fromBase64(secret.slice(SECRET_PREFIX.length))\n : null;\n\n if (key === null || key.length === 0) {\n throw new TypeError(\n `${name} must be the endpoint's signing secret, whsec_..., from its page in the ` +\n 'Mesub dashboard' +\n (typeof secret === 'string' && /^(SUB|PUB)_/.test(secret)\n ? ', not an API key.'\n : '.'),\n );\n }\n\n return key;\n}\n\n/** Checks the client's `webhookSecret`, or MESUB_WEBHOOK_SECRET, once at `new Mesub()`. */\nexport function webhookSecretOf(given: unknown): string | undefined {\n if (given !== undefined && typeof given !== 'string') {\n throw new TypeError('webhookSecret must be a string.');\n }\n\n const secret = given || envSecret();\n\n if (secret) keyOf(secret, given ? 'webhookSecret' : 'MESUB_WEBHOOK_SECRET');\n\n return secret || undefined;\n}\n\nfunction envSecret(): string | undefined {\n return typeof process === 'undefined' ? undefined : process.env?.['MESUB_WEBHOOK_SECRET'];\n}\n\n/** A header from a Fetch `Headers`, or a plain object whatever the case of its names. */\nfunction headerIn(headers: HeaderSource, name: string): string | undefined {\n if (typeof (headers as Headers).get === 'function') {\n return (headers as Headers).get(name) ?? undefined;\n }\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === name) return Array.isArray(value) ? value[0] : value;\n }\n\n return undefined;\n}\n\n/** HMAC-SHA256 of `id.timestamp.body`, as Standard Webhooks signs it. */\nasync function signatureOf(\n key: Uint8Array<ArrayBuffer>,\n id: string,\n timestamp: string,\n body: Uint8Array,\n): Promise<Uint8Array> {\n const prefix = new TextEncoder().encode(`${id}.${timestamp}.`);\n const signed = new Uint8Array(prefix.length + body.length);\n signed.set(prefix);\n signed.set(body, prefix.length);\n\n const hmac = await crypto.subtle.importKey(\n 'raw',\n key,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n\n return new Uint8Array(await crypto.subtle.sign('HMAC', hmac, signed));\n}\n\n/**\n * The three headers Mesub sends with a body, signed with that secret: for\n * `@mesub/node/testing`, and the tests.\n *\n * @internal\n */\nexport async function signedHeaders(\n secret: string,\n id: string,\n timestamp: number,\n body: string,\n): Promise<Record<string, string>> {\n const key = keyOf(secret, 'secret');\n const signature = await signatureOf(key, id, String(timestamp), new TextEncoder().encode(body));\n\n return {\n 'webhook-id': id,\n 'webhook-timestamp': String(timestamp),\n 'webhook-signature': `v1,${toBase64(signature)}`,\n };\n}\n\n/** Constant time over equal lengths; a length is no secret. */\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n\n let difference = 0;\n for (let index = 0; index < a.length; index += 1) {\n difference |= (a[index] ?? 0) ^ (b[index] ?? 0);\n }\n\n return difference === 0;\n}\n\nfunction fromBase64(text: string): Uint8Array<ArrayBuffer> | null {\n try {\n return Uint8Array.from(atob(text), (char) => char.charCodeAt(0));\n } catch {\n return null;\n }\n}\n\nfunction toBase64(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...bytes));\n}\n\nfunction invalid(message: string): MesubError {\n return new MesubError(message, { status: null, code: 'invalid_webhook' });\n}\n","import { MesubError, codeFor } from './errors.js';\nimport { numberOf } from './options.js';\nimport { API_VERSION, API_VERSION_HEADER, VERSION } from './version.js';\n\nexport type QueryValue = string | number | boolean | undefined;\n\nexport interface TransportConfig {\n apiKey: string;\n baseUrl: string;\n /** Sent with every call, before the SDK's own, which they never replace. */\n headers: Record<string, string>;\n fetch: typeof fetch;\n timeout: number;\n maxRetries: number;\n}\n\n/** What any one call may set for itself. */\nexport interface RequestOptions {\n /** Per attempt, in milliseconds. Defaults to the client's `timeout`. */\n timeout?: number;\n /**\n * Stops the call, and any wait for a retry: it then rejects with the\n * signal's reason, and is never sent again.\n */\n signal?: AbortSignal;\n}\n\n/** What one GET may change from the client's configuration. */\nexport interface CallOptions extends RequestOptions {\n maxRetries?: number;\n /**\n * Epoch ms by which the call settles, retries and waits included: each\n * attempt is cut at it, and a wait that would outlast it is not waited.\n */\n deadline?: number;\n /**\n * False to throw a 429 at once, whatever it asks: a guard falls back\n * rather than wait for the rate limit, and send more on top of it.\n */\n retryRateLimited?: boolean;\n}\n\n/** The id Mesub gives every request, on every answer (Mesub-io/backend#472). */\nconst REQUEST_ID_HEADER = 'Mesub-Request-Id';\n\nconst INITIAL_RETRY_DELAY = 500;\nconst MAX_RETRY_DELAY = 8_000;\nconst MAX_RETRY_AFTER = 60_000;\n\ntype Method = 'GET' | 'POST';\n\ntype Attempt =\n | { ok: true; body: unknown }\n | { ok: false; error: MesubError; retry: boolean; retryAfter: number | null };\n\n/** @internal Authenticated JSON calls to the Mesub API, with timeouts and retries. */\nexport class Transport {\n readonly #config: TransportConfig;\n\n constructor(config: TransportConfig) {\n this.#config = config;\n }\n\n async get(\n path: string,\n query: Record<string, QueryValue> = {},\n options: CallOptions = {},\n ): Promise<unknown> {\n const url = endpoint(this.#config.baseUrl, path);\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n return this.#send('GET', url, undefined, options);\n }\n\n /**\n * A POST with a JSON body, sent once: never retried here, whatever the\n * status, a timeout or a network error. A write that got no answer may\n * still have been done: its caller decides, knowing the route, whether\n * the same body may be sent again (`submit` does, Mesub-io/backend#190)\n * or the state read back.\n */\n async post(\n path: string,\n body: unknown,\n options: Omit<CallOptions, 'maxRetries'> = {},\n ): Promise<unknown> {\n return this.#send('POST', endpoint(this.#config.baseUrl, path), body, options);\n }\n\n async #send(method: Method, url: URL, body: unknown, options: CallOptions): Promise<unknown> {\n const maxRetries = method === 'POST' ? 0 : (options.maxRetries ?? this.#config.maxRetries);\n const { deadline, signal } = options;\n // As the client's own: NaN or 0 would cut the call as it is sent.\n numberOf('timeout', options.timeout, this.#config.timeout, { delay: true });\n\n for (let retry = 0; ; retry++) {\n const attempt = await this.#attempt(method, url, body, options);\n if (attempt.ok) return attempt.body;\n if (!attempt.retry || retry >= maxRetries) throw attempt.error;\n if (options.retryRateLimited === false && attempt.error.status === 429) {\n throw attempt.error;\n }\n\n const wait = attempt.retryAfter ?? backoff(retry);\n // Not even a Retry-After is waited past the deadline: the caller\n // falls back now rather than at the end of a wait it cannot afford.\n if (deadline !== undefined && Date.now() + wait >= deadline) {\n throw attempt.error;\n }\n await sleep(wait, signal);\n }\n }\n\n async #attempt(\n method: Method,\n url: URL,\n body: unknown,\n { deadline, signal, ...options }: CallOptions,\n ): Promise<Attempt> {\n // The caller's abort is theirs to handle: thrown as is, never retried.\n signal?.throwIfAborted();\n\n const perAttempt = options.timeout ?? this.#config.timeout;\n const left = deadline === undefined ? Infinity : deadline - Date.now();\n\n if (left <= 0) {\n const error = new MesubError('No time was left to call Mesub.', {\n status: null,\n code: 'unavailable',\n });\n return { ok: false, error, retry: false, retryAfter: null };\n }\n\n // Cut at the deadline when it comes before the attempt's own timeout.\n const timeout = Math.min(perAttempt, left);\n const cutByDeadline = timeout < perAttempt;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n const stop = () => controller.abort(signal?.reason);\n signal?.addEventListener('abort', stop, { once: true });\n\n try {\n const response = await this.#config.fetch(url, {\n method,\n headers: {\n ...this.#config.headers,\n Authorization: `Bearer ${this.#config.apiKey}`,\n Accept: 'application/json',\n 'User-Agent': `@mesub/node/${VERSION}`,\n [API_VERSION_HEADER]: API_VERSION,\n ...(body !== undefined && { 'Content-Type': 'application/json' }),\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n signal: controller.signal,\n // Every call carries the key, and Mesub never redirects: a\n // redirect is never followed, so the key is never sent on (#171).\n redirect: 'manual',\n });\n\n if (isRedirect(response)) {\n await response.body?.cancel();\n const to = response.headers.get('location');\n const error = new MesubError(\n `${url.pathname} answered a redirect${to ? ` to ${to}` : ''}, and Mesub never ` +\n `redirects: is baseUrl (${this.#config.baseUrl}) the Mesub API?`,\n {\n status: response.status || null,\n code: 'unexpected',\n requestId: response.headers.get(REQUEST_ID_HEADER),\n },\n );\n return { ok: false, error, retry: false, retryAfter: null };\n }\n\n const text = await response.text();\n const requestId = response.headers.get(REQUEST_ID_HEADER);\n\n if (response.ok) {\n try {\n return { ok: true, body: JSON.parse(text) };\n } catch (cause) {\n const error = new MesubError('Mesub answered with a body that is not JSON.', {\n status: response.status,\n code: 'unexpected',\n body: text,\n requestId,\n cause,\n });\n return { ok: false, error, retry: false, retryAfter: null };\n }\n }\n\n const answered = parsed(text);\n const asked = retryAfter(response.headers.get('retry-after'));\n const error =\n response.status === 404 && !isMesubErrorBody(answered)\n ? new MesubError(\n `${url.pathname} answered 404 with no Mesub error: is baseUrl ` +\n `(${this.#config.baseUrl}) the Mesub API?`,\n {\n status: 404,\n code: 'unexpected',\n body: answered,\n retryAfter: asked,\n requestId,\n },\n )\n : errorFrom(response.status, answered, asked, requestId);\n\n return {\n ok: false,\n error,\n // Sent again (a GET only, a POST never is) when the error is `retryable`:\n // Mesub's flag when the body has one, the status's otherwise.\n retry: error.retryable,\n retryAfter: asked === null ? null : Math.min(asked, MAX_RETRY_AFTER),\n };\n } catch (cause) {\n if (signal?.aborted) throw signal.reason;\n\n if (controller.signal.aborted && cutByDeadline) {\n const error = new MesubError(\n `Mesub did not answer within the ${Math.round(timeout)} ms left before the deadline.`,\n { status: null, code: 'unavailable', cause },\n );\n return { ok: false, error, retry: false, retryAfter: null };\n }\n\n const message = controller.signal.aborted\n ? `Mesub did not answer within ${perAttempt} ms.`\n : `Could not reach Mesub: ${cause instanceof Error ? cause.message : String(cause)}`;\n const error = new MesubError(message, { status: null, code: 'unavailable', cause });\n return { ok: false, error, retry: true, retryAfter: null };\n } finally {\n clearTimeout(timer);\n signal?.removeEventListener('abort', stop);\n }\n }\n}\n\n/** A 3xx, or the opaque answer a browser's fetch gives for one under `redirect: 'manual'`. */\nfunction isRedirect(response: Response): boolean {\n return response.type === 'opaqueredirect' || (response.status >= 300 && response.status < 400);\n}\n\n/**\n * A path under the base URL, which may carry its own path:\n * `https://proxy.example.com/mesub` and `/v1/access` make\n * `https://proxy.example.com/mesub/v1/access`. Never `new URL(path, base)`,\n * which drops the base's path for a path starting with `/`.\n */\nfunction endpoint(baseUrl: string, path: string): URL {\n return new URL(baseUrl + path);\n}\n\n/**\n * The error a Mesub error body describes since the back's error codes\n * (Mesub-io/backend#180): Nest's `statusCode`, `error` and `message`, plus a\n * stable `code`, a `retryable` flag and the `request_id`. Without them, as from a proxy in\n * front, the status alone decides.\n */\nfunction errorFrom(\n status: number,\n body: unknown,\n retryAfter: number | null,\n header: string | null,\n): MesubError {\n const { code, retryable, request_id } = isRecord(body) ? body : {};\n const apiCode = typeof code === 'string' ? code : null;\n\n return new MesubError(messageFrom(body, status), {\n status,\n code: codeFor(status, apiCode),\n apiCode,\n retryable: typeof retryable === 'boolean' ? retryable : retryableStatus(status),\n body,\n retryAfter,\n // The header first: the body's says the same, and is the fallback\n // when a proxy in front drops the header (Mesub-io/backend#472).\n requestId: header ?? (typeof request_id === 'string' ? request_id : null),\n });\n}\n\n/**\n * What the status says, when the body does not: the Stainless clients' rules,\n * but for a 409, which is Mesub's final word on a state, never a race to retry.\n */\nfunction retryableStatus(status: number): boolean {\n return status === 408 || status === 429 || status >= 500;\n}\n\n/**\n * Milliseconds from a `Retry-After` in seconds or as an HTTP date, as sent:\n * what the error carries. A GET waits it capped at `MAX_RETRY_AFTER`.\n */\nfunction retryAfter(header: string | null): number | null {\n if (header === null || header.trim() === '') return null;\n const seconds = Number(header);\n const ms = Number.isNaN(seconds) ? Date.parse(header) - Date.now() : seconds * 1000;\n if (!Number.isFinite(ms) || ms < 0) return null;\n return ms;\n}\n\n/** 500 ms, then 1 s, ... up to 8 s, minus up to 25% of jitter. */\nfunction backoff(retry: number): number {\n const delay = Math.min(INITIAL_RETRY_DELAY * 2 ** retry, MAX_RETRY_DELAY);\n return delay * (1 - Math.random() * 0.25);\n}\n\n/** The NestJS error body is `{ message, error, statusCode }`, `message` a string or a list. */\nfunction messageFrom(body: unknown, status: number): string {\n const fallback = `Mesub answered with HTTP ${status}.`;\n if (!isRecord(body)) return fallback;\n\n const { message, error } = body;\n if (typeof message === 'string' && message !== '') return message;\n if (Array.isArray(message)) {\n const parts = message.filter((part): part is string => typeof part === 'string');\n if (parts.length > 0) return parts.join('; ');\n }\n if (typeof error === 'string' && error !== '') return error;\n return fallback;\n}\n\n/**\n * Whether a 404 came from the Mesub API rather than from whatever else lives\n * at a wrong `baseUrl`: a JSON error body, as Nest writes it (`statusCode`\n * and `message`) or with a `code`. Nest's own answer for a route it does not\n * have (`Cannot GET /api/v1/access`) is not one, though the back now codes it\n * `not_found`: a path prefix too many.\n */\nfunction isMesubErrorBody(body: unknown): boolean {\n if (!isRecord(body)) return false;\n\n const { code, statusCode, message } = body;\n if (typeof message === 'string' && /^Cannot [A-Z]+ \\//.test(message)) return false;\n\n return typeof code === 'string' || typeof statusCode === 'number';\n}\n\n/** The body as JSON when it parses, the text as it came otherwise. */\nfunction parsed(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** @internal Rejects with the signal's reason as soon as it aborts. */\nexport function sleep(ms: number, signal: AbortSignal | undefined): Promise<void> {\n return new Promise((resolve, reject) => {\n const stop = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', stop);\n resolve();\n }, ms);\n signal?.addEventListener('abort', stop, { once: true });\n });\n}\n","import type {\n BlockedReason,\n Customer,\n EndReason,\n LateReason,\n PaymentStatus,\n PullOutcome,\n StopReason,\n} from './answer.js';\nimport { customerOf } from './customer.js';\nimport { type MesubErrorCode, MesubError, MesubSubmitError } from './errors.js';\nimport { numberOf } from './options.js';\nimport { type QueryValue, type RequestOptions, type Transport, sleep } from './transport.js';\nimport {\n confirmResultFrom,\n serverSubscriptionFrom,\n serverSubscriptionListFrom,\n subscriptionAttemptListFrom,\n submitResultFrom,\n subscribeTransactionFrom,\n walletTransactionFrom,\n} from './validate.js';\n\n/**\n * Subscribing from your own server, with the API key (Mesub-io/backend#143):\n * `POST /v1/subscriptions`, a fresh transaction, its submit, the reads, and what stops one\n * (Mesub-io/backend#276): cancel, resume and close. The types copy the\n * back's `server-subscriptions.service.ts`, which stays the source of truth.\n * Snake case, as the API serves it.\n */\n\n/**\n * A subscription's status. Unlike `/v1/access`, may be `expired`: a checkout\n * nobody signed (Mesub-io/backend#139). `superseded` is a stopped one the\n * wallet came back over (Mesub-io/backend#213): a newer row holds the\n * subscription. Never `none`: a row exists. `cancelled` reads `ended` once\n * its end date passed, as on `/v1/access` (Mesub-io/backend#236).\n */\nexport type ServerSubscriptionStatus =\n | 'pending'\n | 'active'\n | 'cancelled'\n | 'unpaid'\n | 'stopped'\n | 'ended'\n | 'failed'\n | 'expired'\n | 'superseded';\n\n/** One subscription to your project's plans, as `GET /v1/subscriptions/:id` answers it. */\nexport interface ServerSubscription {\n id: string;\n status: ServerSubscriptionStatus;\n /** A seat parked over the project's cap, status unchanged, as `/v1/access` answers it. */\n paused: boolean;\n /** Why it ended, only on `ended`, as `/v1/access` answers it. */\n end_reason: EndReason | null;\n /** Why it is late, on `unpaid` or `stopped`, as `/v1/access` answers it. */\n late_reason: LateReason | null;\n /** Why it stopped, only on `stopped`, as `/v1/access` answers it. */\n stop_reason: StopReason | null;\n /** Whether it grants access now, as `/v1/access` answers it. */\n access: boolean;\n /** `paid`, `late` or `none`, as `/v1/access` answers it. */\n payment_status: PaymentStatus;\n /**\n * Since when its charges are blocked for a reason that is not the\n * subscriber's, and whose cause it is, as `/v1/access` answers them\n * (Mesub-io/backend#398): both null unless blocked. Read as null from a\n * back that predates them.\n */\n blocked_since: string | null;\n blocked_reason: BlockedReason | null;\n /** The plan's slug. */\n plan: string | null;\n wallet: string;\n email: string | null;\n external_id: string | null;\n /** The period paid for last; null before the first. */\n current_period_start: string | null;\n current_period_end: string | null;\n /**\n * The next charge on a running one, the next retry on a late one: never\n * both, and neither once the plan's end leaves no pull to run.\n */\n next_charge_at: string | null;\n next_retry_at: string | null;\n /**\n * Free only, as `/v1/access` answers it (Mesub-io/backend#191): when hand\n * retries close, the plan's end when that comes first; past it the\n * subscription stops. Read as null from a back that predates it.\n */\n retry_deadline: string | null;\n /**\n * The retry due at `next_retry_at` and how many the missed period gets\n * (Mesub-io/backend#289): 2 and 3 before the second retry of three. Both\n * null unless Mesub retries on its own: not late, paused, stopped, on\n * Free, or on a plan that ends before the retry. Read as null from a\n * back that predates them.\n */\n next_retry_number: number | null;\n retries_allowed: number | null;\n /**\n * When access ends unless a pull renews it; null while `access` is false.\n * Never later than the plan's end.\n */\n access_until: string | null;\n /** Whether the subscriber can close it now, as `/v1/access` answers it. */\n closable: boolean;\n /** When it can be closed, if not now and a date is known, as `/v1/access` answers it. */\n closable_from: string | null;\n created_at: string;\n confirmed_at: string | null;\n}\n\n/** What subscribing costs in SOL, in lamports, as strings: a u64 does not survive JSON. */\nexport interface SubscribeCosts {\n /** Given back to the subscriber when the accounts are closed. `authority` is null if it exists. */\n rent: { subscription: string; authority: string | null; total: string };\n /** Spent: the network's fee per signature, the wallet's and Mesub's. */\n fee: { signatures: number; per_signature: string; priority: string; total: string };\n total: string;\n}\n\n/** What `create` and `transaction` answer: what the wallet signs, terms first, then the transaction. */\nexport interface SubscribeTransaction {\n subscription: { id: string; status: ServerSubscriptionStatus };\n /** Base64, signed by nobody: the wallet signs it with signTransaction, without sending it. */\n transaction: string;\n last_valid_block_height: string;\n /**\n * When `last_valid_block_height` is expected to pass, about half a minute\n * after the answer (Mesub-io/backend#471): an estimate that errs early,\n * for a countdown or to ask `transaction` for a fresh one in time. The\n * height is the truth: rebuild after `transaction_expired`, before this\n * time as after it. Null from a back that predates it.\n */\n transaction_expires_at: string | null;\n costs: SubscribeCosts;\n /** Signed as is with the wallet's signMessage, over its UTF-8 bytes, before `expires_at`. */\n terms: { message: string; expires_at: string };\n}\n\n/**\n * What `submit` settled on: what Mesub answered, or, when its answer was lost\n * and the subscription read back is `active` or `cancelled`, that one.\n */\nexport interface SubmitResult {\n /**\n * `active`, or `cancelled` if the wallet set an end (`ended` once that\n * end passed); `pending` or `failed` with a `reason`.\n */\n subscription: ServerSubscription;\n /**\n * Mesub's own, set when nothing landed (`pending`) or what landed is not\n * Mesub's (`failed`). Never made up by the SDK. A sentence for a person,\n * which Mesub may reword: branch on `reason_code`.\n */\n reason?: string;\n /** Why, for a program: set exactly when `reason` is, from a back that serves it. */\n reason_code?: ConfirmReasonCode;\n}\n\n/**\n * What `cancel`, `resume` and `close` answer: a transaction only the wallet\n * signs, and sends itself. Mesub signs nothing of it.\n */\nexport interface WalletTransaction {\n /** Base64, signed by nobody: the wallet signs and sends it, then names its signature. */\n transaction: string;\n /** The block height past which it can no longer land. */\n last_valid_block_height: string;\n /** When that height is expected to pass, as `SubscribeTransaction` says it. */\n transaction_expires_at: string | null;\n}\n\n/** What the wallet answered when it sent the transaction of `cancel`, `resume` or `close`. */\nexport interface ConfirmParams {\n /** The transaction's signature, base58. */\n signature: string;\n}\n\n/** What `confirmCancel`, `confirmResume` and `confirmClose` settled on. */\nexport interface ConfirmResult {\n /** The subscription as it is now: moved, or as it was with a `reason`. */\n subscription: ServerSubscription;\n /**\n * Mesub's own, set when nothing changed: the transaction failed, expired\n * before it landed, or the subscription moved on meanwhile. A sentence\n * for a person, which Mesub may reword: branch on `reason_code`.\n */\n reason?: string;\n /** Why, for a program: set exactly when `reason` is, from a back that serves it. */\n reason_code?: ConfirmReasonCode;\n}\n\n/**\n * Why a submit or a confirm settled nothing, or not what was built\n * (Mesub-io/backend#470): stable, snake case, never reworded. An open family:\n * Mesub adds codes, so keep a default branch; one newer than this release is\n * handed on as it is.\n *\n * - `not_our_transaction`: not a transaction Mesub co-signed over terms the\n * wallet signed for this subscription.\n * - `transaction_failed`: it landed and the chain refused it.\n * - `transaction_expired`: its blockhash ran out before it landed.\n * - `nothing_landed`: on a submit, nothing stands at its address yet.\n * - `subscription_not_on_chain`: on a cancel or a resume, it is gone on chain.\n * - `subscription_mismatch`: what landed names another wallet, plan, amount\n * or period: now `failed`.\n * - `dev_not_running`: Mesub's own Dev plan landed cancelled or part paid.\n * - `earlier_subscription_running`: a comeback paid while the one it came\n * back over ran again.\n * - `subscription_remade`: on a submit, closed and made again without Mesub.\n * - `cancellation_not_landed`, `resume_not_landed`, `close_not_landed`: the\n * chain does not show the action.\n * - `subscription_changed`: it moved on while the cancellation was read.\n * - `subscription_not_cancelled`: on a resume, nothing to resume.\n * - `cancelled_subscription_remade`: on a resume, closed and made again\n * without Mesub: it stays `cancelled`.\n * - `resume_unverified`: on a resume, the one on chain cannot be told to be\n * the one cancelled: it stays `cancelled`.\n */\nexport type ConfirmReasonCode =\n | 'not_our_transaction'\n | 'transaction_failed'\n | 'transaction_expired'\n | 'nothing_landed'\n | 'subscription_not_on_chain'\n | 'subscription_mismatch'\n | 'dev_not_running'\n | 'earlier_subscription_running'\n | 'subscription_remade'\n | 'cancellation_not_landed'\n | 'resume_not_landed'\n | 'close_not_landed'\n | 'subscription_changed'\n | 'subscription_not_cancelled'\n | 'cancelled_subscription_remade'\n | 'resume_unverified'\n // Any newer code, while the names above still complete.\n | (string & {});\n\n/** One page of a customer's subscriptions, newest first. */\nexport interface ServerSubscriptionList {\n data: ServerSubscription[];\n /** Ask for the next page with `starting_after` set to the last id of this one. */\n has_more: boolean;\n}\n\n/**\n * One pull attempt of a subscription, as `GET /v1/subscriptions/:id/attempts`\n * answers it (Mesub-io/backend#289): an attempt of `/v1/access`, with its id,\n * whether it was a retry and the period it was for.\n */\nexport interface SubscriptionAttempt {\n id: string;\n attempted_at: string;\n /** One of `PullOutcome` today; a newer one is handed on as is. */\n outcome: PullOutcome;\n /**\n * Mesub's short reason, null on a paid one, as on `ServedAttempt`: snake\n * case, an open string with the `program_${number}` and\n * `solana_${number}` families.\n */\n reason: string | null;\n /**\n * In the mint's smallest unit, as a string. What was asked for; on a\n * `paid` one that is what the transfer moved.\n */\n amount: string;\n /**\n * That amount in dollars at the price of the instant it was attempted, a\n * decimal string such as `\"9.99\"`. Null until Mesub priced it, when it\n * knows no price, and from a Mesub that predates it.\n */\n amount_usd: string | null;\n /**\n * What the receiver's account got of a `paid` one, in the mint's smallest\n * unit: `amount` less the fee a Token-2022 mint withholds on transfers.\n * Null when not known (an attempt not paid, one recorded before Mesub\n * kept it) and from a Mesub that predates it.\n */\n amount_received: string | null;\n /** The transaction that paid. Null on every outcome but `paid`, a refused one included. */\n signature: string | null;\n /** The subscription had already failed on this period. */\n retry: boolean;\n /**\n * On a retry of the plan's, which one and out of how many, as recorded\n * when it ran: 1 is the first after the missed charge. Both null on a\n * first try, on a hand retry on Dev or Business, on an attempt that kept\n * none, and from a back that does not serve them.\n */\n retry_number: number | null;\n retries_allowed: number | null;\n /** The start of the period it was for; null when Mesub recorded none. */\n period_start: string | null;\n}\n\n/** One page of a subscription's attempts, newest first, and what it paid in all. */\nexport interface SubscriptionAttemptList {\n data: SubscriptionAttempt[];\n /** Ask for the next page with `starting_after` set to the last id of this one. */\n has_more: boolean;\n /**\n * Its `paid` attempts since it began and the sum of what they moved, in\n * the mint's smallest unit: counted by Mesub over all of them, not this page.\n */\n paid: { count: number; amount: string };\n}\n\n/** Which page of a subscription's attempts. */\nexport interface AttemptsParams {\n /** 1 to 100, 20 by default. */\n limit?: number;\n /** The id of the last attempt of the previous page. */\n starting_after?: string;\n}\n\nexport interface SubscribeParams {\n /** The plan's slug. */\n plan: string;\n /** The wallet that signs and pays. */\n wallet: string;\n /** Where the subscriber's notices go. Not verified by Mesub. */\n email?: string;\n /** Your own id for this customer, handed back as given. */\n external_id?: string;\n}\n\nexport interface SubmitParams {\n /** The transaction of `create`, signed by the wallet, base64. */\n transaction: string;\n /** The wallet's signMessage over `terms.message`, base58. */\n terms_signature: string;\n}\n\nexport interface SubmitOptions extends RequestOptions {\n /**\n * Per send, in milliseconds: 90 s by default. Lower it behind a proxy\n * or a function with a shorter limit (Cloudflare cuts at 100 s); a send\n * cut short may still be co-signed and land, so read the subscription\n * back afterwards.\n */\n timeout?: number;\n /**\n * The whole submit in milliseconds, its sends and the waits between\n * them: `timeout` plus 30 s by default, so 120 s. A send is cut at it,\n * and a wait that would outlast it is not waited. Reading the\n * subscription back once it is spent takes 10 s more at most, retries\n * included: 130 s in all by default.\n */\n budget?: number;\n}\n\nexport interface ConfirmOptions extends RequestOptions {\n /**\n * In milliseconds: 90 s by default. Mesub waits for the transaction to\n * land or its blockhash to expire, about a minute. A confirm cut short\n * changed nothing: send it again with the same signature.\n */\n timeout?: number;\n}\n\n/**\n * Whose subscriptions to list: a `Customer`, named exactly one way and\n * normalised as `access` names them, and what narrows the page.\n */\nexport type ListParams = Customer & {\n /** Only this plan's, by slug. */\n plan?: string;\n /** 1 to 100, 20 by default. */\n limit?: number;\n /** The id of the last subscription of the previous page. */\n starting_after?: string;\n};\n\n/**\n * How long one send of `submit` waits by default. Mesub answers once the\n * transaction landed or its blockhash expired, about 150 blocks: a minute,\n * give or take.\n */\nconst SUBMIT_TIMEOUT = 90_000;\n/**\n * What the whole submit gets beyond its first send's timeout, by default:\n * room for a replay, which Mesub answers from the chain, at once if the\n * blockhash has expired by then.\n */\nconst REPLAY_ROOM = 30_000;\n/** The first send and two replays, at most. */\nconst MAX_SENDS = 3;\n/** The wait before a replay when Mesub named none: what `network_unavailable` asks. */\nconst REPLAY_WAIT = 10_000;\n/**\n * The read back once the sends are done, retries included: a submit never\n * takes longer than its budget plus this.\n */\nconst READ_BACK_TIME = 10_000;\n\n/** How long a confirm waits by default: as one send of `submit`, for the same chain. */\nconst CONFIRM_TIMEOUT = SUBMIT_TIMEOUT;\n\n/** What stops, takes back or closes a subscription: each a route, and its `/confirm`. */\ntype Action = 'cancel' | 'resume' | 'close';\n\n/** How a submit whose outcome is unknown is settled from the subscription read back. */\ninterface Unsettled {\n sends: number;\n /** The last send's error. */\n cause: MesubError;\n /** Whose status, apiCode, body and retryAfter the error keeps. */\n from: MesubError;\n code: MesubErrorCode;\n retryable: boolean;\n /** What the message says first. */\n lead: string;\n}\n\nexport class Subscriptions {\n readonly #transport: Transport;\n /**\n * Told of every subscription `submit`, `retrieve` and `list` answer, so\n * the client drops the cached access answers one that landed outdates.\n */\n readonly #seen: (subscription: ServerSubscription) => Promise<void>;\n\n /**\n * Told of the subscription a confirm moved, so the client drops every\n * access answer cached for its customer, the yes as well as the no.\n */\n readonly #changed: (subscription: ServerSubscription) => Promise<void>;\n\n /** @internal */\n constructor(\n transport: Transport,\n seen: (subscription: ServerSubscription) => Promise<void> = async () => {},\n changed: (subscription: ServerSubscription) => Promise<void> = seen,\n ) {\n this.#transport = transport;\n this.#seen = seen;\n this.#changed = changed;\n }\n\n /**\n * Reserves a subscription to that plan for that wallet, and builds what\n * it signs: `terms.message`, then `transaction`. Hand both to your front,\n * then their signatures to `submit`. Called again for the same plan,\n * wallet and customer (`external_id`, else `email`) while nothing landed,\n * it answers the same subscription with a fresh transaction; by its id,\n * `transaction` does. Another\n * customer naming that wallet gets a subscription of its own\n * (Mesub-io/backend#311): only the one the wallet signs for lands, and\n * the others then expire. Sent once, never retried.\n */\n async create(\n params: SubscribeParams,\n options: RequestOptions = {},\n ): Promise<SubscribeTransaction> {\n const { plan, wallet, email, external_id } = params;\n const body = { plan, wallet, email, external_id };\n\n return subscribeTransactionFrom(\n await this.#transport.post('/v1/subscriptions', body, options),\n );\n }\n\n /**\n * Relays what the wallet signed. Mesub checks it, co-signs, sends it and\n * waits for the chain, so one send takes up to a minute or so: 90 s\n * unless `options.timeout` says otherwise.\n *\n * When the send gets no answer that says what became of it (a timeout,\n * a network error, a 5xx), or an error Mesub marks `retryable`, the same\n * request, the same body, is sent again, up to twice, after the\n * `Retry-After` Mesub asked for or 10 s, within `options.budget` (120 s\n * by default). That is safe since Mesub-io/backend#190 and #202: Mesub\n * recognises the transaction and the terms signature it already\n * co-signed, signs nothing again, and answers from the chain or from the\n * subscription it settled. Its answer is then returned as if the first\n * send had got it, a `pending` with Mesub's own `reason` included.\n *\n * When no send got an answer, the subscription is read back once, within\n * 10 s: it is returned if `active` or `cancelled` (it landed), and\n * otherwise a `MesubSubmitError` `unavailable` is thrown with it attached\n * as `subscription` (null if that read failed too): the wallet may have\n * paid and the transaction may still land, so read it again with\n * `retrieve` rather than create anew. A 2xx this SDK cannot read is read\n * back the same way, and thrown as `unexpected`; so is a replay refused\n * with `not_awaiting_signature` after a send that got no answer, thrown\n * as that `conflict`.\n *\n * A 503 `unavailable` (too many submits waiting on the project,\n * Mesub-io/backend#505) says nothing was signed or sent: it is replayed\n * after its `Retry-After` (2 s) like any retryable refusal, and when the\n * replays run out it is thrown as is, `retryable`, with no read back,\n * unless an earlier send got no answer.\n *\n * Any other refusal (`terms_expired`, `transaction_expired`, ...) is\n * thrown as is. After `transaction_expired` nothing was sent and the same\n * submit cannot land: call `transaction(id)` for a new one, never\n * `create` again.\n * An abort through `options.signal` stops everything, sends, waits and\n * the read back, and rejects with the signal's reason; a send already out\n * may have been co-signed, so read the subscription back.\n *\n * A subscription returned with `access` (`active`, or `cancelled` before\n * its end) drops the `/v1/access` answers cached for that customer (its\n * wallet, external id and email) that still say no: `hasAccess` right\n * after asks Mesub again.\n */\n async submit(\n id: string,\n params: SubmitParams,\n options: SubmitOptions = {},\n ): Promise<SubmitResult> {\n const result = await this.#submit(id, params, options);\n\n await this.#seen(result.subscription);\n return result;\n }\n\n /**\n * A fresh transaction for a subscription still `pending`, with the terms\n * it names (Mesub-io/backend#427). A transaction lives about half a\n * minute, the terms five: have the terms signed first, call this when the\n * wallet is ready, have it sign the transaction at once, then `submit`.\n * Always sign the transaction of the last call. It is also what follows\n * a submit refused with `transaction_expired`.\n *\n * A terms signature you hold is still good when `terms.message` is, to\n * the letter, the message the wallet signed. Another text is new terms:\n * have the wallet sign it, the old signature is refused\n * (`invalid_terms_signature`).\n *\n * Reserves nothing and sends no body: the wallet, the plan and the\n * customer are the subscription's own. Refused with `apiCode`\n * `not_awaiting_signature` once it is signed, expired or ended (call\n * `create` again for an expired one), and with what `create` refuses of\n * a plan or a wallet (`plan_ended`, `insufficient_balance`,\n * `already_subscribed`, ...). Sent once, never retried: asking twice is\n * safe, only the last transaction is to be signed.\n */\n async transaction(id: string, options: RequestOptions = {}): Promise<SubscribeTransaction> {\n return subscribeTransactionFrom(\n await this.#transport.post(`${pathOf(id)}/transaction`, undefined, options),\n 'POST /v1/subscriptions/:id/transaction',\n );\n }\n\n async #submit(id: string, params: SubmitParams, options: SubmitOptions): Promise<SubmitResult> {\n const path = `${pathOf(id)}/submit`;\n // Built once: every send carries exactly this body.\n const body = { transaction: params.transaction, terms_signature: params.terms_signature };\n const { signal } = options;\n // Checked here too: the budget is worked out from it.\n const timeout = numberOf('timeout', options.timeout, SUBMIT_TIMEOUT, { delay: true });\n const budget = options.budget ?? timeout + REPLAY_ROOM;\n if (!Number.isFinite(budget) || budget <= 0) {\n throw new TypeError(`budget must be a positive number of ms, not ${budget}.`);\n }\n const deadline = Date.now() + budget;\n /** Whether a send got no answer that says what became of it. */\n let lost = false;\n /** The last send's error that came with a response. */\n let responded: MesubError | null = null;\n\n for (let sent = 1; ; sent++) {\n let error: MesubError;\n try {\n const answer = await this.#transport.post(path, body, {\n timeout,\n deadline,\n ...(signal !== undefined && { signal }),\n });\n return submitResultFrom(answer);\n } catch (caught) {\n // The caller's abort, as is: nothing more is sent nor read.\n if (!(caught instanceof MesubError)) throw caught;\n error = caught;\n }\n\n const lostBefore = lost;\n if (unanswered(error)) lost = true;\n if (error.status !== null) responded = error;\n const unsettled = { sends: sent, cause: error, from: error, lead: error.message };\n\n // Mesub answered, and this SDK cannot read what: the row says.\n if (error.status !== null && error.status < 300) {\n return this.#settle(id, signal, {\n ...unsettled,\n code: 'unexpected',\n retryable: false,\n });\n }\n // The row moved on (failed, expired) since a send that got no\n // answer, which may have been co-signed: the row says what it is.\n if (lostBefore && error.apiCode === 'not_awaiting_signature') {\n return this.#settle(id, signal, {\n ...unsettled,\n code: error.code,\n retryable: error.retryable,\n });\n }\n\n // Refused while the billing period turns: the same transaction is\n // not taken after the wait, a new one is. Nothing to send again.\n const rebuild = error.apiCode === 'comeback_period_rolling';\n const replay = !rebuild && (error.status === null || error.retryable);\n const wait = error.retryAfter ?? REPLAY_WAIT;\n if (!replay || sent >= MAX_SENDS || Date.now() + wait >= deadline) {\n // A refusal is Mesub's word on the request, whatever was lost\n // before. A send that got no answer, or a retryable refusal\n // after one, leaves it to the row.\n if (!lost || (!unanswered(error) && !error.retryable)) throw error;\n return this.#settle(id, signal, {\n ...unsettled,\n from: responded ?? error,\n code: 'unavailable',\n retryable: true,\n lead:\n `Mesub never said what became of the submit, sent ${sent} ` +\n `${sent === 1 ? 'time' : 'times'}: ${error.message}`,\n });\n }\n\n await sleep(wait, signal);\n }\n }\n\n /**\n * A submit whose outcome is unknown, settled from the subscription read\n * back: returned if it landed, thrown with it otherwise.\n */\n async #settle(\n id: string,\n signal: AbortSignal | undefined,\n { sends, cause, from, code, retryable, lead }: Unsettled,\n ): Promise<SubmitResult> {\n const subscription = await this.#readBack(id, signal);\n\n if (subscription?.status === 'active' || subscription?.status === 'cancelled') {\n return { subscription };\n }\n\n const then =\n subscription === null\n ? 'Reading the subscription back failed too'\n : `The subscription read back is ${subscription.status}`;\n\n throw new MesubSubmitError(\n `${lead} ${then}: read it again with retrieve before creating anew.`,\n {\n status: from.status,\n code,\n apiCode: from.apiCode,\n retryable,\n body: from.body,\n retryAfter: from.retryAfter,\n requestId: from.requestId,\n cause,\n subscription,\n sends,\n },\n );\n }\n\n /**\n * The subscription after a submit, within `READ_BACK_TIME`: no attempt\n * nor retry wait goes past it. Null when it cannot be read; the caller's\n * abort is thrown as is.\n */\n async #readBack(\n id: string,\n signal: AbortSignal | undefined,\n ): Promise<ServerSubscription | null> {\n try {\n const body = await this.#transport.get(\n pathOf(id),\n {},\n {\n deadline: Date.now() + READ_BACK_TIME,\n ...(signal !== undefined && { signal }),\n },\n );\n return serverSubscriptionFrom(body);\n } catch (error) {\n if (signal?.aborted) throw error;\n return null;\n }\n }\n\n /**\n * Builds the transaction that cancels an `active`, `unpaid` or `stopped`\n * subscription. Hand it to your front: the subscription's wallet signs\n * and sends it, then `confirmCancel` takes the signature. Nothing is\n * cancelled until then. Sent once, never retried.\n *\n * One paid up keeps its access to the end of its period; a late or\n * stopped one has none, and its missed period is never collected.\n */\n async cancel(id: string, options: RequestOptions = {}): Promise<WalletTransaction> {\n return this.#build('cancel', id, options);\n }\n\n /**\n * Settles a cancellation the wallet sent, by the signature it answered:\n * `cancelled`, or the subscription as it was with a `reason` when nothing\n * landed. Mesub waits for the chain, so up to a minute or so: 90 s unless\n * `options.timeout` says otherwise. Sent once, never retried; the same\n * confirm sent again is answered the same.\n *\n * Settled without a `reason`, it drops every `/v1/access` answer cached\n * for that customer on that plan: `access` right after asks Mesub again.\n */\n async confirmCancel(\n id: string,\n params: ConfirmParams,\n options: ConfirmOptions = {},\n ): Promise<ConfirmResult> {\n return this.#confirm('cancel', id, params, options);\n }\n\n /**\n * Builds the transaction that takes a `cancelled` subscription back,\n * before the period it paid for is over. Hand it to your front: the\n * wallet signs and sends it, then `confirmResume` takes the signature.\n * Sent once, never retried.\n */\n async resume(id: string, options: RequestOptions = {}): Promise<WalletTransaction> {\n return this.#build('resume', id, options);\n }\n\n /** As `confirmCancel`, for a resume: `active` again, or a `reason`. */\n async confirmResume(\n id: string,\n params: ConfirmParams,\n options: ConfirmOptions = {},\n ): Promise<ConfirmResult> {\n return this.#confirm('resume', id, params, options);\n }\n\n /**\n * Builds the transaction that closes the wallet's authorisation on a\n * subscription that is over: its rent goes back to the wallet. Mesub\n * builds it for a cancelled one past its end; for a stopped one\n * cancelled on chain and past its end, or whose authority is closed for\n * good; and for one `ended` with its plan (`end_reason` `plan_removed`,\n * `plan_replaced` or `plan_ended`). A stopped one that nothing cancelled\n * is refused with `subscription_not_cancelled`: cancel it first.\n * Hand the transaction to your front: the wallet signs and sends it,\n * then `confirmClose` takes the signature. Sent once, never retried.\n */\n async close(id: string, options: RequestOptions = {}): Promise<WalletTransaction> {\n return this.#build('close', id, options);\n }\n\n /** As `confirmCancel`, for a close: `ended` with `end_reason` `closed`, or a `reason`. */\n async confirmClose(\n id: string,\n params: ConfirmParams,\n options: ConfirmOptions = {},\n ): Promise<ConfirmResult> {\n return this.#confirm('close', id, params, options);\n }\n\n /**\n * Pays a late payment now (Mesub-io/backend#354): the pull of the missed\n * period runs at once rather than at its next retry, with nothing for the\n * wallet to sign, since it approved the plan when it subscribed. Answers\n * the subscription, still `unpaid`: the outcome comes with the pull, as\n * the `subscription.renewed` or `subscription.payment_failed` webhook.\n *\n * The merchant's hand retry from the dashboard follows the same rules: on\n * Free it is one of the missed period's three, whoever fires it; on Dev\n * and Business it runs beside the schedule, the wait between two doubling.\n * Mesub refuses it first when it cannot succeed, so it never spends a\n * retry for nothing: `apiCode` `insufficient_balance` (top up, then pay\n * again), `retry_cannot_succeed` (the wallet no longer approves it, its\n * token account is frozen, or its authority is closed), `retry_too_soon`\n * (`retryAfter` says when), `retries_spent`, `retry_deadline_passed`,\n * `subscription_not_late`, `subscription_paused`, `plan_ended`. Two more\n * are `retryable`: `subscription_changed` (two retries met, or a pull\n * moved the subscription on) and, on Free, `pull_running` (a pull runs\n * now). Read the subscription back before you ask again. A 503 is\n * `retryable` too, and nothing changed: `unavailable` (the charge could\n * not be queued) or `network_unavailable`, with `retryAfter` saying when\n * to ask again (20 s and 10 s). Sent once, never retried: a second one is\n * a second retry.\n */\n async retry(id: string, options: RequestOptions = {}): Promise<ServerSubscription> {\n return serverSubscriptionFrom(\n await this.#transport.post(`${pathOf(id)}/retry`, undefined, options),\n 'POST /v1/subscriptions/:id/retry',\n 202,\n );\n }\n\n async #build(action: Action, id: string, options: RequestOptions): Promise<WalletTransaction> {\n const path = `${pathOf(id)}/${action}`;\n\n // No body: the wallet is the subscription's own.\n return walletTransactionFrom(\n await this.#transport.post(path, undefined, options),\n `POST /v1/subscriptions/:id/${action}`,\n );\n }\n\n async #confirm(\n action: Action,\n id: string,\n params: ConfirmParams,\n options: ConfirmOptions,\n ): Promise<ConfirmResult> {\n const path = `${pathOf(id)}/${action}/confirm`;\n const result = confirmResultFrom(\n await this.#transport.post(\n path,\n { signature: params.signature },\n { ...options, timeout: options.timeout ?? CONFIRM_TIMEOUT },\n ),\n `POST /v1/subscriptions/:id/${action}/confirm`,\n );\n\n // A reason says nothing moved: only a cached no may be outdated then.\n await (result.reason === undefined ? this.#changed : this.#seen)(result.subscription);\n return result;\n }\n\n /**\n * One subscription of your project, by id. Found with `access`, it drops\n * the access answers cached that still say no, as `submit` does.\n */\n async retrieve(id: string, options: RequestOptions = {}): Promise<ServerSubscription> {\n const subscription = serverSubscriptionFrom(\n await this.#transport.get(pathOf(id), {}, options),\n );\n\n await this.#seen(subscription);\n return subscription;\n }\n\n /**\n * A customer's subscriptions, newest first, one page at a time; expired\n * checkouts included. `listAll` walks every page.\n *\n * The customer is named as for `access`, and normalised the same way;\n * none or two of `wallet`, `external_id` and `email` throws a TypeError.\n * Each one with `access` drops the access answers cached that still say\n * no, as `submit` does.\n */\n async list(params: ListParams, options: RequestOptions = {}): Promise<ServerSubscriptionList> {\n const { kind, value } = customerOf(params);\n const { plan, limit, starting_after } = params;\n const query: Record<string, QueryValue> = {\n [kind]: value,\n plan,\n limit,\n starting_after,\n };\n\n const page = serverSubscriptionListFrom(\n await this.#transport.get('/v1/subscriptions', query, options),\n );\n\n for (const subscription of page.data) await this.#seen(subscription);\n return page;\n }\n\n /**\n * Every subscription `list` would answer, page after page, for `for await`.\n * Each page is one call, made when the previous one is used up.\n */\n async *listAll(\n params: ListParams,\n options: RequestOptions = {},\n ): AsyncGenerator<ServerSubscription, void, undefined> {\n let page = await this.list(params, options);\n yield* page.data;\n\n while (page.has_more && page.data.length > 0) {\n page = await this.list({ ...params, starting_after: page.data.at(-1)!.id }, options);\n yield* page.data;\n }\n }\n\n /**\n * That subscription's own pull attempts, newest first, one page at a\n * time, and `paid`: what it paid since it began, whatever the page.\n * `allAttempts` walks every page.\n *\n * The id asked and no other: a subscription the wallet came back over\n * (`superseded`) keeps its own payments, and the one that replaced it\n * starts from its first pull. Needs a Mesub that serves the route\n * (Mesub-io/backend#289): from an older one it throws a `not_found`\n * whose `apiCode` is null, where an id it does not hold is a `not_found`\n * whose `apiCode` is `subscription_not_found`.\n */\n async attempts(\n id: string,\n params: AttemptsParams = {},\n options: RequestOptions = {},\n ): Promise<SubscriptionAttemptList> {\n const { limit, starting_after } = params;\n\n const path = `${pathOf(id)}/attempts`;\n\n try {\n return subscriptionAttemptListFrom(\n await this.#transport.get(path, { limit, starting_after }, options),\n );\n } catch (error) {\n if (!(error instanceof MesubError) || !routeMissing(error)) throw error;\n\n // Said for what it is, not as a wrong baseUrl.\n throw new MesubError(\n 'This Mesub does not serve GET /v1/subscriptions/:id/attempts yet.',\n {\n status: 404,\n code: 'not_found',\n body: error.body,\n requestId: error.requestId,\n cause: error,\n },\n );\n }\n }\n\n /**\n * Every attempt `attempts` would answer, page after page, for `for await`.\n * Each page is one call, made when the previous one is used up.\n */\n async *allAttempts(\n id: string,\n params: AttemptsParams = {},\n options: RequestOptions = {},\n ): AsyncGenerator<SubscriptionAttempt, void, undefined> {\n let page = await this.attempts(id, params, options);\n yield* page.data;\n\n while (page.has_more && page.data.length > 0) {\n page = await this.attempts(\n id,\n { ...params, starting_after: page.data.at(-1)!.id },\n options,\n );\n yield* page.data;\n }\n }\n}\n\n/** An id as Mesub writes them, and the only kind it reads (`RESOURCE_ID`). */\nconst ID = /^[A-Za-z0-9_-]{1,64}$/;\n\nfunction pathOf(id: string): string {\n // An empty id would reach the list route and read as a missing customer.\n if (typeof id !== 'string' || id === '') {\n throw new MesubError('A subscription id is required.', {\n status: null,\n code: 'invalid_request',\n });\n }\n // `.` and `..` survive encoding and the URL resolves them: `retrieve('.')` would\n // ask the list, `cancel('..')` another route. Refused before it is sent.\n if (!ID.test(id)) {\n throw new MesubError(\n 'A subscription id is letters, digits, hyphens and underscores, 64 at most.',\n { status: null, code: 'invalid_request' },\n );\n }\n return `/v1/subscriptions/${id}`;\n}\n\n/**\n * Mesub's own 404 for a route it does not have: `Cannot GET /v1/...`, never\n * a subscription's nor an event's.\n *\n * @internal\n */\nexport function routeMissing(error: MesubError): boolean {\n const { message } = (error.body ?? {}) as { message?: unknown };\n\n return (\n error.status === 404 &&\n error.apiCode === null &&\n typeof message === 'string' &&\n message.startsWith('Cannot GET /')\n );\n}\n\n/**\n * No answer that says what became of the submit: a timeout, a network error,\n * or a 5xx (a proxy's 502 or 504 among them) after Mesub may have co-signed.\n */\nfunction unanswered(error: MesubError): boolean {\n // Mesub-io/backend#505: too many submits waiting, nothing signed or sent.\n if (error.status === 503 && error.apiCode === 'unavailable') return false;\n\n return error.status === null || error.status >= 500;\n}\n","import { MesubError } from './errors.js';\nimport { routeMissing } from './subscriptions.js';\nimport type { RequestOptions, Transport } from './transport.js';\nimport { eventFrom, eventListFrom } from './validate.js';\nimport type { EventType, MesubEvent } from './webhooks.js';\n\n/**\n * Your project's subscription events (Mesub-io/backend#482): `GET /v1/events`\n * and `GET /v1/events/:id`, each event the very body its webhooks carry. The\n * types copy the back's `server-events.service.ts`, which stays the source\n * of truth. Snake case, as the API serves it.\n */\n\n/** One page of events, newest first. */\nexport interface EventList {\n data: MesubEvent[];\n /** Ask for the next page with `starting_after` set to the last id of this one. */\n has_more: boolean;\n /**\n * When Mesub began keeping events in this shape: events before it are\n * never served, whatever the window. Their webhooks carried the older\n * shape, with no event id.\n */\n available_since: string;\n}\n\n/** Which events, and which page of them. */\nexport interface EventListParams {\n /** Only events of this type. */\n type?: EventType;\n /** Only events about this subscription, by its id. */\n subscription?: string;\n /** 1 to 100, 25 by default. */\n limit?: number;\n /** The id of the last event of the previous page. */\n starting_after?: string;\n}\n\nexport class Events {\n readonly #transport: Transport;\n\n /** @internal */\n constructor(transport: Transport) {\n this.#transport = transport;\n }\n\n /**\n * Your project's events of the last 30 days, newest first, one page at a\n * time: each the very body its webhooks carried, the same `id`, `data`\n * and `detail`. Read it to catch up on what an endpoint missed while it\n * was down or turned off. Events before `available_since` are never\n * served, whatever the window.\n *\n * An event is listed a moment after its `created_at`, so one can appear\n * below one already read: when polling for what is new, page on until\n * `created_at` is a minute older than the newest you held, and skip the\n * ids you have seen.\n *\n * Needs a Mesub that serves the route (Mesub-io/backend#482): from an\n * older one it throws a `not_found` whose `apiCode` is null.\n */\n async list(params: EventListParams = {}, options: RequestOptions = {}): Promise<EventList> {\n const { type, subscription, limit, starting_after } = params;\n\n return eventListFrom(\n await this.#get('/v1/events', { type, subscription, limit, starting_after }, options),\n );\n }\n\n /**\n * One event, by the `id` its webhook body carries, as `list` serves it.\n * A `not_found` whose `apiCode` is `event_not_found` for an id older than\n * 30 days or than `available_since`, or of another project. A delivery's\n * `webhook-id` is not an event id: read `event.id`, not `delivery_id`.\n */\n async retrieve(id: string, options: RequestOptions = {}): Promise<MesubEvent> {\n return eventFrom(await this.#get(pathOf(id), {}, options));\n }\n\n async #get(\n path: string,\n query: Record<string, string | number | undefined>,\n options: RequestOptions,\n ): Promise<unknown> {\n try {\n return await this.#transport.get(path, query, options);\n } catch (error) {\n if (!(error instanceof MesubError) || !routeMissing(error)) throw error;\n\n // Said for what it is, not as a wrong baseUrl.\n throw new MesubError('This Mesub does not serve GET /v1/events yet.', {\n status: 404,\n code: 'not_found',\n body: error.body,\n requestId: error.requestId,\n cause: error,\n });\n }\n }\n}\n\n/** An id as Mesub writes them, and the only kind it reads. */\nconst ID = /^[A-Za-z0-9_-]{1,64}$/;\n\nfunction pathOf(id: string): string {\n // `.` and `..` survive encoding and the URL resolves them; an empty one\n // would ask the list. Refused before it is sent.\n if (typeof id !== 'string' || !ID.test(id)) {\n throw new MesubError(\n 'An event id is letters, digits, hyphens and underscores, 64 at most.',\n { status: null, code: 'invalid_request' },\n );\n }\n return `/v1/events/${id}`;\n}\n","import { MesubError } from './errors.js';\nimport type { RequestOptions, Transport } from './transport.js';\nimport { planFrom, planListFrom } from './validate.js';\n\n/** A plan a subscriber can hold: taking new ones (`active`), or ending (`sunset`). */\nexport type PlanStatus = 'active' | 'sunset';\n\n/**\n * What Mesub knows of a plan's token, resolved by its mint: never from the\n * merchant nor the client. A symbol is free text on chain and a fake \"USDC\"\n * is a known scam: show an unverified token as unverified, with its mint.\n */\nexport interface PlanToken {\n /** Null when Mesub found none, and on an unverified token named like a vouched stablecoin. */\n symbol: string | null;\n name: string | null;\n logo_url: string | null;\n /** True for a mint Mesub vouches for, or one Jupiter marks verified. */\n verified: boolean;\n}\n\n/** A plan of your project, as a pricing page needs it. */\nexport interface Plan {\n /** What `access`, the guards and `subscriptions.create` name it by. */\n slug: string;\n /** The plan's name, or its slug when none was given. */\n name: string;\n description: string | null;\n project_name: string;\n logo_url: string | null;\n /** The price in the token's base units, a string: a u64 does not survive a float. */\n amount: string;\n /** The price as a person counts it: `\"9.99\"`. */\n amount_display: string;\n /**\n * The plan's amount in dollars at the latest price Mesub knows, a decimal\n * string such as `\"9.99\"`: an estimate to show beside the amount, never\n * what is charged. Null when Mesub knows no price, and from a Mesub that\n * predates it.\n */\n amount_usd: string | null;\n decimals: number;\n /** Set only for a mint Mesub vouches for (USDC, USDT, PYUSD), null for any other. */\n symbol: string | null;\n /**\n * The token's symbol, name and logo for any mint, and whether it is\n * verified: when it is not, say so and keep `mint` visible. All null and\n * unverified from a Mesub that predates it.\n */\n token: PlanToken;\n mint: string;\n period_hours: number;\n network: 'devnet' | 'mainnet-beta';\n status: PlanStatus;\n /** Whether it takes new subscribers now: show \"Subscribe\" only when true. */\n available: boolean;\n /**\n * When the plan ends, null when it has no end: nobody has access past\n * it, and the last period before it is charged in full.\n */\n ends_at: string | null;\n}\n\n/** `mesub.plans`: the plans of your project, for a pricing page. */\nexport class Plans {\n readonly #transport: Transport;\n\n /** @internal */\n constructor(transport: Transport) {\n this.#transport = transport;\n }\n\n /**\n * Every plan of your project a subscriber can hold, sorted by slug: the\n * active ones, and the ones ending (`sunset`, `available: false`). Never\n * cached: call it where you render, and cache it your way.\n */\n async list(options: RequestOptions = {}): Promise<Plan[]> {\n return planListFrom(await this.#transport.get('/v1/plans', {}, options));\n }\n\n /** One plan by its slug. A slug your project lacks throws `plan_not_found`. */\n async retrieve(slug: string, options: RequestOptions = {}): Promise<Plan> {\n return planFrom(await this.#transport.get(pathOf(slug), {}, options));\n }\n}\n\n/**\n * A slug as Mesub writes them: lowercase letters, digits and single hyphens,\n * with at least one letter, so a slug never reads as a number.\n */\nconst SLUG = /^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nfunction pathOf(slug: string): string {\n // Anything else would reach another route, or the list: refused before it is sent.\n if (typeof slug !== 'string' || !SLUG.test(slug)) {\n throw new MesubError(\n 'A plan slug is lowercase letters, digits and hyphens, with at least one letter.',\n { status: null, code: 'invalid_request' },\n );\n }\n return `/v1/plans/${slug}`;\n}\n","import type { AccessAnswer, AccessList, AccessOptions, Customer } from './answer.js';\nimport { AccessCache } from './cache/access-cache.js';\nimport { MemoryStore } from './cache/memory-store.js';\nimport type { CacheStore } from './cache/store.js';\nimport { type Asked, customerOf } from './customer.js';\nimport { MesubError } from './errors.js';\nimport { Events } from './events.js';\nimport { apiKeyOf, baseUrlOf, headersOf, numberOf } from './options.js';\nimport { Plans } from './plans.js';\nimport { type ServerSubscription, Subscriptions } from './subscriptions.js';\nimport { type CallOptions, Transport } from './transport.js';\nimport { accessAnswerFrom, accessListFrom } from './validate.js';\nimport { webhookSecretOf, Webhooks } from './webhooks.js';\n\n/** What a guard decided, and on which answer. */\nexport interface Decision {\n access: boolean;\n answer: AccessAnswer | null;\n /** The answer came from the outage fallback, not from Mesub just now. */\n stale: boolean;\n /**\n * Mesub did not answer (an outage, a rate limit, or the guard's time\n * budget running out) and no answer was cached for that customer: nobody\n * knows yet, so the guards answer 503, not 402.\n */\n unavailable?: boolean;\n /**\n * With `unavailable`, the milliseconds Mesub's `Retry-After` asked for,\n * when it sent one: a guard's 503 hands it on (#132).\n */\n retryAfter?: number;\n}\n\nexport interface MesubOptions {\n /** API key. Defaults to `process.env.MESUB_API_KEY`. */\n apiKey?: string;\n /**\n * Defaults to `process.env.MESUB_BASE_URL`, then `https://api.mesub.io`.\n * May carry a path, e.g. behind a proxy at\n * `https://proxy.example.com/mesub`: every call is made under it.\n */\n baseUrl?: string;\n /**\n * Extra headers sent with every call to Mesub: e.g. a Cloudflare Access\n * service token for the proxy in front. Cannot set Authorization,\n * User-Agent, Accept, Content-Type nor Mesub-Version.\n */\n headers?: Record<string, string>;\n /** A custom `fetch`, e.g. one bound to your own agent. Defaults to the global one. */\n fetch?: typeof fetch;\n /** Per attempt, in milliseconds. Defaults to 5000. */\n timeout?: number;\n /**\n * How many times a failed call to Mesub is sent again, after the first\n * attempt. Defaults to 2. HTTP retries only: nothing to do with a plan's\n * pull retries, which Mesub runs on its side (and a Free plan has none).\n */\n maxRetries?: number;\n /**\n * Where `/v1/access` answers are kept. Defaults to a memory store of\n * 10,000 entries, emptied on restart; a Redis store keeps the outage\n * fallback across restarts and servers.\n */\n cache?: CacheStore<AccessAnswer | AccessList>;\n /**\n * How long an answer is kept once stale, for the outage fallback of\n * `hasAccess` and the guards, in milliseconds. Defaults to 24 hours. 0\n * turns the fallback off: an outage then keeps everyone out.\n */\n maxStaleMs?: number;\n /**\n * How long the guards (`requirePlan`, `withMesub`, `RequirePlan`) give\n * Mesub to answer, retries and waits included, in milliseconds. Defaults\n * to 2000. Once it runs out, or Mesub fails, the guard answers from the\n * cache, even stale, or 503 with Retry-After for a wallet it never saw. `access` and\n * `hasAccess`, called directly, are not bound by it.\n */\n guardTimeout?: number;\n /**\n * The webhook endpoint's signing secret, `whsec_...`, for\n * `webhooks.verify`. Defaults to `process.env.MESUB_WEBHOOK_SECRET`; none\n * is needed until a webhook is verified.\n */\n webhookSecret?: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.mesub.io';\nconst DEFAULT_GUARD_TIMEOUT = 2_000;\n\nexport class Mesub {\n /** Subscribing from your server: create, submit, retrieve and list. */\n readonly subscriptions: Subscriptions;\n /** The plans of your project, for a pricing page: `list` and `retrieve`. */\n readonly plans: Plans;\n /** Checking the webhooks Mesub sends: `verify`. */\n readonly webhooks: Webhooks;\n /** The events of the last 30 days, as their webhooks carried them: `list` and `retrieve`. */\n readonly events: Events;\n /** @internal */\n protected readonly transport: Transport;\n /** @internal */\n protected readonly cache: AccessCache<AccessAnswer>;\n /** The lists of `accessList`, in the same store, under their own keys. */\n private readonly lists: AccessCache<AccessList>;\n /** What the cache keys are scoped by: a hash of the API key, computed once. */\n private readonly cacheScope: () => Promise<string>;\n /** What hashes emails and external ids in the cache keys: the API key, imported once. */\n private readonly cacheSecret: () => Promise<HmacKey>;\n private readonly guardTimeout: number;\n /**\n * The `/v1/access` requests in flight, by cache key: a call for the same\n * answer waits for that one rather than send its own (#34).\n */\n private readonly flights = new Map<string, Promise<unknown>>();\n\n constructor(options: MesubOptions = {}) {\n // Every option is checked here, before anything is built: a TypeError\n // now rather than a 401 or a 1 ms timeout on every call.\n const apiKey = apiKeyOf(options.apiKey);\n const baseUrl = baseUrlOf(options.baseUrl, DEFAULT_BASE_URL);\n const headers = headersOf(options.headers);\n const webhookSecret = webhookSecretOf(options.webhookSecret);\n if (options.fetch !== undefined && typeof options.fetch !== 'function') {\n throw new TypeError('fetch must be a function.');\n }\n // Resolved per call so a fetch patched after construction is still used.\n const fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));\n // NaN or 0 would cut every call before it starts.\n this.guardTimeout = numberOf('guardTimeout', options.guardTimeout, DEFAULT_GUARD_TIMEOUT, {\n delay: true,\n });\n\n this.transport = new Transport({\n apiKey,\n baseUrl,\n headers,\n fetch,\n timeout: numberOf('timeout', options.timeout, 5_000, { delay: true }),\n maxRetries: numberOf('maxRetries', options.maxRetries, 2, {\n zero: true,\n integer: true,\n }),\n });\n this.subscriptions = new Subscriptions(\n this.transport,\n (subscription) => this.landed(subscription),\n (subscription) => this.landed(subscription, true),\n );\n this.plans = new Plans(this.transport);\n this.events = new Events(this.transport);\n // Every answer cached for its customer goes, the yes too: `stopped` and\n // `ended` take access away, and `hasAccess` must say so at once.\n this.webhooks = new Webhooks(webhookSecret, (event) => this.landed(event.data, true));\n const store = options.cache ?? new MemoryStore<AccessAnswer | AccessList>();\n const staleness =\n options.maxStaleMs === undefined\n ? {}\n : { maxStaleMs: numberOf('maxStaleMs', options.maxStaleMs, 0, { zero: true }) };\n // One store for both: their keys never meet (`mesub:access:` and\n // `mesub:access-list:`), so each reads back only what it wrote.\n this.cache = new AccessCache(store as CacheStore<AccessAnswer>, staleness);\n this.lists = new AccessCache(store as CacheStore<AccessList>, staleness);\n let cacheScope: Promise<string> | undefined;\n this.cacheScope = () => (cacheScope ??= hashScope(apiKey));\n let cacheSecret: Promise<HmacKey> | undefined;\n this.cacheSecret = () => (cacheSecret ??= importSecret(apiKey));\n }\n\n /**\n * Everything Mesub knows about that customer on that plan: whether they\n * have access, their status, their dates. Served from the cache while fresh.\n *\n * The customer is `{ wallet }`, `{ external_id }` or `{ email }`, or a\n * wallet as a string. Asked by external id or email, the answer is about\n * the wallet that grants access, if any: `wallet` is null when they have\n * nothing on that plan.\n *\n * Throws a `MesubError` whenever Mesub cannot answer: it is for screens.\n * A guard calls `hasAccess`, which falls back instead.\n */\n async access(\n customer: Customer | string,\n plan: string,\n options: AccessOptions = {},\n ): Promise<AccessAnswer> {\n const asked = customerOf(customer);\n const slug = planOf(plan);\n\n // Attempts skip the cache both ways: no key to compute.\n if (options.attempts === true) return this.ask(asked, slug, null, boundOf(options));\n\n const slot = await this.slotOf(asked);\n const cached = await this.cache.read(slot.who, slug, slot.scope);\n\n if (cached?.fresh) return cached.value;\n\n return this.ask(asked, slug, slot);\n }\n\n /**\n * Every plan of the project that customer has anything on, each answered\n * like `access`, for a page listing their entitlements in one call. Cached\n * apart from the answers by plan, for its own `revalidate_after`.\n *\n * Throws a `MesubError` whenever Mesub cannot answer, like `access`.\n */\n async accessList(\n customer: Customer | string,\n options: AccessOptions = {},\n ): Promise<AccessList> {\n const asked = customerOf(customer);\n const attempts = options.attempts === true;\n // Attempts skip the cache both ways, as with `access`.\n const slot = attempts ? null : await this.slotOf(asked);\n\n if (slot) {\n const cached = await this.lists.read(slot.who, null, slot.scope);\n\n if (cached?.fresh) return cached.value;\n }\n\n // Checked like `access`'s answer, before it reaches the cache.\n const request = async () =>\n accessListFrom(\n await this.transport.get('/v1/access', {\n [asked.kind]: asked.value,\n attempts: attempts || undefined,\n }),\n );\n\n if (!slot) return request();\n\n return this.shared(flightKey('call', slot, null), request, (list) =>\n this.lists.write(slot.who, null, list, slot.scope),\n );\n }\n\n /**\n * `access` from Mesub, cached in the slot its caller computed once and\n * already read, or without the cache at all (`null`) for an answer with\n * its attempts.\n */\n private async ask(\n asked: Asked,\n plan: string,\n slot: Slot | null,\n call: CallOptions = {},\n ): Promise<AccessAnswer> {\n const attempts = slot === null;\n\n // Throws a MesubError on any failure, which goes straight to the caller;\n // an answer of the wrong shape too, before it reaches the cache.\n const request = async () =>\n accessAnswerFrom(\n await this.transport.get(\n '/v1/access',\n {\n [asked.kind]: asked.value,\n plan,\n // Left out when not asked: the transport drops undefined values.\n attempts: attempts || undefined,\n },\n call,\n ),\n );\n\n // A heavy answer must not replace the light one a guard reads.\n if (!slot) return request();\n\n // A guard's request is cut at its deadline and never retries a 429:\n // shared between guards only, so a call from your code keeps its\n // retries, and a guard never waits past its budget behind one.\n const mode = call.deadline === undefined ? 'call' : 'guard';\n\n return this.shared(flightKey(mode, slot, plan), request, (answer) =>\n this.cache.write(slot.who, plan, answer, slot.scope),\n );\n }\n\n /**\n * One request in flight per key (#34): fifty calls for a customer not in\n * the cache made fifty requests to Mesub, and as many 429s to retry. The\n * first sends it, the others wait for its answer or its error. Its answer\n * is cached once, unless a subscription that landed dropped the flight\n * meanwhile: then it is answered, not cached.\n */\n private shared<T>(\n key: string,\n request: () => Promise<T>,\n keep: (value: T) => Promise<void>,\n ): Promise<T> {\n const pending = this.flights.get(key);\n\n if (pending) return pending as Promise<T>;\n\n // Not awaited: a store slow to write never holds the answer (#132).\n // `keep` never throws, a store that fails being dropped.\n const flight: Promise<T> = request().then((value) => {\n if (this.flights.get(key) === flight) void keep(value);\n return value;\n });\n const land = () => {\n if (this.flights.get(key) === flight) this.flights.delete(key);\n };\n\n this.flights.set(key, flight);\n void flight.then(land, land);\n\n return flight;\n }\n\n /**\n * Where a customer's answers are cached: the project's scope, and who\n * they are as the key reads it. A wallet is public on chain and stays\n * readable; an email or an external id is never written in clear, only\n * as an HMAC under the API key, which a leaked Redis alone cannot reverse\n * by guessing.\n */\n private async slotOf(asked: Asked): Promise<Slot> {\n const scope = await this.cacheScope();\n\n if (asked.kind === 'wallet') return { scope, who: `wallet:${asked.value}` };\n\n const mac = await crypto.subtle.sign(\n 'HMAC',\n await this.cacheSecret(),\n new TextEncoder().encode(`${asked.kind}:${asked.value}`),\n );\n\n return { scope, who: `${asked.kind}:${hex(mac)}` };\n }\n\n /**\n * A subscription `submit`, `retrieve` or `list` answered (#33): once it\n * grants access (`active`, or `cancelled` before its end), the answers\n * cached for its customer that say no are dropped, by wallet, external\n * id and email, for its plan and in the list of every plan. Without it, a\n * `hasAccess` right after a submit kept answering the cached no until\n * `revalidate_after` ran out. A store that fails costs nothing more than\n * that: it never throws.\n *\n * With `moved`, one a confirm just cancelled, resumed or closed, or one a\n * verified webhook carried: every answer cached for its customer is\n * dropped, the yes too, since its status and dates changed whatever its\n * access. A `stopped` or `ended` event must not leave a yes behind.\n */\n private async landed(subscription: ServerSubscription, moved = false): Promise<void> {\n const { plan } = subscription;\n\n if (!moved && !subscription.access) return;\n\n try {\n for (const asked of customersOf(subscription)) {\n const slot = await this.slotOf(asked);\n\n // A request sent before it landed may answer the old no: its\n // callers get it, the cache does not, and the next call asks again.\n for (const mode of ['call', 'guard'] as const) {\n if (plan !== null) this.flights.delete(flightKey(mode, slot, plan));\n this.flights.delete(flightKey(mode, slot, null));\n }\n\n if (plan !== null) {\n await this.cache.forget(\n slot.who,\n plan,\n slot.scope,\n (answer) => moved || !answer.access,\n );\n }\n await this.lists.forget(\n slot.who,\n null,\n slot.scope,\n (list) =>\n moved ||\n !list.plans.some((answer) => answer.plan === plan && answer.access),\n );\n }\n } catch {\n // The answers stay cached until their revalidate_after, as before.\n }\n }\n\n /**\n * Whether that customer has access to that plan, for a guard. The\n * customer is the same as for `access`, a wallet string included.\n *\n * When Mesub is unavailable after the retries (`unavailable` or\n * `rate_limited`), it answers the last answer it knew, even stale, and\n * `false` for a customer it never saw. Any other error is thrown, never\n * turned into `false`: a bad key or an unknown plan is a broken\n * integration, not a denial.\n *\n * No time budget: it waits out the client's timeout and retries, and a\n * 429's `Retry-After`. A guard answers within `guardTimeout` instead.\n */\n async hasAccess(customer: Customer | string, plan: string): Promise<boolean> {\n return (await this.decideBy(customer, plan)).access;\n }\n\n /**\n * `hasAccess`, with the answer it decided on, within `guardTimeout`: what\n * the middlewares hand the route. `answer` is null only for a customer\n * never seen during an outage, which is then `unavailable`; `stale` is\n * true when the answer came from the fallback.\n *\n * @internal\n */\n async decide(customer: Customer | string, plan: string): Promise<Decision> {\n return this.decideBy(customer, plan, Date.now() + this.guardTimeout);\n }\n\n /**\n * The decision itself. With a deadline (the guards), every call to Mesub\n * fits before it: attempts are cut at it and a Retry-After that would\n * outlast it is not waited. Without one (`hasAccess` called directly),\n * the client's timeout and retries, as configured.\n */\n private async decideBy(\n customer: Customer | string,\n plan: string,\n deadline?: number,\n ): Promise<Decision> {\n // A guard does not wait out a rate limit: it falls back at once.\n const call: CallOptions =\n deadline === undefined ? {} : { deadline, retryRateLimited: false };\n // A malformed customer or a missing plan is thrown before anything is asked.\n const asked = customerOf(customer);\n const slug = planOf(plan);\n const slot = await this.slotOf(asked);\n // Read once, for both: a fresh answer is served as is, a stale one is\n // the fallback. A guard's store that has not answered by the deadline\n // is a miss, as is one that fails (#132). Not read again after Mesub\n // fails: its call runs to the deadline, so a second read would always\n // be a miss. An answer a webhook drops during that call can still be\n // the fallback, at most `guardTimeout` after it was dropped: the same\n // window as a request in flight when it landed (`landed`).\n const cached = await this.cache.read(slot.who, slug, slot.scope, deadline);\n\n if (cached?.fresh) {\n return { access: cached.value.access, answer: cached.value, stale: false };\n }\n\n try {\n const answer = await this.ask(asked, slug, slot, call);\n\n return { access: answer.access, answer, stale: false };\n } catch (error) {\n const unreachable =\n error instanceof MesubError &&\n (error.code === 'unavailable' || error.code === 'rate_limited');\n\n // A broken integration is thrown, never read as \"no access\".\n if (!unreachable) throw error;\n\n // The last answer known, even stale; a customer never seen stays out.\n if (cached) {\n return { access: stillGrants(cached.value), answer: cached.value, stale: true };\n }\n\n // Not Mesub saying no: nobody knows, so a guard asks for a retry\n // later, when Mesub said when.\n return {\n access: false,\n answer: null,\n stale: true,\n unavailable: true,\n ...(error.retryAfter !== null && { retryAfter: error.retryAfter }),\n };\n }\n }\n}\n\ntype HmacKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\n/**\n * The deadline and the 429 rule the widget routes pass with `attempts`, as a\n * guard's call: read here, never named by `AccessOptions`.\n */\nfunction boundOf(options: AccessOptions): CallOptions {\n const { deadline, retryRateLimited } = options as CallOptions;\n\n return {\n ...(deadline !== undefined && { deadline }),\n ...(retryRateLimited !== undefined && { retryRateLimited }),\n };\n}\n\n/** Where a customer's answers are cached: see `slotOf`. */\ninterface Slot {\n scope: string;\n who: string;\n}\n\n/**\n * What a request in flight is shared under: its cache key, and whether a\n * guard sent it. The key holds the project's scope, the kind of identifier\n * and the plan, or the list's own prefix, so two of them never meet.\n */\nfunction flightKey(mode: 'call' | 'guard', slot: Slot, plan: string | null): string {\n return `${mode}|${AccessCache.key(slot.who, plan, slot.scope)}`;\n}\n\n/** Every way a subscription's customer can be asked about: its wallet, external id and email. */\nfunction customersOf(subscription: ServerSubscription): Asked[] {\n const { wallet, external_id, email } = subscription;\n const customers: Customer[] = [{ wallet }];\n\n if (external_id) customers.push({ external_id });\n if (email) customers.push({ email });\n\n return customers.map(customerOf);\n}\n\n/**\n * The plan, which `access`, `hasAccess` and the guards cannot do without.\n * Without it Mesub answers the list of every plan instead: read as one\n * answer, it would be cached under `undefined` and its missing `access` taken\n * for a no. The list is `accessList`.\n */\nfunction planOf(plan: unknown): string {\n if (typeof plan !== 'string' || plan === '') {\n throw new TypeError('A plan is needed: accessList(customer) lists every plan instead.');\n }\n\n return plan;\n}\n\n/**\n * `key-` and 16 hex characters of the API key's SHA-256: a scope, not a way\n * back to the key. Two projects sharing one store never share a key, and it\n * needs no call to Mesub, so it is the same after a restart in an outage. A\n * rotated key starts a new scope; the old entries expire on their TTL.\n */\nasync function hashScope(apiKey: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(apiKey));\n\n return `key-${hex(digest).slice(0, 16)}`;\n}\n\n/** The API key as an HMAC key, for the hashes of emails and external ids. */\nfunction importSecret(apiKey: string): Promise<HmacKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(apiKey),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n}\n\nfunction hex(bytes: ArrayBuffer): string {\n return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * A stale answer's access, past what it paid for (#35): an answer with no\n * charge or retry ahead (cancelled, parked) ends at `access_until`. One with a\n * renewal ahead keeps the fallback: it was likely paid while Mesub was down.\n * A blocked one (`blocked_since`) ends there too, whatever is ahead: Mesub\n * keeps retrying it, which says nothing of a payment, and never grants it\n * past `access_until` (#132).\n */\nfunction stillGrants(answer: AccessAnswer, now: number = Date.now()): boolean {\n if (!answer.access || !answer.access_until) return answer.access;\n\n // Truthy, not non-null: an entry an older release wrote to a shared store may lack them.\n const renewing = Boolean(answer.next_charge_at || answer.next_retry_at);\n\n if (renewing && !answer.blocked_since) return true;\n return Date.parse(answer.access_until) > now;\n}\n"],"mappings":"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","names":["kind","error","parsed","retryAfter","pathOf","ID","pathOf"]}
1
+ {"version":3,"sources":["../src/cache/access-cache.ts","../src/version.ts","../src/options.ts","../src/cache/memory-store.ts","../src/customer.ts","../src/errors.ts","../src/validate.ts","../src/webhooks.ts","../src/transport.ts","../src/subscriptions.ts","../src/events.ts","../src/plans.ts","../src/client.ts"],"sourcesContent":["import type { CacheEntry, CacheStore } from './store.js';\n\n/** How long a stale answer is kept for the outage fallback, then dropped. */\nexport const DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1000;\n\n/** The one field the cache reads from an answer: seconds it stays true. */\nexport interface Revalidating {\n revalidate_after: number;\n}\n\nexport interface AccessCacheOptions {\n maxStaleMs?: number;\n now?: () => number;\n}\n\n/** What a read found: the answer, and whether it can be served without calling Mesub. */\nexport interface CachedAnswer<T> {\n value: T;\n fresh: boolean;\n}\n\n/**\n * The `/v1/access` answers, by customer and plan, over any store.\n *\n * A store that fails never breaks a request: a read that throws is a miss,\n * a write that throws is dropped. A merchant whose Redis is down still gets\n * answers, straight from Mesub. Nor does one that hangs hold a guard: given a\n * deadline, a read not answered by then is a miss too (#132).\n */\nexport class AccessCache<T extends Revalidating> {\n private readonly maxStaleMs: number;\n private readonly now: () => number;\n\n constructor(\n private readonly store: CacheStore<T>,\n options: AccessCacheOptions = {},\n ) {\n this.maxStaleMs = options.maxStaleMs ?? DEFAULT_MAX_STALE_MS;\n this.now = options.now ?? Date.now;\n }\n\n /**\n * `mesub:access:<scope>:<plan>:<who>`, prefixed so it can share a Redis\n * with anything. The scope (the client passes a hash of its API key) keeps\n * two projects sharing one store apart; without one, `mesub:access:<plan>:<who>`.\n *\n * `who` is whatever names the customer, the client's being\n * `<kind>:<wallet or hash>`. A null plan is the list of every plan they\n * have, under its own prefix, `mesub:access-list:`: no plan slug can reach it.\n */\n static key(who: string, plan: string | null, scope?: string): string {\n const prefix = plan === null ? 'mesub:access-list' : 'mesub:access';\n const path = plan === null ? who : `${plan}:${who}`;\n\n return scope === undefined ? `${prefix}:${path}` : `${prefix}:${scope}:${path}`;\n }\n\n /** With a `deadline` (epoch ms), a store that has not answered by then is a miss. */\n async read(\n who: string,\n plan: string | null,\n scope?: string,\n deadline?: number,\n ): Promise<CachedAnswer<T> | undefined> {\n let entry: CacheEntry<T> | undefined;\n\n try {\n const pending = this.store.get(AccessCache.key(who, plan, scope));\n\n entry = deadline === undefined ? await pending : await this.within(pending, deadline);\n } catch {\n return undefined;\n }\n\n const now = this.now();\n\n // Checked here too: a store like Redis may keep a key a little longer.\n if (!entry || now > entry.keepUntil) return undefined;\n\n return { value: entry.value, fresh: now < entry.freshUntil };\n }\n\n /** What `pending` gives by `deadline`, or undefined, as a miss, when it has not answered. */\n private async within<V>(pending: V | Promise<V>, deadline: number): Promise<V | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const late = new Promise<undefined>((resolve) => {\n timer = setTimeout(resolve, Math.max(0, deadline - this.now()), undefined);\n });\n\n try {\n // Promise.race handles a late rejection of `pending`: never unhandled.\n return await Promise.race([pending, late]);\n } finally {\n clearTimeout(timer);\n }\n }\n\n async write(who: string, plan: string | null, value: T, scope?: string): Promise<void> {\n // NaN or Infinity would make an entry that never goes stale, and a TTL\n // Redis refuses. The client checks every answer first; this is the floor.\n if (!Number.isFinite(value.revalidate_after)) return;\n\n const now = this.now();\n const freshUntil = now + Math.max(0, value.revalidate_after) * 1000;\n const keepUntil = freshUntil + this.maxStaleMs;\n\n try {\n await this.store.set(\n AccessCache.key(who, plan, scope),\n { value, freshUntil, keepUntil },\n keepUntil - now,\n );\n } catch {\n // A store that cannot write costs a cached answer, never the request.\n }\n }\n\n /**\n * Drops the cached answer when `outdated` says it no longer holds, so the\n * next call asks Mesub. A store without `delete` gets it back as stale\n * instead: never served as is again, only as the outage fallback.\n */\n async forget(\n who: string,\n plan: string | null,\n scope: string | undefined,\n outdated: (value: T) => boolean,\n ): Promise<void> {\n const key = AccessCache.key(who, plan, scope);\n\n try {\n const entry = await this.store.get(key);\n\n if (!entry || !outdated(entry.value)) return;\n if (this.store.delete) {\n await this.store.delete(key);\n return;\n }\n\n const now = this.now();\n\n if (now < entry.keepUntil) {\n await this.store.set(key, { ...entry, freshUntil: now }, entry.keepUntil - now);\n }\n } catch {\n // A store that fails costs a fresh answer, never the request.\n }\n }\n}\n","/** Kept in step with package.json by `test/version.spec.ts`. */\nexport const VERSION = '0.1.5';\n\n/**\n * The version of the Mesub API this release was written against, sent in\n * the `Mesub-Version` header of every call, so the API can change its\n * answers for newer releases without breaking this one. Moved only with a\n * release, never by a merchant.\n */\nexport const API_VERSION = '2026-10-02';\n\n/** The header `API_VERSION` is sent in. */\nexport const API_VERSION_HEADER = 'Mesub-Version';\n","import { API_VERSION_HEADER } from './version.js';\n\n/**\n * The client's options, checked once at `new Mesub()`: a wrong one throws a\n * TypeError there, never a 401 or a 1 ms timeout on every call later.\n */\n\n/** The longest delay `setTimeout` keeps: beyond it, it fires at once. */\nconst MAX_DELAY = 2_147_483_647;\n\n/** Hosts reached over plain http without sending the key across a network. */\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\n/**\n * The API key: the one given, else `MESUB_API_KEY`. Refuses none. A wrong one\n * is not guessed at here: Mesub answers it 401, thrown as `unauthorized`.\n */\nexport function apiKeyOf(given: unknown): string {\n if (given !== undefined && typeof given !== 'string') {\n throw new TypeError('apiKey must be a string.');\n }\n\n const apiKey = given || envKey();\n\n if (!apiKey) {\n throw new TypeError(\n typeof process === 'undefined'\n ? 'Missing Mesub API key: there is no process.env here (an edge runtime such as ' +\n 'Cloudflare Workers), so pass it: `new Mesub({ apiKey: env.MESUB_API_KEY })`.'\n : 'Missing Mesub API key: pass `new Mesub({ apiKey })` or set MESUB_API_KEY.',\n );\n }\n\n return apiKey;\n}\n\n/** `MESUB_API_KEY`, where there is a `process.env` to read it from. */\nfunction envKey(): string | undefined {\n return typeof process === 'undefined' ? undefined : process.env?.['MESUB_API_KEY'];\n}\n\n/**\n * The base URL, without its trailing slashes: the one given, else\n * `MESUB_BASE_URL`, else `fallback`. https, or http to this machine only,\n * since every call carries the API key.\n */\nexport function baseUrlOf(given: unknown, fallback: string): string {\n if (given !== undefined) return checkedBaseUrl('baseUrl', given);\n\n const fromEnv = envBaseUrl();\n\n return fromEnv ? checkedBaseUrl('MESUB_BASE_URL', fromEnv) : fallback;\n}\n\n/** `MESUB_BASE_URL`, where there is a `process.env` to read it from. */\nfunction envBaseUrl(): string | undefined {\n return typeof process === 'undefined' ? undefined : process.env?.['MESUB_BASE_URL'];\n}\n\n/** `given` checked as a base URL, the errors naming where it came from. */\nfunction checkedBaseUrl(name: string, given: unknown): string {\n if (typeof given !== 'string') throw new TypeError(`${name} must be a string.`);\n\n let url: URL;\n try {\n url = new URL(given);\n } catch {\n throw new TypeError(`${name} must be an absolute URL, not ${JSON.stringify(given)}.`);\n }\n\n // Every path is appended to it: a query or a fragment would swallow them.\n if (url.search !== '' || url.hash !== '' || given.includes('?') || given.includes('#')) {\n throw new TypeError(\n `${name} must have no query nor fragment, not ${JSON.stringify(given)}.`,\n );\n }\n\n const local = url.protocol === 'http:' && LOOPBACK.has(url.hostname);\n if (url.protocol !== 'https:' && !local) {\n throw new TypeError(\n `${name} must be https (http only to localhost or 127.0.0.1), not ${url.protocol}//${url.host}: ` +\n 'every call carries the API key.',\n );\n }\n\n return given.replace(/\\/+$/, '');\n}\n\n/**\n * A number of milliseconds or of retries, when given: finite, and positive,\n * or at least 0 when 0 means something (no retry, no fallback).\n */\nexport function numberOf(\n name: string,\n given: unknown,\n fallback: number,\n rule: { zero?: boolean; integer?: boolean; delay?: boolean } = {},\n): number {\n if (given === undefined) return fallback;\n\n const ok =\n typeof given === 'number' &&\n Number.isFinite(given) &&\n (rule.zero ? given >= 0 : given > 0) &&\n (!rule.integer || Number.isInteger(given)) &&\n (!rule.delay || given <= MAX_DELAY);\n\n if (!ok) {\n const what = rule.integer ? 'an integer' : 'a number of milliseconds';\n const range = rule.zero ? '0 or more' : 'above 0';\n const cap = rule.delay ? `, ${MAX_DELAY} at most` : '';\n const shown = typeof given === 'string' ? JSON.stringify(given) : String(given);\n throw new TypeError(`${name} must be ${what}, ${range}${cap}, not ${shown}.`);\n }\n\n return given;\n}\n\n/** Headers the SDK sets itself, and no extra header may replace. */\nconst OWN_HEADERS = new Set([\n 'authorization',\n 'user-agent',\n 'accept',\n 'content-type',\n API_VERSION_HEADER.toLowerCase(),\n]);\n\n/**\n * Extra headers for every call: for a proxy or an access gateway in front of\n * Mesub. Valid names and values only, and none of the\n * SDK's own, so the API key can never be swapped for another.\n */\nexport function headersOf(given: unknown): Record<string, string> {\n if (given === undefined) return {};\n if (typeof given !== 'object' || given === null || Array.isArray(given)) {\n throw new TypeError('headers must be an object of header names to string values.');\n }\n\n const headers: Record<string, string> = {};\n\n for (const [name, value] of Object.entries(given)) {\n if (typeof value !== 'string') {\n throw new TypeError(`headers.${name} must be a string.`);\n }\n if (OWN_HEADERS.has(name.toLowerCase())) {\n throw new TypeError(`headers cannot set ${name}: the SDK sets it itself.`);\n }\n try {\n new Headers([[name, value]]);\n } catch {\n throw new TypeError(`headers.${name} is not a valid header name and value.`);\n }\n headers[name] = value;\n }\n\n return headers;\n}\n","import { numberOf } from '../options.js';\nimport type { CacheEntry, CacheStore } from './store.js';\n\n/** How many answers the memory store keeps before dropping the least recently used. */\nexport const DEFAULT_MAX_ENTRIES = 10_000;\n\nexport interface MemoryStoreOptions {\n /** A whole number above 0: 10 000 by default. */\n maxEntries?: number;\n /** The clock, replaced in tests. */\n now?: () => number;\n}\n\n/**\n * The default store: an LRU in a `Map`, which keeps insertion order. Reading\n * an entry moves it to the end, so the first key is always the least recently\n * used one. It lives in this process only: a restart empties it.\n */\nexport class MemoryStore<T = unknown> implements CacheStore<T> {\n private readonly entries = new Map<string, CacheEntry<T>>();\n private readonly maxEntries: number;\n private readonly now: () => number;\n\n constructor(options: MemoryStoreOptions = {}) {\n // NaN would never drop an entry: the store would grow without bound.\n this.maxEntries = numberOf('maxEntries', options.maxEntries, DEFAULT_MAX_ENTRIES, {\n integer: true,\n });\n this.now = options.now ?? Date.now;\n }\n\n /** How many entries are held, expired ones included until they are read. */\n get size(): number {\n return this.entries.size;\n }\n\n get(key: string): CacheEntry<T> | undefined {\n const result = this.entries.get(key);\n if (!result) return undefined;\n if (result.keepUntil < this.now()) {\n this.entries.delete(key);\n return undefined;\n }\n // Deleted then set again: the Map moves it to the end, the most recently used place.\n this.entries.delete(key);\n this.entries.set(key, result);\n\n return result;\n }\n\n set(key: string, entry: CacheEntry<T>, _ttlMs: number): void {\n // `_ttlMs` stays unread: `keepUntil` in the entry already says it.\n this.entries.delete(key);\n this.entries.set(key, entry);\n\n // The first key of the Map is the least recently used one.\n while (this.entries.size > this.maxEntries) {\n const oldest = this.entries.keys().next().value;\n\n if (oldest === undefined) break;\n this.entries.delete(oldest);\n }\n }\n\n delete(key: string): void {\n this.entries.delete(key);\n }\n}\n","import type { Customer } from './answer.js';\n\n/** Who a call is about, normalised as Mesub reads it. */\nexport interface Asked {\n kind: 'wallet' | 'external_id' | 'email';\n value: string;\n}\n\nconst CUSTOMER_KINDS = ['wallet', 'external_id', 'email'] as const;\n\n/**\n * The one customer a call names, normalised the way Mesub normalises the\n * query (#157), so `Ada@Example.com ` and `ada@example.com` share an answer and\n * a cache key: an email trimmed and lowercased, an external id trimmed. A\n * wallet is left as given: Mesub refuses one that is not an address.\n *\n * None or two of them, or one that is not a string, throws a TypeError: a\n * broken integration, never a question to send. What the value holds (an\n * email that is not one, an external id too long) is Mesub's to refuse, with\n * a MesubError `invalid_request`.\n */\nexport function customerOf(customer: Customer | string): Asked {\n if (typeof customer === 'string') return { kind: 'wallet', value: customer };\n\n const named = CUSTOMER_KINDS.filter(\n (kind) => (customer as Record<string, unknown> | null)?.[kind] !== undefined,\n );\n const kind = named[0];\n const value: unknown = kind && (customer as Record<string, unknown>)[kind];\n\n if (named.length !== 1 || !kind || typeof value !== 'string') {\n throw new TypeError('Ask about exactly one of wallet, external_id or email, as a string.');\n }\n\n if (kind === 'email') return { kind, value: value.trim().toLowerCase() };\n if (kind === 'external_id') return { kind, value: value.trim() };\n\n return { kind, value };\n}\n","import type { ServerSubscription } from './subscriptions.js';\n\n/**\n * Stable codes to branch on, one per kind of failure; `apiCode` tells the\n * causes within one apart. `unexpected` covers any status without its own\n * code (408, 413, a 2xx whose body is not JSON or not of the shape the SDK\n * reads, a 404 whose body is not a Mesub error, so a wrong `baseUrl`, ...).\n * `invalid_webhook` is a webhook that fails verification: a header missing,\n * no signature matching the secret, a timestamp too far from now.\n */\nexport type MesubErrorCode =\n | 'invalid_request'\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'plan_not_found'\n | 'conflict'\n | 'rate_limited'\n | 'unavailable'\n | 'invalid_webhook'\n | 'unexpected';\n\nexport interface MesubErrorOptions {\n status: number | null;\n code: MesubErrorCode;\n /** The back's own code, from the error body. Defaults to null. */\n apiCode?: string | null;\n /** Defaults to true for `unavailable` and `rate_limited`, false otherwise. */\n retryable?: boolean;\n /** What Mesub answered, when it answered. */\n body?: unknown;\n /** Milliseconds, from the response's `Retry-After`. Defaults to null. */\n retryAfter?: number | null;\n /** The response's `Mesub-Request-Id`, or the body's `request_id`. Defaults to null. */\n requestId?: string | null;\n cause?: unknown;\n}\n\n/** Every failure of a call to the Mesub API, after retries. */\nexport class MesubError extends Error {\n override readonly name = 'MesubError';\n /**\n * The HTTP status, or `null` when no response came back (network error,\n * timeout). On the copy an Express middleware hands to `next(err)` for a\n * 4xx of Mesub's, 500: Mesub's own is on `cause`.\n */\n readonly status: number | null;\n readonly code: MesubErrorCode;\n /**\n * The code Mesub's error body names (`subscription_not_found`,\n * `pending_cap_reached`, ...), finer than `code` and as stable: never\n * renamed nor reused, though new ones get added. Null when no Mesub error\n * came back: a network error, a timeout, a body that is not Mesub's.\n */\n readonly apiCode: string | null;\n /**\n * Whether the same call, sent again unchanged, may succeed later without\n * anybody doing anything: Mesub's own `retryable` when it sent one. Not\n * \"at once\": the SDK has already retried what it retries.\n */\n readonly retryable: boolean;\n /**\n * The body Mesub answered, parsed when it is JSON, the text otherwise;\n * undefined when no response came back.\n */\n readonly body: unknown;\n /**\n * How long Mesub asked to wait before the same call is sent again, in\n * milliseconds, from the response's `Retry-After`, as sent. It comes on\n * a 429 (`rate_limited`, or `pending_cap_reached` on create: a quota,\n * not a rate, and the wait may be most of an hour), on every 503 of a\n * route that builds, sends or queues (`network_unavailable`, about 10 s;\n * `unavailable` on `submit`, about 2 s, and on `retry`, about 20 s),\n * and on two 409s:\n * `retry_too_soon` on `retry`, and `comeback_period_rolling` on\n * `create`, `transaction` and `submit`. Null when the response had none,\n * or none came back.\n */\n readonly retryAfter: number | null;\n /**\n * The id Mesub gave the request (`req_` and 24 letters or digits), from\n * the response's `Mesub-Request-Id` header, or the error body's\n * `request_id` when a proxy dropped the header (Mesub-io/backend#472).\n * Log it, and quote it to Mesub support: it finds the request in Mesub's\n * logs. Null when no response came back, or one that is not Mesub's.\n */\n readonly requestId: string | null;\n\n constructor(message: string, options: MesubErrorOptions) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.status = options.status;\n this.code = options.code;\n this.apiCode = options.apiCode ?? null;\n this.retryable =\n options.retryable ??\n (options.code === 'unavailable' || options.code === 'rate_limited');\n this.body = options.body;\n this.retryAfter = options.retryAfter ?? null;\n this.requestId = options.requestId ?? null;\n }\n}\n\n/** What a `MesubSubmitError` carries beyond a `MesubError`'s. */\nexport interface MesubSubmitErrorOptions extends MesubErrorOptions {\n subscription: ServerSubscription | null;\n sends: number;\n}\n\n/**\n * A submit whose outcome Mesub never told, thrown with the subscription read\n * back. The wallet may have paid: `subscription` is never `active` nor\n * `cancelled` (those are returned), and null when that read failed too. Read\n * it again with `retrieve` before creating anew; a `pending` one may still\n * land, and Mesub settles it on its own within the hour. Three cases:\n *\n * - no send got an answer that says (a timeout, a network error, a 5xx,\n * after the replays): `code` is `unavailable` and `retryable` true, since\n * the same submit, sent again with the same transaction and terms\n * signature, co-signs nothing more and is answered from the chain\n * (Mesub-io/backend#190). `status`, `apiCode`, `body`, `retryAfter` and `requestId` are\n * those of the last send that got a response (a 503, a 429), or all null\n * when none did (timeouts, network errors);\n * - a replay refused with `not_awaiting_signature` after a send that got no\n * answer: the row moved on (`failed`, `expired`), and the error is that\n * refusal's, `conflict`, not retryable;\n * - a 2xx this SDK cannot read: `unexpected`, not retryable, its fields.\n *\n * `cause` is always the last send's own error.\n */\nexport class MesubSubmitError extends MesubError {\n /** The subscription as read back after the last send, or null if that read failed. */\n readonly subscription: ServerSubscription | null;\n /** How many times the submit was sent, the same request each time. */\n readonly sends: number;\n\n constructor(message: string, options: MesubSubmitErrorOptions) {\n super(message, options);\n this.subscription = options.subscription;\n this.sends = options.sends;\n }\n}\n\n/**\n * The SDK's code for an error Mesub answered: its status, but for\n * `plan_not_found`, which keeps its own code from before the back had codes.\n * Every other 404 (`subscription_not_found`, a route Mesub does not have)\n * is `not_found`.\n */\nexport function codeFor(status: number, apiCode: string | null): MesubErrorCode {\n if (status === 404 && apiCode === 'plan_not_found') return 'plan_not_found';\n if (status === 400) return 'invalid_request';\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not_found';\n if (status === 409) return 'conflict';\n if (status === 429) return 'rate_limited';\n if (status >= 500) return 'unavailable';\n return 'unexpected';\n}\n","import type { AccessAnswer, AccessList, ServedAttempt } from './answer.js';\nimport type { EventList } from './events.js';\nimport { MesubError } from './errors.js';\nimport type { Plan, PlanToken } from './plans.js';\nimport type {\n ConfirmResult,\n ServerSubscription,\n ServerSubscriptionList,\n SubmitResult,\n SubscribeCosts,\n SubscribeTransaction,\n SubscriptionAttempt,\n SubscriptionAttemptList,\n WalletTransaction,\n} from './subscriptions.js';\nimport type {\n AccessChangedDetail,\n CreatedDetail,\n EndedDetail,\n MesubEvent,\n PaymentFailedDetail,\n RenewalUpcomingDetail,\n RenewedDetail,\n StoppedDetail,\n WebhookEvent,\n} from './webhooks.js';\n\n/** One field of an answer: how to tell it is right, and what it should have been. */\ninterface Field {\n check: (value: unknown) => boolean;\n expected: string;\n /**\n * A field newer than some backs still serving: absent, it is set to this\n * value rather than refused, so the answer still holds its type. A\n * function builds a fresh one for each answer.\n */\n absent?: null | false | (() => unknown);\n /** A field the back leaves out when it has nothing to say: absent is fine, as is. */\n optional?: true;\n}\n\nconst STRING: Field = { check: (value) => typeof value === 'string', expected: 'a string' };\nconst STRING_OR_NULL: Field = {\n check: (value) => value === null || typeof value === 'string',\n expected: 'a string or null',\n};\nconst OPTIONAL_STRING: Field = { ...STRING, optional: true };\nconst COUNT: Field = {\n check: (value) => typeof value === 'number' && Number.isInteger(value) && value >= 0,\n expected: 'a whole number',\n};\nconst BOOLEAN: Field = { check: (value) => typeof value === 'boolean', expected: 'a boolean' };\nconst OBJECT: Field = { check: isObject, expected: 'an object' };\nconst LIST_OF: Field = { check: Array.isArray, expected: 'a list' };\nconst DATE: Field = { check: isDate, expected: 'a date' };\nconst DATE_OR_NULL: Field = {\n check: (value) => value === null || isDate(value),\n expected: 'a date or null',\n};\n/** `retry_deadline`, served since Mesub-io/backend#191: null when a back predates it. */\nconst RETRY_DEADLINE: Field = { ...DATE_OR_NULL, absent: null };\n/** `paused` and `end_reason`, served since Mesub-io/backend#236: false and null before it. */\nconst PAUSED: Field = { ...BOOLEAN, absent: false };\nconst END_REASON: Field = { ...STRING_OR_NULL, absent: null };\n/** `blocked_since` and `blocked_reason`, served since Mesub-io/backend#398: null before it. */\nconst BLOCKED_SINCE: Field = { ...DATE_OR_NULL, absent: null };\nconst BLOCKED_REASON: Field = { ...STRING_OR_NULL, absent: null };\n/**\n * `subscription_id` (Mesub-io/backend#469), `stop_reason`, `closable` and\n * `closable_from` (Mesub-io/backend#402), and `transaction_expires_at`\n * (Mesub-io/backend#471): null, and false, before them.\n */\nconst SUBSCRIPTION_ID: Field = { ...STRING_OR_NULL, absent: null };\nconst STOP_REASON: Field = { ...STRING_OR_NULL, absent: null };\nconst CLOSABLE: Field = { ...BOOLEAN, absent: false };\nconst CLOSABLE_FROM: Field = { ...DATE_OR_NULL, absent: null };\nconst TRANSACTION_EXPIRES_AT: Field = { ...DATE_OR_NULL, absent: null };\n/**\n * A plan's `token` and `amount_usd`, and an attempt's `amount_usd`, served\n * since Mesub-io/backend#433: an unverified token with nothing known, and\n * null, before it.\n */\nconst AMOUNT_USD: Field = { ...STRING_OR_NULL, absent: null };\n/** An attempt's `amount_received`, served since Mesub-io/backend#396: null before it. */\nconst AMOUNT_RECEIVED: Field = { ...STRING_OR_NULL, absent: null };\nconst TOKEN: Field = {\n ...OBJECT,\n absent: (): PlanToken => ({ symbol: null, name: null, logo_url: null, verified: false }),\n};\n/**\n * `next_retry_number`, `retry_number` and `retries_allowed`, served since\n * Mesub-io/backend#289: null when a back predates them.\n */\nconst RETRY_COUNT: Field = {\n check: (value) => value === null || COUNT.check(value),\n expected: 'a whole number or null',\n absent: null,\n};\n/**\n * What the cache turns into its dates: NaN or Infinity would never go stale.\n * A negative one is read as 0 there, so it is let through.\n */\nconst SECONDS: Field = {\n check: (value) => typeof value === 'number' && Number.isFinite(value),\n expected: 'a finite number of seconds',\n};\n\n/**\n * Every field of an `AccessAnswer` but `attempts`, which is checked apart: a\n * field added to the type and not here fails to compile. A status, an end\n * or blocked reason or an outcome is only checked to be a string, never against the\n * list: one the back adds later must not turn every guard into an error.\n */\nconst ANSWER = {\n /** Null asked by external id or email, for a customer with nothing on that plan. */\n wallet: STRING_OR_NULL,\n plan: STRING,\n subscription_id: SUBSCRIPTION_ID,\n access: BOOLEAN,\n status: STRING,\n paused: PAUSED,\n end_reason: END_REASON,\n late_reason: STRING_OR_NULL,\n stop_reason: STOP_REASON,\n payment_status: STRING,\n blocked_since: BLOCKED_SINCE,\n blocked_reason: BLOCKED_REASON,\n subscribed_since: DATE_OR_NULL,\n first_subscribed_at: DATE_OR_NULL,\n current_period_end: DATE_OR_NULL,\n cancelled_at: DATE_OR_NULL,\n access_until: DATE_OR_NULL,\n next_charge_at: DATE_OR_NULL,\n next_retry_at: DATE_OR_NULL,\n retry_deadline: RETRY_DEADLINE,\n closable: CLOSABLE,\n closable_from: CLOSABLE_FROM,\n revalidate_after: SECONDS,\n} satisfies Record<Exclude<keyof AccessAnswer, 'attempts'>, Field>;\n\nconst ATTEMPT = {\n outcome: STRING,\n reason: STRING_OR_NULL,\n amount: STRING,\n attempted_at: DATE,\n signature: STRING_OR_NULL,\n} satisfies Record<keyof ServedAttempt, Field>;\n\n/**\n * What `/v1/access` answered for one plan, checked before anything reads or\n * caches it (#31). Without `revalidate_after` an entry's dates were NaN and it\n * never went stale, `access: true` served for good; a Redis store refused\n * `PX NaN`.\n *\n * Throws a MesubError `unexpected` on any answer of another shape: a wrong\n * `baseUrl` answering JSON, or a back the SDK no longer agrees with. Never\n * cached, so the next call asks again.\n */\nexport function accessAnswerFrom(body: unknown): AccessAnswer {\n const problem = answerProblem(body);\n\n if (problem !== null) throw unreadable(problem, body);\n\n return body as AccessAnswer;\n}\n\n/**\n * What `/v1/access` answered without a plan, for `accessList`: a list of\n * answers, each checked like `accessAnswerFrom`'s, and a finite\n * `revalidate_after` for the list itself. Throws and is never cached the same way.\n */\nexport function accessListFrom(body: unknown): AccessList {\n const problem = problemWith(body, LIST) ?? plansProblem(body);\n\n if (problem !== null) throw unreadable(problem, body);\n\n return body as AccessList;\n}\n\nconst LIST = {\n plans: LIST_OF,\n revalidate_after: SECONDS,\n} satisfies Record<keyof AccessList, Field>;\n\nfunction answerProblem(body: unknown, label?: string): string | null {\n return problemWith(body, ANSWER, label) ?? attemptsProblem(body, label);\n}\n\nfunction plansProblem(body: unknown): string | null {\n const { plans } = body as { plans: unknown[] };\n\n for (const [index, answer] of plans.entries()) {\n const problem = answerProblem(answer, `plans[${index}]`);\n\n if (problem !== null) return problem;\n }\n\n return null;\n}\n\n/**\n * Every field of a `ServerSubscription`, as `GET /v1/subscriptions/:id`\n * answers it and as `submit` and `list` embed it. Its status is only checked\n * to be a string, like an access answer's.\n */\nconst SUBSCRIPTION = {\n id: STRING,\n status: STRING,\n paused: PAUSED,\n end_reason: END_REASON,\n late_reason: STRING_OR_NULL,\n stop_reason: STOP_REASON,\n access: BOOLEAN,\n payment_status: STRING,\n blocked_since: BLOCKED_SINCE,\n blocked_reason: BLOCKED_REASON,\n plan: STRING_OR_NULL,\n wallet: STRING,\n email: STRING_OR_NULL,\n external_id: STRING_OR_NULL,\n current_period_start: DATE_OR_NULL,\n current_period_end: DATE_OR_NULL,\n next_charge_at: DATE_OR_NULL,\n next_retry_at: DATE_OR_NULL,\n retry_deadline: RETRY_DEADLINE,\n next_retry_number: RETRY_COUNT,\n retries_allowed: RETRY_COUNT,\n access_until: DATE_OR_NULL,\n closable: CLOSABLE,\n closable_from: CLOSABLE_FROM,\n created_at: DATE,\n confirmed_at: DATE_OR_NULL,\n} satisfies Record<keyof ServerSubscription, Field>;\n\nconst SUBSCRIPTION_LIST = {\n data: LIST_OF,\n has_more: BOOLEAN,\n} satisfies Record<keyof ServerSubscriptionList, Field>;\n\n/** An outcome is only checked to be a string: one the back adds later is handed on. */\nconst SUBSCRIPTION_ATTEMPT = {\n id: STRING,\n attempted_at: DATE,\n outcome: STRING,\n reason: STRING_OR_NULL,\n amount: STRING,\n amount_usd: AMOUNT_USD,\n amount_received: AMOUNT_RECEIVED,\n signature: STRING_OR_NULL,\n retry: BOOLEAN,\n retry_number: RETRY_COUNT,\n retries_allowed: RETRY_COUNT,\n period_start: DATE_OR_NULL,\n} satisfies Record<keyof SubscriptionAttempt, Field>;\n\nconst SUBSCRIPTION_ATTEMPT_LIST = {\n data: LIST_OF,\n has_more: BOOLEAN,\n paid: OBJECT,\n} satisfies Record<keyof SubscriptionAttemptList, Field>;\n\n/** A sum in base units: digits only, so it can be shown or added without a float. */\nconst BASE_UNITS: Field = {\n check: (value) => typeof value === 'string' && /^\\d+$/.test(value),\n expected: 'a whole number as a string',\n};\n\n/** What the front needs to have the wallet sign; the costs are only shown. */\nconst SUBSCRIBE_TRANSACTION = {\n subscription: OBJECT,\n transaction: STRING,\n last_valid_block_height: STRING,\n transaction_expires_at: TRANSACTION_EXPIRES_AT,\n costs: OBJECT,\n terms: OBJECT,\n} satisfies Record<keyof SubscribeTransaction, Field>;\n\n/** Lamports, as the back writes a u64. */\nconst COSTS = {\n rent: OBJECT,\n fee: OBJECT,\n total: BASE_UNITS,\n} satisfies Record<keyof SubscribeCosts, Field>;\n\nconst RENT = {\n subscription: BASE_UNITS,\n // Null when the authority exists already: no rent for it.\n authority: {\n check: (value) => value === null || BASE_UNITS.check(value),\n expected: 'a whole number as a string or null',\n },\n total: BASE_UNITS,\n} satisfies Record<keyof SubscribeCosts['rent'], Field>;\n\nconst FEE = {\n signatures: COUNT,\n per_signature: BASE_UNITS,\n priority: BASE_UNITS,\n total: BASE_UNITS,\n} satisfies Record<keyof SubscribeCosts['fee'], Field>;\n\nconst PLAN = {\n slug: STRING,\n name: STRING,\n description: STRING_OR_NULL,\n project_name: STRING,\n logo_url: STRING_OR_NULL,\n amount: STRING,\n amount_display: STRING,\n amount_usd: AMOUNT_USD,\n decimals: COUNT,\n symbol: STRING_OR_NULL,\n token: TOKEN,\n mint: STRING,\n period_hours: COUNT,\n network: STRING,\n // A string, not one of two: a status added later is handed back, not refused.\n status: STRING,\n available: BOOLEAN,\n ends_at: DATE_OR_NULL,\n} satisfies Record<keyof Plan, Field>;\n\n/** A served `token` is whole: one field missing or of another type is refused. */\nconst PLAN_TOKEN = {\n symbol: STRING_OR_NULL,\n name: STRING_OR_NULL,\n logo_url: STRING_OR_NULL,\n verified: BOOLEAN,\n} satisfies Record<keyof PlanToken, Field>;\n\n/** A plan's own fields, then its token's. */\nfunction planProblem(body: unknown, label?: string): string | null {\n return (\n problemWith(body, PLAN, label) ??\n problemWith(\n (body as Plan).token,\n PLAN_TOKEN,\n label === undefined ? 'token' : `${label}.token`,\n )\n );\n}\n\n/** One plan, from `plans.retrieve`. */\nexport function planFrom(body: unknown): Plan {\n const problem = planProblem(body);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/plans/:slug');\n\n return body as Plan;\n}\n\n/** `plans.list`: each plan checked like `retrieve`'s. */\nexport function planListFrom(body: unknown): Plan[] {\n const problem =\n problemWith(body, { plans: LIST_OF }) ??\n firstProblem((body as { plans: unknown[] }).plans, 'plans', planProblem);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/plans');\n\n return (body as { plans: Plan[] }).plans;\n}\n\n/** One subscription, from `retrieve`, `retry`, or read back after a submit. */\nexport function serverSubscriptionFrom(\n body: unknown,\n route = 'GET /v1/subscriptions/:id',\n status = 200,\n): ServerSubscription {\n const problem = problemWith(body, SUBSCRIPTION);\n\n if (problem !== null) throw unreadable(problem, body, route, status);\n\n return body as ServerSubscription;\n}\n\n/** One page of `list`: each subscription checked like `retrieve`'s. */\nexport function serverSubscriptionListFrom(body: unknown): ServerSubscriptionList {\n const problem =\n problemWith(body, SUBSCRIPTION_LIST) ??\n firstProblem((body as { data: unknown[] }).data, 'data', SUBSCRIPTION);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/subscriptions');\n\n return body as ServerSubscriptionList;\n}\n\n/** One page of `attempts`: each attempt checked, and the paid total. */\nexport function subscriptionAttemptListFrom(body: unknown): SubscriptionAttemptList {\n const problem =\n problemWith(body, SUBSCRIPTION_ATTEMPT_LIST) ??\n firstProblem((body as { data: unknown[] }).data, 'data', SUBSCRIPTION_ATTEMPT) ??\n problemWith(\n (body as SubscriptionAttemptList).paid,\n { count: COUNT, amount: BASE_UNITS },\n 'paid',\n );\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/subscriptions/:id/attempts');\n\n return body as SubscriptionAttemptList;\n}\n\n/** What `create` or `transaction` answered: what the wallet signs, and the subscription's id. */\nexport function subscribeTransactionFrom(\n body: unknown,\n route = 'POST /v1/subscriptions',\n): SubscribeTransaction {\n const problem =\n problemWith(body, SUBSCRIBE_TRANSACTION) ??\n problemWith(\n (body as SubscribeTransaction).subscription,\n { id: STRING, status: STRING },\n 'subscription',\n ) ??\n problemWith((body as SubscribeTransaction).costs, COSTS, 'costs') ??\n problemWith((body as SubscribeTransaction).costs.rent, RENT, 'costs.rent') ??\n problemWith((body as SubscribeTransaction).costs.fee, FEE, 'costs.fee') ??\n problemWith(\n (body as SubscribeTransaction).terms,\n { message: STRING, expires_at: DATE },\n 'terms',\n );\n\n if (problem !== null) throw unreadable(problem, body, route, 201);\n\n return body as SubscribeTransaction;\n}\n\n/** What `submit` answered: the subscription as it settled, and a reason when one came. */\nexport function submitResultFrom(body: unknown): SubmitResult {\n return confirmResultFrom(body, 'POST /v1/subscriptions/:id/submit');\n}\n\n/** What a confirm of `route` answered: the same shape as `submit`'s. */\nexport function confirmResultFrom(body: unknown, route: string): ConfirmResult {\n const problem =\n problemWith(body, { subscription: OBJECT }) ??\n problemWith((body as ConfirmResult).subscription, SUBSCRIPTION, 'subscription') ??\n reasonProblem(body as ConfirmResult);\n\n if (problem !== null) throw unreadable(problem, body, route, 201);\n\n return body as ConfirmResult;\n}\n\nconst WALLET_TRANSACTION = {\n transaction: STRING,\n last_valid_block_height: STRING,\n transaction_expires_at: TRANSACTION_EXPIRES_AT,\n} satisfies Record<keyof WalletTransaction, Field>;\n\n/** What `cancel`, `resume` or `close` answered: the transaction the wallet signs and sends. */\nexport function walletTransactionFrom(body: unknown, route: string): WalletTransaction {\n const problem = problemWith(body, WALLET_TRANSACTION);\n\n if (problem !== null) throw unreadable(problem, body, route, 201);\n\n return body as WalletTransaction;\n}\n\n/**\n * `reason` and `reason_code` (Mesub-io/backend#470), each a string when\n * present. One without the other is let through: a back that predates\n * `reason_code` sends `reason` alone.\n */\nfunction reasonProblem({ reason, reason_code }: ConfirmResult): string | null {\n if (reason !== undefined && typeof reason !== 'string') return 'reason is not a string';\n if (reason_code !== undefined && typeof reason_code !== 'string') {\n return 'reason_code is not a string';\n }\n return null;\n}\n\n/**\n * `sequence` (Mesub-io/backend#502): a whole number from 1, left out on a\n * body written before it and on an event Mesub could not number.\n */\nconst SEQUENCE: Field = {\n check: (value) => typeof value === 'number' && Number.isInteger(value) && value >= 1,\n expected: 'a whole number from 1',\n optional: true,\n};\n\n/**\n * An event (Mesub-io/backend#482), as `GET /v1/events` serves it and a\n * webhook carries it: its id, type and date, the network, the version it is\n * written in, the subscription as `retrieve` answers it, and the event's\n * detail, checked field by field for the types this release knows. A type,\n * a network or a version it does not know is only checked to be a string,\n * like a status: one the back adds later must not turn every delivery into\n * an error.\n */\nconst EVENT = {\n id: STRING,\n object: { check: (value) => value === 'event', expected: '\"event\"' },\n type: STRING,\n created_at: DATE,\n network: STRING,\n api_version: STRING,\n sequence: SEQUENCE,\n data: OBJECT,\n detail: OBJECT,\n} satisfies Record<keyof MesubEvent, Field>;\n\n/** Only on a body sent by hand from the dashboard, and then true. */\nconst TEST: Field = { check: (value) => value === true, expected: 'true', optional: true };\n\n/**\n * A webhook body written before the event shape (Mesub-io/backend#144), and\n * kept as it was sent: no id, and the detail inside `data`.\n */\nconst OLDER_WEBHOOK = { type: STRING, created_at: DATE, data: OBJECT, test: TEST };\n\n/**\n * `first_payment` on `subscription.created` (Mesub-io/backend#509): null, or\n * what the first payment paid; left out by an event written before it.\n */\nconst FIRST_PAYMENT: Field = {\n check: (value) =>\n value === null ||\n problemWith(value, { amount: STRING, mint: STRING_OR_NULL, signature: STRING_OR_NULL }) ===\n null,\n expected: 'null or a payment (amount, mint, signature)',\n optional: true,\n};\n\n/**\n * An access event's detail (Mesub-io/backend#507), and since #511 why and\n * until when: both left out by an event written before it.\n */\nconst ACCESS_CHANGED = {\n changed_at: DATE,\n blocked_reason: { ...STRING_OR_NULL, optional: true },\n access_until: { ...DATE_OR_NULL, optional: true },\n} satisfies Record<keyof AccessChangedDetail, Field>;\n\nconst DETAILS: Partial<Record<string, Record<string, Field>>> = {\n 'subscription.created': {\n previous_id: OPTIONAL_STRING,\n first_payment: FIRST_PAYMENT,\n } satisfies Record<keyof CreatedDetail, Field>,\n 'subscription.ended': {\n changed_at: { ...DATE, optional: true },\n // Only `cancelled` today: a newer word is let through, as a status is.\n end_reason: OPTIONAL_STRING,\n } satisfies Record<keyof EndedDetail, Field>,\n 'subscription.renewal_upcoming': {\n can_pay: BOOLEAN,\n renewal_issue: STRING_OR_NULL,\n amount: STRING,\n mint: STRING,\n due_at: DATE,\n } satisfies Record<keyof RenewalUpcomingDetail, Field>,\n 'subscription.renewed': {\n amount: STRING,\n mint: STRING,\n period_start: DATE,\n period_end: DATE,\n signature: STRING,\n } satisfies Record<keyof RenewedDetail, Field>,\n 'subscription.payment_failed': {\n reason: STRING,\n amount: STRING,\n mint: STRING,\n period_start: DATE_OR_NULL,\n period_end: DATE_OR_NULL,\n next_retry_at: DATE_OR_NULL,\n retry_deadline: DATE_OR_NULL,\n retries_left: COUNT,\n retry_mode: STRING_OR_NULL,\n } satisfies Record<keyof PaymentFailedDetail, Field>,\n 'subscription.stopped': {\n reason: STRING,\n } satisfies Record<keyof StoppedDetail, Field>,\n 'subscription.access_granted': ACCESS_CHANGED,\n 'subscription.access_revoked': ACCESS_CHANGED,\n};\n\n/** An event's own fields, its subscription's, then its detail's for a type this release knows. */\nfunction eventProblem(body: unknown, label?: string): string | null {\n const prefix = label === undefined ? '' : `${label}.`;\n const { type, data, detail } = body as { type: string; data: unknown; detail: unknown };\n const fields = DETAILS[type];\n\n return (\n problemWith(body, EVENT, label) ??\n problemWith(data, SUBSCRIPTION, `${prefix}data`) ??\n (fields ? problemWith(detail, fields, `${prefix}detail`) : null)\n );\n}\n\n/** One event, from `events.retrieve`. */\nexport function eventFrom(body: unknown): MesubEvent {\n const problem = eventProblem(body);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/events/:id');\n\n return body as MesubEvent;\n}\n\n/** One page of `events.list`: each event checked like `retrieve`'s, and `available_since`. */\nexport function eventListFrom(body: unknown): EventList {\n const problem =\n problemWith(body, EVENT_LIST) ??\n firstProblem((body as EventList).data, 'data', eventProblem);\n\n if (problem !== null) throw unreadable(problem, body, 'GET /v1/events');\n\n return body as EventList;\n}\n\nconst EVENT_LIST = {\n data: LIST_OF,\n has_more: BOOLEAN,\n available_since: DATE,\n} satisfies Record<keyof EventList, Field>;\n\n/**\n * A webhook whose signature verified, and `delivery_id`, its `webhook-id`\n * header. The event shape (Mesub-io/backend#482) is handed back as it came;\n * a body written before it (no `id`, no `object`, the detail inside `data`)\n * in the same shape, with its detail at the top and what it never carried\n * null. Throws a MesubError `unexpected` on a body of another shape: Mesub\n * signed it, so it is a back this release no longer agrees with.\n */\nexport function webhookEventFrom(body: unknown, deliveryId: string): WebhookEvent {\n const older = isObject(body) && !('id' in (body as object)) && !('object' in (body as object));\n const problem = older\n ? olderWebhookProblem(body)\n : (eventProblem(body) ?? problemWith(body, { test: TEST }));\n\n if (problem !== null) {\n throw new MesubError(`Mesub sent a webhook this SDK cannot read: ${problem}.`, {\n status: null,\n code: 'unexpected',\n body,\n });\n }\n\n if (!older) return { ...(body as MesubEvent), delivery_id: deliveryId } as WebhookEvent;\n\n const { type, created_at, data, test } = body as {\n type: string;\n created_at: string;\n data: ServerSubscription & { detail: Record<string, unknown> };\n test?: true;\n };\n const { detail, ...subscription } = data;\n\n return {\n id: null,\n delivery_id: deliveryId,\n object: 'event',\n type,\n created_at,\n network: null,\n api_version: null,\n data: subscription,\n detail,\n ...(test !== undefined && { test }),\n } as WebhookEvent;\n}\n\n/** A body of the shape webhooks had before the event's: the detail inside `data`. */\nfunction olderWebhookProblem(body: unknown): string | null {\n const { type, data } = body as { type: string; data: unknown };\n const fields = DETAILS[type];\n\n return (\n problemWith(body, OLDER_WEBHOOK) ??\n problemWith(data, { ...SUBSCRIPTION, detail: OBJECT }, 'data') ??\n (fields ? problemWith((data as { detail: unknown }).detail, fields, 'data.detail') : null)\n );\n}\n\n/** The first item of a list that is not of those fields, or null when all are. */\nfunction firstProblem(\n items: unknown[],\n label: string,\n fields: Record<string, Field> | ((item: unknown, label: string) => string | null),\n): string | null {\n for (const [index, item] of items.entries()) {\n const at = `${label}[${index}]`;\n const problem =\n typeof fields === 'function' ? fields(item, at) : problemWith(item, fields, at);\n\n if (problem !== null) return problem;\n }\n\n return null;\n}\n\n/**\n * The transport hands back 2xx bodies only: /v1/access and the reads answer\n * 200, the POSTs 201.\n */\nfunction unreadable(\n problem: string,\n body: unknown,\n route = '/v1/access',\n status = 200,\n): MesubError {\n return new MesubError(\n `Mesub answered ${route} with an answer this SDK cannot read: ${problem}.`,\n { status, code: 'unexpected', body },\n );\n}\n\n/**\n * The first field that is missing or of the wrong type, or null when all are\n * right. A missing field with an `absent` value is set to it on the body,\n * which is the answer just parsed, never one the caller handed in.\n */\nfunction problemWith(body: unknown, fields: Record<string, Field>, label?: string): string | null {\n if (!isObject(body)) {\n return `${label ?? 'the body'} is not an object`;\n }\n\n const prefix = label === undefined ? '' : `${label}.`;\n\n for (const [name, field] of Object.entries(fields)) {\n const value = (body as Record<string, unknown>)[name];\n\n if (value === undefined && field.optional) continue;\n if (value === undefined && 'absent' in field) {\n (body as Record<string, unknown>)[name] =\n typeof field.absent === 'function' ? field.absent() : field.absent;\n continue;\n }\n if (value === undefined) return `${prefix}${name} is missing`;\n if (!field.check(value)) return `${prefix}${name} is not ${field.expected}`;\n }\n\n return null;\n}\n\n/** `attempts`, only there when asked for: a list of attempts when it is. */\nfunction attemptsProblem(body: unknown, label?: string): string | null {\n const { attempts } = body as { attempts?: unknown };\n const prefix = label === undefined ? '' : `${label}.`;\n\n if (attempts === undefined) return null;\n if (!Array.isArray(attempts)) return `${prefix}attempts is not a list`;\n\n for (const [index, attempt] of attempts.entries()) {\n const problem = problemWith(attempt, ATTEMPT, `${prefix}attempts[${index}]`);\n\n if (problem !== null) return problem;\n }\n\n return null;\n}\n\nfunction isDate(value: unknown): boolean {\n return typeof value === 'string' && !Number.isNaN(Date.parse(value));\n}\n\nfunction isObject(value: unknown): boolean {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { BlockedReason } from './answer.js';\nimport { MesubError } from './errors.js';\nimport { numberOf } from './options.js';\nimport type { ServerSubscription } from './subscriptions.js';\nimport { webhookEventFrom } from './validate.js';\n\n/**\n * Webhooks, as Mesub-io/backend#144 sends them: Standard Webhooks\n * (https://www.standardwebhooks.com), a POST whose body is signed with the\n * endpoint's secret. Since Mesub-io/backend#482 the body is the event, as\n * `GET /v1/events` serves it. The types copy the back's `event-body.ts`,\n * `webhook-payload.ts`, `webhook-events.ts` and `pull-events.ts`, which stay\n * the source of truth. Snake case, as the API serves it.\n */\n\n/** A Fetch `Headers` (Next, route handlers) or Node's plain object (Express). */\nexport type HeaderSource = Headers | Record<string, string | string[] | undefined>;\n\n/** What every signing secret starts with, as the endpoint's dashboard page shows it. */\nconst SECRET_PREFIX = 'whsec_';\n\n/** Standard Webhooks' default: how far `webhook-timestamp` may be from now, either way. */\nconst DEFAULT_TOLERANCE = 5 * 60 * 1000;\n\n/** `webhook-timestamp`: Unix seconds, digits only. */\nconst TIMESTAMP = /^[0-9]{1,15}$/;\n\n/** The v1 events an endpoint subscribes to, and `test`, sent from the dashboard on request. */\nexport type WebhookEventType = WebhookEvent['type'];\n\n/** The v1 events `GET /v1/events` serves: every type but `test`. */\nexport type EventType = MesubEvent['type'];\n\n/** The Solana network a subscription is on, as `/v1` spells it. */\nexport type EventNetwork = 'devnet' | 'mainnet-beta';\n\n/**\n * One event, as `GET /v1/events` serves it and its webhooks carry it\n * (Mesub-io/backend#482): written once, the same bytes for every endpoint,\n * every retry and every read.\n */\ninterface EventOf<Type extends string, Detail> {\n /**\n * The event's own id, the same for every endpoint and every delivery:\n * what `events.retrieve` takes.\n */\n id: string;\n object: 'event';\n type: Type;\n /** When the event happened (ISO 8601), not when it was sent. */\n created_at: string;\n /** The Solana network the subscription is on. */\n network: EventNetwork;\n /** The `Mesub-Version` `data` and `detail` are written in, kept with the body. */\n api_version: string;\n /**\n * Its place among this subscription's events (Mesub-io/backend#502): 1,\n * 2, 3... in the order they happened, two events of one change included.\n * Deliveries arrive in any order: apply one subscription's events by\n * `sequence`, and skip one lower than the last you applied for it.\n * Absent on a body written before Mesub numbered events, and on one it\n * could not number: such an event cannot be placed, so read the\n * subscription now (`subscriptions.retrieve` or `access`) rather than\n * applying its `data`. Always 1 on a test delivery.\n */\n sequence?: number;\n /**\n * The subscription as it stood right after the change that raised the\n * event, as `GET /v1/subscriptions/:id` would have answered it then,\n * however late the delivery. Events raised by one change share it:\n * `detail` says what each is about.\n */\n data: ServerSubscription;\n /** What the event is about, beyond its type. */\n detail: Detail;\n}\n\n/**\n * One event as `verify` hands it back: the body Mesub signed, and\n * `delivery_id`, its `webhook-id` header.\n *\n * A body written before the event shape (before `available_since`, see\n * `events.list`) keeps the shape it was sent in, `{ type, created_at, data }`\n * with the detail inside `data`. It is handed back in this shape all the\n * same: `detail` moved to the top, and `id`, `network` and `api_version`\n * null, since it carried none. Its `data` is the subscription as it stood at\n * the delivery's first attempt.\n */\ninterface WebhookEventOf<Type extends string, Detail> extends Omit<\n EventOf<Type, Detail>,\n 'id' | 'network' | 'api_version'\n> {\n /** The event's id, as `EventOf`'s; null on a body written in the older shape. */\n id: string | null;\n /**\n * The delivery's id, from the `webhook-id` header: one per event and per\n * endpoint, the same on every retry and resend. Drop a delivery you\n * already handled by it; across endpoints, by `id`.\n */\n delivery_id: string;\n /** Null on a body written in the older shape. */\n network: EventNetwork | null;\n /** Null on a body written in the older shape. */\n api_version: string | null;\n /**\n * True on a body sent by hand from the dashboard: the `test` event, or\n * a sample of a real type on a made-up subscription (`sub_test`,\n * `user_123`), with the id `evt_test`. Absent on a real event. Change\n * nothing for it.\n */\n test?: true;\n}\n\n/** No detail: the subscription says it all. */\nexport type NoDetail = Record<string, never>;\n\n/** `subscription.created`: the first payment landed. */\nexport interface CreatedDetail {\n /** On a wallet coming back after a stop, the subscription it replaces. */\n previous_id?: string;\n /**\n * What the first payment paid (Mesub-io/backend#509). Null when Mesub\n * recorded none at the confirmation (a period paid only in part, which\n * the next charge collects); absent on an event written before it.\n */\n first_payment?: FirstPayment | null;\n}\n\n/** The first payment of a subscription: the transfer its own transaction carried. */\nexport interface FirstPayment {\n /** In the token's smallest unit, as a string. */\n amount: string;\n mint: string | null;\n /** The transaction that paid it. */\n signature: string | null;\n /**\n * What the receiver's account got of it: `amount` less the fee a\n * Token-2022 mint withholds. Null when not known, absent from a Mesub that\n * predates it.\n */\n amount_received?: string | null;\n}\n\n/**\n * `subscription.ended`: `changed_at` only on a cancellation that reached its\n * end (Mesub-io/backend#509), its expiry to the millisecond, the moment\n * `data` describes. Absent on any other end.\n */\nexport interface EndedDetail {\n changed_at?: string;\n /**\n * `cancelled` on a cancellation that reached its end, as `data.end_reason`\n * says it (Mesub-io/backend#511); absent otherwise, and on an older event.\n */\n end_reason?: 'cancelled';\n}\n\n/**\n * `subscription.renewed`: a period paid. Amounts in the mint's base units, as\n * strings. It can come late: a transfer whose send failed may land later, and\n * Mesub sends `renewed` then. By that time the subscription may no longer be\n * `active`. Ask `hasAccess` before granting access.\n */\nexport interface RenewedDetail {\n amount: string;\n mint: string;\n period_start: string;\n period_end: string;\n /** The transaction that paid it. */\n signature: string;\n}\n\n/** `subscription.payment_failed`: a pull missed, and what comes next. */\nexport interface PaymentFailedDetail {\n /** Why, e.g. `insufficient_balance`: an attempt's reason (`ServedAttempt`). */\n reason: string;\n amount: string;\n mint: string;\n /** The period it was for; null when unknown. */\n period_start: string | null;\n period_end: string | null;\n /**\n * When Mesub retries; null when it will not (the plan ends before the\n * retry, among others), or on Free, where retries are by hand.\n */\n next_retry_at: string | null;\n /** Free only: when hand retries close, the plan's end when that comes first. */\n retry_deadline: string | null;\n /**\n * Pulls still to come before the subscription stops, the next one\n * included; 0 with `next_retry_at` null when the plan ends before any.\n */\n retries_left: number;\n /** Who retries: Mesub on its schedule, or you by hand (Free). Null once none is left. */\n retry_mode: 'scheduled' | 'manual' | null;\n}\n\n/**\n * `subscription.stopped`: no more pulls. `data.stop_reason` says why it\n * stopped, the reading a program branches on.\n */\nexport interface StoppedDetail {\n /** Why, e.g. `insufficient_balance` or, on Free, `retry_deadline_passed`. */\n reason: string;\n}\n\n/**\n * Why the coming charge would not pay: the wallet holds too little or has no\n * account of the token (`balance`), or Mesub's approval can no longer move\n * the tokens (`authority`). One newer than this release is handed back as it is.\n */\nexport type RenewalIssue = 'balance' | 'authority';\n\n/**\n * `subscription.renewal_upcoming`: the charge due next, and whether the wallet\n * can pay it as it stood at the event's `created_at`. A reading, not a promise.\n */\nexport interface RenewalUpcomingDetail {\n can_pay: boolean;\n /** Why not; null when it can. */\n renewal_issue: RenewalIssue | null;\n amount: string;\n mint: string;\n /** When the charge is due. */\n due_at: string;\n}\n\n/**\n * `subscription.access_granted` and `subscription.access_revoked`: when\n * access actually changed. A date that passed with nothing else happening\n * (the end of a paid period, of a cancellation, of the plan) is that date to\n * the millisecond, even when Mesub wrote the event later; `created_at` is\n * when it was written.\n */\nexport interface AccessChangedDetail {\n changed_at: string;\n /**\n * Why the subscription's charges were held when access changed, in the\n * words of `data.blocked_reason`; null when they were not. Absent on an\n * event written before Mesub-io/backend#511. Keep a default branch: Mesub\n * may add a reason.\n */\n blocked_reason?: BlockedReason | null;\n /**\n * On `access_revoked`, the date access ended: the end of a paid period,\n * of a cancellation, of the plan, or of the access a held subscription\n * keeps; never after `changed_at`, and null when access ended with no\n * date of its own (a stop). On `access_granted`, on a held subscription,\n * the date access ends if nothing is collected by then; null otherwise.\n * Absent on an event written before Mesub-io/backend#511.\n */\n access_until?: string | null;\n}\n\n/** Each event type and its `detail`. */\ninterface EventDetails {\n 'subscription.created': CreatedDetail;\n 'subscription.renewal_upcoming': RenewalUpcomingDetail;\n 'subscription.renewed': RenewedDetail;\n 'subscription.payment_failed': PaymentFailedDetail;\n 'subscription.stopped': StoppedDetail;\n 'subscription.cancelled': NoDetail;\n 'subscription.resumed': NoDetail;\n 'subscription.ended': EndedDetail;\n 'subscription.expired': NoDetail;\n 'subscription.access_granted': AccessChangedDetail;\n 'subscription.access_revoked': AccessChangedDetail;\n 'subscription.paused': NoDetail;\n 'subscription.unpaused': NoDetail;\n}\n\n/**\n * Every event `GET /v1/events` serves. Narrow it with `switch (event.type)`,\n * and keep a default branch: a type newer than this release is handed back\n * too, its subscription checked.\n */\nexport type MesubEvent = {\n [Type in keyof EventDetails]: EventOf<Type, EventDetails[Type]>;\n}[keyof EventDetails];\n\nexport type SubscriptionCreatedEvent = WebhookEventOf<'subscription.created', CreatedDetail>;\n/** Once per period, in its last quarter and three days before the charge at most. */\nexport type SubscriptionRenewalUpcomingEvent = WebhookEventOf<\n 'subscription.renewal_upcoming',\n RenewalUpcomingDetail\n>;\nexport type SubscriptionRenewedEvent = WebhookEventOf<'subscription.renewed', RenewedDetail>;\nexport type SubscriptionPaymentFailedEvent = WebhookEventOf<\n 'subscription.payment_failed',\n PaymentFailedDetail\n>;\nexport type SubscriptionStoppedEvent = WebhookEventOf<'subscription.stopped', StoppedDetail>;\nexport type SubscriptionCancelledEvent = WebhookEventOf<'subscription.cancelled', NoDetail>;\nexport type SubscriptionResumedEvent = WebhookEventOf<'subscription.resumed', NoDetail>;\n/**\n * Sent when the plan ends, is removed or replaced, and when a subscription is\n * closed. Once Mesub turns the access events on, also when a cancellation\n * reaches its end (Mesub-io/backend#509): `end_reason` `cancelled`,\n * `detail.changed_at` its expiry, usually within half an hour of it, after\n * that expiry's `subscription.access_revoked`. Closing it later sends it\n * again, `end_reason` `closed`: one subscription can end twice.\n */\nexport type SubscriptionEndedEvent = WebhookEventOf<'subscription.ended', EndedDetail>;\n/** A checkout nobody signed in time. */\nexport type SubscriptionExpiredEvent = WebhookEventOf<'subscription.expired', NoDetail>;\n/**\n * Access started (Mesub-io/backend#507): the subscription grants access now\n * and did not just before. Its first access, a payment that gives access\n * back, a resume after a cancellation ended. A renewal paid on time, or a\n * resume while the period runs, sends none. `data.access` is true.\n *\n * With `subscription.access_revoked`, the one pair to drive access from:\n * sent exactly when access changes, and every change sends one. Apply them\n * by `sequence`; the last one applied says whether the customer has access.\n * The other events say why. Off until Mesub turns them on (`ACCESS_EVENTS`).\n */\nexport type SubscriptionAccessGrantedEvent = WebhookEventOf<\n 'subscription.access_granted',\n AccessChangedDetail\n>;\n/**\n * Access ended (Mesub-io/backend#507): the subscription grants no access now\n * and did just before. It stopped or ended, a cancellation or the plan\n * reached its end, or a blocked collection reached its `access_until`. For a\n * date that passed, sent a few seconds after it. `data.access` is false.\n * Off until Mesub turns it on.\n */\nexport type SubscriptionAccessRevokedEvent = WebhookEventOf<\n 'subscription.access_revoked',\n AccessChangedDetail\n>;\n/**\n * A seat parked (Mesub-io/backend#509): the project holds more subscribers\n * than its tier allows, and Mesub stopped charging the newest. Not a pause\n * the subscriber asked for; the status stays. `data.paused` is true,\n * `data.next_charge_at` null; access runs to the end of the paid period,\n * and its `subscription.access_revoked` comes then. Off until Mesub turns\n * the access events on.\n */\nexport type SubscriptionPausedEvent = WebhookEventOf<'subscription.paused', NoDetail>;\n/**\n * A parked seat back (Mesub-io/backend#509): the tier has room again, and\n * Mesub charges it again. `data.paused` is false; a\n * `subscription.access_granted` follows when its access had ended. Off until\n * Mesub turns the access events on.\n */\nexport type SubscriptionUnpausedEvent = WebhookEventOf<'subscription.unpaused', NoDetail>;\n/**\n * Sent from the dashboard on request, with a made-up subscription (`sub_test`).\n * The dashboard also sends samples of the other types, with `test: true`.\n */\nexport type TestEvent = WebhookEventOf<'test', NoDetail>;\n\n/**\n * Every event Mesub sends. Narrow it with `switch (event.type)`, and keep a\n * default branch: an event type newer than this release is handed back too,\n * its subscription checked, typed as one of these.\n */\nexport type WebhookEvent =\n | SubscriptionCreatedEvent\n | SubscriptionRenewalUpcomingEvent\n | SubscriptionRenewedEvent\n | SubscriptionPaymentFailedEvent\n | SubscriptionStoppedEvent\n | SubscriptionCancelledEvent\n | SubscriptionResumedEvent\n | SubscriptionEndedEvent\n | SubscriptionExpiredEvent\n | SubscriptionAccessGrantedEvent\n | SubscriptionAccessRevokedEvent\n | SubscriptionPausedEvent\n | SubscriptionUnpausedEvent\n | TestEvent;\n\n/** The body as received, never parsed: a string, a Buffer or any Uint8Array, an ArrayBuffer. */\nexport type WebhookBody = string | Uint8Array | ArrayBuffer;\n\nexport interface VerifyWebhookOptions {\n /**\n * The endpoint's signing secret, `whsec_...`. Defaults to the client's\n * `webhookSecret`, then to `process.env.MESUB_WEBHOOK_SECRET`.\n */\n secret?: string;\n /**\n * How far `webhook-timestamp` may be from now, either way, in\n * milliseconds, above 0: older is refused as a replay. Defaults to 5 minutes.\n */\n tolerance?: number;\n}\n\n/**\n * Checks a webhook Mesub sent and hands back its event: the signature over\n * the raw body, against the endpoint's secret, then the timestamp, then the\n * body's shape.\n *\n * Throws a MesubError `invalid_webhook` when it is not Mesub's, or not now:\n * a header missing, no signature matching, a timestamp outside `tolerance`.\n * Answer it with a 400.\n *\n * Throws `unexpected` for a body Mesub signed that this release cannot read.\n * Never answer it with a 400. Log it, update `@mesub/node`, and answer a\n * 5xx: Mesub retries the same body for 3 days, so the event gets through\n * once the update is out. Past 3 days of failures, Mesub disables the\n * endpoint.\n *\n * A TypeError is an integration error: a body already parsed, no secret,\n * one that is not `whsec_...`.\n */\nexport async function verifyWebhook(\n body: WebhookBody,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<WebhookEvent> {\n return verify(body, headers, options, undefined);\n}\n\n/** `mesub.webhooks`: `verifyWebhook` with the client's own secret. */\nexport class Webhooks {\n readonly #secret: string | undefined;\n readonly #received: (event: WebhookEvent) => Promise<void>;\n\n /** @internal */\n constructor(secret: string | undefined, received: (event: WebhookEvent) => Promise<void>) {\n this.#secret = secret;\n this.#received = received;\n }\n\n /**\n * `verifyWebhook`, with `webhookSecret` unless `options.secret` says\n * otherwise. A verified event also drops every access answer cached for\n * its customer, the yes and the no, so the next check asks Mesub.\n * Three drop nothing: `subscription.renewal_upcoming`, which moves no\n * access, the `test` event, and any body with `test: true`, whose\n * subscription is made up.\n */\n async verify(\n body: WebhookBody,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n ): Promise<WebhookEvent> {\n const event = await verify(body, headers, options, this.#secret);\n\n // A charge to come changes no answer: dropping it would cost the outage fallback.\n // A sample from the dashboard names a made-up customer: it moves nobody's access.\n const moves =\n event.test !== true &&\n event.type !== 'test' &&\n event.type !== 'subscription.renewal_upcoming';\n\n if (moves) await this.#received(event);\n\n return event;\n }\n}\n\nasync function verify(\n body: WebhookBody,\n headers: HeaderSource,\n options: VerifyWebhookOptions,\n fallback: string | undefined,\n): Promise<WebhookEvent> {\n const key =\n options.secret !== undefined || fallback !== undefined\n ? keyOf(options.secret ?? fallback, 'secret')\n : keyOf(envSecret(), 'MESUB_WEBHOOK_SECRET');\n const tolerance = numberOf('tolerance', options.tolerance, DEFAULT_TOLERANCE);\n const bytes = bytesOf(body);\n\n if (typeof headers !== 'object' || headers === null) {\n throw new TypeError('headers must be the request headers: a Headers or a plain object.');\n }\n\n const id = headerIn(headers, 'webhook-id');\n const timestamp = headerIn(headers, 'webhook-timestamp');\n const signatures = headerIn(headers, 'webhook-signature');\n\n if (!id || !timestamp || !signatures) {\n const missing = [\n !id && 'webhook-id',\n !timestamp && 'webhook-timestamp',\n !signatures && 'webhook-signature',\n ].filter(Boolean);\n\n throw invalid(`The webhook has no ${missing.join(', ')} header: it is not one of Mesub's.`);\n }\n if (!TIMESTAMP.test(timestamp)) {\n throw invalid('The webhook-timestamp header is not a number of seconds.');\n }\n\n const expected = await signatureOf(key, id, timestamp, bytes);\n // Every candidate is compared, whichever matches: the time taken says nothing.\n let matched = false;\n for (const candidate of signatures.split(' ')) {\n const [version, encoded] = candidate.split(',');\n const given = version === 'v1' && encoded !== undefined ? fromBase64(encoded) : null;\n\n if (given !== null && sameBytes(given, expected)) matched = true;\n }\n\n if (!matched) {\n throw invalid(\n \"No signature of the webhook matches the secret: not Mesub's, another endpoint's \" +\n 'secret, or a body changed on its way (parsed then serialized again?). ' +\n 'Verify the raw body.',\n );\n }\n\n // After the signature: a stale one is Mesub's, replayed or late on a clock.\n const skew = Date.now() - Number(timestamp) * 1000;\n if (Math.abs(skew) > tolerance) {\n throw invalid(\n `The webhook was signed ${Math.round(Math.abs(skew) / 1000)} s ` +\n `${skew > 0 ? 'ago' : 'ahead of this clock'}, beyond the ${tolerance / 1000} s ` +\n \"tolerance: a replay, or this server's clock is off.\",\n );\n }\n\n return webhookEventFrom(parsed(body, bytes), id);\n}\n\n/** The body as signed: the bytes received, or a string's UTF-8. */\nfunction bytesOf(body: WebhookBody): Uint8Array<ArrayBuffer> {\n if (typeof body === 'string') return new TextEncoder().encode(body);\n if (body instanceof Uint8Array) return new Uint8Array(body);\n if (body instanceof ArrayBuffer) return new Uint8Array(body.slice(0));\n\n throw new TypeError(\n 'body must be the raw body as received, a string or a Buffer, never parsed: the ' +\n 'signature is over its exact bytes. With Express, read it with ' +\n \"express.raw({ type: 'application/json' }); in a Next route, await request.text().\",\n );\n}\n\n/** The JSON Mesub signed. Not JSON, or not UTF-8, is a body this release cannot read. */\nfunction parsed(body: WebhookBody, bytes: Uint8Array): unknown {\n try {\n const text =\n typeof body === 'string'\n ? body\n : new TextDecoder('utf-8', { fatal: true }).decode(bytes);\n\n return JSON.parse(text);\n } catch (cause) {\n throw new MesubError('Mesub sent a webhook whose body is not JSON.', {\n status: null,\n code: 'unexpected',\n cause,\n });\n }\n}\n\n/** `whsec_` then base64: the bytes after the prefix are the HMAC key. */\nfunction keyOf(secret: unknown, name: string): Uint8Array<ArrayBuffer> {\n if (secret === undefined || secret === '') {\n throw new TypeError(\n typeof process === 'undefined'\n ? 'Missing Mesub webhook secret: there is no process.env here, so pass it: ' +\n '`{ secret: env.MESUB_WEBHOOK_SECRET }` or `new Mesub({ webhookSecret })`.'\n : 'Missing Mesub webhook secret: pass `{ secret }` or ' +\n '`new Mesub({ webhookSecret })`, or set MESUB_WEBHOOK_SECRET.',\n );\n }\n\n const key =\n typeof secret === 'string' && secret.startsWith(SECRET_PREFIX)\n ? fromBase64(secret.slice(SECRET_PREFIX.length))\n : null;\n\n if (key === null || key.length === 0) {\n throw new TypeError(\n `${name} must be the endpoint's signing secret, whsec_..., from its page in the ` +\n 'Mesub dashboard' +\n (typeof secret === 'string' && /^(SUB|PUB)_/.test(secret)\n ? ', not an API key.'\n : '.'),\n );\n }\n\n return key;\n}\n\n/** Checks the client's `webhookSecret`, or MESUB_WEBHOOK_SECRET, once at `new Mesub()`. */\nexport function webhookSecretOf(given: unknown): string | undefined {\n if (given !== undefined && typeof given !== 'string') {\n throw new TypeError('webhookSecret must be a string.');\n }\n\n const secret = given || envSecret();\n\n if (secret) keyOf(secret, given ? 'webhookSecret' : 'MESUB_WEBHOOK_SECRET');\n\n return secret || undefined;\n}\n\nfunction envSecret(): string | undefined {\n return typeof process === 'undefined' ? undefined : process.env?.['MESUB_WEBHOOK_SECRET'];\n}\n\n/** A header from a Fetch `Headers`, or a plain object whatever the case of its names. */\nfunction headerIn(headers: HeaderSource, name: string): string | undefined {\n if (typeof (headers as Headers).get === 'function') {\n return (headers as Headers).get(name) ?? undefined;\n }\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === name) return Array.isArray(value) ? value[0] : value;\n }\n\n return undefined;\n}\n\n/** HMAC-SHA256 of `id.timestamp.body`, as Standard Webhooks signs it. */\nasync function signatureOf(\n key: Uint8Array<ArrayBuffer>,\n id: string,\n timestamp: string,\n body: Uint8Array,\n): Promise<Uint8Array> {\n const prefix = new TextEncoder().encode(`${id}.${timestamp}.`);\n const signed = new Uint8Array(prefix.length + body.length);\n signed.set(prefix);\n signed.set(body, prefix.length);\n\n const hmac = await crypto.subtle.importKey(\n 'raw',\n key,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n\n return new Uint8Array(await crypto.subtle.sign('HMAC', hmac, signed));\n}\n\n/**\n * The three headers Mesub sends with a body, signed with that secret: for\n * `@mesub/node/testing`, and the tests.\n *\n * @internal\n */\nexport async function signedHeaders(\n secret: string,\n id: string,\n timestamp: number,\n body: string,\n): Promise<Record<string, string>> {\n const key = keyOf(secret, 'secret');\n const signature = await signatureOf(key, id, String(timestamp), new TextEncoder().encode(body));\n\n return {\n 'webhook-id': id,\n 'webhook-timestamp': String(timestamp),\n 'webhook-signature': `v1,${toBase64(signature)}`,\n };\n}\n\n/** Constant time over equal lengths; a length is no secret. */\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n\n let difference = 0;\n for (let index = 0; index < a.length; index += 1) {\n difference |= (a[index] ?? 0) ^ (b[index] ?? 0);\n }\n\n return difference === 0;\n}\n\nfunction fromBase64(text: string): Uint8Array<ArrayBuffer> | null {\n try {\n return Uint8Array.from(atob(text), (char) => char.charCodeAt(0));\n } catch {\n return null;\n }\n}\n\nfunction toBase64(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...bytes));\n}\n\nfunction invalid(message: string): MesubError {\n return new MesubError(message, { status: null, code: 'invalid_webhook' });\n}\n","import { MesubError, codeFor } from './errors.js';\nimport { numberOf } from './options.js';\nimport { API_VERSION, API_VERSION_HEADER, VERSION } from './version.js';\n\nexport type QueryValue = string | number | boolean | undefined;\n\nexport interface TransportConfig {\n apiKey: string;\n baseUrl: string;\n /** Sent with every call, before the SDK's own, which they never replace. */\n headers: Record<string, string>;\n fetch: typeof fetch;\n timeout: number;\n maxRetries: number;\n}\n\n/** What any one call may set for itself. */\nexport interface RequestOptions {\n /** Per attempt, in milliseconds. Defaults to the client's `timeout`. */\n timeout?: number;\n /**\n * Stops the call, and any wait for a retry: it then rejects with the\n * signal's reason, and is never sent again.\n */\n signal?: AbortSignal;\n}\n\n/** What one GET may change from the client's configuration. */\nexport interface CallOptions extends RequestOptions {\n maxRetries?: number;\n /**\n * Epoch ms by which the call settles, retries and waits included: each\n * attempt is cut at it, and a wait that would outlast it is not waited.\n */\n deadline?: number;\n /**\n * False to throw a 429 at once, whatever it asks: a guard falls back\n * rather than wait for the rate limit, and send more on top of it.\n */\n retryRateLimited?: boolean;\n}\n\n/** The id Mesub gives every request, on every answer (Mesub-io/backend#472). */\nconst REQUEST_ID_HEADER = 'Mesub-Request-Id';\n\nconst INITIAL_RETRY_DELAY = 500;\nconst MAX_RETRY_DELAY = 8_000;\nconst MAX_RETRY_AFTER = 60_000;\n\ntype Method = 'GET' | 'POST';\n\ntype Attempt =\n | { ok: true; body: unknown }\n | { ok: false; error: MesubError; retry: boolean; retryAfter: number | null };\n\n/** @internal Authenticated JSON calls to the Mesub API, with timeouts and retries. */\nexport class Transport {\n readonly #config: TransportConfig;\n\n constructor(config: TransportConfig) {\n this.#config = config;\n }\n\n async get(\n path: string,\n query: Record<string, QueryValue> = {},\n options: CallOptions = {},\n ): Promise<unknown> {\n const url = endpoint(this.#config.baseUrl, path);\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n return this.#send('GET', url, undefined, options);\n }\n\n /**\n * A POST with a JSON body, sent once: never retried here, whatever the\n * status, a timeout or a network error. A write that got no answer may\n * still have been done: its caller decides, knowing the route, whether\n * the same body may be sent again (`submit` does, Mesub-io/backend#190)\n * or the state read back.\n */\n async post(\n path: string,\n body: unknown,\n options: Omit<CallOptions, 'maxRetries'> = {},\n ): Promise<unknown> {\n return this.#send('POST', endpoint(this.#config.baseUrl, path), body, options);\n }\n\n async #send(method: Method, url: URL, body: unknown, options: CallOptions): Promise<unknown> {\n const maxRetries = method === 'POST' ? 0 : (options.maxRetries ?? this.#config.maxRetries);\n const { deadline, signal } = options;\n // As the client's own: NaN or 0 would cut the call as it is sent.\n numberOf('timeout', options.timeout, this.#config.timeout, { delay: true });\n\n for (let retry = 0; ; retry++) {\n const attempt = await this.#attempt(method, url, body, options);\n if (attempt.ok) return attempt.body;\n if (!attempt.retry || retry >= maxRetries) throw attempt.error;\n if (options.retryRateLimited === false && attempt.error.status === 429) {\n throw attempt.error;\n }\n\n const wait = attempt.retryAfter ?? backoff(retry);\n // Not even a Retry-After is waited past the deadline: the caller\n // falls back now rather than at the end of a wait it cannot afford.\n if (deadline !== undefined && Date.now() + wait >= deadline) {\n throw attempt.error;\n }\n await sleep(wait, signal);\n }\n }\n\n async #attempt(\n method: Method,\n url: URL,\n body: unknown,\n { deadline, signal, ...options }: CallOptions,\n ): Promise<Attempt> {\n // The caller's abort is theirs to handle: thrown as is, never retried.\n signal?.throwIfAborted();\n\n const perAttempt = options.timeout ?? this.#config.timeout;\n const left = deadline === undefined ? Infinity : deadline - Date.now();\n\n if (left <= 0) {\n const error = new MesubError('No time was left to call Mesub.', {\n status: null,\n code: 'unavailable',\n });\n return { ok: false, error, retry: false, retryAfter: null };\n }\n\n // Cut at the deadline when it comes before the attempt's own timeout.\n const timeout = Math.min(perAttempt, left);\n const cutByDeadline = timeout < perAttempt;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n const stop = () => controller.abort(signal?.reason);\n signal?.addEventListener('abort', stop, { once: true });\n\n try {\n const response = await this.#config.fetch(url, {\n method,\n headers: {\n ...this.#config.headers,\n Authorization: `Bearer ${this.#config.apiKey}`,\n Accept: 'application/json',\n 'User-Agent': `@mesub/node/${VERSION}`,\n [API_VERSION_HEADER]: API_VERSION,\n ...(body !== undefined && { 'Content-Type': 'application/json' }),\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n signal: controller.signal,\n // Every call carries the key, and Mesub never redirects: a\n // redirect is never followed, so the key is never sent on (#171).\n redirect: 'manual',\n });\n\n if (isRedirect(response)) {\n await response.body?.cancel();\n const to = response.headers.get('location');\n const error = new MesubError(\n `${url.pathname} answered a redirect${to ? ` to ${to}` : ''}, and Mesub never ` +\n `redirects: is baseUrl (${this.#config.baseUrl}) the Mesub API?`,\n {\n status: response.status || null,\n code: 'unexpected',\n requestId: response.headers.get(REQUEST_ID_HEADER),\n },\n );\n return { ok: false, error, retry: false, retryAfter: null };\n }\n\n const text = await response.text();\n const requestId = response.headers.get(REQUEST_ID_HEADER);\n\n if (response.ok) {\n try {\n return { ok: true, body: JSON.parse(text) };\n } catch (cause) {\n const error = new MesubError('Mesub answered with a body that is not JSON.', {\n status: response.status,\n code: 'unexpected',\n body: text,\n requestId,\n cause,\n });\n return { ok: false, error, retry: false, retryAfter: null };\n }\n }\n\n const answered = parsed(text);\n const asked = retryAfter(response.headers.get('retry-after'));\n const error =\n response.status === 404 && !isMesubErrorBody(answered)\n ? new MesubError(\n `${url.pathname} answered 404 with no Mesub error: is baseUrl ` +\n `(${this.#config.baseUrl}) the Mesub API?`,\n {\n status: 404,\n code: 'unexpected',\n body: answered,\n retryAfter: asked,\n requestId,\n },\n )\n : errorFrom(response.status, answered, asked, requestId);\n\n return {\n ok: false,\n error,\n // Sent again (a GET only, a POST never is) when the error is `retryable`:\n // Mesub's flag when the body has one, the status's otherwise.\n retry: error.retryable,\n retryAfter: asked === null ? null : Math.min(asked, MAX_RETRY_AFTER),\n };\n } catch (cause) {\n if (signal?.aborted) throw signal.reason;\n\n if (controller.signal.aborted && cutByDeadline) {\n const error = new MesubError(\n `Mesub did not answer within the ${Math.round(timeout)} ms left before the deadline.`,\n { status: null, code: 'unavailable', cause },\n );\n return { ok: false, error, retry: false, retryAfter: null };\n }\n\n const message = controller.signal.aborted\n ? `Mesub did not answer within ${perAttempt} ms.`\n : `Could not reach Mesub: ${cause instanceof Error ? cause.message : String(cause)}`;\n const error = new MesubError(message, { status: null, code: 'unavailable', cause });\n return { ok: false, error, retry: true, retryAfter: null };\n } finally {\n clearTimeout(timer);\n signal?.removeEventListener('abort', stop);\n }\n }\n}\n\n/** A 3xx, or the opaque answer a browser's fetch gives for one under `redirect: 'manual'`. */\nfunction isRedirect(response: Response): boolean {\n return response.type === 'opaqueredirect' || (response.status >= 300 && response.status < 400);\n}\n\n/**\n * A path under the base URL, which may carry its own path:\n * `https://proxy.example.com/mesub` and `/v1/access` make\n * `https://proxy.example.com/mesub/v1/access`. Never `new URL(path, base)`,\n * which drops the base's path for a path starting with `/`.\n */\nfunction endpoint(baseUrl: string, path: string): URL {\n return new URL(baseUrl + path);\n}\n\n/**\n * The error a Mesub error body describes since the back's error codes\n * (Mesub-io/backend#180): Nest's `statusCode`, `error` and `message`, plus a\n * stable `code`, a `retryable` flag and the `request_id`. Without them, as from a proxy in\n * front, the status alone decides.\n */\nfunction errorFrom(\n status: number,\n body: unknown,\n retryAfter: number | null,\n header: string | null,\n): MesubError {\n const { code, retryable, request_id } = isRecord(body) ? body : {};\n const apiCode = typeof code === 'string' ? code : null;\n\n return new MesubError(messageFrom(body, status), {\n status,\n code: codeFor(status, apiCode),\n apiCode,\n retryable: typeof retryable === 'boolean' ? retryable : retryableStatus(status),\n body,\n retryAfter,\n // The header first: the body's says the same, and is the fallback\n // when a proxy in front drops the header (Mesub-io/backend#472).\n requestId: header ?? (typeof request_id === 'string' ? request_id : null),\n });\n}\n\n/**\n * What the status says, when the body does not: the Stainless clients' rules,\n * but for a 409, which is Mesub's final word on a state, never a race to retry.\n */\nfunction retryableStatus(status: number): boolean {\n return status === 408 || status === 429 || status >= 500;\n}\n\n/**\n * Milliseconds from a `Retry-After` in seconds or as an HTTP date, as sent:\n * what the error carries. A GET waits it capped at `MAX_RETRY_AFTER`.\n */\nfunction retryAfter(header: string | null): number | null {\n if (header === null || header.trim() === '') return null;\n const seconds = Number(header);\n const ms = Number.isNaN(seconds) ? Date.parse(header) - Date.now() : seconds * 1000;\n if (!Number.isFinite(ms) || ms < 0) return null;\n return ms;\n}\n\n/** 500 ms, then 1 s, ... up to 8 s, minus up to 25% of jitter. */\nfunction backoff(retry: number): number {\n const delay = Math.min(INITIAL_RETRY_DELAY * 2 ** retry, MAX_RETRY_DELAY);\n return delay * (1 - Math.random() * 0.25);\n}\n\n/** The NestJS error body is `{ message, error, statusCode }`, `message` a string or a list. */\nfunction messageFrom(body: unknown, status: number): string {\n const fallback = `Mesub answered with HTTP ${status}.`;\n if (!isRecord(body)) return fallback;\n\n const { message, error } = body;\n if (typeof message === 'string' && message !== '') return message;\n if (Array.isArray(message)) {\n const parts = message.filter((part): part is string => typeof part === 'string');\n if (parts.length > 0) return parts.join('; ');\n }\n if (typeof error === 'string' && error !== '') return error;\n return fallback;\n}\n\n/**\n * Whether a 404 came from the Mesub API rather than from whatever else lives\n * at a wrong `baseUrl`: a JSON error body, as Nest writes it (`statusCode`\n * and `message`) or with a `code`. Nest's own answer for a route it does not\n * have (`Cannot GET /api/v1/access`) is not one, though the back now codes it\n * `not_found`: a path prefix too many.\n */\nfunction isMesubErrorBody(body: unknown): boolean {\n if (!isRecord(body)) return false;\n\n const { code, statusCode, message } = body;\n if (typeof message === 'string' && /^Cannot [A-Z]+ \\//.test(message)) return false;\n\n return typeof code === 'string' || typeof statusCode === 'number';\n}\n\n/** The body as JSON when it parses, the text as it came otherwise. */\nfunction parsed(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** @internal Rejects with the signal's reason as soon as it aborts. */\nexport function sleep(ms: number, signal: AbortSignal | undefined): Promise<void> {\n return new Promise((resolve, reject) => {\n const stop = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', stop);\n resolve();\n }, ms);\n signal?.addEventListener('abort', stop, { once: true });\n });\n}\n","import type {\n BlockedReason,\n Customer,\n EndReason,\n LateReason,\n PaymentStatus,\n PullOutcome,\n StopReason,\n} from './answer.js';\nimport { customerOf } from './customer.js';\nimport { type MesubErrorCode, MesubError, MesubSubmitError } from './errors.js';\nimport { numberOf } from './options.js';\nimport { type QueryValue, type RequestOptions, type Transport, sleep } from './transport.js';\nimport {\n confirmResultFrom,\n serverSubscriptionFrom,\n serverSubscriptionListFrom,\n subscriptionAttemptListFrom,\n submitResultFrom,\n subscribeTransactionFrom,\n walletTransactionFrom,\n} from './validate.js';\n\n/**\n * Subscribing from your own server, with the API key (Mesub-io/backend#143):\n * `POST /v1/subscriptions`, a fresh transaction, its submit, the reads, and what stops one\n * (Mesub-io/backend#276): cancel, resume and close. The types copy the\n * back's `server-subscriptions.service.ts`, which stays the source of truth.\n * Snake case, as the API serves it.\n */\n\n/**\n * A subscription's status. Unlike `/v1/access`, may be `expired`: a checkout\n * nobody signed (Mesub-io/backend#139). `superseded` is a stopped one the\n * wallet came back over (Mesub-io/backend#213): a newer row holds the\n * subscription. Never `none`: a row exists. `cancelled` reads `ended` once\n * its end date passed, as on `/v1/access` (Mesub-io/backend#236).\n */\nexport type ServerSubscriptionStatus =\n | 'pending'\n | 'active'\n | 'cancelled'\n | 'unpaid'\n | 'stopped'\n | 'ended'\n | 'failed'\n | 'expired'\n | 'superseded';\n\n/** One subscription to your project's plans, as `GET /v1/subscriptions/:id` answers it. */\nexport interface ServerSubscription {\n id: string;\n status: ServerSubscriptionStatus;\n /** A seat parked over the project's cap, status unchanged, as `/v1/access` answers it. */\n paused: boolean;\n /** Why it ended, only on `ended`, as `/v1/access` answers it. */\n end_reason: EndReason | null;\n /** Why it is late, on `unpaid` or `stopped`, as `/v1/access` answers it. */\n late_reason: LateReason | null;\n /** Why it stopped, only on `stopped`, as `/v1/access` answers it. */\n stop_reason: StopReason | null;\n /** Whether it grants access now, as `/v1/access` answers it. */\n access: boolean;\n /** `paid`, `late` or `none`, as `/v1/access` answers it. */\n payment_status: PaymentStatus;\n /**\n * Since when its charges are blocked for a reason that is not the\n * subscriber's, and whose cause it is, as `/v1/access` answers them\n * (Mesub-io/backend#398): both null unless blocked. Read as null from a\n * back that predates them.\n */\n blocked_since: string | null;\n blocked_reason: BlockedReason | null;\n /** The plan's slug. */\n plan: string | null;\n wallet: string;\n email: string | null;\n external_id: string | null;\n /** The period paid for last; null before the first. */\n current_period_start: string | null;\n current_period_end: string | null;\n /**\n * The next charge on a running one, the next retry on a late one: never\n * both, and neither once the plan's end leaves no pull to run.\n */\n next_charge_at: string | null;\n next_retry_at: string | null;\n /**\n * Free only, as `/v1/access` answers it (Mesub-io/backend#191): when hand\n * retries close, the plan's end when that comes first; past it the\n * subscription stops. Read as null from a back that predates it.\n */\n retry_deadline: string | null;\n /**\n * The retry due at `next_retry_at` and how many the missed period gets\n * (Mesub-io/backend#289): 2 and 3 before the second retry of three. Both\n * null unless Mesub retries on its own: not late, paused, stopped, on\n * Free, or on a plan that ends before the retry. Read as null from a\n * back that predates them.\n */\n next_retry_number: number | null;\n retries_allowed: number | null;\n /**\n * When access ends unless a pull renews it; null while `access` is false.\n * Never later than the plan's end.\n */\n access_until: string | null;\n /** Whether the subscriber can close it now, as `/v1/access` answers it. */\n closable: boolean;\n /** When it can be closed, if not now and a date is known, as `/v1/access` answers it. */\n closable_from: string | null;\n created_at: string;\n confirmed_at: string | null;\n}\n\n/** What subscribing costs in SOL, in lamports, as strings: a u64 does not survive JSON. */\nexport interface SubscribeCosts {\n /** Given back to the subscriber when the accounts are closed. `authority` is null if it exists. */\n rent: { subscription: string; authority: string | null; total: string };\n /** Spent: the network's fee per signature, the wallet's and Mesub's. */\n fee: { signatures: number; per_signature: string; priority: string; total: string };\n total: string;\n}\n\n/** What `create` and `transaction` answer: what the wallet signs, terms first, then the transaction. */\nexport interface SubscribeTransaction {\n subscription: { id: string; status: ServerSubscriptionStatus };\n /** Base64, signed by nobody: the wallet signs it with signTransaction, without sending it. */\n transaction: string;\n last_valid_block_height: string;\n /**\n * When `last_valid_block_height` is expected to pass, about half a minute\n * after the answer (Mesub-io/backend#471): an estimate that errs early,\n * for a countdown or to ask `transaction` for a fresh one in time. The\n * height is the truth: rebuild after `transaction_expired`, before this\n * time as after it. Null from a back that predates it.\n */\n transaction_expires_at: string | null;\n costs: SubscribeCosts;\n /** Signed as is with the wallet's signMessage, over its UTF-8 bytes, before `expires_at`. */\n terms: { message: string; expires_at: string };\n}\n\n/**\n * What `submit` settled on: what Mesub answered, or, when its answer was lost\n * and the subscription read back is `active` or `cancelled`, that one.\n */\nexport interface SubmitResult {\n /**\n * `active`, or `cancelled` if the wallet set an end (`ended` once that\n * end passed); `pending` or `failed` with a `reason`.\n */\n subscription: ServerSubscription;\n /**\n * Mesub's own, set when nothing landed (`pending`) or what landed is not\n * Mesub's (`failed`). Never made up by the SDK. A sentence for a person,\n * which Mesub may reword: branch on `reason_code`.\n */\n reason?: string;\n /** Why, for a program: set exactly when `reason` is, from a back that serves it. */\n reason_code?: ConfirmReasonCode;\n}\n\n/**\n * What `cancel`, `resume` and `close` answer: a transaction only the wallet\n * signs, and sends itself. Mesub signs nothing of it.\n */\nexport interface WalletTransaction {\n /** Base64, signed by nobody: the wallet signs and sends it, then names its signature. */\n transaction: string;\n /** The block height past which it can no longer land. */\n last_valid_block_height: string;\n /** When that height is expected to pass, as `SubscribeTransaction` says it. */\n transaction_expires_at: string | null;\n}\n\n/** What the wallet answered when it sent the transaction of `cancel`, `resume` or `close`. */\nexport interface ConfirmParams {\n /** The transaction's signature, base58. */\n signature: string;\n}\n\n/** What `confirmCancel`, `confirmResume` and `confirmClose` settled on. */\nexport interface ConfirmResult {\n /** The subscription as it is now: moved, or as it was with a `reason`. */\n subscription: ServerSubscription;\n /**\n * Mesub's own, set when nothing changed: the transaction failed, expired\n * before it landed, or the subscription moved on meanwhile. A sentence\n * for a person, which Mesub may reword: branch on `reason_code`.\n */\n reason?: string;\n /** Why, for a program: set exactly when `reason` is, from a back that serves it. */\n reason_code?: ConfirmReasonCode;\n}\n\n/**\n * Why a submit or a confirm settled nothing, or not what was built\n * (Mesub-io/backend#470): stable, snake case, never reworded. An open family:\n * Mesub adds codes, so keep a default branch; one newer than this release is\n * handed on as it is.\n *\n * - `not_our_transaction`: not a transaction Mesub co-signed over terms the\n * wallet signed for this subscription.\n * - `transaction_failed`: it landed and the chain refused it.\n * - `transaction_expired`: its blockhash ran out before it landed.\n * - `nothing_landed`: on a submit, nothing stands at its address yet.\n * - `subscription_not_on_chain`: on a cancel or a resume, it is gone on chain.\n * - `subscription_mismatch`: what landed names another wallet, plan, amount\n * or period: now `failed`.\n * - `dev_not_running`: Mesub's own Dev plan landed cancelled or part paid.\n * - `earlier_subscription_running`: a comeback paid while the one it came\n * back over ran again.\n * - `subscription_remade`: on a submit, closed and made again without Mesub.\n * - `cancellation_not_landed`, `resume_not_landed`, `close_not_landed`: the\n * chain does not show the action.\n * - `subscription_changed`: it moved on while the cancellation was read.\n * - `subscription_not_cancelled`: on a resume, nothing to resume.\n * - `cancelled_subscription_remade`: on a resume, closed and made again\n * without Mesub: it stays `cancelled`.\n * - `resume_unverified`: on a resume, the one on chain cannot be told to be\n * the one cancelled: it stays `cancelled`.\n */\nexport type ConfirmReasonCode =\n | 'not_our_transaction'\n | 'transaction_failed'\n | 'transaction_expired'\n | 'nothing_landed'\n | 'subscription_not_on_chain'\n | 'subscription_mismatch'\n | 'dev_not_running'\n | 'earlier_subscription_running'\n | 'subscription_remade'\n | 'cancellation_not_landed'\n | 'resume_not_landed'\n | 'close_not_landed'\n | 'subscription_changed'\n | 'subscription_not_cancelled'\n | 'cancelled_subscription_remade'\n | 'resume_unverified'\n // Any newer code, while the names above still complete.\n | (string & {});\n\n/** One page of a customer's subscriptions, newest first. */\nexport interface ServerSubscriptionList {\n data: ServerSubscription[];\n /** Ask for the next page with `starting_after` set to the last id of this one. */\n has_more: boolean;\n}\n\n/**\n * One pull attempt of a subscription, as `GET /v1/subscriptions/:id/attempts`\n * answers it (Mesub-io/backend#289): an attempt of `/v1/access`, with its id,\n * whether it was a retry and the period it was for.\n */\nexport interface SubscriptionAttempt {\n id: string;\n attempted_at: string;\n /** One of `PullOutcome` today; a newer one is handed on as is. */\n outcome: PullOutcome;\n /**\n * Mesub's short reason, null on a paid one, as on `ServedAttempt`: snake\n * case, an open string with the `program_${number}` and\n * `solana_${number}` families.\n */\n reason: string | null;\n /**\n * In the mint's smallest unit, as a string. What was asked for; on a\n * `paid` one that is what the transfer moved.\n */\n amount: string;\n /**\n * That amount in dollars at the price of the instant it was attempted, a\n * decimal string such as `\"9.99\"`. Null until Mesub priced it, when it\n * knows no price, and from a Mesub that predates it.\n */\n amount_usd: string | null;\n /**\n * What the receiver's account got of a `paid` one, in the mint's smallest\n * unit: `amount` less the fee a Token-2022 mint withholds on transfers.\n * Null when not known (an attempt not paid, one recorded before Mesub\n * kept it) and from a Mesub that predates it.\n */\n amount_received: string | null;\n /** The transaction that paid. Null on every outcome but `paid`, a refused one included. */\n signature: string | null;\n /** The subscription had already failed on this period. */\n retry: boolean;\n /**\n * On a retry of the plan's, which one and out of how many, as recorded\n * when it ran: 1 is the first after the missed charge. Both null on a\n * first try, on a hand retry on Dev or Business, on an attempt that kept\n * none, and from a back that does not serve them.\n */\n retry_number: number | null;\n retries_allowed: number | null;\n /** The start of the period it was for; null when Mesub recorded none. */\n period_start: string | null;\n}\n\n/** One page of a subscription's attempts, newest first, and what it paid in all. */\nexport interface SubscriptionAttemptList {\n data: SubscriptionAttempt[];\n /** Ask for the next page with `starting_after` set to the last id of this one. */\n has_more: boolean;\n /**\n * Its `paid` attempts since it began and the sum of what they moved, in\n * the mint's smallest unit: counted by Mesub over all of them, not this page.\n */\n paid: { count: number; amount: string };\n}\n\n/** Which page of a subscription's attempts. */\nexport interface AttemptsParams {\n /** 1 to 100, 20 by default. */\n limit?: number;\n /** The id of the last attempt of the previous page. */\n starting_after?: string;\n}\n\nexport interface SubscribeParams {\n /** The plan's slug. */\n plan: string;\n /** The wallet that signs and pays. */\n wallet: string;\n /** Where the subscriber's notices go. Not verified by Mesub. */\n email?: string;\n /** Your own id for this customer, handed back as given. */\n external_id?: string;\n}\n\nexport interface SubmitParams {\n /** The transaction of `create`, signed by the wallet, base64. */\n transaction: string;\n /** The wallet's signMessage over `terms.message`, base58. */\n terms_signature: string;\n}\n\nexport interface SubmitOptions extends RequestOptions {\n /**\n * Per send, in milliseconds: 90 s by default. Lower it behind a proxy\n * or a function with a shorter limit (Cloudflare cuts at 100 s); a send\n * cut short may still be co-signed and land, so read the subscription\n * back afterwards.\n */\n timeout?: number;\n /**\n * The whole submit in milliseconds, its sends and the waits between\n * them: `timeout` plus 30 s by default, so 120 s. A send is cut at it,\n * and a wait that would outlast it is not waited. Reading the\n * subscription back once it is spent takes 10 s more at most, retries\n * included: 130 s in all by default.\n */\n budget?: number;\n}\n\nexport interface ConfirmOptions extends RequestOptions {\n /**\n * In milliseconds: 90 s by default. Mesub waits for the transaction to\n * land or its blockhash to expire, about a minute. A confirm cut short\n * changed nothing: send it again with the same signature.\n */\n timeout?: number;\n}\n\n/**\n * Whose subscriptions to list: a `Customer`, named exactly one way and\n * normalised as `access` names them, and what narrows the page.\n */\nexport type ListParams = Customer & {\n /** Only this plan's, by slug. */\n plan?: string;\n /** 1 to 100, 20 by default. */\n limit?: number;\n /** The id of the last subscription of the previous page. */\n starting_after?: string;\n};\n\n/**\n * How long one send of `submit` waits by default. Mesub answers once the\n * transaction landed or its blockhash expired, about 150 blocks: a minute,\n * give or take.\n */\nconst SUBMIT_TIMEOUT = 90_000;\n/**\n * What the whole submit gets beyond its first send's timeout, by default:\n * room for a replay, which Mesub answers from the chain, at once if the\n * blockhash has expired by then.\n */\nconst REPLAY_ROOM = 30_000;\n/** The first send and two replays, at most. */\nconst MAX_SENDS = 3;\n/** The wait before a replay when Mesub named none: what `network_unavailable` asks. */\nconst REPLAY_WAIT = 10_000;\n/**\n * The read back once the sends are done, retries included: a submit never\n * takes longer than its budget plus this.\n */\nconst READ_BACK_TIME = 10_000;\n\n/** How long a confirm waits by default: as one send of `submit`, for the same chain. */\nconst CONFIRM_TIMEOUT = SUBMIT_TIMEOUT;\n\n/** What stops, takes back or closes a subscription: each a route, and its `/confirm`. */\ntype Action = 'cancel' | 'resume' | 'close';\n\n/** How a submit whose outcome is unknown is settled from the subscription read back. */\ninterface Unsettled {\n sends: number;\n /** The last send's error. */\n cause: MesubError;\n /** Whose status, apiCode, body and retryAfter the error keeps. */\n from: MesubError;\n code: MesubErrorCode;\n retryable: boolean;\n /** What the message says first. */\n lead: string;\n}\n\nexport class Subscriptions {\n readonly #transport: Transport;\n /**\n * Told of every subscription `submit`, `retrieve` and `list` answer, so\n * the client drops the cached access answers one that landed outdates.\n */\n readonly #seen: (subscription: ServerSubscription) => Promise<void>;\n\n /**\n * Told of the subscription a confirm moved, so the client drops every\n * access answer cached for its customer, the yes as well as the no.\n */\n readonly #changed: (subscription: ServerSubscription) => Promise<void>;\n\n /** @internal */\n constructor(\n transport: Transport,\n seen: (subscription: ServerSubscription) => Promise<void> = async () => {},\n changed: (subscription: ServerSubscription) => Promise<void> = seen,\n ) {\n this.#transport = transport;\n this.#seen = seen;\n this.#changed = changed;\n }\n\n /**\n * Reserves a subscription to that plan for that wallet, and builds what\n * it signs: `terms.message`, then `transaction`. Hand both to your front,\n * then their signatures to `submit`. Called again for the same plan,\n * wallet and customer (`external_id`, else `email`) while nothing landed,\n * it answers the same subscription with a fresh transaction; by its id,\n * `transaction` does. Another\n * customer naming that wallet gets a subscription of its own\n * (Mesub-io/backend#311): only the one the wallet signs for lands, and\n * the others then expire. Sent once, never retried.\n */\n async create(\n params: SubscribeParams,\n options: RequestOptions = {},\n ): Promise<SubscribeTransaction> {\n const { plan, wallet, email, external_id } = params;\n const body = { plan, wallet, email, external_id };\n\n return subscribeTransactionFrom(\n await this.#transport.post('/v1/subscriptions', body, options),\n );\n }\n\n /**\n * Relays what the wallet signed. Mesub checks it, co-signs, sends it and\n * waits for the chain, so one send takes up to a minute or so: 90 s\n * unless `options.timeout` says otherwise.\n *\n * When the send gets no answer that says what became of it (a timeout,\n * a network error, a 5xx), or an error Mesub marks `retryable`, the same\n * request, the same body, is sent again, up to twice, after the\n * `Retry-After` Mesub asked for or 10 s, within `options.budget` (120 s\n * by default). That is safe since Mesub-io/backend#190 and #202: Mesub\n * recognises the transaction and the terms signature it already\n * co-signed, signs nothing again, and answers from the chain or from the\n * subscription it settled. Its answer is then returned as if the first\n * send had got it, a `pending` with Mesub's own `reason` included.\n *\n * When no send got an answer, the subscription is read back once, within\n * 10 s: it is returned if `active` or `cancelled` (it landed), and\n * otherwise a `MesubSubmitError` `unavailable` is thrown with it attached\n * as `subscription` (null if that read failed too): the wallet may have\n * paid and the transaction may still land, so read it again with\n * `retrieve` rather than create anew. A 2xx this SDK cannot read is read\n * back the same way, and thrown as `unexpected`; so is a replay refused\n * with `not_awaiting_signature` after a send that got no answer, thrown\n * as that `conflict`.\n *\n * A 503 `unavailable` (too many submits waiting on the project,\n * Mesub-io/backend#505) says nothing was signed or sent: it is replayed\n * after its `Retry-After` (2 s) like any retryable refusal, and when the\n * replays run out it is thrown as is, `retryable`, with no read back,\n * unless an earlier send got no answer.\n *\n * Any other refusal (`terms_expired`, `transaction_expired`, ...) is\n * thrown as is. After `transaction_expired` nothing was sent and the same\n * submit cannot land: call `transaction(id)` for a new one, never\n * `create` again.\n * An abort through `options.signal` stops everything, sends, waits and\n * the read back, and rejects with the signal's reason; a send already out\n * may have been co-signed, so read the subscription back.\n *\n * A subscription returned with `access` (`active`, or `cancelled` before\n * its end) drops the `/v1/access` answers cached for that customer (its\n * wallet, external id and email) that still say no: `hasAccess` right\n * after asks Mesub again.\n */\n async submit(\n id: string,\n params: SubmitParams,\n options: SubmitOptions = {},\n ): Promise<SubmitResult> {\n const result = await this.#submit(id, params, options);\n\n await this.#seen(result.subscription);\n return result;\n }\n\n /**\n * A fresh transaction for a subscription still `pending`, with the terms\n * it names (Mesub-io/backend#427). A transaction lives about half a\n * minute, the terms five: have the terms signed first, call this when the\n * wallet is ready, have it sign the transaction at once, then `submit`.\n * Always sign the transaction of the last call. It is also what follows\n * a submit refused with `transaction_expired`.\n *\n * A terms signature you hold is still good when `terms.message` is, to\n * the letter, the message the wallet signed. Another text is new terms:\n * have the wallet sign it, the old signature is refused\n * (`invalid_terms_signature`).\n *\n * Reserves nothing and sends no body: the wallet, the plan and the\n * customer are the subscription's own. Refused with `apiCode`\n * `not_awaiting_signature` once it is signed, expired or ended (call\n * `create` again for an expired one), and with what `create` refuses of\n * a plan or a wallet (`plan_ended`, `insufficient_balance`,\n * `already_subscribed`, ...). Sent once, never retried: asking twice is\n * safe, only the last transaction is to be signed.\n */\n async transaction(id: string, options: RequestOptions = {}): Promise<SubscribeTransaction> {\n return subscribeTransactionFrom(\n await this.#transport.post(`${pathOf(id)}/transaction`, undefined, options),\n 'POST /v1/subscriptions/:id/transaction',\n );\n }\n\n async #submit(id: string, params: SubmitParams, options: SubmitOptions): Promise<SubmitResult> {\n const path = `${pathOf(id)}/submit`;\n // Built once: every send carries exactly this body.\n const body = { transaction: params.transaction, terms_signature: params.terms_signature };\n const { signal } = options;\n // Checked here too: the budget is worked out from it.\n const timeout = numberOf('timeout', options.timeout, SUBMIT_TIMEOUT, { delay: true });\n const budget = options.budget ?? timeout + REPLAY_ROOM;\n if (!Number.isFinite(budget) || budget <= 0) {\n throw new TypeError(`budget must be a positive number of ms, not ${budget}.`);\n }\n const deadline = Date.now() + budget;\n /** Whether a send got no answer that says what became of it. */\n let lost = false;\n /** The last send's error that came with a response. */\n let responded: MesubError | null = null;\n\n for (let sent = 1; ; sent++) {\n let error: MesubError;\n try {\n const answer = await this.#transport.post(path, body, {\n timeout,\n deadline,\n ...(signal !== undefined && { signal }),\n });\n return submitResultFrom(answer);\n } catch (caught) {\n // The caller's abort, as is: nothing more is sent nor read.\n if (!(caught instanceof MesubError)) throw caught;\n error = caught;\n }\n\n const lostBefore = lost;\n if (unanswered(error)) lost = true;\n if (error.status !== null) responded = error;\n const unsettled = { sends: sent, cause: error, from: error, lead: error.message };\n\n // Mesub answered, and this SDK cannot read what: the row says.\n if (error.status !== null && error.status < 300) {\n return this.#settle(id, signal, {\n ...unsettled,\n code: 'unexpected',\n retryable: false,\n });\n }\n // The row moved on (failed, expired) since a send that got no\n // answer, which may have been co-signed: the row says what it is.\n if (lostBefore && error.apiCode === 'not_awaiting_signature') {\n return this.#settle(id, signal, {\n ...unsettled,\n code: error.code,\n retryable: error.retryable,\n });\n }\n\n // Refused while the billing period turns: the same transaction is\n // not taken after the wait, a new one is. Nothing to send again.\n const rebuild = error.apiCode === 'comeback_period_rolling';\n const replay = !rebuild && (error.status === null || error.retryable);\n const wait = error.retryAfter ?? REPLAY_WAIT;\n if (!replay || sent >= MAX_SENDS || Date.now() + wait >= deadline) {\n // A refusal is Mesub's word on the request, whatever was lost\n // before. A send that got no answer, or a retryable refusal\n // after one, leaves it to the row.\n if (!lost || (!unanswered(error) && !error.retryable)) throw error;\n return this.#settle(id, signal, {\n ...unsettled,\n from: responded ?? error,\n code: 'unavailable',\n retryable: true,\n lead:\n `Mesub never said what became of the submit, sent ${sent} ` +\n `${sent === 1 ? 'time' : 'times'}: ${error.message}`,\n });\n }\n\n await sleep(wait, signal);\n }\n }\n\n /**\n * A submit whose outcome is unknown, settled from the subscription read\n * back: returned if it landed, thrown with it otherwise.\n */\n async #settle(\n id: string,\n signal: AbortSignal | undefined,\n { sends, cause, from, code, retryable, lead }: Unsettled,\n ): Promise<SubmitResult> {\n const subscription = await this.#readBack(id, signal);\n\n if (subscription?.status === 'active' || subscription?.status === 'cancelled') {\n return { subscription };\n }\n\n const then =\n subscription === null\n ? 'Reading the subscription back failed too'\n : `The subscription read back is ${subscription.status}`;\n\n throw new MesubSubmitError(\n `${lead} ${then}: read it again with retrieve before creating anew.`,\n {\n status: from.status,\n code,\n apiCode: from.apiCode,\n retryable,\n body: from.body,\n retryAfter: from.retryAfter,\n requestId: from.requestId,\n cause,\n subscription,\n sends,\n },\n );\n }\n\n /**\n * The subscription after a submit, within `READ_BACK_TIME`: no attempt\n * nor retry wait goes past it. Null when it cannot be read; the caller's\n * abort is thrown as is.\n */\n async #readBack(\n id: string,\n signal: AbortSignal | undefined,\n ): Promise<ServerSubscription | null> {\n try {\n const body = await this.#transport.get(\n pathOf(id),\n {},\n {\n deadline: Date.now() + READ_BACK_TIME,\n ...(signal !== undefined && { signal }),\n },\n );\n return serverSubscriptionFrom(body);\n } catch (error) {\n if (signal?.aborted) throw error;\n return null;\n }\n }\n\n /**\n * Builds the transaction that cancels an `active`, `unpaid` or `stopped`\n * subscription. Hand it to your front: the subscription's wallet signs\n * and sends it, then `confirmCancel` takes the signature. Nothing is\n * cancelled until then. Sent once, never retried.\n *\n * One paid up keeps its access to the end of its period; a late or\n * stopped one has none, and its missed period is never collected.\n */\n async cancel(id: string, options: RequestOptions = {}): Promise<WalletTransaction> {\n return this.#build('cancel', id, options);\n }\n\n /**\n * Settles a cancellation the wallet sent, by the signature it answered:\n * `cancelled`, or the subscription as it was with a `reason` when nothing\n * landed. Mesub waits for the chain, so up to a minute or so: 90 s unless\n * `options.timeout` says otherwise. Sent once, never retried; the same\n * confirm sent again is answered the same.\n *\n * Settled without a `reason`, it drops every `/v1/access` answer cached\n * for that customer on that plan: `access` right after asks Mesub again.\n */\n async confirmCancel(\n id: string,\n params: ConfirmParams,\n options: ConfirmOptions = {},\n ): Promise<ConfirmResult> {\n return this.#confirm('cancel', id, params, options);\n }\n\n /**\n * Builds the transaction that takes a `cancelled` subscription back,\n * before the period it paid for is over. Hand it to your front: the\n * wallet signs and sends it, then `confirmResume` takes the signature.\n * Sent once, never retried.\n */\n async resume(id: string, options: RequestOptions = {}): Promise<WalletTransaction> {\n return this.#build('resume', id, options);\n }\n\n /** As `confirmCancel`, for a resume: `active` again, or a `reason`. */\n async confirmResume(\n id: string,\n params: ConfirmParams,\n options: ConfirmOptions = {},\n ): Promise<ConfirmResult> {\n return this.#confirm('resume', id, params, options);\n }\n\n /**\n * Builds the transaction that closes the wallet's authorisation on a\n * subscription that is over: its rent goes back to the wallet. Mesub\n * builds it for a cancelled one past its end; for a stopped one\n * cancelled on chain and past its end, or whose authority is closed for\n * good; and for one `ended` with its plan (`end_reason` `plan_removed`,\n * `plan_replaced` or `plan_ended`). A stopped one that nothing cancelled\n * is refused with `subscription_not_cancelled`: cancel it first.\n * Hand the transaction to your front: the wallet signs and sends it,\n * then `confirmClose` takes the signature. Sent once, never retried.\n */\n async close(id: string, options: RequestOptions = {}): Promise<WalletTransaction> {\n return this.#build('close', id, options);\n }\n\n /** As `confirmCancel`, for a close: `ended` with `end_reason` `closed`, or a `reason`. */\n async confirmClose(\n id: string,\n params: ConfirmParams,\n options: ConfirmOptions = {},\n ): Promise<ConfirmResult> {\n return this.#confirm('close', id, params, options);\n }\n\n /**\n * Pays a late payment now (Mesub-io/backend#354): the pull of the missed\n * period runs at once rather than at its next retry, with nothing for the\n * wallet to sign, since it approved the plan when it subscribed. Answers\n * the subscription, still `unpaid`: the outcome comes with the pull, as\n * the `subscription.renewed` or `subscription.payment_failed` webhook.\n *\n * The merchant's hand retry from the dashboard follows the same rules: on\n * Free it is one of the missed period's three, whoever fires it; on Dev\n * and Business it runs beside the schedule, the wait between two doubling.\n * Mesub refuses it first when it cannot succeed, so it never spends a\n * retry for nothing: `apiCode` `insufficient_balance` (top up, then pay\n * again), `retry_cannot_succeed` (the wallet no longer approves it, its\n * token account is frozen, or its authority is closed), `retry_too_soon`\n * (`retryAfter` says when), `retries_spent`, `retry_deadline_passed`,\n * `subscription_not_late`, `subscription_paused`, `plan_ended`. Two more\n * are `retryable`: `subscription_changed` (two retries met, or a pull\n * moved the subscription on) and, on Free, `pull_running` (a pull runs\n * now). Read the subscription back before you ask again. A 503 is\n * `retryable` too, and nothing changed: `unavailable` (the charge could\n * not be queued) or `network_unavailable`, with `retryAfter` saying when\n * to ask again (20 s and 10 s). Sent once, never retried: a second one is\n * a second retry.\n */\n async retry(id: string, options: RequestOptions = {}): Promise<ServerSubscription> {\n return serverSubscriptionFrom(\n await this.#transport.post(`${pathOf(id)}/retry`, undefined, options),\n 'POST /v1/subscriptions/:id/retry',\n 202,\n );\n }\n\n async #build(action: Action, id: string, options: RequestOptions): Promise<WalletTransaction> {\n const path = `${pathOf(id)}/${action}`;\n\n // No body: the wallet is the subscription's own.\n return walletTransactionFrom(\n await this.#transport.post(path, undefined, options),\n `POST /v1/subscriptions/:id/${action}`,\n );\n }\n\n async #confirm(\n action: Action,\n id: string,\n params: ConfirmParams,\n options: ConfirmOptions,\n ): Promise<ConfirmResult> {\n const path = `${pathOf(id)}/${action}/confirm`;\n const result = confirmResultFrom(\n await this.#transport.post(\n path,\n { signature: params.signature },\n { ...options, timeout: options.timeout ?? CONFIRM_TIMEOUT },\n ),\n `POST /v1/subscriptions/:id/${action}/confirm`,\n );\n\n // A reason says nothing moved: only a cached no may be outdated then.\n await (result.reason === undefined ? this.#changed : this.#seen)(result.subscription);\n return result;\n }\n\n /**\n * One subscription of your project, by id. Found with `access`, it drops\n * the access answers cached that still say no, as `submit` does.\n */\n async retrieve(id: string, options: RequestOptions = {}): Promise<ServerSubscription> {\n const subscription = serverSubscriptionFrom(\n await this.#transport.get(pathOf(id), {}, options),\n );\n\n await this.#seen(subscription);\n return subscription;\n }\n\n /**\n * A customer's subscriptions, newest first, one page at a time; expired\n * checkouts included. `listAll` walks every page.\n *\n * The customer is named as for `access`, and normalised the same way;\n * none or two of `wallet`, `external_id` and `email` throws a TypeError.\n * Each one with `access` drops the access answers cached that still say\n * no, as `submit` does.\n */\n async list(params: ListParams, options: RequestOptions = {}): Promise<ServerSubscriptionList> {\n const { kind, value } = customerOf(params);\n const { plan, limit, starting_after } = params;\n const query: Record<string, QueryValue> = {\n [kind]: value,\n plan,\n limit,\n starting_after,\n };\n\n const page = serverSubscriptionListFrom(\n await this.#transport.get('/v1/subscriptions', query, options),\n );\n\n for (const subscription of page.data) await this.#seen(subscription);\n return page;\n }\n\n /**\n * Every subscription `list` would answer, page after page, for `for await`.\n * Each page is one call, made when the previous one is used up.\n */\n async *listAll(\n params: ListParams,\n options: RequestOptions = {},\n ): AsyncGenerator<ServerSubscription, void, undefined> {\n let page = await this.list(params, options);\n yield* page.data;\n\n while (page.has_more && page.data.length > 0) {\n page = await this.list({ ...params, starting_after: page.data.at(-1)!.id }, options);\n yield* page.data;\n }\n }\n\n /**\n * That subscription's own pull attempts, newest first, one page at a\n * time, and `paid`: what it paid since it began, whatever the page.\n * `allAttempts` walks every page.\n *\n * The id asked and no other: a subscription the wallet came back over\n * (`superseded`) keeps its own payments, and the one that replaced it\n * starts from its first pull. Needs a Mesub that serves the route\n * (Mesub-io/backend#289): from an older one it throws a `not_found`\n * whose `apiCode` is null, where an id it does not hold is a `not_found`\n * whose `apiCode` is `subscription_not_found`.\n */\n async attempts(\n id: string,\n params: AttemptsParams = {},\n options: RequestOptions = {},\n ): Promise<SubscriptionAttemptList> {\n const { limit, starting_after } = params;\n\n const path = `${pathOf(id)}/attempts`;\n\n try {\n return subscriptionAttemptListFrom(\n await this.#transport.get(path, { limit, starting_after }, options),\n );\n } catch (error) {\n if (!(error instanceof MesubError) || !routeMissing(error)) throw error;\n\n // Said for what it is, not as a wrong baseUrl.\n throw new MesubError(\n 'This Mesub does not serve GET /v1/subscriptions/:id/attempts yet.',\n {\n status: 404,\n code: 'not_found',\n body: error.body,\n requestId: error.requestId,\n cause: error,\n },\n );\n }\n }\n\n /**\n * Every attempt `attempts` would answer, page after page, for `for await`.\n * Each page is one call, made when the previous one is used up.\n */\n async *allAttempts(\n id: string,\n params: AttemptsParams = {},\n options: RequestOptions = {},\n ): AsyncGenerator<SubscriptionAttempt, void, undefined> {\n let page = await this.attempts(id, params, options);\n yield* page.data;\n\n while (page.has_more && page.data.length > 0) {\n page = await this.attempts(\n id,\n { ...params, starting_after: page.data.at(-1)!.id },\n options,\n );\n yield* page.data;\n }\n }\n}\n\n/** An id as Mesub writes them, and the only kind it reads (`RESOURCE_ID`). */\nconst ID = /^[A-Za-z0-9_-]{1,64}$/;\n\nfunction pathOf(id: string): string {\n // An empty id would reach the list route and read as a missing customer.\n if (typeof id !== 'string' || id === '') {\n throw new MesubError('A subscription id is required.', {\n status: null,\n code: 'invalid_request',\n });\n }\n // `.` and `..` survive encoding and the URL resolves them: `retrieve('.')` would\n // ask the list, `cancel('..')` another route. Refused before it is sent.\n if (!ID.test(id)) {\n throw new MesubError(\n 'A subscription id is letters, digits, hyphens and underscores, 64 at most.',\n { status: null, code: 'invalid_request' },\n );\n }\n return `/v1/subscriptions/${id}`;\n}\n\n/**\n * Mesub's own 404 for a route it does not have: `Cannot GET /v1/...`, never\n * a subscription's nor an event's.\n *\n * @internal\n */\nexport function routeMissing(error: MesubError): boolean {\n const { message } = (error.body ?? {}) as { message?: unknown };\n\n return (\n error.status === 404 &&\n error.apiCode === null &&\n typeof message === 'string' &&\n message.startsWith('Cannot GET /')\n );\n}\n\n/**\n * No answer that says what became of the submit: a timeout, a network error,\n * or a 5xx (a proxy's 502 or 504 among them) after Mesub may have co-signed.\n */\nfunction unanswered(error: MesubError): boolean {\n // Mesub-io/backend#505: too many submits waiting, nothing signed or sent.\n if (error.status === 503 && error.apiCode === 'unavailable') return false;\n\n return error.status === null || error.status >= 500;\n}\n","import { MesubError } from './errors.js';\nimport { routeMissing } from './subscriptions.js';\nimport type { RequestOptions, Transport } from './transport.js';\nimport { eventFrom, eventListFrom } from './validate.js';\nimport type { EventType, MesubEvent } from './webhooks.js';\n\n/**\n * Your project's subscription events (Mesub-io/backend#482): `GET /v1/events`\n * and `GET /v1/events/:id`, each event the very body its webhooks carry. The\n * types copy the back's `server-events.service.ts`, which stays the source\n * of truth. Snake case, as the API serves it.\n */\n\n/** One page of events, newest first. */\nexport interface EventList {\n data: MesubEvent[];\n /** Ask for the next page with `starting_after` set to the last id of this one. */\n has_more: boolean;\n /**\n * When Mesub began keeping events in this shape: events before it are\n * never served, whatever the window. Their webhooks carried the older\n * shape, with no event id.\n */\n available_since: string;\n}\n\n/** Which events, and which page of them. */\nexport interface EventListParams {\n /** Only events of this type. */\n type?: EventType;\n /** Only events about this subscription, by its id. */\n subscription?: string;\n /** 1 to 100, 25 by default. */\n limit?: number;\n /** The id of the last event of the previous page. */\n starting_after?: string;\n}\n\nexport class Events {\n readonly #transport: Transport;\n\n /** @internal */\n constructor(transport: Transport) {\n this.#transport = transport;\n }\n\n /**\n * Your project's events of the last 30 days, newest first, one page at a\n * time: each the very body its webhooks carried, the same `id`, `data`\n * and `detail`. Read it to catch up on what an endpoint missed while it\n * was down or turned off. Events before `available_since` are never\n * served, whatever the window.\n *\n * An event is listed a moment after its `created_at`, so one can appear\n * below one already read: when polling for what is new, page on until\n * `created_at` is a minute older than the newest you held, and skip the\n * ids you have seen.\n *\n * Needs a Mesub that serves the route (Mesub-io/backend#482): from an\n * older one it throws a `not_found` whose `apiCode` is null.\n */\n async list(params: EventListParams = {}, options: RequestOptions = {}): Promise<EventList> {\n const { type, subscription, limit, starting_after } = params;\n\n return eventListFrom(\n await this.#get('/v1/events', { type, subscription, limit, starting_after }, options),\n );\n }\n\n /**\n * One event, by the `id` its webhook body carries, as `list` serves it.\n * A `not_found` whose `apiCode` is `event_not_found` for an id older than\n * 30 days or than `available_since`, or of another project. A delivery's\n * `webhook-id` is not an event id: read `event.id`, not `delivery_id`.\n */\n async retrieve(id: string, options: RequestOptions = {}): Promise<MesubEvent> {\n return eventFrom(await this.#get(pathOf(id), {}, options));\n }\n\n async #get(\n path: string,\n query: Record<string, string | number | undefined>,\n options: RequestOptions,\n ): Promise<unknown> {\n try {\n return await this.#transport.get(path, query, options);\n } catch (error) {\n if (!(error instanceof MesubError) || !routeMissing(error)) throw error;\n\n // Said for what it is, not as a wrong baseUrl.\n throw new MesubError('This Mesub does not serve GET /v1/events yet.', {\n status: 404,\n code: 'not_found',\n body: error.body,\n requestId: error.requestId,\n cause: error,\n });\n }\n }\n}\n\n/** An id as Mesub writes them, and the only kind it reads. */\nconst ID = /^[A-Za-z0-9_-]{1,64}$/;\n\nfunction pathOf(id: string): string {\n // `.` and `..` survive encoding and the URL resolves them; an empty one\n // would ask the list. Refused before it is sent.\n if (typeof id !== 'string' || !ID.test(id)) {\n throw new MesubError(\n 'An event id is letters, digits, hyphens and underscores, 64 at most.',\n { status: null, code: 'invalid_request' },\n );\n }\n return `/v1/events/${id}`;\n}\n","import { MesubError } from './errors.js';\nimport type { RequestOptions, Transport } from './transport.js';\nimport { planFrom, planListFrom } from './validate.js';\n\n/** A plan a subscriber can hold: taking new ones (`active`), or ending (`sunset`). */\nexport type PlanStatus = 'active' | 'sunset';\n\n/**\n * What Mesub knows of a plan's token, resolved by its mint: never from the\n * merchant nor the client. A symbol is free text on chain and a fake \"USDC\"\n * is a known scam: show an unverified token as unverified, with its mint.\n */\nexport interface PlanToken {\n /** Null when Mesub found none, and on an unverified token named like a vouched stablecoin. */\n symbol: string | null;\n name: string | null;\n logo_url: string | null;\n /** True for a mint Mesub vouches for, or one Jupiter marks verified. */\n verified: boolean;\n}\n\n/** A plan of your project, as a pricing page needs it. */\nexport interface Plan {\n /** What `access`, the guards and `subscriptions.create` name it by. */\n slug: string;\n /** The plan's name, or its slug when none was given. */\n name: string;\n description: string | null;\n project_name: string;\n logo_url: string | null;\n /** The price in the token's base units, a string: a u64 does not survive a float. */\n amount: string;\n /** The price as a person counts it: `\"9.99\"`. */\n amount_display: string;\n /**\n * The plan's amount in dollars at the latest price Mesub knows, a decimal\n * string such as `\"9.99\"`: an estimate to show beside the amount, never\n * what is charged. Null when Mesub knows no price, and from a Mesub that\n * predates it.\n */\n amount_usd: string | null;\n decimals: number;\n /** Set only for a mint Mesub vouches for (USDC, USDT, PYUSD), null for any other. */\n symbol: string | null;\n /**\n * The token's symbol, name and logo for any mint, and whether it is\n * verified: when it is not, say so and keep `mint` visible. All null and\n * unverified from a Mesub that predates it.\n */\n token: PlanToken;\n mint: string;\n period_hours: number;\n network: 'devnet' | 'mainnet-beta';\n status: PlanStatus;\n /** Whether it takes new subscribers now: show \"Subscribe\" only when true. */\n available: boolean;\n /**\n * When the plan ends, null when it has no end: nobody has access past\n * it, and the last period before it is charged in full.\n */\n ends_at: string | null;\n}\n\n/** `mesub.plans`: the plans of your project, for a pricing page. */\nexport class Plans {\n readonly #transport: Transport;\n\n /** @internal */\n constructor(transport: Transport) {\n this.#transport = transport;\n }\n\n /**\n * Every plan of your project a subscriber can hold, sorted by slug: the\n * active ones, and the ones ending (`sunset`, `available: false`). Never\n * cached: call it where you render, and cache it your way.\n */\n async list(options: RequestOptions = {}): Promise<Plan[]> {\n return planListFrom(await this.#transport.get('/v1/plans', {}, options));\n }\n\n /** One plan by its slug. A slug your project lacks throws `plan_not_found`. */\n async retrieve(slug: string, options: RequestOptions = {}): Promise<Plan> {\n return planFrom(await this.#transport.get(pathOf(slug), {}, options));\n }\n}\n\n/**\n * A slug as Mesub writes them: lowercase letters, digits and single hyphens,\n * with at least one letter, so a slug never reads as a number.\n */\nconst SLUG = /^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nfunction pathOf(slug: string): string {\n // Anything else would reach another route, or the list: refused before it is sent.\n if (typeof slug !== 'string' || !SLUG.test(slug)) {\n throw new MesubError(\n 'A plan slug is lowercase letters, digits and hyphens, with at least one letter.',\n { status: null, code: 'invalid_request' },\n );\n }\n return `/v1/plans/${slug}`;\n}\n","import type { AccessAnswer, AccessList, AccessOptions, Customer } from './answer.js';\nimport { AccessCache } from './cache/access-cache.js';\nimport { MemoryStore } from './cache/memory-store.js';\nimport type { CacheStore } from './cache/store.js';\nimport { type Asked, customerOf } from './customer.js';\nimport { MesubError } from './errors.js';\nimport { Events } from './events.js';\nimport { apiKeyOf, baseUrlOf, headersOf, numberOf } from './options.js';\nimport { Plans } from './plans.js';\nimport { type ServerSubscription, Subscriptions } from './subscriptions.js';\nimport { type CallOptions, Transport } from './transport.js';\nimport { accessAnswerFrom, accessListFrom } from './validate.js';\nimport { webhookSecretOf, Webhooks } from './webhooks.js';\n\n/** What a guard decided, and on which answer. */\nexport interface Decision {\n access: boolean;\n answer: AccessAnswer | null;\n /** The answer came from the outage fallback, not from Mesub just now. */\n stale: boolean;\n /**\n * Mesub did not answer (an outage, a rate limit, or the guard's time\n * budget running out) and no answer was cached for that customer: nobody\n * knows yet, so the guards answer 503, not 402.\n */\n unavailable?: boolean;\n /**\n * With `unavailable`, the milliseconds Mesub's `Retry-After` asked for,\n * when it sent one: a guard's 503 hands it on (#132).\n */\n retryAfter?: number;\n}\n\nexport interface MesubOptions {\n /** API key. Defaults to `process.env.MESUB_API_KEY`. */\n apiKey?: string;\n /**\n * Defaults to `process.env.MESUB_BASE_URL`, then `https://api.mesub.io`.\n * May carry a path, e.g. behind a proxy at\n * `https://proxy.example.com/mesub`: every call is made under it.\n */\n baseUrl?: string;\n /**\n * Extra headers sent with every call to Mesub: e.g. a Cloudflare Access\n * service token for the proxy in front. Cannot set Authorization,\n * User-Agent, Accept, Content-Type nor Mesub-Version.\n */\n headers?: Record<string, string>;\n /** A custom `fetch`, e.g. one bound to your own agent. Defaults to the global one. */\n fetch?: typeof fetch;\n /** Per attempt, in milliseconds. Defaults to 5000. */\n timeout?: number;\n /**\n * How many times a failed call to Mesub is sent again, after the first\n * attempt. Defaults to 2. HTTP retries only: nothing to do with a plan's\n * pull retries, which Mesub runs on its side (and a Free plan has none).\n */\n maxRetries?: number;\n /**\n * Where `/v1/access` answers are kept. Defaults to a memory store of\n * 10,000 entries, emptied on restart; a Redis store keeps the outage\n * fallback across restarts and servers.\n */\n cache?: CacheStore<AccessAnswer | AccessList>;\n /**\n * How long an answer is kept once stale, for the outage fallback of\n * `hasAccess` and the guards, in milliseconds. Defaults to 24 hours. 0\n * turns the fallback off: an outage then keeps everyone out.\n */\n maxStaleMs?: number;\n /**\n * How long the guards (`requirePlan`, `withMesub`, `RequirePlan`) give\n * Mesub to answer, retries and waits included, in milliseconds. Defaults\n * to 2000. Once it runs out, or Mesub fails, the guard answers from the\n * cache, even stale, or 503 with Retry-After for a wallet it never saw. `access` and\n * `hasAccess`, called directly, are not bound by it.\n */\n guardTimeout?: number;\n /**\n * The webhook endpoint's signing secret, `whsec_...`, for\n * `webhooks.verify`. Defaults to `process.env.MESUB_WEBHOOK_SECRET`; none\n * is needed until a webhook is verified.\n */\n webhookSecret?: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.mesub.io';\nconst DEFAULT_GUARD_TIMEOUT = 2_000;\n\nexport class Mesub {\n /** Subscribing from your server: create, submit, retrieve and list. */\n readonly subscriptions: Subscriptions;\n /** The plans of your project, for a pricing page: `list` and `retrieve`. */\n readonly plans: Plans;\n /** Checking the webhooks Mesub sends: `verify`. */\n readonly webhooks: Webhooks;\n /** The events of the last 30 days, as their webhooks carried them: `list` and `retrieve`. */\n readonly events: Events;\n /** @internal */\n protected readonly transport: Transport;\n /** @internal */\n protected readonly cache: AccessCache<AccessAnswer>;\n /** The lists of `accessList`, in the same store, under their own keys. */\n private readonly lists: AccessCache<AccessList>;\n /** What the cache keys are scoped by: a hash of the API key, computed once. */\n private readonly cacheScope: () => Promise<string>;\n /** What hashes emails and external ids in the cache keys: the API key, imported once. */\n private readonly cacheSecret: () => Promise<HmacKey>;\n private readonly guardTimeout: number;\n /**\n * The `/v1/access` requests in flight, by cache key: a call for the same\n * answer waits for that one rather than send its own (#34).\n */\n private readonly flights = new Map<string, Promise<unknown>>();\n\n constructor(options: MesubOptions = {}) {\n // Every option is checked here, before anything is built: a TypeError\n // now rather than a 401 or a 1 ms timeout on every call.\n const apiKey = apiKeyOf(options.apiKey);\n const baseUrl = baseUrlOf(options.baseUrl, DEFAULT_BASE_URL);\n const headers = headersOf(options.headers);\n const webhookSecret = webhookSecretOf(options.webhookSecret);\n if (options.fetch !== undefined && typeof options.fetch !== 'function') {\n throw new TypeError('fetch must be a function.');\n }\n // Resolved per call so a fetch patched after construction is still used.\n const fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));\n // NaN or 0 would cut every call before it starts.\n this.guardTimeout = numberOf('guardTimeout', options.guardTimeout, DEFAULT_GUARD_TIMEOUT, {\n delay: true,\n });\n\n this.transport = new Transport({\n apiKey,\n baseUrl,\n headers,\n fetch,\n timeout: numberOf('timeout', options.timeout, 5_000, { delay: true }),\n maxRetries: numberOf('maxRetries', options.maxRetries, 2, {\n zero: true,\n integer: true,\n }),\n });\n this.subscriptions = new Subscriptions(\n this.transport,\n (subscription) => this.landed(subscription),\n (subscription) => this.landed(subscription, true),\n );\n this.plans = new Plans(this.transport);\n this.events = new Events(this.transport);\n // Every answer cached for its customer goes, the yes too: `stopped` and\n // `ended` take access away, and `hasAccess` must say so at once.\n this.webhooks = new Webhooks(webhookSecret, (event) => this.landed(event.data, true));\n const store = options.cache ?? new MemoryStore<AccessAnswer | AccessList>();\n const staleness =\n options.maxStaleMs === undefined\n ? {}\n : { maxStaleMs: numberOf('maxStaleMs', options.maxStaleMs, 0, { zero: true }) };\n // One store for both: their keys never meet (`mesub:access:` and\n // `mesub:access-list:`), so each reads back only what it wrote.\n this.cache = new AccessCache(store as CacheStore<AccessAnswer>, staleness);\n this.lists = new AccessCache(store as CacheStore<AccessList>, staleness);\n let cacheScope: Promise<string> | undefined;\n this.cacheScope = () => (cacheScope ??= hashScope(apiKey));\n let cacheSecret: Promise<HmacKey> | undefined;\n this.cacheSecret = () => (cacheSecret ??= importSecret(apiKey));\n }\n\n /**\n * Everything Mesub knows about that customer on that plan: whether they\n * have access, their status, their dates. Served from the cache while fresh.\n *\n * The customer is `{ wallet }`, `{ external_id }` or `{ email }`, or a\n * wallet as a string. Asked by external id or email, the answer is about\n * the wallet that grants access, if any: `wallet` is null when they have\n * nothing on that plan.\n *\n * Throws a `MesubError` whenever Mesub cannot answer: it is for screens.\n * A guard calls `hasAccess`, which falls back instead.\n */\n async access(\n customer: Customer | string,\n plan: string,\n options: AccessOptions = {},\n ): Promise<AccessAnswer> {\n const asked = customerOf(customer);\n const slug = planOf(plan);\n\n // Attempts skip the cache both ways: no key to compute.\n if (options.attempts === true) return this.ask(asked, slug, null, boundOf(options));\n\n const slot = await this.slotOf(asked);\n const cached = await this.cache.read(slot.who, slug, slot.scope);\n\n if (cached?.fresh) return cached.value;\n\n return this.ask(asked, slug, slot);\n }\n\n /**\n * Every plan of the project that customer has anything on, each answered\n * like `access`, for a page listing their entitlements in one call. Cached\n * apart from the answers by plan, for its own `revalidate_after`.\n *\n * Throws a `MesubError` whenever Mesub cannot answer, like `access`.\n */\n async accessList(\n customer: Customer | string,\n options: AccessOptions = {},\n ): Promise<AccessList> {\n const asked = customerOf(customer);\n const attempts = options.attempts === true;\n // Attempts skip the cache both ways, as with `access`.\n const slot = attempts ? null : await this.slotOf(asked);\n\n if (slot) {\n const cached = await this.lists.read(slot.who, null, slot.scope);\n\n if (cached?.fresh) return cached.value;\n }\n\n // Checked like `access`'s answer, before it reaches the cache.\n const request = async () =>\n accessListFrom(\n await this.transport.get('/v1/access', {\n [asked.kind]: asked.value,\n attempts: attempts || undefined,\n }),\n );\n\n if (!slot) return request();\n\n return this.shared(flightKey('call', slot, null), request, (list) =>\n this.lists.write(slot.who, null, list, slot.scope),\n );\n }\n\n /**\n * `access` from Mesub, cached in the slot its caller computed once and\n * already read, or without the cache at all (`null`) for an answer with\n * its attempts.\n */\n private async ask(\n asked: Asked,\n plan: string,\n slot: Slot | null,\n call: CallOptions = {},\n ): Promise<AccessAnswer> {\n const attempts = slot === null;\n\n // Throws a MesubError on any failure, which goes straight to the caller;\n // an answer of the wrong shape too, before it reaches the cache.\n const request = async () =>\n accessAnswerFrom(\n await this.transport.get(\n '/v1/access',\n {\n [asked.kind]: asked.value,\n plan,\n // Left out when not asked: the transport drops undefined values.\n attempts: attempts || undefined,\n },\n call,\n ),\n );\n\n // A heavy answer must not replace the light one a guard reads.\n if (!slot) return request();\n\n // A guard's request is cut at its deadline and never retries a 429:\n // shared between guards only, so a call from your code keeps its\n // retries, and a guard never waits past its budget behind one.\n const mode = call.deadline === undefined ? 'call' : 'guard';\n\n return this.shared(flightKey(mode, slot, plan), request, (answer) =>\n this.cache.write(slot.who, plan, answer, slot.scope),\n );\n }\n\n /**\n * One request in flight per key (#34): fifty calls for a customer not in\n * the cache made fifty requests to Mesub, and as many 429s to retry. The\n * first sends it, the others wait for its answer or its error. Its answer\n * is cached once, unless a subscription that landed dropped the flight\n * meanwhile: then it is answered, not cached.\n */\n private shared<T>(\n key: string,\n request: () => Promise<T>,\n keep: (value: T) => Promise<void>,\n ): Promise<T> {\n const pending = this.flights.get(key);\n\n if (pending) return pending as Promise<T>;\n\n // Not awaited: a store slow to write never holds the answer (#132).\n // `keep` never throws, a store that fails being dropped.\n const flight: Promise<T> = request().then((value) => {\n if (this.flights.get(key) === flight) void keep(value);\n return value;\n });\n const land = () => {\n if (this.flights.get(key) === flight) this.flights.delete(key);\n };\n\n this.flights.set(key, flight);\n void flight.then(land, land);\n\n return flight;\n }\n\n /**\n * Where a customer's answers are cached: the project's scope, and who\n * they are as the key reads it. A wallet is public on chain and stays\n * readable; an email or an external id is never written in clear, only\n * as an HMAC under the API key, which a leaked Redis alone cannot reverse\n * by guessing.\n */\n private async slotOf(asked: Asked): Promise<Slot> {\n const scope = await this.cacheScope();\n\n if (asked.kind === 'wallet') return { scope, who: `wallet:${asked.value}` };\n\n const mac = await crypto.subtle.sign(\n 'HMAC',\n await this.cacheSecret(),\n new TextEncoder().encode(`${asked.kind}:${asked.value}`),\n );\n\n return { scope, who: `${asked.kind}:${hex(mac)}` };\n }\n\n /**\n * A subscription `submit`, `retrieve` or `list` answered (#33): once it\n * grants access (`active`, or `cancelled` before its end), the answers\n * cached for its customer that say no are dropped, by wallet, external\n * id and email, for its plan and in the list of every plan. Without it, a\n * `hasAccess` right after a submit kept answering the cached no until\n * `revalidate_after` ran out. A store that fails costs nothing more than\n * that: it never throws.\n *\n * With `moved`, one a confirm just cancelled, resumed or closed, or one a\n * verified webhook carried: every answer cached for its customer is\n * dropped, the yes too, since its status and dates changed whatever its\n * access. A `stopped` or `ended` event must not leave a yes behind.\n */\n private async landed(subscription: ServerSubscription, moved = false): Promise<void> {\n const { plan } = subscription;\n\n if (!moved && !subscription.access) return;\n\n try {\n for (const asked of customersOf(subscription)) {\n const slot = await this.slotOf(asked);\n\n // A request sent before it landed may answer the old no: its\n // callers get it, the cache does not, and the next call asks again.\n for (const mode of ['call', 'guard'] as const) {\n if (plan !== null) this.flights.delete(flightKey(mode, slot, plan));\n this.flights.delete(flightKey(mode, slot, null));\n }\n\n if (plan !== null) {\n await this.cache.forget(\n slot.who,\n plan,\n slot.scope,\n (answer) => moved || !answer.access,\n );\n }\n await this.lists.forget(\n slot.who,\n null,\n slot.scope,\n (list) =>\n moved ||\n !list.plans.some((answer) => answer.plan === plan && answer.access),\n );\n }\n } catch {\n // The answers stay cached until their revalidate_after, as before.\n }\n }\n\n /**\n * Whether that customer has access to that plan, for a guard. The\n * customer is the same as for `access`, a wallet string included.\n *\n * When Mesub is unavailable after the retries (`unavailable` or\n * `rate_limited`), it answers the last answer it knew, even stale, and\n * `false` for a customer it never saw. Any other error is thrown, never\n * turned into `false`: a bad key or an unknown plan is a broken\n * integration, not a denial.\n *\n * No time budget: it waits out the client's timeout and retries, and a\n * 429's `Retry-After`. A guard answers within `guardTimeout` instead.\n */\n async hasAccess(customer: Customer | string, plan: string): Promise<boolean> {\n return (await this.decideBy(customer, plan)).access;\n }\n\n /**\n * `hasAccess`, with the answer it decided on, within `guardTimeout`: what\n * the middlewares hand the route. `answer` is null only for a customer\n * never seen during an outage, which is then `unavailable`; `stale` is\n * true when the answer came from the fallback.\n *\n * @internal\n */\n async decide(customer: Customer | string, plan: string): Promise<Decision> {\n return this.decideBy(customer, plan, Date.now() + this.guardTimeout);\n }\n\n /**\n * The decision itself. With a deadline (the guards), every call to Mesub\n * fits before it: attempts are cut at it and a Retry-After that would\n * outlast it is not waited. Without one (`hasAccess` called directly),\n * the client's timeout and retries, as configured.\n */\n private async decideBy(\n customer: Customer | string,\n plan: string,\n deadline?: number,\n ): Promise<Decision> {\n // A guard does not wait out a rate limit: it falls back at once.\n const call: CallOptions =\n deadline === undefined ? {} : { deadline, retryRateLimited: false };\n // A malformed customer or a missing plan is thrown before anything is asked.\n const asked = customerOf(customer);\n const slug = planOf(plan);\n const slot = await this.slotOf(asked);\n // Read once, for both: a fresh answer is served as is, a stale one is\n // the fallback. A guard's store that has not answered by the deadline\n // is a miss, as is one that fails (#132). Not read again after Mesub\n // fails: its call runs to the deadline, so a second read would always\n // be a miss. An answer a webhook drops during that call can still be\n // the fallback, at most `guardTimeout` after it was dropped: the same\n // window as a request in flight when it landed (`landed`).\n const cached = await this.cache.read(slot.who, slug, slot.scope, deadline);\n\n if (cached?.fresh) {\n return { access: cached.value.access, answer: cached.value, stale: false };\n }\n\n try {\n const answer = await this.ask(asked, slug, slot, call);\n\n return { access: answer.access, answer, stale: false };\n } catch (error) {\n const unreachable =\n error instanceof MesubError &&\n (error.code === 'unavailable' || error.code === 'rate_limited');\n\n // A broken integration is thrown, never read as \"no access\".\n if (!unreachable) throw error;\n\n // The last answer known, even stale; a customer never seen stays out.\n if (cached) {\n return { access: stillGrants(cached.value), answer: cached.value, stale: true };\n }\n\n // Not Mesub saying no: nobody knows, so a guard asks for a retry\n // later, when Mesub said when.\n return {\n access: false,\n answer: null,\n stale: true,\n unavailable: true,\n ...(error.retryAfter !== null && { retryAfter: error.retryAfter }),\n };\n }\n }\n}\n\ntype HmacKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\n/**\n * The deadline and the 429 rule the widget routes pass with `attempts`, as a\n * guard's call: read here, never named by `AccessOptions`.\n */\nfunction boundOf(options: AccessOptions): CallOptions {\n const { deadline, retryRateLimited } = options as CallOptions;\n\n return {\n ...(deadline !== undefined && { deadline }),\n ...(retryRateLimited !== undefined && { retryRateLimited }),\n };\n}\n\n/** Where a customer's answers are cached: see `slotOf`. */\ninterface Slot {\n scope: string;\n who: string;\n}\n\n/**\n * What a request in flight is shared under: its cache key, and whether a\n * guard sent it. The key holds the project's scope, the kind of identifier\n * and the plan, or the list's own prefix, so two of them never meet.\n */\nfunction flightKey(mode: 'call' | 'guard', slot: Slot, plan: string | null): string {\n return `${mode}|${AccessCache.key(slot.who, plan, slot.scope)}`;\n}\n\n/** Every way a subscription's customer can be asked about: its wallet, external id and email. */\nfunction customersOf(subscription: ServerSubscription): Asked[] {\n const { wallet, external_id, email } = subscription;\n const customers: Customer[] = [{ wallet }];\n\n if (external_id) customers.push({ external_id });\n if (email) customers.push({ email });\n\n return customers.map(customerOf);\n}\n\n/**\n * The plan, which `access`, `hasAccess` and the guards cannot do without.\n * Without it Mesub answers the list of every plan instead: read as one\n * answer, it would be cached under `undefined` and its missing `access` taken\n * for a no. The list is `accessList`.\n */\nfunction planOf(plan: unknown): string {\n if (typeof plan !== 'string' || plan === '') {\n throw new TypeError('A plan is needed: accessList(customer) lists every plan instead.');\n }\n\n return plan;\n}\n\n/**\n * `key-` and 16 hex characters of the API key's SHA-256: a scope, not a way\n * back to the key. Two projects sharing one store never share a key, and it\n * needs no call to Mesub, so it is the same after a restart in an outage. A\n * rotated key starts a new scope; the old entries expire on their TTL.\n */\nasync function hashScope(apiKey: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(apiKey));\n\n return `key-${hex(digest).slice(0, 16)}`;\n}\n\n/** The API key as an HMAC key, for the hashes of emails and external ids. */\nfunction importSecret(apiKey: string): Promise<HmacKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(apiKey),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n}\n\nfunction hex(bytes: ArrayBuffer): string {\n return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * A stale answer's access, past what it paid for (#35): an answer with no\n * charge or retry ahead (cancelled, parked) ends at `access_until`. One with a\n * renewal ahead keeps the fallback: it was likely paid while Mesub was down.\n * A blocked one (`blocked_since`) ends there too, whatever is ahead: Mesub\n * keeps retrying it, which says nothing of a payment, and never grants it\n * past `access_until` (#132).\n */\nfunction stillGrants(answer: AccessAnswer, now: number = Date.now()): boolean {\n if (!answer.access || !answer.access_until) return answer.access;\n\n // Truthy, not non-null: an entry an older release wrote to a shared store may lack them.\n const renewing = Boolean(answer.next_charge_at || answer.next_retry_at);\n\n if (renewing && !answer.blocked_since) return true;\n return Date.parse(answer.access_until) > now;\n}\n"],"mappings":"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","names":["kind","error","parsed","retryAfter","pathOf","ID","pathOf"]}