@topgunbuild/client 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +329 -25
- package/dist/index.d.ts +329 -25
- package/dist/index.js +800 -105
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +794 -103
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -9
- package/LICENSE +0 -97
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/SyncEngine.ts","../src/utils/logger.ts","../src/SyncState.ts","../src/SyncStateMachine.ts","../src/BackpressureConfig.ts","../src/ConflictResolverClient.ts","../src/sync/WebSocketManager.ts","../src/errors/BackpressureError.ts","../src/sync/BackpressureController.ts","../src/sync/QueryManager.ts","../src/sync/TopicManager.ts","../src/sync/LockManager.ts","../src/sync/WriteConcernManager.ts","../src/sync/CounterManager.ts","../src/sync/EntryProcessorClient.ts","../src/sync/SearchClient.ts","../src/sync/MerkleSyncHandler.ts","../src/sync/ORMapSyncHandler.ts","../src/sync/MessageRouter.ts","../src/sync/ClientMessageHandlers.ts","../src/TopGunClient.ts","../src/utils/deepEqual.ts","../src/ChangeTracker.ts","../src/QueryHandle.ts","../src/DistributedLock.ts","../src/TopicHandle.ts","../src/PNCounterHandle.ts","../src/EventJournalReader.ts","../src/SearchHandle.ts","../src/HybridQueryHandle.ts","../src/cluster/ClusterClient.ts","../src/cluster/ConnectionPool.ts","../src/cluster/PartitionRouter.ts","../src/connection/SingleServerProvider.ts","../src/adapters/IDBAdapter.ts","../src/TopGun.ts","../src/crypto/EncryptionManager.ts","../src/adapters/EncryptedStorageAdapter.ts","../src/index.ts"],"sourcesContent":["import { HLC, LWWMap, ORMap, deserialize, evaluatePredicate } from '@topgunbuild/core';\nimport type { EntryProcessorDef, EntryProcessorResult, SearchOptions } from '@topgunbuild/core';\nimport type { LWWRecord, ORMapRecord, Timestamp } from '@topgunbuild/core';\nimport type {\n AuthFailMessage,\n OpAckMessage,\n QueryRespMessage,\n QueryUpdateMessage,\n ServerEventMessage,\n ServerBatchEventMessage,\n GcPruneMessage,\n HybridQueryRespPayload,\n HybridQueryDeltaPayload,\n BatchMessage,\n} from '@topgunbuild/core';\nimport type { IStorageAdapter } from './IStorageAdapter';\nimport { QueryHandle } from './QueryHandle';\nimport type { QueryFilter } from './QueryHandle';\nimport type { HybridQueryHandle, HybridQueryFilter } from './HybridQueryHandle';\nimport { TopicHandle } from './TopicHandle';\nimport { logger } from './utils/logger';\nimport { SyncStateMachine, StateChangeEvent } from './SyncStateMachine';\nimport { SyncState } from './SyncState';\nimport type {\n BackpressureConfig,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n} from './BackpressureConfig';\nimport { DEFAULT_BACKPRESSURE_CONFIG } from './BackpressureConfig';\nimport type { IConnectionProvider } from './types';\nimport { ConflictResolverClient } from './ConflictResolverClient';\nimport { WebSocketManager, BackpressureController, QueryManager, TopicManager, LockManager, WriteConcernManager, CounterManager, EntryProcessorClient, SearchClient, MerkleSyncHandler, ORMapSyncHandler, MessageRouter, registerClientMessageHandlers } from './sync';\nimport type { SearchResult, IMessageRouter } from './sync';\n\n// Re-export SearchResult from sync module for backwards compatibility\nexport type { SearchResult } from './sync';\n\nexport interface OpLogEntry {\n id: string; // Unique ID for the operation\n mapName: string;\n opType: 'PUT' | 'REMOVE' | 'OR_ADD' | 'OR_REMOVE';\n key: string;\n record?: LWWRecord<any>; // LWW Put/Remove (Remove has null value)\n orRecord?: ORMapRecord<any>; // ORMap Add\n orTag?: string; // ORMap Remove (Tombstone tag)\n timestamp: Timestamp; // HLC timestamp of the operation\n synced: boolean; // True if this operation has been successfully pushed to the server\n}\n\nexport interface HeartbeatConfig {\n intervalMs: number; // Default: 5000 (5 seconds)\n timeoutMs: number; // Default: 15000 (15 seconds)\n enabled: boolean; // Default: true\n}\n\nexport interface BackoffConfig {\n /** Initial delay in milliseconds (default: 1000) */\n initialDelayMs: number;\n /** Maximum delay in milliseconds (default: 30000) */\n maxDelayMs: number;\n /** Multiplier for exponential backoff (default: 2) */\n multiplier: number;\n /** Whether to add random jitter to delay (default: true) */\n jitter: boolean;\n /** Maximum number of retry attempts before entering ERROR state (default: 10) */\n maxRetries: number;\n}\n\nexport interface TopicQueueConfig {\n /** Maximum queued topic messages when offline (default: 100) */\n maxSize: number;\n /** Strategy when queue is full: 'drop-oldest' | 'drop-newest' (default: 'drop-oldest') */\n strategy: 'drop-oldest' | 'drop-newest';\n}\n\nconst DEFAULT_TOPIC_QUEUE_CONFIG: TopicQueueConfig = {\n maxSize: 100,\n strategy: 'drop-oldest',\n};\n\nexport interface SyncEngineConfig {\n nodeId: string;\n /** Connection provider for WebSocket connections */\n connectionProvider: IConnectionProvider;\n storageAdapter: IStorageAdapter;\n reconnectInterval?: number;\n heartbeat?: Partial<HeartbeatConfig>;\n backoff?: Partial<BackoffConfig>;\n backpressure?: Partial<BackpressureConfig>;\n /** Configuration for offline topic message queue */\n topicQueue?: Partial<TopicQueueConfig>;\n}\n\nconst DEFAULT_BACKOFF_CONFIG: BackoffConfig = {\n initialDelayMs: 1000,\n maxDelayMs: 30000,\n multiplier: 2,\n jitter: true,\n maxRetries: 10,\n};\n\nexport class SyncEngine {\n private readonly nodeId: string;\n private readonly storageAdapter: IStorageAdapter;\n private readonly hlc: HLC;\n private readonly stateMachine: SyncStateMachine;\n private readonly heartbeatConfig: HeartbeatConfig;\n private readonly backoffConfig: BackoffConfig;\n\n // WebSocketManager handles all connection/websocket operations\n private readonly webSocketManager: WebSocketManager;\n\n // QueryManager handles all query operations\n private readonly queryManager: QueryManager;\n\n // TopicManager handles all topic (pub/sub) operations\n private readonly topicManager: TopicManager;\n\n // LockManager handles distributed lock operations\n private readonly lockManager: LockManager;\n\n // WriteConcernManager handles write concern tracking\n private readonly writeConcernManager: WriteConcernManager;\n\n // CounterManager handles PN counter operations\n private readonly counterManager: CounterManager;\n\n // EntryProcessorClient handles entry processor operations\n private readonly entryProcessorClient: EntryProcessorClient;\n\n // SearchClient handles full-text search operations\n private readonly searchClient: SearchClient;\n\n // MerkleSyncHandler handles LWWMap sync protocol messages\n private readonly merkleSyncHandler: MerkleSyncHandler;\n\n // ORMapSyncHandler handles ORMap sync protocol messages\n private readonly orMapSyncHandler: ORMapSyncHandler;\n\n // MessageRouter handles type-based message routing\n private readonly messageRouter: IMessageRouter;\n\n private opLog: OpLogEntry[] = [];\n private maps: Map<string, LWWMap<any, any> | ORMap<any, any>> = new Map();\n private lastSyncTimestamp: number = 0;\n private authToken: string | null = null;\n private tokenProvider: (() => Promise<string | null>) | null = null;\n\n // BackpressureController handles all backpressure operations\n private readonly backpressureConfig: BackpressureConfig;\n private readonly backpressureController: BackpressureController;\n\n // Conflict Resolver client\n private readonly conflictResolverClient: ConflictResolverClient;\n\n constructor(config: SyncEngineConfig) {\n // Validate config: connectionProvider is required\n if (!config.connectionProvider) {\n throw new Error('SyncEngine requires connectionProvider');\n }\n\n this.nodeId = config.nodeId;\n this.storageAdapter = config.storageAdapter;\n this.hlc = new HLC(this.nodeId);\n\n // Initialize state machine\n this.stateMachine = new SyncStateMachine();\n\n // Initialize heartbeat config with defaults\n this.heartbeatConfig = {\n intervalMs: config.heartbeat?.intervalMs ?? 5000,\n timeoutMs: config.heartbeat?.timeoutMs ?? 15000,\n enabled: config.heartbeat?.enabled ?? true,\n };\n\n // Merge backoff config with defaults\n this.backoffConfig = {\n ...DEFAULT_BACKOFF_CONFIG,\n ...config.backoff,\n };\n\n // Merge backpressure config with defaults\n this.backpressureConfig = {\n ...DEFAULT_BACKPRESSURE_CONFIG,\n ...config.backpressure,\n };\n\n // Initialize BackpressureController with shared opLog reference\n this.backpressureController = new BackpressureController({\n config: this.backpressureConfig,\n opLog: this.opLog, // Pass reference, not copy\n });\n\n // Merge topic queue config with defaults to ensure consistent backpressure behavior\n const topicQueueConfig: TopicQueueConfig = {\n ...DEFAULT_TOPIC_QUEUE_CONFIG,\n ...config.topicQueue,\n };\n\n // Initialize WebSocketManager with callbacks to SyncEngine\n this.webSocketManager = new WebSocketManager({\n connectionProvider: config.connectionProvider,\n stateMachine: this.stateMachine,\n backoffConfig: this.backoffConfig,\n heartbeatConfig: this.heartbeatConfig,\n onMessage: (msg) => this.handleServerMessage(msg),\n onConnected: () => this.handleConnectionEstablished(),\n onDisconnected: () => this.handleConnectionLost(),\n onReconnected: () => this.handleReconnection(),\n });\n\n // Initialize QueryManager with callbacks\n this.queryManager = new QueryManager({\n storageAdapter: this.storageAdapter,\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize TopicManager with callbacks\n this.topicManager = new TopicManager({\n topicQueueConfig,\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize LockManager with callbacks\n this.lockManager = new LockManager({\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n isAuthenticated: () => this.isAuthenticated(),\n isOnline: () => this.isOnline(),\n });\n\n // Initialize WriteConcernManager for distributed PN counter operations\n this.writeConcernManager = new WriteConcernManager({\n defaultTimeout: 5000,\n });\n\n // Initialize CounterManager for distributed PN counter operations\n this.counterManager = new CounterManager({\n sendMessage: (msg) => this.sendMessage(msg),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize EntryProcessorClient for server-side entry processing\n this.entryProcessorClient = new EntryProcessorClient({\n sendMessage: (msg, key) => key !== undefined ? this.sendMessage(msg, key) : this.sendMessage(msg),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize SearchClient for full-text search operations\n this.searchClient = new SearchClient({\n sendMessage: (msg) => this.sendMessage(msg),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize MerkleSyncHandler for LWWMap sync protocol\n this.merkleSyncHandler = new MerkleSyncHandler({\n getMap: (name) => this.maps.get(name),\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n storageAdapter: this.storageAdapter,\n hlc: this.hlc,\n onTimestampUpdate: async (ts) => {\n this.hlc.update(ts);\n this.lastSyncTimestamp = ts.millis;\n await this.saveOpLog();\n },\n resetMap: (name) => this.resetMap(name),\n });\n\n // Initialize ORMapSyncHandler for ORMap sync protocol\n this.orMapSyncHandler = new ORMapSyncHandler({\n getMap: (name) => this.maps.get(name),\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n hlc: this.hlc,\n onTimestampUpdate: async (ts) => {\n this.hlc.update(ts);\n this.lastSyncTimestamp = ts.millis;\n await this.saveOpLog();\n },\n });\n\n // Initialize Conflict Resolver client\n this.conflictResolverClient = new ConflictResolverClient(this);\n\n // Initialize MessageRouter and register all handlers\n this.messageRouter = new MessageRouter({\n onUnhandled: (msg) => logger.warn({ type: msg?.type }, 'Unhandled message type'),\n });\n registerClientMessageHandlers(\n this.messageRouter,\n {\n sendAuth: () => this.sendAuth(),\n handleAuthAck: () => this.handleAuthAck(),\n handleAuthFail: (msg) => this.handleAuthFail(msg),\n handleOpAck: (msg) => this.handleOpAck(msg),\n handleQueryResp: (msg) => this.handleQueryResp(msg),\n handleQueryUpdate: (msg) => this.handleQueryUpdate(msg),\n handleServerEvent: (msg) => this.handleServerEvent(msg),\n handleServerBatchEvent: (msg) => this.handleServerBatchEvent(msg),\n handleGcPrune: (msg) => this.handleGcPrune(msg),\n handleHybridQueryResponse: (payload) => this.handleHybridQueryResponse(payload),\n handleHybridQueryDelta: (payload) => this.handleHybridQueryDelta(payload),\n },\n {\n topicManager: this.topicManager,\n lockManager: this.lockManager,\n counterManager: this.counterManager,\n entryProcessorClient: this.entryProcessorClient,\n conflictResolverClient: this.conflictResolverClient,\n searchClient: this.searchClient,\n merkleSyncHandler: this.merkleSyncHandler,\n orMapSyncHandler: this.orMapSyncHandler,\n }\n );\n\n // Start connection\n this.webSocketManager.connect();\n\n this.loadOpLog();\n }\n\n // ============================================\n // Connection Callbacks (from WebSocketManager)\n // ============================================\n\n /**\n * Called when connection is established (initial or reconnect).\n */\n private handleConnectionEstablished(): void {\n if (this.authToken || this.tokenProvider) {\n logger.info('Connection established. Sending auth...');\n this.stateMachine.transition(SyncState.AUTHENTICATING);\n this.sendAuth();\n } else {\n logger.info('Connection established. Waiting for auth token...');\n this.stateMachine.transition(SyncState.AUTHENTICATING);\n }\n }\n\n /**\n * Called when connection is lost.\n */\n private handleConnectionLost(): void {\n // WebSocketManager already stopped heartbeat and transitioned state\n // SyncEngine can do additional cleanup if needed\n }\n\n /**\n * Called when reconnection succeeds.\n */\n private handleReconnection(): void {\n if (this.authToken || this.tokenProvider) {\n this.stateMachine.transition(SyncState.AUTHENTICATING);\n this.sendAuth();\n }\n }\n\n // ============================================\n // State Machine Public API\n // ============================================\n\n /**\n * Get the current connection state\n */\n getConnectionState(): SyncState {\n return this.stateMachine.getState();\n }\n\n /**\n * Subscribe to connection state changes\n * @returns Unsubscribe function\n */\n onConnectionStateChange(listener: (event: StateChangeEvent) => void): () => void {\n return this.stateMachine.onStateChange(listener);\n }\n\n /**\n * Get state machine history for debugging\n */\n getStateHistory(limit?: number): StateChangeEvent[] {\n return this.stateMachine.getHistory(limit);\n }\n\n // ============================================\n // Internal State Helpers (replace boolean flags)\n // ============================================\n\n /**\n * Check if WebSocket is connected (but may not be authenticated yet)\n */\n private isOnline(): boolean {\n const state = this.stateMachine.getState();\n return (\n state === SyncState.CONNECTING ||\n state === SyncState.AUTHENTICATING ||\n state === SyncState.SYNCING ||\n state === SyncState.CONNECTED\n );\n }\n\n /**\n * Check if fully authenticated and ready for operations\n */\n private isAuthenticated(): boolean {\n const state = this.stateMachine.getState();\n return state === SyncState.SYNCING || state === SyncState.CONNECTED;\n }\n\n /**\n * Check if fully connected and synced\n */\n private isConnected(): boolean {\n return this.stateMachine.getState() === SyncState.CONNECTED;\n }\n\n // ============================================\n // Message Sending (delegates to WebSocketManager)\n // ============================================\n\n /**\n * Send a message through the current connection.\n * Delegates to WebSocketManager.\n */\n private sendMessage(message: unknown, key?: string): boolean {\n return this.webSocketManager.sendMessage(message, key);\n }\n\n // ============================================\n // Op Log Management\n // ============================================\n\n private async loadOpLog(): Promise<void> {\n const storedTimestamp = await this.storageAdapter.getMeta('lastSyncTimestamp');\n if (storedTimestamp) {\n this.lastSyncTimestamp = storedTimestamp;\n }\n\n const pendingOps = await this.storageAdapter.getPendingOps();\n // Clear and push to existing array (preserves BackpressureController reference)\n this.opLog.length = 0;\n for (const op of pendingOps) {\n this.opLog.push({\n ...op,\n id: String(op.id),\n synced: false,\n } as unknown as OpLogEntry);\n }\n\n if (this.opLog.length > 0) {\n logger.info({ count: this.opLog.length }, 'Loaded pending operations from local storage');\n }\n }\n\n private async saveOpLog(): Promise<void> {\n await this.storageAdapter.setMeta('lastSyncTimestamp', this.lastSyncTimestamp);\n }\n\n public registerMap(mapName: string, map: LWWMap<any, any> | ORMap<any, any>): void {\n this.maps.set(mapName, map);\n }\n\n public async recordOperation(\n mapName: string,\n opType: 'PUT' | 'REMOVE' | 'OR_ADD' | 'OR_REMOVE',\n key: string,\n data: { record?: LWWRecord<any>; orRecord?: ORMapRecord<any>; orTag?: string; timestamp: Timestamp }\n ): Promise<string> {\n // Check backpressure before adding new operation (delegates to BackpressureController)\n await this.backpressureController.checkBackpressure();\n\n const opLogEntry: Omit<OpLogEntry, 'id'> & { id?: string } = {\n mapName,\n opType,\n key,\n record: data.record,\n orRecord: data.orRecord,\n orTag: data.orTag,\n timestamp: data.timestamp,\n synced: false,\n };\n\n const id = await this.storageAdapter.appendOpLog(opLogEntry as any);\n opLogEntry.id = String(id);\n\n this.opLog.push(opLogEntry as OpLogEntry);\n\n // Check high water mark after adding operation (delegates to BackpressureController)\n this.backpressureController.checkHighWaterMark();\n\n if (this.isAuthenticated()) {\n this.syncPendingOperations();\n }\n\n return opLogEntry.id;\n }\n\n private syncPendingOperations(): void {\n const pending = this.opLog.filter(op => !op.synced);\n if (pending.length === 0) return;\n\n logger.info({ count: pending.length }, 'Syncing pending operations');\n\n this.sendMessage({\n type: 'OP_BATCH',\n payload: {\n ops: pending\n }\n });\n }\n\n private startMerkleSync(): void {\n for (const [mapName, map] of this.maps) {\n if (map instanceof LWWMap) {\n this.merkleSyncHandler.sendSyncInit(mapName, this.lastSyncTimestamp);\n } else if (map instanceof ORMap) {\n this.orMapSyncHandler.sendSyncInit(mapName, this.lastSyncTimestamp);\n }\n }\n }\n\n public setAuthToken(token: string): void {\n this.authToken = token;\n this.tokenProvider = null;\n\n const state = this.stateMachine.getState();\n if (state === SyncState.AUTHENTICATING || state === SyncState.CONNECTING) {\n // If we are already connected (e.g. waiting for token), send it now\n this.sendAuth();\n } else if (state === SyncState.BACKOFF || state === SyncState.DISCONNECTED) {\n // Force immediate reconnect if we were waiting for retry timer\n logger.info('Auth token set during backoff/disconnect. Reconnecting immediately.');\n this.webSocketManager.clearReconnectTimer();\n // Reset backoff since user provided new credentials\n this.webSocketManager.resetBackoff();\n this.webSocketManager.connect();\n }\n }\n\n public setTokenProvider(provider: () => Promise<string | null>): void {\n this.tokenProvider = provider;\n const state = this.stateMachine.getState();\n if (state === SyncState.AUTHENTICATING) {\n this.sendAuth();\n }\n }\n\n private async sendAuth(): Promise<void> {\n if (this.tokenProvider) {\n try {\n const token = await this.tokenProvider();\n if (token) {\n this.authToken = token;\n }\n } catch (err) {\n logger.error({ err }, 'Failed to get token from provider');\n return;\n }\n }\n\n const token = this.authToken;\n if (!token) return; // Don't send anonymous auth anymore\n\n this.sendMessage({\n type: 'AUTH',\n token\n });\n }\n\n /**\n * Subscribe to a standard query.\n * Delegates to QueryManager.\n */\n public subscribeToQuery(query: QueryHandle<any>): void {\n this.queryManager.subscribeToQuery(query);\n }\n\n /**\n * Subscribe to a topic.\n * Delegates to TopicManager.\n */\n public subscribeToTopic(topic: string, handle: TopicHandle): void {\n this.topicManager.subscribeToTopic(topic, handle);\n }\n\n /**\n * Unsubscribe from a topic.\n * Delegates to TopicManager.\n */\n public unsubscribeFromTopic(topic: string): void {\n this.topicManager.unsubscribeFromTopic(topic);\n }\n\n /**\n * Publish a message to a topic.\n * Delegates to TopicManager.\n */\n public publishTopic(topic: string, data: unknown): void {\n this.topicManager.publishTopic(topic, data);\n }\n\n /**\n * Get topic queue status.\n * Delegates to TopicManager.\n */\n public getTopicQueueStatus(): { size: number; maxSize: number } {\n return this.topicManager.getTopicQueueStatus();\n }\n\n /**\n * Executes a query against local storage immediately.\n * Delegates to QueryManager.\n */\n public async runLocalQuery(mapName: string, filter: QueryFilter): Promise<{ key: string; value: any }[]> {\n return this.queryManager.runLocalQuery(mapName, filter);\n }\n\n /**\n * Unsubscribe from a query.\n * Delegates to QueryManager.\n */\n public unsubscribeFromQuery(queryId: string): void {\n this.queryManager.unsubscribeFromQuery(queryId);\n }\n\n /**\n * Request a distributed lock.\n * Delegates to LockManager.\n */\n public requestLock(name: string, requestId: string, ttl: number): Promise<{ fencingToken: number }> {\n return this.lockManager.requestLock(name, requestId, ttl);\n }\n\n /**\n * Release a distributed lock.\n * Delegates to LockManager.\n */\n public releaseLock(name: string, requestId: string, fencingToken: number): Promise<boolean> {\n return this.lockManager.releaseLock(name, requestId, fencingToken);\n }\n\n private async handleServerMessage(message: { type: string; payload?: unknown; timestamp?: Timestamp }): Promise<void> {\n // Emit to generic listeners (used by EventJournalReader)\n this.emitMessage(message);\n\n // Handle BATCH specially (recursive unbatch)\n if (message.type === 'BATCH') {\n await this.handleBatch(message as BatchMessage);\n return;\n }\n\n // Route to registered handler\n await this.messageRouter.route(message);\n\n // Update HLC if message has timestamp\n if (message.timestamp) {\n this.hlc.update(message.timestamp);\n this.lastSyncTimestamp = message.timestamp.millis;\n await this.saveOpLog();\n }\n }\n\n // ============================================\n // Message Handler Helpers (extracted from switch)\n // ============================================\n\n private async handleBatch(message: BatchMessage): Promise<void> {\n // Unbatch and process each message\n // Format: [4 bytes: count][4 bytes: len1][msg1][4 bytes: len2][msg2]...\n const batchData = message.data;\n const view = new DataView(batchData.buffer, batchData.byteOffset, batchData.byteLength);\n let offset = 0;\n\n const count = view.getUint32(offset, true);\n offset += 4;\n\n for (let i = 0; i < count; i++) {\n const msgLen = view.getUint32(offset, true);\n offset += 4;\n\n const msgData = batchData.slice(offset, offset + msgLen);\n offset += msgLen;\n\n const innerMsg = deserialize(msgData) as { type: string; payload?: unknown; timestamp?: Timestamp };\n await this.handleServerMessage(innerMsg);\n }\n }\n\n private handleAuthAck(): void {\n logger.info('Authenticated successfully');\n const wasAuthenticated = this.isAuthenticated();\n\n // Transition to SYNCING state\n this.stateMachine.transition(SyncState.SYNCING);\n\n // Reset backoff on successful auth\n this.webSocketManager.resetBackoff();\n\n this.syncPendingOperations();\n\n // Flush any queued topic messages from offline period (delegates to TopicManager)\n this.topicManager.flushTopicQueue();\n\n // Only re-subscribe on first authentication to prevent UI flickering\n if (!wasAuthenticated) {\n this.webSocketManager.startHeartbeat();\n this.startMerkleSync();\n // Re-subscribe all queries via QueryManager\n this.queryManager.resubscribeAll();\n // Re-subscribe topics via TopicManager\n this.topicManager.resubscribeAll();\n }\n\n // After initial sync setup, transition to CONNECTED\n // In a real implementation, you might wait for SYNC_COMPLETE message\n this.stateMachine.transition(SyncState.CONNECTED);\n }\n\n private handleAuthFail(message: AuthFailMessage): void {\n logger.error({ error: message.error }, 'Authentication failed');\n this.authToken = null; // Clear invalid token\n // Stay in AUTHENTICATING or go to ERROR depending on severity\n // For now, let the connection close naturally or retry with new token\n }\n\n private handleOpAck(message: OpAckMessage): void {\n const { lastId, achievedLevel, results } = message.payload;\n logger.info({ lastId, achievedLevel, hasResults: !!results }, 'Received ACK for ops');\n\n // Handle per-operation results if available\n if (results && Array.isArray(results)) {\n for (const result of results) {\n const op = this.opLog.find(o => o.id === result.opId);\n if (op && !op.synced) {\n op.synced = true;\n logger.debug({ opId: result.opId, achievedLevel: result.achievedLevel, success: result.success }, 'Op ACK with Write Concern');\n }\n // Resolve pending Write Concern promise if exists (delegates to WriteConcernManager)\n this.writeConcernManager.resolveWriteConcernPromise(result.opId, result);\n }\n }\n\n // Backwards compatible: mark all ops up to lastId as synced\n let maxSyncedId = -1;\n let ackedCount = 0;\n this.opLog.forEach(op => {\n if (op.id && op.id <= lastId) {\n if (!op.synced) {\n ackedCount++;\n }\n op.synced = true;\n const idNum = parseInt(op.id, 10);\n if (!isNaN(idNum) && idNum > maxSyncedId) {\n maxSyncedId = idNum;\n }\n }\n });\n if (maxSyncedId !== -1) {\n this.storageAdapter.markOpsSynced(maxSyncedId).catch(err => logger.error({ err }, 'Failed to mark ops synced'));\n }\n // Check low water mark after ACKs reduce pending count (delegates to BackpressureController)\n if (ackedCount > 0) {\n this.backpressureController.checkLowWaterMark();\n }\n }\n\n private handleQueryResp(message: QueryRespMessage): void {\n const { queryId, results, nextCursor, hasMore, cursorStatus } = message.payload;\n const query = this.queryManager.getQueries().get(queryId);\n if (query) {\n query.onResult(results, 'server');\n query.updatePaginationInfo({ nextCursor, hasMore, cursorStatus });\n }\n }\n\n private handleQueryUpdate(message: QueryUpdateMessage): void {\n const { queryId, key, value, type } = message.payload;\n const query = this.queryManager.getQueries().get(queryId);\n if (query) {\n query.onUpdate(key, type === 'REMOVE' ? null : value);\n }\n }\n\n private async handleServerEvent(message: ServerEventMessage): Promise<void> {\n // Modified to support ORMap\n const { mapName, eventType, key, record, orRecord, orTag } = message.payload;\n await this.applyServerEvent(mapName, eventType, key, record, orRecord, orTag);\n }\n\n private async handleServerBatchEvent(message: ServerBatchEventMessage): Promise<void> {\n // === OPTIMIZATION: Batch event processing ===\n // Server sends multiple events in a single message for efficiency\n const { events } = message.payload;\n for (const event of events) {\n await this.applyServerEvent(\n event.mapName,\n event.eventType,\n event.key,\n event.record,\n event.orRecord,\n event.orTag\n );\n }\n }\n\n private async handleGcPrune(message: GcPruneMessage): Promise<void> {\n const { olderThan } = message.payload;\n logger.info({ olderThan: olderThan.millis }, 'Received GC_PRUNE request');\n\n for (const [name, map] of this.maps) {\n if (map instanceof LWWMap) {\n const removedKeys = map.prune(olderThan);\n for (const key of removedKeys) {\n await this.storageAdapter.remove(`${name}:${key}`);\n }\n if (removedKeys.length > 0) {\n logger.info({ mapName: name, count: removedKeys.length }, 'Pruned tombstones from LWWMap');\n }\n } else if (map instanceof ORMap) {\n const removedTags = map.prune(olderThan);\n if (removedTags.length > 0) {\n logger.info({ mapName: name, count: removedTags.length }, 'Pruned tombstones from ORMap');\n }\n }\n }\n }\n\n public getHLC(): HLC {\n return this.hlc;\n }\n\n /**\n * Helper method to apply a single server event to the local map.\n * Used by both SERVER_EVENT and SERVER_BATCH_EVENT handlers.\n */\n private async applyServerEvent(\n mapName: string,\n eventType: 'PUT' | 'REMOVE' | 'OR_ADD' | 'OR_REMOVE',\n key: string,\n record?: LWWRecord<unknown>,\n orRecord?: ORMapRecord<unknown>,\n orTag?: string\n ): Promise<void> {\n const localMap = this.maps.get(mapName);\n if (localMap) {\n if (localMap instanceof LWWMap && record) {\n localMap.merge(key, record);\n await this.storageAdapter.put(`${mapName}:${key}`, record);\n } else if (localMap instanceof ORMap) {\n if (eventType === 'OR_ADD' && orRecord) {\n localMap.apply(key, orRecord);\n // We need to store ORMap records differently in storageAdapter or use a convention\n // For now, skipping persistent storage update for ORMap in this example\n } else if (eventType === 'OR_REMOVE' && orTag) {\n localMap.applyTombstone(orTag);\n }\n }\n }\n }\n\n /**\n * Closes the WebSocket connection and cleans up resources.\n */\n public close(): void {\n this.webSocketManager.close();\n\n // Cancel pending Write Concern promises (delegates to WriteConcernManager)\n this.writeConcernManager.cancelAllWriteConcernPromises(new Error('SyncEngine closed'));\n\n // Clean up CounterManager\n this.counterManager.close();\n\n // Clean up EntryProcessorClient\n this.entryProcessorClient.close(new Error('SyncEngine closed'));\n\n // Clean up SearchClient\n this.searchClient.close(new Error('SyncEngine closed'));\n\n this.stateMachine.transition(SyncState.DISCONNECTED);\n logger.info('SyncEngine closed');\n }\n\n /**\n * Reset the state machine and connection.\n * Use after fatal errors to start fresh.\n */\n public resetConnection(): void {\n this.close();\n this.stateMachine.reset();\n this.webSocketManager.reset();\n this.webSocketManager.connect();\n }\n\n // ============================================\n // Failover Support Methods\n // ============================================\n\n /**\n * Wait for a partition map update from the connection provider.\n * Used when an operation fails with NOT_OWNER error and needs\n * to wait for an updated partition map before retrying.\n *\n * @param timeoutMs - Maximum time to wait (default: 5000ms)\n * @returns Promise that resolves when partition map is updated or times out\n */\n public waitForPartitionMapUpdate(timeoutMs: number = 5000): Promise<void> {\n return new Promise((resolve) => {\n const timeout = setTimeout(resolve, timeoutMs);\n const connectionProvider = this.webSocketManager.getConnectionProvider();\n\n const handler = () => {\n clearTimeout(timeout);\n connectionProvider.off('partitionMapUpdated', handler);\n resolve();\n };\n\n connectionProvider.on('partitionMapUpdated', handler);\n });\n }\n\n /**\n * Wait for the connection to be available.\n * Used when an operation fails due to connection issues and needs\n * to wait for reconnection before retrying.\n *\n * @param timeoutMs - Maximum time to wait (default: 10000ms)\n * @returns Promise that resolves when connected or rejects on timeout\n */\n public waitForConnection(timeoutMs: number = 10000): Promise<void> {\n return new Promise((resolve, reject) => {\n const connectionProvider = this.webSocketManager.getConnectionProvider();\n\n // If already connected, resolve immediately\n if (connectionProvider.isConnected()) {\n resolve();\n return;\n }\n\n const timeout = setTimeout(() => {\n connectionProvider.off('connected', handler);\n reject(new Error('Connection timeout waiting for reconnection'));\n }, timeoutMs);\n\n const handler = () => {\n clearTimeout(timeout);\n connectionProvider.off('connected', handler);\n resolve();\n };\n\n connectionProvider.on('connected', handler);\n });\n }\n\n /**\n * Wait for a specific sync state.\n * Useful for waiting until fully connected and synced.\n *\n * @param targetState - The state to wait for\n * @param timeoutMs - Maximum time to wait (default: 30000ms)\n * @returns Promise that resolves when state is reached or rejects on timeout\n */\n public waitForState(targetState: SyncState, timeoutMs: number = 30000): Promise<void> {\n return new Promise((resolve, reject) => {\n // If already in target state, resolve immediately\n if (this.stateMachine.getState() === targetState) {\n resolve();\n return;\n }\n\n const timeout = setTimeout(() => {\n unsubscribe();\n reject(new Error(`Timeout waiting for state ${targetState}`));\n }, timeoutMs);\n\n const unsubscribe = this.stateMachine.onStateChange((event) => {\n if (event.to === targetState) {\n clearTimeout(timeout);\n unsubscribe();\n resolve();\n }\n });\n });\n }\n\n /**\n * Check if the connection provider is connected.\n * Convenience method for failover logic.\n */\n public isProviderConnected(): boolean {\n return this.webSocketManager.getConnectionProvider().isConnected();\n }\n\n /**\n * Get the connection provider for direct access.\n * Use with caution - prefer using SyncEngine methods.\n */\n public getConnectionProvider(): IConnectionProvider {\n return this.webSocketManager.getConnectionProvider();\n }\n\n private async resetMap(mapName: string): Promise<void> {\n const map = this.maps.get(mapName);\n if (map) {\n // Clear memory\n if (map instanceof LWWMap) {\n map.clear();\n } else if (map instanceof ORMap) {\n map.clear();\n }\n }\n\n // Clear storage\n const allKeys = await this.storageAdapter.getAllKeys();\n const mapKeys = allKeys.filter(k => k.startsWith(mapName + ':'));\n for (const key of mapKeys) {\n await this.storageAdapter.remove(key);\n }\n logger.info({ mapName, removedStorageCount: mapKeys.length }, 'Reset map: Cleared memory and storage');\n }\n\n // ============ Heartbeat Methods (delegate to WebSocketManager) ============\n\n /**\n * Returns the last measured round-trip time in milliseconds.\n * Returns null if no PONG has been received yet.\n */\n public getLastRoundTripTime(): number | null {\n return this.webSocketManager.getLastRoundTripTime();\n }\n\n /**\n * Returns true if the connection is considered healthy based on heartbeat.\n * A connection is healthy if it's online, authenticated, and has received\n * a PONG within the timeout window.\n */\n public isConnectionHealthy(): boolean {\n return this.webSocketManager.isConnectionHealthy();\n }\n\n // ============ Backpressure Methods (delegated to BackpressureController) ============\n\n /**\n * Get the current number of pending (unsynced) operations.\n * Delegates to BackpressureController.\n */\n public getPendingOpsCount(): number {\n return this.backpressureController.getPendingOpsCount();\n }\n\n /**\n * Get the current backpressure status.\n * Delegates to BackpressureController.\n */\n public getBackpressureStatus(): BackpressureStatus {\n return this.backpressureController.getBackpressureStatus();\n }\n\n /**\n * Returns true if writes are currently paused due to backpressure.\n * Delegates to BackpressureController.\n */\n public isBackpressurePaused(): boolean {\n return this.backpressureController.isBackpressurePaused();\n }\n\n /**\n * Subscribe to backpressure events.\n * Delegates to BackpressureController.\n * @param event Event name: 'backpressure:high', 'backpressure:low', 'backpressure:paused', 'backpressure:resumed', 'operation:dropped'\n * @param listener Callback function\n * @returns Unsubscribe function\n */\n public onBackpressure(\n event: 'backpressure:high' | 'backpressure:low' | 'backpressure:paused' | 'backpressure:resumed' | 'operation:dropped',\n listener: (data?: BackpressureThresholdEvent | OperationDroppedEvent) => void\n ): () => void {\n return this.backpressureController.onBackpressure(event, listener);\n }\n\n // ============================================\n // Write Concern Methods\n // ============================================\n\n /**\n * Register a pending Write Concern promise for an operation.\n * Delegates to WriteConcernManager.\n *\n * @param opId - Operation ID\n * @param timeout - Timeout in ms (default: 5000)\n * @returns Promise that resolves with the Write Concern result\n */\n public registerWriteConcernPromise(opId: string, timeout: number = 5000): Promise<any> {\n return this.writeConcernManager.registerWriteConcernPromise(opId, timeout);\n }\n\n // ============================================\n // PN Counter Methods - Delegates to CounterManager\n // ============================================\n\n /**\n * Subscribe to counter updates from server.\n * Delegates to CounterManager.\n * @param name Counter name\n * @param listener Callback when counter state is updated\n * @returns Unsubscribe function\n */\n public onCounterUpdate(name: string, listener: (state: { positive: Map<string, number>; negative: Map<string, number> }) => void): () => void {\n return this.counterManager.onCounterUpdate(name, listener);\n }\n\n /**\n * Request initial counter state from server.\n * Delegates to CounterManager.\n * @param name Counter name\n */\n public requestCounter(name: string): void {\n this.counterManager.requestCounter(name);\n }\n\n /**\n * Sync local counter state to server.\n * Delegates to CounterManager.\n * @param name Counter name\n * @param state Counter state to sync\n */\n public syncCounter(name: string, state: { positive: Map<string, number>; negative: Map<string, number> }): void {\n this.counterManager.syncCounter(name, state);\n }\n\n // ============================================\n // Entry Processor Methods - Delegates to EntryProcessorClient\n // ============================================\n\n /**\n * Execute an entry processor on a single key atomically.\n * Delegates to EntryProcessorClient.\n *\n * @param mapName Name of the map\n * @param key Key to process\n * @param processor Processor definition\n * @returns Promise resolving to the processor result\n */\n public async executeOnKey<V, R = V>(\n mapName: string,\n key: string,\n processor: EntryProcessorDef<V, R>,\n ): Promise<EntryProcessorResult<R>> {\n return this.entryProcessorClient.executeOnKey(mapName, key, processor);\n }\n\n /**\n * Execute an entry processor on multiple keys.\n * Delegates to EntryProcessorClient.\n *\n * @param mapName Name of the map\n * @param keys Keys to process\n * @param processor Processor definition\n * @returns Promise resolving to a map of key -> result\n */\n public async executeOnKeys<V, R = V>(\n mapName: string,\n keys: string[],\n processor: EntryProcessorDef<V, R>,\n ): Promise<Map<string, EntryProcessorResult<R>>> {\n return this.entryProcessorClient.executeOnKeys(mapName, keys, processor);\n }\n\n // ============================================\n // Event Journal Methods\n // ============================================\n\n /** Message listeners for journal and other generic messages */\n private messageListeners: Set<(message: unknown) => void> = new Set();\n\n /**\n * Subscribe to all incoming messages.\n * Used by EventJournalReader to receive journal events.\n *\n * @param event Event type (currently only 'message')\n * @param handler Message handler\n */\n public on(event: 'message', handler: (message: unknown) => void): void {\n if (event === 'message') {\n this.messageListeners.add(handler);\n }\n }\n\n /**\n * Unsubscribe from incoming messages.\n *\n * @param event Event type (currently only 'message')\n * @param handler Message handler to remove\n */\n public off(event: 'message', handler: (message: unknown) => void): void {\n if (event === 'message') {\n this.messageListeners.delete(handler);\n }\n }\n\n /**\n * Send a message to the server.\n * Public method for EventJournalReader and other components.\n *\n * @param message Message object to send\n */\n public send(message: unknown): void {\n this.sendMessage(message);\n }\n\n /**\n * Emit message to all listeners.\n * Called internally when a message is received.\n */\n private emitMessage(message: unknown): void {\n for (const listener of this.messageListeners) {\n try {\n listener(message);\n } catch (e) {\n logger.error({ err: e }, 'Message listener error');\n }\n }\n }\n\n // ============================================\n // Full-Text Search Methods - Delegates to SearchClient\n // ============================================\n\n /**\n * Perform a one-shot BM25 search on the server.\n * Delegates to SearchClient.\n *\n * @param mapName Name of the map to search\n * @param query Search query text\n * @param options Search options (limit, minScore, boost)\n * @returns Promise resolving to search results\n */\n public async search<T>(\n mapName: string,\n query: string,\n options?: SearchOptions\n ): Promise<SearchResult<T>[]> {\n return this.searchClient.search<T>(mapName, query, options);\n }\n\n // ============================================\n // Conflict Resolver Client\n // ============================================\n\n /**\n * Get the conflict resolver client for registering custom resolvers\n * and subscribing to merge rejection events.\n */\n public getConflictResolverClient(): ConflictResolverClient {\n return this.conflictResolverClient;\n }\n\n // ============================================\n // Hybrid Query Support - Delegates to QueryManager\n // ============================================\n\n /**\n * Subscribe to a hybrid query (FTS + filter combination).\n * Delegates to QueryManager.\n */\n public subscribeToHybridQuery<T>(query: HybridQueryHandle<T>): void {\n this.queryManager.subscribeToHybridQuery(query);\n }\n\n /**\n * Unsubscribe from a hybrid query.\n * Delegates to QueryManager.\n */\n public unsubscribeFromHybridQuery(queryId: string): void {\n this.queryManager.unsubscribeFromHybridQuery(queryId);\n }\n\n /**\n * Run a local hybrid query (FTS + filter combination).\n * Delegates to QueryManager.\n */\n public async runLocalHybridQuery<T>(\n mapName: string,\n filter: HybridQueryFilter\n ): Promise<Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }>> {\n return this.queryManager.runLocalHybridQuery<T>(mapName, filter);\n }\n\n /**\n * Handle hybrid query response from server.\n */\n public handleHybridQueryResponse(payload: HybridQueryRespPayload): void {\n const query = this.queryManager.getHybridQuery(payload.subscriptionId);\n if (query) {\n query.onResult(payload.results as any, 'server');\n query.updatePaginationInfo({\n nextCursor: payload.nextCursor,\n hasMore: payload.hasMore,\n cursorStatus: payload.cursorStatus,\n });\n }\n }\n\n /**\n * Handle hybrid query delta update from server.\n */\n public handleHybridQueryDelta(payload: HybridQueryDeltaPayload): void {\n const query = this.queryManager.getHybridQuery(payload.subscriptionId);\n if (query) {\n if (payload.type === 'LEAVE') {\n query.onUpdate(payload.key, null);\n } else {\n query.onUpdate(payload.key, payload.value, payload.score, payload.matchedTerms);\n }\n }\n }\n}\n","import pino from 'pino';\n\n// Simple check for browser environment\nconst isBrowser = typeof window !== 'undefined';\n\n// In browser, we might not have process.env, so we default to 'info'\n// Users can configure this via window.LOG_LEVEL or similar if needed,\n// but for now we stick to a safe default.\nconst logLevel = (typeof process !== 'undefined' && process.env && process.env.LOG_LEVEL) || 'info';\n\nexport const logger = pino({\n level: logLevel,\n transport: !isBrowser && (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') ? {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'SYS:standard',\n ignore: 'pid,hostname'\n }\n } : undefined,\n browser: {\n asObject: true\n }\n});\n\nexport type Logger = typeof logger;\n\n","/**\n * Defines the possible states for the SyncEngine connection state machine.\n */\nexport enum SyncState {\n /** Initial state before any connection attempt */\n INITIAL = 'INITIAL',\n /** WebSocket connection is being established */\n CONNECTING = 'CONNECTING',\n /** Connected, waiting for authentication response */\n AUTHENTICATING = 'AUTHENTICATING',\n /** Authenticated, performing initial data sync */\n SYNCING = 'SYNCING',\n /** Fully connected and synchronized */\n CONNECTED = 'CONNECTED',\n /** Intentionally or unexpectedly disconnected */\n DISCONNECTED = 'DISCONNECTED',\n /** Waiting before retry (exponential backoff) */\n BACKOFF = 'BACKOFF',\n /** Fatal error requiring manual intervention or reset */\n ERROR = 'ERROR',\n}\n\n/**\n * Defines valid state transitions for the SyncEngine FSM.\n * Each key is a current state, and the value is an array of valid target states.\n */\nexport const VALID_TRANSITIONS: Record<SyncState, SyncState[]> = {\n [SyncState.INITIAL]: [SyncState.CONNECTING],\n [SyncState.CONNECTING]: [SyncState.AUTHENTICATING, SyncState.BACKOFF, SyncState.ERROR, SyncState.DISCONNECTED],\n [SyncState.AUTHENTICATING]: [SyncState.SYNCING, SyncState.BACKOFF, SyncState.ERROR, SyncState.DISCONNECTED],\n [SyncState.SYNCING]: [SyncState.CONNECTED, SyncState.BACKOFF, SyncState.ERROR, SyncState.DISCONNECTED],\n [SyncState.CONNECTED]: [SyncState.SYNCING, SyncState.DISCONNECTED, SyncState.BACKOFF],\n [SyncState.DISCONNECTED]: [SyncState.CONNECTING, SyncState.BACKOFF, SyncState.INITIAL],\n [SyncState.BACKOFF]: [SyncState.CONNECTING, SyncState.DISCONNECTED, SyncState.INITIAL],\n [SyncState.ERROR]: [SyncState.INITIAL],\n};\n\n/**\n * Helper function to check if a transition is valid\n */\nexport function isValidTransition(from: SyncState, to: SyncState): boolean {\n return VALID_TRANSITIONS[from]?.includes(to) ?? false;\n}\n","import { SyncState, isValidTransition } from './SyncState';\nimport { logger } from './utils/logger';\n\n/**\n * Event emitted when the state machine transitions between states.\n */\nexport interface StateChangeEvent {\n /** The state before the transition */\n from: SyncState;\n /** The state after the transition */\n to: SyncState;\n /** Unix timestamp (ms) when the transition occurred */\n timestamp: number;\n}\n\n/**\n * Listener callback for state change events.\n */\nexport type StateChangeListener = (event: StateChangeEvent) => void;\n\n/**\n * Configuration options for the state machine.\n */\nexport interface SyncStateMachineConfig {\n /** Maximum number of state transitions to keep in history (default: 50) */\n maxHistorySize?: number;\n}\n\nconst DEFAULT_MAX_HISTORY_SIZE = 50;\n\n/**\n * A finite state machine for managing SyncEngine connection states.\n *\n * Features:\n * - Validates all state transitions against allowed paths\n * - Emits events on state changes for observability\n * - Maintains a history of transitions for debugging\n * - Logs invalid transition attempts (graceful degradation)\n */\nexport class SyncStateMachine {\n private state: SyncState = SyncState.INITIAL;\n private readonly listeners: Set<StateChangeListener> = new Set();\n private history: StateChangeEvent[] = [];\n private readonly maxHistorySize: number;\n\n constructor(config: SyncStateMachineConfig = {}) {\n this.maxHistorySize = config.maxHistorySize ?? DEFAULT_MAX_HISTORY_SIZE;\n }\n\n /**\n * Attempt to transition to a new state.\n * @param to The target state\n * @returns true if the transition was valid and executed, false otherwise\n */\n transition(to: SyncState): boolean {\n const from = this.state;\n\n if (from === to) {\n // No-op: already in target state\n return true;\n }\n\n if (!isValidTransition(from, to)) {\n logger.warn(\n { from, to, currentHistory: this.getHistory(5) },\n `Invalid state transition attempted: ${from} → ${to}`\n );\n return false;\n }\n\n // Execute the transition\n this.state = to;\n\n const event: StateChangeEvent = {\n from,\n to,\n timestamp: Date.now(),\n };\n\n // Add to history\n this.history.push(event);\n if (this.history.length > this.maxHistorySize) {\n this.history.shift();\n }\n\n // Notify listeners\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (err) {\n logger.error({ err, event }, 'State change listener threw an error');\n }\n }\n\n logger.debug({ from, to }, `State transition: ${from} → ${to}`);\n\n return true;\n }\n\n /**\n * Get the current state.\n */\n getState(): SyncState {\n return this.state;\n }\n\n /**\n * Check if a transition from the current state to the target state is valid.\n * @param to The target state to check\n */\n canTransition(to: SyncState): boolean {\n return this.state === to || isValidTransition(this.state, to);\n }\n\n /**\n * Subscribe to state change events.\n * @param listener Callback function to be called on each state change\n * @returns An unsubscribe function\n */\n onStateChange(listener: StateChangeListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Get the state transition history.\n * @param limit Maximum number of entries to return (default: all)\n * @returns Array of state change events, oldest first\n */\n getHistory(limit?: number): StateChangeEvent[] {\n if (limit === undefined || limit >= this.history.length) {\n return [...this.history];\n }\n return this.history.slice(-limit);\n }\n\n /**\n * Reset the state machine to INITIAL state.\n * This is a forced reset that bypasses normal transition validation.\n * Use for testing or hard resets after fatal errors.\n * @param clearHistory If true, also clears the transition history (default: true)\n */\n reset(clearHistory = true): void {\n const from = this.state;\n this.state = SyncState.INITIAL;\n\n if (clearHistory) {\n this.history = [];\n } else {\n // Record the reset as a transition\n const event: StateChangeEvent = {\n from,\n to: SyncState.INITIAL,\n timestamp: Date.now(),\n };\n this.history.push(event);\n if (this.history.length > this.maxHistorySize) {\n this.history.shift();\n }\n\n // Notify listeners\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (err) {\n logger.error({ err, event }, 'State change listener threw an error during reset');\n }\n }\n }\n\n logger.info({ from }, 'State machine reset to INITIAL');\n }\n\n /**\n * Check if the state machine is in a \"connected\" state\n * (either SYNCING or CONNECTED)\n */\n isConnected(): boolean {\n return this.state === SyncState.CONNECTED || this.state === SyncState.SYNCING;\n }\n\n /**\n * Check if the state machine is in a state where operations can be sent\n * (authenticated and connected)\n */\n isReady(): boolean {\n return this.state === SyncState.CONNECTED;\n }\n\n /**\n * Check if the state machine is currently attempting to connect\n */\n isConnecting(): boolean {\n return (\n this.state === SyncState.CONNECTING ||\n this.state === SyncState.AUTHENTICATING ||\n this.state === SyncState.SYNCING\n );\n }\n}\n","/**\n * Backpressure strategy when maxPendingOps is reached.\n */\nexport type BackpressureStrategy = 'pause' | 'throw' | 'drop-oldest';\n\n/**\n * Configuration for backpressure control on SyncEngine.\n */\nexport interface BackpressureConfig {\n /**\n * Maximum number of operations waiting for server acknowledgment.\n * When this limit is reached, the configured strategy will be applied.\n * @default 1000\n */\n maxPendingOps: number;\n\n /**\n * Strategy when maxPendingOps is reached:\n * - 'pause': Wait for capacity (returns Promise that resolves when space available)\n * - 'throw': Throw BackpressureError immediately\n * - 'drop-oldest': Remove oldest pending op to make room (data loss!)\n * @default 'pause'\n */\n strategy: BackpressureStrategy;\n\n /**\n * High water mark (percentage of maxPendingOps).\n * Emit 'backpressure:high' event when reached.\n * Value should be between 0 and 1.\n * @default 0.8 (80%)\n */\n highWaterMark: number;\n\n /**\n * Low water mark (percentage of maxPendingOps).\n * Resume paused writes and emit 'backpressure:low' when pending ops drop below this.\n * Value should be between 0 and 1.\n * @default 0.5 (50%)\n */\n lowWaterMark: number;\n}\n\n/**\n * Default backpressure configuration.\n */\nexport const DEFAULT_BACKPRESSURE_CONFIG: BackpressureConfig = {\n maxPendingOps: 1000,\n strategy: 'pause',\n highWaterMark: 0.8,\n lowWaterMark: 0.5,\n};\n\n/**\n * Status of backpressure mechanism.\n */\nexport interface BackpressureStatus {\n /** Current number of pending (unacknowledged) operations */\n pending: number;\n /** Maximum allowed pending operations */\n max: number;\n /** Percentage of capacity used (0-1) */\n percentage: number;\n /** Whether writes are currently paused due to backpressure */\n isPaused: boolean;\n /** Current backpressure strategy */\n strategy: BackpressureStrategy;\n}\n\n/**\n * Event data for backpressure:high and backpressure:low events.\n */\nexport interface BackpressureThresholdEvent {\n pending: number;\n max: number;\n}\n\n/**\n * Event data for operation:dropped event.\n */\nexport interface OperationDroppedEvent {\n opId: string;\n mapName: string;\n opType: string;\n key: string;\n}\n","import type { ConflictResolverDef, MergeRejection, Timestamp } from '@topgunbuild/core';\nimport type { SyncEngine } from './SyncEngine';\nimport { logger } from './utils/logger';\n\n/**\n * Registered resolver info returned from server.\n */\nexport interface ResolverInfo {\n mapName: string;\n name: string;\n priority?: number;\n keyPattern?: string;\n}\n\n/**\n * Registration result from server.\n */\nexport interface RegisterResult {\n success: boolean;\n error?: string;\n}\n\n/**\n * Client-side manager for conflict resolvers.\n *\n * Provides API for:\n * - Registering conflict resolvers on server\n * - Unregistering resolvers\n * - Listing registered resolvers\n * - Subscribing to merge rejection events\n */\nexport class ConflictResolverClient {\n private readonly syncEngine: SyncEngine;\n private readonly rejectionListeners: Set<(rejection: MergeRejection) => void> = new Set();\n private readonly pendingRequests: Map<string, {\n resolve: (result: any) => void;\n reject: (error: Error) => void;\n timeout: NodeJS.Timeout;\n }> = new Map();\n\n private static readonly REQUEST_TIMEOUT = 10000; // 10 seconds\n\n constructor(syncEngine: SyncEngine) {\n this.syncEngine = syncEngine;\n }\n\n /**\n * Register a conflict resolver on the server.\n *\n * @param mapName The map to register the resolver for\n * @param resolver The resolver definition\n * @returns Promise resolving to registration result\n *\n * @example\n * ```typescript\n * // Register a first-write-wins resolver for bookings\n * await client.resolvers.register('bookings', {\n * name: 'first-write-wins',\n * code: `\n * if (context.localValue !== undefined) {\n * return { action: 'reject', reason: 'Slot already booked' };\n * }\n * return { action: 'accept', value: context.remoteValue };\n * `,\n * priority: 100,\n * });\n * ```\n */\n async register<V>(\n mapName: string,\n resolver: Omit<ConflictResolverDef<V>, 'fn'>,\n ): Promise<RegisterResult> {\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n reject(new Error('Register resolver request timed out'));\n }, ConflictResolverClient.REQUEST_TIMEOUT);\n\n this.pendingRequests.set(requestId, {\n resolve: (result: RegisterResult) => {\n clearTimeout(timeout);\n resolve(result);\n },\n reject,\n timeout,\n });\n\n try {\n this.syncEngine.send({\n type: 'REGISTER_RESOLVER',\n requestId,\n mapName,\n resolver: {\n name: resolver.name,\n code: resolver.code || '',\n priority: resolver.priority,\n keyPattern: resolver.keyPattern,\n },\n });\n } catch {\n this.pendingRequests.delete(requestId);\n clearTimeout(timeout);\n resolve({ success: false, error: 'Not connected to server' });\n }\n });\n }\n\n /**\n * Unregister a conflict resolver from the server.\n *\n * @param mapName The map the resolver is registered for\n * @param resolverName The name of the resolver to unregister\n * @returns Promise resolving to unregistration result\n */\n async unregister(mapName: string, resolverName: string): Promise<RegisterResult> {\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n reject(new Error('Unregister resolver request timed out'));\n }, ConflictResolverClient.REQUEST_TIMEOUT);\n\n this.pendingRequests.set(requestId, {\n resolve: (result: RegisterResult) => {\n clearTimeout(timeout);\n resolve(result);\n },\n reject,\n timeout,\n });\n\n try {\n this.syncEngine.send({\n type: 'UNREGISTER_RESOLVER',\n requestId,\n mapName,\n resolverName,\n });\n } catch {\n this.pendingRequests.delete(requestId);\n clearTimeout(timeout);\n resolve({ success: false, error: 'Not connected to server' });\n }\n });\n }\n\n /**\n * List registered conflict resolvers on the server.\n *\n * @param mapName Optional - filter by map name\n * @returns Promise resolving to list of resolver info\n */\n async list(mapName?: string): Promise<ResolverInfo[]> {\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n reject(new Error('List resolvers request timed out'));\n }, ConflictResolverClient.REQUEST_TIMEOUT);\n\n this.pendingRequests.set(requestId, {\n resolve: (result: { resolvers: ResolverInfo[] }) => {\n clearTimeout(timeout);\n resolve(result.resolvers);\n },\n reject,\n timeout,\n });\n\n try {\n this.syncEngine.send({\n type: 'LIST_RESOLVERS',\n requestId,\n mapName,\n });\n } catch {\n this.pendingRequests.delete(requestId);\n clearTimeout(timeout);\n resolve([]);\n }\n });\n }\n\n /**\n * Subscribe to merge rejection events.\n *\n * @param listener Callback for rejection events\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = client.resolvers.onRejection((rejection) => {\n * console.log(`Merge rejected for ${rejection.key}: ${rejection.reason}`);\n * // Optionally refresh the local value\n * });\n *\n * // Later...\n * unsubscribe();\n * ```\n */\n onRejection(listener: (rejection: MergeRejection) => void): () => void {\n this.rejectionListeners.add(listener);\n return () => this.rejectionListeners.delete(listener);\n }\n\n /**\n * Handle REGISTER_RESOLVER_RESPONSE from server.\n * Called by SyncEngine.\n */\n handleRegisterResponse(message: {\n requestId: string;\n success: boolean;\n error?: string;\n }): void {\n const pending = this.pendingRequests.get(message.requestId);\n if (pending) {\n this.pendingRequests.delete(message.requestId);\n pending.resolve({ success: message.success, error: message.error });\n }\n }\n\n /**\n * Handle UNREGISTER_RESOLVER_RESPONSE from server.\n * Called by SyncEngine.\n */\n handleUnregisterResponse(message: {\n requestId: string;\n success: boolean;\n error?: string;\n }): void {\n const pending = this.pendingRequests.get(message.requestId);\n if (pending) {\n this.pendingRequests.delete(message.requestId);\n pending.resolve({ success: message.success, error: message.error });\n }\n }\n\n /**\n * Handle LIST_RESOLVERS_RESPONSE from server.\n * Called by SyncEngine.\n */\n handleListResponse(message: {\n requestId: string;\n resolvers: ResolverInfo[];\n }): void {\n const pending = this.pendingRequests.get(message.requestId);\n if (pending) {\n this.pendingRequests.delete(message.requestId);\n pending.resolve({ resolvers: message.resolvers });\n }\n }\n\n /**\n * Handle MERGE_REJECTED from server.\n * Called by SyncEngine.\n */\n handleMergeRejected(message: {\n mapName: string;\n key: string;\n attemptedValue: unknown;\n reason: string;\n timestamp: Timestamp;\n }): void {\n const rejection: MergeRejection = {\n mapName: message.mapName,\n key: message.key,\n attemptedValue: message.attemptedValue,\n reason: message.reason,\n timestamp: message.timestamp,\n nodeId: '', // Not provided by server in this message\n };\n\n logger.debug({ rejection }, 'Merge rejected by server');\n\n for (const listener of this.rejectionListeners) {\n try {\n listener(rejection);\n } catch (e) {\n logger.error({ error: e }, 'Error in rejection listener');\n }\n }\n }\n\n /**\n * Clear all pending requests (e.g., on disconnect).\n */\n clearPending(): void {\n for (const [requestId, pending] of this.pendingRequests) {\n clearTimeout(pending.timeout);\n pending.reject(new Error('Connection lost'));\n }\n this.pendingRequests.clear();\n }\n\n /**\n * Get the number of registered rejection listeners.\n */\n get rejectionListenerCount(): number {\n return this.rejectionListeners.size;\n }\n}\n","/**\n * WebSocketManager - Handles all WebSocket/connection operations for SyncEngine\n *\n * Responsibilities:\n * - WebSocket lifecycle management (connect, close, reset)\n * - Message serialization/deserialization\n * - Heartbeat mechanism (PING/PONG)\n * - Reconnection with exponential backoff\n * - Event forwarding from connection provider\n */\n\nimport { serialize, deserialize } from '@topgunbuild/core';\nimport type { IConnectionProvider, ConnectionProviderEvent, ConnectionEventHandler } from '../types';\nimport { SyncState } from '../SyncState';\nimport { logger } from '../utils/logger';\nimport type { IWebSocketManager, WebSocketManagerConfig } from './types';\n\n/**\n * WebSocketManager implements IWebSocketManager.\n *\n * Manages WebSocket connections via IConnectionProvider.\n * Supports both single-server and cluster modes through the provider abstraction.\n */\nexport class WebSocketManager implements IWebSocketManager {\n private readonly config: WebSocketManagerConfig;\n private readonly connectionProvider: IConnectionProvider;\n\n // Reconnection state\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private backoffAttempt: number = 0;\n\n // Heartbeat state\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null;\n private lastPongReceived: number = Date.now();\n private lastRoundTripTime: number | null = null;\n\n constructor(config: WebSocketManagerConfig) {\n this.config = config;\n this.connectionProvider = config.connectionProvider;\n }\n\n /**\n * Initialize the connection.\n * Sets up event handlers and starts the connection process.\n */\n connect(): void {\n this.initConnectionProvider();\n }\n\n /**\n * Initialize connection using IConnectionProvider.\n */\n private initConnectionProvider(): void {\n // Transition to CONNECTING state\n this.config.stateMachine.transition(SyncState.CONNECTING);\n\n // Set up event handlers\n this.connectionProvider.on('connected', (_nodeId: string) => {\n logger.info('ConnectionProvider connected.');\n this.config.onConnected?.();\n });\n\n this.connectionProvider.on('disconnected', (_nodeId: string) => {\n logger.info('ConnectionProvider disconnected.');\n this.stopHeartbeat();\n this.config.stateMachine.transition(SyncState.DISCONNECTED);\n this.config.onDisconnected?.();\n // Don't schedule reconnect - provider handles it\n });\n\n this.connectionProvider.on('reconnected', (_nodeId: string) => {\n logger.info('ConnectionProvider reconnected.');\n this.config.stateMachine.transition(SyncState.CONNECTING);\n this.config.onReconnected?.();\n });\n\n this.connectionProvider.on('message', (_nodeId: string, data: any) => {\n const message = this.deserializeMessage(data);\n if (message) {\n this.handleMessage(message);\n }\n });\n\n this.connectionProvider.on('partitionMapUpdated', () => {\n logger.debug('Partition map updated');\n });\n\n this.connectionProvider.on('error', (error: Error) => {\n logger.error({ err: error }, 'ConnectionProvider error');\n });\n\n // Start connection\n this.connectionProvider.connect().catch((err) => {\n logger.error({ err }, 'Failed to connect via ConnectionProvider');\n this.config.stateMachine.transition(SyncState.DISCONNECTED);\n });\n }\n\n /**\n * Deserialize incoming message data.\n */\n private deserializeMessage(data: any): any {\n try {\n if (data instanceof ArrayBuffer) {\n return deserialize(new Uint8Array(data));\n } else if (data instanceof Uint8Array) {\n return deserialize(data);\n } else if (typeof data === 'string') {\n return JSON.parse(data);\n } else {\n return data;\n }\n } catch (e) {\n logger.error({ err: e }, 'Failed to parse message');\n return null;\n }\n }\n\n /**\n * Handle incoming message.\n * Routes PONG to internal handler, all others to SyncEngine.\n */\n private handleMessage(message: any): void {\n // Handle PONG internally for heartbeat tracking\n if (message.type === 'PONG') {\n this.handlePong(message);\n }\n // Route all messages to SyncEngine (including PONG for HLC sync if needed)\n this.config.onMessage(message);\n }\n\n /**\n * Send a message through the current connection.\n */\n sendMessage(message: any, key?: string): boolean {\n const data = serialize(message);\n\n try {\n this.connectionProvider.send(data, key);\n return true;\n } catch (err) {\n logger.warn({ err }, 'Failed to send via ConnectionProvider');\n return false;\n }\n }\n\n /**\n * Check if we can send messages (connection is ready).\n */\n canSend(): boolean {\n return this.connectionProvider.isConnected();\n }\n\n /**\n * Check if connected to the server.\n */\n isOnline(): boolean {\n const state = this.config.stateMachine.getState();\n return (\n state === SyncState.CONNECTING ||\n state === SyncState.AUTHENTICATING ||\n state === SyncState.SYNCING ||\n state === SyncState.CONNECTED\n );\n }\n\n /**\n * Get the connection provider.\n */\n getConnectionProvider(): IConnectionProvider {\n return this.connectionProvider;\n }\n\n /**\n * Close the connection and clean up resources.\n */\n close(): void {\n this.stopHeartbeat();\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n this.connectionProvider.close().catch((err) => {\n logger.error({ err }, 'Error closing ConnectionProvider');\n });\n }\n\n /**\n * Reset connection state for a fresh reconnection.\n */\n reset(): void {\n this.close();\n this.resetBackoff();\n }\n\n /**\n * Subscribe to connection events.\n */\n on(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n this.connectionProvider.on(event, handler);\n }\n\n /**\n * Unsubscribe from connection events.\n */\n off(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n this.connectionProvider.off(event, handler);\n }\n\n /**\n * Reset backoff counter.\n */\n resetBackoff(): void {\n this.backoffAttempt = 0;\n }\n\n /**\n * Get current backoff attempt count.\n */\n getBackoffAttempt(): number {\n return this.backoffAttempt;\n }\n\n /**\n * Clear reconnect timer (for external control, e.g., when new auth token provided).\n */\n clearReconnectTimer(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n // ============================================\n // Heartbeat Mechanism\n // ============================================\n\n /**\n * Starts the heartbeat mechanism after successful connection.\n */\n startHeartbeat(): void {\n if (!this.config.heartbeatConfig.enabled) {\n return;\n }\n\n this.stopHeartbeat(); // Clear any existing interval\n this.lastPongReceived = Date.now();\n\n this.heartbeatInterval = setInterval(() => {\n this.sendPing();\n this.checkHeartbeatTimeout();\n }, this.config.heartbeatConfig.intervalMs);\n\n logger.info({ intervalMs: this.config.heartbeatConfig.intervalMs }, 'Heartbeat started');\n }\n\n /**\n * Stops the heartbeat mechanism.\n */\n stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n logger.info('Heartbeat stopped');\n }\n }\n\n /**\n * Sends a PING message to the server.\n */\n private sendPing(): void {\n if (this.canSend()) {\n const pingMessage = {\n type: 'PING',\n timestamp: Date.now(),\n };\n this.sendMessage(pingMessage);\n }\n }\n\n /**\n * Handles incoming PONG message from server.\n */\n private handlePong(msg: { timestamp: number; serverTime: number }): void {\n const now = Date.now();\n this.lastPongReceived = now;\n this.lastRoundTripTime = now - msg.timestamp;\n\n logger.debug({\n rtt: this.lastRoundTripTime,\n serverTime: msg.serverTime,\n clockSkew: msg.serverTime - (msg.timestamp + this.lastRoundTripTime / 2),\n }, 'Received PONG');\n }\n\n /**\n * Checks if heartbeat has timed out and triggers reconnection if needed.\n */\n private checkHeartbeatTimeout(): void {\n const now = Date.now();\n const timeSinceLastPong = now - this.lastPongReceived;\n\n if (timeSinceLastPong > this.config.heartbeatConfig.timeoutMs) {\n logger.warn({\n timeSinceLastPong,\n timeoutMs: this.config.heartbeatConfig.timeoutMs,\n }, 'Heartbeat timeout - triggering reconnection');\n\n this.stopHeartbeat();\n\n // Force close and reconnect via connection provider\n this.connectionProvider.close().catch((err) => {\n logger.error({ err }, 'Error closing ConnectionProvider on heartbeat timeout');\n });\n }\n }\n\n /**\n * Returns the last measured round-trip time in milliseconds.\n */\n getLastRoundTripTime(): number | null {\n return this.lastRoundTripTime;\n }\n\n /**\n * Returns true if the connection is considered healthy based on heartbeat.\n */\n isConnectionHealthy(): boolean {\n const state = this.config.stateMachine.getState();\n const isOnline = (\n state === SyncState.CONNECTING ||\n state === SyncState.AUTHENTICATING ||\n state === SyncState.SYNCING ||\n state === SyncState.CONNECTED\n );\n const isAuthenticated = state === SyncState.SYNCING || state === SyncState.CONNECTED;\n\n if (!isOnline || !isAuthenticated) {\n return false;\n }\n\n if (!this.config.heartbeatConfig.enabled) {\n return true; // If heartbeat disabled, consider healthy if online\n }\n\n const timeSinceLastPong = Date.now() - this.lastPongReceived;\n return timeSinceLastPong < this.config.heartbeatConfig.timeoutMs;\n }\n}\n","/**\n * Error thrown when backpressure limit is reached and strategy is 'throw'.\n */\nexport class BackpressureError extends Error {\n public readonly name = 'BackpressureError';\n\n constructor(\n public readonly pendingCount: number,\n public readonly maxPending: number\n ) {\n super(\n `Backpressure limit reached: ${pendingCount}/${maxPending} pending operations. ` +\n `Wait for acknowledgments or increase maxPendingOps.`\n );\n\n // Maintains proper stack trace for where error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, BackpressureError);\n }\n }\n}\n","/**\n * BackpressureController\n *\n * Handles all backpressure operations for SyncEngine:\n * - Monitors pending operations count via shared opLog reference\n * - Implements pause/throw/drop-oldest strategies\n * - Emits high/low water mark events\n * - Manages paused writes and capacity waiting\n */\n\nimport type {\n BackpressureConfig,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n} from '../BackpressureConfig';\nimport { BackpressureError } from '../errors/BackpressureError';\nimport type { OpLogEntry } from '../SyncEngine';\nimport type { IBackpressureController, BackpressureControllerConfig } from './types';\nimport { logger } from '../utils/logger';\n\n/**\n * BackpressureController manages flow control for pending operations.\n *\n * It receives a shared reference to opLog from SyncEngine and uses it\n * to count pending operations and implement drop-oldest strategy.\n * SyncEngine retains ownership of opLog.\n */\nexport class BackpressureController implements IBackpressureController {\n private readonly config: BackpressureConfig;\n private readonly opLog: OpLogEntry[];\n\n // Internal state\n private backpressurePaused: boolean = false;\n private waitingForCapacity: Array<() => void> = [];\n private highWaterMarkEmitted: boolean = false;\n private backpressureListeners: Map<string, Set<(...args: any[]) => void>> = new Map();\n\n constructor(controllerConfig: BackpressureControllerConfig) {\n this.config = controllerConfig.config;\n this.opLog = controllerConfig.opLog; // Shared reference, not a copy\n }\n\n // ============================================\n // Status Methods\n // ============================================\n\n /**\n * Get the current number of pending (unsynced) operations.\n */\n public getPendingOpsCount(): number {\n return this.opLog.filter(op => !op.synced).length;\n }\n\n /**\n * Get the current backpressure status.\n */\n public getBackpressureStatus(): BackpressureStatus {\n const pending = this.getPendingOpsCount();\n const max = this.config.maxPendingOps;\n return {\n pending,\n max,\n percentage: max > 0 ? pending / max : 0,\n isPaused: this.backpressurePaused,\n strategy: this.config.strategy,\n };\n }\n\n /**\n * Returns true if writes are currently paused due to backpressure.\n */\n public isBackpressurePaused(): boolean {\n return this.backpressurePaused;\n }\n\n // ============================================\n // Check Methods\n // ============================================\n\n /**\n * Check backpressure before adding a new operation.\n * May pause, throw, or drop depending on strategy.\n */\n public async checkBackpressure(): Promise<void> {\n const pendingCount = this.getPendingOpsCount();\n\n if (pendingCount < this.config.maxPendingOps) {\n return; // Capacity available\n }\n\n switch (this.config.strategy) {\n case 'pause':\n await this.waitForCapacity();\n break;\n case 'throw':\n throw new BackpressureError(\n pendingCount,\n this.config.maxPendingOps\n );\n case 'drop-oldest':\n this.dropOldestOp();\n break;\n }\n }\n\n /**\n * Check high water mark and emit event if threshold reached.\n */\n public checkHighWaterMark(): void {\n const pendingCount = this.getPendingOpsCount();\n const threshold = Math.floor(\n this.config.maxPendingOps * this.config.highWaterMark\n );\n\n if (pendingCount >= threshold && !this.highWaterMarkEmitted) {\n this.highWaterMarkEmitted = true;\n logger.warn(\n { pending: pendingCount, max: this.config.maxPendingOps },\n 'Backpressure high water mark reached'\n );\n this.emitBackpressureEvent('backpressure:high', {\n pending: pendingCount,\n max: this.config.maxPendingOps,\n });\n }\n }\n\n /**\n * Check low water mark and resume paused writes if threshold reached.\n */\n public checkLowWaterMark(): void {\n const pendingCount = this.getPendingOpsCount();\n const lowThreshold = Math.floor(\n this.config.maxPendingOps * this.config.lowWaterMark\n );\n const highThreshold = Math.floor(\n this.config.maxPendingOps * this.config.highWaterMark\n );\n\n // Reset high water mark flag when below high threshold\n if (pendingCount < highThreshold && this.highWaterMarkEmitted) {\n this.highWaterMarkEmitted = false;\n }\n\n // Emit low water mark event when crossing below threshold\n if (pendingCount <= lowThreshold) {\n if (this.backpressurePaused) {\n this.backpressurePaused = false;\n logger.info(\n { pending: pendingCount, max: this.config.maxPendingOps },\n 'Backpressure low water mark reached, resuming writes'\n );\n this.emitBackpressureEvent('backpressure:low', {\n pending: pendingCount,\n max: this.config.maxPendingOps,\n });\n this.emitBackpressureEvent('backpressure:resumed');\n\n // Resume all waiting writes\n const waiting = this.waitingForCapacity;\n this.waitingForCapacity = [];\n for (const resolve of waiting) {\n resolve();\n }\n }\n }\n }\n\n // ============================================\n // Event Methods\n // ============================================\n\n /**\n * Subscribe to backpressure events.\n * @param event Event name: 'backpressure:high', 'backpressure:low', 'backpressure:paused', 'backpressure:resumed', 'operation:dropped'\n * @param listener Callback function\n * @returns Unsubscribe function\n */\n public onBackpressure(\n event: 'backpressure:high' | 'backpressure:low' | 'backpressure:paused' | 'backpressure:resumed' | 'operation:dropped',\n listener: (data?: BackpressureThresholdEvent | OperationDroppedEvent) => void\n ): () => void {\n if (!this.backpressureListeners.has(event)) {\n this.backpressureListeners.set(event, new Set());\n }\n this.backpressureListeners.get(event)!.add(listener);\n\n return () => {\n this.backpressureListeners.get(event)?.delete(listener);\n };\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n /**\n * Emit a backpressure event to all listeners.\n */\n private emitBackpressureEvent(\n event: 'backpressure:high' | 'backpressure:low' | 'backpressure:paused' | 'backpressure:resumed' | 'operation:dropped',\n data?: BackpressureThresholdEvent | OperationDroppedEvent\n ): void {\n const listeners = this.backpressureListeners.get(event);\n if (listeners) {\n for (const listener of listeners) {\n try {\n listener(data);\n } catch (err) {\n logger.error({ err, event }, 'Error in backpressure event listener');\n }\n }\n }\n }\n\n /**\n * Wait for capacity to become available (used by 'pause' strategy).\n */\n private async waitForCapacity(): Promise<void> {\n if (!this.backpressurePaused) {\n this.backpressurePaused = true;\n logger.warn('Backpressure paused - waiting for capacity');\n this.emitBackpressureEvent('backpressure:paused');\n }\n\n return new Promise<void>((resolve) => {\n this.waitingForCapacity.push(resolve);\n });\n }\n\n /**\n * Drop the oldest pending operation (used by 'drop-oldest' strategy).\n * Modifies opLog via shared reference.\n */\n private dropOldestOp(): void {\n // Find oldest unsynced operation by array order (oldest first)\n const oldestIndex = this.opLog.findIndex(op => !op.synced);\n\n if (oldestIndex !== -1) {\n const dropped = this.opLog[oldestIndex];\n this.opLog.splice(oldestIndex, 1);\n\n logger.warn(\n { opId: dropped.id, mapName: dropped.mapName, key: dropped.key },\n 'Dropped oldest pending operation due to backpressure'\n );\n\n this.emitBackpressureEvent('operation:dropped', {\n opId: dropped.id,\n mapName: dropped.mapName,\n opType: dropped.opType,\n key: dropped.key,\n });\n }\n }\n}\n\nexport type { BackpressureControllerConfig } from './types';\n","/**\n * QueryManager - Manages query subscriptions and local query execution\n *\n * This module extracts query management from SyncEngine:\n * - Standard query subscriptions (QueryHandle)\n * - Hybrid query subscriptions (HybridQueryHandle with FTS)\n * - Local query execution against storage adapter\n */\n\nimport { evaluatePredicate } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\nimport type { QueryHandle, QueryFilter } from '../QueryHandle';\nimport type { HybridQueryHandle, HybridQueryFilter } from '../HybridQueryHandle';\nimport type { IQueryManager, QueryManagerConfig } from './types';\n\n/**\n * QueryManager handles all query-related operations for SyncEngine.\n * It owns the queries and hybridQueries Maps (single source of truth).\n */\nexport class QueryManager implements IQueryManager {\n private readonly config: QueryManagerConfig;\n\n /** Standard queries (single source of truth) */\n private queries: Map<string, QueryHandle<any>> = new Map();\n\n /** Hybrid queries with FTS support (single source of truth) */\n private hybridQueries: Map<string, HybridQueryHandle<any>> = new Map();\n\n constructor(config: QueryManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Query Access Methods\n // ============================================\n\n /**\n * Get all queries (read-only access).\n */\n public getQueries(): Map<string, QueryHandle<any>> {\n return this.queries;\n }\n\n /**\n * Get all hybrid queries.\n */\n public getHybridQueries(): Map<string, HybridQueryHandle<any>> {\n return this.hybridQueries;\n }\n\n /**\n * Get a hybrid query by ID.\n */\n public getHybridQuery(queryId: string): HybridQueryHandle<any> | undefined {\n return this.hybridQueries.get(queryId);\n }\n\n // ============================================\n // Standard Query Methods\n // ============================================\n\n /**\n * Subscribe to a standard query.\n * Adds to queries Map and sends subscription to server if authenticated.\n */\n public subscribeToQuery(query: QueryHandle<any>): void {\n this.queries.set(query.id, query);\n if (this.config.isAuthenticated()) {\n this.sendQuerySubscription(query);\n }\n }\n\n /**\n * Unsubscribe from a query.\n * Removes from Map and sends unsubscription to server if authenticated.\n */\n public unsubscribeFromQuery(queryId: string): void {\n this.queries.delete(queryId);\n if (this.config.isAuthenticated()) {\n this.config.sendMessage({\n type: 'QUERY_UNSUB',\n payload: { queryId }\n });\n }\n }\n\n /**\n * Send query subscription message to server.\n */\n private sendQuerySubscription(query: QueryHandle<any>): void {\n this.config.sendMessage({\n type: 'QUERY_SUB',\n payload: {\n queryId: query.id,\n mapName: query.getMapName(),\n query: query.getFilter()\n }\n });\n }\n\n // ============================================\n // Hybrid Query Methods\n // ============================================\n\n /**\n * Subscribe to a hybrid query (FTS + filter combination).\n */\n public subscribeToHybridQuery<T>(query: HybridQueryHandle<T>): void {\n this.hybridQueries.set(query.id, query);\n\n const filter = query.getFilter();\n const mapName = query.getMapName();\n\n // If query has FTS predicate and authenticated, send to server\n if (query.hasFTSPredicate() && this.config.isAuthenticated()) {\n this.sendHybridQuerySubscription(query.id, mapName, filter);\n }\n\n // Load initial local data\n this.runLocalHybridQuery<T>(mapName, filter).then((results) => {\n query.onResult(results, 'local');\n });\n }\n\n /**\n * Unsubscribe from a hybrid query.\n */\n public unsubscribeFromHybridQuery(queryId: string): void {\n const query = this.hybridQueries.get(queryId);\n if (query) {\n this.hybridQueries.delete(queryId);\n\n // Notify server to unsubscribe\n if (this.config.isAuthenticated() && query.hasFTSPredicate()) {\n this.config.sendMessage({\n type: 'HYBRID_QUERY_UNSUBSCRIBE',\n payload: { subscriptionId: queryId },\n });\n }\n }\n }\n\n /**\n * Send hybrid query subscription message to server.\n */\n private sendHybridQuerySubscription(\n queryId: string,\n mapName: string,\n filter: HybridQueryFilter\n ): void {\n this.config.sendMessage({\n type: 'HYBRID_QUERY_SUBSCRIBE',\n payload: {\n subscriptionId: queryId,\n mapName,\n predicate: filter.predicate,\n where: filter.where,\n sort: filter.sort,\n limit: filter.limit,\n cursor: filter.cursor,\n },\n });\n }\n\n // ============================================\n // Local Query Execution\n // ============================================\n\n /**\n * Executes a query against local storage immediately.\n */\n public async runLocalQuery(\n mapName: string,\n filter: QueryFilter\n ): Promise<{ key: string; value: any }[]> {\n // Retrieve all keys for the map\n const keys = await this.config.storageAdapter.getAllKeys();\n const mapKeys = keys.filter(k => k.startsWith(mapName + ':'));\n\n const results: { key: string; value: any }[] = [];\n for (const fullKey of mapKeys) {\n const record = await this.config.storageAdapter.get(fullKey);\n if (record && record.value) {\n // Extract actual key from \"mapName:key\"\n const actualKey = fullKey.slice(mapName.length + 1);\n\n let matches = true;\n\n // Apply 'where' (equality)\n if (filter.where) {\n for (const [k, v] of Object.entries(filter.where)) {\n if (record.value[k] !== v) {\n matches = false;\n break;\n }\n }\n }\n\n // Apply 'predicate'\n if (matches && filter.predicate) {\n if (!evaluatePredicate(filter.predicate, record.value)) {\n matches = false;\n }\n }\n\n if (matches) {\n results.push({ key: actualKey, value: record.value });\n }\n }\n }\n return results;\n }\n\n /**\n * Run a local hybrid query (FTS + filter combination).\n * For FTS predicates, returns results with score = 0 (local-only mode).\n * Server provides actual FTS scoring.\n */\n public async runLocalHybridQuery<T>(\n mapName: string,\n filter: HybridQueryFilter\n ): Promise<Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }>> {\n const results: Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }> = [];\n\n // Get all entries from the map using storage adapter\n const allKeys = await this.config.storageAdapter.getAllKeys();\n const mapPrefix = `${mapName}:`;\n const entries: Array<[string, any]> = [];\n\n for (const fullKey of allKeys) {\n if (fullKey.startsWith(mapPrefix)) {\n const key = fullKey.substring(mapPrefix.length);\n const record = await this.config.storageAdapter.get(fullKey);\n if (record) {\n entries.push([key, record]);\n }\n }\n }\n\n for (const [key, record] of entries) {\n if (record === null || record.value === null) continue;\n\n const value = record.value as T;\n\n // Evaluate predicate (including FTS predicates - basic local evaluation)\n if (filter.predicate) {\n const matches = evaluatePredicate(filter.predicate, value as Record<string, unknown>);\n if (!matches) continue;\n }\n\n // Evaluate where clause\n if (filter.where) {\n let whereMatches = true;\n for (const [field, expected] of Object.entries(filter.where)) {\n if ((value as any)[field] !== expected) {\n whereMatches = false;\n break;\n }\n }\n if (!whereMatches) continue;\n }\n\n results.push({\n key,\n value,\n score: 0, // Local doesn't have FTS scoring\n matchedTerms: [],\n });\n }\n\n // Sort results\n if (filter.sort) {\n results.sort((a, b) => {\n for (const [field, direction] of Object.entries(filter.sort!)) {\n let valA: any;\n let valB: any;\n\n if (field === '_score') {\n valA = a.score ?? 0;\n valB = b.score ?? 0;\n } else if (field === '_key') {\n valA = a.key;\n valB = b.key;\n } else {\n valA = (a.value as any)[field];\n valB = (b.value as any)[field];\n }\n\n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n // Apply limit (cursor filtering is done server-side)\n let sliced = results;\n if (filter.limit) {\n sliced = sliced.slice(0, filter.limit);\n }\n\n return sliced;\n }\n\n // ============================================\n // Re-subscription (after auth)\n // ============================================\n\n /**\n * Re-subscribe all queries after authentication.\n * Called by SyncEngine after AUTH_ACK.\n */\n public resubscribeAll(): void {\n logger.debug({ queryCount: this.queries.size, hybridCount: this.hybridQueries.size }, 'QueryManager: resubscribing all queries');\n\n // Re-subscribe standard queries\n for (const query of this.queries.values()) {\n this.sendQuerySubscription(query);\n }\n\n // Re-subscribe hybrid queries with FTS predicates\n for (const query of this.hybridQueries.values()) {\n if (query.hasFTSPredicate()) {\n this.sendHybridQuerySubscription(query.id, query.getMapName(), query.getFilter());\n }\n }\n }\n}\n","/**\n * TopicManager - Handles topic (pub/sub) operations for SyncEngine\n *\n * Responsibilities:\n * - Topic subscriptions and unsubscriptions\n * - Publishing messages to topics\n * - Queueing messages when offline\n * - Flushing queued messages after authentication\n * - Handling incoming topic messages from server\n */\n\nimport type { TopicQueueConfig } from '../SyncEngine';\nimport { TopicHandle } from '../TopicHandle';\nimport { logger } from '../utils/logger';\nimport type { ITopicManager, TopicManagerConfig } from './types';\n\n/**\n * Queued topic message for offline publishing.\n */\ninterface QueuedTopicMessage {\n topic: string;\n data: any;\n timestamp: number;\n}\n\n/**\n * TopicManager implements ITopicManager.\n *\n * Manages topic subscriptions with support for:\n * - Pub/sub pattern for real-time messaging\n * - Offline message queueing with configurable strategies\n * - Automatic resubscription after authentication\n */\nexport class TopicManager implements ITopicManager {\n private readonly config: TopicManagerConfig;\n\n // Topic subscriptions (single source of truth)\n private topics: Map<string, TopicHandle> = new Map();\n\n // Offline message queue\n private topicQueue: QueuedTopicMessage[] = [];\n\n constructor(config: TopicManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Subscribe to a topic.\n * Adds to topics Map and sends subscription to server if authenticated.\n */\n public subscribeToTopic(topic: string, handle: TopicHandle): void {\n this.topics.set(topic, handle);\n if (this.config.isAuthenticated()) {\n this.sendTopicSubscription(topic);\n }\n }\n\n /**\n * Unsubscribe from a topic.\n * Removes from Map and sends unsubscription to server if authenticated.\n */\n public unsubscribeFromTopic(topic: string): void {\n this.topics.delete(topic);\n if (this.config.isAuthenticated()) {\n this.config.sendMessage({\n type: 'TOPIC_UNSUB',\n payload: { topic }\n });\n }\n }\n\n /**\n * Publish a message to a topic.\n * Sends immediately if authenticated, otherwise queues for later.\n */\n public publishTopic(topic: string, data: any): void {\n if (this.config.isAuthenticated()) {\n this.config.sendMessage({\n type: 'TOPIC_PUB',\n payload: { topic, data }\n });\n } else {\n this.queueTopicMessage(topic, data);\n }\n }\n\n /**\n * Flush all queued topic messages.\n * Called by SyncEngine after authentication.\n */\n public flushTopicQueue(): void {\n if (this.topicQueue.length === 0) return;\n\n logger.info({ count: this.topicQueue.length }, 'Flushing queued topic messages');\n\n for (const msg of this.topicQueue) {\n this.config.sendMessage({\n type: 'TOPIC_PUB',\n payload: { topic: msg.topic, data: msg.data },\n });\n }\n\n this.topicQueue = [];\n }\n\n /**\n * Get topic queue status.\n */\n public getTopicQueueStatus(): { size: number; maxSize: number } {\n return {\n size: this.topicQueue.length,\n maxSize: this.config.topicQueueConfig.maxSize,\n };\n }\n\n /**\n * Get all subscribed topics.\n * Used for resubscription after authentication.\n */\n public getTopics(): IterableIterator<string> {\n return this.topics.keys();\n }\n\n /**\n * Re-subscribe all topics after authentication.\n * Called by SyncEngine after AUTH_ACK.\n */\n public resubscribeAll(): void {\n for (const topic of this.topics.keys()) {\n this.sendTopicSubscription(topic);\n }\n }\n\n /**\n * Handle incoming topic message from server.\n */\n public handleTopicMessage(topic: string, data: any, publisherId: string, timestamp: number): void {\n const handle = this.topics.get(topic);\n if (handle) {\n handle.onMessage(data, { publisherId, timestamp });\n }\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n /**\n * Queue a topic message for offline publishing.\n */\n private queueTopicMessage(topic: string, data: any): void {\n const message: QueuedTopicMessage = {\n topic,\n data,\n timestamp: Date.now(),\n };\n\n if (this.topicQueue.length >= this.config.topicQueueConfig.maxSize) {\n if (this.config.topicQueueConfig.strategy === 'drop-oldest') {\n const dropped = this.topicQueue.shift();\n logger.warn({ topic: dropped?.topic }, 'Dropped oldest queued topic message (queue full)');\n } else {\n logger.warn({ topic }, 'Dropped newest topic message (queue full)');\n return;\n }\n }\n\n this.topicQueue.push(message);\n logger.debug({ topic, queueSize: this.topicQueue.length }, 'Queued topic message for offline');\n }\n\n /**\n * Send topic subscription message to server.\n */\n private sendTopicSubscription(topic: string): void {\n this.config.sendMessage({\n type: 'TOPIC_SUB',\n payload: { topic }\n });\n }\n}\n","/**\n * LockManager - Handles distributed lock operations for SyncEngine\n *\n * Responsibilities:\n * - Lock acquisition with timeout\n * - Lock release with acknowledgment\n * - Pending lock request tracking\n * - Message handlers for LOCK_GRANTED and LOCK_RELEASED\n */\n\nimport { logger } from '../utils/logger';\nimport type { ILockManager, LockManagerConfig } from './types';\n\n/**\n * Pending lock request state.\n */\ninterface PendingLockRequest {\n resolve: (res: any) => void;\n reject: (err: any) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * LockManager implements ILockManager.\n *\n * Manages distributed locks with support for:\n * - Request/release pattern with fencing tokens\n * - Timeout handling for lost messages\n * - Server acknowledgment tracking\n */\nexport class LockManager implements ILockManager {\n private readonly config: LockManagerConfig;\n\n // Pending lock requests (single source of truth)\n private pendingLockRequests: Map<string, PendingLockRequest> = new Map();\n\n constructor(config: LockManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Request a distributed lock.\n */\n public requestLock(name: string, requestId: string, ttl: number): Promise<{ fencingToken: number }> {\n if (!this.config.isAuthenticated()) {\n return Promise.reject(new Error('Not connected or authenticated'));\n }\n\n return new Promise((resolve, reject) => {\n // Timeout if no response (server might be down or message lost)\n // We set a client-side timeout slightly larger than TTL if TTL is short,\n // but usually we want a separate \"Wait Timeout\".\n // For now, use a fixed 30s timeout for the *response*.\n const timer = setTimeout(() => {\n if (this.pendingLockRequests.has(requestId)) {\n this.pendingLockRequests.delete(requestId);\n reject(new Error('Lock request timed out waiting for server response'));\n }\n }, 30000);\n\n this.pendingLockRequests.set(requestId, { resolve, reject, timer });\n\n try {\n const sent = this.config.sendMessage({\n type: 'LOCK_REQUEST',\n payload: { requestId, name, ttl }\n });\n if (!sent) {\n clearTimeout(timer);\n this.pendingLockRequests.delete(requestId);\n reject(new Error('Failed to send lock request'));\n }\n } catch (e) {\n clearTimeout(timer);\n this.pendingLockRequests.delete(requestId);\n reject(e);\n }\n });\n }\n\n /**\n * Release a distributed lock.\n */\n public releaseLock(name: string, requestId: string, fencingToken: number): Promise<boolean> {\n if (!this.config.isOnline()) return Promise.resolve(false);\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pendingLockRequests.has(requestId)) {\n this.pendingLockRequests.delete(requestId);\n // Resolve false on timeout? Or reject?\n // Release is usually fire-and-forget but we wanted ACK.\n resolve(false);\n }\n }, 5000);\n\n this.pendingLockRequests.set(requestId, { resolve, reject, timer });\n\n try {\n const sent = this.config.sendMessage({\n type: 'LOCK_RELEASE',\n payload: { requestId, name, fencingToken }\n });\n if (!sent) {\n clearTimeout(timer);\n this.pendingLockRequests.delete(requestId);\n resolve(false);\n }\n } catch (e) {\n clearTimeout(timer);\n this.pendingLockRequests.delete(requestId);\n resolve(false);\n }\n });\n }\n\n /**\n * Handle lock granted message from server.\n */\n public handleLockGranted(requestId: string, fencingToken: number): void {\n const req = this.pendingLockRequests.get(requestId);\n if (req) {\n clearTimeout(req.timer);\n this.pendingLockRequests.delete(requestId);\n req.resolve({ fencingToken });\n }\n }\n\n /**\n * Handle lock released message from server.\n */\n public handleLockReleased(requestId: string, success: boolean): void {\n const req = this.pendingLockRequests.get(requestId);\n if (req) {\n clearTimeout(req.timer);\n this.pendingLockRequests.delete(requestId);\n req.resolve(success);\n }\n }\n}\n","/**\n * WriteConcernManager - Handles write concern tracking for SyncEngine\n *\n * Responsibilities:\n * - Register pending write concern promises for operations\n * - Resolve promises when server ACK is received\n * - Cancel all promises on disconnect\n * - Timeout handling for operations that don't receive ACK\n */\n\nimport type { IWriteConcernManager, WriteConcernManagerConfig } from './types';\n\n/**\n * Pending write concern promise state.\n */\ninterface PendingWriteConcernPromise {\n resolve: (result: any) => void;\n reject: (error: Error) => void;\n timeoutHandle?: ReturnType<typeof setTimeout>;\n}\n\n/**\n * WriteConcernManager implements IWriteConcernManager.\n *\n * Tracks write concern promises and resolves them when the server\n * sends OP_ACK with operation results.\n */\nexport class WriteConcernManager implements IWriteConcernManager {\n private readonly config: WriteConcernManagerConfig;\n\n // Pending write concern promises (single source of truth)\n private pendingWriteConcernPromises: Map<string, PendingWriteConcernPromise> = new Map();\n\n constructor(config: WriteConcernManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Register a pending Write Concern promise for an operation.\n * The promise will be resolved when the server sends an ACK with the operation result.\n *\n * @param opId - Operation ID\n * @param timeout - Timeout in ms (default: 5000)\n * @returns Promise that resolves with the Write Concern result\n */\n public registerWriteConcernPromise(opId: string, timeout: number = 5000): Promise<any> {\n const actualTimeout = timeout ?? this.config.defaultTimeout ?? 5000;\n\n return new Promise((resolve, reject) => {\n const timeoutHandle = setTimeout(() => {\n this.pendingWriteConcernPromises.delete(opId);\n reject(new Error(`Write Concern timeout for operation ${opId}`));\n }, actualTimeout);\n\n this.pendingWriteConcernPromises.set(opId, {\n resolve,\n reject,\n timeoutHandle,\n });\n });\n }\n\n /**\n * Resolve a pending Write Concern promise with the server result.\n *\n * @param opId - Operation ID\n * @param result - Result from server ACK\n */\n public resolveWriteConcernPromise(opId: string, result: any): void {\n const pending = this.pendingWriteConcernPromises.get(opId);\n if (pending) {\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n pending.resolve(result);\n this.pendingWriteConcernPromises.delete(opId);\n }\n }\n\n /**\n * Cancel all pending Write Concern promises (e.g., on disconnect).\n */\n public cancelAllWriteConcernPromises(error: Error): void {\n for (const [opId, pending] of this.pendingWriteConcernPromises.entries()) {\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n pending.reject(error);\n }\n this.pendingWriteConcernPromises.clear();\n }\n}\n","/**\n * CounterManager - Handles PN counter operations for SyncEngine\n *\n * Responsibilities:\n * - Counter update subscriptions\n * - Requesting initial counter state from server\n * - Syncing local counter state to server\n * - Handling incoming counter updates from server\n */\n\nimport { logger } from '../utils/logger';\nimport type { ICounterManager, CounterManagerConfig } from './types';\n\n/**\n * CounterManager implements ICounterManager.\n *\n * Manages PN counter operations with support for:\n * - Subscribe/unsubscribe to counter updates\n * - Server state synchronization\n * - State conversion between Map and object formats\n */\nexport class CounterManager implements ICounterManager {\n private readonly config: CounterManagerConfig;\n\n // Counter update listeners by name\n private counterUpdateListeners: Map<string, Set<(state: any) => void>> = new Map();\n\n constructor(config: CounterManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Subscribe to counter updates from server.\n * @param name Counter name\n * @param listener Callback when counter state is updated\n * @returns Unsubscribe function\n */\n public onCounterUpdate(name: string, listener: (state: any) => void): () => void {\n if (!this.counterUpdateListeners.has(name)) {\n this.counterUpdateListeners.set(name, new Set());\n }\n this.counterUpdateListeners.get(name)!.add(listener);\n\n return () => {\n this.counterUpdateListeners.get(name)?.delete(listener);\n if (this.counterUpdateListeners.get(name)?.size === 0) {\n this.counterUpdateListeners.delete(name);\n }\n };\n }\n\n /**\n * Request initial counter state from server.\n * @param name Counter name\n */\n public requestCounter(name: string): void {\n if (this.config.isAuthenticated()) {\n this.config.sendMessage({\n type: 'COUNTER_REQUEST',\n payload: { name }\n });\n }\n }\n\n /**\n * Sync local counter state to server.\n * @param name Counter name\n * @param state Counter state to sync\n */\n public syncCounter(name: string, state: any): void {\n if (this.config.isAuthenticated()) {\n // Convert Maps to objects for serialization\n const stateObj = {\n positive: Object.fromEntries(state.positive),\n negative: Object.fromEntries(state.negative),\n };\n\n this.config.sendMessage({\n type: 'COUNTER_SYNC',\n payload: {\n name,\n state: stateObj\n }\n });\n }\n }\n\n /**\n * Handle incoming counter update from server.\n * Called by SyncEngine for COUNTER_UPDATE and COUNTER_RESPONSE messages.\n */\n public handleCounterUpdate(name: string, stateObj: { positive: Record<string, number>; negative: Record<string, number> }): void {\n // Convert objects to Maps\n const state = {\n positive: new Map(Object.entries(stateObj.positive)),\n negative: new Map(Object.entries(stateObj.negative)),\n };\n\n const listeners = this.counterUpdateListeners.get(name);\n if (listeners) {\n for (const listener of listeners) {\n try {\n listener(state);\n } catch (e) {\n logger.error({ err: e, counterName: name }, 'Counter update listener error');\n }\n }\n }\n }\n\n /**\n * Clean up resources.\n * Clears all counter update listeners.\n */\n public close(): void {\n this.counterUpdateListeners.clear();\n }\n}\n","/**\n * EntryProcessorClient - Handles entry processor operations for SyncEngine\n *\n * Responsibilities:\n * - Execute entry processors on single keys\n * - Execute entry processors on multiple keys (batch)\n * - Handle entry processor responses from server\n * - Timeout handling for requests\n * - Cleanup on close\n */\n\nimport type { EntryProcessorDef, EntryProcessorResult, EntryProcessKeyResult } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\nimport type { IEntryProcessorClient, EntryProcessorClientConfig } from './types';\n\n/**\n * Default timeout for entry processor requests (ms).\n */\nconst DEFAULT_PROCESSOR_TIMEOUT = 30000;\n\n/**\n * Pending entry processor request state.\n */\ninterface PendingProcessorRequest<R> {\n resolve: (result: EntryProcessorResult<R>) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Pending batch entry processor request state.\n */\ninterface PendingBatchProcessorRequest<R> {\n resolve: (results: Map<string, EntryProcessorResult<R>>) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/**\n * EntryProcessorClient implements IEntryProcessorClient.\n *\n * Manages entry processor operations with support for:\n * - Single key processing with atomic execution\n * - Batch processing for multiple keys\n * - Request/response pattern with timeout handling\n */\nexport class EntryProcessorClient implements IEntryProcessorClient {\n private readonly config: EntryProcessorClientConfig;\n private readonly timeoutMs: number;\n\n // Pending entry processor requests by requestId\n private pendingProcessorRequests: Map<string, PendingProcessorRequest<any>> = new Map();\n\n // Pending batch entry processor requests by requestId\n private pendingBatchProcessorRequests: Map<string, PendingBatchProcessorRequest<any>> = new Map();\n\n constructor(config: EntryProcessorClientConfig) {\n this.config = config;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_PROCESSOR_TIMEOUT;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Execute an entry processor on a single key atomically.\n *\n * @param mapName Name of the map\n * @param key Key to process\n * @param processor Processor definition\n * @returns Promise resolving to the processor result\n */\n public async executeOnKey<V, R = V>(\n mapName: string,\n key: string,\n processor: EntryProcessorDef<V, R>,\n ): Promise<EntryProcessorResult<R>> {\n if (!this.config.isAuthenticated()) {\n return {\n success: false,\n error: 'Not connected to server',\n };\n }\n\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n // Set timeout\n const timeout = setTimeout(() => {\n this.pendingProcessorRequests.delete(requestId);\n reject(new Error('Entry processor request timed out'));\n }, this.timeoutMs);\n\n // Store pending request\n this.pendingProcessorRequests.set(requestId, {\n resolve: (result) => {\n clearTimeout(timeout);\n resolve(result);\n },\n reject,\n timeout,\n });\n\n // Send request\n const sent = this.config.sendMessage({\n type: 'ENTRY_PROCESS',\n requestId,\n mapName,\n key,\n processor: {\n name: processor.name,\n code: processor.code,\n args: processor.args,\n },\n }, key);\n\n if (!sent) {\n this.pendingProcessorRequests.delete(requestId);\n clearTimeout(timeout);\n reject(new Error('Failed to send entry processor request'));\n }\n });\n }\n\n /**\n * Execute an entry processor on multiple keys.\n *\n * @param mapName Name of the map\n * @param keys Keys to process\n * @param processor Processor definition\n * @returns Promise resolving to a map of key -> result\n */\n public async executeOnKeys<V, R = V>(\n mapName: string,\n keys: string[],\n processor: EntryProcessorDef<V, R>,\n ): Promise<Map<string, EntryProcessorResult<R>>> {\n if (!this.config.isAuthenticated()) {\n const results = new Map<string, EntryProcessorResult<R>>();\n const error: EntryProcessorResult<R> = {\n success: false,\n error: 'Not connected to server',\n };\n for (const key of keys) {\n results.set(key, error);\n }\n return results;\n }\n\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n // Set timeout\n const timeout = setTimeout(() => {\n this.pendingBatchProcessorRequests.delete(requestId);\n reject(new Error('Entry processor batch request timed out'));\n }, this.timeoutMs);\n\n // Store pending request\n this.pendingBatchProcessorRequests.set(requestId, {\n resolve: (results) => {\n clearTimeout(timeout);\n resolve(results);\n },\n reject,\n timeout,\n });\n\n // Send request\n const sent = this.config.sendMessage({\n type: 'ENTRY_PROCESS_BATCH',\n requestId,\n mapName,\n keys,\n processor: {\n name: processor.name,\n code: processor.code,\n args: processor.args,\n },\n });\n\n if (!sent) {\n this.pendingBatchProcessorRequests.delete(requestId);\n clearTimeout(timeout);\n reject(new Error('Failed to send entry processor batch request'));\n }\n });\n }\n\n /**\n * Handle entry processor response from server.\n * Called by SyncEngine for ENTRY_PROCESS_RESPONSE messages.\n */\n public handleEntryProcessResponse(message: {\n requestId: string;\n success: boolean;\n result?: unknown;\n newValue?: unknown;\n error?: string;\n }): void {\n const pending = this.pendingProcessorRequests.get(message.requestId);\n if (pending) {\n this.pendingProcessorRequests.delete(message.requestId);\n pending.resolve({\n success: message.success,\n result: message.result,\n newValue: message.newValue,\n error: message.error,\n });\n }\n }\n\n /**\n * Handle entry processor batch response from server.\n * Called by SyncEngine for ENTRY_PROCESS_BATCH_RESPONSE messages.\n */\n public handleEntryProcessBatchResponse(message: {\n requestId: string;\n results: Record<string, EntryProcessKeyResult>;\n }): void {\n const pending = this.pendingBatchProcessorRequests.get(message.requestId);\n if (pending) {\n this.pendingBatchProcessorRequests.delete(message.requestId);\n\n // Convert Record to Map\n const resultsMap = new Map<string, EntryProcessorResult<any>>();\n for (const [key, result] of Object.entries(message.results)) {\n resultsMap.set(key, {\n success: result.success,\n result: result.result,\n newValue: result.newValue,\n error: result.error,\n });\n }\n\n pending.resolve(resultsMap);\n }\n }\n\n /**\n * Clean up resources.\n * Clears pending timeouts without rejecting promises to match original SyncEngine behavior.\n * Note: This may leave promises hanging, but maintains backward compatibility with tests.\n */\n public close(error?: Error): void {\n // Only clear timeouts, don't reject promises to avoid unhandled rejections in tests\n for (const [requestId, pending] of this.pendingProcessorRequests.entries()) {\n clearTimeout(pending.timeout);\n }\n this.pendingProcessorRequests.clear();\n\n for (const [requestId, pending] of this.pendingBatchProcessorRequests.entries()) {\n clearTimeout(pending.timeout);\n }\n this.pendingBatchProcessorRequests.clear();\n }\n}\n","/**\n * SearchClient - Handles full-text search operations for SyncEngine\n *\n * Responsibilities:\n * - One-shot BM25 search requests\n * - Handle search responses from server\n * - Timeout handling for requests\n * - Cleanup on close\n */\n\nimport type { SearchOptions } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\nimport type { ISearchClient, SearchClientConfig, SearchResult } from './types';\n\n/**\n * Default timeout for search requests (ms).\n */\nconst DEFAULT_SEARCH_TIMEOUT = 30000;\n\n/**\n * Pending search request state.\n */\ninterface PendingSearchRequest {\n resolve: (result: SearchResult<unknown>[]) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/**\n * SearchClient implements ISearchClient.\n *\n * Manages full-text search operations with support for:\n * - One-shot BM25 search with configurable options\n * - Request/response pattern with timeout handling\n */\nexport class SearchClient implements ISearchClient {\n private readonly config: SearchClientConfig;\n private readonly timeoutMs: number;\n\n // Pending search requests by requestId\n private pendingSearchRequests: Map<string, PendingSearchRequest> = new Map();\n\n constructor(config: SearchClientConfig) {\n this.config = config;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_SEARCH_TIMEOUT;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Perform a one-shot BM25 search on the server.\n *\n * @param mapName Name of the map to search\n * @param query Search query text\n * @param options Search options (limit, minScore, boost)\n * @returns Promise resolving to search results\n */\n public async search<T>(\n mapName: string,\n query: string,\n options?: SearchOptions\n ): Promise<SearchResult<T>[]> {\n if (!this.config.isAuthenticated()) {\n throw new Error('Not connected to server');\n }\n\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n // Set timeout\n const timeout = setTimeout(() => {\n this.pendingSearchRequests.delete(requestId);\n reject(new Error('Search request timed out'));\n }, this.timeoutMs);\n\n // Store pending request\n this.pendingSearchRequests.set(requestId, {\n resolve: (results) => {\n clearTimeout(timeout);\n resolve(results as SearchResult<T>[]);\n },\n reject: (error) => {\n clearTimeout(timeout);\n reject(error);\n },\n timeout,\n });\n\n // Send search request\n const sent = this.config.sendMessage({\n type: 'SEARCH',\n payload: {\n requestId,\n mapName,\n query,\n options,\n },\n });\n\n if (!sent) {\n this.pendingSearchRequests.delete(requestId);\n clearTimeout(timeout);\n reject(new Error('Failed to send search request'));\n }\n });\n }\n\n /**\n * Handle search response from server.\n * Called by SyncEngine for SEARCH_RESP messages.\n */\n public handleSearchResponse(payload: {\n requestId: string;\n results: SearchResult<unknown>[];\n totalCount: number;\n error?: string;\n }): void {\n const pending = this.pendingSearchRequests.get(payload.requestId);\n if (pending) {\n this.pendingSearchRequests.delete(payload.requestId);\n\n if (payload.error) {\n pending.reject(new Error(payload.error));\n } else {\n pending.resolve(payload.results);\n }\n }\n }\n\n /**\n * Clean up resources.\n * Clears pending timeouts without rejecting promises to match original SyncEngine behavior.\n * Note: This may leave promises hanging, but maintains backward compatibility with tests.\n */\n public close(error?: Error): void {\n // Only clear timeouts, don't reject promises to avoid unhandled rejections in tests\n for (const [requestId, pending] of this.pendingSearchRequests.entries()) {\n clearTimeout(pending.timeout);\n }\n this.pendingSearchRequests.clear();\n }\n}\n","import { LWWMap } from '@topgunbuild/core';\nimport type { IMerkleSyncHandler, MerkleSyncHandlerConfig } from './types';\nimport { logger } from '../utils/logger';\n\n/**\n * MerkleSyncHandler\n *\n * Handles Merkle tree synchronization protocol messages for LWWMap.\n * Manages sync state, root hash comparison, bucket traversal, and leaf merging.\n */\nexport class MerkleSyncHandler implements IMerkleSyncHandler {\n private readonly config: MerkleSyncHandlerConfig;\n private lastSyncTimestamp: number = 0;\n\n constructor(config: MerkleSyncHandlerConfig) {\n this.config = config;\n }\n\n /**\n * Handle SYNC_RESET_REQUIRED message from server.\n * Resets the map and triggers a fresh sync.\n */\n public async handleSyncResetRequired(payload: { mapName: string }): Promise<void> {\n const { mapName } = payload;\n logger.warn({ mapName }, 'Sync Reset Required due to GC Age');\n await this.config.resetMap(mapName);\n // Trigger re-sync as fresh\n this.config.sendMessage({\n type: 'SYNC_INIT',\n mapName,\n lastSyncTimestamp: 0\n });\n }\n\n /**\n * Handle SYNC_RESP_ROOT message from server.\n * Compares root hashes and requests buckets if mismatch detected.\n */\n public async handleSyncRespRoot(payload: { mapName: string; rootHash: number; timestamp?: any }): Promise<void> {\n const { mapName, rootHash, timestamp } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof LWWMap) {\n const localRootHash = map.getMerkleTree().getRootHash();\n if (localRootHash !== rootHash) {\n logger.info({ mapName, localRootHash, remoteRootHash: rootHash }, 'Root hash mismatch, requesting buckets');\n this.config.sendMessage({\n type: 'MERKLE_REQ_BUCKET',\n payload: { mapName, path: '' }\n });\n } else {\n logger.info({ mapName }, 'Map is in sync');\n }\n }\n // Update HLC with server timestamp\n if (timestamp) {\n await this.config.onTimestampUpdate(timestamp);\n }\n }\n\n /**\n * Handle SYNC_RESP_BUCKETS message from server.\n * Compares bucket hashes and requests mismatched buckets.\n */\n public handleSyncRespBuckets(payload: { mapName: string; path: string; buckets: Record<string, number> }): void {\n const { mapName, path, buckets } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof LWWMap) {\n const tree = map.getMerkleTree();\n const localBuckets = tree.getBuckets(path);\n\n for (const [bucketKey, remoteHash] of Object.entries(buckets)) {\n const localHash = localBuckets[bucketKey] || 0;\n if (localHash !== remoteHash) {\n const newPath = path + bucketKey;\n this.config.sendMessage({\n type: 'MERKLE_REQ_BUCKET',\n payload: { mapName, path: newPath }\n });\n }\n }\n }\n }\n\n /**\n * Handle SYNC_RESP_LEAF message from server.\n * Merges leaf records into local map and persists to storage.\n */\n public async handleSyncRespLeaf(payload: { mapName: string; records: Array<{ key: string; record: any }> }): Promise<void> {\n const { mapName, records } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof LWWMap) {\n let updateCount = 0;\n for (const { key, record } of records) {\n // Merge into local map\n const updated = map.merge(key, record);\n if (updated) {\n updateCount++;\n // Persist to storage\n await this.config.storageAdapter.put(`${mapName}:${key}`, record);\n }\n }\n if (updateCount > 0) {\n logger.info({ mapName, count: updateCount }, 'Synced records from server');\n }\n }\n }\n\n /**\n * Send SYNC_INIT message to server to start sync.\n * Encapsulates sync init message construction.\n */\n public sendSyncInit(mapName: string, lastSyncTimestamp: number): void {\n this.lastSyncTimestamp = lastSyncTimestamp;\n logger.info({ mapName }, 'Starting Merkle sync for LWWMap');\n this.config.sendMessage({\n type: 'SYNC_INIT',\n mapName,\n lastSyncTimestamp\n });\n }\n\n /**\n * Get the last sync timestamp for debugging/testing.\n */\n public getLastSyncTimestamp(): number {\n return this.lastSyncTimestamp;\n }\n}\n","import { ORMap } from '@topgunbuild/core';\nimport type { ORMapRecord } from '@topgunbuild/core';\nimport type { IORMapSyncHandler, ORMapSyncHandlerConfig } from './types';\nimport { logger } from '../utils/logger';\n\n/**\n * ORMapSyncHandler\n *\n * Handles Merkle tree synchronization protocol messages for ORMap.\n * Manages sync state, root hash comparison, bucket traversal, leaf merging,\n * and bidirectional diff exchange.\n */\nexport class ORMapSyncHandler implements IORMapSyncHandler {\n private readonly config: ORMapSyncHandlerConfig;\n private lastSyncTimestamp: number = 0;\n\n constructor(config: ORMapSyncHandlerConfig) {\n this.config = config;\n }\n\n /**\n * Handle ORMAP_SYNC_RESP_ROOT message from server.\n * Compares root hashes and requests buckets if mismatch detected.\n */\n public async handleORMapSyncRespRoot(payload: { mapName: string; rootHash: number; timestamp?: any }): Promise<void> {\n const { mapName, rootHash, timestamp } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n const localTree = map.getMerkleTree();\n const localRootHash = localTree.getRootHash();\n\n if (localRootHash !== rootHash) {\n logger.info({ mapName, localRootHash, remoteRootHash: rootHash }, 'ORMap root hash mismatch, requesting buckets');\n this.config.sendMessage({\n type: 'ORMAP_MERKLE_REQ_BUCKET',\n payload: { mapName, path: '' }\n });\n } else {\n logger.info({ mapName }, 'ORMap is in sync');\n }\n }\n // Update HLC with server timestamp\n if (timestamp) {\n await this.config.onTimestampUpdate(timestamp);\n }\n }\n\n /**\n * Handle ORMAP_SYNC_RESP_BUCKETS message from server.\n * Compares bucket hashes and requests mismatched buckets.\n * Also pushes local data that server doesn't have.\n */\n public async handleORMapSyncRespBuckets(payload: { mapName: string; path: string; buckets: Record<string, number> }): Promise<void> {\n const { mapName, path, buckets } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n const tree = map.getMerkleTree();\n const localBuckets = tree.getBuckets(path);\n\n for (const [bucketKey, remoteHash] of Object.entries(buckets)) {\n const localHash = localBuckets[bucketKey] || 0;\n if (localHash !== remoteHash) {\n const newPath = path + bucketKey;\n this.config.sendMessage({\n type: 'ORMAP_MERKLE_REQ_BUCKET',\n payload: { mapName, path: newPath }\n });\n }\n }\n\n // Also check for buckets that exist locally but not on remote\n for (const [bucketKey, localHash] of Object.entries(localBuckets)) {\n if (!(bucketKey in buckets) && localHash !== 0) {\n // Local has data that remote doesn't - need to push\n const newPath = path + bucketKey;\n const keys = tree.getKeysInBucket(newPath);\n if (keys.length > 0) {\n await this.pushORMapDiff(mapName, keys, map);\n }\n }\n }\n }\n }\n\n /**\n * Handle ORMAP_SYNC_RESP_LEAF message from server.\n * Merges leaf entries into local map and pushes local diff back.\n */\n public async handleORMapSyncRespLeaf(payload: { mapName: string; entries: Array<{ key: string; records: any[]; tombstones: string[] }> }): Promise<void> {\n const { mapName, entries } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n let totalAdded = 0;\n let totalUpdated = 0;\n\n for (const entry of entries) {\n const { key, records, tombstones } = entry;\n const result = map.mergeKey(key, records, tombstones);\n totalAdded += result.added;\n totalUpdated += result.updated;\n }\n\n if (totalAdded > 0 || totalUpdated > 0) {\n logger.info({ mapName, added: totalAdded, updated: totalUpdated }, 'Synced ORMap records from server');\n }\n\n // Now push any local records that server might not have\n const keysToCheck = entries.map((e: { key: string }) => e.key);\n await this.pushORMapDiff(mapName, keysToCheck, map);\n }\n }\n\n /**\n * Handle ORMAP_DIFF_RESPONSE message from server.\n * Merges diff entries into local map.\n */\n public async handleORMapDiffResponse(payload: { mapName: string; entries: Array<{ key: string; records: any[]; tombstones: string[] }> }): Promise<void> {\n const { mapName, entries } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n let totalAdded = 0;\n let totalUpdated = 0;\n\n for (const entry of entries) {\n const { key, records, tombstones } = entry;\n const result = map.mergeKey(key, records, tombstones);\n totalAdded += result.added;\n totalUpdated += result.updated;\n }\n\n if (totalAdded > 0 || totalUpdated > 0) {\n logger.info({ mapName, added: totalAdded, updated: totalUpdated }, 'Merged ORMap diff from server');\n }\n }\n }\n\n /**\n * Push local ORMap diff to server for the given keys.\n * Sends local records and tombstones that the server might not have.\n */\n public async pushORMapDiff(\n mapName: string,\n keys: string[],\n map: ORMap<any, any>\n ): Promise<void> {\n const entries: Array<{\n key: string;\n records: ORMapRecord<any>[];\n tombstones: string[];\n }> = [];\n\n const snapshot = map.getSnapshot();\n\n for (const key of keys) {\n const recordsMap = map.getRecordsMap(key);\n if (recordsMap && recordsMap.size > 0) {\n // Get records as array\n const records = Array.from(recordsMap.values());\n\n // Get tombstones relevant to this key's records\n // (tombstones that match tags that were in this key)\n const tombstones: string[] = [];\n for (const tag of snapshot.tombstones) {\n // Include all tombstones - server will filter\n tombstones.push(tag);\n }\n\n entries.push({\n key,\n records,\n tombstones\n });\n }\n }\n\n if (entries.length > 0) {\n this.config.sendMessage({\n type: 'ORMAP_PUSH_DIFF',\n payload: {\n mapName,\n entries\n }\n });\n logger.debug({ mapName, keyCount: entries.length }, 'Pushed ORMap diff to server');\n }\n }\n\n /**\n * Send ORMAP_SYNC_INIT message to server to start sync.\n * Encapsulates sync init message construction.\n */\n public sendSyncInit(mapName: string, lastSyncTimestamp: number): void {\n this.lastSyncTimestamp = lastSyncTimestamp;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n logger.info({ mapName }, 'Starting Merkle sync for ORMap');\n const tree = map.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n // Build bucket hashes for all non-empty buckets at depth 0\n const bucketHashes: Record<string, number> = tree.getBuckets('');\n\n this.config.sendMessage({\n type: 'ORMAP_SYNC_INIT',\n mapName,\n rootHash,\n bucketHashes,\n lastSyncTimestamp\n });\n }\n }\n\n /**\n * Get the last sync timestamp for debugging/testing.\n */\n public getLastSyncTimestamp(): number {\n return this.lastSyncTimestamp;\n }\n}\n","/**\n * MessageRouter - Routes incoming server messages to appropriate handlers.\n *\n * This module replaces the large switch statement in SyncEngine.handleServerMessage()\n * with a declarative, type-based routing system.\n *\n * Features:\n * - Register handlers for message types\n * - Route incoming messages to appropriate handlers\n * - Support async handlers\n * - Handle unregistered message types via fallback\n */\n\nimport { logger } from '../utils/logger';\nimport type { IMessageRouter, MessageHandler, MessageRouterConfig } from './types';\n\n/**\n * MessageRouter implementation.\n * Routes server messages to registered handlers based on message type.\n */\nexport class MessageRouter implements IMessageRouter {\n private readonly handlers: Map<string, MessageHandler>;\n private readonly onUnhandled?: (message: any) => void;\n\n constructor(config: MessageRouterConfig = {}) {\n // Copy handlers from config or create new Map\n this.handlers = config.handlers\n ? new Map(config.handlers)\n : new Map();\n this.onUnhandled = config.onUnhandled;\n }\n\n /**\n * Register a handler for a message type.\n * @param type - Message type to handle\n * @param handler - Handler function\n */\n registerHandler(type: string, handler: MessageHandler): void {\n if (this.handlers.has(type)) {\n logger.warn({ type }, 'Overwriting existing handler for message type');\n }\n this.handlers.set(type, handler);\n }\n\n /**\n * Register multiple handlers at once.\n * @param handlers - Record of type -> handler\n */\n registerHandlers(handlers: Record<string, MessageHandler>): void {\n for (const [type, handler] of Object.entries(handlers)) {\n this.registerHandler(type, handler);\n }\n }\n\n /**\n * Route a message to its registered handler.\n * Returns true if handled, false if no handler found.\n * @param message - Message to route\n * @returns Promise resolving to true if handled\n */\n async route(message: any): Promise<boolean> {\n const type = message?.type;\n if (!type) {\n logger.warn({ message }, 'Cannot route message without type');\n return false;\n }\n\n const handler = this.handlers.get(type);\n if (!handler) {\n // Call fallback if provided\n if (this.onUnhandled) {\n this.onUnhandled(message);\n }\n return false;\n }\n\n try {\n // Await in case handler is async\n await handler(message);\n return true;\n } catch (error) {\n logger.error({ type, error }, 'Error in message handler');\n // Still return true since handler was found (just errored)\n return true;\n }\n }\n\n /**\n * Check if a handler is registered for a message type.\n * @param type - Message type to check\n * @returns true if handler exists\n */\n hasHandler(type: string): boolean {\n return this.handlers.has(type);\n }\n\n /**\n * Get the count of registered handlers.\n * Useful for debugging/testing.\n */\n get handlerCount(): number {\n return this.handlers.size;\n }\n\n /**\n * Get all registered message types.\n * Useful for debugging/testing.\n */\n getRegisteredTypes(): string[] {\n return Array.from(this.handlers.keys());\n }\n}\n","/**\n * Client message handler registration.\n * Configures all message type -> handler mappings for SyncEngine.\n */\n\nimport type { IMessageRouter } from './types';\nimport type {\n AuthFailMessage,\n OpAckMessage,\n QueryRespMessage,\n QueryUpdateMessage,\n ServerEventMessage,\n ServerBatchEventMessage,\n GcPruneMessage,\n HybridQueryRespPayload,\n HybridQueryDeltaPayload,\n SearchRespPayload,\n SyncRespRootPayload,\n SyncRespBucketsPayload,\n SyncRespLeafPayload,\n SyncResetRequiredPayload,\n ORMapSyncRespRootPayload,\n ORMapSyncRespBucketsPayload,\n ORMapSyncRespLeafPayload,\n ORMapDiffResponsePayload,\n EntryProcessResponse,\n EntryProcessBatchResponse,\n RegisterResolverResponse,\n UnregisterResolverResponse,\n ListResolversResponse,\n MergeRejectedMessage,\n} from '@topgunbuild/core';\n\n/**\n * SyncEngine methods used as handler delegates.\n * These methods are called directly from message handlers.\n */\nexport interface MessageHandlerDelegates {\n sendAuth(): Promise<void>;\n handleAuthAck(): void;\n handleAuthFail(message: AuthFailMessage): void;\n handleOpAck(message: OpAckMessage): void;\n handleQueryResp(message: QueryRespMessage): void;\n handleQueryUpdate(message: QueryUpdateMessage): void;\n handleServerEvent(message: ServerEventMessage): Promise<void>;\n handleServerBatchEvent(message: ServerBatchEventMessage): Promise<void>;\n handleGcPrune(message: GcPruneMessage): Promise<void>;\n handleHybridQueryResponse(payload: HybridQueryRespPayload): void;\n handleHybridQueryDelta(payload: HybridQueryDeltaPayload): void;\n}\n\n/**\n * Manager instances that receive delegated message handling.\n */\nexport interface ManagerDelegates {\n topicManager: {\n handleTopicMessage(topic: string, data: unknown, publisherId: string, timestamp: number): void;\n };\n lockManager: {\n handleLockGranted(requestId: string, fencingToken: number): void;\n handleLockReleased(requestId: string, success: boolean): void;\n };\n counterManager: {\n handleCounterUpdate(name: string, state: { positive: Record<string, number>; negative: Record<string, number> }): void;\n };\n entryProcessorClient: {\n handleEntryProcessResponse(message: EntryProcessResponse): void;\n handleEntryProcessBatchResponse(message: EntryProcessBatchResponse): void;\n };\n conflictResolverClient: {\n handleRegisterResponse(message: RegisterResolverResponse): void;\n handleUnregisterResponse(message: UnregisterResolverResponse): void;\n handleListResponse(message: ListResolversResponse): void;\n handleMergeRejected(message: MergeRejectedMessage): void;\n };\n searchClient: {\n handleSearchResponse(payload: SearchRespPayload): void;\n };\n merkleSyncHandler: {\n handleSyncRespRoot(payload: SyncRespRootPayload): void;\n handleSyncRespBuckets(payload: SyncRespBucketsPayload): void;\n handleSyncRespLeaf(payload: SyncRespLeafPayload): void;\n handleSyncResetRequired(payload: SyncResetRequiredPayload): void;\n };\n orMapSyncHandler: {\n handleORMapSyncRespRoot(payload: ORMapSyncRespRootPayload): void;\n handleORMapSyncRespBuckets(payload: ORMapSyncRespBucketsPayload): void;\n handleORMapSyncRespLeaf(payload: ORMapSyncRespLeafPayload): void;\n handleORMapDiffResponse(payload: ORMapDiffResponsePayload): void;\n };\n}\n\n/**\n * All expected client message types.\n * Used for testing that all types are registered.\n */\nexport const CLIENT_MESSAGE_TYPES = [\n 'AUTH_REQUIRED', 'AUTH_ACK', 'AUTH_FAIL',\n 'PONG',\n 'OP_ACK',\n 'SYNC_RESP_ROOT', 'SYNC_RESP_BUCKETS', 'SYNC_RESP_LEAF', 'SYNC_RESET_REQUIRED',\n 'ORMAP_SYNC_RESP_ROOT', 'ORMAP_SYNC_RESP_BUCKETS', 'ORMAP_SYNC_RESP_LEAF', 'ORMAP_DIFF_RESPONSE',\n 'QUERY_RESP', 'QUERY_UPDATE',\n 'SERVER_EVENT', 'SERVER_BATCH_EVENT',\n 'TOPIC_MESSAGE',\n 'LOCK_GRANTED', 'LOCK_RELEASED',\n 'GC_PRUNE',\n 'COUNTER_UPDATE', 'COUNTER_RESPONSE',\n 'ENTRY_PROCESS_RESPONSE', 'ENTRY_PROCESS_BATCH_RESPONSE',\n 'REGISTER_RESOLVER_RESPONSE', 'UNREGISTER_RESOLVER_RESPONSE', 'LIST_RESOLVERS_RESPONSE', 'MERGE_REJECTED',\n 'SEARCH_RESP', 'SEARCH_UPDATE',\n 'HYBRID_QUERY_RESP', 'HYBRID_QUERY_DELTA',\n] as const;\n\n/**\n * Register all client message handlers with the router.\n *\n * @param router - MessageRouter instance\n * @param delegates - SyncEngine handler methods\n * @param managers - Manager instances for delegation\n */\nexport function registerClientMessageHandlers(\n router: IMessageRouter,\n delegates: MessageHandlerDelegates,\n managers: ManagerDelegates\n): void {\n router.registerHandlers({\n // AUTH handlers\n 'AUTH_REQUIRED': () => delegates.sendAuth(),\n 'AUTH_ACK': () => delegates.handleAuthAck(),\n 'AUTH_FAIL': (msg) => delegates.handleAuthFail(msg),\n\n // HEARTBEAT - handled by WebSocketManager, no-op here\n 'PONG': () => {},\n\n // SYNC handlers\n 'OP_ACK': (msg) => delegates.handleOpAck(msg),\n 'SYNC_RESP_ROOT': (msg) => managers.merkleSyncHandler.handleSyncRespRoot(msg.payload),\n 'SYNC_RESP_BUCKETS': (msg) => managers.merkleSyncHandler.handleSyncRespBuckets(msg.payload),\n 'SYNC_RESP_LEAF': (msg) => managers.merkleSyncHandler.handleSyncRespLeaf(msg.payload),\n 'SYNC_RESET_REQUIRED': (msg) => managers.merkleSyncHandler.handleSyncResetRequired(msg.payload),\n\n // ORMAP SYNC handlers\n 'ORMAP_SYNC_RESP_ROOT': (msg) => managers.orMapSyncHandler.handleORMapSyncRespRoot(msg.payload),\n 'ORMAP_SYNC_RESP_BUCKETS': (msg) => managers.orMapSyncHandler.handleORMapSyncRespBuckets(msg.payload),\n 'ORMAP_SYNC_RESP_LEAF': (msg) => managers.orMapSyncHandler.handleORMapSyncRespLeaf(msg.payload),\n 'ORMAP_DIFF_RESPONSE': (msg) => managers.orMapSyncHandler.handleORMapDiffResponse(msg.payload),\n\n // QUERY handlers\n 'QUERY_RESP': (msg) => delegates.handleQueryResp(msg),\n 'QUERY_UPDATE': (msg) => delegates.handleQueryUpdate(msg),\n\n // EVENT handlers\n 'SERVER_EVENT': (msg) => delegates.handleServerEvent(msg),\n 'SERVER_BATCH_EVENT': (msg) => delegates.handleServerBatchEvent(msg),\n\n // TOPIC handlers\n 'TOPIC_MESSAGE': (msg) => {\n const { topic, data, publisherId, timestamp } = msg.payload;\n managers.topicManager.handleTopicMessage(topic, data, publisherId, timestamp);\n },\n\n // LOCK handlers\n 'LOCK_GRANTED': (msg) => {\n const { requestId, fencingToken } = msg.payload;\n managers.lockManager.handleLockGranted(requestId, fencingToken);\n },\n 'LOCK_RELEASED': (msg) => {\n const { requestId, success } = msg.payload;\n managers.lockManager.handleLockReleased(requestId, success);\n },\n\n // GC handler\n 'GC_PRUNE': (msg) => delegates.handleGcPrune(msg),\n\n // COUNTER handlers\n 'COUNTER_UPDATE': (msg) => {\n const { name, state } = msg.payload;\n managers.counterManager.handleCounterUpdate(name, state);\n },\n 'COUNTER_RESPONSE': (msg) => {\n const { name, state } = msg.payload;\n managers.counterManager.handleCounterUpdate(name, state);\n },\n\n // PROCESSOR handlers\n 'ENTRY_PROCESS_RESPONSE': (msg) => {\n managers.entryProcessorClient.handleEntryProcessResponse(msg);\n },\n 'ENTRY_PROCESS_BATCH_RESPONSE': (msg) => {\n managers.entryProcessorClient.handleEntryProcessBatchResponse(msg);\n },\n\n // RESOLVER handlers\n 'REGISTER_RESOLVER_RESPONSE': (msg) => {\n managers.conflictResolverClient.handleRegisterResponse(msg);\n },\n 'UNREGISTER_RESOLVER_RESPONSE': (msg) => {\n managers.conflictResolverClient.handleUnregisterResponse(msg);\n },\n 'LIST_RESOLVERS_RESPONSE': (msg) => {\n managers.conflictResolverClient.handleListResponse(msg);\n },\n 'MERGE_REJECTED': (msg) => {\n managers.conflictResolverClient.handleMergeRejected(msg);\n },\n\n // SEARCH handlers\n 'SEARCH_RESP': (msg) => {\n managers.searchClient.handleSearchResponse(msg.payload);\n },\n 'SEARCH_UPDATE': () => {\n // SEARCH_UPDATE is handled by SearchHandle via emitMessage, no-op here\n },\n\n // HYBRID handlers\n 'HYBRID_QUERY_RESP': (msg) => delegates.handleHybridQueryResponse(msg.payload),\n 'HYBRID_QUERY_DELTA': (msg) => delegates.handleHybridQueryDelta(msg.payload),\n });\n}\n","import { LWWMap, ORMap } from '@topgunbuild/core';\nimport type { ORMapRecord, LWWRecord, EntryProcessorDef, EntryProcessorResult, SearchOptions } from '@topgunbuild/core';\nimport type { IStorageAdapter } from './IStorageAdapter';\nimport { SyncEngine } from './SyncEngine';\nimport type { BackoffConfig } from './SyncEngine';\nimport { QueryHandle } from './QueryHandle';\nimport type { QueryFilter } from './QueryHandle';\nimport { DistributedLock } from './DistributedLock';\nimport { TopicHandle } from './TopicHandle';\nimport { PNCounterHandle } from './PNCounterHandle';\nimport { EventJournalReader } from './EventJournalReader';\nimport { SearchHandle } from './SearchHandle';\nimport { HybridQueryHandle } from './HybridQueryHandle';\nimport type { HybridQueryFilter } from './HybridQueryHandle';\nimport { logger } from './utils/logger';\nimport { SyncState } from './SyncState';\nimport type { StateChangeEvent } from './SyncStateMachine';\nimport type {\n BackpressureConfig,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n} from './BackpressureConfig';\nimport { ClusterClient } from './cluster/ClusterClient';\nimport { SingleServerProvider } from './connection/SingleServerProvider';\nimport type { NodeHealth } from '@topgunbuild/core';\n\n// ============================================\n// Cluster Configuration Types\n// ============================================\n\n/**\n * Cluster mode configuration for TopGunClient.\n * When provided, the client connects to multiple nodes with partition-aware routing.\n */\nexport interface TopGunClusterConfig {\n /** Initial seed nodes (at least one required) */\n seeds: string[];\n\n /** Connection pool size per node (default: 1) */\n connectionsPerNode?: number;\n\n /** Enable smart routing to partition owner (default: true) */\n smartRouting?: boolean;\n\n /** Partition map refresh interval in ms (default: 30000) */\n partitionMapRefreshMs?: number;\n\n /** Connection timeout per node in ms (default: 5000) */\n connectionTimeoutMs?: number;\n\n /** Retry attempts for failed operations (default: 3) */\n retryAttempts?: number;\n}\n\n/**\n * Default values for cluster configuration\n */\nexport const DEFAULT_CLUSTER_CONFIG: Required<Omit<TopGunClusterConfig, 'seeds'>> = {\n connectionsPerNode: 1,\n smartRouting: true,\n partitionMapRefreshMs: 30000,\n connectionTimeoutMs: 5000,\n retryAttempts: 3,\n};\n\n/**\n * TopGunClient configuration options\n */\nexport interface TopGunClientConfig {\n /** Unique node identifier (auto-generated if not provided) */\n nodeId?: string;\n\n /** Single-server mode: WebSocket URL to connect to */\n serverUrl?: string;\n\n /** Cluster mode: Configuration for multi-node routing */\n cluster?: TopGunClusterConfig;\n\n /** Storage adapter for local persistence */\n storage: IStorageAdapter;\n\n /** Backoff configuration for reconnection */\n backoff?: Partial<BackoffConfig>;\n\n /** Backpressure configuration */\n backpressure?: Partial<BackpressureConfig>;\n}\n\nexport class TopGunClient {\n private readonly nodeId: string;\n private readonly syncEngine: SyncEngine;\n private readonly maps: Map<string, LWWMap<any, any> | ORMap<any, any>> = new Map();\n private readonly storageAdapter: IStorageAdapter;\n private readonly topicHandles: Map<string, TopicHandle> = new Map();\n private readonly counters: Map<string, PNCounterHandle> = new Map();\n private readonly clusterClient?: ClusterClient;\n private readonly isClusterMode: boolean;\n private readonly clusterConfig?: Required<Omit<TopGunClusterConfig, 'seeds'>> & { seeds: string[] };\n\n constructor(config: TopGunClientConfig) {\n // Validate: either serverUrl or cluster, not both\n if (config.serverUrl && config.cluster) {\n throw new Error('Cannot specify both serverUrl and cluster config');\n }\n if (!config.serverUrl && !config.cluster) {\n throw new Error('Must specify either serverUrl or cluster config');\n }\n\n this.nodeId = config.nodeId || crypto.randomUUID();\n this.storageAdapter = config.storage;\n this.isClusterMode = !!config.cluster;\n\n if (config.cluster) {\n // Validate cluster seeds\n if (!config.cluster.seeds || config.cluster.seeds.length === 0) {\n throw new Error('Cluster config requires at least one seed node');\n }\n\n // Merge with defaults\n this.clusterConfig = {\n seeds: config.cluster.seeds,\n connectionsPerNode: config.cluster.connectionsPerNode ?? DEFAULT_CLUSTER_CONFIG.connectionsPerNode,\n smartRouting: config.cluster.smartRouting ?? DEFAULT_CLUSTER_CONFIG.smartRouting,\n partitionMapRefreshMs: config.cluster.partitionMapRefreshMs ?? DEFAULT_CLUSTER_CONFIG.partitionMapRefreshMs,\n connectionTimeoutMs: config.cluster.connectionTimeoutMs ?? DEFAULT_CLUSTER_CONFIG.connectionTimeoutMs,\n retryAttempts: config.cluster.retryAttempts ?? DEFAULT_CLUSTER_CONFIG.retryAttempts,\n };\n\n // Initialize cluster mode\n this.clusterClient = new ClusterClient({\n enabled: true,\n seedNodes: this.clusterConfig.seeds,\n routingMode: this.clusterConfig.smartRouting ? 'direct' : 'forward',\n connectionPool: {\n maxConnectionsPerNode: this.clusterConfig.connectionsPerNode,\n connectionTimeoutMs: this.clusterConfig.connectionTimeoutMs,\n },\n routing: {\n mapRefreshIntervalMs: this.clusterConfig.partitionMapRefreshMs,\n },\n });\n\n // SyncEngine uses ClusterClient as connectionProvider for partition-aware routing\n this.syncEngine = new SyncEngine({\n nodeId: this.nodeId,\n connectionProvider: this.clusterClient,\n storageAdapter: this.storageAdapter,\n backoff: config.backoff,\n backpressure: config.backpressure,\n });\n\n logger.info({ seeds: this.clusterConfig.seeds }, 'TopGunClient initialized in cluster mode');\n } else {\n // Single-server mode: create SingleServerProvider from serverUrl\n const singleServerProvider = new SingleServerProvider({ url: config.serverUrl! });\n\n this.syncEngine = new SyncEngine({\n nodeId: this.nodeId,\n connectionProvider: singleServerProvider,\n storageAdapter: this.storageAdapter,\n backoff: config.backoff,\n backpressure: config.backpressure,\n });\n\n logger.info({ serverUrl: config.serverUrl }, 'TopGunClient initialized in single-server mode');\n }\n }\n\n public async start(): Promise<void> {\n await this.storageAdapter.initialize('topgun_offline_db');\n // this.syncEngine.start();\n }\n\n public setAuthToken(token: string): void {\n this.syncEngine.setAuthToken(token);\n }\n\n public setAuthTokenProvider(provider: () => Promise<string | null>): void {\n this.syncEngine.setTokenProvider(provider);\n }\n\n /**\n * Creates a live query subscription for a map.\n */\n public query<T>(mapName: string, filter: QueryFilter): QueryHandle<T> {\n return new QueryHandle<T>(this.syncEngine, mapName, filter);\n }\n\n /**\n * Retrieves a distributed lock instance.\n * @param name The name of the lock.\n */\n public getLock(name: string): DistributedLock {\n return new DistributedLock(this.syncEngine, name);\n }\n\n /**\n * Retrieves a topic handle for Pub/Sub messaging.\n * @param name The name of the topic.\n */\n public topic(name: string): TopicHandle {\n if (!this.topicHandles.has(name)) {\n this.topicHandles.set(name, new TopicHandle(this.syncEngine, name));\n }\n return this.topicHandles.get(name)!;\n }\n\n /**\n * Retrieves a PN Counter instance. If the counter doesn't exist locally, it's created.\n * PN Counters support increment and decrement operations that work offline\n * and sync to server when connected.\n *\n * @param name The name of the counter (e.g., 'likes:post-123')\n * @returns A PNCounterHandle instance\n *\n * @example\n * ```typescript\n * const likes = client.getPNCounter('likes:post-123');\n * likes.increment(); // +1\n * likes.decrement(); // -1\n * likes.addAndGet(10); // +10\n *\n * likes.subscribe((value) => {\n * console.log('Current likes:', value);\n * });\n * ```\n */\n public getPNCounter(name: string): PNCounterHandle {\n let counter = this.counters.get(name);\n if (!counter) {\n counter = new PNCounterHandle(name, this.nodeId, this.syncEngine, this.storageAdapter);\n this.counters.set(name, counter);\n }\n return counter;\n }\n\n /**\n * Retrieves an LWWMap instance. If the map doesn't exist locally, it's created.\n * @param name The name of the map.\n * @returns An LWWMap instance.\n */\n public getMap<K, V>(name: string): LWWMap<K, V> {\n if (this.maps.has(name)) {\n const map = this.maps.get(name);\n if (map instanceof LWWMap) {\n return map as LWWMap<K, V>;\n }\n throw new Error(`Map ${name} exists but is not an LWWMap`);\n }\n\n const lwwMap = new LWWMap<K, V>(this.syncEngine.getHLC());\n this.maps.set(name, lwwMap);\n this.syncEngine.registerMap(name, lwwMap);\n\n // Restore state from storage asynchronously\n this.storageAdapter.getAllKeys().then(async (keys) => {\n const mapPrefix = `${name}:`;\n for (const fullKey of keys) {\n if (fullKey.startsWith(mapPrefix)) {\n const record = await this.storageAdapter.get(fullKey);\n if (record && (record as LWWRecord<V>).timestamp && !(record as any).tag) {\n // Strip prefix to get actual key\n const key = fullKey.substring(mapPrefix.length) as unknown as K;\n // Merge into in-memory map without triggering new ops\n lwwMap.merge(key, record as LWWRecord<V>);\n }\n }\n }\n }).catch(err => logger.error({ err }, 'Failed to restore keys from storage'));\n\n // Wrap LWWMap with IMap interface logic\n const originalSet = lwwMap.set.bind(lwwMap);\n lwwMap.set = (key: K, value: V, ttlMs?: number) => {\n const record = originalSet(key, value, ttlMs);\n this.storageAdapter.put(`${name}:${key}`, record).catch(err => logger.error({ err }, 'Failed to put record to storage'));\n this.syncEngine.recordOperation(name, 'PUT', String(key), { record, timestamp: record.timestamp }).catch(err => logger.error({ err }, 'Failed to record PUT op'));\n return record;\n };\n\n const originalRemove = lwwMap.remove.bind(lwwMap);\n lwwMap.remove = (key: K) => {\n const tombstone = originalRemove(key);\n this.storageAdapter.put(`${name}:${key}`, tombstone).catch(err => logger.error({ err }, 'Failed to put tombstone to storage'));\n this.syncEngine.recordOperation(name, 'REMOVE', String(key), { record: tombstone, timestamp: tombstone.timestamp }).catch(err => logger.error({ err }, 'Failed to record REMOVE op'));\n return tombstone;\n };\n\n return lwwMap;\n }\n\n /**\n * Retrieves an ORMap instance. If the map doesn't exist locally, it's created.\n * @param name The name of the map.\n * @returns An ORMap instance.\n */\n public getORMap<K, V>(name: string): ORMap<K, V> {\n if (this.maps.has(name)) {\n const map = this.maps.get(name);\n if (map instanceof ORMap) {\n return map as ORMap<K, V>;\n }\n throw new Error(`Map ${name} exists but is not an ORMap`);\n }\n\n const orMap = new ORMap<K, V>(this.syncEngine.getHLC());\n this.maps.set(name, orMap);\n this.syncEngine.registerMap(name, orMap);\n\n // Restore state from storage\n this.restoreORMap(name, orMap);\n\n // Wrap ORMap methods to record operations\n const originalAdd = orMap.add.bind(orMap);\n orMap.add = (key: K, value: V, ttlMs?: number) => {\n const record = originalAdd(key, value, ttlMs);\n \n // Persist records\n this.persistORMapKey(name, orMap, key);\n\n this.syncEngine.recordOperation(name, 'OR_ADD', String(key), { orRecord: record, timestamp: record.timestamp }).catch(err => logger.error({ err }, 'Failed to record OR_ADD op'));\n return record;\n };\n\n const originalRemove = orMap.remove.bind(orMap);\n orMap.remove = (key: K, value: V) => {\n const tombstones = originalRemove(key, value);\n const timestamp = this.syncEngine.getHLC().now(); \n \n // Update storage for the key (items removed)\n this.persistORMapKey(name, orMap, key);\n // Update storage for tombstones\n this.persistORMapTombstones(name, orMap);\n\n for (const tag of tombstones) {\n this.syncEngine.recordOperation(name, 'OR_REMOVE', String(key), { orTag: tag, timestamp }).catch(err => logger.error({ err }, 'Failed to record OR_REMOVE op'));\n }\n return tombstones;\n };\n\n return orMap;\n }\n\n private async restoreORMap<K, V>(name: string, orMap: ORMap<K, V>) {\n try {\n // 1. Restore Tombstones\n const tombstoneKey = `__sys__:${name}:tombstones`;\n const tombstones = await this.storageAdapter.getMeta(tombstoneKey);\n if (Array.isArray(tombstones)) {\n for (const tag of tombstones) {\n orMap.applyTombstone(tag);\n }\n }\n\n // 2. Restore Items\n const keys = await this.storageAdapter.getAllKeys();\n const mapPrefix = `${name}:`;\n for (const fullKey of keys) {\n if (fullKey.startsWith(mapPrefix)) {\n const keyPart = fullKey.substring(mapPrefix.length);\n \n const data = await this.storageAdapter.get(fullKey);\n if (Array.isArray(data)) {\n // It's likely an ORMap value list (Array of ORMapRecord)\n const records = data as ORMapRecord<V>[];\n const key = keyPart as unknown as K;\n \n for (const record of records) {\n orMap.apply(key, record);\n }\n }\n }\n }\n } catch (e) {\n logger.error({ mapName: name, err: e }, 'Failed to restore ORMap');\n }\n }\n\n private async persistORMapKey<K, V>(mapName: string, orMap: ORMap<K, V>, key: K) {\n const records = orMap.getRecords(key);\n if (records.length > 0) {\n await this.storageAdapter.put(`${mapName}:${key}`, records);\n } else {\n await this.storageAdapter.remove(`${mapName}:${key}`);\n }\n }\n \n private async persistORMapTombstones<K, V>(mapName: string, orMap: ORMap<K, V>) {\n const tombstoneKey = `__sys__:${mapName}:tombstones`;\n const tombstones = orMap.getTombstones();\n await this.storageAdapter.setMeta(tombstoneKey, tombstones);\n }\n\n /**\n * Closes the client, disconnecting from the server and cleaning up resources.\n */\n public close(): void {\n if (this.clusterClient) {\n this.clusterClient.close();\n }\n this.syncEngine.close();\n }\n\n // ============================================\n // Cluster Mode API\n // ============================================\n\n /**\n * Check if running in cluster mode\n */\n public isCluster(): boolean {\n return this.isClusterMode;\n }\n\n /**\n * Get list of connected cluster nodes (cluster mode only)\n * @returns Array of connected node IDs, or empty array in single-server mode\n */\n public getConnectedNodes(): string[] {\n if (!this.clusterClient) return [];\n return this.clusterClient.getConnectedNodes();\n }\n\n /**\n * Get the current partition map version (cluster mode only)\n * @returns Partition map version, or 0 in single-server mode\n */\n public getPartitionMapVersion(): number {\n if (!this.clusterClient) return 0;\n return this.clusterClient.getRouterStats().mapVersion;\n }\n\n /**\n * Check if direct routing is active (cluster mode only)\n * Direct routing sends operations directly to partition owners.\n * @returns true if routing is active, false otherwise\n */\n public isRoutingActive(): boolean {\n if (!this.clusterClient) return false;\n return this.clusterClient.isRoutingActive();\n }\n\n /**\n * Get health status for all cluster nodes (cluster mode only)\n * @returns Map of node IDs to their health status\n */\n public getClusterHealth(): Map<string, NodeHealth> {\n if (!this.clusterClient) return new Map();\n return this.clusterClient.getHealthStatus();\n }\n\n /**\n * Force refresh of partition map (cluster mode only)\n * Use this after detecting routing errors.\n */\n public async refreshPartitionMap(): Promise<void> {\n if (!this.clusterClient) return;\n await this.clusterClient.refreshPartitionMap();\n }\n\n /**\n * Get cluster router statistics (cluster mode only)\n */\n public getClusterStats(): {\n mapVersion: number;\n partitionCount: number;\n nodeCount: number;\n lastRefresh: number;\n isStale: boolean;\n } | null {\n if (!this.clusterClient) return null;\n return this.clusterClient.getRouterStats();\n }\n\n // ============================================\n // Connection State API\n // ============================================\n\n /**\n * Get the current connection state\n */\n public getConnectionState(): SyncState {\n return this.syncEngine.getConnectionState();\n }\n\n /**\n * Subscribe to connection state changes\n * @param listener Callback function called on each state change\n * @returns Unsubscribe function\n */\n public onConnectionStateChange(listener: (event: StateChangeEvent) => void): () => void {\n return this.syncEngine.onConnectionStateChange(listener);\n }\n\n /**\n * Get state machine history for debugging\n * @param limit Maximum number of entries to return\n */\n public getStateHistory(limit?: number): StateChangeEvent[] {\n return this.syncEngine.getStateHistory(limit);\n }\n\n /**\n * Reset the connection and state machine.\n * Use after fatal errors to start fresh.\n */\n public resetConnection(): void {\n this.syncEngine.resetConnection();\n }\n\n // ============================================\n // Backpressure API\n // ============================================\n\n /**\n * Get the current number of pending (unacknowledged) operations.\n */\n public getPendingOpsCount(): number {\n return this.syncEngine.getPendingOpsCount();\n }\n\n /**\n * Get the current backpressure status.\n */\n public getBackpressureStatus(): BackpressureStatus {\n return this.syncEngine.getBackpressureStatus();\n }\n\n /**\n * Returns true if writes are currently paused due to backpressure.\n */\n public isBackpressurePaused(): boolean {\n return this.syncEngine.isBackpressurePaused();\n }\n\n /**\n * Subscribe to backpressure events.\n *\n * Available events:\n * - 'backpressure:high': Emitted when pending ops reach high water mark\n * - 'backpressure:low': Emitted when pending ops drop below low water mark\n * - 'backpressure:paused': Emitted when writes are paused (pause strategy)\n * - 'backpressure:resumed': Emitted when writes resume after being paused\n * - 'operation:dropped': Emitted when an operation is dropped (drop-oldest strategy)\n *\n * @param event Event name\n * @param listener Callback function\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * client.onBackpressure('backpressure:high', ({ pending, max }) => {\n * console.warn(`Warning: ${pending}/${max} pending ops`);\n * });\n *\n * client.onBackpressure('backpressure:paused', () => {\n * showLoadingSpinner();\n * });\n *\n * client.onBackpressure('backpressure:resumed', () => {\n * hideLoadingSpinner();\n * });\n * ```\n */\n public onBackpressure(\n event: 'backpressure:high' | 'backpressure:low' | 'backpressure:paused' | 'backpressure:resumed' | 'operation:dropped',\n listener: (data?: BackpressureThresholdEvent | OperationDroppedEvent) => void\n ): () => void {\n return this.syncEngine.onBackpressure(event, listener);\n }\n\n // ============================================\n // Full-Text Search API\n // ============================================\n\n /**\n * Perform a one-shot BM25 search on the server.\n *\n * Searches the specified map using BM25 ranking algorithm.\n * Requires FTS to be enabled for the map on the server.\n *\n * @param mapName Name of the map to search\n * @param query Search query text\n * @param options Search options\n * @returns Promise resolving to search results sorted by relevance\n *\n * @example\n * ```typescript\n * const results = await client.search<Article>('articles', 'machine learning', {\n * limit: 20,\n * minScore: 0.5,\n * boost: { title: 2.0, body: 1.0 }\n * });\n *\n * for (const result of results) {\n * console.log(`${result.key}: ${result.value.title} (score: ${result.score})`);\n * }\n * ```\n */\n public async search<T>(\n mapName: string,\n query: string,\n options?: {\n limit?: number;\n minScore?: number;\n boost?: Record<string, number>;\n }\n ): Promise<Array<{\n key: string;\n value: T;\n score: number;\n matchedTerms: string[];\n }>> {\n return this.syncEngine.search<T>(mapName, query, options);\n }\n\n // ============================================\n // Live Search API\n // ============================================\n\n /**\n * Subscribe to live search results with real-time updates.\n *\n * Unlike the one-shot `search()` method, `searchSubscribe()` returns a handle\n * that receives delta updates (ENTER/UPDATE/LEAVE) when documents change.\n * This is ideal for live search UIs that need to reflect data changes.\n *\n * @param mapName Name of the map to search\n * @param query Search query text\n * @param options Search options (limit, minScore, boost)\n * @returns SearchHandle for managing the subscription\n *\n * @example\n * ```typescript\n * const handle = client.searchSubscribe<Article>('articles', 'machine learning', {\n * limit: 20,\n * minScore: 0.5\n * });\n *\n * // Subscribe to result changes\n * const unsubscribe = handle.subscribe((results) => {\n * setSearchResults(results);\n * });\n *\n * // Update query dynamically\n * handle.setQuery('deep learning');\n *\n * // Get current snapshot\n * const snapshot = handle.getResults();\n *\n * // Cleanup when done\n * handle.dispose();\n * ```\n */\n public searchSubscribe<T>(\n mapName: string,\n query: string,\n options?: SearchOptions\n ): SearchHandle<T> {\n return new SearchHandle<T>(this.syncEngine, mapName, query, options);\n }\n\n // ============================================\n // Hybrid Query API\n // ============================================\n\n /**\n * Create a hybrid query combining FTS with traditional filters.\n *\n * Hybrid queries allow combining full-text search predicates (match, matchPhrase, matchPrefix)\n * with traditional filter predicates (eq, gt, lt, contains, etc.) in a single query.\n * Results include relevance scores for FTS ranking.\n *\n * @param mapName Name of the map to query\n * @param filter Hybrid query filter with predicate, where, sort, limit, cursor\n * @returns HybridQueryHandle for managing the subscription\n *\n * @example\n * ```typescript\n * import { Predicates } from '@topgunbuild/core';\n *\n * // Hybrid query: FTS + filter\n * const handle = client.hybridQuery<Article>('articles', {\n * predicate: Predicates.and(\n * Predicates.match('body', 'machine learning'),\n * Predicates.equal('category', 'tech')\n * ),\n * sort: { _score: 'desc' },\n * limit: 20\n * });\n *\n * // Subscribe to results\n * handle.subscribe((results) => {\n * results.forEach(r => console.log(`${r._key}: score=${r._score}`));\n * });\n * ```\n */\n public hybridQuery<T>(mapName: string, filter: HybridQueryFilter = {}): HybridQueryHandle<T> {\n return new HybridQueryHandle<T>(this.syncEngine, mapName, filter);\n }\n\n // ============================================\n // Entry Processor API\n // ============================================\n\n /**\n * Execute an entry processor on a single key atomically.\n *\n * Entry processors solve the read-modify-write race condition by executing\n * user-defined logic atomically on the server where the data lives.\n *\n * @param mapName Name of the map\n * @param key Key to process\n * @param processor Processor definition with name, code, and optional args\n * @returns Promise resolving to the processor result\n *\n * @example\n * ```typescript\n * // Increment a counter atomically\n * const result = await client.executeOnKey('stats', 'pageViews', {\n * name: 'increment',\n * code: `\n * const current = value ?? 0;\n * return { value: current + 1, result: current + 1 };\n * `,\n * });\n *\n * // Using built-in processor\n * import { BuiltInProcessors } from '@topgunbuild/core';\n * const result = await client.executeOnKey(\n * 'stats',\n * 'pageViews',\n * BuiltInProcessors.INCREMENT(1)\n * );\n * ```\n */\n public async executeOnKey<V, R = V>(\n mapName: string,\n key: string,\n processor: EntryProcessorDef<V, R>,\n ): Promise<EntryProcessorResult<R>> {\n const result = await this.syncEngine.executeOnKey(mapName, key, processor);\n\n // Update local map cache if successful and we have the map\n if (result.success && result.newValue !== undefined) {\n const map = this.maps.get(mapName);\n if (map instanceof LWWMap) {\n // Update local cache - set() generates its own timestamp\n // The server will broadcast the full update to all subscribers\n (map as LWWMap<any, any>).set(key, result.newValue);\n }\n }\n\n return result;\n }\n\n /**\n * Execute an entry processor on multiple keys.\n *\n * Each key is processed atomically. The operation returns when all keys\n * have been processed.\n *\n * @param mapName Name of the map\n * @param keys Keys to process\n * @param processor Processor definition\n * @returns Promise resolving to a map of key -> result\n *\n * @example\n * ```typescript\n * // Reset multiple counters\n * const results = await client.executeOnKeys(\n * 'stats',\n * ['pageViews', 'uniqueVisitors', 'bounceRate'],\n * {\n * name: 'reset',\n * code: `return { value: 0, result: value };`, // Returns old value\n * }\n * );\n *\n * for (const [key, result] of results) {\n * console.log(`${key}: was ${result.result}, now 0`);\n * }\n * ```\n */\n public async executeOnKeys<V, R = V>(\n mapName: string,\n keys: string[],\n processor: EntryProcessorDef<V, R>,\n ): Promise<Map<string, EntryProcessorResult<R>>> {\n const results = await this.syncEngine.executeOnKeys(mapName, keys, processor);\n\n // Update local map cache for successful operations\n const map = this.maps.get(mapName);\n if (map instanceof LWWMap) {\n for (const [key, result] of results) {\n if (result.success && result.newValue !== undefined) {\n (map as LWWMap<any, any>).set(key, result.newValue);\n }\n }\n }\n\n return results;\n }\n\n // ============================================\n // Event Journal API\n // ============================================\n\n /** Cached EventJournalReader instance */\n private journalReader?: EventJournalReader;\n\n /**\n * Get the Event Journal reader for subscribing to and reading\n * map change events.\n *\n * The Event Journal provides:\n * - Append-only log of all map changes (PUT, UPDATE, DELETE)\n * - Subscription to real-time events\n * - Historical event replay\n * - Audit trail for compliance\n *\n * @returns EventJournalReader instance\n *\n * @example\n * ```typescript\n * const journal = client.getEventJournal();\n *\n * // Subscribe to all events\n * const unsubscribe = journal.subscribe((event) => {\n * console.log(`${event.type} on ${event.mapName}:${event.key}`);\n * });\n *\n * // Subscribe to specific map\n * journal.subscribe(\n * (event) => console.log('User changed:', event.key),\n * { mapName: 'users' }\n * );\n *\n * // Read historical events\n * const events = await journal.readFrom(0n, 100);\n * ```\n */\n public getEventJournal(): EventJournalReader {\n if (!this.journalReader) {\n this.journalReader = new EventJournalReader(this.syncEngine);\n }\n return this.journalReader;\n }\n\n // ============================================\n // Conflict Resolver API\n // ============================================\n\n /**\n * Get the conflict resolver client for registering custom merge resolvers.\n *\n * Conflict resolvers allow you to customize how merge conflicts are handled\n * on the server. You can implement business logic like:\n * - First-write-wins for booking systems\n * - Numeric constraints (non-negative, min/max)\n * - Owner-only modifications\n * - Custom merge strategies\n *\n * @returns ConflictResolverClient instance\n *\n * @example\n * ```typescript\n * const resolvers = client.getConflictResolvers();\n *\n * // Register a first-write-wins resolver\n * await resolvers.register('bookings', {\n * name: 'first-write-wins',\n * code: `\n * if (context.localValue !== undefined) {\n * return { action: 'reject', reason: 'Slot already booked' };\n * }\n * return { action: 'accept', value: context.remoteValue };\n * `,\n * priority: 100,\n * });\n *\n * // Subscribe to merge rejections\n * resolvers.onRejection((rejection) => {\n * console.log(`Merge rejected: ${rejection.reason}`);\n * // Optionally refresh local state\n * });\n *\n * // List registered resolvers\n * const registered = await resolvers.list('bookings');\n * console.log('Active resolvers:', registered);\n *\n * // Unregister when done\n * await resolvers.unregister('bookings', 'first-write-wins');\n * ```\n */\n public getConflictResolvers() {\n return this.syncEngine.getConflictResolverClient();\n }\n}\n","/**\n * Deep equality comparison utility for change tracking.\n * Optimized for typical CRDT data structures (objects, arrays, primitives).\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n // Same reference or both primitive and equal\n if (a === b) return true;\n\n // Handle null/undefined\n if (a == null || b == null) return a === b;\n\n // Different types\n if (typeof a !== typeof b) return false;\n\n // Primitives (number, string, boolean, symbol, bigint)\n if (typeof a !== 'object') return a === b;\n\n // Arrays\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Objects (excluding null which is handled above)\n if (Array.isArray(b)) return false;\n\n const objA = a as Record<string, unknown>;\n const objB = b as Record<string, unknown>;\n\n const keysA = Object.keys(objA);\n const keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;\n if (!deepEqual(objA[key], objB[key])) return false;\n }\n\n return true;\n}\n","import { deepEqual } from './utils/deepEqual';\n\n/**\n * Represents a change event for tracking data mutations.\n */\nexport interface ChangeEvent<T> {\n /** Type of change: 'add' for new entries, 'update' for modified entries, 'remove' for deleted entries */\n type: 'add' | 'update' | 'remove';\n /** The key of the changed entry */\n key: string;\n /** New value (present for 'add' and 'update') */\n value?: T;\n /** Previous value (present for 'update' and 'remove') */\n previousValue?: T;\n /** HLC timestamp of the change */\n timestamp: number;\n}\n\n/**\n * ChangeTracker computes differences between snapshots of a Map.\n * Used to track add/update/remove changes for subscription notifications.\n *\n * @example\n * ```typescript\n * const tracker = new ChangeTracker<Todo>();\n *\n * // First snapshot\n * const changes1 = tracker.computeChanges(\n * new Map([['a', { title: 'Todo A' }]]),\n * Date.now()\n * );\n * // changes1 = [{ type: 'add', key: 'a', value: { title: 'Todo A' }, timestamp: ... }]\n *\n * // Second snapshot with update\n * const changes2 = tracker.computeChanges(\n * new Map([['a', { title: 'Todo A Updated' }]]),\n * Date.now()\n * );\n * // changes2 = [{ type: 'update', key: 'a', value: { title: 'Todo A Updated' }, previousValue: { title: 'Todo A' }, timestamp: ... }]\n * ```\n */\nexport class ChangeTracker<T> {\n private previousSnapshot: Map<string, T> = new Map();\n\n /**\n * Computes changes between previous and current state.\n * Updates internal snapshot after computation.\n *\n * @param current - Current state as a Map\n * @param timestamp - HLC timestamp for the changes\n * @returns Array of change events (may be empty if no changes)\n */\n computeChanges(current: Map<string, T>, timestamp: number): ChangeEvent<T>[] {\n const changes: ChangeEvent<T>[] = [];\n\n // Find additions and updates\n for (const [key, value] of current) {\n const previous = this.previousSnapshot.get(key);\n if (previous === undefined) {\n changes.push({ type: 'add', key, value, timestamp });\n } else if (!deepEqual(previous, value)) {\n changes.push({\n type: 'update',\n key,\n value,\n previousValue: previous,\n timestamp,\n });\n }\n }\n\n // Find removals\n for (const [key, value] of this.previousSnapshot) {\n if (!current.has(key)) {\n changes.push({\n type: 'remove',\n key,\n previousValue: value,\n timestamp,\n });\n }\n }\n\n // Update snapshot with deep copy to avoid mutation issues\n this.previousSnapshot = new Map(\n Array.from(current.entries()).map(([k, v]) => [\n k,\n typeof v === 'object' && v !== null ? { ...(v as object) } as T : v,\n ])\n );\n\n return changes;\n }\n\n /**\n * Reset tracker (e.g., on query change or reconnect)\n */\n reset(): void {\n this.previousSnapshot.clear();\n }\n\n /**\n * Get current snapshot size for debugging/metrics\n */\n get size(): number {\n return this.previousSnapshot.size;\n }\n}\n","import { SyncEngine } from './SyncEngine';\nimport { ChangeTracker, ChangeEvent } from './ChangeTracker';\nimport { logger } from './utils/logger';\nimport type { PredicateNode } from '@topgunbuild/core';\n\nexport interface QueryFilter {\n where?: Record<string, any>;\n predicate?: PredicateNode;\n sort?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n /** Cursor for pagination */\n cursor?: string;\n}\n\n/** Cursor status for debugging */\nexport type CursorStatus = 'valid' | 'expired' | 'invalid' | 'none';\n\n/** Pagination info from server */\nexport interface PaginationInfo {\n /** Cursor for fetching next page */\n nextCursor?: string;\n /** Whether more results are available */\n hasMore: boolean;\n /** Debug info: status of input cursor processing */\n cursorStatus: CursorStatus;\n}\n\n/** Source of query results for proper handling of race conditions */\nexport type QueryResultSource = 'local' | 'server';\n\n/** Result item with _key field for client-side lookups */\nexport type QueryResultItem<T> = T & { _key: string };\n\nexport class QueryHandle<T> {\n public readonly id: string;\n private syncEngine: SyncEngine;\n private mapName: string;\n private filter: QueryFilter;\n private listeners: Set<(results: QueryResultItem<T>[]) => void> = new Set();\n private currentResults: Map<string, T> = new Map();\n\n // Change tracking for delta notifications\n private changeTracker = new ChangeTracker<T>();\n private pendingChanges: ChangeEvent<T>[] = [];\n private changeListeners: Set<(changes: ChangeEvent<T>[]) => void> = new Set();\n\n // Pagination info\n private _paginationInfo: PaginationInfo = { hasMore: false, cursorStatus: 'none' };\n private paginationListeners: Set<(info: PaginationInfo) => void> = new Set();\n\n constructor(syncEngine: SyncEngine, mapName: string, filter: QueryFilter = {}) {\n this.id = crypto.randomUUID();\n this.syncEngine = syncEngine;\n this.mapName = mapName;\n this.filter = filter;\n }\n\n public subscribe(callback: (results: QueryResultItem<T>[]) => void): () => void {\n this.listeners.add(callback);\n\n // If this is the first listener, activate subscription\n if (this.listeners.size === 1) {\n this.syncEngine.subscribeToQuery(this);\n } else {\n // Immediately invoke with cached results\n callback(this.getSortedResults());\n }\n\n // [FIX]: Attempt to load local results immediately if available\n // This ensures that if data is already in storage but sync hasn't happened,\n // we still show something.\n this.loadInitialLocalData().then(data => {\n // If we haven't received server results yet (currentResults empty),\n // and we have local data OR it's just the initial load, we should notify.\n // Even if data is empty, we might want to tell the subscriber \"nothing here yet\".\n if (this.currentResults.size === 0) {\n this.onResult(data, 'local');\n }\n });\n\n return () => {\n this.listeners.delete(callback);\n if (this.listeners.size === 0) {\n this.syncEngine.unsubscribeFromQuery(this.id);\n }\n };\n }\n\n private async loadInitialLocalData() {\n // This requires SyncEngine to expose a method to query local storage\n // For now, we can't easily reach storageAdapter directly from here without leaking abstraction.\n // A better approach is for SyncEngine.subscribeToQuery to trigger a local load.\n return this.syncEngine.runLocalQuery(this.mapName, this.filter);\n }\n\n // Track if we've received authoritative server response\n private hasReceivedServerData: boolean = false;\n\n /**\n * Called by SyncEngine when server sends initial results or by local storage load.\n * Uses merge strategy instead of clear to prevent UI flickering.\n *\n * @param items - Array of key-value pairs\n * @param source - 'local' for IndexedDB data, 'server' for QUERY_RESP from server\n *\n * Race condition protection:\n * - Empty server responses are ignored until we receive non-empty server data\n * - This prevents clearing local data when server hasn't loaded from storage yet\n * - Works with any async storage adapter (PostgreSQL, SQLite, Redis, etc.)\n */\n public onResult(items: { key: string, value: T }[], source: QueryResultSource = 'server') {\n logger.debug({\n mapName: this.mapName,\n itemCount: items.length,\n source,\n currentResultsCount: this.currentResults.size,\n hasReceivedServerData: this.hasReceivedServerData\n }, 'QueryHandle onResult');\n\n // [FIX] Race condition protection for any async storage adapter:\n // If server sends empty QUERY_RESP before loading data from storage,\n // we ignore it to prevent clearing valid local data.\n // This is safe because:\n // 1. If server truly has no data, next non-empty response will clear local-only items\n // 2. If server is still loading, we preserve local data until real data arrives\n if (source === 'server' && items.length === 0 && !this.hasReceivedServerData) {\n logger.debug({ mapName: this.mapName }, 'QueryHandle ignoring empty server response - waiting for authoritative data');\n return;\n }\n\n // Mark that we've received authoritative server data (non-empty from server)\n if (source === 'server' && items.length > 0) {\n this.hasReceivedServerData = true;\n }\n\n const newKeys = new Set(items.map(i => i.key));\n\n // Remove only keys that are not in the new results\n const removedKeys: string[] = [];\n for (const key of this.currentResults.keys()) {\n if (!newKeys.has(key)) {\n removedKeys.push(key);\n this.currentResults.delete(key);\n }\n }\n if (removedKeys.length > 0) {\n logger.debug({\n mapName: this.mapName,\n removedCount: removedKeys.length,\n removedKeys\n }, 'QueryHandle removed keys');\n }\n\n // Add/update new results\n for (const item of items) {\n this.currentResults.set(item.key, item.value);\n }\n logger.debug({\n mapName: this.mapName,\n resultCount: this.currentResults.size\n }, 'QueryHandle after merge');\n\n // Compute changes for delta tracking\n this.computeAndNotifyChanges(Date.now());\n\n this.notify();\n }\n\n /**\n * Called by SyncEngine when server sends a live update\n */\n public onUpdate(key: string, value: T | null) {\n if (value === null) {\n this.currentResults.delete(key);\n } else {\n this.currentResults.set(key, value);\n }\n\n // Compute changes for delta tracking\n this.computeAndNotifyChanges(Date.now());\n\n this.notify();\n }\n\n /**\n * Subscribe to change events.\n * Returns an unsubscribe function.\n *\n * @example\n * ```typescript\n * const unsubscribe = handle.onChanges((changes) => {\n * for (const change of changes) {\n * if (change.type === 'add') {\n * console.log('Added:', change.key, change.value);\n * }\n * }\n * });\n * ```\n */\n public onChanges(listener: (changes: ChangeEvent<T>[]) => void): () => void {\n this.changeListeners.add(listener);\n return () => this.changeListeners.delete(listener);\n }\n\n /**\n * Get and clear pending changes.\n * Call this to retrieve all changes since the last consume.\n */\n public consumeChanges(): ChangeEvent<T>[] {\n const changes = [...this.pendingChanges];\n this.pendingChanges = [];\n return changes;\n }\n\n /**\n * Get last change without consuming.\n * Returns null if no pending changes.\n */\n public getLastChange(): ChangeEvent<T> | null {\n return this.pendingChanges.length > 0\n ? this.pendingChanges[this.pendingChanges.length - 1]\n : null;\n }\n\n /**\n * Get all pending changes without consuming.\n */\n public getPendingChanges(): ChangeEvent<T>[] {\n return [...this.pendingChanges];\n }\n\n /**\n * Clear all pending changes.\n */\n public clearChanges(): void {\n this.pendingChanges = [];\n }\n\n /**\n * Reset change tracker.\n * Use when query filter changes or on reconnect.\n */\n public resetChangeTracker(): void {\n this.changeTracker.reset();\n this.pendingChanges = [];\n }\n\n private computeAndNotifyChanges(timestamp: number): void {\n const changes = this.changeTracker.computeChanges(this.currentResults, timestamp);\n\n if (changes.length > 0) {\n this.pendingChanges.push(...changes);\n this.notifyChangeListeners(changes);\n }\n }\n\n private notifyChangeListeners(changes: ChangeEvent<T>[]): void {\n for (const listener of this.changeListeners) {\n try {\n listener(changes);\n } catch (e) {\n logger.error({ err: e }, 'QueryHandle change listener error');\n }\n }\n }\n\n private notify() {\n const results = this.getSortedResults();\n for (const listener of this.listeners) {\n listener(results);\n }\n }\n\n private getSortedResults(): (T & { _key: string })[] {\n // Include _key in each result for client-side matching/lookup\n const results = Array.from(this.currentResults.entries()).map(\n ([key, value]) => ({ ...(value as object), _key: key } as T & { _key: string })\n );\n\n if (this.filter.sort) {\n results.sort((a: any, b: any) => {\n for (const [field, direction] of Object.entries(this.filter.sort!)) {\n const valA = a[field];\n const valB = b[field];\n\n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n return results;\n }\n\n public getFilter(): QueryFilter {\n return this.filter;\n }\n\n public getMapName(): string {\n return this.mapName;\n }\n\n // ============== Pagination Methods ==============\n\n /**\n * Get current pagination info.\n * Returns nextCursor, hasMore, and cursorStatus.\n */\n public getPaginationInfo(): PaginationInfo {\n return { ...this._paginationInfo };\n }\n\n /**\n * Subscribe to pagination info changes.\n * Called when server sends QUERY_RESP with new cursor info.\n *\n * @returns Unsubscribe function\n */\n public onPaginationChange(listener: (info: PaginationInfo) => void): () => void {\n this.paginationListeners.add(listener);\n // Immediately invoke with current value\n listener(this.getPaginationInfo());\n return () => this.paginationListeners.delete(listener);\n }\n\n /**\n * Update pagination info from server response.\n * Called by SyncEngine when processing QUERY_RESP.\n *\n * @internal\n */\n public updatePaginationInfo(info: Partial<PaginationInfo>): void {\n this._paginationInfo = {\n nextCursor: info.nextCursor,\n hasMore: info.hasMore ?? false,\n cursorStatus: info.cursorStatus ?? 'none',\n };\n this.notifyPaginationListeners();\n }\n\n private notifyPaginationListeners(): void {\n const info = this.getPaginationInfo();\n for (const listener of this.paginationListeners) {\n try {\n listener(info);\n } catch (e) {\n logger.error({ err: e }, 'QueryHandle pagination listener error');\n }\n }\n }\n}\n","import { SyncEngine } from './SyncEngine';\n\nexport interface ILock {\n lock(ttl?: number): Promise<boolean>;\n unlock(): Promise<void>;\n isLocked(): boolean;\n}\n\nexport class DistributedLock implements ILock {\n private syncEngine: SyncEngine;\n private name: string;\n private fencingToken: number | null = null;\n private _isLocked: boolean = false;\n\n constructor(syncEngine: SyncEngine, name: string) {\n this.syncEngine = syncEngine;\n this.name = name;\n }\n\n public async lock(ttl: number = 10000): Promise<boolean> {\n const requestId = crypto.randomUUID();\n try {\n const result = await this.syncEngine.requestLock(this.name, requestId, ttl);\n this.fencingToken = result.fencingToken;\n this._isLocked = true;\n return true;\n } catch (e) {\n return false;\n }\n }\n\n public async unlock(): Promise<void> {\n if (!this._isLocked || this.fencingToken === null) return;\n \n const requestId = crypto.randomUUID();\n try {\n await this.syncEngine.releaseLock(this.name, requestId, this.fencingToken);\n } finally {\n this._isLocked = false;\n this.fencingToken = null;\n }\n }\n\n public isLocked(): boolean {\n return this._isLocked;\n }\n}\n\n","import { SyncEngine } from './SyncEngine';\nimport { logger } from './utils/logger';\n\nexport type TopicCallback<T = unknown> = (\n data: T,\n context: { timestamp: number; publisherId?: string }\n) => void;\n\nexport class TopicHandle {\n private engine: SyncEngine;\n private topic: string;\n private listeners: Set<TopicCallback> = new Set();\n\n constructor(engine: SyncEngine, topic: string) {\n this.engine = engine;\n this.topic = topic;\n }\n\n public get id(): string {\n return this.topic;\n }\n\n /**\n * Publish a message to the topic\n */\n public publish(data: unknown): void {\n this.engine.publishTopic(this.topic, data);\n }\n\n /**\n * Subscribe to the topic\n */\n public subscribe(callback: TopicCallback) {\n if (this.listeners.size === 0) {\n this.engine.subscribeToTopic(this.topic, this);\n }\n this.listeners.add(callback);\n return () => this.unsubscribe(callback);\n }\n\n private unsubscribe(callback: TopicCallback) {\n this.listeners.delete(callback);\n if (this.listeners.size === 0) {\n this.engine.unsubscribeFromTopic(this.topic);\n }\n }\n\n /**\n * Called by SyncEngine when a message is received\n */\n public onMessage(data: unknown, context: { timestamp: number; publisherId?: string }): void {\n this.listeners.forEach(cb => {\n try {\n cb(data, context);\n } catch (e) {\n logger.error({ err: e, topic: this.topic, context: 'listener' }, 'Error in topic listener');\n }\n });\n }\n}\n\n","import { PNCounterImpl } from '@topgunbuild/core';\nimport type { PNCounter, PNCounterState, PNCounterStateObject } from '@topgunbuild/core';\nimport { SyncEngine } from './SyncEngine';\nimport type { IStorageAdapter } from './IStorageAdapter';\nimport { logger } from './utils/logger';\n\n/**\n * Storage key prefix for PNCounter state persistence.\n */\nconst COUNTER_STORAGE_PREFIX = '__counter__:';\n\n/**\n * Client-side handle for a PN Counter.\n *\n * Wraps the core PNCounterImpl and integrates with SyncEngine for:\n * - Automatic sync to server when counter changes\n * - Receiving remote updates from other clients\n * - Local persistence via IStorageAdapter (IndexedDB in browser)\n *\n * @example\n * ```typescript\n * const counter = client.getPNCounter('likes:post-123');\n * counter.increment(); // Immediate local update + sync to server\n *\n * counter.subscribe((value) => {\n * console.log('Current likes:', value);\n * });\n * ```\n */\nexport class PNCounterHandle implements PNCounter {\n private readonly counter: PNCounterImpl;\n private readonly name: string;\n private readonly syncEngine: SyncEngine;\n private readonly storageAdapter?: IStorageAdapter;\n private syncScheduled = false;\n private persistScheduled = false;\n private unsubscribeFromUpdates?: () => void;\n\n constructor(name: string, nodeId: string, syncEngine: SyncEngine, storageAdapter?: IStorageAdapter) {\n this.name = name;\n this.syncEngine = syncEngine;\n this.storageAdapter = storageAdapter;\n this.counter = new PNCounterImpl({ nodeId });\n\n // Restore state from local storage first (async, but fast)\n this.restoreFromStorage();\n\n // Subscribe to remote updates via SyncEngine\n this.unsubscribeFromUpdates = this.syncEngine.onCounterUpdate(name, (state) => {\n this.counter.merge(state);\n // Persist merged state to local storage\n this.schedulePersist();\n });\n\n // Request initial state from server\n this.syncEngine.requestCounter(name);\n\n logger.debug({ name, nodeId }, 'PNCounterHandle created');\n }\n\n /**\n * Restore counter state from local storage.\n * Called during construction to recover offline state.\n */\n private async restoreFromStorage(): Promise<void> {\n if (!this.storageAdapter) {\n return;\n }\n\n try {\n const storageKey = COUNTER_STORAGE_PREFIX + this.name;\n const stored = await this.storageAdapter.getMeta(storageKey);\n\n if (stored && typeof stored === 'object' && 'p' in stored && 'n' in stored) {\n // Convert stored object to PNCounterState\n const state = PNCounterImpl.objectToState(stored as PNCounterStateObject);\n this.counter.merge(state);\n logger.debug({ name: this.name, value: this.counter.get() }, 'PNCounter restored from storage');\n }\n } catch (err) {\n logger.error({ err, name: this.name }, 'Failed to restore PNCounter from storage');\n }\n }\n\n /**\n * Persist counter state to local storage.\n * Debounced to avoid excessive writes during rapid operations.\n */\n private schedulePersist(): void {\n if (!this.storageAdapter || this.persistScheduled) return;\n this.persistScheduled = true;\n\n // Debounce persistence (100ms) to batch rapid changes\n setTimeout(() => {\n this.persistScheduled = false;\n this.persistToStorage();\n }, 100);\n }\n\n /**\n * Actually persist state to storage.\n */\n private async persistToStorage(): Promise<void> {\n if (!this.storageAdapter) return;\n\n try {\n const storageKey = COUNTER_STORAGE_PREFIX + this.name;\n const stateObj = PNCounterImpl.stateToObject(this.counter.getState());\n await this.storageAdapter.setMeta(storageKey, stateObj);\n logger.debug({ name: this.name, value: this.counter.get() }, 'PNCounter persisted to storage');\n } catch (err) {\n logger.error({ err, name: this.name }, 'Failed to persist PNCounter to storage');\n }\n }\n\n /**\n * Get current counter value.\n */\n get(): number {\n return this.counter.get();\n }\n\n /**\n * Increment by 1 and return new value.\n */\n increment(): number {\n const value = this.counter.increment();\n this.scheduleSync();\n this.schedulePersist();\n return value;\n }\n\n /**\n * Decrement by 1 and return new value.\n */\n decrement(): number {\n const value = this.counter.decrement();\n this.scheduleSync();\n this.schedulePersist();\n return value;\n }\n\n /**\n * Add delta (positive or negative) and return new value.\n */\n addAndGet(delta: number): number {\n const value = this.counter.addAndGet(delta);\n if (delta !== 0) {\n this.scheduleSync();\n this.schedulePersist();\n }\n return value;\n }\n\n /**\n * Get state for sync.\n */\n getState(): PNCounterState {\n return this.counter.getState();\n }\n\n /**\n * Merge remote state.\n */\n merge(remote: PNCounterState): void {\n this.counter.merge(remote);\n }\n\n /**\n * Subscribe to value changes.\n */\n subscribe(listener: (value: number) => void): () => void {\n return this.counter.subscribe(listener);\n }\n\n /**\n * Get the counter name.\n */\n getName(): string {\n return this.name;\n }\n\n /**\n * Cleanup resources.\n */\n dispose(): void {\n if (this.unsubscribeFromUpdates) {\n this.unsubscribeFromUpdates();\n }\n }\n\n /**\n * Schedule sync to server with debouncing.\n * Batches rapid increments to avoid network spam.\n */\n private scheduleSync(): void {\n if (this.syncScheduled) return;\n this.syncScheduled = true;\n\n // Debounce sync to batch rapid increments (50ms)\n setTimeout(() => {\n this.syncScheduled = false;\n this.syncEngine.syncCounter(this.name, this.counter.getState());\n }, 50);\n }\n}\n","import type { JournalEvent, JournalEventType } from '@topgunbuild/core';\nimport type { SyncEngine } from './SyncEngine';\nimport { logger } from './utils/logger';\n\n/**\n * Serialized journal event from network (bigint as string).\n */\nexport interface JournalEventData {\n sequence: string;\n type: JournalEventType;\n mapName: string;\n key: string;\n value?: unknown;\n previousValue?: unknown;\n timestamp: {\n millis: number;\n counter: number;\n nodeId: string;\n };\n nodeId: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options for journal subscription.\n */\nexport interface JournalSubscribeOptions {\n /** Start from specific sequence */\n fromSequence?: bigint;\n /** Filter by map name */\n mapName?: string;\n /** Filter by event types */\n types?: JournalEventType[];\n}\n\n/**\n * Client-side Event Journal Reader.\n * Communicates with server to read and subscribe to journal events.\n */\nexport class EventJournalReader {\n private readonly syncEngine: SyncEngine;\n private readonly listeners: Map<string, (event: JournalEvent) => void> = new Map();\n private subscriptionCounter: number = 0;\n\n constructor(syncEngine: SyncEngine) {\n this.syncEngine = syncEngine;\n }\n\n /**\n * Read events from sequence with optional limit.\n *\n * @param sequence Starting sequence (inclusive)\n * @param limit Maximum events to return (default: 100)\n * @returns Promise resolving to array of events\n */\n async readFrom(sequence: bigint, limit: number = 100): Promise<JournalEvent[]> {\n const requestId = this.generateRequestId();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Journal read timeout'));\n }, 10000);\n\n const handleResponse = (message: any) => {\n if (message.type === 'JOURNAL_READ_RESPONSE' && message.requestId === requestId) {\n clearTimeout(timeout);\n this.syncEngine.off('message', handleResponse);\n\n const events = message.events.map((e: JournalEventData) => this.parseEvent(e));\n resolve(events);\n }\n };\n\n this.syncEngine.on('message', handleResponse);\n\n this.syncEngine.send({\n type: 'JOURNAL_READ',\n requestId,\n fromSequence: sequence.toString(),\n limit,\n });\n });\n }\n\n /**\n * Read events for a specific map.\n *\n * @param mapName Map name to filter\n * @param sequence Starting sequence (default: 0n)\n * @param limit Maximum events to return (default: 100)\n */\n async readMapEvents(\n mapName: string,\n sequence: bigint = 0n,\n limit: number = 100\n ): Promise<JournalEvent[]> {\n const requestId = this.generateRequestId();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Journal read timeout'));\n }, 10000);\n\n const handleResponse = (message: any) => {\n if (message.type === 'JOURNAL_READ_RESPONSE' && message.requestId === requestId) {\n clearTimeout(timeout);\n this.syncEngine.off('message', handleResponse);\n\n const events = message.events.map((e: JournalEventData) => this.parseEvent(e));\n resolve(events);\n }\n };\n\n this.syncEngine.on('message', handleResponse);\n\n this.syncEngine.send({\n type: 'JOURNAL_READ',\n requestId,\n fromSequence: sequence.toString(),\n limit,\n mapName,\n });\n });\n }\n\n /**\n * Subscribe to new journal events.\n *\n * @param listener Callback for each event\n * @param options Subscription options\n * @returns Unsubscribe function\n */\n subscribe(\n listener: (event: JournalEvent) => void,\n options: JournalSubscribeOptions = {}\n ): () => void {\n const subscriptionId = this.generateRequestId();\n\n this.listeners.set(subscriptionId, listener);\n\n // Set up message handler for this subscription\n const handleEvent = (message: any) => {\n if (message.type === 'JOURNAL_EVENT') {\n const event = this.parseEvent(message.event);\n\n // Apply client-side filters\n if (options.mapName && event.mapName !== options.mapName) return;\n if (options.types && !options.types.includes(event.type)) return;\n\n const listenerFn = this.listeners.get(subscriptionId);\n if (listenerFn) {\n try {\n listenerFn(event);\n } catch (e) {\n logger.error({ err: e }, 'Journal listener error');\n }\n }\n }\n };\n\n this.syncEngine.on('message', handleEvent);\n\n // Send subscription request\n this.syncEngine.send({\n type: 'JOURNAL_SUBSCRIBE',\n requestId: subscriptionId,\n fromSequence: options.fromSequence?.toString(),\n mapName: options.mapName,\n types: options.types,\n });\n\n // Return unsubscribe function\n return () => {\n this.listeners.delete(subscriptionId);\n this.syncEngine.off('message', handleEvent);\n\n this.syncEngine.send({\n type: 'JOURNAL_UNSUBSCRIBE',\n subscriptionId,\n });\n };\n }\n\n /**\n * Get the latest sequence number from server.\n */\n async getLatestSequence(): Promise<bigint> {\n // Read one event from the end to get latest sequence\n const events = await this.readFrom(0n, 1);\n if (events.length === 0) return 0n;\n return events[events.length - 1].sequence;\n }\n\n /**\n * Parse network event data to JournalEvent.\n */\n private parseEvent(raw: JournalEventData): JournalEvent {\n return {\n sequence: BigInt(raw.sequence),\n type: raw.type,\n mapName: raw.mapName,\n key: raw.key,\n value: raw.value,\n previousValue: raw.previousValue,\n timestamp: raw.timestamp,\n nodeId: raw.nodeId,\n metadata: raw.metadata,\n };\n }\n\n /**\n * Generate unique request ID.\n */\n private generateRequestId(): string {\n return `journal_${Date.now()}_${++this.subscriptionCounter}`;\n }\n}\n\n","/**\n * SearchHandle - Client-side Live Search Subscription Handle\n *\n * Manages a live search subscription with delta updates.\n *\n * @module SearchHandle\n */\n\nimport type { SearchOptions, SearchUpdateType } from '@topgunbuild/core';\nimport type { SyncEngine, SearchResult } from './SyncEngine';\nimport { logger } from './utils/logger';\n\n/**\n * Callback type for result change notifications.\n */\nexport type SearchResultsCallback<T> = (results: SearchResult<T>[]) => void;\n\n/**\n * SearchHandle manages a live search subscription.\n *\n * Provides:\n * - Initial results on subscription\n * - Real-time delta updates (ENTER/UPDATE/LEAVE)\n * - Sorted results by relevance score\n * - Query update without re-subscribing\n *\n * @example\n * ```typescript\n * const handle = client.searchSubscribe<Article>('articles', 'machine learning', {\n * limit: 20,\n * minScore: 0.5\n * });\n *\n * // Subscribe to results\n * const unsubscribe = handle.subscribe((results) => {\n * console.log('Results updated:', results.length);\n * });\n *\n * // Get current snapshot\n * const snapshot = handle.getResults();\n *\n * // Update query\n * handle.setQuery('deep learning');\n *\n * // Cleanup\n * handle.dispose();\n * ```\n */\nexport class SearchHandle<T = unknown> {\n /** Map name being searched */\n readonly mapName: string;\n\n /** Current search query */\n private _query: string;\n\n /** Search options */\n private _options?: SearchOptions;\n\n /** Unique subscription ID */\n private subscriptionId: string;\n\n /** Current results map (key → result) */\n private results: Map<string, SearchResult<T>> = new Map();\n\n /** Result change listeners */\n private listeners: Set<SearchResultsCallback<T>> = new Set();\n\n /** Whether the handle has been disposed */\n private disposed = false;\n\n /** Reference to SyncEngine */\n private syncEngine: SyncEngine;\n\n /** Handler for all messages (SEARCH_RESP and SEARCH_UPDATE) */\n private messageHandler: (message: any) => void;\n\n constructor(\n syncEngine: SyncEngine,\n mapName: string,\n query: string,\n options?: SearchOptions\n ) {\n this.syncEngine = syncEngine;\n this.mapName = mapName;\n this._query = query;\n this._options = options;\n this.subscriptionId = crypto.randomUUID();\n\n // Set up message handler that handles both SEARCH_RESP and SEARCH_UPDATE\n this.messageHandler = this.handleMessage.bind(this);\n\n // Register handler with SyncEngine\n this.syncEngine.on('message', this.messageHandler);\n\n // Send subscription request\n this.sendSubscribe();\n }\n\n /**\n * Handle incoming messages (both SEARCH_RESP and SEARCH_UPDATE).\n */\n private handleMessage(message: any): void {\n if (message.type === 'SEARCH_RESP') {\n this.handleSearchResponse(message);\n } else if (message.type === 'SEARCH_UPDATE') {\n this.handleSearchUpdate(message);\n }\n }\n\n /**\n * Get the current query string.\n */\n get query(): string {\n return this._query;\n }\n\n /**\n * Subscribe to result changes.\n * Callback is immediately called with current results.\n *\n * @param callback - Function called with updated results\n * @returns Unsubscribe function\n */\n subscribe(callback: SearchResultsCallback<T>): () => void {\n if (this.disposed) {\n throw new Error('SearchHandle has been disposed');\n }\n\n this.listeners.add(callback);\n\n // Immediately call with current results\n callback(this.getResults());\n\n return () => {\n this.listeners.delete(callback);\n };\n }\n\n /**\n * Get current results snapshot sorted by score (highest first).\n *\n * @returns Array of search results\n */\n getResults(): SearchResult<T>[] {\n return Array.from(this.results.values())\n .sort((a, b) => b.score - a.score);\n }\n\n /**\n * Get result count.\n */\n get size(): number {\n return this.results.size;\n }\n\n /**\n * Update the search query.\n * Triggers a new subscription with the updated query.\n *\n * @param query - New query string\n */\n setQuery(query: string): void {\n if (this.disposed) {\n throw new Error('SearchHandle has been disposed');\n }\n\n if (query === this._query) {\n return; // No change\n }\n\n // Unsubscribe from old query\n this.sendUnsubscribe();\n\n // Clear current results\n this.results.clear();\n\n // Update query and generate new subscription ID\n this._query = query;\n this.subscriptionId = crypto.randomUUID();\n\n // Subscribe to new query\n this.sendSubscribe();\n\n // Notify listeners of cleared results\n this.notifyListeners();\n }\n\n /**\n * Update search options.\n *\n * @param options - New search options\n */\n setOptions(options: SearchOptions): void {\n if (this.disposed) {\n throw new Error('SearchHandle has been disposed');\n }\n\n // Unsubscribe from old subscription\n this.sendUnsubscribe();\n\n // Clear current results\n this.results.clear();\n\n // Update options and generate new subscription ID\n this._options = options;\n this.subscriptionId = crypto.randomUUID();\n\n // Subscribe with new options\n this.sendSubscribe();\n\n // Notify listeners of cleared results\n this.notifyListeners();\n }\n\n /**\n * Dispose of the handle and cleanup resources.\n * After disposal, the handle cannot be used.\n */\n dispose(): void {\n if (this.disposed) {\n return;\n }\n\n this.disposed = true;\n\n // Send unsubscribe\n this.sendUnsubscribe();\n\n // Remove message handler\n this.syncEngine.off('message', this.messageHandler);\n\n // Clear state\n this.results.clear();\n this.listeners.clear();\n }\n\n /**\n * Check if handle is disposed.\n */\n isDisposed(): boolean {\n return this.disposed;\n }\n\n /**\n * Send SEARCH_SUB message to server.\n */\n private sendSubscribe(): void {\n this.syncEngine.send({\n type: 'SEARCH_SUB',\n payload: {\n subscriptionId: this.subscriptionId,\n mapName: this.mapName,\n query: this._query,\n options: this._options,\n },\n });\n }\n\n /**\n * Send SEARCH_UNSUB message to server.\n */\n private sendUnsubscribe(): void {\n this.syncEngine.send({\n type: 'SEARCH_UNSUB',\n payload: {\n subscriptionId: this.subscriptionId,\n },\n });\n }\n\n /**\n * Handle SEARCH_RESP message (initial results).\n */\n private handleSearchResponse(message: any): void {\n if (message.type !== 'SEARCH_RESP') return;\n if (message.payload?.requestId !== this.subscriptionId) return;\n\n const { results } = message.payload;\n\n if (Array.isArray(results)) {\n // Populate initial results\n for (const result of results) {\n this.results.set(result.key, {\n key: result.key,\n value: result.value as T,\n score: result.score,\n matchedTerms: result.matchedTerms || [],\n });\n }\n\n this.notifyListeners();\n }\n }\n\n /**\n * Handle SEARCH_UPDATE message (delta updates).\n */\n private handleSearchUpdate(message: any): void {\n if (message.type !== 'SEARCH_UPDATE') return;\n if (message.payload?.subscriptionId !== this.subscriptionId) return;\n\n const { key, value, score, matchedTerms, type } = message.payload;\n\n switch (type as SearchUpdateType) {\n case 'ENTER':\n // Document entered result set\n this.results.set(key, {\n key,\n value: value as T,\n score,\n matchedTerms: matchedTerms || [],\n });\n break;\n\n case 'UPDATE':\n // Document score changed\n const existing = this.results.get(key);\n if (existing) {\n existing.score = score;\n existing.matchedTerms = matchedTerms || [];\n // Value may have changed too\n existing.value = value as T;\n }\n break;\n\n case 'LEAVE':\n // Document left result set\n this.results.delete(key);\n break;\n }\n\n this.notifyListeners();\n }\n\n /**\n * Notify all listeners of result changes.\n */\n private notifyListeners(): void {\n const results = this.getResults();\n for (const listener of this.listeners) {\n try {\n listener(results);\n } catch (err) {\n logger.error({ err, mapName: this.mapName, context: 'listener' }, 'SearchHandle listener error');\n }\n }\n }\n}\n","/**\n * HybridQueryHandle - Query handle for hybrid FTS + filter queries\n *\n * Extends QueryHandle functionality to support:\n * - FTS predicates (match, matchPhrase, matchPrefix)\n * - Score-based sorting (_score field)\n * - Hybrid queries combining FTS with traditional filters\n *\n * @module HybridQueryHandle\n */\n\nimport { SyncEngine } from './SyncEngine';\nimport { ChangeTracker, ChangeEvent } from './ChangeTracker';\nimport { logger } from './utils/logger';\nimport type { PredicateNode } from '@topgunbuild/core';\nimport type { CursorStatus, PaginationInfo } from './QueryHandle';\n\n/**\n * Filter options for hybrid queries.\n */\nexport interface HybridQueryFilter {\n /** Traditional where clause filters */\n where?: Record<string, any>;\n /** Predicate tree (can include FTS predicates) */\n predicate?: PredicateNode;\n /** Sort configuration - use '_score' for FTS relevance sorting */\n sort?: Record<string, 'asc' | 'desc'>;\n /** Maximum results */\n limit?: number;\n /** Cursor for pagination */\n cursor?: string;\n}\n\n/**\n * Result item with score for hybrid queries.\n */\nexport interface HybridResultItem<T> {\n /** The document */\n value: T;\n /** Unique key */\n _key: string;\n /** Relevance score (only for FTS queries) */\n _score?: number;\n /** Matched terms (only for FTS queries) */\n _matchedTerms?: string[];\n}\n\n/**\n * Source of query results.\n */\nexport type HybridResultSource = 'local' | 'server';\n\n/**\n * HybridQueryHandle manages hybrid queries that combine FTS with filters.\n *\n * @example\n * ```typescript\n * // Create hybrid query: FTS + filter\n * const handle = new HybridQueryHandle(syncEngine, 'articles', {\n * predicate: Predicates.and(\n * Predicates.match('body', 'machine learning'),\n * Predicates.equal('category', 'tech')\n * ),\n * sort: { _score: 'desc' },\n * limit: 20\n * });\n *\n * // Subscribe to results\n * handle.subscribe((results) => {\n * results.forEach(r => console.log(`${r._key}: ${r._score}`));\n * });\n * ```\n */\nexport class HybridQueryHandle<T> {\n public readonly id: string;\n private syncEngine: SyncEngine;\n private mapName: string;\n private filter: HybridQueryFilter;\n private listeners: Set<(results: HybridResultItem<T>[]) => void> = new Set();\n private currentResults: Map<string, { value: T; score?: number; matchedTerms?: string[] }> =\n new Map();\n\n // Change tracking\n private changeTracker = new ChangeTracker<T>();\n private pendingChanges: ChangeEvent<T>[] = [];\n private changeListeners: Set<(changes: ChangeEvent<T>[]) => void> = new Set();\n\n // Track server data reception\n private hasReceivedServerData: boolean = false;\n\n // Pagination info\n private _paginationInfo: PaginationInfo = { hasMore: false, cursorStatus: 'none' };\n private paginationListeners: Set<(info: PaginationInfo) => void> = new Set();\n\n constructor(syncEngine: SyncEngine, mapName: string, filter: HybridQueryFilter = {}) {\n this.id = crypto.randomUUID();\n this.syncEngine = syncEngine;\n this.mapName = mapName;\n this.filter = filter;\n }\n\n /**\n * Subscribe to query results.\n */\n public subscribe(callback: (results: HybridResultItem<T>[]) => void): () => void {\n this.listeners.add(callback);\n\n // Activate subscription on first listener\n if (this.listeners.size === 1) {\n this.syncEngine.subscribeToHybridQuery(this);\n } else {\n // Return cached results immediately\n callback(this.getSortedResults());\n }\n\n // Load initial local data\n this.loadInitialLocalData().then((data) => {\n if (this.currentResults.size === 0) {\n this.onResult(data, 'local');\n }\n });\n\n return () => {\n this.listeners.delete(callback);\n if (this.listeners.size === 0) {\n this.syncEngine.unsubscribeFromHybridQuery(this.id);\n }\n };\n }\n\n private async loadInitialLocalData(): Promise<\n Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }>\n > {\n // Use SyncEngine to run local hybrid query\n return this.syncEngine.runLocalHybridQuery(this.mapName, this.filter);\n }\n\n /**\n * Called by SyncEngine with query results.\n */\n public onResult(\n items: Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }>,\n source: HybridResultSource = 'server'\n ): void {\n logger.debug(\n {\n mapName: this.mapName,\n itemCount: items.length,\n source,\n currentResultsCount: this.currentResults.size,\n hasReceivedServerData: this.hasReceivedServerData,\n },\n 'HybridQueryHandle onResult'\n );\n\n // Race condition protection (same as QueryHandle)\n if (source === 'server' && items.length === 0 && !this.hasReceivedServerData) {\n logger.debug(\n { mapName: this.mapName },\n 'HybridQueryHandle ignoring empty server response'\n );\n return;\n }\n\n if (source === 'server' && items.length > 0) {\n this.hasReceivedServerData = true;\n }\n\n const newKeys = new Set(items.map((i) => i.key));\n\n // Remove keys not in new results\n for (const key of this.currentResults.keys()) {\n if (!newKeys.has(key)) {\n this.currentResults.delete(key);\n }\n }\n\n // Add/update new results\n for (const item of items) {\n this.currentResults.set(item.key, {\n value: item.value,\n score: item.score,\n matchedTerms: item.matchedTerms,\n });\n }\n\n // Compute changes for delta tracking\n this.computeAndNotifyChanges(Date.now());\n\n this.notify();\n }\n\n /**\n * Called by SyncEngine on live update.\n */\n public onUpdate(\n key: string,\n value: T | null,\n score?: number,\n matchedTerms?: string[]\n ): void {\n if (value === null) {\n this.currentResults.delete(key);\n } else {\n this.currentResults.set(key, { value, score, matchedTerms });\n }\n\n this.computeAndNotifyChanges(Date.now());\n this.notify();\n }\n\n /**\n * Subscribe to change events.\n */\n public onChanges(listener: (changes: ChangeEvent<T>[]) => void): () => void {\n this.changeListeners.add(listener);\n return () => this.changeListeners.delete(listener);\n }\n\n /**\n * Get and clear pending changes.\n */\n public consumeChanges(): ChangeEvent<T>[] {\n const changes = [...this.pendingChanges];\n this.pendingChanges = [];\n return changes;\n }\n\n /**\n * Get last change without consuming.\n */\n public getLastChange(): ChangeEvent<T> | null {\n return this.pendingChanges.length > 0\n ? this.pendingChanges[this.pendingChanges.length - 1]\n : null;\n }\n\n /**\n * Get all pending changes without consuming.\n */\n public getPendingChanges(): ChangeEvent<T>[] {\n return [...this.pendingChanges];\n }\n\n /**\n * Clear all pending changes.\n */\n public clearChanges(): void {\n this.pendingChanges = [];\n }\n\n /**\n * Reset change tracker.\n */\n public resetChangeTracker(): void {\n this.changeTracker.reset();\n this.pendingChanges = [];\n }\n\n private computeAndNotifyChanges(timestamp: number): void {\n const dataMap = new Map<string, T>();\n for (const [key, entry] of this.currentResults) {\n dataMap.set(key, entry.value);\n }\n const changes = this.changeTracker.computeChanges(dataMap, timestamp);\n\n if (changes.length > 0) {\n this.pendingChanges.push(...changes);\n this.notifyChangeListeners(changes);\n }\n }\n\n private notifyChangeListeners(changes: ChangeEvent<T>[]): void {\n for (const listener of this.changeListeners) {\n try {\n listener(changes);\n } catch (e) {\n logger.error({ err: e }, 'HybridQueryHandle change listener error');\n }\n }\n }\n\n private notify(): void {\n const results = this.getSortedResults();\n for (const listener of this.listeners) {\n listener(results);\n }\n }\n\n /**\n * Get sorted results with _key and _score.\n */\n private getSortedResults(): HybridResultItem<T>[] {\n const results: HybridResultItem<T>[] = Array.from(this.currentResults.entries()).map(\n ([key, entry]) => ({\n value: entry.value,\n _key: key,\n _score: entry.score,\n _matchedTerms: entry.matchedTerms,\n })\n );\n\n // Sort by configured sort fields\n if (this.filter.sort) {\n results.sort((a, b) => {\n for (const [field, direction] of Object.entries(this.filter.sort!)) {\n let valA: any;\n let valB: any;\n\n if (field === '_score') {\n // Special handling for _score\n valA = a._score ?? 0;\n valB = b._score ?? 0;\n } else if (field === '_key') {\n valA = a._key;\n valB = b._key;\n } else {\n valA = (a.value as any)[field];\n valB = (b.value as any)[field];\n }\n\n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n // Apply limit (cursor filtering is done server-side)\n let sliced = results;\n if (this.filter.limit) {\n sliced = sliced.slice(0, this.filter.limit);\n }\n\n return sliced;\n }\n\n /**\n * Get the filter configuration.\n */\n public getFilter(): HybridQueryFilter {\n return this.filter;\n }\n\n /**\n * Get the map name.\n */\n public getMapName(): string {\n return this.mapName;\n }\n\n /**\n * Check if this query contains FTS predicates.\n */\n public hasFTSPredicate(): boolean {\n return this.filter.predicate ? this.containsFTS(this.filter.predicate) : false;\n }\n\n private containsFTS(predicate: PredicateNode): boolean {\n if (predicate.op === 'match' || predicate.op === 'matchPhrase' || predicate.op === 'matchPrefix') {\n return true;\n }\n if (predicate.children) {\n return predicate.children.some((child) => this.containsFTS(child));\n }\n return false;\n }\n\n // ============== Pagination Methods ==============\n\n /**\n * Get current pagination info.\n * Returns nextCursor, hasMore, and cursorStatus.\n */\n public getPaginationInfo(): PaginationInfo {\n return { ...this._paginationInfo };\n }\n\n /**\n * Subscribe to pagination info changes.\n * Called when server sends HYBRID_QUERY_RESP with new cursor info.\n *\n * @returns Unsubscribe function\n */\n public onPaginationChange(listener: (info: PaginationInfo) => void): () => void {\n this.paginationListeners.add(listener);\n // Immediately invoke with current value\n listener(this.getPaginationInfo());\n return () => this.paginationListeners.delete(listener);\n }\n\n /**\n * Update pagination info from server response.\n * Called by SyncEngine when processing HYBRID_QUERY_RESP.\n *\n * @internal\n */\n public updatePaginationInfo(info: Partial<PaginationInfo>): void {\n this._paginationInfo = {\n nextCursor: info.nextCursor,\n hasMore: info.hasMore ?? false,\n cursorStatus: info.cursorStatus ?? 'none',\n };\n this.notifyPaginationListeners();\n }\n\n private notifyPaginationListeners(): void {\n const info = this.getPaginationInfo();\n for (const listener of this.paginationListeners) {\n try {\n listener(info);\n } catch (e) {\n logger.error({ err: e }, 'HybridQueryHandle pagination listener error');\n }\n }\n }\n}\n","/**\n * ClusterClient - Cluster-aware client wrapper\n *\n * Implements IConnectionProvider for SyncEngine abstraction.\n * Wraps the standard TopGunClient with cluster-aware routing capabilities.\n * Coordinates between ConnectionPool and PartitionRouter for optimal\n * request routing in a clustered environment.\n */\n\nimport {\n ClusterClientConfig,\n ConnectionPoolConfig,\n PartitionRouterConfig,\n CircuitBreakerConfig,\n DEFAULT_CONNECTION_POOL_CONFIG,\n DEFAULT_PARTITION_ROUTER_CONFIG,\n DEFAULT_CIRCUIT_BREAKER_CONFIG,\n NodeHealth,\n serialize,\n} from '@topgunbuild/core';\nimport { ConnectionPool } from './ConnectionPool';\nimport { PartitionRouter } from './PartitionRouter';\nimport { logger } from '../utils/logger';\nimport type { IConnectionProvider, ConnectionProviderEvent, ConnectionEventHandler } from '../types';\n\nexport interface ClusterClientEvents {\n 'connected': () => void;\n 'disconnected': (reason: string) => void;\n 'partitionMap:ready': (version: number) => void;\n 'routing:active': () => void;\n 'error': (error: Error) => void;\n 'circuit:open': (nodeId: string) => void;\n 'circuit:closed': (nodeId: string) => void;\n 'circuit:half-open': (nodeId: string) => void;\n}\n\n/**\n * Circuit breaker state for a node.\n */\nexport interface CircuitState {\n /** Number of consecutive failures */\n failures: number;\n /** Timestamp of last failure */\n lastFailure: number;\n /** Current circuit state */\n state: 'closed' | 'open' | 'half-open';\n}\n\n/**\n * Routing metrics for monitoring smart routing effectiveness.\n */\nexport interface RoutingMetrics {\n /** Operations routed directly to partition owner */\n directRoutes: number;\n /** Operations falling back to any node (owner unavailable) */\n fallbackRoutes: number;\n /** Operations when partition map is missing/stale */\n partitionMisses: number;\n /** Total routing decisions made */\n totalRoutes: number;\n}\n\nexport type ClusterRoutingMode = 'direct' | 'forward';\n\n/**\n * ClusterClient implements IConnectionProvider for multi-node cluster mode.\n * It provides partition-aware routing and connection management.\n */\nexport class ClusterClient implements IConnectionProvider {\n private readonly listeners: Map<string, Set<(...args: any[]) => void>> = new Map();\n private readonly connectionPool: ConnectionPool;\n private readonly partitionRouter: PartitionRouter;\n private readonly config: ClusterClientConfig;\n private initialized: boolean = false;\n private routingActive: boolean = false;\n private readonly routingMetrics: RoutingMetrics = {\n directRoutes: 0,\n fallbackRoutes: 0,\n partitionMisses: 0,\n totalRoutes: 0,\n };\n\n // Circuit breaker state per node\n private readonly circuits: Map<string, CircuitState> = new Map();\n private readonly circuitBreakerConfig: CircuitBreakerConfig;\n\n constructor(config: ClusterClientConfig) {\n this.config = config;\n\n // Initialize circuit breaker config\n this.circuitBreakerConfig = {\n ...DEFAULT_CIRCUIT_BREAKER_CONFIG,\n ...config.circuitBreaker,\n };\n\n // Initialize connection pool\n const poolConfig: ConnectionPoolConfig = {\n ...DEFAULT_CONNECTION_POOL_CONFIG,\n ...config.connectionPool,\n };\n this.connectionPool = new ConnectionPool(poolConfig);\n\n // Initialize partition router\n const routerConfig: PartitionRouterConfig = {\n ...DEFAULT_PARTITION_ROUTER_CONFIG,\n fallbackMode: config.routingMode === 'direct' ? 'error' : 'forward',\n ...config.routing,\n };\n this.partitionRouter = new PartitionRouter(this.connectionPool, routerConfig);\n\n this.setupEventHandlers();\n }\n\n // ============================================\n // Event Emitter Methods (browser-compatible)\n // ============================================\n\n public on(event: string, listener: (...args: any[]) => void): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n return this;\n }\n\n public off(event: string, listener: (...args: any[]) => void): this {\n this.listeners.get(event)?.delete(listener);\n return this;\n }\n\n public emit(event: string, ...args: any[]): boolean {\n const eventListeners = this.listeners.get(event);\n if (!eventListeners || eventListeners.size === 0) {\n return false;\n }\n for (const listener of eventListeners) {\n try {\n listener(...args);\n } catch (err) {\n logger.error({ event, err }, 'Error in event listener');\n }\n }\n return true;\n }\n\n public removeAllListeners(event?: string): this {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n return this;\n }\n\n // ============================================\n // IConnectionProvider Implementation\n // ============================================\n\n /**\n * Connect to cluster nodes (IConnectionProvider interface).\n * Alias for start() method.\n */\n public async connect(): Promise<void> {\n return this.start();\n }\n\n /**\n * Get connection for a specific key (IConnectionProvider interface).\n * Routes to partition owner based on key hash when smart routing is enabled.\n * @throws Error if not connected\n */\n public getConnection(key: string): WebSocket {\n if (!this.isConnected()) {\n throw new Error('ClusterClient not connected');\n }\n\n this.routingMetrics.totalRoutes++;\n\n // If not in direct routing mode or routing not active, use fallback\n if (this.config.routingMode !== 'direct' || !this.routingActive) {\n this.routingMetrics.fallbackRoutes++;\n return this.getFallbackConnection();\n }\n\n // Try to route to partition owner\n const routing = this.partitionRouter.route(key);\n\n // No partition map available\n if (!routing) {\n this.routingMetrics.partitionMisses++;\n logger.debug({ key }, 'No partition map available, using fallback');\n return this.getFallbackConnection();\n }\n\n const owner = routing.nodeId;\n\n // Check if owner is connected\n if (!this.connectionPool.isNodeConnected(owner)) {\n this.routingMetrics.fallbackRoutes++;\n logger.debug({ key, owner }, 'Partition owner not connected, using fallback');\n // Request partition map refresh since owner might have changed\n this.requestPartitionMapRefresh();\n return this.getFallbackConnection();\n }\n\n // Get connection to owner\n const socket = this.connectionPool.getConnection(owner);\n if (!socket) {\n this.routingMetrics.fallbackRoutes++;\n logger.debug({ key, owner }, 'Could not get connection to owner, using fallback');\n return this.getFallbackConnection();\n }\n\n this.routingMetrics.directRoutes++;\n return socket;\n }\n\n /**\n * Get fallback connection when owner is unavailable.\n * @throws Error if no connection available\n */\n private getFallbackConnection(): WebSocket {\n const conn = this.connectionPool.getAnyHealthyConnection();\n if (!conn?.socket) {\n throw new Error('No healthy connection available');\n }\n return conn.socket;\n }\n\n /**\n * Request a partition map refresh in the background.\n * Called when routing to an unknown/disconnected owner.\n */\n private requestPartitionMapRefresh(): void {\n this.partitionRouter.refreshPartitionMap().catch(err => {\n logger.error({ err }, 'Failed to refresh partition map');\n });\n }\n\n /**\n * Request partition map from a specific node.\n * Called on first node connection.\n */\n private requestPartitionMapFromNode(nodeId: string): void {\n const socket = this.connectionPool.getConnection(nodeId);\n if (socket) {\n logger.debug({ nodeId }, 'Requesting partition map from node');\n socket.send(serialize({\n type: 'PARTITION_MAP_REQUEST',\n payload: {\n currentVersion: this.partitionRouter.getMapVersion(),\n },\n }));\n }\n }\n\n /**\n * Check if at least one connection is active (IConnectionProvider interface).\n */\n public isConnected(): boolean {\n return this.connectionPool.getConnectedNodes().length > 0;\n }\n\n /**\n * Send data via the appropriate connection (IConnectionProvider interface).\n * Routes based on key if provided.\n */\n public send(data: ArrayBuffer | Uint8Array, key?: string): void {\n if (!this.isConnected()) {\n throw new Error('ClusterClient not connected');\n }\n\n const socket = key ? this.getConnection(key) : this.getAnyConnection();\n socket.send(data);\n }\n\n /**\n * Send data with automatic retry and rerouting on failure.\n * @param data - Data to send\n * @param key - Optional key for routing\n * @param options - Retry options\n * @throws Error after max retries exceeded\n */\n public async sendWithRetry(\n data: ArrayBuffer | Uint8Array,\n key?: string,\n options: {\n maxRetries?: number;\n retryDelayMs?: number;\n retryOnNotOwner?: boolean;\n } = {}\n ): Promise<void> {\n const {\n maxRetries = 3,\n retryDelayMs = 100,\n retryOnNotOwner = true,\n } = options;\n\n let lastError: Error | null = null;\n let nodeId: string | null = null;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n // Get the target node for circuit breaker tracking\n if (key && this.routingActive) {\n const routing = this.partitionRouter.route(key);\n nodeId = routing?.nodeId ?? null;\n }\n\n // Check circuit breaker\n if (nodeId && !this.canUseNode(nodeId)) {\n logger.debug({ nodeId, attempt }, 'Circuit open, using fallback');\n nodeId = null; // Force fallback\n }\n\n // Get connection and send\n const socket = key && nodeId\n ? this.connectionPool.getConnection(nodeId)\n : this.getAnyConnection();\n\n if (!socket) {\n throw new Error('No connection available');\n }\n\n socket.send(data);\n\n // Record success if using a specific node\n if (nodeId) {\n this.recordSuccess(nodeId);\n }\n\n return; // Success\n } catch (error) {\n lastError = error as Error;\n\n // Record failure if using a specific node\n if (nodeId) {\n this.recordFailure(nodeId);\n }\n\n const errorCode = (error as any)?.code;\n\n // Check if error is retryable\n if (this.isRetryableError(error)) {\n logger.debug(\n { attempt, maxRetries, errorCode, nodeId },\n 'Retryable error, will retry'\n );\n\n // Handle specific error types\n if (errorCode === 'NOT_OWNER' && retryOnNotOwner) {\n // Wait for partition map update\n await this.waitForPartitionMapUpdateInternal(2000);\n } else if (errorCode === 'CONNECTION_CLOSED' || !this.isConnected()) {\n // Wait for reconnection\n await this.waitForConnectionInternal(5000);\n }\n\n // Small delay before retry\n await this.delay(retryDelayMs * (attempt + 1)); // Exponential backoff\n continue;\n }\n\n // Non-retryable error, fail immediately\n throw error;\n }\n }\n\n throw new Error(\n `Operation failed after ${maxRetries} retries: ${lastError?.message}`\n );\n }\n\n /**\n * Check if an error is retryable.\n */\n private isRetryableError(error: any): boolean {\n const code = error?.code;\n const message = error?.message || '';\n\n return (\n code === 'NOT_OWNER' ||\n code === 'CONNECTION_CLOSED' ||\n code === 'TIMEOUT' ||\n code === 'ECONNRESET' ||\n message.includes('No active connections') ||\n message.includes('No connection available') ||\n message.includes('No healthy connection')\n );\n }\n\n /**\n * Wait for partition map update.\n */\n private waitForPartitionMapUpdateInternal(timeoutMs: number): Promise<void> {\n return new Promise((resolve) => {\n const timeout = setTimeout(resolve, timeoutMs);\n\n const handler = () => {\n clearTimeout(timeout);\n this.off('partitionMapUpdated', handler);\n resolve();\n };\n\n this.on('partitionMapUpdated', handler);\n });\n }\n\n /**\n * Wait for at least one connection to be available.\n */\n private waitForConnectionInternal(timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.isConnected()) {\n resolve();\n return;\n }\n\n const timeout = setTimeout(() => {\n this.off('connected', handler);\n reject(new Error('Connection timeout'));\n }, timeoutMs);\n\n const handler = () => {\n clearTimeout(timeout);\n this.off('connected', handler);\n resolve();\n };\n\n this.on('connected', handler);\n });\n }\n\n /**\n * Helper delay function.\n */\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // ============================================\n // Cluster-Specific Methods\n // ============================================\n\n /**\n * Initialize cluster connections\n */\n public async start(): Promise<void> {\n if (this.initialized) return;\n\n logger.info({ seedNodes: this.config.seedNodes }, 'Starting cluster client');\n\n // Connect to seed nodes\n for (let i = 0; i < this.config.seedNodes.length; i++) {\n const endpoint = this.config.seedNodes[i];\n const nodeId = `seed-${i}`; // Temporary ID until we get real ID from handshake\n await this.connectionPool.addNode(nodeId, endpoint);\n }\n\n // Start health monitoring\n this.connectionPool.startHealthCheck();\n\n // Start periodic partition map refresh\n this.partitionRouter.startPeriodicRefresh();\n\n this.initialized = true;\n\n // Wait for first partition map\n await this.waitForPartitionMap();\n }\n\n /**\n * Set authentication token\n */\n public setAuthToken(token: string): void {\n this.connectionPool.setAuthToken(token);\n }\n\n /**\n * Send directly to partition owner\n */\n public sendDirect(key: string, message: any): boolean {\n const connection = this.partitionRouter.routeToConnection(key);\n if (!connection) {\n logger.warn({ key }, 'No route available for key');\n return false;\n }\n\n // Add routing metadata\n const routedMessage = {\n ...message,\n _routing: {\n partitionId: this.partitionRouter.getPartitionId(key),\n mapVersion: this.partitionRouter.getMapVersion(),\n },\n };\n\n connection.socket.send(serialize(routedMessage));\n return true;\n }\n\n /**\n * Send to primary node for server-side forwarding\n */\n public sendForward(message: any): boolean {\n return this.connectionPool.sendToPrimary(message);\n }\n\n /**\n * Send batch of operations with routing\n */\n public sendBatch(operations: Array<{ key: string; message: any }>): Map<string, boolean> {\n const results = new Map<string, boolean>();\n\n if (this.config.routingMode === 'direct' && this.routingActive) {\n // Group by target node\n const nodeMessages = new Map<string, any[]>();\n\n for (const { key, message } of operations) {\n const routing = this.partitionRouter.route(key);\n const nodeId = routing?.nodeId ?? 'primary';\n\n if (!nodeMessages.has(nodeId)) {\n nodeMessages.set(nodeId, []);\n }\n nodeMessages.get(nodeId)!.push({ key, message });\n }\n\n // Send to each node\n for (const [nodeId, messages] of nodeMessages) {\n let success: boolean;\n if (nodeId === 'primary') {\n success = this.connectionPool.sendToPrimary({\n type: 'OP_BATCH',\n payload: { ops: messages.map(m => m.message) },\n });\n } else {\n success = this.connectionPool.send(nodeId, {\n type: 'OP_BATCH',\n payload: { ops: messages.map(m => m.message) },\n });\n }\n\n for (const { key } of messages) {\n results.set(key, success);\n }\n }\n } else {\n // Forward all to primary\n const success = this.connectionPool.sendToPrimary({\n type: 'OP_BATCH',\n payload: { ops: operations.map(o => o.message) },\n });\n\n for (const { key } of operations) {\n results.set(key, success);\n }\n }\n\n return results;\n }\n\n /**\n * Get connection pool health status\n */\n public getHealthStatus(): Map<string, NodeHealth> {\n return this.connectionPool.getHealthStatus();\n }\n\n /**\n * Get partition router stats\n */\n public getRouterStats(): ReturnType<PartitionRouter['getStats']> {\n return this.partitionRouter.getStats();\n }\n\n /**\n * Get routing metrics for monitoring smart routing effectiveness.\n */\n public getRoutingMetrics(): RoutingMetrics {\n return { ...this.routingMetrics };\n }\n\n /**\n * Reset routing metrics counters.\n * Useful for monitoring intervals.\n */\n public resetRoutingMetrics(): void {\n this.routingMetrics.directRoutes = 0;\n this.routingMetrics.fallbackRoutes = 0;\n this.routingMetrics.partitionMisses = 0;\n this.routingMetrics.totalRoutes = 0;\n }\n\n /**\n * Check if cluster routing is active\n */\n public isRoutingActive(): boolean {\n return this.routingActive;\n }\n\n /**\n * Get list of connected nodes\n */\n public getConnectedNodes(): string[] {\n return this.connectionPool.getConnectedNodes();\n }\n\n /**\n * Check if cluster client is initialized\n */\n public isInitialized(): boolean {\n return this.initialized;\n }\n\n /**\n * Force refresh of partition map\n */\n public async refreshPartitionMap(): Promise<void> {\n await this.partitionRouter.refreshPartitionMap();\n }\n\n /**\n * Shutdown cluster client (IConnectionProvider interface).\n */\n public async close(): Promise<void> {\n this.partitionRouter.close();\n this.connectionPool.close();\n this.initialized = false;\n this.routingActive = false;\n logger.info('Cluster client closed');\n }\n\n // ============================================\n // Internal Access for TopGunClient\n // ============================================\n\n /**\n * Get the connection pool (for internal use)\n */\n public getConnectionPool(): ConnectionPool {\n return this.connectionPool;\n }\n\n /**\n * Get the partition router (for internal use)\n */\n public getPartitionRouter(): PartitionRouter {\n return this.partitionRouter;\n }\n\n /**\n * Get any healthy WebSocket connection (IConnectionProvider interface).\n * @throws Error if not connected\n */\n public getAnyConnection(): WebSocket {\n const conn = this.connectionPool.getAnyHealthyConnection();\n if (!conn?.socket) {\n throw new Error('No healthy connection available');\n }\n return conn.socket;\n }\n\n /**\n * Get any healthy WebSocket connection, or null if none available.\n * Use this for optional connection checks.\n */\n public getAnyConnectionOrNull(): WebSocket | null {\n const conn = this.connectionPool.getAnyHealthyConnection();\n return conn?.socket ?? null;\n }\n\n // ============================================\n // Circuit Breaker Methods\n // ============================================\n\n /**\n * Get circuit breaker state for a node.\n */\n public getCircuit(nodeId: string): CircuitState {\n let circuit = this.circuits.get(nodeId);\n if (!circuit) {\n circuit = { failures: 0, lastFailure: 0, state: 'closed' };\n this.circuits.set(nodeId, circuit);\n }\n return circuit;\n }\n\n /**\n * Check if a node can be used (circuit not open).\n */\n public canUseNode(nodeId: string): boolean {\n const circuit = this.getCircuit(nodeId);\n\n if (circuit.state === 'closed') {\n return true;\n }\n\n if (circuit.state === 'open') {\n // Check if reset timeout elapsed\n if (Date.now() - circuit.lastFailure > this.circuitBreakerConfig.resetTimeoutMs) {\n circuit.state = 'half-open';\n logger.debug({ nodeId }, 'Circuit breaker half-open, allowing test request');\n this.emit('circuit:half-open', nodeId);\n return true; // Allow one test request\n }\n return false;\n }\n\n // half-open: allow requests\n return true;\n }\n\n /**\n * Record a successful operation to a node.\n * Resets circuit breaker on success.\n */\n public recordSuccess(nodeId: string): void {\n const circuit = this.getCircuit(nodeId);\n const wasOpen = circuit.state !== 'closed';\n\n circuit.failures = 0;\n circuit.state = 'closed';\n\n if (wasOpen) {\n logger.info({ nodeId }, 'Circuit breaker closed after success');\n this.emit('circuit:closed', nodeId);\n }\n }\n\n /**\n * Record a failed operation to a node.\n * Opens circuit breaker after threshold failures.\n */\n public recordFailure(nodeId: string): void {\n const circuit = this.getCircuit(nodeId);\n circuit.failures++;\n circuit.lastFailure = Date.now();\n\n if (circuit.failures >= this.circuitBreakerConfig.failureThreshold) {\n if (circuit.state !== 'open') {\n circuit.state = 'open';\n logger.warn({ nodeId, failures: circuit.failures }, 'Circuit breaker opened');\n this.emit('circuit:open', nodeId);\n }\n }\n }\n\n /**\n * Get all circuit breaker states.\n */\n public getCircuitStates(): Map<string, CircuitState> {\n return new Map(this.circuits);\n }\n\n /**\n * Reset circuit breaker for a specific node.\n */\n public resetCircuit(nodeId: string): void {\n this.circuits.delete(nodeId);\n logger.debug({ nodeId }, 'Circuit breaker reset');\n }\n\n /**\n * Reset all circuit breakers.\n */\n public resetAllCircuits(): void {\n this.circuits.clear();\n logger.debug('All circuit breakers reset');\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private setupEventHandlers(): void {\n // Connection pool events\n this.connectionPool.on('node:connected', (nodeId: string) => {\n logger.debug({ nodeId }, 'Node connected');\n\n // Request partition map on first connection if not already received\n if (this.partitionRouter.getMapVersion() === 0) {\n this.requestPartitionMapFromNode(nodeId);\n }\n\n if (this.connectionPool.getConnectedNodes().length === 1) {\n this.emit('connected');\n }\n });\n\n this.connectionPool.on('node:disconnected', (nodeId: string, reason: string) => {\n logger.debug({ nodeId, reason }, 'Node disconnected');\n if (this.connectionPool.getConnectedNodes().length === 0) {\n this.routingActive = false;\n this.emit('disconnected', reason);\n }\n });\n\n this.connectionPool.on('node:unhealthy', (nodeId: string, reason: string) => {\n logger.warn({ nodeId, reason }, 'Node unhealthy');\n });\n\n this.connectionPool.on('error', (nodeId: string, error: Error) => {\n this.emit('error', error);\n });\n\n // Forward messages from connection pool\n this.connectionPool.on('message', (nodeId: string, data: any) => {\n this.emit('message', nodeId, data);\n });\n\n // Partition router events\n this.partitionRouter.on('partitionMap:updated', (version: number, changesCount: number) => {\n if (!this.routingActive && this.partitionRouter.hasPartitionMap()) {\n this.routingActive = true;\n logger.info({ version }, 'Direct routing activated');\n this.emit('routing:active');\n }\n this.emit('partitionMap:ready', version);\n // Emit IConnectionProvider compatible event\n this.emit('partitionMapUpdated');\n });\n\n this.partitionRouter.on('routing:miss', (key: string, expected: string, actual: string) => {\n logger.debug({ key, expected, actual }, 'Routing miss detected');\n });\n }\n\n private async waitForPartitionMap(timeoutMs: number = 10000): Promise<void> {\n if (this.partitionRouter.hasPartitionMap()) {\n this.routingActive = true;\n return;\n }\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n this.partitionRouter.off('partitionMap:updated', onUpdate);\n // Don't reject - fallback mode will be used\n logger.warn('Partition map not received, using fallback routing');\n resolve();\n }, timeoutMs);\n\n const onUpdate = () => {\n clearTimeout(timeout);\n this.partitionRouter.off('partitionMap:updated', onUpdate);\n this.routingActive = true;\n resolve();\n };\n\n this.partitionRouter.once('partitionMap:updated', onUpdate);\n });\n }\n}\n","/**\n * ConnectionPool - Manages WebSocket connections to multiple cluster nodes\n *\n * Features:\n * - Maintains connections to all known cluster nodes\n * - Automatic reconnection with exponential backoff\n * - Health monitoring and status tracking\n * - Connection lifecycle management\n */\n\nimport {\n ConnectionPoolConfig,\n DEFAULT_CONNECTION_POOL_CONFIG,\n ConnectionState,\n NodeHealth,\n} from '@topgunbuild/core';\nimport { serialize, deserialize } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport type ConnectionPoolEventType =\n | 'node:connected'\n | 'node:disconnected'\n | 'node:healthy'\n | 'node:unhealthy'\n | 'message'\n | 'error';\n\nexport interface ConnectionPoolEvents {\n 'node:connected': (nodeId: string) => void;\n 'node:disconnected': (nodeId: string, reason: string) => void;\n 'node:healthy': (nodeId: string) => void;\n 'node:unhealthy': (nodeId: string, reason: string) => void;\n 'message': (nodeId: string, message: any) => void;\n 'error': (nodeId: string, error: Error) => void;\n}\n\ninterface NodeConnection {\n nodeId: string;\n endpoint: string;\n socket: WebSocket | null;\n state: ConnectionState;\n lastSeen: number;\n latencyMs: number;\n reconnectAttempts: number;\n reconnectTimer: ReturnType<typeof setTimeout> | null;\n pendingMessages: Uint8Array[];\n}\n\nexport class ConnectionPool {\n private readonly listeners: Map<string, Set<(...args: any[]) => void>> = new Map();\n private readonly config: ConnectionPoolConfig;\n private readonly connections: Map<string, NodeConnection> = new Map();\n private primaryNodeId: string | null = null;\n private healthCheckTimer: ReturnType<typeof setInterval> | null = null;\n private authToken: string | null = null;\n\n constructor(config: Partial<ConnectionPoolConfig> = {}) {\n this.config = {\n ...DEFAULT_CONNECTION_POOL_CONFIG,\n ...config,\n };\n }\n\n // ============================================\n // Event Emitter Methods (browser-compatible)\n // ============================================\n\n public on(event: string, listener: (...args: any[]) => void): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n return this;\n }\n\n public off(event: string, listener: (...args: any[]) => void): this {\n this.listeners.get(event)?.delete(listener);\n return this;\n }\n\n public emit(event: string, ...args: any[]): boolean {\n const eventListeners = this.listeners.get(event);\n if (!eventListeners || eventListeners.size === 0) {\n return false;\n }\n for (const listener of eventListeners) {\n try {\n listener(...args);\n } catch (err) {\n logger.error({ event, err }, 'Error in event listener');\n }\n }\n return true;\n }\n\n public removeAllListeners(event?: string): this {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n return this;\n }\n\n /**\n * Set authentication token for all connections\n */\n public setAuthToken(token: string): void {\n this.authToken = token;\n // Re-authenticate existing connections\n for (const conn of this.connections.values()) {\n if (conn.state === 'CONNECTED') {\n this.sendAuth(conn);\n }\n }\n }\n\n /**\n * Add a node to the connection pool\n */\n public async addNode(nodeId: string, endpoint: string): Promise<void> {\n if (this.connections.has(nodeId)) {\n const existing = this.connections.get(nodeId)!;\n if (existing.endpoint !== endpoint) {\n // Endpoint changed, reconnect\n await this.removeNode(nodeId);\n } else {\n return; // Already connected\n }\n }\n\n const connection: NodeConnection = {\n nodeId,\n endpoint,\n socket: null,\n state: 'DISCONNECTED',\n lastSeen: 0,\n latencyMs: 0,\n reconnectAttempts: 0,\n reconnectTimer: null,\n pendingMessages: [],\n };\n\n this.connections.set(nodeId, connection);\n\n // Set first node as primary\n if (!this.primaryNodeId) {\n this.primaryNodeId = nodeId;\n }\n\n await this.connect(nodeId);\n }\n\n /**\n * Remove a node from the connection pool\n */\n public async removeNode(nodeId: string): Promise<void> {\n const connection = this.connections.get(nodeId);\n if (!connection) return;\n\n // Clear reconnect timer\n if (connection.reconnectTimer) {\n clearTimeout(connection.reconnectTimer);\n connection.reconnectTimer = null;\n }\n\n // Close socket\n if (connection.socket) {\n connection.socket.onclose = null; // Prevent reconnect\n connection.socket.close();\n connection.socket = null;\n }\n\n this.connections.delete(nodeId);\n\n // Update primary if needed\n if (this.primaryNodeId === nodeId) {\n this.primaryNodeId = this.connections.size > 0\n ? this.connections.keys().next().value ?? null\n : null;\n }\n\n logger.info({ nodeId }, 'Node removed from connection pool');\n }\n\n /**\n * Get connection for a specific node\n */\n public getConnection(nodeId: string): WebSocket | null {\n const connection = this.connections.get(nodeId);\n if (!connection || connection.state !== 'AUTHENTICATED') {\n return null;\n }\n return connection.socket;\n }\n\n /**\n * Get primary connection (first/seed node)\n */\n public getPrimaryConnection(): WebSocket | null {\n if (!this.primaryNodeId) return null;\n return this.getConnection(this.primaryNodeId);\n }\n\n /**\n * Get any healthy connection\n */\n public getAnyHealthyConnection(): { nodeId: string; socket: WebSocket } | null {\n for (const [nodeId, conn] of this.connections) {\n if (conn.state === 'AUTHENTICATED' && conn.socket) {\n return { nodeId, socket: conn.socket };\n }\n }\n return null;\n }\n\n /**\n * Send message to a specific node\n */\n public send(nodeId: string, message: any): boolean {\n const connection = this.connections.get(nodeId);\n if (!connection) {\n logger.warn({ nodeId }, 'Cannot send: node not in pool');\n return false;\n }\n\n const data = serialize(message);\n\n if (connection.state === 'AUTHENTICATED' && connection.socket?.readyState === WebSocket.OPEN) {\n connection.socket.send(data);\n return true;\n }\n\n // Queue message for later\n if (connection.pendingMessages.length < 1000) {\n connection.pendingMessages.push(data);\n return true;\n }\n\n logger.warn({ nodeId }, 'Message queue full, dropping message');\n return false;\n }\n\n /**\n * Send message to primary node\n */\n public sendToPrimary(message: any): boolean {\n if (!this.primaryNodeId) {\n logger.warn('No primary node available');\n return false;\n }\n return this.send(this.primaryNodeId, message);\n }\n\n /**\n * Get health status for all nodes\n */\n public getHealthStatus(): Map<string, NodeHealth> {\n const status = new Map<string, NodeHealth>();\n for (const [nodeId, conn] of this.connections) {\n status.set(nodeId, {\n nodeId,\n state: conn.state,\n lastSeen: conn.lastSeen,\n latencyMs: conn.latencyMs,\n reconnectAttempts: conn.reconnectAttempts,\n });\n }\n return status;\n }\n\n /**\n * Get list of connected node IDs\n */\n public getConnectedNodes(): string[] {\n return Array.from(this.connections.entries())\n .filter(([_, conn]) => conn.state === 'AUTHENTICATED')\n .map(([nodeId]) => nodeId);\n }\n\n /**\n * Get all node IDs\n */\n public getAllNodes(): string[] {\n return Array.from(this.connections.keys());\n }\n\n /**\n * Check if node is connected and authenticated\n */\n public isNodeConnected(nodeId: string): boolean {\n const conn = this.connections.get(nodeId);\n return conn?.state === 'AUTHENTICATED';\n }\n\n /**\n * Check if connected to a specific node.\n * Alias for isNodeConnected() for IConnectionProvider compatibility.\n */\n public isConnected(nodeId: string): boolean {\n return this.isNodeConnected(nodeId);\n }\n\n /**\n * Start health monitoring\n */\n public startHealthCheck(): void {\n if (this.healthCheckTimer) return;\n\n this.healthCheckTimer = setInterval(() => {\n this.performHealthCheck();\n }, this.config.healthCheckIntervalMs);\n }\n\n /**\n * Stop health monitoring\n */\n public stopHealthCheck(): void {\n if (this.healthCheckTimer) {\n clearInterval(this.healthCheckTimer);\n this.healthCheckTimer = null;\n }\n }\n\n /**\n * Close all connections and cleanup\n */\n public close(): void {\n this.stopHealthCheck();\n\n for (const nodeId of this.connections.keys()) {\n this.removeNode(nodeId);\n }\n\n this.connections.clear();\n this.primaryNodeId = null;\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private async connect(nodeId: string): Promise<void> {\n const connection = this.connections.get(nodeId);\n if (!connection) return;\n\n if (connection.state === 'CONNECTING' || connection.state === 'CONNECTED') {\n return;\n }\n\n connection.state = 'CONNECTING';\n logger.info({ nodeId, endpoint: connection.endpoint }, 'Connecting to node');\n\n try {\n const socket = new WebSocket(connection.endpoint);\n socket.binaryType = 'arraybuffer';\n connection.socket = socket;\n\n socket.onopen = () => {\n connection.state = 'CONNECTED';\n connection.reconnectAttempts = 0;\n connection.lastSeen = Date.now();\n logger.info({ nodeId }, 'Connected to node');\n this.emit('node:connected', nodeId);\n\n // Send auth if we have token\n if (this.authToken) {\n this.sendAuth(connection);\n }\n\n // Flush pending messages after auth\n // Note: Messages will be sent after AUTH_ACK\n };\n\n socket.onmessage = (event) => {\n connection.lastSeen = Date.now();\n this.handleMessage(nodeId, event);\n };\n\n socket.onerror = (error) => {\n logger.error({ nodeId, error }, 'WebSocket error');\n this.emit('error', nodeId, error instanceof Error ? error : new Error('WebSocket error'));\n };\n\n socket.onclose = () => {\n const wasConnected = connection.state === 'AUTHENTICATED';\n connection.state = 'DISCONNECTED';\n connection.socket = null;\n\n if (wasConnected) {\n this.emit('node:disconnected', nodeId, 'Connection closed');\n }\n\n // Schedule reconnect\n this.scheduleReconnect(nodeId);\n };\n\n } catch (error) {\n connection.state = 'FAILED';\n logger.error({ nodeId, error }, 'Failed to connect');\n this.scheduleReconnect(nodeId);\n }\n }\n\n private sendAuth(connection: NodeConnection): void {\n if (!this.authToken || !connection.socket) return;\n\n connection.socket.send(serialize({\n type: 'AUTH',\n token: this.authToken,\n }));\n }\n\n private handleMessage(nodeId: string, event: MessageEvent): void {\n const connection = this.connections.get(nodeId);\n if (!connection) return;\n\n let message: any;\n try {\n if (event.data instanceof ArrayBuffer) {\n message = deserialize(new Uint8Array(event.data));\n } else {\n message = JSON.parse(event.data);\n }\n } catch (e) {\n logger.error({ nodeId, error: e }, 'Failed to parse message');\n return;\n }\n\n // Handle auth response\n if (message.type === 'AUTH_ACK') {\n connection.state = 'AUTHENTICATED';\n logger.info({ nodeId }, 'Authenticated with node');\n this.emit('node:healthy', nodeId);\n\n // Flush pending messages\n this.flushPendingMessages(connection);\n return;\n }\n\n if (message.type === 'AUTH_REQUIRED') {\n if (this.authToken) {\n this.sendAuth(connection);\n }\n return;\n }\n\n if (message.type === 'AUTH_FAIL') {\n logger.error({ nodeId, error: message.error }, 'Authentication failed');\n connection.state = 'FAILED';\n return;\n }\n\n if (message.type === 'PONG') {\n // Update latency\n if (message.timestamp) {\n connection.latencyMs = Date.now() - message.timestamp;\n }\n return;\n }\n\n // Handle partition map updates\n if (message.type === 'PARTITION_MAP' || message.type === 'PARTITION_MAP_DELTA') {\n this.emit('message', nodeId, message);\n return;\n }\n\n // Emit other messages\n this.emit('message', nodeId, message);\n }\n\n private flushPendingMessages(connection: NodeConnection): void {\n if (!connection.socket || connection.state !== 'AUTHENTICATED') return;\n\n const pending = connection.pendingMessages;\n connection.pendingMessages = [];\n\n for (const data of pending) {\n if (connection.socket.readyState === WebSocket.OPEN) {\n connection.socket.send(data);\n }\n }\n\n if (pending.length > 0) {\n logger.debug({ nodeId: connection.nodeId, count: pending.length }, 'Flushed pending messages');\n }\n }\n\n private scheduleReconnect(nodeId: string): void {\n const connection = this.connections.get(nodeId);\n if (!connection) return;\n\n // Clear existing timer\n if (connection.reconnectTimer) {\n clearTimeout(connection.reconnectTimer);\n connection.reconnectTimer = null;\n }\n\n // Check max attempts\n if (connection.reconnectAttempts >= this.config.maxReconnectAttempts) {\n connection.state = 'FAILED';\n logger.error({ nodeId, attempts: connection.reconnectAttempts }, 'Max reconnect attempts reached');\n this.emit('node:unhealthy', nodeId, 'Max reconnect attempts reached');\n return;\n }\n\n // Calculate backoff delay\n const delay = Math.min(\n this.config.reconnectDelayMs * Math.pow(2, connection.reconnectAttempts),\n this.config.maxReconnectDelayMs\n );\n\n connection.state = 'RECONNECTING';\n connection.reconnectAttempts++;\n\n logger.info({ nodeId, delay, attempt: connection.reconnectAttempts }, 'Scheduling reconnect');\n\n connection.reconnectTimer = setTimeout(() => {\n connection.reconnectTimer = null;\n this.connect(nodeId);\n }, delay);\n }\n\n private performHealthCheck(): void {\n const now = Date.now();\n\n for (const [nodeId, connection] of this.connections) {\n // Skip nodes that are not authenticated\n if (connection.state !== 'AUTHENTICATED') continue;\n\n // Check staleness\n const timeSinceLastSeen = now - connection.lastSeen;\n if (timeSinceLastSeen > this.config.healthCheckIntervalMs * 3) {\n logger.warn({ nodeId, timeSinceLastSeen }, 'Node appears stale, sending ping');\n }\n\n // Send ping\n if (connection.socket?.readyState === WebSocket.OPEN) {\n connection.socket.send(serialize({\n type: 'PING',\n timestamp: now,\n }));\n }\n }\n }\n}\n","/**\n * PartitionRouter - Routes operations to the correct cluster node\n *\n * Features:\n * - Maintains local copy of partition map\n * - Routes keys to owner nodes using consistent hashing\n * - Handles stale routing with automatic refresh\n * - Supports fallback to server-side forwarding\n */\n\nimport {\n PartitionMap,\n PartitionRouterConfig,\n DEFAULT_PARTITION_ROUTER_CONFIG,\n PartitionMapMessage,\n PartitionMapDeltaMessage,\n PartitionChange,\n PARTITION_COUNT,\n hashString,\n} from '@topgunbuild/core';\nimport { ConnectionPool } from './ConnectionPool';\nimport { logger } from '../utils/logger';\n\nexport interface RoutingResult {\n nodeId: string;\n partitionId: number;\n isOwner: boolean;\n isBackup: boolean;\n}\n\nexport interface PartitionRouterEvents {\n 'partitionMap:updated': (version: number, changesCount: number) => void;\n 'partitionMap:stale': (currentVersion: number, lastRefresh: number) => void;\n 'routing:miss': (key: string, expectedOwner: string, actualOwner: string) => void;\n}\n\nexport class PartitionRouter {\n private readonly listeners: Map<string, Set<(...args: any[]) => void>> = new Map();\n private readonly config: PartitionRouterConfig;\n private readonly connectionPool: ConnectionPool;\n private partitionMap: PartitionMap | null = null;\n private lastRefreshTime: number = 0;\n private refreshTimer: ReturnType<typeof setInterval> | null = null;\n private pendingRefresh: Promise<void> | null = null;\n\n constructor(\n connectionPool: ConnectionPool,\n config: Partial<PartitionRouterConfig> = {}\n ) {\n this.connectionPool = connectionPool;\n this.config = {\n ...DEFAULT_PARTITION_ROUTER_CONFIG,\n ...config,\n };\n\n // Listen for partition map updates from any connection\n this.connectionPool.on('message', (nodeId: string, message: any) => {\n if (message.type === 'PARTITION_MAP') {\n this.handlePartitionMap(message as PartitionMapMessage);\n } else if (message.type === 'PARTITION_MAP_DELTA') {\n this.handlePartitionMapDelta(message as PartitionMapDeltaMessage);\n }\n });\n }\n\n // ============================================\n // Event Emitter Methods (browser-compatible)\n // ============================================\n\n public on(event: string, listener: (...args: any[]) => void): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n return this;\n }\n\n public off(event: string, listener: (...args: any[]) => void): this {\n this.listeners.get(event)?.delete(listener);\n return this;\n }\n\n public once(event: string, listener: (...args: any[]) => void): this {\n const wrapper = (...args: any[]) => {\n this.off(event, wrapper);\n listener(...args);\n };\n return this.on(event, wrapper);\n }\n\n public emit(event: string, ...args: any[]): boolean {\n const eventListeners = this.listeners.get(event);\n if (!eventListeners || eventListeners.size === 0) {\n return false;\n }\n for (const listener of eventListeners) {\n try {\n listener(...args);\n } catch (err) {\n logger.error({ event, err }, 'Error in event listener');\n }\n }\n return true;\n }\n\n public removeListener(event: string, listener: (...args: any[]) => void): this {\n return this.off(event, listener);\n }\n\n public removeAllListeners(event?: string): this {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n return this;\n }\n\n /**\n * Get the partition ID for a given key\n */\n public getPartitionId(key: string): number {\n return Math.abs(hashString(key)) % PARTITION_COUNT;\n }\n\n /**\n * Route a key to the owner node\n */\n public route(key: string): RoutingResult | null {\n if (!this.partitionMap) {\n return null;\n }\n\n const partitionId = this.getPartitionId(key);\n const partition = this.partitionMap.partitions.find(p => p.partitionId === partitionId);\n\n if (!partition) {\n logger.warn({ key, partitionId }, 'Partition not found in map');\n return null;\n }\n\n return {\n nodeId: partition.ownerNodeId,\n partitionId,\n isOwner: true,\n isBackup: false,\n };\n }\n\n /**\n * Route a key and get the WebSocket connection to use\n */\n public routeToConnection(key: string): { nodeId: string; socket: WebSocket } | null {\n const routing = this.route(key);\n\n if (!routing) {\n // No partition map, use fallback\n if (this.config.fallbackMode === 'forward') {\n const primary = this.connectionPool.getAnyHealthyConnection();\n if (primary) {\n return primary;\n }\n }\n return null;\n }\n\n // Try to get connection to owner\n const socket = this.connectionPool.getConnection(routing.nodeId);\n if (socket) {\n return { nodeId: routing.nodeId, socket };\n }\n\n // Owner not available, try backup\n const partition = this.partitionMap!.partitions.find(p => p.partitionId === routing.partitionId);\n if (partition) {\n for (const backupId of partition.backupNodeIds) {\n const backupSocket = this.connectionPool.getConnection(backupId);\n if (backupSocket) {\n logger.debug({ key, owner: routing.nodeId, backup: backupId }, 'Using backup node');\n return { nodeId: backupId, socket: backupSocket };\n }\n }\n }\n\n // Fallback to any connection\n if (this.config.fallbackMode === 'forward') {\n return this.connectionPool.getAnyHealthyConnection();\n }\n\n return null;\n }\n\n /**\n * Get routing info for multiple keys (batch routing)\n */\n public routeBatch(keys: string[]): Map<string, RoutingResult[]> {\n const result = new Map<string, RoutingResult[]>();\n\n for (const key of keys) {\n const routing = this.route(key);\n if (routing) {\n const nodeId = routing.nodeId;\n if (!result.has(nodeId)) {\n result.set(nodeId, []);\n }\n result.get(nodeId)!.push({ ...routing, key } as any);\n }\n }\n\n return result;\n }\n\n /**\n * Get all partitions owned by a specific node\n */\n public getPartitionsForNode(nodeId: string): number[] {\n if (!this.partitionMap) return [];\n\n return this.partitionMap.partitions\n .filter(p => p.ownerNodeId === nodeId)\n .map(p => p.partitionId);\n }\n\n /**\n * Get current partition map version\n */\n public getMapVersion(): number {\n return this.partitionMap?.version ?? 0;\n }\n\n /**\n * Check if partition map is available\n */\n public hasPartitionMap(): boolean {\n return this.partitionMap !== null;\n }\n\n /**\n * Get owner node for a key.\n * Returns null if partition map is not available.\n */\n public getOwner(key: string): string | null {\n if (!this.partitionMap) return null;\n\n const partitionId = this.getPartitionId(key);\n const partition = this.partitionMap.partitions.find(p => p.partitionId === partitionId);\n\n return partition?.ownerNodeId ?? null;\n }\n\n /**\n * Get backup nodes for a key.\n * Returns empty array if partition map is not available.\n */\n public getBackups(key: string): string[] {\n if (!this.partitionMap) return [];\n\n const partitionId = this.getPartitionId(key);\n const partition = this.partitionMap.partitions.find(p => p.partitionId === partitionId);\n\n return partition?.backupNodeIds ?? [];\n }\n\n /**\n * Get the full partition map.\n * Returns null if not available.\n */\n public getMap(): PartitionMap | null {\n return this.partitionMap;\n }\n\n /**\n * Update entire partition map.\n * Only accepts newer versions.\n */\n public updateMap(map: PartitionMap): boolean {\n if (this.partitionMap && map.version <= this.partitionMap.version) {\n return false;\n }\n\n this.partitionMap = map;\n this.lastRefreshTime = Date.now();\n\n // Update connection pool with node endpoints\n this.updateConnectionPool(map);\n\n const changesCount = map.partitions.length;\n logger.info({\n version: map.version,\n partitions: map.partitionCount,\n nodes: map.nodes.length\n }, 'Partition map updated via updateMap');\n\n this.emit('partitionMap:updated', map.version, changesCount);\n return true;\n }\n\n /**\n * Update a single partition (for delta updates).\n */\n public updatePartition(partitionId: number, owner: string, backups: string[]): void {\n if (!this.partitionMap) return;\n\n const partition = this.partitionMap.partitions.find(p => p.partitionId === partitionId);\n if (partition) {\n partition.ownerNodeId = owner;\n partition.backupNodeIds = backups;\n }\n }\n\n /**\n * Check if partition map is stale\n */\n public isMapStale(): boolean {\n if (!this.partitionMap) return true;\n\n const now = Date.now();\n return (now - this.lastRefreshTime) > this.config.maxMapStalenessMs;\n }\n\n /**\n * Request fresh partition map from server\n */\n public async refreshPartitionMap(): Promise<void> {\n if (this.pendingRefresh) {\n return this.pendingRefresh;\n }\n\n this.pendingRefresh = this.doRefreshPartitionMap();\n\n try {\n await this.pendingRefresh;\n } finally {\n this.pendingRefresh = null;\n }\n }\n\n /**\n * Start periodic partition map refresh\n */\n public startPeriodicRefresh(): void {\n if (this.refreshTimer) return;\n\n this.refreshTimer = setInterval(() => {\n if (this.isMapStale()) {\n this.emit('partitionMap:stale', this.getMapVersion(), this.lastRefreshTime);\n this.refreshPartitionMap().catch(err => {\n logger.error({ error: err }, 'Failed to refresh partition map');\n });\n }\n }, this.config.mapRefreshIntervalMs);\n }\n\n /**\n * Stop periodic refresh\n */\n public stopPeriodicRefresh(): void {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = null;\n }\n }\n\n /**\n * Handle NOT_OWNER error from server\n */\n public handleNotOwnerError(key: string, actualOwner: string, newMapVersion: number): void {\n const routing = this.route(key);\n const expectedOwner = routing?.nodeId ?? 'unknown';\n\n this.emit('routing:miss', key, expectedOwner, actualOwner);\n\n // If server has newer map, request it\n if (newMapVersion > this.getMapVersion()) {\n this.refreshPartitionMap().catch(err => {\n logger.error({ error: err }, 'Failed to refresh partition map after NOT_OWNER');\n });\n }\n }\n\n /**\n * Get statistics about routing\n */\n public getStats(): {\n mapVersion: number;\n partitionCount: number;\n nodeCount: number;\n lastRefresh: number;\n isStale: boolean;\n } {\n return {\n mapVersion: this.getMapVersion(),\n partitionCount: this.partitionMap?.partitionCount ?? 0,\n nodeCount: this.partitionMap?.nodes.length ?? 0,\n lastRefresh: this.lastRefreshTime,\n isStale: this.isMapStale(),\n };\n }\n\n /**\n * Cleanup resources\n */\n public close(): void {\n this.stopPeriodicRefresh();\n this.partitionMap = null;\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private handlePartitionMap(message: PartitionMapMessage): void {\n const newMap = message.payload;\n\n // Only accept newer versions\n if (this.partitionMap && newMap.version <= this.partitionMap.version) {\n logger.debug({\n current: this.partitionMap.version,\n received: newMap.version\n }, 'Ignoring older partition map');\n return;\n }\n\n this.partitionMap = newMap;\n this.lastRefreshTime = Date.now();\n\n // Update connection pool with node endpoints\n this.updateConnectionPool(newMap);\n\n const changesCount = newMap.partitions.length;\n logger.info({\n version: newMap.version,\n partitions: newMap.partitionCount,\n nodes: newMap.nodes.length\n }, 'Partition map updated');\n\n this.emit('partitionMap:updated', newMap.version, changesCount);\n }\n\n private handlePartitionMapDelta(message: PartitionMapDeltaMessage): void {\n const delta = message.payload;\n\n // Must have base map and correct previous version\n if (!this.partitionMap) {\n logger.warn('Received delta but no base map, requesting full map');\n this.refreshPartitionMap();\n return;\n }\n\n if (delta.previousVersion !== this.partitionMap.version) {\n logger.warn({\n expected: this.partitionMap.version,\n received: delta.previousVersion\n }, 'Delta version mismatch, requesting full map');\n this.refreshPartitionMap();\n return;\n }\n\n // Apply changes\n for (const change of delta.changes) {\n this.applyPartitionChange(change);\n }\n\n this.partitionMap.version = delta.version;\n this.lastRefreshTime = Date.now();\n\n logger.info({\n version: delta.version,\n changes: delta.changes.length\n }, 'Applied partition map delta');\n\n this.emit('partitionMap:updated', delta.version, delta.changes.length);\n }\n\n private applyPartitionChange(change: PartitionChange): void {\n if (!this.partitionMap) return;\n\n const partition = this.partitionMap.partitions.find(p => p.partitionId === change.partitionId);\n if (partition) {\n partition.ownerNodeId = change.newOwner;\n // Backups would also be updated but simplified here\n }\n }\n\n private updateConnectionPool(map: PartitionMap): void {\n // Add new nodes\n for (const node of map.nodes) {\n if (node.status === 'ACTIVE' || node.status === 'JOINING') {\n this.connectionPool.addNode(node.nodeId, node.endpoints.websocket);\n }\n }\n\n // Remove nodes that are no longer in the map\n const currentNodeIds = new Set(map.nodes.map(n => n.nodeId));\n for (const nodeId of this.connectionPool.getAllNodes()) {\n if (!currentNodeIds.has(nodeId)) {\n this.connectionPool.removeNode(nodeId);\n }\n }\n }\n\n private async doRefreshPartitionMap(): Promise<void> {\n logger.debug('Requesting partition map refresh');\n\n // Send request to any connected node\n const sent = this.connectionPool.sendToPrimary({\n type: 'PARTITION_MAP_REQUEST',\n payload: {\n currentVersion: this.getMapVersion(),\n },\n });\n\n if (!sent) {\n throw new Error('No connection available to request partition map');\n }\n\n // Wait for response (handled via message event)\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.removeListener('partitionMap:updated', onUpdate);\n reject(new Error('Partition map refresh timeout'));\n }, 5000);\n\n const onUpdate = () => {\n clearTimeout(timeout);\n this.removeListener('partitionMap:updated', onUpdate);\n resolve();\n };\n\n this.once('partitionMap:updated', onUpdate);\n });\n }\n}\n","import type {\n IConnectionProvider,\n ConnectionProviderEvent,\n ConnectionEventHandler,\n SingleServerProviderConfig,\n} from '../types';\nimport { logger } from '../utils/logger';\n\n/**\n * Default configuration values for SingleServerProvider.\n */\nconst DEFAULT_CONFIG = {\n maxReconnectAttempts: 10,\n reconnectDelayMs: 1000,\n backoffMultiplier: 2,\n maxReconnectDelayMs: 30000,\n};\n\n/**\n * SingleServerProvider implements IConnectionProvider for single-server mode.\n *\n * This is an adapter that wraps direct WebSocket connection handling,\n * providing the same interface used by ClusterClient for multi-node mode.\n */\nexport class SingleServerProvider implements IConnectionProvider {\n private readonly url: string;\n private readonly config: Required<SingleServerProviderConfig>;\n private ws: WebSocket | null = null;\n private reconnectAttempts: number = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private isClosing: boolean = false;\n private listeners: Map<ConnectionProviderEvent, Set<ConnectionEventHandler>> = new Map();\n\n constructor(config: SingleServerProviderConfig) {\n this.url = config.url;\n this.config = {\n url: config.url,\n maxReconnectAttempts: config.maxReconnectAttempts ?? DEFAULT_CONFIG.maxReconnectAttempts,\n reconnectDelayMs: config.reconnectDelayMs ?? DEFAULT_CONFIG.reconnectDelayMs,\n backoffMultiplier: config.backoffMultiplier ?? DEFAULT_CONFIG.backoffMultiplier,\n maxReconnectDelayMs: config.maxReconnectDelayMs ?? DEFAULT_CONFIG.maxReconnectDelayMs,\n };\n }\n\n /**\n * Connect to the WebSocket server.\n */\n async connect(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n return; // Already connected\n }\n\n this.isClosing = false;\n\n return new Promise((resolve, reject) => {\n try {\n this.ws = new WebSocket(this.url);\n this.ws.binaryType = 'arraybuffer';\n\n this.ws.onopen = () => {\n this.reconnectAttempts = 0;\n logger.info({ url: this.url }, 'SingleServerProvider connected');\n this.emit('connected', 'default');\n resolve();\n };\n\n this.ws.onerror = (error) => {\n logger.error({ err: error, url: this.url }, 'SingleServerProvider WebSocket error');\n this.emit('error', error);\n // Don't reject here - wait for onclose\n };\n\n this.ws.onclose = (event) => {\n logger.info({ url: this.url, code: event.code }, 'SingleServerProvider disconnected');\n this.emit('disconnected', 'default');\n\n if (!this.isClosing) {\n this.scheduleReconnect();\n }\n };\n\n this.ws.onmessage = (event) => {\n this.emit('message', 'default', event.data);\n };\n\n // Set up initial connection timeout\n const timeoutId = setTimeout(() => {\n if (this.ws && this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.close();\n reject(new Error(`Connection timeout to ${this.url}`));\n }\n }, this.config.reconnectDelayMs * 5); // 5x initial delay as connection timeout\n\n // Clear timeout on successful connection\n const originalOnOpen = this.ws.onopen;\n const wsRef = this.ws;\n this.ws.onopen = (ev) => {\n clearTimeout(timeoutId);\n if (originalOnOpen) {\n originalOnOpen.call(wsRef, ev);\n }\n };\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Get connection for a specific key.\n * In single-server mode, key is ignored.\n */\n getConnection(_key: string): WebSocket {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected');\n }\n return this.ws;\n }\n\n /**\n * Get any available connection.\n */\n getAnyConnection(): WebSocket {\n return this.getConnection('');\n }\n\n /**\n * Check if connected.\n */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Get connected node IDs.\n * Single-server mode returns ['default'] when connected.\n */\n getConnectedNodes(): string[] {\n return this.isConnected() ? ['default'] : [];\n }\n\n /**\n * Subscribe to connection events.\n */\n on(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n /**\n * Unsubscribe from connection events.\n */\n off(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n this.listeners.get(event)?.delete(handler);\n }\n\n /**\n * Send data via the WebSocket connection.\n * In single-server mode, key parameter is ignored.\n */\n send(data: ArrayBuffer | Uint8Array, _key?: string): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected');\n }\n this.ws.send(data);\n }\n\n /**\n * Close the WebSocket connection.\n */\n async close(): Promise<void> {\n this.isClosing = true;\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.ws) {\n // Remove onclose handler to prevent reconnect\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n\n if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.close();\n }\n this.ws = null;\n }\n\n logger.info({ url: this.url }, 'SingleServerProvider closed');\n }\n\n /**\n * Emit an event to all listeners.\n */\n private emit(event: ConnectionProviderEvent, ...args: any[]): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(...args);\n } catch (err) {\n logger.error({ err, event }, 'Error in SingleServerProvider event handler');\n }\n }\n }\n }\n\n /**\n * Schedule a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {\n logger.error(\n { attempts: this.reconnectAttempts, url: this.url },\n 'SingleServerProvider max reconnect attempts reached'\n );\n this.emit('error', new Error('Max reconnection attempts reached'));\n return;\n }\n\n const delay = this.calculateBackoffDelay();\n logger.info(\n { delay, attempt: this.reconnectAttempts, url: this.url },\n `SingleServerProvider scheduling reconnect in ${delay}ms`\n );\n\n this.reconnectTimer = setTimeout(async () => {\n this.reconnectTimer = null;\n this.reconnectAttempts++;\n\n try {\n await this.connect();\n this.emit('reconnected', 'default');\n } catch (error) {\n logger.error({ err: error }, 'SingleServerProvider reconnection failed');\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n /**\n * Calculate backoff delay with exponential increase.\n */\n private calculateBackoffDelay(): number {\n const { reconnectDelayMs, backoffMultiplier, maxReconnectDelayMs } = this.config;\n let delay = reconnectDelayMs * Math.pow(backoffMultiplier, this.reconnectAttempts);\n delay = Math.min(delay, maxReconnectDelayMs);\n\n // Add jitter (0.5x to 1.5x)\n delay = delay * (0.5 + Math.random());\n\n return Math.floor(delay);\n }\n\n /**\n * Get the WebSocket URL this provider connects to.\n */\n getUrl(): string {\n return this.url;\n }\n\n /**\n * Get current reconnection attempt count.\n */\n getReconnectAttempts(): number {\n return this.reconnectAttempts;\n }\n\n /**\n * Reset reconnection counter.\n * Called externally after successful authentication.\n */\n resetReconnectAttempts(): void {\n this.reconnectAttempts = 0;\n }\n}\n","import type { LWWRecord, ORMapRecord } from '@topgunbuild/core';\nimport type { IStorageAdapter, OpLogEntry } from '../IStorageAdapter';\nimport { openDB } from 'idb';\nimport type { IDBPDatabase } from 'idb';\n\n/**\n * Represents an operation queued before IndexedDB is ready.\n */\ninterface QueuedOperation {\n type: 'put' | 'remove' | 'setMeta' | 'appendOpLog' | 'markOpsSynced' | 'batchPut';\n args: any[];\n resolve: (value: any) => void;\n reject: (error: any) => void;\n}\n\n/**\n * Non-blocking IndexedDB adapter that allows immediate use before initialization completes.\n *\n * Operations are queued in memory and replayed once IndexedDB is ready.\n * This enables true \"memory-first\" behavior where the UI can render immediately\n * without waiting for IndexedDB to initialize (which can take 50-500ms).\n */\nexport class IDBAdapter implements IStorageAdapter {\n private dbPromise?: Promise<IDBPDatabase>;\n private db?: IDBPDatabase;\n private isReady = false;\n private operationQueue: QueuedOperation[] = [];\n private initPromise?: Promise<void>;\n\n /**\n * Initializes IndexedDB in the background.\n * Returns immediately - does NOT block on IndexedDB being ready.\n * Use waitForReady() if you need to ensure initialization is complete.\n */\n async initialize(dbName: string): Promise<void> {\n // Start initialization but don't await it\n this.initPromise = this.initializeInternal(dbName);\n // Return immediately - non-blocking!\n }\n\n /**\n * Internal initialization that actually opens IndexedDB.\n */\n private async initializeInternal(dbName: string): Promise<void> {\n try {\n this.dbPromise = openDB(dbName, 2, {\n upgrade(db) {\n if (!db.objectStoreNames.contains('kv_store')) {\n db.createObjectStore('kv_store', { keyPath: 'key' });\n }\n if (!db.objectStoreNames.contains('op_log')) {\n db.createObjectStore('op_log', { keyPath: 'id', autoIncrement: true });\n }\n if (!db.objectStoreNames.contains('meta_store')) {\n db.createObjectStore('meta_store', { keyPath: 'key' });\n }\n },\n });\n\n this.db = await this.dbPromise;\n this.isReady = true;\n\n // Replay queued operations\n await this.flushQueue();\n } catch (error) {\n // Re-throw to allow error handling\n throw error;\n }\n }\n\n /**\n * Waits for IndexedDB to be fully initialized.\n * Call this if you need guaranteed persistence before proceeding.\n */\n async waitForReady(): Promise<void> {\n if (this.isReady) return;\n if (this.initPromise) {\n await this.initPromise;\n }\n }\n\n /**\n * Flushes all queued operations once IndexedDB is ready.\n */\n private async flushQueue(): Promise<void> {\n const queue = this.operationQueue;\n this.operationQueue = [];\n\n for (const op of queue) {\n try {\n let result: any;\n switch (op.type) {\n case 'put':\n result = await this.putInternal(op.args[0], op.args[1]);\n break;\n case 'remove':\n result = await this.removeInternal(op.args[0]);\n break;\n case 'setMeta':\n result = await this.setMetaInternal(op.args[0], op.args[1]);\n break;\n case 'appendOpLog':\n result = await this.appendOpLogInternal(op.args[0]);\n break;\n case 'markOpsSynced':\n result = await this.markOpsSyncedInternal(op.args[0]);\n break;\n case 'batchPut':\n result = await this.batchPutInternal(op.args[0]);\n break;\n }\n op.resolve(result);\n } catch (error) {\n op.reject(error);\n }\n }\n }\n\n /**\n * Queues an operation if not ready, or executes immediately if ready.\n */\n private queueOrExecute<T>(\n type: QueuedOperation['type'],\n args: any[],\n executor: () => Promise<T>\n ): Promise<T> {\n if (this.isReady) {\n return executor();\n }\n\n return new Promise<T>((resolve, reject) => {\n this.operationQueue.push({ type, args, resolve, reject });\n });\n }\n\n async close(): Promise<void> {\n if (this.db) {\n this.db.close();\n }\n }\n\n // ============================================\n // Read Operations - Wait for ready\n // ============================================\n\n async get<V>(key: string): Promise<LWWRecord<V> | ORMapRecord<V>[] | any | undefined> {\n // Read operations must wait for DB to be ready\n await this.waitForReady();\n const result = await this.db?.get('kv_store', key);\n return result?.value;\n }\n\n async getMeta(key: string): Promise<any> {\n await this.waitForReady();\n const result = await this.db?.get('meta_store', key);\n return result?.value;\n }\n\n async getPendingOps(): Promise<OpLogEntry[]> {\n await this.waitForReady();\n const all = await this.db?.getAll('op_log');\n return all?.filter((op: any) => op.synced === 0) || [];\n }\n\n async getAllKeys(): Promise<string[]> {\n await this.waitForReady();\n return (await this.db?.getAllKeys('kv_store')) as string[] || [];\n }\n\n // ============================================\n // Write Operations - Queue if not ready\n // ============================================\n\n async put(key: string, value: any): Promise<void> {\n return this.queueOrExecute('put', [key, value], () => this.putInternal(key, value));\n }\n\n private async putInternal(key: string, value: any): Promise<void> {\n await this.db?.put('kv_store', { key, value });\n }\n\n async remove(key: string): Promise<void> {\n return this.queueOrExecute('remove', [key], () => this.removeInternal(key));\n }\n\n private async removeInternal(key: string): Promise<void> {\n await this.db?.delete('kv_store', key);\n }\n\n async setMeta(key: string, value: any): Promise<void> {\n return this.queueOrExecute('setMeta', [key, value], () => this.setMetaInternal(key, value));\n }\n\n private async setMetaInternal(key: string, value: any): Promise<void> {\n await this.db?.put('meta_store', { key, value });\n }\n\n async batchPut(entries: Map<string, any>): Promise<void> {\n return this.queueOrExecute('batchPut', [entries], () => this.batchPutInternal(entries));\n }\n\n private async batchPutInternal(entries: Map<string, any>): Promise<void> {\n const tx = this.db?.transaction('kv_store', 'readwrite');\n if (!tx) return;\n\n await Promise.all(\n Array.from(entries.entries()).map(([key, value]) =>\n tx.store.put({ key, value })\n )\n );\n await tx.done;\n }\n\n async appendOpLog(entry: any): Promise<number> {\n return this.queueOrExecute('appendOpLog', [entry], () => this.appendOpLogInternal(entry));\n }\n\n private async appendOpLogInternal(entry: any): Promise<number> {\n const entryToSave = { ...entry, synced: 0 };\n return await this.db?.add('op_log', entryToSave) as number;\n }\n\n async markOpsSynced(lastId: number): Promise<void> {\n return this.queueOrExecute('markOpsSynced', [lastId], () => this.markOpsSyncedInternal(lastId));\n }\n\n private async markOpsSyncedInternal(lastId: number): Promise<void> {\n const tx = this.db?.transaction('op_log', 'readwrite');\n if (!tx) return;\n\n let cursor = await tx.store.openCursor();\n while (cursor) {\n if (cursor.value.id <= lastId) {\n const update = { ...cursor.value, synced: 1 };\n await cursor.update(update);\n }\n cursor = await cursor.continue();\n }\n await tx.done;\n }\n}\n\n","import { TopGunClient } from './TopGunClient';\nimport { IDBAdapter } from './adapters/IDBAdapter';\nimport type { IStorageAdapter } from './IStorageAdapter';\nimport { LWWMap } from '@topgunbuild/core';\nimport type { LWWRecord } from '@topgunbuild/core';\nimport { logger } from './utils/logger';\n\nexport interface TopGunConfig {\n sync: string;\n persist: 'indexeddb' | IStorageAdapter;\n nodeId?: string;\n}\n\n// Generic schema type\nexport type TopGunSchema = Record<string, any>;\n\nconst handler: ProxyHandler<TopGun<any>> = {\n get(target, prop, receiver) {\n if (prop in target || typeof prop === 'symbol') {\n return Reflect.get(target, prop, receiver);\n }\n if (typeof prop === 'string') {\n return target.collection(prop);\n }\n return undefined;\n }\n};\n\nexport class TopGun<T extends TopGunSchema = any> {\n private client: TopGunClient;\n private initPromise: Promise<void>;\n \n // Allow property access for collections based on Schema T\n [key: string]: any;\n\n constructor(config: TopGunConfig) {\n let storage: IStorageAdapter;\n\n if (config.persist === 'indexeddb') {\n storage = new IDBAdapter();\n } else if (typeof config.persist === 'object') {\n storage = config.persist;\n } else {\n throw new Error(`Unsupported persist option: ${config.persist}`);\n }\n\n this.client = new TopGunClient({\n serverUrl: config.sync,\n storage,\n nodeId: config.nodeId\n });\n\n // Start client initialization (non-blocking)\n // The IDBAdapter now initializes in the background and queues operations\n this.initPromise = this.client.start().catch(err => {\n logger.error({ err, context: 'client_start' }, 'Failed to start TopGun client');\n throw err;\n });\n\n return new Proxy(this, handler);\n }\n\n /**\n * Waits for the storage adapter to be fully initialized.\n * This is optional - you can start using the database immediately.\n * Operations are queued in memory and persisted once IndexedDB is ready.\n */\n public async waitForReady(): Promise<void> {\n await this.initPromise;\n }\n\n public collection<K extends keyof T & string>(name: K): CollectionWrapper<T[K]> {\n // Explicitly type the map\n const map = this.client.getMap<string, T[K]>(name);\n return new CollectionWrapper<T[K]>(map);\n }\n}\n\nexport class CollectionWrapper<ItemType = any> {\n private map: LWWMap<string, ItemType>;\n\n constructor(map: LWWMap<string, ItemType>) {\n this.map = map;\n }\n\n /**\n * Sets an item in the collection. \n * The item MUST have an 'id' or '_id' field.\n */\n async set(value: ItemType): Promise<ItemType> {\n const v = value as any;\n const key = v.id || v._id;\n if (!key) {\n throw new Error('Object must have an \"id\" or \"_id\" property to be saved in a collection.');\n }\n \n // LWWMap.set is synchronous in updating memory and queueing ops,\n // but we return a Promise to match typical async DB APIs.\n this.map.set(key, value);\n return Promise.resolve(value); \n }\n \n /**\n * Retrieves an item by ID.\n * Returns the value directly (unwrapped from CRDT record).\n */\n get(key: string): ItemType | undefined {\n return this.map.get(key);\n }\n\n /**\n * Get the raw LWWRecord (including metadata like timestamp).\n */\n getRecord(key: string): LWWRecord<ItemType> | undefined {\n return this.map.getRecord(key);\n }\n\n // Expose raw map if needed for advanced usage\n get raw() {\n return this.map;\n }\n}\n","import { serialize, deserialize } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport class EncryptionManager {\n private static ALGORITHM = 'AES-GCM';\n private static IV_LENGTH = 12;\n\n /**\n * Encrypts data using AES-GCM.\n * Serializes data to MessagePack before encryption.\n */\n static async encrypt(key: CryptoKey, data: any): Promise<{ iv: Uint8Array; data: Uint8Array }> {\n const encoded = serialize(data);\n\n // Generate IV\n const iv = window.crypto.getRandomValues(new Uint8Array(EncryptionManager.IV_LENGTH));\n\n // Encrypt\n const ciphertext = await window.crypto.subtle.encrypt(\n {\n name: EncryptionManager.ALGORITHM,\n iv: iv,\n },\n key,\n encoded as any\n );\n\n return {\n iv,\n data: new Uint8Array(ciphertext),\n };\n }\n\n /**\n * Decrypts AES-GCM encrypted data.\n * Deserializes from MessagePack after decryption.\n */\n static async decrypt(key: CryptoKey, record: { iv: Uint8Array; data: Uint8Array }): Promise<any> {\n try {\n const plaintextBuffer = await window.crypto.subtle.decrypt(\n {\n name: EncryptionManager.ALGORITHM,\n iv: record.iv as any,\n },\n key,\n record.data as any\n );\n\n return deserialize(new Uint8Array(plaintextBuffer));\n } catch (err) {\n logger.error({ err, context: 'decryption' }, 'Decryption failed');\n throw new Error('Failed to decrypt data: ' + err);\n }\n }\n}\n","import { IStorageAdapter, OpLogEntry } from '../IStorageAdapter';\nimport { EncryptionManager } from '../crypto/EncryptionManager';\n\n/**\n * Wraps an underlying storage adapter and encrypts data at rest using AES-GCM.\n */\nexport class EncryptedStorageAdapter implements IStorageAdapter {\n constructor(\n private wrapped: IStorageAdapter,\n private key: CryptoKey\n ) { }\n\n async initialize(dbName: string): Promise<void> {\n return this.wrapped.initialize(dbName);\n }\n\n async close(): Promise<void> {\n return this.wrapped.close();\n }\n\n // --- KV Operations ---\n\n async get<V>(key: string): Promise<V | any | undefined> {\n const raw = await this.wrapped.get<any>(key);\n\n if (!raw) {\n return undefined;\n }\n\n // Check if it looks like an encrypted record\n // We expect { iv: Uint8Array, data: Uint8Array }\n // Note: In a real app we might want a stricter check or a version tag.\n if (this.isEncryptedRecord(raw)) {\n try {\n return await EncryptionManager.decrypt(this.key, raw);\n } catch (e) {\n // Fallback for migration or corruption?\n // For now, fail loud as per spec.\n throw e;\n }\n }\n\n // Return raw if not encrypted (backwards compatibility during dev, or unencrypted data)\n return raw;\n }\n\n async put(key: string, value: any): Promise<void> {\n const encrypted = await EncryptionManager.encrypt(this.key, value);\n // Store as plain object to be compatible with structured clone algorithm of IndexedDB\n const storedValue = {\n iv: encrypted.iv,\n data: encrypted.data\n };\n return this.wrapped.put(key, storedValue);\n }\n\n async remove(key: string): Promise<void> {\n return this.wrapped.remove(key);\n }\n\n // --- Metadata ---\n\n async getMeta(key: string): Promise<any> {\n const raw = await this.wrapped.getMeta(key);\n if (!raw) return undefined;\n\n if (this.isEncryptedRecord(raw)) {\n return EncryptionManager.decrypt(this.key, raw);\n }\n return raw;\n }\n\n async setMeta(key: string, value: any): Promise<void> {\n const encrypted = await EncryptionManager.encrypt(this.key, value);\n return this.wrapped.setMeta(key, {\n iv: encrypted.iv,\n data: encrypted.data\n });\n }\n\n // --- Batch ---\n\n async batchPut(entries: Map<string, any>): Promise<void> {\n const encryptedEntries = new Map<string, any>();\n\n for (const [key, value] of entries.entries()) {\n const encrypted = await EncryptionManager.encrypt(this.key, value);\n encryptedEntries.set(key, {\n iv: encrypted.iv,\n data: encrypted.data\n });\n }\n\n return this.wrapped.batchPut(encryptedEntries);\n }\n\n // --- OpLog ---\n\n async appendOpLog(entry: Omit<OpLogEntry, 'id'>): Promise<number> {\n // Encrypt sensitive fields: value, record, orRecord\n const encryptedEntry = { ...entry };\n\n if (entry.value !== undefined) {\n const enc = await EncryptionManager.encrypt(this.key, entry.value);\n encryptedEntry.value = { iv: enc.iv, data: enc.data };\n }\n\n if (entry.record !== undefined) {\n const enc = await EncryptionManager.encrypt(this.key, entry.record);\n encryptedEntry.record = { iv: enc.iv, data: enc.data } as any;\n }\n\n if (entry.orRecord !== undefined) {\n const enc = await EncryptionManager.encrypt(this.key, entry.orRecord);\n encryptedEntry.orRecord = { iv: enc.iv, data: enc.data } as any;\n }\n\n // Note: 'key', 'op', 'mapName', 'orTag', 'hlc', 'synced' remain plaintext for indexing\n\n return this.wrapped.appendOpLog(encryptedEntry);\n }\n\n async getPendingOps(): Promise<OpLogEntry[]> {\n const ops = await this.wrapped.getPendingOps();\n\n // Decrypt in place\n // We map concurrently for performance\n return Promise.all(ops.map(async op => {\n const decryptedOp = { ...op };\n\n if (this.isEncryptedRecord(op.value)) {\n decryptedOp.value = await EncryptionManager.decrypt(this.key, op.value);\n }\n\n if (this.isEncryptedRecord(op.record)) {\n decryptedOp.record = await EncryptionManager.decrypt(this.key, op.record as any);\n }\n\n if (this.isEncryptedRecord(op.orRecord)) {\n decryptedOp.orRecord = await EncryptionManager.decrypt(this.key, op.orRecord as any);\n }\n\n return decryptedOp;\n }));\n }\n\n async markOpsSynced(lastId: number): Promise<void> {\n return this.wrapped.markOpsSynced(lastId);\n }\n\n // --- Iteration ---\n\n async getAllKeys(): Promise<string[]> {\n return this.wrapped.getAllKeys();\n }\n\n // --- Helpers ---\n\n private isEncryptedRecord(data: any): data is { iv: Uint8Array, data: Uint8Array } {\n return data &&\n typeof data === 'object' &&\n data.iv instanceof Uint8Array &&\n data.data instanceof Uint8Array;\n }\n}\n","import { SyncEngine } from './SyncEngine';\nimport { TopGunClient, DEFAULT_CLUSTER_CONFIG } from './TopGunClient';\nimport { TopGun } from './TopGun';\nexport * from './adapters/IDBAdapter';\nexport * from './adapters/EncryptedStorageAdapter';\nimport { QueryHandle } from './QueryHandle';\nimport { ChangeTracker } from './ChangeTracker';\nimport { LWWMap, Predicates } from '@topgunbuild/core';\nimport { TopicHandle } from './TopicHandle';\nimport { SyncState, VALID_TRANSITIONS, isValidTransition } from './SyncState';\nimport { SyncStateMachine } from './SyncStateMachine';\nimport { BackpressureError } from './errors/BackpressureError';\nimport { DEFAULT_BACKPRESSURE_CONFIG } from './BackpressureConfig';\n\n// Cluster imports\nimport { ConnectionPool, PartitionRouter, ClusterClient } from './cluster';\n\n// Connection provider imports\nimport { SingleServerProvider } from './connection';\n\n// Type imports\nimport type { IStorageAdapter, OpLogEntry } from './IStorageAdapter';\nimport type { LWWRecord, PredicateNode } from '@topgunbuild/core';\nimport type { QueryFilter, QueryResultItem, QueryResultSource, CursorStatus, PaginationInfo } from './QueryHandle';\nimport type { TopicCallback } from './TopicHandle';\nimport type { BackoffConfig, HeartbeatConfig, SyncEngineConfig } from './SyncEngine';\nimport type { StateChangeEvent, StateChangeListener, SyncStateMachineConfig } from './SyncStateMachine';\nimport type {\n BackpressureConfig,\n BackpressureStrategy,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n} from './BackpressureConfig';\n\n// Value exports\nexport { SyncEngine, TopGunClient, TopGun, QueryHandle, LWWMap, Predicates, TopicHandle };\nexport { SyncState, VALID_TRANSITIONS, isValidTransition, SyncStateMachine };\nexport { BackpressureError, DEFAULT_BACKPRESSURE_CONFIG, DEFAULT_CLUSTER_CONFIG };\nexport { logger } from './utils/logger';\n\n// Change tracking exports\nexport { ChangeTracker };\nexport type { ChangeEvent } from './ChangeTracker';\n\n// PN Counter exports\nexport { PNCounterHandle } from './PNCounterHandle';\n\n// Event Journal exports\nexport { EventJournalReader } from './EventJournalReader';\nexport type { JournalEventData, JournalSubscribeOptions } from './EventJournalReader';\n\n// Conflict Resolver exports\nexport { ConflictResolverClient } from './ConflictResolverClient';\nexport type { ResolverInfo, RegisterResult } from './ConflictResolverClient';\n\n// Full-Text Search exports\nexport type { SearchResult } from './SyncEngine';\n\n// Live Search exports\nexport { SearchHandle } from './SearchHandle';\nexport type { SearchResultsCallback } from './SearchHandle';\n\n// Hybrid Query exports\nexport { HybridQueryHandle } from './HybridQueryHandle';\nexport type { HybridQueryFilter, HybridResultItem, HybridResultSource } from './HybridQueryHandle';\n\n// Cluster exports\nexport { ConnectionPool, PartitionRouter, ClusterClient };\nexport type {\n ConnectionPoolEvents,\n RoutingResult,\n PartitionRouterEvents,\n ClusterClientEvents,\n ClusterRoutingMode,\n RoutingMetrics,\n CircuitState,\n} from './cluster';\n\n// Connection provider exports\nexport { SingleServerProvider };\nexport type {\n IConnectionProvider,\n ConnectionProviderEvent,\n ConnectionEventHandler,\n SingleServerProviderConfig,\n} from './types';\n\n// TopGunClient cluster config types\nexport type { TopGunClusterConfig, TopGunClientConfig } from './TopGunClient';\n\n// Type exports\nexport type {\n IStorageAdapter,\n OpLogEntry,\n LWWRecord,\n PredicateNode,\n QueryFilter,\n QueryResultItem,\n QueryResultSource,\n // Pagination types\n CursorStatus,\n PaginationInfo,\n TopicCallback,\n BackoffConfig,\n HeartbeatConfig,\n SyncEngineConfig,\n StateChangeEvent,\n StateChangeListener,\n SyncStateMachineConfig,\n BackpressureConfig,\n BackpressureStrategy,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n};\n"],"mappings":"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","names":["LWWMap","ORMap","deserialize","SyncState","handler","handler","LWWMap","ORMap","token","deserialize","handler","LWWMap","ORMap","DEFAULT_CONNECTION_POOL_CONFIG","DEFAULT_PARTITION_ROUTER_CONFIG","serialize","serialize","deserialize","serialize","deserialize","DEFAULT_CONNECTION_POOL_CONFIG","DEFAULT_PARTITION_ROUTER_CONFIG","serialize","handler","handler","LWWMap","ORMap","serialize","deserialize","serialize","deserialize","LWWMap"]}
|
|
1
|
+
{"version":3,"sources":["../src/SyncEngine.ts","../src/utils/logger.ts","../src/SyncState.ts","../src/SyncStateMachine.ts","../src/BackpressureConfig.ts","../src/ConflictResolverClient.ts","../src/sync/WebSocketManager.ts","../src/errors/BackpressureError.ts","../src/sync/BackpressureController.ts","../src/sync/QueryManager.ts","../src/sync/TopicManager.ts","../src/sync/LockManager.ts","../src/sync/WriteConcernManager.ts","../src/sync/CounterManager.ts","../src/sync/EntryProcessorClient.ts","../src/sync/SearchClient.ts","../src/sync/MerkleSyncHandler.ts","../src/sync/ORMapSyncHandler.ts","../src/sync/MessageRouter.ts","../src/sync/ClientMessageHandlers.ts","../src/TopGunClient.ts","../src/utils/deepEqual.ts","../src/ChangeTracker.ts","../src/QueryHandle.ts","../src/DistributedLock.ts","../src/TopicHandle.ts","../src/PNCounterHandle.ts","../src/EventJournalReader.ts","../src/SearchHandle.ts","../src/HybridQueryHandle.ts","../src/cluster/ClusterClient.ts","../src/cluster/ConnectionPool.ts","../src/connection/WebSocketConnection.ts","../src/cluster/PartitionRouter.ts","../src/connection/SingleServerProvider.ts","../src/adapters/IDBAdapter.ts","../src/TopGun.ts","../src/crypto/EncryptionManager.ts","../src/adapters/EncryptedStorageAdapter.ts","../src/index.ts","../src/connection/HttpSyncProvider.ts","../src/connection/AutoConnectionProvider.ts"],"sourcesContent":["import { HLC, LWWMap, ORMap, deserialize, evaluatePredicate } from '@topgunbuild/core';\nimport type { EntryProcessorDef, EntryProcessorResult, SearchOptions } from '@topgunbuild/core';\nimport type { LWWRecord, ORMapRecord, Timestamp } from '@topgunbuild/core';\nimport type {\n AuthFailMessage,\n OpAckMessage,\n OpRejectedMessage,\n ErrorMessage,\n QueryRespMessage,\n QueryUpdateMessage,\n ServerEventMessage,\n ServerBatchEventMessage,\n GcPruneMessage,\n BatchMessage,\n} from '@topgunbuild/core';\nimport type { IStorageAdapter } from './IStorageAdapter';\nimport { QueryHandle } from './QueryHandle';\nimport type { QueryFilter } from './QueryHandle';\nimport type { HybridQueryHandle, HybridQueryFilter } from './HybridQueryHandle';\nimport { TopicHandle } from './TopicHandle';\nimport { logger } from './utils/logger';\nimport { SyncStateMachine, StateChangeEvent } from './SyncStateMachine';\nimport { SyncState } from './SyncState';\nimport type {\n BackpressureConfig,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n} from './BackpressureConfig';\nimport { DEFAULT_BACKPRESSURE_CONFIG } from './BackpressureConfig';\nimport type { IConnectionProvider } from './types';\nimport { ConflictResolverClient } from './ConflictResolverClient';\nimport { WebSocketManager, BackpressureController, QueryManager, TopicManager, LockManager, WriteConcernManager, CounterManager, EntryProcessorClient, SearchClient, MerkleSyncHandler, ORMapSyncHandler, MessageRouter, registerClientMessageHandlers } from './sync';\nimport type { SearchResult, IMessageRouter } from './sync';\n\n// Re-export SearchResult from sync module for backwards compatibility\nexport type { SearchResult } from './sync';\n\nexport interface OpLogEntry {\n id: string; // Unique ID for the operation\n mapName: string;\n opType: 'PUT' | 'REMOVE' | 'OR_ADD' | 'OR_REMOVE';\n key: string;\n record?: LWWRecord<any>; // LWW Put/Remove (Remove has null value)\n orRecord?: ORMapRecord<any>; // ORMap Add\n orTag?: string; // ORMap Remove (Tombstone tag)\n timestamp: Timestamp; // HLC timestamp of the operation\n synced: boolean; // True if this operation has been successfully pushed to the server\n}\n\nexport interface HeartbeatConfig {\n intervalMs: number; // Default: 5000 (5 seconds)\n timeoutMs: number; // Default: 15000 (15 seconds)\n enabled: boolean; // Default: true\n}\n\nexport interface BackoffConfig {\n /** Initial delay in milliseconds (default: 1000) */\n initialDelayMs: number;\n /** Maximum delay in milliseconds (default: 30000) */\n maxDelayMs: number;\n /** Multiplier for exponential backoff (default: 2) */\n multiplier: number;\n /** Whether to add random jitter to delay (default: true) */\n jitter: boolean;\n /** Maximum number of retry attempts before entering ERROR state (default: 10) */\n maxRetries: number;\n}\n\nexport interface TopicQueueConfig {\n /** Maximum queued topic messages when offline (default: 100) */\n maxSize: number;\n /** Strategy when queue is full: 'drop-oldest' | 'drop-newest' (default: 'drop-oldest') */\n strategy: 'drop-oldest' | 'drop-newest';\n}\n\nconst DEFAULT_TOPIC_QUEUE_CONFIG: TopicQueueConfig = {\n maxSize: 100,\n strategy: 'drop-oldest',\n};\n\nexport interface SyncEngineConfig {\n nodeId: string;\n /** Connection provider for WebSocket connections */\n connectionProvider: IConnectionProvider;\n storageAdapter: IStorageAdapter;\n reconnectInterval?: number;\n heartbeat?: Partial<HeartbeatConfig>;\n backoff?: Partial<BackoffConfig>;\n backpressure?: Partial<BackpressureConfig>;\n /** Configuration for offline topic message queue */\n topicQueue?: Partial<TopicQueueConfig>;\n}\n\nconst DEFAULT_BACKOFF_CONFIG: BackoffConfig = {\n initialDelayMs: 1000,\n maxDelayMs: 30000,\n multiplier: 2,\n jitter: true,\n maxRetries: 10,\n};\n\nexport class SyncEngine {\n private readonly nodeId: string;\n private readonly storageAdapter: IStorageAdapter;\n private readonly hlc: HLC;\n private readonly stateMachine: SyncStateMachine;\n private readonly heartbeatConfig: HeartbeatConfig;\n private readonly backoffConfig: BackoffConfig;\n\n // WebSocketManager handles all connection/websocket operations\n private readonly webSocketManager: WebSocketManager;\n\n // QueryManager handles all query operations\n private readonly queryManager: QueryManager;\n\n // TopicManager handles all topic (pub/sub) operations\n private readonly topicManager: TopicManager;\n\n // LockManager handles distributed lock operations\n private readonly lockManager: LockManager;\n\n // WriteConcernManager handles write concern tracking\n private readonly writeConcernManager: WriteConcernManager;\n\n // CounterManager handles PN counter operations\n private readonly counterManager: CounterManager;\n\n // EntryProcessorClient handles entry processor operations\n private readonly entryProcessorClient: EntryProcessorClient;\n\n // SearchClient handles full-text search operations\n private readonly searchClient: SearchClient;\n\n // MerkleSyncHandler handles LWWMap sync protocol messages\n private readonly merkleSyncHandler: MerkleSyncHandler;\n\n // ORMapSyncHandler handles ORMap sync protocol messages\n private readonly orMapSyncHandler: ORMapSyncHandler;\n\n // MessageRouter handles type-based message routing\n private readonly messageRouter: IMessageRouter;\n\n private opLog: OpLogEntry[] = [];\n private maps: Map<string, LWWMap<any, any> | ORMap<any, any>> = new Map();\n private lastSyncTimestamp: number = 0;\n private authToken: string | null = null;\n private tokenProvider: (() => Promise<string | null>) | null = null;\n\n // BackpressureController handles all backpressure operations\n private readonly backpressureConfig: BackpressureConfig;\n private readonly backpressureController: BackpressureController;\n\n // Conflict Resolver client\n private readonly conflictResolverClient: ConflictResolverClient;\n\n constructor(config: SyncEngineConfig) {\n // Validate config: connectionProvider is required\n if (!config.connectionProvider) {\n throw new Error('SyncEngine requires connectionProvider');\n }\n\n this.nodeId = config.nodeId;\n this.storageAdapter = config.storageAdapter;\n this.hlc = new HLC(this.nodeId);\n\n // Initialize state machine\n this.stateMachine = new SyncStateMachine();\n\n // Initialize heartbeat config with defaults\n this.heartbeatConfig = {\n intervalMs: config.heartbeat?.intervalMs ?? 5000,\n timeoutMs: config.heartbeat?.timeoutMs ?? 15000,\n enabled: config.heartbeat?.enabled ?? true,\n };\n\n // Merge backoff config with defaults\n this.backoffConfig = {\n ...DEFAULT_BACKOFF_CONFIG,\n ...config.backoff,\n };\n\n // Merge backpressure config with defaults\n this.backpressureConfig = {\n ...DEFAULT_BACKPRESSURE_CONFIG,\n ...config.backpressure,\n };\n\n // Initialize BackpressureController with shared opLog reference\n this.backpressureController = new BackpressureController({\n config: this.backpressureConfig,\n opLog: this.opLog, // Pass reference, not copy\n });\n\n // Merge topic queue config with defaults to ensure consistent backpressure behavior\n const topicQueueConfig: TopicQueueConfig = {\n ...DEFAULT_TOPIC_QUEUE_CONFIG,\n ...config.topicQueue,\n };\n\n // Initialize WebSocketManager with callbacks to SyncEngine\n this.webSocketManager = new WebSocketManager({\n connectionProvider: config.connectionProvider,\n stateMachine: this.stateMachine,\n backoffConfig: this.backoffConfig,\n heartbeatConfig: this.heartbeatConfig,\n onMessage: (msg) => this.handleServerMessage(msg),\n onConnected: () => this.handleConnectionEstablished(),\n onDisconnected: () => this.handleConnectionLost(),\n onReconnected: () => this.handleReconnection(),\n });\n\n // Initialize QueryManager with callbacks\n this.queryManager = new QueryManager({\n storageAdapter: this.storageAdapter,\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize TopicManager with callbacks\n this.topicManager = new TopicManager({\n topicQueueConfig,\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize LockManager with callbacks\n this.lockManager = new LockManager({\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n isAuthenticated: () => this.isAuthenticated(),\n isOnline: () => this.isOnline(),\n });\n\n // Initialize WriteConcernManager for distributed PN counter operations\n this.writeConcernManager = new WriteConcernManager({\n defaultTimeout: 5000,\n });\n\n // Initialize CounterManager for distributed PN counter operations\n this.counterManager = new CounterManager({\n sendMessage: (msg) => this.sendMessage(msg),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize EntryProcessorClient for server-side entry processing\n this.entryProcessorClient = new EntryProcessorClient({\n sendMessage: (msg, key) => key !== undefined ? this.sendMessage(msg, key) : this.sendMessage(msg),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize SearchClient for full-text search operations\n this.searchClient = new SearchClient({\n sendMessage: (msg) => this.sendMessage(msg),\n isAuthenticated: () => this.isAuthenticated(),\n });\n\n // Initialize MerkleSyncHandler for LWWMap sync protocol\n this.merkleSyncHandler = new MerkleSyncHandler({\n getMap: (name) => this.maps.get(name),\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n storageAdapter: this.storageAdapter,\n hlc: this.hlc,\n onTimestampUpdate: async (ts) => {\n this.hlc.update(ts);\n this.lastSyncTimestamp = ts.millis;\n await this.saveOpLog();\n },\n resetMap: (name) => this.resetMap(name),\n });\n\n // Initialize ORMapSyncHandler for ORMap sync protocol\n this.orMapSyncHandler = new ORMapSyncHandler({\n getMap: (name) => this.maps.get(name),\n sendMessage: (msg, key) => this.webSocketManager.sendMessage(msg, key),\n hlc: this.hlc,\n onTimestampUpdate: async (ts) => {\n this.hlc.update(ts);\n this.lastSyncTimestamp = ts.millis;\n await this.saveOpLog();\n },\n });\n\n // Initialize Conflict Resolver client\n this.conflictResolverClient = new ConflictResolverClient(this);\n\n // Initialize MessageRouter and register all handlers\n this.messageRouter = new MessageRouter({\n onUnhandled: (msg) => logger.warn({ type: msg?.type }, 'Unhandled message type'),\n });\n registerClientMessageHandlers(\n this.messageRouter,\n {\n sendAuth: () => this.sendAuth(),\n handleAuthAck: () => this.handleAuthAck(),\n handleAuthFail: (msg) => this.handleAuthFail(msg),\n handleOpAck: (msg) => this.handleOpAck(msg),\n handleQueryResp: (msg) => this.handleQueryResp(msg),\n handleQueryUpdate: (msg) => this.handleQueryUpdate(msg),\n handleServerEvent: (msg) => this.handleServerEvent(msg),\n handleServerBatchEvent: (msg) => this.handleServerBatchEvent(msg),\n handleGcPrune: (msg) => this.handleGcPrune(msg),\n handleOpRejected: (msg) => this.handleOpRejected(msg),\n handleError: (msg) => this.handleError(msg),\n },\n {\n topicManager: this.topicManager,\n lockManager: this.lockManager,\n counterManager: this.counterManager,\n entryProcessorClient: this.entryProcessorClient,\n conflictResolverClient: this.conflictResolverClient,\n searchClient: this.searchClient,\n merkleSyncHandler: this.merkleSyncHandler,\n orMapSyncHandler: this.orMapSyncHandler,\n }\n );\n\n // Start connection\n this.webSocketManager.connect();\n\n this.loadOpLog();\n }\n\n // ============================================\n // Connection Callbacks (from WebSocketManager)\n // ============================================\n\n /**\n * Called when connection is established (initial or reconnect).\n */\n private handleConnectionEstablished(): void {\n if (this.authToken || this.tokenProvider) {\n logger.info('Connection established. Sending auth...');\n this.stateMachine.transition(SyncState.AUTHENTICATING);\n this.sendAuth();\n } else {\n logger.info('Connection established. Waiting for auth token...');\n this.stateMachine.transition(SyncState.AUTHENTICATING);\n }\n }\n\n /**\n * Called when connection is lost.\n */\n private handleConnectionLost(): void {\n // WebSocketManager already stopped heartbeat and transitioned state\n // SyncEngine can do additional cleanup if needed\n }\n\n /**\n * Called when reconnection succeeds.\n */\n private handleReconnection(): void {\n if (this.authToken || this.tokenProvider) {\n this.stateMachine.transition(SyncState.AUTHENTICATING);\n this.sendAuth();\n }\n }\n\n // ============================================\n // State Machine Public API\n // ============================================\n\n /**\n * Get the current connection state\n */\n getConnectionState(): SyncState {\n return this.stateMachine.getState();\n }\n\n /**\n * Subscribe to connection state changes\n * @returns Unsubscribe function\n */\n onConnectionStateChange(listener: (event: StateChangeEvent) => void): () => void {\n return this.stateMachine.onStateChange(listener);\n }\n\n /**\n * Get state machine history for debugging\n */\n getStateHistory(limit?: number): StateChangeEvent[] {\n return this.stateMachine.getHistory(limit);\n }\n\n // ============================================\n // Internal State Helpers (replace boolean flags)\n // ============================================\n\n /**\n * Check if WebSocket is connected (but may not be authenticated yet)\n */\n private isOnline(): boolean {\n const state = this.stateMachine.getState();\n return (\n state === SyncState.CONNECTING ||\n state === SyncState.AUTHENTICATING ||\n state === SyncState.SYNCING ||\n state === SyncState.CONNECTED\n );\n }\n\n /**\n * Check if fully authenticated and ready for operations\n */\n private isAuthenticated(): boolean {\n const state = this.stateMachine.getState();\n return state === SyncState.SYNCING || state === SyncState.CONNECTED;\n }\n\n /**\n * Check if fully connected and synced\n */\n private isConnected(): boolean {\n return this.stateMachine.getState() === SyncState.CONNECTED;\n }\n\n // ============================================\n // Message Sending (delegates to WebSocketManager)\n // ============================================\n\n /**\n * Send a message through the current connection.\n * Delegates to WebSocketManager.\n */\n private sendMessage(message: unknown, key?: string): boolean {\n return this.webSocketManager.sendMessage(message, key);\n }\n\n // ============================================\n // Op Log Management\n // ============================================\n\n private async loadOpLog(): Promise<void> {\n const storedTimestamp = await this.storageAdapter.getMeta('lastSyncTimestamp');\n if (storedTimestamp) {\n this.lastSyncTimestamp = storedTimestamp;\n }\n\n const pendingOps = await this.storageAdapter.getPendingOps();\n // Clear and push to existing array (preserves BackpressureController reference)\n this.opLog.length = 0;\n for (const op of pendingOps) {\n this.opLog.push({\n ...op,\n id: String(op.id),\n synced: false,\n } as unknown as OpLogEntry);\n }\n\n if (this.opLog.length > 0) {\n logger.info({ count: this.opLog.length }, 'Loaded pending operations from local storage');\n }\n }\n\n private async saveOpLog(): Promise<void> {\n await this.storageAdapter.setMeta('lastSyncTimestamp', this.lastSyncTimestamp);\n }\n\n public registerMap(mapName: string, map: LWWMap<any, any> | ORMap<any, any>): void {\n this.maps.set(mapName, map);\n }\n\n public async recordOperation(\n mapName: string,\n opType: 'PUT' | 'REMOVE' | 'OR_ADD' | 'OR_REMOVE',\n key: string,\n data: { record?: LWWRecord<any>; orRecord?: ORMapRecord<any>; orTag?: string; timestamp: Timestamp }\n ): Promise<string> {\n // Check backpressure before adding new operation (delegates to BackpressureController)\n await this.backpressureController.checkBackpressure();\n\n const opLogEntry: Omit<OpLogEntry, 'id'> & { id?: string } = {\n mapName,\n opType,\n key,\n record: data.record,\n orRecord: data.orRecord,\n orTag: data.orTag,\n timestamp: data.timestamp,\n synced: false,\n };\n\n const id = await this.storageAdapter.appendOpLog(opLogEntry as any);\n opLogEntry.id = String(id);\n\n this.opLog.push(opLogEntry as OpLogEntry);\n\n // Check high water mark after adding operation (delegates to BackpressureController)\n this.backpressureController.checkHighWaterMark();\n\n if (this.isAuthenticated()) {\n this.syncPendingOperations();\n }\n\n return opLogEntry.id;\n }\n\n private syncPendingOperations(): void {\n const pending = this.opLog.filter(op => !op.synced);\n if (pending.length === 0) return;\n\n logger.info({ count: pending.length }, 'Syncing pending operations');\n\n // Delegate to connection provider's sendBatch when available (cluster mode).\n // This allows ClusterClient to group operations by target partition owner\n // and send separate OP_BATCH messages per node.\n const connectionProvider = this.webSocketManager.getConnectionProvider();\n if (connectionProvider.sendBatch) {\n const results = connectionProvider.sendBatch(pending.map(op => ({ key: op.key, message: op })));\n const failedKeys = [...results.entries()].filter(([, success]) => !success).map(([key]) => key);\n if (failedKeys.length > 0) {\n logger.warn({ failedKeys, count: failedKeys.length }, 'Some batch operations failed to send');\n }\n return;\n }\n\n // Fallback: send all ops in a single OP_BATCH (single-server mode)\n this.sendMessage({\n type: 'OP_BATCH',\n payload: {\n ops: pending\n }\n });\n }\n\n private startMerkleSync(): void {\n for (const [mapName, map] of this.maps) {\n if (map instanceof LWWMap) {\n this.merkleSyncHandler.sendSyncInit(mapName, this.lastSyncTimestamp);\n } else if (map instanceof ORMap) {\n this.orMapSyncHandler.sendSyncInit(mapName, this.lastSyncTimestamp);\n }\n }\n }\n\n public setAuthToken(token: string): void {\n this.authToken = token;\n this.tokenProvider = null;\n\n const state = this.stateMachine.getState();\n if (state === SyncState.AUTHENTICATING) {\n // Already connected and waiting for token — send it now\n this.sendAuth();\n } else if (state === SyncState.BACKOFF || state === SyncState.DISCONNECTED) {\n // Force immediate reconnect if we were waiting for retry timer\n logger.info('Auth token set during backoff/disconnect. Reconnecting immediately.');\n this.webSocketManager.clearReconnectTimer();\n // Reset backoff since user provided new credentials\n this.webSocketManager.resetBackoff();\n this.webSocketManager.connect();\n }\n }\n\n public setTokenProvider(provider: () => Promise<string | null>): void {\n this.tokenProvider = provider;\n const state = this.stateMachine.getState();\n if (state === SyncState.AUTHENTICATING) {\n this.sendAuth();\n }\n }\n\n private async sendAuth(): Promise<void> {\n if (this.tokenProvider) {\n try {\n const token = await this.tokenProvider();\n if (token) {\n this.authToken = token;\n }\n } catch (err) {\n logger.error({ err }, 'Failed to get token from provider');\n return;\n }\n }\n\n const token = this.authToken;\n if (!token) return; // Don't send anonymous auth anymore\n\n this.sendMessage({\n type: 'AUTH',\n token\n });\n }\n\n /**\n * Subscribe to a standard query.\n * Delegates to QueryManager.\n */\n public subscribeToQuery(query: QueryHandle<any>): void {\n this.queryManager.subscribeToQuery(query);\n }\n\n /**\n * Subscribe to a topic.\n * Delegates to TopicManager.\n */\n public subscribeToTopic(topic: string, handle: TopicHandle): void {\n this.topicManager.subscribeToTopic(topic, handle);\n }\n\n /**\n * Unsubscribe from a topic.\n * Delegates to TopicManager.\n */\n public unsubscribeFromTopic(topic: string): void {\n this.topicManager.unsubscribeFromTopic(topic);\n }\n\n /**\n * Publish a message to a topic.\n * Delegates to TopicManager.\n */\n public publishTopic(topic: string, data: unknown): void {\n this.topicManager.publishTopic(topic, data);\n }\n\n /**\n * Get topic queue status.\n * Delegates to TopicManager.\n */\n public getTopicQueueStatus(): { size: number; maxSize: number } {\n return this.topicManager.getTopicQueueStatus();\n }\n\n /**\n * Executes a query against local storage immediately.\n * Delegates to QueryManager.\n */\n public async runLocalQuery(mapName: string, filter: QueryFilter): Promise<{ key: string; value: any }[]> {\n return this.queryManager.runLocalQuery(mapName, filter);\n }\n\n /**\n * Unsubscribe from a query.\n * Delegates to QueryManager.\n */\n public unsubscribeFromQuery(queryId: string): void {\n this.queryManager.unsubscribeFromQuery(queryId);\n }\n\n /**\n * Request a distributed lock.\n * Delegates to LockManager.\n */\n public requestLock(name: string, requestId: string, ttl: number): Promise<{ fencingToken: number }> {\n return this.lockManager.requestLock(name, requestId, ttl);\n }\n\n /**\n * Release a distributed lock.\n * Delegates to LockManager.\n */\n public releaseLock(name: string, requestId: string, fencingToken: number): Promise<boolean> {\n return this.lockManager.releaseLock(name, requestId, fencingToken);\n }\n\n private async handleServerMessage(message: { type: string; payload?: unknown; timestamp?: Timestamp }): Promise<void> {\n // Emit to generic listeners (used by EventJournalReader)\n this.emitMessage(message);\n\n // Handle BATCH specially (recursive unbatch)\n if (message.type === 'BATCH') {\n await this.handleBatch(message as BatchMessage);\n return;\n }\n\n // Route to registered handler\n await this.messageRouter.route(message);\n\n // Update HLC if message has an HLC Timestamp struct (millis + counter + nodeId).\n // Some messages (e.g. PONG) have a raw numeric `timestamp` field — passing that\n // to HLC.update() would poison the clock with NaN via Number(undefined).\n const ts = message.timestamp;\n if (ts && typeof ts === 'object' && 'millis' in ts && 'counter' in ts && 'nodeId' in ts) {\n this.hlc.update(ts);\n this.lastSyncTimestamp = Number(ts.millis);\n await this.saveOpLog();\n }\n }\n\n // ============================================\n // Message Handler Helpers (extracted from switch)\n // ============================================\n\n private async handleBatch(message: BatchMessage): Promise<void> {\n // Unbatch and process each message\n // Format: [4 bytes: count][4 bytes: len1][msg1][4 bytes: len2][msg2]...\n const batchData = message.data;\n const view = new DataView(batchData.buffer, batchData.byteOffset, batchData.byteLength);\n let offset = 0;\n\n const count = view.getUint32(offset, true);\n offset += 4;\n\n for (let i = 0; i < count; i++) {\n const msgLen = view.getUint32(offset, true);\n offset += 4;\n\n const msgData = batchData.slice(offset, offset + msgLen);\n offset += msgLen;\n\n const innerMsg = deserialize(msgData) as { type: string; payload?: unknown; timestamp?: Timestamp };\n await this.handleServerMessage(innerMsg);\n }\n }\n\n private handleAuthAck(): void {\n logger.info('Authenticated successfully');\n const wasAuthenticated = this.isAuthenticated();\n\n // Transition to SYNCING state\n this.stateMachine.transition(SyncState.SYNCING);\n\n // Reset backoff on successful auth\n this.webSocketManager.resetBackoff();\n\n this.syncPendingOperations();\n\n // Flush any queued topic messages from offline period (delegates to TopicManager)\n this.topicManager.flushTopicQueue();\n\n // Only re-subscribe on first authentication to prevent UI flickering\n if (!wasAuthenticated) {\n this.webSocketManager.startHeartbeat();\n this.startMerkleSync();\n // Re-subscribe all queries via QueryManager\n this.queryManager.resubscribeAll();\n // Re-subscribe topics via TopicManager\n this.topicManager.resubscribeAll();\n }\n\n // After initial sync setup, transition to CONNECTED\n // In a real implementation, you might wait for SYNC_COMPLETE message\n this.stateMachine.transition(SyncState.CONNECTED);\n }\n\n private handleAuthFail(message: AuthFailMessage): void {\n logger.error({ error: message.error }, 'Authentication failed');\n this.authToken = null; // Clear invalid token\n // Stay in AUTHENTICATING or go to ERROR depending on severity\n // For now, let the connection close naturally or retry with new token\n }\n\n private handleOpAck(message: OpAckMessage): void {\n const { lastId, achievedLevel, results } = message.payload;\n logger.info({ lastId, achievedLevel, hasResults: !!results }, 'Received ACK for ops');\n\n // Handle per-operation results if available\n if (results && Array.isArray(results)) {\n for (const result of results) {\n const op = this.opLog.find(o => o.id === result.opId);\n if (op && !op.synced) {\n op.synced = true;\n logger.debug({ opId: result.opId, achievedLevel: result.achievedLevel, success: result.success }, 'Op ACK with Write Concern');\n }\n // Resolve pending Write Concern promise if exists (delegates to WriteConcernManager)\n this.writeConcernManager.resolveWriteConcernPromise(result.opId, result);\n }\n }\n\n // Mark all ops up to lastId as synced (numeric comparison — IDs are stringified integers)\n const lastIdNum = parseInt(lastId, 10);\n let maxSyncedId = -1;\n let ackedCount = 0;\n\n if (!isNaN(lastIdNum)) {\n // Normal path: server returned a valid numeric lastId\n this.opLog.forEach(op => {\n if (op.id) {\n const opIdNum = parseInt(op.id, 10);\n if (!isNaN(opIdNum) && opIdNum <= lastIdNum) {\n if (!op.synced) {\n ackedCount++;\n }\n op.synced = true;\n if (opIdNum > maxSyncedId) {\n maxSyncedId = opIdNum;\n }\n }\n }\n });\n } else {\n // Fallback: server returned non-numeric lastId (e.g. \"unknown\", \"undefined\").\n // The server ACKed the batch, so mark ALL pending ops as synced.\n logger.warn({ lastId }, 'OP_ACK has non-numeric lastId — marking all pending ops as synced');\n this.opLog.forEach(op => {\n if (!op.synced) {\n ackedCount++;\n op.synced = true;\n const opIdNum = parseInt(op.id, 10);\n if (!isNaN(opIdNum) && opIdNum > maxSyncedId) {\n maxSyncedId = opIdNum;\n }\n }\n });\n }\n\n if (maxSyncedId !== -1) {\n this.storageAdapter.markOpsSynced(maxSyncedId).catch(err => logger.error({ err }, 'Failed to mark ops synced'));\n }\n // Check low water mark after ACKs reduce pending count (delegates to BackpressureController)\n if (ackedCount > 0) {\n this.backpressureController.checkLowWaterMark();\n }\n }\n\n private handleQueryResp(message: QueryRespMessage): void {\n const { queryId, results, nextCursor, hasMore, cursorStatus } = message.payload;\n const query = this.queryManager.getQueries().get(queryId);\n if (query) {\n query.onResult(results, 'server');\n query.updatePaginationInfo({ nextCursor, hasMore, cursorStatus });\n }\n }\n\n private handleQueryUpdate(message: QueryUpdateMessage): void {\n const { queryId, key, value, changeType } = message.payload;\n const query = this.queryManager.getQueries().get(queryId);\n if (query) {\n query.onUpdate(key, changeType === 'LEAVE' ? null : value);\n }\n }\n\n private async handleServerEvent(message: ServerEventMessage): Promise<void> {\n // Modified to support ORMap\n const { mapName, eventType, key, record, orRecord, orTag } = message.payload;\n await this.applyServerEvent(mapName, eventType, key, record, orRecord, orTag);\n }\n\n private async handleServerBatchEvent(message: ServerBatchEventMessage): Promise<void> {\n // === OPTIMIZATION: Batch event processing ===\n // Server sends multiple events in a single message for efficiency\n const { events } = message.payload;\n for (const event of events) {\n await this.applyServerEvent(\n event.mapName,\n event.eventType,\n event.key,\n event.record,\n event.orRecord,\n event.orTag\n );\n }\n }\n\n private async handleGcPrune(message: GcPruneMessage): Promise<void> {\n const { olderThan } = message.payload;\n logger.info({ olderThan: olderThan.millis }, 'Received GC_PRUNE request');\n\n for (const [name, map] of this.maps) {\n if (map instanceof LWWMap) {\n const removedKeys = map.prune(olderThan);\n for (const key of removedKeys) {\n await this.storageAdapter.remove(`${name}:${key}`);\n }\n if (removedKeys.length > 0) {\n logger.info({ mapName: name, count: removedKeys.length }, 'Pruned tombstones from LWWMap');\n }\n } else if (map instanceof ORMap) {\n const removedTags = map.prune(olderThan);\n if (removedTags.length > 0) {\n logger.info({ mapName: name, count: removedTags.length }, 'Pruned tombstones from ORMap');\n }\n }\n }\n }\n\n public getHLC(): HLC {\n return this.hlc;\n }\n\n /**\n * Helper method to apply a single server event to the local map.\n * Used by both SERVER_EVENT and SERVER_BATCH_EVENT handlers.\n */\n private async applyServerEvent(\n mapName: string,\n eventType: 'PUT' | 'REMOVE' | 'OR_ADD' | 'OR_REMOVE',\n key: string,\n record?: LWWRecord<unknown>,\n orRecord?: ORMapRecord<unknown>,\n orTag?: string\n ): Promise<void> {\n const localMap = this.maps.get(mapName);\n if (localMap) {\n if (localMap instanceof LWWMap && record) {\n localMap.merge(key, record);\n await this.storageAdapter.put(`${mapName}:${key}`, record);\n } else if (localMap instanceof ORMap) {\n if (eventType === 'OR_ADD' && orRecord) {\n localMap.apply(key, orRecord);\n // We need to store ORMap records differently in storageAdapter or use a convention\n // For now, skipping persistent storage update for ORMap in this example\n } else if (eventType === 'OR_REMOVE' && orTag) {\n localMap.applyTombstone(orTag);\n }\n }\n }\n }\n\n /**\n * Closes the WebSocket connection and cleans up resources.\n */\n public close(): void {\n this.webSocketManager.close();\n\n // Cancel pending Write Concern promises (delegates to WriteConcernManager)\n this.writeConcernManager.cancelAllWriteConcernPromises(new Error('SyncEngine closed'));\n\n // Clean up CounterManager\n this.counterManager.close();\n\n // Clean up EntryProcessorClient\n this.entryProcessorClient.close(new Error('SyncEngine closed'));\n\n // Clean up SearchClient\n this.searchClient.close(new Error('SyncEngine closed'));\n\n this.stateMachine.transition(SyncState.DISCONNECTED);\n logger.info('SyncEngine closed');\n }\n\n /**\n * Reset the state machine and connection.\n * Use after fatal errors to start fresh.\n */\n public resetConnection(): void {\n this.close();\n this.stateMachine.reset();\n this.webSocketManager.reset();\n this.webSocketManager.connect();\n }\n\n // ============================================\n // Failover Support Methods\n // ============================================\n\n /**\n * Wait for a partition map update from the connection provider.\n * Used when an operation fails with NOT_OWNER error and needs\n * to wait for an updated partition map before retrying.\n *\n * @param timeoutMs - Maximum time to wait (default: 5000ms)\n * @returns Promise that resolves when partition map is updated or times out\n */\n public waitForPartitionMapUpdate(timeoutMs: number = 5000): Promise<void> {\n return new Promise((resolve) => {\n const timeout = setTimeout(resolve, timeoutMs);\n const connectionProvider = this.webSocketManager.getConnectionProvider();\n\n const handler = () => {\n clearTimeout(timeout);\n connectionProvider.off('partitionMapUpdated', handler);\n resolve();\n };\n\n connectionProvider.on('partitionMapUpdated', handler);\n });\n }\n\n /**\n * Wait for the connection to be available.\n * Used when an operation fails due to connection issues and needs\n * to wait for reconnection before retrying.\n *\n * @param timeoutMs - Maximum time to wait (default: 10000ms)\n * @returns Promise that resolves when connected or rejects on timeout\n */\n public waitForConnection(timeoutMs: number = 10000): Promise<void> {\n return new Promise((resolve, reject) => {\n const connectionProvider = this.webSocketManager.getConnectionProvider();\n\n // If already connected, resolve immediately\n if (connectionProvider.isConnected()) {\n resolve();\n return;\n }\n\n const timeout = setTimeout(() => {\n connectionProvider.off('connected', handler);\n reject(new Error('Connection timeout waiting for reconnection'));\n }, timeoutMs);\n\n const handler = () => {\n clearTimeout(timeout);\n connectionProvider.off('connected', handler);\n resolve();\n };\n\n connectionProvider.on('connected', handler);\n });\n }\n\n /**\n * Wait for a specific sync state.\n * Useful for waiting until fully connected and synced.\n *\n * @param targetState - The state to wait for\n * @param timeoutMs - Maximum time to wait (default: 30000ms)\n * @returns Promise that resolves when state is reached or rejects on timeout\n */\n public waitForState(targetState: SyncState, timeoutMs: number = 30000): Promise<void> {\n return new Promise((resolve, reject) => {\n // If already in target state, resolve immediately\n if (this.stateMachine.getState() === targetState) {\n resolve();\n return;\n }\n\n const timeout = setTimeout(() => {\n unsubscribe();\n reject(new Error(`Timeout waiting for state ${targetState}`));\n }, timeoutMs);\n\n const unsubscribe = this.stateMachine.onStateChange((event) => {\n if (event.to === targetState) {\n clearTimeout(timeout);\n unsubscribe();\n resolve();\n }\n });\n });\n }\n\n /**\n * Check if the connection provider is connected.\n * Convenience method for failover logic.\n */\n public isProviderConnected(): boolean {\n return this.webSocketManager.getConnectionProvider().isConnected();\n }\n\n /**\n * Get the connection provider for direct access.\n * Use with caution - prefer using SyncEngine methods.\n */\n public getConnectionProvider(): IConnectionProvider {\n return this.webSocketManager.getConnectionProvider();\n }\n\n private async resetMap(mapName: string): Promise<void> {\n const map = this.maps.get(mapName);\n if (map) {\n // Clear memory\n if (map instanceof LWWMap) {\n map.clear();\n } else if (map instanceof ORMap) {\n map.clear();\n }\n }\n\n // Clear storage\n const allKeys = await this.storageAdapter.getAllKeys();\n const mapKeys = allKeys.filter(k => k.startsWith(mapName + ':'));\n for (const key of mapKeys) {\n await this.storageAdapter.remove(key);\n }\n logger.info({ mapName, removedStorageCount: mapKeys.length }, 'Reset map: Cleared memory and storage');\n }\n\n // ============ Heartbeat Methods (delegate to WebSocketManager) ============\n\n /**\n * Returns the last measured round-trip time in milliseconds.\n * Returns null if no PONG has been received yet.\n */\n public getLastRoundTripTime(): number | null {\n return this.webSocketManager.getLastRoundTripTime();\n }\n\n /**\n * Returns true if the connection is considered healthy based on heartbeat.\n * A connection is healthy if it's online, authenticated, and has received\n * a PONG within the timeout window.\n */\n public isConnectionHealthy(): boolean {\n return this.webSocketManager.isConnectionHealthy();\n }\n\n // ============ Backpressure Methods (delegated to BackpressureController) ============\n\n /**\n * Get the current number of pending (unsynced) operations.\n * Delegates to BackpressureController.\n */\n public getPendingOpsCount(): number {\n return this.backpressureController.getPendingOpsCount();\n }\n\n /**\n * Get the current backpressure status.\n * Delegates to BackpressureController.\n */\n public getBackpressureStatus(): BackpressureStatus {\n return this.backpressureController.getBackpressureStatus();\n }\n\n /**\n * Returns true if writes are currently paused due to backpressure.\n * Delegates to BackpressureController.\n */\n public isBackpressurePaused(): boolean {\n return this.backpressureController.isBackpressurePaused();\n }\n\n /**\n * Subscribe to backpressure events.\n * Delegates to BackpressureController.\n * @param event Event name: 'backpressure:high', 'backpressure:low', 'backpressure:paused', 'backpressure:resumed', 'operation:dropped'\n * @param listener Callback function\n * @returns Unsubscribe function\n */\n public onBackpressure(\n event: 'backpressure:high' | 'backpressure:low' | 'backpressure:paused' | 'backpressure:resumed' | 'operation:dropped',\n listener: (data?: BackpressureThresholdEvent | OperationDroppedEvent) => void\n ): () => void {\n return this.backpressureController.onBackpressure(event, listener);\n }\n\n // ============================================\n // Write Concern Methods\n // ============================================\n\n /**\n * Register a pending Write Concern promise for an operation.\n * Delegates to WriteConcernManager.\n *\n * @param opId - Operation ID\n * @param timeout - Timeout in ms (default: 5000)\n * @returns Promise that resolves with the Write Concern result\n */\n public registerWriteConcernPromise(opId: string, timeout: number = 5000): Promise<any> {\n return this.writeConcernManager.registerWriteConcernPromise(opId, timeout);\n }\n\n // ============================================\n // PN Counter Methods - Delegates to CounterManager\n // ============================================\n\n /**\n * Subscribe to counter updates from server.\n * Delegates to CounterManager.\n * @param name Counter name\n * @param listener Callback when counter state is updated\n * @returns Unsubscribe function\n */\n public onCounterUpdate(name: string, listener: (state: { positive: Map<string, number>; negative: Map<string, number> }) => void): () => void {\n return this.counterManager.onCounterUpdate(name, listener);\n }\n\n /**\n * Request initial counter state from server.\n * Delegates to CounterManager.\n * @param name Counter name\n */\n public requestCounter(name: string): void {\n this.counterManager.requestCounter(name);\n }\n\n /**\n * Sync local counter state to server.\n * Delegates to CounterManager.\n * @param name Counter name\n * @param state Counter state to sync\n */\n public syncCounter(name: string, state: { positive: Map<string, number>; negative: Map<string, number> }): void {\n this.counterManager.syncCounter(name, state);\n }\n\n // ============================================\n // Entry Processor Methods - Delegates to EntryProcessorClient\n // ============================================\n\n /**\n * Execute an entry processor on a single key atomically.\n * Delegates to EntryProcessorClient.\n *\n * @param mapName Name of the map\n * @param key Key to process\n * @param processor Processor definition\n * @returns Promise resolving to the processor result\n */\n public async executeOnKey<V, R = V>(\n mapName: string,\n key: string,\n processor: EntryProcessorDef<V, R>,\n ): Promise<EntryProcessorResult<R>> {\n return this.entryProcessorClient.executeOnKey(mapName, key, processor);\n }\n\n /**\n * Execute an entry processor on multiple keys.\n * Delegates to EntryProcessorClient.\n *\n * @param mapName Name of the map\n * @param keys Keys to process\n * @param processor Processor definition\n * @returns Promise resolving to a map of key -> result\n */\n public async executeOnKeys<V, R = V>(\n mapName: string,\n keys: string[],\n processor: EntryProcessorDef<V, R>,\n ): Promise<Map<string, EntryProcessorResult<R>>> {\n return this.entryProcessorClient.executeOnKeys(mapName, keys, processor);\n }\n\n // ============================================\n // Event Journal Methods\n // ============================================\n\n /** Message listeners for journal and other generic messages */\n private messageListeners: Set<(message: unknown) => void> = new Set();\n\n /**\n * Subscribe to all incoming messages.\n * Used by EventJournalReader to receive journal events.\n *\n * @param event Event type (currently only 'message')\n * @param handler Message handler\n */\n public on(event: 'message', handler: (message: unknown) => void): void {\n if (event === 'message') {\n this.messageListeners.add(handler);\n }\n }\n\n /**\n * Unsubscribe from incoming messages.\n *\n * @param event Event type (currently only 'message')\n * @param handler Message handler to remove\n */\n public off(event: 'message', handler: (message: unknown) => void): void {\n if (event === 'message') {\n this.messageListeners.delete(handler);\n }\n }\n\n /**\n * Send a message to the server.\n * Public method for EventJournalReader and other components.\n *\n * @param message Message object to send\n */\n public send(message: unknown): void {\n this.sendMessage(message);\n }\n\n /**\n * Emit message to all listeners.\n * Called internally when a message is received.\n */\n private emitMessage(message: unknown): void {\n for (const listener of this.messageListeners) {\n try {\n listener(message);\n } catch (e) {\n logger.error({ err: e }, 'Message listener error');\n }\n }\n }\n\n // ============================================\n // Full-Text Search Methods - Delegates to SearchClient\n // ============================================\n\n /**\n * Perform a one-shot BM25 search on the server.\n * Delegates to SearchClient.\n *\n * @param mapName Name of the map to search\n * @param query Search query text\n * @param options Search options (limit, minScore, boost)\n * @returns Promise resolving to search results\n */\n public async search<T>(\n mapName: string,\n query: string,\n options?: SearchOptions\n ): Promise<SearchResult<T>[]> {\n return this.searchClient.search<T>(mapName, query, options);\n }\n\n // ============================================\n // Conflict Resolver Client\n // ============================================\n\n /**\n * Get the conflict resolver client for registering custom resolvers\n * and subscribing to merge rejection events.\n */\n public getConflictResolverClient(): ConflictResolverClient {\n return this.conflictResolverClient;\n }\n\n // ============================================\n // Hybrid Query Support - Delegates to QueryManager\n // ============================================\n\n /**\n * Subscribe to a hybrid query (FTS + filter combination).\n * Delegates to QueryManager.\n */\n public subscribeToHybridQuery<T>(query: HybridQueryHandle<T>): void {\n this.queryManager.subscribeToHybridQuery(query);\n }\n\n /**\n * Unsubscribe from a hybrid query.\n * Delegates to QueryManager.\n */\n public unsubscribeFromHybridQuery(queryId: string): void {\n this.queryManager.unsubscribeFromHybridQuery(queryId);\n }\n\n /**\n * Run a local hybrid query (FTS + filter combination).\n * Delegates to QueryManager.\n */\n public async runLocalHybridQuery<T>(\n mapName: string,\n filter: HybridQueryFilter\n ): Promise<Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }>> {\n return this.queryManager.runLocalHybridQuery<T>(mapName, filter);\n }\n\n /**\n * Handle operation rejected by server (permission denied, validation failure, etc.).\n */\n private handleOpRejected(message: OpRejectedMessage): void {\n const { opId, reason, code } = message.payload;\n logger.warn({ opId, reason, code }, 'Operation rejected by server');\n\n // Reject pending write concern promise if exists\n this.writeConcernManager.resolveWriteConcernPromise(opId, {\n opId,\n success: false,\n achievedLevel: 'FIRE_AND_FORGET' as any,\n error: reason,\n });\n }\n\n /**\n * Handle generic error message from server.\n */\n private handleError(message: ErrorMessage): void {\n const { code, message: errorMessage, details } = message.payload;\n logger.error({ code, message: errorMessage, details }, 'Server error received');\n }\n}\n","import pino from 'pino';\n\n// Simple check for browser environment\nconst isBrowser = typeof window !== 'undefined';\n\n// In browser, we might not have process.env, so we default to 'info'\n// Users can configure this via window.LOG_LEVEL or similar if needed,\n// but for now we stick to a safe default.\nconst logLevel = (typeof process !== 'undefined' && process.env && process.env.LOG_LEVEL) || 'info';\n\nexport const logger = pino({\n level: logLevel,\n transport: !isBrowser && (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') ? {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'SYS:standard',\n ignore: 'pid,hostname'\n }\n } : undefined,\n browser: {\n asObject: true\n }\n});\n\nexport type Logger = typeof logger;\n\n","/**\n * Defines the possible states for the SyncEngine connection state machine.\n */\nexport enum SyncState {\n /** Initial state before any connection attempt */\n INITIAL = 'INITIAL',\n /** WebSocket connection is being established */\n CONNECTING = 'CONNECTING',\n /** Connected, waiting for authentication response */\n AUTHENTICATING = 'AUTHENTICATING',\n /** Authenticated, performing initial data sync */\n SYNCING = 'SYNCING',\n /** Fully connected and synchronized */\n CONNECTED = 'CONNECTED',\n /** Intentionally or unexpectedly disconnected */\n DISCONNECTED = 'DISCONNECTED',\n /** Waiting before retry (exponential backoff) */\n BACKOFF = 'BACKOFF',\n /** Fatal error requiring manual intervention or reset */\n ERROR = 'ERROR',\n}\n\n/**\n * Defines valid state transitions for the SyncEngine FSM.\n * Each key is a current state, and the value is an array of valid target states.\n */\nexport const VALID_TRANSITIONS: Record<SyncState, SyncState[]> = {\n [SyncState.INITIAL]: [SyncState.CONNECTING],\n [SyncState.CONNECTING]: [SyncState.AUTHENTICATING, SyncState.BACKOFF, SyncState.ERROR, SyncState.DISCONNECTED],\n [SyncState.AUTHENTICATING]: [SyncState.SYNCING, SyncState.BACKOFF, SyncState.ERROR, SyncState.DISCONNECTED],\n [SyncState.SYNCING]: [SyncState.CONNECTED, SyncState.BACKOFF, SyncState.ERROR, SyncState.DISCONNECTED],\n [SyncState.CONNECTED]: [SyncState.SYNCING, SyncState.DISCONNECTED, SyncState.BACKOFF],\n [SyncState.DISCONNECTED]: [SyncState.CONNECTING, SyncState.BACKOFF, SyncState.INITIAL],\n [SyncState.BACKOFF]: [SyncState.CONNECTING, SyncState.DISCONNECTED, SyncState.INITIAL],\n [SyncState.ERROR]: [SyncState.INITIAL],\n};\n\n/**\n * Helper function to check if a transition is valid\n */\nexport function isValidTransition(from: SyncState, to: SyncState): boolean {\n return VALID_TRANSITIONS[from]?.includes(to) ?? false;\n}\n","import { SyncState, isValidTransition } from './SyncState';\nimport { logger } from './utils/logger';\n\n/**\n * Event emitted when the state machine transitions between states.\n */\nexport interface StateChangeEvent {\n /** The state before the transition */\n from: SyncState;\n /** The state after the transition */\n to: SyncState;\n /** Unix timestamp (ms) when the transition occurred */\n timestamp: number;\n}\n\n/**\n * Listener callback for state change events.\n */\nexport type StateChangeListener = (event: StateChangeEvent) => void;\n\n/**\n * Configuration options for the state machine.\n */\nexport interface SyncStateMachineConfig {\n /** Maximum number of state transitions to keep in history (default: 50) */\n maxHistorySize?: number;\n}\n\nconst DEFAULT_MAX_HISTORY_SIZE = 50;\n\n/**\n * A finite state machine for managing SyncEngine connection states.\n *\n * Features:\n * - Validates all state transitions against allowed paths\n * - Emits events on state changes for observability\n * - Maintains a history of transitions for debugging\n * - Logs invalid transition attempts (graceful degradation)\n */\nexport class SyncStateMachine {\n private state: SyncState = SyncState.INITIAL;\n private readonly listeners: Set<StateChangeListener> = new Set();\n private history: StateChangeEvent[] = [];\n private readonly maxHistorySize: number;\n\n constructor(config: SyncStateMachineConfig = {}) {\n this.maxHistorySize = config.maxHistorySize ?? DEFAULT_MAX_HISTORY_SIZE;\n }\n\n /**\n * Attempt to transition to a new state.\n * @param to The target state\n * @returns true if the transition was valid and executed, false otherwise\n */\n transition(to: SyncState): boolean {\n const from = this.state;\n\n if (from === to) {\n // No-op: already in target state\n return true;\n }\n\n if (!isValidTransition(from, to)) {\n logger.warn(\n { from, to, currentHistory: this.getHistory(5) },\n `Invalid state transition attempted: ${from} → ${to}`\n );\n return false;\n }\n\n // Execute the transition\n this.state = to;\n\n const event: StateChangeEvent = {\n from,\n to,\n timestamp: Date.now(),\n };\n\n // Add to history\n this.history.push(event);\n if (this.history.length > this.maxHistorySize) {\n this.history.shift();\n }\n\n // Notify listeners\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (err) {\n logger.error({ err, event }, 'State change listener threw an error');\n }\n }\n\n logger.debug({ from, to }, `State transition: ${from} → ${to}`);\n\n return true;\n }\n\n /**\n * Get the current state.\n */\n getState(): SyncState {\n return this.state;\n }\n\n /**\n * Check if a transition from the current state to the target state is valid.\n * @param to The target state to check\n */\n canTransition(to: SyncState): boolean {\n return this.state === to || isValidTransition(this.state, to);\n }\n\n /**\n * Subscribe to state change events.\n * @param listener Callback function to be called on each state change\n * @returns An unsubscribe function\n */\n onStateChange(listener: StateChangeListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Get the state transition history.\n * @param limit Maximum number of entries to return (default: all)\n * @returns Array of state change events, oldest first\n */\n getHistory(limit?: number): StateChangeEvent[] {\n if (limit === undefined || limit >= this.history.length) {\n return [...this.history];\n }\n return this.history.slice(-limit);\n }\n\n /**\n * Reset the state machine to INITIAL state.\n * This is a forced reset that bypasses normal transition validation.\n * Use for testing or hard resets after fatal errors.\n * @param clearHistory If true, also clears the transition history (default: true)\n */\n reset(clearHistory = true): void {\n const from = this.state;\n this.state = SyncState.INITIAL;\n\n if (clearHistory) {\n this.history = [];\n } else {\n // Record the reset as a transition\n const event: StateChangeEvent = {\n from,\n to: SyncState.INITIAL,\n timestamp: Date.now(),\n };\n this.history.push(event);\n if (this.history.length > this.maxHistorySize) {\n this.history.shift();\n }\n\n // Notify listeners\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (err) {\n logger.error({ err, event }, 'State change listener threw an error during reset');\n }\n }\n }\n\n logger.info({ from }, 'State machine reset to INITIAL');\n }\n\n /**\n * Check if the state machine is in a \"connected\" state\n * (either SYNCING or CONNECTED)\n */\n isConnected(): boolean {\n return this.state === SyncState.CONNECTED || this.state === SyncState.SYNCING;\n }\n\n /**\n * Check if the state machine is in a state where operations can be sent\n * (authenticated and connected)\n */\n isReady(): boolean {\n return this.state === SyncState.CONNECTED;\n }\n\n /**\n * Check if the state machine is currently attempting to connect\n */\n isConnecting(): boolean {\n return (\n this.state === SyncState.CONNECTING ||\n this.state === SyncState.AUTHENTICATING ||\n this.state === SyncState.SYNCING\n );\n }\n}\n","/**\n * Backpressure strategy when maxPendingOps is reached.\n */\nexport type BackpressureStrategy = 'pause' | 'throw' | 'drop-oldest';\n\n/**\n * Configuration for backpressure control on SyncEngine.\n */\nexport interface BackpressureConfig {\n /**\n * Maximum number of operations waiting for server acknowledgment.\n * When this limit is reached, the configured strategy will be applied.\n * @default 1000\n */\n maxPendingOps: number;\n\n /**\n * Strategy when maxPendingOps is reached:\n * - 'pause': Wait for capacity (returns Promise that resolves when space available)\n * - 'throw': Throw BackpressureError immediately\n * - 'drop-oldest': Remove oldest pending op to make room (data loss!)\n * @default 'pause'\n */\n strategy: BackpressureStrategy;\n\n /**\n * High water mark (percentage of maxPendingOps).\n * Emit 'backpressure:high' event when reached.\n * Value should be between 0 and 1.\n * @default 0.8 (80%)\n */\n highWaterMark: number;\n\n /**\n * Low water mark (percentage of maxPendingOps).\n * Resume paused writes and emit 'backpressure:low' when pending ops drop below this.\n * Value should be between 0 and 1.\n * @default 0.5 (50%)\n */\n lowWaterMark: number;\n}\n\n/**\n * Default backpressure configuration.\n */\nexport const DEFAULT_BACKPRESSURE_CONFIG: BackpressureConfig = {\n maxPendingOps: 1000,\n strategy: 'pause',\n highWaterMark: 0.8,\n lowWaterMark: 0.5,\n};\n\n/**\n * Status of backpressure mechanism.\n */\nexport interface BackpressureStatus {\n /** Current number of pending (unacknowledged) operations */\n pending: number;\n /** Maximum allowed pending operations */\n max: number;\n /** Percentage of capacity used (0-1) */\n percentage: number;\n /** Whether writes are currently paused due to backpressure */\n isPaused: boolean;\n /** Current backpressure strategy */\n strategy: BackpressureStrategy;\n}\n\n/**\n * Event data for backpressure:high and backpressure:low events.\n */\nexport interface BackpressureThresholdEvent {\n pending: number;\n max: number;\n}\n\n/**\n * Event data for operation:dropped event.\n */\nexport interface OperationDroppedEvent {\n opId: string;\n mapName: string;\n opType: string;\n key: string;\n}\n","import type { ConflictResolverDef, MergeRejection, Timestamp } from '@topgunbuild/core';\nimport type { SyncEngine } from './SyncEngine';\nimport { logger } from './utils/logger';\n\n/**\n * Registered resolver info returned from server.\n */\nexport interface ResolverInfo {\n mapName: string;\n name: string;\n priority?: number;\n keyPattern?: string;\n}\n\n/**\n * Registration result from server.\n */\nexport interface RegisterResult {\n success: boolean;\n error?: string;\n}\n\n/**\n * Client-side manager for conflict resolvers.\n *\n * Provides API for:\n * - Registering conflict resolvers on server\n * - Unregistering resolvers\n * - Listing registered resolvers\n * - Subscribing to merge rejection events\n */\nexport class ConflictResolverClient {\n private readonly syncEngine: SyncEngine;\n private readonly rejectionListeners: Set<(rejection: MergeRejection) => void> = new Set();\n private readonly pendingRequests: Map<string, {\n resolve: (result: any) => void;\n reject: (error: Error) => void;\n timeout: NodeJS.Timeout;\n }> = new Map();\n\n private static readonly REQUEST_TIMEOUT = 10000; // 10 seconds\n\n constructor(syncEngine: SyncEngine) {\n this.syncEngine = syncEngine;\n }\n\n /**\n * Register a conflict resolver on the server.\n *\n * @param mapName The map to register the resolver for\n * @param resolver The resolver definition\n * @returns Promise resolving to registration result\n *\n * @example\n * ```typescript\n * // Register a first-write-wins resolver for bookings\n * await client.resolvers.register('bookings', {\n * name: 'first-write-wins',\n * code: `\n * if (context.localValue !== undefined) {\n * return { action: 'reject', reason: 'Slot already booked' };\n * }\n * return { action: 'accept', value: context.remoteValue };\n * `,\n * priority: 100,\n * });\n * ```\n */\n async register<V>(\n mapName: string,\n resolver: Omit<ConflictResolverDef<V>, 'fn'>,\n ): Promise<RegisterResult> {\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n reject(new Error('Register resolver request timed out'));\n }, ConflictResolverClient.REQUEST_TIMEOUT);\n\n this.pendingRequests.set(requestId, {\n resolve: (result: RegisterResult) => {\n clearTimeout(timeout);\n resolve(result);\n },\n reject,\n timeout,\n });\n\n try {\n this.syncEngine.send({\n type: 'REGISTER_RESOLVER',\n requestId,\n mapName,\n resolver: {\n name: resolver.name,\n code: resolver.code || '',\n priority: resolver.priority,\n keyPattern: resolver.keyPattern,\n },\n });\n } catch {\n this.pendingRequests.delete(requestId);\n clearTimeout(timeout);\n resolve({ success: false, error: 'Not connected to server' });\n }\n });\n }\n\n /**\n * Unregister a conflict resolver from the server.\n *\n * @param mapName The map the resolver is registered for\n * @param resolverName The name of the resolver to unregister\n * @returns Promise resolving to unregistration result\n */\n async unregister(mapName: string, resolverName: string): Promise<RegisterResult> {\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n reject(new Error('Unregister resolver request timed out'));\n }, ConflictResolverClient.REQUEST_TIMEOUT);\n\n this.pendingRequests.set(requestId, {\n resolve: (result: RegisterResult) => {\n clearTimeout(timeout);\n resolve(result);\n },\n reject,\n timeout,\n });\n\n try {\n this.syncEngine.send({\n type: 'UNREGISTER_RESOLVER',\n requestId,\n mapName,\n resolverName,\n });\n } catch {\n this.pendingRequests.delete(requestId);\n clearTimeout(timeout);\n resolve({ success: false, error: 'Not connected to server' });\n }\n });\n }\n\n /**\n * List registered conflict resolvers on the server.\n *\n * @param mapName Optional - filter by map name\n * @returns Promise resolving to list of resolver info\n */\n async list(mapName?: string): Promise<ResolverInfo[]> {\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n reject(new Error('List resolvers request timed out'));\n }, ConflictResolverClient.REQUEST_TIMEOUT);\n\n this.pendingRequests.set(requestId, {\n resolve: (result: { resolvers: ResolverInfo[] }) => {\n clearTimeout(timeout);\n resolve(result.resolvers);\n },\n reject,\n timeout,\n });\n\n try {\n this.syncEngine.send({\n type: 'LIST_RESOLVERS',\n requestId,\n mapName,\n });\n } catch {\n this.pendingRequests.delete(requestId);\n clearTimeout(timeout);\n resolve([]);\n }\n });\n }\n\n /**\n * Subscribe to merge rejection events.\n *\n * @param listener Callback for rejection events\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = client.resolvers.onRejection((rejection) => {\n * console.log(`Merge rejected for ${rejection.key}: ${rejection.reason}`);\n * // Optionally refresh the local value\n * });\n *\n * // Later...\n * unsubscribe();\n * ```\n */\n onRejection(listener: (rejection: MergeRejection) => void): () => void {\n this.rejectionListeners.add(listener);\n return () => this.rejectionListeners.delete(listener);\n }\n\n /**\n * Handle REGISTER_RESOLVER_RESPONSE from server.\n * Called by SyncEngine.\n */\n handleRegisterResponse(message: {\n requestId: string;\n success: boolean;\n error?: string;\n }): void {\n const pending = this.pendingRequests.get(message.requestId);\n if (pending) {\n this.pendingRequests.delete(message.requestId);\n pending.resolve({ success: message.success, error: message.error });\n }\n }\n\n /**\n * Handle UNREGISTER_RESOLVER_RESPONSE from server.\n * Called by SyncEngine.\n */\n handleUnregisterResponse(message: {\n requestId: string;\n success: boolean;\n error?: string;\n }): void {\n const pending = this.pendingRequests.get(message.requestId);\n if (pending) {\n this.pendingRequests.delete(message.requestId);\n pending.resolve({ success: message.success, error: message.error });\n }\n }\n\n /**\n * Handle LIST_RESOLVERS_RESPONSE from server.\n * Called by SyncEngine.\n */\n handleListResponse(message: {\n requestId: string;\n resolvers: ResolverInfo[];\n }): void {\n const pending = this.pendingRequests.get(message.requestId);\n if (pending) {\n this.pendingRequests.delete(message.requestId);\n pending.resolve({ resolvers: message.resolvers });\n }\n }\n\n /**\n * Handle MERGE_REJECTED from server.\n * Called by SyncEngine.\n */\n handleMergeRejected(message: {\n mapName: string;\n key: string;\n attemptedValue: unknown;\n reason: string;\n timestamp: Timestamp;\n }): void {\n const rejection: MergeRejection = {\n mapName: message.mapName,\n key: message.key,\n attemptedValue: message.attemptedValue,\n reason: message.reason,\n timestamp: message.timestamp,\n nodeId: '', // Not provided by server in this message\n };\n\n logger.debug({ rejection }, 'Merge rejected by server');\n\n for (const listener of this.rejectionListeners) {\n try {\n listener(rejection);\n } catch (e) {\n logger.error({ error: e }, 'Error in rejection listener');\n }\n }\n }\n\n /**\n * Clear all pending requests (e.g., on disconnect).\n */\n clearPending(): void {\n for (const [requestId, pending] of this.pendingRequests) {\n clearTimeout(pending.timeout);\n pending.reject(new Error('Connection lost'));\n }\n this.pendingRequests.clear();\n }\n\n /**\n * Get the number of registered rejection listeners.\n */\n get rejectionListenerCount(): number {\n return this.rejectionListeners.size;\n }\n}\n","/**\n * WebSocketManager - Handles all WebSocket/connection operations for SyncEngine\n *\n * Responsibilities:\n * - WebSocket lifecycle management (connect, close, reset)\n * - Message serialization/deserialization\n * - Heartbeat mechanism (PING/PONG)\n * - Reconnection with exponential backoff\n * - Event forwarding from connection provider\n */\n\nimport { serialize, deserialize } from '@topgunbuild/core';\nimport type { IConnectionProvider, ConnectionProviderEvent, ConnectionEventHandler } from '../types';\nimport { SyncState } from '../SyncState';\nimport { logger } from '../utils/logger';\nimport type { IWebSocketManager, WebSocketManagerConfig } from './types';\n\n/**\n * WebSocketManager implements IWebSocketManager.\n *\n * Manages WebSocket connections via IConnectionProvider.\n * Supports both single-server and cluster modes through the provider abstraction.\n */\nexport class WebSocketManager implements IWebSocketManager {\n private readonly config: WebSocketManagerConfig;\n private readonly connectionProvider: IConnectionProvider;\n\n // Reconnection state\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private backoffAttempt: number = 0;\n\n // Heartbeat state\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null;\n private lastPongReceived: number = Date.now();\n private lastRoundTripTime: number | null = null;\n\n constructor(config: WebSocketManagerConfig) {\n this.config = config;\n this.connectionProvider = config.connectionProvider;\n }\n\n /**\n * Initialize the connection.\n * Sets up event handlers and starts the connection process.\n */\n connect(): void {\n this.initConnectionProvider();\n }\n\n /**\n * Initialize connection using IConnectionProvider.\n */\n private initConnectionProvider(): void {\n // Transition to CONNECTING state\n this.config.stateMachine.transition(SyncState.CONNECTING);\n\n // Set up event handlers\n this.connectionProvider.on('connected', (_nodeId: string) => {\n logger.info('ConnectionProvider connected.');\n this.config.onConnected?.();\n });\n\n this.connectionProvider.on('disconnected', (_nodeId: string) => {\n logger.info('ConnectionProvider disconnected.');\n this.stopHeartbeat();\n this.config.stateMachine.transition(SyncState.DISCONNECTED);\n this.config.onDisconnected?.();\n // Don't schedule reconnect - provider handles it\n });\n\n this.connectionProvider.on('reconnected', (_nodeId: string) => {\n logger.info('ConnectionProvider reconnected.');\n this.config.stateMachine.transition(SyncState.CONNECTING);\n this.config.onReconnected?.();\n });\n\n this.connectionProvider.on('message', (_nodeId: string, data: any) => {\n const message = this.deserializeMessage(data);\n if (message) {\n this.handleMessage(message);\n }\n });\n\n this.connectionProvider.on('partitionMapUpdated', () => {\n logger.debug('Partition map updated');\n });\n\n this.connectionProvider.on('error', (error: Error) => {\n logger.error({ err: error }, 'ConnectionProvider error');\n });\n\n // Start connection\n this.connectionProvider.connect().catch((err) => {\n logger.error({ err }, 'Failed to connect via ConnectionProvider');\n this.config.stateMachine.transition(SyncState.DISCONNECTED);\n });\n }\n\n /**\n * Deserialize incoming message data.\n */\n private deserializeMessage(data: any): any {\n try {\n if (data instanceof ArrayBuffer) {\n return deserialize(new Uint8Array(data));\n } else if (data instanceof Uint8Array) {\n return deserialize(data);\n } else if (typeof data === 'string') {\n return JSON.parse(data);\n } else {\n return data;\n }\n } catch (e) {\n logger.error({ err: e }, 'Failed to parse message');\n return null;\n }\n }\n\n /**\n * Handle incoming message.\n * Routes PONG to internal handler, all others to SyncEngine.\n */\n private handleMessage(message: any): void {\n // Handle PONG internally for heartbeat tracking\n if (message.type === 'PONG') {\n this.handlePong(message);\n }\n // Route all messages to SyncEngine (including PONG for HLC sync if needed)\n this.config.onMessage(message);\n }\n\n /**\n * Send a message through the current connection.\n */\n sendMessage(message: any, key?: string): boolean {\n const data = serialize(message);\n\n try {\n this.connectionProvider.send(data, key);\n return true;\n } catch (err) {\n logger.warn({ err }, 'Failed to send via ConnectionProvider');\n return false;\n }\n }\n\n /**\n * Check if we can send messages (connection is ready).\n */\n canSend(): boolean {\n return this.connectionProvider.isConnected();\n }\n\n /**\n * Check if connected to the server.\n */\n isOnline(): boolean {\n const state = this.config.stateMachine.getState();\n return (\n state === SyncState.CONNECTING ||\n state === SyncState.AUTHENTICATING ||\n state === SyncState.SYNCING ||\n state === SyncState.CONNECTED\n );\n }\n\n /**\n * Get the connection provider.\n */\n getConnectionProvider(): IConnectionProvider {\n return this.connectionProvider;\n }\n\n /**\n * Close the connection and clean up resources.\n */\n close(): void {\n this.stopHeartbeat();\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n this.connectionProvider.close().catch((err) => {\n logger.error({ err }, 'Error closing ConnectionProvider');\n });\n }\n\n /**\n * Reset connection state for a fresh reconnection.\n */\n reset(): void {\n this.close();\n this.resetBackoff();\n }\n\n /**\n * Subscribe to connection events.\n */\n on(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n this.connectionProvider.on(event, handler);\n }\n\n /**\n * Unsubscribe from connection events.\n */\n off(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n this.connectionProvider.off(event, handler);\n }\n\n /**\n * Reset backoff counter.\n */\n resetBackoff(): void {\n this.backoffAttempt = 0;\n }\n\n /**\n * Get current backoff attempt count.\n */\n getBackoffAttempt(): number {\n return this.backoffAttempt;\n }\n\n /**\n * Clear reconnect timer (for external control, e.g., when new auth token provided).\n */\n clearReconnectTimer(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n // ============================================\n // Heartbeat Mechanism\n // ============================================\n\n /**\n * Starts the heartbeat mechanism after successful connection.\n */\n startHeartbeat(): void {\n if (!this.config.heartbeatConfig.enabled) {\n return;\n }\n\n this.stopHeartbeat(); // Clear any existing interval\n this.lastPongReceived = Date.now();\n\n this.heartbeatInterval = setInterval(() => {\n this.sendPing();\n this.checkHeartbeatTimeout();\n }, this.config.heartbeatConfig.intervalMs);\n\n logger.info({ intervalMs: this.config.heartbeatConfig.intervalMs }, 'Heartbeat started');\n }\n\n /**\n * Stops the heartbeat mechanism.\n */\n stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n logger.info('Heartbeat stopped');\n }\n }\n\n /**\n * Sends a PING message to the server.\n */\n private sendPing(): void {\n if (this.canSend()) {\n const pingMessage = {\n type: 'PING',\n timestamp: Date.now(),\n };\n this.sendMessage(pingMessage);\n }\n }\n\n /**\n * Handles incoming PONG message from server.\n */\n private handlePong(msg: { timestamp: number; serverTime: number }): void {\n const now = Date.now();\n this.lastPongReceived = now;\n this.lastRoundTripTime = now - msg.timestamp;\n\n logger.debug({\n rtt: this.lastRoundTripTime,\n serverTime: msg.serverTime,\n clockSkew: msg.serverTime - (msg.timestamp + this.lastRoundTripTime / 2),\n }, 'Received PONG');\n }\n\n /**\n * Checks if heartbeat has timed out and triggers reconnection if needed.\n */\n private checkHeartbeatTimeout(): void {\n const now = Date.now();\n const timeSinceLastPong = now - this.lastPongReceived;\n\n if (timeSinceLastPong > this.config.heartbeatConfig.timeoutMs) {\n logger.warn({\n timeSinceLastPong,\n timeoutMs: this.config.heartbeatConfig.timeoutMs,\n }, 'Heartbeat timeout - triggering reconnection');\n\n this.stopHeartbeat();\n\n // Force reconnect — preserves the provider's reconnect behavior\n // (unlike close() which permanently kills the connection)\n this.connectionProvider.forceReconnect();\n }\n }\n\n /**\n * Returns the last measured round-trip time in milliseconds.\n */\n getLastRoundTripTime(): number | null {\n return this.lastRoundTripTime;\n }\n\n /**\n * Returns true if the connection is considered healthy based on heartbeat.\n */\n isConnectionHealthy(): boolean {\n const state = this.config.stateMachine.getState();\n const isOnline = (\n state === SyncState.CONNECTING ||\n state === SyncState.AUTHENTICATING ||\n state === SyncState.SYNCING ||\n state === SyncState.CONNECTED\n );\n const isAuthenticated = state === SyncState.SYNCING || state === SyncState.CONNECTED;\n\n if (!isOnline || !isAuthenticated) {\n return false;\n }\n\n if (!this.config.heartbeatConfig.enabled) {\n return true; // If heartbeat disabled, consider healthy if online\n }\n\n const timeSinceLastPong = Date.now() - this.lastPongReceived;\n return timeSinceLastPong < this.config.heartbeatConfig.timeoutMs;\n }\n}\n","/**\n * Error thrown when backpressure limit is reached and strategy is 'throw'.\n */\nexport class BackpressureError extends Error {\n public readonly name = 'BackpressureError';\n\n constructor(\n public readonly pendingCount: number,\n public readonly maxPending: number\n ) {\n super(\n `Backpressure limit reached: ${pendingCount}/${maxPending} pending operations. ` +\n `Wait for acknowledgments or increase maxPendingOps.`\n );\n\n // Maintains proper stack trace for where error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, BackpressureError);\n }\n }\n}\n","/**\n * BackpressureController\n *\n * Handles all backpressure operations for SyncEngine:\n * - Monitors pending operations count via shared opLog reference\n * - Implements pause/throw/drop-oldest strategies\n * - Emits high/low water mark events\n * - Manages paused writes and capacity waiting\n */\n\nimport type {\n BackpressureConfig,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n} from '../BackpressureConfig';\nimport { BackpressureError } from '../errors/BackpressureError';\nimport type { OpLogEntry } from '../SyncEngine';\nimport type { IBackpressureController, BackpressureControllerConfig } from './types';\nimport { logger } from '../utils/logger';\n\n/**\n * BackpressureController manages flow control for pending operations.\n *\n * It receives a shared reference to opLog from SyncEngine and uses it\n * to count pending operations and implement drop-oldest strategy.\n * SyncEngine retains ownership of opLog.\n */\nexport class BackpressureController implements IBackpressureController {\n private readonly config: BackpressureConfig;\n private readonly opLog: OpLogEntry[];\n\n // Internal state\n private backpressurePaused: boolean = false;\n private waitingForCapacity: Array<() => void> = [];\n private highWaterMarkEmitted: boolean = false;\n private backpressureListeners: Map<string, Set<(...args: any[]) => void>> = new Map();\n\n constructor(controllerConfig: BackpressureControllerConfig) {\n this.config = controllerConfig.config;\n this.opLog = controllerConfig.opLog; // Shared reference, not a copy\n }\n\n // ============================================\n // Status Methods\n // ============================================\n\n /**\n * Get the current number of pending (unsynced) operations.\n */\n public getPendingOpsCount(): number {\n return this.opLog.filter(op => !op.synced).length;\n }\n\n /**\n * Get the current backpressure status.\n */\n public getBackpressureStatus(): BackpressureStatus {\n const pending = this.getPendingOpsCount();\n const max = this.config.maxPendingOps;\n return {\n pending,\n max,\n percentage: max > 0 ? pending / max : 0,\n isPaused: this.backpressurePaused,\n strategy: this.config.strategy,\n };\n }\n\n /**\n * Returns true if writes are currently paused due to backpressure.\n */\n public isBackpressurePaused(): boolean {\n return this.backpressurePaused;\n }\n\n // ============================================\n // Check Methods\n // ============================================\n\n /**\n * Check backpressure before adding a new operation.\n * May pause, throw, or drop depending on strategy.\n */\n public async checkBackpressure(): Promise<void> {\n const pendingCount = this.getPendingOpsCount();\n\n if (pendingCount < this.config.maxPendingOps) {\n return; // Capacity available\n }\n\n switch (this.config.strategy) {\n case 'pause':\n await this.waitForCapacity();\n break;\n case 'throw':\n throw new BackpressureError(\n pendingCount,\n this.config.maxPendingOps\n );\n case 'drop-oldest':\n this.dropOldestOp();\n break;\n }\n }\n\n /**\n * Check high water mark and emit event if threshold reached.\n */\n public checkHighWaterMark(): void {\n const pendingCount = this.getPendingOpsCount();\n const threshold = Math.floor(\n this.config.maxPendingOps * this.config.highWaterMark\n );\n\n if (pendingCount >= threshold && !this.highWaterMarkEmitted) {\n this.highWaterMarkEmitted = true;\n logger.warn(\n { pending: pendingCount, max: this.config.maxPendingOps },\n 'Backpressure high water mark reached'\n );\n this.emitBackpressureEvent('backpressure:high', {\n pending: pendingCount,\n max: this.config.maxPendingOps,\n });\n }\n }\n\n /**\n * Check low water mark and resume paused writes if threshold reached.\n */\n public checkLowWaterMark(): void {\n const pendingCount = this.getPendingOpsCount();\n const lowThreshold = Math.floor(\n this.config.maxPendingOps * this.config.lowWaterMark\n );\n const highThreshold = Math.floor(\n this.config.maxPendingOps * this.config.highWaterMark\n );\n\n // Reset high water mark flag when below high threshold\n if (pendingCount < highThreshold && this.highWaterMarkEmitted) {\n this.highWaterMarkEmitted = false;\n }\n\n // Emit low water mark event when crossing below threshold\n if (pendingCount <= lowThreshold) {\n if (this.backpressurePaused) {\n this.backpressurePaused = false;\n logger.info(\n { pending: pendingCount, max: this.config.maxPendingOps },\n 'Backpressure low water mark reached, resuming writes'\n );\n this.emitBackpressureEvent('backpressure:low', {\n pending: pendingCount,\n max: this.config.maxPendingOps,\n });\n this.emitBackpressureEvent('backpressure:resumed');\n\n // Resume all waiting writes\n const waiting = this.waitingForCapacity;\n this.waitingForCapacity = [];\n for (const resolve of waiting) {\n resolve();\n }\n }\n }\n }\n\n // ============================================\n // Event Methods\n // ============================================\n\n /**\n * Subscribe to backpressure events.\n * @param event Event name: 'backpressure:high', 'backpressure:low', 'backpressure:paused', 'backpressure:resumed', 'operation:dropped'\n * @param listener Callback function\n * @returns Unsubscribe function\n */\n public onBackpressure(\n event: 'backpressure:high' | 'backpressure:low' | 'backpressure:paused' | 'backpressure:resumed' | 'operation:dropped',\n listener: (data?: BackpressureThresholdEvent | OperationDroppedEvent) => void\n ): () => void {\n if (!this.backpressureListeners.has(event)) {\n this.backpressureListeners.set(event, new Set());\n }\n this.backpressureListeners.get(event)!.add(listener);\n\n return () => {\n this.backpressureListeners.get(event)?.delete(listener);\n };\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n /**\n * Emit a backpressure event to all listeners.\n */\n private emitBackpressureEvent(\n event: 'backpressure:high' | 'backpressure:low' | 'backpressure:paused' | 'backpressure:resumed' | 'operation:dropped',\n data?: BackpressureThresholdEvent | OperationDroppedEvent\n ): void {\n const listeners = this.backpressureListeners.get(event);\n if (listeners) {\n for (const listener of listeners) {\n try {\n listener(data);\n } catch (err) {\n logger.error({ err, event }, 'Error in backpressure event listener');\n }\n }\n }\n }\n\n /**\n * Wait for capacity to become available (used by 'pause' strategy).\n */\n private async waitForCapacity(): Promise<void> {\n if (!this.backpressurePaused) {\n this.backpressurePaused = true;\n logger.warn('Backpressure paused - waiting for capacity');\n this.emitBackpressureEvent('backpressure:paused');\n }\n\n return new Promise<void>((resolve) => {\n this.waitingForCapacity.push(resolve);\n });\n }\n\n /**\n * Drop the oldest pending operation (used by 'drop-oldest' strategy).\n * Modifies opLog via shared reference.\n */\n private dropOldestOp(): void {\n // Find oldest unsynced operation by array order (oldest first)\n const oldestIndex = this.opLog.findIndex(op => !op.synced);\n\n if (oldestIndex !== -1) {\n const dropped = this.opLog[oldestIndex];\n this.opLog.splice(oldestIndex, 1);\n\n logger.warn(\n { opId: dropped.id, mapName: dropped.mapName, key: dropped.key },\n 'Dropped oldest pending operation due to backpressure'\n );\n\n this.emitBackpressureEvent('operation:dropped', {\n opId: dropped.id,\n mapName: dropped.mapName,\n opType: dropped.opType,\n key: dropped.key,\n });\n }\n }\n}\n\nexport type { BackpressureControllerConfig } from './types';\n","/**\n * QueryManager - Manages query subscriptions and local query execution\n *\n * This module extracts query management from SyncEngine:\n * - Standard query subscriptions (QueryHandle)\n * - Hybrid query subscriptions (HybridQueryHandle with FTS)\n * - Local query execution against storage adapter\n */\n\nimport { evaluatePredicate } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\nimport type { QueryHandle, QueryFilter } from '../QueryHandle';\nimport type { HybridQueryHandle, HybridQueryFilter } from '../HybridQueryHandle';\nimport type { IQueryManager, QueryManagerConfig } from './types';\n\n/**\n * QueryManager handles all query-related operations for SyncEngine.\n * It owns the queries and hybridQueries Maps (single source of truth).\n */\nexport class QueryManager implements IQueryManager {\n private readonly config: QueryManagerConfig;\n\n /** Standard queries (single source of truth) */\n private queries: Map<string, QueryHandle<any>> = new Map();\n\n /** Hybrid queries with FTS support (single source of truth) */\n private hybridQueries: Map<string, HybridQueryHandle<any>> = new Map();\n\n constructor(config: QueryManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Query Access Methods\n // ============================================\n\n /**\n * Get all queries (read-only access).\n */\n public getQueries(): Map<string, QueryHandle<any>> {\n return this.queries;\n }\n\n /**\n * Get all hybrid queries.\n */\n public getHybridQueries(): Map<string, HybridQueryHandle<any>> {\n return this.hybridQueries;\n }\n\n /**\n * Get a hybrid query by ID.\n */\n public getHybridQuery(queryId: string): HybridQueryHandle<any> | undefined {\n return this.hybridQueries.get(queryId);\n }\n\n // ============================================\n // Standard Query Methods\n // ============================================\n\n /**\n * Subscribe to a standard query.\n * Adds to queries Map and sends subscription to server if authenticated.\n */\n public subscribeToQuery(query: QueryHandle<any>): void {\n this.queries.set(query.id, query);\n if (this.config.isAuthenticated()) {\n this.sendQuerySubscription(query);\n }\n }\n\n /**\n * Unsubscribe from a query.\n * Removes from Map and sends unsubscription to server if authenticated.\n */\n public unsubscribeFromQuery(queryId: string): void {\n this.queries.delete(queryId);\n if (this.config.isAuthenticated()) {\n this.config.sendMessage({\n type: 'QUERY_UNSUB',\n payload: { queryId }\n });\n }\n }\n\n /**\n * Send query subscription message to server.\n */\n private sendQuerySubscription(query: QueryHandle<any>): void {\n this.config.sendMessage({\n type: 'QUERY_SUB',\n payload: {\n queryId: query.id,\n mapName: query.getMapName(),\n query: query.getFilter()\n }\n });\n }\n\n // ============================================\n // Hybrid Query Methods\n // ============================================\n\n /**\n * Subscribe to a hybrid query (FTS + filter combination).\n */\n public subscribeToHybridQuery<T>(query: HybridQueryHandle<T>): void {\n this.hybridQueries.set(query.id, query);\n\n const filter = query.getFilter();\n const mapName = query.getMapName();\n\n // If query has FTS predicate and authenticated, send to server\n if (query.hasFTSPredicate() && this.config.isAuthenticated()) {\n this.sendHybridQuerySubscription(query.id, mapName, filter);\n }\n\n // Load initial local data\n this.runLocalHybridQuery<T>(mapName, filter).then((results) => {\n query.onResult(results, 'local');\n });\n }\n\n /**\n * Unsubscribe from a hybrid query.\n */\n public unsubscribeFromHybridQuery(queryId: string): void {\n const query = this.hybridQueries.get(queryId);\n if (query) {\n this.hybridQueries.delete(queryId);\n\n // Notify server to unsubscribe\n if (this.config.isAuthenticated() && query.hasFTSPredicate()) {\n this.config.sendMessage({\n type: 'HYBRID_QUERY_UNSUBSCRIBE',\n payload: { subscriptionId: queryId },\n });\n }\n }\n }\n\n /**\n * Send hybrid query subscription message to server.\n */\n private sendHybridQuerySubscription(\n queryId: string,\n mapName: string,\n filter: HybridQueryFilter\n ): void {\n this.config.sendMessage({\n type: 'HYBRID_QUERY_SUBSCRIBE',\n payload: {\n subscriptionId: queryId,\n mapName,\n predicate: filter.predicate,\n where: filter.where,\n sort: filter.sort,\n limit: filter.limit,\n cursor: filter.cursor,\n },\n });\n }\n\n // ============================================\n // Local Query Execution\n // ============================================\n\n /**\n * Executes a query against local storage immediately.\n */\n public async runLocalQuery(\n mapName: string,\n filter: QueryFilter\n ): Promise<{ key: string; value: any }[]> {\n // Retrieve all keys for the map\n const keys = await this.config.storageAdapter.getAllKeys();\n const mapKeys = keys.filter(k => k.startsWith(mapName + ':'));\n\n const results: { key: string; value: any }[] = [];\n for (const fullKey of mapKeys) {\n const record = await this.config.storageAdapter.get(fullKey);\n if (record && record.value) {\n // Extract actual key from \"mapName:key\"\n const actualKey = fullKey.slice(mapName.length + 1);\n\n let matches = true;\n\n // Apply 'where' (equality)\n if (filter.where) {\n for (const [k, v] of Object.entries(filter.where)) {\n if (record.value[k] !== v) {\n matches = false;\n break;\n }\n }\n }\n\n // Apply 'predicate'\n if (matches && filter.predicate) {\n if (!evaluatePredicate(filter.predicate, record.value)) {\n matches = false;\n }\n }\n\n if (matches) {\n results.push({ key: actualKey, value: record.value });\n }\n }\n }\n return results;\n }\n\n /**\n * Run a local hybrid query (FTS + filter combination).\n * For FTS predicates, returns results with score = 0 (local-only mode).\n * Server provides actual FTS scoring.\n */\n public async runLocalHybridQuery<T>(\n mapName: string,\n filter: HybridQueryFilter\n ): Promise<Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }>> {\n const results: Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }> = [];\n\n // Get all entries from the map using storage adapter\n const allKeys = await this.config.storageAdapter.getAllKeys();\n const mapPrefix = `${mapName}:`;\n const entries: Array<[string, any]> = [];\n\n for (const fullKey of allKeys) {\n if (fullKey.startsWith(mapPrefix)) {\n const key = fullKey.substring(mapPrefix.length);\n const record = await this.config.storageAdapter.get(fullKey);\n if (record) {\n entries.push([key, record]);\n }\n }\n }\n\n for (const [key, record] of entries) {\n if (record === null || record.value === null) continue;\n\n const value = record.value as T;\n\n // Evaluate predicate (including FTS predicates - basic local evaluation)\n if (filter.predicate) {\n const matches = evaluatePredicate(filter.predicate, value as Record<string, unknown>);\n if (!matches) continue;\n }\n\n // Evaluate where clause\n if (filter.where) {\n let whereMatches = true;\n for (const [field, expected] of Object.entries(filter.where)) {\n if ((value as any)[field] !== expected) {\n whereMatches = false;\n break;\n }\n }\n if (!whereMatches) continue;\n }\n\n results.push({\n key,\n value,\n score: 0, // Local doesn't have FTS scoring\n matchedTerms: [],\n });\n }\n\n // Sort results\n if (filter.sort) {\n results.sort((a, b) => {\n for (const [field, direction] of Object.entries(filter.sort!)) {\n let valA: any;\n let valB: any;\n\n if (field === '_score') {\n valA = a.score ?? 0;\n valB = b.score ?? 0;\n } else if (field === '_key') {\n valA = a.key;\n valB = b.key;\n } else {\n valA = (a.value as any)[field];\n valB = (b.value as any)[field];\n }\n\n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n // Apply limit (cursor filtering is done server-side)\n let sliced = results;\n if (filter.limit) {\n sliced = sliced.slice(0, filter.limit);\n }\n\n return sliced;\n }\n\n // ============================================\n // Re-subscription (after auth)\n // ============================================\n\n /**\n * Re-subscribe all queries after authentication.\n * Called by SyncEngine after AUTH_ACK.\n */\n public resubscribeAll(): void {\n logger.debug({ queryCount: this.queries.size, hybridCount: this.hybridQueries.size }, 'QueryManager: resubscribing all queries');\n\n // Re-subscribe standard queries\n for (const query of this.queries.values()) {\n this.sendQuerySubscription(query);\n }\n\n // Re-subscribe hybrid queries with FTS predicates\n for (const query of this.hybridQueries.values()) {\n if (query.hasFTSPredicate()) {\n this.sendHybridQuerySubscription(query.id, query.getMapName(), query.getFilter());\n }\n }\n }\n}\n","/**\n * TopicManager - Handles topic (pub/sub) operations for SyncEngine\n *\n * Responsibilities:\n * - Topic subscriptions and unsubscriptions\n * - Publishing messages to topics\n * - Queueing messages when offline\n * - Flushing queued messages after authentication\n * - Handling incoming topic messages from server\n */\n\nimport type { TopicQueueConfig } from '../SyncEngine';\nimport { TopicHandle } from '../TopicHandle';\nimport { logger } from '../utils/logger';\nimport type { ITopicManager, TopicManagerConfig } from './types';\n\n/**\n * Queued topic message for offline publishing.\n */\ninterface QueuedTopicMessage {\n topic: string;\n data: any;\n timestamp: number;\n}\n\n/**\n * TopicManager implements ITopicManager.\n *\n * Manages topic subscriptions with support for:\n * - Pub/sub pattern for real-time messaging\n * - Offline message queueing with configurable strategies\n * - Automatic resubscription after authentication\n */\nexport class TopicManager implements ITopicManager {\n private readonly config: TopicManagerConfig;\n\n // Topic subscriptions (single source of truth)\n private topics: Map<string, TopicHandle> = new Map();\n\n // Offline message queue\n private topicQueue: QueuedTopicMessage[] = [];\n\n constructor(config: TopicManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Subscribe to a topic.\n * Adds to topics Map and sends subscription to server if authenticated.\n */\n public subscribeToTopic(topic: string, handle: TopicHandle): void {\n this.topics.set(topic, handle);\n if (this.config.isAuthenticated()) {\n this.sendTopicSubscription(topic);\n }\n }\n\n /**\n * Unsubscribe from a topic.\n * Removes from Map and sends unsubscription to server if authenticated.\n */\n public unsubscribeFromTopic(topic: string): void {\n this.topics.delete(topic);\n if (this.config.isAuthenticated()) {\n this.config.sendMessage({\n type: 'TOPIC_UNSUB',\n payload: { topic }\n });\n }\n }\n\n /**\n * Publish a message to a topic.\n * Sends immediately if authenticated, otherwise queues for later.\n */\n public publishTopic(topic: string, data: any): void {\n if (this.config.isAuthenticated()) {\n this.config.sendMessage({\n type: 'TOPIC_PUB',\n payload: { topic, data }\n });\n } else {\n this.queueTopicMessage(topic, data);\n }\n }\n\n /**\n * Flush all queued topic messages.\n * Called by SyncEngine after authentication.\n */\n public flushTopicQueue(): void {\n if (this.topicQueue.length === 0) return;\n\n logger.info({ count: this.topicQueue.length }, 'Flushing queued topic messages');\n\n for (const msg of this.topicQueue) {\n this.config.sendMessage({\n type: 'TOPIC_PUB',\n payload: { topic: msg.topic, data: msg.data },\n });\n }\n\n this.topicQueue = [];\n }\n\n /**\n * Get topic queue status.\n */\n public getTopicQueueStatus(): { size: number; maxSize: number } {\n return {\n size: this.topicQueue.length,\n maxSize: this.config.topicQueueConfig.maxSize,\n };\n }\n\n /**\n * Get all subscribed topics.\n * Used for resubscription after authentication.\n */\n public getTopics(): IterableIterator<string> {\n return this.topics.keys();\n }\n\n /**\n * Re-subscribe all topics after authentication.\n * Called by SyncEngine after AUTH_ACK.\n */\n public resubscribeAll(): void {\n for (const topic of this.topics.keys()) {\n this.sendTopicSubscription(topic);\n }\n }\n\n /**\n * Handle incoming topic message from server.\n */\n public handleTopicMessage(topic: string, data: any, publisherId: string, timestamp: number): void {\n const handle = this.topics.get(topic);\n if (handle) {\n handle.onMessage(data, { publisherId, timestamp });\n }\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n /**\n * Queue a topic message for offline publishing.\n */\n private queueTopicMessage(topic: string, data: any): void {\n const message: QueuedTopicMessage = {\n topic,\n data,\n timestamp: Date.now(),\n };\n\n if (this.topicQueue.length >= this.config.topicQueueConfig.maxSize) {\n if (this.config.topicQueueConfig.strategy === 'drop-oldest') {\n const dropped = this.topicQueue.shift();\n logger.warn({ topic: dropped?.topic }, 'Dropped oldest queued topic message (queue full)');\n } else {\n logger.warn({ topic }, 'Dropped newest topic message (queue full)');\n return;\n }\n }\n\n this.topicQueue.push(message);\n logger.debug({ topic, queueSize: this.topicQueue.length }, 'Queued topic message for offline');\n }\n\n /**\n * Send topic subscription message to server.\n */\n private sendTopicSubscription(topic: string): void {\n this.config.sendMessage({\n type: 'TOPIC_SUB',\n payload: { topic }\n });\n }\n}\n","/**\n * LockManager - Handles distributed lock operations for SyncEngine\n *\n * Responsibilities:\n * - Lock acquisition with timeout\n * - Lock release with acknowledgment\n * - Pending lock request tracking\n * - Message handlers for LOCK_GRANTED and LOCK_RELEASED\n */\n\nimport { logger } from '../utils/logger';\nimport type { ILockManager, LockManagerConfig } from './types';\n\n/**\n * Pending lock request state.\n */\ninterface PendingLockRequest {\n resolve: (res: any) => void;\n reject: (err: any) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * LockManager implements ILockManager.\n *\n * Manages distributed locks with support for:\n * - Request/release pattern with fencing tokens\n * - Timeout handling for lost messages\n * - Server acknowledgment tracking\n */\nexport class LockManager implements ILockManager {\n private readonly config: LockManagerConfig;\n\n // Pending lock requests (single source of truth)\n private pendingLockRequests: Map<string, PendingLockRequest> = new Map();\n\n constructor(config: LockManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Request a distributed lock.\n */\n public requestLock(name: string, requestId: string, ttl: number): Promise<{ fencingToken: number }> {\n if (!this.config.isAuthenticated()) {\n return Promise.reject(new Error('Not connected or authenticated'));\n }\n\n return new Promise((resolve, reject) => {\n // Timeout if no response (server might be down or message lost)\n // We set a client-side timeout slightly larger than TTL if TTL is short,\n // but usually we want a separate \"Wait Timeout\".\n // For now, use a fixed 30s timeout for the *response*.\n const timer = setTimeout(() => {\n if (this.pendingLockRequests.has(requestId)) {\n this.pendingLockRequests.delete(requestId);\n reject(new Error('Lock request timed out waiting for server response'));\n }\n }, 30000);\n\n this.pendingLockRequests.set(requestId, { resolve, reject, timer });\n\n try {\n const sent = this.config.sendMessage({\n type: 'LOCK_REQUEST',\n payload: { requestId, name, ttl }\n });\n if (!sent) {\n clearTimeout(timer);\n this.pendingLockRequests.delete(requestId);\n reject(new Error('Failed to send lock request'));\n }\n } catch (e) {\n clearTimeout(timer);\n this.pendingLockRequests.delete(requestId);\n reject(e);\n }\n });\n }\n\n /**\n * Release a distributed lock.\n */\n public releaseLock(name: string, requestId: string, fencingToken: number): Promise<boolean> {\n if (!this.config.isOnline()) return Promise.resolve(false);\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pendingLockRequests.has(requestId)) {\n this.pendingLockRequests.delete(requestId);\n // Resolve false on timeout? Or reject?\n // Release is usually fire-and-forget but we wanted ACK.\n resolve(false);\n }\n }, 5000);\n\n this.pendingLockRequests.set(requestId, { resolve, reject, timer });\n\n try {\n const sent = this.config.sendMessage({\n type: 'LOCK_RELEASE',\n payload: { requestId, name, fencingToken }\n });\n if (!sent) {\n clearTimeout(timer);\n this.pendingLockRequests.delete(requestId);\n resolve(false);\n }\n } catch (e) {\n clearTimeout(timer);\n this.pendingLockRequests.delete(requestId);\n resolve(false);\n }\n });\n }\n\n /**\n * Handle lock granted message from server.\n */\n public handleLockGranted(requestId: string, _name: string, fencingToken: number): void {\n const req = this.pendingLockRequests.get(requestId);\n if (req) {\n clearTimeout(req.timer);\n this.pendingLockRequests.delete(requestId);\n req.resolve({ fencingToken });\n }\n }\n\n /**\n * Handle lock released message from server.\n */\n public handleLockReleased(requestId: string, _name: string, success: boolean): void {\n const req = this.pendingLockRequests.get(requestId);\n if (req) {\n clearTimeout(req.timer);\n this.pendingLockRequests.delete(requestId);\n req.resolve(success);\n }\n }\n}\n","/**\n * WriteConcernManager - Handles write concern tracking for SyncEngine\n *\n * Responsibilities:\n * - Register pending write concern promises for operations\n * - Resolve promises when server ACK is received\n * - Cancel all promises on disconnect\n * - Timeout handling for operations that don't receive ACK\n */\n\nimport type { IWriteConcernManager, WriteConcernManagerConfig } from './types';\n\n/**\n * Pending write concern promise state.\n */\ninterface PendingWriteConcernPromise {\n resolve: (result: any) => void;\n reject: (error: Error) => void;\n timeoutHandle?: ReturnType<typeof setTimeout>;\n}\n\n/**\n * WriteConcernManager implements IWriteConcernManager.\n *\n * Tracks write concern promises and resolves them when the server\n * sends OP_ACK with operation results.\n */\nexport class WriteConcernManager implements IWriteConcernManager {\n private readonly config: WriteConcernManagerConfig;\n\n // Pending write concern promises (single source of truth)\n private pendingWriteConcernPromises: Map<string, PendingWriteConcernPromise> = new Map();\n\n constructor(config: WriteConcernManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Register a pending Write Concern promise for an operation.\n * The promise will be resolved when the server sends an ACK with the operation result.\n *\n * @param opId - Operation ID\n * @param timeout - Timeout in ms (default: 5000)\n * @returns Promise that resolves with the Write Concern result\n */\n public registerWriteConcernPromise(opId: string, timeout: number = 5000): Promise<any> {\n const actualTimeout = timeout ?? this.config.defaultTimeout ?? 5000;\n\n return new Promise((resolve, reject) => {\n const timeoutHandle = setTimeout(() => {\n this.pendingWriteConcernPromises.delete(opId);\n reject(new Error(`Write Concern timeout for operation ${opId}`));\n }, actualTimeout);\n\n this.pendingWriteConcernPromises.set(opId, {\n resolve,\n reject,\n timeoutHandle,\n });\n });\n }\n\n /**\n * Resolve a pending Write Concern promise with the server result.\n *\n * @param opId - Operation ID\n * @param result - Result from server ACK\n */\n public resolveWriteConcernPromise(opId: string, result: any): void {\n const pending = this.pendingWriteConcernPromises.get(opId);\n if (pending) {\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n pending.resolve(result);\n this.pendingWriteConcernPromises.delete(opId);\n }\n }\n\n /**\n * Cancel all pending Write Concern promises (e.g., on disconnect).\n */\n public cancelAllWriteConcernPromises(error: Error): void {\n for (const [opId, pending] of this.pendingWriteConcernPromises.entries()) {\n if (pending.timeoutHandle) {\n clearTimeout(pending.timeoutHandle);\n }\n pending.reject(error);\n }\n this.pendingWriteConcernPromises.clear();\n }\n}\n","/**\n * CounterManager - Handles PN counter operations for SyncEngine\n *\n * Responsibilities:\n * - Counter update subscriptions\n * - Requesting initial counter state from server\n * - Syncing local counter state to server\n * - Handling incoming counter updates from server\n */\n\nimport { logger } from '../utils/logger';\nimport type { ICounterManager, CounterManagerConfig } from './types';\n\n/**\n * CounterManager implements ICounterManager.\n *\n * Manages PN counter operations with support for:\n * - Subscribe/unsubscribe to counter updates\n * - Server state synchronization\n * - State conversion between Map and object formats\n */\nexport class CounterManager implements ICounterManager {\n private readonly config: CounterManagerConfig;\n\n // Counter update listeners by name\n private counterUpdateListeners: Map<string, Set<(state: any) => void>> = new Map();\n\n constructor(config: CounterManagerConfig) {\n this.config = config;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Subscribe to counter updates from server.\n * @param name Counter name\n * @param listener Callback when counter state is updated\n * @returns Unsubscribe function\n */\n public onCounterUpdate(name: string, listener: (state: any) => void): () => void {\n if (!this.counterUpdateListeners.has(name)) {\n this.counterUpdateListeners.set(name, new Set());\n }\n this.counterUpdateListeners.get(name)!.add(listener);\n\n return () => {\n this.counterUpdateListeners.get(name)?.delete(listener);\n if (this.counterUpdateListeners.get(name)?.size === 0) {\n this.counterUpdateListeners.delete(name);\n }\n };\n }\n\n /**\n * Request initial counter state from server.\n * @param name Counter name\n */\n public requestCounter(name: string): void {\n if (this.config.isAuthenticated()) {\n this.config.sendMessage({\n type: 'COUNTER_REQUEST',\n payload: { name }\n });\n }\n }\n\n /**\n * Sync local counter state to server.\n * @param name Counter name\n * @param state Counter state to sync\n */\n public syncCounter(name: string, state: any): void {\n if (this.config.isAuthenticated()) {\n // Convert Maps to objects for serialization\n const stateObj = {\n positive: Object.fromEntries(state.positive),\n negative: Object.fromEntries(state.negative),\n };\n\n this.config.sendMessage({\n type: 'COUNTER_SYNC',\n payload: {\n name,\n state: stateObj\n }\n });\n }\n }\n\n /**\n * Handle incoming counter update from server.\n * Called by SyncEngine for COUNTER_UPDATE and COUNTER_RESPONSE messages.\n */\n public handleCounterUpdate(name: string, stateObj: { positive: Record<string, number>; negative: Record<string, number> }): void {\n // Convert objects to Maps\n const state = {\n positive: new Map(Object.entries(stateObj.positive)),\n negative: new Map(Object.entries(stateObj.negative)),\n };\n\n const listeners = this.counterUpdateListeners.get(name);\n if (listeners) {\n for (const listener of listeners) {\n try {\n listener(state);\n } catch (e) {\n logger.error({ err: e, counterName: name }, 'Counter update listener error');\n }\n }\n }\n }\n\n /**\n * Clean up resources.\n * Clears all counter update listeners.\n */\n public close(): void {\n this.counterUpdateListeners.clear();\n }\n}\n","/**\n * EntryProcessorClient - Handles entry processor operations for SyncEngine\n *\n * Responsibilities:\n * - Execute entry processors on single keys\n * - Execute entry processors on multiple keys (batch)\n * - Handle entry processor responses from server\n * - Timeout handling for requests\n * - Cleanup on close\n */\n\nimport type { EntryProcessorDef, EntryProcessorResult, EntryProcessKeyResult } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\nimport type { IEntryProcessorClient, EntryProcessorClientConfig } from './types';\n\n/**\n * Default timeout for entry processor requests (ms).\n */\nconst DEFAULT_PROCESSOR_TIMEOUT = 30000;\n\n/**\n * Pending entry processor request state.\n */\ninterface PendingProcessorRequest<R> {\n resolve: (result: EntryProcessorResult<R>) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Pending batch entry processor request state.\n */\ninterface PendingBatchProcessorRequest<R> {\n resolve: (results: Map<string, EntryProcessorResult<R>>) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/**\n * EntryProcessorClient implements IEntryProcessorClient.\n *\n * Manages entry processor operations with support for:\n * - Single key processing with atomic execution\n * - Batch processing for multiple keys\n * - Request/response pattern with timeout handling\n */\nexport class EntryProcessorClient implements IEntryProcessorClient {\n private readonly config: EntryProcessorClientConfig;\n private readonly timeoutMs: number;\n\n // Pending entry processor requests by requestId\n private pendingProcessorRequests: Map<string, PendingProcessorRequest<any>> = new Map();\n\n // Pending batch entry processor requests by requestId\n private pendingBatchProcessorRequests: Map<string, PendingBatchProcessorRequest<any>> = new Map();\n\n constructor(config: EntryProcessorClientConfig) {\n this.config = config;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_PROCESSOR_TIMEOUT;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Execute an entry processor on a single key atomically.\n *\n * @param mapName Name of the map\n * @param key Key to process\n * @param processor Processor definition\n * @returns Promise resolving to the processor result\n */\n public async executeOnKey<V, R = V>(\n mapName: string,\n key: string,\n processor: EntryProcessorDef<V, R>,\n ): Promise<EntryProcessorResult<R>> {\n if (!this.config.isAuthenticated()) {\n return {\n success: false,\n error: 'Not connected to server',\n };\n }\n\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n // Set timeout\n const timeout = setTimeout(() => {\n this.pendingProcessorRequests.delete(requestId);\n reject(new Error('Entry processor request timed out'));\n }, this.timeoutMs);\n\n // Store pending request\n this.pendingProcessorRequests.set(requestId, {\n resolve: (result) => {\n clearTimeout(timeout);\n resolve(result);\n },\n reject,\n timeout,\n });\n\n // Send request\n const sent = this.config.sendMessage({\n type: 'ENTRY_PROCESS',\n requestId,\n mapName,\n key,\n processor: {\n name: processor.name,\n code: processor.code,\n args: processor.args,\n },\n }, key);\n\n if (!sent) {\n this.pendingProcessorRequests.delete(requestId);\n clearTimeout(timeout);\n reject(new Error('Failed to send entry processor request'));\n }\n });\n }\n\n /**\n * Execute an entry processor on multiple keys.\n *\n * @param mapName Name of the map\n * @param keys Keys to process\n * @param processor Processor definition\n * @returns Promise resolving to a map of key -> result\n */\n public async executeOnKeys<V, R = V>(\n mapName: string,\n keys: string[],\n processor: EntryProcessorDef<V, R>,\n ): Promise<Map<string, EntryProcessorResult<R>>> {\n if (!this.config.isAuthenticated()) {\n const results = new Map<string, EntryProcessorResult<R>>();\n const error: EntryProcessorResult<R> = {\n success: false,\n error: 'Not connected to server',\n };\n for (const key of keys) {\n results.set(key, error);\n }\n return results;\n }\n\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n // Set timeout\n const timeout = setTimeout(() => {\n this.pendingBatchProcessorRequests.delete(requestId);\n reject(new Error('Entry processor batch request timed out'));\n }, this.timeoutMs);\n\n // Store pending request\n this.pendingBatchProcessorRequests.set(requestId, {\n resolve: (results) => {\n clearTimeout(timeout);\n resolve(results);\n },\n reject,\n timeout,\n });\n\n // Send request\n const sent = this.config.sendMessage({\n type: 'ENTRY_PROCESS_BATCH',\n requestId,\n mapName,\n keys,\n processor: {\n name: processor.name,\n code: processor.code,\n args: processor.args,\n },\n });\n\n if (!sent) {\n this.pendingBatchProcessorRequests.delete(requestId);\n clearTimeout(timeout);\n reject(new Error('Failed to send entry processor batch request'));\n }\n });\n }\n\n /**\n * Handle entry processor response from server.\n * Called by SyncEngine for ENTRY_PROCESS_RESPONSE messages.\n */\n public handleEntryProcessResponse(message: {\n requestId: string;\n success: boolean;\n result?: unknown;\n newValue?: unknown;\n error?: string;\n }): void {\n const pending = this.pendingProcessorRequests.get(message.requestId);\n if (pending) {\n this.pendingProcessorRequests.delete(message.requestId);\n pending.resolve({\n success: message.success,\n result: message.result,\n newValue: message.newValue,\n error: message.error,\n });\n }\n }\n\n /**\n * Handle entry processor batch response from server.\n * Called by SyncEngine for ENTRY_PROCESS_BATCH_RESPONSE messages.\n */\n public handleEntryProcessBatchResponse(message: {\n requestId: string;\n results: Record<string, EntryProcessKeyResult>;\n }): void {\n const pending = this.pendingBatchProcessorRequests.get(message.requestId);\n if (pending) {\n this.pendingBatchProcessorRequests.delete(message.requestId);\n\n // Convert Record to Map\n const resultsMap = new Map<string, EntryProcessorResult<any>>();\n for (const [key, result] of Object.entries(message.results)) {\n resultsMap.set(key, {\n success: result.success,\n result: result.result,\n newValue: result.newValue,\n error: result.error,\n });\n }\n\n pending.resolve(resultsMap);\n }\n }\n\n /**\n * Clean up resources.\n * Clears pending timeouts without rejecting promises to match original SyncEngine behavior.\n * Note: This may leave promises hanging, but maintains backward compatibility with tests.\n */\n public close(error?: Error): void {\n // Only clear timeouts, don't reject promises to avoid unhandled rejections in tests\n for (const [requestId, pending] of this.pendingProcessorRequests.entries()) {\n clearTimeout(pending.timeout);\n }\n this.pendingProcessorRequests.clear();\n\n for (const [requestId, pending] of this.pendingBatchProcessorRequests.entries()) {\n clearTimeout(pending.timeout);\n }\n this.pendingBatchProcessorRequests.clear();\n }\n}\n","/**\n * SearchClient - Handles full-text search operations for SyncEngine\n *\n * Responsibilities:\n * - One-shot BM25 search requests\n * - Handle search responses from server\n * - Timeout handling for requests\n * - Cleanup on close\n */\n\nimport type { SearchOptions } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\nimport type { ISearchClient, SearchClientConfig, SearchResult } from './types';\n\n/**\n * Default timeout for search requests (ms).\n */\nconst DEFAULT_SEARCH_TIMEOUT = 30000;\n\n/**\n * Pending search request state.\n */\ninterface PendingSearchRequest {\n resolve: (result: SearchResult<unknown>[]) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\n/**\n * SearchClient implements ISearchClient.\n *\n * Manages full-text search operations with support for:\n * - One-shot BM25 search with configurable options\n * - Request/response pattern with timeout handling\n */\nexport class SearchClient implements ISearchClient {\n private readonly config: SearchClientConfig;\n private readonly timeoutMs: number;\n\n // Pending search requests by requestId\n private pendingSearchRequests: Map<string, PendingSearchRequest> = new Map();\n\n constructor(config: SearchClientConfig) {\n this.config = config;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_SEARCH_TIMEOUT;\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Perform a one-shot BM25 search on the server.\n *\n * @param mapName Name of the map to search\n * @param query Search query text\n * @param options Search options (limit, minScore, boost)\n * @returns Promise resolving to search results\n */\n public async search<T>(\n mapName: string,\n query: string,\n options?: SearchOptions\n ): Promise<SearchResult<T>[]> {\n if (!this.config.isAuthenticated()) {\n throw new Error('Not connected to server');\n }\n\n const requestId = crypto.randomUUID();\n\n return new Promise((resolve, reject) => {\n // Set timeout\n const timeout = setTimeout(() => {\n this.pendingSearchRequests.delete(requestId);\n reject(new Error('Search request timed out'));\n }, this.timeoutMs);\n\n // Store pending request\n this.pendingSearchRequests.set(requestId, {\n resolve: (results) => {\n clearTimeout(timeout);\n resolve(results as SearchResult<T>[]);\n },\n reject: (error) => {\n clearTimeout(timeout);\n reject(error);\n },\n timeout,\n });\n\n // Send search request\n const sent = this.config.sendMessage({\n type: 'SEARCH',\n payload: {\n requestId,\n mapName,\n query,\n options,\n },\n });\n\n if (!sent) {\n this.pendingSearchRequests.delete(requestId);\n clearTimeout(timeout);\n reject(new Error('Failed to send search request'));\n }\n });\n }\n\n /**\n * Handle search response from server.\n * Called by SyncEngine for SEARCH_RESP messages.\n */\n public handleSearchResponse(payload: {\n requestId: string;\n results: SearchResult<unknown>[];\n totalCount: number;\n error?: string;\n }): void {\n const pending = this.pendingSearchRequests.get(payload.requestId);\n if (pending) {\n this.pendingSearchRequests.delete(payload.requestId);\n\n if (payload.error) {\n pending.reject(new Error(payload.error));\n } else {\n pending.resolve(payload.results);\n }\n }\n }\n\n /**\n * Clean up resources.\n * Clears pending timeouts without rejecting promises to match original SyncEngine behavior.\n * Note: This may leave promises hanging, but maintains backward compatibility with tests.\n */\n public close(error?: Error): void {\n // Only clear timeouts, don't reject promises to avoid unhandled rejections in tests\n for (const [requestId, pending] of this.pendingSearchRequests.entries()) {\n clearTimeout(pending.timeout);\n }\n this.pendingSearchRequests.clear();\n }\n}\n","import { LWWMap } from '@topgunbuild/core';\nimport type { IMerkleSyncHandler, MerkleSyncHandlerConfig } from './types';\nimport { logger } from '../utils/logger';\n\n/**\n * MerkleSyncHandler\n *\n * Handles Merkle tree synchronization protocol messages for LWWMap.\n * Manages sync state, root hash comparison, bucket traversal, and leaf merging.\n */\nexport class MerkleSyncHandler implements IMerkleSyncHandler {\n private readonly config: MerkleSyncHandlerConfig;\n private lastSyncTimestamp: number = 0;\n /** Accumulated sync stats per map, flushed after a quiet period */\n private syncStats = new Map<string, { count: number; timer: ReturnType<typeof setTimeout> }>();\n\n constructor(config: MerkleSyncHandlerConfig) {\n this.config = config;\n }\n\n /**\n * Handle SYNC_RESET_REQUIRED message from server.\n * Resets the map and triggers a fresh sync.\n */\n public async handleSyncResetRequired(payload: { mapName: string }): Promise<void> {\n const { mapName } = payload;\n logger.warn({ mapName }, 'Sync Reset Required due to GC Age');\n await this.config.resetMap(mapName);\n // Trigger re-sync as fresh\n this.config.sendMessage({\n type: 'SYNC_INIT',\n mapName,\n lastSyncTimestamp: 0\n });\n }\n\n /**\n * Handle SYNC_RESP_ROOT message from server.\n * Compares root hashes and requests buckets if mismatch detected.\n */\n public async handleSyncRespRoot(payload: { mapName: string; rootHash: number; timestamp?: any }): Promise<void> {\n const { mapName, timestamp } = payload;\n // Coerce BigInt — Rust server sends u64 hashes which MsgPack decodes as BigInt\n const rootHash = Number(payload.rootHash);\n const map = this.config.getMap(mapName);\n if (map instanceof LWWMap) {\n const localRootHash = map.getMerkleTree().getRootHash();\n if (localRootHash !== rootHash) {\n logger.info({ mapName, localRootHash, remoteRootHash: rootHash }, 'Root hash mismatch, requesting buckets');\n this.config.sendMessage({\n type: 'MERKLE_REQ_BUCKET',\n payload: { mapName, path: '' }\n });\n } else {\n logger.info({ mapName }, 'Map is in sync');\n }\n }\n // Update HLC with server timestamp\n if (timestamp) {\n await this.config.onTimestampUpdate(timestamp);\n }\n }\n\n /**\n * Handle SYNC_RESP_BUCKETS message from server.\n * Compares bucket hashes and requests mismatched buckets.\n */\n public handleSyncRespBuckets(payload: { mapName: string; path: string; buckets: Record<string, number> }): void {\n const { mapName, path, buckets } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof LWWMap) {\n const tree = map.getMerkleTree();\n const localBuckets = tree.getBuckets(path);\n let mismatchCount = 0;\n\n for (const [bucketKey, remoteHash] of Object.entries(buckets)) {\n const localHash = localBuckets[bucketKey] || 0;\n if (localHash !== remoteHash) {\n mismatchCount++;\n const newPath = path + bucketKey;\n this.config.sendMessage({\n type: 'MERKLE_REQ_BUCKET',\n payload: { mapName, path: newPath }\n });\n }\n }\n }\n }\n\n /**\n * Handle SYNC_RESP_LEAF message from server.\n * Merges leaf records into local map and persists to storage.\n */\n public async handleSyncRespLeaf(payload: { mapName: string; records: Array<{ key: string; record: any }> }): Promise<void> {\n const { mapName, records } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof LWWMap) {\n let updateCount = 0;\n for (const { key, record } of records) {\n // Merge into local map\n const updated = map.merge(key, record);\n if (updated) {\n updateCount++;\n // Persist to storage\n await this.config.storageAdapter.put(`${mapName}:${key}`, record);\n }\n }\n if (updateCount > 0) {\n // Aggregate sync stats and flush after a quiet period to avoid per-leaf log spam\n const existing = this.syncStats.get(mapName);\n if (existing) {\n existing.count += updateCount;\n clearTimeout(existing.timer);\n }\n const stats = existing ?? { count: updateCount, timer: undefined as any };\n if (!existing) this.syncStats.set(mapName, stats);\n stats.timer = setTimeout(() => {\n logger.info({ mapName, count: stats.count }, 'Synced records from server');\n this.syncStats.delete(mapName);\n }, 100);\n }\n }\n }\n\n /**\n * Send SYNC_INIT message to server to start sync.\n * Encapsulates sync init message construction.\n */\n public sendSyncInit(mapName: string, lastSyncTimestamp: number): void {\n this.lastSyncTimestamp = lastSyncTimestamp;\n logger.info({ mapName }, 'Starting Merkle sync for LWWMap');\n this.config.sendMessage({\n type: 'SYNC_INIT',\n mapName,\n lastSyncTimestamp\n });\n }\n\n /**\n * Get the last sync timestamp for debugging/testing.\n */\n public getLastSyncTimestamp(): number {\n return this.lastSyncTimestamp;\n }\n}\n","import { ORMap } from '@topgunbuild/core';\nimport type { ORMapRecord } from '@topgunbuild/core';\nimport type { IORMapSyncHandler, ORMapSyncHandlerConfig } from './types';\nimport { logger } from '../utils/logger';\n\n/**\n * ORMapSyncHandler\n *\n * Handles Merkle tree synchronization protocol messages for ORMap.\n * Manages sync state, root hash comparison, bucket traversal, leaf merging,\n * and bidirectional diff exchange.\n */\nexport class ORMapSyncHandler implements IORMapSyncHandler {\n private readonly config: ORMapSyncHandlerConfig;\n private lastSyncTimestamp: number = 0;\n\n constructor(config: ORMapSyncHandlerConfig) {\n this.config = config;\n }\n\n /**\n * Handle ORMAP_SYNC_RESP_ROOT message from server.\n * Compares root hashes and requests buckets if mismatch detected.\n */\n public async handleORMapSyncRespRoot(payload: { mapName: string; rootHash: number; timestamp?: any }): Promise<void> {\n const { mapName, rootHash, timestamp } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n const localTree = map.getMerkleTree();\n const localRootHash = localTree.getRootHash();\n\n if (localRootHash !== rootHash) {\n logger.info({ mapName, localRootHash, remoteRootHash: rootHash }, 'ORMap root hash mismatch, requesting buckets');\n this.config.sendMessage({\n type: 'ORMAP_MERKLE_REQ_BUCKET',\n payload: { mapName, path: '' }\n });\n } else {\n logger.info({ mapName }, 'ORMap is in sync');\n }\n }\n // Update HLC with server timestamp\n if (timestamp) {\n await this.config.onTimestampUpdate(timestamp);\n }\n }\n\n /**\n * Handle ORMAP_SYNC_RESP_BUCKETS message from server.\n * Compares bucket hashes and requests mismatched buckets.\n * Also pushes local data that server doesn't have.\n */\n public async handleORMapSyncRespBuckets(payload: { mapName: string; path: string; buckets: Record<string, number> }): Promise<void> {\n const { mapName, path, buckets } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n const tree = map.getMerkleTree();\n const localBuckets = tree.getBuckets(path);\n\n for (const [bucketKey, remoteHash] of Object.entries(buckets)) {\n const localHash = localBuckets[bucketKey] || 0;\n if (localHash !== remoteHash) {\n const newPath = path + bucketKey;\n this.config.sendMessage({\n type: 'ORMAP_MERKLE_REQ_BUCKET',\n payload: { mapName, path: newPath }\n });\n }\n }\n\n // Also check for buckets that exist locally but not on remote\n for (const [bucketKey, localHash] of Object.entries(localBuckets)) {\n if (!(bucketKey in buckets) && localHash !== 0) {\n // Local has data that remote doesn't - need to push\n const newPath = path + bucketKey;\n const keys = tree.getKeysInBucket(newPath);\n if (keys.length > 0) {\n await this.pushORMapDiff(mapName, keys, map);\n }\n }\n }\n }\n }\n\n /**\n * Handle ORMAP_SYNC_RESP_LEAF message from server.\n * Merges leaf entries into local map and pushes local diff back.\n */\n public async handleORMapSyncRespLeaf(payload: { mapName: string; entries: Array<{ key: string; records: any[]; tombstones: string[] }> }): Promise<void> {\n const { mapName, entries } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n let totalAdded = 0;\n let totalUpdated = 0;\n\n for (const entry of entries) {\n const { key, records, tombstones } = entry;\n const result = map.mergeKey(key, records, tombstones);\n totalAdded += result.added;\n totalUpdated += result.updated;\n }\n\n if (totalAdded > 0 || totalUpdated > 0) {\n logger.info({ mapName, added: totalAdded, updated: totalUpdated }, 'Synced ORMap records from server');\n }\n\n // Now push any local records that server might not have\n const keysToCheck = entries.map((e: { key: string }) => e.key);\n await this.pushORMapDiff(mapName, keysToCheck, map);\n }\n }\n\n /**\n * Handle ORMAP_DIFF_RESPONSE message from server.\n * Merges diff entries into local map.\n */\n public async handleORMapDiffResponse(payload: { mapName: string; entries: Array<{ key: string; records: any[]; tombstones: string[] }> }): Promise<void> {\n const { mapName, entries } = payload;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n let totalAdded = 0;\n let totalUpdated = 0;\n\n for (const entry of entries) {\n const { key, records, tombstones } = entry;\n const result = map.mergeKey(key, records, tombstones);\n totalAdded += result.added;\n totalUpdated += result.updated;\n }\n\n if (totalAdded > 0 || totalUpdated > 0) {\n logger.info({ mapName, added: totalAdded, updated: totalUpdated }, 'Merged ORMap diff from server');\n }\n }\n }\n\n /**\n * Push local ORMap diff to server for the given keys.\n * Sends local records and tombstones that the server might not have.\n */\n public async pushORMapDiff(\n mapName: string,\n keys: string[],\n map: ORMap<any, any>\n ): Promise<void> {\n const entries: Array<{\n key: string;\n records: ORMapRecord<any>[];\n tombstones: string[];\n }> = [];\n\n const snapshot = map.getSnapshot();\n\n for (const key of keys) {\n const recordsMap = map.getRecordsMap(key);\n if (recordsMap && recordsMap.size > 0) {\n // Get records as array\n const records = Array.from(recordsMap.values());\n\n // Get tombstones relevant to this key's records\n // (tombstones that match tags that were in this key)\n const tombstones: string[] = [];\n for (const tag of snapshot.tombstones) {\n // Include all tombstones - server will filter\n tombstones.push(tag);\n }\n\n entries.push({\n key,\n records,\n tombstones\n });\n }\n }\n\n if (entries.length > 0) {\n this.config.sendMessage({\n type: 'ORMAP_PUSH_DIFF',\n payload: {\n mapName,\n entries\n }\n });\n logger.debug({ mapName, keyCount: entries.length }, 'Pushed ORMap diff to server');\n }\n }\n\n /**\n * Send ORMAP_SYNC_INIT message to server to start sync.\n * Encapsulates sync init message construction.\n */\n public sendSyncInit(mapName: string, lastSyncTimestamp: number): void {\n this.lastSyncTimestamp = lastSyncTimestamp;\n const map = this.config.getMap(mapName);\n if (map instanceof ORMap) {\n logger.info({ mapName }, 'Starting Merkle sync for ORMap');\n const tree = map.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n // Build bucket hashes for all non-empty buckets at depth 0\n const bucketHashes: Record<string, number> = tree.getBuckets('');\n\n this.config.sendMessage({\n type: 'ORMAP_SYNC_INIT',\n mapName,\n rootHash,\n bucketHashes,\n lastSyncTimestamp\n });\n }\n }\n\n /**\n * Get the last sync timestamp for debugging/testing.\n */\n public getLastSyncTimestamp(): number {\n return this.lastSyncTimestamp;\n }\n}\n","/**\n * MessageRouter - Routes incoming server messages to appropriate handlers.\n *\n * This module replaces the large switch statement in SyncEngine.handleServerMessage()\n * with a declarative, type-based routing system.\n *\n * Features:\n * - Register handlers for message types\n * - Route incoming messages to appropriate handlers\n * - Support async handlers\n * - Handle unregistered message types via fallback\n */\n\nimport { logger } from '../utils/logger';\nimport type { IMessageRouter, MessageHandler, MessageRouterConfig } from './types';\n\n/**\n * MessageRouter implementation.\n * Routes server messages to registered handlers based on message type.\n */\nexport class MessageRouter implements IMessageRouter {\n private readonly handlers: Map<string, MessageHandler>;\n private readonly onUnhandled?: (message: any) => void;\n\n constructor(config: MessageRouterConfig = {}) {\n // Copy handlers from config or create new Map\n this.handlers = config.handlers\n ? new Map(config.handlers)\n : new Map();\n this.onUnhandled = config.onUnhandled;\n }\n\n /**\n * Register a handler for a message type.\n * @param type - Message type to handle\n * @param handler - Handler function\n */\n registerHandler(type: string, handler: MessageHandler): void {\n if (this.handlers.has(type)) {\n logger.warn({ type }, 'Overwriting existing handler for message type');\n }\n this.handlers.set(type, handler);\n }\n\n /**\n * Register multiple handlers at once.\n * @param handlers - Record of type -> handler\n */\n registerHandlers(handlers: Record<string, MessageHandler>): void {\n for (const [type, handler] of Object.entries(handlers)) {\n this.registerHandler(type, handler);\n }\n }\n\n /**\n * Route a message to its registered handler.\n * Returns true if handled, false if no handler found.\n * @param message - Message to route\n * @returns Promise resolving to true if handled\n */\n async route(message: any): Promise<boolean> {\n const type = message?.type;\n if (!type) {\n logger.warn({ message }, 'Cannot route message without type');\n return false;\n }\n\n const handler = this.handlers.get(type);\n if (!handler) {\n // Call fallback if provided\n if (this.onUnhandled) {\n this.onUnhandled(message);\n }\n return false;\n }\n\n try {\n // Await in case handler is async\n await handler(message);\n return true;\n } catch (error) {\n logger.error({ type, error }, 'Error in message handler');\n // Still return true since handler was found (just errored)\n return true;\n }\n }\n\n /**\n * Check if a handler is registered for a message type.\n * @param type - Message type to check\n * @returns true if handler exists\n */\n hasHandler(type: string): boolean {\n return this.handlers.has(type);\n }\n\n /**\n * Get the count of registered handlers.\n * Useful for debugging/testing.\n */\n get handlerCount(): number {\n return this.handlers.size;\n }\n\n /**\n * Get all registered message types.\n * Useful for debugging/testing.\n */\n getRegisteredTypes(): string[] {\n return Array.from(this.handlers.keys());\n }\n}\n","/**\n * Client message handler registration.\n * Configures all message type -> handler mappings for SyncEngine.\n */\n\nimport type { IMessageRouter } from './types';\nimport type {\n AuthFailMessage,\n OpAckMessage,\n OpRejectedMessage,\n ErrorMessage,\n QueryRespMessage,\n QueryUpdateMessage,\n ServerEventMessage,\n ServerBatchEventMessage,\n GcPruneMessage,\n SearchRespPayload,\n SyncRespRootPayload,\n SyncRespBucketsPayload,\n SyncRespLeafPayload,\n SyncResetRequiredPayload,\n ORMapSyncRespRootPayload,\n ORMapSyncRespBucketsPayload,\n ORMapSyncRespLeafPayload,\n ORMapDiffResponsePayload,\n EntryProcessResponse,\n EntryProcessBatchResponse,\n RegisterResolverResponse,\n UnregisterResolverResponse,\n ListResolversResponse,\n MergeRejectedMessage,\n} from '@topgunbuild/core';\n\n/**\n * SyncEngine methods used as handler delegates.\n * These methods are called directly from message handlers.\n */\nexport interface MessageHandlerDelegates {\n sendAuth(): Promise<void>;\n handleAuthAck(): void;\n handleAuthFail(message: AuthFailMessage): void;\n handleOpAck(message: OpAckMessage): void;\n handleOpRejected(message: OpRejectedMessage): void;\n handleError(message: ErrorMessage): void;\n handleQueryResp(message: QueryRespMessage): void;\n handleQueryUpdate(message: QueryUpdateMessage): void;\n handleServerEvent(message: ServerEventMessage): Promise<void>;\n handleServerBatchEvent(message: ServerBatchEventMessage): Promise<void>;\n handleGcPrune(message: GcPruneMessage): Promise<void>;\n}\n\n/**\n * Manager instances that receive delegated message handling.\n */\nexport interface ManagerDelegates {\n topicManager: {\n handleTopicMessage(topic: string, data: unknown, publisherId: string, timestamp: number): void;\n };\n lockManager: {\n handleLockGranted(requestId: string, name: string, fencingToken: number): void;\n handleLockReleased(requestId: string, name: string, success: boolean): void;\n };\n counterManager: {\n handleCounterUpdate(name: string, state: { positive: Record<string, number>; negative: Record<string, number> }): void;\n };\n entryProcessorClient: {\n handleEntryProcessResponse(message: EntryProcessResponse): void;\n handleEntryProcessBatchResponse(message: EntryProcessBatchResponse): void;\n };\n conflictResolverClient: {\n handleRegisterResponse(message: RegisterResolverResponse): void;\n handleUnregisterResponse(message: UnregisterResolverResponse): void;\n handleListResponse(message: ListResolversResponse): void;\n handleMergeRejected(message: MergeRejectedMessage): void;\n };\n searchClient: {\n handleSearchResponse(payload: SearchRespPayload): void;\n };\n merkleSyncHandler: {\n handleSyncRespRoot(payload: SyncRespRootPayload): void;\n handleSyncRespBuckets(payload: SyncRespBucketsPayload): void;\n handleSyncRespLeaf(payload: SyncRespLeafPayload): void;\n handleSyncResetRequired(payload: SyncResetRequiredPayload): void;\n };\n orMapSyncHandler: {\n handleORMapSyncRespRoot(payload: ORMapSyncRespRootPayload): void;\n handleORMapSyncRespBuckets(payload: ORMapSyncRespBucketsPayload): void;\n handleORMapSyncRespLeaf(payload: ORMapSyncRespLeafPayload): void;\n handleORMapDiffResponse(payload: ORMapDiffResponsePayload): void;\n };\n}\n\n/**\n * All expected client message types.\n * Used for testing that all types are registered.\n */\nexport const CLIENT_MESSAGE_TYPES = [\n 'AUTH_REQUIRED', 'AUTH_ACK', 'AUTH_FAIL',\n 'PONG',\n 'OP_ACK', 'OP_REJECTED', 'ERROR',\n 'SYNC_RESP_ROOT', 'SYNC_RESP_BUCKETS', 'SYNC_RESP_LEAF', 'SYNC_RESET_REQUIRED',\n 'ORMAP_SYNC_RESP_ROOT', 'ORMAP_SYNC_RESP_BUCKETS', 'ORMAP_SYNC_RESP_LEAF', 'ORMAP_DIFF_RESPONSE',\n 'QUERY_RESP', 'QUERY_UPDATE',\n 'SERVER_EVENT', 'SERVER_BATCH_EVENT',\n 'TOPIC_MESSAGE',\n 'LOCK_GRANTED', 'LOCK_RELEASED',\n 'GC_PRUNE',\n 'COUNTER_UPDATE', 'COUNTER_RESPONSE',\n 'ENTRY_PROCESS_RESPONSE', 'ENTRY_PROCESS_BATCH_RESPONSE',\n 'REGISTER_RESOLVER_RESPONSE', 'UNREGISTER_RESOLVER_RESPONSE', 'LIST_RESOLVERS_RESPONSE', 'MERGE_REJECTED',\n 'SEARCH_RESP', 'SEARCH_UPDATE',\n] as const;\n\n/**\n * Register all client message handlers with the router.\n *\n * @param router - MessageRouter instance\n * @param delegates - SyncEngine handler methods\n * @param managers - Manager instances for delegation\n */\nexport function registerClientMessageHandlers(\n router: IMessageRouter,\n delegates: MessageHandlerDelegates,\n managers: ManagerDelegates\n): void {\n router.registerHandlers({\n // AUTH handlers\n 'AUTH_REQUIRED': () => delegates.sendAuth(),\n 'AUTH_ACK': () => delegates.handleAuthAck(),\n 'AUTH_FAIL': (msg) => delegates.handleAuthFail(msg),\n\n // HEARTBEAT - handled by WebSocketManager, no-op here\n 'PONG': () => {},\n\n // SYNC handlers\n 'OP_ACK': (msg) => delegates.handleOpAck(msg),\n 'OP_REJECTED': (msg) => delegates.handleOpRejected(msg),\n 'ERROR': (msg) => delegates.handleError(msg),\n 'SYNC_RESP_ROOT': (msg) => managers.merkleSyncHandler.handleSyncRespRoot(msg.payload),\n 'SYNC_RESP_BUCKETS': (msg) => managers.merkleSyncHandler.handleSyncRespBuckets(msg.payload),\n 'SYNC_RESP_LEAF': (msg) => managers.merkleSyncHandler.handleSyncRespLeaf(msg.payload),\n 'SYNC_RESET_REQUIRED': (msg) => managers.merkleSyncHandler.handleSyncResetRequired(msg.payload),\n\n // ORMAP SYNC handlers\n 'ORMAP_SYNC_RESP_ROOT': (msg) => managers.orMapSyncHandler.handleORMapSyncRespRoot(msg.payload),\n 'ORMAP_SYNC_RESP_BUCKETS': (msg) => managers.orMapSyncHandler.handleORMapSyncRespBuckets(msg.payload),\n 'ORMAP_SYNC_RESP_LEAF': (msg) => managers.orMapSyncHandler.handleORMapSyncRespLeaf(msg.payload),\n 'ORMAP_DIFF_RESPONSE': (msg) => managers.orMapSyncHandler.handleORMapDiffResponse(msg.payload),\n\n // QUERY handlers\n 'QUERY_RESP': (msg) => delegates.handleQueryResp(msg),\n 'QUERY_UPDATE': (msg) => delegates.handleQueryUpdate(msg),\n\n // EVENT handlers\n 'SERVER_EVENT': (msg) => delegates.handleServerEvent(msg),\n 'SERVER_BATCH_EVENT': (msg) => delegates.handleServerBatchEvent(msg),\n\n // TOPIC handlers\n 'TOPIC_MESSAGE': (msg) => {\n const { topic, data, publisherId, timestamp } = msg.payload;\n managers.topicManager.handleTopicMessage(topic, data, publisherId, timestamp);\n },\n\n // LOCK handlers\n 'LOCK_GRANTED': (msg) => {\n const { requestId, name, fencingToken } = msg.payload;\n managers.lockManager.handleLockGranted(requestId, name, fencingToken);\n },\n 'LOCK_RELEASED': (msg) => {\n const { requestId, name, success } = msg.payload;\n managers.lockManager.handleLockReleased(requestId, name, success);\n },\n\n // GC handler\n 'GC_PRUNE': (msg) => delegates.handleGcPrune(msg),\n\n // COUNTER handlers\n 'COUNTER_UPDATE': (msg) => {\n const { name, state } = msg.payload;\n managers.counterManager.handleCounterUpdate(name, state);\n },\n 'COUNTER_RESPONSE': (msg) => {\n const { name, state } = msg.payload;\n managers.counterManager.handleCounterUpdate(name, state);\n },\n\n // PROCESSOR handlers\n 'ENTRY_PROCESS_RESPONSE': (msg) => {\n managers.entryProcessorClient.handleEntryProcessResponse(msg);\n },\n 'ENTRY_PROCESS_BATCH_RESPONSE': (msg) => {\n managers.entryProcessorClient.handleEntryProcessBatchResponse(msg);\n },\n\n // RESOLVER handlers\n 'REGISTER_RESOLVER_RESPONSE': (msg) => {\n managers.conflictResolverClient.handleRegisterResponse(msg);\n },\n 'UNREGISTER_RESOLVER_RESPONSE': (msg) => {\n managers.conflictResolverClient.handleUnregisterResponse(msg);\n },\n 'LIST_RESOLVERS_RESPONSE': (msg) => {\n managers.conflictResolverClient.handleListResponse(msg);\n },\n 'MERGE_REJECTED': (msg) => {\n managers.conflictResolverClient.handleMergeRejected(msg);\n },\n\n // SEARCH handlers\n 'SEARCH_RESP': (msg) => {\n managers.searchClient.handleSearchResponse(msg.payload);\n },\n 'SEARCH_UPDATE': () => {\n // SEARCH_UPDATE is handled by SearchHandle via emitMessage, no-op here\n },\n });\n}\n","import { LWWMap, ORMap } from '@topgunbuild/core';\nimport type { ORMapRecord, LWWRecord, EntryProcessorDef, EntryProcessorResult, SearchOptions } from '@topgunbuild/core';\nimport type { IStorageAdapter } from './IStorageAdapter';\nimport { SyncEngine } from './SyncEngine';\nimport type { BackoffConfig } from './SyncEngine';\nimport { QueryHandle } from './QueryHandle';\nimport type { QueryFilter } from './QueryHandle';\nimport { DistributedLock } from './DistributedLock';\nimport { TopicHandle } from './TopicHandle';\nimport { PNCounterHandle } from './PNCounterHandle';\nimport { EventJournalReader } from './EventJournalReader';\nimport { SearchHandle } from './SearchHandle';\nimport { HybridQueryHandle } from './HybridQueryHandle';\nimport type { HybridQueryFilter } from './HybridQueryHandle';\nimport { logger } from './utils/logger';\nimport { SyncState } from './SyncState';\nimport type { StateChangeEvent } from './SyncStateMachine';\nimport type {\n BackpressureConfig,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n} from './BackpressureConfig';\nimport { ClusterClient } from './cluster/ClusterClient';\nimport { SingleServerProvider } from './connection/SingleServerProvider';\nimport type { NodeHealth } from '@topgunbuild/core';\n\n// ============================================\n// Cluster Configuration Types\n// ============================================\n\n/**\n * Cluster mode configuration for TopGunClient.\n * When provided, the client connects to multiple nodes with partition-aware routing.\n */\nexport interface TopGunClusterConfig {\n /** Initial seed nodes (at least one required) */\n seeds: string[];\n\n /** Connection pool size per node (default: 1) */\n connectionsPerNode?: number;\n\n /** Enable smart routing to partition owner (default: true) */\n smartRouting?: boolean;\n\n /** Partition map refresh interval in ms (default: 30000) */\n partitionMapRefreshMs?: number;\n\n /** Connection timeout per node in ms (default: 5000) */\n connectionTimeoutMs?: number;\n\n /** Retry attempts for failed operations (default: 3) */\n retryAttempts?: number;\n}\n\n/**\n * Default values for cluster configuration\n */\nexport const DEFAULT_CLUSTER_CONFIG: Required<Omit<TopGunClusterConfig, 'seeds'>> = {\n connectionsPerNode: 1,\n smartRouting: true,\n partitionMapRefreshMs: 30000,\n connectionTimeoutMs: 5000,\n retryAttempts: 3,\n};\n\n/**\n * TopGunClient configuration options\n */\nexport interface TopGunClientConfig {\n /** Unique node identifier (auto-generated if not provided) */\n nodeId?: string;\n\n /** Single-server mode: WebSocket URL to connect to */\n serverUrl?: string;\n\n /** Cluster mode: Configuration for multi-node routing */\n cluster?: TopGunClusterConfig;\n\n /** Storage adapter for local persistence */\n storage: IStorageAdapter;\n\n /** Backoff configuration for reconnection */\n backoff?: Partial<BackoffConfig>;\n\n /** Backpressure configuration */\n backpressure?: Partial<BackpressureConfig>;\n}\n\nexport class TopGunClient {\n private readonly nodeId: string;\n private readonly syncEngine: SyncEngine;\n private readonly maps: Map<string, LWWMap<any, any> | ORMap<any, any>> = new Map();\n private readonly storageAdapter: IStorageAdapter;\n private readonly topicHandles: Map<string, TopicHandle> = new Map();\n private readonly counters: Map<string, PNCounterHandle> = new Map();\n private readonly clusterClient?: ClusterClient;\n private readonly isClusterMode: boolean;\n private readonly clusterConfig?: Required<Omit<TopGunClusterConfig, 'seeds'>> & { seeds: string[] };\n\n constructor(config: TopGunClientConfig) {\n // Validate: either serverUrl or cluster, not both\n if (config.serverUrl && config.cluster) {\n throw new Error('Cannot specify both serverUrl and cluster config');\n }\n if (!config.serverUrl && !config.cluster) {\n throw new Error('Must specify either serverUrl or cluster config');\n }\n\n this.nodeId = config.nodeId || crypto.randomUUID();\n this.storageAdapter = config.storage;\n this.isClusterMode = !!config.cluster;\n\n if (config.cluster) {\n // Validate cluster seeds\n if (!config.cluster.seeds || config.cluster.seeds.length === 0) {\n throw new Error('Cluster config requires at least one seed node');\n }\n\n // Merge with defaults\n this.clusterConfig = {\n seeds: config.cluster.seeds,\n connectionsPerNode: config.cluster.connectionsPerNode ?? DEFAULT_CLUSTER_CONFIG.connectionsPerNode,\n smartRouting: config.cluster.smartRouting ?? DEFAULT_CLUSTER_CONFIG.smartRouting,\n partitionMapRefreshMs: config.cluster.partitionMapRefreshMs ?? DEFAULT_CLUSTER_CONFIG.partitionMapRefreshMs,\n connectionTimeoutMs: config.cluster.connectionTimeoutMs ?? DEFAULT_CLUSTER_CONFIG.connectionTimeoutMs,\n retryAttempts: config.cluster.retryAttempts ?? DEFAULT_CLUSTER_CONFIG.retryAttempts,\n };\n\n // Initialize cluster mode\n this.clusterClient = new ClusterClient({\n enabled: true,\n seedNodes: this.clusterConfig.seeds,\n routingMode: this.clusterConfig.smartRouting ? 'direct' : 'forward',\n connectionPool: {\n maxConnectionsPerNode: this.clusterConfig.connectionsPerNode,\n connectionTimeoutMs: this.clusterConfig.connectionTimeoutMs,\n },\n routing: {\n mapRefreshIntervalMs: this.clusterConfig.partitionMapRefreshMs,\n },\n });\n\n // SyncEngine uses ClusterClient as connectionProvider for partition-aware routing\n this.syncEngine = new SyncEngine({\n nodeId: this.nodeId,\n connectionProvider: this.clusterClient,\n storageAdapter: this.storageAdapter,\n backoff: config.backoff,\n backpressure: config.backpressure,\n });\n\n logger.info({ seeds: this.clusterConfig.seeds }, 'TopGunClient initialized in cluster mode');\n } else {\n // Single-server mode: create SingleServerProvider from serverUrl\n // Map BackoffConfig to SingleServerProviderConfig for unified retry behavior\n const singleServerProvider = new SingleServerProvider({\n url: config.serverUrl!,\n maxReconnectAttempts: config.backoff?.maxRetries,\n reconnectDelayMs: config.backoff?.initialDelayMs,\n backoffMultiplier: config.backoff?.multiplier,\n maxReconnectDelayMs: config.backoff?.maxDelayMs,\n });\n\n this.syncEngine = new SyncEngine({\n nodeId: this.nodeId,\n connectionProvider: singleServerProvider,\n storageAdapter: this.storageAdapter,\n backoff: config.backoff,\n backpressure: config.backpressure,\n });\n\n logger.info({ serverUrl: config.serverUrl }, 'TopGunClient initialized in single-server mode');\n }\n }\n\n public async start(): Promise<void> {\n await this.storageAdapter.initialize('topgun_offline_db');\n // this.syncEngine.start();\n }\n\n public setAuthToken(token: string): void {\n this.syncEngine.setAuthToken(token);\n }\n\n public setAuthTokenProvider(provider: () => Promise<string | null>): void {\n this.syncEngine.setTokenProvider(provider);\n }\n\n /**\n * Creates a live query subscription for a map.\n */\n public query<T>(mapName: string, filter: QueryFilter): QueryHandle<T> {\n return new QueryHandle<T>(this.syncEngine, mapName, filter);\n }\n\n /**\n * Retrieves a distributed lock instance.\n * @param name The name of the lock.\n */\n public getLock(name: string): DistributedLock {\n return new DistributedLock(this.syncEngine, name);\n }\n\n /**\n * Retrieves a topic handle for Pub/Sub messaging.\n * @param name The name of the topic.\n */\n public topic(name: string): TopicHandle {\n if (!this.topicHandles.has(name)) {\n this.topicHandles.set(name, new TopicHandle(this.syncEngine, name));\n }\n return this.topicHandles.get(name)!;\n }\n\n /**\n * Retrieves a PN Counter instance. If the counter doesn't exist locally, it's created.\n * PN Counters support increment and decrement operations that work offline\n * and sync to server when connected.\n *\n * @param name The name of the counter (e.g., 'likes:post-123')\n * @returns A PNCounterHandle instance\n *\n * @example\n * ```typescript\n * const likes = client.getPNCounter('likes:post-123');\n * likes.increment(); // +1\n * likes.decrement(); // -1\n * likes.addAndGet(10); // +10\n *\n * likes.subscribe((value) => {\n * console.log('Current likes:', value);\n * });\n * ```\n */\n public getPNCounter(name: string): PNCounterHandle {\n let counter = this.counters.get(name);\n if (!counter) {\n counter = new PNCounterHandle(name, this.nodeId, this.syncEngine, this.storageAdapter);\n this.counters.set(name, counter);\n }\n return counter;\n }\n\n /**\n * Retrieves an LWWMap instance. If the map doesn't exist locally, it's created.\n * @param name The name of the map.\n * @returns An LWWMap instance.\n */\n public getMap<K, V>(name: string): LWWMap<K, V> {\n if (this.maps.has(name)) {\n const map = this.maps.get(name);\n if (map instanceof LWWMap) {\n return map as LWWMap<K, V>;\n }\n throw new Error(`Map ${name} exists but is not an LWWMap`);\n }\n\n const lwwMap = new LWWMap<K, V>(this.syncEngine.getHLC());\n this.maps.set(name, lwwMap);\n this.syncEngine.registerMap(name, lwwMap);\n\n // Restore state from storage asynchronously\n this.storageAdapter.getAllKeys().then(async (keys) => {\n const mapPrefix = `${name}:`;\n for (const fullKey of keys) {\n if (fullKey.startsWith(mapPrefix)) {\n const record = await this.storageAdapter.get(fullKey);\n if (record && (record as LWWRecord<V>).timestamp && !(record as any).tag) {\n // Strip prefix to get actual key\n const key = fullKey.substring(mapPrefix.length) as unknown as K;\n // Merge into in-memory map without triggering new ops\n lwwMap.merge(key, record as LWWRecord<V>);\n }\n }\n }\n }).catch(err => logger.error({ err }, 'Failed to restore keys from storage'));\n\n // Wrap LWWMap with IMap interface logic\n const originalSet = lwwMap.set.bind(lwwMap);\n lwwMap.set = (key: K, value: V, ttlMs?: number) => {\n const record = originalSet(key, value, ttlMs);\n this.storageAdapter.put(`${name}:${key}`, record).catch(err => logger.error({ err }, 'Failed to put record to storage'));\n this.syncEngine.recordOperation(name, 'PUT', String(key), { record, timestamp: record.timestamp }).catch(err => logger.error({ err }, 'Failed to record PUT op'));\n return record;\n };\n\n const originalRemove = lwwMap.remove.bind(lwwMap);\n lwwMap.remove = (key: K) => {\n const tombstone = originalRemove(key);\n this.storageAdapter.put(`${name}:${key}`, tombstone).catch(err => logger.error({ err }, 'Failed to put tombstone to storage'));\n this.syncEngine.recordOperation(name, 'REMOVE', String(key), { record: tombstone, timestamp: tombstone.timestamp }).catch(err => logger.error({ err }, 'Failed to record REMOVE op'));\n return tombstone;\n };\n\n return lwwMap;\n }\n\n /**\n * Retrieves an ORMap instance. If the map doesn't exist locally, it's created.\n * @param name The name of the map.\n * @returns An ORMap instance.\n */\n public getORMap<K, V>(name: string): ORMap<K, V> {\n if (this.maps.has(name)) {\n const map = this.maps.get(name);\n if (map instanceof ORMap) {\n return map as ORMap<K, V>;\n }\n throw new Error(`Map ${name} exists but is not an ORMap`);\n }\n\n const orMap = new ORMap<K, V>(this.syncEngine.getHLC());\n this.maps.set(name, orMap);\n this.syncEngine.registerMap(name, orMap);\n\n // Restore state from storage\n this.restoreORMap(name, orMap);\n\n // Wrap ORMap methods to record operations\n const originalAdd = orMap.add.bind(orMap);\n orMap.add = (key: K, value: V, ttlMs?: number) => {\n const record = originalAdd(key, value, ttlMs);\n \n // Persist records\n this.persistORMapKey(name, orMap, key);\n\n this.syncEngine.recordOperation(name, 'OR_ADD', String(key), { orRecord: record, timestamp: record.timestamp }).catch(err => logger.error({ err }, 'Failed to record OR_ADD op'));\n return record;\n };\n\n const originalRemove = orMap.remove.bind(orMap);\n orMap.remove = (key: K, value: V) => {\n const tombstones = originalRemove(key, value);\n const timestamp = this.syncEngine.getHLC().now(); \n \n // Update storage for the key (items removed)\n this.persistORMapKey(name, orMap, key);\n // Update storage for tombstones\n this.persistORMapTombstones(name, orMap);\n\n for (const tag of tombstones) {\n this.syncEngine.recordOperation(name, 'OR_REMOVE', String(key), { orTag: tag, timestamp }).catch(err => logger.error({ err }, 'Failed to record OR_REMOVE op'));\n }\n return tombstones;\n };\n\n return orMap;\n }\n\n private async restoreORMap<K, V>(name: string, orMap: ORMap<K, V>) {\n try {\n // 1. Restore Tombstones\n const tombstoneKey = `__sys__:${name}:tombstones`;\n const tombstones = await this.storageAdapter.getMeta(tombstoneKey);\n if (Array.isArray(tombstones)) {\n for (const tag of tombstones) {\n orMap.applyTombstone(tag);\n }\n }\n\n // 2. Restore Items\n const keys = await this.storageAdapter.getAllKeys();\n const mapPrefix = `${name}:`;\n for (const fullKey of keys) {\n if (fullKey.startsWith(mapPrefix)) {\n const keyPart = fullKey.substring(mapPrefix.length);\n \n const data = await this.storageAdapter.get(fullKey);\n if (Array.isArray(data)) {\n // It's likely an ORMap value list (Array of ORMapRecord)\n const records = data as ORMapRecord<V>[];\n const key = keyPart as unknown as K;\n \n for (const record of records) {\n orMap.apply(key, record);\n }\n }\n }\n }\n } catch (e) {\n logger.error({ mapName: name, err: e }, 'Failed to restore ORMap');\n }\n }\n\n private async persistORMapKey<K, V>(mapName: string, orMap: ORMap<K, V>, key: K) {\n const records = orMap.getRecords(key);\n if (records.length > 0) {\n await this.storageAdapter.put(`${mapName}:${key}`, records);\n } else {\n await this.storageAdapter.remove(`${mapName}:${key}`);\n }\n }\n \n private async persistORMapTombstones<K, V>(mapName: string, orMap: ORMap<K, V>) {\n const tombstoneKey = `__sys__:${mapName}:tombstones`;\n const tombstones = orMap.getTombstones();\n await this.storageAdapter.setMeta(tombstoneKey, tombstones);\n }\n\n /**\n * Closes the client, disconnecting from the server and cleaning up resources.\n */\n public close(): void {\n if (this.clusterClient) {\n this.clusterClient.close();\n }\n this.syncEngine.close();\n }\n\n // ============================================\n // Cluster Mode API\n // ============================================\n\n /**\n * Check if running in cluster mode\n */\n public isCluster(): boolean {\n return this.isClusterMode;\n }\n\n /**\n * Get list of connected cluster nodes (cluster mode only)\n * @returns Array of connected node IDs, or empty array in single-server mode\n */\n public getConnectedNodes(): string[] {\n if (!this.clusterClient) return [];\n return this.clusterClient.getConnectedNodes();\n }\n\n /**\n * Get the current partition map version (cluster mode only)\n * @returns Partition map version, or 0 in single-server mode\n */\n public getPartitionMapVersion(): number {\n if (!this.clusterClient) return 0;\n return this.clusterClient.getRouterStats().mapVersion;\n }\n\n /**\n * Check if direct routing is active (cluster mode only)\n * Direct routing sends operations directly to partition owners.\n * @returns true if routing is active, false otherwise\n */\n public isRoutingActive(): boolean {\n if (!this.clusterClient) return false;\n return this.clusterClient.isRoutingActive();\n }\n\n /**\n * Get health status for all cluster nodes (cluster mode only)\n * @returns Map of node IDs to their health status\n */\n public getClusterHealth(): Map<string, NodeHealth> {\n if (!this.clusterClient) return new Map();\n return this.clusterClient.getHealthStatus();\n }\n\n /**\n * Force refresh of partition map (cluster mode only)\n * Use this after detecting routing errors.\n */\n public async refreshPartitionMap(): Promise<void> {\n if (!this.clusterClient) return;\n await this.clusterClient.refreshPartitionMap();\n }\n\n /**\n * Get cluster router statistics (cluster mode only)\n */\n public getClusterStats(): {\n mapVersion: number;\n partitionCount: number;\n nodeCount: number;\n lastRefresh: number;\n isStale: boolean;\n } | null {\n if (!this.clusterClient) return null;\n return this.clusterClient.getRouterStats();\n }\n\n // ============================================\n // Connection State API\n // ============================================\n\n /**\n * Get the current connection state\n */\n public getConnectionState(): SyncState {\n return this.syncEngine.getConnectionState();\n }\n\n /**\n * Subscribe to connection state changes\n * @param listener Callback function called on each state change\n * @returns Unsubscribe function\n */\n public onConnectionStateChange(listener: (event: StateChangeEvent) => void): () => void {\n return this.syncEngine.onConnectionStateChange(listener);\n }\n\n /**\n * Get state machine history for debugging\n * @param limit Maximum number of entries to return\n */\n public getStateHistory(limit?: number): StateChangeEvent[] {\n return this.syncEngine.getStateHistory(limit);\n }\n\n /**\n * Reset the connection and state machine.\n * Use after fatal errors to start fresh.\n */\n public resetConnection(): void {\n this.syncEngine.resetConnection();\n }\n\n // ============================================\n // Backpressure API\n // ============================================\n\n /**\n * Get the current number of pending (unacknowledged) operations.\n */\n public getPendingOpsCount(): number {\n return this.syncEngine.getPendingOpsCount();\n }\n\n /**\n * Get the current backpressure status.\n */\n public getBackpressureStatus(): BackpressureStatus {\n return this.syncEngine.getBackpressureStatus();\n }\n\n /**\n * Returns true if writes are currently paused due to backpressure.\n */\n public isBackpressurePaused(): boolean {\n return this.syncEngine.isBackpressurePaused();\n }\n\n /**\n * Subscribe to backpressure events.\n *\n * Available events:\n * - 'backpressure:high': Emitted when pending ops reach high water mark\n * - 'backpressure:low': Emitted when pending ops drop below low water mark\n * - 'backpressure:paused': Emitted when writes are paused (pause strategy)\n * - 'backpressure:resumed': Emitted when writes resume after being paused\n * - 'operation:dropped': Emitted when an operation is dropped (drop-oldest strategy)\n *\n * @param event Event name\n * @param listener Callback function\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * client.onBackpressure('backpressure:high', ({ pending, max }) => {\n * console.warn(`Warning: ${pending}/${max} pending ops`);\n * });\n *\n * client.onBackpressure('backpressure:paused', () => {\n * showLoadingSpinner();\n * });\n *\n * client.onBackpressure('backpressure:resumed', () => {\n * hideLoadingSpinner();\n * });\n * ```\n */\n public onBackpressure(\n event: 'backpressure:high' | 'backpressure:low' | 'backpressure:paused' | 'backpressure:resumed' | 'operation:dropped',\n listener: (data?: BackpressureThresholdEvent | OperationDroppedEvent) => void\n ): () => void {\n return this.syncEngine.onBackpressure(event, listener);\n }\n\n // ============================================\n // Full-Text Search API\n // ============================================\n\n /**\n * Perform a one-shot BM25 search on the server.\n *\n * Searches the specified map using BM25 ranking algorithm.\n * Requires FTS to be enabled for the map on the server.\n *\n * @param mapName Name of the map to search\n * @param query Search query text\n * @param options Search options\n * @returns Promise resolving to search results sorted by relevance\n *\n * @example\n * ```typescript\n * const results = await client.search<Article>('articles', 'machine learning', {\n * limit: 20,\n * minScore: 0.5,\n * boost: { title: 2.0, body: 1.0 }\n * });\n *\n * for (const result of results) {\n * console.log(`${result.key}: ${result.value.title} (score: ${result.score})`);\n * }\n * ```\n */\n public async search<T>(\n mapName: string,\n query: string,\n options?: {\n limit?: number;\n minScore?: number;\n boost?: Record<string, number>;\n }\n ): Promise<Array<{\n key: string;\n value: T;\n score: number;\n matchedTerms: string[];\n }>> {\n return this.syncEngine.search<T>(mapName, query, options);\n }\n\n // ============================================\n // Live Search API\n // ============================================\n\n /**\n * Subscribe to live search results with real-time updates.\n *\n * Unlike the one-shot `search()` method, `searchSubscribe()` returns a handle\n * that receives delta updates (ENTER/UPDATE/LEAVE) when documents change.\n * This is ideal for live search UIs that need to reflect data changes.\n *\n * @param mapName Name of the map to search\n * @param query Search query text\n * @param options Search options (limit, minScore, boost)\n * @returns SearchHandle for managing the subscription\n *\n * @example\n * ```typescript\n * const handle = client.searchSubscribe<Article>('articles', 'machine learning', {\n * limit: 20,\n * minScore: 0.5\n * });\n *\n * // Subscribe to result changes\n * const unsubscribe = handle.subscribe((results) => {\n * setSearchResults(results);\n * });\n *\n * // Update query dynamically\n * handle.setQuery('deep learning');\n *\n * // Get current snapshot\n * const snapshot = handle.getResults();\n *\n * // Cleanup when done\n * handle.dispose();\n * ```\n */\n public searchSubscribe<T>(\n mapName: string,\n query: string,\n options?: SearchOptions\n ): SearchHandle<T> {\n return new SearchHandle<T>(this.syncEngine, mapName, query, options);\n }\n\n // ============================================\n // Hybrid Query API\n // ============================================\n\n /**\n * Create a hybrid query combining FTS with traditional filters.\n *\n * Hybrid queries allow combining full-text search predicates (match, matchPhrase, matchPrefix)\n * with traditional filter predicates (eq, gt, lt, contains, etc.) in a single query.\n * Results include relevance scores for FTS ranking.\n *\n * @param mapName Name of the map to query\n * @param filter Hybrid query filter with predicate, where, sort, limit, cursor\n * @returns HybridQueryHandle for managing the subscription\n *\n * @example\n * ```typescript\n * import { Predicates } from '@topgunbuild/core';\n *\n * // Hybrid query: FTS + filter\n * const handle = client.hybridQuery<Article>('articles', {\n * predicate: Predicates.and(\n * Predicates.match('body', 'machine learning'),\n * Predicates.equal('category', 'tech')\n * ),\n * sort: { _score: 'desc' },\n * limit: 20\n * });\n *\n * // Subscribe to results\n * handle.subscribe((results) => {\n * results.forEach(r => console.log(`${r._key}: score=${r._score}`));\n * });\n * ```\n */\n public hybridQuery<T>(mapName: string, filter: HybridQueryFilter = {}): HybridQueryHandle<T> {\n return new HybridQueryHandle<T>(this.syncEngine, mapName, filter);\n }\n\n // ============================================\n // Entry Processor API\n // ============================================\n\n /**\n * Execute an entry processor on a single key atomically.\n *\n * Entry processors solve the read-modify-write race condition by executing\n * user-defined logic atomically on the server where the data lives.\n *\n * @param mapName Name of the map\n * @param key Key to process\n * @param processor Processor definition with name, code, and optional args\n * @returns Promise resolving to the processor result\n *\n * @example\n * ```typescript\n * // Increment a counter atomically\n * const result = await client.executeOnKey('stats', 'pageViews', {\n * name: 'increment',\n * code: `\n * const current = value ?? 0;\n * return { value: current + 1, result: current + 1 };\n * `,\n * });\n *\n * // Using built-in processor\n * import { BuiltInProcessors } from '@topgunbuild/core';\n * const result = await client.executeOnKey(\n * 'stats',\n * 'pageViews',\n * BuiltInProcessors.INCREMENT(1)\n * );\n * ```\n */\n public async executeOnKey<V, R = V>(\n mapName: string,\n key: string,\n processor: EntryProcessorDef<V, R>,\n ): Promise<EntryProcessorResult<R>> {\n const result = await this.syncEngine.executeOnKey(mapName, key, processor);\n\n // Update local map cache if successful and we have the map\n if (result.success && result.newValue !== undefined) {\n const map = this.maps.get(mapName);\n if (map instanceof LWWMap) {\n // Update local cache - set() generates its own timestamp\n // The server will broadcast the full update to all subscribers\n (map as LWWMap<any, any>).set(key, result.newValue);\n }\n }\n\n return result;\n }\n\n /**\n * Execute an entry processor on multiple keys.\n *\n * Each key is processed atomically. The operation returns when all keys\n * have been processed.\n *\n * @param mapName Name of the map\n * @param keys Keys to process\n * @param processor Processor definition\n * @returns Promise resolving to a map of key -> result\n *\n * @example\n * ```typescript\n * // Reset multiple counters\n * const results = await client.executeOnKeys(\n * 'stats',\n * ['pageViews', 'uniqueVisitors', 'bounceRate'],\n * {\n * name: 'reset',\n * code: `return { value: 0, result: value };`, // Returns old value\n * }\n * );\n *\n * for (const [key, result] of results) {\n * console.log(`${key}: was ${result.result}, now 0`);\n * }\n * ```\n */\n public async executeOnKeys<V, R = V>(\n mapName: string,\n keys: string[],\n processor: EntryProcessorDef<V, R>,\n ): Promise<Map<string, EntryProcessorResult<R>>> {\n const results = await this.syncEngine.executeOnKeys(mapName, keys, processor);\n\n // Update local map cache for successful operations\n const map = this.maps.get(mapName);\n if (map instanceof LWWMap) {\n for (const [key, result] of results) {\n if (result.success && result.newValue !== undefined) {\n (map as LWWMap<any, any>).set(key, result.newValue);\n }\n }\n }\n\n return results;\n }\n\n // ============================================\n // Event Journal API\n // ============================================\n\n /** Cached EventJournalReader instance */\n private journalReader?: EventJournalReader;\n\n /**\n * Get the Event Journal reader for subscribing to and reading\n * map change events.\n *\n * The Event Journal provides:\n * - Append-only log of all map changes (PUT, UPDATE, DELETE)\n * - Subscription to real-time events\n * - Historical event replay\n * - Audit trail for compliance\n *\n * @returns EventJournalReader instance\n *\n * @example\n * ```typescript\n * const journal = client.getEventJournal();\n *\n * // Subscribe to all events\n * const unsubscribe = journal.subscribe((event) => {\n * console.log(`${event.type} on ${event.mapName}:${event.key}`);\n * });\n *\n * // Subscribe to specific map\n * journal.subscribe(\n * (event) => console.log('User changed:', event.key),\n * { mapName: 'users' }\n * );\n *\n * // Read historical events\n * const events = await journal.readFrom(0n, 100);\n * ```\n */\n public getEventJournal(): EventJournalReader {\n if (!this.journalReader) {\n this.journalReader = new EventJournalReader(this.syncEngine);\n }\n return this.journalReader;\n }\n\n // ============================================\n // Conflict Resolver API\n // ============================================\n\n /**\n * Get the conflict resolver client for registering custom merge resolvers.\n *\n * Conflict resolvers allow you to customize how merge conflicts are handled\n * on the server. You can implement business logic like:\n * - First-write-wins for booking systems\n * - Numeric constraints (non-negative, min/max)\n * - Owner-only modifications\n * - Custom merge strategies\n *\n * @returns ConflictResolverClient instance\n *\n * @example\n * ```typescript\n * const resolvers = client.getConflictResolvers();\n *\n * // Register a first-write-wins resolver\n * await resolvers.register('bookings', {\n * name: 'first-write-wins',\n * code: `\n * if (context.localValue !== undefined) {\n * return { action: 'reject', reason: 'Slot already booked' };\n * }\n * return { action: 'accept', value: context.remoteValue };\n * `,\n * priority: 100,\n * });\n *\n * // Subscribe to merge rejections\n * resolvers.onRejection((rejection) => {\n * console.log(`Merge rejected: ${rejection.reason}`);\n * // Optionally refresh local state\n * });\n *\n * // List registered resolvers\n * const registered = await resolvers.list('bookings');\n * console.log('Active resolvers:', registered);\n *\n * // Unregister when done\n * await resolvers.unregister('bookings', 'first-write-wins');\n * ```\n */\n public getConflictResolvers() {\n return this.syncEngine.getConflictResolverClient();\n }\n}\n","/**\n * Deep equality comparison utility for change tracking.\n * Optimized for typical CRDT data structures (objects, arrays, primitives).\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n // Same reference or both primitive and equal\n if (a === b) return true;\n\n // Handle null/undefined\n if (a == null || b == null) return a === b;\n\n // Different types\n if (typeof a !== typeof b) return false;\n\n // Primitives (number, string, boolean, symbol, bigint)\n if (typeof a !== 'object') return a === b;\n\n // Arrays\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Objects (excluding null which is handled above)\n if (Array.isArray(b)) return false;\n\n const objA = a as Record<string, unknown>;\n const objB = b as Record<string, unknown>;\n\n const keysA = Object.keys(objA);\n const keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;\n if (!deepEqual(objA[key], objB[key])) return false;\n }\n\n return true;\n}\n","import { deepEqual } from './utils/deepEqual';\n\n/**\n * Represents a change event for tracking data mutations.\n */\nexport interface ChangeEvent<T> {\n /** Type of change: 'add' for new entries, 'update' for modified entries, 'remove' for deleted entries */\n type: 'add' | 'update' | 'remove';\n /** The key of the changed entry */\n key: string;\n /** New value (present for 'add' and 'update') */\n value?: T;\n /** Previous value (present for 'update' and 'remove') */\n previousValue?: T;\n /** HLC timestamp of the change */\n timestamp: number;\n}\n\n/**\n * ChangeTracker computes differences between snapshots of a Map.\n * Used to track add/update/remove changes for subscription notifications.\n *\n * @example\n * ```typescript\n * const tracker = new ChangeTracker<Todo>();\n *\n * // First snapshot\n * const changes1 = tracker.computeChanges(\n * new Map([['a', { title: 'Todo A' }]]),\n * Date.now()\n * );\n * // changes1 = [{ type: 'add', key: 'a', value: { title: 'Todo A' }, timestamp: ... }]\n *\n * // Second snapshot with update\n * const changes2 = tracker.computeChanges(\n * new Map([['a', { title: 'Todo A Updated' }]]),\n * Date.now()\n * );\n * // changes2 = [{ type: 'update', key: 'a', value: { title: 'Todo A Updated' }, previousValue: { title: 'Todo A' }, timestamp: ... }]\n * ```\n */\nexport class ChangeTracker<T> {\n private previousSnapshot: Map<string, T> = new Map();\n\n /**\n * Computes changes between previous and current state.\n * Updates internal snapshot after computation.\n *\n * @param current - Current state as a Map\n * @param timestamp - HLC timestamp for the changes\n * @returns Array of change events (may be empty if no changes)\n */\n computeChanges(current: Map<string, T>, timestamp: number): ChangeEvent<T>[] {\n const changes: ChangeEvent<T>[] = [];\n\n // Find additions and updates\n for (const [key, value] of current) {\n const previous = this.previousSnapshot.get(key);\n if (previous === undefined) {\n changes.push({ type: 'add', key, value, timestamp });\n } else if (!deepEqual(previous, value)) {\n changes.push({\n type: 'update',\n key,\n value,\n previousValue: previous,\n timestamp,\n });\n }\n }\n\n // Find removals\n for (const [key, value] of this.previousSnapshot) {\n if (!current.has(key)) {\n changes.push({\n type: 'remove',\n key,\n previousValue: value,\n timestamp,\n });\n }\n }\n\n // Update snapshot with deep copy to avoid mutation issues\n this.previousSnapshot = new Map(\n Array.from(current.entries()).map(([k, v]) => [\n k,\n typeof v === 'object' && v !== null ? { ...(v as object) } as T : v,\n ])\n );\n\n return changes;\n }\n\n /**\n * Reset tracker (e.g., on query change or reconnect)\n */\n reset(): void {\n this.previousSnapshot.clear();\n }\n\n /**\n * Get current snapshot size for debugging/metrics\n */\n get size(): number {\n return this.previousSnapshot.size;\n }\n}\n","import { SyncEngine } from './SyncEngine';\nimport { ChangeTracker, ChangeEvent } from './ChangeTracker';\nimport { logger } from './utils/logger';\nimport type { PredicateNode } from '@topgunbuild/core';\n\nexport interface QueryFilter {\n where?: Record<string, any>;\n predicate?: PredicateNode;\n sort?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n /** Cursor for pagination */\n cursor?: string;\n}\n\n/** Cursor status for debugging */\nexport type CursorStatus = 'valid' | 'expired' | 'invalid' | 'none';\n\n/** Pagination info from server */\nexport interface PaginationInfo {\n /** Cursor for fetching next page */\n nextCursor?: string;\n /** Whether more results are available */\n hasMore: boolean;\n /** Debug info: status of input cursor processing */\n cursorStatus: CursorStatus;\n}\n\n/** Source of query results for proper handling of race conditions */\nexport type QueryResultSource = 'local' | 'server';\n\n/** Result item with _key field for client-side lookups */\nexport type QueryResultItem<T> = T & { _key: string };\n\nexport class QueryHandle<T> {\n public readonly id: string;\n private syncEngine: SyncEngine;\n private mapName: string;\n private filter: QueryFilter;\n private listeners: Set<(results: QueryResultItem<T>[]) => void> = new Set();\n private currentResults: Map<string, T> = new Map();\n\n // Change tracking for delta notifications\n private changeTracker = new ChangeTracker<T>();\n private pendingChanges: ChangeEvent<T>[] = [];\n private changeListeners: Set<(changes: ChangeEvent<T>[]) => void> = new Set();\n\n // Pagination info\n private _paginationInfo: PaginationInfo = { hasMore: false, cursorStatus: 'none' };\n private paginationListeners: Set<(info: PaginationInfo) => void> = new Set();\n\n constructor(syncEngine: SyncEngine, mapName: string, filter: QueryFilter = {}) {\n this.id = crypto.randomUUID();\n this.syncEngine = syncEngine;\n this.mapName = mapName;\n this.filter = filter;\n }\n\n public subscribe(callback: (results: QueryResultItem<T>[]) => void): () => void {\n this.listeners.add(callback);\n\n // If this is the first listener, activate subscription\n if (this.listeners.size === 1) {\n this.syncEngine.subscribeToQuery(this);\n } else {\n // Immediately invoke with cached results\n callback(this.getSortedResults());\n }\n\n // [FIX]: Attempt to load local results immediately if available\n // This ensures that if data is already in storage but sync hasn't happened,\n // we still show something.\n this.loadInitialLocalData().then(data => {\n // If we haven't received server results yet (currentResults empty),\n // and we have local data OR it's just the initial load, we should notify.\n // Even if data is empty, we might want to tell the subscriber \"nothing here yet\".\n if (this.currentResults.size === 0) {\n this.onResult(data, 'local');\n }\n });\n\n return () => {\n this.listeners.delete(callback);\n if (this.listeners.size === 0) {\n this.syncEngine.unsubscribeFromQuery(this.id);\n }\n };\n }\n\n private async loadInitialLocalData() {\n // This requires SyncEngine to expose a method to query local storage\n // For now, we can't easily reach storageAdapter directly from here without leaking abstraction.\n // A better approach is for SyncEngine.subscribeToQuery to trigger a local load.\n return this.syncEngine.runLocalQuery(this.mapName, this.filter);\n }\n\n // Track if we've received authoritative server response\n private hasReceivedServerData: boolean = false;\n\n /**\n * Called by SyncEngine when server sends initial results or by local storage load.\n * Uses merge strategy instead of clear to prevent UI flickering.\n *\n * @param items - Array of key-value pairs\n * @param source - 'local' for IndexedDB data, 'server' for QUERY_RESP from server\n *\n * Race condition protection:\n * - Empty server responses are ignored until we receive non-empty server data\n * - This prevents clearing local data when server hasn't loaded from storage yet\n * - Works with any async storage adapter (PostgreSQL, SQLite, Redis, etc.)\n */\n public onResult(items: { key: string, value: T }[], source: QueryResultSource = 'server') {\n logger.debug({\n mapName: this.mapName,\n itemCount: items.length,\n source,\n currentResultsCount: this.currentResults.size,\n hasReceivedServerData: this.hasReceivedServerData\n }, 'QueryHandle onResult');\n\n // [FIX] Race condition protection for any async storage adapter:\n // If server sends empty QUERY_RESP before loading data from storage,\n // we ignore it to prevent clearing valid local data.\n // This is safe because:\n // 1. If server truly has no data, next non-empty response will clear local-only items\n // 2. If server is still loading, we preserve local data until real data arrives\n if (source === 'server' && items.length === 0 && !this.hasReceivedServerData) {\n logger.debug({ mapName: this.mapName }, 'QueryHandle ignoring empty server response - waiting for authoritative data');\n return;\n }\n\n // Mark that we've received authoritative server data (non-empty from server)\n if (source === 'server' && items.length > 0) {\n this.hasReceivedServerData = true;\n }\n\n const newKeys = new Set(items.map(i => i.key));\n\n // Remove only keys that are not in the new results\n const removedKeys: string[] = [];\n for (const key of this.currentResults.keys()) {\n if (!newKeys.has(key)) {\n removedKeys.push(key);\n this.currentResults.delete(key);\n }\n }\n if (removedKeys.length > 0) {\n logger.debug({\n mapName: this.mapName,\n removedCount: removedKeys.length,\n removedKeys\n }, 'QueryHandle removed keys');\n }\n\n // Add/update new results\n for (const item of items) {\n this.currentResults.set(item.key, item.value);\n }\n logger.debug({\n mapName: this.mapName,\n resultCount: this.currentResults.size\n }, 'QueryHandle after merge');\n\n // Compute changes for delta tracking\n this.computeAndNotifyChanges(Date.now());\n\n this.notify();\n }\n\n /**\n * Called by SyncEngine when server sends a live update\n */\n public onUpdate(key: string, value: T | null) {\n if (value === null) {\n this.currentResults.delete(key);\n } else {\n this.currentResults.set(key, value);\n }\n\n // Compute changes for delta tracking\n this.computeAndNotifyChanges(Date.now());\n\n this.notify();\n }\n\n /**\n * Subscribe to change events.\n * Returns an unsubscribe function.\n *\n * @example\n * ```typescript\n * const unsubscribe = handle.onChanges((changes) => {\n * for (const change of changes) {\n * if (change.type === 'add') {\n * console.log('Added:', change.key, change.value);\n * }\n * }\n * });\n * ```\n */\n public onChanges(listener: (changes: ChangeEvent<T>[]) => void): () => void {\n this.changeListeners.add(listener);\n return () => this.changeListeners.delete(listener);\n }\n\n /**\n * Get and clear pending changes.\n * Call this to retrieve all changes since the last consume.\n */\n public consumeChanges(): ChangeEvent<T>[] {\n const changes = [...this.pendingChanges];\n this.pendingChanges = [];\n return changes;\n }\n\n /**\n * Get last change without consuming.\n * Returns null if no pending changes.\n */\n public getLastChange(): ChangeEvent<T> | null {\n return this.pendingChanges.length > 0\n ? this.pendingChanges[this.pendingChanges.length - 1]\n : null;\n }\n\n /**\n * Get all pending changes without consuming.\n */\n public getPendingChanges(): ChangeEvent<T>[] {\n return [...this.pendingChanges];\n }\n\n /**\n * Clear all pending changes.\n */\n public clearChanges(): void {\n this.pendingChanges = [];\n }\n\n /**\n * Reset change tracker.\n * Use when query filter changes or on reconnect.\n */\n public resetChangeTracker(): void {\n this.changeTracker.reset();\n this.pendingChanges = [];\n }\n\n private computeAndNotifyChanges(timestamp: number): void {\n const changes = this.changeTracker.computeChanges(this.currentResults, timestamp);\n\n if (changes.length > 0) {\n this.pendingChanges.push(...changes);\n this.notifyChangeListeners(changes);\n }\n }\n\n private notifyChangeListeners(changes: ChangeEvent<T>[]): void {\n for (const listener of this.changeListeners) {\n try {\n listener(changes);\n } catch (e) {\n logger.error({ err: e }, 'QueryHandle change listener error');\n }\n }\n }\n\n private notify() {\n const results = this.getSortedResults();\n for (const listener of this.listeners) {\n listener(results);\n }\n }\n\n private getSortedResults(): (T & { _key: string })[] {\n // Include _key in each result for client-side matching/lookup\n const results = Array.from(this.currentResults.entries()).map(\n ([key, value]) => ({ ...(value as object), _key: key } as T & { _key: string })\n );\n\n if (this.filter.sort) {\n results.sort((a: any, b: any) => {\n for (const [field, direction] of Object.entries(this.filter.sort!)) {\n const valA = a[field];\n const valB = b[field];\n\n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n return results;\n }\n\n public getFilter(): QueryFilter {\n return this.filter;\n }\n\n public getMapName(): string {\n return this.mapName;\n }\n\n // ============== Pagination Methods ==============\n\n /**\n * Get current pagination info.\n * Returns nextCursor, hasMore, and cursorStatus.\n */\n public getPaginationInfo(): PaginationInfo {\n return { ...this._paginationInfo };\n }\n\n /**\n * Subscribe to pagination info changes.\n * Called when server sends QUERY_RESP with new cursor info.\n *\n * @returns Unsubscribe function\n */\n public onPaginationChange(listener: (info: PaginationInfo) => void): () => void {\n this.paginationListeners.add(listener);\n // Immediately invoke with current value\n listener(this.getPaginationInfo());\n return () => this.paginationListeners.delete(listener);\n }\n\n /**\n * Update pagination info from server response.\n * Called by SyncEngine when processing QUERY_RESP.\n *\n * @internal\n */\n public updatePaginationInfo(info: Partial<PaginationInfo>): void {\n this._paginationInfo = {\n nextCursor: info.nextCursor,\n hasMore: info.hasMore ?? false,\n cursorStatus: info.cursorStatus ?? 'none',\n };\n this.notifyPaginationListeners();\n }\n\n private notifyPaginationListeners(): void {\n const info = this.getPaginationInfo();\n for (const listener of this.paginationListeners) {\n try {\n listener(info);\n } catch (e) {\n logger.error({ err: e }, 'QueryHandle pagination listener error');\n }\n }\n }\n}\n","import { SyncEngine } from './SyncEngine';\n\nexport interface ILock {\n lock(ttl?: number): Promise<boolean>;\n unlock(): Promise<void>;\n isLocked(): boolean;\n}\n\nexport class DistributedLock implements ILock {\n private syncEngine: SyncEngine;\n private name: string;\n private fencingToken: number | null = null;\n private _isLocked: boolean = false;\n\n constructor(syncEngine: SyncEngine, name: string) {\n this.syncEngine = syncEngine;\n this.name = name;\n }\n\n public async lock(ttl: number = 10000): Promise<boolean> {\n const requestId = crypto.randomUUID();\n try {\n const result = await this.syncEngine.requestLock(this.name, requestId, ttl);\n this.fencingToken = result.fencingToken;\n this._isLocked = true;\n return true;\n } catch (e) {\n return false;\n }\n }\n\n public async unlock(): Promise<void> {\n if (!this._isLocked || this.fencingToken === null) return;\n \n const requestId = crypto.randomUUID();\n try {\n await this.syncEngine.releaseLock(this.name, requestId, this.fencingToken);\n } finally {\n this._isLocked = false;\n this.fencingToken = null;\n }\n }\n\n public isLocked(): boolean {\n return this._isLocked;\n }\n}\n\n","import { SyncEngine } from './SyncEngine';\nimport { logger } from './utils/logger';\n\nexport type TopicCallback<T = unknown> = (\n data: T,\n context: { timestamp: number; publisherId?: string }\n) => void;\n\nexport class TopicHandle {\n private engine: SyncEngine;\n private topic: string;\n private listeners: Set<TopicCallback> = new Set();\n\n constructor(engine: SyncEngine, topic: string) {\n this.engine = engine;\n this.topic = topic;\n }\n\n public get id(): string {\n return this.topic;\n }\n\n /**\n * Publish a message to the topic\n */\n public publish(data: unknown): void {\n this.engine.publishTopic(this.topic, data);\n }\n\n /**\n * Subscribe to the topic\n */\n public subscribe(callback: TopicCallback) {\n if (this.listeners.size === 0) {\n this.engine.subscribeToTopic(this.topic, this);\n }\n this.listeners.add(callback);\n return () => this.unsubscribe(callback);\n }\n\n private unsubscribe(callback: TopicCallback) {\n this.listeners.delete(callback);\n if (this.listeners.size === 0) {\n this.engine.unsubscribeFromTopic(this.topic);\n }\n }\n\n /**\n * Called by SyncEngine when a message is received\n */\n public onMessage(data: unknown, context: { timestamp: number; publisherId?: string }): void {\n this.listeners.forEach(cb => {\n try {\n cb(data, context);\n } catch (e) {\n logger.error({ err: e, topic: this.topic, context: 'listener' }, 'Error in topic listener');\n }\n });\n }\n}\n\n","import { PNCounterImpl } from '@topgunbuild/core';\nimport type { PNCounter, PNCounterState, PNCounterStateObject } from '@topgunbuild/core';\nimport { SyncEngine } from './SyncEngine';\nimport type { IStorageAdapter } from './IStorageAdapter';\nimport { logger } from './utils/logger';\n\n/**\n * Storage key prefix for PNCounter state persistence.\n */\nconst COUNTER_STORAGE_PREFIX = '__counter__:';\n\n/**\n * Client-side handle for a PN Counter.\n *\n * Wraps the core PNCounterImpl and integrates with SyncEngine for:\n * - Automatic sync to server when counter changes\n * - Receiving remote updates from other clients\n * - Local persistence via IStorageAdapter (IndexedDB in browser)\n *\n * @example\n * ```typescript\n * const counter = client.getPNCounter('likes:post-123');\n * counter.increment(); // Immediate local update + sync to server\n *\n * counter.subscribe((value) => {\n * console.log('Current likes:', value);\n * });\n * ```\n */\nexport class PNCounterHandle implements PNCounter {\n private readonly counter: PNCounterImpl;\n private readonly name: string;\n private readonly syncEngine: SyncEngine;\n private readonly storageAdapter?: IStorageAdapter;\n private syncScheduled = false;\n private persistScheduled = false;\n private unsubscribeFromUpdates?: () => void;\n\n constructor(name: string, nodeId: string, syncEngine: SyncEngine, storageAdapter?: IStorageAdapter) {\n this.name = name;\n this.syncEngine = syncEngine;\n this.storageAdapter = storageAdapter;\n this.counter = new PNCounterImpl({ nodeId });\n\n // Restore state from local storage first (async, but fast)\n this.restoreFromStorage();\n\n // Subscribe to remote updates via SyncEngine\n this.unsubscribeFromUpdates = this.syncEngine.onCounterUpdate(name, (state) => {\n this.counter.merge(state);\n // Persist merged state to local storage\n this.schedulePersist();\n });\n\n // Request initial state from server\n this.syncEngine.requestCounter(name);\n\n logger.debug({ name, nodeId }, 'PNCounterHandle created');\n }\n\n /**\n * Restore counter state from local storage.\n * Called during construction to recover offline state.\n */\n private async restoreFromStorage(): Promise<void> {\n if (!this.storageAdapter) {\n return;\n }\n\n try {\n const storageKey = COUNTER_STORAGE_PREFIX + this.name;\n const stored = await this.storageAdapter.getMeta(storageKey);\n\n if (stored && typeof stored === 'object' && 'p' in stored && 'n' in stored) {\n // Convert stored object to PNCounterState\n const state = PNCounterImpl.objectToState(stored as PNCounterStateObject);\n this.counter.merge(state);\n logger.debug({ name: this.name, value: this.counter.get() }, 'PNCounter restored from storage');\n }\n } catch (err) {\n logger.error({ err, name: this.name }, 'Failed to restore PNCounter from storage');\n }\n }\n\n /**\n * Persist counter state to local storage.\n * Debounced to avoid excessive writes during rapid operations.\n */\n private schedulePersist(): void {\n if (!this.storageAdapter || this.persistScheduled) return;\n this.persistScheduled = true;\n\n // Debounce persistence (100ms) to batch rapid changes\n setTimeout(() => {\n this.persistScheduled = false;\n this.persistToStorage();\n }, 100);\n }\n\n /**\n * Actually persist state to storage.\n */\n private async persistToStorage(): Promise<void> {\n if (!this.storageAdapter) return;\n\n try {\n const storageKey = COUNTER_STORAGE_PREFIX + this.name;\n const stateObj = PNCounterImpl.stateToObject(this.counter.getState());\n await this.storageAdapter.setMeta(storageKey, stateObj);\n logger.debug({ name: this.name, value: this.counter.get() }, 'PNCounter persisted to storage');\n } catch (err) {\n logger.error({ err, name: this.name }, 'Failed to persist PNCounter to storage');\n }\n }\n\n /**\n * Get current counter value.\n */\n get(): number {\n return this.counter.get();\n }\n\n /**\n * Increment by 1 and return new value.\n */\n increment(): number {\n const value = this.counter.increment();\n this.scheduleSync();\n this.schedulePersist();\n return value;\n }\n\n /**\n * Decrement by 1 and return new value.\n */\n decrement(): number {\n const value = this.counter.decrement();\n this.scheduleSync();\n this.schedulePersist();\n return value;\n }\n\n /**\n * Add delta (positive or negative) and return new value.\n */\n addAndGet(delta: number): number {\n const value = this.counter.addAndGet(delta);\n if (delta !== 0) {\n this.scheduleSync();\n this.schedulePersist();\n }\n return value;\n }\n\n /**\n * Get state for sync.\n */\n getState(): PNCounterState {\n return this.counter.getState();\n }\n\n /**\n * Merge remote state.\n */\n merge(remote: PNCounterState): void {\n this.counter.merge(remote);\n }\n\n /**\n * Subscribe to value changes.\n */\n subscribe(listener: (value: number) => void): () => void {\n return this.counter.subscribe(listener);\n }\n\n /**\n * Get the counter name.\n */\n getName(): string {\n return this.name;\n }\n\n /**\n * Cleanup resources.\n */\n dispose(): void {\n if (this.unsubscribeFromUpdates) {\n this.unsubscribeFromUpdates();\n }\n }\n\n /**\n * Schedule sync to server with debouncing.\n * Batches rapid increments to avoid network spam.\n */\n private scheduleSync(): void {\n if (this.syncScheduled) return;\n this.syncScheduled = true;\n\n // Debounce sync to batch rapid increments (50ms)\n setTimeout(() => {\n this.syncScheduled = false;\n this.syncEngine.syncCounter(this.name, this.counter.getState());\n }, 50);\n }\n}\n","import type { JournalEvent, JournalEventType } from '@topgunbuild/core';\nimport type { SyncEngine } from './SyncEngine';\nimport { logger } from './utils/logger';\n\n/**\n * Serialized journal event from network (bigint as string).\n */\nexport interface JournalEventData {\n sequence: string;\n type: JournalEventType;\n mapName: string;\n key: string;\n value?: unknown;\n previousValue?: unknown;\n timestamp: {\n millis: number;\n counter: number;\n nodeId: string;\n };\n nodeId: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options for journal subscription.\n */\nexport interface JournalSubscribeOptions {\n /** Start from specific sequence */\n fromSequence?: bigint;\n /** Filter by map name */\n mapName?: string;\n /** Filter by event types */\n types?: JournalEventType[];\n}\n\n/**\n * Client-side Event Journal Reader.\n * Communicates with server to read and subscribe to journal events.\n */\nexport class EventJournalReader {\n private readonly syncEngine: SyncEngine;\n private readonly listeners: Map<string, (event: JournalEvent) => void> = new Map();\n private subscriptionCounter: number = 0;\n\n constructor(syncEngine: SyncEngine) {\n this.syncEngine = syncEngine;\n }\n\n /**\n * Read events from sequence with optional limit.\n *\n * @param sequence Starting sequence (inclusive)\n * @param limit Maximum events to return (default: 100)\n * @returns Promise resolving to array of events\n */\n async readFrom(sequence: bigint, limit: number = 100): Promise<JournalEvent[]> {\n const requestId = this.generateRequestId();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Journal read timeout'));\n }, 10000);\n\n const handleResponse = (message: any) => {\n if (message.type === 'JOURNAL_READ_RESPONSE' && message.requestId === requestId) {\n clearTimeout(timeout);\n this.syncEngine.off('message', handleResponse);\n\n const events = message.events.map((e: JournalEventData) => this.parseEvent(e));\n resolve(events);\n }\n };\n\n this.syncEngine.on('message', handleResponse);\n\n this.syncEngine.send({\n type: 'JOURNAL_READ',\n requestId,\n fromSequence: sequence.toString(),\n limit,\n });\n });\n }\n\n /**\n * Read events for a specific map.\n *\n * @param mapName Map name to filter\n * @param sequence Starting sequence (default: 0n)\n * @param limit Maximum events to return (default: 100)\n */\n async readMapEvents(\n mapName: string,\n sequence: bigint = 0n,\n limit: number = 100\n ): Promise<JournalEvent[]> {\n const requestId = this.generateRequestId();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Journal read timeout'));\n }, 10000);\n\n const handleResponse = (message: any) => {\n if (message.type === 'JOURNAL_READ_RESPONSE' && message.requestId === requestId) {\n clearTimeout(timeout);\n this.syncEngine.off('message', handleResponse);\n\n const events = message.events.map((e: JournalEventData) => this.parseEvent(e));\n resolve(events);\n }\n };\n\n this.syncEngine.on('message', handleResponse);\n\n this.syncEngine.send({\n type: 'JOURNAL_READ',\n requestId,\n fromSequence: sequence.toString(),\n limit,\n mapName,\n });\n });\n }\n\n /**\n * Subscribe to new journal events.\n *\n * @param listener Callback for each event\n * @param options Subscription options\n * @returns Unsubscribe function\n */\n subscribe(\n listener: (event: JournalEvent) => void,\n options: JournalSubscribeOptions = {}\n ): () => void {\n const subscriptionId = this.generateRequestId();\n\n this.listeners.set(subscriptionId, listener);\n\n // Set up message handler for this subscription\n const handleEvent = (message: any) => {\n if (message.type === 'JOURNAL_EVENT') {\n const event = this.parseEvent(message.event);\n\n // Apply client-side filters\n if (options.mapName && event.mapName !== options.mapName) return;\n if (options.types && !options.types.includes(event.type)) return;\n\n const listenerFn = this.listeners.get(subscriptionId);\n if (listenerFn) {\n try {\n listenerFn(event);\n } catch (e) {\n logger.error({ err: e }, 'Journal listener error');\n }\n }\n }\n };\n\n this.syncEngine.on('message', handleEvent);\n\n // Send subscription request\n this.syncEngine.send({\n type: 'JOURNAL_SUBSCRIBE',\n requestId: subscriptionId,\n fromSequence: options.fromSequence?.toString(),\n mapName: options.mapName,\n types: options.types,\n });\n\n // Return unsubscribe function\n return () => {\n this.listeners.delete(subscriptionId);\n this.syncEngine.off('message', handleEvent);\n\n this.syncEngine.send({\n type: 'JOURNAL_UNSUBSCRIBE',\n subscriptionId,\n });\n };\n }\n\n /**\n * Get the latest sequence number from server.\n */\n async getLatestSequence(): Promise<bigint> {\n // Read one event from the end to get latest sequence\n const events = await this.readFrom(0n, 1);\n if (events.length === 0) return 0n;\n return events[events.length - 1].sequence;\n }\n\n /**\n * Parse network event data to JournalEvent.\n */\n private parseEvent(raw: JournalEventData): JournalEvent {\n return {\n sequence: BigInt(raw.sequence),\n type: raw.type,\n mapName: raw.mapName,\n key: raw.key,\n value: raw.value,\n previousValue: raw.previousValue,\n timestamp: raw.timestamp,\n nodeId: raw.nodeId,\n metadata: raw.metadata,\n };\n }\n\n /**\n * Generate unique request ID.\n */\n private generateRequestId(): string {\n return `journal_${Date.now()}_${++this.subscriptionCounter}`;\n }\n}\n\n","/**\n * SearchHandle - Client-side Live Search Subscription Handle\n *\n * Manages a live search subscription with delta updates.\n *\n * @module SearchHandle\n */\n\nimport type { SearchOptions, SearchUpdateType } from '@topgunbuild/core';\nimport type { SyncEngine, SearchResult } from './SyncEngine';\nimport { logger } from './utils/logger';\n\n/**\n * Callback type for result change notifications.\n */\nexport type SearchResultsCallback<T> = (results: SearchResult<T>[]) => void;\n\n/**\n * SearchHandle manages a live search subscription.\n *\n * Provides:\n * - Initial results on subscription\n * - Real-time delta updates (ENTER/UPDATE/LEAVE)\n * - Sorted results by relevance score\n * - Query update without re-subscribing\n *\n * @example\n * ```typescript\n * const handle = client.searchSubscribe<Article>('articles', 'machine learning', {\n * limit: 20,\n * minScore: 0.5\n * });\n *\n * // Subscribe to results\n * const unsubscribe = handle.subscribe((results) => {\n * console.log('Results updated:', results.length);\n * });\n *\n * // Get current snapshot\n * const snapshot = handle.getResults();\n *\n * // Update query\n * handle.setQuery('deep learning');\n *\n * // Cleanup\n * handle.dispose();\n * ```\n */\nexport class SearchHandle<T = unknown> {\n /** Map name being searched */\n readonly mapName: string;\n\n /** Current search query */\n private _query: string;\n\n /** Search options */\n private _options?: SearchOptions;\n\n /** Unique subscription ID */\n private subscriptionId: string;\n\n /** Current results map (key → result) */\n private results: Map<string, SearchResult<T>> = new Map();\n\n /** Result change listeners */\n private listeners: Set<SearchResultsCallback<T>> = new Set();\n\n /** Whether the handle has been disposed */\n private disposed = false;\n\n /** Reference to SyncEngine */\n private syncEngine: SyncEngine;\n\n /** Handler for all messages (SEARCH_RESP and SEARCH_UPDATE) */\n private messageHandler: (message: any) => void;\n\n constructor(\n syncEngine: SyncEngine,\n mapName: string,\n query: string,\n options?: SearchOptions\n ) {\n this.syncEngine = syncEngine;\n this.mapName = mapName;\n this._query = query;\n this._options = options;\n this.subscriptionId = crypto.randomUUID();\n\n // Set up message handler that handles both SEARCH_RESP and SEARCH_UPDATE\n this.messageHandler = this.handleMessage.bind(this);\n\n // Register handler with SyncEngine\n this.syncEngine.on('message', this.messageHandler);\n\n // Send subscription request\n this.sendSubscribe();\n }\n\n /**\n * Handle incoming messages (both SEARCH_RESP and SEARCH_UPDATE).\n */\n private handleMessage(message: any): void {\n if (message.type === 'SEARCH_RESP') {\n this.handleSearchResponse(message);\n } else if (message.type === 'SEARCH_UPDATE') {\n this.handleSearchUpdate(message);\n }\n }\n\n /**\n * Get the current query string.\n */\n get query(): string {\n return this._query;\n }\n\n /**\n * Subscribe to result changes.\n * Callback is immediately called with current results.\n *\n * @param callback - Function called with updated results\n * @returns Unsubscribe function\n */\n subscribe(callback: SearchResultsCallback<T>): () => void {\n if (this.disposed) {\n throw new Error('SearchHandle has been disposed');\n }\n\n this.listeners.add(callback);\n\n // Immediately call with current results\n callback(this.getResults());\n\n return () => {\n this.listeners.delete(callback);\n };\n }\n\n /**\n * Get current results snapshot sorted by score (highest first).\n *\n * @returns Array of search results\n */\n getResults(): SearchResult<T>[] {\n return Array.from(this.results.values())\n .sort((a, b) => b.score - a.score);\n }\n\n /**\n * Get result count.\n */\n get size(): number {\n return this.results.size;\n }\n\n /**\n * Update the search query.\n * Triggers a new subscription with the updated query.\n *\n * @param query - New query string\n */\n setQuery(query: string): void {\n if (this.disposed) {\n throw new Error('SearchHandle has been disposed');\n }\n\n if (query === this._query) {\n return; // No change\n }\n\n // Unsubscribe from old query\n this.sendUnsubscribe();\n\n // Clear current results\n this.results.clear();\n\n // Update query and generate new subscription ID\n this._query = query;\n this.subscriptionId = crypto.randomUUID();\n\n // Subscribe to new query\n this.sendSubscribe();\n\n // Notify listeners of cleared results\n this.notifyListeners();\n }\n\n /**\n * Update search options.\n *\n * @param options - New search options\n */\n setOptions(options: SearchOptions): void {\n if (this.disposed) {\n throw new Error('SearchHandle has been disposed');\n }\n\n // Unsubscribe from old subscription\n this.sendUnsubscribe();\n\n // Clear current results\n this.results.clear();\n\n // Update options and generate new subscription ID\n this._options = options;\n this.subscriptionId = crypto.randomUUID();\n\n // Subscribe with new options\n this.sendSubscribe();\n\n // Notify listeners of cleared results\n this.notifyListeners();\n }\n\n /**\n * Dispose of the handle and cleanup resources.\n * After disposal, the handle cannot be used.\n */\n dispose(): void {\n if (this.disposed) {\n return;\n }\n\n this.disposed = true;\n\n // Send unsubscribe\n this.sendUnsubscribe();\n\n // Remove message handler\n this.syncEngine.off('message', this.messageHandler);\n\n // Clear state\n this.results.clear();\n this.listeners.clear();\n }\n\n /**\n * Check if handle is disposed.\n */\n isDisposed(): boolean {\n return this.disposed;\n }\n\n /**\n * Send SEARCH_SUB message to server.\n */\n private sendSubscribe(): void {\n this.syncEngine.send({\n type: 'SEARCH_SUB',\n payload: {\n subscriptionId: this.subscriptionId,\n mapName: this.mapName,\n query: this._query,\n options: this._options,\n },\n });\n }\n\n /**\n * Send SEARCH_UNSUB message to server.\n */\n private sendUnsubscribe(): void {\n this.syncEngine.send({\n type: 'SEARCH_UNSUB',\n payload: {\n subscriptionId: this.subscriptionId,\n },\n });\n }\n\n /**\n * Handle SEARCH_RESP message (initial results).\n */\n private handleSearchResponse(message: any): void {\n if (message.type !== 'SEARCH_RESP') return;\n if (message.payload?.requestId !== this.subscriptionId) return;\n\n const { results } = message.payload;\n\n if (Array.isArray(results)) {\n // Populate initial results\n for (const result of results) {\n this.results.set(result.key, {\n key: result.key,\n value: result.value as T,\n score: result.score,\n matchedTerms: result.matchedTerms || [],\n });\n }\n\n this.notifyListeners();\n }\n }\n\n /**\n * Handle SEARCH_UPDATE message (delta updates).\n */\n private handleSearchUpdate(message: any): void {\n if (message.type !== 'SEARCH_UPDATE') return;\n if (message.payload?.subscriptionId !== this.subscriptionId) return;\n\n const { key, value, score, matchedTerms, changeType } = message.payload;\n\n switch (changeType as SearchUpdateType) {\n case 'ENTER':\n // Document entered result set\n this.results.set(key, {\n key,\n value: value as T,\n score,\n matchedTerms: matchedTerms || [],\n });\n break;\n\n case 'UPDATE':\n // Document score changed\n const existing = this.results.get(key);\n if (existing) {\n existing.score = score;\n existing.matchedTerms = matchedTerms || [];\n // Value may have changed too\n existing.value = value as T;\n }\n break;\n\n case 'LEAVE':\n // Document left result set\n this.results.delete(key);\n break;\n }\n\n this.notifyListeners();\n }\n\n /**\n * Notify all listeners of result changes.\n */\n private notifyListeners(): void {\n const results = this.getResults();\n for (const listener of this.listeners) {\n try {\n listener(results);\n } catch (err) {\n logger.error({ err, mapName: this.mapName, context: 'listener' }, 'SearchHandle listener error');\n }\n }\n }\n}\n","/**\n * HybridQueryHandle - Query handle for hybrid FTS + filter queries\n *\n * Extends QueryHandle functionality to support:\n * - FTS predicates (match, matchPhrase, matchPrefix)\n * - Score-based sorting (_score field)\n * - Hybrid queries combining FTS with traditional filters\n *\n * @module HybridQueryHandle\n */\n\nimport { SyncEngine } from './SyncEngine';\nimport { ChangeTracker, ChangeEvent } from './ChangeTracker';\nimport { logger } from './utils/logger';\nimport type { PredicateNode } from '@topgunbuild/core';\nimport type { CursorStatus, PaginationInfo } from './QueryHandle';\n\n/**\n * Filter options for hybrid queries.\n */\nexport interface HybridQueryFilter {\n /** Traditional where clause filters */\n where?: Record<string, any>;\n /** Predicate tree (can include FTS predicates) */\n predicate?: PredicateNode;\n /** Sort configuration - use '_score' for FTS relevance sorting */\n sort?: Record<string, 'asc' | 'desc'>;\n /** Maximum results */\n limit?: number;\n /** Cursor for pagination */\n cursor?: string;\n}\n\n/**\n * Result item with score for hybrid queries.\n */\nexport interface HybridResultItem<T> {\n /** The document */\n value: T;\n /** Unique key */\n _key: string;\n /** Relevance score (only for FTS queries) */\n _score?: number;\n /** Matched terms (only for FTS queries) */\n _matchedTerms?: string[];\n}\n\n/**\n * Source of query results.\n */\nexport type HybridResultSource = 'local' | 'server';\n\n/**\n * HybridQueryHandle manages hybrid queries that combine FTS with filters.\n *\n * @example\n * ```typescript\n * // Create hybrid query: FTS + filter\n * const handle = new HybridQueryHandle(syncEngine, 'articles', {\n * predicate: Predicates.and(\n * Predicates.match('body', 'machine learning'),\n * Predicates.equal('category', 'tech')\n * ),\n * sort: { _score: 'desc' },\n * limit: 20\n * });\n *\n * // Subscribe to results\n * handle.subscribe((results) => {\n * results.forEach(r => console.log(`${r._key}: ${r._score}`));\n * });\n * ```\n */\nexport class HybridQueryHandle<T> {\n public readonly id: string;\n private syncEngine: SyncEngine;\n private mapName: string;\n private filter: HybridQueryFilter;\n private listeners: Set<(results: HybridResultItem<T>[]) => void> = new Set();\n private currentResults: Map<string, { value: T; score?: number; matchedTerms?: string[] }> =\n new Map();\n\n // Change tracking\n private changeTracker = new ChangeTracker<T>();\n private pendingChanges: ChangeEvent<T>[] = [];\n private changeListeners: Set<(changes: ChangeEvent<T>[]) => void> = new Set();\n\n // Track server data reception\n private hasReceivedServerData: boolean = false;\n\n // Pagination info\n private _paginationInfo: PaginationInfo = { hasMore: false, cursorStatus: 'none' };\n private paginationListeners: Set<(info: PaginationInfo) => void> = new Set();\n\n constructor(syncEngine: SyncEngine, mapName: string, filter: HybridQueryFilter = {}) {\n this.id = crypto.randomUUID();\n this.syncEngine = syncEngine;\n this.mapName = mapName;\n this.filter = filter;\n }\n\n /**\n * Subscribe to query results.\n */\n public subscribe(callback: (results: HybridResultItem<T>[]) => void): () => void {\n this.listeners.add(callback);\n\n // Activate subscription on first listener\n if (this.listeners.size === 1) {\n this.syncEngine.subscribeToHybridQuery(this);\n } else {\n // Return cached results immediately\n callback(this.getSortedResults());\n }\n\n // Load initial local data\n this.loadInitialLocalData().then((data) => {\n if (this.currentResults.size === 0) {\n this.onResult(data, 'local');\n }\n });\n\n return () => {\n this.listeners.delete(callback);\n if (this.listeners.size === 0) {\n this.syncEngine.unsubscribeFromHybridQuery(this.id);\n }\n };\n }\n\n private async loadInitialLocalData(): Promise<\n Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }>\n > {\n // Use SyncEngine to run local hybrid query\n return this.syncEngine.runLocalHybridQuery(this.mapName, this.filter);\n }\n\n /**\n * Called by SyncEngine with query results.\n */\n public onResult(\n items: Array<{ key: string; value: T; score?: number; matchedTerms?: string[] }>,\n source: HybridResultSource = 'server'\n ): void {\n logger.debug(\n {\n mapName: this.mapName,\n itemCount: items.length,\n source,\n currentResultsCount: this.currentResults.size,\n hasReceivedServerData: this.hasReceivedServerData,\n },\n 'HybridQueryHandle onResult'\n );\n\n // Race condition protection (same as QueryHandle)\n if (source === 'server' && items.length === 0 && !this.hasReceivedServerData) {\n logger.debug(\n { mapName: this.mapName },\n 'HybridQueryHandle ignoring empty server response'\n );\n return;\n }\n\n if (source === 'server' && items.length > 0) {\n this.hasReceivedServerData = true;\n }\n\n const newKeys = new Set(items.map((i) => i.key));\n\n // Remove keys not in new results\n for (const key of this.currentResults.keys()) {\n if (!newKeys.has(key)) {\n this.currentResults.delete(key);\n }\n }\n\n // Add/update new results\n for (const item of items) {\n this.currentResults.set(item.key, {\n value: item.value,\n score: item.score,\n matchedTerms: item.matchedTerms,\n });\n }\n\n // Compute changes for delta tracking\n this.computeAndNotifyChanges(Date.now());\n\n this.notify();\n }\n\n /**\n * Called by SyncEngine on live update.\n */\n public onUpdate(\n key: string,\n value: T | null,\n score?: number,\n matchedTerms?: string[]\n ): void {\n if (value === null) {\n this.currentResults.delete(key);\n } else {\n this.currentResults.set(key, { value, score, matchedTerms });\n }\n\n this.computeAndNotifyChanges(Date.now());\n this.notify();\n }\n\n /**\n * Subscribe to change events.\n */\n public onChanges(listener: (changes: ChangeEvent<T>[]) => void): () => void {\n this.changeListeners.add(listener);\n return () => this.changeListeners.delete(listener);\n }\n\n /**\n * Get and clear pending changes.\n */\n public consumeChanges(): ChangeEvent<T>[] {\n const changes = [...this.pendingChanges];\n this.pendingChanges = [];\n return changes;\n }\n\n /**\n * Get last change without consuming.\n */\n public getLastChange(): ChangeEvent<T> | null {\n return this.pendingChanges.length > 0\n ? this.pendingChanges[this.pendingChanges.length - 1]\n : null;\n }\n\n /**\n * Get all pending changes without consuming.\n */\n public getPendingChanges(): ChangeEvent<T>[] {\n return [...this.pendingChanges];\n }\n\n /**\n * Clear all pending changes.\n */\n public clearChanges(): void {\n this.pendingChanges = [];\n }\n\n /**\n * Reset change tracker.\n */\n public resetChangeTracker(): void {\n this.changeTracker.reset();\n this.pendingChanges = [];\n }\n\n private computeAndNotifyChanges(timestamp: number): void {\n const dataMap = new Map<string, T>();\n for (const [key, entry] of this.currentResults) {\n dataMap.set(key, entry.value);\n }\n const changes = this.changeTracker.computeChanges(dataMap, timestamp);\n\n if (changes.length > 0) {\n this.pendingChanges.push(...changes);\n this.notifyChangeListeners(changes);\n }\n }\n\n private notifyChangeListeners(changes: ChangeEvent<T>[]): void {\n for (const listener of this.changeListeners) {\n try {\n listener(changes);\n } catch (e) {\n logger.error({ err: e }, 'HybridQueryHandle change listener error');\n }\n }\n }\n\n private notify(): void {\n const results = this.getSortedResults();\n for (const listener of this.listeners) {\n listener(results);\n }\n }\n\n /**\n * Get sorted results with _key and _score.\n */\n private getSortedResults(): HybridResultItem<T>[] {\n const results: HybridResultItem<T>[] = Array.from(this.currentResults.entries()).map(\n ([key, entry]) => ({\n value: entry.value,\n _key: key,\n _score: entry.score,\n _matchedTerms: entry.matchedTerms,\n })\n );\n\n // Sort by configured sort fields\n if (this.filter.sort) {\n results.sort((a, b) => {\n for (const [field, direction] of Object.entries(this.filter.sort!)) {\n let valA: any;\n let valB: any;\n\n if (field === '_score') {\n // Special handling for _score\n valA = a._score ?? 0;\n valB = b._score ?? 0;\n } else if (field === '_key') {\n valA = a._key;\n valB = b._key;\n } else {\n valA = (a.value as any)[field];\n valB = (b.value as any)[field];\n }\n\n if (valA < valB) return direction === 'asc' ? -1 : 1;\n if (valA > valB) return direction === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n // Apply limit (cursor filtering is done server-side)\n let sliced = results;\n if (this.filter.limit) {\n sliced = sliced.slice(0, this.filter.limit);\n }\n\n return sliced;\n }\n\n /**\n * Get the filter configuration.\n */\n public getFilter(): HybridQueryFilter {\n return this.filter;\n }\n\n /**\n * Get the map name.\n */\n public getMapName(): string {\n return this.mapName;\n }\n\n /**\n * Check if this query contains FTS predicates.\n */\n public hasFTSPredicate(): boolean {\n return this.filter.predicate ? this.containsFTS(this.filter.predicate) : false;\n }\n\n private containsFTS(predicate: PredicateNode): boolean {\n if (predicate.op === 'match' || predicate.op === 'matchPhrase' || predicate.op === 'matchPrefix') {\n return true;\n }\n if (predicate.children) {\n return predicate.children.some((child) => this.containsFTS(child));\n }\n return false;\n }\n\n // ============== Pagination Methods ==============\n\n /**\n * Get current pagination info.\n * Returns nextCursor, hasMore, and cursorStatus.\n */\n public getPaginationInfo(): PaginationInfo {\n return { ...this._paginationInfo };\n }\n\n /**\n * Subscribe to pagination info changes.\n * Called when server sends HYBRID_QUERY_RESP with new cursor info.\n *\n * @returns Unsubscribe function\n */\n public onPaginationChange(listener: (info: PaginationInfo) => void): () => void {\n this.paginationListeners.add(listener);\n // Immediately invoke with current value\n listener(this.getPaginationInfo());\n return () => this.paginationListeners.delete(listener);\n }\n\n /**\n * Update pagination info from server response.\n * Called by SyncEngine when processing HYBRID_QUERY_RESP.\n *\n * @internal\n */\n public updatePaginationInfo(info: Partial<PaginationInfo>): void {\n this._paginationInfo = {\n nextCursor: info.nextCursor,\n hasMore: info.hasMore ?? false,\n cursorStatus: info.cursorStatus ?? 'none',\n };\n this.notifyPaginationListeners();\n }\n\n private notifyPaginationListeners(): void {\n const info = this.getPaginationInfo();\n for (const listener of this.paginationListeners) {\n try {\n listener(info);\n } catch (e) {\n logger.error({ err: e }, 'HybridQueryHandle pagination listener error');\n }\n }\n }\n}\n","/**\n * ClusterClient - Cluster-aware client wrapper\n *\n * Implements IConnectionProvider for SyncEngine abstraction.\n * Wraps the standard TopGunClient with cluster-aware routing capabilities.\n * Coordinates between ConnectionPool and PartitionRouter for optimal\n * request routing in a clustered environment.\n */\n\nimport {\n ClusterClientConfig,\n ConnectionPoolConfig,\n PartitionRouterConfig,\n CircuitBreakerConfig,\n DEFAULT_CONNECTION_POOL_CONFIG,\n DEFAULT_PARTITION_ROUTER_CONFIG,\n DEFAULT_CIRCUIT_BREAKER_CONFIG,\n NodeHealth,\n serialize,\n} from '@topgunbuild/core';\nimport { ConnectionPool } from './ConnectionPool';\nimport { PartitionRouter } from './PartitionRouter';\nimport { logger } from '../utils/logger';\nimport type { IConnectionProvider, IConnection, ConnectionProviderEvent, ConnectionEventHandler } from '../types';\n\nexport interface ClusterClientEvents {\n 'connected': () => void;\n 'disconnected': (reason: string) => void;\n 'partitionMap:ready': (version: number) => void;\n 'routing:active': () => void;\n 'error': (error: Error) => void;\n 'circuit:open': (nodeId: string) => void;\n 'circuit:closed': (nodeId: string) => void;\n 'circuit:half-open': (nodeId: string) => void;\n}\n\n/**\n * Circuit breaker state for a node.\n */\nexport interface CircuitState {\n /** Number of consecutive failures */\n failures: number;\n /** Timestamp of last failure */\n lastFailure: number;\n /** Current circuit state */\n state: 'closed' | 'open' | 'half-open';\n}\n\n/**\n * Routing metrics for monitoring smart routing effectiveness.\n */\nexport interface RoutingMetrics {\n /** Operations routed directly to partition owner */\n directRoutes: number;\n /** Operations falling back to any node (owner unavailable) */\n fallbackRoutes: number;\n /** Operations when partition map is missing/stale */\n partitionMisses: number;\n /** Total routing decisions made */\n totalRoutes: number;\n}\n\nexport type ClusterRoutingMode = 'direct' | 'forward';\n\n/**\n * ClusterClient implements IConnectionProvider for multi-node cluster mode.\n * It provides partition-aware routing and connection management.\n */\nexport class ClusterClient implements IConnectionProvider {\n private readonly listeners: Map<string, Set<(...args: any[]) => void>> = new Map();\n private readonly connectionPool: ConnectionPool;\n private readonly partitionRouter: PartitionRouter;\n private readonly config: ClusterClientConfig;\n private initialized: boolean = false;\n private routingActive: boolean = false;\n private readonly routingMetrics: RoutingMetrics = {\n directRoutes: 0,\n fallbackRoutes: 0,\n partitionMisses: 0,\n totalRoutes: 0,\n };\n\n // Circuit breaker state per node\n private readonly circuits: Map<string, CircuitState> = new Map();\n private readonly circuitBreakerConfig: CircuitBreakerConfig;\n\n // Debounce timer for partition map requests on reconnect\n private partitionMapRequestTimer: ReturnType<typeof setTimeout> | null = null;\n private static readonly PARTITION_MAP_REQUEST_DEBOUNCE_MS = 500;\n\n constructor(config: ClusterClientConfig) {\n this.config = config;\n\n // Initialize circuit breaker config\n this.circuitBreakerConfig = {\n ...DEFAULT_CIRCUIT_BREAKER_CONFIG,\n ...config.circuitBreaker,\n };\n\n // Initialize connection pool\n const poolConfig: ConnectionPoolConfig = {\n ...DEFAULT_CONNECTION_POOL_CONFIG,\n ...config.connectionPool,\n };\n this.connectionPool = new ConnectionPool(poolConfig);\n\n // Initialize partition router\n const routerConfig: PartitionRouterConfig = {\n ...DEFAULT_PARTITION_ROUTER_CONFIG,\n fallbackMode: config.routingMode === 'direct' ? 'error' : 'forward',\n ...config.routing,\n };\n this.partitionRouter = new PartitionRouter(this.connectionPool, routerConfig);\n\n this.setupEventHandlers();\n }\n\n // ============================================\n // Event Emitter Methods (browser-compatible)\n // ============================================\n\n public on(event: string, listener: (...args: any[]) => void): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n return this;\n }\n\n public off(event: string, listener: (...args: any[]) => void): this {\n this.listeners.get(event)?.delete(listener);\n return this;\n }\n\n public emit(event: string, ...args: any[]): boolean {\n const eventListeners = this.listeners.get(event);\n if (!eventListeners || eventListeners.size === 0) {\n return false;\n }\n for (const listener of eventListeners) {\n try {\n listener(...args);\n } catch (err) {\n logger.error({ event, err }, 'Error in event listener');\n }\n }\n return true;\n }\n\n public removeAllListeners(event?: string): this {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n return this;\n }\n\n // ============================================\n // IConnectionProvider Implementation\n // ============================================\n\n /**\n * Connect to cluster nodes (IConnectionProvider interface).\n * Alias for start() method.\n */\n public async connect(): Promise<void> {\n return this.start();\n }\n\n /**\n * Get connection for a specific key (IConnectionProvider interface).\n * Routes to partition owner based on key hash when smart routing is enabled.\n * @throws Error if not connected\n */\n public getConnection(key: string): IConnection {\n if (!this.isConnected()) {\n throw new Error('ClusterClient not connected');\n }\n\n this.routingMetrics.totalRoutes++;\n\n // If not in direct routing mode or routing not active, use fallback\n if (this.config.routingMode !== 'direct' || !this.routingActive) {\n this.routingMetrics.fallbackRoutes++;\n return this.getFallbackConnection();\n }\n\n // Try to route to partition owner\n const routing = this.partitionRouter.route(key);\n\n // No partition map available\n if (!routing) {\n this.routingMetrics.partitionMisses++;\n logger.debug({ key }, 'No partition map available, using fallback');\n return this.getFallbackConnection();\n }\n\n const owner = routing.nodeId;\n\n // Check if owner is connected\n if (!this.connectionPool.isNodeConnected(owner)) {\n this.routingMetrics.fallbackRoutes++;\n logger.debug({ key, owner }, 'Partition owner not connected, using fallback');\n // Request partition map refresh since owner might have changed\n this.requestPartitionMapRefresh();\n return this.getFallbackConnection();\n }\n\n // Get connection to owner\n const connection = this.connectionPool.getConnection(owner);\n if (!connection) {\n this.routingMetrics.fallbackRoutes++;\n logger.debug({ key, owner }, 'Could not get connection to owner, using fallback');\n return this.getFallbackConnection();\n }\n\n this.routingMetrics.directRoutes++;\n return connection;\n }\n\n /**\n * Get fallback connection when owner is unavailable.\n * @throws Error if no connection available\n */\n private getFallbackConnection(): IConnection {\n const conn = this.connectionPool.getAnyHealthyConnection();\n if (!conn?.connection) {\n throw new Error('No healthy connection available');\n }\n return conn.connection;\n }\n\n /**\n * Request a partition map refresh in the background.\n * Called when routing to an unknown/disconnected owner.\n */\n private requestPartitionMapRefresh(): void {\n this.partitionRouter.refreshPartitionMap().catch(err => {\n logger.error({ err }, 'Failed to refresh partition map');\n });\n }\n\n /**\n * Debounce partition map requests to prevent flooding when multiple nodes\n * reconnect simultaneously. Coalesces rapid requests into a single request\n * sent to the most recently connected node.\n */\n private debouncedPartitionMapRequest(nodeId: string): void {\n if (this.partitionMapRequestTimer) {\n clearTimeout(this.partitionMapRequestTimer);\n }\n this.partitionMapRequestTimer = setTimeout(() => {\n this.partitionMapRequestTimer = null;\n this.requestPartitionMapFromNode(nodeId);\n }, ClusterClient.PARTITION_MAP_REQUEST_DEBOUNCE_MS);\n }\n\n /**\n * Request partition map from a specific node.\n * Called on node connection via debounced handler.\n */\n private requestPartitionMapFromNode(nodeId: string): void {\n const socket = this.connectionPool.getConnection(nodeId);\n if (socket) {\n logger.debug({ nodeId }, 'Requesting partition map from node');\n socket.send(serialize({\n type: 'PARTITION_MAP_REQUEST',\n payload: {\n currentVersion: this.partitionRouter.getMapVersion(),\n },\n }));\n }\n }\n\n /**\n * Check if at least one connection is active (IConnectionProvider interface).\n */\n public isConnected(): boolean {\n return this.connectionPool.getConnectedNodes().length > 0;\n }\n\n /**\n * Send data via the appropriate connection (IConnectionProvider interface).\n * Routes based on key if provided.\n */\n public send(data: ArrayBuffer | Uint8Array, key?: string): void {\n if (!this.isConnected()) {\n throw new Error('ClusterClient not connected');\n }\n\n const socket = key ? this.getConnection(key) : this.getAnyConnection();\n socket.send(data);\n }\n\n /**\n * Send data with automatic retry and rerouting on failure.\n * @param data - Data to send\n * @param key - Optional key for routing\n * @param options - Retry options\n * @throws Error after max retries exceeded\n */\n public async sendWithRetry(\n data: ArrayBuffer | Uint8Array,\n key?: string,\n options: {\n maxRetries?: number;\n retryDelayMs?: number;\n retryOnNotOwner?: boolean;\n } = {}\n ): Promise<void> {\n const {\n maxRetries = 3,\n retryDelayMs = 100,\n retryOnNotOwner = true,\n } = options;\n\n let lastError: Error | null = null;\n let nodeId: string | null = null;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n // Get the target node for circuit breaker tracking\n if (key && this.routingActive) {\n const routing = this.partitionRouter.route(key);\n nodeId = routing?.nodeId ?? null;\n }\n\n // Check circuit breaker\n if (nodeId && !this.canUseNode(nodeId)) {\n logger.debug({ nodeId, attempt }, 'Circuit open, using fallback');\n nodeId = null; // Force fallback\n }\n\n // Get connection and send\n const socket = key && nodeId\n ? this.connectionPool.getConnection(nodeId)\n : this.getAnyConnection();\n\n if (!socket) {\n throw new Error('No connection available');\n }\n\n socket.send(data);\n\n // Record success if using a specific node\n if (nodeId) {\n this.recordSuccess(nodeId);\n }\n\n return; // Success\n } catch (error) {\n lastError = error as Error;\n\n // Record failure if using a specific node\n if (nodeId) {\n this.recordFailure(nodeId);\n }\n\n const errorCode = (error as any)?.code;\n\n // Check if error is retryable\n if (this.isRetryableError(error)) {\n logger.debug(\n { attempt, maxRetries, errorCode, nodeId },\n 'Retryable error, will retry'\n );\n\n // Handle specific error types\n if (errorCode === 'NOT_OWNER' && retryOnNotOwner) {\n // Wait for partition map update\n await this.waitForPartitionMapUpdateInternal(2000);\n } else if (errorCode === 'CONNECTION_CLOSED' || !this.isConnected()) {\n // Wait for reconnection\n await this.waitForConnectionInternal(5000);\n }\n\n // Small delay before retry\n await this.delay(retryDelayMs * (attempt + 1)); // Exponential backoff\n continue;\n }\n\n // Non-retryable error, fail immediately\n throw error;\n }\n }\n\n throw new Error(\n `Operation failed after ${maxRetries} retries: ${lastError?.message}`\n );\n }\n\n /**\n * Check if an error is retryable.\n */\n private isRetryableError(error: any): boolean {\n const code = error?.code;\n const message = error?.message || '';\n\n return (\n code === 'NOT_OWNER' ||\n code === 'CONNECTION_CLOSED' ||\n code === 'TIMEOUT' ||\n code === 'ECONNRESET' ||\n message.includes('No active connections') ||\n message.includes('No connection available') ||\n message.includes('No healthy connection')\n );\n }\n\n /**\n * Wait for partition map update.\n */\n private waitForPartitionMapUpdateInternal(timeoutMs: number): Promise<void> {\n return new Promise((resolve) => {\n const timeout = setTimeout(resolve, timeoutMs);\n\n const handler = () => {\n clearTimeout(timeout);\n this.off('partitionMapUpdated', handler);\n resolve();\n };\n\n this.on('partitionMapUpdated', handler);\n });\n }\n\n /**\n * Wait for at least one connection to be available.\n */\n private waitForConnectionInternal(timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.isConnected()) {\n resolve();\n return;\n }\n\n const timeout = setTimeout(() => {\n this.off('connected', handler);\n reject(new Error('Connection timeout'));\n }, timeoutMs);\n\n const handler = () => {\n clearTimeout(timeout);\n this.off('connected', handler);\n resolve();\n };\n\n this.on('connected', handler);\n });\n }\n\n /**\n * Helper delay function.\n */\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // ============================================\n // Cluster-Specific Methods\n // ============================================\n\n /**\n * Initialize cluster connections\n */\n public async start(): Promise<void> {\n if (this.initialized) return;\n\n logger.info({ seedNodes: this.config.seedNodes }, 'Starting cluster client');\n\n // Connect to seed nodes\n for (let i = 0; i < this.config.seedNodes.length; i++) {\n const endpoint = this.config.seedNodes[i];\n const nodeId = `seed-${i}`; // Temporary ID until we get real ID from handshake\n await this.connectionPool.addNode(nodeId, endpoint);\n }\n\n // Start health monitoring\n this.connectionPool.startHealthCheck();\n\n // Start periodic partition map refresh\n this.partitionRouter.startPeriodicRefresh();\n\n this.initialized = true;\n\n // Wait for first partition map\n await this.waitForPartitionMap();\n }\n\n /**\n * Set authentication token\n */\n public setAuthToken(token: string): void {\n this.connectionPool.setAuthToken(token);\n }\n\n /**\n * Send directly to partition owner\n */\n public sendDirect(key: string, message: any): boolean {\n const route = this.partitionRouter.routeToConnection(key);\n if (!route) {\n logger.warn({ key }, 'No route available for key');\n return false;\n }\n\n // Add routing metadata\n const routedMessage = {\n ...message,\n _routing: {\n partitionId: this.partitionRouter.getPartitionId(key),\n mapVersion: this.partitionRouter.getMapVersion(),\n },\n };\n\n route.connection.send(serialize(routedMessage));\n return true;\n }\n\n /**\n * Send to primary node for server-side forwarding\n */\n public sendForward(message: any): boolean {\n return this.connectionPool.sendToPrimary(message);\n }\n\n /**\n * Send batch of operations with routing\n */\n public sendBatch(operations: Array<{ key: string; message: any }>): Map<string, boolean> {\n const results = new Map<string, boolean>();\n\n if (this.config.routingMode === 'direct' && this.routingActive) {\n // Group by target node\n const nodeMessages = new Map<string, any[]>();\n\n for (const { key, message } of operations) {\n const routing = this.partitionRouter.route(key);\n const nodeId = routing?.nodeId ?? 'primary';\n\n if (!nodeMessages.has(nodeId)) {\n nodeMessages.set(nodeId, []);\n }\n nodeMessages.get(nodeId)!.push({ key, message });\n }\n\n // Send to each node\n for (const [nodeId, messages] of nodeMessages) {\n let success: boolean;\n if (nodeId === 'primary') {\n success = this.connectionPool.sendToPrimary({\n type: 'OP_BATCH',\n payload: { ops: messages.map(m => m.message) },\n });\n } else {\n success = this.connectionPool.send(nodeId, {\n type: 'OP_BATCH',\n payload: { ops: messages.map(m => m.message) },\n });\n }\n\n for (const { key } of messages) {\n results.set(key, success);\n }\n }\n } else {\n // Forward all to primary\n const success = this.connectionPool.sendToPrimary({\n type: 'OP_BATCH',\n payload: { ops: operations.map(o => o.message) },\n });\n\n for (const { key } of operations) {\n results.set(key, success);\n }\n }\n\n return results;\n }\n\n /**\n * Get connection pool health status\n */\n public getHealthStatus(): Map<string, NodeHealth> {\n return this.connectionPool.getHealthStatus();\n }\n\n /**\n * Get partition router stats\n */\n public getRouterStats(): ReturnType<PartitionRouter['getStats']> {\n return this.partitionRouter.getStats();\n }\n\n /**\n * Get routing metrics for monitoring smart routing effectiveness.\n */\n public getRoutingMetrics(): RoutingMetrics {\n return { ...this.routingMetrics };\n }\n\n /**\n * Reset routing metrics counters.\n * Useful for monitoring intervals.\n */\n public resetRoutingMetrics(): void {\n this.routingMetrics.directRoutes = 0;\n this.routingMetrics.fallbackRoutes = 0;\n this.routingMetrics.partitionMisses = 0;\n this.routingMetrics.totalRoutes = 0;\n }\n\n /**\n * Check if cluster routing is active\n */\n public isRoutingActive(): boolean {\n return this.routingActive;\n }\n\n /**\n * Get list of connected nodes\n */\n public getConnectedNodes(): string[] {\n return this.connectionPool.getConnectedNodes();\n }\n\n /**\n * Check if cluster client is initialized\n */\n public isInitialized(): boolean {\n return this.initialized;\n }\n\n /**\n * Force refresh of partition map\n */\n public async refreshPartitionMap(): Promise<void> {\n await this.partitionRouter.refreshPartitionMap();\n }\n\n /**\n * Force reconnect all connections in the pool.\n */\n public forceReconnect(): void {\n this.connectionPool.close();\n this.connect().catch((err) => {\n logger.error({ err }, 'ClusterClient forceReconnect failed');\n });\n }\n\n /**\n * Shutdown cluster client (IConnectionProvider interface).\n */\n public async close(): Promise<void> {\n if (this.partitionMapRequestTimer) {\n clearTimeout(this.partitionMapRequestTimer);\n this.partitionMapRequestTimer = null;\n }\n this.partitionRouter.close();\n this.connectionPool.close();\n this.initialized = false;\n this.routingActive = false;\n logger.info('Cluster client closed');\n }\n\n // ============================================\n // Internal Access for TopGunClient\n // ============================================\n\n /**\n * Get the connection pool (for internal use)\n */\n public getConnectionPool(): ConnectionPool {\n return this.connectionPool;\n }\n\n /**\n * Get the partition router (for internal use)\n */\n public getPartitionRouter(): PartitionRouter {\n return this.partitionRouter;\n }\n\n /**\n * Get any healthy connection (IConnectionProvider interface).\n * @throws Error if not connected\n */\n public getAnyConnection(): IConnection {\n const conn = this.connectionPool.getAnyHealthyConnection();\n if (!conn?.connection) {\n throw new Error('No healthy connection available');\n }\n return conn.connection;\n }\n\n /**\n * Get any healthy connection, or null if none available.\n * Use this for optional connection checks.\n */\n public getAnyConnectionOrNull(): IConnection | null {\n const conn = this.connectionPool.getAnyHealthyConnection();\n return conn?.connection ?? null;\n }\n\n // ============================================\n // Circuit Breaker Methods\n // ============================================\n\n /**\n * Get circuit breaker state for a node.\n */\n public getCircuit(nodeId: string): CircuitState {\n let circuit = this.circuits.get(nodeId);\n if (!circuit) {\n circuit = { failures: 0, lastFailure: 0, state: 'closed' };\n this.circuits.set(nodeId, circuit);\n }\n return circuit;\n }\n\n /**\n * Check if a node can be used (circuit not open).\n */\n public canUseNode(nodeId: string): boolean {\n const circuit = this.getCircuit(nodeId);\n\n if (circuit.state === 'closed') {\n return true;\n }\n\n if (circuit.state === 'open') {\n // Check if reset timeout elapsed\n if (Date.now() - circuit.lastFailure > this.circuitBreakerConfig.resetTimeoutMs) {\n circuit.state = 'half-open';\n logger.debug({ nodeId }, 'Circuit breaker half-open, allowing test request');\n this.emit('circuit:half-open', nodeId);\n return true; // Allow one test request\n }\n return false;\n }\n\n // half-open: allow requests\n return true;\n }\n\n /**\n * Record a successful operation to a node.\n * Resets circuit breaker on success.\n */\n public recordSuccess(nodeId: string): void {\n const circuit = this.getCircuit(nodeId);\n const wasOpen = circuit.state !== 'closed';\n\n circuit.failures = 0;\n circuit.state = 'closed';\n\n if (wasOpen) {\n logger.info({ nodeId }, 'Circuit breaker closed after success');\n this.emit('circuit:closed', nodeId);\n }\n }\n\n /**\n * Record a failed operation to a node.\n * Opens circuit breaker after threshold failures.\n */\n public recordFailure(nodeId: string): void {\n const circuit = this.getCircuit(nodeId);\n circuit.failures++;\n circuit.lastFailure = Date.now();\n\n if (circuit.failures >= this.circuitBreakerConfig.failureThreshold) {\n if (circuit.state !== 'open') {\n circuit.state = 'open';\n logger.warn({ nodeId, failures: circuit.failures }, 'Circuit breaker opened');\n this.emit('circuit:open', nodeId);\n }\n }\n }\n\n /**\n * Get all circuit breaker states.\n */\n public getCircuitStates(): Map<string, CircuitState> {\n return new Map(this.circuits);\n }\n\n /**\n * Reset circuit breaker for a specific node.\n */\n public resetCircuit(nodeId: string): void {\n this.circuits.delete(nodeId);\n logger.debug({ nodeId }, 'Circuit breaker reset');\n }\n\n /**\n * Reset all circuit breakers.\n */\n public resetAllCircuits(): void {\n this.circuits.clear();\n logger.debug('All circuit breakers reset');\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private setupEventHandlers(): void {\n // Connection pool events\n this.connectionPool.on('node:connected', (nodeId: string) => {\n logger.debug({ nodeId }, 'Node connected');\n\n // Always request partition map on node connection (may be stale after reconnect).\n // Debounce to prevent flooding when multiple nodes reconnect simultaneously.\n this.debouncedPartitionMapRequest(nodeId);\n\n if (this.connectionPool.getConnectedNodes().length === 1) {\n this.emit('connected');\n }\n });\n\n this.connectionPool.on('node:disconnected', (nodeId: string, reason: string) => {\n logger.debug({ nodeId, reason }, 'Node disconnected');\n if (this.connectionPool.getConnectedNodes().length === 0) {\n this.routingActive = false;\n this.emit('disconnected', reason);\n }\n });\n\n this.connectionPool.on('node:unhealthy', (nodeId: string, reason: string) => {\n logger.warn({ nodeId, reason }, 'Node unhealthy');\n });\n\n this.connectionPool.on('error', (nodeId: string, error: Error) => {\n this.emit('error', error);\n });\n\n // Forward messages from connection pool\n this.connectionPool.on('message', (nodeId: string, data: any) => {\n this.emit('message', nodeId, data);\n });\n\n // Partition router events\n this.partitionRouter.on('partitionMap:updated', (version: number, changesCount: number) => {\n if (!this.routingActive && this.partitionRouter.hasPartitionMap()) {\n this.routingActive = true;\n logger.info({ version }, 'Direct routing activated');\n this.emit('routing:active');\n }\n this.emit('partitionMap:ready', version);\n // Emit IConnectionProvider compatible event\n this.emit('partitionMapUpdated');\n });\n\n this.partitionRouter.on('routing:miss', (key: string, expected: string, actual: string) => {\n logger.debug({ key, expected, actual }, 'Routing miss detected');\n });\n }\n\n private async waitForPartitionMap(timeoutMs: number = 10000): Promise<void> {\n if (this.partitionRouter.hasPartitionMap()) {\n this.routingActive = true;\n return;\n }\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n this.partitionRouter.off('partitionMap:updated', onUpdate);\n // Don't reject - fallback mode will be used\n logger.warn('Partition map not received, using fallback routing');\n resolve();\n }, timeoutMs);\n\n const onUpdate = () => {\n clearTimeout(timeout);\n this.partitionRouter.off('partitionMap:updated', onUpdate);\n this.routingActive = true;\n resolve();\n };\n\n this.partitionRouter.once('partitionMap:updated', onUpdate);\n });\n }\n}\n","/**\n * ConnectionPool - Manages WebSocket connections to multiple cluster nodes\n *\n * Features:\n * - Maintains connections to all known cluster nodes\n * - Automatic reconnection with exponential backoff\n * - Health monitoring and status tracking\n * - Connection lifecycle management\n */\n\nimport {\n ConnectionPoolConfig,\n DEFAULT_CONNECTION_POOL_CONFIG,\n ConnectionState,\n NodeHealth,\n} from '@topgunbuild/core';\nimport { serialize, deserialize } from '@topgunbuild/core';\nimport type { IConnection } from '../types';\nimport { WebSocketConnection } from '../connection/WebSocketConnection';\nimport { logger } from '../utils/logger';\n\nexport type ConnectionPoolEventType =\n | 'node:connected'\n | 'node:disconnected'\n | 'node:healthy'\n | 'node:unhealthy'\n | 'node:remapped'\n | 'message'\n | 'error';\n\nexport interface ConnectionPoolEvents {\n 'node:connected': (nodeId: string) => void;\n 'node:disconnected': (nodeId: string, reason: string) => void;\n 'node:healthy': (nodeId: string) => void;\n 'node:unhealthy': (nodeId: string, reason: string) => void;\n 'node:remapped': (oldId: string, newId: string) => void;\n 'message': (nodeId: string, message: any) => void;\n 'error': (nodeId: string, error: Error) => void;\n}\n\ninterface NodeConnection {\n nodeId: string;\n endpoint: string;\n socket: WebSocket | null;\n cachedConnection: WebSocketConnection | null;\n state: ConnectionState;\n lastSeen: number;\n latencyMs: number;\n reconnectAttempts: number;\n reconnectTimer: ReturnType<typeof setTimeout> | null;\n pendingMessages: Uint8Array[];\n}\n\nexport class ConnectionPool {\n private readonly listeners: Map<string, Set<(...args: any[]) => void>> = new Map();\n private readonly config: ConnectionPoolConfig;\n private readonly connections: Map<string, NodeConnection> = new Map();\n private primaryNodeId: string | null = null;\n private healthCheckTimer: ReturnType<typeof setInterval> | null = null;\n private authToken: string | null = null;\n\n constructor(config: Partial<ConnectionPoolConfig> = {}) {\n this.config = {\n ...DEFAULT_CONNECTION_POOL_CONFIG,\n ...config,\n };\n }\n\n // ============================================\n // Event Emitter Methods (browser-compatible)\n // ============================================\n\n public on(event: string, listener: (...args: any[]) => void): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n return this;\n }\n\n public off(event: string, listener: (...args: any[]) => void): this {\n this.listeners.get(event)?.delete(listener);\n return this;\n }\n\n public emit(event: string, ...args: any[]): boolean {\n const eventListeners = this.listeners.get(event);\n if (!eventListeners || eventListeners.size === 0) {\n return false;\n }\n for (const listener of eventListeners) {\n try {\n listener(...args);\n } catch (err) {\n logger.error({ event, err }, 'Error in event listener');\n }\n }\n return true;\n }\n\n public removeAllListeners(event?: string): this {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n return this;\n }\n\n /**\n * Set authentication token for all connections\n */\n public setAuthToken(token: string): void {\n this.authToken = token;\n // Re-authenticate existing connections\n for (const conn of this.connections.values()) {\n if (conn.state === 'CONNECTED') {\n this.sendAuth(conn);\n }\n }\n }\n\n /**\n * Add a node to the connection pool\n */\n public async addNode(nodeId: string, endpoint: string): Promise<void> {\n if (this.connections.has(nodeId)) {\n const existing = this.connections.get(nodeId)!;\n if (existing.endpoint !== endpoint) {\n // Endpoint changed, reconnect\n await this.removeNode(nodeId);\n } else {\n return; // Already connected\n }\n }\n\n // Check if an existing connection has the same endpoint under a different ID\n // (e.g., seed-0 needs to be remapped to the server-assigned node ID)\n for (const [existingId, existingConn] of this.connections) {\n if (existingConn.endpoint === endpoint && existingId !== nodeId) {\n this.remapNodeId(existingId, nodeId);\n return;\n }\n }\n\n const connection: NodeConnection = {\n nodeId,\n endpoint,\n socket: null,\n cachedConnection: null,\n state: 'DISCONNECTED',\n lastSeen: 0,\n latencyMs: 0,\n reconnectAttempts: 0,\n reconnectTimer: null,\n pendingMessages: [],\n };\n\n this.connections.set(nodeId, connection);\n\n // Set first node as primary\n if (!this.primaryNodeId) {\n this.primaryNodeId = nodeId;\n }\n\n await this.connect(nodeId);\n }\n\n /**\n * Remove a node from the connection pool\n */\n public async removeNode(nodeId: string): Promise<void> {\n const connection = this.connections.get(nodeId);\n if (!connection) return;\n\n // Clear reconnect timer\n if (connection.reconnectTimer) {\n clearTimeout(connection.reconnectTimer);\n connection.reconnectTimer = null;\n }\n\n // Close socket\n if (connection.socket) {\n connection.socket.onclose = null; // Prevent reconnect\n connection.socket.close();\n connection.socket = null;\n }\n\n this.connections.delete(nodeId);\n\n // Update primary if needed\n if (this.primaryNodeId === nodeId) {\n this.primaryNodeId = this.connections.size > 0\n ? this.connections.keys().next().value ?? null\n : null;\n }\n\n logger.info({ nodeId }, 'Node removed from connection pool');\n }\n\n /**\n * Remap a node from one ID to another, preserving the existing connection.\n * Used when the server-assigned node ID differs from the temporary seed ID.\n */\n public remapNodeId(oldId: string, newId: string): void {\n const connection = this.connections.get(oldId);\n if (!connection) return;\n\n // Transfer the entry under the new key\n connection.nodeId = newId;\n this.connections.delete(oldId);\n this.connections.set(newId, connection);\n\n // Update primary if the remapped node was primary\n if (this.primaryNodeId === oldId) {\n this.primaryNodeId = newId;\n }\n\n logger.info({ oldId, newId }, 'Node ID remapped');\n this.emit('node:remapped', oldId, newId);\n }\n\n /**\n * Get connection for a specific node\n */\n public getConnection(nodeId: string): IConnection | null {\n const connection = this.connections.get(nodeId);\n if (\n !connection ||\n (connection.state !== 'CONNECTED' && connection.state !== 'AUTHENTICATED') ||\n !connection.socket\n ) {\n return null;\n }\n if (!connection.cachedConnection) {\n connection.cachedConnection = new WebSocketConnection(connection.socket);\n }\n return connection.cachedConnection;\n }\n\n /**\n * Get primary connection (first/seed node)\n */\n public getPrimaryConnection(): IConnection | null {\n if (!this.primaryNodeId) return null;\n return this.getConnection(this.primaryNodeId);\n }\n\n /**\n * Get any healthy connection\n */\n public getAnyHealthyConnection(): { nodeId: string; connection: IConnection } | null {\n for (const [nodeId, conn] of this.connections) {\n if ((conn.state === 'CONNECTED' || conn.state === 'AUTHENTICATED') && conn.socket) {\n if (!conn.cachedConnection) {\n conn.cachedConnection = new WebSocketConnection(conn.socket);\n }\n return { nodeId, connection: conn.cachedConnection };\n }\n }\n return null;\n }\n\n /**\n * Send message to a specific node\n */\n public send(nodeId: string, message: any): boolean {\n const connection = this.connections.get(nodeId);\n if (!connection) {\n logger.warn({ nodeId }, 'Cannot send: node not in pool');\n return false;\n }\n\n const data = serialize(message);\n\n if (connection.state === 'AUTHENTICATED' && connection.socket?.readyState === WebSocket.OPEN) {\n connection.socket.send(data);\n return true;\n }\n\n // Queue message for later\n if (connection.pendingMessages.length < 1000) {\n connection.pendingMessages.push(data);\n return true;\n }\n\n logger.warn({ nodeId }, 'Message queue full, dropping message');\n return false;\n }\n\n /**\n * Send message to primary node\n */\n public sendToPrimary(message: any): boolean {\n if (!this.primaryNodeId) {\n logger.warn('No primary node available');\n return false;\n }\n return this.send(this.primaryNodeId, message);\n }\n\n /**\n * Get health status for all nodes\n */\n public getHealthStatus(): Map<string, NodeHealth> {\n const status = new Map<string, NodeHealth>();\n for (const [nodeId, conn] of this.connections) {\n status.set(nodeId, {\n nodeId,\n state: conn.state,\n lastSeen: conn.lastSeen,\n latencyMs: conn.latencyMs,\n reconnectAttempts: conn.reconnectAttempts,\n });\n }\n return status;\n }\n\n /**\n * Get list of connected node IDs\n */\n public getConnectedNodes(): string[] {\n return Array.from(this.connections.entries())\n .filter(([_, conn]) => conn.state === 'CONNECTED' || conn.state === 'AUTHENTICATED')\n .map(([nodeId]) => nodeId);\n }\n\n /**\n * Get all node IDs\n */\n public getAllNodes(): string[] {\n return Array.from(this.connections.keys());\n }\n\n /**\n * Check if node has an open WebSocket connection\n */\n public isNodeConnected(nodeId: string): boolean {\n const conn = this.connections.get(nodeId);\n return conn?.state === 'CONNECTED' || conn?.state === 'AUTHENTICATED';\n }\n\n /**\n * Check if connected to a specific node.\n * Alias for isNodeConnected() for IConnectionProvider compatibility.\n */\n public isConnected(nodeId: string): boolean {\n return this.isNodeConnected(nodeId);\n }\n\n /**\n * Start health monitoring\n */\n public startHealthCheck(): void {\n if (this.healthCheckTimer) return;\n\n this.healthCheckTimer = setInterval(() => {\n this.performHealthCheck();\n }, this.config.healthCheckIntervalMs);\n }\n\n /**\n * Stop health monitoring\n */\n public stopHealthCheck(): void {\n if (this.healthCheckTimer) {\n clearInterval(this.healthCheckTimer);\n this.healthCheckTimer = null;\n }\n }\n\n /**\n * Close all connections and cleanup\n */\n public close(): void {\n this.stopHealthCheck();\n\n for (const nodeId of this.connections.keys()) {\n this.removeNode(nodeId);\n }\n\n this.connections.clear();\n this.primaryNodeId = null;\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private async connect(nodeId: string): Promise<void> {\n const connection = this.connections.get(nodeId);\n if (!connection) return;\n\n if (connection.state === 'CONNECTING' || connection.state === 'CONNECTED') {\n return;\n }\n\n connection.state = 'CONNECTING';\n logger.info({ nodeId, endpoint: connection.endpoint }, 'Connecting to node');\n\n try {\n const socket = new WebSocket(connection.endpoint);\n socket.binaryType = 'arraybuffer';\n connection.socket = socket;\n\n socket.onopen = () => {\n connection.state = 'CONNECTED';\n connection.reconnectAttempts = 0;\n connection.lastSeen = Date.now();\n logger.info({ nodeId }, 'Connected to node');\n this.emit('node:connected', nodeId);\n\n // Send auth if we have token\n if (this.authToken) {\n this.sendAuth(connection);\n }\n\n // Flush pending messages after auth\n // Note: Messages will be sent after AUTH_ACK\n };\n\n socket.onmessage = (event) => {\n connection.lastSeen = Date.now();\n // Use connection.nodeId (mutable) instead of captured nodeId parameter,\n // so messages route correctly after remapNodeId() updates the ID\n this.handleMessage(connection.nodeId, event);\n };\n\n socket.onerror = (error) => {\n logger.error({ nodeId: connection.nodeId, error }, 'WebSocket error');\n this.emit('error', connection.nodeId, error instanceof Error ? error : new Error('WebSocket error'));\n };\n\n socket.onclose = () => {\n const wasConnected = connection.state === 'AUTHENTICATED';\n connection.state = 'DISCONNECTED';\n connection.socket = null;\n connection.cachedConnection = null;\n\n if (wasConnected) {\n this.emit('node:disconnected', connection.nodeId, 'Connection closed');\n }\n\n // Schedule reconnect\n this.scheduleReconnect(connection.nodeId);\n };\n\n } catch (error) {\n connection.state = 'FAILED';\n logger.error({ nodeId: connection.nodeId, error }, 'Failed to connect');\n this.scheduleReconnect(connection.nodeId);\n }\n }\n\n private sendAuth(connection: NodeConnection): void {\n if (!this.authToken || !connection.socket) return;\n\n connection.socket.send(serialize({\n type: 'AUTH',\n token: this.authToken,\n }));\n }\n\n private handleMessage(nodeId: string, event: MessageEvent): void {\n const connection = this.connections.get(nodeId);\n if (!connection) return;\n\n let message: any;\n try {\n if (event.data instanceof ArrayBuffer) {\n message = deserialize(new Uint8Array(event.data));\n } else {\n message = JSON.parse(event.data);\n }\n } catch (e) {\n logger.error({ nodeId, error: e }, 'Failed to parse message');\n return;\n }\n\n // Handle auth-related side effects (state tracking and pending message flush)\n if (message.type === 'AUTH_ACK') {\n connection.state = 'AUTHENTICATED';\n logger.info({ nodeId }, 'Authenticated with node');\n this.emit('node:healthy', nodeId);\n this.flushPendingMessages(connection);\n }\n\n if (message.type === 'AUTH_REQUIRED') {\n if (this.authToken) {\n this.sendAuth(connection);\n }\n }\n\n if (message.type === 'AUTH_FAIL') {\n logger.error({ nodeId, error: message.error }, 'Authentication failed');\n connection.state = 'FAILED';\n }\n\n if (message.type === 'PONG') {\n if (message.timestamp) {\n connection.latencyMs = Date.now() - message.timestamp;\n }\n return;\n }\n\n // Forward all messages (including auth messages) to listeners\n this.emit('message', nodeId, message);\n }\n\n private flushPendingMessages(connection: NodeConnection): void {\n if (!connection.socket || connection.state !== 'AUTHENTICATED') return;\n\n const pending = connection.pendingMessages;\n connection.pendingMessages = [];\n\n for (const data of pending) {\n if (connection.socket.readyState === WebSocket.OPEN) {\n connection.socket.send(data);\n }\n }\n\n if (pending.length > 0) {\n logger.debug({ nodeId: connection.nodeId, count: pending.length }, 'Flushed pending messages');\n }\n }\n\n private scheduleReconnect(nodeId: string): void {\n const connection = this.connections.get(nodeId);\n if (!connection) return;\n\n // Clear existing timer\n if (connection.reconnectTimer) {\n clearTimeout(connection.reconnectTimer);\n connection.reconnectTimer = null;\n }\n\n // Check max attempts\n if (connection.reconnectAttempts >= this.config.maxReconnectAttempts) {\n connection.state = 'FAILED';\n logger.error({ nodeId, attempts: connection.reconnectAttempts }, 'Max reconnect attempts reached');\n this.emit('node:unhealthy', nodeId, 'Max reconnect attempts reached');\n return;\n }\n\n // Calculate backoff delay\n const delay = Math.min(\n this.config.reconnectDelayMs * Math.pow(2, connection.reconnectAttempts),\n this.config.maxReconnectDelayMs\n );\n\n connection.state = 'RECONNECTING';\n connection.reconnectAttempts++;\n\n logger.info({ nodeId, delay, attempt: connection.reconnectAttempts }, 'Scheduling reconnect');\n\n connection.reconnectTimer = setTimeout(() => {\n connection.reconnectTimer = null;\n this.connect(nodeId);\n }, delay);\n }\n\n private performHealthCheck(): void {\n const now = Date.now();\n\n for (const [nodeId, connection] of this.connections) {\n // Skip nodes that are not authenticated\n if (connection.state !== 'AUTHENTICATED') continue;\n\n // Check staleness\n const timeSinceLastSeen = now - connection.lastSeen;\n if (timeSinceLastSeen > this.config.healthCheckIntervalMs * 3) {\n logger.warn({ nodeId, timeSinceLastSeen }, 'Node appears stale, sending ping');\n }\n\n // Send ping\n if (connection.socket?.readyState === WebSocket.OPEN) {\n connection.socket.send(serialize({\n type: 'PING',\n timestamp: now,\n }));\n }\n }\n }\n}\n","import type { IConnection } from '../types';\n\n/**\n * Ready-state constants matching the WebSocket spec values.\n * Allows callers to compare readyState without depending on the WebSocket global.\n */\nexport const ConnectionReadyState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n/**\n * Thin adapter that wraps a raw WebSocket and exposes only the IConnection\n * surface. This keeps the concrete WebSocket type out of public return types\n * while adding zero overhead.\n */\nexport class WebSocketConnection implements IConnection {\n constructor(private readonly ws: WebSocket) {}\n\n send(data: ArrayBuffer | Uint8Array | string): void {\n this.ws.send(data);\n }\n\n close(): void {\n this.ws.close();\n }\n\n get readyState(): number {\n return this.ws.readyState;\n }\n}\n","/**\n * PartitionRouter - Routes operations to the correct cluster node\n *\n * Features:\n * - Maintains local copy of partition map\n * - Routes keys to owner nodes using consistent hashing\n * - Handles stale routing with automatic refresh\n * - Supports fallback to server-side forwarding\n */\n\nimport {\n PartitionMap,\n PartitionRouterConfig,\n DEFAULT_PARTITION_ROUTER_CONFIG,\n PartitionMapMessage,\n PartitionMapDeltaMessage,\n PartitionChange,\n PARTITION_COUNT,\n hashString,\n} from '@topgunbuild/core';\nimport type { IConnection } from '../types';\nimport { ConnectionPool } from './ConnectionPool';\nimport { logger } from '../utils/logger';\n\nexport interface RoutingResult {\n nodeId: string;\n partitionId: number;\n isOwner: boolean;\n isBackup: boolean;\n}\n\nexport interface PartitionRouterEvents {\n 'partitionMap:updated': (version: number, changesCount: number) => void;\n 'partitionMap:stale': (currentVersion: number, lastRefresh: number) => void;\n 'routing:miss': (key: string, expectedOwner: string, actualOwner: string) => void;\n}\n\nexport class PartitionRouter {\n private readonly listeners: Map<string, Set<(...args: any[]) => void>> = new Map();\n private readonly config: PartitionRouterConfig;\n private readonly connectionPool: ConnectionPool;\n private partitionMap: PartitionMap | null = null;\n private lastRefreshTime: number = 0;\n private refreshTimer: ReturnType<typeof setInterval> | null = null;\n private pendingRefresh: Promise<void> | null = null;\n\n constructor(\n connectionPool: ConnectionPool,\n config: Partial<PartitionRouterConfig> = {}\n ) {\n this.connectionPool = connectionPool;\n this.config = {\n ...DEFAULT_PARTITION_ROUTER_CONFIG,\n ...config,\n };\n\n // Listen for partition map updates from any connection\n this.connectionPool.on('message', (nodeId: string, message: any) => {\n if (message.type === 'PARTITION_MAP') {\n this.handlePartitionMap(message as PartitionMapMessage);\n } else if (message.type === 'PARTITION_MAP_DELTA') {\n this.handlePartitionMapDelta(message as PartitionMapDeltaMessage);\n }\n });\n }\n\n // ============================================\n // Event Emitter Methods (browser-compatible)\n // ============================================\n\n public on(event: string, listener: (...args: any[]) => void): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n return this;\n }\n\n public off(event: string, listener: (...args: any[]) => void): this {\n this.listeners.get(event)?.delete(listener);\n return this;\n }\n\n public once(event: string, listener: (...args: any[]) => void): this {\n const wrapper = (...args: any[]) => {\n this.off(event, wrapper);\n listener(...args);\n };\n return this.on(event, wrapper);\n }\n\n public emit(event: string, ...args: any[]): boolean {\n const eventListeners = this.listeners.get(event);\n if (!eventListeners || eventListeners.size === 0) {\n return false;\n }\n for (const listener of eventListeners) {\n try {\n listener(...args);\n } catch (err) {\n logger.error({ event, err }, 'Error in event listener');\n }\n }\n return true;\n }\n\n public removeListener(event: string, listener: (...args: any[]) => void): this {\n return this.off(event, listener);\n }\n\n public removeAllListeners(event?: string): this {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n return this;\n }\n\n /**\n * Get the partition ID for a given key\n */\n public getPartitionId(key: string): number {\n return Math.abs(hashString(key)) % PARTITION_COUNT;\n }\n\n /**\n * Route a key to the owner node\n */\n public route(key: string): RoutingResult | null {\n if (!this.partitionMap) {\n return null;\n }\n\n const partitionId = this.getPartitionId(key);\n const partition = this.partitionMap.partitions.find(p => p.partitionId === partitionId);\n\n if (!partition) {\n logger.warn({ key, partitionId }, 'Partition not found in map');\n return null;\n }\n\n return {\n nodeId: partition.ownerNodeId,\n partitionId,\n isOwner: true,\n isBackup: false,\n };\n }\n\n /**\n * Route a key and get the WebSocket connection to use\n */\n public routeToConnection(key: string): { nodeId: string; connection: IConnection } | null {\n const routing = this.route(key);\n\n if (!routing) {\n // No partition map, use fallback\n if (this.config.fallbackMode === 'forward') {\n const primary = this.connectionPool.getAnyHealthyConnection();\n if (primary) {\n return primary;\n }\n }\n return null;\n }\n\n // Try to get connection to owner\n const connection = this.connectionPool.getConnection(routing.nodeId);\n if (connection) {\n return { nodeId: routing.nodeId, connection };\n }\n\n // Owner not available, try backup\n const partition = this.partitionMap!.partitions.find(p => p.partitionId === routing.partitionId);\n if (partition) {\n for (const backupId of partition.backupNodeIds) {\n const backupConnection = this.connectionPool.getConnection(backupId);\n if (backupConnection) {\n logger.debug({ key, owner: routing.nodeId, backup: backupId }, 'Using backup node');\n return { nodeId: backupId, connection: backupConnection };\n }\n }\n }\n\n // Fallback to any connection\n if (this.config.fallbackMode === 'forward') {\n return this.connectionPool.getAnyHealthyConnection();\n }\n\n return null;\n }\n\n /**\n * Get routing info for multiple keys (batch routing)\n */\n public routeBatch(keys: string[]): Map<string, RoutingResult[]> {\n const result = new Map<string, RoutingResult[]>();\n\n for (const key of keys) {\n const routing = this.route(key);\n if (routing) {\n const nodeId = routing.nodeId;\n if (!result.has(nodeId)) {\n result.set(nodeId, []);\n }\n result.get(nodeId)!.push({ ...routing, key } as any);\n }\n }\n\n return result;\n }\n\n /**\n * Get all partitions owned by a specific node\n */\n public getPartitionsForNode(nodeId: string): number[] {\n if (!this.partitionMap) return [];\n\n return this.partitionMap.partitions\n .filter(p => p.ownerNodeId === nodeId)\n .map(p => p.partitionId);\n }\n\n /**\n * Get current partition map version\n */\n public getMapVersion(): number {\n return this.partitionMap?.version ?? 0;\n }\n\n /**\n * Check if partition map is available\n */\n public hasPartitionMap(): boolean {\n return this.partitionMap !== null;\n }\n\n /**\n * Get owner node for a key.\n * Returns null if partition map is not available.\n */\n public getOwner(key: string): string | null {\n if (!this.partitionMap) return null;\n\n const partitionId = this.getPartitionId(key);\n const partition = this.partitionMap.partitions.find(p => p.partitionId === partitionId);\n\n return partition?.ownerNodeId ?? null;\n }\n\n /**\n * Get backup nodes for a key.\n * Returns empty array if partition map is not available.\n */\n public getBackups(key: string): string[] {\n if (!this.partitionMap) return [];\n\n const partitionId = this.getPartitionId(key);\n const partition = this.partitionMap.partitions.find(p => p.partitionId === partitionId);\n\n return partition?.backupNodeIds ?? [];\n }\n\n /**\n * Get the full partition map.\n * Returns null if not available.\n */\n public getMap(): PartitionMap | null {\n return this.partitionMap;\n }\n\n /**\n * Update entire partition map.\n * Only accepts newer versions.\n */\n public updateMap(map: PartitionMap): boolean {\n if (this.partitionMap && map.version <= this.partitionMap.version) {\n return false;\n }\n\n this.partitionMap = map;\n this.lastRefreshTime = Date.now();\n\n // Update connection pool with node endpoints\n this.updateConnectionPool(map);\n\n const changesCount = map.partitions.length;\n logger.info({\n version: map.version,\n partitions: map.partitionCount,\n nodes: map.nodes.length\n }, 'Partition map updated via updateMap');\n\n this.emit('partitionMap:updated', map.version, changesCount);\n return true;\n }\n\n /**\n * Update a single partition (for delta updates).\n */\n public updatePartition(partitionId: number, owner: string, backups: string[]): void {\n if (!this.partitionMap) return;\n\n const partition = this.partitionMap.partitions.find(p => p.partitionId === partitionId);\n if (partition) {\n partition.ownerNodeId = owner;\n partition.backupNodeIds = backups;\n }\n }\n\n /**\n * Check if partition map is stale\n */\n public isMapStale(): boolean {\n if (!this.partitionMap) return true;\n\n const now = Date.now();\n return (now - this.lastRefreshTime) > this.config.maxMapStalenessMs;\n }\n\n /**\n * Request fresh partition map from server\n */\n public async refreshPartitionMap(): Promise<void> {\n if (this.pendingRefresh) {\n return this.pendingRefresh;\n }\n\n this.pendingRefresh = this.doRefreshPartitionMap();\n\n try {\n await this.pendingRefresh;\n } finally {\n this.pendingRefresh = null;\n }\n }\n\n /**\n * Start periodic partition map refresh\n */\n public startPeriodicRefresh(): void {\n if (this.refreshTimer) return;\n\n this.refreshTimer = setInterval(() => {\n if (this.isMapStale()) {\n this.emit('partitionMap:stale', this.getMapVersion(), this.lastRefreshTime);\n this.refreshPartitionMap().catch(err => {\n logger.error({ error: err }, 'Failed to refresh partition map');\n });\n }\n }, this.config.mapRefreshIntervalMs);\n }\n\n /**\n * Stop periodic refresh\n */\n public stopPeriodicRefresh(): void {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = null;\n }\n }\n\n /**\n * Handle NOT_OWNER error from server\n */\n public handleNotOwnerError(key: string, actualOwner: string, newMapVersion: number): void {\n const routing = this.route(key);\n const expectedOwner = routing?.nodeId ?? 'unknown';\n\n this.emit('routing:miss', key, expectedOwner, actualOwner);\n\n // If server has newer map, request it\n if (newMapVersion > this.getMapVersion()) {\n this.refreshPartitionMap().catch(err => {\n logger.error({ error: err }, 'Failed to refresh partition map after NOT_OWNER');\n });\n }\n }\n\n /**\n * Get statistics about routing\n */\n public getStats(): {\n mapVersion: number;\n partitionCount: number;\n nodeCount: number;\n lastRefresh: number;\n isStale: boolean;\n } {\n return {\n mapVersion: this.getMapVersion(),\n partitionCount: this.partitionMap?.partitionCount ?? 0,\n nodeCount: this.partitionMap?.nodes.length ?? 0,\n lastRefresh: this.lastRefreshTime,\n isStale: this.isMapStale(),\n };\n }\n\n /**\n * Cleanup resources\n */\n public close(): void {\n this.stopPeriodicRefresh();\n this.partitionMap = null;\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private handlePartitionMap(message: PartitionMapMessage): void {\n const newMap = message.payload;\n\n // Only accept newer versions\n if (this.partitionMap && newMap.version <= this.partitionMap.version) {\n logger.debug({\n current: this.partitionMap.version,\n received: newMap.version\n }, 'Ignoring older partition map');\n return;\n }\n\n this.partitionMap = newMap;\n this.lastRefreshTime = Date.now();\n\n // Update connection pool with node endpoints\n this.updateConnectionPool(newMap);\n\n const changesCount = newMap.partitions.length;\n logger.info({\n version: newMap.version,\n partitions: newMap.partitionCount,\n nodes: newMap.nodes.length\n }, 'Partition map updated');\n\n this.emit('partitionMap:updated', newMap.version, changesCount);\n }\n\n private handlePartitionMapDelta(message: PartitionMapDeltaMessage): void {\n const delta = message.payload;\n\n // Must have base map and correct previous version\n if (!this.partitionMap) {\n logger.warn('Received delta but no base map, requesting full map');\n this.refreshPartitionMap();\n return;\n }\n\n if (delta.previousVersion !== this.partitionMap.version) {\n logger.warn({\n expected: this.partitionMap.version,\n received: delta.previousVersion\n }, 'Delta version mismatch, requesting full map');\n this.refreshPartitionMap();\n return;\n }\n\n // Apply changes\n for (const change of delta.changes) {\n this.applyPartitionChange(change);\n }\n\n this.partitionMap.version = delta.version;\n this.lastRefreshTime = Date.now();\n\n logger.info({\n version: delta.version,\n changes: delta.changes.length\n }, 'Applied partition map delta');\n\n this.emit('partitionMap:updated', delta.version, delta.changes.length);\n }\n\n private applyPartitionChange(change: PartitionChange): void {\n if (!this.partitionMap) return;\n\n const partition = this.partitionMap.partitions.find(p => p.partitionId === change.partitionId);\n if (partition) {\n partition.ownerNodeId = change.newOwner;\n // Backups would also be updated but simplified here\n }\n }\n\n private updateConnectionPool(map: PartitionMap): void {\n // Add new nodes\n for (const node of map.nodes) {\n if (node.status === 'ACTIVE' || node.status === 'JOINING') {\n this.connectionPool.addNode(node.nodeId, node.endpoints.websocket);\n }\n }\n\n // Remove nodes that are no longer in the map\n const currentNodeIds = new Set(map.nodes.map(n => n.nodeId));\n for (const nodeId of this.connectionPool.getAllNodes()) {\n if (!currentNodeIds.has(nodeId)) {\n this.connectionPool.removeNode(nodeId);\n }\n }\n }\n\n private async doRefreshPartitionMap(): Promise<void> {\n logger.debug('Requesting partition map refresh');\n\n // Send request to any connected node\n const sent = this.connectionPool.sendToPrimary({\n type: 'PARTITION_MAP_REQUEST',\n payload: {\n currentVersion: this.getMapVersion(),\n },\n });\n\n if (!sent) {\n throw new Error('No connection available to request partition map');\n }\n\n // Wait for response (handled via message event)\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.removeListener('partitionMap:updated', onUpdate);\n reject(new Error('Partition map refresh timeout'));\n }, 5000);\n\n const onUpdate = () => {\n clearTimeout(timeout);\n this.removeListener('partitionMap:updated', onUpdate);\n resolve();\n };\n\n this.once('partitionMap:updated', onUpdate);\n });\n }\n}\n","import type {\n IConnectionProvider,\n IConnection,\n ConnectionProviderEvent,\n ConnectionEventHandler,\n SingleServerProviderConfig,\n} from '../types';\nimport { WebSocketConnection } from './WebSocketConnection';\nimport { logger } from '../utils/logger';\n\n/**\n * Default configuration values for SingleServerProvider.\n */\nconst DEFAULT_CONFIG = {\n maxReconnectAttempts: 10,\n reconnectDelayMs: 1000,\n backoffMultiplier: 2,\n maxReconnectDelayMs: 30000,\n};\n\n/**\n * SingleServerProvider implements IConnectionProvider for single-server mode.\n *\n * This is an adapter that wraps direct WebSocket connection handling,\n * providing the same interface used by ClusterClient for multi-node mode.\n */\nexport class SingleServerProvider implements IConnectionProvider {\n private readonly url: string;\n private readonly config: Required<SingleServerProviderConfig>;\n private ws: WebSocket | null = null;\n private reconnectAttempts: number = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private isClosing: boolean = false;\n private listeners: Map<ConnectionProviderEvent, Set<ConnectionEventHandler>> = new Map();\n private onlineHandler: (() => void) | null = null;\n private offlineHandler: (() => void) | null = null;\n\n constructor(config: SingleServerProviderConfig) {\n this.url = config.url;\n this.config = {\n url: config.url,\n maxReconnectAttempts: config.maxReconnectAttempts ?? DEFAULT_CONFIG.maxReconnectAttempts,\n reconnectDelayMs: config.reconnectDelayMs ?? DEFAULT_CONFIG.reconnectDelayMs,\n backoffMultiplier: config.backoffMultiplier ?? DEFAULT_CONFIG.backoffMultiplier,\n maxReconnectDelayMs: config.maxReconnectDelayMs ?? DEFAULT_CONFIG.maxReconnectDelayMs,\n listenNetworkEvents: config.listenNetworkEvents ?? true,\n };\n\n this.setupNetworkListeners();\n }\n\n /**\n * Connect to the WebSocket server.\n */\n async connect(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n return; // Already connected\n }\n\n // Skip connection attempt when browser reports no network —\n // the 'online' event listener will trigger forceReconnect() when network returns\n if (typeof globalThis.navigator !== 'undefined' && globalThis.navigator.onLine === false) {\n throw new Error('Browser is offline — skipping connection attempt');\n }\n\n this.isClosing = false;\n\n return new Promise((resolve, reject) => {\n try {\n this.ws = new WebSocket(this.url);\n this.ws.binaryType = 'arraybuffer';\n\n this.ws.onopen = () => {\n this.reconnectAttempts = 0;\n logger.info({ url: this.url }, 'SingleServerProvider connected');\n this.emit('connected', 'default');\n resolve();\n };\n\n this.ws.onerror = (error) => {\n logger.error({ err: error, url: this.url }, 'SingleServerProvider WebSocket error');\n this.emit('error', error);\n // Don't reject here - wait for onclose\n };\n\n this.ws.onclose = (event) => {\n logger.info({ url: this.url, code: event.code }, 'SingleServerProvider disconnected');\n this.emit('disconnected', 'default');\n\n if (!this.isClosing) {\n this.scheduleReconnect();\n }\n };\n\n this.ws.onmessage = (event) => {\n this.emit('message', 'default', event.data);\n };\n\n // Set up initial connection timeout\n const timeoutId = setTimeout(() => {\n if (this.ws && this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.close();\n reject(new Error(`Connection timeout to ${this.url}`));\n }\n }, this.config.reconnectDelayMs * 5); // 5x initial delay as connection timeout\n\n // Clear timeout on successful connection\n const originalOnOpen = this.ws.onopen;\n const wsRef = this.ws;\n this.ws.onopen = (ev) => {\n clearTimeout(timeoutId);\n if (originalOnOpen) {\n originalOnOpen.call(wsRef, ev);\n }\n };\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Get connection for a specific key.\n * In single-server mode, key is ignored.\n */\n getConnection(_key: string): IConnection {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected');\n }\n return new WebSocketConnection(this.ws);\n }\n\n /**\n * Get any available connection.\n */\n getAnyConnection(): IConnection {\n return this.getConnection('');\n }\n\n /**\n * Check if connected.\n */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Get connected node IDs.\n * Single-server mode returns ['default'] when connected.\n */\n getConnectedNodes(): string[] {\n return this.isConnected() ? ['default'] : [];\n }\n\n /**\n * Subscribe to connection events.\n */\n on(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n /**\n * Unsubscribe from connection events.\n */\n off(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n this.listeners.get(event)?.delete(handler);\n }\n\n /**\n * Send data via the WebSocket connection.\n * In single-server mode, key parameter is ignored.\n */\n send(data: ArrayBuffer | Uint8Array, _key?: string): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected');\n }\n this.ws.send(data);\n }\n\n /**\n * Close the WebSocket connection.\n */\n async close(): Promise<void> {\n this.isClosing = true;\n this.teardownNetworkListeners();\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.ws) {\n // Remove onclose handler to prevent reconnect\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n\n if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.close();\n }\n this.ws = null;\n }\n\n logger.info({ url: this.url }, 'SingleServerProvider closed');\n }\n\n /**\n * Emit an event to all listeners.\n */\n private emit(event: ConnectionProviderEvent, ...args: any[]): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(...args);\n } catch (err) {\n logger.error({ err, event }, 'Error in SingleServerProvider event handler');\n }\n }\n }\n }\n\n /**\n * Schedule a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n // When browser reports offline, don't schedule reconnects —\n // the 'online' event listener will call forceReconnect() when network returns\n if (typeof globalThis.navigator !== 'undefined' && globalThis.navigator.onLine === false\n && this.config.listenNetworkEvents) {\n logger.info({ url: this.url }, 'Browser offline — waiting for online event instead of polling');\n return;\n }\n\n if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {\n logger.error(\n { attempts: this.reconnectAttempts, url: this.url },\n 'SingleServerProvider max reconnect attempts reached'\n );\n this.emit('error', new Error('Max reconnection attempts reached'));\n return;\n }\n\n const delay = this.calculateBackoffDelay();\n logger.info(\n { delay, attempt: this.reconnectAttempts, url: this.url },\n `SingleServerProvider scheduling reconnect in ${delay}ms`\n );\n\n this.reconnectTimer = setTimeout(async () => {\n this.reconnectTimer = null;\n this.reconnectAttempts++;\n\n try {\n await this.connect();\n this.emit('reconnected', 'default');\n } catch (error) {\n logger.error({ err: error }, 'SingleServerProvider reconnection failed');\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n /**\n * Calculate backoff delay with exponential increase.\n */\n private calculateBackoffDelay(): number {\n const { reconnectDelayMs, backoffMultiplier, maxReconnectDelayMs } = this.config;\n let delay = reconnectDelayMs * Math.pow(backoffMultiplier, this.reconnectAttempts);\n delay = Math.min(delay, maxReconnectDelayMs);\n\n // Add jitter (0.5x to 1.5x)\n delay = delay * (0.5 + Math.random());\n\n return Math.floor(delay);\n }\n\n /**\n * Force-close the current WebSocket and immediately schedule reconnection.\n * Unlike close(), this does NOT set isClosing and preserves reconnect behavior.\n * Resets the reconnect counter so the full backoff budget is available.\n *\n * Critically, this does NOT wait for the TCP close handshake (which can\n * hang 20+ seconds on a dead network). Instead it strips all handlers from\n * the old WebSocket, fires a best-effort close(), nulls the reference, and\n * schedules reconnect right away.\n */\n forceReconnect(): void {\n this.reconnectAttempts = 0;\n this.isClosing = false;\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.ws) {\n // Detach all handlers so the lingering socket cannot fire events\n this.ws.onopen = null;\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n\n // Best-effort close — don't await the TCP handshake\n try {\n this.ws.close();\n } catch {\n // Ignore errors on already-dead sockets\n }\n this.ws = null;\n }\n\n // Emit disconnected so SyncEngine knows connection is down NOW\n this.emit('disconnected', 'default');\n\n // Schedule reconnect immediately (delay 0 → first attempt uses jittered base delay)\n this.scheduleReconnect();\n }\n\n /**\n * Get the WebSocket URL this provider connects to.\n */\n getUrl(): string {\n return this.url;\n }\n\n /**\n * Get current reconnection attempt count.\n */\n getReconnectAttempts(): number {\n return this.reconnectAttempts;\n }\n\n /**\n * Reset reconnection counter.\n * Called externally after successful authentication.\n */\n resetReconnectAttempts(): void {\n this.reconnectAttempts = 0;\n }\n\n /**\n * Listen for browser 'online' event to trigger instant reconnect\n * when network comes back. Only active in browser environments.\n */\n private setupNetworkListeners(): void {\n if (!this.config.listenNetworkEvents) return;\n if (typeof globalThis.addEventListener !== 'function') return;\n\n this.onlineHandler = () => {\n if (this.isClosing) return;\n if (this.isConnected()) return;\n\n logger.info({ url: this.url }, 'Network online detected — forcing reconnect');\n this.forceReconnect();\n };\n\n this.offlineHandler = () => {\n if (this.isClosing) return;\n if (!this.isConnected()) return;\n\n logger.info({ url: this.url }, 'Network offline detected — disconnecting immediately');\n this.forceReconnect();\n };\n\n globalThis.addEventListener('online', this.onlineHandler);\n globalThis.addEventListener('offline', this.offlineHandler);\n }\n\n /**\n * Remove browser network event listeners.\n */\n private teardownNetworkListeners(): void {\n if (typeof globalThis.removeEventListener === 'function') {\n if (this.onlineHandler) {\n globalThis.removeEventListener('online', this.onlineHandler);\n this.onlineHandler = null;\n }\n if (this.offlineHandler) {\n globalThis.removeEventListener('offline', this.offlineHandler);\n this.offlineHandler = null;\n }\n }\n }\n}\n","import type { LWWRecord, ORMapRecord } from '@topgunbuild/core';\nimport type { IStorageAdapter, OpLogEntry } from '../IStorageAdapter';\nimport { openDB } from 'idb';\nimport type { IDBPDatabase } from 'idb';\n\n/**\n * Represents an operation queued before IndexedDB is ready.\n */\ninterface QueuedOperation {\n type: 'put' | 'remove' | 'setMeta' | 'appendOpLog' | 'markOpsSynced' | 'batchPut';\n args: any[];\n resolve: (value: any) => void;\n reject: (error: any) => void;\n}\n\n/**\n * Non-blocking IndexedDB adapter that allows immediate use before initialization completes.\n *\n * Operations are queued in memory and replayed once IndexedDB is ready.\n * This enables true \"memory-first\" behavior where the UI can render immediately\n * without waiting for IndexedDB to initialize (which can take 50-500ms).\n */\nexport class IDBAdapter implements IStorageAdapter {\n private dbPromise?: Promise<IDBPDatabase>;\n private db?: IDBPDatabase;\n private isReady = false;\n private operationQueue: QueuedOperation[] = [];\n private initPromise?: Promise<void>;\n\n /**\n * Initializes IndexedDB in the background.\n * Returns immediately - does NOT block on IndexedDB being ready.\n * Use waitForReady() if you need to ensure initialization is complete.\n */\n async initialize(dbName: string): Promise<void> {\n // Start initialization but don't await it\n this.initPromise = this.initializeInternal(dbName);\n // Return immediately - non-blocking!\n }\n\n /**\n * Internal initialization that actually opens IndexedDB.\n */\n private async initializeInternal(dbName: string): Promise<void> {\n try {\n this.dbPromise = openDB(dbName, 2, {\n upgrade(db) {\n if (!db.objectStoreNames.contains('kv_store')) {\n db.createObjectStore('kv_store', { keyPath: 'key' });\n }\n if (!db.objectStoreNames.contains('op_log')) {\n db.createObjectStore('op_log', { keyPath: 'id', autoIncrement: true });\n }\n if (!db.objectStoreNames.contains('meta_store')) {\n db.createObjectStore('meta_store', { keyPath: 'key' });\n }\n },\n });\n\n this.db = await this.dbPromise;\n this.isReady = true;\n\n // Replay queued operations\n await this.flushQueue();\n } catch (error) {\n // Re-throw to allow error handling\n throw error;\n }\n }\n\n /**\n * Waits for IndexedDB to be fully initialized.\n * Call this if you need guaranteed persistence before proceeding.\n */\n async waitForReady(): Promise<void> {\n if (this.isReady) return;\n if (this.initPromise) {\n await this.initPromise;\n }\n }\n\n /**\n * Flushes all queued operations once IndexedDB is ready.\n */\n private async flushQueue(): Promise<void> {\n const queue = this.operationQueue;\n this.operationQueue = [];\n\n for (const op of queue) {\n try {\n let result: any;\n switch (op.type) {\n case 'put':\n result = await this.putInternal(op.args[0], op.args[1]);\n break;\n case 'remove':\n result = await this.removeInternal(op.args[0]);\n break;\n case 'setMeta':\n result = await this.setMetaInternal(op.args[0], op.args[1]);\n break;\n case 'appendOpLog':\n result = await this.appendOpLogInternal(op.args[0]);\n break;\n case 'markOpsSynced':\n result = await this.markOpsSyncedInternal(op.args[0]);\n break;\n case 'batchPut':\n result = await this.batchPutInternal(op.args[0]);\n break;\n }\n op.resolve(result);\n } catch (error) {\n op.reject(error);\n }\n }\n }\n\n /**\n * Queues an operation if not ready, or executes immediately if ready.\n */\n private queueOrExecute<T>(\n type: QueuedOperation['type'],\n args: any[],\n executor: () => Promise<T>\n ): Promise<T> {\n if (this.isReady) {\n return executor();\n }\n\n return new Promise<T>((resolve, reject) => {\n this.operationQueue.push({ type, args, resolve, reject });\n });\n }\n\n async close(): Promise<void> {\n if (this.db) {\n this.db.close();\n }\n }\n\n // ============================================\n // Read Operations - Wait for ready\n // ============================================\n\n async get<V>(key: string): Promise<LWWRecord<V> | ORMapRecord<V>[] | any | undefined> {\n // Read operations must wait for DB to be ready\n await this.waitForReady();\n const result = await this.db?.get('kv_store', key);\n return result?.value;\n }\n\n async getMeta(key: string): Promise<any> {\n await this.waitForReady();\n const result = await this.db?.get('meta_store', key);\n return result?.value;\n }\n\n async getPendingOps(): Promise<OpLogEntry[]> {\n await this.waitForReady();\n const all = await this.db?.getAll('op_log');\n return all?.filter((op: any) => op.synced === 0) || [];\n }\n\n async getAllKeys(): Promise<string[]> {\n await this.waitForReady();\n return (await this.db?.getAllKeys('kv_store')) as string[] || [];\n }\n\n // ============================================\n // Write Operations - Queue if not ready\n // ============================================\n\n async put(key: string, value: any): Promise<void> {\n return this.queueOrExecute('put', [key, value], () => this.putInternal(key, value));\n }\n\n private async putInternal(key: string, value: any): Promise<void> {\n await this.db?.put('kv_store', { key, value });\n }\n\n async remove(key: string): Promise<void> {\n return this.queueOrExecute('remove', [key], () => this.removeInternal(key));\n }\n\n private async removeInternal(key: string): Promise<void> {\n await this.db?.delete('kv_store', key);\n }\n\n async setMeta(key: string, value: any): Promise<void> {\n return this.queueOrExecute('setMeta', [key, value], () => this.setMetaInternal(key, value));\n }\n\n private async setMetaInternal(key: string, value: any): Promise<void> {\n await this.db?.put('meta_store', { key, value });\n }\n\n async batchPut(entries: Map<string, any>): Promise<void> {\n return this.queueOrExecute('batchPut', [entries], () => this.batchPutInternal(entries));\n }\n\n private async batchPutInternal(entries: Map<string, any>): Promise<void> {\n const tx = this.db?.transaction('kv_store', 'readwrite');\n if (!tx) return;\n\n await Promise.all(\n Array.from(entries.entries()).map(([key, value]) =>\n tx.store.put({ key, value })\n )\n );\n await tx.done;\n }\n\n async appendOpLog(entry: any): Promise<number> {\n return this.queueOrExecute('appendOpLog', [entry], () => this.appendOpLogInternal(entry));\n }\n\n private async appendOpLogInternal(entry: any): Promise<number> {\n const entryToSave = { ...entry, synced: 0 };\n return await this.db?.add('op_log', entryToSave) as number;\n }\n\n async markOpsSynced(lastId: number): Promise<void> {\n return this.queueOrExecute('markOpsSynced', [lastId], () => this.markOpsSyncedInternal(lastId));\n }\n\n private async markOpsSyncedInternal(lastId: number): Promise<void> {\n const tx = this.db?.transaction('op_log', 'readwrite');\n if (!tx) return;\n\n let cursor = await tx.store.openCursor();\n while (cursor) {\n if (cursor.value.id <= lastId) {\n const update = { ...cursor.value, synced: 1 };\n await cursor.update(update);\n }\n cursor = await cursor.continue();\n }\n await tx.done;\n }\n}\n\n","import { TopGunClient } from './TopGunClient';\nimport { IDBAdapter } from './adapters/IDBAdapter';\nimport type { IStorageAdapter } from './IStorageAdapter';\nimport { LWWMap } from '@topgunbuild/core';\nimport type { LWWRecord } from '@topgunbuild/core';\nimport { logger } from './utils/logger';\n\nexport interface TopGunConfig {\n sync: string;\n persist: 'indexeddb' | IStorageAdapter;\n nodeId?: string;\n}\n\n// Generic schema type\nexport type TopGunSchema = Record<string, any>;\n\nconst handler: ProxyHandler<TopGun<any>> = {\n get(target, prop, receiver) {\n if (prop in target || typeof prop === 'symbol') {\n return Reflect.get(target, prop, receiver);\n }\n if (typeof prop === 'string') {\n return target.collection(prop);\n }\n return undefined;\n }\n};\n\nexport class TopGun<T extends TopGunSchema = any> {\n private client: TopGunClient;\n private initPromise: Promise<void>;\n \n // Allow property access for collections based on Schema T\n [key: string]: any;\n\n constructor(config: TopGunConfig) {\n let storage: IStorageAdapter;\n\n if (config.persist === 'indexeddb') {\n storage = new IDBAdapter();\n } else if (typeof config.persist === 'object') {\n storage = config.persist;\n } else {\n throw new Error(`Unsupported persist option: ${config.persist}`);\n }\n\n this.client = new TopGunClient({\n serverUrl: config.sync,\n storage,\n nodeId: config.nodeId\n });\n\n // Start client initialization (non-blocking)\n // The IDBAdapter now initializes in the background and queues operations\n this.initPromise = this.client.start().catch(err => {\n logger.error({ err, context: 'client_start' }, 'Failed to start TopGun client');\n throw err;\n });\n\n return new Proxy(this, handler);\n }\n\n /**\n * Waits for the storage adapter to be fully initialized.\n * This is optional - you can start using the database immediately.\n * Operations are queued in memory and persisted once IndexedDB is ready.\n */\n public async waitForReady(): Promise<void> {\n await this.initPromise;\n }\n\n public collection<K extends keyof T & string>(name: K): CollectionWrapper<T[K]> {\n // Explicitly type the map\n const map = this.client.getMap<string, T[K]>(name);\n return new CollectionWrapper<T[K]>(map);\n }\n}\n\nexport class CollectionWrapper<ItemType = any> {\n private map: LWWMap<string, ItemType>;\n\n constructor(map: LWWMap<string, ItemType>) {\n this.map = map;\n }\n\n /**\n * Sets an item in the collection. \n * The item MUST have an 'id' or '_id' field.\n */\n async set(value: ItemType): Promise<ItemType> {\n const v = value as any;\n const key = v.id || v._id;\n if (!key) {\n throw new Error('Object must have an \"id\" or \"_id\" property to be saved in a collection.');\n }\n \n // LWWMap.set is synchronous in updating memory and queueing ops,\n // but we return a Promise to match typical async DB APIs.\n this.map.set(key, value);\n return Promise.resolve(value); \n }\n \n /**\n * Retrieves an item by ID.\n * Returns the value directly (unwrapped from CRDT record).\n */\n get(key: string): ItemType | undefined {\n return this.map.get(key);\n }\n\n /**\n * Get the raw LWWRecord (including metadata like timestamp).\n */\n getRecord(key: string): LWWRecord<ItemType> | undefined {\n return this.map.getRecord(key);\n }\n\n // Expose raw map if needed for advanced usage\n get raw() {\n return this.map;\n }\n}\n","import { serialize, deserialize } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport class EncryptionManager {\n private static ALGORITHM = 'AES-GCM';\n private static IV_LENGTH = 12;\n\n /**\n * Encrypts data using AES-GCM.\n * Serializes data to MessagePack before encryption.\n */\n static async encrypt(key: CryptoKey, data: any): Promise<{ iv: Uint8Array; data: Uint8Array }> {\n const encoded = serialize(data);\n\n // Generate IV\n const iv = window.crypto.getRandomValues(new Uint8Array(EncryptionManager.IV_LENGTH));\n\n // Encrypt\n const ciphertext = await window.crypto.subtle.encrypt(\n {\n name: EncryptionManager.ALGORITHM,\n iv: iv,\n },\n key,\n encoded as any\n );\n\n return {\n iv,\n data: new Uint8Array(ciphertext),\n };\n }\n\n /**\n * Decrypts AES-GCM encrypted data.\n * Deserializes from MessagePack after decryption.\n */\n static async decrypt(key: CryptoKey, record: { iv: Uint8Array; data: Uint8Array }): Promise<any> {\n try {\n const plaintextBuffer = await window.crypto.subtle.decrypt(\n {\n name: EncryptionManager.ALGORITHM,\n iv: record.iv as any,\n },\n key,\n record.data as any\n );\n\n return deserialize(new Uint8Array(plaintextBuffer));\n } catch (err) {\n logger.error({ err, context: 'decryption' }, 'Decryption failed');\n throw new Error('Failed to decrypt data: ' + err);\n }\n }\n}\n","import { IStorageAdapter, OpLogEntry } from '../IStorageAdapter';\nimport { EncryptionManager } from '../crypto/EncryptionManager';\n\n/**\n * Wraps an underlying storage adapter and encrypts data at rest using AES-GCM.\n */\nexport class EncryptedStorageAdapter implements IStorageAdapter {\n constructor(\n private wrapped: IStorageAdapter,\n private key: CryptoKey\n ) { }\n\n async initialize(dbName: string): Promise<void> {\n return this.wrapped.initialize(dbName);\n }\n\n async close(): Promise<void> {\n return this.wrapped.close();\n }\n\n // --- KV Operations ---\n\n async get<V>(key: string): Promise<V | any | undefined> {\n const raw = await this.wrapped.get<any>(key);\n\n if (!raw) {\n return undefined;\n }\n\n // Check if it looks like an encrypted record\n // We expect { iv: Uint8Array, data: Uint8Array }\n // Note: In a real app we might want a stricter check or a version tag.\n if (this.isEncryptedRecord(raw)) {\n try {\n return await EncryptionManager.decrypt(this.key, raw);\n } catch (e) {\n // Fallback for migration or corruption?\n // For now, fail loud as per spec.\n throw e;\n }\n }\n\n // Return raw if not encrypted (backwards compatibility during dev, or unencrypted data)\n return raw;\n }\n\n async put(key: string, value: any): Promise<void> {\n const encrypted = await EncryptionManager.encrypt(this.key, value);\n // Store as plain object to be compatible with structured clone algorithm of IndexedDB\n const storedValue = {\n iv: encrypted.iv,\n data: encrypted.data\n };\n return this.wrapped.put(key, storedValue);\n }\n\n async remove(key: string): Promise<void> {\n return this.wrapped.remove(key);\n }\n\n // --- Metadata ---\n\n async getMeta(key: string): Promise<any> {\n const raw = await this.wrapped.getMeta(key);\n if (!raw) return undefined;\n\n if (this.isEncryptedRecord(raw)) {\n return EncryptionManager.decrypt(this.key, raw);\n }\n return raw;\n }\n\n async setMeta(key: string, value: any): Promise<void> {\n const encrypted = await EncryptionManager.encrypt(this.key, value);\n return this.wrapped.setMeta(key, {\n iv: encrypted.iv,\n data: encrypted.data\n });\n }\n\n // --- Batch ---\n\n async batchPut(entries: Map<string, any>): Promise<void> {\n const encryptedEntries = new Map<string, any>();\n\n for (const [key, value] of entries.entries()) {\n const encrypted = await EncryptionManager.encrypt(this.key, value);\n encryptedEntries.set(key, {\n iv: encrypted.iv,\n data: encrypted.data\n });\n }\n\n return this.wrapped.batchPut(encryptedEntries);\n }\n\n // --- OpLog ---\n\n async appendOpLog(entry: Omit<OpLogEntry, 'id'>): Promise<number> {\n // Encrypt sensitive fields: value, record, orRecord\n const encryptedEntry = { ...entry };\n\n if (entry.value !== undefined) {\n const enc = await EncryptionManager.encrypt(this.key, entry.value);\n encryptedEntry.value = { iv: enc.iv, data: enc.data };\n }\n\n if (entry.record !== undefined) {\n const enc = await EncryptionManager.encrypt(this.key, entry.record);\n encryptedEntry.record = { iv: enc.iv, data: enc.data } as any;\n }\n\n if (entry.orRecord !== undefined) {\n const enc = await EncryptionManager.encrypt(this.key, entry.orRecord);\n encryptedEntry.orRecord = { iv: enc.iv, data: enc.data } as any;\n }\n\n // Note: 'key', 'op', 'mapName', 'orTag', 'hlc', 'synced' remain plaintext for indexing\n\n return this.wrapped.appendOpLog(encryptedEntry);\n }\n\n async getPendingOps(): Promise<OpLogEntry[]> {\n const ops = await this.wrapped.getPendingOps();\n\n // Decrypt in place\n // We map concurrently for performance\n return Promise.all(ops.map(async op => {\n const decryptedOp = { ...op };\n\n if (this.isEncryptedRecord(op.value)) {\n decryptedOp.value = await EncryptionManager.decrypt(this.key, op.value);\n }\n\n if (this.isEncryptedRecord(op.record)) {\n decryptedOp.record = await EncryptionManager.decrypt(this.key, op.record as any);\n }\n\n if (this.isEncryptedRecord(op.orRecord)) {\n decryptedOp.orRecord = await EncryptionManager.decrypt(this.key, op.orRecord as any);\n }\n\n return decryptedOp;\n }));\n }\n\n async markOpsSynced(lastId: number): Promise<void> {\n return this.wrapped.markOpsSynced(lastId);\n }\n\n // --- Iteration ---\n\n async getAllKeys(): Promise<string[]> {\n return this.wrapped.getAllKeys();\n }\n\n // --- Helpers ---\n\n private isEncryptedRecord(data: any): data is { iv: Uint8Array, data: Uint8Array } {\n return data &&\n typeof data === 'object' &&\n data.iv instanceof Uint8Array &&\n data.data instanceof Uint8Array;\n }\n}\n","import { SyncEngine } from './SyncEngine';\nimport { TopGunClient, DEFAULT_CLUSTER_CONFIG } from './TopGunClient';\nimport { TopGun } from './TopGun';\nexport * from './adapters/IDBAdapter';\nexport * from './adapters/EncryptedStorageAdapter';\nimport { QueryHandle } from './QueryHandle';\nimport { ChangeTracker } from './ChangeTracker';\nimport { LWWMap, Predicates } from '@topgunbuild/core';\nimport { TopicHandle } from './TopicHandle';\nimport { SyncState, VALID_TRANSITIONS, isValidTransition } from './SyncState';\nimport { SyncStateMachine } from './SyncStateMachine';\nimport { BackpressureError } from './errors/BackpressureError';\nimport { DEFAULT_BACKPRESSURE_CONFIG } from './BackpressureConfig';\n\n// Cluster imports\nimport { ConnectionPool, PartitionRouter, ClusterClient } from './cluster';\n\n// Connection provider imports\nimport { SingleServerProvider, HttpSyncProvider, AutoConnectionProvider, WebSocketConnection, ConnectionReadyState } from './connection';\n\n// Type imports\nimport type { IStorageAdapter, OpLogEntry } from './IStorageAdapter';\nimport type { LWWRecord, PredicateNode } from '@topgunbuild/core';\nimport type { QueryFilter, QueryResultItem, QueryResultSource, CursorStatus, PaginationInfo } from './QueryHandle';\nimport type { TopicCallback } from './TopicHandle';\nimport type { BackoffConfig, HeartbeatConfig, SyncEngineConfig } from './SyncEngine';\nimport type { StateChangeEvent, StateChangeListener, SyncStateMachineConfig } from './SyncStateMachine';\nimport type {\n BackpressureConfig,\n BackpressureStrategy,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n} from './BackpressureConfig';\n\n// Value exports\nexport { SyncEngine, TopGunClient, TopGun, QueryHandle, LWWMap, Predicates, TopicHandle };\nexport { SyncState, VALID_TRANSITIONS, isValidTransition, SyncStateMachine };\nexport { BackpressureError, DEFAULT_BACKPRESSURE_CONFIG, DEFAULT_CLUSTER_CONFIG };\nexport { logger } from './utils/logger';\n\n// Change tracking exports\nexport { ChangeTracker };\nexport type { ChangeEvent } from './ChangeTracker';\n\n// PN Counter exports\nexport { PNCounterHandle } from './PNCounterHandle';\n\n// Event Journal exports\nexport { EventJournalReader } from './EventJournalReader';\nexport type { JournalEventData, JournalSubscribeOptions } from './EventJournalReader';\n\n// Conflict Resolver exports\nexport { ConflictResolverClient } from './ConflictResolverClient';\nexport type { ResolverInfo, RegisterResult } from './ConflictResolverClient';\n\n// Full-Text Search exports\nexport type { SearchResult } from './SyncEngine';\n\n// Live Search exports\nexport { SearchHandle } from './SearchHandle';\nexport type { SearchResultsCallback } from './SearchHandle';\n\n// Hybrid Query exports\nexport { HybridQueryHandle } from './HybridQueryHandle';\nexport type { HybridQueryFilter, HybridResultItem, HybridResultSource } from './HybridQueryHandle';\n\n// Cluster exports\nexport { ConnectionPool, PartitionRouter, ClusterClient };\nexport type {\n ConnectionPoolEvents,\n RoutingResult,\n PartitionRouterEvents,\n ClusterClientEvents,\n ClusterRoutingMode,\n RoutingMetrics,\n CircuitState,\n} from './cluster';\n\n// Connection provider exports\nexport { SingleServerProvider, HttpSyncProvider, AutoConnectionProvider, WebSocketConnection, ConnectionReadyState };\nexport type {\n IConnection,\n IConnectionProvider,\n ConnectionProviderEvent,\n ConnectionEventHandler,\n SingleServerProviderConfig,\n} from './types';\nexport type { HttpSyncProviderConfig } from './connection/HttpSyncProvider';\nexport type { AutoConnectionProviderConfig } from './connection/AutoConnectionProvider';\n\n// TopGunClient cluster config types\nexport type { TopGunClusterConfig, TopGunClientConfig } from './TopGunClient';\n\n// Type exports\nexport type {\n IStorageAdapter,\n OpLogEntry,\n LWWRecord,\n PredicateNode,\n QueryFilter,\n QueryResultItem,\n QueryResultSource,\n // Pagination types\n CursorStatus,\n PaginationInfo,\n TopicCallback,\n BackoffConfig,\n HeartbeatConfig,\n SyncEngineConfig,\n StateChangeEvent,\n StateChangeListener,\n SyncStateMachineConfig,\n BackpressureConfig,\n BackpressureStrategy,\n BackpressureStatus,\n BackpressureThresholdEvent,\n OperationDroppedEvent,\n};\n","import type { Timestamp } from '@topgunbuild/core';\nimport { HLC, serialize, deserialize } from '@topgunbuild/core';\nimport type {\n IConnectionProvider,\n IConnection,\n ConnectionProviderEvent,\n ConnectionEventHandler,\n} from '../types';\nimport { ConnectionReadyState } from './WebSocketConnection';\nimport { logger } from '../utils/logger';\n\n/**\n * No-op IConnection for HTTP mode. Delegates send() to the provider's\n * internal queue so operations are flushed on the next HTTP poll cycle.\n */\nclass HttpConnection implements IConnection {\n constructor(private readonly provider: HttpSyncProvider) {}\n\n send(data: ArrayBuffer | Uint8Array | string): void {\n if (typeof data === 'string') {\n // Convert string to Uint8Array for the provider's send() method\n const encoder = new TextEncoder();\n this.provider.send(encoder.encode(data));\n } else {\n this.provider.send(data instanceof ArrayBuffer ? new Uint8Array(data) : data);\n }\n }\n\n close(): void {\n // HTTP connections are stateless; close is a no-op\n }\n\n get readyState(): number {\n return this.provider.isConnected()\n ? ConnectionReadyState.OPEN\n : ConnectionReadyState.CLOSED;\n }\n}\n\n/**\n * Configuration for HttpSyncProvider.\n */\nexport interface HttpSyncProviderConfig {\n /** HTTP URL of the TopGun server (e.g., 'http://localhost:8080') */\n url: string;\n /** Client identifier for the server to track HLC state */\n clientId: string;\n /** Hybrid Logical Clock instance for causality tracking */\n hlc: HLC;\n /** JWT auth token for Authorization header */\n authToken?: string;\n /** Polling interval in milliseconds (default: 5000) */\n pollIntervalMs?: number;\n /** HTTP request timeout in milliseconds (default: 30000) */\n requestTimeoutMs?: number;\n /** Map names to sync deltas for on each poll */\n syncMaps?: string[];\n /** Custom fetch implementation for testing or platform compatibility */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * HTTP-based connection provider for serverless environments.\n *\n * Implements IConnectionProvider by translating WebSocket-style send() calls\n * into queued operations that are flushed via HTTP POST /sync at regular\n * polling intervals. Responses are translated back into synthetic message\n * events that SyncEngine understands.\n *\n * This provider is completely stateless on the server side -- each request\n * carries the client's HLC state and the server computes deltas without\n * maintaining per-client state.\n */\nexport class HttpSyncProvider implements IConnectionProvider {\n private readonly url: string;\n private readonly clientId: string;\n private readonly hlc: HLC;\n private readonly pollIntervalMs: number;\n private readonly requestTimeoutMs: number;\n private readonly syncMaps: string[];\n private readonly fetchImpl: typeof fetch;\n\n private authToken: string;\n private listeners: Map<ConnectionProviderEvent, Set<ConnectionEventHandler>> = new Map();\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n\n /** Queued operations to send on next poll */\n private pendingOperations: any[] = [];\n /** Queued one-shot queries to send on next poll */\n private pendingQueries: any[] = [];\n\n /** Per-map last sync timestamps for delta tracking */\n private lastSyncTimestamps: Map<string, Timestamp> = new Map();\n\n /** Whether the last HTTP request succeeded */\n private connected: boolean = false;\n /** Whether we were previously connected (for reconnected event) */\n private wasConnected: boolean = false;\n\n constructor(config: HttpSyncProviderConfig) {\n this.url = config.url.replace(/\\/$/, ''); // Strip trailing slash\n this.clientId = config.clientId;\n this.hlc = config.hlc;\n this.authToken = config.authToken || '';\n this.pollIntervalMs = config.pollIntervalMs ?? 5000;\n this.requestTimeoutMs = config.requestTimeoutMs ?? 30000;\n this.syncMaps = config.syncMaps || [];\n this.fetchImpl = config.fetchImpl || globalThis.fetch.bind(globalThis);\n }\n\n /**\n * Connect by sending an initial sync request to verify auth and get state.\n */\n async connect(): Promise<void> {\n try {\n await this.doSyncRequest();\n this.connected = true;\n this.wasConnected = true;\n this.emit('connected', 'http');\n this.startPolling();\n } catch (err: any) {\n this.connected = false;\n this.emit('error', err);\n throw err;\n }\n }\n\n /**\n * Get connection for a specific key.\n * Returns an HttpConnection that queues operations for the next poll cycle.\n */\n getConnection(_key: string): IConnection {\n return new HttpConnection(this);\n }\n\n /**\n * Get any available connection.\n * Returns an HttpConnection that queues operations for the next poll cycle.\n */\n getAnyConnection(): IConnection {\n return new HttpConnection(this);\n }\n\n /**\n * Check if connected (last HTTP request succeeded).\n */\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get connected node IDs.\n * Returns ['http'] when connected, [] when not.\n */\n getConnectedNodes(): string[] {\n return this.connected ? ['http'] : [];\n }\n\n /**\n * Subscribe to connection events.\n */\n on(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n /**\n * Unsubscribe from connection events.\n */\n off(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n this.listeners.get(event)?.delete(handler);\n }\n\n /**\n * Send data via the HTTP sync provider.\n *\n * Deserializes the msgpackr binary to extract the message type and routes:\n * - OP_BATCH / CLIENT_OP: queued as operations for next poll\n * - AUTH: silently ignored (auth via HTTP header)\n * - SYNC_INIT: silently ignored (HTTP uses timestamp-based deltas)\n * - QUERY_SUB: queued as one-shot query for next poll\n * - All other types: silently dropped with debug log\n */\n send(data: ArrayBuffer | Uint8Array, _key?: string): void {\n try {\n const message = deserialize<any>(\n data instanceof ArrayBuffer ? new Uint8Array(data) : data,\n );\n\n switch (message.type) {\n case 'OP_BATCH':\n if (message.payload?.ops) {\n this.pendingOperations.push(...message.payload.ops);\n }\n break;\n\n case 'CLIENT_OP':\n if (message.payload) {\n this.pendingOperations.push(message.payload);\n }\n break;\n\n case 'AUTH':\n // Auth handled via HTTP Authorization header, ignore\n break;\n\n case 'SYNC_INIT':\n // HTTP uses timestamp-based deltas, not Merkle sync, ignore\n break;\n\n case 'QUERY_SUB':\n if (message.payload) {\n this.pendingQueries.push({\n queryId: message.payload.requestId || `q-${Date.now()}`,\n mapName: message.payload.mapName || message.mapName,\n filter: message.payload.query?.where || message.payload.where,\n limit: message.payload.query?.limit || message.payload.limit,\n });\n }\n break;\n\n default:\n logger.debug(\n { type: message.type },\n 'HTTP sync provider: unsupported message type dropped',\n );\n break;\n }\n } catch (err) {\n logger.warn({ err }, 'HTTP sync provider: failed to deserialize message');\n }\n }\n\n /**\n * Force reconnect by restarting the polling loop.\n */\n forceReconnect(): void {\n this.stopPolling();\n this.connected = false;\n this.emit('disconnected', 'default');\n this.connect().catch((err) => {\n logger.error({ err }, 'HttpSyncProvider forceReconnect failed');\n });\n }\n\n /**\n * Close the HTTP sync provider.\n * Stops the polling loop, clears queued operations, and sets disconnected state.\n */\n async close(): Promise<void> {\n this.stopPolling();\n this.pendingOperations = [];\n this.pendingQueries = [];\n this.connected = false;\n logger.info({ url: this.url }, 'HttpSyncProvider closed');\n }\n\n /**\n * Update the auth token (e.g., after token refresh).\n */\n setAuthToken(token: string): void {\n this.authToken = token;\n }\n\n /**\n * Send an HTTP sync request with queued operations and receive deltas.\n */\n private async doSyncRequest(): Promise<void> {\n // Build syncMaps with lastSyncTimestamp per map\n const syncMaps = this.syncMaps.map((mapName) => ({\n mapName,\n lastSyncTimestamp: this.lastSyncTimestamps.get(mapName) || {\n millis: 0,\n counter: 0,\n nodeId: '',\n },\n }));\n\n // Drain queued operations and queries\n const operations = this.pendingOperations.splice(0);\n const queries = this.pendingQueries.splice(0);\n\n const requestBody: any = {\n clientId: this.clientId,\n clientHlc: this.hlc.now(),\n };\n\n if (operations.length > 0) {\n requestBody.operations = operations;\n }\n if (syncMaps.length > 0) {\n requestBody.syncMaps = syncMaps;\n }\n if (queries.length > 0) {\n requestBody.queries = queries;\n }\n\n const bodyBytes = serialize(requestBody);\n // Create a fresh ArrayBuffer copy for fetch body compatibility\n const bodyBuffer = new ArrayBuffer(bodyBytes.byteLength);\n new Uint8Array(bodyBuffer).set(bodyBytes);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.requestTimeoutMs);\n\n try {\n const response = await this.fetchImpl(`${this.url}/sync`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-msgpack',\n 'Authorization': `Bearer ${this.authToken}`,\n },\n body: bodyBuffer,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`HTTP sync request failed: ${response.status} ${response.statusText}`);\n }\n\n const responseBuffer = await response.arrayBuffer();\n const syncResponse = deserialize<any>(new Uint8Array(responseBuffer));\n\n // Update server HLC\n if (syncResponse.serverHlc) {\n this.hlc.update(syncResponse.serverHlc);\n }\n\n // Process operation acknowledgments\n if (syncResponse.ack) {\n this.emit('message', 'http', serialize({\n type: 'OP_ACK',\n payload: syncResponse.ack,\n }));\n }\n\n // Process deltas as server events\n if (syncResponse.deltas) {\n for (const delta of syncResponse.deltas) {\n // Update lastSyncTimestamp for this map\n this.lastSyncTimestamps.set(delta.mapName, delta.serverSyncTimestamp);\n\n // Emit each record as a server event\n for (const record of delta.records) {\n this.emit('message', 'http', serialize({\n type: 'SERVER_EVENT',\n payload: {\n mapName: delta.mapName,\n key: record.key,\n record: record.record,\n eventType: record.eventType,\n },\n }));\n }\n }\n }\n\n // Process query results\n if (syncResponse.queryResults) {\n for (const result of syncResponse.queryResults) {\n this.emit('message', 'http', serialize({\n type: 'QUERY_RESP',\n payload: {\n requestId: result.queryId,\n results: result.results,\n hasMore: result.hasMore,\n nextCursor: result.nextCursor,\n },\n }));\n }\n }\n\n // Handle connection state transitions\n if (!this.connected) {\n this.connected = true;\n if (this.wasConnected) {\n this.emit('reconnected', 'http');\n } else {\n this.wasConnected = true;\n this.emit('connected', 'http');\n }\n }\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (this.connected) {\n this.connected = false;\n this.emit('disconnected', 'http');\n }\n\n // Re-queue operations that weren't sent\n if (operations.length > 0) {\n this.pendingOperations.unshift(...operations);\n }\n if (queries.length > 0) {\n this.pendingQueries.unshift(...queries);\n }\n\n throw err;\n }\n }\n\n /**\n * Start the polling loop.\n */\n private startPolling(): void {\n if (this.pollTimer) return;\n\n this.pollTimer = setInterval(async () => {\n try {\n await this.doSyncRequest();\n } catch (err) {\n logger.debug({ err }, 'HTTP sync poll failed');\n }\n }, this.pollIntervalMs);\n }\n\n /**\n * Stop the polling loop.\n */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Emit an event to all listeners.\n */\n private emit(event: ConnectionProviderEvent, ...args: any[]): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(...args);\n } catch (err) {\n logger.error({ err, event }, 'Error in HttpSyncProvider event handler');\n }\n }\n }\n }\n}\n","import type { HLC } from '@topgunbuild/core';\nimport type {\n IConnectionProvider,\n IConnection,\n ConnectionProviderEvent,\n ConnectionEventHandler,\n} from '../types';\nimport { SingleServerProvider } from './SingleServerProvider';\nimport { HttpSyncProvider } from './HttpSyncProvider';\nimport type { HttpSyncProviderConfig } from './HttpSyncProvider';\nimport { logger } from '../utils/logger';\n\n/**\n * Configuration for AutoConnectionProvider.\n */\nexport interface AutoConnectionProviderConfig {\n /** Server URL (ws:// or http://) */\n url: string;\n /** Client identifier */\n clientId: string;\n /** Hybrid Logical Clock instance */\n hlc: HLC;\n /** Max WebSocket connection attempts before falling back to HTTP (default: 3) */\n maxWsAttempts?: number;\n /** JWT auth token */\n authToken?: string;\n /** Skip WebSocket and go HTTP-only */\n httpOnly?: boolean;\n /** HTTP polling interval in ms (default: 5000) */\n httpPollIntervalMs?: number;\n /** Map names to sync via HTTP */\n syncMaps?: string[];\n /** Custom fetch implementation for HTTP mode */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * AutoConnectionProvider implements protocol negotiation by trying WebSocket\n * first and falling back to HTTP sync when WebSocket connection fails.\n *\n * This enables seamless deployment in both traditional server environments\n * (using WebSockets) and serverless environments (using HTTP polling).\n */\nexport class AutoConnectionProvider implements IConnectionProvider {\n private readonly config: AutoConnectionProviderConfig;\n private readonly maxWsAttempts: number;\n\n /** The active underlying provider */\n private activeProvider: IConnectionProvider | null = null;\n /** Whether we're using HTTP mode */\n private isHttpMode: boolean;\n\n private listeners: Map<ConnectionProviderEvent, Set<ConnectionEventHandler>> = new Map();\n\n constructor(config: AutoConnectionProviderConfig) {\n this.config = config;\n this.maxWsAttempts = config.maxWsAttempts ?? 3;\n this.isHttpMode = config.httpOnly ?? false;\n }\n\n /**\n * Connect using WebSocket first, falling back to HTTP after maxWsAttempts failures.\n * If httpOnly is true, skips WebSocket entirely.\n */\n async connect(): Promise<void> {\n if (this.isHttpMode) {\n await this.connectHttp();\n return;\n }\n\n // Try WebSocket first\n const wsUrl = this.toWsUrl(this.config.url);\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < this.maxWsAttempts; attempt++) {\n try {\n const wsProvider = new SingleServerProvider({\n url: wsUrl,\n maxReconnectAttempts: 1,\n reconnectDelayMs: 1000,\n });\n\n await wsProvider.connect();\n\n // WebSocket connected successfully\n this.activeProvider = wsProvider;\n this.proxyEvents(wsProvider);\n logger.info({ url: wsUrl }, 'AutoConnectionProvider: WebSocket connected');\n return;\n } catch (err: any) {\n lastError = err;\n logger.debug(\n { attempt: attempt + 1, maxAttempts: this.maxWsAttempts, err: err.message },\n 'AutoConnectionProvider: WebSocket attempt failed',\n );\n }\n }\n\n // WebSocket failed after all attempts, fall back to HTTP\n logger.info(\n { wsAttempts: this.maxWsAttempts, url: this.config.url },\n 'AutoConnectionProvider: WebSocket failed, falling back to HTTP',\n );\n\n this.isHttpMode = true;\n await this.connectHttp();\n }\n\n /**\n * Connect using HTTP sync provider.\n */\n private async connectHttp(): Promise<void> {\n const httpUrl = this.toHttpUrl(this.config.url);\n const httpProvider = new HttpSyncProvider({\n url: httpUrl,\n clientId: this.config.clientId,\n hlc: this.config.hlc,\n authToken: this.config.authToken,\n pollIntervalMs: this.config.httpPollIntervalMs,\n syncMaps: this.config.syncMaps,\n fetchImpl: this.config.fetchImpl,\n });\n\n // Proxy events before connect so listeners capture the 'connected' event\n this.activeProvider = httpProvider;\n this.proxyEvents(httpProvider);\n\n await httpProvider.connect();\n\n logger.info({ url: httpUrl }, 'AutoConnectionProvider: HTTP connected');\n }\n\n getConnection(key: string): IConnection {\n if (!this.activeProvider) {\n throw new Error('Not connected');\n }\n return this.activeProvider.getConnection(key);\n }\n\n getAnyConnection(): IConnection {\n if (!this.activeProvider) {\n throw new Error('Not connected');\n }\n return this.activeProvider.getAnyConnection();\n }\n\n isConnected(): boolean {\n return this.activeProvider?.isConnected() ?? false;\n }\n\n getConnectedNodes(): string[] {\n return this.activeProvider?.getConnectedNodes() ?? [];\n }\n\n on(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n\n // If we already have an active provider, register on it too\n if (this.activeProvider) {\n this.activeProvider.on(event, handler);\n }\n }\n\n off(event: ConnectionProviderEvent, handler: ConnectionEventHandler): void {\n this.listeners.get(event)?.delete(handler);\n\n if (this.activeProvider) {\n this.activeProvider.off(event, handler);\n }\n }\n\n send(data: ArrayBuffer | Uint8Array, key?: string): void {\n if (!this.activeProvider) {\n throw new Error('Not connected');\n }\n this.activeProvider.send(data, key);\n }\n\n /**\n * Force reconnect by delegating to the active provider.\n */\n forceReconnect(): void {\n if (this.activeProvider) {\n this.activeProvider.forceReconnect();\n }\n }\n\n /**\n * Close the active underlying provider.\n */\n async close(): Promise<void> {\n if (this.activeProvider) {\n await this.activeProvider.close();\n this.activeProvider = null;\n }\n }\n\n /**\n * Whether currently using HTTP mode.\n */\n isUsingHttp(): boolean {\n return this.isHttpMode;\n }\n\n /**\n * Proxy events from the underlying provider to our listeners.\n */\n private proxyEvents(provider: IConnectionProvider): void {\n const events: ConnectionProviderEvent[] = [\n 'connected',\n 'disconnected',\n 'reconnected',\n 'message',\n 'partitionMapUpdated',\n 'error',\n ];\n\n for (const event of events) {\n const handlers = this.listeners.get(event);\n if (handlers) {\n for (const handler of handlers) {\n provider.on(event, handler);\n }\n }\n }\n }\n\n /**\n * Convert a URL to WebSocket URL format.\n */\n private toWsUrl(url: string): string {\n return url\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/^https:\\/\\//, 'wss://');\n }\n\n /**\n * Convert a URL to HTTP URL format.\n */\n private toHttpUrl(url: string): string {\n return url\n .replace(/^ws:\\/\\//, 'http://')\n .replace(/^wss:\\/\\//, 'https://');\n }\n}\n"],"mappings":"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","names":["LWWMap","ORMap","deserialize","SyncState","handler","handler","LWWMap","ORMap","token","deserialize","handler","LWWMap","ORMap","DEFAULT_CONNECTION_POOL_CONFIG","DEFAULT_PARTITION_ROUTER_CONFIG","serialize","serialize","deserialize","serialize","deserialize","DEFAULT_CONNECTION_POOL_CONFIG","DEFAULT_PARTITION_ROUTER_CONFIG","serialize","handler","handler","LWWMap","ORMap","serialize","deserialize","serialize","deserialize","LWWMap","serialize","deserialize","handler","deserialize","serialize","handler"]}
|