@palbase/web 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../core/src/config.ts","../../core/src/errors.ts","../../core/src/http.ts","../../core/src/platform.ts","../../core/src/token.ts","../src/errors.ts","../src/request.ts","../src/state.ts","../src/namespaces.ts","../../modules/auth/src/auth-client.ts","../../modules/auth/src/device-client.ts","../src/api-key.ts","../src/analytics-facade.ts","../src/auth-wire.ts","../src/auth-facade.ts","../../modules/flags/src/admin-client.ts","../../modules/flags/src/flags-client.ts","../../modules/flags/src/pooled-flags.ts","../src/flags-facade.ts","../src/realtime/anon-token.ts","../src/realtime/frames.ts","../src/realtime/connection.ts","../src/realtime/facade.ts","../src/storage.ts","../src/version.ts","../src/runtime.ts","../src/call.ts","../src/upload.ts","../src/pb.ts","../src/internal.ts"],"sourcesContent":["import type { HttpClient } from './http.js';\nimport type { ProjectConfig } from './types.js';\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport class ConfigFetcher {\n protected readonly httpClient: HttpClient;\n private cachedConfig: ProjectConfig | null = null;\n private cacheTimestamp = 0;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n async getConfig(): Promise<ProjectConfig | null> {\n const now = Date.now();\n\n if (this.cachedConfig && now - this.cacheTimestamp < CACHE_TTL_MS) {\n return this.cachedConfig;\n }\n\n try {\n const response = await this.httpClient.request<ProjectConfig>('GET', '/v1/config');\n\n if (response.error || !response.data) {\n return null;\n }\n\n this.cachedConfig = response.data;\n this.cacheTimestamp = now;\n\n return this.cachedConfig;\n } catch {\n return null;\n }\n }\n}\n","export class PalbaseError extends Error {\n readonly code: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: string, message: string, status: number, details?: unknown) {\n super(message);\n this.name = 'PalbaseError';\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n","import { PalbaseError } from './errors.js';\nimport type { TokenManager } from './token.js';\nimport type { HttpClientOptions, PalbaseResponse, RequestOptions } from './types.js';\n\n/**\n * Default production host. Dev / staging / local callers override via\n * `options.url`. Apex-style routing (all traffic lands on one host and\n * Kong stamps `X-Project-Ref` from the key-auth consumer) is the only\n * supported production path — subdomain-per-tenant routing was\n * considered but retired in favour of a single gateway.\n */\nconst PALBASE_DEFAULT_HOST = 'api.palbase.studio';\n\n/**\n * Parse project ref from a palbase API key.\n *\n * Canonical shape: `pb_{ref}_{scope}{random}` where `ref` is exactly\n * 8 base62 chars and `scope` is `c` (publishable client key). See\n * docs/MODULE_HEADER_CONTRACT.md §\"API key format\" (palbase repo) for\n * the full spec.\n *\n * Returns the 8-char ref on match; `null` otherwise — callers surface\n * that as an `invalid_api_key` error.\n */\nconst REF_LEN = 8;\nconst BASE62_RE = /^[0-9A-Za-z]+$/;\n\nfunction parseProjectRef(apiKey: string): string | null {\n // pb_ (3) + ref (8) + _ (1) + scope (1) = 13 min chars\n if (apiKey.length < 13) return null;\n if (!apiKey.startsWith('pb_')) return null;\n if (apiKey[11] !== '_') return null;\n const ref = apiKey.slice(3, 11);\n if (ref.length !== REF_LEN) return null;\n if (!BASE62_RE.test(ref)) return null;\n const scope = apiKey[12];\n if (scope !== 'c') return null;\n return ref;\n}\nconst MAX_RETRIES = 3;\nconst INITIAL_BACKOFF_MS = 200;\n/**\n * Upper bound on a single 429 retry sleep. A server may return a long\n * Retry-After (a locked account can send minutes/hours); honoring it verbatim\n * would HANG the request for that whole window. Cap each retry at 10s — after\n * MAX_RETRIES the 429 envelope surfaces to the caller (fail fast, don't sleep\n * minutes). The clamp never skips a retry; it only bounds how long each waits.\n */\nconst MAX_RETRY_DELAY_MS = 10_000;\n\n/**\n * Request interceptor. Runs before every HTTP request.\n * Can modify headers, body, or reject the request.\n */\nexport type RequestInterceptor = (request: {\n headers: Record<string, string>;\n method: string;\n path: string;\n}) => void | Promise<void>;\n\nexport class HttpClient {\n protected readonly apiKey: string;\n protected readonly options?: HttpClientOptions;\n\n tokenManager: TokenManager | null = null;\n\n /**\n * Admin JWT used for platform admin endpoints (/admin/*).\n * When set, takes precedence over tokenManager access token in the\n * Authorization header.\n */\n adminToken: string | null = null;\n\n private readonly interceptors: RequestInterceptor[] = [];\n\n constructor(apiKey: string, options?: HttpClientOptions) {\n this.apiKey = apiKey;\n this.options = options;\n }\n\n /** Set (or clear) the admin JWT used on admin endpoints. */\n setAdminToken(token: string | null): void {\n this.adminToken = token;\n }\n\n /**\n * Create a scoped HttpClient that adds the given extra headers to every\n * request. The returned client shares the admin token and token manager\n * with the parent at runtime — later changes on the parent propagate to\n * the scope and vice versa.\n *\n * Typical use: tagging admin calls with `x-palbase-project: <ref>` so the\n * gateway can route them to the correct project's data plane.\n */\n withHeaders(extra: Record<string, string>): HttpClient {\n const mergedHeaders = { ...(this.options?.headers ?? {}), ...extra };\n\n const scoped: HttpClient = new HttpClient(this.apiKey, {\n ...this.options,\n headers: mergedHeaders,\n });\n scoped.tokenManager = this.tokenManager;\n // Delegate adminToken reads + writes to the parent so the scope always\n // sees the latest token, and setAdminToken on the scope affects the parent.\n Object.defineProperty(scoped, 'adminToken', {\n get: () => this.adminToken,\n set: (v: string | null) => {\n this.adminToken = v;\n },\n configurable: true,\n });\n return scoped;\n }\n\n /** Add a request interceptor. Runs before every request. */\n addInterceptor(interceptor: RequestInterceptor): void {\n this.interceptors.push(interceptor);\n }\n\n async request<T>(\n method: string,\n path: string,\n options?: RequestOptions,\n ): Promise<PalbaseResponse<T>> {\n // If token is expired and refresh is available, refresh before making the request\n if (\n this.tokenManager?.isExpired() &&\n this.tokenManager.getRefreshToken() &&\n this.tokenManager.refreshFunction\n ) {\n try {\n await this.tokenManager.refreshSession();\n } catch (e) {\n const status = e instanceof PalbaseError ? e.status : 0;\n if (status === 400 || status === 401 || status === 403) {\n // Terminal: the refresh token is dead (revoked/expired/forbidden).\n // Clear the session (listeners persist the sign-out) and proceed\n // unauthenticated — the endpoint will 401 into the normal error\n // envelope instead of bricking every subsequent call including\n // the recovery sign-in.\n this.tokenManager.clearSession();\n } else {\n throw e; // network/5xx: transient, stay loud\n }\n }\n }\n\n return this.executeWithRetry<T>(method, path, options, 0);\n }\n\n private getBaseUrl(): string {\n // Explicit URL always wins (local dev, staging, test rigs).\n if (this.options?.url) {\n return this.options.url;\n }\n\n // Validate the key shape up front so apex-routed callers still\n // fail loud on a malformed key instead of hitting the gateway\n // with bad credentials.\n if (this.apiKey && parseProjectRef(this.apiKey) === null) {\n throw new PalbaseError(\n 'invalid_api_key',\n 'Invalid API key format. Expected pb_{ref}_{scope}{random}. For dev/staging pass `url: \"https://api.dev.palbase.studio\"` via options.',\n 0,\n );\n }\n\n return `https://${PALBASE_DEFAULT_HOST}`;\n }\n\n private buildHeaders(options?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Palbase project keys live in the `apikey` header — never in\n // `Authorization` — because Kong's key-auth resolves them on that\n // header and the gateway's pre-function plugin stamps the downstream\n // identity.\n const effectiveKey = this.apiKey;\n if (effectiveKey) {\n headers['apikey'] = effectiveKey;\n // Defense-in-depth X-Project-Ref. Kong's pre-function plugin\n // always overrides this with the consumer's `custom_id` (see\n // docs/MODULE_HEADER_CONTRACT.md §\"pre-function\") so any\n // client-sent value is discarded before it reaches the\n // upstream module — but emitting one here keeps non-Kong\n // test rigs and apex-routed admin paths correct.\n const ref = parseProjectRef(effectiveKey);\n if (ref) {\n headers['X-Project-Ref'] = ref;\n }\n }\n\n // User session token, if any. Kong's pre-function plugin strips\n // Authorization on /v1/* routes anyway (PostgREST has no JWT\n // secret and would crash on a Bearer it can't decode), but\n // sending it preserves the contract for /auth/* endpoints that\n // do consume the bearer (e.g. session refresh).\n const token = this.tokenManager?.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // adminToken (platform admin JWT) takes precedence — used by the\n // @palbase/admin internal flows that hit /admin/* routes; those\n // routes verify the bearer themselves and aren't subject to the\n // /v1/* Authorization-strip rule.\n if (this.adminToken) {\n headers['Authorization'] = `Bearer ${this.adminToken}`;\n }\n\n // Merge global custom headers\n if (this.options?.headers) {\n Object.assign(headers, this.options.headers);\n }\n\n // Merge per-request headers\n if (options?.headers) {\n Object.assign(headers, options.headers);\n }\n\n return headers;\n }\n\n private async executeWithRetry<T>(\n method: string,\n path: string,\n options: RequestOptions | undefined,\n attempt: number,\n ): Promise<PalbaseResponse<T>> {\n const url = `${this.getBaseUrl()}${path}`;\n const headers = this.buildHeaders(options);\n\n // Run interceptors\n for (const interceptor of this.interceptors) {\n await interceptor({ headers, method, path });\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: options?.signal,\n };\n\n if (options?.body !== undefined) {\n fetchOptions.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await fetch(url, fetchOptions);\n } catch (error) {\n // Network error — retry with backoff\n if (attempt < MAX_RETRIES - 1) {\n const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;\n await this.delay(backoff);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n\n // All retries exhausted — throw PalbaseError\n throw new PalbaseError(\n 'network_error',\n error instanceof Error ? error.message : 'Network request failed',\n 0,\n );\n }\n\n // Handle 429 Too Many Requests — retry with Retry-After or backoff;\n // if retries exhausted, fall through to normal error response handling below\n if (response.status === 429) {\n if (attempt < MAX_RETRIES - 1) {\n const retryAfter = response.headers.get('Retry-After');\n const parsed = retryAfter ? Number.parseInt(retryAfter, 10) : Number.NaN;\n // Clamp the server-requested wait: a long Retry-After (locked account)\n // must not hang the request — cap each sleep, exhaust MAX_RETRIES, then\n // fall through to surface the 429 envelope below.\n const delayMs = Number.isNaN(parsed)\n ? INITIAL_BACKOFF_MS * 2 ** attempt\n : Math.min(parsed * 1000, MAX_RETRY_DELAY_MS);\n await this.delay(delayMs);\n return this.executeWithRetry<T>(method, path, options, attempt + 1);\n }\n }\n\n // Parse response body\n let data: T | null = null;\n let errorBody: { error?: string; error_description?: string; status?: number } | undefined;\n\n // HEAD responses have no body by spec — skip parsing.\n const contentType = response.headers.get('Content-Type');\n if (method !== 'HEAD' && contentType?.includes('json')) {\n const body = (await response.json()) as Record<string, unknown>;\n if (response.ok) {\n data = body as T;\n } else {\n errorBody = body as typeof errorBody;\n }\n }\n\n if (!response.ok) {\n return {\n data: null,\n error: new PalbaseError(\n errorBody?.error ?? 'unknown_error',\n errorBody?.error_description ?? response.statusText,\n response.status,\n errorBody,\n ),\n status: response.status,\n };\n }\n\n // Parse PostgREST Content-Range for count queries (e.g. \"0-9/42\" or \"*/42\").\n const contentRange = response.headers.get('Content-Range');\n let count: number | undefined;\n if (contentRange) {\n const slash = contentRange.lastIndexOf('/');\n if (slash >= 0) {\n const totalPart = contentRange.slice(slash + 1);\n if (totalPart !== '*') {\n const parsed = Number.parseInt(totalPart, 10);\n if (!Number.isNaN(parsed)) {\n count = parsed;\n }\n }\n }\n }\n\n return {\n data,\n error: null,\n status: response.status,\n ...(count !== undefined ? { count } : {}),\n };\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","export type Platform = 'browser' | 'node' | 'react-native' | 'deno' | 'bun';\n\ndeclare const Deno: unknown;\ndeclare const process: { versions: Record<string, string> } | undefined;\n\nexport function detectPlatform(): Platform {\n if (typeof Deno !== 'undefined') {\n return 'deno';\n }\n\n if (process?.versions) {\n if ('bun' in process.versions) {\n return 'bun';\n }\n if ('node' in process.versions) {\n return 'node';\n }\n }\n\n if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {\n return 'react-native';\n }\n\n return 'browser';\n}\n","import type { AuthStateCallback, Session, Unsubscribe } from './types.js';\n\nexport class TokenManager {\n private session: Session | null = null;\n private listeners: Set<AuthStateCallback> = new Set();\n private refreshPromise: Promise<void> | null = null;\n private refreshing = false;\n\n refreshFunction: ((refreshToken: string) => Promise<Session>) | null = null;\n\n setSession(session: Session): void {\n this.session = session;\n this.notify('SESSION_SET', session);\n }\n\n getAccessToken(): string | null {\n return this.session?.accessToken ?? null;\n }\n\n getRefreshToken(): string | null {\n return this.session?.refreshToken ?? null;\n }\n\n clearSession(): void {\n this.session = null;\n this.notify('SESSION_CLEARED', null);\n }\n\n isExpired(): boolean {\n if (!this.session) return true;\n return Date.now() >= this.session.expiresAt;\n }\n\n async refreshSession(): Promise<void> {\n if (!this.session?.refreshToken || !this.refreshFunction) {\n return;\n }\n\n // Collapse concurrent refresh calls into a single request\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n // Re-entrancy guard: the wired refreshFunction issues its own HTTP request\n // (POST /auth/token/refresh) through HttpClient, whose pre-flight calls\n // refreshSession() again SYNCHRONOUSLY — before `refreshPromise` below is\n // assigned (the whole chain runs before the first real await). Without\n // this flag that recursion is unbounded (stack overflow). Returning early\n // lets the refresh request itself proceed unauthenticated — it carries\n // the refresh token in its body, not the Bearer header.\n if (this.refreshing) {\n return;\n }\n\n this.refreshing = true;\n this.refreshPromise = this.executeRefresh(this.session.refreshToken);\n\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n this.refreshing = false;\n }\n }\n\n onAuthStateChange(callback: AuthStateCallback): Unsubscribe {\n this.listeners.add(callback);\n return () => {\n this.listeners.delete(callback);\n };\n }\n\n private async executeRefresh(refreshToken: string): Promise<void> {\n if (!this.refreshFunction) return;\n const newSession = await this.refreshFunction(refreshToken);\n this.setSession(newSession);\n }\n\n private notify(event: 'SESSION_SET' | 'SESSION_CLEARED', session: Session | null): void {\n for (const listener of this.listeners) {\n listener(event, session);\n }\n }\n}\n","import { PalbaseError, type PalbaseResponse } from '@palbase/core';\n\nexport interface FieldError {\n field: string;\n message: string;\n}\n\nexport type BackendErrorKind =\n | 'notConfigured'\n | 'validation'\n | 'unauthorized'\n | 'rateLimited'\n | 'server'\n | 'network'\n | 'decode';\n\ninterface BackendErrorParams {\n code: string;\n message: string;\n status?: number;\n requestId?: string;\n fields?: FieldError[];\n retryAfter?: number;\n data?: unknown;\n}\n\nexport class BackendError extends Error {\n readonly kind: BackendErrorKind;\n readonly code: string;\n readonly status: number;\n readonly requestId?: string;\n readonly fields?: FieldError[];\n readonly retryAfter?: number;\n readonly data?: unknown;\n\n constructor(kind: BackendErrorKind, params: BackendErrorParams) {\n super(params.message);\n this.name = 'BackendError';\n this.kind = kind;\n this.code = params.code;\n this.status = params.status ?? 0;\n this.requestId = params.requestId;\n this.fields = params.fields;\n this.retryAfter = params.retryAfter;\n this.data = params.data;\n }\n\n static notConfigured(): BackendError {\n return new BackendError('notConfigured', {\n code: 'not_configured',\n message:\n \"Palbe is not configured. Run 'palbase web link' in your project and make sure palbe.gen.ts is imported once at app startup.\",\n });\n }\n}\n\nfunction isFieldErrorArray(value: unknown): value is FieldError[] {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every(\n (v) =>\n typeof v === 'object' &&\n v !== null &&\n typeof (v as Record<string, unknown>).field === 'string' &&\n typeof (v as Record<string, unknown>).message === 'string',\n )\n );\n}\n\nfunction pickField(value: unknown, key: string): unknown {\n if (typeof value === 'object' && value !== null) {\n return (value as Record<string, unknown>)[key];\n }\n return undefined;\n}\n\nfunction pickNumber(value: unknown, key: string): number | undefined {\n const n = pickField(value, key);\n return typeof n === 'number' ? n : undefined;\n}\n\nfunction pickString(value: unknown, key: string): string | undefined {\n const s = pickField(value, key);\n return typeof s === 'string' ? s : undefined;\n}\n\nexport function fromPalbaseError(err: PalbaseError): BackendError {\n // HttpClient stores the WHOLE wire envelope as err.details:\n // { error, error_description, status, request_id, retry_after?, details?, data? }\n const base: BackendErrorParams = {\n code: err.code,\n message: err.message,\n status: err.status,\n requestId: pickString(err.details, 'request_id'),\n data: pickField(err.details, 'data'),\n };\n if (err.code === 'network_error') return new BackendError('network', base);\n if (err.status === 401) return new BackendError('unauthorized', base);\n if (err.status === 429)\n return new BackendError('rateLimited', {\n ...base,\n retryAfter: pickNumber(err.details, 'retry_after'),\n });\n // Field-error array lives at the envelope's nested `details` key.\n const nested = pickField(err.details, 'details');\n if (err.status === 400 && isFieldErrorArray(nested))\n return new BackendError('validation', { ...base, fields: nested });\n // Fallthrough also covers status-0 non-network errors (e.g. auth client's\n // synthetic 'no_refresh_token' with status 0) — those intentionally map to 'server'.\n return new BackendError('server', base);\n}\n\n/** Decode a raw wire body (used by paths that bypass HttpClient, e.g. upload). */\nexport function fromEnvelope(status: number, body: unknown): BackendError {\n const code = pickString(body, 'error') ?? 'http_error';\n const message = pickString(body, 'error_description') ?? `HTTP ${status}`;\n const requestId = pickString(body, 'request_id');\n const details = pickField(body, 'details');\n const params: BackendErrorParams = {\n code,\n message,\n status,\n requestId,\n data: pickField(body, 'data'),\n };\n if (status === 401) return new BackendError('unauthorized', params);\n if (status === 429)\n return new BackendError('rateLimited', {\n ...params,\n // Real 429 wire body has TOP-LEVEL retry_after; nested details is a fallback.\n retryAfter: pickNumber(body, 'retry_after') ?? pickNumber(details, 'retry_after'),\n });\n if (status === 400 && isFieldErrorArray(details))\n return new BackendError('validation', { ...params, fields: details });\n return new BackendError('server', params);\n}\n\n/**\n * Type guard for BackendError that survives module-identity splits.\n * This package ships dual ESM + CJS builds; if both end up loaded (or the\n * package is installed twice), two distinct BackendError classes coexist and\n * `instanceof` fails for errors thrown by \"the other\" copy. Falls back to a\n * structural check on `name` + `kind`.\n */\nexport function isBackendError(e: unknown): e is BackendError {\n return (\n e instanceof BackendError ||\n (typeof e === 'object' &&\n e !== null &&\n (e as Record<string, unknown>).name === 'BackendError' &&\n typeof (e as Record<string, unknown>).kind === 'string')\n );\n}\n\n/**\n * Structural fallback after instanceof for PalbaseError — same dual ESM+CJS\n * identity-split rationale as isBackendError above: two loaded PalbaseError\n * classes break `instanceof` across copies; the name check bridges that.\n * Returns the error as PalbaseError, or null when it isn't one.\n */\nexport function asPalbaseError(e: unknown): PalbaseError | null {\n if (e instanceof PalbaseError) return e;\n if (e instanceof Error && e.name === 'PalbaseError') return e as PalbaseError;\n return null;\n}\n\n/** Convert an internal {data,error} envelope into data-or-throw. */\nexport function unwrap<T>(res: PalbaseResponse<T>): T {\n if (res.error) throw fromPalbaseError(res.error);\n return res.data as T;\n}\n","import { asPalbaseError, fromPalbaseError, unwrap } from './errors.js';\nimport type { PalbeRuntime } from './runtime.js';\n\nexport interface RequestSpec {\n body?: unknown;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\nexport interface CallOptions {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\nconst MUTATING = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);\n\nexport async function palbeRequest<T>(\n rt: PalbeRuntime,\n method: string,\n path: string,\n spec: RequestSpec = {},\n): Promise<T> {\n const headers: Record<string, string> = { ...spec.headers };\n // Case-insensitive scan: a caller-supplied `idempotency-key` must suppress\n // generation, otherwise Headers merges both casings and dedup breaks.\n const callerHasKey = Object.keys(headers).some((k) => k.toLowerCase() === 'idempotency-key');\n if (MUTATING.has(method) && !callerHasKey) {\n headers['Idempotency-Key'] = crypto.randomUUID();\n }\n\n const attempt = async () => {\n try {\n return await rt.http.request<T>(method, path, {\n body: spec.body,\n headers,\n signal: spec.signal,\n });\n } catch (e) {\n const pe = asPalbaseError(e);\n throw pe ? fromPalbaseError(pe) : e;\n }\n };\n\n let res = await attempt();\n\n // Reactive 401: refresh once, retry once with the same Idempotency-Key.\n if (\n res.error?.status === 401 &&\n rt.tokenManager.getRefreshToken() &&\n rt.tokenManager.refreshFunction\n ) {\n try {\n await rt.tokenManager.refreshSession();\n } catch (refreshErr) {\n const pe = asPalbaseError(refreshErr);\n const status = pe?.status ?? 0;\n if (status === 400 || status === 401 || status === 403) {\n // Terminal: the refresh token is dead. Sign out and surface the\n // ORIGINAL 401 (mirrors HttpClient's pre-flight terminal split).\n rt.tokenManager.clearSession();\n throw fromPalbaseError(res.error);\n }\n // Transient (network/5xx): keep the session, surface the refresh failure.\n throw pe ? fromPalbaseError(pe) : refreshErr;\n }\n res = await attempt();\n }\n\n return unwrap(res);\n}\n","import type { NamespaceTree } from './namespaces.js';\nimport type { PalbeRuntime } from './runtime.js';\n\nexport interface PalbeGlobalState {\n runtime: PalbeRuntime | null;\n registry: NamespaceTree;\n /** Memoized materialized namespaces (per top-level key); reset on (re-)register. */\n nsCache: Record<string, unknown>;\n /** Listeners fired synchronously after every successful __configure call. */\n configuredListeners: Set<() => void>;\n}\n\n/**\n * Module-level state would split between the ESM and CJS bundles of the two\n * entries (esbuild only code-splits ESM) and between dual-package copies in\n * one process. A globalThis slot keyed by Symbol.for converges them all.\n */\nconst STATE_KEY: unique symbol = Symbol.for('palbe.state.v1');\n\ninterface PalbeGlobals {\n [STATE_KEY]?: PalbeGlobalState;\n}\n\nexport function palbeState(): PalbeGlobalState {\n // Single direct widening view of globalThis — typed via the unique-symbol\n // key above so reads/writes of the slot stay fully typed.\n const g = globalThis as PalbeGlobals;\n let state = g[STATE_KEY];\n if (!state) {\n state = { runtime: null, registry: {}, nsCache: {}, configuredListeners: new Set() };\n g[STATE_KEY] = state;\n }\n return state;\n}\n","import { BackendError, isBackendError } from './errors.js';\nimport { getRuntime } from './internal.js';\nimport { type CallOptions, palbeRequest } from './request.js';\nimport type { PalbeRuntime } from './runtime.js';\nimport { palbeState } from './state.js';\n\nexport interface EndpointDescriptor {\n method: string;\n path: string;\n /** `{name}` segments consumed as leading call args, in order. */\n pathParams?: string[];\n /**\n * 'body' (default for non-GET) | 'query' (serialize input into the URL) |\n * 'none' (no input arg — options come straight after path params).\n */\n input?: 'body' | 'query' | 'none';\n /** wire error code → lift fn; the materializer rethrows the lifted error. */\n errors?: Record<string, (e: BackendError) => Error>;\n}\n\nexport type NamespaceTree = { [key: string]: NamespaceTree | EndpointDescriptor };\n\n// Fixed surface names on pb (call/upload/auth/flags/realtime/analytics) — cannot be shadowed at the top level.\nconst FIXED_SURFACE = new Set(['call', 'upload', 'auth', 'flags', 'realtime', 'analytics']);\n\nfunction reservedNamespaceError(key: string): BackendError {\n return new BackendError('validation', {\n code: 'reserved_namespace',\n message: `Cannot register endpoint namespace '${key}': the name is reserved on pb.`,\n });\n}\n\nfunction isDescriptor(node: NamespaceTree | EndpointDescriptor): node is EndpointDescriptor {\n return (\n typeof (node as EndpointDescriptor).path === 'string' &&\n typeof (node as EndpointDescriptor).method === 'string'\n );\n}\n\n/**\n * Validate + normalize one tree level, recursively:\n * - plain-object guard (a literal `__proto__:` in a hand-written tree would\n * swap the node's prototype — cheap proto-pollution insurance);\n * - reject 'then' at EVERY level (a nested then makes that namespace object\n * thenable → `await pb.rooms` hangs) and every Object.prototype member\n * name (`'valueOf' in {}` is true — covers toString, __proto__, …);\n * - uppercase descriptor methods (request.ts matches mutating methods\n * against an uppercase set; a codegen 'get' would otherwise send a body\n * and an Idempotency-Key).\n */\nfunction validateDescriptorExtensions(d: EndpointDescriptor, path: string): void {\n if (d.pathParams !== undefined) {\n if (!Array.isArray(d.pathParams) || !d.pathParams.every((p) => typeof p === 'string')) {\n throw new BackendError('validation', {\n code: 'invalid_namespace_tree',\n message: `Descriptor '${path}': pathParams must be an array of strings.`,\n });\n }\n // Cross-check: a pathParams entry without a matching placeholder would\n // silently consume a call arg and substitute nothing — fail at registration.\n for (const p of d.pathParams) {\n if (!d.path.includes(`{${p}}`)) {\n throw new BackendError('validation', {\n code: 'invalid_namespace_tree',\n message: `Descriptor '${path}': pathParams entry '${p}' has no '{${p}}' placeholder in the path.`,\n });\n }\n }\n }\n if (d.input !== undefined && d.input !== 'body' && d.input !== 'query' && d.input !== 'none') {\n throw new BackendError('validation', {\n code: 'invalid_namespace_tree',\n message: `Descriptor '${path}': input must be 'body', 'query' or 'none'.`,\n });\n }\n if (d.errors !== undefined) {\n for (const [code, fn] of Object.entries(d.errors)) {\n if (typeof fn !== 'function') {\n throw new BackendError('validation', {\n code: 'invalid_namespace_tree',\n message: `Descriptor '${path}': errors['${code}'] must be a function.`,\n });\n }\n }\n }\n}\n\nfunction validateTree(node: NamespaceTree): void {\n const proto = Object.getPrototypeOf(node) as unknown;\n if (proto !== Object.prototype && proto !== null) {\n throw new BackendError('validation', {\n code: 'invalid_namespace_tree',\n message: 'Namespace tree nodes must be plain objects.',\n });\n }\n for (const [key, child] of Object.entries(node)) {\n if (key === 'then' || key in {}) throw reservedNamespaceError(key);\n if (isDescriptor(child)) {\n child.method = child.method.toUpperCase();\n validateDescriptorExtensions(child, child.path);\n } else {\n validateTree(child);\n }\n }\n}\n\nexport function __registerNamespaces(tree: NamespaceTree): void {\n for (const key of Object.keys(tree)) {\n if (FIXED_SURFACE.has(key)) throw reservedNamespaceError(key);\n }\n validateTree(tree);\n const state = palbeState();\n state.registry = tree;\n state.nsCache = {};\n}\n\nexport function resetNamespaces(): void {\n const state = palbeState();\n state.registry = {};\n state.nsCache = {};\n}\n\nexport function getRegistry(): NamespaceTree {\n return palbeState().registry;\n}\n\nfunction serializeQuery(input: unknown): string {\n if (input === undefined || input === null) return '';\n const entries = Object.entries(input as Record<string, unknown>)\n .filter(([, v]) => v !== undefined && v !== null)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n if (entries.length === 0) return '';\n const parts = entries.map(([k, v]) => {\n if (typeof v !== 'string' && typeof v !== 'number' && typeof v !== 'boolean') {\n // Objects/arrays would stringify to '[object Object]' on the wire — fail loud.\n throw new BackendError('validation', {\n code: 'invalid_query_value',\n message: `Query parameter '${k}' must be a string, number, or boolean.`,\n });\n }\n return `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`;\n });\n return `?${parts.join('&')}`;\n}\n\nasync function invokeDescriptor(\n rt: PalbeRuntime,\n d: EndpointDescriptor,\n args: unknown[],\n): Promise<unknown> {\n const params = d.pathParams ?? [];\n let path = d.path;\n for (let i = 0; i < params.length; i++) {\n const v = args[i];\n if (typeof v !== 'string' && typeof v !== 'number') {\n throw new BackendError('validation', {\n code: 'missing_path_param',\n message: `${d.path}: path parameter '${params[i]}' must be a string or number`,\n });\n }\n path = path.replace(`{${params[i]}}`, encodeURIComponent(String(v)));\n }\n // input:'none' ops have NO input slot — options come straight after the\n // path params (a generated `get(id, options?)` would otherwise drop its\n // options into the input slot: GET throws, POST sends them as the body).\n if (d.input === 'none' && args.length > params.length + 1) {\n throw new BackendError('validation', {\n code: 'unexpected_argument',\n message: `${d.path}: endpoint takes no input — pass only path params and options`,\n });\n }\n const input = d.input === 'none' ? undefined : args[params.length];\n const optionsSlot = d.input === 'none' ? params.length : params.length + 1;\n const options = args[optionsSlot] as CallOptions | undefined;\n let body: unknown;\n if (d.input === 'query') {\n path += serializeQuery(input);\n } else if (d.input !== 'none') {\n if (d.method === 'GET') {\n if (input !== undefined) {\n throw new BackendError('validation', {\n code: 'unsupported_get_input',\n message: `${d.path}: GET endpoints take no body input`,\n });\n }\n } else {\n body = input;\n }\n }\n try {\n return await palbeRequest(rt, d.method, path, {\n body,\n headers: options?.headers,\n signal: options?.signal,\n });\n } catch (e) {\n if (d.errors && isBackendError(e)) {\n const lift = d.errors[e.code];\n if (lift) throw lift(e);\n }\n throw e;\n }\n}\n\n/**\n * The runtime is resolved AT CALL TIME via `resolveRt` (late binding): the\n * global path passes `getRuntime` so a `__configure` swap is picked up by\n * already-materialized namespaces; bound clients pass `() => rt` so the\n * captured runtime never changes.\n */\nfunction materialize(\n node: NamespaceTree | EndpointDescriptor,\n resolveRt: () => PalbeRuntime,\n): unknown {\n if (isDescriptor(node)) {\n // async wrapper: resolveRt() may throw (notConfigured) — endpoint methods\n // must ALWAYS reject, never throw synchronously (callers use .catch()).\n return async (...args: unknown[]) => invokeDescriptor(resolveRt(), node, args);\n }\n const out: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(node)) {\n out[key] = materialize(child, resolveRt);\n }\n return out;\n}\n\n/** Global path: resolves the global runtime slot, memoizes in the global nsCache. */\nexport function getNamespace(name: string): unknown {\n const state = palbeState();\n const cached = state.nsCache[name];\n if (cached !== undefined) return cached;\n const node = state.registry[name];\n if (node === undefined) return undefined;\n const ns = materialize(node, getRuntime);\n state.nsCache[name] = ns;\n return ns;\n}\n\n/**\n * Bound path: namespaces materialize against ONE fixed runtime, memoized in a\n * per-client closure cache (the global nsCache stays global-only — a bound\n * entry there would leak one request's runtime into every other client).\n * The cache is invalidated when the registry object is swapped\n * (__registerNamespaces replaces it wholesale, e.g. watch-mode regen).\n */\nexport function boundNamespaceAccessor(rt: PalbeRuntime): (name: string) => unknown {\n let cacheRegistry = palbeState().registry;\n let cache: Record<string, unknown> = {};\n return (name: string): unknown => {\n const registry = palbeState().registry;\n if (registry !== cacheRegistry) {\n cacheRegistry = registry;\n cache = {};\n }\n const cached = cache[name];\n if (cached !== undefined) return cached;\n const node = registry[name];\n if (node === undefined) return undefined;\n const ns = materialize(node, () => rt);\n cache[name] = ns;\n return ns;\n };\n}\n","import type { HttpClient, PalbaseResponse, Session, TokenManager } from '@palbase/core';\nimport { PalbaseError } from '@palbase/core';\nimport { DeviceClient } from './device-client.js';\nimport type {\n AuthEvent,\n AuthResult,\n AuthSession,\n AuthStateChangeCallback,\n CredentialExchangeParams,\n Identity,\n LinkIdentityParams,\n MagicLinkParams,\n MagicLinkVerifyParams,\n MFAChallengeParams,\n MFAEmailChallengeParams,\n MFAEmailVerifyParams,\n MFAEnrollParams,\n MFAEnrollResult,\n MFAFactor,\n MFARecoveryParams,\n OAuthOptions,\n PasswordChangeParams,\n PasswordResetConfirmParams,\n PasswordResetParams,\n RegisterTrustedDeviceParams,\n SignInCredentials,\n SignUpCredentials,\n TokenResponse,\n TrustedDevice,\n User,\n VerifyEmailParams,\n} from './types.js';\n\n/** Reserved for future user-scope options. Admin surface is in @palbase/server. */\nexport type AuthClientOptions = Record<string, never>;\n\nconst PROVIDER_RE = /^[a-zA-Z0-9_-]+$/;\n\n/** Validates that a path parameter does not contain path traversal characters. */\nfunction validatePathParam(name: string, value: string): void {\n if (value.includes('/') || value.includes('..') || value.includes('%')) {\n throw new Error(`Invalid ${name}: must not contain path separators`);\n }\n}\n\n/** Convert AuthResult from the real API into the SDK Session shape. */\nfunction toSession(result: AuthResult): Session {\n return {\n accessToken: result.access_token,\n refreshToken: result.refresh_token,\n expiresAt: Date.now() + result.expires_in * 1000,\n };\n}\n\n/** Convert AuthResult UserInfo into the SDK User shape. */\nfunction toUser(result: AuthResult): User {\n return {\n id: result.user.id,\n email: result.user.email,\n emailVerified: result.user.email_verified,\n createdAt: result.user.created_at,\n updatedAt: result.user.created_at,\n };\n}\n\nexport class AuthClient {\n private readonly httpClient: HttpClient;\n private readonly tokenManager: TokenManager;\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private currentSession: Session | null = null;\n private hasSession = false;\n private _mfa: ReturnType<AuthClient['buildMfa']> | null = null;\n\n /** Device attestation */\n readonly device: DeviceClient;\n\n constructor(\n httpClient: HttpClient,\n tokenManager: TokenManager,\n apiKey?: string,\n baseUrl?: string,\n _options?: AuthClientOptions,\n ) {\n this.httpClient = httpClient;\n this.tokenManager = tokenManager;\n this.apiKey = apiKey ?? '';\n this.baseUrl = baseUrl ?? '';\n this.device = new DeviceClient(httpClient);\n\n this.tokenManager.onAuthStateChange((event, session) => {\n if (event === 'SESSION_SET') {\n this.currentSession = session;\n } else {\n this.currentSession = null;\n }\n });\n }\n\n // ── Core Auth ───────────────────────────────────────────\n\n async signUp(\n credentials: SignUpCredentials,\n ): Promise<PalbaseResponse<{ user: User; session: Session }>> {\n const response = await this.httpClient.request<AuthResult>('POST', '/auth/signup', {\n body: credentials,\n });\n\n if (response.data) {\n const session = toSession(response.data);\n const user = toUser(response.data);\n this.setSessionAndWireRefresh(session);\n return { data: { user, session }, error: null, status: response.status };\n }\n\n return { data: null, error: response.error, status: response.status };\n }\n\n async signIn(\n credentials: SignInCredentials,\n ): Promise<PalbaseResponse<{ user: User; session: Session }>> {\n const response = await this.httpClient.request<AuthResult>('POST', '/auth/login', {\n body: credentials,\n });\n\n if (response.data) {\n const session = toSession(response.data);\n const user = toUser(response.data);\n this.setSessionAndWireRefresh(session);\n return { data: { user, session }, error: null, status: response.status };\n }\n\n return { data: null, error: response.error, status: response.status };\n }\n\n async signOut(): Promise<PalbaseResponse<void>> {\n const response = await this.httpClient.request<void>('POST', '/auth/logout');\n this.tokenManager.clearSession();\n this.hasSession = false;\n return response;\n }\n\n // ── Email Verification ──────────────────────────────────\n\n async verifyEmail(params: VerifyEmailParams): Promise<PalbaseResponse<{ status: string }>> {\n return this.httpClient.request<{ status: string }>('POST', '/auth/verify-email', {\n body: params,\n });\n }\n\n async resendVerification(\n email: string,\n ): Promise<PalbaseResponse<{ verification_token?: string; verification_code?: string }>> {\n return this.httpClient.request<{ verification_token?: string; verification_code?: string }>(\n 'POST',\n '/auth/resend-verification',\n { body: { email } },\n );\n }\n\n // ── Password ────────────────────────────────────────────\n\n async requestPasswordReset(\n params: PasswordResetParams,\n ): Promise<PalbaseResponse<{ success: boolean }>> {\n return this.httpClient.request<{ success: boolean }>('POST', '/auth/password/reset', {\n body: params,\n });\n }\n\n async confirmPasswordReset(\n params: PasswordResetConfirmParams,\n ): Promise<PalbaseResponse<{ success: boolean }>> {\n return this.httpClient.request<{ success: boolean }>('POST', '/auth/password/reset/confirm', {\n body: params,\n });\n }\n\n async changePassword(\n params: PasswordChangeParams,\n ): Promise<PalbaseResponse<{ success: boolean }>> {\n return this.httpClient.request<{ success: boolean }>('POST', '/auth/password/change', {\n body: params,\n });\n }\n\n // ── Token ───────────────────────────────────────────────\n\n async refresh(): Promise<PalbaseResponse<TokenResponse>> {\n const refreshToken = this.tokenManager.getRefreshToken();\n if (!refreshToken) {\n return {\n data: null,\n error: new PalbaseError('no_refresh_token', 'No refresh token available', 0),\n status: 0,\n };\n }\n\n const response = await this.httpClient.request<TokenResponse>('POST', '/auth/token/refresh', {\n body: { refresh_token: refreshToken },\n });\n\n if (response.data) {\n const session: Session = {\n accessToken: response.data.access_token,\n refreshToken: response.data.refresh_token,\n expiresAt: Date.now() + response.data.expires_in * 1000,\n };\n this.tokenManager.setSession(session);\n }\n\n return response;\n }\n\n getAccessToken(): string | null {\n return this.tokenManager.getAccessToken();\n }\n\n onTokenChange(\n callback: (tokens: { accessToken: string | null; refreshToken: string | null }) => void,\n ): () => void {\n return this.tokenManager.onAuthStateChange((event, session) => {\n if (event === 'SESSION_SET' && session) {\n callback({ accessToken: session.accessToken, refreshToken: session.refreshToken });\n } else {\n callback({ accessToken: null, refreshToken: null });\n }\n });\n }\n\n setTokens(accessToken: string, refreshToken: string, expiresIn = 3600): void {\n const session: Session = {\n accessToken,\n refreshToken,\n // Default 1h when the caller has no TTL; corrected on next refresh anyway.\n expiresAt: Date.now() + expiresIn * 1000,\n };\n this.setSessionAndWireRefresh(session);\n }\n\n // ── Sessions ────────────────────────────────────────────\n\n async listSessions(): Promise<PalbaseResponse<AuthSession[]>> {\n const response = await this.httpClient.request<{ sessions: AuthSession[] }>(\n 'GET',\n '/auth/sessions',\n );\n\n if (response.data) {\n return { data: response.data.sessions, error: null, status: response.status };\n }\n\n return { data: null, error: response.error, status: response.status };\n }\n\n async revokeSession(sessionId: string): Promise<PalbaseResponse<void>> {\n validatePathParam('sessionId', sessionId);\n return this.httpClient.request<void>('DELETE', `/auth/sessions/${sessionId}`);\n }\n\n async revokeAllSessions(): Promise<PalbaseResponse<void>> {\n return this.httpClient.request<void>('DELETE', '/auth/sessions');\n }\n\n // ── MFA ─────────────────────────────────────────────────\n\n get mfa() {\n this._mfa ??= this.buildMfa();\n return this._mfa;\n }\n\n private buildMfa() {\n return {\n enroll: (params: MFAEnrollParams): Promise<PalbaseResponse<MFAEnrollResult>> =>\n this.httpClient.request('POST', '/auth/mfa/enroll', { body: params }),\n\n verifyEnrollment: (code: string): Promise<PalbaseResponse<{ status: string }>> =>\n this.httpClient.request('POST', '/auth/mfa/verify', { body: { code } }),\n\n challenge: (params: MFAChallengeParams): Promise<PalbaseResponse<TokenResponse>> =>\n this.httpClient.request('POST', '/auth/mfa/challenge', { body: params }),\n\n recovery: (params: MFARecoveryParams): Promise<PalbaseResponse<TokenResponse>> =>\n this.httpClient.request('POST', '/auth/mfa/recovery', { body: params }),\n\n listFactors: (): Promise<PalbaseResponse<{ factors: MFAFactor[] }>> =>\n this.httpClient.request('GET', '/auth/mfa/factors'),\n\n removeFactor: (\n factorId: string,\n currentPassword: string,\n ): Promise<PalbaseResponse<{ status: string }>> => {\n validatePathParam('factorId', factorId);\n return this.httpClient.request('DELETE', `/auth/mfa/factors/${factorId}`, {\n body: { current_password: currentPassword },\n });\n },\n\n regenerateRecoveryCodes: (): Promise<PalbaseResponse<{ recovery_codes: string[] }>> =>\n this.httpClient.request('POST', '/auth/mfa/recovery-codes/regenerate'),\n\n emailEnroll: (): Promise<PalbaseResponse<{ status: string }>> =>\n this.httpClient.request('POST', '/auth/mfa/email/enroll'),\n\n emailChallenge: (\n params: MFAEmailChallengeParams,\n ): Promise<PalbaseResponse<{ status: string }>> =>\n this.httpClient.request('POST', '/auth/mfa/email/challenge', { body: params }),\n\n emailVerify: (params: MFAEmailVerifyParams): Promise<PalbaseResponse<TokenResponse>> =>\n this.httpClient.request('POST', '/auth/mfa/email/verify', { body: params }),\n };\n }\n\n // ── OAuth / Social ──────────────────────────────────────\n\n async getOAuthURL(options: OAuthOptions): Promise<PalbaseResponse<{ url: string }>> {\n if (!PROVIDER_RE.test(options.provider)) {\n return {\n data: null,\n error: new PalbaseError('invalid_provider', 'Invalid OAuth provider name', 0),\n status: 0,\n };\n }\n\n const params = new URLSearchParams();\n if (options.redirectTo) {\n params.set('redirect_uri', options.redirectTo);\n }\n const query = params.toString();\n const path = `/auth/oauth/${options.provider}/authorize${query ? `?${query}` : ''}`;\n\n return this.httpClient.request<{ url: string }>('GET', path);\n }\n\n async signInWithCredential(\n params: CredentialExchangeParams,\n ): Promise<PalbaseResponse<{ user: User; session: Session }>> {\n const response = await this.httpClient.request<AuthResult>('POST', '/auth/oauth/credential', {\n body: params,\n });\n\n if (response.data?.access_token) {\n const session = toSession(response.data);\n const user = toUser(response.data);\n this.setSessionAndWireRefresh(session);\n return { data: { user, session }, error: null, status: response.status };\n }\n\n return { data: null, error: response.error, status: response.status };\n }\n\n // ── Identities ──────────────────────────────────────────\n\n async listIdentities(): Promise<PalbaseResponse<{ identities: Identity[] }>> {\n return this.httpClient.request<{ identities: Identity[] }>('GET', '/auth/identities');\n }\n\n async linkIdentity(params: LinkIdentityParams): Promise<PalbaseResponse<{ success: boolean }>> {\n return this.httpClient.request<{ success: boolean }>('POST', '/auth/identities/link', {\n body: params,\n });\n }\n\n async unlinkIdentity(identityId: string): Promise<PalbaseResponse<{ success: boolean }>> {\n validatePathParam('identityId', identityId);\n return this.httpClient.request<{ success: boolean }>(\n 'DELETE',\n `/auth/identities/${identityId}`,\n );\n }\n\n // ── Magic Link ──────────────────────────────────────────\n\n async requestMagicLink(params: MagicLinkParams): Promise<PalbaseResponse<{ success: boolean }>> {\n return this.httpClient.request<{ success: boolean }>('POST', '/auth/magic-link', {\n body: params,\n });\n }\n\n async verifyMagicLink(\n params: MagicLinkVerifyParams,\n ): Promise<PalbaseResponse<{ user: User; session: Session }>> {\n const response = await this.httpClient.request<AuthResult>('POST', '/auth/magic-link/verify', {\n body: params,\n });\n\n if (response.data?.access_token) {\n const session = toSession(response.data);\n const user = toUser(response.data);\n this.setSessionAndWireRefresh(session);\n return { data: { user, session }, error: null, status: response.status };\n }\n\n return { data: null, error: response.error, status: response.status };\n }\n\n // ── Trusted Devices ─────────────────────────────────────\n\n async listTrustedDevices(): Promise<PalbaseResponse<{ trusted_devices: TrustedDevice[] }>> {\n return this.httpClient.request<{ trusted_devices: TrustedDevice[] }>(\n 'GET',\n '/auth/trusted-devices',\n );\n }\n\n async registerTrustedDevice(\n params: RegisterTrustedDeviceParams,\n ): Promise<PalbaseResponse<{ trusted_device_token: string }>> {\n return this.httpClient.request<{ trusted_device_token: string }>(\n 'POST',\n '/auth/trusted-devices',\n { body: params },\n );\n }\n\n async revokeTrustedDevice(deviceId: string): Promise<PalbaseResponse<{ success: boolean }>> {\n validatePathParam('deviceId', deviceId);\n return this.httpClient.request<{ success: boolean }>(\n 'DELETE',\n `/auth/trusted-devices/${deviceId}`,\n );\n }\n\n // ── Session State ───────────────────────────────────────\n\n getSession(): { data: Session | null; error: null } {\n return { data: this.currentSession, error: null };\n }\n\n onAuthStateChange(callback: AuthStateChangeCallback): {\n data: { subscription: { unsubscribe: () => void } };\n } {\n const unsubscribe = this.tokenManager.onAuthStateChange((event, session) => {\n let authEvent: AuthEvent;\n\n if (event === 'SESSION_SET') {\n authEvent = this.hasSession ? 'TOKEN_REFRESHED' : 'SIGNED_IN';\n this.hasSession = true;\n } else {\n authEvent = 'SIGNED_OUT';\n this.hasSession = false;\n }\n\n callback(authEvent, session);\n });\n\n return { data: { subscription: { unsubscribe } } };\n }\n\n // ── Server-only ─────────────────────────────────────────\n\n /**\n * Verify a user's JWT token by calling GET /auth/user with their token.\n * Server-only — should NOT be exposed in client SDK.\n */\n async verifyUserToken(jwt: string): Promise<PalbaseResponse<User>> {\n const url = `${this.baseUrl}/auth/user`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'GET',\n headers: {\n apikey: this.apiKey,\n Authorization: `Bearer ${jwt}`,\n 'Content-Type': 'application/json',\n },\n });\n } catch (error) {\n throw new PalbaseError(\n 'network_error',\n error instanceof Error ? error.message : 'Network request failed',\n 0,\n );\n }\n\n const contentType = response.headers.get('Content-Type');\n if (response.ok && contentType?.includes('application/json')) {\n const data = (await response.json()) as User;\n return { data, error: null, status: response.status };\n }\n\n let errorBody: { error?: string; error_description?: string } | undefined;\n if (contentType?.includes('application/json')) {\n errorBody = (await response.json()) as typeof errorBody;\n }\n\n return {\n data: null,\n error: new PalbaseError(\n errorBody?.error ?? 'invalid_token',\n errorBody?.error_description ?? 'Token verification failed',\n response.status,\n errorBody,\n ),\n status: response.status,\n };\n }\n\n // ── Private ─────────────────────────────────────────────\n\n private setSessionAndWireRefresh(session: Session): void {\n this.tokenManager.setSession(session);\n this.hasSession = true;\n\n this.tokenManager.refreshFunction = async (refreshToken: string): Promise<Session> => {\n const response = await this.httpClient.request<TokenResponse>('POST', '/auth/token/refresh', {\n body: { refresh_token: refreshToken },\n });\n\n if (response.error || !response.data) {\n throw response.error ?? new Error('Failed to refresh token');\n }\n\n return {\n accessToken: response.data.access_token,\n refreshToken: response.data.refresh_token,\n expiresAt: Date.now() + response.data.expires_in * 1000,\n };\n };\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport type {\n AttestAndroidParams,\n AttestAndroidResult,\n AttestiOSParams,\n AttestiOSResult,\n BindDeviceParams,\n DeviceInfo,\n VerifyRequestSignatureParams,\n} from './types.js';\n\n/** Validates that a path parameter does not contain path traversal characters. */\nfunction validatePathParam(name: string, value: string): void {\n if (value.includes('/') || value.includes('..') || value.includes('%')) {\n throw new Error(`Invalid ${name}: must not contain path separators`);\n }\n}\n\nconst TOKEN_BUFFER_MS = 5 * 60 * 1000; // 5 min before expiry\n\n/**\n * Device attestation client.\n * Handles device challenge/attestation/bind and App Check JWT cache.\n */\nexport class DeviceClient {\n private readonly httpClient: HttpClient;\n private cachedToken: { token: string; expiresAt: number } | null = null;\n private refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n /** Generate a device attestation challenge. */\n async generateChallenge(): Promise<PalbaseResponse<{ challenge: string }>> {\n return this.httpClient.request<{ challenge: string }>('POST', '/auth/devices/challenge');\n }\n\n /** Attest an Android device with a Play Integrity verdict token. */\n async attestAndroid(params: AttestAndroidParams): Promise<PalbaseResponse<AttestAndroidResult>> {\n return this.httpClient.request<AttestAndroidResult>('POST', '/auth/devices/attest/android', {\n body: params,\n });\n }\n\n /** Attest an iOS device with App Attest attestation data. */\n async attestiOS(params: AttestiOSParams): Promise<PalbaseResponse<AttestiOSResult>> {\n return this.httpClient.request<AttestiOSResult>('POST', '/auth/devices/attest/ios', {\n body: params,\n });\n }\n\n /** Bind a verified device with a public key for request signing. */\n async bind(params: BindDeviceParams): Promise<PalbaseResponse<{ success: boolean }>> {\n return this.httpClient.request<{ success: boolean }>('POST', '/auth/devices/bind', {\n body: params,\n });\n }\n\n /** List all devices for the current user. */\n async list(): Promise<PalbaseResponse<{ devices: DeviceInfo[] }>> {\n return this.httpClient.request<{ devices: DeviceInfo[] }>('GET', '/auth/devices');\n }\n\n /** Delete a device by ID. */\n async delete(deviceId: string): Promise<PalbaseResponse<{ success: boolean }>> {\n validatePathParam('deviceId', deviceId);\n return this.httpClient.request<{ success: boolean }>('DELETE', `/auth/devices/${deviceId}`);\n }\n\n /**\n * Verify a request signature from a device (server-only).\n * Should NOT be exposed in client SDK.\n */\n async verifyRequestSignature(\n deviceId: string,\n params: VerifyRequestSignatureParams,\n ): Promise<PalbaseResponse<{ verified: boolean }>> {\n validatePathParam('deviceId', deviceId);\n return this.httpClient.request<{ verified: boolean }>(\n 'POST',\n `/auth/devices/${deviceId}/verify`,\n { body: params },\n );\n }\n\n /** Get the cached App Check token, or null if not available / expired. */\n getToken(): string | null {\n if (!this.cachedToken) return null;\n if (Date.now() >= this.cachedToken.expiresAt) return null;\n return this.cachedToken.token;\n }\n\n /** Whether App Check is active (token cached and not expired). */\n get isActive(): boolean {\n return this.getToken() !== null;\n }\n\n /** Set a cached App Check token manually (e.g. after attest flow). */\n setCachedToken(token: string, expiresInMs: number): void {\n this.cachedToken = {\n token,\n expiresAt: Date.now() + expiresInMs,\n };\n this.scheduleRefresh(expiresInMs);\n }\n\n /** Clean up timers and cached state. */\n dispose(): void {\n if (this.refreshTimer) {\n clearTimeout(this.refreshTimer);\n this.refreshTimer = null;\n }\n this.cachedToken = null;\n }\n\n private scheduleRefresh(ttlMs: number): void {\n if (this.refreshTimer) {\n clearTimeout(this.refreshTimer);\n }\n const refreshIn = Math.max(ttlMs - TOKEN_BUFFER_MS, 0);\n this.refreshTimer = setTimeout(() => {\n this.cachedToken = null;\n }, refreshIn);\n }\n}\n","/**\n * `pb_<endpointRef>_<scope><random>` → endpointRef ('' if malformed).\n * Single source of truth — used by runtime storage scoping AND the\n * palbe/next cookie naming; the two MUST agree or browser and server\n * would read different cookies.\n */\nexport function endpointRefFromApiKey(apiKey: string): string {\n const parts = apiKey.split('_');\n return parts.length >= 3 && parts[0] === 'pb' ? (parts[1] ?? '') : '';\n}\n","// `pb.analytics` — the stateful analytics surface. Mirrors the iOS\n// `AnalyticsClient`/`AnalyticsStorage` pair (palbackend-ios-src\n// Sources/Palbe/Analytics/): anonymous distinct-id, in-memory buffer with\n// size/timer flushing, identify/alias/reset/optOut, and auth-lifecycle\n// bindings. Transport is direct `palbeRequest` to palanalytics' live\n// ingestion wire (verified against modules/analytics Go handlers):\n//\n// POST /v1/analytics/batch {events:[{event,distinct_id,properties?,timestamp,sent_at}]}\n// POST /v1/analytics/capture single event (server mode — no buffering)\n// POST /v1/analytics/identify {distinct_id,anonymous_id,traits?,timestamp,sent_at}\n// POST /v1/analytics/alias {from,to,timestamp,sent_at}\n//\n// Ingestion is fire-and-forget (iOS K11): nothing here ever throws into the\n// caller — failures are console.warn'ed and the events dropped. The only\n// awaited surface is `flush()`, which still never rejects.\n//\n// Wire decisions (documented deviations):\n// - screen() emits the SERVER-canonical mapping (event `$screen` +\n// `properties.screen_name` — exactly what Go's /v1/analytics/screen\n// shorthand stores), NOT iOS's event-named-after-the-screen variant, so\n// batched screens and shorthand screens collapse to one stored shape.\n// - `sent_at` is stamped at SEND time (flush), not enqueue time like iOS:\n// the server's skew correction (`timestamp + (received_at - sent_at)`)\n// assumes send time; the iOS enqueue-time stamp inflates corrected\n// timestamps by the buffering delay.\n// - optOut gates ALL ingest calls (capture/screen/identify/alias) — iOS\n// parity. reset() and the X-Distinct-Id header are NOT gated (also iOS\n// parity: the header resolver never checks opt-out).\n\nimport { endpointRefFromApiKey } from './api-key.js';\nimport type { AuthChangeEvent } from './auth-facade.js';\nimport { palbeRequest } from './request.js';\nimport type { PalbeRuntime } from './runtime.js';\n\n/** Free-form event property bag (JSON object on the wire). */\nexport type AnalyticsProperties = Record<string, unknown>;\n\n/** Server event-name rule — matches modules/analytics/internal/validator/event.go\n * eventNameRegex exactly: leading letter or `$`, max 64 chars total,\n * MaxEventNameLen=64 (i.e. body up to 63 chars after the leading char). */\nconst EVENT_NAME_RE = /^[a-zA-Z$][a-zA-Z0-9_.:$-]{0,63}$/;\n\nconst FLUSH_AT = 20;\nconst FLUSH_INTERVAL_MS = 10_000;\n/** Server hard cap: /v1/analytics/batch rejects > 100 events per request. */\nconst MAX_BATCH_SIZE = 100;\n\n/** A buffered capture event — distinct_id is stamped at CAPTURE time so the\n * anon id is preserved on the wire for pre-login events (the gateway's Bearer\n * override applies to the REQUEST's X-Distinct-Id header, not the event body).\n * `sent_at` is added at flush time. */\ninterface CaptureWire {\n event: string;\n distinct_id: string;\n properties?: AnalyticsProperties;\n timestamp: number;\n}\n\nlet warnedInvalidEventName = false;\n\nfunction warnInvalidEventName(name: string): void {\n if (warnedInvalidEventName) return;\n warnedInvalidEventName = true;\n console.warn(\n `palbe analytics: dropping event \"${name}\" — names must match ^[a-zA-Z$][a-zA-Z0-9_.:$-]{0,63}$ (warned once per process)`,\n );\n}\n\n/** Minimal persisted string store: localStorage in browsers, per-runtime\n * memory elsewhere (SSR anon ids are ephemeral by design). Browser detection\n * matches the facade (`document`, not localStorage presence) — server\n * runtimes must NEVER touch storage, even when a localStorage global exists\n * (the flags server-env contract pins that). */\ninterface StringStore {\n get(key: string): string | null;\n set(key: string, value: string): void;\n remove(key: string): void;\n}\n\nfunction defaultStringStore(): StringStore {\n if (typeof document !== 'undefined' && typeof localStorage !== 'undefined') {\n return {\n get: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n set: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n // quota/private mode — the id just won't survive a reload\n }\n },\n remove: (key) => {\n try {\n localStorage.removeItem(key);\n } catch {\n // ignore\n }\n },\n };\n }\n const mem = new Map<string, string>();\n return {\n get: (key) => mem.get(key) ?? null,\n set: (key, value) => {\n mem.set(key, value);\n },\n remove: (key) => {\n mem.delete(key);\n },\n };\n}\n\n/**\n * Runtime-scoped analytics identity — constructed EAGERLY in `buildRuntime`\n * (cheap: listener wiring only, no I/O) because the X-Distinct-Id header\n * interceptor must stamp every request from the first one, even when\n * `pb.analytics` is never touched (iOS: the resolver is wired at\n * PalBackend init). The facade wraps this state lazily.\n *\n * Auth events: while no facade exists, the state maintains identity only\n * (user id for the header, anon rotation on user-initiated sign-out). Once\n * the facade attaches, it takes over wholesale and adds the ingest\n * side-effects (flush + identify / flush + reset).\n */\nexport class AnalyticsState {\n identifiedUserId: string | null = null;\n /** Set by PalbeAnalytics on construction — delegates auth events to it. */\n facadeAuthHandler: ((event: AuthChangeEvent) => void) | null = null;\n\n private anonId: string | null = null;\n private readonly store = defaultStringStore();\n private readonly idKey: string;\n private readonly optOutKey: string;\n\n constructor(rt: PalbeRuntime) {\n const ref = endpointRefFromApiKey(rt.config.apiKey);\n this.idKey = ref ? `palbe.analytics.id.${ref}` : 'palbe.analytics.id';\n this.optOutKey = ref ? `palbe.analytics.optout.${ref}` : 'palbe.analytics.optout';\n rt.auth.onAuthEvent((event) => {\n if (this.facadeAuthHandler) {\n this.facadeAuthHandler(event);\n return;\n }\n if (event.type === 'signedIn') {\n // Identity for the header only — no identify POST without a facade\n // (an app that never uses pb.analytics generates no analytics\n // traffic). Opt-out gates the link, mirroring the gated identify.\n if (!this.isOptedOut()) this.identifiedUserId = event.user.id;\n } else if (event.type === 'signedOut' && event.reason === 'userInitiated') {\n this.identifiedUserId = null;\n this.rotateAnonymousId();\n }\n // sessionExpired / tokenRefreshed: identity preserved (iOS K9)\n });\n }\n\n /** User id when identified, else the stable anon id. NEVER empty —\n * this is what `identify()` links, so stamping it on every request lets\n * the trace pipeline stitch pre-login activity to the user. */\n distinctId(): string {\n return this.identifiedUserId ?? this.anonymousId();\n }\n\n anonymousId(): string {\n if (this.anonId) return this.anonId;\n const stored = this.store.get(this.idKey);\n if (stored) {\n this.anonId = stored;\n return stored;\n }\n return this.rotateAnonymousId();\n }\n\n rotateAnonymousId(): string {\n const fresh = crypto.randomUUID();\n this.store.set(this.idKey, fresh);\n this.anonId = fresh;\n return fresh;\n }\n\n isOptedOut(): boolean {\n return this.store.get(this.optOutKey) === 'true';\n }\n\n setOptOut(on: boolean): void {\n if (on) this.store.set(this.optOutKey, 'true');\n else this.store.remove(this.optOutKey);\n }\n}\n\nexport class PalbeAnalytics {\n private readonly buffer: CaptureWire[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n /** Browser → buffer + size/timer flush. Server (no document) → immediate\n * per-call POST, zero timers (nothing leaks into RSC/route handlers). */\n private readonly browser = typeof document !== 'undefined';\n\n constructor(\n private readonly rt: PalbeRuntime,\n private readonly state: AnalyticsState,\n ) {\n this.state.facadeAuthHandler = (event) => {\n this.handleAuthEvent(event);\n };\n }\n\n /** Record an event. Invalid names are dropped (one warn per process). */\n capture(event: string, properties?: AnalyticsProperties): void {\n if (this.state.isOptedOut()) return;\n if (!EVENT_NAME_RE.test(event)) {\n warnInvalidEventName(event);\n return;\n }\n this.ingest(event, properties);\n }\n\n /** Record a screen view — server-canonical `$screen` + `screen_name`.\n * Validates non-empty only (the live wire at /v1/analytics/screen requires\n * non-empty screen_name; the event-name regex does NOT apply here). */\n screen(name: string, properties?: AnalyticsProperties): void {\n if (this.state.isOptedOut()) return;\n if (name.trim() === '') {\n console.warn('palbe analytics: dropping screen() — name must be non-empty');\n return;\n }\n this.ingest('$screen', { ...properties, screen_name: name });\n }\n\n /** Link the current anon id to `userId` and adopt it as the distinct id.\n * Immediate POST (not buffered). Re-identifying a DIFFERENT user\n * auto-resets first (iOS K10) so one anon id never links two users. */\n identify(userId: string, traits?: AnalyticsProperties): void {\n if (this.state.isOptedOut()) return;\n const existing = this.state.identifiedUserId;\n if (existing && existing !== userId) {\n console.warn(\n `palbe analytics: re-identify changes the user from ${existing} to ${userId} — auto-resetting (call reset() first if this is intentional)`,\n );\n this.reset();\n }\n const ts = Date.now();\n const body: Record<string, unknown> = {\n distinct_id: userId,\n anonymous_id: this.state.anonymousId(),\n timestamp: ts,\n sent_at: ts,\n };\n if (traits) body.traits = traits;\n this.state.identifiedUserId = userId;\n void this.post('/v1/analytics/identify', body);\n }\n\n /** Merge distinct id `from` into `to` (identity stitching). Immediate POST. */\n alias(from: string, to: string): void {\n if (this.state.isOptedOut()) return;\n const ts = Date.now();\n void this.post('/v1/analytics/alias', { from, to, timestamp: ts, sent_at: ts });\n }\n\n /** Drop the buffer, clear the identified user and rotate the anon id\n * (sign-out hygiene). Callers wanting pending events delivered flush first\n * — the auth binding does. */\n reset(): void {\n this.buffer.length = 0;\n this.cancelTimer();\n this.state.identifiedUserId = null;\n this.state.rotateAnonymousId();\n }\n\n /** Persisted GDPR opt-out. Opting out drops anything pending so nothing\n * already-buffered leaks after the user said stop. */\n setOptOut(on: boolean): void {\n this.state.setOptOut(on);\n if (on) {\n this.buffer.length = 0;\n this.cancelTimer();\n }\n }\n\n /** Drain the buffer to /v1/analytics/batch (≤100 per request, sequential).\n * Resolves when delivery finished; NEVER rejects — failed slices are\n * dropped with one warn per flush. 207 partial-reject is also warned once\n * (fire-and-forget: no retry for already-delivered batches). */\n async flush(): Promise<void> {\n this.cancelTimer();\n if (this.buffer.length === 0) return;\n const sentAt = Date.now();\n let warned = false;\n while (this.buffer.length > 0) {\n const slice = this.buffer.splice(0, MAX_BATCH_SIZE);\n try {\n const result = await palbeRequest<{ accepted?: number; rejected?: unknown[] }>(\n this.rt,\n 'POST',\n '/v1/analytics/batch',\n { body: { events: slice.map((e) => ({ ...e, sent_at: sentAt })) } },\n );\n if (Array.isArray(result?.rejected) && result.rejected.length > 0) {\n console.warn(\n `palbe analytics: batch partial-reject — ${result.rejected.length} event(s) rejected by server`,\n );\n }\n } catch (err) {\n if (!warned) {\n warned = true;\n console.warn('palbe analytics: batch flush failed — events dropped', err);\n }\n }\n }\n }\n\n // ── internals ──────────────────────────────────────────\n\n private ingest(event: string, properties?: AnalyticsProperties): void {\n const wire: CaptureWire = {\n event,\n distinct_id: this.state.distinctId(), // stamped at capture time\n timestamp: Date.now(),\n };\n if (properties) wire.properties = properties;\n if (!this.browser) {\n void this.post('/v1/analytics/capture', { ...wire, sent_at: wire.timestamp });\n return;\n }\n this.buffer.push(wire);\n if (this.buffer.length >= FLUSH_AT) void this.flush();\n else this.startTimer();\n }\n\n /** iOS PalBackend auth-binding parity: signedIn → flush + identify;\n * userInitiated signedOut → flush + reset; sessionExpired → flush only. */\n private handleAuthEvent(event: AuthChangeEvent): void {\n if (event.type === 'signedIn') {\n void this.flush().then(() => {\n this.identify(event.user.id);\n });\n } else if (event.type === 'signedOut') {\n if (event.reason === 'userInitiated') {\n void this.flush().then(() => {\n this.reset();\n });\n } else {\n void this.flush();\n }\n }\n // tokenRefreshed: nothing\n }\n\n private startTimer(): void {\n if (this.flushTimer !== null) return;\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n void this.flush();\n }, FLUSH_INTERVAL_MS);\n }\n\n private cancelTimer(): void {\n if (this.flushTimer !== null) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n private async post(path: string, body: unknown): Promise<void> {\n try {\n await palbeRequest(this.rt, 'POST', path, { body });\n } catch (err) {\n console.warn(`palbe analytics: POST ${path} failed`, err);\n }\n }\n}\n","/**\n * palauth AuthResult wire shape + its structural guard — shared by the auth\n * facade (sign-in / verify / code-exchange flows) and the palbe/next OAuth\n * callback handler, so both gate on the SAME shape rules.\n */\n\nexport interface WireUser {\n id: string;\n email?: string; // omitted for phone-only users\n email_verified: boolean;\n created_at: string;\n}\n\nexport interface WireAuthResult {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n user: WireUser;\n}\n\n/**\n * Structural guard for the palauth AuthResult wire shape. Consumers adopt\n * the session (persisting tokens to storage / cookies) BEFORE reading user\n * fields, so the full shape must be proven before adoption — a 200 body with\n * access_token but a missing/malformed user, refresh_token or expires_in\n * would otherwise half-adopt a zombie session (tokens stored, then TypeError\n * on user.id).\n */\nexport function asWireAuthResult(raw: unknown): WireAuthResult | null {\n if (typeof raw !== 'object' || raw === null) return null;\n const obj = raw as Record<string, unknown>;\n if (typeof obj.access_token !== 'string') return null;\n if (typeof obj.refresh_token !== 'string') return null;\n if (typeof obj.expires_in !== 'number') return null;\n const user = obj.user;\n if (typeof user !== 'object' || user === null) return null;\n if (typeof (user as Record<string, unknown>).id !== 'string') return null;\n return raw as WireAuthResult;\n}\n","import type { User as AuthClientUser } from '@palbase/auth';\nimport type { Session } from '@palbase/core';\nimport { asWireAuthResult, type WireAuthResult, type WireUser } from './auth-wire.js';\nimport { BackendError, unwrap } from './errors.js';\nimport { palbeRequest } from './request.js';\nimport type { PalbeRuntime } from './runtime.js';\n\nexport type MagicLinkResult =\n | ({ status: 'signedIn' } & AuthSuccess)\n | { status: 'mfaRequired'; mfaToken: string; factors: string[] };\n\nexport type OAuthExchangeResult =\n | ({ status: 'signedIn' } & AuthSuccess)\n | { status: 'mfaRequired'; mfaToken: string; factors: string[] };\n\nexport interface AuthUser {\n id: string;\n /** null for phone-only users (the server omits email). */\n email: string | null;\n emailVerified: boolean;\n createdAt: string;\n}\n\nexport interface AuthSuccess {\n user: AuthUser;\n session: Session;\n}\n\nexport type AuthState = { status: 'signedIn'; user: AuthUser } | { status: 'signedOut' };\n\nexport type AuthChangeEvent =\n | { type: 'signedIn'; user: AuthUser }\n | { type: 'signedOut'; reason: 'userInitiated' | 'sessionExpired' }\n | { type: 'tokenRefreshed' };\n\nexport type Unsubscribe = () => void;\n\n/** Map the wire bare-user shape (GET /auth/user) to AuthUser. */\nfunction mapWireUser(raw: WireUser): AuthUser {\n return {\n id: raw.id,\n email: raw.email ?? null,\n emailVerified: raw.email_verified,\n createdAt: raw.created_at,\n };\n}\n\n/** Map @palbase/auth's camelCase User to palbe's AuthUser (drop extra fields). */\nfunction mapClientUser(u: AuthClientUser): AuthUser {\n // AuthClient types email as a required string, but the wire omits it for\n // phone-only users — normalize missing/empty to null at this boundary.\n return {\n id: u.id,\n email: u.email || null,\n emailVerified: u.emailVerified,\n createdAt: u.createdAt,\n };\n}\n\n/** Key-order-proof equality on the four AuthUser fields (refreshUser change detection). */\nfunction usersEqual(a: AuthUser, b: AuthUser | null): boolean {\n return (\n b !== null &&\n a.id === b.id &&\n a.email === b.email &&\n a.emailVerified === b.emailVerified &&\n a.createdAt === b.createdAt\n );\n}\n\nexport class PalbeAuth {\n private cachedUser: AuthUser | null = null;\n private signingOut = false;\n private signingIn = false; // suppresses AuthClient's TOKEN_REFRESHED during re-signIn\n private signedInState = false; // dedupes AuthClient's repeated SIGNED_OUT events\n private readonly stateListeners = new Set<(state: AuthState) => void>();\n private readonly eventListeners = new Set<(event: AuthChangeEvent) => void>();\n private readonly userListeners = new Set<(user: AuthUser) => void>();\n\n constructor(private readonly rt: PalbeRuntime) {\n // AuthClient fires SIGNED_OUT on any session clear and TOKEN_REFRESHED on\n // refresh. SIGNED_IN is emitted by the facade itself (it knows the user);\n // after hydration AuthClient never fires SIGNED_IN anyway (hasSession=true).\n rt.authClient.onAuthStateChange((event, _session) => {\n if (event === 'SIGNED_OUT') {\n if (!this.signedInState) return; // duplicate clear — already signed out\n this.signedInState = false;\n this.cachedUser = null;\n const reason = this.signingOut ? 'userInitiated' : 'sessionExpired';\n this.emitState({ status: 'signedOut' });\n this.emitEvent({ type: 'signedOut', reason });\n } else if (event === 'TOKEN_REFRESHED') {\n // Re-signIn while signed in makes AuthClient map the new session to\n // TOKEN_REFRESHED (hasSession=true) — that would leak a tokenRefreshed\n // with the OLD user just before signedIn. iOS emits exactly one\n // signedIn per login; suppress it here.\n if (this.signingIn) return;\n this.emitEvent({ type: 'tokenRefreshed' });\n }\n });\n // Hydrated runtime starts in a signed-in-ish state (pending refresh).\n this.signedInState = rt.tokenManager.getRefreshToken() !== null;\n }\n\n // ── state ──────────────────────────────────────────────\n\n get currentUser(): AuthUser | null {\n return this.cachedUser;\n }\n\n get isSignedIn(): boolean {\n return this.rt.tokenManager.getRefreshToken() !== null;\n }\n\n // ── core flows ─────────────────────────────────────────\n\n async signUp(params: { email: string; password: string }): Promise<AuthSuccess> {\n this.signingIn = true;\n try {\n const data = unwrap(await this.rt.authClient.signUp(params));\n return this.adopt(data);\n } finally {\n this.signingIn = false;\n }\n }\n\n async signIn(params: { email: string; password: string }): Promise<AuthSuccess> {\n this.signingIn = true;\n try {\n const data = unwrap(await this.rt.authClient.signIn(params));\n return this.adopt(data);\n } finally {\n this.signingIn = false;\n }\n }\n\n async signOut(): Promise<void> {\n this.signingOut = true;\n try {\n await this.rt.authClient.signOut();\n } catch {\n // local sign-out must win even when the server call fails (offline etc.)\n } finally {\n this.rt.tokenManager.clearSession(); // idempotent; deduped by signedInState\n this.signingOut = false; // AFTER clear → reason stays userInitiated\n this.rt.storage.clear();\n this.cachedUser = null;\n }\n }\n\n async getUser(): Promise<AuthUser> {\n const raw = await palbeRequest<WireUser>(this.rt, 'GET', '/auth/user');\n return mapWireUser(raw);\n }\n\n async refreshUser(): Promise<AuthUser> {\n const user = await this.getUser();\n const changed = !usersEqual(user, this.cachedUser);\n this.cachedUser = user;\n if (changed) for (const cb of this.userListeners) this.safeInvoke(() => cb(user));\n return user;\n }\n\n // ── listeners ──────────────────────────────────────────\n\n /**\n * Subscribe to signed-in/signed-out state. Fires immediately with the\n * current snapshot (iOS parity). NOTE: a restored session (page reload) has\n * no cached user yet, so the immediate snapshot reports signedOut even when\n * `isSignedIn` is true — call `refreshUser()` on boot to populate the user\n * and rely on `isSignedIn` for the session truth.\n */\n onAuthStateChange(callback: (state: AuthState) => void): Unsubscribe {\n this.stateListeners.add(callback);\n this.safeInvoke(() => callback(this.snapshotState()));\n return () => this.stateListeners.delete(callback);\n }\n\n onAuthEvent(callback: (event: AuthChangeEvent) => void): Unsubscribe {\n this.eventListeners.add(callback);\n return () => this.eventListeners.delete(callback);\n }\n\n /** Subscribe to user-profile changes. Replays the cached user on subscribe (iOS parity). */\n onUserChange(callback: (user: AuthUser) => void): Unsubscribe {\n this.userListeners.add(callback);\n const u = this.cachedUser;\n if (u) this.safeInvoke(() => callback(u));\n return () => this.userListeners.delete(callback);\n }\n\n // ── internals ──────────────────────────────────────────\n\n private snapshotState(): AuthState {\n return this.cachedUser && this.isSignedIn\n ? { status: 'signedIn', user: this.cachedUser }\n : { status: 'signedOut' };\n }\n\n /**\n * Listener exception isolation: a throwing consumer callback must never\n * break delivery to other listeners nor propagate into the emit caller\n * (tokenManager.clearSession callers, pb.call paths, signOut, ...).\n */\n private safeInvoke(fn: () => void): void {\n try {\n fn();\n } catch {\n // consumer callback errors are intentionally swallowed\n }\n }\n\n private emitState(state: AuthState): void {\n for (const cb of this.stateListeners) this.safeInvoke(() => cb(state));\n }\n\n private emitEvent(event: AuthChangeEvent): void {\n for (const cb of this.eventListeners) this.safeInvoke(() => cb(event));\n }\n\n /** Cache the user + announce sign-in. Session was already set by AuthClient. */\n private adopt(data: { user: AuthClientUser; session: Session }): AuthSuccess {\n const user = mapClientUser(data.user);\n this.cachedUser = user;\n this.signedInState = true;\n this.emitState({ status: 'signedIn', user });\n this.emitEvent({ type: 'signedIn', user });\n return { user, session: data.session };\n }\n\n /** Adopt a raw palauth AuthResult wire shape: wire tokens, cache user, announce. */\n private adoptWire(raw: WireAuthResult): AuthSuccess {\n this.rt.authClient.setTokens(raw.access_token, raw.refresh_token, raw.expires_in);\n const user = mapWireUser(raw.user);\n this.cachedUser = user;\n this.signedInState = true;\n this.emitState({ status: 'signedIn', user });\n this.emitEvent({ type: 'signedIn', user });\n const session: Session = {\n accessToken: raw.access_token,\n refreshToken: raw.refresh_token,\n expiresAt: Date.now() + raw.expires_in * 1000,\n };\n return { user, session };\n }\n\n /** Run a sign-in flow under the signingIn flag to suppress spurious tokenRefreshed. */\n private async withSigningIn<T>(fn: () => Promise<T>): Promise<T> {\n this.signingIn = true;\n try {\n return await fn();\n } finally {\n this.signingIn = false;\n }\n }\n\n /**\n * Defensively decode a 200 union body (AuthResult | mfa-required) and\n * complete the flow. The wire contract promises one of the two shapes, but\n * a literal-`null` (or otherwise malformed) JSON body must surface as\n * BackendError('decode') — never a raw TypeError from probing a non-object.\n * The mfa branch validates its fields: `mfa_token` is required; the factor\n * list (named `factors` by magic-link verify, `mfa_factors` by the OAuth\n * callback — extraction-verified against palauth) tolerates a missing or\n * malformed value as []. The signedIn branch validates the FULL AuthResult\n * shape (asWireAuthResult) — an incomplete body falls through to decode,\n * never half-adopts.\n */\n private completeAuthUnion(\n raw: unknown,\n factorsKey: 'factors' | 'mfa_factors',\n context: string,\n ): MagicLinkResult {\n if (typeof raw === 'object' && raw !== null) {\n const obj = raw as Record<string, unknown>;\n if (obj.mfa_required === true) {\n if (typeof obj.mfa_token === 'string') {\n const list = obj[factorsKey];\n const factors = Array.isArray(list)\n ? list.filter((f): f is string => typeof f === 'string')\n : [];\n return { status: 'mfaRequired', mfaToken: obj.mfa_token, factors };\n }\n // mfa_required without a usable mfa_token is unrecoverable — fall\n // through to decode (NOT the signedIn branch).\n } else {\n const wire = asWireAuthResult(raw);\n if (wire) return { status: 'signedIn', ...this.adoptWire(wire) };\n }\n }\n // 200 body matching neither shape — never fabricate a result.\n throw this.decodeError(context);\n }\n\n private decodeError(context: string): BackendError {\n return new BackendError('decode', {\n code: 'decode_error',\n message: `Unexpected ${context} response`,\n });\n }\n\n // ── additional auth flows ──────────────────────────────\n\n async signInWithOTP(params: { phone: string }): Promise<void> {\n // The server only supports the 'sms' channel (rejects anything else).\n await palbeRequest(this.rt, 'POST', '/auth/otp', {\n body: { phone: params.phone, channel: 'sms' },\n });\n }\n\n async verifyOTP(params: { phone: string; token: string }): Promise<AuthSuccess> {\n return this.withSigningIn(async () => {\n const raw = await palbeRequest<unknown>(this.rt, 'POST', '/auth/otp/verify', {\n body: params,\n });\n // The OTP-verify wire is AuthResult ONLY (palauth phoneLoginResponse —\n // no MFA union on this route): the same zombie-session guard applies,\n // and an mfa-shaped body here is a decode error, not an MFA result.\n const wire = asWireAuthResult(raw);\n if (!wire) throw this.decodeError('OTP verify');\n return this.adoptWire(wire);\n });\n }\n\n async resetPassword(email: string): Promise<void> {\n unwrap(await this.rt.authClient.requestPasswordReset({ email }));\n }\n\n async confirmPasswordReset(params: { token: string; newPassword: string }): Promise<void> {\n unwrap(\n await this.rt.authClient.confirmPasswordReset({\n token: params.token,\n new_password: params.newPassword,\n }),\n );\n }\n\n async updatePassword(params: { currentPassword: string; newPassword: string }): Promise<void> {\n unwrap(\n await this.rt.authClient.changePassword({\n current_password: params.currentPassword,\n new_password: params.newPassword,\n }),\n );\n }\n\n async verifyEmail(params: { token: string } | { code: string; email: string }): Promise<void> {\n unwrap(await this.rt.authClient.verifyEmail(params));\n }\n\n async resendVerification(email: string): Promise<void> {\n unwrap(await this.rt.authClient.resendVerification(email));\n }\n\n async signInWithMagicLink(email: string): Promise<void> {\n unwrap(await this.rt.authClient.requestMagicLink({ email }));\n }\n\n async verifyMagicLink(token: string): Promise<MagicLinkResult> {\n return this.withSigningIn(async () => {\n const raw = await palbeRequest<unknown>(this.rt, 'POST', '/auth/magic-link/verify', {\n body: { token },\n });\n return this.completeAuthUnion(raw, 'factors', 'magic-link verify');\n });\n }\n\n async signInWithCredential(params: {\n provider: string;\n credential: string;\n }): Promise<AuthSuccess> {\n return this.withSigningIn(async () => {\n const data = unwrap(await this.rt.authClient.signInWithCredential(params));\n return this.adopt(data);\n });\n }\n\n async signInWithOAuth(params: {\n provider: string;\n redirectTo?: string;\n /** Set false to skip the browser redirect (popup/manual flows). Default true. */\n redirect?: boolean;\n }): Promise<{ url: string }> {\n const { redirect = true, ...opts } = params;\n const { url } = unwrap(await this.rt.authClient.getOAuthURL(opts));\n if (redirect && typeof window !== 'undefined' && typeof window.location !== 'undefined') {\n window.location.assign(url);\n }\n return { url };\n }\n\n /**\n * Complete the OAuth redirect flow: trade the provider's `code` + `state`\n * (from the app's redirect_uri query) for a session. PKCE verifier + state\n * live server-side in palauth — the client only relays the two values.\n */\n async exchangeCodeForSession(params: {\n provider: string;\n code: string;\n state: string;\n }): Promise<OAuthExchangeResult> {\n return this.withSigningIn(async () => {\n const path =\n `/auth/oauth/${encodeURIComponent(params.provider)}/callback` +\n `?code=${encodeURIComponent(params.code)}&state=${encodeURIComponent(params.state)}`;\n const raw = await palbeRequest<unknown>(this.rt, 'GET', path);\n return this.completeAuthUnion(raw, 'mfa_factors', 'OAuth code-exchange');\n });\n }\n\n /** Config-as-code gate for the zero-arg provider sugar. */\n private requireProvider(name: 'google' | 'apple', label: string): void {\n if (!this.rt.config.oauth?.[name]?.enabled) {\n throw new BackendError('validation', {\n code: `${name}_not_configured`,\n message: `${label} sign-in is not configured: enable the ${label} provider for this project, then regenerate palbe.gen.ts.`,\n });\n }\n }\n\n async signInWithGoogle(opts?: { redirectTo?: string; redirect?: boolean }): Promise<{\n url: string;\n }> {\n this.requireProvider('google', 'Google');\n return this.signInWithOAuth({ provider: 'google', ...opts });\n }\n\n async signInWithApple(opts?: { redirectTo?: string; redirect?: boolean }): Promise<{\n url: string;\n }> {\n this.requireProvider('apple', 'Apple');\n return this.signInWithOAuth({ provider: 'apple', ...opts });\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport type { FlagValue } from './types.js';\n\n/**\n * Admin client for the user-flags module — wraps the system flag CRUD\n * and per-user override endpoints exposed by `modules/user-flags`.\n *\n * Two surfaces:\n * - `.system.*` — project-wide flag defaults (platform auth)\n * - `.users.*` — per-user overrides (service-side)\n */\nexport class FlagsAdminClient {\n readonly system: SystemFlagsAdminClient;\n readonly users: UserFlagsAdminClient;\n\n constructor(httpClient: HttpClient) {\n this.system = new SystemFlagsAdminClient(httpClient);\n this.users = new UserFlagsAdminClient(httpClient);\n }\n}\n\nexport interface SystemFlag {\n key: string;\n value: FlagValue;\n /** Optional variant label when the flag is variant-typed. */\n variant?: string;\n /** Optional targeting rules (shape passes through from the backend). */\n rules?: unknown;\n updated_at?: string;\n}\n\nexport interface UserFlagOverride {\n user_id: string;\n key: string;\n value: FlagValue;\n variant?: string;\n updated_at?: string;\n}\n\nexport interface UserFlagsSnapshot {\n user_id: string;\n flags: Record<string, FlagValue>;\n}\n\nexport interface BatchFlagInput {\n /** Optional: evaluate for these user IDs. */\n user_ids?: string[];\n /** Optional: evaluate a subset of flag keys. */\n keys?: string[];\n}\n\nexport interface BatchFlagResult {\n user_id: string;\n flags: Record<string, FlagValue>;\n}\n\nexport class SystemFlagsAdminClient {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<PalbaseResponse<SystemFlag[]>> {\n return this.http.request<SystemFlag[]>('GET', '/v1/user-flags/system');\n }\n\n get(key: string): Promise<PalbaseResponse<SystemFlag>> {\n return this.http.request<SystemFlag>('GET', `/v1/user-flags/system/${encodeURIComponent(key)}`);\n }\n\n put(\n key: string,\n input: { value: FlagValue; variant?: string; rules?: unknown },\n ): Promise<PalbaseResponse<SystemFlag>> {\n return this.http.request<SystemFlag>(\n 'PUT',\n `/v1/user-flags/system/${encodeURIComponent(key)}`,\n { body: input },\n );\n }\n\n delete(key: string): Promise<PalbaseResponse<void>> {\n return this.http.request<void>('DELETE', `/v1/user-flags/system/${encodeURIComponent(key)}`);\n }\n}\n\nexport class UserFlagsAdminClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Read evaluated flags for a user (defaults + overrides merged). */\n get(userId: string): Promise<PalbaseResponse<UserFlagsSnapshot>> {\n return this.http.request<UserFlagsSnapshot>(\n 'GET',\n `/v1/user-flags/users/${encodeURIComponent(userId)}`,\n );\n }\n\n /**\n * Replace the full set of overrides for a user in one call. Any key not\n * present in `overrides` is removed.\n */\n putBulk(\n userId: string,\n overrides: Record<string, FlagValue>,\n ): Promise<PalbaseResponse<UserFlagsSnapshot>> {\n return this.http.request<UserFlagsSnapshot>(\n 'PUT',\n `/v1/user-flags/users/${encodeURIComponent(userId)}`,\n { body: { overrides } },\n );\n }\n\n putOverride(\n userId: string,\n key: string,\n input: { value: FlagValue; variant?: string },\n ): Promise<PalbaseResponse<UserFlagOverride>> {\n return this.http.request<UserFlagOverride>(\n 'PUT',\n `/v1/user-flags/users/${encodeURIComponent(userId)}/${encodeURIComponent(key)}`,\n { body: input },\n );\n }\n\n deleteOverride(userId: string, key: string): Promise<PalbaseResponse<void>> {\n return this.http.request<void>(\n 'DELETE',\n `/v1/user-flags/users/${encodeURIComponent(userId)}/${encodeURIComponent(key)}`,\n );\n }\n\n deleteAllOverrides(userId: string): Promise<PalbaseResponse<void>> {\n return this.http.request<void>('DELETE', `/v1/user-flags/users/${encodeURIComponent(userId)}`);\n }\n\n /** Evaluate flags for a batch of users / a subset of keys. */\n batch(input?: BatchFlagInput): Promise<PalbaseResponse<BatchFlagResult[]>> {\n return this.http.request<BatchFlagResult[]>('POST', '/v1/user-flags/batch', {\n body: input ?? {},\n });\n }\n}\n","import type { HttpClient, PalbaseResponse } from '@palbase/core';\nimport type {\n Flag,\n FlagContext,\n FlagSource,\n FlagsDelta,\n FlagsSnapshot,\n FlagValue,\n FlagVariant,\n} from './types.js';\n\nconst FLAG_NAME_RE = /^[a-zA-Z0-9_-]+$/;\n\n/**\n * Result of writing a single user override (`PUT .../{userId}/{key}`) — echoes\n * the resolved key/value with `source: \"override\"`.\n */\nexport interface SetOverrideResult {\n key: string;\n value: FlagValue;\n source: FlagSource;\n}\n\n/**\n * Result of replacing all overrides for one user (`PUT .../{userId}`).\n * `values` is the resolved override set after the write.\n */\nexport interface SetOverridesResult {\n values: Record<string, FlagValue>;\n}\n\n/**\n * Result of clearing a single override (`DELETE .../{userId}/{key}`). `value`\n * is the system (default) value the user falls back to after removal, with\n * `source: \"system\"`.\n */\nexport interface ClearOverrideResult {\n key: string;\n value: FlagValue;\n source: FlagSource;\n}\n\n/** Result of clearing all overrides for a user (`DELETE .../{userId}`). */\nexport interface ClearAllOverridesResult {\n deleted: number;\n}\n\n/** One cross-user override operation for {@link FlagsServiceClient.batchSetOverrides}. */\nexport interface BatchOverrideOperation {\n userId: string;\n values: Record<string, FlagValue>;\n}\n\n/** Result of a cross-user batch write (`POST /v1/user-flags/batch`). */\nexport interface BatchSetOverridesResult {\n applied: number;\n}\n\n/**\n * Cross-user admin write surface, reached via {@link FlagsClient.asService}.\n *\n * These five methods set/clear overrides for an ARBITRARY user (named\n * explicitly), so they bypass the current-user scope that the default\n * `FlagsClient.setOverride(...)` is bound to. Mirrors the\n * `Database` / `Database.asService()` split: the privileged cross-user path is\n * moved OFF the default surface so it is greppable + intentional. Requires a\n * privileged key (service_role / managed runtime).\n */\nexport interface FlagsServiceClient {\n /**\n * Set (or replace) a single feature-flag override for one user.\n * `PUT /v1/user-flags/users/{userId}/{key}` with body `{ value }`.\n */\n setOverrideForUser(\n userId: string,\n key: string,\n value: FlagValue,\n ): Promise<PalbaseResponse<SetOverrideResult>>;\n\n /**\n * Set multiple feature-flag overrides for one user in a single call.\n * `PUT /v1/user-flags/users/{userId}` with body `{ values }`.\n */\n setOverridesForUser(\n userId: string,\n values: Record<string, FlagValue>,\n ): Promise<PalbaseResponse<SetOverridesResult>>;\n\n /**\n * Clear a single override for one user, restoring the project (system)\n * default for that key. `DELETE /v1/user-flags/users/{userId}/{key}`.\n */\n clearOverrideForUser(userId: string, key: string): Promise<PalbaseResponse<ClearOverrideResult>>;\n\n /**\n * Clear all overrides for one user, restoring project defaults for every\n * key. `DELETE /v1/user-flags/users/{userId}`.\n */\n clearAllOverridesForUser(userId: string): Promise<PalbaseResponse<ClearAllOverridesResult>>;\n\n /**\n * Apply override writes across many users in one request (max 1000 ops).\n * `POST /v1/user-flags/batch` with body `{ operations: [{ user_id, values }] }`.\n */\n batchSetOverrides(\n operations: ReadonlyArray<BatchOverrideOperation>,\n ): Promise<PalbaseResponse<BatchSetOverridesResult>>;\n}\n\n/**\n * Stateless, one-shot HTTP wrapper for the flags API. Used directly for legacy\n * `/v1/flags/*` reads, and as the transport layer underneath `FlagsPool`\n * (which adds caching, polling and auth-state binding on top).\n *\n * Writes mirror the `Database` / `Database.asService()` model:\n * — {@link setOverride} (default) writes an override for the CURRENT user (the\n * `getCurrentUserId` getter supplied at construction). No userId argument; it\n * errors clearly when there is no signed-in user.\n * — {@link asService} returns the cross-user admin surface\n * ({@link FlagsServiceClient}) for writing overrides for an arbitrary user.\n */\nexport class FlagsClient {\n private readonly httpClient: HttpClient;\n private readonly getCurrentUserId?: () => string | null | undefined;\n\n /**\n * @param httpClient transport.\n * @param getCurrentUserId optional getter resolving the signed-in user's id\n * for {@link setOverride}. When omitted (or it returns no id), `setOverride`\n * errors and the caller must use {@link asService} for cross-user writes.\n */\n constructor(httpClient: HttpClient, getCurrentUserId?: () => string | null | undefined) {\n this.httpClient = httpClient;\n this.getCurrentUserId = getCurrentUserId;\n }\n\n async isEnabled(flagName: string, context?: FlagContext): Promise<PalbaseResponse<boolean>> {\n if (!FLAG_NAME_RE.test(flagName)) {\n throw new Error(\n `Invalid flag name: \"${flagName}\". Flag names must match ${FLAG_NAME_RE.source}`,\n );\n }\n\n const params = this.buildContextParams(context);\n const query = params.toString();\n const path = `/v1/flags/${flagName}/enabled${query ? `?${query}` : ''}`;\n\n return this.httpClient.request<boolean>('GET', path);\n }\n\n async getVariant(flagName: string, context?: FlagContext): Promise<PalbaseResponse<FlagVariant>> {\n if (!FLAG_NAME_RE.test(flagName)) {\n throw new Error(\n `Invalid flag name: \"${flagName}\". Flag names must match ${FLAG_NAME_RE.source}`,\n );\n }\n\n const params = this.buildContextParams(context);\n const query = params.toString();\n const path = `/v1/flags/${flagName}/variant${query ? `?${query}` : ''}`;\n\n return this.httpClient.request<FlagVariant>('GET', path);\n }\n\n async getAll(context?: FlagContext): Promise<PalbaseResponse<Flag[]>> {\n const params = this.buildContextParams(context);\n const query = params.toString();\n const path = `/v1/flags${query ? `?${query}` : ''}`;\n\n return this.httpClient.request<Flag[]>('GET', path);\n }\n\n /**\n * Cold-start read: the full merged flag set for the current auth identity.\n * With a Bearer token the backend returns the user-merged view; without one\n * it returns project (system) defaults.\n */\n snapshot(): Promise<PalbaseResponse<FlagsSnapshot>> {\n return this.httpClient.request<FlagsSnapshot>('GET', '/v1/user-flags/snapshot');\n }\n\n /**\n * Incremental read of version-ordered ops since `sinceVersion`.\n *\n * Sends `If-None-Match: <sinceVersion>` so an unchanged server can answer\n * `304 Not Modified` cheaply. The 304 surfaces as a `PalbaseResponse` with\n * `status === 304` (the core http client maps non-2xx to an error/empty body);\n * callers treat that as \"no change\".\n */\n delta(sinceVersion: string): Promise<PalbaseResponse<FlagsDelta>> {\n return this.httpClient.request<FlagsDelta>(\n 'GET',\n `/v1/user-flags/delta?since=${encodeURIComponent(sinceVersion)}`,\n { headers: { 'If-None-Match': sinceVersion } },\n );\n }\n\n /**\n * Set (or replace) a single feature-flag override for the CURRENT USER.\n *\n * Resolves the signed-in user from the `getCurrentUserId` getter supplied at\n * construction and issues `PUT /v1/user-flags/users/{uid}/{key}` with body\n * `{ value }`. No userId argument — the override is bound to the current\n * user, so no admin power is required.\n *\n * Errors when there is no signed-in user (anonymous, or no getter supplied);\n * use {@link asService}`.setOverrideForUser(userId, key, value)` to write a\n * flag override for an arbitrary (cross-user) target.\n */\n setOverride(key: string, value: FlagValue): Promise<PalbaseResponse<SetOverrideResult>> {\n const userId = this.getCurrentUserId?.();\n if (!userId) {\n throw new Error(\n 'setOverride requires a signed-in user; use FlagsClient.asService().setOverrideForUser(userId, key, value) for cross-user writes',\n );\n }\n return this.httpClient.request<SetOverrideResult>(\n 'PUT',\n `/v1/user-flags/users/${encodeURIComponent(userId)}/${encodeURIComponent(key)}`,\n { body: { value } },\n );\n }\n\n /**\n * Return the cross-user admin write surface ({@link FlagsServiceClient}).\n *\n * Use sparingly and explicitly — the default {@link setOverride} path is\n * bound to the current user; `asService()` is how you write a flag override\n * for an ARBITRARY user. Mirrors `Database.asService()`. The returned client\n * shares this client's transport (the privileged service_role key).\n */\n asService(): FlagsServiceClient {\n const http = this.httpClient;\n return {\n setOverrideForUser(userId, key, value) {\n return http.request<SetOverrideResult>(\n 'PUT',\n `/v1/user-flags/users/${encodeURIComponent(userId)}/${encodeURIComponent(key)}`,\n { body: { value } },\n );\n },\n setOverridesForUser(userId, values) {\n return http.request<SetOverridesResult>(\n 'PUT',\n `/v1/user-flags/users/${encodeURIComponent(userId)}`,\n { body: { values } },\n );\n },\n clearOverrideForUser(userId, key) {\n return http.request<ClearOverrideResult>(\n 'DELETE',\n `/v1/user-flags/users/${encodeURIComponent(userId)}/${encodeURIComponent(key)}`,\n );\n },\n clearAllOverridesForUser(userId) {\n return http.request<ClearAllOverridesResult>(\n 'DELETE',\n `/v1/user-flags/users/${encodeURIComponent(userId)}`,\n );\n },\n batchSetOverrides(operations) {\n return http.request<BatchSetOverridesResult>('POST', '/v1/user-flags/batch', {\n body: {\n operations: operations.map((op) => ({\n user_id: op.userId,\n values: op.values,\n })),\n },\n });\n },\n };\n }\n\n private buildContextParams(context?: FlagContext): URLSearchParams {\n const params = new URLSearchParams();\n if (context?.userId) {\n params.set('userId', context.userId);\n }\n if (context?.properties) {\n params.set('properties', JSON.stringify(context.properties));\n }\n return params;\n }\n}\n","import type { FlagsClient } from './flags-client.js';\nimport type {\n AuthStateSource,\n FlagSource,\n FlagsChangeListener,\n FlagsSnapshot,\n FlagValue,\n Unsubscribe,\n} from './types.js';\n\n/** Options controlling pool behaviour. All have sensible defaults. */\nexport interface FlagsPoolOptions {\n /** Delta poll interval in ms. Default 30_000. Set `0` to disable polling. */\n pollIntervalMs?: number;\n /**\n * localStorage key for cold-start persistence. Default\n * `palbase.flags.snapshot`. Ignored when localStorage is unavailable (SSR).\n */\n storageKey?: string;\n /** Disable localStorage persistence even in the browser. Default false. */\n persist?: boolean;\n /**\n * Auth-state source. When provided, the pool re-snapshots on sign-in and\n * sign-out so the cache switches between user overrides and project defaults.\n */\n auth?: AuthStateSource;\n /**\n * Inject timer/visibility primitives for testing. Defaults to the global\n * `setInterval`/`clearInterval` and the browser `document`/`window`.\n */\n env?: PoolEnv;\n}\n\n/** Injectable runtime seams (browser globals) so the pool stays testable. */\nexport interface PoolEnv {\n now(): number;\n setInterval(handler: () => void, ms: number): IntervalHandle;\n clearInterval(handle: IntervalHandle): void;\n /** localStorage-like store, or `null` when unavailable (SSR). */\n storage: KeyValueStore | null;\n /** Visibility source, or `null` when there is no document (SSR). */\n visibility: VisibilitySource | null;\n}\n\nexport type IntervalHandle = ReturnType<typeof setInterval>;\n\nexport interface KeyValueStore {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nexport interface VisibilitySource {\n isHidden(): boolean;\n onVisibilityChange(cb: () => void): Unsubscribe;\n}\n\nconst DEFAULT_POLL_MS = 30_000;\nconst DEFAULT_STORAGE_KEY = 'palbase.flags.snapshot';\n\ninterface CacheState {\n values: Record<string, FlagValue>;\n sources: Record<string, FlagSource>;\n syncVersion: string | null;\n}\n\nconst EMPTY_STATE: CacheState = {\n values: {},\n sources: {},\n syncVersion: null,\n};\n\n/**\n * Stateful, auto-pooling flag cache built on top of the stateless\n * {@link FlagsClient} transport.\n *\n * Responsibilities:\n * - Cold start from a snapshot (and from localStorage for instant boot).\n * - Poll deltas with conditional `If-None-Match` (304 → no-op).\n * - Re-snapshot on a version gap / tombstone-expiry signalled by the backend.\n * - Re-snapshot on auth sign-in / sign-out (override view ↔ defaults).\n * - Notify subscribers (drives `useFlag`/`useFlags`).\n *\n * Resolution is server-side: `get(key, default)` returns the cached value or\n * the supplied default; the pool never merges.\n */\nexport class FlagsPool {\n private readonly transport: FlagsClient;\n private readonly pollIntervalMs: number;\n private readonly storageKey: string;\n private readonly persist: boolean;\n private readonly env: PoolEnv;\n\n private state: CacheState = EMPTY_STATE;\n private readonly listeners = new Set<FlagsChangeListener>();\n\n private timer: IntervalHandle | null = null;\n private polling = false;\n private started = false;\n private destroyed = false;\n\n private readyPromise: Promise<void>;\n private resolveReady: (() => void) | null = null;\n private firstLoadDone = false;\n\n private authUnsub: Unsubscribe | null = null;\n private visibilityUnsub: Unsubscribe | null = null;\n private lastAuthHasSession: boolean | null = null;\n\n /** Cached frozen view for `getSnapshot` identity stability (React). */\n private frozenView: Readonly<Record<string, FlagValue>> = Object.freeze({});\n private viewDirty = true;\n\n constructor(transport: FlagsClient, options: FlagsPoolOptions = {}) {\n this.transport = transport;\n this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_MS;\n this.storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;\n this.persist = options.persist ?? true;\n this.env = options.env ?? defaultEnv();\n\n this.readyPromise = new Promise<void>((resolve) => {\n this.resolveReady = resolve;\n });\n\n // Hydrate synchronously from persisted snapshot for instant cold-start.\n this.hydrateFromStorage();\n\n if (options.auth) {\n this.bindAuth(options.auth);\n }\n }\n\n // ---- public surface -----------------------------------------------------\n\n /** Start cold-start fetch + polling + visibility binding. Idempotent. */\n start(): void {\n if (this.started || this.destroyed) {\n return;\n }\n this.started = true;\n\n void this.refresh();\n this.startPolling();\n this.bindVisibility();\n }\n\n /** Resolves once the first snapshot (or persisted hydrate) is available. */\n ready(): Promise<void> {\n if (this.firstLoadDone) {\n return Promise.resolve();\n }\n if (!this.started) {\n this.start();\n }\n return this.readyPromise;\n }\n\n /** Returns the cached value for `key`, or `fallback` when absent. */\n get<T extends FlagValue>(key: string, fallback: T): T {\n const value = this.state.values[key];\n return value === undefined ? fallback : (value as T);\n }\n\n /** Boolean convenience: cached value strictly equals `true`, else `fallback`. */\n isEnabled(key: string, fallback = false): boolean {\n const value = this.state.values[key];\n if (value === undefined) {\n return fallback;\n }\n return value === true;\n }\n\n /** Returns the cached value's source (`system`/`user`) when known. */\n getSource(key: string): FlagSource | undefined {\n return this.state.sources[key];\n }\n\n /** Returns a frozen snapshot of all cached values (stable identity). */\n all(): Readonly<Record<string, FlagValue>> {\n if (this.viewDirty) {\n this.frozenView = Object.freeze({ ...this.state.values });\n this.viewDirty = false;\n }\n return this.frozenView;\n }\n\n /** Current opaque sync version (null before first load). */\n get syncVersion(): string | null {\n return this.state.syncVersion;\n }\n\n /** Subscribe to any change in the cached set. Returns unsubscribe. */\n onChange(listener: FlagsChangeListener): Unsubscribe {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * `useSyncExternalStore`-compatible subscribe. Auto-starts the pool on first\n * subscription so a React component mount triggers cold-start.\n */\n subscribe = (listener: FlagsChangeListener): Unsubscribe => {\n if (!this.started) {\n this.start();\n }\n return this.onChange(listener);\n };\n\n /** Force an immediate re-snapshot (used on auth change / manual refresh). */\n async refresh(): Promise<void> {\n if (this.destroyed) {\n return;\n }\n const res = await this.transport.snapshot();\n if (res.error || res.data == null) {\n // Keep any persisted/previous state; still mark first load complete so\n // `ready()` does not hang forever on a transient network failure.\n this.markFirstLoadDone();\n return;\n }\n this.applySnapshot(res.data);\n this.markFirstLoadDone();\n }\n\n /** Stop timers and detach all auth/visibility listeners. */\n destroy(): void {\n this.destroyed = true;\n this.stopPolling();\n this.authUnsub?.();\n this.authUnsub = null;\n this.visibilityUnsub?.();\n this.visibilityUnsub = null;\n this.listeners.clear();\n }\n\n // ---- internals ----------------------------------------------------------\n\n private async poll(): Promise<void> {\n if (this.polling || this.destroyed) {\n return;\n }\n const since = this.state.syncVersion;\n if (since == null) {\n // No baseline yet — fall back to a snapshot.\n await this.refresh();\n return;\n }\n this.polling = true;\n try {\n const res = await this.transport.delta(since);\n if (res.status === 304) {\n return; // not modified — cheap no-op\n }\n if (res.error || res.data == null) {\n // A version gap / expired tombstone window is signalled by 409/410.\n if (res.status === 409 || res.status === 410) {\n await this.refresh();\n }\n return;\n }\n this.applyDelta(res.data.ops as readonly unknown[], res.data.sync_version);\n } finally {\n this.polling = false;\n }\n }\n\n private applySnapshot(snapshot: FlagsSnapshot): void {\n this.state = {\n values: { ...snapshot.values },\n sources: { ...(snapshot.sources ?? {}) },\n syncVersion: snapshot.sync_version,\n };\n this.viewDirty = true;\n this.persistState();\n this.notify();\n }\n\n private applyDelta(ops: readonly unknown[], nextVersion: string): void {\n // Each op is re-narrowed defensively via `isDeltaOp`.\n const values = { ...this.state.values };\n const sources = { ...this.state.sources };\n let changed = false;\n\n for (const raw of ops) {\n if (!isDeltaOp(raw)) {\n continue;\n }\n if (raw.op === 'delete') {\n if (raw.key in values) {\n delete values[raw.key];\n delete sources[raw.key];\n changed = true;\n }\n } else {\n values[raw.key] = raw.value;\n if (raw.source) {\n sources[raw.key] = raw.source;\n }\n changed = true;\n }\n }\n\n this.state = { values, sources, syncVersion: nextVersion };\n if (changed) {\n this.viewDirty = true;\n this.persistState();\n this.notify();\n } else {\n // version still advances even with no value change\n this.persistState();\n }\n }\n\n private startPolling(): void {\n if (this.pollIntervalMs <= 0 || this.timer != null) {\n return;\n }\n this.timer = this.env.setInterval(() => {\n void this.poll();\n }, this.pollIntervalMs);\n }\n\n private stopPolling(): void {\n if (this.timer != null) {\n this.env.clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n private bindVisibility(): void {\n const vis = this.env.visibility;\n if (!vis) {\n return;\n }\n this.visibilityUnsub = vis.onVisibilityChange(() => {\n if (vis.isHidden()) {\n this.stopPolling();\n } else {\n // Came back to foreground: catch up immediately, then resume polling.\n void this.poll();\n this.startPolling();\n }\n });\n }\n\n private bindAuth(auth: AuthStateSource): void {\n const handle = auth.onAuthStateChange((_event, session) => {\n const hasSession = session != null;\n // Fires immediately with current state on subscribe; only re-snapshot\n // when the identity actually flips (sign-in ↔ sign-out), not on every\n // token refresh, to avoid redundant cold-starts.\n if (this.lastAuthHasSession === null) {\n this.lastAuthHasSession = hasSession;\n return;\n }\n if (hasSession !== this.lastAuthHasSession) {\n this.lastAuthHasSession = hasSession;\n if (this.started) {\n void this.refresh();\n }\n }\n });\n // Normalize the Supabase-style handle to a plain unsubscribe.\n this.authUnsub = () => handle.data.subscription.unsubscribe();\n }\n\n private notify(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n\n private markFirstLoadDone(): void {\n if (!this.firstLoadDone) {\n this.firstLoadDone = true;\n this.resolveReady?.();\n this.resolveReady = null;\n }\n }\n\n private hydrateFromStorage(): void {\n if (!this.persist) {\n return;\n }\n const store = this.env.storage;\n if (!store) {\n return;\n }\n const raw = store.getItem(this.storageKey);\n if (!raw) {\n return;\n }\n const parsed = parsePersisted(raw);\n if (!parsed) {\n return;\n }\n this.state = parsed;\n this.viewDirty = true;\n // A persisted snapshot is enough to satisfy `ready()` instantly; a live\n // `refresh()` will still run on `start()` to revalidate.\n this.markFirstLoadDone();\n }\n\n private persistState(): void {\n if (!this.persist) {\n return;\n }\n const store = this.env.storage;\n if (!store || this.state.syncVersion == null) {\n return;\n }\n try {\n store.setItem(this.storageKey, JSON.stringify(this.state));\n } catch {\n // Quota / private-mode — persistence is best-effort.\n }\n }\n}\n\n// ---- guards / helpers -----------------------------------------------------\n\nfunction isDeltaOp(\n value: unknown,\n): value is\n | { op: 'put'; key: string; value: FlagValue; source?: FlagSource }\n | { op: 'delete'; key: string } {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const rec = value as Record<string, unknown>;\n if (typeof rec.key !== 'string') {\n return false;\n }\n if (rec.op === 'delete') {\n return true;\n }\n return rec.op === 'put' && 'value' in rec;\n}\n\nfunction parsePersisted(raw: string): CacheState | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return null;\n }\n const rec = parsed as Record<string, unknown>;\n if (typeof rec.syncVersion !== 'string') {\n return null;\n }\n if (typeof rec.values !== 'object' || rec.values === null) {\n return null;\n }\n const sources =\n typeof rec.sources === 'object' && rec.sources !== null\n ? (rec.sources as Record<string, FlagSource>)\n : {};\n return {\n values: rec.values as Record<string, FlagValue>,\n sources,\n syncVersion: rec.syncVersion,\n };\n}\n\n/** Build the default browser/Node runtime seam, guarding all DOM globals. */\nfunction defaultEnv(): PoolEnv {\n const storage = resolveStorage();\n const visibility = resolveVisibility();\n return {\n now: () => Date.now(),\n setInterval: (handler, ms) => setInterval(handler, ms),\n clearInterval: (handle) => clearInterval(handle),\n storage,\n visibility,\n };\n}\n\nfunction resolveStorage(): KeyValueStore | null {\n try {\n if (typeof localStorage === 'undefined') {\n return null;\n }\n // Touch it to surface private-mode/security errors up front.\n const probe = '__palbase_flags_probe__';\n localStorage.setItem(probe, '1');\n localStorage.removeItem(probe);\n return localStorage;\n } catch {\n return null;\n }\n}\n\nfunction resolveVisibility(): VisibilitySource | null {\n if (typeof document === 'undefined') {\n return null;\n }\n const doc = document;\n return {\n isHidden: () => doc.visibilityState === 'hidden',\n onVisibilityChange: (cb) => {\n doc.addEventListener('visibilitychange', cb);\n return () => doc.removeEventListener('visibilitychange', cb);\n },\n };\n}\n","import type { PalbaseResponse } from '@palbase/core';\nimport {\n type AuthStateSource,\n FlagsClient,\n FlagsPool,\n type FlagsPoolOptions,\n type FlagValue,\n type FlagVariant,\n type PoolEnv,\n} from '@palbase/flags';\nimport { endpointRefFromApiKey } from './api-key.js';\nimport type { PalbeAuth, Unsubscribe } from './auth-facade.js';\nimport { BackendError } from './errors.js';\nimport type { PalbeRuntime } from './runtime.js';\n\nexport type { FlagValue } from '@palbase/flags';\n\n/** Frozen view of all cached flag values (what `all()` returns / `changes()` yields). */\nexport type FlagsView = Readonly<Record<string, FlagValue>>;\n\n/**\n * Adapt PalbeAuth's `(state: AuthState) => void` listener contract to the\n * Supabase-style `AuthStateSource` FlagsPool binds to (`(event, session)`\n * callback + `{ data: { subscription: { unsubscribe } } }` handle).\n *\n * PalbeAuth fires the callback IMMEDIATELY on subscribe (iOS parity); the\n * pool's bindAuth treats that first call as the baseline (its\n * `lastAuthHasSession === null` branch) and only re-snapshots on a later\n * identity flip — exactly the behaviour we want, no suppression needed.\n */\nexport function palbeAuthAdapter(auth: PalbeAuth): AuthStateSource {\n return {\n onAuthStateChange(callback) {\n const unsubscribe = auth.onAuthStateChange(() => {\n // Token truth, not state.status: a restored session (page reload) has\n // no cachedUser yet, so snapshotState() reports signedOut while\n // isSignedIn is true. Using the token-manager directly ensures the pool\n // baseline matches the actual session presence on the first fire.\n const signedIn = auth.isSignedIn;\n callback(signedIn ? 'SIGNED_IN' : 'SIGNED_OUT', signedIn ? { signedIn } : null);\n });\n return { data: { subscription: { unsubscribe } } };\n },\n };\n}\n\n/**\n * Server-side pool seams: no storage, no visibility, and — because polling is\n * disabled via `pollIntervalMs: 0` — an interval seam that must never be\n * reached. Throwing keeps a future regression loud instead of leaking timers\n * into RSC/route-handler processes.\n */\nfunction serverPoolEnv(): PoolEnv {\n return {\n now: () => Date.now(),\n setInterval: () => {\n throw new Error('palbe flags: polling is disabled server-side');\n },\n clearInterval: () => {},\n storage: null,\n visibility: null,\n };\n}\n\n/** Object.is for primitives; objects/arrays by JSON text. Snapshots are\n * re-frozen NEW references on every re-snapshot, so reference equality would\n * report structurally-identical values as \"changed\". JSON compare is\n * key-order-sensitive — a server that reorders keys without changing values\n * may fire a spurious per-key callback (accepted tradeoff, documented). */\nfunction sameFlagValue(a: FlagValue | undefined, b: FlagValue | undefined): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {\n return JSON.stringify(a) === JSON.stringify(b);\n }\n return false;\n}\n\n/**\n * `pb.flags` — iOS-parity facade over `FlagsPool` (cache + delta polling +\n * auth binding) and `FlagsClient` (stateless transport).\n *\n * Start semantics: the facade is constructed lazily (first `pb.flags` touch).\n * - Browser: the pool starts right here in the constructor — cold snapshot +\n * 30s delta polling + visibility pause. \"First use starts the machinery\"\n * without per-method start checks.\n * - Server (no `document`): NEVER auto-starts. `ready()`/`refresh()` are\n * one-shot snapshot fetches with zero timers and zero storage access, so a\n * pbServer request that reads flags pays exactly one HTTP call.\n *\n * localStorage persistence is scoped per endpoint ref (`palbe.flags.<ref>`),\n * mirroring the session-storage key convention.\n */\nexport class PalbeFlags {\n private readonly transport: FlagsClient;\n private readonly pool: FlagsPool;\n\n constructor(rt: PalbeRuntime) {\n this.transport = new FlagsClient(rt.http);\n const browser = typeof document !== 'undefined';\n const ref = endpointRefFromApiKey(rt.config.apiKey);\n const options: FlagsPoolOptions = {\n auth: palbeAuthAdapter(rt.auth),\n ...(ref ? { storageKey: `palbe.flags.${ref}` } : {}),\n ...(browser ? {} : { pollIntervalMs: 0, env: serverPoolEnv() }),\n };\n this.pool = new FlagsPool(this.transport, options);\n if (browser) this.pool.start();\n }\n\n /** Resolves once the first snapshot (or persisted hydrate) is available. Auto-starts the pool. */\n ready(): Promise<void> {\n return this.pool.ready();\n }\n\n /** Force an immediate re-snapshot. */\n refresh(): Promise<void> {\n return this.pool.refresh();\n }\n\n /** Frozen snapshot of all cached values (identity-stable until a change). */\n all(): FlagsView {\n return this.pool.all();\n }\n\n /** Raw cached value for `key`, or `undefined` when not in the cache. */\n get(key: string): FlagValue | undefined {\n return this.pool.all()[key];\n }\n\n /** `true` only when the cached value is strictly `true`; `fallback` when the key is absent. */\n isEnabled(key: string, fallback = false): boolean {\n return this.pool.isEnabled(key, fallback);\n }\n\n /** Alias of {@link isEnabled} (iOS parity). */\n bool(key: string, fallback = false): boolean {\n return this.isEnabled(key, fallback);\n }\n\n /** Cached value when it is a string, else `fallback`. */\n getString(key: string, fallback: string): string {\n const value = this.pool.all()[key];\n return typeof value === 'string' ? value : fallback;\n }\n\n /** Cached value when it is an integer number, else `fallback`. */\n getInt(key: string, fallback: number): number {\n const value = this.pool.all()[key];\n return typeof value === 'number' && Number.isInteger(value) ? value : fallback;\n }\n\n /** Cached value when it is a number (integers included), else `fallback`. */\n getDouble(key: string, fallback: number): number {\n const value = this.pool.all()[key];\n return typeof value === 'number' ? value : fallback;\n }\n\n /**\n * Resolve the multivariate variant for `key` — the variant name, or `null`\n * when the flag has no variant (or the read fails).\n *\n * Wire failures (network errors, 404, etc.) resolve to `null`.\n * Invalid flag names throw `BackendError('validation', { code: 'invalid_flag_name' })`.\n *\n * DEVIATION from iOS sync-cache parity: the platform does not propagate\n * variant metadata into the user-flags snapshot/delta cache, so this is an\n * ASYNC transport read (`GET /v1/flags/{key}/variant`), not a cache lookup.\n */\n async getVariant(key: string): Promise<string | null> {\n let res: PalbaseResponse<FlagVariant>;\n try {\n res = await this.transport.getVariant(key);\n } catch (err) {\n // FlagsClient throws synchronously for invalid flag names; map to a\n // typed validation error so callers can distinguish \"bad name\" from\n // \"no variant\" (which resolves null).\n throw new BackendError('validation', {\n code: 'invalid_flag_name',\n message: err instanceof Error ? err.message : String(err),\n });\n }\n return res.data?.name ?? null;\n }\n\n /** Subscribe to any change in the cached flag set. */\n onChange(callback: () => void): Unsubscribe {\n return this.pool.onChange(callback);\n }\n\n /**\n * Observe ONE key: fires only when that key's value actually changes\n * (per {@link sameFlagValue} — structural compare for objects), with the\n * new value (`undefined` = deleted). P5 React-hook substrate.\n */\n subscribeKey(key: string, callback: (value: FlagValue | undefined) => void): Unsubscribe {\n let last = this.pool.all()[key];\n return this.pool.onChange(() => {\n const next = this.pool.all()[key];\n if (sameFlagValue(last, next)) return;\n last = next;\n callback(next);\n });\n }\n\n /**\n * Async iteration over flag changes: yields the new {@link all} view on\n * every pool change notification. The listener is detached when the\n * consumer `break`s/`return`s/`throw`s — including while a `next()` is\n * still pending (it resolves `{ done: true }` instead of hanging, which a\n * plain async-generator `finally` would not guarantee).\n */\n changes(): AsyncIterableIterator<FlagsView> {\n const queue: FlagsView[] = [];\n const pending: Array<(result: IteratorResult<FlagsView, undefined>) => void> = [];\n let finished = false;\n const unsubscribe = this.pool.onChange(() => {\n const snapshot = this.pool.all();\n const resolve = pending.shift();\n if (resolve) resolve({ value: snapshot, done: false });\n else queue.push(snapshot);\n });\n const finish = (): void => {\n if (finished) return;\n finished = true;\n unsubscribe();\n while (pending.length > 0) pending.shift()?.({ value: undefined, done: true });\n };\n return {\n next: (): Promise<IteratorResult<FlagsView, undefined>> => {\n if (finished) return Promise.resolve({ value: undefined, done: true });\n const head = queue.shift();\n if (head !== undefined) return Promise.resolve({ value: head, done: false });\n return new Promise((resolve) => {\n pending.push(resolve);\n });\n },\n return: (): Promise<IteratorResult<FlagsView, undefined>> => {\n finish();\n return Promise.resolve({ value: undefined, done: true });\n },\n throw: (error?: unknown): Promise<IteratorResult<FlagsView, undefined>> => {\n finish();\n return Promise.reject(error);\n },\n [Symbol.asyncIterator](): AsyncIterableIterator<FlagsView> {\n return this;\n },\n };\n }\n\n /** Stop polling and detach all listeners (auth + visibility + subscribers). */\n destroy(): void {\n this.pool.destroy();\n }\n}\n","// Anonymous-token minting for the realtime WebSocket — a port of the iOS\n// `AnonTokenProvider` actor (palbackend-ios-src Sources/Palbe/Realtime/\n// AnonTokenProvider.swift).\n//\n// Why this exists: Realtime validates every connection's token against the\n// tenant's JWKS (palauth's ES256 signing key). A signed-in user already has a\n// palauth-issued Bearer that validates there. An anonymous caller only holds\n// the project's publishable API key (`pb_<ref>_c…`) — NOT a JWT, so Realtime\n// rejects it with MalformedJWT. `POST /auth/anonymous` mints the missing\n// piece: a role=anon ES256 JWT signed by the same key Realtime trusts.\n//\n// Deliberately SEPARATE from TokenManager (which owns the user-session\n// lifecycle and auth-state events): an anonymous token has no user, no\n// refresh token, and emits no auth events. The provider memoises one token\n// and re-mints it shortly before expiry.\n\nimport { BackendError, fromEnvelope } from '../errors.js';\nimport type { PalbeRuntime } from '../runtime.js';\n\ninterface AnonToken {\n accessToken: string;\n expiresAt: number; // epoch ms\n}\n\n/** Re-mint this long before real expiry so a long-lived socket never presents\n * an expired token on reconnect/join (iOS refreshSkew = 60s). */\nconst REFRESH_SKEW_MS = 60_000;\n\nexport class AnonTokenProvider {\n private readonly rt: PalbeRuntime;\n private cached: AnonToken | null = null;\n private inFlight: Promise<string> | null = null;\n\n constructor(rt: PalbeRuntime) {\n this.rt = rt;\n }\n\n /**\n * Return a valid anonymous access token, minting (or re-minting) one when\n * the cache is empty or within the skew of expiry. Concurrent callers\n * collapse onto a single in-flight mint.\n */\n token(): Promise<string> {\n if (this.cached && Date.now() < this.cached.expiresAt - REFRESH_SKEW_MS) {\n return Promise.resolve(this.cached.accessToken);\n }\n if (this.inFlight) return this.inFlight;\n const mint = this.mint().then(\n (token) => {\n this.cached = token;\n this.inFlight = null;\n return token.accessToken;\n },\n (e: unknown) => {\n this.inFlight = null; // a failed mint must not poison future attempts\n throw e;\n },\n );\n this.inFlight = mint;\n return mint;\n }\n\n /**\n * Raw fetch, deliberately NOT through HttpClient/palbeRequest: the mint\n * must carry the project apikey and NEVER a Bearer (iOS keeps\n * `/auth/anonymous` on the unauthenticated-path list), and HttpClient's\n * pre-flight would try to refresh a stale session before an anon mint —\n * exactly the entanglement this provider exists to avoid.\n */\n private async mint(): Promise<AnonToken> {\n const base = this.rt.config.url.replace(/\\/+$/, '');\n let response: Response;\n try {\n response = await fetch(`${base}/auth/anonymous`, {\n method: 'POST',\n headers: { apikey: this.rt.config.apiKey },\n });\n } catch (e) {\n throw new BackendError('network', {\n code: 'network_error',\n message: e instanceof Error ? e.message : 'Network request failed',\n });\n }\n const body: unknown = await response.json().catch(() => null);\n if (!response.ok) throw fromEnvelope(response.status, body);\n // Wire shape: { access_token, token_type: \"Bearer\", expires_in: seconds }.\n const obj = (typeof body === 'object' && body !== null ? body : {}) as Record<string, unknown>;\n const accessToken = obj.access_token;\n const expiresIn = obj.expires_in;\n if (typeof accessToken !== 'string' || accessToken === '' || typeof expiresIn !== 'number') {\n throw new BackendError('decode', {\n code: 'decode_error',\n message:\n 'POST /auth/anonymous returned an unexpected shape (expected {access_token, expires_in}).',\n });\n }\n return { accessToken, expiresAt: Date.now() + expiresIn * 1000 };\n }\n}\n\n/**\n * The realtime connection's token closure (iOS PalBackend.swift parity):\n * 1. the signed-in user Bearer when present and unexpired (user-override\n * flag topics + private channels authorize);\n * 2. else an anonymous palauth JWT (role=anon);\n * 3. else — ONLY when the mint fails — the bare project apikey. Realtime\n * would reject it, but it keeps the closure total: the connect proceeds,\n * fails, and the backoff loop retries the mint on the next attempt.\n */\nexport function realtimeTokenProvider(\n rt: PalbeRuntime,\n anon: AnonTokenProvider,\n): () => Promise<string> {\n return async () => {\n const bearer = rt.tokenManager.getAccessToken();\n if (bearer && !rt.tokenManager.isExpired()) return bearer;\n try {\n return await anon.token();\n } catch {\n return rt.config.apiKey;\n }\n };\n}\n","// Phoenix Channels v2 frame codec — the realtime wire format.\n//\n// Every shape here is byte-locked to the iOS encoder (palbackend-ios-src\n// Sources/Palbe/Realtime/RealtimeConnection.swift), the proven-live ground\n// truth against the platform's Supabase Realtime (Elixir/Phoenix):\n//\n// join: [join_ref, ref, \"realtime:<topic>\", \"phx_join\", {access_token, config}]\n// reply: [join_ref, ref, \"realtime:<topic>\", \"phx_reply\", {status, response}]\n// heartbeat: [null, ref, \"phoenix\", \"heartbeat\", {}]\n// leave: [null, ref, \"realtime:<topic>\", \"phx_leave\", {}]\n// broadcast: [join_ref, ref, \"realtime:<topic>\", \"broadcast\", {type, event, payload}]\n//\n// Refs are stringified monotonic ints. Topics are bare sub-topics in the API\n// (\"room:42\"); the codec adds the \"realtime:\" prefix on the way out and strips\n// it on the way in. Goldens: __tests__/realtime-frames.test.ts.\n\nexport const REALTIME_PREFIX = 'realtime:';\n\n/** A decoded Phoenix v2 frame. Only topic/event/payload matter for routing. */\nexport interface PhoenixFrame {\n joinRef: unknown;\n ref: unknown;\n topic: string;\n event: string;\n payload: unknown;\n}\n\n/** An inbound broadcast routed to channel handlers: bare topic + inner event/payload. */\nexport interface RealtimeInbound {\n topic: string;\n event: string;\n payload: Record<string, unknown>;\n}\n\n/**\n * Join a topic. join_ref = ref (iOS sends the same ref in both slots). The\n * config mirrors iOS exactly: broadcast self=false, no presence key, public\n * channel, no postgres_changes.\n */\nexport function encodeJoin(ref: number, topic: string, accessToken: string): string {\n const r = String(ref);\n return JSON.stringify([\n r,\n r,\n REALTIME_PREFIX + topic,\n 'phx_join',\n {\n access_token: accessToken,\n config: {\n broadcast: { self: false },\n presence: { key: '' },\n private: false,\n postgres_changes: [],\n },\n },\n ]);\n}\n\nexport function encodeLeave(ref: number, topic: string): string {\n return JSON.stringify([null, String(ref), REALTIME_PREFIX + topic, 'phx_leave', {}]);\n}\n\nexport function encodeHeartbeat(ref: number): string {\n return JSON.stringify([null, String(ref), 'phoenix', 'heartbeat', {}]);\n}\n\n/**\n * Client-side broadcast push (web addition — iOS is receive-only). The payload\n * envelope {type:\"broadcast\", event, payload} matches what the server fans out\n * to other subscribers (Realtime's BroadcastHandler re-broadcasts it verbatim),\n * so receivers unwrap pushes from peers and from the backend identically.\n * joinRef is the topic's CURRENT join ref — Phoenix drops channel messages\n * whose join_ref does not match the live channel instance.\n */\nexport function encodeBroadcast(\n joinRef: string,\n ref: number,\n topic: string,\n event: string,\n payload: Record<string, unknown>,\n): string {\n return JSON.stringify([\n joinRef,\n String(ref),\n REALTIME_PREFIX + topic,\n 'broadcast',\n { type: 'broadcast', event, payload },\n ]);\n}\n\n/** Decode a Phoenix v2 frame; null when the text is not a valid 5-slot array. */\nexport function decodeFrame(text: string): PhoenixFrame | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return null;\n }\n if (!Array.isArray(parsed)) return null;\n const [joinRef, ref, topic, event, payload] = parsed as unknown[];\n if (typeof topic !== 'string' || typeof event !== 'string') return null;\n return { joinRef, ref, topic, event, payload };\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | null {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return null;\n return value as Record<string, unknown>;\n}\n\n/**\n * Route an inbound frame: only `broadcast` events carry channel traffic\n * (replies/acks/presence are connection bookkeeping — iOS ignores them the\n * same way; reconnect re-joins every topic, so socket health drives recovery).\n * Missing inner event defaults to '' and a non-object payload to {} — exact\n * iOS decoder behavior.\n */\nexport function unwrapBroadcast(frame: PhoenixFrame): RealtimeInbound | null {\n if (frame.event !== 'broadcast') return null;\n const body = asObject(frame.payload);\n if (!body) return null;\n const event = typeof body.event === 'string' ? body.event : '';\n const payload = asObject(body.payload) ?? {};\n return { topic: stripRealtimePrefix(frame.topic), event, payload };\n}\n\nexport function stripRealtimePrefix(topic: string): string {\n return topic.startsWith(REALTIME_PREFIX) ? topic.slice(REALTIME_PREFIX.length) : topic;\n}\n","// The Phoenix-Channels WebSocket transport for Palbase Realtime — a port of\n// the proven-live iOS `RealtimeConnection` actor (palbackend-ios-src\n// Sources/Palbe/Realtime/RealtimeConnection.swift), the wire ground truth\n// against the platform's Supabase Realtime (Elixir/Phoenix):\n//\n// connect: wss://<host>/realtime/v1/websocket?apikey=<jwt>&vsn=2.0.0\n// join: [join_ref, ref, \"realtime:<topic>\", \"phx_join\", {access_token, config}]\n// heartbeat: [null, ref, \"phoenix\", \"heartbeat\", {}] (~every 25s)\n// recv: [null, null, \"realtime:<topic>\", \"broadcast\", {type, event, payload}]\n//\n// ONE socket is shared by every subscribed channel (the Supabase model —\n// multiplex all topics over a single connection). This class is a generic\n// frame pump; the facade (facade.ts) owns per-channel handlers and refcounts.\n//\n// DEVIATION from iOS (documented): iOS sends the auth token in the\n// `x-api-key` HTTP header on the WS upgrade. Browsers cannot set headers on\n// a WebSocket handshake, so the web client uses the server's query fallback —\n// Realtime's user_socket.ex reads `x-api-key` first, then `params[\"apikey\"]`.\n//\n// Resilience mirrors iOS: a dropped socket reconnects with exponential\n// backoff (1,2,4…s capped at 30s + deterministic ref jitter); on reconnect\n// every previously-joined topic is re-joined with a FRESH token and fresh\n// refs. The 25s heartbeat keeps Kong's idle WS timeout (180s) from closing\n// the connection. One behavioral adaptation: iOS promotes to `connected` on\n// the first RECEIVED frame because `URLSessionWebSocketTask.resume()` does\n// not signal handshake completion — a browser WebSocket's `open` event fires\n// only after a successful HTTP-101 handshake, so promotion happens there.\n\nimport {\n decodeFrame,\n encodeBroadcast,\n encodeHeartbeat,\n encodeJoin,\n encodeLeave,\n type RealtimeInbound,\n stripRealtimePrefix,\n unwrapBroadcast,\n} from './frames.js';\n\n/**\n * The connection state of the shared realtime WebSocket (iOS enum parity), plus\n * `'error'` — surfaced when a channel could not be recovered after a\n * token-expiry close exhausted its rejoin attempts (the socket itself may still\n * be up; the app should stop expecting events on the dead channel).\n */\nexport type RealtimeConnectionState = 'idle' | 'connected' | 'reconnecting' | 'error';\n\n/** Connection-level callbacks the owner (the realtime facade) installs once. */\nexport interface RealtimeSocketHandlers {\n /** A broadcast arrived for a topic (bare sub-topic, inner event/payload). */\n onInbound: (inbound: RealtimeInbound) => void;\n /** The socket (re)connected — fires on EVERY successful open, first included. */\n onReconnect: () => void;\n /** The socket's connection state changed (idle/connected/reconnecting). */\n onStateChange: (state: RealtimeConnectionState) => void;\n /**\n * A single channel died for good (token-expiry rejoin exhausted N attempts).\n * The socket is still up; the owner surfaces this on the channel's status so\n * the app stops expecting events on that topic. Distinct from onStateChange,\n * which is connection-global — one bad channel must not flag the whole socket.\n */\n onChannelError: (topic: string) => void;\n}\n\n/** The structural slice of the DOM WebSocket the transport needs (injectable). */\nexport interface WebSocketLike {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number): void;\n onopen: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: (() => void) | null;\n onerror: (() => void) | null;\n}\n\nexport type WebSocketCtor = new (url: string) => WebSocketLike;\n\nexport interface RealtimeSocketOptions {\n /** https base, e.g. https://ref.dev.palbase.studio (PalbeConfig.url). */\n baseUrl: string;\n /**\n * Resolves the token used as the WS `apikey` query + per-join `access_token`.\n * Bearer when signed in, else anon JWT, else the project apikey (see\n * anon-token.ts). Re-invoked on every connect AND every join, so a token\n * rotated while offline is picked up by the next reconnect.\n */\n tokenProvider: () => Promise<string>;\n /** Injectable for non-DOM hosts; defaults to globalThis.WebSocket, resolved per connect. */\n webSocket?: WebSocketCtor;\n /** Heartbeat cadence; default 25s (under Kong's 180s idle WS cap). */\n heartbeatIntervalMs?: number;\n /** Reconnect backoff cap; default 30s. */\n maxBackoffSeconds?: number;\n}\n\nconst WS_OPEN = 1;\nconst DEFAULT_HEARTBEAT_MS = 25_000;\nconst DEFAULT_MAX_BACKOFF_SECONDS = 30;\n// How many times a single channel may be rejoined after a token-expiry close\n// before we give up on it (the rejoin-storm guard). A genuinely-bad token (not\n// just expired) would close every rejoin immediately; capping turns that into a\n// surfaced channel error instead of a tight loop. The first rejoin is attempt 0.\nconst MAX_CHANNEL_REJOIN_ATTEMPTS = 3;\n\n/**\n * Build the Phoenix WS URL: https→wss (http→ws), trailing slashes stripped,\n * Kong's `/realtime/v1/websocket` path, `apikey` + `vsn=2.0.0` query.\n * Port of iOS `websocketURL` — plus the browser apikey-query deviation\n * documented in the file header.\n */\nexport function websocketUrl(base: string, token: string): string {\n let s = base;\n if (s.startsWith('https://')) {\n s = `wss://${s.slice('https://'.length)}`;\n } else if (s.startsWith('http://')) {\n s = `ws://${s.slice('http://'.length)}`;\n }\n while (s.endsWith('/')) s = s.slice(0, -1);\n return `${s}/realtime/v1/websocket?apikey=${encodeURIComponent(token)}&vsn=2.0.0`;\n}\n\n/**\n * Exponential reconnect backoff: 2^attempt seconds capped at `cap`, plus up\n * to 1s of deterministic jitter derived from the ref counter (iOS formula —\n * nondeterministic RNG is banned, and ref-based jitter is enough to de-sync\n * reconnect storms). Pure so growth/cap are unit-testable without sleeping.\n */\nexport function backoffDelaySeconds(attempt: number, refCounter: number, cap: number): number {\n const base = Math.min(2 ** attempt, cap);\n const jitter = (refCounter % 1000) / 1000;\n return base + jitter;\n}\n\ninterface PendingBroadcast {\n topic: string;\n event: string;\n payload: Record<string, unknown>;\n}\n\nexport class RealtimeSocket {\n private readonly options: RealtimeSocketOptions;\n private handlers: RealtimeSocketHandlers | null = null;\n\n private ws: WebSocketLike | null = null;\n // Membership intent (bare sub-topics): re-joined on every (re)connect. A\n // topic stays in this set from joinTopic() until leaveTopic().\n private readonly joinedTopics = new Set<string>();\n // topic → join_ref of the LIVE join on the current socket. Phoenix drops\n // channel messages whose join_ref doesn't match, so broadcasts carry this.\n // Cleared on disconnect (the next socket gets fresh joins/refs).\n private readonly joinRefs = new Map<string, string>();\n // Broadcasts pushed before the topic's join is live — flushed right after it.\n private pendingBroadcasts: PendingBroadcast[] = [];\n // Per-topic rejoin attempt counter for the token-expiry channel-death guard.\n // Bumped on every channel rejoin; reset to 0 when the topic delivers a live\n // broadcast (proof the channel recovered). At MAX_CHANNEL_REJOIN_ATTEMPTS the\n // channel is given up on (onChannelError) instead of looping forever.\n private readonly channelRejoinAttempts = new Map<string, number>();\n // Pending per-topic rejoin backoff timers — separate from the socket-level\n // reconnectTimer so a channel backoff never clobbers socket reconnection.\n private readonly channelRejoinTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n // Monotonic Phoenix message ref. Every outbound frame carries a unique ref.\n private refCounter = 0;\n private connected = false;\n private started = false;\n private stopped = false;\n // Reset ONLY on a genuine open, so a never-opening socket walks\n // attempt 0,1,2,… → delays 1,2,4,…,cap instead of hammering (iOS parity).\n private reconnectAttempt = 0;\n // Invalidates async continuations (token awaits) from a superseded connect.\n private connectEpoch = 0;\n\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(options: RealtimeSocketOptions) {\n this.options = options;\n }\n\n // MARK: lifecycle\n\n /** Install handlers and open the socket. Idempotent while started. */\n start(handlers: RealtimeSocketHandlers): void {\n this.handlers = handlers;\n if (this.started) return;\n this.started = true;\n this.stopped = false;\n void this.connect();\n }\n\n /** Tear down the socket, stop reconnecting, emit `idle`. Idempotent. */\n stop(): void {\n this.stopped = true;\n this.started = false;\n this.connected = false;\n this.connectEpoch += 1; // invalidate in-flight connect continuations\n this.stopHeartbeat();\n if (this.reconnectTimer !== null) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.clearAllChannelRejoins();\n const ws = this.ws;\n this.ws = null;\n this.joinRefs.clear();\n ws?.close(1000);\n this.handlers?.onStateChange('idle');\n }\n\n // MARK: topic membership\n\n /**\n * Join a topic (bare sub-topic, no \"realtime:\" prefix). Idempotent. Sent\n * immediately when the socket is up, else on the next (re)connect.\n */\n joinTopic(topic: string): void {\n if (this.joinedTopics.has(topic)) return;\n this.joinedTopics.add(topic);\n if (this.connected) void this.sendJoin(topic);\n }\n\n /**\n * Leave a topic: sends `phx_leave` when connected and always drops it from\n * the re-join set (a future reconnect won't restore it). Pending broadcasts\n * for the topic are discarded.\n */\n leaveTopic(topic: string): void {\n if (!this.joinedTopics.delete(topic)) return;\n this.joinRefs.delete(topic);\n this.pendingBroadcasts = this.pendingBroadcasts.filter((p) => p.topic !== topic);\n this.clearChannelRejoin(topic); // cancel any pending token-expiry rejoin\n if (this.isOpen()) this.send(encodeLeave(this.nextRef(), topic));\n }\n\n /**\n * Push a broadcast onto a topic (web addition — iOS is receive-only). Sent\n * synchronously when the topic's join is live on the current socket;\n * otherwise queued and flushed right after the join goes out.\n */\n sendBroadcast(topic: string, event: string, payload: Record<string, unknown>): void {\n const joinRef = this.joinRefs.get(topic);\n if (joinRef !== undefined && this.isOpen()) {\n this.send(encodeBroadcast(joinRef, this.nextRef(), topic, event, payload));\n } else {\n this.pendingBroadcasts.push({ topic, event, payload });\n }\n }\n\n // MARK: connect / reconnect\n\n private async connect(): Promise<void> {\n if (this.stopped) return;\n const epoch = ++this.connectEpoch;\n let token: string;\n try {\n token = await this.options.tokenProvider();\n } catch {\n // The provider is total by design (apikey fallback) — but a throw must\n // not kill the reconnect loop.\n this.scheduleReconnect();\n return;\n }\n if (this.stopped || epoch !== this.connectEpoch) return;\n\n // Resolved per attempt (not cached) so an environment that gains a\n // WebSocket later (test stubs, polyfill load order) recovers on retry.\n const Ctor = this.options.webSocket ?? (globalThis as { WebSocket?: WebSocketCtor }).WebSocket;\n if (!Ctor) {\n this.scheduleReconnect();\n return;\n }\n\n const ws = new Ctor(websocketUrl(this.options.baseUrl, token));\n this.ws = ws;\n // Every callback gates on `ws === this.ws` so events from a superseded\n // socket (replaced during reconnect) can't corrupt the live one's state.\n ws.onopen = () => {\n if (ws === this.ws) void this.handleOpen();\n };\n ws.onmessage = (event) => {\n if (ws === this.ws) this.handleMessage(event.data);\n };\n ws.onclose = () => {\n if (ws === this.ws) this.handleClose();\n };\n // Browsers always follow `error` with `close` — onclose drives recovery.\n ws.onerror = () => {};\n }\n\n /**\n * The browser `open` event proves the HTTP-101 handshake completed (the\n * analogue of iOS promoteToConnected): flip connected, announce, then\n * (re)join every topic with a fresh token + fresh refs. Backoff is NOT\n * reset here — it resets on the first inbound frame (iOS parity), so a\n * server that opens but immediately closes still grows the backoff.\n */\n private async handleOpen(): Promise<void> {\n if (this.stopped) return;\n this.connected = true;\n this.handlers?.onStateChange('connected');\n this.handlers?.onReconnect();\n this.startHeartbeat();\n for (const topic of this.joinedTopics) {\n await this.sendJoin(topic);\n }\n }\n\n private handleClose(): void {\n if (this.stopped) return;\n this.ws = null;\n this.joinRefs.clear();\n // The socket reconnect rejoins ALL topics fresh — drop per-channel rejoin\n // state so a pending channel backoff doesn't race the rejoin-all.\n this.clearAllChannelRejoins();\n this.stopHeartbeat();\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.stopped || !this.started) return;\n this.connected = false;\n this.handlers?.onStateChange('reconnecting');\n const attempt = this.reconnectAttempt;\n this.reconnectAttempt += 1;\n const cap = this.options.maxBackoffSeconds ?? DEFAULT_MAX_BACKOFF_SECONDS;\n const delaySeconds = backoffDelaySeconds(attempt, this.refCounter, cap);\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n void this.connect();\n }, delaySeconds * 1000);\n }\n\n // MARK: frames out\n\n /**\n * Join carries a freshly-resolved token (iOS parity — `sendJoin` awaits the\n * provider), so a Bearer rotated while offline rides the next rejoin.\n */\n private async sendJoin(topic: string): Promise<void> {\n const token = await this.options.tokenProvider();\n // Re-validate after the await: the socket may have dropped or the topic\n // may have been left while the token resolved.\n if (!this.isOpen() || !this.joinedTopics.has(topic)) return;\n const ref = this.nextRef();\n this.joinRefs.set(topic, String(ref));\n this.send(encodeJoin(ref, topic, token));\n this.flushPending(topic);\n }\n\n private flushPending(topic: string): void {\n const ready = this.pendingBroadcasts.filter((p) => p.topic === topic);\n if (ready.length === 0) return;\n this.pendingBroadcasts = this.pendingBroadcasts.filter((p) => p.topic !== topic);\n for (const p of ready) this.sendBroadcast(p.topic, p.event, p.payload);\n }\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n const interval = this.options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS;\n this.heartbeatTimer = setInterval(() => {\n if (this.isOpen()) this.send(encodeHeartbeat(this.nextRef()));\n }, interval);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer !== null) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n /** Drop-if-not-open: a send racing a disconnect is recovered by the rejoin-all on reconnect. */\n private send(text: string): void {\n if (this.ws && this.ws.readyState === WS_OPEN) this.ws.send(text);\n }\n\n // MARK: frames in\n\n private handleMessage(data: unknown): void {\n if (typeof data !== 'string') return;\n const frame = decodeFrame(data);\n if (!frame) return;\n // Any valid inbound frame proves the server is actually delivering data\n // (not just completing the TCP handshake). Reset backoff here — iOS parity:\n // iOS promotes to connected on the first frame because URLSession does not\n // surface a separate handshake-complete signal. A server that opens then\n // immediately closes does NOT trigger this, so backoff keeps growing.\n this.reconnectAttempt = 0;\n // Token-expiry channel death (the cross-SDK P4 bug): at JWT TTL the server\n // kills just THIS channel (`{:stop, :normal}`) while the socket stays up\n // (heartbeats hold it past Kong's idle cap). Phoenix v2 pushes a per-topic\n // phx_close carrying the channel's join_ref ([<joinRef>,null,topic,\"phx_close\",{}];\n // phx_error on a crash). We rejoin that one topic with a FRESH token rather\n // than letting it silently die. Socket-level reconnect rejoins ALL topics —\n // this handles the case where only ONE channel was killed.\n if (frame.event === 'phx_close' || frame.event === 'phx_error') {\n this.handleChannelClose(frame);\n return;\n }\n // Only `broadcast` events carry channel traffic; replies/acks/presence\n // are bookkeeping (iOS ignores them identically).\n const inbound = unwrapBroadcast(frame);\n if (inbound) {\n // A live broadcast proves the channel recovered — reset its rejoin guard.\n this.channelRejoinAttempts.delete(inbound.topic);\n this.handlers?.onInbound(inbound);\n }\n }\n\n /**\n * Recover (or give up on) a single channel the server closed for token\n * reasons. Ignored unless the close targets a topic we still intend to be\n * joined AND its join_ref matches the LIVE join — so a stale close from a\n * superseded join, or a close arriving after our OWN phx_leave (the topic is\n * already dropped from joinedTopics/joinRefs), does NOT trigger a rejoin.\n */\n private handleChannelClose(frame: { topic: string; joinRef: unknown }): void {\n const topic = stripRealtimePrefix(frame.topic);\n // Intentional-leave / stale-close guard: leaveTopic() removes the topic\n // from joinedTopics and its joinRefs entry, so neither check passes here.\n if (!this.joinedTopics.has(topic)) return;\n if (this.joinRefs.get(topic) !== frame.joinRef) return;\n // The current join is dead; drop its ref so a queued send won't use it.\n this.joinRefs.delete(topic);\n\n const attempt = this.channelRejoinAttempts.get(topic) ?? 0;\n if (attempt >= MAX_CHANNEL_REJOIN_ATTEMPTS) {\n // A genuinely-bad token (not just expired) closes every rejoin at once —\n // give up on this channel instead of tight-looping.\n this.joinedTopics.delete(topic);\n this.channelRejoinAttempts.delete(topic);\n this.handlers?.onChannelError(topic);\n return;\n }\n this.channelRejoinAttempts.set(topic, attempt + 1);\n if (attempt === 0) {\n // First close: rejoin immediately (the common case — token simply expired).\n void this.sendJoin(topic);\n return;\n }\n // Repeated closes: back off (reuse the socket reconnect curve) so a flapping\n // channel doesn't hammer the server. Replaces any pending rejoin for the topic.\n const cap = this.options.maxBackoffSeconds ?? DEFAULT_MAX_BACKOFF_SECONDS;\n const delayMs = backoffDelaySeconds(attempt, this.refCounter, cap) * 1000;\n const existing = this.channelRejoinTimers.get(topic);\n if (existing !== undefined) clearTimeout(existing);\n const timer = setTimeout(() => {\n this.channelRejoinTimers.delete(topic);\n if (this.joinedTopics.has(topic)) void this.sendJoin(topic);\n }, delayMs);\n this.channelRejoinTimers.set(topic, timer);\n }\n\n // MARK: helpers\n\n /** Drop a single topic's token-expiry rejoin state (cancel its pending timer). */\n private clearChannelRejoin(topic: string): void {\n const timer = this.channelRejoinTimers.get(topic);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.channelRejoinTimers.delete(topic);\n }\n this.channelRejoinAttempts.delete(topic);\n }\n\n /** Drop all per-channel rejoin state (socket teardown / full reconnect). */\n private clearAllChannelRejoins(): void {\n for (const timer of this.channelRejoinTimers.values()) clearTimeout(timer);\n this.channelRejoinTimers.clear();\n this.channelRejoinAttempts.clear();\n }\n\n private isOpen(): boolean {\n return this.connected && this.ws !== null && this.ws.readyState === WS_OPEN;\n }\n\n private nextRef(): number {\n this.refCounter += 1;\n return this.refCounter;\n }\n}\n","// `pb.realtime` — the public, customer-facing realtime surface. Mirrors the\n// iOS `Facade+Realtime` + `RealtimeClient` pair (palbackend-ios-src\n// Sources/Palbe/Realtime/): a channel/handler manager multiplexing every\n// subscription over ONE shared, auto-reconnecting WebSocket (connection.ts).\n//\n// const channel = pb.realtime.channel('room:42');\n// const sub = channel.on('message', (payload) => { ... });\n// channel.send('message', { text: 'hi' }); // web addition — iOS is receive-only\n// // later: sub.cancel() (last cancel on the channel → phx_leave)\n//\n// Channel names are app-defined bare sub-topics — do NOT prefix with\n// \"realtime:\" (that's internal to the transport). `realtime` is a reserved\n// namespace: a backend endpoint cannot be named `realtime` (namespaces.ts\n// FIXED_SURFACE, alongside auth/flags).\n//\n// Realtime is CLIENT-ONLY: on a host with no WebSocket (SSR/RSC/route\n// handlers), `channel()` throws a guided validation error. Reading\n// `status` is always safe (it just reports `idle`).\n\nimport type { Unsubscribe } from '../auth-facade.js';\nimport { BackendError } from '../errors.js';\nimport type { PalbeRuntime } from '../runtime.js';\nimport { AnonTokenProvider, realtimeTokenProvider } from './anon-token.js';\nimport { type RealtimeConnectionState, RealtimeSocket } from './connection.js';\nimport type { RealtimeInbound } from './frames.js';\n\nexport type { RealtimeConnectionState } from './connection.js';\n\n/** An inbound broadcast payload (the inner `payload` of the Phoenix envelope). */\nexport type RealtimePayload = Record<string, unknown>;\nexport type RealtimeHandler = (payload: RealtimePayload) => void;\n\n/** A cancellable realtime subscription. `cancel()` removes the handler and,\n * when it was the channel's last, leaves the channel on the shared socket.\n * Cancelling twice is a no-op. */\nexport interface RealtimeSubscription {\n cancel(): void;\n}\n\nexport interface RealtimeStatusSnapshot {\n state: RealtimeConnectionState;\n lastEventAt: Date | null;\n}\n\n/**\n * The observable connection status (`pb.realtime.status`) — the plain-JS\n * analogue of the iOS `@Observable RealtimeStatusStore`. `state` drives a\n * live \"connected\" badge; `lastEventAt` proves a push actually arrived.\n * `onChange` fires on state transitions and on every recorded event.\n */\nexport interface RealtimeStatus {\n readonly state: RealtimeConnectionState;\n readonly lastEventAt: Date | null;\n onChange(callback: (status: RealtimeStatusSnapshot) => void): Unsubscribe;\n}\n\nclass StatusStore implements RealtimeStatus {\n private currentState: RealtimeConnectionState = 'idle';\n private lastEvent: Date | null = null;\n private readonly listeners = new Set<(status: RealtimeStatusSnapshot) => void>();\n\n get state(): RealtimeConnectionState {\n return this.currentState;\n }\n\n get lastEventAt(): Date | null {\n return this.lastEvent;\n }\n\n onChange(callback: (status: RealtimeStatusSnapshot) => void): Unsubscribe {\n this.listeners.add(callback);\n return () => {\n this.listeners.delete(callback);\n };\n }\n\n /** No-op on equal state (iOS setState guard) — listeners see transitions only. */\n setState(state: RealtimeConnectionState): void {\n if (state === this.currentState) return;\n this.currentState = state;\n this.emit();\n }\n\n recordEvent(at: Date): void {\n this.lastEvent = at;\n this.emit();\n }\n\n private emit(): void {\n const snapshot: RealtimeStatusSnapshot = {\n state: this.currentState,\n lastEventAt: this.lastEvent,\n };\n for (const listener of this.listeners) listener(snapshot);\n }\n}\n\ninterface HandlerEntry {\n topic: string;\n event: string;\n handler: RealtimeHandler;\n}\n\n/**\n * A subscription handle for one realtime channel (returned by\n * `pb.realtime.channel(name)` — the SAME instance per name). A channel is\n * joined on the shared socket when its first handler is added (or on the\n * first `send`) and left when its last handler cancels.\n */\nexport class RealtimeChannel {\n /** The app-defined channel name (bare sub-topic, no \"realtime:\" prefix). */\n readonly name: string;\n private readonly owner: PalbeRealtime;\n\n /** @internal — obtain channels via `pb.realtime.channel(name)`. */\n constructor(name: string, owner: PalbeRealtime) {\n this.name = name;\n this.owner = owner;\n }\n\n /**\n * Subscribe `handler` to `event` on this channel. Fire-and-forget: the\n * join rides the shared socket asynchronously. Hold the returned\n * subscription and `cancel()` it to stop.\n */\n on(event: string, handler: RealtimeHandler): RealtimeSubscription {\n return this.owner.subscribe(this.name, event, handler);\n }\n\n /**\n * Broadcast `payload` to the channel's other subscribers (web addition —\n * iOS is receive-only). Joins the channel if it isn't already; queued\n * until the join is live on the socket.\n */\n send(event: string, payload: RealtimePayload = {}): void {\n this.owner.send(this.name, event, payload);\n }\n}\n\nexport class PalbeRealtime {\n private readonly rt: PalbeRuntime;\n private socket: RealtimeSocket | null = null;\n private readonly channels = new Map<string, RealtimeChannel>();\n private readonly statusStore = new StatusStore();\n // Per-(topic, event) handlers — an event may have multiple handlers\n // (multiple .on calls); entry identity is the unsubscribe token.\n private readonly handlers = new Set<HandlerEntry>();\n // Joins are refcounted per topic so the socket only leaves a topic when\n // its last handler cancels (iOS RealtimeClient parity).\n private readonly topicRefcount = new Map<string, number>();\n\n constructor(rt: PalbeRuntime) {\n this.rt = rt;\n }\n\n /**\n * Get the handle for an app-defined channel (e.g. \"room:42\"). The same\n * name returns the SAME instance. Throws a guided error on server-side\n * hosts (SSR/RSC/Node) — realtime is client-only.\n */\n channel(name: string): RealtimeChannel {\n // Primary: detect server environment by the absence of the browser globals\n // `document` and `window`. Node ≥22 ships a global WebSocket, so the old\n // `typeof WebSocket === 'undefined'` check is no-op there — server code\n // would silently open real sockets.\n if (typeof document === 'undefined' && typeof window === 'undefined') {\n throw new BackendError('validation', {\n code: 'realtime_unavailable',\n message:\n 'pb.realtime requires a browser environment: document and window are not defined ' +\n '(server/SSR/RSC). Subscribe from browser code instead — e.g. a client component or useEffect.',\n });\n }\n // Secondary: browser polyfill may be missing even with document/window present.\n if (typeof WebSocket === 'undefined') {\n throw new BackendError('validation', {\n code: 'realtime_unavailable',\n message:\n 'pb.realtime is client-only: this environment has no WebSocket. ' +\n 'Subscribe from browser code instead — e.g. a client component or useEffect.',\n });\n }\n let channel = this.channels.get(name);\n if (!channel) {\n channel = new RealtimeChannel(name, this);\n this.channels.set(name, channel);\n }\n return channel;\n }\n\n /**\n * Tear down the shared socket and clear all channel state. Called by\n * `__configure` and `__reset` when the runtime is replaced, so old sockets\n * are not left open and heartbeat timers do not leak.\n */\n destroy(): void {\n this.socket?.stop();\n this.socket = null;\n this.channels.clear();\n }\n\n /** The observable connection status. Safe to read anywhere (reports `idle` until a socket exists). */\n get status(): RealtimeStatus {\n return this.statusStore;\n }\n\n /** @internal */\n subscribe(topic: string, event: string, handler: RealtimeHandler): RealtimeSubscription {\n const socket = this.ensureSocket();\n const entry: HandlerEntry = { topic, event, handler };\n this.handlers.add(entry);\n const count = (this.topicRefcount.get(topic) ?? 0) + 1;\n this.topicRefcount.set(topic, count);\n if (count === 1) socket.joinTopic(topic);\n\n let cancelled = false;\n return {\n cancel: () => {\n if (cancelled) return;\n cancelled = true;\n this.handlers.delete(entry);\n const next = (this.topicRefcount.get(topic) ?? 1) - 1;\n if (next <= 0) {\n this.topicRefcount.delete(topic);\n socket.leaveTopic(topic);\n } else {\n this.topicRefcount.set(topic, next);\n }\n },\n };\n }\n\n /** @internal */\n send(topic: string, event: string, payload: RealtimePayload): void {\n const socket = this.ensureSocket();\n // Phoenix only accepts pushes on joined topics. Idempotent when already\n // joined; a send-only channel stays joined until its handlers (if any\n // are ever added) all cancel.\n socket.joinTopic(topic);\n socket.sendBroadcast(topic, event, payload);\n }\n\n /** Lazily build + start the shared socket on first subscription/send. */\n private ensureSocket(): RealtimeSocket {\n if (!this.socket) {\n const anon = new AnonTokenProvider(this.rt);\n this.socket = new RealtimeSocket({\n baseUrl: this.rt.config.url,\n tokenProvider: realtimeTokenProvider(this.rt, anon),\n });\n this.socket.start({\n onInbound: (inbound) => this.route(inbound),\n // Rejoin-all already happens inside the socket; nothing to refresh\n // here (no managed flags-sync on web yet — pb.flags polls).\n onReconnect: () => {},\n onStateChange: (state) => this.statusStore.setState(state),\n onChannelError: (topic) => this.handleChannelError(topic),\n });\n }\n return this.socket;\n }\n\n private route(inbound: RealtimeInbound): void {\n // Any inbound broadcast counts as a \"live event\" (iOS parity) — even one\n // without a matching handler proves the socket is delivering.\n this.statusStore.recordEvent(new Date());\n for (const entry of this.handlers) {\n if (entry.topic === inbound.topic && entry.event === inbound.event) {\n entry.handler(inbound.payload);\n }\n }\n }\n\n /**\n * A channel died for good (token-expiry rejoin exhausted N attempts). Drop its\n * handlers + refcount so the app stops expecting events on it, and flip the\n * status observable to `'error'` (the React `useChannel` hook reports this).\n * A later `.on()` for the same topic re-joins from scratch (refcount 1).\n */\n private handleChannelError(topic: string): void {\n for (const entry of this.handlers) {\n if (entry.topic === topic) this.handlers.delete(entry);\n }\n this.topicRefcount.delete(topic);\n this.statusStore.setState('error');\n }\n}\n","/**\n * Persisted session. The refresh token is the durable credential; access\n * token + expiry ride along (when available) so a reload — or a server-side\n * cookie read — can reuse a still-fresh access token instead of burning a\n * refresh per hydration (rotation storm under SSR).\n */\nexport interface PersistedSession {\n refreshToken: string;\n accessToken?: string;\n /** ms epoch */\n expiresAt?: number;\n}\n\nexport interface SessionStorageAdapter {\n load(): PersistedSession | null;\n save(session: PersistedSession): void;\n clear(): void;\n}\n\nexport function memorySessionStorage(): SessionStorageAdapter {\n let current: PersistedSession | null = null;\n return {\n load: () => current,\n save: (s) => {\n current = s;\n },\n clear: () => {\n current = null;\n },\n };\n}\n\nconst DEFAULT_KEY = 'palbe.session';\n\nexport function localStorageSessionStorage(key: string = DEFAULT_KEY): SessionStorageAdapter {\n return {\n load: () => {\n try {\n const raw = localStorage.getItem(key);\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n if (typeof parsed === 'object' && parsed !== null) {\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.refreshToken === 'string') {\n // Optional fields are validated as a PAIR: a wrong-typed (or lone)\n // access token / expiry degrades to the legacy {refreshToken}\n // shape — never null the whole session over the optional part.\n if (typeof obj.accessToken === 'string' && typeof obj.expiresAt === 'number') {\n return {\n refreshToken: obj.refreshToken,\n accessToken: obj.accessToken,\n expiresAt: obj.expiresAt,\n };\n }\n return { refreshToken: obj.refreshToken };\n }\n }\n return null;\n } catch {\n return null;\n }\n },\n save: (s) => {\n try {\n localStorage.setItem(key, JSON.stringify(s));\n } catch {\n // quota/SSR — non-fatal: session just won't survive reload\n }\n },\n clear: () => {\n try {\n localStorage.removeItem(key);\n } catch {\n // ignore\n }\n },\n };\n}\n\n/**\n * Browser → localStorage, else memory. Pass an endpoint-scoped key\n * (`palbe.session.<endpointRef>`) — the bare default key would collide\n * between two Palbase projects served from the same origin.\n */\nexport function defaultSessionStorage(key?: string): SessionStorageAdapter {\n if (typeof localStorage !== 'undefined') return localStorageSessionStorage(key);\n return memorySessionStorage();\n}\n","// AUTO-GENERATED from package.json — do not edit\nexport const VERSION = '1.0.1';\n","import { AuthClient } from '@palbase/auth';\nimport { HttpClient, TokenManager } from '@palbase/core';\nimport { AnalyticsState, PalbeAnalytics } from './analytics-facade.js';\nimport { endpointRefFromApiKey } from './api-key.js';\nimport { PalbeAuth } from './auth-facade.js';\nimport type { PalbeConfig } from './config.js';\nimport { PalbeFlags } from './flags-facade.js';\nimport { PalbeRealtime } from './realtime/facade.js';\nimport { defaultSessionStorage, type SessionStorageAdapter } from './storage.js';\nimport { VERSION } from './version.js';\n\nexport interface PalbeRuntime {\n config: PalbeConfig;\n http: HttpClient;\n tokenManager: TokenManager;\n authClient: AuthClient;\n auth: PalbeAuth;\n flags: PalbeFlags;\n realtime: PalbeRealtime;\n analytics: PalbeAnalytics;\n storage: SessionStorageAdapter;\n /**\n * Destroy the realtime facade if it was already constructed (no-op otherwise).\n * Called by `__configure` / `__reset` when the runtime is replaced so the old\n * socket is closed and heartbeat timers do not leak.\n */\n destroyRealtime(): void;\n}\n\nexport function buildRuntime(config: PalbeConfig): PalbeRuntime {\n const http = new HttpClient(config.apiKey, {\n url: config.url,\n headers: { 'X-Client-Info': `palbe-web/${VERSION}`, ...config.headers },\n });\n const tokenManager = new TokenManager();\n http.tokenManager = tokenManager;\n const authClient = new AuthClient(http, tokenManager);\n const ref = endpointRefFromApiKey(config.apiKey);\n const storage = config.storage ?? defaultSessionStorage(ref ? `palbe.session.${ref}` : undefined);\n\n // Hydrate. Two paths:\n //\n // 1. FULL session persisted and still fresh → adopt it as a REAL session\n // (setTokens with the remaining TTL). The first request goes out with\n // the persisted Bearer and NO pre-flight refresh — this is what keeps\n // per-request server runtimes (palbe/next) from burning a token\n // rotation per request.\n //\n // 2. Legacy {refreshToken}-only shape, or an expired/invalid full session\n // → the expired-trick: the refresh token becomes an already-expired\n // session so HttpClient's pre-flight refresh runs before the first\n // real request. setTokens wires refreshFunction but sets\n // expiresAt=now+1h — we then override expiresAt to 0 so isExpired()\n // returns true immediately.\n //\n // Two implicit consequences, relied upon downstream (BOTH paths):\n // (a) setTokens marks AuthClient.hasSession=true, so the first successful\n // refresh after restore emits TOKEN_REFRESHED (not SIGNED_IN) — the\n // auth facade relies on this distinction.\n // (b) In the expired-trick path the transient session is\n // {accessToken:'', expiresAt:0} = \"pending refresh\" — never usable\n // directly, it only exists to arm pre-flight.\n const persisted = storage.load();\n if (persisted) {\n const { accessToken, expiresAt } = persisted;\n if (accessToken && expiresAt && expiresAt > Date.now()) {\n authClient.setTokens(\n accessToken,\n persisted.refreshToken,\n Math.floor((expiresAt - Date.now()) / 1000),\n );\n } else {\n authClient.setTokens('', persisted.refreshToken); // wires refreshFunction\n tokenManager.setSession({\n accessToken: '',\n refreshToken: persisted.refreshToken,\n expiresAt: 0,\n });\n }\n }\n\n // Persist token changes — the FULL session (access+expiry ride along so the\n // next hydration can skip the refresh); clear storage on sign-out.\n // AuthClient's own session mirror is registered first (its constructor), so\n // getSession() here already reflects the change that triggered us.\n authClient.onTokenChange(({ refreshToken }) => {\n if (refreshToken) {\n const session = authClient.getSession().data;\n if (session) {\n storage.save({\n refreshToken,\n accessToken: session.accessToken,\n expiresAt: session.expiresAt,\n });\n } else {\n storage.save({ refreshToken });\n }\n } else {\n storage.clear();\n }\n });\n\n // Lazy getter resolves the rt↔PalbeAuth construction cycle without a cast:\n // PalbeAuth needs rt, and `auth` stays non-optional on the interface.\n let auth: PalbeAuth | undefined;\n let flags: PalbeFlags | undefined;\n let realtime: PalbeRealtime | undefined;\n let analytics: PalbeAnalytics | undefined;\n const rt: PalbeRuntime = {\n config,\n http,\n tokenManager,\n authClient,\n storage,\n get auth() {\n if (!auth) auth = new PalbeAuth(rt);\n return auth;\n },\n // Same lazy pattern as `auth`: the pool is constructed (and, in the\n // browser, started) only when `pb.flags` is actually touched — a pbServer\n // request that never reads flags pays nothing.\n get flags() {\n if (!flags) flags = new PalbeFlags(rt);\n return flags;\n },\n // Lazy like `auth`/`flags` — but constructing PalbeRealtime is itself\n // inert: the WebSocket only opens on the first channel subscription/send.\n get realtime() {\n if (!realtime) realtime = new PalbeRealtime(rt);\n return realtime;\n },\n destroyRealtime() {\n // Only destroy if the lazy getter was already accessed (memo is set).\n // Do NOT touch `rt.realtime` here — that would construct it just to destroy.\n realtime?.destroy();\n realtime = undefined;\n },\n // The buffering facade is lazy; its identity state is NOT (below).\n get analytics() {\n if (!analytics) analytics = new PalbeAnalytics(rt, analyticsState);\n return analytics;\n },\n };\n\n // Analytics identity state is EAGER (cheap: listener wiring, no I/O): the\n // X-Distinct-Id interceptor must stamp EVERY request from the first one,\n // even when pb.analytics is never touched (iOS wires the resolver at\n // PalBackend init). Subscribing to auth events here constructs PalbeAuth\n // up front — acceptable, its constructor only attaches listeners.\n const analyticsState = new AnalyticsState(rt);\n http.addInterceptor((request) => {\n // Caller-supplied X-Distinct-Id wins (iOS applyDistinctId parity).\n const hasOwn = Object.keys(request.headers).some((k) => k.toLowerCase() === 'x-distinct-id');\n if (!hasOwn) request.headers['X-Distinct-Id'] = analyticsState.distinctId();\n });\n return rt;\n}\n","import { BackendError } from './errors.js';\nimport { type CallOptions, palbeRequest } from './request.js';\nimport type { PalbeRuntime } from './runtime.js';\n\n/**\n * Runtime resolution is deferred behind `resolveRt` (global pb passes\n * `getRuntime`, bound clients pass `() => rt`) — but name validation runs\n * FIRST so an empty name fails as `validation` even before `notConfigured`.\n */\nexport async function callEndpoint<O>(\n resolveRt: () => PalbeRuntime,\n name: string,\n input?: unknown,\n options?: CallOptions,\n): Promise<O> {\n if (!name || name === '/') {\n throw new BackendError('validation', {\n code: 'invalid_endpoint_name',\n message: 'Endpoint name must be a non-empty path like \"todos/create\"',\n });\n }\n const rt = resolveRt();\n const path = name.startsWith('/') ? name : `/${name}`;\n return palbeRequest<O>(rt, 'POST', path, {\n body: input,\n headers: options?.headers,\n signal: options?.signal,\n });\n}\n","import { asPalbaseError, BackendError, fromEnvelope, fromPalbaseError } from './errors.js';\nimport type { PalbeRuntime } from './runtime.js';\nimport { VERSION } from './version.js';\n\nexport interface UploadProgress {\n /** Bytes sent so far. */\n sent: number;\n /**\n * Total bytes. Always > 0 when delivered — progress events whose length is\n * not computable are skipped entirely.\n */\n total: number;\n}\n\nexport interface UploadOptions {\n file: Blob;\n filename?: string;\n contentType?: string;\n fields?: Record<string, string>;\n onProgress?: (progress: UploadProgress) => void;\n signal?: AbortSignal;\n headers?: Record<string, string>;\n /** Client-side pre-send validation (iOS parity). */\n constraints?: { maxSize?: number; allowedTypes?: string[] };\n}\n\nfunction abortError(): BackendError {\n return new BackendError('network', { code: 'aborted', message: 'Upload aborted' });\n}\n\nfunction checkConstraints(options: UploadOptions): void {\n const c = options.constraints;\n if (!c) return;\n if (c.maxSize !== undefined && options.file.size > c.maxSize) {\n const message = `File size ${options.file.size} exceeds max ${c.maxSize} bytes`;\n throw new BackendError('validation', {\n code: 'file_too_large',\n message,\n fields: [{ field: 'file', message }],\n });\n }\n // Empty allowedTypes = no type check (iOS contract). Validate the EFFECTIVE\n // type: a contentType override wins over file.type (it is what gets sent).\n const effType = options.contentType ?? options.file.type;\n if (c.allowedTypes && c.allowedTypes.length > 0 && !c.allowedTypes.includes(effType)) {\n const message = `File type '${effType}' is not allowed`;\n throw new BackendError('validation', {\n code: 'file_type_not_allowed',\n message,\n fields: [{ field: 'file', message }],\n });\n }\n}\n\nasync function buildHeaders(\n rt: PalbeRuntime,\n extra?: Record<string, string>,\n): Promise<Record<string, string>> {\n // Pre-flight refresh: mirrors the pattern in request.ts / HttpClient.\n if (\n rt.tokenManager.isExpired() &&\n rt.tokenManager.getRefreshToken() &&\n rt.tokenManager.refreshFunction\n ) {\n try {\n await rt.tokenManager.refreshSession();\n } catch (e) {\n const pe = asPalbaseError(e);\n const status = pe?.status ?? 0;\n if (status === 400 || status === 401 || status === 403) {\n rt.tokenManager.clearSession(); // terminal — proceed unauthenticated\n } else {\n throw pe ? fromPalbaseError(pe) : e;\n }\n }\n }\n\n // X-Project-Ref is deliberately NOT set here: uploads bypass @palbase/core's\n // HttpClient (which stamps it from the apikey) to stream multipart bodies, and\n // Kong already derives endpoint_ref from the apikey/host on every request, so\n // the header is redundant on the wire. This matches the iOS SDK, whose upload\n // path likewise omits it.\n const headers: Record<string, string> = {\n apikey: rt.config.apiKey,\n 'X-Client-Info': `palbe-web/${VERSION}`,\n ...rt.config.headers,\n };\n const token = rt.tokenManager.getAccessToken();\n if (token) headers.Authorization = `Bearer ${token}`;\n // Case-insensitive scan (mirrors request.ts): a caller-supplied\n // `idempotency-key` in any casing must suppress generation.\n const callerHasKey = Object.keys(extra ?? {}).some((k) => k.toLowerCase() === 'idempotency-key');\n if (!callerHasKey) headers['Idempotency-Key'] = crypto.randomUUID();\n // extra spreads LAST so a caller-pinned Authorization wins (HttpClient/iOS precedence).\n return { ...headers, ...extra };\n}\n\nfunction buildForm(options: UploadOptions): FormData {\n const form = new FormData();\n for (const [k, v] of Object.entries(options.fields ?? {})) form.append(k, v);\n const file =\n options.contentType && options.file.type !== options.contentType\n ? new Blob([options.file], { type: options.contentType })\n : options.file;\n const filename =\n options.filename ??\n (typeof File !== 'undefined' && options.file instanceof File ? options.file.name : 'file');\n form.append('file', file, filename);\n return form;\n}\n\nfunction decode<O>(status: number, text: string): O {\n let body: unknown;\n try {\n body = text === '' ? null : JSON.parse(text);\n } catch {\n if (status >= 200 && status < 300) {\n throw new BackendError('decode', {\n code: 'decode_error',\n message: 'Invalid JSON in response',\n status,\n });\n }\n body = text;\n }\n if (status < 200 || status >= 300) throw fromEnvelope(status, body);\n return body as O;\n}\n\nfunction uploadViaXHR<O>(\n url: string,\n form: FormData,\n headers: Record<string, string>,\n options: UploadOptions,\n): Promise<O> {\n return new Promise<O>((resolve, reject) => {\n // Pre-aborted signal: never send. (xhr.abort() before send() fires no\n // abort event per spec, so routing this through onabort would hang.)\n if (options.signal?.aborted) {\n reject(abortError());\n return;\n }\n const xhr = new XMLHttpRequest();\n xhr.open('POST', url);\n for (const [k, v] of Object.entries(headers)) xhr.setRequestHeader(k, v);\n xhr.upload.onprogress = (e: { lengthComputable: boolean; loaded: number; total: number }) => {\n if (!e.lengthComputable) return;\n options.onProgress?.({ sent: e.loaded, total: e.total });\n };\n const onAbort = () => xhr.abort();\n const cleanup = () => options.signal?.removeEventListener('abort', onAbort);\n xhr.onload = () => {\n cleanup();\n try {\n resolve(decode<O>(xhr.status, xhr.responseText));\n } catch (err) {\n reject(err);\n }\n };\n xhr.onerror = () => {\n cleanup();\n reject(new BackendError('network', { code: 'network_error', message: 'Upload failed' }));\n };\n xhr.onabort = () => {\n cleanup();\n reject(abortError());\n };\n options.signal?.addEventListener('abort', onAbort, { once: true });\n xhr.send(form);\n });\n}\n\nasync function uploadViaFetch<O>(\n url: string,\n form: FormData,\n headers: Record<string, string>,\n options: UploadOptions,\n): Promise<O> {\n let res: Response;\n try {\n res = await fetch(url, { method: 'POST', body: form, headers, signal: options.signal });\n } catch (e) {\n // DOMException extends Error in modern runtimes — name check covers both.\n if (e instanceof Error && e.name === 'AbortError') throw abortError();\n throw new BackendError('network', {\n code: 'network_error',\n message: e instanceof Error ? e.message : 'Upload failed',\n });\n }\n return decode<O>(res.status, await res.text());\n}\n\n/** Runtime resolution deferred behind `resolveRt` — see call.ts for the pattern. */\nexport async function uploadEndpoint<O>(\n resolveRt: () => PalbeRuntime,\n name: string,\n options: UploadOptions,\n): Promise<O> {\n const rt = resolveRt();\n checkConstraints(options); // pre-send guard — rejects before any network I/O\n const path = name.startsWith('/') ? name : `/${name}`;\n const url = `${rt.config.url}${path}`;\n const headers = await buildHeaders(rt, options.headers);\n const form = buildForm(options);\n const useXHR = options.onProgress !== undefined && typeof XMLHttpRequest !== 'undefined';\n return useXHR\n ? uploadViaXHR<O>(url, form, headers, options)\n : uploadViaFetch<O>(url, form, headers, options);\n}\n","import type { PalbeAnalytics } from './analytics-facade.js';\nimport type { PalbeAuth } from './auth-facade.js';\nimport { callEndpoint } from './call.js';\nimport type { PalbeFlags } from './flags-facade.js';\nimport { getRuntime } from './internal.js';\nimport { boundNamespaceAccessor, getNamespace, getRegistry } from './namespaces.js';\nimport type { PalbeRealtime } from './realtime/facade.js';\nimport type { CallOptions } from './request.js';\nimport type { PalbeRuntime } from './runtime.js';\nimport type { UploadOptions } from './upload.js';\nimport { uploadEndpoint } from './upload.js';\n\n/**\n * The Palbe surface. Codegen augments this interface with typed endpoint\n * namespaces: declare module '@palbase/web' { interface PB { rooms: ... } }\n */\nexport interface PB {\n call<O = unknown>(name: string, input?: unknown, options?: CallOptions): Promise<O>;\n upload<O = unknown>(name: string, options: UploadOptions): Promise<O>;\n readonly auth: PalbeAuth;\n readonly flags: PalbeFlags;\n readonly realtime: PalbeRealtime;\n readonly analytics: PalbeAnalytics;\n}\n\n/**\n * One proxy construction for BOTH client flavors:\n * - global `pb`: resolveRt = getRuntime (late-bound — a __configure swap is\n * visible to every subsequent call), nsAccessor = the globally-cached path;\n * - bound clients: resolveRt = () => rt (captured once, immune to global\n * swaps), nsAccessor = a per-client cache (see namespaces.ts).\n */\nfunction createClientProxy(\n resolveRt: () => PalbeRuntime,\n nsAccessor: (name: string) => unknown,\n): PB {\n const base = {\n call<O = unknown>(name: string, input?: unknown, options?: CallOptions): Promise<O> {\n return callEndpoint<O>(resolveRt, name, input, options);\n },\n upload<O = unknown>(name: string, options: UploadOptions): Promise<O> {\n return uploadEndpoint<O>(resolveRt, name, options);\n },\n get auth(): PalbeAuth {\n return resolveRt().auth;\n },\n get flags(): PalbeFlags {\n return resolveRt().flags;\n },\n get realtime(): PalbeRealtime {\n return resolveRt().realtime;\n },\n get analytics(): PalbeAnalytics {\n return resolveRt().analytics;\n },\n };\n return new Proxy(base, {\n get(target, prop, receiver) {\n if (prop in target) return Reflect.get(target, prop, receiver);\n // `then` must NEVER resolve to a namespace: a thenable pb would make\n // `await pb` (or Promise.resolve(pb)) hang on the proxy. Registration\n // also rejects it (see namespaces.ts) — this is defense in depth.\n if (prop === 'then') return undefined;\n if (typeof prop === 'string') {\n const ns = nsAccessor(prop);\n if (ns !== undefined) return ns;\n }\n return undefined;\n },\n has(target, prop) {\n if (prop in target) return true;\n return typeof prop === 'string' && prop !== 'then' && getRegistry()[prop] !== undefined;\n },\n }) as PB;\n}\n\nexport const pb: PB = createClientProxy(getRuntime, getNamespace);\n\n/**\n * A `PB` client permanently bound to ONE runtime — typed namespaces, `call`,\n * `upload` and `auth` all resolve `rt`, NEVER the global slot. This is the\n * per-request isolation primitive for server adapters (palbe/next pbServer):\n * two bound clients serving concurrent requests cannot leak each other's\n * sessions, and global `__configure` swaps don't touch them.\n */\nexport function createBoundClient(rt: PalbeRuntime): PB {\n return createClientProxy(() => rt, boundNamespaceAccessor(rt));\n}\n","import type { PalbeConfig } from './config.js';\nimport { BackendError } from './errors.js';\nimport { __registerNamespaces, resetNamespaces } from './namespaces.js';\nimport { buildRuntime, type PalbeRuntime } from './runtime.js';\nimport { palbeState } from './state.js';\n\n// Runtime lives in a process-global slot (see state.ts) so every build copy\n// (ESM/CJS, palbe + palbe/internal entries) converges on one singleton.\n// This is the BROWSER path (one runtime per tab). Server-side per-request\n// runtimes arrive with palbe/next (P3); do not sign in from shared server\n// code against this singleton.\n\n/**\n * Called by the generated palbe.gen.ts on import. REPLACES the runtime\n * (re-runs storage hydration) — safe to call repeatedly, e.g. watch-mode\n * regen. The namespace registry intentionally survives re-configure: the\n * gen file re-registers its static descriptors right after.\n */\nexport function __configure(config: PalbeConfig): void {\n const state = palbeState();\n // Destroy the old realtime facade if it was constructed — closes the socket\n // and clears heartbeat timers so they do not outlive the replaced runtime.\n state.runtime?.destroyRealtime();\n state.runtime = buildRuntime(config);\n // Fire after the runtime is in place so listeners that read pb.* immediately\n // (e.g. useSyncExternalStore getSnapshot) see the new runtime.\n for (const cb of state.configuredListeners) {\n try {\n cb();\n } catch {\n // consumer errors must not abort delivery to other listeners\n }\n }\n}\n\n/**\n * Register a callback that fires synchronously after every successful\n * `__configure` call (including re-configure / watch-mode regen).\n *\n * Returns an unsubscribe function. Safe to call before the first `__configure`.\n *\n * @internal — consumed by `palbe/react`; not part of the public `palbe` surface.\n */\nexport function onConfigured(cb: () => void): () => void {\n const state = palbeState();\n state.configuredListeners.add(cb);\n return () => state.configuredListeners.delete(cb);\n}\n\n/** @internal */\nexport function getRuntime(): PalbeRuntime {\n const rt = palbeState().runtime;\n if (!rt) throw BackendError.notConfigured();\n return rt;\n}\n\n/** Test-only: drop runtime + registered namespaces. */\nexport function __reset(): void {\n const state = palbeState();\n state.runtime?.destroyRealtime();\n state.runtime = null;\n state.configuredListeners.clear();\n resetNamespaces();\n}\n\nexport type { EndpointDescriptor, NamespaceTree } from './namespaces.js';\nexport type { PB } from './pb.js';\nexport { createBoundClient } from './pb.js';\nexport type { PalbeConfig, PalbeRuntime };\n// Server-adapter seams (palbe/next): per-request runtime + bound client.\nexport { __registerNamespaces, buildRuntime };\n"],"mappings":"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","names":["validatePathParam"]}