@rebasepro/client 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","names":[],"sources":["../src/reviver.ts","../src/transport.ts","../src/auth.ts","../src/admin.ts","../src/cron.ts","../src/backups.ts","../src/api-keys.ts","../src/sdk_query_builder.ts","../src/collection.ts","../src/functions.ts","../src/storage.ts","../src/storage-registry.ts","../src/websocket.ts","../src/realtime-channel.ts","../src/offline-codec.ts","../src/offline-connectivity.ts","../src/offline-store.ts","../src/offline-query.ts","../src/offline.ts","../src/index.ts"],"sourcesContent":["import { EntityReference, EntityRelation, GeoPoint, Vector } from \"@rebasepro/types\";\n\nexport function rebaseReviver(_key: string, value: unknown): unknown {\n if (value && typeof value === \"object\" && \"__type\" in value) {\n const record = value as Record<string, unknown>;\n switch (record.__type) {\n case \"date\":\n case \"Date\": {\n if (typeof record.value !== \"string\") {\n return value;\n }\n const date = new Date(record.value);\n return isNaN(date.getTime()) ? null : date;\n }\n case \"reference\":\n case \"EntityReference\":\n return new EntityReference({\n id: String(record.id),\n path: record.path as string,\n driver: record.driver as string | undefined,\n databaseId: record.databaseId as string | undefined\n });\n case \"relation\":\n case \"EntityRelation\":\n return new EntityRelation(\n record.id as string | number,\n record.path as string,\n record.data as Record<string, unknown> | undefined\n );\n case \"GeoPoint\":\n return new GeoPoint(record.latitude as number, record.longitude as number);\n case \"Vector\":\n return new Vector(record.value as number[]);\n default:\n return value;\n }\n }\n return value;\n}\n","import { FindParams as TypesFindParams, FindResponse as TypesFindResponse, RebaseApiError } from \"@rebasepro/types\";\nimport { serializeFilter, serializeLogicalCondition, serializeOrderBy } from \"@rebasepro/common\";\nimport { rebaseReviver } from \"./reviver\";\n\n// The canonical client error now lives in `@rebasepro/types` so every package\n// (client, auth, …) throws one type. Re-exported here to preserve the historical\n// `import { RebaseApiError } from \".../transport\"` path used across the SDK.\nexport { RebaseApiError } from \"@rebasepro/types\";\nexport type { RebaseErrorInit } from \"@rebasepro/types\";\nimport { RebaseClientError } from \"@rebasepro/types\";\n\nexport interface RebaseClientConfig {\n /**\n * Origin of the Rebase server — scheme, host and port **only**.\n *\n * {@link apiPath} is appended to this, so do not include it here:\n * `\"http://localhost:3001\"` is correct, while `\"http://localhost:3001/api\"`\n * silently builds `/api/api/…` and every request 404s. Omit entirely for\n * same-origin requests from the browser.\n */\n baseUrl?: string;\n /**\n * Bearer token sent as `Authorization` on every request.\n *\n * In the browser this is the signed-in user's access token, so row-level\n * security applies. Server-side callers — scripts, cron jobs, ETL — pass the\n * service key instead, which resolves to `{ uid: \"service\", roles: [\"admin\"] }`\n * and **bypasses RLS**: there is no user to constrain those queries, so scope\n * them explicitly.\n */\n token?: string;\n /**\n * Path the API is mounted under, appended to {@link baseUrl}.\n * Defaults to `\"/api\"`; override only if the server mounts it elsewhere.\n */\n apiPath?: string;\n /**\n * Origin to use instead of {@link baseUrl} for URLs that are handed to the\n * browser to fetch on its own — storage file downloads and previews.\n *\n * API *requests* always go to `baseUrl`; this only changes URLs the SDK\n * *returns* (e.g. `storage.getSignedUrl`). It exists for proxied setups:\n * when `baseUrl` routes through an authenticated middleman (the Rebase\n * console's Studio proxy), a plain `<img src>` or a copied link cannot\n * satisfy the middleman's auth — but the file route itself is reachable\n * directly at the origin server and secured by its own scoped `?token=`.\n * Set this to that server's public origin (no path; {@link apiPath} is\n * appended) and returned file URLs point straight at it.\n */\n storageUrlOrigin?: string;\n fetch?: typeof globalThis.fetch;\n onUnauthorized?: () => Promise<boolean>;\n websocketUrl?: string; // Optional real-time WebSocket connection\n /**\n * Open the realtime WebSocket. **Defaults to `true`.**\n *\n * The socket connects as soon as the client is constructed and keeps the\n * Node event loop alive, so a one-shot script (CLI, cron job, ETL) will not\n * exit on its own. Set this to `false` for any process that reads or writes\n * and then terminates — `.listen()` and `.listenById()` then throw instead\n * of silently doing nothing.\n *\n * Long-lived processes that do want realtime can instead call\n * `client.close()` when shutting down.\n */\n realtime?: boolean;\n /**\n * \"Yes, I meant to be anonymous.\"\n *\n * Off-browser, a client with no credential can only ever call as an\n * anonymous user, and row-level security answers it with whatever is\n * public — usually nothing. That is almost always a mistake in a script or\n * cron job, so the SDK warns once on the first request (see\n * {@link ANONYMOUS_SERVER_CLIENT_WARNING}). Anonymous is a legitimate\n * choice for public reads, though; set this to `true` to say so and\n * silence the warning.\n *\n * Has no effect in the browser, where anonymous-before-sign-in is normal\n * and nothing is ever warned about.\n */\n anonymous?: boolean;\n}\n\n/**\n * Facts about the surrounding client that the transport cannot read off its own\n * config, but needs in order to decide whether a request is *meaningfully*\n * credential-less.\n */\nexport interface TransportEnvironment {\n /**\n * The credential reaches the server without an `Authorization` header —\n * i.e. `auth.authFlowMode: \"cookie\"`, where the refresh token lives in an\n * httpOnly cookie. Such a client looks tokenless to the transport but is\n * not anonymous, so it must never trip the guard.\n */\n credentialOutOfBand?: boolean;\n}\n\n/**\n * True when there is no browser to have signed a user in — a Node script, a\n * cron job, an edge worker.\n *\n * Anonymous is an ordinary, correct state in a browser: before sign-in, on a\n * marketing page, for public reads. Warning there would be noise that teaches\n * people to ignore warnings, so the guard is off entirely. This uses the same\n * `typeof window` test as {@link resolveBaseUrl}, and additionally treats a\n * defined `document` as a browser so an SSR shim or test harness that installs\n * only one of the two is still excluded.\n */\nfunction isServerLikeEnvironment(): boolean {\n return typeof window === \"undefined\" && typeof document === \"undefined\";\n}\n\n/**\n * Emitted once per client. Kept as a constant so the wording is testable and\n * greppable — this is the string a user will paste into a search.\n */\nexport const ANONYMOUS_SERVER_CLIENT_WARNING =\n \"[rebase] This client was created outside a browser with no credential — no `token`, no auth token getter, \"\n + \"and no cookie auth flow — so every request runs as an anonymous caller. Row-level security will return only \"\n + \"publicly readable rows, which is usually nothing and occasionally the wrong thing. \"\n + \"Inside a cron or function handler, use the `client` you were handed instead of building a new one: its data \"\n + \"plane is already admin-scoped. In a standalone script or job, pass the service key as `token`. \"\n + \"If you really do want anonymous access, pass `anonymous: true` to silence this.\";\n\n/**\n * Re-export from `@rebasepro/types` for backward compatibility.\n *\n * Forwards the row type: without the parameter this alias flattened\n * `FindParams<M>` back to its `Record<string, unknown>` default, and `where` /\n * `orderBy` went back to accepting any column name — the alias, not the\n * definition, was where the typing was lost.\n */\nexport type FindParams<M extends Record<string, unknown> = Record<string, unknown>> = TypesFindParams<M>;\nexport type FindResponse<T> = TypesFindResponse<T extends Record<string, unknown> ? T : Record<string, unknown>>;\n\n/**\n * Refuse a filter whose *value* is missing.\n *\n * `where: { status: [\"==\", undefined] }` used to serialize to the literal\n * string, so `status=eq.undefined` went out on the wire and the server dutifully\n * looked for rows whose status is the four-letter word \"undefined\". The caller\n * saw an empty page, not an error — the classic shape of a variable that was\n * never set.\n *\n * Dropping the condition instead would be worse than sending it: the query\n * would come back *unfiltered*, which for an ownership or tenant filter means\n * returning rows the caller never asked to see. So this is a hard error, and\n * both correct spellings are named in the message: omit the key to skip the\n * filter, or use `[\"is-null\", null]` to match SQL NULL (which still\n * serializes — `null` is a value, `undefined` is the absence of one).\n */\nfunction assertNoUndefinedFilterValues(where: Record<string, unknown>): void {\n const reject = (field: string, op: unknown): never => {\n throw new RebaseClientError(\n `Filter on \"${field}\" has an undefined value ([\"${String(op)}\", undefined]). `\n + `Omit \"${field}\" from \\`where\\` to skip the filter, or use [\"is-null\", null] to match SQL NULL.`\n );\n };\n\n for (const [field, condition] of Object.entries(where)) {\n // An entirely absent condition is the documented way to skip a filter.\n if (condition === undefined) continue;\n if (!Array.isArray(condition)) continue;\n\n // Either one `[op, value]` tuple or an array of them.\n const tuples = Array.isArray(condition[0]) ? condition as unknown[][] : [condition as unknown[]];\n for (const tuple of tuples) {\n if (!Array.isArray(tuple) || tuple.length !== 2) continue;\n const [op, value] = tuple;\n if (value === undefined) reject(field, op);\n // `[\"in\", [...]]` — a hole in the list is the same mistake.\n if (Array.isArray(value) && value.some(v => v === undefined)) reject(field, op);\n }\n }\n}\n\nexport function buildQueryString(params?: FindParams): string {\n if (!params) return \"\";\n const parts: string[] = [];\n\n if (params.limit != null) parts.push(`limit=${params.limit}`);\n if (params.offset != null) parts.push(`offset=${params.offset}`);\n if (params.page != null) parts.push(`page=${params.page}`);\n\n if (params.orderBy) {\n const wire = serializeOrderBy(params.orderBy);\n if (wire) parts.push(`orderBy=${encodeURIComponent(wire)}`);\n }\n\n if (params.searchString) {\n parts.push(`searchString=${encodeURIComponent(params.searchString)}`);\n if (params.searchExplain) parts.push(\"searchExplain=true\");\n }\n\n // The server keys vector search off `vector_search` naming the property and\n // `vector` carrying the embedding as a JSON array; both must be present or\n // it ignores the pair entirely.\n if (params.vectorSearch) {\n const vs = params.vectorSearch;\n parts.push(`vector_search=${encodeURIComponent(vs.property)}`);\n parts.push(`vector=${encodeURIComponent(JSON.stringify(vs.vector))}`);\n if (vs.distance) parts.push(`vector_distance=${encodeURIComponent(vs.distance)}`);\n if (vs.threshold !== undefined) parts.push(`vector_threshold=${encodeURIComponent(String(vs.threshold))}`);\n }\n\n if (params.include && params.include.length > 0) {\n parts.push(`include=${encodeURIComponent(params.include.join(\",\"))}`);\n }\n\n if (params.logical) {\n const root = params.logical;\n const serialized = (root.conditions ?? []).map(serializeLogicalCondition).join(\",\");\n parts.push(`${root.type}=${encodeURIComponent(`(${serialized})`)}`);\n }\n\n if (params.where) {\n assertNoUndefinedFilterValues(params.where);\n const serialized = serializeFilter(params.where);\n for (const [field, value] of Object.entries(serialized)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(v)}`);\n }\n } else {\n parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(value)}`);\n }\n }\n }\n\n return parts.length > 0 ? \"?\" + parts.join(\"&\") : \"\";\n}\n\nexport interface Transport {\n request: <T = unknown>(path: string, init?: RequestInit) => Promise<T>;\n setToken: (newToken: string | null) => void;\n setAuthTokenGetter: (getter: () => Promise<string | null>) => void;\n setOnUnauthorized: (handler: () => Promise<boolean>) => void;\n readonly baseUrl: string;\n readonly apiPath: string;\n /** See {@link RebaseClientConfig.storageUrlOrigin}. Undefined = use `baseUrl`. */\n readonly storageUrlOrigin?: string;\n readonly fetchFn: typeof globalThis.fetch;\n getHeaders: (init?: RequestInit) => Record<string, string>;\n resolveToken: () => Promise<string | null>;\n}\n\n/**\n * The base every request and every caller-built URL resolves against.\n *\n * `baseUrl` is optional because the common production shape is a Rebase\n * backend serving its own SPA, where the API is simply the page's origin.\n * Leaving it unset is therefore the *correct* configuration there — and the\n * one that keeps working when a second hostname (a custom domain) points at\n * the same app.\n *\n * When unset in a browser this resolves to the page origin rather than \"\".\n * Requests behave identically either way, but the empty string is a trap for\n * anything that builds a URL from `client.baseUrl`: `new URL(\"\" + path)`\n * throws, so apps \"fixed\" it by baking an absolute host into their bundle —\n * which is exactly what breaks the day a custom domain is added, and which no\n * amount of CORS configuration repairs, because a SameSite=Lax auth cookie is\n * not sent cross-site either.\n */\nfunction resolveBaseUrl(configured?: string): string {\n if (configured) return configured.replace(/\\/$/, \"\");\n if (typeof window !== \"undefined\" && window.location?.origin) return window.location.origin;\n return \"\";\n}\n\nexport function createTransport(config: RebaseClientConfig, environment?: TransportEnvironment): Transport {\n const fetchFn = config.fetch || globalThis.fetch;\n const apiPath = config.apiPath || \"/api\";\n\n // `apiPath` is appended to `baseUrl`, so a `baseUrl` that already ends in it\n // builds `/api/api/…` and every request 404s. That was documented on\n // `baseUrl` and left to be discovered at runtime — including by this\n // package's own tests, which configured it that way a dozen times. A 404 on\n // every call looks like a server that is down, not like a doubled path.\n // `storageUrlOrigin` is checked alongside it because `storage.ts` composes\n // it the same way — `${storageUrlOrigin ?? baseUrl}${apiPath}` — and its own\n // docblock carries the same \"no path\" caveat.\n for (const field of [\"baseUrl\", \"storageUrlOrigin\"] as const) {\n const value = config[field];\n if (!value || !apiPath) continue;\n const trimmed = value.replace(/\\/+$/, \"\");\n if (!trimmed.endsWith(apiPath)) continue;\n console.warn(\n `[Rebase] ${field} ${JSON.stringify(value)} already ends with the API path ` +\n `${JSON.stringify(apiPath)}, which is appended to it — requests will go to ` +\n `${trimmed}${apiPath}/… and 404. Pass the origin only ` +\n `(${JSON.stringify(trimmed.slice(0, trimmed.length - apiPath.length) || \"/\")}), or set ` +\n \"`apiPath` if the server really does mount the API one level deeper.\"\n );\n }\n let token = config.token;\n let tokenGetter: (() => Promise<string | null>) | undefined;\n let onUnauthorizedHandler = config.onUnauthorized;\n /** Once per client, never per request — log spam is its own bug. */\n let anonymousWarningIssued = false;\n\n /**\n * Warn a server-side caller that it built a client that can only ever be\n * anonymous. Deliberately checked at the *first request* rather than at\n * construction: `setToken()` / `setAuthTokenGetter()` and a server-side\n * `auth.signIn…()` (which calls `transport.setToken`) all land after the\n * constructor, and warning at construction would fire on every one of them.\n */\n function warnIfAnonymousServerClient(activeToken: string | undefined): void {\n if (anonymousWarningIssued) return;\n if (activeToken) return; // a credential is being sent\n if (tokenGetter) return; // a credential is being fetched per request\n if (config.anonymous) return; // \"yes, I meant this\"\n if (environment?.credentialOutOfBand) return; // cookie auth flow — credential is not a header\n if (!isServerLikeEnvironment()) return; // browsers are legitimately anonymous\n anonymousWarningIssued = true;\n console.warn(ANONYMOUS_SERVER_CLIENT_WARNING);\n }\n\n function getHeaders(activeToken: string | undefined, init?: RequestInit) {\n return {\n \"Content-Type\": \"application/json\",\n ...(activeToken ? { Authorization: `Bearer ${activeToken}` } : {}),\n ...((init?.headers as Record<string, string>) || {})\n };\n }\n\n /**\n * The refusal for a success status carrying a body this client cannot read.\n *\n * The first 120 characters go in the message because they identify the\n * sender at a glance: `<!doctype html>` says \"you are talking to a web\n * server, not to this API\" faster than any wording here could.\n *\n * One function for both the first attempt and the post-refresh retry — the\n * retry is a second copy of this whole response-reading path, and copies\n * are how one of them ends up fixed and the other not.\n */\n function unreadableResponse(status: number, text: string): RebaseApiError {\n return new RebaseApiError(\n `The server answered ${status} with a body that is not JSON, so there is nothing to return. ` +\n \"This usually means the request reached something other than the Rebase API — a single-page-app \" +\n \"fallback serving index.html, or a proxy error page — so check the API URL configuration \" +\n `(e.g. VITE_API_URL). The body began: ${JSON.stringify(text.slice(0, 120))}`,\n { status, code: \"INVALID_JSON_RESPONSE\" }\n );\n }\n\n async function request<T = unknown>(path: string, init?: RequestInit): Promise<T> {\n const url = resolveBaseUrl(config.baseUrl) + apiPath + path;\n\n let activeToken = token;\n if (tokenGetter) {\n try {\n const fetched = await tokenGetter();\n if (fetched !== null && fetched !== undefined) {\n activeToken = fetched;\n }\n } catch (e) {\n // Ignore error, fallback to static token if any\n }\n }\n\n warnIfAnonymousServerClient(activeToken);\n\n const headers = getHeaders(activeToken, init);\n\n // If passing FormData, we MUST let fetch set the boundary, so remove Content-Type\n if (init?.body instanceof FormData) {\n delete (headers as Record<string, string>)[\"Content-Type\"];\n }\n\n const res = await fetchFn(url, { ...init,\nheaders });\n\n if (res.status === 204) return undefined as T; // SAFETY: HTTP 204 No Content has no body\n\n const text = await res.text().catch(() => \"\");\n let body: Record<string, unknown> = {};\n /**\n * Whether the body was there and could not be read as JSON.\n *\n * On an error status this does not matter — the status is the answer\n * and the message falls back to `statusText`. On a *success* status it\n * is the whole answer, and `{}` was being returned as though the server\n * had sent it: `find()` answered `{}` instead of an array, `getOne()`\n * an empty object, with nothing thrown.\n *\n * The case that produces it is not exotic. Point `VITE_API_URL` at the\n * frontend's own host and `/api/data/posts` lands on the SPA fallback,\n * which answers `200` with `index.html` — so the misconfiguration the\n * 404 branch below spends four lines explaining reaches the caller, in\n * its most common form, as an empty success.\n */\n let unreadableBody = false;\n if (text) {\n try {\n body = JSON.parse(text, rebaseReviver) as Record<string, unknown>;\n } catch (e) {\n unreadableBody = true;\n }\n }\n\n // The server always emits the canonical `{ error: { message, code, details? } }`\n // envelope (formatted by the central errorHandler), so we read strictly\n // from `body.error.*`.\n const getErrorField = (obj: Record<string, unknown>, field: string): unknown => {\n const err = obj?.error;\n if (err && typeof err === \"object\" && err !== null) {\n return (err as Record<string, unknown>)[field];\n }\n return undefined;\n };\n\n if (res.status === 401 && onUnauthorizedHandler) {\n const retried = await onUnauthorizedHandler();\n if (retried) {\n let retryToken = token;\n if (tokenGetter) {\n try {\n const fetched = await tokenGetter();\n if (fetched !== null && fetched !== undefined) {\n retryToken = fetched;\n }\n } catch (e) { /* ignore */ }\n }\n const retryHeaders = getHeaders(retryToken, init) as Record<string, string>;\n const retryRes = await fetchFn(url, { ...init,\nheaders: retryHeaders });\n if (retryRes.status === 204) return undefined as T; // SAFETY: HTTP 204 No Content has no body\n const retryText = await retryRes.text().catch(() => \"\");\n let retryBody: Record<string, unknown> = {};\n let retryUnreadable = false;\n if (retryText) {\n try {\n retryBody = JSON.parse(retryText, rebaseReviver);\n } catch (e) {\n retryUnreadable = true;\n }\n }\n if (!retryRes.ok) {\n let fallbackMessage = retryRes.statusText;\n if (retryRes.status === 404 && !fallbackMessage) {\n const method = init?.method || \"GET\";\n fallbackMessage = `Endpoint not found (${method} ${path}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;\n }\n throw new RebaseApiError(\n String(getErrorField(retryBody, \"message\") || fallbackMessage || `Request failed with status ${retryRes.status}`),\n {\n status: retryRes.status,\n code: getErrorField(retryBody, \"code\") as string | undefined,\n details: getErrorField(retryBody, \"details\")\n }\n );\n }\n if (retryUnreadable) throw unreadableResponse(retryRes.status, retryText);\n return retryBody as T;\n }\n }\n\n if (!res.ok) {\n let fallbackMessage = res.statusText;\n if (res.status === 404 && !fallbackMessage) {\n const method = init?.method || \"GET\";\n fallbackMessage = `Endpoint not found (${method} ${path}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;\n }\n throw new RebaseApiError(\n String(getErrorField(body, \"message\") || fallbackMessage || `Request failed with status ${res.status}`),\n {\n status: res.status,\n code: getErrorField(body, \"code\") as string | undefined,\n details: getErrorField(body, \"details\")\n }\n );\n }\n\n if (unreadableBody) throw unreadableResponse(res.status, text);\n\n return body as T;\n }\n\n return {\n request,\n setToken(newToken: string | null) { token = newToken || undefined; },\n setAuthTokenGetter(getter: () => Promise<string | null>) { tokenGetter = getter; },\n setOnUnauthorized(handler: () => Promise<boolean>) { onUnauthorizedHandler = handler; },\n get baseUrl() { return resolveBaseUrl(config.baseUrl); },\n get apiPath() { return apiPath; },\n get storageUrlOrigin() { return config.storageUrlOrigin?.replace(/\\/$/, \"\") || undefined; },\n get fetchFn() { return fetchFn; },\n getHeaders: (init?: RequestInit) => getHeaders(token, init) as Record<string, string>,\n resolveToken: async () => {\n if (tokenGetter) {\n try {\n const fetched = await tokenGetter();\n if (fetched !== null && fetched !== undefined) {\n return fetched;\n }\n } catch (e) { /* ignore */ }\n }\n return token || null;\n }\n };\n}\n","import { RebaseApiError, Transport } from \"./transport\";\nimport type { 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 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\nexport interface AuthConfig {\n needsSetup: boolean;\n registrationEnabled: boolean;\n emailServiceEnabled?: boolean;\n passwordReset?: boolean;\n emailVerification?: boolean;\n magicLink?: boolean;\n enabledProviders: string[];\n}\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\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 and signing out.\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 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 let resolveInitialized: (value: void | PromiseLike<void>) => void;\n const isInitialized = new Promise<void>((resolve) => {\n resolveInitialized = resolve;\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 function throwApiError(status: number, body: { error?: { message?: string; code?: string; details?: unknown }; message?: string; code?: string; details?: unknown } | undefined, statusText: string): never {\n throw 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 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 function abandonSessionLocally() {\n currentSession = null;\n 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 try {\n await refreshSession();\n return true;\n } catch (err) {\n if (isFatalRefreshError(err)) {\n abandonSessionLocally();\n }\n return false;\n }\n }\n\n async function attemptScheduledRefresh(attempt: number) {\n try {\n await refreshSession();\n // On success, refreshSession() re-schedules the next refresh itself.\n } catch (err) {\n if (isFatalRefreshError(err)) {\n abandonSessionLocally();\n return;\n }\n if (attempt >= MAX_REFRESH_RETRIES) {\n abandonSessionLocally();\n return;\n }\n // Transient failure — back off and retry rather than dropping the session.\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\n function scheduleRefresh(expiresAt: number) {\n if (refreshTimeout) clearTimeout(refreshTimeout);\n if (!autoRefresh) 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 const user: User = mapRawUser(data.user);\n const session: RebaseSession = {\n accessToken: data.tokens.accessToken,\n refreshToken: data.tokens.refreshToken || (currentSession?.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 const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\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 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: JSON.stringify({ refreshToken: currentSession?.refreshToken }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n }\n } catch (e) { /* ignore */ }\n currentSession = null;\n clearStoredSession();\n if (refreshTimeout) {\n clearTimeout(refreshTimeout);\n refreshTimeout = null;\n }\n transport.setToken(null);\n emit(\"SIGNED_OUT\", null);\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.\n if (inFlightRefresh) return inFlightRefresh;\n inFlightRefresh = withRefreshLock(() => doRefreshSession()).finally(() => {\n inFlightRefresh = null;\n });\n return inFlightRefresh;\n }\n\n async function doRefreshSession(): Promise<RebaseSession> {\n if (authFlowMode !== \"cookie\" && !currentSession?.refreshToken) {\n throw new Error(\"No active session to refresh\");\n }\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/refresh\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: currentSession?.refreshToken }),\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\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 try {\n user = await getUser();\n } catch { /* fall through to the empty stub below */ }\n }\n\n const session: RebaseSession = {\n accessToken,\n refreshToken: body.tokens.refreshToken || currentSession?.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 async function changePassword(oldPassword: string, newPassword: string) {\n return transport.request<{ success: boolean; message: string; }>(authPath + \"/change-password\", {\n method: \"POST\",\n body: JSON.stringify({ oldPassword,\nnewPassword })\n });\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<{ success: boolean; provider: string; alreadyLinked: boolean; }>(\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 async function verifyEmail(token: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/verify-email?token=\" + encodeURIComponent(token)), {\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 { success: boolean; message: string; };\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 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 currentSession = null;\n clearStoredSession();\n if (refreshTimeout) {\n clearTimeout(refreshTimeout);\n refreshTimeout = null;\n }\n transport.setToken(null);\n emit(\"SIGNED_OUT\", null);\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 resolveInitialized!();\n } else if (authFlowMode === \"cookie\" || stored.refreshToken) {\n currentSession = stored;\n refreshSession().then(() => {\n resolveInitialized!();\n }).catch(() => {\n currentSession = null;\n clearStoredSession();\n transport.setToken(null);\n resolveInitialized!();\n });\n } else {\n resolveInitialized!();\n }\n } else if (authFlowMode === \"cookie\") {\n // Silent refresh on boot to pick up httpOnly session\n refreshSession().then(() => {\n resolveInitialized!();\n }).catch(() => {\n resolveInitialized!();\n });\n } else {\n resolveInitialized!();\n }\n } else {\n resolveInitialized!();\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 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 sendMagicLink,\n verifyMagicLink,\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}\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 { AdminUser } from \"@rebasepro/types\";\n\nexport type { AdminUser };\n\n\nexport interface CreateAdminOptions {\n adminPath?: string;\n}\n\nexport function createAdmin(transport: Transport, options?: CreateAdminOptions) {\n const opts = options || {};\n const adminPath = opts.adminPath || \"/admin\";\n\n async function listUsers() {\n return transport.request<{ users: AdminUser[] }>(adminPath + \"/users\", { method: \"GET\" });\n }\n\n async function listUsersPaginated(options?: { search?: string; limit?: number; offset?: number; orderBy?: string; orderDir?: \"asc\" | \"desc\" }) {\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 async function createUser(data: { email: string, displayName?: string, password?: string, roles?: string[] }) {\n return transport.request<{ user: AdminUser }>(adminPath + \"/users\", {\n method: \"POST\",\n body: JSON.stringify(data)\n });\n }\n\n async function updateUser(userId: string, data: { email?: string, displayName?: string, password?: string, roles?: string[] }) {\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 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: Array<{ id: string; name: string }> }>(\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 resetPassword,\n listRoles,\n bootstrap\n };\n}\n","import { Transport } from \"./transport\";\nimport type { CronJobStatus, CronJobLogEntry } from \"@rebasepro/types\";\n\nexport interface CreateCronOptions {\n cronPath?: string;\n}\n\nexport function createCron(transport: Transport, options?: CreateCronOptions) {\n const cronPath = options?.cronPath || \"/cron\";\n\n async function listJobs(): Promise<{ jobs: CronJobStatus[] }> {\n return transport.request<{ jobs: CronJobStatus[] }>(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 async function triggerJob(jobId: string): Promise<{ log: CronJobLogEntry; job: CronJobStatus }> {\n return transport.request<{ log: CronJobLogEntry; job: CronJobStatus }>(\n cronPath + \"/\" + encodeURIComponent(jobId) + \"/trigger\",\n { method: \"POST\" }\n );\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 type { BackupInfo, BackupDestinationKind } from \"@rebasepro/types\";\n\nexport interface CreateBackupsOptions {\n backupsPath?: string;\n}\n\nexport function createBackups(transport: Transport, options?: CreateBackupsOptions) {\n const backupsPath = options?.backupsPath || \"/admin/backups\";\n\n async function list(): Promise<{\n backups: BackupInfo[];\n destinationKind: BackupDestinationKind;\n configured: boolean;\n }> {\n return transport.request(backupsPath, { method: \"GET\" });\n }\n\n /**\n * Download a backup's bytes. Uses an authenticated fetch (not the JSON\n * transport) so the octet-stream response comes back as a Blob.\n */\n async function download(key: string): Promise<Blob> {\n const token = await transport.resolveToken();\n // Mirror transport.request's URL construction (baseUrl + apiPath + path)\n // — this endpoint returns an octet-stream, so we fetch it directly\n // instead of going through the JSON transport.\n const url = `${transport.baseUrl}${transport.apiPath}${backupsPath}/download?key=${encodeURIComponent(key)}`;\n const res = await fetch(url, {\n method: \"GET\",\n headers: token ? { Authorization: `Bearer ${token}` } : {}\n });\n if (!res.ok) {\n throw new Error(`Failed to download backup (${res.status})`);\n }\n return res.blob();\n }\n\n return { list, download };\n}\n","import type { Transport } from \"./transport\";\n\n/**\n * These were re-declared here, under a comment saying they lived in the server\n * package rather than in `@rebasepro/types`. That stopped being true, and the\n * copy drifted: it never gained `admin`, the flag that grants a key the `admin`\n * role — admin routes plus the RLS `default_admin` policies — so the SDK could\n * describe every kind of key except a privileged one, and\n * `createKey({ …, admin: true })` was an excess-property error.\n */\nexport type {\n ApiKeyPermission,\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n UpdateApiKeyRequest\n} from \"@rebasepro/types\";\n\nimport type {\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\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 managing API keys via the admin routes.\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","import {\n FindParams,\n FindResult,\n LogicalCondition,\n SDKCollectionClient,\n SDKQueryBuilderInterface,\n WhereFilterOp,\n WhereValueFor,\n type ComputedSortField\n} from \"@rebasepro/types\";\n\n/**\n * SDK Query Builder — returns flat rows (`FindResult<M>`) instead of\n * Entity-wrapped results (`FindResponse<M>`).\n *\n * @example\n * const { data } = await rebase.data.posts\n * .where(\"status\", \"==\", \"published\")\n * .orderBy(\"created_at\", \"desc\")\n * .limit(10)\n * .find();\n *\n * console.log(data[0].title); // flat access\n */\nexport class SDKQueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements SDKQueryBuilderInterface<M> {\n // The accumulator stays keyed by plain `string`: it is written through\n // `where()` / `orderBy()` below, whose own parameters are typed against `M`,\n // and a `Partial<Record<FieldPath<M>, …>>` is read-only under a generic `M`\n // (TS2862) so it cannot be built up in place. The typing users see is on the\n // methods; this field is the buffer behind them, cast once on the way out.\n private params: FindParams = { where: {} };\n\n constructor(private collection: SDKCollectionClient<M>) {}\n\n /**\n * Add a filter condition to your query.\n * @example\n * client.data.users.where('age', '>=', 18).find()\n */\n where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;\n where(logicalCondition: LogicalCondition): this;\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown): this {\n if (typeof columnOrCondition === \"object\" && columnOrCondition !== null && \"type\" in columnOrCondition) {\n this.params.logical = columnOrCondition as LogicalCondition;\n return this;\n }\n\n if (!this.params.where) {\n this.params.where = {};\n }\n\n const column = columnOrCondition as string;\n const condition: [WhereFilterOp, unknown] = [operator!, value];\n const existing = this.params.where[column];\n\n if (existing === undefined) {\n this.params.where[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n (this.params.where[column] as [WhereFilterOp, unknown][]).push(condition);\n } else {\n let firstCondition: [WhereFilterOp, unknown];\n if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === \"string\") {\n firstCondition = existing as [WhereFilterOp, unknown];\n } else {\n firstCondition = [\"==\", existing];\n }\n this.params.where[column] = [firstCondition, condition];\n }\n\n return this;\n }\n\n /**\n * Order the results by a specific column.\n */\n orderBy(column: (keyof M & string) | ComputedSortField, direction: \"asc\" | \"desc\" = \"asc\"): this {\n this.params.orderBy = [column, direction];\n return this;\n }\n\n /**\n * Limit the number of results returned.\n */\n limit(count: number): this {\n this.params.limit = count;\n return this;\n }\n\n /**\n * Skip the first N results.\n */\n offset(count: number): this {\n this.params.offset = count;\n return this;\n }\n\n /**\n * Set a free-text search string if supported by the backend.\n *\n * By default this is a substring match across the collection's top-level\n * string properties. A Postgres collection that declares a `search` block\n * gets ranked full-text matching over the fields it named instead, and each\n * row comes back with a `_score` you can sort on:\n *\n * ```ts\n * client.data.talents.search(\"auditor iso 14001\").orderBy(\"_score\", \"desc\").find()\n * ```\n *\n * Pass `{ explain: true }` to have each row report which of the declared\n * fields matched, with a highlighted snippet, on `_matches`:\n *\n * ```ts\n * const { data } = await client.data.talents.search(\"iso 14001\", { explain: true }).find();\n * data[0]._matches\n * // [{ field: \"questionnaire.certifications\", snippet: \"<mark>ISO</mark> <mark>14001</mark> Lead Auditor\" }]\n * ```\n */\n search(searchString: string, options?: { explain?: boolean }): this {\n this.params.searchString = searchString;\n if (options?.explain !== undefined) this.params.searchExplain = options.explain;\n return this;\n }\n\n /**\n * Order rows by nearest-neighbour distance to `vector`.\n *\n * The server has supported this from the REST layer since vectors landed;\n * this is the SDK reaching it. Results come back closest-first with a\n * `_distance` on each row, and any `where` / `orderBy` on the same query is\n * a filter applied before the ordering — distance decides the order.\n *\n * You supply the query vector. Rebase stores and searches embeddings; it\n * does not produce them, so this is where whatever model you already use\n * for the stored vectors gets called.\n *\n * @param property - Name of the `vector` property to compare against.\n * @param vector - The query embedding. Its length must match the property's\n * declared `dimensions`, or the server answers 400.\n * @example\n * client.data.docs.vectorSearch(\"embedding\", queryVector, { threshold: 0.35 }).limit(10).find()\n */\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ): this {\n this.params.vectorSearch = {\n property,\n vector,\n ...(options?.distance !== undefined && { distance: options.distance }),\n ...(options?.threshold !== undefined && { threshold: options.threshold })\n };\n return this;\n }\n\n /**\n * Include related entities in the response.\n * Relations will be populated with full data instead of just IDs.\n *\n * @param relations - Relation names to include, or \"*\" for all.\n * @example\n * client.data.posts.include(\"tags\", \"author\").find()\n */\n include(...relations: string[]): this {\n this.params.include = relations;\n return this;\n }\n\n /**\n * Execute the find query and return the results as flat rows.\n */\n async find(): Promise<FindResult<M>> {\n return this.collection.find(this.params as FindParams<M>);\n }\n\n /**\n * Count the records matching this query.\n */\n async count(): Promise<number> {\n if (!this.collection.count) {\n throw new Error(\"count() is not supported by this collection client.\");\n }\n return this.collection.count(this.params as FindParams<M>);\n }\n\n /**\n * Listen to realtime updates matching this query.\n */\n listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void {\n if (!this.collection.listen) {\n throw new Error(\n \"Listen is only available when RebaseClient is configured with a websocketUrl, \" +\n \"and not when it was created with realtime: false.\"\n );\n }\n return this.collection.listen(this.params as FindParams<M>, onUpdate, onError);\n }\n}\n","import { buildQueryString, FindParams, RebaseApiError, Transport } from \"./transport\";\nimport { RebaseWebSocketClient } from \"./websocket\";\nimport {\n FindAllParams,\n FindResult,\n IterateParams,\n LogicalCondition,\n SDKCollectionClient,\n SDKQueryBuilderInterface,\n WhereFilterOp,\n WhereValueFor,\n WriteOptions,\n type ComputedSortField\n} from \"@rebasepro/types\";\nimport { collectAllPages, paginateFind, resolveFindWindow } from \"@rebasepro/common\";\n\nimport { SDKQueryBuilder } from \"./sdk_query_builder\";\n\n/**\n * Counts currently in flight, keyed by the exact request they issue. Entries\n * live only for the duration of the request — see `count()` for why.\n */\nconst inflightCounts = new Map<string, Promise<number>>();\n\n/**\n * A live query result: a normal {@link FindResult} plus what an interface\n * needs to decide whether to show a \"saving…\" or \"offline\" affordance over it.\n *\n * The three flags are always `false` on a client without offline support —\n * every result there came straight from the server.\n */\nexport interface LiveResult<M extends Record<string, unknown>> extends FindResult<M> {\n /** The data came from the local database, not from a completed request. */\n fromCache: boolean;\n /** At least one row here carries a write the server has not accepted yet. */\n hasPendingWrites: boolean;\n /**\n * The local database may not hold every row the server would have\n * returned, so treat this as a best effort rather than a complete answer.\n */\n partial: boolean;\n /** The most recent revalidation failure, when the last one failed. */\n error?: Error;\n}\n\n/** Snapshot metadata for a single observed row. */\nexport interface RowSnapshotMeta {\n fromCache: boolean;\n hasPendingWrites: boolean;\n}\n\nexport interface ObserveOptions {\n /**\n * Keep the subscription live off the realtime socket, so changes made by\n * other clients arrive without a refetch. On by default whenever realtime\n * is enabled on the client; pass `false` for a one-shot read that still\n * reports offline/pending metadata.\n */\n realtime?: boolean;\n}\n\n/**\n * The concrete, HTTP-backed implementation of the public\n * {@link SDKCollectionClient} contract — flat rows (no Entity wrapper), plus\n * fluent query-builder methods (`.where()`, `.orderBy()`, …).\n *\n * This is what `createRebaseClient().data.<collection>` returns. It is not a\n * separate API from {@link SDKCollectionClient}; it only widens it with\n * `count()` and the reactive `observe()` pair. Program against\n * {@link SDKCollectionClient} when you want a transport-agnostic type.\n */\nexport interface CollectionClient<\n M extends Record<string, unknown> = Record<string, unknown>,\n I = Partial<M>,\n U = Partial<M>\n> extends SDKCollectionClient<M, I, U> {\n count(params?: FindParams<M>): Promise<number>;\n\n /**\n * Subscribe to a query's results.\n *\n * This is the reactive read primitive, and the one to reach for in a UI:\n * unlike `find()` it keeps emitting. On a client with `offline` enabled it\n * is local-first — the first emission comes from the local database, with\n * no request in the way — and re-emits on every local write, every queued\n * write reaching the server, and every rollback. With realtime enabled it\n * also re-emits on changes made by other clients.\n *\n * Emissions are de-duplicated: a refresh that changes nothing does not\n * call back.\n *\n * @returns An unsubscribe function.\n */\n observe(\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ): () => void;\n\n /** {@link CollectionClient.observe} for a single row. */\n observeById(\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ): () => void;\n}\n\nexport function createCollectionClient<M extends Record<string, unknown> = Record<string, unknown>>(transport: Transport, slug: string, ws?: RebaseWebSocketClient): CollectionClient<M> {\n const basePath = `/data/${slug}`;\n\n const client: CollectionClient<M> = {\n async find(params?: FindParams<M>): Promise<FindResult<M>> {\n const qs = buildQueryString(params);\n const raw = await transport.request<{\n data: Record<string, unknown>[];\n meta: FindResult<M>[\"meta\"]\n }>(basePath + qs, { method: \"GET\" });\n return {\n data: (raw.data || []) as M[],\n meta: raw.meta\n };\n },\n\n // The pagination engine lives in `@rebasepro/common`, shared with the\n // in-process accessor: `iterate()` has to mean the same thing whichever\n // transport the caller happens to be holding.\n iterate(params?: IterateParams<M>) {\n return paginateFind<M>((p) => client.find(p), params, slug);\n },\n\n findAll(params?: FindAllParams<M>) {\n return collectAllPages<M>((p) => client.find(p), params, slug);\n },\n\n async findById(id: string | number) {\n try {\n const raw = await transport.request<Record<string, unknown>>(`${basePath}/${encodeURIComponent(String(id))}`, { method: \"GET\" });\n if (!raw) return undefined;\n return raw as M;\n } catch (err) {\n if (err instanceof RebaseApiError && err.status === 404) {\n return undefined;\n }\n throw err;\n }\n },\n\n async create(data: Partial<M>, id?: string | number, options?: WriteOptions) {\n const body: Record<string, unknown> = { ...data };\n if (id !== undefined) {\n body.id = id;\n }\n const raw = await transport.request<Record<string, unknown>>(basePath, {\n method: \"POST\",\n body: JSON.stringify(body),\n ...(options?.idempotencyKey\n ? { headers: { \"Idempotency-Key\": options.idempotencyKey } }\n : {})\n });\n return raw as M;\n },\n\n async createMany(data: Partial<M>[], options?: { upsert?: boolean } & WriteOptions) {\n if (!Array.isArray(data)) {\n throw new TypeError(\"createMany expects an array of records.\");\n }\n if (data.length === 0) return [];\n\n const raw = await transport.request<{ data: Record<string, unknown>[] }>(`${basePath}/bulk`, {\n method: \"POST\",\n body: JSON.stringify({\n rows: data,\n ...(options?.upsert ? { upsert: true } : {})\n }),\n ...(options?.idempotencyKey\n ? { headers: { \"Idempotency-Key\": options.idempotencyKey } }\n : {})\n });\n return (raw.data || []) as M[];\n },\n\n /**\n * Still `PUT`, deliberately, even though the server now serves `PATCH`\n * on the same handler and `PATCH` is the honest verb for a merge.\n *\n * The two are interchangeable server-side, so switching buys nothing at\n * runtime — and it costs compatibility in the direction that fails\n * quietly. A 0.14 client talking to a 0.13 server would send `PATCH` to\n * a route that does not exist and get a **404**, which is\n * indistinguishable from \"that row is gone\". Every write would look like\n * a missing record.\n *\n * `PATCH` is what the OpenAPI spec advertises, so anyone generating a\n * client gets the correct verb; this stays on `PUT` until the oldest\n * supported server is one that serves both.\n */\n async update(id: string | number, data: Partial<M>) {\n const raw = await transport.request<Record<string, unknown>>(`${basePath}/${encodeURIComponent(String(id))}`, {\n method: \"PUT\",\n body: JSON.stringify(data)\n });\n return raw as M;\n },\n\n async updateMany(updates: { id: string | number; data: Partial<M> }[], options?: WriteOptions) {\n if (!Array.isArray(updates)) {\n throw new TypeError(\"updateMany expects an array of { id, data } entries.\");\n }\n if (updates.length === 0) return [];\n\n const raw = await transport.request<{ data: Record<string, unknown>[] }>(`${basePath}/bulk`, {\n method: \"PATCH\",\n body: JSON.stringify({ updates }),\n ...(options?.idempotencyKey\n ? { headers: { \"Idempotency-Key\": options.idempotencyKey } }\n : {})\n });\n return (raw.data || []) as M[];\n },\n\n async delete(id: string | number) {\n await transport.request<void>(`${basePath}/${encodeURIComponent(String(id))}`, {\n method: \"DELETE\"\n });\n },\n\n /**\n * `POST .../bulk/delete`, not `DELETE .../bulk`.\n *\n * The honest verb would take the ids in a DELETE body, and that is the\n * one request shape the HTTP ecosystem handles unreliably: bodies on\n * DELETE are permitted but widely dropped by proxies and CDNs, and\n * several OpenAPI generators ignore `requestBody` on a DELETE\n * operation, so a generated client would send the request without its\n * ids. A backend deployed behind arbitrary ingress cannot take that\n * bet. Same reason `:batchDelete` exists in Google's API guidelines.\n */\n async deleteMany(ids: (string | number)[], options?: WriteOptions) {\n if (!Array.isArray(ids)) {\n throw new TypeError(\"deleteMany expects an array of ids.\");\n }\n if (ids.length === 0) return;\n\n await transport.request<void>(`${basePath}/bulk/delete`, {\n method: \"POST\",\n body: JSON.stringify({ ids }),\n ...(options?.idempotencyKey\n ? { headers: { \"Idempotency-Key\": options.idempotencyKey } }\n : {})\n });\n },\n\n async count(params?: FindParams<M>): Promise<number> {\n const countParams: FindParams<M> = {\n ...params,\n limit: undefined,\n offset: undefined,\n // A count reads no relation data, so `include` can only add\n // joins — and a join that does not match drops rows, which\n // would make the total disagree with the `find()` it describes.\n // Same reasoning as limit/offset: parameters that cannot affect\n // the answer are not forwarded.\n include: undefined\n };\n const qs = buildQueryString(countParams);\n\n // One count per query in flight, not one per caller.\n //\n // A count is a property of the query, and every concurrent caller\n // asking the same question wants the same answer — so they can\n // share the one request. This is not a micro-optimisation: a live\n // subscription re-counts on every push (see `listen` below), and\n // `listenCollection` deliberately collapses identical queries onto\n // a single socket subscription while keeping one callback per\n // subscriber. Each push therefore woke N subscribers, and each of\n // them issued its own identical count. A table showing one relation\n // column fired one count per visible cell, on every update.\n //\n // The entry is dropped as soon as it settles, so this merges\n // concurrent calls only and never serves a cached total.\n const key = basePath + \"/count\" + qs;\n const inflight = inflightCounts.get(key);\n if (inflight) return inflight;\n\n const request = transport\n .request<{ count: number }>(key, { method: \"GET\" })\n .then((raw) => raw.count ?? 0);\n inflightCounts.set(key, request);\n try {\n return await request;\n } finally {\n inflightCounts.delete(key);\n }\n },\n\n // Reactive reads. Without the offline layer there is no local database\n // to read from, so this is a fetch plus — when realtime is available —\n // the live subscription that keeps it current.\n observe(\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) {\n let closed = false;\n // Two sources race into one callback: the one-shot fetch below and\n // the subscription beside it. Whichever resolves last used to win,\n // so a socket update that landed first was overwritten by the\n // fetch's older snapshot and stayed wrong until the next change.\n // `listenCollection` replays cached rows synchronously to a second\n // subscriber, so a second component observing the same query hit\n // that ordering every time.\n let liveDelivered = false;\n let signature: string | undefined;\n\n const deliver = (result: FindResult<M>, fromLive: boolean) => {\n if (closed) return;\n // Once the socket has spoken, the fetch issued alongside it is\n // older news — delivering it would move the app backwards.\n if (fromLive) liveDelivered = true;\n else if (liveDelivered) return;\n // The de-duplication `observe()` documents. Keyed on the rows\n // and the total, the same two things the offline layer keys on,\n // so both implementations mean the same thing by \"changed\".\n const next = `${result.meta?.total ?? \"\"}|${JSON.stringify(result.data)}`;\n if (signature !== undefined && next === signature) return;\n signature = next;\n onResult({ ...result, fromCache: false, hasPendingWrites: false, partial: false });\n };\n\n client.find(params).then((result) => deliver(result, false)).catch((error) => {\n if (!closed) onError?.(error as Error);\n });\n const live = options?.realtime !== false && client.listen\n ? client.listen(params, (result) => deliver(result, true), onError)\n : undefined;\n return () => {\n closed = true;\n live?.();\n };\n },\n\n observeById(\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) {\n let closed = false;\n let liveDelivered = false;\n let signature: string | undefined;\n\n // Same ordering and de-duplication rules as `observe`, for one row.\n const deliver = (row: M | undefined, fromLive: boolean) => {\n if (closed) return;\n if (fromLive) liveDelivered = true;\n else if (liveDelivered) return;\n const next = row === undefined ? \"\\u0000missing\" : JSON.stringify(row);\n if (signature !== undefined && next === signature) return;\n signature = next;\n onResult(row, { fromCache: false, hasPendingWrites: false });\n };\n\n client.findById(id).then((row) => deliver(row, false)).catch((error) => {\n if (!closed) onError?.(error as Error);\n });\n const live = options?.realtime !== false && client.listenById\n ? client.listenById(id, (row) => deliver(row, true), onError)\n : undefined;\n return () => {\n closed = true;\n live?.();\n };\n },\n\n // Fluent builder instantiation\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SDKQueryBuilder<M>(client);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy(column: (keyof M & string) | ComputedSortField, direction?: \"asc\" | \"desc\") {\n return new SDKQueryBuilder<M>(client).orderBy(column, direction);\n },\n limit(count: number) {\n return new SDKQueryBuilder<M>(client).limit(count);\n },\n offset(count: number) {\n return new SDKQueryBuilder<M>(client).offset(count);\n },\n search(searchString: string, options?: { explain?: boolean }) {\n return new SDKQueryBuilder<M>(client).search(searchString, options);\n },\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) {\n return new SDKQueryBuilder<M>(client).vectorSearch(property, vector, options);\n },\n include(...relations: string[]) {\n return new SDKQueryBuilder<M>(client).include(...relations);\n }\n };\n\n if (ws) {\n client.listen = (params: FindParams<M> | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void) => {\n let active = true;\n let lastUpdateId = 0;\n // The last total a `count()` actually returned. A later count that\n // fails says nothing about how big the collection is, so it must\n // not be allowed to replace this with the length of one page.\n let lastKnownTotal: number | undefined;\n const window = resolveFindWindow(params);\n const unsub = ws.listenCollection(\n {\n path: slug,\n filter: params?.where,\n // The group used to be dropped here, so a subscription\n // filtered with `or(...)` was pushed every row instead.\n logical: params?.logical,\n limit: params?.limit,\n // `offset` used to be stringified into `startAfter`, which\n // is a cursor *row*, not a row count — so the server was\n // handed \"20\" where it expected a keyset value, and the\n // offset it does understand never arrived at all.\n offset: window.driverOffset,\n orderBy: params?.orderBy?.[0],\n order: params?.orderBy?.[1],\n searchString: params?.searchString,\n searchExplain: params?.searchExplain\n },\n (incomingRows: Record<string, unknown>[]) => {\n const currentUpdateId = ++lastUpdateId;\n // What the server pages by when the caller names no limit.\n // A hardcoded 20 here described a window the rows had not\n // come from, and any app sizing its next request off\n // `meta.limit` was told the wrong number.\n const requestedLimit = window.limit;\n const offset = window.offset;\n\n // WS client already delivers flat rows — just cast\n const rows = incomingRows as M[];\n\n const emit = (total: number, hasMore: boolean) => {\n if (!active || currentUpdateId !== lastUpdateId) return;\n onUpdate({\n data: rows,\n meta: {\n total,\n limit: requestedLimit,\n offset,\n hasMore\n }\n });\n };\n\n // With no count to go on, the only defensible total is a\n // lower bound: the rows on this page plus the ones paged\n // past to reach them. Reporting `rows.length` claimed a\n // collection read at offset 10 held two rows.\n const emitWithoutCount = () => emit(\n offset + rows.length,\n rows.length >= requestedLimit\n );\n\n if (client.count) {\n client.count(params)\n .then((total) => {\n lastKnownTotal = total;\n emit(total, offset + rows.length < total);\n })\n .catch(() => {\n // A count that failed is not evidence about the\n // size of the collection. Keep the last real\n // answer; only guess if there has never been one.\n if (lastKnownTotal !== undefined) {\n emit(lastKnownTotal, offset + rows.length < lastKnownTotal);\n } else {\n emitWithoutCount();\n }\n });\n } else {\n emitWithoutCount();\n }\n },\n onError\n );\n\n return () => {\n active = false;\n unsub();\n };\n };\n\n client.listenById = (id: string | number, onUpdate: (data: M | undefined) => void, onError?: (error: Error) => void) => {\n return ws.listenOne(\n {\n path: slug,\n id: String(id)\n },\n (row: Record<string, unknown> | null) => {\n if (row) {\n onUpdate(row as M);\n } else {\n onUpdate(undefined);\n }\n },\n onError\n );\n };\n }\n\n return client;\n}\n","import type { Transport } from \"./transport\";\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 * @typeParam T - Expected shape of the response payload.\n * @param name - Function name (the filename without extension, e.g. `\"extract-job\"`).\n * @param payload - Optional JSON-serialisable body sent as `POST`.\n * @param options - Optional overrides (HTTP method, sub-path, extra headers).\n * @returns The parsed JSON response from the function.\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 */\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 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 return transport.request<T>(routePath, init);\n }\n };\n}\n","import { StorageSource, UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult, DownloadMetadata, PUBLIC_STORAGE_PREFIX, isPublicStoragePath } from \"@rebasepro/types\";\nimport { Transport } 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 */\nexport function createStorage(transport: Transport, storageId?: string): StorageSource {\n const urlsCache = new Map<string, { config: DownloadConfig; expiresAt?: number }>();\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 async function putObject({\n file,\n key,\n metadata,\n bucket,\n public: isPublic\n }: UploadFileProps): Promise<UploadFileResult> {\n const formData = new FormData();\n formData.append(\"file\", file);\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 let effectiveKey = key;\n if (isPublic && effectiveKey && !isPublicStoragePath(effectiveKey)) {\n effectiveKey = `${PUBLIC_STORAGE_PREFIX}${effectiveKey.replace(/^\\/+/, \"\")}`;\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 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 const cachedEntry = urlsCache.get(cacheKey);\n if (cachedEntry) {\n if (!cachedEntry.expiresAt || cachedEntry.expiresAt > Date.now()) {\n return cachedEntry.config;\n }\n urlsCache.delete(cacheKey);\n }\n\n let filePath = keyOrUrl;\n\n if (filePath && (filePath.startsWith(\"local://\") || filePath.startsWith(\"s3://\") || filePath.startsWith(\"gs://\"))) {\n filePath = filePath.substring(filePath.indexOf(\"://\") + 3);\n }\n\n if (bucket && filePath && !filePath.startsWith(bucket)) {\n filePath = `${bucket}/${filePath}`;\n }\n\n if (!filePath || filePath.trim() === \"\" || filePath === \"/\") {\n return { url: null, fileNotFound: 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 const publicConfig: DownloadConfig = {\n url: withStorageId(`${fileUrlBase()}/storage/file/${filePath}`)\n };\n urlsCache.set(cacheKey, { config: publicConfig }); // no expiry\n return publicConfig;\n }\n\n try {\n const result = await transport.request<{ data: DownloadMetadata }>(withStorageId(`/storage/metadata/${filePath}`));\n\n // Public object (server-confirmed): token-less permanent URL.\n if (result.data.public) {\n const publicConfig: DownloadConfig = {\n url: withStorageId(`${fileUrlBase()}/storage/file/${filePath}`),\n metadata: result.data\n };\n urlsCache.set(cacheKey, { config: publicConfig }); // no expiry\n return publicConfig;\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 = result.data.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/${filePath}${tokenQuery}`),\n metadata: result.data\n };\n\n const expiresAt = result.data.tokenExpiresIn\n ? Date.now() + (result.data.tokenExpiresIn - 10) * 1000 // subtract 10s buffer\n : undefined;\n\n urlsCache.set(cacheKey, { config: downloadConfig, expiresAt });\n return downloadConfig;\n } catch (e: unknown) {\n if (e instanceof Error && \"status\" in e && (e as { status: number }).status === 404) {\n return { url: null, fileNotFound: 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 if (!response.ok) throw new Error(\"Failed to get file\");\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 let filePath = key;\n\n if (filePath && (filePath.startsWith(\"local://\") || filePath.startsWith(\"s3://\") || filePath.startsWith(\"gs://\"))) {\n filePath = filePath.substring(filePath.indexOf(\"://\") + 3);\n }\n\n if (bucket && filePath && !filePath.startsWith(bucket)) {\n filePath = `${bucket}/${filePath}`;\n }\n\n if (!filePath || filePath.trim() === \"\" || filePath === \"/\") {\n return;\n }\n\n try {\n await transport.request(withStorageId(`/storage/file/${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 // Fallback to default\n console.warn(\n `[StorageSourceRegistry] Storage source \"${key}\" not found, ` +\n `falling back to \"${DEFAULT_STORAGE_SOURCE_KEY}\".`\n );\n return this.getDefault();\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 {\n DeleteProps,\n CollectionConfig,\n FetchCollectionProps,\n FetchOneProps,\n SaveProps,\n WebSocketMessage,\n WebSocketErrorPayload,\n CollectionUpdateMessage,\n SingleUpdateMessage,\n TableMetadata,\n BranchInfo,\n RebaseApiError\n} from \"@rebasepro/types\";\nimport { buildCompositeId, COMPOSITE_ID_SEPARATOR, type PrimaryKeyInfo } from \"@rebasepro/common\";\nimport { rebaseReviver } from \"./reviver\";\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\nexport interface RebaseWebSocketConfig {\n websocketUrl: string;\n /** Optional auth token getter for WebSocket authentication */\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\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 * 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 */\nexport class RebaseWebSocketClient {\n private websocketUrl: string;\n private ws: WebSocket | null = null;\n public getAuthToken?: () => Promise<string | null>;\n private subscriptions = new Map<string, {\n onUpdate: (data: WebSocketMessage) => void,\n onError?: (error: Error) => void\n }>();\n\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: Record<string, unknown>) => 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, cleared by anything that earns a\n * fresh one.\n *\n * Unlike {@link closedByCaller} this is not final — nobody *asked* for the\n * socket to stay down. Five attempts with exponential backoff is about a\n * minute, which a laptop lid, a wifi handover or a backend rollout all\n * exceed routinely; treating that as permanent meant realtime silently\n * stopped for the rest of the page's life, with a reload the only cure.\n */\n private gaveUp = false;\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 /** Subscribe to broadcast/presence frames for one channel. */\n public onChannelMessage(channel: string, handler: (message: Record<string, unknown>) => 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 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>[]) => void;\n onError?: (error: Error) => void;\n }>;\n props: FetchCollectionProps;\n latestData?: Record<string, unknown>[]; // Cache the latest flat rows\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: FetchOneProps;\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 private maxReconnectAttempts = 5;\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\n constructor(config: RebaseWebSocketConfig) {\n this.websocketUrl = config.websocketUrl;\n this.getAuthToken = config.getAuthToken;\n this.onUnauthorized = config.onUnauthorized;\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.installOnlineListener();\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 }\n this.initWebSocket();\n }\n\n /**\n * The browser says the network is back — the usual reason the budget ran\n * out in the first place. Registered lazily so a Node client, or a page\n * that never subscribes, adds no listener.\n */\n private installOnlineListener() {\n if (this.onlineListener || typeof window === \"undefined\" || typeof window.addEventListener !== \"function\") return;\n this.onlineListener = () => {\n if (this.closedByCaller || !this.gaveUp) return;\n console.debug(\"Network is back — retrying the realtime connection\");\n this.ensureConnected();\n };\n window.addEventListener(\"online\", this.onlineListener);\n }\n\n private onlineListener: (() => void) | null = null;\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: { 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 * Set the auth token getter function\n */\n setAuthTokenGetter(getAuthToken: () => Promise<string | null>): void {\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 console.debug(\"WebSocket auto-authenticating after token getter set\");\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) console.debug(\"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) console.debug(\"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.onlineListener && typeof window !== \"undefined\") {\n window.removeEventListener(\"online\", this.onlineListener);\n this.onlineListener = null;\n }\n this.isAuthenticated = false;\n this.authPromise = null;\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\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(this.websocketUrl);\n this.ws = socket;\n\n this.ws!.onopen = async () => {\n console.debug(\"Connected to PostgreSQL backend\");\n const wasReconnect = this.reconnectAttempts > 0;\n this.isConnected = true;\n this.reconnectAttempts = 0;\n\n // Auto-authenticate if token getter is available\n if (this.getAuthToken && !this.isAuthenticated) {\n try {\n const token = await this.getAuthToken();\n if (token) {\n await this.authenticate(token);\n console.debug(\"WebSocket auto-authenticated\");\n }\n } catch (error) {\n // User not logged in or auth still loading — this is expected.\n // Authentication will happen on-demand when the user logs in.\n console.debug(\"WebSocket connected without auth:\", (error as Error)?.message || error);\n }\n }\n\n this.emit(wasReconnect ? \"reconnect\" : \"connect\");\n this.processMessageQueue();\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 try {\n const message = JSON.parse(event.data, rebaseReviver);\n this.handleWebSocketMessage(message);\n } catch (error) {\n console.error(\"Error parsing WebSocket message:\", error);\n }\n };\n\n this.ws!.onclose = () => {\n console.debug(\"Disconnected from PostgreSQL backend\");\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 // Re-queue pending requests so the UI doesn't hang indefinitely or crash\n for (const [reqId, request] of this.pendingRequests.entries()) {\n if (reqId.startsWith(\"auth_\")) {\n request.reject(new Error(\"Connection closed during authentication\"));\n } else if (request.message) {\n request.message._queuedResolve = request.resolve;\n request.message._queuedReject = request.reject;\n this.messageQueue.push(request.message);\n } else {\n request.reject(new RebaseApiError(\"Connection closed\"));\n }\n this.pendingRequests.delete(reqId);\n }\n\n this.attemptReconnect();\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 private processMessageQueue() {\n while (this.messageQueue.length > 0 && this.isConnected) {\n const message = this.messageQueue.shift();\n if (message) this.sendMessage(message);\n }\n }\n\n private attemptReconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.error(\"Max reconnection attempts reached\");\n // Nothing will re-subscribe now, so stop every subscription that\n // never loaded from spinning forever.\n this.gaveUp = true;\n this.failAllPendingSubscriptions(\n new RebaseApiError(\"Connection lost\", { code: \"CONNECTION_LOST\" })\n );\n return;\n }\n\n this.reconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n\n console.debug(`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 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 | FetchOneProps;\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 { errorMessage, errorCode } = extractMessageError(message);\n const error = new RebaseApiError(errorMessage, { code: errorCode });\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 // 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 const { errorMessage, errorCode } = extractMessageError(message);\n pendingReq.reject(new RebaseApiError(errorMessage, { code: errorCode }));\n }\n }).catch(err => {\n pendingReq.reject(err);\n });\n } else {\n this.pendingRequests.delete(requestId);\n const { errorMessage, errorCode } = extractMessageError(message);\n pendingReq.reject(new RebaseApiError(errorMessage, { code: errorCode }));\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 (typeof message.channel === \"string\" &&\n (type === \"broadcast\" || type === \"presence_state\" || type === \"presence_diff\" || type === \"channel_history\")) {\n const handlers = this.channelHandlers.get(message.channel);\n if (handlers) {\n for (const handler of [...handlers]) {\n try {\n handler(message as unknown as Record<string, unknown>);\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 && type === \"collection_update\") {\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 unknown 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 = (message as unknown as { pks?: PrimaryKeyInfo[] }).pks;\n if (updatePks) collectionSub.pks = updatePks;\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);\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 // Handle instant row-level patches for collection subscriptions.\n // These arrive before the full refetch and give immediate cross-tab feedback.\n if (subscriptionId && type === \"collection_patch\") {\n const subscriptionKey = this.backendToCollectionKey.get(subscriptionId);\n if (subscriptionKey) {\n const collectionSub = this.collectionSubscriptions.get(subscriptionKey);\n if (collectionSub && collectionSub.isInitialDataReceived && collectionSub.latestData) {\n const patchWireEntity = message.row ?? null;\n const patchMessage = message as unknown as { id: string; pks?: PrimaryKeyInfo[] };\n const patchEntityId = patchMessage.id;\n // The server knows the key columns; remember them, because the\n // refetch reconciliation needs them too and carries no id.\n if (patchMessage.pks) collectionSub.pks = patchMessage.pks;\n const patchRow = patchWireEntity ? (patchWireEntity as unknown as Record<string, unknown>) : null;\n let updated: Record<string, unknown>[];\n\n if (patchRow === null) {\n // Row was deleted — remove it from the cached list\n updated = collectionSub.latestData.filter(\n e => this.rowAddress(e, collectionSub.pks) !== String(patchEntityId)\n );\n } else {\n // Row was created or updated — merge into the cached list.\n // Matched against the patch's own address rather than\n // anything read off the row: `patchRow.id` is undefined\n // for a table not keyed on `id`, so every update looked\n // like a new row and was prepended as a duplicate.\n const idx = collectionSub.latestData.findIndex(\n e => this.rowAddress(e, collectionSub.pks) === String(patchEntityId)\n );\n if (idx >= 0) {\n // Update in place (preserve array position)\n updated = [...collectionSub.latestData];\n updated[idx] = patchRow;\n } else {\n // New row — prepend (most recently created first)\n updated = [patchRow, ...collectionSub.latestData];\n }\n }\n\n collectionSub.latestData = updated;\n collectionSub.lastUpdated = Date.now();\n\n // Fire all callbacks with the patched data\n collectionSub.callbacks.forEach(callback => {\n try {\n callback.onUpdate(updated);\n } catch (error) {\n console.error(\"Error in collection patch 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 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 const row = wireEntity ? (wireEntity as unknown as Record<string, unknown>) : 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 if (subscriptionId && (type === \"ERROR\" || message.error)) {\n const collectionKey = this.backendToCollectionKey.get(subscriptionId);\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 { errorMessage, errorCode } = extractMessageError(message);\n const error = new RebaseApiError(errorMessage, { code: errorCode });\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(subscriptionId);\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 { errorMessage, errorCode } = extractMessageError(message);\n const error = new RebaseApiError(errorMessage, { code: errorCode });\n entitySub.callbacks.forEach(callback => {\n if (callback.onError) {\n callback.onError(error);\n }\n });\n return;\n }\n }\n }\n\n // Legacy subscription handling (for backward compatibility)\n if (subscriptionId && this.subscriptions.has(subscriptionId)) {\n const callback = this.subscriptions.get(subscriptionId);\n if (!callback) {\n throw new Error(`Subscription callback not found for subscriptionId: ${subscriptionId}`);\n }\n if (message.type === \"ERROR\" || message.error) {\n if (callback.onError) {\n const { errorMessage, errorCode } = extractMessageError(message);\n callback.onError(new RebaseApiError(errorMessage, { code: errorCode }));\n }\n } else {\n callback.onUpdate(message);\n }\n return;\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 do:\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. A\n // console warning is the floor, not the answer: an `onError` on\n // `RebaseRealtimeChannel` is the shape this should eventually take.\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 try {\n const token = await this.getAuthToken!();\n if (!token) throw new Error(\"user not logged in\");\n await this.authenticate(token);\n console.debug(\"WebSocket authenticated on demand\");\n return; // Success\n } catch (error: unknown) {\n lastError = 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 console.debug(`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 console.debug(\"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 // If already has a requestId (re-sending from queue), use the stored promise handlers\n const queuedMsg = message as Record<string, unknown> & { _queuedResolve?: (p: unknown) => void; _queuedReject?: (p: Error) => void };\n if (queuedMsg._queuedResolve && queuedMsg._queuedReject) {\n return this.doSendMessage(message, queuedMsg._queuedResolve, queuedMsg._queuedReject);\n }\n\n if (!this.isConnected || !this.ws) {\n // The queue is only ever drained by a socket opening, so something\n // has to open one. Before lazy connect this was guaranteed by the\n // constructor; now the first frame is what asks for it.\n this.ensureConnected();\n // Queue the message and return a promise that will be resolved when actually sent\n return new Promise<unknown>((resolve, reject) => {\n const queueable = message as Record<string, unknown> & { _queuedResolve?: (p: unknown) => void; _queuedReject?: (p: Error) => void };\n queueable._queuedResolve = resolve;\n queueable._queuedReject = reject;\n this.messageQueue.push(message);\n });\n }\n\n return new Promise<unknown>((resolve, reject) => {\n this.doSendMessage(message, resolve, reject);\n });\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 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 const errorMessage = error instanceof Error ? error.message : \"Authentication required\";\n reject(new RebaseApiError(errorMessage));\n return;\n }\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 = !(\n message.type === \"subscribe_collection\"\n || message.type === \"subscribe_one\"\n || message.type === \"unsubscribe\"\n || CHANNEL_MESSAGE_TYPES.has(message.type as string)\n );\n\n if (expectsResponse && !this.pendingRequests.has(requestId)) {\n const timeoutHandle = setTimeout(() => {\n if (this.pendingRequests.has(requestId)) {\n this.pendingRequests.delete(requestId);\n reject(new RebaseApiError(\"Request timed out\"));\n }\n }, this.requestTimeoutMs);\n\n this.pendingRequests.set(requestId, {\n resolve: (value: unknown) => {\n clearTimeout(timeoutHandle);\n resolve(value);\n },\n reject: (error: Error) => {\n clearTimeout(timeoutHandle);\n reject(error);\n },\n message: message as Record<string, unknown> & { _queuedResolve?: (p: unknown) => void; _queuedReject?: (p: Error) => void }\n });\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 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 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 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 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 for structural equality.\n * Handles primitives, null, undefined, Date, RegExp, arrays, and plain objects.\n */\n private deepEqual(a: unknown, b: unknown): 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])) return false;\n }\n return true;\n }\n\n // Plain object comparison\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n\n if (aKeys.length !== bKeys.length) return false;\n\n for (const key of aKeys) {\n if (!Object.prototype.hasOwnProperty.call(bObj, key)) return false;\n if (!this.deepEqual(aObj[key], bObj[key])) return false;\n }\n\n return true;\n }\n\n private normalizeForComparison(val: unknown): unknown {\n if (!val) return val;\n\n if (Array.isArray(val)) {\n return val.map(item => this.normalizeForComparison(item));\n }\n\n if (typeof val === \"object\") {\n if (val instanceof Date) return val;\n if (val instanceof RegExp) return val;\n\n const obj = val as Record<string, unknown>;\n if (obj.__type === \"relation\") {\n // `data` is dropped on purpose: a relation compares by the\n // reference it holds, not by the row it happens to have loaded.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { data, ...rest } = obj;\n return rest;\n }\n\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n result[k] = this.normalizeForComparison(v);\n }\n return result;\n }\n\n return val;\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 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 const normCached = this.normalizeForComparison(cachedRow) as Record<string, unknown>;\n const normIncoming = this.normalizeForComparison(incomingRow) as Record<string, unknown>;\n\n if (this.deepEqual(normCached, normIncoming)) {\n return cachedRow;\n } else {\n // Deep debug: Why did it fail?\n const mismatches: Record<string, { cached: unknown, incoming: unknown }> = {};\n const allKeys = new Set([...Object.keys(normCached), ...Object.keys(normIncoming)]);\n for (const key of allKeys) {\n if (!this.deepEqual(normCached[key], normIncoming[key])) {\n mismatches[key] = { cached: normCached[key],\nincoming: normIncoming[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 props: FetchCollectionProps<M>,\n onUpdate: (rows: Record<string, unknown>[]) => 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>[]) => void;\n onError?: (error: Error) => void;\n }>;\n callbackMap.set(callbackId, { onUpdate,\nonError });\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 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>[]) => void;\n onError?: (error: Error) => void;\n }>();\n callbackMap.set(callbackId, { onUpdate,\nonError });\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: FetchOneProps<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\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\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 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 payload: {\n ...subscription.props,\n subscriptionId: backendSubscriptionId\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 * 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 * Fail every subscription that never received data. Called when reconnection\n * is given up on, so views surface an error instead of spinning forever.\n */\n private failAllPendingSubscriptions(error: Error): void {\n for (const key of [...this.collectionSubscriptions.keys()]) {\n const sub = this.collectionSubscriptions.get(key);\n if (sub && !sub.isInitialDataReceived) this.failCollectionSubscription(key, error);\n }\n for (const key of [...this.singleSubscriptions.keys()]) {\n const sub = this.singleSubscriptions.get(key);\n if (sub && !sub.isInitialDataReceived) this.failEntitySubscription(key, error);\n }\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 console.debug(`[WS] Re-subscribing: ${this.collectionSubscriptions.size} collection(s), ${this.singleSubscriptions.size} row(ies)`);\n\n // Re-subscribe collection subscriptions\n for (const [key, sub] of this.collectionSubscriptions.entries()) {\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 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: FetchCollectionProps): 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: FetchOneProps): string {\n return `${props.path}|${props.id}`;\n }\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/** Presence state keyed by the server's client id. */\nexport type PresenceState = Record<string, Record<string, unknown>>;\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 type { ChannelHistoryEntry } from \"@rebasepro/types\";\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: Record<string, unknown>) => void): () => void;\n onReconnect(handler: () => void): () => void;\n}\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 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: BroadcastEvent[] = [];\n private catchUpInFlight = false;\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 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\n if (this.catchUpTimeout) clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = setTimeout(() => this.abandonCatchUp(), CATCH_UP_TIMEOUT_MS);\n (this.catchUpTimeout as unknown as { unref?: () => void }).unref?.();\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 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 (this.heartbeat as unknown as { unref?: () => void }).unref?.();\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 /** Send a broadcast. The sender does not receive its own message. */\n async broadcast(event: string, payload: unknown): Promise<void> {\n await this.join();\n await this.send(\"broadcast\", { event, payload });\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) this.lastSeq = options.sinceSeq;\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.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: Record<string, unknown>): void {\n switch (message.type) {\n case \"presence_state\": {\n this.presences = (message.presences as PresenceState) ?? {};\n this.emitPresence();\n break;\n }\n case \"presence_diff\": {\n const joins = (message.joins as PresenceState) ?? {};\n const leaves = (message.leaves as PresenceState) ?? {};\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 = typeof message.seq === \"number\" ? message.seq : undefined;\n const event: BroadcastEvent = {\n event: message.event as string,\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 this.lastSeq = seq;\n this.deliver(event);\n break;\n }\n case \"channel_history\": {\n this.catchUpInFlight = false;\n if (this.catchUpTimeout) {\n clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = null;\n }\n\n const entries = (message.messages as ChannelHistoryEntry[] | undefined) ?? [];\n const retained = message.retained === true;\n const latestSeq = typeof message.latestSeq === \"number\" ? message.latestSeq : undefined;\n\n for (const resolve of this.historyWaiters.splice(0)) {\n resolve({ messages: entries, retained, latestSeq });\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 for (const entry of entries) {\n if (entry.seq <= this.lastSeq) continue;\n this.lastSeq = entry.seq;\n this.deliver({\n event: entry.event,\n payload: entry.payload,\n seq: entry.seq,\n replayed: true\n });\n }\n\n this.flushPendingLive();\n break;\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 }\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 { EntityReference, EntityRelation, GeoPoint, Vector } from \"@rebasepro/types\";\nimport { rebaseReviver } from \"./reviver\";\n\n/**\n * Lossless round-tripping of rows through the offline store.\n *\n * Both persistence backends move values by structured clone, which keeps\n * `Date` but flattens every class instance to a plain object. For\n * `EntityReference`/`EntityRelation` that is harmless — they carry their own\n * `__type` discriminator, so the JSON reviver can rebuild them — but\n * `GeoPoint` and `Vector` do not, and would come back out of the cache as\n * anonymous `{ latitude, longitude }` / `{ value }` bags. A row read from the\n * cache must be indistinguishable from the same row read from the network, so\n * those two are tagged on the way in and revived on the way out.\n *\n * Type tests here are structural rather than `instanceof`, because a structured\n * clone can arrive from another realm — an iframe, a worker, or the polyfill\n * the tests run against — where the constructor identity differs but the value\n * is the real thing. Only *plain* objects are walked; anything else is passed\n * through whole, so a class instance is never quietly reduced to `{}`.\n */\n\nfunction isDate(value: unknown): value is Date {\n return Object.prototype.toString.call(value) === \"[object Date]\";\n}\n\n/** An object literal — not a Date, RegExp, Map, or any class instance. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n const proto = Object.getPrototypeOf(value) as { constructor?: { name?: string } } | null;\n if (proto === null || proto === Object.prototype) return true;\n // A literal cloned out of another realm has a different `Object.prototype`\n // but is still, in every way that matters here, a plain object.\n return proto.constructor?.name === \"Object\";\n}\n\nfunction dehydrateValue(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (value instanceof GeoPoint) {\n return { __type: \"GeoPoint\", latitude: value.latitude, longitude: value.longitude };\n }\n if (value instanceof Vector) return { __type: \"Vector\", value: [...value.value] };\n // EntityReference/EntityRelation already serialize themselves via `__type`\n // own properties, so a structured clone is enough for the reviver below.\n if (value instanceof EntityReference || value instanceof EntityRelation) return value;\n if (Array.isArray(value)) return value.map(dehydrateValue);\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value)) out[key] = dehydrateValue(inner);\n return out;\n }\n return value;\n}\n\nfunction hydrateValue(value: unknown): unknown {\n if (value === null || value === undefined || isDate(value)) return value;\n if (Array.isArray(value)) return value.map(hydrateValue);\n if (typeof value === \"object\") {\n const revived = rebaseReviver(\"\", value);\n // The reviver recognised it — hand back the class instance untouched\n // rather than walking into its (now private) internals.\n if (revived !== value) return revived;\n if (!isPlainObject(value)) return value;\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value)) out[key] = hydrateValue(inner);\n return out;\n }\n return value;\n}\n\n/** Prepare a row for the store. */\nexport function dehydrateRow<T extends Record<string, unknown>>(row: T): Record<string, unknown> {\n return dehydrateValue(row) as Record<string, unknown>;\n}\n\n/** Restore a row read back from the store. */\nexport function hydrateRow<T extends Record<string, unknown>>(row: Record<string, unknown>): T {\n return hydrateValue(row) as T;\n}\n","import { RebaseApiError } from \"./transport\";\n\n/**\n * Whether the network is worth trying, and when to try again after it wasn't.\n *\n * `navigator.onLine` is necessary but not sufficient: it reports the state of\n * the network interface, so it stays `true` behind a captive portal, on a\n * connection that resolves DNS but reaches nothing, and while the API itself\n * is down. This tracks what actually happened to requests as well, so the\n * first failure is the only one an app pays for — everything after it inside\n * the backoff window skips the doomed round trip and answers from the local\n * store immediately, which is the difference between an app that freezes when\n * the wifi drops and one that does not.\n */\n\n/** The request never reached the server, so nothing was decided by it. */\nexport function isNetworkError(error: unknown): boolean {\n if (error instanceof RebaseApiError) {\n // A 0 status is what a transport reports when it has no response at all.\n return error.status === 0;\n }\n // fetch rejects with TypeError on network failure in every runtime we\n // support (browsers: \"Failed to fetch\"/\"Load failed\"; undici: \"fetch\n // failed\").\n if (error instanceof TypeError) return true;\n const name = (error as { name?: string } | undefined)?.name;\n // AbortError covers both an explicit abort and a fetch timeout; the others\n // are what Node and Safari surface for a dropped connection.\n return name === \"AbortError\" || name === \"TimeoutError\" || name === \"NetworkError\";\n}\n\n/**\n * Statuses that mean \"not now\" rather than \"not ever\": a queued write that\n * gets one of these is worth replaying, while a 400 or a 403 never will be.\n * 500 is deliberately absent — an unhandled server error is far more often a\n * bug the same payload will hit again than a blip, and retrying it forever\n * jams every write behind it.\n */\nconst RETRYABLE_STATUSES = new Set([408, 425, 429, 502, 503, 504]);\n\n/**\n * The server holds this key for a request it has not answered yet.\n *\n * It is a 409 like a duplicate row is a 409, and nothing but the code separates\n * them — one means \"your write is already there\", the other means \"your write\n * may not have happened at all, ask again\".\n */\nconst IDEMPOTENCY_IN_PROGRESS = \"IDEMPOTENCY_KEY_IN_PROGRESS\";\n\n/**\n * Is the server still answering an earlier attempt of this same write?\n *\n * The only correct response is to ask again — which is exactly what the\n * server's own message says, and exactly what this SDK used not to do.\n */\nexport function isIdempotencyInProgressError(error: unknown): boolean {\n return error instanceof RebaseApiError\n && error.status === 409\n && error.code === IDEMPOTENCY_IN_PROGRESS;\n}\n\n/** Is this failure worth another attempt later? */\nexport function isRetryableError(error: unknown): boolean {\n if (isNetworkError(error)) return true;\n if (!(error instanceof RebaseApiError)) return false;\n // The one 409 that resolves on its own. A key whose claim outlived the\n // request that took it — the process was killed between the write and the\n // answer — is refused until the claim's lease expires, and giving up on it\n // means dropping a write that retrying would have completed.\n if (isIdempotencyInProgressError(error)) return true;\n return error.status !== undefined && RETRYABLE_STATUSES.has(error.status);\n}\n\n/**\n * Did this write fail because the row is already there?\n *\n * Matched on the SQLSTATE the server passes through (`23505`, unique_violation)\n * and on 409, never on the message — a duplicate-key message names the\n * constraint and the values, so it is neither stable nor safe to parse.\n *\n * The queue uses this to recognise its own earlier attempt. A create whose\n * response was lost is replayed, and for a row carrying an id the SDK generated\n * the server can only be rejecting it because the first attempt actually landed.\n *\n * Which is why the status alone cannot decide it: `IDEMPOTENCY_KEY_IN_PROGRESS`\n * is a 409 that means the opposite — the row may not exist at all. Read as a\n * duplicate, the queue looked for a row that was never written, found nothing,\n * concluded there was nothing left to do and deleted the write from the queue.\n */\nexport function isDuplicateKeyError(error: unknown): boolean {\n if (!(error instanceof RebaseApiError)) return false;\n if (error.code === \"23505\") return true;\n return error.status === 409 && !isIdempotencyInProgressError(error);\n}\n\nexport interface ConnectivityOptions {\n /** First retry delay after a failure. Defaults to 1 000 ms. */\n initialBackoffMs?: number;\n /** Ceiling for the doubling retry delay. Defaults to 60 000 ms. */\n maxBackoffMs?: number;\n /**\n * Let a known-failed connection suppress further attempts until the\n * backoff window opens. On by default — it is what makes a read or write\n * during an outage instant instead of a timeout. Turn it off when nothing\n * will ever wake the client up again (no retry timer, no `online` event),\n * where suppressing attempts would mean never recovering.\n */\n respectBackoff?: boolean;\n /** Injected for tests. */\n now?: () => number;\n /** Injected for tests; must return a handle `clearTimeout` accepts. */\n setTimer?: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;\n clearTimer?: (handle: ReturnType<typeof setTimeout>) => void;\n}\n\nexport class ConnectivityMonitor {\n private state: \"online\" | \"offline\" = \"online\";\n private backoffMs: number;\n private readonly initialBackoffMs: number;\n private readonly maxBackoffMs: number;\n private retryAt = 0;\n private timer?: ReturnType<typeof setTimeout>;\n private listeners = new Set<(online: boolean) => void>();\n private readonly respectBackoff: boolean;\n private readonly now: () => number;\n private readonly setTimer: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;\n private readonly clearTimer: (handle: ReturnType<typeof setTimeout>) => void;\n /** Called when the backoff window expires, to drive an automatic retry. */\n onRetryDue?: () => void;\n\n private readonly handleOnline = () => {\n // The OS says the interface is back. Trust it enough to try\n // immediately rather than sitting out the rest of the backoff — and to\n // say so, or a client with nothing queued would have no request whose\n // success could ever flip the badge back to \"online\".\n this.retryAt = 0;\n this.backoffMs = this.initialBackoffMs;\n this.clearPendingTimer();\n this.setState(\"online\");\n this.onRetryDue?.();\n };\n private readonly handleOffline = () => {\n this.setState(\"offline\");\n };\n\n constructor(options: ConnectivityOptions = {}) {\n this.initialBackoffMs = options.initialBackoffMs ?? 1_000;\n this.maxBackoffMs = Math.max(this.initialBackoffMs, options.maxBackoffMs ?? 60_000);\n this.backoffMs = this.initialBackoffMs;\n this.respectBackoff = options.respectBackoff ?? true;\n this.now = options.now ?? (() => Date.now());\n this.setTimer = options.setTimer ?? ((fn, ms) => setTimeout(fn, ms));\n this.clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));\n\n if (typeof window !== \"undefined\" && typeof window.addEventListener === \"function\") {\n window.addEventListener(\"online\", this.handleOnline);\n window.addEventListener(\"offline\", this.handleOffline);\n }\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) {\n this.state = \"offline\";\n }\n }\n\n /** What the app should be told: are we connected? */\n isOnline(): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return false;\n return this.state === \"online\";\n }\n\n /**\n * Should this request even be sent? False means \"answer from the local\n * store instead\" — the request would only burn a timeout to reach the same\n * conclusion the last one already did.\n */\n shouldAttempt(): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return false;\n if (this.state === \"online\" || !this.respectBackoff) return true;\n // Exactly one request is let through when the window opens; it is the\n // probe whose outcome decides whether we are back.\n return this.now() >= this.retryAt;\n }\n\n /** A request reached the server. */\n markSuccess(): void {\n this.backoffMs = this.initialBackoffMs;\n this.retryAt = 0;\n this.clearPendingTimer();\n this.setState(\"online\");\n }\n\n /** A request did not reach the server: we are offline until proven otherwise. */\n markFailure(): void {\n this.deferRetry();\n this.setState(\"offline\");\n }\n\n /**\n * Back off and try again later without claiming the connection is gone.\n * This is what a 429 or a 503 deserves — the server answered, so the app\n * is demonstrably online; it just should not hammer.\n */\n deferRetry(): void {\n const jitter = 0.8 + Math.random() * 0.4;\n this.retryAt = this.now() + this.backoffMs * jitter;\n const delay = Math.max(0, this.retryAt - this.now());\n this.backoffMs = Math.min(this.maxBackoffMs, this.backoffMs * 2);\n this.scheduleRetry(delay);\n }\n\n /** Milliseconds until the next attempt is allowed; 0 when one is allowed now. */\n msUntilRetry(): number {\n if (this.state === \"online\") return 0;\n return Math.max(0, this.retryAt - this.now());\n }\n\n onChange(listener: (online: boolean) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n dispose(): void {\n if (typeof window !== \"undefined\" && typeof window.removeEventListener === \"function\") {\n window.removeEventListener(\"online\", this.handleOnline);\n window.removeEventListener(\"offline\", this.handleOffline);\n }\n this.clearPendingTimer();\n this.listeners.clear();\n this.onRetryDue = undefined;\n }\n\n private scheduleRetry(delay: number): void {\n this.clearPendingTimer();\n if (!this.onRetryDue) return;\n this.timer = this.setTimer(() => {\n this.timer = undefined;\n this.onRetryDue?.();\n }, delay);\n // A retry timer must never be the reason a Node script refuses to exit.\n (this.timer as unknown as { unref?: () => void }).unref?.();\n }\n\n private clearPendingTimer(): void {\n if (this.timer !== undefined) {\n this.clearTimer(this.timer);\n this.timer = undefined;\n }\n }\n\n private setState(next: \"online\" | \"offline\"): void {\n if (this.state === next) return;\n this.state = next;\n const online = this.isOnline();\n for (const listener of this.listeners) listener(online);\n }\n}\n","/**\n * Persistence backends for the SDK's offline support.\n *\n * The store is a dumb, namespaced key/value surface with two areas: a read\n * cache (normalized rows, query snapshots and sync bookkeeping) and a mutation\n * queue (local writes waiting to reach the server). All structure — per-user\n * prefixes, the `row|`/`q|`/`meta|` namespaces, mutation ordering — is owned by\n * the {@link OfflineManager}; the store only promises that a prefix listing\n * comes back in lexicographic key order, which is what makes the queue a FIFO.\n *\n * Two implementations ship with the SDK:\n * - {@link IndexedDBOfflineStore} — the browser default; survives reloads.\n * - {@link MemoryOfflineStore} — the fallback everywhere IndexedDB does not\n * exist (Node, React Native, tests); survives only the process.\n *\n * Environments with neither (React Native + AsyncStorage, Electron main, …)\n * implement this interface and pass it via `offline.store`.\n */\n\n/** A cached value plus the moment it was written, for LRU eviction. */\nexport interface OfflineCacheEntry {\n value: unknown;\n cachedAt: number;\n}\n\n/** A cache entry with its key, as returned by prefix listings. */\nexport interface OfflineCacheRecord extends OfflineCacheEntry {\n key: string;\n}\n\n/** What a mutation has to put back if the server rejects it. */\nexport interface MutationRollback {\n /**\n * The rows as they were locally *before* this mutation was applied, keyed\n * by id. A `null` value means \"the row did not exist\" — restoring it is a\n * delete, not a write.\n */\n rows: Record<string, Record<string, unknown> | null>;\n}\n\n/**\n * A local write waiting to be replayed against the server.\n *\n * `mutationId` orders the queue globally (not per collection): a create in one\n * collection may be the parent a later insert in another references, so replay\n * must preserve the order the app issued the writes in. It is lexicographically\n * time-ordered and carries a random suffix, so two browser tabs writing in the\n * same millisecond produce distinct, still-roughly-ordered ids instead of\n * silently overwriting each other's queue entry.\n */\nexport interface PendingMutation {\n /** Unique, lexicographically sortable identity — also the queue key suffix. */\n mutationId: string;\n collection: string;\n type: \"create\" | \"createMany\" | \"update\" | \"updateMany\" | \"delete\" | \"deleteMany\";\n /** Target row id for update/delete, and the (client-generated) id of an offline create. */\n id?: string | number;\n /** Target row ids for `deleteMany`. */\n ids?: (string | number)[];\n /** `{ id, data }` entries for `updateMany`. */\n updates?: { id: string | number; data: Record<string, unknown> }[];\n /**\n * True when the SDK minted this create's id itself. Only such creates may\n * cancel out against a later offline delete: a freshly generated UUID\n * cannot name a row the server already has, while a caller-supplied id\n * can — and there the delete must still replay to remove the server row.\n */\n generatedId?: boolean;\n /** The payload: a row for create/update, an array of rows for createMany. */\n data?: Record<string, unknown> | Record<string, unknown>[];\n upsert?: boolean;\n queuedAt: number;\n /** How many times replay has been attempted (diagnostics for a stuck queue). */\n attempts?: number;\n /** The last replay failure's message, when there was one. */\n lastError?: string;\n /** Local state to restore if the server rejects this mutation. */\n rollback?: MutationRollback;\n}\n\nexport interface OfflineStore {\n getCache(key: string): Promise<OfflineCacheEntry | undefined>;\n setCache(key: string, entry: OfflineCacheEntry): Promise<void>;\n /** Write many entries at once — one transaction where the backend has them. */\n setCacheMany(entries: { key: string; entry: OfflineCacheEntry }[]): Promise<void>;\n deleteCache(keys: string[]): Promise<void>;\n /** Every cache key starting with `prefix`, with its write time (for eviction). */\n listCache(prefix: string): Promise<{ key: string; cachedAt: number }[]>;\n /** As {@link listCache}, but with the values — the local query engine's input. */\n listCacheEntries(prefix: string): Promise<OfflineCacheRecord[]>;\n\n enqueue(key: string, mutation: PendingMutation): Promise<void>;\n dequeue(key: string): Promise<void>;\n /** Queued mutations whose key starts with `prefix`, in lexicographic key order. */\n listQueue(prefix: string): Promise<PendingMutation[]>;\n\n /** Remove every cache entry and queued mutation whose key starts with `prefix`. */\n clear(prefix: string): Promise<void>;\n}\n\n// ─── Mutation ids ────────────────────────────────────────────────────────────\n\n/**\n * Monotonic within a tab, unique across tabs, and sortable as a plain string:\n * `<ms base36, padded>-<counter>-<random>`. The padding is what keeps\n * lexicographic order equal to chronological order, and the random suffix is\n * what stops two tabs from writing the same queue key in the same millisecond\n * — which would silently drop one of the two writes.\n */\nlet mutationCounter = 0;\nexport function createMutationId(now: number = Date.now()): string {\n const time = now.toString(36).padStart(10, \"0\");\n const counter = (mutationCounter = (mutationCounter + 1) % 1_679_616).toString(36).padStart(4, \"0\");\n const random = Math.random().toString(36).slice(2, 10).padStart(8, \"0\");\n return `${time}-${counter}-${random}`;\n}\n\n// ─── Memory ──────────────────────────────────────────────────────────────────\n\n/**\n * In-memory store: the default outside the browser and the workhorse of the\n * test suite. Values are deep-copied on the way in and out so a caller\n * mutating a returned row cannot silently edit the \"persisted\" copy — the\n * IndexedDB implementation gets the same guarantee for free from structured\n * cloning, and the two must not differ in aliasing behaviour.\n */\nexport class MemoryOfflineStore implements OfflineStore {\n private cache = new Map<string, OfflineCacheEntry>();\n private queue = new Map<string, PendingMutation>();\n\n async getCache(key: string): Promise<OfflineCacheEntry | undefined> {\n const entry = this.cache.get(key);\n return entry ? structuredClone(entry) : undefined;\n }\n\n async setCache(key: string, entry: OfflineCacheEntry): Promise<void> {\n this.cache.set(key, structuredClone(entry));\n }\n\n async setCacheMany(entries: { key: string; entry: OfflineCacheEntry }[]): Promise<void> {\n for (const { key, entry } of entries) this.cache.set(key, structuredClone(entry));\n }\n\n async deleteCache(keys: string[]): Promise<void> {\n for (const key of keys) this.cache.delete(key);\n }\n\n async listCache(prefix: string): Promise<{ key: string; cachedAt: number }[]> {\n const out: { key: string; cachedAt: number }[] = [];\n for (const [key, entry] of this.cache) {\n if (key.startsWith(prefix)) out.push({ key, cachedAt: entry.cachedAt });\n }\n return out;\n }\n\n async listCacheEntries(prefix: string): Promise<OfflineCacheRecord[]> {\n const out: OfflineCacheRecord[] = [];\n for (const [key, entry] of this.cache) {\n if (key.startsWith(prefix)) out.push({ key, ...structuredClone(entry) });\n }\n out.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n return out;\n }\n\n async enqueue(key: string, mutation: PendingMutation): Promise<void> {\n this.queue.set(key, structuredClone(mutation));\n }\n\n async dequeue(key: string): Promise<void> {\n this.queue.delete(key);\n }\n\n async listQueue(prefix: string): Promise<PendingMutation[]> {\n return [...this.queue.entries()]\n .filter(([key]) => key.startsWith(prefix))\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([, mutation]) => structuredClone(mutation));\n }\n\n async clear(prefix: string): Promise<void> {\n for (const key of [...this.cache.keys()]) {\n if (key.startsWith(prefix)) this.cache.delete(key);\n }\n for (const key of [...this.queue.keys()]) {\n if (key.startsWith(prefix)) this.queue.delete(key);\n }\n }\n}\n\n// ─── IndexedDB ───────────────────────────────────────────────────────────────\n\nconst IDB_NAME = \"rebase-offline\";\n/**\n * v2 introduced the normalized row cache and string mutation ids. A v1\n * database holds whole-response blobs under keys this version cannot read and\n * queue entries ordered by a numeric `seq` this version no longer writes, so\n * the upgrade drops both stores rather than trying to translate them. Offline\n * support had not shipped in a release when v2 landed, so nothing in the wild\n * loses a queued write to this.\n */\nconst IDB_VERSION = 2;\nconst CACHE_STORE = \"cache\";\nconst QUEUE_STORE = \"queue\";\n\n/** The exclusive upper bound of an IDBKeyRange covering every key under `prefix`. */\nfunction prefixRange(prefix: string): IDBKeyRange {\n return IDBKeyRange.bound(prefix, prefix + \"￿\", false, false);\n}\n\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB request failed\"));\n });\n}\n\n/** Resolve when the whole transaction commits, not just when the last request returns. */\nfunction transactionDone(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onabort = tx.onerror = () => reject(tx.error ?? new Error(\"IndexedDB transaction failed\"));\n });\n}\n\n/**\n * IndexedDB-backed store — the browser default, so cached rows and queued\n * writes survive a reload or a browser restart. Everything lives in one\n * database with two object stores; keys are the manager's full prefixed\n * strings, so multiple users (scopes) share the database without ever\n * sharing entries.\n */\nexport class IndexedDBOfflineStore implements OfflineStore {\n private dbPromise?: Promise<IDBDatabase>;\n\n private open(): Promise<IDBDatabase> {\n if (!this.dbPromise) {\n this.dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(IDB_NAME, IDB_VERSION);\n request.onupgradeneeded = (event) => {\n const db = request.result;\n // A v1 database speaks a key layout this version cannot\n // read; keeping it would surface as corrupt cache entries\n // and un-replayable mutations. Start clean instead.\n if (event.oldVersion > 0 && event.oldVersion < 2) {\n if (db.objectStoreNames.contains(CACHE_STORE)) db.deleteObjectStore(CACHE_STORE);\n if (db.objectStoreNames.contains(QUEUE_STORE)) db.deleteObjectStore(QUEUE_STORE);\n }\n if (!db.objectStoreNames.contains(CACHE_STORE)) db.createObjectStore(CACHE_STORE);\n if (!db.objectStoreNames.contains(QUEUE_STORE)) db.createObjectStore(QUEUE_STORE);\n };\n request.onsuccess = () => {\n const db = request.result;\n // Another tab asking for a newer version needs this\n // connection out of the way, or its upgrade blocks forever.\n db.onversionchange = () => {\n db.close();\n this.dbPromise = undefined;\n };\n resolve(db);\n };\n // Reset so a transient failure (private browsing quota, a\n // version race with another tab) can be retried instead of\n // poisoning every later call with the same rejection.\n request.onerror = () => {\n this.dbPromise = undefined;\n reject(request.error ?? new Error(\"Failed to open IndexedDB\"));\n };\n request.onblocked = () => {\n this.dbPromise = undefined;\n reject(new Error(\"IndexedDB upgrade blocked by another tab\"));\n };\n });\n }\n return this.dbPromise;\n }\n\n private async store(name: string, mode: IDBTransactionMode): Promise<IDBObjectStore> {\n const db = await this.open();\n return db.transaction(name, mode).objectStore(name);\n }\n\n async getCache(key: string): Promise<OfflineCacheEntry | undefined> {\n const store = await this.store(CACHE_STORE, \"readonly\");\n const entry = await requestToPromise(store.get(key));\n return entry as OfflineCacheEntry | undefined;\n }\n\n async setCache(key: string, entry: OfflineCacheEntry): Promise<void> {\n const store = await this.store(CACHE_STORE, \"readwrite\");\n await requestToPromise(store.put(entry, key));\n }\n\n async setCacheMany(entries: { key: string; entry: OfflineCacheEntry }[]): Promise<void> {\n if (entries.length === 0) return;\n const store = await this.store(CACHE_STORE, \"readwrite\");\n for (const { key, entry } of entries) store.put(entry, key);\n // One commit for the whole batch: a `find` writing 200 rows must not\n // be 200 round-trips through the transaction queue.\n await transactionDone(store.transaction);\n }\n\n async deleteCache(keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n const store = await this.store(CACHE_STORE, \"readwrite\");\n for (const key of keys) store.delete(key);\n await transactionDone(store.transaction);\n }\n\n async listCache(prefix: string): Promise<{ key: string; cachedAt: number }[]> {\n const store = await this.store(CACHE_STORE, \"readonly\");\n const [keys, entries] = await Promise.all([\n requestToPromise(store.getAllKeys(prefixRange(prefix))),\n requestToPromise(store.getAll(prefixRange(prefix)))\n ]);\n return keys.map((key, i) => ({\n key: String(key),\n cachedAt: (entries[i] as OfflineCacheEntry)?.cachedAt ?? 0\n }));\n }\n\n async listCacheEntries(prefix: string): Promise<OfflineCacheRecord[]> {\n const store = await this.store(CACHE_STORE, \"readonly\");\n const [keys, entries] = await Promise.all([\n requestToPromise(store.getAllKeys(prefixRange(prefix))),\n requestToPromise(store.getAll(prefixRange(prefix)))\n ]);\n return keys.map((key, i) => {\n const entry = entries[i] as OfflineCacheEntry | undefined;\n return { key: String(key), value: entry?.value, cachedAt: entry?.cachedAt ?? 0 };\n });\n }\n\n async enqueue(key: string, mutation: PendingMutation): Promise<void> {\n const store = await this.store(QUEUE_STORE, \"readwrite\");\n await requestToPromise(store.put(mutation, key));\n }\n\n async dequeue(key: string): Promise<void> {\n const store = await this.store(QUEUE_STORE, \"readwrite\");\n await requestToPromise(store.delete(key));\n }\n\n async listQueue(prefix: string): Promise<PendingMutation[]> {\n const store = await this.store(QUEUE_STORE, \"readonly\");\n // getAll on a key range returns values in key order, which is the\n // FIFO guarantee this interface promises.\n const entries = await requestToPromise(store.getAll(prefixRange(prefix)));\n return entries as PendingMutation[];\n }\n\n async clear(prefix: string): Promise<void> {\n const cache = await this.store(CACHE_STORE, \"readwrite\");\n await requestToPromise(cache.delete(prefixRange(prefix)));\n const queue = await this.store(QUEUE_STORE, \"readwrite\");\n await requestToPromise(queue.delete(prefixRange(prefix)));\n }\n}\n","import {\n EntityRelation,\n FilterValues,\n FindResult,\n LogicalCondition,\n FilterCondition,\n OrderByTuple,\n WhereFilterOp,\n toCanonicalOp\n} from \"@rebasepro/types\";\nimport { FindParams } from \"./transport\";\nimport { resolveFindWindow } from \"@rebasepro/common\";\n\n/**\n * A local evaluator for `FindParams`, so cached rows can answer a query the\n * client has never sent to the server — and so a row written offline shows up\n * in every filtered list it belongs to, not just in unfiltered ones.\n *\n * This mirrors the Postgres driver's semantics rather than JavaScript's:\n *\n * - Comparing against NULL is *unknown*, not false-or-true. `status != \"done\"`\n * excludes rows where `status` is null, exactly as SQL does — a JS `!==`\n * would have included them.\n * - `ORDER BY` puts nulls last ascending and first descending, which is the\n * Postgres default.\n * - The wire format carries no types, so values arriving as strings are\n * compared numerically against numeric columns and as instants against\n * date columns. `[\"==\", \"3\"]` matches the number `3`, as it does server-side.\n *\n * Two things it deliberately approximates, both flagged by\n * {@link isExactlyEvaluable}: `searchString` becomes a case-insensitive\n * substring scan over the row's string fields (the server runs real full-text\n * search over the collection's configured columns), and `include` cannot be\n * evaluated at all, because the related rows live in collections this query\n * knows nothing about.\n */\n\nconst collator = typeof Intl !== \"undefined\" && typeof Intl.Collator === \"function\"\n ? new Intl.Collator(undefined, { numeric: false, sensitivity: \"variant\" })\n : undefined;\n\n/**\n * The server's page size when the caller does not ask for one.\n *\n * Re-exported rather than redeclared. This was its own `= 20` — a third\n * constant of this name in the workspace, next to `@rebasepro/common`'s 200 and\n * the 50 the REST layer actually applies — and a local copy of a number that\n * belongs to another process is a number that goes stale silently.\n */\nexport { DEFAULT_LIST_LIMIT as DEFAULT_PAGE_SIZE } from \"@rebasepro/types\";\n\nfunction isNullish(value: unknown): boolean {\n return value === null || value === undefined;\n}\n\n/**\n * Reduce a value to something comparable. Relations compare by the id they\n * point at — the column holds a foreign key, so that is what the server\n * compares too.\n */\nfunction toComparable(value: unknown): unknown {\n if (value instanceof Date) return value.getTime();\n if (value instanceof EntityRelation) return value.id;\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n // A reference/relation that lost its prototype somewhere still\n // compares by id.\n if (typeof record.__type === \"string\" && \"id\" in record) return record.id;\n }\n return value;\n}\n\n/**\n * Three-way compare with SQL's type coercion but not its collation. Returns\n * `undefined` when the two values are not ordered relative to each other,\n * which is how NULL propagates through a comparison.\n */\nexport function compareValues(a: unknown, b: unknown): number | undefined {\n const left = toComparable(a);\n const right = toComparable(b);\n if (isNullish(left) || isNullish(right)) return undefined;\n\n if (typeof left === \"boolean\" || typeof right === \"boolean\") {\n const l = left === true || left === \"true\" || left === 1 ? 1 : 0;\n const r = right === true || right === \"true\" || right === 1 ? 1 : 0;\n return l - r;\n }\n\n // A numeric string on either side means the wire dropped the type; compare\n // as numbers so `[\"<\", \"10\"]` does not order \"10\" before \"9\" as text.\n const leftNum = typeof left === \"number\" ? left : numericOrNaN(left);\n const rightNum = typeof right === \"number\" ? right : numericOrNaN(right);\n if (!Number.isNaN(leftNum) && !Number.isNaN(rightNum)) {\n return leftNum < rightNum ? -1 : leftNum > rightNum ? 1 : 0;\n }\n\n // One side is a date-shaped string and the other an instant.\n if (typeof left === \"number\" || typeof right === \"number\") {\n const leftTime = toTime(left);\n const rightTime = toTime(right);\n if (leftTime !== undefined && rightTime !== undefined) {\n return leftTime < rightTime ? -1 : leftTime > rightTime ? 1 : 0;\n }\n }\n\n const leftStr = String(left);\n const rightStr = String(right);\n if (collator) return collator.compare(leftStr, rightStr);\n return leftStr < rightStr ? -1 : leftStr > rightStr ? 1 : 0;\n}\n\nfunction numericOrNaN(value: unknown): number {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const n = Number(value);\n return Number.isNaN(n) ? NaN : n;\n }\n if (typeof value === \"bigint\") return Number(value);\n return NaN;\n}\n\nfunction toTime(value: unknown): number | undefined {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") {\n const t = Date.parse(value);\n return Number.isNaN(t) ? undefined : t;\n }\n return undefined;\n}\n\n/** Equality with the wire's type erasure allowed for, but never across NULL. */\nexport function looseEquals(a: unknown, b: unknown): boolean {\n const left = toComparable(a);\n const right = toComparable(b);\n if (isNullish(left) || isNullish(right)) return isNullish(left) && isNullish(right);\n if (left === right) return true;\n const cmp = compareValues(left, right);\n return cmp === 0;\n}\n\n/**\n * Translate a SQL `LIKE` pattern to an anchored regular expression.\n * `%` matches any run of characters, `_` exactly one, and a backslash escapes\n * either of them.\n */\nfunction likeToRegExp(pattern: string, caseInsensitive: boolean): RegExp {\n let source = \"^\";\n // Runs of `%` collapse to one. `%%%%X` means exactly what `%X` means, but\n // as a regular expression it is four adjacent unbounded quantifiers, and on\n // a subject that does not match the engine tries every way of splitting the\n // subject between them. Fourteen of them against a forty-eight character\n // value took eighty-seven seconds to answer `false`.\n //\n // The pattern is user input — `?title=like.%25%25%25…` over HTTP — so that\n // is a request that pins a CPU. Collapsing is semantics-preserving and\n // removes the ambiguity the backtracking feeds on.\n let lastWasWildcard = false;\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i];\n if (char === \"\\\\\" && i + 1 < pattern.length) {\n source += pattern[i + 1].replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n i++;\n lastWasWildcard = false;\n } else if (char === \"%\") {\n if (!lastWasWildcard) source += \"[\\\\s\\\\S]*\";\n lastWasWildcard = true;\n } else if (char === \"_\") {\n source += \"[\\\\s\\\\S]\";\n lastWasWildcard = false;\n } else {\n source += char.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n lastWasWildcard = false;\n }\n }\n return new RegExp(source + \"$\", caseInsensitive ? \"i\" : \"\");\n}\n\nfunction asArray(value: unknown): unknown[] {\n if (Array.isArray(value)) return value;\n if (value === undefined) return [];\n return [value];\n}\n\n/** Evaluate one canonical operator against one row value. */\nexport function matchesOperator(rowValue: unknown, op: WhereFilterOp, filterValue: unknown): boolean {\n switch (op) {\n case \"is-null\":\n return isNullish(rowValue);\n case \"is-not-null\":\n return !isNullish(rowValue);\n case \"==\":\n return looseEquals(rowValue, filterValue);\n case \"!=\":\n // SQL: `x != v` is unknown when x is NULL, so the row drops out.\n if (isNullish(rowValue)) return false;\n return !looseEquals(rowValue, filterValue);\n case \"<\":\n case \"<=\":\n case \">\":\n case \">=\": {\n const cmp = compareValues(rowValue, filterValue);\n if (cmp === undefined) return false;\n if (op === \"<\") return cmp < 0;\n if (op === \"<=\") return cmp <= 0;\n if (op === \">\") return cmp > 0;\n return cmp >= 0;\n }\n case \"in\":\n if (isNullish(rowValue)) return false;\n return asArray(filterValue).some((v) => looseEquals(rowValue, v));\n case \"not-in\":\n if (isNullish(rowValue)) return false;\n return !asArray(filterValue).some((v) => looseEquals(rowValue, v));\n case \"array-contains\": {\n if (!Array.isArray(rowValue)) return false;\n return rowValue.some((v) => looseEquals(v, filterValue));\n }\n case \"array-contains-any\": {\n if (!Array.isArray(rowValue)) return false;\n const wanted = asArray(filterValue);\n return rowValue.some((v) => wanted.some((w) => looseEquals(v, w)));\n }\n case \"like\":\n case \"not-like\":\n case \"ilike\":\n case \"not-ilike\": {\n if (isNullish(rowValue)) return false;\n const insensitive = op === \"ilike\" || op === \"not-ilike\";\n const negated = op === \"not-like\" || op === \"not-ilike\";\n const matched = likeToRegExp(String(filterValue), insensitive).test(String(rowValue));\n return negated ? !matched : matched;\n }\n default:\n // An operator this build does not know must not silently drop rows.\n return true;\n }\n}\n\nfunction isTuple(value: unknown): value is [WhereFilterOp, unknown] {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === \"string\"\n && toCanonicalOp(value[0]) !== undefined;\n}\n\n/** Evaluate a `where` clause: every field, and every tuple on a field, AND-ed. */\nexport function matchesWhere(row: Record<string, unknown>, where: FilterValues<string> | undefined): boolean {\n if (!where) return true;\n for (const [field, condition] of Object.entries(where)) {\n if (condition === undefined) continue;\n const tuples: [WhereFilterOp, unknown][] = isTuple(condition)\n ? [condition]\n : Array.isArray(condition)\n ? (condition as unknown[]).filter(isTuple) as [WhereFilterOp, unknown][]\n : [];\n for (const [rawOp, value] of tuples) {\n const op = toCanonicalOp(rawOp) ?? rawOp;\n if (!matchesOperator(row[field], op, value)) return false;\n }\n }\n return true;\n}\n\n/** Evaluate a nested and/or tree. */\nexport function matchesLogical(\n row: Record<string, unknown>,\n condition: LogicalCondition | FilterCondition | undefined\n): boolean {\n if (!condition) return true;\n if (\"type\" in condition) {\n const children = condition.conditions ?? [];\n if (children.length === 0) return true;\n return condition.type === \"or\"\n ? children.some((c) => matchesLogical(row, c))\n : children.every((c) => matchesLogical(row, c));\n }\n const op = toCanonicalOp(condition.operator) ?? condition.operator;\n return matchesOperator(row[condition.column], op as WhereFilterOp, condition.value);\n}\n\n/**\n * Approximate the server's full-text search with a case-insensitive substring\n * scan over the row's own string fields. Narrower than the real thing (no\n * stemming, no configured search columns), and it never matches a field the\n * cached row does not carry — a local list may therefore be missing rows the\n * server would have returned, which is why {@link isExactlyEvaluable} refuses\n * to call a search query exact.\n */\nexport function matchesSearch(row: Record<string, unknown>, searchString: string | undefined): boolean {\n if (!searchString) return true;\n const needle = searchString.trim().toLowerCase();\n if (!needle) return true;\n for (const value of Object.values(row)) {\n if (typeof value === \"string\" && value.toLowerCase().includes(needle)) return true;\n if (typeof value === \"number\" && String(value).includes(needle)) return true;\n }\n return false;\n}\n\n/** Does this row belong in the result set for `params`, ignoring pagination? */\nexport function matchesParams(row: Record<string, unknown>, params?: FindParams): boolean {\n if (!params) return true;\n return matchesWhere(row, params.where)\n && matchesLogical(row, params.logical)\n && matchesSearch(row, params.searchString);\n}\n\n/**\n * Sort in place, Postgres-style: nulls last ascending, first descending, with\n * the row id as a tiebreak so paging through an unsorted-but-equal run does\n * not shuffle rows between pages.\n */\nexport function sortRows<M extends Record<string, unknown>>(rows: M[], orderBy?: OrderByTuple): M[] {\n if (!orderBy) return rows;\n const [field, direction = \"asc\"] = orderBy;\n const sign = direction === \"desc\" ? -1 : 1;\n return rows.sort((a, b) => {\n const av = a[field];\n const bv = b[field];\n const aNull = isNullish(toComparable(av));\n const bNull = isNullish(toComparable(bv));\n if (aNull || bNull) {\n if (aNull && bNull) return tiebreak(a, b);\n // NULLS LAST ascending, NULLS FIRST descending.\n return (aNull ? 1 : -1) * (direction === \"desc\" ? -1 : 1);\n }\n const cmp = compareValues(av, bv);\n if (cmp === undefined || cmp === 0) return tiebreak(a, b);\n return cmp * sign;\n });\n}\n\nfunction tiebreak(a: Record<string, unknown>, b: Record<string, unknown>): number {\n const cmp = compareValues(a.id, b.id);\n return cmp ?? 0;\n}\n\n/**\n * Resolve `page`/`offset`/`limit` the way the server does.\n *\n * It did not: this defaulted an absent limit to 20 while `/api/data` pages by\n * 50, so the same `observe()` answered with 20 rows from the local database and\n * 50 from the network — a list that changed length depending on which side\n * answered, with `page` striding differently on each. Delegated now, so the\n * sentence above is true by construction rather than by agreement.\n */\nexport function resolvePagination(params?: FindParams): { limit: number; offset: number } {\n const { limit, offset } = resolveFindWindow(params);\n return { limit, offset };\n}\n\n/** `<`, `<=`, `>`, `>=` — the operators whose answer depends on a collation. */\nconst ORDERING_OPS = new Set<WhereFilterOp>([\"<\", \"<=\", \">\", \">=\"]);\n\n/** Does any condition in this `where` clause order its operands? */\nfunction whereOrders(where: FilterValues<string> | undefined): boolean {\n if (!where) return false;\n for (const condition of Object.values(where)) {\n const tuples = isTuple(condition) ? [condition] : (condition as unknown[]).filter(isTuple);\n if (tuples.some(([op]) => ORDERING_OPS.has(op))) return true;\n }\n return false;\n}\n\n/** The same question, through an `and(...)`/`or(...)` tree. */\nfunction logicalOrders(condition: LogicalCondition | FilterCondition | undefined, depth = 0): boolean {\n if (!condition || depth > 32) return false;\n if (\"type\" in condition) {\n return (condition.conditions ?? []).some((c) => logicalOrders(c, depth + 1));\n }\n return ORDERING_OPS.has(condition.operator);\n}\n\n/**\n * Can a locally evaluated answer to `params` be trusted to match the server's,\n * assuming the cache holds every row of the collection?\n *\n * `include` pulls in rows from other collections that this evaluator never\n * sees, and `searchString` is only approximated — both make the local answer a\n * best effort rather than an equivalent one.\n *\n * **Ordering comparisons are refused, and that is the interesting one.**\n * `compareValues` falls back to an `Intl.Collator` for operands it cannot read\n * as numbers or instants. PostgreSQL orders text by the *database's* collation,\n * which is a property of the server this process has never been told: under the\n * C collation `'apple' < 'Banana'` is false, under `en_US.UTF-8` it is true,\n * and the collator says true. So `[\"<\", \"Banana\"]` selects a different set here\n * than it does there — silently, and in whichever direction the deployment\n * happens to have been created.\n *\n * The refusal covers *every* ordering comparison rather than only the ones with\n * a string operand, because the operand type does not settle it: a numeric\n * bound against a text column (`[\"<\", 10]` on a `varchar`) also reaches the\n * collator, and nothing in `params` says what the column holds. Conservative on\n * purpose — the cost is that a query combining an ordering filter with\n * *unsynced local writes* stops placing those writes optimistically, which is a\n * degraded answer rather than a wrong one. Claiming exactness we do not have is\n * the other way round.\n *\n * This says nothing about ordering *results*; that is a separate claim with a\n * separate answer, because a sort changes which rows come first and not which\n * rows match. See {@link isLocallySortable}.\n */\nexport function isExactlyEvaluable(params?: FindParams): boolean {\n if (!params) return true;\n if (params.include && params.include.length > 0) return false;\n if (params.searchString) return false;\n // Nearest-neighbour ordering is the server's to compute: the cache holds no\n // vectors, and even with them, answering from a subset would return the\n // nearest of what happens to be cached while looking like the nearest there\n // are — a wrong answer that is indistinguishable from a right one.\n if (params.vectorSearch) return false;\n if (whereOrders(params.where)) return false;\n if (logicalOrders(params.logical)) return false;\n return true;\n}\n\n/**\n * Would sorting `rows` locally reproduce the order the server would have sent?\n *\n * Asked of the rows rather than of the query, because unlike a filter this one\n * *is* decidable from the data in hand: {@link compareValues} reaches the\n * collator only when it cannot read both operands as numbers, and `toComparable`\n * has already turned dates and relations into numbers and ids by then. If every\n * value on the sort column normalises to a number, the collator is unreachable\n * and the local order is the server's order.\n *\n * A text column is therefore refused — see {@link isExactlyEvaluable} for why\n * the two cannot be made to agree — and so is a column this page happens to see\n * only as strings, which is the same thing from here.\n *\n * Nulls are fine either way: they are ordered by an explicit rule (last\n * ascending, first descending) that matches Postgres and never reaches the\n * comparator.\n */\nexport function isLocallySortable(\n rows: readonly Record<string, unknown>[],\n orderBy?: OrderByTuple\n): boolean {\n if (!orderBy) return true;\n const [field] = orderBy;\n for (const row of rows) {\n const value = toComparable(row[field]);\n if (isNullish(value)) continue;\n if (typeof value === \"number\" || typeof value === \"boolean\") continue;\n if (typeof value === \"bigint\") continue;\n // A numeric string is compared as a number, so it is safe too — this is\n // the wire's type erasure, which `compareValues` already undoes.\n if (typeof value === \"string\" && value.trim() !== \"\" && !Number.isNaN(Number(value))) continue;\n return false;\n }\n return true;\n}\n\n/** Run a full query — filter, sort, paginate — over a set of rows. */\nexport function runLocalQuery<M extends Record<string, unknown>>(\n rows: M[],\n params?: FindParams\n): FindResult<M> {\n const matched = rows.filter((row) => matchesParams(row, params));\n sortRows(matched, params?.orderBy);\n const { limit, offset } = resolvePagination(params);\n const page = matched.slice(offset, offset + limit);\n return {\n data: page,\n meta: {\n total: matched.length,\n limit,\n offset,\n hasMore: offset + page.length < matched.length\n }\n };\n}\n","import { buildQueryString, FindParams, RebaseApiError } from \"./transport\";\nimport { FindAllParams, FindResult, IterateParams, LogicalCondition, SDKCollectionClient, WhereFilterOp, WhereValueFor, WriteOptions } from \"@rebasepro/types\";\nimport { collectAllPages, paginateFind } from \"@rebasepro/common\";\nimport { CollectionClient, LiveResult, ObserveOptions, RowSnapshotMeta } from \"./collection\";\nimport { SDKQueryBuilder } from \"./sdk_query_builder\";\nimport { dehydrateRow, hydrateRow } from \"./offline-codec\";\nimport {\n ConnectivityMonitor,\n isDuplicateKeyError,\n isIdempotencyInProgressError,\n isNetworkError,\n isRetryableError\n} from \"./offline-connectivity\";\nimport {\n IndexedDBOfflineStore,\n MemoryOfflineStore,\n OfflineStore,\n PendingMutation,\n createMutationId\n} from \"./offline-store\";\nimport {\n isExactlyEvaluable,\n isLocallySortable,\n matchesParams,\n resolvePagination,\n runLocalQuery,\n sortRows\n} from \"./offline-query\";\n\n/**\n * The SDK's local-first sync engine.\n *\n * The design goal is that the network is never in the way of the interface.\n * That comes from three properties, and everything in this file exists to\n * serve one of them:\n *\n * 1. **A local database, not a response cache.** Rows are stored normalized,\n * by id, and queries are answered by evaluating them\n * ({@link ./offline-query}) against those rows. A row written offline\n * therefore appears in *every* list it belongs to, a row edited in one view\n * updates in all of them, and `findById` answers for a row only ever seen\n * inside a `find`. Server responses are merged into this database rather\n * than replacing it, and a row with unsynced local writes keeps them: the\n * user's own change never flickers away underneath them.\n *\n * 2. **Writes are decided locally.** A write made while offline is applied to\n * the local database and queued — with the state it replaced, so a server\n * rejection can be undone — and the call returns immediately. When\n * connectivity is known to be gone the request is not even attempted, so\n * an offline write costs nothing instead of a timeout.\n *\n * 3. **Reads are reactive.** {@link OfflineManager.observe} emits from the\n * local database synchronously-ish, revalidates in the background, and\n * re-emits whenever anything touches the rows it covers — a local write,\n * a replay landing, a rollback, a realtime event, or another browser tab.\n *\n * What it deliberately is not: a full replica. Only rows the app has actually\n * read or written are local, so a query the cache cannot fully answer is\n * flagged `partial` rather than silently reported as complete.\n */\n\nexport interface OfflineConfig {\n /**\n * Persistence backend. Defaults to IndexedDB in the browser and an\n * in-memory store elsewhere; pass a custom implementation (e.g. backed by\n * AsyncStorage in React Native) to persist in other environments.\n */\n store?: OfflineStore;\n /**\n * Cached query snapshots kept per collection; the least recently written\n * are evicted beyond this. Defaults to 50.\n */\n maxCachedQueriesPerCollection?: number;\n /**\n * Cached rows kept per collection. Rows with unsynced local writes are\n * never evicted. Defaults to 5 000.\n */\n maxCachedRowsPerCollection?: number;\n /**\n * Ceiling for the exponential retry backoff, in milliseconds. Replay\n * retries start at one second and double up to this. `0` disables\n * automatic retries entirely — `client.offline.sync()`, a sign-in, and the\n * browser's `online` event still trigger one. Defaults to 60 000.\n */\n syncIntervalMs?: number;\n /**\n * Keep several tabs of the same app in step over a `BroadcastChannel`: a\n * write in one appears in the others, and only one of them replays the\n * shared queue. Defaults to on for the IndexedDB store (a real shared\n * database) and off for the in-memory one, which no other tab can see.\n */\n crossTab?: boolean;\n /**\n * How many times a mutation rejected with a *retryable* status (429, 503,\n * …) is replayed before it is given up on and rolled back. Network\n * failures do not count against this: being offline is not an attempt.\n * Defaults to 5.\n */\n maxRetries?: number;\n /**\n * Called when the server *rejects* a queued mutation (a 4xx/5xx that will\n * not resolve on its own — validation, RLS, a since-deleted row). The\n * local rows it wrote are rolled back to the state they had before it, and\n * any later queued writes to the same rows are discarded with it — they\n * were built on a change that never happened. Each discarded mutation is\n * reported here.\n *\n * Network failures are not errors: those mutations stay queued.\n */\n onSyncError?: (error: Error, mutation: PendingMutation) => void;\n}\n\n/** A snapshot of the engine's state, for a status indicator. */\nexport interface OfflineStatus {\n /** False once a request has failed to reach the server, until one does. */\n online: boolean;\n /** True while the queue is being replayed. */\n syncing: boolean;\n /** Local writes not yet accepted by the server. */\n pending: number;\n /** When the queue was last fully drained. */\n lastSyncedAt?: number;\n /** The last replay rejection, if any. */\n lastError?: string;\n}\n\nexport type { LiveResult, ObserveOptions, RowSnapshotMeta } from \"./collection\";\n\n/** What `client.offline` exposes to the app. */\nexport interface OfflineApi {\n /** Replay the queue now. Resolves with what was flushed and what remains. */\n sync(): Promise<{ flushed: number; remaining: number }>;\n /** The queued mutations for the current user, oldest first. */\n pending(): Promise<PendingMutation[]>;\n /** The current engine state — connectivity, queue depth, last sync. */\n status(): OfflineStatus;\n /** Subscribe to {@link OfflineStatus} changes (for a sync indicator). */\n onStatusChange(listener: (status: OfflineStatus) => void): () => void;\n /**\n * Drop the current user's queued mutations AND their local rows.\n * Destructive: queued writes are lost, not replayed. For \"discard my\n * offline changes\" flows, not for sign-out (scoping already isolates\n * users).\n */\n clear(): Promise<void>;\n /** Subscribe to queue-size changes (for a \"pending changes\" badge). */\n onQueueChange(listener: (count: number) => void): () => void;\n}\n\n/** True when a read failed because there was neither network nor local data. */\nexport function isOfflineError(error: unknown): boolean {\n return error instanceof RebaseApiError && error.code === \"offline\";\n}\n\nfunction offlineError(message: string): RebaseApiError {\n return new RebaseApiError(message, { status: 0, code: \"offline\" });\n}\n\nfunction generateOfflineId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n // Non-cryptographic fallback for exotic runtimes; collision odds are\n // irrelevant at offline-queue scale.\n return `off-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\ntype AnyRow = Record<string, unknown>;\ntype InnerFactory = (slug: string) => SDKCollectionClient<AnyRow>;\n\n/** What the server said about one query, as ids into the local row database. */\ninterface QuerySnapshot {\n ids: (string | number)[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\ninterface RowEntry {\n row: AnyRow;\n cachedAt: number;\n /** Bumped on every local change, so observers can diff cheaply. */\n rev: number;\n}\n\ninterface CollectionState {\n rows: Map<string, RowEntry>;\n snapshots: Map<string, QuerySnapshot>;\n /**\n * Query keys whose snapshot came from a request that completed in this\n * session. Deliberately not persisted: a snapshot read back off disk is\n * exactly what \"from the cache\" means, however recent it looks.\n */\n fresh: Set<string>;\n /** The same, per row id, for `observeById`. */\n freshRows: Set<string>;\n /** Ids the server has confirmed do not exist — a negative cache. */\n absent: Set<string>;\n loaded?: Promise<void>;\n /**\n * True once the persisted rows are in memory. Observers must not emit\n * before this: an empty map during the load is not an empty collection,\n * and emitting it would flash an empty list over real data.\n */\n ready: boolean;\n}\n\ninterface Observer {\n slug: string;\n params?: FindParams;\n /** Set for observeById; then `params` is unused. */\n id?: string | number;\n emit: () => void;\n /** Re-run this observer's query against the server. */\n refresh: () => Promise<unknown>;\n signature?: string;\n error?: Error;\n settled: boolean;\n}\n\n// `\\u0000` as an escape, not a raw NUL byte in the source. The value is\n// identical — an id can never contain it, which is the point — but written raw it\n// made this whole file test as binary, so every `grep` over the repo skipped\n// all 1,700 lines of it in silence.\nconst MISSING = \"\\u0000missing\";\n\n/**\n * Replays to spend on a mutation whose idempotency key the server is still\n * holding, when the app has not asked for more.\n *\n * Retries double from a second and cap at the sync interval, so the default\n * budget of five covers about half a minute — less than the lease a server\n * gives a claim nobody came back for. This many outlast it with room for a slow\n * batch, and the count is what stops a server that never releases the key from\n * blocking the queue behind it indefinitely.\n */\nconst IN_PROGRESS_MIN_RETRIES = 12;\n\nexport class OfflineManager {\n private readonly store: OfflineStore;\n private readonly maxCachedQueries: number;\n private readonly maxCachedRows: number;\n private readonly maxRetries: number;\n private readonly onSyncError?: OfflineConfig[\"onSyncError\"];\n private readonly createInner: InnerFactory;\n private readonly inners = new Map<string, SDKCollectionClient<AnyRow>>();\n private readonly connectivity: ConnectivityMonitor;\n\n private scope = \"anon\";\n /** The local database: normalized rows and query snapshots per collection. */\n private collections = new Map<string, CollectionState>();\n /** In-memory mirror of the current scope's queue, in replay order. */\n private queue: PendingMutation[] = [];\n /**\n * The mutation currently on the wire, if any.\n *\n * `flush` awaits `replay(op)` with `op` still at the head of `queue`, so for\n * the whole duration of that request the in-flight op is also the queue's\n * *tail* whenever it is the only entry. Both shortcuts in `enqueue` reach\n * for the tail, and neither may touch an op the server is already reading:\n *\n * - Coalescing an update into it mutates a payload that has already been\n * serialized and sent, and `drop` then removes the whole entry on ACK —\n * so the second edit is neither sent nor kept. A silently lost write.\n * - Cancelling it out against a delete assumes the server never saw the\n * create. It is seeing it right now, so the row would be created and the\n * delete never queued — an orphan row nothing will ever remove.\n *\n * Guarding on the id rather than on a boolean keeps this correct if the\n * flush loop ever sends more than one op at a time.\n */\n private inFlightId: string | null = null;\n private queueLoad?: Promise<void>;\n /** Serializes enqueues so concurrent writes keep the order the app made them. */\n private enqueueChain: Promise<unknown> = Promise.resolve();\n private flushPromise?: Promise<{ flushed: number; remaining: number }>;\n private queueListeners = new Set<(count: number) => void>();\n private statusListeners = new Set<(status: OfflineStatus) => void>();\n private observers = new Map<string, Set<Observer>>();\n private refreshPending = new Set<string>();\n private revCounter = 0;\n private disposed = false;\n private currentStatus: OfflineStatus = { online: true, syncing: false, pending: 0 };\n private readonly channel?: BroadcastChannel;\n private readonly tabId = createMutationId();\n\n readonly api: OfflineApi;\n\n constructor(config: OfflineConfig, createInner: InnerFactory) {\n this.store = config.store\n ?? (typeof indexedDB !== \"undefined\" ? new IndexedDBOfflineStore() : new MemoryOfflineStore());\n this.maxCachedQueries = config.maxCachedQueriesPerCollection ?? 50;\n this.maxCachedRows = config.maxCachedRowsPerCollection ?? 5_000;\n this.maxRetries = config.maxRetries ?? 5;\n this.onSyncError = config.onSyncError;\n this.createInner = createInner;\n\n const maxBackoffMs = config.syncIntervalMs ?? 60_000;\n this.connectivity = new ConnectivityMonitor({\n maxBackoffMs: Math.max(1_000, maxBackoffMs),\n // With no retry timer nothing would ever reopen the window, so a\n // single failure would strand the client offline forever.\n respectBackoff: maxBackoffMs > 0\n });\n if (maxBackoffMs > 0) {\n this.connectivity.onRetryDue = () => { void this.sync().catch(() => undefined); };\n }\n this.connectivity.onChange((online) => {\n this.patchStatus({ online });\n if (online) this.revalidateAll();\n });\n this.currentStatus.online = this.connectivity.isOnline();\n\n // Other tabs share the same IndexedDB. Without this they would each\n // hold a stale copy of the row database and quietly diverge — one tab\n // showing an edit the other never learns about. A memory store is not\n // shared with anyone, so there is nothing to reconcile and the channel\n // would only relay writes between unrelated clients.\n const crossTab = config.crossTab ?? this.store instanceof IndexedDBOfflineStore;\n if (crossTab && typeof BroadcastChannel !== \"undefined\") {\n try {\n this.channel = new BroadcastChannel(\"rebase-offline\");\n this.channel.onmessage = (event: MessageEvent) => this.onBroadcast(event.data);\n // Node's BroadcastChannel is ref'd, and a script that opened a\n // client should still be able to exit.\n (this.channel as unknown as { unref?: () => void }).unref?.();\n } catch {\n // Not fatal: a browser that refuses the channel just loses\n // cross-tab propagation.\n }\n }\n\n this.api = {\n sync: () => this.sync(),\n pending: async () => {\n await this.ensureQueueLoaded();\n // Deep-copied: these are live queue entries (tail coalescing\n // mutates them in place), and a caller must not be able to\n // edit what will be replayed.\n return this.queue.map((m) => structuredClone(m));\n },\n status: () => ({ ...this.currentStatus }),\n onStatusChange: (listener) => {\n this.statusListeners.add(listener);\n return () => this.statusListeners.delete(listener);\n },\n clear: async () => {\n await this.store.clear(`${this.scope}|`);\n this.queue = [];\n this.resetCollections();\n this.patchStatus({ pending: 0, lastError: undefined });\n this.notifyQueue();\n for (const slug of this.observers.keys()) this.notifyCollection(slug, false);\n },\n onQueueChange: (listener) => {\n this.queueListeners.add(listener);\n return () => this.queueListeners.delete(listener);\n }\n };\n }\n\n /**\n * Cache and queue are partitioned per signed-in user: cached rows are\n * RLS-filtered for the user who fetched them, and queued writes must\n * replay under the credentials that made them — so neither may ever leak\n * across a sign-out/sign-in on a shared browser.\n */\n setScope(uid: string | undefined): void {\n const next = uid || \"anon\";\n if (next === this.scope) return;\n this.scope = next;\n this.queueLoad = undefined;\n this.queue = [];\n this.resetCollections();\n this.patchStatus({ pending: 0, lastError: undefined });\n this.notifyQueue();\n // Everything on screen belongs to the previous user.\n for (const slug of this.observers.keys()) this.notifyCollection(slug, false);\n this.revalidateAll();\n // The returning user's queue may hold writes from a previous session.\n void this.sync().catch(() => undefined);\n }\n\n /**\n * Throw away every local row, for a scope change or an explicit clear.\n *\n * The state objects are replaced rather than emptied, so a load still in\n * flight for the previous user fails its identity check and discards what\n * it read instead of grafting it onto the new one. The replacements are\n * marked ready: nothing needs loading until something asks, and observers\n * have to be told *now* that the rows they are showing are gone.\n */\n private resetCollections(): void {\n const slugs = [...this.collections.keys()];\n this.collections = new Map();\n for (const slug of slugs) {\n this.collections.set(slug, {\n rows: new Map(),\n snapshots: new Map(),\n fresh: new Set(),\n freshRows: new Set(),\n absent: new Set(),\n ready: true\n });\n }\n }\n\n /** Release listeners, timers and the cross-tab channel (client.close()). */\n dispose(): void {\n this.disposed = true;\n this.connectivity.dispose();\n try {\n this.channel?.close();\n } catch {\n // A channel that is already closed is not a problem.\n }\n this.observers.clear();\n this.queueListeners.clear();\n this.statusListeners.clear();\n }\n\n // ─── Collection wrapping ─────────────────────────────────────────────────\n\n wrap<M extends AnyRow>(slug: string, inner: CollectionClient<M>): CollectionClient<M> {\n this.inners.set(slug, inner as SDKCollectionClient<AnyRow>);\n\n const wrapped: CollectionClient<M> = {\n find: async (params?: FindParams<M>): Promise<FindResult<M>> => {\n const state = await this.ensureCollection(slug);\n if (this.connectivity.shouldAttempt()) {\n try {\n const res = await inner.find(params);\n this.connectivity.markSuccess();\n await this.ingest(slug, res.data ?? []);\n const snapshot = this.recordSnapshot(slug, params, res);\n const answer = this.answer<M>(slug, params, snapshot);\n this.notifyCollection(slug, false);\n return { data: answer.data, meta: answer.meta };\n } catch (error) {\n if (!isNetworkError(error)) {\n // A 5xx or a rate limit still deserves the cached\n // answer rather than an exception the app has to\n // special-case, but only when we have one.\n if (isRetryableError(error) && this.hasLocalAnswer(state, slug, params)) {\n const answer = this.answer<M>(slug, params, this.snapshotFor(slug, params));\n return { data: answer.data, meta: answer.meta };\n }\n throw error;\n }\n this.connectivity.markFailure();\n }\n }\n const answer = this.localFind<M>(slug, params);\n // Falling back is a state change even when the rows are the\n // same — it is how a \"showing cached data\" badge lights up.\n this.notifyCollection(slug, false);\n return { data: answer.data, meta: answer.meta };\n },\n\n // Paginates the *wrapped* find, so a walk started offline is served\n // page by page out of the local database exactly as it would be\n // from the server, and rejoins the network mid-walk if it returns.\n iterate: (params?: IterateParams<M>) => paginateFind<M>((p) => wrapped.find(p), params, slug),\n\n findAll: (params?: FindAllParams<M>) => collectAllPages<M>((p) => wrapped.find(p), params, slug),\n\n findById: async (id: string | number) => {\n await this.ensureCollection(slug);\n if (this.connectivity.shouldAttempt()) {\n try {\n const row = await inner.findById(id);\n this.connectivity.markSuccess();\n if (row !== undefined) {\n await this.ingest(slug, [row]);\n } else if (!this.hasPending(slug, id)) {\n // The server is authoritative that it is gone, and\n // nothing local is waiting to recreate it.\n this.removeLocalRow(slug, id, true);\n }\n this.notifyCollection(slug, false);\n return this.localRow<M>(slug, id);\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const local = this.localRow<M>(slug, id);\n if (local !== undefined || this.hasPending(slug, id)) return local;\n // \"Not there\" is an answer, and one we may already have.\n if (this.collections.get(slug)?.absent.has(String(id))) return undefined;\n throw offlineError(\n `Offline: \"${slug}\" row ${String(id)} is not in the local database.`\n );\n },\n\n create: async (data: Partial<M>, id?: string | number) => {\n await this.ensureCollection(slug);\n if (this.connectivity.shouldAttempt()) {\n try {\n const row = await inner.create(data, id);\n this.connectivity.markSuccess();\n await this.ingest(slug, [row]);\n this.notifyCollection(slug);\n this.scheduleRefresh(slug);\n return row;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const providedId = id ?? (data as AnyRow).id as string | number | undefined;\n const rowId = providedId ?? generateOfflineId();\n const row = { ...(data as AnyRow), id: rowId } as unknown as M;\n await this.enqueue({\n collection: slug,\n type: \"create\",\n id: rowId,\n data: row,\n generatedId: providedId === undefined,\n rollback: { rows: { [String(rowId)]: this.rawLocalRow(slug, rowId) ?? null } }\n });\n this.setLocalRow(slug, rowId, row);\n this.notifyCollection(slug);\n return row;\n },\n\n createMany: async (data: Partial<M>[], options?: { upsert?: boolean }) => {\n await this.ensureCollection(slug);\n if (!Array.isArray(data)) {\n throw new TypeError(\"createMany expects an array of records.\");\n }\n if (data.length === 0) return [];\n if (this.connectivity.shouldAttempt()) {\n try {\n const rows = await inner.createMany(data, options);\n this.connectivity.markSuccess();\n await this.ingest(slug, rows);\n this.notifyCollection(slug);\n this.scheduleRefresh(slug);\n return rows;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const rows = data.map((r) => ({\n ...(r as AnyRow),\n id: (r as AnyRow).id ?? generateOfflineId()\n })) as unknown as M[];\n const rollback: Record<string, AnyRow | null> = {};\n for (const row of rows) {\n const key = String(row.id);\n rollback[key] = this.rawLocalRow(slug, row.id as string | number) ?? null;\n }\n await this.enqueue({\n collection: slug,\n type: \"createMany\",\n data: rows,\n upsert: options?.upsert,\n rollback: { rows: rollback }\n });\n for (const row of rows) this.setLocalRow(slug, row.id as string | number, row);\n this.notifyCollection(slug);\n return rows;\n },\n\n updateMany: async (updates: { id: string | number; data: Partial<M> }[], options?: WriteOptions) => {\n await this.ensureCollection(slug);\n if (!Array.isArray(updates)) {\n throw new TypeError(\"updateMany expects an array of { id, data } entries.\");\n }\n if (updates.length === 0) return [];\n // Any row in the batch with a write already queued sends the\n // whole batch to the queue. Splitting it — some rows now, some\n // later — would break the one guarantee a batch makes, that its\n // rows land together, and would reorder writes against a row\n // whose own create has not landed yet.\n const anyPending = updates.some((u) => this.hasPending(slug, u.id));\n if (this.connectivity.shouldAttempt() && !anyPending) {\n try {\n const rows = await inner.updateMany(updates, options);\n this.connectivity.markSuccess();\n await this.ingest(slug, rows);\n this.notifyCollection(slug);\n return rows;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const rollback: Record<string, AnyRow | null> = {};\n const optimistic: M[] = [];\n for (const { id, data } of updates) {\n const base = this.rawLocalRow(slug, id);\n rollback[String(id)] = base ?? null;\n optimistic.push({ ...(base ?? {}), ...(data as AnyRow), id } as unknown as M);\n }\n await this.enqueue({\n collection: slug,\n type: \"updateMany\",\n updates: updates.map((u) => ({ id: u.id,\ndata: u.data as AnyRow })),\n rollback: { rows: rollback }\n });\n for (const row of optimistic) this.setLocalRow(slug, row.id as string | number, row);\n this.notifyCollection(slug);\n return optimistic;\n },\n\n deleteMany: async (ids: (string | number)[], options?: WriteOptions) => {\n await this.ensureCollection(slug);\n if (!Array.isArray(ids)) {\n throw new TypeError(\"deleteMany expects an array of ids.\");\n }\n if (ids.length === 0) return;\n const anyPending = ids.some((id) => this.hasPending(slug, id));\n if (this.connectivity.shouldAttempt() && !anyPending) {\n try {\n await inner.deleteMany(ids, options);\n this.connectivity.markSuccess();\n for (const id of ids) this.removeLocalRow(slug, id, true);\n this.notifyCollection(slug);\n this.scheduleRefresh(slug);\n return;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const rollback: Record<string, AnyRow | null> = {};\n for (const id of ids) {\n rollback[String(id)] = this.rawLocalRow(slug, id) ?? null;\n }\n await this.enqueue({\n collection: slug,\n type: \"deleteMany\",\n ids,\n rollback: { rows: rollback }\n });\n for (const id of ids) this.removeLocalRow(slug, id, false);\n this.notifyCollection(slug);\n },\n\n update: async (id: string | number, data: Partial<M>) => {\n await this.ensureCollection(slug);\n // Never overtake a write already queued for this row. The\n // reads already respect the queue; the writes did not, so an\n // edit made while the row's own create was still pending went\n // straight to a server that had never heard of the row and came\n // back 404 — the caller's edit failing on a row they could see.\n // Queuing keeps the order the app issued the writes in.\n if (this.connectivity.shouldAttempt() && !this.hasPending(slug, id)) {\n try {\n const row = await inner.update(id, data);\n this.connectivity.markSuccess();\n await this.ingest(slug, [row]);\n this.notifyCollection(slug);\n return row;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const base = this.rawLocalRow(slug, id);\n await this.enqueue({\n collection: slug,\n type: \"update\",\n id,\n data: data as AnyRow,\n rollback: { rows: { [String(id)]: base ?? null } }\n });\n const optimistic = { ...(base ?? {}), ...(data as AnyRow), id } as unknown as M;\n this.setLocalRow(slug, id, optimistic);\n this.notifyCollection(slug);\n return optimistic;\n },\n\n delete: async (id: string | number) => {\n await this.ensureCollection(slug);\n // As in `update`: a delete must not overtake this row's own\n // queued create, or it 404s and the create then lands behind\n // it, leaving the row the caller just deleted.\n if (this.connectivity.shouldAttempt() && !this.hasPending(slug, id)) {\n try {\n await inner.delete(id);\n this.connectivity.markSuccess();\n this.removeLocalRow(slug, id, true);\n this.notifyCollection(slug);\n this.scheduleRefresh(slug);\n return;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n await this.enqueue({\n collection: slug,\n type: \"delete\",\n id,\n rollback: { rows: { [String(id)]: this.rawLocalRow(slug, id) ?? null } }\n });\n this.removeLocalRow(slug, id);\n this.notifyCollection(slug);\n },\n\n count: async (params?: FindParams<M>): Promise<number> => {\n await this.ensureCollection(slug);\n if (this.connectivity.shouldAttempt()) {\n try {\n const n = await inner.count(params);\n this.connectivity.markSuccess();\n void this.writeCache(this.countKey(slug, params), n);\n return Math.max(0, n + this.pendingDelta(slug, params));\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const cached = await this.readCache<number>(this.countKey(slug, params));\n if (cached !== undefined) return Math.max(0, cached + this.pendingDelta(slug, params));\n const state = this.collections.get(slug);\n if (state && state.rows.size > 0) {\n return runLocalQuery([...state.rows.values()].map((e) => e.row), params).meta.total;\n }\n throw offlineError(`Offline: no cached count for \"${slug}\".`);\n },\n\n observe: (\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) => this.observe<M>(slug, wrapped, inner, params, onResult, onError, options),\n\n observeById: (\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) => this.observeById<M>(slug, wrapped, inner, id, onResult, onError, options),\n\n // The builder calls back into `wrapped.find(...)`, so fluent\n // queries go through the local database like direct calls.\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SDKQueryBuilder<M>(wrapped);\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) => new SDKQueryBuilder<M>(wrapped).orderBy(column, direction),\n limit: (count) => new SDKQueryBuilder<M>(wrapped).limit(count),\n offset: (count) => new SDKQueryBuilder<M>(wrapped).offset(count),\n search: (searchString, options) => new SDKQueryBuilder<M>(wrapped).search(searchString, options),\n vectorSearch: (property, vector, options) => new SDKQueryBuilder<M>(wrapped).vectorSearch(property, vector, options),\n include: (...relations) => new SDKQueryBuilder<M>(wrapped).include(...relations)\n };\n\n // Realtime stays a live server stream — but everything it delivers is\n // worth keeping, so it feeds the local database on its way past.\n if (inner.listen) {\n wrapped.listen = (params, onUpdate, onError) => inner.listen!(\n params,\n (response) => {\n void this.ingest(slug, response.data ?? []).then(() => this.notifyCollection(slug, false));\n onUpdate(response);\n },\n onError\n );\n }\n if (inner.listenById) {\n wrapped.listenById = (id, onUpdate, onError) => inner.listenById!(\n id,\n (row) => {\n if (row) void this.ingest(slug, [row]).then(() => this.notifyCollection(slug, false));\n onUpdate(row);\n },\n onError\n );\n }\n\n return wrapped;\n }\n\n // ─── Live queries ────────────────────────────────────────────────────────\n\n private observe<M extends AnyRow>(\n slug: string,\n wrapped: CollectionClient<M>,\n inner: CollectionClient<M>,\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ): () => void {\n let closed = false;\n let unlisten: (() => void) | undefined;\n\n const observer: Observer = {\n slug,\n params,\n settled: false,\n refresh: () => wrapped.find(params).catch(() => undefined),\n emit: () => {\n if (closed || !this.collections.get(slug)?.ready) return;\n const result = this.answer<M>(slug, params, this.snapshotFor(slug, params));\n // Every field the callback receives has to be in the\n // signature, or a change to one of them is deduplicated away —\n // a row settling from \"saving\" to saved is exactly that.\n const signature = `${result.fromCache ? \"c\" : \"s\"}${result.hasPendingWrites ? \"p\" : \"-\"}`\n + this.signature(slug, result.data, result.meta.total);\n if (observer.settled && signature === observer.signature) return;\n observer.signature = signature;\n observer.settled = true;\n onResult(observer.error ? { ...result, error: observer.error } : result);\n }\n };\n this.observersFor(slug).add(observer);\n\n void (async () => {\n await this.ensureCollection(slug);\n if (closed) return;\n // Emit whatever is already local before touching the network. An\n // app that has run this query before renders instantly.\n if (this.hasLocalAnswer(this.collections.get(slug), slug, params)) observer.emit();\n try {\n await wrapped.find(params);\n observer.error = undefined;\n } catch (error) {\n observer.error = error as Error;\n if (closed) return;\n // A read that found nothing locally has nothing to emit, so the\n // failure is all the app gets.\n if (!observer.settled) {\n onError?.(error as Error);\n return;\n }\n }\n if (!closed) observer.emit();\n })();\n\n if (options?.realtime !== false && inner.listen) {\n unlisten = inner.listen(params, (response) => {\n void this.ingest(slug, response.data ?? []).then(() => {\n this.recordSnapshot(slug, params, response);\n this.notifyCollection(slug, false);\n });\n }, onError);\n }\n\n return () => {\n closed = true;\n this.observersFor(slug).delete(observer);\n unlisten?.();\n };\n }\n\n private observeById<M extends AnyRow>(\n slug: string,\n wrapped: CollectionClient<M>,\n inner: CollectionClient<M>,\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ): () => void {\n let closed = false;\n let unlisten: (() => void) | undefined;\n const observer: Observer = {\n slug,\n id,\n settled: false,\n refresh: () => wrapped.findById(id).catch(() => undefined),\n emit: () => {\n if (closed || !this.collections.get(slug)?.ready) return;\n const row = this.localRow<M>(slug, id);\n const entry = this.collections.get(slug)?.rows.get(String(id));\n const fromCache = !this.collections.get(slug)?.freshRows.has(String(id));\n const hasPendingWrites = this.hasPending(slug, id);\n const signature = `${fromCache ? \"c\" : \"s\"}${hasPendingWrites ? \"p\" : \"-\"}|`\n + (row === undefined ? MISSING : `${String(id)}:${entry?.rev ?? 0}`);\n if (observer.settled && signature === observer.signature) return;\n observer.signature = signature;\n observer.settled = true;\n onResult(row, { fromCache, hasPendingWrites });\n }\n };\n this.observersFor(slug).add(observer);\n\n void (async () => {\n await this.ensureCollection(slug);\n if (closed) return;\n if (this.localRow<M>(slug, id) !== undefined) observer.emit();\n try {\n await wrapped.findById(id);\n } catch (error) {\n if (closed) return;\n if (!observer.settled) {\n onError?.(error as Error);\n return;\n }\n }\n if (!closed) observer.emit();\n })();\n\n if (options?.realtime !== false && inner.listenById) {\n unlisten = inner.listenById(id, (row) => {\n if (!row) {\n if (!this.hasPending(slug, id)) this.removeLocalRow(slug, id, true);\n this.notifyCollection(slug, false);\n return;\n }\n void this.ingest(slug, [row]).then(() => this.notifyCollection(slug, false));\n }, onError);\n }\n\n return () => {\n closed = true;\n this.observersFor(slug).delete(observer);\n unlisten?.();\n };\n }\n\n private observersFor(slug: string): Set<Observer> {\n let set = this.observers.get(slug);\n if (!set) {\n set = new Set();\n this.observers.set(slug, set);\n }\n return set;\n }\n\n /** Cheap change detection: which rows, in what order, at which revision. */\n private signature(slug: string, rows: AnyRow[], total: number): string {\n const state = this.collections.get(slug);\n const parts = rows.map((row) => {\n const key = String(row.id);\n return `${key}:${state?.rows.get(key)?.rev ?? 0}`;\n });\n return `${total}|${parts.join(\",\")}`;\n }\n\n private notifyCollection(slug: string, broadcast = true): void {\n const set = this.observers.get(slug);\n if (set) for (const observer of [...set]) observer.emit();\n if (broadcast) this.broadcast({ type: \"rows\", slugs: [slug] });\n }\n\n /** Connectivity came back (or the user changed): re-read everything live. */\n private revalidateAll(): void {\n for (const slug of this.observers.keys()) {\n this.notifyCollection(slug, false);\n this.scheduleRefresh(slug);\n }\n }\n\n // ─── Reading the local database ──────────────────────────────────────────\n\n private collectionState(slug: string): CollectionState {\n let state = this.collections.get(slug);\n if (!state) {\n state = {\n rows: new Map(),\n snapshots: new Map(),\n fresh: new Set(),\n freshRows: new Set(),\n absent: new Set(),\n ready: false\n };\n this.collections.set(slug, state);\n }\n return state;\n }\n\n private ensureCollection(slug: string): Promise<CollectionState> {\n const state = this.collectionState(slug);\n if (!state.loaded) {\n const scope = this.scope;\n state.loaded = (async () => {\n await this.ensureQueueLoaded();\n const [rows, snapshots, absent] = await Promise.all([\n this.store.listCacheEntries(`${scope}|row|${slug}|`).catch(() => []),\n this.store.listCacheEntries(`${scope}|q|${slug}|`).catch(() => []),\n this.store.listCache(`${scope}|abs|${slug}|`).catch(() => [])\n ]);\n // A scope switch mid-load must not graft the previous user's\n // rows onto the new one.\n if (this.scope !== scope || this.collections.get(slug) !== state) return;\n for (const entry of rows) {\n const row = entry.value as AnyRow | undefined;\n if (!row || row.id === undefined || row.id === null) continue;\n state.rows.set(String(row.id), {\n row: hydrateRow(row),\n cachedAt: entry.cachedAt,\n rev: ++this.revCounter\n });\n }\n for (const entry of snapshots) {\n const key = entry.key.slice(`${scope}|q|${slug}|`.length);\n if (entry.value) state.snapshots.set(key, entry.value as QuerySnapshot);\n }\n for (const entry of absent) {\n state.absent.add(entry.key.slice(`${scope}|abs|${slug}|`.length));\n }\n })().catch(() => undefined).finally(() => { state.ready = true; });\n }\n return state.loaded.then(() => state);\n }\n\n private snapshotFor(slug: string, params?: FindParams): QuerySnapshot | undefined {\n return this.collections.get(slug)?.snapshots.get(buildQueryString(params));\n }\n\n private hasLocalAnswer(state: CollectionState | undefined, slug: string, params?: FindParams): boolean {\n if (!state) return false;\n return state.snapshots.has(buildQueryString(params)) || state.rows.size > 0;\n }\n\n /**\n * Answer a query from the local database.\n *\n * With a snapshot, the server's own page — its ids, order and total — is\n * the skeleton, and the local rows fill it in: rows deleted locally drop\n * out, rows edited locally show the edit, and rows *created* locally join\n * the first page if they match. Without one, the query is evaluated\n * outright over every cached row, which is the best that can be done for a\n * query the server has never answered here.\n */\n private answer<M extends AnyRow>(\n slug: string,\n params: FindParams | undefined,\n snapshot: QuerySnapshot | undefined\n ): LiveResult<M> {\n const state = this.collections.get(slug);\n const exact = isExactlyEvaluable(params);\n const fromCache = !state?.fresh.has(buildQueryString(params));\n if (!state) {\n return {\n data: [],\n meta: { ...resolvePagination(params), total: 0, hasMore: false },\n fromCache: true,\n hasPendingWrites: false,\n partial: true\n };\n }\n\n if (!snapshot) {\n const local = runLocalQuery<M>([...state.rows.values()].map((e) => e.row) as M[], params);\n return {\n ...local,\n fromCache,\n hasPendingWrites: local.data.some((row) => this.hasPending(slug, row.id as string | number)),\n partial: true\n };\n }\n\n const rows: M[] = [];\n const seen = new Set<string>();\n /** Rows the server counted that we know are no longer in the result. */\n let removed = 0;\n for (const id of snapshot.ids) {\n const key = String(id);\n const entry = state.rows.get(key);\n if (!entry) {\n // Gone for a reason (deleted here, or confirmed gone by the\n // server) versus merely evicted to stay under the cache cap:\n // only the former should move the total the server gave us.\n if (state.absent.has(key) || this.hasPending(slug, key)) removed++;\n continue;\n }\n // A local edit that moves a row out of its own filter should take\n // it off the list, exactly as a refetch would.\n if (exact && this.hasPending(slug, key) && !matchesParams(entry.row, params)) {\n removed++;\n continue;\n }\n rows.push(entry.row as M);\n seen.add(key);\n }\n\n // Rows the server has never seen belong on the first page of a\n // matching query. Injecting them into *every* page would show the same\n // new row once per page.\n let added = 0;\n const offset = snapshot.offset ?? 0;\n if (exact && offset === 0) {\n for (const [key, entry] of state.rows) {\n if (seen.has(key) || !this.hasPending(slug, key)) continue;\n if (!this.isLocallyCreated(slug, key)) continue;\n if (!matchesParams(entry.row, params)) continue;\n rows.push(entry.row as M);\n added++;\n }\n }\n\n // Order is part of the query, not a detail of how the rows were\n // obtained. This used to sort only when a locally-created row had been\n // injected — every other read handed back cache order, which is\n // insertion order, and a caller that asked for `orderBy` got whatever\n // the store happened to hold. In the admin that is the collection's\n // `sort` being silently ignored on every list backed by this overlay:\n // the query carries it, the server honours it, and the answer served\n // from here did not.\n //\n // …but only when the local sort would land where the server's did.\n // `snapshot.ids` already arrived in the server's order, so re-sorting a\n // text column with `Intl.Collator` *replaces* a correct order with a\n // possibly different one — under the C collation Postgres puts\n // `Banana` before `apple` and the collator does not. When the column\n // cannot be ordered locally the snapshot's order is the better answer,\n // and the result says so rather than presenting it as the sorted page\n // that was asked for.\n const orderIsLocal = isLocallySortable(rows, params?.orderBy);\n if (params?.orderBy && orderIsLocal) sortRows(rows, params.orderBy);\n\n const total = Math.max(rows.length, snapshot.total - removed + added);\n return {\n data: rows,\n meta: {\n total,\n limit: snapshot.limit,\n offset,\n hasMore: snapshot.hasMore\n },\n fromCache,\n hasPendingWrites: rows.some((row) => this.hasPending(slug, row.id as string | number)),\n // Not the page that was asked for if either the membership\n // decision or the order could not be reproduced here.\n partial: !exact || !orderIsLocal\n };\n }\n\n private localFind<M extends AnyRow>(slug: string, params?: FindParams): LiveResult<M> {\n const state = this.collections.get(slug);\n const snapshot = this.snapshotFor(slug, params);\n // However recent it looks, this answer did not come from the server.\n state?.fresh.delete(buildQueryString(params));\n if (!snapshot && (!state || state.rows.size === 0)) {\n throw offlineError(`Offline: no cached data for \"${slug}\".`);\n }\n const answer = this.answer<M>(slug, params, snapshot);\n return snapshot ? answer : { ...answer, partial: true };\n }\n\n private rawLocalRow(slug: string, id: string | number): AnyRow | undefined {\n const entry = this.collections.get(slug)?.rows.get(String(id));\n return entry ? { ...entry.row } : undefined;\n }\n\n private localRow<M extends AnyRow>(slug: string, id: string | number): M | undefined {\n return this.collections.get(slug)?.rows.get(String(id))?.row as M | undefined;\n }\n\n // ─── Writing the local database ──────────────────────────────────────────\n\n private setLocalRow(slug: string, id: string | number, row: AnyRow): void {\n const state = this.collectionState(slug);\n const key = String(id);\n const cachedAt = Date.now();\n state.rows.set(key, { row: { ...row }, cachedAt, rev: ++this.revCounter });\n state.freshRows.delete(key);\n this.forgetTombstone(slug, key);\n void this.writeCache(this.rowKey(slug, key), dehydrateRow(row), cachedAt);\n this.evictRows(slug);\n }\n\n /**\n * Drop a row and, when the server is the one saying it is gone, remember\n * that. \"I looked it up and it does not exist\" is real knowledge: without\n * it, opening a deleted row while offline would report a missing local\n * database instead of a missing row.\n */\n private removeLocalRow(slug: string, id: string | number, known = false): void {\n const state = this.collectionState(slug);\n const key = String(id);\n const existed = state.rows.delete(key);\n if (known) {\n state.absent.add(key);\n state.freshRows.add(key);\n void this.writeCache(this.absentKey(slug, key), true);\n } else {\n state.freshRows.delete(key);\n }\n if (existed) void this.deleteCache([this.rowKey(slug, key)]);\n }\n\n private forgetTombstone(slug: string, key: string): void {\n const state = this.collectionState(slug);\n if (!state.absent.delete(key)) return;\n void this.deleteCache([this.absentKey(slug, key)]);\n }\n\n /**\n * Merge server rows into the local database. A row with unsynced local\n * writes keeps them: the server's copy is the base the queued mutations\n * are re-applied to, not a replacement for what the user did.\n *\n * Rows that came back unchanged keep their identity and revision, so a\n * refetch that changed nothing does not re-render every live query that\n * touches them — or rewrite them all to disk.\n */\n private async ingest(slug: string, rows: AnyRow[]): Promise<void> {\n if (rows.length === 0) return;\n const state = await this.ensureCollection(slug);\n const cachedAt = Date.now();\n const writes: { key: string; entry: { value: unknown; cachedAt: number } }[] = [];\n const deletes: string[] = [];\n for (const raw of rows) {\n if (!raw || raw.id === undefined || raw.id === null) continue;\n const key = String(raw.id);\n const merged = this.hasPending(slug, key)\n ? this.applyPendingToRow(slug, key, { ...raw })\n : { ...raw };\n if (merged === undefined) {\n // A queued delete says this row is gone; do not resurrect it.\n state.rows.delete(key);\n deletes.push(this.rowKey(slug, key));\n continue;\n }\n this.forgetTombstone(slug, key);\n state.freshRows.add(key);\n const existing = state.rows.get(key);\n if (existing && JSON.stringify(existing.row) === JSON.stringify(merged)) {\n existing.cachedAt = cachedAt;\n continue;\n }\n state.rows.set(key, { row: merged, cachedAt, rev: ++this.revCounter });\n writes.push({ key: this.rowKey(slug, key), entry: { value: dehydrateRow(merged), cachedAt } });\n }\n if (writes.length > 0) void this.store.setCacheMany(writes).catch(() => undefined);\n if (deletes.length > 0) void this.deleteCache(deletes);\n this.evictRows(slug);\n }\n\n /**\n * Fold the queued mutations for one row over a base, newest last.\n * `afterMutationId` skips everything up to and including that mutation,\n * which is how a just-replayed write avoids being applied on top of the\n * server's response to it.\n */\n private applyPendingToRow(\n slug: string,\n idKey: string,\n base: AnyRow | undefined,\n afterMutationId?: string\n ): AnyRow | undefined {\n let row = base;\n let skipping = afterMutationId !== undefined;\n for (const op of this.queue) {\n if (skipping) {\n if (op.mutationId === afterMutationId) skipping = false;\n continue;\n }\n if (op.collection !== slug) continue;\n if (op.type === \"createMany\") {\n const match = (op.data as AnyRow[] | undefined)?.find((r) => String(r.id) === idKey);\n if (match) row = { ...match };\n continue;\n }\n if (op.id === undefined || String(op.id) !== idKey) continue;\n if (op.type === \"create\") row = { ...(op.data as AnyRow) };\n else if (op.type === \"update\") row = { ...(row ?? {}), ...(op.data as AnyRow), id: op.id };\n else if (op.type === \"delete\") row = undefined;\n }\n return row;\n }\n\n private recordSnapshot(slug: string, params: FindParams | undefined, result: FindResult<AnyRow>): QuerySnapshot {\n const window = resolvePagination(params);\n const meta = result.meta ?? { total: result.data?.length ?? 0, ...window, hasMore: false };\n const snapshot: QuerySnapshot = {\n ids: (result.data ?? []).map((row) => row.id as string | number).filter((id) => id !== undefined),\n total: meta.total ?? result.data?.length ?? 0,\n limit: meta.limit ?? window.limit,\n offset: meta.offset ?? window.offset,\n hasMore: meta.hasMore ?? false\n };\n const state = this.collectionState(slug);\n const key = buildQueryString(params);\n state.snapshots.set(key, snapshot);\n state.fresh.add(key);\n void this.writeCache(`${this.scope}|q|${slug}|${key}`, snapshot);\n this.evictSnapshots(slug);\n return snapshot;\n }\n\n /**\n * A write changed which rows belong in a list, and only the server can say\n * how — a row it generated is in no cached page, and the totals moved.\n * Re-run every live query on the collection; queries nobody is watching\n * are corrected by their next `find`.\n *\n * Coalesced per microtask so a burst of writes costs one round trip, and\n * skipped entirely while offline, where the local database is already the\n * best answer available.\n */\n private scheduleRefresh(slug: string): void {\n if (this.refreshPending.has(slug)) return;\n const observers = this.observers.get(slug);\n if (!observers || observers.size === 0) return;\n this.refreshPending.add(slug);\n void Promise.resolve().then(() => {\n this.refreshPending.delete(slug);\n if (this.disposed || !this.connectivity.shouldAttempt()) return;\n for (const observer of [...(this.observers.get(slug) ?? [])]) void observer.refresh();\n });\n }\n\n private evictRows(slug: string): void {\n const state = this.collections.get(slug);\n if (!state || state.rows.size <= this.maxCachedRows) return;\n const evictable = [...state.rows.entries()]\n .filter(([key]) => !this.hasPending(slug, key))\n .sort((a, b) => a[1].cachedAt - b[1].cachedAt);\n const excess = state.rows.size - this.maxCachedRows;\n const doomed = evictable.slice(0, excess);\n for (const [key] of doomed) state.rows.delete(key);\n if (doomed.length > 0) void this.deleteCache(doomed.map(([key]) => this.rowKey(slug, key)));\n\n // Tombstones are tiny but unbounded — every row the app ever asked for\n // and did not find leaves one. Cap them against the same budget.\n if (state.absent.size > this.maxCachedRows) {\n const stale = [...state.absent].slice(0, state.absent.size - this.maxCachedRows);\n for (const key of stale) state.absent.delete(key);\n void this.deleteCache(stale.map((key) => this.absentKey(slug, key)));\n }\n }\n\n private evictSnapshots(slug: string): void {\n const state = this.collections.get(slug);\n if (!state || state.snapshots.size <= this.maxCachedQueries) return;\n // Insertion order is recency order for a Map that re-sets on write.\n const excess = state.snapshots.size - this.maxCachedQueries;\n const doomed = [...state.snapshots.keys()].slice(0, excess);\n for (const key of doomed) state.snapshots.delete(key);\n void this.deleteCache(doomed.map((key) => `${this.scope}|q|${slug}|${key}`));\n }\n\n // ─── Queue ───────────────────────────────────────────────────────────────\n\n private ensureQueueLoaded(): Promise<void> {\n if (!this.queueLoad) {\n const scope = this.scope;\n this.queueLoad = this.store.listQueue(`${scope}|`).then((queue) => {\n // A scope switch during the load must not graft the old\n // user's queue onto the new one.\n if (this.scope !== scope) return;\n this.queue = queue;\n this.patchStatus({ pending: queue.length });\n this.notifyQueue();\n }).catch(() => undefined);\n }\n return this.queueLoad;\n }\n\n private enqueue(mutation: Omit<PendingMutation, \"mutationId\" | \"queuedAt\">): Promise<void> {\n const result = this.enqueueChain.then(async () => {\n await this.ensureQueueLoaded();\n\n // Tail coalescing: repeated edits to the most recently written row\n // (typing in a form) collapse into the queued op instead of\n // growing the queue. Only the queue *tail* may absorb an update —\n // merging into an earlier op would move this write across ops\n // queued after it, silently reordering what the app did.\n if (mutation.type === \"update\") {\n const tail = this.queue[this.queue.length - 1];\n if (tail\n && tail.mutationId !== this.inFlightId\n && tail.collection === mutation.collection\n && (tail.type === \"create\" || tail.type === \"update\")\n && tail.id === mutation.id) {\n // The id must survive the merge: a queued create carries\n // the client-generated id inside its data. The rollback\n // stays the tail's — the state before the *first* of the\n // merged writes, which is what undoing them all restores.\n tail.data = { ...(tail.data as AnyRow), ...(mutation.data as AnyRow), id: tail.id };\n await this.store.enqueue(this.queueKey(tail), tail);\n return;\n }\n }\n\n // Cancel-out: deleting a row whose create is still queued — and\n // whose id the SDK generated, so the server cannot already have a\n // row under it — means the server never saw the row. Remove every\n // queued op for it and queue nothing. Creates with caller-supplied\n // ids do NOT cancel (the id may name an existing server row, which\n // the delete must still remove), and neither do rows queued inside\n // a createMany (the bulk op replays first, then the delete).\n if (mutation.type === \"delete\") {\n // An in-flight create disqualifies the shortcut entirely: the\n // server is being told about the row as we speak, so \"it never\n // saw it\" is false and the delete has to replay after it.\n const hasPendingCreate = this.queue.some((m) =>\n m.collection === mutation.collection && m.type === \"create\"\n && m.id === mutation.id && m.generatedId === true\n && m.mutationId !== this.inFlightId);\n if (hasPendingCreate) {\n const doomed = this.queue.filter((m) =>\n m.collection === mutation.collection\n && m.id === mutation.id\n && (m.type === \"create\" || m.type === \"update\")\n && m.mutationId !== this.inFlightId);\n for (const op of doomed) await this.store.dequeue(this.queueKey(op));\n this.queue = this.queue.filter((m) => !doomed.includes(m));\n this.afterQueueChange();\n return;\n }\n }\n\n const full: PendingMutation = {\n ...mutation,\n mutationId: createMutationId(),\n queuedAt: Date.now()\n };\n await this.store.enqueue(this.queueKey(full), full);\n this.queue.push(full);\n this.afterQueueChange();\n });\n // The chain must survive a failed enqueue, or every later write dies\n // on the same stale rejection.\n this.enqueueChain = result.catch(() => undefined);\n return result;\n }\n\n private hasPending(slug: string, id: string | number): boolean {\n const key = String(id);\n return this.queue.some((op) => {\n if (op.collection !== slug) return false;\n if (op.type === \"createMany\") {\n return (op.data as AnyRow[] | undefined)?.some((r) => String(r.id) === key) ?? false;\n }\n return op.id !== undefined && String(op.id) === key;\n });\n }\n\n /** Is this row one the server has never been told about? */\n private isLocallyCreated(slug: string, idKey: string): boolean {\n return this.queue.some((op) => {\n if (op.collection !== slug) return false;\n if (op.type === \"create\") return op.id !== undefined && String(op.id) === idKey;\n if (op.type === \"createMany\") {\n return (op.data as AnyRow[] | undefined)?.some((r) => String(r.id) === idKey) ?? false;\n }\n return false;\n });\n }\n\n /** How many rows the queue adds to (or removes from) a server-side count. */\n private pendingDelta(slug: string, params?: FindParams): number {\n if (!isExactlyEvaluable(params)) return 0;\n let delta = 0;\n for (const op of this.queue) {\n if (op.collection !== slug) continue;\n if (op.type === \"create\") {\n if (matchesParams(op.data as AnyRow, params)) delta++;\n } else if (op.type === \"createMany\") {\n for (const row of (op.data as AnyRow[] | undefined) ?? []) {\n if (matchesParams(row, params)) delta++;\n }\n } else if (op.type === \"delete\") {\n const before = op.rollback?.rows?.[String(op.id)];\n if (before && matchesParams(before, params)) delta--;\n }\n }\n return delta;\n }\n\n // ─── Replay ──────────────────────────────────────────────────────────────\n\n sync(): Promise<{ flushed: number; remaining: number }> {\n if (this.flushPromise) return this.flushPromise;\n this.flushPromise = this.withLock(() => this.flush())\n .finally(() => { this.flushPromise = undefined; });\n return this.flushPromise;\n }\n\n private async flush(): Promise<{ flushed: number; remaining: number }> {\n await this.ensureQueueLoaded();\n // Another tab may have queued or drained work since we last looked.\n await this.reloadQueue();\n if (this.queue.length === 0) return { flushed: 0, remaining: 0 };\n // No `shouldAttempt` guard: every caller of `sync` — the app, the\n // retry timer, an `online` event, a sign-in — is asking for a real\n // attempt, and its outcome is what reopens the connection.\n\n this.patchStatus({ syncing: true });\n const touched = new Set<string>();\n const queuedAtStart = this.queue.length;\n let flushed = 0;\n try {\n while (this.queue.length > 0 && !this.disposed) {\n const op = this.queue[0];\n touched.add(op.collection);\n // Held across `drop` as well as `replay`: between the ACK and\n // the dequeue the op is still in `queue`, still the tail, and\n // still about to be removed — coalescing into it there loses\n // the write exactly as coalescing during the request does.\n this.inFlightId = op.mutationId;\n try {\n try {\n await this.replay(op);\n } catch (error) {\n if (isNetworkError(error)) {\n // Still offline — keep the op and everything behind it.\n this.connectivity.markFailure();\n break;\n }\n op.attempts = (op.attempts ?? 0) + 1;\n op.lastError = (error as Error)?.message ?? String(error);\n // A key the server is still holding gets a longer\n // budget than a busy server does. The claim outlives\n // the request that took it — the process was killed\n // between the write and its answer — so it is refused\n // until the claim's lease runs out, which is longer\n // than the default five retries reach. Giving up on\n // that schedule rolls back precisely the write the key\n // exists to save.\n const limit = isIdempotencyInProgressError(error)\n ? Math.max(this.maxRetries, IN_PROGRESS_MIN_RETRIES)\n : this.maxRetries;\n if (isRetryableError(error) && op.attempts < limit) {\n // The server is busy, not unhappy. Keep the op — and\n // its place in line, since later writes may depend on\n // it — and come back after a backoff.\n await this.store.enqueue(this.queueKey(op), op).catch(() => undefined);\n this.connectivity.deferRetry();\n this.patchStatus({ lastError: op.lastError });\n break;\n }\n await this.rejectMutation(op, error as Error);\n continue;\n }\n this.connectivity.markSuccess();\n await this.drop(op);\n flushed++;\n } finally {\n this.inFlightId = null;\n }\n }\n } finally {\n this.patchStatus({ syncing: false });\n }\n\n if (this.queue.length !== queuedAtStart) {\n for (const slug of touched) {\n this.notifyCollection(slug);\n // The server has now seen these writes, and its page\n // composition and totals moved with them.\n this.scheduleRefresh(slug);\n }\n // One message for the whole drain — including a drain that only\n // rolled writes back, which other tabs need to hear about just as\n // much as one that succeeded.\n this.broadcast({ type: \"queue\" });\n }\n if (this.queue.length === 0) this.patchStatus({ lastSyncedAt: Date.now() });\n return { flushed, remaining: this.queue.length };\n }\n\n private async replay(op: PendingMutation): Promise<void> {\n const inner = this.innerFor(op.collection);\n if (op.type === \"create\") {\n // The queued row already carries its (client-generated) id.\n let row: AnyRow | undefined;\n try {\n // The mutation id names this write, so a server that stores keys\n // recognises a replay instead of inserting a second row. This is\n // the only defence for a table with a server-assigned id: the id\n // the client chose was never used, so a duplicate is invisible\n // from here. Ignored by servers that do not support it.\n row = await inner.create(op.data as AnyRow, undefined, { idempotencyKey: op.mutationId });\n } catch (error) {\n // A lost response, not a rejection. The request reached the\n // server and committed; only the ACK went missing, so the\n // replay finds the row already there.\n //\n // Restricted to ids the SDK minted: a fresh uuid cannot name a\n // row anyone else created, so a duplicate under it is\n // necessarily this mutation's own first attempt. A\n // caller-supplied id carries no such guarantee — it may well\n // collide with a row that was already there, which is a real\n // conflict the caller has to hear about.\n //\n // Without this, `rejectMutation` rolled the write back and\n // DELETED the local row — the one case where the row does exist\n // on the server. The user watched their own saved record vanish.\n if (!(op.generatedId === true && isDuplicateKeyError(error))) throw error;\n row = await inner.findById(op.id!).catch(() => undefined) as AnyRow | undefined;\n // The read can fail on its own (offline again, RLS). The row is\n // known to exist, so keep the local copy rather than rolling\n // back; the next refresh reconciles it.\n if (!row) return;\n }\n await this.adoptServerRow(op, op.id, row);\n } else if (op.type === \"createMany\") {\n const queued = (op.data as AnyRow[]) ?? [];\n // The mutation id names this batch, exactly as it names a single\n // `create` above — and it matters more here. Without it, a batch\n // whose ACK went missing replays as a second genuine import and\n // duplicates every row it holds, not one. `upsert` masked that for\n // the callers who set it; nothing covered the ones who did not.\n const rows = await inner.createMany(queued, {\n ...(op.upsert ? { upsert: true } : {}),\n idempotencyKey: op.mutationId\n });\n for (let i = 0; i < rows.length; i++) {\n await this.adoptServerRow(op, queued[i]?.id as string | number | undefined, rows[i]);\n }\n } else if (op.type === \"updateMany\") {\n const queued = op.updates ?? [];\n // Keyed like every other replay: an update re-applied in full is\n // naturally idempotent, but one interleaved with another writer's is\n // not, and a lost ACK would otherwise re-apply a stale batch over\n // newer data.\n const rows = await inner.updateMany(\n queued.map(u => ({ id: u.id,\ndata: u.data as AnyRow })),\n { idempotencyKey: op.mutationId }\n );\n for (let i = 0; i < rows.length; i++) {\n await this.ingestReplaced(op, queued[i].id, rows[i]);\n }\n } else if (op.type === \"update\") {\n const row = await inner.update(op.id!, op.data as AnyRow);\n await this.ingestReplaced(op, op.id!, row);\n } else if (op.type === \"deleteMany\") {\n const ids = op.ids ?? [];\n await inner.deleteMany(ids, { idempotencyKey: op.mutationId });\n for (const id of ids) this.removeLocalRow(op.collection, id, true);\n } else if (op.type === \"delete\") {\n await inner.delete(op.id!);\n this.removeLocalRow(op.collection, op.id!, true);\n }\n }\n\n /**\n * Take the server's version of a row the client created offline.\n *\n * The server may have assigned a different id — a serial column ignores\n * the id we invented — in which case every local trace of the temporary id\n * has to move with it, including queued writes that were made against it\n * before it was ever sent.\n */\n private async adoptServerRow(\n op: PendingMutation,\n localId: string | number | undefined,\n row: AnyRow | undefined\n ): Promise<void> {\n if (!row) return;\n const slug = op.collection;\n const serverId = row.id as string | number | undefined;\n if (localId !== undefined && serverId !== undefined && String(serverId) !== String(localId)) {\n const oldKey = String(localId);\n this.removeLocalRow(slug, localId);\n for (const queued of this.queue) {\n if (queued.collection !== slug) continue;\n let dirty = false;\n if (queued.id !== undefined && String(queued.id) === oldKey) {\n queued.id = serverId;\n if (queued.data && !Array.isArray(queued.data)) {\n (queued.data as AnyRow).id = serverId;\n }\n dirty = true;\n }\n // The rollback map is keyed by row id too, and restoring it\n // under a name the server never had would resurrect a ghost.\n const rollbackRows = queued.rollback?.rows;\n if (rollbackRows && oldKey in rollbackRows) {\n rollbackRows[String(serverId)] = rollbackRows[oldKey];\n delete rollbackRows[oldKey];\n dirty = true;\n }\n if (dirty) await this.store.enqueue(this.queueKey(queued), queued).catch(() => undefined);\n }\n }\n await this.ingestReplaced(op, serverId ?? localId!, row);\n }\n\n /**\n * Write a server row over the local one, ignoring the mutation that just\n * produced it — re-applying that would put the pre-server values back on\n * top of the server's answer — but keeping every write queued *after* it.\n * Those are still unsent, and dropping them here would make the row snap\n * back to the server's version in front of the user, only to change again\n * when they replay a moment later.\n */\n private async ingestReplaced(op: PendingMutation, id: string | number, row: AnyRow): Promise<void> {\n const slug = op.collection;\n const state = await this.ensureCollection(slug);\n const key = String(id);\n const merged = this.applyPendingToRow(slug, key, { ...row }, op.mutationId);\n if (merged === undefined) {\n // A queued delete is still waiting behind this write.\n this.removeLocalRow(slug, key);\n return;\n }\n const cachedAt = Date.now();\n state.rows.set(key, { row: merged, cachedAt, rev: ++this.revCounter });\n if (this.applyPendingToRow(slug, key, undefined, op.mutationId) === undefined) {\n // Nothing local is left on top of it, so this *is* the server's row.\n state.freshRows.add(key);\n }\n void this.writeCache(this.rowKey(slug, key), dehydrateRow(merged), cachedAt);\n }\n\n /**\n * The server refused a mutation. Put back what it changed, and discard the\n * queued writes that were built on top of it: an edit to a row whose\n * creation was rejected can only fail the same way, and applying it would\n * leave the local database claiming a row the server does not have.\n *\n * The cascade stops the moment a later write stops *depending* on the\n * rejected one. An `update` reads the row it edits, so it is doomed with\n * it; a `create` overwrites the row outright and a `delete` needs nothing\n * of it, so both stand on their own and are kept — dropping them would\n * silently lose writes the server would have accepted.\n */\n private async rejectMutation(op: PendingMutation, error: Error): Promise<void> {\n const ids = new Set(Object.keys(op.rollback?.rows ?? {}));\n if (op.id !== undefined) ids.add(String(op.id));\n\n const doomed: PendingMutation[] = [op];\n const orphaned = new Set(ids);\n const position = this.queue.indexOf(op);\n for (const later of this.queue.slice(position + 1)) {\n if (later.collection !== op.collection) continue;\n const hit = this.idsOf(later).filter((id) => orphaned.has(id));\n if (hit.length === 0) continue;\n if (later.type === \"update\") doomed.push(later);\n else for (const id of hit) orphaned.delete(id);\n }\n\n for (const dropped of doomed) await this.drop(dropped);\n\n for (const [idKey, previous] of Object.entries(op.rollback?.rows ?? {})) {\n // With the doomed writes gone, whatever survives in the queue is\n // what the row should still look like on top of the restored base.\n const restored = this.applyPendingToRow(op.collection, idKey, previous ?? undefined);\n if (restored === undefined) this.removeLocalRow(op.collection, idKey);\n else this.setLocalRow(op.collection, idKey, restored);\n }\n\n this.patchStatus({ lastError: error.message });\n this.notifyCollection(op.collection);\n this.scheduleRefresh(op.collection);\n for (const dropped of doomed) this.onSyncError?.(error, dropped);\n }\n\n /** Every row id a mutation writes to. */\n private idsOf(op: PendingMutation): string[] {\n if (op.type === \"createMany\") {\n return ((op.data as AnyRow[] | undefined) ?? []).map((r) => String(r.id));\n }\n return op.id === undefined ? [] : [String(op.id)];\n }\n\n private async drop(op: PendingMutation): Promise<void> {\n await this.store.dequeue(this.queueKey(op)).catch(() => undefined);\n this.queue = this.queue.filter((m) => m.mutationId !== op.mutationId);\n // No broadcast per item: draining a queue of fifty would be fifty\n // messages to every other tab. The flush announces itself once, at the end.\n this.afterQueueChange(false);\n }\n\n /** Replay uses unwrapped clients: a failure must never re-enqueue itself. */\n private innerFor(slug: string): SDKCollectionClient<AnyRow> {\n let inner = this.inners.get(slug);\n if (!inner) {\n inner = this.createInner(slug);\n this.inners.set(slug, inner);\n }\n return inner;\n }\n\n private async withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as { navigator?: { locks?: LockManager } }).navigator?.locks;\n // Two tabs replaying the same queue would each send every mutation.\n if (!locks?.request) return fn();\n try {\n return await locks.request(`rebase-offline-sync:${this.scope}`, fn) as T;\n } catch {\n // A browser that denies the lock (or a policy that blocks it) must\n // not stop the queue from draining at all.\n return fn();\n }\n }\n\n // ─── Cross-tab ───────────────────────────────────────────────────────────\n\n private broadcast(message: { type: \"rows\"; slugs: string[] } | { type: \"queue\" }): void {\n if (!this.channel) return;\n try {\n this.channel.postMessage({ ...message, scope: this.scope, sender: this.tabId });\n } catch {\n // Structured-clone failures here would only cost cross-tab freshness.\n }\n }\n\n private onBroadcast(message: unknown): void {\n if (this.disposed || !message || typeof message !== \"object\") return;\n const msg = message as { type?: string; scope?: string; sender?: string; slugs?: string[] };\n if (msg.sender === this.tabId || msg.scope !== this.scope) return;\n if (msg.type === \"rows\") {\n for (const slug of msg.slugs ?? []) void this.reloadCollection(slug);\n } else if (msg.type === \"queue\") {\n void this.reloadQueue();\n }\n }\n\n /** Re-read one collection from the store, replacing what is in memory. */\n private async reloadCollection(slug: string): Promise<void> {\n const state = this.collections.get(slug);\n if (!state?.loaded) return; // never loaded here — nothing to keep fresh\n await this.reloadQueue();\n const scope = this.scope;\n const [rows, snapshots, absent] = await Promise.all([\n this.store.listCacheEntries(`${scope}|row|${slug}|`).catch(() => []),\n this.store.listCacheEntries(`${scope}|q|${slug}|`).catch(() => []),\n this.store.listCache(`${scope}|abs|${slug}|`).catch(() => [])\n ]);\n if (this.scope !== scope || this.collections.get(slug) !== state) return;\n const next = new Map<string, RowEntry>();\n for (const entry of rows) {\n const row = entry.value as AnyRow | undefined;\n if (!row || row.id === undefined || row.id === null) continue;\n const key = String(row.id);\n const existing = state.rows.get(key);\n const hydrated = hydrateRow(row);\n // Keep the previous revision when nothing actually changed, so a\n // cross-tab ping does not re-render every observer.\n const unchanged = existing && JSON.stringify(existing.row) === JSON.stringify(hydrated);\n next.set(key, {\n row: hydrated,\n cachedAt: entry.cachedAt,\n rev: unchanged ? existing!.rev : ++this.revCounter\n });\n }\n state.rows = next;\n state.snapshots = new Map();\n for (const entry of snapshots) {\n const key = entry.key.slice(`${scope}|q|${slug}|`.length);\n if (entry.value) state.snapshots.set(key, entry.value as QuerySnapshot);\n }\n state.absent = new Set(absent.map((entry) => entry.key.slice(`${scope}|abs|${slug}|`.length)));\n this.notifyCollection(slug, false);\n }\n\n private async reloadQueue(): Promise<void> {\n const scope = this.scope;\n const queue = await this.store.listQueue(`${scope}|`).catch(() => undefined);\n if (!queue || this.scope !== scope) return;\n this.queue = queue;\n this.afterQueueChange(false);\n }\n\n // ─── Notifications ───────────────────────────────────────────────────────\n\n private afterQueueChange(broadcast = true): void {\n this.patchStatus({ pending: this.queue.length });\n this.notifyQueue();\n if (broadcast) this.broadcast({ type: \"queue\" });\n }\n\n private notifyQueue(): void {\n for (const listener of this.queueListeners) listener(this.queue.length);\n }\n\n private patchStatus(patch: Partial<OfflineStatus>): void {\n let changed = false;\n for (const [key, value] of Object.entries(patch) as [keyof OfflineStatus, never][]) {\n if (this.currentStatus[key] !== value) {\n this.currentStatus[key] = value;\n changed = true;\n }\n }\n if (!changed) return;\n const snapshot = { ...this.currentStatus };\n for (const listener of this.statusListeners) listener(snapshot);\n }\n\n // ─── Store keys and access ───────────────────────────────────────────────\n\n private countKey(slug: string, params?: FindParams): string {\n return `${this.scope}|count|${slug}|${buildQueryString(params)}`;\n }\n\n private rowKey(slug: string, id: string | number): string {\n return `${this.scope}|row|${slug}|${String(id)}`;\n }\n\n private absentKey(slug: string, id: string | number): string {\n return `${this.scope}|abs|${slug}|${String(id)}`;\n }\n\n private queueKey(mutation: PendingMutation): string {\n return `${this.scope}|${mutation.mutationId}`;\n }\n\n private async readCache<T>(key: string): Promise<T | undefined> {\n try {\n const entry = await this.store.getCache(key);\n return entry?.value as T | undefined;\n } catch {\n // A broken cache read must degrade to \"no cache\", never break the app.\n return undefined;\n }\n }\n\n private async writeCache(key: string, value: unknown, cachedAt = Date.now()): Promise<void> {\n try {\n await this.store.setCache(key, { value, cachedAt });\n } catch {\n // Quota errors and private-browsing restrictions must not fail the\n // read or write that got us here.\n }\n }\n\n private async deleteCache(keys: string[]): Promise<void> {\n try {\n await this.store.deleteCache(keys);\n } catch {\n // Same rationale as writeCache.\n }\n }\n}\n","import { createTransport, 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 } from \"./api-keys\";\nimport { CollectionClient, createCollectionClient } 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 { OfflineManager, type OfflineApi, type OfflineConfig } from \"./offline\";\nimport {\n DEFAULT_STORAGE_SOURCE_KEY,\n InsertOf,\n RebaseClient,\n RebaseSdkData,\n RowOf,\n StorageSource,\n StorageSourceDefinition,\n StorageSourceRegistry,\n UpdateOf\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 } 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 { QueryBuilder, or, and, cond } from \"@rebasepro/common\";\n\n// Auth: session/token types, config, and the pluggable storage strategies.\nexport { createCookieStorage, createMemoryStorage } from \"./auth\";\nexport type { AuthConfig, AuthStorage, CookieStorageOptions, CreateAuthOptions } 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 } from \"./admin\";\nexport type { AdminUser } from \"./admin\";\nexport type { CreateCronOptions } from \"./cron\";\nexport { createBackups } from \"./backups\";\nexport type { CreateBackupsOptions } from \"./backups\";\nexport type {\n ApiKeyMasked,\n ApiKeyPermission,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n CreateApiKeysOptions,\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 { OfflineApi, OfflineConfig, OfflineStatus } from \"./offline\";\nexport { isOfflineError } from \"./offline\";\nexport type { LiveResult, ObserveOptions, RowSnapshotMeta } from \"./collection\";\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 /**\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// Resolve a generated `Database` entry from a (kebab-case) slug literal,\n// or `unknown` when the slug isn't in the schema — the extractors below\n// then fall back to the open row / partial shapes.\ntype DBEntry<DB, S extends string> =\n KebabToCamelCase<S> extends keyof DB ? DB[KebabToCamelCase<S>] : unknown;\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 >;\n} & {\n [K in keyof DB]: CollectionClient<RowOf<DB[K]>, InsertOf<DB[K]>, UpdateOf<DB[K]>>;\n} & RebaseSdkData;\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 setToken: (token: string | null) => void;\n setAuthTokenGetter: (getter: () => Promise<string | null>) => void;\n setOnUnauthorized: (handler: () => Promise<boolean>) => void;\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 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 call: <T = unknown>(endpoint: string, payload?: unknown) => Promise<T>;\n collection: <M extends Record<string, unknown> = Record<string, unknown>>(slug: string) => CollectionClient<M>;\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 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 let ws: RebaseWebSocketClient | undefined;\n /** One channel object per name — see `realtime.channel`. */\n const realtimeChannels = new Map<string, RebaseRealtimeChannel>();\n if (resolvedWsUrl) {\n const wsOnUnauthorized = options.onUnauthorized || (() => auth.handleUnauthorized());\n\n ws = new RebaseWebSocketClient({\n websocketUrl: resolvedWsUrl,\n getAuthToken: async () => {\n let session = auth.getSession();\n if (session && session.expiresAt <= Date.now() + 10000) {\n try {\n session = await auth.refreshSession();\n } catch (e) { /* ignore */ }\n }\n return session?.accessToken || options.token || \"\";\n },\n onUnauthorized: wsOnUnauthorized\n });\n\n auth.onAuthStateChange((event, session) => {\n if (!ws) return;\n if (event === \"SIGNED_OUT\") {\n // Not permanent: the client stays usable, and a later subscribe\n // should reconnect anonymously.\n ws.disconnect();\n } else if (event === \"SIGNED_IN\" || event === \"TOKEN_REFRESHED\") {\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 if (session?.accessToken && ws.hasSocket) {\n ws.authenticate(session.accessToken).catch(console.warn);\n }\n }\n });\n }\n\n // Register transport callback for 401s after auth is instantiated.\n // IMPORTANT: We must use transport.setOnUnauthorized() here — NOT set\n // options.onUnauthorized — because the transport was already created above\n // and captured the (undefined) value from the config closure.\n if (!options.onUnauthorized) {\n // `handleUnauthorized` (not a bare `refreshSession`) so that a refresh\n // the server rejects outright drops the session and emits SIGNED_OUT —\n // otherwise the app keeps thinking it is signed in and every view just\n // renders \"Invalid or expired token\".\n transport.setOnUnauthorized(() => auth.handleUnauthorized());\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 OfflineManager(\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 offlineManager.setScope(auth.getSession()?.user?.uid);\n auth.onAuthStateChange((event, session) => {\n offlineManager.setScope(event === \"SIGNED_OUT\" ? undefined : session?.user?.uid);\n });\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 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 (typeof prop === \"string\" && prop !== \"then\" && prop !== \"toJSON\" && prop !== \"$$typeof\") {\n if (options.collections) {\n if (prop in options.collections) {\n return collection(options.collections[prop]);\n }\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 }\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 return undefined;\n }\n });\n\n const target = {\n auth,\n admin,\n cron,\n backups,\n apiKeys,\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 );\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: transport.setToken,\n setAuthTokenGetter: transport.setAuthTokenGetter,\n setOnUnauthorized: transport.setOnUnauthorized,\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 const res = await transport.request<{ data: T }>(`${prefix}${endpoint}`, {\n method: \"POST\",\n body: payload ? JSON.stringify(payload) : undefined\n });\n return res.data ?? (res as T);\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/reviver.ts","../src/transport.ts","../src/auth.ts","../src/admin.ts","../src/cron.ts","../src/backups.ts","../src/api-keys.ts","../src/sdk_query_builder.ts","../src/collection.ts","../src/functions.ts","../src/storage.ts","../src/storage-registry.ts","../src/websocket.ts","../src/realtime-channel.ts","../src/offline-codec.ts","../src/offline-connectivity.ts","../src/offline-store.ts","../src/offline-query.ts","../src/offline.ts","../src/index.ts"],"sourcesContent":["import { EntityReference, EntityRelation, GeoPoint, Vector } from \"@rebasepro/types\";\n\nexport function rebaseReviver(_key: string, value: unknown): unknown {\n if (value && typeof value === \"object\" && \"__type\" in value) {\n const record = value as Record<string, unknown>;\n switch (record.__type) {\n case \"date\":\n case \"Date\": {\n if (typeof record.value !== \"string\") {\n return value;\n }\n const date = new Date(record.value);\n return isNaN(date.getTime()) ? null : date;\n }\n case \"reference\":\n case \"EntityReference\":\n return new EntityReference({\n id: String(record.id),\n path: record.path as string,\n driver: record.driver as string | undefined,\n databaseId: record.databaseId as string | undefined\n });\n case \"relation\":\n case \"EntityRelation\":\n return new EntityRelation(\n record.id as string | number,\n record.path as string,\n record.data as Record<string, unknown> | undefined\n );\n case \"GeoPoint\":\n return new GeoPoint(record.latitude as number, record.longitude as number);\n case \"Vector\":\n return new Vector(record.value as number[]);\n default:\n return value;\n }\n }\n return value;\n}\n","import { FindParams as TypesFindParams, FindResponse as TypesFindResponse, RebaseApiError } from \"@rebasepro/types\";\nimport { serializeFilter, serializeLogicalCondition, serializeOrderBy } from \"@rebasepro/common\";\nimport { rebaseReviver } from \"./reviver\";\n\n// The canonical client error now lives in `@rebasepro/types` so every package\n// (client, auth, …) throws one type. Re-exported here to preserve the historical\n// `import { RebaseApiError } from \".../transport\"` path used across the SDK.\nexport { RebaseApiError } from \"@rebasepro/types\";\nexport type { RebaseErrorInit } from \"@rebasepro/types\";\nimport { RebaseClientError } from \"@rebasepro/types\";\n\nexport interface RebaseClientConfig {\n /**\n * Origin of the Rebase server — scheme, host and port **only**.\n *\n * {@link apiPath} is appended to this, so do not include it here:\n * `\"http://localhost:3001\"` is correct, while `\"http://localhost:3001/api\"`\n * silently builds `/api/api/…` and every request 404s. Omit entirely for\n * same-origin requests from the browser.\n */\n baseUrl?: string;\n /**\n * Bearer token sent as `Authorization` on every request.\n *\n * In the browser this is the signed-in user's access token, so row-level\n * security applies. Server-side callers — scripts, cron jobs, ETL — pass the\n * service key instead, which resolves to `{ uid: \"service\", roles: [\"admin\"] }`\n * and **bypasses RLS**: there is no user to constrain those queries, so scope\n * them explicitly.\n */\n token?: string;\n /**\n * Path the API is mounted under, appended to {@link baseUrl}.\n * Defaults to `\"/api\"`; override only if the server mounts it elsewhere.\n */\n apiPath?: string;\n /**\n * Origin to use instead of {@link baseUrl} for URLs that are handed to the\n * browser to fetch on its own — storage file downloads and previews.\n *\n * API *requests* always go to `baseUrl`; this only changes URLs the SDK\n * *returns* (e.g. `storage.getSignedUrl`). It exists for proxied setups:\n * when `baseUrl` routes through an authenticated middleman (the Rebase\n * console's Studio proxy), a plain `<img src>` or a copied link cannot\n * satisfy the middleman's auth — but the file route itself is reachable\n * directly at the origin server and secured by its own scoped `?token=`.\n * Set this to that server's public origin (no path; {@link apiPath} is\n * appended) and returned file URLs point straight at it.\n */\n storageUrlOrigin?: string;\n fetch?: typeof globalThis.fetch;\n onUnauthorized?: () => Promise<boolean>;\n websocketUrl?: string; // Optional real-time WebSocket connection\n /**\n * Open the realtime WebSocket. **Defaults to `true`.**\n *\n * The socket connects as soon as the client is constructed and keeps the\n * Node event loop alive, so a one-shot script (CLI, cron job, ETL) will not\n * exit on its own. Set this to `false` for any process that reads or writes\n * and then terminates — `.listen()` and `.listenById()` then throw instead\n * of silently doing nothing.\n *\n * Long-lived processes that do want realtime can instead call\n * `client.close()` when shutting down.\n */\n realtime?: boolean;\n /**\n * \"Yes, I meant to be anonymous.\"\n *\n * Off-browser, a client with no credential can only ever call as an\n * anonymous user, and row-level security answers it with whatever is\n * public — usually nothing. That is almost always a mistake in a script or\n * cron job, so the SDK warns once on the first request (see\n * {@link ANONYMOUS_SERVER_CLIENT_WARNING}). Anonymous is a legitimate\n * choice for public reads, though; set this to `true` to say so and\n * silence the warning.\n *\n * Has no effect in the browser, where anonymous-before-sign-in is normal\n * and nothing is ever warned about.\n */\n anonymous?: boolean;\n}\n\n/**\n * Facts about the surrounding client that the transport cannot read off its own\n * config, but needs in order to decide whether a request is *meaningfully*\n * credential-less.\n */\nexport interface TransportEnvironment {\n /**\n * The credential reaches the server without an `Authorization` header —\n * i.e. `auth.authFlowMode: \"cookie\"`, where the refresh token lives in an\n * httpOnly cookie. Such a client looks tokenless to the transport but is\n * not anonymous, so it must never trip the guard.\n */\n credentialOutOfBand?: boolean;\n}\n\n/**\n * True when there is no browser to have signed a user in — a Node script, a\n * cron job, an edge worker.\n *\n * Anonymous is an ordinary, correct state in a browser: before sign-in, on a\n * marketing page, for public reads. Warning there would be noise that teaches\n * people to ignore warnings, so the guard is off entirely. This uses the same\n * `typeof window` test as {@link resolveBaseUrl}, and additionally treats a\n * defined `document` as a browser so an SSR shim or test harness that installs\n * only one of the two is still excluded.\n */\nfunction isServerLikeEnvironment(): boolean {\n return typeof window === \"undefined\" && typeof document === \"undefined\";\n}\n\n/**\n * Emitted once per client. Kept as a constant so the wording is testable and\n * greppable — this is the string a user will paste into a search.\n */\nexport const ANONYMOUS_SERVER_CLIENT_WARNING =\n \"[rebase] This client was created outside a browser with no credential — no `token`, no auth token getter, \"\n + \"and no cookie auth flow — so every request runs as an anonymous caller. Row-level security will return only \"\n + \"publicly readable rows, which is usually nothing and occasionally the wrong thing. \"\n + \"Inside a cron or function handler, use the `client` you were handed instead of building a new one: its data \"\n + \"plane is already admin-scoped. In a standalone script or job, pass the service key as `token`. \"\n + \"If you really do want anonymous access, pass `anonymous: true` to silence this.\";\n\n/**\n * Re-export from `@rebasepro/types` for backward compatibility.\n *\n * Forwards the row type: without the parameter this alias flattened\n * `FindParams<M>` back to its `Record<string, unknown>` default, and `where` /\n * `orderBy` went back to accepting any column name — the alias, not the\n * definition, was where the typing was lost.\n */\nexport type FindParams<M extends Record<string, unknown> = Record<string, unknown>> = TypesFindParams<M>;\nexport type FindResponse<T> = TypesFindResponse<T extends Record<string, unknown> ? T : Record<string, unknown>>;\n\n/**\n * Refuse a filter whose *value* is missing.\n *\n * `where: { status: [\"==\", undefined] }` used to serialize to the literal\n * string, so `status=eq.undefined` went out on the wire and the server dutifully\n * looked for rows whose status is the four-letter word \"undefined\". The caller\n * saw an empty page, not an error — the classic shape of a variable that was\n * never set.\n *\n * Dropping the condition instead would be worse than sending it: the query\n * would come back *unfiltered*, which for an ownership or tenant filter means\n * returning rows the caller never asked to see. So this is a hard error, and\n * both correct spellings are named in the message: omit the key to skip the\n * filter, or use `[\"is-null\", null]` to match SQL NULL (which still\n * serializes — `null` is a value, `undefined` is the absence of one).\n */\nfunction assertNoUndefinedFilterValues(where: Record<string, unknown>): void {\n const reject = (field: string, op: unknown): never => {\n throw new RebaseClientError(\n `Filter on \"${field}\" has an undefined value ([\"${String(op)}\", undefined]). `\n + `Omit \"${field}\" from \\`where\\` to skip the filter, or use [\"is-null\", null] to match SQL NULL.`\n );\n };\n\n for (const [field, condition] of Object.entries(where)) {\n // An entirely absent condition is the documented way to skip a filter.\n if (condition === undefined) continue;\n if (!Array.isArray(condition)) continue;\n\n // Either one `[op, value]` tuple or an array of them.\n const tuples = Array.isArray(condition[0]) ? condition as unknown[][] : [condition as unknown[]];\n for (const tuple of tuples) {\n if (!Array.isArray(tuple) || tuple.length !== 2) continue;\n const [op, value] = tuple;\n if (value === undefined) reject(field, op);\n // `[\"in\", [...]]` — a hole in the list is the same mistake.\n if (Array.isArray(value) && value.some(v => v === undefined)) reject(field, op);\n }\n }\n}\n\nexport function buildQueryString(params?: FindParams): string {\n if (!params) return \"\";\n const parts: string[] = [];\n\n if (params.limit != null) parts.push(`limit=${params.limit}`);\n if (params.offset != null) parts.push(`offset=${params.offset}`);\n if (params.page != null) parts.push(`page=${params.page}`);\n\n if (params.orderBy) {\n const wire = serializeOrderBy(params.orderBy);\n if (wire) parts.push(`orderBy=${encodeURIComponent(wire)}`);\n }\n\n if (params.searchString) {\n parts.push(`searchString=${encodeURIComponent(params.searchString)}`);\n if (params.searchExplain) parts.push(\"searchExplain=true\");\n }\n\n // The server keys vector search off `vector_search` naming the property and\n // `vector` carrying the embedding as a JSON array; both must be present or\n // it ignores the pair entirely.\n if (params.vectorSearch) {\n const vs = params.vectorSearch;\n parts.push(`vector_search=${encodeURIComponent(vs.property)}`);\n parts.push(`vector=${encodeURIComponent(JSON.stringify(vs.vector))}`);\n if (vs.distance) parts.push(`vector_distance=${encodeURIComponent(vs.distance)}`);\n if (vs.threshold !== undefined) parts.push(`vector_threshold=${encodeURIComponent(String(vs.threshold))}`);\n }\n\n if (params.include && params.include.length > 0) {\n parts.push(`include=${encodeURIComponent(params.include.join(\",\"))}`);\n }\n\n if (params.logical) {\n const root = params.logical;\n const serialized = (root.conditions ?? []).map(serializeLogicalCondition).join(\",\");\n parts.push(`${root.type}=${encodeURIComponent(`(${serialized})`)}`);\n }\n\n if (params.where) {\n assertNoUndefinedFilterValues(params.where);\n const serialized = serializeFilter(params.where);\n for (const [field, value] of Object.entries(serialized)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(v)}`);\n }\n } else {\n parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(value)}`);\n }\n }\n }\n\n return parts.length > 0 ? \"?\" + parts.join(\"&\") : \"\";\n}\n\nexport interface Transport {\n request: <T = unknown>(path: string, init?: RequestInit) => Promise<T>;\n setToken: (newToken: string | null) => void;\n setAuthTokenGetter: (getter: () => Promise<string | null>) => void;\n setOnUnauthorized: (handler: () => Promise<boolean>) => void;\n readonly baseUrl: string;\n readonly apiPath: string;\n /** See {@link RebaseClientConfig.storageUrlOrigin}. Undefined = use `baseUrl`. */\n readonly storageUrlOrigin?: string;\n readonly fetchFn: typeof globalThis.fetch;\n getHeaders: (init?: RequestInit) => Record<string, string>;\n resolveToken: () => Promise<string | null>;\n}\n\n/**\n * The base every request and every caller-built URL resolves against.\n *\n * `baseUrl` is optional because the common production shape is a Rebase\n * backend serving its own SPA, where the API is simply the page's origin.\n * Leaving it unset is therefore the *correct* configuration there — and the\n * one that keeps working when a second hostname (a custom domain) points at\n * the same app.\n *\n * When unset in a browser this resolves to the page origin rather than \"\".\n * Requests behave identically either way, but the empty string is a trap for\n * anything that builds a URL from `client.baseUrl`: `new URL(\"\" + path)`\n * throws, so apps \"fixed\" it by baking an absolute host into their bundle —\n * which is exactly what breaks the day a custom domain is added, and which no\n * amount of CORS configuration repairs, because a SameSite=Lax auth cookie is\n * not sent cross-site either.\n */\nfunction resolveBaseUrl(configured?: string): string {\n if (configured) return configured.replace(/\\/$/, \"\");\n if (typeof window !== \"undefined\" && window.location?.origin) return window.location.origin;\n return \"\";\n}\n\nexport function createTransport(config: RebaseClientConfig, environment?: TransportEnvironment): Transport {\n const fetchFn = config.fetch || globalThis.fetch;\n const apiPath = config.apiPath || \"/api\";\n\n // `apiPath` is appended to `baseUrl`, so a `baseUrl` that already ends in it\n // builds `/api/api/…` and every request 404s. That was documented on\n // `baseUrl` and left to be discovered at runtime — including by this\n // package's own tests, which configured it that way a dozen times. A 404 on\n // every call looks like a server that is down, not like a doubled path.\n // `storageUrlOrigin` is checked alongside it because `storage.ts` composes\n // it the same way — `${storageUrlOrigin ?? baseUrl}${apiPath}` — and its own\n // docblock carries the same \"no path\" caveat.\n for (const field of [\"baseUrl\", \"storageUrlOrigin\"] as const) {\n const value = config[field];\n if (!value || !apiPath) continue;\n const trimmed = value.replace(/\\/+$/, \"\");\n if (!trimmed.endsWith(apiPath)) continue;\n console.warn(\n `[Rebase] ${field} ${JSON.stringify(value)} already ends with the API path ` +\n `${JSON.stringify(apiPath)}, which is appended to it — requests will go to ` +\n `${trimmed}${apiPath}/… and 404. Pass the origin only ` +\n `(${JSON.stringify(trimmed.slice(0, trimmed.length - apiPath.length) || \"/\")}), or set ` +\n \"`apiPath` if the server really does mount the API one level deeper.\"\n );\n }\n let token = config.token;\n let tokenGetter: (() => Promise<string | null>) | undefined;\n let onUnauthorizedHandler = config.onUnauthorized;\n /** Once per client, never per request — log spam is its own bug. */\n let anonymousWarningIssued = false;\n\n /**\n * Warn a server-side caller that it built a client that can only ever be\n * anonymous. Deliberately checked at the *first request* rather than at\n * construction: `setToken()` / `setAuthTokenGetter()` and a server-side\n * `auth.signIn…()` (which calls `transport.setToken`) all land after the\n * constructor, and warning at construction would fire on every one of them.\n */\n function warnIfAnonymousServerClient(activeToken: string | undefined): void {\n if (anonymousWarningIssued) return;\n if (activeToken) return; // a credential is being sent\n if (tokenGetter) return; // a credential is being fetched per request\n if (config.anonymous) return; // \"yes, I meant this\"\n if (environment?.credentialOutOfBand) return; // cookie auth flow — credential is not a header\n if (!isServerLikeEnvironment()) return; // browsers are legitimately anonymous\n anonymousWarningIssued = true;\n console.warn(ANONYMOUS_SERVER_CLIENT_WARNING);\n }\n\n function getHeaders(activeToken: string | undefined, init?: RequestInit) {\n return {\n \"Content-Type\": \"application/json\",\n ...(activeToken ? { Authorization: `Bearer ${activeToken}` } : {}),\n ...((init?.headers as Record<string, string>) || {})\n };\n }\n\n /**\n * The refusal for a success status carrying a body this client cannot read.\n *\n * The first 120 characters go in the message because they identify the\n * sender at a glance: `<!doctype html>` says \"you are talking to a web\n * server, not to this API\" faster than any wording here could.\n *\n * One function for both the first attempt and the post-refresh retry — the\n * retry is a second copy of this whole response-reading path, and copies\n * are how one of them ends up fixed and the other not.\n */\n function unreadableResponse(status: number, text: string): RebaseApiError {\n return new RebaseApiError(\n `The server answered ${status} with a body that is not JSON, so there is nothing to return. ` +\n \"This usually means the request reached something other than the Rebase API — a single-page-app \" +\n \"fallback serving index.html, or a proxy error page — so check the API URL configuration \" +\n `(e.g. VITE_API_URL). The body began: ${JSON.stringify(text.slice(0, 120))}`,\n { status, code: \"INVALID_JSON_RESPONSE\" }\n );\n }\n\n async function request<T = unknown>(path: string, init?: RequestInit): Promise<T> {\n const url = resolveBaseUrl(config.baseUrl) + apiPath + path;\n\n let activeToken = token;\n if (tokenGetter) {\n try {\n const fetched = await tokenGetter();\n if (fetched !== null && fetched !== undefined) {\n activeToken = fetched;\n }\n } catch (e) {\n // Ignore error, fallback to static token if any\n }\n }\n\n warnIfAnonymousServerClient(activeToken);\n\n const headers = getHeaders(activeToken, init);\n\n // If passing FormData, we MUST let fetch set the boundary, so remove Content-Type\n if (init?.body instanceof FormData) {\n delete (headers as Record<string, string>)[\"Content-Type\"];\n }\n\n const res = await fetchFn(url, { ...init,\nheaders });\n\n if (res.status === 204) return undefined as T; // SAFETY: HTTP 204 No Content has no body\n\n const text = await res.text().catch(() => \"\");\n let body: Record<string, unknown> = {};\n /**\n * Whether the body was there and could not be read as JSON.\n *\n * On an error status this does not matter — the status is the answer\n * and the message falls back to `statusText`. On a *success* status it\n * is the whole answer, and `{}` was being returned as though the server\n * had sent it: `find()` answered `{}` instead of an array, `getOne()`\n * an empty object, with nothing thrown.\n *\n * The case that produces it is not exotic. Point `VITE_API_URL` at the\n * frontend's own host and `/api/data/posts` lands on the SPA fallback,\n * which answers `200` with `index.html` — so the misconfiguration the\n * 404 branch below spends four lines explaining reaches the caller, in\n * its most common form, as an empty success.\n */\n let unreadableBody = false;\n if (text) {\n try {\n body = JSON.parse(text, rebaseReviver) as Record<string, unknown>;\n } catch (e) {\n unreadableBody = true;\n }\n }\n\n // The server always emits the canonical `{ error: { message, code, details? } }`\n // envelope (formatted by the central errorHandler), so we read strictly\n // from `body.error.*`.\n const getErrorField = (obj: Record<string, unknown>, field: string): unknown => {\n const err = obj?.error;\n if (err && typeof err === \"object\" && err !== null) {\n return (err as Record<string, unknown>)[field];\n }\n return undefined;\n };\n\n if (res.status === 401 && onUnauthorizedHandler) {\n const retried = await onUnauthorizedHandler();\n if (retried) {\n let retryToken = token;\n if (tokenGetter) {\n try {\n const fetched = await tokenGetter();\n if (fetched !== null && fetched !== undefined) {\n retryToken = fetched;\n }\n } catch (e) { /* ignore */ }\n }\n const retryHeaders = getHeaders(retryToken, init) as Record<string, string>;\n const retryRes = await fetchFn(url, { ...init,\nheaders: retryHeaders });\n if (retryRes.status === 204) return undefined as T; // SAFETY: HTTP 204 No Content has no body\n const retryText = await retryRes.text().catch(() => \"\");\n let retryBody: Record<string, unknown> = {};\n let retryUnreadable = false;\n if (retryText) {\n try {\n retryBody = JSON.parse(retryText, rebaseReviver);\n } catch (e) {\n retryUnreadable = true;\n }\n }\n if (!retryRes.ok) {\n let fallbackMessage = retryRes.statusText;\n if (retryRes.status === 404 && !fallbackMessage) {\n const method = init?.method || \"GET\";\n fallbackMessage = `Endpoint not found (${method} ${path}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;\n }\n throw new RebaseApiError(\n String(getErrorField(retryBody, \"message\") || fallbackMessage || `Request failed with status ${retryRes.status}`),\n {\n status: retryRes.status,\n code: getErrorField(retryBody, \"code\") as string | undefined,\n details: getErrorField(retryBody, \"details\")\n }\n );\n }\n if (retryUnreadable) throw unreadableResponse(retryRes.status, retryText);\n return retryBody as T;\n }\n }\n\n if (!res.ok) {\n let fallbackMessage = res.statusText;\n if (res.status === 404 && !fallbackMessage) {\n const method = init?.method || \"GET\";\n fallbackMessage = `Endpoint not found (${method} ${path}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;\n }\n throw new RebaseApiError(\n String(getErrorField(body, \"message\") || fallbackMessage || `Request failed with status ${res.status}`),\n {\n status: res.status,\n code: getErrorField(body, \"code\") as string | undefined,\n details: getErrorField(body, \"details\")\n }\n );\n }\n\n if (unreadableBody) throw unreadableResponse(res.status, text);\n\n return body as T;\n }\n\n return {\n request,\n setToken(newToken: string | null) { token = newToken || undefined; },\n setAuthTokenGetter(getter: () => Promise<string | null>) { tokenGetter = getter; },\n setOnUnauthorized(handler: () => Promise<boolean>) { onUnauthorizedHandler = handler; },\n get baseUrl() { return resolveBaseUrl(config.baseUrl); },\n get apiPath() { return apiPath; },\n get storageUrlOrigin() { return config.storageUrlOrigin?.replace(/\\/$/, \"\") || undefined; },\n get fetchFn() { return fetchFn; },\n getHeaders: (init?: RequestInit) => getHeaders(token, init) as Record<string, string>,\n resolveToken: async () => {\n if (tokenGetter) {\n try {\n const fetched = await tokenGetter();\n if (fetched !== null && fetched !== undefined) {\n return fetched;\n }\n } catch (e) { /* ignore */ }\n }\n return token || null;\n }\n };\n}\n","import { RebaseApiError, Transport } from \"./transport\";\nimport type { 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 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\nexport interface AuthConfig {\n needsSetup: boolean;\n registrationEnabled: boolean;\n emailServiceEnabled?: boolean;\n passwordReset?: boolean;\n emailVerification?: boolean;\n magicLink?: boolean;\n enabledProviders: string[];\n}\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\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 and signing out.\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 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 let resolveInitialized: (value: void | PromiseLike<void>) => void;\n const isInitialized = new Promise<void>((resolve) => {\n resolveInitialized = resolve;\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 function throwApiError(status: number, body: { error?: { message?: string; code?: string; details?: unknown }; message?: string; code?: string; details?: unknown } | undefined, statusText: string): never {\n throw 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 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 function abandonSessionLocally() {\n currentSession = null;\n 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 try {\n await refreshSession();\n return true;\n } catch (err) {\n if (isFatalRefreshError(err)) {\n abandonSessionLocally();\n }\n return false;\n }\n }\n\n async function attemptScheduledRefresh(attempt: number) {\n try {\n await refreshSession();\n // On success, refreshSession() re-schedules the next refresh itself.\n } catch (err) {\n if (isFatalRefreshError(err)) {\n abandonSessionLocally();\n return;\n }\n if (attempt >= MAX_REFRESH_RETRIES) {\n abandonSessionLocally();\n return;\n }\n // Transient failure — back off and retry rather than dropping the session.\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\n function scheduleRefresh(expiresAt: number) {\n if (refreshTimeout) clearTimeout(refreshTimeout);\n if (!autoRefresh) 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 const user: User = mapRawUser(data.user);\n const session: RebaseSession = {\n accessToken: data.tokens.accessToken,\n refreshToken: data.tokens.refreshToken || (currentSession?.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 const session = handleAuthResponse(body, \"SIGNED_IN\");\n return { user: session.user,\naccessToken: session.accessToken,\nrefreshToken: session.refreshToken };\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 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: JSON.stringify({ refreshToken: currentSession?.refreshToken }),\n credentials: authFlowMode === \"cookie\" ? \"include\" : undefined\n } as RequestInit);\n }\n } catch (e) { /* ignore */ }\n currentSession = null;\n clearStoredSession();\n if (refreshTimeout) {\n clearTimeout(refreshTimeout);\n refreshTimeout = null;\n }\n transport.setToken(null);\n emit(\"SIGNED_OUT\", null);\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.\n if (inFlightRefresh) return inFlightRefresh;\n inFlightRefresh = withRefreshLock(() => doRefreshSession()).finally(() => {\n inFlightRefresh = null;\n });\n return inFlightRefresh;\n }\n\n async function doRefreshSession(): Promise<RebaseSession> {\n if (authFlowMode !== \"cookie\" && !currentSession?.refreshToken) {\n throw new Error(\"No active session to refresh\");\n }\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/refresh\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: currentSession?.refreshToken }),\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\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 try {\n user = await getUser();\n } catch { /* fall through to the empty stub below */ }\n }\n\n const session: RebaseSession = {\n accessToken,\n refreshToken: body.tokens.refreshToken || currentSession?.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 async function changePassword(oldPassword: string, newPassword: string) {\n return transport.request<{ success: boolean; message: string; }>(authPath + \"/change-password\", {\n method: \"POST\",\n body: JSON.stringify({ oldPassword,\nnewPassword })\n });\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<{ success: boolean; provider: string; alreadyLinked: boolean; }>(\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 async function verifyEmail(token: string) {\n const fetchFn = getFetch();\n const res = await fetchFn(authUrl(\"/verify-email?token=\" + encodeURIComponent(token)), {\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 { success: boolean; message: string; };\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 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 currentSession = null;\n clearStoredSession();\n if (refreshTimeout) {\n clearTimeout(refreshTimeout);\n refreshTimeout = null;\n }\n transport.setToken(null);\n emit(\"SIGNED_OUT\", null);\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 resolveInitialized!();\n } else if (authFlowMode === \"cookie\" || stored.refreshToken) {\n currentSession = stored;\n refreshSession().then(() => {\n resolveInitialized!();\n }).catch(() => {\n currentSession = null;\n clearStoredSession();\n transport.setToken(null);\n resolveInitialized!();\n });\n } else {\n resolveInitialized!();\n }\n } else if (authFlowMode === \"cookie\") {\n // Silent refresh on boot to pick up httpOnly session\n refreshSession().then(() => {\n resolveInitialized!();\n }).catch(() => {\n resolveInitialized!();\n });\n } else {\n resolveInitialized!();\n }\n } else {\n resolveInitialized!();\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 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 sendMagicLink,\n verifyMagicLink,\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}\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 { AdminUser } from \"@rebasepro/types\";\n\nexport type { AdminUser };\n\n\nexport interface CreateAdminOptions {\n adminPath?: string;\n}\n\nexport function createAdmin(transport: Transport, options?: CreateAdminOptions) {\n const opts = options || {};\n const adminPath = opts.adminPath || \"/admin\";\n\n async function listUsers() {\n return transport.request<{ users: AdminUser[] }>(adminPath + \"/users\", { method: \"GET\" });\n }\n\n async function listUsersPaginated(options?: { search?: string; limit?: number; offset?: number; orderBy?: string; orderDir?: \"asc\" | \"desc\" }) {\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 async function createUser(data: { email: string, displayName?: string, password?: string, roles?: string[] }) {\n return transport.request<{ user: AdminUser }>(adminPath + \"/users\", {\n method: \"POST\",\n body: JSON.stringify(data)\n });\n }\n\n async function updateUser(userId: string, data: { email?: string, displayName?: string, password?: string, roles?: string[] }) {\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 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: Array<{ id: string; name: string }> }>(\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 resetPassword,\n listRoles,\n bootstrap\n };\n}\n","import { Transport } from \"./transport\";\nimport type { CronJobStatus, CronJobLogEntry } from \"@rebasepro/types\";\n\nexport interface CreateCronOptions {\n cronPath?: string;\n}\n\nexport function createCron(transport: Transport, options?: CreateCronOptions) {\n const cronPath = options?.cronPath || \"/cron\";\n\n async function listJobs(): Promise<{ jobs: CronJobStatus[] }> {\n return transport.request<{ jobs: CronJobStatus[] }>(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 async function triggerJob(jobId: string): Promise<{ log: CronJobLogEntry; job: CronJobStatus }> {\n return transport.request<{ log: CronJobLogEntry; job: CronJobStatus }>(\n cronPath + \"/\" + encodeURIComponent(jobId) + \"/trigger\",\n { method: \"POST\" }\n );\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 type { BackupInfo, BackupDestinationKind } from \"@rebasepro/types\";\n\nexport interface CreateBackupsOptions {\n backupsPath?: string;\n}\n\nexport function createBackups(transport: Transport, options?: CreateBackupsOptions) {\n const backupsPath = options?.backupsPath || \"/admin/backups\";\n\n async function list(): Promise<{\n backups: BackupInfo[];\n destinationKind: BackupDestinationKind;\n configured: boolean;\n }> {\n return transport.request(backupsPath, { method: \"GET\" });\n }\n\n /**\n * Download a backup's bytes. Uses an authenticated fetch (not the JSON\n * transport) so the octet-stream response comes back as a Blob.\n */\n async function download(key: string): Promise<Blob> {\n const token = await transport.resolveToken();\n // Mirror transport.request's URL construction (baseUrl + apiPath + path)\n // — this endpoint returns an octet-stream, so we fetch it directly\n // instead of going through the JSON transport.\n const url = `${transport.baseUrl}${transport.apiPath}${backupsPath}/download?key=${encodeURIComponent(key)}`;\n const res = await fetch(url, {\n method: \"GET\",\n headers: token ? { Authorization: `Bearer ${token}` } : {}\n });\n if (!res.ok) {\n throw new Error(`Failed to download backup (${res.status})`);\n }\n return res.blob();\n }\n\n return { list, download };\n}\n","import type { Transport } from \"./transport\";\n\n/**\n * These were re-declared here, under a comment saying they lived in the server\n * package rather than in `@rebasepro/types`. That stopped being true, and the\n * copy drifted: it never gained `admin`, the flag that grants a key the `admin`\n * role — admin routes plus the RLS `default_admin` policies — so the SDK could\n * describe every kind of key except a privileged one, and\n * `createKey({ …, admin: true })` was an excess-property error.\n */\nexport type {\n ApiKeyPermission,\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n UpdateApiKeyRequest\n} from \"@rebasepro/types\";\n\nimport type {\n ApiKeyMasked,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\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 managing API keys via the admin routes.\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","import {\n FindParams,\n FindResult,\n LogicalCondition,\n OrderByTuple,\n SDKCollectionClient,\n SDKQueryBuilderInterface,\n WhereFilterOp,\n WhereValueFor,\n type ComputedSortField\n} from \"@rebasepro/types\";\nimport { normalizeOrderBy } from \"@rebasepro/common\";\n\n/**\n * SDK Query Builder — returns flat rows (`FindResult<M>`) instead of\n * Entity-wrapped results (`FindResponse<M>`).\n *\n * @example\n * const { data } = await rebase.data.posts\n * .where(\"status\", \"==\", \"published\")\n * .orderBy(\"created_at\", \"desc\")\n * .limit(10)\n * .find();\n *\n * console.log(data[0].title); // flat access\n */\nexport class SDKQueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements SDKQueryBuilderInterface<M> {\n // The accumulator stays keyed by plain `string`: it is written through\n // `where()` / `orderBy()` below, whose own parameters are typed against `M`,\n // and a `Partial<Record<FieldPath<M>, …>>` is read-only under a generic `M`\n // (TS2862) so it cannot be built up in place. The typing users see is on the\n // methods; this field is the buffer behind them, cast once on the way out.\n private params: FindParams = { where: {} };\n\n constructor(private collection: SDKCollectionClient<M>) {}\n\n /**\n * Add a filter condition to your query.\n * @example\n * client.data.users.where('age', '>=', 18).find()\n */\n where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;\n where(logicalCondition: LogicalCondition): this;\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown): this {\n if (typeof columnOrCondition === \"object\" && columnOrCondition !== null && \"type\" in columnOrCondition) {\n this.params.logical = columnOrCondition as LogicalCondition;\n return this;\n }\n\n if (!this.params.where) {\n this.params.where = {};\n }\n\n const column = columnOrCondition as string;\n const condition: [WhereFilterOp, unknown] = [operator!, value];\n const existing = this.params.where[column];\n\n if (existing === undefined) {\n this.params.where[column] = condition;\n } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {\n (this.params.where[column] as [WhereFilterOp, unknown][]).push(condition);\n } else {\n let firstCondition: [WhereFilterOp, unknown];\n if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === \"string\") {\n firstCondition = existing as [WhereFilterOp, unknown];\n } else {\n firstCondition = [\"==\", existing];\n }\n this.params.where[column] = [firstCondition, condition];\n }\n\n return this;\n }\n\n /**\n * Order the results by a specific column.\n *\n * Call it again to add a tie-breaker rather than replace the sort: keys\n * apply in the order they were added, so\n * `.orderBy(\"roles\").orderBy(\"created_at\", \"desc\")` sorts by role and\n * shows the newest first within each one.\n */\n orderBy(column: (keyof M & string) | ComputedSortField, direction: \"asc\" | \"desc\" = \"asc\"): this {\n const existing = normalizeOrderBy(this.params.orderBy) ?? [];\n this.params.orderBy = [...existing, [column, direction] as OrderByTuple];\n return this;\n }\n\n /**\n * Limit the number of results returned.\n */\n limit(count: number): this {\n this.params.limit = count;\n return this;\n }\n\n /**\n * Skip the first N results.\n */\n offset(count: number): this {\n this.params.offset = count;\n return this;\n }\n\n /**\n * Set a free-text search string if supported by the backend.\n *\n * By default this is a substring match across the collection's top-level\n * string properties. A Postgres collection that declares a `search` block\n * gets ranked full-text matching over the fields it named instead, and each\n * row comes back with a `_score` you can sort on:\n *\n * ```ts\n * client.data.talents.search(\"auditor iso 14001\").orderBy(\"_score\", \"desc\").find()\n * ```\n *\n * Pass `{ explain: true }` to have each row report which of the declared\n * fields matched, with a highlighted snippet, on `_matches`:\n *\n * ```ts\n * const { data } = await client.data.talents.search(\"iso 14001\", { explain: true }).find();\n * data[0]._matches\n * // [{ field: \"questionnaire.certifications\", snippet: \"<mark>ISO</mark> <mark>14001</mark> Lead Auditor\" }]\n * ```\n */\n search(searchString: string, options?: { explain?: boolean }): this {\n this.params.searchString = searchString;\n if (options?.explain !== undefined) this.params.searchExplain = options.explain;\n return this;\n }\n\n /**\n * Order rows by nearest-neighbour distance to `vector`.\n *\n * The server has supported this from the REST layer since vectors landed;\n * this is the SDK reaching it. Results come back closest-first with a\n * `_distance` on each row, and any `where` / `orderBy` on the same query is\n * a filter applied before the ordering — distance decides the order.\n *\n * You supply the query vector. Rebase stores and searches embeddings; it\n * does not produce them, so this is where whatever model you already use\n * for the stored vectors gets called.\n *\n * @param property - Name of the `vector` property to compare against.\n * @param vector - The query embedding. Its length must match the property's\n * declared `dimensions`, or the server answers 400.\n * @example\n * client.data.docs.vectorSearch(\"embedding\", queryVector, { threshold: 0.35 }).limit(10).find()\n */\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ): this {\n this.params.vectorSearch = {\n property,\n vector,\n ...(options?.distance !== undefined && { distance: options.distance }),\n ...(options?.threshold !== undefined && { threshold: options.threshold })\n };\n return this;\n }\n\n /**\n * Include related entities in the response.\n * Relations will be populated with full data instead of just IDs.\n *\n * @param relations - Relation names to include, or \"*\" for all.\n * @example\n * client.data.posts.include(\"tags\", \"author\").find()\n */\n include(...relations: string[]): this {\n this.params.include = relations;\n return this;\n }\n\n /**\n * Execute the find query and return the results as flat rows.\n */\n async find(): Promise<FindResult<M>> {\n return this.collection.find(this.params as FindParams<M>);\n }\n\n /**\n * Count the records matching this query.\n */\n async count(): Promise<number> {\n if (!this.collection.count) {\n throw new Error(\"count() is not supported by this collection client.\");\n }\n return this.collection.count(this.params as FindParams<M>);\n }\n\n /**\n * Listen to realtime updates matching this query.\n */\n listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void {\n if (!this.collection.listen) {\n throw new Error(\n \"Listen is only available when RebaseClient is configured with a websocketUrl, \" +\n \"and not when it was created with realtime: false.\"\n );\n }\n return this.collection.listen(this.params as FindParams<M>, onUpdate, onError);\n }\n}\n","import { buildQueryString, FindParams, RebaseApiError, Transport } from \"./transport\";\nimport { RebaseWebSocketClient } from \"./websocket\";\nimport {\n FindAllParams,\n FindResult,\n IterateParams,\n LogicalCondition,\n SDKCollectionClient,\n SDKQueryBuilderInterface,\n WhereFilterOp,\n WhereValueFor,\n WriteOptions,\n type ComputedSortField\n} from \"@rebasepro/types\";\nimport { collectAllPages, normalizeOrderBy, paginateFind, resolveFindWindow } from \"@rebasepro/common\";\n\nimport { SDKQueryBuilder } from \"./sdk_query_builder\";\n\n/**\n * Counts currently in flight, keyed by the exact request they issue. Entries\n * live only for the duration of the request — see `count()` for why.\n */\nconst inflightCounts = new Map<string, Promise<number>>();\n\n/**\n * A live query result: a normal {@link FindResult} plus what an interface\n * needs to decide whether to show a \"saving…\" or \"offline\" affordance over it.\n *\n * The three flags are always `false` on a client without offline support —\n * every result there came straight from the server.\n */\nexport interface LiveResult<M extends Record<string, unknown>> extends FindResult<M> {\n /** The data came from the local database, not from a completed request. */\n fromCache: boolean;\n /** At least one row here carries a write the server has not accepted yet. */\n hasPendingWrites: boolean;\n /**\n * The local database may not hold every row the server would have\n * returned, so treat this as a best effort rather than a complete answer.\n */\n partial: boolean;\n /** The most recent revalidation failure, when the last one failed. */\n error?: Error;\n}\n\n/** Snapshot metadata for a single observed row. */\nexport interface RowSnapshotMeta {\n fromCache: boolean;\n hasPendingWrites: boolean;\n}\n\nexport interface ObserveOptions {\n /**\n * Keep the subscription live off the realtime socket, so changes made by\n * other clients arrive without a refetch. On by default whenever realtime\n * is enabled on the client; pass `false` for a one-shot read that still\n * reports offline/pending metadata.\n */\n realtime?: boolean;\n}\n\n/**\n * The concrete, HTTP-backed implementation of the public\n * {@link SDKCollectionClient} contract — flat rows (no Entity wrapper), plus\n * fluent query-builder methods (`.where()`, `.orderBy()`, …).\n *\n * This is what `createRebaseClient().data.<collection>` returns. It is not a\n * separate API from {@link SDKCollectionClient}; it only widens it with\n * `count()` and the reactive `observe()` pair. Program against\n * {@link SDKCollectionClient} when you want a transport-agnostic type.\n */\nexport interface CollectionClient<\n M extends Record<string, unknown> = Record<string, unknown>,\n I = Partial<M>,\n U = Partial<M>\n> extends SDKCollectionClient<M, I, U> {\n count(params?: FindParams<M>): Promise<number>;\n\n /**\n * Subscribe to a query's results.\n *\n * This is the reactive read primitive, and the one to reach for in a UI:\n * unlike `find()` it keeps emitting. On a client with `offline` enabled it\n * is local-first — the first emission comes from the local database, with\n * no request in the way — and re-emits on every local write, every queued\n * write reaching the server, and every rollback. With realtime enabled it\n * also re-emits on changes made by other clients.\n *\n * Emissions are de-duplicated: a refresh that changes nothing does not\n * call back.\n *\n * @returns An unsubscribe function.\n */\n observe(\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ): () => void;\n\n /** {@link CollectionClient.observe} for a single row. */\n observeById(\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ): () => void;\n}\n\nexport function createCollectionClient<M extends Record<string, unknown> = Record<string, unknown>>(transport: Transport, slug: string, ws?: RebaseWebSocketClient): CollectionClient<M> {\n const basePath = `/data/${slug}`;\n\n const client: CollectionClient<M> = {\n async find(params?: FindParams<M>): Promise<FindResult<M>> {\n const qs = buildQueryString(params);\n const raw = await transport.request<{\n data: Record<string, unknown>[];\n meta: FindResult<M>[\"meta\"]\n }>(basePath + qs, { method: \"GET\" });\n return {\n data: (raw.data || []) as M[],\n meta: raw.meta\n };\n },\n\n // The pagination engine lives in `@rebasepro/common`, shared with the\n // in-process accessor: `iterate()` has to mean the same thing whichever\n // transport the caller happens to be holding.\n iterate(params?: IterateParams<M>) {\n return paginateFind<M>((p) => client.find(p), params, slug);\n },\n\n findAll(params?: FindAllParams<M>) {\n return collectAllPages<M>((p) => client.find(p), params, slug);\n },\n\n async findById(id: string | number) {\n try {\n const raw = await transport.request<Record<string, unknown>>(`${basePath}/${encodeURIComponent(String(id))}`, { method: \"GET\" });\n if (!raw) return undefined;\n return raw as M;\n } catch (err) {\n if (err instanceof RebaseApiError && err.status === 404) {\n return undefined;\n }\n throw err;\n }\n },\n\n async create(data: Partial<M>, id?: string | number, options?: WriteOptions) {\n const body: Record<string, unknown> = { ...data };\n if (id !== undefined) {\n body.id = id;\n }\n const raw = await transport.request<Record<string, unknown>>(basePath, {\n method: \"POST\",\n body: JSON.stringify(body),\n ...(options?.idempotencyKey\n ? { headers: { \"Idempotency-Key\": options.idempotencyKey } }\n : {})\n });\n return raw as M;\n },\n\n async createMany(data: Partial<M>[], options?: { upsert?: boolean } & WriteOptions) {\n if (!Array.isArray(data)) {\n throw new TypeError(\"createMany expects an array of records.\");\n }\n if (data.length === 0) return [];\n\n const raw = await transport.request<{ data: Record<string, unknown>[] }>(`${basePath}/bulk`, {\n method: \"POST\",\n body: JSON.stringify({\n rows: data,\n ...(options?.upsert ? { upsert: true } : {})\n }),\n ...(options?.idempotencyKey\n ? { headers: { \"Idempotency-Key\": options.idempotencyKey } }\n : {})\n });\n return (raw.data || []) as M[];\n },\n\n /**\n * Still `PUT`, deliberately, even though the server now serves `PATCH`\n * on the same handler and `PATCH` is the honest verb for a merge.\n *\n * The two are interchangeable server-side, so switching buys nothing at\n * runtime — and it costs compatibility in the direction that fails\n * quietly. A 0.14 client talking to a 0.13 server would send `PATCH` to\n * a route that does not exist and get a **404**, which is\n * indistinguishable from \"that row is gone\". Every write would look like\n * a missing record.\n *\n * `PATCH` is what the OpenAPI spec advertises, so anyone generating a\n * client gets the correct verb; this stays on `PUT` until the oldest\n * supported server is one that serves both.\n */\n async update(id: string | number, data: Partial<M>) {\n const raw = await transport.request<Record<string, unknown>>(`${basePath}/${encodeURIComponent(String(id))}`, {\n method: \"PUT\",\n body: JSON.stringify(data)\n });\n return raw as M;\n },\n\n async updateMany(updates: { id: string | number; data: Partial<M> }[], options?: WriteOptions) {\n if (!Array.isArray(updates)) {\n throw new TypeError(\"updateMany expects an array of { id, data } entries.\");\n }\n if (updates.length === 0) return [];\n\n const raw = await transport.request<{ data: Record<string, unknown>[] }>(`${basePath}/bulk`, {\n method: \"PATCH\",\n body: JSON.stringify({ updates }),\n ...(options?.idempotencyKey\n ? { headers: { \"Idempotency-Key\": options.idempotencyKey } }\n : {})\n });\n return (raw.data || []) as M[];\n },\n\n async delete(id: string | number) {\n await transport.request<void>(`${basePath}/${encodeURIComponent(String(id))}`, {\n method: \"DELETE\"\n });\n },\n\n /**\n * `POST .../bulk/delete`, not `DELETE .../bulk`.\n *\n * The honest verb would take the ids in a DELETE body, and that is the\n * one request shape the HTTP ecosystem handles unreliably: bodies on\n * DELETE are permitted but widely dropped by proxies and CDNs, and\n * several OpenAPI generators ignore `requestBody` on a DELETE\n * operation, so a generated client would send the request without its\n * ids. A backend deployed behind arbitrary ingress cannot take that\n * bet. Same reason `:batchDelete` exists in Google's API guidelines.\n */\n async deleteMany(ids: (string | number)[], options?: WriteOptions) {\n if (!Array.isArray(ids)) {\n throw new TypeError(\"deleteMany expects an array of ids.\");\n }\n if (ids.length === 0) return;\n\n await transport.request<void>(`${basePath}/bulk/delete`, {\n method: \"POST\",\n body: JSON.stringify({ ids }),\n ...(options?.idempotencyKey\n ? { headers: { \"Idempotency-Key\": options.idempotencyKey } }\n : {})\n });\n },\n\n async count(params?: FindParams<M>): Promise<number> {\n const countParams: FindParams<M> = {\n ...params,\n limit: undefined,\n offset: undefined,\n // A count reads no relation data, so `include` can only add\n // joins — and a join that does not match drops rows, which\n // would make the total disagree with the `find()` it describes.\n // Same reasoning as limit/offset: parameters that cannot affect\n // the answer are not forwarded.\n include: undefined\n };\n const qs = buildQueryString(countParams);\n\n // One count per query in flight, not one per caller.\n //\n // A count is a property of the query, and every concurrent caller\n // asking the same question wants the same answer — so they can\n // share the one request. This is not a micro-optimisation: a live\n // subscription re-counts on every push (see `listen` below), and\n // `listenCollection` deliberately collapses identical queries onto\n // a single socket subscription while keeping one callback per\n // subscriber. Each push therefore woke N subscribers, and each of\n // them issued its own identical count. A table showing one relation\n // column fired one count per visible cell, on every update.\n //\n // The entry is dropped as soon as it settles, so this merges\n // concurrent calls only and never serves a cached total.\n const key = basePath + \"/count\" + qs;\n const inflight = inflightCounts.get(key);\n if (inflight) return inflight;\n\n const request = transport\n .request<{ count: number }>(key, { method: \"GET\" })\n .then((raw) => raw.count ?? 0);\n inflightCounts.set(key, request);\n try {\n return await request;\n } finally {\n inflightCounts.delete(key);\n }\n },\n\n // Reactive reads. Without the offline layer there is no local database\n // to read from, so this is a fetch plus — when realtime is available —\n // the live subscription that keeps it current.\n observe(\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) {\n let closed = false;\n // Two sources race into one callback: the one-shot fetch below and\n // the subscription beside it. Whichever resolves last used to win,\n // so a socket update that landed first was overwritten by the\n // fetch's older snapshot and stayed wrong until the next change.\n // `listenCollection` replays cached rows synchronously to a second\n // subscriber, so a second component observing the same query hit\n // that ordering every time.\n let liveDelivered = false;\n let signature: string | undefined;\n\n const deliver = (result: FindResult<M>, fromLive: boolean) => {\n if (closed) return;\n // Once the socket has spoken, the fetch issued alongside it is\n // older news — delivering it would move the app backwards.\n if (fromLive) liveDelivered = true;\n else if (liveDelivered) return;\n // The de-duplication `observe()` documents. Keyed on the rows\n // and the total, the same two things the offline layer keys on,\n // so both implementations mean the same thing by \"changed\".\n const next = `${result.meta?.total ?? \"\"}|${JSON.stringify(result.data)}`;\n if (signature !== undefined && next === signature) return;\n signature = next;\n onResult({ ...result, fromCache: false, hasPendingWrites: false, partial: false });\n };\n\n client.find(params).then((result) => deliver(result, false)).catch((error) => {\n if (!closed) onError?.(error as Error);\n });\n const live = options?.realtime !== false && client.listen\n ? client.listen(params, (result) => deliver(result, true), onError)\n : undefined;\n return () => {\n closed = true;\n live?.();\n };\n },\n\n observeById(\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) {\n let closed = false;\n let liveDelivered = false;\n let signature: string | undefined;\n\n // Same ordering and de-duplication rules as `observe`, for one row.\n const deliver = (row: M | undefined, fromLive: boolean) => {\n if (closed) return;\n if (fromLive) liveDelivered = true;\n else if (liveDelivered) return;\n const next = row === undefined ? \"\\u0000missing\" : JSON.stringify(row);\n if (signature !== undefined && next === signature) return;\n signature = next;\n onResult(row, { fromCache: false, hasPendingWrites: false });\n };\n\n client.findById(id).then((row) => deliver(row, false)).catch((error) => {\n if (!closed) onError?.(error as Error);\n });\n const live = options?.realtime !== false && client.listenById\n ? client.listenById(id, (row) => deliver(row, true), onError)\n : undefined;\n return () => {\n closed = true;\n live?.();\n };\n },\n\n // Fluent builder instantiation\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SDKQueryBuilder<M>(client);\n if (typeof columnOrCondition === \"object\") {\n return builder.where(columnOrCondition);\n }\n return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValueFor<WhereFilterOp, M[keyof M & string]>);\n },\n orderBy(column: (keyof M & string) | ComputedSortField, direction?: \"asc\" | \"desc\") {\n return new SDKQueryBuilder<M>(client).orderBy(column, direction);\n },\n limit(count: number) {\n return new SDKQueryBuilder<M>(client).limit(count);\n },\n offset(count: number) {\n return new SDKQueryBuilder<M>(client).offset(count);\n },\n search(searchString: string, options?: { explain?: boolean }) {\n return new SDKQueryBuilder<M>(client).search(searchString, options);\n },\n vectorSearch(\n property: string,\n vector: number[],\n options?: { distance?: \"cosine\" | \"l2\" | \"inner_product\"; threshold?: number }\n ) {\n return new SDKQueryBuilder<M>(client).vectorSearch(property, vector, options);\n },\n include(...relations: string[]) {\n return new SDKQueryBuilder<M>(client).include(...relations);\n }\n };\n\n if (ws) {\n client.listen = (params: FindParams<M> | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void) => {\n let active = true;\n let lastUpdateId = 0;\n // The last total a `count()` actually returned. A later count that\n // fails says nothing about how big the collection is, so it must\n // not be allowed to replace this with the length of one page.\n let lastKnownTotal: number | undefined;\n const window = resolveFindWindow(params);\n const unsub = ws.listenCollection(\n {\n path: slug,\n filter: params?.where,\n // The group used to be dropped here, so a subscription\n // filtered with `or(...)` was pushed every row instead.\n logical: params?.logical,\n limit: params?.limit,\n // `offset` used to be stringified into `startAfter`, which\n // is a cursor *row*, not a row count — so the server was\n // handed \"20\" where it expected a keyset value, and the\n // offset it does understand never arrived at all.\n offset: window.driverOffset,\n // The list form, so a multi-key sort reaches the socket\n // whole. Indexing `[0]`/`[1]` here read a tuple-of-tuples as\n // a field name and a direction, and a live subscription came\n // back in a different order from the same query fetched.\n orderBy: normalizeOrderBy(params?.orderBy),\n searchString: params?.searchString,\n searchExplain: params?.searchExplain\n },\n (incomingRows: Record<string, unknown>[]) => {\n const currentUpdateId = ++lastUpdateId;\n // What the server pages by when the caller names no limit.\n // A hardcoded 20 here described a window the rows had not\n // come from, and any app sizing its next request off\n // `meta.limit` was told the wrong number.\n const requestedLimit = window.limit;\n const offset = window.offset;\n\n // WS client already delivers flat rows — just cast\n const rows = incomingRows as M[];\n\n const emit = (total: number, hasMore: boolean) => {\n if (!active || currentUpdateId !== lastUpdateId) return;\n onUpdate({\n data: rows,\n meta: {\n total,\n limit: requestedLimit,\n offset,\n hasMore\n }\n });\n };\n\n // With no count to go on, the only defensible total is a\n // lower bound: the rows on this page plus the ones paged\n // past to reach them. Reporting `rows.length` claimed a\n // collection read at offset 10 held two rows.\n const emitWithoutCount = () => emit(\n offset + rows.length,\n rows.length >= requestedLimit\n );\n\n if (client.count) {\n client.count(params)\n .then((total) => {\n lastKnownTotal = total;\n emit(total, offset + rows.length < total);\n })\n .catch(() => {\n // A count that failed is not evidence about the\n // size of the collection. Keep the last real\n // answer; only guess if there has never been one.\n if (lastKnownTotal !== undefined) {\n emit(lastKnownTotal, offset + rows.length < lastKnownTotal);\n } else {\n emitWithoutCount();\n }\n });\n } else {\n emitWithoutCount();\n }\n },\n onError\n );\n\n return () => {\n active = false;\n unsub();\n };\n };\n\n client.listenById = (id: string | number, onUpdate: (data: M | undefined) => void, onError?: (error: Error) => void) => {\n return ws.listenOne(\n {\n path: slug,\n id: String(id)\n },\n (row: Record<string, unknown> | null) => {\n if (row) {\n onUpdate(row as M);\n } else {\n onUpdate(undefined);\n }\n },\n onError\n );\n };\n }\n\n return client;\n}\n","import type { Transport } from \"./transport\";\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 * @typeParam T - Expected shape of the response payload.\n * @param name - Function name (the filename without extension, e.g. `\"extract-job\"`).\n * @param payload - Optional JSON-serialisable body sent as `POST`.\n * @param options - Optional overrides (HTTP method, sub-path, extra headers).\n * @returns The parsed JSON response from the function.\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 */\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 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 return transport.request<T>(routePath, init);\n }\n };\n}\n","import { StorageSource, UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult, DownloadMetadata, PUBLIC_STORAGE_PREFIX, isPublicStoragePath } from \"@rebasepro/types\";\nimport { Transport } 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 */\nexport function createStorage(transport: Transport, storageId?: string): StorageSource {\n const urlsCache = new Map<string, { config: DownloadConfig; expiresAt?: number }>();\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 async function putObject({\n file,\n key,\n metadata,\n bucket,\n public: isPublic\n }: UploadFileProps): Promise<UploadFileResult> {\n const formData = new FormData();\n formData.append(\"file\", file);\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 let effectiveKey = key;\n if (isPublic && effectiveKey && !isPublicStoragePath(effectiveKey)) {\n effectiveKey = `${PUBLIC_STORAGE_PREFIX}${effectiveKey.replace(/^\\/+/, \"\")}`;\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 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 const cachedEntry = urlsCache.get(cacheKey);\n if (cachedEntry) {\n if (!cachedEntry.expiresAt || cachedEntry.expiresAt > Date.now()) {\n return cachedEntry.config;\n }\n urlsCache.delete(cacheKey);\n }\n\n let filePath = keyOrUrl;\n\n if (filePath && (filePath.startsWith(\"local://\") || filePath.startsWith(\"s3://\") || filePath.startsWith(\"gs://\"))) {\n filePath = filePath.substring(filePath.indexOf(\"://\") + 3);\n }\n\n if (bucket && filePath && !filePath.startsWith(bucket)) {\n filePath = `${bucket}/${filePath}`;\n }\n\n if (!filePath || filePath.trim() === \"\" || filePath === \"/\") {\n return { url: null, fileNotFound: 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 const publicConfig: DownloadConfig = {\n url: withStorageId(`${fileUrlBase()}/storage/file/${filePath}`)\n };\n urlsCache.set(cacheKey, { config: publicConfig }); // no expiry\n return publicConfig;\n }\n\n try {\n const result = await transport.request<{ data: DownloadMetadata }>(withStorageId(`/storage/metadata/${filePath}`));\n\n // Public object (server-confirmed): token-less permanent URL.\n if (result.data.public) {\n const publicConfig: DownloadConfig = {\n url: withStorageId(`${fileUrlBase()}/storage/file/${filePath}`),\n metadata: result.data\n };\n urlsCache.set(cacheKey, { config: publicConfig }); // no expiry\n return publicConfig;\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 = result.data.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/${filePath}${tokenQuery}`),\n metadata: result.data\n };\n\n const expiresAt = result.data.tokenExpiresIn\n ? Date.now() + (result.data.tokenExpiresIn - 10) * 1000 // subtract 10s buffer\n : undefined;\n\n urlsCache.set(cacheKey, { config: downloadConfig, expiresAt });\n return downloadConfig;\n } catch (e: unknown) {\n if (e instanceof Error && \"status\" in e && (e as { status: number }).status === 404) {\n return { url: null, fileNotFound: 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 if (!response.ok) throw new Error(\"Failed to get file\");\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 let filePath = key;\n\n if (filePath && (filePath.startsWith(\"local://\") || filePath.startsWith(\"s3://\") || filePath.startsWith(\"gs://\"))) {\n filePath = filePath.substring(filePath.indexOf(\"://\") + 3);\n }\n\n if (bucket && filePath && !filePath.startsWith(bucket)) {\n filePath = `${bucket}/${filePath}`;\n }\n\n if (!filePath || filePath.trim() === \"\" || filePath === \"/\") {\n return;\n }\n\n try {\n await transport.request(withStorageId(`/storage/file/${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 // Fallback to default\n console.warn(\n `[StorageSourceRegistry] Storage source \"${key}\" not found, ` +\n `falling back to \"${DEFAULT_STORAGE_SOURCE_KEY}\".`\n );\n return this.getDefault();\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 {\n DeleteProps,\n CollectionConfig,\n FetchCollectionProps,\n FetchOneProps,\n SaveProps,\n WebSocketMessage,\n WebSocketErrorPayload,\n CollectionUpdateMessage,\n SingleUpdateMessage,\n TableMetadata,\n BranchInfo,\n RebaseApiError\n} from \"@rebasepro/types\";\nimport { buildCompositeId, COMPOSITE_ID_SEPARATOR, type PrimaryKeyInfo } from \"@rebasepro/common\";\nimport { rebaseReviver } from \"./reviver\";\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\nexport interface RebaseWebSocketConfig {\n websocketUrl: string;\n /** Optional auth token getter for WebSocket authentication */\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\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 * 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 */\nexport class RebaseWebSocketClient {\n private websocketUrl: string;\n private ws: WebSocket | null = null;\n public getAuthToken?: () => Promise<string | null>;\n private subscriptions = new Map<string, {\n onUpdate: (data: WebSocketMessage) => void,\n onError?: (error: Error) => void\n }>();\n\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: Record<string, unknown>) => 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, cleared by anything that earns a\n * fresh one.\n *\n * Unlike {@link closedByCaller} this is not final — nobody *asked* for the\n * socket to stay down. Five attempts with exponential backoff is about a\n * minute, which a laptop lid, a wifi handover or a backend rollout all\n * exceed routinely; treating that as permanent meant realtime silently\n * stopped for the rest of the page's life, with a reload the only cure.\n */\n private gaveUp = false;\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 /** Subscribe to broadcast/presence frames for one channel. */\n public onChannelMessage(channel: string, handler: (message: Record<string, unknown>) => 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 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>[]) => void;\n onError?: (error: Error) => void;\n }>;\n props: FetchCollectionProps;\n latestData?: Record<string, unknown>[]; // Cache the latest flat rows\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: FetchOneProps;\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 private maxReconnectAttempts = 5;\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\n constructor(config: RebaseWebSocketConfig) {\n this.websocketUrl = config.websocketUrl;\n this.getAuthToken = config.getAuthToken;\n this.onUnauthorized = config.onUnauthorized;\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.installOnlineListener();\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 }\n this.initWebSocket();\n }\n\n /**\n * The browser says the network is back — the usual reason the budget ran\n * out in the first place. Registered lazily so a Node client, or a page\n * that never subscribes, adds no listener.\n */\n private installOnlineListener() {\n if (this.onlineListener || typeof window === \"undefined\" || typeof window.addEventListener !== \"function\") return;\n this.onlineListener = () => {\n if (this.closedByCaller || !this.gaveUp) return;\n console.debug(\"Network is back — retrying the realtime connection\");\n this.ensureConnected();\n };\n window.addEventListener(\"online\", this.onlineListener);\n }\n\n private onlineListener: (() => void) | null = null;\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: { 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 * Set the auth token getter function\n */\n setAuthTokenGetter(getAuthToken: () => Promise<string | null>): void {\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 console.debug(\"WebSocket auto-authenticating after token getter set\");\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) console.debug(\"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) console.debug(\"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.onlineListener && typeof window !== \"undefined\") {\n window.removeEventListener(\"online\", this.onlineListener);\n this.onlineListener = null;\n }\n this.isAuthenticated = false;\n this.authPromise = null;\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\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(this.websocketUrl);\n this.ws = socket;\n\n this.ws!.onopen = async () => {\n console.debug(\"Connected to PostgreSQL backend\");\n const wasReconnect = this.reconnectAttempts > 0;\n this.isConnected = true;\n this.reconnectAttempts = 0;\n\n // Auto-authenticate if token getter is available\n if (this.getAuthToken && !this.isAuthenticated) {\n try {\n const token = await this.getAuthToken();\n if (token) {\n await this.authenticate(token);\n console.debug(\"WebSocket auto-authenticated\");\n }\n } catch (error) {\n // User not logged in or auth still loading — this is expected.\n // Authentication will happen on-demand when the user logs in.\n console.debug(\"WebSocket connected without auth:\", (error as Error)?.message || error);\n }\n }\n\n this.emit(wasReconnect ? \"reconnect\" : \"connect\");\n this.processMessageQueue();\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 try {\n const message = JSON.parse(event.data, rebaseReviver);\n this.handleWebSocketMessage(message);\n } catch (error) {\n console.error(\"Error parsing WebSocket message:\", error);\n }\n };\n\n this.ws!.onclose = () => {\n console.debug(\"Disconnected from PostgreSQL backend\");\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 // Re-queue pending requests so the UI doesn't hang indefinitely or crash\n for (const [reqId, request] of this.pendingRequests.entries()) {\n if (reqId.startsWith(\"auth_\")) {\n request.reject(new Error(\"Connection closed during authentication\"));\n } else if (request.message) {\n request.message._queuedResolve = request.resolve;\n request.message._queuedReject = request.reject;\n this.messageQueue.push(request.message);\n } else {\n request.reject(new RebaseApiError(\"Connection closed\"));\n }\n this.pendingRequests.delete(reqId);\n }\n\n this.attemptReconnect();\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 private processMessageQueue() {\n while (this.messageQueue.length > 0 && this.isConnected) {\n const message = this.messageQueue.shift();\n if (message) this.sendMessage(message);\n }\n }\n\n private attemptReconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.error(\"Max reconnection attempts reached\");\n // Nothing will re-subscribe now, so stop every subscription that\n // never loaded from spinning forever.\n this.gaveUp = true;\n this.failAllPendingSubscriptions(\n new RebaseApiError(\"Connection lost\", { code: \"CONNECTION_LOST\" })\n );\n return;\n }\n\n this.reconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n\n console.debug(`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 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 | FetchOneProps;\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 { errorMessage, errorCode } = extractMessageError(message);\n const error = new RebaseApiError(errorMessage, { code: errorCode });\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 // 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 const { errorMessage, errorCode } = extractMessageError(message);\n pendingReq.reject(new RebaseApiError(errorMessage, { code: errorCode }));\n }\n }).catch(err => {\n pendingReq.reject(err);\n });\n } else {\n this.pendingRequests.delete(requestId);\n const { errorMessage, errorCode } = extractMessageError(message);\n pendingReq.reject(new RebaseApiError(errorMessage, { code: errorCode }));\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 (typeof message.channel === \"string\" &&\n (type === \"broadcast\" || type === \"presence_state\" || type === \"presence_diff\" || type === \"channel_history\")) {\n const handlers = this.channelHandlers.get(message.channel);\n if (handlers) {\n for (const handler of [...handlers]) {\n try {\n handler(message as unknown as Record<string, unknown>);\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 && type === \"collection_update\") {\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 unknown 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 = (message as unknown as { pks?: PrimaryKeyInfo[] }).pks;\n if (updatePks) collectionSub.pks = updatePks;\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);\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 // Handle instant row-level patches for collection subscriptions.\n // These arrive before the full refetch and give immediate cross-tab feedback.\n if (subscriptionId && type === \"collection_patch\") {\n const subscriptionKey = this.backendToCollectionKey.get(subscriptionId);\n if (subscriptionKey) {\n const collectionSub = this.collectionSubscriptions.get(subscriptionKey);\n if (collectionSub && collectionSub.isInitialDataReceived && collectionSub.latestData) {\n const patchWireEntity = message.row ?? null;\n const patchMessage = message as unknown as { id: string; pks?: PrimaryKeyInfo[] };\n const patchEntityId = patchMessage.id;\n // The server knows the key columns; remember them, because the\n // refetch reconciliation needs them too and carries no id.\n if (patchMessage.pks) collectionSub.pks = patchMessage.pks;\n const patchRow = patchWireEntity ? (patchWireEntity as unknown as Record<string, unknown>) : null;\n let updated: Record<string, unknown>[];\n\n if (patchRow === null) {\n // Row was deleted — remove it from the cached list\n updated = collectionSub.latestData.filter(\n e => this.rowAddress(e, collectionSub.pks) !== String(patchEntityId)\n );\n } else {\n // Row was created or updated — merge into the cached list.\n // Matched against the patch's own address rather than\n // anything read off the row: `patchRow.id` is undefined\n // for a table not keyed on `id`, so every update looked\n // like a new row and was prepended as a duplicate.\n const idx = collectionSub.latestData.findIndex(\n e => this.rowAddress(e, collectionSub.pks) === String(patchEntityId)\n );\n if (idx >= 0) {\n // Update in place (preserve array position)\n updated = [...collectionSub.latestData];\n updated[idx] = patchRow;\n } else {\n // New row — prepend (most recently created first)\n updated = [patchRow, ...collectionSub.latestData];\n }\n }\n\n collectionSub.latestData = updated;\n collectionSub.lastUpdated = Date.now();\n\n // Fire all callbacks with the patched data\n collectionSub.callbacks.forEach(callback => {\n try {\n callback.onUpdate(updated);\n } catch (error) {\n console.error(\"Error in collection patch 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 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 const row = wireEntity ? (wireEntity as unknown as Record<string, unknown>) : 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 if (subscriptionId && (type === \"ERROR\" || message.error)) {\n const collectionKey = this.backendToCollectionKey.get(subscriptionId);\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 { errorMessage, errorCode } = extractMessageError(message);\n const error = new RebaseApiError(errorMessage, { code: errorCode });\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(subscriptionId);\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 { errorMessage, errorCode } = extractMessageError(message);\n const error = new RebaseApiError(errorMessage, { code: errorCode });\n entitySub.callbacks.forEach(callback => {\n if (callback.onError) {\n callback.onError(error);\n }\n });\n return;\n }\n }\n }\n\n // Legacy subscription handling (for backward compatibility)\n if (subscriptionId && this.subscriptions.has(subscriptionId)) {\n const callback = this.subscriptions.get(subscriptionId);\n if (!callback) {\n throw new Error(`Subscription callback not found for subscriptionId: ${subscriptionId}`);\n }\n if (message.type === \"ERROR\" || message.error) {\n if (callback.onError) {\n const { errorMessage, errorCode } = extractMessageError(message);\n callback.onError(new RebaseApiError(errorMessage, { code: errorCode }));\n }\n } else {\n callback.onUpdate(message);\n }\n return;\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 do:\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. A\n // console warning is the floor, not the answer: an `onError` on\n // `RebaseRealtimeChannel` is the shape this should eventually take.\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 try {\n const token = await this.getAuthToken!();\n if (!token) throw new Error(\"user not logged in\");\n await this.authenticate(token);\n console.debug(\"WebSocket authenticated on demand\");\n return; // Success\n } catch (error: unknown) {\n lastError = 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 console.debug(`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 console.debug(\"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 // If already has a requestId (re-sending from queue), use the stored promise handlers\n const queuedMsg = message as Record<string, unknown> & { _queuedResolve?: (p: unknown) => void; _queuedReject?: (p: Error) => void };\n if (queuedMsg._queuedResolve && queuedMsg._queuedReject) {\n return this.doSendMessage(message, queuedMsg._queuedResolve, queuedMsg._queuedReject);\n }\n\n if (!this.isConnected || !this.ws) {\n // The queue is only ever drained by a socket opening, so something\n // has to open one. Before lazy connect this was guaranteed by the\n // constructor; now the first frame is what asks for it.\n this.ensureConnected();\n // Queue the message and return a promise that will be resolved when actually sent\n return new Promise<unknown>((resolve, reject) => {\n const queueable = message as Record<string, unknown> & { _queuedResolve?: (p: unknown) => void; _queuedReject?: (p: Error) => void };\n queueable._queuedResolve = resolve;\n queueable._queuedReject = reject;\n this.messageQueue.push(message);\n });\n }\n\n return new Promise<unknown>((resolve, reject) => {\n this.doSendMessage(message, resolve, reject);\n });\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 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 const errorMessage = error instanceof Error ? error.message : \"Authentication required\";\n reject(new RebaseApiError(errorMessage));\n return;\n }\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 = !(\n message.type === \"subscribe_collection\"\n || message.type === \"subscribe_one\"\n || message.type === \"unsubscribe\"\n || CHANNEL_MESSAGE_TYPES.has(message.type as string)\n );\n\n if (expectsResponse && !this.pendingRequests.has(requestId)) {\n const timeoutHandle = setTimeout(() => {\n if (this.pendingRequests.has(requestId)) {\n this.pendingRequests.delete(requestId);\n reject(new RebaseApiError(\"Request timed out\"));\n }\n }, this.requestTimeoutMs);\n\n this.pendingRequests.set(requestId, {\n resolve: (value: unknown) => {\n clearTimeout(timeoutHandle);\n resolve(value);\n },\n reject: (error: Error) => {\n clearTimeout(timeoutHandle);\n reject(error);\n },\n message: message as Record<string, unknown> & { _queuedResolve?: (p: unknown) => void; _queuedReject?: (p: Error) => void }\n });\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 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 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 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 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 for structural equality.\n * Handles primitives, null, undefined, Date, RegExp, arrays, and plain objects.\n */\n private deepEqual(a: unknown, b: unknown): 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])) return false;\n }\n return true;\n }\n\n // Plain object comparison\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n\n if (aKeys.length !== bKeys.length) return false;\n\n for (const key of aKeys) {\n if (!Object.prototype.hasOwnProperty.call(bObj, key)) return false;\n if (!this.deepEqual(aObj[key], bObj[key])) return false;\n }\n\n return true;\n }\n\n private normalizeForComparison(val: unknown): unknown {\n if (!val) return val;\n\n if (Array.isArray(val)) {\n return val.map(item => this.normalizeForComparison(item));\n }\n\n if (typeof val === \"object\") {\n if (val instanceof Date) return val;\n if (val instanceof RegExp) return val;\n\n const obj = val as Record<string, unknown>;\n if (obj.__type === \"relation\") {\n // `data` is dropped on purpose: a relation compares by the\n // reference it holds, not by the row it happens to have loaded.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { data, ...rest } = obj;\n return rest;\n }\n\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n result[k] = this.normalizeForComparison(v);\n }\n return result;\n }\n\n return val;\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 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 const normCached = this.normalizeForComparison(cachedRow) as Record<string, unknown>;\n const normIncoming = this.normalizeForComparison(incomingRow) as Record<string, unknown>;\n\n if (this.deepEqual(normCached, normIncoming)) {\n return cachedRow;\n } else {\n // Deep debug: Why did it fail?\n const mismatches: Record<string, { cached: unknown, incoming: unknown }> = {};\n const allKeys = new Set([...Object.keys(normCached), ...Object.keys(normIncoming)]);\n for (const key of allKeys) {\n if (!this.deepEqual(normCached[key], normIncoming[key])) {\n mismatches[key] = { cached: normCached[key],\nincoming: normIncoming[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 props: FetchCollectionProps<M>,\n onUpdate: (rows: Record<string, unknown>[]) => 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>[]) => void;\n onError?: (error: Error) => void;\n }>;\n callbackMap.set(callbackId, { onUpdate,\nonError });\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 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>[]) => void;\n onError?: (error: Error) => void;\n }>();\n callbackMap.set(callbackId, { onUpdate,\nonError });\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: FetchOneProps<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\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\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 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 payload: {\n ...subscription.props,\n subscriptionId: backendSubscriptionId\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 * 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 * Fail every subscription that never received data. Called when reconnection\n * is given up on, so views surface an error instead of spinning forever.\n */\n private failAllPendingSubscriptions(error: Error): void {\n for (const key of [...this.collectionSubscriptions.keys()]) {\n const sub = this.collectionSubscriptions.get(key);\n if (sub && !sub.isInitialDataReceived) this.failCollectionSubscription(key, error);\n }\n for (const key of [...this.singleSubscriptions.keys()]) {\n const sub = this.singleSubscriptions.get(key);\n if (sub && !sub.isInitialDataReceived) this.failEntitySubscription(key, error);\n }\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 console.debug(`[WS] Re-subscribing: ${this.collectionSubscriptions.size} collection(s), ${this.singleSubscriptions.size} row(ies)`);\n\n // Re-subscribe collection subscriptions\n for (const [key, sub] of this.collectionSubscriptions.entries()) {\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 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: FetchCollectionProps): 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: FetchOneProps): string {\n return `${props.path}|${props.id}`;\n }\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/** Presence state keyed by the server's client id. */\nexport type PresenceState = Record<string, Record<string, unknown>>;\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 type { ChannelHistoryEntry } from \"@rebasepro/types\";\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: Record<string, unknown>) => void): () => void;\n onReconnect(handler: () => void): () => void;\n}\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 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: BroadcastEvent[] = [];\n private catchUpInFlight = false;\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 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\n if (this.catchUpTimeout) clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = setTimeout(() => this.abandonCatchUp(), CATCH_UP_TIMEOUT_MS);\n (this.catchUpTimeout as unknown as { unref?: () => void }).unref?.();\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 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 (this.heartbeat as unknown as { unref?: () => void }).unref?.();\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 /** Send a broadcast. The sender does not receive its own message. */\n async broadcast(event: string, payload: unknown): Promise<void> {\n await this.join();\n await this.send(\"broadcast\", { event, payload });\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) this.lastSeq = options.sinceSeq;\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.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: Record<string, unknown>): void {\n switch (message.type) {\n case \"presence_state\": {\n this.presences = (message.presences as PresenceState) ?? {};\n this.emitPresence();\n break;\n }\n case \"presence_diff\": {\n const joins = (message.joins as PresenceState) ?? {};\n const leaves = (message.leaves as PresenceState) ?? {};\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 = typeof message.seq === \"number\" ? message.seq : undefined;\n const event: BroadcastEvent = {\n event: message.event as string,\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 this.lastSeq = seq;\n this.deliver(event);\n break;\n }\n case \"channel_history\": {\n this.catchUpInFlight = false;\n if (this.catchUpTimeout) {\n clearTimeout(this.catchUpTimeout);\n this.catchUpTimeout = null;\n }\n\n const entries = (message.messages as ChannelHistoryEntry[] | undefined) ?? [];\n const retained = message.retained === true;\n const latestSeq = typeof message.latestSeq === \"number\" ? message.latestSeq : undefined;\n\n for (const resolve of this.historyWaiters.splice(0)) {\n resolve({ messages: entries, retained, latestSeq });\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 for (const entry of entries) {\n if (entry.seq <= this.lastSeq) continue;\n this.lastSeq = entry.seq;\n this.deliver({\n event: entry.event,\n payload: entry.payload,\n seq: entry.seq,\n replayed: true\n });\n }\n\n this.flushPendingLive();\n break;\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 }\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 { EntityReference, EntityRelation, GeoPoint, Vector } from \"@rebasepro/types\";\nimport { rebaseReviver } from \"./reviver\";\n\n/**\n * Lossless round-tripping of rows through the offline store.\n *\n * Both persistence backends move values by structured clone, which keeps\n * `Date` but flattens every class instance to a plain object. For\n * `EntityReference`/`EntityRelation` that is harmless — they carry their own\n * `__type` discriminator, so the JSON reviver can rebuild them — but\n * `GeoPoint` and `Vector` do not, and would come back out of the cache as\n * anonymous `{ latitude, longitude }` / `{ value }` bags. A row read from the\n * cache must be indistinguishable from the same row read from the network, so\n * those two are tagged on the way in and revived on the way out.\n *\n * Type tests here are structural rather than `instanceof`, because a structured\n * clone can arrive from another realm — an iframe, a worker, or the polyfill\n * the tests run against — where the constructor identity differs but the value\n * is the real thing. Only *plain* objects are walked; anything else is passed\n * through whole, so a class instance is never quietly reduced to `{}`.\n */\n\nfunction isDate(value: unknown): value is Date {\n return Object.prototype.toString.call(value) === \"[object Date]\";\n}\n\n/** An object literal — not a Date, RegExp, Map, or any class instance. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n const proto = Object.getPrototypeOf(value) as { constructor?: { name?: string } } | null;\n if (proto === null || proto === Object.prototype) return true;\n // A literal cloned out of another realm has a different `Object.prototype`\n // but is still, in every way that matters here, a plain object.\n return proto.constructor?.name === \"Object\";\n}\n\nfunction dehydrateValue(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (value instanceof GeoPoint) {\n return { __type: \"GeoPoint\", latitude: value.latitude, longitude: value.longitude };\n }\n if (value instanceof Vector) return { __type: \"Vector\", value: [...value.value] };\n // EntityReference/EntityRelation already serialize themselves via `__type`\n // own properties, so a structured clone is enough for the reviver below.\n if (value instanceof EntityReference || value instanceof EntityRelation) return value;\n if (Array.isArray(value)) return value.map(dehydrateValue);\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value)) out[key] = dehydrateValue(inner);\n return out;\n }\n return value;\n}\n\nfunction hydrateValue(value: unknown): unknown {\n if (value === null || value === undefined || isDate(value)) return value;\n if (Array.isArray(value)) return value.map(hydrateValue);\n if (typeof value === \"object\") {\n const revived = rebaseReviver(\"\", value);\n // The reviver recognised it — hand back the class instance untouched\n // rather than walking into its (now private) internals.\n if (revived !== value) return revived;\n if (!isPlainObject(value)) return value;\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value)) out[key] = hydrateValue(inner);\n return out;\n }\n return value;\n}\n\n/** Prepare a row for the store. */\nexport function dehydrateRow<T extends Record<string, unknown>>(row: T): Record<string, unknown> {\n return dehydrateValue(row) as Record<string, unknown>;\n}\n\n/** Restore a row read back from the store. */\nexport function hydrateRow<T extends Record<string, unknown>>(row: Record<string, unknown>): T {\n return hydrateValue(row) as T;\n}\n","import { RebaseApiError } from \"./transport\";\n\n/**\n * Whether the network is worth trying, and when to try again after it wasn't.\n *\n * `navigator.onLine` is necessary but not sufficient: it reports the state of\n * the network interface, so it stays `true` behind a captive portal, on a\n * connection that resolves DNS but reaches nothing, and while the API itself\n * is down. This tracks what actually happened to requests as well, so the\n * first failure is the only one an app pays for — everything after it inside\n * the backoff window skips the doomed round trip and answers from the local\n * store immediately, which is the difference between an app that freezes when\n * the wifi drops and one that does not.\n */\n\n/** The request never reached the server, so nothing was decided by it. */\nexport function isNetworkError(error: unknown): boolean {\n if (error instanceof RebaseApiError) {\n // A 0 status is what a transport reports when it has no response at all.\n return error.status === 0;\n }\n // fetch rejects with TypeError on network failure in every runtime we\n // support (browsers: \"Failed to fetch\"/\"Load failed\"; undici: \"fetch\n // failed\").\n if (error instanceof TypeError) return true;\n const name = (error as { name?: string } | undefined)?.name;\n // AbortError covers both an explicit abort and a fetch timeout; the others\n // are what Node and Safari surface for a dropped connection.\n return name === \"AbortError\" || name === \"TimeoutError\" || name === \"NetworkError\";\n}\n\n/**\n * Statuses that mean \"not now\" rather than \"not ever\": a queued write that\n * gets one of these is worth replaying, while a 400 or a 403 never will be.\n * 500 is deliberately absent — an unhandled server error is far more often a\n * bug the same payload will hit again than a blip, and retrying it forever\n * jams every write behind it.\n */\nconst RETRYABLE_STATUSES = new Set([408, 425, 429, 502, 503, 504]);\n\n/**\n * The server holds this key for a request it has not answered yet.\n *\n * It is a 409 like a duplicate row is a 409, and nothing but the code separates\n * them — one means \"your write is already there\", the other means \"your write\n * may not have happened at all, ask again\".\n */\nconst IDEMPOTENCY_IN_PROGRESS = \"IDEMPOTENCY_KEY_IN_PROGRESS\";\n\n/**\n * Is the server still answering an earlier attempt of this same write?\n *\n * The only correct response is to ask again — which is exactly what the\n * server's own message says, and exactly what this SDK used not to do.\n */\nexport function isIdempotencyInProgressError(error: unknown): boolean {\n return error instanceof RebaseApiError\n && error.status === 409\n && error.code === IDEMPOTENCY_IN_PROGRESS;\n}\n\n/** Is this failure worth another attempt later? */\nexport function isRetryableError(error: unknown): boolean {\n if (isNetworkError(error)) return true;\n if (!(error instanceof RebaseApiError)) return false;\n // The one 409 that resolves on its own. A key whose claim outlived the\n // request that took it — the process was killed between the write and the\n // answer — is refused until the claim's lease expires, and giving up on it\n // means dropping a write that retrying would have completed.\n if (isIdempotencyInProgressError(error)) return true;\n return error.status !== undefined && RETRYABLE_STATUSES.has(error.status);\n}\n\n/**\n * Did this write fail because the row is already there?\n *\n * Matched on the SQLSTATE the server passes through (`23505`, unique_violation)\n * and on 409, never on the message — a duplicate-key message names the\n * constraint and the values, so it is neither stable nor safe to parse.\n *\n * The queue uses this to recognise its own earlier attempt. A create whose\n * response was lost is replayed, and for a row carrying an id the SDK generated\n * the server can only be rejecting it because the first attempt actually landed.\n *\n * Which is why the status alone cannot decide it: `IDEMPOTENCY_KEY_IN_PROGRESS`\n * is a 409 that means the opposite — the row may not exist at all. Read as a\n * duplicate, the queue looked for a row that was never written, found nothing,\n * concluded there was nothing left to do and deleted the write from the queue.\n */\nexport function isDuplicateKeyError(error: unknown): boolean {\n if (!(error instanceof RebaseApiError)) return false;\n if (error.code === \"23505\") return true;\n return error.status === 409 && !isIdempotencyInProgressError(error);\n}\n\nexport interface ConnectivityOptions {\n /** First retry delay after a failure. Defaults to 1 000 ms. */\n initialBackoffMs?: number;\n /** Ceiling for the doubling retry delay. Defaults to 60 000 ms. */\n maxBackoffMs?: number;\n /**\n * Let a known-failed connection suppress further attempts until the\n * backoff window opens. On by default — it is what makes a read or write\n * during an outage instant instead of a timeout. Turn it off when nothing\n * will ever wake the client up again (no retry timer, no `online` event),\n * where suppressing attempts would mean never recovering.\n */\n respectBackoff?: boolean;\n /** Injected for tests. */\n now?: () => number;\n /** Injected for tests; must return a handle `clearTimeout` accepts. */\n setTimer?: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;\n clearTimer?: (handle: ReturnType<typeof setTimeout>) => void;\n}\n\nexport class ConnectivityMonitor {\n private state: \"online\" | \"offline\" = \"online\";\n private backoffMs: number;\n private readonly initialBackoffMs: number;\n private readonly maxBackoffMs: number;\n private retryAt = 0;\n private timer?: ReturnType<typeof setTimeout>;\n private listeners = new Set<(online: boolean) => void>();\n private readonly respectBackoff: boolean;\n private readonly now: () => number;\n private readonly setTimer: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;\n private readonly clearTimer: (handle: ReturnType<typeof setTimeout>) => void;\n /** Called when the backoff window expires, to drive an automatic retry. */\n onRetryDue?: () => void;\n\n private readonly handleOnline = () => {\n // The OS says the interface is back. Trust it enough to try\n // immediately rather than sitting out the rest of the backoff — and to\n // say so, or a client with nothing queued would have no request whose\n // success could ever flip the badge back to \"online\".\n this.retryAt = 0;\n this.backoffMs = this.initialBackoffMs;\n this.clearPendingTimer();\n this.setState(\"online\");\n this.onRetryDue?.();\n };\n private readonly handleOffline = () => {\n this.setState(\"offline\");\n };\n\n constructor(options: ConnectivityOptions = {}) {\n this.initialBackoffMs = options.initialBackoffMs ?? 1_000;\n this.maxBackoffMs = Math.max(this.initialBackoffMs, options.maxBackoffMs ?? 60_000);\n this.backoffMs = this.initialBackoffMs;\n this.respectBackoff = options.respectBackoff ?? true;\n this.now = options.now ?? (() => Date.now());\n this.setTimer = options.setTimer ?? ((fn, ms) => setTimeout(fn, ms));\n this.clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));\n\n if (typeof window !== \"undefined\" && typeof window.addEventListener === \"function\") {\n window.addEventListener(\"online\", this.handleOnline);\n window.addEventListener(\"offline\", this.handleOffline);\n }\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) {\n this.state = \"offline\";\n }\n }\n\n /** What the app should be told: are we connected? */\n isOnline(): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return false;\n return this.state === \"online\";\n }\n\n /**\n * Should this request even be sent? False means \"answer from the local\n * store instead\" — the request would only burn a timeout to reach the same\n * conclusion the last one already did.\n */\n shouldAttempt(): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return false;\n if (this.state === \"online\" || !this.respectBackoff) return true;\n // Exactly one request is let through when the window opens; it is the\n // probe whose outcome decides whether we are back.\n return this.now() >= this.retryAt;\n }\n\n /** A request reached the server. */\n markSuccess(): void {\n this.backoffMs = this.initialBackoffMs;\n this.retryAt = 0;\n this.clearPendingTimer();\n this.setState(\"online\");\n }\n\n /** A request did not reach the server: we are offline until proven otherwise. */\n markFailure(): void {\n this.deferRetry();\n this.setState(\"offline\");\n }\n\n /**\n * Back off and try again later without claiming the connection is gone.\n * This is what a 429 or a 503 deserves — the server answered, so the app\n * is demonstrably online; it just should not hammer.\n */\n deferRetry(): void {\n const jitter = 0.8 + Math.random() * 0.4;\n this.retryAt = this.now() + this.backoffMs * jitter;\n const delay = Math.max(0, this.retryAt - this.now());\n this.backoffMs = Math.min(this.maxBackoffMs, this.backoffMs * 2);\n this.scheduleRetry(delay);\n }\n\n /** Milliseconds until the next attempt is allowed; 0 when one is allowed now. */\n msUntilRetry(): number {\n if (this.state === \"online\") return 0;\n return Math.max(0, this.retryAt - this.now());\n }\n\n onChange(listener: (online: boolean) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n dispose(): void {\n if (typeof window !== \"undefined\" && typeof window.removeEventListener === \"function\") {\n window.removeEventListener(\"online\", this.handleOnline);\n window.removeEventListener(\"offline\", this.handleOffline);\n }\n this.clearPendingTimer();\n this.listeners.clear();\n this.onRetryDue = undefined;\n }\n\n private scheduleRetry(delay: number): void {\n this.clearPendingTimer();\n if (!this.onRetryDue) return;\n this.timer = this.setTimer(() => {\n this.timer = undefined;\n this.onRetryDue?.();\n }, delay);\n // A retry timer must never be the reason a Node script refuses to exit.\n (this.timer as unknown as { unref?: () => void }).unref?.();\n }\n\n private clearPendingTimer(): void {\n if (this.timer !== undefined) {\n this.clearTimer(this.timer);\n this.timer = undefined;\n }\n }\n\n private setState(next: \"online\" | \"offline\"): void {\n if (this.state === next) return;\n this.state = next;\n const online = this.isOnline();\n for (const listener of this.listeners) listener(online);\n }\n}\n","/**\n * Persistence backends for the SDK's offline support.\n *\n * The store is a dumb, namespaced key/value surface with two areas: a read\n * cache (normalized rows, query snapshots and sync bookkeeping) and a mutation\n * queue (local writes waiting to reach the server). All structure — per-user\n * prefixes, the `row|`/`q|`/`meta|` namespaces, mutation ordering — is owned by\n * the {@link OfflineManager}; the store only promises that a prefix listing\n * comes back in lexicographic key order, which is what makes the queue a FIFO.\n *\n * Two implementations ship with the SDK:\n * - {@link IndexedDBOfflineStore} — the browser default; survives reloads.\n * - {@link MemoryOfflineStore} — the fallback everywhere IndexedDB does not\n * exist (Node, React Native, tests); survives only the process.\n *\n * Environments with neither (React Native + AsyncStorage, Electron main, …)\n * implement this interface and pass it via `offline.store`.\n */\n\n/** A cached value plus the moment it was written, for LRU eviction. */\nexport interface OfflineCacheEntry {\n value: unknown;\n cachedAt: number;\n}\n\n/** A cache entry with its key, as returned by prefix listings. */\nexport interface OfflineCacheRecord extends OfflineCacheEntry {\n key: string;\n}\n\n/** What a mutation has to put back if the server rejects it. */\nexport interface MutationRollback {\n /**\n * The rows as they were locally *before* this mutation was applied, keyed\n * by id. A `null` value means \"the row did not exist\" — restoring it is a\n * delete, not a write.\n */\n rows: Record<string, Record<string, unknown> | null>;\n}\n\n/**\n * A local write waiting to be replayed against the server.\n *\n * `mutationId` orders the queue globally (not per collection): a create in one\n * collection may be the parent a later insert in another references, so replay\n * must preserve the order the app issued the writes in. It is lexicographically\n * time-ordered and carries a random suffix, so two browser tabs writing in the\n * same millisecond produce distinct, still-roughly-ordered ids instead of\n * silently overwriting each other's queue entry.\n */\nexport interface PendingMutation {\n /** Unique, lexicographically sortable identity — also the queue key suffix. */\n mutationId: string;\n collection: string;\n type: \"create\" | \"createMany\" | \"update\" | \"updateMany\" | \"delete\" | \"deleteMany\";\n /** Target row id for update/delete, and the (client-generated) id of an offline create. */\n id?: string | number;\n /** Target row ids for `deleteMany`. */\n ids?: (string | number)[];\n /** `{ id, data }` entries for `updateMany`. */\n updates?: { id: string | number; data: Record<string, unknown> }[];\n /**\n * True when the SDK minted this create's id itself. Only such creates may\n * cancel out against a later offline delete: a freshly generated UUID\n * cannot name a row the server already has, while a caller-supplied id\n * can — and there the delete must still replay to remove the server row.\n */\n generatedId?: boolean;\n /** The payload: a row for create/update, an array of rows for createMany. */\n data?: Record<string, unknown> | Record<string, unknown>[];\n upsert?: boolean;\n queuedAt: number;\n /** How many times replay has been attempted (diagnostics for a stuck queue). */\n attempts?: number;\n /** The last replay failure's message, when there was one. */\n lastError?: string;\n /** Local state to restore if the server rejects this mutation. */\n rollback?: MutationRollback;\n}\n\nexport interface OfflineStore {\n getCache(key: string): Promise<OfflineCacheEntry | undefined>;\n setCache(key: string, entry: OfflineCacheEntry): Promise<void>;\n /** Write many entries at once — one transaction where the backend has them. */\n setCacheMany(entries: { key: string; entry: OfflineCacheEntry }[]): Promise<void>;\n deleteCache(keys: string[]): Promise<void>;\n /** Every cache key starting with `prefix`, with its write time (for eviction). */\n listCache(prefix: string): Promise<{ key: string; cachedAt: number }[]>;\n /** As {@link listCache}, but with the values — the local query engine's input. */\n listCacheEntries(prefix: string): Promise<OfflineCacheRecord[]>;\n\n enqueue(key: string, mutation: PendingMutation): Promise<void>;\n dequeue(key: string): Promise<void>;\n /** Queued mutations whose key starts with `prefix`, in lexicographic key order. */\n listQueue(prefix: string): Promise<PendingMutation[]>;\n\n /** Remove every cache entry and queued mutation whose key starts with `prefix`. */\n clear(prefix: string): Promise<void>;\n}\n\n// ─── Mutation ids ────────────────────────────────────────────────────────────\n\n/**\n * Monotonic within a tab, unique across tabs, and sortable as a plain string:\n * `<ms base36, padded>-<counter>-<random>`. The padding is what keeps\n * lexicographic order equal to chronological order, and the random suffix is\n * what stops two tabs from writing the same queue key in the same millisecond\n * — which would silently drop one of the two writes.\n */\nlet mutationCounter = 0;\nexport function createMutationId(now: number = Date.now()): string {\n const time = now.toString(36).padStart(10, \"0\");\n const counter = (mutationCounter = (mutationCounter + 1) % 1_679_616).toString(36).padStart(4, \"0\");\n const random = Math.random().toString(36).slice(2, 10).padStart(8, \"0\");\n return `${time}-${counter}-${random}`;\n}\n\n// ─── Memory ──────────────────────────────────────────────────────────────────\n\n/**\n * In-memory store: the default outside the browser and the workhorse of the\n * test suite. Values are deep-copied on the way in and out so a caller\n * mutating a returned row cannot silently edit the \"persisted\" copy — the\n * IndexedDB implementation gets the same guarantee for free from structured\n * cloning, and the two must not differ in aliasing behaviour.\n */\nexport class MemoryOfflineStore implements OfflineStore {\n private cache = new Map<string, OfflineCacheEntry>();\n private queue = new Map<string, PendingMutation>();\n\n async getCache(key: string): Promise<OfflineCacheEntry | undefined> {\n const entry = this.cache.get(key);\n return entry ? structuredClone(entry) : undefined;\n }\n\n async setCache(key: string, entry: OfflineCacheEntry): Promise<void> {\n this.cache.set(key, structuredClone(entry));\n }\n\n async setCacheMany(entries: { key: string; entry: OfflineCacheEntry }[]): Promise<void> {\n for (const { key, entry } of entries) this.cache.set(key, structuredClone(entry));\n }\n\n async deleteCache(keys: string[]): Promise<void> {\n for (const key of keys) this.cache.delete(key);\n }\n\n async listCache(prefix: string): Promise<{ key: string; cachedAt: number }[]> {\n const out: { key: string; cachedAt: number }[] = [];\n for (const [key, entry] of this.cache) {\n if (key.startsWith(prefix)) out.push({ key, cachedAt: entry.cachedAt });\n }\n return out;\n }\n\n async listCacheEntries(prefix: string): Promise<OfflineCacheRecord[]> {\n const out: OfflineCacheRecord[] = [];\n for (const [key, entry] of this.cache) {\n if (key.startsWith(prefix)) out.push({ key, ...structuredClone(entry) });\n }\n out.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n return out;\n }\n\n async enqueue(key: string, mutation: PendingMutation): Promise<void> {\n this.queue.set(key, structuredClone(mutation));\n }\n\n async dequeue(key: string): Promise<void> {\n this.queue.delete(key);\n }\n\n async listQueue(prefix: string): Promise<PendingMutation[]> {\n return [...this.queue.entries()]\n .filter(([key]) => key.startsWith(prefix))\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([, mutation]) => structuredClone(mutation));\n }\n\n async clear(prefix: string): Promise<void> {\n for (const key of [...this.cache.keys()]) {\n if (key.startsWith(prefix)) this.cache.delete(key);\n }\n for (const key of [...this.queue.keys()]) {\n if (key.startsWith(prefix)) this.queue.delete(key);\n }\n }\n}\n\n// ─── IndexedDB ───────────────────────────────────────────────────────────────\n\nconst IDB_NAME = \"rebase-offline\";\n/**\n * v2 introduced the normalized row cache and string mutation ids. A v1\n * database holds whole-response blobs under keys this version cannot read and\n * queue entries ordered by a numeric `seq` this version no longer writes, so\n * the upgrade drops both stores rather than trying to translate them. Offline\n * support had not shipped in a release when v2 landed, so nothing in the wild\n * loses a queued write to this.\n */\nconst IDB_VERSION = 2;\nconst CACHE_STORE = \"cache\";\nconst QUEUE_STORE = \"queue\";\n\n/** The exclusive upper bound of an IDBKeyRange covering every key under `prefix`. */\nfunction prefixRange(prefix: string): IDBKeyRange {\n return IDBKeyRange.bound(prefix, prefix + \"￿\", false, false);\n}\n\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB request failed\"));\n });\n}\n\n/** Resolve when the whole transaction commits, not just when the last request returns. */\nfunction transactionDone(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onabort = tx.onerror = () => reject(tx.error ?? new Error(\"IndexedDB transaction failed\"));\n });\n}\n\n/**\n * IndexedDB-backed store — the browser default, so cached rows and queued\n * writes survive a reload or a browser restart. Everything lives in one\n * database with two object stores; keys are the manager's full prefixed\n * strings, so multiple users (scopes) share the database without ever\n * sharing entries.\n */\nexport class IndexedDBOfflineStore implements OfflineStore {\n private dbPromise?: Promise<IDBDatabase>;\n\n private open(): Promise<IDBDatabase> {\n if (!this.dbPromise) {\n this.dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(IDB_NAME, IDB_VERSION);\n request.onupgradeneeded = (event) => {\n const db = request.result;\n // A v1 database speaks a key layout this version cannot\n // read; keeping it would surface as corrupt cache entries\n // and un-replayable mutations. Start clean instead.\n if (event.oldVersion > 0 && event.oldVersion < 2) {\n if (db.objectStoreNames.contains(CACHE_STORE)) db.deleteObjectStore(CACHE_STORE);\n if (db.objectStoreNames.contains(QUEUE_STORE)) db.deleteObjectStore(QUEUE_STORE);\n }\n if (!db.objectStoreNames.contains(CACHE_STORE)) db.createObjectStore(CACHE_STORE);\n if (!db.objectStoreNames.contains(QUEUE_STORE)) db.createObjectStore(QUEUE_STORE);\n };\n request.onsuccess = () => {\n const db = request.result;\n // Another tab asking for a newer version needs this\n // connection out of the way, or its upgrade blocks forever.\n db.onversionchange = () => {\n db.close();\n this.dbPromise = undefined;\n };\n resolve(db);\n };\n // Reset so a transient failure (private browsing quota, a\n // version race with another tab) can be retried instead of\n // poisoning every later call with the same rejection.\n request.onerror = () => {\n this.dbPromise = undefined;\n reject(request.error ?? new Error(\"Failed to open IndexedDB\"));\n };\n request.onblocked = () => {\n this.dbPromise = undefined;\n reject(new Error(\"IndexedDB upgrade blocked by another tab\"));\n };\n });\n }\n return this.dbPromise;\n }\n\n private async store(name: string, mode: IDBTransactionMode): Promise<IDBObjectStore> {\n const db = await this.open();\n return db.transaction(name, mode).objectStore(name);\n }\n\n async getCache(key: string): Promise<OfflineCacheEntry | undefined> {\n const store = await this.store(CACHE_STORE, \"readonly\");\n const entry = await requestToPromise(store.get(key));\n return entry as OfflineCacheEntry | undefined;\n }\n\n async setCache(key: string, entry: OfflineCacheEntry): Promise<void> {\n const store = await this.store(CACHE_STORE, \"readwrite\");\n await requestToPromise(store.put(entry, key));\n }\n\n async setCacheMany(entries: { key: string; entry: OfflineCacheEntry }[]): Promise<void> {\n if (entries.length === 0) return;\n const store = await this.store(CACHE_STORE, \"readwrite\");\n for (const { key, entry } of entries) store.put(entry, key);\n // One commit for the whole batch: a `find` writing 200 rows must not\n // be 200 round-trips through the transaction queue.\n await transactionDone(store.transaction);\n }\n\n async deleteCache(keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n const store = await this.store(CACHE_STORE, \"readwrite\");\n for (const key of keys) store.delete(key);\n await transactionDone(store.transaction);\n }\n\n async listCache(prefix: string): Promise<{ key: string; cachedAt: number }[]> {\n const store = await this.store(CACHE_STORE, \"readonly\");\n const [keys, entries] = await Promise.all([\n requestToPromise(store.getAllKeys(prefixRange(prefix))),\n requestToPromise(store.getAll(prefixRange(prefix)))\n ]);\n return keys.map((key, i) => ({\n key: String(key),\n cachedAt: (entries[i] as OfflineCacheEntry)?.cachedAt ?? 0\n }));\n }\n\n async listCacheEntries(prefix: string): Promise<OfflineCacheRecord[]> {\n const store = await this.store(CACHE_STORE, \"readonly\");\n const [keys, entries] = await Promise.all([\n requestToPromise(store.getAllKeys(prefixRange(prefix))),\n requestToPromise(store.getAll(prefixRange(prefix)))\n ]);\n return keys.map((key, i) => {\n const entry = entries[i] as OfflineCacheEntry | undefined;\n return { key: String(key), value: entry?.value, cachedAt: entry?.cachedAt ?? 0 };\n });\n }\n\n async enqueue(key: string, mutation: PendingMutation): Promise<void> {\n const store = await this.store(QUEUE_STORE, \"readwrite\");\n await requestToPromise(store.put(mutation, key));\n }\n\n async dequeue(key: string): Promise<void> {\n const store = await this.store(QUEUE_STORE, \"readwrite\");\n await requestToPromise(store.delete(key));\n }\n\n async listQueue(prefix: string): Promise<PendingMutation[]> {\n const store = await this.store(QUEUE_STORE, \"readonly\");\n // getAll on a key range returns values in key order, which is the\n // FIFO guarantee this interface promises.\n const entries = await requestToPromise(store.getAll(prefixRange(prefix)));\n return entries as PendingMutation[];\n }\n\n async clear(prefix: string): Promise<void> {\n const cache = await this.store(CACHE_STORE, \"readwrite\");\n await requestToPromise(cache.delete(prefixRange(prefix)));\n const queue = await this.store(QUEUE_STORE, \"readwrite\");\n await requestToPromise(queue.delete(prefixRange(prefix)));\n }\n}\n","import {\n EntityRelation,\n FilterValues,\n FindResult,\n LogicalCondition,\n FilterCondition,\n OrderBySpec,\n WhereFilterOp,\n toCanonicalOp\n} from \"@rebasepro/types\";\nimport { FindParams } from \"./transport\";\nimport { normalizeOrderBy, resolveFindWindow } from \"@rebasepro/common\";\n\n/**\n * A local evaluator for `FindParams`, so cached rows can answer a query the\n * client has never sent to the server — and so a row written offline shows up\n * in every filtered list it belongs to, not just in unfiltered ones.\n *\n * This mirrors the Postgres driver's semantics rather than JavaScript's:\n *\n * - Comparing against NULL is *unknown*, not false-or-true. `status != \"done\"`\n * excludes rows where `status` is null, exactly as SQL does — a JS `!==`\n * would have included them.\n * - `ORDER BY` puts nulls last ascending and first descending, which is the\n * Postgres default.\n * - The wire format carries no types, so values arriving as strings are\n * compared numerically against numeric columns and as instants against\n * date columns. `[\"==\", \"3\"]` matches the number `3`, as it does server-side.\n *\n * Two things it deliberately approximates, both flagged by\n * {@link isExactlyEvaluable}: `searchString` becomes a case-insensitive\n * substring scan over the row's string fields (the server runs real full-text\n * search over the collection's configured columns), and `include` cannot be\n * evaluated at all, because the related rows live in collections this query\n * knows nothing about.\n */\n\nconst collator = typeof Intl !== \"undefined\" && typeof Intl.Collator === \"function\"\n ? new Intl.Collator(undefined, { numeric: false, sensitivity: \"variant\" })\n : undefined;\n\n/**\n * The server's page size when the caller does not ask for one.\n *\n * Re-exported rather than redeclared. This was its own `= 20` — a third\n * constant of this name in the workspace, next to `@rebasepro/common`'s 200 and\n * the 50 the REST layer actually applies — and a local copy of a number that\n * belongs to another process is a number that goes stale silently.\n */\nexport { DEFAULT_LIST_LIMIT as DEFAULT_PAGE_SIZE } from \"@rebasepro/types\";\n\nfunction isNullish(value: unknown): boolean {\n return value === null || value === undefined;\n}\n\n/**\n * Reduce a value to something comparable. Relations compare by the id they\n * point at — the column holds a foreign key, so that is what the server\n * compares too.\n */\nfunction toComparable(value: unknown): unknown {\n if (value instanceof Date) return value.getTime();\n if (value instanceof EntityRelation) return value.id;\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n // A reference/relation that lost its prototype somewhere still\n // compares by id.\n if (typeof record.__type === \"string\" && \"id\" in record) return record.id;\n }\n return value;\n}\n\n/**\n * Three-way compare with SQL's type coercion but not its collation. Returns\n * `undefined` when the two values are not ordered relative to each other,\n * which is how NULL propagates through a comparison.\n */\nexport function compareValues(a: unknown, b: unknown): number | undefined {\n const left = toComparable(a);\n const right = toComparable(b);\n if (isNullish(left) || isNullish(right)) return undefined;\n\n if (typeof left === \"boolean\" || typeof right === \"boolean\") {\n const l = left === true || left === \"true\" || left === 1 ? 1 : 0;\n const r = right === true || right === \"true\" || right === 1 ? 1 : 0;\n return l - r;\n }\n\n // A numeric string on either side means the wire dropped the type; compare\n // as numbers so `[\"<\", \"10\"]` does not order \"10\" before \"9\" as text.\n const leftNum = typeof left === \"number\" ? left : numericOrNaN(left);\n const rightNum = typeof right === \"number\" ? right : numericOrNaN(right);\n if (!Number.isNaN(leftNum) && !Number.isNaN(rightNum)) {\n return leftNum < rightNum ? -1 : leftNum > rightNum ? 1 : 0;\n }\n\n // One side is a date-shaped string and the other an instant.\n if (typeof left === \"number\" || typeof right === \"number\") {\n const leftTime = toTime(left);\n const rightTime = toTime(right);\n if (leftTime !== undefined && rightTime !== undefined) {\n return leftTime < rightTime ? -1 : leftTime > rightTime ? 1 : 0;\n }\n }\n\n const leftStr = String(left);\n const rightStr = String(right);\n if (collator) return collator.compare(leftStr, rightStr);\n return leftStr < rightStr ? -1 : leftStr > rightStr ? 1 : 0;\n}\n\nfunction numericOrNaN(value: unknown): number {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const n = Number(value);\n return Number.isNaN(n) ? NaN : n;\n }\n if (typeof value === \"bigint\") return Number(value);\n return NaN;\n}\n\nfunction toTime(value: unknown): number | undefined {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") {\n const t = Date.parse(value);\n return Number.isNaN(t) ? undefined : t;\n }\n return undefined;\n}\n\n/** Equality with the wire's type erasure allowed for, but never across NULL. */\nexport function looseEquals(a: unknown, b: unknown): boolean {\n const left = toComparable(a);\n const right = toComparable(b);\n if (isNullish(left) || isNullish(right)) return isNullish(left) && isNullish(right);\n if (left === right) return true;\n const cmp = compareValues(left, right);\n return cmp === 0;\n}\n\n/**\n * Translate a SQL `LIKE` pattern to an anchored regular expression.\n * `%` matches any run of characters, `_` exactly one, and a backslash escapes\n * either of them.\n */\nfunction likeToRegExp(pattern: string, caseInsensitive: boolean): RegExp {\n let source = \"^\";\n // Runs of `%` collapse to one. `%%%%X` means exactly what `%X` means, but\n // as a regular expression it is four adjacent unbounded quantifiers, and on\n // a subject that does not match the engine tries every way of splitting the\n // subject between them. Fourteen of them against a forty-eight character\n // value took eighty-seven seconds to answer `false`.\n //\n // The pattern is user input — `?title=like.%25%25%25…` over HTTP — so that\n // is a request that pins a CPU. Collapsing is semantics-preserving and\n // removes the ambiguity the backtracking feeds on.\n let lastWasWildcard = false;\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i];\n if (char === \"\\\\\" && i + 1 < pattern.length) {\n source += pattern[i + 1].replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n i++;\n lastWasWildcard = false;\n } else if (char === \"%\") {\n if (!lastWasWildcard) source += \"[\\\\s\\\\S]*\";\n lastWasWildcard = true;\n } else if (char === \"_\") {\n source += \"[\\\\s\\\\S]\";\n lastWasWildcard = false;\n } else {\n source += char.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n lastWasWildcard = false;\n }\n }\n return new RegExp(source + \"$\", caseInsensitive ? \"i\" : \"\");\n}\n\nfunction asArray(value: unknown): unknown[] {\n if (Array.isArray(value)) return value;\n if (value === undefined) return [];\n return [value];\n}\n\n/** Evaluate one canonical operator against one row value. */\nexport function matchesOperator(rowValue: unknown, op: WhereFilterOp, filterValue: unknown): boolean {\n switch (op) {\n case \"is-null\":\n return isNullish(rowValue);\n case \"is-not-null\":\n return !isNullish(rowValue);\n case \"==\":\n return looseEquals(rowValue, filterValue);\n case \"!=\":\n // SQL: `x != v` is unknown when x is NULL, so the row drops out.\n if (isNullish(rowValue)) return false;\n return !looseEquals(rowValue, filterValue);\n case \"<\":\n case \"<=\":\n case \">\":\n case \">=\": {\n const cmp = compareValues(rowValue, filterValue);\n if (cmp === undefined) return false;\n if (op === \"<\") return cmp < 0;\n if (op === \"<=\") return cmp <= 0;\n if (op === \">\") return cmp > 0;\n return cmp >= 0;\n }\n case \"in\":\n if (isNullish(rowValue)) return false;\n return asArray(filterValue).some((v) => looseEquals(rowValue, v));\n case \"not-in\":\n if (isNullish(rowValue)) return false;\n return !asArray(filterValue).some((v) => looseEquals(rowValue, v));\n case \"array-contains\": {\n if (!Array.isArray(rowValue)) return false;\n return rowValue.some((v) => looseEquals(v, filterValue));\n }\n case \"array-contains-any\": {\n if (!Array.isArray(rowValue)) return false;\n const wanted = asArray(filterValue);\n return rowValue.some((v) => wanted.some((w) => looseEquals(v, w)));\n }\n case \"like\":\n case \"not-like\":\n case \"ilike\":\n case \"not-ilike\": {\n if (isNullish(rowValue)) return false;\n const insensitive = op === \"ilike\" || op === \"not-ilike\";\n const negated = op === \"not-like\" || op === \"not-ilike\";\n const matched = likeToRegExp(String(filterValue), insensitive).test(String(rowValue));\n return negated ? !matched : matched;\n }\n default:\n // An operator this build does not know must not silently drop rows.\n return true;\n }\n}\n\nfunction isTuple(value: unknown): value is [WhereFilterOp, unknown] {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === \"string\"\n && toCanonicalOp(value[0]) !== undefined;\n}\n\n/** Evaluate a `where` clause: every field, and every tuple on a field, AND-ed. */\nexport function matchesWhere(row: Record<string, unknown>, where: FilterValues<string> | undefined): boolean {\n if (!where) return true;\n for (const [field, condition] of Object.entries(where)) {\n if (condition === undefined) continue;\n const tuples: [WhereFilterOp, unknown][] = isTuple(condition)\n ? [condition]\n : Array.isArray(condition)\n ? (condition as unknown[]).filter(isTuple) as [WhereFilterOp, unknown][]\n : [];\n for (const [rawOp, value] of tuples) {\n const op = toCanonicalOp(rawOp) ?? rawOp;\n if (!matchesOperator(row[field], op, value)) return false;\n }\n }\n return true;\n}\n\n/** Evaluate a nested and/or tree. */\nexport function matchesLogical(\n row: Record<string, unknown>,\n condition: LogicalCondition | FilterCondition | undefined\n): boolean {\n if (!condition) return true;\n if (\"type\" in condition) {\n const children = condition.conditions ?? [];\n if (children.length === 0) return true;\n return condition.type === \"or\"\n ? children.some((c) => matchesLogical(row, c))\n : children.every((c) => matchesLogical(row, c));\n }\n const op = toCanonicalOp(condition.operator) ?? condition.operator;\n return matchesOperator(row[condition.column], op as WhereFilterOp, condition.value);\n}\n\n/**\n * Approximate the server's full-text search with a case-insensitive substring\n * scan over the row's own string fields. Narrower than the real thing (no\n * stemming, no configured search columns), and it never matches a field the\n * cached row does not carry — a local list may therefore be missing rows the\n * server would have returned, which is why {@link isExactlyEvaluable} refuses\n * to call a search query exact.\n */\nexport function matchesSearch(row: Record<string, unknown>, searchString: string | undefined): boolean {\n if (!searchString) return true;\n const needle = searchString.trim().toLowerCase();\n if (!needle) return true;\n for (const value of Object.values(row)) {\n if (typeof value === \"string\" && value.toLowerCase().includes(needle)) return true;\n if (typeof value === \"number\" && String(value).includes(needle)) return true;\n }\n return false;\n}\n\n/** Does this row belong in the result set for `params`, ignoring pagination? */\nexport function matchesParams(row: Record<string, unknown>, params?: FindParams): boolean {\n if (!params) return true;\n return matchesWhere(row, params.where)\n && matchesLogical(row, params.logical)\n && matchesSearch(row, params.searchString);\n}\n\n/**\n * Sort in place, Postgres-style: nulls last ascending, first descending, with\n * the row id as a tiebreak so paging through an unsorted-but-equal run does\n * not shuffle rows between pages.\n *\n * The tiebreak runs *descending*, which is not a taste: every server-side sort\n * ends on `id DESC` — `FetchService.buildOrderExpressions` appends it to make\n * the ordering total, and the keyset cursor is built to match. This ran\n * ascending, so two rows sharing a sort value came back from the local overlay\n * in the opposite order to the server's, and {@link isLocallySortable} called\n * that page exactly reproducible while it was not.\n */\nexport function sortRows<M extends Record<string, unknown>>(rows: M[], orderBy?: OrderBySpec): M[] {\n const keys = normalizeOrderBy(orderBy);\n if (!keys) return rows;\n return rows.sort((a, b) => {\n for (const [field, direction = \"asc\"] of keys) {\n const cmp = compareOnKey(a, b, field, direction);\n // Equal on this key — and equal is not a decision, so the next key\n // gets to make one. Returning the tiebreak here instead is what a\n // single-key sort does at the end, and doing it per key would order\n // by the id the moment two rows shared a role.\n if (cmp !== 0) return cmp;\n }\n return tiebreak(a, b);\n });\n}\n\n/** One key's verdict: negative, positive, or 0 for \"these two are equal here\". */\nfunction compareOnKey(\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n field: string,\n direction: \"asc\" | \"desc\"\n): number {\n const av = a[field];\n const bv = b[field];\n const aNull = isNullish(toComparable(av));\n const bNull = isNullish(toComparable(bv));\n if (aNull || bNull) {\n if (aNull && bNull) return 0;\n // NULLS LAST ascending, NULLS FIRST descending.\n return (aNull ? 1 : -1) * (direction === \"desc\" ? -1 : 1);\n }\n const cmp = compareValues(av, bv);\n if (cmp === undefined || cmp === 0) return 0;\n return cmp * (direction === \"desc\" ? -1 : 1);\n}\n\n/** The last word, and the server's: `id DESC`. */\nfunction tiebreak(a: Record<string, unknown>, b: Record<string, unknown>): number {\n const cmp = compareValues(a.id, b.id);\n return cmp === undefined ? 0 : -cmp;\n}\n\n/**\n * Resolve `page`/`offset`/`limit` the way the server does.\n *\n * It did not: this defaulted an absent limit to 20 while `/api/data` pages by\n * 50, so the same `observe()` answered with 20 rows from the local database and\n * 50 from the network — a list that changed length depending on which side\n * answered, with `page` striding differently on each. Delegated now, so the\n * sentence above is true by construction rather than by agreement.\n */\nexport function resolvePagination(params?: FindParams): { limit: number; offset: number } {\n const { limit, offset } = resolveFindWindow(params);\n return { limit, offset };\n}\n\n/** `<`, `<=`, `>`, `>=` — the operators whose answer depends on a collation. */\nconst ORDERING_OPS = new Set<WhereFilterOp>([\"<\", \"<=\", \">\", \">=\"]);\n\n/** Does any condition in this `where` clause order its operands? */\nfunction whereOrders(where: FilterValues<string> | undefined): boolean {\n if (!where) return false;\n for (const condition of Object.values(where)) {\n const tuples = isTuple(condition) ? [condition] : (condition as unknown[]).filter(isTuple);\n if (tuples.some(([op]) => ORDERING_OPS.has(op))) return true;\n }\n return false;\n}\n\n/** The same question, through an `and(...)`/`or(...)` tree. */\nfunction logicalOrders(condition: LogicalCondition | FilterCondition | undefined, depth = 0): boolean {\n if (!condition || depth > 32) return false;\n if (\"type\" in condition) {\n return (condition.conditions ?? []).some((c) => logicalOrders(c, depth + 1));\n }\n return ORDERING_OPS.has(condition.operator);\n}\n\n/**\n * Can a locally evaluated answer to `params` be trusted to match the server's,\n * assuming the cache holds every row of the collection?\n *\n * `include` pulls in rows from other collections that this evaluator never\n * sees, and `searchString` is only approximated — both make the local answer a\n * best effort rather than an equivalent one.\n *\n * **Ordering comparisons are refused, and that is the interesting one.**\n * `compareValues` falls back to an `Intl.Collator` for operands it cannot read\n * as numbers or instants. PostgreSQL orders text by the *database's* collation,\n * which is a property of the server this process has never been told: under the\n * C collation `'apple' < 'Banana'` is false, under `en_US.UTF-8` it is true,\n * and the collator says true. So `[\"<\", \"Banana\"]` selects a different set here\n * than it does there — silently, and in whichever direction the deployment\n * happens to have been created.\n *\n * The refusal covers *every* ordering comparison rather than only the ones with\n * a string operand, because the operand type does not settle it: a numeric\n * bound against a text column (`[\"<\", 10]` on a `varchar`) also reaches the\n * collator, and nothing in `params` says what the column holds. Conservative on\n * purpose — the cost is that a query combining an ordering filter with\n * *unsynced local writes* stops placing those writes optimistically, which is a\n * degraded answer rather than a wrong one. Claiming exactness we do not have is\n * the other way round.\n *\n * This says nothing about ordering *results*; that is a separate claim with a\n * separate answer, because a sort changes which rows come first and not which\n * rows match. See {@link isLocallySortable}.\n */\nexport function isExactlyEvaluable(params?: FindParams): boolean {\n if (!params) return true;\n if (params.include && params.include.length > 0) return false;\n if (params.searchString) return false;\n // Nearest-neighbour ordering is the server's to compute: the cache holds no\n // vectors, and even with them, answering from a subset would return the\n // nearest of what happens to be cached while looking like the nearest there\n // are — a wrong answer that is indistinguishable from a right one.\n if (params.vectorSearch) return false;\n if (whereOrders(params.where)) return false;\n if (logicalOrders(params.logical)) return false;\n return true;\n}\n\n/**\n * Would sorting `rows` locally reproduce the order the server would have sent?\n *\n * Asked of the rows rather than of the query, because unlike a filter this one\n * *is* decidable from the data in hand: {@link compareValues} reaches the\n * collator only when it cannot read both operands as numbers, and `toComparable`\n * has already turned dates and relations into numbers and ids by then. If every\n * value on the sort column normalises to a number, the collator is unreachable\n * and the local order is the server's order.\n *\n * A text column is therefore refused — see {@link isExactlyEvaluable} for why\n * the two cannot be made to agree — and so is a column this page happens to see\n * only as strings, which is the same thing from here.\n *\n * Nulls are fine either way: they are ordered by an explicit rule (last\n * ascending, first descending) that matches Postgres and never reaches the\n * comparator.\n */\nexport function isLocallySortable(\n rows: readonly Record<string, unknown>[],\n orderBy?: OrderBySpec\n): boolean {\n const keys = normalizeOrderBy(orderBy);\n if (!keys) return true;\n // Every key has to be decidable, not just the first: a sort the local side\n // can only agree with down to its second column is one it disagrees with.\n return keys.every(([field]) => rows.every((row) => {\n const value = toComparable(row[field]);\n if (isNullish(value)) return true;\n if (typeof value === \"number\" || typeof value === \"boolean\") return true;\n if (typeof value === \"bigint\") return true;\n // A numeric string is compared as a number, so it is safe too — this is\n // the wire's type erasure, which `compareValues` already undoes.\n if (typeof value === \"string\" && value.trim() !== \"\" && !Number.isNaN(Number(value))) return true;\n return false;\n }));\n}\n\n/** Run a full query — filter, sort, paginate — over a set of rows. */\nexport function runLocalQuery<M extends Record<string, unknown>>(\n rows: M[],\n params?: FindParams\n): FindResult<M> {\n const matched = rows.filter((row) => matchesParams(row, params));\n sortRows(matched, params?.orderBy);\n const { limit, offset } = resolvePagination(params);\n const page = matched.slice(offset, offset + limit);\n return {\n data: page,\n meta: {\n total: matched.length,\n limit,\n offset,\n hasMore: offset + page.length < matched.length\n }\n };\n}\n","import { buildQueryString, FindParams, RebaseApiError } from \"./transport\";\nimport { FindAllParams, FindResult, IterateParams, LogicalCondition, SDKCollectionClient, WhereFilterOp, WhereValueFor, WriteOptions } from \"@rebasepro/types\";\nimport { collectAllPages, paginateFind } from \"@rebasepro/common\";\nimport { CollectionClient, LiveResult, ObserveOptions, RowSnapshotMeta } from \"./collection\";\nimport { SDKQueryBuilder } from \"./sdk_query_builder\";\nimport { dehydrateRow, hydrateRow } from \"./offline-codec\";\nimport {\n ConnectivityMonitor,\n isDuplicateKeyError,\n isIdempotencyInProgressError,\n isNetworkError,\n isRetryableError\n} from \"./offline-connectivity\";\nimport {\n IndexedDBOfflineStore,\n MemoryOfflineStore,\n OfflineStore,\n PendingMutation,\n createMutationId\n} from \"./offline-store\";\nimport {\n isExactlyEvaluable,\n isLocallySortable,\n matchesParams,\n resolvePagination,\n runLocalQuery,\n sortRows\n} from \"./offline-query\";\n\n/**\n * The SDK's local-first sync engine.\n *\n * The design goal is that the network is never in the way of the interface.\n * That comes from three properties, and everything in this file exists to\n * serve one of them:\n *\n * 1. **A local database, not a response cache.** Rows are stored normalized,\n * by id, and queries are answered by evaluating them\n * ({@link ./offline-query}) against those rows. A row written offline\n * therefore appears in *every* list it belongs to, a row edited in one view\n * updates in all of them, and `findById` answers for a row only ever seen\n * inside a `find`. Server responses are merged into this database rather\n * than replacing it, and a row with unsynced local writes keeps them: the\n * user's own change never flickers away underneath them.\n *\n * 2. **Writes are decided locally.** A write made while offline is applied to\n * the local database and queued — with the state it replaced, so a server\n * rejection can be undone — and the call returns immediately. When\n * connectivity is known to be gone the request is not even attempted, so\n * an offline write costs nothing instead of a timeout.\n *\n * 3. **Reads are reactive.** {@link OfflineManager.observe} emits from the\n * local database synchronously-ish, revalidates in the background, and\n * re-emits whenever anything touches the rows it covers — a local write,\n * a replay landing, a rollback, a realtime event, or another browser tab.\n *\n * What it deliberately is not: a full replica. Only rows the app has actually\n * read or written are local, so a query the cache cannot fully answer is\n * flagged `partial` rather than silently reported as complete.\n */\n\nexport interface OfflineConfig {\n /**\n * Persistence backend. Defaults to IndexedDB in the browser and an\n * in-memory store elsewhere; pass a custom implementation (e.g. backed by\n * AsyncStorage in React Native) to persist in other environments.\n */\n store?: OfflineStore;\n /**\n * Cached query snapshots kept per collection; the least recently written\n * are evicted beyond this. Defaults to 50.\n */\n maxCachedQueriesPerCollection?: number;\n /**\n * Cached rows kept per collection. Rows with unsynced local writes are\n * never evicted. Defaults to 5 000.\n */\n maxCachedRowsPerCollection?: number;\n /**\n * Ceiling for the exponential retry backoff, in milliseconds. Replay\n * retries start at one second and double up to this. `0` disables\n * automatic retries entirely — `client.offline.sync()`, a sign-in, and the\n * browser's `online` event still trigger one. Defaults to 60 000.\n */\n syncIntervalMs?: number;\n /**\n * Keep several tabs of the same app in step over a `BroadcastChannel`: a\n * write in one appears in the others, and only one of them replays the\n * shared queue. Defaults to on for the IndexedDB store (a real shared\n * database) and off for the in-memory one, which no other tab can see.\n */\n crossTab?: boolean;\n /**\n * How many times a mutation rejected with a *retryable* status (429, 503,\n * …) is replayed before it is given up on and rolled back. Network\n * failures do not count against this: being offline is not an attempt.\n * Defaults to 5.\n */\n maxRetries?: number;\n /**\n * Called when the server *rejects* a queued mutation (a 4xx/5xx that will\n * not resolve on its own — validation, RLS, a since-deleted row). The\n * local rows it wrote are rolled back to the state they had before it, and\n * any later queued writes to the same rows are discarded with it — they\n * were built on a change that never happened. Each discarded mutation is\n * reported here.\n *\n * Network failures are not errors: those mutations stay queued.\n */\n onSyncError?: (error: Error, mutation: PendingMutation) => void;\n}\n\n/** A snapshot of the engine's state, for a status indicator. */\nexport interface OfflineStatus {\n /** False once a request has failed to reach the server, until one does. */\n online: boolean;\n /** True while the queue is being replayed. */\n syncing: boolean;\n /** Local writes not yet accepted by the server. */\n pending: number;\n /** When the queue was last fully drained. */\n lastSyncedAt?: number;\n /** The last replay rejection, if any. */\n lastError?: string;\n}\n\nexport type { LiveResult, ObserveOptions, RowSnapshotMeta } from \"./collection\";\n\n/** What `client.offline` exposes to the app. */\nexport interface OfflineApi {\n /** Replay the queue now. Resolves with what was flushed and what remains. */\n sync(): Promise<{ flushed: number; remaining: number }>;\n /** The queued mutations for the current user, oldest first. */\n pending(): Promise<PendingMutation[]>;\n /** The current engine state — connectivity, queue depth, last sync. */\n status(): OfflineStatus;\n /** Subscribe to {@link OfflineStatus} changes (for a sync indicator). */\n onStatusChange(listener: (status: OfflineStatus) => void): () => void;\n /**\n * Drop the current user's queued mutations AND their local rows.\n * Destructive: queued writes are lost, not replayed. For \"discard my\n * offline changes\" flows, not for sign-out (scoping already isolates\n * users).\n */\n clear(): Promise<void>;\n /** Subscribe to queue-size changes (for a \"pending changes\" badge). */\n onQueueChange(listener: (count: number) => void): () => void;\n}\n\n/** True when a read failed because there was neither network nor local data. */\nexport function isOfflineError(error: unknown): boolean {\n return error instanceof RebaseApiError && error.code === \"offline\";\n}\n\nfunction offlineError(message: string): RebaseApiError {\n return new RebaseApiError(message, { status: 0, code: \"offline\" });\n}\n\nfunction generateOfflineId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n // Non-cryptographic fallback for exotic runtimes; collision odds are\n // irrelevant at offline-queue scale.\n return `off-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\ntype AnyRow = Record<string, unknown>;\ntype InnerFactory = (slug: string) => SDKCollectionClient<AnyRow>;\n\n/** What the server said about one query, as ids into the local row database. */\ninterface QuerySnapshot {\n ids: (string | number)[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\ninterface RowEntry {\n row: AnyRow;\n cachedAt: number;\n /** Bumped on every local change, so observers can diff cheaply. */\n rev: number;\n}\n\ninterface CollectionState {\n rows: Map<string, RowEntry>;\n snapshots: Map<string, QuerySnapshot>;\n /**\n * Query keys whose snapshot came from a request that completed in this\n * session. Deliberately not persisted: a snapshot read back off disk is\n * exactly what \"from the cache\" means, however recent it looks.\n */\n fresh: Set<string>;\n /** The same, per row id, for `observeById`. */\n freshRows: Set<string>;\n /** Ids the server has confirmed do not exist — a negative cache. */\n absent: Set<string>;\n loaded?: Promise<void>;\n /**\n * True once the persisted rows are in memory. Observers must not emit\n * before this: an empty map during the load is not an empty collection,\n * and emitting it would flash an empty list over real data.\n */\n ready: boolean;\n}\n\ninterface Observer {\n slug: string;\n params?: FindParams;\n /** Set for observeById; then `params` is unused. */\n id?: string | number;\n emit: () => void;\n /** Re-run this observer's query against the server. */\n refresh: () => Promise<unknown>;\n signature?: string;\n error?: Error;\n settled: boolean;\n}\n\n// `\\u0000` as an escape, not a raw NUL byte in the source. The value is\n// identical — an id can never contain it, which is the point — but written raw it\n// made this whole file test as binary, so every `grep` over the repo skipped\n// all 1,700 lines of it in silence.\nconst MISSING = \"\\u0000missing\";\n\n/**\n * Replays to spend on a mutation whose idempotency key the server is still\n * holding, when the app has not asked for more.\n *\n * Retries double from a second and cap at the sync interval, so the default\n * budget of five covers about half a minute — less than the lease a server\n * gives a claim nobody came back for. This many outlast it with room for a slow\n * batch, and the count is what stops a server that never releases the key from\n * blocking the queue behind it indefinitely.\n */\nconst IN_PROGRESS_MIN_RETRIES = 12;\n\nexport class OfflineManager {\n private readonly store: OfflineStore;\n private readonly maxCachedQueries: number;\n private readonly maxCachedRows: number;\n private readonly maxRetries: number;\n private readonly onSyncError?: OfflineConfig[\"onSyncError\"];\n private readonly createInner: InnerFactory;\n private readonly inners = new Map<string, SDKCollectionClient<AnyRow>>();\n private readonly connectivity: ConnectivityMonitor;\n\n private scope = \"anon\";\n /** The local database: normalized rows and query snapshots per collection. */\n private collections = new Map<string, CollectionState>();\n /** In-memory mirror of the current scope's queue, in replay order. */\n private queue: PendingMutation[] = [];\n /**\n * The mutation currently on the wire, if any.\n *\n * `flush` awaits `replay(op)` with `op` still at the head of `queue`, so for\n * the whole duration of that request the in-flight op is also the queue's\n * *tail* whenever it is the only entry. Both shortcuts in `enqueue` reach\n * for the tail, and neither may touch an op the server is already reading:\n *\n * - Coalescing an update into it mutates a payload that has already been\n * serialized and sent, and `drop` then removes the whole entry on ACK —\n * so the second edit is neither sent nor kept. A silently lost write.\n * - Cancelling it out against a delete assumes the server never saw the\n * create. It is seeing it right now, so the row would be created and the\n * delete never queued — an orphan row nothing will ever remove.\n *\n * Guarding on the id rather than on a boolean keeps this correct if the\n * flush loop ever sends more than one op at a time.\n */\n private inFlightId: string | null = null;\n private queueLoad?: Promise<void>;\n /** Serializes enqueues so concurrent writes keep the order the app made them. */\n private enqueueChain: Promise<unknown> = Promise.resolve();\n private flushPromise?: Promise<{ flushed: number; remaining: number }>;\n private queueListeners = new Set<(count: number) => void>();\n private statusListeners = new Set<(status: OfflineStatus) => void>();\n private observers = new Map<string, Set<Observer>>();\n private refreshPending = new Set<string>();\n private revCounter = 0;\n private disposed = false;\n private currentStatus: OfflineStatus = { online: true, syncing: false, pending: 0 };\n private readonly channel?: BroadcastChannel;\n private readonly tabId = createMutationId();\n\n readonly api: OfflineApi;\n\n constructor(config: OfflineConfig, createInner: InnerFactory) {\n this.store = config.store\n ?? (typeof indexedDB !== \"undefined\" ? new IndexedDBOfflineStore() : new MemoryOfflineStore());\n this.maxCachedQueries = config.maxCachedQueriesPerCollection ?? 50;\n this.maxCachedRows = config.maxCachedRowsPerCollection ?? 5_000;\n this.maxRetries = config.maxRetries ?? 5;\n this.onSyncError = config.onSyncError;\n this.createInner = createInner;\n\n const maxBackoffMs = config.syncIntervalMs ?? 60_000;\n this.connectivity = new ConnectivityMonitor({\n maxBackoffMs: Math.max(1_000, maxBackoffMs),\n // With no retry timer nothing would ever reopen the window, so a\n // single failure would strand the client offline forever.\n respectBackoff: maxBackoffMs > 0\n });\n if (maxBackoffMs > 0) {\n this.connectivity.onRetryDue = () => { void this.sync().catch(() => undefined); };\n }\n this.connectivity.onChange((online) => {\n this.patchStatus({ online });\n if (online) this.revalidateAll();\n });\n this.currentStatus.online = this.connectivity.isOnline();\n\n // Other tabs share the same IndexedDB. Without this they would each\n // hold a stale copy of the row database and quietly diverge — one tab\n // showing an edit the other never learns about. A memory store is not\n // shared with anyone, so there is nothing to reconcile and the channel\n // would only relay writes between unrelated clients.\n const crossTab = config.crossTab ?? this.store instanceof IndexedDBOfflineStore;\n if (crossTab && typeof BroadcastChannel !== \"undefined\") {\n try {\n this.channel = new BroadcastChannel(\"rebase-offline\");\n this.channel.onmessage = (event: MessageEvent) => this.onBroadcast(event.data);\n // Node's BroadcastChannel is ref'd, and a script that opened a\n // client should still be able to exit.\n (this.channel as unknown as { unref?: () => void }).unref?.();\n } catch {\n // Not fatal: a browser that refuses the channel just loses\n // cross-tab propagation.\n }\n }\n\n this.api = {\n sync: () => this.sync(),\n pending: async () => {\n await this.ensureQueueLoaded();\n // Deep-copied: these are live queue entries (tail coalescing\n // mutates them in place), and a caller must not be able to\n // edit what will be replayed.\n return this.queue.map((m) => structuredClone(m));\n },\n status: () => ({ ...this.currentStatus }),\n onStatusChange: (listener) => {\n this.statusListeners.add(listener);\n return () => this.statusListeners.delete(listener);\n },\n clear: async () => {\n await this.store.clear(`${this.scope}|`);\n this.queue = [];\n this.resetCollections();\n this.patchStatus({ pending: 0, lastError: undefined });\n this.notifyQueue();\n for (const slug of this.observers.keys()) this.notifyCollection(slug, false);\n },\n onQueueChange: (listener) => {\n this.queueListeners.add(listener);\n return () => this.queueListeners.delete(listener);\n }\n };\n }\n\n /**\n * Cache and queue are partitioned per signed-in user: cached rows are\n * RLS-filtered for the user who fetched them, and queued writes must\n * replay under the credentials that made them — so neither may ever leak\n * across a sign-out/sign-in on a shared browser.\n */\n setScope(uid: string | undefined): void {\n const next = uid || \"anon\";\n if (next === this.scope) return;\n this.scope = next;\n this.queueLoad = undefined;\n this.queue = [];\n this.resetCollections();\n this.patchStatus({ pending: 0, lastError: undefined });\n this.notifyQueue();\n // Everything on screen belongs to the previous user.\n for (const slug of this.observers.keys()) this.notifyCollection(slug, false);\n this.revalidateAll();\n // The returning user's queue may hold writes from a previous session.\n void this.sync().catch(() => undefined);\n }\n\n /**\n * Throw away every local row, for a scope change or an explicit clear.\n *\n * The state objects are replaced rather than emptied, so a load still in\n * flight for the previous user fails its identity check and discards what\n * it read instead of grafting it onto the new one. The replacements are\n * marked ready: nothing needs loading until something asks, and observers\n * have to be told *now* that the rows they are showing are gone.\n */\n private resetCollections(): void {\n const slugs = [...this.collections.keys()];\n this.collections = new Map();\n for (const slug of slugs) {\n this.collections.set(slug, {\n rows: new Map(),\n snapshots: new Map(),\n fresh: new Set(),\n freshRows: new Set(),\n absent: new Set(),\n ready: true\n });\n }\n }\n\n /** Release listeners, timers and the cross-tab channel (client.close()). */\n dispose(): void {\n this.disposed = true;\n this.connectivity.dispose();\n try {\n this.channel?.close();\n } catch {\n // A channel that is already closed is not a problem.\n }\n this.observers.clear();\n this.queueListeners.clear();\n this.statusListeners.clear();\n }\n\n // ─── Collection wrapping ─────────────────────────────────────────────────\n\n wrap<M extends AnyRow>(slug: string, inner: CollectionClient<M>): CollectionClient<M> {\n this.inners.set(slug, inner as SDKCollectionClient<AnyRow>);\n\n const wrapped: CollectionClient<M> = {\n find: async (params?: FindParams<M>): Promise<FindResult<M>> => {\n const state = await this.ensureCollection(slug);\n if (this.connectivity.shouldAttempt()) {\n try {\n const res = await inner.find(params);\n this.connectivity.markSuccess();\n await this.ingest(slug, res.data ?? []);\n const snapshot = this.recordSnapshot(slug, params, res);\n const answer = this.answer<M>(slug, params, snapshot);\n this.notifyCollection(slug, false);\n return { data: answer.data, meta: answer.meta };\n } catch (error) {\n if (!isNetworkError(error)) {\n // A 5xx or a rate limit still deserves the cached\n // answer rather than an exception the app has to\n // special-case, but only when we have one.\n if (isRetryableError(error) && this.hasLocalAnswer(state, slug, params)) {\n const answer = this.answer<M>(slug, params, this.snapshotFor(slug, params));\n return { data: answer.data, meta: answer.meta };\n }\n throw error;\n }\n this.connectivity.markFailure();\n }\n }\n const answer = this.localFind<M>(slug, params);\n // Falling back is a state change even when the rows are the\n // same — it is how a \"showing cached data\" badge lights up.\n this.notifyCollection(slug, false);\n return { data: answer.data, meta: answer.meta };\n },\n\n // Paginates the *wrapped* find, so a walk started offline is served\n // page by page out of the local database exactly as it would be\n // from the server, and rejoins the network mid-walk if it returns.\n iterate: (params?: IterateParams<M>) => paginateFind<M>((p) => wrapped.find(p), params, slug),\n\n findAll: (params?: FindAllParams<M>) => collectAllPages<M>((p) => wrapped.find(p), params, slug),\n\n findById: async (id: string | number) => {\n await this.ensureCollection(slug);\n if (this.connectivity.shouldAttempt()) {\n try {\n const row = await inner.findById(id);\n this.connectivity.markSuccess();\n if (row !== undefined) {\n await this.ingest(slug, [row]);\n } else if (!this.hasPending(slug, id)) {\n // The server is authoritative that it is gone, and\n // nothing local is waiting to recreate it.\n this.removeLocalRow(slug, id, true);\n }\n this.notifyCollection(slug, false);\n return this.localRow<M>(slug, id);\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const local = this.localRow<M>(slug, id);\n if (local !== undefined || this.hasPending(slug, id)) return local;\n // \"Not there\" is an answer, and one we may already have.\n if (this.collections.get(slug)?.absent.has(String(id))) return undefined;\n throw offlineError(\n `Offline: \"${slug}\" row ${String(id)} is not in the local database.`\n );\n },\n\n create: async (data: Partial<M>, id?: string | number) => {\n await this.ensureCollection(slug);\n if (this.connectivity.shouldAttempt()) {\n try {\n const row = await inner.create(data, id);\n this.connectivity.markSuccess();\n await this.ingest(slug, [row]);\n this.notifyCollection(slug);\n this.scheduleRefresh(slug);\n return row;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const providedId = id ?? (data as AnyRow).id as string | number | undefined;\n const rowId = providedId ?? generateOfflineId();\n const row = { ...(data as AnyRow), id: rowId } as unknown as M;\n await this.enqueue({\n collection: slug,\n type: \"create\",\n id: rowId,\n data: row,\n generatedId: providedId === undefined,\n rollback: { rows: { [String(rowId)]: this.rawLocalRow(slug, rowId) ?? null } }\n });\n this.setLocalRow(slug, rowId, row);\n this.notifyCollection(slug);\n return row;\n },\n\n createMany: async (data: Partial<M>[], options?: { upsert?: boolean }) => {\n await this.ensureCollection(slug);\n if (!Array.isArray(data)) {\n throw new TypeError(\"createMany expects an array of records.\");\n }\n if (data.length === 0) return [];\n if (this.connectivity.shouldAttempt()) {\n try {\n const rows = await inner.createMany(data, options);\n this.connectivity.markSuccess();\n await this.ingest(slug, rows);\n this.notifyCollection(slug);\n this.scheduleRefresh(slug);\n return rows;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const rows = data.map((r) => ({\n ...(r as AnyRow),\n id: (r as AnyRow).id ?? generateOfflineId()\n })) as unknown as M[];\n const rollback: Record<string, AnyRow | null> = {};\n for (const row of rows) {\n const key = String(row.id);\n rollback[key] = this.rawLocalRow(slug, row.id as string | number) ?? null;\n }\n await this.enqueue({\n collection: slug,\n type: \"createMany\",\n data: rows,\n upsert: options?.upsert,\n rollback: { rows: rollback }\n });\n for (const row of rows) this.setLocalRow(slug, row.id as string | number, row);\n this.notifyCollection(slug);\n return rows;\n },\n\n updateMany: async (updates: { id: string | number; data: Partial<M> }[], options?: WriteOptions) => {\n await this.ensureCollection(slug);\n if (!Array.isArray(updates)) {\n throw new TypeError(\"updateMany expects an array of { id, data } entries.\");\n }\n if (updates.length === 0) return [];\n // Any row in the batch with a write already queued sends the\n // whole batch to the queue. Splitting it — some rows now, some\n // later — would break the one guarantee a batch makes, that its\n // rows land together, and would reorder writes against a row\n // whose own create has not landed yet.\n const anyPending = updates.some((u) => this.hasPending(slug, u.id));\n if (this.connectivity.shouldAttempt() && !anyPending) {\n try {\n const rows = await inner.updateMany(updates, options);\n this.connectivity.markSuccess();\n await this.ingest(slug, rows);\n this.notifyCollection(slug);\n return rows;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const rollback: Record<string, AnyRow | null> = {};\n const optimistic: M[] = [];\n for (const { id, data } of updates) {\n const base = this.rawLocalRow(slug, id);\n rollback[String(id)] = base ?? null;\n optimistic.push({ ...(base ?? {}), ...(data as AnyRow), id } as unknown as M);\n }\n await this.enqueue({\n collection: slug,\n type: \"updateMany\",\n updates: updates.map((u) => ({ id: u.id,\ndata: u.data as AnyRow })),\n rollback: { rows: rollback }\n });\n for (const row of optimistic) this.setLocalRow(slug, row.id as string | number, row);\n this.notifyCollection(slug);\n return optimistic;\n },\n\n deleteMany: async (ids: (string | number)[], options?: WriteOptions) => {\n await this.ensureCollection(slug);\n if (!Array.isArray(ids)) {\n throw new TypeError(\"deleteMany expects an array of ids.\");\n }\n if (ids.length === 0) return;\n const anyPending = ids.some((id) => this.hasPending(slug, id));\n if (this.connectivity.shouldAttempt() && !anyPending) {\n try {\n await inner.deleteMany(ids, options);\n this.connectivity.markSuccess();\n for (const id of ids) this.removeLocalRow(slug, id, true);\n this.notifyCollection(slug);\n this.scheduleRefresh(slug);\n return;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const rollback: Record<string, AnyRow | null> = {};\n for (const id of ids) {\n rollback[String(id)] = this.rawLocalRow(slug, id) ?? null;\n }\n await this.enqueue({\n collection: slug,\n type: \"deleteMany\",\n ids,\n rollback: { rows: rollback }\n });\n for (const id of ids) this.removeLocalRow(slug, id, false);\n this.notifyCollection(slug);\n },\n\n update: async (id: string | number, data: Partial<M>) => {\n await this.ensureCollection(slug);\n // Never overtake a write already queued for this row. The\n // reads already respect the queue; the writes did not, so an\n // edit made while the row's own create was still pending went\n // straight to a server that had never heard of the row and came\n // back 404 — the caller's edit failing on a row they could see.\n // Queuing keeps the order the app issued the writes in.\n if (this.connectivity.shouldAttempt() && !this.hasPending(slug, id)) {\n try {\n const row = await inner.update(id, data);\n this.connectivity.markSuccess();\n await this.ingest(slug, [row]);\n this.notifyCollection(slug);\n return row;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const base = this.rawLocalRow(slug, id);\n await this.enqueue({\n collection: slug,\n type: \"update\",\n id,\n data: data as AnyRow,\n rollback: { rows: { [String(id)]: base ?? null } }\n });\n const optimistic = { ...(base ?? {}), ...(data as AnyRow), id } as unknown as M;\n this.setLocalRow(slug, id, optimistic);\n this.notifyCollection(slug);\n return optimistic;\n },\n\n delete: async (id: string | number) => {\n await this.ensureCollection(slug);\n // As in `update`: a delete must not overtake this row's own\n // queued create, or it 404s and the create then lands behind\n // it, leaving the row the caller just deleted.\n if (this.connectivity.shouldAttempt() && !this.hasPending(slug, id)) {\n try {\n await inner.delete(id);\n this.connectivity.markSuccess();\n this.removeLocalRow(slug, id, true);\n this.notifyCollection(slug);\n this.scheduleRefresh(slug);\n return;\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n await this.enqueue({\n collection: slug,\n type: \"delete\",\n id,\n rollback: { rows: { [String(id)]: this.rawLocalRow(slug, id) ?? null } }\n });\n this.removeLocalRow(slug, id);\n this.notifyCollection(slug);\n },\n\n count: async (params?: FindParams<M>): Promise<number> => {\n await this.ensureCollection(slug);\n if (this.connectivity.shouldAttempt()) {\n try {\n const n = await inner.count(params);\n this.connectivity.markSuccess();\n void this.writeCache(this.countKey(slug, params), n);\n return Math.max(0, n + this.pendingDelta(slug, params));\n } catch (error) {\n if (!isNetworkError(error)) throw error;\n this.connectivity.markFailure();\n }\n }\n const cached = await this.readCache<number>(this.countKey(slug, params));\n if (cached !== undefined) return Math.max(0, cached + this.pendingDelta(slug, params));\n const state = this.collections.get(slug);\n if (state && state.rows.size > 0) {\n return runLocalQuery([...state.rows.values()].map((e) => e.row), params).meta.total;\n }\n throw offlineError(`Offline: no cached count for \"${slug}\".`);\n },\n\n observe: (\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) => this.observe<M>(slug, wrapped, inner, params, onResult, onError, options),\n\n observeById: (\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ) => this.observeById<M>(slug, wrapped, inner, id, onResult, onError, options),\n\n // The builder calls back into `wrapped.find(...)`, so fluent\n // queries go through the local database like direct calls.\n where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOp, value?: unknown) {\n const builder = new SDKQueryBuilder<M>(wrapped);\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) => new SDKQueryBuilder<M>(wrapped).orderBy(column, direction),\n limit: (count) => new SDKQueryBuilder<M>(wrapped).limit(count),\n offset: (count) => new SDKQueryBuilder<M>(wrapped).offset(count),\n search: (searchString, options) => new SDKQueryBuilder<M>(wrapped).search(searchString, options),\n vectorSearch: (property, vector, options) => new SDKQueryBuilder<M>(wrapped).vectorSearch(property, vector, options),\n include: (...relations) => new SDKQueryBuilder<M>(wrapped).include(...relations)\n };\n\n // Realtime stays a live server stream — but everything it delivers is\n // worth keeping, so it feeds the local database on its way past.\n if (inner.listen) {\n wrapped.listen = (params, onUpdate, onError) => inner.listen!(\n params,\n (response) => {\n void this.ingest(slug, response.data ?? []).then(() => this.notifyCollection(slug, false));\n onUpdate(response);\n },\n onError\n );\n }\n if (inner.listenById) {\n wrapped.listenById = (id, onUpdate, onError) => inner.listenById!(\n id,\n (row) => {\n if (row) void this.ingest(slug, [row]).then(() => this.notifyCollection(slug, false));\n onUpdate(row);\n },\n onError\n );\n }\n\n return wrapped;\n }\n\n // ─── Live queries ────────────────────────────────────────────────────────\n\n private observe<M extends AnyRow>(\n slug: string,\n wrapped: CollectionClient<M>,\n inner: CollectionClient<M>,\n params: FindParams<M> | undefined,\n onResult: (result: LiveResult<M>) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ): () => void {\n let closed = false;\n let unlisten: (() => void) | undefined;\n\n const observer: Observer = {\n slug,\n params,\n settled: false,\n refresh: () => wrapped.find(params).catch(() => undefined),\n emit: () => {\n if (closed || !this.collections.get(slug)?.ready) return;\n const result = this.answer<M>(slug, params, this.snapshotFor(slug, params));\n // Every field the callback receives has to be in the\n // signature, or a change to one of them is deduplicated away —\n // a row settling from \"saving\" to saved is exactly that.\n const signature = `${result.fromCache ? \"c\" : \"s\"}${result.hasPendingWrites ? \"p\" : \"-\"}`\n + this.signature(slug, result.data, result.meta.total);\n if (observer.settled && signature === observer.signature) return;\n observer.signature = signature;\n observer.settled = true;\n onResult(observer.error ? { ...result, error: observer.error } : result);\n }\n };\n this.observersFor(slug).add(observer);\n\n void (async () => {\n await this.ensureCollection(slug);\n if (closed) return;\n // Emit whatever is already local before touching the network. An\n // app that has run this query before renders instantly.\n if (this.hasLocalAnswer(this.collections.get(slug), slug, params)) observer.emit();\n try {\n await wrapped.find(params);\n observer.error = undefined;\n } catch (error) {\n observer.error = error as Error;\n if (closed) return;\n // A read that found nothing locally has nothing to emit, so the\n // failure is all the app gets.\n if (!observer.settled) {\n onError?.(error as Error);\n return;\n }\n }\n if (!closed) observer.emit();\n })();\n\n if (options?.realtime !== false && inner.listen) {\n unlisten = inner.listen(params, (response) => {\n void this.ingest(slug, response.data ?? []).then(() => {\n this.recordSnapshot(slug, params, response);\n this.notifyCollection(slug, false);\n });\n }, onError);\n }\n\n return () => {\n closed = true;\n this.observersFor(slug).delete(observer);\n unlisten?.();\n };\n }\n\n private observeById<M extends AnyRow>(\n slug: string,\n wrapped: CollectionClient<M>,\n inner: CollectionClient<M>,\n id: string | number,\n onResult: (row: M | undefined, meta: RowSnapshotMeta) => void,\n onError?: (error: Error) => void,\n options?: ObserveOptions\n ): () => void {\n let closed = false;\n let unlisten: (() => void) | undefined;\n const observer: Observer = {\n slug,\n id,\n settled: false,\n refresh: () => wrapped.findById(id).catch(() => undefined),\n emit: () => {\n if (closed || !this.collections.get(slug)?.ready) return;\n const row = this.localRow<M>(slug, id);\n const entry = this.collections.get(slug)?.rows.get(String(id));\n const fromCache = !this.collections.get(slug)?.freshRows.has(String(id));\n const hasPendingWrites = this.hasPending(slug, id);\n const signature = `${fromCache ? \"c\" : \"s\"}${hasPendingWrites ? \"p\" : \"-\"}|`\n + (row === undefined ? MISSING : `${String(id)}:${entry?.rev ?? 0}`);\n if (observer.settled && signature === observer.signature) return;\n observer.signature = signature;\n observer.settled = true;\n onResult(row, { fromCache, hasPendingWrites });\n }\n };\n this.observersFor(slug).add(observer);\n\n void (async () => {\n await this.ensureCollection(slug);\n if (closed) return;\n if (this.localRow<M>(slug, id) !== undefined) observer.emit();\n try {\n await wrapped.findById(id);\n } catch (error) {\n if (closed) return;\n if (!observer.settled) {\n onError?.(error as Error);\n return;\n }\n }\n if (!closed) observer.emit();\n })();\n\n if (options?.realtime !== false && inner.listenById) {\n unlisten = inner.listenById(id, (row) => {\n if (!row) {\n if (!this.hasPending(slug, id)) this.removeLocalRow(slug, id, true);\n this.notifyCollection(slug, false);\n return;\n }\n void this.ingest(slug, [row]).then(() => this.notifyCollection(slug, false));\n }, onError);\n }\n\n return () => {\n closed = true;\n this.observersFor(slug).delete(observer);\n unlisten?.();\n };\n }\n\n private observersFor(slug: string): Set<Observer> {\n let set = this.observers.get(slug);\n if (!set) {\n set = new Set();\n this.observers.set(slug, set);\n }\n return set;\n }\n\n /** Cheap change detection: which rows, in what order, at which revision. */\n private signature(slug: string, rows: AnyRow[], total: number): string {\n const state = this.collections.get(slug);\n const parts = rows.map((row) => {\n const key = String(row.id);\n return `${key}:${state?.rows.get(key)?.rev ?? 0}`;\n });\n return `${total}|${parts.join(\",\")}`;\n }\n\n private notifyCollection(slug: string, broadcast = true): void {\n const set = this.observers.get(slug);\n if (set) for (const observer of [...set]) observer.emit();\n if (broadcast) this.broadcast({ type: \"rows\", slugs: [slug] });\n }\n\n /** Connectivity came back (or the user changed): re-read everything live. */\n private revalidateAll(): void {\n for (const slug of this.observers.keys()) {\n this.notifyCollection(slug, false);\n this.scheduleRefresh(slug);\n }\n }\n\n // ─── Reading the local database ──────────────────────────────────────────\n\n private collectionState(slug: string): CollectionState {\n let state = this.collections.get(slug);\n if (!state) {\n state = {\n rows: new Map(),\n snapshots: new Map(),\n fresh: new Set(),\n freshRows: new Set(),\n absent: new Set(),\n ready: false\n };\n this.collections.set(slug, state);\n }\n return state;\n }\n\n private ensureCollection(slug: string): Promise<CollectionState> {\n const state = this.collectionState(slug);\n if (!state.loaded) {\n const scope = this.scope;\n state.loaded = (async () => {\n await this.ensureQueueLoaded();\n const [rows, snapshots, absent] = await Promise.all([\n this.store.listCacheEntries(`${scope}|row|${slug}|`).catch(() => []),\n this.store.listCacheEntries(`${scope}|q|${slug}|`).catch(() => []),\n this.store.listCache(`${scope}|abs|${slug}|`).catch(() => [])\n ]);\n // A scope switch mid-load must not graft the previous user's\n // rows onto the new one.\n if (this.scope !== scope || this.collections.get(slug) !== state) return;\n for (const entry of rows) {\n const row = entry.value as AnyRow | undefined;\n if (!row || row.id === undefined || row.id === null) continue;\n state.rows.set(String(row.id), {\n row: hydrateRow(row),\n cachedAt: entry.cachedAt,\n rev: ++this.revCounter\n });\n }\n for (const entry of snapshots) {\n const key = entry.key.slice(`${scope}|q|${slug}|`.length);\n if (entry.value) state.snapshots.set(key, entry.value as QuerySnapshot);\n }\n for (const entry of absent) {\n state.absent.add(entry.key.slice(`${scope}|abs|${slug}|`.length));\n }\n })().catch(() => undefined).finally(() => { state.ready = true; });\n }\n return state.loaded.then(() => state);\n }\n\n private snapshotFor(slug: string, params?: FindParams): QuerySnapshot | undefined {\n return this.collections.get(slug)?.snapshots.get(buildQueryString(params));\n }\n\n private hasLocalAnswer(state: CollectionState | undefined, slug: string, params?: FindParams): boolean {\n if (!state) return false;\n return state.snapshots.has(buildQueryString(params)) || state.rows.size > 0;\n }\n\n /**\n * Answer a query from the local database.\n *\n * With a snapshot, the server's own page — its ids, order and total — is\n * the skeleton, and the local rows fill it in: rows deleted locally drop\n * out, rows edited locally show the edit, and rows *created* locally join\n * the first page if they match. Without one, the query is evaluated\n * outright over every cached row, which is the best that can be done for a\n * query the server has never answered here.\n */\n private answer<M extends AnyRow>(\n slug: string,\n params: FindParams | undefined,\n snapshot: QuerySnapshot | undefined\n ): LiveResult<M> {\n const state = this.collections.get(slug);\n const exact = isExactlyEvaluable(params);\n const fromCache = !state?.fresh.has(buildQueryString(params));\n if (!state) {\n return {\n data: [],\n meta: { ...resolvePagination(params), total: 0, hasMore: false },\n fromCache: true,\n hasPendingWrites: false,\n partial: true\n };\n }\n\n if (!snapshot) {\n const local = runLocalQuery<M>([...state.rows.values()].map((e) => e.row) as M[], params);\n return {\n ...local,\n fromCache,\n hasPendingWrites: local.data.some((row) => this.hasPending(slug, row.id as string | number)),\n partial: true\n };\n }\n\n const rows: M[] = [];\n const seen = new Set<string>();\n /** Rows the server counted that we know are no longer in the result. */\n let removed = 0;\n for (const id of snapshot.ids) {\n const key = String(id);\n const entry = state.rows.get(key);\n if (!entry) {\n // Gone for a reason (deleted here, or confirmed gone by the\n // server) versus merely evicted to stay under the cache cap:\n // only the former should move the total the server gave us.\n if (state.absent.has(key) || this.hasPending(slug, key)) removed++;\n continue;\n }\n // A local edit that moves a row out of its own filter should take\n // it off the list, exactly as a refetch would.\n if (exact && this.hasPending(slug, key) && !matchesParams(entry.row, params)) {\n removed++;\n continue;\n }\n rows.push(entry.row as M);\n seen.add(key);\n }\n\n // Rows the server has never seen belong on the first page of a\n // matching query. Injecting them into *every* page would show the same\n // new row once per page.\n let added = 0;\n const offset = snapshot.offset ?? 0;\n if (exact && offset === 0) {\n for (const [key, entry] of state.rows) {\n if (seen.has(key) || !this.hasPending(slug, key)) continue;\n if (!this.isLocallyCreated(slug, key)) continue;\n if (!matchesParams(entry.row, params)) continue;\n rows.push(entry.row as M);\n added++;\n }\n }\n\n // Order is part of the query, not a detail of how the rows were\n // obtained. This used to sort only when a locally-created row had been\n // injected — every other read handed back cache order, which is\n // insertion order, and a caller that asked for `orderBy` got whatever\n // the store happened to hold. In the admin that is the collection's\n // `sort` being silently ignored on every list backed by this overlay:\n // the query carries it, the server honours it, and the answer served\n // from here did not.\n //\n // …but only when the local sort would land where the server's did.\n // `snapshot.ids` already arrived in the server's order, so re-sorting a\n // text column with `Intl.Collator` *replaces* a correct order with a\n // possibly different one — under the C collation Postgres puts\n // `Banana` before `apple` and the collator does not. When the column\n // cannot be ordered locally the snapshot's order is the better answer,\n // and the result says so rather than presenting it as the sorted page\n // that was asked for.\n const orderIsLocal = isLocallySortable(rows, params?.orderBy);\n if (params?.orderBy && orderIsLocal) sortRows(rows, params.orderBy);\n\n const total = Math.max(rows.length, snapshot.total - removed + added);\n return {\n data: rows,\n meta: {\n total,\n limit: snapshot.limit,\n offset,\n hasMore: snapshot.hasMore\n },\n fromCache,\n hasPendingWrites: rows.some((row) => this.hasPending(slug, row.id as string | number)),\n // Not the page that was asked for if either the membership\n // decision or the order could not be reproduced here.\n partial: !exact || !orderIsLocal\n };\n }\n\n private localFind<M extends AnyRow>(slug: string, params?: FindParams): LiveResult<M> {\n const state = this.collections.get(slug);\n const snapshot = this.snapshotFor(slug, params);\n // However recent it looks, this answer did not come from the server.\n state?.fresh.delete(buildQueryString(params));\n if (!snapshot && (!state || state.rows.size === 0)) {\n throw offlineError(`Offline: no cached data for \"${slug}\".`);\n }\n const answer = this.answer<M>(slug, params, snapshot);\n return snapshot ? answer : { ...answer, partial: true };\n }\n\n private rawLocalRow(slug: string, id: string | number): AnyRow | undefined {\n const entry = this.collections.get(slug)?.rows.get(String(id));\n return entry ? { ...entry.row } : undefined;\n }\n\n private localRow<M extends AnyRow>(slug: string, id: string | number): M | undefined {\n return this.collections.get(slug)?.rows.get(String(id))?.row as M | undefined;\n }\n\n // ─── Writing the local database ──────────────────────────────────────────\n\n private setLocalRow(slug: string, id: string | number, row: AnyRow): void {\n const state = this.collectionState(slug);\n const key = String(id);\n const cachedAt = Date.now();\n state.rows.set(key, { row: { ...row }, cachedAt, rev: ++this.revCounter });\n state.freshRows.delete(key);\n this.forgetTombstone(slug, key);\n void this.writeCache(this.rowKey(slug, key), dehydrateRow(row), cachedAt);\n this.evictRows(slug);\n }\n\n /**\n * Drop a row and, when the server is the one saying it is gone, remember\n * that. \"I looked it up and it does not exist\" is real knowledge: without\n * it, opening a deleted row while offline would report a missing local\n * database instead of a missing row.\n */\n private removeLocalRow(slug: string, id: string | number, known = false): void {\n const state = this.collectionState(slug);\n const key = String(id);\n const existed = state.rows.delete(key);\n if (known) {\n state.absent.add(key);\n state.freshRows.add(key);\n void this.writeCache(this.absentKey(slug, key), true);\n } else {\n state.freshRows.delete(key);\n }\n if (existed) void this.deleteCache([this.rowKey(slug, key)]);\n }\n\n private forgetTombstone(slug: string, key: string): void {\n const state = this.collectionState(slug);\n if (!state.absent.delete(key)) return;\n void this.deleteCache([this.absentKey(slug, key)]);\n }\n\n /**\n * Merge server rows into the local database. A row with unsynced local\n * writes keeps them: the server's copy is the base the queued mutations\n * are re-applied to, not a replacement for what the user did.\n *\n * Rows that came back unchanged keep their identity and revision, so a\n * refetch that changed nothing does not re-render every live query that\n * touches them — or rewrite them all to disk.\n */\n private async ingest(slug: string, rows: AnyRow[]): Promise<void> {\n if (rows.length === 0) return;\n const state = await this.ensureCollection(slug);\n const cachedAt = Date.now();\n const writes: { key: string; entry: { value: unknown; cachedAt: number } }[] = [];\n const deletes: string[] = [];\n for (const raw of rows) {\n if (!raw || raw.id === undefined || raw.id === null) continue;\n const key = String(raw.id);\n const merged = this.hasPending(slug, key)\n ? this.applyPendingToRow(slug, key, { ...raw })\n : { ...raw };\n if (merged === undefined) {\n // A queued delete says this row is gone; do not resurrect it.\n state.rows.delete(key);\n deletes.push(this.rowKey(slug, key));\n continue;\n }\n this.forgetTombstone(slug, key);\n state.freshRows.add(key);\n const existing = state.rows.get(key);\n if (existing && JSON.stringify(existing.row) === JSON.stringify(merged)) {\n existing.cachedAt = cachedAt;\n continue;\n }\n state.rows.set(key, { row: merged, cachedAt, rev: ++this.revCounter });\n writes.push({ key: this.rowKey(slug, key), entry: { value: dehydrateRow(merged), cachedAt } });\n }\n if (writes.length > 0) void this.store.setCacheMany(writes).catch(() => undefined);\n if (deletes.length > 0) void this.deleteCache(deletes);\n this.evictRows(slug);\n }\n\n /**\n * Fold the queued mutations for one row over a base, newest last.\n * `afterMutationId` skips everything up to and including that mutation,\n * which is how a just-replayed write avoids being applied on top of the\n * server's response to it.\n */\n private applyPendingToRow(\n slug: string,\n idKey: string,\n base: AnyRow | undefined,\n afterMutationId?: string\n ): AnyRow | undefined {\n let row = base;\n let skipping = afterMutationId !== undefined;\n for (const op of this.queue) {\n if (skipping) {\n if (op.mutationId === afterMutationId) skipping = false;\n continue;\n }\n if (op.collection !== slug) continue;\n if (op.type === \"createMany\") {\n const match = (op.data as AnyRow[] | undefined)?.find((r) => String(r.id) === idKey);\n if (match) row = { ...match };\n continue;\n }\n if (op.id === undefined || String(op.id) !== idKey) continue;\n if (op.type === \"create\") row = { ...(op.data as AnyRow) };\n else if (op.type === \"update\") row = { ...(row ?? {}), ...(op.data as AnyRow), id: op.id };\n else if (op.type === \"delete\") row = undefined;\n }\n return row;\n }\n\n private recordSnapshot(slug: string, params: FindParams | undefined, result: FindResult<AnyRow>): QuerySnapshot {\n const window = resolvePagination(params);\n const meta = result.meta ?? { total: result.data?.length ?? 0, ...window, hasMore: false };\n const snapshot: QuerySnapshot = {\n ids: (result.data ?? []).map((row) => row.id as string | number).filter((id) => id !== undefined),\n total: meta.total ?? result.data?.length ?? 0,\n limit: meta.limit ?? window.limit,\n offset: meta.offset ?? window.offset,\n hasMore: meta.hasMore ?? false\n };\n const state = this.collectionState(slug);\n const key = buildQueryString(params);\n state.snapshots.set(key, snapshot);\n state.fresh.add(key);\n void this.writeCache(`${this.scope}|q|${slug}|${key}`, snapshot);\n this.evictSnapshots(slug);\n return snapshot;\n }\n\n /**\n * A write changed which rows belong in a list, and only the server can say\n * how — a row it generated is in no cached page, and the totals moved.\n * Re-run every live query on the collection; queries nobody is watching\n * are corrected by their next `find`.\n *\n * Coalesced per microtask so a burst of writes costs one round trip, and\n * skipped entirely while offline, where the local database is already the\n * best answer available.\n */\n private scheduleRefresh(slug: string): void {\n if (this.refreshPending.has(slug)) return;\n const observers = this.observers.get(slug);\n if (!observers || observers.size === 0) return;\n this.refreshPending.add(slug);\n void Promise.resolve().then(() => {\n this.refreshPending.delete(slug);\n if (this.disposed || !this.connectivity.shouldAttempt()) return;\n for (const observer of [...(this.observers.get(slug) ?? [])]) void observer.refresh();\n });\n }\n\n private evictRows(slug: string): void {\n const state = this.collections.get(slug);\n if (!state || state.rows.size <= this.maxCachedRows) return;\n const evictable = [...state.rows.entries()]\n .filter(([key]) => !this.hasPending(slug, key))\n .sort((a, b) => a[1].cachedAt - b[1].cachedAt);\n const excess = state.rows.size - this.maxCachedRows;\n const doomed = evictable.slice(0, excess);\n for (const [key] of doomed) state.rows.delete(key);\n if (doomed.length > 0) void this.deleteCache(doomed.map(([key]) => this.rowKey(slug, key)));\n\n // Tombstones are tiny but unbounded — every row the app ever asked for\n // and did not find leaves one. Cap them against the same budget.\n if (state.absent.size > this.maxCachedRows) {\n const stale = [...state.absent].slice(0, state.absent.size - this.maxCachedRows);\n for (const key of stale) state.absent.delete(key);\n void this.deleteCache(stale.map((key) => this.absentKey(slug, key)));\n }\n }\n\n private evictSnapshots(slug: string): void {\n const state = this.collections.get(slug);\n if (!state || state.snapshots.size <= this.maxCachedQueries) return;\n // Insertion order is recency order for a Map that re-sets on write.\n const excess = state.snapshots.size - this.maxCachedQueries;\n const doomed = [...state.snapshots.keys()].slice(0, excess);\n for (const key of doomed) state.snapshots.delete(key);\n void this.deleteCache(doomed.map((key) => `${this.scope}|q|${slug}|${key}`));\n }\n\n // ─── Queue ───────────────────────────────────────────────────────────────\n\n private ensureQueueLoaded(): Promise<void> {\n if (!this.queueLoad) {\n const scope = this.scope;\n this.queueLoad = this.store.listQueue(`${scope}|`).then((queue) => {\n // A scope switch during the load must not graft the old\n // user's queue onto the new one.\n if (this.scope !== scope) return;\n this.queue = queue;\n this.patchStatus({ pending: queue.length });\n this.notifyQueue();\n }).catch(() => undefined);\n }\n return this.queueLoad;\n }\n\n private enqueue(mutation: Omit<PendingMutation, \"mutationId\" | \"queuedAt\">): Promise<void> {\n const result = this.enqueueChain.then(async () => {\n await this.ensureQueueLoaded();\n\n // Tail coalescing: repeated edits to the most recently written row\n // (typing in a form) collapse into the queued op instead of\n // growing the queue. Only the queue *tail* may absorb an update —\n // merging into an earlier op would move this write across ops\n // queued after it, silently reordering what the app did.\n if (mutation.type === \"update\") {\n const tail = this.queue[this.queue.length - 1];\n if (tail\n && tail.mutationId !== this.inFlightId\n && tail.collection === mutation.collection\n && (tail.type === \"create\" || tail.type === \"update\")\n && tail.id === mutation.id) {\n // The id must survive the merge: a queued create carries\n // the client-generated id inside its data. The rollback\n // stays the tail's — the state before the *first* of the\n // merged writes, which is what undoing them all restores.\n tail.data = { ...(tail.data as AnyRow), ...(mutation.data as AnyRow), id: tail.id };\n await this.store.enqueue(this.queueKey(tail), tail);\n return;\n }\n }\n\n // Cancel-out: deleting a row whose create is still queued — and\n // whose id the SDK generated, so the server cannot already have a\n // row under it — means the server never saw the row. Remove every\n // queued op for it and queue nothing. Creates with caller-supplied\n // ids do NOT cancel (the id may name an existing server row, which\n // the delete must still remove), and neither do rows queued inside\n // a createMany (the bulk op replays first, then the delete).\n if (mutation.type === \"delete\") {\n // An in-flight create disqualifies the shortcut entirely: the\n // server is being told about the row as we speak, so \"it never\n // saw it\" is false and the delete has to replay after it.\n const hasPendingCreate = this.queue.some((m) =>\n m.collection === mutation.collection && m.type === \"create\"\n && m.id === mutation.id && m.generatedId === true\n && m.mutationId !== this.inFlightId);\n if (hasPendingCreate) {\n const doomed = this.queue.filter((m) =>\n m.collection === mutation.collection\n && m.id === mutation.id\n && (m.type === \"create\" || m.type === \"update\")\n && m.mutationId !== this.inFlightId);\n for (const op of doomed) await this.store.dequeue(this.queueKey(op));\n this.queue = this.queue.filter((m) => !doomed.includes(m));\n this.afterQueueChange();\n return;\n }\n }\n\n const full: PendingMutation = {\n ...mutation,\n mutationId: createMutationId(),\n queuedAt: Date.now()\n };\n await this.store.enqueue(this.queueKey(full), full);\n this.queue.push(full);\n this.afterQueueChange();\n });\n // The chain must survive a failed enqueue, or every later write dies\n // on the same stale rejection.\n this.enqueueChain = result.catch(() => undefined);\n return result;\n }\n\n private hasPending(slug: string, id: string | number): boolean {\n const key = String(id);\n return this.queue.some((op) => {\n if (op.collection !== slug) return false;\n if (op.type === \"createMany\") {\n return (op.data as AnyRow[] | undefined)?.some((r) => String(r.id) === key) ?? false;\n }\n return op.id !== undefined && String(op.id) === key;\n });\n }\n\n /** Is this row one the server has never been told about? */\n private isLocallyCreated(slug: string, idKey: string): boolean {\n return this.queue.some((op) => {\n if (op.collection !== slug) return false;\n if (op.type === \"create\") return op.id !== undefined && String(op.id) === idKey;\n if (op.type === \"createMany\") {\n return (op.data as AnyRow[] | undefined)?.some((r) => String(r.id) === idKey) ?? false;\n }\n return false;\n });\n }\n\n /** How many rows the queue adds to (or removes from) a server-side count. */\n private pendingDelta(slug: string, params?: FindParams): number {\n if (!isExactlyEvaluable(params)) return 0;\n let delta = 0;\n for (const op of this.queue) {\n if (op.collection !== slug) continue;\n if (op.type === \"create\") {\n if (matchesParams(op.data as AnyRow, params)) delta++;\n } else if (op.type === \"createMany\") {\n for (const row of (op.data as AnyRow[] | undefined) ?? []) {\n if (matchesParams(row, params)) delta++;\n }\n } else if (op.type === \"delete\") {\n const before = op.rollback?.rows?.[String(op.id)];\n if (before && matchesParams(before, params)) delta--;\n }\n }\n return delta;\n }\n\n // ─── Replay ──────────────────────────────────────────────────────────────\n\n sync(): Promise<{ flushed: number; remaining: number }> {\n if (this.flushPromise) return this.flushPromise;\n this.flushPromise = this.withLock(() => this.flush())\n .finally(() => { this.flushPromise = undefined; });\n return this.flushPromise;\n }\n\n private async flush(): Promise<{ flushed: number; remaining: number }> {\n await this.ensureQueueLoaded();\n // Another tab may have queued or drained work since we last looked.\n await this.reloadQueue();\n if (this.queue.length === 0) return { flushed: 0, remaining: 0 };\n // No `shouldAttempt` guard: every caller of `sync` — the app, the\n // retry timer, an `online` event, a sign-in — is asking for a real\n // attempt, and its outcome is what reopens the connection.\n\n this.patchStatus({ syncing: true });\n const touched = new Set<string>();\n const queuedAtStart = this.queue.length;\n let flushed = 0;\n try {\n while (this.queue.length > 0 && !this.disposed) {\n const op = this.queue[0];\n touched.add(op.collection);\n // Held across `drop` as well as `replay`: between the ACK and\n // the dequeue the op is still in `queue`, still the tail, and\n // still about to be removed — coalescing into it there loses\n // the write exactly as coalescing during the request does.\n this.inFlightId = op.mutationId;\n try {\n try {\n await this.replay(op);\n } catch (error) {\n if (isNetworkError(error)) {\n // Still offline — keep the op and everything behind it.\n this.connectivity.markFailure();\n break;\n }\n op.attempts = (op.attempts ?? 0) + 1;\n op.lastError = (error as Error)?.message ?? String(error);\n // A key the server is still holding gets a longer\n // budget than a busy server does. The claim outlives\n // the request that took it — the process was killed\n // between the write and its answer — so it is refused\n // until the claim's lease runs out, which is longer\n // than the default five retries reach. Giving up on\n // that schedule rolls back precisely the write the key\n // exists to save.\n const limit = isIdempotencyInProgressError(error)\n ? Math.max(this.maxRetries, IN_PROGRESS_MIN_RETRIES)\n : this.maxRetries;\n if (isRetryableError(error) && op.attempts < limit) {\n // The server is busy, not unhappy. Keep the op — and\n // its place in line, since later writes may depend on\n // it — and come back after a backoff.\n await this.store.enqueue(this.queueKey(op), op).catch(() => undefined);\n this.connectivity.deferRetry();\n this.patchStatus({ lastError: op.lastError });\n break;\n }\n await this.rejectMutation(op, error as Error);\n continue;\n }\n this.connectivity.markSuccess();\n await this.drop(op);\n flushed++;\n } finally {\n this.inFlightId = null;\n }\n }\n } finally {\n this.patchStatus({ syncing: false });\n }\n\n if (this.queue.length !== queuedAtStart) {\n for (const slug of touched) {\n this.notifyCollection(slug);\n // The server has now seen these writes, and its page\n // composition and totals moved with them.\n this.scheduleRefresh(slug);\n }\n // One message for the whole drain — including a drain that only\n // rolled writes back, which other tabs need to hear about just as\n // much as one that succeeded.\n this.broadcast({ type: \"queue\" });\n }\n if (this.queue.length === 0) this.patchStatus({ lastSyncedAt: Date.now() });\n return { flushed, remaining: this.queue.length };\n }\n\n private async replay(op: PendingMutation): Promise<void> {\n const inner = this.innerFor(op.collection);\n if (op.type === \"create\") {\n // The queued row already carries its (client-generated) id.\n let row: AnyRow | undefined;\n try {\n // The mutation id names this write, so a server that stores keys\n // recognises a replay instead of inserting a second row. This is\n // the only defence for a table with a server-assigned id: the id\n // the client chose was never used, so a duplicate is invisible\n // from here. Ignored by servers that do not support it.\n row = await inner.create(op.data as AnyRow, undefined, { idempotencyKey: op.mutationId });\n } catch (error) {\n // A lost response, not a rejection. The request reached the\n // server and committed; only the ACK went missing, so the\n // replay finds the row already there.\n //\n // Restricted to ids the SDK minted: a fresh uuid cannot name a\n // row anyone else created, so a duplicate under it is\n // necessarily this mutation's own first attempt. A\n // caller-supplied id carries no such guarantee — it may well\n // collide with a row that was already there, which is a real\n // conflict the caller has to hear about.\n //\n // Without this, `rejectMutation` rolled the write back and\n // DELETED the local row — the one case where the row does exist\n // on the server. The user watched their own saved record vanish.\n if (!(op.generatedId === true && isDuplicateKeyError(error))) throw error;\n row = await inner.findById(op.id!).catch(() => undefined) as AnyRow | undefined;\n // The read can fail on its own (offline again, RLS). The row is\n // known to exist, so keep the local copy rather than rolling\n // back; the next refresh reconciles it.\n if (!row) return;\n }\n await this.adoptServerRow(op, op.id, row);\n } else if (op.type === \"createMany\") {\n const queued = (op.data as AnyRow[]) ?? [];\n // The mutation id names this batch, exactly as it names a single\n // `create` above — and it matters more here. Without it, a batch\n // whose ACK went missing replays as a second genuine import and\n // duplicates every row it holds, not one. `upsert` masked that for\n // the callers who set it; nothing covered the ones who did not.\n const rows = await inner.createMany(queued, {\n ...(op.upsert ? { upsert: true } : {}),\n idempotencyKey: op.mutationId\n });\n for (let i = 0; i < rows.length; i++) {\n await this.adoptServerRow(op, queued[i]?.id as string | number | undefined, rows[i]);\n }\n } else if (op.type === \"updateMany\") {\n const queued = op.updates ?? [];\n // Keyed like every other replay: an update re-applied in full is\n // naturally idempotent, but one interleaved with another writer's is\n // not, and a lost ACK would otherwise re-apply a stale batch over\n // newer data.\n const rows = await inner.updateMany(\n queued.map(u => ({ id: u.id,\ndata: u.data as AnyRow })),\n { idempotencyKey: op.mutationId }\n );\n for (let i = 0; i < rows.length; i++) {\n await this.ingestReplaced(op, queued[i].id, rows[i]);\n }\n } else if (op.type === \"update\") {\n const row = await inner.update(op.id!, op.data as AnyRow);\n await this.ingestReplaced(op, op.id!, row);\n } else if (op.type === \"deleteMany\") {\n const ids = op.ids ?? [];\n await inner.deleteMany(ids, { idempotencyKey: op.mutationId });\n for (const id of ids) this.removeLocalRow(op.collection, id, true);\n } else if (op.type === \"delete\") {\n await inner.delete(op.id!);\n this.removeLocalRow(op.collection, op.id!, true);\n }\n }\n\n /**\n * Take the server's version of a row the client created offline.\n *\n * The server may have assigned a different id — a serial column ignores\n * the id we invented — in which case every local trace of the temporary id\n * has to move with it, including queued writes that were made against it\n * before it was ever sent.\n */\n private async adoptServerRow(\n op: PendingMutation,\n localId: string | number | undefined,\n row: AnyRow | undefined\n ): Promise<void> {\n if (!row) return;\n const slug = op.collection;\n const serverId = row.id as string | number | undefined;\n if (localId !== undefined && serverId !== undefined && String(serverId) !== String(localId)) {\n const oldKey = String(localId);\n this.removeLocalRow(slug, localId);\n for (const queued of this.queue) {\n if (queued.collection !== slug) continue;\n let dirty = false;\n if (queued.id !== undefined && String(queued.id) === oldKey) {\n queued.id = serverId;\n if (queued.data && !Array.isArray(queued.data)) {\n (queued.data as AnyRow).id = serverId;\n }\n dirty = true;\n }\n // The rollback map is keyed by row id too, and restoring it\n // under a name the server never had would resurrect a ghost.\n const rollbackRows = queued.rollback?.rows;\n if (rollbackRows && oldKey in rollbackRows) {\n rollbackRows[String(serverId)] = rollbackRows[oldKey];\n delete rollbackRows[oldKey];\n dirty = true;\n }\n if (dirty) await this.store.enqueue(this.queueKey(queued), queued).catch(() => undefined);\n }\n }\n await this.ingestReplaced(op, serverId ?? localId!, row);\n }\n\n /**\n * Write a server row over the local one, ignoring the mutation that just\n * produced it — re-applying that would put the pre-server values back on\n * top of the server's answer — but keeping every write queued *after* it.\n * Those are still unsent, and dropping them here would make the row snap\n * back to the server's version in front of the user, only to change again\n * when they replay a moment later.\n */\n private async ingestReplaced(op: PendingMutation, id: string | number, row: AnyRow): Promise<void> {\n const slug = op.collection;\n const state = await this.ensureCollection(slug);\n const key = String(id);\n const merged = this.applyPendingToRow(slug, key, { ...row }, op.mutationId);\n if (merged === undefined) {\n // A queued delete is still waiting behind this write.\n this.removeLocalRow(slug, key);\n return;\n }\n const cachedAt = Date.now();\n state.rows.set(key, { row: merged, cachedAt, rev: ++this.revCounter });\n if (this.applyPendingToRow(slug, key, undefined, op.mutationId) === undefined) {\n // Nothing local is left on top of it, so this *is* the server's row.\n state.freshRows.add(key);\n }\n void this.writeCache(this.rowKey(slug, key), dehydrateRow(merged), cachedAt);\n }\n\n /**\n * The server refused a mutation. Put back what it changed, and discard the\n * queued writes that were built on top of it: an edit to a row whose\n * creation was rejected can only fail the same way, and applying it would\n * leave the local database claiming a row the server does not have.\n *\n * The cascade stops the moment a later write stops *depending* on the\n * rejected one. An `update` reads the row it edits, so it is doomed with\n * it; a `create` overwrites the row outright and a `delete` needs nothing\n * of it, so both stand on their own and are kept — dropping them would\n * silently lose writes the server would have accepted.\n */\n private async rejectMutation(op: PendingMutation, error: Error): Promise<void> {\n const ids = new Set(Object.keys(op.rollback?.rows ?? {}));\n if (op.id !== undefined) ids.add(String(op.id));\n\n const doomed: PendingMutation[] = [op];\n const orphaned = new Set(ids);\n const position = this.queue.indexOf(op);\n for (const later of this.queue.slice(position + 1)) {\n if (later.collection !== op.collection) continue;\n const hit = this.idsOf(later).filter((id) => orphaned.has(id));\n if (hit.length === 0) continue;\n if (later.type === \"update\") doomed.push(later);\n else for (const id of hit) orphaned.delete(id);\n }\n\n for (const dropped of doomed) await this.drop(dropped);\n\n for (const [idKey, previous] of Object.entries(op.rollback?.rows ?? {})) {\n // With the doomed writes gone, whatever survives in the queue is\n // what the row should still look like on top of the restored base.\n const restored = this.applyPendingToRow(op.collection, idKey, previous ?? undefined);\n if (restored === undefined) this.removeLocalRow(op.collection, idKey);\n else this.setLocalRow(op.collection, idKey, restored);\n }\n\n this.patchStatus({ lastError: error.message });\n this.notifyCollection(op.collection);\n this.scheduleRefresh(op.collection);\n for (const dropped of doomed) this.onSyncError?.(error, dropped);\n }\n\n /** Every row id a mutation writes to. */\n private idsOf(op: PendingMutation): string[] {\n if (op.type === \"createMany\") {\n return ((op.data as AnyRow[] | undefined) ?? []).map((r) => String(r.id));\n }\n return op.id === undefined ? [] : [String(op.id)];\n }\n\n private async drop(op: PendingMutation): Promise<void> {\n await this.store.dequeue(this.queueKey(op)).catch(() => undefined);\n this.queue = this.queue.filter((m) => m.mutationId !== op.mutationId);\n // No broadcast per item: draining a queue of fifty would be fifty\n // messages to every other tab. The flush announces itself once, at the end.\n this.afterQueueChange(false);\n }\n\n /** Replay uses unwrapped clients: a failure must never re-enqueue itself. */\n private innerFor(slug: string): SDKCollectionClient<AnyRow> {\n let inner = this.inners.get(slug);\n if (!inner) {\n inner = this.createInner(slug);\n this.inners.set(slug, inner);\n }\n return inner;\n }\n\n private async withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as { navigator?: { locks?: LockManager } }).navigator?.locks;\n // Two tabs replaying the same queue would each send every mutation.\n if (!locks?.request) return fn();\n try {\n return await locks.request(`rebase-offline-sync:${this.scope}`, fn) as T;\n } catch {\n // A browser that denies the lock (or a policy that blocks it) must\n // not stop the queue from draining at all.\n return fn();\n }\n }\n\n // ─── Cross-tab ───────────────────────────────────────────────────────────\n\n private broadcast(message: { type: \"rows\"; slugs: string[] } | { type: \"queue\" }): void {\n if (!this.channel) return;\n try {\n this.channel.postMessage({ ...message, scope: this.scope, sender: this.tabId });\n } catch {\n // Structured-clone failures here would only cost cross-tab freshness.\n }\n }\n\n private onBroadcast(message: unknown): void {\n if (this.disposed || !message || typeof message !== \"object\") return;\n const msg = message as { type?: string; scope?: string; sender?: string; slugs?: string[] };\n if (msg.sender === this.tabId || msg.scope !== this.scope) return;\n if (msg.type === \"rows\") {\n for (const slug of msg.slugs ?? []) void this.reloadCollection(slug);\n } else if (msg.type === \"queue\") {\n void this.reloadQueue();\n }\n }\n\n /** Re-read one collection from the store, replacing what is in memory. */\n private async reloadCollection(slug: string): Promise<void> {\n const state = this.collections.get(slug);\n if (!state?.loaded) return; // never loaded here — nothing to keep fresh\n await this.reloadQueue();\n const scope = this.scope;\n const [rows, snapshots, absent] = await Promise.all([\n this.store.listCacheEntries(`${scope}|row|${slug}|`).catch(() => []),\n this.store.listCacheEntries(`${scope}|q|${slug}|`).catch(() => []),\n this.store.listCache(`${scope}|abs|${slug}|`).catch(() => [])\n ]);\n if (this.scope !== scope || this.collections.get(slug) !== state) return;\n const next = new Map<string, RowEntry>();\n for (const entry of rows) {\n const row = entry.value as AnyRow | undefined;\n if (!row || row.id === undefined || row.id === null) continue;\n const key = String(row.id);\n const existing = state.rows.get(key);\n const hydrated = hydrateRow(row);\n // Keep the previous revision when nothing actually changed, so a\n // cross-tab ping does not re-render every observer.\n const unchanged = existing && JSON.stringify(existing.row) === JSON.stringify(hydrated);\n next.set(key, {\n row: hydrated,\n cachedAt: entry.cachedAt,\n rev: unchanged ? existing!.rev : ++this.revCounter\n });\n }\n state.rows = next;\n state.snapshots = new Map();\n for (const entry of snapshots) {\n const key = entry.key.slice(`${scope}|q|${slug}|`.length);\n if (entry.value) state.snapshots.set(key, entry.value as QuerySnapshot);\n }\n state.absent = new Set(absent.map((entry) => entry.key.slice(`${scope}|abs|${slug}|`.length)));\n this.notifyCollection(slug, false);\n }\n\n private async reloadQueue(): Promise<void> {\n const scope = this.scope;\n const queue = await this.store.listQueue(`${scope}|`).catch(() => undefined);\n if (!queue || this.scope !== scope) return;\n this.queue = queue;\n this.afterQueueChange(false);\n }\n\n // ─── Notifications ───────────────────────────────────────────────────────\n\n private afterQueueChange(broadcast = true): void {\n this.patchStatus({ pending: this.queue.length });\n this.notifyQueue();\n if (broadcast) this.broadcast({ type: \"queue\" });\n }\n\n private notifyQueue(): void {\n for (const listener of this.queueListeners) listener(this.queue.length);\n }\n\n private patchStatus(patch: Partial<OfflineStatus>): void {\n let changed = false;\n for (const [key, value] of Object.entries(patch) as [keyof OfflineStatus, never][]) {\n if (this.currentStatus[key] !== value) {\n this.currentStatus[key] = value;\n changed = true;\n }\n }\n if (!changed) return;\n const snapshot = { ...this.currentStatus };\n for (const listener of this.statusListeners) listener(snapshot);\n }\n\n // ─── Store keys and access ───────────────────────────────────────────────\n\n private countKey(slug: string, params?: FindParams): string {\n return `${this.scope}|count|${slug}|${buildQueryString(params)}`;\n }\n\n private rowKey(slug: string, id: string | number): string {\n return `${this.scope}|row|${slug}|${String(id)}`;\n }\n\n private absentKey(slug: string, id: string | number): string {\n return `${this.scope}|abs|${slug}|${String(id)}`;\n }\n\n private queueKey(mutation: PendingMutation): string {\n return `${this.scope}|${mutation.mutationId}`;\n }\n\n private async readCache<T>(key: string): Promise<T | undefined> {\n try {\n const entry = await this.store.getCache(key);\n return entry?.value as T | undefined;\n } catch {\n // A broken cache read must degrade to \"no cache\", never break the app.\n return undefined;\n }\n }\n\n private async writeCache(key: string, value: unknown, cachedAt = Date.now()): Promise<void> {\n try {\n await this.store.setCache(key, { value, cachedAt });\n } catch {\n // Quota errors and private-browsing restrictions must not fail the\n // read or write that got us here.\n }\n }\n\n private async deleteCache(keys: string[]): Promise<void> {\n try {\n await this.store.deleteCache(keys);\n } catch {\n // Same rationale as writeCache.\n }\n }\n}\n","import { createTransport, 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 } from \"./api-keys\";\nimport { CollectionClient, createCollectionClient } 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 { OfflineManager, type OfflineApi, type OfflineConfig } from \"./offline\";\nimport {\n DEFAULT_STORAGE_SOURCE_KEY,\n InsertOf,\n RebaseClient,\n RebaseSdkData,\n RowOf,\n StorageSource,\n StorageSourceDefinition,\n StorageSourceRegistry,\n UpdateOf\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 } 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 { QueryBuilder, or, and, cond } from \"@rebasepro/common\";\n\n// Auth: session/token types, config, and the pluggable storage strategies.\nexport { createCookieStorage, createMemoryStorage } from \"./auth\";\nexport type { AuthConfig, AuthStorage, CookieStorageOptions, CreateAuthOptions } 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 } from \"./admin\";\nexport type { AdminUser } from \"./admin\";\nexport type { CreateCronOptions } from \"./cron\";\nexport { createBackups } from \"./backups\";\nexport type { CreateBackupsOptions } from \"./backups\";\nexport type {\n ApiKeyMasked,\n ApiKeyPermission,\n ApiKeyWithSecret,\n CreateApiKeyRequest,\n CreateApiKeysOptions,\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 { OfflineApi, OfflineConfig, OfflineStatus } from \"./offline\";\nexport { isOfflineError } from \"./offline\";\nexport type { LiveResult, ObserveOptions, RowSnapshotMeta } from \"./collection\";\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 /**\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// Resolve a generated `Database` entry from a (kebab-case) slug literal,\n// or `unknown` when the slug isn't in the schema — the extractors below\n// then fall back to the open row / partial shapes.\ntype DBEntry<DB, S extends string> =\n KebabToCamelCase<S> extends keyof DB ? DB[KebabToCamelCase<S>] : unknown;\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 >;\n} & {\n [K in keyof DB]: CollectionClient<RowOf<DB[K]>, InsertOf<DB[K]>, UpdateOf<DB[K]>>;\n} & RebaseSdkData;\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 setToken: (token: string | null) => void;\n setAuthTokenGetter: (getter: () => Promise<string | null>) => void;\n setOnUnauthorized: (handler: () => Promise<boolean>) => void;\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 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 call: <T = unknown>(endpoint: string, payload?: unknown) => Promise<T>;\n collection: <M extends Record<string, unknown> = Record<string, unknown>>(slug: string) => CollectionClient<M>;\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 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 let ws: RebaseWebSocketClient | undefined;\n /** One channel object per name — see `realtime.channel`. */\n const realtimeChannels = new Map<string, RebaseRealtimeChannel>();\n if (resolvedWsUrl) {\n const wsOnUnauthorized = options.onUnauthorized || (() => auth.handleUnauthorized());\n\n ws = new RebaseWebSocketClient({\n websocketUrl: resolvedWsUrl,\n getAuthToken: async () => {\n let session = auth.getSession();\n if (session && session.expiresAt <= Date.now() + 10000) {\n try {\n session = await auth.refreshSession();\n } catch (e) { /* ignore */ }\n }\n return session?.accessToken || options.token || \"\";\n },\n onUnauthorized: wsOnUnauthorized\n });\n\n auth.onAuthStateChange((event, session) => {\n if (!ws) return;\n if (event === \"SIGNED_OUT\") {\n // Not permanent: the client stays usable, and a later subscribe\n // should reconnect anonymously.\n ws.disconnect();\n } else if (event === \"SIGNED_IN\" || event === \"TOKEN_REFRESHED\") {\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 if (session?.accessToken && ws.hasSocket) {\n ws.authenticate(session.accessToken).catch(console.warn);\n }\n }\n });\n }\n\n // Register transport callback for 401s after auth is instantiated.\n // IMPORTANT: We must use transport.setOnUnauthorized() here — NOT set\n // options.onUnauthorized — because the transport was already created above\n // and captured the (undefined) value from the config closure.\n if (!options.onUnauthorized) {\n // `handleUnauthorized` (not a bare `refreshSession`) so that a refresh\n // the server rejects outright drops the session and emits SIGNED_OUT —\n // otherwise the app keeps thinking it is signed in and every view just\n // renders \"Invalid or expired token\".\n transport.setOnUnauthorized(() => auth.handleUnauthorized());\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 OfflineManager(\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 offlineManager.setScope(auth.getSession()?.user?.uid);\n auth.onAuthStateChange((event, session) => {\n offlineManager.setScope(event === \"SIGNED_OUT\" ? undefined : session?.user?.uid);\n });\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 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 (typeof prop === \"string\" && prop !== \"then\" && prop !== \"toJSON\" && prop !== \"$$typeof\") {\n if (options.collections) {\n if (prop in options.collections) {\n return collection(options.collections[prop]);\n }\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 }\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 return undefined;\n }\n });\n\n const target = {\n auth,\n admin,\n cron,\n backups,\n apiKeys,\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 );\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: transport.setToken,\n setAuthTokenGetter: transport.setAuthTokenGetter,\n setOnUnauthorized: transport.setOnUnauthorized,\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 const res = await transport.request<{ data: T }>(`${prefix}${endpoint}`, {\n method: \"POST\",\n body: payload ? JSON.stringify(payload) : undefined\n });\n return res.data ?? (res as T);\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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"}