@lark-sh/client 0.1.21 → 0.1.22
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.
- package/dist/{chunk-HHBHX2EM.mjs → chunk-T5RXZAER.mjs} +3 -26
- package/dist/chunk-T5RXZAER.mjs.map +1 -0
- package/dist/fb-v8/index.js +2 -25
- package/dist/fb-v8/index.js.map +1 -1
- package/dist/fb-v8/index.mjs +1 -1
- package/dist/index.js +2 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-HHBHX2EM.mjs.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/connection/WebSocketTransport.ts","../src/connection/WebTransportClient.ts","../src/connection/createTransport.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/protocol/constants.ts","../src/OnDisconnect.ts","../src/utils/hash.ts","../src/utils/pushid.ts","../src/utils/validation.ts","../src/DatabaseReference.ts","../src/DataSnapshot.ts","../src/utils/volatile.ts","../src/LarkDatabase.ts"],"sourcesContent":["/**\n * @lark-sh/client - JavaScript client for Lark real-time database\n *\n * @example\n * ```typescript\n * import { LarkDatabase } from '@lark-sh/client';\n *\n * const db = new LarkDatabase();\n * await db.connect('my-project/my-database', { anonymous: true });\n *\n * // Write data\n * await db.ref('players/abc').set({ name: 'Riley', score: 0 });\n *\n * // Subscribe to changes\n * const unsubscribe = db.ref('players').on('child_changed', (snap) => {\n * console.log(`Player ${snap.key} changed:`, snap.val());\n * });\n *\n * // Later: unsubscribe\n * unsubscribe();\n *\n * // Disconnect\n * await db.disconnect();\n * ```\n */\n\n// Main classes\nexport { LarkDatabase, ServerValue } from './LarkDatabase';\nexport type {\n ConnectOptions,\n AuthInfo,\n TransactionOp,\n TransactionSetOp,\n TransactionUpdateOp,\n TransactionDeleteOp,\n TransactionConditionOp,\n TransactionObject,\n} from './LarkDatabase';\n\nexport { DatabaseReference, ThenableReference } from './DatabaseReference';\nexport type { SnapshotCallback, QueryState, TransactionResult } from './DatabaseReference';\n\nexport { DataSnapshot } from './DataSnapshot';\n\nexport { OnDisconnect } from './OnDisconnect';\n\nexport { LarkError } from './LarkError';\n\n// Protocol types (for advanced usage)\nexport type { EventType } from './protocol/constants';\nexport type { QueryParams } from './protocol/messages';\n\n// Connection utilities (for advanced usage)\nexport { PendingWriteManager } from './connection/PendingWriteManager';\nexport type { WriteOperation, PendingWrite } from './connection/PendingWriteManager';\n\n// Transport (for advanced usage)\nexport type { TransportType } from './connection/createTransport';\n\n// Utility functions (for advanced usage)\nexport { generatePushId } from './utils/pushid';\nexport { isVolatilePath } from './utils/volatile';\n","/**\n * WebSocketTransport - WebSocket implementation of the Transport interface.\n * Works in both browser (native WebSocket) and Node.js (ws package).\n */\n\nimport WebSocketNode from 'ws';\nimport { Transport, TransportState, TransportOptions } from './Transport';\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 class WebSocketTransport implements Transport {\n private ws: WebSocket | null = null;\n private options: TransportOptions;\n private _state: TransportState = 'disconnected';\n\n constructor(options: TransportOptions) {\n this.options = options;\n }\n\n get state(): TransportState {\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 const error = new Error('WebSocket connection failed');\n reject(error);\n this.options.onError(error);\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', () => {\n this.options.onError(new Error('WebSocket error'));\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 * Send a volatile message.\n * WebSocket doesn't have a true unreliable channel, so this just calls send().\n */\n sendVolatile(data: string): void {\n this.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 * WebTransportClient - WebTransport implementation of the Transport interface.\n *\n * Uses QUIC-based WebTransport for:\n * - Reliable bidirectional stream (with 4-byte length prefix framing)\n * - Unreliable datagrams for volatile writes\n *\n * Browser-only (no Node.js support). Falls back to WebSocket if unavailable.\n */\n\nimport { Transport, TransportState, TransportOptions } from './Transport';\n\n// TypeScript declarations for WebTransport API\n// These are not in lib.dom.d.ts yet\ndeclare class WebTransport {\n constructor(url: string);\n ready: Promise<void>;\n closed: Promise<{ closeCode: number; reason: string }>;\n close(options?: { closeCode?: number; reason?: string }): void;\n createBidirectionalStream(): Promise<WebTransportBidirectionalStream>;\n datagrams: {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n };\n}\n\ninterface WebTransportBidirectionalStream {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n}\n\nexport class WebTransportClient implements Transport {\n private transport: WebTransport | null = null;\n private writer: WritableStreamDefaultWriter<Uint8Array> | null = null;\n private datagramWriter: WritableStreamDefaultWriter<Uint8Array> | null = null;\n private options: TransportOptions;\n private _state: TransportState = 'disconnected';\n private readLoopActive = false;\n private datagramLoopActive = false;\n\n constructor(options: TransportOptions) {\n this.options = options;\n }\n\n get state(): TransportState {\n return this._state;\n }\n\n get connected(): boolean {\n return this._state === 'connected';\n }\n\n /**\n * Connect to a WebTransport server.\n */\n async connect(url: string): Promise<void> {\n if (this._state !== 'disconnected') {\n throw new Error('Already connected or connecting');\n }\n\n this._state = 'connecting';\n\n try {\n // Create WebTransport connection\n this.transport = new WebTransport(url);\n await this.transport.ready;\n\n // Open bidirectional stream for reliable messages\n const stream = await this.transport.createBidirectionalStream();\n this.writer = stream.writable.getWriter();\n\n // Get datagram writer for volatile messages\n this.datagramWriter = this.transport.datagrams.writable.getWriter();\n\n this._state = 'connected';\n\n // Start reading from reliable stream\n this.readLoop(stream.readable.getReader());\n\n // Start reading datagrams\n this.datagramLoop(this.transport.datagrams.readable.getReader());\n\n // Handle connection close\n this.transport.closed\n .then(({ closeCode, reason }) => {\n this.handleClose(closeCode, reason);\n })\n .catch(() => {\n this.handleClose(0, 'Connection failed');\n });\n\n this.options.onOpen();\n } catch (err) {\n this._state = 'disconnected';\n this.transport = null;\n this.writer = null;\n this.datagramWriter = null;\n throw err;\n }\n }\n\n /**\n * Read messages from the reliable stream.\n * Messages are framed with 4-byte big-endian length prefix.\n */\n private async readLoop(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<void> {\n this.readLoopActive = true;\n let buffer = new Uint8Array(0);\n\n try {\n while (this.readLoopActive) {\n const { value, done } = await reader.read();\n if (done) break;\n\n // Append new data to buffer\n const newBuffer = new Uint8Array(buffer.length + value.length);\n newBuffer.set(buffer);\n newBuffer.set(value, buffer.length);\n buffer = newBuffer;\n\n // Parse complete messages\n while (buffer.length >= 4) {\n const view = new DataView(buffer.buffer, buffer.byteOffset);\n const msgLength = view.getUint32(0, false); // big-endian\n\n if (buffer.length < 4 + msgLength) break; // Not enough data yet\n\n // Extract message\n const msgBytes = buffer.slice(4, 4 + msgLength);\n const json = new TextDecoder().decode(msgBytes);\n\n // Deliver to handler\n try {\n this.options.onMessage(json);\n } catch (err) {\n console.error('Error handling message:', err);\n }\n\n // Remove processed bytes\n buffer = buffer.slice(4 + msgLength);\n }\n }\n } catch (err) {\n if (this.readLoopActive) {\n console.error('Read loop error:', err);\n }\n }\n }\n\n /**\n * Read volatile events from datagrams.\n * No framing needed - each datagram is a complete JSON message.\n */\n private async datagramLoop(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<void> {\n this.datagramLoopActive = true;\n\n try {\n while (this.datagramLoopActive) {\n const { value, done } = await reader.read();\n if (done) break;\n\n const json = new TextDecoder().decode(value);\n\n // Deliver to handler\n try {\n this.options.onMessage(json);\n } catch (err) {\n console.error('Error handling datagram:', err);\n }\n }\n } catch (err) {\n if (this.datagramLoopActive) {\n console.error('Datagram loop error:', err);\n }\n }\n }\n\n /**\n * Handle connection close.\n */\n private handleClose(code: number, reason: string): void {\n if (this._state === 'disconnected') return;\n\n this.readLoopActive = false;\n this.datagramLoopActive = false;\n this._state = 'disconnected';\n this.transport = null;\n this.writer = null;\n this.datagramWriter = null;\n\n this.options.onClose(code, reason);\n }\n\n /**\n * Send a message over the reliable stream.\n * Uses 4-byte big-endian length prefix.\n */\n send(data: string): void {\n if (!this.writer || this._state !== 'connected') {\n throw new Error('WebTransport not connected');\n }\n\n const encoder = new TextEncoder();\n const msgBytes = encoder.encode(data);\n\n // Create length prefix (4 bytes, big-endian)\n const lengthBuffer = new ArrayBuffer(4);\n new DataView(lengthBuffer).setUint32(0, msgBytes.length, false); // false = big-endian\n\n // Send length + message\n // Note: We can't await in a sync method, so we fire and let errors bubble up\n this.writer.write(new Uint8Array(lengthBuffer)).catch((err) => {\n console.error('Failed to write length prefix:', err);\n });\n this.writer.write(msgBytes).catch((err) => {\n console.error('Failed to write message:', err);\n });\n }\n\n /**\n * Send a volatile message via datagram (unreliable).\n * No framing needed - each datagram is a complete message.\n */\n sendVolatile(data: string): void {\n if (!this.datagramWriter || this._state !== 'connected') {\n throw new Error('WebTransport not connected');\n }\n\n const encoder = new TextEncoder();\n const msgBytes = encoder.encode(data);\n\n // Fire and forget - datagrams are unreliable by design\n this.datagramWriter.write(msgBytes).catch(() => {\n // Ignore datagram write errors - they're expected for unreliable transport\n });\n }\n\n /**\n * Close the WebTransport connection.\n */\n close(code: number = 0, reason: string = 'Client disconnect'): void {\n this.readLoopActive = false;\n this.datagramLoopActive = false;\n\n if (this.writer) {\n this.writer.releaseLock();\n this.writer = null;\n }\n\n if (this.datagramWriter) {\n this.datagramWriter.releaseLock();\n this.datagramWriter = null;\n }\n\n if (this.transport) {\n this.transport.close({ closeCode: code, reason });\n this.transport = null;\n }\n\n this._state = 'disconnected';\n }\n}\n\n/**\n * Check if WebTransport is available in the current environment.\n */\nexport function isWebTransportAvailable(): boolean {\n return typeof globalThis !== 'undefined' && 'WebTransport' in globalThis;\n}\n","/**\n * Transport factory - creates appropriate transport based on availability and options.\n */\n\nimport { Transport, TransportOptions } from './Transport';\nimport { WebSocketTransport } from './WebSocketTransport';\nimport { WebTransportClient, isWebTransportAvailable } from './WebTransportClient';\n\nexport type TransportType = 'auto' | 'websocket' | 'webtransport';\n\nexport interface CreateTransportOptions {\n /** Force a specific transport type. Default: 'auto' */\n transport?: TransportType;\n /** Timeout for WebTransport connection attempt in ms. Default: 2000 */\n webtransportTimeout?: number;\n}\n\n/**\n * Transform WebSocket URL to WebTransport URL.\n * wss://projectId.larkdb.net/ws → https://projectId.larkdb.net:7778-7809/wt\n *\n * Uses a random port in range 7778-7809 for load balancing.\n */\nexport function wsUrlToWtUrl(wsUrl: string): string {\n const url = new URL(wsUrl);\n\n // Change protocol: wss → https, ws → http\n url.protocol = url.protocol === 'wss:' ? 'https:' : 'http:';\n\n // Choose random port in range 7778-7809 (inclusive) for load balancing\n const port = 7778 + Math.floor(Math.random() * 32);\n url.port = String(port);\n\n // Change path from /ws to /wt\n url.pathname = '/wt';\n\n return url.toString();\n}\n\n/**\n * Result of createTransport - includes both the transport and info about what was created.\n */\nexport interface TransportResult {\n transport: Transport;\n type: 'websocket' | 'webtransport';\n url: string;\n}\n\n/**\n * Create a transport connection.\n *\n * By default, attempts WebTransport first (if available), falling back to WebSocket.\n * Can be forced to a specific transport type via options.\n *\n * @param wsUrl - The WebSocket URL (e.g., wss://projectId.larkdb.net/ws)\n * @param transportOptions - Callbacks for message handling\n * @param options - Transport type preferences\n */\nexport async function createTransport(\n wsUrl: string,\n transportOptions: TransportOptions,\n options: CreateTransportOptions = {}\n): Promise<TransportResult> {\n const preferredType = options.transport || 'auto';\n\n // Determine if we should try WebTransport\n const shouldTryWebTransport =\n (preferredType === 'auto' || preferredType === 'webtransport') && isWebTransportAvailable();\n\n // If explicitly requested webtransport but not available, throw\n if (preferredType === 'webtransport' && !isWebTransportAvailable()) {\n throw new Error('WebTransport is not available in this environment');\n }\n\n // Try WebTransport first if appropriate\n if (shouldTryWebTransport) {\n const wtUrl = wsUrlToWtUrl(wsUrl);\n const wtTransport = new WebTransportClient(transportOptions);\n const timeout = options.webtransportTimeout ?? 2000;\n\n // Create a timeout promise that we can clean up\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error('WebTransport connection timeout')), timeout);\n });\n\n try {\n await Promise.race([wtTransport.connect(wtUrl), timeoutPromise]);\n return {\n transport: wtTransport,\n type: 'webtransport',\n url: wtUrl,\n };\n } catch (err) {\n // Clean up the transport on failure/timeout\n try {\n wtTransport.close();\n } catch {\n // Ignore cleanup errors\n }\n // If explicitly requested webtransport, don't fall back\n if (preferredType === 'webtransport') {\n throw err;\n }\n // Otherwise fall through to WebSocket\n console.warn('WebTransport connection failed, falling back to WebSocket:', err);\n } finally {\n // Always clear the timeout to prevent unhandled rejections\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n // Use WebSocket\n const wsTransport = new WebSocketTransport(transportOptions);\n await wsTransport.connect(wsUrl);\n\n return {\n transport: wsTransport,\n type: 'websocket',\n url: wsUrl,\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 d: string; // database ID (e.g., \"project/database\")\n r: string; // request ID\n pcid?: string; // previous connection ID (for reconnect deduplication)\n}\n\nexport interface AuthMessage {\n o: 'au'; // operation: auth\n t: string; // token (empty string for anonymous)\n r: string; // request ID\n}\n\nexport interface UnauthMessage {\n o: 'ua'; // operation: unauth (sign out)\n r: string; // request ID\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 r: string; // request ID\n tag?: number; // client-local tag for routing events to correct View (non-default queries)\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 startAfter?: unknown;\n startAfterKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n endBefore?: unknown;\n endBeforeKey?: 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 startAfter?: unknown; // startAfter value (exclusive)\n startAfterKey?: string; // startAfter key tie-breaker\n endAt?: unknown; // endAt value\n endAtKey?: string; // endAt key tie-breaker\n endBefore?: unknown; // endBefore value (exclusive)\n endBeforeKey?: string; // endBefore 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 tag?: number; // client-local tag (same as sent on subscribe)\n // Query parameters (optional) - same format as SubscribeMessage\n // Server uses these to identify which specific subscription to remove\n orderBy?: string;\n orderByChild?: string;\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: unknown;\n startAtKey?: string;\n startAfter?: unknown;\n startAfterKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n endBefore?: unknown;\n endBeforeKey?: string;\n equalTo?: unknown;\n equalToKey?: string;\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 startAfter?: unknown;\n startAfterKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n endBefore?: unknown;\n endBeforeKey?: 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\n// Condition with direct value comparison (for primitives)\nexport interface TxConditionValueOp {\n o: 'c'; // condition (compare-and-swap check)\n p: string; // path\n v: unknown; // expected value (for primitives: null, boolean, number, string)\n}\n\n// Condition with hash comparison (for complex objects/arrays)\nexport interface TxConditionHashOp {\n o: 'c'; // condition (compare-and-swap check)\n p: string; // path\n h: string; // SHA-256 hash of JCS-canonicalized value\n}\n\nexport type TxConditionOp = TxConditionValueOp | TxConditionHashOp;\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 | AuthMessage\n | UnauthMessage\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 sp?: string; // subscription path (for subscription revocation due to auth change)\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 tag?: number; // tag for routing to specific View (non-default queries)\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 tag?: number; // tag for routing to specific View (non-default queries)\n}\n\n// Volatile batch event - batches multiple volatile updates across subscriptions\n// Server batches volatile events in 50ms intervals to reduce message overhead\nexport interface VolatileBatchEventMessage {\n ev: 'vb'; // event type: volatile batch\n b: Record<string, Record<string, unknown>>; // subscriptionPath → relativePath → value\n ts?: number; // server timestamp in ms (for latency compensation / interpolation)\n}\n\nexport type EventMessage = PutEventMessage | PatchEventMessage | VolatileBatchEventMessage;\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 st?: number; // server time in ms (for calculating serverTimeOffset)\n}\n\nexport interface AuthCompleteMessage {\n ac: string; // auth complete with request ID\n au: string; // UID (empty string for anonymous)\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 | AuthCompleteMessage\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 isAuthCompleteMessage(msg: ServerMessage): msg is AuthCompleteMessage {\n return 'ac' 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 isVolatileBatchEventMessage(msg: ServerMessage): msg is VolatileBatchEventMessage {\n return 'ev' in msg && (msg as EventMessage).ev === 'vb';\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 isAuthCompleteMessage,\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 serverTime: number | null;\n}\n\n/** Response from auth complete message */\nexport interface AuthCompleteResponse {\n uid: string | null; // null for anonymous (empty string from server)\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 serverTime: message.st ?? null,\n };\n pending.resolve(response);\n return true;\n }\n }\n\n // Auth complete (includes UID)\n if (isAuthCompleteMessage(message)) {\n const pending = this.pending.get(message.ac);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.ac);\n // Resolve with auth response (empty string from server means anonymous)\n const response: AuthCompleteResponse = {\n uid: message.au || 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 // Normalize undefined to null (null = no data at path)\n pending.resolve(message.ov ?? null);\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 * Sorting utilities for client-side data ordering.\n *\n * Implements sorting rules matching the server:\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 value 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 type-aware 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 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 no queryParams, default to priority ordering\n if (!queryParams) {\n return getNestedValue(value, '.priority');\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 if (queryParams.orderBy === 'key') {\n return null; // orderByKey uses key, not value\n }\n\n // No explicit orderBy - check if there are range filters or limits\n // If so, default to priority ordering (matching server behavior)\n const hasRangeFilter =\n queryParams.startAt !== undefined ||\n queryParams.startAfter !== undefined ||\n queryParams.endAt !== undefined ||\n queryParams.endBefore !== undefined ||\n queryParams.equalTo !== undefined;\n\n const hasLimit =\n queryParams.limitToFirst !== undefined ||\n queryParams.limitToLast !== undefined;\n\n if (hasRangeFilter || hasLimit) {\n // Default to priority when range filters or limits exist but no orderBy specified\n return getNestedValue(value, '.priority');\n }\n\n // No ordering constraints - key is used\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 explicitly ordering by key, just compare keys\n if (queryParams?.orderBy === 'key') {\n return compareKeys(a.key, b.key);\n }\n\n // Compare by sort value first (priority by default, or specified orderBy)\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 // Check if this is a wrapped primitive (only .value and .priority keys)\n // Wrapped primitives have no children to sort\n const keys = Object.keys(obj);\n if (\n keys.length === 2 &&\n '.value' in obj &&\n '.priority' in obj\n ) {\n return [];\n }\n\n for (const key of keys) {\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, compareValues, createSortEntries, 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\n/**\n * Expand path-like keys in an update object into nested structure.\n * Multi-path updates treat keys with '/' as paths.\n * E.g., { 'child/increment': 42 } becomes { child: { increment: 42 } }\n */\nfunction expandPathKeys(obj: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n if (key.includes('/')) {\n // Path-like key - expand into nested structure\n const segments = key.split('/').filter(s => s.length > 0);\n let current = result;\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 current[segments[segments.length - 1]] = value;\n } else {\n // Regular key - just assign\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Deep merge two objects, with source overwriting target.\n * Handles nested path expansions correctly.\n */\nfunction deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {\n const result = { ...target };\n\n for (const [key, value] of Object.entries(source)) {\n if (value === null) {\n // null = delete\n delete result[key];\n } else if (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n result[key] !== null &&\n typeof result[key] === 'object' &&\n !Array.isArray(result[key])\n ) {\n // Both are objects - deep merge\n result[key] = deepMerge(result[key] as Record<string, unknown>, value as Record<string, unknown>);\n } else {\n // Overwrite\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/** A pending write operation that hasn't been ACKed yet */\nexport interface PendingWriteEntry {\n requestId: string;\n relativePath: string; // Relative to this View's path ('/' for the view itself)\n value: unknown;\n operation: 'set' | 'update' | 'delete';\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 (computed from display cache) */\n private _orderedChildren: string[] = [];\n\n /** Server cache: what the server has told us (baseline) */\n private _serverCache: unknown = null;\n\n /** Whether we've received the initial snapshot from the server */\n private _hasReceivedInitialSnapshot = false;\n\n /** Pending write operations that haven't been ACKed yet (for local-first) */\n private _pendingWriteData: PendingWriteEntry[] = [];\n\n /** Cached display value (server + pending writes merged) - invalidated when either changes */\n private _displayCacheValid = false;\n private _displayCache: unknown = null;\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 // Invalidate display cache and re-sort\n this._displayCacheValid = false;\n this.recomputeOrderedChildren();\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 (Dual Cache: Server + Pending Writes)\n // ============================================\n\n /**\n * Set the server cache (what the server told us).\n * This is the baseline before pending writes are applied.\n * Deep clones the value to ensure each View has its own copy.\n */\n setServerCache(value: unknown): void {\n // Deep clone to ensure this View has its own copy (important for multi-view)\n this._serverCache = this.deepClone(value);\n this._hasReceivedInitialSnapshot = true;\n this.invalidateDisplayCache();\n }\n\n /**\n * Get the server cache (baseline without pending writes).\n */\n getServerCache(): unknown {\n return this._serverCache;\n }\n\n /**\n * Get the display cache (server + pending writes merged).\n * This is what should be shown to the user.\n */\n getDisplayCache(): unknown {\n if (!this._displayCacheValid) {\n this._displayCache = this.computeMergedCache();\n this._displayCacheValid = true;\n }\n return this._displayCache;\n }\n\n /**\n * Alias for getDisplayCache() - this is what callbacks receive.\n */\n getCache(): unknown {\n return this.getDisplayCache();\n }\n\n /**\n * Invalidate the display cache (call when serverCache or pendingWrites change).\n */\n private invalidateDisplayCache(): void {\n this._displayCacheValid = false;\n this.recomputeOrderedChildren();\n }\n\n /**\n * Compute the merged cache (serverCache + pendingWrites), with query constraints applied.\n */\n private computeMergedCache(): unknown {\n let result: unknown;\n\n if (this._pendingWriteData.length === 0) {\n result = this._serverCache;\n } else {\n // Deep clone server cache to avoid mutations\n result = this.deepClone(this._serverCache);\n\n // Apply each pending write in order\n for (const write of this._pendingWriteData) {\n result = this.applyWrite(result, write);\n }\n }\n\n // Apply query constraints (range filtering and limits) to the merged result\n return this.applyQueryConstraints(result);\n }\n\n /**\n * Apply query constraints (range filtering and limits) to data.\n * This ensures the displayCache only contains data matching the query.\n */\n private applyQueryConstraints(data: unknown): unknown {\n // No constraints to apply if no query params or data isn't an object\n if (!this._queryParams) {\n return data;\n }\n\n if (data === null || typeof data !== 'object' || Array.isArray(data)) {\n return data;\n }\n\n const obj = data as Record<string, unknown>;\n\n // Check if we have any filtering/limiting to do\n const hasRangeFilter =\n this._queryParams.startAt !== undefined ||\n this._queryParams.startAfter !== undefined ||\n this._queryParams.endAt !== undefined ||\n this._queryParams.endBefore !== undefined ||\n this._queryParams.equalTo !== undefined;\n const hasLimit =\n this._queryParams.limitToFirst !== undefined ||\n this._queryParams.limitToLast !== undefined;\n\n if (!hasRangeFilter && !hasLimit) {\n return data;\n }\n\n // Get sorted entries\n let entries = createSortEntries(obj, this._queryParams);\n\n // Apply range filters (startAt, endAt, startAfter, endBefore, equalTo)\n if (hasRangeFilter) {\n entries = this.filterByRange(entries);\n }\n\n // Apply limits (limitToFirst, limitToLast)\n if (hasLimit) {\n entries = this.applyLimits(entries);\n }\n\n // Convert back to object\n if (entries.length === 0) {\n return null;\n }\n\n const result: Record<string, unknown> = {};\n for (const entry of entries) {\n result[entry.key] = entry.value;\n }\n\n return result;\n }\n\n /**\n * Filter entries by range constraints (startAt, endAt, startAfter, endBefore, equalTo).\n */\n private filterByRange(entries: SortEntry[]): SortEntry[] {\n if (!this._queryParams) return entries;\n\n const {\n orderBy,\n startAt, startAtKey,\n startAfter, startAfterKey,\n endAt, endAtKey,\n endBefore, endBeforeKey,\n equalTo, equalToKey\n } = this._queryParams;\n\n // For orderByKey, the startAt/endAt values are compared against the key directly\n const isOrderByKey = orderBy === 'key';\n\n return entries.filter(entry => {\n // For orderByKey, compare against key; otherwise compare against sortValue\n const compareValue = isOrderByKey ? entry.key : entry.sortValue;\n const compareFn = isOrderByKey\n ? (a: unknown, b: unknown) => compareKeys(a as string, b as string)\n : compareValues;\n\n // equalTo filter - most restrictive, check first\n if (equalTo !== undefined) {\n const cmp = compareFn(compareValue, equalTo);\n if (cmp !== 0) return false;\n if (equalToKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, equalToKey);\n if (keyCmp !== 0) return false;\n }\n return true;\n }\n\n // startAt filter (inclusive)\n if (startAt !== undefined) {\n const cmp = compareFn(compareValue, startAt);\n if (cmp < 0) return false;\n if (cmp === 0 && startAtKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, startAtKey);\n if (keyCmp < 0) return false;\n }\n }\n\n // startAfter filter (exclusive)\n if (startAfter !== undefined) {\n const cmp = compareFn(compareValue, startAfter);\n if (cmp < 0) return false;\n if (cmp === 0) {\n if (startAfterKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, startAfterKey);\n if (keyCmp <= 0) return false;\n } else {\n // startAfter without key means exclude all with same value\n return false;\n }\n }\n }\n\n // endAt filter (inclusive)\n if (endAt !== undefined) {\n const cmp = compareFn(compareValue, endAt);\n if (cmp > 0) return false;\n if (cmp === 0 && endAtKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, endAtKey);\n if (keyCmp > 0) return false;\n }\n }\n\n // endBefore filter (exclusive)\n if (endBefore !== undefined) {\n const cmp = compareFn(compareValue, endBefore);\n if (cmp > 0) return false;\n if (cmp === 0) {\n if (endBeforeKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, endBeforeKey);\n if (keyCmp >= 0) return false;\n } else {\n // endBefore without key means exclude all with same value\n return false;\n }\n }\n }\n\n return true;\n });\n }\n\n /**\n * Apply limit constraints (limitToFirst, limitToLast) to entries.\n * Entries are already sorted, so we just slice.\n */\n private applyLimits(entries: SortEntry[]): SortEntry[] {\n if (!this._queryParams) return entries;\n\n const { limitToFirst, limitToLast } = this._queryParams;\n\n if (limitToFirst !== undefined) {\n return entries.slice(0, limitToFirst);\n }\n\n if (limitToLast !== undefined) {\n return entries.slice(-limitToLast);\n }\n\n return entries;\n }\n\n /**\n * Apply a single write operation to a value.\n */\n private applyWrite(base: unknown, write: PendingWriteEntry): unknown {\n const { relativePath, value, operation } = write;\n\n if (operation === 'delete') {\n if (relativePath === '/') {\n return null;\n }\n return this.deleteAtPath(base, relativePath);\n }\n\n if (operation === 'set') {\n if (relativePath === '/') {\n return value;\n }\n return this.setAtPath(base, relativePath, value);\n }\n\n if (operation === 'update') {\n // Expand path-like keys (e.g., 'child/increment' -> { child: { increment: ... } })\n const expanded = expandPathKeys(value as Record<string, unknown>);\n\n if (relativePath === '/') {\n // Merge at root using deep merge (handles nested structures correctly)\n if (base === null || base === undefined || typeof base !== 'object') {\n base = {};\n }\n return deepMerge(base as Record<string, unknown>, expanded);\n }\n // Get current value at path, merge, set back\n const current = getValueAtPath(base, relativePath);\n let merged: unknown;\n if (current && typeof current === 'object' && !Array.isArray(current)) {\n merged = deepMerge(current as Record<string, unknown>, expanded);\n } else {\n merged = expanded;\n }\n return this.setAtPath(base, relativePath, merged);\n }\n\n return base;\n }\n\n /**\n * Set a value at a path in an object, creating intermediate objects as needed.\n */\n private setAtPath(obj: unknown, path: string, value: unknown): unknown {\n if (path === '/') return value;\n\n const segments = path.split('/').filter(s => s.length > 0);\n if (segments.length === 0) return value;\n\n // Ensure we have an object to work with\n const result = (obj === null || obj === undefined || typeof obj !== 'object')\n ? {}\n : { ...(obj as object) };\n\n let current: Record<string, unknown> = result as Record<string, unknown>;\n for (let i = 0; i < segments.length - 1; i++) {\n const key = segments[i];\n if (current[key] === null || current[key] === undefined || typeof current[key] !== 'object') {\n current[key] = {};\n } else {\n current[key] = { ...(current[key] as object) };\n }\n current = current[key] as Record<string, unknown>;\n }\n\n const lastKey = segments[segments.length - 1];\n current[lastKey] = value;\n\n return result;\n }\n\n /**\n * Delete a value at a path in an object.\n */\n private deleteAtPath(obj: unknown, path: string): unknown {\n if (path === '/' || obj === null || obj === undefined || typeof obj !== 'object') {\n return null;\n }\n\n const segments = path.split('/').filter(s => s.length > 0);\n if (segments.length === 0) return null;\n\n const result = { ...(obj as object) } as Record<string, unknown>;\n\n if (segments.length === 1) {\n delete result[segments[0]];\n return Object.keys(result).length > 0 ? result : null;\n }\n\n // Navigate to parent and delete\n let current: Record<string, unknown> = result;\n for (let i = 0; i < segments.length - 1; i++) {\n const key = segments[i];\n if (current[key] === null || current[key] === undefined || typeof current[key] !== 'object') {\n return result; // Path doesn't exist\n }\n current[key] = { ...(current[key] as object) };\n current = current[key] as Record<string, unknown>;\n }\n\n delete current[segments[segments.length - 1]];\n return result;\n }\n\n /**\n * Deep clone a value.\n */\n private deepClone(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map(v => this.deepClone(v));\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = this.deepClone(v);\n }\n return result;\n }\n\n /**\n * Recompute ordered children from display cache.\n */\n private recomputeOrderedChildren(): void {\n const displayCache = this.getDisplayCache();\n if (displayCache && typeof displayCache === 'object' && !Array.isArray(displayCache)) {\n this._orderedChildren = getSortedKeys(displayCache as Record<string, unknown>, this.queryParams);\n } else {\n this._orderedChildren = [];\n }\n }\n\n /**\n * Get a value from the display 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 const displayCache = this.getDisplayCache();\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 (displayCache !== undefined && displayCache !== null) {\n return { value: displayCache, found: true };\n }\n // Even if cache is null/undefined, if we have received initial snapshot, we \"found\" null\n if (this._hasReceivedInitialSnapshot || this._pendingWriteData.length > 0) {\n // Convert undefined to null (null = no data at path)\n return { value: displayCache ?? null, 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._hasReceivedInitialSnapshot || this._pendingWriteData.length > 0) {\n const extractedValue = getValueAtPath(displayCache, relativePath);\n // Convert undefined to null (null = no data at path)\n return { value: extractedValue ?? null, found: true };\n }\n }\n\n return { value: undefined, found: false };\n }\n\n /**\n * Update a child value in the SERVER cache.\n * relativePath should be relative to this View's path.\n * Called when server events (put/patch) arrive.\n */\n updateServerCacheChild(relativePath: string, value: unknown): void {\n if (relativePath === '/') {\n // Full replacement\n this.setServerCache(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 // Ensure serverCache is an object\n if (this._serverCache === null || this._serverCache === undefined || typeof this._serverCache !== 'object') {\n this._serverCache = {};\n }\n\n const cache = this._serverCache as Record<string, unknown>;\n\n if (segments.length === 1) {\n // Direct child update\n if (value === null) {\n delete cache[segments[0]];\n } else {\n cache[segments[0]] = value;\n }\n } else {\n // Nested update\n if (value === null) {\n setValueAtPath(cache, relativePath, undefined);\n } else {\n const childKey = segments[0];\n if (!(childKey in cache)) {\n cache[childKey] = {};\n }\n setValueAtPath(cache, relativePath, value);\n }\n }\n\n this.invalidateDisplayCache();\n }\n\n /**\n * Remove a child from the SERVER cache.\n */\n removeServerCacheChild(relativePath: string): void {\n this.updateServerCacheChild(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 * Check if this View loads all data (no limits, no range filters).\n * A View that loads all data can serve as a \"complete\" cache for child paths.\n */\n loadsAllData(): boolean {\n if (!this.queryParams) return true; // No query params = loads all data\n\n const hasLimit = !!(this.queryParams.limitToFirst || this.queryParams.limitToLast);\n const hasRangeFilter =\n this.queryParams.startAt !== undefined ||\n this.queryParams.endAt !== undefined ||\n this.queryParams.equalTo !== undefined ||\n this.queryParams.startAfter !== undefined ||\n this.queryParams.endBefore !== undefined;\n\n return !hasLimit && !hasRangeFilter;\n }\n\n // ============================================\n // Pending Writes (for local-first)\n // ============================================\n\n /**\n * Get current pending write IDs (to include in pw field).\n */\n getPendingWriteIds(): string[] {\n // Filter out volatile writes (empty requestId)\n return this._pendingWriteData\n .filter(w => w.requestId !== '')\n .map(w => w.requestId);\n }\n\n /**\n * Add a pending write with its data.\n * This is used for local-first writes - the write is applied to displayCache immediately.\n */\n addPendingWriteData(\n requestId: string,\n relativePath: string,\n value: unknown,\n operation: 'set' | 'update' | 'delete'\n ): void {\n this._pendingWriteData.push({ requestId, relativePath, value, operation });\n this.invalidateDisplayCache();\n }\n\n /**\n * Remove a pending write by request ID (on ack or nack).\n * Returns true if the write was found and removed.\n */\n removePendingWrite(requestId: string): boolean {\n const idx = this._pendingWriteData.findIndex(w => w.requestId === requestId);\n if (idx === -1) return false;\n this._pendingWriteData.splice(idx, 1);\n this.invalidateDisplayCache();\n return true;\n }\n\n /**\n * Clear all pending writes.\n */\n clearPendingWrites(): void {\n if (this._pendingWriteData.length > 0) {\n this._pendingWriteData = [];\n this.invalidateDisplayCache();\n }\n }\n\n /**\n * Check if this View has pending writes.\n */\n hasPendingWrites(): boolean {\n // Only count non-volatile writes (those with a requestId)\n return this._pendingWriteData.some(w => w.requestId !== '');\n }\n\n /**\n * Get the pending write data (for debugging/testing).\n */\n getPendingWriteData(): PendingWriteEntry[] {\n return [...this._pendingWriteData];\n }\n\n /**\n * Check if a specific write is pending.\n */\n isWritePending(requestId: string): boolean {\n return this._pendingWriteData.some(w => w.requestId === requestId);\n }\n\n // ============================================\n // Cleanup\n // ============================================\n\n /**\n * Clear the server cache but preserve callbacks and pending writes.\n * Used during reconnection.\n */\n clearServerCache(): void {\n this._serverCache = null;\n this._hasReceivedInitialSnapshot = false;\n this.invalidateDisplayCache();\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._serverCache = null;\n this._orderedChildren = [];\n this._hasReceivedInitialSnapshot = false;\n this._pendingWriteData = [];\n this._displayCacheValid = false;\n this._displayCache = null;\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, VolatileBatchEventMessage, QueryParams } from '../protocol/messages';\nimport { getValueAtPath, joinPath, normalizePath } from '../utils/path';\nimport { getSortValue } from '../utils/sort';\nimport { View, SnapshotCallback } from './View';\n\nexport { SnapshotCallback } from './View';\n\nexport class SubscriptionManager {\n // viewKey (path:queryIdentifier) -> View\n // Each unique path+query combination gets its own View\n private views = new Map<string, View>();\n\n // path -> Set of queryIdentifiers for that path\n // Used to find all Views at a path when events arrive\n private pathToQueryIds = new Map<string, Set<string>>();\n\n // Tag counter for generating unique tags per non-default query\n // Tags are client-local and used to route server events to correct View\n private nextTag = 1;\n\n // tag -> viewKey mapping for routing tagged events\n private tagToViewKey = new Map<number, string>();\n\n // viewKey -> tag mapping for unsubscribe\n private viewKeyToTag = new Map<string, number>();\n\n // Callback to send subscribe message to server\n private sendSubscribe: ((path: string, queryParams?: QueryParams, tag?: number) => Promise<void>) | null = null;\n\n // Callback to send unsubscribe message to server (includes queryParams and tag for server to identify subscription)\n private sendUnsubscribe: ((path: string, queryParams?: QueryParams, tag?: number) => 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, orderedKeys?: string[]) => DataSnapshot)\n | null = null;\n\n /**\n * Initialize the manager with server communication callbacks.\n */\n initialize(options: {\n sendSubscribe: (path: string, queryParams?: QueryParams, tag?: number) => Promise<void>;\n sendUnsubscribe: (path: string, queryParams?: QueryParams, tag?: number) => Promise<void>;\n createSnapshot: (path: string, value: unknown, volatile: boolean, serverTimestamp?: number, orderedKeys?: string[]) => DataSnapshot;\n }): void {\n this.sendSubscribe = options.sendSubscribe;\n this.sendUnsubscribe = options.sendUnsubscribe;\n this.createSnapshot = options.createSnapshot;\n }\n\n /**\n * Create a view key from path and query identifier.\n */\n private makeViewKey(path: string, queryIdentifier: string): string {\n return `${path}:${queryIdentifier}`;\n }\n\n /**\n * Get all Views at a given path (across all query identifiers).\n */\n private getViewsAtPath(path: string): View[] {\n const queryIds = this.pathToQueryIds.get(path);\n if (!queryIds || queryIds.size === 0) {\n return [];\n }\n\n const views: View[] = [];\n for (const queryId of queryIds) {\n const viewKey = this.makeViewKey(path, queryId);\n const view = this.views.get(viewKey);\n if (view) {\n views.push(view);\n }\n }\n return views;\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, queryIdentifier?: string): () => void {\n const normalizedPath = path;\n const queryId = queryIdentifier ?? 'default';\n const viewKey = this.makeViewKey(normalizedPath, queryId);\n const isNonDefaultQuery = queryId !== 'default';\n\n // Get or create View for this path+query combination\n let view = this.views.get(viewKey);\n const isNewView = !view;\n let queryParamsChanged = false;\n let tag: number | undefined;\n\n if (!view) {\n view = new View(normalizedPath, queryParams);\n this.views.set(viewKey, view);\n\n // Track this query ID for the path\n if (!this.pathToQueryIds.has(normalizedPath)) {\n this.pathToQueryIds.set(normalizedPath, new Set());\n }\n this.pathToQueryIds.get(normalizedPath)!.add(queryId);\n\n // Generate tag for non-default queries\n if (isNonDefaultQuery) {\n tag = this.nextTag++;\n this.tagToViewKey.set(tag, viewKey);\n this.viewKeyToTag.set(viewKey, tag);\n }\n } else {\n // Check if queryParams changed - update View to match latest subscription\n // (This should rarely happen since same queryId means same query)\n queryParamsChanged = view.updateQueryParams(queryParams);\n // Get existing tag for this view\n tag = this.viewKeyToTag.get(viewKey);\n }\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, queryId);\n };\n\n // If this is a new view or queryParams changed, send subscribe to server\n // (We no longer send event types - server sends all data changes, client filters locally)\n // BUT only if there's no ancestor with a complete view (loadsAllData) that covers this path\n if (isNewView || queryParamsChanged) {\n // Check if an ancestor has a complete view that can provide data for this path\n // If so, we don't need our own server subscription\n const hasAncestorComplete = this.hasAncestorCompleteView(normalizedPath);\n\n if (!hasAncestorComplete) {\n // Fire and forget - we don't wait for the server\n // Include tag for non-default queries\n this.sendSubscribe?.(normalizedPath, view.queryParams ?? undefined, tag).catch((err) => {\n console.error('Failed to subscribe:', err);\n });\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 // Pass orderedChildren so forEach() iterates in correct order\n const snapshot = this.createSnapshot?.(view.path, cache, false, undefined, view.orderedChildren);\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 unsubscribeCallback(path: string, eventType: EventType, callback: SnapshotCallback, queryIdentifier?: string): void {\n const queryId = queryIdentifier ?? 'default';\n const viewKey = this.makeViewKey(path, queryId);\n const view = this.views.get(viewKey);\n if (!view) return;\n\n // Capture queryParams and tag before removing the view\n const queryParams = view.queryParams ?? undefined;\n const tag = this.viewKeyToTag.get(viewKey);\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(viewKey);\n\n // Remove from pathToQueryIds\n const queryIds = this.pathToQueryIds.get(path);\n if (queryIds) {\n queryIds.delete(queryId);\n if (queryIds.size === 0) {\n this.pathToQueryIds.delete(path);\n }\n }\n\n // Clean up tag mappings\n if (tag !== undefined) {\n this.tagToViewKey.delete(tag);\n this.viewKeyToTag.delete(viewKey);\n }\n\n // Send unsubscribe for this specific View (with its queryParams and tag)\n this.sendUnsubscribe?.(path, queryParams, tag).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 * This affects ALL Views at the path (across all query identifiers).\n */\n unsubscribeEventType(path: string, eventType: EventType): void {\n const normalizedPath = path;\n const queryIds = this.pathToQueryIds.get(normalizedPath);\n if (!queryIds) return;\n\n // Iterate over a copy since we may delete during iteration\n for (const queryId of Array.from(queryIds)) {\n const viewKey = this.makeViewKey(normalizedPath, queryId);\n const view = this.views.get(viewKey);\n if (!view) continue;\n\n // Capture queryParams before potentially removing the view\n const queryParams = view.queryParams ?? undefined;\n\n view.removeAllCallbacks(eventType);\n\n if (!view.hasCallbacks()) {\n // View is now empty - remove it and send unsubscribe\n this.views.delete(viewKey);\n queryIds.delete(queryId);\n\n this.sendUnsubscribe?.(normalizedPath, queryParams).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n }\n // If view still has callbacks, keep the subscription - server sends all data changes\n }\n\n // Clean up pathToQueryIds if empty\n if (queryIds.size === 0) {\n this.pathToQueryIds.delete(normalizedPath);\n }\n }\n\n /**\n * Remove ALL subscriptions at a path.\n * This affects ALL Views at the path (across all query identifiers).\n */\n unsubscribeAll(path: string): void {\n const normalizedPath = path;\n const queryIds = this.pathToQueryIds.get(normalizedPath);\n if (!queryIds || queryIds.size === 0) return;\n\n // Remove all Views at this path and send unsubscribe for each\n for (const queryId of queryIds) {\n const viewKey = this.makeViewKey(normalizedPath, queryId);\n const view = this.views.get(viewKey);\n if (view) {\n // Capture queryParams before clearing\n const queryParams = view.queryParams ?? undefined;\n view.clear();\n this.views.delete(viewKey);\n\n // Send unsubscribe for this specific View\n this.sendUnsubscribe?.(normalizedPath, queryParams).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n }\n }\n this.pathToQueryIds.delete(normalizedPath);\n }\n\n /**\n * Handle an incoming event message from the server.\n * Server sends 'put', 'patch', or 'vb' (volatile batch) 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 if (message.ev === 'vb') {\n this.handleVolatileBatchEvent(message);\n } else {\n console.warn('Unknown event type:', (message as { ev: string }).ev);\n }\n }\n\n /**\n * Get View by tag. Returns undefined if tag not found.\n */\n private getViewByTag(tag: number): View | undefined {\n const viewKey = this.tagToViewKey.get(tag);\n if (!viewKey) return undefined;\n return this.views.get(viewKey);\n }\n\n /**\n * Handle a 'put' event - single path change.\n * Routes to specific View by tag if present, otherwise to default View at path.\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 const tag = message.tag;\n\n // Skip if no value to apply\n if (value === undefined) return;\n\n // Route by tag if present\n if (tag !== undefined) {\n const view = this.getViewByTag(tag);\n if (view) {\n this.applyServerUpdateToView(view, [{ relativePath, value }], isVolatile, serverTimestamp);\n }\n return;\n }\n\n // No tag - route to default View at this path\n const defaultViewKey = this.makeViewKey(subscriptionPath, 'default');\n const view = this.views.get(defaultViewKey);\n if (view) {\n this.applyServerUpdateToView(view, [{ relativePath, value }], isVolatile, serverTimestamp);\n }\n }\n\n /**\n * Handle a 'patch' event - multi-path change.\n * Routes to specific View by tag if present, otherwise to default View at path.\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 const tag = message.tag;\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 // Route by tag if present\n if (tag !== undefined) {\n const view = this.getViewByTag(tag);\n if (view) {\n this.applyServerUpdateToView(view, updates, isVolatile, serverTimestamp);\n }\n return;\n }\n\n // No tag - route to default View at this path\n const defaultViewKey = this.makeViewKey(subscriptionPath, 'default');\n const view = this.views.get(defaultViewKey);\n if (view) {\n this.applyServerUpdateToView(view, updates, isVolatile, serverTimestamp);\n }\n }\n\n /**\n * Handle a 'vb' (volatile batch) event - batched volatile updates across subscriptions.\n * Server batches volatile events in 50ms intervals to reduce message overhead.\n * Format: { ev: 'vb', b: { subscriptionPath: { relativePath: value } }, ts: timestamp }\n * Dispatches to ALL Views at each subscription path.\n */\n private handleVolatileBatchEvent(message: VolatileBatchEventMessage): void {\n const batch = message.b;\n const serverTimestamp = message.ts;\n\n if (!batch) return;\n\n // Process each subscription path in the batch\n for (const [subscriptionPath, updates] of Object.entries(batch)) {\n // Get all Views at this path (across all query identifiers)\n const views = this.getViewsAtPath(subscriptionPath);\n if (views.length === 0) continue;\n\n // Build updates array from the batch entry\n const updatesList: Array<{ relativePath: string; value: unknown }> = [];\n for (const [relativePath, value] of Object.entries(updates)) {\n updatesList.push({ relativePath, value });\n }\n\n // Apply all updates to each View (each View will filter according to its query params)\n for (const view of views) {\n this.applyServerUpdateToView(view, updatesList, true, serverTimestamp);\n }\n }\n }\n\n /**\n * Detect and fire child_moved events for children that changed position OR sort value.\n *\n * child_moved fires for ANY priority/sort value change, regardless of whether\n * the position actually changes.\n *\n * IMPORTANT: child_moved should only fire for children whose VALUE or PRIORITY changed.\n * Children that are merely \"displaced\" by another child moving should NOT fire child_moved.\n *\n * @param affectedChildren - Only check these children for moves. If not provided,\n * checks all children (for full snapshots where we compare values).\n * @param previousDisplayCache - Previous display cache for comparing sort values\n * @param currentDisplayCache - Current display cache for comparing sort values\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 affectedChildren?: Set<string>,\n previousDisplayCache?: Record<string, unknown> | null,\n currentDisplayCache?: Record<string, unknown> | null\n ): void {\n if (childMovedSubs.length === 0) return;\n\n // Determine which children to check for moves\n // For delta updates, only check children that were actually modified\n // For full snapshots, affectedChildren will contain children with changed values\n const childrenToCheck = affectedChildren ?? new Set(currentOrder);\n\n const queryParams = view.queryParams;\n\n // For each affected child that exists in both old and new, check if position or sort value changed\n for (const key of childrenToCheck) {\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 if (oldPos === undefined || newPos === undefined) {\n continue;\n }\n\n // Check if the relative order changed\n const oldPrevKey = oldPos > 0 ? previousOrder[oldPos - 1] : null;\n const newPrevKey = newPos > 0 ? currentOrder[newPos - 1] : null;\n\n let positionChanged = oldPrevKey !== newPrevKey;\n\n // Also check if sort value changed\n // Case 2003: fire child_moved for ANY priority change (when ordering by priority)\n // For orderByChild, only fire if position actually changed\n let sortValueChanged = false;\n let isPriorityOrdering = false;\n if (previousDisplayCache && currentDisplayCache) {\n const prevValue = previousDisplayCache[key];\n const currValue = currentDisplayCache[key];\n const prevSortValue = getSortValue(prevValue, queryParams);\n const currSortValue = getSortValue(currValue, queryParams);\n // Compare sort values - use JSON.stringify for deep comparison\n sortValueChanged = JSON.stringify(prevSortValue) !== JSON.stringify(currSortValue);\n\n // Check if we're ordering by priority (default when no orderBy, or explicit orderByPriority)\n // Note: When queryParams exists but orderBy is undefined, we default to priority ordering\n isPriorityOrdering = !queryParams?.orderBy || queryParams.orderBy === 'priority';\n }\n\n // Behavior:\n // - For priority ordering: child_moved fires when priority changes\n // - For orderByChild/orderByValue/orderByKey: child_moved only fires if position changes\n // - child_moved does NOT fire for children merely \"displaced\" by other children moving\n //\n // For delta updates (affectedChildren provided): the affected child's value changed\n // For full snapshots (affectedChildren undefined): only fire if this child's sort value changed\n let shouldFire: boolean;\n if (affectedChildren) {\n // Delta update - this child was modified\n // Fire if position changed, or if priority changed (for priority ordering)\n shouldFire = positionChanged || (isPriorityOrdering && sortValueChanged);\n } else {\n // Full snapshot - only fire if this child's own sort value changed\n // (and only for priority ordering, per Case 2003)\n shouldFire = isPriorityOrdering && sortValueChanged;\n }\n\n if (shouldFire) {\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 previousValue?: unknown\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n // Pass the previous value for removed children, not null\n const snapshot = this.createSnapshot?.(childPath, previousValue ?? 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 * Handle subscription revocation due to auth change.\n * The server has already removed the subscription, so we just clean up locally.\n * This is different from unsubscribeAll which sends an unsubscribe message.\n */\n handleSubscriptionRevoked(path: string): void {\n const normalizedPath = path;\n const queryIds = this.pathToQueryIds.get(normalizedPath);\n if (!queryIds || queryIds.size === 0) return;\n\n // Remove all Views at this path locally (don't send unsubscribe, server already revoked)\n for (const queryId of queryIds) {\n const viewKey = this.makeViewKey(normalizedPath, queryId);\n const view = this.views.get(viewKey);\n if (view) {\n // Clean up tag mappings\n const tag = this.viewKeyToTag.get(viewKey);\n if (tag !== undefined) {\n this.tagToViewKey.delete(tag);\n this.viewKeyToTag.delete(viewKey);\n }\n view.clear();\n this.views.delete(viewKey);\n }\n }\n this.pathToQueryIds.delete(normalizedPath);\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 this.pathToQueryIds.clear();\n this.tagToViewKey.clear();\n this.viewKeyToTag.clear();\n this.nextTag = 1;\n }\n\n /**\n * Check if there are any subscriptions at a path (across all query identifiers).\n */\n hasSubscriptions(path: string): boolean {\n const queryIds = this.pathToQueryIds.get(path);\n return queryIds !== undefined && queryIds.size > 0;\n }\n\n /**\n * Check if a path is \"covered\" by an active subscription that has received data.\n *\n * A path is covered if:\n * - There's an active subscription at that exact path that has data, OR\n * - There's an active subscription at an ancestor path that has data\n *\n * Note: Any subscription type (value, child_added, child_moved, etc.) receives\n * the initial snapshot from the server and thus has cached data.\n */\n isPathCovered(path: string): boolean {\n const normalized = normalizePath(path);\n\n // Check exact match\n if (this.hasActiveSubscriptionWithData(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.hasActiveSubscriptionWithData(ancestorPath)) {\n return true;\n }\n }\n\n // Check root\n if (normalized !== '/' && this.hasActiveSubscriptionWithData('/')) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Check if there's an active subscription at a path that has data.\n * A View has data if it has received the initial snapshot OR has pending writes.\n * Any subscription type (value, child_added, child_moved, etc.) counts.\n */\n private hasActiveSubscriptionWithData(path: string): boolean {\n const views = this.getViewsAtPath(path);\n // A View has data if it received initial snapshot OR has pending writes (optimistic)\n return views.some(view => view.hasCallbacks() && (view.hasReceivedInitialSnapshot || view.hasPendingWrites()));\n }\n\n /**\n * Check if any ancestor path has a \"complete\" View (one that loadsAllData).\n * A complete View has no limits and no range filters, so it contains all data\n * for its subtree. Child subscriptions can use the ancestor's data instead\n * of creating their own server subscription.\n */\n hasAncestorCompleteView(path: string): boolean {\n const normalized = normalizePath(path);\n const segments = normalized.split('/').filter((s) => s.length > 0);\n\n // Walk up the path tree checking ancestors\n for (let i = segments.length - 1; i >= 0; i--) {\n const ancestorPath = i === 0 ? '/' : '/' + segments.slice(0, i).join('/');\n const views = this.getViewsAtPath(ancestorPath);\n for (const view of views) {\n if (view.hasCallbacks() && view.loadsAllData() && view.hasReceivedInitialSnapshot) {\n return true;\n }\n }\n }\n\n // Check root separately if not already checked\n if (normalized !== '/') {\n const rootViews = this.getViewsAtPath('/');\n for (const view of rootViews) {\n if (view.hasCallbacks() && view.loadsAllData() && view.hasReceivedInitialSnapshot) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Get a cached value if the path is covered by an active subscription.\n * Returns the value from the first View that has it (typically the default/unfiltered one).\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 - try all Views at this path\n const exactViews = this.getViewsAtPath(normalized);\n for (const view of exactViews) {\n const result = view.getCacheValue(normalized);\n if (result.found) {\n return result;\n }\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 ancestorViews = this.getViewsAtPath(ancestorPath);\n for (const view of ancestorViews) {\n // Any subscription with data (initial snapshot or pending writes) can provide cached values\n if (view.hasCallbacks() && (view.hasReceivedInitialSnapshot || view.hasPendingWrites())) {\n const result = view.getCacheValue(normalized);\n if (result.found) {\n return result;\n }\n }\n }\n }\n\n // Check root\n if (normalized !== '/') {\n const rootViews = this.getViewsAtPath('/');\n for (const view of rootViews) {\n // Any subscription with data (initial snapshot or pending writes) can provide cached values\n if (view.hasCallbacks() && (view.hasReceivedInitialSnapshot || view.hasPendingWrites())) {\n const result = view.getCacheValue(normalized);\n if (result.found) {\n return result;\n }\n }\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.clearServerCache();\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 view of this.views.values()) {\n if (view.hasCallbacks()) {\n try {\n await this.sendSubscribe?.(view.path, view.queryParams ?? undefined);\n } catch (err) {\n console.error(`Failed to resubscribe to ${view.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 view of this.views.values()) {\n const eventTypes = view.getEventTypes();\n const queryParams = view.queryParams ?? undefined;\n result.push({ path: view.path, eventTypes, queryParams });\n }\n\n return result;\n }\n\n /**\n * Get a View by path (for testing/debugging).\n * Returns the first View at this path (default query identifier).\n */\n getView(path: string): View | undefined {\n // Try default query identifier first\n const defaultView = this.views.get(this.makeViewKey(path, 'default'));\n if (defaultView) return defaultView;\n\n // Otherwise return any View at this path\n const views = this.getViewsAtPath(path);\n return views.length > 0 ? views[0] : undefined;\n }\n\n // ============================================\n // Shared Write Application (used by server events and optimistic writes)\n // ============================================\n\n /**\n * Recursively sort object keys for consistent comparison.\n * This ensures {a:1, b:2} and {b:2, a:1} compare as equal.\n * Uses simple alphabetical sorting since we're just checking data equality,\n * not display order.\n */\n private sortKeysForComparison(value: unknown): unknown {\n if (value === null || typeof value !== 'object') {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.sortKeysForComparison(item));\n }\n\n // Create a new object with keys in sorted order\n const obj = value as Record<string, unknown>;\n const sortedKeys = Object.keys(obj).sort();\n const sorted: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n sorted[key] = this.sortKeysForComparison(obj[key]);\n }\n return sorted;\n }\n\n /**\n * Serialize cache with sorted keys for consistent comparison.\n */\n private serializeCacheForComparison(cache: unknown): string {\n const sorted = this.sortKeysForComparison(cache);\n return JSON.stringify(sorted);\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 applyServerUpdateToView(\n view: View,\n updates: Array<{ relativePath: string; value: unknown }>,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n // Capture previous display state (for change detection)\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n const isFirstSnapshot = !view.hasReceivedInitialSnapshot && !view.hasPendingWrites();\n\n // For non-volatile writes, capture previous display cache state for deduplication.\n // Volatile writes skip deduplication entirely because:\n // 1. Server does NOT echo our volatile writes back (fire-and-forget)\n // 2. Incoming volatile events are from OTHER clients and should always fire\n // 3. Skipping serialization reduces overhead on the hot path (30+ Hz updates)\n let previousCacheJson: string | null = null;\n let previousDisplayCache: Record<string, unknown> | null = null;\n if (!isVolatile) {\n const cache = view.getDisplayCache();\n previousDisplayCache = (cache && typeof cache === 'object' && !Array.isArray(cache))\n ? cache as Record<string, unknown>\n : null;\n previousCacheJson = this.serializeCacheForComparison(cache);\n }\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 to SERVER cache (display cache is computed automatically)\n for (const { relativePath, value } of updates) {\n if (relativePath === '/') {\n // Full snapshot replacement\n view.setServerCache(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 to server cache\n if (value === null) {\n view.removeServerCacheChild(relativePath);\n } else {\n view.updateServerCacheChild(relativePath, value);\n }\n }\n }\n\n // Capture current state (from display cache, which includes pending writes)\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n\n // Deduplication check for non-volatile writes (skip for volatile and first snapshot)\n // This prevents duplicate events when server confirms our optimistic write.\n // Uses sorted key comparison to handle server returning keys in different order.\n if (!isVolatile && !isFirstSnapshot && previousCacheJson !== null) {\n const currentCacheJson = this.serializeCacheForComparison(view.getDisplayCache());\n if (previousCacheJson === currentCacheJson) {\n return;\n }\n }\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getDisplayCache();\n // Pass orderedChildren so forEach() iterates in correct order\n const snapshot = this.createSnapshot?.(view.path, fullValue, isVolatile, serverTimestamp, view.orderedChildren);\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 const prevValue = previousDisplayCache?.[key];\n this.fireChildRemoved(view, key, childRemovedSubs, isVolatile, serverTimestamp, prevValue);\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 const prevValue = previousDisplayCache?.[childKey];\n this.fireChildRemoved(view, childKey, childRemovedSubs, isVolatile, serverTimestamp, prevValue);\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 // Get current display cache for sort value comparison\n const currentCache = view.getDisplayCache();\n const currentDisplayCache = (currentCache && typeof currentCache === 'object' && !Array.isArray(currentCache))\n ? currentCache as Record<string, unknown>\n : null;\n\n // For delta updates, only check affected children for moves\n // For full snapshots, we pass undefined which checks all children\n this.detectAndFireMoves(\n view,\n previousOrder,\n currentOrder,\n previousPositions,\n currentPositions,\n previousChildSet,\n currentChildSet,\n childMovedSubs,\n isVolatile,\n serverTimestamp,\n isFullSnapshot ? undefined : affectedChildren,\n previousDisplayCache,\n currentDisplayCache\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 view of this.views.values()) {\n const viewPath = view.path;\n // Check if viewPath is an ancestor of, equal to, or descendant of the write path\n if (normalized === viewPath) {\n // Exact match - write is directly at View's path\n views.push(view);\n } else if (normalized.startsWith(viewPath + '/')) {\n // Write is INSIDE View's path (write is a descendant)\n views.push(view);\n } else if (viewPath.startsWith(normalized + '/')) {\n // View is INSIDE write path (View is a descendant of write)\n // e.g., writing to /foo affects View at /foo/bar\n views.push(view);\n } else if (viewPath === '/') {\n // Root View is always affected\n views.push(view);\n } else if (normalized === '/') {\n // Write to root affects all Views\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 /**\n * Track a pending write by request ID.\n * Note: With the dual-cache system, this is now handled by applyOptimisticWrite\n * via addPendingWriteData. This method is kept for API compatibility but is a no-op.\n */\n trackPendingWrite(_writePath: string, _requestId: string): void {\n // No-op: tracking now happens in applyOptimisticWrite via addPendingWriteData\n }\n\n /**\n * Clear a pending write from all Views (on ack).\n * Fires callbacks only if displayCache changed (e.g., server modified the data).\n * With PUT-before-ACK ordering, this handles the case where server data differs from optimistic.\n */\n clearPendingWrite(requestId: string): void {\n for (const view of this.views.values()) {\n if (!view.isWritePending(requestId)) {\n continue;\n }\n\n // Capture previous display state\n const previousDisplayCache = view.getDisplayCache() as Record<string, unknown> | null;\n const previousCacheJson = this.serializeCacheForComparison(previousDisplayCache);\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n\n // Remove the pending write\n view.removePendingWrite(requestId);\n\n // Check if display cache changed (server data differs from optimistic)\n const currentCacheJson = this.serializeCacheForComparison(view.getDisplayCache());\n if (previousCacheJson === currentCacheJson) {\n // No change - server data matches optimistic, nothing to do\n continue;\n }\n\n // Display cache changed - fire callbacks\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getDisplayCache();\n const snapshot = this.createSnapshot?.(view.path, fullValue, false, undefined, view.orderedChildren);\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 // Note: We pass skipMoves=true because moves were already fired by the optimistic write.\n // The ack just confirms the write; if server data differs, it will come via PUT event\n // which will fire the correct move events. Firing moves here causes duplicate/wrong events\n // when ACK arrives before PUT due to network ordering.\n this.fireChildEventsForAck(view, previousOrder, previousChildSet, currentOrder, currentChildSet, previousDisplayCache);\n }\n }\n\n /**\n * Fire child events for ACK handling, skipping child_moved.\n * This is a variant of fireChildEvents that doesn't fire moves because:\n * 1. Moves were already fired optimistically\n * 2. If server modifies data, PUT event will fire correct moves\n * 3. ACK can arrive before PUT, causing incorrect intermediate state\n */\n private fireChildEventsForAck(\n view: View,\n previousOrder: string[],\n previousChildSet: Set<string>,\n currentOrder: string[],\n currentChildSet: Set<string>,\n previousDisplayCache?: Record<string, unknown> | null\n ): void {\n const childAddedSubs = view.getCallbacks('child_added');\n const childChangedSubs = view.getCallbacks('child_changed');\n const childRemovedSubs = view.getCallbacks('child_removed');\n\n // Skip if no relevant subscriptions\n if (childAddedSubs.length === 0 && childChangedSubs.length === 0 && childRemovedSubs.length === 0) {\n return;\n }\n\n const displayCache = view.getDisplayCache() as Record<string, unknown> | null;\n\n // Full comparison of children\n for (const key of currentOrder) {\n if (!previousChildSet.has(key)) {\n // New child added\n if (childAddedSubs.length > 0 && displayCache) {\n const prevKey = view.getPreviousChildKey(key);\n this.fireChildAdded(view, key, childAddedSubs, prevKey, false, undefined);\n }\n } else if (previousDisplayCache && childChangedSubs.length > 0 && displayCache) {\n // Check if value changed\n const prevValue = previousDisplayCache[key];\n const currentValue = displayCache[key];\n const prevJson = this.serializeCacheForComparison(prevValue);\n const currJson = this.serializeCacheForComparison(currentValue);\n if (prevJson !== currJson) {\n const prevKey = view.getPreviousChildKey(key);\n this.fireChildChanged(view, key, childChangedSubs, prevKey, false, undefined);\n }\n }\n }\n\n // Removed children\n for (const key of previousOrder) {\n if (!currentChildSet.has(key)) {\n if (childRemovedSubs.length > 0) {\n const prevValue = previousDisplayCache?.[key];\n this.fireChildRemoved(view, key, childRemovedSubs, false, undefined, prevValue);\n }\n }\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 /**\n * Handle a write NACK by removing the pending write from affected Views.\n * With the dual-cache system, this automatically reverts to server truth.\n * Returns the affected Views so callbacks can be fired if data changed.\n */\n handleWriteNack(requestId: string): View[] {\n const affectedViews: View[] = [];\n\n for (const view of this.views.values()) {\n if (view.isWritePending(requestId)) {\n // Capture previous display state\n const previousDisplayCache = view.getDisplayCache() as Record<string, unknown> | null;\n const previousCacheJson = this.serializeCacheForComparison(previousDisplayCache);\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n\n // Remove the pending write - displayCache automatically updates\n view.removePendingWrite(requestId);\n\n // Capture new state\n const currentCacheJson = this.serializeCacheForComparison(view.getDisplayCache());\n\n // If display cache changed, fire callbacks\n if (previousCacheJson !== currentCacheJson) {\n affectedViews.push(view);\n\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getDisplayCache();\n const snapshot = this.createSnapshot?.(view.path, fullValue, false, undefined, view.orderedChildren);\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 this.fireChildEvents(view, previousOrder, previousChildSet, currentOrder, currentChildSet, '/', false, undefined, previousDisplayCache);\n }\n }\n }\n\n return affectedViews;\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 // Calculate relative path and potentially extract value for this View\n let relativePath: string;\n let effectiveValue: unknown = value;\n\n if (normalized === view.path) {\n // Exact match - write is directly at View's path\n relativePath = '/';\n } else if (view.path === '/') {\n // View is at root, write is somewhere inside\n relativePath = normalized;\n } else if (normalized.startsWith(view.path + '/')) {\n // Write is INSIDE View's path (write is a descendant)\n relativePath = normalized.slice(view.path.length);\n } else if (view.path.startsWith(normalized + '/')) {\n // View is INSIDE write path (View is a descendant of write)\n // e.g., writing { a: 1, b: 2 } to /foo, View is at /foo/a\n // We need to extract the value at the path difference\n const pathDiff = view.path.slice(normalized.length); // e.g., '/a'\n effectiveValue = getValueAtPath(value, pathDiff);\n\n // For UPDATE operations, only affect this View if the update actually\n // contains data for this path. Otherwise skip it.\n if (operation === 'update' && effectiveValue === undefined) {\n continue;\n }\n\n relativePath = '/';\n } else {\n // This shouldn't happen given findViewsForWritePath logic\n continue;\n }\n\n // Capture previous display state for change detection\n const previousDisplayCache = view.getDisplayCache() as Record<string, unknown> | null;\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n const previousCacheJson = this.serializeCacheForComparison(previousDisplayCache);\n\n // Add the pending write - this automatically updates the display cache\n view.addPendingWriteData(requestId, relativePath, effectiveValue, operation);\n\n // Capture new state\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n const currentCacheJson = this.serializeCacheForComparison(view.getDisplayCache());\n\n // Skip if display cache didn't actually change\n if (previousCacheJson === currentCacheJson) {\n continue;\n }\n\n updatedViews.push(view);\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getDisplayCache();\n const snapshot = this.createSnapshot?.(view.path, fullValue, false, undefined, view.orderedChildren);\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 this.fireChildEvents(view, previousOrder, previousChildSet, currentOrder, currentChildSet, relativePath, false, undefined, previousDisplayCache);\n }\n\n return updatedViews;\n }\n\n /**\n * Fire child_added/child_changed/child_removed/child_moved events.\n * Extracted as helper since it's used by both server updates and optimistic writes.\n */\n private fireChildEvents(\n view: View,\n previousOrder: string[],\n previousChildSet: Set<string>,\n currentOrder: string[],\n currentChildSet: Set<string>,\n affectedPath: string,\n isVolatile: boolean,\n serverTimestamp: number | undefined,\n previousDisplayCache?: Record<string, unknown> | null\n ): void {\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 const displayCache = view.getDisplayCache() as Record<string, unknown> | null;\n const affectedChildren = new Set<string>();\n\n // Extract immediate child from the affected path\n if (affectedPath !== '/') {\n const segments = affectedPath.split('/').filter(s => s.length > 0);\n if (segments.length > 0) {\n affectedChildren.add(segments[0]);\n }\n }\n\n const isFullSnapshot = affectedPath === '/';\n\n if (isFullSnapshot) {\n // Full snapshot - compare all children\n for (const key of currentOrder) {\n if (!previousChildSet.has(key)) {\n // New child\n if (childAddedSubs.length > 0 && displayCache) {\n const childValue = displayCache[key];\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n childValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childAddedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_added callback:', err);\n }\n }\n }\n }\n } else if (previousDisplayCache && childChangedSubs.length > 0 && displayCache) {\n // Child exists in both - check if value changed\n const prevValue = previousDisplayCache[key];\n const currentValue = displayCache[key];\n const prevJson = this.serializeCacheForComparison(prevValue);\n const currJson = this.serializeCacheForComparison(currentValue);\n if (prevJson !== currJson) {\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n currentValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childChangedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_changed callback:', err);\n }\n }\n }\n }\n }\n }\n\n // Removed children\n for (const key of previousOrder) {\n if (!currentChildSet.has(key)) {\n if (childRemovedSubs.length > 0) {\n // Pass previous value for removed children\n const prevValue = previousDisplayCache?.[key];\n const snapshot = this.createSnapshot?.(joinPath(view.path, key), prevValue ?? null, isVolatile, serverTimestamp);\n if (snapshot) {\n for (const entry of childRemovedSubs) {\n try {\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in child_removed callback:', err);\n }\n }\n }\n }\n }\n }\n } else {\n // Delta update - only check affected children\n // Events fire in this order: child_removed (evictions), child_added, child_removed (direct), child_changed\n\n // Step 1: Fire child_removed for children pushed out by limit constraints FIRST\n // These are children that were in previousOrder but aren't in currentChildSet\n // even though the affected path wasn't directly about them\n if (childRemovedSubs.length > 0) {\n for (const key of previousOrder) {\n // Skip if in affectedChildren (will be handled below)\n if (affectedChildren.has(key)) continue;\n // Skip if still present in current display\n if (currentChildSet.has(key)) continue;\n // This child was pushed out by a limit constraint - fire child_removed\n const prevValue = previousDisplayCache ? previousDisplayCache[key] : null;\n const snapshot = this.createSnapshot?.(joinPath(view.path, key), prevValue, isVolatile, serverTimestamp);\n if (snapshot) {\n for (const entry of childRemovedSubs) {\n try {\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in child_removed callback:', err);\n }\n }\n }\n }\n }\n\n // Step 2: Process affected children (added, removed, changed)\n for (const key of affectedChildren) {\n const wasPresent = previousChildSet.has(key);\n const isPresent = currentChildSet.has(key);\n\n if (!wasPresent && isPresent) {\n // child_added\n if (childAddedSubs.length > 0 && displayCache) {\n const childValue = displayCache[key];\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n childValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childAddedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_added callback:', err);\n }\n }\n }\n }\n } else if (wasPresent && !isPresent) {\n // child_removed (direct removal, not eviction)\n if (childRemovedSubs.length > 0) {\n // Pass previous value for removed children\n const prevValue = previousDisplayCache ? previousDisplayCache[key] : null;\n const snapshot = this.createSnapshot?.(joinPath(view.path, key), prevValue, isVolatile, serverTimestamp);\n if (snapshot) {\n for (const entry of childRemovedSubs) {\n try {\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in child_removed callback:', err);\n }\n }\n }\n }\n } else if (wasPresent && isPresent) {\n // child_changed\n if (childChangedSubs.length > 0 && displayCache) {\n const childValue = displayCache[key];\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n childValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childChangedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_changed callback:', err);\n }\n }\n }\n }\n }\n }\n\n // Step 3: Fire child_added for children that slide INTO view due to limit constraints\n // (window backfill). These are children in currentOrder but not in previousChildSet\n // and not in affectedChildren (already handled above).\n if (childAddedSubs.length > 0 && displayCache) {\n for (const key of currentOrder) {\n // Skip if was already present\n if (previousChildSet.has(key)) continue;\n // Skip if in affectedChildren (already handled in Step 2)\n if (affectedChildren.has(key)) continue;\n // This child slid into view due to limit constraint - fire child_added\n const childValue = displayCache[key];\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n childValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childAddedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_added callback:', err);\n }\n }\n }\n }\n }\n }\n\n // Check for child_moved (position or sort value changes)\n if (childMovedSubs.length > 0) {\n // Build position maps for detectAndFireMoves\n const previousPositions = new Map<string, number>();\n previousOrder.forEach((key, i) => previousPositions.set(key, i));\n const currentPositions = new Map<string, number>();\n currentOrder.forEach((key, i) => currentPositions.set(key, i));\n\n // For delta updates, only check affected children for moves\n // For full snapshots, we pass undefined which checks all children\n this.detectAndFireMoves(\n view,\n previousOrder,\n currentOrder,\n previousPositions,\n currentPositions,\n previousChildSet,\n currentChildSet,\n childMovedSubs,\n isVolatile,\n serverTimestamp,\n isFullSnapshot ? undefined : affectedChildren,\n previousDisplayCache,\n displayCache\n );\n }\n }\n}\n","/**\n * Wire protocol constants.\n * Keys are shortened for bandwidth efficiency.\n */\n\n// Client -> Server operations\nexport const Op = {\n JOIN: 'j',\n AUTH: 'au',\n UNAUTH: 'ua',\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 INVALID_QUERY: 'invalid_query',\n AUTH_REQUIRED: 'auth_required',\n} as const;\n\nexport type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n// Default Lark domain for direct connections\nexport const DEFAULT_LARK_DOMAIN = 'larkdb.net';\n\n// Default coordinator URL (for load balancing - not yet used)\nexport const DEFAULT_COORDINATOR_URL = 'https://coordinator.larkdb.net';\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 * SHA-256 hashing utilities for transaction conditions.\n *\n * Uses Web Crypto API which is available in:\n * - All modern browsers\n * - Node.js 16+ (via globalThis.crypto)\n */\n\nimport canonicalize from 'canonicalize';\n\n/**\n * Compute SHA-256 hash of a value using JCS canonicalization.\n * Returns the hash as a hex string.\n *\n * @param value - The value to hash (will be JCS-canonicalized first)\n * @returns Hex-encoded SHA-256 hash\n */\nexport async function hashValue(value: unknown): Promise<string> {\n // JCS-canonicalize the value to ensure consistent serialization\n const canonical = canonicalize(value);\n\n if (canonical === undefined) {\n // canonicalize returns undefined for undefined input\n // Treat as empty string for hashing\n return hashString('');\n }\n\n return hashString(canonical);\n}\n\n/**\n * Compute SHA-256 hash of a string.\n * Returns the hash as a hex string.\n */\nasync function hashString(str: string): Promise<string> {\n // Convert string to Uint8Array\n const encoder = new TextEncoder();\n const data = encoder.encode(str);\n\n // Use Web Crypto API (available in browsers and Node.js 16+)\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\n // Convert ArrayBuffer to hex string\n const hashArray = new Uint8Array(hashBuffer);\n const hashHex = Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n\n return hashHex;\n}\n\n/**\n * Check if a value is a primitive (not an object or array).\n * Primitives: null, undefined, boolean, number, string\n */\nexport function isPrimitive(value: unknown): boolean {\n return value === null || typeof value !== 'object';\n}\n","/**\n * Push ID generation - chronologically-sortable unique 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 unique 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 * Validation utilities for keys and values.\n *\n * Server rules:\n * - Keys: UTF-8 encoded, max 768 bytes, cannot contain . $ # [ ] /\n * or ASCII control characters (0-31, 127)\n * Note: . is allowed as first character for special keys (.priority, .value, .sv)\n * - String values: Max 10 MB, no control character restriction\n */\n\nimport { LarkError } from '../LarkError';\nimport { ErrorCode } from '../protocol/constants';\n\n/** Maximum key size in bytes */\nconst MAX_KEY_BYTES = 768;\n\n/** Maximum string value size in bytes (10 MB) */\nconst MAX_STRING_VALUE_BYTES = 10 * 1024 * 1024;\n\n/** Maximum write size in bytes for set/update operations (16 MB) */\nconst MAX_WRITE_BYTES = 16 * 1024 * 1024;\n\n/** Maximum write size in bytes for volatile operations (2 KB) */\nconst MAX_VOLATILE_WRITE_BYTES = 2 * 1024;\n\n/** Characters not allowed in keys */\nconst INVALID_KEY_CHARS = /[.#$\\[\\]\\/]/;\n\n/** ASCII control characters (0-31 and 127) */\nconst CONTROL_CHARS = /[\\x00-\\x1f\\x7f]/;\n\n/**\n * Check if a string contains ASCII control characters.\n */\nfunction hasControlChars(str: string): boolean {\n return CONTROL_CHARS.test(str);\n}\n\n/**\n * Get the byte length of a UTF-8 string.\n */\nfunction getByteLength(str: string): number {\n // TextEncoder is available in browsers and Node.js 11+\n if (typeof TextEncoder !== 'undefined') {\n return new TextEncoder().encode(str).length;\n }\n // Fallback for older environments\n return Buffer.byteLength(str, 'utf8');\n}\n\n/**\n * Validate a single key (path segment).\n *\n * @throws LarkError if key is invalid\n */\nexport function validateKey(key: string, context?: string): void {\n const prefix = context ? `${context}: ` : '';\n\n if (typeof key !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key must be a string`\n );\n }\n\n if (key.length === 0) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key cannot be empty`\n );\n }\n\n // Check for invalid characters\n if (INVALID_KEY_CHARS.test(key)) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key \"${key}\" contains invalid characters (. $ # [ ] / are not allowed)`\n );\n }\n\n // Check for control characters\n if (hasControlChars(key)) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key \"${key}\" contains control characters (ASCII 0-31, 127 are not allowed)`\n );\n }\n\n // Check byte length\n const byteLength = getByteLength(key);\n if (byteLength > MAX_KEY_BYTES) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key \"${key.substring(0, 50)}...\" exceeds maximum size of ${MAX_KEY_BYTES} bytes (got ${byteLength})`\n );\n }\n}\n\n/**\n * Validate a path (multiple segments separated by /).\n * Each segment is validated as a key.\n *\n * @throws LarkError if any segment is invalid\n */\nexport function validatePath(path: string, context?: string): void {\n if (typeof path !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_PATH,\n `${context ? `${context}: ` : ''}path must be a string`\n );\n }\n\n // Empty path or root is valid\n if (path === '' || path === '/') {\n return;\n }\n\n // Split and validate each segment\n const segments = path.split('/').filter(s => s.length > 0);\n for (const segment of segments) {\n // Allow special keys: .priority, .value, .sv, .info (internal/system use)\n if (segment === '.priority' || segment === '.value' || segment === '.sv' || segment === '.info') {\n continue;\n }\n validateKey(segment, context);\n }\n}\n\n/**\n * Validate a value recursively.\n * - String values: max 10 MB (no control character restriction)\n * - Object keys are validated as keys\n * - .value can only coexist with .priority\n *\n * @throws LarkError if value contains invalid data\n */\nexport function validateValue(value: unknown, context?: string, path: string = ''): void {\n const prefix = context ? `${context}: ` : '';\n const location = path ? ` at \"${path}\"` : '';\n\n if (value === null || value === undefined) {\n return;\n }\n\n if (typeof value === 'string') {\n // Only check size - control characters are allowed in string values\n const byteLength = getByteLength(value);\n if (byteLength > MAX_STRING_VALUE_BYTES) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}string value${location} exceeds maximum size of 10 MB (got ${Math.round(byteLength / 1024 / 1024 * 100) / 100} MB)`\n );\n }\n return;\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return;\n }\n\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n validateValue(value[i], context, path ? `${path}/${i}` : String(i));\n }\n return;\n }\n\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj);\n\n // .value can only coexist with .priority\n if ('.value' in obj) {\n const otherKeys = keys.filter(k => k !== '.value' && k !== '.priority');\n if (otherKeys.length > 0) {\n const loc = path || '/';\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}data at ${loc} contains \".value\" alongside other children (${otherKeys.join(', ')}). ` +\n `\".value\" can only be used with \".priority\" for primitives with priority.`\n );\n }\n }\n\n for (const [key, childValue] of Object.entries(obj)) {\n // Allow special keys: .priority, .value, .sv (ServerValue)\n if (key !== '.priority' && key !== '.value' && key !== '.sv') {\n validateKey(key, context);\n }\n validateValue(childValue, context, path ? `${path}/${key}` : key);\n }\n return;\n }\n\n // Other types (functions, symbols, etc.) are not valid\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}invalid value type${location}: ${typeof value}`\n );\n}\n\n/**\n * Validate data for a write operation.\n * Validates both the value and any nested keys.\n */\nexport function validateWriteData(value: unknown, context?: string): void {\n validateValue(value, context);\n}\n\n/**\n * Get the JSON byte size of a value.\n * This measures the size as the server would see it (raw JSON bytes of the value).\n */\nexport function getJsonByteSize(value: unknown): number {\n const json = JSON.stringify(value);\n return getByteLength(json);\n}\n\n/**\n * Validate write size for set/update operations.\n * Server limit is 16 MB for the JSON-encoded value.\n *\n * @throws LarkError if value exceeds 16 MB\n */\nexport function validateWriteSize(value: unknown, context?: string): void {\n const byteSize = getJsonByteSize(value);\n if (byteSize > MAX_WRITE_BYTES) {\n const sizeMB = Math.round(byteSize / 1024 / 1024 * 100) / 100;\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${context ? `${context}: ` : ''}write exceeds 16 MB limit (got ${sizeMB} MB)`\n );\n }\n}\n\n/**\n * Validate write size for volatile operations.\n * Server limit is 2 KB for volatile writes.\n *\n * @throws LarkError if value exceeds 2 KB\n */\nexport function validateVolatileWriteSize(value: unknown, context?: string): void {\n const byteSize = getJsonByteSize(value);\n if (byteSize > MAX_VOLATILE_WRITE_BYTES) {\n const sizeKB = Math.round(byteSize / 1024 * 100) / 100;\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${context ? `${context}: ` : ''}volatile write exceeds 2 KB limit (got ${sizeKB} KB)`\n );\n }\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, TxConditionOp, TxOperation } from './protocol/messages';\nimport { hashValue, isPrimitive } from './utils/hash';\nimport { getKey, getParentPath, joinPath, normalizePath } from './utils/path';\nimport { generatePushId } from './utils/pushid';\nimport { validateKey, validatePath, validateWriteData } from './utils/validation';\n\n/**\n * Check if a path is under .info (read-only system paths).\n */\nfunction isInfoPath(path: string): boolean {\n return path === '/.info' || path.startsWith('/.info/');\n}\n\n/**\n * Throw if attempting to write to a .info path.\n */\nfunction validateNotInfoPath(path: string, operation: string): void {\n if (isInfoPath(path)) {\n throw new LarkError(\n ErrorCode.INVALID_PATH,\n `Cannot ${operation} on .info paths - they are read-only system paths`\n );\n }\n}\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 startAfter?: { value: unknown; key?: string };\n endAt?: { value: unknown; key?: string };\n endBefore?: { value: unknown; key?: string };\n equalTo?: { value: unknown; key?: string };\n}\n\n/**\n * Wire protocol constants for query params.\n * Used to build the queryIdentifier.\n */\nconst WIRE_PROTOCOL_CONSTANTS = {\n INDEX_START_VALUE: 'sp',\n INDEX_START_NAME: 'sn',\n INDEX_START_IS_INCLUSIVE: 'sin',\n INDEX_END_VALUE: 'ep',\n INDEX_END_NAME: 'en',\n INDEX_END_IS_INCLUSIVE: 'ein',\n LIMIT: 'l',\n VIEW_FROM: 'vf',\n INDEX: 'i',\n} as const;\n\n/**\n * Convert an object to a unique sorted-key string for consistent comparison.\n * Keys are sorted alphabetically and serialized as JSON.\n */\nfunction objectToUniqueKey(obj: Record<string, unknown>): string {\n const sortedKeys = Object.keys(obj).sort();\n const sorted: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n sorted[key] = obj[key];\n }\n return JSON.stringify(sorted);\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 * Get the LarkDatabase instance this reference belongs to.\n */\n get database(): LarkDatabase {\n return this._db;\n }\n\n /**\n * Get the underlying reference for a query.\n * For queries (created via orderBy*, limitTo*, startAt, etc.), this returns\n * a reference to the same path without query constraints.\n * For non-query references, this returns the reference itself.\n */\n get ref(): DatabaseReference {\n // If this reference has no query constraints, return itself\n if (Object.keys(this._query).length === 0) {\n return this;\n }\n // Return a new reference to the same path without query constraints\n return new DatabaseReference(this._db, this._path);\n }\n\n /**\n * Check if this reference/query is equal to another.\n * Two references are equal if they have the same database, path, and query constraints.\n */\n isEqual(other: DatabaseReference | null): boolean {\n if (!other) return false;\n if (this._db !== other._db) return false;\n if (this._path !== other._path) return false;\n // Compare query constraints\n return JSON.stringify(this._query) === JSON.stringify(other._query);\n }\n\n /**\n * Get the unique identifier for this query's parameters.\n * Used to differentiate multiple queries on the same path.\n *\n * Returns \"default\" for non-query references (no constraints).\n * Returns a sorted JSON string of wire-format params for queries.\n *\n * Used for wire protocol and subscription deduplication.\n */\n get queryIdentifier(): string {\n // Build wire-format query object\n const queryObj: Record<string, unknown> = {};\n\n // Index (orderBy)\n if (this._query.orderBy === 'key') {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX] = '.key';\n } else if (this._query.orderBy === 'value') {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX] = '.value';\n } else if (this._query.orderBy === 'priority') {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX] = '.priority';\n } else if (this._query.orderBy === 'child' && this._query.orderByChildPath) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX] = this._query.orderByChildPath;\n }\n\n // Start constraint (startAt or startAfter)\n // Convert undefined to null for wire protocol\n if (this._query.startAt !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_VALUE] = this._query.startAt.value ?? null;\n if (this._query.startAt.key !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_NAME] = this._query.startAt.key;\n }\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_IS_INCLUSIVE] = true;\n } else if (this._query.startAfter !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_VALUE] = this._query.startAfter.value ?? null;\n // Use [MAX_NAME] sentinel when startAfter is used without a key\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_NAME] = this._query.startAfter.key ?? '[MAX_NAME]';\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_IS_INCLUSIVE] = false;\n }\n\n // End constraint (endAt or endBefore)\n // Convert undefined to null for wire protocol\n if (this._query.endAt !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_VALUE] = this._query.endAt.value ?? null;\n if (this._query.endAt.key !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_NAME] = this._query.endAt.key;\n }\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_IS_INCLUSIVE] = true;\n } else if (this._query.endBefore !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_VALUE] = this._query.endBefore.value ?? null;\n // Use [MIN_NAME] sentinel when endBefore is used without a key\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_NAME] = this._query.endBefore.key ?? '[MIN_NAME]';\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_IS_INCLUSIVE] = false;\n }\n\n // equalTo is startAt + endAt with same value\n if (this._query.equalTo !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_VALUE] = this._query.equalTo.value;\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_VALUE] = this._query.equalTo.value;\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_IS_INCLUSIVE] = true;\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_IS_INCLUSIVE] = true;\n if (this._query.equalTo.key !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_NAME] = this._query.equalTo.key;\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_NAME] = this._query.equalTo.key;\n }\n }\n\n // Limit\n if (this._query.limitToFirst !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.LIMIT] = this._query.limitToFirst;\n queryObj[WIRE_PROTOCOL_CONSTANTS.VIEW_FROM] = 'l'; // left (first)\n } else if (this._query.limitToLast !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.LIMIT] = this._query.limitToLast;\n queryObj[WIRE_PROTOCOL_CONSTANTS.VIEW_FROM] = 'r'; // right (last)\n }\n\n // If no constraints, return \"default\"\n if (Object.keys(queryObj).length === 0) {\n return 'default';\n }\n\n // Return sorted unique key\n return objectToUniqueKey(queryObj);\n }\n\n /**\n * Get the data at this location. Alias for once('value').\n * Alternative to once('value') for reading data.\n */\n async get(): Promise<DataSnapshot> {\n return this.once('value');\n }\n\n // ============================================\n // Navigation\n // ============================================\n\n /**\n * Get a reference to a child path.\n */\n child(path: string): DatabaseReference {\n validatePath(path, 'child');\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 * @param value - The value to write\n */\n async set(value: unknown): Promise<void> {\n validateNotInfoPath(this._path, 'set');\n validateWriteData(value, 'set');\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 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 * @param values - The values to update\n */\n async update(values: Record<string, unknown>): Promise<void> {\n validateNotInfoPath(this._path, 'update');\n\n // Validate all keys and values\n for (const [key, value] of Object.entries(values)) {\n if (key.includes('/')) {\n // Path-like key (contains /) - validate as path\n validatePath(key, 'update');\n } else {\n // Regular key - validate as key\n validateKey(key, 'update');\n }\n if (value !== null) {\n validateWriteData(value, 'update');\n }\n }\n\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)\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 validateNotInfoPath(this._path, 'remove');\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 * Returns a ThenableReference - an object that is both a DatabaseReference\n * AND a Promise. You can access reference properties immediately (like `.key`)\n * and also await the result.\n *\n * If value is provided, the promise resolves after the write completes.\n * If no value is provided, the promise resolves immediately.\n *\n * @example\n * ```javascript\n * // Without value - returns ThenableReference, resolves immediately\n * const pushed = ref.push();\n * console.log(pushed.key); // \"-Abc123...\" (available immediately)\n * await pushed; // Resolves immediately to the same reference\n *\n * // With value - returns ThenableReference, resolves after write\n * const pushed2 = ref.push({ name: 'Alice' });\n * console.log(pushed2.key); // \"-Xyz789...\" (available immediately)\n * await pushed2; // Waits for write to complete\n * ```\n */\n push(): ThenableReference;\n push(value: unknown): ThenableReference;\n push(value?: unknown): ThenableReference {\n validateNotInfoPath(this._path, 'push');\n // Always generate key locally\n const key = generatePushId();\n const childRef = this.child(key);\n\n if (value === undefined) {\n // No value - return ThenableReference that resolves immediately\n return new ThenableReference(this._db, childRef.path);\n }\n\n // Value provided - do the set and create promise that resolves after\n const setPromise = childRef.set(value) as Promise<void>;\n const writePromise = setPromise.then(() => childRef);\n\n return new ThenableReference(this._db, childRef.path, writePromise);\n }\n\n /**\n * Set the data with a priority value for ordering.\n *\n * For objects: injects `.priority` into the value object.\n * For primitives: wraps as `{ '.value': primitive, '.priority': priority }`.\n *\n * Uses { '.value': value, '.priority': priority } format for the wire protocol.\n *\n * @param value - The value to write\n * @param priority - The priority for ordering\n */\n async setWithPriority(value: unknown, priority: number | string): Promise<void> {\n validateNotInfoPath(this._path, 'setWithPriority');\n validateWriteData(value, 'setWithPriority');\n if (typeof priority === 'string') {\n validateWriteData(priority, 'setWithPriority (priority)');\n }\n\n // Setting null/undefined with priority - just set the value (priority is lost)\n if (value === null || value === undefined) {\n await this._db._sendSet(this._path, value);\n return;\n }\n\n // Primitives (number, string, boolean) get wrapped in { '.value': x, '.priority': y }\n if (typeof value !== 'object' || Array.isArray(value)) {\n const wrappedValue = { '.value': value, '.priority': priority };\n await this._db._sendSet(this._path, wrappedValue);\n return;\n }\n\n // Objects get .priority injected directly\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 * Uses cached value for optimistic behavior (local effects are immediate).\n * The optimistic update happens synchronously, Promise resolves after server ack.\n */\n setPriority(priority: number | string | null): Promise<void> {\n validateNotInfoPath(this._path, 'setPriority');\n\n // Get cached value for optimistic behavior (no network fetch needed)\n const { value: cachedValue, found } = this._db._getCachedValue(this._path);\n\n // If not in cache, we need to fall back to async fetch\n if (!found) {\n return this.once().then(snapshot => {\n const actualValue = snapshot.val();\n if (actualValue === null || actualValue === undefined) {\n return this._db._sendSet(this._path, { '.priority': priority });\n }\n return this.setWithPriority(actualValue, priority as number | string);\n });\n }\n\n // Extract the actual value from the cached data\n // Cached data might be wrapped as { '.value': x, '.priority': y } for primitives\n // or as { key: val, '.priority': y } for objects\n let actualValue: unknown;\n if (cachedValue === null || cachedValue === undefined) {\n // Null value in cache\n actualValue = null;\n } else if (typeof cachedValue === 'object' && !Array.isArray(cachedValue)) {\n const obj = cachedValue as Record<string, unknown>;\n // Check for wrapped primitive: { '.value': x, '.priority': y }\n if ('.value' in obj && Object.keys(obj).every(k => k === '.value' || k === '.priority')) {\n actualValue = obj['.value'];\n } else {\n // Object - extract value without .priority\n const { '.priority': _oldPriority, ...rest } = obj;\n actualValue = Object.keys(rest).length > 0 ? rest : null;\n }\n } else {\n // Primitive value (no wrapper)\n actualValue = cachedValue;\n }\n\n if (actualValue === null || actualValue === undefined) {\n // No data exists, just set priority on empty object\n return this._db._sendSet(this._path, { '.priority': priority });\n }\n\n // setWithPriority does the optimistic update synchronously, returns Promise for ack\n return this.setWithPriority(actualValue, priority as number | string);\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 // eslint-disable-next-line @typescript-eslint/no-explicit-any\n updateFunction: (currentValue: any) => any,\n maxRetries: number = DEFAULT_MAX_RETRIES\n ): Promise<TransactionResult> {\n validateNotInfoPath(this._path, 'transaction');\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(); // Stripped value for user callback\n const rawValue = currentSnapshot.exportVal(); // Raw value for condition check\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 condition based on RAW value type (includes priority metadata)\n // Primitives use direct value comparison, complex values use hash\n let condition: TxConditionOp;\n if (isPrimitive(rawValue)) {\n condition = { o: 'c', p: this._path, v: rawValue };\n } else {\n const hash = await hashValue(rawValue);\n condition = { o: 'c', p: this._path, h: hash };\n }\n\n // Step 5: Build transaction with condition + set\n // Preserves priority unless explicitly changed, so if the old value had\n // priority and the new value is a primitive, wrap it to preserve the priority\n let valueToSet = newValue;\n const existingPriority = currentSnapshot.getPriority();\n if (existingPriority !== null && isPrimitive(newValue) && newValue !== null) {\n valueToSet = { '.priority': existingPriority, '.value': newValue };\n }\n const ops: TxOperation[] = [condition, { o: 's', p: this._path, v: valueToSet }];\n\n try {\n // Step 6: 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 7: 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 * For 'value' events, this fetches data directly from the server.\n * For child events ('child_added', 'child_changed', 'child_removed', 'child_moved'),\n * this subscribes, waits for the first event, then unsubscribes.\n *\n * @param eventType - The event type\n * @returns Promise that resolves to the DataSnapshot\n */\n once(eventType: EventType = 'value'): Promise<DataSnapshot> {\n if (eventType === 'value') {\n // Use the optimized direct fetch for 'value' events\n return this._db._sendOnce(this._path, this._buildQueryParams());\n }\n\n // For child events, subscribe and wait for the first event\n return new Promise((resolve) => {\n const unsubscribe = this.on(eventType, (snapshot) => {\n unsubscribe();\n resolve(snapshot);\n });\n });\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(), this.queryIdentifier);\n }\n\n /**\n * Unsubscribe from events.\n *\n * - `off()` - removes ALL listeners at this path\n * - `off('value')` - removes all 'value' listeners at this path\n *\n * Note: To unsubscribe a specific callback, use the unsubscribe function\n * returned by `on()`.\n *\n * @param eventType - Optional event type to unsubscribe from\n */\n off(eventType?: EventType): void {\n if (eventType) {\n // Remove all callbacks for this event type\n this._db._unsubscribeEventType(this._path, eventType);\n } else {\n // Remove all callbacks at this path\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 this._validateNoOrderBy('orderByKey');\n // orderByKey doesn't allow key parameters on startAt/endAt/equalTo\n this._validateNoKeyParameterForOrderByKey('orderByKey');\n // orderByKey requires string values for startAt/endAt/equalTo (validate if already set)\n this._validateStringValuesForOrderByKey('orderByKey');\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 this._validateNoOrderBy('orderByPriority');\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 this._validateNoOrderBy('orderByChild');\n // Validate path doesn't contain reserved $ prefixes\n if (path.startsWith('$') || path.includes('/$')) {\n throw new LarkError(\n ErrorCode.INVALID_PATH,\n `orderByChild: Invalid path '${path}'. Paths cannot contain '$' prefix (reserved for internal use)`\n );\n }\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 this._validateNoOrderBy('orderByValue');\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 // Validate limit is a positive integer\n if (limit === undefined || limit === null) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToFirst: a limit must be provided'\n );\n }\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToFirst: limit must be a positive integer'\n );\n }\n if (this._query.limitToFirst !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToFirst: limitToFirst() cannot be called multiple times'\n );\n }\n if (this._query.limitToLast !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToFirst: cannot use limitToFirst() with limitToLast()'\n );\n }\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 // Validate limit is a positive integer\n if (limit === undefined || limit === null) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToLast: a limit must be provided'\n );\n }\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToLast: limit must be a positive integer'\n );\n }\n if (this._query.limitToLast !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToLast: limitToLast() cannot be called multiple times'\n );\n }\n if (this._query.limitToFirst !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToLast: cannot use limitToLast() with limitToFirst()'\n );\n }\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 * If no value is provided, starts at the beginning (null priority).\n */\n startAt(value?: unknown, key?: string): DatabaseReference {\n if (this._query.startAt !== undefined || this._query.startAfter !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.startAt: startAt()/startAfter() cannot be called multiple times'\n );\n }\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.startAt: cannot use startAt() with equalTo()'\n );\n }\n this._validateQueryValue('startAt', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n startAt: { value, key },\n });\n }\n\n /**\n * Start after a specific value/key (exclusive).\n * Like startAt() but excludes the starting point.\n */\n startAfter(value?: unknown, key?: string): DatabaseReference {\n if (this._query.startAfter !== undefined || this._query.startAt !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.startAfter: startAfter()/startAt() cannot be called multiple times'\n );\n }\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.startAfter: cannot use startAfter() with equalTo()'\n );\n }\n this._validateQueryValue('startAfter', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n startAfter: { value, key },\n });\n }\n\n /**\n * End at a specific value/key.\n * If no value is provided, ends at the end (null priority).\n */\n endAt(value?: unknown, key?: string): DatabaseReference {\n if (this._query.endAt !== undefined || this._query.endBefore !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.endAt: endAt()/endBefore() cannot be called multiple times'\n );\n }\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.endAt: cannot use endAt() with equalTo()'\n );\n }\n this._validateQueryValue('endAt', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n endAt: { value, key },\n });\n }\n\n /**\n * End before a specific value/key (exclusive).\n * Like endAt() but excludes the ending point.\n */\n endBefore(value?: unknown, key?: string): DatabaseReference {\n if (this._query.endBefore !== undefined || this._query.endAt !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.endBefore: endBefore()/endAt() cannot be called multiple times'\n );\n }\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.endBefore: cannot use endBefore() with equalTo()'\n );\n }\n this._validateQueryValue('endBefore', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n endBefore: { value, key },\n });\n }\n\n /**\n * Filter to items equal to a specific value.\n */\n equalTo(value: unknown, key?: string): DatabaseReference {\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.equalTo: equalTo() cannot be called multiple times'\n );\n }\n if (this._query.startAt !== undefined || this._query.startAfter !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.equalTo: cannot use equalTo() with startAt()/startAfter()'\n );\n }\n if (this._query.endAt !== undefined || this._query.endBefore !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.equalTo: cannot use equalTo() with endAt()/endBefore()'\n );\n }\n this._validateQueryValue('equalTo', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n equalTo: { value, key },\n });\n }\n\n // ============================================\n // Query Validation Helpers\n // ============================================\n\n /**\n * Validate that no orderBy has been set yet.\n */\n private _validateNoOrderBy(methodName: string): void {\n if (this._query.orderBy !== undefined) {\n const existingMethod =\n this._query.orderBy === 'child'\n ? `orderByChild('${this._query.orderByChildPath}')`\n : `orderBy${this._query.orderBy.charAt(0).toUpperCase()}${this._query.orderBy.slice(1)}()`;\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: cannot use ${methodName}() with ${existingMethod}`\n );\n }\n }\n\n /**\n * Validate that no key parameter has been set on startAt/endAt/equalTo.\n * Used when calling orderByKey() after startAt/endAt/equalTo.\n */\n private _validateNoKeyParameterForOrderByKey(methodName: string): void {\n if (this._query.startAt?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in startAt()`\n );\n }\n if (this._query.startAfter?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in startAfter()`\n );\n }\n if (this._query.endAt?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in endAt()`\n );\n }\n if (this._query.endBefore?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in endBefore()`\n );\n }\n if (this._query.equalTo?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in equalTo()`\n );\n }\n }\n\n /**\n * Validate that startAt/endAt/equalTo values are strings.\n * Used when calling orderByKey() after startAt/endAt/equalTo.\n */\n private _validateStringValuesForOrderByKey(methodName: string): void {\n if (this._query.startAt !== undefined && typeof this._query.startAt.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), startAt() value must be a string`\n );\n }\n if (this._query.startAfter !== undefined && typeof this._query.startAfter.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), startAfter() value must be a string`\n );\n }\n if (this._query.endAt !== undefined && typeof this._query.endAt.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), endAt() value must be a string`\n );\n }\n if (this._query.endBefore !== undefined && typeof this._query.endBefore.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), endBefore() value must be a string`\n );\n }\n if (this._query.equalTo !== undefined && typeof this._query.equalTo.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), equalTo() value must be a string`\n );\n }\n }\n\n /**\n * Validate value and key types for query methods (startAt, endAt, equalTo, etc.)\n */\n private _validateQueryValue(methodName: string, value: unknown, key?: string): void {\n // Validate key parameter format if provided\n if (key !== undefined) {\n this._validateKeyFormat(methodName, key);\n }\n\n // orderByKey: requires string value, no key parameter\n if (this._query.orderBy === 'key') {\n // Value must be a string for orderByKey (null is also not allowed)\n if (typeof value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), the value must be a string`\n );\n }\n // Key parameter is not allowed with orderByKey (the value IS the key)\n if (key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter`\n );\n }\n return;\n }\n\n // orderByPriority: booleans not allowed (only null, number, string)\n if (this._query.orderBy === 'priority') {\n if (typeof value === 'boolean') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByPriority(), the value must be a valid priority (null, number, or string)`\n );\n }\n }\n\n // General validation: objects not allowed as values (arrays are objects too)\n // This applies to orderByChild and default ordering\n if (value !== null && typeof value === 'object') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: the value argument cannot be an object`\n );\n }\n }\n\n /**\n * Validate that a key is a valid key format.\n * Delegates to the shared validation utility.\n */\n private _validateKeyFormat(methodName: string, key: string): void {\n validateKey(key, `Query.${methodName}`);\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 // Convert undefined to null for wire protocol\n if (this._query.startAt !== undefined) {\n params.startAt = this._query.startAt.value ?? null;\n if (this._query.startAt.key !== undefined) {\n params.startAtKey = this._query.startAt.key;\n }\n hasParams = true;\n }\n\n if (this._query.startAfter !== undefined) {\n params.startAfter = this._query.startAfter.value ?? null;\n if (this._query.startAfter.key !== undefined) {\n params.startAfterKey = this._query.startAfter.key;\n }\n hasParams = true;\n }\n\n if (this._query.endAt !== undefined) {\n params.endAt = this._query.endAt.value ?? null;\n if (this._query.endAt.key !== undefined) {\n params.endAtKey = this._query.endAt.key;\n }\n hasParams = true;\n }\n\n if (this._query.endBefore !== undefined) {\n params.endBefore = this._query.endBefore.value ?? null;\n if (this._query.endBefore.key !== undefined) {\n params.endBeforeKey = this._query.endBefore.key;\n }\n hasParams = true;\n }\n\n if (this._query.equalTo !== undefined) {\n params.equalTo = this._query.equalTo.value ?? null;\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 * Returns the URL for JSON serialization.\n * This allows refs to be serialized with JSON.stringify().\n */\n toJSON(): string {\n return this.toString();\n }\n}\n\n/**\n * A reference that is also a Promise - returned by push().\n *\n * This allows patterns like:\n * ```javascript\n * const pushed = ref.push();\n * console.log(pushed.key); // Key available immediately\n * await pushed; // Wait for write to complete\n * ```\n */\nexport class ThenableReference extends DatabaseReference implements PromiseLike<DatabaseReference> {\n private readonly _promise: Promise<DatabaseReference>;\n\n constructor(\n db: LarkDatabase,\n path: string,\n promise?: Promise<DatabaseReference>\n ) {\n super(db, path);\n // If no promise provided, resolve immediately with this reference\n this._promise = promise ?? Promise.resolve(this);\n }\n\n then<TResult1 = DatabaseReference, TResult2 = never>(\n onfulfilled?: ((value: DatabaseReference) => TResult1 | PromiseLike<TResult1>) | undefined | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | undefined | null\n ): Promise<TResult1 | TResult2> {\n return this._promise.then(onfulfilled, onrejected);\n }\n\n catch<TResult = never>(\n onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | undefined | null\n ): Promise<DatabaseReference | TResult> {\n return this._promise.catch(onrejected);\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 *\n * Wrapped Primitives:\n * - Primitives with priority are stored as `{ '.value': x, '.priority': y }`\n * - val() automatically unwraps these to return just the primitive value\n */\n\nimport type { DatabaseReference } from './DatabaseReference';\nimport type { LarkDatabase } from './LarkDatabase';\nimport type { QueryParams } from './protocol/messages';\nimport { getKey, getValueAtPath, joinPath, normalizePath } from './utils/path';\nimport { getSortedKeys } from './utils/sort';\n\n/**\n * Check if data is a wrapped primitive.\n * Wrapped primitives are objects with:\n * - { '.value': x, '.priority': y } - full form\n * - { '.value': x } - when server strips null priority\n * This is the wire format for primitives with priority.\n */\nfunction isWrappedPrimitive(data: unknown): data is { '.value': unknown; '.priority'?: unknown } {\n if (!data || typeof data !== 'object' || Array.isArray(data)) {\n return false;\n }\n const keys = Object.keys(data);\n // Full form: exactly '.value' and '.priority'\n if (keys.length === 2 && '.value' in data && '.priority' in data) {\n return true;\n }\n // Stripped form: only '.value' (server strips null priority)\n if (keys.length === 1 && '.value' in data) {\n return true;\n }\n return false;\n}\n\n/**\n * Recursively strip priority metadata from data.\n * - Unwraps wrapped primitives: { '.value': x, '.priority': y } → x\n * - Strips '.priority' from objects\n * - Recursively processes all children\n */\nfunction stripPriorityMetadata(data: unknown): unknown {\n // Return null for non-existent data, never undefined\n if (data === null || data === undefined) {\n return null;\n }\n\n // Non-objects pass through (primitives)\n if (typeof data !== 'object') {\n return data;\n }\n\n // Arrays: recursively process elements\n if (Array.isArray(data)) {\n return data.map(stripPriorityMetadata);\n }\n\n // Check for wrapped primitive: { '.value': x, '.priority': y }\n if (isWrappedPrimitive(data)) {\n // Recursively process the value in case it's nested\n return stripPriorityMetadata(data['.value']);\n }\n\n // Regular object: strip .priority and null values, recursively process children\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key === '.priority') {\n continue; // Skip priority metadata\n }\n const stripped = stripPriorityMetadata(value);\n // Omit null values from objects (null = deleted/non-existent)\n if (stripped !== null) {\n result[key] = stripped;\n }\n }\n\n // If object is now empty (only had .priority or null values), return null\n return Object.keys(result).length > 0 ? result : null;\n}\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 private readonly _queryParams: QueryParams | null;\n private readonly _orderedKeys: string[] | null;\n\n constructor(\n data: unknown,\n path: string,\n db: LarkDatabase,\n options: {\n volatile?: boolean;\n serverTimestamp?: number | null;\n queryParams?: QueryParams | null;\n orderedKeys?: string[] | null;\n } = {}\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 this._queryParams = options.queryParams ?? null;\n this._orderedKeys = options.orderedKeys ?? 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 metadata stripped.\n *\n * - Strips `.priority` from objects (it's metadata, not user data)\n * - Unwraps `{ '.value': x, '.priority': y }` to just `x` (wrapped primitives)\n *\n * @typeParam T - Optional type for the returned value. Defaults to `any`.\n * @example\n * ```typescript\n * // Untyped (returns any)\n * const data = snapshot.val();\n *\n * // Typed (returns User)\n * const user = snapshot.val<User>();\n * ```\n */\n val<T = any>(): T {\n // Recursively strip priority metadata from the entire data tree\n return stripPriorityMetadata(this._data) as T;\n }\n\n /**\n * Check if data exists at this location (is not null/undefined).\n * Returns false for priority-only nodes (only .priority, no actual value).\n */\n exists(): boolean {\n if (this._data === null || this._data === undefined) {\n return false;\n }\n\n // Check for priority-only object (no actual data, just metadata)\n if (typeof this._data === 'object' && !Array.isArray(this._data)) {\n const keys = Object.keys(this._data);\n if (keys.length === 1 && keys[0] === '.priority') {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Get a child snapshot at the specified path.\n *\n * Special handling:\n * - `.priority` returns the priority value\n * - For wrapped primitives, only `.priority` returns data; other paths return null\n * - Non-existent paths return a snapshot with val() === null\n */\n child(path: string): DataSnapshot {\n const childPath = joinPath(this._path, path);\n\n // Special case: .priority returns the priority value\n if (path === '.priority') {\n const priority = this.getPriority();\n return new DataSnapshot(priority, childPath, this._db, {\n volatile: this._volatile,\n serverTimestamp: this._serverTimestamp,\n });\n }\n\n // For wrapped primitives, no children exist (except .priority handled above)\n if (isWrappedPrimitive(this._data)) {\n return new DataSnapshot(null, childPath, this._db, {\n volatile: this._volatile,\n serverTimestamp: this._serverTimestamp,\n });\n }\n\n const childData = getValueAtPath(this._data, path);\n // Convert undefined to null\n return new DataSnapshot(childData ?? null, childPath, this._db, {\n volatile: this._volatile,\n serverTimestamp: this._serverTimestamp,\n });\n }\n\n /**\n * Check if this snapshot has any children.\n * Excludes `.priority` metadata from consideration.\n * Wrapped primitives have no children.\n */\n hasChildren(): boolean {\n if (typeof this._data !== 'object' || this._data === null) {\n return false;\n }\n // Wrapped primitives have no children\n if (isWrappedPrimitive(this._data)) {\n return false;\n }\n // Count keys excluding .priority\n const keys = Object.keys(this._data).filter(k => k !== '.priority');\n return keys.length > 0;\n }\n\n /**\n * Check if this snapshot has a specific child.\n * `.priority` is always accessible if it exists.\n * Wrapped primitives have no children except `.priority`.\n */\n hasChild(path: string): boolean {\n // .priority is accessible if it exists\n if (path === '.priority') {\n return this.getPriority() !== null;\n }\n // Wrapped primitives have no other children\n if (isWrappedPrimitive(this._data)) {\n return false;\n }\n const childData = getValueAtPath(this._data, path);\n return childData !== undefined && childData !== null;\n }\n\n /**\n * Get the number of children.\n * Excludes `.priority` metadata from count.\n * Wrapped primitives have 0 children.\n */\n numChildren(): number {\n if (typeof this._data !== 'object' || this._data === null) {\n return 0;\n }\n // Wrapped primitives have no children\n if (isWrappedPrimitive(this._data)) {\n return 0;\n }\n // Count keys excluding .priority\n return Object.keys(this._data).filter(k => k !== '.priority').length;\n }\n\n /**\n * Iterate over children in the correct order.\n * Uses pre-computed orderedKeys if available (from subscription View),\n * otherwise computes sorted keys based on query params.\n * Excludes `.priority` metadata from iteration.\n * Wrapped primitives have no children to iterate.\n */\n forEach(callback: (child: DataSnapshot) => boolean | void): void {\n if (typeof this._data !== 'object' || this._data === null) {\n return;\n }\n\n // Wrapped primitives have no children\n if (isWrappedPrimitive(this._data)) {\n return;\n }\n\n // Use pre-computed orderedKeys if available (from subscription View),\n // otherwise compute sorted keys based on query params\n const keys = this._orderedKeys ?? getSortedKeys(this._data, this._queryParams);\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 */\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 */\n getServerTimestamp(): number | null {\n return this._serverTimestamp;\n }\n\n /**\n * Export the snapshot data with priority metadata intact.\n * Unlike val(), this preserves `.value` and `.priority` wrappers.\n * Useful for serializing data while preserving priorities.\n *\n * @typeParam T - Optional type for the returned value. Defaults to `any`.\n */\n exportVal<T = any>(): T {\n return this._data as T;\n }\n\n /**\n * Export the snapshot data as JSON.\n * Same as exportVal() - preserves priority metadata.\n *\n * @typeParam T - Optional type for the returned value. Defaults to `any`.\n */\n toJSON<T = any>(): T {\n return this._data as T;\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 (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 { createTransport, TransportType } from './connection/createTransport';\nimport { AuthCompleteResponse, JoinCompleteResponse, MessageQueue } from './connection/MessageQueue';\nimport { PendingWriteManager } from './connection/PendingWriteManager';\nimport { SubscriptionManager, SnapshotCallback } from './connection/SubscriptionManager';\nimport { Transport } from './connection/Transport';\nimport { DatabaseReference } from './DatabaseReference';\nimport { DataSnapshot } from './DataSnapshot';\nimport { LarkError } from './LarkError';\nimport { DEFAULT_LARK_DOMAIN, EventType } from './protocol/constants';\nimport {\n AuthMessage,\n ClientMessage,\n JoinMessage,\n ServerMessage,\n UnauthMessage,\n isAckMessage,\n isAuthCompleteMessage,\n isEventMessage,\n isNackMessage,\n isPingMessage,\n QueryParams,\n TxOperation,\n TransactionMessage,\n} from './protocol/messages';\nimport { hashValue, isPrimitive } from './utils/hash';\nimport { normalizePath } from './utils/path';\nimport { validatePath, validateWriteData, validateWriteSize, validateVolatileWriteSize } from './utils/validation';\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 /** Lark domain (default: larkdb.net) */\n domain?: string;\n\n /**\n * Transport type preference.\n * - 'auto': Try WebTransport first, fall back to WebSocket (default)\n * - 'websocket': Force WebSocket only\n * - 'webtransport': Force WebTransport only (fails if unavailable)\n */\n transport?: TransportType;\n\n /**\n * Timeout for WebTransport connection attempt in milliseconds.\n * If WebTransport doesn't connect within this time, falls back to WebSocket.\n * Only applies when transport is 'auto'.\n * Default: 2000 (2 seconds)\n */\n webtransportTimeout?: number;\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\n/**\n * Connection state machine:\n * - 'disconnected': Not connected\n * - 'connecting': Initial connection in progress\n * - 'connected': WebSocket open\n * - 'joined': Join complete (database identified)\n * - 'authenticated': Auth complete (ready to operate)\n * - 'reconnecting': Attempting to reconnect after disconnect\n */\ntype ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'joined' | 'authenticated' | 'reconnecting';\n\n/** Callback for .info path subscriptions */\ntype InfoCallback = (snapshot: DataSnapshot) => void;\n\n/** Subscription info for .info paths */\ninterface InfoSubscription {\n path: string;\n callback: InfoCallback;\n}\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\n/**\n * Check if a value is a ServerValue placeholder.\n */\nfunction isServerValue(value: unknown): value is { '.sv': unknown } {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n '.sv' in value\n );\n}\n\n/**\n * Resolve ServerValue placeholders locally for optimistic updates.\n * - TIMESTAMP: replaced with Date.now()\n * - increment(delta): replaced with currentValue + delta (or delta if no current value)\n *\n * @param value - The value to resolve\n * @param currentValue - The current value at the path (for increment resolution)\n * @returns The value with ServerValues resolved locally\n */\nfunction resolveServerValuesLocally(value: unknown, currentValue?: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n // Check for ServerValue placeholder\n if (isServerValue(value)) {\n const sv = value['.sv'];\n\n // TIMESTAMP\n if (sv === 'timestamp') {\n return Date.now();\n }\n\n // increment\n if (typeof sv === 'object' && sv !== null && 'increment' in sv) {\n const delta = (sv as { increment: number }).increment;\n if (typeof currentValue === 'number') {\n return currentValue + delta;\n }\n // If current value is not a number, increment treats it as 0\n return delta;\n }\n\n // Unknown server value, return as-is (will be resolved by server)\n return value;\n }\n\n // Recursively process objects\n if (typeof value === 'object' && !Array.isArray(value)) {\n const result: Record<string, unknown> = {};\n const currentObj = currentValue !== null && typeof currentValue === 'object' && !Array.isArray(currentValue)\n ? currentValue as Record<string, unknown>\n : {};\n\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n result[key] = resolveServerValuesLocally(val, currentObj[key]);\n }\n return result;\n }\n\n // Recursively process arrays\n if (Array.isArray(value)) {\n return value.map((item, index) => {\n const currentArr = Array.isArray(currentValue) ? currentValue : [];\n return resolveServerValuesLocally(item, currentArr[index]);\n });\n }\n\n // Primitive values pass through unchanged\n return value;\n}\n\n/**\n * Server values that are resolved by the server when a write is committed.\n * Use these with set(), update(), or push() to have the server fill in values.\n */\nexport const ServerValue = {\n /**\n * A placeholder value for auto-populating the current server timestamp.\n * The server will replace this with the actual Unix timestamp in milliseconds.\n *\n * @example\n * ```javascript\n * import { ServerValue } from '@lark-sh/client';\n * await ref.set({ createdAt: ServerValue.TIMESTAMP });\n * // Server stores: { createdAt: 1704067200000 }\n * ```\n */\n TIMESTAMP: { '.sv': 'timestamp' } as const,\n\n /**\n * Returns a placeholder value for atomically incrementing a numeric field.\n * If the field doesn't exist or isn't a number, it's treated as 0.\n *\n * @param delta - The amount to increment by (can be negative to decrement)\n * @returns A server value placeholder\n *\n * @example\n * ```javascript\n * import { ServerValue } from '@lark-sh/client';\n * await ref.child('score').set(ServerValue.increment(10));\n * // Atomically adds 10 to the current score\n * ```\n */\n increment: (delta: number) => ({ '.sv': { increment: delta } }),\n};\n\nexport class LarkDatabase {\n /**\n * Server values that are resolved by the server when a write is committed.\n * Alias for the exported ServerValue object.\n */\n static ServerValue = ServerValue;\n\n private _state: ConnectionState = 'disconnected';\n private _auth: AuthInfo | null = null;\n private _databaseId: string;\n private _domain: string;\n private _volatilePaths: string[] = [];\n private _transportType: 'websocket' | 'webtransport' | null = null;\n\n // Auth state\n private _currentToken: string; // Token for auth (empty string = anonymous)\n private _isAnonymous: boolean; // True if connected anonymously\n\n // Reconnection state\n private _connectionId: string | null = null;\n private _connectOptions: ConnectOptions;\n private _intentionalDisconnect = false;\n private _reconnectAttempt = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n private transport: Transport | 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 private authStateChangedCallbacks = new Set<(auth: AuthInfo | null) => void>();\n\n // .info path subscriptions (handled locally, not sent to server)\n private infoSubscriptions: InfoSubscription[] = [];\n\n // Connection promise - shared by connect() and lazy operations\n private _connectionPromise: Promise<void> | null = null;\n private _serverTimeOffset: number = 0;\n\n /**\n * Create a new LarkDatabase instance.\n *\n * @param databaseId - Database ID in format \"project/database\"\n * @param options - Connection options (token, anonymous, domain, transport).\n * Defaults to anonymous auth if not specified.\n *\n * @example\n * ```typescript\n * // Lazy connection - connects on first operation\n * const db = new LarkDatabase('project/database', { anonymous: true });\n * db.ref('/users').on('value', cb); // Auto-connects here\n *\n * // Explicit connection - for UI feedback or error handling\n * const db = new LarkDatabase('project/database', { anonymous: true });\n * await db.connect(); // Explicitly await connection\n * db.ref('/users').on('value', cb);\n * ```\n */\n constructor(databaseId: string, options: ConnectOptions = {}) {\n this.messageQueue = new MessageQueue();\n this.subscriptionManager = new SubscriptionManager();\n this.pendingWrites = new PendingWriteManager();\n\n // Validate database ID\n const projectId = databaseId.split('/')[0];\n if (!projectId) {\n throw new Error('Invalid database ID: must be in format \"projectId/databaseName\"');\n }\n\n // Store config\n this._databaseId = databaseId;\n this._connectOptions = options;\n this._domain = options.domain || DEFAULT_LARK_DOMAIN;\n this._currentToken = options.token || '';\n this._isAnonymous = !options.token && options.anonymous !== false;\n\n // Initialize subscription manager callbacks\n // This enables lazy connection - on()/once() will trigger connection via sendSubscribeMessage\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 // ============================================\n // Connection State\n // ============================================\n\n /**\n * Whether the database is fully connected and authenticated.\n * Returns true when ready to perform database operations.\n */\n get connected(): boolean {\n return this._state === 'authenticated';\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._domain && this._databaseId) {\n const projectId = this._databaseId.split('/')[0];\n return `https://${projectId}.${this._domain}`;\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 */\n get volatilePaths(): string[] {\n return this._volatilePaths;\n }\n\n /**\n * Get the current transport type.\n * Returns 'websocket' or 'webtransport', or null if not connected.\n */\n get transportType(): 'websocket' | 'webtransport' | null {\n return this._transportType;\n }\n\n /**\n * Get the estimated server time offset in milliseconds.\n * Add this to Date.now() to get approximate server time.\n */\n get serverTimeOffset(): number {\n return this._serverTimeOffset;\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 * Explicitly connect to the database.\n *\n * This is optional - operations will auto-connect if needed.\n * Use this when you want to:\n * - Await connection completion for UI feedback\n * - Handle connection errors separately from operation errors\n * - Pre-warm the connection before first operation\n *\n * @returns Promise that resolves when fully authenticated\n */\n async connect(): Promise<void> {\n return this.ensureConnected();\n }\n\n /**\n * Ensure connection is established, triggering it if needed.\n * Multiple concurrent calls share the same connection promise.\n */\n private async ensureConnected(): Promise<void> {\n // Already authenticated - nothing to do\n if (this._state === 'authenticated') {\n return;\n }\n\n // Connection in progress - wait for it\n if (this._connectionPromise) {\n return this._connectionPromise;\n }\n\n // Need to connect\n this._intentionalDisconnect = false;\n this._connectionPromise = this.performConnect(this._databaseId, this._connectOptions);\n\n try {\n await this._connectionPromise;\n } catch (error) {\n // Clear promise so next attempt can retry\n this._connectionPromise = null;\n throw error;\n }\n }\n\n /**\n * Internal connect implementation used by both initial connect and reconnect.\n * Implements the Join → Auth flow:\n * 1. Connect WebSocket\n * 2. Send join (identifies database)\n * 3. Send auth (authenticates user - required even for anonymous)\n */\n private async performConnect(\n databaseId: string,\n options: ConnectOptions,\n isReconnect = false\n ): Promise<void> {\n this._state = isReconnect ? 'reconnecting' : 'connecting';\n\n try {\n // Construct server URL from database ID\n // Format: \"projectId/databaseName\" -> \"wss://projectId.larkdb.net/ws\"\n const projectId = databaseId.split('/')[0];\n const wsUrl = `wss://${projectId}.${this._domain}/ws`;\n\n // Step 2: Connect to the server via WebTransport or WebSocket\n const transportResult = await createTransport(\n wsUrl,\n {\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 transport: options.transport,\n webtransportTimeout: options.webtransportTimeout,\n }\n );\n\n this.transport = transportResult.transport;\n this._transportType = transportResult.type;\n this._state = 'connected';\n\n // Step 3: Send join message (identifies the database, no token)\n const joinRequestId = this.messageQueue.nextRequestId();\n const joinMessage: JoinMessage = {\n o: 'j',\n d: databaseId,\n r: joinRequestId,\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(joinRequestId)) as JoinCompleteResponse;\n this._volatilePaths = joinResponse.volatilePaths;\n this._connectionId = joinResponse.connectionId;\n\n // Calculate server time offset (for .info/serverTimeOffset)\n if (joinResponse.serverTime != null) {\n this._serverTimeOffset = joinResponse.serverTime - Date.now();\n }\n\n this._state = 'joined';\n\n // Step 4: Send auth message (REQUIRED - even for anonymous)\n const authRequestId = this.messageQueue.nextRequestId();\n const authMessage: AuthMessage = {\n o: 'au',\n t: this._currentToken ?? '',\n r: authRequestId,\n };\n\n this.send(authMessage);\n\n // Wait for auth complete (returns UID)\n const authResponse = (await this.messageQueue.registerRequest(authRequestId)) as AuthCompleteResponse;\n\n // Update auth info from auth response\n this._auth = {\n uid: authResponse.uid || '',\n provider: this._isAnonymous ? 'anonymous' : 'custom',\n token: {}, // Token claims would need to be decoded from the token if needed\n };\n\n this._state = 'authenticated';\n this._reconnectAttempt = 0; // Reset reconnect attempt counter on success\n\n // Fire connection state change for .info/connected subscribers\n this.fireConnectionStateChange();\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\n // Fire auth state change\n this.authStateChangedCallbacks.forEach((cb) => cb(this._auth));\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 transport but keep config for retry\n this._state = 'disconnected';\n this._auth = null;\n this._connectionId = null;\n this._transportType = null;\n this.transport?.close();\n this.transport = 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 // Check if we were fully authenticated or at least partially connected\n const wasAuthenticated = this._state === 'authenticated';\n const wasPartiallyConnected = this._state === 'connected' || this._state === 'joined';\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 ((wasAuthenticated || wasPartiallyConnected) && this.transport) {\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 (wasAuthenticated || wasPartiallyConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n }\n\n /**\n * Temporarily disable the connection.\n * Disconnects from the server but preserves subscriptions for later reconnection via goOnline().\n */\n goOffline(): void {\n if (this._state === 'authenticated' || this._state === 'joined' ||\n this._state === 'connected' || this._state === 'reconnecting') {\n this._intentionalDisconnect = true;\n // Clear any pending reconnect timer\n if (this._reconnectTimer) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n this.transport?.close();\n this.transport = null;\n this._state = 'disconnected';\n // Keep cache intact so get()/once() can read from it while offline\n // Fire connection state change for .info/connected subscribers\n this.fireConnectionStateChange();\n }\n }\n\n /**\n * Re-enable the connection after goOffline().\n * Reconnects to the database and restores subscriptions.\n */\n goOnline(): void {\n this._intentionalDisconnect = false;\n\n // If we're disconnected and have connection info, reconnect\n if (this._state === 'disconnected' && this._databaseId && this._connectOptions) {\n this._state = 'reconnecting';\n this.reconnectingCallbacks.forEach((cb) => cb());\n // Attempt to reconnect immediately (no backoff for intentional goOnline)\n this._reconnectAttempt = 0;\n this.attemptReconnect();\n }\n }\n\n /**\n * Full cleanup - clears connection state including subscriptions.\n * Used for intentional disconnect.\n * Preserves config (databaseId, domain, token) so connect() can be called again.\n */\n private cleanupFull(): void {\n const wasAuthenticated = this._state === 'authenticated';\n\n this.transport?.close();\n this.transport = null;\n this._state = 'disconnected';\n this._auth = null;\n this._volatilePaths = [];\n this._connectionId = null;\n this._transportType = null;\n this._reconnectAttempt = 0;\n this._connectionPromise = null;\n this.subscriptionManager.clear();\n this.messageQueue.rejectAll(new Error('Connection closed'));\n this.pendingWrites.clear();\n\n // Fire connection state change for .info/connected subscribers\n if (wasAuthenticated) {\n this.fireConnectionStateChange();\n }\n\n // Clear info subscriptions\n this.infoSubscriptions = [];\n }\n\n /**\n * Partial cleanup - preserves state needed for reconnect.\n * Used for unexpected disconnect.\n */\n private cleanupForReconnect(): void {\n this.transport?.close();\n this.transport = null;\n this._auth = null;\n // Keep: _databaseId, _domain, _connectionId, _connectOptions, _transportType\n // Keep: _currentToken, _isAnonymous (for re-auth on reconnect)\n // Keep: subscriptionManager subscriptions (but clear cache)\n // Keep: pendingWrites\n // Keep: infoSubscriptions (will fire again when reconnected)\n this.subscriptionManager.clearCacheOnly();\n this.messageQueue.rejectAll(new Error('Connection closed'));\n\n // Fire connection state change for .info/connected subscribers (now disconnected)\n this.fireConnectionStateChange();\n }\n\n // ============================================\n // .info Path Handling\n // ============================================\n\n /**\n * Check if a path is a .info path (handled locally).\n */\n private isInfoPath(path: string): boolean {\n const normalizedPath = normalizePath(path) || '/';\n return normalizedPath === '/.info' || normalizedPath.startsWith('/.info/');\n }\n\n /**\n * Get the current value for a .info path.\n */\n private getInfoValue(path: string): unknown {\n const normalizedPath = normalizePath(path) || '/';\n\n if (normalizedPath === '/.info/connected') {\n return this._state === 'authenticated';\n }\n\n if (normalizedPath === '/.info/serverTimeOffset') {\n return this._serverTimeOffset;\n }\n\n // Unknown .info path - return null\n return null;\n }\n\n /**\n * Subscribe to a .info path.\n * Returns an unsubscribe function.\n */\n private subscribeToInfo(path: string, callback: InfoCallback): () => void {\n const normalizedPath = normalizePath(path) || '/';\n const subscription: InfoSubscription = { path: normalizedPath, callback };\n this.infoSubscriptions.push(subscription);\n\n // Fire initial value immediately\n const value = this.getInfoValue(normalizedPath);\n const snapshot = new DataSnapshot(value, normalizedPath, this);\n // Use setTimeout to ensure callback is called asynchronously (like real subscriptions)\n setTimeout(() => callback(snapshot), 0);\n\n // Return unsubscribe function\n return () => {\n const index = this.infoSubscriptions.indexOf(subscription);\n if (index !== -1) {\n this.infoSubscriptions.splice(index, 1);\n }\n };\n }\n\n /**\n * Fire events for .info path changes.\n */\n private fireInfoEvents(path: string): void {\n const normalizedPath = normalizePath(path) || '/';\n const value = this.getInfoValue(normalizedPath);\n\n for (const sub of this.infoSubscriptions) {\n if (sub.path === normalizedPath) {\n const snapshot = new DataSnapshot(value, normalizedPath, this);\n sub.callback(snapshot);\n }\n }\n }\n\n /**\n * Fire connection state change events to .info/connected subscribers.\n */\n private fireConnectionStateChange(): void {\n this.fireInfoEvents('/.info/connected');\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) {\n return;\n }\n\n // Set up connection promise for reconnect - operations can await this\n this._connectionPromise = this.performConnect(this._databaseId, this._connectOptions, true);\n\n try {\n await this._connectionPromise;\n } catch {\n // performConnect already handles scheduling next attempt on failure\n this._connectionPromise = null;\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** (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 (async for hash computation)\n txOps = await Promise.all(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 * Async because condition operations may require hash computation.\n */\n private async convertToTxOp(op: TransactionOp): Promise<TxOperation> {\n const path = normalizePath(op.path) || '/';\n validatePath(op.path, 'transaction');\n\n switch (op.op) {\n case 'set':\n validateWriteData(op.value, 'transaction');\n return { o: 's', p: path, v: op.value };\n case 'update':\n validateWriteData(op.value, 'transaction');\n return { o: 'u', p: path, v: op.value };\n case 'delete':\n return { o: 'd', p: path };\n case 'condition':\n // Condition values are for comparison, not writing, but still validate\n if (op.value !== undefined && op.value !== null) {\n validateWriteData(op.value, 'transaction condition');\n }\n // Use direct value for primitives, hash for complex objects\n if (isPrimitive(op.value)) {\n return { o: 'c', p: path, v: op.value };\n } else {\n const hash = await hashValue(op.value);\n return { o: 'c', p: path, h: hash };\n }\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 validatePath(path, 'transaction');\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 validateWriteData(value, 'transaction');\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 if (this._state !== 'authenticated') await this.ensureConnected();\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 * Register a callback for auth state changes.\n * Fires when user signs in, signs out, or auth changes.\n * Returns an unsubscribe function.\n */\n onAuthStateChanged(callback: (auth: AuthInfo | null) => void): () => void {\n this.authStateChangedCallbacks.add(callback);\n return () => this.authStateChangedCallbacks.delete(callback);\n }\n\n // ============================================\n // Authentication Management\n // ============================================\n\n /**\n * Sign in with a new auth token while connected.\n * Changes the authenticated user without disconnecting.\n *\n * Note: Some subscriptions may be revoked if the new user doesn't have\n * permission. Listen for 'permission_denied' errors on your subscriptions.\n *\n * @param token - The auth token for the new user\n * @throws Error if not connected (must call connect() first)\n */\n async signIn(token: string): Promise<void> {\n if (this._state !== 'authenticated' && this._state !== 'joined') {\n throw new LarkError('not_connected', 'Must be connected first - call connect()');\n }\n\n // Send auth with new token\n const authRequestId = this.messageQueue.nextRequestId();\n const authMessage: AuthMessage = {\n o: 'au',\n t: token,\n r: authRequestId,\n };\n\n this.send(authMessage);\n\n // Wait for auth complete\n const authResponse = (await this.messageQueue.registerRequest(authRequestId)) as AuthCompleteResponse;\n\n // Update auth state\n this._currentToken = token;\n this._isAnonymous = false;\n this._auth = {\n uid: authResponse.uid || '',\n provider: 'custom',\n token: {},\n };\n\n // Fire auth state change\n this.authStateChangedCallbacks.forEach((cb) => cb(this._auth));\n }\n\n /**\n * Sign out the current user.\n * Reverts to anonymous authentication.\n *\n * Note: Some subscriptions may be revoked if anonymous users don't have\n * permission. Listen for 'permission_denied' errors on your subscriptions.\n */\n async signOut(): Promise<void> {\n if (this._state !== 'authenticated') {\n return; // Already signed out or not connected\n }\n\n // Send unauth message\n const unauthRequestId = this.messageQueue.nextRequestId();\n const unauthMessage: UnauthMessage = {\n o: 'ua',\n r: unauthRequestId,\n };\n\n this.send(unauthMessage);\n\n // Wait for auth complete (server responds with ac even for unauth)\n const authResponse = (await this.messageQueue.registerRequest(unauthRequestId)) as AuthCompleteResponse;\n\n // Update auth state (now anonymous)\n this._currentToken = '';\n this._isAnonymous = true;\n this._auth = {\n uid: authResponse.uid || '',\n provider: 'anonymous',\n token: {},\n };\n\n // Fire auth state change\n this.authStateChangedCallbacks.forEach((cb) => cb(this._auth));\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 // DEBUG: Log all incoming messages\n if (process.env.LARK_DEBUG) {\n console.log('[LARK] <<< SERVER:', JSON.stringify(message, null, 2));\n }\n\n // Handle ping - respond with pong immediately\n if (isPingMessage(message)) {\n this.transport?.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 // Handle subscription revocation due to auth change\n if (message.e === 'permission_denied' && message.sp) {\n const path = message.sp;\n console.warn(`Subscription revoked at ${path}: permission_denied`);\n // Clean up local subscription state\n this.subscriptionManager.handleSubscriptionRevoked(path);\n return;\n }\n\n // Log the error (except for condition_failed which is expected in transactions)\n if (message.e !== 'condition_failed') {\n console.error(`Write failed (${message.e}): ${message.m || message.e}`);\n }\n\n // Remove the pending write from affected Views\n // With dual-cache, this automatically reverts to server truth and fires callbacks if needed\n this.subscriptionManager.handleWriteNack(message.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 // Check if we were fully authenticated (ready to operate)\n const wasAuthenticated = this._state === 'authenticated';\n const wasReconnecting = this._state === 'reconnecting';\n // Also consider partial connection states (connected, joined) as needing callbacks\n const wasPartiallyConnected = this._state === 'connected' || this._state === 'joined';\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 (wasAuthenticated || wasPartiallyConnected) {\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 ((wasAuthenticated || wasPartiallyConnected || 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 (wasAuthenticated || wasPartiallyConnected) {\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 (wasAuthenticated || wasPartiallyConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n }\n }\n\n private handleError(error: Error): void {\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.transport || !this.transport.connected) {\n throw new LarkError('not_connected', 'Not connected to database');\n }\n // DEBUG: Log all outgoing messages\n if (process.env.LARK_DEBUG) {\n console.log('[LARK] >>> CLIENT:', JSON.stringify(message, null, 2));\n }\n this.transport.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 if (this._state !== 'authenticated') await this.ensureConnected();\n const normalizedPath = normalizePath(path) || '/';\n\n // Validate data before sending\n validateWriteData(value, normalizedPath);\n validateWriteSize(value, normalizedPath);\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 // Resolve ServerValues locally for optimistic update\n // Get current cached value for increment resolution\n const { value: currentValue } = this.subscriptionManager.getCachedValue(normalizedPath);\n const resolvedValue = resolveServerValuesLocally(value, currentValue);\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, resolvedValue, requestId, 'set');\n\n const message: ClientMessage = {\n o: 's',\n p: normalizedPath,\n v: value, // Send original value with ServerValue placeholders to server\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 if (this._state !== 'authenticated') await this.ensureConnected();\n const normalizedPath = normalizePath(path) || '/';\n\n // Validate each value being updated\n for (const [key, value] of Object.entries(values)) {\n const fullPath = key.startsWith('/') ? key : `${normalizedPath}/${key}`;\n validateWriteData(value, fullPath);\n }\n // Validate total size of the update\n validateWriteSize(values, normalizedPath);\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 // Resolve ServerValues locally for optimistic update\n // For update, we need to resolve each value with its own current value at the appropriate path\n const resolvedValues: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(values)) {\n const fullPath = key.startsWith('/') ? key : `${normalizedPath}/${key}`;\n const { value: currentValue } = this.subscriptionManager.getCachedValue(fullPath);\n resolvedValues[key] = resolveServerValuesLocally(value, currentValue);\n }\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, resolvedValues, requestId, 'update');\n\n const message: ClientMessage = {\n o: 'u',\n p: normalizedPath,\n v: values, // Send original value with ServerValue placeholders to server\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 if (this._state !== 'authenticated') await this.ensureConnected();\n const normalizedPath = normalizePath(path) || '/';\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 * When using WebTransport, volatile writes are sent via datagrams (UDP).\n */\n _sendVolatileSet(path: string, value: unknown): void {\n const normalizedPath = normalizePath(path) || '/';\n\n // Validate volatile write size (2 KB limit)\n validateVolatileWriteSize(value, normalizedPath);\n\n // Apply optimistically to local cache (for UI feedback)\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, value, '', 'set');\n\n if (this._state !== 'authenticated' || !this.transport || !this.transport.connected) {\n // Silent fail for volatile writes when not authenticated or disconnected\n return;\n }\n\n // Fire-and-forget: no request ID, no pending tracking, no await\n // Uses sendVolatile which goes via datagrams on WebTransport\n const message: ClientMessage = {\n o: 's',\n p: normalizedPath,\n v: value,\n };\n this.transport.sendVolatile(JSON.stringify(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 // Validate volatile write size (2 KB limit)\n validateVolatileWriteSize(values, normalizedPath);\n\n // Apply optimistically to local cache (for UI feedback)\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, values, '', 'update');\n\n if (this._state !== 'authenticated' || !this.transport || !this.transport.connected) {\n // Silent fail for volatile writes when not authenticated or disconnected\n return;\n }\n\n // Fire-and-forget via datagram on WebTransport\n const message: ClientMessage = {\n o: 'u',\n p: normalizedPath,\n v: values,\n };\n this.transport.sendVolatile(JSON.stringify(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 if (this._state !== 'authenticated' || !this.transport || !this.transport.connected) {\n // Silent fail for volatile writes when not authenticated or disconnected\n return;\n }\n\n // Fire-and-forget via datagram on WebTransport\n const message: ClientMessage = {\n o: 'd',\n p: normalizedPath,\n };\n this.transport.sendVolatile(JSON.stringify(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 // Handle .info paths locally (they're synthetic, not on the server)\n if (this.isInfoPath(normalizedPath)) {\n const value = this.getInfoValue(normalizedPath);\n return new DataSnapshot(value, path, this);\n }\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 if (this._state !== 'authenticated') await this.ensureConnected();\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, { queryParams: query ?? null });\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 if (this._state !== 'authenticated') await this.ensureConnected();\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 Get a cached value from the subscription manager.\n * Used for optimistic writes where we need the current value without a network fetch.\n */\n _getCachedValue(path: string): { value: unknown; found: boolean } {\n const normalizedPath = normalizePath(path) || '/';\n return this.subscriptionManager.getCachedValue(normalizedPath);\n }\n\n /**\n * @internal Send a subscribe message to server.\n * Includes tag for non-default queries to enable proper event routing.\n */\n private async sendSubscribeMessage(path: string, queryParams?: QueryParams, tag?: number): Promise<void> {\n if (this._state !== 'authenticated') await this.ensureConnected();\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'sb',\n p: normalizePath(path) || '/',\n r: requestId,\n ...queryParams,\n ...(tag !== undefined ? { tag } : {}),\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send an unsubscribe message to server.\n * Includes query params and tag so server can identify which specific subscription to remove.\n */\n private async sendUnsubscribeMessage(path: string, queryParams?: QueryParams, tag?: number): Promise<void> {\n if (this._state !== 'authenticated') await this.ensureConnected();\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'us',\n p: normalizePath(path) || '/',\n r: requestId,\n ...queryParams,\n ...(tag !== undefined ? { tag } : {}),\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 orderedKeys?: string[]\n ): DataSnapshot {\n return new DataSnapshot(value, path, this, {\n volatile,\n serverTimestamp: serverTimestamp ?? null,\n orderedKeys: orderedKeys ?? 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, queryIdentifier?: string): () => void {\n // Handle .info paths locally (not sent to server)\n if (this.isInfoPath(path)) {\n return this.subscribeToInfo(path, callback);\n }\n return this.subscriptionManager.subscribe(path, eventType, callback, queryParams, queryIdentifier);\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":["WebSocketNode","view","canonicalize","actualValue","value"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/connection/WebSocketTransport.ts","../src/connection/WebTransportClient.ts","../src/connection/createTransport.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/protocol/constants.ts","../src/OnDisconnect.ts","../src/utils/hash.ts","../src/utils/pushid.ts","../src/utils/validation.ts","../src/DatabaseReference.ts","../src/DataSnapshot.ts","../src/utils/volatile.ts","../src/LarkDatabase.ts"],"sourcesContent":["/**\n * @lark-sh/client - JavaScript client for Lark real-time database\n *\n * @example\n * ```typescript\n * import { LarkDatabase } from '@lark-sh/client';\n *\n * const db = new LarkDatabase();\n * await db.connect('my-project/my-database', { anonymous: true });\n *\n * // Write data\n * await db.ref('players/abc').set({ name: 'Riley', score: 0 });\n *\n * // Subscribe to changes\n * const unsubscribe = db.ref('players').on('child_changed', (snap) => {\n * console.log(`Player ${snap.key} changed:`, snap.val());\n * });\n *\n * // Later: unsubscribe\n * unsubscribe();\n *\n * // Disconnect\n * await db.disconnect();\n * ```\n */\n\n// Main classes\nexport { LarkDatabase, ServerValue } from './LarkDatabase';\nexport type {\n ConnectOptions,\n AuthInfo,\n TransactionOp,\n TransactionSetOp,\n TransactionUpdateOp,\n TransactionDeleteOp,\n TransactionConditionOp,\n TransactionObject,\n} from './LarkDatabase';\n\nexport { DatabaseReference, ThenableReference } from './DatabaseReference';\nexport type { SnapshotCallback, QueryState, TransactionResult } from './DatabaseReference';\n\nexport { DataSnapshot } from './DataSnapshot';\n\nexport { OnDisconnect } from './OnDisconnect';\n\nexport { LarkError } from './LarkError';\n\n// Protocol types (for advanced usage)\nexport type { EventType } from './protocol/constants';\nexport type { QueryParams } from './protocol/messages';\n\n// Connection utilities (for advanced usage)\nexport { PendingWriteManager } from './connection/PendingWriteManager';\nexport type { WriteOperation, PendingWrite } from './connection/PendingWriteManager';\n\n// Transport (for advanced usage)\nexport type { TransportType } from './connection/createTransport';\n\n// Utility functions (for advanced usage)\nexport { generatePushId } from './utils/pushid';\nexport { isVolatilePath } from './utils/volatile';\n","/**\n * WebSocketTransport - WebSocket implementation of the Transport interface.\n * Works in both browser (native WebSocket) and Node.js (ws package).\n */\n\nimport WebSocketNode from 'ws';\nimport { Transport, TransportState, TransportOptions } from './Transport';\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 class WebSocketTransport implements Transport {\n private ws: WebSocket | null = null;\n private options: TransportOptions;\n private _state: TransportState = 'disconnected';\n\n constructor(options: TransportOptions) {\n this.options = options;\n }\n\n get state(): TransportState {\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 const error = new Error('WebSocket connection failed');\n reject(error);\n this.options.onError(error);\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', () => {\n this.options.onError(new Error('WebSocket error'));\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 * Send a volatile message.\n * WebSocket doesn't have a true unreliable channel, so this just calls send().\n */\n sendVolatile(data: string): void {\n this.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 * WebTransportClient - WebTransport implementation of the Transport interface.\n *\n * Uses QUIC-based WebTransport for:\n * - Reliable bidirectional stream (with 4-byte length prefix framing)\n * - Unreliable datagrams for volatile writes\n *\n * Browser-only (no Node.js support). Falls back to WebSocket if unavailable.\n */\n\nimport { Transport, TransportState, TransportOptions } from './Transport';\n\n// TypeScript declarations for WebTransport API\n// These are not in lib.dom.d.ts yet\ndeclare class WebTransport {\n constructor(url: string);\n ready: Promise<void>;\n closed: Promise<{ closeCode: number; reason: string }>;\n close(options?: { closeCode?: number; reason?: string }): void;\n createBidirectionalStream(): Promise<WebTransportBidirectionalStream>;\n datagrams: {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n };\n}\n\ninterface WebTransportBidirectionalStream {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n}\n\nexport class WebTransportClient implements Transport {\n private transport: WebTransport | null = null;\n private writer: WritableStreamDefaultWriter<Uint8Array> | null = null;\n private datagramWriter: WritableStreamDefaultWriter<Uint8Array> | null = null;\n private options: TransportOptions;\n private _state: TransportState = 'disconnected';\n private readLoopActive = false;\n private datagramLoopActive = false;\n\n constructor(options: TransportOptions) {\n this.options = options;\n }\n\n get state(): TransportState {\n return this._state;\n }\n\n get connected(): boolean {\n return this._state === 'connected';\n }\n\n /**\n * Connect to a WebTransport server.\n */\n async connect(url: string): Promise<void> {\n if (this._state !== 'disconnected') {\n throw new Error('Already connected or connecting');\n }\n\n this._state = 'connecting';\n\n try {\n // Create WebTransport connection\n this.transport = new WebTransport(url);\n await this.transport.ready;\n\n // Open bidirectional stream for reliable messages\n const stream = await this.transport.createBidirectionalStream();\n this.writer = stream.writable.getWriter();\n\n // Get datagram writer for volatile messages\n this.datagramWriter = this.transport.datagrams.writable.getWriter();\n\n this._state = 'connected';\n\n // Start reading from reliable stream\n this.readLoop(stream.readable.getReader());\n\n // Start reading datagrams\n this.datagramLoop(this.transport.datagrams.readable.getReader());\n\n // Handle connection close\n this.transport.closed\n .then(({ closeCode, reason }) => {\n this.handleClose(closeCode, reason);\n })\n .catch(() => {\n this.handleClose(0, 'Connection failed');\n });\n\n this.options.onOpen();\n } catch (err) {\n this._state = 'disconnected';\n this.transport = null;\n this.writer = null;\n this.datagramWriter = null;\n throw err;\n }\n }\n\n /**\n * Read messages from the reliable stream.\n * Messages are framed with 4-byte big-endian length prefix.\n */\n private async readLoop(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<void> {\n this.readLoopActive = true;\n let buffer = new Uint8Array(0);\n\n try {\n while (this.readLoopActive) {\n const { value, done } = await reader.read();\n if (done) break;\n\n // Append new data to buffer\n const newBuffer = new Uint8Array(buffer.length + value.length);\n newBuffer.set(buffer);\n newBuffer.set(value, buffer.length);\n buffer = newBuffer;\n\n // Parse complete messages\n while (buffer.length >= 4) {\n const view = new DataView(buffer.buffer, buffer.byteOffset);\n const msgLength = view.getUint32(0, false); // big-endian\n\n if (buffer.length < 4 + msgLength) break; // Not enough data yet\n\n // Extract message\n const msgBytes = buffer.slice(4, 4 + msgLength);\n const json = new TextDecoder().decode(msgBytes);\n\n // Deliver to handler\n try {\n this.options.onMessage(json);\n } catch (err) {\n console.error('Error handling message:', err);\n }\n\n // Remove processed bytes\n buffer = buffer.slice(4 + msgLength);\n }\n }\n } catch (err) {\n if (this.readLoopActive) {\n console.error('Read loop error:', err);\n }\n }\n }\n\n /**\n * Read volatile events from datagrams.\n * No framing needed - each datagram is a complete JSON message.\n */\n private async datagramLoop(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<void> {\n this.datagramLoopActive = true;\n\n try {\n while (this.datagramLoopActive) {\n const { value, done } = await reader.read();\n if (done) break;\n\n const json = new TextDecoder().decode(value);\n\n // Deliver to handler\n try {\n this.options.onMessage(json);\n } catch (err) {\n console.error('Error handling datagram:', err);\n }\n }\n } catch (err) {\n if (this.datagramLoopActive) {\n console.error('Datagram loop error:', err);\n }\n }\n }\n\n /**\n * Handle connection close.\n */\n private handleClose(code: number, reason: string): void {\n if (this._state === 'disconnected') return;\n\n this.readLoopActive = false;\n this.datagramLoopActive = false;\n this._state = 'disconnected';\n this.transport = null;\n this.writer = null;\n this.datagramWriter = null;\n\n this.options.onClose(code, reason);\n }\n\n /**\n * Send a message over the reliable stream.\n * Uses 4-byte big-endian length prefix.\n */\n send(data: string): void {\n if (!this.writer || this._state !== 'connected') {\n throw new Error('WebTransport not connected');\n }\n\n const encoder = new TextEncoder();\n const msgBytes = encoder.encode(data);\n\n // Create length prefix (4 bytes, big-endian)\n const lengthBuffer = new ArrayBuffer(4);\n new DataView(lengthBuffer).setUint32(0, msgBytes.length, false); // false = big-endian\n\n // Send length + message\n // Note: We can't await in a sync method, so we fire and let errors bubble up\n this.writer.write(new Uint8Array(lengthBuffer)).catch((err) => {\n console.error('Failed to write length prefix:', err);\n });\n this.writer.write(msgBytes).catch((err) => {\n console.error('Failed to write message:', err);\n });\n }\n\n /**\n * Send a volatile message via datagram (unreliable).\n * No framing needed - each datagram is a complete message.\n */\n sendVolatile(data: string): void {\n if (!this.datagramWriter || this._state !== 'connected') {\n throw new Error('WebTransport not connected');\n }\n\n const encoder = new TextEncoder();\n const msgBytes = encoder.encode(data);\n\n // Fire and forget - datagrams are unreliable by design\n this.datagramWriter.write(msgBytes).catch(() => {\n // Ignore datagram write errors - they're expected for unreliable transport\n });\n }\n\n /**\n * Close the WebTransport connection.\n */\n close(code: number = 0, reason: string = 'Client disconnect'): void {\n this.readLoopActive = false;\n this.datagramLoopActive = false;\n\n if (this.writer) {\n this.writer.releaseLock();\n this.writer = null;\n }\n\n if (this.datagramWriter) {\n this.datagramWriter.releaseLock();\n this.datagramWriter = null;\n }\n\n if (this.transport) {\n this.transport.close({ closeCode: code, reason });\n this.transport = null;\n }\n\n this._state = 'disconnected';\n }\n}\n\n/**\n * Check if WebTransport is available in the current environment.\n */\nexport function isWebTransportAvailable(): boolean {\n return typeof globalThis !== 'undefined' && 'WebTransport' in globalThis;\n}\n","/**\n * Transport factory - creates appropriate transport based on availability and options.\n */\n\nimport { Transport, TransportOptions } from './Transport';\nimport { WebSocketTransport } from './WebSocketTransport';\nimport { WebTransportClient, isWebTransportAvailable } from './WebTransportClient';\n\nexport type TransportType = 'auto' | 'websocket' | 'webtransport';\n\nexport interface CreateTransportOptions {\n /** Force a specific transport type. Default: 'auto' */\n transport?: TransportType;\n /** Timeout for WebTransport connection attempt in ms. Default: 2000 */\n webtransportTimeout?: number;\n}\n\n/**\n * Transform WebSocket URL to WebTransport URL.\n * wss://projectId.larkdb.net/ws → https://projectId.larkdb.net:7778-7809/wt\n *\n * Uses a random port in range 7778-7809 for load balancing.\n */\nexport function wsUrlToWtUrl(wsUrl: string): string {\n const url = new URL(wsUrl);\n\n // Change protocol: wss → https, ws → http\n url.protocol = url.protocol === 'wss:' ? 'https:' : 'http:';\n\n // Choose random port in range 7778-7809 (inclusive) for load balancing\n const port = 7778 + Math.floor(Math.random() * 32);\n url.port = String(port);\n\n // Change path from /ws to /wt\n url.pathname = '/wt';\n\n return url.toString();\n}\n\n/**\n * Result of createTransport - includes both the transport and info about what was created.\n */\nexport interface TransportResult {\n transport: Transport;\n type: 'websocket' | 'webtransport';\n url: string;\n}\n\n/**\n * Create a transport connection.\n *\n * By default, attempts WebTransport first (if available), falling back to WebSocket.\n * Can be forced to a specific transport type via options.\n *\n * @param wsUrl - The WebSocket URL (e.g., wss://projectId.larkdb.net/ws)\n * @param transportOptions - Callbacks for message handling\n * @param options - Transport type preferences\n */\nexport async function createTransport(\n wsUrl: string,\n transportOptions: TransportOptions,\n options: CreateTransportOptions = {}\n): Promise<TransportResult> {\n const preferredType = options.transport || 'auto';\n\n // Determine if we should try WebTransport\n const shouldTryWebTransport =\n (preferredType === 'auto' || preferredType === 'webtransport') && isWebTransportAvailable();\n\n // If explicitly requested webtransport but not available, throw\n if (preferredType === 'webtransport' && !isWebTransportAvailable()) {\n throw new Error('WebTransport is not available in this environment');\n }\n\n // Try WebTransport first if appropriate\n if (shouldTryWebTransport) {\n const wtUrl = wsUrlToWtUrl(wsUrl);\n const wtTransport = new WebTransportClient(transportOptions);\n const timeout = options.webtransportTimeout ?? 2000;\n\n // Create a timeout promise that we can clean up\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error('WebTransport connection timeout')), timeout);\n });\n\n try {\n await Promise.race([wtTransport.connect(wtUrl), timeoutPromise]);\n return {\n transport: wtTransport,\n type: 'webtransport',\n url: wtUrl,\n };\n } catch (err) {\n // Clean up the transport on failure/timeout\n try {\n wtTransport.close();\n } catch {\n // Ignore cleanup errors\n }\n // If explicitly requested webtransport, don't fall back\n if (preferredType === 'webtransport') {\n throw err;\n }\n // Otherwise fall through to WebSocket\n console.warn('WebTransport connection failed, falling back to WebSocket:', err);\n } finally {\n // Always clear the timeout to prevent unhandled rejections\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n // Use WebSocket\n const wsTransport = new WebSocketTransport(transportOptions);\n await wsTransport.connect(wsUrl);\n\n return {\n transport: wsTransport,\n type: 'websocket',\n url: wsUrl,\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 d: string; // database ID (e.g., \"project/database\")\n r: string; // request ID\n pcid?: string; // previous connection ID (for reconnect deduplication)\n}\n\nexport interface AuthMessage {\n o: 'au'; // operation: auth\n t: string; // token (empty string for anonymous)\n r: string; // request ID\n}\n\nexport interface UnauthMessage {\n o: 'ua'; // operation: unauth (sign out)\n r: string; // request ID\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 r: string; // request ID\n tag?: number; // client-local tag for routing events to correct View (non-default queries)\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 startAfter?: unknown;\n startAfterKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n endBefore?: unknown;\n endBeforeKey?: 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 startAfter?: unknown; // startAfter value (exclusive)\n startAfterKey?: string; // startAfter key tie-breaker\n endAt?: unknown; // endAt value\n endAtKey?: string; // endAt key tie-breaker\n endBefore?: unknown; // endBefore value (exclusive)\n endBeforeKey?: string; // endBefore 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 tag?: number; // client-local tag (same as sent on subscribe)\n // Query parameters (optional) - same format as SubscribeMessage\n // Server uses these to identify which specific subscription to remove\n orderBy?: string;\n orderByChild?: string;\n limitToFirst?: number;\n limitToLast?: number;\n startAt?: unknown;\n startAtKey?: string;\n startAfter?: unknown;\n startAfterKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n endBefore?: unknown;\n endBeforeKey?: string;\n equalTo?: unknown;\n equalToKey?: string;\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 startAfter?: unknown;\n startAfterKey?: string;\n endAt?: unknown;\n endAtKey?: string;\n endBefore?: unknown;\n endBeforeKey?: 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\n// Condition with direct value comparison (for primitives)\nexport interface TxConditionValueOp {\n o: 'c'; // condition (compare-and-swap check)\n p: string; // path\n v: unknown; // expected value (for primitives: null, boolean, number, string)\n}\n\n// Condition with hash comparison (for complex objects/arrays)\nexport interface TxConditionHashOp {\n o: 'c'; // condition (compare-and-swap check)\n p: string; // path\n h: string; // SHA-256 hash of JCS-canonicalized value\n}\n\nexport type TxConditionOp = TxConditionValueOp | TxConditionHashOp;\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 | AuthMessage\n | UnauthMessage\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 sp?: string; // subscription path (for subscription revocation due to auth change)\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 tag?: number; // tag for routing to specific View (non-default queries)\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 tag?: number; // tag for routing to specific View (non-default queries)\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 st?: number; // server time in ms (for calculating serverTimeOffset)\n}\n\nexport interface AuthCompleteMessage {\n ac: string; // auth complete with request ID\n au: string; // UID (empty string for anonymous)\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 | AuthCompleteMessage\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 isAuthCompleteMessage(msg: ServerMessage): msg is AuthCompleteMessage {\n return 'ac' 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 isAuthCompleteMessage,\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 serverTime: number | null;\n}\n\n/** Response from auth complete message */\nexport interface AuthCompleteResponse {\n uid: string | null; // null for anonymous (empty string from server)\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 serverTime: message.st ?? null,\n };\n pending.resolve(response);\n return true;\n }\n }\n\n // Auth complete (includes UID)\n if (isAuthCompleteMessage(message)) {\n const pending = this.pending.get(message.ac);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(message.ac);\n // Resolve with auth response (empty string from server means anonymous)\n const response: AuthCompleteResponse = {\n uid: message.au || 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 // Normalize undefined to null (null = no data at path)\n pending.resolve(message.ov ?? null);\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 * Sorting utilities for client-side data ordering.\n *\n * Implements sorting rules matching the server:\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 value 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 type-aware 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 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 no queryParams, default to priority ordering\n if (!queryParams) {\n return getNestedValue(value, '.priority');\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 if (queryParams.orderBy === 'key') {\n return null; // orderByKey uses key, not value\n }\n\n // No explicit orderBy - check if there are range filters or limits\n // If so, default to priority ordering (matching server behavior)\n const hasRangeFilter =\n queryParams.startAt !== undefined ||\n queryParams.startAfter !== undefined ||\n queryParams.endAt !== undefined ||\n queryParams.endBefore !== undefined ||\n queryParams.equalTo !== undefined;\n\n const hasLimit =\n queryParams.limitToFirst !== undefined ||\n queryParams.limitToLast !== undefined;\n\n if (hasRangeFilter || hasLimit) {\n // Default to priority when range filters or limits exist but no orderBy specified\n return getNestedValue(value, '.priority');\n }\n\n // No ordering constraints - key is used\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 explicitly ordering by key, just compare keys\n if (queryParams?.orderBy === 'key') {\n return compareKeys(a.key, b.key);\n }\n\n // Compare by sort value first (priority by default, or specified orderBy)\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 // Check if this is a wrapped primitive (only .value and .priority keys)\n // Wrapped primitives have no children to sort\n const keys = Object.keys(obj);\n if (\n keys.length === 2 &&\n '.value' in obj &&\n '.priority' in obj\n ) {\n return [];\n }\n\n for (const key of keys) {\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, compareValues, createSortEntries, 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\n/**\n * Expand path-like keys in an update object into nested structure.\n * Multi-path updates treat keys with '/' as paths.\n * E.g., { 'child/increment': 42 } becomes { child: { increment: 42 } }\n */\nfunction expandPathKeys(obj: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n if (key.includes('/')) {\n // Path-like key - expand into nested structure\n const segments = key.split('/').filter(s => s.length > 0);\n let current = result;\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 current[segments[segments.length - 1]] = value;\n } else {\n // Regular key - just assign\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Deep merge two objects, with source overwriting target.\n * Handles nested path expansions correctly.\n */\nfunction deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {\n const result = { ...target };\n\n for (const [key, value] of Object.entries(source)) {\n if (value === null) {\n // null = delete\n delete result[key];\n } else if (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n result[key] !== null &&\n typeof result[key] === 'object' &&\n !Array.isArray(result[key])\n ) {\n // Both are objects - deep merge\n result[key] = deepMerge(result[key] as Record<string, unknown>, value as Record<string, unknown>);\n } else {\n // Overwrite\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/** A pending write operation that hasn't been ACKed yet */\nexport interface PendingWriteEntry {\n requestId: string;\n relativePath: string; // Relative to this View's path ('/' for the view itself)\n value: unknown;\n operation: 'set' | 'update' | 'delete';\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 (computed from display cache) */\n private _orderedChildren: string[] = [];\n\n /** Server cache: what the server has told us (baseline) */\n private _serverCache: unknown = null;\n\n /** Whether we've received the initial snapshot from the server */\n private _hasReceivedInitialSnapshot = false;\n\n /** Pending write operations that haven't been ACKed yet (for local-first) */\n private _pendingWriteData: PendingWriteEntry[] = [];\n\n /** Cached display value (server + pending writes merged) - invalidated when either changes */\n private _displayCacheValid = false;\n private _displayCache: unknown = null;\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 // Invalidate display cache and re-sort\n this._displayCacheValid = false;\n this.recomputeOrderedChildren();\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 (Dual Cache: Server + Pending Writes)\n // ============================================\n\n /**\n * Set the server cache (what the server told us).\n * This is the baseline before pending writes are applied.\n * Deep clones the value to ensure each View has its own copy.\n */\n setServerCache(value: unknown): void {\n // Deep clone to ensure this View has its own copy (important for multi-view)\n this._serverCache = this.deepClone(value);\n this._hasReceivedInitialSnapshot = true;\n this.invalidateDisplayCache();\n }\n\n /**\n * Get the server cache (baseline without pending writes).\n */\n getServerCache(): unknown {\n return this._serverCache;\n }\n\n /**\n * Get the display cache (server + pending writes merged).\n * This is what should be shown to the user.\n */\n getDisplayCache(): unknown {\n if (!this._displayCacheValid) {\n this._displayCache = this.computeMergedCache();\n this._displayCacheValid = true;\n }\n return this._displayCache;\n }\n\n /**\n * Alias for getDisplayCache() - this is what callbacks receive.\n */\n getCache(): unknown {\n return this.getDisplayCache();\n }\n\n /**\n * Invalidate the display cache (call when serverCache or pendingWrites change).\n */\n private invalidateDisplayCache(): void {\n this._displayCacheValid = false;\n this.recomputeOrderedChildren();\n }\n\n /**\n * Compute the merged cache (serverCache + pendingWrites), with query constraints applied.\n */\n private computeMergedCache(): unknown {\n let result: unknown;\n\n if (this._pendingWriteData.length === 0) {\n result = this._serverCache;\n } else {\n // Deep clone server cache to avoid mutations\n result = this.deepClone(this._serverCache);\n\n // Apply each pending write in order\n for (const write of this._pendingWriteData) {\n result = this.applyWrite(result, write);\n }\n }\n\n // Apply query constraints (range filtering and limits) to the merged result\n return this.applyQueryConstraints(result);\n }\n\n /**\n * Apply query constraints (range filtering and limits) to data.\n * This ensures the displayCache only contains data matching the query.\n */\n private applyQueryConstraints(data: unknown): unknown {\n // No constraints to apply if no query params or data isn't an object\n if (!this._queryParams) {\n return data;\n }\n\n if (data === null || typeof data !== 'object' || Array.isArray(data)) {\n return data;\n }\n\n const obj = data as Record<string, unknown>;\n\n // Check if we have any filtering/limiting to do\n const hasRangeFilter =\n this._queryParams.startAt !== undefined ||\n this._queryParams.startAfter !== undefined ||\n this._queryParams.endAt !== undefined ||\n this._queryParams.endBefore !== undefined ||\n this._queryParams.equalTo !== undefined;\n const hasLimit =\n this._queryParams.limitToFirst !== undefined ||\n this._queryParams.limitToLast !== undefined;\n\n if (!hasRangeFilter && !hasLimit) {\n return data;\n }\n\n // Get sorted entries\n let entries = createSortEntries(obj, this._queryParams);\n\n // Apply range filters (startAt, endAt, startAfter, endBefore, equalTo)\n if (hasRangeFilter) {\n entries = this.filterByRange(entries);\n }\n\n // Apply limits (limitToFirst, limitToLast)\n if (hasLimit) {\n entries = this.applyLimits(entries);\n }\n\n // Convert back to object\n if (entries.length === 0) {\n return null;\n }\n\n const result: Record<string, unknown> = {};\n for (const entry of entries) {\n result[entry.key] = entry.value;\n }\n\n return result;\n }\n\n /**\n * Filter entries by range constraints (startAt, endAt, startAfter, endBefore, equalTo).\n */\n private filterByRange(entries: SortEntry[]): SortEntry[] {\n if (!this._queryParams) return entries;\n\n const {\n orderBy,\n startAt, startAtKey,\n startAfter, startAfterKey,\n endAt, endAtKey,\n endBefore, endBeforeKey,\n equalTo, equalToKey\n } = this._queryParams;\n\n // For orderByKey, the startAt/endAt values are compared against the key directly\n const isOrderByKey = orderBy === 'key';\n\n return entries.filter(entry => {\n // For orderByKey, compare against key; otherwise compare against sortValue\n const compareValue = isOrderByKey ? entry.key : entry.sortValue;\n const compareFn = isOrderByKey\n ? (a: unknown, b: unknown) => compareKeys(a as string, b as string)\n : compareValues;\n\n // equalTo filter - most restrictive, check first\n if (equalTo !== undefined) {\n const cmp = compareFn(compareValue, equalTo);\n if (cmp !== 0) return false;\n if (equalToKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, equalToKey);\n if (keyCmp !== 0) return false;\n }\n return true;\n }\n\n // startAt filter (inclusive)\n if (startAt !== undefined) {\n const cmp = compareFn(compareValue, startAt);\n if (cmp < 0) return false;\n if (cmp === 0 && startAtKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, startAtKey);\n if (keyCmp < 0) return false;\n }\n }\n\n // startAfter filter (exclusive)\n if (startAfter !== undefined) {\n const cmp = compareFn(compareValue, startAfter);\n if (cmp < 0) return false;\n if (cmp === 0) {\n if (startAfterKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, startAfterKey);\n if (keyCmp <= 0) return false;\n } else {\n // startAfter without key means exclude all with same value\n return false;\n }\n }\n }\n\n // endAt filter (inclusive)\n if (endAt !== undefined) {\n const cmp = compareFn(compareValue, endAt);\n if (cmp > 0) return false;\n if (cmp === 0 && endAtKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, endAtKey);\n if (keyCmp > 0) return false;\n }\n }\n\n // endBefore filter (exclusive)\n if (endBefore !== undefined) {\n const cmp = compareFn(compareValue, endBefore);\n if (cmp > 0) return false;\n if (cmp === 0) {\n if (endBeforeKey !== undefined && !isOrderByKey) {\n const keyCmp = compareKeys(entry.key, endBeforeKey);\n if (keyCmp >= 0) return false;\n } else {\n // endBefore without key means exclude all with same value\n return false;\n }\n }\n }\n\n return true;\n });\n }\n\n /**\n * Apply limit constraints (limitToFirst, limitToLast) to entries.\n * Entries are already sorted, so we just slice.\n */\n private applyLimits(entries: SortEntry[]): SortEntry[] {\n if (!this._queryParams) return entries;\n\n const { limitToFirst, limitToLast } = this._queryParams;\n\n if (limitToFirst !== undefined) {\n return entries.slice(0, limitToFirst);\n }\n\n if (limitToLast !== undefined) {\n return entries.slice(-limitToLast);\n }\n\n return entries;\n }\n\n /**\n * Apply a single write operation to a value.\n */\n private applyWrite(base: unknown, write: PendingWriteEntry): unknown {\n const { relativePath, value, operation } = write;\n\n if (operation === 'delete') {\n if (relativePath === '/') {\n return null;\n }\n return this.deleteAtPath(base, relativePath);\n }\n\n if (operation === 'set') {\n if (relativePath === '/') {\n return value;\n }\n return this.setAtPath(base, relativePath, value);\n }\n\n if (operation === 'update') {\n // Expand path-like keys (e.g., 'child/increment' -> { child: { increment: ... } })\n const expanded = expandPathKeys(value as Record<string, unknown>);\n\n if (relativePath === '/') {\n // Merge at root using deep merge (handles nested structures correctly)\n if (base === null || base === undefined || typeof base !== 'object') {\n base = {};\n }\n return deepMerge(base as Record<string, unknown>, expanded);\n }\n // Get current value at path, merge, set back\n const current = getValueAtPath(base, relativePath);\n let merged: unknown;\n if (current && typeof current === 'object' && !Array.isArray(current)) {\n merged = deepMerge(current as Record<string, unknown>, expanded);\n } else {\n merged = expanded;\n }\n return this.setAtPath(base, relativePath, merged);\n }\n\n return base;\n }\n\n /**\n * Set a value at a path in an object, creating intermediate objects as needed.\n */\n private setAtPath(obj: unknown, path: string, value: unknown): unknown {\n if (path === '/') return value;\n\n const segments = path.split('/').filter(s => s.length > 0);\n if (segments.length === 0) return value;\n\n // Ensure we have an object to work with\n const result = (obj === null || obj === undefined || typeof obj !== 'object')\n ? {}\n : { ...(obj as object) };\n\n let current: Record<string, unknown> = result as Record<string, unknown>;\n for (let i = 0; i < segments.length - 1; i++) {\n const key = segments[i];\n if (current[key] === null || current[key] === undefined || typeof current[key] !== 'object') {\n current[key] = {};\n } else {\n current[key] = { ...(current[key] as object) };\n }\n current = current[key] as Record<string, unknown>;\n }\n\n const lastKey = segments[segments.length - 1];\n current[lastKey] = value;\n\n return result;\n }\n\n /**\n * Delete a value at a path in an object.\n */\n private deleteAtPath(obj: unknown, path: string): unknown {\n if (path === '/' || obj === null || obj === undefined || typeof obj !== 'object') {\n return null;\n }\n\n const segments = path.split('/').filter(s => s.length > 0);\n if (segments.length === 0) return null;\n\n const result = { ...(obj as object) } as Record<string, unknown>;\n\n if (segments.length === 1) {\n delete result[segments[0]];\n return Object.keys(result).length > 0 ? result : null;\n }\n\n // Navigate to parent and delete\n let current: Record<string, unknown> = result;\n for (let i = 0; i < segments.length - 1; i++) {\n const key = segments[i];\n if (current[key] === null || current[key] === undefined || typeof current[key] !== 'object') {\n return result; // Path doesn't exist\n }\n current[key] = { ...(current[key] as object) };\n current = current[key] as Record<string, unknown>;\n }\n\n delete current[segments[segments.length - 1]];\n return result;\n }\n\n /**\n * Deep clone a value.\n */\n private deepClone(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map(v => this.deepClone(v));\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = this.deepClone(v);\n }\n return result;\n }\n\n /**\n * Recompute ordered children from display cache.\n */\n private recomputeOrderedChildren(): void {\n const displayCache = this.getDisplayCache();\n if (displayCache && typeof displayCache === 'object' && !Array.isArray(displayCache)) {\n this._orderedChildren = getSortedKeys(displayCache as Record<string, unknown>, this.queryParams);\n } else {\n this._orderedChildren = [];\n }\n }\n\n /**\n * Get a value from the display 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 const displayCache = this.getDisplayCache();\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 (displayCache !== undefined && displayCache !== null) {\n return { value: displayCache, found: true };\n }\n // Even if cache is null/undefined, if we have received initial snapshot, we \"found\" null\n if (this._hasReceivedInitialSnapshot || this._pendingWriteData.length > 0) {\n // Convert undefined to null (null = no data at path)\n return { value: displayCache ?? null, 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._hasReceivedInitialSnapshot || this._pendingWriteData.length > 0) {\n const extractedValue = getValueAtPath(displayCache, relativePath);\n // Convert undefined to null (null = no data at path)\n return { value: extractedValue ?? null, found: true };\n }\n }\n\n return { value: undefined, found: false };\n }\n\n /**\n * Update a child value in the SERVER cache.\n * relativePath should be relative to this View's path.\n * Called when server events (put/patch) arrive.\n */\n updateServerCacheChild(relativePath: string, value: unknown): void {\n if (relativePath === '/') {\n // Full replacement\n this.setServerCache(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 // Ensure serverCache is an object\n if (this._serverCache === null || this._serverCache === undefined || typeof this._serverCache !== 'object') {\n this._serverCache = {};\n }\n\n const cache = this._serverCache as Record<string, unknown>;\n\n if (segments.length === 1) {\n // Direct child update\n if (value === null) {\n delete cache[segments[0]];\n } else {\n cache[segments[0]] = value;\n }\n } else {\n // Nested update\n if (value === null) {\n setValueAtPath(cache, relativePath, undefined);\n } else {\n const childKey = segments[0];\n if (!(childKey in cache)) {\n cache[childKey] = {};\n }\n setValueAtPath(cache, relativePath, value);\n }\n }\n\n this.invalidateDisplayCache();\n }\n\n /**\n * Remove a child from the SERVER cache.\n */\n removeServerCacheChild(relativePath: string): void {\n this.updateServerCacheChild(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 * Check if this View loads all data (no limits, no range filters).\n * A View that loads all data can serve as a \"complete\" cache for child paths.\n */\n loadsAllData(): boolean {\n if (!this.queryParams) return true; // No query params = loads all data\n\n const hasLimit = !!(this.queryParams.limitToFirst || this.queryParams.limitToLast);\n const hasRangeFilter =\n this.queryParams.startAt !== undefined ||\n this.queryParams.endAt !== undefined ||\n this.queryParams.equalTo !== undefined ||\n this.queryParams.startAfter !== undefined ||\n this.queryParams.endBefore !== undefined;\n\n return !hasLimit && !hasRangeFilter;\n }\n\n // ============================================\n // Pending Writes (for local-first)\n // ============================================\n\n /**\n * Get current pending write IDs (to include in pw field).\n */\n getPendingWriteIds(): string[] {\n // Filter out volatile writes (empty requestId)\n return this._pendingWriteData\n .filter(w => w.requestId !== '')\n .map(w => w.requestId);\n }\n\n /**\n * Add a pending write with its data.\n * This is used for local-first writes - the write is applied to displayCache immediately.\n */\n addPendingWriteData(\n requestId: string,\n relativePath: string,\n value: unknown,\n operation: 'set' | 'update' | 'delete'\n ): void {\n this._pendingWriteData.push({ requestId, relativePath, value, operation });\n this.invalidateDisplayCache();\n }\n\n /**\n * Remove a pending write by request ID (on ack or nack).\n * Returns true if the write was found and removed.\n */\n removePendingWrite(requestId: string): boolean {\n const idx = this._pendingWriteData.findIndex(w => w.requestId === requestId);\n if (idx === -1) return false;\n this._pendingWriteData.splice(idx, 1);\n this.invalidateDisplayCache();\n return true;\n }\n\n /**\n * Clear all pending writes.\n */\n clearPendingWrites(): void {\n if (this._pendingWriteData.length > 0) {\n this._pendingWriteData = [];\n this.invalidateDisplayCache();\n }\n }\n\n /**\n * Check if this View has pending writes.\n */\n hasPendingWrites(): boolean {\n // Only count non-volatile writes (those with a requestId)\n return this._pendingWriteData.some(w => w.requestId !== '');\n }\n\n /**\n * Get the pending write data (for debugging/testing).\n */\n getPendingWriteData(): PendingWriteEntry[] {\n return [...this._pendingWriteData];\n }\n\n /**\n * Check if a specific write is pending.\n */\n isWritePending(requestId: string): boolean {\n return this._pendingWriteData.some(w => w.requestId === requestId);\n }\n\n // ============================================\n // Cleanup\n // ============================================\n\n /**\n * Clear the server cache but preserve callbacks and pending writes.\n * Used during reconnection.\n */\n clearServerCache(): void {\n this._serverCache = null;\n this._hasReceivedInitialSnapshot = false;\n this.invalidateDisplayCache();\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._serverCache = null;\n this._orderedChildren = [];\n this._hasReceivedInitialSnapshot = false;\n this._pendingWriteData = [];\n this._displayCacheValid = false;\n this._displayCache = null;\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 { getValueAtPath, joinPath, normalizePath } from '../utils/path';\nimport { getSortValue } from '../utils/sort';\nimport { View, SnapshotCallback } from './View';\n\nexport { SnapshotCallback } from './View';\n\nexport class SubscriptionManager {\n // viewKey (path:queryIdentifier) -> View\n // Each unique path+query combination gets its own View\n private views = new Map<string, View>();\n\n // path -> Set of queryIdentifiers for that path\n // Used to find all Views at a path when events arrive\n private pathToQueryIds = new Map<string, Set<string>>();\n\n // Tag counter for generating unique tags per non-default query\n // Tags are client-local and used to route server events to correct View\n private nextTag = 1;\n\n // tag -> viewKey mapping for routing tagged events\n private tagToViewKey = new Map<number, string>();\n\n // viewKey -> tag mapping for unsubscribe\n private viewKeyToTag = new Map<string, number>();\n\n // Callback to send subscribe message to server\n private sendSubscribe: ((path: string, queryParams?: QueryParams, tag?: number) => Promise<void>) | null = null;\n\n // Callback to send unsubscribe message to server (includes queryParams and tag for server to identify subscription)\n private sendUnsubscribe: ((path: string, queryParams?: QueryParams, tag?: number) => 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, orderedKeys?: string[]) => DataSnapshot)\n | null = null;\n\n /**\n * Initialize the manager with server communication callbacks.\n */\n initialize(options: {\n sendSubscribe: (path: string, queryParams?: QueryParams, tag?: number) => Promise<void>;\n sendUnsubscribe: (path: string, queryParams?: QueryParams, tag?: number) => Promise<void>;\n createSnapshot: (path: string, value: unknown, volatile: boolean, serverTimestamp?: number, orderedKeys?: string[]) => DataSnapshot;\n }): void {\n this.sendSubscribe = options.sendSubscribe;\n this.sendUnsubscribe = options.sendUnsubscribe;\n this.createSnapshot = options.createSnapshot;\n }\n\n /**\n * Create a view key from path and query identifier.\n */\n private makeViewKey(path: string, queryIdentifier: string): string {\n return `${path}:${queryIdentifier}`;\n }\n\n /**\n * Get all Views at a given path (across all query identifiers).\n */\n private getViewsAtPath(path: string): View[] {\n const queryIds = this.pathToQueryIds.get(path);\n if (!queryIds || queryIds.size === 0) {\n return [];\n }\n\n const views: View[] = [];\n for (const queryId of queryIds) {\n const viewKey = this.makeViewKey(path, queryId);\n const view = this.views.get(viewKey);\n if (view) {\n views.push(view);\n }\n }\n return views;\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, queryIdentifier?: string): () => void {\n const normalizedPath = path;\n const queryId = queryIdentifier ?? 'default';\n const viewKey = this.makeViewKey(normalizedPath, queryId);\n const isNonDefaultQuery = queryId !== 'default';\n\n // Get or create View for this path+query combination\n let view = this.views.get(viewKey);\n const isNewView = !view;\n let queryParamsChanged = false;\n let tag: number | undefined;\n\n if (!view) {\n view = new View(normalizedPath, queryParams);\n this.views.set(viewKey, view);\n\n // Track this query ID for the path\n if (!this.pathToQueryIds.has(normalizedPath)) {\n this.pathToQueryIds.set(normalizedPath, new Set());\n }\n this.pathToQueryIds.get(normalizedPath)!.add(queryId);\n\n // Generate tag for non-default queries\n if (isNonDefaultQuery) {\n tag = this.nextTag++;\n this.tagToViewKey.set(tag, viewKey);\n this.viewKeyToTag.set(viewKey, tag);\n }\n } else {\n // Check if queryParams changed - update View to match latest subscription\n // (This should rarely happen since same queryId means same query)\n queryParamsChanged = view.updateQueryParams(queryParams);\n // Get existing tag for this view\n tag = this.viewKeyToTag.get(viewKey);\n }\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, queryId);\n };\n\n // If this is a new view or queryParams changed, send subscribe to server\n // (We no longer send event types - server sends all data changes, client filters locally)\n // BUT only if there's no ancestor with a complete view (loadsAllData) that covers this path\n if (isNewView || queryParamsChanged) {\n // Check if an ancestor has a complete view that can provide data for this path\n // If so, we don't need our own server subscription\n const hasAncestorComplete = this.hasAncestorCompleteView(normalizedPath);\n\n if (!hasAncestorComplete) {\n // Fire and forget - we don't wait for the server\n // Include tag for non-default queries\n this.sendSubscribe?.(normalizedPath, view.queryParams ?? undefined, tag).catch((err) => {\n console.error('Failed to subscribe:', err);\n });\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 // Pass orderedChildren so forEach() iterates in correct order\n const snapshot = this.createSnapshot?.(view.path, cache, false, undefined, view.orderedChildren);\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 unsubscribeCallback(path: string, eventType: EventType, callback: SnapshotCallback, queryIdentifier?: string): void {\n const queryId = queryIdentifier ?? 'default';\n const viewKey = this.makeViewKey(path, queryId);\n const view = this.views.get(viewKey);\n if (!view) return;\n\n // Capture queryParams and tag before removing the view\n const queryParams = view.queryParams ?? undefined;\n const tag = this.viewKeyToTag.get(viewKey);\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(viewKey);\n\n // Remove from pathToQueryIds\n const queryIds = this.pathToQueryIds.get(path);\n if (queryIds) {\n queryIds.delete(queryId);\n if (queryIds.size === 0) {\n this.pathToQueryIds.delete(path);\n }\n }\n\n // Clean up tag mappings\n if (tag !== undefined) {\n this.tagToViewKey.delete(tag);\n this.viewKeyToTag.delete(viewKey);\n }\n\n // Send unsubscribe for this specific View (with its queryParams and tag)\n this.sendUnsubscribe?.(path, queryParams, tag).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 * This affects ALL Views at the path (across all query identifiers).\n */\n unsubscribeEventType(path: string, eventType: EventType): void {\n const normalizedPath = path;\n const queryIds = this.pathToQueryIds.get(normalizedPath);\n if (!queryIds) return;\n\n // Iterate over a copy since we may delete during iteration\n for (const queryId of Array.from(queryIds)) {\n const viewKey = this.makeViewKey(normalizedPath, queryId);\n const view = this.views.get(viewKey);\n if (!view) continue;\n\n // Capture queryParams before potentially removing the view\n const queryParams = view.queryParams ?? undefined;\n\n view.removeAllCallbacks(eventType);\n\n if (!view.hasCallbacks()) {\n // View is now empty - remove it and send unsubscribe\n this.views.delete(viewKey);\n queryIds.delete(queryId);\n\n this.sendUnsubscribe?.(normalizedPath, queryParams).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n }\n // If view still has callbacks, keep the subscription - server sends all data changes\n }\n\n // Clean up pathToQueryIds if empty\n if (queryIds.size === 0) {\n this.pathToQueryIds.delete(normalizedPath);\n }\n }\n\n /**\n * Remove ALL subscriptions at a path.\n * This affects ALL Views at the path (across all query identifiers).\n */\n unsubscribeAll(path: string): void {\n const normalizedPath = path;\n const queryIds = this.pathToQueryIds.get(normalizedPath);\n if (!queryIds || queryIds.size === 0) return;\n\n // Remove all Views at this path and send unsubscribe for each\n for (const queryId of queryIds) {\n const viewKey = this.makeViewKey(normalizedPath, queryId);\n const view = this.views.get(viewKey);\n if (view) {\n // Capture queryParams before clearing\n const queryParams = view.queryParams ?? undefined;\n view.clear();\n this.views.delete(viewKey);\n\n // Send unsubscribe for this specific View\n this.sendUnsubscribe?.(normalizedPath, queryParams).catch((err) => {\n console.error('Failed to unsubscribe:', err);\n });\n }\n }\n this.pathToQueryIds.delete(normalizedPath);\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 * Volatile events use `x: true` flag on patch events.\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 * Get View by tag. Returns undefined if tag not found.\n */\n private getViewByTag(tag: number): View | undefined {\n const viewKey = this.tagToViewKey.get(tag);\n if (!viewKey) return undefined;\n return this.views.get(viewKey);\n }\n\n /**\n * Handle a 'put' event - single path change.\n * Routes to specific View by tag if present, otherwise to default View at path.\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 const tag = message.tag;\n\n // Skip if no value to apply\n if (value === undefined) return;\n\n // Route by tag if present\n if (tag !== undefined) {\n const view = this.getViewByTag(tag);\n if (view) {\n this.applyServerUpdateToView(view, [{ relativePath, value }], isVolatile, serverTimestamp);\n }\n return;\n }\n\n // No tag - route to default View at this path\n const defaultViewKey = this.makeViewKey(subscriptionPath, 'default');\n const view = this.views.get(defaultViewKey);\n if (view) {\n this.applyServerUpdateToView(view, [{ relativePath, value }], isVolatile, serverTimestamp);\n }\n }\n\n /**\n * Handle a 'patch' event - multi-path change.\n * Routes to specific View by tag if present, otherwise to default View at path.\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 const tag = message.tag;\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 // Route by tag if present\n if (tag !== undefined) {\n const view = this.getViewByTag(tag);\n if (view) {\n this.applyServerUpdateToView(view, updates, isVolatile, serverTimestamp);\n }\n return;\n }\n\n // No tag - route to default View at this path\n const defaultViewKey = this.makeViewKey(subscriptionPath, 'default');\n const view = this.views.get(defaultViewKey);\n if (view) {\n this.applyServerUpdateToView(view, updates, isVolatile, serverTimestamp);\n }\n }\n\n /**\n * Detect and fire child_moved events for children that changed position OR sort value.\n *\n * child_moved fires for ANY priority/sort value change, regardless of whether\n * the position actually changes.\n *\n * IMPORTANT: child_moved should only fire for children whose VALUE or PRIORITY changed.\n * Children that are merely \"displaced\" by another child moving should NOT fire child_moved.\n *\n * @param affectedChildren - Only check these children for moves. If not provided,\n * checks all children (for full snapshots where we compare values).\n * @param previousDisplayCache - Previous display cache for comparing sort values\n * @param currentDisplayCache - Current display cache for comparing sort values\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 affectedChildren?: Set<string>,\n previousDisplayCache?: Record<string, unknown> | null,\n currentDisplayCache?: Record<string, unknown> | null\n ): void {\n if (childMovedSubs.length === 0) return;\n\n // Determine which children to check for moves\n // For delta updates, only check children that were actually modified\n // For full snapshots, affectedChildren will contain children with changed values\n const childrenToCheck = affectedChildren ?? new Set(currentOrder);\n\n const queryParams = view.queryParams;\n\n // For each affected child that exists in both old and new, check if position or sort value changed\n for (const key of childrenToCheck) {\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 if (oldPos === undefined || newPos === undefined) {\n continue;\n }\n\n // Check if the relative order changed\n const oldPrevKey = oldPos > 0 ? previousOrder[oldPos - 1] : null;\n const newPrevKey = newPos > 0 ? currentOrder[newPos - 1] : null;\n\n let positionChanged = oldPrevKey !== newPrevKey;\n\n // Also check if sort value changed\n // Case 2003: fire child_moved for ANY priority change (when ordering by priority)\n // For orderByChild, only fire if position actually changed\n let sortValueChanged = false;\n let isPriorityOrdering = false;\n if (previousDisplayCache && currentDisplayCache) {\n const prevValue = previousDisplayCache[key];\n const currValue = currentDisplayCache[key];\n const prevSortValue = getSortValue(prevValue, queryParams);\n const currSortValue = getSortValue(currValue, queryParams);\n // Compare sort values - use JSON.stringify for deep comparison\n sortValueChanged = JSON.stringify(prevSortValue) !== JSON.stringify(currSortValue);\n\n // Check if we're ordering by priority (default when no orderBy, or explicit orderByPriority)\n // Note: When queryParams exists but orderBy is undefined, we default to priority ordering\n isPriorityOrdering = !queryParams?.orderBy || queryParams.orderBy === 'priority';\n }\n\n // Behavior:\n // - For priority ordering: child_moved fires when priority changes\n // - For orderByChild/orderByValue/orderByKey: child_moved only fires if position changes\n // - child_moved does NOT fire for children merely \"displaced\" by other children moving\n //\n // For delta updates (affectedChildren provided): the affected child's value changed\n // For full snapshots (affectedChildren undefined): only fire if this child's sort value changed\n let shouldFire: boolean;\n if (affectedChildren) {\n // Delta update - this child was modified\n // Fire if position changed, or if priority changed (for priority ordering)\n shouldFire = positionChanged || (isPriorityOrdering && sortValueChanged);\n } else {\n // Full snapshot - only fire if this child's own sort value changed\n // (and only for priority ordering, per Case 2003)\n shouldFire = isPriorityOrdering && sortValueChanged;\n }\n\n if (shouldFire) {\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 previousValue?: unknown\n ): void {\n if (subs.length === 0) return;\n\n const childPath = joinPath(view.path, childKey);\n // Pass the previous value for removed children, not null\n const snapshot = this.createSnapshot?.(childPath, previousValue ?? 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 * Handle subscription revocation due to auth change.\n * The server has already removed the subscription, so we just clean up locally.\n * This is different from unsubscribeAll which sends an unsubscribe message.\n */\n handleSubscriptionRevoked(path: string): void {\n const normalizedPath = path;\n const queryIds = this.pathToQueryIds.get(normalizedPath);\n if (!queryIds || queryIds.size === 0) return;\n\n // Remove all Views at this path locally (don't send unsubscribe, server already revoked)\n for (const queryId of queryIds) {\n const viewKey = this.makeViewKey(normalizedPath, queryId);\n const view = this.views.get(viewKey);\n if (view) {\n // Clean up tag mappings\n const tag = this.viewKeyToTag.get(viewKey);\n if (tag !== undefined) {\n this.tagToViewKey.delete(tag);\n this.viewKeyToTag.delete(viewKey);\n }\n view.clear();\n this.views.delete(viewKey);\n }\n }\n this.pathToQueryIds.delete(normalizedPath);\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 this.pathToQueryIds.clear();\n this.tagToViewKey.clear();\n this.viewKeyToTag.clear();\n this.nextTag = 1;\n }\n\n /**\n * Check if there are any subscriptions at a path (across all query identifiers).\n */\n hasSubscriptions(path: string): boolean {\n const queryIds = this.pathToQueryIds.get(path);\n return queryIds !== undefined && queryIds.size > 0;\n }\n\n /**\n * Check if a path is \"covered\" by an active subscription that has received data.\n *\n * A path is covered if:\n * - There's an active subscription at that exact path that has data, OR\n * - There's an active subscription at an ancestor path that has data\n *\n * Note: Any subscription type (value, child_added, child_moved, etc.) receives\n * the initial snapshot from the server and thus has cached data.\n */\n isPathCovered(path: string): boolean {\n const normalized = normalizePath(path);\n\n // Check exact match\n if (this.hasActiveSubscriptionWithData(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.hasActiveSubscriptionWithData(ancestorPath)) {\n return true;\n }\n }\n\n // Check root\n if (normalized !== '/' && this.hasActiveSubscriptionWithData('/')) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Check if there's an active subscription at a path that has data.\n * A View has data if it has received the initial snapshot OR has pending writes.\n * Any subscription type (value, child_added, child_moved, etc.) counts.\n */\n private hasActiveSubscriptionWithData(path: string): boolean {\n const views = this.getViewsAtPath(path);\n // A View has data if it received initial snapshot OR has pending writes (optimistic)\n return views.some(view => view.hasCallbacks() && (view.hasReceivedInitialSnapshot || view.hasPendingWrites()));\n }\n\n /**\n * Check if any ancestor path has a \"complete\" View (one that loadsAllData).\n * A complete View has no limits and no range filters, so it contains all data\n * for its subtree. Child subscriptions can use the ancestor's data instead\n * of creating their own server subscription.\n */\n hasAncestorCompleteView(path: string): boolean {\n const normalized = normalizePath(path);\n const segments = normalized.split('/').filter((s) => s.length > 0);\n\n // Walk up the path tree checking ancestors\n for (let i = segments.length - 1; i >= 0; i--) {\n const ancestorPath = i === 0 ? '/' : '/' + segments.slice(0, i).join('/');\n const views = this.getViewsAtPath(ancestorPath);\n for (const view of views) {\n if (view.hasCallbacks() && view.loadsAllData() && view.hasReceivedInitialSnapshot) {\n return true;\n }\n }\n }\n\n // Check root separately if not already checked\n if (normalized !== '/') {\n const rootViews = this.getViewsAtPath('/');\n for (const view of rootViews) {\n if (view.hasCallbacks() && view.loadsAllData() && view.hasReceivedInitialSnapshot) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Get a cached value if the path is covered by an active subscription.\n * Returns the value from the first View that has it (typically the default/unfiltered one).\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 - try all Views at this path\n const exactViews = this.getViewsAtPath(normalized);\n for (const view of exactViews) {\n const result = view.getCacheValue(normalized);\n if (result.found) {\n return result;\n }\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 ancestorViews = this.getViewsAtPath(ancestorPath);\n for (const view of ancestorViews) {\n // Any subscription with data (initial snapshot or pending writes) can provide cached values\n if (view.hasCallbacks() && (view.hasReceivedInitialSnapshot || view.hasPendingWrites())) {\n const result = view.getCacheValue(normalized);\n if (result.found) {\n return result;\n }\n }\n }\n }\n\n // Check root\n if (normalized !== '/') {\n const rootViews = this.getViewsAtPath('/');\n for (const view of rootViews) {\n // Any subscription with data (initial snapshot or pending writes) can provide cached values\n if (view.hasCallbacks() && (view.hasReceivedInitialSnapshot || view.hasPendingWrites())) {\n const result = view.getCacheValue(normalized);\n if (result.found) {\n return result;\n }\n }\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.clearServerCache();\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 view of this.views.values()) {\n if (view.hasCallbacks()) {\n try {\n await this.sendSubscribe?.(view.path, view.queryParams ?? undefined);\n } catch (err) {\n console.error(`Failed to resubscribe to ${view.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 view of this.views.values()) {\n const eventTypes = view.getEventTypes();\n const queryParams = view.queryParams ?? undefined;\n result.push({ path: view.path, eventTypes, queryParams });\n }\n\n return result;\n }\n\n /**\n * Get a View by path (for testing/debugging).\n * Returns the first View at this path (default query identifier).\n */\n getView(path: string): View | undefined {\n // Try default query identifier first\n const defaultView = this.views.get(this.makeViewKey(path, 'default'));\n if (defaultView) return defaultView;\n\n // Otherwise return any View at this path\n const views = this.getViewsAtPath(path);\n return views.length > 0 ? views[0] : undefined;\n }\n\n // ============================================\n // Shared Write Application (used by server events and optimistic writes)\n // ============================================\n\n /**\n * Recursively sort object keys for consistent comparison.\n * This ensures {a:1, b:2} and {b:2, a:1} compare as equal.\n * Uses simple alphabetical sorting since we're just checking data equality,\n * not display order.\n */\n private sortKeysForComparison(value: unknown): unknown {\n if (value === null || typeof value !== 'object') {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.sortKeysForComparison(item));\n }\n\n // Create a new object with keys in sorted order\n const obj = value as Record<string, unknown>;\n const sortedKeys = Object.keys(obj).sort();\n const sorted: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n sorted[key] = this.sortKeysForComparison(obj[key]);\n }\n return sorted;\n }\n\n /**\n * Serialize cache with sorted keys for consistent comparison.\n */\n private serializeCacheForComparison(cache: unknown): string {\n const sorted = this.sortKeysForComparison(cache);\n return JSON.stringify(sorted);\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 applyServerUpdateToView(\n view: View,\n updates: Array<{ relativePath: string; value: unknown }>,\n isVolatile: boolean,\n serverTimestamp?: number\n ): void {\n // Capture previous display state (for change detection)\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n const isFirstSnapshot = !view.hasReceivedInitialSnapshot && !view.hasPendingWrites();\n\n // For non-volatile writes, capture previous display cache state for deduplication.\n // Volatile writes skip deduplication entirely because:\n // 1. Server does NOT echo our volatile writes back (fire-and-forget)\n // 2. Incoming volatile events are from OTHER clients and should always fire\n // 3. Skipping serialization reduces overhead on the hot path (30+ Hz updates)\n let previousCacheJson: string | null = null;\n let previousDisplayCache: Record<string, unknown> | null = null;\n if (!isVolatile) {\n const cache = view.getDisplayCache();\n previousDisplayCache = (cache && typeof cache === 'object' && !Array.isArray(cache))\n ? cache as Record<string, unknown>\n : null;\n previousCacheJson = this.serializeCacheForComparison(cache);\n }\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 to SERVER cache (display cache is computed automatically)\n for (const { relativePath, value } of updates) {\n if (relativePath === '/') {\n // Full snapshot replacement\n view.setServerCache(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 to server cache\n if (value === null) {\n view.removeServerCacheChild(relativePath);\n } else {\n view.updateServerCacheChild(relativePath, value);\n }\n }\n }\n\n // Capture current state (from display cache, which includes pending writes)\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n\n // Deduplication check for non-volatile writes (skip for volatile and first snapshot)\n // This prevents duplicate events when server confirms our optimistic write.\n // Uses sorted key comparison to handle server returning keys in different order.\n if (!isVolatile && !isFirstSnapshot && previousCacheJson !== null) {\n const currentCacheJson = this.serializeCacheForComparison(view.getDisplayCache());\n if (previousCacheJson === currentCacheJson) {\n return;\n }\n }\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getDisplayCache();\n // Pass orderedChildren so forEach() iterates in correct order\n const snapshot = this.createSnapshot?.(view.path, fullValue, isVolatile, serverTimestamp, view.orderedChildren);\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 const prevValue = previousDisplayCache?.[key];\n this.fireChildRemoved(view, key, childRemovedSubs, isVolatile, serverTimestamp, prevValue);\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 const prevValue = previousDisplayCache?.[childKey];\n this.fireChildRemoved(view, childKey, childRemovedSubs, isVolatile, serverTimestamp, prevValue);\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 // Get current display cache for sort value comparison\n const currentCache = view.getDisplayCache();\n const currentDisplayCache = (currentCache && typeof currentCache === 'object' && !Array.isArray(currentCache))\n ? currentCache as Record<string, unknown>\n : null;\n\n // For delta updates, only check affected children for moves\n // For full snapshots, we pass undefined which checks all children\n this.detectAndFireMoves(\n view,\n previousOrder,\n currentOrder,\n previousPositions,\n currentPositions,\n previousChildSet,\n currentChildSet,\n childMovedSubs,\n isVolatile,\n serverTimestamp,\n isFullSnapshot ? undefined : affectedChildren,\n previousDisplayCache,\n currentDisplayCache\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 view of this.views.values()) {\n const viewPath = view.path;\n // Check if viewPath is an ancestor of, equal to, or descendant of the write path\n if (normalized === viewPath) {\n // Exact match - write is directly at View's path\n views.push(view);\n } else if (normalized.startsWith(viewPath + '/')) {\n // Write is INSIDE View's path (write is a descendant)\n views.push(view);\n } else if (viewPath.startsWith(normalized + '/')) {\n // View is INSIDE write path (View is a descendant of write)\n // e.g., writing to /foo affects View at /foo/bar\n views.push(view);\n } else if (viewPath === '/') {\n // Root View is always affected\n views.push(view);\n } else if (normalized === '/') {\n // Write to root affects all Views\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 /**\n * Track a pending write by request ID.\n * Note: With the dual-cache system, this is now handled by applyOptimisticWrite\n * via addPendingWriteData. This method is kept for API compatibility but is a no-op.\n */\n trackPendingWrite(_writePath: string, _requestId: string): void {\n // No-op: tracking now happens in applyOptimisticWrite via addPendingWriteData\n }\n\n /**\n * Clear a pending write from all Views (on ack).\n * Fires callbacks only if displayCache changed (e.g., server modified the data).\n * With PUT-before-ACK ordering, this handles the case where server data differs from optimistic.\n */\n clearPendingWrite(requestId: string): void {\n for (const view of this.views.values()) {\n if (!view.isWritePending(requestId)) {\n continue;\n }\n\n // Capture previous display state\n const previousDisplayCache = view.getDisplayCache() as Record<string, unknown> | null;\n const previousCacheJson = this.serializeCacheForComparison(previousDisplayCache);\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n\n // Remove the pending write\n view.removePendingWrite(requestId);\n\n // Check if display cache changed (server data differs from optimistic)\n const currentCacheJson = this.serializeCacheForComparison(view.getDisplayCache());\n if (previousCacheJson === currentCacheJson) {\n // No change - server data matches optimistic, nothing to do\n continue;\n }\n\n // Display cache changed - fire callbacks\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getDisplayCache();\n const snapshot = this.createSnapshot?.(view.path, fullValue, false, undefined, view.orderedChildren);\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 // Note: We pass skipMoves=true because moves were already fired by the optimistic write.\n // The ack just confirms the write; if server data differs, it will come via PUT event\n // which will fire the correct move events. Firing moves here causes duplicate/wrong events\n // when ACK arrives before PUT due to network ordering.\n this.fireChildEventsForAck(view, previousOrder, previousChildSet, currentOrder, currentChildSet, previousDisplayCache);\n }\n }\n\n /**\n * Fire child events for ACK handling, skipping child_moved.\n * This is a variant of fireChildEvents that doesn't fire moves because:\n * 1. Moves were already fired optimistically\n * 2. If server modifies data, PUT event will fire correct moves\n * 3. ACK can arrive before PUT, causing incorrect intermediate state\n */\n private fireChildEventsForAck(\n view: View,\n previousOrder: string[],\n previousChildSet: Set<string>,\n currentOrder: string[],\n currentChildSet: Set<string>,\n previousDisplayCache?: Record<string, unknown> | null\n ): void {\n const childAddedSubs = view.getCallbacks('child_added');\n const childChangedSubs = view.getCallbacks('child_changed');\n const childRemovedSubs = view.getCallbacks('child_removed');\n\n // Skip if no relevant subscriptions\n if (childAddedSubs.length === 0 && childChangedSubs.length === 0 && childRemovedSubs.length === 0) {\n return;\n }\n\n const displayCache = view.getDisplayCache() as Record<string, unknown> | null;\n\n // Full comparison of children\n for (const key of currentOrder) {\n if (!previousChildSet.has(key)) {\n // New child added\n if (childAddedSubs.length > 0 && displayCache) {\n const prevKey = view.getPreviousChildKey(key);\n this.fireChildAdded(view, key, childAddedSubs, prevKey, false, undefined);\n }\n } else if (previousDisplayCache && childChangedSubs.length > 0 && displayCache) {\n // Check if value changed\n const prevValue = previousDisplayCache[key];\n const currentValue = displayCache[key];\n const prevJson = this.serializeCacheForComparison(prevValue);\n const currJson = this.serializeCacheForComparison(currentValue);\n if (prevJson !== currJson) {\n const prevKey = view.getPreviousChildKey(key);\n this.fireChildChanged(view, key, childChangedSubs, prevKey, false, undefined);\n }\n }\n }\n\n // Removed children\n for (const key of previousOrder) {\n if (!currentChildSet.has(key)) {\n if (childRemovedSubs.length > 0) {\n const prevValue = previousDisplayCache?.[key];\n this.fireChildRemoved(view, key, childRemovedSubs, false, undefined, prevValue);\n }\n }\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 /**\n * Handle a write NACK by removing the pending write from affected Views.\n * With the dual-cache system, this automatically reverts to server truth.\n * Returns the affected Views so callbacks can be fired if data changed.\n */\n handleWriteNack(requestId: string): View[] {\n const affectedViews: View[] = [];\n\n for (const view of this.views.values()) {\n if (view.isWritePending(requestId)) {\n // Capture previous display state\n const previousDisplayCache = view.getDisplayCache() as Record<string, unknown> | null;\n const previousCacheJson = this.serializeCacheForComparison(previousDisplayCache);\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n\n // Remove the pending write - displayCache automatically updates\n view.removePendingWrite(requestId);\n\n // Capture new state\n const currentCacheJson = this.serializeCacheForComparison(view.getDisplayCache());\n\n // If display cache changed, fire callbacks\n if (previousCacheJson !== currentCacheJson) {\n affectedViews.push(view);\n\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getDisplayCache();\n const snapshot = this.createSnapshot?.(view.path, fullValue, false, undefined, view.orderedChildren);\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 this.fireChildEvents(view, previousOrder, previousChildSet, currentOrder, currentChildSet, '/', false, undefined, previousDisplayCache);\n }\n }\n }\n\n return affectedViews;\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 // Calculate relative path and potentially extract value for this View\n let relativePath: string;\n let effectiveValue: unknown = value;\n\n if (normalized === view.path) {\n // Exact match - write is directly at View's path\n relativePath = '/';\n } else if (view.path === '/') {\n // View is at root, write is somewhere inside\n relativePath = normalized;\n } else if (normalized.startsWith(view.path + '/')) {\n // Write is INSIDE View's path (write is a descendant)\n relativePath = normalized.slice(view.path.length);\n } else if (view.path.startsWith(normalized + '/')) {\n // View is INSIDE write path (View is a descendant of write)\n // e.g., writing { a: 1, b: 2 } to /foo, View is at /foo/a\n // We need to extract the value at the path difference\n const pathDiff = view.path.slice(normalized.length); // e.g., '/a'\n effectiveValue = getValueAtPath(value, pathDiff);\n\n // For UPDATE operations, only affect this View if the update actually\n // contains data for this path. Otherwise skip it.\n if (operation === 'update' && effectiveValue === undefined) {\n continue;\n }\n\n relativePath = '/';\n } else {\n // This shouldn't happen given findViewsForWritePath logic\n continue;\n }\n\n // Capture previous display state for change detection\n const previousDisplayCache = view.getDisplayCache() as Record<string, unknown> | null;\n const previousOrder = view.orderedChildren;\n const previousChildSet = new Set(previousOrder);\n const previousCacheJson = this.serializeCacheForComparison(previousDisplayCache);\n\n // Add the pending write - this automatically updates the display cache\n view.addPendingWriteData(requestId, relativePath, effectiveValue, operation);\n\n // Capture new state\n const currentOrder = view.orderedChildren;\n const currentChildSet = new Set(currentOrder);\n const currentCacheJson = this.serializeCacheForComparison(view.getDisplayCache());\n\n // Skip if display cache didn't actually change\n if (previousCacheJson === currentCacheJson) {\n continue;\n }\n\n updatedViews.push(view);\n\n // Fire value callbacks\n const valueSubs = view.getCallbacks('value');\n if (valueSubs.length > 0) {\n const fullValue = view.getDisplayCache();\n const snapshot = this.createSnapshot?.(view.path, fullValue, false, undefined, view.orderedChildren);\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 this.fireChildEvents(view, previousOrder, previousChildSet, currentOrder, currentChildSet, relativePath, false, undefined, previousDisplayCache);\n }\n\n return updatedViews;\n }\n\n /**\n * Fire child_added/child_changed/child_removed/child_moved events.\n * Extracted as helper since it's used by both server updates and optimistic writes.\n */\n private fireChildEvents(\n view: View,\n previousOrder: string[],\n previousChildSet: Set<string>,\n currentOrder: string[],\n currentChildSet: Set<string>,\n affectedPath: string,\n isVolatile: boolean,\n serverTimestamp: number | undefined,\n previousDisplayCache?: Record<string, unknown> | null\n ): void {\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 const displayCache = view.getDisplayCache() as Record<string, unknown> | null;\n const affectedChildren = new Set<string>();\n\n // Extract immediate child from the affected path\n if (affectedPath !== '/') {\n const segments = affectedPath.split('/').filter(s => s.length > 0);\n if (segments.length > 0) {\n affectedChildren.add(segments[0]);\n }\n }\n\n const isFullSnapshot = affectedPath === '/';\n\n if (isFullSnapshot) {\n // Full snapshot - compare all children\n for (const key of currentOrder) {\n if (!previousChildSet.has(key)) {\n // New child\n if (childAddedSubs.length > 0 && displayCache) {\n const childValue = displayCache[key];\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n childValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childAddedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_added callback:', err);\n }\n }\n }\n }\n } else if (previousDisplayCache && childChangedSubs.length > 0 && displayCache) {\n // Child exists in both - check if value changed\n const prevValue = previousDisplayCache[key];\n const currentValue = displayCache[key];\n const prevJson = this.serializeCacheForComparison(prevValue);\n const currJson = this.serializeCacheForComparison(currentValue);\n if (prevJson !== currJson) {\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n currentValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childChangedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_changed callback:', err);\n }\n }\n }\n }\n }\n }\n\n // Removed children\n for (const key of previousOrder) {\n if (!currentChildSet.has(key)) {\n if (childRemovedSubs.length > 0) {\n // Pass previous value for removed children\n const prevValue = previousDisplayCache?.[key];\n const snapshot = this.createSnapshot?.(joinPath(view.path, key), prevValue ?? null, isVolatile, serverTimestamp);\n if (snapshot) {\n for (const entry of childRemovedSubs) {\n try {\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in child_removed callback:', err);\n }\n }\n }\n }\n }\n }\n } else {\n // Delta update - only check affected children\n // Events fire in this order: child_removed (evictions), child_added, child_removed (direct), child_changed\n\n // Step 1: Fire child_removed for children pushed out by limit constraints FIRST\n // These are children that were in previousOrder but aren't in currentChildSet\n // even though the affected path wasn't directly about them\n if (childRemovedSubs.length > 0) {\n for (const key of previousOrder) {\n // Skip if in affectedChildren (will be handled below)\n if (affectedChildren.has(key)) continue;\n // Skip if still present in current display\n if (currentChildSet.has(key)) continue;\n // This child was pushed out by a limit constraint - fire child_removed\n const prevValue = previousDisplayCache ? previousDisplayCache[key] : null;\n const snapshot = this.createSnapshot?.(joinPath(view.path, key), prevValue, isVolatile, serverTimestamp);\n if (snapshot) {\n for (const entry of childRemovedSubs) {\n try {\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in child_removed callback:', err);\n }\n }\n }\n }\n }\n\n // Step 2: Process affected children (added, removed, changed)\n for (const key of affectedChildren) {\n const wasPresent = previousChildSet.has(key);\n const isPresent = currentChildSet.has(key);\n\n if (!wasPresent && isPresent) {\n // child_added\n if (childAddedSubs.length > 0 && displayCache) {\n const childValue = displayCache[key];\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n childValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childAddedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_added callback:', err);\n }\n }\n }\n }\n } else if (wasPresent && !isPresent) {\n // child_removed (direct removal, not eviction)\n if (childRemovedSubs.length > 0) {\n // Pass previous value for removed children\n const prevValue = previousDisplayCache ? previousDisplayCache[key] : null;\n const snapshot = this.createSnapshot?.(joinPath(view.path, key), prevValue, isVolatile, serverTimestamp);\n if (snapshot) {\n for (const entry of childRemovedSubs) {\n try {\n entry.callback(snapshot, undefined);\n } catch (err) {\n console.error('Error in child_removed callback:', err);\n }\n }\n }\n }\n } else if (wasPresent && isPresent) {\n // child_changed\n if (childChangedSubs.length > 0 && displayCache) {\n const childValue = displayCache[key];\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n childValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childChangedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_changed callback:', err);\n }\n }\n }\n }\n }\n }\n\n // Step 3: Fire child_added for children that slide INTO view due to limit constraints\n // (window backfill). These are children in currentOrder but not in previousChildSet\n // and not in affectedChildren (already handled above).\n if (childAddedSubs.length > 0 && displayCache) {\n for (const key of currentOrder) {\n // Skip if was already present\n if (previousChildSet.has(key)) continue;\n // Skip if in affectedChildren (already handled in Step 2)\n if (affectedChildren.has(key)) continue;\n // This child slid into view due to limit constraint - fire child_added\n const childValue = displayCache[key];\n const snapshot = this.createSnapshot?.(\n joinPath(view.path, key),\n childValue,\n isVolatile,\n serverTimestamp\n );\n if (snapshot) {\n const prevKey = view.getPreviousChildKey(key);\n for (const entry of childAddedSubs) {\n try {\n entry.callback(snapshot, prevKey);\n } catch (err) {\n console.error('Error in child_added callback:', err);\n }\n }\n }\n }\n }\n }\n\n // Check for child_moved (position or sort value changes)\n if (childMovedSubs.length > 0) {\n // Build position maps for detectAndFireMoves\n const previousPositions = new Map<string, number>();\n previousOrder.forEach((key, i) => previousPositions.set(key, i));\n const currentPositions = new Map<string, number>();\n currentOrder.forEach((key, i) => currentPositions.set(key, i));\n\n // For delta updates, only check affected children for moves\n // For full snapshots, we pass undefined which checks all children\n this.detectAndFireMoves(\n view,\n previousOrder,\n currentOrder,\n previousPositions,\n currentPositions,\n previousChildSet,\n currentChildSet,\n childMovedSubs,\n isVolatile,\n serverTimestamp,\n isFullSnapshot ? undefined : affectedChildren,\n previousDisplayCache,\n displayCache\n );\n }\n }\n}\n","/**\n * Wire protocol constants.\n * Keys are shortened for bandwidth efficiency.\n */\n\n// Client -> Server operations\nexport const Op = {\n JOIN: 'j',\n AUTH: 'au',\n UNAUTH: 'ua',\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 INVALID_QUERY: 'invalid_query',\n AUTH_REQUIRED: 'auth_required',\n} as const;\n\nexport type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n// Default Lark domain for direct connections\nexport const DEFAULT_LARK_DOMAIN = 'larkdb.net';\n\n// Default coordinator URL (for load balancing - not yet used)\nexport const DEFAULT_COORDINATOR_URL = 'https://coordinator.larkdb.net';\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 * SHA-256 hashing utilities for transaction conditions.\n *\n * Uses Web Crypto API which is available in:\n * - All modern browsers\n * - Node.js 16+ (via globalThis.crypto)\n */\n\nimport canonicalize from 'canonicalize';\n\n/**\n * Compute SHA-256 hash of a value using JCS canonicalization.\n * Returns the hash as a hex string.\n *\n * @param value - The value to hash (will be JCS-canonicalized first)\n * @returns Hex-encoded SHA-256 hash\n */\nexport async function hashValue(value: unknown): Promise<string> {\n // JCS-canonicalize the value to ensure consistent serialization\n const canonical = canonicalize(value);\n\n if (canonical === undefined) {\n // canonicalize returns undefined for undefined input\n // Treat as empty string for hashing\n return hashString('');\n }\n\n return hashString(canonical);\n}\n\n/**\n * Compute SHA-256 hash of a string.\n * Returns the hash as a hex string.\n */\nasync function hashString(str: string): Promise<string> {\n // Convert string to Uint8Array\n const encoder = new TextEncoder();\n const data = encoder.encode(str);\n\n // Use Web Crypto API (available in browsers and Node.js 16+)\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\n // Convert ArrayBuffer to hex string\n const hashArray = new Uint8Array(hashBuffer);\n const hashHex = Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n\n return hashHex;\n}\n\n/**\n * Check if a value is a primitive (not an object or array).\n * Primitives: null, undefined, boolean, number, string\n */\nexport function isPrimitive(value: unknown): boolean {\n return value === null || typeof value !== 'object';\n}\n","/**\n * Push ID generation - chronologically-sortable unique 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 unique 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 * Validation utilities for keys and values.\n *\n * Server rules:\n * - Keys: UTF-8 encoded, max 768 bytes, cannot contain . $ # [ ] /\n * or ASCII control characters (0-31, 127)\n * Note: . is allowed as first character for special keys (.priority, .value, .sv)\n * - String values: Max 10 MB, no control character restriction\n */\n\nimport { LarkError } from '../LarkError';\nimport { ErrorCode } from '../protocol/constants';\n\n/** Maximum key size in bytes */\nconst MAX_KEY_BYTES = 768;\n\n/** Maximum string value size in bytes (10 MB) */\nconst MAX_STRING_VALUE_BYTES = 10 * 1024 * 1024;\n\n/** Maximum write size in bytes for set/update operations (16 MB) */\nconst MAX_WRITE_BYTES = 16 * 1024 * 1024;\n\n/** Maximum write size in bytes for volatile operations (2 KB) */\nconst MAX_VOLATILE_WRITE_BYTES = 2 * 1024;\n\n/** Characters not allowed in keys */\nconst INVALID_KEY_CHARS = /[.#$\\[\\]\\/]/;\n\n/** ASCII control characters (0-31 and 127) */\nconst CONTROL_CHARS = /[\\x00-\\x1f\\x7f]/;\n\n/**\n * Check if a string contains ASCII control characters.\n */\nfunction hasControlChars(str: string): boolean {\n return CONTROL_CHARS.test(str);\n}\n\n/**\n * Get the byte length of a UTF-8 string.\n */\nfunction getByteLength(str: string): number {\n // TextEncoder is available in browsers and Node.js 11+\n if (typeof TextEncoder !== 'undefined') {\n return new TextEncoder().encode(str).length;\n }\n // Fallback for older environments\n return Buffer.byteLength(str, 'utf8');\n}\n\n/**\n * Validate a single key (path segment).\n *\n * @throws LarkError if key is invalid\n */\nexport function validateKey(key: string, context?: string): void {\n const prefix = context ? `${context}: ` : '';\n\n if (typeof key !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key must be a string`\n );\n }\n\n if (key.length === 0) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key cannot be empty`\n );\n }\n\n // Check for invalid characters\n if (INVALID_KEY_CHARS.test(key)) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key \"${key}\" contains invalid characters (. $ # [ ] / are not allowed)`\n );\n }\n\n // Check for control characters\n if (hasControlChars(key)) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key \"${key}\" contains control characters (ASCII 0-31, 127 are not allowed)`\n );\n }\n\n // Check byte length\n const byteLength = getByteLength(key);\n if (byteLength > MAX_KEY_BYTES) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}key \"${key.substring(0, 50)}...\" exceeds maximum size of ${MAX_KEY_BYTES} bytes (got ${byteLength})`\n );\n }\n}\n\n/**\n * Validate a path (multiple segments separated by /).\n * Each segment is validated as a key.\n *\n * @throws LarkError if any segment is invalid\n */\nexport function validatePath(path: string, context?: string): void {\n if (typeof path !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_PATH,\n `${context ? `${context}: ` : ''}path must be a string`\n );\n }\n\n // Empty path or root is valid\n if (path === '' || path === '/') {\n return;\n }\n\n // Split and validate each segment\n const segments = path.split('/').filter(s => s.length > 0);\n for (const segment of segments) {\n // Allow special keys: .priority, .value, .sv, .info (internal/system use)\n if (segment === '.priority' || segment === '.value' || segment === '.sv' || segment === '.info') {\n continue;\n }\n validateKey(segment, context);\n }\n}\n\n/**\n * Validate a value recursively.\n * - String values: max 10 MB (no control character restriction)\n * - Object keys are validated as keys\n * - .value can only coexist with .priority\n *\n * @throws LarkError if value contains invalid data\n */\nexport function validateValue(value: unknown, context?: string, path: string = ''): void {\n const prefix = context ? `${context}: ` : '';\n const location = path ? ` at \"${path}\"` : '';\n\n if (value === null || value === undefined) {\n return;\n }\n\n if (typeof value === 'string') {\n // Only check size - control characters are allowed in string values\n const byteLength = getByteLength(value);\n if (byteLength > MAX_STRING_VALUE_BYTES) {\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}string value${location} exceeds maximum size of 10 MB (got ${Math.round(byteLength / 1024 / 1024 * 100) / 100} MB)`\n );\n }\n return;\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return;\n }\n\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n validateValue(value[i], context, path ? `${path}/${i}` : String(i));\n }\n return;\n }\n\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj);\n\n // .value can only coexist with .priority\n if ('.value' in obj) {\n const otherKeys = keys.filter(k => k !== '.value' && k !== '.priority');\n if (otherKeys.length > 0) {\n const loc = path || '/';\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}data at ${loc} contains \".value\" alongside other children (${otherKeys.join(', ')}). ` +\n `\".value\" can only be used with \".priority\" for primitives with priority.`\n );\n }\n }\n\n for (const [key, childValue] of Object.entries(obj)) {\n // Allow special keys: .priority, .value, .sv (ServerValue)\n if (key !== '.priority' && key !== '.value' && key !== '.sv') {\n validateKey(key, context);\n }\n validateValue(childValue, context, path ? `${path}/${key}` : key);\n }\n return;\n }\n\n // Other types (functions, symbols, etc.) are not valid\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${prefix}invalid value type${location}: ${typeof value}`\n );\n}\n\n/**\n * Validate data for a write operation.\n * Validates both the value and any nested keys.\n */\nexport function validateWriteData(value: unknown, context?: string): void {\n validateValue(value, context);\n}\n\n/**\n * Get the JSON byte size of a value.\n * This measures the size as the server would see it (raw JSON bytes of the value).\n */\nexport function getJsonByteSize(value: unknown): number {\n const json = JSON.stringify(value);\n return getByteLength(json);\n}\n\n/**\n * Validate write size for set/update operations.\n * Server limit is 16 MB for the JSON-encoded value.\n *\n * @throws LarkError if value exceeds 16 MB\n */\nexport function validateWriteSize(value: unknown, context?: string): void {\n const byteSize = getJsonByteSize(value);\n if (byteSize > MAX_WRITE_BYTES) {\n const sizeMB = Math.round(byteSize / 1024 / 1024 * 100) / 100;\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${context ? `${context}: ` : ''}write exceeds 16 MB limit (got ${sizeMB} MB)`\n );\n }\n}\n\n/**\n * Validate write size for volatile operations.\n * Server limit is 2 KB for volatile writes.\n *\n * @throws LarkError if value exceeds 2 KB\n */\nexport function validateVolatileWriteSize(value: unknown, context?: string): void {\n const byteSize = getJsonByteSize(value);\n if (byteSize > MAX_VOLATILE_WRITE_BYTES) {\n const sizeKB = Math.round(byteSize / 1024 * 100) / 100;\n throw new LarkError(\n ErrorCode.INVALID_DATA,\n `${context ? `${context}: ` : ''}volatile write exceeds 2 KB limit (got ${sizeKB} KB)`\n );\n }\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, TxConditionOp, TxOperation } from './protocol/messages';\nimport { hashValue, isPrimitive } from './utils/hash';\nimport { getKey, getParentPath, joinPath, normalizePath } from './utils/path';\nimport { generatePushId } from './utils/pushid';\nimport { validateKey, validatePath, validateWriteData } from './utils/validation';\n\n/**\n * Check if a path is under .info (read-only system paths).\n */\nfunction isInfoPath(path: string): boolean {\n return path === '/.info' || path.startsWith('/.info/');\n}\n\n/**\n * Throw if attempting to write to a .info path.\n */\nfunction validateNotInfoPath(path: string, operation: string): void {\n if (isInfoPath(path)) {\n throw new LarkError(\n ErrorCode.INVALID_PATH,\n `Cannot ${operation} on .info paths - they are read-only system paths`\n );\n }\n}\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 startAfter?: { value: unknown; key?: string };\n endAt?: { value: unknown; key?: string };\n endBefore?: { value: unknown; key?: string };\n equalTo?: { value: unknown; key?: string };\n}\n\n/**\n * Wire protocol constants for query params.\n * Used to build the queryIdentifier.\n */\nconst WIRE_PROTOCOL_CONSTANTS = {\n INDEX_START_VALUE: 'sp',\n INDEX_START_NAME: 'sn',\n INDEX_START_IS_INCLUSIVE: 'sin',\n INDEX_END_VALUE: 'ep',\n INDEX_END_NAME: 'en',\n INDEX_END_IS_INCLUSIVE: 'ein',\n LIMIT: 'l',\n VIEW_FROM: 'vf',\n INDEX: 'i',\n} as const;\n\n/**\n * Convert an object to a unique sorted-key string for consistent comparison.\n * Keys are sorted alphabetically and serialized as JSON.\n */\nfunction objectToUniqueKey(obj: Record<string, unknown>): string {\n const sortedKeys = Object.keys(obj).sort();\n const sorted: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n sorted[key] = obj[key];\n }\n return JSON.stringify(sorted);\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 * Get the LarkDatabase instance this reference belongs to.\n */\n get database(): LarkDatabase {\n return this._db;\n }\n\n /**\n * Get the underlying reference for a query.\n * For queries (created via orderBy*, limitTo*, startAt, etc.), this returns\n * a reference to the same path without query constraints.\n * For non-query references, this returns the reference itself.\n */\n get ref(): DatabaseReference {\n // If this reference has no query constraints, return itself\n if (Object.keys(this._query).length === 0) {\n return this;\n }\n // Return a new reference to the same path without query constraints\n return new DatabaseReference(this._db, this._path);\n }\n\n /**\n * Check if this reference/query is equal to another.\n * Two references are equal if they have the same database, path, and query constraints.\n */\n isEqual(other: DatabaseReference | null): boolean {\n if (!other) return false;\n if (this._db !== other._db) return false;\n if (this._path !== other._path) return false;\n // Compare query constraints\n return JSON.stringify(this._query) === JSON.stringify(other._query);\n }\n\n /**\n * Get the unique identifier for this query's parameters.\n * Used to differentiate multiple queries on the same path.\n *\n * Returns \"default\" for non-query references (no constraints).\n * Returns a sorted JSON string of wire-format params for queries.\n *\n * Used for wire protocol and subscription deduplication.\n */\n get queryIdentifier(): string {\n // Build wire-format query object\n const queryObj: Record<string, unknown> = {};\n\n // Index (orderBy)\n if (this._query.orderBy === 'key') {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX] = '.key';\n } else if (this._query.orderBy === 'value') {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX] = '.value';\n } else if (this._query.orderBy === 'priority') {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX] = '.priority';\n } else if (this._query.orderBy === 'child' && this._query.orderByChildPath) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX] = this._query.orderByChildPath;\n }\n\n // Start constraint (startAt or startAfter)\n // Convert undefined to null for wire protocol\n if (this._query.startAt !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_VALUE] = this._query.startAt.value ?? null;\n if (this._query.startAt.key !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_NAME] = this._query.startAt.key;\n }\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_IS_INCLUSIVE] = true;\n } else if (this._query.startAfter !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_VALUE] = this._query.startAfter.value ?? null;\n // Use [MAX_NAME] sentinel when startAfter is used without a key\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_NAME] = this._query.startAfter.key ?? '[MAX_NAME]';\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_IS_INCLUSIVE] = false;\n }\n\n // End constraint (endAt or endBefore)\n // Convert undefined to null for wire protocol\n if (this._query.endAt !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_VALUE] = this._query.endAt.value ?? null;\n if (this._query.endAt.key !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_NAME] = this._query.endAt.key;\n }\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_IS_INCLUSIVE] = true;\n } else if (this._query.endBefore !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_VALUE] = this._query.endBefore.value ?? null;\n // Use [MIN_NAME] sentinel when endBefore is used without a key\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_NAME] = this._query.endBefore.key ?? '[MIN_NAME]';\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_IS_INCLUSIVE] = false;\n }\n\n // equalTo is startAt + endAt with same value\n if (this._query.equalTo !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_VALUE] = this._query.equalTo.value;\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_VALUE] = this._query.equalTo.value;\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_IS_INCLUSIVE] = true;\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_IS_INCLUSIVE] = true;\n if (this._query.equalTo.key !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_NAME] = this._query.equalTo.key;\n queryObj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_NAME] = this._query.equalTo.key;\n }\n }\n\n // Limit\n if (this._query.limitToFirst !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.LIMIT] = this._query.limitToFirst;\n queryObj[WIRE_PROTOCOL_CONSTANTS.VIEW_FROM] = 'l'; // left (first)\n } else if (this._query.limitToLast !== undefined) {\n queryObj[WIRE_PROTOCOL_CONSTANTS.LIMIT] = this._query.limitToLast;\n queryObj[WIRE_PROTOCOL_CONSTANTS.VIEW_FROM] = 'r'; // right (last)\n }\n\n // If no constraints, return \"default\"\n if (Object.keys(queryObj).length === 0) {\n return 'default';\n }\n\n // Return sorted unique key\n return objectToUniqueKey(queryObj);\n }\n\n /**\n * Get the data at this location. Alias for once('value').\n * Alternative to once('value') for reading data.\n */\n async get(): Promise<DataSnapshot> {\n return this.once('value');\n }\n\n // ============================================\n // Navigation\n // ============================================\n\n /**\n * Get a reference to a child path.\n */\n child(path: string): DatabaseReference {\n validatePath(path, 'child');\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 * @param value - The value to write\n */\n async set(value: unknown): Promise<void> {\n validateNotInfoPath(this._path, 'set');\n validateWriteData(value, 'set');\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 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 * @param values - The values to update\n */\n async update(values: Record<string, unknown>): Promise<void> {\n validateNotInfoPath(this._path, 'update');\n\n // Validate all keys and values\n for (const [key, value] of Object.entries(values)) {\n if (key.includes('/')) {\n // Path-like key (contains /) - validate as path\n validatePath(key, 'update');\n } else {\n // Regular key - validate as key\n validateKey(key, 'update');\n }\n if (value !== null) {\n validateWriteData(value, 'update');\n }\n }\n\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)\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 validateNotInfoPath(this._path, 'remove');\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 * Returns a ThenableReference - an object that is both a DatabaseReference\n * AND a Promise. You can access reference properties immediately (like `.key`)\n * and also await the result.\n *\n * If value is provided, the promise resolves after the write completes.\n * If no value is provided, the promise resolves immediately.\n *\n * @example\n * ```javascript\n * // Without value - returns ThenableReference, resolves immediately\n * const pushed = ref.push();\n * console.log(pushed.key); // \"-Abc123...\" (available immediately)\n * await pushed; // Resolves immediately to the same reference\n *\n * // With value - returns ThenableReference, resolves after write\n * const pushed2 = ref.push({ name: 'Alice' });\n * console.log(pushed2.key); // \"-Xyz789...\" (available immediately)\n * await pushed2; // Waits for write to complete\n * ```\n */\n push(): ThenableReference;\n push(value: unknown): ThenableReference;\n push(value?: unknown): ThenableReference {\n validateNotInfoPath(this._path, 'push');\n // Always generate key locally\n const key = generatePushId();\n const childRef = this.child(key);\n\n if (value === undefined) {\n // No value - return ThenableReference that resolves immediately\n return new ThenableReference(this._db, childRef.path);\n }\n\n // Value provided - do the set and create promise that resolves after\n const setPromise = childRef.set(value) as Promise<void>;\n const writePromise = setPromise.then(() => childRef);\n\n return new ThenableReference(this._db, childRef.path, writePromise);\n }\n\n /**\n * Set the data with a priority value for ordering.\n *\n * For objects: injects `.priority` into the value object.\n * For primitives: wraps as `{ '.value': primitive, '.priority': priority }`.\n *\n * Uses { '.value': value, '.priority': priority } format for the wire protocol.\n *\n * @param value - The value to write\n * @param priority - The priority for ordering\n */\n async setWithPriority(value: unknown, priority: number | string): Promise<void> {\n validateNotInfoPath(this._path, 'setWithPriority');\n validateWriteData(value, 'setWithPriority');\n if (typeof priority === 'string') {\n validateWriteData(priority, 'setWithPriority (priority)');\n }\n\n // Setting null/undefined with priority - just set the value (priority is lost)\n if (value === null || value === undefined) {\n await this._db._sendSet(this._path, value);\n return;\n }\n\n // Primitives (number, string, boolean) get wrapped in { '.value': x, '.priority': y }\n if (typeof value !== 'object' || Array.isArray(value)) {\n const wrappedValue = { '.value': value, '.priority': priority };\n await this._db._sendSet(this._path, wrappedValue);\n return;\n }\n\n // Objects get .priority injected directly\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 * Uses cached value for optimistic behavior (local effects are immediate).\n * The optimistic update happens synchronously, Promise resolves after server ack.\n */\n setPriority(priority: number | string | null): Promise<void> {\n validateNotInfoPath(this._path, 'setPriority');\n\n // Get cached value for optimistic behavior (no network fetch needed)\n const { value: cachedValue, found } = this._db._getCachedValue(this._path);\n\n // If not in cache, we need to fall back to async fetch\n if (!found) {\n return this.once().then(snapshot => {\n const actualValue = snapshot.val();\n if (actualValue === null || actualValue === undefined) {\n return this._db._sendSet(this._path, { '.priority': priority });\n }\n return this.setWithPriority(actualValue, priority as number | string);\n });\n }\n\n // Extract the actual value from the cached data\n // Cached data might be wrapped as { '.value': x, '.priority': y } for primitives\n // or as { key: val, '.priority': y } for objects\n let actualValue: unknown;\n if (cachedValue === null || cachedValue === undefined) {\n // Null value in cache\n actualValue = null;\n } else if (typeof cachedValue === 'object' && !Array.isArray(cachedValue)) {\n const obj = cachedValue as Record<string, unknown>;\n // Check for wrapped primitive: { '.value': x, '.priority': y }\n if ('.value' in obj && Object.keys(obj).every(k => k === '.value' || k === '.priority')) {\n actualValue = obj['.value'];\n } else {\n // Object - extract value without .priority\n const { '.priority': _oldPriority, ...rest } = obj;\n actualValue = Object.keys(rest).length > 0 ? rest : null;\n }\n } else {\n // Primitive value (no wrapper)\n actualValue = cachedValue;\n }\n\n if (actualValue === null || actualValue === undefined) {\n // No data exists, just set priority on empty object\n return this._db._sendSet(this._path, { '.priority': priority });\n }\n\n // setWithPriority does the optimistic update synchronously, returns Promise for ack\n return this.setWithPriority(actualValue, priority as number | string);\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 // eslint-disable-next-line @typescript-eslint/no-explicit-any\n updateFunction: (currentValue: any) => any,\n maxRetries: number = DEFAULT_MAX_RETRIES\n ): Promise<TransactionResult> {\n validateNotInfoPath(this._path, 'transaction');\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(); // Stripped value for user callback\n const rawValue = currentSnapshot.exportVal(); // Raw value for condition check\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 condition based on RAW value type (includes priority metadata)\n // Primitives use direct value comparison, complex values use hash\n let condition: TxConditionOp;\n if (isPrimitive(rawValue)) {\n condition = { o: 'c', p: this._path, v: rawValue };\n } else {\n const hash = await hashValue(rawValue);\n condition = { o: 'c', p: this._path, h: hash };\n }\n\n // Step 5: Build transaction with condition + set\n // Preserves priority unless explicitly changed, so if the old value had\n // priority and the new value is a primitive, wrap it to preserve the priority\n let valueToSet = newValue;\n const existingPriority = currentSnapshot.getPriority();\n if (existingPriority !== null && isPrimitive(newValue) && newValue !== null) {\n valueToSet = { '.priority': existingPriority, '.value': newValue };\n }\n const ops: TxOperation[] = [condition, { o: 's', p: this._path, v: valueToSet }];\n\n try {\n // Step 6: 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 7: 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 * For 'value' events, this fetches data directly from the server.\n * For child events ('child_added', 'child_changed', 'child_removed', 'child_moved'),\n * this subscribes, waits for the first event, then unsubscribes.\n *\n * @param eventType - The event type\n * @returns Promise that resolves to the DataSnapshot\n */\n once(eventType: EventType = 'value'): Promise<DataSnapshot> {\n if (eventType === 'value') {\n // Use the optimized direct fetch for 'value' events\n return this._db._sendOnce(this._path, this._buildQueryParams());\n }\n\n // For child events, subscribe and wait for the first event\n return new Promise((resolve) => {\n const unsubscribe = this.on(eventType, (snapshot) => {\n unsubscribe();\n resolve(snapshot);\n });\n });\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(), this.queryIdentifier);\n }\n\n /**\n * Unsubscribe from events.\n *\n * - `off()` - removes ALL listeners at this path\n * - `off('value')` - removes all 'value' listeners at this path\n *\n * Note: To unsubscribe a specific callback, use the unsubscribe function\n * returned by `on()`.\n *\n * @param eventType - Optional event type to unsubscribe from\n */\n off(eventType?: EventType): void {\n if (eventType) {\n // Remove all callbacks for this event type\n this._db._unsubscribeEventType(this._path, eventType);\n } else {\n // Remove all callbacks at this path\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 this._validateNoOrderBy('orderByKey');\n // orderByKey doesn't allow key parameters on startAt/endAt/equalTo\n this._validateNoKeyParameterForOrderByKey('orderByKey');\n // orderByKey requires string values for startAt/endAt/equalTo (validate if already set)\n this._validateStringValuesForOrderByKey('orderByKey');\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 this._validateNoOrderBy('orderByPriority');\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 this._validateNoOrderBy('orderByChild');\n // Validate path doesn't contain reserved $ prefixes\n if (path.startsWith('$') || path.includes('/$')) {\n throw new LarkError(\n ErrorCode.INVALID_PATH,\n `orderByChild: Invalid path '${path}'. Paths cannot contain '$' prefix (reserved for internal use)`\n );\n }\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 this._validateNoOrderBy('orderByValue');\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 // Validate limit is a positive integer\n if (limit === undefined || limit === null) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToFirst: a limit must be provided'\n );\n }\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToFirst: limit must be a positive integer'\n );\n }\n if (this._query.limitToFirst !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToFirst: limitToFirst() cannot be called multiple times'\n );\n }\n if (this._query.limitToLast !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToFirst: cannot use limitToFirst() with limitToLast()'\n );\n }\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 // Validate limit is a positive integer\n if (limit === undefined || limit === null) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToLast: a limit must be provided'\n );\n }\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToLast: limit must be a positive integer'\n );\n }\n if (this._query.limitToLast !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToLast: limitToLast() cannot be called multiple times'\n );\n }\n if (this._query.limitToFirst !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.limitToLast: cannot use limitToLast() with limitToFirst()'\n );\n }\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 * If no value is provided, starts at the beginning (null priority).\n */\n startAt(value?: unknown, key?: string): DatabaseReference {\n if (this._query.startAt !== undefined || this._query.startAfter !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.startAt: startAt()/startAfter() cannot be called multiple times'\n );\n }\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.startAt: cannot use startAt() with equalTo()'\n );\n }\n this._validateQueryValue('startAt', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n startAt: { value, key },\n });\n }\n\n /**\n * Start after a specific value/key (exclusive).\n * Like startAt() but excludes the starting point.\n */\n startAfter(value?: unknown, key?: string): DatabaseReference {\n if (this._query.startAfter !== undefined || this._query.startAt !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.startAfter: startAfter()/startAt() cannot be called multiple times'\n );\n }\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.startAfter: cannot use startAfter() with equalTo()'\n );\n }\n this._validateQueryValue('startAfter', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n startAfter: { value, key },\n });\n }\n\n /**\n * End at a specific value/key.\n * If no value is provided, ends at the end (null priority).\n */\n endAt(value?: unknown, key?: string): DatabaseReference {\n if (this._query.endAt !== undefined || this._query.endBefore !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.endAt: endAt()/endBefore() cannot be called multiple times'\n );\n }\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.endAt: cannot use endAt() with equalTo()'\n );\n }\n this._validateQueryValue('endAt', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n endAt: { value, key },\n });\n }\n\n /**\n * End before a specific value/key (exclusive).\n * Like endAt() but excludes the ending point.\n */\n endBefore(value?: unknown, key?: string): DatabaseReference {\n if (this._query.endBefore !== undefined || this._query.endAt !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.endBefore: endBefore()/endAt() cannot be called multiple times'\n );\n }\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.endBefore: cannot use endBefore() with equalTo()'\n );\n }\n this._validateQueryValue('endBefore', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n endBefore: { value, key },\n });\n }\n\n /**\n * Filter to items equal to a specific value.\n */\n equalTo(value: unknown, key?: string): DatabaseReference {\n if (this._query.equalTo !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.equalTo: equalTo() cannot be called multiple times'\n );\n }\n if (this._query.startAt !== undefined || this._query.startAfter !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.equalTo: cannot use equalTo() with startAt()/startAfter()'\n );\n }\n if (this._query.endAt !== undefined || this._query.endBefore !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n 'Query.equalTo: cannot use equalTo() with endAt()/endBefore()'\n );\n }\n this._validateQueryValue('equalTo', value, key);\n return new DatabaseReference(this._db, this._path, {\n ...this._query,\n equalTo: { value, key },\n });\n }\n\n // ============================================\n // Query Validation Helpers\n // ============================================\n\n /**\n * Validate that no orderBy has been set yet.\n */\n private _validateNoOrderBy(methodName: string): void {\n if (this._query.orderBy !== undefined) {\n const existingMethod =\n this._query.orderBy === 'child'\n ? `orderByChild('${this._query.orderByChildPath}')`\n : `orderBy${this._query.orderBy.charAt(0).toUpperCase()}${this._query.orderBy.slice(1)}()`;\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: cannot use ${methodName}() with ${existingMethod}`\n );\n }\n }\n\n /**\n * Validate that no key parameter has been set on startAt/endAt/equalTo.\n * Used when calling orderByKey() after startAt/endAt/equalTo.\n */\n private _validateNoKeyParameterForOrderByKey(methodName: string): void {\n if (this._query.startAt?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in startAt()`\n );\n }\n if (this._query.startAfter?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in startAfter()`\n );\n }\n if (this._query.endAt?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in endAt()`\n );\n }\n if (this._query.endBefore?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in endBefore()`\n );\n }\n if (this._query.equalTo?.key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter in equalTo()`\n );\n }\n }\n\n /**\n * Validate that startAt/endAt/equalTo values are strings.\n * Used when calling orderByKey() after startAt/endAt/equalTo.\n */\n private _validateStringValuesForOrderByKey(methodName: string): void {\n if (this._query.startAt !== undefined && typeof this._query.startAt.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), startAt() value must be a string`\n );\n }\n if (this._query.startAfter !== undefined && typeof this._query.startAfter.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), startAfter() value must be a string`\n );\n }\n if (this._query.endAt !== undefined && typeof this._query.endAt.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), endAt() value must be a string`\n );\n }\n if (this._query.endBefore !== undefined && typeof this._query.endBefore.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), endBefore() value must be a string`\n );\n }\n if (this._query.equalTo !== undefined && typeof this._query.equalTo.value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), equalTo() value must be a string`\n );\n }\n }\n\n /**\n * Validate value and key types for query methods (startAt, endAt, equalTo, etc.)\n */\n private _validateQueryValue(methodName: string, value: unknown, key?: string): void {\n // Validate key parameter format if provided\n if (key !== undefined) {\n this._validateKeyFormat(methodName, key);\n }\n\n // orderByKey: requires string value, no key parameter\n if (this._query.orderBy === 'key') {\n // Value must be a string for orderByKey (null is also not allowed)\n if (typeof value !== 'string') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), the value must be a string`\n );\n }\n // Key parameter is not allowed with orderByKey (the value IS the key)\n if (key !== undefined) {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByKey(), you cannot specify a key parameter`\n );\n }\n return;\n }\n\n // orderByPriority: booleans not allowed (only null, number, string)\n if (this._query.orderBy === 'priority') {\n if (typeof value === 'boolean') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: when using orderByPriority(), the value must be a valid priority (null, number, or string)`\n );\n }\n }\n\n // General validation: objects not allowed as values (arrays are objects too)\n // This applies to orderByChild and default ordering\n if (value !== null && typeof value === 'object') {\n throw new LarkError(\n ErrorCode.INVALID_QUERY,\n `Query.${methodName}: the value argument cannot be an object`\n );\n }\n }\n\n /**\n * Validate that a key is a valid key format.\n * Delegates to the shared validation utility.\n */\n private _validateKeyFormat(methodName: string, key: string): void {\n validateKey(key, `Query.${methodName}`);\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 // Convert undefined to null for wire protocol\n if (this._query.startAt !== undefined) {\n params.startAt = this._query.startAt.value ?? null;\n if (this._query.startAt.key !== undefined) {\n params.startAtKey = this._query.startAt.key;\n }\n hasParams = true;\n }\n\n if (this._query.startAfter !== undefined) {\n params.startAfter = this._query.startAfter.value ?? null;\n if (this._query.startAfter.key !== undefined) {\n params.startAfterKey = this._query.startAfter.key;\n }\n hasParams = true;\n }\n\n if (this._query.endAt !== undefined) {\n params.endAt = this._query.endAt.value ?? null;\n if (this._query.endAt.key !== undefined) {\n params.endAtKey = this._query.endAt.key;\n }\n hasParams = true;\n }\n\n if (this._query.endBefore !== undefined) {\n params.endBefore = this._query.endBefore.value ?? null;\n if (this._query.endBefore.key !== undefined) {\n params.endBeforeKey = this._query.endBefore.key;\n }\n hasParams = true;\n }\n\n if (this._query.equalTo !== undefined) {\n params.equalTo = this._query.equalTo.value ?? null;\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 * Returns the URL for JSON serialization.\n * This allows refs to be serialized with JSON.stringify().\n */\n toJSON(): string {\n return this.toString();\n }\n}\n\n/**\n * A reference that is also a Promise - returned by push().\n *\n * This allows patterns like:\n * ```javascript\n * const pushed = ref.push();\n * console.log(pushed.key); // Key available immediately\n * await pushed; // Wait for write to complete\n * ```\n */\nexport class ThenableReference extends DatabaseReference implements PromiseLike<DatabaseReference> {\n private readonly _promise: Promise<DatabaseReference>;\n\n constructor(\n db: LarkDatabase,\n path: string,\n promise?: Promise<DatabaseReference>\n ) {\n super(db, path);\n // If no promise provided, resolve immediately with this reference\n this._promise = promise ?? Promise.resolve(this);\n }\n\n then<TResult1 = DatabaseReference, TResult2 = never>(\n onfulfilled?: ((value: DatabaseReference) => TResult1 | PromiseLike<TResult1>) | undefined | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | undefined | null\n ): Promise<TResult1 | TResult2> {\n return this._promise.then(onfulfilled, onrejected);\n }\n\n catch<TResult = never>(\n onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | undefined | null\n ): Promise<DatabaseReference | TResult> {\n return this._promise.catch(onrejected);\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 *\n * Wrapped Primitives:\n * - Primitives with priority are stored as `{ '.value': x, '.priority': y }`\n * - val() automatically unwraps these to return just the primitive value\n */\n\nimport type { DatabaseReference } from './DatabaseReference';\nimport type { LarkDatabase } from './LarkDatabase';\nimport type { QueryParams } from './protocol/messages';\nimport { getKey, getValueAtPath, joinPath, normalizePath } from './utils/path';\nimport { getSortedKeys } from './utils/sort';\n\n/**\n * Check if data is a wrapped primitive.\n * Wrapped primitives are objects with:\n * - { '.value': x, '.priority': y } - full form\n * - { '.value': x } - when server strips null priority\n * This is the wire format for primitives with priority.\n */\nfunction isWrappedPrimitive(data: unknown): data is { '.value': unknown; '.priority'?: unknown } {\n if (!data || typeof data !== 'object' || Array.isArray(data)) {\n return false;\n }\n const keys = Object.keys(data);\n // Full form: exactly '.value' and '.priority'\n if (keys.length === 2 && '.value' in data && '.priority' in data) {\n return true;\n }\n // Stripped form: only '.value' (server strips null priority)\n if (keys.length === 1 && '.value' in data) {\n return true;\n }\n return false;\n}\n\n/**\n * Recursively strip priority metadata from data.\n * - Unwraps wrapped primitives: { '.value': x, '.priority': y } → x\n * - Strips '.priority' from objects\n * - Recursively processes all children\n */\nfunction stripPriorityMetadata(data: unknown): unknown {\n // Return null for non-existent data, never undefined\n if (data === null || data === undefined) {\n return null;\n }\n\n // Non-objects pass through (primitives)\n if (typeof data !== 'object') {\n return data;\n }\n\n // Arrays: recursively process elements\n if (Array.isArray(data)) {\n return data.map(stripPriorityMetadata);\n }\n\n // Check for wrapped primitive: { '.value': x, '.priority': y }\n if (isWrappedPrimitive(data)) {\n // Recursively process the value in case it's nested\n return stripPriorityMetadata(data['.value']);\n }\n\n // Regular object: strip .priority and null values, recursively process children\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key === '.priority') {\n continue; // Skip priority metadata\n }\n const stripped = stripPriorityMetadata(value);\n // Omit null values from objects (null = deleted/non-existent)\n if (stripped !== null) {\n result[key] = stripped;\n }\n }\n\n // If object is now empty (only had .priority or null values), return null\n return Object.keys(result).length > 0 ? result : null;\n}\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 private readonly _queryParams: QueryParams | null;\n private readonly _orderedKeys: string[] | null;\n\n constructor(\n data: unknown,\n path: string,\n db: LarkDatabase,\n options: {\n volatile?: boolean;\n serverTimestamp?: number | null;\n queryParams?: QueryParams | null;\n orderedKeys?: string[] | null;\n } = {}\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 this._queryParams = options.queryParams ?? null;\n this._orderedKeys = options.orderedKeys ?? 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 metadata stripped.\n *\n * - Strips `.priority` from objects (it's metadata, not user data)\n * - Unwraps `{ '.value': x, '.priority': y }` to just `x` (wrapped primitives)\n *\n * @typeParam T - Optional type for the returned value. Defaults to `any`.\n * @example\n * ```typescript\n * // Untyped (returns any)\n * const data = snapshot.val();\n *\n * // Typed (returns User)\n * const user = snapshot.val<User>();\n * ```\n */\n val<T = any>(): T {\n // Recursively strip priority metadata from the entire data tree\n return stripPriorityMetadata(this._data) as T;\n }\n\n /**\n * Check if data exists at this location (is not null/undefined).\n * Returns false for priority-only nodes (only .priority, no actual value).\n */\n exists(): boolean {\n if (this._data === null || this._data === undefined) {\n return false;\n }\n\n // Check for priority-only object (no actual data, just metadata)\n if (typeof this._data === 'object' && !Array.isArray(this._data)) {\n const keys = Object.keys(this._data);\n if (keys.length === 1 && keys[0] === '.priority') {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Get a child snapshot at the specified path.\n *\n * Special handling:\n * - `.priority` returns the priority value\n * - For wrapped primitives, only `.priority` returns data; other paths return null\n * - Non-existent paths return a snapshot with val() === null\n */\n child(path: string): DataSnapshot {\n const childPath = joinPath(this._path, path);\n\n // Special case: .priority returns the priority value\n if (path === '.priority') {\n const priority = this.getPriority();\n return new DataSnapshot(priority, childPath, this._db, {\n volatile: this._volatile,\n serverTimestamp: this._serverTimestamp,\n });\n }\n\n // For wrapped primitives, no children exist (except .priority handled above)\n if (isWrappedPrimitive(this._data)) {\n return new DataSnapshot(null, childPath, this._db, {\n volatile: this._volatile,\n serverTimestamp: this._serverTimestamp,\n });\n }\n\n const childData = getValueAtPath(this._data, path);\n // Convert undefined to null\n return new DataSnapshot(childData ?? null, childPath, this._db, {\n volatile: this._volatile,\n serverTimestamp: this._serverTimestamp,\n });\n }\n\n /**\n * Check if this snapshot has any children.\n * Excludes `.priority` metadata from consideration.\n * Wrapped primitives have no children.\n */\n hasChildren(): boolean {\n if (typeof this._data !== 'object' || this._data === null) {\n return false;\n }\n // Wrapped primitives have no children\n if (isWrappedPrimitive(this._data)) {\n return false;\n }\n // Count keys excluding .priority\n const keys = Object.keys(this._data).filter(k => k !== '.priority');\n return keys.length > 0;\n }\n\n /**\n * Check if this snapshot has a specific child.\n * `.priority` is always accessible if it exists.\n * Wrapped primitives have no children except `.priority`.\n */\n hasChild(path: string): boolean {\n // .priority is accessible if it exists\n if (path === '.priority') {\n return this.getPriority() !== null;\n }\n // Wrapped primitives have no other children\n if (isWrappedPrimitive(this._data)) {\n return false;\n }\n const childData = getValueAtPath(this._data, path);\n return childData !== undefined && childData !== null;\n }\n\n /**\n * Get the number of children.\n * Excludes `.priority` metadata from count.\n * Wrapped primitives have 0 children.\n */\n numChildren(): number {\n if (typeof this._data !== 'object' || this._data === null) {\n return 0;\n }\n // Wrapped primitives have no children\n if (isWrappedPrimitive(this._data)) {\n return 0;\n }\n // Count keys excluding .priority\n return Object.keys(this._data).filter(k => k !== '.priority').length;\n }\n\n /**\n * Iterate over children in the correct order.\n * Uses pre-computed orderedKeys if available (from subscription View),\n * otherwise computes sorted keys based on query params.\n * Excludes `.priority` metadata from iteration.\n * Wrapped primitives have no children to iterate.\n */\n forEach(callback: (child: DataSnapshot) => boolean | void): void {\n if (typeof this._data !== 'object' || this._data === null) {\n return;\n }\n\n // Wrapped primitives have no children\n if (isWrappedPrimitive(this._data)) {\n return;\n }\n\n // Use pre-computed orderedKeys if available (from subscription View),\n // otherwise compute sorted keys based on query params\n const keys = this._orderedKeys ?? getSortedKeys(this._data, this._queryParams);\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 */\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 */\n getServerTimestamp(): number | null {\n return this._serverTimestamp;\n }\n\n /**\n * Export the snapshot data with priority metadata intact.\n * Unlike val(), this preserves `.value` and `.priority` wrappers.\n * Useful for serializing data while preserving priorities.\n *\n * @typeParam T - Optional type for the returned value. Defaults to `any`.\n */\n exportVal<T = any>(): T {\n return this._data as T;\n }\n\n /**\n * Export the snapshot data as JSON.\n * Same as exportVal() - preserves priority metadata.\n *\n * @typeParam T - Optional type for the returned value. Defaults to `any`.\n */\n toJSON<T = any>(): T {\n return this._data as T;\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 (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 { createTransport, TransportType } from './connection/createTransport';\nimport { AuthCompleteResponse, JoinCompleteResponse, MessageQueue } from './connection/MessageQueue';\nimport { PendingWriteManager } from './connection/PendingWriteManager';\nimport { SubscriptionManager, SnapshotCallback } from './connection/SubscriptionManager';\nimport { Transport } from './connection/Transport';\nimport { DatabaseReference } from './DatabaseReference';\nimport { DataSnapshot } from './DataSnapshot';\nimport { LarkError } from './LarkError';\nimport { DEFAULT_LARK_DOMAIN, EventType } from './protocol/constants';\nimport {\n AuthMessage,\n ClientMessage,\n JoinMessage,\n ServerMessage,\n UnauthMessage,\n isAckMessage,\n isAuthCompleteMessage,\n isEventMessage,\n isNackMessage,\n isPingMessage,\n QueryParams,\n TxOperation,\n TransactionMessage,\n} from './protocol/messages';\nimport { hashValue, isPrimitive } from './utils/hash';\nimport { normalizePath } from './utils/path';\nimport { validatePath, validateWriteData, validateWriteSize, validateVolatileWriteSize } from './utils/validation';\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 /** Lark domain (default: larkdb.net) */\n domain?: string;\n\n /**\n * Transport type preference.\n * - 'auto': Try WebTransport first, fall back to WebSocket (default)\n * - 'websocket': Force WebSocket only\n * - 'webtransport': Force WebTransport only (fails if unavailable)\n */\n transport?: TransportType;\n\n /**\n * Timeout for WebTransport connection attempt in milliseconds.\n * If WebTransport doesn't connect within this time, falls back to WebSocket.\n * Only applies when transport is 'auto'.\n * Default: 2000 (2 seconds)\n */\n webtransportTimeout?: number;\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\n/**\n * Connection state machine:\n * - 'disconnected': Not connected\n * - 'connecting': Initial connection in progress\n * - 'connected': WebSocket open\n * - 'joined': Join complete (database identified)\n * - 'authenticated': Auth complete (ready to operate)\n * - 'reconnecting': Attempting to reconnect after disconnect\n */\ntype ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'joined' | 'authenticated' | 'reconnecting';\n\n/** Callback for .info path subscriptions */\ntype InfoCallback = (snapshot: DataSnapshot) => void;\n\n/** Subscription info for .info paths */\ninterface InfoSubscription {\n path: string;\n callback: InfoCallback;\n}\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\n/**\n * Check if a value is a ServerValue placeholder.\n */\nfunction isServerValue(value: unknown): value is { '.sv': unknown } {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n '.sv' in value\n );\n}\n\n/**\n * Resolve ServerValue placeholders locally for optimistic updates.\n * - TIMESTAMP: replaced with Date.now()\n * - increment(delta): replaced with currentValue + delta (or delta if no current value)\n *\n * @param value - The value to resolve\n * @param currentValue - The current value at the path (for increment resolution)\n * @returns The value with ServerValues resolved locally\n */\nfunction resolveServerValuesLocally(value: unknown, currentValue?: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n // Check for ServerValue placeholder\n if (isServerValue(value)) {\n const sv = value['.sv'];\n\n // TIMESTAMP\n if (sv === 'timestamp') {\n return Date.now();\n }\n\n // increment\n if (typeof sv === 'object' && sv !== null && 'increment' in sv) {\n const delta = (sv as { increment: number }).increment;\n if (typeof currentValue === 'number') {\n return currentValue + delta;\n }\n // If current value is not a number, increment treats it as 0\n return delta;\n }\n\n // Unknown server value, return as-is (will be resolved by server)\n return value;\n }\n\n // Recursively process objects\n if (typeof value === 'object' && !Array.isArray(value)) {\n const result: Record<string, unknown> = {};\n const currentObj = currentValue !== null && typeof currentValue === 'object' && !Array.isArray(currentValue)\n ? currentValue as Record<string, unknown>\n : {};\n\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n result[key] = resolveServerValuesLocally(val, currentObj[key]);\n }\n return result;\n }\n\n // Recursively process arrays\n if (Array.isArray(value)) {\n return value.map((item, index) => {\n const currentArr = Array.isArray(currentValue) ? currentValue : [];\n return resolveServerValuesLocally(item, currentArr[index]);\n });\n }\n\n // Primitive values pass through unchanged\n return value;\n}\n\n/**\n * Server values that are resolved by the server when a write is committed.\n * Use these with set(), update(), or push() to have the server fill in values.\n */\nexport const ServerValue = {\n /**\n * A placeholder value for auto-populating the current server timestamp.\n * The server will replace this with the actual Unix timestamp in milliseconds.\n *\n * @example\n * ```javascript\n * import { ServerValue } from '@lark-sh/client';\n * await ref.set({ createdAt: ServerValue.TIMESTAMP });\n * // Server stores: { createdAt: 1704067200000 }\n * ```\n */\n TIMESTAMP: { '.sv': 'timestamp' } as const,\n\n /**\n * Returns a placeholder value for atomically incrementing a numeric field.\n * If the field doesn't exist or isn't a number, it's treated as 0.\n *\n * @param delta - The amount to increment by (can be negative to decrement)\n * @returns A server value placeholder\n *\n * @example\n * ```javascript\n * import { ServerValue } from '@lark-sh/client';\n * await ref.child('score').set(ServerValue.increment(10));\n * // Atomically adds 10 to the current score\n * ```\n */\n increment: (delta: number) => ({ '.sv': { increment: delta } }),\n};\n\nexport class LarkDatabase {\n /**\n * Server values that are resolved by the server when a write is committed.\n * Alias for the exported ServerValue object.\n */\n static ServerValue = ServerValue;\n\n private _state: ConnectionState = 'disconnected';\n private _auth: AuthInfo | null = null;\n private _databaseId: string;\n private _domain: string;\n private _volatilePaths: string[] = [];\n private _transportType: 'websocket' | 'webtransport' | null = null;\n\n // Auth state\n private _currentToken: string; // Token for auth (empty string = anonymous)\n private _isAnonymous: boolean; // True if connected anonymously\n\n // Reconnection state\n private _connectionId: string | null = null;\n private _connectOptions: ConnectOptions;\n private _intentionalDisconnect = false;\n private _reconnectAttempt = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n private transport: Transport | 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 private authStateChangedCallbacks = new Set<(auth: AuthInfo | null) => void>();\n\n // .info path subscriptions (handled locally, not sent to server)\n private infoSubscriptions: InfoSubscription[] = [];\n\n // Connection promise - shared by connect() and lazy operations\n private _connectionPromise: Promise<void> | null = null;\n private _serverTimeOffset: number = 0;\n\n /**\n * Create a new LarkDatabase instance.\n *\n * @param databaseId - Database ID in format \"project/database\"\n * @param options - Connection options (token, anonymous, domain, transport).\n * Defaults to anonymous auth if not specified.\n *\n * @example\n * ```typescript\n * // Lazy connection - connects on first operation\n * const db = new LarkDatabase('project/database', { anonymous: true });\n * db.ref('/users').on('value', cb); // Auto-connects here\n *\n * // Explicit connection - for UI feedback or error handling\n * const db = new LarkDatabase('project/database', { anonymous: true });\n * await db.connect(); // Explicitly await connection\n * db.ref('/users').on('value', cb);\n * ```\n */\n constructor(databaseId: string, options: ConnectOptions = {}) {\n this.messageQueue = new MessageQueue();\n this.subscriptionManager = new SubscriptionManager();\n this.pendingWrites = new PendingWriteManager();\n\n // Validate database ID\n const projectId = databaseId.split('/')[0];\n if (!projectId) {\n throw new Error('Invalid database ID: must be in format \"projectId/databaseName\"');\n }\n\n // Store config\n this._databaseId = databaseId;\n this._connectOptions = options;\n this._domain = options.domain || DEFAULT_LARK_DOMAIN;\n this._currentToken = options.token || '';\n this._isAnonymous = !options.token && options.anonymous !== false;\n\n // Initialize subscription manager callbacks\n // This enables lazy connection - on()/once() will trigger connection via sendSubscribeMessage\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 // ============================================\n // Connection State\n // ============================================\n\n /**\n * Whether the database is fully connected and authenticated.\n * Returns true when ready to perform database operations.\n */\n get connected(): boolean {\n return this._state === 'authenticated';\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._domain && this._databaseId) {\n const projectId = this._databaseId.split('/')[0];\n return `https://${projectId}.${this._domain}`;\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 */\n get volatilePaths(): string[] {\n return this._volatilePaths;\n }\n\n /**\n * Get the current transport type.\n * Returns 'websocket' or 'webtransport', or null if not connected.\n */\n get transportType(): 'websocket' | 'webtransport' | null {\n return this._transportType;\n }\n\n /**\n * Get the estimated server time offset in milliseconds.\n * Add this to Date.now() to get approximate server time.\n */\n get serverTimeOffset(): number {\n return this._serverTimeOffset;\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 * Explicitly connect to the database.\n *\n * This is optional - operations will auto-connect if needed.\n * Use this when you want to:\n * - Await connection completion for UI feedback\n * - Handle connection errors separately from operation errors\n * - Pre-warm the connection before first operation\n *\n * @returns Promise that resolves when fully authenticated\n */\n async connect(): Promise<void> {\n return this.ensureConnected();\n }\n\n /**\n * Ensure connection is established, triggering it if needed.\n * Multiple concurrent calls share the same connection promise.\n */\n private async ensureConnected(): Promise<void> {\n // Already authenticated - nothing to do\n if (this._state === 'authenticated') {\n return;\n }\n\n // Connection in progress - wait for it\n if (this._connectionPromise) {\n return this._connectionPromise;\n }\n\n // Need to connect\n this._intentionalDisconnect = false;\n this._connectionPromise = this.performConnect(this._databaseId, this._connectOptions);\n\n try {\n await this._connectionPromise;\n } catch (error) {\n // Clear promise so next attempt can retry\n this._connectionPromise = null;\n throw error;\n }\n }\n\n /**\n * Internal connect implementation used by both initial connect and reconnect.\n * Implements the Join → Auth flow:\n * 1. Connect WebSocket\n * 2. Send join (identifies database)\n * 3. Send auth (authenticates user - required even for anonymous)\n */\n private async performConnect(\n databaseId: string,\n options: ConnectOptions,\n isReconnect = false\n ): Promise<void> {\n this._state = isReconnect ? 'reconnecting' : 'connecting';\n\n try {\n // Construct server URL from database ID\n // Format: \"projectId/databaseName\" -> \"wss://projectId.larkdb.net/ws\"\n const projectId = databaseId.split('/')[0];\n const wsUrl = `wss://${projectId}.${this._domain}/ws`;\n\n // Step 2: Connect to the server via WebTransport or WebSocket\n const transportResult = await createTransport(\n wsUrl,\n {\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 transport: options.transport,\n webtransportTimeout: options.webtransportTimeout,\n }\n );\n\n this.transport = transportResult.transport;\n this._transportType = transportResult.type;\n this._state = 'connected';\n\n // Step 3: Send join message (identifies the database, no token)\n const joinRequestId = this.messageQueue.nextRequestId();\n const joinMessage: JoinMessage = {\n o: 'j',\n d: databaseId,\n r: joinRequestId,\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(joinRequestId)) as JoinCompleteResponse;\n this._volatilePaths = joinResponse.volatilePaths;\n this._connectionId = joinResponse.connectionId;\n\n // Calculate server time offset (for .info/serverTimeOffset)\n if (joinResponse.serverTime != null) {\n this._serverTimeOffset = joinResponse.serverTime - Date.now();\n }\n\n this._state = 'joined';\n\n // Step 4: Send auth message (REQUIRED - even for anonymous)\n const authRequestId = this.messageQueue.nextRequestId();\n const authMessage: AuthMessage = {\n o: 'au',\n t: this._currentToken ?? '',\n r: authRequestId,\n };\n\n this.send(authMessage);\n\n // Wait for auth complete (returns UID)\n const authResponse = (await this.messageQueue.registerRequest(authRequestId)) as AuthCompleteResponse;\n\n // Update auth info from auth response\n this._auth = {\n uid: authResponse.uid || '',\n provider: this._isAnonymous ? 'anonymous' : 'custom',\n token: {}, // Token claims would need to be decoded from the token if needed\n };\n\n this._state = 'authenticated';\n this._reconnectAttempt = 0; // Reset reconnect attempt counter on success\n\n // Fire connection state change for .info/connected subscribers\n this.fireConnectionStateChange();\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\n // Fire auth state change\n this.authStateChangedCallbacks.forEach((cb) => cb(this._auth));\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 transport but keep config for retry\n this._state = 'disconnected';\n this._auth = null;\n this._connectionId = null;\n this._transportType = null;\n this.transport?.close();\n this.transport = 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 // Check if we were fully authenticated or at least partially connected\n const wasAuthenticated = this._state === 'authenticated';\n const wasPartiallyConnected = this._state === 'connected' || this._state === 'joined';\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 ((wasAuthenticated || wasPartiallyConnected) && this.transport) {\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 (wasAuthenticated || wasPartiallyConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n }\n\n /**\n * Temporarily disable the connection.\n * Disconnects from the server but preserves subscriptions for later reconnection via goOnline().\n */\n goOffline(): void {\n if (this._state === 'authenticated' || this._state === 'joined' ||\n this._state === 'connected' || this._state === 'reconnecting') {\n this._intentionalDisconnect = true;\n // Clear any pending reconnect timer\n if (this._reconnectTimer) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n this.transport?.close();\n this.transport = null;\n this._state = 'disconnected';\n // Keep cache intact so get()/once() can read from it while offline\n // Fire connection state change for .info/connected subscribers\n this.fireConnectionStateChange();\n }\n }\n\n /**\n * Re-enable the connection after goOffline().\n * Reconnects to the database and restores subscriptions.\n */\n goOnline(): void {\n this._intentionalDisconnect = false;\n\n // If we're disconnected and have connection info, reconnect\n if (this._state === 'disconnected' && this._databaseId && this._connectOptions) {\n this._state = 'reconnecting';\n this.reconnectingCallbacks.forEach((cb) => cb());\n // Attempt to reconnect immediately (no backoff for intentional goOnline)\n this._reconnectAttempt = 0;\n this.attemptReconnect();\n }\n }\n\n /**\n * Full cleanup - clears connection state including subscriptions.\n * Used for intentional disconnect.\n * Preserves config (databaseId, domain, token) so connect() can be called again.\n */\n private cleanupFull(): void {\n const wasAuthenticated = this._state === 'authenticated';\n\n this.transport?.close();\n this.transport = null;\n this._state = 'disconnected';\n this._auth = null;\n this._volatilePaths = [];\n this._connectionId = null;\n this._transportType = null;\n this._reconnectAttempt = 0;\n this._connectionPromise = null;\n this.subscriptionManager.clear();\n this.messageQueue.rejectAll(new Error('Connection closed'));\n this.pendingWrites.clear();\n\n // Fire connection state change for .info/connected subscribers\n if (wasAuthenticated) {\n this.fireConnectionStateChange();\n }\n\n // Clear info subscriptions\n this.infoSubscriptions = [];\n }\n\n /**\n * Partial cleanup - preserves state needed for reconnect.\n * Used for unexpected disconnect.\n */\n private cleanupForReconnect(): void {\n this.transport?.close();\n this.transport = null;\n this._auth = null;\n // Keep: _databaseId, _domain, _connectionId, _connectOptions, _transportType\n // Keep: _currentToken, _isAnonymous (for re-auth on reconnect)\n // Keep: subscriptionManager subscriptions (but clear cache)\n // Keep: pendingWrites\n // Keep: infoSubscriptions (will fire again when reconnected)\n this.subscriptionManager.clearCacheOnly();\n this.messageQueue.rejectAll(new Error('Connection closed'));\n\n // Fire connection state change for .info/connected subscribers (now disconnected)\n this.fireConnectionStateChange();\n }\n\n // ============================================\n // .info Path Handling\n // ============================================\n\n /**\n * Check if a path is a .info path (handled locally).\n */\n private isInfoPath(path: string): boolean {\n const normalizedPath = normalizePath(path) || '/';\n return normalizedPath === '/.info' || normalizedPath.startsWith('/.info/');\n }\n\n /**\n * Get the current value for a .info path.\n */\n private getInfoValue(path: string): unknown {\n const normalizedPath = normalizePath(path) || '/';\n\n if (normalizedPath === '/.info/connected') {\n return this._state === 'authenticated';\n }\n\n if (normalizedPath === '/.info/serverTimeOffset') {\n return this._serverTimeOffset;\n }\n\n // Unknown .info path - return null\n return null;\n }\n\n /**\n * Subscribe to a .info path.\n * Returns an unsubscribe function.\n */\n private subscribeToInfo(path: string, callback: InfoCallback): () => void {\n const normalizedPath = normalizePath(path) || '/';\n const subscription: InfoSubscription = { path: normalizedPath, callback };\n this.infoSubscriptions.push(subscription);\n\n // Fire initial value immediately\n const value = this.getInfoValue(normalizedPath);\n const snapshot = new DataSnapshot(value, normalizedPath, this);\n // Use setTimeout to ensure callback is called asynchronously (like real subscriptions)\n setTimeout(() => callback(snapshot), 0);\n\n // Return unsubscribe function\n return () => {\n const index = this.infoSubscriptions.indexOf(subscription);\n if (index !== -1) {\n this.infoSubscriptions.splice(index, 1);\n }\n };\n }\n\n /**\n * Fire events for .info path changes.\n */\n private fireInfoEvents(path: string): void {\n const normalizedPath = normalizePath(path) || '/';\n const value = this.getInfoValue(normalizedPath);\n\n for (const sub of this.infoSubscriptions) {\n if (sub.path === normalizedPath) {\n const snapshot = new DataSnapshot(value, normalizedPath, this);\n sub.callback(snapshot);\n }\n }\n }\n\n /**\n * Fire connection state change events to .info/connected subscribers.\n */\n private fireConnectionStateChange(): void {\n this.fireInfoEvents('/.info/connected');\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) {\n return;\n }\n\n // Set up connection promise for reconnect - operations can await this\n this._connectionPromise = this.performConnect(this._databaseId, this._connectOptions, true);\n\n try {\n await this._connectionPromise;\n } catch {\n // performConnect already handles scheduling next attempt on failure\n this._connectionPromise = null;\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** (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 (async for hash computation)\n txOps = await Promise.all(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 * Async because condition operations may require hash computation.\n */\n private async convertToTxOp(op: TransactionOp): Promise<TxOperation> {\n const path = normalizePath(op.path) || '/';\n validatePath(op.path, 'transaction');\n\n switch (op.op) {\n case 'set':\n validateWriteData(op.value, 'transaction');\n return { o: 's', p: path, v: op.value };\n case 'update':\n validateWriteData(op.value, 'transaction');\n return { o: 'u', p: path, v: op.value };\n case 'delete':\n return { o: 'd', p: path };\n case 'condition':\n // Condition values are for comparison, not writing, but still validate\n if (op.value !== undefined && op.value !== null) {\n validateWriteData(op.value, 'transaction condition');\n }\n // Use direct value for primitives, hash for complex objects\n if (isPrimitive(op.value)) {\n return { o: 'c', p: path, v: op.value };\n } else {\n const hash = await hashValue(op.value);\n return { o: 'c', p: path, h: hash };\n }\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 validatePath(path, 'transaction');\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 validateWriteData(value, 'transaction');\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 if (this._state !== 'authenticated') await this.ensureConnected();\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 * Register a callback for auth state changes.\n * Fires when user signs in, signs out, or auth changes.\n * Returns an unsubscribe function.\n */\n onAuthStateChanged(callback: (auth: AuthInfo | null) => void): () => void {\n this.authStateChangedCallbacks.add(callback);\n return () => this.authStateChangedCallbacks.delete(callback);\n }\n\n // ============================================\n // Authentication Management\n // ============================================\n\n /**\n * Sign in with a new auth token while connected.\n * Changes the authenticated user without disconnecting.\n *\n * Note: Some subscriptions may be revoked if the new user doesn't have\n * permission. Listen for 'permission_denied' errors on your subscriptions.\n *\n * @param token - The auth token for the new user\n * @throws Error if not connected (must call connect() first)\n */\n async signIn(token: string): Promise<void> {\n if (this._state !== 'authenticated' && this._state !== 'joined') {\n throw new LarkError('not_connected', 'Must be connected first - call connect()');\n }\n\n // Send auth with new token\n const authRequestId = this.messageQueue.nextRequestId();\n const authMessage: AuthMessage = {\n o: 'au',\n t: token,\n r: authRequestId,\n };\n\n this.send(authMessage);\n\n // Wait for auth complete\n const authResponse = (await this.messageQueue.registerRequest(authRequestId)) as AuthCompleteResponse;\n\n // Update auth state\n this._currentToken = token;\n this._isAnonymous = false;\n this._auth = {\n uid: authResponse.uid || '',\n provider: 'custom',\n token: {},\n };\n\n // Fire auth state change\n this.authStateChangedCallbacks.forEach((cb) => cb(this._auth));\n }\n\n /**\n * Sign out the current user.\n * Reverts to anonymous authentication.\n *\n * Note: Some subscriptions may be revoked if anonymous users don't have\n * permission. Listen for 'permission_denied' errors on your subscriptions.\n */\n async signOut(): Promise<void> {\n if (this._state !== 'authenticated') {\n return; // Already signed out or not connected\n }\n\n // Send unauth message\n const unauthRequestId = this.messageQueue.nextRequestId();\n const unauthMessage: UnauthMessage = {\n o: 'ua',\n r: unauthRequestId,\n };\n\n this.send(unauthMessage);\n\n // Wait for auth complete (server responds with ac even for unauth)\n const authResponse = (await this.messageQueue.registerRequest(unauthRequestId)) as AuthCompleteResponse;\n\n // Update auth state (now anonymous)\n this._currentToken = '';\n this._isAnonymous = true;\n this._auth = {\n uid: authResponse.uid || '',\n provider: 'anonymous',\n token: {},\n };\n\n // Fire auth state change\n this.authStateChangedCallbacks.forEach((cb) => cb(this._auth));\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 // DEBUG: Log all incoming messages\n if (process.env.LARK_DEBUG) {\n console.log('[LARK] <<< SERVER:', JSON.stringify(message, null, 2));\n }\n\n // Handle ping - respond with pong immediately\n if (isPingMessage(message)) {\n this.transport?.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 // Handle subscription revocation due to auth change\n if (message.e === 'permission_denied' && message.sp) {\n const path = message.sp;\n console.warn(`Subscription revoked at ${path}: permission_denied`);\n // Clean up local subscription state\n this.subscriptionManager.handleSubscriptionRevoked(path);\n return;\n }\n\n // Log the error (except for condition_failed which is expected in transactions)\n if (message.e !== 'condition_failed') {\n console.error(`Write failed (${message.e}): ${message.m || message.e}`);\n }\n\n // Remove the pending write from affected Views\n // With dual-cache, this automatically reverts to server truth and fires callbacks if needed\n this.subscriptionManager.handleWriteNack(message.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 // Check if we were fully authenticated (ready to operate)\n const wasAuthenticated = this._state === 'authenticated';\n const wasReconnecting = this._state === 'reconnecting';\n // Also consider partial connection states (connected, joined) as needing callbacks\n const wasPartiallyConnected = this._state === 'connected' || this._state === 'joined';\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 (wasAuthenticated || wasPartiallyConnected) {\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 ((wasAuthenticated || wasPartiallyConnected || 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 (wasAuthenticated || wasPartiallyConnected) {\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 (wasAuthenticated || wasPartiallyConnected) {\n this.disconnectCallbacks.forEach((cb) => cb());\n }\n }\n }\n\n private handleError(error: Error): void {\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.transport || !this.transport.connected) {\n throw new LarkError('not_connected', 'Not connected to database');\n }\n // DEBUG: Log all outgoing messages\n if (process.env.LARK_DEBUG) {\n console.log('[LARK] >>> CLIENT:', JSON.stringify(message, null, 2));\n }\n this.transport.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 if (this._state !== 'authenticated') await this.ensureConnected();\n const normalizedPath = normalizePath(path) || '/';\n\n // Validate data before sending\n validateWriteData(value, normalizedPath);\n validateWriteSize(value, normalizedPath);\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 // Resolve ServerValues locally for optimistic update\n // Get current cached value for increment resolution\n const { value: currentValue } = this.subscriptionManager.getCachedValue(normalizedPath);\n const resolvedValue = resolveServerValuesLocally(value, currentValue);\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, resolvedValue, requestId, 'set');\n\n const message: ClientMessage = {\n o: 's',\n p: normalizedPath,\n v: value, // Send original value with ServerValue placeholders to server\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 if (this._state !== 'authenticated') await this.ensureConnected();\n const normalizedPath = normalizePath(path) || '/';\n\n // Validate each value being updated\n for (const [key, value] of Object.entries(values)) {\n const fullPath = key.startsWith('/') ? key : `${normalizedPath}/${key}`;\n validateWriteData(value, fullPath);\n }\n // Validate total size of the update\n validateWriteSize(values, normalizedPath);\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 // Resolve ServerValues locally for optimistic update\n // For update, we need to resolve each value with its own current value at the appropriate path\n const resolvedValues: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(values)) {\n const fullPath = key.startsWith('/') ? key : `${normalizedPath}/${key}`;\n const { value: currentValue } = this.subscriptionManager.getCachedValue(fullPath);\n resolvedValues[key] = resolveServerValuesLocally(value, currentValue);\n }\n\n // Apply optimistically to local cache and fire events before sending\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, resolvedValues, requestId, 'update');\n\n const message: ClientMessage = {\n o: 'u',\n p: normalizedPath,\n v: values, // Send original value with ServerValue placeholders to server\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 if (this._state !== 'authenticated') await this.ensureConnected();\n const normalizedPath = normalizePath(path) || '/';\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 * When using WebTransport, volatile writes are sent via datagrams (UDP).\n */\n _sendVolatileSet(path: string, value: unknown): void {\n const normalizedPath = normalizePath(path) || '/';\n\n // Validate volatile write size (2 KB limit)\n validateVolatileWriteSize(value, normalizedPath);\n\n // Apply optimistically to local cache (for UI feedback)\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, value, '', 'set');\n\n if (this._state !== 'authenticated' || !this.transport || !this.transport.connected) {\n // Silent fail for volatile writes when not authenticated or disconnected\n return;\n }\n\n // Fire-and-forget: no request ID, no pending tracking, no await\n // Uses sendVolatile which goes via datagrams on WebTransport\n const message: ClientMessage = {\n o: 's',\n p: normalizedPath,\n v: value,\n };\n this.transport.sendVolatile(JSON.stringify(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 // Validate volatile write size (2 KB limit)\n validateVolatileWriteSize(values, normalizedPath);\n\n // Apply optimistically to local cache (for UI feedback)\n this.subscriptionManager.applyOptimisticWrite(normalizedPath, values, '', 'update');\n\n if (this._state !== 'authenticated' || !this.transport || !this.transport.connected) {\n // Silent fail for volatile writes when not authenticated or disconnected\n return;\n }\n\n // Fire-and-forget via datagram on WebTransport\n const message: ClientMessage = {\n o: 'u',\n p: normalizedPath,\n v: values,\n };\n this.transport.sendVolatile(JSON.stringify(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 if (this._state !== 'authenticated' || !this.transport || !this.transport.connected) {\n // Silent fail for volatile writes when not authenticated or disconnected\n return;\n }\n\n // Fire-and-forget via datagram on WebTransport\n const message: ClientMessage = {\n o: 'd',\n p: normalizedPath,\n };\n this.transport.sendVolatile(JSON.stringify(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 // Handle .info paths locally (they're synthetic, not on the server)\n if (this.isInfoPath(normalizedPath)) {\n const value = this.getInfoValue(normalizedPath);\n return new DataSnapshot(value, path, this);\n }\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 if (this._state !== 'authenticated') await this.ensureConnected();\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, { queryParams: query ?? null });\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 if (this._state !== 'authenticated') await this.ensureConnected();\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 Get a cached value from the subscription manager.\n * Used for optimistic writes where we need the current value without a network fetch.\n */\n _getCachedValue(path: string): { value: unknown; found: boolean } {\n const normalizedPath = normalizePath(path) || '/';\n return this.subscriptionManager.getCachedValue(normalizedPath);\n }\n\n /**\n * @internal Send a subscribe message to server.\n * Includes tag for non-default queries to enable proper event routing.\n */\n private async sendSubscribeMessage(path: string, queryParams?: QueryParams, tag?: number): Promise<void> {\n if (this._state !== 'authenticated') await this.ensureConnected();\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'sb',\n p: normalizePath(path) || '/',\n r: requestId,\n ...queryParams,\n ...(tag !== undefined ? { tag } : {}),\n };\n this.send(message);\n await this.messageQueue.registerRequest(requestId);\n }\n\n /**\n * @internal Send an unsubscribe message to server.\n * Includes query params and tag so server can identify which specific subscription to remove.\n */\n private async sendUnsubscribeMessage(path: string, queryParams?: QueryParams, tag?: number): Promise<void> {\n if (this._state !== 'authenticated') await this.ensureConnected();\n const requestId = this.messageQueue.nextRequestId();\n const message: ClientMessage = {\n o: 'us',\n p: normalizePath(path) || '/',\n r: requestId,\n ...queryParams,\n ...(tag !== undefined ? { tag } : {}),\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 orderedKeys?: string[]\n ): DataSnapshot {\n return new DataSnapshot(value, path, this, {\n volatile,\n serverTimestamp: serverTimestamp ?? null,\n orderedKeys: orderedKeys ?? 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, queryIdentifier?: string): () => void {\n // Handle .info paths locally (not sent to server)\n if (this.isInfoPath(path)) {\n return this.subscribeToInfo(path, callback);\n }\n return this.subscriptionManager.subscribe(path, eventType, callback, queryParams, queryIdentifier);\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":["WebSocketNode","view","canonicalize","actualValue","value"]}
|