@rebasepro/client 0.25.1 → 0.26.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,"file":"index.es.js","names":[],"sources":["../src/auth.ts","../src/admin.ts","../src/cron.ts","../src/backups.ts","../src/api-keys.ts","../src/functions.ts","../src/storage.ts","../src/storage-registry.ts","../src/sql-script.ts","../src/websocket.ts","../src/realtime-channel.ts","../src/offline-lazy.ts","../src/index.ts"],"sourcesContent":["import { RebaseApiError, Transport } from \"./transport\";\nimport { RebaseClientError } from \"@rebasepro/types\";\nimport type { AuthConfigDocument, AuthChangeEvent, RebaseSession, AuthTokens, DeviceSession, User } from \"@rebasepro/types\";\n\n// Re-export canonical types so `import { RebaseSession } from \"@rebasepro/client\"` keeps working\nexport type { RebaseSession, AuthTokens, AuthChangeEvent, DeviceSession } from \"@rebasepro/types\";\n\n/** Minimal, non-sensitive user profile returned by {@link findUserByEmail}. */\nexport interface PublicUserProfile {\n uid: string;\n displayName: string | null;\n photoURL: string | null;\n}\n\n/** Map a raw user object from an auth response (`/login`, `/refresh`, `/me`) to a `User`. */\nfunction mapRawUser(raw: Record<string, unknown>): User {\n return {\n uid: raw.uid as string,\n email: (raw.email as string | null) ?? null,\n displayName: (raw.displayName as string | null) ?? null,\n photoURL: (raw.photoURL as string | null) ?? null,\n providerId: (raw.providerId as string | undefined) ?? \"password\",\n isAnonymous: (raw.isAnonymous as boolean | undefined) ?? false,\n emailVerified: raw.emailVerified as boolean | undefined,\n ...(raw.pendingEmail !== undefined ? { pendingEmail: raw.pendingEmail as string | null } : {}),\n roles: raw.roles as string[] | undefined,\n metadata: raw.metadata as Record<string, unknown> | undefined,\n };\n}\n\n/** Placeholder user, used only as a last resort when none can be resolved. */\nconst EMPTY_USER: User = { uid: \"\", email: null, displayName: null, photoURL: null, providerId: \"password\", isAnonymous: false };\n\n\n/**\n * What `GET /auth/config` answers: the backend's auth capability document, and\n * whether it serves storage.\n *\n * Declared once, in `@rebasepro/types`, and re-exported here under the name the\n * endpoint uses. The SDK used to carry its own near-copy of it, which listed an\n * `emailServiceEnabled` flag no backend has ever sent and omitted half of what\n * every backend does send.\n */\nexport type AuthConfig = AuthConfigDocument;\n\nexport interface AuthStorage {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n}\n\nexport function createMemoryStorage(): AuthStorage {\n const store: Record<string, string> = {};\n return {\n getItem(key) { return store[key] ?? null; },\n setItem(key, value) { store[key] = value; },\n removeItem(key) { delete store[key]; }\n };\n}\n\nfunction detectStorage(): AuthStorage {\n try {\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(\"__rebase_test__\", \"1\");\n localStorage.removeItem(\"__rebase_test__\");\n return localStorage;\n }\n } catch (e) { /* ignore */ }\n return createMemoryStorage();\n}\n\nexport interface CreateAuthOptions {\n storage?: AuthStorage;\n authPath?: string;\n autoRefresh?: boolean;\n persistSession?: boolean;\n /**\n * Authentication flow mode.\n * - 'json' (default): Tokens are sent/received in JSON bodies. Refresh token is stored in local storage.\n * - 'cookie': Refresh token is sent/received via httpOnly cookies. Access token remains in memory.\n */\n authFlowMode?: \"json\" | \"cookie\";\n}\n\n/**\n * Finishing a sign-in that the server refused with `MFA_REQUIRED`.\n *\n * That refusal carries `details.mfaToken` — proof the first factor was\n * accepted, good for five minutes and for nothing but the two challenge routes\n * — and `details.factors`, the factors the account can answer with.\n */\nexport interface MfaPendingOptions {\n /** `details.mfaToken` of the `MFA_REQUIRED` refusal. */\n mfaToken?: string;\n}\n\nexport function createAuth(transport: Transport, options?: CreateAuthOptions) {\n const opts = options || {};\n const storage = opts.storage || detectStorage();\n const authPath = opts.authPath || \"/auth\";\n const autoRefresh = opts.autoRefresh !== false;\n const persistSession = opts.persistSession !== false;\n const authFlowMode = opts.authFlowMode || \"json\";\n\n const STORAGE_KEY = \"rebase_auth\";\n const REFRESH_BUFFER_MS = 120000;\n /**\n * The largest delay `setTimeout` can hold — 2^31 - 1 ms, about 24.8 days.\n * Anything larger is silently clamped to 1ms by Node and every browser.\n */\n const MAX_TIMER_DELAY_MS = 2_147_483_647;\n // Auto-refresh resilience: retry transient failures with exponential backoff\n // (1s, 2s, 4s, … capped) before giving up.\n const MAX_REFRESH_RETRIES = 5;\n const REFRESH_RETRY_BASE_MS = 1000;\n const REFRESH_RETRY_MAX_MS = 30000;\n\n let currentSession: RebaseSession | null = null;\n /**\n * Which sign-in `currentSession` belongs to. Bumped whenever the session\n * is replaced by anything other than its own refresh — a sign-in, a\n * sign-out, a revocation, a local abandon — so a refresh already in flight\n * can tell, when its answer lands, that the session it was refreshing has\n * ended. Its answer is then dropped: adopting it put a signed-out session\n * back in memory, in storage and on the transport, announced\n * TOKEN_REFRESHED after SIGNED_OUT, and overwrote whoever had signed in\n * since.\n */\n let sessionEpoch = 0;\n const listeners = new Set<(event: AuthChangeEvent, session: RebaseSession | null) => void>();\n let refreshTimeout: ReturnType<typeof setTimeout> | null = null;\n // De-dupe concurrent refreshes. On boot (esp. cookie mode + React StrictMode)\n // multiple callers can trigger refresh at once; without this they race — the\n // server rotates the refresh token twice and the browser can end up with a\n // cookie the DB no longer matches. A single in-flight promise is shared.\n let inFlightRefresh: Promise<RebaseSession> | null = null;\n /** The `sessionEpoch` the in-flight refresh was started for. */\n let inFlightEpoch = -1;\n let resolveInitialized: (value: void | PromiseLike<void>) => void;\n const isInitialized = new Promise<void>((resolve) => {\n resolveInitialized = resolve;\n });\n /** See {@link SessionRestoreOutcome}. Set once, before `isInitialized` settles. */\n let restoreOutcome: SessionRestoreOutcome = \"pending\";\n /** Settle the load-time restore, saying how it ended. */\n function settleRestore(outcome: Exclude<SessionRestoreOutcome, \"pending\">) {\n if (restoreOutcome === \"pending\") restoreOutcome = outcome;\n resolveInitialized!();\n }\n\n function authUrl(endpoint: string) {\n return transport.baseUrl + transport.apiPath + authPath + endpoint;\n }\n\n function getFetch() {\n return transport.fetchFn || globalThis.fetch;\n }\n\n /**\n * The body of a request that presents the refresh token — `/refresh` and\n * `/logout`.\n *\n * Empty in cookie mode. The token is in an HttpOnly cookie the browser\n * attaches, and this client never holds it: what it holds is the `\"\"` the\n * server answers in its place. Sending that back was sending a blank token\n * beside the cookie, and a server that refused the blank (0.10.0 through\n * 0.23, with 400 INVALID_INPUT) never read the cookie — so no tab that outlived\n * its access token could renew it, while a reload, which had no session in\n * memory and so sent no field, could.\n */\n function refreshTokenBody(): string {\n return JSON.stringify(authFlowMode === \"cookie\" ? {} : { refreshToken: currentSession?.refreshToken });\n }\n\n /**\n * The refresh token a new session keeps, given the one an answer carried.\n *\n * In cookie mode, none: the server answers `refreshToken: \"\"` because the\n * cookie holds it, and that `\"\"` is \"not held here\", not a token to carry\n * forward or present. Otherwise the answer's token, or — from a server\n * that omits it on a rotation it did not make — the one already held.\n */\n function heldRefreshToken(received: string | undefined): string {\n if (authFlowMode === \"cookie\") return \"\";\n return received || currentSession?.refreshToken || \"\";\n }\n\n /**\n * Whether the server refused a refresh as malformed: a 400, which says the\n * request this client built is not one that server accepts.\n *\n * That is a client/server contract mismatch, not a blip. The identical\n * request is refused identically however often it is sent, so retrying it\n * on a backoff only hides the error behind six identical ones — which is\n * how a cookie-mode tab, refused this way after every wake from sleep,\n * quietly ended on its login screen.\n */\n function isMalformedRefreshError(err: unknown): boolean {\n return err instanceof RebaseApiError && err.status === 400;\n }\n\n type ErrorBody = { error?: { message?: string; code?: string; details?: unknown }; message?: string; code?: string; details?: unknown } | undefined;\n\n function apiError(status: number, body: ErrorBody, statusText: string): RebaseApiError {\n return new RebaseApiError(\n body?.error?.message || body?.message || statusText,\n {\n status,\n code: body?.error?.code || body?.code,\n details: body?.error?.details || body?.details\n }\n );\n }\n\n function throwApiError(status: number, body: ErrorBody, statusText: string): never {\n throw apiError(status, body, statusText);\n }\n\n function emit(event: AuthChangeEvent, session: RebaseSession | null) {\n for (const fn of listeners) {\n try {\n fn(event, session);\n } catch (e) {\n // Isolated so one bad handler cannot stop the rest being told —\n // but reported, because the throw came from the caller's own\n // code and discarding it made a broken `onAuthStateChange`\n // handler look like an event that never fired. The socket in\n // this package already reports handler errors this way.\n console.error(\"Error in auth state change listener:\", e);\n }\n }\n }\n\n function saveSession(session: RebaseSession) {\n if (!persistSession || authFlowMode === \"cookie\") return;\n try {\n storage.setItem(STORAGE_KEY, JSON.stringify(session));\n } catch (e) { /* ignore */ }\n }\n\n function clearStoredSession() {\n try {\n storage.removeItem(STORAGE_KEY);\n } catch (e) { /* ignore */ }\n }\n\n function loadStoredSession(): RebaseSession | null {\n try {\n const raw = storage.getItem(STORAGE_KEY);\n if (raw) return JSON.parse(raw) as RebaseSession;\n } catch (e) { /* ignore */ }\n return null;\n }\n\n /**\n * A refresh failure is only fatal if the refresh token itself is rejected\n * (expired / invalid / forbidden). Network blips, timeouts, and 5xx (e.g. a\n * backend restart mid-session) are transient and must NOT log the user out.\n */\n function isFatalRefreshError(err: unknown): boolean {\n if (!(err instanceof RebaseApiError)) return false; // network/other → transient\n // Another tab (or a retry of our own request) rotated the token we\n // were holding. In cookie mode the jar may ALREADY contain the\n // replacement, so this is the one 401 that is worth retrying: giving\n // up here is precisely the bug where opening a second tab signs you\n // out of both.\n if (err.code === \"TOKEN_ALREADY_USED\") return false;\n if (err.code === \"INVALID_TOKEN\" || err.code === \"TOKEN_EXPIRED\") return true;\n // 401/403 are auth failures; other statuses (incl. 5xx, 0) are transient.\n return err.status === 401 || err.status === 403;\n }\n\n /**\n * Drop this client's session without telling the server.\n *\n * `signOut()` is a user action: it POSTs /logout, which revokes the whole\n * sign-in. That is the wrong hammer for a refresh that failed. Our token\n * may be stale precisely because a sibling tab holds a live one, and\n * logging out on its behalf would turn one tab's bad luck into everybody\n * being signed out — the exact failure this work exists to remove.\n *\n * Storage is left alone when it no longer holds the session being\n * abandoned. It is shared with every other tab of the app, so a different\n * session there is a sibling's live one — a rotation this tab missed, or\n * another sign-in — and deleting it signed that tab out on its next load.\n */\n function abandonSessionLocally() {\n endSession(storageHoldsCurrentSession());\n }\n\n /** Whether storage holds this client's session — or no session at all. */\n function storageHoldsCurrentSession(): boolean {\n const persisted = loadStoredSession();\n return !persisted || !currentSession || persisted.refreshToken === currentSession.refreshToken;\n }\n\n /** Drop the session from memory, storage and the transport, and say so. */\n function endSession(clearStorage = true) {\n sessionEpoch++;\n currentSession = null;\n if (clearStorage) clearStoredSession();\n if (refreshTimeout) {\n clearTimeout(refreshTimeout);\n refreshTimeout = null;\n }\n transport.setToken(null);\n emit(\"SIGNED_OUT\", null);\n }\n\n /**\n * Recover from a 401 on an ordinary API request.\n *\n * Returns `true` when the caller should retry — we minted a fresh access\n * token. When the refresh is rejected *fatally* (the refresh token itself\n * is invalid, expired or revoked) this client can no longer act as the\n * user at all, so we drop the session and emit `SIGNED_OUT`. UIs gate on\n * that event, so they show their login screen instead of leaving the user\n * staring at \"Invalid or expired token\" on every view.\n *\n * Transient failures (offline, 5xx, backend restarting) keep the session:\n * the scheduled refresh backs off and retries, and the token is very\n * likely still good once the backend answers again.\n */\n async function handleUnauthorized(): Promise<boolean> {\n // No session to recover: the 401 is just an anonymous caller hitting a\n // protected route. Emitting SIGNED_OUT here would fire sign-out\n // handlers for a user who was never signed in.\n if (!currentSession) return false;\n\n // Nothing to refresh *with* — the access token is dead and there is no\n // way back. Same end state as a rejected refresh token.\n if (authFlowMode !== \"cookie\" && !currentSession.refreshToken) {\n abandonSessionLocally();\n return false;\n }\n\n const epoch = sessionEpoch;\n try {\n await refreshSession();\n // Retry only as the session the request was refused for. If it\n // ended while the refresh was in flight, re-sending the request\n // would send it as whoever signed in since — or as nobody.\n return epoch === sessionEpoch;\n } catch (err) {\n if (epoch === sessionEpoch && isFatalRefreshError(err)) {\n abandonSessionLocally();\n }\n return false;\n }\n }\n\n /**\n * Requests the refresh itself is making — the `/me` read when its answer\n * carries no user. They go out with the token the refresh has just put on\n * the transport, and must not wait for the refresh they are part of.\n */\n let refreshOwnRequests = 0;\n\n /** The cookie-mode restore started on load, until it settles. */\n let cookieRestore: Promise<void> | null = null;\n\n /**\n * How close to expiry a token is treated as spent by a request about to\n * carry it — the margin the socket's token check uses. Well inside the\n * scheduled refresh's own lead ({@link REFRESH_BUFFER_MS}), so while that\n * timer runs a request never waits for it.\n */\n const SPENT_TOKEN_MARGIN_MS = 10_000;\n\n /**\n * The transport's {@link TokenRefresher}: whether a request should wait for\n * a fresher token than the one held, and which token to send when it does.\n *\n * It waits when this client holds a session whose access token has expired\n * (or is about to) and that can be refreshed. That is a session restored\n * from storage with an expired token, whose refresh started at load and\n * left the transport holding no token at all until it landed — so a request\n * made meanwhile went out anonymous and, on a route with a public read\n * policy, was answered with public rows instead of a 401. And it is a\n * session whose refresh timer never fired — a laptop asleep past\n * `expiresAt`, a frozen tab restored — where every request went out with a\n * dead token, came back 401, and was sent again: twice the requests and a\n * round trip of latency each.\n *\n * The refresh is {@link refreshSession}, so a burst of requests shares one\n * (the one started at load included). A refresh that fails hands back the\n * token the request would have carried anyway: the 401 path then does\n * exactly what it always did. One whose session ended while it ran hands\n * back nothing, so the transport sends what it holds now — no token after\n * a sign-out, the new account's after a sign-in — and never the token of\n * the account that has gone. The realtime socket authenticates through\n * this same path.\n */\n function tokenForRequest(): Promise<string | undefined> | undefined {\n if (refreshOwnRequests > 0) return undefined;\n const session = currentSession;\n // Cookie mode restores on load with no session in memory at all — the\n // refresh token is a cookie this page cannot read — so a request made\n // before that restore answers would go out anonymous. It waits for the\n // answer and then carries whatever token the transport holds: the\n // restored session's, or none when there was nothing to restore.\n if (!session) return cookieRestore?.then(() => undefined);\n // An expiry nobody stated is not one that has passed (see\n // `scheduleRefresh`): read as spent, it refreshed before every request.\n if (!Number.isFinite(session.expiresAt)) return undefined;\n if (session.expiresAt > Date.now() + SPENT_TOKEN_MARGIN_MS) return undefined;\n if (authFlowMode !== \"cookie\" && !session.refreshToken) return undefined;\n const epoch = sessionEpoch;\n return refreshSession().then(\n (fresh) => (epoch === sessionEpoch ? fresh.accessToken : undefined),\n () => (epoch === sessionEpoch ? session.accessToken : undefined)\n );\n }\n transport.setTokenRefresher?.(tokenForRequest);\n\n async function attemptScheduledRefresh(attempt: number) {\n const epoch = sessionEpoch;\n try {\n await refreshSession();\n // On success, refreshSession() re-schedules the next refresh itself.\n } catch (err) {\n // The session this was refreshing has ended — signed out, or\n // replaced by a sign-in that schedules its own. Retrying would\n // refresh nothing, and giving up would sign out a second time.\n if (epoch !== sessionEpoch) return;\n if (isFatalRefreshError(err)) {\n abandonSessionLocally();\n return;\n }\n // Refused as malformed, and already reported by `requestRefresh`.\n // Sending it again gets the same answer. The token was never\n // refused, so the session stays, as it does when retries run out.\n if (isMalformedRefreshError(err)) return;\n if (attempt >= MAX_REFRESH_RETRIES) {\n // Out of retries. A token the server kept refusing — as\n // already used, the one refusal retried at all — is as dead as\n // one it rejected outright. A server that could not be reached,\n // or kept failing, has said nothing about the token: the\n // session stays, and the next request it refuses tries again.\n if (err instanceof RebaseApiError && (err.status === 401 || err.status === 403)) {\n abandonSessionLocally();\n }\n return;\n }\n // Transient failure — back off and retry rather than dropping the session.\n scheduleRetry(attempt);\n }\n }\n\n /** Try the scheduled refresh again after a backoff: 1s, 2s, 4s, … capped. */\n function scheduleRetry(attempt: number) {\n const backoff = Math.min(REFRESH_RETRY_BASE_MS * 2 ** attempt, REFRESH_RETRY_MAX_MS);\n refreshTimeout = setTimeout(() => { void attemptScheduledRefresh(attempt + 1); }, backoff);\n }\n\n function scheduleRefresh(expiresAt: number) {\n if (refreshTimeout) clearTimeout(refreshTimeout);\n if (!autoRefresh) return;\n // No stated expiry — a backend that left `accessTokenExpiresAt` out.\n // `delay` would be NaN, which `setTimeout` runs at once, and every\n // refresh would re-arm the next with no gap. Nothing to schedule: the\n // token is used until the server refuses it, and the 401 path\n // refreshes it then.\n if (!Number.isFinite(expiresAt)) return;\n\n const delay = (expiresAt - REFRESH_BUFFER_MS) - Date.now();\n\n if (delay <= 0) {\n void attemptScheduledRefresh(0);\n return;\n }\n\n // `setTimeout` holds its delay in a 32-bit signed integer. Past\n // ~24.8 days it does not wait — it clamps to 1ms and fires at once. The\n // refresh would then land, receive a token expiring just as far out,\n // schedule again and overflow again: a hot loop against\n // `/auth/refresh`, one per open tab.\n //\n // `auth.accessExpiresIn` is configurable and defaults to \"1h\", so this\n // is dormant on a default deployment and immediate on `\"30d\"` — an\n // ordinary setting for an internal tool. Re-arm instead of refreshing:\n // sleep the maximum, then work out again how long is left.\n if (delay > MAX_TIMER_DELAY_MS) {\n refreshTimeout = setTimeout(() => scheduleRefresh(expiresAt), MAX_TIMER_DELAY_MS);\n return;\n }\n\n refreshTimeout = setTimeout(() => { void attemptScheduledRefresh(0); }, delay);\n }\n\n /**\n * Stop the scheduled token refresh, leaving the session itself alone.\n *\n * This is teardown, not sign-out. `scheduleRefresh` arms an ordinary\n * `setTimeout` up to a token lifetime away, and it is not `unref`'d — so on\n * Node it holds the event loop open by itself. `client.close()` promised\n * that \"a script that does not call this will not exit on its own\", which\n * was true, while the converse it plainly implies was not: a signed-in\n * client that closed its socket still hung, because this timer outlived it.\n * Any script, cron handler or job that signs in hit that.\n *\n * Deliberately does NOT clear the session, touch storage, or emit\n * SIGNED_OUT. Closing a client is not the user signing out — `signOut()`\n * POSTs /logout and revokes the whole sign-in, which is the wrong hammer\n * (see `abandonSessionLocally`) — and a persisted session must still be\n * there for the next client to restore.\n */\n function stopAutoRefresh() {\n if (refreshTimeout) {\n clearTimeout(refreshTimeout);\n refreshTimeout = null;\n }\n }\n\n function handleAuthResponse(data: { tokens: AuthTokens, user: Record<string, unknown> }, event?: AuthChangeEvent): RebaseSession {\n sessionEpoch++;\n const user: User = mapRawUser(data.user);\n const session: RebaseSession = {\n accessToken: data.tokens.accessToken,\n refreshToken: heldRefreshToken(data.tokens.refreshToken),\n expiresAt: data.tokens.accessTokenExpiresAt,\n user\n };\n currentSession = session;\n saveSession(session);\n transport.setToken(session.accessToken);\n scheduleRefresh(session.expiresAt);\n emit(event || \"SIGNED_IN\", session);\n return session;\n }\n\n async function signInWithEmail(email: string, password: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/login\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email,\npassword }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n async function signUp(email: string, password: string, displayName?: string) {\n const fetchFn = getFetch();\n const payload: Record<string, string> = { email,\npassword };\n if (displayName !== undefined) payload.displayName = displayName;\n const res = await fetchFn(authUrl(\"/register\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n // A backend with `requireEmailVerification` registers confirm-first:\n // no session until the address is proven, and the same answer whether\n // or not the address already had an account.\n if (body && typeof body === \"object\" && \"confirmationRequired\" in body && body.confirmationRequired === true) {\n return { confirmationRequired: true as const, user: null, accessToken: null, refreshToken: null };\n }\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return {\n confirmationRequired: false as const,\n user: session.user,\n accessToken: session.accessToken,\n refreshToken: session.refreshToken\n };\n }\n\n /**\n * Sign in with Google.\n *\n * Supports three invocation styles:\n * - `signInWithGoogle({ idToken })` — ID-token flow (One Tap / Sign In button)\n * - `signInWithGoogle({ accessToken })` — Access-token flow (popup)\n * - `signInWithGoogle({ code, redirectUri })` — Authorization code flow (most secure)\n */\n async function signInWithGoogle(\n payload: { idToken: string } | { accessToken: string } | { code: string; redirectUri: string }\n ) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/google\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const responseBody = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, responseBody, res.statusText);\n const session = handleAuthResponse(responseBody, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n async function signInWithLinkedin(code: string, redirectUri: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/linkedin\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ code,\nredirectUri }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Generic OAuth sign-in. Posts the given payload to `/auth/{providerId}`.\n * Use this for any provider registered on the backend.\n */\n async function signInWithOAuth(providerId: string, payload: Record<string, unknown>) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(`/${providerId}`), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n // Convenience wrappers for all supported OAuth providers\n\n async function signInWithGitHub(code: string, redirectUri: string) {\n return signInWithOAuth(\"github\", { code,\nredirectUri });\n }\n\n async function signInWithMicrosoft(code: string, redirectUri: string) {\n return signInWithOAuth(\"microsoft\", { code,\nredirectUri });\n }\n\n async function signInWithApple(code: string, redirectUri: string, user?: { name?: { firstName?: string; lastName?: string }; email?: string }) {\n return signInWithOAuth(\"apple\", { code,\nredirectUri,\nuser });\n }\n\n async function signInWithFacebook(code: string, redirectUri: string) {\n return signInWithOAuth(\"facebook\", { code,\nredirectUri });\n }\n\n async function signInWithTwitter(code: string, redirectUri: string, codeVerifier: string) {\n return signInWithOAuth(\"twitter\", { code,\nredirectUri,\ncodeVerifier });\n }\n\n async function signInWithDiscord(code: string, redirectUri: string) {\n return signInWithOAuth(\"discord\", { code,\nredirectUri });\n }\n\n async function signInWithGitLab(code: string, redirectUri: string) {\n return signInWithOAuth(\"gitlab\", { code,\nredirectUri });\n }\n\n async function signInWithBitbucket(code: string, redirectUri: string) {\n return signInWithOAuth(\"bitbucket\", { code,\nredirectUri });\n }\n\n async function signInWithSlack(code: string, redirectUri: string) {\n return signInWithOAuth(\"slack\", { code,\nredirectUri });\n }\n\n async function signInWithSpotify(code: string, redirectUri: string) {\n return signInWithOAuth(\"spotify\", { code,\nredirectUri });\n }\n\n async function signOut() {\n // The sign-out takes effect when it is asked for, not when /logout\n // answers: a refresh landing in between must not be adopted.\n sessionEpoch++;\n const fetchFn = getFetch();\n try {\n if (authFlowMode === \"cookie\" || currentSession?.refreshToken) {\n await fetchFn(authUrl(\"/logout\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: refreshTokenBody(),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n }\n } catch (e) { /* ignore */ }\n endSession();\n }\n\n /**\n * Serialise refreshes across TABS, not just within one.\n *\n * The in-flight promise below covers callers inside a single JavaScript\n * context. It does nothing about the far more common case: two tabs of the\n * same app booting together, each firing its own /refresh with the same\n * cookie. The server tolerates that now (superseded tokens stay usable for\n * a grace window), but tolerating a stampede is not the same as avoiding\n * one, and every extra rotation is another chance to end up holding a\n * token whose response never arrived.\n *\n * Web Locks are best-effort on purpose. supabase-js shipped this and then\n * spent a year fielding deadlock reports — a lock held by a crashed or\n * frozen tab must never be able to wedge sign-in — so a lock we cannot\n * take within the timeout is simply not taken, and the refresh proceeds\n * unserialised, exactly as it did before.\n */\n const REFRESH_LOCK_NAME = \"rebase-auth-refresh\";\n const REFRESH_LOCK_TIMEOUT_MS = 5000;\n\n async function withRefreshLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as { navigator?: { locks?: LockManager } }).navigator?.locks;\n if (!locks?.request) return fn();\n\n const controller = new AbortController();\n const giveUp = setTimeout(() => controller.abort(), REFRESH_LOCK_TIMEOUT_MS);\n try {\n return await locks.request(\n REFRESH_LOCK_NAME,\n { signal: controller.signal },\n async () => fn()\n ) as T;\n } catch (e) {\n // AbortError means only that we waited long enough for the lock.\n // Anything the callback itself threw has to keep propagating.\n if ((e as { name?: string })?.name !== \"AbortError\") throw e;\n return fn();\n } finally {\n clearTimeout(giveUp);\n }\n }\n\n function refreshSession(): Promise<RebaseSession> {\n // Share a single in-flight refresh across concurrent callers — of the\n // same session. One started for a session that has since ended answers\n // with nothing of use to the session that replaced it.\n if (inFlightRefresh && inFlightEpoch === sessionEpoch) return inFlightRefresh;\n const refresh = withRefreshLock(() => doRefreshSession()).finally(() => {\n if (inFlightRefresh === refresh) inFlightRefresh = null;\n });\n inFlightRefresh = refresh;\n inFlightEpoch = sessionEpoch;\n return refresh;\n }\n\n /**\n * What a refresh answers when its session ended while it was in flight.\n *\n * The tokens it received belong to a session this client no longer holds,\n * so they are dropped, and so is any error it hit: a refusal of the old\n * session's token says nothing about the new one, and acting on it signed\n * out whoever had signed in since. The caller gets the session there is\n * now — or, if there is none, is told so.\n */\n function supersededRefresh(): RebaseSession {\n if (currentSession) return currentSession;\n throw new RebaseClientError(\n \"The session was signed out while it was being refreshed.\",\n { code: \"NOT_SIGNED_IN\" }\n );\n }\n\n /**\n * The session a sibling tab has persisted since this one last looked, when\n * it continues this one.\n *\n * Tabs of one app share `localStorage` but each holds its session in\n * memory, and a refresh rotates the refresh token. Once one tab has\n * refreshed, every other tab is holding a token the server has already\n * superseded: presented after the server's reuse window it is refused\n * with TOKEN_ALREADY_USED, again on every retry, until the tab gives up\n * and signs out. Its replacement was in storage all along.\n *\n * Only a session of the same user counts. A different user in storage\n * means another tab signed someone else in, and that is not this tab's\n * session to take over.\n */\n function siblingSession(): RebaseSession | null {\n if (!persistSession || authFlowMode === \"cookie\" || !currentSession) return null;\n const persisted = loadStoredSession();\n if (!persisted?.accessToken || !persisted.refreshToken) return null;\n if (persisted.refreshToken === currentSession.refreshToken) return null;\n if (!persisted.user?.uid || persisted.user.uid !== currentSession.user?.uid) return null;\n return persisted;\n }\n\n /**\n * Take over a sibling's session. Its access token is used as it is while\n * it has time left — exactly as a session restored on load would be — and\n * otherwise this answers `null` and the caller refreshes, now with the\n * sibling's refresh token rather than the superseded one.\n */\n function adoptSibling(sibling: RebaseSession): RebaseSession | null {\n currentSession = sibling;\n if (sibling.expiresAt - REFRESH_BUFFER_MS <= Date.now()) return null;\n transport.setToken(sibling.accessToken);\n scheduleRefresh(sibling.expiresAt);\n emit(\"TOKEN_REFRESHED\", sibling);\n return sibling;\n }\n\n async function doRefreshSession(): Promise<RebaseSession> {\n const epoch = sessionEpoch;\n try {\n // Inside the refresh lock, so a sibling that held it has finished\n // and persisted what it received.\n const sibling = siblingSession();\n if (sibling) {\n const adopted = adoptSibling(sibling);\n if (adopted) return adopted;\n }\n try {\n return await requestRefresh(epoch);\n } catch (err) {\n // Refused as already used: a sibling rotated it while this\n // request was on the wire (or without taking the lock).\n const late = epoch === sessionEpoch\n && err instanceof RebaseApiError\n && err.code === \"TOKEN_ALREADY_USED\"\n ? siblingSession()\n : null;\n if (!late) throw err;\n return adoptSibling(late) ?? await requestRefresh(epoch);\n }\n } catch (err) {\n if (epoch !== sessionEpoch) return supersededRefresh();\n throw err;\n }\n }\n\n async function requestRefresh(epoch: number): Promise<RebaseSession> {\n if (authFlowMode !== \"cookie\" && !currentSession?.refreshToken) {\n // A `RebaseClientError`, not a bare `Error`: the SDK documents one\n // class a `catch` block has to check for, and this is raised before\n // any request is made, which is exactly what that class is.\n throw new RebaseClientError(\n \"No active session to refresh. Sign in first, or set `authFlowMode: \\\"cookie\\\"` \" +\n \"if the refresh token is held in an HttpOnly cookie.\",\n { code: \"NOT_SIGNED_IN\" }\n );\n }\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/refresh\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: refreshTokenBody(),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (epoch !== sessionEpoch) return supersededRefresh();\n if (!res.ok) {\n const err = apiError(res.status, body, res.statusText);\n if (isMalformedRefreshError(err)) {\n // Said out loud, once per refusal, because nothing else will:\n // the session cannot be renewed, and every caller of a refresh\n // treats a non-auth failure as something to wait out.\n console.error(\n `[rebase] POST ${authPath}/refresh was refused as malformed ` +\n `(${res.status}${err.code ? ` ${err.code}` : \"\"}): ${err.message}. ` +\n \"The client and server disagree on the refresh request, so this session \" +\n \"cannot be renewed and retrying will not help. Check that @rebasepro/client \" +\n \"and @rebasepro/server are on compatible versions, and that `authFlowMode` \" +\n \"matches the server's `cookieAuth`.\"\n );\n }\n throw err;\n }\n\n const accessToken = body.tokens.accessToken;\n transport.setToken(accessToken);\n\n // Resolve the user, in order of preference:\n // 1. the user returned by /refresh (modern backends include it),\n // 2. the user already in memory,\n // 3. a fetch of /me — required to restore a session from an httpOnly\n // cookie alone (cold start in cookie mode), where there is no\n // in-memory user and the backend didn't echo one.\n let user = currentSession?.user;\n if (body.user && typeof body.user.uid === \"string\") {\n user = mapRawUser(body.user as Record<string, unknown>);\n } else if (!user || !user.uid) {\n refreshOwnRequests++;\n try {\n user = await getUser();\n } catch { /* fall through to the empty stub below */ } finally {\n refreshOwnRequests--;\n }\n // Whoever ended the session meanwhile has already set the\n // transport's token; nothing of this refresh is kept.\n if (epoch !== sessionEpoch) return supersededRefresh();\n }\n\n const session: RebaseSession = {\n accessToken,\n refreshToken: heldRefreshToken(body.tokens.refreshToken),\n expiresAt: body.tokens.accessTokenExpiresAt,\n user: user ?? EMPTY_USER\n };\n currentSession = session;\n saveSession(session);\n transport.setToken(session.accessToken);\n scheduleRefresh(session.expiresAt);\n emit(\"TOKEN_REFRESHED\", session);\n return session;\n }\n\n async function getUser() {\n const data = await transport.request<{ user: User }>(authPath + \"/me\", { method: \"GET\" });\n return data.user;\n }\n\n /**\n * Resolve an email to a minimal public profile (`uid`, `displayName`,\n * `photoURL`) for invite-by-email flows. Returns `null` when no account\n * matches. Requires the backend to opt in via `auth.allowUserLookup`;\n * otherwise the endpoint is absent and this rejects.\n */\n async function findUserByEmail(email: string): Promise<PublicUserProfile | null> {\n const data = await transport.request<{ user: PublicUserProfile | null }>(authPath + \"/find-user\", {\n method: \"POST\",\n body: JSON.stringify({ email })\n });\n return data.user;\n }\n\n async function updateUser(updates: { displayName?: string, photoURL?: string }) {\n const data = await transport.request<{ user: User }>(authPath + \"/me\", {\n method: \"PATCH\",\n body: JSON.stringify(updates)\n });\n if (currentSession) {\n currentSession = { ...currentSession,\nuser: data.user };\n saveSession(currentSession);\n emit(\"USER_UPDATED\", currentSession);\n }\n return data.user;\n }\n\n async function resetPasswordForEmail(email: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/forgot-password\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as { success: boolean; message: string; };\n }\n\n async function resetPassword(token: string, password: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/reset-password\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token,\npassword })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as { success: boolean; message: string; };\n }\n\n /**\n * Change the signed-in account's password. Every other session ends; this\n * one is replaced by the fresh session the server answers with, so the\n * client stays signed in.\n */\n async function changePassword(oldPassword: string, newPassword: string) {\n // Sent with this client's own fetch rather than the transport, so that\n // in cookie mode the new refresh cookie the answer sets is kept — so\n // the transport's refresh-on-401 is done here, before the call.\n if (currentSession && currentSession.expiresAt <= Date.now() + 10_000) {\n await refreshSession().catch(() => undefined);\n }\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/change-password\"), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(currentSession?.accessToken ? { Authorization: `Bearer ${currentSession.accessToken}` } : {})\n },\n body: JSON.stringify({ oldPassword, newPassword }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const result = body as { success: boolean; message: string; tokens?: AuthTokens; user?: Record<string, unknown> };\n // A server from before this release answers without a session, having\n // revoked this one: nothing to adopt, and the next call signs out.\n if (result.tokens && result.user) {\n handleAuthResponse({ tokens: result.tokens, user: result.user }, \"TOKEN_REFRESHED\");\n }\n return { success: result.success, message: result.message };\n }\n\n /**\n * Link an OAuth provider to the **currently signed-in** account.\n *\n * Use this when `signIn*` failed with `EMAIL_NOT_VERIFIED` — an account\n * with that email already exists under a different sign-in method — or to\n * attach a provider whose email differs from the account's.\n *\n * The payload is the same one the provider's sign-in method takes, e.g.\n * `linkProvider(\"google\", { idToken })`.\n *\n * Unlike sign-in, this does not require the provider to have verified the\n * email, and the emails need not match: the active session already proves\n * account ownership.\n *\n * Throws `IDENTITY_ALREADY_LINKED` (409) if that provider identity is\n * attached to a different user. Succeeds idempotently (`alreadyLinked:\n * true`) if it is already attached to the current one.\n */\n async function linkProvider(\n providerId: string,\n payload: Record<string, unknown>\n ) {\n return transport.request<{\n success: boolean;\n provider: string;\n alreadyLinked: boolean;\n /**\n * The provider's profile picture, when it has one. Usually a signed\n * URL with an expiry, so copy the bytes rather than storing this.\n */\n photoURL?: string | null;\n }>(\n authPath + \"/link/\" + providerId,\n {\n method: \"POST\",\n body: JSON.stringify(payload)\n }\n );\n }\n\n async function sendVerificationEmail() {\n return transport.request<{ success: boolean; message: string; }>(authPath + \"/send-verification\", {\n method: \"POST\"\n });\n }\n\n /**\n * Verify the address with the token from the link.\n *\n * The link proves the inbox, not who registered the address, so the\n * server keeps only what this call also proves: the current session (sent\n * when this client is signed in as that account) or `password`. An account\n * carrying a password or identity neither proves is refused with\n * `PROOF_REQUIRED` and the token is left unspent — ask for the password,\n * or pass `removeUnproven: true` to verify without it. With `password`,\n * the call also signs in.\n */\n async function verifyEmail(token: string, options?: { password?: string; removeUnproven?: boolean }) {\n const fetchFn = getFetch();\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (currentSession?.accessToken) headers.Authorization = `Bearer ${currentSession.accessToken}`;\n const res = await fetchFn(authUrl(\"/verify-email\"), {\n method: \"POST\",\n headers,\n body: JSON.stringify({ token, ...options }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const result = body as {\n success: boolean;\n message?: string;\n /** A password nobody proved was on the account, and is gone. */\n passwordRemoved: boolean;\n /** Providers whose identities were detached for not vouching for the address. */\n removedProviders: string[];\n tokens?: AuthTokens;\n user?: Record<string, unknown>;\n };\n const session = result.tokens && result.user\n ? handleAuthResponse({ tokens: result.tokens, user: result.user }, \"SIGNED_IN\")\n : null;\n return { success: result.success, passwordRemoved: result.passwordRemoved, removedProviders: result.removedProviders ?? [], session };\n }\n\n /**\n * Move the signed-in account to `newEmail`. The server mails a link to the\n * new address and a notice to the current one; nothing changes until the\n * link is followed and {@link confirmEmailChange} is called with its\n * token. A new request replaces the last.\n *\n * Rejects with `EMAIL_EXISTS` or `UNDELIVERABLE_ADDRESS` (409),\n * `EMAIL_UNCHANGED` (400), `AAL2_REQUIRED` when the account has a second\n * factor this session did not present, and `ANONYMOUS_USER` for a guest.\n */\n async function changeEmail(newEmail: string) {\n const data = await transport.request<{ success: boolean; pendingEmail: string; expiresAt: string }>(authPath + \"/change-email\", {\n method: \"POST\",\n body: JSON.stringify({ newEmail })\n });\n // The user this client holds says a change is waiting, as `/me` does.\n if (currentSession) {\n currentSession = { ...currentSession, user: { ...currentSession.user, pendingEmail: data.pendingEmail } };\n saveSession(currentSession);\n emit(\"USER_UPDATED\", currentSession);\n }\n return { pendingEmail: data.pendingEmail, expiresAt: data.expiresAt };\n }\n\n /**\n * Follow an address-change link: the token from\n * `<frontend>/confirm-email-change?token=…`. Needs no session — the link\n * proves the new inbox — and when this client is signed in as the account\n * it moved, its user is updated and `USER_UPDATED` is emitted.\n *\n * `removedProviders` names the sign-in providers detached because they\n * vouched for the old address. Rejects with `INVALID_TOKEN` for a spent,\n * replaced or expired link and `EMAIL_EXISTS` when the address was taken\n * while the link waited.\n */\n async function confirmEmailChange(token: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/confirm-email-change\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const result = body as { user: { uid: string; email: string }; removedProviders?: string[] };\n if (currentSession && currentSession.user.uid === result.user.uid) {\n currentSession = {\n ...currentSession,\n user: { ...currentSession.user, email: result.user.email, emailVerified: true, pendingEmail: null }\n };\n saveSession(currentSession);\n emit(\"USER_UPDATED\", currentSession);\n }\n return { uid: result.user.uid, email: result.user.email, removedProviders: result.removedProviders ?? [] };\n }\n\n async function sendMagicLink(email: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/magic-link\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as { success: boolean; message: string; };\n }\n\n async function verifyMagicLink(token: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/magic-link/verify\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Ask for a six-digit sign-in code by email.\n *\n * Answers the same thing whether or not the address has an account — do not\n * use the result to tell a person whether they are registered, because it\n * does not know.\n */\n async function sendEmailOtp(email: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/otp\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as { success: boolean; message: string; expiresInSeconds: number };\n }\n\n /**\n * Trade a code for a session.\n *\n * The address is sent with the code because the code is only valid for it:\n * that is what keeps a six-digit guess a guess against one account rather\n * than against every account at once.\n */\n async function verifyEmailOtp(email: string, code: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/otp/verify\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, code }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Sign in without credentials, as a throwaway account.\n *\n * The account is real — it has an id, roles and a session, so row-level\n * security scopes its data exactly as it would a signed-up user's. What it\n * does not have is a way back: nobody can sign in *as* it a second time, so\n * everything it owns is lost when the session is. `linkAnonymous` is how it\n * stops being throwaway.\n *\n * Refused with `ANONYMOUS_AUTH_DISABLED` unless the backend has it on.\n */\n async function signInAnonymously() {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/anonymous\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Put an email and password on the anonymous account this client holds.\n *\n * The user keeps their id, so everything they created while anonymous stays\n * theirs — which is the point: the alternative is asking a visitor to sign\n * up before they have any reason to.\n *\n * Requires a live anonymous session. `NOT_ANONYMOUS` (400) if the session\n * belongs to an ordinary account, `EMAIL_EXISTS` (409) if the address is\n * taken.\n */\n async function linkAnonymous(email: string, password: string) {\n const body = await transport.request<{ tokens: AuthTokens; user: Record<string, unknown> }>(\n authPath + \"/anonymous/link\",\n {\n method: \"POST\",\n body: JSON.stringify({ email, password })\n }\n );\n const session = handleAuthResponse(body, \"USER_UPDATED\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Multi-factor authentication: TOTP factors, and the challenge that raises\n * a session to `aal2`.\n *\n * The backend has served all six of these since MFA landed; the SDK exposed\n * none of them, so every app that wanted MFA hand-wrote `fetch` calls\n * against `/auth/mfa/*` — including the auth header and the cookie flag,\n * which is exactly what a client is for.\n */\n const mfa = {\n /**\n * Start enrolling a TOTP factor.\n *\n * Returns the secret and an `otpauth://` URI to render as a QR code,\n * plus — with the account's first factor — ten single-use recovery\n * codes. **Show the recovery codes once and never again**: only their\n * hashes are stored. Adding another factor keeps the codes the account\n * has, and `recoveryCodes` is then `null`; `regenerateRecoveryCodes()`\n * replaces them.\n *\n * The factor is not usable until `verify` confirms the user's\n * authenticator produced a correct code from it.\n */\n async enroll(options?: { friendlyName?: string; issuer?: string }) {\n return transport.request<{\n factor: { id: string; factorType: string; friendlyName?: string };\n totp: { secret: string; uri: string; qrUri: string };\n recoveryCodes: string[] | null;\n }>(authPath + \"/mfa/enroll\", {\n method: \"POST\",\n body: JSON.stringify(options ?? {})\n });\n },\n\n /** Confirm an enrolment with a code from the authenticator app. */\n async verify(factorId: string, code: string) {\n return transport.request<{ success: boolean; factor: { id: string; verified: boolean } }>(\n authPath + \"/mfa/verify\",\n { method: \"POST\", body: JSON.stringify({ factorId, code }) }\n );\n },\n\n /** The factors on this account, verified or not. */\n async listFactors() {\n const data = await transport.request<{\n factors: { id: string; factorType: string; friendlyName?: string; verified: boolean; createdAt: string }[];\n }>(authPath + \"/mfa/factors\", { method: \"GET\" });\n return data.factors;\n },\n\n /**\n * Replace the account's recovery codes with ten new ones, shown once.\n * Needs an `aal2` session. Adding a second factor keeps the codes the\n * account already has; this is how to get new ones.\n */\n async regenerateRecoveryCodes() {\n return transport.request<{ recoveryCodes: string[] }>(authPath + \"/mfa/recovery-codes\", { method: \"POST\" });\n },\n\n /**\n * Remove a factor. Once the account has a verified factor this\n * requires an `aal2` session — one that has already answered a\n * challenge — so a stolen `aal1` token cannot turn MFA off. An\n * unfinished first enrolment can be removed from any session.\n */\n async unenroll(factorId: string) {\n return transport.request<{ success: boolean; message: string }>(\n authPath + \"/mfa/unenroll\",\n { method: \"DELETE\", body: JSON.stringify({ factorId }) }\n );\n },\n\n /**\n * Open a challenge against a verified factor. Expires in five minutes.\n *\n * Pass `mfaToken` to finish a sign-in that was refused with\n * `MFA_REQUIRED`: it is the `details.mfaToken` of that refusal, and the\n * challenge is opened with it as this one request's credential. Without\n * it, the challenge steps up the session this client already holds.\n */\n async challenge(factorId: string, options?: MfaPendingOptions) {\n type Challenge = { challengeId: string; factorId: string; expiresAt: string };\n const payload = JSON.stringify({ factorId });\n if (options?.mfaToken) {\n return withPendingToken<Challenge>(\"/mfa/challenge\", payload, options.mfaToken);\n }\n return transport.request<Challenge>(authPath + \"/mfa/challenge\", { method: \"POST\", body: payload });\n },\n\n /**\n * Answer a challenge with a TOTP code or a recovery code.\n *\n * This is where an MFA-enrolled account's real session is minted, at\n * `aal2` — so this client adopts the returned tokens and announces\n * `SIGNED_IN`, exactly as any other sign-in does. Pass the same\n * `mfaToken` the challenge was opened with when finishing a sign-in.\n */\n async verifyChallenge(challengeId: string, code: string, options?: MfaPendingOptions) {\n type Minted = { tokens: AuthTokens; user: Record<string, unknown> };\n const payload = JSON.stringify({ challengeId, code });\n const body = options?.mfaToken\n ? await withPendingToken<Minted>(\"/mfa/challenge/verify\", payload, options.mfaToken)\n : await transport.request<Minted>(authPath + \"/mfa/challenge/verify\", { method: \"POST\", body: payload });\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n };\n\n /**\n * POST to a challenge route with a sign-in's pending token as the credential.\n *\n * Straight to `fetch`, like the sign-in routes, and never through the\n * transport: the token is a credential for these two requests only. Set on\n * the transport it would go out on every request the app made while the\n * code step was open — and stay there if the step were abandoned. It would\n * also displace a session the client already holds, and a refused code\n * (401 `INVALID_CODE`) would be taken for an expired session and refreshed\n * and re-sent, spending a second of the challenge's attempts on it.\n */\n async function withPendingToken<T>(endpoint: string, payload: string, mfaToken: string): Promise<T> {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(endpoint), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${mfaToken}`\n },\n body: payload,\n // The verify route sets the refresh cookie in cookie mode.\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as T;\n }\n\n async function getSessions(): Promise<DeviceSession[]> {\n const data = await transport.request<{ sessions: DeviceSession[] }>(authPath + \"/sessions\", { method: \"GET\" });\n return data.sessions;\n }\n\n async function revokeSession(sessionId: string) {\n return transport.request<{ success: boolean }>(authPath + \"/sessions/\" + encodeURIComponent(sessionId), {\n method: \"DELETE\"\n });\n }\n\n async function revokeAllSessions() {\n const result = await transport.request<{ success: boolean }>(authPath + \"/sessions\", {\n method: \"DELETE\"\n });\n endSession();\n return result;\n }\n\n async function getAuthConfig() {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/config\"), {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" }\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as AuthConfig;\n }\n\n function getSession() {\n return currentSession;\n }\n\n function onAuthStateChange(callback: (event: AuthChangeEvent, session: RebaseSession | null) => void) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n }\n\n if (persistSession) {\n const stored = loadStoredSession();\n if (stored && stored.accessToken) {\n if (stored.expiresAt > Date.now()) {\n currentSession = stored;\n transport.setToken(stored.accessToken);\n scheduleRefresh(stored.expiresAt);\n settleRestore(\"restored\");\n } else if (authFlowMode === \"cookie\" || stored.refreshToken) {\n currentSession = stored;\n const epoch = sessionEpoch;\n refreshSession().then(() => {\n settleRestore(\"restored\");\n }).catch((err: unknown) => {\n let outcome: Exclude<SessionRestoreOutcome, \"pending\"> = isFatalRefreshError(err) || isMalformedRefreshError(err)\n ? \"refused\"\n : \"unreachable\";\n if (epoch === sessionEpoch && currentSession) {\n if (isFatalRefreshError(err)) {\n // The refresh token itself was refused: there is no\n // session to restore.\n if (storageHoldsCurrentSession()) clearStoredSession();\n currentSession = null;\n transport.setToken(null);\n } else {\n // Offline, or the backend restarting. The refresh\n // token was never refused, only not delivered, and\n // deleting it here signed out every app opened\n // without a connection — while a running app, on\n // the very same error, keeps its session. Keep it\n // here too, and try again on the backoff schedule.\n // A refusal as malformed is neither: it is not\n // retried, since it would be refused again.\n transport.setToken(currentSession.accessToken);\n if (autoRefresh && !isMalformedRefreshError(err)) scheduleRetry(0);\n }\n } else {\n // Signed out, or someone else signed in, while it was\n // in flight: whatever this refresh hit is not about\n // the session there is now.\n outcome = currentSession ? \"restored\" : \"none\";\n }\n settleRestore(outcome);\n });\n } else {\n settleRestore(\"none\");\n }\n } else if (authFlowMode === \"cookie\") {\n // Silent refresh on boot to pick up httpOnly session\n const restore = refreshSession().then(() => {\n settleRestore(\"restored\");\n }).catch((err: unknown) => {\n // A 401 is the usual answer — no cookie, or an expired one —\n // and means there is nothing to restore. Only a failure to\n // reach the server leaves the question open.\n settleRestore(isFatalRefreshError(err) || isMalformedRefreshError(err)\n ? (currentSession ? \"restored\" : \"none\")\n : (currentSession ? \"restored\" : \"unreachable\"));\n }).finally(() => {\n if (cookieRestore === restore) cookieRestore = null;\n });\n cookieRestore = restore;\n } else {\n settleRestore(\"none\");\n }\n } else {\n settleRestore(\"none\");\n }\n\n return {\n signInWithEmail,\n signUp,\n signInWithGoogle,\n signInWithLinkedin,\n signInWithOAuth,\n signInWithGitHub,\n signInWithMicrosoft,\n signInWithApple,\n signInWithFacebook,\n signInWithTwitter,\n signInWithDiscord,\n signInWithGitLab,\n signInWithBitbucket,\n signInWithSlack,\n signInWithSpotify,\n signInAnonymously,\n linkAnonymous,\n mfa,\n signOut,\n stopAutoRefresh,\n refreshSession,\n handleUnauthorized,\n getUser,\n findUserByEmail,\n updateUser,\n resetPasswordForEmail,\n resetPassword,\n changePassword,\n linkProvider,\n sendVerificationEmail,\n verifyEmail,\n changeEmail,\n confirmEmailChange,\n sendMagicLink,\n verifyMagicLink,\n sendEmailOtp,\n verifyEmailOtp,\n getSessions,\n revokeSession,\n revokeAllSessions,\n getAuthConfig,\n getSession,\n onAuthStateChange,\n // A client that neither persists sessions nor uses cookie auth has\n // nowhere to restore one from, so \"no session in memory\" is the final\n // answer rather than a reason to ask the server. See the docblock on\n // `AuthClient.canRestoreSession`.\n canRestoreSession: () => persistSession || authFlowMode === \"cookie\",\n isInitialized: () => isInitialized,\n /**\n * How restoring the session on load ended — see\n * {@link SessionRestoreOutcome}. `\"unreachable\"` with no session is\n * the one case where \"signed out\" is not known to be true: the\n * device could not ask.\n */\n restoreOutcome: (): SessionRestoreOutcome => restoreOutcome\n };\n}\n\n/**\n * How restoring the session on load ended — `auth.restoreOutcome()`.\n *\n * - `\"pending\"`: still restoring (`isInitialized()` has not settled).\n * - `\"restored\"`: a session was restored, from storage or from the server.\n * - `\"none\"`: there was nothing to restore.\n * - `\"refused\"`: there was a session to restore, and the server refused it.\n * - `\"unreachable\"`: the server could not be reached (or answered 5xx), so the\n * session was neither confirmed nor refused. With a stored session the\n * client keeps it and retries; in cookie mode, where nothing is stored on the\n * device, it has no session until a later refresh or sign-in succeeds.\n */\nexport type SessionRestoreOutcome = \"pending\" | \"restored\" | \"none\" | \"refused\" | \"unreachable\";\n\nexport interface CookieStorageOptions {\n path?: string;\n domain?: string;\n secure?: boolean;\n sameSite?: \"Lax\" | \"Strict\" | \"None\";\n maxAge?: number;\n}\n\nexport function createCookieStorage(options: CookieStorageOptions = {}): AuthStorage {\n const defaultOptions = {\n path: \"/\",\n sameSite: \"Lax\" as const,\n ...options\n };\n\n return {\n getItem(key: string): string | null {\n if (typeof document === \"undefined\") return null;\n const nameEQ = encodeURIComponent(key) + \"=\";\n const ca = document.cookie.split(\";\");\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) === \" \") c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) === 0) {\n return decodeURIComponent(c.substring(nameEQ.length, c.length));\n }\n }\n return null;\n },\n setItem(key: string, value: string): void {\n if (typeof document === \"undefined\") return;\n let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;\n\n if (defaultOptions.path) {\n cookieStr += `; path=${defaultOptions.path}`;\n }\n if (defaultOptions.domain) {\n cookieStr += `; domain=${defaultOptions.domain}`;\n }\n if (defaultOptions.maxAge !== undefined) {\n cookieStr += `; max-age=${defaultOptions.maxAge}`;\n } else {\n cookieStr += `; max-age=${365 * 24 * 60 * 60}`;\n }\n if (defaultOptions.secure) {\n cookieStr += \"; secure\";\n }\n if (defaultOptions.sameSite) {\n cookieStr += `; samesite=${defaultOptions.sameSite}`;\n }\n\n document.cookie = cookieStr;\n },\n removeItem(key: string): void {\n if (typeof document === \"undefined\") return;\n let cookieStr = `${encodeURIComponent(key)}=; path=${defaultOptions.path || \"/\"}; max-age=-1`;\n if (defaultOptions.domain) {\n cookieStr += `; domain=${defaultOptions.domain}`;\n }\n document.cookie = cookieStr;\n }\n };\n}\n","import type { Transport } from \"./transport\";\nimport type { AdminUser, RoleSummary } from \"@rebasepro/types\";\n\nexport type { AdminUser };\n\n\nexport interface CreateAdminOptions {\n adminPath?: string;\n}\n\n/** The window and filter `GET /admin/users` takes. */\nexport interface ListUsersOptions {\n search?: string;\n limit?: number;\n offset?: number;\n orderBy?: string;\n orderDir?: \"asc\" | \"desc\";\n}\n\nexport function createAdmin(transport: Transport, options?: CreateAdminOptions) {\n const opts = options || {};\n const adminPath = opts.adminPath || \"/admin\";\n\n /**\n * One page of users — the route's default is 25 — with the `total` that\n * says whether there are more. Takes the same window as\n * {@link listUsersPaginated}; without one, it used to hand back the first\n * page typed as the whole list.\n */\n async function listUsers(options?: ListUsersOptions) {\n return listUsersPaginated(options);\n }\n\n async function listUsersPaginated(options?: ListUsersOptions) {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n if (options?.offset !== undefined) params.set(\"offset\", String(options.offset));\n if (options?.search) params.set(\"search\", options.search);\n if (options?.orderBy) params.set(\"orderBy\", options.orderBy);\n if (options?.orderDir) params.set(\"orderDir\", options.orderDir);\n const qs = params.toString();\n return transport.request<{ users: AdminUser[]; total: number; limit: number; offset: number }>(\n adminPath + \"/users\" + (qs ? \"?\" + qs : \"\"), { method: \"GET\" }\n );\n }\n\n async function getUser(userId: string) {\n return transport.request<{ user: AdminUser }>(adminPath + \"/users/\" + encodeURIComponent(userId), { method: \"GET\" });\n }\n\n /**\n * Create an account. When the server could not send an invitation (no\n * email configured, or the send failed) the answer carries the generated\n * `temporaryPassword` — returned this once and never again.\n *\n * A chosen `password` is held to the password policy (`WEAK_PASSWORD`).\n * `skipPasswordPolicy: true` waives it, for an import of accounts whose\n * passwords another system chose; the server honours it only for an\n * administrator or the service key.\n */\n async function createUser(data: { email: string, displayName?: string, password?: string, roles?: string[], skipPasswordPolicy?: boolean }) {\n return transport.request<{ user: AdminUser; temporaryPassword?: string; invitationSent?: boolean; emailDeliveryFailed?: boolean }>(adminPath + \"/users\", {\n method: \"POST\",\n body: JSON.stringify(data)\n });\n }\n\n /**\n * Change an account. `disabled: true` switches it off — no sign-in, no\n * refresh, and every token it holds refused at once — without deleting it.\n */\n async function updateUser(userId: string, data: { email?: string, displayName?: string, password?: string, roles?: string[], disabled?: boolean }) {\n return transport.request<{ user: AdminUser }>(adminPath + \"/users/\" + encodeURIComponent(userId), {\n method: \"PUT\",\n body: JSON.stringify(data)\n });\n }\n\n async function deleteUser(userId: string) {\n return transport.request<{ success: boolean }>(adminPath + \"/users/\" + encodeURIComponent(userId), {\n method: \"DELETE\"\n });\n }\n\n /**\n * Remove every second factor and recovery code the account has, and end\n * its sessions — for someone who lost their authenticator and their\n * codes. Needs `users:write`.\n */\n async function resetMfa(userId: string) {\n return transport.request<{ success: boolean; removedFactors: number }>(\n adminPath + \"/users/\" + encodeURIComponent(userId) + \"/mfa\",\n { method: \"DELETE\" }\n );\n }\n\n async function resetPassword(userId: string, options?: { password?: string }) {\n return transport.request<{ user: AdminUser; temporaryPassword?: string; invitationSent?: boolean; emailDeliveryFailed?: boolean }>(\n adminPath + \"/users/\" + encodeURIComponent(userId) + \"/reset-password\",\n {\n method: \"POST\",\n ...(options?.password ? { body: JSON.stringify({ password: options.password }) } : {})\n }\n );\n }\n\n async function listRoles() {\n return transport.request<{ roles: RoleSummary[] }>(\n adminPath + \"/roles\",\n { method: \"GET\" }\n );\n }\n\n async function bootstrap() {\n return transport.request<{ success: boolean; message: string; user: { uid: string; roles: string[] } }>(adminPath + \"/bootstrap\", {\n method: \"POST\"\n });\n }\n\n return {\n listUsers,\n listUsersPaginated,\n getUser,\n createUser,\n updateUser,\n deleteUser,\n resetMfa,\n resetPassword,\n listRoles,\n bootstrap\n };\n}\n","import { Transport } from \"./transport\";\nimport type { CronJobStatus, CronJobLogEntry, CronJobListing, CronJobStarted } from \"@rebasepro/types\";\n\nexport interface CreateCronOptions {\n cronPath?: string;\n}\n\nexport function createCron(transport: Transport, options?: CreateCronOptions) {\n const cronPath = options?.cronPath || \"/admin/cron\";\n\n async function listJobs(): Promise<CronJobListing> {\n return transport.request<CronJobListing>(cronPath, { method: \"GET\" });\n }\n\n async function getJob(jobId: string): Promise<{ job: CronJobStatus }> {\n return transport.request<{ job: CronJobStatus }>(\n cronPath + \"/\" + encodeURIComponent(jobId),\n { method: \"GET\" }\n );\n }\n\n /**\n * Run a job now. By default the call waits for the run and answers with\n * its log; `{ async: true }` answers once the run has started (`202`), for\n * jobs longer than a proxy in front of the server will hold a request.\n */\n function triggerJob(jobId: string, options?: { async?: false }): Promise<{ log: CronJobLogEntry; job: CronJobStatus }>;\n function triggerJob(jobId: string, options: { async: true }): Promise<CronJobStarted>;\n async function triggerJob(\n jobId: string,\n options?: { async?: boolean }\n ): Promise<{ log: CronJobLogEntry; job: CronJobStatus } | CronJobStarted> {\n const path = cronPath + \"/\" + encodeURIComponent(jobId) + \"/trigger\";\n if (options?.async) {\n return transport.request<CronJobStarted>(path + \"?async=1\", { method: \"POST\" });\n }\n return transport.request<{ log: CronJobLogEntry; job: CronJobStatus }>(path, { method: \"POST\" });\n }\n\n async function getJobLogs(\n jobId: string,\n options?: { limit?: number }\n ): Promise<{ logs: CronJobLogEntry[] }> {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return transport.request<{ logs: CronJobLogEntry[] }>(\n cronPath + \"/\" + encodeURIComponent(jobId) + \"/logs\" + (qs ? \"?\" + qs : \"\"),\n { method: \"GET\" }\n );\n }\n\n async function toggleJob(\n jobId: string,\n enabled: boolean\n ): Promise<{ job: CronJobStatus }> {\n return transport.request<{ job: CronJobStatus }>(\n cronPath + \"/\" + encodeURIComponent(jobId),\n {\n method: \"PUT\",\n body: JSON.stringify({ enabled })\n }\n );\n }\n\n return {\n listJobs,\n getJob,\n triggerJob,\n getJobLogs,\n toggleJob\n };\n}\n","import { Transport } from \"./transport\";\nimport { RebaseApiError, type BackupDownloadLink, type BackupListing } from \"@rebasepro/types\";\n\nexport interface CreateBackupsOptions {\n backupsPath?: string;\n}\n\n/** One backup's bytes, as {@link createBackups}' `stream()` answers them. */\nexport interface BackupStream {\n /** The dump, read from the server as it is consumed. */\n stream: ReadableStream<Uint8Array>;\n /** Its size in bytes, when the server said. */\n size?: number;\n /** Its file name: the key's last segment, as the server names the download. */\n name: string;\n}\n\n/**\n * Statuses that say the server cannot mint a download link at all — one that\n * predates `POST /download-url` (404/405), or one with no JWT secret to sign\n * it with (503) — rather than that this backup cannot be had. `download()`\n * then falls back to reading the bytes itself.\n */\nconst NO_LINKS = new Set([404, 405, 503]);\n\nexport function createBackups(transport: Transport, options?: CreateBackupsOptions) {\n const backupsPath = options?.backupsPath || \"/admin/backups\";\n /** Mirror transport.request's URL construction (baseUrl + apiPath + path). */\n const downloadPath = () => `${transport.baseUrl}${transport.apiPath}${backupsPath}/download`;\n\n async function list(): Promise<BackupListing> {\n return transport.request(backupsPath, { method: \"GET\" });\n }\n\n /**\n * A link to one backup's bytes, good for 60 seconds. The URL carries a\n * token that stands in for the `Authorization` header, for this backup\n * only, so anything can be pointed at it — a browser, `curl`, a\n * `fetch` that streams it to a file. The URL is absolute, on this\n * client's base URL.\n */\n async function downloadUrl(key: string): Promise<BackupDownloadLink> {\n const link = await transport.request<BackupDownloadLink>(`${backupsPath}/download-url`, {\n method: \"POST\",\n body: JSON.stringify({ key })\n });\n return {\n url: `${downloadPath()}?key=${encodeURIComponent(key)}&token=${encodeURIComponent(link.token)}`,\n token: link.token,\n expiresIn: link.expiresIn\n };\n }\n\n /**\n * A backup's bytes as a stream, read as they are consumed — the way to\n * write one to a file outside a browser:\n * `await pipeline(Readable.fromWeb(stream), fs.createWriteStream(name))`.\n *\n * Through the transport's own `fetch` and headers, so a client configured\n * with its own `fetch` or with default headers (a gateway key, the schema\n * version) reaches the same place every other call does.\n */\n async function stream(key: string): Promise<BackupStream> {\n const url = `${downloadPath()}?key=${encodeURIComponent(key)}`;\n const token = await transport.resolveToken();\n const headers = transport.getHeaders();\n // A GET with no body: the JSON default says nothing true here.\n delete headers[\"Content-Type\"];\n if (token) headers.Authorization = `Bearer ${token}`;\n const res = await transport.fetchFn(url, { method: \"GET\", headers });\n if (!res.ok || !res.body) {\n throw new RebaseApiError(`Failed to download backup (${res.status})`, { status: res.status });\n }\n // `Content-Length` is CORS-safelisted; `Content-Disposition` is not,\n // so the name comes from the key, as the server's does.\n const length = Number(res.headers.get(\"Content-Length\"));\n return {\n stream: res.body,\n ...(Number.isFinite(length) && length > 0 ? { size: length } : {}),\n name: fileNameOf(key)\n };\n }\n\n /**\n * Save a backup in the browser: the browser is sent to a\n * {@link downloadUrl} link and streams the dump to disk, so the page never\n * holds it. It used to be fetched into a Blob — the whole database in the\n * tab's memory, with no progress shown.\n *\n * Against a server that cannot mint links (one older than them, or one\n * with no JWT secret) it reads the bytes itself and saves them as before.\n *\n * Browser only. Elsewhere, {@link stream} writes a backup to a file and\n * {@link downloadUrl} gives a link.\n */\n async function download(key: string): Promise<void> {\n if (typeof document === \"undefined\") {\n throw new Error(\n \"backups.download() hands the file to a browser to save. Outside one, use \" +\n \"backups.stream(key) to write it to a file, or backups.downloadUrl(key) for a link.\"\n );\n }\n let link: BackupDownloadLink;\n try {\n link = await downloadUrl(key);\n } catch (error: unknown) {\n if (!(error instanceof RebaseApiError) || error.status === undefined || !NO_LINKS.has(error.status)) throw error;\n const { stream: body, name } = await stream(key);\n const objectUrl = URL.createObjectURL(await new Response(body).blob());\n try {\n saveFrom(objectUrl, name);\n } finally {\n URL.revokeObjectURL(objectUrl);\n }\n return;\n }\n saveFrom(link.url, fileNameOf(key));\n }\n\n return { list, download, downloadUrl, stream };\n}\n\n/** The last segment of a backup's key: its file name, local path or object key alike. */\nfunction fileNameOf(key: string): string {\n return key.split(/[\\\\/]/).pop() || key;\n}\n\n/**\n * Have the browser save `url` as `fileName`. The server answers a backup with\n * `Content-Disposition: attachment`, so the browser saves it rather than\n * navigating, cross-origin too, where `download` is ignored.\n */\nfunction saveFrom(url: string, fileName: string): void {\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = fileName;\n a.rel = \"noopener\";\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n","import type { Transport } from \"./transport\";\n\nexport type {\n ApiKeyKind,\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n CreatePersonalKeyRequest,\n UpdateApiKeyRequest\n} from \"@rebasepro/types\";\n\nimport type {\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n CreatePersonalKeyRequest,\n ScopeSummary,\n UpdateApiKeyRequest\n} from \"@rebasepro/types\";\n\n/** Options for the `createApiKeys` factory. */\nexport interface CreateApiKeysOptions {\n apiKeysPath?: string;\n}\n\n/**\n * Creates a client for the project's service keys, via the admin routes.\n * Needs `keys:read` / `keys:write`.\n *\n * @param transport - The shared HTTP transport created by `createTransport`.\n * @param options - Optional overrides (e.g. a custom base path).\n */\nexport function createApiKeys(transport: Transport, options?: CreateApiKeysOptions) {\n const apiKeysPath = options?.apiKeysPath || \"/admin/api-keys\";\n\n /** List all API keys (masked). */\n async function listKeys(): Promise<{ keys: ApiKeyMasked[] }> {\n return transport.request<{ keys: ApiKeyMasked[] }>(apiKeysPath, { method: \"GET\" });\n }\n\n /** Get a single API key by ID (masked). */\n async function getKey(id: string): Promise<{ key: ApiKeyMasked }> {\n return transport.request<{ key: ApiKeyMasked }>(\n apiKeysPath + \"/\" + encodeURIComponent(id),\n { method: \"GET\" }\n );\n }\n\n /** Create a new API key. The full secret is included in the response. */\n async function createKey(data: CreateApiKeyRequest): Promise<{ key: ApiKeyWithSecret }> {\n return transport.request<{ key: ApiKeyWithSecret }>(apiKeysPath, {\n method: \"POST\",\n body: JSON.stringify(data)\n });\n }\n\n /** Update an existing API key. */\n async function updateKey(id: string, data: UpdateApiKeyRequest): Promise<{ key: ApiKeyMasked }> {\n return transport.request<{ key: ApiKeyMasked }>(\n apiKeysPath + \"/\" + encodeURIComponent(id),\n {\n method: \"PUT\",\n body: JSON.stringify(data)\n }\n );\n }\n\n /** Revoke (soft-delete) an API key. */\n async function revokeKey(id: string): Promise<{ success: boolean }> {\n return transport.request<{ success: boolean }>(\n apiKeysPath + \"/\" + encodeURIComponent(id),\n { method: \"DELETE\" }\n );\n }\n\n return {\n listKeys,\n getKey,\n createKey,\n updateKey,\n revokeKey\n };\n}\n\n/** Options for the `createPersonalKeys` factory. */\nexport interface CreatePersonalKeysOptions {\n personalKeysPath?: string;\n scopesPath?: string;\n}\n\n/**\n * Creates a client for the signed-in account's own API keys — each acts as the\n * account and holds no scope it does not. Answers `PERSONAL_KEYS_DISABLED`\n * unless the backend sets `personalKeys: true` on the users collection.\n */\nexport function createPersonalKeys(transport: Transport, options?: CreatePersonalKeysOptions) {\n const keysPath = options?.personalKeysPath || \"/auth/keys\";\n const scopesPath = options?.scopesPath || \"/auth/scopes\";\n\n /** List this account's keys (masked). */\n async function listKeys(): Promise<{ keys: ApiKeyMasked[] }> {\n return transport.request<{ keys: ApiKeyMasked[] }>(keysPath, { method: \"GET\" });\n }\n\n /** Create a key for this account. The full secret is included in the response. */\n async function createKey(data: CreatePersonalKeyRequest): Promise<{ key: ApiKeyWithSecret }> {\n return transport.request<{ key: ApiKeyWithSecret }>(keysPath, {\n method: \"POST\",\n body: JSON.stringify(data)\n });\n }\n\n /** Revoke one of this account's keys. */\n async function revokeKey(id: string): Promise<{ success: boolean }> {\n return transport.request<{ success: boolean }>(\n keysPath + \"/\" + encodeURIComponent(id),\n { method: \"DELETE\" }\n );\n }\n\n /**\n * Every scope the backend knows, described, and the ones the caller holds —\n * what a screen offering scopes for a key needs.\n */\n async function listScopes(): Promise<{ scopes: ScopeSummary[]; held: string[] }> {\n return transport.request<{ scopes: ScopeSummary[]; held: string[] }>(scopesPath, { method: \"GET\" });\n }\n\n return {\n listKeys,\n createKey,\n revokeKey,\n listScopes\n };\n}\n","import type { Transport } from \"./transport\";\nimport { RebaseClientError } from \"@rebasepro/types\";\n\n/**\n * Client interface for invoking custom backend functions.\n *\n * Custom functions are Hono route files auto-mounted by the Rebase backend\n * at `/api/functions/{name}`. The `FunctionsClient` wraps the shared\n * transport so callers never need to manually construct URLs or inject\n * auth tokens.\n *\n * @example\n * ```ts\n * const result = await client.functions.invoke<{ job: Job }>('extract-job', {\n * url: 'https://example.com/posting',\n * html: htmlContent,\n * });\n * ```\n */\nexport interface FunctionsClient {\n /**\n * Invoke a custom backend function by name.\n *\n * **The method is `POST` unless you say otherwise.** A function whose only\n * route is `app.get(\"/\")` therefore answers 404 to a bare `invoke(name)` —\n * pass `{ method: \"GET\" }`, or write the route as `app.post`.\n *\n * @typeParam T - Expected shape of the response payload.\n * @param name - Function name (the filename without extension, e.g. `\"extract-job\"`).\n * One path segment: a sub-path goes in `options.path`.\n * @param payload - Optional JSON-serialisable body. Dropped for `GET`.\n * @param options - Optional overrides (HTTP method, sub-path, extra headers).\n * @returns The parsed JSON response from the function, or — with\n * `options.responseType` — its text, a `Blob`, or the raw `Response`.\n */\n invoke<T = unknown>(\n name: string,\n payload?: unknown,\n options?: FunctionInvokeOptions,\n ): Promise<T>;\n}\n\nexport type { FunctionInvokeOptions } from \"@rebasepro/types\";\nimport type { FunctionInvokeOptions } from \"@rebasepro/types\";\n\n/**\n * Create a `FunctionsClient` backed by the given transport.\n *\n * The transport already handles:\n * - Base URL resolution\n * - JWT injection via `Authorization: Bearer`\n * - 401 retry / `onUnauthorized` flow\n * - Consistent error throwing via `RebaseApiError`\n *\n * @internal\n */\n/** Whether a URL path segment is `.` or `..`, percent-encoded or not. */\nfunction isDotSegment(segment: string): boolean {\n return /^(?:\\.|%2e){1,2}$/i.test(segment);\n}\n\nexport function createFunctionsClient(transport: Transport): FunctionsClient {\n return {\n async invoke<T = unknown>(\n name: string,\n payload?: unknown,\n options?: FunctionInvokeOptions\n ): Promise<T> {\n // A function name is ONE path segment — the loader takes functions\n // from the top level of the directory only and refuses\n // subdirectories — so `encodeURIComponent` below is right, and a\n // name carrying a `/` is always a mistake at the call site.\n //\n // Refused rather than encoded, because encoding it is silent and\n // wrong in the worst way: `invoke(\"storage-provision/<id>\")` became\n // `POST /api/functions/storage-provision%2F<id>`, which matches no\n // route, so a route that exists answered a bare 404. That shipped,\n // and `rebase cloud storage create` read as an unimplemented feature\n // — including to the person who had implemented it. The sub-path\n // belongs in `options.path`.\n if (name.includes(\"/\")) {\n throw new RebaseClientError(\n `Invalid function name \"${name}\": a function name is a single path segment. ` +\n \"Pass anything after it as `options.path` — \" +\n `invoke(\"${name.split(\"/\")[0]}\", payload, { path: \"${name.split(\"/\").slice(1).join(\"/\")}\" }).`,\n { code: \"INVALID_FUNCTION_NAME\", details: { name } }\n );\n }\n\n // `.` and `..` are resolved by `fetch` before the request leaves:\n // `invoke(\"x\", undefined, { path: \"../../admin/users/<uid>\",\n // method: \"DELETE\" })` reached `/api/admin/users/<uid>`, a route\n // this method does not describe (and an agent tool built on it\n // offers). A function's own routes never need one, so a dot\n // segment — as the name, or anywhere in the path before its query\n // — is refused, spelled plainly or percent-encoded.\n const pathPart = (options?.path ?? \"\").split(/[?#]/)[0];\n if (isDotSegment(name) || pathPart.split(\"/\").some(isDotSegment)) {\n throw new RebaseClientError(\n `Invalid function route \"${name}${options?.path ? ` + ${options.path}` : \"\"}\": ` +\n \"a `.` or `..` segment would leave /functions/\" + name + \".\",\n { code: \"INVALID_FUNCTION_PATH\", details: { name, path: options?.path } }\n );\n }\n\n const method = options?.method ?? \"POST\";\n // A `path` that starts the query or fragment is appended as-is. Only a\n // real sub-path gets a separator: inserting one before `?days=30` asks\n // for `/functions/dashboard-stats/?days=30`, and the trailing slash\n // misses the route, so a function that exists answers 404 — and the\n // caller sees it as the backend being down rather than as a bad URL.\n const rawPath = options?.path;\n const subPath = rawPath\n ? (/^[?#]/.test(rawPath) ? rawPath : `/${rawPath.replace(/^\\//, \"\")}`)\n : \"\";\n const routePath = `/functions/${encodeURIComponent(name)}${subPath}`;\n\n const init: RequestInit = { method };\n\n if (payload !== undefined && method !== \"GET\") {\n init.body = JSON.stringify(payload);\n }\n\n if (options?.headers) {\n init.headers = options.headers;\n }\n\n // A function may answer text or a file; reading that as JSON\n // refused a body the function sent on purpose.\n const responseType = options?.responseType ?? \"json\";\n if (responseType !== \"json\") {\n if (!transport.requestRaw) {\n throw new RebaseClientError(\n `responseType \"${responseType}\" needs a transport that can hand back the raw response.`,\n { code: \"UNSUPPORTED_OPTION\", details: { responseType } }\n );\n }\n const res = await transport.requestRaw(routePath, init);\n // SAFETY: the caller names T for the mode it asked for — a\n // string, a Blob or the Response — as with the JSON default.\n if (responseType === \"response\") return res as unknown as T;\n if (responseType === \"blob\") return (await res.blob()) as unknown as T;\n return (await res.text()) as unknown as T;\n }\n\n // The function's body, verbatim: nothing here reaches in for a\n // `data` key. `client.call()` used to, which made the documented\n // \"shorthand for invoke\" return a different value for any function\n // that answered `{ data: … }`.\n return transport.request<T>(routePath, init);\n }\n };\n}\n","import { StorageSource, UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult, DownloadMetadata, PUBLIC_STORAGE_PREFIX, RebaseApiError, isPublicStoragePath, storageObjectPath } from \"@rebasepro/types\";\nimport {\n DOWNLOAD_URL_CONCURRENCY,\n RESUMABLE_UPLOAD_THRESHOLD,\n createBatchLoader,\n createConcurrencyLimit,\n createDownloadUrlCache,\n type DownloadMetadataBatchEntry,\n type ResolvedDownload,\n type ResumableUploadSend\n} from \"@rebasepro/common\";\nimport { defaultCodeForStatus, Transport, type ResponseMeta } from \"./transport\";\n\n/**\n * Create a StorageSource that talks to the Rebase backend REST API.\n *\n * @param transport - HTTP transport instance\n * @param storageId - Optional storage-source key for multi-backend routing.\n * When set, it is forwarded to the server so the correct\n * `StorageController` is resolved from the registry.\n */\n/**\n * A storage key, encoded for a URL path.\n *\n * Every call below interpolated the key raw, and the server decodes what it\n * receives — so three ordinary filenames did three different wrong things:\n *\n * `Invoice #12.pdf` the `#` began the fragment, the server saw\n * `default/Invoice ` and looked up a key that is not the\n * file — and a scoped `?token=` after it was swallowed too\n * `100% done.png` `decodeURIComponent` threw URIError → 500\n * `a%2Fb.png` decoded to `a/b.png`, silently resolving a DIFFERENT\n * object\n *\n * Per SEGMENT, because `/` is the key's own separator and must survive; every\n * other character is encoded, including a literal `%`. Verified to round-trip\n * through the server's decode for all of the above, plus `+`, accented\n * characters and a trailing `%`.\n */\nexport function encodeStorageKey(key: string): string {\n return key.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\n/**\n * The fields of an upload's form, as the resumable endpoint's\n * `Upload-Metadata`: the same names, plus the file's own name and type, which\n * the form carries in the file part. A Blob travels in a form as `blob`.\n */\nfunction resumableMetadataOf(form: FormData): Record<string, string> {\n const entries: Record<string, string> = {};\n for (const [name, value] of form.entries()) {\n if (typeof value === \"string\") {\n entries[name] = value;\n } else if (name === \"file\") {\n const filename = value instanceof File ? value.name : \"blob\";\n if (filename) entries.filename = filename;\n entries.filetype = value.type || \"application/octet-stream\";\n }\n }\n if (!entries.key && !entries.filename) entries.key = \"unnamed\";\n return entries;\n}\n\nexport function createStorage(transport: Transport, storageId?: string): StorageSource {\n // Keyed by `[bucket/]keyOrUrl`; an entry lives until its download token\n // expires (public URLs: until evicted), is dropped by `deleteObject`, and\n // is bounded LRU. One per source instance, i.e. per client, and owned by\n // the credential its URLs were minted with (see `getSignedUrl`), so a\n // sign-out or another user's sign-in empties it. Concurrent lookups of one\n // key share one `/metadata` request, and at most\n // DOWNLOAD_URL_CONCURRENCY of those are open at once.\n const urlsCache = createDownloadUrlCache();\n const limitMetadata = createConcurrencyLimit(DOWNLOAD_URL_CONCURRENCY);\n\n /**\n * A private file's metadata and download token. The lookups a page starts\n * together — a table of thumbnails — go as one `POST /storage/metadata`\n * per hundred files, which the server charges a tenth of the requests it\n * replaces; one asked for alone is the `GET` it always was. A server\n * without the batch route (404) is asked one file at a time from then on.\n */\n const describe = createBatchLoader<DownloadMetadata>({\n loadOne: async (filePath) => (await limitMetadata(() =>\n transport.request<{ data: DownloadMetadata }>(withStorageId(`/storage/metadata/${encodeStorageKey(filePath)}`)))).data,\n loadMany: async (filePaths) => {\n const result = await limitMetadata(() => transport.request<{ data: Record<string, DownloadMetadataBatchEntry> }>(\"/storage/metadata\", {\n method: \"POST\",\n body: JSON.stringify({ keys: filePaths, ...(storageId ? { storageId } : {}) })\n }));\n const answers = new Map<string, DownloadMetadata | Error>();\n for (const [filePath, entry] of Object.entries(result.data ?? {})) {\n answers.set(filePath, \"error\" in entry\n ? new RebaseApiError(entry.error.message, { status: entry.error.status, code: entry.error.code })\n : entry);\n }\n return answers;\n },\n isUnsupported: (error) => error instanceof Error && \"status\" in error && (error.status === 404 || error.status === 405)\n });\n\n /**\n * Base for URLs the *browser* will fetch on its own (file downloads,\n * previews). API requests keep going to `baseUrl`; see\n * {@link RebaseClientConfig.storageUrlOrigin} for why these can differ.\n */\n const fileUrlBase = (): string =>\n `${transport.storageUrlOrigin ?? transport.baseUrl}${transport.apiPath}`;\n\n /** Append ?storageId=... to a path when multi-backend routing is active. */\n const withStorageId = (path: string): string => {\n if (!storageId) return path;\n const sep = path.includes(\"?\") ? \"&\" : \"?\";\n return `${path}${sep}storageId=${encodeURIComponent(storageId)}`;\n };\n\n /**\n * Whether this source sends large files through the resumable endpoint.\n * Turned off for the rest of its life by the first upload that finds it\n * cannot — a server without the endpoint, or several instances that do\n * not share an upload — so only that one upload pays for finding out.\n */\n let resumable = true;\n\n /** One request of a resumable upload, through the transport (its token, its refresh). */\n const sendResumable: ResumableUploadSend = async (method, path, headers, body) => {\n const meta: ResponseMeta = {};\n try {\n await transport.request(`/storage/tus${path}`, { method, headers, ...(body ? { body } : {}) }, meta);\n return { status: meta.status ?? 200, header: (name) => meta.headers?.get(name) ?? null };\n } catch (error: unknown) {\n // An HTTP answer is the protocol's to read; a dropped connection\n // (status 0) is thrown, and the upload asks where it got to.\n if (error instanceof RebaseApiError && typeof error.status === \"number\" && error.status > 0) {\n return { status: error.status, header: () => null, error };\n }\n throw error;\n }\n };\n\n async function putObject({\n file,\n key,\n metadata,\n bucket,\n public: isPublic,\n context\n }: UploadFileProps): Promise<UploadFileResult> {\n const formData = new FormData();\n formData.append(\"file\", file);\n\n // Which property this file is for, so the server can enforce that\n // property's own `maxSize` and `acceptedFiles` — declared in the\n // collection, rendered by the panel's picker, and previously checked by\n // the browser alone. Two plain fields rather than a header: the request\n // is already multipart, and a header would have to survive every proxy\n // between here and the route.\n if (context?.collection && context?.property) {\n formData.append(\"collection\", context.collection);\n formData.append(\"property\", context.property);\n }\n\n // Public objects live under the public prefix so they can be served\n // token-less via a stable, permanent URL. Normalize the key here so the\n // stored path is self-describing (no server round-trip needed to know\n // it's public).\n //\n // With no key, the server names the object after the uploaded file —\n // at the root, which is not the public prefix. So a public upload with\n // no key is given that same name here, prefixed: the key is optional,\n // and `public: true` promised a token-less URL either way. A Blob\n // travels in the form as a file called \"blob\".\n let effectiveKey = key;\n if (isPublic) {\n effectiveKey ||= (file instanceof File && file.name) ? file.name : \"blob\";\n if (!isPublicStoragePath(effectiveKey)) {\n effectiveKey = `${PUBLIC_STORAGE_PREFIX}${effectiveKey.replace(/^\\/+/, \"\")}`;\n }\n }\n\n if (effectiveKey) formData.append(\"key\", effectiveKey);\n if (bucket) formData.append(\"bucket\", bucket);\n if (storageId) formData.append(\"storageId\", storageId);\n\n if (metadata) {\n for (const [key, value] of Object.entries(metadata)) {\n if (value !== undefined && value !== null) {\n formData.append(\n `metadata_${key}`,\n typeof value === \"string\" ? value : JSON.stringify(value)\n );\n }\n }\n }\n\n // A large file goes up a chunk at a time: the multipart request is\n // parsed whole by the server, three to five times the file in its\n // memory. Same fields, so the same object, the same checks and the\n // same triggers — only the transport differs.\n if (resumable && file.size > RESUMABLE_UPLOAD_THRESHOLD) {\n // Fetched here, the first time a file is this large: a client\n // that never uploads one never downloads the code that does it.\n const { uploadResumable } = await import(\"./storage-resumable\");\n const outcome = await uploadResumable({ file, metadata: resumableMetadataOf(formData), send: sendResumable });\n if (\"stored\" in outcome) return outcome.stored;\n resumable = false;\n }\n\n const result = await transport.request<{ data: UploadFileResult }>(withStorageId(\"/storage/upload\"), {\n method: \"POST\",\n body: formData,\n headers: {}\n });\n\n return result.data;\n }\n\n async function getSignedUrl(\n keyOrUrl: string,\n bucket?: string\n ): Promise<DownloadConfig> {\n const cacheKey = bucket ? `${bucket}/${keyOrUrl}` : keyOrUrl;\n // A private file's URL carries a download token the server minted for\n // whoever asked, and this source outlives them: after a sign-out, or\n // another user signing in in the same tab, the next preview of the\n // path was handed the previous user's URL. The credential the request\n // goes out with is the owner — the access token, in cookie mode too\n // (only the refresh token is a cookie) — so a different one empties\n // the cache first. A token refresh empties it as well, which costs one\n // request per file shown afterwards, once per access-token lifetime.\n urlsCache.claim((await transport.resolveToken()) ?? \"\");\n return urlsCache.get(cacheKey, () => resolveDownload(keyOrUrl, bucket));\n }\n\n async function resolveDownload(\n keyOrUrl: string,\n bucket?: string\n ): Promise<ResolvedDownload> {\n const filePath = storageObjectPath(keyOrUrl, bucket);\n\n if (!filePath || filePath.trim() === \"\" || filePath === \"/\") {\n return { config: { url: null, fileNotFound: true }, noStore: true };\n }\n\n // ── Public objects ────────────────────────────────────────────────\n // A public file (under the public prefix) is served token-less via a\n // stable, permanent, CDN-cacheable URL. No metadata round-trip and no\n // token are needed — build the URL directly and cache it forever.\n if (isPublicStoragePath(filePath)) {\n return {\n config: { url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}`) }\n // no expiry\n };\n }\n\n try {\n const metadata = await describe(filePath);\n\n // Public object (server-confirmed): token-less permanent URL.\n if (metadata.public) {\n return {\n config: {\n url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}`),\n metadata\n }\n // no expiry\n };\n }\n\n // Private object: use the short-lived, file-scoped download token\n // minted by the server. We deliberately do NOT fall back to the\n // caller's access token — a URL must never carry a full-privilege\n // credential. If no scoped token is present the URL fails closed.\n const scopedToken = metadata.token;\n const tokenQuery = scopedToken ? `?token=${scopedToken}` : \"\";\n\n const downloadConfig: DownloadConfig = {\n // `withStorageId` picks `?` or `&` based on whether the token\n // query is already present, so the URL stays valid even when\n // there is no token.\n url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}${tokenQuery}`),\n metadata\n };\n\n const expiresAt = metadata.tokenExpiresIn\n ? Date.now() + (metadata.tokenExpiresIn - 10) * 1000 // subtract 10s buffer\n : undefined;\n\n return { config: downloadConfig, expiresAt };\n } catch (e: unknown) {\n if (e instanceof Error && \"status\" in e && (e as { status: number }).status === 404) {\n return { config: { url: null, fileNotFound: true }, noStore: true };\n }\n throw e;\n }\n }\n\n async function getObject(\n key: string,\n bucket?: string\n ): Promise<File | null> {\n const downloadConfig = await getSignedUrl(key, bucket);\n if (downloadConfig.fileNotFound || !downloadConfig.url) {\n return null;\n }\n\n // Fetch using the signed URL directly. Since the scoped token is in the ?token= query param,\n // we explicitly omit any Authorization headers to prevent passing full access tokens to file serving routes.\n const response = await transport.fetchFn(downloadConfig.url, {\n headers: {}\n });\n\n if (response.status === 404) return null;\n // A RebaseApiError with the status, as every other failure here is;\n // a plain Error dropped the status, so a 403 and a 500 read the same.\n if (!response.ok) {\n throw new RebaseApiError(`Failed to get file ${key}: the server answered ${response.status}.`, {\n status: response.status,\n code: defaultCodeForStatus(response.status)\n });\n }\n\n const blob = await response.blob();\n const fileName = (bucket ? `${bucket}/${key}` : key).split(\"/\").pop() || \"file\";\n return new File([blob], fileName, { type: blob.type });\n }\n\n async function deleteObject(\n key: string,\n bucket?: string\n ): Promise<void> {\n const filePath = storageObjectPath(key, bucket);\n\n if (!filePath || filePath.trim() === \"\" || filePath === \"/\") {\n return;\n }\n\n try {\n await transport.request(withStorageId(`/storage/file/${encodeStorageKey(filePath)}`), { method: \"DELETE\" });\n } catch (e: unknown) {\n if (!(e instanceof Error && \"status\" in e && (e as { status: number }).status === 404)) throw e;\n }\n\n urlsCache.delete(bucket ? `${bucket}/${key}` : key);\n }\n\n async function listObjects(\n prefix: string,\n options?: {\n bucket?: string;\n maxResults?: number;\n pageToken?: string;\n }\n ): Promise<StorageListResult> {\n const params = new URLSearchParams();\n if (prefix) params.set(\"prefix\", prefix);\n if (options?.bucket) params.set(\"bucket\", options.bucket);\n if (options?.maxResults) params.set(\"maxResults\", String(options.maxResults));\n if (options?.pageToken) params.set(\"pageToken\", options.pageToken);\n\n if (storageId) params.set(\"storageId\", storageId);\n\n const result = await transport.request<{ data: StorageListResult }>(`/storage/list?${params.toString()}`);\n return result.data;\n }\n\n return {\n putObject,\n getSignedUrl,\n getObject,\n deleteObject,\n listObjects\n };\n}\n","/**\n * Client-side storage source registry.\n *\n * Manages multiple `StorageSource` instances keyed by\n * `StorageSourceDefinition.key`. Collection properties reference\n * a source by key via `StorageConfig.storageSource`.\n *\n * Typical bootstrap flow:\n * 1. Fetch definitions from `GET /api/storage/sources`\n * 2. Build server-backed sources automatically via `createStorage(transport, key)`\n * 3. Register \"direct\" sources manually (e.g. Firebase Storage hook)\n */\n\nimport type { StorageSource, StorageSourceRegistry, StorageSourceDefinition } from \"@rebasepro/types\";\nimport { DEFAULT_STORAGE_SOURCE_KEY } from \"@rebasepro/types\";\nimport { createStorage } from \"./storage\";\nimport type { Transport } from \"./transport\";\n\n/**\n * Default implementation of the client-side `StorageSourceRegistry`.\n */\nexport class ClientStorageSourceRegistry implements StorageSourceRegistry {\n private sources = new Map<string, StorageSource>();\n\n /**\n * Register a storage source.\n * @param key - Unique key matching a `StorageSourceDefinition.key`\n * @param source - The `StorageSource` instance\n */\n register(key: string, source: StorageSource): void {\n this.sources.set(key, source);\n }\n\n getDefault(): StorageSource {\n const source = this.sources.get(DEFAULT_STORAGE_SOURCE_KEY);\n if (!source) {\n throw new Error(\n `[StorageSourceRegistry] No default storage source registered. ` +\n `Register one with key \"${DEFAULT_STORAGE_SOURCE_KEY}\".`\n );\n }\n return source;\n }\n\n get(key: string | undefined | null): StorageSource | undefined {\n if (key === undefined || key === null) {\n return this.sources.get(DEFAULT_STORAGE_SOURCE_KEY);\n }\n return this.sources.get(key);\n }\n\n getOrDefault(key: string | undefined | null): StorageSource {\n if (key === undefined || key === null) {\n return this.getDefault();\n }\n const source = this.sources.get(key);\n if (source) return source;\n\n // Not the default source: a key that names another is another bucket,\n // and answering the default wrote that source's files into the wrong\n // one — the fallback the server registry removed. The default only for\n // a call that names no source.\n throw new Error(\n `[StorageSourceRegistry] Storage source \"${key}\" is not registered. ` +\n `Registered: ${this.list().map(k => `\"${k}\"`).join(\", \") || \"(none)\"}.`\n );\n }\n\n has(key: string): boolean {\n return this.sources.has(key);\n }\n\n list(): string[] {\n return Array.from(this.sources.keys());\n }\n\n /**\n * Build a registry from `StorageSourceDefinition[]` and an HTTP transport.\n *\n * - Sources with `transport: \"server\"` are auto-wired via `createStorage(transport, key)`.\n * - Sources with `transport: \"direct\"` are **not** auto-wired — they must\n * be registered manually after this call (e.g. via a Firebase hook).\n *\n * @param definitions - Array of storage source definitions\n * @param transport - HTTP transport for server-backed sources\n */\n static fromDefinitions(\n definitions: StorageSourceDefinition[],\n transport: Transport\n ): ClientStorageSourceRegistry {\n const registry = new ClientStorageSourceRegistry();\n\n for (const def of definitions) {\n if (def.transport === \"server\") {\n // Auto-create a server-backed StorageSource for this key\n const source = createStorage(transport, def.key === DEFAULT_STORAGE_SOURCE_KEY ? undefined : def.key);\n registry.register(def.key, source);\n }\n // \"direct\" sources must be registered manually\n }\n\n return registry;\n }\n}\n","import type { SqlScriptColumn, SqlScriptNotice, SqlScriptResult, SqlScriptTable } from \"@rebasepro/types\";\n\n/**\n * Read an `EXECUTE_SQL_SUCCESS` payload for a script run as a\n * {@link SqlScriptResult}, checking every field rather than trusting the frame.\n *\n * A server from before scripts existed answers `{ result }` alone, with parsed\n * values. Its rows are kept, as text, and no column is said to come from\n * anywhere — the console edits no cell of a result it cannot trace.\n */\nexport function readSqlScriptResult(payload: unknown): SqlScriptResult {\n const record = asRecord(payload);\n const rows = Array.isArray(record?.result) ? record.result.map(asRecord).filter(isDefined).map(textRow) : [];\n const columns = Array.isArray(record?.columns)\n ? record.columns.map(readColumn).filter(isDefined)\n : Object.keys(rows[0] ?? {}).map((name): SqlScriptColumn => ({ name }));\n const tables = Array.isArray(record?.tables) ? record.tables.map(readTable).filter(isDefined) : [];\n const notices = Array.isArray(record?.notices) ? record.notices.map(readNotice).filter(isDefined) : [];\n const result: SqlScriptResult = { rows, columns, tables, notices };\n if (typeof record?.command === \"string\") result.command = record.command;\n if (typeof record?.rowCount === \"number\") result.rowCount = record.rowCount;\n if (record?.truncated === true) result.truncated = true;\n if (typeof record?.effectiveRole === \"string\") result.effectiveRole = record.effectiveRole;\n return result;\n}\n\nfunction isDefined<T>(value: T | undefined): value is T {\n return value !== undefined;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? Object.fromEntries(Object.entries(value))\n : undefined;\n}\n\nfunction textRow(row: Record<string, unknown>): Record<string, string | null> {\n return Object.fromEntries(Object.entries(row).map(([key, value]) => [key, asText(value)]));\n}\n\nfunction asText(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"string\") return value;\n if (typeof value === \"object\") return JSON.stringify(value);\n return String(value);\n}\n\nfunction readColumn(value: unknown): SqlScriptColumn | undefined {\n const record = asRecord(value);\n if (typeof record?.name !== \"string\") return undefined;\n const column: SqlScriptColumn = { name: record.name };\n if (typeof record.type === \"string\") column.type = record.type;\n const source = asRecord(record.source);\n if (typeof source?.schema === \"string\" && typeof source.table === \"string\" && typeof source.column === \"string\") {\n column.source = { schema: source.schema, table: source.table, column: source.column };\n }\n return column;\n}\n\nconst TABLE_KINDS: readonly SqlScriptTable[\"kind\"][] = [\"table\", \"partitioned table\", \"view\", \"materialized view\", \"foreign table\", \"other\"];\n\nfunction readTable(value: unknown): SqlScriptTable | undefined {\n const record = asRecord(value);\n if (typeof record?.schema !== \"string\" || typeof record.table !== \"string\") return undefined;\n const kind = TABLE_KINDS.find(candidate => candidate === record.kind) ?? \"other\";\n const primaryKey = Array.isArray(record.primaryKey) && record.primaryKey.every((column): column is string => typeof column === \"string\")\n ? record.primaryKey\n : [];\n return {\n schema: record.schema,\n table: record.table,\n kind,\n primaryKey,\n // Unknown reads as the unsafe answer: a table whose rows may live in\n // tables that inherit it is not one a key finds a single row in.\n hasInheritors: record.hasInheritors !== false\n };\n}\n\nfunction readNotice(value: unknown): SqlScriptNotice | undefined {\n const record = asRecord(value);\n if (typeof record?.message !== \"string\") return undefined;\n return { severity: typeof record.severity === \"string\" ? record.severity : \"NOTICE\", message: record.message };\n}\n","import {\n DeleteProps,\n CollectionConfig,\n FetchCollectionProps,\n ListenCollectionProps,\n FetchOneProps,\n ListenOneTarget,\n SaveProps,\n WebSocketMessage,\n WebSocketErrorPayload,\n CollectionUpdateMessage,\n CollectionUpdateMeta,\n SingleUpdateMessage,\n ChannelMessage,\n TableMetadata,\n BranchInfo,\n RebaseApiError,\n type RealtimeConnectionState\n} from \"@rebasepro/types\";\nimport { buildCompositeId, COMPOSITE_ID_SEPARATOR, warnDeprecated, withETag, type PrimaryKeyInfo } from \"@rebasepro/common\";\nimport { parseRebaseJson } from \"./reviver\";\nimport { unref } from \"@rebasepro/utils\";\nimport { readSqlScriptResult } from \"./sql-script\";\nimport type { SqlScriptResult } from \"@rebasepro/types\";\n\n\n\n/**\n * Extract error message and code from a WebSocket message payload.\n * Handles both `{ error: string }` and `{ error: { message, code } }` shapes.\n */\nfunction extractMessageError(message: WebSocketMessage): { errorMessage: string; errorCode?: string } {\n const payload = message.payload as WebSocketErrorPayload | undefined;\n const errPayload = payload?.error;\n const errorMessage = typeof errPayload === \"object\"\n ? errPayload.message\n : payload?.message || (typeof errPayload === \"string\" ? errPayload : undefined) || message.error || \"Unknown error\";\n const errorCode = typeof errPayload === \"object\"\n ? errPayload.code\n : payload?.code;\n // Callers treat this as a string (`.toLowerCase()` in isAuthError). A frame\n // carrying a non-string here would throw inside the message handler, where\n // the surrounding try/catch would swallow it — and a subscription error that\n // never reaches its listener is a view stuck loading forever.\n const safeMessage = typeof errorMessage === \"string\"\n ? errorMessage\n : (errorMessage == null ? \"Unknown error\" : JSON.stringify(errorMessage));\n return { errorMessage: safeMessage,\nerrorCode };\n}\n\n/**\n * The error an `ERROR` frame describes, with the server's `details` when it\n * sent them — `{ stage, path }` for a collection-callback refusal, the object\n * the REST envelope carries for the same veto. `status` stays `undefined`:\n * a frame is not an HTTP response (see `RebaseErrorInit.status`).\n */\nfunction frameError(message: WebSocketMessage): RebaseApiError {\n const { errorMessage, errorCode } = extractMessageError(message);\n const errPayload = (message.payload as WebSocketErrorPayload | undefined)?.error;\n const details = typeof errPayload === \"object\" && errPayload !== null ? errPayload.details : undefined;\n return new RebaseApiError(errorMessage, { code: errorCode, details });\n}\n\n/**\n * A request whose socket closed after it was written and before the server\n * answered. The server may have run it. It is not re-sent — see `onclose`.\n */\nfunction connectionLostError(): RebaseApiError {\n return new RebaseApiError(\n \"The realtime connection closed before the server answered. The request may or may not have been applied, and it was not sent again.\",\n { code: \"CONNECTION_LOST\" }\n );\n}\n\n/**\n * A request that got no answer within `requestTimeoutMs` of the call — whether\n * it waited in the queue for a socket the whole time, or was written and never\n * answered (a half-open connection looks exactly like that).\n */\nfunction requestTimeoutError(written: boolean): RebaseApiError {\n return new RebaseApiError(\n written\n ? \"The server did not answer the realtime request in time. It may or may not have been applied.\"\n : \"The realtime request timed out before it could be sent. It was not applied.\",\n { code: \"REQUEST_TIMEOUT\" }\n );\n}\n\n/**\n * What a live subscription or a joined channel is told, once, when the\n * connection has been down for longer than a blip. The registration is kept and\n * resumes when the socket is back.\n */\nfunction realtimeLostError(): RebaseApiError {\n return new RebaseApiError(\n \"The realtime connection is down, so live data is not updating. The client keeps reconnecting and refreshes this subscription when the connection is back.\",\n { code: \"CONNECTION_LOST\" }\n );\n}\n\n/**\n * Whether realtime debugging is on: `globalThis.__REBASE_DEBUG_REALTIME__ = true`,\n * set from a console or a test. While it is, each refetched row that differs\n * from the one held is explained key by key with `console.debug`.\n */\nfunction realtimeDebugOn(): boolean {\n return Reflect.get(globalThis, \"__REBASE_DEBUG_REALTIME__\") === true;\n}\n\n/**\n * Connection chatter: the socket opened, signed in, closed, is redialling.\n *\n * `console.debug` where that is a debug level — a browser's console hides it\n * unless Verbose is asked for. Not in Node, where `console.debug` is\n * `console.log`: a script's `listen()` opened its stdout with \"Connected to\n * PostgreSQL backend\" and \"WebSocket auto-authenticated\", ahead of what the\n * script prints. There it is written only while realtime debugging is on.\n */\nfunction connectionDebug(...args: unknown[]): void {\n const browser = typeof window !== \"undefined\" || typeof document !== \"undefined\";\n if (!browser && !realtimeDebugOn()) return;\n console.debug(...args);\n}\n\n/**\n * The codes an `AUTHENTICATE` is refused with when the server will not honour\n * the session: the token does not verify or has expired, the session was\n * revoked or its account is gone (`SESSION_REVOKED`), the account is disabled\n * (`ACCOUNT_DISABLED`) — the codes the HTTP routes answer the same token with.\n * An answer, not a failure: asking again with the same token gets the same one.\n */\nconst SESSION_REFUSAL_CODES = new Set([\"INVALID_TOKEN\", \"TOKEN_EXPIRED\", \"JWT_EXPIRED\", \"SESSION_REVOKED\", \"ACCOUNT_DISABLED\", \"SESSION_ENDED\"]);\n\nfunction isSessionRefusal(error: unknown): error is RebaseApiError {\n return error instanceof RebaseApiError && typeof error.code === \"string\" && SESSION_REFUSAL_CODES.has(error.code);\n}\n\n/** The ceiling of the reconnect backoff. */\nconst MAX_RECONNECT_DELAY_MS = 30_000;\n\n/**\n * The shortest reconnect delay. Each delay is drawn from the whole of its step\n * (full jitter), so without a floor a redial could land the instant the socket\n * dropped — before a restarting server has any chance of being back.\n */\nconst MIN_RECONNECT_DELAY_MS = 250;\n\n/**\n * How long the connection is down before the outage is reported: every\n * registration is told `CONNECTION_LOST` and {@link RebaseWebSocketClient.state}\n * reads `disconnected`. The first three steps of the backoff, 2 + 4 + 8 s.\n *\n * Measured from the drop rather than counted in redials: with full jitter,\n * three redials can all fail within the first second, and a server restart\n * that short is a blip, not an outage.\n */\nconst OUTAGE_REPORT_AFTER_MS = 14_000;\n\n/**\n * What this client opts into when it opens the socket, named on the URL as\n * `?rebase_caps=` — the server's `socket-opt-ins.ts` says what each one does.\n * A server that knows them answers with a `hello` listing the ones it will\n * honour; an older one ignores the parameter, sends no `hello`, and the\n * client goes on as it did before there were any.\n */\nconst SOCKET_OPT_IN_PARAM = \"rebase_caps\";\nconst SOCKET_OPT_INS = [\"heartbeat\", \"channel_seq\", \"cancel_sql\"];\n\n/** Slack on top of two heartbeat intervals before a silent socket is given up on. */\nconst HEARTBEAT_GRACE_MS = 5_000;\n\n/**\n * `url` with the opt-ins named on it. Appended rather than rebuilt through\n * `URL`, which would rewrite the address the caller gave (a bare origin gains\n * a `/`).\n */\nfunction withOptIns(url: string): string {\n return `${url}${url.includes(\"?\") ? \"&\" : \"?\"}${SOCKET_OPT_IN_PARAM}=${SOCKET_OPT_INS.join(\",\")}`;\n}\n\n/** A frame waiting in `messageQueue`, carrying the handlers of the promise its caller holds. */\ntype QueuedFrame = Record<string, unknown> & {\n _queuedResolve?: (value: unknown) => void;\n _queuedReject?: (error: Error) => void;\n};\n\nexport interface RebaseWebSocketConfig {\n websocketUrl: string;\n /**\n * Where the token the socket authenticates with comes from, asked each\n * time it authenticates. A falsy answer leaves it anonymous.\n */\n getAuthToken?: () => Promise<string | null>;\n /** Optional WebSocket constructor to override globalThis.WebSocket (e.g. for Node environments) */\n WebSocket?: typeof WebSocket;\n /** Callback to handle unauthorized requests or token expiration (refreshes auth session) */\n onUnauthorized?: () => Promise<boolean>;\n /**\n * Sign in as this user rather than as the token's own — the socket half of\n * `RebaseClientConfig.impersonate`. Sent with every `AUTHENTICATE`.\n */\n impersonate?: string;\n}\n\n\n/**\n * Broadcast and presence frames.\n *\n * Fire-and-forget (the server sends no response envelope), and exempt from the\n * client-side auth gate — a public channel is usable without an account.\n */\nconst CHANNEL_MESSAGE_TYPES = new Set([\n \"join_channel\",\n \"leave_channel\",\n \"broadcast\",\n \"presence_track\",\n \"presence_untrack\",\n \"presence_state\",\n // The catch-up request. Like `presence_state`, its answer comes back as a\n // channel-addressed frame rather than a response envelope, so it must not\n // be given a pending request to wait on.\n \"channel_history\"\n]);\n\n/**\n * Subscription frames. Like channel frames they get no response envelope, and\n * like them they go out without an account when there is none: whether an\n * anonymous visitor may subscribe is the server's `requireAuth` to decide, and\n * a server that refuses answers the frame, which reaches the listener's\n * `onError`.\n */\nconst SUBSCRIPTION_MESSAGE_TYPES = new Set([\n \"subscribe_collection\",\n \"subscribe_one\",\n \"unsubscribe\"\n]);\n\n/**\n * The token getter's \"there is no account\" — an empty token, or its own\n * \"not logged in\". Distinct from a getter that failed, which says nothing about\n * whether there is one.\n */\nfunction isNotSignedIn(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return message.includes(\"not logged in\");\n}\n\n/**\n * The other direction: the frame types the *server* addresses by channel.\n *\n * Not the mirror of {@link CHANNEL_MESSAGE_TYPES} — a request and its answer\n * have different names (`presence_state` is the one type in both) — so the two\n * sets are listed separately rather than derived from each other. These are\n * exactly the members of `ChannelMessage`; adding one here without declaring it\n * there leaves the handler with a frame it cannot read.\n */\nconst CHANNEL_FRAME_TYPES = new Set([\n \"broadcast\",\n // The number the sender's own retained broadcast took (`channel_seq`).\n \"broadcast_ack\",\n \"presence_state\",\n \"presence_diff\",\n \"channel_history\",\n // A refusal about a channel. Fire-and-forget frames have no pending request\n // to reject, so this is the only way one becomes visible to the caller.\n \"error\",\n \"ERROR\"\n]);\n\n/**\n * Low-level realtime WebSocket client.\n *\n * @internal Not a stable app-facing API. `createRebaseClient()` constructs and\n * manages this internally (exposed as `client.ws`, typed by the minimal\n * `RebaseWebSocket` contract in `@rebasepro/types`). It stays exported from the\n * package root for the same reason it always was — a data-source driver may\n * instantiate it directly — but nothing in this repo does since\n * `@rebasepro/client-postgres` was removed; its surface may change without a\n * major bump.\n */\n/**\n * Narrow a frame to the collection-update shape. See its use below.\n *\n * `WebSocketMessage.type` is `string`, so the message types that extend it do\n * not form a discriminated union and `type === \"collection_update\"` narrows\n * nothing on its own.\n */\nfunction isCollectionUpdate(message: WebSocketMessage): message is CollectionUpdateMessage {\n return message.type === \"collection_update\";\n}\n\n/**\n * Narrow a frame to one of the channel-addressed shapes.\n *\n * Both halves of the test are load-bearing. The type says which member of\n * {@link ChannelMessage} it is; `channel` is what a channel frame is *addressed\n * by*, and an `ERROR` from a server older than the change that names the\n * channel has none — it is a generic error, and falls through to the catch-all\n * warning rather than being delivered to a channel that was never named.\n */\nfunction isChannelMessage(message: WebSocketMessage): message is ChannelMessage {\n return typeof message.channel === \"string\" && CHANNEL_FRAME_TYPES.has(message.type);\n}\n\nexport class RebaseWebSocketClient {\n private websocketUrl: string;\n private ws: WebSocket | null = null;\n /**\n * Where the socket's token comes from. A client built by\n * `createRebaseClient()` resolves it through the HTTP transport, so\n * `client.setAuthTokenGetter()` and `client.setToken()` reach both.\n */\n public getAuthToken?: () => Promise<string | null>;\n private listeners = new Map<string, Set<(...args: unknown[]) => void>>();\n\n /** Channel-name → handlers, for broadcast and presence frames. */\n private channelHandlers = new Map<string, Set<(message: ChannelMessage) => void>>();\n\n /** Set by `close()`. Blocks any later operation from silently redialling. */\n private closedByCaller = false;\n\n /**\n * Set when the backoff budget ran out with nothing registered, cleared by\n * anything that earns a fresh one.\n *\n * Only reachable with nothing registered: while a subscription or a joined\n * channel exists the client never stops retrying (see `attemptReconnect`).\n * And it is not final like {@link closedByCaller} — nobody *asked* for the\n * socket to stay down, so the next subscribe dials again.\n */\n private gaveUp = false;\n\n /** See {@link state}. */\n private connectionState: RealtimeConnectionState = \"idle\";\n private stateListeners = new Set<(state: RealtimeConnectionState) => void>();\n\n /**\n * Whether the current outage has been reported to the registrations as\n * `CONNECTION_LOST`. Once per outage: cleared when a socket opens.\n */\n private outageReported = false;\n\n /**\n * Where the connection is.\n *\n * - `idle` — no socket, and none wanted yet (the connection is lazy), or\n * the last one was dropped by a sign-out.\n * - `connecting` — dialling, with no outage in progress.\n * - `connected` — the socket is open.\n * - `reconnecting` — the socket dropped and the client is redialling. A\n * blip, so far: nothing has been reported.\n * - `disconnected` — the outage has outlasted the blip budget. Every\n * subscription and joined channel has been told `CONNECTION_LOST` once;\n * the client keeps redialling (every 30 s at most) while any exists.\n * - `closed` — `client.close()`. Final.\n */\n public get state(): RealtimeConnectionState {\n return this.connectionState;\n }\n\n /** Called on every change of {@link state}. Returns the unsubscribe. */\n public onStateChange(listener: (state: RealtimeConnectionState) => void): () => void {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n private setState(next: RealtimeConnectionState): void {\n if (next === this.connectionState) return;\n this.connectionState = next;\n for (const listener of [...this.stateListeners]) {\n try {\n listener(next);\n } catch (error) {\n console.error(\"Error in realtime state listener:\", error);\n }\n }\n }\n\n /**\n * Whether a socket exists at all (open or still opening).\n *\n * Lets callers distinguish \"authenticate the live socket\" from \"there is\n * nothing to authenticate yet\", without that question forcing a dial.\n */\n public get hasSocket(): boolean {\n return this.ws !== null;\n }\n\n /** So the \"no WebSocket in this environment\" warning is said once, not per call. */\n private warnedNoWebSocket = false;\n\n /**\n * What the server said in its `hello` it does for this socket: empty\n * until then, after a close, and for a server that sends no `hello`.\n */\n private serverOptIns: ReadonlySet<string> = new Set();\n /** This socket's id on the server, from its `hello` (with `channel_seq`). */\n private serverClientId: string | undefined;\n /** When the current socket last delivered anything, heartbeats included. */\n private lastFrameAt = 0;\n private livenessTimer: ReturnType<typeof setInterval> | null = null;\n /** Give up on the current socket as though it had closed. Set per socket. */\n private dropSocket: (() => void) | null = null;\n\n /** Whether the server agreed, in its `hello`, to `name` for this socket. */\n public hasServerOptIn(name: string): boolean {\n return this.serverOptIns.has(name);\n }\n\n /**\n * This socket's id on the server, as its broadcasts' `senderId` in a\n * channel's history. Known once a server that numbers retained channels\n * for it (`channel_seq`) has said hello.\n */\n public get socketClientId(): string | undefined {\n return this.serverClientId;\n }\n\n /** Subscribe to broadcast/presence frames for one channel. */\n public onChannelMessage(channel: string, handler: (message: ChannelMessage) => void): () => void {\n if (!this.channelHandlers.has(channel)) this.channelHandlers.set(channel, new Set());\n this.channelHandlers.get(channel)!.add(handler);\n return () => {\n const handlers = this.channelHandlers.get(channel);\n if (!handlers) return;\n handlers.delete(handler);\n if (handlers.size === 0) this.channelHandlers.delete(channel);\n };\n }\n\n /** Notified after the socket comes back, so channels can re-join. */\n public onReconnect(handler: () => void): () => void {\n return this.on(\"reconnect\", handler);\n }\n\n /**\n * Notified once per outage that outlasted a blip, with the\n * `CONNECTION_LOST` error — so a joined channel can tell its `onError`\n * handlers, as subscriptions are told through theirs.\n */\n public onConnectionLost(handler: (error: RebaseApiError) => void): () => void {\n this.connectionLostHandlers.add(handler);\n return () => {\n this.connectionLostHandlers.delete(handler);\n };\n }\n\n private connectionLostHandlers = new Set<(error: RebaseApiError) => void>();\n\n public on(event: \"connect\" | \"disconnect\" | \"reconnect\" | \"error\", cb: (...args: unknown[]) => void) {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(cb);\n return () => this.listeners.get(event)!.delete(cb);\n }\n\n private emit(event: string, ...args: unknown[]) {\n if (this.listeners.has(event)) {\n this.listeners.get(event)!.forEach(cb => cb(...args));\n }\n }\n\n // New: Subscription deduplication management with optimizations\n private collectionSubscriptions = new Map<string, {\n backendSubscriptionId: string;\n callbacks: Map<string, {\n onUpdate: (rows: Record<string, unknown>[], meta?: CollectionUpdateMeta) => void;\n onError?: (error: Error) => void;\n }>;\n props: Omit<ListenCollectionProps, \"onUpdate\" | \"onError\">;\n latestData?: Record<string, unknown>[]; // Cache the latest flat rows\n /**\n * The `meta` of the last frame — total, hasMore, nextCursor.\n *\n * Cached beside the rows so a listener attaching to an existing\n * subscription is replayed both, rather than the rows alone and a\n * `meta` it has to go and fetch.\n */\n latestMeta?: CollectionUpdateMeta;\n lastUpdated?: number; // Timestamp for cache invalidation\n isInitialDataReceived?: boolean; // Track if we got initial data\n /**\n * A `subscribe_collection` frame is on the wire and its initial payload\n * has not arrived yet. Without this, a subscription whose subscribe\n * failed is indistinguishable from one still loading, and every later\n * listener attaches to it and waits forever.\n */\n subscribeInFlight?: boolean;\n /**\n * Watchdog for the above. `subscribe_collection` expects no response\n * envelope, so it is not covered by `pendingRequests`' timeout — a lost\n * initial payload would otherwise hang the subscription indefinitely.\n */\n subscribeTimeout?: ReturnType<typeof setTimeout>;\n /**\n * The key columns of this collection, as told by the server on a patch.\n * Rows are columns only, and the SDK holds no collection config, so\n * without this there is nothing to derive an address from.\n */\n pks?: PrimaryKeyInfo[];\n }>();\n\n private singleSubscriptions = new Map<string, {\n backendSubscriptionId: string;\n callbacks: Map<string, {\n onUpdate: (row: Record<string, unknown> | null) => void;\n onError?: (error: Error) => void;\n }>;\n props: ListenOneTarget;\n latestData?: Record<string, unknown> | null; // Cache the latest flat row\n lastUpdated?: number; // Timestamp for cache invalidation\n isInitialDataReceived?: boolean; // Track if we got initial data\n /** See the collection subscription counterparts. */\n subscribeInFlight?: boolean;\n subscribeTimeout?: ReturnType<typeof setTimeout>;\n }>();\n\n // Maps to quickly find subscription by backend subscription ID\n private backendToCollectionKey = new Map<string, string>();\n private backendToEntityKey = new Map<string, string>();\n\n\n private pendingRequests = new Map<string, {\n resolve: (p: unknown) => void;\n reject: (p: Error) => void;\n message?: Record<string, unknown> & { _queuedResolve?: (p: unknown) => void; _queuedReject?: (p: Error) => void }\n }>();\n private reconnectAttempts = 0;\n /**\n * With nothing registered, how many failed redials before the client stops\n * trying until something asks for the connection. With a registration it\n * never stops.\n */\n private maxReconnectAttempts = 5;\n /**\n * Reports the outage once the connection has been down for\n * {@link OUTAGE_REPORT_AFTER_MS}. Armed by the first redial after a drop,\n * cleared when a socket opens or the caller disconnects.\n */\n private outageTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * Whether a socket of this client has ever opened — what makes the next\n * open a *reconnect*, whose server has forgotten every subscription and\n * channel membership and must be told them again.\n *\n * Not `reconnectAttempts > 0`: that counter is a backoff budget, and a\n * fresh one (after giving up, or after a sign-out dropped the socket)\n * starts at zero, so the socket that finally came back looked like a first\n * connect and nothing was re-sent.\n */\n private hadConnection = false;\n private isConnected = false;\n private messageQueue: Record<string, unknown>[] = [];\n private requestTimeoutMs = 30000;\n private subscriptionTimeoutMs = 30000;\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null;\n\n private isAuthenticated = false;\n private authPromise: Promise<void> | null = null;\n private WebSocketConstructor: typeof WebSocket | undefined;\n public onUnauthorized?: () => Promise<boolean>;\n private refreshInProgress: Promise<boolean> | null = null;\n private readonly impersonate?: string;\n\n constructor(config: RebaseWebSocketConfig) {\n this.websocketUrl = config.websocketUrl;\n this.getAuthToken = config.getAuthToken;\n this.onUnauthorized = config.onUnauthorized;\n this.impersonate = config.impersonate;\n this.WebSocketConstructor = config.WebSocket || (typeof WebSocket !== \"undefined\" ? WebSocket : undefined);\n\n // Deliberately does NOT dial here. Constructing the client is not a\n // statement that the app wants a socket — `createRebaseClient` builds\n // one whenever realtime is not explicitly disabled, so connecting here\n // opened a socket on every page load of every app that merely *might*\n // subscribe later. Anonymous-first apps paid that on every visit, to\n // authenticate with nothing, which left them choosing between \"socket\n // on every page load\" and \"no channels at all\".\n //\n // The environment warning is also deferred: an app that never\n // subscribes should say nothing at all. See `ensureConnected`.\n }\n\n /**\n * Open the socket if it is not open (or opening) already.\n *\n * Idempotent, synchronous, and safe to call on every operation that needs a\n * live socket — `initWebSocket` already no-ops on an open socket and is\n * re-entrant, since the reconnect path has always called it.\n */\n public ensureConnected(): void {\n // An explicit `close()` is final. Without this, one queued frame could\n // redial a socket the caller just released and keep a Node process\n // alive forever.\n if (this.closedByCaller) return;\n if (!this.WebSocketConstructor) {\n if (!this.warnedNoWebSocket) {\n this.warnedNoWebSocket = true;\n console.warn(\"WebSocket is not defined in this environment. Realtime subscriptions will not work unless you provide a WebSocket implementation in the config.\");\n }\n return;\n }\n this.installNetworkListeners();\n if (this.ws || this.reconnectTimeout) return;\n // A caller asking for a connection is a fresh reason to try, so it also\n // buys a fresh backoff budget. Without this, the first `subscribe`\n // after a give-up would exhaust the counter on attempt one.\n if (this.gaveUp) {\n this.gaveUp = false;\n this.reconnectAttempts = 0;\n this.outageReported = false;\n }\n // A fresh dial — not a retry inside an outage, which keeps its state.\n if (!this.outageReported) this.setState(\"connecting\");\n this.initWebSocket();\n }\n\n /**\n * Dial now rather than at the next backoff step — the browser says the\n * network is back, or the tab is in front of the user again. Both are the\n * usual end of an outage; neither fires for a server restart, which the\n * backoff covers.\n */\n private retryNow(reason: string): void {\n if (this.closedByCaller) return;\n if (this.connectionState !== \"reconnecting\" && this.connectionState !== \"disconnected\") return;\n // A dial is already on its way.\n if (this.ws) return;\n connectionDebug(`${reason} — retrying the realtime connection`);\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n this.gaveUp = false;\n this.reconnectAttempts = 0;\n this.initWebSocket();\n }\n\n /**\n * `online` and `visibilitychange`. Registered lazily so a Node client, or a\n * page that never subscribes, adds no listener.\n */\n private installNetworkListeners() {\n if (this.onlineListener || typeof window === \"undefined\" || typeof window.addEventListener !== \"function\") return;\n this.onlineListener = () => this.retryNow(\"Network is back\");\n window.addEventListener(\"online\", this.onlineListener);\n if (typeof document !== \"undefined\" && typeof document.addEventListener === \"function\") {\n this.visibilityListener = () => {\n if (document.visibilityState === \"visible\") this.retryNow(\"Tab is visible again\");\n };\n document.addEventListener(\"visibilitychange\", this.visibilityListener);\n }\n }\n\n private removeNetworkListeners() {\n if (this.onlineListener && typeof window !== \"undefined\") {\n window.removeEventListener(\"online\", this.onlineListener);\n }\n if (this.visibilityListener && typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", this.visibilityListener);\n }\n this.onlineListener = null;\n this.visibilityListener = null;\n }\n\n private onlineListener: (() => void) | null = null;\n private visibilityListener: (() => void) | null = null;\n\n /** The session this socket's server refused, and why. Cleared when a socket opens. */\n private refusedSession: { token: string; error: RebaseApiError } | null = null;\n\n /**\n * Whether `error` is the server refusing the session `token` signs in as —\n * revoked, its account gone or disabled, a token that does not verify. Said\n * once per socket and token, as a warning that names the code.\n */\n private noteSessionRefused(token: string, error: unknown): boolean {\n if (!isSessionRefusal(error)) return false;\n if (this.refusedSession?.token !== token) {\n this.refusedSession = { token, error };\n console.warn(\n `[Rebase] Realtime: the server refused this session (${error.code}: ${error.message}). ` +\n \"The socket is connected but not signed in, and what needs an account is refused with that code.\"\n );\n }\n return true;\n }\n\n /**\n * Authenticate the WebSocket connection\n */\n async authenticate(token: string): Promise<void> {\n return new Promise((resolve, reject) => {\n // Random suffix, like every other request id here. Two auth\n // attempts started in the same millisecond produced the same id,\n // and `pendingRequests` is a Map: the second registration replaced\n // the first, so one caller's promise was never settled either way.\n const requestId = `auth_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n const timeout = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n // `authPromise` belongs to `ensureAuthenticated`, which clears\n // it when the whole attempt — retries included — settles.\n // Clearing it here let a second attempt start while this one\n // was still retrying.\n reject(new Error(\"Authentication timeout\"));\n }, 30000);\n\n this.pendingRequests.set(requestId, {\n resolve: () => {\n clearTimeout(timeout);\n this.isAuthenticated = true;\n resolve();\n },\n reject: (error) => {\n clearTimeout(timeout);\n reject(error);\n }\n });\n\n const message = {\n type: \"AUTHENTICATE\",\n requestId,\n payload: this.impersonate !== undefined ? { token, impersonate: this.impersonate } : { token }\n };\n\n if (!this.isConnected || !this.ws) {\n this.messageQueue.unshift(message); // Auth should be first\n } else {\n this.ws.send(JSON.stringify(message));\n }\n });\n }\n\n /**\n * Replace where this socket's token comes from, and authenticate an open,\n * unauthenticated socket with it.\n *\n * @deprecated Use `client.setAuthTokenGetter()` instead — on a client from\n * `createRebaseClient()` the socket already resolves its token through it,\n * together with HTTP, and setting one here splits the two. Will be removed\n * in a future release.\n */\n setAuthTokenGetter(getAuthToken: () => Promise<string | null>): void {\n warnDeprecated(\n \"RebaseWebSocketClient.setAuthTokenGetter\",\n \"`client.ws.setAuthTokenGetter()` is deprecated; use `client.setAuthTokenGetter()`, \" +\n \"which authenticates HTTP requests and the realtime socket together.\"\n );\n this.getAuthToken = getAuthToken;\n // Auto-authenticate if we are already connected but didn't have the token getter yet\n if (this.isConnected && !this.isAuthenticated && !this.authPromise) {\n this.getAuthToken().then(token => {\n if (!this.ws) return; // Prevent memory leaks / actions after disconnect\n if (token) {\n this.authenticate(token).catch(e => {\n if (this.ws) connectionDebug(\"WebSocket auto-auth skipped:\", e?.message || e);\n });\n }\n }).catch(e => {\n // User not logged in or auth still loading — this is expected,\n // the WebSocket will authenticate on-demand when a request is made.\n if (this.ws) connectionDebug(\"WebSocket auto-auth skipped:\", e?.message || e);\n });\n }\n }\n\n /**\n * Drop the socket.\n *\n * `permanent` distinguishes the two callers. Signing out drops the socket\n * but the client stays usable — a later subscribe should reconnect\n * anonymously. `client.close()` is the caller saying they are done, and\n * must not be undone by a stray queued frame.\n */\n public disconnect(permanent = false): void {\n if (permanent) this.closedByCaller = true;\n if (permanent) this.removeNetworkListeners();\n this.isAuthenticated = false;\n this.authPromise = null;\n this.refusedSession = null;\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n // Whatever outage there was ends here: the next dial is a fresh one.\n this.reconnectAttempts = 0;\n this.clearOutageTimer();\n this.outageReported = false;\n this.gaveUp = false;\n this.setState(permanent ? \"closed\" : \"idle\");\n // The subscribe watchdogs too, and for the same reason as the reconnect\n // timer: nothing armed for a socket the caller just released may still\n // go off.\n //\n // It has to be here rather than left to `onclose`, because the line\n // below deliberately nulls that handler — so an explicit disconnect was\n // the one close that did NOT suspend them. They then fired up to\n // `subscriptionTimeoutMs` later: on sign-out, killing a subscription the\n // non-permanent disconnect had deliberately kept so a later subscribe\n // could resume it; on `close()`, calling a consumer's `onError` after\n // they were done, and holding the Node event loop open with a timer that\n // is not unref'd — which is the one thing `close()` exists to prevent.\n this.suspendSubscribeWatchdogs();\n // What the socket delivered was read as the identity it was\n // authenticated as, and a sign-out is exactly that identity going away.\n // The registrations stay so a later dial can resume them; their rows do\n // not, or the next `listen()` on the same query is handed the previous\n // user's rows as already loaded — and, since a loaded registration\n // needs no subscribe, never asks the server at all.\n this.forgetCachedData();\n this.stopLivenessWatch();\n this.forgetServerOptIns();\n if (this.ws) {\n this.ws.onclose = null; // Prevent reconnect on explicit disconnect\n this.ws.onerror = null; // Prevent errors on explicit disconnect\n this.ws.onopen = null;\n this.ws.onmessage = null;\n this.ws.close();\n this.ws = null;\n }\n }\n\n // Initialize WebSocket connection\n private initWebSocket() {\n if (!this.WebSocketConstructor) return;\n if (this.ws?.readyState === this.WebSocketConstructor.OPEN) return;\n\n // Guard against race condition: if a previous socket is still connecting, tear it down\n if (this.ws) {\n this.ws.onclose = null;\n this.ws.close();\n this.ws = null;\n }\n\n try {\n // Captured so each handler can tell \"my socket\" from a later one:\n // a close arriving after a redial must not clear the new socket.\n const socket = new this.WebSocketConstructor(withOptIns(this.websocketUrl));\n this.ws = socket;\n\n this.ws!.onopen = async () => {\n connectionDebug(\"Connected to PostgreSQL backend\");\n const wasReconnect = this.hadConnection;\n this.hadConnection = true;\n this.isConnected = true;\n this.reconnectAttempts = 0;\n // The outage, if there was one, is over. The registrations\n // hear about it through the re-subscribe below: each one's\n // next `onUpdate` carries what was written meanwhile.\n this.clearOutageTimer();\n this.outageReported = false;\n this.gaveUp = false;\n this.setState(\"connected\");\n // A new socket asks the server afresh.\n this.refusedSession = null;\n\n // Auto-authenticate if token getter is available\n if (this.getAuthToken && !this.isAuthenticated) {\n let token: string | null = null;\n try {\n token = await this.getAuthToken();\n if (token) {\n await this.authenticate(token);\n connectionDebug(\"WebSocket auto-authenticated\");\n }\n } catch (error) {\n // A refused session is said as that — once, as a\n // warning — and not as a connect that went without\n // signing in. Anything else (not logged in, auth still\n // loading) is expected: authentication happens on\n // demand.\n if (!(token && this.noteSessionRefused(token, error))) {\n connectionDebug(\"WebSocket connected, not signed in yet:\", (error as Error)?.message || error);\n }\n }\n }\n\n this.emit(wasReconnect ? \"reconnect\" : \"connect\");\n this.processMessageQueue(wasReconnect);\n\n // Re-subscribe all active subscriptions after reconnect.\n // The server-side subscription state was lost when the connection dropped,\n // so we need to re-register every active subscription.\n if (wasReconnect) {\n this.resubscribeAll();\n }\n\n // Subscribes requested while offline have just gone out; they\n // could not be watchdogged at request time.\n this.armPendingSubscribeWatchdogs();\n };\n\n this.ws!.onmessage = (event) => {\n this.lastFrameAt = Date.now();\n try {\n // A reviver parse calls back for every key and value of the\n // frame; most frames have nothing to revive. Same result.\n const message = parseRebaseJson<WebSocketMessage>(event.data);\n this.handleWebSocketMessage(message);\n } catch (error) {\n console.error(\"Error parsing WebSocket message:\", error);\n }\n };\n\n const onClosed = () => {\n connectionDebug(\"Disconnected from PostgreSQL backend\");\n // The next socket says hello again; nothing agreed for this one carries over.\n this.stopLivenessWatch();\n this.forgetServerOptIns();\n // Release the dead socket. `ensureConnected` returns early\n // while `this.ws` is set, so holding a closed one made the\n // \"give up after N attempts\" state permanent: nothing could\n // ever redial, not even a fresh `subscribe`.\n if (this.ws === socket) this.ws = null;\n this.isConnected = false;\n this.isAuthenticated = false;\n this.authPromise = null;\n // The reconnect path re-subscribes everything; a watchdog firing\n // in the meantime would tear down healthy subscriptions.\n this.suspendSubscribeWatchdogs();\n this.emit(\"disconnect\");\n\n // Every request still waiting here was written to this socket,\n // so the server may have run it: the answer is what got lost,\n // not necessarily the request. Re-sending it on the next socket\n // made socket requests at-least-once — one `save()` made two\n // rows, and a statement typed into the Studio SQL editor ran\n // twice behind a single success. They are at-most-once: the\n // caller is told the outcome is unknown, and decides. Frames\n // that never reached a socket are still in `messageQueue` and\n // are not affected.\n for (const [reqId, request] of this.pendingRequests.entries()) {\n this.pendingRequests.delete(reqId);\n if (reqId.startsWith(\"auth_\")) {\n request.reject(new Error(\"Connection closed during authentication\"));\n } else {\n request.reject(connectionLostError());\n }\n }\n\n this.setState(this.outageReported ? \"disconnected\" : \"reconnecting\");\n this.attemptReconnect();\n };\n this.ws!.onclose = onClosed;\n // A socket that went half-open never closes: a browser sees no TCP\n // reset and cannot send a ping. Dropped here instead, when the\n // heartbeats it asked for stop arriving — handlers first, so its\n // close (if one ever comes) does not run `onClosed` twice.\n this.dropSocket = () => {\n if (this.ws !== socket) return;\n socket.onclose = null;\n socket.onmessage = null;\n socket.onerror = null;\n socket.onopen = null;\n try {\n socket.close();\n } catch {\n // Already closing.\n }\n onClosed();\n };\n\n this.ws!.onerror = (error) => {\n console.error(\"WebSocket error:\", error);\n this.isConnected = false;\n this.emit(\"error\", error);\n };\n } catch (error) {\n console.error(\"Failed to initialize WebSocket:\", error);\n this.attemptReconnect();\n }\n }\n\n /** Read the server's `hello`: what it agreed to, and start watching for heartbeats. */\n private receiveHello(message: WebSocketMessage): void {\n const capabilities = \"capabilities\" in message && Array.isArray(message.capabilities)\n ? message.capabilities.filter((name): name is string => typeof name === \"string\")\n : [];\n this.serverOptIns = new Set(capabilities);\n this.serverClientId = \"clientId\" in message && typeof message.clientId === \"string\" ? message.clientId : undefined;\n const heartbeatMs = \"heartbeatMs\" in message && typeof message.heartbeatMs === \"number\" && message.heartbeatMs > 0\n ? message.heartbeatMs\n : undefined;\n if (this.serverOptIns.has(\"heartbeat\") && heartbeatMs !== undefined) this.watchLiveness(heartbeatMs);\n }\n\n /**\n * Redial when two heartbeats in a row have not arrived. Anything the\n * server sends counts as a beat, so a busy socket is never judged on them.\n */\n private watchLiveness(heartbeatMs: number): void {\n this.stopLivenessWatch();\n const deadline = 2 * heartbeatMs + Math.min(HEARTBEAT_GRACE_MS, heartbeatMs);\n this.lastFrameAt = Date.now();\n this.livenessTimer = setInterval(() => {\n const silentFor = Date.now() - this.lastFrameAt;\n if (silentFor <= deadline) return;\n console.warn(`[Rebase] Realtime: nothing from the server for ${Math.round(silentFor / 1000)} s, two missed heartbeats. Reconnecting.`);\n this.stopLivenessWatch();\n this.dropSocket?.();\n }, Math.max(250, Math.floor(heartbeatMs / 2)));\n unref(this.livenessTimer);\n }\n\n private stopLivenessWatch(): void {\n if (this.livenessTimer) {\n clearInterval(this.livenessTimer);\n this.livenessTimer = null;\n }\n }\n\n private forgetServerOptIns(): void {\n this.serverOptIns = new Set();\n this.serverClientId = undefined;\n }\n\n private processMessageQueue(isReconnect: boolean) {\n while (this.messageQueue.length > 0 && this.isConnected) {\n const message = this.messageQueue.shift();\n if (!message) continue;\n if (!this.shouldSendQueued(message, isReconnect)) {\n // Fire-and-forget, so there is no answer to wait for: settle\n // it the way a sent one is settled.\n const resolveQueued = message._queuedResolve;\n if (typeof resolveQueued === \"function\") resolveQueued(undefined);\n continue;\n }\n this.sendMessage(message);\n }\n }\n\n /**\n * Whether a frame that waited in the queue should go out on the socket\n * that just opened. Only a subscribe can be refused, and one is exactly\n * when sending it would give the server a subscription this side will not\n * read:\n *\n * - Its registration is gone, or has been given a new id, while it waited.\n * A listener that unmounted before the socket opened (a StrictMode double\n * mount does exactly this) sends no `unsubscribe`, because there was no\n * socket to send it on, so the server would keep that one forever.\n * - This open is a reconnect. `resubscribeAll` sends every registration\n * under a fresh id right after the queue drains, so the queued copy would\n * be the second of two. It is handed back to that pass instead.\n */\n private shouldSendQueued(message: Record<string, unknown>, isReconnect: boolean): boolean {\n if (message.type !== \"subscribe_collection\" && message.type !== \"subscribe_one\") return true;\n const payload = message.payload;\n if (!payload || typeof payload !== \"object\" || !(\"subscriptionId\" in payload)) return true;\n const subscriptionId = payload.subscriptionId;\n if (typeof subscriptionId !== \"string\") return true;\n\n const registration = message.type === \"subscribe_collection\"\n ? this.collectionSubscriptions.get(this.backendToCollectionKey.get(subscriptionId) ?? \"\")\n : this.singleSubscriptions.get(this.backendToEntityKey.get(subscriptionId) ?? \"\");\n if (!registration || registration.backendSubscriptionId !== subscriptionId) return false;\n if (isReconnect) {\n registration.subscribeInFlight = false;\n return false;\n }\n return true;\n }\n\n private attemptReconnect() {\n if (this.closedByCaller) return;\n\n // A blip becomes an outage once it has lasted OUTAGE_REPORT_AFTER_MS.\n // Said once, to everything that is live, so a view can say its data is\n // stale instead of looking current.\n if (!this.outageReported && !this.outageTimer) {\n this.outageTimer = setTimeout(() => {\n this.outageTimer = null;\n this.reportConnectionLost();\n }, OUTAGE_REPORT_AFTER_MS);\n }\n\n // Giving up is only for a client nothing depends on. While anything is\n // registered the client keeps redialling at the capped interval:\n // stopping after five attempts (about a minute) left every live view\n // that had loaded frozen for the life of the page after any longer\n // outage — a deploy, a crash loop, a slow migration — because a server\n // restart fires no `online` event and Node has no `window` at all.\n if (this.reconnectAttempts >= this.maxReconnectAttempts && !this.hasRegistrations()) {\n console.warn(\"[Rebase] Realtime: the server is unreachable and nothing is subscribed. Not retrying until something asks for the connection.\");\n this.gaveUp = true;\n // Given up is down, however soon the redials happened to fail.\n this.clearOutageTimer();\n this.reportConnectionLost();\n return;\n }\n\n this.reconnectAttempts = Math.min(this.reconnectAttempts + 1, 30);\n const ceiling = Math.min(1000 * Math.pow(2, this.reconnectAttempts), MAX_RECONNECT_DELAY_MS);\n // Anywhere in the step at random, above the floor (\"full jitter\"): a\n // deploy drops every client at the same instant, and the same schedule\n // would bring them all back at the same instant too. Drawn from the\n // last fifth of the step only, the first redials still landed in one\n // 400 ms window; spread over 250 ms – 2 s the peak is 4.4x lower.\n // Uniform above the floor rather than clamped to it, which would land\n // one client in eight on the same millisecond.\n const delay = MIN_RECONNECT_DELAY_MS + Math.floor(Math.random() * (ceiling - MIN_RECONNECT_DELAY_MS));\n\n connectionDebug(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n }\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectTimeout = null;\n this.initWebSocket();\n }, delay);\n }\n\n /**\n * Whether anything depends on the socket coming back: a subscription, a\n * joined channel, or a request whose caller is still waiting for a socket.\n */\n private hasRegistrations(): boolean {\n return this.collectionSubscriptions.size > 0\n || this.singleSubscriptions.size > 0\n || this.channelHandlers.size > 0\n || this.messageQueue.some(frame => typeof frame._queuedReject === \"function\");\n }\n\n /**\n * Tell every subscription and joined channel, once per outage, that the\n * connection is down and their data is not updating.\n *\n * Registrations are kept — that is the difference from a subscribe that\n * failed. When the socket is back they are re-subscribed, and the fresh\n * `onUpdate` is the recovery.\n */\n private clearOutageTimer(): void {\n if (this.outageTimer) clearTimeout(this.outageTimer);\n this.outageTimer = null;\n }\n\n private reportConnectionLost(): void {\n if (this.outageReported) return;\n this.outageReported = true;\n this.setState(\"disconnected\");\n const error = realtimeLostError();\n for (const subscription of this.collectionSubscriptions.values()) {\n this.notifyError(subscription.callbacks, error);\n }\n for (const subscription of this.singleSubscriptions.values()) {\n this.notifyError(subscription.callbacks, error);\n }\n for (const handler of [...this.connectionLostHandlers]) {\n try {\n handler(error);\n } catch (handlerError) {\n console.error(\"Error in connection-lost handler:\", handlerError);\n }\n }\n }\n\n private notifyError(callbacks: Map<string, { onError?: (error: Error) => void }>, error: Error): void {\n for (const callback of [...callbacks.values()]) {\n if (!callback.onError) continue;\n try {\n callback.onError(error);\n } catch (callbackError) {\n console.error(\"Error in subscription error callback:\", callbackError);\n }\n }\n }\n\n /**\n * A listener attaching while an outage is being reported is told at once:\n * its subscribe waits for a socket that may be minutes away, and nothing\n * else would say so.\n */\n private reportOutageTo(onError: ((error: Error) => void) | undefined): void {\n if (!this.outageReported || !onError) return;\n queueMicrotask(() => {\n if (!this.outageReported) return;\n try {\n onError(realtimeLostError());\n } catch (callbackError) {\n console.error(\"Error in subscription error callback:\", callbackError);\n }\n });\n }\n\n private isAuthError(message: WebSocketMessage): boolean {\n if (message.type === \"AUTH_ERROR\") return true;\n const { errorMessage, errorCode } = extractMessageError(message);\n if (errorCode === \"UNAUTHORIZED\" || errorCode === \"JWT_EXPIRED\" || errorCode === \"AUTH_ERROR\") return true;\n const lowerMessage = errorMessage.toLowerCase();\n return lowerMessage.includes(\"unauthorized\") || lowerMessage.includes(\"token expired\") || lowerMessage.includes(\"token is expired\") || lowerMessage.includes(\"invalid token\") || lowerMessage.includes(\"session expired\") || lowerMessage.includes(\"auth error\");\n }\n\n private async handleAuthFailure(): Promise<boolean> {\n if (this.refreshInProgress) {\n return this.refreshInProgress;\n }\n this.refreshInProgress = (async () => {\n this.isAuthenticated = false;\n this.authPromise = null;\n if (this.onUnauthorized) {\n try {\n const refreshed = await this.onUnauthorized();\n if (refreshed && this.getAuthToken) {\n const token = await this.getAuthToken();\n if (token) {\n await this.authenticate(token);\n return true;\n }\n }\n } catch (error) {\n console.error(\"WebSocket auth refresh failed:\", error);\n }\n }\n return false;\n })();\n try {\n return await this.refreshInProgress;\n } finally {\n this.refreshInProgress = null;\n }\n }\n\n /**\n * Shared logic for re-subscribing a collection or row subscription\n * after an auth error is resolved by refreshing credentials.\n */\n private resubscribeAfterAuthRefresh(\n message: WebSocketMessage,\n subscription: {\n backendSubscriptionId: string;\n callbacks: Map<string, { onUpdate: (...args: never[]) => void; onError?: (error: Error) => void }>;\n props: FetchCollectionProps | ListenOneTarget;\n },\n subscriptionKey: string,\n idPrefix: \"collection\" | \"row\",\n backendKeyMap: Map<string, string>,\n messageType: \"subscribe_collection\" | \"subscribe_one\"\n ): void {\n this.handleAuthFailure().then(refreshed => {\n if (refreshed) {\n const oldBackendId = subscription.backendSubscriptionId;\n const newBackendId = `${idPrefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n subscription.backendSubscriptionId = newBackendId;\n backendKeyMap.delete(oldBackendId);\n backendKeyMap.set(newBackendId, subscriptionKey);\n\n // Route through the helpers so the retry is watchdogged too.\n if (messageType === \"subscribe_collection\") {\n this.sendCollectionSubscribe(subscriptionKey);\n } else {\n this.sendEntitySubscribe(subscriptionKey);\n }\n return;\n }\n\n // The refresh did not produce usable credentials. Report the original\n // error and drop the registration, so a later mount can try again\n // rather than attaching to a subscription that will never load.\n const error = frameError(message);\n if (messageType === \"subscribe_collection\") {\n this.failCollectionSubscription(subscriptionKey, error);\n } else {\n this.failEntitySubscription(subscriptionKey, error);\n }\n }).catch(err => {\n const error = err instanceof Error ? err : new Error(String(err));\n if (messageType === \"subscribe_collection\") {\n this.failCollectionSubscription(subscriptionKey, error);\n } else {\n this.failEntitySubscription(subscriptionKey, error);\n }\n });\n }\n\n private handleWebSocketMessage(message: WebSocketMessage) {\n const {\n type,\n requestId,\n subscriptionId\n } = message;\n\n // The socket's own frames. A heartbeat's work is done by arriving\n // (`lastFrameAt`); a server only sends either to a client that asked.\n if (type === \"hello\") {\n this.receiveHello(message);\n return;\n }\n if (type === \"heartbeat\") return;\n\n // Handle responses to pending requests\n if (requestId && this.pendingRequests.has(requestId)) {\n const pendingReq = this.pendingRequests.get(requestId)!;\n if (type === \"ERROR\" || type === \"AUTH_ERROR\" || message.error) {\n if (this.isAuthError(message)) {\n this.pendingRequests.delete(requestId);\n this.handleAuthFailure().then(refreshed => {\n if (refreshed && pendingReq.message) {\n this.doSendMessage(pendingReq.message, pendingReq.resolve, pendingReq.reject).catch(pendingReq.reject);\n } else {\n pendingReq.reject(frameError(message));\n }\n }).catch(err => {\n pendingReq.reject(err);\n });\n } else {\n this.pendingRequests.delete(requestId);\n pendingReq.reject(frameError(message));\n }\n } else {\n this.pendingRequests.delete(requestId);\n pendingReq.resolve(message.payload || message);\n }\n return;\n }\n\n // Channel traffic (broadcast / presence) is addressed by channel name\n // rather than by requestId or subscriptionId, so it is dispatched\n // before the subscription paths — none of which would match it, and\n // the message would otherwise fall through and be dropped silently.\n if (isChannelMessage(message)) {\n const handlers = this.channelHandlers.get(message.channel);\n if (handlers) {\n for (const handler of [...handlers]) {\n try {\n handler(message);\n } catch (error) {\n console.error(\"Error in channel handler:\", error);\n }\n }\n }\n return;\n }\n\n // Handle subscription updates for collection subscriptions\n if (subscriptionId && isCollectionUpdate(message)) {\n // Narrowed, not asserted. `pks` and `meta` are declared on\n // `CollectionUpdateMessage` — they always were — but\n // `WebSocketMessage.type` is a `string` rather than a literal, so\n // the union does not discriminate on its own and both reads went\n // through `message as unknown as { … }`: a second, local\n // declaration of two wire fields, free to drift from the one in\n // `@rebasepro/types` that describes them.\n const collectionUpdate = message;\n const subscriptionKey = this.backendToCollectionKey.get(subscriptionId);\n if (subscriptionKey) {\n const collectionSub = this.collectionSubscriptions.get(subscriptionKey);\n if (collectionSub) {\n const wireEntities = (message.rows || []) as Record<string, unknown>[];\n const incomingRows = wireEntities;\n\n // The keys arrive with the rows, so they are known before the\n // first merge — a CDC-driven change never sends a patch, and\n // learning them from patches alone would leave every\n // externally-written collection unable to match a thing.\n const updatePks = collectionUpdate.pks;\n if (updatePks) collectionSub.pks = updatePks;\n\n // The page metadata, beside the rows it describes.\n //\n // The frame used to carry rows and keys and nothing else, so\n // every subscriber issued a `GET /<collection>/count` per\n // push to recover a total it needed to render the list it\n // had just been handed — one extra round trip per write, per\n // subscriber, and a window in which the count and the rows\n // described different states of the collection.\n const updateMeta = collectionUpdate.meta;\n if (updateMeta) collectionSub.latestMeta = updateMeta;\n\n // Structural merge: preserve cached row references for rows\n // whose values haven't changed. This prevents downstream React components\n // from re-rendering (VirtualTableCell uses deepEqual on rowData —\n // same reference = instant true, avoiding expensive deep comparison).\n const rows = this.mergeRows(collectionSub.latestData, incomingRows, collectionSub.pks);\n\n // Cache the latest data with optimizations\n collectionSub.latestData = rows;\n collectionSub.lastUpdated = Date.now();\n collectionSub.isInitialDataReceived = true;\n // The subscribe landed — stand the watchdog down.\n if (collectionSub.subscribeTimeout) clearTimeout(collectionSub.subscribeTimeout);\n collectionSub.subscribeTimeout = undefined;\n collectionSub.subscribeInFlight = false;\n\n // Notify all callbacks for this subscription\n collectionSub.callbacks.forEach(callback => {\n try {\n callback.onUpdate(rows, collectionSub.latestMeta);\n } catch (error) {\n console.error(\"Error in collection subscription callback:\", error);\n if (callback.onError) {\n callback.onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n });\n return;\n }\n }\n }\n\n // `collection_patch` was handled here. Nothing sends it any more: the\n // server's immediate row patch was replaced by a scoped refetch, which\n // is the only delivery on any path — `realtime-patch-pks.test.ts` in\n // `@rebasepro/server-postgres` asserts zero patch frames, and says so.\n //\n // A branch for a message that cannot arrive is not harmless. It carried\n // its own copy of the cache-merge rules — insert-vs-update by derived\n // address, delete by filter — beside the refetch reconciliation that\n // now does the same job, so a fix to one of them was a fix to half the\n // client, in a half nobody could reach to notice.\n\n // Handle subscription updates for row subscriptions\n if (subscriptionId && type === \"single_update\") {\n const subscriptionKey = this.backendToEntityKey.get(subscriptionId);\n if (subscriptionKey) {\n const entitySub = this.singleSubscriptions.get(subscriptionKey);\n if (entitySub) {\n const wireEntity = message.row ?? null;\n // The row's version, when the server tags its frames (see\n // `sendEntitySubscribe`): kept on the row the way a\n // `findById` keeps it, so `etagOf(row)` is the `If-Match`\n // of an edit made against exactly this frame.\n const etag = \"etag\" in message && typeof message.etag === \"string\" ? message.etag : undefined;\n const row = wireEntity ? withETag(wireEntity as Record<string, unknown>, etag) : null;\n // Cache the latest data with optimizations\n entitySub.latestData = row;\n entitySub.lastUpdated = Date.now();\n entitySub.isInitialDataReceived = true;\n if (entitySub.subscribeTimeout) clearTimeout(entitySub.subscribeTimeout);\n entitySub.subscribeTimeout = undefined;\n entitySub.subscribeInFlight = false;\n\n // Notify all callbacks for this subscription\n entitySub.callbacks.forEach(callback => {\n try {\n callback.onUpdate(row);\n } catch (error) {\n console.error(\"Error in row subscription callback:\", error);\n if (callback.onError) {\n callback.onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n });\n return;\n }\n }\n }\n\n // Handle subscription errors.\n //\n // A refusal the server issues before the frame reaches its realtime\n // service — UNAUTHORIZED on a `requireAuth` server, RATE_LIMITED —\n // carries the frame's `requestId` and no `subscriptionId`. A subscribe\n // is sent with its subscription id as its request id, so that still\n // names it; without this the refusal fell through to a console warning\n // and the listener waited out the watchdog for a \"timed out\".\n const erroredSubscriptionId = subscriptionId ?? requestId;\n if (erroredSubscriptionId && (type === \"ERROR\" || message.error)) {\n const collectionKey = this.backendToCollectionKey.get(erroredSubscriptionId);\n if (collectionKey) {\n const collectionSub = this.collectionSubscriptions.get(collectionKey);\n if (collectionSub) {\n if (this.isAuthError(message)) {\n this.resubscribeAfterAuthRefresh(\n message,\n collectionSub,\n collectionKey,\n \"collection\",\n this.backendToCollectionKey,\n \"subscribe_collection\"\n );\n return;\n }\n\n // The server answered, so nothing is in flight any more. Leave\n // the registration in place (its listeners are still mounted\n // and have been told), but marked idle so the next listener\n // re-subscribes instead of attaching to a dead entry.\n if (collectionSub.subscribeTimeout) clearTimeout(collectionSub.subscribeTimeout);\n collectionSub.subscribeTimeout = undefined;\n collectionSub.subscribeInFlight = false;\n\n const error = frameError(message);\n collectionSub.callbacks.forEach(callback => {\n if (callback.onError) {\n callback.onError(error);\n }\n });\n return;\n }\n }\n\n const entityKey = this.backendToEntityKey.get(erroredSubscriptionId);\n if (entityKey) {\n const entitySub = this.singleSubscriptions.get(entityKey);\n if (entitySub) {\n if (this.isAuthError(message)) {\n this.resubscribeAfterAuthRefresh(\n message,\n entitySub,\n entityKey,\n \"row\",\n this.backendToEntityKey,\n \"subscribe_one\"\n );\n return;\n }\n\n if (entitySub.subscribeTimeout) clearTimeout(entitySub.subscribeTimeout);\n entitySub.subscribeTimeout = undefined;\n entitySub.subscribeInFlight = false;\n\n const error = frameError(message);\n entitySub.callbacks.forEach(callback => {\n if (callback.onError) {\n callback.onError(error);\n }\n });\n return;\n }\n }\n }\n\n // An error that matched no waiter used to fall off the end of this\n // method and disappear. Channel frames are the ones that always did:\n // they are fire-and-forget by design, so no `pendingRequests` entry\n // exists to reject and the server's errors about them — RATE_LIMITED,\n // CHANNEL_FORBIDDEN, CHANNEL_HISTORY_WRITE_FAILED — were dropped while\n // `await channel.broadcast(...)` resolved as if it had been sent.\n //\n // The server now names the channel on those, and the channel-addressed\n // branch above delivers them to `channel.onError()`. This stays as the\n // floor for an error that names nothing — including one from a server\n // older than that change.\n if (type === \"ERROR\" || type === \"error\" || message.error) {\n const { errorMessage, errorCode } = extractMessageError(message);\n console.warn(\n `[Rebase] Realtime error from the server${errorCode ? ` (${errorCode})` : \"\"}: ${errorMessage}`\n );\n }\n }\n\n private async ensureAuthenticated(retryCount = 3): Promise<void> {\n // If already authenticated or no token getter, skip\n if (this.isAuthenticated || !this.getAuthToken) return;\n\n // If auth is in progress, wait for it.\n //\n // The share has to be published *before* the first await, which is why\n // the work lives in its own method. The guard used to be read here and\n // the promise only assigned after `await this.getAuthToken()` — so\n // every caller that arrived during that gap saw `null` and started an\n // attempt of its own. A queue flushing six subscriptions on connect\n // does exactly that, and each extra attempt raced the others through a\n // single `pendingRequests` slot: one settled, the rest hung until their\n // own 30s timeout, and the frames waiting behind them were never sent.\n // Their subscriptions then reported \"Subscription timed out\" — the\n // board's columns loading one at a time, or not at all.\n if (!this.authPromise) {\n this.authPromise = this.runAuthentication(retryCount);\n this.authPromise.finally(() => {\n this.authPromise = null;\n }).catch(() => undefined);\n }\n await this.authPromise;\n }\n\n private async runAuthentication(retryCount: number): Promise<void> {\n // Try to authenticate with retries\n let lastError: unknown = null;\n\n for (let attempt = 0; attempt < retryCount; attempt++) {\n let token: string | null = null;\n try {\n token = await this.getAuthToken!();\n if (!token) throw new Error(\"user not logged in\");\n // Refused on this socket already: the same token gets the\n // same answer, so it is not asked again.\n if (this.refusedSession?.token === token) throw this.refusedSession.error;\n await this.authenticate(token);\n connectionDebug(\"WebSocket authenticated on demand\");\n return; // Success\n } catch (error: unknown) {\n lastError = error;\n\n // A refused session is definitive. Retrying it with backoff\n // kept a `listen()` from hearing why for over three seconds.\n if (token && this.noteSessionRefused(token, error)) throw error;\n\n const errMsg = error instanceof Error ? error.message : String(error);\n // \"not logged in\" / \"Session expired\" are definitive - don't retry\n if (errMsg.includes(\"not logged in\") || errMsg.includes(\"Session expired\")) {\n console.warn(\"WebSocket auth failed: user not logged in\");\n throw error;\n }\n\n // \"still loading\" is transient - retry with backoff (auth controller\n // is restoring tokens from localStorage; it will resolve shortly)\n if (errMsg.includes(\"still loading\")) {\n if (attempt < retryCount - 1) {\n const delay = Math.min(500 * (attempt + 1), 2000);\n await new Promise(resolve => setTimeout(resolve, delay));\n continue;\n }\n }\n\n // For other errors, retry with backoff\n if (attempt < retryCount - 1) {\n const delay = Math.min(1000 * (attempt + 1), 3000);\n connectionDebug(`WebSocket auth attempt ${attempt + 1} failed, retrying in ${delay}ms...`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\n console.warn(\"WebSocket on-demand auth failed after retries:\", lastError);\n throw lastError;\n }\n\n async reauthenticate(): Promise<void> {\n if (!this.getAuthToken) return;\n\n this.isAuthenticated = false;\n try {\n const token = await this.getAuthToken();\n if (!token) throw new Error(\"user not logged in\");\n await this.authenticate(token);\n connectionDebug(\"WebSocket reauthenticated successfully\");\n } catch (error) {\n console.error(\"WebSocket reauthentication failed:\", error);\n throw error;\n }\n }\n\n /**\n * Public because `RebaseRealtimeChannel` sends channel frames through it.\n * Not part of the stable surface — prefer `client.realtime.channel(name)`.\n */\n public sendMessage(message: Record<string, unknown>): Promise<unknown> {\n // Drained from the queue: its caller already holds a promise.\n const queuedMsg: QueuedFrame = message;\n if (queuedMsg._queuedResolve && queuedMsg._queuedReject) {\n const resolve = queuedMsg._queuedResolve;\n const reject = queuedMsg._queuedReject;\n delete queuedMsg._queuedResolve;\n delete queuedMsg._queuedReject;\n return this.doSendMessage(message, resolve, reject);\n }\n\n return new Promise<unknown>((resolve, reject) => {\n // One deadline per request, armed at the call. It used to be armed\n // only when the frame was written, so a request queued while the\n // socket was down had none at all — and when the socket never came\n // back, its caller's promise stayed pending for the life of the\n // page. Running it from the call also keeps a request that waited\n // 25s for a socket from then getting another full 30s.\n let settled = false;\n let deadline: ReturnType<typeof setTimeout> | undefined;\n const onResolve = (value: unknown) => {\n if (settled) return;\n settled = true;\n if (deadline) clearTimeout(deadline);\n resolve(value);\n };\n const onReject = (error: Error) => {\n if (settled) return;\n settled = true;\n if (deadline) clearTimeout(deadline);\n this.abandonedFrames.add(message);\n reject(error);\n };\n if (this.expectsResponse(message)) {\n deadline = setTimeout(() => {\n const queuedAt = this.messageQueue.indexOf(message);\n if (queuedAt >= 0) this.messageQueue.splice(queuedAt, 1);\n let written = false;\n for (const [requestId, pending] of this.pendingRequests) {\n if (pending.message !== message) continue;\n this.pendingRequests.delete(requestId);\n written = true;\n }\n onReject(requestTimeoutError(written));\n }, this.requestTimeoutMs);\n }\n\n if (!this.isConnected || !this.ws) {\n // The queue is only ever drained by a socket opening, so\n // something has to open one. Before lazy connect this was\n // guaranteed by the constructor; now the first frame is what\n // asks for it.\n this.ensureConnected();\n queuedMsg._queuedResolve = onResolve;\n queuedMsg._queuedReject = onReject;\n this.messageQueue.push(message);\n return;\n }\n\n void this.doSendMessage(message, onResolve, onReject);\n });\n }\n\n /**\n * Frames whose caller has already been answered — timed out, or rejected\n * by a close. One can still be on its way to the socket (awaiting\n * authentication), and must not be written after its caller was told it\n * failed.\n */\n private abandonedFrames = new WeakSet<Record<string, unknown>>();\n\n /** Whether the server answers this frame with a response envelope. */\n private expectsResponse(message: Record<string, unknown>): boolean {\n const type = message.type as string;\n return !(SUBSCRIPTION_MESSAGE_TYPES.has(type) || CHANNEL_MESSAGE_TYPES.has(type));\n }\n\n private async doSendMessage(message: Record<string, unknown>, resolve: (value: unknown) => void, reject: (error: Error) => void): Promise<void> {\n // Ensure authenticated before sending non-auth messages.\n //\n // Channel traffic is exempt. `ensureAuthenticated` throws \"user not\n // logged in\" when there is no token, which rejects the frame before it\n // is ever sent — so on an anonymous-first app (the kind this API was\n // added for) *every* channel operation failed client-side, and the\n // server never got to decide. Presence in a public room does not\n // require an account. A signed-in caller still authenticates: the\n // socket does it from `getAuthToken` on open, and the server authorizes\n // these frames either way.\n //\n // Subscription frames authenticate when there is an account and go out\n // without one when there is not. Refusing them here refused every\n // visitor's `listen()` on a server that serves visitors — `find()`\n // beside it worked — so the client was deciding `requireAuth` for a\n // server that had decided the other way.\n const isSubscriptionFrame = SUBSCRIPTION_MESSAGE_TYPES.has(message.type as string);\n if (message.type !== \"AUTHENTICATE\"\n && !CHANNEL_MESSAGE_TYPES.has(message.type as string)\n && this.getAuthToken && !this.isAuthenticated) {\n try {\n await this.ensureAuthenticated();\n } catch (error: unknown) {\n if (!(isSubscriptionFrame && isNotSignedIn(error))) {\n // A refusal keeps its code (`SESSION_REVOKED`, …), so\n // `onError` can tell a revoked session from a broken one.\n if (error instanceof RebaseApiError) {\n reject(error);\n return;\n }\n const errorMessage = error instanceof Error ? error.message : \"Authentication required\";\n reject(new RebaseApiError(errorMessage));\n return;\n }\n }\n }\n\n // Its caller gave up while this waited for authentication.\n if (this.abandonedFrames.has(message)) return;\n\n // The socket went away while this waited for authentication. Nothing\n // was written, so it is still safe to send — on the next socket.\n if (!this.isConnected || !this.ws) {\n const queueable: QueuedFrame = message;\n queueable._queuedResolve = resolve;\n queueable._queuedReject = reject;\n this.messageQueue.push(message);\n this.ensureConnected();\n return;\n }\n\n const requestId = (message.requestId as string) || `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n message.requestId = requestId;\n\n const expectsResponse = this.expectsResponse(message);\n\n // The deadline is the caller's, armed in `sendMessage`; registering\n // here only says \"written, waiting for its answer\".\n if (expectsResponse && !this.pendingRequests.has(requestId)) {\n this.pendingRequests.set(requestId, { resolve, reject, message });\n }\n\n try {\n this.ws.send(JSON.stringify(message));\n if (!expectsResponse) {\n resolve(undefined);\n }\n } catch (error) {\n if (expectsResponse) {\n this.pendingRequests.delete(requestId);\n }\n reject(new RebaseApiError(\"Failed to send message\", { cause: error }));\n }\n }\n\n // Data source methods\n async fetchCollection<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]> {\n const response = await this.sendMessage({\n type: \"FETCH_COLLECTION\",\n payload: props\n }) as { rows?: Record<string, unknown>[] };\n return (response.rows || []);\n }\n\n async fetchOne<M extends Record<string, unknown>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined> {\n const response = await this.sendMessage({\n type: \"FETCH_ONE\",\n payload: props\n }) as { row?: Record<string, unknown> };\n const wireEntity = response.row;\n return wireEntity ?? undefined;\n }\n\n async save<M extends Record<string, unknown>>(props: SaveProps<M>): Promise<Record<string, unknown>> {\n const response = await this.sendMessage({\n type: \"SAVE\",\n payload: props\n }) as { row: Record<string, unknown> };\n return response.row;\n }\n\n async delete<M extends Record<string, unknown>>(props: DeleteProps<M>): Promise<void> {\n await this.sendMessage({\n type: \"DELETE\",\n payload: props\n });\n }\n\n /**\n * @deprecated Will leave the public `client.ws` type in the next minor.\n * Not part of the app-facing client: the admin UI's SQL console reaches it\n * through `<Rebase>`, and it answers only a caller with `database:write`.\n * App code has no supported SQL or branch path on the client; run SQL\n * server-side (`isSQLAdmin(driver.admin)`), and use `client.admin` for\n * users and roles.\n */\n async executeSql(sql: string, options?: { database?: string, role?: string }): Promise<Record<string, unknown>[]> {\n const response = await this.sendMessage({\n type: \"EXECUTE_SQL\",\n payload: { sql,\noptions }\n }) as { result?: Record<string, unknown>[] };\n return response.result || [];\n }\n\n /**\n * Run a script a person wrote — the Studio console — and get back what its\n * last statement returned, every value as the database's text, with the\n * table column each result column was read from. See\n * `SQLAdmin.runSqlScript`.\n */\n async runSqlScript(sql: string, options?: { database?: string, role?: string, signal?: AbortSignal }): Promise<SqlScriptResult> {\n const signal = options?.signal;\n if (signal?.aborted) throw abortReason(signal);\n const requestId = `sql_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n const message: Record<string, unknown> = {\n type: \"EXECUTE_SQL\",\n requestId,\n payload: {\n sql,\n options: { database: options?.database, role: options?.role },\n mode: \"script\"\n }\n };\n const answer = this.sendMessage(message);\n if (!signal) return readSqlScriptResult(await answer);\n // Aborting rejects at once, whatever the server can do; a server that\n // takes `CANCEL_SQL` also stops the statement. The run's own late\n // answer then settles a promise nobody waits on.\n return new Promise<SqlScriptResult>((resolve, reject) => {\n const onAbort = () => {\n reject(abortReason(signal));\n this.cancelSqlRun(message, requestId);\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n answer.then(\n (response) => {\n signal.removeEventListener(\"abort\", onAbort);\n try {\n resolve(readSqlScriptResult(response));\n } catch (error) {\n reject(error);\n }\n },\n (error: unknown) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(error);\n }\n );\n });\n }\n\n /**\n * Stop a console run its caller gave up on: never written if it has not\n * been yet, and cancelled on a server whose `hello` listed `cancel_sql`. A server\n * that does not is left to finish it, bounded by its `statement_timeout`.\n */\n private cancelSqlRun(message: Record<string, unknown>, requestId: string): void {\n const queuedAt = this.messageQueue.indexOf(message);\n if (queuedAt >= 0) {\n this.messageQueue.splice(queuedAt, 1);\n return;\n }\n // Still waiting on authentication in `doSendMessage`: it checks this\n // before writing.\n this.abandonedFrames.add(message);\n // Only to a server whose `hello` said it takes the frame; an older\n // server would answer an unknown type with an error nobody reads.\n if (this.isAuthenticated && this.serverOptIns.has(\"cancel_sql\")) {\n this.sendMessage({ type: \"CANCEL_SQL\", payload: { requestId } }).catch(() => undefined);\n }\n }\n\n async fetchAvailableDatabases(): Promise<string[]> {\n const response = await this.sendMessage({\n type: \"FETCH_DATABASES\",\n payload: {}\n }) as { databases?: string[] };\n return response.databases || [];\n }\n\n async fetchAvailableRoles(): Promise<string[]> {\n const response = await this.sendMessage({\n type: \"FETCH_ROLES\"\n }) as { roles?: string[] };\n return response.roles || [];\n }\n\n async fetchApplicationRoles(): Promise<string[]> {\n const response = await this.sendMessage({\n type: \"FETCH_APPLICATION_ROLES\"\n }) as { roles?: string[] };\n return response.roles || [];\n }\n\n async fetchCurrentDatabase(): Promise<string | undefined> {\n const response = await this.sendMessage({\n type: \"FETCH_CURRENT_DATABASE\"\n }) as { database?: string };\n return response.database;\n }\n\n async checkUniqueField(path: string, name: string, value: unknown, id?: string, collection?: CollectionConfig): Promise<boolean> {\n const response = await this.sendMessage({\n type: \"CHECK_UNIQUE_FIELD\",\n payload: {\n path,\n name,\n value,\n id,\n collection\n }\n }) as { isUnique: boolean };\n return response.isUnique;\n }\n\n async count<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number> {\n const response = await this.sendMessage({\n type: \"COUNT\",\n payload: props\n }) as { count: number };\n return response.count;\n }\n\n async fetchUnmappedTables(mappedPaths?: string[]): Promise<string[]> {\n const response = await this.sendMessage({\n type: \"FETCH_UNMAPPED_TABLES\",\n payload: { mappedPaths }\n }) as { tables?: string[] };\n return response.tables || [];\n }\n\n async fetchTableMetadata(tableName: string): Promise<TableMetadata> {\n const response = await this.sendMessage({\n type: \"FETCH_TABLE_METADATA\",\n payload: { tableName }\n }) as { metadata?: TableMetadata };\n\n return response.metadata || ({ columns: [],\nforeignKeys: [],\njunctions: [],\npolicies: [] } as TableMetadata);\n }\n\n /**\n * @deprecated Will leave the public `client.ws` type in the next minor.\n * Not part of the app-facing client: the admin UI's SQL console reaches it\n * through `<Rebase>`, and it answers only a caller with `database:write`.\n * App code has no supported SQL or branch path on the client; run SQL\n * server-side (`isSQLAdmin(driver.admin)`), and use `client.admin` for\n * users and roles.\n */\n async createBranch(name: string, options?: { source?: string }): Promise<BranchInfo> {\n const response = await this.sendMessage({\n type: \"CREATE_BRANCH\",\n payload: { name,\noptions }\n }) as { branch: BranchInfo };\n return response.branch;\n }\n\n /**\n * @deprecated Will leave the public `client.ws` type in the next minor.\n * Not part of the app-facing client: the admin UI's SQL console reaches it\n * through `<Rebase>`, and it answers only a caller with `database:write`.\n * App code has no supported SQL or branch path on the client; run SQL\n * server-side (`isSQLAdmin(driver.admin)`), and use `client.admin` for\n * users and roles.\n */\n async deleteBranch(name: string): Promise<void> {\n await this.sendMessage({\n type: \"DELETE_BRANCH\",\n payload: { name }\n });\n }\n\n async listBranches(): Promise<BranchInfo[]> {\n const response = await this.sendMessage({\n type: \"LIST_BRANCHES\",\n payload: {}\n }) as { branches?: BranchInfo[] };\n return response.branches || [];\n }\n\n /**\n * Recursively compare two values a refetch can carry for structural\n * equality: primitives, null, undefined, Date, RegExp, arrays and objects.\n *\n * A relation compares by the reference it holds, not by the row it happens\n * to have loaded, so its `data` is left out on both sides. That used to be\n * done by normalising each side into a copy first — two deep copies of\n * every cached row on every frame, most of which then compared equal. It is\n * done in place now, and an unchanged row costs no copy.\n *\n * `relationAware` is off for a relation's own fields: the copies never\n * looked inside those either.\n */\n private deepEqual(a: unknown, b: unknown, relationAware = true): boolean {\n // Same reference or same primitive\n if (a === b) return true;\n\n // Handle null/undefined\n if (a === null || b === null || a === undefined || b === undefined) return false;\n\n // Different types\n if (typeof a !== typeof b) return false;\n\n // Non-object primitives (number, string, boolean, bigint, symbol)\n // that weren't caught by === above (e.g. NaN !== NaN)\n if (typeof a !== \"object\") return false;\n\n // Date comparison\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime();\n }\n if (a instanceof Date || b instanceof Date) return false;\n\n // RegExp comparison\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags;\n }\n if (a instanceof RegExp || b instanceof RegExp) return false;\n\n // Array comparison\n const aIsArray = Array.isArray(a);\n const bIsArray = Array.isArray(b);\n if (aIsArray !== bIsArray) return false;\n\n if (aIsArray && bIsArray) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!this.deepEqual(a[i], b[i], relationAware)) return false;\n }\n return true;\n }\n\n // Object comparison\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n // A relation is equal only to a relation, and never by its `data`.\n let skip: string | undefined;\n let nested = relationAware;\n if (relationAware) {\n const aIsRelation = aObj.__type === \"relation\";\n if (aIsRelation !== (bObj.__type === \"relation\")) return false;\n if (aIsRelation) {\n skip = \"data\";\n nested = false;\n }\n }\n\n let aCount = 0;\n for (const key of Object.keys(aObj)) {\n if (key === skip) continue;\n aCount++;\n if (!Object.prototype.hasOwnProperty.call(bObj, key)) return false;\n if (!this.deepEqual(aObj[key], bObj[key], nested)) return false;\n }\n let bCount = 0;\n for (const key of Object.keys(bObj)) {\n if (key !== skip) bCount++;\n }\n return aCount === bCount;\n }\n\n /**\n * The address of a row, for matching it against another copy of itself.\n *\n * A row is exactly its columns and carries no address, so it is derived\n * from the key columns the server named — including the ordinary case where\n * that key is `id`, which the server reports like any other.\n *\n * Undefined when there are no keys, which means the server could not\n * resolve any: such rows genuinely cannot be recognised, and guessing at a\n * column called `id` would be inventing an identity for a table that has\n * none.\n */\n private rowAddress(row: Record<string, unknown>, pks: PrimaryKeyInfo[] | undefined): string | undefined {\n if (!pks || pks.length === 0) return undefined;\n const address = buildCompositeId(row, pks);\n if (!address || address.split(COMPOSITE_ID_SEPARATOR).every(part => part === \"\")) return undefined;\n return address;\n }\n\n /**\n * Merge incoming rows with cached data, preserving cached references\n * for rows whose values haven't changed. This avoids unnecessary\n * React re-renders when the server refetches all rows but most\n * haven't actually changed.\n */\n private mergeRows(\n cached: Record<string, unknown>[] | undefined,\n incoming: Record<string, unknown>[],\n pks?: PrimaryKeyInfo[]\n ): Record<string, unknown>[] {\n if (!cached || cached.length === 0) return incoming;\n\n // Build a lookup from cached rows by address for O(1) access\n const cachedById = new Map<string, Record<string, unknown>>();\n for (const row of cached) {\n const address = this.rowAddress(row, pks);\n if (address !== undefined) cachedById.set(address, row);\n }\n\n // Read once per frame, not per row.\n const explain = realtimeDebugOn();\n\n return incoming.map(incomingRow => {\n const address = this.rowAddress(incomingRow, pks);\n const cachedRow = address === undefined ? undefined : cachedById.get(address);\n if (!cachedRow) return incomingRow;\n\n // Compare flat rows directly (no more path/values nesting)\n if (this.deepEqual(cachedRow, incomingRow)) return cachedRow;\n\n // Why it differs, key by key — a debugging aid, so only while\n // realtime debugging is on. It used to run for every changed row\n // of every frame, in production: a bulk write to a 1,000-row live\n // list was 1,000 `JSON.stringify` calls and console lines a frame.\n if (explain) {\n const mismatches: Record<string, { cached: unknown, incoming: unknown }> = {};\n const allKeys = new Set([...Object.keys(cachedRow), ...Object.keys(incomingRow)]);\n for (const key of allKeys) {\n if (!this.deepEqual(cachedRow[key], incomingRow[key])) {\n mismatches[key] = { cached: cachedRow[key],\nincoming: incomingRow[key] };\n }\n }\n console.debug(`[RebaseWS] Row ${address} refetch mismatch:\\n`, JSON.stringify(mismatches, null, 2));\n }\n return incomingRow;\n });\n }\n\n // Subscription methods\n listenCollection<M extends Record<string, unknown>>(\n // The subscribe frame, minus the callbacks — so `page`, which only a\n // subscription takes (a fetch resolves it to an offset before it gets\n // here), is expressible. The subscription key is derived from these\n // props rather than a hand-listed subset, so a new field narrows the\n // key by construction and two different queries cannot collide on it.\n props: Omit<ListenCollectionProps<M>, \"onUpdate\" | \"onError\">,\n onUpdate: (rows: Record<string, unknown>[], meta?: CollectionUpdateMeta) => void,\n onError?: (error: Error) => void\n ): () => void {\n // A subscription is the app asking for live data, so this is where the\n // socket is wanted. Called before the dedup check below: joining an\n // existing subscription must still work if the socket has since gone.\n this.ensureConnected();\n\n const subscriptionKey = this.createCollectionSubscriptionKey(props);\n const callbackId = `callback_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n // Check if we already have a subscription for these exact parameters\n const existingSubscription = this.collectionSubscriptions.get(subscriptionKey);\n\n if (existingSubscription) {\n // Reuse existing subscription - just add the new callback\n const callbackMap = existingSubscription.callbacks as Map<string, {\n onUpdate: (rows: Record<string, unknown>[], meta?: CollectionUpdateMeta) => void;\n onError?: (error: Error) => void;\n }>;\n callbackMap.set(callbackId, { onUpdate,\nonError });\n this.reportOutageTo(onError);\n\n // Immediately fire the callback with cached data if available\n if (existingSubscription.latestData !== undefined && existingSubscription.isInitialDataReceived) {\n try {\n onUpdate(existingSubscription.latestData, existingSubscription.latestMeta);\n } catch (error) {\n console.error(\"Error in collection subscription callback:\", error);\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n } else if (!existingSubscription.subscribeInFlight) {\n // Registered but idle: its subscribe never landed (the send failed,\n // or the server answered with an error). Nothing is coming, so\n // re-issue it — otherwise this listener waits forever.\n this.sendCollectionSubscribe(subscriptionKey);\n }\n\n // Return unsubscribe function\n return () => {\n callbackMap.delete(callbackId);\n if (callbackMap.size === 0) {\n // Only tear down if this is still the same registration — a\n // failed subscribe may have replaced it in the meantime.\n if (this.collectionSubscriptions.get(subscriptionKey) !== existingSubscription) return;\n if (existingSubscription.subscribeTimeout) clearTimeout(existingSubscription.subscribeTimeout);\n this.collectionSubscriptions.delete(subscriptionKey);\n this.backendToCollectionKey.delete(existingSubscription.backendSubscriptionId);\n if (this.isConnected && this.ws) {\n this.sendMessage({\n type: \"unsubscribe\",\n payload: { subscriptionId: existingSubscription.backendSubscriptionId }\n }).catch(console.error);\n }\n }\n };\n }\n\n // Create new subscription\n const backendSubscriptionId = `collection_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n const callbackMap = new Map<string, {\n onUpdate: (rows: Record<string, unknown>[], meta?: CollectionUpdateMeta) => void;\n onError?: (error: Error) => void;\n }>();\n callbackMap.set(callbackId, { onUpdate,\nonError });\n this.reportOutageTo(onError);\n\n this.collectionSubscriptions.set(subscriptionKey, {\n backendSubscriptionId,\n callbacks: callbackMap,\n props\n });\n\n // Add reverse lookup\n this.backendToCollectionKey.set(backendSubscriptionId, subscriptionKey);\n\n // Send subscription request to backend. A failure here drops the\n // registration and notifies every listener, so the next mount retries.\n this.sendCollectionSubscribe(subscriptionKey);\n\n // Return unsubscribe function\n return () => {\n const subscription = this.collectionSubscriptions.get(subscriptionKey);\n if (subscription) {\n const callbacks = subscription.callbacks;\n callbacks.delete(callbackId);\n if (callbacks.size === 0) {\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n this.collectionSubscriptions.delete(subscriptionKey);\n this.backendToCollectionKey.delete(subscription.backendSubscriptionId);\n if (this.isConnected && this.ws) {\n this.sendMessage({\n type: \"unsubscribe\",\n payload: { subscriptionId: subscription.backendSubscriptionId }\n }).catch(console.error);\n }\n }\n }\n };\n }\n\n listenOne<M extends Record<string, unknown>>(\n props: ListenOneTarget<M>,\n onUpdate: (row: Record<string, unknown> | null) => void,\n onError?: (error: Error) => void\n ): () => void {\n this.ensureConnected();\n\n const subscriptionKey = this.createSingleSubscriptionKey(props);\n const callbackId = `callback_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n // Check if we already have a subscription for these exact parameters\n const existingSubscription = this.singleSubscriptions.get(subscriptionKey);\n\n if (existingSubscription) {\n // Reuse existing subscription - just add the new callback\n const callbackMap = existingSubscription.callbacks as Map<string, {\n onUpdate: (row: Record<string, unknown> | null) => void;\n onError?: (error: Error) => void;\n }>;\n callbackMap.set(callbackId, { onUpdate,\nonError });\n this.reportOutageTo(onError);\n\n // Immediately fire the callback with cached data if available\n if (existingSubscription.latestData !== undefined && existingSubscription.isInitialDataReceived) {\n try {\n onUpdate(existingSubscription.latestData);\n } catch (error) {\n console.error(\"Error in row subscription callback:\", error);\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n } else if (!existingSubscription.subscribeInFlight) {\n // See listenCollection: a registration with nothing in flight is\n // dead, and attaching to it silently would hang this listener.\n this.sendEntitySubscribe(subscriptionKey);\n }\n\n // Return unsubscribe function\n return () => {\n callbackMap.delete(callbackId);\n if (callbackMap.size === 0) {\n if (this.singleSubscriptions.get(subscriptionKey) !== existingSubscription) return;\n if (existingSubscription.subscribeTimeout) clearTimeout(existingSubscription.subscribeTimeout);\n // No more callbacks, unsubscribe from backend\n this.singleSubscriptions.delete(subscriptionKey);\n this.backendToEntityKey.delete(existingSubscription.backendSubscriptionId);\n if (this.isConnected && this.ws) {\n this.sendMessage({\n type: \"unsubscribe\",\n payload: { subscriptionId: existingSubscription.backendSubscriptionId }\n }).catch(console.error);\n }\n }\n };\n }\n\n // Create new subscription\n const backendSubscriptionId = `entity_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n const callbackMap = new Map<string, {\n onUpdate: (row: Record<string, unknown> | null) => void;\n onError?: (error: Error) => void;\n }>();\n callbackMap.set(callbackId, { onUpdate,\nonError });\n this.reportOutageTo(onError);\n\n this.singleSubscriptions.set(subscriptionKey, {\n backendSubscriptionId,\n callbacks: callbackMap,\n props\n });\n\n // Add reverse lookup\n this.backendToEntityKey.set(backendSubscriptionId, subscriptionKey);\n\n // Send subscription request to backend\n this.sendEntitySubscribe(subscriptionKey);\n\n // Return unsubscribe function\n return () => {\n const subscription = this.singleSubscriptions.get(subscriptionKey);\n if (subscription) {\n const callbacks = subscription.callbacks;\n callbacks.delete(callbackId);\n if (callbacks.size === 0) {\n this.singleSubscriptions.delete(subscriptionKey);\n this.backendToEntityKey.delete(subscription.backendSubscriptionId);\n if (this.isConnected && this.ws) {\n this.sendMessage({\n type: \"unsubscribe\",\n payload: { subscriptionId: subscription.backendSubscriptionId }\n }).catch(console.error);\n }\n }\n }\n };\n }\n\n /**\n * Send a `subscribe_collection` for an already-registered subscription and\n * arm its watchdog.\n *\n * Every path that registers a collection subscription goes through here, so\n * that a subscribe which never lands — a rejected send, or a server that\n * never answers — always ends up in `failCollectionSubscription` rather than\n * leaving the entry parked with `isInitialDataReceived === false` forever.\n */\n private sendCollectionSubscribe(subscriptionKey: string): void {\n const subscription = this.collectionSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n\n const backendSubscriptionId = subscription.backendSubscriptionId;\n subscription.subscribeInFlight = true;\n\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n subscription.subscribeTimeout = undefined;\n // Only time out a frame that is actually on the wire. While offline the\n // message just sits in the queue, and reconnect backoff can exceed the\n // timeout — `armPendingSubscribeWatchdogs` picks these up on connect.\n if (this.isConnected) this.sendCollectionSubscribeWatchdog(subscriptionKey);\n\n this.sendMessage({\n type: \"subscribe_collection\",\n // So a refusal addressed by request id names the subscription —\n // see the subscription-error branch of `handleWebSocketMessage`.\n requestId: backendSubscriptionId,\n payload: {\n ...subscription.props,\n subscriptionId: backendSubscriptionId\n }\n }).catch(error => {\n const current = this.collectionSubscriptions.get(subscriptionKey);\n if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;\n this.failCollectionSubscription(\n subscriptionKey,\n error instanceof Error ? error : new Error(String(error))\n );\n });\n }\n\n /** The `listenOne` counterpart of {@link sendCollectionSubscribe}. */\n private sendEntitySubscribe(subscriptionKey: string): void {\n const subscription = this.singleSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n\n const backendSubscriptionId = subscription.backendSubscriptionId;\n subscription.subscribeInFlight = true;\n\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n subscription.subscribeTimeout = undefined;\n if (this.isConnected) this.sendEntitySubscribeWatchdog(subscriptionKey);\n\n this.sendMessage({\n type: \"subscribe_one\",\n requestId: backendSubscriptionId,\n payload: {\n ...subscription.props,\n subscriptionId: backendSubscriptionId,\n // Ask for the row's `ETag` on every frame. A server that\n // predates it ignores the key and sends untagged frames, which\n // read as rows with no version — what they were before.\n etag: true\n }\n }).catch(error => {\n const current = this.singleSubscriptions.get(subscriptionKey);\n if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;\n this.failEntitySubscription(\n subscriptionKey,\n error instanceof Error ? error : new Error(String(error))\n );\n });\n }\n\n /**\n * Report a subscribe failure to every listener and drop the registration.\n *\n * Dropping it is the point: the callbacks stay live (their components are\n * still mounted and have been told), but the next `listenCollection` for\n * these params finds no entry and issues a fresh subscribe instead of\n * silently attaching to a dead one.\n */\n private failCollectionSubscription(subscriptionKey: string, error: Error): void {\n const subscription = this.collectionSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n subscription.subscribeInFlight = false;\n\n this.collectionSubscriptions.delete(subscriptionKey);\n this.backendToCollectionKey.delete(subscription.backendSubscriptionId);\n\n subscription.callbacks.forEach(callback => {\n if (callback.onError) {\n try {\n callback.onError(error);\n } catch (callbackError) {\n console.error(\"Error in collection subscription error callback:\", callbackError);\n }\n }\n });\n }\n\n /** The `listenOne` counterpart of {@link failCollectionSubscription}. */\n private failEntitySubscription(subscriptionKey: string, error: Error): void {\n const subscription = this.singleSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n subscription.subscribeInFlight = false;\n\n this.singleSubscriptions.delete(subscriptionKey);\n this.backendToEntityKey.delete(subscription.backendSubscriptionId);\n\n subscription.callbacks.forEach(callback => {\n if (callback.onError) {\n try {\n callback.onError(error);\n } catch (callbackError) {\n console.error(\"Error in row subscription error callback:\", callbackError);\n }\n }\n });\n }\n\n /**\n * Stop the watchdogs without failing anything — used when the socket drops,\n * since the reconnect path re-subscribes everything anyway and a watchdog\n * firing mid-reconnect would tear down healthy subscriptions.\n */\n private suspendSubscribeWatchdogs(): void {\n for (const sub of this.collectionSubscriptions.values()) {\n if (sub.subscribeTimeout) clearTimeout(sub.subscribeTimeout);\n sub.subscribeTimeout = undefined;\n sub.subscribeInFlight = false;\n }\n for (const sub of this.singleSubscriptions.values()) {\n if (sub.subscribeTimeout) clearTimeout(sub.subscribeTimeout);\n sub.subscribeTimeout = undefined;\n sub.subscribeInFlight = false;\n }\n }\n\n /**\n * Drop every registration's cached rows, keeping the registrations.\n *\n * A registration without rows counts as not loaded, so the next listener\n * to attach asks the server (or waits for the resubscribe) instead of\n * being handed what is cached.\n */\n private forgetCachedData(): void {\n for (const sub of this.collectionSubscriptions.values()) {\n sub.latestData = undefined;\n sub.latestMeta = undefined;\n sub.lastUpdated = undefined;\n sub.isInitialDataReceived = false;\n }\n for (const sub of this.singleSubscriptions.values()) {\n sub.latestData = undefined;\n sub.lastUpdated = undefined;\n sub.isInitialDataReceived = false;\n }\n }\n\n /**\n * Arm watchdogs for subscribes that were requested while offline and have\n * just been flushed to the socket. Their timers were deliberately not set at\n * request time, so without this they would have no timeout at all.\n */\n private armPendingSubscribeWatchdogs(): void {\n for (const [key, sub] of this.collectionSubscriptions.entries()) {\n if (sub.subscribeInFlight && !sub.subscribeTimeout) this.sendCollectionSubscribeWatchdog(key);\n }\n for (const [key, sub] of this.singleSubscriptions.entries()) {\n if (sub.subscribeInFlight && !sub.subscribeTimeout) this.sendEntitySubscribeWatchdog(key);\n }\n }\n\n private sendCollectionSubscribeWatchdog(subscriptionKey: string): void {\n const subscription = this.collectionSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n const backendSubscriptionId = subscription.backendSubscriptionId;\n subscription.subscribeTimeout = setTimeout(() => {\n const current = this.collectionSubscriptions.get(subscriptionKey);\n if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;\n if (!current.subscribeInFlight) return;\n this.failCollectionSubscription(\n subscriptionKey,\n new RebaseApiError(\"Subscription timed out\", { code: \"SUBSCRIPTION_TIMEOUT\" })\n );\n }, this.subscriptionTimeoutMs);\n }\n\n private sendEntitySubscribeWatchdog(subscriptionKey: string): void {\n const subscription = this.singleSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n const backendSubscriptionId = subscription.backendSubscriptionId;\n subscription.subscribeTimeout = setTimeout(() => {\n const current = this.singleSubscriptions.get(subscriptionKey);\n if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;\n if (!current.subscribeInFlight) return;\n this.failEntitySubscription(\n subscriptionKey,\n new RebaseApiError(\"Subscription timed out\", { code: \"SUBSCRIPTION_TIMEOUT\" })\n );\n }, this.subscriptionTimeoutMs);\n }\n\n /**\n * Re-send all active subscriptions to the backend after a reconnect.\n * The server wipes subscription state when a client disconnects, so\n * we need to re-register everything to resume receiving updates.\n */\n private resubscribeAll(): void {\n connectionDebug(`[WS] Re-subscribing: ${this.collectionSubscriptions.size} collection(s), ${this.singleSubscriptions.size} row(ies)`);\n\n // A registration still in flight here had its subscribe sent on this\n // socket already, while it was authenticating: the close clears the\n // flag on every registration, and the queue hands its own subscribes\n // back to this pass. Re-sending one of those opened a second server\n // subscription under a new id and orphaned the first — every write was\n // refetched twice, and the extra one was never unsubscribed.\n\n // Re-subscribe collection subscriptions\n for (const [key, sub] of this.collectionSubscriptions.entries()) {\n if (sub.subscribeInFlight) continue;\n // Generate a fresh backend ID since the old one is no longer valid on the server\n const oldBackendId = sub.backendSubscriptionId;\n const newBackendId = `collection_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n sub.backendSubscriptionId = newBackendId;\n\n // Update reverse lookup\n this.backendToCollectionKey.delete(oldBackendId);\n this.backendToCollectionKey.set(newBackendId, key);\n\n this.sendCollectionSubscribe(key);\n }\n\n // Re-subscribe row subscriptions\n for (const [key, sub] of this.singleSubscriptions.entries()) {\n if (sub.subscribeInFlight) continue;\n const oldBackendId = sub.backendSubscriptionId;\n const newBackendId = `entity_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n sub.backendSubscriptionId = newBackendId;\n\n this.backendToEntityKey.delete(oldBackendId);\n this.backendToEntityKey.set(newBackendId, key);\n\n this.sendEntitySubscribe(key);\n }\n }\n\n private createCollectionSubscriptionKey(props: Omit<ListenCollectionProps, \"onUpdate\" | \"onError\">): string {\n // Derived from the props, not a hand-listed subset of them.\n //\n // Two subscriptions share one server subscription when their keys\n // match, so a field left off the key makes two different queries\n // collide and hands the second listener the first one's rows. `offset`\n // and `logical` were both missing: page two of a live list showed page\n // one, and two views filtered by different `or(...)` groups saw the\n // same rows. Listing fields by hand is what let that happen, so the key\n // now covers whatever `FetchCollectionProps` carries.\n //\n // `collection` is the exception: it is the whole collection config,\n // property thunks and all, so it contributes its name as before.\n const { collection, ...query } = props as FetchCollectionProps & Record<string, unknown>;\n const key = {\n ...query,\n collection: collection?.name\n };\n // Use replacer function (not array) to sort keys at all levels for deterministic output\n return JSON.stringify(key, (_, value) => {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return Object.keys(value).sort().reduce((sorted: Record<string, unknown>, k) => {\n sorted[k] = value[k];\n return sorted;\n }, {});\n }\n return value;\n });\n }\n\n private createSingleSubscriptionKey(props: ListenOneTarget): string {\n // Listeners asking for different relations are different reads: one\n // shared subscription would hand each the other's shape.\n const include = props.include !== undefined ? `|${JSON.stringify(props.include)}` : \"\";\n return `${props.path}|${props.id}${include}`;\n }\n}\n\n/** What an aborted signal rejects with: its reason, or an `AbortError` as `fetch` gives. */\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException(\"The operation was aborted.\", \"AbortError\");\n}\n","/**\n * Broadcast channels and presence, as an SDK surface.\n *\n * The realtime engine has supported `join_channel`, `broadcast`,\n * `presence_track`, `presence_untrack` and `presence_state` for a while, but\n * the client only recognised those types well enough to send them\n * fire-and-forget: there were no methods to call and no way to receive channel\n * or broadcast events, since `on()` handles only connect / disconnect /\n * reconnect / error. Anything wanting presence therefore opened a *second*\n * socket and reimplemented the AUTHENTICATE → AUTH_SUCCESS handshake, the\n * reconnect backoff, and the presence heartbeat — a couple of hundred lines\n * per app, all of it duplicating this package.\n *\n * Two protocol details this hides, because both are easy to get wrong and\n * neither is discoverable from the message list:\n *\n * - **A joining client is told only about its own join.** The `presence_diff`\n * it receives after `presence_track` contains just itself. The existing\n * roster arrives only in response to an explicit `presence_state` request,\n * so `join()` sends one.\n * - **Presence expires after 30s** (`PRESENCE_TIMEOUT_MS` server-side). A\n * client that tracks once and goes quiet silently vanishes from everyone\n * else's roster while still sitting in the document, so `track()` starts a\n * heartbeat and `leave()` stops it.\n */\n\n/**\n * Presence state keyed by the server's client id.\n *\n * Re-exported rather than re-declared, for the same reason\n * {@link ChannelHistoryEntry} is below: the frames that carry a roster are\n * declared in `@rebasepro/types`, and a second copy of the shape here is a\n * second thing to keep in step with the wire.\n */\nexport type { PresenceState } from \"@rebasepro/types\";\nimport type { ChannelHistoryEntry, ChannelMessage, PresenceState } from \"@rebasepro/types\";\n\nexport interface PresenceDiff {\n joins: PresenceState;\n leaves: PresenceState;\n}\n\nexport interface BroadcastEvent {\n event: string;\n payload: unknown;\n /**\n * Per-channel sequence number, present only on retained channels.\n *\n * Monotonically increasing and dense, so a consumer that remembers the last\n * one it applied can tell the server exactly where to resume from.\n */\n seq?: number;\n /**\n * True when this arrived through catch-up rather than live.\n *\n * Handlers do not have to care — replayed messages are delivered to the\n * same `onBroadcast` handlers, in sequence order, so an operation stream\n * needs no second code path. It is exposed for consumers that want to,\n * for example, skip an animation while fast-forwarding.\n */\n replayed?: boolean;\n}\n\n/**\n * One retained message, as returned by {@link RebaseRealtimeChannel.history}.\n *\n * Re-exported rather than re-declared: the copy that used to live here had\n * drifted `at` to optional, while the server always sends it.\n */\nexport type { ChannelHistoryEntry } from \"@rebasepro/types\";\nimport { RebaseApiError } from \"@rebasepro/types\";\nimport { unref } from \"@rebasepro/utils\";\n\n/** The answer to a catch-up request. */\nexport interface ChannelHistoryResult {\n messages: ChannelHistoryEntry[];\n /**\n * Whether the server retains anything for this channel.\n *\n * False means there is no retention rule configured for it, so the empty\n * list means \"never keeps history\" rather than \"you missed nothing\" — a\n * client that needs to converge has to fall back to a full resync.\n */\n retained: boolean;\n /** Highest sequence the server holds, even if this batch was capped. */\n latestSeq?: number;\n}\n\n/** Options for a channel handle. */\nexport interface ChannelOptions {\n /**\n * Ask the server to replay what this client missed, on join and on every\n * reconnect.\n *\n * Only meaningful for a channel the *server* has a retention rule for —\n * retention is configured on the backend, since a channel is created by\n * whoever names it and a client-chosen history depth would let any visitor\n * commit the backend to unbounded storage. On a channel with no rule the\n * server answers `retained: false` and this is inert.\n */\n history?: boolean;\n}\n\n/** The socket operations a channel needs; satisfied by RebaseWebSocketClient. */\nexport interface ChannelTransport {\n sendMessage(message: Record<string, unknown>): Promise<unknown>;\n onChannelMessage(channel: string, handler: (message: ChannelMessage) => void): () => void;\n onReconnect(handler: () => void): () => void;\n /**\n * Notified once per outage that outlasted a blip. Optional so a test\n * transport need not implement it.\n */\n onConnectionLost?(handler: (error: RebaseApiError) => void): () => void;\n /**\n * Whether the server agreed, for the current socket, to `name` (see the\n * socket's `hello`). A transport without it is one whose server agreed to\n * nothing.\n */\n hasServerOptIn?(name: string): boolean;\n /** The current socket's id on the server: the `senderId` of what it broadcasts. */\n readonly socketClientId?: string;\n}\n\n/**\n * A message held back while a catch-up is in flight — or, `own`, the number\n * this client's own broadcast took, which moves the watermark and is never\n * delivered (a sender is not sent its own messages).\n */\ntype HeldBack = BroadcastEvent & { own?: true };\n\n/**\n * Re-send presence comfortably inside the server's 30s expiry.\n *\n * Two-thirds of the window: one lost heartbeat still leaves time for the next\n * before the entry is reaped, so a single dropped frame is not a disappearance.\n */\nconst PRESENCE_HEARTBEAT_MS = 20_000;\n\n/**\n * How long live messages are held back waiting for a catch-up response.\n *\n * Short, because the cost of waiting is visible — on a collaborative document\n * this is a stall in everyone else's edits appearing. Long enough that a slow\n * replay of a busy channel is not abandoned needlessly.\n */\nconst CATCH_UP_TIMEOUT_MS = 10_000;\n\nexport class RebaseRealtimeChannel {\n private presenceHandlers = new Set<(state: PresenceState, diff?: PresenceDiff) => void>();\n private broadcastHandlers = new Set<(event: BroadcastEvent) => void>();\n private errorHandlers = new Set<(error: RebaseApiError) => void>();\n private unsubscribers: (() => void)[] = [];\n\n /** Last known roster, kept so handlers always get a full picture. */\n private presences: PresenceState = {};\n /** What this client last tracked, replayed on reconnect and heartbeat. */\n private trackedState: Record<string, unknown> | null = null;\n private heartbeat: ReturnType<typeof setInterval> | null = null;\n private joined = false;\n\n /** Whether this handle asks the server to replay missed messages. */\n private wantsHistory: boolean;\n\n /**\n * Highest sequence number delivered to handlers so far.\n *\n * This is the resume point sent as `sinceSeq`, and the watermark that makes\n * replay idempotent: catch-up ranges overlap with what arrived live, and\n * anything at or below this has already been seen.\n */\n private lastSeq = 0;\n\n /**\n * Live messages that arrived while a catch-up was in flight.\n *\n * Without this they would be delivered ahead of the older messages being\n * fetched, and — worse — would advance {@link lastSeq} past them, so the\n * catch-up response would then be discarded as already-seen and those\n * messages would be lost for good. Held here and flushed, in order, once\n * the replay lands.\n */\n private pendingLive: HeldBack[] = [];\n private catchUpInFlight = false;\n\n /**\n * Whether {@link lastSeq} is a position this client reached, rather than\n * the zero of a handle that has not caught up yet.\n *\n * A client joining for the first time asks for whatever is retained, so\n * history pruned before it ever joined is not something it missed. Only a\n * client with a position can have a hole in what it saw.\n */\n private positioned = false;\n\n /** The page size the catch-up in flight asked for, reused for its later pages. */\n private catchUpLimit: number | undefined;\n\n /**\n * `latestSeq` of an empty page that left this client behind, while a\n * second request confirms it.\n *\n * The server reads the retained messages and then the cursor, so a message\n * committed between the two shows up in `latestSeq` and not in the page.\n * The second request returns it if it exists; only a second empty page\n * means the messages up to this seq are gone.\n */\n private emptyTailSeq: number | null = null;\n\n /**\n * Deadline for a catch-up response.\n *\n * Buffering live messages is only safe because the wait is bounded. A\n * catch-up frame that never arrives — a server that dropped it, a socket\n * that died between request and reply — would otherwise leave the channel\n * silently holding every subsequent edit forever, which is a worse failure\n * than the one replay was added to fix.\n */\n private catchUpTimeout: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Callers of {@link history} awaiting the next `channel_history` frame.\n *\n * These frames are addressed by channel rather than by request id, so they\n * are matched in arrival order. Requests on one channel are serialized by\n * the socket, so FIFO is the right correlation here.\n */\n private historyWaiters: Array<(result: ChannelHistoryResult) => void> = [];\n\n constructor(\n public readonly name: string,\n private transport: ChannelTransport,\n options: ChannelOptions = {}\n ) {\n this.wantsHistory = options.history ?? false;\n }\n\n /**\n * Turn on catch-up for a handle that was created without it.\n *\n * The client hands back the same channel object for a given name, so a\n * later `channel(name, { history: true })` has no new object to configure —\n * it upgrades this one instead. Idempotent, and never downgrades: one\n * caller asking for history must not be switched off by another that did\n * not ask.\n */\n enableHistory(): void {\n if (this.wantsHistory) return;\n this.wantsHistory = true;\n if (this.joined) void this.requestHistory();\n }\n\n /**\n * Join the channel and ask for the current roster.\n *\n * Called automatically by `track`, `broadcast`, `onPresence` and\n * `onBroadcast`; calling it directly is only needed to start receiving\n * before there is anything to send.\n */\n /**\n * Send a channel message.\n *\n * Every channel message is read by the server out of a `payload` envelope\n * (`payload?.channel`, `payload?.state`, `payload?.event`). Sending those\n * fields flat does not error: `payload?.channel` simply reads as\n * `undefined`, so the client is registered into channel `undefined` with\n * empty state, and the echo comes back with no `channel` for\n * `onChannelMessage` to match — presence and broadcast both go quiet with\n * nothing logged. Funnelled through one place so a new message type cannot\n * reintroduce that.\n */\n private send(type: string, fields: Record<string, unknown> = {}): Promise<unknown> {\n return this.transport.sendMessage({ type, payload: { channel: this.name, ...fields } });\n }\n\n async join(): Promise<void> {\n if (this.joined) return;\n this.joined = true;\n\n this.unsubscribers.push(\n this.transport.onChannelMessage(this.name, (message) => this.handle(message))\n );\n\n // A reconnect drops server-side channel membership and presence, so\n // both have to be re-established. Nothing else notices this: the\n // socket comes back, and the client just stops receiving.\n this.unsubscribers.push(\n this.transport.onReconnect(() => {\n void this.rejoin();\n })\n );\n\n // An outage longer than a blip: nothing arrives on this channel until\n // the socket is back, and the app should be able to say so. The\n // re-join above is the recovery.\n const onConnectionLost = this.transport.onConnectionLost?.((error) => this.emitError(error));\n if (onConnectionLost) this.unsubscribers.push(onConnectionLost);\n\n await this.send(\"join_channel\");\n // Not optional. Joining does not push the roster — without this the\n // channel believes it is alone until somebody else happens to move.\n await this.send(\"presence_state\");\n if (this.wantsHistory) await this.requestHistory();\n }\n\n private async rejoin(): Promise<void> {\n try {\n await this.send(\"join_channel\");\n await this.send(\"presence_state\");\n if (this.trackedState) {\n await this.send(\"presence_track\", { state: this.trackedState });\n }\n // The reason this class tracks a sequence number at all: whatever\n // was broadcast while the socket was down was delivered to everyone\n // else and never to us. Asking from `lastSeq` is the difference\n // between resuming and resyncing the whole document.\n if (this.wantsHistory) await this.requestHistory();\n } catch {\n // The socket is down again; the next reconnect will retry.\n }\n }\n\n /**\n * Ask the server for everything after {@link lastSeq}.\n *\n * Live messages are buffered from here until the answer arrives — see\n * {@link pendingLive}.\n */\n private async requestHistory(limit?: number): Promise<void> {\n this.catchUpInFlight = true;\n this.catchUpLimit = limit;\n\n if (this.catchUpTimeout) clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = setTimeout(() => this.abandonCatchUp(), CATCH_UP_TIMEOUT_MS);\n unref(this.catchUpTimeout);\n\n try {\n await this.send(\"channel_history\", {\n sinceSeq: this.lastSeq,\n ...(limit !== undefined ? { limit } : {})\n });\n } catch {\n // The frame never went out, so nothing will answer it.\n this.abandonCatchUp();\n }\n }\n\n /**\n * Give up waiting for a catch-up and release what was held back.\n *\n * The buffered messages are still the freshest thing this client has, so\n * they are delivered rather than dropped. Callers of {@link history} are\n * answered with `retained: false` — accurate in the sense that matters:\n * this client has no history to work from and has to resync.\n */\n private abandonCatchUp(): void {\n if (this.catchUpTimeout) {\n clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = null;\n }\n if (!this.catchUpInFlight) return;\n this.catchUpInFlight = false;\n this.emptyTailSeq = null;\n for (const resolve of this.historyWaiters.splice(0)) {\n resolve({ messages: [], retained: false });\n }\n this.flushPendingLive();\n }\n\n /**\n * Publish this client's presence state, and keep publishing it.\n *\n * Calling `track` again replaces the state (and restarts the heartbeat),\n * which is how you update e.g. a cursor position.\n */\n async track(state: Record<string, unknown>): Promise<void> {\n await this.join();\n this.trackedState = state;\n\n await this.send(\"presence_track\", { state });\n\n if (!this.heartbeat) {\n this.heartbeat = setInterval(() => {\n if (!this.trackedState) return;\n void this.send(\"presence_track\", { state: this.trackedState })\n .catch(() => { /* a dropped beat is recoverable; the next one carries the same state */ });\n }, PRESENCE_HEARTBEAT_MS);\n // Do not hold a Node process open just to say \"still here\".\n unref(this.heartbeat);\n }\n }\n\n /** Stop publishing presence, without leaving the channel. */\n async untrack(): Promise<void> {\n this.stopHeartbeat();\n this.trackedState = null;\n if (this.joined) {\n await this.send(\"presence_untrack\");\n }\n }\n\n /**\n * Observe the roster. The handler fires immediately with what is already\n * known, then on every change.\n */\n onPresence(handler: (state: PresenceState, diff?: PresenceDiff) => void): () => void {\n this.presenceHandlers.add(handler);\n void this.join();\n if (Object.keys(this.presences).length > 0) handler({ ...this.presences });\n return () => this.presenceHandlers.delete(handler);\n }\n\n /**\n * Send a broadcast. The sender does not receive its own message.\n *\n * Resolves when the frame is written to the socket, **not** when the server\n * has accepted it. Channel frames are fire-and-forget by design — a\n * collaborative app broadcasts a cursor position sixty times a second, and\n * waiting for an acknowledgement on each would make that a round trip.\n *\n * A refusal therefore arrives on {@link onError}, not as a rejection here.\n */\n async broadcast(event: string, payload: unknown): Promise<void> {\n await this.join();\n await this.send(\"broadcast\", { event, payload });\n }\n\n /**\n * Observe refusals about this channel.\n *\n * `CHANNEL_FORBIDDEN` when an authorizer refuses a join, a broadcast or a\n * history read; `RATE_LIMITED` past the channel frame budget;\n * `CHANNEL_HISTORY_WRITE_FAILED` / `CHANNEL_HISTORY_READ_FAILED` when\n * retention cannot be served; `CHANNEL_BUS_PAYLOAD_TOO_LARGE` when a\n * broadcast on an ephemeral channel is too big to cross the bus.\n *\n * `CHANNEL_HISTORY_GAP` is raised by the client: a catch-up found that the\n * server no longer retains messages this client never received, so its\n * state has a hole a replay cannot fill. `details` is `{ from, to }`, the\n * inclusive range of sequence numbers missed. Resync the channel's state\n * from its source of truth.\n *\n * These used to be dropped on the floor — there is no promise to reject on\n * a fire-and-forget frame, so a forbidden broadcast looked exactly like a\n * delivered one. With no handler attached they are logged as a warning,\n * because an unobserved refusal is the failure this exists for.\n *\n * @returns An unsubscribe function.\n */\n onError(handler: (error: RebaseApiError) => void): () => void {\n this.errorHandlers.add(handler);\n void this.join();\n return () => this.errorHandlers.delete(handler);\n }\n\n /** Observe broadcasts. Pass an event name to filter. */\n onBroadcast(handler: (event: BroadcastEvent) => void): () => void;\n onBroadcast(event: string, handler: (payload: unknown) => void): () => void;\n onBroadcast(\n eventOrHandler: string | ((event: BroadcastEvent) => void),\n maybeHandler?: (payload: unknown) => void\n ): () => void {\n const wrapped: (event: BroadcastEvent) => void = typeof eventOrHandler === \"string\"\n ? (e) => { if (e.event === eventOrHandler) maybeHandler!(e.payload); }\n : eventOrHandler;\n\n this.broadcastHandlers.add(wrapped);\n void this.join();\n return () => this.broadcastHandlers.delete(wrapped);\n }\n\n /**\n * The last sequence number this channel has delivered.\n *\n * Zero on a channel that retains nothing. Persist it if you want catch-up\n * to survive a page reload as well as a reconnect, and pass it back via\n * {@link history}.\n */\n get sequence(): number {\n return this.lastSeq;\n }\n\n /**\n * Fetch retained messages explicitly, instead of waiting for join or\n * reconnect to do it.\n *\n * Defaults to resuming from {@link sequence}. Messages are delivered to\n * `onBroadcast` handlers as usual — the returned value is for callers that\n * want to inspect the batch, or to learn from `retained` that the channel\n * keeps no history at all.\n */\n async history(options: { sinceSeq?: number; limit?: number } = {}): Promise<ChannelHistoryResult> {\n await this.join();\n if (options.sinceSeq !== undefined) {\n this.lastSeq = options.sinceSeq;\n this.positioned = options.sinceSeq > 0;\n }\n\n const result = new Promise<ChannelHistoryResult>((resolve) => {\n this.historyWaiters.push(resolve);\n });\n await this.requestHistory(options.limit);\n return result;\n }\n\n /** Leave the channel and release every listener and timer. */\n async leave(): Promise<void> {\n this.stopHeartbeat();\n this.trackedState = null;\n this.presences = {};\n this.presenceHandlers.clear();\n this.broadcastHandlers.clear();\n // A rejoin is a fresh start: replaying from a watermark left over from\n // the previous membership would silently skip everything before it.\n this.lastSeq = 0;\n this.positioned = false;\n this.emptyTailSeq = null;\n this.pendingLive = [];\n this.catchUpInFlight = false;\n if (this.catchUpTimeout) {\n clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = null;\n }\n for (const resolve of this.historyWaiters.splice(0)) {\n resolve({ messages: [], retained: false });\n }\n\n for (const off of this.unsubscribers) off();\n this.unsubscribers = [];\n\n if (this.joined) {\n this.joined = false;\n await this.send(\"leave_channel\");\n }\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeat) {\n clearInterval(this.heartbeat);\n this.heartbeat = null;\n }\n }\n\n /** Fold an incoming frame into the roster and fan it out. */\n private handle(message: ChannelMessage): void {\n switch (message.type) {\n case \"presence_state\": {\n this.presences = message.presences;\n this.emitPresence();\n break;\n }\n case \"presence_diff\": {\n const { joins, leaves } = message;\n // A diff carries only what moved, so the roster is maintained\n // here rather than handed to callers to reassemble.\n for (const [id, state] of Object.entries(joins)) this.presences[id] = state;\n for (const id of Object.keys(leaves)) delete this.presences[id];\n this.emitPresence({ joins, leaves });\n break;\n }\n case \"broadcast\": {\n const seq = message.seq;\n const event: BroadcastEvent = {\n event: message.event,\n payload: message.payload,\n ...(seq !== undefined ? { seq } : {})\n };\n\n // Unsequenced channels keep the original behaviour exactly:\n // straight through, no buffering, no watermark.\n if (seq === undefined) {\n this.deliver(event);\n break;\n }\n\n if (this.catchUpInFlight) {\n this.pendingLive.push(event);\n break;\n }\n if (seq <= this.lastSeq) break; // already delivered\n if (this.fillsGap(event)) break;\n this.lastSeq = seq;\n this.positioned = true;\n this.deliver(event);\n break;\n }\n case \"broadcast_ack\": {\n // This client's own message took `seq`. Nothing to deliver —\n // a sender is never sent its own broadcast — but the number\n // is accounted for, so it is not mistaken for a missed one.\n const held: HeldBack = { event: \"\", payload: undefined, seq: message.seq, own: true };\n if (this.catchUpInFlight) {\n this.pendingLive.push(held);\n break;\n }\n if (message.seq <= this.lastSeq) break;\n if (this.fillsGap(held)) break;\n this.lastSeq = message.seq;\n this.positioned = true;\n break;\n }\n case \"channel_history\": {\n if (this.catchUpTimeout) {\n clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = null;\n }\n\n const { messages: entries, retained, latestSeq } = message;\n\n for (const resolve of this.historyWaiters.splice(0)) {\n resolve({ messages: entries, retained, latestSeq });\n }\n\n // The log numbers every retained message densely, so a first\n // new entry past `lastSeq + 1` means retention pruned what lies\n // between. Said before the entries are delivered, so a handler\n // sees the hole where it is.\n const wasPositioned = this.positioned;\n const firstNew = entries.find((entry) => entry.seq > this.lastSeq);\n if (firstNew && wasPositioned && firstNew.seq > this.lastSeq + 1) {\n this.reportHistoryGap(this.lastSeq + 1, firstNew.seq - 1);\n }\n\n // Server-ordered ascending; the watermark check makes the\n // overlap with anything already seen a no-op rather than a\n // double-apply.\n const ownId = this.watchesForGaps() ? this.transport.socketClientId : undefined;\n for (const entry of entries) {\n if (entry.seq <= this.lastSeq) continue;\n this.lastSeq = entry.seq;\n // Sent from this very socket, which is never sent its own\n // messages live; a hole filled from history keeps to that.\n // (After a reconnect the socket is a new one, and a replay\n // delivers the old socket's messages as it always has.)\n if (ownId !== undefined && entry.senderId === ownId) continue;\n this.deliver({\n event: entry.event,\n payload: entry.payload,\n seq: entry.seq,\n replayed: true\n });\n }\n if (retained) this.positioned = true;\n\n // One answer is one page. Until the watermark reaches the\n // server's cursor, live messages stay held back: flushing them\n // now would advance the watermark over the rest of the replay,\n // and nothing would ask for it again.\n const behind = retained && latestSeq !== undefined && latestSeq > this.lastSeq;\n if (behind && firstNew) {\n this.emptyTailSeq = null;\n void this.requestHistory(this.catchUpLimit);\n break;\n }\n if (behind && wasPositioned) {\n if (this.emptyTailSeq === null) {\n this.emptyTailSeq = latestSeq;\n void this.requestHistory(this.catchUpLimit);\n break;\n }\n // Confirmed: nothing after `lastSeq` is retained up to the\n // first page's cursor. A live message already held back is\n // real, so the hole ends before it.\n const firstLive = this.pendingLive.reduce<number | undefined>(\n (min, event) => event.seq !== undefined && (min === undefined || event.seq < min) ? event.seq : min,\n undefined\n );\n const end = firstLive !== undefined ? Math.min(this.emptyTailSeq, firstLive - 1) : this.emptyTailSeq;\n if (end > this.lastSeq) {\n this.reportHistoryGap(this.lastSeq + 1, end);\n this.lastSeq = end;\n }\n }\n\n this.emptyTailSeq = null;\n this.catchUpInFlight = false;\n this.flushPendingLive();\n break;\n }\n case \"ERROR\":\n case \"error\": {\n // The server refused something about this channel. Channel\n // frames are fire-and-forget — there is no promise to reject —\n // so this is where a refusal becomes visible.\n const raw = message.payload?.error;\n const asObject = typeof raw === \"object\" ? raw : undefined;\n const text = asObject?.message\n ?? (typeof raw === \"string\" ? raw : undefined)\n ?? message.error\n ?? \"The server refused a channel operation.\";\n this.emitError(new RebaseApiError(text, {\n ...(asObject?.code ? { code: asObject.code } : {})\n }));\n break;\n }\n }\n }\n\n /**\n * Whether a jump in the sequence means a missed message: only when the\n * server numbers this client's own broadcasts for it (`channel_seq`), so\n * its own messages leave no hole. Against a server that does not, a hole\n * is as likely to be the client's own message, and the old behaviour —\n * deliver, advance — stands.\n */\n private watchesForGaps(): boolean {\n return this.transport.hasServerOptIn?.(\"channel_seq\") === true;\n }\n\n /**\n * Hold `event` back and fetch what lies between it and {@link lastSeq},\n * when it is past a hole. A frame can go missing between instances — a\n * dropped bus connection, a bus that does not order — and the watermark\n * would otherwise step over it for good.\n *\n * @returns Whether it was held back.\n */\n private fillsGap(event: HeldBack): boolean {\n const seq = event.seq;\n if (seq === undefined || !this.positioned || seq <= this.lastSeq + 1 || !this.watchesForGaps()) return false;\n this.pendingLive.push(event);\n void this.requestHistory();\n return true;\n }\n\n /** Tell the app that retention dropped messages it never received. */\n private reportHistoryGap(from: number, to: number): void {\n this.emitError(new RebaseApiError(\n `Channel \"${this.name}\" missed messages ${from}–${to}: the server no longer retains them. Resync this channel's state.`,\n { code: \"CHANNEL_HISTORY_GAP\", details: { from, to } }\n ));\n }\n\n private emitError(error: RebaseApiError): void {\n if (this.errorHandlers.size === 0) {\n // Not silence: an unobserved refusal is exactly the failure\n // this exists for.\n console.warn(\n `[Rebase] Channel \"${this.name}\" error${error.code ? ` (${error.code})` : \"\"}: ${error.message}. ` +\n \"Attach `channel.onError(...)` to handle it.\"\n );\n return;\n }\n for (const handler of [...this.errorHandlers]) {\n try {\n handler(error);\n } catch (e) {\n console.error(\"Error in channel error handler:\", e);\n }\n }\n }\n\n /** Deliver everything held back during a catch-up, in sequence order. */\n private flushPendingLive(): void {\n if (this.pendingLive.length === 0) return;\n const buffered = this.pendingLive.sort((a, b) => (a.seq ?? 0) - (b.seq ?? 0));\n this.pendingLive = [];\n for (const event of buffered) {\n const seq = event.seq;\n if (seq !== undefined) {\n if (seq <= this.lastSeq) continue;\n this.lastSeq = seq;\n this.positioned = true;\n }\n if (event.own) continue;\n this.deliver(event);\n }\n }\n\n private deliver(event: BroadcastEvent): void {\n for (const handler of [...this.broadcastHandlers]) handler(event);\n }\n\n private emitPresence(diff?: PresenceDiff): void {\n const snapshot = { ...this.presences };\n for (const handler of this.presenceHandlers) handler(snapshot, diff);\n }\n}\n","import {\n isUnsupported,\n type ByIdReadOptions,\n type FindAllParams,\n type IterateParams,\n type LogicalCondition,\n type SDKCollectionClient,\n type UpdateValues,\n type UpsertOptions,\n type WhereFilterOp,\n type WhereValueFor,\n type WriteOptions\n} from \"@rebasepro/types\";\nimport type { FindParams } from \"./transport\";\nimport type {\n CollectionClient,\n LiveResult,\n ObserveByIdOptions,\n ObserveOptions,\n RestoreOptions,\n RowSnapshotMeta,\n TrashClient\n} from \"./collection\";\nimport type { OfflineApi, OfflineConfig, OfflineManager, OfflineQueueOptions, OfflineStatus } from \"./offline\";\nimport { SDKQueryBuilder } from \"./sdk_query_builder\";\n\ntype AnyRow = Record<string, unknown>;\ntype InnerFactory = (slug: string) => SDKCollectionClient<AnyRow>;\n\n/**\n * `createRebaseClient({ offline })`'s handle on the sync engine, which it\n * fetches with `import()` the moment the client is created.\n *\n * The engine ({@link OfflineManager} and the local query evaluator, store and\n * codec it brings) is most of the SDK's weight, and a client created without\n * `offline` never runs a line of it. It used to be a static import of the\n * client factory, so every app built on the SDK downloaded it before its first\n * paint whether or not it turned offline on.\n *\n * Everything the factory and the app reach is here, with the same shape:\n *\n * - Calls made before the engine has arrived are queued and run, in the order\n * they were made, the moment it does — the scope changes the factory makes\n * from auth events, and every collection a client wrapped meanwhile.\n * - What answers with a promise waits for the engine and then asks it.\n * - What answers synchronously answers what the engine would have before\n * anything ran: `status()` is the initial status, a subscription is\n * registered as soon as there is something to register it with, and its\n * unsubscribe works before and after.\n *\n * Once the engine is there every call goes straight to it.\n *\n * @internal\n */\nexport class LazyOfflineManager {\n private engine?: OfflineManager;\n private loadError?: unknown;\n private queued: ((engine: OfflineManager) => void)[] = [];\n private readonly failed: ((error: unknown) => void)[] = [];\n readonly api: OfflineApi;\n readonly keepsLastUser: boolean;\n\n constructor(config: OfflineConfig, createInner: InnerFactory) {\n this.keepsLastUser = config.keepLastUser === true;\n import(\"./offline\").then(({ OfflineManager: Engine }) => {\n const engine = new Engine(config, createInner);\n this.engine = engine;\n const queued = this.queued;\n this.queued = [];\n this.failed.length = 0;\n for (const op of queued) op(engine);\n }).catch((error: unknown) => {\n this.loadError = error;\n this.queued = [];\n console.error(\"[Rebase] The offline engine could not be loaded; offline is unavailable on this client.\", error);\n for (const reject of this.failed.splice(0)) reject(error);\n });\n\n this.api = {\n sync: async () => await (await this.run((engine) => engine.api.sync())),\n pending: async (options?: OfflineQueueOptions) => await (await this.run((engine) => engine.api.pending(options))),\n clear: async (options?: OfflineQueueOptions) => await (await this.run((engine) => engine.api.clear(options))),\n status: (): OfflineStatus => this.engine\n ? this.engine.api.status()\n : { online: !(typeof navigator !== \"undefined\" && navigator.onLine === false), syncing: false, pending: 0 },\n onStatusChange: (listener) => this.subscribe((engine) => engine.api.onStatusChange(listener)),\n onQueueChange: (listener) => this.subscribe((engine) => engine.api.onQueueChange(listener))\n };\n }\n\n /**\n * Run `op` on the engine: now if it is there, otherwise once it is, after\n * everything queued before it.\n */\n private run<T>(op: (engine: OfflineManager) => T): Promise<T> {\n if (this.engine) {\n try {\n return Promise.resolve(op(this.engine));\n } catch (error) {\n return Promise.reject(error);\n }\n }\n if (this.loadError !== undefined) return Promise.reject(this.loadError);\n return new Promise<T>((resolve, reject) => {\n this.failed.push(reject);\n this.queued.push((engine) => {\n try {\n resolve(op(engine));\n } catch (error) {\n reject(error);\n }\n });\n });\n }\n\n /** A subscription made now, registered on the engine as soon as there is one. */\n private subscribe(start: (engine: OfflineManager) => () => unknown, onError?: (error: Error) => void): () => void {\n if (this.engine) {\n const stop = start(this.engine);\n return () => { stop(); };\n }\n let stop: (() => unknown) | undefined;\n let cancelled = false;\n this.run((engine) => {\n if (!cancelled) stop = start(engine);\n }).catch((error: unknown) => {\n onError?.(error instanceof Error ? error : new Error(String(error)));\n });\n return () => {\n cancelled = true;\n stop?.();\n stop = undefined;\n };\n }\n\n holdScope(): void {\n void this.run((engine) => engine.holdScope()).catch(() => undefined);\n }\n\n setScope(uid: string | undefined, options?: { unverified?: boolean }): void {\n void this.run((engine) => engine.setScope(uid, options)).catch(() => undefined);\n }\n\n async resumeLastUser(): Promise<void> {\n await (await this.run((engine) => engine.resumeLastUser()));\n }\n\n dispose(): void {\n void this.run((engine) => engine.dispose()).catch(() => undefined);\n }\n\n /**\n * The engine's wrapper around `inner` — itself once the engine is there,\n * and until then a client of the same shape that hands each call to it.\n */\n wrap<M extends AnyRow>(slug: string, inner: CollectionClient<M>): CollectionClient<M> {\n if (this.engine) return this.engine.wrap(slug, inner);\n\n let real: CollectionClient<M> | undefined;\n void this.run((engine) => { real = engine.wrap(slug, inner); }).catch(() => undefined);\n const client = async (): Promise<CollectionClient<M>> => {\n if (!real) await this.run(() => undefined);\n return real!;\n };\n const live = (start: (target: CollectionClient<M>) => () => void, onError?: (error: Error) => void): () => void =>\n real ? start(real) : this.subscribe(() => start(real!), onError);\n\n const trash: TrashClient<M> = {\n find: async (params?: FindParams<M>) => (await client()).trash.find(params),\n count: async (params?: FindParams<M>) => (await client()).trash.count(params),\n restore: async (id: string | number, options?: RestoreOptions<M>) => (await client()).trash.restore(id, options),\n restoreMany: async (ids: (string | number)[], options?: RestoreOptions<M>) => (await client()).trash.restoreMany(ids, options),\n purge: async (id: string | number, options?: WriteOptions) => (await client()).trash.purge(id, options),\n purgeMany: async (ids: (string | number)[], options?: WriteOptions) => (await client()).trash.purgeMany(ids, options)\n };\n\n const deferred: CollectionClient<M> = {\n find: async (params?: FindParams<M>) => (await client()).find(params),\n iterate: (params?: IterateParams<M>) => (async function* () {\n yield* (await client()).iterate(params);\n })(),\n findAll: async (params?: FindAllParams<M>) => (await client()).findAll(params),\n findById: async (id: string | number, options?: ByIdReadOptions) => (await client()).findById(id, options),\n get: async (id: string | number) => (await client()).get(id),\n create: async (data: Partial<M>, id?: string | number, options?: WriteOptions) => (await client()).create(data, id, options),\n createMany: async (\n data: Partial<M>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] } & WriteOptions\n ) => (await client()).createMany(data, options),\n upsert: async (data: Partial<M>, options?: UpsertOptions) => (await client()).upsert(data, options),\n updateMany: async (\n updates: { id: string | number; data: Partial<M> | UpdateValues<Partial<M>> }[],\n options?: WriteOptions\n ) => (await client()).updateMany(updates, options),\n deleteMany: async (ids: (string | number)[], options?: WriteOptions) => (await client()).deleteMany(ids, options),\n update: async (\n id: string | number,\n data: Partial<M> | UpdateValues<Partial<M>>,\n options?: WriteOptions\n ) => (await client()).update(id, data, options),\n delete: async (id: string | number, options?: WriteOptions) => (await client()).delete(id, options),\n count: async (params?: FindParams<M>): Promise<number> => (await client()).count(params),\n observe: (\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) => live((target) => target.observe(params, onResult, onError, options), onError),\n observeById: (\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveByIdOptions\n ) => live((target) => target.observeById(id, onResult, onError, options), onError),\n // As the engine does: a client that cannot listen keeps the inner\n // client's marker, so `isUnsupported(client.listen)` still says so.\n listen: isUnsupported(inner.listen)\n ? inner.listen\n : (params, onUpdate, onError) => live((target) => target.listen(params, onUpdate, onError), onError),\n listenById: isUnsupported(inner.listenById)\n ? inner.listenById\n : (id, onUpdate, onError, options) => live((target) => target.listenById(id, onUpdate, onError, options), onError),\n // The engine hands these to the inner client as they are.\n listenCount: inner.listenCount,\n aggregate: inner.aggregate,\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SDKQueryBuilder<M>(deferred);\n if (typeof columnOrCondition === \"object\") return builder.where(columnOrCondition);\n return builder.where(\n columnOrCondition as keyof M & string,\n operator!,\n value as WhereValueFor<WhereFilterOp, M[keyof M & string]>\n );\n },\n orderBy: (column, direction, nulls) => new SDKQueryBuilder<M>(deferred).orderBy(column, direction, nulls),\n limit: (count) => new SDKQueryBuilder<M>(deferred).limit(count),\n offset: (count) => new SDKQueryBuilder<M>(deferred).offset(count),\n search: (searchString, options) => new SDKQueryBuilder<M>(deferred).search(searchString, options),\n vectorSearch: (property, vector, options) => new SDKQueryBuilder<M>(deferred).vectorSearch(property, vector, options),\n include: (...relations) => new SDKQueryBuilder<M>(deferred).include(...relations),\n fields: (...columns) => new SDKQueryBuilder<M>(deferred).fields(...columns),\n distinct: (enabled) => new SDKQueryBuilder<M>(deferred).distinct(enabled),\n after: (cursor) => new SDKQueryBuilder<M>(deferred).after(cursor),\n trash\n };\n return deferred;\n }\n}\n","import { createTransport, RebaseApiError, RebaseClientConfig } from \"./transport\";\nimport { RebaseClientError } from \"./errors\";\nimport { createAuth, CreateAuthOptions } from \"./auth\";\nimport { createAdmin, CreateAdminOptions } from \"./admin\";\nimport { createCron, CreateCronOptions } from \"./cron\";\nimport { createBackups } from \"./backups\";\nimport { createApiKeys, CreateApiKeysOptions, createPersonalKeys, CreatePersonalKeysOptions } from \"./api-keys\";\nimport { CollectionClient, createCollectionClient, invalidBulkBody } from \"./collection\";\nimport { createFunctionsClient } from \"./functions\";\nimport { createStorage } from \"./storage\";\nimport { ClientStorageSourceRegistry } from \"./storage-registry\";\nimport { RebaseWebSocketClient } from \"./websocket\";\nimport { RebaseRealtimeChannel, type ChannelOptions } from \"./realtime-channel\";\nimport type { OfflineApi, OfflineConfig } from \"./offline\";\nimport { LazyOfflineManager } from \"./offline-lazy\";\nimport {\n type BatchOperation,\n type BatchResult,\n DEFAULT_STORAGE_SOURCE_KEY,\n type IncludeOf,\n InsertOf,\n RebaseClient,\n RebaseSdkData,\n RowOf,\n StorageSource,\n StorageSourceDefinition,\n StorageSourceRegistry,\n UpdateOf,\n type WriteOptions\n} from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n// ─── Public API surface ──────────────────────────────────────────────────────\n//\n// This barrel is the public API of `@rebasepro/client`. It is an explicit,\n// curated list — NOT `export *` — so that adding an export to a module below\n// does not silently republish it to app developers. Internal factories\n// (`createTransport`, `createAuth`, `createCollectionClient`, …), the raw\n// `Transport`, the storage-source registry impl, the JSON reviver, and the\n// concrete `SDKQueryBuilder` class are intentionally NOT re-exported: they are\n// implementation details of `createRebaseClient()` and have no external\n// consumers. App developers reach them through the client instance, never by\n// importing the factory. To add something to the public surface, add it here\n// deliberately.\n\n// Errors — the single error type thrown by SDK HTTP calls, plus the\n// data-proxy's unknown-collection error.\nexport { RebaseApiError } from \"./transport\";\nexport { RebaseClientError } from \"./errors\";\n// The codes `RebaseApiError.code` carries. An open union — routes add their own\n// — so it gives completion on the common ones without pretending to be closed.\nexport type { RebaseErrorCode } from \"@rebasepro/types\";\n\n// Query + collection types (annotate SDK results; construct via the fluent API).\nexport type { RebaseClientConfig, FindParams, FindResponse } from \"./transport\";\nexport type { CollectionClient, RestoreOptions, TrashClient } from \"./collection\";\nexport type { FindResult, SDKCollectionClient, SDKQueryBuilderInterface, PaginationMeta } from \"@rebasepro/types\";\n\n// Pagination: `iterate()` / `findAll()` parameter types and the error a walk\n// throws instead of quietly returning a truncated answer.\nexport type { IterateParams, FindAllParams, PageWalkOptions, CursorSpec } from \"@rebasepro/types\";\nexport { RebasePaginationError } from \"@rebasepro/common\";\nexport type { PaginationErrorCode } from \"@rebasepro/common\";\n\n// Logical-condition helpers for `.where(or(...), and(...))`.\nexport { or, and, not, cond } from \"@rebasepro/common\";\n\n// Files named inside text — a markdown body's images. The text stores a\n// `rebase-storage:` reference, never a URL whose download token expires;\n// `resolveStorageReferences(text, client)` exchanges each one for a fresh URL\n// when the text is rendered.\nexport {\n resolveStorageReferences,\n storageReference,\n parseStorageReference,\n isStorageReference,\n STORAGE_REFERENCE_SCHEME,\n storageTargetOfDownloadUrl,\n storageReferencesForDownloadUrls,\n storageTargetsInText\n} from \"@rebasepro/types\";\nexport type { StorageReferenceTarget, StorageReferenceResolver } from \"@rebasepro/types\";\n\n// Auth: session/token types, config, and the pluggable storage strategies.\nexport { createCookieStorage, createMemoryStorage } from \"./auth\";\nexport type { AuthConfig, AuthStorage, CookieStorageOptions, CreateAuthOptions, SessionRestoreOutcome } from \"./auth\";\n// `User` is re-exported alongside the session types because `client.auth` hands\n// one back and a browser app installs `@rebasepro/client` only — `@rebasepro/types`\n// is a transitive dependency there, not something a consumer can import from.\nexport type { User, RebaseSession, AuthTokens, AuthChangeEvent, DeviceSession } from \"@rebasepro/types\";\n\n// Control-plane client option/DTO types (the client instance exposes the impls).\nexport type { CreateAdminOptions, ListUsersOptions } from \"./admin\";\nexport type { AdminUser } from \"./admin\";\nexport type { CreateCronOptions } from \"./cron\";\n// `client.backups` answers these; the factory behind it is internal.\nexport type { BackupStream } from \"./backups\";\nexport type {\n ApiKeyKind,\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n CreateApiKeysOptions,\n CreatePersonalKeyRequest,\n CreatePersonalKeysOptions,\n UpdateApiKeyRequest\n} from \"./api-keys\";\nexport type { FunctionInvokeOptions, FunctionsClient } from \"./functions\";\n\n// Realtime: the WebSocket client class is internal to `createRebaseClient()`,\n// but re-exported (see @internal on the class) so a data-source driver can\n// construct it directly. Not a stable app-facing API.\nexport { RebaseWebSocketClient } from \"./websocket\";\nexport { RebaseRealtimeChannel } from \"./realtime-channel\";\nexport type {\n PresenceState,\n PresenceDiff,\n BroadcastEvent,\n ChannelTransport,\n ChannelOptions,\n ChannelHistoryEntry,\n ChannelHistoryResult\n} from \"./realtime-channel\";\n\n// Offline: config, the `client.offline` surface, and the metadata a UI needs\n// to reflect sync state. `isOfflineError` distinguishes \"there was no network\n// and nothing local to answer with\" from a request that genuinely failed.\n// The store contract is public so other environments (React Native/\n// AsyncStorage, Electron, …) can supply their own persistence;\n// `MemoryOfflineStore` is exported for tests and as the reference\n// implementation, while the IndexedDB store is wired automatically in the\n// browser and needs no direct construction.\nexport type { ConflictResolution, OfflineApi, OfflineConfig, OfflineQueueOptions, OfflineStatus } from \"./offline\";\nexport { isOfflineError } from \"./offline-errors\";\nexport type { LiveResult, ObserveByIdOptions, ObserveOptions, RowSnapshotMeta } from \"./collection\";\n// The reader for a fetched row's version. A function rather than a field so the\n// version never enters the generated `Row` type — see `etagOf`.\nexport { etagOf } from \"./collection\";\nexport type { BatchOperation, BatchRef, BatchResult, FieldOperation, UpdateValues, UpsertOptions } from \"@rebasepro/types\";\n// The types a call's own arguments and answers are written in. A browser app\n// installs `@rebasepro/client` alone, so a type it cannot import from here is a\n// type it cannot name: a dynamic filter UI holding a `WhereFilterOp`, a helper\n// taking `WriteOptions`, a wrapper returning an `AggregateResult`.\nexport type {\n WriteOptions,\n WhereFilterOp,\n WhereFor,\n WhereConditionFor,\n WhereValueFor,\n FilterValues,\n FieldPath,\n OrderBySpec,\n LogicalCondition,\n LogicalConditionFor,\n FilterCondition,\n FilterConditionFor,\n AggregateParams,\n AggregateSelect,\n AggregateRow,\n AggregateResult,\n AggregateMeta,\n IncludeSpec,\n IncludeOptions,\n QueryComputedFields,\n RowOf,\n InsertOf,\n UpdateOf,\n StorageSource\n} from \"@rebasepro/types\";\nexport type { OfflineStore, OfflineCacheEntry, OfflineCacheRecord, PendingMutation, MutationRollback } from \"./offline-store\";\nexport { MemoryOfflineStore } from \"./offline-store\";\n\nexport interface CreateRebaseClientOptions extends RebaseClientConfig {\n auth?: CreateAuthOptions;\n admin?: CreateAdminOptions;\n cron?: CreateCronOptions;\n apiKeys?: CreateApiKeysOptions;\n personalKeys?: CreatePersonalKeysOptions;\n /**\n * Declared storage sources for multi-backend support. Server-transport\n * entries are auto-wired into `client.storageRegistry`; `direct` sources\n * are registered app-side (e.g. via a Firebase Storage hook). The default\n * source (`storage`) is always registered under\n * {@link DEFAULT_STORAGE_SOURCE_KEY}.\n */\n storageSources?: StorageSourceDefinition[];\n /**\n * Maps camelCase property names / safe identifiers to the actual\n * collection slugs on the server (e.g. `{ companyMembers: \"company-members\" }`).\n * If provided, the data layer proxy will resolve property accessors to their\n * correct slugs via this map before falling back to automatic snake_casing.\n */\n collections?: Record<string, string>;\n /**\n * Local-first sync for the data layer.\n *\n * `true` enables it with defaults: reads populate a local row database and\n * fall back to it (evaluating filters and sorts locally) when the network\n * is gone, writes made offline apply immediately and replay in order when\n * it returns, and `observe()` becomes a live query that emits from the\n * local database first. A rejected write is rolled back. Pass an\n * {@link OfflineConfig} to control the store, cache sizes, retry backoff,\n * or rejection handling.\n *\n * Local rows and queued writes are partitioned per signed-in user, and\n * shared across tabs. Off by default.\n */\n offline?: boolean | OfflineConfig;\n}\n\n// ─── Typed Data Proxy ────────────────────────────────────────────────────────\n// Adds typed collection accessors when `DB` is provided via the SDK generator.\n\ntype KebabToCamelCase<S extends string> =\n S extends `${infer T}-${infer U}`\n ? `${T}${Capitalize<KebabToCamelCase<U>>}`\n : S;\n\n/** The accessor whose generated entry declares `Slug: S`, or `never`. */\ntype AccessorOfSlug<DB, S extends string> = {\n [K in keyof DB]: DB[K] extends { Slug: S } ? K : never\n}[keyof DB];\n\n// Resolve a generated `Database` entry from a slug literal, or `unknown` when\n// the slug isn't in the schema — the extractors below then fall back to the\n// open row / partial shapes.\n//\n// Through the `Slug` each entry declares: the generator wrote it beside the\n// accessor it chose, so this reads one name rather than deriving the other. A\n// `Database` generated before entries carried `Slug` falls back to the old\n// kebab-only derivation, which is still right for kebab and single-word slugs.\ntype DBEntry<DB, S extends string> =\n [AccessorOfSlug<DB, S>] extends [never]\n ? (KebabToCamelCase<S> extends keyof DB ? DB[KebabToCamelCase<S>] : unknown)\n : DB[AccessorOfSlug<DB, S>];\n\ntype TypedDataLayer<DB> = {\n collection<S extends string>(slug: S): CollectionClient<\n RowOf<DBEntry<DB, S>>,\n InsertOf<DBEntry<DB, S>>,\n UpdateOf<DBEntry<DB, S>>,\n IncludeOf<DBEntry<DB, S>, DB>\n >;\n} & {\n [K in keyof DB]: CollectionClient<RowOf<DB[K]>, InsertOf<DB[K]>, UpdateOf<DB[K]>, IncludeOf<DB[K], DB>>;\n} & (\n // Untyped, every name is a collection: the index signature is the point.\n // Typed, it is the opposite — it was intersected in unconditionally, and\n // its `[slug: string]` made `client.data.nopeCollection` compile on a\n // `createRebaseClient<Database>`, to throw UNKNOWN_COLLECTION (or 404)\n // at runtime. Only the by-slug `collection<M>()` overload is kept.\n string extends keyof DB ? RebaseSdkData : Pick<RebaseSdkData, \"collection\">\n);\n\n/**\n * The return type of `createRebaseClient<DB>()`.\n *\n * This is `RebaseClient` (from `@rebasepro/types`) with all optional\n * capabilities populated and the `data` layer narrowed to provide\n * typed collection accessors when a `DB` schema generic is supplied.\n */\nexport type CreateRebaseClientResult<DB = Record<string, unknown>> = Omit<RebaseClient<DB>, \"data\" | \"email\"> & {\n /**\n * Send this token from now on, over HTTP and on the realtime socket. An\n * open socket is rebuilt to authenticate with it. A token getter, when one\n * is installed, still comes first.\n */\n setToken: (token: string | null) => void;\n /**\n * Ask `getter` for the token each time one is needed — every HTTP request\n * and every time the realtime socket authenticates. This is how a host\n * that signs users in with its own provider (Clerk, Firebase Auth, a\n * session of its own) authenticates the client:\n *\n * ```ts\n * client.setAuthTokenGetter(() => clerk.session?.getToken() ?? Promise.resolve(null));\n * ```\n *\n * It comes before the client's own session and the `token` option, which\n * are used when it answers `null` or throws. An open socket is rebuilt to\n * authenticate through it.\n */\n setAuthTokenGetter: (getter: () => Promise<string | null>) => void;\n /**\n * Answer a refused credential — a 401 over HTTP, an auth error on the\n * socket — with `handler`; resolve `true` to have the request or\n * subscription retried with the token resolved again. Replaces the\n * client's own session refresh.\n */\n setOnUnauthorized: (handler: () => Promise<boolean>) => void;\n /** The token the next request would carry, resolved as it would be. */\n resolveToken: () => Promise<string | null>;\n auth: ReturnType<typeof createAuth>;\n admin: ReturnType<typeof createAdmin>;\n cron: ReturnType<typeof createCron>;\n backups: ReturnType<typeof createBackups>;\n apiKeys: ReturnType<typeof createApiKeys>;\n personalKeys: ReturnType<typeof createPersonalKeys>;\n functions: ReturnType<typeof createFunctionsClient>;\n ws?: RebaseWebSocketClient;\n /**\n * Broadcast and presence channels.\n *\n * Was missing from this type while present on the returned object, which\n * made `client.realtime.channel(...)` a type error and forced every adopter\n * to cast around the feature before they could reach it.\n */\n realtime: {\n /**\n * Join a broadcast/presence channel. Repeated calls with the same name\n * return the same channel object. Throws only when the client was\n * created with `realtime: false`.\n *\n * Pass `{ history: true }` to have the channel replay what it missed on\n * join and on every reconnect, for channels the server retains.\n */\n channel: (name: string, options?: ChannelOptions) => RebaseRealtimeChannel;\n };\n /**\n * Release everything this client holds that can keep a process alive: the\n * realtime socket and its reconnect timer, channel presence heartbeats, the\n * offline manager, and the scheduled token refresh.\n *\n * Each of those keeps the Node event loop alive on its own, so a script\n * that does not call this will not exit — and, until the refresh timer was\n * included, one that *did* call it still would not if it had signed in.\n *\n * Safe when realtime was never started (`realtime: false`), safe when\n * signed out, and safe to call twice. It does not sign the user out: a\n * persisted session survives for the next client to restore.\n */\n close: () => void;\n storage: StorageSource;\n storageRegistry: StorageSourceRegistry;\n createStorageSource: (storageId: string) => StorageSource;\n fetchStorageSources: () => Promise<StorageSourceDefinition[]>;\n /**\n * POST to an arbitrary endpoint on this backend, returning its body\n * verbatim.\n *\n * The shorthand for {@link FunctionsClient.invoke} — `call(\"functions/x\",\n * p)` is `functions.invoke(\"x\", p)` — and it unwraps exactly as little.\n * Neither reaches into the response for a `data` key.\n */\n call: <T = unknown>(endpoint: string, payload?: unknown) => Promise<T>;\n /**\n * A collection by slug — the same client `data.collection(slug)` returns,\n * typed from the generated `Database` when the slug is one of its\n * collections, or by the row type passed explicitly.\n */\n collection: {\n <S extends string>(slug: S): CollectionClient<\n RowOf<DBEntry<DB, S>>,\n InsertOf<DBEntry<DB, S>>,\n UpdateOf<DBEntry<DB, S>>,\n IncludeOf<DBEntry<DB, S>, DB>\n >;\n <M extends Record<string, unknown> = Record<string, unknown>>(slug: string): CollectionClient<M>;\n };\n /**\n * Write across collections in one transaction.\n *\n * `createMany` and friends are one collection at a time, which is the wrong\n * shape for the writes that most need to be atomic: an order and its line\n * items, a user and their membership row, a document and its audit entry.\n * Sent as separate calls those can half-succeed, and the recovery — read\n * back, work out which half landed, undo it — is code nobody writes.\n *\n * Operations run in order, each through the pipeline its single-row\n * equivalent uses: the same validation, callbacks and row-level security,\n * as the same user. All of them land or none do.\n *\n * Name an operation with `ref` and a later one can stand\n * `{ $ref: \"<name>.<field>\" }` wherever a value goes — which is the only\n * way to express \"insert the parent, then point the children at it\" in a\n * single request, since the parent's id does not exist until the server\n * assigns it. Only backward references resolve.\n *\n * `data` is aligned to `operations`: the written row for a create, upsert\n * or update, and `null` for a delete.\n *\n * `collection` is the accessor `client.data.<accessor>` takes — `orderItems`\n * for the slug `order_items` — and is sent as the slug through the\n * `collections` dictionary. A name that is not an accessor goes out as\n * written, so an untyped client can still pass the slug.\n *\n * @example\n * ```ts\n * await client.batch([\n * { op: \"create\", collection: \"orders\", values: { total: 40 }, ref: \"order\" },\n * { op: \"create\", collection: \"orderItems\",\n * values: { orderId: { $ref: \"order.id\" }, sku: \"A-1\" } },\n * { op: \"update\", collection: \"stock\", id: \"A-1\", values: { count: { $inc: -1 } } }\n * ]);\n * ```\n */\n batch: (\n operations: BatchOperation<DB>[],\n options?: WriteOptions\n ) => Promise<BatchResult>;\n data: TypedDataLayer<DB>;\n /** Present only when the client was created with `offline` enabled. */\n offline?: OfflineApi;\n};\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/**\n * Derive a WebSocket URL from an HTTP base URL.\n * `http://` → `ws://`, `https://` → `wss://`.\n *\n * A backend mounted under a path is the reason `baseUrl` accepts one, so the\n * path is kept. It used to be kept for an absolute `baseUrl` and dropped for a\n * relative one — resolved through `.origin` — so one deployment dialled two\n * different sockets depending on whether its config said `\"/backend\"` or\n * `\"https://app.example.com/backend\"`.\n *\n * Returns `\"\"` when there is nothing to resolve against: a relative `baseUrl`\n * outside a browser has no origin, and inventing one would dial somewhere\n * arbitrary. The caller warns rather than leaving that silent.\n */\nfunction deriveWebSocketUrl(baseUrl?: string): string {\n const toWsProtocol = (url: string): string => {\n const secure = /^(https|wss):/i.test(url);\n return url\n .replace(/^https?:\\/\\//i, secure ? \"wss://\" : \"ws://\")\n .replace(/^wss?:\\/\\//i, secure ? \"wss://\" : \"ws://\")\n .replace(/\\/$/, \"\");\n };\n\n if (typeof window !== \"undefined\") {\n let absoluteUrl: string;\n if (!baseUrl) {\n absoluteUrl = window.location.origin;\n } else if (/^https?:\\/\\//i.test(baseUrl) || /^wss?:\\/\\//i.test(baseUrl)) {\n absoluteUrl = baseUrl;\n } else {\n try {\n const resolved = new URL(baseUrl, window.location.href);\n absoluteUrl = resolved.origin + resolved.pathname;\n } catch {\n absoluteUrl = window.location.origin;\n }\n }\n return toWsProtocol(absoluteUrl);\n }\n\n if (!baseUrl) return \"\";\n if (!/^https?:\\/\\//i.test(baseUrl) && !/^wss?:\\/\\//i.test(baseUrl)) {\n return \"\";\n }\n return toWsProtocol(baseUrl);\n}\n\nexport function createRebaseClient<DB = Record<string, unknown>>(options: CreateRebaseClientOptions): CreateRebaseClientResult<DB> {\n // `credentialOutOfBand`: in cookie auth mode the credential is an httpOnly\n // cookie, so a tokenless transport is not an anonymous client and must not\n // trip the server-side anonymous guard (see `RebaseClientConfig.anonymous`).\n const transport = createTransport(options, { credentialOutOfBand: options.auth?.authFlowMode === \"cookie\" });\n const auth = createAuth(transport, options.auth);\n const admin = createAdmin(transport, options.admin);\n const cron = createCron(transport, options.cron);\n const backups = createBackups(transport);\n const apiKeys = createApiKeys(transport, options.apiKeys);\n const personalKeys = createPersonalKeys(transport, options.personalKeys);\n const storage = createStorage(transport);\n const functions = createFunctionsClient(transport);\n\n // Build a server-backed StorageSource for a given storage-source key.\n const createStorageSource = (storageId: string): StorageSource =>\n storageId === DEFAULT_STORAGE_SOURCE_KEY ? storage : createStorage(transport, storageId);\n\n // Storage registry: always holds the default source, plus any declared\n // server-transport sources. `direct` sources are registered app-side.\n const storageRegistry = new ClientStorageSourceRegistry();\n storageRegistry.register(DEFAULT_STORAGE_SOURCE_KEY, storage);\n for (const def of options.storageSources ?? []) {\n if (def.transport === \"server\" && def.key !== DEFAULT_STORAGE_SOURCE_KEY) {\n storageRegistry.register(def.key, createStorageSource(def.key));\n }\n }\n\n // Discover storage sources from the backend, making the server the single\n // source of truth. Server-transport sources are auto-wired into the\n // registry; `direct` sources are returned for the app to register. The\n // promise is cached on success and reset on failure so it can be retried\n // (e.g. once the user authenticates).\n let storageSourcesPromise: Promise<StorageSourceDefinition[]> | undefined;\n const fetchStorageSources = (): Promise<StorageSourceDefinition[]> => {\n if (storageSourcesPromise) return storageSourcesPromise;\n storageSourcesPromise = transport\n .request<{ data: StorageSourceDefinition[] }>(\"/storage/sources\")\n .then((res) => {\n const defs = res.data ?? [];\n for (const def of defs) {\n if (def.transport === \"server\"\n && def.key !== DEFAULT_STORAGE_SOURCE_KEY\n && !storageRegistry.has(def.key)) {\n storageRegistry.register(def.key, createStorageSource(def.key));\n }\n }\n return defs;\n })\n .catch((e) => {\n storageSourcesPromise = undefined; // allow retry\n throw e;\n });\n return storageSourcesPromise;\n };\n\n // Opting out has to happen before the URL is derived: `deriveWebSocketUrl`\n // always produces one, so a truthy check alone can never leave the socket\n // closed.\n const realtimeEnabled = options.realtime !== false;\n const resolvedWsUrl = realtimeEnabled\n ? (options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl))\n : undefined;\n\n // Realtime is on unless it was switched off, so \"on, but no URL could be\n // derived\" is a misconfiguration and not a choice. It used to be silent:\n // the client simply had no socket, `observe()` quietly degraded to a\n // one-shot fetch, and `realtime.channel()` blamed `realtime: false` — an\n // option the caller had not passed.\n const realtimeUnreachable = realtimeEnabled && !resolvedWsUrl;\n const unreachableReason =\n \"no WebSocket URL could be derived from baseUrl \" +\n `${JSON.stringify(options.baseUrl ?? null)} — outside a browser there is no page origin ` +\n \"to resolve a relative URL against. Pass an absolute `baseUrl`, set `websocketUrl` \" +\n \"explicitly, or pass `realtime: false` to say this was intended.\";\n if (realtimeUnreachable) {\n console.warn(\n `[Rebase] Realtime is enabled but ${unreachableReason} ` +\n \"Live queries will fall back to a single fetch and channels will throw.\"\n );\n }\n\n /**\n * What a refused credential is answered with, on HTTP and on the socket:\n * the handler passed as `onUnauthorized` or set with `setOnUnauthorized()`,\n * otherwise the auth module's `handleUnauthorized` — not a bare\n * `refreshSession`, so that a refresh the server rejects outright drops the\n * session and emits SIGNED_OUT instead of leaving every view rendering\n * \"Invalid or expired token\". One variable so that setting it reaches\n * both: a host whose own auth provider can mint a fresh token says so once.\n */\n let onUnauthorized: () => Promise<boolean> = options.onUnauthorized ?? (() => auth.handleUnauthorized());\n transport.setOnUnauthorized(() => onUnauthorized());\n\n let ws: RebaseWebSocketClient | undefined;\n /** One channel object per name — see `realtime.channel`. */\n const realtimeChannels = new Map<string, RebaseRealtimeChannel>();\n if (resolvedWsUrl) {\n ws = new RebaseWebSocketClient({\n websocketUrl: resolvedWsUrl,\n // The socket's token is the one an HTTP request would carry now,\n // resolved by the same function: a token getter installed with\n // `setAuthTokenGetter()`, otherwise the token held — the session's,\n // refreshed first when it is about to expire, one set with\n // `setToken()`, or the `token` option. Resolving it separately is\n // how a host signing users in with its own provider got an\n // authenticated HTTP client and an anonymous socket.\n getAuthToken: () => transport.resolveToken(),\n onUnauthorized: () => onUnauthorized(),\n impersonate: options.impersonate\n });\n\n // The account the socket's server-side state belongs to. The server\n // re-scopes a socket's subscriptions and channel memberships when it\n // authenticates again (`rescopeClient`), but a change of account still\n // rebuilds the socket here: a fresh socket carries nothing the previous\n // account's socket held, so this does not depend on the server alone.\n let realtimeUid: string | null = auth.getSession()?.user?.uid ?? null;\n\n auth.onAuthStateChange((event, session) => {\n if (!ws) return;\n if (event === \"SIGNED_OUT\") {\n realtimeUid = null;\n // Not permanent: the client stays usable, and a later subscribe\n // should reconnect anonymously. It also drops what the socket\n // cached, so none of it is replayed to whoever comes next.\n ws.disconnect();\n } else if (event === \"SIGNED_IN\" || event === \"TOKEN_REFRESHED\") {\n const uid = session?.user?.uid ?? null;\n if (uid !== realtimeUid) {\n // A different account — signed in over a session (which\n // emits no SIGNED_OUT), or a visitor signing in. Rebuild\n // the socket rather than re-authenticate it: a new socket\n // starts with nothing on the server, authenticates as this\n // account on open, and re-sends every live subscription and\n // channel as it. Only when there was a socket, for the same\n // reason as below.\n realtimeUid = uid;\n const hadSocket = ws.hasSocket;\n ws.disconnect();\n if (hadSocket) ws.ensureConnected();\n return;\n }\n // Only re-authenticate a socket that already exists. Signing in\n // is not a request for realtime, and dialling here would undo\n // lazy connect for every app with a login. A socket opened\n // later authenticates itself from `getAuthToken` on open.\n //\n // With the token HTTP would now send: the session's, unless a\n // token getter is installed, which HTTP asks first.\n if (session?.accessToken && ws.hasSocket) {\n const socket = ws;\n transport.resolveToken()\n .then((token) => (token && socket.hasSocket ? socket.authenticate(token) : undefined))\n .catch(console.warn);\n }\n }\n });\n }\n\n /**\n * Suggest the closest known collection key for a mistyped accessor.\n * Uses edit-distance-1 and prefix matching — no external dependency.\n */\n function suggestCollection(prop: string, knownKeys: string[]): string | undefined {\n // Prefix match (e.g. \"prod\" → \"products\")\n const prefixMatch = knownKeys.find(k => k.startsWith(prop) || prop.startsWith(k));\n if (prefixMatch) return prefixMatch;\n\n // Edit-distance-1: deletions, insertions, substitutions, transpositions\n for (const key of knownKeys) {\n if (Math.abs(key.length - prop.length) > 1) continue;\n let diffs = 0;\n const longer = key.length >= prop.length ? key : prop;\n const shorter = key.length >= prop.length ? prop : key;\n if (longer.length === shorter.length) {\n // Same length: allow 1 substitution or 1 transposition\n for (let i = 0; i < longer.length; i++) {\n if (longer[i] !== shorter[i]) {\n // Check for transposition\n if (\n i + 1 < longer.length &&\n longer[i] === shorter[i + 1] &&\n longer[i + 1] === shorter[i]\n ) {\n diffs++;\n i++; // skip next char (already accounted for)\n if (diffs > 1) break;\n continue;\n }\n diffs++;\n }\n if (diffs > 1) break;\n }\n } else {\n // Length differs by 1: allow 1 insertion/deletion\n let li = 0;\n let si = 0;\n while (li < longer.length) {\n if (si < shorter.length && longer[li] === shorter[si]) {\n si++;\n } else {\n diffs++;\n }\n li++;\n if (diffs > 1) break;\n }\n }\n if (diffs <= 1) return key;\n }\n\n return undefined;\n }\n\n // Offline layer: wraps every collection client with a read cache and a\n // write queue. Replay goes through *unwrapped* clients (the factory below)\n // so a failing replay can never re-queue itself.\n const offlineManager = options.offline\n ? new LazyOfflineManager(\n typeof options.offline === \"object\" ? options.offline : {},\n (slug) => createCollectionClient(transport, slug)\n )\n : undefined;\n\n if (offlineManager) {\n // Cache and queue are partitioned per user: cached rows are RLS-scoped\n // to whoever fetched them, and queued writes must replay as the user\n // who made them — a shared browser must never mix the two.\n //\n // Who that is, is not known until auth has restored the session or\n // found there is none. In cookie mode nothing is stored on the device,\n // so that takes the refresh request made on load; an expired stored\n // token takes one too, and may turn out to be refused. Until then the\n // manager has no scope, and everything offline waits. Reading the\n // missing session as \"signed out\" would queue the user's own writes\n // as the signed-out user's, which no sign-in replays; reading the\n // stored one as the answer would keep a refused session's user in\n // scope for a client that is signed out.\n offlineManager.holdScope();\n auth.onAuthStateChange((event, session) => {\n offlineManager.setScope(event === \"SIGNED_OUT\" ? undefined : session?.user?.uid);\n });\n void auth.isInitialized().then(() => {\n const uid = auth.getSession()?.user?.uid;\n // No session, and only because the server could not be asked:\n // with `keepLastUser`, the last user's data rather than the\n // signed-out user's. Never when the server answered.\n if (!uid && auth.restoreOutcome() === \"unreachable\" && offlineManager.keepsLastUser) {\n void offlineManager.resumeLastUser();\n return;\n }\n offlineManager.setScope(uid);\n });\n if (offlineManager.keepsLastUser) {\n // Once the connection is back, ask again — the restore on load\n // could not. Success announces the session, which confirms the\n // user (or switches to whoever it really is); a refusal means\n // there is no session, and the device is signed out.\n let confirming = false;\n offlineManager.api.onStatusChange((status) => {\n if (!status.online || !status.unverifiedUser || confirming || auth.getSession()) return;\n confirming = true;\n auth.refreshSession().catch((error: unknown) => {\n const refused = error instanceof RebaseApiError\n && error.status !== undefined && error.status >= 400 && error.status < 500;\n if (refused && !auth.getSession()) offlineManager.setScope(undefined);\n }).finally(() => {\n confirming = false;\n });\n });\n }\n }\n\n /**\n * `setToken()` or `setAuthTokenGetter()` changed the credential HTTP\n * sends, and a socket may be open as whoever the previous one named.\n *\n * Rebuilt rather than re-authenticated, as on a change of account: a raw\n * token does not say whose it is, and a new socket carries nothing the\n * previous identity held — on the server or in this client's cache. It\n * authenticates on open with the token HTTP now sends and re-sends every\n * live subscription and channel as it. With no socket there is nothing to\n * do: one opened later authenticates itself.\n */\n function credentialChanged(): void {\n if (!ws?.hasSocket) return;\n ws.disconnect();\n ws.ensureConnected();\n }\n\n const collectionClients = new Map<string, CollectionClient<Record<string, unknown>>>();\n let untypedWarned = false;\n\n function collection(slug: string): CollectionClient<Record<string, unknown>> {\n if (!collectionClients.has(slug)) {\n const inner = createCollectionClient(transport, slug, ws);\n collectionClients.set(slug, offlineManager ? offlineManager.wrap(slug, inner) : inner);\n }\n return collectionClients.get(slug)!;\n }\n\n const dataTarget = { collection } as Record<string, unknown>;\n\n /**\n * Names the proxy never serves as a collection: the ones promise and\n * serialisation machinery probe for (`then`, `toJSON`, React's\n * `$$typeof`), and everything a plain object inherits — so\n * `client.data.toString` is `Object.prototype.toString`, not a collection\n * client, and `String(client.data)` is `\"[object Object]\"` rather than a\n * throw. `prop in collections` used to walk the prototype too, which is how\n * `toString` and `constructor` became \"collections\" under a dictionary.\n */\n const isProbedName = (prop: string) => prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\";\n const isReservedName = (prop: string) => isProbedName(prop) || prop in Object.prototype;\n\n const dataProxy = new Proxy(dataTarget, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") {\n return collection;\n }\n if (typeof prop === \"symbol\") return undefined;\n if (isProbedName(prop)) return undefined;\n if (options.collections && Object.hasOwn(options.collections, prop)) {\n return collection(options.collections[prop]);\n }\n if (prop in Object.prototype) return Reflect.get(_target, prop);\n if (options.collections) {\n // Strict mode: the developer supplied a typed dictionary,\n // so we know the full set of valid accessors.\n const knownKeys = Object.keys(options.collections);\n const suggestion = suggestCollection(prop, knownKeys);\n const knownList = knownKeys.join(\", \");\n let msg = `Unknown collection accessor \"${prop}\". Known collections: ${knownList}.`;\n if (suggestion) msg += ` Did you mean \"${suggestion}\"?`;\n msg += ` Use data.collection(\"<slug>\") for dynamic slugs.`;\n throw new RebaseClientError(msg, {\n code: \"UNKNOWN_COLLECTION\",\n details: { accessor: prop, known: knownKeys }\n });\n }\n // Untyped fallback: convert camelCase property names to snake_case slugs.\n // e.g. `companyMembers` → `company_members`\n if (!untypedWarned) {\n untypedWarned = true;\n console.warn(\n `[Rebase] Untyped data access detected (client.data.${prop}). ` +\n `Collection names are resolved via snake_case conversion, which may cause silent 404s at request time. ` +\n `Pass a \\`collections\\` dictionary to createRebaseClient() or use the generated SDK for type-safe access.`\n );\n }\n const slug = toSnakeCase(prop);\n return collection(slug);\n },\n // `\"posts\" in client.data` answers what `get` serves: with a\n // dictionary, its own accessors; without one, any name that is not\n // reserved, since every such name reaches a collection by its slug.\n has(_target, prop: string | symbol) {\n if (prop === \"collection\") return true;\n if (typeof prop === \"symbol\") return false;\n if (isProbedName(prop)) return false;\n if (options.collections) return Object.hasOwn(options.collections, prop);\n return !isReservedName(prop);\n }\n });\n\n const target = {\n auth,\n admin,\n cron,\n backups,\n apiKeys,\n personalKeys,\n functions,\n storage,\n storageRegistry,\n createStorageSource,\n fetchStorageSources,\n ws,\n realtime: {\n /**\n * Join a broadcast/presence channel.\n *\n * Repeated calls with the same name return the same channel, so\n * separate components can attach handlers without each opening its\n * own membership — and `leave()` from one would otherwise silently\n * cut off the others.\n */\n channel: (name: string, options?: ChannelOptions): RebaseRealtimeChannel => {\n // Being merely *unconnected* is not an error: the socket opens\n // on the first channel operation, which is the whole point of\n // asking for a channel before you use one. Having no socket at\n // all is, and there are two reasons for it — say which.\n if (!ws) {\n throw new RebaseClientError(\n realtimeUnreachable\n ? `Realtime is enabled but ${unreachableReason}`\n : \"Realtime is disabled on this client (realtime: false), so channels are unavailable.\",\n { code: \"REALTIME_DISABLED\" }\n );\n }\n let existing = realtimeChannels.get(name);\n if (!existing) {\n existing = new RebaseRealtimeChannel(name, ws, options);\n realtimeChannels.set(name, existing);\n } else if (options?.history) {\n // Same object by name, so options on a later call have no\n // new channel to apply to. Asking for history upgrades the\n // one that exists rather than being quietly ignored — but\n // never the reverse, so a caller that omits the option\n // cannot switch it off under one that asked for it.\n existing.enableHistory();\n }\n return existing;\n }\n },\n /**\n * Release every handle that can keep a process alive — see the\n * `close` docblock on the client interface.\n *\n * Safe to call when realtime was never started, safe when signed out,\n * and safe to call twice.\n */\n close: () => {\n // Channels hold presence heartbeat timers, which would otherwise\n // keep firing (and keep a Node process alive) after the socket\n // they publish over is gone.\n for (const channel of realtimeChannels.values()) void channel.leave();\n realtimeChannels.clear();\n // Permanent: nothing queued afterwards may redial and keep the\n // event loop alive, which is the reason this method exists.\n ws?.disconnect(true);\n // The offline retry timer is unref'd but the `online` listener is\n // not, and neither should outlive the client.\n offlineManager?.dispose();\n // The scheduled token refresh is a plain setTimeout up to a token\n // lifetime away, and not unref'd — so on Node it holds the event\n // loop open all by itself. Without this, closing a SIGNED-IN client\n // released the socket and the process still never exited, which is\n // the opposite of what this method exists to guarantee.\n auth.stopAutoRefresh();\n },\n setToken: (token: string | null) => {\n transport.setToken(token);\n credentialChanged();\n },\n setAuthTokenGetter: (getter: () => Promise<string | null>) => {\n transport.setAuthTokenGetter(getter);\n credentialChanged();\n },\n setOnUnauthorized: (handler: () => Promise<boolean>) => {\n onUnauthorized = handler;\n },\n resolveToken: transport.resolveToken,\n baseUrl: transport.baseUrl,\n apiPath: transport.apiPath,\n collection,\n call: async <T = unknown>(endpoint: string, payload?: unknown): Promise<T> => {\n const prefix = endpoint.startsWith(\"/\") ? \"\" : \"/\";\n // The body, verbatim — the same thing `functions.invoke()` returns.\n //\n // This used to end `return res.data ?? (res as T)`, unwrapping a\n // top-level `data` key when it saw one. The docs present `call()`\n // as a shorthand for `invoke()`, which does not, so the two\n // returned *different values* for the one function that happens to\n // answer `{ data: … }` — and silently, since the unwrap only fires\n // on the shape it fires on. A caller who wants `.data` can write\n // it; a caller whose function legitimately returns a `data` field\n // could not get it back at all.\n //\n // For the same reason the body goes out whenever there is one, as\n // `invoke()` sends it: `false`, `0`, `\"\"` and `null` are all bodies\n // a function can switch on, and a truthiness test sent none.\n return transport.request<T>(`${prefix}${endpoint}`, {\n method: \"POST\",\n body: payload !== undefined ? JSON.stringify(payload) : undefined\n });\n },\n batch: async (operations: unknown[], batchOptions?: WriteOptions): Promise<BatchResult> => {\n if (!Array.isArray(operations)) {\n throw invalidBulkBody(\"batch expects an array of operations.\");\n }\n // Answered locally rather than sent: an empty transaction is a\n // round trip that cannot do anything, and the server would have to\n // answer this same shape anyway.\n if (operations.length === 0) return { data: [], meta: { operations: 0 } };\n\n const headers: Record<string, string> = {};\n if (batchOptions?.idempotencyKey) headers[\"Idempotency-Key\"] = batchOptions.idempotencyKey;\n if (batchOptions?.returning === false) headers.Prefer = \"return=minimal\";\n\n // `collection` is typed as the generated accessor (`orderItems`),\n // the name `client.data.<accessor>` takes; the route resolves it by\n // slug (`order_items`). The dictionary is the one place that knows\n // both, so it is resolved here — and a name that is not an accessor\n // goes out as written, so a slug still works.\n const dictionary = options.collections;\n const wire = dictionary\n ? operations.map((operation) => {\n if (!operation || typeof operation !== \"object\") return operation;\n const name = (operation as { collection?: unknown }).collection;\n return typeof name === \"string\" && Object.hasOwn(dictionary, name)\n ? { ...operation, collection: dictionary[name] }\n : operation;\n })\n : operations;\n\n return transport.request<BatchResult>(\"/data/_batch\", {\n method: \"POST\",\n body: JSON.stringify({ operations: wire }),\n ...(Object.keys(headers).length > 0 ? { headers } : {})\n });\n },\n data: dataProxy,\n ...(offlineManager ? { offline: offlineManager.api } : {}),\n } as unknown as CreateRebaseClientResult<DB>;\n\n return target;\n}\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","names":[],"sources":["../src/auth.ts","../src/admin.ts","../src/cron.ts","../src/backups.ts","../src/api-keys.ts","../src/functions.ts","../src/storage.ts","../src/storage-registry.ts","../src/sql-script.ts","../src/websocket.ts","../src/realtime-channel.ts","../src/offline-lazy.ts","../src/index.ts"],"sourcesContent":["import { RebaseApiError, Transport } from \"./transport\";\nimport { RebaseClientError } from \"@rebasepro/types\";\nimport type { AuthConfigDocument, AuthChangeEvent, RebaseSession, AuthTokens, DeviceSession, User } from \"@rebasepro/types\";\n\n// Re-export canonical types so `import { RebaseSession } from \"@rebasepro/client\"` keeps working\nexport type { RebaseSession, AuthTokens, AuthChangeEvent, DeviceSession } from \"@rebasepro/types\";\n\n/** Minimal, non-sensitive user profile returned by {@link findUserByEmail}. */\nexport interface PublicUserProfile {\n uid: string;\n displayName: string | null;\n photoURL: string | null;\n}\n\n/** Map a raw user object from an auth response (`/login`, `/refresh`, `/me`) to a `User`. */\nfunction mapRawUser(raw: Record<string, unknown>): User {\n return {\n uid: raw.uid as string,\n email: (raw.email as string | null) ?? null,\n displayName: (raw.displayName as string | null) ?? null,\n photoURL: (raw.photoURL as string | null) ?? null,\n providerId: (raw.providerId as string | undefined) ?? \"password\",\n isAnonymous: (raw.isAnonymous as boolean | undefined) ?? false,\n emailVerified: raw.emailVerified as boolean | undefined,\n ...(raw.pendingEmail !== undefined ? { pendingEmail: raw.pendingEmail as string | null } : {}),\n roles: raw.roles as string[] | undefined,\n metadata: raw.metadata as Record<string, unknown> | undefined,\n };\n}\n\n/** Placeholder user, used only as a last resort when none can be resolved. */\nconst EMPTY_USER: User = { uid: \"\", email: null, displayName: null, photoURL: null, providerId: \"password\", isAnonymous: false };\n\n\n/**\n * What `GET /auth/config` answers: the backend's auth capability document, and\n * whether it serves storage.\n *\n * Declared once, in `@rebasepro/types`, and re-exported here under the name the\n * endpoint uses. The SDK used to carry its own near-copy of it, which listed an\n * `emailServiceEnabled` flag no backend has ever sent and omitted half of what\n * every backend does send.\n */\nexport type AuthConfig = AuthConfigDocument;\n\nexport interface AuthStorage {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n}\n\nexport function createMemoryStorage(): AuthStorage {\n const store: Record<string, string> = {};\n return {\n getItem(key) { return store[key] ?? null; },\n setItem(key, value) { store[key] = value; },\n removeItem(key) { delete store[key]; }\n };\n}\n\nfunction detectStorage(): AuthStorage {\n try {\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(\"__rebase_test__\", \"1\");\n localStorage.removeItem(\"__rebase_test__\");\n return localStorage;\n }\n } catch (e) { /* ignore */ }\n return createMemoryStorage();\n}\n\nexport interface CreateAuthOptions {\n storage?: AuthStorage;\n authPath?: string;\n autoRefresh?: boolean;\n persistSession?: boolean;\n /**\n * Authentication flow mode.\n * - 'json' (default): Tokens are sent/received in JSON bodies. Refresh token is stored in local storage.\n * - 'cookie': Refresh token is sent/received via httpOnly cookies. Access token remains in memory.\n */\n authFlowMode?: \"json\" | \"cookie\";\n}\n\n/**\n * Finishing a sign-in that the server refused with `MFA_REQUIRED`.\n *\n * That refusal carries `details.mfaToken` — proof the first factor was\n * accepted, good for five minutes and for nothing but the two challenge routes\n * — and `details.factors`, the factors the account can answer with.\n */\nexport interface MfaPendingOptions {\n /** `details.mfaToken` of the `MFA_REQUIRED` refusal. */\n mfaToken?: string;\n}\n\nexport function createAuth(transport: Transport, options?: CreateAuthOptions) {\n const opts = options || {};\n const storage = opts.storage || detectStorage();\n const authPath = opts.authPath || \"/auth\";\n const autoRefresh = opts.autoRefresh !== false;\n const persistSession = opts.persistSession !== false;\n const authFlowMode = opts.authFlowMode || \"json\";\n\n const STORAGE_KEY = \"rebase_auth\";\n const REFRESH_BUFFER_MS = 120000;\n /**\n * The largest delay `setTimeout` can hold — 2^31 - 1 ms, about 24.8 days.\n * Anything larger is silently clamped to 1ms by Node and every browser.\n */\n const MAX_TIMER_DELAY_MS = 2_147_483_647;\n // Auto-refresh resilience: retry transient failures with exponential backoff\n // (1s, 2s, 4s, … capped) before giving up.\n const MAX_REFRESH_RETRIES = 5;\n const REFRESH_RETRY_BASE_MS = 1000;\n const REFRESH_RETRY_MAX_MS = 30000;\n\n let currentSession: RebaseSession | null = null;\n /**\n * Which sign-in `currentSession` belongs to. Bumped whenever the session\n * is replaced by anything other than its own refresh — a sign-in, a\n * sign-out, a revocation, a local abandon — so a refresh already in flight\n * can tell, when its answer lands, that the session it was refreshing has\n * ended. Its answer is then dropped: adopting it put a signed-out session\n * back in memory, in storage and on the transport, announced\n * TOKEN_REFRESHED after SIGNED_OUT, and overwrote whoever had signed in\n * since.\n */\n let sessionEpoch = 0;\n const listeners = new Set<(event: AuthChangeEvent, session: RebaseSession | null) => void>();\n let refreshTimeout: ReturnType<typeof setTimeout> | null = null;\n // De-dupe concurrent refreshes. On boot (esp. cookie mode + React StrictMode)\n // multiple callers can trigger refresh at once; without this they race — the\n // server rotates the refresh token twice and the browser can end up with a\n // cookie the DB no longer matches. A single in-flight promise is shared.\n let inFlightRefresh: Promise<RebaseSession> | null = null;\n /** The `sessionEpoch` the in-flight refresh was started for. */\n let inFlightEpoch = -1;\n let resolveInitialized: (value: void | PromiseLike<void>) => void;\n const isInitialized = new Promise<void>((resolve) => {\n resolveInitialized = resolve;\n });\n /** See {@link SessionRestoreOutcome}. Set once, before `isInitialized` settles. */\n let restoreOutcome: SessionRestoreOutcome = \"pending\";\n /** Settle the load-time restore, saying how it ended. */\n function settleRestore(outcome: Exclude<SessionRestoreOutcome, \"pending\">) {\n if (restoreOutcome === \"pending\") restoreOutcome = outcome;\n resolveInitialized!();\n }\n\n function authUrl(endpoint: string) {\n return transport.baseUrl + transport.apiPath + authPath + endpoint;\n }\n\n function getFetch() {\n return transport.fetchFn || globalThis.fetch;\n }\n\n /**\n * The body of a request that presents the refresh token — `/refresh` and\n * `/logout`.\n *\n * Empty in cookie mode. The token is in an HttpOnly cookie the browser\n * attaches, and this client never holds it: what it holds is the `\"\"` the\n * server answers in its place. Sending that back was sending a blank token\n * beside the cookie, and a server that refused the blank (0.10.0 through\n * 0.23, with 400 INVALID_INPUT) never read the cookie — so no tab that outlived\n * its access token could renew it, while a reload, which had no session in\n * memory and so sent no field, could.\n */\n function refreshTokenBody(): string {\n return JSON.stringify(authFlowMode === \"cookie\" ? {} : { refreshToken: currentSession?.refreshToken });\n }\n\n /**\n * The refresh token a new session keeps, given the one an answer carried.\n *\n * In cookie mode, none: the server answers `refreshToken: \"\"` because the\n * cookie holds it, and that `\"\"` is \"not held here\", not a token to carry\n * forward or present. Otherwise the answer's token, or — from a server\n * that omits it on a rotation it did not make — the one already held.\n */\n function heldRefreshToken(received: string | undefined): string {\n if (authFlowMode === \"cookie\") return \"\";\n return received || currentSession?.refreshToken || \"\";\n }\n\n /**\n * Whether the server refused a refresh as malformed: a 400, which says the\n * request this client built is not one that server accepts.\n *\n * That is a client/server contract mismatch, not a blip. The identical\n * request is refused identically however often it is sent, so retrying it\n * on a backoff only hides the error behind six identical ones — which is\n * how a cookie-mode tab, refused this way after every wake from sleep,\n * quietly ended on its login screen.\n */\n function isMalformedRefreshError(err: unknown): boolean {\n return err instanceof RebaseApiError && err.status === 400;\n }\n\n type ErrorBody = { error?: { message?: string; code?: string; details?: unknown }; message?: string; code?: string; details?: unknown } | undefined;\n\n function apiError(status: number, body: ErrorBody, statusText: string): RebaseApiError {\n return new RebaseApiError(\n body?.error?.message || body?.message || statusText,\n {\n status,\n code: body?.error?.code || body?.code,\n details: body?.error?.details || body?.details\n }\n );\n }\n\n function throwApiError(status: number, body: ErrorBody, statusText: string): never {\n throw apiError(status, body, statusText);\n }\n\n function emit(event: AuthChangeEvent, session: RebaseSession | null) {\n for (const fn of listeners) {\n try {\n fn(event, session);\n } catch (e) {\n // Isolated so one bad handler cannot stop the rest being told —\n // but reported, because the throw came from the caller's own\n // code and discarding it made a broken `onAuthStateChange`\n // handler look like an event that never fired. The socket in\n // this package already reports handler errors this way.\n console.error(\"Error in auth state change listener:\", e);\n }\n }\n }\n\n function saveSession(session: RebaseSession) {\n if (!persistSession || authFlowMode === \"cookie\") return;\n try {\n storage.setItem(STORAGE_KEY, JSON.stringify(session));\n } catch (e) { /* ignore */ }\n }\n\n function clearStoredSession() {\n try {\n storage.removeItem(STORAGE_KEY);\n } catch (e) { /* ignore */ }\n }\n\n function loadStoredSession(): RebaseSession | null {\n try {\n const raw = storage.getItem(STORAGE_KEY);\n if (raw) return JSON.parse(raw) as RebaseSession;\n } catch (e) { /* ignore */ }\n return null;\n }\n\n /**\n * A refresh failure is only fatal if the refresh token itself is rejected\n * (expired / invalid / forbidden). Network blips, timeouts, and 5xx (e.g. a\n * backend restart mid-session) are transient and must NOT log the user out.\n */\n function isFatalRefreshError(err: unknown): boolean {\n if (!(err instanceof RebaseApiError)) return false; // network/other → transient\n // Another tab (or a retry of our own request) rotated the token we\n // were holding. In cookie mode the jar may ALREADY contain the\n // replacement, so this is the one 401 that is worth retrying: giving\n // up here is precisely the bug where opening a second tab signs you\n // out of both.\n if (err.code === \"TOKEN_ALREADY_USED\") return false;\n if (err.code === \"INVALID_TOKEN\" || err.code === \"TOKEN_EXPIRED\") return true;\n // 401/403 are auth failures; other statuses (incl. 5xx, 0) are transient.\n return err.status === 401 || err.status === 403;\n }\n\n /**\n * Drop this client's session without telling the server.\n *\n * `signOut()` is a user action: it POSTs /logout, which revokes the whole\n * sign-in. That is the wrong hammer for a refresh that failed. Our token\n * may be stale precisely because a sibling tab holds a live one, and\n * logging out on its behalf would turn one tab's bad luck into everybody\n * being signed out — the exact failure this work exists to remove.\n *\n * Storage is left alone when it no longer holds the session being\n * abandoned. It is shared with every other tab of the app, so a different\n * session there is a sibling's live one — a rotation this tab missed, or\n * another sign-in — and deleting it signed that tab out on its next load.\n */\n function abandonSessionLocally() {\n endSession(storageHoldsCurrentSession());\n }\n\n /** Whether storage holds this client's session — or no session at all. */\n function storageHoldsCurrentSession(): boolean {\n const persisted = loadStoredSession();\n return !persisted || !currentSession || persisted.refreshToken === currentSession.refreshToken;\n }\n\n /** Drop the session from memory, storage and the transport, and say so. */\n function endSession(clearStorage = true) {\n sessionEpoch++;\n currentSession = null;\n if (clearStorage) clearStoredSession();\n if (refreshTimeout) {\n clearTimeout(refreshTimeout);\n refreshTimeout = null;\n }\n transport.setToken(null);\n emit(\"SIGNED_OUT\", null);\n }\n\n /**\n * Recover from a 401 on an ordinary API request.\n *\n * Returns `true` when the caller should retry — we minted a fresh access\n * token. When the refresh is rejected *fatally* (the refresh token itself\n * is invalid, expired or revoked) this client can no longer act as the\n * user at all, so we drop the session and emit `SIGNED_OUT`. UIs gate on\n * that event, so they show their login screen instead of leaving the user\n * staring at \"Invalid or expired token\" on every view.\n *\n * Transient failures (offline, 5xx, backend restarting) keep the session:\n * the scheduled refresh backs off and retries, and the token is very\n * likely still good once the backend answers again.\n */\n async function handleUnauthorized(): Promise<boolean> {\n // No session to recover: the 401 is just an anonymous caller hitting a\n // protected route. Emitting SIGNED_OUT here would fire sign-out\n // handlers for a user who was never signed in.\n if (!currentSession) return false;\n\n // Nothing to refresh *with* — the access token is dead and there is no\n // way back. Same end state as a rejected refresh token.\n if (authFlowMode !== \"cookie\" && !currentSession.refreshToken) {\n abandonSessionLocally();\n return false;\n }\n\n const epoch = sessionEpoch;\n try {\n await refreshSession();\n // Retry only as the session the request was refused for. If it\n // ended while the refresh was in flight, re-sending the request\n // would send it as whoever signed in since — or as nobody.\n return epoch === sessionEpoch;\n } catch (err) {\n if (epoch === sessionEpoch && isFatalRefreshError(err)) {\n abandonSessionLocally();\n }\n return false;\n }\n }\n\n /**\n * Requests the refresh itself is making — the `/me` read when its answer\n * carries no user. They go out with the token the refresh has just put on\n * the transport, and must not wait for the refresh they are part of.\n */\n let refreshOwnRequests = 0;\n\n /** The cookie-mode restore started on load, until it settles. */\n let cookieRestore: Promise<void> | null = null;\n\n /**\n * How close to expiry a token is treated as spent by a request about to\n * carry it — the margin the socket's token check uses. Well inside the\n * scheduled refresh's own lead ({@link REFRESH_BUFFER_MS}), so while that\n * timer runs a request never waits for it.\n */\n const SPENT_TOKEN_MARGIN_MS = 10_000;\n\n /**\n * The transport's {@link TokenRefresher}: whether a request should wait for\n * a fresher token than the one held, and which token to send when it does.\n *\n * It waits when this client holds a session whose access token has expired\n * (or is about to) and that can be refreshed. That is a session restored\n * from storage with an expired token, whose refresh started at load and\n * left the transport holding no token at all until it landed — so a request\n * made meanwhile went out anonymous and, on a route with a public read\n * policy, was answered with public rows instead of a 401. And it is a\n * session whose refresh timer never fired — a laptop asleep past\n * `expiresAt`, a frozen tab restored — where every request went out with a\n * dead token, came back 401, and was sent again: twice the requests and a\n * round trip of latency each.\n *\n * The refresh is {@link refreshSession}, so a burst of requests shares one\n * (the one started at load included). A refresh that fails hands back the\n * token the request would have carried anyway: the 401 path then does\n * exactly what it always did. One whose session ended while it ran hands\n * back nothing, so the transport sends what it holds now — no token after\n * a sign-out, the new account's after a sign-in — and never the token of\n * the account that has gone. The realtime socket authenticates through\n * this same path.\n */\n function tokenForRequest(): Promise<string | undefined> | undefined {\n if (refreshOwnRequests > 0) return undefined;\n const session = currentSession;\n // Cookie mode restores on load with no session in memory at all — the\n // refresh token is a cookie this page cannot read — so a request made\n // before that restore answers would go out anonymous. It waits for the\n // answer and then carries whatever token the transport holds: the\n // restored session's, or none when there was nothing to restore.\n if (!session) return cookieRestore?.then(() => undefined);\n // An expiry nobody stated is not one that has passed (see\n // `scheduleRefresh`): read as spent, it refreshed before every request.\n if (!Number.isFinite(session.expiresAt)) return undefined;\n if (session.expiresAt > Date.now() + SPENT_TOKEN_MARGIN_MS) return undefined;\n if (authFlowMode !== \"cookie\" && !session.refreshToken) return undefined;\n const epoch = sessionEpoch;\n return refreshSession().then(\n (fresh) => (epoch === sessionEpoch ? fresh.accessToken : undefined),\n () => (epoch === sessionEpoch ? session.accessToken : undefined)\n );\n }\n transport.setTokenRefresher?.(tokenForRequest);\n\n async function attemptScheduledRefresh(attempt: number) {\n const epoch = sessionEpoch;\n try {\n await refreshSession();\n // On success, refreshSession() re-schedules the next refresh itself.\n } catch (err) {\n // The session this was refreshing has ended — signed out, or\n // replaced by a sign-in that schedules its own. Retrying would\n // refresh nothing, and giving up would sign out a second time.\n if (epoch !== sessionEpoch) return;\n if (isFatalRefreshError(err)) {\n abandonSessionLocally();\n return;\n }\n // Refused as malformed, and already reported by `requestRefresh`.\n // Sending it again gets the same answer. The token was never\n // refused, so the session stays, as it does when retries run out.\n if (isMalformedRefreshError(err)) return;\n if (attempt >= MAX_REFRESH_RETRIES) {\n // Out of retries. A token the server kept refusing — as\n // already used, the one refusal retried at all — is as dead as\n // one it rejected outright. A server that could not be reached,\n // or kept failing, has said nothing about the token: the\n // session stays, and the next request it refuses tries again.\n if (err instanceof RebaseApiError && (err.status === 401 || err.status === 403)) {\n abandonSessionLocally();\n }\n return;\n }\n // Transient failure — back off and retry rather than dropping the session.\n scheduleRetry(attempt);\n }\n }\n\n /** Try the scheduled refresh again after a backoff: 1s, 2s, 4s, … capped. */\n function scheduleRetry(attempt: number) {\n const backoff = Math.min(REFRESH_RETRY_BASE_MS * 2 ** attempt, REFRESH_RETRY_MAX_MS);\n refreshTimeout = setTimeout(() => { void attemptScheduledRefresh(attempt + 1); }, backoff);\n }\n\n function scheduleRefresh(expiresAt: number) {\n if (refreshTimeout) clearTimeout(refreshTimeout);\n if (!autoRefresh) return;\n // No stated expiry — a backend that left `accessTokenExpiresAt` out.\n // `delay` would be NaN, which `setTimeout` runs at once, and every\n // refresh would re-arm the next with no gap. Nothing to schedule: the\n // token is used until the server refuses it, and the 401 path\n // refreshes it then.\n if (!Number.isFinite(expiresAt)) return;\n\n const delay = (expiresAt - REFRESH_BUFFER_MS) - Date.now();\n\n if (delay <= 0) {\n void attemptScheduledRefresh(0);\n return;\n }\n\n // `setTimeout` holds its delay in a 32-bit signed integer. Past\n // ~24.8 days it does not wait — it clamps to 1ms and fires at once. The\n // refresh would then land, receive a token expiring just as far out,\n // schedule again and overflow again: a hot loop against\n // `/auth/refresh`, one per open tab.\n //\n // `auth.accessExpiresIn` is configurable and defaults to \"1h\", so this\n // is dormant on a default deployment and immediate on `\"30d\"` — an\n // ordinary setting for an internal tool. Re-arm instead of refreshing:\n // sleep the maximum, then work out again how long is left.\n if (delay > MAX_TIMER_DELAY_MS) {\n refreshTimeout = setTimeout(() => scheduleRefresh(expiresAt), MAX_TIMER_DELAY_MS);\n return;\n }\n\n refreshTimeout = setTimeout(() => { void attemptScheduledRefresh(0); }, delay);\n }\n\n /**\n * Stop the scheduled token refresh, leaving the session itself alone.\n *\n * This is teardown, not sign-out. `scheduleRefresh` arms an ordinary\n * `setTimeout` up to a token lifetime away, and it is not `unref`'d — so on\n * Node it holds the event loop open by itself. `client.close()` promised\n * that \"a script that does not call this will not exit on its own\", which\n * was true, while the converse it plainly implies was not: a signed-in\n * client that closed its socket still hung, because this timer outlived it.\n * Any script, cron handler or job that signs in hit that.\n *\n * Deliberately does NOT clear the session, touch storage, or emit\n * SIGNED_OUT. Closing a client is not the user signing out — `signOut()`\n * POSTs /logout and revokes the whole sign-in, which is the wrong hammer\n * (see `abandonSessionLocally`) — and a persisted session must still be\n * there for the next client to restore.\n */\n function stopAutoRefresh() {\n if (refreshTimeout) {\n clearTimeout(refreshTimeout);\n refreshTimeout = null;\n }\n }\n\n function handleAuthResponse(data: { tokens: AuthTokens, user: Record<string, unknown> }, event?: AuthChangeEvent): RebaseSession {\n sessionEpoch++;\n const user: User = mapRawUser(data.user);\n const session: RebaseSession = {\n accessToken: data.tokens.accessToken,\n refreshToken: heldRefreshToken(data.tokens.refreshToken),\n expiresAt: data.tokens.accessTokenExpiresAt,\n user\n };\n currentSession = session;\n saveSession(session);\n transport.setToken(session.accessToken);\n scheduleRefresh(session.expiresAt);\n emit(event || \"SIGNED_IN\", session);\n return session;\n }\n\n async function signInWithEmail(email: string, password: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/login\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email,\npassword }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n async function signUp(email: string, password: string, displayName?: string) {\n const fetchFn = getFetch();\n const payload: Record<string, string> = { email,\npassword };\n if (displayName !== undefined) payload.displayName = displayName;\n const res = await fetchFn(authUrl(\"/register\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n // A backend with `requireEmailVerification` registers confirm-first:\n // no session until the address is proven, and the same answer whether\n // or not the address already had an account.\n if (body && typeof body === \"object\" && \"confirmationRequired\" in body && body.confirmationRequired === true) {\n return { confirmationRequired: true as const, user: null, accessToken: null, refreshToken: null };\n }\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return {\n confirmationRequired: false as const,\n user: session.user,\n accessToken: session.accessToken,\n refreshToken: session.refreshToken\n };\n }\n\n /**\n * Sign in with Google.\n *\n * Supports three invocation styles:\n * - `signInWithGoogle({ idToken })` — ID-token flow (One Tap / Sign In button)\n * - `signInWithGoogle({ accessToken })` — Access-token flow (popup)\n * - `signInWithGoogle({ code, redirectUri })` — Authorization code flow (most secure)\n */\n async function signInWithGoogle(\n payload: { idToken: string } | { accessToken: string } | { code: string; redirectUri: string }\n ) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/google\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const responseBody = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, responseBody, res.statusText);\n const session = handleAuthResponse(responseBody, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n async function signInWithLinkedin(code: string, redirectUri: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/linkedin\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ code,\nredirectUri }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Generic OAuth sign-in. Posts the given payload to `/auth/{providerId}`.\n * Use this for any provider registered on the backend.\n */\n async function signInWithOAuth(providerId: string, payload: Record<string, unknown>) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(`/${providerId}`), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n // Convenience wrappers for all supported OAuth providers\n\n async function signInWithGitHub(code: string, redirectUri: string) {\n return signInWithOAuth(\"github\", { code,\nredirectUri });\n }\n\n async function signInWithMicrosoft(code: string, redirectUri: string) {\n return signInWithOAuth(\"microsoft\", { code,\nredirectUri });\n }\n\n async function signInWithApple(code: string, redirectUri: string, user?: { name?: { firstName?: string; lastName?: string }; email?: string }) {\n return signInWithOAuth(\"apple\", { code,\nredirectUri,\nuser });\n }\n\n async function signInWithFacebook(code: string, redirectUri: string) {\n return signInWithOAuth(\"facebook\", { code,\nredirectUri });\n }\n\n async function signInWithTwitter(code: string, redirectUri: string, codeVerifier: string) {\n return signInWithOAuth(\"twitter\", { code,\nredirectUri,\ncodeVerifier });\n }\n\n async function signInWithDiscord(code: string, redirectUri: string) {\n return signInWithOAuth(\"discord\", { code,\nredirectUri });\n }\n\n async function signInWithGitLab(code: string, redirectUri: string) {\n return signInWithOAuth(\"gitlab\", { code,\nredirectUri });\n }\n\n async function signInWithBitbucket(code: string, redirectUri: string) {\n return signInWithOAuth(\"bitbucket\", { code,\nredirectUri });\n }\n\n async function signInWithSlack(code: string, redirectUri: string) {\n return signInWithOAuth(\"slack\", { code,\nredirectUri });\n }\n\n async function signInWithSpotify(code: string, redirectUri: string) {\n return signInWithOAuth(\"spotify\", { code,\nredirectUri });\n }\n\n async function signOut() {\n // The sign-out takes effect when it is asked for, not when /logout\n // answers: a refresh landing in between must not be adopted.\n sessionEpoch++;\n const fetchFn = getFetch();\n try {\n if (authFlowMode === \"cookie\" || currentSession?.refreshToken) {\n await fetchFn(authUrl(\"/logout\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: refreshTokenBody(),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n }\n } catch (e) { /* ignore */ }\n endSession();\n }\n\n /**\n * Serialise refreshes across TABS, not just within one.\n *\n * The in-flight promise below covers callers inside a single JavaScript\n * context. It does nothing about the far more common case: two tabs of the\n * same app booting together, each firing its own /refresh with the same\n * cookie. The server tolerates that now (superseded tokens stay usable for\n * a grace window), but tolerating a stampede is not the same as avoiding\n * one, and every extra rotation is another chance to end up holding a\n * token whose response never arrived.\n *\n * Web Locks are best-effort on purpose. supabase-js shipped this and then\n * spent a year fielding deadlock reports — a lock held by a crashed or\n * frozen tab must never be able to wedge sign-in — so a lock we cannot\n * take within the timeout is simply not taken, and the refresh proceeds\n * unserialised, exactly as it did before.\n */\n const REFRESH_LOCK_NAME = \"rebase-auth-refresh\";\n const REFRESH_LOCK_TIMEOUT_MS = 5000;\n\n async function withRefreshLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as { navigator?: { locks?: LockManager } }).navigator?.locks;\n if (!locks?.request) return fn();\n\n const controller = new AbortController();\n const giveUp = setTimeout(() => controller.abort(), REFRESH_LOCK_TIMEOUT_MS);\n try {\n return await locks.request(\n REFRESH_LOCK_NAME,\n { signal: controller.signal },\n async () => fn()\n ) as T;\n } catch (e) {\n // AbortError means only that we waited long enough for the lock.\n // Anything the callback itself threw has to keep propagating.\n if ((e as { name?: string })?.name !== \"AbortError\") throw e;\n return fn();\n } finally {\n clearTimeout(giveUp);\n }\n }\n\n function refreshSession(): Promise<RebaseSession> {\n // Share a single in-flight refresh across concurrent callers — of the\n // same session. One started for a session that has since ended answers\n // with nothing of use to the session that replaced it.\n if (inFlightRefresh && inFlightEpoch === sessionEpoch) return inFlightRefresh;\n const refresh = withRefreshLock(() => doRefreshSession()).finally(() => {\n if (inFlightRefresh === refresh) inFlightRefresh = null;\n });\n inFlightRefresh = refresh;\n inFlightEpoch = sessionEpoch;\n return refresh;\n }\n\n /**\n * What a refresh answers when its session ended while it was in flight.\n *\n * The tokens it received belong to a session this client no longer holds,\n * so they are dropped, and so is any error it hit: a refusal of the old\n * session's token says nothing about the new one, and acting on it signed\n * out whoever had signed in since. The caller gets the session there is\n * now — or, if there is none, is told so.\n */\n function supersededRefresh(): RebaseSession {\n if (currentSession) return currentSession;\n throw new RebaseClientError(\n \"The session was signed out while it was being refreshed.\",\n { code: \"NOT_SIGNED_IN\" }\n );\n }\n\n /**\n * The session a sibling tab has persisted since this one last looked, when\n * it continues this one.\n *\n * Tabs of one app share `localStorage` but each holds its session in\n * memory, and a refresh rotates the refresh token. Once one tab has\n * refreshed, every other tab is holding a token the server has already\n * superseded: presented after the server's reuse window it is refused\n * with TOKEN_ALREADY_USED, again on every retry, until the tab gives up\n * and signs out. Its replacement was in storage all along.\n *\n * Only a session of the same user counts. A different user in storage\n * means another tab signed someone else in, and that is not this tab's\n * session to take over.\n */\n function siblingSession(): RebaseSession | null {\n if (!persistSession || authFlowMode === \"cookie\" || !currentSession) return null;\n const persisted = loadStoredSession();\n if (!persisted?.accessToken || !persisted.refreshToken) return null;\n if (persisted.refreshToken === currentSession.refreshToken) return null;\n if (!persisted.user?.uid || persisted.user.uid !== currentSession.user?.uid) return null;\n return persisted;\n }\n\n /**\n * Take over a sibling's session. Its access token is used as it is while\n * it has time left — exactly as a session restored on load would be — and\n * otherwise this answers `null` and the caller refreshes, now with the\n * sibling's refresh token rather than the superseded one.\n */\n function adoptSibling(sibling: RebaseSession): RebaseSession | null {\n currentSession = sibling;\n if (sibling.expiresAt - REFRESH_BUFFER_MS <= Date.now()) return null;\n transport.setToken(sibling.accessToken);\n scheduleRefresh(sibling.expiresAt);\n emit(\"TOKEN_REFRESHED\", sibling);\n return sibling;\n }\n\n async function doRefreshSession(): Promise<RebaseSession> {\n const epoch = sessionEpoch;\n try {\n // Inside the refresh lock, so a sibling that held it has finished\n // and persisted what it received.\n const sibling = siblingSession();\n if (sibling) {\n const adopted = adoptSibling(sibling);\n if (adopted) return adopted;\n }\n try {\n return await requestRefresh(epoch);\n } catch (err) {\n // Refused as already used: a sibling rotated it while this\n // request was on the wire (or without taking the lock).\n const late = epoch === sessionEpoch\n && err instanceof RebaseApiError\n && err.code === \"TOKEN_ALREADY_USED\"\n ? siblingSession()\n : null;\n if (!late) throw err;\n return adoptSibling(late) ?? await requestRefresh(epoch);\n }\n } catch (err) {\n if (epoch !== sessionEpoch) return supersededRefresh();\n throw err;\n }\n }\n\n async function requestRefresh(epoch: number): Promise<RebaseSession> {\n if (authFlowMode !== \"cookie\" && !currentSession?.refreshToken) {\n // A `RebaseClientError`, not a bare `Error`: the SDK documents one\n // class a `catch` block has to check for, and this is raised before\n // any request is made, which is exactly what that class is.\n throw new RebaseClientError(\n \"No active session to refresh. Sign in first, or set `authFlowMode: \\\"cookie\\\"` \" +\n \"if the refresh token is held in an HttpOnly cookie.\",\n { code: \"NOT_SIGNED_IN\" }\n );\n }\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/refresh\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: refreshTokenBody(),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (epoch !== sessionEpoch) return supersededRefresh();\n if (!res.ok) {\n const err = apiError(res.status, body, res.statusText);\n if (isMalformedRefreshError(err)) {\n // Said out loud, once per refusal, because nothing else will:\n // the session cannot be renewed, and every caller of a refresh\n // treats a non-auth failure as something to wait out.\n console.error(\n `[rebase] POST ${authPath}/refresh was refused as malformed ` +\n `(${res.status}${err.code ? ` ${err.code}` : \"\"}): ${err.message}. ` +\n \"The client and server disagree on the refresh request, so this session \" +\n \"cannot be renewed and retrying will not help. Check that @rebasepro/client \" +\n \"and @rebasepro/server are on compatible versions, and that `authFlowMode` \" +\n \"matches the server's `cookieAuth`.\"\n );\n }\n throw err;\n }\n\n const accessToken = body.tokens.accessToken;\n transport.setToken(accessToken);\n\n // Resolve the user, in order of preference:\n // 1. the user returned by /refresh (modern backends include it),\n // 2. the user already in memory,\n // 3. a fetch of /me — required to restore a session from an httpOnly\n // cookie alone (cold start in cookie mode), where there is no\n // in-memory user and the backend didn't echo one.\n let user = currentSession?.user;\n if (body.user && typeof body.user.uid === \"string\") {\n user = mapRawUser(body.user as Record<string, unknown>);\n } else if (!user || !user.uid) {\n refreshOwnRequests++;\n try {\n user = await getUser();\n } catch { /* fall through to the empty stub below */ } finally {\n refreshOwnRequests--;\n }\n // Whoever ended the session meanwhile has already set the\n // transport's token; nothing of this refresh is kept.\n if (epoch !== sessionEpoch) return supersededRefresh();\n }\n\n const session: RebaseSession = {\n accessToken,\n refreshToken: heldRefreshToken(body.tokens.refreshToken),\n expiresAt: body.tokens.accessTokenExpiresAt,\n user: user ?? EMPTY_USER\n };\n currentSession = session;\n saveSession(session);\n transport.setToken(session.accessToken);\n scheduleRefresh(session.expiresAt);\n emit(\"TOKEN_REFRESHED\", session);\n return session;\n }\n\n async function getUser() {\n const data = await transport.request<{ user: User }>(authPath + \"/me\", { method: \"GET\" });\n return data.user;\n }\n\n /**\n * Resolve an email to a minimal public profile (`uid`, `displayName`,\n * `photoURL`) for invite-by-email flows. Returns `null` when no account\n * matches. Requires the backend to opt in via `auth.allowUserLookup`;\n * otherwise the endpoint is absent and this rejects.\n */\n async function findUserByEmail(email: string): Promise<PublicUserProfile | null> {\n const data = await transport.request<{ user: PublicUserProfile | null }>(authPath + \"/find-user\", {\n method: \"POST\",\n body: JSON.stringify({ email })\n });\n return data.user;\n }\n\n async function updateUser(updates: { displayName?: string, photoURL?: string }) {\n const data = await transport.request<{ user: User }>(authPath + \"/me\", {\n method: \"PATCH\",\n body: JSON.stringify(updates)\n });\n if (currentSession) {\n currentSession = { ...currentSession,\nuser: data.user };\n saveSession(currentSession);\n emit(\"USER_UPDATED\", currentSession);\n }\n return data.user;\n }\n\n async function resetPasswordForEmail(email: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/forgot-password\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as { success: boolean; message: string; };\n }\n\n async function resetPassword(token: string, password: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/reset-password\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token,\npassword })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as { success: boolean; message: string; };\n }\n\n /**\n * Change the signed-in account's password. Every other session ends; this\n * one is replaced by the fresh session the server answers with, so the\n * client stays signed in.\n */\n async function changePassword(oldPassword: string, newPassword: string) {\n // Sent with this client's own fetch rather than the transport, so that\n // in cookie mode the new refresh cookie the answer sets is kept — so\n // the transport's refresh-on-401 is done here, before the call.\n if (currentSession && currentSession.expiresAt <= Date.now() + 10_000) {\n await refreshSession().catch(() => undefined);\n }\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/change-password\"), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(currentSession?.accessToken ? { Authorization: `Bearer ${currentSession.accessToken}` } : {})\n },\n body: JSON.stringify({ oldPassword, newPassword }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const result = body as { success: boolean; message: string; tokens?: AuthTokens; user?: Record<string, unknown> };\n // A server from before this release answers without a session, having\n // revoked this one: nothing to adopt, and the next call signs out.\n if (result.tokens && result.user) {\n handleAuthResponse({ tokens: result.tokens, user: result.user }, \"TOKEN_REFRESHED\");\n }\n return { success: result.success, message: result.message };\n }\n\n /**\n * Link an OAuth provider to the **currently signed-in** account.\n *\n * Use this when `signIn*` failed with `EMAIL_NOT_VERIFIED` — an account\n * with that email already exists under a different sign-in method — or to\n * attach a provider whose email differs from the account's.\n *\n * The payload is the same one the provider's sign-in method takes, e.g.\n * `linkProvider(\"google\", { idToken })`.\n *\n * Unlike sign-in, this does not require the provider to have verified the\n * email, and the emails need not match: the active session already proves\n * account ownership.\n *\n * Throws `IDENTITY_ALREADY_LINKED` (409) if that provider identity is\n * attached to a different user. Succeeds idempotently (`alreadyLinked:\n * true`) if it is already attached to the current one.\n */\n async function linkProvider(\n providerId: string,\n payload: Record<string, unknown>\n ) {\n return transport.request<{\n success: boolean;\n provider: string;\n alreadyLinked: boolean;\n /**\n * The provider's profile picture, when it has one. Usually a signed\n * URL with an expiry, so copy the bytes rather than storing this.\n */\n photoURL?: string | null;\n }>(\n authPath + \"/link/\" + providerId,\n {\n method: \"POST\",\n body: JSON.stringify(payload)\n }\n );\n }\n\n async function sendVerificationEmail() {\n return transport.request<{ success: boolean; message: string; }>(authPath + \"/send-verification\", {\n method: \"POST\"\n });\n }\n\n /**\n * Verify the address with the token from the link.\n *\n * The link proves the inbox, not who registered the address, so the\n * server keeps only what this call also proves: the current session (sent\n * when this client is signed in as that account) or `password`. An account\n * carrying a password or identity neither proves is refused with\n * `PROOF_REQUIRED` and the token is left unspent — ask for the password,\n * or pass `removeUnproven: true` to verify without it. With `password`,\n * the call also signs in.\n */\n async function verifyEmail(token: string, options?: { password?: string; removeUnproven?: boolean }) {\n const fetchFn = getFetch();\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (currentSession?.accessToken) headers.Authorization = `Bearer ${currentSession.accessToken}`;\n const res = await fetchFn(authUrl(\"/verify-email\"), {\n method: \"POST\",\n headers,\n body: JSON.stringify({ token, ...options }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const result = body as {\n success: boolean;\n message?: string;\n /** A password nobody proved was on the account, and is gone. */\n passwordRemoved: boolean;\n /** Providers whose identities were detached for not vouching for the address. */\n removedProviders: string[];\n tokens?: AuthTokens;\n user?: Record<string, unknown>;\n };\n const session = result.tokens && result.user\n ? handleAuthResponse({ tokens: result.tokens, user: result.user }, \"SIGNED_IN\")\n : null;\n return { success: result.success, passwordRemoved: result.passwordRemoved, removedProviders: result.removedProviders ?? [], session };\n }\n\n /**\n * Move the signed-in account to `newEmail`. The server mails a link to the\n * new address and a notice to the current one; nothing changes until the\n * link is followed and {@link confirmEmailChange} is called with its\n * token. A new request replaces the last.\n *\n * Rejects with `EMAIL_EXISTS` or `UNDELIVERABLE_ADDRESS` (409),\n * `EMAIL_UNCHANGED` (400), `AAL2_REQUIRED` when the account has a second\n * factor this session did not present, and `ANONYMOUS_USER` for a guest.\n */\n async function changeEmail(newEmail: string) {\n const data = await transport.request<{ success: boolean; pendingEmail: string; expiresAt: string }>(authPath + \"/change-email\", {\n method: \"POST\",\n body: JSON.stringify({ newEmail })\n });\n // The user this client holds says a change is waiting, as `/me` does.\n if (currentSession) {\n currentSession = { ...currentSession, user: { ...currentSession.user, pendingEmail: data.pendingEmail } };\n saveSession(currentSession);\n emit(\"USER_UPDATED\", currentSession);\n }\n return { pendingEmail: data.pendingEmail, expiresAt: data.expiresAt };\n }\n\n /**\n * Follow an address-change link: the token from\n * `<frontend>/confirm-email-change?token=…`. Needs no session — the link\n * proves the new inbox — and when this client is signed in as the account\n * it moved, its user is updated and `USER_UPDATED` is emitted.\n *\n * `removedProviders` names the sign-in providers detached because they\n * vouched for the old address. Rejects with `INVALID_TOKEN` for a spent,\n * replaced or expired link and `EMAIL_EXISTS` when the address was taken\n * while the link waited.\n */\n async function confirmEmailChange(token: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/confirm-email-change\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const result = body as { user: { uid: string; email: string }; removedProviders?: string[] };\n if (currentSession && currentSession.user.uid === result.user.uid) {\n currentSession = {\n ...currentSession,\n user: { ...currentSession.user, email: result.user.email, emailVerified: true, pendingEmail: null }\n };\n saveSession(currentSession);\n emit(\"USER_UPDATED\", currentSession);\n }\n return { uid: result.user.uid, email: result.user.email, removedProviders: result.removedProviders ?? [] };\n }\n\n async function sendMagicLink(email: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/magic-link\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as { success: boolean; message: string; };\n }\n\n async function verifyMagicLink(token: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/magic-link/verify\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Ask for a six-digit sign-in code by email.\n *\n * Answers the same thing whether or not the address has an account — do not\n * use the result to tell a person whether they are registered, because it\n * does not know.\n */\n async function sendEmailOtp(email: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/otp\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email })\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as { success: boolean; message: string; expiresInSeconds: number };\n }\n\n /**\n * Trade a code for a session.\n *\n * The address is sent with the code because the code is only valid for it:\n * that is what keeps a six-digit guess a guess against one account rather\n * than against every account at once.\n */\n async function verifyEmailOtp(email: string, code: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/otp/verify\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, code }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Sign in without credentials, as a throwaway account.\n *\n * The account is real — it has an id, roles and a session, so row-level\n * security scopes its data exactly as it would a signed-up user's. What it\n * does not have is a way back: nobody can sign in *as* it a second time, so\n * everything it owns is lost when the session is. `linkAnonymous` is how it\n * stops being throwaway.\n *\n * Refused with `ANONYMOUS_AUTH_DISABLED` unless the backend has it on.\n */\n async function signInAnonymously() {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/anonymous\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Put an email and password on the anonymous account this client holds.\n *\n * The user keeps their id, so everything they created while anonymous stays\n * theirs — which is the point: the alternative is asking a visitor to sign\n * up before they have any reason to.\n *\n * Requires a live anonymous session. `NOT_ANONYMOUS` (400) if the session\n * belongs to an ordinary account, `EMAIL_EXISTS` (409) if the address is\n * taken.\n */\n async function linkAnonymous(email: string, password: string) {\n const body = await transport.request<{ tokens: AuthTokens; user: Record<string, unknown> }>(\n authPath + \"/anonymous/link\",\n {\n method: \"POST\",\n body: JSON.stringify({ email, password })\n }\n );\n const session = handleAuthResponse(body, \"USER_UPDATED\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n\n /**\n * Multi-factor authentication: TOTP factors, and the challenge that raises\n * a session to `aal2`.\n *\n * The backend has served all six of these since MFA landed; the SDK exposed\n * none of them, so every app that wanted MFA hand-wrote `fetch` calls\n * against `/auth/mfa/*` — including the auth header and the cookie flag,\n * which is exactly what a client is for.\n */\n const mfa = {\n /**\n * Start enrolling a TOTP factor.\n *\n * Returns the secret and an `otpauth://` URI to render as a QR code,\n * plus — with the account's first factor — ten single-use recovery\n * codes. **Show the recovery codes once and never again**: only their\n * hashes are stored. Adding another factor keeps the codes the account\n * has, and `recoveryCodes` is then `null`; `regenerateRecoveryCodes()`\n * replaces them.\n *\n * The factor is not usable until `verify` confirms the user's\n * authenticator produced a correct code from it.\n */\n async enroll(options?: { friendlyName?: string; issuer?: string }) {\n return transport.request<{\n factor: { id: string; factorType: string; friendlyName?: string };\n totp: { secret: string; uri: string; qrUri: string };\n recoveryCodes: string[] | null;\n }>(authPath + \"/mfa/enroll\", {\n method: \"POST\",\n body: JSON.stringify(options ?? {})\n });\n },\n\n /** Confirm an enrolment with a code from the authenticator app. */\n async verify(factorId: string, code: string) {\n return transport.request<{ success: boolean; factor: { id: string; verified: boolean } }>(\n authPath + \"/mfa/verify\",\n { method: \"POST\", body: JSON.stringify({ factorId, code }) }\n );\n },\n\n /** The factors on this account, verified or not. */\n async listFactors() {\n const data = await transport.request<{\n factors: { id: string; factorType: string; friendlyName?: string; verified: boolean; createdAt: string }[];\n }>(authPath + \"/mfa/factors\", { method: \"GET\" });\n return data.factors;\n },\n\n /**\n * Replace the account's recovery codes with ten new ones, shown once.\n * Needs an `aal2` session. Adding a second factor keeps the codes the\n * account already has; this is how to get new ones.\n */\n async regenerateRecoveryCodes() {\n return transport.request<{ recoveryCodes: string[] }>(authPath + \"/mfa/recovery-codes\", { method: \"POST\" });\n },\n\n /**\n * Remove a factor. Once the account has a verified factor this\n * requires an `aal2` session — one that has already answered a\n * challenge — so a stolen `aal1` token cannot turn MFA off. An\n * unfinished first enrolment can be removed from any session.\n */\n async unenroll(factorId: string) {\n return transport.request<{ success: boolean; message: string }>(\n authPath + \"/mfa/unenroll\",\n { method: \"DELETE\", body: JSON.stringify({ factorId }) }\n );\n },\n\n /**\n * Open a challenge against a verified factor. Expires in five minutes.\n *\n * Pass `mfaToken` to finish a sign-in that was refused with\n * `MFA_REQUIRED`: it is the `details.mfaToken` of that refusal, and the\n * challenge is opened with it as this one request's credential. Without\n * it, the challenge steps up the session this client already holds.\n */\n async challenge(factorId: string, options?: MfaPendingOptions) {\n type Challenge = { challengeId: string; factorId: string; expiresAt: string };\n const payload = JSON.stringify({ factorId });\n if (options?.mfaToken) {\n return withPendingToken<Challenge>(\"/mfa/challenge\", payload, options.mfaToken);\n }\n return transport.request<Challenge>(authPath + \"/mfa/challenge\", { method: \"POST\", body: payload });\n },\n\n /**\n * Answer a challenge with a TOTP code or a recovery code.\n *\n * This is where an MFA-enrolled account's real session is minted, at\n * `aal2` — so this client adopts the returned tokens and announces\n * `SIGNED_IN`, exactly as any other sign-in does. Pass the same\n * `mfaToken` the challenge was opened with when finishing a sign-in.\n */\n async verifyChallenge(challengeId: string, code: string, options?: MfaPendingOptions) {\n type Minted = { tokens: AuthTokens; user: Record<string, unknown> };\n const payload = JSON.stringify({ challengeId, code });\n const body = options?.mfaToken\n ? await withPendingToken<Minted>(\"/mfa/challenge/verify\", payload, options.mfaToken)\n : await transport.request<Minted>(authPath + \"/mfa/challenge/verify\", { method: \"POST\", body: payload });\n const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\n }\n };\n\n /**\n * POST to a challenge route with a sign-in's pending token as the credential.\n *\n * Straight to `fetch`, like the sign-in routes, and never through the\n * transport: the token is a credential for these two requests only. Set on\n * the transport it would go out on every request the app made while the\n * code step was open — and stay there if the step were abandoned. It would\n * also displace a session the client already holds, and a refused code\n * (401 `INVALID_CODE`) would be taken for an expired session and refreshed\n * and re-sent, spending a second of the challenge's attempts on it.\n */\n async function withPendingToken<T>(endpoint: string, payload: string, mfaToken: string): Promise<T> {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(endpoint), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${mfaToken}`\n },\n body: payload,\n // The verify route sets the refresh cookie in cookie mode.\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as T;\n }\n\n async function getSessions(): Promise<DeviceSession[]> {\n const data = await transport.request<{ sessions: DeviceSession[] }>(authPath + \"/sessions\", { method: \"GET\" });\n return data.sessions;\n }\n\n async function revokeSession(sessionId: string) {\n return transport.request<{ success: boolean }>(authPath + \"/sessions/\" + encodeURIComponent(sessionId), {\n method: \"DELETE\"\n });\n }\n\n async function revokeAllSessions() {\n const result = await transport.request<{ success: boolean }>(authPath + \"/sessions\", {\n method: \"DELETE\"\n });\n endSession();\n return result;\n }\n\n async function getAuthConfig() {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/config\"), {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" }\n });\n const body = await res.json().catch(() => ({}));\n if (!res.ok) throwApiError(res.status, body, res.statusText);\n return body as AuthConfig;\n }\n\n function getSession() {\n return currentSession;\n }\n\n function onAuthStateChange(callback: (event: AuthChangeEvent, session: RebaseSession | null) => void) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n }\n\n if (persistSession) {\n const stored = loadStoredSession();\n if (stored && stored.accessToken) {\n if (stored.expiresAt > Date.now()) {\n currentSession = stored;\n transport.setToken(stored.accessToken);\n scheduleRefresh(stored.expiresAt);\n settleRestore(\"restored\");\n } else if (authFlowMode === \"cookie\" || stored.refreshToken) {\n currentSession = stored;\n const epoch = sessionEpoch;\n refreshSession().then(() => {\n settleRestore(\"restored\");\n }).catch((err: unknown) => {\n let outcome: Exclude<SessionRestoreOutcome, \"pending\"> = isFatalRefreshError(err) || isMalformedRefreshError(err)\n ? \"refused\"\n : \"unreachable\";\n if (epoch === sessionEpoch && currentSession) {\n if (isFatalRefreshError(err)) {\n // The refresh token itself was refused: there is no\n // session to restore.\n if (storageHoldsCurrentSession()) clearStoredSession();\n currentSession = null;\n transport.setToken(null);\n } else {\n // Offline, or the backend restarting. The refresh\n // token was never refused, only not delivered, and\n // deleting it here signed out every app opened\n // without a connection — while a running app, on\n // the very same error, keeps its session. Keep it\n // here too, and try again on the backoff schedule.\n // A refusal as malformed is neither: it is not\n // retried, since it would be refused again.\n transport.setToken(currentSession.accessToken);\n if (autoRefresh && !isMalformedRefreshError(err)) scheduleRetry(0);\n }\n } else {\n // Signed out, or someone else signed in, while it was\n // in flight: whatever this refresh hit is not about\n // the session there is now.\n outcome = currentSession ? \"restored\" : \"none\";\n }\n settleRestore(outcome);\n });\n } else {\n settleRestore(\"none\");\n }\n } else if (authFlowMode === \"cookie\") {\n // Silent refresh on boot to pick up httpOnly session\n const restore = refreshSession().then(() => {\n settleRestore(\"restored\");\n }).catch((err: unknown) => {\n // A 401 is the usual answer — no cookie, or an expired one —\n // and means there is nothing to restore. Only a failure to\n // reach the server leaves the question open.\n settleRestore(isFatalRefreshError(err) || isMalformedRefreshError(err)\n ? (currentSession ? \"restored\" : \"none\")\n : (currentSession ? \"restored\" : \"unreachable\"));\n }).finally(() => {\n if (cookieRestore === restore) cookieRestore = null;\n });\n cookieRestore = restore;\n } else {\n settleRestore(\"none\");\n }\n } else {\n settleRestore(\"none\");\n }\n\n return {\n signInWithEmail,\n signUp,\n signInWithGoogle,\n signInWithLinkedin,\n signInWithOAuth,\n signInWithGitHub,\n signInWithMicrosoft,\n signInWithApple,\n signInWithFacebook,\n signInWithTwitter,\n signInWithDiscord,\n signInWithGitLab,\n signInWithBitbucket,\n signInWithSlack,\n signInWithSpotify,\n signInAnonymously,\n linkAnonymous,\n mfa,\n signOut,\n stopAutoRefresh,\n refreshSession,\n handleUnauthorized,\n getUser,\n findUserByEmail,\n updateUser,\n resetPasswordForEmail,\n resetPassword,\n changePassword,\n linkProvider,\n sendVerificationEmail,\n verifyEmail,\n changeEmail,\n confirmEmailChange,\n sendMagicLink,\n verifyMagicLink,\n sendEmailOtp,\n verifyEmailOtp,\n getSessions,\n revokeSession,\n revokeAllSessions,\n getAuthConfig,\n getSession,\n onAuthStateChange,\n // A client that neither persists sessions nor uses cookie auth has\n // nowhere to restore one from, so \"no session in memory\" is the final\n // answer rather than a reason to ask the server. See the docblock on\n // `AuthClient.canRestoreSession`.\n canRestoreSession: () => persistSession || authFlowMode === \"cookie\",\n isInitialized: () => isInitialized,\n /**\n * How restoring the session on load ended — see\n * {@link SessionRestoreOutcome}. `\"unreachable\"` with no session is\n * the one case where \"signed out\" is not known to be true: the\n * device could not ask.\n */\n restoreOutcome: (): SessionRestoreOutcome => restoreOutcome\n };\n}\n\n/**\n * How restoring the session on load ended — `auth.restoreOutcome()`.\n *\n * - `\"pending\"`: still restoring (`isInitialized()` has not settled).\n * - `\"restored\"`: a session was restored, from storage or from the server.\n * - `\"none\"`: there was nothing to restore.\n * - `\"refused\"`: there was a session to restore, and the server refused it.\n * - `\"unreachable\"`: the server could not be reached (or answered 5xx), so the\n * session was neither confirmed nor refused. With a stored session the\n * client keeps it and retries; in cookie mode, where nothing is stored on the\n * device, it has no session until a later refresh or sign-in succeeds.\n */\nexport type SessionRestoreOutcome = \"pending\" | \"restored\" | \"none\" | \"refused\" | \"unreachable\";\n\nexport interface CookieStorageOptions {\n path?: string;\n domain?: string;\n secure?: boolean;\n sameSite?: \"Lax\" | \"Strict\" | \"None\";\n maxAge?: number;\n}\n\nexport function createCookieStorage(options: CookieStorageOptions = {}): AuthStorage {\n const defaultOptions = {\n path: \"/\",\n sameSite: \"Lax\" as const,\n ...options\n };\n\n return {\n getItem(key: string): string | null {\n if (typeof document === \"undefined\") return null;\n const nameEQ = encodeURIComponent(key) + \"=\";\n const ca = document.cookie.split(\";\");\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) === \" \") c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) === 0) {\n return decodeURIComponent(c.substring(nameEQ.length, c.length));\n }\n }\n return null;\n },\n setItem(key: string, value: string): void {\n if (typeof document === \"undefined\") return;\n let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;\n\n if (defaultOptions.path) {\n cookieStr += `; path=${defaultOptions.path}`;\n }\n if (defaultOptions.domain) {\n cookieStr += `; domain=${defaultOptions.domain}`;\n }\n if (defaultOptions.maxAge !== undefined) {\n cookieStr += `; max-age=${defaultOptions.maxAge}`;\n } else {\n cookieStr += `; max-age=${365 * 24 * 60 * 60}`;\n }\n if (defaultOptions.secure) {\n cookieStr += \"; secure\";\n }\n if (defaultOptions.sameSite) {\n cookieStr += `; samesite=${defaultOptions.sameSite}`;\n }\n\n document.cookie = cookieStr;\n },\n removeItem(key: string): void {\n if (typeof document === \"undefined\") return;\n let cookieStr = `${encodeURIComponent(key)}=; path=${defaultOptions.path || \"/\"}; max-age=-1`;\n if (defaultOptions.domain) {\n cookieStr += `; domain=${defaultOptions.domain}`;\n }\n document.cookie = cookieStr;\n }\n };\n}\n","import type { Transport } from \"./transport\";\nimport type { AdminUser, RoleSummary } from \"@rebasepro/types\";\n\nexport type { AdminUser };\n\n\nexport interface CreateAdminOptions {\n adminPath?: string;\n}\n\n/** The window and filter `GET /admin/users` takes. */\nexport interface ListUsersOptions {\n search?: string;\n limit?: number;\n offset?: number;\n orderBy?: string;\n orderDir?: \"asc\" | \"desc\";\n /** Only the users holding this role id (`?role=`). */\n role?: string;\n /**\n * Resolve exactly these users in one round trip (`?ids=`), for turning\n * stored ids into names. The window, search and role are ignored when it\n * is given, and ids that name no user are left out of the answer.\n */\n ids?: readonly string[];\n}\n\nexport function createAdmin(transport: Transport, options?: CreateAdminOptions) {\n const opts = options || {};\n const adminPath = opts.adminPath || \"/admin\";\n\n /**\n * One page of users — the route's default is 25 — with the `total` that\n * says whether there are more. Takes the same window as\n * {@link listUsersPaginated}; without one, it used to hand back the first\n * page typed as the whole list.\n */\n async function listUsers(options?: ListUsersOptions) {\n return listUsersPaginated(options);\n }\n\n async function listUsersPaginated(options?: ListUsersOptions) {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n if (options?.offset !== undefined) params.set(\"offset\", String(options.offset));\n if (options?.search) params.set(\"search\", options.search);\n if (options?.orderBy) params.set(\"orderBy\", options.orderBy);\n if (options?.orderDir) params.set(\"orderDir\", options.orderDir);\n if (options?.role) params.set(\"role\", options.role);\n if (options?.ids) params.set(\"ids\", options.ids.join(\",\"));\n const qs = params.toString();\n return transport.request<{ users: AdminUser[]; total: number; limit: number; offset: number }>(\n adminPath + \"/users\" + (qs ? \"?\" + qs : \"\"), { method: \"GET\" }\n );\n }\n\n async function getUser(userId: string) {\n return transport.request<{ user: AdminUser }>(adminPath + \"/users/\" + encodeURIComponent(userId), { method: \"GET\" });\n }\n\n /**\n * Create an account. When the server could not send an invitation (no\n * email configured, or the send failed) the answer carries the generated\n * `temporaryPassword` — returned this once and never again.\n *\n * A chosen `password` is held to the password policy (`WEAK_PASSWORD`).\n * `skipPasswordPolicy: true` waives it, for an import of accounts whose\n * passwords another system chose; the server honours it only for an\n * administrator or the service key.\n */\n async function createUser(data: { email: string, displayName?: string, password?: string, roles?: string[], skipPasswordPolicy?: boolean }) {\n return transport.request<{ user: AdminUser; temporaryPassword?: string; invitationSent?: boolean; emailDeliveryFailed?: boolean }>(adminPath + \"/users\", {\n method: \"POST\",\n body: JSON.stringify(data)\n });\n }\n\n /**\n * Change an account. `disabled: true` switches it off — no sign-in, no\n * refresh, and every token it holds refused at once — without deleting it.\n */\n async function updateUser(userId: string, data: { email?: string, displayName?: string, password?: string, roles?: string[], disabled?: boolean }) {\n return transport.request<{ user: AdminUser }>(adminPath + \"/users/\" + encodeURIComponent(userId), {\n method: \"PUT\",\n body: JSON.stringify(data)\n });\n }\n\n async function deleteUser(userId: string) {\n return transport.request<{ success: boolean }>(adminPath + \"/users/\" + encodeURIComponent(userId), {\n method: \"DELETE\"\n });\n }\n\n /**\n * Remove every second factor and recovery code the account has, and end\n * its sessions — for someone who lost their authenticator and their\n * codes. Needs `users:write`.\n */\n async function resetMfa(userId: string) {\n return transport.request<{ success: boolean; removedFactors: number }>(\n adminPath + \"/users/\" + encodeURIComponent(userId) + \"/mfa\",\n { method: \"DELETE\" }\n );\n }\n\n async function resetPassword(userId: string, options?: { password?: string }) {\n return transport.request<{ user: AdminUser; temporaryPassword?: string; invitationSent?: boolean; emailDeliveryFailed?: boolean }>(\n adminPath + \"/users/\" + encodeURIComponent(userId) + \"/reset-password\",\n {\n method: \"POST\",\n ...(options?.password ? { body: JSON.stringify({ password: options.password }) } : {})\n }\n );\n }\n\n async function listRoles() {\n return transport.request<{ roles: RoleSummary[] }>(\n adminPath + \"/roles\",\n { method: \"GET\" }\n );\n }\n\n async function bootstrap() {\n return transport.request<{ success: boolean; message: string; user: { uid: string; roles: string[] } }>(adminPath + \"/bootstrap\", {\n method: \"POST\"\n });\n }\n\n return {\n listUsers,\n listUsersPaginated,\n getUser,\n createUser,\n updateUser,\n deleteUser,\n resetMfa,\n resetPassword,\n listRoles,\n bootstrap\n };\n}\n","import { Transport } from \"./transport\";\nimport type { CronJobStatus, CronJobLogEntry, CronJobListing, CronJobStarted } from \"@rebasepro/types\";\n\nexport interface CreateCronOptions {\n cronPath?: string;\n}\n\nexport function createCron(transport: Transport, options?: CreateCronOptions) {\n const cronPath = options?.cronPath || \"/admin/cron\";\n\n async function listJobs(): Promise<CronJobListing> {\n return transport.request<CronJobListing>(cronPath, { method: \"GET\" });\n }\n\n async function getJob(jobId: string): Promise<{ job: CronJobStatus }> {\n return transport.request<{ job: CronJobStatus }>(\n cronPath + \"/\" + encodeURIComponent(jobId),\n { method: \"GET\" }\n );\n }\n\n /**\n * Run a job now. By default the call waits for the run and answers with\n * its log; `{ async: true }` answers once the run has started (`202`), for\n * jobs longer than a proxy in front of the server will hold a request.\n */\n function triggerJob(jobId: string, options?: { async?: false }): Promise<{ log: CronJobLogEntry; job: CronJobStatus }>;\n function triggerJob(jobId: string, options: { async: true }): Promise<CronJobStarted>;\n async function triggerJob(\n jobId: string,\n options?: { async?: boolean }\n ): Promise<{ log: CronJobLogEntry; job: CronJobStatus } | CronJobStarted> {\n const path = cronPath + \"/\" + encodeURIComponent(jobId) + \"/trigger\";\n if (options?.async) {\n return transport.request<CronJobStarted>(path + \"?async=1\", { method: \"POST\" });\n }\n return transport.request<{ log: CronJobLogEntry; job: CronJobStatus }>(path, { method: \"POST\" });\n }\n\n async function getJobLogs(\n jobId: string,\n options?: { limit?: number }\n ): Promise<{ logs: CronJobLogEntry[] }> {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return transport.request<{ logs: CronJobLogEntry[] }>(\n cronPath + \"/\" + encodeURIComponent(jobId) + \"/logs\" + (qs ? \"?\" + qs : \"\"),\n { method: \"GET\" }\n );\n }\n\n async function toggleJob(\n jobId: string,\n enabled: boolean\n ): Promise<{ job: CronJobStatus }> {\n return transport.request<{ job: CronJobStatus }>(\n cronPath + \"/\" + encodeURIComponent(jobId),\n {\n method: \"PUT\",\n body: JSON.stringify({ enabled })\n }\n );\n }\n\n return {\n listJobs,\n getJob,\n triggerJob,\n getJobLogs,\n toggleJob\n };\n}\n","import { Transport } from \"./transport\";\nimport { RebaseApiError, type BackupDownloadLink, type BackupListing } from \"@rebasepro/types\";\n\nexport interface CreateBackupsOptions {\n backupsPath?: string;\n}\n\n/** One backup's bytes, as {@link createBackups}' `stream()` answers them. */\nexport interface BackupStream {\n /** The dump, read from the server as it is consumed. */\n stream: ReadableStream<Uint8Array>;\n /** Its size in bytes, when the server said. */\n size?: number;\n /** Its file name: the key's last segment, as the server names the download. */\n name: string;\n}\n\n/**\n * Statuses that say the server cannot mint a download link at all — one that\n * predates `POST /download-url` (404/405), or one with no JWT secret to sign\n * it with (503) — rather than that this backup cannot be had. `download()`\n * then falls back to reading the bytes itself.\n */\nconst NO_LINKS = new Set([404, 405, 503]);\n\nexport function createBackups(transport: Transport, options?: CreateBackupsOptions) {\n const backupsPath = options?.backupsPath || \"/admin/backups\";\n /** Mirror transport.request's URL construction (baseUrl + apiPath + path). */\n const downloadPath = () => `${transport.baseUrl}${transport.apiPath}${backupsPath}/download`;\n\n async function list(): Promise<BackupListing> {\n return transport.request(backupsPath, { method: \"GET\" });\n }\n\n /**\n * A link to one backup's bytes, good for 60 seconds. The URL carries a\n * token that stands in for the `Authorization` header, for this backup\n * only, so anything can be pointed at it — a browser, `curl`, a\n * `fetch` that streams it to a file. The URL is absolute, on this\n * client's base URL.\n */\n async function downloadUrl(key: string): Promise<BackupDownloadLink> {\n const link = await transport.request<BackupDownloadLink>(`${backupsPath}/download-url`, {\n method: \"POST\",\n body: JSON.stringify({ key })\n });\n return {\n url: `${downloadPath()}?key=${encodeURIComponent(key)}&token=${encodeURIComponent(link.token)}`,\n token: link.token,\n expiresIn: link.expiresIn\n };\n }\n\n /**\n * A backup's bytes as a stream, read as they are consumed — the way to\n * write one to a file outside a browser:\n * `await pipeline(Readable.fromWeb(stream), fs.createWriteStream(name))`.\n *\n * Through the transport's own `fetch` and headers, so a client configured\n * with its own `fetch` or with default headers (a gateway key, the schema\n * version) reaches the same place every other call does.\n */\n async function stream(key: string): Promise<BackupStream> {\n const url = `${downloadPath()}?key=${encodeURIComponent(key)}`;\n const token = await transport.resolveToken();\n const headers = transport.getHeaders();\n // A GET with no body: the JSON default says nothing true here.\n delete headers[\"Content-Type\"];\n if (token) headers.Authorization = `Bearer ${token}`;\n const res = await transport.fetchFn(url, { method: \"GET\", headers });\n if (!res.ok || !res.body) {\n throw new RebaseApiError(`Failed to download backup (${res.status})`, { status: res.status });\n }\n // `Content-Length` is CORS-safelisted; `Content-Disposition` is not,\n // so the name comes from the key, as the server's does.\n const length = Number(res.headers.get(\"Content-Length\"));\n return {\n stream: res.body,\n ...(Number.isFinite(length) && length > 0 ? { size: length } : {}),\n name: fileNameOf(key)\n };\n }\n\n /**\n * Save a backup in the browser: the browser is sent to a\n * {@link downloadUrl} link and streams the dump to disk, so the page never\n * holds it. It used to be fetched into a Blob — the whole database in the\n * tab's memory, with no progress shown.\n *\n * Against a server that cannot mint links (one older than them, or one\n * with no JWT secret) it reads the bytes itself and saves them as before.\n *\n * Browser only. Elsewhere, {@link stream} writes a backup to a file and\n * {@link downloadUrl} gives a link.\n */\n async function download(key: string): Promise<void> {\n if (typeof document === \"undefined\") {\n throw new Error(\n \"backups.download() hands the file to a browser to save. Outside one, use \" +\n \"backups.stream(key) to write it to a file, or backups.downloadUrl(key) for a link.\"\n );\n }\n let link: BackupDownloadLink;\n try {\n link = await downloadUrl(key);\n } catch (error: unknown) {\n if (!(error instanceof RebaseApiError) || error.status === undefined || !NO_LINKS.has(error.status)) throw error;\n const { stream: body, name } = await stream(key);\n const objectUrl = URL.createObjectURL(await new Response(body).blob());\n try {\n saveFrom(objectUrl, name);\n } finally {\n URL.revokeObjectURL(objectUrl);\n }\n return;\n }\n saveFrom(link.url, fileNameOf(key));\n }\n\n return { list, download, downloadUrl, stream };\n}\n\n/** The last segment of a backup's key: its file name, local path or object key alike. */\nfunction fileNameOf(key: string): string {\n return key.split(/[\\\\/]/).pop() || key;\n}\n\n/**\n * Have the browser save `url` as `fileName`. The server answers a backup with\n * `Content-Disposition: attachment`, so the browser saves it rather than\n * navigating, cross-origin too, where `download` is ignored.\n */\nfunction saveFrom(url: string, fileName: string): void {\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = fileName;\n a.rel = \"noopener\";\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n","import type { Transport } from \"./transport\";\n\nexport type {\n ApiKeyKind,\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n CreatePersonalKeyRequest,\n UpdateApiKeyRequest\n} from \"@rebasepro/types\";\n\nimport type {\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n CreatePersonalKeyRequest,\n ScopeSummary,\n UpdateApiKeyRequest\n} from \"@rebasepro/types\";\n\n/** Options for the `createApiKeys` factory. */\nexport interface CreateApiKeysOptions {\n apiKeysPath?: string;\n}\n\n/**\n * Creates a client for the project's service keys, via the admin routes.\n * Needs `keys:read` / `keys:write`.\n *\n * @param transport - The shared HTTP transport created by `createTransport`.\n * @param options - Optional overrides (e.g. a custom base path).\n */\nexport function createApiKeys(transport: Transport, options?: CreateApiKeysOptions) {\n const apiKeysPath = options?.apiKeysPath || \"/admin/api-keys\";\n\n /** List all API keys (masked). */\n async function listKeys(): Promise<{ keys: ApiKeyMasked[] }> {\n return transport.request<{ keys: ApiKeyMasked[] }>(apiKeysPath, { method: \"GET\" });\n }\n\n /** Get a single API key by ID (masked). */\n async function getKey(id: string): Promise<{ key: ApiKeyMasked }> {\n return transport.request<{ key: ApiKeyMasked }>(\n apiKeysPath + \"/\" + encodeURIComponent(id),\n { method: \"GET\" }\n );\n }\n\n /** Create a new API key. The full secret is included in the response. */\n async function createKey(data: CreateApiKeyRequest): Promise<{ key: ApiKeyWithSecret }> {\n return transport.request<{ key: ApiKeyWithSecret }>(apiKeysPath, {\n method: \"POST\",\n body: JSON.stringify(data)\n });\n }\n\n /** Update an existing API key. */\n async function updateKey(id: string, data: UpdateApiKeyRequest): Promise<{ key: ApiKeyMasked }> {\n return transport.request<{ key: ApiKeyMasked }>(\n apiKeysPath + \"/\" + encodeURIComponent(id),\n {\n method: \"PUT\",\n body: JSON.stringify(data)\n }\n );\n }\n\n /** Revoke (soft-delete) an API key. */\n async function revokeKey(id: string): Promise<{ success: boolean }> {\n return transport.request<{ success: boolean }>(\n apiKeysPath + \"/\" + encodeURIComponent(id),\n { method: \"DELETE\" }\n );\n }\n\n return {\n listKeys,\n getKey,\n createKey,\n updateKey,\n revokeKey\n };\n}\n\n/** Options for the `createPersonalKeys` factory. */\nexport interface CreatePersonalKeysOptions {\n personalKeysPath?: string;\n scopesPath?: string;\n}\n\n/**\n * Creates a client for the signed-in account's own API keys — each acts as the\n * account and holds no scope it does not. Answers `PERSONAL_KEYS_DISABLED`\n * unless the backend sets `personalKeys: true` on the users collection.\n */\nexport function createPersonalKeys(transport: Transport, options?: CreatePersonalKeysOptions) {\n const keysPath = options?.personalKeysPath || \"/auth/keys\";\n const scopesPath = options?.scopesPath || \"/auth/scopes\";\n\n /** List this account's keys (masked). */\n async function listKeys(): Promise<{ keys: ApiKeyMasked[] }> {\n return transport.request<{ keys: ApiKeyMasked[] }>(keysPath, { method: \"GET\" });\n }\n\n /** Create a key for this account. The full secret is included in the response. */\n async function createKey(data: CreatePersonalKeyRequest): Promise<{ key: ApiKeyWithSecret }> {\n return transport.request<{ key: ApiKeyWithSecret }>(keysPath, {\n method: \"POST\",\n body: JSON.stringify(data)\n });\n }\n\n /** Revoke one of this account's keys. */\n async function revokeKey(id: string): Promise<{ success: boolean }> {\n return transport.request<{ success: boolean }>(\n keysPath + \"/\" + encodeURIComponent(id),\n { method: \"DELETE\" }\n );\n }\n\n /**\n * Every scope the backend knows, described, and the ones the caller holds —\n * what a screen offering scopes for a key needs.\n */\n async function listScopes(): Promise<{ scopes: ScopeSummary[]; held: string[] }> {\n return transport.request<{ scopes: ScopeSummary[]; held: string[] }>(scopesPath, { method: \"GET\" });\n }\n\n return {\n listKeys,\n createKey,\n revokeKey,\n listScopes\n };\n}\n","import type { Transport } from \"./transport\";\nimport { RebaseClientError } from \"@rebasepro/types\";\n\n/**\n * Client interface for invoking custom backend functions.\n *\n * Custom functions are Hono route files auto-mounted by the Rebase backend\n * at `/api/functions/{name}`. The `FunctionsClient` wraps the shared\n * transport so callers never need to manually construct URLs or inject\n * auth tokens.\n *\n * @example\n * ```ts\n * const result = await client.functions.invoke<{ job: Job }>('extract-job', {\n * url: 'https://example.com/posting',\n * html: htmlContent,\n * });\n * ```\n */\nexport interface FunctionsClient {\n /**\n * Invoke a custom backend function by name.\n *\n * **The method is `POST` unless you say otherwise.** A function whose only\n * route is `app.get(\"/\")` therefore answers 404 to a bare `invoke(name)` —\n * pass `{ method: \"GET\" }`, or write the route as `app.post`.\n *\n * @typeParam T - Expected shape of the response payload.\n * @param name - Function name (the filename without extension, e.g. `\"extract-job\"`).\n * One path segment: a sub-path goes in `options.path`.\n * @param payload - Optional JSON-serialisable body. Dropped for `GET`.\n * @param options - Optional overrides (HTTP method, sub-path, extra headers).\n * @returns The parsed JSON response from the function, or — with\n * `options.responseType` — its text, a `Blob`, or the raw `Response`.\n */\n invoke<T = unknown>(\n name: string,\n payload?: unknown,\n options?: FunctionInvokeOptions,\n ): Promise<T>;\n}\n\nexport type { FunctionInvokeOptions } from \"@rebasepro/types\";\nimport type { FunctionInvokeOptions } from \"@rebasepro/types\";\n\n/**\n * Create a `FunctionsClient` backed by the given transport.\n *\n * The transport already handles:\n * - Base URL resolution\n * - JWT injection via `Authorization: Bearer`\n * - 401 retry / `onUnauthorized` flow\n * - Consistent error throwing via `RebaseApiError`\n *\n * @internal\n */\n/** Whether a URL path segment is `.` or `..`, percent-encoded or not. */\nfunction isDotSegment(segment: string): boolean {\n return /^(?:\\.|%2e){1,2}$/i.test(segment);\n}\n\nexport function createFunctionsClient(transport: Transport): FunctionsClient {\n return {\n async invoke<T = unknown>(\n name: string,\n payload?: unknown,\n options?: FunctionInvokeOptions\n ): Promise<T> {\n // A function name is ONE path segment — the loader takes functions\n // from the top level of the directory only and refuses\n // subdirectories — so `encodeURIComponent` below is right, and a\n // name carrying a `/` is always a mistake at the call site.\n //\n // Refused rather than encoded, because encoding it is silent and\n // wrong in the worst way: `invoke(\"storage-provision/<id>\")` became\n // `POST /api/functions/storage-provision%2F<id>`, which matches no\n // route, so a route that exists answered a bare 404. That shipped,\n // and `rebase cloud storage create` read as an unimplemented feature\n // — including to the person who had implemented it. The sub-path\n // belongs in `options.path`.\n if (name.includes(\"/\")) {\n throw new RebaseClientError(\n `Invalid function name \"${name}\": a function name is a single path segment. ` +\n \"Pass anything after it as `options.path` — \" +\n `invoke(\"${name.split(\"/\")[0]}\", payload, { path: \"${name.split(\"/\").slice(1).join(\"/\")}\" }).`,\n { code: \"INVALID_FUNCTION_NAME\", details: { name } }\n );\n }\n\n // `.` and `..` are resolved by `fetch` before the request leaves:\n // `invoke(\"x\", undefined, { path: \"../../admin/users/<uid>\",\n // method: \"DELETE\" })` reached `/api/admin/users/<uid>`, a route\n // this method does not describe (and an agent tool built on it\n // offers). A function's own routes never need one, so a dot\n // segment — as the name, or anywhere in the path before its query\n // — is refused, spelled plainly or percent-encoded.\n const pathPart = (options?.path ?? \"\").split(/[?#]/)[0];\n if (isDotSegment(name) || pathPart.split(\"/\").some(isDotSegment)) {\n throw new RebaseClientError(\n `Invalid function route \"${name}${options?.path ? ` + ${options.path}` : \"\"}\": ` +\n \"a `.` or `..` segment would leave /functions/\" + name + \".\",\n { code: \"INVALID_FUNCTION_PATH\", details: { name, path: options?.path } }\n );\n }\n\n const method = options?.method ?? \"POST\";\n // A `path` that starts the query or fragment is appended as-is. Only a\n // real sub-path gets a separator: inserting one before `?days=30` asks\n // for `/functions/dashboard-stats/?days=30`, and the trailing slash\n // misses the route, so a function that exists answers 404 — and the\n // caller sees it as the backend being down rather than as a bad URL.\n const rawPath = options?.path;\n const subPath = rawPath\n ? (/^[?#]/.test(rawPath) ? rawPath : `/${rawPath.replace(/^\\//, \"\")}`)\n : \"\";\n const routePath = `/functions/${encodeURIComponent(name)}${subPath}`;\n\n const init: RequestInit = { method };\n\n if (payload !== undefined && method !== \"GET\") {\n init.body = JSON.stringify(payload);\n }\n\n if (options?.headers) {\n init.headers = options.headers;\n }\n\n // A function may answer text or a file; reading that as JSON\n // refused a body the function sent on purpose.\n const responseType = options?.responseType ?? \"json\";\n if (responseType !== \"json\") {\n if (!transport.requestRaw) {\n throw new RebaseClientError(\n `responseType \"${responseType}\" needs a transport that can hand back the raw response.`,\n { code: \"UNSUPPORTED_OPTION\", details: { responseType } }\n );\n }\n const res = await transport.requestRaw(routePath, init);\n // SAFETY: the caller names T for the mode it asked for — a\n // string, a Blob or the Response — as with the JSON default.\n if (responseType === \"response\") return res as unknown as T;\n if (responseType === \"blob\") return (await res.blob()) as unknown as T;\n return (await res.text()) as unknown as T;\n }\n\n // The function's body, verbatim: nothing here reaches in for a\n // `data` key. `client.call()` used to, which made the documented\n // \"shorthand for invoke\" return a different value for any function\n // that answered `{ data: … }`.\n return transport.request<T>(routePath, init);\n }\n };\n}\n","import { StorageSource, UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult, DownloadMetadata, PUBLIC_STORAGE_PREFIX, RebaseApiError, isPublicStoragePath, storageObjectPath } from \"@rebasepro/types\";\nimport {\n DOWNLOAD_URL_CONCURRENCY,\n RESUMABLE_UPLOAD_THRESHOLD,\n createBatchLoader,\n createConcurrencyLimit,\n createDownloadUrlCache,\n type DownloadMetadataBatchEntry,\n type ResolvedDownload,\n type ResumableUploadSend\n} from \"@rebasepro/common\";\nimport { defaultCodeForStatus, Transport, type ResponseMeta } from \"./transport\";\n\n/**\n * Create a StorageSource that talks to the Rebase backend REST API.\n *\n * @param transport - HTTP transport instance\n * @param storageId - Optional storage-source key for multi-backend routing.\n * When set, it is forwarded to the server so the correct\n * `StorageController` is resolved from the registry.\n */\n/**\n * A storage key, encoded for a URL path.\n *\n * Every call below interpolated the key raw, and the server decodes what it\n * receives — so three ordinary filenames did three different wrong things:\n *\n * `Invoice #12.pdf` the `#` began the fragment, the server saw\n * `default/Invoice ` and looked up a key that is not the\n * file — and a scoped `?token=` after it was swallowed too\n * `100% done.png` `decodeURIComponent` threw URIError → 500\n * `a%2Fb.png` decoded to `a/b.png`, silently resolving a DIFFERENT\n * object\n *\n * Per SEGMENT, because `/` is the key's own separator and must survive; every\n * other character is encoded, including a literal `%`. Verified to round-trip\n * through the server's decode for all of the above, plus `+`, accented\n * characters and a trailing `%`.\n */\nexport function encodeStorageKey(key: string): string {\n return key.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\n/**\n * The fields of an upload's form, as the resumable endpoint's\n * `Upload-Metadata`: the same names, plus the file's own name and type, which\n * the form carries in the file part. A Blob travels in a form as `blob`.\n */\nfunction resumableMetadataOf(form: FormData): Record<string, string> {\n const entries: Record<string, string> = {};\n for (const [name, value] of form.entries()) {\n if (typeof value === \"string\") {\n entries[name] = value;\n } else if (name === \"file\") {\n const filename = value instanceof File ? value.name : \"blob\";\n if (filename) entries.filename = filename;\n entries.filetype = value.type || \"application/octet-stream\";\n }\n }\n if (!entries.key && !entries.filename) entries.key = \"unnamed\";\n return entries;\n}\n\nexport function createStorage(transport: Transport, storageId?: string): StorageSource {\n // Keyed by `[bucket/]keyOrUrl`; an entry lives until its download token\n // expires (public URLs: until evicted), is dropped by `deleteObject`, and\n // is bounded LRU. One per source instance, i.e. per client, and owned by\n // the credential its URLs were minted with (see `getSignedUrl`), so a\n // sign-out or another user's sign-in empties it. Concurrent lookups of one\n // key share one `/metadata` request, and at most\n // DOWNLOAD_URL_CONCURRENCY of those are open at once.\n const urlsCache = createDownloadUrlCache();\n const limitMetadata = createConcurrencyLimit(DOWNLOAD_URL_CONCURRENCY);\n\n /**\n * A private file's metadata and download token. The lookups a page starts\n * together — a table of thumbnails — go as one `POST /storage/metadata`\n * per hundred files, which the server charges a tenth of the requests it\n * replaces; one asked for alone is the `GET` it always was. A server\n * without the batch route (404) is asked one file at a time from then on.\n */\n const describe = createBatchLoader<DownloadMetadata>({\n loadOne: async (filePath) => (await limitMetadata(() =>\n transport.request<{ data: DownloadMetadata }>(withStorageId(`/storage/metadata/${encodeStorageKey(filePath)}`)))).data,\n loadMany: async (filePaths) => {\n const result = await limitMetadata(() => transport.request<{ data: Record<string, DownloadMetadataBatchEntry> }>(\"/storage/metadata\", {\n method: \"POST\",\n body: JSON.stringify({ keys: filePaths, ...(storageId ? { storageId } : {}) })\n }));\n const answers = new Map<string, DownloadMetadata | Error>();\n for (const [filePath, entry] of Object.entries(result.data ?? {})) {\n answers.set(filePath, \"error\" in entry\n ? new RebaseApiError(entry.error.message, { status: entry.error.status, code: entry.error.code })\n : entry);\n }\n return answers;\n },\n isUnsupported: (error) => error instanceof Error && \"status\" in error && (error.status === 404 || error.status === 405)\n });\n\n /**\n * Base for URLs the *browser* will fetch on its own (file downloads,\n * previews). API requests keep going to `baseUrl`; see\n * {@link RebaseClientConfig.storageUrlOrigin} for why these can differ.\n */\n const fileUrlBase = (): string =>\n `${transport.storageUrlOrigin ?? transport.baseUrl}${transport.apiPath}`;\n\n /** Append ?storageId=... to a path when multi-backend routing is active. */\n const withStorageId = (path: string): string => {\n if (!storageId) return path;\n const sep = path.includes(\"?\") ? \"&\" : \"?\";\n return `${path}${sep}storageId=${encodeURIComponent(storageId)}`;\n };\n\n /**\n * Whether this source sends large files through the resumable endpoint.\n * Turned off for the rest of its life by the first upload that finds it\n * cannot — a server without the endpoint, or several instances that do\n * not share an upload — so only that one upload pays for finding out.\n */\n let resumable = true;\n\n /** One request of a resumable upload, through the transport (its token, its refresh). */\n const sendResumable: ResumableUploadSend = async (method, path, headers, body) => {\n const meta: ResponseMeta = {};\n try {\n await transport.request(`/storage/tus${path}`, { method, headers, ...(body ? { body } : {}) }, meta);\n return { status: meta.status ?? 200, header: (name) => meta.headers?.get(name) ?? null };\n } catch (error: unknown) {\n // An HTTP answer is the protocol's to read; a dropped connection\n // (status 0) is thrown, and the upload asks where it got to.\n if (error instanceof RebaseApiError && typeof error.status === \"number\" && error.status > 0) {\n return { status: error.status, header: () => null, error };\n }\n throw error;\n }\n };\n\n async function putObject({\n file,\n key,\n metadata,\n bucket,\n public: isPublic,\n context\n }: UploadFileProps): Promise<UploadFileResult> {\n const formData = new FormData();\n formData.append(\"file\", file);\n\n // Which property this file is for, so the server can enforce that\n // property's own `maxSize` and `acceptedFiles` — declared in the\n // collection, rendered by the panel's picker, and previously checked by\n // the browser alone. Two plain fields rather than a header: the request\n // is already multipart, and a header would have to survive every proxy\n // between here and the route.\n if (context?.collection && context?.property) {\n formData.append(\"collection\", context.collection);\n formData.append(\"property\", context.property);\n }\n\n // Public objects live under the public prefix so they can be served\n // token-less via a stable, permanent URL. Normalize the key here so the\n // stored path is self-describing (no server round-trip needed to know\n // it's public).\n //\n // With no key, the server names the object after the uploaded file —\n // at the root, which is not the public prefix. So a public upload with\n // no key is given that same name here, prefixed: the key is optional,\n // and `public: true` promised a token-less URL either way. A Blob\n // travels in the form as a file called \"blob\".\n let effectiveKey = key;\n if (isPublic) {\n effectiveKey ||= (file instanceof File && file.name) ? file.name : \"blob\";\n if (!isPublicStoragePath(effectiveKey)) {\n effectiveKey = `${PUBLIC_STORAGE_PREFIX}${effectiveKey.replace(/^\\/+/, \"\")}`;\n }\n }\n\n if (effectiveKey) formData.append(\"key\", effectiveKey);\n if (bucket) formData.append(\"bucket\", bucket);\n if (storageId) formData.append(\"storageId\", storageId);\n\n if (metadata) {\n for (const [key, value] of Object.entries(metadata)) {\n if (value !== undefined && value !== null) {\n formData.append(\n `metadata_${key}`,\n typeof value === \"string\" ? value : JSON.stringify(value)\n );\n }\n }\n }\n\n // A large file goes up a chunk at a time: the multipart request is\n // parsed whole by the server, three to five times the file in its\n // memory. Same fields, so the same object, the same checks and the\n // same triggers — only the transport differs.\n if (resumable && file.size > RESUMABLE_UPLOAD_THRESHOLD) {\n // Fetched here, the first time a file is this large: a client\n // that never uploads one never downloads the code that does it.\n const { uploadResumable } = await import(\"./storage-resumable\");\n const outcome = await uploadResumable({ file, metadata: resumableMetadataOf(formData), send: sendResumable });\n if (\"stored\" in outcome) return outcome.stored;\n resumable = false;\n }\n\n const result = await transport.request<{ data: UploadFileResult }>(withStorageId(\"/storage/upload\"), {\n method: \"POST\",\n body: formData,\n headers: {}\n });\n\n return result.data;\n }\n\n async function getSignedUrl(\n keyOrUrl: string,\n bucket?: string\n ): Promise<DownloadConfig> {\n const cacheKey = bucket ? `${bucket}/${keyOrUrl}` : keyOrUrl;\n // A private file's URL carries a download token the server minted for\n // whoever asked, and this source outlives them: after a sign-out, or\n // another user signing in in the same tab, the next preview of the\n // path was handed the previous user's URL. The credential the request\n // goes out with is the owner — the access token, in cookie mode too\n // (only the refresh token is a cookie) — so a different one empties\n // the cache first. A token refresh empties it as well, which costs one\n // request per file shown afterwards, once per access-token lifetime.\n urlsCache.claim((await transport.resolveToken()) ?? \"\");\n return urlsCache.get(cacheKey, () => resolveDownload(keyOrUrl, bucket));\n }\n\n async function resolveDownload(\n keyOrUrl: string,\n bucket?: string\n ): Promise<ResolvedDownload> {\n const filePath = storageObjectPath(keyOrUrl, bucket);\n\n if (!filePath || filePath.trim() === \"\" || filePath === \"/\") {\n return { config: { url: null, fileNotFound: true }, noStore: true };\n }\n\n // ── Public objects ────────────────────────────────────────────────\n // A public file (under the public prefix) is served token-less via a\n // stable, permanent, CDN-cacheable URL. No metadata round-trip and no\n // token are needed — build the URL directly and cache it forever.\n if (isPublicStoragePath(filePath)) {\n return {\n config: { url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}`) }\n // no expiry\n };\n }\n\n try {\n const metadata = await describe(filePath);\n\n // Public object (server-confirmed): token-less permanent URL.\n if (metadata.public) {\n return {\n config: {\n url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}`),\n metadata\n }\n // no expiry\n };\n }\n\n // Private object: use the short-lived, file-scoped download token\n // minted by the server. We deliberately do NOT fall back to the\n // caller's access token — a URL must never carry a full-privilege\n // credential. If no scoped token is present the URL fails closed.\n const scopedToken = metadata.token;\n const tokenQuery = scopedToken ? `?token=${scopedToken}` : \"\";\n\n const downloadConfig: DownloadConfig = {\n // `withStorageId` picks `?` or `&` based on whether the token\n // query is already present, so the URL stays valid even when\n // there is no token.\n url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}${tokenQuery}`),\n metadata\n };\n\n const expiresAt = metadata.tokenExpiresIn\n ? Date.now() + (metadata.tokenExpiresIn - 10) * 1000 // subtract 10s buffer\n : undefined;\n\n return { config: downloadConfig, expiresAt };\n } catch (e: unknown) {\n if (e instanceof Error && \"status\" in e && (e as { status: number }).status === 404) {\n return { config: { url: null, fileNotFound: true }, noStore: true };\n }\n throw e;\n }\n }\n\n async function getObject(\n key: string,\n bucket?: string\n ): Promise<File | null> {\n const downloadConfig = await getSignedUrl(key, bucket);\n if (downloadConfig.fileNotFound || !downloadConfig.url) {\n return null;\n }\n\n // Fetch using the signed URL directly. Since the scoped token is in the ?token= query param,\n // we explicitly omit any Authorization headers to prevent passing full access tokens to file serving routes.\n const response = await transport.fetchFn(downloadConfig.url, {\n headers: {}\n });\n\n if (response.status === 404) return null;\n // A RebaseApiError with the status, as every other failure here is;\n // a plain Error dropped the status, so a 403 and a 500 read the same.\n if (!response.ok) {\n throw new RebaseApiError(`Failed to get file ${key}: the server answered ${response.status}.`, {\n status: response.status,\n code: defaultCodeForStatus(response.status)\n });\n }\n\n const blob = await response.blob();\n const fileName = (bucket ? `${bucket}/${key}` : key).split(\"/\").pop() || \"file\";\n return new File([blob], fileName, { type: blob.type });\n }\n\n async function deleteObject(\n key: string,\n bucket?: string\n ): Promise<void> {\n const filePath = storageObjectPath(key, bucket);\n\n if (!filePath || filePath.trim() === \"\" || filePath === \"/\") {\n return;\n }\n\n try {\n await transport.request(withStorageId(`/storage/file/${encodeStorageKey(filePath)}`), { method: \"DELETE\" });\n } catch (e: unknown) {\n if (!(e instanceof Error && \"status\" in e && (e as { status: number }).status === 404)) throw e;\n }\n\n urlsCache.delete(bucket ? `${bucket}/${key}` : key);\n }\n\n async function listObjects(\n prefix: string,\n options?: {\n bucket?: string;\n maxResults?: number;\n pageToken?: string;\n }\n ): Promise<StorageListResult> {\n const params = new URLSearchParams();\n if (prefix) params.set(\"prefix\", prefix);\n if (options?.bucket) params.set(\"bucket\", options.bucket);\n if (options?.maxResults) params.set(\"maxResults\", String(options.maxResults));\n if (options?.pageToken) params.set(\"pageToken\", options.pageToken);\n\n if (storageId) params.set(\"storageId\", storageId);\n\n const result = await transport.request<{ data: StorageListResult }>(`/storage/list?${params.toString()}`);\n return result.data;\n }\n\n return {\n putObject,\n getSignedUrl,\n getObject,\n deleteObject,\n listObjects\n };\n}\n","/**\n * Client-side storage source registry.\n *\n * Manages multiple `StorageSource` instances keyed by\n * `StorageSourceDefinition.key`. Collection properties reference\n * a source by key via `StorageConfig.storageSource`.\n *\n * Typical bootstrap flow:\n * 1. Fetch definitions from `GET /api/storage/sources`\n * 2. Build server-backed sources automatically via `createStorage(transport, key)`\n * 3. Register \"direct\" sources manually (e.g. Firebase Storage hook)\n */\n\nimport type { StorageSource, StorageSourceRegistry, StorageSourceDefinition } from \"@rebasepro/types\";\nimport { DEFAULT_STORAGE_SOURCE_KEY } from \"@rebasepro/types\";\nimport { createStorage } from \"./storage\";\nimport type { Transport } from \"./transport\";\n\n/**\n * Default implementation of the client-side `StorageSourceRegistry`.\n */\nexport class ClientStorageSourceRegistry implements StorageSourceRegistry {\n private sources = new Map<string, StorageSource>();\n\n /**\n * Register a storage source.\n * @param key - Unique key matching a `StorageSourceDefinition.key`\n * @param source - The `StorageSource` instance\n */\n register(key: string, source: StorageSource): void {\n this.sources.set(key, source);\n }\n\n getDefault(): StorageSource {\n const source = this.sources.get(DEFAULT_STORAGE_SOURCE_KEY);\n if (!source) {\n throw new Error(\n `[StorageSourceRegistry] No default storage source registered. ` +\n `Register one with key \"${DEFAULT_STORAGE_SOURCE_KEY}\".`\n );\n }\n return source;\n }\n\n get(key: string | undefined | null): StorageSource | undefined {\n if (key === undefined || key === null) {\n return this.sources.get(DEFAULT_STORAGE_SOURCE_KEY);\n }\n return this.sources.get(key);\n }\n\n getOrDefault(key: string | undefined | null): StorageSource {\n if (key === undefined || key === null) {\n return this.getDefault();\n }\n const source = this.sources.get(key);\n if (source) return source;\n\n // Not the default source: a key that names another is another bucket,\n // and answering the default wrote that source's files into the wrong\n // one — the fallback the server registry removed. The default only for\n // a call that names no source.\n throw new Error(\n `[StorageSourceRegistry] Storage source \"${key}\" is not registered. ` +\n `Registered: ${this.list().map(k => `\"${k}\"`).join(\", \") || \"(none)\"}.`\n );\n }\n\n has(key: string): boolean {\n return this.sources.has(key);\n }\n\n list(): string[] {\n return Array.from(this.sources.keys());\n }\n\n /**\n * Build a registry from `StorageSourceDefinition[]` and an HTTP transport.\n *\n * - Sources with `transport: \"server\"` are auto-wired via `createStorage(transport, key)`.\n * - Sources with `transport: \"direct\"` are **not** auto-wired — they must\n * be registered manually after this call (e.g. via a Firebase hook).\n *\n * @param definitions - Array of storage source definitions\n * @param transport - HTTP transport for server-backed sources\n */\n static fromDefinitions(\n definitions: StorageSourceDefinition[],\n transport: Transport\n ): ClientStorageSourceRegistry {\n const registry = new ClientStorageSourceRegistry();\n\n for (const def of definitions) {\n if (def.transport === \"server\") {\n // Auto-create a server-backed StorageSource for this key\n const source = createStorage(transport, def.key === DEFAULT_STORAGE_SOURCE_KEY ? undefined : def.key);\n registry.register(def.key, source);\n }\n // \"direct\" sources must be registered manually\n }\n\n return registry;\n }\n}\n","import type { SqlScriptColumn, SqlScriptNotice, SqlScriptResult, SqlScriptTable } from \"@rebasepro/types\";\n\n/**\n * Read an `EXECUTE_SQL_SUCCESS` payload for a script run as a\n * {@link SqlScriptResult}, checking every field rather than trusting the frame.\n *\n * A server from before scripts existed answers `{ result }` alone, with parsed\n * values. Its rows are kept, as text, and no column is said to come from\n * anywhere — the console edits no cell of a result it cannot trace.\n */\nexport function readSqlScriptResult(payload: unknown): SqlScriptResult {\n const record = asRecord(payload);\n const rows = Array.isArray(record?.result) ? record.result.map(asRecord).filter(isDefined).map(textRow) : [];\n const columns = Array.isArray(record?.columns)\n ? record.columns.map(readColumn).filter(isDefined)\n : Object.keys(rows[0] ?? {}).map((name): SqlScriptColumn => ({ name }));\n const tables = Array.isArray(record?.tables) ? record.tables.map(readTable).filter(isDefined) : [];\n const notices = Array.isArray(record?.notices) ? record.notices.map(readNotice).filter(isDefined) : [];\n const result: SqlScriptResult = { rows, columns, tables, notices };\n if (typeof record?.command === \"string\") result.command = record.command;\n if (typeof record?.rowCount === \"number\") result.rowCount = record.rowCount;\n if (record?.truncated === true) result.truncated = true;\n if (typeof record?.effectiveRole === \"string\") result.effectiveRole = record.effectiveRole;\n return result;\n}\n\nfunction isDefined<T>(value: T | undefined): value is T {\n return value !== undefined;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? Object.fromEntries(Object.entries(value))\n : undefined;\n}\n\nfunction textRow(row: Record<string, unknown>): Record<string, string | null> {\n return Object.fromEntries(Object.entries(row).map(([key, value]) => [key, asText(value)]));\n}\n\nfunction asText(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"string\") return value;\n if (typeof value === \"object\") return JSON.stringify(value);\n return String(value);\n}\n\nfunction readColumn(value: unknown): SqlScriptColumn | undefined {\n const record = asRecord(value);\n if (typeof record?.name !== \"string\") return undefined;\n const column: SqlScriptColumn = { name: record.name };\n if (typeof record.type === \"string\") column.type = record.type;\n const source = asRecord(record.source);\n if (typeof source?.schema === \"string\" && typeof source.table === \"string\" && typeof source.column === \"string\") {\n column.source = { schema: source.schema, table: source.table, column: source.column };\n }\n return column;\n}\n\nconst TABLE_KINDS: readonly SqlScriptTable[\"kind\"][] = [\"table\", \"partitioned table\", \"view\", \"materialized view\", \"foreign table\", \"other\"];\n\nfunction readTable(value: unknown): SqlScriptTable | undefined {\n const record = asRecord(value);\n if (typeof record?.schema !== \"string\" || typeof record.table !== \"string\") return undefined;\n const kind = TABLE_KINDS.find(candidate => candidate === record.kind) ?? \"other\";\n const primaryKey = Array.isArray(record.primaryKey) && record.primaryKey.every((column): column is string => typeof column === \"string\")\n ? record.primaryKey\n : [];\n return {\n schema: record.schema,\n table: record.table,\n kind,\n primaryKey,\n // Unknown reads as the unsafe answer: a table whose rows may live in\n // tables that inherit it is not one a key finds a single row in.\n hasInheritors: record.hasInheritors !== false\n };\n}\n\nfunction readNotice(value: unknown): SqlScriptNotice | undefined {\n const record = asRecord(value);\n if (typeof record?.message !== \"string\") return undefined;\n return { severity: typeof record.severity === \"string\" ? record.severity : \"NOTICE\", message: record.message };\n}\n","import {\n DeleteProps,\n CollectionConfig,\n FetchCollectionProps,\n ListenCollectionProps,\n FetchOneProps,\n ListenOneTarget,\n SaveProps,\n WebSocketMessage,\n WebSocketErrorPayload,\n CollectionUpdateMessage,\n CollectionUpdateMeta,\n SingleUpdateMessage,\n ChannelMessage,\n TableMetadata,\n BranchInfo,\n RebaseApiError,\n type RealtimeConnectionState\n} from \"@rebasepro/types\";\nimport { buildCompositeId, COMPOSITE_ID_SEPARATOR, warnDeprecated, withETag, type PrimaryKeyInfo } from \"@rebasepro/common\";\nimport { parseRebaseJson } from \"./reviver\";\nimport { unref } from \"@rebasepro/utils\";\nimport { readSqlScriptResult } from \"./sql-script\";\nimport type { SqlScriptResult } from \"@rebasepro/types\";\n\n\n\n/**\n * Extract error message and code from a WebSocket message payload.\n * Handles both `{ error: string }` and `{ error: { message, code } }` shapes.\n */\nfunction extractMessageError(message: WebSocketMessage): { errorMessage: string; errorCode?: string } {\n const payload = message.payload as WebSocketErrorPayload | undefined;\n const errPayload = payload?.error;\n const errorMessage = typeof errPayload === \"object\"\n ? errPayload.message\n : payload?.message || (typeof errPayload === \"string\" ? errPayload : undefined) || message.error || \"Unknown error\";\n const errorCode = typeof errPayload === \"object\"\n ? errPayload.code\n : payload?.code;\n // Callers treat this as a string (`.toLowerCase()` in isAuthError). A frame\n // carrying a non-string here would throw inside the message handler, where\n // the surrounding try/catch would swallow it — and a subscription error that\n // never reaches its listener is a view stuck loading forever.\n const safeMessage = typeof errorMessage === \"string\"\n ? errorMessage\n : (errorMessage == null ? \"Unknown error\" : JSON.stringify(errorMessage));\n return { errorMessage: safeMessage,\nerrorCode };\n}\n\n/**\n * The error an `ERROR` frame describes, with the server's `details` when it\n * sent them — `{ stage, path }` for a collection-callback refusal, the object\n * the REST envelope carries for the same veto. `status` stays `undefined`:\n * a frame is not an HTTP response (see `RebaseErrorInit.status`).\n */\nfunction frameError(message: WebSocketMessage): RebaseApiError {\n const { errorMessage, errorCode } = extractMessageError(message);\n const errPayload = (message.payload as WebSocketErrorPayload | undefined)?.error;\n const details = typeof errPayload === \"object\" && errPayload !== null ? errPayload.details : undefined;\n return new RebaseApiError(errorMessage, { code: errorCode, details });\n}\n\n/**\n * A request whose socket closed after it was written and before the server\n * answered. The server may have run it. It is not re-sent — see `onclose`.\n */\nfunction connectionLostError(): RebaseApiError {\n return new RebaseApiError(\n \"The realtime connection closed before the server answered. The request may or may not have been applied, and it was not sent again.\",\n { code: \"CONNECTION_LOST\" }\n );\n}\n\n/**\n * A request that got no answer within `requestTimeoutMs` of the call — whether\n * it waited in the queue for a socket the whole time, or was written and never\n * answered (a half-open connection looks exactly like that).\n */\nfunction requestTimeoutError(written: boolean): RebaseApiError {\n return new RebaseApiError(\n written\n ? \"The server did not answer the realtime request in time. It may or may not have been applied.\"\n : \"The realtime request timed out before it could be sent. It was not applied.\",\n { code: \"REQUEST_TIMEOUT\" }\n );\n}\n\n/**\n * What a live subscription or a joined channel is told, once, when the\n * connection has been down for longer than a blip. The registration is kept and\n * resumes when the socket is back.\n */\nfunction realtimeLostError(refusal?: RebaseApiError): RebaseApiError {\n if (refusal) {\n return new RebaseApiError(\n `The server refused this realtime connection (${refusal.code}: ${refusal.message}), so live data is not ` +\n \"updating. The client keeps retrying, more slowly each time, and refreshes this subscription once it is let in.\",\n { code: \"CONNECTION_LOST\", details: { reason: refusal.code }, cause: refusal }\n );\n }\n return new RebaseApiError(\n \"The realtime connection is down, so live data is not updating. The client keeps reconnecting and refreshes this subscription when the connection is back.\",\n { code: \"CONNECTION_LOST\" }\n );\n}\n\n/**\n * Whether `error` is the server refusing this socket for its per-user ceiling\n * (`REALTIME_MAX_SOCKETS_PER_USER`): it answers AUTHENTICATE with\n * `TOO_MANY_SOCKETS` and closes the socket.\n */\nfunction isSocketCeilingRefusal(error: unknown): error is RebaseApiError {\n return error instanceof RebaseApiError && error.code === \"TOO_MANY_SOCKETS\";\n}\n\n/**\n * Whether realtime debugging is on: `globalThis.__REBASE_DEBUG_REALTIME__ = true`,\n * set from a console or a test. While it is, each refetched row that differs\n * from the one held is explained key by key with `console.debug`.\n */\nfunction realtimeDebugOn(): boolean {\n return Reflect.get(globalThis, \"__REBASE_DEBUG_REALTIME__\") === true;\n}\n\n/**\n * The `localStorage` key that turns connection logging on for one browser,\n * without a code change or a rebuild: `localStorage.setItem(\"rebase_debug\", \"1\")`\n * in its console. For a page that loads the client as a plain script, where\n * `createRebaseClient({ debug: true })` means editing and redeploying the site.\n */\nconst DEBUG_STORAGE_KEY = \"rebase_debug\";\n\n/**\n * Whether connection logging was switched on from outside the code:\n * `globalThis.__REBASE_DEBUG__`, `globalThis.__REBASE_DEBUG_REALTIME__` (which\n * also explains refetched rows), or {@link DEBUG_STORAGE_KEY} in this\n * browser's `localStorage`. Read at each line, so it can be switched on in a\n * console without reloading a page whose socket is already open.\n */\nfunction debugSwitchedOn(): boolean {\n if (Reflect.get(globalThis, \"__REBASE_DEBUG__\") === true || realtimeDebugOn()) return true;\n // Only a browser's: Node 25 has a `localStorage` of its own, which warns\n // when it is touched without `--localstorage-file`.\n if (typeof window === \"undefined\" || typeof localStorage === \"undefined\") return false;\n try {\n const value = localStorage.getItem(DEBUG_STORAGE_KEY);\n return value === \"1\" || value === \"true\";\n } catch {\n // Storage refused: a sandboxed frame, blocked site data.\n return false;\n }\n}\n\n/**\n * The codes an `AUTHENTICATE` is refused with when the server will not honour\n * the session: the token does not verify or has expired, the session was\n * revoked or its account is gone (`SESSION_REVOKED`), the account is disabled\n * (`ACCOUNT_DISABLED`) — the codes the HTTP routes answer the same token with.\n * An answer, not a failure: asking again with the same token gets the same one.\n */\nconst SESSION_REFUSAL_CODES = new Set([\"INVALID_TOKEN\", \"TOKEN_EXPIRED\", \"JWT_EXPIRED\", \"SESSION_REVOKED\", \"ACCOUNT_DISABLED\", \"SESSION_ENDED\"]);\n\nfunction isSessionRefusal(error: unknown): error is RebaseApiError {\n return error instanceof RebaseApiError && typeof error.code === \"string\" && SESSION_REFUSAL_CODES.has(error.code);\n}\n\n/** The ceiling of the reconnect backoff. */\nconst MAX_RECONNECT_DELAY_MS = 30_000;\n\n/**\n * The shortest reconnect delay. Each delay is drawn from the whole of its step\n * (full jitter), so without a floor a redial could land the instant the socket\n * dropped — before a restarting server has any chance of being back.\n */\nconst MIN_RECONNECT_DELAY_MS = 250;\n\n/**\n * How long the connection is down before the outage is reported: every\n * registration is told `CONNECTION_LOST` and {@link RebaseWebSocketClient.state}\n * reads `disconnected`. The first three steps of the backoff, 2 + 4 + 8 s.\n *\n * Measured from the drop rather than counted in redials: with full jitter,\n * three redials can all fail within the first second, and a server restart\n * that short is a blip, not an outage.\n */\nconst OUTAGE_REPORT_AFTER_MS = 14_000;\n\n/**\n * What this client opts into when it opens the socket, named on the URL as\n * `?rebase_caps=` — the server's `socket-opt-ins.ts` says what each one does.\n * A server that knows them answers with a `hello` listing the ones it will\n * honour; an older one ignores the parameter, sends no `hello`, and the\n * client goes on as it did before there were any.\n */\nconst SOCKET_OPT_IN_PARAM = \"rebase_caps\";\nconst SOCKET_OPT_INS = [\"heartbeat\", \"channel_seq\", \"cancel_sql\"];\n\n/** Slack on top of two heartbeat intervals before a silent socket is given up on. */\nconst HEARTBEAT_GRACE_MS = 5_000;\n\n/**\n * `url` with the opt-ins named on it. Appended rather than rebuilt through\n * `URL`, which would rewrite the address the caller gave (a bare origin gains\n * a `/`).\n */\nfunction withOptIns(url: string): string {\n return `${url}${url.includes(\"?\") ? \"&\" : \"?\"}${SOCKET_OPT_IN_PARAM}=${SOCKET_OPT_INS.join(\",\")}`;\n}\n\n/** A frame waiting in `messageQueue`, carrying the handlers of the promise its caller holds. */\ntype QueuedFrame = Record<string, unknown> & {\n _queuedResolve?: (value: unknown) => void;\n _queuedReject?: (error: Error) => void;\n};\n\nexport interface RebaseWebSocketConfig {\n websocketUrl: string;\n /**\n * Where the token the socket authenticates with comes from, asked each\n * time it authenticates. A falsy answer leaves it anonymous.\n */\n getAuthToken?: () => Promise<string | null>;\n /** Optional WebSocket constructor to override globalThis.WebSocket (e.g. for Node environments) */\n WebSocket?: typeof WebSocket;\n /** Callback to handle unauthorized requests or token expiration (refreshes auth session) */\n onUnauthorized?: () => Promise<boolean>;\n /**\n * Sign in as this user rather than as the token's own — the socket half of\n * `RebaseClientConfig.impersonate`. Sent with every `AUTHENTICATE`.\n */\n impersonate?: string;\n /**\n * Log the connection's progress — opened, signed in, closed, redialling —\n * with `console.debug`. Off by default. See `CreateRebaseClientOptions.debug`.\n */\n debug?: boolean;\n}\n\n\n/**\n * Broadcast and presence frames.\n *\n * Fire-and-forget (the server sends no response envelope), and exempt from the\n * client-side auth gate — a public channel is usable without an account.\n */\nconst CHANNEL_MESSAGE_TYPES = new Set([\n \"join_channel\",\n \"leave_channel\",\n \"broadcast\",\n \"presence_track\",\n \"presence_untrack\",\n \"presence_state\",\n // The catch-up request. Like `presence_state`, its answer comes back as a\n // channel-addressed frame rather than a response envelope, so it must not\n // be given a pending request to wait on.\n \"channel_history\"\n]);\n\n/**\n * Subscription frames. Like channel frames they get no response envelope, and\n * like them they go out without an account when there is none: whether an\n * anonymous visitor may subscribe is the server's `requireAuth` to decide, and\n * a server that refuses answers the frame, which reaches the listener's\n * `onError`.\n */\nconst SUBSCRIPTION_MESSAGE_TYPES = new Set([\n \"subscribe_collection\",\n \"subscribe_one\",\n \"unsubscribe\"\n]);\n\n/**\n * The token getter's \"there is no account\" — an empty token, or its own\n * \"not logged in\". Distinct from a getter that failed, which says nothing about\n * whether there is one.\n */\nfunction isNotSignedIn(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return message.includes(\"not logged in\");\n}\n\n/**\n * The other direction: the frame types the *server* addresses by channel.\n *\n * Not the mirror of {@link CHANNEL_MESSAGE_TYPES} — a request and its answer\n * have different names (`presence_state` is the one type in both) — so the two\n * sets are listed separately rather than derived from each other. These are\n * exactly the members of `ChannelMessage`; adding one here without declaring it\n * there leaves the handler with a frame it cannot read.\n */\nconst CHANNEL_FRAME_TYPES = new Set([\n \"broadcast\",\n // The number the sender's own retained broadcast took (`channel_seq`).\n \"broadcast_ack\",\n \"presence_state\",\n \"presence_diff\",\n \"channel_history\",\n // A refusal about a channel. Fire-and-forget frames have no pending request\n // to reject, so this is the only way one becomes visible to the caller.\n \"error\",\n \"ERROR\"\n]);\n\n/**\n * Low-level realtime WebSocket client.\n *\n * @internal Not a stable app-facing API. `createRebaseClient()` constructs and\n * manages this internally (exposed as `client.ws`, typed by the minimal\n * `RebaseWebSocket` contract in `@rebasepro/types`). It stays exported from the\n * package root for the same reason it always was — a data-source driver may\n * instantiate it directly — but nothing in this repo does since\n * `@rebasepro/client-postgres` was removed; its surface may change without a\n * major bump.\n */\n/**\n * Narrow a frame to the collection-update shape. See its use below.\n *\n * `WebSocketMessage.type` is `string`, so the message types that extend it do\n * not form a discriminated union and `type === \"collection_update\"` narrows\n * nothing on its own.\n */\nfunction isCollectionUpdate(message: WebSocketMessage): message is CollectionUpdateMessage {\n return message.type === \"collection_update\";\n}\n\n/**\n * Narrow a frame to one of the channel-addressed shapes.\n *\n * Both halves of the test are load-bearing. The type says which member of\n * {@link ChannelMessage} it is; `channel` is what a channel frame is *addressed\n * by*, and an `ERROR` from a server older than the change that names the\n * channel has none — it is a generic error, and falls through to the catch-all\n * warning rather than being delivered to a channel that was never named.\n */\nfunction isChannelMessage(message: WebSocketMessage): message is ChannelMessage {\n return typeof message.channel === \"string\" && CHANNEL_FRAME_TYPES.has(message.type);\n}\n\nexport class RebaseWebSocketClient {\n private websocketUrl: string;\n private ws: WebSocket | null = null;\n /**\n * Where the socket's token comes from. A client built by\n * `createRebaseClient()` resolves it through the HTTP transport, so\n * `client.setAuthTokenGetter()` and `client.setToken()` reach both.\n */\n public getAuthToken?: () => Promise<string | null>;\n private listeners = new Map<string, Set<(...args: unknown[]) => void>>();\n\n /** Channel-name → handlers, for broadcast and presence frames. */\n private channelHandlers = new Map<string, Set<(message: ChannelMessage) => void>>();\n\n /** Set by `close()`. Blocks any later operation from silently redialling. */\n private closedByCaller = false;\n\n /**\n * Set when the backoff budget ran out with nothing registered, cleared by\n * anything that earns a fresh one.\n *\n * Only reachable with nothing registered: while a subscription or a joined\n * channel exists the client never stops retrying (see `attemptReconnect`).\n * And it is not final like {@link closedByCaller} — nobody *asked* for the\n * socket to stay down, so the next subscribe dials again.\n */\n private gaveUp = false;\n\n /** See {@link state}. */\n private connectionState: RealtimeConnectionState = \"idle\";\n private stateListeners = new Set<(state: RealtimeConnectionState) => void>();\n\n /**\n * Whether the current outage has been reported to the registrations as\n * `CONNECTION_LOST`. Once per outage: cleared when a socket opens.\n */\n private outageReported = false;\n\n /**\n * Where the connection is.\n *\n * - `idle` — no socket, and none wanted yet (the connection is lazy), or\n * the last one was dropped by a sign-out.\n * - `connecting` — dialling, with no outage in progress.\n * - `connected` — the socket is open.\n * - `reconnecting` — the socket dropped and the client is redialling. A\n * blip, so far: nothing has been reported.\n * - `disconnected` — the outage has outlasted the blip budget. Every\n * subscription and joined channel has been told `CONNECTION_LOST` once;\n * the client keeps redialling (every 30 s at most) while any exists.\n * - `closed` — `client.close()`. Final.\n */\n public get state(): RealtimeConnectionState {\n return this.connectionState;\n }\n\n /** Called on every change of {@link state}. Returns the unsubscribe. */\n public onStateChange(listener: (state: RealtimeConnectionState) => void): () => void {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n private setState(next: RealtimeConnectionState): void {\n if (next === this.connectionState) return;\n this.connectionState = next;\n for (const listener of [...this.stateListeners]) {\n try {\n listener(next);\n } catch (error) {\n console.error(\"Error in realtime state listener:\", error);\n }\n }\n }\n\n /**\n * Whether a socket exists at all (open or still opening).\n *\n * Lets callers distinguish \"authenticate the live socket\" from \"there is\n * nothing to authenticate yet\", without that question forcing a dial.\n */\n public get hasSocket(): boolean {\n return this.ws !== null;\n }\n\n /** So the \"no WebSocket in this environment\" warning is said once, not per call. */\n private warnedNoWebSocket = false;\n\n /**\n * What the server said in its `hello` it does for this socket: empty\n * until then, after a close, and for a server that sends no `hello`.\n */\n private serverOptIns: ReadonlySet<string> = new Set();\n /** This socket's id on the server, from its `hello` (with `channel_seq`). */\n private serverClientId: string | undefined;\n /** When the current socket last delivered anything, heartbeats included. */\n private lastFrameAt = 0;\n private livenessTimer: ReturnType<typeof setInterval> | null = null;\n /** Give up on the current socket as though it had closed. Set per socket. */\n private dropSocket: (() => void) | null = null;\n\n /** Whether the server agreed, in its `hello`, to `name` for this socket. */\n public hasServerOptIn(name: string): boolean {\n return this.serverOptIns.has(name);\n }\n\n /**\n * This socket's id on the server, as its broadcasts' `senderId` in a\n * channel's history. Known once a server that numbers retained channels\n * for it (`channel_seq`) has said hello.\n */\n public get socketClientId(): string | undefined {\n return this.serverClientId;\n }\n\n /** Subscribe to broadcast/presence frames for one channel. */\n public onChannelMessage(channel: string, handler: (message: ChannelMessage) => void): () => void {\n if (!this.channelHandlers.has(channel)) this.channelHandlers.set(channel, new Set());\n this.channelHandlers.get(channel)!.add(handler);\n return () => {\n const handlers = this.channelHandlers.get(channel);\n if (!handlers) return;\n handlers.delete(handler);\n if (handlers.size === 0) this.channelHandlers.delete(channel);\n };\n }\n\n /** Notified after the socket comes back, so channels can re-join. */\n public onReconnect(handler: () => void): () => void {\n return this.on(\"reconnect\", handler);\n }\n\n /**\n * Notified once per outage that outlasted a blip, with the\n * `CONNECTION_LOST` error — so a joined channel can tell its `onError`\n * handlers, as subscriptions are told through theirs.\n */\n public onConnectionLost(handler: (error: RebaseApiError) => void): () => void {\n this.connectionLostHandlers.add(handler);\n return () => {\n this.connectionLostHandlers.delete(handler);\n };\n }\n\n private connectionLostHandlers = new Set<(error: RebaseApiError) => void>();\n\n public on(event: \"connect\" | \"disconnect\" | \"reconnect\" | \"error\", cb: (...args: unknown[]) => void) {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(cb);\n return () => this.listeners.get(event)!.delete(cb);\n }\n\n private emit(event: string, ...args: unknown[]) {\n if (this.listeners.has(event)) {\n this.listeners.get(event)!.forEach(cb => cb(...args));\n }\n }\n\n // New: Subscription deduplication management with optimizations\n private collectionSubscriptions = new Map<string, {\n backendSubscriptionId: string;\n callbacks: Map<string, {\n onUpdate: (rows: Record<string, unknown>[], meta?: CollectionUpdateMeta) => void;\n onError?: (error: Error) => void;\n }>;\n props: Omit<ListenCollectionProps, \"onUpdate\" | \"onError\">;\n latestData?: Record<string, unknown>[]; // Cache the latest flat rows\n /**\n * The `meta` of the last frame — total, hasMore, nextCursor.\n *\n * Cached beside the rows so a listener attaching to an existing\n * subscription is replayed both, rather than the rows alone and a\n * `meta` it has to go and fetch.\n */\n latestMeta?: CollectionUpdateMeta;\n lastUpdated?: number; // Timestamp for cache invalidation\n isInitialDataReceived?: boolean; // Track if we got initial data\n /**\n * A `subscribe_collection` frame is on the wire and its initial payload\n * has not arrived yet. Without this, a subscription whose subscribe\n * failed is indistinguishable from one still loading, and every later\n * listener attaches to it and waits forever.\n */\n subscribeInFlight?: boolean;\n /**\n * Watchdog for the above. `subscribe_collection` expects no response\n * envelope, so it is not covered by `pendingRequests`' timeout — a lost\n * initial payload would otherwise hang the subscription indefinitely.\n */\n subscribeTimeout?: ReturnType<typeof setTimeout>;\n /**\n * The key columns of this collection, as told by the server on a patch.\n * Rows are columns only, and the SDK holds no collection config, so\n * without this there is nothing to derive an address from.\n */\n pks?: PrimaryKeyInfo[];\n }>();\n\n private singleSubscriptions = new Map<string, {\n backendSubscriptionId: string;\n callbacks: Map<string, {\n onUpdate: (row: Record<string, unknown> | null) => void;\n onError?: (error: Error) => void;\n }>;\n props: ListenOneTarget;\n latestData?: Record<string, unknown> | null; // Cache the latest flat row\n lastUpdated?: number; // Timestamp for cache invalidation\n isInitialDataReceived?: boolean; // Track if we got initial data\n /** See the collection subscription counterparts. */\n subscribeInFlight?: boolean;\n subscribeTimeout?: ReturnType<typeof setTimeout>;\n }>();\n\n // Maps to quickly find subscription by backend subscription ID\n private backendToCollectionKey = new Map<string, string>();\n private backendToEntityKey = new Map<string, string>();\n\n\n private pendingRequests = new Map<string, {\n resolve: (p: unknown) => void;\n reject: (p: Error) => void;\n message?: Record<string, unknown> & { _queuedResolve?: (p: unknown) => void; _queuedReject?: (p: Error) => void }\n }>();\n private reconnectAttempts = 0;\n /**\n * With nothing registered, how many failed redials before the client stops\n * trying until something asks for the connection. With a registration it\n * never stops.\n */\n private maxReconnectAttempts = 5;\n /**\n * Reports the outage once the connection has been down for\n * {@link OUTAGE_REPORT_AFTER_MS}. Armed by the first redial after a drop,\n * cleared when a socket opens or the caller disconnects.\n */\n private outageTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * Whether a socket of this client has ever opened — what makes the next\n * open a *reconnect*, whose server has forgotten every subscription and\n * channel membership and must be told them again.\n *\n * Not `reconnectAttempts > 0`: that counter is a backoff budget, and a\n * fresh one (after giving up, or after a sign-out dropped the socket)\n * starts at zero, so the socket that finally came back looked like a first\n * connect and nothing was re-sent.\n */\n private hadConnection = false;\n private isConnected = false;\n private messageQueue: Record<string, unknown>[] = [];\n private requestTimeoutMs = 30000;\n private subscriptionTimeoutMs = 30000;\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null;\n\n private isAuthenticated = false;\n private authPromise: Promise<void> | null = null;\n private WebSocketConstructor: typeof WebSocket | undefined;\n public onUnauthorized?: () => Promise<boolean>;\n private refreshInProgress: Promise<boolean> | null = null;\n private readonly impersonate?: string;\n private readonly debug: boolean;\n\n /**\n * Connection chatter: the socket opened, signed in, closed, is redialling.\n *\n * Silent unless asked for, with the `debug` option or\n * {@link debugSwitchedOn}. It used to go to `console.debug` in every\n * browser: a public page printed \"Connected to PostgreSQL backend\" and\n * \"WebSocket auto-authenticated\" to each visitor's console, naming the\n * backend on every load. In Node, where `console.debug` is `console.log`,\n * it opened a script's stdout ahead of what the script prints. Warnings and\n * errors are not chatter and are always written.\n */\n private connectionDebug(...args: unknown[]): void {\n if (!this.debug && !debugSwitchedOn()) return;\n console.debug(...args);\n }\n\n constructor(config: RebaseWebSocketConfig) {\n this.websocketUrl = config.websocketUrl;\n this.getAuthToken = config.getAuthToken;\n this.onUnauthorized = config.onUnauthorized;\n this.impersonate = config.impersonate;\n this.debug = config.debug === true;\n this.WebSocketConstructor = config.WebSocket || (typeof WebSocket !== \"undefined\" ? WebSocket : undefined);\n\n // Deliberately does NOT dial here. Constructing the client is not a\n // statement that the app wants a socket — `createRebaseClient` builds\n // one whenever realtime is not explicitly disabled, so connecting here\n // opened a socket on every page load of every app that merely *might*\n // subscribe later. Anonymous-first apps paid that on every visit, to\n // authenticate with nothing, which left them choosing between \"socket\n // on every page load\" and \"no channels at all\".\n //\n // The environment warning is also deferred: an app that never\n // subscribes should say nothing at all. See `ensureConnected`.\n }\n\n /**\n * Open the socket if it is not open (or opening) already.\n *\n * Idempotent, synchronous, and safe to call on every operation that needs a\n * live socket — `initWebSocket` already no-ops on an open socket and is\n * re-entrant, since the reconnect path has always called it.\n */\n public ensureConnected(): void {\n // An explicit `close()` is final. Without this, one queued frame could\n // redial a socket the caller just released and keep a Node process\n // alive forever.\n if (this.closedByCaller) return;\n if (!this.WebSocketConstructor) {\n if (!this.warnedNoWebSocket) {\n this.warnedNoWebSocket = true;\n console.warn(\"WebSocket is not defined in this environment. Realtime subscriptions will not work unless you provide a WebSocket implementation in the config.\");\n }\n return;\n }\n this.installNetworkListeners();\n if (this.ws || this.reconnectTimeout) return;\n // A caller asking for a connection is a fresh reason to try, so it also\n // buys a fresh backoff budget. Without this, the first `subscribe`\n // after a give-up would exhaust the counter on attempt one.\n if (this.gaveUp) {\n this.gaveUp = false;\n this.reconnectAttempts = 0;\n this.outageReported = false;\n }\n // A fresh dial — not a retry inside an outage, which keeps its state.\n if (!this.outageReported) this.setState(\"connecting\");\n this.initWebSocket();\n }\n\n /**\n * Dial now rather than at the next backoff step — the browser says the\n * network is back, or the tab is in front of the user again. Both are the\n * usual end of an outage; neither fires for a server restart, which the\n * backoff covers.\n */\n private retryNow(reason: string): void {\n if (this.closedByCaller) return;\n if (this.connectionState !== \"reconnecting\" && this.connectionState !== \"disconnected\") return;\n // A dial is already on its way.\n if (this.ws) return;\n this.connectionDebug(`${reason} — retrying the realtime connection`);\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n this.gaveUp = false;\n this.reconnectAttempts = 0;\n this.initWebSocket();\n }\n\n /**\n * `online` and `visibilitychange`. Registered lazily so a Node client, or a\n * page that never subscribes, adds no listener.\n */\n private installNetworkListeners() {\n if (this.onlineListener || typeof window === \"undefined\" || typeof window.addEventListener !== \"function\") return;\n this.onlineListener = () => this.retryNow(\"Network is back\");\n window.addEventListener(\"online\", this.onlineListener);\n if (typeof document !== \"undefined\" && typeof document.addEventListener === \"function\") {\n this.visibilityListener = () => {\n if (document.visibilityState === \"visible\") this.retryNow(\"Tab is visible again\");\n };\n document.addEventListener(\"visibilitychange\", this.visibilityListener);\n }\n }\n\n private removeNetworkListeners() {\n if (this.onlineListener && typeof window !== \"undefined\") {\n window.removeEventListener(\"online\", this.onlineListener);\n }\n if (this.visibilityListener && typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", this.visibilityListener);\n }\n this.onlineListener = null;\n this.visibilityListener = null;\n }\n\n private onlineListener: (() => void) | null = null;\n private visibilityListener: (() => void) | null = null;\n\n /** The session this socket's server refused, and why. Cleared when a socket opens. */\n private refusedSession: { token: string; error: RebaseApiError } | null = null;\n\n /**\n * Whether `error` is the server refusing the session `token` signs in as —\n * revoked, its account gone or disabled, a token that does not verify. Said\n * once per socket and token, as a warning that names the code.\n */\n private noteSessionRefused(token: string, error: unknown): boolean {\n if (!isSessionRefusal(error)) return false;\n if (this.refusedSession?.token !== token) {\n this.refusedSession = { token, error };\n console.warn(\n `[Rebase] Realtime: the server refused this session (${error.code}: ${error.message}). ` +\n \"The socket is connected but not signed in, and what needs an account is refused with that code.\"\n );\n }\n return true;\n }\n\n /** Whether the current run of ceiling refusals has been said in the console. */\n private socketCeilingWarned = false;\n\n /**\n * The server refused this socket for its per-user ceiling. Said once per\n * run of refusals, as a warning, and reported to every registration at\n * once as an outage — a refusal is not a blip that the next dial fixes.\n */\n private noteSocketCeiling(error: RebaseApiError): void {\n if (!this.socketCeilingWarned) {\n this.socketCeilingWarned = true;\n console.warn(\n `[Rebase] Realtime: the server refused this connection (${error.code}: ${error.message}). ` +\n \"Live data is not updating; the client retries, more slowly each time, until another connection closes.\"\n );\n }\n this.clearOutageTimer();\n this.reportConnectionLost(error);\n }\n\n /**\n * Authenticate the WebSocket connection\n */\n async authenticate(token: string): Promise<void> {\n return new Promise((resolve, reject) => {\n // Random suffix, like every other request id here. Two auth\n // attempts started in the same millisecond produced the same id,\n // and `pendingRequests` is a Map: the second registration replaced\n // the first, so one caller's promise was never settled either way.\n const requestId = `auth_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n const timeout = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n // `authPromise` belongs to `ensureAuthenticated`, which clears\n // it when the whole attempt — retries included — settles.\n // Clearing it here let a second attempt start while this one\n // was still retrying.\n reject(new Error(\"Authentication timeout\"));\n }, 30000);\n\n this.pendingRequests.set(requestId, {\n resolve: () => {\n clearTimeout(timeout);\n this.isAuthenticated = true;\n resolve();\n },\n reject: (error) => {\n clearTimeout(timeout);\n reject(error);\n }\n });\n\n const message = {\n type: \"AUTHENTICATE\",\n requestId,\n payload: this.impersonate !== undefined ? { token, impersonate: this.impersonate } : { token }\n };\n\n if (!this.isConnected || !this.ws) {\n this.messageQueue.unshift(message); // Auth should be first\n } else {\n this.ws.send(JSON.stringify(message));\n }\n });\n }\n\n /**\n * Replace where this socket's token comes from, and authenticate an open,\n * unauthenticated socket with it.\n *\n * @deprecated Use `client.setAuthTokenGetter()` instead — on a client from\n * `createRebaseClient()` the socket already resolves its token through it,\n * together with HTTP, and setting one here splits the two. Will be removed\n * in a future release.\n */\n setAuthTokenGetter(getAuthToken: () => Promise<string | null>): void {\n warnDeprecated(\n \"RebaseWebSocketClient.setAuthTokenGetter\",\n \"`client.ws.setAuthTokenGetter()` is deprecated; use `client.setAuthTokenGetter()`, \" +\n \"which authenticates HTTP requests and the realtime socket together.\"\n );\n this.getAuthToken = getAuthToken;\n // Auto-authenticate if we are already connected but didn't have the token getter yet\n if (this.isConnected && !this.isAuthenticated && !this.authPromise) {\n this.getAuthToken().then(token => {\n if (!this.ws) return; // Prevent memory leaks / actions after disconnect\n if (token) {\n this.authenticate(token).catch(e => {\n if (this.ws) this.connectionDebug(\"WebSocket auto-auth skipped:\", e?.message || e);\n });\n }\n }).catch(e => {\n // User not logged in or auth still loading — this is expected,\n // the WebSocket will authenticate on-demand when a request is made.\n if (this.ws) this.connectionDebug(\"WebSocket auto-auth skipped:\", e?.message || e);\n });\n }\n }\n\n /**\n * Drop the socket.\n *\n * `permanent` distinguishes the two callers. Signing out drops the socket\n * but the client stays usable — a later subscribe should reconnect\n * anonymously. `client.close()` is the caller saying they are done, and\n * must not be undone by a stray queued frame.\n */\n public disconnect(permanent = false): void {\n if (permanent) this.closedByCaller = true;\n if (permanent) this.removeNetworkListeners();\n this.isAuthenticated = false;\n this.authPromise = null;\n this.refusedSession = null;\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n // Whatever outage there was ends here: the next dial is a fresh one.\n this.reconnectAttempts = 0;\n this.clearOutageTimer();\n this.outageReported = false;\n this.gaveUp = false;\n this.setState(permanent ? \"closed\" : \"idle\");\n // The subscribe watchdogs too, and for the same reason as the reconnect\n // timer: nothing armed for a socket the caller just released may still\n // go off.\n //\n // It has to be here rather than left to `onclose`, because the line\n // below deliberately nulls that handler — so an explicit disconnect was\n // the one close that did NOT suspend them. They then fired up to\n // `subscriptionTimeoutMs` later: on sign-out, killing a subscription the\n // non-permanent disconnect had deliberately kept so a later subscribe\n // could resume it; on `close()`, calling a consumer's `onError` after\n // they were done, and holding the Node event loop open with a timer that\n // is not unref'd — which is the one thing `close()` exists to prevent.\n this.suspendSubscribeWatchdogs();\n // What the socket delivered was read as the identity it was\n // authenticated as, and a sign-out is exactly that identity going away.\n // The registrations stay so a later dial can resume them; their rows do\n // not, or the next `listen()` on the same query is handed the previous\n // user's rows as already loaded — and, since a loaded registration\n // needs no subscribe, never asks the server at all.\n this.forgetCachedData();\n this.stopLivenessWatch();\n this.forgetServerOptIns();\n if (this.ws) {\n this.ws.onclose = null; // Prevent reconnect on explicit disconnect\n this.ws.onerror = null; // Prevent errors on explicit disconnect\n this.ws.onopen = null;\n this.ws.onmessage = null;\n this.ws.close();\n this.ws = null;\n }\n }\n\n // Initialize WebSocket connection\n private initWebSocket() {\n if (!this.WebSocketConstructor) return;\n if (this.ws?.readyState === this.WebSocketConstructor.OPEN) return;\n\n // Guard against race condition: if a previous socket is still connecting, tear it down\n if (this.ws) {\n this.ws.onclose = null;\n this.ws.close();\n this.ws = null;\n }\n\n try {\n // Captured so each handler can tell \"my socket\" from a later one:\n // a close arriving after a redial must not clear the new socket.\n const socket = new this.WebSocketConstructor(withOptIns(this.websocketUrl));\n this.ws = socket;\n\n this.ws!.onopen = async () => {\n this.connectionDebug(\"Connected to PostgreSQL backend\");\n const wasReconnect = this.hadConnection;\n this.hadConnection = true;\n this.isConnected = true;\n this.gaveUp = false;\n // Connected, unless an outage is being reported: then only once\n // the socket is let in, below.\n if (!this.outageReported) this.setState(\"connected\");\n // A new socket asks the server afresh.\n this.refusedSession = null;\n\n // Auto-authenticate if token getter is available\n if (this.getAuthToken && !this.isAuthenticated) {\n let token: string | null = null;\n try {\n token = await this.getAuthToken();\n if (token) {\n await this.authenticate(token);\n this.connectionDebug(\"WebSocket auto-authenticated\");\n }\n } catch (error) {\n // Refused for the per-user socket ceiling: the server\n // closes this socket, and the redial waits on the\n // growing schedule — nothing below runs, since this\n // open was not a connection.\n if (isSocketCeilingRefusal(error)) {\n this.noteSocketCeiling(error);\n return;\n }\n // A refused session is said as that — once, as a\n // warning — and not as a connect that went without\n // signing in. Anything else (not logged in, auth still\n // loading) is expected: authentication happens on\n // demand.\n if (!(token && this.noteSessionRefused(token, error))) {\n this.connectionDebug(\"WebSocket connected, not signed in yet:\", (error as Error)?.message || error);\n }\n }\n }\n // Closed while signing in: the next socket starts over.\n if (this.ws !== socket) return;\n\n // Only now is the outage over and the backoff reset: on `open`\n // they were reset before a refusal could arrive, so a refused\n // socket redialled at the first step forever and its outage\n // was never reported. The registrations hear the outage ended\n // through the re-subscribe below: each one's next `onUpdate`\n // carries what was written meanwhile.\n this.reconnectAttempts = 0;\n this.clearOutageTimer();\n this.outageReported = false;\n this.socketCeilingWarned = false;\n this.setState(\"connected\");\n\n this.emit(wasReconnect ? \"reconnect\" : \"connect\");\n this.processMessageQueue(wasReconnect);\n\n // Re-subscribe all active subscriptions after reconnect.\n // The server-side subscription state was lost when the connection dropped,\n // so we need to re-register every active subscription.\n if (wasReconnect) {\n this.resubscribeAll();\n }\n\n // Subscribes requested while offline have just gone out; they\n // could not be watchdogged at request time.\n this.armPendingSubscribeWatchdogs();\n };\n\n this.ws!.onmessage = (event) => {\n this.lastFrameAt = Date.now();\n try {\n // A reviver parse calls back for every key and value of the\n // frame; most frames have nothing to revive. Same result.\n const message = parseRebaseJson<WebSocketMessage>(event.data);\n this.handleWebSocketMessage(message);\n } catch (error) {\n console.error(\"Error parsing WebSocket message:\", error);\n }\n };\n\n const onClosed = () => {\n this.connectionDebug(\"Disconnected from PostgreSQL backend\");\n // The next socket says hello again; nothing agreed for this one carries over.\n this.stopLivenessWatch();\n this.forgetServerOptIns();\n // Release the dead socket. `ensureConnected` returns early\n // while `this.ws` is set, so holding a closed one made the\n // \"give up after N attempts\" state permanent: nothing could\n // ever redial, not even a fresh `subscribe`.\n if (this.ws === socket) this.ws = null;\n this.isConnected = false;\n this.isAuthenticated = false;\n this.authPromise = null;\n // The reconnect path re-subscribes everything; a watchdog firing\n // in the meantime would tear down healthy subscriptions.\n this.suspendSubscribeWatchdogs();\n this.emit(\"disconnect\");\n\n // Every request still waiting here was written to this socket,\n // so the server may have run it: the answer is what got lost,\n // not necessarily the request. Re-sending it on the next socket\n // made socket requests at-least-once — one `save()` made two\n // rows, and a statement typed into the Studio SQL editor ran\n // twice behind a single success. They are at-most-once: the\n // caller is told the outcome is unknown, and decides. Frames\n // that never reached a socket are still in `messageQueue` and\n // are not affected.\n for (const [reqId, request] of this.pendingRequests.entries()) {\n this.pendingRequests.delete(reqId);\n if (reqId.startsWith(\"auth_\")) {\n request.reject(new Error(\"Connection closed during authentication\"));\n } else {\n request.reject(connectionLostError());\n }\n }\n\n this.setState(this.outageReported ? \"disconnected\" : \"reconnecting\");\n this.attemptReconnect();\n };\n this.ws!.onclose = onClosed;\n // A socket that went half-open never closes: a browser sees no TCP\n // reset and cannot send a ping. Dropped here instead, when the\n // heartbeats it asked for stop arriving — handlers first, so its\n // close (if one ever comes) does not run `onClosed` twice.\n this.dropSocket = () => {\n if (this.ws !== socket) return;\n socket.onclose = null;\n socket.onmessage = null;\n socket.onerror = null;\n socket.onopen = null;\n try {\n socket.close();\n } catch {\n // Already closing.\n }\n onClosed();\n };\n\n this.ws!.onerror = (error) => {\n console.error(\"WebSocket error:\", error);\n this.isConnected = false;\n this.emit(\"error\", error);\n };\n } catch (error) {\n console.error(\"Failed to initialize WebSocket:\", error);\n this.attemptReconnect();\n }\n }\n\n /** Read the server's `hello`: what it agreed to, and start watching for heartbeats. */\n private receiveHello(message: WebSocketMessage): void {\n const capabilities = \"capabilities\" in message && Array.isArray(message.capabilities)\n ? message.capabilities.filter((name): name is string => typeof name === \"string\")\n : [];\n this.serverOptIns = new Set(capabilities);\n this.serverClientId = \"clientId\" in message && typeof message.clientId === \"string\" ? message.clientId : undefined;\n const heartbeatMs = \"heartbeatMs\" in message && typeof message.heartbeatMs === \"number\" && message.heartbeatMs > 0\n ? message.heartbeatMs\n : undefined;\n if (this.serverOptIns.has(\"heartbeat\") && heartbeatMs !== undefined) this.watchLiveness(heartbeatMs);\n }\n\n /**\n * Redial when two heartbeats in a row have not arrived. Anything the\n * server sends counts as a beat, so a busy socket is never judged on them.\n */\n private watchLiveness(heartbeatMs: number): void {\n this.stopLivenessWatch();\n const deadline = 2 * heartbeatMs + Math.min(HEARTBEAT_GRACE_MS, heartbeatMs);\n this.lastFrameAt = Date.now();\n this.livenessTimer = setInterval(() => {\n const silentFor = Date.now() - this.lastFrameAt;\n if (silentFor <= deadline) return;\n console.warn(`[Rebase] Realtime: nothing from the server for ${Math.round(silentFor / 1000)} s, two missed heartbeats. Reconnecting.`);\n this.stopLivenessWatch();\n this.dropSocket?.();\n }, Math.max(250, Math.floor(heartbeatMs / 2)));\n unref(this.livenessTimer);\n }\n\n private stopLivenessWatch(): void {\n if (this.livenessTimer) {\n clearInterval(this.livenessTimer);\n this.livenessTimer = null;\n }\n }\n\n private forgetServerOptIns(): void {\n this.serverOptIns = new Set();\n this.serverClientId = undefined;\n }\n\n private processMessageQueue(isReconnect: boolean) {\n while (this.messageQueue.length > 0 && this.isConnected) {\n const message = this.messageQueue.shift();\n if (!message) continue;\n if (!this.shouldSendQueued(message, isReconnect)) {\n // Fire-and-forget, so there is no answer to wait for: settle\n // it the way a sent one is settled.\n const resolveQueued = message._queuedResolve;\n if (typeof resolveQueued === \"function\") resolveQueued(undefined);\n continue;\n }\n this.sendMessage(message);\n }\n }\n\n /**\n * Whether a frame that waited in the queue should go out on the socket\n * that just opened. Only a subscribe can be refused, and one is exactly\n * when sending it would give the server a subscription this side will not\n * read:\n *\n * - Its registration is gone, or has been given a new id, while it waited.\n * A listener that unmounted before the socket opened (a StrictMode double\n * mount does exactly this) sends no `unsubscribe`, because there was no\n * socket to send it on, so the server would keep that one forever.\n * - This open is a reconnect. `resubscribeAll` sends every registration\n * under a fresh id right after the queue drains, so the queued copy would\n * be the second of two. It is handed back to that pass instead.\n */\n private shouldSendQueued(message: Record<string, unknown>, isReconnect: boolean): boolean {\n if (message.type !== \"subscribe_collection\" && message.type !== \"subscribe_one\") return true;\n const payload = message.payload;\n if (!payload || typeof payload !== \"object\" || !(\"subscriptionId\" in payload)) return true;\n const subscriptionId = payload.subscriptionId;\n if (typeof subscriptionId !== \"string\") return true;\n\n const registration = message.type === \"subscribe_collection\"\n ? this.collectionSubscriptions.get(this.backendToCollectionKey.get(subscriptionId) ?? \"\")\n : this.singleSubscriptions.get(this.backendToEntityKey.get(subscriptionId) ?? \"\");\n if (!registration || registration.backendSubscriptionId !== subscriptionId) return false;\n if (isReconnect) {\n registration.subscribeInFlight = false;\n return false;\n }\n return true;\n }\n\n private attemptReconnect() {\n if (this.closedByCaller) return;\n\n // A blip becomes an outage once it has lasted OUTAGE_REPORT_AFTER_MS.\n // Said once, to everything that is live, so a view can say its data is\n // stale instead of looking current.\n if (!this.outageReported && !this.outageTimer) {\n this.outageTimer = setTimeout(() => {\n this.outageTimer = null;\n this.reportConnectionLost();\n }, OUTAGE_REPORT_AFTER_MS);\n }\n\n // Giving up is only for a client nothing depends on. While anything is\n // registered the client keeps redialling at the capped interval:\n // stopping after five attempts (about a minute) left every live view\n // that had loaded frozen for the life of the page after any longer\n // outage — a deploy, a crash loop, a slow migration — because a server\n // restart fires no `online` event and Node has no `window` at all.\n if (this.reconnectAttempts >= this.maxReconnectAttempts && !this.hasRegistrations()) {\n console.warn(\"[Rebase] Realtime: the server is unreachable and nothing is subscribed. Not retrying until something asks for the connection.\");\n this.gaveUp = true;\n // Given up is down, however soon the redials happened to fail.\n this.clearOutageTimer();\n this.reportConnectionLost();\n return;\n }\n\n this.reconnectAttempts = Math.min(this.reconnectAttempts + 1, 30);\n const ceiling = Math.min(1000 * Math.pow(2, this.reconnectAttempts), MAX_RECONNECT_DELAY_MS);\n // Anywhere in the step at random, above the floor (\"full jitter\"): a\n // deploy drops every client at the same instant, and the same schedule\n // would bring them all back at the same instant too. Drawn from the\n // last fifth of the step only, the first redials still landed in one\n // 400 ms window; spread over 250 ms – 2 s the peak is 4.4x lower.\n // Uniform above the floor rather than clamped to it, which would land\n // one client in eight on the same millisecond.\n const delay = MIN_RECONNECT_DELAY_MS + Math.floor(Math.random() * (ceiling - MIN_RECONNECT_DELAY_MS));\n\n this.connectionDebug(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n }\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectTimeout = null;\n this.initWebSocket();\n }, delay);\n }\n\n /**\n * Whether anything depends on the socket coming back: a subscription, a\n * joined channel, or a request whose caller is still waiting for a socket.\n */\n private hasRegistrations(): boolean {\n return this.collectionSubscriptions.size > 0\n || this.singleSubscriptions.size > 0\n || this.channelHandlers.size > 0\n || this.messageQueue.some(frame => typeof frame._queuedReject === \"function\");\n }\n\n /**\n * Tell every subscription and joined channel, once per outage, that the\n * connection is down and their data is not updating.\n *\n * Registrations are kept — that is the difference from a subscribe that\n * failed. When the socket is back they are re-subscribed, and the fresh\n * `onUpdate` is the recovery.\n */\n private clearOutageTimer(): void {\n if (this.outageTimer) clearTimeout(this.outageTimer);\n this.outageTimer = null;\n }\n\n private reportConnectionLost(refusal?: RebaseApiError): void {\n if (this.outageReported) return;\n this.outageReported = true;\n this.setState(\"disconnected\");\n const error = realtimeLostError(refusal);\n for (const subscription of this.collectionSubscriptions.values()) {\n this.notifyError(subscription.callbacks, error);\n }\n for (const subscription of this.singleSubscriptions.values()) {\n this.notifyError(subscription.callbacks, error);\n }\n for (const handler of [...this.connectionLostHandlers]) {\n try {\n handler(error);\n } catch (handlerError) {\n console.error(\"Error in connection-lost handler:\", handlerError);\n }\n }\n }\n\n private notifyError(callbacks: Map<string, { onError?: (error: Error) => void }>, error: Error): void {\n for (const callback of [...callbacks.values()]) {\n if (!callback.onError) continue;\n try {\n callback.onError(error);\n } catch (callbackError) {\n console.error(\"Error in subscription error callback:\", callbackError);\n }\n }\n }\n\n /**\n * A listener attaching while an outage is being reported is told at once:\n * its subscribe waits for a socket that may be minutes away, and nothing\n * else would say so.\n */\n private reportOutageTo(onError: ((error: Error) => void) | undefined): void {\n if (!this.outageReported || !onError) return;\n queueMicrotask(() => {\n if (!this.outageReported) return;\n try {\n onError(realtimeLostError());\n } catch (callbackError) {\n console.error(\"Error in subscription error callback:\", callbackError);\n }\n });\n }\n\n private isAuthError(message: WebSocketMessage): boolean {\n if (message.type === \"AUTH_ERROR\") return true;\n const { errorMessage, errorCode } = extractMessageError(message);\n if (errorCode === \"UNAUTHORIZED\" || errorCode === \"JWT_EXPIRED\" || errorCode === \"AUTH_ERROR\") return true;\n const lowerMessage = errorMessage.toLowerCase();\n return lowerMessage.includes(\"unauthorized\") || lowerMessage.includes(\"token expired\") || lowerMessage.includes(\"token is expired\") || lowerMessage.includes(\"invalid token\") || lowerMessage.includes(\"session expired\") || lowerMessage.includes(\"auth error\");\n }\n\n private async handleAuthFailure(): Promise<boolean> {\n if (this.refreshInProgress) {\n return this.refreshInProgress;\n }\n this.refreshInProgress = (async () => {\n this.isAuthenticated = false;\n this.authPromise = null;\n if (this.onUnauthorized) {\n try {\n const refreshed = await this.onUnauthorized();\n if (refreshed && this.getAuthToken) {\n const token = await this.getAuthToken();\n if (token) {\n await this.authenticate(token);\n return true;\n }\n }\n } catch (error) {\n console.error(\"WebSocket auth refresh failed:\", error);\n }\n }\n return false;\n })();\n try {\n return await this.refreshInProgress;\n } finally {\n this.refreshInProgress = null;\n }\n }\n\n /**\n * Shared logic for re-subscribing a collection or row subscription\n * after an auth error is resolved by refreshing credentials.\n */\n private resubscribeAfterAuthRefresh(\n message: WebSocketMessage,\n subscription: {\n backendSubscriptionId: string;\n callbacks: Map<string, { onUpdate: (...args: never[]) => void; onError?: (error: Error) => void }>;\n props: FetchCollectionProps | ListenOneTarget;\n },\n subscriptionKey: string,\n idPrefix: \"collection\" | \"row\",\n backendKeyMap: Map<string, string>,\n messageType: \"subscribe_collection\" | \"subscribe_one\"\n ): void {\n this.handleAuthFailure().then(refreshed => {\n if (refreshed) {\n const oldBackendId = subscription.backendSubscriptionId;\n const newBackendId = `${idPrefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n subscription.backendSubscriptionId = newBackendId;\n backendKeyMap.delete(oldBackendId);\n backendKeyMap.set(newBackendId, subscriptionKey);\n\n // Route through the helpers so the retry is watchdogged too.\n if (messageType === \"subscribe_collection\") {\n this.sendCollectionSubscribe(subscriptionKey);\n } else {\n this.sendEntitySubscribe(subscriptionKey);\n }\n return;\n }\n\n // The refresh did not produce usable credentials. Report the original\n // error and drop the registration, so a later mount can try again\n // rather than attaching to a subscription that will never load.\n const error = frameError(message);\n if (messageType === \"subscribe_collection\") {\n this.failCollectionSubscription(subscriptionKey, error);\n } else {\n this.failEntitySubscription(subscriptionKey, error);\n }\n }).catch(err => {\n const error = err instanceof Error ? err : new Error(String(err));\n if (messageType === \"subscribe_collection\") {\n this.failCollectionSubscription(subscriptionKey, error);\n } else {\n this.failEntitySubscription(subscriptionKey, error);\n }\n });\n }\n\n private handleWebSocketMessage(message: WebSocketMessage) {\n const {\n type,\n requestId,\n subscriptionId\n } = message;\n\n // The socket's own frames. A heartbeat's work is done by arriving\n // (`lastFrameAt`); a server only sends either to a client that asked.\n if (type === \"hello\") {\n this.receiveHello(message);\n return;\n }\n if (type === \"heartbeat\") return;\n\n // Handle responses to pending requests\n if (requestId && this.pendingRequests.has(requestId)) {\n const pendingReq = this.pendingRequests.get(requestId)!;\n if (type === \"ERROR\" || type === \"AUTH_ERROR\" || message.error) {\n if (this.isAuthError(message)) {\n this.pendingRequests.delete(requestId);\n this.handleAuthFailure().then(refreshed => {\n if (refreshed && pendingReq.message) {\n this.doSendMessage(pendingReq.message, pendingReq.resolve, pendingReq.reject).catch(pendingReq.reject);\n } else {\n pendingReq.reject(frameError(message));\n }\n }).catch(err => {\n pendingReq.reject(err);\n });\n } else {\n this.pendingRequests.delete(requestId);\n pendingReq.reject(frameError(message));\n }\n } else {\n this.pendingRequests.delete(requestId);\n pendingReq.resolve(message.payload || message);\n }\n return;\n }\n\n // Channel traffic (broadcast / presence) is addressed by channel name\n // rather than by requestId or subscriptionId, so it is dispatched\n // before the subscription paths — none of which would match it, and\n // the message would otherwise fall through and be dropped silently.\n if (isChannelMessage(message)) {\n const handlers = this.channelHandlers.get(message.channel);\n if (handlers) {\n for (const handler of [...handlers]) {\n try {\n handler(message);\n } catch (error) {\n console.error(\"Error in channel handler:\", error);\n }\n }\n }\n return;\n }\n\n // Handle subscription updates for collection subscriptions\n if (subscriptionId && isCollectionUpdate(message)) {\n // Narrowed, not asserted. `pks` and `meta` are declared on\n // `CollectionUpdateMessage` — they always were — but\n // `WebSocketMessage.type` is a `string` rather than a literal, so\n // the union does not discriminate on its own and both reads went\n // through `message as unknown as { … }`: a second, local\n // declaration of two wire fields, free to drift from the one in\n // `@rebasepro/types` that describes them.\n const collectionUpdate = message;\n const subscriptionKey = this.backendToCollectionKey.get(subscriptionId);\n if (subscriptionKey) {\n const collectionSub = this.collectionSubscriptions.get(subscriptionKey);\n if (collectionSub) {\n const wireEntities = (message.rows || []) as Record<string, unknown>[];\n const incomingRows = wireEntities;\n\n // The keys arrive with the rows, so they are known before the\n // first merge — a CDC-driven change never sends a patch, and\n // learning them from patches alone would leave every\n // externally-written collection unable to match a thing.\n const updatePks = collectionUpdate.pks;\n if (updatePks) collectionSub.pks = updatePks;\n\n // The page metadata, beside the rows it describes.\n //\n // The frame used to carry rows and keys and nothing else, so\n // every subscriber issued a `GET /<collection>/count` per\n // push to recover a total it needed to render the list it\n // had just been handed — one extra round trip per write, per\n // subscriber, and a window in which the count and the rows\n // described different states of the collection.\n const updateMeta = collectionUpdate.meta;\n if (updateMeta) collectionSub.latestMeta = updateMeta;\n\n // Structural merge: preserve cached row references for rows\n // whose values haven't changed. This prevents downstream React components\n // from re-rendering (VirtualTableCell uses deepEqual on rowData —\n // same reference = instant true, avoiding expensive deep comparison).\n const rows = this.mergeRows(collectionSub.latestData, incomingRows, collectionSub.pks);\n\n // Cache the latest data with optimizations\n collectionSub.latestData = rows;\n collectionSub.lastUpdated = Date.now();\n collectionSub.isInitialDataReceived = true;\n // The subscribe landed — stand the watchdog down.\n if (collectionSub.subscribeTimeout) clearTimeout(collectionSub.subscribeTimeout);\n collectionSub.subscribeTimeout = undefined;\n collectionSub.subscribeInFlight = false;\n\n // Notify all callbacks for this subscription\n collectionSub.callbacks.forEach(callback => {\n try {\n callback.onUpdate(rows, collectionSub.latestMeta);\n } catch (error) {\n console.error(\"Error in collection subscription callback:\", error);\n if (callback.onError) {\n callback.onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n });\n return;\n }\n }\n }\n\n // `collection_patch` was handled here. Nothing sends it any more: the\n // server's immediate row patch was replaced by a scoped refetch, which\n // is the only delivery on any path — `realtime-patch-pks.test.ts` in\n // `@rebasepro/server-postgres` asserts zero patch frames, and says so.\n //\n // A branch for a message that cannot arrive is not harmless. It carried\n // its own copy of the cache-merge rules — insert-vs-update by derived\n // address, delete by filter — beside the refetch reconciliation that\n // now does the same job, so a fix to one of them was a fix to half the\n // client, in a half nobody could reach to notice.\n\n // Handle subscription updates for row subscriptions\n if (subscriptionId && type === \"single_update\") {\n const subscriptionKey = this.backendToEntityKey.get(subscriptionId);\n if (subscriptionKey) {\n const entitySub = this.singleSubscriptions.get(subscriptionKey);\n if (entitySub) {\n const wireEntity = message.row ?? null;\n // The row's version, when the server tags its frames (see\n // `sendEntitySubscribe`): kept on the row the way a\n // `findById` keeps it, so `etagOf(row)` is the `If-Match`\n // of an edit made against exactly this frame.\n const etag = \"etag\" in message && typeof message.etag === \"string\" ? message.etag : undefined;\n const row = wireEntity ? withETag(wireEntity as Record<string, unknown>, etag) : null;\n // Cache the latest data with optimizations\n entitySub.latestData = row;\n entitySub.lastUpdated = Date.now();\n entitySub.isInitialDataReceived = true;\n if (entitySub.subscribeTimeout) clearTimeout(entitySub.subscribeTimeout);\n entitySub.subscribeTimeout = undefined;\n entitySub.subscribeInFlight = false;\n\n // Notify all callbacks for this subscription\n entitySub.callbacks.forEach(callback => {\n try {\n callback.onUpdate(row);\n } catch (error) {\n console.error(\"Error in row subscription callback:\", error);\n if (callback.onError) {\n callback.onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n });\n return;\n }\n }\n }\n\n // Handle subscription errors.\n //\n // A refusal the server issues before the frame reaches its realtime\n // service — UNAUTHORIZED on a `requireAuth` server, RATE_LIMITED —\n // carries the frame's `requestId` and no `subscriptionId`. A subscribe\n // is sent with its subscription id as its request id, so that still\n // names it; without this the refusal fell through to a console warning\n // and the listener waited out the watchdog for a \"timed out\".\n const erroredSubscriptionId = subscriptionId ?? requestId;\n if (erroredSubscriptionId && (type === \"ERROR\" || message.error)) {\n const collectionKey = this.backendToCollectionKey.get(erroredSubscriptionId);\n if (collectionKey) {\n const collectionSub = this.collectionSubscriptions.get(collectionKey);\n if (collectionSub) {\n if (this.isAuthError(message)) {\n this.resubscribeAfterAuthRefresh(\n message,\n collectionSub,\n collectionKey,\n \"collection\",\n this.backendToCollectionKey,\n \"subscribe_collection\"\n );\n return;\n }\n\n // The server answered, so nothing is in flight any more. Leave\n // the registration in place (its listeners are still mounted\n // and have been told), but marked idle so the next listener\n // re-subscribes instead of attaching to a dead entry.\n if (collectionSub.subscribeTimeout) clearTimeout(collectionSub.subscribeTimeout);\n collectionSub.subscribeTimeout = undefined;\n collectionSub.subscribeInFlight = false;\n\n const error = frameError(message);\n collectionSub.callbacks.forEach(callback => {\n if (callback.onError) {\n callback.onError(error);\n }\n });\n return;\n }\n }\n\n const entityKey = this.backendToEntityKey.get(erroredSubscriptionId);\n if (entityKey) {\n const entitySub = this.singleSubscriptions.get(entityKey);\n if (entitySub) {\n if (this.isAuthError(message)) {\n this.resubscribeAfterAuthRefresh(\n message,\n entitySub,\n entityKey,\n \"row\",\n this.backendToEntityKey,\n \"subscribe_one\"\n );\n return;\n }\n\n if (entitySub.subscribeTimeout) clearTimeout(entitySub.subscribeTimeout);\n entitySub.subscribeTimeout = undefined;\n entitySub.subscribeInFlight = false;\n\n const error = frameError(message);\n entitySub.callbacks.forEach(callback => {\n if (callback.onError) {\n callback.onError(error);\n }\n });\n return;\n }\n }\n }\n\n // An error that matched no waiter used to fall off the end of this\n // method and disappear. Channel frames are the ones that always did:\n // they are fire-and-forget by design, so no `pendingRequests` entry\n // exists to reject and the server's errors about them — RATE_LIMITED,\n // CHANNEL_FORBIDDEN, CHANNEL_HISTORY_WRITE_FAILED — were dropped while\n // `await channel.broadcast(...)` resolved as if it had been sent.\n //\n // The server now names the channel on those, and the channel-addressed\n // branch above delivers them to `channel.onError()`. This stays as the\n // floor for an error that names nothing — including one from a server\n // older than that change.\n if (type === \"ERROR\" || type === \"error\" || message.error) {\n const { errorMessage, errorCode } = extractMessageError(message);\n console.warn(\n `[Rebase] Realtime error from the server${errorCode ? ` (${errorCode})` : \"\"}: ${errorMessage}`\n );\n }\n }\n\n private async ensureAuthenticated(retryCount = 3): Promise<void> {\n // If already authenticated or no token getter, skip\n if (this.isAuthenticated || !this.getAuthToken) return;\n\n // If auth is in progress, wait for it.\n //\n // The share has to be published *before* the first await, which is why\n // the work lives in its own method. The guard used to be read here and\n // the promise only assigned after `await this.getAuthToken()` — so\n // every caller that arrived during that gap saw `null` and started an\n // attempt of its own. A queue flushing six subscriptions on connect\n // does exactly that, and each extra attempt raced the others through a\n // single `pendingRequests` slot: one settled, the rest hung until their\n // own 30s timeout, and the frames waiting behind them were never sent.\n // Their subscriptions then reported \"Subscription timed out\" — the\n // board's columns loading one at a time, or not at all.\n if (!this.authPromise) {\n this.authPromise = this.runAuthentication(retryCount);\n this.authPromise.finally(() => {\n this.authPromise = null;\n }).catch(() => undefined);\n }\n await this.authPromise;\n }\n\n private async runAuthentication(retryCount: number): Promise<void> {\n // Try to authenticate with retries\n let lastError: unknown = null;\n\n for (let attempt = 0; attempt < retryCount; attempt++) {\n let token: string | null = null;\n try {\n token = await this.getAuthToken!();\n if (!token) throw new Error(\"user not logged in\");\n // Refused on this socket already: the same token gets the\n // same answer, so it is not asked again.\n if (this.refusedSession?.token === token) throw this.refusedSession.error;\n await this.authenticate(token);\n this.connectionDebug(\"WebSocket authenticated on demand\");\n return; // Success\n } catch (error: unknown) {\n lastError = error;\n\n // A refused session is definitive. Retrying it with backoff\n // kept a `listen()` from hearing why for over three seconds.\n if (token && this.noteSessionRefused(token, error)) throw error;\n\n const errMsg = error instanceof Error ? error.message : String(error);\n // \"not logged in\" / \"Session expired\" are definitive - don't retry\n if (errMsg.includes(\"not logged in\") || errMsg.includes(\"Session expired\")) {\n console.warn(\"WebSocket auth failed: user not logged in\");\n throw error;\n }\n\n // \"still loading\" is transient - retry with backoff (auth controller\n // is restoring tokens from localStorage; it will resolve shortly)\n if (errMsg.includes(\"still loading\")) {\n if (attempt < retryCount - 1) {\n const delay = Math.min(500 * (attempt + 1), 2000);\n await new Promise(resolve => setTimeout(resolve, delay));\n continue;\n }\n }\n\n // For other errors, retry with backoff\n if (attempt < retryCount - 1) {\n const delay = Math.min(1000 * (attempt + 1), 3000);\n this.connectionDebug(`WebSocket auth attempt ${attempt + 1} failed, retrying in ${delay}ms...`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\n console.warn(\"WebSocket on-demand auth failed after retries:\", lastError);\n throw lastError;\n }\n\n async reauthenticate(): Promise<void> {\n if (!this.getAuthToken) return;\n\n this.isAuthenticated = false;\n try {\n const token = await this.getAuthToken();\n if (!token) throw new Error(\"user not logged in\");\n await this.authenticate(token);\n this.connectionDebug(\"WebSocket reauthenticated successfully\");\n } catch (error) {\n console.error(\"WebSocket reauthentication failed:\", error);\n throw error;\n }\n }\n\n /**\n * Public because `RebaseRealtimeChannel` sends channel frames through it.\n * Not part of the stable surface — prefer `client.realtime.channel(name)`.\n */\n public sendMessage(message: Record<string, unknown>): Promise<unknown> {\n // Drained from the queue: its caller already holds a promise.\n const queuedMsg: QueuedFrame = message;\n if (queuedMsg._queuedResolve && queuedMsg._queuedReject) {\n const resolve = queuedMsg._queuedResolve;\n const reject = queuedMsg._queuedReject;\n delete queuedMsg._queuedResolve;\n delete queuedMsg._queuedReject;\n return this.doSendMessage(message, resolve, reject);\n }\n\n return new Promise<unknown>((resolve, reject) => {\n // One deadline per request, armed at the call. It used to be armed\n // only when the frame was written, so a request queued while the\n // socket was down had none at all — and when the socket never came\n // back, its caller's promise stayed pending for the life of the\n // page. Running it from the call also keeps a request that waited\n // 25s for a socket from then getting another full 30s.\n let settled = false;\n let deadline: ReturnType<typeof setTimeout> | undefined;\n const onResolve = (value: unknown) => {\n if (settled) return;\n settled = true;\n if (deadline) clearTimeout(deadline);\n resolve(value);\n };\n const onReject = (error: Error) => {\n if (settled) return;\n settled = true;\n if (deadline) clearTimeout(deadline);\n this.abandonedFrames.add(message);\n reject(error);\n };\n if (this.expectsResponse(message)) {\n deadline = setTimeout(() => {\n const queuedAt = this.messageQueue.indexOf(message);\n if (queuedAt >= 0) this.messageQueue.splice(queuedAt, 1);\n let written = false;\n for (const [requestId, pending] of this.pendingRequests) {\n if (pending.message !== message) continue;\n this.pendingRequests.delete(requestId);\n written = true;\n }\n onReject(requestTimeoutError(written));\n }, this.requestTimeoutMs);\n }\n\n if (!this.isConnected || !this.ws) {\n // The queue is only ever drained by a socket opening, so\n // something has to open one. Before lazy connect this was\n // guaranteed by the constructor; now the first frame is what\n // asks for it.\n this.ensureConnected();\n queuedMsg._queuedResolve = onResolve;\n queuedMsg._queuedReject = onReject;\n this.messageQueue.push(message);\n return;\n }\n\n void this.doSendMessage(message, onResolve, onReject);\n });\n }\n\n /**\n * Frames whose caller has already been answered — timed out, or rejected\n * by a close. One can still be on its way to the socket (awaiting\n * authentication), and must not be written after its caller was told it\n * failed.\n */\n private abandonedFrames = new WeakSet<Record<string, unknown>>();\n\n /** Whether the server answers this frame with a response envelope. */\n private expectsResponse(message: Record<string, unknown>): boolean {\n const type = message.type as string;\n return !(SUBSCRIPTION_MESSAGE_TYPES.has(type) || CHANNEL_MESSAGE_TYPES.has(type));\n }\n\n private async doSendMessage(message: Record<string, unknown>, resolve: (value: unknown) => void, reject: (error: Error) => void): Promise<void> {\n // Ensure authenticated before sending non-auth messages.\n //\n // Channel traffic is exempt. `ensureAuthenticated` throws \"user not\n // logged in\" when there is no token, which rejects the frame before it\n // is ever sent — so on an anonymous-first app (the kind this API was\n // added for) *every* channel operation failed client-side, and the\n // server never got to decide. Presence in a public room does not\n // require an account. A signed-in caller still authenticates: the\n // socket does it from `getAuthToken` on open, and the server authorizes\n // these frames either way.\n //\n // Subscription frames authenticate when there is an account and go out\n // without one when there is not. Refusing them here refused every\n // visitor's `listen()` on a server that serves visitors — `find()`\n // beside it worked — so the client was deciding `requireAuth` for a\n // server that had decided the other way.\n const isSubscriptionFrame = SUBSCRIPTION_MESSAGE_TYPES.has(message.type as string);\n if (message.type !== \"AUTHENTICATE\"\n && !CHANNEL_MESSAGE_TYPES.has(message.type as string)\n && this.getAuthToken && !this.isAuthenticated) {\n try {\n await this.ensureAuthenticated();\n } catch (error: unknown) {\n if (!(isSubscriptionFrame && isNotSignedIn(error))) {\n // A refusal keeps its code (`SESSION_REVOKED`, …), so\n // `onError` can tell a revoked session from a broken one.\n if (error instanceof RebaseApiError) {\n reject(error);\n return;\n }\n const errorMessage = error instanceof Error ? error.message : \"Authentication required\";\n reject(new RebaseApiError(errorMessage));\n return;\n }\n }\n }\n\n // Its caller gave up while this waited for authentication.\n if (this.abandonedFrames.has(message)) return;\n\n // The socket went away while this waited for authentication. Nothing\n // was written, so it is still safe to send — on the next socket.\n if (!this.isConnected || !this.ws) {\n const queueable: QueuedFrame = message;\n queueable._queuedResolve = resolve;\n queueable._queuedReject = reject;\n this.messageQueue.push(message);\n this.ensureConnected();\n return;\n }\n\n const requestId = (message.requestId as string) || `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n message.requestId = requestId;\n\n const expectsResponse = this.expectsResponse(message);\n\n // The deadline is the caller's, armed in `sendMessage`; registering\n // here only says \"written, waiting for its answer\".\n if (expectsResponse && !this.pendingRequests.has(requestId)) {\n this.pendingRequests.set(requestId, { resolve, reject, message });\n }\n\n try {\n this.ws.send(JSON.stringify(message));\n if (!expectsResponse) {\n resolve(undefined);\n }\n } catch (error) {\n if (expectsResponse) {\n this.pendingRequests.delete(requestId);\n }\n reject(new RebaseApiError(\"Failed to send message\", { cause: error }));\n }\n }\n\n // Data source methods\n async fetchCollection<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]> {\n const response = await this.sendMessage({\n type: \"FETCH_COLLECTION\",\n payload: props\n }) as { rows?: Record<string, unknown>[] };\n return (response.rows || []);\n }\n\n async fetchOne<M extends Record<string, unknown>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined> {\n const response = await this.sendMessage({\n type: \"FETCH_ONE\",\n payload: props\n }) as { row?: Record<string, unknown> };\n const wireEntity = response.row;\n return wireEntity ?? undefined;\n }\n\n async save<M extends Record<string, unknown>>(props: SaveProps<M>): Promise<Record<string, unknown>> {\n const response = await this.sendMessage({\n type: \"SAVE\",\n payload: props\n }) as { row: Record<string, unknown> };\n return response.row;\n }\n\n async delete<M extends Record<string, unknown>>(props: DeleteProps<M>): Promise<void> {\n await this.sendMessage({\n type: \"DELETE\",\n payload: props\n });\n }\n\n /**\n * @deprecated Will leave the public `client.ws` type in the next minor.\n * Not part of the app-facing client: the admin UI's SQL console reaches it\n * through `<Rebase>`, and it answers only a caller with `database:write`.\n * App code has no supported SQL or branch path on the client; run SQL\n * server-side (`isSQLAdmin(driver.admin)`), and use `client.admin` for\n * users and roles.\n */\n async executeSql(sql: string, options?: { database?: string, role?: string }): Promise<Record<string, unknown>[]> {\n const response = await this.sendMessage({\n type: \"EXECUTE_SQL\",\n payload: { sql,\noptions }\n }) as { result?: Record<string, unknown>[] };\n return response.result || [];\n }\n\n /**\n * Run a script a person wrote — the Studio console — and get back what its\n * last statement returned, every value as the database's text, with the\n * table column each result column was read from. See\n * `SQLAdmin.runSqlScript`.\n */\n async runSqlScript(sql: string, options?: { database?: string, role?: string, signal?: AbortSignal }): Promise<SqlScriptResult> {\n const signal = options?.signal;\n if (signal?.aborted) throw abortReason(signal);\n const requestId = `sql_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n const message: Record<string, unknown> = {\n type: \"EXECUTE_SQL\",\n requestId,\n payload: {\n sql,\n options: { database: options?.database, role: options?.role },\n mode: \"script\"\n }\n };\n const answer = this.sendMessage(message);\n if (!signal) return readSqlScriptResult(await answer);\n // Aborting rejects at once, whatever the server can do; a server that\n // takes `CANCEL_SQL` also stops the statement. The run's own late\n // answer then settles a promise nobody waits on.\n return new Promise<SqlScriptResult>((resolve, reject) => {\n const onAbort = () => {\n reject(abortReason(signal));\n this.cancelSqlRun(message, requestId);\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n answer.then(\n (response) => {\n signal.removeEventListener(\"abort\", onAbort);\n try {\n resolve(readSqlScriptResult(response));\n } catch (error) {\n reject(error);\n }\n },\n (error: unknown) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(error);\n }\n );\n });\n }\n\n /**\n * Stop a console run its caller gave up on: never written if it has not\n * been yet, and cancelled on a server whose `hello` listed `cancel_sql`. A server\n * that does not is left to finish it, bounded by its `statement_timeout`.\n */\n private cancelSqlRun(message: Record<string, unknown>, requestId: string): void {\n const queuedAt = this.messageQueue.indexOf(message);\n if (queuedAt >= 0) {\n this.messageQueue.splice(queuedAt, 1);\n return;\n }\n // Still waiting on authentication in `doSendMessage`: it checks this\n // before writing.\n this.abandonedFrames.add(message);\n // Only to a server whose `hello` said it takes the frame; an older\n // server would answer an unknown type with an error nobody reads.\n if (this.isAuthenticated && this.serverOptIns.has(\"cancel_sql\")) {\n this.sendMessage({ type: \"CANCEL_SQL\", payload: { requestId } }).catch(() => undefined);\n }\n }\n\n async fetchAvailableDatabases(): Promise<string[]> {\n const response = await this.sendMessage({\n type: \"FETCH_DATABASES\",\n payload: {}\n }) as { databases?: string[] };\n return response.databases || [];\n }\n\n async fetchAvailableRoles(): Promise<string[]> {\n const response = await this.sendMessage({\n type: \"FETCH_ROLES\"\n }) as { roles?: string[] };\n return response.roles || [];\n }\n\n async fetchApplicationRoles(): Promise<string[]> {\n const response = await this.sendMessage({\n type: \"FETCH_APPLICATION_ROLES\"\n }) as { roles?: string[] };\n return response.roles || [];\n }\n\n async fetchCurrentDatabase(): Promise<string | undefined> {\n const response = await this.sendMessage({\n type: \"FETCH_CURRENT_DATABASE\"\n }) as { database?: string };\n return response.database;\n }\n\n async checkUniqueField(path: string, name: string, value: unknown, id?: string, collection?: CollectionConfig): Promise<boolean> {\n const response = await this.sendMessage({\n type: \"CHECK_UNIQUE_FIELD\",\n payload: {\n path,\n name,\n value,\n id,\n collection\n }\n }) as { isUnique: boolean };\n return response.isUnique;\n }\n\n async count<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number> {\n const response = await this.sendMessage({\n type: \"COUNT\",\n payload: props\n }) as { count: number };\n return response.count;\n }\n\n async fetchUnmappedTables(mappedPaths?: string[]): Promise<string[]> {\n const response = await this.sendMessage({\n type: \"FETCH_UNMAPPED_TABLES\",\n payload: { mappedPaths }\n }) as { tables?: string[] };\n return response.tables || [];\n }\n\n async fetchTableMetadata(tableName: string): Promise<TableMetadata> {\n const response = await this.sendMessage({\n type: \"FETCH_TABLE_METADATA\",\n payload: { tableName }\n }) as { metadata?: TableMetadata };\n\n return response.metadata || ({ columns: [],\nforeignKeys: [],\njunctions: [],\npolicies: [] } as TableMetadata);\n }\n\n /**\n * @deprecated Will leave the public `client.ws` type in the next minor.\n * Not part of the app-facing client: the admin UI's SQL console reaches it\n * through `<Rebase>`, and it answers only a caller with `database:write`.\n * App code has no supported SQL or branch path on the client; run SQL\n * server-side (`isSQLAdmin(driver.admin)`), and use `client.admin` for\n * users and roles.\n */\n async createBranch(name: string, options?: { source?: string }): Promise<BranchInfo> {\n const response = await this.sendMessage({\n type: \"CREATE_BRANCH\",\n payload: { name,\noptions }\n }) as { branch: BranchInfo };\n return response.branch;\n }\n\n /**\n * @deprecated Will leave the public `client.ws` type in the next minor.\n * Not part of the app-facing client: the admin UI's SQL console reaches it\n * through `<Rebase>`, and it answers only a caller with `database:write`.\n * App code has no supported SQL or branch path on the client; run SQL\n * server-side (`isSQLAdmin(driver.admin)`), and use `client.admin` for\n * users and roles.\n */\n async deleteBranch(name: string): Promise<void> {\n await this.sendMessage({\n type: \"DELETE_BRANCH\",\n payload: { name }\n });\n }\n\n async listBranches(): Promise<BranchInfo[]> {\n const response = await this.sendMessage({\n type: \"LIST_BRANCHES\",\n payload: {}\n }) as { branches?: BranchInfo[] };\n return response.branches || [];\n }\n\n /**\n * Recursively compare two values a refetch can carry for structural\n * equality: primitives, null, undefined, Date, RegExp, arrays and objects.\n *\n * A relation compares by the reference it holds, not by the row it happens\n * to have loaded, so its `data` is left out on both sides. That used to be\n * done by normalising each side into a copy first — two deep copies of\n * every cached row on every frame, most of which then compared equal. It is\n * done in place now, and an unchanged row costs no copy.\n *\n * `relationAware` is off for a relation's own fields: the copies never\n * looked inside those either.\n */\n private deepEqual(a: unknown, b: unknown, relationAware = true): boolean {\n // Same reference or same primitive\n if (a === b) return true;\n\n // Handle null/undefined\n if (a === null || b === null || a === undefined || b === undefined) return false;\n\n // Different types\n if (typeof a !== typeof b) return false;\n\n // Non-object primitives (number, string, boolean, bigint, symbol)\n // that weren't caught by === above (e.g. NaN !== NaN)\n if (typeof a !== \"object\") return false;\n\n // Date comparison\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime();\n }\n if (a instanceof Date || b instanceof Date) return false;\n\n // RegExp comparison\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags;\n }\n if (a instanceof RegExp || b instanceof RegExp) return false;\n\n // Array comparison\n const aIsArray = Array.isArray(a);\n const bIsArray = Array.isArray(b);\n if (aIsArray !== bIsArray) return false;\n\n if (aIsArray && bIsArray) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!this.deepEqual(a[i], b[i], relationAware)) return false;\n }\n return true;\n }\n\n // Object comparison\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n // A relation is equal only to a relation, and never by its `data`.\n let skip: string | undefined;\n let nested = relationAware;\n if (relationAware) {\n const aIsRelation = aObj.__type === \"relation\";\n if (aIsRelation !== (bObj.__type === \"relation\")) return false;\n if (aIsRelation) {\n skip = \"data\";\n nested = false;\n }\n }\n\n let aCount = 0;\n for (const key of Object.keys(aObj)) {\n if (key === skip) continue;\n aCount++;\n if (!Object.prototype.hasOwnProperty.call(bObj, key)) return false;\n if (!this.deepEqual(aObj[key], bObj[key], nested)) return false;\n }\n let bCount = 0;\n for (const key of Object.keys(bObj)) {\n if (key !== skip) bCount++;\n }\n return aCount === bCount;\n }\n\n /**\n * The address of a row, for matching it against another copy of itself.\n *\n * A row is exactly its columns and carries no address, so it is derived\n * from the key columns the server named — including the ordinary case where\n * that key is `id`, which the server reports like any other.\n *\n * Undefined when there are no keys, which means the server could not\n * resolve any: such rows genuinely cannot be recognised, and guessing at a\n * column called `id` would be inventing an identity for a table that has\n * none.\n */\n private rowAddress(row: Record<string, unknown>, pks: PrimaryKeyInfo[] | undefined): string | undefined {\n if (!pks || pks.length === 0) return undefined;\n const address = buildCompositeId(row, pks);\n if (!address || address.split(COMPOSITE_ID_SEPARATOR).every(part => part === \"\")) return undefined;\n return address;\n }\n\n /**\n * Merge incoming rows with cached data, preserving cached references\n * for rows whose values haven't changed. This avoids unnecessary\n * React re-renders when the server refetches all rows but most\n * haven't actually changed.\n */\n private mergeRows(\n cached: Record<string, unknown>[] | undefined,\n incoming: Record<string, unknown>[],\n pks?: PrimaryKeyInfo[]\n ): Record<string, unknown>[] {\n if (!cached || cached.length === 0) return incoming;\n\n // Build a lookup from cached rows by address for O(1) access\n const cachedById = new Map<string, Record<string, unknown>>();\n for (const row of cached) {\n const address = this.rowAddress(row, pks);\n if (address !== undefined) cachedById.set(address, row);\n }\n\n // Read once per frame, not per row.\n const explain = realtimeDebugOn();\n\n return incoming.map(incomingRow => {\n const address = this.rowAddress(incomingRow, pks);\n const cachedRow = address === undefined ? undefined : cachedById.get(address);\n if (!cachedRow) return incomingRow;\n\n // Compare flat rows directly (no more path/values nesting)\n if (this.deepEqual(cachedRow, incomingRow)) return cachedRow;\n\n // Why it differs, key by key — a debugging aid, so only while\n // realtime debugging is on. It used to run for every changed row\n // of every frame, in production: a bulk write to a 1,000-row live\n // list was 1,000 `JSON.stringify` calls and console lines a frame.\n if (explain) {\n const mismatches: Record<string, { cached: unknown, incoming: unknown }> = {};\n const allKeys = new Set([...Object.keys(cachedRow), ...Object.keys(incomingRow)]);\n for (const key of allKeys) {\n if (!this.deepEqual(cachedRow[key], incomingRow[key])) {\n mismatches[key] = { cached: cachedRow[key],\nincoming: incomingRow[key] };\n }\n }\n console.debug(`[RebaseWS] Row ${address} refetch mismatch:\\n`, JSON.stringify(mismatches, null, 2));\n }\n return incomingRow;\n });\n }\n\n // Subscription methods\n listenCollection<M extends Record<string, unknown>>(\n // The subscribe frame, minus the callbacks — so `page`, which only a\n // subscription takes (a fetch resolves it to an offset before it gets\n // here), is expressible. The subscription key is derived from these\n // props rather than a hand-listed subset, so a new field narrows the\n // key by construction and two different queries cannot collide on it.\n props: Omit<ListenCollectionProps<M>, \"onUpdate\" | \"onError\">,\n onUpdate: (rows: Record<string, unknown>[], meta?: CollectionUpdateMeta) => void,\n onError?: (error: Error) => void\n ): () => void {\n // A subscription is the app asking for live data, so this is where the\n // socket is wanted. Called before the dedup check below: joining an\n // existing subscription must still work if the socket has since gone.\n this.ensureConnected();\n\n const subscriptionKey = this.createCollectionSubscriptionKey(props);\n const callbackId = `callback_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n // Check if we already have a subscription for these exact parameters\n const existingSubscription = this.collectionSubscriptions.get(subscriptionKey);\n\n if (existingSubscription) {\n // Reuse existing subscription - just add the new callback\n const callbackMap = existingSubscription.callbacks as Map<string, {\n onUpdate: (rows: Record<string, unknown>[], meta?: CollectionUpdateMeta) => void;\n onError?: (error: Error) => void;\n }>;\n callbackMap.set(callbackId, { onUpdate,\nonError });\n this.reportOutageTo(onError);\n\n // Immediately fire the callback with cached data if available\n if (existingSubscription.latestData !== undefined && existingSubscription.isInitialDataReceived) {\n try {\n onUpdate(existingSubscription.latestData, existingSubscription.latestMeta);\n } catch (error) {\n console.error(\"Error in collection subscription callback:\", error);\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n } else if (!existingSubscription.subscribeInFlight) {\n // Registered but idle: its subscribe never landed (the send failed,\n // or the server answered with an error). Nothing is coming, so\n // re-issue it — otherwise this listener waits forever.\n this.sendCollectionSubscribe(subscriptionKey);\n }\n\n // Return unsubscribe function\n return () => {\n callbackMap.delete(callbackId);\n if (callbackMap.size === 0) {\n // Only tear down if this is still the same registration — a\n // failed subscribe may have replaced it in the meantime.\n if (this.collectionSubscriptions.get(subscriptionKey) !== existingSubscription) return;\n if (existingSubscription.subscribeTimeout) clearTimeout(existingSubscription.subscribeTimeout);\n this.collectionSubscriptions.delete(subscriptionKey);\n this.backendToCollectionKey.delete(existingSubscription.backendSubscriptionId);\n if (this.isConnected && this.ws) {\n this.sendMessage({\n type: \"unsubscribe\",\n payload: { subscriptionId: existingSubscription.backendSubscriptionId }\n }).catch(console.error);\n }\n }\n };\n }\n\n // Create new subscription\n const backendSubscriptionId = `collection_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n const callbackMap = new Map<string, {\n onUpdate: (rows: Record<string, unknown>[], meta?: CollectionUpdateMeta) => void;\n onError?: (error: Error) => void;\n }>();\n callbackMap.set(callbackId, { onUpdate,\nonError });\n this.reportOutageTo(onError);\n\n this.collectionSubscriptions.set(subscriptionKey, {\n backendSubscriptionId,\n callbacks: callbackMap,\n props\n });\n\n // Add reverse lookup\n this.backendToCollectionKey.set(backendSubscriptionId, subscriptionKey);\n\n // Send subscription request to backend. A failure here drops the\n // registration and notifies every listener, so the next mount retries.\n this.sendCollectionSubscribe(subscriptionKey);\n\n // Return unsubscribe function\n return () => {\n const subscription = this.collectionSubscriptions.get(subscriptionKey);\n if (subscription) {\n const callbacks = subscription.callbacks;\n callbacks.delete(callbackId);\n if (callbacks.size === 0) {\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n this.collectionSubscriptions.delete(subscriptionKey);\n this.backendToCollectionKey.delete(subscription.backendSubscriptionId);\n if (this.isConnected && this.ws) {\n this.sendMessage({\n type: \"unsubscribe\",\n payload: { subscriptionId: subscription.backendSubscriptionId }\n }).catch(console.error);\n }\n }\n }\n };\n }\n\n listenOne<M extends Record<string, unknown>>(\n props: ListenOneTarget<M>,\n onUpdate: (row: Record<string, unknown> | null) => void,\n onError?: (error: Error) => void\n ): () => void {\n this.ensureConnected();\n\n const subscriptionKey = this.createSingleSubscriptionKey(props);\n const callbackId = `callback_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n // Check if we already have a subscription for these exact parameters\n const existingSubscription = this.singleSubscriptions.get(subscriptionKey);\n\n if (existingSubscription) {\n // Reuse existing subscription - just add the new callback\n const callbackMap = existingSubscription.callbacks as Map<string, {\n onUpdate: (row: Record<string, unknown> | null) => void;\n onError?: (error: Error) => void;\n }>;\n callbackMap.set(callbackId, { onUpdate,\nonError });\n this.reportOutageTo(onError);\n\n // Immediately fire the callback with cached data if available\n if (existingSubscription.latestData !== undefined && existingSubscription.isInitialDataReceived) {\n try {\n onUpdate(existingSubscription.latestData);\n } catch (error) {\n console.error(\"Error in row subscription callback:\", error);\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n } else if (!existingSubscription.subscribeInFlight) {\n // See listenCollection: a registration with nothing in flight is\n // dead, and attaching to it silently would hang this listener.\n this.sendEntitySubscribe(subscriptionKey);\n }\n\n // Return unsubscribe function\n return () => {\n callbackMap.delete(callbackId);\n if (callbackMap.size === 0) {\n if (this.singleSubscriptions.get(subscriptionKey) !== existingSubscription) return;\n if (existingSubscription.subscribeTimeout) clearTimeout(existingSubscription.subscribeTimeout);\n // No more callbacks, unsubscribe from backend\n this.singleSubscriptions.delete(subscriptionKey);\n this.backendToEntityKey.delete(existingSubscription.backendSubscriptionId);\n if (this.isConnected && this.ws) {\n this.sendMessage({\n type: \"unsubscribe\",\n payload: { subscriptionId: existingSubscription.backendSubscriptionId }\n }).catch(console.error);\n }\n }\n };\n }\n\n // Create new subscription\n const backendSubscriptionId = `entity_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n const callbackMap = new Map<string, {\n onUpdate: (row: Record<string, unknown> | null) => void;\n onError?: (error: Error) => void;\n }>();\n callbackMap.set(callbackId, { onUpdate,\nonError });\n this.reportOutageTo(onError);\n\n this.singleSubscriptions.set(subscriptionKey, {\n backendSubscriptionId,\n callbacks: callbackMap,\n props\n });\n\n // Add reverse lookup\n this.backendToEntityKey.set(backendSubscriptionId, subscriptionKey);\n\n // Send subscription request to backend\n this.sendEntitySubscribe(subscriptionKey);\n\n // Return unsubscribe function\n return () => {\n const subscription = this.singleSubscriptions.get(subscriptionKey);\n if (subscription) {\n const callbacks = subscription.callbacks;\n callbacks.delete(callbackId);\n if (callbacks.size === 0) {\n this.singleSubscriptions.delete(subscriptionKey);\n this.backendToEntityKey.delete(subscription.backendSubscriptionId);\n if (this.isConnected && this.ws) {\n this.sendMessage({\n type: \"unsubscribe\",\n payload: { subscriptionId: subscription.backendSubscriptionId }\n }).catch(console.error);\n }\n }\n }\n };\n }\n\n /**\n * Send a `subscribe_collection` for an already-registered subscription and\n * arm its watchdog.\n *\n * Every path that registers a collection subscription goes through here, so\n * that a subscribe which never lands — a rejected send, or a server that\n * never answers — always ends up in `failCollectionSubscription` rather than\n * leaving the entry parked with `isInitialDataReceived === false` forever.\n */\n private sendCollectionSubscribe(subscriptionKey: string): void {\n const subscription = this.collectionSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n\n const backendSubscriptionId = subscription.backendSubscriptionId;\n subscription.subscribeInFlight = true;\n\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n subscription.subscribeTimeout = undefined;\n // Only time out a frame that is actually on the wire. While offline the\n // message just sits in the queue, and reconnect backoff can exceed the\n // timeout — `armPendingSubscribeWatchdogs` picks these up on connect.\n if (this.isConnected) this.sendCollectionSubscribeWatchdog(subscriptionKey);\n\n this.sendMessage({\n type: \"subscribe_collection\",\n // So a refusal addressed by request id names the subscription —\n // see the subscription-error branch of `handleWebSocketMessage`.\n requestId: backendSubscriptionId,\n payload: {\n ...subscription.props,\n subscriptionId: backendSubscriptionId\n }\n }).catch(error => {\n const current = this.collectionSubscriptions.get(subscriptionKey);\n if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;\n this.failCollectionSubscription(\n subscriptionKey,\n error instanceof Error ? error : new Error(String(error))\n );\n });\n }\n\n /** The `listenOne` counterpart of {@link sendCollectionSubscribe}. */\n private sendEntitySubscribe(subscriptionKey: string): void {\n const subscription = this.singleSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n\n const backendSubscriptionId = subscription.backendSubscriptionId;\n subscription.subscribeInFlight = true;\n\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n subscription.subscribeTimeout = undefined;\n if (this.isConnected) this.sendEntitySubscribeWatchdog(subscriptionKey);\n\n this.sendMessage({\n type: \"subscribe_one\",\n requestId: backendSubscriptionId,\n payload: {\n ...subscription.props,\n subscriptionId: backendSubscriptionId,\n // Ask for the row's `ETag` on every frame. A server that\n // predates it ignores the key and sends untagged frames, which\n // read as rows with no version — what they were before.\n etag: true\n }\n }).catch(error => {\n const current = this.singleSubscriptions.get(subscriptionKey);\n if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;\n this.failEntitySubscription(\n subscriptionKey,\n error instanceof Error ? error : new Error(String(error))\n );\n });\n }\n\n /**\n * Report a subscribe failure to every listener and drop the registration.\n *\n * Dropping it is the point: the callbacks stay live (their components are\n * still mounted and have been told), but the next `listenCollection` for\n * these params finds no entry and issues a fresh subscribe instead of\n * silently attaching to a dead one.\n */\n private failCollectionSubscription(subscriptionKey: string, error: Error): void {\n const subscription = this.collectionSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n subscription.subscribeInFlight = false;\n\n this.collectionSubscriptions.delete(subscriptionKey);\n this.backendToCollectionKey.delete(subscription.backendSubscriptionId);\n\n subscription.callbacks.forEach(callback => {\n if (callback.onError) {\n try {\n callback.onError(error);\n } catch (callbackError) {\n console.error(\"Error in collection subscription error callback:\", callbackError);\n }\n }\n });\n }\n\n /** The `listenOne` counterpart of {@link failCollectionSubscription}. */\n private failEntitySubscription(subscriptionKey: string, error: Error): void {\n const subscription = this.singleSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n\n if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);\n subscription.subscribeInFlight = false;\n\n this.singleSubscriptions.delete(subscriptionKey);\n this.backendToEntityKey.delete(subscription.backendSubscriptionId);\n\n subscription.callbacks.forEach(callback => {\n if (callback.onError) {\n try {\n callback.onError(error);\n } catch (callbackError) {\n console.error(\"Error in row subscription error callback:\", callbackError);\n }\n }\n });\n }\n\n /**\n * Stop the watchdogs without failing anything — used when the socket drops,\n * since the reconnect path re-subscribes everything anyway and a watchdog\n * firing mid-reconnect would tear down healthy subscriptions.\n */\n private suspendSubscribeWatchdogs(): void {\n for (const sub of this.collectionSubscriptions.values()) {\n if (sub.subscribeTimeout) clearTimeout(sub.subscribeTimeout);\n sub.subscribeTimeout = undefined;\n sub.subscribeInFlight = false;\n }\n for (const sub of this.singleSubscriptions.values()) {\n if (sub.subscribeTimeout) clearTimeout(sub.subscribeTimeout);\n sub.subscribeTimeout = undefined;\n sub.subscribeInFlight = false;\n }\n }\n\n /**\n * Drop every registration's cached rows, keeping the registrations.\n *\n * A registration without rows counts as not loaded, so the next listener\n * to attach asks the server (or waits for the resubscribe) instead of\n * being handed what is cached.\n */\n private forgetCachedData(): void {\n for (const sub of this.collectionSubscriptions.values()) {\n sub.latestData = undefined;\n sub.latestMeta = undefined;\n sub.lastUpdated = undefined;\n sub.isInitialDataReceived = false;\n }\n for (const sub of this.singleSubscriptions.values()) {\n sub.latestData = undefined;\n sub.lastUpdated = undefined;\n sub.isInitialDataReceived = false;\n }\n }\n\n /**\n * Arm watchdogs for subscribes that were requested while offline and have\n * just been flushed to the socket. Their timers were deliberately not set at\n * request time, so without this they would have no timeout at all.\n */\n private armPendingSubscribeWatchdogs(): void {\n for (const [key, sub] of this.collectionSubscriptions.entries()) {\n if (sub.subscribeInFlight && !sub.subscribeTimeout) this.sendCollectionSubscribeWatchdog(key);\n }\n for (const [key, sub] of this.singleSubscriptions.entries()) {\n if (sub.subscribeInFlight && !sub.subscribeTimeout) this.sendEntitySubscribeWatchdog(key);\n }\n }\n\n private sendCollectionSubscribeWatchdog(subscriptionKey: string): void {\n const subscription = this.collectionSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n const backendSubscriptionId = subscription.backendSubscriptionId;\n subscription.subscribeTimeout = setTimeout(() => {\n const current = this.collectionSubscriptions.get(subscriptionKey);\n if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;\n if (!current.subscribeInFlight) return;\n this.failCollectionSubscription(\n subscriptionKey,\n new RebaseApiError(\"Subscription timed out\", { code: \"SUBSCRIPTION_TIMEOUT\" })\n );\n }, this.subscriptionTimeoutMs);\n }\n\n private sendEntitySubscribeWatchdog(subscriptionKey: string): void {\n const subscription = this.singleSubscriptions.get(subscriptionKey);\n if (!subscription) return;\n const backendSubscriptionId = subscription.backendSubscriptionId;\n subscription.subscribeTimeout = setTimeout(() => {\n const current = this.singleSubscriptions.get(subscriptionKey);\n if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;\n if (!current.subscribeInFlight) return;\n this.failEntitySubscription(\n subscriptionKey,\n new RebaseApiError(\"Subscription timed out\", { code: \"SUBSCRIPTION_TIMEOUT\" })\n );\n }, this.subscriptionTimeoutMs);\n }\n\n /**\n * Re-send all active subscriptions to the backend after a reconnect.\n * The server wipes subscription state when a client disconnects, so\n * we need to re-register everything to resume receiving updates.\n */\n private resubscribeAll(): void {\n this.connectionDebug(`[WS] Re-subscribing: ${this.collectionSubscriptions.size} collection(s), ${this.singleSubscriptions.size} row(ies)`);\n\n // A registration still in flight here had its subscribe sent on this\n // socket already, while it was authenticating: the close clears the\n // flag on every registration, and the queue hands its own subscribes\n // back to this pass. Re-sending one of those opened a second server\n // subscription under a new id and orphaned the first — every write was\n // refetched twice, and the extra one was never unsubscribed.\n\n // Re-subscribe collection subscriptions\n for (const [key, sub] of this.collectionSubscriptions.entries()) {\n if (sub.subscribeInFlight) continue;\n // Generate a fresh backend ID since the old one is no longer valid on the server\n const oldBackendId = sub.backendSubscriptionId;\n const newBackendId = `collection_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n sub.backendSubscriptionId = newBackendId;\n\n // Update reverse lookup\n this.backendToCollectionKey.delete(oldBackendId);\n this.backendToCollectionKey.set(newBackendId, key);\n\n this.sendCollectionSubscribe(key);\n }\n\n // Re-subscribe row subscriptions\n for (const [key, sub] of this.singleSubscriptions.entries()) {\n if (sub.subscribeInFlight) continue;\n const oldBackendId = sub.backendSubscriptionId;\n const newBackendId = `entity_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n sub.backendSubscriptionId = newBackendId;\n\n this.backendToEntityKey.delete(oldBackendId);\n this.backendToEntityKey.set(newBackendId, key);\n\n this.sendEntitySubscribe(key);\n }\n }\n\n private createCollectionSubscriptionKey(props: Omit<ListenCollectionProps, \"onUpdate\" | \"onError\">): string {\n // Derived from the props, not a hand-listed subset of them.\n //\n // Two subscriptions share one server subscription when their keys\n // match, so a field left off the key makes two different queries\n // collide and hands the second listener the first one's rows. `offset`\n // and `logical` were both missing: page two of a live list showed page\n // one, and two views filtered by different `or(...)` groups saw the\n // same rows. Listing fields by hand is what let that happen, so the key\n // now covers whatever `FetchCollectionProps` carries.\n //\n // `collection` is the exception: it is the whole collection config,\n // property thunks and all, so it contributes its name as before.\n const { collection, ...query } = props as FetchCollectionProps & Record<string, unknown>;\n const key = {\n ...query,\n collection: collection?.name\n };\n // Use replacer function (not array) to sort keys at all levels for deterministic output\n return JSON.stringify(key, (_, value) => {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return Object.keys(value).sort().reduce((sorted: Record<string, unknown>, k) => {\n sorted[k] = value[k];\n return sorted;\n }, {});\n }\n return value;\n });\n }\n\n private createSingleSubscriptionKey(props: ListenOneTarget): string {\n // Listeners asking for different relations are different reads: one\n // shared subscription would hand each the other's shape.\n const include = props.include !== undefined ? `|${JSON.stringify(props.include)}` : \"\";\n return `${props.path}|${props.id}${include}`;\n }\n}\n\n/** What an aborted signal rejects with: its reason, or an `AbortError` as `fetch` gives. */\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException(\"The operation was aborted.\", \"AbortError\");\n}\n","/**\n * Broadcast channels and presence, as an SDK surface.\n *\n * The realtime engine has supported `join_channel`, `broadcast`,\n * `presence_track`, `presence_untrack` and `presence_state` for a while, but\n * the client only recognised those types well enough to send them\n * fire-and-forget: there were no methods to call and no way to receive channel\n * or broadcast events, since `on()` handles only connect / disconnect /\n * reconnect / error. Anything wanting presence therefore opened a *second*\n * socket and reimplemented the AUTHENTICATE → AUTH_SUCCESS handshake, the\n * reconnect backoff, and the presence heartbeat — a couple of hundred lines\n * per app, all of it duplicating this package.\n *\n * Two protocol details this hides, because both are easy to get wrong and\n * neither is discoverable from the message list:\n *\n * - **A joining client is told only about its own join.** The `presence_diff`\n * it receives after `presence_track` contains just itself. The existing\n * roster arrives only in response to an explicit `presence_state` request,\n * so `join()` sends one.\n * - **Presence expires after 30s** (`PRESENCE_TIMEOUT_MS` server-side). A\n * client that tracks once and goes quiet silently vanishes from everyone\n * else's roster while still sitting in the document, so `track()` starts a\n * heartbeat and `leave()` stops it.\n */\n\n/**\n * Presence state keyed by the server's client id.\n *\n * Re-exported rather than re-declared, for the same reason\n * {@link ChannelHistoryEntry} is below: the frames that carry a roster are\n * declared in `@rebasepro/types`, and a second copy of the shape here is a\n * second thing to keep in step with the wire.\n */\nexport type { PresenceState } from \"@rebasepro/types\";\nimport type { ChannelHistoryEntry, ChannelMessage, PresenceState } from \"@rebasepro/types\";\n\nexport interface PresenceDiff {\n joins: PresenceState;\n leaves: PresenceState;\n}\n\nexport interface BroadcastEvent {\n event: string;\n payload: unknown;\n /**\n * Per-channel sequence number, present only on retained channels.\n *\n * Monotonically increasing and dense, so a consumer that remembers the last\n * one it applied can tell the server exactly where to resume from.\n */\n seq?: number;\n /**\n * True when this arrived through catch-up rather than live.\n *\n * Handlers do not have to care — replayed messages are delivered to the\n * same `onBroadcast` handlers, in sequence order, so an operation stream\n * needs no second code path. It is exposed for consumers that want to,\n * for example, skip an animation while fast-forwarding.\n */\n replayed?: boolean;\n}\n\n/**\n * One retained message, as returned by {@link RebaseRealtimeChannel.history}.\n *\n * Re-exported rather than re-declared: the copy that used to live here had\n * drifted `at` to optional, while the server always sends it.\n */\nexport type { ChannelHistoryEntry } from \"@rebasepro/types\";\nimport { RebaseApiError } from \"@rebasepro/types\";\nimport { unref } from \"@rebasepro/utils\";\n\n/** The answer to a catch-up request. */\nexport interface ChannelHistoryResult {\n messages: ChannelHistoryEntry[];\n /**\n * Whether the server retains anything for this channel.\n *\n * False means there is no retention rule configured for it, so the empty\n * list means \"never keeps history\" rather than \"you missed nothing\" — a\n * client that needs to converge has to fall back to a full resync.\n */\n retained: boolean;\n /** Highest sequence the server holds, even if this batch was capped. */\n latestSeq?: number;\n}\n\n/** Options for a channel handle. */\nexport interface ChannelOptions {\n /**\n * Ask the server to replay what this client missed, on join and on every\n * reconnect.\n *\n * Only meaningful for a channel the *server* has a retention rule for —\n * retention is configured on the backend, since a channel is created by\n * whoever names it and a client-chosen history depth would let any visitor\n * commit the backend to unbounded storage. On a channel with no rule the\n * server answers `retained: false` and this is inert.\n */\n history?: boolean;\n}\n\n/** The socket operations a channel needs; satisfied by RebaseWebSocketClient. */\nexport interface ChannelTransport {\n sendMessage(message: Record<string, unknown>): Promise<unknown>;\n onChannelMessage(channel: string, handler: (message: ChannelMessage) => void): () => void;\n onReconnect(handler: () => void): () => void;\n /**\n * Notified once per outage that outlasted a blip. Optional so a test\n * transport need not implement it.\n */\n onConnectionLost?(handler: (error: RebaseApiError) => void): () => void;\n /**\n * Whether the server agreed, for the current socket, to `name` (see the\n * socket's `hello`). A transport without it is one whose server agreed to\n * nothing.\n */\n hasServerOptIn?(name: string): boolean;\n /** The current socket's id on the server: the `senderId` of what it broadcasts. */\n readonly socketClientId?: string;\n}\n\n/**\n * A message held back while a catch-up is in flight — or, `own`, the number\n * this client's own broadcast took, which moves the watermark and is never\n * delivered (a sender is not sent its own messages).\n */\ntype HeldBack = BroadcastEvent & { own?: true };\n\n/**\n * Re-send presence comfortably inside the server's 30s expiry.\n *\n * Two-thirds of the window: one lost heartbeat still leaves time for the next\n * before the entry is reaped, so a single dropped frame is not a disappearance.\n */\nconst PRESENCE_HEARTBEAT_MS = 20_000;\n\n/**\n * How long live messages are held back waiting for a catch-up response.\n *\n * Short, because the cost of waiting is visible — on a collaborative document\n * this is a stall in everyone else's edits appearing. Long enough that a slow\n * replay of a busy channel is not abandoned needlessly.\n */\nconst CATCH_UP_TIMEOUT_MS = 10_000;\n\nexport class RebaseRealtimeChannel {\n private presenceHandlers = new Set<(state: PresenceState, diff?: PresenceDiff) => void>();\n private broadcastHandlers = new Set<(event: BroadcastEvent) => void>();\n private errorHandlers = new Set<(error: RebaseApiError) => void>();\n private unsubscribers: (() => void)[] = [];\n\n /** Last known roster, kept so handlers always get a full picture. */\n private presences: PresenceState = {};\n /** What this client last tracked, replayed on reconnect and heartbeat. */\n private trackedState: Record<string, unknown> | null = null;\n private heartbeat: ReturnType<typeof setInterval> | null = null;\n private joined = false;\n\n /** Whether this handle asks the server to replay missed messages. */\n private wantsHistory: boolean;\n\n /**\n * Highest sequence number delivered to handlers so far.\n *\n * This is the resume point sent as `sinceSeq`, and the watermark that makes\n * replay idempotent: catch-up ranges overlap with what arrived live, and\n * anything at or below this has already been seen.\n */\n private lastSeq = 0;\n\n /**\n * Live messages that arrived while a catch-up was in flight.\n *\n * Without this they would be delivered ahead of the older messages being\n * fetched, and — worse — would advance {@link lastSeq} past them, so the\n * catch-up response would then be discarded as already-seen and those\n * messages would be lost for good. Held here and flushed, in order, once\n * the replay lands.\n */\n private pendingLive: HeldBack[] = [];\n private catchUpInFlight = false;\n\n /**\n * Whether {@link lastSeq} is a position this client reached, rather than\n * the zero of a handle that has not caught up yet.\n *\n * A client joining for the first time asks for whatever is retained, so\n * history pruned before it ever joined is not something it missed. Only a\n * client with a position can have a hole in what it saw.\n */\n private positioned = false;\n\n /** The page size the catch-up in flight asked for, reused for its later pages. */\n private catchUpLimit: number | undefined;\n\n /**\n * `latestSeq` of an empty page that left this client behind, while a\n * second request confirms it.\n *\n * The server reads the retained messages and then the cursor, so a message\n * committed between the two shows up in `latestSeq` and not in the page.\n * The second request returns it if it exists; only a second empty page\n * means the messages up to this seq are gone.\n */\n private emptyTailSeq: number | null = null;\n\n /**\n * Deadline for a catch-up response.\n *\n * Buffering live messages is only safe because the wait is bounded. A\n * catch-up frame that never arrives — a server that dropped it, a socket\n * that died between request and reply — would otherwise leave the channel\n * silently holding every subsequent edit forever, which is a worse failure\n * than the one replay was added to fix.\n */\n private catchUpTimeout: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Callers of {@link history} awaiting the next `channel_history` frame.\n *\n * These frames are addressed by channel rather than by request id, so they\n * are matched in arrival order. Requests on one channel are serialized by\n * the socket, so FIFO is the right correlation here.\n */\n private historyWaiters: Array<(result: ChannelHistoryResult) => void> = [];\n\n constructor(\n public readonly name: string,\n private transport: ChannelTransport,\n options: ChannelOptions = {}\n ) {\n this.wantsHistory = options.history ?? false;\n }\n\n /**\n * Turn on catch-up for a handle that was created without it.\n *\n * The client hands back the same channel object for a given name, so a\n * later `channel(name, { history: true })` has no new object to configure —\n * it upgrades this one instead. Idempotent, and never downgrades: one\n * caller asking for history must not be switched off by another that did\n * not ask.\n */\n enableHistory(): void {\n if (this.wantsHistory) return;\n this.wantsHistory = true;\n if (this.joined) void this.requestHistory();\n }\n\n /**\n * Join the channel and ask for the current roster.\n *\n * Called automatically by `track`, `broadcast`, `onPresence` and\n * `onBroadcast`; calling it directly is only needed to start receiving\n * before there is anything to send.\n */\n /**\n * Send a channel message.\n *\n * Every channel message is read by the server out of a `payload` envelope\n * (`payload?.channel`, `payload?.state`, `payload?.event`). Sending those\n * fields flat does not error: `payload?.channel` simply reads as\n * `undefined`, so the client is registered into channel `undefined` with\n * empty state, and the echo comes back with no `channel` for\n * `onChannelMessage` to match — presence and broadcast both go quiet with\n * nothing logged. Funnelled through one place so a new message type cannot\n * reintroduce that.\n */\n private send(type: string, fields: Record<string, unknown> = {}): Promise<unknown> {\n return this.transport.sendMessage({ type, payload: { channel: this.name, ...fields } });\n }\n\n async join(): Promise<void> {\n if (this.joined) return;\n this.joined = true;\n\n this.unsubscribers.push(\n this.transport.onChannelMessage(this.name, (message) => this.handle(message))\n );\n\n // A reconnect drops server-side channel membership and presence, so\n // both have to be re-established. Nothing else notices this: the\n // socket comes back, and the client just stops receiving.\n this.unsubscribers.push(\n this.transport.onReconnect(() => {\n void this.rejoin();\n })\n );\n\n // An outage longer than a blip: nothing arrives on this channel until\n // the socket is back, and the app should be able to say so. The\n // re-join above is the recovery.\n const onConnectionLost = this.transport.onConnectionLost?.((error) => this.emitError(error));\n if (onConnectionLost) this.unsubscribers.push(onConnectionLost);\n\n await this.send(\"join_channel\");\n // Not optional. Joining does not push the roster — without this the\n // channel believes it is alone until somebody else happens to move.\n await this.send(\"presence_state\");\n if (this.wantsHistory) await this.requestHistory();\n }\n\n private async rejoin(): Promise<void> {\n try {\n await this.send(\"join_channel\");\n await this.send(\"presence_state\");\n if (this.trackedState) {\n await this.send(\"presence_track\", { state: this.trackedState });\n }\n // The reason this class tracks a sequence number at all: whatever\n // was broadcast while the socket was down was delivered to everyone\n // else and never to us. Asking from `lastSeq` is the difference\n // between resuming and resyncing the whole document.\n if (this.wantsHistory) await this.requestHistory();\n } catch {\n // The socket is down again; the next reconnect will retry.\n }\n }\n\n /**\n * Ask the server for everything after {@link lastSeq}.\n *\n * Live messages are buffered from here until the answer arrives — see\n * {@link pendingLive}.\n */\n private async requestHistory(limit?: number): Promise<void> {\n this.catchUpInFlight = true;\n this.catchUpLimit = limit;\n\n if (this.catchUpTimeout) clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = setTimeout(() => this.abandonCatchUp(), CATCH_UP_TIMEOUT_MS);\n unref(this.catchUpTimeout);\n\n try {\n await this.send(\"channel_history\", {\n sinceSeq: this.lastSeq,\n ...(limit !== undefined ? { limit } : {})\n });\n } catch {\n // The frame never went out, so nothing will answer it.\n this.abandonCatchUp();\n }\n }\n\n /**\n * Give up waiting for a catch-up and release what was held back.\n *\n * The buffered messages are still the freshest thing this client has, so\n * they are delivered rather than dropped. Callers of {@link history} are\n * answered with `retained: false` — accurate in the sense that matters:\n * this client has no history to work from and has to resync.\n */\n private abandonCatchUp(): void {\n if (this.catchUpTimeout) {\n clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = null;\n }\n if (!this.catchUpInFlight) return;\n this.catchUpInFlight = false;\n this.emptyTailSeq = null;\n for (const resolve of this.historyWaiters.splice(0)) {\n resolve({ messages: [], retained: false });\n }\n this.flushPendingLive();\n }\n\n /**\n * Publish this client's presence state, and keep publishing it.\n *\n * Calling `track` again replaces the state (and restarts the heartbeat),\n * which is how you update e.g. a cursor position.\n */\n async track(state: Record<string, unknown>): Promise<void> {\n await this.join();\n this.trackedState = state;\n\n await this.send(\"presence_track\", { state });\n\n if (!this.heartbeat) {\n this.heartbeat = setInterval(() => {\n if (!this.trackedState) return;\n void this.send(\"presence_track\", { state: this.trackedState })\n .catch(() => { /* a dropped beat is recoverable; the next one carries the same state */ });\n }, PRESENCE_HEARTBEAT_MS);\n // Do not hold a Node process open just to say \"still here\".\n unref(this.heartbeat);\n }\n }\n\n /** Stop publishing presence, without leaving the channel. */\n async untrack(): Promise<void> {\n this.stopHeartbeat();\n this.trackedState = null;\n if (this.joined) {\n await this.send(\"presence_untrack\");\n }\n }\n\n /**\n * Observe the roster. The handler fires immediately with what is already\n * known, then on every change.\n */\n onPresence(handler: (state: PresenceState, diff?: PresenceDiff) => void): () => void {\n this.presenceHandlers.add(handler);\n void this.join();\n if (Object.keys(this.presences).length > 0) handler({ ...this.presences });\n return () => this.presenceHandlers.delete(handler);\n }\n\n /**\n * Send a broadcast. The sender does not receive its own message.\n *\n * Resolves when the frame is written to the socket, **not** when the server\n * has accepted it. Channel frames are fire-and-forget by design — a\n * collaborative app broadcasts a cursor position sixty times a second, and\n * waiting for an acknowledgement on each would make that a round trip.\n *\n * A refusal therefore arrives on {@link onError}, not as a rejection here.\n */\n async broadcast(event: string, payload: unknown): Promise<void> {\n await this.join();\n this.rememberOwnSender(this.transport.socketClientId);\n await this.send(\"broadcast\", { event, payload });\n }\n\n /**\n * The socket ids this channel has broadcast from — the current one and the\n * ones before a reconnect. A message in history from any of them is this\n * client's own, and a sender is never sent its own messages: replayed after\n * a reconnect whose `broadcast_ack` was lost, it was applied twice.\n */\n private readonly ownSenderIds = new Set<string>();\n\n private rememberOwnSender(id: string | undefined): void {\n if (id === undefined || this.ownSenderIds.has(id)) return;\n this.ownSenderIds.add(id);\n // A long-lived tab reconnects many times; the ones that matter are the\n // last few, whose messages a catch-up can still replay.\n if (this.ownSenderIds.size > 16) {\n const oldest = this.ownSenderIds.values().next().value;\n if (oldest !== undefined) this.ownSenderIds.delete(oldest);\n }\n }\n\n /**\n * Observe refusals about this channel.\n *\n * `CHANNEL_FORBIDDEN` when an authorizer refuses a join, a broadcast or a\n * history read; `RATE_LIMITED` past the channel frame budget;\n * `CHANNEL_HISTORY_WRITE_FAILED` / `CHANNEL_HISTORY_READ_FAILED` when\n * retention cannot be served; `CHANNEL_BUS_PAYLOAD_TOO_LARGE` when a\n * broadcast on an ephemeral channel is too big to cross the bus.\n *\n * `CHANNEL_HISTORY_GAP` is raised by the client: a catch-up found that the\n * server no longer retains messages this client never received, so its\n * state has a hole a replay cannot fill. `details` is `{ from, to }`, the\n * inclusive range of sequence numbers missed. Resync the channel's state\n * from its source of truth.\n *\n * These used to be dropped on the floor — there is no promise to reject on\n * a fire-and-forget frame, so a forbidden broadcast looked exactly like a\n * delivered one. With no handler attached they are logged as a warning,\n * because an unobserved refusal is the failure this exists for.\n *\n * @returns An unsubscribe function.\n */\n onError(handler: (error: RebaseApiError) => void): () => void {\n this.errorHandlers.add(handler);\n void this.join();\n return () => this.errorHandlers.delete(handler);\n }\n\n /** Observe broadcasts. Pass an event name to filter. */\n onBroadcast(handler: (event: BroadcastEvent) => void): () => void;\n onBroadcast(event: string, handler: (payload: unknown) => void): () => void;\n onBroadcast(\n eventOrHandler: string | ((event: BroadcastEvent) => void),\n maybeHandler?: (payload: unknown) => void\n ): () => void {\n const wrapped: (event: BroadcastEvent) => void = typeof eventOrHandler === \"string\"\n ? (e) => { if (e.event === eventOrHandler) maybeHandler!(e.payload); }\n : eventOrHandler;\n\n this.broadcastHandlers.add(wrapped);\n void this.join();\n return () => this.broadcastHandlers.delete(wrapped);\n }\n\n /**\n * The last sequence number this channel has delivered.\n *\n * Zero on a channel that retains nothing. Persist it if you want catch-up\n * to survive a page reload as well as a reconnect, and pass it back via\n * {@link history}.\n */\n get sequence(): number {\n return this.lastSeq;\n }\n\n /**\n * Fetch retained messages explicitly, instead of waiting for join or\n * reconnect to do it.\n *\n * Defaults to resuming from {@link sequence}. Messages are delivered to\n * `onBroadcast` handlers as usual — the returned value is for callers that\n * want to inspect the batch, or to learn from `retained` that the channel\n * keeps no history at all.\n */\n async history(options: { sinceSeq?: number; limit?: number } = {}): Promise<ChannelHistoryResult> {\n await this.join();\n if (options.sinceSeq !== undefined) {\n this.lastSeq = options.sinceSeq;\n this.positioned = options.sinceSeq > 0;\n }\n\n const result = new Promise<ChannelHistoryResult>((resolve) => {\n this.historyWaiters.push(resolve);\n });\n await this.requestHistory(options.limit);\n return result;\n }\n\n /** Leave the channel and release every listener and timer. */\n async leave(): Promise<void> {\n this.stopHeartbeat();\n this.trackedState = null;\n this.presences = {};\n this.presenceHandlers.clear();\n this.broadcastHandlers.clear();\n // A rejoin is a fresh start: replaying from a watermark left over from\n // the previous membership would silently skip everything before it.\n this.lastSeq = 0;\n this.positioned = false;\n this.emptyTailSeq = null;\n this.pendingLive = [];\n this.catchUpInFlight = false;\n if (this.catchUpTimeout) {\n clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = null;\n }\n for (const resolve of this.historyWaiters.splice(0)) {\n resolve({ messages: [], retained: false });\n }\n\n for (const off of this.unsubscribers) off();\n this.unsubscribers = [];\n\n if (this.joined) {\n this.joined = false;\n await this.send(\"leave_channel\");\n }\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeat) {\n clearInterval(this.heartbeat);\n this.heartbeat = null;\n }\n }\n\n /** Fold an incoming frame into the roster and fan it out. */\n private handle(message: ChannelMessage): void {\n switch (message.type) {\n case \"presence_state\": {\n this.presences = message.presences;\n this.emitPresence();\n break;\n }\n case \"presence_diff\": {\n const { joins, leaves } = message;\n // A diff carries only what moved, so the roster is maintained\n // here rather than handed to callers to reassemble.\n for (const [id, state] of Object.entries(joins)) this.presences[id] = state;\n for (const id of Object.keys(leaves)) delete this.presences[id];\n this.emitPresence({ joins, leaves });\n break;\n }\n case \"broadcast\": {\n const seq = message.seq;\n const event: BroadcastEvent = {\n event: message.event,\n payload: message.payload,\n ...(seq !== undefined ? { seq } : {})\n };\n\n // Unsequenced channels keep the original behaviour exactly:\n // straight through, no buffering, no watermark.\n if (seq === undefined) {\n this.deliver(event);\n break;\n }\n\n if (this.catchUpInFlight) {\n this.pendingLive.push(event);\n break;\n }\n if (seq <= this.lastSeq) break; // already delivered\n if (this.fillsGap(event)) break;\n this.lastSeq = seq;\n this.positioned = true;\n this.deliver(event);\n break;\n }\n case \"broadcast_ack\": {\n // This client's own message took `seq`. Nothing to deliver —\n // a sender is never sent its own broadcast — but the number\n // is accounted for, so it is not mistaken for a missed one.\n const held: HeldBack = { event: \"\", payload: undefined, seq: message.seq, own: true };\n if (this.catchUpInFlight) {\n this.pendingLive.push(held);\n break;\n }\n if (message.seq <= this.lastSeq) break;\n if (this.fillsGap(held)) break;\n this.lastSeq = message.seq;\n this.positioned = true;\n break;\n }\n case \"channel_history\": {\n if (this.catchUpTimeout) {\n clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = null;\n }\n\n const { messages: entries, retained, latestSeq } = message;\n\n for (const resolve of this.historyWaiters.splice(0)) {\n resolve({ messages: entries, retained, latestSeq });\n }\n\n // The log numbers every retained message densely, so a first\n // new entry past `lastSeq + 1` means retention pruned what lies\n // between. Said before the entries are delivered, so a handler\n // sees the hole where it is.\n const wasPositioned = this.positioned;\n const firstNew = entries.find((entry) => entry.seq > this.lastSeq);\n if (firstNew && wasPositioned && firstNew.seq > this.lastSeq + 1) {\n this.reportHistoryGap(this.lastSeq + 1, firstNew.seq - 1);\n }\n\n // Server-ordered ascending; the watermark check makes the\n // overlap with anything already seen a no-op rather than a\n // double-apply.\n const ownId = this.watchesForGaps() ? this.transport.socketClientId : undefined;\n for (const entry of entries) {\n if (entry.seq <= this.lastSeq) continue;\n this.lastSeq = entry.seq;\n // Sent from this very socket, which is never sent its own\n // messages live; a hole filled from history keeps to that.\n // So is one sent from this channel's socket before a\n // reconnect.\n if (ownId !== undefined && (entry.senderId === ownId || (entry.senderId !== undefined && this.ownSenderIds.has(entry.senderId)))) continue;\n this.deliver({\n event: entry.event,\n payload: entry.payload,\n seq: entry.seq,\n replayed: true\n });\n }\n if (retained) this.positioned = true;\n\n // One answer is one page. Until the watermark reaches the\n // server's cursor, live messages stay held back: flushing them\n // now would advance the watermark over the rest of the replay,\n // and nothing would ask for it again.\n const behind = retained && latestSeq !== undefined && latestSeq > this.lastSeq;\n if (behind && firstNew) {\n this.emptyTailSeq = null;\n void this.requestHistory(this.catchUpLimit);\n break;\n }\n if (behind && wasPositioned) {\n if (this.emptyTailSeq === null) {\n this.emptyTailSeq = latestSeq;\n void this.requestHistory(this.catchUpLimit);\n break;\n }\n // Confirmed: nothing after `lastSeq` is retained up to the\n // first page's cursor. A live message already held back is\n // real, so the hole ends before it.\n const firstLive = this.pendingLive.reduce<number | undefined>(\n (min, event) => event.seq !== undefined && (min === undefined || event.seq < min) ? event.seq : min,\n undefined\n );\n const end = firstLive !== undefined ? Math.min(this.emptyTailSeq, firstLive - 1) : this.emptyTailSeq;\n if (end > this.lastSeq) {\n this.reportHistoryGap(this.lastSeq + 1, end);\n this.lastSeq = end;\n }\n }\n\n this.emptyTailSeq = null;\n this.catchUpInFlight = false;\n this.flushPendingLive();\n break;\n }\n case \"ERROR\":\n case \"error\": {\n // The server refused something about this channel. Channel\n // frames are fire-and-forget — there is no promise to reject —\n // so this is where a refusal becomes visible.\n const raw = message.payload?.error;\n const asObject = typeof raw === \"object\" ? raw : undefined;\n const text = asObject?.message\n ?? (typeof raw === \"string\" ? raw : undefined)\n ?? message.error\n ?? \"The server refused a channel operation.\";\n this.emitError(new RebaseApiError(text, {\n ...(asObject?.code ? { code: asObject.code } : {})\n }));\n break;\n }\n }\n }\n\n /**\n * Whether a jump in the sequence means a missed message: only when the\n * server numbers this client's own broadcasts for it (`channel_seq`), so\n * its own messages leave no hole. Against a server that does not, a hole\n * is as likely to be the client's own message, and the old behaviour —\n * deliver, advance — stands.\n */\n private watchesForGaps(): boolean {\n return this.transport.hasServerOptIn?.(\"channel_seq\") === true;\n }\n\n /**\n * Hold `event` back and fetch what lies between it and {@link lastSeq},\n * when it is past a hole. A frame can go missing between instances — a\n * dropped bus connection, a bus that does not order — and the watermark\n * would otherwise step over it for good.\n *\n * @returns Whether it was held back.\n */\n private fillsGap(event: HeldBack): boolean {\n const seq = event.seq;\n if (seq === undefined || !this.positioned || seq <= this.lastSeq + 1 || !this.watchesForGaps()) return false;\n this.pendingLive.push(event);\n void this.requestHistory();\n return true;\n }\n\n /** Tell the app that retention dropped messages it never received. */\n private reportHistoryGap(from: number, to: number): void {\n this.emitError(new RebaseApiError(\n `Channel \"${this.name}\" missed messages ${from}–${to}: the server no longer retains them. Resync this channel's state.`,\n { code: \"CHANNEL_HISTORY_GAP\", details: { from, to } }\n ));\n }\n\n private emitError(error: RebaseApiError): void {\n if (this.errorHandlers.size === 0) {\n // Not silence: an unobserved refusal is exactly the failure\n // this exists for.\n console.warn(\n `[Rebase] Channel \"${this.name}\" error${error.code ? ` (${error.code})` : \"\"}: ${error.message}. ` +\n \"Attach `channel.onError(...)` to handle it.\"\n );\n return;\n }\n for (const handler of [...this.errorHandlers]) {\n try {\n handler(error);\n } catch (e) {\n console.error(\"Error in channel error handler:\", e);\n }\n }\n }\n\n /** Deliver everything held back during a catch-up, in sequence order. */\n private flushPendingLive(): void {\n if (this.pendingLive.length === 0) return;\n const buffered = this.pendingLive.sort((a, b) => (a.seq ?? 0) - (b.seq ?? 0));\n this.pendingLive = [];\n for (const event of buffered) {\n const seq = event.seq;\n if (seq !== undefined) {\n if (seq <= this.lastSeq) continue;\n this.lastSeq = seq;\n this.positioned = true;\n }\n if (event.own) continue;\n this.deliver(event);\n }\n }\n\n private deliver(event: BroadcastEvent): void {\n for (const handler of [...this.broadcastHandlers]) handler(event);\n }\n\n private emitPresence(diff?: PresenceDiff): void {\n const snapshot = { ...this.presences };\n for (const handler of this.presenceHandlers) handler(snapshot, diff);\n }\n}\n","import {\n isUnsupported,\n type ByIdReadOptions,\n type FindAllParams,\n type IterateParams,\n type LogicalCondition,\n type SDKCollectionClient,\n type UpdateValues,\n type UpsertOptions,\n type WhereFilterOp,\n type WhereValueFor,\n type WriteOptions\n} from \"@rebasepro/types\";\nimport type { FindParams } from \"./transport\";\nimport type {\n CollectionClient,\n LiveResult,\n ObserveByIdOptions,\n ObserveOptions,\n RestoreOptions,\n RowSnapshotMeta,\n TrashClient\n} from \"./collection\";\nimport type { OfflineApi, OfflineConfig, OfflineManager, OfflineQueueOptions, OfflineStatus } from \"./offline\";\nimport { SDKQueryBuilder } from \"./sdk_query_builder\";\n\ntype AnyRow = Record<string, unknown>;\ntype InnerFactory = (slug: string) => SDKCollectionClient<AnyRow>;\n\n/**\n * `createRebaseClient({ offline })`'s handle on the sync engine, which it\n * fetches with `import()` the moment the client is created.\n *\n * The engine ({@link OfflineManager} and the local query evaluator, store and\n * codec it brings) is most of the SDK's weight, and a client created without\n * `offline` never runs a line of it. It used to be a static import of the\n * client factory, so every app built on the SDK downloaded it before its first\n * paint whether or not it turned offline on.\n *\n * Everything the factory and the app reach is here, with the same shape:\n *\n * - Calls made before the engine has arrived are queued and run, in the order\n * they were made, the moment it does — the scope changes the factory makes\n * from auth events, and every collection a client wrapped meanwhile.\n * - What answers with a promise waits for the engine and then asks it.\n * - What answers synchronously answers what the engine would have before\n * anything ran: `status()` is the initial status, a subscription is\n * registered as soon as there is something to register it with, and its\n * unsubscribe works before and after.\n *\n * Once the engine is there every call goes straight to it.\n *\n * @internal\n */\nexport class LazyOfflineManager {\n private engine?: OfflineManager;\n private loadError?: unknown;\n private queued: ((engine: OfflineManager) => void)[] = [];\n private readonly failed: ((error: unknown) => void)[] = [];\n readonly api: OfflineApi;\n readonly keepsLastUser: boolean;\n\n constructor(config: OfflineConfig, createInner: InnerFactory) {\n this.keepsLastUser = config.keepLastUser === true;\n import(\"./offline\").then(({ OfflineManager: Engine }) => {\n const engine = new Engine(config, createInner);\n this.engine = engine;\n const queued = this.queued;\n this.queued = [];\n this.failed.length = 0;\n for (const op of queued) op(engine);\n }).catch((error: unknown) => {\n this.loadError = error;\n this.queued = [];\n console.error(\"[Rebase] The offline engine could not be loaded; offline is unavailable on this client.\", error);\n for (const reject of this.failed.splice(0)) reject(error);\n });\n\n this.api = {\n sync: async () => await (await this.run((engine) => engine.api.sync())),\n pending: async (options?: OfflineQueueOptions) => await (await this.run((engine) => engine.api.pending(options))),\n clear: async (options?: OfflineQueueOptions) => await (await this.run((engine) => engine.api.clear(options))),\n status: (): OfflineStatus => this.engine\n ? this.engine.api.status()\n : { online: !(typeof navigator !== \"undefined\" && navigator.onLine === false), syncing: false, pending: 0 },\n onStatusChange: (listener) => this.subscribe((engine) => engine.api.onStatusChange(listener)),\n onQueueChange: (listener) => this.subscribe((engine) => engine.api.onQueueChange(listener))\n };\n }\n\n /**\n * Run `op` on the engine: now if it is there, otherwise once it is, after\n * everything queued before it.\n */\n private run<T>(op: (engine: OfflineManager) => T): Promise<T> {\n if (this.engine) {\n try {\n return Promise.resolve(op(this.engine));\n } catch (error) {\n return Promise.reject(error);\n }\n }\n if (this.loadError !== undefined) return Promise.reject(this.loadError);\n return new Promise<T>((resolve, reject) => {\n this.failed.push(reject);\n this.queued.push((engine) => {\n try {\n resolve(op(engine));\n } catch (error) {\n reject(error);\n }\n });\n });\n }\n\n /** A subscription made now, registered on the engine as soon as there is one. */\n private subscribe(start: (engine: OfflineManager) => () => unknown, onError?: (error: Error) => void): () => void {\n if (this.engine) {\n const stop = start(this.engine);\n return () => { stop(); };\n }\n let stop: (() => unknown) | undefined;\n let cancelled = false;\n this.run((engine) => {\n if (!cancelled) stop = start(engine);\n }).catch((error: unknown) => {\n onError?.(error instanceof Error ? error : new Error(String(error)));\n });\n return () => {\n cancelled = true;\n stop?.();\n stop = undefined;\n };\n }\n\n holdScope(): void {\n void this.run((engine) => engine.holdScope()).catch(() => undefined);\n }\n\n setScope(uid: string | undefined, options?: { unverified?: boolean }): void {\n void this.run((engine) => engine.setScope(uid, options)).catch(() => undefined);\n }\n\n onReachedUnconfirmed(listener: () => void): () => void {\n return this.subscribe((engine) => engine.onReachedUnconfirmed(listener));\n }\n\n async resumeLastUser(): Promise<void> {\n await (await this.run((engine) => engine.resumeLastUser()));\n }\n\n dispose(): void {\n void this.run((engine) => engine.dispose()).catch(() => undefined);\n }\n\n /**\n * The engine's wrapper around `inner` — itself once the engine is there,\n * and until then a client of the same shape that hands each call to it.\n */\n wrap<M extends AnyRow>(slug: string, inner: CollectionClient<M>): CollectionClient<M> {\n if (this.engine) return this.engine.wrap(slug, inner);\n\n let real: CollectionClient<M> | undefined;\n void this.run((engine) => { real = engine.wrap(slug, inner); }).catch(() => undefined);\n const client = async (): Promise<CollectionClient<M>> => {\n if (!real) await this.run(() => undefined);\n return real!;\n };\n const live = (start: (target: CollectionClient<M>) => () => void, onError?: (error: Error) => void): () => void =>\n real ? start(real) : this.subscribe(() => start(real!), onError);\n\n const trash: TrashClient<M> = {\n find: async (params?: FindParams<M>) => (await client()).trash.find(params),\n count: async (params?: FindParams<M>) => (await client()).trash.count(params),\n restore: async (id: string | number, options?: RestoreOptions<M>) => (await client()).trash.restore(id, options),\n restoreMany: async (ids: (string | number)[], options?: RestoreOptions<M>) => (await client()).trash.restoreMany(ids, options),\n purge: async (id: string | number, options?: WriteOptions) => (await client()).trash.purge(id, options),\n purgeMany: async (ids: (string | number)[], options?: WriteOptions) => (await client()).trash.purgeMany(ids, options)\n };\n\n const deferred: CollectionClient<M> = {\n find: async (params?: FindParams<M>) => (await client()).find(params),\n iterate: (params?: IterateParams<M>) => (async function* () {\n yield* (await client()).iterate(params);\n })(),\n findAll: async (params?: FindAllParams<M>) => (await client()).findAll(params),\n findById: async (id: string | number, options?: ByIdReadOptions) => (await client()).findById(id, options),\n get: async (id: string | number) => (await client()).get(id),\n create: async (data: Partial<M>, id?: string | number, options?: WriteOptions) => (await client()).create(data, id, options),\n createMany: async (\n data: Partial<M>[],\n options?: { upsert?: boolean; onConflict?: readonly string[] } & WriteOptions\n ) => (await client()).createMany(data, options),\n upsert: async (data: Partial<M>, options?: UpsertOptions) => (await client()).upsert(data, options),\n updateMany: async (\n updates: { id: string | number; data: Partial<M> | UpdateValues<Partial<M>> }[],\n options?: WriteOptions\n ) => (await client()).updateMany(updates, options),\n deleteMany: async (ids: (string | number)[], options?: WriteOptions) => (await client()).deleteMany(ids, options),\n update: async (\n id: string | number,\n data: Partial<M> | UpdateValues<Partial<M>>,\n options?: WriteOptions\n ) => (await client()).update(id, data, options),\n delete: async (id: string | number, options?: WriteOptions) => (await client()).delete(id, options),\n count: async (params?: FindParams<M>): Promise<number> => (await client()).count(params),\n observe: (\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) => live((target) => target.observe(params, onResult, onError, options), onError),\n observeById: (\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveByIdOptions\n ) => live((target) => target.observeById(id, onResult, onError, options), onError),\n // As the engine does: a client that cannot listen keeps the inner\n // client's marker, so `isUnsupported(client.listen)` still says so.\n listen: isUnsupported(inner.listen)\n ? inner.listen\n : (params, onUpdate, onError) => live((target) => target.listen(params, onUpdate, onError), onError),\n listenById: isUnsupported(inner.listenById)\n ? inner.listenById\n : (id, onUpdate, onError, options) => live((target) => target.listenById(id, onUpdate, onError, options), onError),\n // The engine hands these to the inner client as they are.\n listenCount: inner.listenCount,\n aggregate: inner.aggregate,\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SDKQueryBuilder<M>(deferred);\n if (typeof columnOrCondition === \"object\") return builder.where(columnOrCondition);\n return builder.where(\n columnOrCondition as keyof M & string,\n operator!,\n value as WhereValueFor<WhereFilterOp, M[keyof M & string]>\n );\n },\n orderBy: (column, direction, nulls) => new SDKQueryBuilder<M>(deferred).orderBy(column, direction, nulls),\n limit: (count) => new SDKQueryBuilder<M>(deferred).limit(count),\n offset: (count) => new SDKQueryBuilder<M>(deferred).offset(count),\n search: (searchString, options) => new SDKQueryBuilder<M>(deferred).search(searchString, options),\n vectorSearch: (property, vector, options) => new SDKQueryBuilder<M>(deferred).vectorSearch(property, vector, options),\n include: (...relations) => new SDKQueryBuilder<M>(deferred).include(...relations),\n fields: (...columns) => new SDKQueryBuilder<M>(deferred).fields(...columns),\n distinct: (enabled) => new SDKQueryBuilder<M>(deferred).distinct(enabled),\n after: (cursor) => new SDKQueryBuilder<M>(deferred).after(cursor),\n trash\n };\n return deferred;\n }\n}\n","import { createTransport, RebaseApiError, RebaseClientConfig } from \"./transport\";\nimport { RebaseClientError } from \"./errors\";\nimport { createAuth, CreateAuthOptions, createMemoryStorage } from \"./auth\";\nimport { createAdmin, CreateAdminOptions } from \"./admin\";\nimport { createCron, CreateCronOptions } from \"./cron\";\nimport { createBackups } from \"./backups\";\nimport { createApiKeys, CreateApiKeysOptions, createPersonalKeys, CreatePersonalKeysOptions } from \"./api-keys\";\nimport { CollectionClient, createCollectionClient, invalidBulkBody } from \"./collection\";\nimport { createFunctionsClient } from \"./functions\";\nimport { createStorage } from \"./storage\";\nimport { ClientStorageSourceRegistry } from \"./storage-registry\";\nimport { RebaseWebSocketClient } from \"./websocket\";\nimport { RebaseRealtimeChannel, type ChannelOptions } from \"./realtime-channel\";\nimport type { OfflineApi, OfflineConfig } from \"./offline\";\nimport { LazyOfflineManager } from \"./offline-lazy\";\nimport {\n type AccessSimulation,\n type BatchOperation,\n type BatchResult,\n DEFAULT_STORAGE_SOURCE_KEY,\n type IncludeOf,\n InsertOf,\n RebaseClient,\n RebaseSdkData,\n RowOf,\n StorageSource,\n StorageSourceDefinition,\n StorageSourceRegistry,\n simulationHeaders,\n UpdateOf,\n type WriteOptions\n} from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n// ─── Public API surface ──────────────────────────────────────────────────────\n//\n// This barrel is the public API of `@rebasepro/client`. It is an explicit,\n// curated list — NOT `export *` — so that adding an export to a module below\n// does not silently republish it to app developers. Internal factories\n// (`createTransport`, `createAuth`, `createCollectionClient`, …), the raw\n// `Transport`, the storage-source registry impl, the JSON reviver, and the\n// concrete `SDKQueryBuilder` class are intentionally NOT re-exported: they are\n// implementation details of `createRebaseClient()` and have no external\n// consumers. App developers reach them through the client instance, never by\n// importing the factory. To add something to the public surface, add it here\n// deliberately.\n\n// Errors — the single error type thrown by SDK HTTP calls, plus the\n// data-proxy's unknown-collection error.\nexport { RebaseApiError } from \"./transport\";\nexport { RebaseClientError } from \"./errors\";\n// The codes `RebaseApiError.code` carries. An open union — routes add their own\n// — so it gives completion on the common ones without pretending to be closed.\nexport type { RebaseErrorCode } from \"@rebasepro/types\";\n\n// Query + collection types (annotate SDK results; construct via the fluent API).\nexport type { RebaseClientConfig, FindParams, FindResponse } from \"./transport\";\nexport type { CollectionClient, RestoreOptions, TrashClient } from \"./collection\";\nexport type { FindResult, SDKCollectionClient, SDKQueryBuilderInterface, PaginationMeta } from \"@rebasepro/types\";\n\n// Pagination: `iterate()` / `findAll()` parameter types and the error a walk\n// throws instead of quietly returning a truncated answer.\nexport type { IterateParams, FindAllParams, PageWalkOptions, CursorSpec } from \"@rebasepro/types\";\nexport { RebasePaginationError } from \"@rebasepro/common\";\nexport type { PaginationErrorCode } from \"@rebasepro/common\";\n\n// Logical-condition helpers for `.where(or(...), and(...))`.\nexport { or, and, not, cond } from \"@rebasepro/common\";\n\n// Files named inside text — a markdown body's images. The text stores a\n// `rebase-storage:` reference, never a URL whose download token expires;\n// `resolveStorageReferences(text, client)` exchanges each one for a fresh URL\n// when the text is rendered.\nexport {\n resolveStorageReferences,\n storageReference,\n parseStorageReference,\n isStorageReference,\n STORAGE_REFERENCE_SCHEME,\n storageTargetOfDownloadUrl,\n storageReferencesForDownloadUrls,\n storageTargetsInText\n} from \"@rebasepro/types\";\nexport type { StorageReferenceTarget, StorageReferenceResolver } from \"@rebasepro/types\";\n\n// Auth: session/token types, config, and the pluggable storage strategies.\nexport { createCookieStorage, createMemoryStorage } from \"./auth\";\nexport type { AuthConfig, AuthStorage, CookieStorageOptions, CreateAuthOptions, SessionRestoreOutcome } from \"./auth\";\n// `User` is re-exported alongside the session types because `client.auth` hands\n// one back and a browser app installs `@rebasepro/client` only — `@rebasepro/types`\n// is a transitive dependency there, not something a consumer can import from.\nexport type { User, RebaseSession, AuthTokens, AuthChangeEvent, DeviceSession } from \"@rebasepro/types\";\n\n// Control-plane client option/DTO types (the client instance exposes the impls).\nexport type { CreateAdminOptions, ListUsersOptions } from \"./admin\";\nexport type { AdminUser } from \"./admin\";\nexport type { CreateCronOptions } from \"./cron\";\n// `client.backups` answers these; the factory behind it is internal.\nexport type { BackupStream } from \"./backups\";\nexport type {\n ApiKeyKind,\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n CreateApiKeysOptions,\n CreatePersonalKeyRequest,\n CreatePersonalKeysOptions,\n UpdateApiKeyRequest\n} from \"./api-keys\";\nexport type { FunctionInvokeOptions, FunctionsClient } from \"./functions\";\n\n// Realtime: the WebSocket client class is internal to `createRebaseClient()`,\n// but re-exported (see @internal on the class) so a data-source driver can\n// construct it directly. Not a stable app-facing API.\nexport { RebaseWebSocketClient } from \"./websocket\";\nexport { RebaseRealtimeChannel } from \"./realtime-channel\";\nexport type {\n PresenceState,\n PresenceDiff,\n BroadcastEvent,\n ChannelTransport,\n ChannelOptions,\n ChannelHistoryEntry,\n ChannelHistoryResult\n} from \"./realtime-channel\";\n\n// Offline: config, the `client.offline` surface, and the metadata a UI needs\n// to reflect sync state. `isOfflineError` distinguishes \"there was no network\n// and nothing local to answer with\" from a request that genuinely failed.\n// The store contract is public so other environments (React Native/\n// AsyncStorage, Electron, …) can supply their own persistence;\n// `MemoryOfflineStore` is exported for tests and as the reference\n// implementation, while the IndexedDB store is wired automatically in the\n// browser and needs no direct construction.\nexport type { ConflictResolution, OfflineApi, OfflineConfig, OfflineQueueOptions, OfflineStatus } from \"./offline\";\nexport { isOfflineError } from \"./offline-errors\";\nexport type { LiveResult, ObserveByIdOptions, ObserveOptions, RowSnapshotMeta } from \"./collection\";\n// The reader for a fetched row's version. A function rather than a field so the\n// version never enters the generated `Row` type — see `etagOf`.\nexport { etagOf } from \"./collection\";\nexport type { BatchOperation, BatchRef, BatchResult, FieldOperation, UpdateValues, UpsertOptions } from \"@rebasepro/types\";\n// The types a call's own arguments and answers are written in. A browser app\n// installs `@rebasepro/client` alone, so a type it cannot import from here is a\n// type it cannot name: a dynamic filter UI holding a `WhereFilterOp`, a helper\n// taking `WriteOptions`, a wrapper returning an `AggregateResult`.\nexport type {\n WriteOptions,\n WhereFilterOp,\n WhereFor,\n WhereConditionFor,\n WhereValueFor,\n FilterValues,\n FieldPath,\n OrderBySpec,\n LogicalCondition,\n LogicalConditionFor,\n FilterCondition,\n FilterConditionFor,\n AggregateParams,\n AggregateSelect,\n AggregateRow,\n AggregateResult,\n AggregateMeta,\n IncludeSpec,\n IncludeOptions,\n QueryComputedFields,\n RowOf,\n InsertOf,\n UpdateOf,\n StorageSource\n} from \"@rebasepro/types\";\nexport type { OfflineStore, OfflineCacheEntry, OfflineCacheRecord, PendingMutation, MutationRollback } from \"./offline-store\";\nexport { MemoryOfflineStore } from \"./offline-store\";\n\nexport interface CreateRebaseClientOptions extends RebaseClientConfig {\n auth?: CreateAuthOptions;\n admin?: CreateAdminOptions;\n cron?: CreateCronOptions;\n apiKeys?: CreateApiKeysOptions;\n personalKeys?: CreatePersonalKeysOptions;\n /**\n * Declared storage sources for multi-backend support. Server-transport\n * entries are auto-wired into `client.storageRegistry`; `direct` sources\n * are registered app-side (e.g. via a Firebase Storage hook). The default\n * source (`storage`) is always registered under\n * {@link DEFAULT_STORAGE_SOURCE_KEY}.\n */\n storageSources?: StorageSourceDefinition[];\n /**\n * Maps each `client.data` accessor to the collection slug the server\n * serves (e.g. `{ companyMembers: \"company-members\" }`): the generated\n * `collectionsDictionary`.\n *\n * **Required for a typed client** (`createRebaseClient<Database>`). The\n * types offer every accessor, but without the map the proxy sends\n * `client.data.companyMembers` to `/data/company_members`, so a kebab-case\n * slug or a renamed accessor (`collectionCollection`) answers 404; a\n * one-time warning names the accessor. With the map, an unknown accessor\n * throws `UNKNOWN_COLLECTION` with a suggestion. A later version will make\n * it required in the types when `Database` is supplied.\n */\n collections?: Record<string, string>;\n /**\n * Local-first sync for the data layer.\n *\n * `true` enables it with defaults: reads populate a local row database and\n * fall back to it (evaluating filters and sorts locally) when the network\n * is gone, writes made offline apply immediately and replay in order when\n * it returns, and `observe()` becomes a live query that emits from the\n * local database first. A rejected write is rolled back. Pass an\n * {@link OfflineConfig} to control the store, cache sizes, retry backoff,\n * or rejection handling.\n *\n * Local rows and queued writes are partitioned per signed-in user, and\n * shared across tabs. Off by default.\n */\n offline?: boolean | OfflineConfig;\n /**\n * Log the realtime connection's progress — opened, signed in, closed,\n * redialling — with `console.debug`. Off by default, so a public page says\n * nothing about its backend in a visitor's console; warnings and errors\n * are written either way.\n *\n * Without a code change, one browser can turn it on from its console with\n * `localStorage.setItem(\"rebase_debug\", \"1\")` (remove the key to stop), and\n * any runtime with `globalThis.__REBASE_DEBUG__ = true`.\n */\n debug?: boolean;\n}\n\n// ─── Typed Data Proxy ────────────────────────────────────────────────────────\n// Adds typed collection accessors when `DB` is provided via the SDK generator.\n\ntype KebabToCamelCase<S extends string> =\n S extends `${infer T}-${infer U}`\n ? `${T}${Capitalize<KebabToCamelCase<U>>}`\n : S;\n\n/** The accessor whose generated entry declares `Slug: S`, or `never`. */\ntype AccessorOfSlug<DB, S extends string> = {\n [K in keyof DB]: DB[K] extends { Slug: S } ? K : never\n}[keyof DB];\n\n// Resolve a generated `Database` entry from a slug literal, or `unknown` when\n// the slug isn't in the schema — the extractors below then fall back to the\n// open row / partial shapes.\n//\n// Through the `Slug` each entry declares: the generator wrote it beside the\n// accessor it chose, so this reads one name rather than deriving the other. A\n// `Database` generated before entries carried `Slug` falls back to the old\n// kebab-only derivation, which is still right for kebab and single-word slugs.\ntype DBEntry<DB, S extends string> =\n [AccessorOfSlug<DB, S>] extends [never]\n ? (KebabToCamelCase<S> extends keyof DB ? DB[KebabToCamelCase<S>] : unknown)\n : DB[AccessorOfSlug<DB, S>];\n\ntype TypedDataLayer<DB> = {\n collection<S extends string>(slug: S): CollectionClient<\n RowOf<DBEntry<DB, S>>,\n InsertOf<DBEntry<DB, S>>,\n UpdateOf<DBEntry<DB, S>>,\n IncludeOf<DBEntry<DB, S>, DB>\n >;\n} & {\n [K in keyof DB]: CollectionClient<RowOf<DB[K]>, InsertOf<DB[K]>, UpdateOf<DB[K]>, IncludeOf<DB[K], DB>>;\n} & (\n // Untyped, every name is a collection: the index signature is the point.\n // Typed, it is the opposite — it was intersected in unconditionally, and\n // its `[slug: string]` made `client.data.nopeCollection` compile on a\n // `createRebaseClient<Database>`, to throw UNKNOWN_COLLECTION (or 404)\n // at runtime. Only the by-slug `collection<M>()` overload is kept.\n string extends keyof DB ? RebaseSdkData : Pick<RebaseSdkData, \"collection\">\n);\n\n/**\n * The return type of `createRebaseClient<DB>()`.\n *\n * This is `RebaseClient` (from `@rebasepro/types`) with all optional\n * capabilities populated and the `data` layer narrowed to provide\n * typed collection accessors when a `DB` schema generic is supplied.\n */\nexport type CreateRebaseClientResult<DB = Record<string, unknown>> = Omit<RebaseClient<DB>, \"data\" | \"email\" | \"simulate\"> & {\n /**\n * A client whose data requests run with simulated access — as a role, a\n * uid holding a role, or a service key — for an administrator checking\n * what row-level security lets that identity read. See\n * {@link RebaseClient.simulate} for what the server honours and refuses.\n *\n * Always present here, typed by the same `Database` (it is built with the\n * same `collections` dictionary), and closable: it is a client of its own,\n * so release it with `close()` when done. On `RebaseClient` it is optional\n * and untyped, because a server-side client has none.\n */\n simulate: (simulation: AccessSimulation) => CreateRebaseClientResult<DB>;\n /**\n * Send this token from now on, over HTTP and on the realtime socket. An\n * open socket is rebuilt to authenticate with it. A token getter, when one\n * is installed, still comes first.\n */\n setToken: (token: string | null) => void;\n /**\n * Ask `getter` for the token each time one is needed — every HTTP request\n * and every time the realtime socket authenticates. This is how a host\n * that signs users in with its own provider (Clerk, Firebase Auth, a\n * session of its own) authenticates the client:\n *\n * ```ts\n * client.setAuthTokenGetter(() => clerk.session?.getToken() ?? Promise.resolve(null));\n * ```\n *\n * It comes before the client's own session and the `token` option, which\n * are used when it answers `null` or throws. An open socket is rebuilt to\n * authenticate through it.\n */\n setAuthTokenGetter: (getter: () => Promise<string | null>) => void;\n /**\n * Answer a refused credential — a 401 over HTTP, an auth error on the\n * socket — with `handler`; resolve `true` to have the request or\n * subscription retried with the token resolved again. Replaces the\n * client's own session refresh.\n */\n setOnUnauthorized: (handler: () => Promise<boolean>) => void;\n /** The token the next request would carry, resolved as it would be. */\n resolveToken: () => Promise<string | null>;\n auth: ReturnType<typeof createAuth>;\n admin: ReturnType<typeof createAdmin>;\n cron: ReturnType<typeof createCron>;\n backups: ReturnType<typeof createBackups>;\n apiKeys: ReturnType<typeof createApiKeys>;\n personalKeys: ReturnType<typeof createPersonalKeys>;\n functions: ReturnType<typeof createFunctionsClient>;\n ws?: RebaseWebSocketClient;\n /**\n * Broadcast and presence channels.\n *\n * Was missing from this type while present on the returned object, which\n * made `client.realtime.channel(...)` a type error and forced every adopter\n * to cast around the feature before they could reach it.\n */\n realtime: {\n /**\n * Join a broadcast/presence channel. Repeated calls with the same name\n * return the same channel object. Throws only when the client was\n * created with `realtime: false`.\n *\n * Pass `{ history: true }` to have the channel replay what it missed on\n * join and on every reconnect, for channels the server retains.\n */\n channel: (name: string, options?: ChannelOptions) => RebaseRealtimeChannel;\n };\n /**\n * Release everything this client holds that can keep a process alive: the\n * realtime socket and its reconnect timer, channel presence heartbeats, the\n * offline manager, and the scheduled token refresh.\n *\n * Each of those keeps the Node event loop alive on its own, so a script\n * that does not call this will not exit — and, until the refresh timer was\n * included, one that *did* call it still would not if it had signed in.\n *\n * Safe when realtime was never started (`realtime: false`), safe when\n * signed out, and safe to call twice. It does not sign the user out: a\n * persisted session survives for the next client to restore.\n */\n close: () => void;\n storage: StorageSource;\n storageRegistry: StorageSourceRegistry;\n createStorageSource: (storageId: string) => StorageSource;\n fetchStorageSources: () => Promise<StorageSourceDefinition[]>;\n /**\n * POST to an arbitrary endpoint on this backend, returning its body\n * verbatim.\n *\n * The shorthand for {@link FunctionsClient.invoke} — `call(\"functions/x\",\n * p)` is `functions.invoke(\"x\", p)` — and it unwraps exactly as little.\n * Neither reaches into the response for a `data` key.\n */\n call: <T = unknown>(endpoint: string, payload?: unknown) => Promise<T>;\n /**\n * A collection by slug — the same client `data.collection(slug)` returns,\n * typed from the generated `Database` when the slug is one of its\n * collections, or by the row type passed explicitly.\n */\n collection: {\n <S extends string>(slug: S): CollectionClient<\n RowOf<DBEntry<DB, S>>,\n InsertOf<DBEntry<DB, S>>,\n UpdateOf<DBEntry<DB, S>>,\n IncludeOf<DBEntry<DB, S>, DB>\n >;\n <M extends Record<string, unknown> = Record<string, unknown>>(slug: string): CollectionClient<M>;\n };\n /**\n * Write across collections in one transaction.\n *\n * `createMany` and friends are one collection at a time, which is the wrong\n * shape for the writes that most need to be atomic: an order and its line\n * items, a user and their membership row, a document and its audit entry.\n * Sent as separate calls those can half-succeed, and the recovery — read\n * back, work out which half landed, undo it — is code nobody writes.\n *\n * Operations run in order, each through the pipeline its single-row\n * equivalent uses: the same validation, callbacks and row-level security,\n * as the same user. All of them land or none do.\n *\n * Name an operation with `ref` and a later one can stand\n * `{ $ref: \"<name>.<field>\" }` wherever a value goes — which is the only\n * way to express \"insert the parent, then point the children at it\" in a\n * single request, since the parent's id does not exist until the server\n * assigns it. Only backward references resolve.\n *\n * `data` is aligned to `operations`: the written row for a create, upsert\n * or update, and `null` for a delete.\n *\n * `collection` is the accessor `client.data.<accessor>` takes — `orderItems`\n * for the slug `order_items` — and is sent as the slug through the\n * `collections` dictionary. A name that is not an accessor goes out as\n * written, so an untyped client can still pass the slug.\n *\n * @example\n * ```ts\n * await client.batch([\n * { op: \"create\", collection: \"orders\", values: { total: 40 }, ref: \"order\" },\n * { op: \"create\", collection: \"orderItems\",\n * values: { orderId: { $ref: \"order.id\" }, sku: \"A-1\" } },\n * { op: \"update\", collection: \"stock\", id: \"A-1\", values: { count: { $inc: -1 } } }\n * ]);\n * ```\n */\n batch: (\n operations: BatchOperation<DB>[],\n options?: WriteOptions\n ) => Promise<BatchResult>;\n data: TypedDataLayer<DB>;\n /** Present only when the client was created with `offline` enabled. */\n offline?: OfflineApi;\n};\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/**\n * Derive a WebSocket URL from an HTTP base URL.\n * `http://` → `ws://`, `https://` → `wss://`.\n *\n * A backend mounted under a path is the reason `baseUrl` accepts one, so the\n * path is kept. It used to be kept for an absolute `baseUrl` and dropped for a\n * relative one — resolved through `.origin` — so one deployment dialled two\n * different sockets depending on whether its config said `\"/backend\"` or\n * `\"https://app.example.com/backend\"`.\n *\n * Returns `\"\"` when there is nothing to resolve against: a relative `baseUrl`\n * outside a browser has no origin, and inventing one would dial somewhere\n * arbitrary. The caller warns rather than leaving that silent.\n */\nfunction deriveWebSocketUrl(baseUrl?: string): string {\n const toWsProtocol = (url: string): string => {\n const secure = /^(https|wss):/i.test(url);\n return url\n .replace(/^https?:\\/\\//i, secure ? \"wss://\" : \"ws://\")\n .replace(/^wss?:\\/\\//i, secure ? \"wss://\" : \"ws://\")\n .replace(/\\/$/, \"\");\n };\n\n if (typeof window !== \"undefined\") {\n let absoluteUrl: string;\n if (!baseUrl) {\n absoluteUrl = window.location.origin;\n } else if (/^https?:\\/\\//i.test(baseUrl) || /^wss?:\\/\\//i.test(baseUrl)) {\n absoluteUrl = baseUrl;\n } else {\n try {\n const resolved = new URL(baseUrl, window.location.href);\n absoluteUrl = resolved.origin + resolved.pathname;\n } catch {\n absoluteUrl = window.location.origin;\n }\n }\n return toWsProtocol(absoluteUrl);\n }\n\n if (!baseUrl) return \"\";\n if (!/^https?:\\/\\//i.test(baseUrl) && !/^wss?:\\/\\//i.test(baseUrl)) {\n return \"\";\n }\n return toWsProtocol(baseUrl);\n}\n\nexport function createRebaseClient<DB = Record<string, unknown>>(options: CreateRebaseClientOptions): CreateRebaseClientResult<DB> {\n // `credentialOutOfBand`: in cookie auth mode the credential is an httpOnly\n // cookie, so a tokenless transport is not an anonymous client and must not\n // trip the server-side anonymous guard (see `RebaseClientConfig.anonymous`).\n const transport = createTransport(options, { credentialOutOfBand: options.auth?.authFlowMode === \"cookie\" });\n const auth = createAuth(transport, options.auth);\n const admin = createAdmin(transport, options.admin);\n const cron = createCron(transport, options.cron);\n const backups = createBackups(transport);\n const apiKeys = createApiKeys(transport, options.apiKeys);\n const personalKeys = createPersonalKeys(transport, options.personalKeys);\n const storage = createStorage(transport);\n const functions = createFunctionsClient(transport);\n\n // Build a server-backed StorageSource for a given storage-source key.\n const createStorageSource = (storageId: string): StorageSource =>\n storageId === DEFAULT_STORAGE_SOURCE_KEY ? storage : createStorage(transport, storageId);\n\n // Storage registry: always holds the default source, plus any declared\n // server-transport sources. `direct` sources are registered app-side.\n const storageRegistry = new ClientStorageSourceRegistry();\n storageRegistry.register(DEFAULT_STORAGE_SOURCE_KEY, storage);\n for (const def of options.storageSources ?? []) {\n if (def.transport === \"server\" && def.key !== DEFAULT_STORAGE_SOURCE_KEY) {\n storageRegistry.register(def.key, createStorageSource(def.key));\n }\n }\n\n // Discover storage sources from the backend, making the server the single\n // source of truth. Server-transport sources are auto-wired into the\n // registry; `direct` sources are returned for the app to register. The\n // promise is cached on success and reset on failure so it can be retried\n // (e.g. once the user authenticates).\n let storageSourcesPromise: Promise<StorageSourceDefinition[]> | undefined;\n const fetchStorageSources = (): Promise<StorageSourceDefinition[]> => {\n if (storageSourcesPromise) return storageSourcesPromise;\n storageSourcesPromise = transport\n .request<{ data: StorageSourceDefinition[] }>(\"/storage/sources\")\n .then((res) => {\n const defs = res.data ?? [];\n for (const def of defs) {\n if (def.transport === \"server\"\n && def.key !== DEFAULT_STORAGE_SOURCE_KEY\n && !storageRegistry.has(def.key)) {\n storageRegistry.register(def.key, createStorageSource(def.key));\n }\n }\n return defs;\n })\n .catch((e) => {\n storageSourcesPromise = undefined; // allow retry\n throw e;\n });\n return storageSourcesPromise;\n };\n\n // Opting out has to happen before the URL is derived: `deriveWebSocketUrl`\n // always produces one, so a truthy check alone can never leave the socket\n // closed.\n const realtimeEnabled = options.realtime !== false;\n const resolvedWsUrl = realtimeEnabled\n ? (options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl))\n : undefined;\n\n // Realtime is on unless it was switched off, so \"on, but no URL could be\n // derived\" is a misconfiguration and not a choice. It used to be silent:\n // the client simply had no socket, `observe()` quietly degraded to a\n // one-shot fetch, and `realtime.channel()` blamed `realtime: false` — an\n // option the caller had not passed.\n const realtimeUnreachable = realtimeEnabled && !resolvedWsUrl;\n const unreachableReason =\n \"no WebSocket URL could be derived from baseUrl \" +\n `${JSON.stringify(options.baseUrl ?? null)} — outside a browser there is no page origin ` +\n \"to resolve a relative URL against. Pass an absolute `baseUrl`, set `websocketUrl` \" +\n \"explicitly, or pass `realtime: false` to say this was intended.\";\n if (realtimeUnreachable) {\n console.warn(\n `[Rebase] Realtime is enabled but ${unreachableReason} ` +\n \"Live queries will fall back to a single fetch and channels will throw.\"\n );\n }\n\n /**\n * What a refused credential is answered with, on HTTP and on the socket:\n * the handler passed as `onUnauthorized` or set with `setOnUnauthorized()`,\n * otherwise the auth module's `handleUnauthorized` — not a bare\n * `refreshSession`, so that a refresh the server rejects outright drops the\n * session and emits SIGNED_OUT instead of leaving every view rendering\n * \"Invalid or expired token\". One variable so that setting it reaches\n * both: a host whose own auth provider can mint a fresh token says so once.\n */\n let onUnauthorized: () => Promise<boolean> = options.onUnauthorized ?? (() => auth.handleUnauthorized());\n transport.setOnUnauthorized(() => onUnauthorized());\n\n let ws: RebaseWebSocketClient | undefined;\n /** One channel object per name — see `realtime.channel`. */\n const realtimeChannels = new Map<string, RebaseRealtimeChannel>();\n if (resolvedWsUrl) {\n ws = new RebaseWebSocketClient({\n websocketUrl: resolvedWsUrl,\n // The socket's token is the one an HTTP request would carry now,\n // resolved by the same function: a token getter installed with\n // `setAuthTokenGetter()`, otherwise the token held — the session's,\n // refreshed first when it is about to expire, one set with\n // `setToken()`, or the `token` option. Resolving it separately is\n // how a host signing users in with its own provider got an\n // authenticated HTTP client and an anonymous socket.\n getAuthToken: () => transport.resolveToken(),\n onUnauthorized: () => onUnauthorized(),\n impersonate: options.impersonate,\n debug: options.debug\n });\n\n // The account the socket's server-side state belongs to. The server\n // re-scopes a socket's subscriptions and channel memberships when it\n // authenticates again (`rescopeClient`), but a change of account still\n // rebuilds the socket here: a fresh socket carries nothing the previous\n // account's socket held, so this does not depend on the server alone.\n let realtimeUid: string | null = auth.getSession()?.user?.uid ?? null;\n\n auth.onAuthStateChange((event, session) => {\n if (!ws) return;\n if (event === \"SIGNED_OUT\") {\n realtimeUid = null;\n // Not permanent: the client stays usable, and a later subscribe\n // should reconnect anonymously. It also drops what the socket\n // cached, so none of it is replayed to whoever comes next.\n ws.disconnect();\n } else if (event === \"SIGNED_IN\" || event === \"TOKEN_REFRESHED\") {\n const uid = session?.user?.uid ?? null;\n if (uid !== realtimeUid) {\n // A different account — signed in over a session (which\n // emits no SIGNED_OUT), or a visitor signing in. Rebuild\n // the socket rather than re-authenticate it: a new socket\n // starts with nothing on the server, authenticates as this\n // account on open, and re-sends every live subscription and\n // channel as it. Only when there was a socket, for the same\n // reason as below.\n realtimeUid = uid;\n const hadSocket = ws.hasSocket;\n ws.disconnect();\n if (hadSocket) ws.ensureConnected();\n return;\n }\n // Only re-authenticate a socket that already exists. Signing in\n // is not a request for realtime, and dialling here would undo\n // lazy connect for every app with a login. A socket opened\n // later authenticates itself from `getAuthToken` on open.\n //\n // With the token HTTP would now send: the session's, unless a\n // token getter is installed, which HTTP asks first.\n if (session?.accessToken && ws.hasSocket) {\n const socket = ws;\n transport.resolveToken()\n .then((token) => (token && socket.hasSocket ? socket.authenticate(token) : undefined))\n .catch(console.warn);\n }\n }\n });\n }\n\n /**\n * Suggest the closest known collection key for a mistyped accessor.\n * Uses edit-distance-1 and prefix matching — no external dependency.\n */\n function suggestCollection(prop: string, knownKeys: string[]): string | undefined {\n // Prefix match (e.g. \"prod\" → \"products\")\n const prefixMatch = knownKeys.find(k => k.startsWith(prop) || prop.startsWith(k));\n if (prefixMatch) return prefixMatch;\n\n // Edit-distance-1: deletions, insertions, substitutions, transpositions\n for (const key of knownKeys) {\n if (Math.abs(key.length - prop.length) > 1) continue;\n let diffs = 0;\n const longer = key.length >= prop.length ? key : prop;\n const shorter = key.length >= prop.length ? prop : key;\n if (longer.length === shorter.length) {\n // Same length: allow 1 substitution or 1 transposition\n for (let i = 0; i < longer.length; i++) {\n if (longer[i] !== shorter[i]) {\n // Check for transposition\n if (\n i + 1 < longer.length &&\n longer[i] === shorter[i + 1] &&\n longer[i + 1] === shorter[i]\n ) {\n diffs++;\n i++; // skip next char (already accounted for)\n if (diffs > 1) break;\n continue;\n }\n diffs++;\n }\n if (diffs > 1) break;\n }\n } else {\n // Length differs by 1: allow 1 insertion/deletion\n let li = 0;\n let si = 0;\n while (li < longer.length) {\n if (si < shorter.length && longer[li] === shorter[si]) {\n si++;\n } else {\n diffs++;\n }\n li++;\n if (diffs > 1) break;\n }\n }\n if (diffs <= 1) return key;\n }\n\n return undefined;\n }\n\n // Offline layer: wraps every collection client with a read cache and a\n // write queue. Replay goes through *unwrapped* clients (the factory below)\n // so a failing replay can never re-queue itself.\n const offlineManager = options.offline\n ? new LazyOfflineManager(\n typeof options.offline === \"object\" ? options.offline : {},\n (slug) => createCollectionClient(transport, slug)\n )\n : undefined;\n\n if (offlineManager) {\n // Cache and queue are partitioned per user: cached rows are RLS-scoped\n // to whoever fetched them, and queued writes must replay as the user\n // who made them — a shared browser must never mix the two.\n //\n // Who that is, is not known until auth has restored the session or\n // found there is none. In cookie mode nothing is stored on the device,\n // so that takes the refresh request made on load; an expired stored\n // token takes one too, and may turn out to be refused. Until then the\n // manager has no scope, and everything offline waits. Reading the\n // missing session as \"signed out\" would queue the user's own writes\n // as the signed-out user's, which no sign-in replays; reading the\n // stored one as the answer would keep a refused session's user in\n // scope for a client that is signed out.\n offlineManager.holdScope();\n auth.onAuthStateChange((event, session) => {\n offlineManager.setScope(event === \"SIGNED_OUT\" ? undefined : session?.user?.uid);\n });\n void auth.isInitialized().then(() => {\n const uid = auth.getSession()?.user?.uid;\n // No session, and only because the server could not be asked:\n // with `keepLastUser`, the last user's data rather than the\n // signed-out user's. Never when the server answered.\n if (!uid && auth.restoreOutcome() === \"unreachable\" && offlineManager.keepsLastUser) {\n void offlineManager.resumeLastUser();\n return;\n }\n offlineManager.setScope(uid);\n });\n if (offlineManager.keepsLastUser) {\n // Once the connection is back, ask again — the restore on load\n // could not. Success announces the session, which confirms the\n // user (or switches to whoever it really is); a refusal means\n // there is no session, and the device is signed out.\n let confirming = false;\n const confirm = () => {\n if (confirming || auth.getSession()) return;\n confirming = true;\n auth.refreshSession().catch((error: unknown) => {\n const refused = error instanceof RebaseApiError\n && error.status !== undefined && error.status >= 400 && error.status < 500;\n if (refused && !auth.getSession()) offlineManager.setScope(undefined);\n }).finally(() => {\n confirming = false;\n });\n };\n offlineManager.api.onStatusChange((status) => {\n if (status.online && status.unverifiedUser) confirm();\n });\n // A read made while unconfirmed reached the server — the\n // connection is back even when the status never said it was gone.\n offlineManager.onReachedUnconfirmed(confirm);\n }\n }\n\n /**\n * `setToken()` or `setAuthTokenGetter()` changed the credential HTTP\n * sends, and a socket may be open as whoever the previous one named.\n *\n * Rebuilt rather than re-authenticated, as on a change of account: a raw\n * token does not say whose it is, and a new socket carries nothing the\n * previous identity held — on the server or in this client's cache. It\n * authenticates on open with the token HTTP now sends and re-sends every\n * live subscription and channel as it. With no socket there is nothing to\n * do: one opened later authenticates itself.\n */\n function credentialChanged(): void {\n if (!ws?.hasSocket) return;\n ws.disconnect();\n ws.ensureConnected();\n }\n\n const collectionClients = new Map<string, CollectionClient<Record<string, unknown>>>();\n let untypedWarned = false;\n\n function collection(slug: string): CollectionClient<Record<string, unknown>> {\n if (!collectionClients.has(slug)) {\n const inner = createCollectionClient(transport, slug, ws);\n collectionClients.set(slug, offlineManager ? offlineManager.wrap(slug, inner) : inner);\n }\n return collectionClients.get(slug)!;\n }\n\n const dataTarget = { collection } as Record<string, unknown>;\n\n /**\n * Names the proxy never serves as a collection: the ones promise and\n * serialisation machinery probe for (`then`, `toJSON`, React's\n * `$$typeof`), and everything a plain object inherits — so\n * `client.data.toString` is `Object.prototype.toString`, not a collection\n * client, and `String(client.data)` is `\"[object Object]\"` rather than a\n * throw. `prop in collections` used to walk the prototype too, which is how\n * `toString` and `constructor` became \"collections\" under a dictionary.\n */\n const isProbedName = (prop: string) => prop === \"then\" || prop === \"toJSON\" || prop === \"$$typeof\";\n const isReservedName = (prop: string) => isProbedName(prop) || prop in Object.prototype;\n\n const dataProxy = new Proxy(dataTarget, {\n get(_target, prop: string | symbol) {\n if (prop === \"collection\") {\n return collection;\n }\n if (typeof prop === \"symbol\") return undefined;\n if (isProbedName(prop)) return undefined;\n if (options.collections && Object.hasOwn(options.collections, prop)) {\n return collection(options.collections[prop]);\n }\n if (prop in Object.prototype) return Reflect.get(_target, prop);\n if (options.collections) {\n // Strict mode: the developer supplied a typed dictionary,\n // so we know the full set of valid accessors.\n const knownKeys = Object.keys(options.collections);\n const suggestion = suggestCollection(prop, knownKeys);\n const knownList = knownKeys.join(\", \");\n let msg = `Unknown collection accessor \"${prop}\". Known collections: ${knownList}.`;\n if (suggestion) msg += ` Did you mean \"${suggestion}\"?`;\n msg += ` Use data.collection(\"<slug>\") for dynamic slugs.`;\n throw new RebaseClientError(msg, {\n code: \"UNKNOWN_COLLECTION\",\n details: { accessor: prop, known: knownKeys }\n });\n }\n // Untyped fallback: convert camelCase property names to snake_case slugs.\n // e.g. `companyMembers` → `company_members`\n const slug = toSnakeCase(prop);\n if (!untypedWarned) {\n untypedWarned = true;\n // Once per client, and it names everything needed to act:\n // which accessor, where it went, and the two fixes. A typed\n // client (`createRebaseClient<Database>`) without the\n // dictionary compiles every accessor and 404s on any slug that\n // is not the accessor in snake_case.\n console.warn(\n `[Rebase] Untyped data access detected: client.data.${prop} was sent to /data/${slug}, ` +\n `its name in snake_case, because this client has no \\`collections\\` dictionary. ` +\n `A collection whose slug is not that (a kebab-case slug, a renamed accessor) answers 404. ` +\n `With generated types, pass \\`collections: collectionsDictionary\\` (from generated/sdk/database.types) ` +\n `to createRebaseClient(); a typed client needs it. Otherwise use client.data.collection(\"<slug>\").`\n );\n }\n return collection(slug);\n },\n // `\"posts\" in client.data` answers what `get` serves: with a\n // dictionary, its own accessors; without one, any name that is not\n // reserved, since every such name reaches a collection by its slug.\n has(_target, prop: string | symbol) {\n if (prop === \"collection\") return true;\n if (typeof prop === \"symbol\") return false;\n if (isProbedName(prop)) return false;\n if (options.collections) return Object.hasOwn(options.collections, prop);\n return !isReservedName(prop);\n }\n });\n\n const target = {\n auth,\n admin,\n cron,\n backups,\n apiKeys,\n personalKeys,\n functions,\n storage,\n storageRegistry,\n createStorageSource,\n fetchStorageSources,\n ws,\n realtime: {\n /**\n * Join a broadcast/presence channel.\n *\n * Repeated calls with the same name return the same channel, so\n * separate components can attach handlers without each opening its\n * own membership — and `leave()` from one would otherwise silently\n * cut off the others.\n */\n channel: (name: string, options?: ChannelOptions): RebaseRealtimeChannel => {\n // Being merely *unconnected* is not an error: the socket opens\n // on the first channel operation, which is the whole point of\n // asking for a channel before you use one. Having no socket at\n // all is, and there are two reasons for it — say which.\n if (!ws) {\n throw new RebaseClientError(\n realtimeUnreachable\n ? `Realtime is enabled but ${unreachableReason}`\n : \"Realtime is disabled on this client (realtime: false), so channels are unavailable.\",\n { code: \"REALTIME_DISABLED\" }\n );\n }\n let existing = realtimeChannels.get(name);\n if (!existing) {\n existing = new RebaseRealtimeChannel(name, ws, options);\n realtimeChannels.set(name, existing);\n } else if (options?.history) {\n // Same object by name, so options on a later call have no\n // new channel to apply to. Asking for history upgrades the\n // one that exists rather than being quietly ignored — but\n // never the reverse, so a caller that omits the option\n // cannot switch it off under one that asked for it.\n existing.enableHistory();\n }\n return existing;\n }\n },\n /**\n * Release every handle that can keep a process alive — see the\n * `close` docblock on the client interface.\n *\n * Safe to call when realtime was never started, safe when signed out,\n * and safe to call twice.\n */\n close: () => {\n // Channels hold presence heartbeat timers, which would otherwise\n // keep firing (and keep a Node process alive) after the socket\n // they publish over is gone.\n for (const channel of realtimeChannels.values()) void channel.leave();\n realtimeChannels.clear();\n // Permanent: nothing queued afterwards may redial and keep the\n // event loop alive, which is the reason this method exists.\n ws?.disconnect(true);\n // The offline retry timer is unref'd but the `online` listener is\n // not, and neither should outlive the client.\n offlineManager?.dispose();\n // The scheduled token refresh is a plain setTimeout up to a token\n // lifetime away, and not unref'd — so on Node it holds the event\n // loop open all by itself. Without this, closing a SIGNED-IN client\n // released the socket and the process still never exited, which is\n // the opposite of what this method exists to guarantee.\n auth.stopAutoRefresh();\n },\n setToken: (token: string | null) => {\n transport.setToken(token);\n credentialChanged();\n },\n setAuthTokenGetter: (getter: () => Promise<string | null>) => {\n transport.setAuthTokenGetter(getter);\n credentialChanged();\n },\n setOnUnauthorized: (handler: () => Promise<boolean>) => {\n onUnauthorized = handler;\n },\n resolveToken: transport.resolveToken,\n simulate: (simulation: AccessSimulation): CreateRebaseClientResult<DB> => {\n // A data client and nothing more: no socket (the server honours\n // the headers over HTTP only), no offline store (simulated rows\n // must never land in the administrator's cache), no session of its\n // own — the token is this client's, asked for on every request, so\n // this client's session is the one that refreshes.\n const derived = createRebaseClient<DB>({\n baseUrl: options.baseUrl,\n apiPath: options.apiPath,\n fetch: options.fetch,\n collections: options.collections,\n schemaVersion: options.schemaVersion,\n headers: { ...(options.headers ?? {}), ...simulationHeaders(simulation) },\n realtime: false,\n auth: { storage: createMemoryStorage(), persistSession: false, autoRefresh: false }\n });\n derived.setAuthTokenGetter(() => transport.resolveToken());\n derived.setOnUnauthorized(() => onUnauthorized());\n return derived;\n },\n baseUrl: transport.baseUrl,\n apiPath: transport.apiPath,\n collection,\n call: async <T = unknown>(endpoint: string, payload?: unknown): Promise<T> => {\n const prefix = endpoint.startsWith(\"/\") ? \"\" : \"/\";\n // The body, verbatim — the same thing `functions.invoke()` returns.\n //\n // This used to end `return res.data ?? (res as T)`, unwrapping a\n // top-level `data` key when it saw one. The docs present `call()`\n // as a shorthand for `invoke()`, which does not, so the two\n // returned *different values* for the one function that happens to\n // answer `{ data: … }` — and silently, since the unwrap only fires\n // on the shape it fires on. A caller who wants `.data` can write\n // it; a caller whose function legitimately returns a `data` field\n // could not get it back at all.\n //\n // For the same reason the body goes out whenever there is one, as\n // `invoke()` sends it: `false`, `0`, `\"\"` and `null` are all bodies\n // a function can switch on, and a truthiness test sent none.\n return transport.request<T>(`${prefix}${endpoint}`, {\n method: \"POST\",\n body: payload !== undefined ? JSON.stringify(payload) : undefined\n });\n },\n batch: async (operations: unknown[], batchOptions?: WriteOptions): Promise<BatchResult> => {\n if (!Array.isArray(operations)) {\n throw invalidBulkBody(\"batch expects an array of operations.\");\n }\n // Answered locally rather than sent: an empty transaction is a\n // round trip that cannot do anything, and the server would have to\n // answer this same shape anyway.\n if (operations.length === 0) return { data: [], meta: { operations: 0 } };\n\n const headers: Record<string, string> = {};\n if (batchOptions?.idempotencyKey) headers[\"Idempotency-Key\"] = batchOptions.idempotencyKey;\n if (batchOptions?.returning === false) headers.Prefer = \"return=minimal\";\n\n // `collection` is typed as the generated accessor (`orderItems`),\n // the name `client.data.<accessor>` takes; the route resolves it by\n // slug (`order_items`). The dictionary is the one place that knows\n // both, so it is resolved here — and a name that is not an accessor\n // goes out as written, so a slug still works.\n const dictionary = options.collections;\n const wire = dictionary\n ? operations.map((operation) => {\n if (!operation || typeof operation !== \"object\") return operation;\n const name = (operation as { collection?: unknown }).collection;\n return typeof name === \"string\" && Object.hasOwn(dictionary, name)\n ? { ...operation, collection: dictionary[name] }\n : operation;\n })\n : operations;\n\n return transport.request<BatchResult>(\"/data/_batch\", {\n method: \"POST\",\n body: JSON.stringify({ operations: wire }),\n ...(Object.keys(headers).length > 0 ? { headers } : {})\n });\n },\n data: dataProxy,\n ...(offlineManager ? { offline: offlineManager.api } : {}),\n } as unknown as CreateRebaseClientResult<DB>;\n\n return target;\n}\n\n"],"mappings":"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"}