@usethrottle/auth 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -82,6 +82,11 @@ framework adapter would use to re-render.
82
82
 
83
83
  ## API reference
84
84
 
85
+ Two objects. `ThrottleAuth` owns the session and the buyer's credentials;
86
+ `ThrottleAccount` owns the buyer's data.
87
+
88
+ ### `ThrottleAuth` — session and credentials
89
+
85
90
  Every method on the `ThrottleAuth` returned by `createThrottleAuth()` (also
86
91
  reachable as `useAuth().auth` in React):
87
92
 
@@ -102,7 +107,7 @@ reachable as `useAuth().auth` in React):
102
107
  | `resetPassword(token: string, password: string): Promise<void>` | Consumes a one-time reset-link token and sets a new password; revokes every session, this one included. |
103
108
  | `changePassword(password: string): Promise<void>` | Changes the signed-in buyer's password (requires step-up). |
104
109
  | `listSessions(): Promise<DeviceSession[]>` | Lists this buyer's active device sessions. |
105
- | `revokeSession(id: string): Promise<void>` | Revokes one device session by id (requires step-up); revoking the current session signs this device out. |
110
+ | `revokeSession(id: string): Promise<void>` | Revokes one device session by id; revoking the current session signs this device out. |
106
111
 
107
112
  ```ts
108
113
  interface DeviceSession {
@@ -115,6 +120,49 @@ interface DeviceSession {
115
120
  }
116
121
  ```
117
122
 
123
+ ### `ThrottleAccount` — the buyer's data
124
+
125
+ Every method on the `ThrottleAccount` returned by `createAccount(auth)` (also
126
+ reachable as `useAuth().account` in React). The React hooks in
127
+ [Account pages](#account-pages) wrap these; use these directly from the
128
+ headless core, or anywhere you want the call without the hook.
129
+
130
+ | Method | Calls | Description |
131
+ | ------ | ----- | ----------- |
132
+ | `getProfile(): Promise<Customer>` | `GET /me` | The signed-in buyer. While unverified after a claim, only `id` and `email` are populated — see [the claim cutoff](#email-verification-and-the-claim-cutoff). |
133
+ | `updateProfile(patch: ProfilePatch): Promise<Customer>` | `PATCH /me` | Name, phone, company, marketing opt-in. 403s while a claim cutoff is active. |
134
+ | `listAddresses(): Promise<Address[]>` | `GET /me/addresses` | All of the buyer's addresses. |
135
+ | `createAddress(input: AddressInput): Promise<Address>` | `POST /me/addresses` | `addressLine1`, `city` and `countryCode` are required. |
136
+ | `updateAddress(id, patch: Partial<AddressInput>): Promise<Address>` | `PATCH /me/addresses/:id` | 404s for an id that is not this buyer's. |
137
+ | `deleteAddress(id): Promise<void>` | `DELETE /me/addresses/:id` | |
138
+ | `listPaymentMethods(): Promise<PaymentMethod[]>` | `GET /me/payment-methods` | Requires a verified email. |
139
+ | `setDefaultPaymentMethod(id): Promise<PaymentMethod>` | `PATCH /me/payment-methods/:id` | How a buyer changes what they are billed on. |
140
+ | `deletePaymentMethod(id): Promise<void>` | `DELETE /me/payment-methods/:id` | Requires step-up. |
141
+ | `listOrders(page?: Page): Promise<Order[]>` | `GET /me/orders` | Cursor-paginated. |
142
+ | `getOrder(id): Promise<Order>` | `GET /me/orders/:id` | |
143
+ | `listInvoices(page?: Page): Promise<Invoice[]>` | `GET /me/invoices` | Cursor-paginated. |
144
+ | `getInvoice(id): Promise<Invoice>` | `GET /me/invoices/:id` | |
145
+ | `listSubscriptions(page?: Page): Promise<Subscription[]>` | `GET /me/subscriptions` | Cursor-paginated. |
146
+ | `getSubscription(id): Promise<Subscription>` | `GET /me/subscriptions/:id` | |
147
+ | `cancelSubscription(id, opts?: { atPeriodEnd?: boolean })` | `POST /me/subscriptions/:id/cancel` | Immediate by default; `atPeriodEnd: true` defers to the period end. Requires step-up. |
148
+ | `pauseSubscription(id): Promise<Subscription>` | `POST /me/subscriptions/:id/pause` | Requires step-up. |
149
+ | `resumeSubscription(id): Promise<Subscription>` | `POST /me/subscriptions/:id/resume` | Requires step-up. |
150
+ | `claimCart(cartId): Promise<{ cartId: string; customerId: string }>` | `POST /me/carts/:cartId/claim` | Attaches a guest cart to the signed-in buyer. Throws `cart_already_claimed` if it belongs to someone else. |
151
+
152
+ Paging is `{ cursor?: string; limit?: number }`; `limit` is 1-100 and defaults to
153
+ 25. A list returns rows only — carry the last row's `id` as the next `cursor`.
154
+
155
+ ```ts
156
+ const account = createAccount(auth);
157
+
158
+ const firstPage = await account.listOrders({ limit: 10 });
159
+ const nextPage = await account.listOrders({ limit: 10, cursor: firstPage.at(-1)?.id });
160
+ ```
161
+
162
+ The row types — `Customer`, `Address`, `AddressInput`, `ProfilePatch`,
163
+ `PaymentMethod`, `Order`, `Invoice`, `Subscription` — are all exported from the
164
+ package root and mirror the API's responses field for field.
165
+
118
166
  ## Account pages
119
167
 
120
168
  Six hooks, each a `{ data, isLoading, error, refetch }` resource plus the
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createAccount,
3
3
  createThrottleAuth
4
- } from "./chunk-OYNGTNOS.js";
4
+ } from "./chunk-SOPTBXF3.js";
5
5
 
6
6
  // src/react/context.tsx
7
7
  import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
@@ -46,4 +46,4 @@ export {
46
46
  useAuthContext,
47
47
  useAuth
48
48
  };
49
- //# sourceMappingURL=chunk-IJ3DNMNG.js.map
49
+ //# sourceMappingURL=chunk-5GLLPF6L.js.map
@@ -230,6 +230,7 @@ function createThrottleAuth(config) {
230
230
  async function call(method, path, body) {
231
231
  let retriedAuth = false;
232
232
  let retriedStepUp = false;
233
+ let sendStepUp = false;
233
234
  for (; ; ) {
234
235
  const token = await accessToken();
235
236
  try {
@@ -239,7 +240,7 @@ function createThrottleAuth(config) {
239
240
  path,
240
241
  body,
241
242
  accessToken: token ?? void 0,
242
- stepUpToken: stepUpToken ?? void 0
243
+ stepUpToken: sendStepUp ? stepUpToken ?? void 0 : void 0
243
244
  });
244
245
  } catch (err) {
245
246
  const e = err;
@@ -251,8 +252,10 @@ function createThrottleAuth(config) {
251
252
  }
252
253
  if (e.code === "step_up_required" && !retriedStepUp) {
253
254
  retriedStepUp = true;
254
- stepUpToken = null;
255
- if (await ensureStepUp()) continue;
255
+ if (await ensureStepUp()) {
256
+ sendStepUp = true;
257
+ continue;
258
+ }
256
259
  }
257
260
  throw err;
258
261
  }
@@ -378,4 +381,4 @@ export {
378
381
  createThrottleAuth,
379
382
  createAccount
380
383
  };
381
- //# sourceMappingURL=chunk-OYNGTNOS.js.map
384
+ //# sourceMappingURL=chunk-SOPTBXF3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/request.ts","../src/session.ts","../src/client.ts","../src/account.ts"],"sourcesContent":["export class ThrottleAuthError extends Error {\n readonly code: string;\n readonly statusCode: number;\n readonly retryAfter?: number;\n readonly details?: unknown;\n\n constructor(args: { code: string; message: string; statusCode: number; retryAfter?: number; details?: unknown }) {\n super(args.message);\n this.name = 'ThrottleAuthError';\n this.code = args.code;\n this.statusCode = args.statusCode;\n this.retryAfter = args.retryAfter;\n this.details = args.details;\n }\n}\n","import type { AuthStorage } from './types';\n\nexport type { AuthStorage };\n\nexport function memoryStorage(): AuthStorage {\n const map = new Map<string, string>();\n return {\n get: (k) => map.get(k) ?? null,\n set: (k, v) => void map.set(k, v),\n remove: (k) => void map.delete(k),\n };\n}\n\n/**\n * localStorage when it works, memory when it does not. Safari private mode,\n * hardened browser settings and server-side rendering all make localStorage\n * either absent or throwing; none of them may take the storefront down.\n */\nexport function localStorageAdapter(): AuthStorage {\n const fallback = memoryStorage();\n const ls = (): Storage | null => {\n try {\n return typeof localStorage !== 'undefined' ? localStorage : null;\n } catch {\n return null;\n }\n };\n return {\n get(k) {\n try {\n const v = ls()?.getItem(k);\n if (v != null) return v;\n } catch { /* fall through */ }\n return fallback.get(k);\n },\n set(k, v) {\n fallback.set(k, v);\n try { ls()?.setItem(k, v); } catch { /* memory-only this session */ }\n },\n remove(k) {\n fallback.remove(k);\n try { ls()?.removeItem(k); } catch { /* already gone */ }\n },\n };\n}\n","import { ThrottleAuthError } from './errors';\n\nexport interface RequestInput {\n method: string;\n path: string;\n baseUrl: string;\n publishableKey: string;\n accessToken?: string;\n stepUpToken?: string;\n body?: unknown;\n}\n\n/** Returns the UNWRAPPED `data` from the `{ data: … }` envelope. Throws ThrottleAuthError otherwise. */\nexport async function request(input: RequestInput): Promise<any> {\n const headers: Record<string, string> = { 'X-API-Key': input.publishableKey };\n if (input.body !== undefined) headers['Content-Type'] = 'application/json';\n if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;\n if (input.stepUpToken) headers['X-Step-Up'] = input.stepUpToken;\n\n const res = await fetch(`${input.baseUrl}/v1/storefront${input.path}`, {\n method: input.method,\n headers,\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — an HTML gateway page, say. Leave parsed empty so the\n // branch below still throws a typed error rather than a raw SyntaxError.\n }\n\n if (!res.ok) {\n const e = parsed?.error ?? {};\n // `Retry-After` is server-controlled but not trusted: Number('soon') is\n // NaN, and a NaN reaches the UI as \"wait NaN seconds\".\n const ra = Number(res.headers.get('retry-after'));\n throw new ThrottleAuthError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n retryAfter: Number.isFinite(ra) && ra >= 0 ? ra : undefined,\n details: e.details,\n });\n }\n\n // `'data' in parsed`, not `parsed?.data ?? parsed`: a legitimate\n // `{ \"data\": null }` is nullish, and `??` would fall through and hand the\n // caller the whole envelope instead of the null the API actually sent.\n return parsed && typeof parsed === 'object' && 'data' in parsed ? parsed.data : (parsed ?? {});\n}\n","import type { Customer, Session } from './types';\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\nexport interface AuthSnapshot {\n status: AuthStatus;\n customer: Customer | null;\n /**\n * False here does NOT only mean \"we emailed them and they have not clicked\".\n * While it is false the API may also be withholding pre-existing account\n * data behind the claim cutoff — empty order lists and a 403 on `PATCH /me`\n * are expected, not broken. See the plan header.\n */\n emailVerified: boolean;\n}\n\ntype Listener = (snapshot: AuthSnapshot) => void;\n\nexport class SessionState {\n #snapshot: AuthSnapshot = { status: 'loading', customer: null, emailVerified: false };\n #accessToken: string | null = null;\n #expiresAt: number | null = null;\n #listeners = new Set<Listener>();\n\n get(): AuthSnapshot { return this.#snapshot; }\n accessToken(): string | null { return this.#accessToken; }\n expiresAt(): number | null { return this.#expiresAt; }\n\n setSession(session: Session): void {\n this.#accessToken = session.accessToken;\n this.#expiresAt = Date.parse(session.expiresAt);\n this.#publish({\n status: 'authenticated',\n customer: session.customer,\n emailVerified: Boolean(session.customer?.emailVerified),\n });\n }\n\n setStatus(status: AuthStatus): void {\n this.#publish({ ...this.#snapshot, status });\n }\n\n clear(): void {\n this.#accessToken = null;\n this.#expiresAt = null;\n this.#publish({ status: 'unauthenticated', customer: null, emailVerified: false });\n }\n\n subscribe(fn: Listener): () => void {\n this.#listeners.add(fn);\n return () => { this.#listeners.delete(fn); };\n }\n\n #publish(next: AuthSnapshot): void {\n // Identity matters: React's useSyncExternalStore re-renders on reference\n // change, and a token refresh every ten minutes must not re-render the\n // whole account area when nothing visible changed.\n const same =\n next.status === this.#snapshot.status &&\n next.emailVerified === this.#snapshot.emailVerified &&\n customerEquals(next.customer, this.#snapshot.customer);\n if (same) return;\n this.#snapshot = next;\n for (const fn of [...this.#listeners]) {\n // A merchant's subscriber throwing must not stop the rest from hearing.\n try { fn(next); } catch { /* merchant callback */ }\n }\n }\n}\n\nfunction customerEquals(a: Customer | null, b: Customer | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<keyof Customer>;\n for (const k of keys) if (a[k] !== b[k]) return false;\n return true;\n}\n","import { request } from './request';\nimport { ThrottleAuthError } from './errors';\nimport { SessionState, type AuthSnapshot } from './session';\nimport { localStorageAdapter, type AuthStorage } from './storage';\nimport type { Customer, Session, ThrottleAuthConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n/** Refresh this far before expiry so ordinary calls rarely see a 401 at all. */\nconst REFRESH_SKEW_MS = 30_000;\n\nexport interface DeviceSession {\n id: string;\n userAgent: string | null;\n ipPrefix: string | null;\n createdAt: string;\n lastUsedAt: string;\n current: boolean;\n}\n\nexport interface ThrottleAuth {\n register(email: string, password: string): Promise<{ status: 'active' | 'pending' }>;\n signIn(email: string, password: string): Promise<void>;\n signOut(): Promise<void>;\n signOutEverywhere(): Promise<void>;\n restore(): Promise<void>;\n getSnapshot(): AuthSnapshot;\n subscribe(fn: (s: AuthSnapshot) => void): () => void;\n stepUp(password: string): Promise<void>;\n call<T = any>(method: string, path: string, body?: unknown): Promise<T>;\n verifyEmail(token: string): Promise<void>;\n resendVerification(): Promise<void>;\n forgotPassword(email: string): Promise<void>;\n resetPassword(token: string, password: string): Promise<void>;\n changePassword(password: string): Promise<void>;\n listSessions(): Promise<DeviceSession[]>;\n revokeSession(id: string): Promise<void>;\n}\n\nexport function createThrottleAuth(config: ThrottleAuthConfig): ThrottleAuth {\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const storage: AuthStorage = config.storage ?? localStorageAdapter();\n // Namespaced by key so two Throttle applications on one origin cannot read\n // each other's refresh token out of shared localStorage.\n const storageKey = `throttle_auth_rt:${config.publishableKey}`;\n const lockName = `throttle_auth_refresh:${config.publishableKey}`;\n\n const state = new SessionState();\n let refreshInFlight: Promise<void> | null = null;\n let stepUpToken: string | null = null;\n let stepUpExpiresAt = 0;\n\n const base = { baseUrl, publishableKey: config.publishableKey };\n\n async function persist(session: Session): Promise<void> {\n state.setSession(session);\n await storage.set(storageKey, session.refreshToken);\n }\n\n async function forget(): Promise<void> {\n stepUpToken = null;\n state.clear();\n await storage.remove(storageKey);\n }\n\n /**\n * Cross-tab single flight. Two tabs refreshing at once would each present the\n * same stored token; the second is a replay, and the server kills the family.\n * Web Locks serialises them; where it is unavailable the in-process guard\n * below still covers the common single-tab case.\n */\n function withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as any).navigator?.locks;\n return locks?.request ? locks.request(lockName, fn) : fn();\n }\n\n async function doRefresh(): Promise<void> {\n await withLock(async () => {\n // Re-read INSIDE the lock: another tab may have rotated while we waited,\n // and presenting the token we read before the lock would be a replay.\n const refreshToken = await storage.get(storageKey);\n if (!refreshToken) {\n await forget();\n throw new ThrottleAuthError({ code: 'session_revoked', message: 'No session', statusCode: 401 });\n }\n try {\n const data = await request({ ...base, method: 'POST', path: '/auth/refresh', body: { refreshToken } });\n await persist(data as Session);\n } catch (err) {\n // Any refusal of a refresh token is terminal — there is nothing left to\n // retry with. Drop it rather than letting a dead token sit in storage.\n await forget();\n throw err;\n }\n });\n }\n\n function refresh(): Promise<void> {\n if (!refreshInFlight) {\n refreshInFlight = doRefresh().finally(() => { refreshInFlight = null; });\n }\n return refreshInFlight;\n }\n\n async function accessToken(): Promise<string | null> {\n const expires = state.expiresAt();\n if (state.accessToken() && expires && expires - Date.now() > REFRESH_SKEW_MS) {\n return state.accessToken();\n }\n if (await storage.get(storageKey)) {\n await refresh();\n return state.accessToken();\n }\n return state.accessToken();\n }\n\n async function ensureStepUp(): Promise<boolean> {\n if (stepUpToken && stepUpExpiresAt > Date.now()) return true;\n if (!config.onStepUpRequired) return false;\n const password = await config.onStepUpRequired();\n if (!password) return false;\n await stepUp(password);\n return true;\n }\n\n async function stepUp(password: string): Promise<void> {\n const data = await request({\n ...base, method: 'POST', path: '/auth/step-up',\n accessToken: await accessToken() ?? undefined, body: { password },\n });\n stepUpToken = data.stepUpToken;\n stepUpExpiresAt = Date.now() + (Number(data.expiresIn) || 300) * 1000 - 5_000;\n }\n\n async function call<T = any>(method: string, path: string, body?: unknown): Promise<T> {\n let retriedAuth = false;\n let retriedStepUp = false;\n // Only true on the retry that follows a `step_up_required` 403 — never\n // attached speculatively. Sending an authorization-grade header on every\n // request once minted meant it rode along on unrelated calls too, and\n // cross-origin each of those then failed CORS preflight. The cost is one\n // extra round trip per gated call, which is how the server signals\n // step-up in the first place.\n let sendStepUp = false;\n\n for (;;) {\n const token = await accessToken();\n try {\n return await request({\n ...base, method, path, body,\n accessToken: token ?? undefined,\n stepUpToken: sendStepUp ? (stepUpToken ?? undefined) : undefined,\n });\n } catch (err) {\n const e = err as ThrottleAuthError;\n // `session_revoked` belongs here too. The server returns it for a\n // stale session EPOCH as well as for a genuinely dead session, and a\n // changed password bumps the epoch while deliberately sparing the\n // current session — so one refresh recovers it. When the session\n // really is dead the refresh fails and forget() runs, which is the\n // same outcome, one request later.\n const expired = e.statusCode === 401 &&\n (e.code === 'unauthorized' || e.code === 'token_expired' || e.code === 'session_revoked');\n if (expired && !retriedAuth) {\n retriedAuth = true;\n await refresh(); // shared — ten callers here produce ONE request\n continue;\n }\n if (e.code === 'step_up_required' && !retriedStepUp) {\n retriedStepUp = true;\n // ensureStepUp() reuses the cached token when it's still within its\n // 300s validity (no re-prompt); it was never sent above, so this is\n // the first the server has seen of it for this call.\n if (await ensureStepUp()) { sendStepUp = true; continue; }\n }\n throw err;\n }\n }\n }\n\n return {\n async register(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/register', body: { email, password } });\n if (data?.status === 'pending') return { status: 'pending' };\n await persist(data as Session);\n return { status: 'active' };\n },\n\n async signIn(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/login', body: { email, password } });\n await persist(data as Session);\n },\n\n async signOut() {\n try { await call('POST', '/auth/logout'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async signOutEverywhere() {\n try { await call('POST', '/auth/logout-all'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async restore() {\n if (!(await storage.get(storageKey))) { state.setStatus('unauthenticated'); return; }\n try { await refresh(); } catch { /* forget() already ran */ }\n },\n\n getSnapshot: () => state.get(),\n subscribe: (fn) => state.subscribe(fn),\n stepUp,\n call,\n\n async verifyEmail(token) {\n await request({ ...base, method: 'POST', path: '/auth/verify-email', body: { token } });\n // `ver` is a claim baked into the access token, and the claim cutoff is\n // read from the session row. Without a new token the buyer stays gated —\n // and their own order history stays hidden — for the rest of this\n // token's ten minutes.\n if (await storage.get(storageKey)) {\n try { await refresh(); } catch { /* verification still succeeded */ }\n }\n },\n\n async resendVerification() {\n await call('POST', '/auth/verify-email/resend');\n },\n\n async forgotPassword(email) {\n // Always 202, whether or not the address has an account. Do not infer\n // anything from it and do not tell the buyer whether it existed.\n await request({ ...base, method: 'POST', path: '/auth/forgot-password', body: { email } });\n },\n\n async resetPassword(token, password) {\n await request({ ...base, method: 'POST', path: '/auth/reset-password', body: { token, password } });\n // The server revokes every session on reset, this one included.\n await forget();\n },\n\n async changePassword(password) {\n await call('POST', '/me/change-password', { password });\n // The change bumps the session epoch. This session was deliberately\n // spared, but the access token in hand now carries a stale epoch and the\n // next write would 401. Rotate now rather than on the buyer's next click\n // — and do not let a failed rotation report a change that already\n // succeeded server-side as a failure.\n try { await refresh(); } catch { /* the password change already succeeded */ }\n },\n\n async listSessions() {\n return call<DeviceSession[]>('GET', '/me/sessions');\n },\n\n async revokeSession(id) {\n const mine = await call<DeviceSession[]>('GET', '/me/sessions');\n const current = mine.find((s) => s.current);\n await call('DELETE', `/me/sessions/${encodeURIComponent(id)}`);\n if (current?.id === id) await forget();\n },\n };\n}\n\nexport type { AuthSnapshot, Customer, Session, ThrottleAuthConfig };\n","import type { ThrottleAuth } from './client';\nimport type {\n Address, AddressInput, Customer, Invoice, Order,\n PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n\nexport interface Page { cursor?: string; limit?: number }\n\nexport interface ThrottleAccount {\n getProfile(): Promise<Customer>;\n updateProfile(patch: ProfilePatch): Promise<Customer>;\n listAddresses(): Promise<Address[]>;\n createAddress(input: AddressInput): Promise<Address>;\n updateAddress(id: string, patch: Partial<AddressInput>): Promise<Address>;\n deleteAddress(id: string): Promise<void>;\n listPaymentMethods(): Promise<PaymentMethod[]>;\n setDefaultPaymentMethod(id: string): Promise<PaymentMethod>;\n deletePaymentMethod(id: string): Promise<void>;\n listOrders(page?: Page): Promise<Order[]>;\n getOrder(id: string): Promise<Order>;\n listInvoices(page?: Page): Promise<Invoice[]>;\n getInvoice(id: string): Promise<Invoice>;\n listSubscriptions(page?: Page): Promise<Subscription[]>;\n getSubscription(id: string): Promise<Subscription>;\n cancelSubscription(id: string, opts?: { atPeriodEnd?: boolean }): Promise<Subscription>;\n pauseSubscription(id: string): Promise<Subscription>;\n resumeSubscription(id: string): Promise<Subscription>;\n claimCart(cartId: string): Promise<{ cartId: string; customerId: string }>;\n}\n\nconst seg = (v: string) => encodeURIComponent(v);\n\nfunction query(page?: Page): string {\n const params = new URLSearchParams();\n if (page?.cursor) params.set('cursor', page.cursor);\n if (page?.limit != null) params.set('limit', String(page.limit));\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\nexport function createAccount(auth: ThrottleAuth): ThrottleAccount {\n const { call } = auth;\n return {\n getProfile: () => call('GET', '/me'),\n updateProfile: (patch) => call('PATCH', '/me', patch),\n\n listAddresses: () => call('GET', '/me/addresses'),\n createAddress: (input) => call('POST', '/me/addresses', input),\n updateAddress: (id, patch) => call('PATCH', `/me/addresses/${seg(id)}`, patch),\n deleteAddress: (id) => call('DELETE', `/me/addresses/${seg(id)}`),\n\n listPaymentMethods: () => call('GET', '/me/payment-methods'),\n // The platform has no \"change this subscription's card\" capability, so\n // this is how a buyer changes what they are billed on: move the default.\n setDefaultPaymentMethod: (id) => call('PATCH', `/me/payment-methods/${seg(id)}`, { isDefault: true }),\n deletePaymentMethod: (id) => call('DELETE', `/me/payment-methods/${seg(id)}`),\n\n listOrders: (page) => call('GET', `/me/orders${query(page)}`),\n getOrder: (id) => call('GET', `/me/orders/${seg(id)}`),\n listInvoices: (page) => call('GET', `/me/invoices${query(page)}`),\n getInvoice: (id) => call('GET', `/me/invoices/${seg(id)}`),\n listSubscriptions: (page) => call('GET', `/me/subscriptions${query(page)}`),\n getSubscription: (id) => call('GET', `/me/subscriptions/${seg(id)}`),\n\n cancelSubscription: (id, opts) => call('POST', `/me/subscriptions/${seg(id)}/cancel`, opts),\n pauseSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/pause`),\n resumeSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/resume`),\n\n claimCart: (cartId) => call('POST', `/me/carts/${seg(cartId)}/claim`),\n };\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -260,6 +260,7 @@ function createThrottleAuth(config) {
260
260
  async function call(method, path, body) {
261
261
  let retriedAuth = false;
262
262
  let retriedStepUp = false;
263
+ let sendStepUp = false;
263
264
  for (; ; ) {
264
265
  const token = await accessToken();
265
266
  try {
@@ -269,7 +270,7 @@ function createThrottleAuth(config) {
269
270
  path,
270
271
  body,
271
272
  accessToken: token ?? void 0,
272
- stepUpToken: stepUpToken ?? void 0
273
+ stepUpToken: sendStepUp ? stepUpToken ?? void 0 : void 0
273
274
  });
274
275
  } catch (err) {
275
276
  const e = err;
@@ -281,8 +282,10 @@ function createThrottleAuth(config) {
281
282
  }
282
283
  if (e.code === "step_up_required" && !retriedStepUp) {
283
284
  retriedStepUp = true;
284
- stepUpToken = null;
285
- if (await ensureStepUp()) continue;
285
+ if (await ensureStepUp()) {
286
+ sendStepUp = true;
287
+ continue;
288
+ }
286
289
  }
287
290
  throw err;
288
291
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/request.ts","../src/session.ts","../src/storage.ts","../src/client.ts","../src/account.ts"],"sourcesContent":["export { createThrottleAuth } from './client';\nexport type { ThrottleAuth, DeviceSession } from './client';\nexport { ThrottleAuthError } from './errors';\nexport { memoryStorage, localStorageAdapter } from './storage';\nexport type { AuthSnapshot, AuthStatus } from './session';\nexport type { AuthStorage, Customer, Session, ThrottleAuthConfig } from './types';\nexport { createAccount } from './account';\nexport type { ThrottleAccount, Page } from './account';\nexport type {\n Address, AddressInput, Invoice, Order, PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n","export class ThrottleAuthError extends Error {\n readonly code: string;\n readonly statusCode: number;\n readonly retryAfter?: number;\n readonly details?: unknown;\n\n constructor(args: { code: string; message: string; statusCode: number; retryAfter?: number; details?: unknown }) {\n super(args.message);\n this.name = 'ThrottleAuthError';\n this.code = args.code;\n this.statusCode = args.statusCode;\n this.retryAfter = args.retryAfter;\n this.details = args.details;\n }\n}\n","import { ThrottleAuthError } from './errors';\n\nexport interface RequestInput {\n method: string;\n path: string;\n baseUrl: string;\n publishableKey: string;\n accessToken?: string;\n stepUpToken?: string;\n body?: unknown;\n}\n\n/** Returns the UNWRAPPED `data` from the `{ data: … }` envelope. Throws ThrottleAuthError otherwise. */\nexport async function request(input: RequestInput): Promise<any> {\n const headers: Record<string, string> = { 'X-API-Key': input.publishableKey };\n if (input.body !== undefined) headers['Content-Type'] = 'application/json';\n if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;\n if (input.stepUpToken) headers['X-Step-Up'] = input.stepUpToken;\n\n const res = await fetch(`${input.baseUrl}/v1/storefront${input.path}`, {\n method: input.method,\n headers,\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — an HTML gateway page, say. Leave parsed empty so the\n // branch below still throws a typed error rather than a raw SyntaxError.\n }\n\n if (!res.ok) {\n const e = parsed?.error ?? {};\n // `Retry-After` is server-controlled but not trusted: Number('soon') is\n // NaN, and a NaN reaches the UI as \"wait NaN seconds\".\n const ra = Number(res.headers.get('retry-after'));\n throw new ThrottleAuthError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n retryAfter: Number.isFinite(ra) && ra >= 0 ? ra : undefined,\n details: e.details,\n });\n }\n\n // `'data' in parsed`, not `parsed?.data ?? parsed`: a legitimate\n // `{ \"data\": null }` is nullish, and `??` would fall through and hand the\n // caller the whole envelope instead of the null the API actually sent.\n return parsed && typeof parsed === 'object' && 'data' in parsed ? parsed.data : (parsed ?? {});\n}\n","import type { Customer, Session } from './types';\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\nexport interface AuthSnapshot {\n status: AuthStatus;\n customer: Customer | null;\n /**\n * False here does NOT only mean \"we emailed them and they have not clicked\".\n * While it is false the API may also be withholding pre-existing account\n * data behind the claim cutoff — empty order lists and a 403 on `PATCH /me`\n * are expected, not broken. See the plan header.\n */\n emailVerified: boolean;\n}\n\ntype Listener = (snapshot: AuthSnapshot) => void;\n\nexport class SessionState {\n #snapshot: AuthSnapshot = { status: 'loading', customer: null, emailVerified: false };\n #accessToken: string | null = null;\n #expiresAt: number | null = null;\n #listeners = new Set<Listener>();\n\n get(): AuthSnapshot { return this.#snapshot; }\n accessToken(): string | null { return this.#accessToken; }\n expiresAt(): number | null { return this.#expiresAt; }\n\n setSession(session: Session): void {\n this.#accessToken = session.accessToken;\n this.#expiresAt = Date.parse(session.expiresAt);\n this.#publish({\n status: 'authenticated',\n customer: session.customer,\n emailVerified: Boolean(session.customer?.emailVerified),\n });\n }\n\n setStatus(status: AuthStatus): void {\n this.#publish({ ...this.#snapshot, status });\n }\n\n clear(): void {\n this.#accessToken = null;\n this.#expiresAt = null;\n this.#publish({ status: 'unauthenticated', customer: null, emailVerified: false });\n }\n\n subscribe(fn: Listener): () => void {\n this.#listeners.add(fn);\n return () => { this.#listeners.delete(fn); };\n }\n\n #publish(next: AuthSnapshot): void {\n // Identity matters: React's useSyncExternalStore re-renders on reference\n // change, and a token refresh every ten minutes must not re-render the\n // whole account area when nothing visible changed.\n const same =\n next.status === this.#snapshot.status &&\n next.emailVerified === this.#snapshot.emailVerified &&\n customerEquals(next.customer, this.#snapshot.customer);\n if (same) return;\n this.#snapshot = next;\n for (const fn of [...this.#listeners]) {\n // A merchant's subscriber throwing must not stop the rest from hearing.\n try { fn(next); } catch { /* merchant callback */ }\n }\n }\n}\n\nfunction customerEquals(a: Customer | null, b: Customer | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<keyof Customer>;\n for (const k of keys) if (a[k] !== b[k]) return false;\n return true;\n}\n","import type { AuthStorage } from './types';\n\nexport type { AuthStorage };\n\nexport function memoryStorage(): AuthStorage {\n const map = new Map<string, string>();\n return {\n get: (k) => map.get(k) ?? null,\n set: (k, v) => void map.set(k, v),\n remove: (k) => void map.delete(k),\n };\n}\n\n/**\n * localStorage when it works, memory when it does not. Safari private mode,\n * hardened browser settings and server-side rendering all make localStorage\n * either absent or throwing; none of them may take the storefront down.\n */\nexport function localStorageAdapter(): AuthStorage {\n const fallback = memoryStorage();\n const ls = (): Storage | null => {\n try {\n return typeof localStorage !== 'undefined' ? localStorage : null;\n } catch {\n return null;\n }\n };\n return {\n get(k) {\n try {\n const v = ls()?.getItem(k);\n if (v != null) return v;\n } catch { /* fall through */ }\n return fallback.get(k);\n },\n set(k, v) {\n fallback.set(k, v);\n try { ls()?.setItem(k, v); } catch { /* memory-only this session */ }\n },\n remove(k) {\n fallback.remove(k);\n try { ls()?.removeItem(k); } catch { /* already gone */ }\n },\n };\n}\n","import { request } from './request';\nimport { ThrottleAuthError } from './errors';\nimport { SessionState, type AuthSnapshot } from './session';\nimport { localStorageAdapter, type AuthStorage } from './storage';\nimport type { Customer, Session, ThrottleAuthConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n/** Refresh this far before expiry so ordinary calls rarely see a 401 at all. */\nconst REFRESH_SKEW_MS = 30_000;\n\nexport interface DeviceSession {\n id: string;\n userAgent: string | null;\n ipPrefix: string | null;\n createdAt: string;\n lastUsedAt: string;\n current: boolean;\n}\n\nexport interface ThrottleAuth {\n register(email: string, password: string): Promise<{ status: 'active' | 'pending' }>;\n signIn(email: string, password: string): Promise<void>;\n signOut(): Promise<void>;\n signOutEverywhere(): Promise<void>;\n restore(): Promise<void>;\n getSnapshot(): AuthSnapshot;\n subscribe(fn: (s: AuthSnapshot) => void): () => void;\n stepUp(password: string): Promise<void>;\n call<T = any>(method: string, path: string, body?: unknown): Promise<T>;\n verifyEmail(token: string): Promise<void>;\n resendVerification(): Promise<void>;\n forgotPassword(email: string): Promise<void>;\n resetPassword(token: string, password: string): Promise<void>;\n changePassword(password: string): Promise<void>;\n listSessions(): Promise<DeviceSession[]>;\n revokeSession(id: string): Promise<void>;\n}\n\nexport function createThrottleAuth(config: ThrottleAuthConfig): ThrottleAuth {\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const storage: AuthStorage = config.storage ?? localStorageAdapter();\n // Namespaced by key so two Throttle applications on one origin cannot read\n // each other's refresh token out of shared localStorage.\n const storageKey = `throttle_auth_rt:${config.publishableKey}`;\n const lockName = `throttle_auth_refresh:${config.publishableKey}`;\n\n const state = new SessionState();\n let refreshInFlight: Promise<void> | null = null;\n let stepUpToken: string | null = null;\n let stepUpExpiresAt = 0;\n\n const base = { baseUrl, publishableKey: config.publishableKey };\n\n async function persist(session: Session): Promise<void> {\n state.setSession(session);\n await storage.set(storageKey, session.refreshToken);\n }\n\n async function forget(): Promise<void> {\n stepUpToken = null;\n state.clear();\n await storage.remove(storageKey);\n }\n\n /**\n * Cross-tab single flight. Two tabs refreshing at once would each present the\n * same stored token; the second is a replay, and the server kills the family.\n * Web Locks serialises them; where it is unavailable the in-process guard\n * below still covers the common single-tab case.\n */\n function withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as any).navigator?.locks;\n return locks?.request ? locks.request(lockName, fn) : fn();\n }\n\n async function doRefresh(): Promise<void> {\n await withLock(async () => {\n // Re-read INSIDE the lock: another tab may have rotated while we waited,\n // and presenting the token we read before the lock would be a replay.\n const refreshToken = await storage.get(storageKey);\n if (!refreshToken) {\n await forget();\n throw new ThrottleAuthError({ code: 'session_revoked', message: 'No session', statusCode: 401 });\n }\n try {\n const data = await request({ ...base, method: 'POST', path: '/auth/refresh', body: { refreshToken } });\n await persist(data as Session);\n } catch (err) {\n // Any refusal of a refresh token is terminal — there is nothing left to\n // retry with. Drop it rather than letting a dead token sit in storage.\n await forget();\n throw err;\n }\n });\n }\n\n function refresh(): Promise<void> {\n if (!refreshInFlight) {\n refreshInFlight = doRefresh().finally(() => { refreshInFlight = null; });\n }\n return refreshInFlight;\n }\n\n async function accessToken(): Promise<string | null> {\n const expires = state.expiresAt();\n if (state.accessToken() && expires && expires - Date.now() > REFRESH_SKEW_MS) {\n return state.accessToken();\n }\n if (await storage.get(storageKey)) {\n await refresh();\n return state.accessToken();\n }\n return state.accessToken();\n }\n\n async function ensureStepUp(): Promise<boolean> {\n if (stepUpToken && stepUpExpiresAt > Date.now()) return true;\n if (!config.onStepUpRequired) return false;\n const password = await config.onStepUpRequired();\n if (!password) return false;\n await stepUp(password);\n return true;\n }\n\n async function stepUp(password: string): Promise<void> {\n const data = await request({\n ...base, method: 'POST', path: '/auth/step-up',\n accessToken: await accessToken() ?? undefined, body: { password },\n });\n stepUpToken = data.stepUpToken;\n stepUpExpiresAt = Date.now() + (Number(data.expiresIn) || 300) * 1000 - 5_000;\n }\n\n async function call<T = any>(method: string, path: string, body?: unknown): Promise<T> {\n let retriedAuth = false;\n let retriedStepUp = false;\n\n for (;;) {\n const token = await accessToken();\n try {\n return await request({\n ...base, method, path, body,\n accessToken: token ?? undefined,\n stepUpToken: stepUpToken ?? undefined,\n });\n } catch (err) {\n const e = err as ThrottleAuthError;\n // `session_revoked` belongs here too. The server returns it for a\n // stale session EPOCH as well as for a genuinely dead session, and a\n // changed password bumps the epoch while deliberately sparing the\n // current session — so one refresh recovers it. When the session\n // really is dead the refresh fails and forget() runs, which is the\n // same outcome, one request later.\n const expired = e.statusCode === 401 &&\n (e.code === 'unauthorized' || e.code === 'token_expired' || e.code === 'session_revoked');\n if (expired && !retriedAuth) {\n retriedAuth = true;\n await refresh(); // shared — ten callers here produce ONE request\n continue;\n }\n if (e.code === 'step_up_required' && !retriedStepUp) {\n retriedStepUp = true;\n stepUpToken = null;\n if (await ensureStepUp()) continue;\n }\n throw err;\n }\n }\n }\n\n return {\n async register(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/register', body: { email, password } });\n if (data?.status === 'pending') return { status: 'pending' };\n await persist(data as Session);\n return { status: 'active' };\n },\n\n async signIn(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/login', body: { email, password } });\n await persist(data as Session);\n },\n\n async signOut() {\n try { await call('POST', '/auth/logout'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async signOutEverywhere() {\n try { await call('POST', '/auth/logout-all'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async restore() {\n if (!(await storage.get(storageKey))) { state.setStatus('unauthenticated'); return; }\n try { await refresh(); } catch { /* forget() already ran */ }\n },\n\n getSnapshot: () => state.get(),\n subscribe: (fn) => state.subscribe(fn),\n stepUp,\n call,\n\n async verifyEmail(token) {\n await request({ ...base, method: 'POST', path: '/auth/verify-email', body: { token } });\n // `ver` is a claim baked into the access token, and the claim cutoff is\n // read from the session row. Without a new token the buyer stays gated —\n // and their own order history stays hidden — for the rest of this\n // token's ten minutes.\n if (await storage.get(storageKey)) {\n try { await refresh(); } catch { /* verification still succeeded */ }\n }\n },\n\n async resendVerification() {\n await call('POST', '/auth/verify-email/resend');\n },\n\n async forgotPassword(email) {\n // Always 202, whether or not the address has an account. Do not infer\n // anything from it and do not tell the buyer whether it existed.\n await request({ ...base, method: 'POST', path: '/auth/forgot-password', body: { email } });\n },\n\n async resetPassword(token, password) {\n await request({ ...base, method: 'POST', path: '/auth/reset-password', body: { token, password } });\n // The server revokes every session on reset, this one included.\n await forget();\n },\n\n async changePassword(password) {\n await call('POST', '/me/change-password', { password });\n // The change bumps the session epoch. This session was deliberately\n // spared, but the access token in hand now carries a stale epoch and the\n // next write would 401. Rotate now rather than on the buyer's next click\n // — and do not let a failed rotation report a change that already\n // succeeded server-side as a failure.\n try { await refresh(); } catch { /* the password change already succeeded */ }\n },\n\n async listSessions() {\n return call<DeviceSession[]>('GET', '/me/sessions');\n },\n\n async revokeSession(id) {\n const mine = await call<DeviceSession[]>('GET', '/me/sessions');\n const current = mine.find((s) => s.current);\n await call('DELETE', `/me/sessions/${encodeURIComponent(id)}`);\n if (current?.id === id) await forget();\n },\n };\n}\n\nexport type { AuthSnapshot, Customer, Session, ThrottleAuthConfig };\n","import type { ThrottleAuth } from './client';\nimport type {\n Address, AddressInput, Customer, Invoice, Order,\n PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n\nexport interface Page { cursor?: string; limit?: number }\n\nexport interface ThrottleAccount {\n getProfile(): Promise<Customer>;\n updateProfile(patch: ProfilePatch): Promise<Customer>;\n listAddresses(): Promise<Address[]>;\n createAddress(input: AddressInput): Promise<Address>;\n updateAddress(id: string, patch: Partial<AddressInput>): Promise<Address>;\n deleteAddress(id: string): Promise<void>;\n listPaymentMethods(): Promise<PaymentMethod[]>;\n setDefaultPaymentMethod(id: string): Promise<PaymentMethod>;\n deletePaymentMethod(id: string): Promise<void>;\n listOrders(page?: Page): Promise<Order[]>;\n getOrder(id: string): Promise<Order>;\n listInvoices(page?: Page): Promise<Invoice[]>;\n getInvoice(id: string): Promise<Invoice>;\n listSubscriptions(page?: Page): Promise<Subscription[]>;\n getSubscription(id: string): Promise<Subscription>;\n cancelSubscription(id: string, opts?: { atPeriodEnd?: boolean }): Promise<Subscription>;\n pauseSubscription(id: string): Promise<Subscription>;\n resumeSubscription(id: string): Promise<Subscription>;\n claimCart(cartId: string): Promise<{ cartId: string; customerId: string }>;\n}\n\nconst seg = (v: string) => encodeURIComponent(v);\n\nfunction query(page?: Page): string {\n const params = new URLSearchParams();\n if (page?.cursor) params.set('cursor', page.cursor);\n if (page?.limit != null) params.set('limit', String(page.limit));\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\nexport function createAccount(auth: ThrottleAuth): ThrottleAccount {\n const { call } = auth;\n return {\n getProfile: () => call('GET', '/me'),\n updateProfile: (patch) => call('PATCH', '/me', patch),\n\n listAddresses: () => call('GET', '/me/addresses'),\n createAddress: (input) => call('POST', '/me/addresses', input),\n updateAddress: (id, patch) => call('PATCH', `/me/addresses/${seg(id)}`, patch),\n deleteAddress: (id) => call('DELETE', `/me/addresses/${seg(id)}`),\n\n listPaymentMethods: () => call('GET', '/me/payment-methods'),\n // The platform has no \"change this subscription's card\" capability, so\n // this is how a buyer changes what they are billed on: move the default.\n setDefaultPaymentMethod: (id) => call('PATCH', `/me/payment-methods/${seg(id)}`, { isDefault: true }),\n deletePaymentMethod: (id) => call('DELETE', `/me/payment-methods/${seg(id)}`),\n\n listOrders: (page) => call('GET', `/me/orders${query(page)}`),\n getOrder: (id) => call('GET', `/me/orders/${seg(id)}`),\n listInvoices: (page) => call('GET', `/me/invoices${query(page)}`),\n getInvoice: (id) => call('GET', `/me/invoices/${seg(id)}`),\n listSubscriptions: (page) => call('GET', `/me/subscriptions${query(page)}`),\n getSubscription: (id) => call('GET', `/me/subscriptions/${seg(id)}`),\n\n cancelSubscription: (id, opts) => call('POST', `/me/subscriptions/${seg(id)}/cancel`, opts),\n pauseSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/pause`),\n resumeSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/resume`),\n\n claimCart: (cartId) => call('POST', `/me/carts/${seg(cartId)}/claim`),\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/request.ts","../src/session.ts","../src/storage.ts","../src/client.ts","../src/account.ts"],"sourcesContent":["export { createThrottleAuth } from './client';\nexport type { ThrottleAuth, DeviceSession } from './client';\nexport { ThrottleAuthError } from './errors';\nexport { memoryStorage, localStorageAdapter } from './storage';\nexport type { AuthSnapshot, AuthStatus } from './session';\nexport type { AuthStorage, Customer, Session, ThrottleAuthConfig } from './types';\nexport { createAccount } from './account';\nexport type { ThrottleAccount, Page } from './account';\nexport type {\n Address, AddressInput, Invoice, Order, PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n","export class ThrottleAuthError extends Error {\n readonly code: string;\n readonly statusCode: number;\n readonly retryAfter?: number;\n readonly details?: unknown;\n\n constructor(args: { code: string; message: string; statusCode: number; retryAfter?: number; details?: unknown }) {\n super(args.message);\n this.name = 'ThrottleAuthError';\n this.code = args.code;\n this.statusCode = args.statusCode;\n this.retryAfter = args.retryAfter;\n this.details = args.details;\n }\n}\n","import { ThrottleAuthError } from './errors';\n\nexport interface RequestInput {\n method: string;\n path: string;\n baseUrl: string;\n publishableKey: string;\n accessToken?: string;\n stepUpToken?: string;\n body?: unknown;\n}\n\n/** Returns the UNWRAPPED `data` from the `{ data: … }` envelope. Throws ThrottleAuthError otherwise. */\nexport async function request(input: RequestInput): Promise<any> {\n const headers: Record<string, string> = { 'X-API-Key': input.publishableKey };\n if (input.body !== undefined) headers['Content-Type'] = 'application/json';\n if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;\n if (input.stepUpToken) headers['X-Step-Up'] = input.stepUpToken;\n\n const res = await fetch(`${input.baseUrl}/v1/storefront${input.path}`, {\n method: input.method,\n headers,\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — an HTML gateway page, say. Leave parsed empty so the\n // branch below still throws a typed error rather than a raw SyntaxError.\n }\n\n if (!res.ok) {\n const e = parsed?.error ?? {};\n // `Retry-After` is server-controlled but not trusted: Number('soon') is\n // NaN, and a NaN reaches the UI as \"wait NaN seconds\".\n const ra = Number(res.headers.get('retry-after'));\n throw new ThrottleAuthError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n retryAfter: Number.isFinite(ra) && ra >= 0 ? ra : undefined,\n details: e.details,\n });\n }\n\n // `'data' in parsed`, not `parsed?.data ?? parsed`: a legitimate\n // `{ \"data\": null }` is nullish, and `??` would fall through and hand the\n // caller the whole envelope instead of the null the API actually sent.\n return parsed && typeof parsed === 'object' && 'data' in parsed ? parsed.data : (parsed ?? {});\n}\n","import type { Customer, Session } from './types';\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\nexport interface AuthSnapshot {\n status: AuthStatus;\n customer: Customer | null;\n /**\n * False here does NOT only mean \"we emailed them and they have not clicked\".\n * While it is false the API may also be withholding pre-existing account\n * data behind the claim cutoff — empty order lists and a 403 on `PATCH /me`\n * are expected, not broken. See the plan header.\n */\n emailVerified: boolean;\n}\n\ntype Listener = (snapshot: AuthSnapshot) => void;\n\nexport class SessionState {\n #snapshot: AuthSnapshot = { status: 'loading', customer: null, emailVerified: false };\n #accessToken: string | null = null;\n #expiresAt: number | null = null;\n #listeners = new Set<Listener>();\n\n get(): AuthSnapshot { return this.#snapshot; }\n accessToken(): string | null { return this.#accessToken; }\n expiresAt(): number | null { return this.#expiresAt; }\n\n setSession(session: Session): void {\n this.#accessToken = session.accessToken;\n this.#expiresAt = Date.parse(session.expiresAt);\n this.#publish({\n status: 'authenticated',\n customer: session.customer,\n emailVerified: Boolean(session.customer?.emailVerified),\n });\n }\n\n setStatus(status: AuthStatus): void {\n this.#publish({ ...this.#snapshot, status });\n }\n\n clear(): void {\n this.#accessToken = null;\n this.#expiresAt = null;\n this.#publish({ status: 'unauthenticated', customer: null, emailVerified: false });\n }\n\n subscribe(fn: Listener): () => void {\n this.#listeners.add(fn);\n return () => { this.#listeners.delete(fn); };\n }\n\n #publish(next: AuthSnapshot): void {\n // Identity matters: React's useSyncExternalStore re-renders on reference\n // change, and a token refresh every ten minutes must not re-render the\n // whole account area when nothing visible changed.\n const same =\n next.status === this.#snapshot.status &&\n next.emailVerified === this.#snapshot.emailVerified &&\n customerEquals(next.customer, this.#snapshot.customer);\n if (same) return;\n this.#snapshot = next;\n for (const fn of [...this.#listeners]) {\n // A merchant's subscriber throwing must not stop the rest from hearing.\n try { fn(next); } catch { /* merchant callback */ }\n }\n }\n}\n\nfunction customerEquals(a: Customer | null, b: Customer | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<keyof Customer>;\n for (const k of keys) if (a[k] !== b[k]) return false;\n return true;\n}\n","import type { AuthStorage } from './types';\n\nexport type { AuthStorage };\n\nexport function memoryStorage(): AuthStorage {\n const map = new Map<string, string>();\n return {\n get: (k) => map.get(k) ?? null,\n set: (k, v) => void map.set(k, v),\n remove: (k) => void map.delete(k),\n };\n}\n\n/**\n * localStorage when it works, memory when it does not. Safari private mode,\n * hardened browser settings and server-side rendering all make localStorage\n * either absent or throwing; none of them may take the storefront down.\n */\nexport function localStorageAdapter(): AuthStorage {\n const fallback = memoryStorage();\n const ls = (): Storage | null => {\n try {\n return typeof localStorage !== 'undefined' ? localStorage : null;\n } catch {\n return null;\n }\n };\n return {\n get(k) {\n try {\n const v = ls()?.getItem(k);\n if (v != null) return v;\n } catch { /* fall through */ }\n return fallback.get(k);\n },\n set(k, v) {\n fallback.set(k, v);\n try { ls()?.setItem(k, v); } catch { /* memory-only this session */ }\n },\n remove(k) {\n fallback.remove(k);\n try { ls()?.removeItem(k); } catch { /* already gone */ }\n },\n };\n}\n","import { request } from './request';\nimport { ThrottleAuthError } from './errors';\nimport { SessionState, type AuthSnapshot } from './session';\nimport { localStorageAdapter, type AuthStorage } from './storage';\nimport type { Customer, Session, ThrottleAuthConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n/** Refresh this far before expiry so ordinary calls rarely see a 401 at all. */\nconst REFRESH_SKEW_MS = 30_000;\n\nexport interface DeviceSession {\n id: string;\n userAgent: string | null;\n ipPrefix: string | null;\n createdAt: string;\n lastUsedAt: string;\n current: boolean;\n}\n\nexport interface ThrottleAuth {\n register(email: string, password: string): Promise<{ status: 'active' | 'pending' }>;\n signIn(email: string, password: string): Promise<void>;\n signOut(): Promise<void>;\n signOutEverywhere(): Promise<void>;\n restore(): Promise<void>;\n getSnapshot(): AuthSnapshot;\n subscribe(fn: (s: AuthSnapshot) => void): () => void;\n stepUp(password: string): Promise<void>;\n call<T = any>(method: string, path: string, body?: unknown): Promise<T>;\n verifyEmail(token: string): Promise<void>;\n resendVerification(): Promise<void>;\n forgotPassword(email: string): Promise<void>;\n resetPassword(token: string, password: string): Promise<void>;\n changePassword(password: string): Promise<void>;\n listSessions(): Promise<DeviceSession[]>;\n revokeSession(id: string): Promise<void>;\n}\n\nexport function createThrottleAuth(config: ThrottleAuthConfig): ThrottleAuth {\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const storage: AuthStorage = config.storage ?? localStorageAdapter();\n // Namespaced by key so two Throttle applications on one origin cannot read\n // each other's refresh token out of shared localStorage.\n const storageKey = `throttle_auth_rt:${config.publishableKey}`;\n const lockName = `throttle_auth_refresh:${config.publishableKey}`;\n\n const state = new SessionState();\n let refreshInFlight: Promise<void> | null = null;\n let stepUpToken: string | null = null;\n let stepUpExpiresAt = 0;\n\n const base = { baseUrl, publishableKey: config.publishableKey };\n\n async function persist(session: Session): Promise<void> {\n state.setSession(session);\n await storage.set(storageKey, session.refreshToken);\n }\n\n async function forget(): Promise<void> {\n stepUpToken = null;\n state.clear();\n await storage.remove(storageKey);\n }\n\n /**\n * Cross-tab single flight. Two tabs refreshing at once would each present the\n * same stored token; the second is a replay, and the server kills the family.\n * Web Locks serialises them; where it is unavailable the in-process guard\n * below still covers the common single-tab case.\n */\n function withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as any).navigator?.locks;\n return locks?.request ? locks.request(lockName, fn) : fn();\n }\n\n async function doRefresh(): Promise<void> {\n await withLock(async () => {\n // Re-read INSIDE the lock: another tab may have rotated while we waited,\n // and presenting the token we read before the lock would be a replay.\n const refreshToken = await storage.get(storageKey);\n if (!refreshToken) {\n await forget();\n throw new ThrottleAuthError({ code: 'session_revoked', message: 'No session', statusCode: 401 });\n }\n try {\n const data = await request({ ...base, method: 'POST', path: '/auth/refresh', body: { refreshToken } });\n await persist(data as Session);\n } catch (err) {\n // Any refusal of a refresh token is terminal — there is nothing left to\n // retry with. Drop it rather than letting a dead token sit in storage.\n await forget();\n throw err;\n }\n });\n }\n\n function refresh(): Promise<void> {\n if (!refreshInFlight) {\n refreshInFlight = doRefresh().finally(() => { refreshInFlight = null; });\n }\n return refreshInFlight;\n }\n\n async function accessToken(): Promise<string | null> {\n const expires = state.expiresAt();\n if (state.accessToken() && expires && expires - Date.now() > REFRESH_SKEW_MS) {\n return state.accessToken();\n }\n if (await storage.get(storageKey)) {\n await refresh();\n return state.accessToken();\n }\n return state.accessToken();\n }\n\n async function ensureStepUp(): Promise<boolean> {\n if (stepUpToken && stepUpExpiresAt > Date.now()) return true;\n if (!config.onStepUpRequired) return false;\n const password = await config.onStepUpRequired();\n if (!password) return false;\n await stepUp(password);\n return true;\n }\n\n async function stepUp(password: string): Promise<void> {\n const data = await request({\n ...base, method: 'POST', path: '/auth/step-up',\n accessToken: await accessToken() ?? undefined, body: { password },\n });\n stepUpToken = data.stepUpToken;\n stepUpExpiresAt = Date.now() + (Number(data.expiresIn) || 300) * 1000 - 5_000;\n }\n\n async function call<T = any>(method: string, path: string, body?: unknown): Promise<T> {\n let retriedAuth = false;\n let retriedStepUp = false;\n // Only true on the retry that follows a `step_up_required` 403 — never\n // attached speculatively. Sending an authorization-grade header on every\n // request once minted meant it rode along on unrelated calls too, and\n // cross-origin each of those then failed CORS preflight. The cost is one\n // extra round trip per gated call, which is how the server signals\n // step-up in the first place.\n let sendStepUp = false;\n\n for (;;) {\n const token = await accessToken();\n try {\n return await request({\n ...base, method, path, body,\n accessToken: token ?? undefined,\n stepUpToken: sendStepUp ? (stepUpToken ?? undefined) : undefined,\n });\n } catch (err) {\n const e = err as ThrottleAuthError;\n // `session_revoked` belongs here too. The server returns it for a\n // stale session EPOCH as well as for a genuinely dead session, and a\n // changed password bumps the epoch while deliberately sparing the\n // current session — so one refresh recovers it. When the session\n // really is dead the refresh fails and forget() runs, which is the\n // same outcome, one request later.\n const expired = e.statusCode === 401 &&\n (e.code === 'unauthorized' || e.code === 'token_expired' || e.code === 'session_revoked');\n if (expired && !retriedAuth) {\n retriedAuth = true;\n await refresh(); // shared — ten callers here produce ONE request\n continue;\n }\n if (e.code === 'step_up_required' && !retriedStepUp) {\n retriedStepUp = true;\n // ensureStepUp() reuses the cached token when it's still within its\n // 300s validity (no re-prompt); it was never sent above, so this is\n // the first the server has seen of it for this call.\n if (await ensureStepUp()) { sendStepUp = true; continue; }\n }\n throw err;\n }\n }\n }\n\n return {\n async register(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/register', body: { email, password } });\n if (data?.status === 'pending') return { status: 'pending' };\n await persist(data as Session);\n return { status: 'active' };\n },\n\n async signIn(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/login', body: { email, password } });\n await persist(data as Session);\n },\n\n async signOut() {\n try { await call('POST', '/auth/logout'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async signOutEverywhere() {\n try { await call('POST', '/auth/logout-all'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async restore() {\n if (!(await storage.get(storageKey))) { state.setStatus('unauthenticated'); return; }\n try { await refresh(); } catch { /* forget() already ran */ }\n },\n\n getSnapshot: () => state.get(),\n subscribe: (fn) => state.subscribe(fn),\n stepUp,\n call,\n\n async verifyEmail(token) {\n await request({ ...base, method: 'POST', path: '/auth/verify-email', body: { token } });\n // `ver` is a claim baked into the access token, and the claim cutoff is\n // read from the session row. Without a new token the buyer stays gated —\n // and their own order history stays hidden — for the rest of this\n // token's ten minutes.\n if (await storage.get(storageKey)) {\n try { await refresh(); } catch { /* verification still succeeded */ }\n }\n },\n\n async resendVerification() {\n await call('POST', '/auth/verify-email/resend');\n },\n\n async forgotPassword(email) {\n // Always 202, whether or not the address has an account. Do not infer\n // anything from it and do not tell the buyer whether it existed.\n await request({ ...base, method: 'POST', path: '/auth/forgot-password', body: { email } });\n },\n\n async resetPassword(token, password) {\n await request({ ...base, method: 'POST', path: '/auth/reset-password', body: { token, password } });\n // The server revokes every session on reset, this one included.\n await forget();\n },\n\n async changePassword(password) {\n await call('POST', '/me/change-password', { password });\n // The change bumps the session epoch. This session was deliberately\n // spared, but the access token in hand now carries a stale epoch and the\n // next write would 401. Rotate now rather than on the buyer's next click\n // — and do not let a failed rotation report a change that already\n // succeeded server-side as a failure.\n try { await refresh(); } catch { /* the password change already succeeded */ }\n },\n\n async listSessions() {\n return call<DeviceSession[]>('GET', '/me/sessions');\n },\n\n async revokeSession(id) {\n const mine = await call<DeviceSession[]>('GET', '/me/sessions');\n const current = mine.find((s) => s.current);\n await call('DELETE', `/me/sessions/${encodeURIComponent(id)}`);\n if (current?.id === id) await forget();\n },\n };\n}\n\nexport type { AuthSnapshot, Customer, Session, ThrottleAuthConfig };\n","import type { ThrottleAuth } from './client';\nimport type {\n Address, AddressInput, Customer, Invoice, Order,\n PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n\nexport interface Page { cursor?: string; limit?: number }\n\nexport interface ThrottleAccount {\n getProfile(): Promise<Customer>;\n updateProfile(patch: ProfilePatch): Promise<Customer>;\n listAddresses(): Promise<Address[]>;\n createAddress(input: AddressInput): Promise<Address>;\n updateAddress(id: string, patch: Partial<AddressInput>): Promise<Address>;\n deleteAddress(id: string): Promise<void>;\n listPaymentMethods(): Promise<PaymentMethod[]>;\n setDefaultPaymentMethod(id: string): Promise<PaymentMethod>;\n deletePaymentMethod(id: string): Promise<void>;\n listOrders(page?: Page): Promise<Order[]>;\n getOrder(id: string): Promise<Order>;\n listInvoices(page?: Page): Promise<Invoice[]>;\n getInvoice(id: string): Promise<Invoice>;\n listSubscriptions(page?: Page): Promise<Subscription[]>;\n getSubscription(id: string): Promise<Subscription>;\n cancelSubscription(id: string, opts?: { atPeriodEnd?: boolean }): Promise<Subscription>;\n pauseSubscription(id: string): Promise<Subscription>;\n resumeSubscription(id: string): Promise<Subscription>;\n claimCart(cartId: string): Promise<{ cartId: string; customerId: string }>;\n}\n\nconst seg = (v: string) => encodeURIComponent(v);\n\nfunction query(page?: Page): string {\n const params = new URLSearchParams();\n if (page?.cursor) params.set('cursor', page.cursor);\n if (page?.limit != null) params.set('limit', String(page.limit));\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\nexport function createAccount(auth: ThrottleAuth): ThrottleAccount {\n const { call } = auth;\n return {\n getProfile: () => call('GET', '/me'),\n updateProfile: (patch) => call('PATCH', '/me', patch),\n\n listAddresses: () => call('GET', '/me/addresses'),\n createAddress: (input) => call('POST', '/me/addresses', input),\n updateAddress: (id, patch) => call('PATCH', `/me/addresses/${seg(id)}`, patch),\n deleteAddress: (id) => call('DELETE', `/me/addresses/${seg(id)}`),\n\n listPaymentMethods: () => call('GET', '/me/payment-methods'),\n // The platform has no \"change this subscription's card\" capability, so\n // this is how a buyer changes what they are billed on: move the default.\n setDefaultPaymentMethod: (id) => call('PATCH', `/me/payment-methods/${seg(id)}`, { isDefault: true }),\n deletePaymentMethod: (id) => call('DELETE', `/me/payment-methods/${seg(id)}`),\n\n listOrders: (page) => call('GET', `/me/orders${query(page)}`),\n getOrder: (id) => call('GET', `/me/orders/${seg(id)}`),\n listInvoices: (page) => call('GET', `/me/invoices${query(page)}`),\n getInvoice: (id) => call('GET', `/me/invoices/${seg(id)}`),\n listSubscriptions: (page) => call('GET', `/me/subscriptions${query(page)}`),\n getSubscription: (id) => call('GET', `/me/subscriptions/${seg(id)}`),\n\n cancelSubscription: (id, opts) => call('POST', `/me/subscriptions/${seg(id)}/cancel`, opts),\n pauseSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/pause`),\n resumeSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/resume`),\n\n claimCart: (cartId) => call('POST', `/me/carts/${seg(cartId)}/claim`),\n };\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  createThrottleAuth,
6
6
  localStorageAdapter,
7
7
  memoryStorage
8
- } from "./chunk-OYNGTNOS.js";
8
+ } from "./chunk-SOPTBXF3.js";
9
9
  export {
10
10
  ThrottleAuthError,
11
11
  createAccount,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  useAuth
3
- } from "../../chunk-IJ3DNMNG.js";
4
- import "../../chunk-OYNGTNOS.js";
3
+ } from "../../chunk-5GLLPF6L.js";
4
+ import "../../chunk-SOPTBXF3.js";
5
5
 
6
6
  // src/react/forms/SignIn.tsx
7
7
  import { useId, useState as useState2 } from "react";
@@ -272,6 +272,7 @@ function createThrottleAuth(config) {
272
272
  async function call(method, path, body) {
273
273
  let retriedAuth = false;
274
274
  let retriedStepUp = false;
275
+ let sendStepUp = false;
275
276
  for (; ; ) {
276
277
  const token = await accessToken();
277
278
  try {
@@ -281,7 +282,7 @@ function createThrottleAuth(config) {
281
282
  path,
282
283
  body,
283
284
  accessToken: token ?? void 0,
284
- stepUpToken: stepUpToken ?? void 0
285
+ stepUpToken: sendStepUp ? stepUpToken ?? void 0 : void 0
285
286
  });
286
287
  } catch (err) {
287
288
  const e = err;
@@ -293,8 +294,10 @@ function createThrottleAuth(config) {
293
294
  }
294
295
  if (e.code === "step_up_required" && !retriedStepUp) {
295
296
  retriedStepUp = true;
296
- stepUpToken = null;
297
- if (await ensureStepUp()) continue;
297
+ if (await ensureStepUp()) {
298
+ sendStepUp = true;
299
+ continue;
300
+ }
298
301
  }
299
302
  throw err;
300
303
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/index.tsx","../../src/react/context.tsx","../../src/errors.ts","../../src/request.ts","../../src/session.ts","../../src/storage.ts","../../src/client.ts","../../src/account.ts","../../src/react/hooks.ts"],"sourcesContent":["export { ThrottleAuthProvider, useAuth, useAuthContext } from './context';\nexport type { ThrottleAuthProviderProps, UseAuthResult } from './context';\nexport { useCustomer, useAddresses, useSavedCards, useOrders, useInvoices, useSubscriptions } from './hooks';\nexport type { Resource } from './hooks';\nexport * from '../index';\n","import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport type { ReactNode } from 'react';\nimport { createThrottleAuth, type ThrottleAuth } from '../client';\nimport { createAccount, type ThrottleAccount } from '../account';\nimport type { AuthSnapshot } from '../session';\nimport type { ThrottleAuthConfig } from '../types';\n\nexport interface ThrottleAuthProviderProps extends Partial<ThrottleAuthConfig> {\n publishableKey: string;\n /** Inject a pre-built client. Tests use it; merchants sharing one client across roots may too. */\n auth?: ThrottleAuth;\n children: ReactNode;\n}\n\ninterface ContextValue { auth: ThrottleAuth; account: ThrottleAccount }\n\nconst Ctx = createContext<ContextValue | null>(null);\n\nexport function ThrottleAuthProvider(props: ThrottleAuthProviderProps) {\n const { auth: injected, publishableKey, baseUrl, storage, onStepUpRequired, children } = props;\n\n // `storage` and `onStepUpRequired` are deliberately NOT dependencies: they are\n // read once at mount. A merchant passing `storage={memoryStorage()}` inline —\n // which the README suggests — would otherwise get a new client on every parent\n // render, and with it a fresh empty SessionState that reads as signed out.\n const value = useMemo<ContextValue>(() => {\n const auth = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired });\n return { auth, account: createAccount(auth) };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, publishableKey, baseUrl]);\n\n const restored = useRef<ThrottleAuth | null>(null);\n useEffect(() => {\n // Once per client. StrictMode runs effects twice in development, and a\n // second restore() would present the refresh token that the first one\n // just rotated away.\n if (restored.current === value.auth) return;\n restored.current = value.auth;\n void value.auth.restore();\n }, [value]);\n\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\nfunction useCtx(): ContextValue {\n const ctx = useContext(Ctx);\n if (!ctx) throw new Error('useAuth must be used inside a <ThrottleAuthProvider>');\n return ctx;\n}\n\nexport function useAuthContext(): ContextValue { return useCtx(); }\n\nexport type UseAuthResult = AuthSnapshot & {\n isLoading: boolean;\n isAuthenticated: boolean;\n auth: ThrottleAuth;\n account: ThrottleAccount;\n};\n\nexport function useAuth(): UseAuthResult {\n const { auth, account } = useCtx();\n const snapshot = useSyncExternalStore(auth.subscribe, auth.getSnapshot, auth.getSnapshot);\n return {\n ...snapshot,\n isLoading: snapshot.status === 'loading',\n isAuthenticated: snapshot.status === 'authenticated',\n auth,\n account,\n };\n}\n","export class ThrottleAuthError extends Error {\n readonly code: string;\n readonly statusCode: number;\n readonly retryAfter?: number;\n readonly details?: unknown;\n\n constructor(args: { code: string; message: string; statusCode: number; retryAfter?: number; details?: unknown }) {\n super(args.message);\n this.name = 'ThrottleAuthError';\n this.code = args.code;\n this.statusCode = args.statusCode;\n this.retryAfter = args.retryAfter;\n this.details = args.details;\n }\n}\n","import { ThrottleAuthError } from './errors';\n\nexport interface RequestInput {\n method: string;\n path: string;\n baseUrl: string;\n publishableKey: string;\n accessToken?: string;\n stepUpToken?: string;\n body?: unknown;\n}\n\n/** Returns the UNWRAPPED `data` from the `{ data: … }` envelope. Throws ThrottleAuthError otherwise. */\nexport async function request(input: RequestInput): Promise<any> {\n const headers: Record<string, string> = { 'X-API-Key': input.publishableKey };\n if (input.body !== undefined) headers['Content-Type'] = 'application/json';\n if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;\n if (input.stepUpToken) headers['X-Step-Up'] = input.stepUpToken;\n\n const res = await fetch(`${input.baseUrl}/v1/storefront${input.path}`, {\n method: input.method,\n headers,\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — an HTML gateway page, say. Leave parsed empty so the\n // branch below still throws a typed error rather than a raw SyntaxError.\n }\n\n if (!res.ok) {\n const e = parsed?.error ?? {};\n // `Retry-After` is server-controlled but not trusted: Number('soon') is\n // NaN, and a NaN reaches the UI as \"wait NaN seconds\".\n const ra = Number(res.headers.get('retry-after'));\n throw new ThrottleAuthError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n retryAfter: Number.isFinite(ra) && ra >= 0 ? ra : undefined,\n details: e.details,\n });\n }\n\n // `'data' in parsed`, not `parsed?.data ?? parsed`: a legitimate\n // `{ \"data\": null }` is nullish, and `??` would fall through and hand the\n // caller the whole envelope instead of the null the API actually sent.\n return parsed && typeof parsed === 'object' && 'data' in parsed ? parsed.data : (parsed ?? {});\n}\n","import type { Customer, Session } from './types';\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\nexport interface AuthSnapshot {\n status: AuthStatus;\n customer: Customer | null;\n /**\n * False here does NOT only mean \"we emailed them and they have not clicked\".\n * While it is false the API may also be withholding pre-existing account\n * data behind the claim cutoff — empty order lists and a 403 on `PATCH /me`\n * are expected, not broken. See the plan header.\n */\n emailVerified: boolean;\n}\n\ntype Listener = (snapshot: AuthSnapshot) => void;\n\nexport class SessionState {\n #snapshot: AuthSnapshot = { status: 'loading', customer: null, emailVerified: false };\n #accessToken: string | null = null;\n #expiresAt: number | null = null;\n #listeners = new Set<Listener>();\n\n get(): AuthSnapshot { return this.#snapshot; }\n accessToken(): string | null { return this.#accessToken; }\n expiresAt(): number | null { return this.#expiresAt; }\n\n setSession(session: Session): void {\n this.#accessToken = session.accessToken;\n this.#expiresAt = Date.parse(session.expiresAt);\n this.#publish({\n status: 'authenticated',\n customer: session.customer,\n emailVerified: Boolean(session.customer?.emailVerified),\n });\n }\n\n setStatus(status: AuthStatus): void {\n this.#publish({ ...this.#snapshot, status });\n }\n\n clear(): void {\n this.#accessToken = null;\n this.#expiresAt = null;\n this.#publish({ status: 'unauthenticated', customer: null, emailVerified: false });\n }\n\n subscribe(fn: Listener): () => void {\n this.#listeners.add(fn);\n return () => { this.#listeners.delete(fn); };\n }\n\n #publish(next: AuthSnapshot): void {\n // Identity matters: React's useSyncExternalStore re-renders on reference\n // change, and a token refresh every ten minutes must not re-render the\n // whole account area when nothing visible changed.\n const same =\n next.status === this.#snapshot.status &&\n next.emailVerified === this.#snapshot.emailVerified &&\n customerEquals(next.customer, this.#snapshot.customer);\n if (same) return;\n this.#snapshot = next;\n for (const fn of [...this.#listeners]) {\n // A merchant's subscriber throwing must not stop the rest from hearing.\n try { fn(next); } catch { /* merchant callback */ }\n }\n }\n}\n\nfunction customerEquals(a: Customer | null, b: Customer | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<keyof Customer>;\n for (const k of keys) if (a[k] !== b[k]) return false;\n return true;\n}\n","import type { AuthStorage } from './types';\n\nexport type { AuthStorage };\n\nexport function memoryStorage(): AuthStorage {\n const map = new Map<string, string>();\n return {\n get: (k) => map.get(k) ?? null,\n set: (k, v) => void map.set(k, v),\n remove: (k) => void map.delete(k),\n };\n}\n\n/**\n * localStorage when it works, memory when it does not. Safari private mode,\n * hardened browser settings and server-side rendering all make localStorage\n * either absent or throwing; none of them may take the storefront down.\n */\nexport function localStorageAdapter(): AuthStorage {\n const fallback = memoryStorage();\n const ls = (): Storage | null => {\n try {\n return typeof localStorage !== 'undefined' ? localStorage : null;\n } catch {\n return null;\n }\n };\n return {\n get(k) {\n try {\n const v = ls()?.getItem(k);\n if (v != null) return v;\n } catch { /* fall through */ }\n return fallback.get(k);\n },\n set(k, v) {\n fallback.set(k, v);\n try { ls()?.setItem(k, v); } catch { /* memory-only this session */ }\n },\n remove(k) {\n fallback.remove(k);\n try { ls()?.removeItem(k); } catch { /* already gone */ }\n },\n };\n}\n","import { request } from './request';\nimport { ThrottleAuthError } from './errors';\nimport { SessionState, type AuthSnapshot } from './session';\nimport { localStorageAdapter, type AuthStorage } from './storage';\nimport type { Customer, Session, ThrottleAuthConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n/** Refresh this far before expiry so ordinary calls rarely see a 401 at all. */\nconst REFRESH_SKEW_MS = 30_000;\n\nexport interface DeviceSession {\n id: string;\n userAgent: string | null;\n ipPrefix: string | null;\n createdAt: string;\n lastUsedAt: string;\n current: boolean;\n}\n\nexport interface ThrottleAuth {\n register(email: string, password: string): Promise<{ status: 'active' | 'pending' }>;\n signIn(email: string, password: string): Promise<void>;\n signOut(): Promise<void>;\n signOutEverywhere(): Promise<void>;\n restore(): Promise<void>;\n getSnapshot(): AuthSnapshot;\n subscribe(fn: (s: AuthSnapshot) => void): () => void;\n stepUp(password: string): Promise<void>;\n call<T = any>(method: string, path: string, body?: unknown): Promise<T>;\n verifyEmail(token: string): Promise<void>;\n resendVerification(): Promise<void>;\n forgotPassword(email: string): Promise<void>;\n resetPassword(token: string, password: string): Promise<void>;\n changePassword(password: string): Promise<void>;\n listSessions(): Promise<DeviceSession[]>;\n revokeSession(id: string): Promise<void>;\n}\n\nexport function createThrottleAuth(config: ThrottleAuthConfig): ThrottleAuth {\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const storage: AuthStorage = config.storage ?? localStorageAdapter();\n // Namespaced by key so two Throttle applications on one origin cannot read\n // each other's refresh token out of shared localStorage.\n const storageKey = `throttle_auth_rt:${config.publishableKey}`;\n const lockName = `throttle_auth_refresh:${config.publishableKey}`;\n\n const state = new SessionState();\n let refreshInFlight: Promise<void> | null = null;\n let stepUpToken: string | null = null;\n let stepUpExpiresAt = 0;\n\n const base = { baseUrl, publishableKey: config.publishableKey };\n\n async function persist(session: Session): Promise<void> {\n state.setSession(session);\n await storage.set(storageKey, session.refreshToken);\n }\n\n async function forget(): Promise<void> {\n stepUpToken = null;\n state.clear();\n await storage.remove(storageKey);\n }\n\n /**\n * Cross-tab single flight. Two tabs refreshing at once would each present the\n * same stored token; the second is a replay, and the server kills the family.\n * Web Locks serialises them; where it is unavailable the in-process guard\n * below still covers the common single-tab case.\n */\n function withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as any).navigator?.locks;\n return locks?.request ? locks.request(lockName, fn) : fn();\n }\n\n async function doRefresh(): Promise<void> {\n await withLock(async () => {\n // Re-read INSIDE the lock: another tab may have rotated while we waited,\n // and presenting the token we read before the lock would be a replay.\n const refreshToken = await storage.get(storageKey);\n if (!refreshToken) {\n await forget();\n throw new ThrottleAuthError({ code: 'session_revoked', message: 'No session', statusCode: 401 });\n }\n try {\n const data = await request({ ...base, method: 'POST', path: '/auth/refresh', body: { refreshToken } });\n await persist(data as Session);\n } catch (err) {\n // Any refusal of a refresh token is terminal — there is nothing left to\n // retry with. Drop it rather than letting a dead token sit in storage.\n await forget();\n throw err;\n }\n });\n }\n\n function refresh(): Promise<void> {\n if (!refreshInFlight) {\n refreshInFlight = doRefresh().finally(() => { refreshInFlight = null; });\n }\n return refreshInFlight;\n }\n\n async function accessToken(): Promise<string | null> {\n const expires = state.expiresAt();\n if (state.accessToken() && expires && expires - Date.now() > REFRESH_SKEW_MS) {\n return state.accessToken();\n }\n if (await storage.get(storageKey)) {\n await refresh();\n return state.accessToken();\n }\n return state.accessToken();\n }\n\n async function ensureStepUp(): Promise<boolean> {\n if (stepUpToken && stepUpExpiresAt > Date.now()) return true;\n if (!config.onStepUpRequired) return false;\n const password = await config.onStepUpRequired();\n if (!password) return false;\n await stepUp(password);\n return true;\n }\n\n async function stepUp(password: string): Promise<void> {\n const data = await request({\n ...base, method: 'POST', path: '/auth/step-up',\n accessToken: await accessToken() ?? undefined, body: { password },\n });\n stepUpToken = data.stepUpToken;\n stepUpExpiresAt = Date.now() + (Number(data.expiresIn) || 300) * 1000 - 5_000;\n }\n\n async function call<T = any>(method: string, path: string, body?: unknown): Promise<T> {\n let retriedAuth = false;\n let retriedStepUp = false;\n\n for (;;) {\n const token = await accessToken();\n try {\n return await request({\n ...base, method, path, body,\n accessToken: token ?? undefined,\n stepUpToken: stepUpToken ?? undefined,\n });\n } catch (err) {\n const e = err as ThrottleAuthError;\n // `session_revoked` belongs here too. The server returns it for a\n // stale session EPOCH as well as for a genuinely dead session, and a\n // changed password bumps the epoch while deliberately sparing the\n // current session — so one refresh recovers it. When the session\n // really is dead the refresh fails and forget() runs, which is the\n // same outcome, one request later.\n const expired = e.statusCode === 401 &&\n (e.code === 'unauthorized' || e.code === 'token_expired' || e.code === 'session_revoked');\n if (expired && !retriedAuth) {\n retriedAuth = true;\n await refresh(); // shared — ten callers here produce ONE request\n continue;\n }\n if (e.code === 'step_up_required' && !retriedStepUp) {\n retriedStepUp = true;\n stepUpToken = null;\n if (await ensureStepUp()) continue;\n }\n throw err;\n }\n }\n }\n\n return {\n async register(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/register', body: { email, password } });\n if (data?.status === 'pending') return { status: 'pending' };\n await persist(data as Session);\n return { status: 'active' };\n },\n\n async signIn(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/login', body: { email, password } });\n await persist(data as Session);\n },\n\n async signOut() {\n try { await call('POST', '/auth/logout'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async signOutEverywhere() {\n try { await call('POST', '/auth/logout-all'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async restore() {\n if (!(await storage.get(storageKey))) { state.setStatus('unauthenticated'); return; }\n try { await refresh(); } catch { /* forget() already ran */ }\n },\n\n getSnapshot: () => state.get(),\n subscribe: (fn) => state.subscribe(fn),\n stepUp,\n call,\n\n async verifyEmail(token) {\n await request({ ...base, method: 'POST', path: '/auth/verify-email', body: { token } });\n // `ver` is a claim baked into the access token, and the claim cutoff is\n // read from the session row. Without a new token the buyer stays gated —\n // and their own order history stays hidden — for the rest of this\n // token's ten minutes.\n if (await storage.get(storageKey)) {\n try { await refresh(); } catch { /* verification still succeeded */ }\n }\n },\n\n async resendVerification() {\n await call('POST', '/auth/verify-email/resend');\n },\n\n async forgotPassword(email) {\n // Always 202, whether or not the address has an account. Do not infer\n // anything from it and do not tell the buyer whether it existed.\n await request({ ...base, method: 'POST', path: '/auth/forgot-password', body: { email } });\n },\n\n async resetPassword(token, password) {\n await request({ ...base, method: 'POST', path: '/auth/reset-password', body: { token, password } });\n // The server revokes every session on reset, this one included.\n await forget();\n },\n\n async changePassword(password) {\n await call('POST', '/me/change-password', { password });\n // The change bumps the session epoch. This session was deliberately\n // spared, but the access token in hand now carries a stale epoch and the\n // next write would 401. Rotate now rather than on the buyer's next click\n // — and do not let a failed rotation report a change that already\n // succeeded server-side as a failure.\n try { await refresh(); } catch { /* the password change already succeeded */ }\n },\n\n async listSessions() {\n return call<DeviceSession[]>('GET', '/me/sessions');\n },\n\n async revokeSession(id) {\n const mine = await call<DeviceSession[]>('GET', '/me/sessions');\n const current = mine.find((s) => s.current);\n await call('DELETE', `/me/sessions/${encodeURIComponent(id)}`);\n if (current?.id === id) await forget();\n },\n };\n}\n\nexport type { AuthSnapshot, Customer, Session, ThrottleAuthConfig };\n","import type { ThrottleAuth } from './client';\nimport type {\n Address, AddressInput, Customer, Invoice, Order,\n PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n\nexport interface Page { cursor?: string; limit?: number }\n\nexport interface ThrottleAccount {\n getProfile(): Promise<Customer>;\n updateProfile(patch: ProfilePatch): Promise<Customer>;\n listAddresses(): Promise<Address[]>;\n createAddress(input: AddressInput): Promise<Address>;\n updateAddress(id: string, patch: Partial<AddressInput>): Promise<Address>;\n deleteAddress(id: string): Promise<void>;\n listPaymentMethods(): Promise<PaymentMethod[]>;\n setDefaultPaymentMethod(id: string): Promise<PaymentMethod>;\n deletePaymentMethod(id: string): Promise<void>;\n listOrders(page?: Page): Promise<Order[]>;\n getOrder(id: string): Promise<Order>;\n listInvoices(page?: Page): Promise<Invoice[]>;\n getInvoice(id: string): Promise<Invoice>;\n listSubscriptions(page?: Page): Promise<Subscription[]>;\n getSubscription(id: string): Promise<Subscription>;\n cancelSubscription(id: string, opts?: { atPeriodEnd?: boolean }): Promise<Subscription>;\n pauseSubscription(id: string): Promise<Subscription>;\n resumeSubscription(id: string): Promise<Subscription>;\n claimCart(cartId: string): Promise<{ cartId: string; customerId: string }>;\n}\n\nconst seg = (v: string) => encodeURIComponent(v);\n\nfunction query(page?: Page): string {\n const params = new URLSearchParams();\n if (page?.cursor) params.set('cursor', page.cursor);\n if (page?.limit != null) params.set('limit', String(page.limit));\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\nexport function createAccount(auth: ThrottleAuth): ThrottleAccount {\n const { call } = auth;\n return {\n getProfile: () => call('GET', '/me'),\n updateProfile: (patch) => call('PATCH', '/me', patch),\n\n listAddresses: () => call('GET', '/me/addresses'),\n createAddress: (input) => call('POST', '/me/addresses', input),\n updateAddress: (id, patch) => call('PATCH', `/me/addresses/${seg(id)}`, patch),\n deleteAddress: (id) => call('DELETE', `/me/addresses/${seg(id)}`),\n\n listPaymentMethods: () => call('GET', '/me/payment-methods'),\n // The platform has no \"change this subscription's card\" capability, so\n // this is how a buyer changes what they are billed on: move the default.\n setDefaultPaymentMethod: (id) => call('PATCH', `/me/payment-methods/${seg(id)}`, { isDefault: true }),\n deletePaymentMethod: (id) => call('DELETE', `/me/payment-methods/${seg(id)}`),\n\n listOrders: (page) => call('GET', `/me/orders${query(page)}`),\n getOrder: (id) => call('GET', `/me/orders/${seg(id)}`),\n listInvoices: (page) => call('GET', `/me/invoices${query(page)}`),\n getInvoice: (id) => call('GET', `/me/invoices/${seg(id)}`),\n listSubscriptions: (page) => call('GET', `/me/subscriptions${query(page)}`),\n getSubscription: (id) => call('GET', `/me/subscriptions/${seg(id)}`),\n\n cancelSubscription: (id, opts) => call('POST', `/me/subscriptions/${seg(id)}/cancel`, opts),\n pauseSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/pause`),\n resumeSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/resume`),\n\n claimCart: (cartId) => call('POST', `/me/carts/${seg(cartId)}/claim`),\n };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { ThrottleAuthError } from '../errors';\nimport type { Address, AddressInput, Customer, Invoice, Order, PaymentMethod, ProfilePatch, Subscription } from '../types';\nimport type { Page } from '../account';\nimport { useAuth } from './context';\n\nexport interface Resource<T> {\n data: T;\n isLoading: boolean;\n error: ThrottleAuthError | null;\n refetch(): Promise<void>;\n}\n\nfunction asError(err: unknown): ThrottleAuthError {\n if (err instanceof ThrottleAuthError) return err;\n const anyErr = err as any;\n return new ThrottleAuthError({\n code: anyErr?.code ?? 'request_failed',\n message: anyErr?.message ?? String(err),\n statusCode: anyErr?.statusCode ?? 0,\n });\n}\n\n/**\n * One fetch-on-auth-change primitive behind every resource hook. `emailVerified`\n * is a dependency on purpose: verifying lifts the claim cutoff, and lists that\n * were legitimately empty a moment ago are not empty any more.\n */\nfunction useResource<T>(empty: T, load: () => Promise<T>, deps: unknown[]): Resource<T> {\n const { status, emailVerified } = useAuth();\n const [data, setData] = useState<T>(empty);\n const [isLoading, setIsLoading] = useState(status === 'loading');\n const [error, setError] = useState<ThrottleAuthError | null>(null);\n const gen = useRef(0);\n const alive = useRef(true);\n\n useEffect(() => {\n // Set on mount, not only cleared on unmount: React StrictMode mounts,\n // unmounts and remounts in development, and a ref that is only ever set\n // to false would silently discard every response for the rest of the\n // component's life.\n alive.current = true;\n return () => { alive.current = false; gen.current++; };\n }, []);\n\n const refetch = useCallback(async () => {\n const mine = ++gen.current;\n setIsLoading(true);\n try {\n const next = await load();\n if (!alive.current || gen.current !== mine) return;\n setData(next);\n setError(null);\n } catch (err) {\n if (!alive.current || gen.current !== mine) return;\n setError(asError(err));\n } finally {\n if (alive.current && gen.current === mine) setIsLoading(false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n\n useEffect(() => {\n if (status !== 'authenticated') {\n gen.current++; // abandon anything in flight\n setData(empty);\n setError(null);\n setIsLoading(status === 'loading');\n return;\n }\n void refetch();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [status, emailVerified, refetch]);\n\n return { data, isLoading, error, refetch };\n}\n\nexport function useCustomer(): Resource<Customer | null> & { update(patch: ProfilePatch): Promise<void> } {\n const { account } = useAuth();\n const res = useResource<Customer | null>(null, () => account.getProfile(), [account]);\n const update = useCallback(async (patch: ProfilePatch) => {\n await account.updateProfile(patch);\n await res.refetch();\n }, [account, res]);\n return { ...res, update };\n}\n\nexport function useAddresses(): Resource<Address[]> & {\n create(input: AddressInput): Promise<void>;\n update(id: string, patch: Partial<AddressInput>): Promise<void>;\n remove(id: string): Promise<void>;\n} {\n const { account } = useAuth();\n const res = useResource<Address[]>([], () => account.listAddresses(), [account]);\n const after = async (p: Promise<unknown>) => { await p; await res.refetch(); };\n return {\n ...res,\n create: (input) => after(account.createAddress(input)),\n update: (id, patch) => after(account.updateAddress(id, patch)),\n remove: (id) => after(account.deleteAddress(id)),\n };\n}\n\nexport function useSavedCards(): Resource<PaymentMethod[]> & {\n setDefault(id: string): Promise<void>;\n remove(id: string): Promise<void>;\n} {\n const { account } = useAuth();\n const res = useResource<PaymentMethod[]>([], () => account.listPaymentMethods(), [account]);\n const after = async (p: Promise<unknown>) => { await p; await res.refetch(); };\n return {\n ...res,\n setDefault: (id) => after(account.setDefaultPaymentMethod(id)),\n remove: (id) => after(account.deletePaymentMethod(id)),\n };\n}\n\nexport function useOrders(page?: Page): Resource<Order[]> {\n const { account } = useAuth();\n return useResource<Order[]>([], () => account.listOrders(page), [account, page?.cursor, page?.limit]);\n}\n\nexport function useInvoices(page?: Page): Resource<Invoice[]> {\n const { account } = useAuth();\n return useResource<Invoice[]>([], () => account.listInvoices(page), [account, page?.cursor, page?.limit]);\n}\n\nexport function useSubscriptions(page?: Page): Resource<Subscription[]> & {\n cancel(id: string, opts?: { atPeriodEnd?: boolean }): Promise<void>;\n pause(id: string): Promise<void>;\n resume(id: string): Promise<void>;\n} {\n const { account } = useAuth();\n const res = useResource<Subscription[]>([], () => account.listSubscriptions(page),\n [account, page?.cursor, page?.limit]);\n const after = async (p: Promise<unknown>) => { await p; await res.refetch(); };\n return {\n ...res,\n cancel: (id, opts) => after(account.cancelSubscription(id, opts)),\n pause: (id) => after(account.pauseSubscription(id)),\n resume: (id) => after(account.resumeSubscription(id)),\n };\n}\n"],"mappings":"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","names":["import_react"]}
1
+ {"version":3,"sources":["../../src/react/index.tsx","../../src/react/context.tsx","../../src/errors.ts","../../src/request.ts","../../src/session.ts","../../src/storage.ts","../../src/client.ts","../../src/account.ts","../../src/react/hooks.ts"],"sourcesContent":["export { ThrottleAuthProvider, useAuth, useAuthContext } from './context';\nexport type { ThrottleAuthProviderProps, UseAuthResult } from './context';\nexport { useCustomer, useAddresses, useSavedCards, useOrders, useInvoices, useSubscriptions } from './hooks';\nexport type { Resource } from './hooks';\nexport * from '../index';\n","import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport type { ReactNode } from 'react';\nimport { createThrottleAuth, type ThrottleAuth } from '../client';\nimport { createAccount, type ThrottleAccount } from '../account';\nimport type { AuthSnapshot } from '../session';\nimport type { ThrottleAuthConfig } from '../types';\n\nexport interface ThrottleAuthProviderProps extends Partial<ThrottleAuthConfig> {\n publishableKey: string;\n /** Inject a pre-built client. Tests use it; merchants sharing one client across roots may too. */\n auth?: ThrottleAuth;\n children: ReactNode;\n}\n\ninterface ContextValue { auth: ThrottleAuth; account: ThrottleAccount }\n\nconst Ctx = createContext<ContextValue | null>(null);\n\nexport function ThrottleAuthProvider(props: ThrottleAuthProviderProps) {\n const { auth: injected, publishableKey, baseUrl, storage, onStepUpRequired, children } = props;\n\n // `storage` and `onStepUpRequired` are deliberately NOT dependencies: they are\n // read once at mount. A merchant passing `storage={memoryStorage()}` inline —\n // which the README suggests — would otherwise get a new client on every parent\n // render, and with it a fresh empty SessionState that reads as signed out.\n const value = useMemo<ContextValue>(() => {\n const auth = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired });\n return { auth, account: createAccount(auth) };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, publishableKey, baseUrl]);\n\n const restored = useRef<ThrottleAuth | null>(null);\n useEffect(() => {\n // Once per client. StrictMode runs effects twice in development, and a\n // second restore() would present the refresh token that the first one\n // just rotated away.\n if (restored.current === value.auth) return;\n restored.current = value.auth;\n void value.auth.restore();\n }, [value]);\n\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\nfunction useCtx(): ContextValue {\n const ctx = useContext(Ctx);\n if (!ctx) throw new Error('useAuth must be used inside a <ThrottleAuthProvider>');\n return ctx;\n}\n\nexport function useAuthContext(): ContextValue { return useCtx(); }\n\nexport type UseAuthResult = AuthSnapshot & {\n isLoading: boolean;\n isAuthenticated: boolean;\n auth: ThrottleAuth;\n account: ThrottleAccount;\n};\n\nexport function useAuth(): UseAuthResult {\n const { auth, account } = useCtx();\n const snapshot = useSyncExternalStore(auth.subscribe, auth.getSnapshot, auth.getSnapshot);\n return {\n ...snapshot,\n isLoading: snapshot.status === 'loading',\n isAuthenticated: snapshot.status === 'authenticated',\n auth,\n account,\n };\n}\n","export class ThrottleAuthError extends Error {\n readonly code: string;\n readonly statusCode: number;\n readonly retryAfter?: number;\n readonly details?: unknown;\n\n constructor(args: { code: string; message: string; statusCode: number; retryAfter?: number; details?: unknown }) {\n super(args.message);\n this.name = 'ThrottleAuthError';\n this.code = args.code;\n this.statusCode = args.statusCode;\n this.retryAfter = args.retryAfter;\n this.details = args.details;\n }\n}\n","import { ThrottleAuthError } from './errors';\n\nexport interface RequestInput {\n method: string;\n path: string;\n baseUrl: string;\n publishableKey: string;\n accessToken?: string;\n stepUpToken?: string;\n body?: unknown;\n}\n\n/** Returns the UNWRAPPED `data` from the `{ data: … }` envelope. Throws ThrottleAuthError otherwise. */\nexport async function request(input: RequestInput): Promise<any> {\n const headers: Record<string, string> = { 'X-API-Key': input.publishableKey };\n if (input.body !== undefined) headers['Content-Type'] = 'application/json';\n if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;\n if (input.stepUpToken) headers['X-Step-Up'] = input.stepUpToken;\n\n const res = await fetch(`${input.baseUrl}/v1/storefront${input.path}`, {\n method: input.method,\n headers,\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — an HTML gateway page, say. Leave parsed empty so the\n // branch below still throws a typed error rather than a raw SyntaxError.\n }\n\n if (!res.ok) {\n const e = parsed?.error ?? {};\n // `Retry-After` is server-controlled but not trusted: Number('soon') is\n // NaN, and a NaN reaches the UI as \"wait NaN seconds\".\n const ra = Number(res.headers.get('retry-after'));\n throw new ThrottleAuthError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n retryAfter: Number.isFinite(ra) && ra >= 0 ? ra : undefined,\n details: e.details,\n });\n }\n\n // `'data' in parsed`, not `parsed?.data ?? parsed`: a legitimate\n // `{ \"data\": null }` is nullish, and `??` would fall through and hand the\n // caller the whole envelope instead of the null the API actually sent.\n return parsed && typeof parsed === 'object' && 'data' in parsed ? parsed.data : (parsed ?? {});\n}\n","import type { Customer, Session } from './types';\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\nexport interface AuthSnapshot {\n status: AuthStatus;\n customer: Customer | null;\n /**\n * False here does NOT only mean \"we emailed them and they have not clicked\".\n * While it is false the API may also be withholding pre-existing account\n * data behind the claim cutoff — empty order lists and a 403 on `PATCH /me`\n * are expected, not broken. See the plan header.\n */\n emailVerified: boolean;\n}\n\ntype Listener = (snapshot: AuthSnapshot) => void;\n\nexport class SessionState {\n #snapshot: AuthSnapshot = { status: 'loading', customer: null, emailVerified: false };\n #accessToken: string | null = null;\n #expiresAt: number | null = null;\n #listeners = new Set<Listener>();\n\n get(): AuthSnapshot { return this.#snapshot; }\n accessToken(): string | null { return this.#accessToken; }\n expiresAt(): number | null { return this.#expiresAt; }\n\n setSession(session: Session): void {\n this.#accessToken = session.accessToken;\n this.#expiresAt = Date.parse(session.expiresAt);\n this.#publish({\n status: 'authenticated',\n customer: session.customer,\n emailVerified: Boolean(session.customer?.emailVerified),\n });\n }\n\n setStatus(status: AuthStatus): void {\n this.#publish({ ...this.#snapshot, status });\n }\n\n clear(): void {\n this.#accessToken = null;\n this.#expiresAt = null;\n this.#publish({ status: 'unauthenticated', customer: null, emailVerified: false });\n }\n\n subscribe(fn: Listener): () => void {\n this.#listeners.add(fn);\n return () => { this.#listeners.delete(fn); };\n }\n\n #publish(next: AuthSnapshot): void {\n // Identity matters: React's useSyncExternalStore re-renders on reference\n // change, and a token refresh every ten minutes must not re-render the\n // whole account area when nothing visible changed.\n const same =\n next.status === this.#snapshot.status &&\n next.emailVerified === this.#snapshot.emailVerified &&\n customerEquals(next.customer, this.#snapshot.customer);\n if (same) return;\n this.#snapshot = next;\n for (const fn of [...this.#listeners]) {\n // A merchant's subscriber throwing must not stop the rest from hearing.\n try { fn(next); } catch { /* merchant callback */ }\n }\n }\n}\n\nfunction customerEquals(a: Customer | null, b: Customer | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<keyof Customer>;\n for (const k of keys) if (a[k] !== b[k]) return false;\n return true;\n}\n","import type { AuthStorage } from './types';\n\nexport type { AuthStorage };\n\nexport function memoryStorage(): AuthStorage {\n const map = new Map<string, string>();\n return {\n get: (k) => map.get(k) ?? null,\n set: (k, v) => void map.set(k, v),\n remove: (k) => void map.delete(k),\n };\n}\n\n/**\n * localStorage when it works, memory when it does not. Safari private mode,\n * hardened browser settings and server-side rendering all make localStorage\n * either absent or throwing; none of them may take the storefront down.\n */\nexport function localStorageAdapter(): AuthStorage {\n const fallback = memoryStorage();\n const ls = (): Storage | null => {\n try {\n return typeof localStorage !== 'undefined' ? localStorage : null;\n } catch {\n return null;\n }\n };\n return {\n get(k) {\n try {\n const v = ls()?.getItem(k);\n if (v != null) return v;\n } catch { /* fall through */ }\n return fallback.get(k);\n },\n set(k, v) {\n fallback.set(k, v);\n try { ls()?.setItem(k, v); } catch { /* memory-only this session */ }\n },\n remove(k) {\n fallback.remove(k);\n try { ls()?.removeItem(k); } catch { /* already gone */ }\n },\n };\n}\n","import { request } from './request';\nimport { ThrottleAuthError } from './errors';\nimport { SessionState, type AuthSnapshot } from './session';\nimport { localStorageAdapter, type AuthStorage } from './storage';\nimport type { Customer, Session, ThrottleAuthConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n/** Refresh this far before expiry so ordinary calls rarely see a 401 at all. */\nconst REFRESH_SKEW_MS = 30_000;\n\nexport interface DeviceSession {\n id: string;\n userAgent: string | null;\n ipPrefix: string | null;\n createdAt: string;\n lastUsedAt: string;\n current: boolean;\n}\n\nexport interface ThrottleAuth {\n register(email: string, password: string): Promise<{ status: 'active' | 'pending' }>;\n signIn(email: string, password: string): Promise<void>;\n signOut(): Promise<void>;\n signOutEverywhere(): Promise<void>;\n restore(): Promise<void>;\n getSnapshot(): AuthSnapshot;\n subscribe(fn: (s: AuthSnapshot) => void): () => void;\n stepUp(password: string): Promise<void>;\n call<T = any>(method: string, path: string, body?: unknown): Promise<T>;\n verifyEmail(token: string): Promise<void>;\n resendVerification(): Promise<void>;\n forgotPassword(email: string): Promise<void>;\n resetPassword(token: string, password: string): Promise<void>;\n changePassword(password: string): Promise<void>;\n listSessions(): Promise<DeviceSession[]>;\n revokeSession(id: string): Promise<void>;\n}\n\nexport function createThrottleAuth(config: ThrottleAuthConfig): ThrottleAuth {\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const storage: AuthStorage = config.storage ?? localStorageAdapter();\n // Namespaced by key so two Throttle applications on one origin cannot read\n // each other's refresh token out of shared localStorage.\n const storageKey = `throttle_auth_rt:${config.publishableKey}`;\n const lockName = `throttle_auth_refresh:${config.publishableKey}`;\n\n const state = new SessionState();\n let refreshInFlight: Promise<void> | null = null;\n let stepUpToken: string | null = null;\n let stepUpExpiresAt = 0;\n\n const base = { baseUrl, publishableKey: config.publishableKey };\n\n async function persist(session: Session): Promise<void> {\n state.setSession(session);\n await storage.set(storageKey, session.refreshToken);\n }\n\n async function forget(): Promise<void> {\n stepUpToken = null;\n state.clear();\n await storage.remove(storageKey);\n }\n\n /**\n * Cross-tab single flight. Two tabs refreshing at once would each present the\n * same stored token; the second is a replay, and the server kills the family.\n * Web Locks serialises them; where it is unavailable the in-process guard\n * below still covers the common single-tab case.\n */\n function withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as any).navigator?.locks;\n return locks?.request ? locks.request(lockName, fn) : fn();\n }\n\n async function doRefresh(): Promise<void> {\n await withLock(async () => {\n // Re-read INSIDE the lock: another tab may have rotated while we waited,\n // and presenting the token we read before the lock would be a replay.\n const refreshToken = await storage.get(storageKey);\n if (!refreshToken) {\n await forget();\n throw new ThrottleAuthError({ code: 'session_revoked', message: 'No session', statusCode: 401 });\n }\n try {\n const data = await request({ ...base, method: 'POST', path: '/auth/refresh', body: { refreshToken } });\n await persist(data as Session);\n } catch (err) {\n // Any refusal of a refresh token is terminal — there is nothing left to\n // retry with. Drop it rather than letting a dead token sit in storage.\n await forget();\n throw err;\n }\n });\n }\n\n function refresh(): Promise<void> {\n if (!refreshInFlight) {\n refreshInFlight = doRefresh().finally(() => { refreshInFlight = null; });\n }\n return refreshInFlight;\n }\n\n async function accessToken(): Promise<string | null> {\n const expires = state.expiresAt();\n if (state.accessToken() && expires && expires - Date.now() > REFRESH_SKEW_MS) {\n return state.accessToken();\n }\n if (await storage.get(storageKey)) {\n await refresh();\n return state.accessToken();\n }\n return state.accessToken();\n }\n\n async function ensureStepUp(): Promise<boolean> {\n if (stepUpToken && stepUpExpiresAt > Date.now()) return true;\n if (!config.onStepUpRequired) return false;\n const password = await config.onStepUpRequired();\n if (!password) return false;\n await stepUp(password);\n return true;\n }\n\n async function stepUp(password: string): Promise<void> {\n const data = await request({\n ...base, method: 'POST', path: '/auth/step-up',\n accessToken: await accessToken() ?? undefined, body: { password },\n });\n stepUpToken = data.stepUpToken;\n stepUpExpiresAt = Date.now() + (Number(data.expiresIn) || 300) * 1000 - 5_000;\n }\n\n async function call<T = any>(method: string, path: string, body?: unknown): Promise<T> {\n let retriedAuth = false;\n let retriedStepUp = false;\n // Only true on the retry that follows a `step_up_required` 403 — never\n // attached speculatively. Sending an authorization-grade header on every\n // request once minted meant it rode along on unrelated calls too, and\n // cross-origin each of those then failed CORS preflight. The cost is one\n // extra round trip per gated call, which is how the server signals\n // step-up in the first place.\n let sendStepUp = false;\n\n for (;;) {\n const token = await accessToken();\n try {\n return await request({\n ...base, method, path, body,\n accessToken: token ?? undefined,\n stepUpToken: sendStepUp ? (stepUpToken ?? undefined) : undefined,\n });\n } catch (err) {\n const e = err as ThrottleAuthError;\n // `session_revoked` belongs here too. The server returns it for a\n // stale session EPOCH as well as for a genuinely dead session, and a\n // changed password bumps the epoch while deliberately sparing the\n // current session — so one refresh recovers it. When the session\n // really is dead the refresh fails and forget() runs, which is the\n // same outcome, one request later.\n const expired = e.statusCode === 401 &&\n (e.code === 'unauthorized' || e.code === 'token_expired' || e.code === 'session_revoked');\n if (expired && !retriedAuth) {\n retriedAuth = true;\n await refresh(); // shared — ten callers here produce ONE request\n continue;\n }\n if (e.code === 'step_up_required' && !retriedStepUp) {\n retriedStepUp = true;\n // ensureStepUp() reuses the cached token when it's still within its\n // 300s validity (no re-prompt); it was never sent above, so this is\n // the first the server has seen of it for this call.\n if (await ensureStepUp()) { sendStepUp = true; continue; }\n }\n throw err;\n }\n }\n }\n\n return {\n async register(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/register', body: { email, password } });\n if (data?.status === 'pending') return { status: 'pending' };\n await persist(data as Session);\n return { status: 'active' };\n },\n\n async signIn(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/login', body: { email, password } });\n await persist(data as Session);\n },\n\n async signOut() {\n try { await call('POST', '/auth/logout'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async signOutEverywhere() {\n try { await call('POST', '/auth/logout-all'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async restore() {\n if (!(await storage.get(storageKey))) { state.setStatus('unauthenticated'); return; }\n try { await refresh(); } catch { /* forget() already ran */ }\n },\n\n getSnapshot: () => state.get(),\n subscribe: (fn) => state.subscribe(fn),\n stepUp,\n call,\n\n async verifyEmail(token) {\n await request({ ...base, method: 'POST', path: '/auth/verify-email', body: { token } });\n // `ver` is a claim baked into the access token, and the claim cutoff is\n // read from the session row. Without a new token the buyer stays gated —\n // and their own order history stays hidden — for the rest of this\n // token's ten minutes.\n if (await storage.get(storageKey)) {\n try { await refresh(); } catch { /* verification still succeeded */ }\n }\n },\n\n async resendVerification() {\n await call('POST', '/auth/verify-email/resend');\n },\n\n async forgotPassword(email) {\n // Always 202, whether or not the address has an account. Do not infer\n // anything from it and do not tell the buyer whether it existed.\n await request({ ...base, method: 'POST', path: '/auth/forgot-password', body: { email } });\n },\n\n async resetPassword(token, password) {\n await request({ ...base, method: 'POST', path: '/auth/reset-password', body: { token, password } });\n // The server revokes every session on reset, this one included.\n await forget();\n },\n\n async changePassword(password) {\n await call('POST', '/me/change-password', { password });\n // The change bumps the session epoch. This session was deliberately\n // spared, but the access token in hand now carries a stale epoch and the\n // next write would 401. Rotate now rather than on the buyer's next click\n // — and do not let a failed rotation report a change that already\n // succeeded server-side as a failure.\n try { await refresh(); } catch { /* the password change already succeeded */ }\n },\n\n async listSessions() {\n return call<DeviceSession[]>('GET', '/me/sessions');\n },\n\n async revokeSession(id) {\n const mine = await call<DeviceSession[]>('GET', '/me/sessions');\n const current = mine.find((s) => s.current);\n await call('DELETE', `/me/sessions/${encodeURIComponent(id)}`);\n if (current?.id === id) await forget();\n },\n };\n}\n\nexport type { AuthSnapshot, Customer, Session, ThrottleAuthConfig };\n","import type { ThrottleAuth } from './client';\nimport type {\n Address, AddressInput, Customer, Invoice, Order,\n PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n\nexport interface Page { cursor?: string; limit?: number }\n\nexport interface ThrottleAccount {\n getProfile(): Promise<Customer>;\n updateProfile(patch: ProfilePatch): Promise<Customer>;\n listAddresses(): Promise<Address[]>;\n createAddress(input: AddressInput): Promise<Address>;\n updateAddress(id: string, patch: Partial<AddressInput>): Promise<Address>;\n deleteAddress(id: string): Promise<void>;\n listPaymentMethods(): Promise<PaymentMethod[]>;\n setDefaultPaymentMethod(id: string): Promise<PaymentMethod>;\n deletePaymentMethod(id: string): Promise<void>;\n listOrders(page?: Page): Promise<Order[]>;\n getOrder(id: string): Promise<Order>;\n listInvoices(page?: Page): Promise<Invoice[]>;\n getInvoice(id: string): Promise<Invoice>;\n listSubscriptions(page?: Page): Promise<Subscription[]>;\n getSubscription(id: string): Promise<Subscription>;\n cancelSubscription(id: string, opts?: { atPeriodEnd?: boolean }): Promise<Subscription>;\n pauseSubscription(id: string): Promise<Subscription>;\n resumeSubscription(id: string): Promise<Subscription>;\n claimCart(cartId: string): Promise<{ cartId: string; customerId: string }>;\n}\n\nconst seg = (v: string) => encodeURIComponent(v);\n\nfunction query(page?: Page): string {\n const params = new URLSearchParams();\n if (page?.cursor) params.set('cursor', page.cursor);\n if (page?.limit != null) params.set('limit', String(page.limit));\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\nexport function createAccount(auth: ThrottleAuth): ThrottleAccount {\n const { call } = auth;\n return {\n getProfile: () => call('GET', '/me'),\n updateProfile: (patch) => call('PATCH', '/me', patch),\n\n listAddresses: () => call('GET', '/me/addresses'),\n createAddress: (input) => call('POST', '/me/addresses', input),\n updateAddress: (id, patch) => call('PATCH', `/me/addresses/${seg(id)}`, patch),\n deleteAddress: (id) => call('DELETE', `/me/addresses/${seg(id)}`),\n\n listPaymentMethods: () => call('GET', '/me/payment-methods'),\n // The platform has no \"change this subscription's card\" capability, so\n // this is how a buyer changes what they are billed on: move the default.\n setDefaultPaymentMethod: (id) => call('PATCH', `/me/payment-methods/${seg(id)}`, { isDefault: true }),\n deletePaymentMethod: (id) => call('DELETE', `/me/payment-methods/${seg(id)}`),\n\n listOrders: (page) => call('GET', `/me/orders${query(page)}`),\n getOrder: (id) => call('GET', `/me/orders/${seg(id)}`),\n listInvoices: (page) => call('GET', `/me/invoices${query(page)}`),\n getInvoice: (id) => call('GET', `/me/invoices/${seg(id)}`),\n listSubscriptions: (page) => call('GET', `/me/subscriptions${query(page)}`),\n getSubscription: (id) => call('GET', `/me/subscriptions/${seg(id)}`),\n\n cancelSubscription: (id, opts) => call('POST', `/me/subscriptions/${seg(id)}/cancel`, opts),\n pauseSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/pause`),\n resumeSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/resume`),\n\n claimCart: (cartId) => call('POST', `/me/carts/${seg(cartId)}/claim`),\n };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { ThrottleAuthError } from '../errors';\nimport type { Address, AddressInput, Customer, Invoice, Order, PaymentMethod, ProfilePatch, Subscription } from '../types';\nimport type { Page } from '../account';\nimport { useAuth } from './context';\n\nexport interface Resource<T> {\n data: T;\n isLoading: boolean;\n error: ThrottleAuthError | null;\n refetch(): Promise<void>;\n}\n\nfunction asError(err: unknown): ThrottleAuthError {\n if (err instanceof ThrottleAuthError) return err;\n const anyErr = err as any;\n return new ThrottleAuthError({\n code: anyErr?.code ?? 'request_failed',\n message: anyErr?.message ?? String(err),\n statusCode: anyErr?.statusCode ?? 0,\n });\n}\n\n/**\n * One fetch-on-auth-change primitive behind every resource hook. `emailVerified`\n * is a dependency on purpose: verifying lifts the claim cutoff, and lists that\n * were legitimately empty a moment ago are not empty any more.\n */\nfunction useResource<T>(empty: T, load: () => Promise<T>, deps: unknown[]): Resource<T> {\n const { status, emailVerified } = useAuth();\n const [data, setData] = useState<T>(empty);\n const [isLoading, setIsLoading] = useState(status === 'loading');\n const [error, setError] = useState<ThrottleAuthError | null>(null);\n const gen = useRef(0);\n const alive = useRef(true);\n\n useEffect(() => {\n // Set on mount, not only cleared on unmount: React StrictMode mounts,\n // unmounts and remounts in development, and a ref that is only ever set\n // to false would silently discard every response for the rest of the\n // component's life.\n alive.current = true;\n return () => { alive.current = false; gen.current++; };\n }, []);\n\n const refetch = useCallback(async () => {\n const mine = ++gen.current;\n setIsLoading(true);\n try {\n const next = await load();\n if (!alive.current || gen.current !== mine) return;\n setData(next);\n setError(null);\n } catch (err) {\n if (!alive.current || gen.current !== mine) return;\n setError(asError(err));\n } finally {\n if (alive.current && gen.current === mine) setIsLoading(false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n\n useEffect(() => {\n if (status !== 'authenticated') {\n gen.current++; // abandon anything in flight\n setData(empty);\n setError(null);\n setIsLoading(status === 'loading');\n return;\n }\n void refetch();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [status, emailVerified, refetch]);\n\n return { data, isLoading, error, refetch };\n}\n\nexport function useCustomer(): Resource<Customer | null> & { update(patch: ProfilePatch): Promise<void> } {\n const { account } = useAuth();\n const res = useResource<Customer | null>(null, () => account.getProfile(), [account]);\n const update = useCallback(async (patch: ProfilePatch) => {\n await account.updateProfile(patch);\n await res.refetch();\n }, [account, res]);\n return { ...res, update };\n}\n\nexport function useAddresses(): Resource<Address[]> & {\n create(input: AddressInput): Promise<void>;\n update(id: string, patch: Partial<AddressInput>): Promise<void>;\n remove(id: string): Promise<void>;\n} {\n const { account } = useAuth();\n const res = useResource<Address[]>([], () => account.listAddresses(), [account]);\n const after = async (p: Promise<unknown>) => { await p; await res.refetch(); };\n return {\n ...res,\n create: (input) => after(account.createAddress(input)),\n update: (id, patch) => after(account.updateAddress(id, patch)),\n remove: (id) => after(account.deleteAddress(id)),\n };\n}\n\nexport function useSavedCards(): Resource<PaymentMethod[]> & {\n setDefault(id: string): Promise<void>;\n remove(id: string): Promise<void>;\n} {\n const { account } = useAuth();\n const res = useResource<PaymentMethod[]>([], () => account.listPaymentMethods(), [account]);\n const after = async (p: Promise<unknown>) => { await p; await res.refetch(); };\n return {\n ...res,\n setDefault: (id) => after(account.setDefaultPaymentMethod(id)),\n remove: (id) => after(account.deletePaymentMethod(id)),\n };\n}\n\nexport function useOrders(page?: Page): Resource<Order[]> {\n const { account } = useAuth();\n return useResource<Order[]>([], () => account.listOrders(page), [account, page?.cursor, page?.limit]);\n}\n\nexport function useInvoices(page?: Page): Resource<Invoice[]> {\n const { account } = useAuth();\n return useResource<Invoice[]>([], () => account.listInvoices(page), [account, page?.cursor, page?.limit]);\n}\n\nexport function useSubscriptions(page?: Page): Resource<Subscription[]> & {\n cancel(id: string, opts?: { atPeriodEnd?: boolean }): Promise<void>;\n pause(id: string): Promise<void>;\n resume(id: string): Promise<void>;\n} {\n const { account } = useAuth();\n const res = useResource<Subscription[]>([], () => account.listSubscriptions(page),\n [account, page?.cursor, page?.limit]);\n const after = async (p: Promise<unknown>) => { await p; await res.refetch(); };\n return {\n ...res,\n cancel: (id, opts) => after(account.cancelSubscription(id, opts)),\n pause: (id) => after(account.pauseSubscription(id)),\n resume: (id) => after(account.resumeSubscription(id)),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA4F;;;ACArF,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,MAAqG;AAC/G,UAAM,KAAK,OAAO;AAClB,SAAK,OAAO;AACZ,SAAK,OAAO,KAAK;AACjB,SAAK,aAAa,KAAK;AACvB,SAAK,aAAa,KAAK;AACvB,SAAK,UAAU,KAAK;AAAA,EACtB;AACF;;;ACDA,eAAsB,QAAQ,OAAmC;AAC/D,QAAM,UAAkC,EAAE,aAAa,MAAM,eAAe;AAC5E,MAAI,MAAM,SAAS,OAAW,SAAQ,cAAc,IAAI;AACxD,MAAI,MAAM,YAAa,SAAQ,gBAAgB,UAAU,MAAM,WAAW;AAC1E,MAAI,MAAM,YAAa,SAAQ,WAAW,IAAI,MAAM;AAEpD,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,iBAAiB,MAAM,IAAI,IAAI;AAAA,IACrE,QAAQ,MAAM;AAAA,IACd;AAAA,IACA,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,KAAK,UAAU,MAAM,IAAI,EAAE,IAAI,CAAC;AAAA,EACzE,CAAC;AAED,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,SAAc,CAAC;AACnB,MAAI;AACF,aAAS,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC;AAAA,EACtC,QAAQ;AAAA,EAGR;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,QAAQ,SAAS,CAAC;AAG5B,UAAM,KAAK,OAAO,IAAI,QAAQ,IAAI,aAAa,CAAC;AAChD,UAAM,IAAI,kBAAkB;AAAA,MAC1B,MAAM,EAAE,QAAQ;AAAA,MAChB,SAAS,EAAE,WAAW,uBAAuB,IAAI,MAAM;AAAA,MACvD,YAAY,IAAI;AAAA,MAChB,YAAY,OAAO,SAAS,EAAE,KAAK,MAAM,IAAI,KAAK;AAAA,MAClD,SAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAKA,SAAO,UAAU,OAAO,WAAW,YAAY,UAAU,SAAS,OAAO,OAAQ,UAAU,CAAC;AAC9F;;;AClCO,IAAM,eAAN,MAAmB;AAAA,EACxB,YAA0B,EAAE,QAAQ,WAAW,UAAU,MAAM,eAAe,MAAM;AAAA,EACpF,eAA8B;AAAA,EAC9B,aAA4B;AAAA,EAC5B,aAAa,oBAAI,IAAc;AAAA,EAE/B,MAAoB;AAAE,WAAO,KAAK;AAAA,EAAW;AAAA,EAC7C,cAA6B;AAAE,WAAO,KAAK;AAAA,EAAc;AAAA,EACzD,YAA2B;AAAE,WAAO,KAAK;AAAA,EAAY;AAAA,EAErD,WAAW,SAAwB;AACjC,SAAK,eAAe,QAAQ;AAC5B,SAAK,aAAa,KAAK,MAAM,QAAQ,SAAS;AAC9C,SAAK,SAAS;AAAA,MACZ,QAAQ;AAAA,MACR,UAAU,QAAQ;AAAA,MAClB,eAAe,QAAQ,QAAQ,UAAU,aAAa;AAAA,IACxD,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,QAA0B;AAClC,SAAK,SAAS,EAAE,GAAG,KAAK,WAAW,OAAO,CAAC;AAAA,EAC7C;AAAA,EAEA,QAAc;AACZ,SAAK,eAAe;AACpB,SAAK,aAAa;AAClB,SAAK,SAAS,EAAE,QAAQ,mBAAmB,UAAU,MAAM,eAAe,MAAM,CAAC;AAAA,EACnF;AAAA,EAEA,UAAU,IAA0B;AAClC,SAAK,WAAW,IAAI,EAAE;AACtB,WAAO,MAAM;AAAE,WAAK,WAAW,OAAO,EAAE;AAAA,IAAG;AAAA,EAC7C;AAAA,EAEA,SAAS,MAA0B;AAIjC,UAAM,OACJ,KAAK,WAAW,KAAK,UAAU,UAC/B,KAAK,kBAAkB,KAAK,UAAU,iBACtC,eAAe,KAAK,UAAU,KAAK,UAAU,QAAQ;AACvD,QAAI,KAAM;AACV,SAAK,YAAY;AACjB,eAAW,MAAM,CAAC,GAAG,KAAK,UAAU,GAAG;AAErC,UAAI;AAAE,WAAG,IAAI;AAAA,MAAG,QAAQ;AAAA,MAA0B;AAAA,IACpD;AAAA,EACF;AACF;AAEA,SAAS,eAAe,GAAoB,GAA6B;AACvE,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,QAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC;AAC3D,aAAW,KAAK,KAAM,KAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO;AAChD,SAAO;AACT;;;ACxEO,SAAS,gBAA6B;AAC3C,QAAM,MAAM,oBAAI,IAAoB;AACpC,SAAO;AAAA,IACL,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK;AAAA,IAC1B,KAAK,CAAC,GAAG,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC;AAAA,IAChC,QAAQ,CAAC,MAAM,KAAK,IAAI,OAAO,CAAC;AAAA,EAClC;AACF;AAOO,SAAS,sBAAmC;AACjD,QAAM,WAAW,cAAc;AAC/B,QAAM,KAAK,MAAsB;AAC/B,QAAI;AACF,aAAO,OAAO,iBAAiB,cAAc,eAAe;AAAA,IAC9D,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AAAA,IACL,IAAI,GAAG;AACL,UAAI;AACF,cAAM,IAAI,GAAG,GAAG,QAAQ,CAAC;AACzB,YAAI,KAAK,KAAM,QAAO;AAAA,MACxB,QAAQ;AAAA,MAAqB;AAC7B,aAAO,SAAS,IAAI,CAAC;AAAA,IACvB;AAAA,IACA,IAAI,GAAG,GAAG;AACR,eAAS,IAAI,GAAG,CAAC;AACjB,UAAI;AAAE,WAAG,GAAG,QAAQ,GAAG,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAiC;AAAA,IACtE;AAAA,IACA,OAAO,GAAG;AACR,eAAS,OAAO,CAAC;AACjB,UAAI;AAAE,WAAG,GAAG,WAAW,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAqB;AAAA,IAC1D;AAAA,EACF;AACF;;;ACtCA,IAAM,mBAAmB;AAEzB,IAAM,kBAAkB;AA8BjB,SAAS,mBAAmB,QAA0C;AAC3E,QAAM,WAAW,OAAO,WAAW,kBAAkB,QAAQ,OAAO,EAAE;AACtE,QAAM,UAAuB,OAAO,WAAW,oBAAoB;AAGnE,QAAM,aAAa,oBAAoB,OAAO,cAAc;AAC5D,QAAM,WAAW,yBAAyB,OAAO,cAAc;AAE/D,QAAM,QAAQ,IAAI,aAAa;AAC/B,MAAI,kBAAwC;AAC5C,MAAI,cAA6B;AACjC,MAAI,kBAAkB;AAEtB,QAAM,OAAO,EAAE,SAAS,gBAAgB,OAAO,eAAe;AAE9D,iBAAe,QAAQ,SAAiC;AACtD,UAAM,WAAW,OAAO;AACxB,UAAM,QAAQ,IAAI,YAAY,QAAQ,YAAY;AAAA,EACpD;AAEA,iBAAe,SAAwB;AACrC,kBAAc;AACd,UAAM,MAAM;AACZ,UAAM,QAAQ,OAAO,UAAU;AAAA,EACjC;AAQA,WAAS,SAAY,IAAkC;AACrD,UAAM,QAAS,WAAmB,WAAW;AAC7C,WAAO,OAAO,UAAU,MAAM,QAAQ,UAAU,EAAE,IAAI,GAAG;AAAA,EAC3D;AAEA,iBAAe,YAA2B;AACxC,UAAM,SAAS,YAAY;AAGzB,YAAM,eAAe,MAAM,QAAQ,IAAI,UAAU;AACjD,UAAI,CAAC,cAAc;AACjB,cAAM,OAAO;AACb,cAAM,IAAI,kBAAkB,EAAE,MAAM,mBAAmB,SAAS,cAAc,YAAY,IAAI,CAAC;AAAA,MACjG;AACA,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,EAAE,GAAG,MAAM,QAAQ,QAAQ,MAAM,iBAAiB,MAAM,EAAE,aAAa,EAAE,CAAC;AACrG,cAAM,QAAQ,IAAe;AAAA,MAC/B,SAAS,KAAK;AAGZ,cAAM,OAAO;AACb,cAAM;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAEA,WAAS,UAAyB;AAChC,QAAI,CAAC,iBAAiB;AACpB,wBAAkB,UAAU,EAAE,QAAQ,MAAM;AAAE,0BAAkB;AAAA,MAAM,CAAC;AAAA,IACzE;AACA,WAAO;AAAA,EACT;AAEA,iBAAe,cAAsC;AACnD,UAAM,UAAU,MAAM,UAAU;AAChC,QAAI,MAAM,YAAY,KAAK,WAAW,UAAU,KAAK,IAAI,IAAI,iBAAiB;AAC5E,aAAO,MAAM,YAAY;AAAA,IAC3B;AACA,QAAI,MAAM,QAAQ,IAAI,UAAU,GAAG;AACjC,YAAM,QAAQ;AACd,aAAO,MAAM,YAAY;AAAA,IAC3B;AACA,WAAO,MAAM,YAAY;AAAA,EAC3B;AAEA,iBAAe,eAAiC;AAC9C,QAAI,eAAe,kBAAkB,KAAK,IAAI,EAAG,QAAO;AACxD,QAAI,CAAC,OAAO,iBAAkB,QAAO;AACrC,UAAM,WAAW,MAAM,OAAO,iBAAiB;AAC/C,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,OAAO,QAAQ;AACrB,WAAO;AAAA,EACT;AAEA,iBAAe,OAAO,UAAiC;AACrD,UAAM,OAAO,MAAM,QAAQ;AAAA,MACzB,GAAG;AAAA,MAAM,QAAQ;AAAA,MAAQ,MAAM;AAAA,MAC/B,aAAa,MAAM,YAAY,KAAK;AAAA,MAAW,MAAM,EAAE,SAAS;AAAA,IAClE,CAAC;AACD,kBAAc,KAAK;AACnB,sBAAkB,KAAK,IAAI,KAAK,OAAO,KAAK,SAAS,KAAK,OAAO,MAAO;AAAA,EAC1E;AAEA,iBAAe,KAAc,QAAgB,MAAc,MAA4B;AACrF,QAAI,cAAc;AAClB,QAAI,gBAAgB;AAOpB,QAAI,aAAa;AAEjB,eAAS;AACP,YAAM,QAAQ,MAAM,YAAY;AAChC,UAAI;AACF,eAAO,MAAM,QAAQ;AAAA,UACnB,GAAG;AAAA,UAAM;AAAA,UAAQ;AAAA,UAAM;AAAA,UACvB,aAAa,SAAS;AAAA,UACtB,aAAa,aAAc,eAAe,SAAa;AAAA,QACzD,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,cAAM,IAAI;AAOV,cAAM,UAAU,EAAE,eAAe,QAC9B,EAAE,SAAS,kBAAkB,EAAE,SAAS,mBAAmB,EAAE,SAAS;AACzE,YAAI,WAAW,CAAC,aAAa;AAC3B,wBAAc;AACd,gBAAM,QAAQ;AACd;AAAA,QACF;AACA,YAAI,EAAE,SAAS,sBAAsB,CAAC,eAAe;AACnD,0BAAgB;AAIhB,cAAI,MAAM,aAAa,GAAG;AAAE,yBAAa;AAAM;AAAA,UAAU;AAAA,QAC3D;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,SAAS,OAAO,UAAU;AAC9B,YAAM,OAAO,MAAM,QAAQ,EAAE,GAAG,MAAM,QAAQ,QAAQ,MAAM,kBAAkB,MAAM,EAAE,OAAO,SAAS,EAAE,CAAC;AACzG,UAAI,MAAM,WAAW,UAAW,QAAO,EAAE,QAAQ,UAAU;AAC3D,YAAM,QAAQ,IAAe;AAC7B,aAAO,EAAE,QAAQ,SAAS;AAAA,IAC5B;AAAA,IAEA,MAAM,OAAO,OAAO,UAAU;AAC5B,YAAM,OAAO,MAAM,QAAQ,EAAE,GAAG,MAAM,QAAQ,QAAQ,MAAM,eAAe,MAAM,EAAE,OAAO,SAAS,EAAE,CAAC;AACtG,YAAM,QAAQ,IAAe;AAAA,IAC/B;AAAA,IAEA,MAAM,UAAU;AACd,UAAI;AAAE,cAAM,KAAK,QAAQ,cAAc;AAAA,MAAG,QAAQ;AAAA,MAA+B;AACjF,YAAM,OAAO;AAAA,IACf;AAAA,IAEA,MAAM,oBAAoB;AACxB,UAAI;AAAE,cAAM,KAAK,QAAQ,kBAAkB;AAAA,MAAG,QAAQ;AAAA,MAA+B;AACrF,YAAM,OAAO;AAAA,IACf;AAAA,IAEA,MAAM,UAAU;AACd,UAAI,CAAE,MAAM,QAAQ,IAAI,UAAU,GAAI;AAAE,cAAM,UAAU,iBAAiB;AAAG;AAAA,MAAQ;AACpF,UAAI;AAAE,cAAM,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAA6B;AAAA,IAC9D;AAAA,IAEA,aAAa,MAAM,MAAM,IAAI;AAAA,IAC7B,WAAW,CAAC,OAAO,MAAM,UAAU,EAAE;AAAA,IACrC;AAAA,IACA;AAAA,IAEA,MAAM,YAAY,OAAO;AACvB,YAAM,QAAQ,EAAE,GAAG,MAAM,QAAQ,QAAQ,MAAM,sBAAsB,MAAM,EAAE,MAAM,EAAE,CAAC;AAKtF,UAAI,MAAM,QAAQ,IAAI,UAAU,GAAG;AACjC,YAAI;AAAE,gBAAM,QAAQ;AAAA,QAAG,QAAQ;AAAA,QAAqC;AAAA,MACtE;AAAA,IACF;AAAA,IAEA,MAAM,qBAAqB;AACzB,YAAM,KAAK,QAAQ,2BAA2B;AAAA,IAChD;AAAA,IAEA,MAAM,eAAe,OAAO;AAG1B,YAAM,QAAQ,EAAE,GAAG,MAAM,QAAQ,QAAQ,MAAM,yBAAyB,MAAM,EAAE,MAAM,EAAE,CAAC;AAAA,IAC3F;AAAA,IAEA,MAAM,cAAc,OAAO,UAAU;AACnC,YAAM,QAAQ,EAAE,GAAG,MAAM,QAAQ,QAAQ,MAAM,wBAAwB,MAAM,EAAE,OAAO,SAAS,EAAE,CAAC;AAElG,YAAM,OAAO;AAAA,IACf;AAAA,IAEA,MAAM,eAAe,UAAU;AAC7B,YAAM,KAAK,QAAQ,uBAAuB,EAAE,SAAS,CAAC;AAMtD,UAAI;AAAE,cAAM,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAA8C;AAAA,IAC/E;AAAA,IAEA,MAAM,eAAe;AACnB,aAAO,KAAsB,OAAO,cAAc;AAAA,IACpD;AAAA,IAEA,MAAM,cAAc,IAAI;AACtB,YAAM,OAAO,MAAM,KAAsB,OAAO,cAAc;AAC9D,YAAM,UAAU,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO;AAC1C,YAAM,KAAK,UAAU,gBAAgB,mBAAmB,EAAE,CAAC,EAAE;AAC7D,UAAI,SAAS,OAAO,GAAI,OAAM,OAAO;AAAA,IACvC;AAAA,EACF;AACF;;;ACtOA,IAAM,MAAM,CAAC,MAAc,mBAAmB,CAAC;AAE/C,SAAS,MAAM,MAAqB;AAClC,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,MAAM,OAAQ,QAAO,IAAI,UAAU,KAAK,MAAM;AAClD,MAAI,MAAM,SAAS,KAAM,QAAO,IAAI,SAAS,OAAO,KAAK,KAAK,CAAC;AAC/D,QAAM,IAAI,OAAO,SAAS;AAC1B,SAAO,IAAI,IAAI,CAAC,KAAK;AACvB;AAEO,SAAS,cAAc,MAAqC;AACjE,QAAM,EAAE,KAAK,IAAI;AACjB,SAAO;AAAA,IACL,YAAY,MAAM,KAAK,OAAO,KAAK;AAAA,IACnC,eAAe,CAAC,UAAU,KAAK,SAAS,OAAO,KAAK;AAAA,IAEpD,eAAe,MAAM,KAAK,OAAO,eAAe;AAAA,IAChD,eAAe,CAAC,UAAU,KAAK,QAAQ,iBAAiB,KAAK;AAAA,IAC7D,eAAe,CAAC,IAAI,UAAU,KAAK,SAAS,iBAAiB,IAAI,EAAE,CAAC,IAAI,KAAK;AAAA,IAC7E,eAAe,CAAC,OAAO,KAAK,UAAU,iBAAiB,IAAI,EAAE,CAAC,EAAE;AAAA,IAEhE,oBAAoB,MAAM,KAAK,OAAO,qBAAqB;AAAA;AAAA;AAAA,IAG3D,yBAAyB,CAAC,OAAO,KAAK,SAAS,uBAAuB,IAAI,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,CAAC;AAAA,IACpG,qBAAqB,CAAC,OAAO,KAAK,UAAU,uBAAuB,IAAI,EAAE,CAAC,EAAE;AAAA,IAE5E,YAAY,CAAC,SAAS,KAAK,OAAO,aAAa,MAAM,IAAI,CAAC,EAAE;AAAA,IAC5D,UAAU,CAAC,OAAO,KAAK,OAAO,cAAc,IAAI,EAAE,CAAC,EAAE;AAAA,IACrD,cAAc,CAAC,SAAS,KAAK,OAAO,eAAe,MAAM,IAAI,CAAC,EAAE;AAAA,IAChE,YAAY,CAAC,OAAO,KAAK,OAAO,gBAAgB,IAAI,EAAE,CAAC,EAAE;AAAA,IACzD,mBAAmB,CAAC,SAAS,KAAK,OAAO,oBAAoB,MAAM,IAAI,CAAC,EAAE;AAAA,IAC1E,iBAAiB,CAAC,OAAO,KAAK,OAAO,qBAAqB,IAAI,EAAE,CAAC,EAAE;AAAA,IAEnE,oBAAoB,CAAC,IAAI,SAAS,KAAK,QAAQ,qBAAqB,IAAI,EAAE,CAAC,WAAW,IAAI;AAAA,IAC1F,mBAAmB,CAAC,OAAO,KAAK,QAAQ,qBAAqB,IAAI,EAAE,CAAC,QAAQ;AAAA,IAC5E,oBAAoB,CAAC,OAAO,KAAK,QAAQ,qBAAqB,IAAI,EAAE,CAAC,SAAS;AAAA,IAE9E,WAAW,CAAC,WAAW,KAAK,QAAQ,aAAa,IAAI,MAAM,CAAC,QAAQ;AAAA,EACtE;AACF;;;AN7BS;AAzBT,IAAM,UAAM,4BAAmC,IAAI;AAE5C,SAAS,qBAAqB,OAAkC;AACrE,QAAM,EAAE,MAAM,UAAU,gBAAgB,SAAS,SAAS,kBAAkB,SAAS,IAAI;AAMzF,QAAM,YAAQ,sBAAsB,MAAM;AACxC,UAAM,OAAO,YAAY,mBAAmB,EAAE,gBAAgB,SAAS,SAAS,iBAAiB,CAAC;AAClG,WAAO,EAAE,MAAM,SAAS,cAAc,IAAI,EAAE;AAAA,EAE9C,GAAG,CAAC,UAAU,gBAAgB,OAAO,CAAC;AAEtC,QAAM,eAAW,qBAA4B,IAAI;AACjD,8BAAU,MAAM;AAId,QAAI,SAAS,YAAY,MAAM,KAAM;AACrC,aAAS,UAAU,MAAM;AACzB,SAAK,MAAM,KAAK,QAAQ;AAAA,EAC1B,GAAG,CAAC,KAAK,CAAC;AAEV,SAAO,4CAAC,IAAI,UAAJ,EAAa,OAAe,UAAS;AAC/C;AAEA,SAAS,SAAuB;AAC9B,QAAM,UAAM,yBAAW,GAAG;AAC1B,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sDAAsD;AAChF,SAAO;AACT;AAEO,SAAS,iBAA+B;AAAE,SAAO,OAAO;AAAG;AAS3D,SAAS,UAAyB;AACvC,QAAM,EAAE,MAAM,QAAQ,IAAI,OAAO;AACjC,QAAM,eAAW,mCAAqB,KAAK,WAAW,KAAK,aAAa,KAAK,WAAW;AACxF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW,SAAS,WAAW;AAAA,IAC/B,iBAAiB,SAAS,WAAW;AAAA,IACrC;AAAA,IACA;AAAA,EACF;AACF;;;AOrEA,IAAAA,gBAAyD;AAazD,SAAS,QAAQ,KAAiC;AAChD,MAAI,eAAe,kBAAmB,QAAO;AAC7C,QAAM,SAAS;AACf,SAAO,IAAI,kBAAkB;AAAA,IAC3B,MAAM,QAAQ,QAAQ;AAAA,IACtB,SAAS,QAAQ,WAAW,OAAO,GAAG;AAAA,IACtC,YAAY,QAAQ,cAAc;AAAA,EACpC,CAAC;AACH;AAOA,SAAS,YAAe,OAAU,MAAwB,MAA8B;AACtF,QAAM,EAAE,QAAQ,cAAc,IAAI,QAAQ;AAC1C,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAY,KAAK;AACzC,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,WAAW,SAAS;AAC/D,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAmC,IAAI;AACjE,QAAM,UAAM,sBAAO,CAAC;AACpB,QAAM,YAAQ,sBAAO,IAAI;AAEzB,+BAAU,MAAM;AAKd,UAAM,UAAU;AAChB,WAAO,MAAM;AAAE,YAAM,UAAU;AAAO,UAAI;AAAA,IAAW;AAAA,EACvD,GAAG,CAAC,CAAC;AAEL,QAAM,cAAU,2BAAY,YAAY;AACtC,UAAM,OAAO,EAAE,IAAI;AACnB,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,OAAO,MAAM,KAAK;AACxB,UAAI,CAAC,MAAM,WAAW,IAAI,YAAY,KAAM;AAC5C,cAAQ,IAAI;AACZ,eAAS,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,CAAC,MAAM,WAAW,IAAI,YAAY,KAAM;AAC5C,eAAS,QAAQ,GAAG,CAAC;AAAA,IACvB,UAAE;AACA,UAAI,MAAM,WAAW,IAAI,YAAY,KAAM,cAAa,KAAK;AAAA,IAC/D;AAAA,EAEF,GAAG,IAAI;AAEP,+BAAU,MAAM;AACd,QAAI,WAAW,iBAAiB;AAC9B,UAAI;AACJ,cAAQ,KAAK;AACb,eAAS,IAAI;AACb,mBAAa,WAAW,SAAS;AACjC;AAAA,IACF;AACA,SAAK,QAAQ;AAAA,EAEf,GAAG,CAAC,QAAQ,eAAe,OAAO,CAAC;AAEnC,SAAO,EAAE,MAAM,WAAW,OAAO,QAAQ;AAC3C;AAEO,SAAS,cAA0F;AACxG,QAAM,EAAE,QAAQ,IAAI,QAAQ;AAC5B,QAAM,MAAM,YAA6B,MAAM,MAAM,QAAQ,WAAW,GAAG,CAAC,OAAO,CAAC;AACpF,QAAM,aAAS,2BAAY,OAAO,UAAwB;AACxD,UAAM,QAAQ,cAAc,KAAK;AACjC,UAAM,IAAI,QAAQ;AAAA,EACpB,GAAG,CAAC,SAAS,GAAG,CAAC;AACjB,SAAO,EAAE,GAAG,KAAK,OAAO;AAC1B;AAEO,SAAS,eAId;AACA,QAAM,EAAE,QAAQ,IAAI,QAAQ;AAC5B,QAAM,MAAM,YAAuB,CAAC,GAAG,MAAM,QAAQ,cAAc,GAAG,CAAC,OAAO,CAAC;AAC/E,QAAM,QAAQ,OAAO,MAAwB;AAAE,UAAM;AAAG,UAAM,IAAI,QAAQ;AAAA,EAAG;AAC7E,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,CAAC,UAAU,MAAM,QAAQ,cAAc,KAAK,CAAC;AAAA,IACrD,QAAQ,CAAC,IAAI,UAAU,MAAM,QAAQ,cAAc,IAAI,KAAK,CAAC;AAAA,IAC7D,QAAQ,CAAC,OAAO,MAAM,QAAQ,cAAc,EAAE,CAAC;AAAA,EACjD;AACF;AAEO,SAAS,gBAGd;AACA,QAAM,EAAE,QAAQ,IAAI,QAAQ;AAC5B,QAAM,MAAM,YAA6B,CAAC,GAAG,MAAM,QAAQ,mBAAmB,GAAG,CAAC,OAAO,CAAC;AAC1F,QAAM,QAAQ,OAAO,MAAwB;AAAE,UAAM;AAAG,UAAM,IAAI,QAAQ;AAAA,EAAG;AAC7E,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY,CAAC,OAAO,MAAM,QAAQ,wBAAwB,EAAE,CAAC;AAAA,IAC7D,QAAQ,CAAC,OAAO,MAAM,QAAQ,oBAAoB,EAAE,CAAC;AAAA,EACvD;AACF;AAEO,SAAS,UAAU,MAAgC;AACxD,QAAM,EAAE,QAAQ,IAAI,QAAQ;AAC5B,SAAO,YAAqB,CAAC,GAAG,MAAM,QAAQ,WAAW,IAAI,GAAG,CAAC,SAAS,MAAM,QAAQ,MAAM,KAAK,CAAC;AACtG;AAEO,SAAS,YAAY,MAAkC;AAC5D,QAAM,EAAE,QAAQ,IAAI,QAAQ;AAC5B,SAAO,YAAuB,CAAC,GAAG,MAAM,QAAQ,aAAa,IAAI,GAAG,CAAC,SAAS,MAAM,QAAQ,MAAM,KAAK,CAAC;AAC1G;AAEO,SAAS,iBAAiB,MAI/B;AACA,QAAM,EAAE,QAAQ,IAAI,QAAQ;AAC5B,QAAM,MAAM;AAAA,IAA4B,CAAC;AAAA,IAAG,MAAM,QAAQ,kBAAkB,IAAI;AAAA,IAC9E,CAAC,SAAS,MAAM,QAAQ,MAAM,KAAK;AAAA,EAAC;AACtC,QAAM,QAAQ,OAAO,MAAwB;AAAE,UAAM;AAAG,UAAM,IAAI,QAAQ;AAAA,EAAG;AAC7E,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,CAAC,IAAI,SAAS,MAAM,QAAQ,mBAAmB,IAAI,IAAI,CAAC;AAAA,IAChE,OAAO,CAAC,OAAO,MAAM,QAAQ,kBAAkB,EAAE,CAAC;AAAA,IAClD,QAAQ,CAAC,OAAO,MAAM,QAAQ,mBAAmB,EAAE,CAAC;AAAA,EACtD;AACF;","names":["import_react"]}
@@ -3,14 +3,14 @@ import {
3
3
  ThrottleAuthProvider,
4
4
  useAuth,
5
5
  useAuthContext
6
- } from "../chunk-IJ3DNMNG.js";
6
+ } from "../chunk-5GLLPF6L.js";
7
7
  import {
8
8
  ThrottleAuthError,
9
9
  createAccount,
10
10
  createThrottleAuth,
11
11
  localStorageAdapter,
12
12
  memoryStorage
13
- } from "../chunk-OYNGTNOS.js";
13
+ } from "../chunk-SOPTBXF3.js";
14
14
 
15
15
  // src/react/hooks.ts
16
16
  import { useCallback, useEffect, useRef, useState } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usethrottle/auth",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Buyer authentication for Throttle storefronts — headless core, React hooks, and drop-in forms.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/request.ts","../src/session.ts","../src/client.ts","../src/account.ts"],"sourcesContent":["export class ThrottleAuthError extends Error {\n readonly code: string;\n readonly statusCode: number;\n readonly retryAfter?: number;\n readonly details?: unknown;\n\n constructor(args: { code: string; message: string; statusCode: number; retryAfter?: number; details?: unknown }) {\n super(args.message);\n this.name = 'ThrottleAuthError';\n this.code = args.code;\n this.statusCode = args.statusCode;\n this.retryAfter = args.retryAfter;\n this.details = args.details;\n }\n}\n","import type { AuthStorage } from './types';\n\nexport type { AuthStorage };\n\nexport function memoryStorage(): AuthStorage {\n const map = new Map<string, string>();\n return {\n get: (k) => map.get(k) ?? null,\n set: (k, v) => void map.set(k, v),\n remove: (k) => void map.delete(k),\n };\n}\n\n/**\n * localStorage when it works, memory when it does not. Safari private mode,\n * hardened browser settings and server-side rendering all make localStorage\n * either absent or throwing; none of them may take the storefront down.\n */\nexport function localStorageAdapter(): AuthStorage {\n const fallback = memoryStorage();\n const ls = (): Storage | null => {\n try {\n return typeof localStorage !== 'undefined' ? localStorage : null;\n } catch {\n return null;\n }\n };\n return {\n get(k) {\n try {\n const v = ls()?.getItem(k);\n if (v != null) return v;\n } catch { /* fall through */ }\n return fallback.get(k);\n },\n set(k, v) {\n fallback.set(k, v);\n try { ls()?.setItem(k, v); } catch { /* memory-only this session */ }\n },\n remove(k) {\n fallback.remove(k);\n try { ls()?.removeItem(k); } catch { /* already gone */ }\n },\n };\n}\n","import { ThrottleAuthError } from './errors';\n\nexport interface RequestInput {\n method: string;\n path: string;\n baseUrl: string;\n publishableKey: string;\n accessToken?: string;\n stepUpToken?: string;\n body?: unknown;\n}\n\n/** Returns the UNWRAPPED `data` from the `{ data: … }` envelope. Throws ThrottleAuthError otherwise. */\nexport async function request(input: RequestInput): Promise<any> {\n const headers: Record<string, string> = { 'X-API-Key': input.publishableKey };\n if (input.body !== undefined) headers['Content-Type'] = 'application/json';\n if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;\n if (input.stepUpToken) headers['X-Step-Up'] = input.stepUpToken;\n\n const res = await fetch(`${input.baseUrl}/v1/storefront${input.path}`, {\n method: input.method,\n headers,\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — an HTML gateway page, say. Leave parsed empty so the\n // branch below still throws a typed error rather than a raw SyntaxError.\n }\n\n if (!res.ok) {\n const e = parsed?.error ?? {};\n // `Retry-After` is server-controlled but not trusted: Number('soon') is\n // NaN, and a NaN reaches the UI as \"wait NaN seconds\".\n const ra = Number(res.headers.get('retry-after'));\n throw new ThrottleAuthError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n retryAfter: Number.isFinite(ra) && ra >= 0 ? ra : undefined,\n details: e.details,\n });\n }\n\n // `'data' in parsed`, not `parsed?.data ?? parsed`: a legitimate\n // `{ \"data\": null }` is nullish, and `??` would fall through and hand the\n // caller the whole envelope instead of the null the API actually sent.\n return parsed && typeof parsed === 'object' && 'data' in parsed ? parsed.data : (parsed ?? {});\n}\n","import type { Customer, Session } from './types';\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\nexport interface AuthSnapshot {\n status: AuthStatus;\n customer: Customer | null;\n /**\n * False here does NOT only mean \"we emailed them and they have not clicked\".\n * While it is false the API may also be withholding pre-existing account\n * data behind the claim cutoff — empty order lists and a 403 on `PATCH /me`\n * are expected, not broken. See the plan header.\n */\n emailVerified: boolean;\n}\n\ntype Listener = (snapshot: AuthSnapshot) => void;\n\nexport class SessionState {\n #snapshot: AuthSnapshot = { status: 'loading', customer: null, emailVerified: false };\n #accessToken: string | null = null;\n #expiresAt: number | null = null;\n #listeners = new Set<Listener>();\n\n get(): AuthSnapshot { return this.#snapshot; }\n accessToken(): string | null { return this.#accessToken; }\n expiresAt(): number | null { return this.#expiresAt; }\n\n setSession(session: Session): void {\n this.#accessToken = session.accessToken;\n this.#expiresAt = Date.parse(session.expiresAt);\n this.#publish({\n status: 'authenticated',\n customer: session.customer,\n emailVerified: Boolean(session.customer?.emailVerified),\n });\n }\n\n setStatus(status: AuthStatus): void {\n this.#publish({ ...this.#snapshot, status });\n }\n\n clear(): void {\n this.#accessToken = null;\n this.#expiresAt = null;\n this.#publish({ status: 'unauthenticated', customer: null, emailVerified: false });\n }\n\n subscribe(fn: Listener): () => void {\n this.#listeners.add(fn);\n return () => { this.#listeners.delete(fn); };\n }\n\n #publish(next: AuthSnapshot): void {\n // Identity matters: React's useSyncExternalStore re-renders on reference\n // change, and a token refresh every ten minutes must not re-render the\n // whole account area when nothing visible changed.\n const same =\n next.status === this.#snapshot.status &&\n next.emailVerified === this.#snapshot.emailVerified &&\n customerEquals(next.customer, this.#snapshot.customer);\n if (same) return;\n this.#snapshot = next;\n for (const fn of [...this.#listeners]) {\n // A merchant's subscriber throwing must not stop the rest from hearing.\n try { fn(next); } catch { /* merchant callback */ }\n }\n }\n}\n\nfunction customerEquals(a: Customer | null, b: Customer | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<keyof Customer>;\n for (const k of keys) if (a[k] !== b[k]) return false;\n return true;\n}\n","import { request } from './request';\nimport { ThrottleAuthError } from './errors';\nimport { SessionState, type AuthSnapshot } from './session';\nimport { localStorageAdapter, type AuthStorage } from './storage';\nimport type { Customer, Session, ThrottleAuthConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n/** Refresh this far before expiry so ordinary calls rarely see a 401 at all. */\nconst REFRESH_SKEW_MS = 30_000;\n\nexport interface DeviceSession {\n id: string;\n userAgent: string | null;\n ipPrefix: string | null;\n createdAt: string;\n lastUsedAt: string;\n current: boolean;\n}\n\nexport interface ThrottleAuth {\n register(email: string, password: string): Promise<{ status: 'active' | 'pending' }>;\n signIn(email: string, password: string): Promise<void>;\n signOut(): Promise<void>;\n signOutEverywhere(): Promise<void>;\n restore(): Promise<void>;\n getSnapshot(): AuthSnapshot;\n subscribe(fn: (s: AuthSnapshot) => void): () => void;\n stepUp(password: string): Promise<void>;\n call<T = any>(method: string, path: string, body?: unknown): Promise<T>;\n verifyEmail(token: string): Promise<void>;\n resendVerification(): Promise<void>;\n forgotPassword(email: string): Promise<void>;\n resetPassword(token: string, password: string): Promise<void>;\n changePassword(password: string): Promise<void>;\n listSessions(): Promise<DeviceSession[]>;\n revokeSession(id: string): Promise<void>;\n}\n\nexport function createThrottleAuth(config: ThrottleAuthConfig): ThrottleAuth {\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const storage: AuthStorage = config.storage ?? localStorageAdapter();\n // Namespaced by key so two Throttle applications on one origin cannot read\n // each other's refresh token out of shared localStorage.\n const storageKey = `throttle_auth_rt:${config.publishableKey}`;\n const lockName = `throttle_auth_refresh:${config.publishableKey}`;\n\n const state = new SessionState();\n let refreshInFlight: Promise<void> | null = null;\n let stepUpToken: string | null = null;\n let stepUpExpiresAt = 0;\n\n const base = { baseUrl, publishableKey: config.publishableKey };\n\n async function persist(session: Session): Promise<void> {\n state.setSession(session);\n await storage.set(storageKey, session.refreshToken);\n }\n\n async function forget(): Promise<void> {\n stepUpToken = null;\n state.clear();\n await storage.remove(storageKey);\n }\n\n /**\n * Cross-tab single flight. Two tabs refreshing at once would each present the\n * same stored token; the second is a replay, and the server kills the family.\n * Web Locks serialises them; where it is unavailable the in-process guard\n * below still covers the common single-tab case.\n */\n function withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as any).navigator?.locks;\n return locks?.request ? locks.request(lockName, fn) : fn();\n }\n\n async function doRefresh(): Promise<void> {\n await withLock(async () => {\n // Re-read INSIDE the lock: another tab may have rotated while we waited,\n // and presenting the token we read before the lock would be a replay.\n const refreshToken = await storage.get(storageKey);\n if (!refreshToken) {\n await forget();\n throw new ThrottleAuthError({ code: 'session_revoked', message: 'No session', statusCode: 401 });\n }\n try {\n const data = await request({ ...base, method: 'POST', path: '/auth/refresh', body: { refreshToken } });\n await persist(data as Session);\n } catch (err) {\n // Any refusal of a refresh token is terminal — there is nothing left to\n // retry with. Drop it rather than letting a dead token sit in storage.\n await forget();\n throw err;\n }\n });\n }\n\n function refresh(): Promise<void> {\n if (!refreshInFlight) {\n refreshInFlight = doRefresh().finally(() => { refreshInFlight = null; });\n }\n return refreshInFlight;\n }\n\n async function accessToken(): Promise<string | null> {\n const expires = state.expiresAt();\n if (state.accessToken() && expires && expires - Date.now() > REFRESH_SKEW_MS) {\n return state.accessToken();\n }\n if (await storage.get(storageKey)) {\n await refresh();\n return state.accessToken();\n }\n return state.accessToken();\n }\n\n async function ensureStepUp(): Promise<boolean> {\n if (stepUpToken && stepUpExpiresAt > Date.now()) return true;\n if (!config.onStepUpRequired) return false;\n const password = await config.onStepUpRequired();\n if (!password) return false;\n await stepUp(password);\n return true;\n }\n\n async function stepUp(password: string): Promise<void> {\n const data = await request({\n ...base, method: 'POST', path: '/auth/step-up',\n accessToken: await accessToken() ?? undefined, body: { password },\n });\n stepUpToken = data.stepUpToken;\n stepUpExpiresAt = Date.now() + (Number(data.expiresIn) || 300) * 1000 - 5_000;\n }\n\n async function call<T = any>(method: string, path: string, body?: unknown): Promise<T> {\n let retriedAuth = false;\n let retriedStepUp = false;\n\n for (;;) {\n const token = await accessToken();\n try {\n return await request({\n ...base, method, path, body,\n accessToken: token ?? undefined,\n stepUpToken: stepUpToken ?? undefined,\n });\n } catch (err) {\n const e = err as ThrottleAuthError;\n // `session_revoked` belongs here too. The server returns it for a\n // stale session EPOCH as well as for a genuinely dead session, and a\n // changed password bumps the epoch while deliberately sparing the\n // current session — so one refresh recovers it. When the session\n // really is dead the refresh fails and forget() runs, which is the\n // same outcome, one request later.\n const expired = e.statusCode === 401 &&\n (e.code === 'unauthorized' || e.code === 'token_expired' || e.code === 'session_revoked');\n if (expired && !retriedAuth) {\n retriedAuth = true;\n await refresh(); // shared — ten callers here produce ONE request\n continue;\n }\n if (e.code === 'step_up_required' && !retriedStepUp) {\n retriedStepUp = true;\n stepUpToken = null;\n if (await ensureStepUp()) continue;\n }\n throw err;\n }\n }\n }\n\n return {\n async register(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/register', body: { email, password } });\n if (data?.status === 'pending') return { status: 'pending' };\n await persist(data as Session);\n return { status: 'active' };\n },\n\n async signIn(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/login', body: { email, password } });\n await persist(data as Session);\n },\n\n async signOut() {\n try { await call('POST', '/auth/logout'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async signOutEverywhere() {\n try { await call('POST', '/auth/logout-all'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async restore() {\n if (!(await storage.get(storageKey))) { state.setStatus('unauthenticated'); return; }\n try { await refresh(); } catch { /* forget() already ran */ }\n },\n\n getSnapshot: () => state.get(),\n subscribe: (fn) => state.subscribe(fn),\n stepUp,\n call,\n\n async verifyEmail(token) {\n await request({ ...base, method: 'POST', path: '/auth/verify-email', body: { token } });\n // `ver` is a claim baked into the access token, and the claim cutoff is\n // read from the session row. Without a new token the buyer stays gated —\n // and their own order history stays hidden — for the rest of this\n // token's ten minutes.\n if (await storage.get(storageKey)) {\n try { await refresh(); } catch { /* verification still succeeded */ }\n }\n },\n\n async resendVerification() {\n await call('POST', '/auth/verify-email/resend');\n },\n\n async forgotPassword(email) {\n // Always 202, whether or not the address has an account. Do not infer\n // anything from it and do not tell the buyer whether it existed.\n await request({ ...base, method: 'POST', path: '/auth/forgot-password', body: { email } });\n },\n\n async resetPassword(token, password) {\n await request({ ...base, method: 'POST', path: '/auth/reset-password', body: { token, password } });\n // The server revokes every session on reset, this one included.\n await forget();\n },\n\n async changePassword(password) {\n await call('POST', '/me/change-password', { password });\n // The change bumps the session epoch. This session was deliberately\n // spared, but the access token in hand now carries a stale epoch and the\n // next write would 401. Rotate now rather than on the buyer's next click\n // — and do not let a failed rotation report a change that already\n // succeeded server-side as a failure.\n try { await refresh(); } catch { /* the password change already succeeded */ }\n },\n\n async listSessions() {\n return call<DeviceSession[]>('GET', '/me/sessions');\n },\n\n async revokeSession(id) {\n const mine = await call<DeviceSession[]>('GET', '/me/sessions');\n const current = mine.find((s) => s.current);\n await call('DELETE', `/me/sessions/${encodeURIComponent(id)}`);\n if (current?.id === id) await forget();\n },\n };\n}\n\nexport type { AuthSnapshot, Customer, Session, ThrottleAuthConfig };\n","import type { ThrottleAuth } from './client';\nimport type {\n Address, AddressInput, Customer, Invoice, Order,\n PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n\nexport interface Page { cursor?: string; limit?: number }\n\nexport interface ThrottleAccount {\n getProfile(): Promise<Customer>;\n updateProfile(patch: ProfilePatch): Promise<Customer>;\n listAddresses(): Promise<Address[]>;\n createAddress(input: AddressInput): Promise<Address>;\n updateAddress(id: string, patch: Partial<AddressInput>): Promise<Address>;\n deleteAddress(id: string): Promise<void>;\n listPaymentMethods(): Promise<PaymentMethod[]>;\n setDefaultPaymentMethod(id: string): Promise<PaymentMethod>;\n deletePaymentMethod(id: string): Promise<void>;\n listOrders(page?: Page): Promise<Order[]>;\n getOrder(id: string): Promise<Order>;\n listInvoices(page?: Page): Promise<Invoice[]>;\n getInvoice(id: string): Promise<Invoice>;\n listSubscriptions(page?: Page): Promise<Subscription[]>;\n getSubscription(id: string): Promise<Subscription>;\n cancelSubscription(id: string, opts?: { atPeriodEnd?: boolean }): Promise<Subscription>;\n pauseSubscription(id: string): Promise<Subscription>;\n resumeSubscription(id: string): Promise<Subscription>;\n claimCart(cartId: string): Promise<{ cartId: string; customerId: string }>;\n}\n\nconst seg = (v: string) => encodeURIComponent(v);\n\nfunction query(page?: Page): string {\n const params = new URLSearchParams();\n if (page?.cursor) params.set('cursor', page.cursor);\n if (page?.limit != null) params.set('limit', String(page.limit));\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\nexport function createAccount(auth: ThrottleAuth): ThrottleAccount {\n const { call } = auth;\n return {\n getProfile: () => call('GET', '/me'),\n updateProfile: (patch) => call('PATCH', '/me', patch),\n\n listAddresses: () => call('GET', '/me/addresses'),\n createAddress: (input) => call('POST', '/me/addresses', input),\n updateAddress: (id, patch) => call('PATCH', `/me/addresses/${seg(id)}`, patch),\n deleteAddress: (id) => call('DELETE', `/me/addresses/${seg(id)}`),\n\n listPaymentMethods: () => call('GET', '/me/payment-methods'),\n // The platform has no \"change this subscription's card\" capability, so\n // this is how a buyer changes what they are billed on: move the default.\n setDefaultPaymentMethod: (id) => call('PATCH', `/me/payment-methods/${seg(id)}`, { isDefault: true }),\n deletePaymentMethod: (id) => call('DELETE', `/me/payment-methods/${seg(id)}`),\n\n listOrders: (page) => call('GET', `/me/orders${query(page)}`),\n getOrder: (id) => call('GET', `/me/orders/${seg(id)}`),\n listInvoices: (page) => call('GET', `/me/invoices${query(page)}`),\n getInvoice: (id) => call('GET', `/me/invoices/${seg(id)}`),\n listSubscriptions: (page) => call('GET', `/me/subscriptions${query(page)}`),\n getSubscription: (id) => call('GET', `/me/subscriptions/${seg(id)}`),\n\n cancelSubscription: (id, opts) => call('POST', `/me/subscriptions/${seg(id)}/cancel`, opts),\n pauseSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/pause`),\n resumeSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/resume`),\n\n claimCart: (cartId) => call('POST', `/me/carts/${seg(cartId)}/claim`),\n };\n}\n"],"mappings":"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","names":[]}