@lark-sh/client 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protocol/constants.ts","../src/connection/Coordinator.ts","../src/LarkError.ts","../src/protocol/messages.ts","../src/connection/MessageQueue.ts","../src/connection/PendingWriteManager.ts","../src/utils/path.ts","../src/utils/sort.ts","../src/connection/View.ts","../src/connection/SubscriptionManager.ts","../src/connection/WebSocketClient.ts","../src/OnDisconnect.ts","../src/utils/pushid.ts","../src/DatabaseReference.ts","../src/DataSnapshot.ts","../src/utils/jwt.ts","../src/LarkDatabase.ts","../src/utils/volatile.ts"],"sourcesContent":["/**\n * Wire protocol constants.\n * Keys are shortened for bandwidth efficiency.\n */\n\n// Client -> Server operations\nexport const Op = {\n JOIN: 'j',\n SET: 's',\n UPDATE: 'u',\n DELETE: 'd',\n PUSH: 'p',\n SUBSCRIBE: 'sb',\n UNSUBSCRIBE: 'us',\n ONCE: 'o',\n ON_DISCONNECT: 'od',\n LEAVE: 'l',\n TRANSACTION: 'tx',\n} as const;\n\n// Transaction operation types (used within tx ops array)\nexport const TxOp = {\n SET: 's',\n UPDATE: 'u',\n DELETE: 'd',\n CONDITION: 'c',\n} as const;\n\nexport type TxOpType = (typeof TxOp)[keyof typeof TxOp];\n\n// OnDisconnect actions\nexport const OnDisconnectAction = {\n SET: 's',\n UPDATE: 'u',\n DELETE: 'd',\n CANCEL: 'c',\n} as const;\n\n// Server -> Client event types (wire format)\n// Server now sends 'put' and 'patch' events; client generates child_* events locally\nexport const ServerEventType = {\n PUT: 'put',\n PATCH: 'patch',\n} as const;\n\nexport type ServerEventTypeValue = (typeof ServerEventType)[keyof typeof ServerEventType];\n\n// Client-side event types (API-facing)\n// These are generated by the client from put/patch events\nexport type EventType = 'value' | 'child_added' | 'child_changed' | 'child_removed' | 'child_moved';\n\n// Map client event types to short form for subscribe message\nexport const eventTypeToShort: Record<EventType, string> = {\n value: 'v',\n child_added: 'ca',\n child_changed: 'cc',\n child_removed: 'cr',\n child_moved: 'cm',\n};\n\n// Error codes\nexport const ErrorCode = {\n PERMISSION_DENIED: 'permission_denied',\n INVALID_DATA: 'invalid_data',\n NOT_FOUND: 'not_found',\n INVALID_PATH: 'invalid_path',\n INVALID_OPERATION: 'invalid_operation',\n INTERNAL_ERROR: 'internal_error',\n CONDITION_FAILED: 'condition_failed',\n} as const;\n\nexport type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n// Default coordinator URL\nexport const DEFAULT_COORDINATOR_URL = 'https://db.lark.sh';\n","/**\n * Coordinator client - handles HTTP requests to the coordinator service\n * to get connection details for a specific database.\n */\n\nimport { DEFAULT_COORDINATOR_URL } from '../protocol/constants';\n\nexport interface ConnectRequest {\n database: string;\n token?: string;\n anonymous?: boolean;\n}\n\nexport interface ConnectResponse {\n ws_url: string; // Full WebSocket URL (e.g., \"wss://smash-1.larksh.com/ws\")\n udp_host: string; // Direct IP for UDP connections\n udp_port: number; // UDP port\n token: string; // Connection JWT to use with the server\n}\n\nexport interface CoordinatorError {\n error: string;\n message: string;\n}\n\nexport class Coordinator {\n private readonly baseUrl: string;\n\n constructor(baseUrl: string = DEFAULT_COORDINATOR_URL) {\n // Remove trailing slash if present\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n }\n\n /**\n * Request connection details from the coordinator.\n *\n * @param database - Database ID in format \"project/database\"\n * @param options - Either a user token or anonymous flag\n * @returns Connection details including host, port, and connection token\n */\n async connect(\n database: string,\n options: { token?: string; anonymous?: boolean } = {}\n ): Promise<ConnectResponse> {\n const body: ConnectRequest = { database };\n\n if (options.token) {\n body.token = options.token;\n } else if (options.anonymous) {\n body.anonymous = true;\n } else {\n // Default to anonymous if nothing specified\n body.anonymous = true;\n }\n\n const response = await fetch(`${this.baseUrl}/connect`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n let errorMessage = `Coordinator request failed: ${response.status}`;\n try {\n const errorBody = (await response.json()) as CoordinatorError;\n if (errorBody.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n // Ignore JSON parse errors\n }\n throw new Error(errorMessage);\n }\n\n const data = (await response.json()) as ConnectResponse;\n return data;\n }\n}\n","/**\n * LarkError - custom error class for Lark operations.\n *\n * Standard error codes:\n * - `permission_denied` - Security rules rejected operation\n * - `invalid_data` - Malformed data\n * - `not_found` - Path doesn't exist\n * - `invalid_path` - Invalid path format\n * - `timeout` - Request timed out\n * - `not_connected` - Operation attempted while disconnected\n * - `condition_failed` - Transaction condition check failed (CAS mismatch)\n * - `max_retries_exceeded` - Optimistic transaction exceeded retry limit\n * - `write_tainted` - Write rejected locally because it depended on a failed write\n * - `view_recovering` - Write rejected because View is recovering from failed write\n */\n\nexport class LarkError extends Error {\n readonly code: string;\n\n constructor(code: string, message?: string) {\n super(message || code);\n this.code = code;\n this.name = 'LarkError';\n\n // Maintain proper stack trace in V8 environments\n const ErrorWithCapture = Error as typeof Error & {\n captureStackTrace?: (target: object, constructor: Function) => void;\n };\n if (ErrorWithCapture.captureStackTrace) {\n ErrorWithCapture.captureStackTrace(this, LarkError);\n }\n }\n}\n","/**\n * Wire protocol message types.\n * All communication uses JSON over WebSocket with shortened keys.\n */\n\nimport { EventType } from './constants';\n\n// ============================================\n// Client -> Server Messages\n// ============================================\n\nexport interface JoinMessage {\n o: 'j'; // operation: join\n t: string; // token (JWT from coordinator)\n r: string; // request ID\n pcid?: string; // previous connection ID (for reconnect deduplication)\n}\n\nexport interface SetMessage {\n o: 's'; // operation: set\n p: string; // path\n v: unknown; // value (priority is included as .priority if set)\n r: string; // request ID\n pw?: string[]; // pending write IDs for same View (for local-first recovery)\n}\n\nexport interface UpdateMessage {\n o: 'u'; // operation: update\n p: string; // path\n v: Record<string, unknown>; // values to merge\n r: string; // request ID\n pw?: string[]; // pending write IDs for same View (for local-first recovery)\n}\n\nexport interface DeleteMessage {\n o: 'd'; // operation: delete\n p: string; // path\n r: string; // request ID\n pw?: string[]; // pending write IDs for same View (for local-first recovery)\n}\n\nexport interface SubscribeMessage {\n o: 'sb'; // operation: subscribe\n p: string; // path\n e: string[]; // event types (short form)\n r: string; // request ID\n // Query parameters (optional) - at top level per VIEW_REFACTOR.md\n orderBy?: string;\n orderByChild?: string;\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: unknown;\n startAtKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n equalTo?: unknown;\n equalToKey?: string;\n}\n\nexport interface QueryParams {\n orderBy?: 'key' | 'priority' | 'child' | 'value';\n orderByChild?: string; // child path (when orderBy='child')\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: unknown; // startAt value\n startAtKey?: string; // startAt key tie-breaker\n endAt?: unknown; // endAt value\n endAtKey?: string; // endAt key tie-breaker\n equalTo?: unknown; // equalTo value\n equalToKey?: string; // equalTo key tie-breaker\n}\n\nexport interface UnsubscribeMessage {\n o: 'us'; // operation: unsubscribe\n p: string; // path\n r: string; // request ID\n}\n\nexport interface OnceMessage {\n o: 'o'; // operation: once\n p: string; // path\n r: string; // request ID\n // Query parameters (optional) - at top level (same as SubscribeMessage)\n orderBy?: string;\n orderByChild?: string;\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: unknown;\n startAtKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n equalTo?: unknown;\n equalToKey?: string;\n}\n\nexport interface OnDisconnectMessage {\n o: 'od'; // operation: onDisconnect\n p: string; // path\n a: 's' | 'u' | 'd' | 'c'; // action: set, update, delete, cancel\n v?: unknown; // value (for set/update, priority is included as .priority if set)\n r: string; // request ID\n}\n\nexport interface LeaveMessage {\n o: 'l'; // operation: leave\n r: string; // request ID\n}\n\nexport interface PongMessage {\n o: 'po'; // operation: pong (response to server ping)\n}\n\n// Transaction operation types (used in ops array)\nexport interface TxSetOp {\n o: 's'; // set\n p: string; // path\n v: unknown; // value\n}\n\nexport interface TxUpdateOp {\n o: 'u'; // update (merge)\n p: string; // path\n v: Record<string, unknown>; // values to merge\n}\n\nexport interface TxDeleteOp {\n o: 'd'; // delete\n p: string; // path\n}\n\nexport interface TxConditionOp {\n o: 'c'; // condition (compare-and-swap check)\n p: string; // path\n v: unknown; // expected value\n}\n\nexport type TxOperation = TxSetOp | TxUpdateOp | TxDeleteOp | TxConditionOp;\n\nexport interface TransactionMessage {\n o: 'tx'; // operation: transaction\n ops: TxOperation[]; // array of operations\n r: string; // request ID\n}\n\nexport type ClientMessage =\n | JoinMessage\n | SetMessage\n | UpdateMessage\n | DeleteMessage\n | SubscribeMessage\n | UnsubscribeMessage\n | OnceMessage\n | OnDisconnectMessage\n | LeaveMessage\n | PongMessage\n | TransactionMessage;\n\n// ============================================\n// Server -> Client Messages\n// ============================================\n\nexport interface AckMessage {\n a: string; // ack with request ID\n}\n\nexport interface NackMessage {\n n: string; // nack with request ID\n e: string; // error code\n m?: string; // human-readable message\n}\n\n// New delta-based event format\n// Server sends 'put' for single-path changes, 'patch' for multi-path changes\n// Client generates child_* events locally from these deltas\n\nexport interface PutEventMessage {\n ev: 'put'; // event type: single-path delta\n sp: string; // subscription path (absolute) - identifies which subscription\n p: string; // delta path (relative to sp), \"/\" means full snapshot\n v?: unknown; // value, null for deletion\n x?: boolean; // volatile flag\n ts?: number; // server timestamp in ms (for volatile events)\n}\n\nexport interface PatchEventMessage {\n ev: 'patch'; // event type: multi-path delta\n sp: string; // subscription path (absolute)\n p: string; // base path (usually \"/\")\n v?: Record<string, unknown>; // multiple path:value pairs (relative to sp+p)\n x?: boolean; // volatile flag\n ts?: number; // server timestamp in ms (for volatile events)\n}\n\nexport type EventMessage = PutEventMessage | PatchEventMessage;\n\nexport interface OnceResponseMessage {\n oc: string; // once response with request ID\n ov: unknown; // value\n}\n\nexport interface JoinCompleteMessage {\n jc: string; // join complete with request ID\n vp?: string[] | null; // volatile path patterns (e.g., [\"players/*/position\"])\n cid?: string; // connection ID (for reconnect deduplication)\n}\n\nexport interface PingMessage {\n o: 'pi'; // operation: ping (keepalive from server)\n}\n\nexport type ServerMessage =\n | AckMessage\n | NackMessage\n | EventMessage\n | OnceResponseMessage\n | JoinCompleteMessage\n | PingMessage;\n\n// ============================================\n// Helper functions\n// ============================================\n\nexport function isAckMessage(msg: ServerMessage): msg is AckMessage {\n return 'a' in msg;\n}\n\nexport function isJoinCompleteMessage(msg: ServerMessage): msg is JoinCompleteMessage {\n return 'jc' in msg;\n}\n\nexport function isNackMessage(msg: ServerMessage): msg is NackMessage {\n return 'n' in msg;\n}\n\nexport function isEventMessage(msg: ServerMessage): msg is EventMessage {\n return 'ev' in msg;\n}\n\nexport function isPutEventMessage(msg: ServerMessage): msg is PutEventMessage {\n return 'ev' in msg && (msg as EventMessage).ev === 'put';\n}\n\nexport function isPatchEventMessage(msg: ServerMessage): msg is PatchEventMessage {\n return 'ev' in msg && (msg as EventMessage).ev === 'patch';\n}\n\nexport function isOnceResponseMessage(msg: ServerMessage): msg is OnceResponseMessage {\n return 'oc' in msg;\n}\n\nexport function isPingMessage(msg: ServerMessage): msg is PingMessage {\n return 'o' in msg && (msg as PingMessage).o === 'pi';\n}\n","/**\n * MessageQueue - handles request/response correlation for WebSocket messages.\n *\n * Every reliable operation gets a unique request ID. When we send a message,\n * we create a promise and store it. When we receive an ack/nack, we resolve/reject\n * the corresponding promise.\n */\n\nimport { LarkError } from '../LarkError';\nimport {\n ServerMessage,\n isAckMessage,\n isJoinCompleteMessage,\n isNackMessage,\n isOnceResponseMessage,\n} from '../protocol/messages';\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/** Response from join complete message */\nexport interface JoinCompleteResponse {\n volatilePaths: string[];\n connectionId: string | null;\n}\n\nexport class MessageQueue {\n private nextId = 1;\n private pending = new Map<string, PendingRequest>();\n private defaultTimeout: number;\n\n constructor(defaultTimeout: number = 30000) {\n this.defaultTimeout = defaultTimeout;\n }\n\n /**\n * Generate a new unique request ID.\n */\n nextRequestId(): string {\n return String(this.nextId++);\n }\n\n /**\n * Register a pending request that expects a response.\n * Returns a promise that resolves when ack is received, or rejects on nack/timeout.\n */\n registerRequest(requestId: string, timeout?: number): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const timeoutMs = timeout ?? this.defaultTimeout;\n\n const timeoutHandle = setTimeout(() => {\n this.pending.delete(requestId);\n reject(new LarkError('timeout', `Request ${requestId} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n this.pending.set(requestId, {\n resolve,\n reject,\n timeout: timeoutHandle,\n });\n });\n }\n\n /**\n * Handle an incoming server message. If it's a response to a pending request,\n * resolve or reject the corresponding promise.\n *\n * @returns true if the message was handled (was a response), false otherwise\n */\n handleMessage(message: ServerMessage): boolean {\n // Join complete (includes volatile paths and connection ID)\n if (isJoinCompleteMessage(message)) {\n const pending = this.pending.get(message.jc);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.jc);\n // Resolve with full join response\n const response: JoinCompleteResponse = {\n volatilePaths: message.vp || [],\n connectionId: message.cid || null,\n };\n pending.resolve(response);\n return true;\n }\n }\n\n // Regular ack\n if (isAckMessage(message)) {\n const pending = this.pending.get(message.a);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.a);\n pending.resolve(undefined);\n return true;\n }\n }\n\n // Nack (error)\n if (isNackMessage(message)) {\n const pending = this.pending.get(message.n);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.n);\n pending.reject(new LarkError(message.e, message.m));\n return true;\n }\n }\n\n // Once response\n if (isOnceResponseMessage(message)) {\n const pending = this.pending.get(message.oc);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.oc);\n pending.resolve(message.ov);\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Reject a specific pending request locally (without server message).\n * Used for client-side taint propagation when a write fails and\n * dependent writes need to be rejected.\n *\n * @returns true if the request was pending and rejected, false otherwise\n */\n rejectLocally(requestId: string, error: Error): boolean {\n const pending = this.pending.get(requestId);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(requestId);\n pending.reject(error);\n return true;\n }\n return false;\n }\n\n /**\n * Reject all pending requests (e.g., on disconnect).\n */\n rejectAll(error: Error): void {\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timeout);\n pending.reject(error);\n }\n this.pending.clear();\n }\n\n /**\n * Get the number of pending requests.\n */\n get pendingCount(): number {\n return this.pending.size;\n }\n}\n","/**\n * PendingWriteManager - tracks pending write operations for optimistic updates.\n *\n * Each write operation is assigned a unique operation ID (oid). The manager tracks\n * pending writes until they are acknowledged by the server. On reconnect, pending\n * writes can be retried to ensure data consistency.\n */\n\nexport type WriteOperation = 'set' | 'update' | 'delete' | 'transaction';\n\nexport interface PendingWrite {\n oid: string;\n operation: WriteOperation;\n path: string;\n value?: unknown;\n timestamp: number;\n}\n\nexport class PendingWriteManager {\n private pending = new Map<string, PendingWrite>();\n\n /**\n * Track a new pending write operation.\n * Uses the request ID as the tracking key.\n */\n trackWrite(requestId: string, operation: WriteOperation, path: string, value?: unknown): void {\n this.pending.set(requestId, {\n oid: requestId,\n operation,\n path,\n value,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Called when a write is acknowledged by the server.\n * Removes the write from pending tracking.\n */\n onAck(oid: string): boolean {\n return this.pending.delete(oid);\n }\n\n /**\n * Called when a write is rejected by the server.\n * Removes the write from pending tracking.\n */\n onNack(oid: string): boolean {\n return this.pending.delete(oid);\n }\n\n /**\n * Get all pending writes for retry on reconnect.\n * Returns writes in order of their timestamps (oldest first).\n */\n getPendingWrites(): PendingWrite[] {\n const writes = Array.from(this.pending.values());\n writes.sort((a, b) => a.timestamp - b.timestamp);\n return writes;\n }\n\n /**\n * Check if a specific operation is still pending.\n */\n isPending(oid: string): boolean {\n return this.pending.has(oid);\n }\n\n /**\n * Get the number of pending writes.\n */\n get pendingCount(): number {\n return this.pending.size;\n }\n\n /**\n * Clear all pending writes (e.g., on disconnect when not retrying).\n */\n clear(): void {\n this.pending.clear();\n }\n\n /**\n * Remove writes older than a specified age (in milliseconds).\n * Useful for cleaning up stale pending writes that may never be acked.\n */\n removeStale(maxAgeMs: number): number {\n const cutoff = Date.now() - maxAgeMs;\n let removed = 0;\n\n for (const [oid, write] of this.pending) {\n if (write.timestamp < cutoff) {\n this.pending.delete(oid);\n removed++;\n }\n }\n\n return removed;\n }\n}\n","/**\n * Path utilities for database paths.\n * Paths are Unix-style: /players/abc/score\n */\n\n/**\n * Normalize a path to match server format:\n * - Root is \"/\"\n * - All other paths start with \"/\" and have no trailing slash\n * - Multiple slashes are collapsed\n */\nexport function normalizePath(path: string): string {\n if (!path || path === '/') return '/';\n\n // Split, filter empty segments, rejoin with leading slash\n const segments = path.split('/').filter((segment) => segment.length > 0);\n if (segments.length === 0) return '/';\n\n return '/' + segments.join('/');\n}\n\n/**\n * Join path segments together.\n */\nexport function joinPath(...segments: string[]): string {\n const allParts: string[] = [];\n\n for (const segment of segments) {\n if (!segment || segment === '/') continue;\n // Split each segment and add non-empty parts\n const parts = segment.split('/').filter((p) => p.length > 0);\n allParts.push(...parts);\n }\n\n if (allParts.length === 0) return '/';\n return '/' + allParts.join('/');\n}\n\n/**\n * Get the parent path. Returns \"/\" for paths one level deep, \"/\" for root.\n */\nexport function getParentPath(path: string): string {\n const normalized = normalizePath(path);\n if (normalized === '/') return '/';\n\n const lastSlash = normalized.lastIndexOf('/');\n if (lastSlash <= 0) return '/';\n\n return normalized.substring(0, lastSlash);\n}\n\n/**\n * Get the last segment of a path (the \"key\").\n */\nexport function getKey(path: string): string | null {\n const normalized = normalizePath(path);\n if (normalized === '/') return null;\n\n const lastSlash = normalized.lastIndexOf('/');\n return normalized.substring(lastSlash + 1);\n}\n\n/**\n * Check if a path is valid (no invalid characters).\n */\nexport function isValidPath(path: string): boolean {\n if (path === '' || path === '/') return true;\n\n const normalized = normalizePath(path);\n // Filter out empty segments from leading slash\n const segments = normalized.split('/').filter((s) => s.length > 0);\n\n for (const segment of segments) {\n // Check for invalid characters: . $ # [ ]\n if (/[.\\$#\\[\\]]/.test(segment)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Get a nested value from an object by path.\n */\nexport function getValueAtPath(obj: unknown, path: string): unknown {\n const normalized = normalizePath(path);\n if (normalized === '/') return obj;\n\n // Split and filter empty segments (from leading slash)\n const segments = normalized.split('/').filter((s) => s.length > 0);\n let current: unknown = obj;\n\n for (const segment of segments) {\n if (current === null || current === undefined) {\n return undefined;\n }\n if (typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n return current;\n}\n\n/**\n * Set a nested value in an object by path. Mutates the object.\n * Creates intermediate objects as needed.\n */\nexport function setValueAtPath(obj: Record<string, unknown>, path: string, value: unknown): void {\n const normalized = normalizePath(path);\n if (normalized === '/') {\n // Can't set root on an object\n return;\n }\n\n // Split and filter empty segments (from leading slash)\n const segments = normalized.split('/').filter((s) => s.length > 0);\n let current: Record<string, unknown> = obj;\n\n for (let i = 0; i < segments.length - 1; i++) {\n const segment = segments[i];\n if (!(segment in current) || typeof current[segment] !== 'object' || current[segment] === null) {\n current[segment] = {};\n }\n current = current[segment] as Record<string, unknown>;\n }\n\n const lastSegment = segments[segments.length - 1];\n current[lastSegment] = value;\n}\n","/**\n * Firebase-compatible sorting utilities.\n *\n * Implements the same sorting rules as the server (and Firebase):\n * - Type hierarchy: null < false < true < numbers < strings < objects\n * - orderByKey: special handling for 32-bit integer keys\n * - orderByChild: sorts by nested child value\n * - orderByValue: sorts by the value itself\n * - orderByPriority: same as orderByChild('.priority')\n *\n * All sorting includes tie-breaking by key when values are equal.\n */\n\nimport { QueryParams } from '../protocol/messages';\n\n/**\n * Entry for sorting - contains key, value, and sort value.\n */\nexport interface SortEntry {\n key: string;\n value: unknown;\n sortValue: unknown; // The value to sort by (depends on orderBy)\n}\n\n/**\n * Returns the type rank for Firebase-style sorting.\n * Order: null (0) < false (1) < true (2) < numbers (3) < strings (4) < objects (5)\n */\nexport function typeRank(value: unknown): number {\n if (value === null || value === undefined) {\n return 0;\n }\n if (typeof value === 'boolean') {\n return value ? 2 : 1; // false = 1, true = 2\n }\n if (typeof value === 'number') {\n return 3;\n }\n if (typeof value === 'string') {\n return 4;\n }\n if (typeof value === 'object') {\n return 5;\n }\n return 6; // Unknown types sort last\n}\n\n/**\n * Compare two values using Firebase ordering rules.\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n */\nexport function compareValues(a: unknown, b: unknown): number {\n const rankA = typeRank(a);\n const rankB = typeRank(b);\n\n // Different types - compare by rank\n if (rankA !== rankB) {\n return rankA < rankB ? -1 : 1;\n }\n\n // Same type - compare values\n switch (rankA) {\n case 0: // null\n return 0;\n\n case 1: // false\n case 2: // true\n return 0; // Same boolean value (rank already differentiated)\n\n case 3: // number\n const numA = a as number;\n const numB = b as number;\n if (numA < numB) return -1;\n if (numA > numB) return 1;\n return 0;\n\n case 4: // string\n const strA = a as string;\n const strB = b as string;\n if (strA < strB) return -1;\n if (strA > strB) return 1;\n return 0;\n\n case 5: // object\n return 0; // Objects are equal for value comparison, sort by key\n\n default:\n return 0;\n }\n}\n\n/**\n * Check if a key should be treated as a 32-bit integer for sorting.\n * Integer keys sort numerically before string keys.\n */\nexport function isInt32Key(key: string): boolean {\n if (key.length === 0) {\n return false;\n }\n\n // Check for leading zeros (except \"0\" itself)\n if (key.length > 1 && key[0] === '0') {\n return false;\n }\n\n // Handle negative numbers\n if (key[0] === '-') {\n if (key.length === 1) {\n return false; // Just \"-\" is not valid\n }\n // Check for leading zeros after minus: \"-0\", \"-007\"\n if (key.length > 2 && key[1] === '0') {\n return false;\n }\n // Check for \"-0\" (negative zero)\n if (key === '-0') {\n return false;\n }\n }\n\n // Try to parse as integer\n const parsed = parseInt(key, 10);\n if (isNaN(parsed)) {\n return false;\n }\n\n // Check if it's the same string (no extra characters, no scientific notation)\n if (String(parsed) !== key) {\n return false;\n }\n\n // Check 32-bit signed integer range\n const MIN_INT32 = -2147483648;\n const MAX_INT32 = 2147483647;\n return parsed >= MIN_INT32 && parsed <= MAX_INT32;\n}\n\n/**\n * Compare two keys using Firebase orderByKey rules.\n * Integer keys sort numerically first, then string keys sort lexicographically.\n */\nexport function compareKeys(a: string, b: string): number {\n const aIsInt = isInt32Key(a);\n const bIsInt = isInt32Key(b);\n\n // Integers come before strings\n if (aIsInt && !bIsInt) {\n return -1;\n }\n if (!aIsInt && bIsInt) {\n return 1;\n }\n\n // Both integers: compare numerically\n if (aIsInt && bIsInt) {\n const aInt = parseInt(a, 10);\n const bInt = parseInt(b, 10);\n if (aInt < bInt) return -1;\n if (aInt > bInt) return 1;\n return 0;\n }\n\n // Both strings: compare lexicographically\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n}\n\n/**\n * Get a nested value from an object using a path.\n */\nfunction getNestedValue(obj: unknown, path: string): unknown {\n if (!obj || typeof obj !== 'object') {\n return undefined;\n }\n\n const segments = path.split('/').filter((s) => s.length > 0);\n let current: unknown = obj;\n\n for (const segment of segments) {\n if (!current || typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n return current;\n}\n\n/**\n * Get the sort value for an entry based on query parameters.\n */\nexport function getSortValue(value: unknown, queryParams: QueryParams | null): unknown {\n if (!queryParams) {\n return null; // No ordering, key is used\n }\n\n // orderByPriority is normalized to orderByChild('.priority') on the server\n // but we handle it explicitly here too\n if (queryParams.orderBy === 'priority') {\n return getNestedValue(value, '.priority');\n }\n\n if (queryParams.orderByChild) {\n return getNestedValue(value, queryParams.orderByChild);\n }\n\n if (queryParams.orderBy === 'value') {\n return value;\n }\n\n // orderByKey or default - sort by key, not value\n return null;\n}\n\n/**\n * Compare two entries for sorting.\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n */\nexport function compareEntries(a: SortEntry, b: SortEntry, queryParams: QueryParams | null): number {\n // If ordering by key (or no ordering), just compare keys\n if (!queryParams || queryParams.orderBy === 'key' || (!queryParams.orderBy && !queryParams.orderByChild)) {\n return compareKeys(a.key, b.key);\n }\n\n // Compare by sort value first\n const cmp = compareValues(a.sortValue, b.sortValue);\n if (cmp !== 0) {\n return cmp;\n }\n\n // Equal sort values - tie-break by key\n return compareKeys(a.key, b.key);\n}\n\n/**\n * Sort an array of entries according to query parameters.\n * Modifies the array in place and returns it.\n */\nexport function sortEntries(entries: SortEntry[], queryParams: QueryParams | null): SortEntry[] {\n entries.sort((a, b) => compareEntries(a, b, queryParams));\n return entries;\n}\n\n/**\n * Create sort entries from an object's children.\n */\nexport function createSortEntries(data: unknown, queryParams: QueryParams | null): SortEntry[] {\n if (!data || typeof data !== 'object' || Array.isArray(data)) {\n return [];\n }\n\n const obj = data as Record<string, unknown>;\n const entries: SortEntry[] = [];\n\n for (const key of Object.keys(obj)) {\n // Skip .priority when creating entries (it's metadata, not a child)\n if (key === '.priority') {\n continue;\n }\n\n const value = obj[key];\n entries.push({\n key,\n value,\n sortValue: getSortValue(value, queryParams),\n });\n }\n\n return sortEntries(entries, queryParams);\n}\n\n/**\n * Get the sorted keys from data according to query parameters.\n */\nexport function getSortedKeys(data: unknown, queryParams: QueryParams | null): string[] {\n const entries = createSortEntries(data, queryParams);\n return entries.map((e) => e.key);\n}\n\n/**\n * Find the position where a key should be inserted to maintain sort order.\n * Returns the index of the key that should come before this one,\n * or -1 if it should be first.\n */\nexport function findInsertPosition(\n orderedKeys: string[],\n data: Record<string, unknown>,\n newKey: string,\n queryParams: QueryParams | null\n): number {\n const newValue = data[newKey];\n const newSortValue = getSortValue(newValue, queryParams);\n const newEntry: SortEntry = { key: newKey, value: newValue, sortValue: newSortValue };\n\n // Find where this entry belongs\n for (let i = 0; i < orderedKeys.length; i++) {\n const existingKey = orderedKeys[i];\n const existingValue = data[existingKey];\n const existingSortValue = getSortValue(existingValue, queryParams);\n const existingEntry: SortEntry = { key: existingKey, value: existingValue, sortValue: existingSortValue };\n\n const cmp = compareEntries(newEntry, existingEntry, queryParams);\n if (cmp < 0) {\n // New entry should come before this one\n return i - 1; // Return the previous index, or -1 if first\n }\n }\n\n // New entry goes at the end\n return orderedKeys.length - 1;\n}\n\n/**\n * Get the previous child key (the key that comes before this one in sorted order).\n * Returns null if this key should be first.\n */\nexport function getPreviousKey(orderedKeys: string[], key: string): string | null {\n const idx = orderedKeys.indexOf(key);\n if (idx <= 0) return null;\n return orderedKeys[idx - 1];\n}\n","/**\n * View - represents a client's subscription to a database path.\n *\n * Each View encapsulates all state for a single subscription:\n * - Path and query parameters\n * - Event callbacks by type\n * - Ordered children (keys in sorted order, sorted by client)\n * - Local cache of visible data\n *\n * This mirrors the server's View concept, enabling:\n * - Per-View caching (no shared global cache)\n * - Client-side sorting (ignores server's ak/mv hints)\n * - Local-first writes (future)\n * - Independent reconnection per View\n */\n\nimport type { DataSnapshot } from '../DataSnapshot';\nimport { EventType } from '../protocol/constants';\nimport { QueryParams } from '../protocol/messages';\nimport { getValueAtPath, joinPath, normalizePath, setValueAtPath } from '../utils/path';\nimport { getSortedKeys, compareKeys, getSortValue, compareEntries, SortEntry } from '../utils/sort';\n\nexport type SnapshotCallback = (snapshot: DataSnapshot, previousChildKey?: string | null) => void;\n\ninterface SubscriptionEntry {\n callback: SnapshotCallback;\n unsubscribe: () => void;\n}\n\nexport class View {\n /** The absolute path this View is subscribed to */\n readonly path: string;\n\n /** Query parameters for this View (ordering, limits, ranges) */\n private _queryParams: QueryParams | null;\n\n /** Event callbacks organized by event type */\n private eventCallbacks = new Map<EventType, SubscriptionEntry[]>();\n\n /** Child keys in sorted order */\n private _orderedChildren: string[] = [];\n\n /** Local cache: stores the value at the subscription path */\n private _cache: unknown = undefined;\n\n /** Whether we've received the initial snapshot from the server */\n private _hasReceivedInitialSnapshot = false;\n\n /** Pending write request IDs for this View (for local-first recovery) */\n private _pendingWrites = new Set<string>();\n\n /** Whether this View is in recovery mode after a nack */\n private _recovering = false;\n\n constructor(path: string, queryParams?: QueryParams) {\n this.path = normalizePath(path);\n this._queryParams = queryParams ?? null;\n }\n\n /** Get the current query parameters */\n get queryParams(): QueryParams | null {\n return this._queryParams;\n }\n\n /**\n * Update query parameters and re-sort cached data.\n * Returns true if queryParams actually changed.\n */\n updateQueryParams(newQueryParams?: QueryParams): boolean {\n const newParams = newQueryParams ?? null;\n\n // Check if params actually changed\n if (this.queryParamsEqual(this._queryParams, newParams)) {\n return false;\n }\n\n this._queryParams = newParams;\n\n // Re-sort cached data with new query params\n if (this._cache && typeof this._cache === 'object' && this._cache !== null) {\n this._orderedChildren = getSortedKeys(this._cache as Record<string, unknown>, this._queryParams);\n }\n\n return true;\n }\n\n /**\n * Compare two QueryParams objects for equality.\n */\n private queryParamsEqual(a: QueryParams | null, b: QueryParams | null): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n\n // Compare all relevant fields\n return (\n a.orderBy === b.orderBy &&\n a.orderByChild === b.orderByChild &&\n a.limitToFirst === b.limitToFirst &&\n a.limitToLast === b.limitToLast &&\n a.startAt === b.startAt &&\n a.startAtKey === b.startAtKey &&\n a.endAt === b.endAt &&\n a.endAtKey === b.endAtKey &&\n a.equalTo === b.equalTo &&\n a.equalToKey === b.equalToKey\n );\n }\n\n // ============================================\n // Subscription Management\n // ============================================\n\n /**\n * Add a callback for an event type.\n * Returns an unsubscribe function.\n */\n addCallback(eventType: EventType, callback: SnapshotCallback): () => void {\n if (!this.eventCallbacks.has(eventType)) {\n this.eventCallbacks.set(eventType, []);\n }\n\n const unsubscribe = () => {\n this.removeCallback(eventType, callback);\n };\n\n this.eventCallbacks.get(eventType)!.push({ callback, unsubscribe });\n return unsubscribe;\n }\n\n /**\n * Remove a specific callback for an event type.\n */\n removeCallback(eventType: EventType, callback: SnapshotCallback): void {\n const entries = this.eventCallbacks.get(eventType);\n if (!entries) return;\n\n const index = entries.findIndex((entry) => entry.callback === callback);\n if (index !== -1) {\n entries.splice(index, 1);\n }\n\n // Clean up empty arrays\n if (entries.length === 0) {\n this.eventCallbacks.delete(eventType);\n }\n }\n\n /**\n * Remove all callbacks for an event type.\n */\n removeAllCallbacks(eventType: EventType): void {\n this.eventCallbacks.delete(eventType);\n }\n\n /**\n * Get all subscribed event types.\n */\n getEventTypes(): EventType[] {\n return Array.from(this.eventCallbacks.keys());\n }\n\n /**\n * Get callbacks for a specific event type.\n */\n getCallbacks(eventType: EventType): SubscriptionEntry[] {\n return this.eventCallbacks.get(eventType) ?? [];\n }\n\n /**\n * Check if there are any callbacks registered.\n */\n hasCallbacks(): boolean {\n return this.eventCallbacks.size > 0;\n }\n\n /**\n * Check if there's a callback for a specific event type.\n */\n hasCallbacksForType(eventType: EventType): boolean {\n const entries = this.eventCallbacks.get(eventType);\n return entries !== undefined && entries.length > 0;\n }\n\n // ============================================\n // Cache Management\n // ============================================\n\n /**\n * Set the full cache value (used for initial snapshot).\n * Children are sorted using client-side sorting rules.\n */\n setCache(value: unknown): void {\n this._cache = value;\n this._hasReceivedInitialSnapshot = true;\n\n // Update ordered children from the value using client-side sorting\n if (value && typeof value === 'object' && value !== null && !Array.isArray(value)) {\n this._orderedChildren = getSortedKeys(value, this.queryParams);\n } else {\n this._orderedChildren = [];\n }\n }\n\n /**\n * Get the full cached value at the subscription path.\n */\n getCache(): unknown {\n return this._cache;\n }\n\n /**\n * Get a value from the cache at a relative or absolute path.\n * If the path is outside this View's scope, returns undefined.\n */\n getCacheValue(path: string): { value: unknown; found: boolean } {\n const normalized = normalizePath(path);\n\n // Check if the requested path is within this View's scope\n if (normalized === this.path) {\n // Exact match - return full cache\n if (this._cache !== undefined) {\n return { value: this._cache, found: true };\n }\n return { value: undefined, found: false };\n }\n\n // Check if path is a descendant of this View's path\n if (normalized.startsWith(this.path + '/') || this.path === '/') {\n const relativePath = this.path === '/'\n ? normalized\n : normalized.slice(this.path.length);\n\n if (this._cache !== undefined) {\n const extractedValue = getValueAtPath(this._cache, relativePath);\n return { value: extractedValue, found: true };\n }\n }\n\n return { value: undefined, found: false };\n }\n\n /**\n * Update a child value in the cache.\n * relativePath should be relative to this View's path.\n * Maintains sorted order of children using client-side sorting.\n */\n updateCacheChild(relativePath: string, value: unknown): void {\n if (relativePath === '/') {\n // Full replacement\n this.setCache(value);\n return;\n }\n\n // Extract child key from relative path\n const segments = relativePath.split('/').filter((s) => s.length > 0);\n if (segments.length === 0) return;\n\n const childKey = segments[0];\n\n // Ensure cache is an object\n if (this._cache === null || this._cache === undefined || typeof this._cache !== 'object') {\n this._cache = {};\n }\n\n // Update the cache\n const cache = this._cache as Record<string, unknown>;\n\n if (segments.length === 1) {\n // Direct child update\n if (value === null) {\n delete cache[childKey];\n // Remove from ordered children\n const idx = this._orderedChildren.indexOf(childKey);\n if (idx !== -1) {\n this._orderedChildren.splice(idx, 1);\n }\n } else {\n const wasPresent = childKey in cache;\n cache[childKey] = value;\n\n if (!wasPresent) {\n // New child - insert at correct sorted position\n this.insertChildSorted(childKey, value);\n } else {\n // Existing child - re-sort if sort field might have changed\n this.resortChild(childKey);\n }\n }\n } else {\n // Nested update - need to navigate and update\n if (value === null) {\n // Deletion at nested path\n setValueAtPath(cache, relativePath, undefined);\n } else {\n // Ensure parent path exists\n const wasPresent = childKey in cache;\n if (!wasPresent) {\n cache[childKey] = {};\n }\n setValueAtPath(cache, relativePath, value);\n\n if (!wasPresent) {\n // New child - insert at correct sorted position\n this.insertChildSorted(childKey, cache[childKey]);\n } else {\n // Existing child - re-sort if sort field might have changed\n this.resortChild(childKey);\n }\n }\n }\n }\n\n /**\n * Insert a child key at the correct sorted position.\n */\n private insertChildSorted(key: string, value: unknown): void {\n if (this._orderedChildren.includes(key)) {\n // Already present, just re-sort\n this.resortChild(key);\n return;\n }\n\n const cache = this._cache as Record<string, unknown>;\n const newSortValue = getSortValue(value, this.queryParams);\n const newEntry: SortEntry = { key, value, sortValue: newSortValue };\n\n // Find the correct position\n let insertIdx = this._orderedChildren.length; // Default: end\n for (let i = 0; i < this._orderedChildren.length; i++) {\n const existingKey = this._orderedChildren[i];\n const existingValue = cache[existingKey];\n const existingSortValue = getSortValue(existingValue, this.queryParams);\n const existingEntry: SortEntry = { key: existingKey, value: existingValue, sortValue: existingSortValue };\n\n if (compareEntries(newEntry, existingEntry, this.queryParams) < 0) {\n insertIdx = i;\n break;\n }\n }\n\n this._orderedChildren.splice(insertIdx, 0, key);\n }\n\n /**\n * Re-sort a child that already exists (its sort value may have changed).\n */\n private resortChild(key: string): void {\n const idx = this._orderedChildren.indexOf(key);\n if (idx === -1) return;\n\n // Remove and re-insert\n this._orderedChildren.splice(idx, 1);\n const cache = this._cache as Record<string, unknown>;\n const value = cache[key];\n this.insertChildSorted(key, value);\n }\n\n /**\n * Remove a child from the cache.\n */\n removeCacheChild(relativePath: string): void {\n this.updateCacheChild(relativePath, null);\n }\n\n /**\n * Check if we've received the initial snapshot.\n */\n get hasReceivedInitialSnapshot(): boolean {\n return this._hasReceivedInitialSnapshot;\n }\n\n // ============================================\n // Ordered Children\n // ============================================\n\n /**\n * Get the ordered children keys.\n */\n get orderedChildren(): string[] {\n return [...this._orderedChildren];\n }\n\n /**\n * Get the previous sibling key for a given key.\n * Returns null if the key is first or not found.\n */\n getPreviousChildKey(key: string): string | null {\n const idx = this._orderedChildren.indexOf(key);\n if (idx <= 0) return null;\n return this._orderedChildren[idx - 1];\n }\n\n /**\n * Check if a key exists in the ordered children.\n */\n hasChild(key: string): boolean {\n return this._orderedChildren.includes(key);\n }\n\n // ============================================\n // Query Helpers\n // ============================================\n\n /**\n * Check if this View has query parameters.\n */\n hasQuery(): boolean {\n if (!this.queryParams) return false;\n return !!(\n this.queryParams.limitToFirst ||\n this.queryParams.limitToLast ||\n this.queryParams.startAt !== undefined ||\n this.queryParams.endAt !== undefined ||\n this.queryParams.equalTo !== undefined ||\n this.queryParams.orderBy ||\n this.queryParams.orderByChild\n );\n }\n\n /**\n * Check if this View has limit constraints.\n */\n hasLimit(): boolean {\n if (!this.queryParams) return false;\n return !!(this.queryParams.limitToFirst || this.queryParams.limitToLast);\n }\n\n // ============================================\n // Pending Writes (for local-first)\n // ============================================\n\n /**\n * Get current pending write IDs (to include in pw field).\n * Returns a copy of the set as an array.\n */\n getPendingWriteIds(): string[] {\n return Array.from(this._pendingWrites);\n }\n\n /**\n * Add a pending write request ID.\n */\n addPendingWrite(requestId: string): void {\n this._pendingWrites.add(requestId);\n }\n\n /**\n * Remove a pending write (on ack or nack).\n */\n removePendingWrite(requestId: string): boolean {\n return this._pendingWrites.delete(requestId);\n }\n\n /**\n * Clear all pending writes (on nack recovery).\n */\n clearPendingWrites(): void {\n this._pendingWrites.clear();\n }\n\n /**\n * Check if this View has pending writes.\n */\n hasPendingWrites(): boolean {\n return this._pendingWrites.size > 0;\n }\n\n /**\n * Check if a specific write is pending.\n */\n isWritePending(requestId: string): boolean {\n return this._pendingWrites.has(requestId);\n }\n\n // ============================================\n // Recovery State (for local-first nack handling)\n // ============================================\n\n /**\n * Check if this View is in recovery mode.\n */\n get recovering(): boolean {\n return this._recovering;\n }\n\n /**\n * Enter recovery mode (after a nack).\n */\n enterRecovery(): void {\n this._recovering = true;\n this.clearPendingWrites();\n }\n\n /**\n * Exit recovery mode (after fresh snapshot received).\n */\n exitRecovery(): void {\n this._recovering = false;\n }\n\n // ============================================\n // Cleanup\n // ============================================\n\n /**\n * Clear the cache but preserve callbacks and pending writes.\n * Used during reconnection.\n */\n clearCache(): void {\n this._cache = undefined;\n this._orderedChildren = [];\n this._hasReceivedInitialSnapshot = false;\n // Note: pending writes are preserved for retry on reconnect\n }\n\n /**\n * Clear everything - used when unsubscribing completely.\n */\n clear(): void {\n this.eventCallbacks.clear();\n this._cache = undefined;\n this._orderedChildren = [];\n this._hasReceivedInitialSnapshot = false;\n this._pendingWrites.clear();\n this._recovering = false;\n }\n}\n","/**\n * SubscriptionManager - manages Views and routes events to callbacks.\n *\n * Refactored to use View-based architecture:\n * - Each subscription path has a single View\n * - Each View maintains its own cache and ordered children\n * - No shared global cache\n *\n * Handles the delta-based protocol where server sends 'put' and 'patch' events.\n * Client generates child_added, child_changed, child_removed, child_moved events locally.\n */\n\nimport type { DataSnapshot } from '../DataSnapshot';\nimport { EventType } from '../protocol/constants';\nimport { EventMessage, PutEventMessage, PatchEventMessage, QueryParams } from '../protocol/messages';\nimport { joinPath, normalizePath } from '../utils/path';\nimport { View, SnapshotCallback } from './View';\n\nexport { SnapshotCallback } from './View';\n\nexport class SubscriptionManager {\n // path -> View (one View per subscribed path)\n private views = new Map<string, View>();\n\n // Callback to send subscribe message to server\n private sendSubscribe: ((path: string, eventTypes: string[], queryParams?: QueryParams) => Promise<void>) | null = null;\n\n // Callback to send unsubscribe message to server\n private sendUnsubscribe: ((path: string) => Promise<void>) | null = null;\n\n // Callback to create DataSnapshot from event data\n private createSnapshot:\n | ((path: string, value: unknown, volatile: boolean, serverTimestamp?: number) => DataSnapshot)\n | null = null;\n\n /**\n * Initialize the manager with server communication callbacks.\n */\n initialize(options: {\n sendSubscribe: (path: string, eventTypes: string[], queryParams?: QueryParams) => Promise<void>;\n sendUnsubscribe: (path: string) => Promise<void>;\n createSnapshot: (path: string, value: unknown, volatile: boolean, serverTimestamp?: number) => DataSnapshot;\n }): void {\n this.sendSubscribe = options.sendSubscribe;\n this.sendUnsubscribe = options.sendUnsubscribe;\n this.createSnapshot = options.createSnapshot;\n }\n\n /**\n * Subscribe to events at a path.\n * Returns an unsubscribe function.\n */\n subscribe(path: string, eventType: EventType, callback: SnapshotCallback, queryParams?: QueryParams): () => void {\n const normalizedPath = path;\n\n // Get or create View for this path\n let view = this.views.get(normalizedPath);\n const isNewView = !view;\n let queryParamsChanged = false;\n\n if (!view) {\n view = new View(normalizedPath, queryParams);\n this.views.set(normalizedPath, view);\n } else {\n // Check if queryParams changed - update View to match latest subscription\n // This matches server behavior where latest subscription overwrites\n queryParamsChanged = view.updateQueryParams(queryParams);\n }\n\n // Check if this is a new event type for this view\n const existingEventTypes = view.getEventTypes();\n const isNewEventType = !existingEventTypes.includes(eventType);\n\n // Add callback to view\n const unsubscribe = view.addCallback(eventType, callback);\n\n // Wrap unsubscribe to handle cleanup\n const wrappedUnsubscribe = () => {\n this.unsubscribeCallback(normalizedPath, eventType, callback);\n };\n\n // If this is a new view, new event type, or queryParams changed, update server subscription\n if (isNewView || isNewEventType || queryParamsChanged) {\n const allEventTypes = view.getEventTypes();\n // Fire and forget - we don't wait for the server\n this.sendSubscribe?.(normalizedPath, allEventTypes, view.queryParams ?? undefined).catch((err) => {\n console.error('Failed to subscribe:', err);\n });\n }\n\n // If the View already has cached data, fire initial events to the new callback\n // This ensures new subscribers get current state even if View already existed\n if (!isNewView && view.hasReceivedInitialSnapshot) {\n this.fireInitialEventsToCallback(view, eventType, callback);\n }\n\n return wrappedUnsubscribe;\n }\n\n /**\n * Fire initial events to a newly added callback from cached data.\n * This ensures new subscribers get current state even if View already existed.\n */\n private fireInitialEventsToCallback(\n view: View,\n eventType: EventType,\n callback: SnapshotCallback\n ): void {\n const cache = view.getCache();\n\n if (eventType === 'value') {\n // Fire value event with full cached data\n const snapshot = this.createSnapshot?.(view.path, cache, false);\n if (snapshot) {\n try {\n callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in value subscription callback:', err);\n }\n }\n } else if (eventType === 'child_added') {\n // Fire child_added for each child in order\n const orderedChildren = view.orderedChildren;\n for (let i = 0; i < orderedChildren.length; i++) {\n const childKey = orderedChildren[i];\n const previousChildKey = i > 0 ? orderedChildren[i - 1] : null;\n const childPath = joinPath(view.path, childKey);\n const { value: childValue } = view.getCacheValue(childPath);\n const snapshot = this.createSnapshot?.(childPath, childValue, false);\n if (snapshot) {\n try {\n callback(snapshot, previousChildKey);\n } catch (err) {\n console.error('Error in child_added subscription callback:', err);\n }\n }\n }\n }\n // child_changed, child_removed, child_moved don't need initial events\n }\n\n /**\n * Remove a specific callback from a subscription.\n */\n private unsubscribeCallback(path: string, eventType: EventType, callback: SnapshotCallback): void {\n const view = this.views.get(path);\n if (!view) return;\n\n view.removeCallback(eventType, callback);\n\n // If no more callbacks for this view, remove it entirely\n if (!view.hasCallbacks()) {\n this.views.delete(path);\n this.sendUnsubscribe?.(path).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n }\n }\n\n /**\n * Remove all subscriptions of a specific event type at a path.\n */\n unsubscribeEventType(path: string, eventType: EventType): void {\n const normalizedPath = path;\n const view = this.views.get(normalizedPath);\n if (!view) return;\n\n view.removeAllCallbacks(eventType);\n\n // If no more callbacks, unsubscribe from server\n if (!view.hasCallbacks()) {\n this.views.delete(normalizedPath);\n this.sendUnsubscribe?.(normalizedPath).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n } else {\n // Update server with remaining event types\n const remainingEventTypes = view.getEventTypes();\n this.sendSubscribe?.(normalizedPath, remainingEventTypes, view.queryParams ?? undefined).catch((err) => {\n console.error('Failed to update subscription:', err);\n });\n }\n }\n\n /**\n * Remove ALL subscriptions at a path.\n */\n unsubscribeAll(path: string): void {\n const normalizedPath = path;\n const view = this.views.get(normalizedPath);\n if (!view) return;\n\n view.clear();\n this.views.delete(normalizedPath);\n this.sendUnsubscribe?.(normalizedPath).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n }\n\n /**\n * Handle an incoming event message from the server.\n * Server sends 'put' or 'patch' events; we generate child_* events client-side.\n */\n handleEvent(message: EventMessage): void {\n if (message.ev === 'put') {\n this.handlePutEvent(message);\n } else if (message.ev === 'patch') {\n this.handlePatchEvent(message);\n } else {\n console.warn('Unknown event type:', (message as { ev: string }).ev);\n }\n }\n\n /**\n * Handle a 'put' event - single path change.\n */\n private handlePutEvent(message: PutEventMessage): void {\n const subscriptionPath = message.sp;\n const relativePath = message.p;\n const value = message.v;\n const isVolatile = message.x ?? false;\n const serverTimestamp = message.ts;\n\n const view = this.views.get(subscriptionPath);\n if (!view) return;\n\n // Skip if no value to apply\n if (value === undefined) return;\n\n // If View is recovering, only accept full snapshots (relativePath === '/')\n // Delta events during recovery are stale and should be ignored\n if (view.recovering) {\n if (relativePath !== '/') {\n // Ignore delta events during recovery\n return;\n }\n // This is a full snapshot - exit recovery after applying\n this.applyWriteToView(view, [{ relativePath, value }], isVolatile, serverTimestamp);\n view.exitRecovery();\n return;\n }\n\n // Use shared helper for cache update and event firing\n this.applyWriteToView(view, [{ relativePath, value }], isVolatile, serverTimestamp);\n }\n\n /**\n * Handle a 'patch' event - multi-path change.\n */\n private handlePatchEvent(message: PatchEventMessage): void {\n const subscriptionPath = message.sp;\n const basePath = message.p;\n const patches = message.v;\n const isVolatile = message.x ?? false;\n const serverTimestamp = message.ts;\n\n const view = this.views.get(subscriptionPath);\n if (!view) return;\n\n // Ignore patch events during recovery - they're always deltas\n // We're waiting for a full snapshot (put with p: '/')\n if (view.recovering) {\n return;\n }\n\n // Skip if no patches to apply\n if (!patches) return;\n\n // Build updates array from patches\n const updates: Array<{ relativePath: string; value: unknown }> = [];\n for (const [relativePath, patchValue] of Object.entries(patches)) {\n // Build the full relative path from base + relativePath\n let fullRelativePath: string;\n if (basePath === '/') {\n fullRelativePath = relativePath.startsWith('/') ? relativePath : '/' + relativePath;\n } else {\n fullRelativePath = joinPath(basePath, relativePath);\n }\n updates.push({ relativePath: fullRelativePath, value: patchValue });\n }\n\n // Use shared helper for cache update and event firing\n this.applyWriteToView(view, updates, isVolatile, serverTimestamp);\n }\n\n /**\n * Detect and fire child_moved events for children that changed position.\n */\n private detectAndFireMoves(\n view: View,\n previousOrder: string[],\n currentOrder: string[],\n previousPositions: Map<string, number>,\n currentPositions: Map<string, number>,\n previousChildSet: Set<string>,\n currentChildSet: Set<string>,\n childMovedSubs: Array<{ callback: SnapshotCallback }>,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (childMovedSubs.length === 0) return;\n\n // For each child that exists in both old and new, check if position changed\n for (const key of currentOrder) {\n if (!previousChildSet.has(key) || !currentChildSet.has(key)) {\n continue; // Skip newly added or removed children\n }\n\n const oldPos = previousPositions.get(key)!;\n const newPos = currentPositions.get(key)!;\n\n // Check if the relative order changed\n // We need to compare the previous sibling, not just the absolute position\n const oldPrevKey = oldPos > 0 ? previousOrder[oldPos - 1] : null;\n const newPrevKey = newPos > 0 ? currentOrder[newPos - 1] : null;\n\n if (oldPrevKey !== newPrevKey) {\n // Position changed - fire child_moved with new previousChildKey\n this.fireChildMoved(view, key, childMovedSubs, newPrevKey, isVolatile, serverTimestamp);\n }\n }\n }\n\n /**\n * Fire child_added callbacks for a child key.\n */\n private fireChildAdded(\n view: View,\n childKey: string,\n subs: Array<{ callback: SnapshotCallback }>,\n previousChildKey: string | null,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n const { value: childValue } = view.getCacheValue(childPath);\n const snapshot = this.createSnapshot?.(childPath, childValue, isVolatile, serverTimestamp);\n\n if (snapshot) {\n for (const entry of subs) {\n try {\n entry.callback(snapshot, previousChildKey);\n } catch (err) {\n console.error('Error in child_added subscription callback:', err);\n }\n }\n }\n }\n\n /**\n * Fire child_changed callbacks for a child key.\n */\n private fireChildChanged(\n view: View,\n childKey: string,\n subs: Array<{ callback: SnapshotCallback }>,\n previousChildKey: string | null,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n const { value: childValue } = view.getCacheValue(childPath);\n const snapshot = this.createSnapshot?.(childPath, childValue, isVolatile, serverTimestamp);\n\n if (snapshot) {\n for (const entry of subs) {\n try {\n entry.callback(snapshot, previousChildKey);\n } catch (err) {\n console.error('Error in child_changed subscription callback:', err);\n }\n }\n }\n }\n\n /**\n * Fire child_removed callbacks for a child key.\n */\n private fireChildRemoved(\n view: View,\n childKey: string,\n subs: Array<{ callback: SnapshotCallback }>,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n // For removed children, value is null\n const snapshot = this.createSnapshot?.(childPath, null, isVolatile, serverTimestamp);\n\n if (snapshot) {\n for (const entry of subs) {\n try {\n // previousChildKey is not meaningful for removed children\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in child_removed subscription callback:', err);\n }\n }\n }\n }\n\n /**\n * Fire child_moved callbacks for a child key.\n */\n private fireChildMoved(\n view: View,\n childKey: string,\n subs: Array<{ callback: SnapshotCallback }>,\n previousChildKey: string | null,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n const { value: childValue } = view.getCacheValue(childPath);\n const snapshot = this.createSnapshot?.(childPath, childValue, isVolatile, serverTimestamp);\n\n if (snapshot) {\n for (const entry of subs) {\n try {\n entry.callback(snapshot, previousChildKey);\n } catch (err) {\n console.error('Error in child_moved subscription callback:', err);\n }\n }\n }\n }\n\n /**\n * Clear all subscriptions (e.g., on disconnect).\n */\n clear(): void {\n for (const view of this.views.values()) {\n view.clear();\n }\n this.views.clear();\n }\n\n /**\n * Check if there are any subscriptions at a path.\n */\n hasSubscriptions(path: string): boolean {\n return this.views.has(path);\n }\n\n /**\n * Check if a path is \"covered\" by an active subscription.\n *\n * A path is covered if:\n * - There's an active 'value' subscription at that exact path, OR\n * - There's an active 'value' subscription at an ancestor path\n */\n isPathCovered(path: string): boolean {\n const normalized = normalizePath(path);\n\n // Check exact match - must have 'value' subscription\n if (this.hasValueSubscription(normalized)) {\n return true;\n }\n\n // Check ancestors - walk up the path tree\n const segments = normalized.split('/').filter((s) => s.length > 0);\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const ancestorPath = i === 0 ? '/' : '/' + segments.slice(0, i).join('/');\n if (this.hasValueSubscription(ancestorPath)) {\n return true;\n }\n }\n\n // Check root\n if (normalized !== '/' && this.hasValueSubscription('/')) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Check if there's a 'value' subscription at a path.\n */\n private hasValueSubscription(path: string): boolean {\n const view = this.views.get(path);\n return view !== undefined && view.hasCallbacksForType('value');\n }\n\n /**\n * Get a cached value if the path is covered by an active subscription.\n */\n getCachedValue(path: string): { value: unknown; found: boolean } {\n const normalized = normalizePath(path);\n\n // First check if the path is covered by a subscription\n if (!this.isPathCovered(normalized)) {\n return { value: undefined, found: false };\n }\n\n // Try to find a View that covers this path\n // Check exact match first\n const exactView = this.views.get(normalized);\n if (exactView) {\n return exactView.getCacheValue(normalized);\n }\n\n // Check ancestors\n const segments = normalized.split('/').filter((s) => s.length > 0);\n for (let i = segments.length - 1; i >= 0; i--) {\n const ancestorPath = i === 0 ? '/' : '/' + segments.slice(0, i).join('/');\n const ancestorView = this.views.get(ancestorPath);\n if (ancestorView && ancestorView.hasCallbacksForType('value')) {\n return ancestorView.getCacheValue(normalized);\n }\n }\n\n // Check root\n if (normalized !== '/') {\n const rootView = this.views.get('/');\n if (rootView && rootView.hasCallbacksForType('value')) {\n return rootView.getCacheValue(normalized);\n }\n }\n\n return { value: undefined, found: false };\n }\n\n /**\n * Get the cache size (for testing/debugging).\n * Returns the number of Views.\n */\n get cacheSize(): number {\n return this.views.size;\n }\n\n /**\n * Clear only the cache, preserving subscription registrations.\n * Used when preparing for reconnect - we keep subscriptions but will get fresh data.\n */\n clearCacheOnly(): void {\n for (const view of this.views.values()) {\n view.clearCache();\n }\n }\n\n /**\n * Re-subscribe to all active subscriptions.\n * Used after reconnecting to restore subscriptions on the server.\n */\n async resubscribeAll(): Promise<void> {\n for (const [path, view] of this.views) {\n const eventTypes = view.getEventTypes();\n if (eventTypes.length > 0) {\n try {\n await this.sendSubscribe?.(path, eventTypes, view.queryParams ?? undefined);\n } catch (err) {\n console.error(`Failed to resubscribe to ${path}:`, err);\n // Continue with other subscriptions even if one fails\n }\n }\n }\n }\n\n /**\n * Get information about active subscriptions (for debugging).\n */\n getActiveSubscriptions(): Array<{ path: string; eventTypes: EventType[]; queryParams?: QueryParams }> {\n const result: Array<{ path: string; eventTypes: EventType[]; queryParams?: QueryParams }> = [];\n\n for (const [path, view] of this.views) {\n const eventTypes = view.getEventTypes();\n const queryParams = view.queryParams ?? undefined;\n result.push({ path, eventTypes, queryParams });\n }\n\n return result;\n }\n\n /**\n * Get a View by path (for testing/debugging).\n */\n getView(path: string): View | undefined {\n return this.views.get(path);\n }\n\n // ============================================\n // Shared Write Application (used by server events and optimistic writes)\n // ============================================\n\n /**\n * Apply write(s) to a View's cache and fire appropriate events.\n * This is the core logic shared between server events and optimistic writes.\n *\n * @param view - The View to update\n * @param updates - Array of {relativePath, value} pairs to apply\n * @param isVolatile - Whether this is a volatile update\n * @param serverTimestamp - Optional server timestamp\n */\n private applyWriteToView(\n view: View,\n updates: Array<{ relativePath: string; value: unknown }>,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n // Capture previous state (including cache snapshot for change detection)\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n const previousCacheJson = JSON.stringify(view.getCache());\n\n // Track which immediate children are affected (for child_* events)\n const affectedChildren = new Set<string>();\n let isFullSnapshot = false;\n\n // Apply all updates\n for (const { relativePath, value } of updates) {\n if (relativePath === '/') {\n // Full snapshot replacement\n view.setCache(value);\n isFullSnapshot = true;\n } else {\n // Extract immediate child key from the path\n const segments = relativePath.split('/').filter((s) => s.length > 0);\n if (segments.length > 0) {\n affectedChildren.add(segments[0]);\n }\n\n // Apply the update\n if (value === null) {\n view.removeCacheChild(relativePath);\n } else {\n view.updateCacheChild(relativePath, value);\n }\n }\n }\n\n // Capture current state\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n const currentCacheJson = JSON.stringify(view.getCache());\n\n // Skip ALL event firing if the cache hasn't actually changed\n // This prevents duplicate events when server confirms our optimistic write\n if (previousCacheJson === currentCacheJson) {\n return;\n }\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getCache();\n const snapshot = this.createSnapshot?.(view.path, fullValue, isVolatile, serverTimestamp);\n if (snapshot) {\n for (const entry of valueSubs) {\n try {\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in value subscription callback:', err);\n }\n }\n }\n }\n\n // Fire child_* events\n const childAddedSubs = view.getCallbacks('child_added');\n const childChangedSubs = view.getCallbacks('child_changed');\n const childRemovedSubs = view.getCallbacks('child_removed');\n const childMovedSubs = view.getCallbacks('child_moved');\n\n // Skip if no child subscriptions\n if (\n childAddedSubs.length === 0 &&\n childChangedSubs.length === 0 &&\n childRemovedSubs.length === 0 &&\n childMovedSubs.length === 0\n ) {\n return;\n }\n\n if (isFullSnapshot) {\n // Full snapshot - compare all children\n for (const key of currentOrder) {\n if (!previousChildSet.has(key)) {\n const prevKey = view.getPreviousChildKey(key);\n this.fireChildAdded(view, key, childAddedSubs, prevKey, isVolatile, serverTimestamp);\n }\n }\n for (const key of previousOrder) {\n if (!currentChildSet.has(key)) {\n this.fireChildRemoved(view, key, childRemovedSubs, isVolatile, serverTimestamp);\n }\n }\n } else {\n // Delta update - fire events for affected children\n for (const childKey of affectedChildren) {\n const wasPresent = previousChildSet.has(childKey);\n const isPresent = currentChildSet.has(childKey);\n\n if (!wasPresent && isPresent) {\n const prevKey = view.getPreviousChildKey(childKey);\n this.fireChildAdded(view, childKey, childAddedSubs, prevKey, isVolatile, serverTimestamp);\n } else if (wasPresent && !isPresent) {\n this.fireChildRemoved(view, childKey, childRemovedSubs, isVolatile, serverTimestamp);\n } else if (wasPresent && isPresent) {\n const prevKey = view.getPreviousChildKey(childKey);\n this.fireChildChanged(view, childKey, childChangedSubs, prevKey, isVolatile, serverTimestamp);\n }\n }\n }\n\n // Detect and fire child_moved events\n const previousPositions = new Map<string, number>();\n previousOrder.forEach((key, idx) => previousPositions.set(key, idx));\n const currentPositions = new Map<string, number>();\n currentOrder.forEach((key, idx) => currentPositions.set(key, idx));\n\n this.detectAndFireMoves(\n view,\n previousOrder,\n currentOrder,\n previousPositions,\n currentPositions,\n previousChildSet,\n currentChildSet,\n childMovedSubs,\n isVolatile,\n serverTimestamp\n );\n }\n\n // ============================================\n // Pending Writes (for local-first)\n // ============================================\n\n /**\n * Find all Views that cover a given write path.\n * A View covers a path if the View's path is an ancestor of or equal to the write path.\n */\n findViewsForWritePath(writePath: string): View[] {\n const normalized = normalizePath(writePath);\n const views: View[] = [];\n\n for (const [viewPath, view] of this.views) {\n // Check if viewPath is an ancestor of or equal to normalized\n if (normalized === viewPath) {\n views.push(view);\n } else if (normalized.startsWith(viewPath + '/')) {\n views.push(view);\n } else if (viewPath === '/') {\n views.push(view);\n }\n }\n\n return views;\n }\n\n /**\n * Get pending write IDs for a write path.\n * Returns the union of pending writes from all Views that cover this path.\n */\n getPendingWriteIdsForPath(writePath: string): string[] {\n const views = this.findViewsForWritePath(writePath);\n const allPendingIds = new Set<string>();\n\n for (const view of views) {\n for (const id of view.getPendingWriteIds()) {\n allPendingIds.add(id);\n }\n }\n\n return Array.from(allPendingIds);\n }\n\n /**\n * Track a pending write for all Views that cover the write path.\n */\n trackPendingWrite(writePath: string, requestId: string): void {\n const views = this.findViewsForWritePath(writePath);\n for (const view of views) {\n view.addPendingWrite(requestId);\n }\n }\n\n /**\n * Clear a pending write from all Views (on ack).\n */\n clearPendingWrite(requestId: string): void {\n for (const view of this.views.values()) {\n view.removePendingWrite(requestId);\n }\n }\n\n /**\n * Handle a nack for a pending write.\n * Collects all tainted request IDs and puts affected Views into recovery mode.\n * Returns the affected Views and all tainted request IDs for local rejection.\n */\n handleWriteNack(requestId: string): { affectedViews: View[]; taintedIds: string[] } {\n const affectedViews: View[] = [];\n const taintedIds = new Set<string>();\n\n // First pass: find affected Views and collect ALL their pending writes\n for (const view of this.views.values()) {\n if (view.isWritePending(requestId)) {\n affectedViews.push(view);\n // Collect all pending writes from this View - they're all tainted\n for (const id of view.getPendingWriteIds()) {\n taintedIds.add(id);\n }\n }\n }\n\n // Second pass: enter recovery (which clears pending writes)\n for (const view of affectedViews) {\n view.enterRecovery();\n }\n\n return { affectedViews, taintedIds: Array.from(taintedIds) };\n }\n\n /**\n * Check if any View covering a path is in recovery mode.\n */\n isPathInRecovery(writePath: string): boolean {\n const views = this.findViewsForWritePath(writePath);\n return views.some(view => view.recovering);\n }\n\n /**\n * Re-subscribe a specific View to get a fresh snapshot.\n * Used during recovery after a nack.\n */\n async resubscribeView(path: string): Promise<void> {\n const view = this.views.get(path);\n if (!view) return;\n\n const eventTypes = view.getEventTypes();\n if (eventTypes.length > 0 && this.sendSubscribe) {\n await this.sendSubscribe(path, eventTypes, view.queryParams ?? undefined);\n }\n }\n\n // ============================================\n // Optimistic Writes (for local-first)\n // ============================================\n\n /**\n * Apply an optimistic write to local cache and fire events.\n * Called before sending the write to the server.\n *\n * @param writePath - The absolute path being written to\n * @param value - The value to write (null for delete)\n * @param requestId - The request ID for pending write tracking\n * @param operation - The type of operation ('set', 'update', 'delete')\n * @returns The Views that were updated\n */\n applyOptimisticWrite(\n writePath: string,\n value: unknown,\n requestId: string,\n operation: 'set' | 'update' | 'delete'\n ): View[] {\n const normalized = normalizePath(writePath);\n const affectedViews = this.findViewsForWritePath(normalized);\n\n if (affectedViews.length === 0) {\n return [];\n }\n\n const updatedViews: View[] = [];\n\n for (const view of affectedViews) {\n // Skip Views that haven't received initial snapshot yet\n if (!view.hasReceivedInitialSnapshot) {\n continue;\n }\n\n // Calculate relative path from View's subscription path\n let relativePath: string;\n if (normalized === view.path) {\n relativePath = '/';\n } else if (view.path === '/') {\n relativePath = normalized;\n } else {\n relativePath = normalized.slice(view.path.length);\n }\n\n // Build updates array based on operation type\n const updates: Array<{ relativePath: string; value: unknown }> = [];\n\n if (operation === 'delete') {\n updates.push({ relativePath, value: null });\n } else if (operation === 'update') {\n // For update, value is a Record<string, unknown> with fields to merge\n const updateObj = value as Record<string, unknown>;\n for (const [key, val] of Object.entries(updateObj)) {\n const updatePath = relativePath === '/' ? '/' + key : relativePath + '/' + key;\n updates.push({ relativePath: updatePath, value: val });\n }\n } else {\n // set operation\n updates.push({ relativePath, value });\n }\n\n // Use shared helper for cache update and event firing\n this.applyWriteToView(view, updates, false, undefined);\n updatedViews.push(view);\n }\n\n return updatedViews;\n }\n}\n","/**\n * WebSocketClient - wrapper around WebSocket with event handling and reconnection support.\n * Works in both browser (native WebSocket) and Node.js (ws package).\n */\n\nimport WebSocketNode from 'ws';\n\n// Use native WebSocket in browser, ws package in Node.js\nconst WebSocketImpl: typeof WebSocket =\n typeof WebSocket !== 'undefined' ? WebSocket : (WebSocketNode as unknown as typeof WebSocket);\n\nexport type WebSocketState = 'disconnected' | 'connecting' | 'connected';\n\nexport interface WebSocketClientOptions {\n onMessage: (data: string) => void;\n onOpen: () => void;\n onClose: (code: number, reason: string) => void;\n onError: (error: Event) => void;\n}\n\nexport class WebSocketClient {\n private ws: WebSocket | null = null;\n private options: WebSocketClientOptions;\n private _state: WebSocketState = 'disconnected';\n\n constructor(options: WebSocketClientOptions) {\n this.options = options;\n }\n\n get state(): WebSocketState {\n return this._state;\n }\n\n get connected(): boolean {\n return this._state === 'connected';\n }\n\n /**\n * Connect to a WebSocket server.\n */\n connect(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this._state !== 'disconnected') {\n reject(new Error('Already connected or connecting'));\n return;\n }\n\n this._state = 'connecting';\n\n try {\n this.ws = new WebSocketImpl(url) as WebSocket;\n } catch (err) {\n this._state = 'disconnected';\n reject(err);\n return;\n }\n\n const onOpen = () => {\n cleanup();\n this._state = 'connected';\n this.setupEventHandlers();\n resolve();\n this.options.onOpen();\n };\n\n const onError = (event: Event) => {\n cleanup();\n this._state = 'disconnected';\n this.ws = null;\n reject(new Error('WebSocket connection failed'));\n this.options.onError(event);\n };\n\n const onClose = (event: CloseEvent) => {\n cleanup();\n this._state = 'disconnected';\n this.ws = null;\n reject(new Error(`WebSocket closed: ${event.code} ${event.reason}`));\n };\n\n const cleanup = () => {\n this.ws?.removeEventListener('open', onOpen);\n this.ws?.removeEventListener('error', onError);\n this.ws?.removeEventListener('close', onClose);\n };\n\n this.ws.addEventListener('open', onOpen);\n this.ws.addEventListener('error', onError);\n this.ws.addEventListener('close', onClose);\n });\n }\n\n /**\n * Set up persistent event handlers after connection is established.\n */\n private setupEventHandlers(): void {\n if (!this.ws) return;\n\n this.ws.addEventListener('message', (event: MessageEvent) => {\n this.options.onMessage(event.data as string);\n });\n\n this.ws.addEventListener('close', (event: CloseEvent) => {\n this._state = 'disconnected';\n this.ws = null;\n this.options.onClose(event.code, event.reason);\n });\n\n this.ws.addEventListener('error', (event: Event) => {\n this.options.onError(event);\n });\n }\n\n /**\n * Send a message over the WebSocket.\n */\n send(data: string): void {\n if (!this.ws || this._state !== 'connected') {\n throw new Error('WebSocket not connected');\n }\n this.ws.send(data);\n }\n\n /**\n * Close the WebSocket connection.\n */\n close(code: number = 1000, reason: string = 'Client disconnect'): void {\n if (this.ws) {\n this.ws.close(code, reason);\n this.ws = null;\n }\n this._state = 'disconnected';\n }\n}\n","/**\n * OnDisconnect - registers operations to perform on the server when this client disconnects.\n */\n\nimport type { LarkDatabase } from './LarkDatabase';\nimport { OnDisconnectAction } from './protocol/constants';\n\nexport class OnDisconnect {\n private readonly _db: LarkDatabase;\n private readonly _path: string;\n\n constructor(db: LarkDatabase, path: string) {\n this._db = db;\n this._path = path;\n }\n\n /**\n * Set a value when disconnected.\n */\n async set(value: unknown): Promise<void> {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.SET, value);\n }\n\n /**\n * Update values when disconnected.\n */\n async update(values: Record<string, unknown>): Promise<void> {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.UPDATE, values);\n }\n\n /**\n * Remove data when disconnected.\n */\n async remove(): Promise<void> {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.DELETE);\n }\n\n /**\n * Cancel any pending onDisconnect handlers at this location.\n */\n async cancel(): Promise<void> {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.CANCEL);\n }\n\n /**\n * Set a value with priority when disconnected.\n * Priority is injected as `.priority` into the value object.\n */\n async setWithPriority(value: unknown, priority: number | string): Promise<void> {\n // Priority can only be set on objects\n if (value === null || value === undefined) {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.SET, value);\n return;\n }\n\n if (typeof value !== 'object' || Array.isArray(value)) {\n throw new Error('Priority can only be set on object values');\n }\n\n // Inject .priority into the value\n const valueWithPriority = { ...(value as Record<string, unknown>), '.priority': priority };\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.SET, valueWithPriority);\n }\n}\n","/**\n * Push ID generation - Firebase-compatible chronologically-sortable IDs.\n *\n * Format: 20 characters\n * - First 8 chars: timestamp (milliseconds since epoch, base64-encoded)\n * - Last 12 chars: random suffix (incremented on same-millisecond calls)\n *\n * This ensures:\n * - IDs created later sort after IDs created earlier\n * - IDs created in the same millisecond are unique and still sort correctly\n */\n\nconst PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';\n\n// Persistent state for generating unique IDs within the same millisecond\nlet lastPushTime = 0;\nlet lastRandChars: number[] = [];\n\n/**\n * Generate a Firebase-compatible push ID.\n */\nexport function generatePushId(): string {\n let now = Date.now();\n const duplicateTime = now === lastPushTime;\n lastPushTime = now;\n\n // Encode timestamp in first 8 characters\n const timeChars = new Array(8);\n for (let i = 7; i >= 0; i--) {\n timeChars[i] = PUSH_CHARS.charAt(now % 64);\n now = Math.floor(now / 64);\n }\n\n let id = timeChars.join('');\n\n if (!duplicateTime) {\n // New timestamp - generate fresh random suffix\n lastRandChars = [];\n for (let i = 0; i < 12; i++) {\n lastRandChars.push(Math.floor(Math.random() * 64));\n }\n } else {\n // Same timestamp - increment the random suffix\n // This ensures uniqueness even with multiple calls in the same millisecond\n let i = 11;\n while (i >= 0 && lastRandChars[i] === 63) {\n lastRandChars[i] = 0;\n i--;\n }\n if (i >= 0) {\n lastRandChars[i]++;\n }\n }\n\n // Append random suffix\n for (let i = 0; i < 12; i++) {\n id += PUSH_CHARS.charAt(lastRandChars[i]);\n }\n\n return id;\n}\n","/**\n * DatabaseReference - a reference to a specific path in the database.\n * Immutable - navigation returns new references.\n */\n\nimport type { DataSnapshot } from './DataSnapshot';\nimport type { LarkDatabase } from './LarkDatabase';\nimport { LarkError } from './LarkError';\nimport { OnDisconnect } from './OnDisconnect';\nimport { ErrorCode, EventType } from './protocol/constants';\nimport { QueryParams, TxOperation } from './protocol/messages';\nimport { getKey, getParentPath, joinPath, normalizePath } from './utils/path';\nimport { generatePushId } from './utils/pushid';\n\n/** Result of a transaction operation */\nexport interface TransactionResult {\n /** Whether the transaction was committed (always true on success) */\n committed: boolean;\n /** Snapshot of the data after the transaction */\n snapshot: DataSnapshot;\n}\n\n/** Default maximum retries for optimistic transactions */\nconst DEFAULT_MAX_RETRIES = 25;\n\nexport type SnapshotCallback = (snapshot: DataSnapshot, previousChildKey?: string | null) => void;\n\nexport interface QueryState {\n orderBy?: 'key' | 'priority' | 'child' | 'value';\n orderByChildPath?: string;\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: { value: unknown; key?: string };\n endAt?: { value: unknown; key?: string };\n equalTo?: { value: unknown; key?: string };\n}\n\nexport class DatabaseReference {\n private readonly _db: LarkDatabase;\n private readonly _path: string;\n private readonly _query: QueryState;\n\n constructor(db: LarkDatabase, path: string, query: QueryState = {}) {\n this._db = db;\n this._path = normalizePath(path);\n this._query = query;\n }\n\n /**\n * The path of this reference.\n */\n get path(): string {\n return this._path;\n }\n\n /**\n * The last segment of the path (the \"key\"), or null for root.\n */\n get key(): string | null {\n return getKey(this._path);\n }\n\n /**\n * Get a reference to the parent node, or null if this is root.\n */\n get parent(): DatabaseReference | null {\n if (this._path === '/') return null;\n const parentPath = getParentPath(this._path);\n return new DatabaseReference(this._db, parentPath);\n }\n\n /**\n * Get a reference to the root of the database.\n */\n get root(): DatabaseReference {\n return new DatabaseReference(this._db, '');\n }\n\n // ============================================\n // Navigation\n // ============================================\n\n /**\n * Get a reference to a child path.\n */\n child(path: string): DatabaseReference {\n const childPath = joinPath(this._path, path);\n return new DatabaseReference(this._db, childPath);\n }\n\n // ============================================\n // Write Operations\n // ============================================\n\n /**\n * Set the data at this location, overwriting any existing data.\n */\n async set(value: unknown): Promise<void> {\n await this._db._sendSet(this._path, value);\n }\n\n /**\n * Update specific children at this location without overwriting other children.\n *\n * Also supports Firebase-style multi-path updates when keys look like paths\n * (start with '/'). In this mode, each path is written atomically as a transaction.\n *\n * @example\n * ```javascript\n * // Normal update (merge at single path)\n * await ref.update({ score: 10, name: 'Riley' });\n *\n * // Multi-path update (atomic writes to multiple paths)\n * await db.ref().update({\n * '/users/alice/score': 100,\n * '/users/bob/score': 200,\n * '/leaderboard/alice': null // null = delete\n * });\n * ```\n */\n async update(values: Record<string, unknown>): Promise<void> {\n // Check if any key looks like a path (starts with '/')\n const hasPathKeys = Object.keys(values).some((key) => key.startsWith('/'));\n\n if (hasPathKeys) {\n // Multi-path mode: use transaction for atomic writes\n const ops: TxOperation[] = [];\n\n for (const [key, value] of Object.entries(values)) {\n // Resolve path relative to this ref\n const fullPath = key.startsWith('/')\n ? joinPath(this._path, key)\n : joinPath(this._path, key);\n const normalizedPath = normalizePath(fullPath) || '/';\n\n if (value === null) {\n // null = delete\n ops.push({ o: 'd', p: normalizedPath });\n } else {\n // set the value (not merge, matching Firebase behavior)\n ops.push({ o: 's', p: normalizedPath, v: value });\n }\n }\n\n await this._db._sendTransaction(ops);\n } else {\n // Normal update (merge at single path)\n await this._db._sendUpdate(this._path, values);\n }\n }\n\n /**\n * Remove the data at this location.\n */\n async remove(): Promise<void> {\n await this._db._sendDelete(this._path);\n }\n\n /**\n * Generate a new child location with a unique key and optionally set its value.\n *\n * If value is provided, sets the value and returns a Promise that resolves\n * to the new reference.\n *\n * If no value is provided, returns a reference immediately with a client-generated\n * push key (you can then call set() on it).\n */\n push(value?: unknown): DatabaseReference | Promise<DatabaseReference> {\n // Always generate key locally (matches Firebase behavior)\n const key = generatePushId();\n const childRef = this.child(key);\n\n if (value === undefined) {\n // No value - return reference immediately\n return childRef;\n }\n\n // Value provided - set it and return promise\n return childRef.set(value).then(() => childRef);\n }\n\n /**\n * Set the data with a priority value for ordering.\n * Priority is injected as `.priority` into the value object.\n */\n async setWithPriority(value: unknown, priority: number | string): Promise<void> {\n // Priority can only be set on objects\n if (value === null || value === undefined) {\n // Setting null/undefined with priority - just set the value\n await this._db._sendSet(this._path, value);\n return;\n }\n\n if (typeof value !== 'object' || Array.isArray(value)) {\n throw new Error('Priority can only be set on object values');\n }\n\n // Inject .priority into the value\n const valueWithPriority = { ...(value as Record<string, unknown>), '.priority': priority };\n await this._db._sendSet(this._path, valueWithPriority);\n }\n\n /**\n * Set the priority of the data at this location.\n * Fetches current value and sets it with the new priority.\n */\n async setPriority(priority: number | string): Promise<void> {\n // Fetch current value and set with new priority\n const snapshot = await this.once();\n const currentVal = snapshot.val();\n\n if (currentVal === null || currentVal === undefined) {\n // No data exists, just set priority on empty object\n await this._db._sendSet(this._path, { '.priority': priority });\n return;\n }\n\n if (typeof currentVal !== 'object' || Array.isArray(currentVal)) {\n throw new Error('Priority can only be set on object values');\n }\n\n await this.setWithPriority(currentVal, priority);\n }\n\n /**\n * Atomically modify the data at this location using optimistic concurrency.\n *\n * The update function receives the current value and should return the new\n * value. If the data changed on the server before the write could be\n * committed, the function is called again with the new value, and the\n * process repeats until successful or the maximum retries are exceeded.\n *\n * @example\n * ```javascript\n * // Increment a counter atomically\n * const result = await ref.transaction(currentValue => {\n * return (currentValue || 0) + 1;\n * });\n * console.log('New value:', result.snapshot.val());\n * ```\n *\n * @param updateFunction - Function that receives current value and returns new value.\n * Return undefined to abort the transaction.\n * @param maxRetries - Maximum number of retries (default: 25)\n * @returns TransactionResult with committed status and final snapshot\n */\n async transaction(\n updateFunction: (currentValue: unknown) => unknown,\n maxRetries: number = DEFAULT_MAX_RETRIES\n ): Promise<TransactionResult> {\n let retries = 0;\n\n while (retries < maxRetries) {\n // Step 1: Read current value\n const currentSnapshot = await this.once();\n const currentValue = currentSnapshot.val();\n\n // Step 2: Call update function\n const newValue = updateFunction(currentValue);\n\n // Step 3: If undefined returned, abort\n if (newValue === undefined) {\n return {\n committed: false,\n snapshot: currentSnapshot,\n };\n }\n\n // Step 4: Build transaction with condition + set\n const ops: TxOperation[] = [\n { o: 'c', p: this._path, v: currentValue },\n { o: 's', p: this._path, v: newValue },\n ];\n\n try {\n // Step 5: Try to commit\n await this._db._sendTransaction(ops);\n\n // Success! Read back the value for the result snapshot\n const finalSnapshot = await this.once();\n return {\n committed: true,\n snapshot: finalSnapshot,\n };\n } catch (error) {\n // Step 6: If condition failed, retry\n if (error instanceof LarkError && error.code === ErrorCode.CONDITION_FAILED) {\n retries++;\n continue;\n }\n // Other errors should propagate\n throw error;\n }\n }\n\n // Max retries exceeded\n throw new LarkError(\n 'max_retries_exceeded',\n `Transaction failed after ${maxRetries} retries`\n );\n }\n\n // ============================================\n // Read Operations\n // ============================================\n\n /**\n * Read the data at this location once.\n */\n async once(eventType: 'value' = 'value'): Promise<DataSnapshot> {\n if (eventType !== 'value') {\n throw new Error('once() only supports \"value\" event type');\n }\n return this._db._sendOnce(this._path, this._buildQueryParams());\n }\n\n // ============================================\n // Subscriptions\n // ============================================\n\n /**\n * Subscribe to events at this location.\n * Returns an unsubscribe function.\n */\n on(eventType: EventType, callback: SnapshotCallback): () => void {\n return this._db._subscribe(this._path, eventType, callback, this._buildQueryParams());\n }\n\n /**\n * Unsubscribe from events.\n * If eventType is specified, removes all listeners of that type.\n * If no eventType, removes ALL listeners at this path.\n */\n off(eventType?: EventType): void {\n if (eventType) {\n this._db._unsubscribeEventType(this._path, eventType);\n } else {\n this._db._unsubscribeAll(this._path);\n }\n }\n\n // ============================================\n // OnDisconnect\n // ============================================\n\n /**\n * Get an OnDisconnect handler for this location.\n */\n onDisconnect(): OnDisconnect {\n return new OnDisconnect(this._db, this._path);\n }\n\n // ============================================\n // Query Modifiers\n // ============================================\n\n /**\n * Order results by key.\n */\n orderByKey(): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n orderBy: 'key',\n });\n }\n\n /**\n * Order results by priority.\n */\n orderByPriority(): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n orderBy: 'priority',\n });\n }\n\n /**\n * Order results by a child key.\n */\n orderByChild(path: string): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n orderBy: 'child',\n orderByChildPath: path,\n });\n }\n\n /**\n * Order results by value.\n */\n orderByValue(): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n orderBy: 'value',\n });\n }\n\n /**\n * Limit to the first N results.\n */\n limitToFirst(limit: number): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n limitToFirst: limit,\n });\n }\n\n /**\n * Limit to the last N results.\n */\n limitToLast(limit: number): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n limitToLast: limit,\n });\n }\n\n /**\n * Start at a specific value/key.\n */\n startAt(value: unknown, key?: string): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n startAt: { value, key },\n });\n }\n\n /**\n * End at a specific value/key.\n */\n endAt(value: unknown, key?: string): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n endAt: { value, key },\n });\n }\n\n /**\n * Filter to items equal to a specific value.\n */\n equalTo(value: unknown, key?: string): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n equalTo: { value, key },\n });\n }\n\n // ============================================\n // Internal Helpers\n // ============================================\n\n /**\n * Build query parameters for wire protocol.\n */\n private _buildQueryParams(): QueryParams | undefined {\n const params: QueryParams = {};\n let hasParams = false;\n\n // Order by\n if (this._query.orderBy === 'key') {\n params.orderBy = 'key';\n hasParams = true;\n } else if (this._query.orderBy === 'priority') {\n params.orderBy = 'priority';\n hasParams = true;\n } else if (this._query.orderBy === 'child') {\n params.orderBy = 'child';\n if (this._query.orderByChildPath) {\n params.orderByChild = this._query.orderByChildPath;\n }\n hasParams = true;\n } else if (this._query.orderBy === 'value') {\n params.orderBy = 'value';\n hasParams = true;\n }\n\n // Limits\n if (this._query.limitToFirst !== undefined) {\n params.limitToFirst = this._query.limitToFirst;\n hasParams = true;\n }\n\n if (this._query.limitToLast !== undefined) {\n params.limitToLast = this._query.limitToLast;\n hasParams = true;\n }\n\n // Range queries\n if (this._query.startAt !== undefined) {\n params.startAt = this._query.startAt.value;\n if (this._query.startAt.key !== undefined) {\n params.startAtKey = this._query.startAt.key;\n }\n hasParams = true;\n }\n\n if (this._query.endAt !== undefined) {\n params.endAt = this._query.endAt.value;\n if (this._query.endAt.key !== undefined) {\n params.endAtKey = this._query.endAt.key;\n }\n hasParams = true;\n }\n\n if (this._query.equalTo !== undefined) {\n params.equalTo = this._query.equalTo.value;\n if (this._query.equalTo.key !== undefined) {\n params.equalToKey = this._query.equalTo.key;\n }\n hasParams = true;\n }\n\n return hasParams ? params : undefined;\n }\n\n /**\n * Returns the absolute URL for this database location.\n * Format: https://db.lark.sh/project/database/path/to/data\n */\n toString(): string {\n const baseUrl = this._db._getBaseUrl();\n // For root, just return base URL; otherwise append path (path already has leading slash)\n if (this._path === '/') {\n return baseUrl;\n }\n return `${baseUrl}${this._path}`;\n }\n}\n","/**\n * DataSnapshot - an immutable snapshot of data at a location.\n * Received in event callbacks and from once() operations.\n *\n * Priority Handling:\n * - Priority is stored as a regular child value `.priority` in the data\n * - val() strips `.priority` from returned objects (so app code doesn't see it)\n * - getPriority() reads from the data's `.priority` field\n * - This matches Firebase behavior where priority is metadata, not user data\n */\n\nimport type { DatabaseReference } from './DatabaseReference';\nimport type { LarkDatabase } from './LarkDatabase';\nimport { getKey, getValueAtPath, joinPath, normalizePath } from './utils/path';\n\nexport class DataSnapshot {\n private readonly _data: unknown;\n private readonly _path: string;\n private readonly _db: LarkDatabase;\n private readonly _volatile: boolean;\n private readonly _serverTimestamp: number | null;\n\n constructor(\n data: unknown,\n path: string,\n db: LarkDatabase,\n options: { volatile?: boolean; serverTimestamp?: number | null } = {}\n ) {\n this._data = data;\n this._path = normalizePath(path);\n this._db = db;\n this._volatile = options.volatile ?? false;\n this._serverTimestamp = options.serverTimestamp ?? null;\n }\n\n /**\n * Get a DatabaseReference for the location of this snapshot.\n */\n get ref(): DatabaseReference {\n return this._db.ref(this._path);\n }\n\n /**\n * Get the last segment of the path (the \"key\"), or null for root.\n */\n get key(): string | null {\n return getKey(this._path);\n }\n\n /**\n * Get the data value, with `.priority` stripped if present.\n * Priority is internal metadata and should not be visible to app code.\n */\n val(): unknown {\n // Strip .priority from objects\n if (this._data && typeof this._data === 'object' && !Array.isArray(this._data)) {\n const data = this._data as Record<string, unknown>;\n if ('.priority' in data) {\n const { '.priority': _, ...rest } = data;\n // If the object only had .priority, return the original (empty object case)\n return Object.keys(rest).length > 0 ? rest : (Object.keys(data).length === 1 ? null : rest);\n }\n }\n return this._data;\n }\n\n /**\n * Check if data exists at this location (is not null/undefined).\n */\n exists(): boolean {\n return this._data !== null && this._data !== undefined;\n }\n\n /**\n * Get a child snapshot at the specified path.\n */\n child(path: string): DataSnapshot {\n const childPath = joinPath(this._path, path);\n const childData = getValueAtPath(this._data, path);\n return new DataSnapshot(childData, childPath, this._db, {\n volatile: this._volatile,\n serverTimestamp: this._serverTimestamp,\n });\n }\n\n /**\n * Check if this snapshot has any children.\n */\n hasChildren(): boolean {\n if (typeof this._data !== 'object' || this._data === null) {\n return false;\n }\n return Object.keys(this._data).length > 0;\n }\n\n /**\n * Check if this snapshot has a specific child.\n */\n hasChild(path: string): boolean {\n const childData = getValueAtPath(this._data, path);\n return childData !== undefined && childData !== null;\n }\n\n /**\n * Get the number of children.\n */\n numChildren(): number {\n if (typeof this._data !== 'object' || this._data === null) {\n return 0;\n }\n return Object.keys(this._data).length;\n }\n\n /**\n * Iterate over children. Return true from callback to stop iteration.\n */\n forEach(callback: (child: DataSnapshot) => boolean | void): void {\n if (typeof this._data !== 'object' || this._data === null) {\n return;\n }\n\n const keys = Object.keys(this._data);\n for (const key of keys) {\n const childSnap = this.child(key);\n if (callback(childSnap) === true) {\n break;\n }\n }\n }\n\n /**\n * Get the priority of the data at this location.\n * Priority is stored as `.priority` in the data object.\n */\n getPriority(): number | string | null {\n if (this._data && typeof this._data === 'object' && !Array.isArray(this._data)) {\n const priority = (this._data as Record<string, unknown>)['.priority'];\n if (typeof priority === 'number' || typeof priority === 'string') {\n return priority;\n }\n }\n return null;\n }\n\n /**\n * Check if this snapshot was from a volatile (high-frequency) update.\n * This is a Lark extension not present in Firebase.\n */\n isVolatile(): boolean {\n return this._volatile;\n }\n\n /**\n * Get the server timestamp for this snapshot (milliseconds since Unix epoch).\n * Only present on volatile value events. Use deltas between timestamps for\n * interpolation rather than absolute times to avoid clock sync issues.\n * This is a Lark extension not present in Firebase.\n */\n getServerTimestamp(): number | null {\n return this._serverTimestamp;\n }\n\n /**\n * Export the snapshot data as JSON (alias for val()).\n */\n toJSON(): unknown {\n return this._data;\n }\n}\n","/**\n * Simple JWT payload decoder (no verification - server validates).\n * JWTs are three base64url-encoded parts separated by dots: header.payload.signature\n */\n\nexport interface JwtPayload {\n sub: string; // User ID\n aud: string; // Database ID\n project: string; // Project ID\n server: string; // Server ID\n provider: string; // Auth provider\n claims: Record<string, unknown>; // Custom claims\n iss: string; // Issuer\n iat: number; // Issued at\n exp: number; // Expiration\n}\n\n/**\n * Decode a JWT payload without verification.\n * The server validates the token - we just need to read the claims.\n */\nexport function decodeJwtPayload(token: string): JwtPayload {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid JWT format');\n }\n\n const payload = parts[1];\n\n // Base64url decode (replace - with +, _ with /, add padding)\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n\n // Decode - works in both browser and Node.js\n let decoded: string;\n if (typeof atob === 'function') {\n // Browser\n decoded = atob(padded);\n } else {\n // Node.js\n decoded = Buffer.from(padded, 'base64').toString('utf-8');\n }\n\n return JSON.parse(decoded) as JwtPayload;\n}\n","/**\n * LarkDatabase - the main entry point for connecting to a Lark database.\n */\n\nimport { Coordinator } from './connection/Coordinator';\nimport { JoinCompleteResponse, MessageQueue } from './connection/MessageQueue';\nimport { PendingWriteManager } from './connection/PendingWriteManager';\nimport { SubscriptionManager, SnapshotCallback } from './connection/SubscriptionManager';\nimport { WebSocketClient } from './connection/WebSocketClient';\nimport { DatabaseReference } from './DatabaseReference';\nimport { DataSnapshot } from './DataSnapshot';\nimport { LarkError } from './LarkError';\nimport { DEFAULT_COORDINATOR_URL, EventType } from './protocol/constants';\nimport {\n ClientMessage,\n JoinMessage,\n ServerMessage,\n isAckMessage,\n isEventMessage,\n isNackMessage,\n isPingMessage,\n QueryParams,\n TxOperation,\n TransactionMessage,\n} from './protocol/messages';\nimport { decodeJwtPayload } from './utils/jwt';\nimport { normalizePath } from './utils/path';\n\nexport interface ConnectOptions {\n /** User's auth token (from your auth system) */\n token?: string;\n\n /** Connect anonymously (server assigns a UID) */\n anonymous?: boolean;\n\n /** Coordinator URL (default: https://db.lark.dev) */\n coordinator?: string;\n}\n\nexport interface AuthInfo {\n uid: string;\n provider: string;\n token: Record<string, unknown>;\n}\n\n// ============================================\n// Transaction Types (Public API)\n// ============================================\n\n/** A set operation in a transaction */\nexport interface TransactionSetOp {\n op: 'set';\n path: string;\n value: unknown;\n}\n\n/** An update (merge) operation in a transaction */\nexport interface TransactionUpdateOp {\n op: 'update';\n path: string;\n value: Record<string, unknown>;\n}\n\n/** A delete operation in a transaction */\nexport interface TransactionDeleteOp {\n op: 'delete';\n path: string;\n}\n\n/** A condition check in a transaction (for compare-and-swap) */\nexport interface TransactionConditionOp {\n op: 'condition';\n path: string;\n value: unknown;\n}\n\n/** A single operation in a transaction (array syntax) */\nexport type TransactionOp =\n | TransactionSetOp\n | TransactionUpdateOp\n | TransactionDeleteOp\n | TransactionConditionOp;\n\n/** Object syntax for transactions: path -> value (null = delete) */\nexport type TransactionObject = Record<string, unknown>;\n\ntype ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';\n\n// Reconnection constants\nconst RECONNECT_BASE_DELAY_MS = 1000; // Start with 1 second\nconst RECONNECT_MAX_DELAY_MS = 30000; // Cap at 30 seconds\nconst RECONNECT_JITTER_FACTOR = 0.5; // Add up to 50% random jitter\n\nexport class LarkDatabase {\n private _state: ConnectionState = 'disconnected';\n private _auth: AuthInfo | null = null;\n private _databaseId: string | null = null;\n private _coordinatorUrl: string | null = null;\n private _volatilePaths: string[] = [];\n\n // Reconnection state\n private _connectionId: string | null = null;\n private _connectOptions: ConnectOptions | null = null;\n private _intentionalDisconnect = false;\n private _reconnectAttempt = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n private ws: WebSocketClient | null = null;\n private messageQueue: MessageQueue;\n private subscriptionManager: SubscriptionManager;\n private pendingWrites: PendingWriteManager;\n\n // Event callbacks\n private connectCallbacks = new Set<() => void>();\n private disconnectCallbacks = new Set<() => void>();\n private errorCallbacks = new Set<(error: Error) => void>();\n private reconnectingCallbacks = new Set<() => void>();\n\n constructor() {\n this.messageQueue = new MessageQueue();\n this.subscriptionManager = new SubscriptionManager();\n this.pendingWrites = new PendingWriteManager();\n }\n\n // ============================================\n // Connection State\n // ============================================\n\n /**\n * Whether the database is currently connected.\n */\n get connected(): boolean {\n return this._state === 'connected';\n }\n\n /**\n * Whether the database is currently attempting to reconnect.\n */\n get reconnecting(): boolean {\n return this._state === 'reconnecting';\n }\n\n /**\n * Current connection state.\n */\n get state(): ConnectionState {\n return this._state;\n }\n\n /**\n * Current auth info, or null if not connected.\n */\n get auth(): AuthInfo | null {\n return this._auth;\n }\n\n /**\n * @internal Get the base URL for reference toString().\n */\n _getBaseUrl(): string {\n if (this._coordinatorUrl && this._databaseId) {\n return `${this._coordinatorUrl}/${this._databaseId}`;\n }\n return 'lark://';\n }\n\n /**\n * Get the volatile path patterns from the server.\n * These patterns indicate which paths should use unreliable transport.\n * WebSocket always uses reliable transport, but this is stored for future UDP support.\n */\n get volatilePaths(): string[] {\n return this._volatilePaths;\n }\n\n /**\n * Check if there are any pending writes waiting for acknowledgment.\n * Useful for showing \"saving...\" indicators in UI.\n */\n hasPendingWrites(): boolean {\n return this.pendingWrites.pendingCount > 0;\n }\n\n /**\n * Get the number of pending writes waiting for acknowledgment.\n */\n getPendingWriteCount(): number {\n return this.pendingWrites.pendingCount;\n }\n\n /**\n * Clear all pending writes.\n * Call this if you don't want to retry writes on reconnect.\n */\n clearPendingWrites(): void {\n this.pendingWrites.clear();\n }\n\n // ============================================\n // Connection Management\n // ============================================\n\n /**\n * Connect to a database.\n *\n * @param databaseId - Database ID in format \"project/database\"\n * @param options - Connection options (token, anonymous, coordinator URL)\n */\n async connect(databaseId: string, options: ConnectOptions = {}): Promise<void> {\n if (this._state !== 'disconnected') {\n throw new Error('Already connected or connecting');\n }\n\n // Store options for potential reconnect\n this._connectOptions = options;\n this._intentionalDisconnect = false;\n\n await this.performConnect(databaseId, options);\n }\n\n /**\n * Internal connect implementation used by both initial connect and reconnect.\n */\n private async performConnect(\n databaseId: string,\n options: ConnectOptions,\n isReconnect = false\n ): Promise<void> {\n const previousState = this._state;\n this._state = isReconnect ? 'reconnecting' : 'connecting';\n this._databaseId = databaseId;\n this._coordinatorUrl = options.coordinator || DEFAULT_COORDINATOR_URL;\n\n try {\n // Step 1: Get connection details from coordinator\n const coordinatorUrl = this._coordinatorUrl;\n const coordinator = new Coordinator(coordinatorUrl);\n\n const connectResponse = await coordinator.connect(databaseId, {\n token: options.token,\n anonymous: options.anonymous,\n });\n\n // Step 2: Connect to the server via WebSocket\n const wsUrl = connectResponse.ws_url;\n\n this.ws = new WebSocketClient({\n onMessage: this.handleMessage.bind(this),\n onOpen: () => {}, // Handled in connect flow\n onClose: this.handleClose.bind(this),\n onError: this.handleError.bind(this),\n });\n\n await this.ws.connect(wsUrl);\n\n // Step 3: Send join message (include previous connection ID if reconnecting)\n const requestId = this.messageQueue.nextRequestId();\n const joinMessage: JoinMessage = {\n o: 'j',\n t: connectResponse.token,\n r: requestId,\n };\n\n // Include previous connection ID for reconnect deduplication\n if (this._connectionId) {\n joinMessage.pcid = this._connectionId;\n }\n\n this.send(joinMessage);\n\n // Wait for join complete (returns volatile paths and connection ID)\n const joinResponse = (await this.messageQueue.registerRequest(requestId)) as JoinCompleteResponse;\n this._volatilePaths = joinResponse.volatilePaths;\n this._connectionId = joinResponse.connectionId;\n\n // Step 4: Extract auth info from JWT and update state\n const jwtPayload = decodeJwtPayload(connectResponse.token);\n this._auth = {\n uid: jwtPayload.sub,\n provider: jwtPayload.provider,\n token: jwtPayload.claims || {},\n };\n\n this._state = 'connected';\n this._reconnectAttempt = 0; // Reset reconnect attempt counter on success\n\n // Initialize subscription manager (only on first connect)\n if (!isReconnect) {\n this.subscriptionManager.initialize({\n sendSubscribe: this.sendSubscribeMessage.bind(this),\n sendUnsubscribe: this.sendUnsubscribeMessage.bind(this),\n createSnapshot: this.createSnapshot.bind(this),\n });\n }\n\n // On reconnect, restore subscriptions and retry pending writes\n if (isReconnect) {\n await this.restoreAfterReconnect();\n }\n\n // Notify listeners\n this.connectCallbacks.forEach((cb) => cb());\n } catch (error) {\n // On reconnect failure, schedule another attempt\n if (isReconnect) {\n this._state = 'reconnecting';\n this.scheduleReconnect();\n return;\n }\n\n // On initial connect failure, clean up completely\n this._state = 'disconnected';\n this._auth = null;\n this._databaseId = null;\n this._connectOptions = null;\n this._connectionId = null;\n this.ws?.close();\n this.ws = null;\n throw error;\n }\n }\n\n /**\n * Disconnect from the database.\n * This triggers onDisconnect hooks on the server.\n */\n async disconnect(): Promise<void> {\n if (this._state === 'disconnected') {\n return;\n }\n\n const wasConnected = this._state === 'connected';\n\n // Mark as intentional to prevent auto-reconnect\n this._intentionalDisconnect = true;\n\n // Cancel any pending reconnect\n if (this._reconnectTimer) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n\n // Send leave message for graceful disconnect\n if (wasConnected && this.ws) {\n try {\n const requestId = this.messageQueue.nextRequestId();\n this.send({ o: 'l', r: requestId });\n // Wait briefly for ack, but don't block\n await Promise.race([\n this.messageQueue.registerRequest(requestId),\n new Promise((resolve) => setTimeout(resolve, 1000)),\n ]);\n } catch {\n // Ignore errors during disconnect\n }\n }\n\n this.cleanupFull();\n\n // Fire disconnect callbacks\n if (wasConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n }\n\n /**\n * Full cleanup - clears all state including subscriptions.\n * Used for intentional disconnect.\n */\n private cleanupFull(): void {\n this.ws?.close();\n this.ws = null;\n this._state = 'disconnected';\n this._auth = null;\n this._databaseId = null;\n this._volatilePaths = [];\n this._coordinatorUrl = null;\n this._connectionId = null;\n this._connectOptions = null;\n this._reconnectAttempt = 0;\n this.subscriptionManager.clear();\n this.messageQueue.rejectAll(new Error('Connection closed'));\n this.pendingWrites.clear();\n }\n\n /**\n * Partial cleanup - preserves state needed for reconnect.\n * Used for unexpected disconnect.\n */\n private cleanupForReconnect(): void {\n this.ws?.close();\n this.ws = null;\n this._auth = null;\n // Keep: _databaseId, _coordinatorUrl, _connectionId, _connectOptions\n // Keep: subscriptionManager subscriptions (but clear cache)\n // Keep: pendingWrites\n this.subscriptionManager.clearCacheOnly();\n this.messageQueue.rejectAll(new Error('Connection closed'));\n }\n\n /**\n * Schedule a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(): void {\n this._reconnectAttempt++;\n\n // Calculate delay with exponential backoff and jitter\n const baseDelay = Math.min(\n RECONNECT_BASE_DELAY_MS * Math.pow(2, this._reconnectAttempt - 1),\n RECONNECT_MAX_DELAY_MS\n );\n const jitter = baseDelay * RECONNECT_JITTER_FACTOR * Math.random();\n const delay = baseDelay + jitter;\n\n this._reconnectTimer = setTimeout(() => {\n this._reconnectTimer = null;\n this.attemptReconnect();\n }, delay);\n }\n\n /**\n * Attempt to reconnect to the database.\n */\n private async attemptReconnect(): Promise<void> {\n if (this._intentionalDisconnect || !this._databaseId || !this._connectOptions) {\n return;\n }\n\n try {\n await this.performConnect(this._databaseId, this._connectOptions, true);\n } catch {\n // performConnect already handles scheduling next attempt on failure\n }\n }\n\n /**\n * Restore subscriptions and retry pending writes after reconnect.\n */\n private async restoreAfterReconnect(): Promise<void> {\n // Re-subscribe to all active subscriptions\n await this.subscriptionManager.resubscribeAll();\n\n // Retry all pending writes\n await this.retryPendingWrites();\n }\n\n /**\n * Retry all pending writes after reconnect.\n */\n private async retryPendingWrites(): Promise<void> {\n const pendingWrites = this.pendingWrites.getPendingWrites();\n\n for (const write of pendingWrites) {\n try {\n // Re-send the write with the same request ID\n // Server will deduplicate if already processed\n switch (write.operation) {\n case 'set': {\n const message: ClientMessage = {\n o: 's',\n p: write.path,\n v: write.value,\n r: write.oid, // Use original request ID\n };\n this.send(message);\n break;\n }\n case 'update': {\n const message: ClientMessage = {\n o: 'u',\n p: write.path,\n v: write.value as Record<string, unknown>,\n r: write.oid,\n };\n this.send(message);\n break;\n }\n case 'delete': {\n const message: ClientMessage = {\n o: 'd',\n p: write.path,\n r: write.oid,\n };\n this.send(message);\n break;\n }\n case 'transaction': {\n const message: TransactionMessage = {\n o: 'tx',\n ops: write.value as TxOperation[],\n r: write.oid,\n };\n this.send(message);\n break;\n }\n }\n } catch (error) {\n // If we fail to send, the connection might have dropped again\n // The next reconnect will retry\n console.error('Failed to retry pending write:', error);\n }\n }\n }\n\n // ============================================\n // Reference Access\n // ============================================\n\n /**\n * Get a reference to a path in the database.\n */\n ref(path: string = ''): DatabaseReference {\n return new DatabaseReference(this, path);\n }\n\n // ============================================\n // Transactions\n // ============================================\n\n /**\n * Execute an atomic transaction with multiple operations.\n *\n * Supports two syntaxes:\n *\n * **Object syntax** (like Firebase multi-path update):\n * ```javascript\n * await db.transaction({\n * '/users/alice/name': 'Alice',\n * '/users/alice/score': 100,\n * '/temp/data': null // null = delete\n * });\n * ```\n *\n * **Array syntax** (explicit operations):\n * ```javascript\n * await db.transaction([\n * { op: 'set', path: '/users/alice/name', value: 'Alice' },\n * { op: 'update', path: '/metadata', value: { lastUpdated: '...' } },\n * { op: 'delete', path: '/temp/data' },\n * { op: 'condition', path: '/counter', value: 5 } // CAS check\n * ]);\n * ```\n *\n * All operations are atomic: either all succeed or all fail.\n * Conditions are checked first; if any fail, the transaction is rejected\n * with error code 'condition_failed'.\n */\n async transaction(operations: TransactionOp[] | TransactionObject): Promise<void> {\n let txOps: TxOperation[];\n\n if (Array.isArray(operations)) {\n // Array syntax - convert to wire format\n txOps = operations.map((op) => this.convertToTxOp(op));\n } else {\n // Object syntax - convert paths to set/delete operations\n txOps = this.convertObjectToTxOps(operations);\n }\n\n await this._sendTransaction(txOps);\n }\n\n /**\n * Convert a public TransactionOp to wire format TxOperation.\n */\n private convertToTxOp(op: TransactionOp): TxOperation {\n const path = normalizePath(op.path) || '/';\n\n switch (op.op) {\n case 'set':\n return { o: 's', p: path, v: op.value };\n case 'update':\n return { o: 'u', p: path, v: op.value };\n case 'delete':\n return { o: 'd', p: path };\n case 'condition':\n return { o: 'c', p: path, v: op.value };\n default:\n throw new Error(`Unknown transaction operation: ${(op as TransactionOp).op}`);\n }\n }\n\n /**\n * Convert object syntax to wire format TxOperations.\n * Each path becomes a set operation, null values become deletes.\n */\n private convertObjectToTxOps(obj: TransactionObject): TxOperation[] {\n const ops: TxOperation[] = [];\n\n for (const [path, value] of Object.entries(obj)) {\n const normalizedPath = normalizePath(path) || '/';\n\n if (value === null) {\n // null = delete\n ops.push({ o: 'd', p: normalizedPath });\n } else {\n // set the value\n ops.push({ o: 's', p: normalizedPath, v: value });\n }\n }\n\n return ops;\n }\n\n /**\n * @internal Send a transaction to the server.\n */\n async _sendTransaction(ops: TxOperation[]): Promise<void> {\n const requestId = this.messageQueue.nextRequestId();\n\n // Track the pending write using request ID (store ops as value for potential retry)\n this.pendingWrites.trackWrite(requestId, 'transaction', '/', ops);\n\n const message: TransactionMessage = {\n o: 'tx',\n ops,\n r: requestId,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n // ============================================\n // Connection Events\n // ============================================\n\n /**\n * Register a callback for when connection is established.\n * Returns an unsubscribe function.\n */\n onConnect(callback: () => void): () => void {\n this.connectCallbacks.add(callback);\n return () => this.connectCallbacks.delete(callback);\n }\n\n /**\n * Register a callback for when connection is lost.\n * Returns an unsubscribe function.\n */\n onDisconnect(callback: () => void): () => void {\n this.disconnectCallbacks.add(callback);\n return () => this.disconnectCallbacks.delete(callback);\n }\n\n /**\n * Register a callback for connection errors.\n * Returns an unsubscribe function.\n */\n onError(callback: (error: Error) => void): () => void {\n this.errorCallbacks.add(callback);\n return () => this.errorCallbacks.delete(callback);\n }\n\n /**\n * Register a callback for when reconnection attempts begin.\n * This fires when an unexpected disconnect occurs and auto-reconnect starts.\n * Returns an unsubscribe function.\n */\n onReconnecting(callback: () => void): () => void {\n this.reconnectingCallbacks.add(callback);\n return () => this.reconnectingCallbacks.delete(callback);\n }\n\n // ============================================\n // Internal: Message Handling\n // ============================================\n\n private handleMessage(data: string): void {\n let message: ServerMessage;\n try {\n message = JSON.parse(data) as ServerMessage;\n } catch {\n console.error('Failed to parse message:', data);\n return;\n }\n\n // Handle ping - respond with pong immediately\n if (isPingMessage(message)) {\n this.ws?.send(JSON.stringify({ o: 'po' }));\n return;\n }\n\n // Clear pending writes on ack/nack using request ID\n if (isAckMessage(message)) {\n this.pendingWrites.onAck(message.a);\n // Also clear from View-level pending write tracking\n this.subscriptionManager.clearPendingWrite(message.a);\n } else if (isNackMessage(message)) {\n this.pendingWrites.onNack(message.n);\n\n // Skip recovery for condition_failed (handled by transaction retry)\n if (message.e !== 'condition_failed') {\n // Log the error\n console.error(`Write failed (${message.e}): ${message.m || message.e}`);\n\n // Handle nack: collect tainted IDs and put affected Views into recovery mode\n const { affectedViews, taintedIds } = this.subscriptionManager.handleWriteNack(message.n);\n\n if (affectedViews.length > 0) {\n // Reject all tainted writes locally (except the original nack, which is handled by messageQueue below)\n for (const id of taintedIds) {\n if (id !== message.n) {\n this.messageQueue.rejectLocally(\n id,\n new LarkError('write_tainted', 'Write cancelled: depends on failed write')\n );\n // Also clear from pending writes manager\n this.pendingWrites.onNack(id);\n }\n }\n\n // Re-subscribe affected Views to get fresh snapshots\n for (const view of affectedViews) {\n this.subscriptionManager.resubscribeView(view.path).catch((err) => {\n console.error(`Failed to re-subscribe ${view.path} during recovery:`, err);\n });\n }\n }\n } else {\n // For condition_failed, just clear the pending write\n this.subscriptionManager.clearPendingWrite(message.n);\n }\n }\n\n // Check if this is a response to a pending request\n if (this.messageQueue.handleMessage(message)) {\n return;\n }\n\n // Otherwise, check if it's an event\n if (isEventMessage(message)) {\n this.subscriptionManager.handleEvent(message);\n }\n }\n\n private handleClose(code: number, reason: string): void {\n // Already disconnected (e.g., through disconnect() method) - nothing to do\n if (this._state === 'disconnected') {\n return;\n }\n\n const wasConnected = this._state === 'connected';\n const wasReconnecting = this._state === 'reconnecting';\n\n // Check if this was an intentional disconnect\n // (disconnect() already handled cleanup and callbacks, but the ws close event\n // may fire later - just clean up any remaining state)\n if (this._intentionalDisconnect) {\n this.cleanupFull();\n if (wasConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n return;\n }\n\n // Only attempt to reconnect if we have the necessary state\n // (databaseId and connectOptions from a previous successful connection)\n const canReconnect = this._databaseId && this._connectOptions;\n\n // Unexpected disconnect - attempt to reconnect if we can\n if ((wasConnected || wasReconnecting) && canReconnect) {\n this._state = 'reconnecting';\n this.cleanupForReconnect();\n\n // Notify listeners that we're reconnecting\n this.reconnectingCallbacks.forEach((cb) => cb());\n if (wasConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n\n // Schedule reconnection\n this.scheduleReconnect();\n } else {\n // Can't reconnect - do full cleanup\n this.cleanupFull();\n if (wasConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n }\n }\n\n private handleError(event: Event): void {\n const error = new Error('WebSocket error');\n this.errorCallbacks.forEach((cb) => cb(error));\n }\n\n // ============================================\n // Internal: Sending Messages\n // ============================================\n\n private send(message: ClientMessage): void {\n if (!this.ws || !this.ws.connected) {\n throw new LarkError('not_connected', 'Not connected to database');\n }\n this.ws.send(JSON.stringify(message));\n }\n\n /**\n * @internal Send a set operation.\n * Note: Priority is now part of the value (as .priority), not a separate field.\n */\n async _sendSet(path: string, value: unknown): Promise<void> {\n const normalizedPath = normalizePath(path) || '/';\n\n // Check if any affected View is recovering - reject writes during recovery\n if (this.subscriptionManager.isPathInRecovery(normalizedPath)) {\n throw new LarkError('view_recovering', 'Cannot write: View is recovering from failed write');\n }\n\n const requestId = this.messageQueue.nextRequestId();\n\n // Get pending write IDs for this path (from subscribed Views)\n const pendingWriteIds = this.subscriptionManager.getPendingWriteIdsForPath(normalizedPath);\n\n // Track the pending write using request ID\n this.pendingWrites.trackWrite(requestId, 'set', normalizedPath, value);\n\n // Also track in SubscriptionManager for View-level pending write tracking\n this.subscriptionManager.trackPendingWrite(normalizedPath, requestId);\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, value, requestId, 'set');\n\n const message: ClientMessage = {\n o: 's',\n p: normalizedPath,\n v: value,\n r: requestId,\n pw: pendingWriteIds.length > 0 ? pendingWriteIds : undefined,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send an update operation.\n */\n async _sendUpdate(path: string, values: Record<string, unknown>): Promise<void> {\n const normalizedPath = normalizePath(path) || '/';\n\n // Check if any affected View is recovering - reject writes during recovery\n if (this.subscriptionManager.isPathInRecovery(normalizedPath)) {\n throw new LarkError('view_recovering', 'Cannot write: View is recovering from failed write');\n }\n\n const requestId = this.messageQueue.nextRequestId();\n\n // Get pending write IDs for this path (from subscribed Views)\n const pendingWriteIds = this.subscriptionManager.getPendingWriteIdsForPath(normalizedPath);\n\n // Track the pending write using request ID\n this.pendingWrites.trackWrite(requestId, 'update', normalizedPath, values);\n\n // Also track in SubscriptionManager for View-level pending write tracking\n this.subscriptionManager.trackPendingWrite(normalizedPath, requestId);\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, values, requestId, 'update');\n\n const message: ClientMessage = {\n o: 'u',\n p: normalizedPath,\n v: values,\n r: requestId,\n pw: pendingWriteIds.length > 0 ? pendingWriteIds : undefined,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send a delete operation.\n */\n async _sendDelete(path: string): Promise<void> {\n const normalizedPath = normalizePath(path) || '/';\n\n // Check if any affected View is recovering - reject writes during recovery\n if (this.subscriptionManager.isPathInRecovery(normalizedPath)) {\n throw new LarkError('view_recovering', 'Cannot write: View is recovering from failed write');\n }\n\n const requestId = this.messageQueue.nextRequestId();\n\n // Get pending write IDs for this path (from subscribed Views)\n const pendingWriteIds = this.subscriptionManager.getPendingWriteIdsForPath(normalizedPath);\n\n // Track the pending write using request ID\n this.pendingWrites.trackWrite(requestId, 'delete', normalizedPath);\n\n // Also track in SubscriptionManager for View-level pending write tracking\n this.subscriptionManager.trackPendingWrite(normalizedPath, requestId);\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, null, requestId, 'delete');\n\n const message: ClientMessage = {\n o: 'd',\n p: normalizedPath,\n r: requestId,\n pw: pendingWriteIds.length > 0 ? pendingWriteIds : undefined,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send a once (read) operation.\n *\n * This method first checks if the data is available in the local cache\n * (from an active subscription). If so, it returns the cached value\n * immediately without a server round-trip.\n *\n * Cache is only used when:\n * - No query parameters are specified (queries may filter/order differently)\n * - The path is covered by an active 'value' subscription\n * - We have cached data available\n */\n async _sendOnce(path: string, query?: QueryParams): Promise<DataSnapshot> {\n const normalizedPath = normalizePath(path) || '/';\n\n // Try cache first (only for non-query requests)\n if (!query) {\n const cached = this.subscriptionManager.getCachedValue(normalizedPath);\n if (cached.found) {\n return new DataSnapshot(cached.value, path, this);\n }\n }\n\n // Cache miss or query specified - fetch from server\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'o',\n p: normalizedPath,\n r: requestId,\n // Spread query params at top level (not nested in 'q')\n ...query,\n };\n this.send(message);\n const value = await this.messageQueue.registerRequest(requestId);\n return new DataSnapshot(value, path, this);\n }\n\n /**\n * @internal Send an onDisconnect operation.\n */\n async _sendOnDisconnect(\n path: string,\n action: string,\n value?: unknown\n ): Promise<void> {\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'od',\n p: normalizePath(path) || '/',\n a: action as 's' | 'u' | 'd' | 'c',\n r: requestId,\n };\n if (value !== undefined) {\n message.v = value;\n }\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send a subscribe message to server.\n */\n private async sendSubscribeMessage(path: string, eventTypes: string[], queryParams?: QueryParams): Promise<void> {\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'sb',\n p: normalizePath(path) || '/',\n e: eventTypes,\n r: requestId,\n ...queryParams,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send an unsubscribe message to server.\n */\n private async sendUnsubscribeMessage(path: string): Promise<void> {\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'us',\n p: normalizePath(path) || '/',\n r: requestId,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Create a DataSnapshot from event data.\n */\n private createSnapshot(\n path: string,\n value: unknown,\n volatile: boolean,\n serverTimestamp?: number\n ): DataSnapshot {\n return new DataSnapshot(value, path, this, {\n volatile,\n serverTimestamp: serverTimestamp ?? null,\n });\n }\n\n // ============================================\n // Internal: Subscription Management\n // ============================================\n\n /**\n * @internal Subscribe to events at a path.\n */\n _subscribe(path: string, eventType: EventType, callback: SnapshotCallback, queryParams?: QueryParams): () => void {\n return this.subscriptionManager.subscribe(path, eventType, callback, queryParams);\n }\n\n /**\n * @internal Unsubscribe from a specific event type at a path.\n */\n _unsubscribeEventType(path: string, eventType: EventType): void {\n this.subscriptionManager.unsubscribeEventType(path, eventType);\n }\n\n /**\n * @internal Unsubscribe from all events at a path.\n */\n _unsubscribeAll(path: string): void {\n this.subscriptionManager.unsubscribeAll(path);\n }\n}\n","/**\n * Volatile path matching utilities.\n *\n * Volatile paths are patterns where a wildcard matches exactly one path segment.\n * These paths use unreliable transport for better performance (UDP when available).\n */\n\n/**\n * Check if a path matches any of the volatile path patterns.\n *\n * Pattern matching rules:\n * - Wildcard matches exactly one path segment (not zero, not multiple)\n * - Patterns and paths are compared segment by segment\n * - Path must have the same number of segments as the pattern\n *\n * @param path - The path to check (e.g., \"/players/abc/position\")\n * @param patterns - Array of volatile patterns with wildcards\n * @returns true if the path matches any pattern\n */\nexport function isVolatilePath(path: string, patterns: string[] | null | undefined): boolean {\n if (!patterns || patterns.length === 0) {\n return false;\n }\n\n // Remove leading slash and split into segments\n const segments = path.replace(/^\\//, '').split('/');\n\n return patterns.some((pattern) => {\n const patternSegments = pattern.split('/');\n\n // Must have same number of segments\n if (segments.length !== patternSegments.length) {\n return false;\n }\n\n // Each segment must match (or pattern segment is wildcard)\n return patternSegments.every((p, i) => p === '*' || p === segments[i]);\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/protocol/constants.ts","../src/connection/Coordinator.ts","../src/LarkError.ts","../src/protocol/messages.ts","../src/connection/MessageQueue.ts","../src/connection/PendingWriteManager.ts","../src/utils/path.ts","../src/utils/sort.ts","../src/connection/View.ts","../src/connection/SubscriptionManager.ts","../src/connection/WebSocketClient.ts","../src/OnDisconnect.ts","../src/utils/pushid.ts","../src/DatabaseReference.ts","../src/DataSnapshot.ts","../src/utils/jwt.ts","../src/utils/volatile.ts","../src/LarkDatabase.ts"],"sourcesContent":["/**\n * Wire protocol constants.\n * Keys are shortened for bandwidth efficiency.\n */\n\n// Client -> Server operations\nexport const Op = {\n JOIN: 'j',\n SET: 's',\n UPDATE: 'u',\n DELETE: 'd',\n PUSH: 'p',\n SUBSCRIBE: 'sb',\n UNSUBSCRIBE: 'us',\n ONCE: 'o',\n ON_DISCONNECT: 'od',\n LEAVE: 'l',\n TRANSACTION: 'tx',\n} as const;\n\n// Transaction operation types (used within tx ops array)\nexport const TxOp = {\n SET: 's',\n UPDATE: 'u',\n DELETE: 'd',\n CONDITION: 'c',\n} as const;\n\nexport type TxOpType = (typeof TxOp)[keyof typeof TxOp];\n\n// OnDisconnect actions\nexport const OnDisconnectAction = {\n SET: 's',\n UPDATE: 'u',\n DELETE: 'd',\n CANCEL: 'c',\n} as const;\n\n// Server -> Client event types (wire format)\n// Server now sends 'put' and 'patch' events; client generates child_* events locally\nexport const ServerEventType = {\n PUT: 'put',\n PATCH: 'patch',\n} as const;\n\nexport type ServerEventTypeValue = (typeof ServerEventType)[keyof typeof ServerEventType];\n\n// Client-side event types (API-facing)\n// These are generated by the client from put/patch events\nexport type EventType = 'value' | 'child_added' | 'child_changed' | 'child_removed' | 'child_moved';\n\n// Map client event types to short form for subscribe message\nexport const eventTypeToShort: Record<EventType, string> = {\n value: 'v',\n child_added: 'ca',\n child_changed: 'cc',\n child_removed: 'cr',\n child_moved: 'cm',\n};\n\n// Error codes\nexport const ErrorCode = {\n PERMISSION_DENIED: 'permission_denied',\n INVALID_DATA: 'invalid_data',\n NOT_FOUND: 'not_found',\n INVALID_PATH: 'invalid_path',\n INVALID_OPERATION: 'invalid_operation',\n INTERNAL_ERROR: 'internal_error',\n CONDITION_FAILED: 'condition_failed',\n} as const;\n\nexport type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n// Default coordinator URL\nexport const DEFAULT_COORDINATOR_URL = 'https://db.lark.sh';\n","/**\n * Coordinator client - handles HTTP requests to the coordinator service\n * to get connection details for a specific database.\n */\n\nimport { DEFAULT_COORDINATOR_URL } from '../protocol/constants';\n\nexport interface ConnectRequest {\n database: string;\n token?: string;\n anonymous?: boolean;\n}\n\nexport interface ConnectResponse {\n ws_url: string; // Full WebSocket URL (e.g., \"wss://smash-1.larksh.com/ws\")\n udp_host: string; // Direct IP for UDP connections\n udp_port: number; // UDP port\n token: string; // Connection JWT to use with the server\n}\n\nexport interface CoordinatorError {\n error: string;\n message: string;\n}\n\nexport class Coordinator {\n private readonly baseUrl: string;\n\n constructor(baseUrl: string = DEFAULT_COORDINATOR_URL) {\n // Remove trailing slash if present\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n }\n\n /**\n * Request connection details from the coordinator.\n *\n * @param database - Database ID in format \"project/database\"\n * @param options - Either a user token or anonymous flag\n * @returns Connection details including host, port, and connection token\n */\n async connect(\n database: string,\n options: { token?: string; anonymous?: boolean } = {}\n ): Promise<ConnectResponse> {\n const body: ConnectRequest = { database };\n\n if (options.token) {\n body.token = options.token;\n } else if (options.anonymous) {\n body.anonymous = true;\n } else {\n // Default to anonymous if nothing specified\n body.anonymous = true;\n }\n\n const response = await fetch(`${this.baseUrl}/connect`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n let errorMessage = `Coordinator request failed: ${response.status}`;\n try {\n const errorBody = (await response.json()) as CoordinatorError;\n if (errorBody.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n // Ignore JSON parse errors\n }\n throw new Error(errorMessage);\n }\n\n const data = (await response.json()) as ConnectResponse;\n return data;\n }\n}\n","/**\n * LarkError - custom error class for Lark operations.\n *\n * Standard error codes:\n * - `permission_denied` - Security rules rejected operation\n * - `invalid_data` - Malformed data\n * - `not_found` - Path doesn't exist\n * - `invalid_path` - Invalid path format\n * - `timeout` - Request timed out\n * - `not_connected` - Operation attempted while disconnected\n * - `condition_failed` - Transaction condition check failed (CAS mismatch)\n * - `max_retries_exceeded` - Optimistic transaction exceeded retry limit\n * - `write_tainted` - Write rejected locally because it depended on a failed write\n * - `view_recovering` - Write rejected because View is recovering from failed write\n */\n\nexport class LarkError extends Error {\n readonly code: string;\n\n constructor(code: string, message?: string) {\n super(message || code);\n this.code = code;\n this.name = 'LarkError';\n\n // Maintain proper stack trace in V8 environments\n const ErrorWithCapture = Error as typeof Error & {\n captureStackTrace?: (target: object, constructor: Function) => void;\n };\n if (ErrorWithCapture.captureStackTrace) {\n ErrorWithCapture.captureStackTrace(this, LarkError);\n }\n }\n}\n","/**\n * Wire protocol message types.\n * All communication uses JSON over WebSocket with shortened keys.\n */\n\nimport { EventType } from './constants';\n\n// ============================================\n// Client -> Server Messages\n// ============================================\n\nexport interface JoinMessage {\n o: 'j'; // operation: join\n t: string; // token (JWT from coordinator)\n r: string; // request ID\n pcid?: string; // previous connection ID (for reconnect deduplication)\n}\n\nexport interface SetMessage {\n o: 's'; // operation: set\n p: string; // path\n v: unknown; // value (priority is included as .priority if set)\n r?: string; // request ID (optional for volatile/fire-and-forget writes)\n pw?: string[]; // pending write IDs for same View (for local-first recovery)\n}\n\nexport interface UpdateMessage {\n o: 'u'; // operation: update\n p: string; // path\n v: Record<string, unknown>; // values to merge\n r?: string; // request ID (optional for volatile/fire-and-forget writes)\n pw?: string[]; // pending write IDs for same View (for local-first recovery)\n}\n\nexport interface DeleteMessage {\n o: 'd'; // operation: delete\n p: string; // path\n r?: string; // request ID (optional for volatile/fire-and-forget writes)\n pw?: string[]; // pending write IDs for same View (for local-first recovery)\n}\n\nexport interface SubscribeMessage {\n o: 'sb'; // operation: subscribe\n p: string; // path\n e: string[]; // event types (short form)\n r: string; // request ID\n // Query parameters (optional) - at top level per VIEW_REFACTOR.md\n orderBy?: string;\n orderByChild?: string;\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: unknown;\n startAtKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n equalTo?: unknown;\n equalToKey?: string;\n}\n\nexport interface QueryParams {\n orderBy?: 'key' | 'priority' | 'child' | 'value';\n orderByChild?: string; // child path (when orderBy='child')\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: unknown; // startAt value\n startAtKey?: string; // startAt key tie-breaker\n endAt?: unknown; // endAt value\n endAtKey?: string; // endAt key tie-breaker\n equalTo?: unknown; // equalTo value\n equalToKey?: string; // equalTo key tie-breaker\n}\n\nexport interface UnsubscribeMessage {\n o: 'us'; // operation: unsubscribe\n p: string; // path\n r: string; // request ID\n}\n\nexport interface OnceMessage {\n o: 'o'; // operation: once\n p: string; // path\n r: string; // request ID\n // Query parameters (optional) - at top level (same as SubscribeMessage)\n orderBy?: string;\n orderByChild?: string;\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: unknown;\n startAtKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n equalTo?: unknown;\n equalToKey?: string;\n}\n\nexport interface OnDisconnectMessage {\n o: 'od'; // operation: onDisconnect\n p: string; // path\n a: 's' | 'u' | 'd' | 'c'; // action: set, update, delete, cancel\n v?: unknown; // value (for set/update, priority is included as .priority if set)\n r: string; // request ID\n}\n\nexport interface LeaveMessage {\n o: 'l'; // operation: leave\n r: string; // request ID\n}\n\nexport interface PongMessage {\n o: 'po'; // operation: pong (response to server ping)\n}\n\n// Transaction operation types (used in ops array)\nexport interface TxSetOp {\n o: 's'; // set\n p: string; // path\n v: unknown; // value\n}\n\nexport interface TxUpdateOp {\n o: 'u'; // update (merge)\n p: string; // path\n v: Record<string, unknown>; // values to merge\n}\n\nexport interface TxDeleteOp {\n o: 'd'; // delete\n p: string; // path\n}\n\nexport interface TxConditionOp {\n o: 'c'; // condition (compare-and-swap check)\n p: string; // path\n v: unknown; // expected value\n}\n\nexport type TxOperation = TxSetOp | TxUpdateOp | TxDeleteOp | TxConditionOp;\n\nexport interface TransactionMessage {\n o: 'tx'; // operation: transaction\n ops: TxOperation[]; // array of operations\n r: string; // request ID\n}\n\nexport type ClientMessage =\n | JoinMessage\n | SetMessage\n | UpdateMessage\n | DeleteMessage\n | SubscribeMessage\n | UnsubscribeMessage\n | OnceMessage\n | OnDisconnectMessage\n | LeaveMessage\n | PongMessage\n | TransactionMessage;\n\n// ============================================\n// Server -> Client Messages\n// ============================================\n\nexport interface AckMessage {\n a: string; // ack with request ID\n}\n\nexport interface NackMessage {\n n: string; // nack with request ID\n e: string; // error code\n m?: string; // human-readable message\n}\n\n// New delta-based event format\n// Server sends 'put' for single-path changes, 'patch' for multi-path changes\n// Client generates child_* events locally from these deltas\n\nexport interface PutEventMessage {\n ev: 'put'; // event type: single-path delta\n sp: string; // subscription path (absolute) - identifies which subscription\n p: string; // delta path (relative to sp), \"/\" means full snapshot\n v?: unknown; // value, null for deletion\n x?: boolean; // volatile flag\n ts?: number; // server timestamp in ms (for volatile events)\n}\n\nexport interface PatchEventMessage {\n ev: 'patch'; // event type: multi-path delta\n sp: string; // subscription path (absolute)\n p: string; // base path (usually \"/\")\n v?: Record<string, unknown>; // multiple path:value pairs (relative to sp+p)\n x?: boolean; // volatile flag\n ts?: number; // server timestamp in ms (for volatile events)\n}\n\nexport type EventMessage = PutEventMessage | PatchEventMessage;\n\nexport interface OnceResponseMessage {\n oc: string; // once response with request ID\n ov: unknown; // value\n}\n\nexport interface JoinCompleteMessage {\n jc: string; // join complete with request ID\n vp?: string[] | null; // volatile path patterns (e.g., [\"players/*/position\"])\n cid?: string; // connection ID (for reconnect deduplication)\n}\n\nexport interface PingMessage {\n o: 'pi'; // operation: ping (keepalive from server)\n}\n\nexport type ServerMessage =\n | AckMessage\n | NackMessage\n | EventMessage\n | OnceResponseMessage\n | JoinCompleteMessage\n | PingMessage;\n\n// ============================================\n// Helper functions\n// ============================================\n\nexport function isAckMessage(msg: ServerMessage): msg is AckMessage {\n return 'a' in msg;\n}\n\nexport function isJoinCompleteMessage(msg: ServerMessage): msg is JoinCompleteMessage {\n return 'jc' in msg;\n}\n\nexport function isNackMessage(msg: ServerMessage): msg is NackMessage {\n return 'n' in msg;\n}\n\nexport function isEventMessage(msg: ServerMessage): msg is EventMessage {\n return 'ev' in msg;\n}\n\nexport function isPutEventMessage(msg: ServerMessage): msg is PutEventMessage {\n return 'ev' in msg && (msg as EventMessage).ev === 'put';\n}\n\nexport function isPatchEventMessage(msg: ServerMessage): msg is PatchEventMessage {\n return 'ev' in msg && (msg as EventMessage).ev === 'patch';\n}\n\nexport function isOnceResponseMessage(msg: ServerMessage): msg is OnceResponseMessage {\n return 'oc' in msg;\n}\n\nexport function isPingMessage(msg: ServerMessage): msg is PingMessage {\n return 'o' in msg && (msg as PingMessage).o === 'pi';\n}\n","/**\n * MessageQueue - handles request/response correlation for WebSocket messages.\n *\n * Every reliable operation gets a unique request ID. When we send a message,\n * we create a promise and store it. When we receive an ack/nack, we resolve/reject\n * the corresponding promise.\n */\n\nimport { LarkError } from '../LarkError';\nimport {\n ServerMessage,\n isAckMessage,\n isJoinCompleteMessage,\n isNackMessage,\n isOnceResponseMessage,\n} from '../protocol/messages';\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/** Response from join complete message */\nexport interface JoinCompleteResponse {\n volatilePaths: string[];\n connectionId: string | null;\n}\n\nexport class MessageQueue {\n private nextId = 1;\n private pending = new Map<string, PendingRequest>();\n private defaultTimeout: number;\n\n constructor(defaultTimeout: number = 30000) {\n this.defaultTimeout = defaultTimeout;\n }\n\n /**\n * Generate a new unique request ID.\n */\n nextRequestId(): string {\n return String(this.nextId++);\n }\n\n /**\n * Register a pending request that expects a response.\n * Returns a promise that resolves when ack is received, or rejects on nack/timeout.\n */\n registerRequest(requestId: string, timeout?: number): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const timeoutMs = timeout ?? this.defaultTimeout;\n\n const timeoutHandle = setTimeout(() => {\n this.pending.delete(requestId);\n reject(new LarkError('timeout', `Request ${requestId} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n this.pending.set(requestId, {\n resolve,\n reject,\n timeout: timeoutHandle,\n });\n });\n }\n\n /**\n * Handle an incoming server message. If it's a response to a pending request,\n * resolve or reject the corresponding promise.\n *\n * @returns true if the message was handled (was a response), false otherwise\n */\n handleMessage(message: ServerMessage): boolean {\n // Join complete (includes volatile paths and connection ID)\n if (isJoinCompleteMessage(message)) {\n const pending = this.pending.get(message.jc);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.jc);\n // Resolve with full join response\n const response: JoinCompleteResponse = {\n volatilePaths: message.vp || [],\n connectionId: message.cid || null,\n };\n pending.resolve(response);\n return true;\n }\n }\n\n // Regular ack\n if (isAckMessage(message)) {\n const pending = this.pending.get(message.a);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.a);\n pending.resolve(undefined);\n return true;\n }\n }\n\n // Nack (error)\n if (isNackMessage(message)) {\n const pending = this.pending.get(message.n);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.n);\n pending.reject(new LarkError(message.e, message.m));\n return true;\n }\n }\n\n // Once response\n if (isOnceResponseMessage(message)) {\n const pending = this.pending.get(message.oc);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.oc);\n pending.resolve(message.ov);\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Reject a specific pending request locally (without server message).\n * Used for client-side taint propagation when a write fails and\n * dependent writes need to be rejected.\n *\n * @returns true if the request was pending and rejected, false otherwise\n */\n rejectLocally(requestId: string, error: Error): boolean {\n const pending = this.pending.get(requestId);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(requestId);\n pending.reject(error);\n return true;\n }\n return false;\n }\n\n /**\n * Reject all pending requests (e.g., on disconnect).\n */\n rejectAll(error: Error): void {\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timeout);\n pending.reject(error);\n }\n this.pending.clear();\n }\n\n /**\n * Get the number of pending requests.\n */\n get pendingCount(): number {\n return this.pending.size;\n }\n}\n","/**\n * PendingWriteManager - tracks pending write operations for optimistic updates.\n *\n * Each write operation is assigned a unique operation ID (oid). The manager tracks\n * pending writes until they are acknowledged by the server. On reconnect, pending\n * writes can be retried to ensure data consistency.\n */\n\nexport type WriteOperation = 'set' | 'update' | 'delete' | 'transaction';\n\nexport interface PendingWrite {\n oid: string;\n operation: WriteOperation;\n path: string;\n value?: unknown;\n timestamp: number;\n}\n\nexport class PendingWriteManager {\n private pending = new Map<string, PendingWrite>();\n\n /**\n * Track a new pending write operation.\n * Uses the request ID as the tracking key.\n */\n trackWrite(requestId: string, operation: WriteOperation, path: string, value?: unknown): void {\n this.pending.set(requestId, {\n oid: requestId,\n operation,\n path,\n value,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Called when a write is acknowledged by the server.\n * Removes the write from pending tracking.\n */\n onAck(oid: string): boolean {\n return this.pending.delete(oid);\n }\n\n /**\n * Called when a write is rejected by the server.\n * Removes the write from pending tracking.\n */\n onNack(oid: string): boolean {\n return this.pending.delete(oid);\n }\n\n /**\n * Get all pending writes for retry on reconnect.\n * Returns writes in order of their timestamps (oldest first).\n */\n getPendingWrites(): PendingWrite[] {\n const writes = Array.from(this.pending.values());\n writes.sort((a, b) => a.timestamp - b.timestamp);\n return writes;\n }\n\n /**\n * Check if a specific operation is still pending.\n */\n isPending(oid: string): boolean {\n return this.pending.has(oid);\n }\n\n /**\n * Get the number of pending writes.\n */\n get pendingCount(): number {\n return this.pending.size;\n }\n\n /**\n * Clear all pending writes (e.g., on disconnect when not retrying).\n */\n clear(): void {\n this.pending.clear();\n }\n\n /**\n * Remove writes older than a specified age (in milliseconds).\n * Useful for cleaning up stale pending writes that may never be acked.\n */\n removeStale(maxAgeMs: number): number {\n const cutoff = Date.now() - maxAgeMs;\n let removed = 0;\n\n for (const [oid, write] of this.pending) {\n if (write.timestamp < cutoff) {\n this.pending.delete(oid);\n removed++;\n }\n }\n\n return removed;\n }\n}\n","/**\n * Path utilities for database paths.\n * Paths are Unix-style: /players/abc/score\n */\n\n/**\n * Normalize a path to match server format:\n * - Root is \"/\"\n * - All other paths start with \"/\" and have no trailing slash\n * - Multiple slashes are collapsed\n */\nexport function normalizePath(path: string): string {\n if (!path || path === '/') return '/';\n\n // Split, filter empty segments, rejoin with leading slash\n const segments = path.split('/').filter((segment) => segment.length > 0);\n if (segments.length === 0) return '/';\n\n return '/' + segments.join('/');\n}\n\n/**\n * Join path segments together.\n */\nexport function joinPath(...segments: string[]): string {\n const allParts: string[] = [];\n\n for (const segment of segments) {\n if (!segment || segment === '/') continue;\n // Split each segment and add non-empty parts\n const parts = segment.split('/').filter((p) => p.length > 0);\n allParts.push(...parts);\n }\n\n if (allParts.length === 0) return '/';\n return '/' + allParts.join('/');\n}\n\n/**\n * Get the parent path. Returns \"/\" for paths one level deep, \"/\" for root.\n */\nexport function getParentPath(path: string): string {\n const normalized = normalizePath(path);\n if (normalized === '/') return '/';\n\n const lastSlash = normalized.lastIndexOf('/');\n if (lastSlash <= 0) return '/';\n\n return normalized.substring(0, lastSlash);\n}\n\n/**\n * Get the last segment of a path (the \"key\").\n */\nexport function getKey(path: string): string | null {\n const normalized = normalizePath(path);\n if (normalized === '/') return null;\n\n const lastSlash = normalized.lastIndexOf('/');\n return normalized.substring(lastSlash + 1);\n}\n\n/**\n * Check if a path is valid (no invalid characters).\n */\nexport function isValidPath(path: string): boolean {\n if (path === '' || path === '/') return true;\n\n const normalized = normalizePath(path);\n // Filter out empty segments from leading slash\n const segments = normalized.split('/').filter((s) => s.length > 0);\n\n for (const segment of segments) {\n // Check for invalid characters: . $ # [ ]\n if (/[.\\$#\\[\\]]/.test(segment)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Get a nested value from an object by path.\n */\nexport function getValueAtPath(obj: unknown, path: string): unknown {\n const normalized = normalizePath(path);\n if (normalized === '/') return obj;\n\n // Split and filter empty segments (from leading slash)\n const segments = normalized.split('/').filter((s) => s.length > 0);\n let current: unknown = obj;\n\n for (const segment of segments) {\n if (current === null || current === undefined) {\n return undefined;\n }\n if (typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n return current;\n}\n\n/**\n * Set a nested value in an object by path. Mutates the object.\n * Creates intermediate objects as needed.\n */\nexport function setValueAtPath(obj: Record<string, unknown>, path: string, value: unknown): void {\n const normalized = normalizePath(path);\n if (normalized === '/') {\n // Can't set root on an object\n return;\n }\n\n // Split and filter empty segments (from leading slash)\n const segments = normalized.split('/').filter((s) => s.length > 0);\n let current: Record<string, unknown> = obj;\n\n for (let i = 0; i < segments.length - 1; i++) {\n const segment = segments[i];\n if (!(segment in current) || typeof current[segment] !== 'object' || current[segment] === null) {\n current[segment] = {};\n }\n current = current[segment] as Record<string, unknown>;\n }\n\n const lastSegment = segments[segments.length - 1];\n current[lastSegment] = value;\n}\n","/**\n * Firebase-compatible sorting utilities.\n *\n * Implements the same sorting rules as the server (and Firebase):\n * - Type hierarchy: null < false < true < numbers < strings < objects\n * - orderByKey: special handling for 32-bit integer keys\n * - orderByChild: sorts by nested child value\n * - orderByValue: sorts by the value itself\n * - orderByPriority: same as orderByChild('.priority')\n *\n * All sorting includes tie-breaking by key when values are equal.\n */\n\nimport { QueryParams } from '../protocol/messages';\n\n/**\n * Entry for sorting - contains key, value, and sort value.\n */\nexport interface SortEntry {\n key: string;\n value: unknown;\n sortValue: unknown; // The value to sort by (depends on orderBy)\n}\n\n/**\n * Returns the type rank for Firebase-style sorting.\n * Order: null (0) < false (1) < true (2) < numbers (3) < strings (4) < objects (5)\n */\nexport function typeRank(value: unknown): number {\n if (value === null || value === undefined) {\n return 0;\n }\n if (typeof value === 'boolean') {\n return value ? 2 : 1; // false = 1, true = 2\n }\n if (typeof value === 'number') {\n return 3;\n }\n if (typeof value === 'string') {\n return 4;\n }\n if (typeof value === 'object') {\n return 5;\n }\n return 6; // Unknown types sort last\n}\n\n/**\n * Compare two values using Firebase ordering rules.\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n */\nexport function compareValues(a: unknown, b: unknown): number {\n const rankA = typeRank(a);\n const rankB = typeRank(b);\n\n // Different types - compare by rank\n if (rankA !== rankB) {\n return rankA < rankB ? -1 : 1;\n }\n\n // Same type - compare values\n switch (rankA) {\n case 0: // null\n return 0;\n\n case 1: // false\n case 2: // true\n return 0; // Same boolean value (rank already differentiated)\n\n case 3: // number\n const numA = a as number;\n const numB = b as number;\n if (numA < numB) return -1;\n if (numA > numB) return 1;\n return 0;\n\n case 4: // string\n const strA = a as string;\n const strB = b as string;\n if (strA < strB) return -1;\n if (strA > strB) return 1;\n return 0;\n\n case 5: // object\n return 0; // Objects are equal for value comparison, sort by key\n\n default:\n return 0;\n }\n}\n\n/**\n * Check if a key should be treated as a 32-bit integer for sorting.\n * Integer keys sort numerically before string keys.\n */\nexport function isInt32Key(key: string): boolean {\n if (key.length === 0) {\n return false;\n }\n\n // Check for leading zeros (except \"0\" itself)\n if (key.length > 1 && key[0] === '0') {\n return false;\n }\n\n // Handle negative numbers\n if (key[0] === '-') {\n if (key.length === 1) {\n return false; // Just \"-\" is not valid\n }\n // Check for leading zeros after minus: \"-0\", \"-007\"\n if (key.length > 2 && key[1] === '0') {\n return false;\n }\n // Check for \"-0\" (negative zero)\n if (key === '-0') {\n return false;\n }\n }\n\n // Try to parse as integer\n const parsed = parseInt(key, 10);\n if (isNaN(parsed)) {\n return false;\n }\n\n // Check if it's the same string (no extra characters, no scientific notation)\n if (String(parsed) !== key) {\n return false;\n }\n\n // Check 32-bit signed integer range\n const MIN_INT32 = -2147483648;\n const MAX_INT32 = 2147483647;\n return parsed >= MIN_INT32 && parsed <= MAX_INT32;\n}\n\n/**\n * Compare two keys using Firebase orderByKey rules.\n * Integer keys sort numerically first, then string keys sort lexicographically.\n */\nexport function compareKeys(a: string, b: string): number {\n const aIsInt = isInt32Key(a);\n const bIsInt = isInt32Key(b);\n\n // Integers come before strings\n if (aIsInt && !bIsInt) {\n return -1;\n }\n if (!aIsInt && bIsInt) {\n return 1;\n }\n\n // Both integers: compare numerically\n if (aIsInt && bIsInt) {\n const aInt = parseInt(a, 10);\n const bInt = parseInt(b, 10);\n if (aInt < bInt) return -1;\n if (aInt > bInt) return 1;\n return 0;\n }\n\n // Both strings: compare lexicographically\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n}\n\n/**\n * Get a nested value from an object using a path.\n */\nfunction getNestedValue(obj: unknown, path: string): unknown {\n if (!obj || typeof obj !== 'object') {\n return undefined;\n }\n\n const segments = path.split('/').filter((s) => s.length > 0);\n let current: unknown = obj;\n\n for (const segment of segments) {\n if (!current || typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n return current;\n}\n\n/**\n * Get the sort value for an entry based on query parameters.\n */\nexport function getSortValue(value: unknown, queryParams: QueryParams | null): unknown {\n if (!queryParams) {\n return null; // No ordering, key is used\n }\n\n // orderByPriority is normalized to orderByChild('.priority') on the server\n // but we handle it explicitly here too\n if (queryParams.orderBy === 'priority') {\n return getNestedValue(value, '.priority');\n }\n\n if (queryParams.orderByChild) {\n return getNestedValue(value, queryParams.orderByChild);\n }\n\n if (queryParams.orderBy === 'value') {\n return value;\n }\n\n // orderByKey or default - sort by key, not value\n return null;\n}\n\n/**\n * Compare two entries for sorting.\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n */\nexport function compareEntries(a: SortEntry, b: SortEntry, queryParams: QueryParams | null): number {\n // If ordering by key (or no ordering), just compare keys\n if (!queryParams || queryParams.orderBy === 'key' || (!queryParams.orderBy && !queryParams.orderByChild)) {\n return compareKeys(a.key, b.key);\n }\n\n // Compare by sort value first\n const cmp = compareValues(a.sortValue, b.sortValue);\n if (cmp !== 0) {\n return cmp;\n }\n\n // Equal sort values - tie-break by key\n return compareKeys(a.key, b.key);\n}\n\n/**\n * Sort an array of entries according to query parameters.\n * Modifies the array in place and returns it.\n */\nexport function sortEntries(entries: SortEntry[], queryParams: QueryParams | null): SortEntry[] {\n entries.sort((a, b) => compareEntries(a, b, queryParams));\n return entries;\n}\n\n/**\n * Create sort entries from an object's children.\n */\nexport function createSortEntries(data: unknown, queryParams: QueryParams | null): SortEntry[] {\n if (!data || typeof data !== 'object' || Array.isArray(data)) {\n return [];\n }\n\n const obj = data as Record<string, unknown>;\n const entries: SortEntry[] = [];\n\n for (const key of Object.keys(obj)) {\n // Skip .priority when creating entries (it's metadata, not a child)\n if (key === '.priority') {\n continue;\n }\n\n const value = obj[key];\n entries.push({\n key,\n value,\n sortValue: getSortValue(value, queryParams),\n });\n }\n\n return sortEntries(entries, queryParams);\n}\n\n/**\n * Get the sorted keys from data according to query parameters.\n */\nexport function getSortedKeys(data: unknown, queryParams: QueryParams | null): string[] {\n const entries = createSortEntries(data, queryParams);\n return entries.map((e) => e.key);\n}\n\n/**\n * Find the position where a key should be inserted to maintain sort order.\n * Returns the index of the key that should come before this one,\n * or -1 if it should be first.\n */\nexport function findInsertPosition(\n orderedKeys: string[],\n data: Record<string, unknown>,\n newKey: string,\n queryParams: QueryParams | null\n): number {\n const newValue = data[newKey];\n const newSortValue = getSortValue(newValue, queryParams);\n const newEntry: SortEntry = { key: newKey, value: newValue, sortValue: newSortValue };\n\n // Find where this entry belongs\n for (let i = 0; i < orderedKeys.length; i++) {\n const existingKey = orderedKeys[i];\n const existingValue = data[existingKey];\n const existingSortValue = getSortValue(existingValue, queryParams);\n const existingEntry: SortEntry = { key: existingKey, value: existingValue, sortValue: existingSortValue };\n\n const cmp = compareEntries(newEntry, existingEntry, queryParams);\n if (cmp < 0) {\n // New entry should come before this one\n return i - 1; // Return the previous index, or -1 if first\n }\n }\n\n // New entry goes at the end\n return orderedKeys.length - 1;\n}\n\n/**\n * Get the previous child key (the key that comes before this one in sorted order).\n * Returns null if this key should be first.\n */\nexport function getPreviousKey(orderedKeys: string[], key: string): string | null {\n const idx = orderedKeys.indexOf(key);\n if (idx <= 0) return null;\n return orderedKeys[idx - 1];\n}\n","/**\n * View - represents a client's subscription to a database path.\n *\n * Each View encapsulates all state for a single subscription:\n * - Path and query parameters\n * - Event callbacks by type\n * - Ordered children (keys in sorted order, sorted by client)\n * - Local cache of visible data\n *\n * This mirrors the server's View concept, enabling:\n * - Per-View caching (no shared global cache)\n * - Client-side sorting (ignores server's ak/mv hints)\n * - Local-first writes (future)\n * - Independent reconnection per View\n */\n\nimport type { DataSnapshot } from '../DataSnapshot';\nimport { EventType } from '../protocol/constants';\nimport { QueryParams } from '../protocol/messages';\nimport { getValueAtPath, joinPath, normalizePath, setValueAtPath } from '../utils/path';\nimport { getSortedKeys, compareKeys, getSortValue, compareEntries, SortEntry } from '../utils/sort';\n\nexport type SnapshotCallback = (snapshot: DataSnapshot, previousChildKey?: string | null) => void;\n\ninterface SubscriptionEntry {\n callback: SnapshotCallback;\n unsubscribe: () => void;\n}\n\nexport class View {\n /** The absolute path this View is subscribed to */\n readonly path: string;\n\n /** Query parameters for this View (ordering, limits, ranges) */\n private _queryParams: QueryParams | null;\n\n /** Event callbacks organized by event type */\n private eventCallbacks = new Map<EventType, SubscriptionEntry[]>();\n\n /** Child keys in sorted order */\n private _orderedChildren: string[] = [];\n\n /** Local cache: stores the value at the subscription path */\n private _cache: unknown = undefined;\n\n /** Whether we've received the initial snapshot from the server */\n private _hasReceivedInitialSnapshot = false;\n\n /** Pending write request IDs for this View (for local-first recovery) */\n private _pendingWrites = new Set<string>();\n\n /** Whether this View is in recovery mode after a nack */\n private _recovering = false;\n\n constructor(path: string, queryParams?: QueryParams) {\n this.path = normalizePath(path);\n this._queryParams = queryParams ?? null;\n }\n\n /** Get the current query parameters */\n get queryParams(): QueryParams | null {\n return this._queryParams;\n }\n\n /**\n * Update query parameters and re-sort cached data.\n * Returns true if queryParams actually changed.\n */\n updateQueryParams(newQueryParams?: QueryParams): boolean {\n const newParams = newQueryParams ?? null;\n\n // Check if params actually changed\n if (this.queryParamsEqual(this._queryParams, newParams)) {\n return false;\n }\n\n this._queryParams = newParams;\n\n // Re-sort cached data with new query params\n if (this._cache && typeof this._cache === 'object' && this._cache !== null) {\n this._orderedChildren = getSortedKeys(this._cache as Record<string, unknown>, this._queryParams);\n }\n\n return true;\n }\n\n /**\n * Compare two QueryParams objects for equality.\n */\n private queryParamsEqual(a: QueryParams | null, b: QueryParams | null): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n\n // Compare all relevant fields\n return (\n a.orderBy === b.orderBy &&\n a.orderByChild === b.orderByChild &&\n a.limitToFirst === b.limitToFirst &&\n a.limitToLast === b.limitToLast &&\n a.startAt === b.startAt &&\n a.startAtKey === b.startAtKey &&\n a.endAt === b.endAt &&\n a.endAtKey === b.endAtKey &&\n a.equalTo === b.equalTo &&\n a.equalToKey === b.equalToKey\n );\n }\n\n // ============================================\n // Subscription Management\n // ============================================\n\n /**\n * Add a callback for an event type.\n * Returns an unsubscribe function.\n */\n addCallback(eventType: EventType, callback: SnapshotCallback): () => void {\n if (!this.eventCallbacks.has(eventType)) {\n this.eventCallbacks.set(eventType, []);\n }\n\n const unsubscribe = () => {\n this.removeCallback(eventType, callback);\n };\n\n this.eventCallbacks.get(eventType)!.push({ callback, unsubscribe });\n return unsubscribe;\n }\n\n /**\n * Remove a specific callback for an event type.\n */\n removeCallback(eventType: EventType, callback: SnapshotCallback): void {\n const entries = this.eventCallbacks.get(eventType);\n if (!entries) return;\n\n const index = entries.findIndex((entry) => entry.callback === callback);\n if (index !== -1) {\n entries.splice(index, 1);\n }\n\n // Clean up empty arrays\n if (entries.length === 0) {\n this.eventCallbacks.delete(eventType);\n }\n }\n\n /**\n * Remove all callbacks for an event type.\n */\n removeAllCallbacks(eventType: EventType): void {\n this.eventCallbacks.delete(eventType);\n }\n\n /**\n * Get all subscribed event types.\n */\n getEventTypes(): EventType[] {\n return Array.from(this.eventCallbacks.keys());\n }\n\n /**\n * Get callbacks for a specific event type.\n */\n getCallbacks(eventType: EventType): SubscriptionEntry[] {\n return this.eventCallbacks.get(eventType) ?? [];\n }\n\n /**\n * Check if there are any callbacks registered.\n */\n hasCallbacks(): boolean {\n return this.eventCallbacks.size > 0;\n }\n\n /**\n * Check if there's a callback for a specific event type.\n */\n hasCallbacksForType(eventType: EventType): boolean {\n const entries = this.eventCallbacks.get(eventType);\n return entries !== undefined && entries.length > 0;\n }\n\n // ============================================\n // Cache Management\n // ============================================\n\n /**\n * Set the full cache value (used for initial snapshot).\n * Children are sorted using client-side sorting rules.\n */\n setCache(value: unknown): void {\n this._cache = value;\n this._hasReceivedInitialSnapshot = true;\n\n // Update ordered children from the value using client-side sorting\n if (value && typeof value === 'object' && value !== null && !Array.isArray(value)) {\n this._orderedChildren = getSortedKeys(value, this.queryParams);\n } else {\n this._orderedChildren = [];\n }\n }\n\n /**\n * Get the full cached value at the subscription path.\n */\n getCache(): unknown {\n return this._cache;\n }\n\n /**\n * Get a value from the cache at a relative or absolute path.\n * If the path is outside this View's scope, returns undefined.\n */\n getCacheValue(path: string): { value: unknown; found: boolean } {\n const normalized = normalizePath(path);\n\n // Check if the requested path is within this View's scope\n if (normalized === this.path) {\n // Exact match - return full cache\n if (this._cache !== undefined) {\n return { value: this._cache, found: true };\n }\n return { value: undefined, found: false };\n }\n\n // Check if path is a descendant of this View's path\n if (normalized.startsWith(this.path + '/') || this.path === '/') {\n const relativePath = this.path === '/'\n ? normalized\n : normalized.slice(this.path.length);\n\n if (this._cache !== undefined) {\n const extractedValue = getValueAtPath(this._cache, relativePath);\n return { value: extractedValue, found: true };\n }\n }\n\n return { value: undefined, found: false };\n }\n\n /**\n * Update a child value in the cache.\n * relativePath should be relative to this View's path.\n * Maintains sorted order of children using client-side sorting.\n */\n updateCacheChild(relativePath: string, value: unknown): void {\n if (relativePath === '/') {\n // Full replacement\n this.setCache(value);\n return;\n }\n\n // Extract child key from relative path\n const segments = relativePath.split('/').filter((s) => s.length > 0);\n if (segments.length === 0) return;\n\n const childKey = segments[0];\n\n // Ensure cache is an object\n if (this._cache === null || this._cache === undefined || typeof this._cache !== 'object') {\n this._cache = {};\n }\n\n // Update the cache\n const cache = this._cache as Record<string, unknown>;\n\n if (segments.length === 1) {\n // Direct child update\n if (value === null) {\n delete cache[childKey];\n // Remove from ordered children\n const idx = this._orderedChildren.indexOf(childKey);\n if (idx !== -1) {\n this._orderedChildren.splice(idx, 1);\n }\n } else {\n const wasPresent = childKey in cache;\n cache[childKey] = value;\n\n if (!wasPresent) {\n // New child - insert at correct sorted position\n this.insertChildSorted(childKey, value);\n } else {\n // Existing child - re-sort if sort field might have changed\n this.resortChild(childKey);\n }\n }\n } else {\n // Nested update - need to navigate and update\n if (value === null) {\n // Deletion at nested path\n setValueAtPath(cache, relativePath, undefined);\n } else {\n // Ensure parent path exists\n const wasPresent = childKey in cache;\n if (!wasPresent) {\n cache[childKey] = {};\n }\n setValueAtPath(cache, relativePath, value);\n\n if (!wasPresent) {\n // New child - insert at correct sorted position\n this.insertChildSorted(childKey, cache[childKey]);\n } else {\n // Existing child - re-sort if sort field might have changed\n this.resortChild(childKey);\n }\n }\n }\n }\n\n /**\n * Insert a child key at the correct sorted position.\n */\n private insertChildSorted(key: string, value: unknown): void {\n if (this._orderedChildren.includes(key)) {\n // Already present, just re-sort\n this.resortChild(key);\n return;\n }\n\n const cache = this._cache as Record<string, unknown>;\n const newSortValue = getSortValue(value, this.queryParams);\n const newEntry: SortEntry = { key, value, sortValue: newSortValue };\n\n // Find the correct position\n let insertIdx = this._orderedChildren.length; // Default: end\n for (let i = 0; i < this._orderedChildren.length; i++) {\n const existingKey = this._orderedChildren[i];\n const existingValue = cache[existingKey];\n const existingSortValue = getSortValue(existingValue, this.queryParams);\n const existingEntry: SortEntry = { key: existingKey, value: existingValue, sortValue: existingSortValue };\n\n if (compareEntries(newEntry, existingEntry, this.queryParams) < 0) {\n insertIdx = i;\n break;\n }\n }\n\n this._orderedChildren.splice(insertIdx, 0, key);\n }\n\n /**\n * Re-sort a child that already exists (its sort value may have changed).\n */\n private resortChild(key: string): void {\n const idx = this._orderedChildren.indexOf(key);\n if (idx === -1) return;\n\n // Remove and re-insert\n this._orderedChildren.splice(idx, 1);\n const cache = this._cache as Record<string, unknown>;\n const value = cache[key];\n this.insertChildSorted(key, value);\n }\n\n /**\n * Remove a child from the cache.\n */\n removeCacheChild(relativePath: string): void {\n this.updateCacheChild(relativePath, null);\n }\n\n /**\n * Check if we've received the initial snapshot.\n */\n get hasReceivedInitialSnapshot(): boolean {\n return this._hasReceivedInitialSnapshot;\n }\n\n // ============================================\n // Ordered Children\n // ============================================\n\n /**\n * Get the ordered children keys.\n */\n get orderedChildren(): string[] {\n return [...this._orderedChildren];\n }\n\n /**\n * Get the previous sibling key for a given key.\n * Returns null if the key is first or not found.\n */\n getPreviousChildKey(key: string): string | null {\n const idx = this._orderedChildren.indexOf(key);\n if (idx <= 0) return null;\n return this._orderedChildren[idx - 1];\n }\n\n /**\n * Check if a key exists in the ordered children.\n */\n hasChild(key: string): boolean {\n return this._orderedChildren.includes(key);\n }\n\n // ============================================\n // Query Helpers\n // ============================================\n\n /**\n * Check if this View has query parameters.\n */\n hasQuery(): boolean {\n if (!this.queryParams) return false;\n return !!(\n this.queryParams.limitToFirst ||\n this.queryParams.limitToLast ||\n this.queryParams.startAt !== undefined ||\n this.queryParams.endAt !== undefined ||\n this.queryParams.equalTo !== undefined ||\n this.queryParams.orderBy ||\n this.queryParams.orderByChild\n );\n }\n\n /**\n * Check if this View has limit constraints.\n */\n hasLimit(): boolean {\n if (!this.queryParams) return false;\n return !!(this.queryParams.limitToFirst || this.queryParams.limitToLast);\n }\n\n // ============================================\n // Pending Writes (for local-first)\n // ============================================\n\n /**\n * Get current pending write IDs (to include in pw field).\n * Returns a copy of the set as an array.\n */\n getPendingWriteIds(): string[] {\n return Array.from(this._pendingWrites);\n }\n\n /**\n * Add a pending write request ID.\n */\n addPendingWrite(requestId: string): void {\n this._pendingWrites.add(requestId);\n }\n\n /**\n * Remove a pending write (on ack or nack).\n */\n removePendingWrite(requestId: string): boolean {\n return this._pendingWrites.delete(requestId);\n }\n\n /**\n * Clear all pending writes (on nack recovery).\n */\n clearPendingWrites(): void {\n this._pendingWrites.clear();\n }\n\n /**\n * Check if this View has pending writes.\n */\n hasPendingWrites(): boolean {\n return this._pendingWrites.size > 0;\n }\n\n /**\n * Check if a specific write is pending.\n */\n isWritePending(requestId: string): boolean {\n return this._pendingWrites.has(requestId);\n }\n\n // ============================================\n // Recovery State (for local-first nack handling)\n // ============================================\n\n /**\n * Check if this View is in recovery mode.\n */\n get recovering(): boolean {\n return this._recovering;\n }\n\n /**\n * Enter recovery mode (after a nack).\n */\n enterRecovery(): void {\n this._recovering = true;\n this.clearPendingWrites();\n }\n\n /**\n * Exit recovery mode (after fresh snapshot received).\n */\n exitRecovery(): void {\n this._recovering = false;\n }\n\n // ============================================\n // Cleanup\n // ============================================\n\n /**\n * Clear the cache but preserve callbacks and pending writes.\n * Used during reconnection.\n */\n clearCache(): void {\n this._cache = undefined;\n this._orderedChildren = [];\n this._hasReceivedInitialSnapshot = false;\n // Note: pending writes are preserved for retry on reconnect\n }\n\n /**\n * Clear everything - used when unsubscribing completely.\n */\n clear(): void {\n this.eventCallbacks.clear();\n this._cache = undefined;\n this._orderedChildren = [];\n this._hasReceivedInitialSnapshot = false;\n this._pendingWrites.clear();\n this._recovering = false;\n }\n}\n","/**\n * SubscriptionManager - manages Views and routes events to callbacks.\n *\n * Refactored to use View-based architecture:\n * - Each subscription path has a single View\n * - Each View maintains its own cache and ordered children\n * - No shared global cache\n *\n * Handles the delta-based protocol where server sends 'put' and 'patch' events.\n * Client generates child_added, child_changed, child_removed, child_moved events locally.\n */\n\nimport type { DataSnapshot } from '../DataSnapshot';\nimport { EventType } from '../protocol/constants';\nimport { EventMessage, PutEventMessage, PatchEventMessage, QueryParams } from '../protocol/messages';\nimport { joinPath, normalizePath } from '../utils/path';\nimport { View, SnapshotCallback } from './View';\n\nexport { SnapshotCallback } from './View';\n\nexport class SubscriptionManager {\n // path -> View (one View per subscribed path)\n private views = new Map<string, View>();\n\n // Callback to send subscribe message to server\n private sendSubscribe: ((path: string, eventTypes: string[], queryParams?: QueryParams) => Promise<void>) | null = null;\n\n // Callback to send unsubscribe message to server\n private sendUnsubscribe: ((path: string) => Promise<void>) | null = null;\n\n // Callback to create DataSnapshot from event data\n private createSnapshot:\n | ((path: string, value: unknown, volatile: boolean, serverTimestamp?: number) => DataSnapshot)\n | null = null;\n\n /**\n * Initialize the manager with server communication callbacks.\n */\n initialize(options: {\n sendSubscribe: (path: string, eventTypes: string[], queryParams?: QueryParams) => Promise<void>;\n sendUnsubscribe: (path: string) => Promise<void>;\n createSnapshot: (path: string, value: unknown, volatile: boolean, serverTimestamp?: number) => DataSnapshot;\n }): void {\n this.sendSubscribe = options.sendSubscribe;\n this.sendUnsubscribe = options.sendUnsubscribe;\n this.createSnapshot = options.createSnapshot;\n }\n\n /**\n * Subscribe to events at a path.\n * Returns an unsubscribe function.\n */\n subscribe(path: string, eventType: EventType, callback: SnapshotCallback, queryParams?: QueryParams): () => void {\n const normalizedPath = path;\n\n // Get or create View for this path\n let view = this.views.get(normalizedPath);\n const isNewView = !view;\n let queryParamsChanged = false;\n\n if (!view) {\n view = new View(normalizedPath, queryParams);\n this.views.set(normalizedPath, view);\n } else {\n // Check if queryParams changed - update View to match latest subscription\n // This matches server behavior where latest subscription overwrites\n queryParamsChanged = view.updateQueryParams(queryParams);\n }\n\n // Check if this is a new event type for this view\n const existingEventTypes = view.getEventTypes();\n const isNewEventType = !existingEventTypes.includes(eventType);\n\n // Add callback to view\n const unsubscribe = view.addCallback(eventType, callback);\n\n // Wrap unsubscribe to handle cleanup\n const wrappedUnsubscribe = () => {\n this.unsubscribeCallback(normalizedPath, eventType, callback);\n };\n\n // If this is a new view, new event type, or queryParams changed, update server subscription\n if (isNewView || isNewEventType || queryParamsChanged) {\n const allEventTypes = view.getEventTypes();\n // Fire and forget - we don't wait for the server\n this.sendSubscribe?.(normalizedPath, allEventTypes, view.queryParams ?? undefined).catch((err) => {\n console.error('Failed to subscribe:', err);\n });\n }\n\n // If the View already has cached data, fire initial events to the new callback\n // This ensures new subscribers get current state even if View already existed\n if (!isNewView && view.hasReceivedInitialSnapshot) {\n this.fireInitialEventsToCallback(view, eventType, callback);\n }\n\n return wrappedUnsubscribe;\n }\n\n /**\n * Fire initial events to a newly added callback from cached data.\n * This ensures new subscribers get current state even if View already existed.\n */\n private fireInitialEventsToCallback(\n view: View,\n eventType: EventType,\n callback: SnapshotCallback\n ): void {\n const cache = view.getCache();\n\n if (eventType === 'value') {\n // Fire value event with full cached data\n const snapshot = this.createSnapshot?.(view.path, cache, false);\n if (snapshot) {\n try {\n callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in value subscription callback:', err);\n }\n }\n } else if (eventType === 'child_added') {\n // Fire child_added for each child in order\n const orderedChildren = view.orderedChildren;\n for (let i = 0; i < orderedChildren.length; i++) {\n const childKey = orderedChildren[i];\n const previousChildKey = i > 0 ? orderedChildren[i - 1] : null;\n const childPath = joinPath(view.path, childKey);\n const { value: childValue } = view.getCacheValue(childPath);\n const snapshot = this.createSnapshot?.(childPath, childValue, false);\n if (snapshot) {\n try {\n callback(snapshot, previousChildKey);\n } catch (err) {\n console.error('Error in child_added subscription callback:', err);\n }\n }\n }\n }\n // child_changed, child_removed, child_moved don't need initial events\n }\n\n /**\n * Remove a specific callback from a subscription.\n */\n private unsubscribeCallback(path: string, eventType: EventType, callback: SnapshotCallback): void {\n const view = this.views.get(path);\n if (!view) return;\n\n view.removeCallback(eventType, callback);\n\n // If no more callbacks for this view, remove it entirely\n if (!view.hasCallbacks()) {\n this.views.delete(path);\n this.sendUnsubscribe?.(path).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n }\n }\n\n /**\n * Remove all subscriptions of a specific event type at a path.\n */\n unsubscribeEventType(path: string, eventType: EventType): void {\n const normalizedPath = path;\n const view = this.views.get(normalizedPath);\n if (!view) return;\n\n view.removeAllCallbacks(eventType);\n\n // If no more callbacks, unsubscribe from server\n if (!view.hasCallbacks()) {\n this.views.delete(normalizedPath);\n this.sendUnsubscribe?.(normalizedPath).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n } else {\n // Update server with remaining event types\n const remainingEventTypes = view.getEventTypes();\n this.sendSubscribe?.(normalizedPath, remainingEventTypes, view.queryParams ?? undefined).catch((err) => {\n console.error('Failed to update subscription:', err);\n });\n }\n }\n\n /**\n * Remove ALL subscriptions at a path.\n */\n unsubscribeAll(path: string): void {\n const normalizedPath = path;\n const view = this.views.get(normalizedPath);\n if (!view) return;\n\n view.clear();\n this.views.delete(normalizedPath);\n this.sendUnsubscribe?.(normalizedPath).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n }\n\n /**\n * Handle an incoming event message from the server.\n * Server sends 'put' or 'patch' events; we generate child_* events client-side.\n */\n handleEvent(message: EventMessage): void {\n if (message.ev === 'put') {\n this.handlePutEvent(message);\n } else if (message.ev === 'patch') {\n this.handlePatchEvent(message);\n } else {\n console.warn('Unknown event type:', (message as { ev: string }).ev);\n }\n }\n\n /**\n * Handle a 'put' event - single path change.\n */\n private handlePutEvent(message: PutEventMessage): void {\n const subscriptionPath = message.sp;\n const relativePath = message.p;\n const value = message.v;\n const isVolatile = message.x ?? false;\n const serverTimestamp = message.ts;\n\n const view = this.views.get(subscriptionPath);\n if (!view) return;\n\n // Skip if no value to apply\n if (value === undefined) return;\n\n // If View is recovering, only accept full snapshots (relativePath === '/')\n // Delta events during recovery are stale and should be ignored\n if (view.recovering) {\n if (relativePath !== '/') {\n // Ignore delta events during recovery\n return;\n }\n // This is a full snapshot - exit recovery after applying\n this.applyWriteToView(view, [{ relativePath, value }], isVolatile, serverTimestamp);\n view.exitRecovery();\n return;\n }\n\n // Use shared helper for cache update and event firing\n this.applyWriteToView(view, [{ relativePath, value }], isVolatile, serverTimestamp);\n }\n\n /**\n * Handle a 'patch' event - multi-path change.\n */\n private handlePatchEvent(message: PatchEventMessage): void {\n const subscriptionPath = message.sp;\n const basePath = message.p;\n const patches = message.v;\n const isVolatile = message.x ?? false;\n const serverTimestamp = message.ts;\n\n const view = this.views.get(subscriptionPath);\n if (!view) return;\n\n // Ignore patch events during recovery - they're always deltas\n // We're waiting for a full snapshot (put with p: '/')\n if (view.recovering) {\n return;\n }\n\n // Skip if no patches to apply\n if (!patches) return;\n\n // Build updates array from patches\n const updates: Array<{ relativePath: string; value: unknown }> = [];\n for (const [relativePath, patchValue] of Object.entries(patches)) {\n // Build the full relative path from base + relativePath\n let fullRelativePath: string;\n if (basePath === '/') {\n fullRelativePath = relativePath.startsWith('/') ? relativePath : '/' + relativePath;\n } else {\n fullRelativePath = joinPath(basePath, relativePath);\n }\n updates.push({ relativePath: fullRelativePath, value: patchValue });\n }\n\n // Use shared helper for cache update and event firing\n this.applyWriteToView(view, updates, isVolatile, serverTimestamp);\n }\n\n /**\n * Detect and fire child_moved events for children that changed position.\n */\n private detectAndFireMoves(\n view: View,\n previousOrder: string[],\n currentOrder: string[],\n previousPositions: Map<string, number>,\n currentPositions: Map<string, number>,\n previousChildSet: Set<string>,\n currentChildSet: Set<string>,\n childMovedSubs: Array<{ callback: SnapshotCallback }>,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (childMovedSubs.length === 0) return;\n\n // For each child that exists in both old and new, check if position changed\n for (const key of currentOrder) {\n if (!previousChildSet.has(key) || !currentChildSet.has(key)) {\n continue; // Skip newly added or removed children\n }\n\n const oldPos = previousPositions.get(key)!;\n const newPos = currentPositions.get(key)!;\n\n // Check if the relative order changed\n // We need to compare the previous sibling, not just the absolute position\n const oldPrevKey = oldPos > 0 ? previousOrder[oldPos - 1] : null;\n const newPrevKey = newPos > 0 ? currentOrder[newPos - 1] : null;\n\n if (oldPrevKey !== newPrevKey) {\n // Position changed - fire child_moved with new previousChildKey\n this.fireChildMoved(view, key, childMovedSubs, newPrevKey, isVolatile, serverTimestamp);\n }\n }\n }\n\n /**\n * Fire child_added callbacks for a child key.\n */\n private fireChildAdded(\n view: View,\n childKey: string,\n subs: Array<{ callback: SnapshotCallback }>,\n previousChildKey: string | null,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n const { value: childValue } = view.getCacheValue(childPath);\n const snapshot = this.createSnapshot?.(childPath, childValue, isVolatile, serverTimestamp);\n\n if (snapshot) {\n for (const entry of subs) {\n try {\n entry.callback(snapshot, previousChildKey);\n } catch (err) {\n console.error('Error in child_added subscription callback:', err);\n }\n }\n }\n }\n\n /**\n * Fire child_changed callbacks for a child key.\n */\n private fireChildChanged(\n view: View,\n childKey: string,\n subs: Array<{ callback: SnapshotCallback }>,\n previousChildKey: string | null,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n const { value: childValue } = view.getCacheValue(childPath);\n const snapshot = this.createSnapshot?.(childPath, childValue, isVolatile, serverTimestamp);\n\n if (snapshot) {\n for (const entry of subs) {\n try {\n entry.callback(snapshot, previousChildKey);\n } catch (err) {\n console.error('Error in child_changed subscription callback:', err);\n }\n }\n }\n }\n\n /**\n * Fire child_removed callbacks for a child key.\n */\n private fireChildRemoved(\n view: View,\n childKey: string,\n subs: Array<{ callback: SnapshotCallback }>,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n // For removed children, value is null\n const snapshot = this.createSnapshot?.(childPath, null, isVolatile, serverTimestamp);\n\n if (snapshot) {\n for (const entry of subs) {\n try {\n // previousChildKey is not meaningful for removed children\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in child_removed subscription callback:', err);\n }\n }\n }\n }\n\n /**\n * Fire child_moved callbacks for a child key.\n */\n private fireChildMoved(\n view: View,\n childKey: string,\n subs: Array<{ callback: SnapshotCallback }>,\n previousChildKey: string | null,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n const { value: childValue } = view.getCacheValue(childPath);\n const snapshot = this.createSnapshot?.(childPath, childValue, isVolatile, serverTimestamp);\n\n if (snapshot) {\n for (const entry of subs) {\n try {\n entry.callback(snapshot, previousChildKey);\n } catch (err) {\n console.error('Error in child_moved subscription callback:', err);\n }\n }\n }\n }\n\n /**\n * Clear all subscriptions (e.g., on disconnect).\n */\n clear(): void {\n for (const view of this.views.values()) {\n view.clear();\n }\n this.views.clear();\n }\n\n /**\n * Check if there are any subscriptions at a path.\n */\n hasSubscriptions(path: string): boolean {\n return this.views.has(path);\n }\n\n /**\n * Check if a path is \"covered\" by an active subscription.\n *\n * A path is covered if:\n * - There's an active 'value' subscription at that exact path, OR\n * - There's an active 'value' subscription at an ancestor path\n */\n isPathCovered(path: string): boolean {\n const normalized = normalizePath(path);\n\n // Check exact match - must have 'value' subscription\n if (this.hasValueSubscription(normalized)) {\n return true;\n }\n\n // Check ancestors - walk up the path tree\n const segments = normalized.split('/').filter((s) => s.length > 0);\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const ancestorPath = i === 0 ? '/' : '/' + segments.slice(0, i).join('/');\n if (this.hasValueSubscription(ancestorPath)) {\n return true;\n }\n }\n\n // Check root\n if (normalized !== '/' && this.hasValueSubscription('/')) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Check if there's a 'value' subscription at a path.\n */\n private hasValueSubscription(path: string): boolean {\n const view = this.views.get(path);\n return view !== undefined && view.hasCallbacksForType('value');\n }\n\n /**\n * Get a cached value if the path is covered by an active subscription.\n */\n getCachedValue(path: string): { value: unknown; found: boolean } {\n const normalized = normalizePath(path);\n\n // First check if the path is covered by a subscription\n if (!this.isPathCovered(normalized)) {\n return { value: undefined, found: false };\n }\n\n // Try to find a View that covers this path\n // Check exact match first\n const exactView = this.views.get(normalized);\n if (exactView) {\n return exactView.getCacheValue(normalized);\n }\n\n // Check ancestors\n const segments = normalized.split('/').filter((s) => s.length > 0);\n for (let i = segments.length - 1; i >= 0; i--) {\n const ancestorPath = i === 0 ? '/' : '/' + segments.slice(0, i).join('/');\n const ancestorView = this.views.get(ancestorPath);\n if (ancestorView && ancestorView.hasCallbacksForType('value')) {\n return ancestorView.getCacheValue(normalized);\n }\n }\n\n // Check root\n if (normalized !== '/') {\n const rootView = this.views.get('/');\n if (rootView && rootView.hasCallbacksForType('value')) {\n return rootView.getCacheValue(normalized);\n }\n }\n\n return { value: undefined, found: false };\n }\n\n /**\n * Get the cache size (for testing/debugging).\n * Returns the number of Views.\n */\n get cacheSize(): number {\n return this.views.size;\n }\n\n /**\n * Clear only the cache, preserving subscription registrations.\n * Used when preparing for reconnect - we keep subscriptions but will get fresh data.\n */\n clearCacheOnly(): void {\n for (const view of this.views.values()) {\n view.clearCache();\n }\n }\n\n /**\n * Re-subscribe to all active subscriptions.\n * Used after reconnecting to restore subscriptions on the server.\n */\n async resubscribeAll(): Promise<void> {\n for (const [path, view] of this.views) {\n const eventTypes = view.getEventTypes();\n if (eventTypes.length > 0) {\n try {\n await this.sendSubscribe?.(path, eventTypes, view.queryParams ?? undefined);\n } catch (err) {\n console.error(`Failed to resubscribe to ${path}:`, err);\n // Continue with other subscriptions even if one fails\n }\n }\n }\n }\n\n /**\n * Get information about active subscriptions (for debugging).\n */\n getActiveSubscriptions(): Array<{ path: string; eventTypes: EventType[]; queryParams?: QueryParams }> {\n const result: Array<{ path: string; eventTypes: EventType[]; queryParams?: QueryParams }> = [];\n\n for (const [path, view] of this.views) {\n const eventTypes = view.getEventTypes();\n const queryParams = view.queryParams ?? undefined;\n result.push({ path, eventTypes, queryParams });\n }\n\n return result;\n }\n\n /**\n * Get a View by path (for testing/debugging).\n */\n getView(path: string): View | undefined {\n return this.views.get(path);\n }\n\n // ============================================\n // Shared Write Application (used by server events and optimistic writes)\n // ============================================\n\n /**\n * Apply write(s) to a View's cache and fire appropriate events.\n * This is the core logic shared between server events and optimistic writes.\n *\n * @param view - The View to update\n * @param updates - Array of {relativePath, value} pairs to apply\n * @param isVolatile - Whether this is a volatile update\n * @param serverTimestamp - Optional server timestamp\n */\n private applyWriteToView(\n view: View,\n updates: Array<{ relativePath: string; value: unknown }>,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n // Capture previous state (including cache snapshot for change detection)\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n const previousCacheJson = JSON.stringify(view.getCache());\n\n // Track which immediate children are affected (for child_* events)\n const affectedChildren = new Set<string>();\n let isFullSnapshot = false;\n\n // Apply all updates\n for (const { relativePath, value } of updates) {\n if (relativePath === '/') {\n // Full snapshot replacement\n view.setCache(value);\n isFullSnapshot = true;\n } else {\n // Extract immediate child key from the path\n const segments = relativePath.split('/').filter((s) => s.length > 0);\n if (segments.length > 0) {\n affectedChildren.add(segments[0]);\n }\n\n // Apply the update\n if (value === null) {\n view.removeCacheChild(relativePath);\n } else {\n view.updateCacheChild(relativePath, value);\n }\n }\n }\n\n // Capture current state\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n const currentCacheJson = JSON.stringify(view.getCache());\n\n // Skip ALL event firing if the cache hasn't actually changed\n // This prevents duplicate events when server confirms our optimistic write\n if (previousCacheJson === currentCacheJson) {\n return;\n }\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getCache();\n const snapshot = this.createSnapshot?.(view.path, fullValue, isVolatile, serverTimestamp);\n if (snapshot) {\n for (const entry of valueSubs) {\n try {\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in value subscription callback:', err);\n }\n }\n }\n }\n\n // Fire child_* events\n const childAddedSubs = view.getCallbacks('child_added');\n const childChangedSubs = view.getCallbacks('child_changed');\n const childRemovedSubs = view.getCallbacks('child_removed');\n const childMovedSubs = view.getCallbacks('child_moved');\n\n // Skip if no child subscriptions\n if (\n childAddedSubs.length === 0 &&\n childChangedSubs.length === 0 &&\n childRemovedSubs.length === 0 &&\n childMovedSubs.length === 0\n ) {\n return;\n }\n\n if (isFullSnapshot) {\n // Full snapshot - compare all children\n for (const key of currentOrder) {\n if (!previousChildSet.has(key)) {\n const prevKey = view.getPreviousChildKey(key);\n this.fireChildAdded(view, key, childAddedSubs, prevKey, isVolatile, serverTimestamp);\n }\n }\n for (const key of previousOrder) {\n if (!currentChildSet.has(key)) {\n this.fireChildRemoved(view, key, childRemovedSubs, isVolatile, serverTimestamp);\n }\n }\n } else {\n // Delta update - fire events for affected children\n for (const childKey of affectedChildren) {\n const wasPresent = previousChildSet.has(childKey);\n const isPresent = currentChildSet.has(childKey);\n\n if (!wasPresent && isPresent) {\n const prevKey = view.getPreviousChildKey(childKey);\n this.fireChildAdded(view, childKey, childAddedSubs, prevKey, isVolatile, serverTimestamp);\n } else if (wasPresent && !isPresent) {\n this.fireChildRemoved(view, childKey, childRemovedSubs, isVolatile, serverTimestamp);\n } else if (wasPresent && isPresent) {\n const prevKey = view.getPreviousChildKey(childKey);\n this.fireChildChanged(view, childKey, childChangedSubs, prevKey, isVolatile, serverTimestamp);\n }\n }\n }\n\n // Detect and fire child_moved events\n const previousPositions = new Map<string, number>();\n previousOrder.forEach((key, idx) => previousPositions.set(key, idx));\n const currentPositions = new Map<string, number>();\n currentOrder.forEach((key, idx) => currentPositions.set(key, idx));\n\n this.detectAndFireMoves(\n view,\n previousOrder,\n currentOrder,\n previousPositions,\n currentPositions,\n previousChildSet,\n currentChildSet,\n childMovedSubs,\n isVolatile,\n serverTimestamp\n );\n }\n\n // ============================================\n // Pending Writes (for local-first)\n // ============================================\n\n /**\n * Find all Views that cover a given write path.\n * A View covers a path if the View's path is an ancestor of or equal to the write path.\n */\n findViewsForWritePath(writePath: string): View[] {\n const normalized = normalizePath(writePath);\n const views: View[] = [];\n\n for (const [viewPath, view] of this.views) {\n // Check if viewPath is an ancestor of or equal to normalized\n if (normalized === viewPath) {\n views.push(view);\n } else if (normalized.startsWith(viewPath + '/')) {\n views.push(view);\n } else if (viewPath === '/') {\n views.push(view);\n }\n }\n\n return views;\n }\n\n /**\n * Get pending write IDs for a write path.\n * Returns the union of pending writes from all Views that cover this path.\n */\n getPendingWriteIdsForPath(writePath: string): string[] {\n const views = this.findViewsForWritePath(writePath);\n const allPendingIds = new Set<string>();\n\n for (const view of views) {\n for (const id of view.getPendingWriteIds()) {\n allPendingIds.add(id);\n }\n }\n\n return Array.from(allPendingIds);\n }\n\n /**\n * Track a pending write for all Views that cover the write path.\n */\n trackPendingWrite(writePath: string, requestId: string): void {\n const views = this.findViewsForWritePath(writePath);\n for (const view of views) {\n view.addPendingWrite(requestId);\n }\n }\n\n /**\n * Clear a pending write from all Views (on ack).\n */\n clearPendingWrite(requestId: string): void {\n for (const view of this.views.values()) {\n view.removePendingWrite(requestId);\n }\n }\n\n /**\n * Handle a nack for a pending write.\n * Collects all tainted request IDs and puts affected Views into recovery mode.\n * Returns the affected Views and all tainted request IDs for local rejection.\n */\n handleWriteNack(requestId: string): { affectedViews: View[]; taintedIds: string[] } {\n const affectedViews: View[] = [];\n const taintedIds = new Set<string>();\n\n // First pass: find affected Views and collect ALL their pending writes\n for (const view of this.views.values()) {\n if (view.isWritePending(requestId)) {\n affectedViews.push(view);\n // Collect all pending writes from this View - they're all tainted\n for (const id of view.getPendingWriteIds()) {\n taintedIds.add(id);\n }\n }\n }\n\n // Second pass: enter recovery (which clears pending writes)\n for (const view of affectedViews) {\n view.enterRecovery();\n }\n\n return { affectedViews, taintedIds: Array.from(taintedIds) };\n }\n\n /**\n * Check if any View covering a path is in recovery mode.\n */\n isPathInRecovery(writePath: string): boolean {\n const views = this.findViewsForWritePath(writePath);\n return views.some(view => view.recovering);\n }\n\n /**\n * Re-subscribe a specific View to get a fresh snapshot.\n * Used during recovery after a nack.\n */\n async resubscribeView(path: string): Promise<void> {\n const view = this.views.get(path);\n if (!view) return;\n\n const eventTypes = view.getEventTypes();\n if (eventTypes.length > 0 && this.sendSubscribe) {\n await this.sendSubscribe(path, eventTypes, view.queryParams ?? undefined);\n }\n }\n\n // ============================================\n // Optimistic Writes (for local-first)\n // ============================================\n\n /**\n * Apply an optimistic write to local cache and fire events.\n * Called before sending the write to the server.\n *\n * @param writePath - The absolute path being written to\n * @param value - The value to write (null for delete)\n * @param requestId - The request ID for pending write tracking\n * @param operation - The type of operation ('set', 'update', 'delete')\n * @returns The Views that were updated\n */\n applyOptimisticWrite(\n writePath: string,\n value: unknown,\n requestId: string,\n operation: 'set' | 'update' | 'delete'\n ): View[] {\n const normalized = normalizePath(writePath);\n const affectedViews = this.findViewsForWritePath(normalized);\n\n if (affectedViews.length === 0) {\n return [];\n }\n\n const updatedViews: View[] = [];\n\n for (const view of affectedViews) {\n // Skip Views that haven't received initial snapshot yet\n if (!view.hasReceivedInitialSnapshot) {\n continue;\n }\n\n // Calculate relative path from View's subscription path\n let relativePath: string;\n if (normalized === view.path) {\n relativePath = '/';\n } else if (view.path === '/') {\n relativePath = normalized;\n } else {\n relativePath = normalized.slice(view.path.length);\n }\n\n // Build updates array based on operation type\n const updates: Array<{ relativePath: string; value: unknown }> = [];\n\n if (operation === 'delete') {\n updates.push({ relativePath, value: null });\n } else if (operation === 'update') {\n // For update, value is a Record<string, unknown> with fields to merge\n const updateObj = value as Record<string, unknown>;\n for (const [key, val] of Object.entries(updateObj)) {\n const updatePath = relativePath === '/' ? '/' + key : relativePath + '/' + key;\n updates.push({ relativePath: updatePath, value: val });\n }\n } else {\n // set operation\n updates.push({ relativePath, value });\n }\n\n // Use shared helper for cache update and event firing\n this.applyWriteToView(view, updates, false, undefined);\n updatedViews.push(view);\n }\n\n return updatedViews;\n }\n}\n","/**\n * WebSocketClient - wrapper around WebSocket with event handling and reconnection support.\n * Works in both browser (native WebSocket) and Node.js (ws package).\n */\n\nimport WebSocketNode from 'ws';\n\n// Use native WebSocket in browser, ws package in Node.js\nconst WebSocketImpl: typeof WebSocket =\n typeof WebSocket !== 'undefined' ? WebSocket : (WebSocketNode as unknown as typeof WebSocket);\n\nexport type WebSocketState = 'disconnected' | 'connecting' | 'connected';\n\nexport interface WebSocketClientOptions {\n onMessage: (data: string) => void;\n onOpen: () => void;\n onClose: (code: number, reason: string) => void;\n onError: (error: Event) => void;\n}\n\nexport class WebSocketClient {\n private ws: WebSocket | null = null;\n private options: WebSocketClientOptions;\n private _state: WebSocketState = 'disconnected';\n\n constructor(options: WebSocketClientOptions) {\n this.options = options;\n }\n\n get state(): WebSocketState {\n return this._state;\n }\n\n get connected(): boolean {\n return this._state === 'connected';\n }\n\n /**\n * Connect to a WebSocket server.\n */\n connect(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this._state !== 'disconnected') {\n reject(new Error('Already connected or connecting'));\n return;\n }\n\n this._state = 'connecting';\n\n try {\n this.ws = new WebSocketImpl(url) as WebSocket;\n } catch (err) {\n this._state = 'disconnected';\n reject(err);\n return;\n }\n\n const onOpen = () => {\n cleanup();\n this._state = 'connected';\n this.setupEventHandlers();\n resolve();\n this.options.onOpen();\n };\n\n const onError = (event: Event) => {\n cleanup();\n this._state = 'disconnected';\n this.ws = null;\n reject(new Error('WebSocket connection failed'));\n this.options.onError(event);\n };\n\n const onClose = (event: CloseEvent) => {\n cleanup();\n this._state = 'disconnected';\n this.ws = null;\n reject(new Error(`WebSocket closed: ${event.code} ${event.reason}`));\n };\n\n const cleanup = () => {\n this.ws?.removeEventListener('open', onOpen);\n this.ws?.removeEventListener('error', onError);\n this.ws?.removeEventListener('close', onClose);\n };\n\n this.ws.addEventListener('open', onOpen);\n this.ws.addEventListener('error', onError);\n this.ws.addEventListener('close', onClose);\n });\n }\n\n /**\n * Set up persistent event handlers after connection is established.\n */\n private setupEventHandlers(): void {\n if (!this.ws) return;\n\n this.ws.addEventListener('message', (event: MessageEvent) => {\n this.options.onMessage(event.data as string);\n });\n\n this.ws.addEventListener('close', (event: CloseEvent) => {\n this._state = 'disconnected';\n this.ws = null;\n this.options.onClose(event.code, event.reason);\n });\n\n this.ws.addEventListener('error', (event: Event) => {\n this.options.onError(event);\n });\n }\n\n /**\n * Send a message over the WebSocket.\n */\n send(data: string): void {\n if (!this.ws || this._state !== 'connected') {\n throw new Error('WebSocket not connected');\n }\n this.ws.send(data);\n }\n\n /**\n * Close the WebSocket connection.\n */\n close(code: number = 1000, reason: string = 'Client disconnect'): void {\n if (this.ws) {\n this.ws.close(code, reason);\n this.ws = null;\n }\n this._state = 'disconnected';\n }\n}\n","/**\n * OnDisconnect - registers operations to perform on the server when this client disconnects.\n */\n\nimport type { LarkDatabase } from './LarkDatabase';\nimport { OnDisconnectAction } from './protocol/constants';\n\nexport class OnDisconnect {\n private readonly _db: LarkDatabase;\n private readonly _path: string;\n\n constructor(db: LarkDatabase, path: string) {\n this._db = db;\n this._path = path;\n }\n\n /**\n * Set a value when disconnected.\n */\n async set(value: unknown): Promise<void> {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.SET, value);\n }\n\n /**\n * Update values when disconnected.\n */\n async update(values: Record<string, unknown>): Promise<void> {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.UPDATE, values);\n }\n\n /**\n * Remove data when disconnected.\n */\n async remove(): Promise<void> {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.DELETE);\n }\n\n /**\n * Cancel any pending onDisconnect handlers at this location.\n */\n async cancel(): Promise<void> {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.CANCEL);\n }\n\n /**\n * Set a value with priority when disconnected.\n * Priority is injected as `.priority` into the value object.\n */\n async setWithPriority(value: unknown, priority: number | string): Promise<void> {\n // Priority can only be set on objects\n if (value === null || value === undefined) {\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.SET, value);\n return;\n }\n\n if (typeof value !== 'object' || Array.isArray(value)) {\n throw new Error('Priority can only be set on object values');\n }\n\n // Inject .priority into the value\n const valueWithPriority = { ...(value as Record<string, unknown>), '.priority': priority };\n await this._db._sendOnDisconnect(this._path, OnDisconnectAction.SET, valueWithPriority);\n }\n}\n","/**\n * Push ID generation - Firebase-compatible chronologically-sortable IDs.\n *\n * Format: 20 characters\n * - First 8 chars: timestamp (milliseconds since epoch, base64-encoded)\n * - Last 12 chars: random suffix (incremented on same-millisecond calls)\n *\n * This ensures:\n * - IDs created later sort after IDs created earlier\n * - IDs created in the same millisecond are unique and still sort correctly\n */\n\nconst PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';\n\n// Persistent state for generating unique IDs within the same millisecond\nlet lastPushTime = 0;\nlet lastRandChars: number[] = [];\n\n/**\n * Generate a Firebase-compatible push ID.\n */\nexport function generatePushId(): string {\n let now = Date.now();\n const duplicateTime = now === lastPushTime;\n lastPushTime = now;\n\n // Encode timestamp in first 8 characters\n const timeChars = new Array(8);\n for (let i = 7; i >= 0; i--) {\n timeChars[i] = PUSH_CHARS.charAt(now % 64);\n now = Math.floor(now / 64);\n }\n\n let id = timeChars.join('');\n\n if (!duplicateTime) {\n // New timestamp - generate fresh random suffix\n lastRandChars = [];\n for (let i = 0; i < 12; i++) {\n lastRandChars.push(Math.floor(Math.random() * 64));\n }\n } else {\n // Same timestamp - increment the random suffix\n // This ensures uniqueness even with multiple calls in the same millisecond\n let i = 11;\n while (i >= 0 && lastRandChars[i] === 63) {\n lastRandChars[i] = 0;\n i--;\n }\n if (i >= 0) {\n lastRandChars[i]++;\n }\n }\n\n // Append random suffix\n for (let i = 0; i < 12; i++) {\n id += PUSH_CHARS.charAt(lastRandChars[i]);\n }\n\n return id;\n}\n","/**\n * DatabaseReference - a reference to a specific path in the database.\n * Immutable - navigation returns new references.\n */\n\nimport type { DataSnapshot } from './DataSnapshot';\nimport type { LarkDatabase } from './LarkDatabase';\nimport { LarkError } from './LarkError';\nimport { OnDisconnect } from './OnDisconnect';\nimport { ErrorCode, EventType } from './protocol/constants';\nimport { QueryParams, TxOperation } from './protocol/messages';\nimport { getKey, getParentPath, joinPath, normalizePath } from './utils/path';\nimport { generatePushId } from './utils/pushid';\n\n/** Result of a transaction operation */\nexport interface TransactionResult {\n /** Whether the transaction was committed (always true on success) */\n committed: boolean;\n /** Snapshot of the data after the transaction */\n snapshot: DataSnapshot;\n}\n\n/** Default maximum retries for optimistic transactions */\nconst DEFAULT_MAX_RETRIES = 25;\n\nexport type SnapshotCallback = (snapshot: DataSnapshot, previousChildKey?: string | null) => void;\n\nexport interface QueryState {\n orderBy?: 'key' | 'priority' | 'child' | 'value';\n orderByChildPath?: string;\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: { value: unknown; key?: string };\n endAt?: { value: unknown; key?: string };\n equalTo?: { value: unknown; key?: string };\n}\n\nexport class DatabaseReference {\n private readonly _db: LarkDatabase;\n private readonly _path: string;\n private readonly _query: QueryState;\n\n constructor(db: LarkDatabase, path: string, query: QueryState = {}) {\n this._db = db;\n this._path = normalizePath(path);\n this._query = query;\n }\n\n /**\n * The path of this reference.\n */\n get path(): string {\n return this._path;\n }\n\n /**\n * The last segment of the path (the \"key\"), or null for root.\n */\n get key(): string | null {\n return getKey(this._path);\n }\n\n /**\n * Get a reference to the parent node, or null if this is root.\n */\n get parent(): DatabaseReference | null {\n if (this._path === '/') return null;\n const parentPath = getParentPath(this._path);\n return new DatabaseReference(this._db, parentPath);\n }\n\n /**\n * Get a reference to the root of the database.\n */\n get root(): DatabaseReference {\n return new DatabaseReference(this._db, '');\n }\n\n // ============================================\n // Navigation\n // ============================================\n\n /**\n * Get a reference to a child path.\n */\n child(path: string): DatabaseReference {\n const childPath = joinPath(this._path, path);\n return new DatabaseReference(this._db, childPath);\n }\n\n // ============================================\n // Write Operations\n // ============================================\n\n /**\n * Set the data at this location, overwriting any existing data.\n *\n * For volatile paths (high-frequency updates), this is fire-and-forget\n * and resolves immediately without waiting for server confirmation.\n */\n async set(value: unknown): Promise<void> {\n if (this._db.isVolatilePath(this._path)) {\n this._db._sendVolatileSet(this._path, value);\n return;\n }\n await this._db._sendSet(this._path, value);\n }\n\n /**\n * Update specific children at this location without overwriting other children.\n *\n * Also supports Firebase-style multi-path updates when keys look like paths\n * (start with '/'). In this mode, each path is written atomically as a transaction.\n *\n * @example\n * ```javascript\n * // Normal update (merge at single path)\n * await ref.update({ score: 10, name: 'Riley' });\n *\n * // Multi-path update (atomic writes to multiple paths)\n * await db.ref().update({\n * '/users/alice/score': 100,\n * '/users/bob/score': 200,\n * '/leaderboard/alice': null // null = delete\n * });\n * ```\n */\n async update(values: Record<string, unknown>): Promise<void> {\n // Check if any key looks like a path (starts with '/')\n const hasPathKeys = Object.keys(values).some((key) => key.startsWith('/'));\n\n if (hasPathKeys) {\n // Multi-path mode: use transaction for atomic writes\n // Note: Transactions are NOT volatile (need reliability for atomicity)\n const ops: TxOperation[] = [];\n\n for (const [key, value] of Object.entries(values)) {\n // Resolve path relative to this ref\n const fullPath = key.startsWith('/')\n ? joinPath(this._path, key)\n : joinPath(this._path, key);\n const normalizedPath = normalizePath(fullPath) || '/';\n\n if (value === null) {\n // null = delete\n ops.push({ o: 'd', p: normalizedPath });\n } else {\n // set the value (not merge, matching Firebase behavior)\n ops.push({ o: 's', p: normalizedPath, v: value });\n }\n }\n\n await this._db._sendTransaction(ops);\n } else {\n // Normal update (merge at single path)\n if (this._db.isVolatilePath(this._path)) {\n this._db._sendVolatileUpdate(this._path, values);\n return;\n }\n await this._db._sendUpdate(this._path, values);\n }\n }\n\n /**\n * Remove the data at this location.\n *\n * For volatile paths, this is fire-and-forget.\n */\n async remove(): Promise<void> {\n if (this._db.isVolatilePath(this._path)) {\n this._db._sendVolatileDelete(this._path);\n return;\n }\n await this._db._sendDelete(this._path);\n }\n\n /**\n * Generate a new child location with a unique key and optionally set its value.\n *\n * If value is provided, sets the value and returns a Promise that resolves\n * to the new reference.\n *\n * If no value is provided, returns a reference immediately with a client-generated\n * push key (you can then call set() on it).\n */\n push(value?: unknown): DatabaseReference | Promise<DatabaseReference> {\n // Always generate key locally (matches Firebase behavior)\n const key = generatePushId();\n const childRef = this.child(key);\n\n if (value === undefined) {\n // No value - return reference immediately\n return childRef;\n }\n\n // Value provided - set it and return promise\n return childRef.set(value).then(() => childRef);\n }\n\n /**\n * Set the data with a priority value for ordering.\n * Priority is injected as `.priority` into the value object.\n */\n async setWithPriority(value: unknown, priority: number | string): Promise<void> {\n // Priority can only be set on objects\n if (value === null || value === undefined) {\n // Setting null/undefined with priority - just set the value\n await this._db._sendSet(this._path, value);\n return;\n }\n\n if (typeof value !== 'object' || Array.isArray(value)) {\n throw new Error('Priority can only be set on object values');\n }\n\n // Inject .priority into the value\n const valueWithPriority = { ...(value as Record<string, unknown>), '.priority': priority };\n await this._db._sendSet(this._path, valueWithPriority);\n }\n\n /**\n * Set the priority of the data at this location.\n * Fetches current value and sets it with the new priority.\n */\n async setPriority(priority: number | string): Promise<void> {\n // Fetch current value and set with new priority\n const snapshot = await this.once();\n const currentVal = snapshot.val();\n\n if (currentVal === null || currentVal === undefined) {\n // No data exists, just set priority on empty object\n await this._db._sendSet(this._path, { '.priority': priority });\n return;\n }\n\n if (typeof currentVal !== 'object' || Array.isArray(currentVal)) {\n throw new Error('Priority can only be set on object values');\n }\n\n await this.setWithPriority(currentVal, priority);\n }\n\n /**\n * Atomically modify the data at this location using optimistic concurrency.\n *\n * The update function receives the current value and should return the new\n * value. If the data changed on the server before the write could be\n * committed, the function is called again with the new value, and the\n * process repeats until successful or the maximum retries are exceeded.\n *\n * @example\n * ```javascript\n * // Increment a counter atomically\n * const result = await ref.transaction(currentValue => {\n * return (currentValue || 0) + 1;\n * });\n * console.log('New value:', result.snapshot.val());\n * ```\n *\n * @param updateFunction - Function that receives current value and returns new value.\n * Return undefined to abort the transaction.\n * @param maxRetries - Maximum number of retries (default: 25)\n * @returns TransactionResult with committed status and final snapshot\n */\n async transaction(\n updateFunction: (currentValue: unknown) => unknown,\n maxRetries: number = DEFAULT_MAX_RETRIES\n ): Promise<TransactionResult> {\n let retries = 0;\n\n while (retries < maxRetries) {\n // Step 1: Read current value\n const currentSnapshot = await this.once();\n const currentValue = currentSnapshot.val();\n\n // Step 2: Call update function\n const newValue = updateFunction(currentValue);\n\n // Step 3: If undefined returned, abort\n if (newValue === undefined) {\n return {\n committed: false,\n snapshot: currentSnapshot,\n };\n }\n\n // Step 4: Build transaction with condition + set\n const ops: TxOperation[] = [\n { o: 'c', p: this._path, v: currentValue },\n { o: 's', p: this._path, v: newValue },\n ];\n\n try {\n // Step 5: Try to commit\n await this._db._sendTransaction(ops);\n\n // Success! Read back the value for the result snapshot\n const finalSnapshot = await this.once();\n return {\n committed: true,\n snapshot: finalSnapshot,\n };\n } catch (error) {\n // Step 6: If condition failed, retry\n if (error instanceof LarkError && error.code === ErrorCode.CONDITION_FAILED) {\n retries++;\n continue;\n }\n // Other errors should propagate\n throw error;\n }\n }\n\n // Max retries exceeded\n throw new LarkError(\n 'max_retries_exceeded',\n `Transaction failed after ${maxRetries} retries`\n );\n }\n\n // ============================================\n // Read Operations\n // ============================================\n\n /**\n * Read the data at this location once.\n */\n async once(eventType: 'value' = 'value'): Promise<DataSnapshot> {\n if (eventType !== 'value') {\n throw new Error('once() only supports \"value\" event type');\n }\n return this._db._sendOnce(this._path, this._buildQueryParams());\n }\n\n // ============================================\n // Subscriptions\n // ============================================\n\n /**\n * Subscribe to events at this location.\n * Returns an unsubscribe function.\n */\n on(eventType: EventType, callback: SnapshotCallback): () => void {\n return this._db._subscribe(this._path, eventType, callback, this._buildQueryParams());\n }\n\n /**\n * Unsubscribe from events.\n * If eventType is specified, removes all listeners of that type.\n * If no eventType, removes ALL listeners at this path.\n */\n off(eventType?: EventType): void {\n if (eventType) {\n this._db._unsubscribeEventType(this._path, eventType);\n } else {\n this._db._unsubscribeAll(this._path);\n }\n }\n\n // ============================================\n // OnDisconnect\n // ============================================\n\n /**\n * Get an OnDisconnect handler for this location.\n */\n onDisconnect(): OnDisconnect {\n return new OnDisconnect(this._db, this._path);\n }\n\n // ============================================\n // Query Modifiers\n // ============================================\n\n /**\n * Order results by key.\n */\n orderByKey(): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n orderBy: 'key',\n });\n }\n\n /**\n * Order results by priority.\n */\n orderByPriority(): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n orderBy: 'priority',\n });\n }\n\n /**\n * Order results by a child key.\n */\n orderByChild(path: string): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n orderBy: 'child',\n orderByChildPath: path,\n });\n }\n\n /**\n * Order results by value.\n */\n orderByValue(): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n orderBy: 'value',\n });\n }\n\n /**\n * Limit to the first N results.\n */\n limitToFirst(limit: number): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n limitToFirst: limit,\n });\n }\n\n /**\n * Limit to the last N results.\n */\n limitToLast(limit: number): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n limitToLast: limit,\n });\n }\n\n /**\n * Start at a specific value/key.\n */\n startAt(value: unknown, key?: string): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n startAt: { value, key },\n });\n }\n\n /**\n * End at a specific value/key.\n */\n endAt(value: unknown, key?: string): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n endAt: { value, key },\n });\n }\n\n /**\n * Filter to items equal to a specific value.\n */\n equalTo(value: unknown, key?: string): DatabaseReference {\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n equalTo: { value, key },\n });\n }\n\n // ============================================\n // Internal Helpers\n // ============================================\n\n /**\n * Build query parameters for wire protocol.\n */\n private _buildQueryParams(): QueryParams | undefined {\n const params: QueryParams = {};\n let hasParams = false;\n\n // Order by\n if (this._query.orderBy === 'key') {\n params.orderBy = 'key';\n hasParams = true;\n } else if (this._query.orderBy === 'priority') {\n params.orderBy = 'priority';\n hasParams = true;\n } else if (this._query.orderBy === 'child') {\n params.orderBy = 'child';\n if (this._query.orderByChildPath) {\n params.orderByChild = this._query.orderByChildPath;\n }\n hasParams = true;\n } else if (this._query.orderBy === 'value') {\n params.orderBy = 'value';\n hasParams = true;\n }\n\n // Limits\n if (this._query.limitToFirst !== undefined) {\n params.limitToFirst = this._query.limitToFirst;\n hasParams = true;\n }\n\n if (this._query.limitToLast !== undefined) {\n params.limitToLast = this._query.limitToLast;\n hasParams = true;\n }\n\n // Range queries\n if (this._query.startAt !== undefined) {\n params.startAt = this._query.startAt.value;\n if (this._query.startAt.key !== undefined) {\n params.startAtKey = this._query.startAt.key;\n }\n hasParams = true;\n }\n\n if (this._query.endAt !== undefined) {\n params.endAt = this._query.endAt.value;\n if (this._query.endAt.key !== undefined) {\n params.endAtKey = this._query.endAt.key;\n }\n hasParams = true;\n }\n\n if (this._query.equalTo !== undefined) {\n params.equalTo = this._query.equalTo.value;\n if (this._query.equalTo.key !== undefined) {\n params.equalToKey = this._query.equalTo.key;\n }\n hasParams = true;\n }\n\n return hasParams ? params : undefined;\n }\n\n /**\n * Returns the absolute URL for this database location.\n * Format: https://db.lark.sh/project/database/path/to/data\n */\n toString(): string {\n const baseUrl = this._db._getBaseUrl();\n // For root, just return base URL; otherwise append path (path already has leading slash)\n if (this._path === '/') {\n return baseUrl;\n }\n return `${baseUrl}${this._path}`;\n }\n}\n","/**\n * DataSnapshot - an immutable snapshot of data at a location.\n * Received in event callbacks and from once() operations.\n *\n * Priority Handling:\n * - Priority is stored as a regular child value `.priority` in the data\n * - val() strips `.priority` from returned objects (so app code doesn't see it)\n * - getPriority() reads from the data's `.priority` field\n * - This matches Firebase behavior where priority is metadata, not user data\n */\n\nimport type { DatabaseReference } from './DatabaseReference';\nimport type { LarkDatabase } from './LarkDatabase';\nimport { getKey, getValueAtPath, joinPath, normalizePath } from './utils/path';\n\nexport class DataSnapshot {\n private readonly _data: unknown;\n private readonly _path: string;\n private readonly _db: LarkDatabase;\n private readonly _volatile: boolean;\n private readonly _serverTimestamp: number | null;\n\n constructor(\n data: unknown,\n path: string,\n db: LarkDatabase,\n options: { volatile?: boolean; serverTimestamp?: number | null } = {}\n ) {\n this._data = data;\n this._path = normalizePath(path);\n this._db = db;\n this._volatile = options.volatile ?? false;\n this._serverTimestamp = options.serverTimestamp ?? null;\n }\n\n /**\n * Get a DatabaseReference for the location of this snapshot.\n */\n get ref(): DatabaseReference {\n return this._db.ref(this._path);\n }\n\n /**\n * Get the last segment of the path (the \"key\"), or null for root.\n */\n get key(): string | null {\n return getKey(this._path);\n }\n\n /**\n * Get the data value, with `.priority` stripped if present.\n * Priority is internal metadata and should not be visible to app code.\n */\n val(): unknown {\n // Strip .priority from objects\n if (this._data && typeof this._data === 'object' && !Array.isArray(this._data)) {\n const data = this._data as Record<string, unknown>;\n if ('.priority' in data) {\n const { '.priority': _, ...rest } = data;\n // If the object only had .priority, return the original (empty object case)\n return Object.keys(rest).length > 0 ? rest : (Object.keys(data).length === 1 ? null : rest);\n }\n }\n return this._data;\n }\n\n /**\n * Check if data exists at this location (is not null/undefined).\n */\n exists(): boolean {\n return this._data !== null && this._data !== undefined;\n }\n\n /**\n * Get a child snapshot at the specified path.\n */\n child(path: string): DataSnapshot {\n const childPath = joinPath(this._path, path);\n const childData = getValueAtPath(this._data, path);\n return new DataSnapshot(childData, childPath, this._db, {\n volatile: this._volatile,\n serverTimestamp: this._serverTimestamp,\n });\n }\n\n /**\n * Check if this snapshot has any children.\n */\n hasChildren(): boolean {\n if (typeof this._data !== 'object' || this._data === null) {\n return false;\n }\n return Object.keys(this._data).length > 0;\n }\n\n /**\n * Check if this snapshot has a specific child.\n */\n hasChild(path: string): boolean {\n const childData = getValueAtPath(this._data, path);\n return childData !== undefined && childData !== null;\n }\n\n /**\n * Get the number of children.\n */\n numChildren(): number {\n if (typeof this._data !== 'object' || this._data === null) {\n return 0;\n }\n return Object.keys(this._data).length;\n }\n\n /**\n * Iterate over children. Return true from callback to stop iteration.\n */\n forEach(callback: (child: DataSnapshot) => boolean | void): void {\n if (typeof this._data !== 'object' || this._data === null) {\n return;\n }\n\n const keys = Object.keys(this._data);\n for (const key of keys) {\n const childSnap = this.child(key);\n if (callback(childSnap) === true) {\n break;\n }\n }\n }\n\n /**\n * Get the priority of the data at this location.\n * Priority is stored as `.priority` in the data object.\n */\n getPriority(): number | string | null {\n if (this._data && typeof this._data === 'object' && !Array.isArray(this._data)) {\n const priority = (this._data as Record<string, unknown>)['.priority'];\n if (typeof priority === 'number' || typeof priority === 'string') {\n return priority;\n }\n }\n return null;\n }\n\n /**\n * Check if this snapshot was from a volatile (high-frequency) update.\n * This is a Lark extension not present in Firebase.\n */\n isVolatile(): boolean {\n return this._volatile;\n }\n\n /**\n * Get the server timestamp for this snapshot (milliseconds since Unix epoch).\n * Only present on volatile value events. Use deltas between timestamps for\n * interpolation rather than absolute times to avoid clock sync issues.\n * This is a Lark extension not present in Firebase.\n */\n getServerTimestamp(): number | null {\n return this._serverTimestamp;\n }\n\n /**\n * Export the snapshot data as JSON (alias for val()).\n */\n toJSON(): unknown {\n return this._data;\n }\n}\n","/**\n * Simple JWT payload decoder (no verification - server validates).\n * JWTs are three base64url-encoded parts separated by dots: header.payload.signature\n */\n\nexport interface JwtPayload {\n sub: string; // User ID\n aud: string; // Database ID\n project: string; // Project ID\n server: string; // Server ID\n provider: string; // Auth provider\n claims: Record<string, unknown>; // Custom claims\n iss: string; // Issuer\n iat: number; // Issued at\n exp: number; // Expiration\n}\n\n/**\n * Decode a JWT payload without verification.\n * The server validates the token - we just need to read the claims.\n */\nexport function decodeJwtPayload(token: string): JwtPayload {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid JWT format');\n }\n\n const payload = parts[1];\n\n // Base64url decode (replace - with +, _ with /, add padding)\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n\n // Decode - works in both browser and Node.js\n let decoded: string;\n if (typeof atob === 'function') {\n // Browser\n decoded = atob(padded);\n } else {\n // Node.js\n decoded = Buffer.from(padded, 'base64').toString('utf-8');\n }\n\n return JSON.parse(decoded) as JwtPayload;\n}\n","/**\n * Volatile path matching utilities.\n *\n * Volatile paths are patterns where a wildcard matches exactly one path segment.\n * These paths use unreliable transport for better performance (WebTransport datagrams when available).\n */\n\n/**\n * Check if a path matches any of the volatile path patterns.\n *\n * Pattern matching rules:\n * - Wildcard matches exactly one path segment\n * - Patterns and paths are compared segment by segment\n * - If pattern is exhausted before path, it's still a match (descendant rule)\n * - If path is exhausted before pattern, no match\n *\n * @param path - The path to check\n * @param patterns - Array of volatile patterns with wildcards\n * @returns true if the path matches any pattern\n */\nexport function isVolatilePath(path: string, patterns: string[] | null | undefined): boolean {\n if (!patterns || patterns.length === 0) {\n return false;\n }\n\n // Remove leading slash and split into segments, filter empty\n const segments = path.replace(/^\\//, '').split('/').filter(s => s.length > 0);\n\n return patterns.some((pattern) => {\n // Remove leading slash from pattern too and split\n const patternSegments = pattern.replace(/^\\//, '').split('/').filter(s => s.length > 0);\n\n // If path has fewer segments than pattern, no match\n // (path must be at or below the pattern level)\n if (segments.length < patternSegments.length) {\n return false;\n }\n\n // Check each pattern segment matches the corresponding path segment\n // (* matches any single segment)\n for (let i = 0; i < patternSegments.length; i++) {\n const p = patternSegments[i];\n const s = segments[i];\n if (p !== '*' && p !== s) {\n return false;\n }\n }\n\n // Pattern exhausted - all segments matched\n // Path may have more segments (descendant) which is fine\n return true;\n });\n}\n","/**\n * LarkDatabase - the main entry point for connecting to a Lark database.\n */\n\nimport { Coordinator } from './connection/Coordinator';\nimport { JoinCompleteResponse, MessageQueue } from './connection/MessageQueue';\nimport { PendingWriteManager } from './connection/PendingWriteManager';\nimport { SubscriptionManager, SnapshotCallback } from './connection/SubscriptionManager';\nimport { WebSocketClient } from './connection/WebSocketClient';\nimport { DatabaseReference } from './DatabaseReference';\nimport { DataSnapshot } from './DataSnapshot';\nimport { LarkError } from './LarkError';\nimport { DEFAULT_COORDINATOR_URL, EventType } from './protocol/constants';\nimport {\n ClientMessage,\n JoinMessage,\n ServerMessage,\n isAckMessage,\n isEventMessage,\n isNackMessage,\n isPingMessage,\n QueryParams,\n TxOperation,\n TransactionMessage,\n} from './protocol/messages';\nimport { decodeJwtPayload } from './utils/jwt';\nimport { normalizePath } from './utils/path';\nimport { isVolatilePath } from './utils/volatile';\n\nexport interface ConnectOptions {\n /** User's auth token (from your auth system) */\n token?: string;\n\n /** Connect anonymously (server assigns a UID) */\n anonymous?: boolean;\n\n /** Coordinator URL (default: https://db.lark.dev) */\n coordinator?: string;\n}\n\nexport interface AuthInfo {\n uid: string;\n provider: string;\n token: Record<string, unknown>;\n}\n\n// ============================================\n// Transaction Types (Public API)\n// ============================================\n\n/** A set operation in a transaction */\nexport interface TransactionSetOp {\n op: 'set';\n path: string;\n value: unknown;\n}\n\n/** An update (merge) operation in a transaction */\nexport interface TransactionUpdateOp {\n op: 'update';\n path: string;\n value: Record<string, unknown>;\n}\n\n/** A delete operation in a transaction */\nexport interface TransactionDeleteOp {\n op: 'delete';\n path: string;\n}\n\n/** A condition check in a transaction (for compare-and-swap) */\nexport interface TransactionConditionOp {\n op: 'condition';\n path: string;\n value: unknown;\n}\n\n/** A single operation in a transaction (array syntax) */\nexport type TransactionOp =\n | TransactionSetOp\n | TransactionUpdateOp\n | TransactionDeleteOp\n | TransactionConditionOp;\n\n/** Object syntax for transactions: path -> value (null = delete) */\nexport type TransactionObject = Record<string, unknown>;\n\ntype ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';\n\n// Reconnection constants\nconst RECONNECT_BASE_DELAY_MS = 1000; // Start with 1 second\nconst RECONNECT_MAX_DELAY_MS = 30000; // Cap at 30 seconds\nconst RECONNECT_JITTER_FACTOR = 0.5; // Add up to 50% random jitter\n\nexport class LarkDatabase {\n private _state: ConnectionState = 'disconnected';\n private _auth: AuthInfo | null = null;\n private _databaseId: string | null = null;\n private _coordinatorUrl: string | null = null;\n private _volatilePaths: string[] = [];\n\n // Reconnection state\n private _connectionId: string | null = null;\n private _connectOptions: ConnectOptions | null = null;\n private _intentionalDisconnect = false;\n private _reconnectAttempt = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n private ws: WebSocketClient | null = null;\n private messageQueue: MessageQueue;\n private subscriptionManager: SubscriptionManager;\n private pendingWrites: PendingWriteManager;\n\n // Event callbacks\n private connectCallbacks = new Set<() => void>();\n private disconnectCallbacks = new Set<() => void>();\n private errorCallbacks = new Set<(error: Error) => void>();\n private reconnectingCallbacks = new Set<() => void>();\n\n constructor() {\n this.messageQueue = new MessageQueue();\n this.subscriptionManager = new SubscriptionManager();\n this.pendingWrites = new PendingWriteManager();\n }\n\n // ============================================\n // Connection State\n // ============================================\n\n /**\n * Whether the database is currently connected.\n */\n get connected(): boolean {\n return this._state === 'connected';\n }\n\n /**\n * Whether the database is currently attempting to reconnect.\n */\n get reconnecting(): boolean {\n return this._state === 'reconnecting';\n }\n\n /**\n * Current connection state.\n */\n get state(): ConnectionState {\n return this._state;\n }\n\n /**\n * Current auth info, or null if not connected.\n */\n get auth(): AuthInfo | null {\n return this._auth;\n }\n\n /**\n * @internal Get the base URL for reference toString().\n */\n _getBaseUrl(): string {\n if (this._coordinatorUrl && this._databaseId) {\n return `${this._coordinatorUrl}/${this._databaseId}`;\n }\n return 'lark://';\n }\n\n /**\n * Get the volatile path patterns from the server.\n * These patterns indicate which paths should use unreliable transport.\n * WebSocket always uses reliable transport, but this is stored for future UDP support.\n */\n get volatilePaths(): string[] {\n return this._volatilePaths;\n }\n\n /**\n * Check if there are any pending writes waiting for acknowledgment.\n * Useful for showing \"saving...\" indicators in UI.\n */\n hasPendingWrites(): boolean {\n return this.pendingWrites.pendingCount > 0;\n }\n\n /**\n * Get the number of pending writes waiting for acknowledgment.\n */\n getPendingWriteCount(): number {\n return this.pendingWrites.pendingCount;\n }\n\n /**\n * Clear all pending writes.\n * Call this if you don't want to retry writes on reconnect.\n */\n clearPendingWrites(): void {\n this.pendingWrites.clear();\n }\n\n // ============================================\n // Connection Management\n // ============================================\n\n /**\n * Connect to a database.\n *\n * @param databaseId - Database ID in format \"project/database\"\n * @param options - Connection options (token, anonymous, coordinator URL)\n */\n async connect(databaseId: string, options: ConnectOptions = {}): Promise<void> {\n if (this._state !== 'disconnected') {\n throw new Error('Already connected or connecting');\n }\n\n // Store options for potential reconnect\n this._connectOptions = options;\n this._intentionalDisconnect = false;\n\n await this.performConnect(databaseId, options);\n }\n\n /**\n * Internal connect implementation used by both initial connect and reconnect.\n */\n private async performConnect(\n databaseId: string,\n options: ConnectOptions,\n isReconnect = false\n ): Promise<void> {\n const previousState = this._state;\n this._state = isReconnect ? 'reconnecting' : 'connecting';\n this._databaseId = databaseId;\n this._coordinatorUrl = options.coordinator || DEFAULT_COORDINATOR_URL;\n\n try {\n // Step 1: Get connection details from coordinator\n const coordinatorUrl = this._coordinatorUrl;\n const coordinator = new Coordinator(coordinatorUrl);\n\n const connectResponse = await coordinator.connect(databaseId, {\n token: options.token,\n anonymous: options.anonymous,\n });\n\n // Step 2: Connect to the server via WebSocket\n const wsUrl = connectResponse.ws_url;\n\n this.ws = new WebSocketClient({\n onMessage: this.handleMessage.bind(this),\n onOpen: () => {}, // Handled in connect flow\n onClose: this.handleClose.bind(this),\n onError: this.handleError.bind(this),\n });\n\n await this.ws.connect(wsUrl);\n\n // Step 3: Send join message (include previous connection ID if reconnecting)\n const requestId = this.messageQueue.nextRequestId();\n const joinMessage: JoinMessage = {\n o: 'j',\n t: connectResponse.token,\n r: requestId,\n };\n\n // Include previous connection ID for reconnect deduplication\n if (this._connectionId) {\n joinMessage.pcid = this._connectionId;\n }\n\n this.send(joinMessage);\n\n // Wait for join complete (returns volatile paths and connection ID)\n const joinResponse = (await this.messageQueue.registerRequest(requestId)) as JoinCompleteResponse;\n this._volatilePaths = joinResponse.volatilePaths;\n this._connectionId = joinResponse.connectionId;\n\n // Step 4: Extract auth info from JWT and update state\n const jwtPayload = decodeJwtPayload(connectResponse.token);\n this._auth = {\n uid: jwtPayload.sub,\n provider: jwtPayload.provider,\n token: jwtPayload.claims || {},\n };\n\n this._state = 'connected';\n this._reconnectAttempt = 0; // Reset reconnect attempt counter on success\n\n // Initialize subscription manager (only on first connect)\n if (!isReconnect) {\n this.subscriptionManager.initialize({\n sendSubscribe: this.sendSubscribeMessage.bind(this),\n sendUnsubscribe: this.sendUnsubscribeMessage.bind(this),\n createSnapshot: this.createSnapshot.bind(this),\n });\n }\n\n // On reconnect, restore subscriptions and retry pending writes\n if (isReconnect) {\n await this.restoreAfterReconnect();\n }\n\n // Notify listeners\n this.connectCallbacks.forEach((cb) => cb());\n } catch (error) {\n // On reconnect failure, schedule another attempt\n if (isReconnect) {\n this._state = 'reconnecting';\n this.scheduleReconnect();\n return;\n }\n\n // On initial connect failure, clean up completely\n this._state = 'disconnected';\n this._auth = null;\n this._databaseId = null;\n this._connectOptions = null;\n this._connectionId = null;\n this.ws?.close();\n this.ws = null;\n throw error;\n }\n }\n\n /**\n * Disconnect from the database.\n * This triggers onDisconnect hooks on the server.\n */\n async disconnect(): Promise<void> {\n if (this._state === 'disconnected') {\n return;\n }\n\n const wasConnected = this._state === 'connected';\n\n // Mark as intentional to prevent auto-reconnect\n this._intentionalDisconnect = true;\n\n // Cancel any pending reconnect\n if (this._reconnectTimer) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n\n // Send leave message for graceful disconnect\n if (wasConnected && this.ws) {\n try {\n const requestId = this.messageQueue.nextRequestId();\n this.send({ o: 'l', r: requestId });\n // Wait briefly for ack, but don't block\n await Promise.race([\n this.messageQueue.registerRequest(requestId),\n new Promise((resolve) => setTimeout(resolve, 1000)),\n ]);\n } catch {\n // Ignore errors during disconnect\n }\n }\n\n this.cleanupFull();\n\n // Fire disconnect callbacks\n if (wasConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n }\n\n /**\n * Full cleanup - clears all state including subscriptions.\n * Used for intentional disconnect.\n */\n private cleanupFull(): void {\n this.ws?.close();\n this.ws = null;\n this._state = 'disconnected';\n this._auth = null;\n this._databaseId = null;\n this._volatilePaths = [];\n this._coordinatorUrl = null;\n this._connectionId = null;\n this._connectOptions = null;\n this._reconnectAttempt = 0;\n this.subscriptionManager.clear();\n this.messageQueue.rejectAll(new Error('Connection closed'));\n this.pendingWrites.clear();\n }\n\n /**\n * Partial cleanup - preserves state needed for reconnect.\n * Used for unexpected disconnect.\n */\n private cleanupForReconnect(): void {\n this.ws?.close();\n this.ws = null;\n this._auth = null;\n // Keep: _databaseId, _coordinatorUrl, _connectionId, _connectOptions\n // Keep: subscriptionManager subscriptions (but clear cache)\n // Keep: pendingWrites\n this.subscriptionManager.clearCacheOnly();\n this.messageQueue.rejectAll(new Error('Connection closed'));\n }\n\n /**\n * Schedule a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(): void {\n this._reconnectAttempt++;\n\n // Calculate delay with exponential backoff and jitter\n const baseDelay = Math.min(\n RECONNECT_BASE_DELAY_MS * Math.pow(2, this._reconnectAttempt - 1),\n RECONNECT_MAX_DELAY_MS\n );\n const jitter = baseDelay * RECONNECT_JITTER_FACTOR * Math.random();\n const delay = baseDelay + jitter;\n\n this._reconnectTimer = setTimeout(() => {\n this._reconnectTimer = null;\n this.attemptReconnect();\n }, delay);\n }\n\n /**\n * Attempt to reconnect to the database.\n */\n private async attemptReconnect(): Promise<void> {\n if (this._intentionalDisconnect || !this._databaseId || !this._connectOptions) {\n return;\n }\n\n try {\n await this.performConnect(this._databaseId, this._connectOptions, true);\n } catch {\n // performConnect already handles scheduling next attempt on failure\n }\n }\n\n /**\n * Restore subscriptions and retry pending writes after reconnect.\n */\n private async restoreAfterReconnect(): Promise<void> {\n // Re-subscribe to all active subscriptions\n await this.subscriptionManager.resubscribeAll();\n\n // Retry all pending writes\n await this.retryPendingWrites();\n }\n\n /**\n * Retry all pending writes after reconnect.\n */\n private async retryPendingWrites(): Promise<void> {\n const pendingWrites = this.pendingWrites.getPendingWrites();\n\n for (const write of pendingWrites) {\n try {\n // Re-send the write with the same request ID\n // Server will deduplicate if already processed\n switch (write.operation) {\n case 'set': {\n const message: ClientMessage = {\n o: 's',\n p: write.path,\n v: write.value,\n r: write.oid, // Use original request ID\n };\n this.send(message);\n break;\n }\n case 'update': {\n const message: ClientMessage = {\n o: 'u',\n p: write.path,\n v: write.value as Record<string, unknown>,\n r: write.oid,\n };\n this.send(message);\n break;\n }\n case 'delete': {\n const message: ClientMessage = {\n o: 'd',\n p: write.path,\n r: write.oid,\n };\n this.send(message);\n break;\n }\n case 'transaction': {\n const message: TransactionMessage = {\n o: 'tx',\n ops: write.value as TxOperation[],\n r: write.oid,\n };\n this.send(message);\n break;\n }\n }\n } catch (error) {\n // If we fail to send, the connection might have dropped again\n // The next reconnect will retry\n console.error('Failed to retry pending write:', error);\n }\n }\n }\n\n // ============================================\n // Reference Access\n // ============================================\n\n /**\n * Get a reference to a path in the database.\n */\n ref(path: string = ''): DatabaseReference {\n return new DatabaseReference(this, path);\n }\n\n // ============================================\n // Transactions\n // ============================================\n\n /**\n * Execute an atomic transaction with multiple operations.\n *\n * Supports two syntaxes:\n *\n * **Object syntax** (like Firebase multi-path update):\n * ```javascript\n * await db.transaction({\n * '/users/alice/name': 'Alice',\n * '/users/alice/score': 100,\n * '/temp/data': null // null = delete\n * });\n * ```\n *\n * **Array syntax** (explicit operations):\n * ```javascript\n * await db.transaction([\n * { op: 'set', path: '/users/alice/name', value: 'Alice' },\n * { op: 'update', path: '/metadata', value: { lastUpdated: '...' } },\n * { op: 'delete', path: '/temp/data' },\n * { op: 'condition', path: '/counter', value: 5 } // CAS check\n * ]);\n * ```\n *\n * All operations are atomic: either all succeed or all fail.\n * Conditions are checked first; if any fail, the transaction is rejected\n * with error code 'condition_failed'.\n */\n async transaction(operations: TransactionOp[] | TransactionObject): Promise<void> {\n let txOps: TxOperation[];\n\n if (Array.isArray(operations)) {\n // Array syntax - convert to wire format\n txOps = operations.map((op) => this.convertToTxOp(op));\n } else {\n // Object syntax - convert paths to set/delete operations\n txOps = this.convertObjectToTxOps(operations);\n }\n\n await this._sendTransaction(txOps);\n }\n\n /**\n * Convert a public TransactionOp to wire format TxOperation.\n */\n private convertToTxOp(op: TransactionOp): TxOperation {\n const path = normalizePath(op.path) || '/';\n\n switch (op.op) {\n case 'set':\n return { o: 's', p: path, v: op.value };\n case 'update':\n return { o: 'u', p: path, v: op.value };\n case 'delete':\n return { o: 'd', p: path };\n case 'condition':\n return { o: 'c', p: path, v: op.value };\n default:\n throw new Error(`Unknown transaction operation: ${(op as TransactionOp).op}`);\n }\n }\n\n /**\n * Convert object syntax to wire format TxOperations.\n * Each path becomes a set operation, null values become deletes.\n */\n private convertObjectToTxOps(obj: TransactionObject): TxOperation[] {\n const ops: TxOperation[] = [];\n\n for (const [path, value] of Object.entries(obj)) {\n const normalizedPath = normalizePath(path) || '/';\n\n if (value === null) {\n // null = delete\n ops.push({ o: 'd', p: normalizedPath });\n } else {\n // set the value\n ops.push({ o: 's', p: normalizedPath, v: value });\n }\n }\n\n return ops;\n }\n\n /**\n * @internal Send a transaction to the server.\n */\n async _sendTransaction(ops: TxOperation[]): Promise<void> {\n const requestId = this.messageQueue.nextRequestId();\n\n // Track the pending write using request ID (store ops as value for potential retry)\n this.pendingWrites.trackWrite(requestId, 'transaction', '/', ops);\n\n const message: TransactionMessage = {\n o: 'tx',\n ops,\n r: requestId,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n // ============================================\n // Connection Events\n // ============================================\n\n /**\n * Register a callback for when connection is established.\n * Returns an unsubscribe function.\n */\n onConnect(callback: () => void): () => void {\n this.connectCallbacks.add(callback);\n return () => this.connectCallbacks.delete(callback);\n }\n\n /**\n * Register a callback for when connection is lost.\n * Returns an unsubscribe function.\n */\n onDisconnect(callback: () => void): () => void {\n this.disconnectCallbacks.add(callback);\n return () => this.disconnectCallbacks.delete(callback);\n }\n\n /**\n * Register a callback for connection errors.\n * Returns an unsubscribe function.\n */\n onError(callback: (error: Error) => void): () => void {\n this.errorCallbacks.add(callback);\n return () => this.errorCallbacks.delete(callback);\n }\n\n /**\n * Register a callback for when reconnection attempts begin.\n * This fires when an unexpected disconnect occurs and auto-reconnect starts.\n * Returns an unsubscribe function.\n */\n onReconnecting(callback: () => void): () => void {\n this.reconnectingCallbacks.add(callback);\n return () => this.reconnectingCallbacks.delete(callback);\n }\n\n // ============================================\n // Internal: Message Handling\n // ============================================\n\n private handleMessage(data: string): void {\n let message: ServerMessage;\n try {\n message = JSON.parse(data) as ServerMessage;\n } catch {\n console.error('Failed to parse message:', data);\n return;\n }\n\n // Handle ping - respond with pong immediately\n if (isPingMessage(message)) {\n this.ws?.send(JSON.stringify({ o: 'po' }));\n return;\n }\n\n // Clear pending writes on ack/nack using request ID\n if (isAckMessage(message)) {\n this.pendingWrites.onAck(message.a);\n // Also clear from View-level pending write tracking\n this.subscriptionManager.clearPendingWrite(message.a);\n } else if (isNackMessage(message)) {\n this.pendingWrites.onNack(message.n);\n\n // Skip recovery for condition_failed (handled by transaction retry)\n if (message.e !== 'condition_failed') {\n // Log the error\n console.error(`Write failed (${message.e}): ${message.m || message.e}`);\n\n // Handle nack: collect tainted IDs and put affected Views into recovery mode\n const { affectedViews, taintedIds } = this.subscriptionManager.handleWriteNack(message.n);\n\n if (affectedViews.length > 0) {\n // Reject all tainted writes locally (except the original nack, which is handled by messageQueue below)\n for (const id of taintedIds) {\n if (id !== message.n) {\n this.messageQueue.rejectLocally(\n id,\n new LarkError('write_tainted', 'Write cancelled: depends on failed write')\n );\n // Also clear from pending writes manager\n this.pendingWrites.onNack(id);\n }\n }\n\n // Re-subscribe affected Views to get fresh snapshots\n for (const view of affectedViews) {\n this.subscriptionManager.resubscribeView(view.path).catch((err) => {\n console.error(`Failed to re-subscribe ${view.path} during recovery:`, err);\n });\n }\n }\n } else {\n // For condition_failed, just clear the pending write\n this.subscriptionManager.clearPendingWrite(message.n);\n }\n }\n\n // Check if this is a response to a pending request\n if (this.messageQueue.handleMessage(message)) {\n return;\n }\n\n // Otherwise, check if it's an event\n if (isEventMessage(message)) {\n this.subscriptionManager.handleEvent(message);\n }\n }\n\n private handleClose(code: number, reason: string): void {\n // Already disconnected (e.g., through disconnect() method) - nothing to do\n if (this._state === 'disconnected') {\n return;\n }\n\n const wasConnected = this._state === 'connected';\n const wasReconnecting = this._state === 'reconnecting';\n\n // Check if this was an intentional disconnect\n // (disconnect() already handled cleanup and callbacks, but the ws close event\n // may fire later - just clean up any remaining state)\n if (this._intentionalDisconnect) {\n this.cleanupFull();\n if (wasConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n return;\n }\n\n // Only attempt to reconnect if we have the necessary state\n // (databaseId and connectOptions from a previous successful connection)\n const canReconnect = this._databaseId && this._connectOptions;\n\n // Unexpected disconnect - attempt to reconnect if we can\n if ((wasConnected || wasReconnecting) && canReconnect) {\n this._state = 'reconnecting';\n this.cleanupForReconnect();\n\n // Notify listeners that we're reconnecting\n this.reconnectingCallbacks.forEach((cb) => cb());\n if (wasConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n\n // Schedule reconnection\n this.scheduleReconnect();\n } else {\n // Can't reconnect - do full cleanup\n this.cleanupFull();\n if (wasConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n }\n }\n\n private handleError(event: Event): void {\n const error = new Error('WebSocket error');\n this.errorCallbacks.forEach((cb) => cb(error));\n }\n\n // ============================================\n // Internal: Sending Messages\n // ============================================\n\n private send(message: ClientMessage): void {\n if (!this.ws || !this.ws.connected) {\n throw new LarkError('not_connected', 'Not connected to database');\n }\n this.ws.send(JSON.stringify(message));\n }\n\n /**\n * @internal Send a set operation.\n * Note: Priority is now part of the value (as .priority), not a separate field.\n */\n async _sendSet(path: string, value: unknown): Promise<void> {\n const normalizedPath = normalizePath(path) || '/';\n\n // Check if any affected View is recovering - reject writes during recovery\n if (this.subscriptionManager.isPathInRecovery(normalizedPath)) {\n throw new LarkError('view_recovering', 'Cannot write: View is recovering from failed write');\n }\n\n const requestId = this.messageQueue.nextRequestId();\n\n // Get pending write IDs for this path (from subscribed Views)\n const pendingWriteIds = this.subscriptionManager.getPendingWriteIdsForPath(normalizedPath);\n\n // Track the pending write using request ID\n this.pendingWrites.trackWrite(requestId, 'set', normalizedPath, value);\n\n // Also track in SubscriptionManager for View-level pending write tracking\n this.subscriptionManager.trackPendingWrite(normalizedPath, requestId);\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, value, requestId, 'set');\n\n const message: ClientMessage = {\n o: 's',\n p: normalizedPath,\n v: value,\n r: requestId,\n pw: pendingWriteIds.length > 0 ? pendingWriteIds : undefined,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send an update operation.\n */\n async _sendUpdate(path: string, values: Record<string, unknown>): Promise<void> {\n const normalizedPath = normalizePath(path) || '/';\n\n // Check if any affected View is recovering - reject writes during recovery\n if (this.subscriptionManager.isPathInRecovery(normalizedPath)) {\n throw new LarkError('view_recovering', 'Cannot write: View is recovering from failed write');\n }\n\n const requestId = this.messageQueue.nextRequestId();\n\n // Get pending write IDs for this path (from subscribed Views)\n const pendingWriteIds = this.subscriptionManager.getPendingWriteIdsForPath(normalizedPath);\n\n // Track the pending write using request ID\n this.pendingWrites.trackWrite(requestId, 'update', normalizedPath, values);\n\n // Also track in SubscriptionManager for View-level pending write tracking\n this.subscriptionManager.trackPendingWrite(normalizedPath, requestId);\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, values, requestId, 'update');\n\n const message: ClientMessage = {\n o: 'u',\n p: normalizedPath,\n v: values,\n r: requestId,\n pw: pendingWriteIds.length > 0 ? pendingWriteIds : undefined,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send a delete operation.\n */\n async _sendDelete(path: string): Promise<void> {\n const normalizedPath = normalizePath(path) || '/';\n\n // Check if any affected View is recovering - reject writes during recovery\n if (this.subscriptionManager.isPathInRecovery(normalizedPath)) {\n throw new LarkError('view_recovering', 'Cannot write: View is recovering from failed write');\n }\n\n const requestId = this.messageQueue.nextRequestId();\n\n // Get pending write IDs for this path (from subscribed Views)\n const pendingWriteIds = this.subscriptionManager.getPendingWriteIdsForPath(normalizedPath);\n\n // Track the pending write using request ID\n this.pendingWrites.trackWrite(requestId, 'delete', normalizedPath);\n\n // Also track in SubscriptionManager for View-level pending write tracking\n this.subscriptionManager.trackPendingWrite(normalizedPath, requestId);\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, null, requestId, 'delete');\n\n const message: ClientMessage = {\n o: 'd',\n p: normalizedPath,\n r: requestId,\n pw: pendingWriteIds.length > 0 ? pendingWriteIds : undefined,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n // ============================================\n // Volatile Write Operations (Fire-and-Forget)\n // ============================================\n\n /**\n * @internal Send a volatile set operation (fire-and-forget).\n *\n * Volatile writes skip:\n * - Recovery checks (volatile paths don't participate in recovery)\n * - Request ID generation (no ack expected)\n * - Pending write tracking (no retry on reconnect)\n * - pw field (no dependency tracking)\n *\n * The write is applied optimistically to local cache for UI feedback,\n * but we don't await server confirmation.\n */\n _sendVolatileSet(path: string, value: unknown): void {\n const normalizedPath = normalizePath(path) || '/';\n\n // Apply optimistically to local cache (for UI feedback)\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, value, '', 'set');\n\n // Fire-and-forget: no request ID, no pending tracking, no await\n const message: ClientMessage = {\n o: 's',\n p: normalizedPath,\n v: value,\n };\n this.send(message);\n }\n\n /**\n * @internal Send a volatile update operation (fire-and-forget).\n */\n _sendVolatileUpdate(path: string, values: Record<string, unknown>): void {\n const normalizedPath = normalizePath(path) || '/';\n\n // Apply optimistically to local cache (for UI feedback)\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, values, '', 'update');\n\n // Fire-and-forget\n const message: ClientMessage = {\n o: 'u',\n p: normalizedPath,\n v: values,\n };\n this.send(message);\n }\n\n /**\n * @internal Send a volatile delete operation (fire-and-forget).\n */\n _sendVolatileDelete(path: string): void {\n const normalizedPath = normalizePath(path) || '/';\n\n // Apply optimistically to local cache (for UI feedback)\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, null, '', 'delete');\n\n // Fire-and-forget\n const message: ClientMessage = {\n o: 'd',\n p: normalizedPath,\n };\n this.send(message);\n }\n\n /**\n * Check if a path is a volatile path (high-frequency, fire-and-forget).\n */\n isVolatilePath(path: string): boolean {\n return isVolatilePath(path, this._volatilePaths);\n }\n\n /**\n * @internal Send a once (read) operation.\n *\n * This method first checks if the data is available in the local cache\n * (from an active subscription). If so, it returns the cached value\n * immediately without a server round-trip.\n *\n * Cache is only used when:\n * - No query parameters are specified (queries may filter/order differently)\n * - The path is covered by an active 'value' subscription\n * - We have cached data available\n */\n async _sendOnce(path: string, query?: QueryParams): Promise<DataSnapshot> {\n const normalizedPath = normalizePath(path) || '/';\n\n // Try cache first (only for non-query requests)\n if (!query) {\n const cached = this.subscriptionManager.getCachedValue(normalizedPath);\n if (cached.found) {\n return new DataSnapshot(cached.value, path, this);\n }\n }\n\n // Cache miss or query specified - fetch from server\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'o',\n p: normalizedPath,\n r: requestId,\n // Spread query params at top level (not nested in 'q')\n ...query,\n };\n this.send(message);\n const value = await this.messageQueue.registerRequest(requestId);\n return new DataSnapshot(value, path, this);\n }\n\n /**\n * @internal Send an onDisconnect operation.\n */\n async _sendOnDisconnect(\n path: string,\n action: string,\n value?: unknown\n ): Promise<void> {\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'od',\n p: normalizePath(path) || '/',\n a: action as 's' | 'u' | 'd' | 'c',\n r: requestId,\n };\n if (value !== undefined) {\n message.v = value;\n }\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send a subscribe message to server.\n */\n private async sendSubscribeMessage(path: string, eventTypes: string[], queryParams?: QueryParams): Promise<void> {\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'sb',\n p: normalizePath(path) || '/',\n e: eventTypes,\n r: requestId,\n ...queryParams,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send an unsubscribe message to server.\n */\n private async sendUnsubscribeMessage(path: string): Promise<void> {\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'us',\n p: normalizePath(path) || '/',\n r: requestId,\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Create a DataSnapshot from event data.\n */\n private createSnapshot(\n path: string,\n value: unknown,\n volatile: boolean,\n serverTimestamp?: number\n ): DataSnapshot {\n return new DataSnapshot(value, path, this, {\n volatile,\n serverTimestamp: serverTimestamp ?? null,\n });\n }\n\n // ============================================\n // Internal: Subscription Management\n // ============================================\n\n /**\n * @internal Subscribe to events at a path.\n */\n _subscribe(path: string, eventType: EventType, callback: SnapshotCallback, queryParams?: QueryParams): () => void {\n return this.subscriptionManager.subscribe(path, eventType, callback, queryParams);\n }\n\n /**\n * @internal Unsubscribe from a specific event type at a path.\n */\n _unsubscribeEventType(path: string, eventType: EventType): void {\n this.subscriptionManager.unsubscribeEventType(path, eventType);\n }\n\n /**\n * @internal Unsubscribe from all events at a path.\n */\n _unsubscribeAll(path: string): void {\n this.subscriptionManager.unsubscribeAll(path);\n }\n}\n"],"mappings":"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","names":[]}