@topgunbuild/server 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ServerCoordinator.ts","../src/query/Matcher.ts","../src/query/QueryRegistry.ts","../src/utils/logger.ts","../src/topic/TopicManager.ts","../src/cluster/ClusterManager.ts","../src/cluster/PartitionService.ts","../src/cluster/LockManager.ts","../src/security/SecurityManager.ts","../src/monitoring/MetricsService.ts","../src/system/SystemManager.ts","../src/storage/PostgresAdapter.ts","../src/storage/MemoryServerAdapter.ts","../src/interceptor/TimestampInterceptor.ts","../src/interceptor/RateLimitInterceptor.ts"],"sourcesContent":["import { createServer as createHttpServer, Server as HttpServer } from 'http';\nimport { createServer as createHttpsServer, Server as HttpsServer, ServerOptions as HttpsServerOptions } from 'https';\nimport { readFileSync } from 'fs';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { HLC, LWWMap, ORMap, MerkleTree, serialize, deserialize, PermissionPolicy, Principal, PermissionType, Timestamp, LWWRecord, ORMapRecord, MessageSchema } from '@topgunbuild/core';\nimport { IServerStorage, StorageValue, ORMapValue, ORMapTombstones } from './storage/IServerStorage';\nimport { IInterceptor, ServerOp, OpContext, ConnectionContext } from './interceptor/IInterceptor';\nimport * as jwt from 'jsonwebtoken';\nimport * as crypto from 'crypto';\nimport { QueryRegistry, Subscription } from './query/QueryRegistry';\n\nconst GC_INTERVAL_MS = 60 * 60 * 1000; // 1 hour\nconst GC_AGE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\nimport { TopicManager } from './topic/TopicManager';\nimport { ClusterManager } from './cluster/ClusterManager';\nimport { PartitionService } from './cluster/PartitionService';\nimport { LockManager } from './cluster/LockManager';\nimport { executeQuery, Query } from './query/Matcher';\nimport { SecurityManager } from './security/SecurityManager';\nimport { logger } from './utils/logger';\nimport { MetricsService } from './monitoring/MetricsService';\nimport { SystemManager } from './system/SystemManager';\nimport { TLSConfig, ClusterTLSConfig } from './types/TLSConfig';\n\ninterface ClientConnection {\n id: string;\n socket: WebSocket;\n principal?: Principal; // Auth info\n isAuthenticated: boolean;\n subscriptions: Set<string>; // Set of Query IDs\n lastActiveHlc: Timestamp;\n}\n\ninterface PendingClusterQuery {\n requestId: string;\n client: ClientConnection;\n queryId: string; // Client's Query ID\n mapName: string;\n query: Query;\n results: { key: string; value: any }[];\n expectedNodes: Set<string>;\n respondedNodes: Set<string>;\n timer: NodeJS.Timeout;\n}\n\nexport interface ServerCoordinatorConfig {\n port: number;\n nodeId: string;\n storage?: IServerStorage;\n jwtSecret?: string;\n host?: string;\n clusterPort?: number;\n peers?: string[];\n securityPolicies?: PermissionPolicy[];\n /** Callback to resolve dynamic peer addresses after ports are known */\n resolvePeers?: () => string[];\n interceptors?: IInterceptor[];\n metricsPort?: number;\n discovery?: 'manual' | 'kubernetes';\n serviceName?: string;\n discoveryInterval?: number;\n tls?: TLSConfig;\n clusterTls?: ClusterTLSConfig;\n}\n\nexport class ServerCoordinator {\n private httpServer: HttpServer | HttpsServer;\n private metricsServer?: HttpServer;\n private metricsService: MetricsService;\n private wss: WebSocketServer;\n private clients: Map<string, ClientConnection> = new Map();\n\n // Interceptors\n private interceptors: IInterceptor[] = [];\n\n // In-memory storage (partitioned later)\n private maps: Map<string, LWWMap<string, any> | ORMap<string, any>> = new Map();\n private hlc: HLC;\n private storage?: IServerStorage;\n private jwtSecret: string;\n private queryRegistry: QueryRegistry;\n\n private cluster!: ClusterManager;\n private partitionService!: PartitionService;\n private lockManager!: LockManager;\n private topicManager!: TopicManager;\n private securityManager: SecurityManager;\n private systemManager!: SystemManager;\n\n private pendingClusterQueries: Map<string, PendingClusterQuery> = new Map();\n private gcInterval?: NodeJS.Timeout;\n\n // GC Consensus State\n private gcReports: Map<string, Timestamp> = new Map();\n\n // Track map loading state to avoid returning empty results during async load\n private mapLoadingPromises: Map<string, Promise<void>> = new Map();\n\n private _actualPort: number = 0;\n private _actualClusterPort: number = 0;\n private _readyPromise: Promise<void>;\n private _readyResolve!: () => void;\n\n constructor(config: ServerCoordinatorConfig) {\n this._readyPromise = new Promise((resolve) => {\n this._readyResolve = resolve;\n });\n\n this.hlc = new HLC(config.nodeId);\n this.storage = config.storage;\n // Handle JWT_SECRET with escaped newlines (e.g., from Docker/Dokploy env vars)\n const rawSecret = config.jwtSecret || process.env.JWT_SECRET || 'topgun-secret-dev';\n this.jwtSecret = rawSecret.replace(/\\\\n/g, '\\n');\n this.queryRegistry = new QueryRegistry();\n this.securityManager = new SecurityManager(config.securityPolicies || []);\n this.interceptors = config.interceptors || [];\n this.metricsService = new MetricsService();\n\n // HTTP Server Setup first (to get actual port if port=0)\n if (config.tls?.enabled) {\n const tlsOptions = this.buildTLSOptions(config.tls);\n this.httpServer = createHttpsServer(tlsOptions, (_req, res) => {\n res.writeHead(200);\n res.end('TopGun Server Running (Secure)');\n });\n logger.info('TLS enabled for client connections');\n } else {\n this.httpServer = createHttpServer((_req, res) => {\n res.writeHead(200);\n res.end('TopGun Server Running');\n });\n\n if (process.env.NODE_ENV === 'production') {\n logger.warn('⚠️ TLS is disabled! Client connections are NOT encrypted.');\n }\n }\n\n const metricsPort = config.metricsPort !== undefined ? config.metricsPort : 9090;\n this.metricsServer = createHttpServer(async (req, res) => {\n if (req.url === '/metrics') {\n try {\n res.setHeader('Content-Type', this.metricsService.getContentType());\n res.end(await this.metricsService.getMetrics());\n } catch (err) {\n res.statusCode = 500;\n res.end('Internal Server Error');\n }\n } else {\n res.statusCode = 404;\n res.end();\n }\n });\n this.metricsServer.listen(metricsPort, () => {\n logger.info({ port: metricsPort }, 'Metrics server listening');\n });\n this.metricsServer.on('error', (err) => {\n logger.error({ err, port: metricsPort }, 'Metrics server failed to start');\n });\n\n this.wss = new WebSocketServer({ server: this.httpServer });\n this.wss.on('connection', (ws) => this.handleConnection(ws));\n\n // Use port 0 to let OS assign a free port\n this.httpServer.listen(config.port, () => {\n const addr = this.httpServer.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : config.port;\n logger.info({ port: this._actualPort }, 'Server Coordinator listening');\n\n // Now setup cluster with actual/configured cluster port\n const clusterPort = config.clusterPort ?? 0;\n\n // Resolve peers dynamically if callback provided\n const peers = config.resolvePeers ? config.resolvePeers() : (config.peers || []);\n\n this.cluster = new ClusterManager({\n nodeId: config.nodeId,\n host: config.host || 'localhost',\n port: clusterPort,\n peers,\n discovery: config.discovery,\n serviceName: config.serviceName,\n discoveryInterval: config.discoveryInterval,\n tls: config.clusterTls\n });\n this.partitionService = new PartitionService(this.cluster);\n this.lockManager = new LockManager();\n this.lockManager.on('lockGranted', (evt) => this.handleLockGranted(evt));\n\n this.topicManager = new TopicManager({\n cluster: this.cluster,\n sendToClient: (clientId, message) => {\n const client = this.clients.get(clientId);\n if (client && client.socket.readyState === WebSocket.OPEN) {\n client.socket.send(serialize(message));\n }\n }\n });\n\n this.systemManager = new SystemManager(\n this.cluster,\n this.metricsService,\n (name) => this.getMap(name) as LWWMap<string, any>\n );\n\n this.setupClusterListeners();\n this.cluster.start().then((actualClusterPort) => {\n this._actualClusterPort = actualClusterPort;\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n logger.info({ clusterPort: this._actualClusterPort }, 'Cluster started');\n this.systemManager.start();\n this._readyResolve();\n }).catch((err) => {\n // Fallback for ClusterManager that doesn't return port\n this._actualClusterPort = clusterPort;\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n logger.info({ clusterPort: this._actualClusterPort }, 'Cluster started (sync)');\n this.systemManager.start();\n this._readyResolve();\n });\n });\n\n if (this.storage) {\n this.storage.initialize().then(() => {\n logger.info('Storage adapter initialized');\n }).catch(err => {\n logger.error({ err }, 'Failed to initialize storage');\n });\n }\n\n this.startGarbageCollection();\n }\n\n /** Wait for server to be fully ready (ports assigned) */\n public ready(): Promise<void> {\n return this._readyPromise;\n }\n\n /** Get the actual port the server is listening on */\n public get port(): number {\n return this._actualPort;\n }\n\n /** Get the actual cluster port */\n public get clusterPort(): number {\n return this._actualClusterPort;\n }\n\n public async shutdown() {\n logger.info('Shutting down Server Coordinator...');\n\n // 1. Stop accepting new connections\n this.httpServer.close();\n if (this.metricsServer) {\n this.metricsServer.close();\n }\n this.metricsService.destroy();\n this.wss.close();\n\n // 2. Notify and Close Clients\n logger.info(`Closing ${this.clients.size} client connections...`);\n const shutdownMsg = serialize({ type: 'SHUTDOWN_PENDING', retryAfter: 5000 });\n\n for (const client of this.clients.values()) {\n try {\n if (client.socket.readyState === WebSocket.OPEN) {\n client.socket.send(shutdownMsg);\n client.socket.close(1001, 'Server Shutdown');\n }\n } catch (e) {\n logger.error({ err: e, clientId: client.id }, 'Error closing client connection');\n }\n }\n this.clients.clear();\n\n // 3. Stop Cluster\n if (this.cluster) {\n this.cluster.stop();\n }\n\n // 4. Close Storage\n if (this.storage) {\n logger.info('Closing storage connection...');\n try {\n await this.storage.close();\n logger.info('Storage closed successfully.');\n } catch (err) {\n logger.error({ err }, 'Error closing storage');\n }\n }\n\n // 5. Cleanup\n if (this.gcInterval) {\n clearInterval(this.gcInterval);\n this.gcInterval = undefined;\n }\n\n // Stop LockManager\n if (this.lockManager) {\n this.lockManager.stop();\n }\n\n // Stop SystemManager\n if (this.systemManager) {\n this.systemManager.stop();\n }\n\n logger.info('Server Coordinator shutdown complete.');\n }\n\n private async handleConnection(ws: WebSocket) {\n // Client ID is temporary until auth\n const clientId = crypto.randomUUID();\n logger.info({ clientId }, 'Client connected (pending auth)');\n\n const connection: ClientConnection = {\n id: clientId,\n socket: ws,\n isAuthenticated: false,\n subscriptions: new Set(),\n lastActiveHlc: this.hlc.now() // Initialize with current time\n };\n this.clients.set(clientId, connection);\n this.metricsService.setConnectedClients(this.clients.size);\n\n // Run onConnection interceptors\n try {\n const context: ConnectionContext = {\n clientId: connection.id,\n socket: connection.socket,\n isAuthenticated: connection.isAuthenticated,\n principal: connection.principal\n };\n for (const interceptor of this.interceptors) {\n if (interceptor.onConnection) {\n await interceptor.onConnection(context);\n }\n }\n } catch (err) {\n logger.error({ clientId, err }, 'Interceptor rejected connection');\n ws.close(4000, 'Connection Rejected');\n this.clients.delete(clientId);\n return;\n }\n\n ws.on('message', (message) => {\n try {\n let data: any;\n let buf: Uint8Array;\n\n if (Buffer.isBuffer(message)) {\n buf = message;\n } else if (message instanceof ArrayBuffer) {\n buf = new Uint8Array(message);\n } else if (Array.isArray(message)) {\n buf = Buffer.concat(message);\n } else {\n // Fallback or unexpected type\n buf = Buffer.from(message as any);\n }\n\n try {\n data = deserialize(buf);\n } catch (e) {\n // If msgpack fails, try JSON (legacy support)\n try {\n // Use Buffer.toString() or TextDecoder\n const text = Buffer.isBuffer(buf) ? buf.toString() : new TextDecoder().decode(buf);\n data = JSON.parse(text);\n } catch (jsonErr) {\n // Original error likely relevant\n throw e;\n }\n }\n\n this.handleMessage(connection, data);\n } catch (err) {\n logger.error({ err }, 'Invalid message format');\n ws.close(1002, 'Protocol Error');\n }\n });\n\n ws.on('close', () => {\n logger.info({ clientId }, 'Client disconnected');\n\n // Run onDisconnect interceptors\n const context: ConnectionContext = {\n clientId: connection.id,\n socket: connection.socket,\n isAuthenticated: connection.isAuthenticated,\n principal: connection.principal\n };\n for (const interceptor of this.interceptors) {\n if (interceptor.onDisconnect) {\n interceptor.onDisconnect(context).catch(err => {\n logger.error({ clientId, err }, 'Error in onDisconnect interceptor');\n });\n }\n }\n\n // Cleanup subscriptions\n for (const subId of connection.subscriptions) {\n this.queryRegistry.unregister(subId);\n }\n\n // Cleanup Locks (Local)\n this.lockManager.handleClientDisconnect(clientId);\n\n // Cleanup Topics (Local)\n this.topicManager.unsubscribeAll(clientId);\n\n // Notify Cluster to Cleanup Locks (Remote)\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_CLIENT_DISCONNECTED', {\n originNodeId: this.cluster.config.nodeId,\n clientId\n });\n }\n }\n\n this.clients.delete(clientId);\n this.metricsService.setConnectedClients(this.clients.size);\n });\n\n // Send Auth Challenge immediately\n ws.send(serialize({ type: 'AUTH_REQUIRED' }));\n }\n\n private async handleMessage(client: ClientConnection, rawMessage: any) {\n // Validation with Zod\n const parseResult = MessageSchema.safeParse(rawMessage);\n if (!parseResult.success) {\n logger.error({ clientId: client.id, error: parseResult.error }, 'Invalid message format from client');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: 'Invalid message format', details: (parseResult.error as any).errors }\n }));\n return;\n }\n const message = parseResult.data;\n\n // Update client's last active HLC\n // Try to extract from payload if present, otherwise assume near current time but logically before next op\n this.updateClientHlc(client, message);\n\n // Handshake / Auth handling\n if (!client.isAuthenticated) {\n if (message.type === 'AUTH') {\n const token = message.token;\n try {\n // Verify JWT - support both HS256 (symmetric) and RS256 (asymmetric/Clerk)\n const isRSAKey = this.jwtSecret.includes('-----BEGIN');\n const verifyOptions: jwt.VerifyOptions = isRSAKey\n ? { algorithms: ['RS256'] }\n : { algorithms: ['HS256'] };\n const decoded = jwt.verify(token, this.jwtSecret, verifyOptions) as any;\n // Ensure roles exist\n if (!decoded.roles) {\n decoded.roles = ['USER']; // Default role\n }\n // Ensure userId exists (map from sub if needed)\n if (!decoded.userId && decoded.sub) {\n decoded.userId = decoded.sub;\n }\n\n client.principal = decoded;\n client.isAuthenticated = true;\n logger.info({ clientId: client.id, user: client.principal!.userId || 'anon' }, 'Client authenticated');\n\n client.socket.send(serialize({ type: 'AUTH_ACK' }));\n return; // Stop processing this message\n } catch (e) {\n logger.error({ clientId: client.id, err: e }, 'Auth failed');\n client.socket.send(serialize({ type: 'AUTH_FAIL', error: 'Invalid token' }));\n client.socket.close(4001, 'Unauthorized');\n }\n } else {\n // Reject any other message before auth\n client.socket.close(4001, 'Auth required');\n }\n return;\n }\n\n // Standard Protocol Handling (Authenticated)\n switch (message.type) {\n case 'QUERY_SUB': {\n const { queryId, mapName, query } = message.payload;\n\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, mapName, 'READ')) {\n logger.warn({ clientId: client.id, mapName }, 'Access Denied: QUERY_SUB');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${mapName}` }\n }));\n return;\n }\n\n logger.info({ clientId: client.id, mapName, query }, 'Client subscribed');\n this.metricsService.incOp('SUBSCRIBE', mapName);\n\n // Identify all relevant nodes\n const allMembers = this.cluster.getMembers();\n const remoteMembers = allMembers.filter(id => !this.cluster.isLocal(id));\n\n const requestId = crypto.randomUUID();\n\n const pending: PendingClusterQuery = {\n requestId,\n client,\n queryId,\n mapName,\n query,\n results: [], // Will populate with local results first\n expectedNodes: new Set(remoteMembers),\n respondedNodes: new Set(),\n timer: setTimeout(() => this.finalizeClusterQuery(requestId, true), 5000) // 5s timeout\n };\n\n this.pendingClusterQueries.set(requestId, pending);\n\n // Execute Locally (async - wait for map to load from storage)\n // [FIX] Using await ensures handleMessage completes only after query execution\n // This is important for:\n // 1. Tests that need to verify results immediately after handleMessage\n // 2. Ensuring storage is loaded before returning results\n try {\n const localResults = await this.executeLocalQuery(mapName, query);\n pending.results.push(...localResults);\n\n // Scatter: Send to other nodes\n if (remoteMembers.length > 0) {\n for (const nodeId of remoteMembers) {\n this.cluster.send(nodeId, 'CLUSTER_QUERY_EXEC', {\n requestId,\n mapName,\n query\n });\n }\n } else {\n // Single node cluster: finalize immediately\n this.finalizeClusterQuery(requestId);\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to execute local query');\n // Finalize with empty results on error\n this.finalizeClusterQuery(requestId);\n }\n break;\n }\n\n case 'QUERY_UNSUB': {\n const { queryId: unsubId } = message.payload;\n this.queryRegistry.unregister(unsubId);\n client.subscriptions.delete(unsubId);\n break;\n }\n\n case 'CLIENT_OP': {\n const op = message.payload;\n\n // Determine action type\n // LWW: op.record.value === null -> REMOVE\n // OR: OR_REMOVE or OR_ADD -> PUT (effectively)\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\n this.metricsService.incOp(isRemove ? 'DELETE' : 'PUT', op.mapName);\n\n // Check Permission\n if (!this.securityManager.checkPermission(client.principal!, op.mapName, action)) {\n logger.warn({ clientId: client.id, action, mapName: op.mapName }, 'Access Denied: Client OP');\n client.socket.send(serialize({\n type: 'OP_REJECTED',\n payload: { opId: op.id, reason: 'Access Denied' }\n }));\n return;\n }\n\n logger.info({ clientId: client.id, opType: op.opType, key: op.key, mapName: op.mapName }, 'Received op');\n\n if (this.partitionService.isLocalOwner(op.key)) {\n this.processLocalOp(op, false, client.id).catch(err => {\n logger.error({ clientId: client.id, err }, 'Op failed');\n client.socket.send(serialize({\n type: 'OP_REJECTED',\n payload: { opId: op.id, reason: err.message || 'Internal Error' }\n }));\n });\n } else {\n const owner = this.partitionService.getOwner(op.key);\n logger.info({ key: op.key, owner }, 'Forwarding op');\n this.cluster.sendToNode(owner, op);\n }\n break;\n }\n\n case 'OP_BATCH': {\n const ops = message.payload.ops;\n logger.info({ clientId: client.id, count: ops.length }, 'Received batch');\n\n let lastProcessedId: string | null = null;\n let rejectedCount = 0;\n\n for (const op of ops) {\n // OpLogEntry has { mapName, key, record, ... }\n\n // Check Permission for each op\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\n if (!this.securityManager.checkPermission(client.principal!, op.mapName, action)) {\n rejectedCount++;\n logger.warn({ clientId: client.id, action, mapName: op.mapName }, 'Access Denied (Batch)');\n continue;\n }\n\n // processLocalOp expects { mapName, key, record, orRecord, orTag, opType }\n\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n await this.processLocalOp({\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType\n }, false, client.id);\n\n if (op.id) {\n lastProcessedId = op.id;\n }\n } catch (err) {\n rejectedCount++;\n logger.warn({ clientId: client.id, mapName: op.mapName, err }, 'Op rejected in batch');\n // We do NOT update lastProcessedId for failed op\n }\n } else {\n const owner = this.partitionService.getOwner(op.key);\n this.cluster.sendToNode(owner, {\n type: 'CLIENT_OP',\n payload: {\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType\n }\n });\n // For forwarded ops, we optimistically assume success for batch ACK purposes?\n // Or we should only ACK what we processed locally?\n // Batch ACK usually implies \"accepted\". Forwarding = accepted for processing.\n if (op.id) {\n lastProcessedId = op.id;\n }\n }\n }\n\n if (lastProcessedId !== null) {\n client.socket.send(serialize({\n type: 'OP_ACK',\n payload: { lastId: lastProcessedId }\n }));\n }\n\n if (rejectedCount > 0) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Partial batch failure: ${rejectedCount} ops denied` }\n }));\n }\n break;\n }\n\n case 'SYNC_INIT': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.mapName, 'READ')) {\n logger.warn({ clientId: client.id, mapName: message.mapName }, 'Access Denied: SYNC_INIT');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }));\n return;\n }\n\n const lastSync = message.lastSyncTimestamp || 0;\n const now = Date.now();\n if (lastSync > 0 && (now - lastSync) > GC_AGE_MS) {\n logger.warn({ clientId: client.id, lastSync, age: now - lastSync }, 'Client too old, sending SYNC_RESET_REQUIRED');\n client.socket.send(serialize({\n type: 'SYNC_RESET_REQUIRED',\n payload: { mapName: message.mapName }\n }));\n return;\n }\n\n logger.info({ clientId: client.id, mapName: message.mapName }, 'Client requested sync');\n this.metricsService.incOp('GET', message.mapName);\n\n // [FIX] Wait for map to be fully loaded from storage before sending rootHash\n // This prevents sending rootHash=0 for maps that are still loading from PostgreSQL\n try {\n const mapForSync = await this.getMapAsync(message.mapName);\n if (mapForSync instanceof LWWMap) {\n // Use the incremental Merkle Tree from LWWMap\n const tree = mapForSync.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n client.socket.send(serialize({\n type: 'SYNC_RESP_ROOT',\n payload: {\n mapName: message.mapName,\n rootHash,\n timestamp: this.hlc.now()\n }\n }));\n } else {\n // ORMap sync not implemented via Merkle Tree yet\n logger.warn({ mapName: message.mapName }, 'SYNC_INIT requested for ORMap - Not Implemented');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 501, message: `Merkle Sync not supported for ORMap ${message.mapName}` }\n }));\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for SYNC_INIT');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }));\n }\n break;\n }\n\n case 'MERKLE_REQ_BUCKET': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\n return;\n }\n\n const { mapName, path } = message.payload;\n\n // [FIX] Wait for map to be fully loaded before accessing Merkle tree\n try {\n const mapForBucket = await this.getMapAsync(mapName);\n if (mapForBucket instanceof LWWMap) {\n const treeForBucket = mapForBucket.getMerkleTree();\n const buckets = treeForBucket.getBuckets(path);\n const node = treeForBucket.getNode(path);\n if (node && node.entries && node.entries.size > 0) {\n const diffRecords = [];\n for (const key of node.entries.keys()) {\n diffRecords.push({ key, record: mapForBucket.getRecord(key) });\n }\n client.socket.send(serialize({\n type: 'SYNC_RESP_LEAF',\n payload: { mapName, path, records: diffRecords }\n }));\n } else {\n client.socket.send(serialize({\n type: 'SYNC_RESP_BUCKETS',\n payload: { mapName, path, buckets }\n }));\n }\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to load map for MERKLE_REQ_BUCKET');\n }\n break;\n }\n\n case 'LOCK_REQUEST': {\n const { requestId, name, ttl } = message.payload;\n\n // 1. Access Control\n // Define a convention: lock names are resources.\n // Check if user has 'WRITE' permission on \"locks\" map or specific lock name.\n // Since locks are ephemeral, we might treat them as a special resource \"sys:locks\".\n // Or just check against the lock name itself.\n // Let's use `sys:lock:${name}` pattern or just `${name}`.\n // If we use just name, it might conflict with map names if policies are strict.\n // Assuming for now that lock name represents the resource being protected.\n if (!this.securityManager.checkPermission(client.principal!, name, 'PUT')) {\n client.socket.send(serialize({\n // We don't have LOCK_DENIED type in schema yet?\n // Using LOCK_RELEASED with success=false as a hack or ERROR.\n // Ideally ERROR.\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for lock ${name}` }\n }));\n return;\n }\n\n if (this.partitionService.isLocalOwner(name)) {\n const result = this.lockManager.acquire(name, client.id, requestId, ttl || 10000);\n if (result.granted) {\n client.socket.send(serialize({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken: result.fencingToken }\n }));\n }\n // If not granted, it is queued. Response sent later via event.\n } else {\n const owner = this.partitionService.getOwner(name);\n // 2. Cluster Reliability Check\n if (!this.cluster.getMembers().includes(owner)) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 503, message: `Lock owner ${owner} is unavailable` }\n }));\n return;\n }\n\n this.cluster.send(owner, 'CLUSTER_LOCK_REQ', {\n originNodeId: this.cluster.config.nodeId,\n clientId: client.id,\n requestId,\n name,\n ttl\n });\n }\n break;\n }\n\n case 'LOCK_RELEASE': {\n const { requestId, name, fencingToken } = message.payload;\n\n if (this.partitionService.isLocalOwner(name)) {\n const success = this.lockManager.release(name, client.id, fencingToken);\n client.socket.send(serialize({\n type: 'LOCK_RELEASED',\n payload: { requestId, name, success }\n }));\n } else {\n const owner = this.partitionService.getOwner(name);\n this.cluster.send(owner, 'CLUSTER_LOCK_RELEASE', {\n originNodeId: this.cluster.config.nodeId,\n clientId: client.id,\n requestId,\n name,\n fencingToken\n });\n }\n break;\n }\n\n case 'TOPIC_SUB': {\n const { topic } = message.payload;\n\n // C1: Access Control\n // We treat topics as resources. \n // Policy check: action 'READ' on resource `topic:${topic}`\n if (!this.securityManager.checkPermission(client.principal!, `topic:${topic}`, 'READ')) {\n logger.warn({ clientId: client.id, topic }, 'Access Denied: TOPIC_SUB');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }));\n return;\n }\n\n try {\n this.topicManager.subscribe(client.id, topic);\n } catch (e: any) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }));\n }\n break;\n }\n\n case 'TOPIC_UNSUB': {\n const { topic } = message.payload;\n this.topicManager.unsubscribe(client.id, topic);\n break;\n }\n\n case 'TOPIC_PUB': {\n const { topic, data } = message.payload;\n\n // C1: Access Control\n // Policy check: action 'PUT' (publish) on resource `topic:${topic}`\n if (!this.securityManager.checkPermission(client.principal!, `topic:${topic}`, 'PUT')) {\n logger.warn({ clientId: client.id, topic }, 'Access Denied: TOPIC_PUB');\n // No error sent back? Fire and forget usually implies silent drop or async error.\n // But for security violations, an error is useful during dev.\n // Spec says fire-and-forget delivery, but security rejection should ideally notify.\n // Let's send error.\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }));\n return;\n }\n\n try {\n this.topicManager.publish(topic, data, client.id);\n } catch (e: any) {\n // Invalid topic name etc\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }));\n }\n break;\n }\n\n default:\n logger.warn({ type: message.type }, 'Unknown message type');\n }\n }\n\n private updateClientHlc(client: ClientConnection, message: any) {\n // Try to extract timestamp from message if available\n // This is heuristic based on typical message structure\n let ts: Timestamp | undefined;\n\n if (message.type === 'CLIENT_OP') {\n const op = message.payload;\n if (op.record && op.record.timestamp) {\n ts = op.record.timestamp;\n } else if (op.orRecord && op.orRecord.timestamp) {\n // orRecord usually has entries which have timestamps, or value itself is decorated?\n // Depends on implementation.\n } else if (op.orTag) {\n try {\n ts = HLC.parse(op.orTag);\n } catch (e) { }\n }\n }\n\n if (ts) {\n // Client sent an explicit timestamp, update their HLC\n this.hlc.update(ts); // Also update server clock\n // Client HLC is at least this\n client.lastActiveHlc = ts;\n } else {\n // Just bump to current server time if no explicit TS\n // This assumes client is \"alive\" at this moment.\n client.lastActiveHlc = this.hlc.now();\n }\n }\n\n private broadcast(message: any, excludeClientId?: string) {\n const isServerEvent = message.type === 'SERVER_EVENT';\n\n if (isServerEvent) {\n for (const [id, client] of this.clients) {\n if (id !== excludeClientId && client.socket.readyState === 1 && client.isAuthenticated && client.principal) {\n const payload = message.payload;\n const mapName = payload.mapName;\n\n // Shallow clone payload\n const newPayload = { ...payload };\n\n if (newPayload.record) { // LWW\n const newVal = this.securityManager.filterObject(newPayload.record.value, client.principal, mapName);\n newPayload.record = { ...newPayload.record, value: newVal };\n }\n\n if (newPayload.orRecord) { // OR_ADD\n const newVal = this.securityManager.filterObject(newPayload.orRecord.value, client.principal, mapName);\n newPayload.orRecord = { ...newPayload.orRecord, value: newVal };\n }\n\n client.socket.send(serialize({ ...message, payload: newPayload }));\n }\n }\n } else {\n const msgData = serialize(message);\n for (const [id, client] of this.clients) {\n if (id !== excludeClientId && client.socket.readyState === 1) { // 1 = OPEN\n client.socket.send(msgData);\n }\n }\n }\n }\n\n private setupClusterListeners() {\n this.cluster.on('memberJoined', () => {\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n });\n this.cluster.on('memberLeft', () => {\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n });\n\n this.cluster.on('message', (msg) => {\n switch (msg.type) {\n case 'OP_FORWARD':\n logger.info({ senderId: msg.senderId }, 'Received forwarded op');\n if (this.partitionService.isLocalOwner(msg.payload.key)) {\n this.processLocalOp(msg.payload, true, msg.senderId).catch(err => {\n logger.error({ err, senderId: msg.senderId }, 'Forwarded op failed');\n });\n } else {\n logger.warn({ key: msg.payload.key }, 'Received OP_FORWARD but not owner. Dropping.');\n }\n break;\n case 'CLUSTER_EVENT':\n this.handleClusterEvent(msg.payload);\n break;\n\n case 'CLUSTER_QUERY_EXEC': {\n const { requestId, mapName, query } = msg.payload;\n this.executeLocalQuery(mapName, query).then(results => {\n this.cluster.send(msg.senderId, 'CLUSTER_QUERY_RESP', {\n requestId,\n results\n });\n }).catch(err => {\n logger.error({ err, mapName }, 'Failed to execute cluster query');\n this.cluster.send(msg.senderId, 'CLUSTER_QUERY_RESP', {\n requestId,\n results: []\n });\n });\n break;\n }\n\n case 'CLUSTER_QUERY_RESP': {\n const { requestId: reqId, results: remoteResults } = msg.payload;\n const pendingQuery = this.pendingClusterQueries.get(reqId);\n if (pendingQuery) {\n pendingQuery.results.push(...remoteResults);\n pendingQuery.respondedNodes.add(msg.senderId);\n\n if (pendingQuery.respondedNodes.size === pendingQuery.expectedNodes.size) {\n this.finalizeClusterQuery(reqId);\n }\n }\n break;\n }\n\n case 'CLUSTER_GC_REPORT': {\n this.handleGcReport(msg.senderId, msg.payload.minHlc);\n break;\n }\n\n case 'CLUSTER_GC_COMMIT': {\n this.performGarbageCollection(msg.payload.safeTimestamp);\n break;\n }\n\n case 'CLUSTER_LOCK_REQ': {\n const { originNodeId, clientId, requestId, name, ttl } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n const result = this.lockManager.acquire(name, compositeId, requestId, ttl || 10000);\n if (result.granted) {\n this.cluster.send(originNodeId, 'CLUSTER_LOCK_GRANTED', {\n clientId,\n requestId,\n name,\n fencingToken: result.fencingToken\n });\n }\n break;\n }\n\n case 'CLUSTER_LOCK_RELEASE': {\n const { originNodeId, clientId, requestId, name, fencingToken } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n const success = this.lockManager.release(name, compositeId, fencingToken);\n this.cluster.send(originNodeId, 'CLUSTER_LOCK_RELEASED', {\n clientId, requestId, name, success\n });\n break;\n }\n\n case 'CLUSTER_LOCK_RELEASED': {\n const { clientId, requestId, name, success } = msg.payload;\n const client = this.clients.get(clientId);\n if (client) {\n client.socket.send(serialize({\n type: 'LOCK_RELEASED',\n payload: { requestId, name, success }\n }));\n }\n break;\n }\n\n case 'CLUSTER_LOCK_GRANTED': {\n const { clientId, requestId, name, fencingToken } = msg.payload;\n const client = this.clients.get(clientId);\n if (client) {\n client.socket.send(serialize({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken }\n }));\n }\n break;\n }\n\n case 'CLUSTER_CLIENT_DISCONNECTED': {\n const { clientId, originNodeId } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n this.lockManager.handleClientDisconnect(compositeId);\n break;\n }\n\n case 'CLUSTER_TOPIC_PUB': {\n const { topic, data, originalSenderId } = msg.payload;\n this.topicManager.publish(topic, data, originalSenderId, true);\n break;\n }\n }\n });\n }\n\n private async executeLocalQuery(mapName: string, query: Query) {\n // Wait for map to be fully loaded from storage before querying\n const map = await this.getMapAsync(mapName);\n const records = new Map<string, any>();\n\n if (map instanceof LWWMap) {\n for (const key of map.allKeys()) {\n const rec = map.getRecord(key);\n if (rec && rec.value !== null) {\n records.set(key, rec);\n }\n }\n } else if (map instanceof ORMap) {\n // For ORMap, we flatten values. A key matches if ANY of its values match?\n // Or we expose the array of values?\n // For now, we expose { key, value: [v1, v2, ...] }\n // Accessing properties on array might fail depending on query.\n // Assuming user knows what they are querying.\n // Since ORMap doesn't have allKeys, we iterate internal structure?\n // ORMap doesn't expose keys iterator publicly in the class I read?\n // Wait, checking ORMap.ts...\n // It doesn't export keys()! It exports items: Map.\n // But items is private.\n // I need to add keys() to ORMap or use 'any' cast.\n // I will cast to any for now.\n const items = (map as any).items as Map<string, any>;\n for (const key of items.keys()) {\n const values = map.get(key);\n if (values.length > 0) {\n // We wrap in object matching LWWRecord structure roughly?\n // { value: values, timestamp: ... }\n // But timestamp differs per record.\n records.set(key, { value: values });\n }\n }\n }\n\n // Fix: Do not apply offset/limit locally for cluster queries.\n // They will be applied in finalizeClusterQuery after aggregation.\n const localQuery = { ...query };\n delete localQuery.offset;\n delete localQuery.limit;\n\n return executeQuery(records, localQuery);\n }\n\n private finalizeClusterQuery(requestId: string, timeout = false) {\n const pending = this.pendingClusterQueries.get(requestId);\n if (!pending) return;\n\n if (timeout) {\n logger.warn({ requestId, responded: pending.respondedNodes.size, expected: pending.expectedNodes.size }, 'Query timed out. Returning partial results.');\n }\n\n clearTimeout(pending.timer);\n this.pendingClusterQueries.delete(requestId);\n\n const { client, queryId, mapName, query, results } = pending;\n\n // Deduplicate results (if backups responded or multiple nodes have same key)\n const uniqueResults = new Map<string, any>();\n for (const res of results) {\n uniqueResults.set(res.key, res);\n }\n const finalResults = Array.from(uniqueResults.values());\n\n // Re-Apply Sort (Global)\n if (query.sort) {\n finalResults.sort((a, b) => {\n for (const [field, direction] of Object.entries(query.sort!)) {\n // Handle ORMap array values vs LWW single values?\n // Assuming LWW for sort logic or array comparison.\n const valA = a.value[field];\n const valB = b.value[field];\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 const slicedResults = (query.offset || query.limit)\n ? finalResults.slice(query.offset || 0, (query.offset || 0) + (query.limit || finalResults.length))\n : finalResults;\n\n // Register Subscription\n const resultKeys = new Set(slicedResults.map(r => r.key));\n const sub: Subscription = {\n id: queryId,\n clientId: client.id,\n mapName,\n query,\n socket: client.socket,\n previousResultKeys: resultKeys,\n interestedFields: 'ALL'\n };\n\n this.queryRegistry.register(sub);\n client.subscriptions.add(queryId);\n\n // Apply Field Level Security\n const filteredResults = slicedResults.map(res => {\n const filteredValue = this.securityManager.filterObject(res.value, client.principal!, mapName);\n return { ...res, value: filteredValue };\n });\n\n client.socket.send(serialize({\n type: 'QUERY_RESP',\n payload: { queryId, results: filteredResults }\n }));\n }\n\n private handleLockGranted({ clientId, requestId, name, fencingToken }: { clientId: string, requestId: string, name: string, fencingToken: number }) {\n // Check if local client\n const client = this.clients.get(clientId);\n if (client) {\n client.socket.send(serialize({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken }\n }));\n return;\n }\n\n // Check if remote client (composite ID: \"nodeId:realClientId\")\n const parts = clientId.split(':');\n if (parts.length === 2) {\n const [nodeId, realClientId] = parts;\n // Verify nodeId is not self (loopback check, though split should handle it)\n if (nodeId !== this.cluster.config.nodeId) {\n this.cluster.send(nodeId, 'CLUSTER_LOCK_GRANTED', {\n clientId: realClientId,\n requestId,\n name,\n fencingToken\n });\n return;\n }\n }\n\n logger.warn({ clientId, name }, 'Lock granted to unknown client');\n }\n\n private async processLocalOp(op: any, fromCluster: boolean, originalSenderId?: string) {\n // 0. Prepare Context\n let context: OpContext = {\n clientId: originalSenderId || 'unknown',\n isAuthenticated: false, // We might need to fetch this if local\n fromCluster,\n originalSenderId\n };\n\n if (!fromCluster && originalSenderId) {\n const client = this.clients.get(originalSenderId);\n if (client) {\n context = {\n clientId: client.id,\n socket: client.socket,\n isAuthenticated: client.isAuthenticated,\n principal: client.principal,\n fromCluster,\n originalSenderId\n };\n }\n }\n\n // 1. Interceptors: onBeforeOp\n let currentOp: ServerOp | null = op;\n try {\n for (const interceptor of this.interceptors) {\n if (interceptor.onBeforeOp) {\n if (currentOp) {\n currentOp = await interceptor.onBeforeOp(currentOp, context);\n if (!currentOp) {\n logger.debug({ interceptor: interceptor.name, opId: op.id }, 'Interceptor silently dropped op');\n return; // Silent drop\n }\n }\n }\n }\n } catch (err: any) {\n // Find which interceptor failed? We don't know easily unless we tracked loop index.\n // But logging err is good enough.\n logger.warn({ err, opId: op.id }, 'Interceptor rejected op');\n throw err; // Re-throw to caller\n }\n\n if (!currentOp) return; // Should be caught above but safe check\n\n op = currentOp;\n\n // Apply to server state (Owner or Forwarded)\n // Determine type hint from op\n const typeHint = (op.opType === 'OR_ADD' || op.opType === 'OR_REMOVE') ? 'OR' : 'LWW';\n const map = this.getMap(op.mapName, typeHint);\n\n // Check compatibility\n if (typeHint === 'OR' && map instanceof LWWMap) {\n logger.error({ mapName: op.mapName }, 'Map type mismatch: LWWMap but received OR op');\n throw new Error('Map type mismatch: LWWMap but received OR op');\n }\n if (typeHint === 'LWW' && map instanceof ORMap) {\n logger.error({ mapName: op.mapName }, 'Map type mismatch: ORMap but received LWW op');\n throw new Error('Map type mismatch: ORMap but received LWW op');\n }\n\n let oldRecord: any;\n let recordToStore: StorageValue<any> | undefined;\n let tombstonesToStore: StorageValue<any> | undefined;\n\n const eventPayload: any = {\n mapName: op.mapName,\n key: op.key,\n // Common fields\n };\n\n if (map instanceof LWWMap) {\n oldRecord = map.getRecord(op.key);\n map.merge(op.key, op.record);\n recordToStore = op.record;\n eventPayload.eventType = 'UPDATED';\n eventPayload.record = op.record;\n } else if (map instanceof ORMap) {\n // ORMap\n oldRecord = map.getRecords(op.key); // Logic for \"old record\" in ORMap is complex for query.\n\n if (op.opType === 'OR_ADD') {\n map.apply(op.key, op.orRecord);\n eventPayload.eventType = 'OR_ADD';\n eventPayload.orRecord = op.orRecord;\n\n // Prepare storage: full state of key\n recordToStore = {\n type: 'OR',\n records: map.getRecords(op.key)\n };\n } else if (op.opType === 'OR_REMOVE') {\n map.applyTombstone(op.orTag);\n eventPayload.eventType = 'OR_REMOVE';\n eventPayload.orTag = op.orTag;\n\n // OR_REMOVE modifies the key's records implicitly (filters them out)\n // So we should update the key state too?\n // Yes, if we remove a tag, the getRecords(key) result changes.\n // But we don't know which key held the tag easily unless we search or client provided it.\n // Client provided `op.key`.\n recordToStore = {\n type: 'OR',\n records: map.getRecords(op.key)\n };\n\n // Also persist tombstones\n tombstonesToStore = {\n type: 'OR_TOMBSTONES',\n tags: map.getTombstones()\n };\n }\n }\n\n // Live Query Evaluation (Local)\n this.queryRegistry.processChange(op.mapName, map, op.key, op.record || op.orRecord, oldRecord);\n\n const mapSize = (map instanceof ORMap) ? map.totalRecords : map.size;\n this.metricsService.setMapSize(op.mapName, mapSize);\n\n // Persist to storage (Only Owner persists)\n if (this.storage) {\n if (recordToStore) {\n this.storage.store(op.mapName, op.key, recordToStore).catch(err => {\n logger.error({ mapName: op.mapName, key: op.key, err }, 'Failed to persist op');\n });\n }\n if (tombstonesToStore) {\n this.storage.store(op.mapName, '__tombstones__', tombstonesToStore).catch(err => {\n logger.error({ mapName: op.mapName, err }, 'Failed to persist tombstones');\n });\n }\n }\n\n // 1. Broadcast EVENT to other clients (Notification)\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n }, originalSenderId);\n\n // 2. Broadcast EVENT/REPLICATION to Cluster\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n\n // 4. Interceptors: onAfterOp\n for (const interceptor of this.interceptors) {\n if (interceptor.onAfterOp) {\n interceptor.onAfterOp(op, context).catch(err => {\n logger.error({ err }, 'Error in onAfterOp');\n });\n }\n }\n }\n\n private handleClusterEvent(payload: any) {\n // 1. Replication Logic: Am I a Backup?\n const { mapName, key, eventType } = payload;\n const map = this.getMap(mapName, (eventType === 'OR_ADD' || eventType === 'OR_REMOVE') ? 'OR' : 'LWW');\n const oldRecord = (map instanceof LWWMap) ? map.getRecord(key) : null;\n\n // Only store if we are Owner (shouldn't receive event unless forwarded) or Backup\n if (this.partitionService.isRelated(key)) {\n if (map instanceof LWWMap && payload.record) {\n map.merge(key, payload.record);\n } else if (map instanceof ORMap) {\n if (eventType === 'OR_ADD' && payload.orRecord) {\n map.apply(key, payload.orRecord);\n } else if (eventType === 'OR_REMOVE' && payload.orTag) {\n map.applyTombstone(payload.orTag);\n }\n }\n }\n\n // 2. Notify Query Subscriptions\n this.queryRegistry.processChange(mapName, map, key, payload.record || payload.orRecord, oldRecord);\n\n // 3. Broadcast to local clients (Notification)\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: payload,\n timestamp: this.hlc.now()\n });\n }\n\n public getMap(name: string, typeHint: 'LWW' | 'OR' = 'LWW'): LWWMap<string, any> | ORMap<string, any> {\n if (!this.maps.has(name)) {\n let map: LWWMap<string, any> | ORMap<string, any>;\n\n if (typeHint === 'OR') {\n map = new ORMap(this.hlc);\n } else {\n map = new LWWMap(this.hlc);\n }\n\n this.maps.set(name, map);\n\n // Lazy load from storage - track the promise for getMapAsync\n if (this.storage) {\n logger.info({ mapName: name }, 'Loading map from storage...');\n const loadPromise = this.loadMapFromStorage(name, typeHint);\n this.mapLoadingPromises.set(name, loadPromise);\n loadPromise.finally(() => {\n this.mapLoadingPromises.delete(name);\n });\n }\n }\n return this.maps.get(name)!;\n }\n\n /**\n * Returns map after ensuring it's fully loaded from storage.\n * Use this for queries to avoid returning empty results during initial load.\n */\n public async getMapAsync(name: string, typeHint: 'LWW' | 'OR' = 'LWW'): Promise<LWWMap<string, any> | ORMap<string, any>> {\n const mapExisted = this.maps.has(name);\n\n // First ensure map exists (this triggers loading if needed)\n this.getMap(name, typeHint);\n\n // Wait for loading to complete if in progress\n const loadingPromise = this.mapLoadingPromises.get(name);\n\n // [DEBUG] Log state for troubleshooting sync issues\n const map = this.maps.get(name);\n const mapSize = map instanceof LWWMap ? Array.from(map.entries()).length :\n map instanceof ORMap ? map.size : 0;\n logger.info({\n mapName: name,\n mapExisted,\n hasLoadingPromise: !!loadingPromise,\n currentMapSize: mapSize\n }, '[getMapAsync] State check');\n\n if (loadingPromise) {\n logger.info({ mapName: name }, '[getMapAsync] Waiting for loadMapFromStorage...');\n await loadingPromise;\n const newMapSize = map instanceof LWWMap ? Array.from(map.entries()).length :\n map instanceof ORMap ? map.size : 0;\n logger.info({ mapName: name, mapSizeAfterLoad: newMapSize }, '[getMapAsync] Load completed');\n }\n\n return this.maps.get(name)!;\n }\n\n private async loadMapFromStorage(name: string, typeHint: 'LWW' | 'OR'): Promise<void> {\n try {\n const keys = await this.storage!.loadAllKeys(name);\n if (keys.length === 0) return;\n\n // Check for ORMap markers in keys\n const hasTombstones = keys.includes('__tombstones__');\n\n const relatedKeys = keys.filter(k => this.partitionService.isRelated(k));\n if (relatedKeys.length === 0) return;\n\n const records = await this.storage!.loadAll(name, relatedKeys);\n let count = 0;\n\n // Check for Type Mismatch and Replace Map if needed\n let isOR = hasTombstones;\n if (!isOR) {\n // Check first record\n for (const [k, v] of records) {\n if (k !== '__tombstones__' && (v as any).type === 'OR') {\n isOR = true;\n break;\n }\n }\n }\n\n // If we created LWW but it's OR, replace it.\n // If we created OR but it's LWW, replace it? (Less likely if hint was OR, but possible if hint was wrong?)\n const currentMap = this.maps.get(name);\n if (!currentMap) return;\n let targetMap = currentMap;\n\n if (isOR && currentMap instanceof LWWMap) {\n logger.info({ mapName: name }, 'Map auto-detected as ORMap. Switching type.');\n targetMap = new ORMap(this.hlc);\n this.maps.set(name, targetMap);\n } else if (!isOR && currentMap instanceof ORMap && typeHint !== 'OR') {\n // Only switch back to LWW if hint wasn't explicit OR\n logger.info({ mapName: name }, 'Map auto-detected as LWWMap. Switching type.');\n targetMap = new LWWMap(this.hlc);\n this.maps.set(name, targetMap);\n }\n\n if (targetMap instanceof ORMap) {\n for (const [key, record] of records) {\n if (key === '__tombstones__') {\n const t = record as ORMapTombstones;\n if (t && t.tags) t.tags.forEach(tag => targetMap.applyTombstone(tag));\n } else {\n const orVal = record as ORMapValue<any>;\n if (orVal && orVal.records) {\n orVal.records.forEach(r => targetMap.apply(key, r));\n count++;\n }\n }\n }\n } else if (targetMap instanceof LWWMap) {\n for (const [key, record] of records) {\n // Expect LWWRecord\n // If record is actually ORMapValue (mismatch), we skip or error?\n // If !isOR, we assume LWWRecord.\n if (!(record as any).type) { // LWWRecord doesn't have type property in my impl\n targetMap.merge(key, record as LWWRecord<any>);\n count++;\n }\n }\n }\n\n if (count > 0) {\n logger.info({ mapName: name, count }, 'Loaded records for map');\n this.queryRegistry.refreshSubscriptions(name, targetMap);\n const mapSize = (targetMap instanceof ORMap) ? targetMap.totalRecords : targetMap.size;\n this.metricsService.setMapSize(name, mapSize);\n }\n } catch (err) {\n logger.error({ mapName: name, err }, 'Failed to load map');\n }\n }\n\n private startGarbageCollection() {\n this.gcInterval = setInterval(() => {\n this.reportLocalHlc();\n }, GC_INTERVAL_MS);\n }\n\n private reportLocalHlc() {\n // 1. Calculate Local Min HLC\n let minHlc = this.hlc.now();\n\n for (const client of this.clients.values()) {\n if (HLC.compare(client.lastActiveHlc, minHlc) < 0) {\n minHlc = client.lastActiveHlc;\n }\n }\n\n const members = this.cluster.getMembers().sort();\n const leaderId = members[0];\n const myId = this.cluster.config.nodeId;\n\n if (leaderId === myId) {\n // I am Leader\n this.handleGcReport(myId, minHlc);\n } else {\n // Send to Leader\n this.cluster.send(leaderId, 'CLUSTER_GC_REPORT', { minHlc });\n }\n }\n\n private handleGcReport(nodeId: string, minHlc: Timestamp) {\n this.gcReports.set(nodeId, minHlc);\n\n const members = this.cluster.getMembers();\n\n // Check if we have reports from ALL members\n // (Including self, which is inserted directly)\n const allReported = members.every(m => this.gcReports.has(m));\n\n if (allReported) {\n // Calculate Global Safe Timestamp\n let globalSafe = this.hlc.now(); // Start high\n let initialized = false;\n\n for (const ts of this.gcReports.values()) {\n if (!initialized || HLC.compare(ts, globalSafe) < 0) {\n globalSafe = ts;\n initialized = true;\n }\n }\n\n // Add safety buffer (e.g. GC_AGE)\n // prune(timestamp) removes items OLDER than timestamp.\n // We want to remove items OLDER than (GlobalMin - GC_AGE).\n\n const olderThanMillis = globalSafe.millis - GC_AGE_MS;\n const safeTimestamp: Timestamp = {\n millis: olderThanMillis,\n counter: 0,\n nodeId: globalSafe.nodeId // Doesn't matter much for comparison if millis match, but best effort\n };\n\n logger.info({\n globalMinHlc: globalSafe.millis,\n safeGcTimestamp: olderThanMillis,\n reportsCount: this.gcReports.size\n }, 'GC Consensus Reached. Broadcasting Commit.');\n\n // Broadcast Commit\n const commitMsg = {\n type: 'CLUSTER_GC_COMMIT', // Handled by cluster listener\n payload: { safeTimestamp }\n };\n\n // Send to others\n for (const member of members) {\n if (!this.cluster.isLocal(member)) {\n this.cluster.send(member, 'CLUSTER_GC_COMMIT', { safeTimestamp });\n }\n }\n\n // Execute Locally\n this.performGarbageCollection(safeTimestamp);\n\n // Clear reports for next round?\n // Or keep them and overwrite?\n // Overwriting is better for partial updates, but clearing ensures freshness.\n // Since we run interval based, clearing is safer to ensure active participation next time.\n this.gcReports.clear();\n }\n }\n\n private performGarbageCollection(olderThan: Timestamp) {\n logger.info({ olderThanMillis: olderThan.millis }, 'Performing Garbage Collection');\n const now = Date.now();\n\n for (const [name, map] of this.maps) {\n // 1. Check for active expired records (TTL)\n if (map instanceof LWWMap) {\n for (const key of map.allKeys()) {\n const record = map.getRecord(key);\n if (record && record.value !== null && record.ttlMs) {\n const expirationTime = record.timestamp.millis + record.ttlMs;\n if (expirationTime < now) {\n logger.info({ mapName: name, key }, 'Record expired (TTL). Converting to tombstone.');\n\n // Create Tombstone at expiration time to handle \"Resurrection\" correctly\n const tombstoneTimestamp: Timestamp = {\n millis: expirationTime,\n counter: 0, // Reset counter for expiration time\n nodeId: this.hlc.getNodeId // Use our ID\n };\n\n const tombstone: LWWRecord<any> = { value: null, timestamp: tombstoneTimestamp };\n\n // Apply locally\n const changed = map.merge(key, tombstone);\n\n if (changed) {\n // Persist and Broadcast\n // Construct an artificial op to reuse pipeline logic or do manual steps\n // Manual steps are safer here as we don't have a client op context\n\n if (this.storage) {\n this.storage.store(name, key, tombstone).catch(err =>\n logger.error({ mapName: name, key, err }, 'Failed to persist expired tombstone')\n );\n }\n\n const eventPayload = {\n mapName: name,\n key: key,\n eventType: 'UPDATED',\n record: tombstone\n };\n\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n }\n }\n }\n\n // 2. Prune old tombstones\n const removedKeys = map.prune(olderThan);\n if (removedKeys.length > 0) {\n logger.info({ mapName: name, count: removedKeys.length }, 'Pruned records from LWW map');\n if (this.storage) {\n this.storage.deleteAll(name, removedKeys).catch(err => {\n logger.error({ mapName: name, err }, 'Failed to delete pruned keys from storage');\n });\n }\n }\n } else if (map instanceof ORMap) {\n // ORMap Expiration\n // We need to check all active records in the ORMap\n const items = (map as any).items as Map<string, Map<string, ORMapRecord<any>>>;\n const tombstonesSet = (map as any).tombstones as Set<string>;\n\n const tagsToExpire: { key: string; tag: string }[] = [];\n\n for (const [key, keyMap] of items) {\n for (const [tag, record] of keyMap) {\n if (!tombstonesSet.has(tag)) {\n if (record.ttlMs) {\n const expirationTime = record.timestamp.millis + record.ttlMs;\n if (expirationTime < now) {\n tagsToExpire.push({ key, tag });\n }\n }\n }\n }\n }\n\n for (const { key, tag } of tagsToExpire) {\n logger.info({ mapName: name, key, tag }, 'ORMap Record expired (TTL). Removing.');\n\n // Remove by adding tag to tombstones\n map.applyTombstone(tag);\n\n // Persist change\n if (this.storage) {\n // We need to update the key's record list and tombstones\n // Optimally, we should batch these updates\n const records = map.getRecords(key);\n if (records.length > 0) {\n this.storage.store(name, key, { type: 'OR', records });\n } else {\n this.storage.delete(name, key);\n }\n\n const currentTombstones = map.getTombstones();\n this.storage.store(name, '__tombstones__', {\n type: 'OR_TOMBSTONES',\n tags: currentTombstones\n });\n }\n\n // Broadcast\n const eventPayload = {\n mapName: name,\n key: key,\n eventType: 'OR_REMOVE',\n orTag: tag\n };\n\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n\n // 2. Prune old tombstones\n const removedTags = map.prune(olderThan);\n if (removedTags.length > 0) {\n logger.info({ mapName: name, count: removedTags.length }, 'Pruned tombstones from OR map');\n // We need to update __tombstones__ in storage\n if (this.storage) {\n const currentTombstones = map.getTombstones();\n this.storage.store(name, '__tombstones__', {\n type: 'OR_TOMBSTONES',\n tags: currentTombstones\n }).catch(err => {\n logger.error({ mapName: name, err }, 'Failed to update tombstones');\n });\n }\n }\n }\n }\n\n // Broadcast to clients\n this.broadcast({\n type: 'GC_PRUNE',\n payload: {\n olderThan\n }\n });\n }\n private buildTLSOptions(config: TLSConfig): HttpsServerOptions {\n const options: HttpsServerOptions = {\n cert: readFileSync(config.certPath),\n key: readFileSync(config.keyPath),\n minVersion: config.minVersion || 'TLSv1.2',\n };\n\n if (config.caCertPath) {\n options.ca = readFileSync(config.caCertPath);\n }\n\n if (config.ciphers) {\n options.ciphers = config.ciphers;\n }\n\n if (config.passphrase) {\n options.passphrase = config.passphrase;\n }\n\n return options;\n }\n}\n","import { LWWRecord, PredicateNode, evaluatePredicate } from '@topgunbuild/core';\n\nexport interface Query {\n where?: Record<string, any>;\n predicate?: PredicateNode;\n sort?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n offset?: number;\n}\n\n/**\n * Checks if a record matches a query.\n * Supports simple exact match for now.\n */\nexport function matchesQuery(record: LWWRecord<any>, query: Query): boolean {\n const data = record.value;\n if (!data) return false; \n\n // Check TTL\n if (record.ttlMs) {\n const now = Date.now();\n if (record.timestamp.millis + record.ttlMs < now) {\n return false; // Expired\n }\n }\n\n // 1. New Predicate API\n if (query.predicate) {\n return evaluatePredicate(query.predicate, data);\n }\n\n // 2. Legacy 'where' clause\n if (!query.where) return true; // Empty query matches everything\n\n for (const [field, expected] of Object.entries(query.where)) {\n const actual = data[field];\n\n // Operator matching (e.g. { age: { $gt: 18 } })\n if (typeof expected === 'object' && expected !== null && !Array.isArray(expected)) {\n for (const [op, opValueRaw] of Object.entries(expected)) {\n const opValue = opValueRaw as any; // Cast for comparison\n switch (op) {\n case '$gt':\n if (!(actual > opValue)) return false;\n break;\n case '$gte':\n if (!(actual >= opValue)) return false;\n break;\n case '$lt':\n if (!(actual < opValue)) return false;\n break;\n case '$lte':\n if (!(actual <= opValue)) return false;\n break;\n case '$ne':\n if (!(actual !== opValue)) return false;\n break;\n // Add more operators as needed ($in, etc.)\n default:\n // Unknown operator, treating as exact match (or should we fail?)\n // For now, ignore unknown operators or treat as mismatch?\n // Let's treat unknown operators as false to be safe.\n return false; \n }\n }\n } else {\n // Simple exact match\n if (actual !== expected) {\n return false;\n }\n }\n }\n \n return true;\n}\n\nexport function executeQuery(records: Map<string, LWWRecord<any>> | LWWRecord<any>[], query: Query): { key: string; value: any }[] {\n // Handle null/undefined query\n if (!query) {\n query = {};\n }\n\n let results: { key: string; record: LWWRecord<any> }[] = [];\n\n // 1. Filter\n if (records instanceof Map) {\n for (const [key, record] of records) {\n if (matchesQuery(record, query)) {\n results.push({ key, record });\n }\n }\n } else {\n // If array, we might not have keys easily unless they are in the record or we iterate\n // For now assume Map input primarily for ServerCoordinator\n // But if input is array of records?\n for (const record of records) {\n // Assuming key is not readily available if just array of records, \n // but usually we pass Map from ServerCoordinator.\n // If we really need key, we need it in the input.\n // Let's stick to Map input for now as that's what ServerCoordinator has.\n // But wait, the signature I defined allows array.\n if (matchesQuery(record, query)) {\n results.push({ key: '?', record }); \n }\n }\n }\n\n // 2. Sort\n if (query.sort) {\n results.sort((a, b) => {\n for (const [field, direction] of Object.entries(query.sort!)) {\n const valA = a.record.value[field];\n const valB = b.record.value[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 // 3. Limit & Offset\n if (query.offset || query.limit) {\n const offset = query.offset || 0;\n const limit = query.limit || results.length;\n results = results.slice(offset, offset + limit);\n }\n\n return results.map(r => ({ key: r.key, value: r.record.value }));\n}\n","import { Query, matchesQuery, executeQuery } from './Matcher';\nimport { LWWRecord, LWWMap, ORMap, serialize, PredicateNode, ORMapRecord } from '@topgunbuild/core';\nimport { WebSocket } from 'ws';\nimport { logger } from '../utils/logger';\n\nexport interface Subscription {\n id: string; // queryId\n clientId: string;\n mapName: string;\n query: Query;\n socket: WebSocket;\n previousResultKeys: Set<string>;\n interestedFields?: Set<string> | 'ALL';\n _cleanup?: () => void; // For Reverse Index cleanup\n}\n\nclass ReverseQueryIndex {\n // field -> value -> Set<Subscription>\n private equality = new Map<string, Map<any, Set<Subscription>>>();\n // field -> Set<Subscription>\n private interest = new Map<string, Set<Subscription>>();\n // catch-all\n private wildcard = new Set<Subscription>();\n\n public add(sub: Subscription) {\n const query = sub.query;\n let indexed = false;\n const cleanupFns: (() => void)[] = [];\n\n // 1. Where\n if (query.where) {\n for (const [field, value] of Object.entries(query.where)) {\n if (typeof value !== 'object') {\n // Exact match\n this.addEquality(field, value, sub);\n cleanupFns.push(() => this.removeEquality(field, value, sub));\n indexed = true;\n } else {\n // Operator - add to interest\n this.addInterest(field, sub);\n cleanupFns.push(() => this.removeInterest(field, sub));\n indexed = true;\n }\n }\n }\n \n // 2. Predicate\n if (query.predicate) {\n const visit = (node: PredicateNode) => {\n if (node.op === 'eq' && node.attribute && node.value !== undefined) {\n this.addEquality(node.attribute, node.value, sub);\n cleanupFns.push(() => this.removeEquality(node.attribute!, node.value, sub));\n indexed = true;\n } else if (node.attribute) {\n // Any other op on attribute\n this.addInterest(node.attribute, sub);\n cleanupFns.push(() => this.removeInterest(node.attribute!, sub));\n indexed = true;\n }\n \n if (node.children) {\n node.children.forEach(visit);\n }\n };\n visit(query.predicate);\n }\n \n // 3. Sort\n if (query.sort) {\n Object.keys(query.sort).forEach(k => {\n this.addInterest(k, sub);\n cleanupFns.push(() => this.removeInterest(k, sub));\n indexed = true;\n });\n }\n\n if (!indexed) {\n this.wildcard.add(sub);\n cleanupFns.push(() => this.wildcard.delete(sub));\n }\n \n sub._cleanup = () => cleanupFns.forEach(fn => fn());\n }\n\n public remove(sub: Subscription) {\n if (sub._cleanup) {\n sub._cleanup();\n sub._cleanup = undefined;\n }\n }\n\n public getCandidates(changedFields: Set<string> | 'ALL', oldVal: any, newVal: any): Set<Subscription> {\n const candidates = new Set<Subscription>(this.wildcard);\n\n if (changedFields === 'ALL') {\n // Return all possible candidates (inefficient but safe)\n // We collect from all indexes? Or just return all subs?\n // To match \"wildcard\" behavior, we should probably iterate all.\n // But we don't track all subs in index easily.\n // We can iterate this.interest and this.equality.\n for (const set of this.interest.values()) {\n for (const s of set) candidates.add(s);\n }\n for (const map of this.equality.values()) {\n for (const set of map.values()) {\n for (const s of set) candidates.add(s);\n }\n }\n return candidates;\n }\n\n // If no changes detected (shouldn't happen if called correctly), just return wildcard\n if (changedFields.size === 0) return candidates;\n\n for (const field of changedFields) {\n // 1. Interest (General)\n if (this.interest.has(field)) {\n for (const sub of this.interest.get(field)!) {\n candidates.add(sub);\n }\n }\n\n // 2. Equality\n if (this.equality.has(field)) {\n const valMap = this.equality.get(field)!;\n \n // Check New Value queries\n if (newVal && newVal[field] !== undefined && valMap.has(newVal[field])) {\n for (const sub of valMap.get(newVal[field])!) {\n candidates.add(sub);\n }\n }\n \n // Check Old Value queries\n if (oldVal && oldVal[field] !== undefined && valMap.has(oldVal[field])) {\n for (const sub of valMap.get(oldVal[field])!) {\n candidates.add(sub);\n }\n }\n }\n }\n \n return candidates;\n }\n\n private addEquality(field: string, value: any, sub: Subscription) {\n if (!this.equality.has(field)) this.equality.set(field, new Map());\n const valMap = this.equality.get(field)!;\n if (!valMap.has(value)) valMap.set(value, new Set());\n valMap.get(value)!.add(sub);\n }\n\n private removeEquality(field: string, value: any, sub: Subscription) {\n const valMap = this.equality.get(field);\n if (valMap) {\n const set = valMap.get(value);\n if (set) {\n set.delete(sub);\n if (set.size === 0) valMap.delete(value);\n }\n if (valMap.size === 0) this.equality.delete(field);\n }\n }\n\n private addInterest(field: string, sub: Subscription) {\n if (!this.interest.has(field)) this.interest.set(field, new Set());\n this.interest.get(field)!.add(sub);\n }\n\n private removeInterest(field: string, sub: Subscription) {\n const set = this.interest.get(field);\n if (set) {\n set.delete(sub);\n if (set.size === 0) this.interest.delete(field);\n }\n }\n}\n\nexport class QueryRegistry {\n // MapName -> Set of Subscriptions (Legacy/Backup)\n private subscriptions: Map<string, Set<Subscription>> = new Map();\n \n // MapName -> Reverse Index\n private indexes: Map<string, ReverseQueryIndex> = new Map();\n\n public register(sub: Subscription) {\n if (!this.subscriptions.has(sub.mapName)) {\n this.subscriptions.set(sub.mapName, new Set());\n this.indexes.set(sub.mapName, new ReverseQueryIndex());\n }\n \n const interestedFields = this.analyzeQueryFields(sub.query);\n sub.interestedFields = interestedFields;\n\n this.subscriptions.get(sub.mapName)!.add(sub);\n this.indexes.get(sub.mapName)!.add(sub);\n \n logger.info({ clientId: sub.clientId, mapName: sub.mapName, query: sub.query }, 'Client subscribed');\n }\n\n public unregister(queryId: string) {\n for (const [mapName, subs] of this.subscriptions) {\n for (const sub of subs) {\n if (sub.id === queryId) {\n subs.delete(sub);\n this.indexes.get(mapName)?.remove(sub);\n return; \n }\n }\n }\n }\n\n public unsubscribeAll(clientId: string) {\n for (const [mapName, subs] of this.subscriptions) {\n for (const sub of subs) {\n if (sub.clientId === clientId) {\n subs.delete(sub);\n this.indexes.get(mapName)?.remove(sub);\n }\n }\n }\n }\n\n /**\n * Refreshes all subscriptions for a given map.\n * Useful when the map is bulk-loaded from storage.\n */\n public refreshSubscriptions(mapName: string, map: LWWMap<string, any> | ORMap<string, any>) {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) return;\n\n const allRecords = this.getMapRecords(map);\n\n for (const sub of subs) {\n const newResults = executeQuery(allRecords, sub.query);\n const newResultKeys = new Set(newResults.map(r => r.key));\n\n // 1. Removed\n for (const key of sub.previousResultKeys) {\n if (!newResultKeys.has(key)) {\n this.sendUpdate(sub, key, null, 'REMOVE');\n }\n }\n\n // 2. Added/Updated\n for (const res of newResults) {\n // Send update for all currently matching records\n // We assume value might have changed or it is new\n this.sendUpdate(sub, res.key, res.value, 'UPDATE');\n }\n\n sub.previousResultKeys = newResultKeys;\n }\n }\n\n private getMapRecords(map: LWWMap<string, any> | ORMap<string, any>): Map<string, any> {\n const recordsMap = new Map<string, any>();\n\n // Use duck-typing to support mocks and proxies\n const mapAny = map as any;\n\n // LWWMap-like: has allKeys() and getRecord()\n if (typeof mapAny.allKeys === 'function' && typeof mapAny.getRecord === 'function') {\n for (const key of mapAny.allKeys()) {\n const rec = mapAny.getRecord(key);\n if (rec) {\n recordsMap.set(key, rec);\n }\n }\n }\n // ORMap-like: has items Map and get() returns array\n else if (mapAny.items instanceof Map && typeof mapAny.get === 'function') {\n const items = mapAny.items as Map<string, any>;\n for (const key of items.keys()) {\n const values = mapAny.get(key);\n if (values.length > 0) {\n recordsMap.set(key, { value: values });\n }\n }\n }\n return recordsMap;\n }\n\n /**\n * Processes a record change for all relevant subscriptions.\n * Calculates diffs and sends updates.\n */\n public processChange(\n mapName: string,\n map: LWWMap<string, any> | ORMap<string, any>,\n changeKey: string,\n changeRecord: any, // LWWRecord | ORMapRecord | ORMapRecord[]\n oldRecord?: any // LWWRecord | ORMapRecord[]\n ) {\n const index = this.indexes.get(mapName);\n if (!index) return;\n\n // Extract Values\n const newVal = this.extractValue(changeRecord);\n const oldVal = this.extractValue(oldRecord);\n\n // 0. Calculate Changed Fields\n const changedFields = this.getChangedFields(oldVal, newVal);\n\n if (changedFields !== 'ALL' && changedFields.size === 0 && oldRecord && changeRecord) {\n return;\n }\n \n const candidates = index.getCandidates(changedFields, oldVal, newVal);\n \n if (candidates.size === 0) return;\n\n // Helper to get all records as a Map for executeQuery\n let recordsMap: Map<string, any> | null = null;\n const getRecordsMap = () => {\n if (recordsMap) return recordsMap;\n recordsMap = this.getMapRecords(map);\n return recordsMap;\n };\n\n for (const sub of candidates) {\n const dummyRecord: LWWRecord<any> = { \n value: newVal,\n timestamp: { millis: 0, counter: 0, nodeId: '' } // Dummy timestamp for matchesQuery\n };\n const isMatch = matchesQuery(dummyRecord, sub.query); // Approximate match check\n const wasInResult = sub.previousResultKeys.has(changeKey);\n\n if (!isMatch && !wasInResult) {\n continue;\n }\n\n // Re-evaluate query\n const allRecords = getRecordsMap();\n const newResults = executeQuery(allRecords, sub.query);\n const newResultKeys = new Set(newResults.map(r => r.key));\n\n // Determine changes\n // 1. Removed\n for (const key of sub.previousResultKeys) {\n if (!newResultKeys.has(key)) {\n this.sendUpdate(sub, key, null, 'REMOVE');\n }\n }\n\n // 2. Added/Updated\n for (const res of newResults) {\n const key = res.key;\n const isNew = !sub.previousResultKeys.has(key);\n \n if (key === changeKey) {\n this.sendUpdate(sub, key, res.value, 'UPDATE');\n } else if (isNew) {\n this.sendUpdate(sub, key, res.value, 'UPDATE');\n }\n }\n\n sub.previousResultKeys = newResultKeys;\n }\n }\n\n private extractValue(record: any): any {\n if (!record) return null;\n if (Array.isArray(record)) {\n // ORMapRecord[]\n return record.map(r => r.value);\n }\n // LWWRecord or ORMapRecord\n return record.value;\n }\n\n private sendUpdate(sub: Subscription, key: string, value: any, type: 'UPDATE' | 'REMOVE') {\n if (sub.socket.readyState === 1) {\n sub.socket.send(serialize({\n type: 'QUERY_UPDATE',\n payload: {\n queryId: sub.id,\n key,\n value,\n type\n }\n }));\n }\n }\n\n private analyzeQueryFields(query: Query): Set<string> | 'ALL' {\n const fields = new Set<string>();\n try {\n if (query.predicate) {\n const extract = (node: PredicateNode) => {\n if (node.attribute) fields.add(node.attribute);\n if (node.children) node.children.forEach(extract);\n };\n extract(query.predicate);\n }\n if (query.where) {\n Object.keys(query.where).forEach(k => fields.add(k));\n }\n if (query.sort) {\n Object.keys(query.sort).forEach(k => fields.add(k));\n }\n } catch (e) {\n return 'ALL';\n }\n return fields.size > 0 ? fields : 'ALL';\n }\n\n private getChangedFields(oldValue: any, newValue: any): Set<string> | 'ALL' {\n // If values are arrays (ORMap), just return ALL for now to force check\n if (Array.isArray(oldValue) || Array.isArray(newValue)) return 'ALL';\n\n if (oldValue === newValue) return new Set();\n if (!oldValue && !newValue) return new Set();\n\n if (!oldValue) return new Set(Object.keys(newValue || {}));\n if (!newValue) return new Set(Object.keys(oldValue || {}));\n \n const changes = new Set<string>();\n const allKeys = new Set([...Object.keys(oldValue), ...Object.keys(newValue)]);\n \n for (const key of allKeys) {\n if (oldValue[key] !== newValue[key]) {\n changes.add(key);\n }\n }\n return changes;\n }\n}\n","import pino from 'pino';\n\nconst logLevel = process.env.LOG_LEVEL || 'info';\n\nexport const logger = pino({\n level: logLevel,\n transport: 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 formatters: {\n level: (label) => {\n return { level: label };\n }\n }\n});\n\nexport type Logger = typeof logger;\n\n","import { ClusterManager } from '../cluster/ClusterManager';\nimport { logger } from '../utils/logger';\n\nexport interface TopicManagerConfig {\n cluster: ClusterManager;\n /** Callback to send message to a specific client */\n sendToClient: (clientId: string, message: any) => void;\n}\n\nexport class TopicManager {\n private subscribers: Map<string, Set<string>> = new Map(); // topic -> Set<clientId>\n private cluster: ClusterManager;\n private sendToClient: (clientId: string, message: any) => void;\n private readonly MAX_SUBSCRIPTIONS = 100; // M1: Basic limit\n\n constructor(config: TopicManagerConfig) {\n this.cluster = config.cluster;\n this.sendToClient = config.sendToClient;\n }\n\n private validateTopic(topic: string): void {\n // H2: Validation\n if (!topic || topic.length > 256 || !/^[\\w\\-.:/]+$/.test(topic)) {\n throw new Error('Invalid topic name');\n }\n }\n\n /**\n * Subscribe a client to a topic\n */\n public subscribe(clientId: string, topic: string) {\n this.validateTopic(topic);\n\n // Check limit (M1)\n // This is expensive (iterating all topics). Optimized: maintain client->topics map?\n // For now, iterate.\n let count = 0;\n for (const subs of this.subscribers.values()) {\n if (subs.has(clientId)) count++;\n }\n if (count >= this.MAX_SUBSCRIPTIONS) {\n throw new Error('Subscription limit reached');\n }\n\n if (!this.subscribers.has(topic)) {\n this.subscribers.set(topic, new Set());\n }\n this.subscribers.get(topic)!.add(clientId);\n logger.debug({ clientId, topic }, 'Client subscribed to topic');\n }\n\n /**\n * Unsubscribe a client from a topic\n */\n public unsubscribe(clientId: string, topic: string) {\n const subs = this.subscribers.get(topic);\n if (subs) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscribers.delete(topic);\n }\n logger.debug({ clientId, topic }, 'Client unsubscribed from topic');\n }\n }\n\n /**\n * Clean up all subscriptions for a client (e.g. on disconnect)\n */\n public unsubscribeAll(clientId: string) {\n for (const [topic, subs] of this.subscribers) {\n if (subs.has(clientId)) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscribers.delete(topic);\n }\n }\n }\n }\n\n /**\n * Publish a message to a topic\n * @param topic Topic name\n * @param data Message data\n * @param senderId Client ID of the publisher (optional)\n * @param fromCluster Whether this message came from another cluster node\n */\n public publish(topic: string, data: any, senderId?: string, fromCluster: boolean = false) {\n this.validateTopic(topic);\n\n // 1. Send to local subscribers\n const subs = this.subscribers.get(topic);\n if (subs) {\n const payload = {\n topic,\n data,\n publisherId: senderId,\n timestamp: Date.now()\n };\n \n const message = {\n type: 'TOPIC_MESSAGE',\n payload\n };\n\n for (const clientId of subs) {\n // Don't echo back to sender if local\n if (clientId !== senderId) {\n this.sendToClient(clientId, message);\n }\n }\n }\n\n // 2. Broadcast to cluster (only if not already from cluster)\n if (!fromCluster) {\n this.cluster.getMembers().forEach(nodeId => {\n if (!this.cluster.isLocal(nodeId)) {\n this.cluster.send(nodeId, 'CLUSTER_TOPIC_PUB', {\n topic,\n data,\n originalSenderId: senderId\n });\n }\n });\n }\n }\n}\n\n","import { WebSocket, WebSocketServer, ClientOptions as WsClientOptions } from 'ws';\nimport { EventEmitter } from 'events';\nimport * as dns from 'dns';\nimport { logger } from '../utils/logger';\nimport { readFileSync } from 'fs';\nimport * as https from 'https';\nimport { ClusterTLSConfig } from '../types/TLSConfig';\n\nexport interface ClusterConfig {\n nodeId: string;\n host: string;\n port: number;\n peers: string[]; // List of \"host:port\"\n discovery?: 'manual' | 'kubernetes';\n serviceName?: string;\n discoveryInterval?: number;\n tls?: ClusterTLSConfig;\n}\n\nexport interface ClusterMember {\n nodeId: string;\n host: string;\n port: number;\n socket: WebSocket;\n isSelf: boolean;\n}\n\nexport interface ClusterMessage {\n type: 'HELLO' | 'OP_FORWARD' | 'PARTITION_UPDATE' | 'HEARTBEAT' | 'CLUSTER_EVENT' | 'CLUSTER_QUERY_EXEC' | 'CLUSTER_QUERY_RESP' | 'CLUSTER_GC_REPORT' | 'CLUSTER_GC_COMMIT' | 'CLUSTER_LOCK_REQ' | 'CLUSTER_LOCK_RELEASE' | 'CLUSTER_LOCK_GRANTED' | 'CLUSTER_LOCK_RELEASED' | 'CLUSTER_CLIENT_DISCONNECTED' | 'CLUSTER_TOPIC_PUB';\n senderId: string;\n payload: any;\n}\n\nexport class ClusterManager extends EventEmitter {\n public readonly config: ClusterConfig;\n private server?: WebSocketServer;\n private members: Map<string, ClusterMember> = new Map();\n private pendingConnections: Set<string> = new Set();\n private reconnectIntervals: Map<string, NodeJS.Timeout> = new Map();\n private discoveryTimer?: NodeJS.Timeout;\n\n constructor(config: ClusterConfig) {\n super();\n this.config = config;\n }\n\n private _actualPort: number = 0;\n\n /** Get the actual port the cluster is listening on */\n public get port(): number {\n return this._actualPort;\n }\n\n public start(): Promise<number> {\n return new Promise((resolve) => {\n logger.info({ port: this.config.port, tls: !!this.config.tls?.enabled }, 'Starting Cluster Manager');\n\n if (this.config.tls?.enabled) {\n // HTTPS-based WebSocket Server for cluster\n const tlsOptions = this.buildClusterTLSOptions();\n const httpsServer = https.createServer(tlsOptions);\n this.server = new WebSocketServer({ server: httpsServer });\n\n httpsServer.listen(this.config.port, () => {\n const addr = httpsServer.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : this.config.port;\n logger.info({ port: this._actualPort }, 'Cluster Manager listening (TLS enabled)');\n this.onServerReady(resolve);\n });\n } else {\n this.server = new WebSocketServer({ port: this.config.port });\n\n this.server.on('listening', () => {\n const addr = this.server!.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : this.config.port;\n logger.info({ port: this._actualPort }, 'Cluster Manager listening');\n this.onServerReady(resolve);\n });\n }\n\n this.server?.on('connection', (ws, req) => {\n logger.info({ remoteAddress: req.socket.remoteAddress }, 'Incoming cluster connection');\n this.handleSocket(ws, false);\n });\n });\n }\n\n /** Called when server is ready - registers self and initiates peer connections */\n private onServerReady(resolve: (port: number) => void): void {\n // Add self to members with actual port\n this.members.set(this.config.nodeId, {\n nodeId: this.config.nodeId,\n host: this.config.host,\n port: this._actualPort,\n socket: null as any,\n isSelf: true\n });\n\n // Connect to peers after we know our port\n if (this.config.discovery === 'kubernetes' && this.config.serviceName) {\n this.startDiscovery();\n } else {\n this.connectToPeers();\n }\n\n resolve(this._actualPort);\n }\n\n public stop() {\n logger.info({ port: this.config.port }, 'Stopping Cluster Manager');\n\n // Clear reconnect intervals\n for (const timeout of this.reconnectIntervals.values()) {\n clearTimeout(timeout);\n }\n this.reconnectIntervals.clear();\n if (this.discoveryTimer) {\n clearInterval(this.discoveryTimer);\n this.discoveryTimer = undefined;\n }\n this.pendingConnections.clear();\n\n // Close all peer connections\n for (const member of this.members.values()) {\n if (member.socket) {\n member.socket.terminate(); // Force close\n }\n }\n this.members.clear();\n\n // Close server\n if (this.server) {\n this.server.close();\n }\n }\n\n private connectToPeers() {\n for (const peer of this.config.peers) {\n this.connectToPeer(peer);\n }\n }\n\n private startDiscovery() {\n const runDiscovery = async () => {\n if (!this.config.serviceName) return;\n\n try {\n const addresses = await dns.promises.resolve4(this.config.serviceName);\n logger.debug({ addresses, serviceName: this.config.serviceName }, 'DNS discovery results');\n\n for (const ip of addresses) {\n // Use actual port if available (likely matching K8s config), fallback to config port\n const targetPort = this._actualPort || this.config.port;\n const peerAddress = `${ip}:${targetPort}`;\n // Attempt to connect. connectToPeer handles dupes and self-checks (via handshake eventually)\n this.connectToPeer(peerAddress);\n }\n } catch (err: any) {\n logger.error({ err: err.message, serviceName: this.config.serviceName }, 'DNS discovery failed');\n }\n };\n\n logger.info({ serviceName: this.config.serviceName }, 'Starting Kubernetes DNS discovery');\n runDiscovery();\n // Default to 10s if not specified, to be less aggressive\n this.discoveryTimer = setInterval(runDiscovery, this.config.discoveryInterval || 10000);\n }\n\n private scheduleReconnect(peerAddress: string, attempt: number = 0) {\n if (this.reconnectIntervals.has(peerAddress)) return;\n\n // Exponential backoff: 5s, 10s, 20s, 40s, 60s (max)\n const delay = Math.min(5000 * Math.pow(2, attempt), 60000);\n\n const timeout = setTimeout(() => {\n this.reconnectIntervals.delete(peerAddress);\n // Pass next attempt number\n this.connectToPeerWithBackoff(peerAddress, attempt + 1);\n }, delay);\n\n this.reconnectIntervals.set(peerAddress, timeout);\n }\n\n // Helper to track attempts\n private connectToPeerWithBackoff(peerAddress: string, attempt: number) {\n // We need to modify connectToPeer to accept attempt or create a wrapper.\n // To keep it simple without changing signature of connectToPeer everywhere,\n // we'll just call connectToPeer and let it fail -> scheduleReconnect -> increment attempt.\n // But connectToPeer logic needs to pass the attempt to scheduleReconnect on failure.\n // Refactoring connectToPeer to take optional attempt param.\n this._connectToPeerInternal(peerAddress, attempt);\n }\n\n private connectToPeer(peerAddress: string) {\n this._connectToPeerInternal(peerAddress, 0);\n }\n\n private _connectToPeerInternal(peerAddress: string, attempt: number) {\n if (this.pendingConnections.has(peerAddress)) return;\n\n // Check if already connected\n for (const member of this.members.values()) {\n if (`${member.host}:${member.port}` === peerAddress) return;\n }\n\n // PREVENT LOOP: ... (omitted comments)\n\n logger.info({ peerAddress, attempt, tls: !!this.config.tls?.enabled }, 'Connecting to peer');\n this.pendingConnections.add(peerAddress);\n\n try {\n let ws: WebSocket;\n\n if (this.config.tls?.enabled) {\n // Secure WebSocket connection\n const protocol = 'wss://';\n const wsOptions: WsClientOptions = {\n rejectUnauthorized: this.config.tls.rejectUnauthorized !== false,\n };\n\n // mTLS: Provide client certificate\n if (this.config.tls.certPath && this.config.tls.keyPath) {\n wsOptions.cert = readFileSync(this.config.tls.certPath);\n wsOptions.key = readFileSync(this.config.tls.keyPath);\n\n if (this.config.tls.passphrase) {\n wsOptions.passphrase = this.config.tls.passphrase;\n }\n }\n\n // CA for peer verification\n if (this.config.tls.caCertPath) {\n wsOptions.ca = readFileSync(this.config.tls.caCertPath);\n }\n\n ws = new WebSocket(`${protocol}${peerAddress}`, wsOptions);\n } else {\n // Plain WebSocket (development)\n ws = new WebSocket(`ws://${peerAddress}`);\n }\n\n ws.on('open', () => {\n this.pendingConnections.delete(peerAddress);\n logger.info({ peerAddress }, 'Connected to peer');\n // Reset backoff on success\n this.handleSocket(ws, true, peerAddress);\n });\n\n ws.on('error', (err) => {\n logger.error({ peerAddress, err: err.message }, 'Connection error to peer');\n this.pendingConnections.delete(peerAddress);\n this.scheduleReconnect(peerAddress, attempt);\n });\n\n ws.on('close', () => {\n this.pendingConnections.delete(peerAddress);\n });\n\n } catch (e) {\n this.pendingConnections.delete(peerAddress);\n this.scheduleReconnect(peerAddress, attempt);\n }\n }\n\n private handleSocket(ws: WebSocket, initiated: boolean, peerAddress?: string) {\n // Handshake: Send my NodeID with actual port (not config port which may be 0)\n const helloMsg: ClusterMessage = {\n type: 'HELLO',\n senderId: this.config.nodeId,\n payload: {\n host: this.config.host,\n port: this._actualPort || this.config.port\n }\n };\n ws.send(JSON.stringify(helloMsg));\n\n let remoteNodeId: string | null = null;\n\n ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString()) as ClusterMessage;\n\n if (msg.type === 'HELLO') {\n remoteNodeId = msg.senderId;\n const { host, port } = msg.payload;\n logger.info({ nodeId: remoteNodeId, host, port }, 'Peer identified');\n\n // Tie-Breaker Rule: Connection initiated by Low ID wins.\n // Initiator < Receiver = Valid.\n // Initiator > Receiver = Invalid (Drop).\n\n const myId = this.config.nodeId;\n const otherId = remoteNodeId;\n\n // Determine who initiated this specific socket\n const initiatorId = initiated ? myId : otherId;\n const receiverId = initiated ? otherId : myId;\n\n /*\n // Tie-Breaker Rule: Connection initiated by Low ID wins.\n // Initiator < Receiver = Valid.\n // Initiator > Receiver = Invalid (Drop).\n // \n // DISABLED: This strict rule prevents High-ID nodes from joining Low-ID seeds (common pattern).\n // We only use this for duplicate resolution now.\n \n if (initiatorId >= receiverId) {\n logger.info({ initiatorId, receiverId }, 'Dropping connection (Low-ID Initiator Policy)');\n try {\n ws.close();\n } catch(e) {}\n return;\n }\n */\n\n // If we get here, this is a VALID connection.\n // Check if we somehow already have a connection\n if (this.members.has(remoteNodeId)) {\n logger.warn({ nodeId: remoteNodeId }, 'Duplicate valid connection. Replacing.');\n // In a real production system, we should use the Tie-Breaker here to decide which one to keep\n // to avoid split-brain socket usage.\n // For now, 'Replacing' means Last-Write-Wins on the connection slot.\n }\n\n this.members.set(remoteNodeId, {\n nodeId: remoteNodeId,\n host,\n port,\n socket: ws,\n isSelf: false\n });\n\n this.emit('memberJoined', remoteNodeId);\n } else {\n this.emit('message', msg);\n }\n } catch (err) {\n logger.error({ err }, 'Failed to parse cluster message');\n }\n });\n\n ws.on('close', () => {\n if (remoteNodeId) {\n // Only handle disconnect if this was the ACTIVE socket\n // This prevents \"duplicate connection\" cleanup from killing the valid session\n const current = this.members.get(remoteNodeId);\n if (current && current.socket === ws) {\n logger.info({ nodeId: remoteNodeId }, 'Peer disconnected');\n this.members.delete(remoteNodeId);\n this.emit('memberLeft', remoteNodeId);\n\n // If we initiated, we should try to reconnect\n if (initiated && peerAddress) {\n // Start with 0 attempt on fresh disconnect? \n // Or maybe we should consider this a failure and backoff?\n // Let's restart with 0 for now as it might be a temp network blip\n this.scheduleReconnect(peerAddress, 0);\n }\n } else {\n // console.log(`Ignored close from stale/duplicate socket for ${remoteNodeId}`);\n }\n }\n });\n }\n\n public send(nodeId: string, type: ClusterMessage['type'], payload: any) {\n const member = this.members.get(nodeId);\n if (member && member.socket && member.socket.readyState === WebSocket.OPEN) {\n const msg: ClusterMessage = {\n type,\n senderId: this.config.nodeId,\n payload\n };\n member.socket.send(JSON.stringify(msg));\n } else {\n logger.warn({ nodeId }, 'Cannot send to node: not connected');\n }\n }\n\n public sendToNode(nodeId: string, message: any) {\n this.send(nodeId, 'OP_FORWARD', message);\n }\n\n public getMembers(): string[] {\n return Array.from(this.members.keys());\n }\n\n public isLocal(nodeId: string): boolean {\n return nodeId === this.config.nodeId;\n }\n\n private buildClusterTLSOptions(): https.ServerOptions {\n const config = this.config.tls!;\n\n const options: https.ServerOptions = {\n cert: readFileSync(config.certPath),\n key: readFileSync(config.keyPath),\n minVersion: config.minVersion || 'TLSv1.2',\n };\n\n if (config.caCertPath) {\n options.ca = readFileSync(config.caCertPath);\n }\n\n if (config.requireClientCert) {\n options.requestCert = true;\n options.rejectUnauthorized = true;\n }\n\n if (config.passphrase) {\n options.passphrase = config.passphrase;\n }\n\n return options;\n }\n}\n\n","import { ClusterManager } from './ClusterManager';\nimport { hashString } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport interface PartitionDistribution {\n owner: string;\n backups: string[];\n}\n\nexport class PartitionService {\n private cluster: ClusterManager;\n // partitionId -> { owner, backups }\n private partitions: Map<number, PartitionDistribution> = new Map();\n private readonly PARTITION_COUNT = 271;\n private readonly BACKUP_COUNT = 1; // Standard Hazelcast default\n\n constructor(cluster: ClusterManager) {\n this.cluster = cluster;\n this.cluster.on('memberJoined', () => this.rebalance());\n this.cluster.on('memberLeft', () => this.rebalance());\n \n // Initial rebalance\n this.rebalance();\n }\n\n public getPartitionId(key: string): number {\n // Use Math.abs to ensure positive partition ID\n return Math.abs(hashString(key)) % this.PARTITION_COUNT;\n }\n\n public getDistribution(key: string): PartitionDistribution {\n const pId = this.getPartitionId(key);\n return this.partitions.get(pId) || { \n owner: this.cluster.config.nodeId, \n backups: [] \n };\n }\n\n public getOwner(key: string): string {\n return this.getDistribution(key).owner;\n }\n\n public isLocalOwner(key: string): boolean {\n return this.getOwner(key) === this.cluster.config.nodeId;\n }\n\n public isLocalBackup(key: string): boolean {\n const dist = this.getDistribution(key);\n return dist.backups.includes(this.cluster.config.nodeId);\n }\n\n public isRelated(key: string): boolean {\n return this.isLocalOwner(key) || this.isLocalBackup(key);\n }\n\n private rebalance() {\n // this.cluster.getMembers() includes self (added in ClusterManager.start)\n let allMembers = this.cluster.getMembers().sort();\n\n // If no other members, include self\n if (allMembers.length === 0) {\n allMembers = [this.cluster.config.nodeId];\n }\n\n logger.info({ memberCount: allMembers.length, members: allMembers }, 'Rebalancing partitions');\n\n for (let i = 0; i < this.PARTITION_COUNT; i++) {\n const ownerIndex = i % allMembers.length;\n const owner = allMembers[ownerIndex];\n \n const backups: string[] = [];\n if (allMembers.length > 1) {\n for (let b = 1; b <= this.BACKUP_COUNT; b++) {\n const backupIndex = (ownerIndex + b) % allMembers.length;\n backups.push(allMembers[backupIndex]);\n }\n }\n\n this.partitions.set(i, { owner, backups });\n }\n }\n}\n","import { EventEmitter } from 'events';\nimport { logger } from '../utils/logger';\n\nexport interface LockRequest {\n clientId: string;\n requestId: string;\n ttl: number;\n timestamp: number;\n}\n\nexport interface LockState {\n name: string;\n owner: string; // clientId\n fencingToken: number;\n expiry: number;\n queue: LockRequest[];\n}\n\nexport class LockManager extends EventEmitter {\n private locks: Map<string, LockState> = new Map();\n private checkInterval: NodeJS.Timeout;\n\n private static readonly MIN_TTL = 1000; // 1 second\n private static readonly MAX_TTL = 300000; // 5 minutes\n\n constructor() {\n super();\n this.checkInterval = setInterval(() => this.cleanupExpiredLocks(), 1000);\n }\n\n public stop() {\n clearInterval(this.checkInterval);\n }\n\n public acquire(name: string, clientId: string, requestId: string, ttl: number): { granted: boolean; fencingToken?: number; error?: string } {\n // Validate TTL\n const safeTtl = Math.max(LockManager.MIN_TTL, Math.min(ttl || LockManager.MIN_TTL, LockManager.MAX_TTL));\n\n let lock = this.locks.get(name);\n if (!lock) {\n lock = {\n name,\n owner: '',\n fencingToken: 0,\n expiry: 0,\n queue: []\n };\n this.locks.set(name, lock);\n }\n\n const now = Date.now();\n\n // If lock is free or expired\n if (!lock.owner || lock.expiry < now) {\n this.grantLock(lock, clientId, safeTtl);\n return { granted: true, fencingToken: lock.fencingToken };\n }\n\n // If already owned by same client, extend lease\n if (lock.owner === clientId) {\n lock.expiry = Math.max(lock.expiry, now + safeTtl);\n logger.info({ name, clientId, fencingToken: lock.fencingToken }, 'Lock lease extended');\n return { granted: true, fencingToken: lock.fencingToken };\n }\n\n // Queue request\n lock.queue.push({ clientId, requestId, ttl: safeTtl, timestamp: now });\n logger.info({ name, clientId, queueLength: lock.queue.length }, 'Lock queued');\n return { granted: false };\n }\n\n public release(name: string, clientId: string, fencingToken: number): boolean {\n const lock = this.locks.get(name);\n if (!lock) return false;\n\n if (lock.owner !== clientId) {\n logger.warn({ name, clientId, owner: lock.owner }, 'Release failed: Not owner');\n return false;\n }\n\n if (lock.fencingToken !== fencingToken) {\n logger.warn({ name, clientId, sentToken: fencingToken, actualToken: lock.fencingToken }, 'Release failed: Token mismatch');\n return false;\n }\n\n this.processNext(lock);\n return true;\n }\n\n public handleClientDisconnect(clientId: string) {\n for (const lock of this.locks.values()) {\n // 1. If client owns the lock, force release\n if (lock.owner === clientId) {\n logger.info({ name: lock.name, clientId }, 'Releasing lock due to disconnect');\n this.processNext(lock);\n } else {\n // 2. Remove from queue if present\n const initialLen = lock.queue.length;\n lock.queue = lock.queue.filter(req => req.clientId !== clientId);\n if (lock.queue.length < initialLen) {\n logger.info({ name: lock.name, clientId }, 'Removed from lock queue due to disconnect');\n }\n }\n }\n }\n\n private grantLock(lock: LockState, clientId: string, ttl: number) {\n lock.owner = clientId;\n lock.expiry = Date.now() + ttl;\n lock.fencingToken++;\n logger.info({ name: lock.name, clientId, fencingToken: lock.fencingToken }, 'Lock granted');\n }\n\n private processNext(lock: LockState) {\n const now = Date.now();\n \n // Reset owner\n lock.owner = '';\n lock.expiry = 0;\n\n // Process queue\n while (lock.queue.length > 0) {\n const next = lock.queue.shift()!;\n \n // Grant to next\n this.grantLock(lock, next.clientId, next.ttl);\n \n // Emit event so ServerCoordinator can notify the client\n this.emit('lockGranted', {\n clientId: next.clientId,\n requestId: next.requestId,\n name: lock.name,\n fencingToken: lock.fencingToken\n });\n \n return;\n }\n \n // No one waiting\n if (lock.queue.length === 0) {\n this.locks.delete(lock.name);\n }\n }\n\n private cleanupExpiredLocks() {\n const now = Date.now();\n // Use a copy of keys to avoid concurrent modification issues during iteration\n const lockNames = Array.from(this.locks.keys());\n \n for (const name of lockNames) {\n const lock = this.locks.get(name);\n if (!lock) continue;\n\n if (lock.owner && lock.expiry < now) {\n logger.info({ name: lock.name, owner: lock.owner }, 'Lock expired, processing next');\n this.processNext(lock);\n } else if (!lock.owner && lock.queue.length === 0) {\n // Cleanup empty orphaned locks\n this.locks.delete(name);\n }\n }\n }\n}\n\n","import { PermissionPolicy, Principal, PermissionType } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport class SecurityManager {\n private policies: PermissionPolicy[] = [];\n\n constructor(policies: PermissionPolicy[] = []) {\n this.policies = policies;\n }\n\n public addPolicy(policy: PermissionPolicy) {\n this.policies.push(policy);\n }\n\n public checkPermission(principal: Principal, mapName: string, action: PermissionType): boolean {\n // 1. Superuser check (optional, but good practice)\n if (principal.roles.includes('ADMIN')) {\n return true;\n }\n\n // 2. System Map Protection\n if (mapName.startsWith('$sys/')) {\n logger.warn({ userId: principal.userId, mapName }, 'Access Denied: System Map requires ADMIN role');\n return false;\n }\n\n // 2. Iterate policies to find a match\n for (const policy of this.policies) {\n const hasRole = this.hasRole(principal, policy.role);\n const matchesMap = this.matchesMap(mapName, policy.mapNamePattern, principal);\n\n if (hasRole && matchesMap) {\n if (policy.actions.includes('ALL') || policy.actions.includes(action)) {\n return true;\n }\n } else {\n // Trace why it failed matching if needed (verbose)\n // logger.trace({ policy, hasRole, matchesMap, mapName, user: principal.userId }, 'Policy mismatch');\n }\n }\n\n logger.warn({\n userId: principal.userId,\n roles: principal.roles,\n mapName,\n action,\n policyCount: this.policies.length\n }, 'SecurityManager: Access Denied - No matching policy found');\n\n return false;\n }\n\n public filterObject(object: any, principal: Principal, mapName: string): any {\n if (!object || typeof object !== 'object') return object;\n if (principal.roles.includes('ADMIN')) return object;\n\n if (Array.isArray(object)) {\n return object.map(item => this.filterObject(item, principal, mapName));\n }\n\n let allowedFields: Set<string> | null = null;\n let accessGranted = false;\n\n for (const policy of this.policies) {\n if (this.hasRole(principal, policy.role) && this.matchesMap(mapName, policy.mapNamePattern, principal)) {\n if (policy.actions.includes('ALL') || policy.actions.includes('READ')) {\n accessGranted = true;\n\n // If any policy allows everything, return immediately\n if (!policy.allowedFields || policy.allowedFields.length === 0 || policy.allowedFields.includes('*')) {\n return object;\n }\n\n if (allowedFields === null) allowedFields = new Set();\n policy.allowedFields.forEach(f => allowedFields!.add(f));\n }\n }\n }\n\n if (!accessGranted) return null;\n if (allowedFields === null) return object; // Should have returned above, but as fallback\n\n const filtered: any = {};\n for (const key of Object.keys(object)) {\n if (allowedFields.has(key)) {\n filtered[key] = object[key];\n }\n }\n return filtered;\n }\n\n private hasRole(principal: Principal, role: string): boolean {\n return principal.roles.includes(role);\n }\n\n private matchesMap(mapName: string, pattern: string, principal?: Principal): boolean {\n // Dynamic substitution for {userId}\n let finalPattern = pattern;\n if (pattern.includes('{userId}') && principal) {\n finalPattern = pattern.replace('{userId}', principal.userId);\n }\n\n if (finalPattern === '*') return true;\n if (finalPattern === mapName) return true;\n\n if (finalPattern.endsWith('*')) {\n const prefix = finalPattern.slice(0, -1);\n return mapName.startsWith(prefix);\n }\n\n return false;\n }\n}\n","import { Registry, Gauge, Counter, collectDefaultMetrics } from 'prom-client';\n\nexport class MetricsService {\n public readonly registry: Registry;\n\n // Metrics\n private connectedClients: Gauge;\n private mapSizeItems: Gauge;\n private opsTotal: Counter;\n private memoryUsage: Gauge;\n private clusterMembers: Gauge;\n\n constructor() {\n this.registry = new Registry();\n\n // Enable default nodejs metrics (cpu, memory, etc.)\n collectDefaultMetrics({ register: this.registry, prefix: 'topgun_' });\n\n this.connectedClients = new Gauge({\n name: 'topgun_connected_clients',\n help: 'Number of currently connected clients',\n registers: [this.registry],\n });\n\n this.mapSizeItems = new Gauge({\n name: 'topgun_map_size_items',\n help: 'Number of items in a map',\n labelNames: ['map'],\n registers: [this.registry],\n });\n\n this.opsTotal = new Counter({\n name: 'topgun_ops_total',\n help: 'Total number of operations',\n labelNames: ['type', 'map'],\n registers: [this.registry],\n });\n\n this.memoryUsage = new Gauge({\n name: 'topgun_memory_usage_bytes',\n help: 'Current memory usage in bytes',\n registers: [this.registry],\n collect() {\n this.set(process.memoryUsage().heapUsed);\n }\n });\n\n this.clusterMembers = new Gauge({\n name: 'topgun_cluster_members',\n help: 'Number of active cluster members',\n registers: [this.registry],\n });\n }\n\n public destroy() {\n this.registry.clear();\n }\n\n public setConnectedClients(count: number) {\n this.connectedClients.set(count);\n }\n\n public setMapSize(mapName: string, size: number) {\n this.mapSizeItems.set({ map: mapName }, size);\n }\n\n public incOp(type: 'PUT' | 'GET' | 'DELETE' | 'SUBSCRIBE', mapName: string) {\n this.opsTotal.inc({ type, map: mapName });\n }\n\n public setClusterMembers(count: number) {\n this.clusterMembers.set(count);\n }\n\n public async getMetrics(): Promise<string> {\n return this.registry.metrics();\n }\n\n public async getMetricsJson(): Promise<Record<string, any>> {\n const metrics = await this.registry.getMetricsAsJSON();\n // Flatten or simplify for dashboard if needed, but raw JSON is fine for now\n const result: Record<string, any> = {};\n for (const metric of metrics) {\n // Simple flattening: name -> value (if single value)\n // metric.type is an enum/number in some versions or string in others. \n // To be safe and avoid type errors, we just check values length.\n if (metric.values.length === 1) {\n result[metric.name] = metric.values[0].value;\n } else {\n // Complex metrics\n result[metric.name] = metric.values;\n }\n }\n return result;\n }\n\n public getContentType(): string {\n return this.registry.contentType;\n }\n}\n\n","import { LWWMap, LWWRecord } from '@topgunbuild/core';\nimport { ClusterManager } from '../cluster/ClusterManager';\nimport { MetricsService } from '../monitoring/MetricsService';\nimport { logger } from '../utils/logger';\n\nexport class SystemManager {\n private cluster: ClusterManager;\n private metrics: MetricsService;\n private getMap: (name: string) => LWWMap<string, any>;\n\n private statsInterval?: NodeJS.Timeout;\n\n constructor(\n cluster: ClusterManager,\n metrics: MetricsService,\n getMap: (name: string) => LWWMap<string, any>\n ) {\n this.cluster = cluster;\n this.metrics = metrics;\n this.getMap = getMap;\n }\n\n public start() {\n this.setupClusterMap();\n this.setupStatsMap();\n this.setupMapsMap();\n\n // Update stats every 5 seconds\n this.statsInterval = setInterval(() => this.updateStats(), 5000);\n\n // Listen for cluster events\n this.cluster.on('memberJoined', () => this.updateClusterMap());\n this.cluster.on('memberLeft', () => this.updateClusterMap());\n\n // Initial updates\n this.updateClusterMap();\n this.updateStats();\n }\n\n public stop() {\n if (this.statsInterval) {\n clearInterval(this.statsInterval);\n }\n }\n\n public notifyMapCreated(mapName: string) {\n if (mapName.startsWith('$sys/')) return; // Don't track system maps\n this.updateMapsMap(mapName);\n }\n\n private setupClusterMap() {\n // Ensure map exists\n this.getMap('$sys/cluster');\n }\n\n private setupStatsMap() {\n this.getMap('$sys/stats');\n }\n\n private setupMapsMap() {\n this.getMap('$sys/maps');\n }\n\n private updateClusterMap() {\n try {\n const map = this.getMap('$sys/cluster');\n const members = this.cluster.getMembers();\n\n // We can't easily \"remove\" missing members without iterating the whole map\n // For now, we just put current members.\n // A proper sync would require diffing.\n\n // In a real implementation, we might want to store more info than just ID.\n // But ClusterManager currently only gives us IDs easily or we have to look them up.\n // Let's iterate members map from ClusterManager if possible, or just use IDs.\n\n // Accessing private members map via any cast for now or just using IDs\n // The ClusterManager.getMembers() returns IDs.\n\n for (const memberId of members) {\n const isLocal = this.cluster.isLocal(memberId);\n map.set(memberId, {\n id: memberId,\n status: 'UP',\n isLocal,\n lastUpdated: Date.now()\n });\n }\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/cluster');\n }\n }\n\n private async updateStats() {\n try {\n const map = this.getMap('$sys/stats');\n const metrics = await this.metrics.getMetricsJson(); // We need to add getMetricsJson to MetricsService\n\n map.set(this.cluster.config.nodeId, {\n ...metrics,\n timestamp: Date.now()\n });\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/stats');\n }\n }\n\n private updateMapsMap(mapName: string) {\n try {\n const map = this.getMap('$sys/maps');\n map.set(mapName, {\n name: mapName,\n createdAt: Date.now()\n });\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/maps');\n }\n }\n}\n","import { Pool, PoolConfig } from 'pg';\nimport { LWWRecord } from '@topgunbuild/core';\nimport { IServerStorage, StorageValue } from './IServerStorage';\n\nexport interface PostgresAdapterOptions {\n tableName?: string;\n}\n\nconst DEFAULT_TABLE_NAME = 'topgun_maps';\nconst TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction validateTableName(name: string): void {\n if (!TABLE_NAME_REGEX.test(name)) {\n throw new Error(\n `Invalid table name \"${name}\". Table name must start with a letter or underscore and contain only alphanumeric characters and underscores.`\n );\n }\n}\n\nexport class PostgresAdapter implements IServerStorage {\n private pool: Pool;\n private tableName: string;\n\n constructor(configOrPool: PoolConfig | Pool, options?: PostgresAdapterOptions) {\n if (configOrPool instanceof Pool || (configOrPool as any).connect) {\n this.pool = configOrPool as Pool;\n } else {\n this.pool = new Pool(configOrPool as PoolConfig);\n }\n\n const tableName = options?.tableName ?? DEFAULT_TABLE_NAME;\n validateTableName(tableName);\n this.tableName = tableName;\n }\n\n async initialize(): Promise<void> {\n const client = await this.pool.connect();\n try {\n // Create a generic table for storing key-value pairs per map\n // schema: map_name (text), key (text), value (jsonb), timestamp_millis (bigint), timestamp_counter (int), node_id (text), is_deleted (boolean)\n await client.query(`\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n map_name TEXT NOT NULL,\n key TEXT NOT NULL,\n value JSONB,\n ts_millis BIGINT NOT NULL,\n ts_counter INTEGER NOT NULL,\n ts_node_id TEXT NOT NULL,\n is_deleted BOOLEAN DEFAULT FALSE,\n PRIMARY KEY (map_name, key)\n );\n `);\n } finally {\n client.release();\n }\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n async load(mapName: string, key: string): Promise<StorageValue<any> | undefined> {\n const res = await this.pool.query(\n `SELECT value, ts_millis, ts_counter, ts_node_id, is_deleted \n FROM ${this.tableName} \n WHERE map_name = $1 AND key = $2`,\n [mapName, key]\n );\n\n if (res.rows.length === 0) return undefined;\n\n const row = res.rows[0];\n return this.mapRowToRecord(row);\n }\n\n async loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>> {\n const result = new Map<string, StorageValue<any>>();\n if (keys.length === 0) return result;\n\n const res = await this.pool.query(\n `SELECT key, value, ts_millis, ts_counter, ts_node_id, is_deleted \n FROM ${this.tableName} \n WHERE map_name = $1 AND key = ANY($2)`,\n [mapName, keys]\n );\n\n for (const row of res.rows) {\n result.set(row.key, this.mapRowToRecord(row));\n }\n\n return result;\n }\n\n async loadAllKeys(mapName: string): Promise<string[]> {\n const res = await this.pool.query(\n `SELECT key FROM ${this.tableName} WHERE map_name = $1`,\n [mapName]\n );\n return res.rows.map(row => row.key);\n }\n\n async store(mapName: string, key: string, record: StorageValue<any>): Promise<void> {\n let value: any;\n let tsMillis: number;\n let tsCounter: number;\n let tsNodeId: string;\n let isDeleted: boolean;\n\n if (this.isORMapValue(record)) {\n // Store ORMap data\n // We use a special marker in ts_node_id to distinguish ORMap data from LWW data\n value = record;\n tsMillis = 0;\n tsCounter = 0;\n tsNodeId = '__ORMAP__';\n isDeleted = false;\n } else {\n // LWWRecord\n const lww = record as LWWRecord<any>;\n value = lww.value;\n tsMillis = lww.timestamp.millis;\n tsCounter = lww.timestamp.counter;\n tsNodeId = lww.timestamp.nodeId;\n isDeleted = lww.value === null;\n }\n\n await this.pool.query(\n `INSERT INTO ${this.tableName} (map_name, key, value, ts_millis, ts_counter, ts_node_id, is_deleted)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (map_name, key) DO UPDATE SET\n value = EXCLUDED.value,\n ts_millis = EXCLUDED.ts_millis,\n ts_counter = EXCLUDED.ts_counter,\n ts_node_id = EXCLUDED.ts_node_id,\n is_deleted = EXCLUDED.is_deleted`,\n [\n mapName,\n key,\n JSON.stringify(value),\n tsMillis,\n tsCounter,\n tsNodeId,\n isDeleted\n ]\n );\n }\n\n async storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n // Note: For high performance, this should use UNNEST or multi-row INSERT.\n // Keeping loop for simplicity in MVP alignment.\n for (const [key, record] of records) {\n await this.store(mapName, key, record); \n }\n await client.query('COMMIT');\n } catch (e) {\n await client.query('ROLLBACK');\n throw e;\n } finally {\n client.release();\n }\n }\n\n async delete(mapName: string, key: string): Promise<void> {\n await this.pool.query(`DELETE FROM ${this.tableName} WHERE map_name = $1 AND key = $2`, [mapName, key]);\n }\n\n async deleteAll(mapName: string, keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.pool.query(\n `DELETE FROM ${this.tableName} WHERE map_name = $1 AND key = ANY($2)`, \n [mapName, keys]\n );\n }\n\n private mapRowToRecord(row: any): StorageValue<any> {\n if (row.ts_node_id === '__ORMAP__') {\n // It's an ORMap value (ORMapValue or ORMapTombstones)\n return row.value as StorageValue<any>;\n }\n\n // It's LWWRecord\n return {\n value: row.is_deleted ? null : row.value,\n timestamp: {\n millis: Number(row.ts_millis),\n counter: row.ts_counter,\n nodeId: row.ts_node_id\n }\n };\n }\n\n private isORMapValue(record: any): boolean {\n return (record && typeof record === 'object' && (record.type === 'OR' || record.type === 'OR_TOMBSTONES'));\n }\n}\n","import { IServerStorage, StorageValue } from './IServerStorage';\n\n/**\n * In-memory implementation of IServerStorage.\n * Useful for development, testing, and demos without requiring a database.\n *\n * Note: Data is lost when the server restarts.\n */\nexport class MemoryServerAdapter implements IServerStorage {\n // Map<mapName, Map<key, value>>\n private storage = new Map<string, Map<string, StorageValue<any>>>();\n\n async initialize(): Promise<void> {\n // No-op for in-memory storage\n console.log('[MemoryServerAdapter] Initialized in-memory storage');\n }\n\n async close(): Promise<void> {\n this.storage.clear();\n console.log('[MemoryServerAdapter] Storage cleared and closed');\n }\n\n private getMap(mapName: string): Map<string, StorageValue<any>> {\n let map = this.storage.get(mapName);\n if (!map) {\n map = new Map();\n this.storage.set(mapName, map);\n }\n return map;\n }\n\n async load(mapName: string, key: string): Promise<StorageValue<any> | undefined> {\n return this.getMap(mapName).get(key);\n }\n\n async loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>> {\n const map = this.getMap(mapName);\n const result = new Map<string, StorageValue<any>>();\n for (const key of keys) {\n const value = map.get(key);\n if (value !== undefined) {\n result.set(key, value);\n }\n }\n return result;\n }\n\n async loadAllKeys(mapName: string): Promise<string[]> {\n return Array.from(this.getMap(mapName).keys());\n }\n\n async store(mapName: string, key: string, record: StorageValue<any>): Promise<void> {\n this.getMap(mapName).set(key, record);\n }\n\n async storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void> {\n const map = this.getMap(mapName);\n for (const [key, value] of records) {\n map.set(key, value);\n }\n }\n\n async delete(mapName: string, key: string): Promise<void> {\n this.getMap(mapName).delete(key);\n }\n\n async deleteAll(mapName: string, keys: string[]): Promise<void> {\n const map = this.getMap(mapName);\n for (const key of keys) {\n map.delete(key);\n }\n }\n}\n","import { IInterceptor, ServerOp, OpContext } from './IInterceptor';\nimport { logger } from '../utils/logger';\n\nexport class TimestampInterceptor implements IInterceptor {\n name = 'TimestampInterceptor';\n\n async onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp> {\n // Only apply to PUT operations with LWW records\n if (op.opType === 'PUT' && op.record && op.record.value) {\n // Modifying the value to include server timestamp\n // This assumes value is an object where we can add properties\n if (typeof op.record.value === 'object' && op.record.value !== null && !Array.isArray(op.record.value)) {\n const newValue = {\n ...op.record.value,\n _serverTimestamp: Date.now()\n };\n logger.debug({ key: op.key, mapName: op.mapName, interceptor: this.name }, 'Added timestamp');\n return {\n ...op,\n record: {\n ...op.record,\n value: newValue\n }\n };\n }\n }\n return op;\n }\n}\n","import { IInterceptor, ServerOp, OpContext } from './IInterceptor';\nimport { logger } from '../utils/logger';\n\ninterface RateLimitConfig {\n windowMs: number;\n maxOps: number;\n}\n\ninterface ClientLimit {\n count: number;\n resetTime: number;\n}\n\nexport class RateLimitInterceptor implements IInterceptor {\n name = 'RateLimitInterceptor';\n \n private limits = new Map<string, ClientLimit>();\n private config: RateLimitConfig;\n\n constructor(config: RateLimitConfig = { windowMs: 1000, maxOps: 50 }) {\n this.config = config;\n }\n\n async onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp | null> {\n // Rate limit based on clientId\n const clientId = context.clientId;\n const now = Date.now();\n \n let limit = this.limits.get(clientId);\n \n if (!limit || now > limit.resetTime) {\n limit = {\n count: 0,\n resetTime: now + this.config.windowMs\n };\n this.limits.set(clientId, limit);\n }\n\n limit.count++;\n\n if (limit.count > this.config.maxOps) {\n logger.warn({ clientId, opId: op.id, count: limit.count }, 'Rate limit exceeded');\n throw new Error('Rate limit exceeded');\n }\n\n return op;\n }\n\n // Cleanup old entries periodically? \n // For now we rely on resetTime check, but map grows. \n // Simple cleanup on reset logic:\n // In a real system, we'd use Redis or a proper cache with TTL.\n // Here we can just prune occasionally or relying on connection disconnect?\n \n // Optimization: Cleanup on disconnect\n async onDisconnect(context: any) {\n this.limits.delete(context.clientId);\n }\n}\n\n"],"mappings":"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","names":["readFileSync","WebSocketServer","WebSocket","LWWMap","ORMap","serialize","EventEmitter","EventEmitter","WebSocketServer","WebSocket","serialize","LWWMap","ORMap","readFileSync"]}
1
+ {"version":3,"sources":["../src/ServerCoordinator.ts","../src/query/Matcher.ts","../src/query/QueryRegistry.ts","../src/utils/logger.ts","../src/topic/TopicManager.ts","../src/cluster/ClusterManager.ts","../src/cluster/PartitionService.ts","../src/cluster/LockManager.ts","../src/security/SecurityManager.ts","../src/monitoring/MetricsService.ts","../src/system/SystemManager.ts","../src/storage/PostgresAdapter.ts","../src/storage/MemoryServerAdapter.ts","../src/interceptor/TimestampInterceptor.ts","../src/interceptor/RateLimitInterceptor.ts"],"sourcesContent":["import { createServer as createHttpServer, Server as HttpServer } from 'http';\nimport { createServer as createHttpsServer, Server as HttpsServer, ServerOptions as HttpsServerOptions } from 'https';\nimport { readFileSync } from 'fs';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { HLC, LWWMap, ORMap, MerkleTree, serialize, deserialize, PermissionPolicy, Principal, PermissionType, Timestamp, LWWRecord, ORMapRecord, MessageSchema } from '@topgunbuild/core';\nimport { IServerStorage, StorageValue, ORMapValue, ORMapTombstones } from './storage/IServerStorage';\nimport { IInterceptor, ServerOp, OpContext, ConnectionContext } from './interceptor/IInterceptor';\nimport * as jwt from 'jsonwebtoken';\nimport * as crypto from 'crypto';\nimport { QueryRegistry, Subscription } from './query/QueryRegistry';\n\nconst GC_INTERVAL_MS = 60 * 60 * 1000; // 1 hour\nconst GC_AGE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\nconst CLIENT_HEARTBEAT_TIMEOUT_MS = 20000; // 20 seconds - evict clients that haven't pinged\nconst CLIENT_HEARTBEAT_CHECK_INTERVAL_MS = 5000; // Check for dead clients every 5 seconds\nimport { TopicManager } from './topic/TopicManager';\nimport { ClusterManager } from './cluster/ClusterManager';\nimport { PartitionService } from './cluster/PartitionService';\nimport { LockManager } from './cluster/LockManager';\nimport { executeQuery, Query } from './query/Matcher';\nimport { SecurityManager } from './security/SecurityManager';\nimport { logger } from './utils/logger';\nimport { MetricsService } from './monitoring/MetricsService';\nimport { SystemManager } from './system/SystemManager';\nimport { TLSConfig, ClusterTLSConfig } from './types/TLSConfig';\n\ninterface ClientConnection {\n id: string;\n socket: WebSocket;\n principal?: Principal; // Auth info\n isAuthenticated: boolean;\n subscriptions: Set<string>; // Set of Query IDs\n lastActiveHlc: Timestamp;\n lastPingReceived: number; // Date.now() of last PING received\n}\n\ninterface PendingClusterQuery {\n requestId: string;\n client: ClientConnection;\n queryId: string; // Client's Query ID\n mapName: string;\n query: Query;\n results: { key: string; value: any }[];\n expectedNodes: Set<string>;\n respondedNodes: Set<string>;\n timer: NodeJS.Timeout;\n}\n\nexport interface ServerCoordinatorConfig {\n port: number;\n nodeId: string;\n storage?: IServerStorage;\n jwtSecret?: string;\n host?: string;\n clusterPort?: number;\n peers?: string[];\n securityPolicies?: PermissionPolicy[];\n /** Callback to resolve dynamic peer addresses after ports are known */\n resolvePeers?: () => string[];\n interceptors?: IInterceptor[];\n metricsPort?: number;\n discovery?: 'manual' | 'kubernetes';\n serviceName?: string;\n discoveryInterval?: number;\n tls?: TLSConfig;\n clusterTls?: ClusterTLSConfig;\n}\n\nexport class ServerCoordinator {\n private httpServer: HttpServer | HttpsServer;\n private metricsServer?: HttpServer;\n private metricsService: MetricsService;\n private wss: WebSocketServer;\n private clients: Map<string, ClientConnection> = new Map();\n\n // Interceptors\n private interceptors: IInterceptor[] = [];\n\n // In-memory storage (partitioned later)\n private maps: Map<string, LWWMap<string, any> | ORMap<string, any>> = new Map();\n private hlc: HLC;\n private storage?: IServerStorage;\n private jwtSecret: string;\n private queryRegistry: QueryRegistry;\n\n private cluster!: ClusterManager;\n private partitionService!: PartitionService;\n private lockManager!: LockManager;\n private topicManager!: TopicManager;\n private securityManager: SecurityManager;\n private systemManager!: SystemManager;\n\n private pendingClusterQueries: Map<string, PendingClusterQuery> = new Map();\n private gcInterval?: NodeJS.Timeout;\n private heartbeatCheckInterval?: NodeJS.Timeout;\n\n // GC Consensus State\n private gcReports: Map<string, Timestamp> = new Map();\n\n // Track map loading state to avoid returning empty results during async load\n private mapLoadingPromises: Map<string, Promise<void>> = new Map();\n\n private _actualPort: number = 0;\n private _actualClusterPort: number = 0;\n private _readyPromise: Promise<void>;\n private _readyResolve!: () => void;\n\n constructor(config: ServerCoordinatorConfig) {\n this._readyPromise = new Promise((resolve) => {\n this._readyResolve = resolve;\n });\n\n this.hlc = new HLC(config.nodeId);\n this.storage = config.storage;\n // Handle JWT_SECRET with escaped newlines (e.g., from Docker/Dokploy env vars)\n const rawSecret = config.jwtSecret || process.env.JWT_SECRET || 'topgun-secret-dev';\n this.jwtSecret = rawSecret.replace(/\\\\n/g, '\\n');\n this.queryRegistry = new QueryRegistry();\n this.securityManager = new SecurityManager(config.securityPolicies || []);\n this.interceptors = config.interceptors || [];\n this.metricsService = new MetricsService();\n\n // HTTP Server Setup first (to get actual port if port=0)\n if (config.tls?.enabled) {\n const tlsOptions = this.buildTLSOptions(config.tls);\n this.httpServer = createHttpsServer(tlsOptions, (_req, res) => {\n res.writeHead(200);\n res.end('TopGun Server Running (Secure)');\n });\n logger.info('TLS enabled for client connections');\n } else {\n this.httpServer = createHttpServer((_req, res) => {\n res.writeHead(200);\n res.end('TopGun Server Running');\n });\n\n if (process.env.NODE_ENV === 'production') {\n logger.warn('⚠️ TLS is disabled! Client connections are NOT encrypted.');\n }\n }\n\n const metricsPort = config.metricsPort !== undefined ? config.metricsPort : 9090;\n this.metricsServer = createHttpServer(async (req, res) => {\n if (req.url === '/metrics') {\n try {\n res.setHeader('Content-Type', this.metricsService.getContentType());\n res.end(await this.metricsService.getMetrics());\n } catch (err) {\n res.statusCode = 500;\n res.end('Internal Server Error');\n }\n } else {\n res.statusCode = 404;\n res.end();\n }\n });\n this.metricsServer.listen(metricsPort, () => {\n logger.info({ port: metricsPort }, 'Metrics server listening');\n });\n this.metricsServer.on('error', (err) => {\n logger.error({ err, port: metricsPort }, 'Metrics server failed to start');\n });\n\n this.wss = new WebSocketServer({ server: this.httpServer });\n this.wss.on('connection', (ws) => this.handleConnection(ws));\n\n // Use port 0 to let OS assign a free port\n this.httpServer.listen(config.port, () => {\n const addr = this.httpServer.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : config.port;\n logger.info({ port: this._actualPort }, 'Server Coordinator listening');\n\n // Now setup cluster with actual/configured cluster port\n const clusterPort = config.clusterPort ?? 0;\n\n // Resolve peers dynamically if callback provided\n const peers = config.resolvePeers ? config.resolvePeers() : (config.peers || []);\n\n this.cluster = new ClusterManager({\n nodeId: config.nodeId,\n host: config.host || 'localhost',\n port: clusterPort,\n peers,\n discovery: config.discovery,\n serviceName: config.serviceName,\n discoveryInterval: config.discoveryInterval,\n tls: config.clusterTls\n });\n this.partitionService = new PartitionService(this.cluster);\n this.lockManager = new LockManager();\n this.lockManager.on('lockGranted', (evt) => this.handleLockGranted(evt));\n\n this.topicManager = new TopicManager({\n cluster: this.cluster,\n sendToClient: (clientId, message) => {\n const client = this.clients.get(clientId);\n if (client && client.socket.readyState === WebSocket.OPEN) {\n client.socket.send(serialize(message));\n }\n }\n });\n\n this.systemManager = new SystemManager(\n this.cluster,\n this.metricsService,\n (name) => this.getMap(name) as LWWMap<string, any>\n );\n\n this.setupClusterListeners();\n this.cluster.start().then((actualClusterPort) => {\n this._actualClusterPort = actualClusterPort;\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n logger.info({ clusterPort: this._actualClusterPort }, 'Cluster started');\n this.systemManager.start();\n this._readyResolve();\n }).catch((err) => {\n // Fallback for ClusterManager that doesn't return port\n this._actualClusterPort = clusterPort;\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n logger.info({ clusterPort: this._actualClusterPort }, 'Cluster started (sync)');\n this.systemManager.start();\n this._readyResolve();\n });\n });\n\n if (this.storage) {\n this.storage.initialize().then(() => {\n logger.info('Storage adapter initialized');\n }).catch(err => {\n logger.error({ err }, 'Failed to initialize storage');\n });\n }\n\n this.startGarbageCollection();\n this.startHeartbeatCheck();\n }\n\n /** Wait for server to be fully ready (ports assigned) */\n public ready(): Promise<void> {\n return this._readyPromise;\n }\n\n /** Get the actual port the server is listening on */\n public get port(): number {\n return this._actualPort;\n }\n\n /** Get the actual cluster port */\n public get clusterPort(): number {\n return this._actualClusterPort;\n }\n\n public async shutdown() {\n logger.info('Shutting down Server Coordinator...');\n\n // 1. Stop accepting new connections\n this.httpServer.close();\n if (this.metricsServer) {\n this.metricsServer.close();\n }\n this.metricsService.destroy();\n this.wss.close();\n\n // 2. Notify and Close Clients\n logger.info(`Closing ${this.clients.size} client connections...`);\n const shutdownMsg = serialize({ type: 'SHUTDOWN_PENDING', retryAfter: 5000 });\n\n for (const client of this.clients.values()) {\n try {\n if (client.socket.readyState === WebSocket.OPEN) {\n client.socket.send(shutdownMsg);\n client.socket.close(1001, 'Server Shutdown');\n }\n } catch (e) {\n logger.error({ err: e, clientId: client.id }, 'Error closing client connection');\n }\n }\n this.clients.clear();\n\n // 3. Stop Cluster\n if (this.cluster) {\n this.cluster.stop();\n }\n\n // 4. Close Storage\n if (this.storage) {\n logger.info('Closing storage connection...');\n try {\n await this.storage.close();\n logger.info('Storage closed successfully.');\n } catch (err) {\n logger.error({ err }, 'Error closing storage');\n }\n }\n\n // 5. Cleanup\n if (this.gcInterval) {\n clearInterval(this.gcInterval);\n this.gcInterval = undefined;\n }\n\n if (this.heartbeatCheckInterval) {\n clearInterval(this.heartbeatCheckInterval);\n this.heartbeatCheckInterval = undefined;\n }\n\n // Stop LockManager\n if (this.lockManager) {\n this.lockManager.stop();\n }\n\n // Stop SystemManager\n if (this.systemManager) {\n this.systemManager.stop();\n }\n\n logger.info('Server Coordinator shutdown complete.');\n }\n\n private async handleConnection(ws: WebSocket) {\n // Client ID is temporary until auth\n const clientId = crypto.randomUUID();\n logger.info({ clientId }, 'Client connected (pending auth)');\n\n const connection: ClientConnection = {\n id: clientId,\n socket: ws,\n isAuthenticated: false,\n subscriptions: new Set(),\n lastActiveHlc: this.hlc.now(), // Initialize with current time\n lastPingReceived: Date.now(), // Initialize heartbeat tracking\n };\n this.clients.set(clientId, connection);\n this.metricsService.setConnectedClients(this.clients.size);\n\n // Run onConnection interceptors\n try {\n const context: ConnectionContext = {\n clientId: connection.id,\n socket: connection.socket,\n isAuthenticated: connection.isAuthenticated,\n principal: connection.principal\n };\n for (const interceptor of this.interceptors) {\n if (interceptor.onConnection) {\n await interceptor.onConnection(context);\n }\n }\n } catch (err) {\n logger.error({ clientId, err }, 'Interceptor rejected connection');\n ws.close(4000, 'Connection Rejected');\n this.clients.delete(clientId);\n return;\n }\n\n ws.on('message', (message) => {\n try {\n let data: any;\n let buf: Uint8Array;\n\n if (Buffer.isBuffer(message)) {\n buf = message;\n } else if (message instanceof ArrayBuffer) {\n buf = new Uint8Array(message);\n } else if (Array.isArray(message)) {\n buf = Buffer.concat(message);\n } else {\n // Fallback or unexpected type\n buf = Buffer.from(message as any);\n }\n\n try {\n data = deserialize(buf);\n } catch (e) {\n // If msgpack fails, try JSON (legacy support)\n try {\n // Use Buffer.toString() or TextDecoder\n const text = Buffer.isBuffer(buf) ? buf.toString() : new TextDecoder().decode(buf);\n data = JSON.parse(text);\n } catch (jsonErr) {\n // Original error likely relevant\n throw e;\n }\n }\n\n this.handleMessage(connection, data);\n } catch (err) {\n logger.error({ err }, 'Invalid message format');\n ws.close(1002, 'Protocol Error');\n }\n });\n\n ws.on('close', () => {\n logger.info({ clientId }, 'Client disconnected');\n\n // Run onDisconnect interceptors\n const context: ConnectionContext = {\n clientId: connection.id,\n socket: connection.socket,\n isAuthenticated: connection.isAuthenticated,\n principal: connection.principal\n };\n for (const interceptor of this.interceptors) {\n if (interceptor.onDisconnect) {\n interceptor.onDisconnect(context).catch(err => {\n logger.error({ clientId, err }, 'Error in onDisconnect interceptor');\n });\n }\n }\n\n // Cleanup subscriptions\n for (const subId of connection.subscriptions) {\n this.queryRegistry.unregister(subId);\n }\n\n // Cleanup Locks (Local)\n this.lockManager.handleClientDisconnect(clientId);\n\n // Cleanup Topics (Local)\n this.topicManager.unsubscribeAll(clientId);\n\n // Notify Cluster to Cleanup Locks (Remote)\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_CLIENT_DISCONNECTED', {\n originNodeId: this.cluster.config.nodeId,\n clientId\n });\n }\n }\n\n this.clients.delete(clientId);\n this.metricsService.setConnectedClients(this.clients.size);\n });\n\n // Send Auth Challenge immediately\n ws.send(serialize({ type: 'AUTH_REQUIRED' }));\n }\n\n private async handleMessage(client: ClientConnection, rawMessage: any) {\n // Validation with Zod\n const parseResult = MessageSchema.safeParse(rawMessage);\n if (!parseResult.success) {\n logger.error({ clientId: client.id, error: parseResult.error }, 'Invalid message format from client');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: 'Invalid message format', details: (parseResult.error as any).errors }\n }));\n return;\n }\n const message = parseResult.data;\n\n // Handle PING immediately (even before auth check for authenticated clients)\n if (message.type === 'PING') {\n this.handlePing(client, message.timestamp);\n return;\n }\n\n // Update client's last active HLC\n // Try to extract from payload if present, otherwise assume near current time but logically before next op\n this.updateClientHlc(client, message);\n\n // Handshake / Auth handling\n if (!client.isAuthenticated) {\n if (message.type === 'AUTH') {\n const token = message.token;\n try {\n // Verify JWT - support both HS256 (symmetric) and RS256 (asymmetric/Clerk)\n const isRSAKey = this.jwtSecret.includes('-----BEGIN');\n const verifyOptions: jwt.VerifyOptions = isRSAKey\n ? { algorithms: ['RS256'] }\n : { algorithms: ['HS256'] };\n const decoded = jwt.verify(token, this.jwtSecret, verifyOptions) as any;\n // Ensure roles exist\n if (!decoded.roles) {\n decoded.roles = ['USER']; // Default role\n }\n // Ensure userId exists (map from sub if needed)\n if (!decoded.userId && decoded.sub) {\n decoded.userId = decoded.sub;\n }\n\n client.principal = decoded;\n client.isAuthenticated = true;\n logger.info({ clientId: client.id, user: client.principal!.userId || 'anon' }, 'Client authenticated');\n\n client.socket.send(serialize({ type: 'AUTH_ACK' }));\n return; // Stop processing this message\n } catch (e) {\n logger.error({ clientId: client.id, err: e }, 'Auth failed');\n client.socket.send(serialize({ type: 'AUTH_FAIL', error: 'Invalid token' }));\n client.socket.close(4001, 'Unauthorized');\n }\n } else {\n // Reject any other message before auth\n client.socket.close(4001, 'Auth required');\n }\n return;\n }\n\n // Standard Protocol Handling (Authenticated)\n switch (message.type) {\n case 'QUERY_SUB': {\n const { queryId, mapName, query } = message.payload;\n\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, mapName, 'READ')) {\n logger.warn({ clientId: client.id, mapName }, 'Access Denied: QUERY_SUB');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${mapName}` }\n }));\n return;\n }\n\n logger.info({ clientId: client.id, mapName, query }, 'Client subscribed');\n this.metricsService.incOp('SUBSCRIBE', mapName);\n\n // Identify all relevant nodes\n const allMembers = this.cluster.getMembers();\n const remoteMembers = allMembers.filter(id => !this.cluster.isLocal(id));\n\n const requestId = crypto.randomUUID();\n\n const pending: PendingClusterQuery = {\n requestId,\n client,\n queryId,\n mapName,\n query,\n results: [], // Will populate with local results first\n expectedNodes: new Set(remoteMembers),\n respondedNodes: new Set(),\n timer: setTimeout(() => this.finalizeClusterQuery(requestId, true), 5000) // 5s timeout\n };\n\n this.pendingClusterQueries.set(requestId, pending);\n\n // Execute Locally (async - wait for map to load from storage)\n // [FIX] Using await ensures handleMessage completes only after query execution\n // This is important for:\n // 1. Tests that need to verify results immediately after handleMessage\n // 2. Ensuring storage is loaded before returning results\n try {\n const localResults = await this.executeLocalQuery(mapName, query);\n pending.results.push(...localResults);\n\n // Scatter: Send to other nodes\n if (remoteMembers.length > 0) {\n for (const nodeId of remoteMembers) {\n this.cluster.send(nodeId, 'CLUSTER_QUERY_EXEC', {\n requestId,\n mapName,\n query\n });\n }\n } else {\n // Single node cluster: finalize immediately\n this.finalizeClusterQuery(requestId);\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to execute local query');\n // Finalize with empty results on error\n this.finalizeClusterQuery(requestId);\n }\n break;\n }\n\n case 'QUERY_UNSUB': {\n const { queryId: unsubId } = message.payload;\n this.queryRegistry.unregister(unsubId);\n client.subscriptions.delete(unsubId);\n break;\n }\n\n case 'CLIENT_OP': {\n const op = message.payload;\n\n // Determine action type\n // LWW: op.record.value === null -> REMOVE\n // OR: OR_REMOVE or OR_ADD -> PUT (effectively)\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\n this.metricsService.incOp(isRemove ? 'DELETE' : 'PUT', op.mapName);\n\n // Check Permission\n if (!this.securityManager.checkPermission(client.principal!, op.mapName, action)) {\n logger.warn({ clientId: client.id, action, mapName: op.mapName }, 'Access Denied: Client OP');\n client.socket.send(serialize({\n type: 'OP_REJECTED',\n payload: { opId: op.id, reason: 'Access Denied' }\n }));\n return;\n }\n\n logger.info({ clientId: client.id, opType: op.opType, key: op.key, mapName: op.mapName }, 'Received op');\n\n if (this.partitionService.isLocalOwner(op.key)) {\n this.processLocalOp(op, false, client.id).catch(err => {\n logger.error({ clientId: client.id, err }, 'Op failed');\n client.socket.send(serialize({\n type: 'OP_REJECTED',\n payload: { opId: op.id, reason: err.message || 'Internal Error' }\n }));\n });\n } else {\n const owner = this.partitionService.getOwner(op.key);\n logger.info({ key: op.key, owner }, 'Forwarding op');\n this.cluster.sendToNode(owner, op);\n }\n break;\n }\n\n case 'OP_BATCH': {\n const ops = message.payload.ops;\n logger.info({ clientId: client.id, count: ops.length }, 'Received batch');\n\n let lastProcessedId: string | null = null;\n let rejectedCount = 0;\n\n for (const op of ops) {\n // OpLogEntry has { mapName, key, record, ... }\n\n // Check Permission for each op\n const isRemove = op.opType === 'REMOVE' || (op.record && op.record.value === null);\n const action: PermissionType = isRemove ? 'REMOVE' : 'PUT';\n if (!this.securityManager.checkPermission(client.principal!, op.mapName, action)) {\n rejectedCount++;\n logger.warn({ clientId: client.id, action, mapName: op.mapName }, 'Access Denied (Batch)');\n continue;\n }\n\n // processLocalOp expects { mapName, key, record, orRecord, orTag, opType }\n\n if (this.partitionService.isLocalOwner(op.key)) {\n try {\n await this.processLocalOp({\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType\n }, false, client.id);\n\n if (op.id) {\n lastProcessedId = op.id;\n }\n } catch (err) {\n rejectedCount++;\n logger.warn({ clientId: client.id, mapName: op.mapName, err }, 'Op rejected in batch');\n // We do NOT update lastProcessedId for failed op\n }\n } else {\n const owner = this.partitionService.getOwner(op.key);\n this.cluster.sendToNode(owner, {\n type: 'CLIENT_OP',\n payload: {\n mapName: op.mapName,\n key: op.key,\n record: op.record,\n orRecord: op.orRecord,\n orTag: op.orTag,\n opType: op.opType\n }\n });\n // For forwarded ops, we optimistically assume success for batch ACK purposes?\n // Or we should only ACK what we processed locally?\n // Batch ACK usually implies \"accepted\". Forwarding = accepted for processing.\n if (op.id) {\n lastProcessedId = op.id;\n }\n }\n }\n\n if (lastProcessedId !== null) {\n client.socket.send(serialize({\n type: 'OP_ACK',\n payload: { lastId: lastProcessedId }\n }));\n }\n\n if (rejectedCount > 0) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Partial batch failure: ${rejectedCount} ops denied` }\n }));\n }\n break;\n }\n\n case 'SYNC_INIT': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.mapName, 'READ')) {\n logger.warn({ clientId: client.id, mapName: message.mapName }, 'Access Denied: SYNC_INIT');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }));\n return;\n }\n\n const lastSync = message.lastSyncTimestamp || 0;\n const now = Date.now();\n if (lastSync > 0 && (now - lastSync) > GC_AGE_MS) {\n logger.warn({ clientId: client.id, lastSync, age: now - lastSync }, 'Client too old, sending SYNC_RESET_REQUIRED');\n client.socket.send(serialize({\n type: 'SYNC_RESET_REQUIRED',\n payload: { mapName: message.mapName }\n }));\n return;\n }\n\n logger.info({ clientId: client.id, mapName: message.mapName }, 'Client requested sync');\n this.metricsService.incOp('GET', message.mapName);\n\n // [FIX] Wait for map to be fully loaded from storage before sending rootHash\n // This prevents sending rootHash=0 for maps that are still loading from PostgreSQL\n try {\n const mapForSync = await this.getMapAsync(message.mapName);\n if (mapForSync instanceof LWWMap) {\n // Use the incremental Merkle Tree from LWWMap\n const tree = mapForSync.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n client.socket.send(serialize({\n type: 'SYNC_RESP_ROOT',\n payload: {\n mapName: message.mapName,\n rootHash,\n timestamp: this.hlc.now()\n }\n }));\n } else {\n // ORMap sync not implemented via Merkle Tree yet\n logger.warn({ mapName: message.mapName }, 'SYNC_INIT requested for ORMap - Not Implemented');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 501, message: `Merkle Sync not supported for ORMap ${message.mapName}` }\n }));\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for SYNC_INIT');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }));\n }\n break;\n }\n\n case 'MERKLE_REQ_BUCKET': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\n return;\n }\n\n const { mapName, path } = message.payload;\n\n // [FIX] Wait for map to be fully loaded before accessing Merkle tree\n try {\n const mapForBucket = await this.getMapAsync(mapName);\n if (mapForBucket instanceof LWWMap) {\n const treeForBucket = mapForBucket.getMerkleTree();\n const buckets = treeForBucket.getBuckets(path);\n const node = treeForBucket.getNode(path);\n if (node && node.entries && node.entries.size > 0) {\n const diffRecords = [];\n for (const key of node.entries.keys()) {\n diffRecords.push({ key, record: mapForBucket.getRecord(key) });\n }\n client.socket.send(serialize({\n type: 'SYNC_RESP_LEAF',\n payload: { mapName, path, records: diffRecords }\n }));\n } else {\n client.socket.send(serialize({\n type: 'SYNC_RESP_BUCKETS',\n payload: { mapName, path, buckets }\n }));\n }\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to load map for MERKLE_REQ_BUCKET');\n }\n break;\n }\n\n case 'LOCK_REQUEST': {\n const { requestId, name, ttl } = message.payload;\n\n // 1. Access Control\n // Define a convention: lock names are resources.\n // Check if user has 'WRITE' permission on \"locks\" map or specific lock name.\n // Since locks are ephemeral, we might treat them as a special resource \"sys:locks\".\n // Or just check against the lock name itself.\n // Let's use `sys:lock:${name}` pattern or just `${name}`.\n // If we use just name, it might conflict with map names if policies are strict.\n // Assuming for now that lock name represents the resource being protected.\n if (!this.securityManager.checkPermission(client.principal!, name, 'PUT')) {\n client.socket.send(serialize({\n // We don't have LOCK_DENIED type in schema yet?\n // Using LOCK_RELEASED with success=false as a hack or ERROR.\n // Ideally ERROR.\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for lock ${name}` }\n }));\n return;\n }\n\n if (this.partitionService.isLocalOwner(name)) {\n const result = this.lockManager.acquire(name, client.id, requestId, ttl || 10000);\n if (result.granted) {\n client.socket.send(serialize({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken: result.fencingToken }\n }));\n }\n // If not granted, it is queued. Response sent later via event.\n } else {\n const owner = this.partitionService.getOwner(name);\n // 2. Cluster Reliability Check\n if (!this.cluster.getMembers().includes(owner)) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 503, message: `Lock owner ${owner} is unavailable` }\n }));\n return;\n }\n\n this.cluster.send(owner, 'CLUSTER_LOCK_REQ', {\n originNodeId: this.cluster.config.nodeId,\n clientId: client.id,\n requestId,\n name,\n ttl\n });\n }\n break;\n }\n\n case 'LOCK_RELEASE': {\n const { requestId, name, fencingToken } = message.payload;\n\n if (this.partitionService.isLocalOwner(name)) {\n const success = this.lockManager.release(name, client.id, fencingToken);\n client.socket.send(serialize({\n type: 'LOCK_RELEASED',\n payload: { requestId, name, success }\n }));\n } else {\n const owner = this.partitionService.getOwner(name);\n this.cluster.send(owner, 'CLUSTER_LOCK_RELEASE', {\n originNodeId: this.cluster.config.nodeId,\n clientId: client.id,\n requestId,\n name,\n fencingToken\n });\n }\n break;\n }\n\n case 'TOPIC_SUB': {\n const { topic } = message.payload;\n\n // C1: Access Control\n // We treat topics as resources. \n // Policy check: action 'READ' on resource `topic:${topic}`\n if (!this.securityManager.checkPermission(client.principal!, `topic:${topic}`, 'READ')) {\n logger.warn({ clientId: client.id, topic }, 'Access Denied: TOPIC_SUB');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }));\n return;\n }\n\n try {\n this.topicManager.subscribe(client.id, topic);\n } catch (e: any) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }));\n }\n break;\n }\n\n case 'TOPIC_UNSUB': {\n const { topic } = message.payload;\n this.topicManager.unsubscribe(client.id, topic);\n break;\n }\n\n case 'TOPIC_PUB': {\n const { topic, data } = message.payload;\n\n // C1: Access Control\n // Policy check: action 'PUT' (publish) on resource `topic:${topic}`\n if (!this.securityManager.checkPermission(client.principal!, `topic:${topic}`, 'PUT')) {\n logger.warn({ clientId: client.id, topic }, 'Access Denied: TOPIC_PUB');\n // No error sent back? Fire and forget usually implies silent drop or async error.\n // But for security violations, an error is useful during dev.\n // Spec says fire-and-forget delivery, but security rejection should ideally notify.\n // Let's send error.\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for topic ${topic}` }\n }));\n return;\n }\n\n try {\n this.topicManager.publish(topic, data, client.id);\n } catch (e: any) {\n // Invalid topic name etc\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: e.message }\n }));\n }\n break;\n }\n\n // ============ ORMap Sync Message Handlers ============\n\n case 'ORMAP_SYNC_INIT': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.mapName, 'READ')) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.mapName}` }\n }));\n return;\n }\n\n const lastSync = message.lastSyncTimestamp || 0;\n const now = Date.now();\n if (lastSync > 0 && (now - lastSync) > GC_AGE_MS) {\n logger.warn({ clientId: client.id, lastSync, age: now - lastSync }, 'ORMap client too old, sending SYNC_RESET_REQUIRED');\n client.socket.send(serialize({\n type: 'SYNC_RESET_REQUIRED',\n payload: { mapName: message.mapName }\n }));\n return;\n }\n\n logger.info({ clientId: client.id, mapName: message.mapName }, 'Client requested ORMap sync');\n this.metricsService.incOp('GET', message.mapName);\n\n try {\n const mapForSync = await this.getMapAsync(message.mapName, 'OR');\n if (mapForSync instanceof ORMap) {\n const tree = mapForSync.getMerkleTree();\n const rootHash = tree.getRootHash();\n\n client.socket.send(serialize({\n type: 'ORMAP_SYNC_RESP_ROOT',\n payload: {\n mapName: message.mapName,\n rootHash,\n timestamp: this.hlc.now()\n }\n }));\n } else {\n // It's actually an LWWMap, client should use SYNC_INIT\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 400, message: `Map ${message.mapName} is not an ORMap` }\n }));\n }\n } catch (err) {\n logger.error({ err, mapName: message.mapName }, 'Failed to load map for ORMAP_SYNC_INIT');\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 500, message: `Failed to load map ${message.mapName}` }\n }));\n }\n break;\n }\n\n case 'ORMAP_MERKLE_REQ_BUCKET': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\n return;\n }\n\n const { mapName, path } = message.payload;\n\n try {\n const mapForBucket = await this.getMapAsync(mapName, 'OR');\n if (mapForBucket instanceof ORMap) {\n const tree = mapForBucket.getMerkleTree();\n const buckets = tree.getBuckets(path);\n const isLeaf = tree.isLeaf(path);\n\n if (isLeaf) {\n // This is a leaf node - send actual records\n const keys = tree.getKeysInBucket(path);\n const entries: Array<{ key: string; records: ORMapRecord<any>[]; tombstones: string[] }> = [];\n\n for (const key of keys) {\n const recordsMap = mapForBucket.getRecordsMap(key);\n if (recordsMap && recordsMap.size > 0) {\n entries.push({\n key,\n records: Array.from(recordsMap.values()),\n tombstones: mapForBucket.getTombstones()\n });\n }\n }\n\n client.socket.send(serialize({\n type: 'ORMAP_SYNC_RESP_LEAF',\n payload: { mapName, path, entries }\n }));\n } else {\n // Not a leaf - send bucket hashes\n client.socket.send(serialize({\n type: 'ORMAP_SYNC_RESP_BUCKETS',\n payload: { mapName, path, buckets }\n }));\n }\n }\n } catch (err) {\n logger.error({ err, mapName }, 'Failed to load map for ORMAP_MERKLE_REQ_BUCKET');\n }\n break;\n }\n\n case 'ORMAP_DIFF_REQUEST': {\n // Check READ permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'READ')) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\n return;\n }\n\n const { mapName: diffMapName, keys } = message.payload;\n\n try {\n const mapForDiff = await this.getMapAsync(diffMapName, 'OR');\n if (mapForDiff instanceof ORMap) {\n const entries: Array<{ key: string; records: ORMapRecord<any>[]; tombstones: string[] }> = [];\n const allTombstones = mapForDiff.getTombstones();\n\n for (const key of keys) {\n const recordsMap = mapForDiff.getRecordsMap(key);\n entries.push({\n key,\n records: recordsMap ? Array.from(recordsMap.values()) : [],\n tombstones: allTombstones\n });\n }\n\n client.socket.send(serialize({\n type: 'ORMAP_DIFF_RESPONSE',\n payload: { mapName: diffMapName, entries }\n }));\n }\n } catch (err) {\n logger.error({ err, mapName: diffMapName }, 'Failed to load map for ORMAP_DIFF_REQUEST');\n }\n break;\n }\n\n case 'ORMAP_PUSH_DIFF': {\n // Check WRITE permission\n if (!this.securityManager.checkPermission(client.principal!, message.payload.mapName, 'PUT')) {\n client.socket.send(serialize({\n type: 'ERROR',\n payload: { code: 403, message: `Access Denied for map ${message.payload.mapName}` }\n }));\n return;\n }\n\n const { mapName: pushMapName, entries: pushEntries } = message.payload;\n\n try {\n const mapForPush = await this.getMapAsync(pushMapName, 'OR');\n if (mapForPush instanceof ORMap) {\n let totalAdded = 0;\n let totalUpdated = 0;\n\n for (const entry of pushEntries) {\n const { key, records, tombstones } = entry;\n const result = mapForPush.mergeKey(key, records, tombstones);\n totalAdded += result.added;\n totalUpdated += result.updated;\n }\n\n if (totalAdded > 0 || totalUpdated > 0) {\n logger.info({ mapName: pushMapName, added: totalAdded, updated: totalUpdated, clientId: client.id }, 'Merged ORMap diff from client');\n\n // Broadcast changes to other clients\n for (const entry of pushEntries) {\n for (const record of entry.records) {\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: {\n mapName: pushMapName,\n eventType: 'OR_ADD',\n key: entry.key,\n orRecord: record\n }\n }, client.id);\n }\n }\n\n // Persist to storage\n if (this.storage) {\n for (const entry of pushEntries) {\n const recordsMap = mapForPush.getRecordsMap(entry.key);\n if (recordsMap && recordsMap.size > 0) {\n await this.storage.store(pushMapName, entry.key, {\n type: 'OR',\n records: Array.from(recordsMap.values())\n });\n }\n }\n }\n }\n }\n } catch (err) {\n logger.error({ err, mapName: pushMapName }, 'Failed to process ORMAP_PUSH_DIFF');\n }\n break;\n }\n\n default:\n logger.warn({ type: message.type }, 'Unknown message type');\n }\n }\n\n private updateClientHlc(client: ClientConnection, message: any) {\n // Try to extract timestamp from message if available\n // This is heuristic based on typical message structure\n let ts: Timestamp | undefined;\n\n if (message.type === 'CLIENT_OP') {\n const op = message.payload;\n if (op.record && op.record.timestamp) {\n ts = op.record.timestamp;\n } else if (op.orRecord && op.orRecord.timestamp) {\n // orRecord usually has entries which have timestamps, or value itself is decorated?\n // Depends on implementation.\n } else if (op.orTag) {\n try {\n ts = HLC.parse(op.orTag);\n } catch (e) { }\n }\n }\n\n if (ts) {\n // Client sent an explicit timestamp, update their HLC\n this.hlc.update(ts); // Also update server clock\n // Client HLC is at least this\n client.lastActiveHlc = ts;\n } else {\n // Just bump to current server time if no explicit TS\n // This assumes client is \"alive\" at this moment.\n client.lastActiveHlc = this.hlc.now();\n }\n }\n\n private broadcast(message: any, excludeClientId?: string) {\n const isServerEvent = message.type === 'SERVER_EVENT';\n\n if (isServerEvent) {\n for (const [id, client] of this.clients) {\n if (id !== excludeClientId && client.socket.readyState === 1 && client.isAuthenticated && client.principal) {\n const payload = message.payload;\n const mapName = payload.mapName;\n\n // Shallow clone payload\n const newPayload = { ...payload };\n\n if (newPayload.record) { // LWW\n const newVal = this.securityManager.filterObject(newPayload.record.value, client.principal, mapName);\n newPayload.record = { ...newPayload.record, value: newVal };\n }\n\n if (newPayload.orRecord) { // OR_ADD\n const newVal = this.securityManager.filterObject(newPayload.orRecord.value, client.principal, mapName);\n newPayload.orRecord = { ...newPayload.orRecord, value: newVal };\n }\n\n client.socket.send(serialize({ ...message, payload: newPayload }));\n }\n }\n } else {\n const msgData = serialize(message);\n for (const [id, client] of this.clients) {\n if (id !== excludeClientId && client.socket.readyState === 1) { // 1 = OPEN\n client.socket.send(msgData);\n }\n }\n }\n }\n\n private setupClusterListeners() {\n this.cluster.on('memberJoined', () => {\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n });\n this.cluster.on('memberLeft', () => {\n this.metricsService.setClusterMembers(this.cluster.getMembers().length);\n });\n\n this.cluster.on('message', (msg) => {\n switch (msg.type) {\n case 'OP_FORWARD':\n logger.info({ senderId: msg.senderId }, 'Received forwarded op');\n if (this.partitionService.isLocalOwner(msg.payload.key)) {\n this.processLocalOp(msg.payload, true, msg.senderId).catch(err => {\n logger.error({ err, senderId: msg.senderId }, 'Forwarded op failed');\n });\n } else {\n logger.warn({ key: msg.payload.key }, 'Received OP_FORWARD but not owner. Dropping.');\n }\n break;\n case 'CLUSTER_EVENT':\n this.handleClusterEvent(msg.payload);\n break;\n\n case 'CLUSTER_QUERY_EXEC': {\n const { requestId, mapName, query } = msg.payload;\n this.executeLocalQuery(mapName, query).then(results => {\n this.cluster.send(msg.senderId, 'CLUSTER_QUERY_RESP', {\n requestId,\n results\n });\n }).catch(err => {\n logger.error({ err, mapName }, 'Failed to execute cluster query');\n this.cluster.send(msg.senderId, 'CLUSTER_QUERY_RESP', {\n requestId,\n results: []\n });\n });\n break;\n }\n\n case 'CLUSTER_QUERY_RESP': {\n const { requestId: reqId, results: remoteResults } = msg.payload;\n const pendingQuery = this.pendingClusterQueries.get(reqId);\n if (pendingQuery) {\n pendingQuery.results.push(...remoteResults);\n pendingQuery.respondedNodes.add(msg.senderId);\n\n if (pendingQuery.respondedNodes.size === pendingQuery.expectedNodes.size) {\n this.finalizeClusterQuery(reqId);\n }\n }\n break;\n }\n\n case 'CLUSTER_GC_REPORT': {\n this.handleGcReport(msg.senderId, msg.payload.minHlc);\n break;\n }\n\n case 'CLUSTER_GC_COMMIT': {\n this.performGarbageCollection(msg.payload.safeTimestamp);\n break;\n }\n\n case 'CLUSTER_LOCK_REQ': {\n const { originNodeId, clientId, requestId, name, ttl } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n const result = this.lockManager.acquire(name, compositeId, requestId, ttl || 10000);\n if (result.granted) {\n this.cluster.send(originNodeId, 'CLUSTER_LOCK_GRANTED', {\n clientId,\n requestId,\n name,\n fencingToken: result.fencingToken\n });\n }\n break;\n }\n\n case 'CLUSTER_LOCK_RELEASE': {\n const { originNodeId, clientId, requestId, name, fencingToken } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n const success = this.lockManager.release(name, compositeId, fencingToken);\n this.cluster.send(originNodeId, 'CLUSTER_LOCK_RELEASED', {\n clientId, requestId, name, success\n });\n break;\n }\n\n case 'CLUSTER_LOCK_RELEASED': {\n const { clientId, requestId, name, success } = msg.payload;\n const client = this.clients.get(clientId);\n if (client) {\n client.socket.send(serialize({\n type: 'LOCK_RELEASED',\n payload: { requestId, name, success }\n }));\n }\n break;\n }\n\n case 'CLUSTER_LOCK_GRANTED': {\n const { clientId, requestId, name, fencingToken } = msg.payload;\n const client = this.clients.get(clientId);\n if (client) {\n client.socket.send(serialize({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken }\n }));\n }\n break;\n }\n\n case 'CLUSTER_CLIENT_DISCONNECTED': {\n const { clientId, originNodeId } = msg.payload;\n const compositeId = `${originNodeId}:${clientId}`;\n this.lockManager.handleClientDisconnect(compositeId);\n break;\n }\n\n case 'CLUSTER_TOPIC_PUB': {\n const { topic, data, originalSenderId } = msg.payload;\n this.topicManager.publish(topic, data, originalSenderId, true);\n break;\n }\n }\n });\n }\n\n private async executeLocalQuery(mapName: string, query: Query) {\n // Wait for map to be fully loaded from storage before querying\n const map = await this.getMapAsync(mapName);\n const records = new Map<string, any>();\n\n if (map instanceof LWWMap) {\n for (const key of map.allKeys()) {\n const rec = map.getRecord(key);\n if (rec && rec.value !== null) {\n records.set(key, rec);\n }\n }\n } else if (map instanceof ORMap) {\n // For ORMap, we flatten values. A key matches if ANY of its values match?\n // Or we expose the array of values?\n // For now, we expose { key, value: [v1, v2, ...] }\n // Accessing properties on array might fail depending on query.\n // Assuming user knows what they are querying.\n // Since ORMap doesn't have allKeys, we iterate internal structure?\n // ORMap doesn't expose keys iterator publicly in the class I read?\n // Wait, checking ORMap.ts...\n // It doesn't export keys()! It exports items: Map.\n // But items is private.\n // I need to add keys() to ORMap or use 'any' cast.\n // I will cast to any for now.\n const items = (map as any).items as Map<string, any>;\n for (const key of items.keys()) {\n const values = map.get(key);\n if (values.length > 0) {\n // We wrap in object matching LWWRecord structure roughly?\n // { value: values, timestamp: ... }\n // But timestamp differs per record.\n records.set(key, { value: values });\n }\n }\n }\n\n // Fix: Do not apply offset/limit locally for cluster queries.\n // They will be applied in finalizeClusterQuery after aggregation.\n const localQuery = { ...query };\n delete localQuery.offset;\n delete localQuery.limit;\n\n return executeQuery(records, localQuery);\n }\n\n private finalizeClusterQuery(requestId: string, timeout = false) {\n const pending = this.pendingClusterQueries.get(requestId);\n if (!pending) return;\n\n if (timeout) {\n logger.warn({ requestId, responded: pending.respondedNodes.size, expected: pending.expectedNodes.size }, 'Query timed out. Returning partial results.');\n }\n\n clearTimeout(pending.timer);\n this.pendingClusterQueries.delete(requestId);\n\n const { client, queryId, mapName, query, results } = pending;\n\n // Deduplicate results (if backups responded or multiple nodes have same key)\n const uniqueResults = new Map<string, any>();\n for (const res of results) {\n uniqueResults.set(res.key, res);\n }\n const finalResults = Array.from(uniqueResults.values());\n\n // Re-Apply Sort (Global)\n if (query.sort) {\n finalResults.sort((a, b) => {\n for (const [field, direction] of Object.entries(query.sort!)) {\n // Handle ORMap array values vs LWW single values?\n // Assuming LWW for sort logic or array comparison.\n const valA = a.value[field];\n const valB = b.value[field];\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 const slicedResults = (query.offset || query.limit)\n ? finalResults.slice(query.offset || 0, (query.offset || 0) + (query.limit || finalResults.length))\n : finalResults;\n\n // Register Subscription\n const resultKeys = new Set(slicedResults.map(r => r.key));\n const sub: Subscription = {\n id: queryId,\n clientId: client.id,\n mapName,\n query,\n socket: client.socket,\n previousResultKeys: resultKeys,\n interestedFields: 'ALL'\n };\n\n this.queryRegistry.register(sub);\n client.subscriptions.add(queryId);\n\n // Apply Field Level Security\n const filteredResults = slicedResults.map(res => {\n const filteredValue = this.securityManager.filterObject(res.value, client.principal!, mapName);\n return { ...res, value: filteredValue };\n });\n\n client.socket.send(serialize({\n type: 'QUERY_RESP',\n payload: { queryId, results: filteredResults }\n }));\n }\n\n private handleLockGranted({ clientId, requestId, name, fencingToken }: { clientId: string, requestId: string, name: string, fencingToken: number }) {\n // Check if local client\n const client = this.clients.get(clientId);\n if (client) {\n client.socket.send(serialize({\n type: 'LOCK_GRANTED',\n payload: { requestId, name, fencingToken }\n }));\n return;\n }\n\n // Check if remote client (composite ID: \"nodeId:realClientId\")\n const parts = clientId.split(':');\n if (parts.length === 2) {\n const [nodeId, realClientId] = parts;\n // Verify nodeId is not self (loopback check, though split should handle it)\n if (nodeId !== this.cluster.config.nodeId) {\n this.cluster.send(nodeId, 'CLUSTER_LOCK_GRANTED', {\n clientId: realClientId,\n requestId,\n name,\n fencingToken\n });\n return;\n }\n }\n\n logger.warn({ clientId, name }, 'Lock granted to unknown client');\n }\n\n private async processLocalOp(op: any, fromCluster: boolean, originalSenderId?: string) {\n // 0. Prepare Context\n let context: OpContext = {\n clientId: originalSenderId || 'unknown',\n isAuthenticated: false, // We might need to fetch this if local\n fromCluster,\n originalSenderId\n };\n\n if (!fromCluster && originalSenderId) {\n const client = this.clients.get(originalSenderId);\n if (client) {\n context = {\n clientId: client.id,\n socket: client.socket,\n isAuthenticated: client.isAuthenticated,\n principal: client.principal,\n fromCluster,\n originalSenderId\n };\n }\n }\n\n // 1. Interceptors: onBeforeOp\n let currentOp: ServerOp | null = op;\n try {\n for (const interceptor of this.interceptors) {\n if (interceptor.onBeforeOp) {\n if (currentOp) {\n currentOp = await interceptor.onBeforeOp(currentOp, context);\n if (!currentOp) {\n logger.debug({ interceptor: interceptor.name, opId: op.id }, 'Interceptor silently dropped op');\n return; // Silent drop\n }\n }\n }\n }\n } catch (err: any) {\n // Find which interceptor failed? We don't know easily unless we tracked loop index.\n // But logging err is good enough.\n logger.warn({ err, opId: op.id }, 'Interceptor rejected op');\n throw err; // Re-throw to caller\n }\n\n if (!currentOp) return; // Should be caught above but safe check\n\n op = currentOp;\n\n // Apply to server state (Owner or Forwarded)\n // Determine type hint from op\n const typeHint = (op.opType === 'OR_ADD' || op.opType === 'OR_REMOVE') ? 'OR' : 'LWW';\n const map = this.getMap(op.mapName, typeHint);\n\n // Check compatibility\n if (typeHint === 'OR' && map instanceof LWWMap) {\n logger.error({ mapName: op.mapName }, 'Map type mismatch: LWWMap but received OR op');\n throw new Error('Map type mismatch: LWWMap but received OR op');\n }\n if (typeHint === 'LWW' && map instanceof ORMap) {\n logger.error({ mapName: op.mapName }, 'Map type mismatch: ORMap but received LWW op');\n throw new Error('Map type mismatch: ORMap but received LWW op');\n }\n\n let oldRecord: any;\n let recordToStore: StorageValue<any> | undefined;\n let tombstonesToStore: StorageValue<any> | undefined;\n\n const eventPayload: any = {\n mapName: op.mapName,\n key: op.key,\n // Common fields\n };\n\n if (map instanceof LWWMap) {\n oldRecord = map.getRecord(op.key);\n map.merge(op.key, op.record);\n recordToStore = op.record;\n eventPayload.eventType = 'UPDATED';\n eventPayload.record = op.record;\n } else if (map instanceof ORMap) {\n // ORMap\n oldRecord = map.getRecords(op.key); // Logic for \"old record\" in ORMap is complex for query.\n\n if (op.opType === 'OR_ADD') {\n map.apply(op.key, op.orRecord);\n eventPayload.eventType = 'OR_ADD';\n eventPayload.orRecord = op.orRecord;\n\n // Prepare storage: full state of key\n recordToStore = {\n type: 'OR',\n records: map.getRecords(op.key)\n };\n } else if (op.opType === 'OR_REMOVE') {\n map.applyTombstone(op.orTag);\n eventPayload.eventType = 'OR_REMOVE';\n eventPayload.orTag = op.orTag;\n\n // OR_REMOVE modifies the key's records implicitly (filters them out)\n // So we should update the key state too?\n // Yes, if we remove a tag, the getRecords(key) result changes.\n // But we don't know which key held the tag easily unless we search or client provided it.\n // Client provided `op.key`.\n recordToStore = {\n type: 'OR',\n records: map.getRecords(op.key)\n };\n\n // Also persist tombstones\n tombstonesToStore = {\n type: 'OR_TOMBSTONES',\n tags: map.getTombstones()\n };\n }\n }\n\n // Live Query Evaluation (Local)\n this.queryRegistry.processChange(op.mapName, map, op.key, op.record || op.orRecord, oldRecord);\n\n const mapSize = (map instanceof ORMap) ? map.totalRecords : map.size;\n this.metricsService.setMapSize(op.mapName, mapSize);\n\n // Persist to storage (Only Owner persists)\n if (this.storage) {\n if (recordToStore) {\n this.storage.store(op.mapName, op.key, recordToStore).catch(err => {\n logger.error({ mapName: op.mapName, key: op.key, err }, 'Failed to persist op');\n });\n }\n if (tombstonesToStore) {\n this.storage.store(op.mapName, '__tombstones__', tombstonesToStore).catch(err => {\n logger.error({ mapName: op.mapName, err }, 'Failed to persist tombstones');\n });\n }\n }\n\n // 1. Broadcast EVENT to other clients (Notification)\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n }, originalSenderId);\n\n // 2. Broadcast EVENT/REPLICATION to Cluster\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n\n // 4. Interceptors: onAfterOp\n for (const interceptor of this.interceptors) {\n if (interceptor.onAfterOp) {\n interceptor.onAfterOp(op, context).catch(err => {\n logger.error({ err }, 'Error in onAfterOp');\n });\n }\n }\n }\n\n private handleClusterEvent(payload: any) {\n // 1. Replication Logic: Am I a Backup?\n const { mapName, key, eventType } = payload;\n const map = this.getMap(mapName, (eventType === 'OR_ADD' || eventType === 'OR_REMOVE') ? 'OR' : 'LWW');\n const oldRecord = (map instanceof LWWMap) ? map.getRecord(key) : null;\n\n // Only store if we are Owner (shouldn't receive event unless forwarded) or Backup\n if (this.partitionService.isRelated(key)) {\n if (map instanceof LWWMap && payload.record) {\n map.merge(key, payload.record);\n } else if (map instanceof ORMap) {\n if (eventType === 'OR_ADD' && payload.orRecord) {\n map.apply(key, payload.orRecord);\n } else if (eventType === 'OR_REMOVE' && payload.orTag) {\n map.applyTombstone(payload.orTag);\n }\n }\n }\n\n // 2. Notify Query Subscriptions\n this.queryRegistry.processChange(mapName, map, key, payload.record || payload.orRecord, oldRecord);\n\n // 3. Broadcast to local clients (Notification)\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: payload,\n timestamp: this.hlc.now()\n });\n }\n\n public getMap(name: string, typeHint: 'LWW' | 'OR' = 'LWW'): LWWMap<string, any> | ORMap<string, any> {\n if (!this.maps.has(name)) {\n let map: LWWMap<string, any> | ORMap<string, any>;\n\n if (typeHint === 'OR') {\n map = new ORMap(this.hlc);\n } else {\n map = new LWWMap(this.hlc);\n }\n\n this.maps.set(name, map);\n\n // Lazy load from storage - track the promise for getMapAsync\n if (this.storage) {\n logger.info({ mapName: name }, 'Loading map from storage...');\n const loadPromise = this.loadMapFromStorage(name, typeHint);\n this.mapLoadingPromises.set(name, loadPromise);\n loadPromise.finally(() => {\n this.mapLoadingPromises.delete(name);\n });\n }\n }\n return this.maps.get(name)!;\n }\n\n /**\n * Returns map after ensuring it's fully loaded from storage.\n * Use this for queries to avoid returning empty results during initial load.\n */\n public async getMapAsync(name: string, typeHint: 'LWW' | 'OR' = 'LWW'): Promise<LWWMap<string, any> | ORMap<string, any>> {\n const mapExisted = this.maps.has(name);\n\n // First ensure map exists (this triggers loading if needed)\n this.getMap(name, typeHint);\n\n // Wait for loading to complete if in progress\n const loadingPromise = this.mapLoadingPromises.get(name);\n\n // [DEBUG] Log state for troubleshooting sync issues\n const map = this.maps.get(name);\n const mapSize = map instanceof LWWMap ? Array.from(map.entries()).length :\n map instanceof ORMap ? map.size : 0;\n logger.info({\n mapName: name,\n mapExisted,\n hasLoadingPromise: !!loadingPromise,\n currentMapSize: mapSize\n }, '[getMapAsync] State check');\n\n if (loadingPromise) {\n logger.info({ mapName: name }, '[getMapAsync] Waiting for loadMapFromStorage...');\n await loadingPromise;\n const newMapSize = map instanceof LWWMap ? Array.from(map.entries()).length :\n map instanceof ORMap ? map.size : 0;\n logger.info({ mapName: name, mapSizeAfterLoad: newMapSize }, '[getMapAsync] Load completed');\n }\n\n return this.maps.get(name)!;\n }\n\n private async loadMapFromStorage(name: string, typeHint: 'LWW' | 'OR'): Promise<void> {\n try {\n const keys = await this.storage!.loadAllKeys(name);\n if (keys.length === 0) return;\n\n // Check for ORMap markers in keys\n const hasTombstones = keys.includes('__tombstones__');\n\n const relatedKeys = keys.filter(k => this.partitionService.isRelated(k));\n if (relatedKeys.length === 0) return;\n\n const records = await this.storage!.loadAll(name, relatedKeys);\n let count = 0;\n\n // Check for Type Mismatch and Replace Map if needed\n let isOR = hasTombstones;\n if (!isOR) {\n // Check first record\n for (const [k, v] of records) {\n if (k !== '__tombstones__' && (v as any).type === 'OR') {\n isOR = true;\n break;\n }\n }\n }\n\n // If we created LWW but it's OR, replace it.\n // If we created OR but it's LWW, replace it? (Less likely if hint was OR, but possible if hint was wrong?)\n const currentMap = this.maps.get(name);\n if (!currentMap) return;\n let targetMap = currentMap;\n\n if (isOR && currentMap instanceof LWWMap) {\n logger.info({ mapName: name }, 'Map auto-detected as ORMap. Switching type.');\n targetMap = new ORMap(this.hlc);\n this.maps.set(name, targetMap);\n } else if (!isOR && currentMap instanceof ORMap && typeHint !== 'OR') {\n // Only switch back to LWW if hint wasn't explicit OR\n logger.info({ mapName: name }, 'Map auto-detected as LWWMap. Switching type.');\n targetMap = new LWWMap(this.hlc);\n this.maps.set(name, targetMap);\n }\n\n if (targetMap instanceof ORMap) {\n for (const [key, record] of records) {\n if (key === '__tombstones__') {\n const t = record as ORMapTombstones;\n if (t && t.tags) t.tags.forEach(tag => targetMap.applyTombstone(tag));\n } else {\n const orVal = record as ORMapValue<any>;\n if (orVal && orVal.records) {\n orVal.records.forEach(r => targetMap.apply(key, r));\n count++;\n }\n }\n }\n } else if (targetMap instanceof LWWMap) {\n for (const [key, record] of records) {\n // Expect LWWRecord\n // If record is actually ORMapValue (mismatch), we skip or error?\n // If !isOR, we assume LWWRecord.\n if (!(record as any).type) { // LWWRecord doesn't have type property in my impl\n targetMap.merge(key, record as LWWRecord<any>);\n count++;\n }\n }\n }\n\n if (count > 0) {\n logger.info({ mapName: name, count }, 'Loaded records for map');\n this.queryRegistry.refreshSubscriptions(name, targetMap);\n const mapSize = (targetMap instanceof ORMap) ? targetMap.totalRecords : targetMap.size;\n this.metricsService.setMapSize(name, mapSize);\n }\n } catch (err) {\n logger.error({ mapName: name, err }, 'Failed to load map');\n }\n }\n\n private startGarbageCollection() {\n this.gcInterval = setInterval(() => {\n this.reportLocalHlc();\n }, GC_INTERVAL_MS);\n }\n\n // ============ Heartbeat Methods ============\n\n /**\n * Starts the periodic check for dead clients (those that haven't sent PING).\n */\n private startHeartbeatCheck() {\n this.heartbeatCheckInterval = setInterval(() => {\n this.evictDeadClients();\n }, CLIENT_HEARTBEAT_CHECK_INTERVAL_MS);\n }\n\n /**\n * Handles incoming PING message from client.\n * Responds with PONG immediately.\n */\n private handlePing(client: ClientConnection, clientTimestamp: number): void {\n client.lastPingReceived = Date.now();\n\n const pongMessage = {\n type: 'PONG',\n timestamp: clientTimestamp,\n serverTime: Date.now(),\n };\n\n if (client.socket.readyState === WebSocket.OPEN) {\n client.socket.send(serialize(pongMessage));\n }\n }\n\n /**\n * Checks if a client is still alive based on heartbeat.\n */\n public isClientAlive(clientId: string): boolean {\n const client = this.clients.get(clientId);\n if (!client) return false;\n\n const idleTime = Date.now() - client.lastPingReceived;\n return idleTime < CLIENT_HEARTBEAT_TIMEOUT_MS;\n }\n\n /**\n * Returns how long the client has been idle (no PING received).\n */\n public getClientIdleTime(clientId: string): number {\n const client = this.clients.get(clientId);\n if (!client) return Infinity;\n\n return Date.now() - client.lastPingReceived;\n }\n\n /**\n * Evicts clients that haven't sent a PING within the timeout period.\n */\n private evictDeadClients(): void {\n const now = Date.now();\n const deadClients: string[] = [];\n\n for (const [clientId, client] of this.clients) {\n // Only check authenticated clients (unauthenticated ones will timeout via auth mechanism)\n if (client.isAuthenticated) {\n const idleTime = now - client.lastPingReceived;\n if (idleTime > CLIENT_HEARTBEAT_TIMEOUT_MS) {\n deadClients.push(clientId);\n }\n }\n }\n\n for (const clientId of deadClients) {\n const client = this.clients.get(clientId);\n if (client) {\n logger.warn({\n clientId,\n idleTime: now - client.lastPingReceived,\n timeoutMs: CLIENT_HEARTBEAT_TIMEOUT_MS,\n }, 'Evicting dead client (heartbeat timeout)');\n\n // Close the connection\n if (client.socket.readyState === WebSocket.OPEN) {\n client.socket.close(4002, 'Heartbeat timeout');\n }\n }\n }\n }\n\n private reportLocalHlc() {\n // 1. Calculate Local Min HLC\n let minHlc = this.hlc.now();\n\n for (const client of this.clients.values()) {\n if (HLC.compare(client.lastActiveHlc, minHlc) < 0) {\n minHlc = client.lastActiveHlc;\n }\n }\n\n const members = this.cluster.getMembers().sort();\n const leaderId = members[0];\n const myId = this.cluster.config.nodeId;\n\n if (leaderId === myId) {\n // I am Leader\n this.handleGcReport(myId, minHlc);\n } else {\n // Send to Leader\n this.cluster.send(leaderId, 'CLUSTER_GC_REPORT', { minHlc });\n }\n }\n\n private handleGcReport(nodeId: string, minHlc: Timestamp) {\n this.gcReports.set(nodeId, minHlc);\n\n const members = this.cluster.getMembers();\n\n // Check if we have reports from ALL members\n // (Including self, which is inserted directly)\n const allReported = members.every(m => this.gcReports.has(m));\n\n if (allReported) {\n // Calculate Global Safe Timestamp\n let globalSafe = this.hlc.now(); // Start high\n let initialized = false;\n\n for (const ts of this.gcReports.values()) {\n if (!initialized || HLC.compare(ts, globalSafe) < 0) {\n globalSafe = ts;\n initialized = true;\n }\n }\n\n // Add safety buffer (e.g. GC_AGE)\n // prune(timestamp) removes items OLDER than timestamp.\n // We want to remove items OLDER than (GlobalMin - GC_AGE).\n\n const olderThanMillis = globalSafe.millis - GC_AGE_MS;\n const safeTimestamp: Timestamp = {\n millis: olderThanMillis,\n counter: 0,\n nodeId: globalSafe.nodeId // Doesn't matter much for comparison if millis match, but best effort\n };\n\n logger.info({\n globalMinHlc: globalSafe.millis,\n safeGcTimestamp: olderThanMillis,\n reportsCount: this.gcReports.size\n }, 'GC Consensus Reached. Broadcasting Commit.');\n\n // Broadcast Commit\n const commitMsg = {\n type: 'CLUSTER_GC_COMMIT', // Handled by cluster listener\n payload: { safeTimestamp }\n };\n\n // Send to others\n for (const member of members) {\n if (!this.cluster.isLocal(member)) {\n this.cluster.send(member, 'CLUSTER_GC_COMMIT', { safeTimestamp });\n }\n }\n\n // Execute Locally\n this.performGarbageCollection(safeTimestamp);\n\n // Clear reports for next round?\n // Or keep them and overwrite?\n // Overwriting is better for partial updates, but clearing ensures freshness.\n // Since we run interval based, clearing is safer to ensure active participation next time.\n this.gcReports.clear();\n }\n }\n\n private performGarbageCollection(olderThan: Timestamp) {\n logger.info({ olderThanMillis: olderThan.millis }, 'Performing Garbage Collection');\n const now = Date.now();\n\n for (const [name, map] of this.maps) {\n // 1. Check for active expired records (TTL)\n if (map instanceof LWWMap) {\n for (const key of map.allKeys()) {\n const record = map.getRecord(key);\n if (record && record.value !== null && record.ttlMs) {\n const expirationTime = record.timestamp.millis + record.ttlMs;\n if (expirationTime < now) {\n logger.info({ mapName: name, key }, 'Record expired (TTL). Converting to tombstone.');\n\n // Create Tombstone at expiration time to handle \"Resurrection\" correctly\n const tombstoneTimestamp: Timestamp = {\n millis: expirationTime,\n counter: 0, // Reset counter for expiration time\n nodeId: this.hlc.getNodeId // Use our ID\n };\n\n const tombstone: LWWRecord<any> = { value: null, timestamp: tombstoneTimestamp };\n\n // Apply locally\n const changed = map.merge(key, tombstone);\n\n if (changed) {\n // Persist and Broadcast\n // Construct an artificial op to reuse pipeline logic or do manual steps\n // Manual steps are safer here as we don't have a client op context\n\n if (this.storage) {\n this.storage.store(name, key, tombstone).catch(err =>\n logger.error({ mapName: name, key, err }, 'Failed to persist expired tombstone')\n );\n }\n\n const eventPayload = {\n mapName: name,\n key: key,\n eventType: 'UPDATED',\n record: tombstone\n };\n\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n }\n }\n }\n\n // 2. Prune old tombstones\n const removedKeys = map.prune(olderThan);\n if (removedKeys.length > 0) {\n logger.info({ mapName: name, count: removedKeys.length }, 'Pruned records from LWW map');\n if (this.storage) {\n this.storage.deleteAll(name, removedKeys).catch(err => {\n logger.error({ mapName: name, err }, 'Failed to delete pruned keys from storage');\n });\n }\n }\n } else if (map instanceof ORMap) {\n // ORMap Expiration\n // We need to check all active records in the ORMap\n const items = (map as any).items as Map<string, Map<string, ORMapRecord<any>>>;\n const tombstonesSet = (map as any).tombstones as Set<string>;\n\n const tagsToExpire: { key: string; tag: string }[] = [];\n\n for (const [key, keyMap] of items) {\n for (const [tag, record] of keyMap) {\n if (!tombstonesSet.has(tag)) {\n if (record.ttlMs) {\n const expirationTime = record.timestamp.millis + record.ttlMs;\n if (expirationTime < now) {\n tagsToExpire.push({ key, tag });\n }\n }\n }\n }\n }\n\n for (const { key, tag } of tagsToExpire) {\n logger.info({ mapName: name, key, tag }, 'ORMap Record expired (TTL). Removing.');\n\n // Remove by adding tag to tombstones\n map.applyTombstone(tag);\n\n // Persist change\n if (this.storage) {\n // We need to update the key's record list and tombstones\n // Optimally, we should batch these updates\n const records = map.getRecords(key);\n if (records.length > 0) {\n this.storage.store(name, key, { type: 'OR', records });\n } else {\n this.storage.delete(name, key);\n }\n\n const currentTombstones = map.getTombstones();\n this.storage.store(name, '__tombstones__', {\n type: 'OR_TOMBSTONES',\n tags: currentTombstones\n });\n }\n\n // Broadcast\n const eventPayload = {\n mapName: name,\n key: key,\n eventType: 'OR_REMOVE',\n orTag: tag\n };\n\n this.broadcast({\n type: 'SERVER_EVENT',\n payload: eventPayload,\n timestamp: this.hlc.now()\n });\n\n const members = this.cluster.getMembers();\n for (const memberId of members) {\n if (!this.cluster.isLocal(memberId)) {\n this.cluster.send(memberId, 'CLUSTER_EVENT', eventPayload);\n }\n }\n }\n\n // 2. Prune old tombstones\n const removedTags = map.prune(olderThan);\n if (removedTags.length > 0) {\n logger.info({ mapName: name, count: removedTags.length }, 'Pruned tombstones from OR map');\n // We need to update __tombstones__ in storage\n if (this.storage) {\n const currentTombstones = map.getTombstones();\n this.storage.store(name, '__tombstones__', {\n type: 'OR_TOMBSTONES',\n tags: currentTombstones\n }).catch(err => {\n logger.error({ mapName: name, err }, 'Failed to update tombstones');\n });\n }\n }\n }\n }\n\n // Broadcast to clients\n this.broadcast({\n type: 'GC_PRUNE',\n payload: {\n olderThan\n }\n });\n }\n private buildTLSOptions(config: TLSConfig): HttpsServerOptions {\n const options: HttpsServerOptions = {\n cert: readFileSync(config.certPath),\n key: readFileSync(config.keyPath),\n minVersion: config.minVersion || 'TLSv1.2',\n };\n\n if (config.caCertPath) {\n options.ca = readFileSync(config.caCertPath);\n }\n\n if (config.ciphers) {\n options.ciphers = config.ciphers;\n }\n\n if (config.passphrase) {\n options.passphrase = config.passphrase;\n }\n\n return options;\n }\n}\n","import { LWWRecord, PredicateNode, evaluatePredicate } from '@topgunbuild/core';\n\nexport interface Query {\n where?: Record<string, any>;\n predicate?: PredicateNode;\n sort?: Record<string, 'asc' | 'desc'>;\n limit?: number;\n offset?: number;\n}\n\n/**\n * Checks if a record matches a query.\n * Supports simple exact match for now.\n */\nexport function matchesQuery(record: LWWRecord<any>, query: Query): boolean {\n const data = record.value;\n if (!data) return false; \n\n // Check TTL\n if (record.ttlMs) {\n const now = Date.now();\n if (record.timestamp.millis + record.ttlMs < now) {\n return false; // Expired\n }\n }\n\n // 1. New Predicate API\n if (query.predicate) {\n return evaluatePredicate(query.predicate, data);\n }\n\n // 2. Legacy 'where' clause\n if (!query.where) return true; // Empty query matches everything\n\n for (const [field, expected] of Object.entries(query.where)) {\n const actual = data[field];\n\n // Operator matching (e.g. { age: { $gt: 18 } })\n if (typeof expected === 'object' && expected !== null && !Array.isArray(expected)) {\n for (const [op, opValueRaw] of Object.entries(expected)) {\n const opValue = opValueRaw as any; // Cast for comparison\n switch (op) {\n case '$gt':\n if (!(actual > opValue)) return false;\n break;\n case '$gte':\n if (!(actual >= opValue)) return false;\n break;\n case '$lt':\n if (!(actual < opValue)) return false;\n break;\n case '$lte':\n if (!(actual <= opValue)) return false;\n break;\n case '$ne':\n if (!(actual !== opValue)) return false;\n break;\n // Add more operators as needed ($in, etc.)\n default:\n // Unknown operator, treating as exact match (or should we fail?)\n // For now, ignore unknown operators or treat as mismatch?\n // Let's treat unknown operators as false to be safe.\n return false; \n }\n }\n } else {\n // Simple exact match\n if (actual !== expected) {\n return false;\n }\n }\n }\n \n return true;\n}\n\nexport function executeQuery(records: Map<string, LWWRecord<any>> | LWWRecord<any>[], query: Query): { key: string; value: any }[] {\n // Handle null/undefined query\n if (!query) {\n query = {};\n }\n\n let results: { key: string; record: LWWRecord<any> }[] = [];\n\n // 1. Filter\n if (records instanceof Map) {\n for (const [key, record] of records) {\n if (matchesQuery(record, query)) {\n results.push({ key, record });\n }\n }\n } else {\n // If array, we might not have keys easily unless they are in the record or we iterate\n // For now assume Map input primarily for ServerCoordinator\n // But if input is array of records?\n for (const record of records) {\n // Assuming key is not readily available if just array of records, \n // but usually we pass Map from ServerCoordinator.\n // If we really need key, we need it in the input.\n // Let's stick to Map input for now as that's what ServerCoordinator has.\n // But wait, the signature I defined allows array.\n if (matchesQuery(record, query)) {\n results.push({ key: '?', record }); \n }\n }\n }\n\n // 2. Sort\n if (query.sort) {\n results.sort((a, b) => {\n for (const [field, direction] of Object.entries(query.sort!)) {\n const valA = a.record.value[field];\n const valB = b.record.value[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 // 3. Limit & Offset\n if (query.offset || query.limit) {\n const offset = query.offset || 0;\n const limit = query.limit || results.length;\n results = results.slice(offset, offset + limit);\n }\n\n return results.map(r => ({ key: r.key, value: r.record.value }));\n}\n","import { Query, matchesQuery, executeQuery } from './Matcher';\nimport { LWWRecord, LWWMap, ORMap, serialize, PredicateNode, ORMapRecord } from '@topgunbuild/core';\nimport { WebSocket } from 'ws';\nimport { logger } from '../utils/logger';\n\nexport interface Subscription {\n id: string; // queryId\n clientId: string;\n mapName: string;\n query: Query;\n socket: WebSocket;\n previousResultKeys: Set<string>;\n interestedFields?: Set<string> | 'ALL';\n _cleanup?: () => void; // For Reverse Index cleanup\n}\n\nclass ReverseQueryIndex {\n // field -> value -> Set<Subscription>\n private equality = new Map<string, Map<any, Set<Subscription>>>();\n // field -> Set<Subscription>\n private interest = new Map<string, Set<Subscription>>();\n // catch-all\n private wildcard = new Set<Subscription>();\n\n public add(sub: Subscription) {\n const query = sub.query;\n let indexed = false;\n const cleanupFns: (() => void)[] = [];\n\n // 1. Where\n if (query.where) {\n for (const [field, value] of Object.entries(query.where)) {\n if (typeof value !== 'object') {\n // Exact match\n this.addEquality(field, value, sub);\n cleanupFns.push(() => this.removeEquality(field, value, sub));\n indexed = true;\n } else {\n // Operator - add to interest\n this.addInterest(field, sub);\n cleanupFns.push(() => this.removeInterest(field, sub));\n indexed = true;\n }\n }\n }\n \n // 2. Predicate\n if (query.predicate) {\n const visit = (node: PredicateNode) => {\n if (node.op === 'eq' && node.attribute && node.value !== undefined) {\n this.addEquality(node.attribute, node.value, sub);\n cleanupFns.push(() => this.removeEquality(node.attribute!, node.value, sub));\n indexed = true;\n } else if (node.attribute) {\n // Any other op on attribute\n this.addInterest(node.attribute, sub);\n cleanupFns.push(() => this.removeInterest(node.attribute!, sub));\n indexed = true;\n }\n \n if (node.children) {\n node.children.forEach(visit);\n }\n };\n visit(query.predicate);\n }\n \n // 3. Sort\n if (query.sort) {\n Object.keys(query.sort).forEach(k => {\n this.addInterest(k, sub);\n cleanupFns.push(() => this.removeInterest(k, sub));\n indexed = true;\n });\n }\n\n if (!indexed) {\n this.wildcard.add(sub);\n cleanupFns.push(() => this.wildcard.delete(sub));\n }\n \n sub._cleanup = () => cleanupFns.forEach(fn => fn());\n }\n\n public remove(sub: Subscription) {\n if (sub._cleanup) {\n sub._cleanup();\n sub._cleanup = undefined;\n }\n }\n\n public getCandidates(changedFields: Set<string> | 'ALL', oldVal: any, newVal: any): Set<Subscription> {\n const candidates = new Set<Subscription>(this.wildcard);\n\n if (changedFields === 'ALL') {\n // Return all possible candidates (inefficient but safe)\n // We collect from all indexes? Or just return all subs?\n // To match \"wildcard\" behavior, we should probably iterate all.\n // But we don't track all subs in index easily.\n // We can iterate this.interest and this.equality.\n for (const set of this.interest.values()) {\n for (const s of set) candidates.add(s);\n }\n for (const map of this.equality.values()) {\n for (const set of map.values()) {\n for (const s of set) candidates.add(s);\n }\n }\n return candidates;\n }\n\n // If no changes detected (shouldn't happen if called correctly), just return wildcard\n if (changedFields.size === 0) return candidates;\n\n for (const field of changedFields) {\n // 1. Interest (General)\n if (this.interest.has(field)) {\n for (const sub of this.interest.get(field)!) {\n candidates.add(sub);\n }\n }\n\n // 2. Equality\n if (this.equality.has(field)) {\n const valMap = this.equality.get(field)!;\n \n // Check New Value queries\n if (newVal && newVal[field] !== undefined && valMap.has(newVal[field])) {\n for (const sub of valMap.get(newVal[field])!) {\n candidates.add(sub);\n }\n }\n \n // Check Old Value queries\n if (oldVal && oldVal[field] !== undefined && valMap.has(oldVal[field])) {\n for (const sub of valMap.get(oldVal[field])!) {\n candidates.add(sub);\n }\n }\n }\n }\n \n return candidates;\n }\n\n private addEquality(field: string, value: any, sub: Subscription) {\n if (!this.equality.has(field)) this.equality.set(field, new Map());\n const valMap = this.equality.get(field)!;\n if (!valMap.has(value)) valMap.set(value, new Set());\n valMap.get(value)!.add(sub);\n }\n\n private removeEquality(field: string, value: any, sub: Subscription) {\n const valMap = this.equality.get(field);\n if (valMap) {\n const set = valMap.get(value);\n if (set) {\n set.delete(sub);\n if (set.size === 0) valMap.delete(value);\n }\n if (valMap.size === 0) this.equality.delete(field);\n }\n }\n\n private addInterest(field: string, sub: Subscription) {\n if (!this.interest.has(field)) this.interest.set(field, new Set());\n this.interest.get(field)!.add(sub);\n }\n\n private removeInterest(field: string, sub: Subscription) {\n const set = this.interest.get(field);\n if (set) {\n set.delete(sub);\n if (set.size === 0) this.interest.delete(field);\n }\n }\n}\n\nexport class QueryRegistry {\n // MapName -> Set of Subscriptions (Legacy/Backup)\n private subscriptions: Map<string, Set<Subscription>> = new Map();\n \n // MapName -> Reverse Index\n private indexes: Map<string, ReverseQueryIndex> = new Map();\n\n public register(sub: Subscription) {\n if (!this.subscriptions.has(sub.mapName)) {\n this.subscriptions.set(sub.mapName, new Set());\n this.indexes.set(sub.mapName, new ReverseQueryIndex());\n }\n \n const interestedFields = this.analyzeQueryFields(sub.query);\n sub.interestedFields = interestedFields;\n\n this.subscriptions.get(sub.mapName)!.add(sub);\n this.indexes.get(sub.mapName)!.add(sub);\n \n logger.info({ clientId: sub.clientId, mapName: sub.mapName, query: sub.query }, 'Client subscribed');\n }\n\n public unregister(queryId: string) {\n for (const [mapName, subs] of this.subscriptions) {\n for (const sub of subs) {\n if (sub.id === queryId) {\n subs.delete(sub);\n this.indexes.get(mapName)?.remove(sub);\n return; \n }\n }\n }\n }\n\n public unsubscribeAll(clientId: string) {\n for (const [mapName, subs] of this.subscriptions) {\n for (const sub of subs) {\n if (sub.clientId === clientId) {\n subs.delete(sub);\n this.indexes.get(mapName)?.remove(sub);\n }\n }\n }\n }\n\n /**\n * Refreshes all subscriptions for a given map.\n * Useful when the map is bulk-loaded from storage.\n */\n public refreshSubscriptions(mapName: string, map: LWWMap<string, any> | ORMap<string, any>) {\n const subs = this.subscriptions.get(mapName);\n if (!subs || subs.size === 0) return;\n\n const allRecords = this.getMapRecords(map);\n\n for (const sub of subs) {\n const newResults = executeQuery(allRecords, sub.query);\n const newResultKeys = new Set(newResults.map(r => r.key));\n\n // 1. Removed\n for (const key of sub.previousResultKeys) {\n if (!newResultKeys.has(key)) {\n this.sendUpdate(sub, key, null, 'REMOVE');\n }\n }\n\n // 2. Added/Updated\n for (const res of newResults) {\n // Send update for all currently matching records\n // We assume value might have changed or it is new\n this.sendUpdate(sub, res.key, res.value, 'UPDATE');\n }\n\n sub.previousResultKeys = newResultKeys;\n }\n }\n\n private getMapRecords(map: LWWMap<string, any> | ORMap<string, any>): Map<string, any> {\n const recordsMap = new Map<string, any>();\n\n // Use duck-typing to support mocks and proxies\n const mapAny = map as any;\n\n // LWWMap-like: has allKeys() and getRecord()\n if (typeof mapAny.allKeys === 'function' && typeof mapAny.getRecord === 'function') {\n for (const key of mapAny.allKeys()) {\n const rec = mapAny.getRecord(key);\n if (rec) {\n recordsMap.set(key, rec);\n }\n }\n }\n // ORMap-like: has items Map and get() returns array\n else if (mapAny.items instanceof Map && typeof mapAny.get === 'function') {\n const items = mapAny.items as Map<string, any>;\n for (const key of items.keys()) {\n const values = mapAny.get(key);\n if (values.length > 0) {\n recordsMap.set(key, { value: values });\n }\n }\n }\n return recordsMap;\n }\n\n /**\n * Processes a record change for all relevant subscriptions.\n * Calculates diffs and sends updates.\n */\n public processChange(\n mapName: string,\n map: LWWMap<string, any> | ORMap<string, any>,\n changeKey: string,\n changeRecord: any, // LWWRecord | ORMapRecord | ORMapRecord[]\n oldRecord?: any // LWWRecord | ORMapRecord[]\n ) {\n const index = this.indexes.get(mapName);\n if (!index) return;\n\n // Extract Values\n const newVal = this.extractValue(changeRecord);\n const oldVal = this.extractValue(oldRecord);\n\n // 0. Calculate Changed Fields\n const changedFields = this.getChangedFields(oldVal, newVal);\n\n if (changedFields !== 'ALL' && changedFields.size === 0 && oldRecord && changeRecord) {\n return;\n }\n \n const candidates = index.getCandidates(changedFields, oldVal, newVal);\n \n if (candidates.size === 0) return;\n\n // Helper to get all records as a Map for executeQuery\n let recordsMap: Map<string, any> | null = null;\n const getRecordsMap = () => {\n if (recordsMap) return recordsMap;\n recordsMap = this.getMapRecords(map);\n return recordsMap;\n };\n\n for (const sub of candidates) {\n const dummyRecord: LWWRecord<any> = { \n value: newVal,\n timestamp: { millis: 0, counter: 0, nodeId: '' } // Dummy timestamp for matchesQuery\n };\n const isMatch = matchesQuery(dummyRecord, sub.query); // Approximate match check\n const wasInResult = sub.previousResultKeys.has(changeKey);\n\n if (!isMatch && !wasInResult) {\n continue;\n }\n\n // Re-evaluate query\n const allRecords = getRecordsMap();\n const newResults = executeQuery(allRecords, sub.query);\n const newResultKeys = new Set(newResults.map(r => r.key));\n\n // Determine changes\n // 1. Removed\n for (const key of sub.previousResultKeys) {\n if (!newResultKeys.has(key)) {\n this.sendUpdate(sub, key, null, 'REMOVE');\n }\n }\n\n // 2. Added/Updated\n for (const res of newResults) {\n const key = res.key;\n const isNew = !sub.previousResultKeys.has(key);\n \n if (key === changeKey) {\n this.sendUpdate(sub, key, res.value, 'UPDATE');\n } else if (isNew) {\n this.sendUpdate(sub, key, res.value, 'UPDATE');\n }\n }\n\n sub.previousResultKeys = newResultKeys;\n }\n }\n\n private extractValue(record: any): any {\n if (!record) return null;\n if (Array.isArray(record)) {\n // ORMapRecord[]\n return record.map(r => r.value);\n }\n // LWWRecord or ORMapRecord\n return record.value;\n }\n\n private sendUpdate(sub: Subscription, key: string, value: any, type: 'UPDATE' | 'REMOVE') {\n if (sub.socket.readyState === 1) {\n sub.socket.send(serialize({\n type: 'QUERY_UPDATE',\n payload: {\n queryId: sub.id,\n key,\n value,\n type\n }\n }));\n }\n }\n\n private analyzeQueryFields(query: Query): Set<string> | 'ALL' {\n const fields = new Set<string>();\n try {\n if (query.predicate) {\n const extract = (node: PredicateNode) => {\n if (node.attribute) fields.add(node.attribute);\n if (node.children) node.children.forEach(extract);\n };\n extract(query.predicate);\n }\n if (query.where) {\n Object.keys(query.where).forEach(k => fields.add(k));\n }\n if (query.sort) {\n Object.keys(query.sort).forEach(k => fields.add(k));\n }\n } catch (e) {\n return 'ALL';\n }\n return fields.size > 0 ? fields : 'ALL';\n }\n\n private getChangedFields(oldValue: any, newValue: any): Set<string> | 'ALL' {\n // If values are arrays (ORMap), just return ALL for now to force check\n if (Array.isArray(oldValue) || Array.isArray(newValue)) return 'ALL';\n\n if (oldValue === newValue) return new Set();\n if (!oldValue && !newValue) return new Set();\n\n if (!oldValue) return new Set(Object.keys(newValue || {}));\n if (!newValue) return new Set(Object.keys(oldValue || {}));\n \n const changes = new Set<string>();\n const allKeys = new Set([...Object.keys(oldValue), ...Object.keys(newValue)]);\n \n for (const key of allKeys) {\n if (oldValue[key] !== newValue[key]) {\n changes.add(key);\n }\n }\n return changes;\n }\n}\n","import pino from 'pino';\n\nconst logLevel = process.env.LOG_LEVEL || 'info';\n\nexport const logger = pino({\n level: logLevel,\n transport: 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 formatters: {\n level: (label) => {\n return { level: label };\n }\n }\n});\n\nexport type Logger = typeof logger;\n\n","import { ClusterManager } from '../cluster/ClusterManager';\nimport { logger } from '../utils/logger';\n\nexport interface TopicManagerConfig {\n cluster: ClusterManager;\n /** Callback to send message to a specific client */\n sendToClient: (clientId: string, message: any) => void;\n}\n\nexport class TopicManager {\n private subscribers: Map<string, Set<string>> = new Map(); // topic -> Set<clientId>\n private cluster: ClusterManager;\n private sendToClient: (clientId: string, message: any) => void;\n private readonly MAX_SUBSCRIPTIONS = 100; // M1: Basic limit\n\n constructor(config: TopicManagerConfig) {\n this.cluster = config.cluster;\n this.sendToClient = config.sendToClient;\n }\n\n private validateTopic(topic: string): void {\n // H2: Validation\n if (!topic || topic.length > 256 || !/^[\\w\\-.:/]+$/.test(topic)) {\n throw new Error('Invalid topic name');\n }\n }\n\n /**\n * Subscribe a client to a topic\n */\n public subscribe(clientId: string, topic: string) {\n this.validateTopic(topic);\n\n // Check limit (M1)\n // This is expensive (iterating all topics). Optimized: maintain client->topics map?\n // For now, iterate.\n let count = 0;\n for (const subs of this.subscribers.values()) {\n if (subs.has(clientId)) count++;\n }\n if (count >= this.MAX_SUBSCRIPTIONS) {\n throw new Error('Subscription limit reached');\n }\n\n if (!this.subscribers.has(topic)) {\n this.subscribers.set(topic, new Set());\n }\n this.subscribers.get(topic)!.add(clientId);\n logger.debug({ clientId, topic }, 'Client subscribed to topic');\n }\n\n /**\n * Unsubscribe a client from a topic\n */\n public unsubscribe(clientId: string, topic: string) {\n const subs = this.subscribers.get(topic);\n if (subs) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscribers.delete(topic);\n }\n logger.debug({ clientId, topic }, 'Client unsubscribed from topic');\n }\n }\n\n /**\n * Clean up all subscriptions for a client (e.g. on disconnect)\n */\n public unsubscribeAll(clientId: string) {\n for (const [topic, subs] of this.subscribers) {\n if (subs.has(clientId)) {\n subs.delete(clientId);\n if (subs.size === 0) {\n this.subscribers.delete(topic);\n }\n }\n }\n }\n\n /**\n * Publish a message to a topic\n * @param topic Topic name\n * @param data Message data\n * @param senderId Client ID of the publisher (optional)\n * @param fromCluster Whether this message came from another cluster node\n */\n public publish(topic: string, data: any, senderId?: string, fromCluster: boolean = false) {\n this.validateTopic(topic);\n\n // 1. Send to local subscribers\n const subs = this.subscribers.get(topic);\n if (subs) {\n const payload = {\n topic,\n data,\n publisherId: senderId,\n timestamp: Date.now()\n };\n \n const message = {\n type: 'TOPIC_MESSAGE',\n payload\n };\n\n for (const clientId of subs) {\n // Don't echo back to sender if local\n if (clientId !== senderId) {\n this.sendToClient(clientId, message);\n }\n }\n }\n\n // 2. Broadcast to cluster (only if not already from cluster)\n if (!fromCluster) {\n this.cluster.getMembers().forEach(nodeId => {\n if (!this.cluster.isLocal(nodeId)) {\n this.cluster.send(nodeId, 'CLUSTER_TOPIC_PUB', {\n topic,\n data,\n originalSenderId: senderId\n });\n }\n });\n }\n }\n}\n\n","import { WebSocket, WebSocketServer, ClientOptions as WsClientOptions } from 'ws';\nimport { EventEmitter } from 'events';\nimport * as dns from 'dns';\nimport { logger } from '../utils/logger';\nimport { readFileSync } from 'fs';\nimport * as https from 'https';\nimport { ClusterTLSConfig } from '../types/TLSConfig';\n\nexport interface ClusterConfig {\n nodeId: string;\n host: string;\n port: number;\n peers: string[]; // List of \"host:port\"\n discovery?: 'manual' | 'kubernetes';\n serviceName?: string;\n discoveryInterval?: number;\n tls?: ClusterTLSConfig;\n}\n\nexport interface ClusterMember {\n nodeId: string;\n host: string;\n port: number;\n socket: WebSocket;\n isSelf: boolean;\n}\n\nexport interface ClusterMessage {\n type: 'HELLO' | 'OP_FORWARD' | 'PARTITION_UPDATE' | 'HEARTBEAT' | 'CLUSTER_EVENT' | 'CLUSTER_QUERY_EXEC' | 'CLUSTER_QUERY_RESP' | 'CLUSTER_GC_REPORT' | 'CLUSTER_GC_COMMIT' | 'CLUSTER_LOCK_REQ' | 'CLUSTER_LOCK_RELEASE' | 'CLUSTER_LOCK_GRANTED' | 'CLUSTER_LOCK_RELEASED' | 'CLUSTER_CLIENT_DISCONNECTED' | 'CLUSTER_TOPIC_PUB';\n senderId: string;\n payload: any;\n}\n\nexport class ClusterManager extends EventEmitter {\n public readonly config: ClusterConfig;\n private server?: WebSocketServer;\n private members: Map<string, ClusterMember> = new Map();\n private pendingConnections: Set<string> = new Set();\n private reconnectIntervals: Map<string, NodeJS.Timeout> = new Map();\n private discoveryTimer?: NodeJS.Timeout;\n\n constructor(config: ClusterConfig) {\n super();\n this.config = config;\n }\n\n private _actualPort: number = 0;\n\n /** Get the actual port the cluster is listening on */\n public get port(): number {\n return this._actualPort;\n }\n\n public start(): Promise<number> {\n return new Promise((resolve) => {\n logger.info({ port: this.config.port, tls: !!this.config.tls?.enabled }, 'Starting Cluster Manager');\n\n if (this.config.tls?.enabled) {\n // HTTPS-based WebSocket Server for cluster\n const tlsOptions = this.buildClusterTLSOptions();\n const httpsServer = https.createServer(tlsOptions);\n this.server = new WebSocketServer({ server: httpsServer });\n\n httpsServer.listen(this.config.port, () => {\n const addr = httpsServer.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : this.config.port;\n logger.info({ port: this._actualPort }, 'Cluster Manager listening (TLS enabled)');\n this.onServerReady(resolve);\n });\n } else {\n this.server = new WebSocketServer({ port: this.config.port });\n\n this.server.on('listening', () => {\n const addr = this.server!.address();\n this._actualPort = typeof addr === 'object' && addr ? addr.port : this.config.port;\n logger.info({ port: this._actualPort }, 'Cluster Manager listening');\n this.onServerReady(resolve);\n });\n }\n\n this.server?.on('connection', (ws, req) => {\n logger.info({ remoteAddress: req.socket.remoteAddress }, 'Incoming cluster connection');\n this.handleSocket(ws, false);\n });\n });\n }\n\n /** Called when server is ready - registers self and initiates peer connections */\n private onServerReady(resolve: (port: number) => void): void {\n // Add self to members with actual port\n this.members.set(this.config.nodeId, {\n nodeId: this.config.nodeId,\n host: this.config.host,\n port: this._actualPort,\n socket: null as any,\n isSelf: true\n });\n\n // Connect to peers after we know our port\n if (this.config.discovery === 'kubernetes' && this.config.serviceName) {\n this.startDiscovery();\n } else {\n this.connectToPeers();\n }\n\n resolve(this._actualPort);\n }\n\n public stop() {\n logger.info({ port: this.config.port }, 'Stopping Cluster Manager');\n\n // Clear reconnect intervals\n for (const timeout of this.reconnectIntervals.values()) {\n clearTimeout(timeout);\n }\n this.reconnectIntervals.clear();\n if (this.discoveryTimer) {\n clearInterval(this.discoveryTimer);\n this.discoveryTimer = undefined;\n }\n this.pendingConnections.clear();\n\n // Close all peer connections\n for (const member of this.members.values()) {\n if (member.socket) {\n member.socket.terminate(); // Force close\n }\n }\n this.members.clear();\n\n // Close server\n if (this.server) {\n this.server.close();\n }\n }\n\n private connectToPeers() {\n for (const peer of this.config.peers) {\n this.connectToPeer(peer);\n }\n }\n\n private startDiscovery() {\n const runDiscovery = async () => {\n if (!this.config.serviceName) return;\n\n try {\n const addresses = await dns.promises.resolve4(this.config.serviceName);\n logger.debug({ addresses, serviceName: this.config.serviceName }, 'DNS discovery results');\n\n for (const ip of addresses) {\n // Use actual port if available (likely matching K8s config), fallback to config port\n const targetPort = this._actualPort || this.config.port;\n const peerAddress = `${ip}:${targetPort}`;\n // Attempt to connect. connectToPeer handles dupes and self-checks (via handshake eventually)\n this.connectToPeer(peerAddress);\n }\n } catch (err: any) {\n logger.error({ err: err.message, serviceName: this.config.serviceName }, 'DNS discovery failed');\n }\n };\n\n logger.info({ serviceName: this.config.serviceName }, 'Starting Kubernetes DNS discovery');\n runDiscovery();\n // Default to 10s if not specified, to be less aggressive\n this.discoveryTimer = setInterval(runDiscovery, this.config.discoveryInterval || 10000);\n }\n\n private scheduleReconnect(peerAddress: string, attempt: number = 0) {\n if (this.reconnectIntervals.has(peerAddress)) return;\n\n // Exponential backoff: 5s, 10s, 20s, 40s, 60s (max)\n const delay = Math.min(5000 * Math.pow(2, attempt), 60000);\n\n const timeout = setTimeout(() => {\n this.reconnectIntervals.delete(peerAddress);\n // Pass next attempt number\n this.connectToPeerWithBackoff(peerAddress, attempt + 1);\n }, delay);\n\n this.reconnectIntervals.set(peerAddress, timeout);\n }\n\n // Helper to track attempts\n private connectToPeerWithBackoff(peerAddress: string, attempt: number) {\n // We need to modify connectToPeer to accept attempt or create a wrapper.\n // To keep it simple without changing signature of connectToPeer everywhere,\n // we'll just call connectToPeer and let it fail -> scheduleReconnect -> increment attempt.\n // But connectToPeer logic needs to pass the attempt to scheduleReconnect on failure.\n // Refactoring connectToPeer to take optional attempt param.\n this._connectToPeerInternal(peerAddress, attempt);\n }\n\n private connectToPeer(peerAddress: string) {\n this._connectToPeerInternal(peerAddress, 0);\n }\n\n private _connectToPeerInternal(peerAddress: string, attempt: number) {\n if (this.pendingConnections.has(peerAddress)) return;\n\n // Check if already connected\n for (const member of this.members.values()) {\n if (`${member.host}:${member.port}` === peerAddress) return;\n }\n\n // PREVENT LOOP: ... (omitted comments)\n\n logger.info({ peerAddress, attempt, tls: !!this.config.tls?.enabled }, 'Connecting to peer');\n this.pendingConnections.add(peerAddress);\n\n try {\n let ws: WebSocket;\n\n if (this.config.tls?.enabled) {\n // Secure WebSocket connection\n const protocol = 'wss://';\n const wsOptions: WsClientOptions = {\n rejectUnauthorized: this.config.tls.rejectUnauthorized !== false,\n };\n\n // mTLS: Provide client certificate\n if (this.config.tls.certPath && this.config.tls.keyPath) {\n wsOptions.cert = readFileSync(this.config.tls.certPath);\n wsOptions.key = readFileSync(this.config.tls.keyPath);\n\n if (this.config.tls.passphrase) {\n wsOptions.passphrase = this.config.tls.passphrase;\n }\n }\n\n // CA for peer verification\n if (this.config.tls.caCertPath) {\n wsOptions.ca = readFileSync(this.config.tls.caCertPath);\n }\n\n ws = new WebSocket(`${protocol}${peerAddress}`, wsOptions);\n } else {\n // Plain WebSocket (development)\n ws = new WebSocket(`ws://${peerAddress}`);\n }\n\n ws.on('open', () => {\n this.pendingConnections.delete(peerAddress);\n logger.info({ peerAddress }, 'Connected to peer');\n // Reset backoff on success\n this.handleSocket(ws, true, peerAddress);\n });\n\n ws.on('error', (err) => {\n logger.error({ peerAddress, err: err.message }, 'Connection error to peer');\n this.pendingConnections.delete(peerAddress);\n this.scheduleReconnect(peerAddress, attempt);\n });\n\n ws.on('close', () => {\n this.pendingConnections.delete(peerAddress);\n });\n\n } catch (e) {\n this.pendingConnections.delete(peerAddress);\n this.scheduleReconnect(peerAddress, attempt);\n }\n }\n\n private handleSocket(ws: WebSocket, initiated: boolean, peerAddress?: string) {\n // Handshake: Send my NodeID with actual port (not config port which may be 0)\n const helloMsg: ClusterMessage = {\n type: 'HELLO',\n senderId: this.config.nodeId,\n payload: {\n host: this.config.host,\n port: this._actualPort || this.config.port\n }\n };\n ws.send(JSON.stringify(helloMsg));\n\n let remoteNodeId: string | null = null;\n\n ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString()) as ClusterMessage;\n\n if (msg.type === 'HELLO') {\n remoteNodeId = msg.senderId;\n const { host, port } = msg.payload;\n logger.info({ nodeId: remoteNodeId, host, port }, 'Peer identified');\n\n // Tie-Breaker Rule: Connection initiated by Low ID wins.\n // Initiator < Receiver = Valid.\n // Initiator > Receiver = Invalid (Drop).\n\n const myId = this.config.nodeId;\n const otherId = remoteNodeId;\n\n // Determine who initiated this specific socket\n const initiatorId = initiated ? myId : otherId;\n const receiverId = initiated ? otherId : myId;\n\n /*\n // Tie-Breaker Rule: Connection initiated by Low ID wins.\n // Initiator < Receiver = Valid.\n // Initiator > Receiver = Invalid (Drop).\n // \n // DISABLED: This strict rule prevents High-ID nodes from joining Low-ID seeds (common pattern).\n // We only use this for duplicate resolution now.\n \n if (initiatorId >= receiverId) {\n logger.info({ initiatorId, receiverId }, 'Dropping connection (Low-ID Initiator Policy)');\n try {\n ws.close();\n } catch(e) {}\n return;\n }\n */\n\n // If we get here, this is a VALID connection.\n // Check if we somehow already have a connection\n if (this.members.has(remoteNodeId)) {\n logger.warn({ nodeId: remoteNodeId }, 'Duplicate valid connection. Replacing.');\n // In a real production system, we should use the Tie-Breaker here to decide which one to keep\n // to avoid split-brain socket usage.\n // For now, 'Replacing' means Last-Write-Wins on the connection slot.\n }\n\n this.members.set(remoteNodeId, {\n nodeId: remoteNodeId,\n host,\n port,\n socket: ws,\n isSelf: false\n });\n\n this.emit('memberJoined', remoteNodeId);\n } else {\n this.emit('message', msg);\n }\n } catch (err) {\n logger.error({ err }, 'Failed to parse cluster message');\n }\n });\n\n ws.on('close', () => {\n if (remoteNodeId) {\n // Only handle disconnect if this was the ACTIVE socket\n // This prevents \"duplicate connection\" cleanup from killing the valid session\n const current = this.members.get(remoteNodeId);\n if (current && current.socket === ws) {\n logger.info({ nodeId: remoteNodeId }, 'Peer disconnected');\n this.members.delete(remoteNodeId);\n this.emit('memberLeft', remoteNodeId);\n\n // If we initiated, we should try to reconnect\n if (initiated && peerAddress) {\n // Start with 0 attempt on fresh disconnect? \n // Or maybe we should consider this a failure and backoff?\n // Let's restart with 0 for now as it might be a temp network blip\n this.scheduleReconnect(peerAddress, 0);\n }\n } else {\n // console.log(`Ignored close from stale/duplicate socket for ${remoteNodeId}`);\n }\n }\n });\n }\n\n public send(nodeId: string, type: ClusterMessage['type'], payload: any) {\n const member = this.members.get(nodeId);\n if (member && member.socket && member.socket.readyState === WebSocket.OPEN) {\n const msg: ClusterMessage = {\n type,\n senderId: this.config.nodeId,\n payload\n };\n member.socket.send(JSON.stringify(msg));\n } else {\n logger.warn({ nodeId }, 'Cannot send to node: not connected');\n }\n }\n\n public sendToNode(nodeId: string, message: any) {\n this.send(nodeId, 'OP_FORWARD', message);\n }\n\n public getMembers(): string[] {\n return Array.from(this.members.keys());\n }\n\n public isLocal(nodeId: string): boolean {\n return nodeId === this.config.nodeId;\n }\n\n private buildClusterTLSOptions(): https.ServerOptions {\n const config = this.config.tls!;\n\n const options: https.ServerOptions = {\n cert: readFileSync(config.certPath),\n key: readFileSync(config.keyPath),\n minVersion: config.minVersion || 'TLSv1.2',\n };\n\n if (config.caCertPath) {\n options.ca = readFileSync(config.caCertPath);\n }\n\n if (config.requireClientCert) {\n options.requestCert = true;\n options.rejectUnauthorized = true;\n }\n\n if (config.passphrase) {\n options.passphrase = config.passphrase;\n }\n\n return options;\n }\n}\n\n","import { ClusterManager } from './ClusterManager';\nimport { hashString } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport interface PartitionDistribution {\n owner: string;\n backups: string[];\n}\n\nexport class PartitionService {\n private cluster: ClusterManager;\n // partitionId -> { owner, backups }\n private partitions: Map<number, PartitionDistribution> = new Map();\n private readonly PARTITION_COUNT = 271;\n private readonly BACKUP_COUNT = 1; // Standard Hazelcast default\n\n constructor(cluster: ClusterManager) {\n this.cluster = cluster;\n this.cluster.on('memberJoined', () => this.rebalance());\n this.cluster.on('memberLeft', () => this.rebalance());\n \n // Initial rebalance\n this.rebalance();\n }\n\n public getPartitionId(key: string): number {\n // Use Math.abs to ensure positive partition ID\n return Math.abs(hashString(key)) % this.PARTITION_COUNT;\n }\n\n public getDistribution(key: string): PartitionDistribution {\n const pId = this.getPartitionId(key);\n return this.partitions.get(pId) || { \n owner: this.cluster.config.nodeId, \n backups: [] \n };\n }\n\n public getOwner(key: string): string {\n return this.getDistribution(key).owner;\n }\n\n public isLocalOwner(key: string): boolean {\n return this.getOwner(key) === this.cluster.config.nodeId;\n }\n\n public isLocalBackup(key: string): boolean {\n const dist = this.getDistribution(key);\n return dist.backups.includes(this.cluster.config.nodeId);\n }\n\n public isRelated(key: string): boolean {\n return this.isLocalOwner(key) || this.isLocalBackup(key);\n }\n\n private rebalance() {\n // this.cluster.getMembers() includes self (added in ClusterManager.start)\n let allMembers = this.cluster.getMembers().sort();\n\n // If no other members, include self\n if (allMembers.length === 0) {\n allMembers = [this.cluster.config.nodeId];\n }\n\n logger.info({ memberCount: allMembers.length, members: allMembers }, 'Rebalancing partitions');\n\n for (let i = 0; i < this.PARTITION_COUNT; i++) {\n const ownerIndex = i % allMembers.length;\n const owner = allMembers[ownerIndex];\n \n const backups: string[] = [];\n if (allMembers.length > 1) {\n for (let b = 1; b <= this.BACKUP_COUNT; b++) {\n const backupIndex = (ownerIndex + b) % allMembers.length;\n backups.push(allMembers[backupIndex]);\n }\n }\n\n this.partitions.set(i, { owner, backups });\n }\n }\n}\n","import { EventEmitter } from 'events';\nimport { logger } from '../utils/logger';\n\nexport interface LockRequest {\n clientId: string;\n requestId: string;\n ttl: number;\n timestamp: number;\n}\n\nexport interface LockState {\n name: string;\n owner: string; // clientId\n fencingToken: number;\n expiry: number;\n queue: LockRequest[];\n}\n\nexport class LockManager extends EventEmitter {\n private locks: Map<string, LockState> = new Map();\n private checkInterval: NodeJS.Timeout;\n\n private static readonly MIN_TTL = 1000; // 1 second\n private static readonly MAX_TTL = 300000; // 5 minutes\n\n constructor() {\n super();\n this.checkInterval = setInterval(() => this.cleanupExpiredLocks(), 1000);\n }\n\n public stop() {\n clearInterval(this.checkInterval);\n }\n\n public acquire(name: string, clientId: string, requestId: string, ttl: number): { granted: boolean; fencingToken?: number; error?: string } {\n // Validate TTL\n const safeTtl = Math.max(LockManager.MIN_TTL, Math.min(ttl || LockManager.MIN_TTL, LockManager.MAX_TTL));\n\n let lock = this.locks.get(name);\n if (!lock) {\n lock = {\n name,\n owner: '',\n fencingToken: 0,\n expiry: 0,\n queue: []\n };\n this.locks.set(name, lock);\n }\n\n const now = Date.now();\n\n // If lock is free or expired\n if (!lock.owner || lock.expiry < now) {\n this.grantLock(lock, clientId, safeTtl);\n return { granted: true, fencingToken: lock.fencingToken };\n }\n\n // If already owned by same client, extend lease\n if (lock.owner === clientId) {\n lock.expiry = Math.max(lock.expiry, now + safeTtl);\n logger.info({ name, clientId, fencingToken: lock.fencingToken }, 'Lock lease extended');\n return { granted: true, fencingToken: lock.fencingToken };\n }\n\n // Queue request\n lock.queue.push({ clientId, requestId, ttl: safeTtl, timestamp: now });\n logger.info({ name, clientId, queueLength: lock.queue.length }, 'Lock queued');\n return { granted: false };\n }\n\n public release(name: string, clientId: string, fencingToken: number): boolean {\n const lock = this.locks.get(name);\n if (!lock) return false;\n\n if (lock.owner !== clientId) {\n logger.warn({ name, clientId, owner: lock.owner }, 'Release failed: Not owner');\n return false;\n }\n\n if (lock.fencingToken !== fencingToken) {\n logger.warn({ name, clientId, sentToken: fencingToken, actualToken: lock.fencingToken }, 'Release failed: Token mismatch');\n return false;\n }\n\n this.processNext(lock);\n return true;\n }\n\n public handleClientDisconnect(clientId: string) {\n for (const lock of this.locks.values()) {\n // 1. If client owns the lock, force release\n if (lock.owner === clientId) {\n logger.info({ name: lock.name, clientId }, 'Releasing lock due to disconnect');\n this.processNext(lock);\n } else {\n // 2. Remove from queue if present\n const initialLen = lock.queue.length;\n lock.queue = lock.queue.filter(req => req.clientId !== clientId);\n if (lock.queue.length < initialLen) {\n logger.info({ name: lock.name, clientId }, 'Removed from lock queue due to disconnect');\n }\n }\n }\n }\n\n private grantLock(lock: LockState, clientId: string, ttl: number) {\n lock.owner = clientId;\n lock.expiry = Date.now() + ttl;\n lock.fencingToken++;\n logger.info({ name: lock.name, clientId, fencingToken: lock.fencingToken }, 'Lock granted');\n }\n\n private processNext(lock: LockState) {\n const now = Date.now();\n \n // Reset owner\n lock.owner = '';\n lock.expiry = 0;\n\n // Process queue\n while (lock.queue.length > 0) {\n const next = lock.queue.shift()!;\n \n // Grant to next\n this.grantLock(lock, next.clientId, next.ttl);\n \n // Emit event so ServerCoordinator can notify the client\n this.emit('lockGranted', {\n clientId: next.clientId,\n requestId: next.requestId,\n name: lock.name,\n fencingToken: lock.fencingToken\n });\n \n return;\n }\n \n // No one waiting\n if (lock.queue.length === 0) {\n this.locks.delete(lock.name);\n }\n }\n\n private cleanupExpiredLocks() {\n const now = Date.now();\n // Use a copy of keys to avoid concurrent modification issues during iteration\n const lockNames = Array.from(this.locks.keys());\n \n for (const name of lockNames) {\n const lock = this.locks.get(name);\n if (!lock) continue;\n\n if (lock.owner && lock.expiry < now) {\n logger.info({ name: lock.name, owner: lock.owner }, 'Lock expired, processing next');\n this.processNext(lock);\n } else if (!lock.owner && lock.queue.length === 0) {\n // Cleanup empty orphaned locks\n this.locks.delete(name);\n }\n }\n }\n}\n\n","import { PermissionPolicy, Principal, PermissionType } from '@topgunbuild/core';\nimport { logger } from '../utils/logger';\n\nexport class SecurityManager {\n private policies: PermissionPolicy[] = [];\n\n constructor(policies: PermissionPolicy[] = []) {\n this.policies = policies;\n }\n\n public addPolicy(policy: PermissionPolicy) {\n this.policies.push(policy);\n }\n\n public checkPermission(principal: Principal, mapName: string, action: PermissionType): boolean {\n // 1. Superuser check (optional, but good practice)\n if (principal.roles.includes('ADMIN')) {\n return true;\n }\n\n // 2. System Map Protection\n if (mapName.startsWith('$sys/')) {\n logger.warn({ userId: principal.userId, mapName }, 'Access Denied: System Map requires ADMIN role');\n return false;\n }\n\n // 2. Iterate policies to find a match\n for (const policy of this.policies) {\n const hasRole = this.hasRole(principal, policy.role);\n const matchesMap = this.matchesMap(mapName, policy.mapNamePattern, principal);\n\n if (hasRole && matchesMap) {\n if (policy.actions.includes('ALL') || policy.actions.includes(action)) {\n return true;\n }\n } else {\n // Trace why it failed matching if needed (verbose)\n // logger.trace({ policy, hasRole, matchesMap, mapName, user: principal.userId }, 'Policy mismatch');\n }\n }\n\n logger.warn({\n userId: principal.userId,\n roles: principal.roles,\n mapName,\n action,\n policyCount: this.policies.length\n }, 'SecurityManager: Access Denied - No matching policy found');\n\n return false;\n }\n\n public filterObject(object: any, principal: Principal, mapName: string): any {\n if (!object || typeof object !== 'object') return object;\n if (principal.roles.includes('ADMIN')) return object;\n\n if (Array.isArray(object)) {\n return object.map(item => this.filterObject(item, principal, mapName));\n }\n\n let allowedFields: Set<string> | null = null;\n let accessGranted = false;\n\n for (const policy of this.policies) {\n if (this.hasRole(principal, policy.role) && this.matchesMap(mapName, policy.mapNamePattern, principal)) {\n if (policy.actions.includes('ALL') || policy.actions.includes('READ')) {\n accessGranted = true;\n\n // If any policy allows everything, return immediately\n if (!policy.allowedFields || policy.allowedFields.length === 0 || policy.allowedFields.includes('*')) {\n return object;\n }\n\n if (allowedFields === null) allowedFields = new Set();\n policy.allowedFields.forEach(f => allowedFields!.add(f));\n }\n }\n }\n\n if (!accessGranted) return null;\n if (allowedFields === null) return object; // Should have returned above, but as fallback\n\n const filtered: any = {};\n for (const key of Object.keys(object)) {\n if (allowedFields.has(key)) {\n filtered[key] = object[key];\n }\n }\n return filtered;\n }\n\n private hasRole(principal: Principal, role: string): boolean {\n return principal.roles.includes(role);\n }\n\n private matchesMap(mapName: string, pattern: string, principal?: Principal): boolean {\n // Dynamic substitution for {userId}\n let finalPattern = pattern;\n if (pattern.includes('{userId}') && principal) {\n finalPattern = pattern.replace('{userId}', principal.userId);\n }\n\n if (finalPattern === '*') return true;\n if (finalPattern === mapName) return true;\n\n if (finalPattern.endsWith('*')) {\n const prefix = finalPattern.slice(0, -1);\n return mapName.startsWith(prefix);\n }\n\n return false;\n }\n}\n","import { Registry, Gauge, Counter, collectDefaultMetrics } from 'prom-client';\n\nexport class MetricsService {\n public readonly registry: Registry;\n\n // Metrics\n private connectedClients: Gauge;\n private mapSizeItems: Gauge;\n private opsTotal: Counter;\n private memoryUsage: Gauge;\n private clusterMembers: Gauge;\n\n constructor() {\n this.registry = new Registry();\n\n // Enable default nodejs metrics (cpu, memory, etc.)\n collectDefaultMetrics({ register: this.registry, prefix: 'topgun_' });\n\n this.connectedClients = new Gauge({\n name: 'topgun_connected_clients',\n help: 'Number of currently connected clients',\n registers: [this.registry],\n });\n\n this.mapSizeItems = new Gauge({\n name: 'topgun_map_size_items',\n help: 'Number of items in a map',\n labelNames: ['map'],\n registers: [this.registry],\n });\n\n this.opsTotal = new Counter({\n name: 'topgun_ops_total',\n help: 'Total number of operations',\n labelNames: ['type', 'map'],\n registers: [this.registry],\n });\n\n this.memoryUsage = new Gauge({\n name: 'topgun_memory_usage_bytes',\n help: 'Current memory usage in bytes',\n registers: [this.registry],\n collect() {\n this.set(process.memoryUsage().heapUsed);\n }\n });\n\n this.clusterMembers = new Gauge({\n name: 'topgun_cluster_members',\n help: 'Number of active cluster members',\n registers: [this.registry],\n });\n }\n\n public destroy() {\n this.registry.clear();\n }\n\n public setConnectedClients(count: number) {\n this.connectedClients.set(count);\n }\n\n public setMapSize(mapName: string, size: number) {\n this.mapSizeItems.set({ map: mapName }, size);\n }\n\n public incOp(type: 'PUT' | 'GET' | 'DELETE' | 'SUBSCRIBE', mapName: string) {\n this.opsTotal.inc({ type, map: mapName });\n }\n\n public setClusterMembers(count: number) {\n this.clusterMembers.set(count);\n }\n\n public async getMetrics(): Promise<string> {\n return this.registry.metrics();\n }\n\n public async getMetricsJson(): Promise<Record<string, any>> {\n const metrics = await this.registry.getMetricsAsJSON();\n // Flatten or simplify for dashboard if needed, but raw JSON is fine for now\n const result: Record<string, any> = {};\n for (const metric of metrics) {\n // Simple flattening: name -> value (if single value)\n // metric.type is an enum/number in some versions or string in others. \n // To be safe and avoid type errors, we just check values length.\n if (metric.values.length === 1) {\n result[metric.name] = metric.values[0].value;\n } else {\n // Complex metrics\n result[metric.name] = metric.values;\n }\n }\n return result;\n }\n\n public getContentType(): string {\n return this.registry.contentType;\n }\n}\n\n","import { LWWMap, LWWRecord } from '@topgunbuild/core';\nimport { ClusterManager } from '../cluster/ClusterManager';\nimport { MetricsService } from '../monitoring/MetricsService';\nimport { logger } from '../utils/logger';\n\nexport class SystemManager {\n private cluster: ClusterManager;\n private metrics: MetricsService;\n private getMap: (name: string) => LWWMap<string, any>;\n\n private statsInterval?: NodeJS.Timeout;\n\n constructor(\n cluster: ClusterManager,\n metrics: MetricsService,\n getMap: (name: string) => LWWMap<string, any>\n ) {\n this.cluster = cluster;\n this.metrics = metrics;\n this.getMap = getMap;\n }\n\n public start() {\n this.setupClusterMap();\n this.setupStatsMap();\n this.setupMapsMap();\n\n // Update stats every 5 seconds\n this.statsInterval = setInterval(() => this.updateStats(), 5000);\n\n // Listen for cluster events\n this.cluster.on('memberJoined', () => this.updateClusterMap());\n this.cluster.on('memberLeft', () => this.updateClusterMap());\n\n // Initial updates\n this.updateClusterMap();\n this.updateStats();\n }\n\n public stop() {\n if (this.statsInterval) {\n clearInterval(this.statsInterval);\n }\n }\n\n public notifyMapCreated(mapName: string) {\n if (mapName.startsWith('$sys/')) return; // Don't track system maps\n this.updateMapsMap(mapName);\n }\n\n private setupClusterMap() {\n // Ensure map exists\n this.getMap('$sys/cluster');\n }\n\n private setupStatsMap() {\n this.getMap('$sys/stats');\n }\n\n private setupMapsMap() {\n this.getMap('$sys/maps');\n }\n\n private updateClusterMap() {\n try {\n const map = this.getMap('$sys/cluster');\n const members = this.cluster.getMembers();\n\n // We can't easily \"remove\" missing members without iterating the whole map\n // For now, we just put current members.\n // A proper sync would require diffing.\n\n // In a real implementation, we might want to store more info than just ID.\n // But ClusterManager currently only gives us IDs easily or we have to look them up.\n // Let's iterate members map from ClusterManager if possible, or just use IDs.\n\n // Accessing private members map via any cast for now or just using IDs\n // The ClusterManager.getMembers() returns IDs.\n\n for (const memberId of members) {\n const isLocal = this.cluster.isLocal(memberId);\n map.set(memberId, {\n id: memberId,\n status: 'UP',\n isLocal,\n lastUpdated: Date.now()\n });\n }\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/cluster');\n }\n }\n\n private async updateStats() {\n try {\n const map = this.getMap('$sys/stats');\n const metrics = await this.metrics.getMetricsJson(); // We need to add getMetricsJson to MetricsService\n\n map.set(this.cluster.config.nodeId, {\n ...metrics,\n timestamp: Date.now()\n });\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/stats');\n }\n }\n\n private updateMapsMap(mapName: string) {\n try {\n const map = this.getMap('$sys/maps');\n map.set(mapName, {\n name: mapName,\n createdAt: Date.now()\n });\n } catch (err) {\n logger.error({ err }, 'Failed to update $sys/maps');\n }\n }\n}\n","import { Pool, PoolConfig } from 'pg';\nimport { LWWRecord } from '@topgunbuild/core';\nimport { IServerStorage, StorageValue } from './IServerStorage';\n\nexport interface PostgresAdapterOptions {\n tableName?: string;\n}\n\nconst DEFAULT_TABLE_NAME = 'topgun_maps';\nconst TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction validateTableName(name: string): void {\n if (!TABLE_NAME_REGEX.test(name)) {\n throw new Error(\n `Invalid table name \"${name}\". Table name must start with a letter or underscore and contain only alphanumeric characters and underscores.`\n );\n }\n}\n\nexport class PostgresAdapter implements IServerStorage {\n private pool: Pool;\n private tableName: string;\n\n constructor(configOrPool: PoolConfig | Pool, options?: PostgresAdapterOptions) {\n if (configOrPool instanceof Pool || (configOrPool as any).connect) {\n this.pool = configOrPool as Pool;\n } else {\n this.pool = new Pool(configOrPool as PoolConfig);\n }\n\n const tableName = options?.tableName ?? DEFAULT_TABLE_NAME;\n validateTableName(tableName);\n this.tableName = tableName;\n }\n\n async initialize(): Promise<void> {\n const client = await this.pool.connect();\n try {\n // Create a generic table for storing key-value pairs per map\n // schema: map_name (text), key (text), value (jsonb), timestamp_millis (bigint), timestamp_counter (int), node_id (text), is_deleted (boolean)\n await client.query(`\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n map_name TEXT NOT NULL,\n key TEXT NOT NULL,\n value JSONB,\n ts_millis BIGINT NOT NULL,\n ts_counter INTEGER NOT NULL,\n ts_node_id TEXT NOT NULL,\n is_deleted BOOLEAN DEFAULT FALSE,\n PRIMARY KEY (map_name, key)\n );\n `);\n } finally {\n client.release();\n }\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n async load(mapName: string, key: string): Promise<StorageValue<any> | undefined> {\n const res = await this.pool.query(\n `SELECT value, ts_millis, ts_counter, ts_node_id, is_deleted \n FROM ${this.tableName} \n WHERE map_name = $1 AND key = $2`,\n [mapName, key]\n );\n\n if (res.rows.length === 0) return undefined;\n\n const row = res.rows[0];\n return this.mapRowToRecord(row);\n }\n\n async loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>> {\n const result = new Map<string, StorageValue<any>>();\n if (keys.length === 0) return result;\n\n const res = await this.pool.query(\n `SELECT key, value, ts_millis, ts_counter, ts_node_id, is_deleted \n FROM ${this.tableName} \n WHERE map_name = $1 AND key = ANY($2)`,\n [mapName, keys]\n );\n\n for (const row of res.rows) {\n result.set(row.key, this.mapRowToRecord(row));\n }\n\n return result;\n }\n\n async loadAllKeys(mapName: string): Promise<string[]> {\n const res = await this.pool.query(\n `SELECT key FROM ${this.tableName} WHERE map_name = $1`,\n [mapName]\n );\n return res.rows.map(row => row.key);\n }\n\n async store(mapName: string, key: string, record: StorageValue<any>): Promise<void> {\n let value: any;\n let tsMillis: number;\n let tsCounter: number;\n let tsNodeId: string;\n let isDeleted: boolean;\n\n if (this.isORMapValue(record)) {\n // Store ORMap data\n // We use a special marker in ts_node_id to distinguish ORMap data from LWW data\n value = record;\n tsMillis = 0;\n tsCounter = 0;\n tsNodeId = '__ORMAP__';\n isDeleted = false;\n } else {\n // LWWRecord\n const lww = record as LWWRecord<any>;\n value = lww.value;\n tsMillis = lww.timestamp.millis;\n tsCounter = lww.timestamp.counter;\n tsNodeId = lww.timestamp.nodeId;\n isDeleted = lww.value === null;\n }\n\n await this.pool.query(\n `INSERT INTO ${this.tableName} (map_name, key, value, ts_millis, ts_counter, ts_node_id, is_deleted)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (map_name, key) DO UPDATE SET\n value = EXCLUDED.value,\n ts_millis = EXCLUDED.ts_millis,\n ts_counter = EXCLUDED.ts_counter,\n ts_node_id = EXCLUDED.ts_node_id,\n is_deleted = EXCLUDED.is_deleted`,\n [\n mapName,\n key,\n JSON.stringify(value),\n tsMillis,\n tsCounter,\n tsNodeId,\n isDeleted\n ]\n );\n }\n\n async storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n // Note: For high performance, this should use UNNEST or multi-row INSERT.\n // Keeping loop for simplicity in MVP alignment.\n for (const [key, record] of records) {\n await this.store(mapName, key, record); \n }\n await client.query('COMMIT');\n } catch (e) {\n await client.query('ROLLBACK');\n throw e;\n } finally {\n client.release();\n }\n }\n\n async delete(mapName: string, key: string): Promise<void> {\n await this.pool.query(`DELETE FROM ${this.tableName} WHERE map_name = $1 AND key = $2`, [mapName, key]);\n }\n\n async deleteAll(mapName: string, keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.pool.query(\n `DELETE FROM ${this.tableName} WHERE map_name = $1 AND key = ANY($2)`, \n [mapName, keys]\n );\n }\n\n private mapRowToRecord(row: any): StorageValue<any> {\n if (row.ts_node_id === '__ORMAP__') {\n // It's an ORMap value (ORMapValue or ORMapTombstones)\n return row.value as StorageValue<any>;\n }\n\n // It's LWWRecord\n return {\n value: row.is_deleted ? null : row.value,\n timestamp: {\n millis: Number(row.ts_millis),\n counter: row.ts_counter,\n nodeId: row.ts_node_id\n }\n };\n }\n\n private isORMapValue(record: any): boolean {\n return (record && typeof record === 'object' && (record.type === 'OR' || record.type === 'OR_TOMBSTONES'));\n }\n}\n","import { IServerStorage, StorageValue } from './IServerStorage';\n\n/**\n * In-memory implementation of IServerStorage.\n * Useful for development, testing, and demos without requiring a database.\n *\n * Note: Data is lost when the server restarts.\n */\nexport class MemoryServerAdapter implements IServerStorage {\n // Map<mapName, Map<key, value>>\n private storage = new Map<string, Map<string, StorageValue<any>>>();\n\n async initialize(): Promise<void> {\n // No-op for in-memory storage\n console.log('[MemoryServerAdapter] Initialized in-memory storage');\n }\n\n async close(): Promise<void> {\n this.storage.clear();\n console.log('[MemoryServerAdapter] Storage cleared and closed');\n }\n\n private getMap(mapName: string): Map<string, StorageValue<any>> {\n let map = this.storage.get(mapName);\n if (!map) {\n map = new Map();\n this.storage.set(mapName, map);\n }\n return map;\n }\n\n async load(mapName: string, key: string): Promise<StorageValue<any> | undefined> {\n return this.getMap(mapName).get(key);\n }\n\n async loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>> {\n const map = this.getMap(mapName);\n const result = new Map<string, StorageValue<any>>();\n for (const key of keys) {\n const value = map.get(key);\n if (value !== undefined) {\n result.set(key, value);\n }\n }\n return result;\n }\n\n async loadAllKeys(mapName: string): Promise<string[]> {\n return Array.from(this.getMap(mapName).keys());\n }\n\n async store(mapName: string, key: string, record: StorageValue<any>): Promise<void> {\n this.getMap(mapName).set(key, record);\n }\n\n async storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void> {\n const map = this.getMap(mapName);\n for (const [key, value] of records) {\n map.set(key, value);\n }\n }\n\n async delete(mapName: string, key: string): Promise<void> {\n this.getMap(mapName).delete(key);\n }\n\n async deleteAll(mapName: string, keys: string[]): Promise<void> {\n const map = this.getMap(mapName);\n for (const key of keys) {\n map.delete(key);\n }\n }\n}\n","import { IInterceptor, ServerOp, OpContext } from './IInterceptor';\nimport { logger } from '../utils/logger';\n\nexport class TimestampInterceptor implements IInterceptor {\n name = 'TimestampInterceptor';\n\n async onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp> {\n // Only apply to PUT operations with LWW records\n if (op.opType === 'PUT' && op.record && op.record.value) {\n // Modifying the value to include server timestamp\n // This assumes value is an object where we can add properties\n if (typeof op.record.value === 'object' && op.record.value !== null && !Array.isArray(op.record.value)) {\n const newValue = {\n ...op.record.value,\n _serverTimestamp: Date.now()\n };\n logger.debug({ key: op.key, mapName: op.mapName, interceptor: this.name }, 'Added timestamp');\n return {\n ...op,\n record: {\n ...op.record,\n value: newValue\n }\n };\n }\n }\n return op;\n }\n}\n","import { IInterceptor, ServerOp, OpContext } from './IInterceptor';\nimport { logger } from '../utils/logger';\n\ninterface RateLimitConfig {\n windowMs: number;\n maxOps: number;\n}\n\ninterface ClientLimit {\n count: number;\n resetTime: number;\n}\n\nexport class RateLimitInterceptor implements IInterceptor {\n name = 'RateLimitInterceptor';\n \n private limits = new Map<string, ClientLimit>();\n private config: RateLimitConfig;\n\n constructor(config: RateLimitConfig = { windowMs: 1000, maxOps: 50 }) {\n this.config = config;\n }\n\n async onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp | null> {\n // Rate limit based on clientId\n const clientId = context.clientId;\n const now = Date.now();\n \n let limit = this.limits.get(clientId);\n \n if (!limit || now > limit.resetTime) {\n limit = {\n count: 0,\n resetTime: now + this.config.windowMs\n };\n this.limits.set(clientId, limit);\n }\n\n limit.count++;\n\n if (limit.count > this.config.maxOps) {\n logger.warn({ clientId, opId: op.id, count: limit.count }, 'Rate limit exceeded');\n throw new Error('Rate limit exceeded');\n }\n\n return op;\n }\n\n // Cleanup old entries periodically? \n // For now we rely on resetTime check, but map grows. \n // Simple cleanup on reset logic:\n // In a real system, we'd use Redis or a proper cache with TTL.\n // Here we can just prune occasionally or relying on connection disconnect?\n \n // Optimization: Cleanup on disconnect\n async onDisconnect(context: any) {\n this.limits.delete(context.clientId);\n }\n}\n\n"],"mappings":"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","names":["readFileSync","WebSocketServer","WebSocket","LWWMap","ORMap","serialize","EventEmitter","EventEmitter","WebSocketServer","WebSocket","serialize","LWWMap","ORMap","readFileSync"]}