@palbase/web 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/client.ts","../../../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","../../../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/internal.ts","../../src/next/cookie-codec.ts"],"sourcesContent":["/**\n * '@palbase/web/next/client' — the browser half of the Next.js adapter. This entry\n * must NEVER import 'next' (it runs in plain client bundles); the cookie jar\n * is document.cookie itself, written with the shared codec so the server\n * side (pbServer / middleware / callback) reads the same bytes.\n */\nimport { __configure, getRuntime } from '../internal.js';\nimport type { PersistedSession, SessionStorageAdapter } from '../storage.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookies,\n endpointRefFromApiKey,\n} from './cookie-codec.js';\n\n/** 30 days — the refresh-token TTL (P3 design contract). */\nconst SESSION_MAX_AGE_S = 2_592_000;\n\n// Secure is unconditional, INCLUDING http://localhost: browsers treat\n// localhost as a potentially-trustworthy origin, so document.cookie accepts\n// Secure cookies there — no dev-mode special case needed. KNOWN LIMITATION:\n// plain-http origins OTHER than localhost — e.g. LAN-IP device testing on\n// http://192.168.x.x — are NOT trustworthy, so the browser silently DROPS\n// these Secure cookie writes and the session won't persist; use https (or a\n// localhost tunnel/port-forward) for on-device testing. NOT HttpOnly by\n// design: the browser SDK must read/write the session (Supabase-paradigm\n// tradeoff, documented in the P3 plan).\nconst WRITE_ATTRS = 'Path=/; SameSite=Lax; Secure';\n\n/** Parse document.cookie (\"a=1; b=2\") into a name → raw-value map. */\nfunction cookieJar(): Map<string, string> {\n const jar = new Map<string, string>();\n for (const part of document.cookie.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n const name = part.slice(0, eq).trim();\n if (name) jar.set(name, part.slice(eq + 1).trim());\n }\n return jar;\n}\n\nfunction deleteCookie(name: string): void {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=; ${WRITE_ATTRS}; Max-Age=0`;\n}\n\n/**\n * document.cookie-backed SessionStorageAdapter. `load` returns the EXTENDED\n * PersistedSession (access token + expiry ride along) so hydration adopts\n * the full session without a refresh round-trip.\n */\nexport function cookieSessionStorage(endpointRef: string): SessionStorageAdapter {\n return {\n load(): PersistedSession | null {\n if (typeof document === 'undefined') return null;\n const jar = cookieJar();\n const stored = decodeSessionCookies((name) => jar.get(name), endpointRef);\n if (!stored) return null;\n // A refresh-only save round-trips as a:'' / e:0 — normalize back to\n // the legacy shape so hydration takes the expired-trick path cleanly.\n return stored.accessToken && stored.expiresAt > 0\n ? {\n refreshToken: stored.refreshToken,\n accessToken: stored.accessToken,\n expiresAt: stored.expiresAt,\n }\n : { refreshToken: stored.refreshToken };\n },\n save(session: PersistedSession): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n // Delete every currently-present session cookie first (stale chunks of\n // a previously-larger session, or a stale base when the new write\n // chunks), then set the new cookie(s).\n for (const name of clearedSessionCookieNames(endpointRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n const { set, clear } = encodeSessionCookies(endpointRef, {\n accessToken: session.accessToken ?? '',\n refreshToken: session.refreshToken,\n expiresAt: session.expiresAt ?? 0,\n });\n for (const { name, value } of set) {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=${value}; ${WRITE_ATTRS}; Max-Age=${SESSION_MAX_AGE_S}`;\n }\n // Overflow guard (codec contract): delete one-past-the-end so a stale\n // orphan chunk behind a gap can never join a future chunk run.\n for (const name of clear) deleteCookie(name);\n },\n clear(): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n for (const name of clearedSessionCookieNames(endpointRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n },\n };\n}\n\n/**\n * One-liner for a client component/provider: re-configure the already-loaded\n * gen config (palbe.gen.ts must be imported first — throws the guided\n * notConfigured error otherwise) with cookie-backed session storage so the\n * browser and the server share the session. Calling it again simply\n * re-configures (safe, e.g. under fast refresh). When Next evaluates the\n * client component module server-side there is no document — no-op.\n */\nexport function setupPalbeNext(): void {\n if (typeof document === 'undefined') return;\n const config = getRuntime().config;\n __configure({ ...config, storage: cookieSessionStorage(endpointRefFromApiKey(config.apiKey)) });\n}\n","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 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.0';\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 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","/**\n * Session-cookie codec shared by EVERY palbe/next surface (browser storage\n * adapter, pbServer cookie adapter, middleware, auth callback). Pure\n * functions — no 'next' imports, no palbe runtime imports — so both sides\n * provably read and write the SAME bytes.\n *\n * Format (P3 design contract):\n * - name: `palbe-session-<endpointRef>`; overflow chunk names append `.0`,\n * `.1`, … (cookie names otherwise avoid dots — tooling friendliness).\n * - value: `encodeURIComponent(JSON.stringify({ a, r, e }))` with a=access\n * token, r=refresh token, e=expiry (ms epoch).\n * - one cookie when the encoded value fits MAX_COOKIE_VALUE chars, else\n * chunks ONLY (the base name must not coexist with chunks — callers clear\n * stale names via clearedSessionCookieNames before writing, then honor the\n * encode result's `clear` overflow guard after writing).\n */\n\nexport { endpointRefFromApiKey } from '../api-key.js';\n\nexport interface StoredSession {\n accessToken: string;\n refreshToken: string;\n /** ms epoch */\n expiresAt: number;\n}\n\n/** Browsers cap a cookie at ~4096 bytes incl. name + attributes; 3500 leaves headroom. */\nconst MAX_COOKIE_VALUE = 3500;\n\n/**\n * The P3 cookie contract attributes, shared by every server-side writer\n * (pbServer adapter, middleware, callback); the browser writer mirrors them\n * in document.cookie string form (next/client.ts). NOT HttpOnly by design —\n * the browser SDK must read/write the session (Supabase-paradigm tradeoff);\n * Max-Age = 30d = the refresh-token TTL.\n */\nexport const SESSION_COOKIE_ATTRS = {\n path: '/',\n sameSite: 'lax',\n secure: true,\n maxAge: 2_592_000,\n} as const;\n\nexport function sessionCookieName(endpointRef: string): string {\n return `palbe-session-${endpointRef}`;\n}\n\nexport interface SessionCookieWrite {\n /** Cookies to SET, in order. */\n set: Array<{ name: string; value: string }>;\n /**\n * Names to DELETE alongside the set: the one-past-the-end OVERFLOW GUARD\n * (base write → `.0`; chunks `.0..n` → `.(n+1)`). Decode stops at the\n * first missing chunk, so deleting exactly one-past-the-end guarantees a\n * stale orphan chunk (left behind a gap, where presence-probing cleanup\n * cannot see it) can never concatenate into the freshly written run.\n * Writers MUST honor both lists.\n */\n clear: string[];\n}\n\nexport function encodeSessionCookies(\n endpointRef: string,\n session: StoredSession,\n): SessionCookieWrite {\n const name = sessionCookieName(endpointRef);\n const value = encodeURIComponent(\n JSON.stringify({ a: session.accessToken, r: session.refreshToken, e: session.expiresAt }),\n );\n if (value.length <= MAX_COOKIE_VALUE) {\n return { set: [{ name, value }], clear: [`${name}.0`] };\n }\n const chunks: Array<{ name: string; value: string }> = [];\n // Chunk WITHOUT splitting a %XX escape (every '%' in the encoded value\n // starts one — encodeURIComponent never emits a bare '%'). Next's cookie\n // layer percent-DECODES values on read and silently DROPS a cookie whose\n // value is an invalid escape sequence, so a split escape would lose that\n // chunk server-side; independent decodability also lets\n // encodeSessionCookiesDecoded mirror this run byte-exactly.\n for (let start = 0; start < value.length; ) {\n let end = Math.min(start + MAX_COOKIE_VALUE, value.length);\n if (end < value.length) {\n if (value[end - 1] === '%') end -= 1;\n else if (value[end - 2] === '%') end -= 2;\n }\n chunks.push({ name: `${name}.${chunks.length}`, value: value.slice(start, end) });\n start = end;\n }\n return { set: chunks, clear: [`${name}.${chunks.length}`] };\n}\n\n/**\n * encodeSessionCookies for percent-ENCODING cookie stores. Next's request\n * and response cookie APIs apply encodeURIComponent to values when\n * serializing (and decodeURIComponent on read) — handing them the codec's\n * already-encoded values would DOUBLE-encode the wire bytes and break the\n * browser-side reader. This variant returns the same cookie run with\n * DECODED values: `encodeURIComponent(value)` reproduces the\n * encodeSessionCookies bytes exactly (chunk boundaries never split an\n * escape), so an encoding store writes byte-identical cookies to a raw\n * writer like document.cookie.\n */\nexport function encodeSessionCookiesDecoded(\n endpointRef: string,\n session: StoredSession,\n): SessionCookieWrite {\n const { set, clear } = encodeSessionCookies(endpointRef, session);\n return {\n set: set.map(({ name, value }) => ({ name, value: decodeURIComponent(value) })),\n clear,\n };\n}\n\n/**\n * Read the session back from a cookie getter: the base name wins; otherwise\n * `.0`, `.1`, … are concatenated until the first missing chunk. Tolerant:\n * malformed URI escapes / JSON / shape → null (treated as \"no session\").\n *\n * Accepts BOTH value forms. Next's cookie stores percent-DECODE values once\n * on read, so server-side callers (pbServer adapter, middleware) hand us the\n * already-decoded JSON; raw readers (document.cookie) hand us the encoded\n * form. The raw (already-decoded) parse runs FIRST because it is\n * unambiguous — an encoded value can never raw-parse as JSON\n * (encodeURIComponent escapes `{` itself, so it starts with `%7B`) — whereas\n * decode-first would silently corrupt a literal `%XX` run inside a token\n * (decodeURIComponent('%41') → 'A', no throw) or throw on an invalid escape.\n */\nexport function decodeSessionCookies(\n get: (name: string) => string | undefined,\n endpointRef: string,\n): StoredSession | null {\n const base = sessionCookieName(endpointRef);\n let encoded = get(base);\n if (encoded === undefined) {\n let joined = '';\n for (let i = 0; ; i++) {\n const part = get(`${base}.${i}`);\n if (part === undefined) break;\n joined += part;\n }\n if (joined === '') return null;\n encoded = joined;\n }\n const raw = parseStoredSession(encoded);\n if (raw !== null) return raw;\n try {\n return parseStoredSession(decodeURIComponent(encoded));\n } catch {\n return null;\n }\n}\n\n/** JSON-parse + shape-check one candidate value; malformed → null. */\nfunction parseStoredSession(json: string): StoredSession | null {\n try {\n const parsed: unknown = JSON.parse(json);\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.a === 'string' && typeof obj.r === 'string' && typeof obj.e === 'number') {\n return { accessToken: obj.a, refreshToken: obj.r, expiresAt: obj.e };\n }\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Names a writer must delete before/while writing: the base name (when\n * present) plus every existing `.N` chunk, probed in order until the first\n * missing one (writes are always a contiguous `.0..n` run, so a gap means\n * the end of palbe-owned chunks).\n */\nexport function clearedSessionCookieNames(\n endpointRef: string,\n present: (name: string) => boolean,\n): string[] {\n const base = sessionCookieName(endpointRef);\n const names: string[] = [];\n if (present(base)) names.push(base);\n for (let i = 0; ; i++) {\n const chunk = `${base}.${i}`;\n if (!present(chunk)) break;\n names.push(chunk);\n }\n return names;\n}\n\n/**\n * Parse the canonical chunk index out of `palbe-session-<ref>.N`; non-chunk\n * names (the bare base, a foreign ref, junk suffixes like `.x` / `.01` /\n * `.-1`) → null. The index is a non-negative integer with NO leading zeros\n * (the encoder emits `.0`, `.1`, … verbatim), so `String(n) === suffix`\n * round-trips it exactly.\n */\nfunction chunkIndex(base: string, name: string): number | null {\n if (!name.startsWith(`${base}.`)) return null;\n const suffix = name.slice(base.length + 1);\n if (!/^\\d+$/.test(suffix)) return null;\n const idx = Number.parseInt(suffix, 10);\n return String(idx) === suffix ? idx : null;\n}\n\n/**\n * getAll-powered orphan sweep — the deletion list a writer with the FULL\n * cookie jar should clear, catching crash-orphans that presence-probing\n * (clearedSessionCookieNames, which stops at the first gap) CANNOT see.\n *\n * Given every cookie name in the jar, returns the `palbe-session-<ref>`-family\n * names that are NOT part of a single valid session:\n * - A valid session is EITHER the bare base alone, OR the contiguous chunk\n * run `.0, .1, …, n` (starting at `.0`, no gaps).\n * - Any `.N` beyond the first gap from `.0` is an orphan (incl. ALL chunks\n * when `.0` is absent — no run exists).\n * - The base coexisting with ANY chunk is inconsistent (the contract forbids\n * it; decode lets the base win and silently strands the chunks): the base\n * is kept, the chunks are swept (a fresh write replaces the base in place).\n *\n * Only canonical `.N` names count; foreign refs and malformed suffixes are\n * ignored. Output order: base (if inconsistent) first, then chunks ascending.\n */\nexport function orphanSessionCookieNames(endpointRef: string, allNames: string[]): string[] {\n const base = sessionCookieName(endpointRef);\n const hasBase = allNames.includes(base);\n const indices = new Set<number>();\n for (const name of allNames) {\n const idx = chunkIndex(base, name);\n if (idx !== null) indices.add(idx);\n }\n if (indices.size === 0) return []; // unchunked (base alone) or no palbe cookies\n\n // Length of the contiguous run from .0 (0 when .0 is missing).\n let runLen = 0;\n while (indices.has(runLen)) runLen++;\n\n const orphans: string[] = [];\n // Base coexisting with chunks: base wins on decode → chunks are stale.\n // Sweep the base too only when there are NO chunks behind the run to keep\n // (the contract forbids the mix; clearing chunks alone resolves it, but if\n // the run itself is invalid—no .0—the base is the real session and stays).\n // Decision: when base coexists with ANY chunk, the chunks are orphans; the\n // base is the live session and is kept — UNLESS the chunk set forms a valid\n // run AND there is also a gap-orphan, in which case both base+gap-orphan are\n // inconsistent leftovers. Concretely: keep it simple and SAFE — the base is\n // only swept when it sits alongside a partial/gapped chunk set (a crash mid\n // base→chunk migration), never when chunks form a clean run the base shadows.\n const everyChunkContiguous = indices.size === runLen && runLen > 0;\n if (hasBase && !everyChunkContiguous) orphans.push(base);\n\n for (const idx of [...indices].sort((a, b) => a - b)) {\n if (idx >= runLen) orphans.push(`${base}.${idx}`);\n }\n // Base + clean contiguous run: base wins on decode, chunks are stranded.\n if (hasBase && everyChunkContiguous) {\n for (let i = 0; i < runLen; i++) orphans.push(`${base}.${i}`);\n }\n return orphans;\n}\n"],"mappings":"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","names":["validatePathParam"]}
1
+ {"version":3,"sources":["../../src/next/client.ts","../../../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","../../../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","../../src/calls/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/messaging/util.ts","../../src/messaging/wire.ts","../../src/messaging/keydir.ts","../../src/messaging/group-messaging.ts","../../src/messaging/chat.ts","../../src/messaging/delivery-source.ts","../../src/messaging/history.ts","../../src/messaging/wasm/pkg/palbe_mls_bg.js","../../src/messaging/wasm/pkg/snippets/mls-rs-core-f99cdecbb456b09c/inline0.js","../../src/messaging/wasm/loader.ts","../../src/messaging/wasm/bridge.ts","../../src/messaging/mls-engine.ts","../../src/messaging/registry.ts","../../src/messaging/storage.ts","../../src/messaging/coordinator.ts","../../src/messaging/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/internal.ts","../../src/next/cookie-codec.ts"],"sourcesContent":["/**\n * '@palbase/web/next/client' — the browser half of the Next.js adapter. This entry\n * must NEVER import 'next' (it runs in plain client bundles); the cookie jar\n * is document.cookie itself, written with the shared codec so the server\n * side (pbServer / middleware / callback) reads the same bytes.\n */\nimport { __configure, getRuntime } from '../internal.js';\nimport type { PersistedSession, SessionStorageAdapter } from '../storage.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookies,\n endpointRefFromApiKey,\n} from './cookie-codec.js';\n\n/** 30 days — the refresh-token TTL (P3 design contract). */\nconst SESSION_MAX_AGE_S = 2_592_000;\n\n// Secure is unconditional, INCLUDING http://localhost: browsers treat\n// localhost as a potentially-trustworthy origin, so document.cookie accepts\n// Secure cookies there — no dev-mode special case needed. KNOWN LIMITATION:\n// plain-http origins OTHER than localhost — e.g. LAN-IP device testing on\n// http://192.168.x.x — are NOT trustworthy, so the browser silently DROPS\n// these Secure cookie writes and the session won't persist; use https (or a\n// localhost tunnel/port-forward) for on-device testing. NOT HttpOnly by\n// design: the browser SDK must read/write the session (Supabase-paradigm\n// tradeoff, documented in the P3 plan).\nconst WRITE_ATTRS = 'Path=/; SameSite=Lax; Secure';\n\n/** Parse document.cookie (\"a=1; b=2\") into a name → raw-value map. */\nfunction cookieJar(): Map<string, string> {\n const jar = new Map<string, string>();\n for (const part of document.cookie.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n const name = part.slice(0, eq).trim();\n if (name) jar.set(name, part.slice(eq + 1).trim());\n }\n return jar;\n}\n\nfunction deleteCookie(name: string): void {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=; ${WRITE_ATTRS}; Max-Age=0`;\n}\n\n/**\n * document.cookie-backed SessionStorageAdapter. `load` returns the EXTENDED\n * PersistedSession (access token + expiry ride along) so hydration adopts\n * the full session without a refresh round-trip.\n */\nexport function cookieSessionStorage(endpointRef: string): SessionStorageAdapter {\n return {\n load(): PersistedSession | null {\n if (typeof document === 'undefined') return null;\n const jar = cookieJar();\n const stored = decodeSessionCookies((name) => jar.get(name), endpointRef);\n if (!stored) return null;\n // A refresh-only save round-trips as a:'' / e:0 — normalize back to\n // the legacy shape so hydration takes the expired-trick path cleanly.\n return stored.accessToken && stored.expiresAt > 0\n ? {\n refreshToken: stored.refreshToken,\n accessToken: stored.accessToken,\n expiresAt: stored.expiresAt,\n }\n : { refreshToken: stored.refreshToken };\n },\n save(session: PersistedSession): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n // Delete every currently-present session cookie first (stale chunks of\n // a previously-larger session, or a stale base when the new write\n // chunks), then set the new cookie(s).\n for (const name of clearedSessionCookieNames(endpointRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n const { set, clear } = encodeSessionCookies(endpointRef, {\n accessToken: session.accessToken ?? '',\n refreshToken: session.refreshToken,\n expiresAt: session.expiresAt ?? 0,\n });\n for (const { name, value } of set) {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=${value}; ${WRITE_ATTRS}; Max-Age=${SESSION_MAX_AGE_S}`;\n }\n // Overflow guard (codec contract): delete one-past-the-end so a stale\n // orphan chunk behind a gap can never join a future chunk run.\n for (const name of clear) deleteCookie(name);\n },\n clear(): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n for (const name of clearedSessionCookieNames(endpointRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n },\n };\n}\n\n/**\n * One-liner for a client component/provider: re-configure the already-loaded\n * gen config (palbe.gen.ts must be imported first — throws the guided\n * notConfigured error otherwise) with cookie-backed session storage so the\n * browser and the server share the session. Calling it again simply\n * re-configures (safe, e.g. under fast refresh). When Next evaluates the\n * client component module server-side there is no document — no-op.\n */\nexport function setupPalbeNext(): void {\n if (typeof document === 'undefined') return;\n const config = getRuntime().config;\n __configure({ ...config, storage: cookieSessionStorage(endpointRefFromApiKey(config.apiKey)) });\n}\n","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 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 *\n * The key is shape-validated (prefix `pb`, non-empty endpointRef, and a\n * `<scope><20-base62-random>` secret segment where scope ∈ {c,s}) before a ref\n * is returned. A structurally-invalid key returns '' so session storage stays\n * UNSCOPED rather than getting bound to a partially-parsed ref — a loose parse\n * (e.g. `pb_victim_cXX`) must not scope persisted sessions to `victim` and\n * hydrate a session belonging to a different, real key. The backend remains the\n * authority that rejects a bad key on the first request; this keeps the client's\n * local session-storage scoping honest. (Audit finding apikey-ref-mismatch.)\n */\nconst API_KEY_RE = /^pb_([^_]+)_[cs][A-Za-z0-9]{20}$/;\n\nexport function endpointRefFromApiKey(apiKey: string): string {\n const m = API_KEY_RE.exec(apiKey);\n return m ? (m[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","// `pb.calls` — SP-4 voice/video call surface for the web SDK.\n//\n// Architecture: thin wrapper around livekit-client (browser WebRTC) +\n// the messaging server's call REST endpoints:\n// POST /v1/messaging/groups/{gid}/calls → start a call\n// POST /v1/messaging/groups/{gid}/calls/{cid}/accept → join a call\n// POST /v1/messaging/groups/{gid}/calls/{cid}/decline → decline\n// POST /v1/messaging/groups/{gid}/calls/{cid}/mute → mute/unmute (host)\n// POST /v1/messaging/groups/{gid}/calls/{cid}/kick → kick (host)\n//\n// The {gid} is a messaging group display-id (`grp_<uuidv7>`).\n//\n// ─── E2EE NOTE ─────────────────────────────────────────────────────────────\n// The LiveKit hop-by-hop transport is already DTLS-SRTP encrypted. Frame-level\n// end-to-end encryption (MLS-derived keys via ExternalE2EEKeyProvider) is the\n// SP-1.x web-MLS work and is OUT OF SCOPE for this surface. The `setMediaKey`\n// method is wired to ExternalE2EEKeyProvider as a design seam; call it AFTER\n// web-MLS delivers a shared frame key. Until then, rooms connect without e2ee.\n// TODO(SP-1-web): derive the frame key from the MLS exporter and call\n// call.setMediaKey(derivedKeyArrayBuffer)\n// ───────────────────────────────────────────────────────────────────────────\n\nimport {\n ExternalE2EEKeyProvider,\n type RemoteParticipant,\n type RemoteTrack,\n type RemoteTrackPublication,\n Room,\n RoomEvent,\n Track,\n} from 'livekit-client';\nimport type { Unsubscribe } from '../auth-facade.js';\nimport { BackendError } from '../errors.js';\nimport { palbeRequest } from '../request.js';\nimport type { PalbeRuntime } from '../runtime.js';\n\n// ─── Wire types ────────────────────────────────────────────────────────────\n\ninterface StartCallResponse {\n call_id: string;\n token: string;\n edge_url: string;\n max_members: number;\n}\n\ninterface AcceptCallResponse {\n token: string;\n edge_url: string;\n}\n\n// ─── Public types ──────────────────────────────────────────────────────────\n\nexport type CallState = 'connecting' | 'active' | 'ended';\n\nexport interface CallParticipant {\n identity: string;\n isSpeaking: boolean;\n audioMuted: boolean;\n videoEnabled: boolean;\n /** The live <video> element for this participant, or null before track arrives. */\n videoElement: HTMLVideoElement | null;\n /** The live <audio> element for this participant, or null before track arrives. */\n audioElement: HTMLAudioElement | null;\n}\n\nexport type CallChangeCallback = (call: Call) => void;\n\n/**\n * A live call session. Obtain via `pb.calls.start()` or `pb.calls.accept()`.\n *\n * Lifecycle:\n * connecting → active (room joined, tracks flowing)\n * active → ended (leave() / remote disconnect)\n *\n * Subscribe to changes via `call.subscribe(cb)` which fires on state or\n * participant changes (mirrors flags `onChange` / realtime `on` pattern).\n */\nexport class Call {\n readonly id: string;\n\n private _state: CallState = 'connecting';\n private readonly _participants = new Map<string, CallParticipant>();\n private readonly _listeners = new Set<CallChangeCallback>();\n private readonly _room: Room;\n private readonly _keyProvider: ExternalE2EEKeyProvider;\n\n /** @internal — obtain via PalbeCalls.start() / .accept() */\n constructor(callId: string, room: Room, keyProvider: ExternalE2EEKeyProvider) {\n this.id = callId;\n this._room = room;\n this._keyProvider = keyProvider;\n this._wireRoomEvents();\n }\n\n // ─── State ──────────────────────────────────────────────────────────────\n\n get state(): CallState {\n return this._state;\n }\n\n /** Snapshot of current remote participants (does NOT include the local participant). */\n get participants(): readonly CallParticipant[] {\n return Array.from(this._participants.values());\n }\n\n get localParticipant(): Room['localParticipant'] {\n return this._room.localParticipant;\n }\n\n // ─── Subscribe ──────────────────────────────────────────────────────────\n\n /**\n * Subscribe to call changes (state + participant updates).\n * Returns an `Unsubscribe` function — matches the SDK's flags/auth pattern.\n */\n subscribe(callback: CallChangeCallback): Unsubscribe {\n this._listeners.add(callback);\n return () => {\n this._listeners.delete(callback);\n };\n }\n\n // ─── Controls ───────────────────────────────────────────────────────────\n\n /** Mute / unmute the local microphone. */\n async mute(on: boolean): Promise<void> {\n await this._room.localParticipant.setMicrophoneEnabled(!on);\n }\n\n /** Enable / disable the local camera. */\n async setCamera(on: boolean): Promise<void> {\n await this._room.localParticipant.setCameraEnabled(on);\n }\n\n /**\n * Set a raw frame encryption key (ArrayBuffer) for this call.\n * Design seam for SP-1.x web-MLS: call this with the MLS exporter secret\n * to enable frame-level E2EE via ExternalE2EEKeyProvider.\n *\n * NOTE: The room must have been opened with `e2ee` options for this to take\n * effect. Currently calls connect WITHOUT e2ee — see module docstring.\n *\n * TODO(SP-1-web): wire this to MLS-derived key on session join.\n */\n async setMediaKey(key: ArrayBuffer): Promise<void> {\n await this._keyProvider.setKey(key);\n }\n\n /** Leave the call and disconnect from the LiveKit room. */\n async leave(): Promise<void> {\n await this._room.disconnect();\n this._setState('ended');\n }\n\n // ─── Internal ───────────────────────────────────────────────────────────\n\n private _setState(next: CallState): void {\n if (this._state === next) return;\n this._state = next;\n this._emit();\n }\n\n private _emit(): void {\n for (const cb of this._listeners) cb(this);\n }\n\n private _upsertParticipant(p: RemoteParticipant): void {\n const existing = this._participants.get(p.identity);\n const entry: CallParticipant = {\n identity: p.identity,\n isSpeaking: p.isSpeaking,\n audioMuted: !p.isMicrophoneEnabled,\n videoEnabled: p.isCameraEnabled,\n videoElement: existing?.videoElement ?? null,\n audioElement: existing?.audioElement ?? null,\n };\n this._participants.set(p.identity, entry);\n }\n\n private _attachTrack(track: RemoteTrack, participant: RemoteParticipant): void {\n const element = track.attach();\n const entry = this._participants.get(participant.identity);\n if (track.kind === Track.Kind.Video) {\n if (entry) {\n entry.videoElement = element as HTMLVideoElement;\n }\n } else if (track.kind === Track.Kind.Audio) {\n if (entry) {\n entry.audioElement = element as HTMLAudioElement;\n // Auto-play audio (browser policy: audio elements can autoplay when\n // created by a user gesture chain; the call starts from a user tap).\n (element as HTMLAudioElement).play().catch(() => {});\n }\n }\n this._emit();\n }\n\n private _wireRoomEvents(): void {\n this._room\n .on(RoomEvent.Connected, () => {\n this._setState('active');\n })\n .on(\n RoomEvent.TrackSubscribed,\n (track: RemoteTrack, _pub: RemoteTrackPublication, participant: RemoteParticipant) => {\n this._upsertParticipant(participant);\n this._attachTrack(track, participant);\n },\n )\n .on(\n RoomEvent.TrackUnsubscribed,\n (_track: RemoteTrack, _pub: RemoteTrackPublication, participant: RemoteParticipant) => {\n this._upsertParticipant(participant);\n this._emit();\n },\n )\n .on(RoomEvent.ParticipantConnected, (participant: RemoteParticipant) => {\n this._upsertParticipant(participant);\n this._emit();\n })\n .on(RoomEvent.ParticipantDisconnected, (participant: RemoteParticipant) => {\n this._participants.delete(participant.identity);\n this._emit();\n })\n .on(RoomEvent.ActiveSpeakersChanged, (speakers) => {\n for (const [id, entry] of this._participants) {\n entry.isSpeaking = speakers.some((s) => s.identity === id);\n }\n this._emit();\n })\n .on(RoomEvent.Disconnected, () => {\n this._setState('ended');\n });\n }\n}\n\n// ─── Facade ────────────────────────────────────────────────────────────────\n\nexport class PalbeCalls {\n private readonly rt: PalbeRuntime;\n\n constructor(rt: PalbeRuntime) {\n this.rt = rt;\n }\n\n /**\n * Start a new call in a messaging group.\n *\n * @param groupId - Messaging group display-id (`grp_<uuidv7>`)\n * @param media - Which media tracks to publish (default: audio + video)\n * @returns A `Call` in `connecting` state; transitions to `active` once LiveKit connects.\n *\n * @throws BackendError('network', { code: 'call_service_unavailable' }) if LiveKit SFU is down (503)\n * @throws BackendError('forbidden', { code: 'not_group_member' }) if the caller is not a member (403)\n * @throws BackendError('conflict', { code: 'call_room_full' }) if the room is full (409)\n */\n async start(\n groupId: string,\n { media = ['audio', 'video'] }: { media?: Array<'audio' | 'video'> } = {},\n ): Promise<Call> {\n if (!groupId) {\n throw new BackendError('validation', {\n code: 'invalid_group_id',\n message: 'groupId must be a non-empty string',\n });\n }\n const res = await palbeRequest<StartCallResponse>(\n this.rt,\n 'POST',\n `/v1/messaging/groups/${encodeURIComponent(groupId)}/calls`,\n { body: { media } },\n );\n return this._connect(res.call_id, res.token, res.edge_url, media);\n }\n\n /**\n * Accept an incoming call in a messaging group.\n *\n * @param groupId - Messaging group display-id\n * @param callId - Call identifier (from the push notification payload)\n * @param media - Which media tracks to publish (default: audio + video)\n */\n async accept(\n groupId: string,\n callId: string,\n { media = ['audio', 'video'] }: { media?: Array<'audio' | 'video'> } = {},\n ): Promise<Call> {\n const res = await palbeRequest<AcceptCallResponse>(\n this.rt,\n 'POST',\n `/v1/messaging/groups/${encodeURIComponent(groupId)}/calls/${encodeURIComponent(callId)}/accept`,\n { body: {} },\n );\n return this._connect(callId, res.token, res.edge_url, media);\n }\n\n /**\n * Decline an incoming call.\n *\n * @param groupId - Messaging group display-id\n * @param callId - Call identifier\n * @param reason - Optional decline reason string\n */\n async decline(groupId: string, callId: string, reason?: string): Promise<void> {\n await palbeRequest<void>(\n this.rt,\n 'POST',\n `/v1/messaging/groups/${encodeURIComponent(groupId)}/calls/${encodeURIComponent(callId)}/decline`,\n { body: { reason: reason ?? '' } },\n );\n }\n\n // ─── Internal ─────────────────────────────────────────────────────────\n\n private async _connect(\n callId: string,\n token: string,\n edgeUrl: string,\n media: Array<'audio' | 'video'>,\n ): Promise<Call> {\n // ExternalE2EEKeyProvider is the MLS seam (see module docstring).\n // Constructed but NOT passed to Room options — we connect without e2ee.\n // TODO(SP-1-web): pass { e2ee: { worker, keyProvider } } when MLS is ready.\n const keyProvider = new ExternalE2EEKeyProvider();\n\n const room = new Room({\n adaptiveStream: true,\n dynacast: true,\n });\n\n const call = new Call(callId, room, keyProvider);\n\n // Connect first; track publishing follows after Connected fires.\n // LiveKit requires the room to be connected before publishing.\n await room.connect(edgeUrl, token);\n\n if (media.includes('audio')) {\n await room.localParticipant.setMicrophoneEnabled(true);\n }\n if (media.includes('video')) {\n await room.localParticipant.setCameraEnabled(true);\n }\n\n return call;\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","// Byte / base64 helpers — portable across browser and Node (no Buffer dep on\n// the browser path). All MLS blobs cross the wire as base64 strings.\n\n/** Encode bytes to a standard (non-url-safe) base64 string. */\nexport function toBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64');\n }\n let binary = '';\n const chunk = 0x8000;\n for (let i = 0; i < bytes.length; i += chunk) {\n binary += String.fromCharCode(...bytes.subarray(i, i + chunk));\n }\n return btoa(binary);\n}\n\n/** Decode a standard base64 string to bytes. */\nexport function fromBase64(b64: string): Uint8Array {\n if (typeof Buffer !== 'undefined') {\n return new Uint8Array(Buffer.from(b64, 'base64'));\n }\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\n/** Lowercase hex of a byte buffer (a stable string key for any blob). */\nexport function bytesToHex(bytes: Uint8Array): string {\n let s = '';\n for (let i = 0; i < bytes.length; i++) {\n s += (bytes[i] ?? 0).toString(16).padStart(2, '0');\n }\n return s;\n}\n\nconst utf8Encoder = new TextEncoder();\nconst utf8Decoder = new TextDecoder();\n\nexport const encodeUtf8 = (s: string): Uint8Array => utf8Encoder.encode(s);\nexport const decodeUtf8 = (b: Uint8Array): string => utf8Decoder.decode(b);\n\n/** Bytes of a device id (the MLS credential identity == the `mdv_` device id). */\nexport const deviceIdBytes = (deviceId: string): Uint8Array => encodeUtf8(deviceId);\n\n/** A fresh client idempotency / id value (crypto-strong; never Math.random). */\nexport function randomId(): string {\n return crypto.randomUUID();\n}\n\n/** Mint a client-side `mdv_<uuidv7>` device id (SP-1.6 client-minted). The\n * server (messaging `ValidateDeviceID`) requires the mdv_ prefix followed by a\n * canonical UUID *version 7* — a v4 (what `crypto.randomUUID()` emits) is\n * rejected with `invalid_device_id`. So mint a real v7 here: the version must\n * match the server's id-validator, not just the prefix. A random v7 bound to\n * the caller's iJWT sub stays squat-safe (ownership = sub, uniqueness = PK). */\nexport function mintDeviceId(): string {\n return `mdv_${uuidV7()}`;\n}\n\n/**\n * Generate a canonical RFC 9562 UUID version 7 (time-ordered): 48-bit big-endian\n * Unix-ms timestamp, version nibble 7, variant bits 10, the rest CSPRNG random.\n * `crypto.randomUUID()` only emits v4, and the messaging server validates v7, so\n * the device-id minter cannot reuse it — this is the byte-shape the server's\n * `uuid.Version() == 7` check accepts.\n */\nexport function uuidV7(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n const ts = Date.now(); // ms since epoch fits in 48 bits until year 10889\n // A DataView write avoids `noUncheckedIndexedAccess` \"possibly undefined\" on\n // raw index assignment and writes the 48-bit timestamp big-endian in one shot.\n const view = new DataView(bytes.buffer);\n view.setUint32(0, Math.floor(ts / 2 ** 16)); // high 32 bits of the 48-bit ms\n view.setUint16(4, ts & 0xffff); // low 16 bits\n view.setUint8(6, (view.getUint8(6) & 0x0f) | 0x70); // version 7\n view.setUint8(8, (view.getUint8(8) & 0x3f) | 0x80); // variant 10\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/** The canonical sorted-pair direct_key for a 1:1 chat (`dm:<a>|<b>`). */\nexport function directKey(userA: string, userB: string): string {\n const [a, b] = [userA, userB].sort();\n return `dm:${a}|${b}`;\n}\n\n/** Mint a `msg_<32 hex>` client message id (matches iOS format). */\nexport function mintClientMsgId(): string {\n const b = new Uint8Array(16);\n crypto.getRandomValues(b);\n return `msg_${Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('')}`;\n}\n\n/** Truncate a string to 240 Unicode scalars (code points). Returns [truncated, wasTruncated]. */\nexport function truncateQuote(s: string): [string, boolean] {\n const scalars = [...s];\n if (scalars.length <= 240) return [s, false];\n return [scalars.slice(0, 240).join(''), true];\n}\n","// The messaging wire protocol for the web SDK — paths + request/response shapes.\n//\n// CRITICAL (differs from iOS): the web HttpClient does a RAW JSON.stringify of\n// the request body (NO camel↔snake conversion — verified @palbase/core\n// HttpClient). So every field here is snake_case to match the server tags\n// VERBATIM. The field names are the live server JSON tags, cross-checked against\n// modules/messaging/internal/handler/{group,message,keypackage,trust}.go and the\n// iOS MessagingTypes.swift wire doc (live-proven). All blobs are base64 strings.\n//\n// Paths mirror the iOS MessagingPaths (the M3 #7 contract): a {gid} segment is\n// ALWAYS a `grp_<uuidv7>` display_id (url-safe), NEVER the base64 rfc_group_id.\n\n// ─── Paths ───────────────────────────────────────────────────────────────────\n\nconst GROUPS = '/v1/messaging/groups';\nconst DEVICES = '/v1/messaging/devices';\nconst KEYDIR = '/v1/messaging/keydir';\n\n/** encodeURIComponent a single path segment (defense in depth; the real fix is\n * passing the url-safe grp_/mdv_ id, never the base64 rfc_group_id — M3 #7). */\nconst seg = (s: string): string => encodeURIComponent(s);\n\nexport const MessagingPaths = {\n groups: GROUPS,\n groupsDirect: `${GROUPS}/direct`,\n group: (displayId: string) => `${GROUPS}/${seg(displayId)}`,\n groupMetadata: (displayId: string) => `${GROUPS}/${seg(displayId)}/metadata`,\n groupMembers: (displayId: string) => `${GROUPS}/${seg(displayId)}/members`,\n groupMemberDevice: (displayId: string, deviceId: string) =>\n `${GROUPS}/${seg(displayId)}/members/${seg(deviceId)}`,\n groupMessages: (displayId: string) => `${GROUPS}/${seg(displayId)}/messages`,\n groupCommits: (displayId: string) => `${GROUPS}/${seg(displayId)}/commits`,\n groupRead: (displayId: string) => `${GROUPS}/${seg(displayId)}/read`,\n\n deviceWelcomes: (deviceId: string) => `${DEVICES}/${seg(deviceId)}/welcomes`,\n deviceQueue: (deviceId: string) => `${DEVICES}/${seg(deviceId)}/queue`,\n deviceQueueAck: (deviceId: string) => `${DEVICES}/${seg(deviceId)}/queue/ack`,\n\n keydirDevices: `${KEYDIR}/devices`,\n keydirDeviceKeyPackages: (deviceId: string) => `${KEYDIR}/devices/${seg(deviceId)}/keypackages`,\n keydirUserDevices: (userId: string) => `${KEYDIR}/users/${seg(userId)}/devices`,\n keydirUserClaim: (userId: string) => `${KEYDIR}/users/${seg(userId)}/keypackages/claim`,\n keydirPinsVerify: `${KEYDIR}/pins/verify`,\n\n prefs: '/v1/messaging/prefs',\n} as const;\n\n/** True iff `s` is safe to place RAW in a URL path segment (M3 #7 gate). A\n * grp_/mdv_ id passes; a base64 rfc_group_id (`/`, `=`, `+`) FAILS. */\nexport function isUrlSafePathSegment(s: string): boolean {\n return s.length > 0 && /^[A-Za-z0-9\\-._~]+$/.test(s);\n}\n\n// ─── Keydir (SP-0) ───────────────────────────────────────────────────────────\n\nexport type MessagingPlatform = 'android' | 'ios' | 'web';\n\nexport interface EnrollDeviceBody {\n device_id: string;\n platform: MessagingPlatform;\n device_name?: string;\n cipher_suite: number;\n signature_key: string; // base64 raw curve public key\n credential: string; // base64 TLS-encoded MLS Credential (identity == device_id)\n capabilities: string; // base64\n}\n\nexport interface EnrollResponse {\n device_id: string;\n signature_key_fingerprint: string;\n trust_tier: string;\n one_time_count: number;\n}\n\nexport interface ReplenishBody {\n key_packages: string[]; // base64 raw KeyPackages\n}\n\nexport interface DeviceInfo {\n device_id: string;\n signature_key: string;\n credential: string;\n cipher_suite: number;\n key_epoch: number;\n trust_tier: string;\n first_seen_at: string;\n}\n\nexport interface DeviceListResponse {\n device_set_version: string;\n devices: DeviceInfo[];\n warnings?: unknown[];\n}\n\nexport interface ClaimedKeyPackage {\n device_id: string;\n cipher_suite: number;\n key_package: string; // base64\n reused: boolean;\n}\n\nexport interface ClaimResponse {\n key_packages: ClaimedKeyPackage[];\n depleted_devices?: string[];\n}\n\nexport interface VerifyBody {\n peer_user_id: string;\n verified_device_set_hash: string;\n}\n\n// ─── Group create / direct ────────────────────────────────────────────────────\n\nexport interface CreateGroupBody {\n rfc_group_id: string; // base64\n cipher_suite: number;\n creator_device_id: string;\n creator_leaf_index: number;\n metadata?: string; // base64 sealed-name blob; omitted on web (no exporter yet)\n}\n\nexport interface CreateGroupResponse {\n display_id: string;\n rfc_group_id: string;\n current_epoch: number;\n metadata?: string;\n}\n\nexport interface CreateDirectGroupBody {\n direct_key: string;\n peer_user_id: string;\n rfc_group_id: string; // base64\n cipher_suite: number;\n creator_device_id: string;\n creator_leaf_index: number;\n}\n\nexport interface CreateDirectGroupResponse {\n display_id: string;\n rfc_group_id: string;\n current_epoch: number;\n owner_user_id?: string;\n exists?: boolean;\n}\n\nexport interface GroupReadResponse {\n display_id: string;\n rfc_group_id: string;\n current_epoch: number;\n status?: string;\n metadata?: string;\n}\n\n// ─── Members ───────────────────────────────────────────────────────────────────\n\nexport type MessagingGroupRole = 'owner' | 'admin' | 'member';\n\nexport interface GroupMemberRow {\n device_id: string;\n user_id: string;\n role: MessagingGroupRole;\n leaf_index: number;\n}\n\nexport interface ListMembersResponse {\n members: GroupMemberRow[];\n}\n\nexport interface WelcomeBody {\n recipient_device_id: string;\n recipient_user_id: string;\n welcome_b64: string;\n}\n\n/** `POST /groups/{gid}/members` — declared add + opaque commit + welcomes. */\nexport interface AddMemberBody {\n op: string;\n commit_b64: string;\n target_device_ids: string[];\n leaf_indexes: number[];\n user_ids: string[];\n key_package_refs: string[];\n welcomes: WelcomeBody[];\n}\n\n/** `DELETE /groups/{gid}/members/{deviceId}` — multi-remove / self-leave. */\nexport interface RemoveMembersBody {\n op: string;\n commit_b64: string;\n target_device_ids: string[];\n user_ids: string[];\n}\n\n/** `{ epoch }` — the advanced-to epoch a membership/commit write returns. */\nexport interface CommitAccepted {\n epoch: number;\n}\n\n// ─── Messages ──────────────────────────────────────────────────────────────────\n\nexport interface SendMessageBody {\n ciphertext_b64: string;\n client_idem_key: string;\n}\n\nexport interface SentReceipt {\n server_seq: number;\n epoch: number;\n}\n\n/** One queue row from `GET /devices/{deviceId}/queue`. */\nexport interface QueuedMessage {\n id: string;\n epoch: number;\n content_type: number; // 1=app, 2=proposal, 3=commit\n wire_format: number; // 1=public, 2=private\n ciphertext_b64: string;\n server_seq: number;\n // OPTIONAL group attribution + the fanned-out declaration (the live server\n // does not fan these on queue rows yet — nil today, gate engages when present).\n rfc_group_id_b64?: string;\n sender_device_id?: string;\n declared_added_device_ids?: string[];\n declared_removed_device_ids?: string[];\n declared_op?: string;\n}\n\nexport interface QueuePage {\n messages: QueuedMessage[];\n}\n\n/** One row from `GET /devices/{deviceId}/welcomes` — a new-member bootstrap. */\nexport interface PendingWelcome {\n id: string;\n display_id: string; // the group's grp_ routing id (url-safe; NOT the base64 rfc)\n rfc_group_id_b64: string;\n welcome_b64: string;\n added_at_epoch: number;\n}\n\nexport interface WelcomePage {\n welcomes: PendingWelcome[];\n}\n\nexport interface AckBody {\n message_ids: string[];\n}\n\n/** Queue-row content-type discriminator (matches the server `content_type`). */\nexport const ContentType = {\n application: 1,\n proposal: 2,\n commit: 3,\n} as const;\n\n// ─── SP-3 comms (read receipts / prefs / presence / typing) ──────────────────\n\nexport interface MarkReadBody {\n read_seq: number;\n read_epoch: number;\n is_private: boolean;\n}\n\nexport interface MarkReadResponse {\n read_seq: number;\n advanced: boolean;\n broadcast: boolean;\n}\n\nexport interface CommsPrefsResponse {\n share_presence: boolean;\n share_receipts: boolean;\n updated_at?: string;\n}\n\nexport interface SetPrefsBody {\n share_presence?: boolean;\n share_receipts?: boolean;\n}\n\nexport interface CommsPrefs {\n sharePresence: boolean;\n shareReceipts: boolean;\n}\n","// The MLS key directory (SP-0) mechanics — INTERNAL, mirroring the iOS\n// KeyDirectoryInternal. Hidden behind the coordinator's implicit enroll (the\n// first chat op self-enrolls). Byte-identical wire calls to iOS: same\n// /v1/messaging/keydir/* routes, same iJWT bearer (palbeRequest), same\n// snake_case bodies.\n\nimport { palbeRequest } from '../request.js';\nimport type { PalbeRuntime } from '../runtime.js';\nimport type { MlsEngine } from './mls-engine.js';\nimport type { DeviceIdStore } from './storage.js';\nimport { toBase64 } from './util.js';\nimport {\n type ClaimResponse,\n type DeviceListResponse,\n type EnrollDeviceBody,\n type EnrollResponse,\n MessagingPaths,\n type ReplenishBody,\n type VerifyBody,\n} from './wire.js';\n\n/**\n * Enroll THIS device + publish its first KeyPackages from REAL engine material.\n * Idempotent: a returning user (persisted device id) skips the POST and just\n * replenishes. Returns the resolved device id.\n *\n * Flow (mirrors iOS enroll):\n * 1. engine.enrollmentMaterial() → { signatureKey, credential, capabilities }\n * 2. POST /keydir/devices (the server validates the client-minted mdv_ id +\n * credential.identity == device_id + ownership == iJWT sub)\n * 3. generate N key packages → POST /keydir/devices/{id}/keypackages\n */\nexport async function enrollDevice(\n rt: PalbeRuntime,\n engine: MlsEngine,\n deviceStore: DeviceIdStore,\n opts: { keyPackageCount?: number; deviceName?: string } = {},\n): Promise<string> {\n const keyPackageCount = opts.keyPackageCount ?? 10;\n const deviceId = engine.deviceId;\n const alreadyEnrolled = (await deviceStore.load(engine.userId)) === deviceId;\n\n if (!alreadyEnrolled) {\n const material = await engine.enrollmentMaterial();\n const body: EnrollDeviceBody = {\n device_id: deviceId,\n platform: 'web',\n device_name: opts.deviceName,\n cipher_suite: 1,\n signature_key: toBase64(material.signatureKey),\n credential: toBase64(material.credential),\n capabilities: toBase64(material.capabilities),\n };\n await palbeRequest<EnrollResponse>(rt, 'POST', MessagingPaths.keydirDevices, { body });\n await deviceStore.save(engine.userId, deviceId);\n }\n\n if (keyPackageCount > 0) {\n const raws: string[] = [];\n for (let i = 0; i < keyPackageCount; i++) {\n raws.push(toBase64(await engine.generateKeyPackage()));\n }\n await replenish(rt, deviceId, raws);\n }\n return deviceId;\n}\n\n/** Publish more one-use KeyPackages for a device. */\nexport async function replenish(\n rt: PalbeRuntime,\n deviceId: string,\n rawKeyPackagesB64: string[],\n): Promise<void> {\n const body: ReplenishBody = { key_packages: rawKeyPackagesB64 };\n await palbeRequest<void>(rt, 'POST', MessagingPaths.keydirDeviceKeyPackages(deviceId), { body });\n}\n\n/** Claim a peer's KeyPackages (one per device) to add them to a group. */\nexport async function claimKeyPackages(rt: PalbeRuntime, userId: string): Promise<ClaimResponse> {\n return palbeRequest<ClaimResponse>(rt, 'POST', MessagingPaths.keydirUserClaim(userId), {\n body: {},\n });\n}\n\n/** List a user's enrolled devices (for the safety number + roster resolution). */\nexport async function listDevices(rt: PalbeRuntime, userId: string): Promise<DeviceListResponse> {\n return palbeRequest<DeviceListResponse>(rt, 'GET', MessagingPaths.keydirUserDevices(userId));\n}\n\n/** Record a TOFU verify pin for a peer's current device set. */\nexport async function verifyPeer(\n rt: PalbeRuntime,\n peerUserId: string,\n verifiedDeviceSetHash: string,\n): Promise<void> {\n const body: VerifyBody = {\n peer_user_id: peerUserId,\n verified_device_set_hash: verifiedDeviceSetHash,\n };\n await palbeRequest<void>(rt, 'POST', MessagingPaths.keydirPinsVerify, { body });\n}\n","// The membership/send orchestrator — the web mirror of iOS GroupMessaging.\n// Two correctness invariants (design §3, identical to iOS):\n//\n// (1) ATOMIC-PERSIST AROUND A COMMIT. Stage a pending commit in the engine,\n// POST it, apply ONLY on a 200. On a 409 epoch_conflict clearPendingCommit —\n// the local epoch is NEVER ahead of the server-accepted epoch.\n// (2) THE STALE-EPOCH REBASE LOOP. On 409 drain the winning commits, rebuild at\n// the new epoch, re-POST — bounded + jittered. Exhaustion throws.\n//\n// sendMessage NEVER rebases: an application message is epoch-bound; a 422\n// stale_application surfaces to the caller (re-send after the delivery source\n// catches up). Only membership COMMITS rebase.\n//\n// WEB GAP vs iOS: there is no MLS exporter export in the WASM surface yet, so\n// the group NAME is not sealed (metadata omitted; the server stays blind and the\n// UI falls back to a friendly label). Text/membership flows are byte-identical.\n\nimport { BackendError, isBackendError } from '../errors.js';\nimport { palbeRequest } from '../request.js';\nimport type { PalbeRuntime } from '../runtime.js';\nimport type { MessageStore, StoredMessage } from './history.js';\nimport { claimKeyPackages } from './keydir.js';\nimport type { MlsEngine } from './mls-engine.js';\nimport type { ResolvedReply, SentReceipt } from './types.js';\nimport {\n decodeUtf8,\n deviceIdBytes,\n encodeUtf8,\n fromBase64,\n directKey as makeDirectKey,\n mintClientMsgId,\n randomId,\n toBase64,\n} from './util.js';\nimport {\n type AddMemberBody,\n type ClaimResponse,\n type CommitAccepted,\n ContentType,\n type CreateDirectGroupBody,\n type CreateDirectGroupResponse,\n type CreateGroupBody,\n type CreateGroupResponse,\n type ListMembersResponse,\n MessagingPaths,\n type QueuePage,\n type RemoveMembersBody,\n type SendMessageBody,\n type WelcomeBody,\n type SentReceipt as WireReceipt,\n} from './wire.js';\n\n/** A resolved group as the SDK tracks it. `name` is always null on web (no exporter). */\nexport interface MessagingGroup {\n displayId: string;\n rfcGroupId: string; // base64 routing key\n currentEpoch: number;\n ownerUserId: string | null;\n directKey: string | null;\n name: string | null;\n}\n\nexport interface QuotePreview {\n kind: string;\n author_user_id: string;\n body?: string;\n body_truncated: boolean;\n}\nexport interface ReplyRef {\n v: number;\n client_msg_id: string;\n preview?: QuotePreview;\n}\n\n/** The typed E2E message envelope (matches the iOS wire format exactly). */\ninterface MsgEnvelope {\n v: 1;\n type: 'text';\n client_msg_id: string;\n text: string;\n reply_to?: ReplyRef;\n}\n\n/** Encode a text message into the wire envelope bytes. */\nexport function encodeEnvelope(args: {\n text: string;\n clientMsgId: string;\n replyTo?: ReplyRef | null;\n}): Uint8Array {\n const env: MsgEnvelope = {\n v: 1,\n type: 'text',\n client_msg_id: args.clientMsgId,\n text: args.text,\n ...(args.replyTo ? { reply_to: args.replyTo } : {}),\n };\n return encodeUtf8(JSON.stringify(env));\n}\n\n/** Decode an application plaintext (tolerant: bare-text or missing-v → legacy fallback). */\nexport function decodeEnvelope(bytes: Uint8Array): {\n text: string | null;\n clientMsgId: string;\n replyTo: ReplyRef | null;\n} {\n const s = decodeUtf8(bytes);\n try {\n const o = JSON.parse(s) as {\n type?: string;\n v?: number;\n text?: string;\n client_msg_id?: string;\n reply_to?: ReplyRef;\n };\n if (typeof o === 'object' && o !== null && o.type === 'text' && typeof o.v === 'number') {\n // Full v:1 envelope — extract all fields.\n return {\n text: o.text ?? null,\n clientMsgId: o.client_msg_id ?? '',\n replyTo: o.reply_to ?? null,\n };\n }\n if (typeof o === 'object' && o !== null && o.type === 'text') {\n // Legacy envelope (no v) — extract text, no reply context.\n return { text: o.text ?? null, clientMsgId: '', replyTo: null };\n }\n // A media envelope (not yet supported on web) → no text.\n if (typeof o === 'object' && o !== null && o.type === 'media')\n return { text: null, clientMsgId: '', replyTo: null };\n } catch {\n // legacy/bare plaintext\n }\n return { text: s, clientMsgId: '', replyTo: null };\n}\n\n/**\n * Resolve a reply reference against local history.\n *\n * `lookup(clientMsgId)` should return `{ text, senderUserId }` when the parent\n * is in local history, or `null` when not found.\n *\n * Spoof defense: when the parent IS found, the REAL text from local history wins\n * over the embedded `preview.body` — an attacker cannot make a reply appear to\n * quote different text than what was actually sent.\n */\nexport function resolveReply(\n ref: ReplyRef,\n lookup: (clientMsgId: string) => { text: string; senderUserId: string } | null,\n): ResolvedReply {\n const parent = lookup(ref.client_msg_id);\n if (parent !== null) {\n return {\n parentClientMsgId: ref.client_msg_id,\n state: 'resolved',\n quoteSenderUserId: parent.senderUserId,\n quoteText: parent.text,\n quoteKind: ref.preview?.kind ?? 'text',\n };\n }\n return {\n parentClientMsgId: ref.client_msg_id,\n state: 'unavailable',\n quoteSenderUserId: ref.preview?.author_user_id ?? '',\n quoteText: ref.preview?.body ?? null,\n quoteKind: ref.preview?.kind ?? 'text',\n };\n}\n\nexport interface RebaseOptions {\n maxAttempts: number;\n baseDelayMs: number;\n jitterMs: number;\n}\n\nconst DEFAULT_REBASE: RebaseOptions = { maxAttempts: 6, baseDelayMs: 80, jitterMs: 120 };\n\n/** Thrown when the rebase loop exhausts its budget (the change did NOT land). */\nexport class RebaseExhausted extends Error {\n constructor(\n readonly attempts: number,\n readonly lastKnownEpoch: number | null,\n ) {\n super(`commit rebase exhausted after ${attempts} attempts`);\n this.name = 'RebaseExhausted';\n }\n}\n\nexport class GroupMessaging {\n constructor(\n private readonly rt: PalbeRuntime,\n private readonly engine: MlsEngine,\n private readonly selfUserId: string,\n private readonly selfDeviceId: string,\n private readonly messageStore: MessageStore,\n private readonly rebase: RebaseOptions = DEFAULT_REBASE,\n ) {}\n\n // ── Create ──\n\n /** Create a group: mint the rfc_group_id, POST create, return the server group.\n * No name sealing on web (metadata omitted — server-blind, label fallback). */\n async createGroup(cipherSuite = 1): Promise<MessagingGroup> {\n const rfcGroupId = await this.engine.createGroup();\n const body: CreateGroupBody = {\n rfc_group_id: toBase64(rfcGroupId),\n cipher_suite: cipherSuite,\n creator_device_id: this.selfDeviceId,\n creator_leaf_index: 0,\n };\n const res = await palbeRequest<CreateGroupResponse>(this.rt, 'POST', MessagingPaths.groups, {\n body,\n });\n return {\n displayId: res.display_id,\n rfcGroupId: res.rfc_group_id,\n currentEpoch: res.current_epoch,\n ownerUserId: this.selfUserId,\n directKey: null,\n name: null,\n };\n }\n\n // ── Direct (1:1) get-or-create ──\n\n /** Materialize a DIRECT chat keyed by the stable direct_key (sorted user pair).\n * EXISTS → adopt (the Welcome drains via the pump); ABSENT → create + add peer. */\n async getOrCreateDirect(peerUserId: string): Promise<MessagingGroup> {\n const dk = makeDirectKey(this.selfUserId, peerUserId);\n const rfcGroupId = await this.engine.createGroup(); // reserved (used only if INSERT)\n const body: CreateDirectGroupBody = {\n direct_key: dk,\n peer_user_id: peerUserId,\n rfc_group_id: toBase64(rfcGroupId),\n cipher_suite: 1,\n creator_device_id: this.selfDeviceId,\n creator_leaf_index: 0,\n };\n const res = await palbeRequest<CreateDirectGroupResponse>(\n this.rt,\n 'POST',\n MessagingPaths.groupsDirect,\n { body },\n );\n\n if (res.exists === true) {\n // ADOPT — discard the reserved group; the founder's Welcome drains in.\n await this.engine.clearPendingCommit(rfcGroupId);\n return {\n displayId: res.display_id,\n rfcGroupId: res.rfc_group_id,\n currentEpoch: res.current_epoch,\n ownerUserId: res.owner_user_id ?? null,\n directKey: dk,\n name: null,\n };\n }\n\n // CREATE branch — the server bound the group to direct_key; add the peer.\n const group: MessagingGroup = {\n displayId: res.display_id,\n rfcGroupId: res.rfc_group_id,\n currentEpoch: res.current_epoch,\n ownerUserId: this.selfUserId,\n directKey: dk,\n name: null,\n };\n const claimed = await claimKeyPackages(this.rt, peerUserId);\n await this.addMember(group, peerUserId, claimed);\n return group;\n }\n\n // ── Members ──\n\n /** The group roster (device leaves). The coordinator dedups to users. */\n async listMembers(displayId: string): Promise<ListMembersResponse['members']> {\n const res = await palbeRequest<ListMembersResponse>(\n this.rt,\n 'GET',\n MessagingPaths.groupMembers(displayId),\n );\n return res.members;\n }\n\n /** Add a member: claim → stage the add commit → run the rebase loop. SP-1.6:\n * EVERY device of the user joins in ONE atomic commit (one shared Welcome). */\n async addMember(group: MessagingGroup, userId: string, claimed?: ClaimResponse): Promise<void> {\n const claim = claimed ?? (await claimKeyPackages(this.rt, userId));\n const adds = claim.key_packages\n .map((kp) => {\n try {\n return { deviceId: kp.device_id, kp: fromBase64(kp.key_package) };\n } catch {\n return null;\n }\n })\n .filter((x): x is { deviceId: string; kp: Uint8Array } => x !== null);\n if (adds.length === 0) {\n throw new BackendError('validation', {\n code: 'no_keypackage',\n message: 'no claimable key package for user',\n status: 422,\n });\n }\n const userIds = adds.map(() => userId);\n\n await this.commitWithRebase(group.rfcGroupId, async () => {\n const out = await this.engine.commitChanges(\n fromBase64(group.rfcGroupId),\n adds.map((a) => a.kp),\n [],\n );\n // One shared Welcome per added device (the engine returns one welcome for a\n // single-Welcome multi-add; route it to each added device).\n const welcomeB64 = out.welcome ? toBase64(out.welcome) : '';\n const welcomes: WelcomeBody[] = adds.map((a) => ({\n recipient_device_id: a.deviceId,\n recipient_user_id: userId,\n welcome_b64: welcomeB64,\n }));\n const memberBody: AddMemberBody = {\n op: 'add',\n commit_b64: toBase64(out.commit),\n target_device_ids: adds.map((a) => a.deviceId),\n leaf_indexes: out.addedLeafIndices,\n user_ids: userIds,\n key_package_refs: [],\n welcomes,\n };\n return {\n method: 'POST',\n path: MessagingPaths.groupMembers(group.displayId),\n body: memberBody,\n };\n });\n }\n\n /** Remove a user = evict ALL their device leaves in ONE commit (SP-1.6 D4). */\n async removeMember(group: MessagingGroup, userId: string): Promise<void> {\n const members = await this.listMembers(group.displayId);\n const deviceIds = members.filter((m) => m.user_id === userId).map((m) => m.device_id);\n if (deviceIds.length === 0) {\n throw new BackendError('validation', {\n code: 'not_a_member',\n message: 'user has no device in this group',\n status: 404,\n });\n }\n await this.stageMultiRemove(group, deviceIds, [userId]);\n }\n\n /** Leave = remove the CALLING device's leaf only (per-device self-leave). */\n async leaveGroup(group: MessagingGroup): Promise<void> {\n await this.stageMultiRemove(group, [this.selfDeviceId], [this.selfUserId]);\n }\n\n private async stageMultiRemove(\n group: MessagingGroup,\n deviceIds: string[],\n removedUserIds: string[],\n ): Promise<void> {\n const pathDeviceId = deviceIds[0];\n if (!pathDeviceId) throw new Error('no device to remove');\n await this.commitWithRebase(group.rfcGroupId, async () => {\n const out = await this.engine.commitChanges(\n fromBase64(group.rfcGroupId),\n [],\n deviceIds.map(deviceIdBytes),\n );\n const body: RemoveMembersBody = {\n op: 'remove',\n commit_b64: toBase64(out.commit),\n target_device_ids: deviceIds,\n user_ids: removedUserIds,\n };\n return {\n method: 'DELETE',\n path: MessagingPaths.groupMemberDevice(group.displayId, pathDeviceId),\n body,\n };\n });\n }\n\n // ── Send ──\n\n /** Send a text message. Encrypt at the current epoch, POST, return the receipt +\n * the client-minted `clientMsgId` (needed by the Chat layer for reply indexing).\n * NEVER rebases (a 422 stale_application surfaces to the caller). */\n async sendText(\n group: MessagingGroup,\n text: string,\n replyTo?: ReplyRef | null,\n ): Promise<{ receipt: SentReceipt; clientMsgId: string }> {\n const clientMsgId = mintClientMsgId();\n const plaintext = encodeEnvelope({ text, clientMsgId, replyTo });\n const ct = await this.engine.encryptApplication(fromBase64(group.rfcGroupId), plaintext);\n const body: SendMessageBody = {\n ciphertext_b64: toBase64(ct),\n client_idem_key: randomId(),\n };\n const wire = await palbeRequest<WireReceipt>(\n this.rt,\n 'POST',\n MessagingPaths.groupMessages(group.displayId),\n { body },\n );\n\n // Persist the OUTGOING copy — the ONLY durable form of an own-sent message\n // (the sender can never decrypt its own ciphertext). Best-effort: the send\n // already landed; a persist failure must not fail/re-trigger it.\n const stored: StoredMessage = {\n id: `${group.rfcGroupId}#${wire.server_seq}`,\n direction: 'outgoing',\n text,\n senderDeviceId: this.selfDeviceId,\n epoch: wire.epoch,\n serverSeq: wire.server_seq,\n at: Date.now(),\n clientMsgId,\n replyTo: replyTo\n ? {\n clientMsgId: replyTo.client_msg_id,\n previewBody: replyTo.preview?.body ?? null,\n previewAuthorUserId: replyTo.preview?.author_user_id ?? null,\n previewKind: replyTo.preview?.kind ?? 'text',\n }\n : null,\n };\n try {\n await this.messageStore.append(group.rfcGroupId, stored);\n } catch {\n // logged at the coordinator level; never fails the send\n }\n return { receipt: { serverSeq: wire.server_seq, epoch: wire.epoch }, clientMsgId };\n }\n\n // ── The rebase loop ──\n\n private async commitWithRebase(\n rfcGroupId: string,\n build: () => Promise<{ method: string; path: string; body: unknown }>,\n ): Promise<void> {\n const gidBytes = fromBase64(rfcGroupId);\n let lastEpoch: number | null = null;\n for (let attempt = 1; attempt <= this.rebase.maxAttempts; attempt++) {\n const pending = await build();\n try {\n await palbeRequest<CommitAccepted>(this.rt, pending.method, pending.path, {\n body: pending.body,\n });\n await this.engine.applyPendingCommit(gidBytes);\n return;\n } catch (e) {\n if (isBackendError(e) && e.code === 'epoch_conflict') {\n await this.engine.clearPendingCommit(gidBytes);\n const ce = (e.data as { current_epoch?: number } | undefined)?.current_epoch;\n if (typeof ce === 'number') lastEpoch = ce;\n if (attempt === this.rebase.maxAttempts) break;\n await this.drain(rfcGroupId);\n await this.backoff(attempt);\n continue; // rebuild + re-POST\n }\n throw e;\n }\n }\n await this.engine.clearPendingCommit(gidBytes);\n throw new RebaseExhausted(this.rebase.maxAttempts, lastEpoch);\n }\n\n /** Pull the device queue, process the winning commit(s) so the engine advances\n * to the new epoch, ack the commits (the delivery source owns app messages). */\n private async drain(rfcGroupId: string): Promise<void> {\n const page = await palbeRequest<QueuePage>(\n this.rt,\n 'GET',\n MessagingPaths.deviceQueue(this.selfDeviceId),\n );\n const gidBytes = fromBase64(rfcGroupId);\n const ackIds: string[] = [];\n for (const row of page.messages) {\n if (row.content_type !== ContentType.commit) continue;\n try {\n await this.engine.processIncoming(gidBytes, fromBase64(row.ciphertext_b64));\n } catch {\n // a non-applicable commit (own / consumed) — still ack so it isn't redelivered\n }\n ackIds.push(row.id);\n }\n if (ackIds.length > 0) {\n await palbeRequest<void>(this.rt, 'POST', MessagingPaths.deviceQueueAck(this.selfDeviceId), {\n body: { message_ids: ackIds },\n });\n }\n }\n\n private async backoff(attempt: number): Promise<void> {\n const base = this.rebase.baseDelayMs * attempt;\n const jitter = this.rebase.jitterMs === 0 ? 0 : Math.random() * this.rebase.jitterMs;\n const total = base + jitter;\n if (total <= 0) return;\n await new Promise((r) => setTimeout(r, total));\n }\n}\n","// The Chat — the one object a chat screen binds to. A DM or a group, uniformly.\n// Web idiom of the iOS `@Observable Chat`: an event-emitter exposing `onChange`\n// returning an Unsubscribe (matches flags/realtime), plus snapshot getters\n// (messages/members/typing/unreadCount/lastMessage). React hooks wrap it with\n// useSyncExternalStore.\n//\n// WhatsApp-lazy: a direct/group chat opens as a `.draft` (no network); the first\n// `send` MATERIALIZES it (claim → create/get-or-create → add → register), flips\n// to `.active`, wires live subscriptions, and hydrates durable history.\n\nimport type { IncomingMessage } from './delivery-source.js';\nimport { type MessagingGroup, type ReplyRef, resolveReply } from './group-messaging.js';\nimport type {\n ChatKind,\n ChatMember,\n ChatMessage,\n ChatMessageKind,\n ChatState,\n MessageDirection,\n PresenceState,\n ResolvedReply,\n SentReceipt,\n Unsubscribe,\n} from './types.js';\nimport { truncateQuote } from './util.js';\n\n/** The intended participants of a draft chat (held until the first send). */\nexport interface ChatDraft {\n mode: { kind: 'direct'; peerUserId: string } | { kind: 'group'; members: string[] };\n /** The reserved local id a draft shows before it materializes. */\n reservedId: string;\n}\n\n/** The seam the Chat talks to for the work needing the resolved machinery. */\nexport interface ChatBackend {\n selfUserId: string;\n /** Materialize a draft → the active group (DM get-or-create / group create+add). */\n materialize(draft: ChatDraft): Promise<MessagingGroup>;\n sendText(\n group: MessagingGroup,\n text: string,\n replyTo?: ReplyRef | null,\n ): Promise<{ receipt: SentReceipt; clientMsgId: string }>;\n history(group: MessagingGroup, limit: number, before?: number): Promise<ChatMessage[]>;\n members(group: MessagingGroup): Promise<ChatMember[]>;\n addMember(group: MessagingGroup, userId: string): Promise<void>;\n removeMember(group: MessagingGroup, userId: string): Promise<void>;\n leave(group: MessagingGroup): Promise<void>;\n registerActive(group: MessagingGroup): void;\n setTyping(group: MessagingGroup, isTyping: boolean): void;\n markRead(group: MessagingGroup, upToServerSeq: number): Promise<void>;\n /** Subscribe live message + conv (presence/typing/read) for an active group. */\n subscribeLive(group: MessagingGroup, chat: Chat): Unsubscribe;\n /** Resolve a sender device id → its owning user id (for senderUserId). */\n userIdForDevice(group: MessagingGroup, deviceId: string): Promise<string | null>;\n}\n\nexport class Chat {\n /** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */\n id: string;\n readonly kind: ChatKind;\n private _state: ChatState;\n\n private _group: MessagingGroup | null;\n private _draft: ChatDraft | null;\n private readonly backend: ChatBackend;\n\n private messageList: ChatMessage[] = [];\n private memberCache: ChatMember[] = [];\n private typingList: ChatMember[] = [];\n private presenceStates = new Map<string, PresenceState>();\n private readWatermark = -1;\n private titleOverride: string | null = null;\n\n private readonly seenKeys = new Set<string>();\n /** Index: clientMsgId → { text, senderUserId } for resolveReply lookups. */\n private readonly byClientMsgId = new Map<string, { text: string; senderUserId: string }>();\n private loadedEarliestSeq: number | null = null;\n private historyLoaded = false;\n private wired = false;\n private liveUnsub: Unsubscribe | null = null;\n\n private readonly listeners = new Set<() => void>();\n\n /** @internal — obtain via pb.messaging.directChat / groupChat / chat(id). */\n constructor(\n args:\n | { group: MessagingGroup; kind: ChatKind; backend: ChatBackend }\n | { draft: ChatDraft; kind: ChatKind; backend: ChatBackend },\n ) {\n this.backend = args.backend;\n if ('group' in args) {\n this.id = args.group.displayId;\n this.kind = args.kind;\n this._state = 'active';\n this._group = args.group;\n this._draft = null;\n this.seedMembersFromGroup(args.group);\n } else {\n this.id = args.draft.reservedId;\n this.kind = args.kind;\n this._state = 'draft';\n this._group = null;\n this._draft = args.draft;\n this.seedDraftMembers(args.draft);\n }\n }\n\n // ── Observability ──\n\n /** Subscribe to any change in this chat (messages/members/typing/state). */\n onChange(cb: () => void): Unsubscribe {\n this.listeners.add(cb);\n // Lazily wire live + hydrate history on first observe of an active chat.\n this.ensureWired();\n return () => this.listeners.delete(cb);\n }\n\n private emit(): void {\n for (const cb of this.listeners) cb();\n }\n\n // ── Snapshot getters ──\n\n get state(): ChatState {\n return this._state;\n }\n get isDirect(): boolean {\n return this.kind === 'direct';\n }\n get messages(): readonly ChatMessage[] {\n return this.messageList;\n }\n get members(): readonly ChatMember[] {\n return this.memberCache;\n }\n get typing(): readonly ChatMember[] {\n return this.typingList;\n }\n get lastMessage(): ChatMessage | null {\n return this.messageList.at(-1) ?? null;\n }\n get unreadCount(): number {\n return this.messageList.filter(\n (m) => m.direction === 'incoming' && m.serverSeq > this.readWatermark,\n ).length;\n }\n get title(): string {\n if (this.titleOverride) return this.titleOverride;\n if (this._group?.name) return this._group.name;\n if (this._draft) {\n const mode = this._draft.mode;\n if (mode.kind === 'direct') {\n const peer = this.memberCache.find((m) => m.userId === mode.peerUserId);\n return peer?.displayName ?? 'Chat';\n }\n return 'Group';\n }\n // Active group, no name (web has no exporter yet) — friendly fallback.\n const tail = this.id.startsWith('grp_') ? this.id.slice(4) : this.id;\n const short = tail.slice(-4);\n return short ? `Group ${short}` : 'Group';\n }\n\n presence(userId: string): PresenceState | null {\n return this.presenceStates.get(userId) ?? null;\n }\n\n // ── Wiring (live + history) ──\n\n private ensureWired(): void {\n if (this.wired || this._state !== 'active' || !this._group) return;\n this.wired = true;\n this.liveUnsub = this.backend.subscribeLive(this._group, this);\n void this.hydrateHistory();\n void this.refreshMembers();\n }\n\n private async hydrateHistory(): Promise<void> {\n if (this.historyLoaded || !this._group) return;\n this.historyLoaded = true;\n const entries = await this.backend.history(this._group, 50);\n this.mergeHistory(entries);\n }\n\n private mergeHistory(incoming: ChatMessage[]): void {\n let changed = false;\n // Pass 1: populate byClientMsgId from all new history rows (so replies within\n // the same page and against already-indexed live messages resolve correctly).\n for (const m of incoming) {\n if (m.serverSeq <= 0) continue;\n if (m.clientMsgId && m.text !== null) {\n // Index ANY row with a non-empty clientMsgId + non-null text, regardless\n // of direction. For incoming rows senderUserId is null — use '' so the\n // lookup entry exists and resolveReply returns state:'resolved' (real text\n // wins over attacker-controlled preview). Spoof defense covers peer messages.\n this.byClientMsgId.set(m.clientMsgId, {\n text: m.text,\n senderUserId: m.senderUserId ?? '',\n });\n }\n }\n // Pass 2: merge into messageList (replyTo is already resolved by coordinator).\n for (const m of incoming) {\n if (m.serverSeq <= 0) continue;\n const key = this.internalKey(m.serverSeq);\n if (this.seenKeys.has(key)) continue;\n this.seenKeys.add(key);\n this.messageList.push(m);\n changed = true;\n this.loadedEarliestSeq = Math.min(this.loadedEarliestSeq ?? m.serverSeq, m.serverSeq);\n }\n if (changed) {\n this.messageList.sort((a, b) => a.serverSeq - b.serverSeq);\n this.emit();\n }\n }\n\n /** @internal — called by the backend's live subscription. */\n async ingestLive(incoming: IncomingMessage): Promise<void> {\n if (!this._group) return;\n if (incoming.kind === 'commit') {\n await this.refreshMembers();\n return;\n }\n if (incoming.serverSeq <= 0) return; // welcome/proposal — not a row\n const key = this.internalKey(incoming.serverSeq);\n if (this.seenKeys.has(key)) return;\n this.seenKeys.add(key);\n let senderUser: string | null = null;\n if (incoming.senderDeviceId) {\n senderUser = await this.backend.userIdForDevice(this._group, incoming.senderDeviceId);\n }\n const direction: MessageDirection =\n senderUser !== null && senderUser === this.backend.selfUserId ? 'outgoing' : 'incoming';\n\n // clientMsgId and replyRef are decoded by the delivery source (decodeEnvelope)\n // and carried on IncomingMessage. Legacy/system messages carry empty string / null.\n const incomingClientMsgId = incoming.clientMsgId;\n const incomingReplyRef = incoming.replyRef;\n\n // Resolve reply if present.\n let resolvedReplyTo: ResolvedReply | null = null;\n if (incomingReplyRef) {\n resolvedReplyTo = resolveReply(incomingReplyRef, (id) => this.byClientMsgId.get(id) ?? null);\n }\n\n const msg: ChatMessage = {\n id: this.publicId(incoming.serverSeq),\n kind: this.kindOf(incoming),\n direction,\n senderUserId: senderUser,\n text: incoming.text,\n serverSeq: incoming.serverSeq,\n sentAt: incoming.receivedAt,\n clientMsgId: incomingClientMsgId,\n replyTo: resolvedReplyTo,\n };\n\n // Index non-empty clientMsgId for future resolveReply lookups.\n // Index ANY incoming message (senderUser may be null for peers); use '' for\n // the senderUserId so resolveReply still returns state:'resolved' (real text\n // wins over attacker-controlled preview). Spoof defense covers peer messages.\n if (incomingClientMsgId && incoming.text !== null) {\n this.byClientMsgId.set(incomingClientMsgId, {\n text: incoming.text,\n senderUserId: senderUser ?? '',\n });\n }\n\n this.messageList.push(msg);\n this.messageList.sort((a, b) => a.serverSeq - b.serverSeq);\n this.loadedEarliestSeq = Math.min(\n this.loadedEarliestSeq ?? incoming.serverSeq,\n incoming.serverSeq,\n );\n this.emit();\n }\n\n /** @internal — called by the backend's conv subscription. */\n applyConv(event: string, payload: Record<string, unknown>): void {\n const userId = typeof payload.user_id === 'string' ? payload.user_id : null;\n if (!userId || userId === this.backend.selfUserId) return;\n if (event === 'typing') {\n const isTyping = payload.typing === true;\n const member = this.memberCache.find((m) => m.userId === userId) ?? {\n id: userId,\n userId,\n displayName: null,\n role: 'member' as const,\n isSelf: false,\n };\n this.typingList = isTyping\n ? [...this.typingList.filter((m) => m.userId !== userId), member]\n : this.typingList.filter((m) => m.userId !== userId);\n this.emit();\n } else if (event === 'presence') {\n const online = payload.online === true;\n const ts = typeof payload.ts === 'number' ? payload.ts : null;\n this.presenceStates.set(userId, {\n userId,\n isOnline: online,\n lastSeenAt: ts ? new Date(ts * 1000) : null,\n });\n this.emit();\n }\n // 'read' receipts update sender read-ticks; the readers() accessor is a\n // Phase-2.5 extension (kept minimal here).\n }\n\n private kindOf(incoming: IncomingMessage): ChatMessageKind {\n if (incoming.kind === 'commit' || incoming.kind === 'proposal' || incoming.kind === 'welcome')\n return 'system';\n return 'text';\n }\n\n private internalKey(serverSeq: number): string {\n return this._group ? `${this._group.rfcGroupId}#${serverSeq}` : `draft#${serverSeq}`;\n }\n private publicId(serverSeq: number): string {\n return `${this.id}#${serverSeq}`;\n }\n\n /** Page older messages in. Returns how many were prepended. */\n async loadEarlier(limit = 50): Promise<number> {\n if (!this._group) return 0;\n const before = this.loadedEarliestSeq ?? undefined;\n const older = await this.backend.history(this._group, limit, before);\n const before0 = this.messageList.length;\n this.mergeHistory(older);\n return this.messageList.length - before0;\n }\n\n // ── Members ──\n\n private async refreshMembers(): Promise<void> {\n if (!this._group) return;\n const m = await this.backend.members(this._group);\n if (m.length > 0) {\n this.memberCache = m;\n this.emit();\n }\n }\n\n private seedMembersFromGroup(group: MessagingGroup): void {\n const seed: ChatMember[] = [\n {\n id: this.backend.selfUserId,\n userId: this.backend.selfUserId,\n displayName: null,\n role: 'owner',\n isSelf: true,\n },\n ];\n if (group.ownerUserId && group.ownerUserId !== this.backend.selfUserId) {\n seed.push({\n id: group.ownerUserId,\n userId: group.ownerUserId,\n displayName: null,\n role: 'owner',\n isSelf: false,\n });\n }\n this.memberCache = seed;\n }\n\n private seedDraftMembers(draft: ChatDraft): void {\n const seed: ChatMember[] = [\n {\n id: this.backend.selfUserId,\n userId: this.backend.selfUserId,\n displayName: null,\n role: draft.mode.kind === 'group' ? 'owner' : 'member',\n isSelf: true,\n },\n ];\n if (draft.mode.kind === 'direct') {\n seed.push({\n id: draft.mode.peerUserId,\n userId: draft.mode.peerUserId,\n displayName: null,\n role: 'member',\n isSelf: false,\n });\n } else {\n for (const uid of draft.mode.members) {\n seed.push({ id: uid, userId: uid, displayName: null, role: 'member', isSelf: false });\n }\n }\n this.memberCache = seed;\n }\n\n // ── Send (with WhatsApp lazy materialize) ──\n\n async send(text: string, opts?: { replyTo?: ChatMessage }): Promise<SentReceipt> {\n const group = await this.materializeIfNeeded();\n\n // Build ReplyRef from the parent ChatMessage if provided (and replyable).\n let replyRef: ReplyRef | null = null;\n let resolvedReplyTo: ResolvedReply | null = null;\n const parent = opts?.replyTo;\n if (parent?.clientMsgId && parent.kind !== 'system') {\n const [previewBody, wasTruncated] = parent.text ? truncateQuote(parent.text) : ['', false];\n replyRef = {\n v: 1,\n client_msg_id: parent.clientMsgId,\n preview: {\n kind: 'text',\n author_user_id: parent.senderUserId ?? '',\n body: previewBody || undefined,\n body_truncated: wasTruncated,\n },\n };\n // Resolve the reply against local history immediately (the parent IS local).\n resolvedReplyTo = resolveReply(replyRef, (id) => this.byClientMsgId.get(id) ?? null);\n }\n\n const { receipt, clientMsgId } = await this.backend.sendText(group, text, replyRef);\n this.appendOwnSend(text, receipt, clientMsgId, resolvedReplyTo);\n return receipt;\n }\n\n private appendOwnSend(\n text: string,\n receipt: SentReceipt,\n clientMsgId: string,\n resolvedReplyTo: ResolvedReply | null,\n ): void {\n if (receipt.serverSeq <= 0) return;\n const key = this.internalKey(receipt.serverSeq);\n if (this.seenKeys.has(key)) return;\n this.seenKeys.add(key);\n\n // Index own-sent clientMsgId for future resolveReply lookups (self-reply support).\n if (clientMsgId) {\n this.byClientMsgId.set(clientMsgId, { text, senderUserId: this.backend.selfUserId });\n }\n\n this.messageList.push({\n id: this.publicId(receipt.serverSeq),\n kind: 'text',\n direction: 'outgoing',\n senderUserId: this.backend.selfUserId,\n text,\n serverSeq: receipt.serverSeq,\n sentAt: new Date(),\n clientMsgId,\n replyTo: resolvedReplyTo,\n });\n this.messageList.sort((a, b) => a.serverSeq - b.serverSeq);\n this.emit();\n }\n\n private async materializeIfNeeded(): Promise<MessagingGroup> {\n if (this._group) return this._group;\n if (!this._draft) throw new Error('messaging_not_configured');\n const group = await this.backend.materialize(this._draft);\n this._group = group;\n this.id = group.displayId;\n this._state = 'active';\n this._draft = null;\n this.backend.registerActive(group);\n this.ensureWired();\n this.emit();\n return group;\n }\n\n // ── Membership (groups only) ──\n\n async addMemberUser(userId: string): Promise<void> {\n if (this.kind !== 'group') throw new Error('chat_is_direct');\n const group = await this.materializeIfNeeded();\n await this.backend.addMember(group, userId);\n await this.refreshMembers();\n }\n\n async removeMemberUser(userId: string): Promise<void> {\n if (this.kind !== 'group') throw new Error('chat_is_direct');\n if (!this._group) throw new Error('messaging_not_configured');\n await this.backend.removeMember(this._group, userId);\n await this.refreshMembers();\n }\n\n async leave(): Promise<void> {\n if (!this._group) return;\n await this.backend.leave(this._group);\n this.liveUnsub?.();\n this.liveUnsub = null;\n }\n\n // ── Imperative ──\n\n setTyping(isTyping: boolean): void {\n if (this._group) this.backend.setTyping(this._group, isTyping);\n }\n\n async markRead(message: ChatMessage): Promise<void> {\n if (!this._group) return;\n await this.backend.markRead(this._group, message.serverSeq);\n this.readWatermark = Math.max(this.readWatermark, message.serverSeq);\n this.emit();\n }\n}\n","// The live delivery source — the web mirror of iOS MessageDeliverySource.\n// Subscribes the device wake topic `messaging:device:<id>` over pb.realtime\n// (new_message wake → pumpOnce), and on each pump drains:\n//\n// 1. welcomes → engine.joinFromWelcome → register the group + emit `welcome`\n// 2. device queue → for each row:\n// - COMMIT: processIncoming (applies), bump epoch, emit `commit`\n// - APPLICATION: decrypt → persist plaintext → emit `message`\n// 3. ack everything consumed.\n//\n// The realtime wake is notification-only; the authoritative messages come from\n// the authenticated queue pull (never the broadcast payload), exactly as iOS.\n//\n// SERVER WIRE GAP (same as iOS): queue rows do NOT yet carry rfc_group_id or the\n// fanned declaration. When absent, route a blob to the SOLE known group (the\n// common 1:1/small-group case). The reconciliation-gate hook (describeIncoming)\n// is ready for the day the server fans the declaration — a pure server change.\n\nimport { palbeRequest } from '../request.js';\nimport type { PalbeRuntime } from '../runtime.js';\nimport {\n decodeEnvelope,\n GroupMessaging,\n type MessagingGroup,\n type ReplyRef,\n} from './group-messaging.js';\nimport type { MessageStore, StoredMessage } from './history.js';\nimport type { MlsEngine } from './mls-engine.js';\nimport type { GroupRegistry } from './registry.js';\nimport { fromBase64, toBase64 } from './util.js';\nimport {\n ContentType,\n MessagingPaths,\n type QueuedMessage,\n type QueuePage,\n type WelcomePage,\n} from './wire.js';\n\n/** A decoded incoming message handed to chat listeners. */\nexport interface IncomingMessage {\n kind: 'application' | 'commit' | 'proposal' | 'welcome';\n group: MessagingGroup;\n text: string | null;\n senderDeviceId: string | null;\n epoch: number;\n serverSeq: number;\n receivedAt: Date;\n /** The client-minted idempotency id from the decoded envelope (empty string for legacy/system). */\n clientMsgId: string;\n /** The decoded reply reference, if present in the envelope. */\n replyRef: ReplyRef | null;\n}\n\ntype MessageListener = (m: IncomingMessage) => void;\ntype ConvEvent = { event: string; payload: Record<string, unknown> };\ntype ConvListener = (e: ConvEvent) => void;\n\n/** A tiny per-group fan-out hub (the web analogue of the iOS message/conv hubs). */\nexport class MessageHub {\n private readonly messageListeners = new Map<string, Set<MessageListener>>();\n private readonly convListeners = new Map<string, Set<ConvListener>>();\n\n onMessage(displayId: string, fn: MessageListener): () => void {\n return this.add(this.messageListeners, displayId, fn);\n }\n emitMessage(displayId: string, m: IncomingMessage): void {\n for (const fn of this.messageListeners.get(displayId) ?? []) fn(m);\n }\n onConv(displayId: string, fn: ConvListener): () => void {\n return this.add(this.convListeners, displayId, fn);\n }\n emitConv(displayId: string, e: ConvEvent): void {\n for (const fn of this.convListeners.get(displayId) ?? []) fn(e);\n }\n\n private add<T>(map: Map<string, Set<T>>, key: string, fn: T): () => void {\n let set = map.get(key);\n if (!set) {\n set = new Set();\n map.set(key, set);\n }\n set.add(fn);\n return () => {\n set?.delete(fn);\n };\n }\n}\n\nexport class MessageDeliverySource {\n private started = false;\n private wakeUnsub: (() => void) | null = null;\n // Per-observed-group conv subscription + heartbeat (presence/typing/read).\n private readonly observed = new Map<\n string,\n { unsub: () => void; heartbeat?: ReturnType<typeof setInterval> }\n >();\n\n constructor(\n private readonly rt: PalbeRuntime,\n private readonly engine: MlsEngine,\n private readonly hub: MessageHub,\n private readonly registry: GroupRegistry,\n private readonly messageStore: MessageStore,\n private readonly deviceId: string,\n private readonly selfUserId: string,\n ) {}\n\n /** Subscribe the device wake topic + run an initial drain. Idempotent. */\n async start(): Promise<void> {\n if (this.started) return;\n this.started = true;\n try {\n const channel = this.rt.realtime.channel(`messaging:device:${this.deviceId}`);\n const sub = channel.on('new_message', () => {\n void this.pumpOnce();\n });\n // Re-pump on every (re)connect to catch wakes missed while offline.\n const offConn = this.rt.realtime.status.onChange((s) => {\n if (s.state === 'connected') void this.pumpOnce();\n });\n this.wakeUnsub = () => {\n sub.cancel();\n offConn();\n };\n } catch {\n // No WebSocket (SSR/Node) — the wake source is inert; explicit pumps still work.\n }\n await this.pumpOnce();\n }\n\n stop(): void {\n this.wakeUnsub?.();\n this.wakeUnsub = null;\n for (const [, o] of this.observed) {\n o.unsub();\n if (o.heartbeat) clearInterval(o.heartbeat);\n }\n this.observed.clear();\n this.started = false;\n }\n\n /** One full drain: welcomes → queue (decrypt + emit) → ack. Never throws. */\n async pumpOnce(): Promise<void> {\n try {\n await this.pullWelcomes();\n } catch {\n // transient — the next wake retries\n }\n try {\n await this.pullQueue();\n } catch {\n // transient — the next wake retries\n }\n }\n\n // ── Welcomes ──\n\n private async pullWelcomes(): Promise<void> {\n const page = await palbeRequest<WelcomePage>(\n this.rt,\n 'GET',\n MessagingPaths.deviceWelcomes(this.deviceId),\n );\n for (const w of page.welcomes) {\n try {\n const gidBytes = await this.engine.joinFromWelcome(fromBase64(w.welcome_b64));\n // Register under the server-minted grp_ display_id (url-safe), NEVER the\n // base64 rfc_group_id (which would 404 the members route — M3 #7).\n const group: MessagingGroup = {\n displayId: w.display_id,\n rfcGroupId: toBase64(gidBytes),\n currentEpoch: w.added_at_epoch,\n ownerUserId: null,\n directKey: null,\n name: null,\n };\n this.registry.register(group);\n this.hub.emitMessage(group.displayId, {\n kind: 'welcome',\n group,\n text: null,\n senderDeviceId: null,\n epoch: w.added_at_epoch,\n serverSeq: 0,\n receivedAt: new Date(),\n clientMsgId: '',\n replyRef: null,\n });\n } catch {\n // POISON-PILL GUARD: a re-served welcome whose one-time KeyPackage was\n // already consumed (a duplicate for a membership we hold) fails join\n // forever — skip quietly when the group is already known.\n const known = this.registry.all().some((g) => g.displayId === w.display_id);\n if (!known) {\n // genuinely unknown group — swallow (best-effort pump); next wake retries\n }\n }\n }\n }\n\n // ── Queue ──\n\n private async pullQueue(): Promise<void> {\n const page = await palbeRequest<QueuePage>(\n this.rt,\n 'GET',\n MessagingPaths.deviceQueue(this.deviceId),\n );\n const ackIds: string[] = [];\n for (const row of page.messages) {\n if (await this.process(row)) ackIds.push(row.id);\n }\n if (ackIds.length > 0) {\n await palbeRequest<void>(this.rt, 'POST', MessagingPaths.deviceQueueAck(this.deviceId), {\n body: { message_ids: ackIds },\n });\n }\n }\n\n /** Process one queue row; returns true if consumed (should ack). */\n private async process(row: QueuedMessage): Promise<boolean> {\n let blob: Uint8Array;\n try {\n blob = fromBase64(row.ciphertext_b64);\n } catch {\n return true; // junk → drop\n }\n // Resolve the target group: prefer the row's own rfc, else the sole group.\n const group =\n (row.rfc_group_id_b64 ? this.registry.group(row.rfc_group_id_b64) : undefined) ??\n this.registry.soleGroup();\n if (!group) return false; // unresolvable → leave unacked\n\n try {\n const received = await this.engine.processIncoming(fromBase64(group.rfcGroupId), blob);\n if (received.type === 'application') {\n const { text, clientMsgId, replyTo } = decodeEnvelope(received.data);\n const stored: StoredMessage = {\n id: `${group.rfcGroupId}#${row.server_seq}`,\n direction: 'incoming',\n text,\n senderDeviceId: row.sender_device_id ?? null,\n epoch: row.epoch,\n serverSeq: row.server_seq,\n at: Date.now(),\n clientMsgId: clientMsgId || undefined,\n replyTo: replyTo\n ? {\n clientMsgId: replyTo.client_msg_id,\n previewBody: replyTo.preview?.body ?? null,\n previewAuthorUserId: replyTo.preview?.author_user_id ?? null,\n previewKind: replyTo.preview?.kind ?? 'text',\n }\n : null,\n };\n try {\n await this.messageStore.append(group.rfcGroupId, stored);\n } catch {\n return false; // persist failed → NOT acked, re-served next pump\n }\n this.hub.emitMessage(group.displayId, {\n kind: 'application',\n group,\n text,\n senderDeviceId: row.sender_device_id ?? null,\n epoch: row.epoch,\n serverSeq: row.server_seq,\n receivedAt: new Date(),\n clientMsgId,\n replyRef: replyTo,\n });\n return true;\n }\n if (received.type === 'commitApplied') {\n const newEpoch = Math.max(row.epoch, group.currentEpoch + 1);\n this.registry.bumpEpoch(group.rfcGroupId, newEpoch);\n const fresh = this.registry.group(group.rfcGroupId) ?? group;\n this.hub.emitMessage(group.displayId, {\n kind: 'commit',\n group: fresh,\n text: null,\n senderDeviceId: row.sender_device_id ?? null,\n epoch: newEpoch,\n serverSeq: 0,\n receivedAt: new Date(),\n clientMsgId: '',\n replyRef: null,\n });\n return true;\n }\n return true; // proposal / other — consumed\n } catch (e) {\n // Own-sent echo or consumed-key: mls-rs refuses to decrypt own ciphertext /\n // a replayed generation. ACK + skip (the durable copy is the send path's).\n if (isOwnEchoOrConsumed(e)) return true;\n return false; // decode/transient → leave unacked for retry\n }\n }\n\n // ── Conv topic (presence / typing / read) ──\n\n /** Begin observing a group's conv topic; fans events into the hub. Idempotent. */\n observeConversation(group: MessagingGroup): void {\n if (this.observed.has(group.displayId)) return;\n try {\n const channel = this.rt.realtime.channel(`messaging:conv:${group.rfcGroupId}`);\n const subs = ['presence', 'typing', 'read'].map((ev) =>\n channel.on(ev, (payload) => {\n this.hub.emitConv(group.displayId, { event: ev, payload });\n }),\n );\n // Announce online + heartbeat every 25s.\n const emitPresence = (online: boolean) =>\n channel.send('presence', {\n user_id: this.selfUserId,\n device_id: this.deviceId,\n online,\n ts: Date.now() / 1000,\n });\n emitPresence(true);\n const heartbeat = setInterval(() => emitPresence(true), 25_000);\n this.observed.set(group.displayId, {\n unsub: () => {\n for (const s of subs) s.cancel();\n },\n heartbeat,\n });\n } catch {\n // no socket (SSR) — presence/typing simply unavailable\n }\n }\n\n /** Announce typing on a group's conv topic (the app calls per keystroke). */\n setTyping(group: MessagingGroup, isTyping: boolean): void {\n this.observeConversation(group);\n try {\n this.rt.realtime.channel(`messaging:conv:${group.rfcGroupId}`).send('typing', {\n user_id: this.selfUserId,\n device_id: this.deviceId,\n typing: isTyping,\n });\n } catch {\n // no socket\n }\n }\n\n /** Advance this device's read cursor (idempotent; the server clamps). */\n async markRead(group: MessagingGroup, upToServerSeq: number): Promise<void> {\n await palbeRequest<void>(this.rt, 'POST', MessagingPaths.groupRead(group.displayId), {\n body: { read_seq: upToServerSeq, read_epoch: group.currentEpoch, is_private: false },\n });\n }\n}\n\n/** mls-rs own-echo (\"message from self can't be processed\") or consumed-key\n * (\"key not available, invalid generation\") — both ACK-and-skip on the receiver. */\nfunction isOwnEchoOrConsumed(e: unknown): boolean {\n const msg = e instanceof Error ? e.message : String(e);\n return msg.includes('message from self') || msg.includes('key not available, invalid generation');\n}\n\n// keep GroupMessaging/ContentType referenced for the wire-gap declaration hook\nvoid GroupMessaging;\nvoid ContentType;\n","// Local decrypted-message history + the group catalog — the web mirror of iOS\n// MessageStore + GroupCatalog. MLS decrypt-once means a received message's key\n// is erased on first decrypt, so you can NEVER refetch+redecrypt it from the\n// server: the decrypted plaintext MUST be persisted locally (sealed at rest by\n// the durable KV — IndexedDB+WebCrypto-AES-GCM in the browser). The own-sent\n// copy is likewise the only durable form (the sender can't decrypt its own\n// ciphertext).\n\nimport type { DurableKV } from './storage.js';\nimport type { MessageDirection } from './types.js';\nimport { decodeUtf8, encodeUtf8 } from './util.js';\n\n/** A durable stored message (one row in the local sealed transcript). */\nexport interface StoredMessage {\n /** Stable across reloads: `<rfcGroupId>#<serverSeq>`. */\n id: string;\n direction: MessageDirection;\n text: string | null;\n senderDeviceId: string | null;\n epoch: number;\n serverSeq: number;\n /** ms since epoch. */\n at: number;\n /**\n * Client-minted idempotency id from the decoded envelope.\n * Optional for backward-compat: old persisted rows without this key\n * are treated as '' (no id) on read.\n */\n clientMsgId?: string;\n /**\n * The decoded reply reference, if the message was a reply.\n * Optional for backward-compat: old persisted rows without this key\n * are treated as null on read.\n */\n replyTo?: {\n clientMsgId: string;\n previewBody: string | null;\n previewAuthorUserId: string | null;\n previewKind: string;\n } | null;\n}\n\n/** Persist decrypted messages per group, newest-last, deduped by id. */\nexport class MessageStore {\n constructor(private readonly kv: DurableKV) {}\n\n private key(rfcGroupId: string): string {\n return `msgs:${rfcGroupId}`;\n }\n\n /** Append (idempotent on id) and persist the group's transcript. */\n async append(rfcGroupId: string, msg: StoredMessage): Promise<void> {\n const list = await this.load(rfcGroupId);\n if (list.some((m) => m.id === msg.id)) return; // dedup by stable id\n list.push(msg);\n list.sort((a, b) => a.serverSeq - b.serverSeq);\n await this.save(rfcGroupId, list);\n }\n\n /** True if a message id is already durable (the consumed-key recovery check). */\n async contains(rfcGroupId: string, id: string): Promise<boolean> {\n const list = await this.load(rfcGroupId);\n return list.some((m) => m.id === id);\n }\n\n /**\n * Read history for a group, newest-last. `before` pages older: returns up to\n * `limit` rows with serverSeq < before. nil `before` returns the newest page.\n */\n async history(rfcGroupId: string, limit: number, before?: number): Promise<StoredMessage[]> {\n const list = await this.load(rfcGroupId);\n const filtered = before == null ? list : list.filter((m) => m.serverSeq < before);\n return filtered.slice(Math.max(0, filtered.length - limit));\n }\n\n async load(rfcGroupId: string): Promise<StoredMessage[]> {\n const raw = await this.kv.get(this.key(rfcGroupId));\n if (!raw) return [];\n try {\n return JSON.parse(decodeUtf8(raw)) as StoredMessage[];\n } catch {\n return [];\n }\n }\n\n private async save(rfcGroupId: string, list: StoredMessage[]): Promise<void> {\n await this.kv.set(this.key(rfcGroupId), encodeUtf8(JSON.stringify(list)));\n }\n\n async wipe(): Promise<void> {\n for (const k of await this.kv.keys('msgs:')) await this.kv.delete(k);\n }\n}\n\n/** A persisted group entry (the catalog row — survives reloads). */\nexport interface CatalogGroup {\n displayId: string;\n rfcGroupId: string;\n currentEpoch: number;\n ownerUserId: string | null;\n directKey: string | null;\n name: string | null;\n}\n\n/** Durable list of known groups (the web mirror of iOS GroupCatalog), so a\n * reload hydrates the chat list + lets queue rows route before the first pump. */\nexport class GroupCatalog {\n constructor(private readonly kv: DurableKV) {}\n\n async upsert(g: CatalogGroup): Promise<void> {\n await this.kv.set(`cat:${g.rfcGroupId}`, encodeUtf8(JSON.stringify(g)));\n }\n\n async remove(rfcGroupId: string): Promise<void> {\n await this.kv.delete(`cat:${rfcGroupId}`);\n }\n\n async all(): Promise<CatalogGroup[]> {\n const keys = await this.kv.keys('cat:');\n const out: CatalogGroup[] = [];\n for (const k of keys) {\n const raw = await this.kv.get(k);\n if (!raw) continue;\n try {\n out.push(JSON.parse(decodeUtf8(raw)) as CatalogGroup);\n } catch {\n // skip a corrupt row\n }\n }\n return out;\n }\n\n async wipe(): Promise<void> {\n for (const k of await this.kv.keys('cat:')) await this.kv.delete(k);\n }\n}\n","/**\n * Browser binding of [`PalbeMlsClient`]. Holds the same `Arc<PalbeMlsClient>`\n * the native FFI would; every method delegates to the engine.\n */\nexport class MlsClient {\n static __wrap(ptr) {\n const obj = Object.create(MlsClient.prototype);\n obj.__wbg_ptr = ptr;\n MlsClientFinalization.register(obj, obj.__wbg_ptr, obj);\n return obj;\n }\n __destroy_into_raw() {\n const ptr = this.__wbg_ptr;\n this.__wbg_ptr = 0;\n MlsClientFinalization.unregister(this);\n return ptr;\n }\n free() {\n const ptr = this.__destroy_into_raw();\n wasm.__wbg_mlsclient_free(ptr, 0);\n }\n /**\n * The MLS Capabilities wire bytes (the keydir's `capabilities`).\n * @returns {Uint8Array}\n */\n capabilities() {\n const ret = wasm.mlsclient_capabilities(this.__wbg_ptr);\n var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v1;\n }\n /**\n * Create a new group at epoch 0. `groupId` `undefined`/`null` lets mls-rs\n * mint a globally-unique id (the rfc_group_id routing key).\n * @param {Uint8Array | null} [group_id]\n * @returns {MlsGroup}\n */\n createGroup(group_id) {\n var ptr0 = isLikeNone(group_id) ? 0 : passArray8ToWasm0(group_id, wasm.__wbindgen_malloc);\n var len0 = WASM_VECTOR_LEN;\n const ret = wasm.mlsclient_createGroup(this.__wbg_ptr, ptr0, len0);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return MlsGroup.__wrap(ret[0]);\n }\n /**\n * The MLS Credential wire bytes (the keydir's `credential`).\n * @returns {Uint8Array}\n */\n credential() {\n const ret = wasm.mlsclient_credential(this.__wbg_ptr);\n var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v1;\n }\n /**\n * The MLS-encoded enrollment material `{ signatureKey, credential,\n * capabilities }` (byte fields as arrays-of-numbers — wrap in Uint8Array as\n * needed). Base64 these into the SP-0 keydir enroll body.\n * @returns {any}\n */\n enrollmentMaterial() {\n const ret = wasm.mlsclient_enrollmentMaterial(this.__wbg_ptr);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return takeFromExternrefTable0(ret[0]);\n }\n /**\n * Mint a one-use KeyPackage (RAW RFC 9420 §10 bytes) — uploaded to the\n * keydir, consumed by an adder's `addMember`.\n * @returns {Uint8Array}\n */\n generateKeyPackage() {\n const ret = wasm.mlsclient_generateKeyPackage(this.__wbg_ptr);\n if (ret[3]) {\n throw takeFromExternrefTable0(ret[2]);\n }\n var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v1;\n }\n /**\n * Join a group from a Welcome (opaque MLSMessage bytes).\n * @param {Uint8Array} welcome\n * @returns {MlsGroup}\n */\n joinGroup(welcome) {\n const ptr0 = passArray8ToWasm0(welcome, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.mlsclient_joinGroup(this.__wbg_ptr, ptr0, len0);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return MlsGroup.__wrap(ret[0]);\n }\n /**\n * Re-hydrate a previously-persisted group by its `groupId` (restart-safe\n * reload of an already-joined group).\n * @param {Uint8Array} group_id\n * @returns {MlsGroup}\n */\n loadGroup(group_id) {\n const ptr0 = passArray8ToWasm0(group_id, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.mlsclient_loadGroup(this.__wbg_ptr, ptr0, len0);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return MlsGroup.__wrap(ret[0]);\n }\n /**\n * The serialized STABLE signature keypair (secret-bearing). Persist sealed\n * and pass back as `signatureKeypair` on restart so the device keeps the\n * SAME MLS signing identity.\n * @returns {Uint8Array}\n */\n signatureKeypair() {\n const ret = wasm.mlsclient_signatureKeypair(this.__wbg_ptr);\n var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v1;\n }\n /**\n * The device's STABLE signature PUBLIC key bytes (the keydir's\n * `signature_key`).\n * @returns {Uint8Array}\n */\n signaturePublicKey() {\n const ret = wasm.mlsclient_signaturePublicKey(this.__wbg_ptr);\n var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v1;\n }\n}\nif (Symbol.dispose) MlsClient.prototype[Symbol.dispose] = MlsClient.prototype.free;\n\n/**\n * Browser binding of [`PalbeMlsGroup`].\n */\nexport class MlsGroup {\n static __wrap(ptr) {\n const obj = Object.create(MlsGroup.prototype);\n obj.__wbg_ptr = ptr;\n MlsGroupFinalization.register(obj, obj.__wbg_ptr, obj);\n return obj;\n }\n __destroy_into_raw() {\n const ptr = this.__wbg_ptr;\n this.__wbg_ptr = 0;\n MlsGroupFinalization.unregister(this);\n return ptr;\n }\n free() {\n const ptr = this.__destroy_into_raw();\n wasm.__wbg_mlsgroup_free(ptr, 0);\n }\n /**\n * Stage a member add. Returns `{ commit, welcome?, addedLeafIndices }`\n * (byte fields as arrays-of-numbers). The group is PENDING after this.\n * @param {Uint8Array} key_package_msg\n * @returns {any}\n */\n addMember(key_package_msg) {\n const ptr0 = passArray8ToWasm0(key_package_msg, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.mlsgroup_addMember(this.__wbg_ptr, ptr0, len0);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return takeFromExternrefTable0(ret[0]);\n }\n /**\n * Apply the staged commit (advances the local epoch). Call ONLY after the\n * server accepted the commit.\n */\n applyPendingCommit() {\n const ret = wasm.mlsgroup_applyPendingCommit(this.__wbg_ptr);\n if (ret[1]) {\n throw takeFromExternrefTable0(ret[0]);\n }\n }\n /**\n * Discard the staged commit without advancing (the 409-rebase path).\n */\n clearPendingCommit() {\n wasm.mlsgroup_clearPendingCommit(this.__wbg_ptr);\n }\n /**\n * Stage a SINGLE atomic add+remove commit. `addKeyPackages` is an\n * `Array<Uint8Array>` of one-use key packages; `removeMemberIds` an\n * `Array<Uint8Array>` of member identity bytes. Returns the same\n * `AddResult` shape as `addMember`. PENDING after this.\n * @param {Uint8Array[]} add_key_packages\n * @param {Uint8Array[]} remove_member_ids\n * @returns {any}\n */\n commitChanges(add_key_packages, remove_member_ids) {\n const ptr0 = passArrayJsValueToWasm0(add_key_packages, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArrayJsValueToWasm0(remove_member_ids, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n const ret = wasm.mlsgroup_commitChanges(this.__wbg_ptr, ptr0, len0, ptr1, len1);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return takeFromExternrefTable0(ret[0]);\n }\n /**\n * The current (applied) epoch as a JS BigInt (u64 exceeds safe-integer).\n * @returns {bigint}\n */\n currentEpoch() {\n const ret = wasm.mlsgroup_currentEpoch(this.__wbg_ptr);\n return BigInt.asUintN(64, ret);\n }\n /**\n * Inspect an incoming Commit and return the membership delta it WOULD\n * effect WITHOUT applying it (the reconciliation gate's crypto-truth).\n * Returns `{ kind, addedIdentities, removedIdentities }`.\n * @param {Uint8Array} commit_msg\n * @returns {any}\n */\n describeIncomingCommit(commit_msg) {\n const ptr0 = passArray8ToWasm0(commit_msg, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.mlsgroup_describeIncomingCommit(this.__wbg_ptr, ptr0, len0);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return takeFromExternrefTable0(ret[0]);\n }\n /**\n * Encrypt an application message at the current epoch. Returns the opaque\n * MLSMessage bytes (PrivateMessage).\n * @param {Uint8Array} plaintext\n * @returns {Uint8Array}\n */\n encryptApplicationMessage(plaintext) {\n const ptr0 = passArray8ToWasm0(plaintext, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.mlsgroup_encryptApplicationMessage(this.__wbg_ptr, ptr0, len0);\n if (ret[3]) {\n throw takeFromExternrefTable0(ret[2]);\n }\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n }\n /**\n * Derive the group's current-epoch exporter secret (RFC 9420 §8.5) — the SAME\n * engine call iOS uses (uniffi `exportSecret`), so a cross-platform call/group\n * derives a byte-identical key. The SP-4 web call sets this as the LiveKit\n * frame key (server + SFU stay blind); also group-name/media sealing.\n * @param {Uint8Array} label\n * @param {Uint8Array} context\n * @param {bigint} len\n * @returns {Uint8Array}\n */\n exportSecret(label, context, len) {\n const ptr0 = passArray8ToWasm0(label, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArray8ToWasm0(context, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n const ret = wasm.mlsgroup_exportSecret(this.__wbg_ptr, ptr0, len0, ptr1, len1, len);\n if (ret[3]) {\n throw takeFromExternrefTable0(ret[2]);\n }\n var v3 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v3;\n }\n /**\n * The MLS group id (the rfc_group_id routing key / storage key).\n * @returns {Uint8Array}\n */\n groupId() {\n const ret = wasm.mlsgroup_groupId(this.__wbg_ptr);\n var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v1;\n }\n /**\n * True while a staged (unapplied) commit exists.\n * @returns {boolean}\n */\n hasPendingCommit() {\n const ret = wasm.mlsgroup_hasPendingCommit(this.__wbg_ptr);\n return ret !== 0;\n }\n /**\n * Process an incoming MLSMessage and apply its effect. Returns a\n * `ReceivedMessage` plain object (serde-tagged enum): e.g.\n * `{ Application: { sender, data } }`, `{ CommitApplied: { epoch,\n * removedSelf } }`, `\"Proposal\"`, or `\"Other\"`.\n * @param {Uint8Array} message\n * @returns {any}\n */\n processIncomingMessage(message) {\n const ptr0 = passArray8ToWasm0(message, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.mlsgroup_processIncomingMessage(this.__wbg_ptr, ptr0, len0);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return takeFromExternrefTable0(ret[0]);\n }\n /**\n * Stage a member remove (by the member's identity bytes). Returns\n * `{ commit }`. PENDING after this.\n * @param {Uint8Array} member_id\n * @returns {any}\n */\n removeMember(member_id) {\n const ptr0 = passArray8ToWasm0(member_id, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.mlsgroup_removeMember(this.__wbg_ptr, ptr0, len0);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return takeFromExternrefTable0(ret[0]);\n }\n /**\n * Persist the current group state through the JS group storage callback.\n */\n writeToStorage() {\n const ret = wasm.mlsgroup_writeToStorage(this.__wbg_ptr);\n if (ret[1]) {\n throw takeFromExternrefTable0(ret[0]);\n }\n }\n}\nif (Symbol.dispose) MlsGroup.prototype[Symbol.dispose] = MlsGroup.prototype.free;\n\n/**\n * Mint a device MLS client (browser binding of [`engine::generate_client`]).\n *\n * - `id`: the member identity bytes (the SP-0 device id `mdv_`).\n * - `group_storage`: a JS object implementing `IGroupStateStorage`.\n * - `key_package_storage`: a JS object implementing `IKeyPackageStorage`.\n * - `signature_keypair`: `undefined`/`null` on first run (a fresh keypair is\n * minted), or the previously-persisted serialized secret-key bytes on restart.\n * @param {Uint8Array} id\n * @param {IGroupStateStorage} group_storage\n * @param {IKeyPackageStorage} key_package_storage\n * @param {Uint8Array | null} [signature_keypair]\n * @returns {MlsClient}\n */\nexport function generateClient(id, group_storage, key_package_storage, signature_keypair) {\n const ptr0 = passArray8ToWasm0(id, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n var ptr1 = isLikeNone(signature_keypair) ? 0 : passArray8ToWasm0(signature_keypair, wasm.__wbindgen_malloc);\n var len1 = WASM_VECTOR_LEN;\n const ret = wasm.generateClient(ptr0, len0, group_storage, key_package_storage, ptr1, len1);\n if (ret[2]) {\n throw takeFromExternrefTable0(ret[1]);\n }\n return MlsClient.__wrap(ret[0]);\n}\n\n/**\n * Install the browser console panic hook so a Rust panic shows a readable\n * stack in the devtools console instead of an opaque `unreachable`. Idempotent;\n * the TS facade calls this once at module init.\n */\nexport function setPanicHook() {\n wasm.setPanicHook();\n}\nexport function __wbg_BigInt_231999d28f899902() { return handleError(function (arg0) {\n const ret = BigInt(arg0);\n return ret;\n}, arguments); }\nexport function __wbg_Error_fdd633d4bb5dd76a(arg0, arg1) {\n const ret = Error(getStringFromWasm0(arg0, arg1));\n return ret;\n}\nexport function __wbg_String_8564e559799eccda(arg0, arg1) {\n const ret = String(arg1);\n const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n const len1 = WASM_VECTOR_LEN;\n getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);\n getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);\n}\nexport function __wbg___wbindgen_bigint_get_as_i64_d9e915702856f831(arg0, arg1) {\n const v = arg1;\n const ret = typeof(v) === 'bigint' ? v : undefined;\n getDataViewMemory0().setBigInt64(arg0 + 8 * 1, isLikeNone(ret) ? BigInt(0) : ret, true);\n getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);\n}\nexport function __wbg___wbindgen_debug_string_8a447059637473e2(arg0, arg1) {\n const ret = debugString(arg1);\n const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n const len1 = WASM_VECTOR_LEN;\n getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);\n getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);\n}\nexport function __wbg___wbindgen_is_function_acc5528be2b923f2(arg0) {\n const ret = typeof(arg0) === 'function';\n return ret;\n}\nexport function __wbg___wbindgen_is_null_6d937fbfb6478470(arg0) {\n const ret = arg0 === null;\n return ret;\n}\nexport function __wbg___wbindgen_is_object_0beba4a1980d3eea(arg0) {\n const val = arg0;\n const ret = typeof(val) === 'object' && val !== null;\n return ret;\n}\nexport function __wbg___wbindgen_is_string_1fca8072260dd261(arg0) {\n const ret = typeof(arg0) === 'string';\n return ret;\n}\nexport function __wbg___wbindgen_is_undefined_721f8decd50c87a3(arg0) {\n const ret = arg0 === undefined;\n return ret;\n}\nexport function __wbg___wbindgen_jsval_eq_4e8c38722cb8ff51(arg0, arg1) {\n const ret = arg0 === arg1;\n return ret;\n}\nexport function __wbg___wbindgen_memory_9751d9a3017e7c25() {\n const ret = wasm.memory;\n return ret;\n}\nexport function __wbg___wbindgen_number_get_1cc01dd708740256(arg0, arg1) {\n const obj = arg1;\n const ret = typeof(obj) === 'number' ? obj : undefined;\n getDataViewMemory0().setFloat64(arg0 + 8 * 1, isLikeNone(ret) ? 0 : ret, true);\n getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);\n}\nexport function __wbg___wbindgen_string_get_71bb4348194e31f0(arg0, arg1) {\n const obj = arg1;\n const ret = typeof(obj) === 'string' ? obj : undefined;\n var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n var len1 = WASM_VECTOR_LEN;\n getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);\n getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);\n}\nexport function __wbg___wbindgen_throw_ea4887a5f8f9a9db(arg0, arg1) {\n throw new Error(getStringFromWasm0(arg0, arg1));\n}\nexport function __wbg_buffer_7447b9cc2267a9e5(arg0) {\n const ret = arg0.buffer;\n return ret;\n}\nexport function __wbg_call_67f43c91d09298f2() { return handleError(function (arg0, arg1, arg2) {\n const ret = arg0.call(arg1, arg2);\n return ret;\n}, arguments); }\nexport function __wbg_call_b51415974987aa44() { return handleError(function (arg0, arg1) {\n const ret = arg0.call(arg1);\n return ret;\n}, arguments); }\nexport function __wbg_crypto_38df2bab126b63dc(arg0) {\n const ret = arg0.crypto;\n return ret;\n}\nexport function __wbg_delete_21fafcbc7bb82c85() { return handleError(function (arg0, arg1) {\n arg0.delete(arg1);\n}, arguments); }\nexport function __wbg_epoch_15c000ffe3004541() { return handleError(function (arg0, arg1, arg2) {\n const ret = arg0.epoch(arg1, BigInt.asUintN(64, arg2));\n return ret;\n}, arguments); }\nexport function __wbg_error_a6fa202b58aa1cd3(arg0, arg1) {\n let deferred0_0;\n let deferred0_1;\n try {\n deferred0_0 = arg0;\n deferred0_1 = arg1;\n console.error(getStringFromWasm0(arg0, arg1));\n } finally {\n wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);\n }\n}\nexport function __wbg_getRandomValues_c44a50d8cfdaebeb() { return handleError(function (arg0, arg1) {\n arg0.getRandomValues(arg1);\n}, arguments); }\nexport function __wbg_get_615446055a48103f() { return handleError(function (arg0, arg1) {\n const ret = arg0.get(arg1);\n return ret;\n}, arguments); }\nexport function __wbg_globalThis_6d268067835e6709() { return handleError(function () {\n const ret = globalThis.globalThis;\n return ret;\n}, arguments); }\nexport function __wbg_global_3fe6c6c8ad6e6fb2() { return handleError(function () {\n const ret = global.global;\n return ret;\n}, arguments); }\nexport function __wbg_insert_2d611f2bf9b60fee() { return handleError(function (arg0, arg1, arg2) {\n arg0.insert(arg1, arg2);\n}, arguments); }\nexport function __wbg_instanceof_Error_38f854307ecab4ce(arg0) {\n let result;\n try {\n result = arg0 instanceof Error;\n } catch (_) {\n result = false;\n }\n const ret = result;\n return ret;\n}\nexport function __wbg_length_c552db98817b9523(arg0) {\n const ret = arg0.length;\n return ret;\n}\nexport function __wbg_maxEpochId_0b176768b2f352fb() { return handleError(function (arg0, arg1) {\n const ret = arg0.maxEpochId(arg1);\n return ret;\n}, arguments); }\nexport function __wbg_message_a05fcf872473ffc5(arg0) {\n const ret = arg0.message;\n return ret;\n}\nexport function __wbg_msCrypto_bd5a034af96bcba6(arg0) {\n const ret = arg0.msCrypto;\n return ret;\n}\nexport function __wbg_new_227d7c05414eb861() {\n const ret = new Error();\n return ret;\n}\nexport function __wbg_new_32de5cbf49ca7dcb(arg0) {\n const ret = new Uint8Array(arg0);\n return ret;\n}\nexport function __wbg_new_364c96143b8f3496() {\n const ret = new Object();\n return ret;\n}\nexport function __wbg_new_b8b71ca8104fc178(arg0, arg1) {\n const ret = new Error(getStringFromWasm0(arg0, arg1));\n return ret;\n}\nexport function __wbg_new_d9762fd75876aafe() {\n const ret = new Array();\n return ret;\n}\nexport function __wbg_new_no_args_4010ad257320fa4f(arg0, arg1) {\n const ret = new Function(getStringFromWasm0(arg0, arg1));\n return ret;\n}\nexport function __wbg_new_with_byte_offset_and_length_8b21e3b1308deb48(arg0, arg1, arg2) {\n const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0);\n return ret;\n}\nexport function __wbg_new_with_length_5fdafe029be917a5(arg0) {\n const ret = new Uint8Array(arg0 >>> 0);\n return ret;\n}\nexport function __wbg_node_84ea875411254db1(arg0) {\n const ret = arg0.node;\n return ret;\n}\nexport function __wbg_process_44c7a14e11e9f69e(arg0) {\n const ret = arg0.process;\n return ret;\n}\nexport function __wbg_push_1303ce035391aed3(arg0, arg1) {\n const ret = arg0.push(arg1);\n return ret;\n}\nexport function __wbg_randomFillSync_6c25eac9869eb53c() { return handleError(function (arg0, arg1) {\n arg0.randomFillSync(arg1);\n}, arguments); }\nexport function __wbg_require_b4edbdcf3e2a1ef0() { return handleError(function () {\n const ret = module.require;\n return ret;\n}, arguments); }\nexport function __wbg_self_1035a7cbd1b0d959() { return handleError(function () {\n const ret = self.self;\n return ret;\n}, arguments); }\nexport function __wbg_set_047d1ea37bb67c19(arg0, arg1, arg2) {\n arg0.set(arg1, arg2 >>> 0);\n}\nexport function __wbg_set_1ddc4b8cd44d0da4() { return handleError(function (arg0, arg1, arg2) {\n const ret = Reflect.set(arg0, arg1, arg2);\n return ret;\n}, arguments); }\nexport function __wbg_set_28cba565792ec75f(arg0, arg1, arg2) {\n arg0[arg1 >>> 0] = arg2;\n}\nexport function __wbg_set_6be42768c690e380(arg0, arg1, arg2) {\n arg0[arg1] = arg2;\n}\nexport function __wbg_set_name_b4c29a3a72ebbddc(arg0, arg1, arg2) {\n arg0.name = getStringFromWasm0(arg1, arg2);\n}\nexport function __wbg_stack_3b0d974bbf31e44f(arg0, arg1) {\n const ret = arg1.stack;\n const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n const len1 = WASM_VECTOR_LEN;\n getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);\n getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);\n}\nexport function __wbg_state_bd20456c0f3efbc9() { return handleError(function (arg0, arg1) {\n const ret = arg0.state(arg1);\n return ret;\n}, arguments); }\nexport function __wbg_subarray_e0162dcdea48eb3a(arg0, arg1, arg2) {\n const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0);\n return ret;\n}\nexport function __wbg_versions_276b2795b1c6a219(arg0) {\n const ret = arg0.versions;\n return ret;\n}\nexport function __wbg_window_9c17850b5e99c0ab() { return handleError(function () {\n const ret = window.window;\n return ret;\n}, arguments); }\nexport function __wbg_write_56b1cf5bb0e780d6() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {\n arg0.write(arg1, arg2, arg3, arg4);\n}, arguments); }\nexport function __wbindgen_cast_0000000000000001(arg0) {\n // Cast intrinsic for `F64 -> Externref`.\n const ret = arg0;\n return ret;\n}\nexport function __wbindgen_cast_0000000000000002(arg0, arg1) {\n // Cast intrinsic for `Ref(String) -> Externref`.\n const ret = getStringFromWasm0(arg0, arg1);\n return ret;\n}\nexport function __wbindgen_cast_0000000000000003(arg0) {\n // Cast intrinsic for `U64 -> Externref`.\n const ret = BigInt.asUintN(64, arg0);\n return ret;\n}\nexport function __wbindgen_init_externref_table() {\n const table = wasm.__wbindgen_externrefs;\n const offset = table.grow(4);\n table.set(0, undefined);\n table.set(offset + 0, undefined);\n table.set(offset + 1, null);\n table.set(offset + 2, true);\n table.set(offset + 3, false);\n}\nconst MlsClientFinalization = (typeof FinalizationRegistry === 'undefined')\n ? { register: () => {}, unregister: () => {} }\n : new FinalizationRegistry(ptr => wasm.__wbg_mlsclient_free(ptr, 1));\nconst MlsGroupFinalization = (typeof FinalizationRegistry === 'undefined')\n ? { register: () => {}, unregister: () => {} }\n : new FinalizationRegistry(ptr => wasm.__wbg_mlsgroup_free(ptr, 1));\n\nfunction addToExternrefTable0(obj) {\n const idx = wasm.__externref_table_alloc();\n wasm.__wbindgen_externrefs.set(idx, obj);\n return idx;\n}\n\nfunction debugString(val) {\n // primitive types\n const type = typeof val;\n if (type == 'number' || type == 'boolean' || val == null) {\n return `${val}`;\n }\n if (type == 'string') {\n return `\"${val}\"`;\n }\n if (type == 'symbol') {\n const description = val.description;\n if (description == null) {\n return 'Symbol';\n } else {\n return `Symbol(${description})`;\n }\n }\n if (type == 'function') {\n const name = val.name;\n if (typeof name == 'string' && name.length > 0) {\n return `Function(${name})`;\n } else {\n return 'Function';\n }\n }\n // objects\n if (Array.isArray(val)) {\n const length = val.length;\n let debug = '[';\n if (length > 0) {\n debug += debugString(val[0]);\n }\n for(let i = 1; i < length; i++) {\n debug += ', ' + debugString(val[i]);\n }\n debug += ']';\n return debug;\n }\n // Test for built-in\n const builtInMatches = /\\[object ([^\\]]+)\\]/.exec(toString.call(val));\n let className;\n if (builtInMatches && builtInMatches.length > 1) {\n className = builtInMatches[1];\n } else {\n // Failed to match the standard '[object ClassName]'\n return toString.call(val);\n }\n if (className == 'Object') {\n // we're a user defined class or Object\n // JSON.stringify avoids problems with cycles, and is generally much\n // easier than looping through ownProperties of `val`.\n try {\n return 'Object(' + JSON.stringify(val) + ')';\n } catch (_) {\n return 'Object';\n }\n }\n // errors\n if (val instanceof Error) {\n return `${val.name}: ${val.message}\\n${val.stack}`;\n }\n // TODO we could test for more things here, like `Set`s and `Map`s.\n return className;\n}\n\nfunction getArrayU8FromWasm0(ptr, len) {\n ptr = ptr >>> 0;\n return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);\n}\n\nlet cachedDataViewMemory0 = null;\nfunction getDataViewMemory0() {\n if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {\n cachedDataViewMemory0 = new DataView(wasm.memory.buffer);\n }\n return cachedDataViewMemory0;\n}\n\nfunction getStringFromWasm0(ptr, len) {\n return decodeText(ptr >>> 0, len);\n}\n\nlet cachedUint8ArrayMemory0 = null;\nfunction getUint8ArrayMemory0() {\n if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {\n cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);\n }\n return cachedUint8ArrayMemory0;\n}\n\nfunction handleError(f, args) {\n try {\n return f.apply(this, args);\n } catch (e) {\n const idx = addToExternrefTable0(e);\n wasm.__wbindgen_exn_store(idx);\n }\n}\n\nfunction isLikeNone(x) {\n return x === undefined || x === null;\n}\n\nfunction passArray8ToWasm0(arg, malloc) {\n const ptr = malloc(arg.length * 1, 1) >>> 0;\n getUint8ArrayMemory0().set(arg, ptr / 1);\n WASM_VECTOR_LEN = arg.length;\n return ptr;\n}\n\nfunction passArrayJsValueToWasm0(array, malloc) {\n const ptr = malloc(array.length * 4, 4) >>> 0;\n for (let i = 0; i < array.length; i++) {\n const add = addToExternrefTable0(array[i]);\n getDataViewMemory0().setUint32(ptr + 4 * i, add, true);\n }\n WASM_VECTOR_LEN = array.length;\n return ptr;\n}\n\nfunction passStringToWasm0(arg, malloc, realloc) {\n if (realloc === undefined) {\n const buf = cachedTextEncoder.encode(arg);\n const ptr = malloc(buf.length, 1) >>> 0;\n getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);\n WASM_VECTOR_LEN = buf.length;\n return ptr;\n }\n\n let len = arg.length;\n let ptr = malloc(len, 1) >>> 0;\n\n const mem = getUint8ArrayMemory0();\n\n let offset = 0;\n\n for (; offset < len; offset++) {\n const code = arg.charCodeAt(offset);\n if (code > 0x7F) break;\n mem[ptr + offset] = code;\n }\n if (offset !== len) {\n if (offset !== 0) {\n arg = arg.slice(offset);\n }\n ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;\n const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);\n const ret = cachedTextEncoder.encodeInto(arg, view);\n\n offset += ret.written;\n ptr = realloc(ptr, len, offset, 1) >>> 0;\n }\n\n WASM_VECTOR_LEN = offset;\n return ptr;\n}\n\nfunction takeFromExternrefTable0(idx) {\n const value = wasm.__wbindgen_externrefs.get(idx);\n wasm.__externref_table_dealloc(idx);\n return value;\n}\n\nlet cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });\ncachedTextDecoder.decode();\nconst MAX_SAFARI_DECODE_BYTES = 2146435072;\nlet numBytesDecoded = 0;\nfunction decodeText(ptr, len) {\n numBytesDecoded += len;\n if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {\n cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });\n cachedTextDecoder.decode();\n numBytesDecoded = len;\n }\n return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));\n}\n\nconst cachedTextEncoder = new TextEncoder();\n\nif (!('encodeInto' in cachedTextEncoder)) {\n cachedTextEncoder.encodeInto = function (arg, view) {\n const buf = cachedTextEncoder.encode(arg);\n view.set(buf);\n return {\n read: arg.length,\n written: buf.length\n };\n };\n}\n\nlet WASM_VECTOR_LEN = 0;\n\n\nlet wasm;\nexport function __wbg_set_wasm(val) {\n wasm = val;\n}\n","\nexport function date_now() {\n return Date.now();\n}","// Portable WASM loader for the palbe-mls engine — works in the browser\n// (Next.js/Vite), in Node (vitest), and in any bundler, WITHOUT relying on a\n// bundler's `.wasm` ESM-import support.\n//\n// Why not just `import * as wasm from './palbe_mls_bg.wasm'` (the bundler\n// target's own entry, `palbe_mls.js`)? Because that import form only works in\n// bundlers that special-case `.wasm` (webpack/Vite with config) and NEVER in\n// Node — so the two-client crypto smoke (vitest, Node) could not run the real\n// engine. Instead we replicate what the wasm-pack `web`/`nodejs` targets do\n// internally: build the import object from the glue module's named exports +\n// the inline snippet, then `WebAssembly.instantiate` the bytes ourselves.\n//\n// The bytes are obtained per-environment:\n// · Node → read the vendored `.wasm` file off disk (fs).\n// · Browser → fetch the asset URL the bundler emits for the `.wasm`\n// (`new URL('./palbe_mls_bg.wasm', import.meta.url)`), which\n// every modern bundler (Next.js/webpack5, Vite, esbuild/tsup)\n// rewrites to the correct hashed asset path. This is the same\n// pattern Web Workers / wasm assets use; no loader plugin.\n//\n// init is idempotent + races-safe: concurrent callers share one in-flight\n// promise, and a second call after success is a no-op returning the same glue.\n\n// The glue exports every user-facing binding (MlsClient/MlsGroup/generateClient/\n// setPanicHook) AND all the `__wbg_*` import functions the `.wasm` needs, plus\n// `__wbg_set_wasm` (hand the instantiated exports back to the glue) and\n// `__wbindgen_start` (run wasm-bindgen's start section). We import it as a\n// namespace so we can both call the bindings and assemble the import object.\nimport * as glue from './pkg/palbe_mls_bg.js';\n// The single inline snippet the `.wasm` imports directly (Date.now()).\nimport * as snippet from './pkg/snippets/mls-rs-core-f99cdecbb456b09c/inline0.js';\n\n/** The subset of the glue surface the bridge calls. */\nexport interface MlsGlue {\n generateClient: typeof glue.generateClient;\n setPanicHook: typeof glue.setPanicHook;\n MlsClient: typeof glue.MlsClient;\n MlsGroup: typeof glue.MlsGroup;\n}\n\n// The two import-module specifiers the `.wasm` declares (verified via\n// WebAssembly.Module.imports — './palbe_mls_bg.js' + the inline snippet).\nconst GLUE_MODULE = './palbe_mls_bg.js';\nconst SNIPPET_MODULE = './snippets/mls-rs-core-f99cdecbb456b09c/inline0.js';\n\nlet inflight: Promise<MlsGlue> | null = null;\nlet ready: MlsGlue | null = null;\n\n/**\n * Read the `.wasm` bytes for the current environment.\n *\n * - Browser/bundler: `fetch(new URL('...wasm', import.meta.url))`. tsup/webpack/\n * Vite rewrite that URL to the emitted asset path; the fetch returns the\n * compiled module via `WebAssembly.compileStreaming` when available.\n * - Node (no `fetch` of a `file:` URL): fall back to `fs.readFile`.\n */\nasync function loadWasmModule(): Promise<WebAssembly.Module> {\n const wasmUrl = new URL('./pkg/palbe_mls_bg.wasm', import.meta.url);\n\n // Node path: a `file:` URL — fetch won't take it; read the bytes off disk.\n // (Detected by the URL protocol so the browser never hits the fs branch.)\n if (wasmUrl.protocol === 'file:') {\n // The specifier is assembled at runtime so a browser bundler (Next.js/\n // webpack) cannot STATICALLY analyze this dynamic import and try to resolve\n // `fs/promises` for the client bundle (which fails — `Module not found:\n // Can't resolve 'fs/promises'`). esbuild also strips the `node:` prefix on a\n // literal `import('node:fs/promises')`, which is what triggered that webpack\n // resolve in the first place. A computed specifier keeps the `node:` builtin\n // for the Node runtime AND stays opaque to the browser build, so the branch\n // is never pulled into a client bundle it can never execute.\n const fsSpecifier = ['node', 'fs/promises'].join(':');\n // Type the module via a minimal local shape rather than\n // `typeof import('node:fs/promises')` — that type reference forces tsc (the\n // DTS build) to resolve `node:fs/promises`, which fails with TS2307 wherever\n // `@types/node` isn't in scope (it is NOT a dep of @palbase/web; it only\n // resolved locally via a hoisted install). The runtime import stays computed\n // + webpackIgnore'd so the browser bundle never pulls in the Node branch.\n // `readFile` returns a Node Buffer; type it as a plain BufferSource (what\n // WebAssembly.compile takes) without `@types/node` in scope.\n type NodeFsPromises = { readFile(path: URL): Promise<BufferSource> };\n const { readFile } = (await import(\n /* webpackIgnore: true */ fsSpecifier\n )) as unknown as NodeFsPromises;\n const bytes = await readFile(wasmUrl);\n return WebAssembly.compile(bytes);\n }\n\n // Browser / bundler path: stream-compile from the emitted asset URL.\n const response = await fetch(wasmUrl);\n if (typeof WebAssembly.compileStreaming === 'function') {\n try {\n return await WebAssembly.compileStreaming(Promise.resolve(response));\n } catch {\n // Some servers serve the wrong MIME type for `.wasm`; fall back to a\n // buffered compile rather than failing the whole init.\n }\n }\n const buffer = await response.arrayBuffer();\n return WebAssembly.compile(buffer);\n}\n\n/** Assemble the import object the `.wasm` expects from the glue + snippet. */\nfunction buildImports(): WebAssembly.Imports {\n return {\n [GLUE_MODULE]: glue as unknown as WebAssembly.ModuleImports,\n [SNIPPET_MODULE]: snippet as unknown as WebAssembly.ModuleImports,\n };\n}\n\n/**\n * Initialize the MLS WASM engine. Idempotent — call it once before any\n * messaging crypto op (the facade does this on `pb.messaging.start()` and\n * before the first chat op). Returns the glue surface (the bridge uses it).\n */\nexport function initMls(): Promise<MlsGlue> {\n if (ready) return Promise.resolve(ready);\n if (inflight) return inflight;\n inflight = (async () => {\n const module = await loadWasmModule();\n const instance = await WebAssembly.instantiate(module, buildImports());\n // Hand the instance's exports back to the glue (its module-level `wasm`)…\n const setWasm = (glue as unknown as { __wbg_set_wasm: (e: unknown) => void }).__wbg_set_wasm;\n setWasm(instance.exports);\n // …then run wasm-bindgen's start section (sets up the heap, etc).\n (instance.exports as { __wbindgen_start?: () => void }).__wbindgen_start?.();\n const g: MlsGlue = {\n generateClient: glue.generateClient,\n setPanicHook: glue.setPanicHook,\n MlsClient: glue.MlsClient,\n MlsGroup: glue.MlsGroup,\n };\n // Surface Rust panics as readable console errors in the browser.\n try {\n g.setPanicHook();\n } catch {\n // best-effort: a missing console_error_panic_hook must not fail init\n }\n ready = g;\n return g;\n })();\n return inflight;\n}\n\n/** True once `initMls()` has completed (the engine is usable synchronously). */\nexport function isMlsReady(): boolean {\n return ready !== null;\n}\n\n/** The initialized glue, or throw if `initMls()` has not completed. */\nexport function requireMls(): MlsGlue {\n if (!ready) {\n throw new Error('palbe-mls WASM not initialized — await initMls() first');\n }\n return ready;\n}\n","// Typed bridge over the raw WASM `MlsClient`/`MlsGroup`. It normalizes the two\n// byte conventions the engine uses and presents clean TS records:\n//\n// · Direct byte methods (signaturePublicKey, generateKeyPackage,\n// encryptApplicationMessage, groupId, …) already return `Uint8Array`.\n// · serde records/enums (enrollmentMaterial, addMember/commitChanges result,\n// processIncomingMessage, describeIncomingCommit) cross via\n// serde-wasm-bindgen, where `Vec<u8>` fields serialize as ARRAYS OF NUMBERS\n// (documented in src/wasm.rs §Records). We wrap those back into Uint8Array.\n//\n// Field names are camelCase (the engine derives `serde(rename_all =\n// \"camelCase\")`). The `ReceivedMessage` enum is externally tagged:\n// { Application: { sender, data } } | { CommitApplied: { epoch, removedSelf } }\n// | \"Proposal\" | \"Other\"\n//\n// Everything here is SYNC — the engine methods are synchronous (mls-rs storage\n// is sync in this build). Serialization of state happens through the JS storage\n// callbacks the host supplies (see ../storage.ts).\n\nimport { requireMls } from './loader.js';\nimport type {\n IGroupStateStorage,\n IKeyPackageStorage,\n MlsClient as RawClientType,\n MlsGroup as RawGroup,\n} from './pkg/palbe_mls.types.d.ts';\n\nexport type { IGroupStateStorage, IKeyPackageStorage };\n\n/** Coerce a serde byte field (array-of-numbers OR Uint8Array) to Uint8Array. */\nfunction toBytes(v: unknown): Uint8Array {\n if (v instanceof Uint8Array) return v;\n if (Array.isArray(v)) return Uint8Array.from(v as number[]);\n if (v == null) return new Uint8Array(0);\n // ArrayBuffer / typed-array view fallthrough.\n if (ArrayBuffer.isView(v)) return new Uint8Array((v as ArrayBufferView).buffer);\n throw new Error('expected byte field (Uint8Array | number[])');\n}\n\nfunction optBytes(v: unknown): Uint8Array | undefined {\n if (v == null) return undefined;\n return toBytes(v);\n}\n\n// ─── Record types (normalized) ───────────────────────────────────────────────\n\n/** `{ signatureKey, credential, capabilities }` — the SP-0 enroll material. */\nexport interface EnrollmentMaterial {\n signatureKey: Uint8Array;\n credential: Uint8Array;\n capabilities: Uint8Array;\n}\n\n/** Result of staging an add/commit-changes: commit + optional welcome + leaves. */\nexport interface AddResult {\n commit: Uint8Array;\n welcome?: Uint8Array;\n addedLeafIndices: number[];\n}\n\n/** Result of staging a remove: just the commit. */\nexport interface RemoveResult {\n commit: Uint8Array;\n}\n\nexport type CommitKind = 'Add' | 'Remove' | 'Update' | 'Mixed' | 'Other';\n\n/** The membership delta a commit WOULD effect (the reconciliation gate truth). */\nexport interface CommitDescription {\n kind: CommitKind;\n addedIdentities: Uint8Array[];\n removedIdentities: Uint8Array[];\n}\n\n/** What `processIncomingMessage` decoded (the normalized form of the serde enum). */\nexport type ReceivedMessage =\n | { type: 'application'; sender: Uint8Array; data: Uint8Array }\n | { type: 'commitApplied'; epoch: number; removedSelf: boolean }\n | { type: 'proposal' }\n | { type: 'other' };\n\n// ─── Group wrapper ───────────────────────────────────────────────────────────\n\n/** Typed wrapper over a raw `MlsGroup`. */\nexport class MlsGroupHandle {\n /** @internal */\n constructor(private readonly raw: RawGroup) {}\n\n /** The MLS rfc_group_id (routing key / storage key) bytes. */\n groupId(): Uint8Array {\n return this.raw.groupId();\n }\n\n /** Current applied epoch (BigInt → number; epochs stay well under 2^53). */\n currentEpoch(): number {\n return Number(this.raw.currentEpoch());\n }\n\n /** Stage a member add (PENDING after). */\n addMember(keyPackageMsg: Uint8Array): AddResult {\n return normalizeAddResult(this.raw.addMember(keyPackageMsg));\n }\n\n /** Stage a member remove by identity bytes (PENDING after). */\n removeMember(memberId: Uint8Array): RemoveResult {\n const r = this.raw.removeMember(memberId) as { commit: unknown };\n return { commit: toBytes(r.commit) };\n }\n\n /** Stage a SINGLE atomic add+remove commit (PENDING after). */\n commitChanges(addKeyPackages: Uint8Array[], removeMemberIds: Uint8Array[]): AddResult {\n return normalizeAddResult(this.raw.commitChanges(addKeyPackages, removeMemberIds));\n }\n\n /** Apply the staged commit (advance the local epoch). Only after server-accept. */\n applyPendingCommit(): void {\n this.raw.applyPendingCommit();\n }\n\n /** Discard the staged commit without advancing (the 409-rebase path). */\n clearPendingCommit(): void {\n this.raw.clearPendingCommit();\n }\n\n /** True while a staged (unapplied) commit exists. */\n hasPendingCommit(): boolean {\n return this.raw.hasPendingCommit();\n }\n\n /** Encrypt an application message at the current epoch → PrivateMessage bytes. */\n encryptApplicationMessage(plaintext: Uint8Array): Uint8Array {\n return this.raw.encryptApplicationMessage(plaintext);\n }\n\n /** Process an incoming MLSMessage and apply its effect (normalized result). */\n processIncomingMessage(message: Uint8Array): ReceivedMessage {\n return normalizeReceived(this.raw.processIncomingMessage(message));\n }\n\n /** Inspect an incoming Commit WITHOUT applying it (the gate's crypto-truth). */\n describeIncomingCommit(commitMsg: Uint8Array): CommitDescription {\n const d = this.raw.describeIncomingCommit(commitMsg) as {\n kind: CommitKind;\n addedIdentities: unknown[];\n removedIdentities: unknown[];\n };\n return {\n kind: d.kind,\n addedIdentities: d.addedIdentities.map(toBytes),\n removedIdentities: d.removedIdentities.map(toBytes),\n };\n }\n\n /** Persist the current group state through the JS group-storage callback. */\n writeToStorage(): void {\n this.raw.writeToStorage();\n }\n}\n\nfunction normalizeAddResult(v: unknown): AddResult {\n const r = v as { commit: unknown; welcome?: unknown; addedLeafIndices?: unknown[] };\n return {\n commit: toBytes(r.commit),\n welcome: optBytes(r.welcome),\n addedLeafIndices: (r.addedLeafIndices ?? []).map((n) => Number(n)),\n };\n}\n\nfunction normalizeReceived(v: unknown): ReceivedMessage {\n // Unit variants serialize as the bare string (\"Proposal\" / \"Other\").\n if (v === 'Proposal') return { type: 'proposal' };\n if (v === 'Other') return { type: 'other' };\n const obj = v as Record<string, unknown>;\n if (obj.Application) {\n const a = obj.Application as { sender: unknown; data: unknown };\n return { type: 'application', sender: toBytes(a.sender), data: toBytes(a.data) };\n }\n if (obj.CommitApplied) {\n const c = obj.CommitApplied as { epoch: unknown; removedSelf: unknown };\n return { type: 'commitApplied', epoch: Number(c.epoch), removedSelf: Boolean(c.removedSelf) };\n }\n return { type: 'other' };\n}\n\n// ─── Client wrapper ──────────────────────────────────────────────────────────\n\n/** Typed wrapper over a raw `MlsClient`. */\nexport class MlsClientHandle {\n /** @internal */\n constructor(private readonly raw: RawClientType) {}\n\n /** The SP-0 keydir enroll material (`{ signatureKey, credential, capabilities }`). */\n enrollmentMaterial(): EnrollmentMaterial {\n const m = this.raw.enrollmentMaterial() as {\n signatureKey: unknown;\n credential: unknown;\n capabilities: unknown;\n };\n return {\n signatureKey: toBytes(m.signatureKey),\n credential: toBytes(m.credential),\n capabilities: toBytes(m.capabilities),\n };\n }\n\n /** The device's STABLE signature PUBLIC key bytes (the keydir `signature_key`). */\n signaturePublicKey(): Uint8Array {\n return this.raw.signaturePublicKey();\n }\n\n /** The MLS Credential wire bytes (the keydir `credential`). */\n credential(): Uint8Array {\n return this.raw.credential();\n }\n\n /** The MLS Capabilities wire bytes (the keydir `capabilities`). */\n capabilities(): Uint8Array {\n return this.raw.capabilities();\n }\n\n /** The serialized STABLE signature keypair (secret-bearing — persist sealed). */\n signatureKeypair(): Uint8Array {\n return this.raw.signatureKeypair();\n }\n\n /** Mint a one-use KeyPackage (RAW RFC 9420 §10 bytes). */\n generateKeyPackage(): Uint8Array {\n return this.raw.generateKeyPackage();\n }\n\n /** Create a new group at epoch 0 (engine mints the rfc_group_id). */\n createGroup(groupId?: Uint8Array): MlsGroupHandle {\n return new MlsGroupHandle(this.raw.createGroup(groupId ?? undefined));\n }\n\n /** Join a group from a Welcome (opaque MLSMessage bytes). */\n joinGroup(welcome: Uint8Array): MlsGroupHandle {\n return new MlsGroupHandle(this.raw.joinGroup(welcome));\n }\n\n /** Re-hydrate an already-joined group by its rfc_group_id bytes. */\n loadGroup(groupId: Uint8Array): MlsGroupHandle {\n return new MlsGroupHandle(this.raw.loadGroup(groupId));\n }\n}\n\n/**\n * Mint a device MLS client. `initMls()` MUST have completed first\n * (the facade enforces this). `signatureKeypair` is undefined on first run, or\n * the previously-persisted sealed keypair bytes on restart (same MLS identity).\n */\nexport function generateClient(\n id: Uint8Array,\n groupStorage: IGroupStateStorage,\n keyPackageStorage: IKeyPackageStorage,\n signatureKeypair?: Uint8Array,\n): MlsClientHandle {\n const { generateClient: gen } = requireMls();\n return new MlsClientHandle(\n gen(id, groupStorage, keyPackageStorage, signatureKeypair ?? undefined),\n );\n}\n","// The web mirror of iOS `MLSEngine` — a single serialized owner of the WASM\n// MlsClient + the per-group MlsGroup handles. Every public method runs through\n// a promise-chain mutex (`enqueue`) so the synchronous engine critical section\n// is never re-entered concurrently (the engine is single-threaded; two awaited\n// ops interleaving would corrupt the storage write-through cache).\n//\n// DURABILITY BOUNDARY: a mutating engine op (create / join / commit / encrypt /\n// process / applyPending) is followed by an awaited storage flush before the\n// host treats the op as done — so an acked message / applied commit is always\n// durable (the engine erases a message key on first decrypt; the durable copy\n// is the only recoverable form, mirroring the iOS D3/D5 ordering).\n//\n// IDENTITY: the MLS credential identity is the SP-0 device id (`mdv_`), passed\n// as the `id` bytes to generateClient (SP-1.6 per-device leaf identity).\n\nimport type { GroupStateStorage, KeyPackageStorage, SignatureKeyStore } from './storage.js';\nimport { bytesToHex } from './util.js';\nimport {\n type AddResult,\n type CommitDescription,\n generateClient,\n type MlsClientHandle,\n type MlsGroupHandle,\n type ReceivedMessage,\n} from './wasm/bridge.js';\nimport { initMls } from './wasm/loader.js';\n\nconst utf8 = new TextEncoder();\n\n/** The rfc_group_id keyed by its hex form (the per-group handle cache key). */\nconst gidHex = bytesToHex;\n\nexport interface EngineEnrollmentMaterial {\n signatureKey: Uint8Array;\n credential: Uint8Array;\n capabilities: Uint8Array;\n}\n\n/**\n * The serialized MLS engine. Built once per (userId, deviceId). All crypto +\n * storage flows through here; `GroupMessaging` and `MessageDeliverySource` hold\n * the SAME instance so one MLS identity backs enroll + group ops + delivery.\n */\nexport class MlsEngine {\n private readonly groups = new Map<string, MlsGroupHandle>();\n private chain: Promise<unknown> = Promise.resolve();\n\n private constructor(\n readonly userId: string,\n readonly deviceId: string,\n private readonly client: MlsClientHandle,\n private readonly groupStore: GroupStateStorage,\n private readonly kpStore: KeyPackageStorage,\n ) {}\n\n /**\n * Build the engine: init the WASM, hydrate the durable stores into the sync\n * caches, generate (or restore) the device client. Idempotent restore: the\n * persisted signature keypair keeps the SAME MLS signing identity across\n * reloads; the group-state cache makes already-joined groups reloadable.\n */\n static async create(opts: {\n userId: string;\n deviceId: string;\n groupStore: GroupStateStorage;\n kpStore: KeyPackageStorage;\n sigStore: SignatureKeyStore;\n }): Promise<MlsEngine> {\n await initMls();\n // Hydrate the durable state into the sync caches BEFORE generateClient so a\n // restart sees its persisted groups + key-package secrets.\n await Promise.all([opts.groupStore.hydrate(), opts.kpStore.hydrate()]);\n const persistedKeypair = await opts.sigStore.load(opts.userId);\n const idBytes = utf8.encode(opts.deviceId);\n const client = generateClient(idBytes, opts.groupStore, opts.kpStore, persistedKeypair);\n // Persist the (possibly freshly minted) signature keypair so the next reload\n // restores the SAME identity.\n if (!persistedKeypair) {\n await opts.sigStore.save(opts.userId, client.signatureKeypair());\n }\n return new MlsEngine(opts.userId, opts.deviceId, client, opts.groupStore, opts.kpStore);\n }\n\n /** Serialize an engine op onto the chain so the critical section is exclusive. */\n private enqueue<T>(fn: () => T | Promise<T>): Promise<T> {\n const run = this.chain.then(fn, fn);\n // Keep the chain alive but swallow rejections so one failure doesn't poison\n // every later op (each caller still sees its own rejection via `run`).\n this.chain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n /** Flush both storage caches durably (the post-mutation durability boundary). */\n private async flush(): Promise<void> {\n await Promise.all([this.groupStore.flush(), this.kpStore.flush()]);\n }\n\n // ── Enroll material / key packages ──\n\n enrollmentMaterial(): Promise<EngineEnrollmentMaterial> {\n return this.enqueue(() => this.client.enrollmentMaterial());\n }\n\n signaturePublicKey(): Promise<Uint8Array> {\n return this.enqueue(() => this.client.signaturePublicKey());\n }\n\n generateKeyPackage(): Promise<Uint8Array> {\n return this.enqueue(async () => {\n const kp = this.client.generateKeyPackage();\n // A generated key package stores its private bits via the kp store — flush.\n await this.flush();\n return kp;\n });\n }\n\n // ── Group create / join / reload ──\n\n /** Create a group; returns its rfc_group_id (the routing key). */\n createGroup(): Promise<Uint8Array> {\n return this.enqueue(async () => {\n const group = this.client.createGroup();\n const gid = group.groupId();\n this.groups.set(gidHex(gid), group);\n group.writeToStorage();\n await this.flush();\n return gid;\n });\n }\n\n /** Join from a Welcome; returns the joined group's rfc_group_id. */\n joinFromWelcome(welcome: Uint8Array): Promise<Uint8Array> {\n return this.enqueue(async () => {\n const group = this.client.joinGroup(welcome);\n const gid = group.groupId();\n this.groups.set(gidHex(gid), group);\n group.writeToStorage();\n await this.flush();\n return gid;\n });\n }\n\n /** The current applied epoch for a group (0 if unknown). */\n epoch(rfcGroupId: Uint8Array): Promise<number> {\n return this.enqueue(() => {\n const g = this.handle(rfcGroupId);\n return g ? g.currentEpoch() : 0;\n });\n }\n\n // ── Membership commits (staged → applied/cleared by the rebase loop) ──\n\n /** Stage a SINGLE atomic add+remove commit. Returns the AddResult (PENDING). */\n commitChanges(\n rfcGroupId: Uint8Array,\n adds: Uint8Array[],\n removeMemberIds: Uint8Array[],\n ): Promise<AddResult> {\n return this.enqueue(() => {\n const g = this.requireHandle(rfcGroupId);\n return g.commitChanges(adds, removeMemberIds);\n });\n }\n\n /** Apply the staged commit (advance the epoch). Only after a server-accept. */\n applyPendingCommit(rfcGroupId: Uint8Array): Promise<void> {\n return this.enqueue(async () => {\n const g = this.requireHandle(rfcGroupId);\n g.applyPendingCommit();\n g.writeToStorage();\n await this.flush();\n });\n }\n\n /** Discard the staged commit (the 409-rebase path). */\n clearPendingCommit(rfcGroupId: Uint8Array): Promise<void> {\n return this.enqueue(() => {\n const g = this.handle(rfcGroupId);\n if (g?.hasPendingCommit()) g.clearPendingCommit();\n });\n }\n\n // ── Application messages ──\n\n /** Encrypt a plaintext at the current epoch → PrivateMessage bytes. */\n encryptApplication(rfcGroupId: Uint8Array, plaintext: Uint8Array): Promise<Uint8Array> {\n return this.enqueue(async () => {\n const g = this.requireHandle(rfcGroupId);\n const ct = g.encryptApplicationMessage(plaintext);\n // The sender ratchet advanced — checkpoint.\n g.writeToStorage();\n await this.flush();\n return ct;\n });\n }\n\n /**\n * Process an incoming MLSMessage (decrypt application / apply commit). Returns\n * the normalized ReceivedMessage. `describeIncomingCommit` could gate a commit\n * before applying when the server fans a declaration; today the live server\n * does not, so we process directly (parity with the iOS nil-declaration path).\n */\n processIncoming(rfcGroupId: Uint8Array, blob: Uint8Array): Promise<ReceivedMessage> {\n return this.enqueue(async () => {\n const g = this.requireHandle(rfcGroupId);\n const received = g.processIncomingMessage(blob);\n // A commit/decrypt mutated state — checkpoint before the host acks.\n g.writeToStorage();\n await this.flush();\n return received;\n });\n }\n\n /** Inspect a commit WITHOUT applying (the reconciliation gate's crypto-truth). */\n describeIncomingCommit(rfcGroupId: Uint8Array, commit: Uint8Array): Promise<CommitDescription> {\n return this.enqueue(() => this.requireHandle(rfcGroupId).describeIncomingCommit(commit));\n }\n\n /** Persist the current group state (explicit checkpoint). */\n writeToStorage(rfcGroupId: Uint8Array): Promise<void> {\n return this.enqueue(async () => {\n const g = this.handle(rfcGroupId);\n if (g) {\n g.writeToStorage();\n await this.flush();\n }\n });\n }\n\n // ── Handle resolution ──\n\n /** Resolve (or reload from storage) the handle for a group, or undefined. */\n private handle(rfcGroupId: Uint8Array): MlsGroupHandle | undefined {\n const key = gidHex(rfcGroupId);\n const cached = this.groups.get(key);\n if (cached) return cached;\n // Try to reload from the hydrated group-state cache (restart-safe).\n try {\n const g = this.client.loadGroup(rfcGroupId);\n this.groups.set(key, g);\n return g;\n } catch {\n return undefined;\n }\n }\n\n private requireHandle(rfcGroupId: Uint8Array): MlsGroupHandle {\n const g = this.handle(rfcGroupId);\n if (!g) throw new Error(`MLS group not loaded: ${gidHex(rfcGroupId)}`);\n return g;\n }\n}\n","// The group registry — maps rfc_group_id ↔ the resolved MessagingGroup and\n// drives the observable chat list. Web mirror of the iOS GroupRegistry: a SET\n// change (register-new / remove / hydrate) publishes a fresh [Chat] snapshot;\n// an in-place epoch bump does NOT (a chatty group must not invalidate the list).\n\nimport type { Chat } from './chat.js';\nimport type { MessagingGroup } from './group-messaging.js';\nimport type { GroupCatalog } from './history.js';\n\n/** A pointer-stable Chat factory: one Chat per rfc_group_id. */\nexport type ChatFactory = (group: MessagingGroup) => Chat;\n\nexport class GroupRegistry {\n private readonly byRfc = new Map<string, MessagingGroup>();\n private catalog: GroupCatalog | null = null;\n private chatFactory: ChatFactory | null = null;\n private onListChange: ((chats: Chat[]) => void) | null = null;\n\n /** Wire the observable chat-list sink + the pointer-stable Chat factory, then\n * replay the current set so a sink attached after groups loaded reflects them. */\n attachChatList(onListChange: (chats: Chat[]) => void, factory: ChatFactory): void {\n this.onListChange = onListChange;\n this.chatFactory = factory;\n this.publish();\n }\n\n /** Hydrate from the durable catalog (live entries win — fresher epoch). */\n async attachCatalog(catalog: GroupCatalog): Promise<void> {\n this.catalog = catalog;\n let changed = false;\n for (const c of await catalog.all()) {\n if (!this.byRfc.has(c.rfcGroupId)) {\n this.byRfc.set(c.rfcGroupId, {\n displayId: c.displayId,\n rfcGroupId: c.rfcGroupId,\n currentEpoch: c.currentEpoch,\n ownerUserId: c.ownerUserId,\n directKey: c.directKey,\n name: c.name,\n });\n changed = true;\n }\n }\n if (changed) this.publish();\n }\n\n register(group: MessagingGroup): void {\n const isNew = !this.byRfc.has(group.rfcGroupId);\n this.byRfc.set(group.rfcGroupId, group);\n void this.persist(group);\n if (isNew) this.publish();\n }\n\n remove(rfcGroupId: string): void {\n if (!this.byRfc.has(rfcGroupId)) return;\n this.byRfc.delete(rfcGroupId);\n if (this.catalog) void this.catalog.remove(rfcGroupId);\n this.publish();\n }\n\n group(rfcGroupId: string): MessagingGroup | undefined {\n return this.byRfc.get(rfcGroupId);\n }\n\n /** The single known group, if exactly one (the wire-gap routing fallback). */\n soleGroup(): MessagingGroup | undefined {\n return this.byRfc.size === 1 ? this.byRfc.values().next().value : undefined;\n }\n\n all(): MessagingGroup[] {\n return [...this.byRfc.values()];\n }\n\n /** Advance a cached epoch (after a processed commit). Does NOT publish (the\n * group SET is unchanged — only metadata). */\n bumpEpoch(rfcGroupId: string, epoch: number): void {\n const g = this.byRfc.get(rfcGroupId);\n if (!g) return;\n const bumped = { ...g, currentEpoch: epoch };\n this.byRfc.set(rfcGroupId, bumped);\n void this.persist(bumped);\n }\n\n private publish(): void {\n const factory = this.chatFactory;\n if (!this.onListChange || !factory) return;\n const chats = this.all().map((g) => factory(g));\n this.onListChange(chats);\n }\n\n private async persist(group: MessagingGroup): Promise<void> {\n if (!this.catalog) return;\n try {\n await this.catalog.upsert({\n displayId: group.displayId,\n rfcGroupId: group.rfcGroupId,\n currentEpoch: group.currentEpoch,\n ownerUserId: group.ownerUserId,\n directKey: group.directKey,\n name: group.name,\n });\n } catch {\n // best-effort persistence — never blocks the live path\n }\n }\n}\n","// The persistence layer for web messaging — the web mirror of the iOS\n// Keychain-sealed MLS stores (MessagingGroupStateStorage / KeyPackageStorage /\n// MessagingDeviceIDStore / MessagingSignatureKeyStore / MessageStore /\n// GroupCatalog).\n//\n// The CRITICAL contract (src/wasm.rs §Storage): mls-rs calls IGroupStateStorage\n// / IKeyPackageStorage methods SYNCHRONOUSLY inside the engine's locked\n// critical section. So those are backed by an in-memory write-through cache\n// (sync reads/writes) and flushed to a durable, encrypted-at-rest backend\n// (IndexedDB + WebCrypto AES-GCM) ASYNCHRONOUSLY, OUTSIDE the engine call. The\n// engine never awaits I/O; the host (the engine wrapper) awaits the flush after\n// each mutating op so an acked message is always durable.\n//\n// At-rest encryption: a per-device AES-GCM key, non-extractable, kept in\n// IndexedDB as a CryptoKey (browsers persist CryptoKey objects in IDB without\n// exposing raw bytes — the web analogue of the iOS Keychain seal key). In Node\n// (vitest / SSR) there is no IndexedDB; a pure in-memory durable backend stands\n// in (the crypto smoke needs durability WITHIN a process, not across reloads).\n\nimport { bytesToHex } from './util.js';\nimport type { IGroupStateStorage, IKeyPackageStorage } from './wasm/bridge.js';\n\n// ─── Durable KV backend (IndexedDB in browser, memory in Node) ──────────────\n\n/** A namespaced async key→bytes store. Values are sealed before write. */\nexport interface DurableKV {\n get(key: string): Promise<Uint8Array | undefined>;\n set(key: string, value: Uint8Array): Promise<void>;\n delete(key: string): Promise<void>;\n /** All keys under a prefix (for hydrating a store on launch). */\n keys(prefix: string): Promise<string[]>;\n}\n\nconst hasIndexedDB = (): boolean =>\n typeof indexedDB !== 'undefined' && typeof crypto !== 'undefined' && !!crypto.subtle;\n\n/** A tight, non-shared ArrayBuffer copy of a byte view (WebCrypto BufferSource). */\nfunction copyToArrayBuffer(view: Uint8Array): ArrayBuffer {\n const out = new ArrayBuffer(view.byteLength);\n new Uint8Array(out).set(view);\n return out;\n}\n\n// ── In-memory backend (Node/SSR/tests) ──────────────────────────────────────\n\nclass MemoryKV implements DurableKV {\n private readonly map = new Map<string, Uint8Array>();\n get(key: string): Promise<Uint8Array | undefined> {\n return Promise.resolve(this.map.get(key));\n }\n set(key: string, value: Uint8Array): Promise<void> {\n this.map.set(key, value.slice());\n return Promise.resolve();\n }\n delete(key: string): Promise<void> {\n this.map.delete(key);\n return Promise.resolve();\n }\n keys(prefix: string): Promise<string[]> {\n return Promise.resolve([...this.map.keys()].filter((k) => k.startsWith(prefix)));\n }\n}\n\n// ── IndexedDB backend (browser) with WebCrypto AES-GCM seal at rest ─────────\n\nconst DB_NAME = 'palbe.messaging';\nconst STORE = 'kv';\nconst KEY_STORE = 'keys';\nconst SEAL_KEY_ID = 'seal-key-v1';\n\nfunction openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE);\n if (!db.objectStoreNames.contains(KEY_STORE)) db.createObjectStore(KEY_STORE);\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\nfunction idbReq<T>(req: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\n/** Get-or-create the non-extractable AES-GCM seal key (persisted as a CryptoKey). */\nasync function getSealKey(db: IDBDatabase): Promise<CryptoKey> {\n const existing = await idbReq(\n db.transaction(KEY_STORE, 'readonly').objectStore(KEY_STORE).get(SEAL_KEY_ID),\n );\n if (existing) return existing as CryptoKey;\n const key = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, [\n 'encrypt',\n 'decrypt',\n ]);\n // Storing a non-extractable CryptoKey keeps the raw bytes inside the browser's\n // key store — the page can use it to seal/open but can never read it out.\n await idbReq(db.transaction(KEY_STORE, 'readwrite').objectStore(KEY_STORE).put(key, SEAL_KEY_ID));\n return key;\n}\n\nclass IndexedDbKV implements DurableKV {\n private dbPromise: Promise<IDBDatabase> | null = null;\n private keyPromise: Promise<CryptoKey> | null = null;\n\n private db(): Promise<IDBDatabase> {\n if (!this.dbPromise) this.dbPromise = openDb();\n return this.dbPromise;\n }\n private async sealKey(): Promise<CryptoKey> {\n if (!this.keyPromise) this.keyPromise = this.db().then(getSealKey);\n return this.keyPromise;\n }\n\n private async seal(plain: Uint8Array): Promise<Uint8Array> {\n const key = await this.sealKey();\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const ct = new Uint8Array(\n await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, copyToArrayBuffer(plain)),\n );\n const out = new Uint8Array(iv.length + ct.length);\n out.set(iv, 0);\n out.set(ct, iv.length);\n return out;\n }\n\n private async open(sealed: Uint8Array): Promise<Uint8Array> {\n const key = await this.sealKey();\n const iv = copyToArrayBuffer(sealed.subarray(0, 12));\n const ct = copyToArrayBuffer(sealed.subarray(12));\n const pt = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ct);\n return new Uint8Array(pt);\n }\n\n async get(k: string): Promise<Uint8Array | undefined> {\n const db = await this.db();\n const sealed = (await idbReq(db.transaction(STORE, 'readonly').objectStore(STORE).get(k))) as\n | Uint8Array\n | undefined;\n if (!sealed) return undefined;\n return this.open(sealed);\n }\n async set(k: string, v: Uint8Array): Promise<void> {\n const db = await this.db();\n const sealed = await this.seal(v);\n await idbReq(db.transaction(STORE, 'readwrite').objectStore(STORE).put(sealed, k));\n }\n async delete(k: string): Promise<void> {\n const db = await this.db();\n await idbReq(db.transaction(STORE, 'readwrite').objectStore(STORE).delete(k));\n }\n async keys(prefix: string): Promise<string[]> {\n const db = await this.db();\n const all = (await idbReq(\n db.transaction(STORE, 'readonly').objectStore(STORE).getAllKeys(),\n )) as IDBValidKey[];\n return all.map(String).filter((k) => k.startsWith(prefix));\n }\n}\n\n/** Build the durable KV for this environment (IndexedDB in browser, memory else). */\nexport function createDurableKV(): DurableKV {\n return hasIndexedDB() ? new IndexedDbKV() : new MemoryKV();\n}\n\n// ─── Byte / hex helpers ──────────────────────────────────────────────────────\n\nfunction bytesKey(prefix: string, id: Uint8Array): string {\n // Hex the id bytes so any blob can be a string key.\n return prefix + bytesToHex(id);\n}\n\n// ─── Sync write-through cache (the IGroupStateStorage / IKeyPackageStorage seam)\n\n/**\n * The synchronous group-state storage mls-rs calls inside the engine. Reads/\n * writes hit an in-memory cache (sync); a durable flush to the encrypted KV is\n * scheduled and awaited by the engine wrapper OUTSIDE the engine call.\n *\n * Layout per group (keyed by hex(groupId)):\n * state:<gid> → latest serialized group state\n * epoch:<gid>:<epochId> → a stored prior-epoch record\n * maxEpoch:<gid> → highest stored prior-epoch id (decimal string bytes)\n */\nexport class GroupStateStorage implements IGroupStateStorage {\n // In-memory authoritative cache (the engine reads/writes this synchronously).\n private readonly stateCache = new Map<string, Uint8Array>();\n private readonly epochCache = new Map<string, Uint8Array>();\n private readonly maxEpochCache = new Map<string, bigint>();\n // Keys touched since the last flush (durable write queue).\n private readonly dirty = new Set<string>();\n\n constructor(private readonly kv: DurableKV) {}\n\n // ── sync engine seam ──\n state(groupId: Uint8Array): Uint8Array | undefined {\n return this.stateCache.get(bytesKey('', groupId));\n }\n epoch(groupId: Uint8Array, epochId: bigint): Uint8Array | undefined {\n return this.epochCache.get(`${bytesKey('', groupId)}:${epochId}`);\n }\n write(\n groupId: Uint8Array,\n groupState: Uint8Array,\n epochInserts: { id: bigint; data: Uint8Array }[],\n epochUpdates: { id: bigint; data: Uint8Array }[],\n ): void {\n const gid = bytesKey('', groupId);\n this.stateCache.set(gid, groupState.slice());\n this.dirty.add(`state:${gid}`);\n let max = this.maxEpochCache.get(gid) ?? -1n;\n for (const rec of [...epochInserts, ...epochUpdates]) {\n this.epochCache.set(`${gid}:${rec.id}`, rec.data.slice());\n this.dirty.add(`epoch:${gid}:${rec.id}`);\n if (rec.id > max) max = rec.id;\n }\n if (max >= 0n) {\n this.maxEpochCache.set(gid, max);\n this.dirty.add(`maxEpoch:${gid}`);\n }\n }\n maxEpochId(groupId: Uint8Array): bigint | undefined {\n return this.maxEpochCache.get(bytesKey('', groupId));\n }\n\n // ── async durable flush (called by the engine wrapper after a mutating op) ──\n async flush(): Promise<void> {\n if (this.dirty.size === 0) return;\n const pending = [...this.dirty];\n this.dirty.clear();\n await Promise.all(\n pending.map(async (key) => {\n if (key.startsWith('state:')) {\n const gid = key.slice('state:'.length);\n const v = this.stateCache.get(gid);\n if (v) await this.kv.set(`gss:state:${gid}`, v);\n } else if (key.startsWith('epoch:')) {\n const rest = key.slice('epoch:'.length);\n const v = this.epochCache.get(rest);\n if (v) await this.kv.set(`gss:epoch:${rest}`, v);\n } else if (key.startsWith('maxEpoch:')) {\n const gid = key.slice('maxEpoch:'.length);\n const max = this.maxEpochCache.get(gid);\n if (max != null) await this.kv.set(`gss:maxEpoch:${gid}`, encodeBigint(max));\n }\n }),\n );\n }\n\n /** Hydrate the in-memory cache from the durable KV (call before generateClient). */\n async hydrate(): Promise<void> {\n const keys = await this.kv.keys('gss:');\n for (const k of keys) {\n const v = await this.kv.get(k);\n if (!v) continue;\n if (k.startsWith('gss:state:')) {\n this.stateCache.set(k.slice('gss:state:'.length), v);\n } else if (k.startsWith('gss:epoch:')) {\n this.epochCache.set(k.slice('gss:epoch:'.length), v);\n } else if (k.startsWith('gss:maxEpoch:')) {\n this.maxEpochCache.set(k.slice('gss:maxEpoch:'.length), decodeBigint(v));\n }\n }\n }\n\n async wipe(): Promise<void> {\n this.stateCache.clear();\n this.epochCache.clear();\n this.maxEpochCache.clear();\n this.dirty.clear();\n for (const k of await this.kv.keys('gss:')) await this.kv.delete(k);\n }\n}\n\n/**\n * The synchronous key-package secret storage mls-rs calls. Secret-bearing (HPKE\n * private keys) — sealed at rest by the KV. Same sync-cache + async-flush shape.\n */\nexport class KeyPackageStorage implements IKeyPackageStorage {\n private readonly cache = new Map<string, Uint8Array>();\n private readonly dirtySet = new Set<string>();\n private readonly dirtyDelete = new Set<string>();\n\n constructor(private readonly kv: DurableKV) {}\n\n // ── sync engine seam ──\n delete(id: Uint8Array): void {\n const k = bytesKey('', id);\n this.cache.delete(k);\n this.dirtyDelete.add(k);\n this.dirtySet.delete(k);\n }\n insert(id: Uint8Array, data: Uint8Array): void {\n const k = bytesKey('', id);\n this.cache.set(k, data.slice());\n this.dirtySet.add(k);\n this.dirtyDelete.delete(k);\n }\n get(id: Uint8Array): Uint8Array | undefined {\n return this.cache.get(bytesKey('', id));\n }\n\n // ── async durable flush ──\n async flush(): Promise<void> {\n if (this.dirtySet.size === 0 && this.dirtyDelete.size === 0) return;\n const sets = [...this.dirtySet];\n const dels = [...this.dirtyDelete];\n this.dirtySet.clear();\n this.dirtyDelete.clear();\n await Promise.all([\n ...sets.map((k) => {\n const v = this.cache.get(k);\n return v ? this.kv.set(`kps:${k}`, v) : Promise.resolve();\n }),\n ...dels.map((k) => this.kv.delete(`kps:${k}`)),\n ]);\n }\n\n async hydrate(): Promise<void> {\n for (const k of await this.kv.keys('kps:')) {\n const v = await this.kv.get(k);\n if (v) this.cache.set(k.slice('kps:'.length), v);\n }\n }\n\n async wipe(): Promise<void> {\n this.cache.clear();\n this.dirtySet.clear();\n this.dirtyDelete.clear();\n for (const k of await this.kv.keys('kps:')) await this.kv.delete(k);\n }\n}\n\nfunction encodeBigint(n: bigint): Uint8Array {\n return new TextEncoder().encode(n.toString());\n}\nfunction decodeBigint(b: Uint8Array): bigint {\n return BigInt(new TextDecoder().decode(b));\n}\n\n// ─── Device-id + signature-keypair stores (small singletons) ────────────────\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\n/** Persisted server-minted device id, keyed by user id (enroll idempotency). */\nexport class DeviceIdStore {\n constructor(private readonly kv: DurableKV) {}\n async load(userId: string): Promise<string | undefined> {\n const v = await this.kv.get(`devid:${userId}`);\n return v ? dec.decode(v) : undefined;\n }\n async save(userId: string, deviceId: string): Promise<void> {\n await this.kv.set(`devid:${userId}`, enc.encode(deviceId));\n }\n async clear(userId: string): Promise<void> {\n await this.kv.delete(`devid:${userId}`);\n }\n}\n\n/** Persisted STABLE signature keypair bytes, keyed by user id (same MLS identity). */\nexport class SignatureKeyStore {\n constructor(private readonly kv: DurableKV) {}\n async load(userId: string): Promise<Uint8Array | undefined> {\n return this.kv.get(`sigkey:${userId}`);\n }\n async save(userId: string, keypair: Uint8Array): Promise<void> {\n await this.kv.set(`sigkey:${userId}`, keypair);\n }\n}\n","// The messaging coordinator — the web mirror of iOS MessagingCoordinator +\n// MessagingGroupState. Builds the engine/storage/delivery-source ONCE (lazily,\n// on first chat op after sign-in), implements the ChatBackend seam, mints\n// pointer-stable Chats (one per rfc id), and owns the observable chat list.\n//\n// Process-shared singleton (one runtime per browser tab), like the iOS shared\n// holder. Bound to ONE PalbeRuntime (the configured pb).\n\nimport type { PalbeRuntime } from '../runtime.js';\nimport { Chat, type ChatBackend, type ChatDraft } from './chat.js';\nimport { type IncomingMessage, MessageDeliverySource, MessageHub } from './delivery-source.js';\nimport {\n GroupMessaging,\n type MessagingGroup,\n type ReplyRef,\n resolveReply,\n} from './group-messaging.js';\nimport { GroupCatalog, MessageStore, type StoredMessage } from './history.js';\nimport { enrollDevice, listDevices } from './keydir.js';\nimport { MlsEngine } from './mls-engine.js';\nimport { GroupRegistry } from './registry.js';\nimport {\n createDurableKV,\n DeviceIdStore,\n type DurableKV,\n GroupStateStorage,\n KeyPackageStorage,\n SignatureKeyStore,\n} from './storage.js';\nimport type { ChatMember, ChatMessage, ResolvedReply, SentReceipt, Unsubscribe } from './types.js';\nimport { directKey as makeDirectKey, mintDeviceId } from './util.js';\n\ninterface Resolved {\n engine: MlsEngine;\n groups: GroupMessaging;\n source: MessageDeliverySource;\n hub: MessageHub;\n messageStore: MessageStore;\n catalog: GroupCatalog;\n registry: GroupRegistry;\n deviceId: string;\n userId: string;\n}\n\nexport class MessagingCoordinator implements ChatBackend {\n private readonly kv: DurableKV;\n private readonly deviceStore: DeviceIdStore;\n private readonly sigStore: SignatureKeyStore;\n private readonly groupStore: GroupStateStorage;\n private readonly kpStore: KeyPackageStorage;\n private readonly registry = new GroupRegistry();\n\n private resolved: Resolved | null = null;\n private resolvePromise: Promise<Resolved> | null = null;\n\n // Pointer-stable Chat cache (one per rfc id) + the observable chat list.\n private readonly chatByRfc = new Map<string, Chat>();\n private chatList: Chat[] = [];\n private readonly listListeners = new Set<() => void>();\n\n constructor(private readonly rt: PalbeRuntime) {\n this.kv = createDurableKV();\n this.deviceStore = new DeviceIdStore(this.kv);\n this.sigStore = new SignatureKeyStore(this.kv);\n this.groupStore = new GroupStateStorage(this.kv);\n this.kpStore = new KeyPackageStorage(this.kv);\n // Wire the registry → observable chat list with the pointer-stable factory.\n this.registry.attachChatList(\n (chats) => {\n this.chatList = chats;\n this.emitList();\n },\n (group) => this.chatFor(group),\n );\n }\n\n // ── ChatBackend identity ──\n\n get selfUserId(): string {\n const u = this.rt.auth.currentUser;\n if (!u) throw new Error('not_signed_in');\n return u.id;\n }\n\n // ── Public surface (used by the facade) ──\n\n /** Self-enroll + start the delivery source (idempotent). The first chat op\n * calls this implicitly; pb.messaging.start() is the opt-in warm-at-sign-in. */\n async ensureEnrolled(): Promise<void> {\n await this.resolve();\n }\n\n /** Open the ONE direct chat with userId. INSTANT + LOCAL (no network); the\n * group materializes on the first send. Pointer-stable per peer pair. */\n directChat(peerUserId: string): Chat {\n const selfId = this.selfUserId;\n const dk = makeDirectKey(selfId, peerUserId);\n // If an active direct chat with this key already exists, return it.\n for (const chat of this.chatByRfc.values()) {\n if (chat.isDirect && chat.state === 'active') {\n // best-effort: match the active group's direct key via the registry\n const g = this.registry.all().find((x) => x.displayId === chat.id);\n if (g?.directKey === dk) return chat;\n }\n }\n const draft: ChatDraft = {\n mode: { kind: 'direct', peerUserId },\n reservedId: `draft:${dk}`,\n };\n // Cache the draft under its reserved id so two opens converge.\n const cached = this.chatByRfc.get(draft.reservedId);\n if (cached) return cached;\n const chat = new Chat({ draft, kind: 'direct', backend: this });\n this.chatByRfc.set(draft.reservedId, chat);\n // Fire-and-forget enroll so a peer's DM/Welcome can drain in.\n void this.ensureEnrolled().catch(() => {});\n return chat;\n }\n\n /** Open a multi-party group chat (LOCAL + LAZY; created on first send). */\n groupChat(members: string[] = []): Chat {\n const draft: ChatDraft = {\n mode: { kind: 'group', members },\n reservedId: `draft:group:${crypto.randomUUID()}`,\n };\n const chat = new Chat({ draft, kind: 'group', backend: this });\n this.chatByRfc.set(draft.reservedId, chat);\n void this.ensureEnrolled().catch(() => {});\n return chat;\n }\n\n /** Look up an active chat by id (grp_ display id). */\n chatById(id: string): Chat | null {\n const g = this.registry.all().find((x) => x.displayId === id);\n return g ? this.chatFor(g) : (this.chatByRfc.get(id) ?? null);\n }\n\n /** The observable chat list (active chats only). */\n get chats(): readonly Chat[] {\n void this.ensureEnrolled().catch(() => {});\n return this.chatList;\n }\n\n onChatsChange(cb: () => void): Unsubscribe {\n this.listListeners.add(cb);\n void this.ensureEnrolled().catch(() => {});\n return () => this.listListeners.delete(cb);\n }\n\n private emitList(): void {\n for (const cb of this.listListeners) cb();\n }\n\n /** Pointer-stable Chat for a group (one per rfc id). */\n private chatFor(group: MessagingGroup): Chat {\n const existing = this.chatByRfc.get(group.rfcGroupId);\n if (existing) return existing;\n const chat = new Chat({\n group,\n kind: group.directKey ? 'direct' : 'group',\n backend: this,\n });\n this.chatByRfc.set(group.rfcGroupId, chat);\n return chat;\n }\n\n // ── Resolution (build-once) ──\n\n private resolve(): Promise<Resolved> {\n if (this.resolved) return Promise.resolve(this.resolved);\n if (this.resolvePromise) return this.resolvePromise;\n this.resolvePromise = this.buildResolved();\n return this.resolvePromise;\n }\n\n private async buildResolved(): Promise<Resolved> {\n const userId = this.selfUserId;\n // Resolve (or mint + persist) the device id BEFORE building the engine — the\n // engine's MLS credential identity IS the device id (SP-1.6).\n const deviceId = (await this.deviceStore.load(userId)) ?? mintDeviceId();\n\n const engine = await MlsEngine.create({\n userId,\n deviceId,\n groupStore: this.groupStore,\n kpStore: this.kpStore,\n sigStore: this.sigStore,\n });\n\n const messageStore = new MessageStore(this.kv);\n const catalog = new GroupCatalog(this.kv);\n const hub = new MessageHub();\n const groups = new GroupMessaging(this.rt, engine, userId, deviceId, messageStore);\n const source = new MessageDeliverySource(\n this.rt,\n engine,\n hub,\n this.registry,\n messageStore,\n deviceId,\n userId,\n );\n\n // Enroll this device (idempotent — persists the id, publishes key packages).\n await enrollDevice(this.rt, engine, this.deviceStore, {});\n\n // Hydrate the registry from the durable catalog BEFORE the first pump.\n await this.registry.attachCatalog(catalog);\n\n const resolved: Resolved = {\n engine,\n groups,\n source,\n hub,\n messageStore,\n catalog,\n registry: this.registry,\n deviceId,\n userId,\n };\n this.resolved = resolved;\n\n // Start the live delivery source (subscribe wake + initial drain).\n await source.start();\n return resolved;\n }\n\n // ── ChatBackend impl ──\n\n async materialize(draft: ChatDraft): Promise<MessagingGroup> {\n const r = await this.resolve();\n let group: MessagingGroup;\n if (draft.mode.kind === 'direct') {\n group = await r.groups.getOrCreateDirect(draft.mode.peerUserId);\n } else {\n group = await r.groups.createGroup();\n for (const userId of draft.mode.members) {\n await r.groups.addMember(group, userId);\n }\n }\n r.registry.register(group);\n void r.catalog.upsert({\n displayId: group.displayId,\n rfcGroupId: group.rfcGroupId,\n currentEpoch: group.currentEpoch,\n ownerUserId: group.ownerUserId,\n directKey: group.directKey,\n name: group.name,\n });\n return group;\n }\n\n async sendText(\n group: MessagingGroup,\n text: string,\n replyTo?: ReplyRef | null,\n ): Promise<{ receipt: SentReceipt; clientMsgId: string }> {\n const r = await this.resolve();\n return r.groups.sendText(group, text, replyTo);\n }\n\n async history(group: MessagingGroup, limit: number, before?: number): Promise<ChatMessage[]> {\n const r = await this.resolve();\n const rows = await r.messageStore.history(group.rfcGroupId, limit, before);\n // Two-pass: first build a lookup from all rows' clientMsgId (so a reply to a\n // parent within the same page resolves correctly), then resolve each row's replyTo.\n const lookup = new Map<string, { text: string; senderUserId: string }>();\n for (const s of rows) {\n const cid = s.clientMsgId ?? '';\n if (cid && s.text !== null) {\n // Use selfUserId for outgoing rows; '' for incoming rows where the peer\n // userId isn't resolved at history time. The empty string still yields\n // state:'resolved' in resolveReply (real text wins — spoof defense covers\n // peer messages, not just own-sent).\n const senderUserId = s.direction === 'outgoing' ? this.selfUserId : '';\n lookup.set(cid, { text: s.text, senderUserId });\n }\n }\n return rows.map((s) => this.toChatMessage(group, s, (id) => lookup.get(id) ?? null));\n }\n\n private toChatMessage(\n group: MessagingGroup,\n s: StoredMessage,\n lookup?: (id: string) => { text: string; senderUserId: string } | null,\n ): ChatMessage {\n const clientMsgId = s.clientMsgId ?? '';\n let replyTo: ResolvedReply | null = null;\n if (s.replyTo && lookup) {\n // Reconstruct a minimal ReplyRef from the stored replyTo fields and resolve.\n const ref: ReplyRef = {\n v: 1,\n client_msg_id: s.replyTo.clientMsgId,\n preview: {\n kind: s.replyTo.previewKind,\n author_user_id: s.replyTo.previewAuthorUserId ?? '',\n body: s.replyTo.previewBody ?? undefined,\n body_truncated: false,\n },\n };\n replyTo = resolveReply(ref, lookup);\n }\n return {\n id: `${group.displayId}#${s.serverSeq}`,\n kind: s.text != null ? 'text' : 'system',\n direction: s.direction,\n senderUserId: s.direction === 'outgoing' ? this.selfUserId : null,\n text: s.text,\n serverSeq: s.serverSeq,\n sentAt: new Date(s.at),\n clientMsgId,\n replyTo,\n };\n }\n\n async members(group: MessagingGroup): Promise<ChatMember[]> {\n const r = await this.resolve();\n try {\n const rows = await r.groups.listMembers(group.displayId);\n // Dedup device rows → users.\n const byUser = new Map<string, ChatMember>();\n for (const row of rows) {\n if (!byUser.has(row.user_id)) {\n byUser.set(row.user_id, {\n id: row.user_id,\n userId: row.user_id,\n displayName: null,\n role: row.role,\n isSelf: row.user_id === this.selfUserId,\n });\n }\n }\n return [...byUser.values()];\n } catch {\n return [];\n }\n }\n\n async addMember(group: MessagingGroup, userId: string): Promise<void> {\n const r = await this.resolve();\n await r.groups.addMember(group, userId);\n }\n\n async removeMember(group: MessagingGroup, userId: string): Promise<void> {\n const r = await this.resolve();\n await r.groups.removeMember(group, userId);\n }\n\n async leave(group: MessagingGroup): Promise<void> {\n const r = await this.resolve();\n await r.groups.leaveGroup(group);\n r.registry.remove(group.rfcGroupId);\n }\n\n registerActive(group: MessagingGroup): void {\n this.registry.register(group);\n }\n\n setTyping(group: MessagingGroup, isTyping: boolean): void {\n void this.resolve().then((r) => r.source.setTyping(group, isTyping));\n }\n\n async markRead(group: MessagingGroup, upToServerSeq: number): Promise<void> {\n const r = await this.resolve();\n await r.source.markRead(group, upToServerSeq);\n }\n\n subscribeLive(group: MessagingGroup, chat: Chat): Unsubscribe {\n let offMsg: (() => void) | null = null;\n let offConv: (() => void) | null = null;\n void this.resolve().then((r) => {\n offMsg = r.hub.onMessage(group.displayId, (m: IncomingMessage) => {\n void chat.ingestLive(m);\n });\n r.source.observeConversation(group);\n offConv = r.hub.onConv(group.displayId, (e) => chat.applyConv(e.event, e.payload));\n });\n return () => {\n offMsg?.();\n offConv?.();\n };\n }\n\n async userIdForDevice(group: MessagingGroup, deviceId: string): Promise<string | null> {\n const r = await this.resolve();\n try {\n const rows = await r.groups.listMembers(group.displayId);\n return rows.find((m) => m.device_id === deviceId)?.user_id ?? null;\n } catch {\n return null;\n }\n }\n\n // ── Trust (chat-scoped) ──\n\n /** List a user's devices (used by safety-number computation). */\n async listUserDevices(userId: string): Promise<string[]> {\n const res = await listDevices(this.rt, userId);\n return res.devices.map((d) => d.device_id);\n }\n}\n","// `pb.messaging` — the chat-centric E2E-encrypted messaging surface (Web-MLS\n// Phase 2). The web mirror of the iOS `pb.messaging`: the dev's mental model is\n// a CHAT (a DM or a group), so the top-level noun is `Chat`, bundling its own\n// messages + typing + presence + members.\n//\n// const dm = pb.messaging.directChat(peerUserId); // INSTANT, LOCAL, no network\n// await dm.send('hi'); // first send MATERIALIZES it\n// const off = pb.messaging.onChatsChange(() => render(pb.messaging.chats));\n//\n// The MLS protocol underneath (keydir enroll/claim, group commits, queue pull)\n// is INTERNAL — the first chat op self-enrolls. `messaging` is a reserved\n// namespace (no backend endpoint may be named `messaging`).\n\nimport type { PalbeRuntime } from '../runtime.js';\nimport type { Chat } from './chat.js';\nimport { MessagingCoordinator } from './coordinator.js';\nimport type { Unsubscribe } from './types.js';\n\nexport class PalbeMessaging {\n private readonly coordinator: MessagingCoordinator;\n\n constructor(rt: PalbeRuntime) {\n this.coordinator = new MessagingCoordinator(rt);\n }\n\n /**\n * Warm messaging at sign-in (optional). Enrolls this device + starts the live\n * delivery source so incoming DMs/Welcomes drain before the chat list opens.\n * Never REQUIRED — any chat op self-enrolls. Idempotent.\n */\n async start(): Promise<void> {\n await this.coordinator.ensureEnrolled();\n }\n\n /**\n * Open the ONE direct chat with `userId`. INSTANT + LOCAL — no network, nothing\n * created yet. Returns a draft `Chat` to show immediately; the server group +\n * MLS material materialize LAZILY on the first `chat.send`. Idempotent + stable:\n * the same two users always resolve to the SAME Chat.\n */\n directChat(userId: string): Chat {\n return this.coordinator.directChat(userId);\n }\n\n /**\n * Open a multi-party group chat. LOCAL + LAZY like `directChat`: pick the\n * members, get a draft Chat, the group is created on the FIRST `chat.send`.\n */\n groupChat(opts: { members?: string[] } = {}): Chat {\n return this.coordinator.groupChat(opts.members ?? []);\n }\n\n /** Look up a chat by id (e.g. from a push payload / deep link). null if unknown. */\n chat(id: string): Chat | null {\n return this.coordinator.chatById(id);\n }\n\n /** The observable chat list (DMs + groups, active only). Read it after an\n * `onChatsChange` subscription to render the inbox. */\n get chats(): readonly Chat[] {\n return this.coordinator.chats;\n }\n\n /** Subscribe to chat-list changes (a chat added/removed/hydrated). */\n onChatsChange(cb: () => void): Unsubscribe {\n return this.coordinator.onChatsChange(cb);\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.1.0';\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 { PalbeCalls } from './calls/facade.js';\nimport type { PalbeConfig } from './config.js';\nimport { PalbeFlags } from './flags-facade.js';\nimport { PalbeMessaging } from './messaging/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 calls: PalbeCalls;\n messaging: PalbeMessaging;\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 let calls: PalbeCalls | undefined;\n let messaging: PalbeMessaging | 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 // PalbeCalls is lazy + stateless per instance (no timer/socket to destroy).\n get calls() {\n if (!calls) calls = new PalbeCalls(rt);\n return calls;\n },\n // Lazy like the others — constructing PalbeMessaging is inert (no enroll /\n // socket / WASM load until the first chat op or pb.messaging.start()).\n get messaging() {\n if (!messaging) messaging = new PalbeMessaging(rt);\n return messaging;\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 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","/**\n * Session-cookie codec shared by EVERY palbe/next surface (browser storage\n * adapter, pbServer cookie adapter, middleware, auth callback). Pure\n * functions — no 'next' imports, no palbe runtime imports — so both sides\n * provably read and write the SAME bytes.\n *\n * Format (P3 design contract):\n * - name: `palbe-session-<endpointRef>`; overflow chunk names append `.0`,\n * `.1`, … (cookie names otherwise avoid dots — tooling friendliness).\n * - value: `encodeURIComponent(JSON.stringify({ a, r, e }))` with a=access\n * token, r=refresh token, e=expiry (ms epoch).\n * - one cookie when the encoded value fits MAX_COOKIE_VALUE chars, else\n * chunks ONLY (the base name must not coexist with chunks — callers clear\n * stale names via clearedSessionCookieNames before writing, then honor the\n * encode result's `clear` overflow guard after writing).\n */\n\nexport { endpointRefFromApiKey } from '../api-key.js';\n\nexport interface StoredSession {\n accessToken: string;\n refreshToken: string;\n /** ms epoch */\n expiresAt: number;\n}\n\n/** Browsers cap a cookie at ~4096 bytes incl. name + attributes; 3500 leaves headroom. */\nconst MAX_COOKIE_VALUE = 3500;\n\n/**\n * The P3 cookie contract attributes, shared by every server-side writer\n * (pbServer adapter, middleware, callback); the browser writer mirrors them\n * in document.cookie string form (next/client.ts). NOT HttpOnly by design —\n * the browser SDK must read/write the session (Supabase-paradigm tradeoff);\n * Max-Age = 30d = the refresh-token TTL.\n */\nexport const SESSION_COOKIE_ATTRS = {\n path: '/',\n sameSite: 'lax',\n secure: true,\n maxAge: 2_592_000,\n} as const;\n\nexport function sessionCookieName(endpointRef: string): string {\n return `palbe-session-${endpointRef}`;\n}\n\nexport interface SessionCookieWrite {\n /** Cookies to SET, in order. */\n set: Array<{ name: string; value: string }>;\n /**\n * Names to DELETE alongside the set: the one-past-the-end OVERFLOW GUARD\n * (base write → `.0`; chunks `.0..n` → `.(n+1)`). Decode stops at the\n * first missing chunk, so deleting exactly one-past-the-end guarantees a\n * stale orphan chunk (left behind a gap, where presence-probing cleanup\n * cannot see it) can never concatenate into the freshly written run.\n * Writers MUST honor both lists.\n */\n clear: string[];\n}\n\nexport function encodeSessionCookies(\n endpointRef: string,\n session: StoredSession,\n): SessionCookieWrite {\n const name = sessionCookieName(endpointRef);\n const value = encodeURIComponent(\n JSON.stringify({ a: session.accessToken, r: session.refreshToken, e: session.expiresAt }),\n );\n if (value.length <= MAX_COOKIE_VALUE) {\n return { set: [{ name, value }], clear: [`${name}.0`] };\n }\n const chunks: Array<{ name: string; value: string }> = [];\n // Chunk WITHOUT splitting a %XX escape (every '%' in the encoded value\n // starts one — encodeURIComponent never emits a bare '%'). Next's cookie\n // layer percent-DECODES values on read and silently DROPS a cookie whose\n // value is an invalid escape sequence, so a split escape would lose that\n // chunk server-side; independent decodability also lets\n // encodeSessionCookiesDecoded mirror this run byte-exactly.\n for (let start = 0; start < value.length; ) {\n let end = Math.min(start + MAX_COOKIE_VALUE, value.length);\n if (end < value.length) {\n if (value[end - 1] === '%') end -= 1;\n else if (value[end - 2] === '%') end -= 2;\n }\n chunks.push({ name: `${name}.${chunks.length}`, value: value.slice(start, end) });\n start = end;\n }\n return { set: chunks, clear: [`${name}.${chunks.length}`] };\n}\n\n/**\n * encodeSessionCookies for percent-ENCODING cookie stores. Next's request\n * and response cookie APIs apply encodeURIComponent to values when\n * serializing (and decodeURIComponent on read) — handing them the codec's\n * already-encoded values would DOUBLE-encode the wire bytes and break the\n * browser-side reader. This variant returns the same cookie run with\n * DECODED values: `encodeURIComponent(value)` reproduces the\n * encodeSessionCookies bytes exactly (chunk boundaries never split an\n * escape), so an encoding store writes byte-identical cookies to a raw\n * writer like document.cookie.\n */\nexport function encodeSessionCookiesDecoded(\n endpointRef: string,\n session: StoredSession,\n): SessionCookieWrite {\n const { set, clear } = encodeSessionCookies(endpointRef, session);\n return {\n set: set.map(({ name, value }) => ({ name, value: decodeURIComponent(value) })),\n clear,\n };\n}\n\n/**\n * Read the session back from a cookie getter: the base name wins; otherwise\n * `.0`, `.1`, … are concatenated until the first missing chunk. Tolerant:\n * malformed URI escapes / JSON / shape → null (treated as \"no session\").\n *\n * Accepts BOTH value forms. Next's cookie stores percent-DECODE values once\n * on read, so server-side callers (pbServer adapter, middleware) hand us the\n * already-decoded JSON; raw readers (document.cookie) hand us the encoded\n * form. The raw (already-decoded) parse runs FIRST because it is\n * unambiguous — an encoded value can never raw-parse as JSON\n * (encodeURIComponent escapes `{` itself, so it starts with `%7B`) — whereas\n * decode-first would silently corrupt a literal `%XX` run inside a token\n * (decodeURIComponent('%41') → 'A', no throw) or throw on an invalid escape.\n */\nexport function decodeSessionCookies(\n get: (name: string) => string | undefined,\n endpointRef: string,\n): StoredSession | null {\n const base = sessionCookieName(endpointRef);\n let encoded = get(base);\n if (encoded === undefined) {\n let joined = '';\n for (let i = 0; ; i++) {\n const part = get(`${base}.${i}`);\n if (part === undefined) break;\n joined += part;\n }\n if (joined === '') return null;\n encoded = joined;\n }\n const raw = parseStoredSession(encoded);\n if (raw !== null) return raw;\n try {\n return parseStoredSession(decodeURIComponent(encoded));\n } catch {\n return null;\n }\n}\n\n/** JSON-parse + shape-check one candidate value; malformed → null. */\nfunction parseStoredSession(json: string): StoredSession | null {\n try {\n const parsed: unknown = JSON.parse(json);\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.a === 'string' && typeof obj.r === 'string' && typeof obj.e === 'number') {\n return { accessToken: obj.a, refreshToken: obj.r, expiresAt: obj.e };\n }\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Names a writer must delete before/while writing: the base name (when\n * present) plus every existing `.N` chunk, probed in order until the first\n * missing one (writes are always a contiguous `.0..n` run, so a gap means\n * the end of palbe-owned chunks).\n */\nexport function clearedSessionCookieNames(\n endpointRef: string,\n present: (name: string) => boolean,\n): string[] {\n const base = sessionCookieName(endpointRef);\n const names: string[] = [];\n if (present(base)) names.push(base);\n for (let i = 0; ; i++) {\n const chunk = `${base}.${i}`;\n if (!present(chunk)) break;\n names.push(chunk);\n }\n return names;\n}\n\n/**\n * Parse the canonical chunk index out of `palbe-session-<ref>.N`; non-chunk\n * names (the bare base, a foreign ref, junk suffixes like `.x` / `.01` /\n * `.-1`) → null. The index is a non-negative integer with NO leading zeros\n * (the encoder emits `.0`, `.1`, … verbatim), so `String(n) === suffix`\n * round-trips it exactly.\n */\nfunction chunkIndex(base: string, name: string): number | null {\n if (!name.startsWith(`${base}.`)) return null;\n const suffix = name.slice(base.length + 1);\n if (!/^\\d+$/.test(suffix)) return null;\n const idx = Number.parseInt(suffix, 10);\n return String(idx) === suffix ? idx : null;\n}\n\n/**\n * getAll-powered orphan sweep — the deletion list a writer with the FULL\n * cookie jar should clear, catching crash-orphans that presence-probing\n * (clearedSessionCookieNames, which stops at the first gap) CANNOT see.\n *\n * Given every cookie name in the jar, returns the `palbe-session-<ref>`-family\n * names that are NOT part of a single valid session:\n * - A valid session is EITHER the bare base alone, OR the contiguous chunk\n * run `.0, .1, …, n` (starting at `.0`, no gaps).\n * - Any `.N` beyond the first gap from `.0` is an orphan (incl. ALL chunks\n * when `.0` is absent — no run exists).\n * - The base coexisting with ANY chunk is inconsistent (the contract forbids\n * it; decode lets the base win and silently strands the chunks): the base\n * is kept, the chunks are swept (a fresh write replaces the base in place).\n *\n * Only canonical `.N` names count; foreign refs and malformed suffixes are\n * ignored. Output order: base (if inconsistent) first, then chunks ascending.\n */\nexport function orphanSessionCookieNames(endpointRef: string, allNames: string[]): string[] {\n const base = sessionCookieName(endpointRef);\n const hasBase = allNames.includes(base);\n const indices = new Set<number>();\n for (const name of allNames) {\n const idx = chunkIndex(base, name);\n if (idx !== null) indices.add(idx);\n }\n if (indices.size === 0) return []; // unchunked (base alone) or no palbe cookies\n\n // Length of the contiguous run from .0 (0 when .0 is missing).\n let runLen = 0;\n while (indices.has(runLen)) runLen++;\n\n const orphans: string[] = [];\n // Base coexisting with chunks: base wins on decode → chunks are stale.\n // Sweep the base too only when there are NO chunks behind the run to keep\n // (the contract forbids the mix; clearing chunks alone resolves it, but if\n // the run itself is invalid—no .0—the base is the real session and stays).\n // Decision: when base coexists with ANY chunk, the chunks are orphans; the\n // base is the live session and is kept — UNLESS the chunk set forms a valid\n // run AND there is also a gap-orphan, in which case both base+gap-orphan are\n // inconsistent leftovers. Concretely: keep it simple and SAFE — the base is\n // only swept when it sits alongside a partial/gapped chunk set (a crash mid\n // base→chunk migration), never when chunks form a clean run the base shadows.\n const everyChunkContiguous = indices.size === runLen && runLen > 0;\n if (hasBase && !everyChunkContiguous) orphans.push(base);\n\n for (const idx of [...indices].sort((a, b) => a - b)) {\n if (idx >= runLen) orphans.push(`${base}.${idx}`);\n }\n // Base + clean contiguous run: base wins on decode, chunks are stranded.\n if (hasBase && everyChunkContiguous) {\n for (let i = 0; i < runLen; i++) orphans.push(`${base}.${i}`);\n }\n return orphans;\n}\n"],"mappings":"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","names":["validatePathParam","ptr","module","generateClient","generateClient","chat","ready"]}