@scpxl/nodejs-framework 1.0.30 → 1.0.42

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.
Files changed (56) hide show
  1. package/README.md +1 -0
  2. package/dist/application/base-application.d.ts +7 -7
  3. package/dist/application/base-application.d.ts.map +1 -1
  4. package/dist/application/base-application.interface.d.ts +1 -1
  5. package/dist/application/base-application.interface.d.ts.map +1 -1
  6. package/dist/application/base-application.js +122 -14
  7. package/dist/application/base-application.js.map +2 -2
  8. package/dist/application/web-application.d.ts.map +1 -1
  9. package/dist/application/web-application.js +2 -4
  10. package/dist/application/web-application.js.map +2 -2
  11. package/dist/cache/manager.d.ts +1 -0
  12. package/dist/cache/manager.d.ts.map +1 -1
  13. package/dist/cache/manager.js +11 -2
  14. package/dist/cache/manager.js.map +2 -2
  15. package/dist/cli/index.js +24 -10
  16. package/dist/cli/index.js.map +2 -2
  17. package/dist/config/schema.d.ts +2 -63
  18. package/dist/config/schema.d.ts.map +1 -1
  19. package/dist/config/schema.js +1 -7
  20. package/dist/config/schema.js.map +2 -2
  21. package/dist/event/manager.js +2 -2
  22. package/dist/event/manager.js.map +2 -2
  23. package/dist/logger/logger.js +0 -1
  24. package/dist/logger/logger.js.map +2 -2
  25. package/dist/queue/manager.d.ts.map +1 -1
  26. package/dist/queue/manager.js +19 -7
  27. package/dist/queue/manager.js.map +2 -2
  28. package/dist/redis/instance.d.ts.map +1 -1
  29. package/dist/redis/instance.js +9 -1
  30. package/dist/redis/instance.js.map +2 -2
  31. package/dist/redis/manager.d.ts.map +1 -1
  32. package/dist/redis/manager.js +26 -16
  33. package/dist/redis/manager.js.map +3 -3
  34. package/dist/webserver/controller/entity.js +1 -1
  35. package/dist/webserver/controller/entity.js.map +2 -2
  36. package/dist/webserver/controller/health.js.map +1 -1
  37. package/dist/webserver/util.d.ts +1 -1
  38. package/dist/webserver/util.d.ts.map +1 -1
  39. package/dist/webserver/util.js +5 -23
  40. package/dist/webserver/util.js.map +2 -2
  41. package/dist/webserver/webserver.d.ts +2 -13
  42. package/dist/webserver/webserver.d.ts.map +1 -1
  43. package/dist/webserver/webserver.interface.d.ts +1 -21
  44. package/dist/webserver/webserver.interface.d.ts.map +1 -1
  45. package/dist/webserver/webserver.interface.js.map +1 -1
  46. package/dist/webserver/webserver.js +34 -36
  47. package/dist/webserver/webserver.js.map +2 -2
  48. package/dist/websocket/utils.d.ts.map +1 -1
  49. package/dist/websocket/utils.js +5 -1
  50. package/dist/websocket/utils.js.map +2 -2
  51. package/dist/websocket/websocket-base.d.ts.map +1 -1
  52. package/dist/websocket/websocket-base.js +9 -1
  53. package/dist/websocket/websocket-base.js.map +2 -2
  54. package/dist/websocket/websocket-server.d.ts.map +1 -1
  55. package/dist/websocket/websocket-server.js.map +2 -2
  56. package/package.json +5 -6
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/websocket/websocket-server.ts"],
4
- "sourcesContent": ["import { type RawData, WebSocketServer as WS, WebSocket } from 'ws';\nimport {\n type WebSocketOptions,\n WebSocketRedisSubscriberEvent,\n type WebSocketRoute,\n type WebSocketSubscriberDefinition,\n type WebSocketSubscriberHandlerContext,\n type WebSocketSubscriberMatcher,\n type WebSocketType,\n} from './websocket.interface.js';\nimport type RedisInstance from '../redis/instance.js';\nimport type QueueManager from '../queue/manager.js';\nimport type DatabaseInstance from '../database/instance.js';\nimport type { WebSocketServerProps } from './websocket-server.interface.js';\nimport WebSocketClientManager from './websocket-client-manager.js';\nimport { generateClientId, log } from './utils.js';\nimport WebSocketBase from './websocket-base.js';\nimport { Logger } from '../logger/index.js';\nimport path from 'path';\nimport { type WebApplicationConfig, baseDir } from '../index.js';\nimport WebSocketRoomManager from './websocket-room-manager.js';\nimport logger from '../logger/logger.js';\nimport type { FastifyInstance } from 'fastify';\nimport { WebSocketAuthService } from './websocket-auth.js';\nimport { File, Loader } from '../util/index.js';\n\nexport default class WebSocketServer extends WebSocketBase {\n protected defaultRoutes: WebSocketRoute[] = [\n {\n type: 'system',\n action: 'joinRoom',\n controllerName: 'system',\n },\n {\n type: 'system',\n action: 'leaveRoom',\n controllerName: 'system',\n },\n ];\n\n private server?: WS;\n\n private abortController = new AbortController();\n private workerId: number | null;\n private uniqueInstanceId: string;\n private applicationConfig: WebApplicationConfig;\n private options: WebSocketOptions;\n public clientManager = new WebSocketClientManager();\n private roomManager = new WebSocketRoomManager({\n clientManager: this.clientManager,\n });\n private authService: WebSocketAuthService;\n\n public get rooms() {\n return this.roomManager.rooms;\n }\n private redisInstance: RedisInstance;\n private queueManager: QueueManager;\n private databaseInstance: DatabaseInstance;\n private subscriberHandlersByChannel: Map<string, WebSocketSubscriberDefinition[]> = new Map();\n private subscriberMatcherHandlers: WebSocketSubscriberDefinition[] = [];\n private wildcardSubscriberHandlers: WebSocketSubscriberDefinition[] = [];\n\n /** Redis subscriber events */\n private redisSubscriberEvents: string[] = [\n WebSocketRedisSubscriberEvent.ClientConnected,\n WebSocketRedisSubscriberEvent.ClientJoinedRoom,\n WebSocketRedisSubscriberEvent.ClientLeftRoom,\n WebSocketRedisSubscriberEvent.ClientDisconnected,\n WebSocketRedisSubscriberEvent.DisconnectClient,\n WebSocketRedisSubscriberEvent.SendMessage,\n WebSocketRedisSubscriberEvent.SendMessageToAll,\n WebSocketRedisSubscriberEvent.MessageError,\n WebSocketRedisSubscriberEvent.QueueJobCompleted,\n WebSocketRedisSubscriberEvent.QueueJobError,\n WebSocketRedisSubscriberEvent.Custom,\n ];\n\n constructor(props: WebSocketServerProps) {\n super();\n\n this.uniqueInstanceId = props.uniqueInstanceId;\n this.applicationConfig = props.applicationConfig;\n this.options = props.options;\n this.redisInstance = props.redisInstance;\n this.queueManager = props.queueManager;\n this.databaseInstance = props.databaseInstance;\n this.routes = props.routes;\n this.workerId = props.workerId;\n this.authService = new WebSocketAuthService(props.applicationConfig);\n }\n\n public get type(): WebSocketType {\n return 'server';\n }\n\n private async validateWebSocketAuth(\n url: string,\n ): Promise<{ userId: number; payload: Record<string, unknown> } | null> {\n return this.authService.validateAuth(url);\n }\n\n public async load(): Promise<void> {\n const libraryControllersDirectory = path.join(baseDir, 'websocket', 'controllers', 'server');\n\n // Configure default routes\n await this.configureRoutes(this.defaultRoutes, libraryControllersDirectory);\n\n // Configure custom routes\n await this.configureRoutes(this.routes, this.options.controllersDirectory);\n\n await this.loadSubscriberHandlers();\n }\n\n private async loadSubscriberHandlers(): Promise<void> {\n this.subscriberHandlersByChannel.clear();\n this.subscriberMatcherHandlers = [];\n this.wildcardSubscriberHandlers = [];\n\n const config = this.options.subscriberHandlers;\n\n if (!config) {\n return;\n }\n\n const definitions: WebSocketSubscriberDefinition[] = [];\n\n if (config.directory) {\n const directoryExists = await File.pathExists(config.directory);\n\n if (!directoryExists) {\n logger.warn('WebSocket subscriber handlers directory not found', {\n Directory: config.directory,\n });\n } else {\n const modules = await Loader.loadModulesInDirectory<unknown>({\n directory: config.directory,\n extensions: ['.ts', '.js'],\n });\n\n for (const [moduleName, moduleExport] of Object.entries(modules)) {\n definitions.push(\n ...this.normalizeSubscriberExport(moduleExport, {\n source: 'file',\n moduleName,\n directory: config.directory,\n }),\n );\n }\n }\n }\n\n if (Array.isArray(config.handlers)) {\n for (const handler of config.handlers) {\n definitions.push(\n ...this.normalizeSubscriberExport(handler, {\n source: 'inline',\n }),\n );\n }\n }\n\n for (const definition of definitions) {\n this.registerSubscriberDefinition(definition);\n }\n\n if (definitions.length > 0) {\n logger.info('WebSocket subscriber handlers loaded', {\n Channels: Array.from(this.subscriberHandlersByChannel.keys()),\n Matchers: this.subscriberMatcherHandlers.length,\n Wildcards: this.wildcardSubscriberHandlers.length,\n });\n }\n }\n\n private normalizeSubscriberExport(\n value: unknown,\n metadata: { source: 'file' | 'inline'; moduleName?: string; directory?: string },\n ): WebSocketSubscriberDefinition[] {\n if (!value) {\n return [];\n }\n\n if (Array.isArray(value)) {\n return value.flatMap(entry => this.normalizeSubscriberExport(entry, metadata));\n }\n\n if (typeof value === 'function') {\n const moduleChannel = metadata.moduleName ?? value.name ?? 'anonymous';\n\n return [\n {\n name: metadata.moduleName ?? value.name,\n channels: [moduleChannel],\n handle: async context => {\n await value(context);\n },\n },\n ];\n }\n\n if (typeof value === 'object') {\n const definition = this.normalizeSubscriberObject(value as Record<string, unknown>, metadata);\n if (definition) {\n return [definition];\n }\n\n return Object.values(value as Record<string, unknown>).flatMap(entry =>\n this.normalizeSubscriberExport(entry, metadata),\n );\n }\n\n return [];\n }\n\n private normalizeSubscriberObject(\n value: Record<string, unknown>,\n metadata: { source: 'file' | 'inline'; moduleName?: string },\n ): WebSocketSubscriberDefinition | null {\n const handle = value.handle;\n\n if (typeof handle !== 'function') {\n return null;\n }\n\n let channelSource: unknown;\n if (Object.prototype.hasOwnProperty.call(value, 'channel')) {\n channelSource = value.channel;\n } else if (Object.prototype.hasOwnProperty.call(value, 'channels')) {\n channelSource = value.channels;\n }\n\n const channels = this.normalizeChannels(channelSource);\n\n let matcherSource: unknown;\n if (Object.prototype.hasOwnProperty.call(value, 'match')) {\n matcherSource = value.match;\n } else if (Object.prototype.hasOwnProperty.call(value, 'matcher')) {\n matcherSource = value.matcher;\n } else if (Object.prototype.hasOwnProperty.call(value, 'matchers')) {\n matcherSource = value.matchers;\n }\n\n const matchers = this.normalizeMatchers(matcherSource);\n\n if (channels.length === 0 && matchers.length === 0) {\n logger.warn('Skipping WebSocket subscriber handler without channels or matchers', {\n Source: metadata.source,\n Module: metadata.moduleName,\n });\n\n return null;\n }\n\n const definition: WebSocketSubscriberDefinition = {\n name: typeof value.name === 'string' ? value.name : metadata.moduleName,\n description: typeof value.description === 'string' ? value.description : undefined,\n priority: typeof value.priority === 'number' ? value.priority : undefined,\n channels,\n matchers,\n handle: handle as (context: WebSocketSubscriberHandlerContext) => unknown | Promise<unknown>,\n };\n\n return definition;\n }\n\n private normalizeChannels(input: unknown): string[] {\n if (typeof input === 'string') {\n const trimmed = input.trim();\n return trimmed.length > 0 ? [trimmed] : [];\n }\n\n if (Array.isArray(input)) {\n return input\n .filter((channel): channel is string => typeof channel === 'string')\n .map(channel => channel.trim())\n .filter(channel => channel.length > 0);\n }\n\n return [];\n }\n\n private normalizeMatchers(input: unknown): WebSocketSubscriberMatcher[] {\n if (!input) {\n return [];\n }\n\n const matchers: WebSocketSubscriberMatcher[] = [];\n const addMatcher = (matcher: unknown) => {\n if (matcher instanceof RegExp || typeof matcher === 'function') {\n matchers.push(matcher as WebSocketSubscriberMatcher);\n } else if (typeof matcher === 'string') {\n const trimmed = matcher.trim();\n if (trimmed.length > 0) {\n matchers.push(trimmed as WebSocketSubscriberMatcher);\n }\n }\n };\n\n if (Array.isArray(input)) {\n input.forEach(addMatcher);\n } else {\n addMatcher(input);\n }\n\n return matchers;\n }\n\n private registerSubscriberDefinition(definition: WebSocketSubscriberDefinition): void {\n const normalizedPriority = definition.priority ?? 0;\n const channels = definition.channels?.map(channel => channel.trim()).filter(Boolean) ?? [];\n\n const normalizedDefinition: WebSocketSubscriberDefinition = {\n ...definition,\n priority: normalizedPriority,\n channels,\n matchers: definition.matchers ?? [],\n };\n\n if (channels.length === 0 && normalizedDefinition.matchers?.length === 0) {\n logger.warn('Skipping WebSocket subscriber handler registration due to missing filters', {\n Handler: definition.name ?? '(anonymous)',\n });\n\n return;\n }\n\n if (channels.length > 0) {\n for (const channel of channels) {\n if (channel === '*') {\n this.wildcardSubscriberHandlers.push(normalizedDefinition);\n continue;\n }\n\n const channelHandlers = this.subscriberHandlersByChannel.get(channel) ?? [];\n channelHandlers.push(normalizedDefinition);\n this.subscriberHandlersByChannel.set(channel, channelHandlers);\n }\n }\n\n if (normalizedDefinition.matchers && normalizedDefinition.matchers.length > 0) {\n this.subscriberMatcherHandlers.push(normalizedDefinition);\n }\n }\n\n private doesDefinitionMatch(\n definition: WebSocketSubscriberDefinition,\n context: WebSocketSubscriberHandlerContext,\n ): boolean {\n if (!definition.matchers || definition.matchers.length === 0) {\n return false;\n }\n\n return definition.matchers.some(matcher => this.evaluateMatcher(matcher, context));\n }\n\n private evaluateMatcher(matcher: WebSocketSubscriberMatcher, context: WebSocketSubscriberHandlerContext): boolean {\n if (typeof matcher === 'string') {\n if (matcher === '*') {\n return true;\n }\n\n return matcher === context.channel;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(context.channel);\n }\n\n try {\n return Boolean(matcher(context));\n } catch (error) {\n logger.error({\n error,\n message: 'WebSocket subscriber matcher threw an error',\n meta: {\n Channel: context.channel,\n Matcher: String(matcher),\n },\n });\n\n return false;\n }\n }\n\n private async executeSubscriberHandlers(channel: string, message: any): Promise<void> {\n if (\n this.subscriberHandlersByChannel.size === 0 &&\n this.subscriberMatcherHandlers.length === 0 &&\n this.wildcardSubscriberHandlers.length === 0\n ) {\n return;\n }\n\n const context: WebSocketSubscriberHandlerContext = {\n channel,\n message,\n webSocketServer: this,\n databaseInstance: this.databaseInstance,\n redisInstance: this.redisInstance,\n queueManager: this.queueManager,\n };\n\n const candidates = new Set<WebSocketSubscriberDefinition>();\n\n const channelHandlers = this.subscriberHandlersByChannel.get(channel);\n\n if (channelHandlers) {\n channelHandlers.forEach(handler => candidates.add(handler));\n }\n\n this.wildcardSubscriberHandlers.forEach(handler => candidates.add(handler));\n\n for (const definition of this.subscriberMatcherHandlers) {\n if (this.doesDefinitionMatch(definition, context)) {\n candidates.add(definition);\n }\n }\n\n if (candidates.size === 0) {\n return;\n }\n\n const orderedHandlers = Array.from(candidates).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n\n for (const handler of orderedHandlers) {\n try {\n await handler.handle(context);\n } catch (error) {\n logger.error({\n error,\n message: 'WebSocket subscriber handler failed',\n meta: {\n Handler: handler.name ?? '(anonymous)',\n Channel: channel,\n },\n });\n }\n }\n }\n\n public async start({ fastifyServer }: { fastifyServer: FastifyInstance }): Promise<{ server: WS }> {\n return new Promise(resolve => {\n const server = new WS({\n noServer: true, // We're handling the server externally\n });\n\n this.server = server;\n\n // Ensure this is called after the server has been properly set up\n this.handleServerStart();\n\n fastifyServer.server.on('upgrade', async (request, socket, head) => {\n if (request.url?.startsWith('/ws')) {\n try {\n // Validate authentication token if provided\n const authenticatedUser = await this.validateWebSocketAuth(request.url);\n\n server.handleUpgrade(request, socket, head, ws => {\n server.emit('connection', ws, request, authenticatedUser);\n });\n } catch (error: any) {\n log('WebSocket authentication failed', { error: error.message });\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n }\n } else {\n socket.destroy();\n }\n });\n\n server.on('error', this.handleServerError);\n\n server.on(\n 'connection',\n (ws: WebSocket, request: any, authenticatedUser: { userId: number; payload: any } | null) => {\n this.handleServerClientConnection(ws, authenticatedUser);\n },\n );\n\n // Resolve the promise with the server instance\n resolve({ server });\n });\n }\n\n public async stop(): Promise<void> {\n // Abort all ongoing operations (intervals, etc.)\n this.abortController.abort();\n\n // Clean up Redis subscriber listeners\n this.redisInstance.subscriberClient?.removeListener('message', this.handleSubscriberMessage);\n\n // Unsubscribe from all Redis events\n this.redisSubscriberEvents.forEach(subscriberEventName => {\n this.redisInstance.subscriberClient?.unsubscribe(subscriberEventName);\n });\n\n // Close all client connections and clean up\n if (this.server) {\n this.server.clients.forEach(client => {\n client.removeAllListeners();\n client.close();\n });\n\n this.server.removeAllListeners();\n this.server.close();\n }\n\n // Clean up client manager and room manager\n this.clientManager.cleanup();\n this.roomManager.cleanup();\n\n // Reset managers\n this.clientManager = new WebSocketClientManager();\n this.roomManager = new WebSocketRoomManager({\n clientManager: this.clientManager,\n });\n\n // Create new AbortController for potential restart\n this.abortController = new AbortController();\n\n log('Server stopped');\n }\n\n protected getControllerDependencies(): {\n webSocketServer: WebSocketServer;\n redisInstance: RedisInstance;\n queueManager: QueueManager;\n databaseInstance: DatabaseInstance;\n } {\n return {\n webSocketServer: this,\n redisInstance: this.redisInstance,\n queueManager: this.queueManager,\n databaseInstance: this.databaseInstance,\n };\n }\n\n protected shouldPrintRoutes(): boolean {\n return this.options.debug?.printRoutes ?? false;\n }\n\n private handleServerStart = (): void => {\n if (!this.server) {\n throw new Error('WebSocket server not started');\n }\n\n if (this.options.disconnectInactiveClients?.enabled && this.options.disconnectInactiveClients.intervalCheckTime) {\n // Note: setInterval with signal option requires Node.js 15+\n // TypeScript types may not reflect this, so we use type assertion\n (setInterval as (fn: () => void, ms: number, options?: { signal: AbortSignal }) => NodeJS.Timeout)(\n () => this.checkInactiveClients(),\n this.options.disconnectInactiveClients.intervalCheckTime,\n { signal: this.abortController.signal },\n );\n }\n\n // Go through each event and subscribe to it\n this.redisSubscriberEvents.forEach(subscriberEventName => {\n // Subscribe to event\n this.redisInstance.subscriberClient?.subscribe(subscriberEventName);\n });\n\n // Handle subscriber message\n this.redisInstance.subscriberClient.on('message', this.handleSubscriberMessage);\n\n log('Server started', {\n Host: this.options.host,\n URL: this.options.url,\n });\n\n if (this.options.events?.onServerStarted) {\n this.options.events.onServerStarted({\n webSocketServer: this.server,\n });\n }\n };\n\n /**\n * Handle subscriber message.\n */\n private handleSubscriberMessage = async (channel: string, message: string): Promise<void> => {\n let parsedMessage: { [key: string]: any };\n\n try {\n parsedMessage = JSON.parse(message);\n } catch (error) {\n log('Failed to parse subscriber message', {\n Channel: channel,\n Message: message,\n Error: error,\n });\n\n return;\n }\n\n const includeSender = parsedMessage.includeSender === true;\n\n const isSameWorker = parsedMessage.workerId === this.workerId;\n\n // Check if message is from the same worker\n if (includeSender !== true && isSameWorker) {\n // Ignore the message if it's from the same worker\n return;\n }\n\n log('Incoming subscriber message', {\n Channel: channel,\n // 'Run Same Worker': parsedMessage.includeSender ? 'Yes' : 'No',\n 'Client ID': parsedMessage.clientId ?? '-',\n });\n\n switch (channel) {\n case WebSocketRedisSubscriberEvent.ClientConnected: {\n this.onClientConnect({\n clientId: parsedMessage.clientId,\n lastActivity: parsedMessage.lastActivity,\n user: parsedMessage.user,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientDisconnected: {\n this.onClientDisconnect({\n clientId: parsedMessage.clientId,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.DisconnectClient: {\n const clientToDisconnect = this.clientManager.getClient({\n clientId: parsedMessage.clientId,\n // requireWs: true,\n });\n\n if (clientToDisconnect) {\n this.clientManager.disconnectClient({\n clientId: parsedMessage.clientId,\n });\n\n // Remove client from rooms\n this.roomManager.removeClientFromAllRooms({\n clientId: parsedMessage.clientId,\n });\n }\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientJoinedRoom: {\n this.onJoinRoom({\n clientId: parsedMessage.clientId,\n roomName: parsedMessage.roomName,\n userData: parsedMessage.user,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientLeftRoom: {\n this.roomManager.removeClientFromRoom({\n roomName: parsedMessage.room,\n clientId: parsedMessage.clientId,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.SendMessage: {\n break;\n }\n case WebSocketRedisSubscriberEvent.SendMessageToAll: {\n this.broadcastToAllClients({ data: parsedMessage });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.MessageError: {\n this.sendMessageError({\n webSocketClientId: parsedMessage.clientId,\n error: parsedMessage.error,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.QueueJobCompleted: {\n // const parsedMessage = JSON.parse(message);\n\n break;\n }\n case WebSocketRedisSubscriberEvent.QueueJobError: {\n // For queue job errors, merge error information into data field\n // This maintains backward compatibility while allowing flexible error data\n parsedMessage.data = {\n ...(parsedMessage.data ?? {}),\n error: parsedMessage.error,\n };\n\n break;\n }\n case WebSocketRedisSubscriberEvent.Custom: {\n // Custom logic is being handled in the app\n\n break;\n }\n default: {\n log('Unknown subscriber message received', {\n Channel: channel,\n Message: message,\n });\n }\n }\n\n await this.executeSubscriberHandlers(channel, parsedMessage);\n };\n\n private handleServerError = (error: Error): void => {\n Logger.error({ error });\n };\n\n private handleServerClientConnection = (\n ws: WebSocket,\n authenticatedUser?: { userId: number; payload: any } | null,\n ): void => {\n const clientId = generateClientId();\n\n const lastActivity = Date.now();\n\n ws.on('message', (message: RawData) => this.handleClientMessage(ws, message));\n\n ws.on('close', () => {\n this.handleServerClientDisconnection(clientId);\n this.clientManager.removeClient(clientId);\n\n // Clean up event listeners to prevent memory leaks\n ws.removeAllListeners();\n });\n\n try {\n this.clientManager.addClient({\n clientId,\n ws,\n lastActivity,\n user: authenticatedUser,\n });\n\n // Let other workers know that the client has connected\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientConnected,\n JSON.stringify({\n clientId,\n lastActivity,\n workerId: this.workerId,\n user: authenticatedUser,\n }),\n );\n\n // Send authentication success message if user is authenticated\n if (authenticatedUser) {\n this.sendClientMessage(ws, {\n type: 'auth',\n action: 'authenticated',\n data: {\n userId: authenticatedUser.userId,\n message: 'Authentication successful',\n },\n });\n }\n } catch (error) {\n logger.error({ error });\n }\n };\n\n public leaveRoom({ ws, roomName }: { ws: WebSocket; roomName: string }): void {\n const clientId = this.clientManager.getClientId({ ws });\n\n if (!clientId) {\n log('Client ID not found when removing client from room');\n\n return;\n }\n\n // Check if client is in room\n const clientInRoom = this.roomManager.isClientInRoom({\n clientId,\n roomName,\n });\n\n if (!clientInRoom) {\n log('Client not in room when removing client from room', {\n 'Client ID': clientId || '-',\n 'Room Name': roomName,\n });\n\n return;\n }\n\n this.roomManager.removeClientFromRoom({\n roomName,\n clientId,\n });\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientLeftRoom,\n JSON.stringify({\n clientId,\n room: roomName,\n workerId: this.workerId,\n }),\n );\n\n // Optionally send a message to the client\n this.sendClientMessage(ws, {\n type: 'user',\n action: 'leftRoom',\n data: {\n roomName,\n },\n });\n }\n\n private onClientConnect({\n clientId,\n lastActivity,\n user,\n }: {\n clientId: string;\n lastActivity: number;\n user?: { userId: number; payload: any } | null;\n }): void {\n this.clientManager.addClient({\n clientId,\n ws: null,\n lastActivity,\n user,\n });\n }\n\n private onClientDisconnect({ clientId }: { clientId: string }): void {\n // Set client as disconnected\n this.clientManager.removeClient(clientId);\n\n // Remove client from rooms\n this.roomManager.removeClientFromAllRooms({ clientId });\n }\n\n private handleServerClientDisconnection = (clientId: string): void => {\n const client = this.clientManager.getClient({\n clientId,\n });\n\n if (!client) {\n log('Client not found when handling server client disconnection', {\n 'Client ID': clientId || '-',\n });\n\n return;\n }\n\n this.onClientDisconnect({ clientId });\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientDisconnected,\n JSON.stringify({\n clientId,\n workerId: this.workerId,\n }),\n );\n\n // log('Client disconnected', { ID: clientId });\n };\n\n private handleClientMessage = async (ws: WebSocket, message: RawData): Promise<void> => {\n try {\n const clientId = this.clientManager.getClientId({\n ws,\n });\n\n if (!clientId) {\n log('Client ID not found when handling server message');\n\n return;\n }\n\n // Handle server message\n const serverMessageResponse = await this.handleServerMessage(ws, message, clientId);\n\n if (serverMessageResponse) {\n this.sendClientMessage(ws, {\n type: serverMessageResponse.type,\n action: serverMessageResponse.action,\n response: serverMessageResponse?.response,\n });\n\n if (\n serverMessageResponse?.response &&\n typeof serverMessageResponse.response === 'object' &&\n 'error' in serverMessageResponse.response\n ) {\n // Log error but don't throw to prevent connection disruption\n Logger.error({\n error: serverMessageResponse.response.error,\n meta: {\n clientId,\n type: serverMessageResponse.type,\n action: serverMessageResponse.action,\n },\n });\n }\n }\n } catch (error) {\n Logger.error({ error });\n\n log('Error handling client message', {\n Error: error,\n });\n }\n };\n\n protected handleMessageError(clientId: string, error: string): void {\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.MessageError,\n JSON.stringify({\n includeSender: true,\n clientId,\n error,\n }),\n );\n }\n\n /**\n * Check and disconnect inactive clients based on configuration\n * This helps prevent stale connections from accumulating\n */\n private checkInactiveClients(): void {\n const config = this.options.disconnectInactiveClients;\n\n if (!config?.enabled || !config.inactiveTime) {\n return;\n }\n\n if (config.log) {\n log('Checking inactive clients...');\n }\n\n const now = Date.now();\n const clients = this.clientManager.getClients();\n\n for (const client of clients) {\n const inactiveTime = now - client.lastActivity;\n\n if (inactiveTime > config.inactiveTime) {\n this.clientManager.disconnectClient(client.clientId);\n\n if (config.log) {\n log('Disconnected inactive client', {\n 'Client ID': client.clientId,\n 'Inactive Time': `${inactiveTime}ms`,\n });\n }\n }\n }\n }\n\n /**\n * Broadcast a message to all connected WebSocket clients\n * @param data - The data to broadcast (will be JSON stringified)\n * @param excludeClientId - Optional client ID to exclude from broadcast\n */\n public broadcastToAllClients({\n data,\n excludeClientId,\n }: {\n data: { [key: string]: any };\n excludeClientId?: string;\n }): void {\n if (!this.server) {\n log('Server not started when broadcasting to all clients');\n return;\n }\n\n for (const client of this.server.clients) {\n if (client.readyState !== WebSocket.OPEN) {\n continue;\n }\n\n // Skip excluded client if specified\n if (excludeClientId) {\n const clientId = this.clientManager.getClientId({ ws: client });\n if (clientId === excludeClientId) {\n continue;\n }\n }\n\n client.send(JSON.stringify(data));\n }\n }\n\n public sendMessageError({ webSocketClientId, error }: { webSocketClientId: string; error: string }): void {\n const client = this.clientManager.getClient({\n clientId: webSocketClientId,\n });\n\n if (!client) {\n log('Client not found when sending message error', {\n 'Client ID': webSocketClientId || '-',\n Error: error,\n });\n\n return;\n } else if (!client.ws) {\n log('Client WebSocket not found when sending message error', {\n 'Client ID': webSocketClientId || '-',\n Error: error,\n });\n\n return;\n }\n\n this.sendClientMessage(client.ws, {\n type: 'error',\n action: 'message',\n data: {\n error,\n },\n });\n }\n\n // private getClientId({\n // client,\n // }: {\n // client: WebSocket;\n // }): string | undefined {\n // return [...this.connectedClients.entries()].find(\n // ([_, value]) => value.ws === client,\n // )?.[0];\n // }\n\n private onJoinRoom({ clientId, roomName, userData }: { clientId: string; roomName: string; userData: any }): void {\n const client = this.clientManager.getClient({\n clientId,\n });\n\n if (!client) {\n log('Client not found when joining room', {\n 'Client ID': clientId || '-',\n 'Room Name': roomName,\n });\n\n return;\n }\n\n // Check if client can join multiple rooms\n const canJoinMultipleRooms = this.options.rooms?.clientCanJoinMultipleRooms ?? true; // Default to true for backward compatibility\n\n if (!canJoinMultipleRooms && client.roomName) {\n // Remove client from current room before joining new one\n this.roomManager.removeClientFromRoom({\n roomName: client.roomName,\n clientId,\n broadcast: false, // Don't broadcast here, will broadcast after adding to new room\n });\n }\n\n // Update client with user in client manager\n this.clientManager.updateClient({\n clientId,\n key: 'user',\n data: userData,\n });\n\n this.roomManager.addClientToRoom({\n clientId,\n user: userData,\n roomName,\n });\n }\n\n public async joinRoom({\n ws,\n userId,\n userType,\n username,\n roomName,\n }: {\n ws: WebSocket;\n userId?: number;\n userType?: string;\n username?: string;\n roomName: string;\n }) {\n const clientId = this.clientManager.getClientId({ ws });\n\n if (!clientId) {\n // throw new Error('Client ID not found when joining room');\n\n logger.warn({\n message: 'Client ID not found when joining room',\n meta: {\n // 'WebSocket ID': ws?.id || '-',\n 'Room Name': roomName,\n },\n });\n\n return;\n }\n\n // Check if client is already in room\n const isClientInRoom = this.roomManager.isClientInRoom({\n clientId,\n roomName,\n });\n\n if (isClientInRoom) {\n // throw new Error('Client already in room when joining');\n\n logger.warn({\n message: 'Client already in room when joining',\n meta: {\n // 'WebSocket ID': ws?. || '-',\n 'Room Name': roomName,\n 'Client ID': clientId,\n },\n });\n\n return;\n }\n\n let userData: any = {};\n\n // // Get WebSocket client ID\n // const webSocketId = this.clientManager.getClientId({ ws });\n\n if (userId) {\n // Get user email from database\n const dbEntityManager = this.databaseInstance.getEntityManager();\n\n const getUserQuery = 'SELECT email FROM users WHERE id = ?';\n const getUserParams = [userId];\n\n const getUserResult = await dbEntityManager.execute(getUserQuery, getUserParams);\n\n if (!getUserResult || getUserResult.length === 0) {\n throw new Error('User not found in database');\n }\n\n const user = getUserResult[0];\n\n userData = {\n id: userId,\n ...user,\n };\n }\n\n // userData.uniqueId = webSocketId;\n\n if (username) {\n userData.username = username;\n }\n\n userData.userType = userType;\n\n // if user with same email is already connected, disconnect the previous connection\n // const existingClient =\n // this.clientManager.getClientByKey({\n // key: 'user.email',\n // value: user.email,\n // });\n\n // if (existingClient) {\n // if (existingClient.ws) {\n // this.clientManager.disconnectClient({\n // clientId: existingClient.clientId,\n // });\n // } else {\n // // Publish to Redis that we should disconnect this client\n // this.redisInstance.publisherClient.publish(\n // WebSocketRedisSubscriberEvent.DisconnectClient,\n // JSON.stringify({\n // clientId,\n // workerId: this.workerId,\n // }),\n // );\n // }\n // }\n\n this.onJoinRoom({\n clientId,\n roomName,\n userData,\n });\n\n // Let other workers know that the client has joined the room\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientJoinedRoom,\n JSON.stringify({\n clientId,\n user: userData,\n roomName,\n workerId: this.workerId,\n }),\n );\n\n return true;\n }\n\n public sendClientMessage = (ws: WebSocket, data: unknown, binary: boolean = false): void => {\n const webSocketMessage = JSON.stringify(data);\n\n ws.send(webSocketMessage, { binary });\n };\n\n public sendMessage = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.SendMessage,\n JSON.stringify(formattedData),\n );\n };\n\n public sendMessageToAll = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.SendMessageToAll,\n JSON.stringify(formattedData),\n );\n };\n\n public sendCustomMessage = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(WebSocketRedisSubscriberEvent.Custom, JSON.stringify(formattedData));\n };\n\n public getClients({ userType }: { userType?: string }): any[] {\n return this.clientManager.getClients({ userType });\n }\n}\n"],
5
- "mappings": ";;AAAA,SAAuB,mBAAmB,IAAI,iBAAiB;AAC/D;AAAA,EAEE;AAAA,OAMK;AAKP,OAAO,4BAA4B;AACnC,SAAS,kBAAkB,WAAW;AACtC,OAAO,mBAAmB;AAC1B,SAAS,cAAc;AACvB,OAAO,UAAU;AACjB,SAAoC,eAAe;AACnD,OAAO,0BAA0B;AACjC,OAAO,YAAY;AAEnB,SAAS,4BAA4B;AACrC,SAAS,MAAM,cAAc;AAE7B,MAAO,wBAAsC,cAAc;AAAA,EA1B3D,OA0B2D;AAAA;AAAA;AAAA,EAC/C,gBAAkC;AAAA,IAC1C;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAClB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAClB;AAAA,EACF;AAAA,EAEQ;AAAA,EAEA,kBAAkB,IAAI,gBAAgB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACD,gBAAgB,IAAI,uBAAuB;AAAA,EAC1C,cAAc,IAAI,qBAAqB;AAAA,IAC7C,eAAe,KAAK;AAAA,EACtB,CAAC;AAAA,EACO;AAAA,EAER,IAAW,QAAQ;AACjB,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA,EACQ;AAAA,EACA;AAAA,EACA;AAAA,EACA,8BAA4E,oBAAI,IAAI;AAAA,EACpF,4BAA6D,CAAC;AAAA,EAC9D,6BAA8D,CAAC;AAAA;AAAA,EAG/D,wBAAkC;AAAA,IACxC,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,EAChC;AAAA,EAEA,YAAY,OAA6B;AACvC,UAAM;AAEN,SAAK,mBAAmB,MAAM;AAC9B,SAAK,oBAAoB,MAAM;AAC/B,SAAK,UAAU,MAAM;AACrB,SAAK,gBAAgB,MAAM;AAC3B,SAAK,eAAe,MAAM;AAC1B,SAAK,mBAAmB,MAAM;AAC9B,SAAK,SAAS,MAAM;AACpB,SAAK,WAAW,MAAM;AACtB,SAAK,cAAc,IAAI,qBAAqB,MAAM,iBAAiB;AAAA,EACrE;AAAA,EAEA,IAAW,OAAsB;AAC/B,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,sBACZ,KACsE;AACtE,WAAO,KAAK,YAAY,aAAa,GAAG;AAAA,EAC1C;AAAA,EAEA,MAAa,OAAsB;AACjC,UAAM,8BAA8B,KAAK,KAAK,SAAS,aAAa,eAAe,QAAQ;AAG3F,UAAM,KAAK,gBAAgB,KAAK,eAAe,2BAA2B;AAG1E,UAAM,KAAK,gBAAgB,KAAK,QAAQ,KAAK,QAAQ,oBAAoB;AAEzE,UAAM,KAAK,uBAAuB;AAAA,EACpC;AAAA,EAEA,MAAc,yBAAwC;AACpD,SAAK,4BAA4B,MAAM;AACvC,SAAK,4BAA4B,CAAC;AAClC,SAAK,6BAA6B,CAAC;AAEnC,UAAM,SAAS,KAAK,QAAQ;AAE5B,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AAEA,UAAM,cAA+C,CAAC;AAEtD,QAAI,OAAO,WAAW;AACpB,YAAM,kBAAkB,MAAM,KAAK,WAAW,OAAO,SAAS;AAE9D,UAAI,CAAC,iBAAiB;AACpB,eAAO,KAAK,qDAAqD;AAAA,UAC/D,WAAW,OAAO;AAAA,QACpB,CAAC;AAAA,MACH,OAAO;AACL,cAAM,UAAU,MAAM,OAAO,uBAAgC;AAAA,UAC3D,WAAW,OAAO;AAAA,UAClB,YAAY,CAAC,OAAO,KAAK;AAAA,QAC3B,CAAC;AAED,mBAAW,CAAC,YAAY,YAAY,KAAK,OAAO,QAAQ,OAAO,GAAG;AAChE,sBAAY;AAAA,YACV,GAAG,KAAK,0BAA0B,cAAc;AAAA,cAC9C,QAAQ;AAAA,cACR;AAAA,cACA,WAAW,OAAO;AAAA,YACpB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG;AAClC,iBAAW,WAAW,OAAO,UAAU;AACrC,oBAAY;AAAA,UACV,GAAG,KAAK,0BAA0B,SAAS;AAAA,YACzC,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,eAAW,cAAc,aAAa;AACpC,WAAK,6BAA6B,UAAU;AAAA,IAC9C;AAEA,QAAI,YAAY,SAAS,GAAG;AAC1B,aAAO,KAAK,wCAAwC;AAAA,QAClD,UAAU,MAAM,KAAK,KAAK,4BAA4B,KAAK,CAAC;AAAA,QAC5D,UAAU,KAAK,0BAA0B;AAAA,QACzC,WAAW,KAAK,2BAA2B;AAAA,MAC7C,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,0BACN,OACA,UACiC;AACjC,QAAI,CAAC,OAAO;AACV,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,MAAM,QAAQ,WAAS,KAAK,0BAA0B,OAAO,QAAQ,CAAC;AAAA,IAC/E;AAEA,QAAI,OAAO,UAAU,YAAY;AAC/B,YAAM,gBAAgB,SAAS,cAAc,MAAM,QAAQ;AAE3D,aAAO;AAAA,QACL;AAAA,UACE,MAAM,SAAS,cAAc,MAAM;AAAA,UACnC,UAAU,CAAC,aAAa;AAAA,UACxB,QAAQ,8BAAM,YAAW;AACvB,kBAAM,MAAM,OAAO;AAAA,UACrB,GAFQ;AAAA,QAGV;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,aAAa,KAAK,0BAA0B,OAAkC,QAAQ;AAC5F,UAAI,YAAY;AACd,eAAO,CAAC,UAAU;AAAA,MACpB;AAEA,aAAO,OAAO,OAAO,KAAgC,EAAE;AAAA,QAAQ,WAC7D,KAAK,0BAA0B,OAAO,QAAQ;AAAA,MAChD;AAAA,IACF;AAEA,WAAO,CAAC;AAAA,EACV;AAAA,EAEQ,0BACN,OACA,UACsC;AACtC,UAAM,SAAS,MAAM;AAErB,QAAI,OAAO,WAAW,YAAY;AAChC,aAAO;AAAA,IACT;AAEA,QAAI;AACJ,QAAI,OAAO,UAAU,eAAe,KAAK,OAAO,SAAS,GAAG;AAC1D,sBAAgB,MAAM;AAAA,IACxB,WAAW,OAAO,UAAU,eAAe,KAAK,OAAO,UAAU,GAAG;AAClE,sBAAgB,MAAM;AAAA,IACxB;AAEA,UAAM,WAAW,KAAK,kBAAkB,aAAa;AAErD,QAAI;AACJ,QAAI,OAAO,UAAU,eAAe,KAAK,OAAO,OAAO,GAAG;AACxD,sBAAgB,MAAM;AAAA,IACxB,WAAW,OAAO,UAAU,eAAe,KAAK,OAAO,SAAS,GAAG;AACjE,sBAAgB,MAAM;AAAA,IACxB,WAAW,OAAO,UAAU,eAAe,KAAK,OAAO,UAAU,GAAG;AAClE,sBAAgB,MAAM;AAAA,IACxB;AAEA,UAAM,WAAW,KAAK,kBAAkB,aAAa;AAErD,QAAI,SAAS,WAAW,KAAK,SAAS,WAAW,GAAG;AAClD,aAAO,KAAK,sEAAsE;AAAA,QAChF,QAAQ,SAAS;AAAA,QACjB,QAAQ,SAAS;AAAA,MACnB,CAAC;AAED,aAAO;AAAA,IACT;AAEA,UAAM,aAA4C;AAAA,MAChD,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,SAAS;AAAA,MAC7D,aAAa,OAAO,MAAM,gBAAgB,WAAW,MAAM,cAAc;AAAA,MACzE,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;AAAA,MAChE;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,OAA0B;AAClD,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,UAAU,MAAM,KAAK;AAC3B,aAAO,QAAQ,SAAS,IAAI,CAAC,OAAO,IAAI,CAAC;AAAA,IAC3C;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,MACJ,OAAO,CAAC,YAA+B,OAAO,YAAY,QAAQ,EAClE,IAAI,aAAW,QAAQ,KAAK,CAAC,EAC7B,OAAO,aAAW,QAAQ,SAAS,CAAC;AAAA,IACzC;AAEA,WAAO,CAAC;AAAA,EACV;AAAA,EAEQ,kBAAkB,OAA8C;AACtE,QAAI,CAAC,OAAO;AACV,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,WAAyC,CAAC;AAChD,UAAM,aAAa,wBAAC,YAAqB;AACvC,UAAI,mBAAmB,UAAU,OAAO,YAAY,YAAY;AAC9D,iBAAS,KAAK,OAAqC;AAAA,MACrD,WAAW,OAAO,YAAY,UAAU;AACtC,cAAM,UAAU,QAAQ,KAAK;AAC7B,YAAI,QAAQ,SAAS,GAAG;AACtB,mBAAS,KAAK,OAAqC;AAAA,QACrD;AAAA,MACF;AAAA,IACF,GATmB;AAWnB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,YAAM,QAAQ,UAAU;AAAA,IAC1B,OAAO;AACL,iBAAW,KAAK;AAAA,IAClB;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,6BAA6B,YAAiD;AACpF,UAAM,qBAAqB,WAAW,YAAY;AAClD,UAAM,WAAW,WAAW,UAAU,IAAI,aAAW,QAAQ,KAAK,CAAC,EAAE,OAAO,OAAO,KAAK,CAAC;AAEzF,UAAM,uBAAsD;AAAA,MAC1D,GAAG;AAAA,MACH,UAAU;AAAA,MACV;AAAA,MACA,UAAU,WAAW,YAAY,CAAC;AAAA,IACpC;AAEA,QAAI,SAAS,WAAW,KAAK,qBAAqB,UAAU,WAAW,GAAG;AACxE,aAAO,KAAK,6EAA6E;AAAA,QACvF,SAAS,WAAW,QAAQ;AAAA,MAC9B,CAAC;AAED;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,GAAG;AACvB,iBAAW,WAAW,UAAU;AAC9B,YAAI,YAAY,KAAK;AACnB,eAAK,2BAA2B,KAAK,oBAAoB;AACzD;AAAA,QACF;AAEA,cAAM,kBAAkB,KAAK,4BAA4B,IAAI,OAAO,KAAK,CAAC;AAC1E,wBAAgB,KAAK,oBAAoB;AACzC,aAAK,4BAA4B,IAAI,SAAS,eAAe;AAAA,MAC/D;AAAA,IACF;AAEA,QAAI,qBAAqB,YAAY,qBAAqB,SAAS,SAAS,GAAG;AAC7E,WAAK,0BAA0B,KAAK,oBAAoB;AAAA,IAC1D;AAAA,EACF;AAAA,EAEQ,oBACN,YACA,SACS;AACT,QAAI,CAAC,WAAW,YAAY,WAAW,SAAS,WAAW,GAAG;AAC5D,aAAO;AAAA,IACT;AAEA,WAAO,WAAW,SAAS,KAAK,aAAW,KAAK,gBAAgB,SAAS,OAAO,CAAC;AAAA,EACnF;AAAA,EAEQ,gBAAgB,SAAqC,SAAqD;AAChH,QAAI,OAAO,YAAY,UAAU;AAC/B,UAAI,YAAY,KAAK;AACnB,eAAO;AAAA,MACT;AAEA,aAAO,YAAY,QAAQ;AAAA,IAC7B;AAEA,QAAI,mBAAmB,QAAQ;AAC7B,aAAO,QAAQ,KAAK,QAAQ,OAAO;AAAA,IACrC;AAEA,QAAI;AACF,aAAO,QAAQ,QAAQ,OAAO,CAAC;AAAA,IACjC,SAAS,OAAO;AACd,aAAO,MAAM;AAAA,QACX;AAAA,QACA,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,SAAS,QAAQ;AAAA,UACjB,SAAS,OAAO,OAAO;AAAA,QACzB;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,0BAA0B,SAAiB,SAA6B;AACpF,QACE,KAAK,4BAA4B,SAAS,KAC1C,KAAK,0BAA0B,WAAW,KAC1C,KAAK,2BAA2B,WAAW,GAC3C;AACA;AAAA,IACF;AAEA,UAAM,UAA6C;AAAA,MACjD;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,MACjB,kBAAkB,KAAK;AAAA,MACvB,eAAe,KAAK;AAAA,MACpB,cAAc,KAAK;AAAA,IACrB;AAEA,UAAM,aAAa,oBAAI,IAAmC;AAE1D,UAAM,kBAAkB,KAAK,4BAA4B,IAAI,OAAO;AAEpE,QAAI,iBAAiB;AACnB,sBAAgB,QAAQ,aAAW,WAAW,IAAI,OAAO,CAAC;AAAA,IAC5D;AAEA,SAAK,2BAA2B,QAAQ,aAAW,WAAW,IAAI,OAAO,CAAC;AAE1E,eAAW,cAAc,KAAK,2BAA2B;AACvD,UAAI,KAAK,oBAAoB,YAAY,OAAO,GAAG;AACjD,mBAAW,IAAI,UAAU;AAAA,MAC3B;AAAA,IACF;AAEA,QAAI,WAAW,SAAS,GAAG;AACzB;AAAA,IACF;AAEA,UAAM,kBAAkB,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE;AAEnG,eAAW,WAAW,iBAAiB;AACrC,UAAI;AACF,cAAM,QAAQ,OAAO,OAAO;AAAA,MAC9B,SAAS,OAAO;AACd,eAAO,MAAM;AAAA,UACX;AAAA,UACA,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,SAAS,QAAQ,QAAQ;AAAA,YACzB,SAAS;AAAA,UACX;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,MAAM,EAAE,cAAc,GAAgE;AACjG,WAAO,IAAI,QAAQ,aAAW;AAC5B,YAAM,SAAS,IAAI,GAAG;AAAA,QACpB,UAAU;AAAA;AAAA,MACZ,CAAC;AAED,WAAK,SAAS;AAGd,WAAK,kBAAkB;AAEvB,oBAAc,OAAO,GAAG,WAAW,OAAO,SAAS,QAAQ,SAAS;AAClE,YAAI,QAAQ,KAAK,WAAW,KAAK,GAAG;AAClC,cAAI;AAEF,kBAAM,oBAAoB,MAAM,KAAK,sBAAsB,QAAQ,GAAG;AAEtE,mBAAO,cAAc,SAAS,QAAQ,MAAM,QAAM;AAChD,qBAAO,KAAK,cAAc,IAAI,SAAS,iBAAiB;AAAA,YAC1D,CAAC;AAAA,UACH,SAAS,OAAY;AACnB,gBAAI,mCAAmC,EAAE,OAAO,MAAM,QAAQ,CAAC;AAC/D,mBAAO,MAAM,mCAAmC;AAChD,mBAAO,QAAQ;AAAA,UACjB;AAAA,QACF,OAAO;AACL,iBAAO,QAAQ;AAAA,QACjB;AAAA,MACF,CAAC;AAED,aAAO,GAAG,SAAS,KAAK,iBAAiB;AAEzC,aAAO;AAAA,QACL;AAAA,QACA,CAAC,IAAe,SAAc,sBAA+D;AAC3F,eAAK,6BAA6B,IAAI,iBAAiB;AAAA,QACzD;AAAA,MACF;AAGA,cAAQ,EAAE,OAAO,CAAC;AAAA,IACpB,CAAC;AAAA,EACH;AAAA,EAEA,MAAa,OAAsB;AAEjC,SAAK,gBAAgB,MAAM;AAG3B,SAAK,cAAc,kBAAkB,eAAe,WAAW,KAAK,uBAAuB;AAG3F,SAAK,sBAAsB,QAAQ,yBAAuB;AACxD,WAAK,cAAc,kBAAkB,YAAY,mBAAmB;AAAA,IACtE,CAAC;AAGD,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,QAAQ,QAAQ,YAAU;AACpC,eAAO,mBAAmB;AAC1B,eAAO,MAAM;AAAA,MACf,CAAC;AAED,WAAK,OAAO,mBAAmB;AAC/B,WAAK,OAAO,MAAM;AAAA,IACpB;AAGA,SAAK,cAAc,QAAQ;AAC3B,SAAK,YAAY,QAAQ;AAGzB,SAAK,gBAAgB,IAAI,uBAAuB;AAChD,SAAK,cAAc,IAAI,qBAAqB;AAAA,MAC1C,eAAe,KAAK;AAAA,IACtB,CAAC;AAGD,SAAK,kBAAkB,IAAI,gBAAgB;AAE3C,QAAI,gBAAgB;AAAA,EACtB;AAAA,EAEU,4BAKR;AACA,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,eAAe,KAAK;AAAA,MACpB,cAAc,KAAK;AAAA,MACnB,kBAAkB,KAAK;AAAA,IACzB;AAAA,EACF;AAAA,EAEU,oBAA6B;AACrC,WAAO,KAAK,QAAQ,OAAO,eAAe;AAAA,EAC5C;AAAA,EAEQ,oBAAoB,6BAAY;AACtC,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,QAAI,KAAK,QAAQ,2BAA2B,WAAW,KAAK,QAAQ,0BAA0B,mBAAmB;AAG/G,MAAC;AAAA,QACC,MAAM,KAAK,qBAAqB;AAAA,QAChC,KAAK,QAAQ,0BAA0B;AAAA,QACvC,EAAE,QAAQ,KAAK,gBAAgB,OAAO;AAAA,MACxC;AAAA,IACF;AAGA,SAAK,sBAAsB,QAAQ,yBAAuB;AAExD,WAAK,cAAc,kBAAkB,UAAU,mBAAmB;AAAA,IACpE,CAAC;AAGD,SAAK,cAAc,iBAAiB,GAAG,WAAW,KAAK,uBAAuB;AAE9E,QAAI,kBAAkB;AAAA,MACpB,MAAM,KAAK,QAAQ;AAAA,MACnB,KAAK,KAAK,QAAQ;AAAA,IACpB,CAAC;AAED,QAAI,KAAK,QAAQ,QAAQ,iBAAiB;AACxC,WAAK,QAAQ,OAAO,gBAAgB;AAAA,QAClC,iBAAiB,KAAK;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,EACF,GAlC4B;AAAA;AAAA;AAAA;AAAA,EAuCpB,0BAA0B,8BAAO,SAAiB,YAAmC;AAC3F,QAAI;AAEJ,QAAI;AACF,sBAAgB,KAAK,MAAM,OAAO;AAAA,IACpC,SAAS,OAAO;AACd,UAAI,sCAAsC;AAAA,QACxC,SAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AAED;AAAA,IACF;AAEA,UAAM,gBAAgB,cAAc,kBAAkB;AAEtD,UAAM,eAAe,cAAc,aAAa,KAAK;AAGrD,QAAI,kBAAkB,QAAQ,cAAc;AAE1C;AAAA,IACF;AAEA,QAAI,+BAA+B;AAAA,MACjC,SAAS;AAAA;AAAA,MAET,aAAa,cAAc,YAAY;AAAA,IACzC,CAAC;AAED,YAAQ,SAAS;AAAA,MACf,KAAK,8BAA8B,iBAAiB;AAClD,aAAK,gBAAgB;AAAA,UACnB,UAAU,cAAc;AAAA,UACxB,cAAc,cAAc;AAAA,UAC5B,MAAM,cAAc;AAAA,QACtB,CAAC;AAED;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,oBAAoB;AACrD,aAAK,mBAAmB;AAAA,UACtB,UAAU,cAAc;AAAA,QAC1B,CAAC;AAED;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,kBAAkB;AACnD,cAAM,qBAAqB,KAAK,cAAc,UAAU;AAAA,UACtD,UAAU,cAAc;AAAA;AAAA,QAE1B,CAAC;AAED,YAAI,oBAAoB;AACtB,eAAK,cAAc,iBAAiB;AAAA,YAClC,UAAU,cAAc;AAAA,UAC1B,CAAC;AAGD,eAAK,YAAY,yBAAyB;AAAA,YACxC,UAAU,cAAc;AAAA,UAC1B,CAAC;AAAA,QACH;AAEA;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,kBAAkB;AACnD,aAAK,WAAW;AAAA,UACd,UAAU,cAAc;AAAA,UACxB,UAAU,cAAc;AAAA,UACxB,UAAU,cAAc;AAAA,QAC1B,CAAC;AAED;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,gBAAgB;AACjD,aAAK,YAAY,qBAAqB;AAAA,UACpC,UAAU,cAAc;AAAA,UACxB,UAAU,cAAc;AAAA,QAC1B,CAAC;AAED;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,aAAa;AAC9C;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,kBAAkB;AACnD,aAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAElD;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,cAAc;AAC/C,aAAK,iBAAiB;AAAA,UACpB,mBAAmB,cAAc;AAAA,UACjC,OAAO,cAAc;AAAA,QACvB,CAAC;AAED;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,mBAAmB;AAGpD;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,eAAe;AAGhD,sBAAc,OAAO;AAAA,UACnB,GAAI,cAAc,QAAQ,CAAC;AAAA,UAC3B,OAAO,cAAc;AAAA,QACvB;AAEA;AAAA,MACF;AAAA,MACA,KAAK,8BAA8B,QAAQ;AAGzC;AAAA,MACF;AAAA,MACA,SAAS;AACP,YAAI,uCAAuC;AAAA,UACzC,SAAS;AAAA,UACT,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,KAAK,0BAA0B,SAAS,aAAa;AAAA,EAC7D,GAjIkC;AAAA,EAmI1B,oBAAoB,wBAAC,UAAuB;AAClD,WAAO,MAAM,EAAE,MAAM,CAAC;AAAA,EACxB,GAF4B;AAAA,EAIpB,+BAA+B,wBACrC,IACA,sBACS;AACT,UAAM,WAAW,iBAAiB;AAElC,UAAM,eAAe,KAAK,IAAI;AAE9B,OAAG,GAAG,WAAW,CAAC,YAAqB,KAAK,oBAAoB,IAAI,OAAO,CAAC;AAE5E,OAAG,GAAG,SAAS,MAAM;AACnB,WAAK,gCAAgC,QAAQ;AAC7C,WAAK,cAAc,aAAa,QAAQ;AAGxC,SAAG,mBAAmB;AAAA,IACxB,CAAC;AAED,QAAI;AACF,WAAK,cAAc,UAAU;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AAGD,WAAK,cAAc,gBAAgB;AAAA,QACjC,8BAA8B;AAAA,QAC9B,KAAK,UAAU;AAAA,UACb;AAAA,UACA;AAAA,UACA,UAAU,KAAK;AAAA,UACf,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAGA,UAAI,mBAAmB;AACrB,aAAK,kBAAkB,IAAI;AAAA,UACzB,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,MAAM;AAAA,YACJ,QAAQ,kBAAkB;AAAA,YAC1B,SAAS;AAAA,UACX;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,SAAS,OAAO;AACd,aAAO,MAAM,EAAE,MAAM,CAAC;AAAA,IACxB;AAAA,EACF,GAnDuC;AAAA,EAqDhC,UAAU,EAAE,IAAI,SAAS,GAA8C;AAC5E,UAAM,WAAW,KAAK,cAAc,YAAY,EAAE,GAAG,CAAC;AAEtD,QAAI,CAAC,UAAU;AACb,UAAI,oDAAoD;AAExD;AAAA,IACF;AAGA,UAAM,eAAe,KAAK,YAAY,eAAe;AAAA,MACnD;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,CAAC,cAAc;AACjB,UAAI,qDAAqD;AAAA,QACvD,aAAa,YAAY;AAAA,QACzB,aAAa;AAAA,MACf,CAAC;AAED;AAAA,IACF;AAEA,SAAK,YAAY,qBAAqB;AAAA,MACpC;AAAA,MACA;AAAA,IACF,CAAC;AAED,SAAK,cAAc,gBAAgB;AAAA,MACjC,8BAA8B;AAAA,MAC9B,KAAK,UAAU;AAAA,QACb;AAAA,QACA,MAAM;AAAA,QACN,UAAU,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AAGA,SAAK,kBAAkB,IAAI;AAAA,MACzB,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,gBAAgB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIS;AACP,SAAK,cAAc,UAAU;AAAA,MAC3B;AAAA,MACA,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,mBAAmB,EAAE,SAAS,GAA+B;AAEnE,SAAK,cAAc,aAAa,QAAQ;AAGxC,SAAK,YAAY,yBAAyB,EAAE,SAAS,CAAC;AAAA,EACxD;AAAA,EAEQ,kCAAkC,wBAAC,aAA2B;AACpE,UAAM,SAAS,KAAK,cAAc,UAAU;AAAA,MAC1C;AAAA,IACF,CAAC;AAED,QAAI,CAAC,QAAQ;AACX,UAAI,8DAA8D;AAAA,QAChE,aAAa,YAAY;AAAA,MAC3B,CAAC;AAED;AAAA,IACF;AAEA,SAAK,mBAAmB,EAAE,SAAS,CAAC;AAEpC,SAAK,cAAc,gBAAgB;AAAA,MACjC,8BAA8B;AAAA,MAC9B,KAAK,UAAU;AAAA,QACb;AAAA,QACA,UAAU,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,EAGF,GAxB0C;AAAA,EA0BlC,sBAAsB,8BAAO,IAAe,YAAoC;AACtF,QAAI;AACF,YAAM,WAAW,KAAK,cAAc,YAAY;AAAA,QAC9C;AAAA,MACF,CAAC;AAED,UAAI,CAAC,UAAU;AACb,YAAI,kDAAkD;AAEtD;AAAA,MACF;AAGA,YAAM,wBAAwB,MAAM,KAAK,oBAAoB,IAAI,SAAS,QAAQ;AAElF,UAAI,uBAAuB;AACzB,aAAK,kBAAkB,IAAI;AAAA,UACzB,MAAM,sBAAsB;AAAA,UAC5B,QAAQ,sBAAsB;AAAA,UAC9B,UAAU,uBAAuB;AAAA,QACnC,CAAC;AAED,YACE,uBAAuB,YACvB,OAAO,sBAAsB,aAAa,YAC1C,WAAW,sBAAsB,UACjC;AAEA,iBAAO,MAAM;AAAA,YACX,OAAO,sBAAsB,SAAS;AAAA,YACtC,MAAM;AAAA,cACJ;AAAA,cACA,MAAM,sBAAsB;AAAA,cAC5B,QAAQ,sBAAsB;AAAA,YAChC;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,aAAO,MAAM,EAAE,MAAM,CAAC;AAEtB,UAAI,iCAAiC;AAAA,QACnC,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF,GA7C8B;AAAA,EA+CpB,mBAAmB,UAAkB,OAAqB;AAClE,SAAK,cAAc,gBAAgB;AAAA,MACjC,8BAA8B;AAAA,MAC9B,KAAK,UAAU;AAAA,QACb,eAAe;AAAA,QACf;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,uBAA6B;AACnC,UAAM,SAAS,KAAK,QAAQ;AAE5B,QAAI,CAAC,QAAQ,WAAW,CAAC,OAAO,cAAc;AAC5C;AAAA,IACF;AAEA,QAAI,OAAO,KAAK;AACd,UAAI,8BAA8B;AAAA,IACpC;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,KAAK,cAAc,WAAW;AAE9C,eAAW,UAAU,SAAS;AAC5B,YAAM,eAAe,MAAM,OAAO;AAElC,UAAI,eAAe,OAAO,cAAc;AACtC,aAAK,cAAc,iBAAiB,OAAO,QAAQ;AAEnD,YAAI,OAAO,KAAK;AACd,cAAI,gCAAgC;AAAA,YAClC,aAAa,OAAO;AAAA,YACpB,iBAAiB,GAAG,YAAY;AAAA,UAClC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,sBAAsB;AAAA,IAC3B;AAAA,IACA;AAAA,EACF,GAGS;AACP,QAAI,CAAC,KAAK,QAAQ;AAChB,UAAI,qDAAqD;AACzD;AAAA,IACF;AAEA,eAAW,UAAU,KAAK,OAAO,SAAS;AACxC,UAAI,OAAO,eAAe,UAAU,MAAM;AACxC;AAAA,MACF;AAGA,UAAI,iBAAiB;AACnB,cAAM,WAAW,KAAK,cAAc,YAAY,EAAE,IAAI,OAAO,CAAC;AAC9D,YAAI,aAAa,iBAAiB;AAChC;AAAA,QACF;AAAA,MACF;AAEA,aAAO,KAAK,KAAK,UAAU,IAAI,CAAC;AAAA,IAClC;AAAA,EACF;AAAA,EAEO,iBAAiB,EAAE,mBAAmB,MAAM,GAAuD;AACxG,UAAM,SAAS,KAAK,cAAc,UAAU;AAAA,MAC1C,UAAU;AAAA,IACZ,CAAC;AAED,QAAI,CAAC,QAAQ;AACX,UAAI,+CAA+C;AAAA,QACjD,aAAa,qBAAqB;AAAA,QAClC,OAAO;AAAA,MACT,CAAC;AAED;AAAA,IACF,WAAW,CAAC,OAAO,IAAI;AACrB,UAAI,yDAAyD;AAAA,QAC3D,aAAa,qBAAqB;AAAA,QAClC,OAAO;AAAA,MACT,CAAC;AAED;AAAA,IACF;AAEA,SAAK,kBAAkB,OAAO,IAAI;AAAA,MAChC,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,WAAW,EAAE,UAAU,UAAU,SAAS,GAAgE;AAChH,UAAM,SAAS,KAAK,cAAc,UAAU;AAAA,MAC1C;AAAA,IACF,CAAC;AAED,QAAI,CAAC,QAAQ;AACX,UAAI,sCAAsC;AAAA,QACxC,aAAa,YAAY;AAAA,QACzB,aAAa;AAAA,MACf,CAAC;AAED;AAAA,IACF;AAGA,UAAM,uBAAuB,KAAK,QAAQ,OAAO,8BAA8B;AAE/E,QAAI,CAAC,wBAAwB,OAAO,UAAU;AAE5C,WAAK,YAAY,qBAAqB;AAAA,QACpC,UAAU,OAAO;AAAA,QACjB;AAAA,QACA,WAAW;AAAA;AAAA,MACb,CAAC;AAAA,IACH;AAGA,SAAK,cAAc,aAAa;AAAA,MAC9B;AAAA,MACA,KAAK;AAAA,MACL,MAAM;AAAA,IACR,CAAC;AAED,SAAK,YAAY,gBAAgB;AAAA,MAC/B;AAAA,MACA,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAa,SAAS;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAMG;AACD,UAAM,WAAW,KAAK,cAAc,YAAY,EAAE,GAAG,CAAC;AAEtD,QAAI,CAAC,UAAU;AAGb,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA;AAAA,UAEJ,aAAa;AAAA,QACf;AAAA,MACF,CAAC;AAED;AAAA,IACF;AAGA,UAAM,iBAAiB,KAAK,YAAY,eAAe;AAAA,MACrD;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,gBAAgB;AAGlB,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA;AAAA,UAEJ,aAAa;AAAA,UACb,aAAa;AAAA,QACf;AAAA,MACF,CAAC;AAED;AAAA,IACF;AAEA,QAAI,WAAgB,CAAC;AAKrB,QAAI,QAAQ;AAEV,YAAM,kBAAkB,KAAK,iBAAiB,iBAAiB;AAE/D,YAAM,eAAe;AACrB,YAAM,gBAAgB,CAAC,MAAM;AAE7B,YAAM,gBAAgB,MAAM,gBAAgB,QAAQ,cAAc,aAAa;AAE/E,UAAI,CAAC,iBAAiB,cAAc,WAAW,GAAG;AAChD,cAAM,IAAI,MAAM,4BAA4B;AAAA,MAC9C;AAEA,YAAM,OAAO,cAAc,CAAC;AAE5B,iBAAW;AAAA,QACT,IAAI;AAAA,QACJ,GAAG;AAAA,MACL;AAAA,IACF;AAIA,QAAI,UAAU;AACZ,eAAS,WAAW;AAAA,IACtB;AAEA,aAAS,WAAW;AA0BpB,SAAK,WAAW;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAGD,SAAK,cAAc,gBAAgB;AAAA,MACjC,8BAA8B;AAAA,MAC9B,KAAK,UAAU;AAAA,QACb;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA,UAAU,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,oBAAoB,wBAAC,IAAe,MAAe,SAAkB,UAAgB;AAC1F,UAAM,mBAAmB,KAAK,UAAU,IAAI;AAE5C,OAAG,KAAK,kBAAkB,EAAE,OAAO,CAAC;AAAA,EACtC,GAJ2B;AAAA,EAMpB,cAAc,wBAAC,EAAE,KAAK,MAA+B;AAC1D,UAAM,gBAAgB;AAAA,MACpB,GAAI;AAAA,MACJ,UAAU,KAAK;AAAA,IACjB;AAEA,SAAK,cAAc,gBAAgB;AAAA,MACjC,8BAA8B;AAAA,MAC9B,KAAK,UAAU,aAAa;AAAA,IAC9B;AAAA,EACF,GAVqB;AAAA,EAYd,mBAAmB,wBAAC,EAAE,KAAK,MAA+B;AAC/D,UAAM,gBAAgB;AAAA,MACpB,GAAI;AAAA,MACJ,UAAU,KAAK;AAAA,IACjB;AAEA,SAAK,cAAc,gBAAgB;AAAA,MACjC,8BAA8B;AAAA,MAC9B,KAAK,UAAU,aAAa;AAAA,IAC9B;AAAA,EACF,GAV0B;AAAA,EAYnB,oBAAoB,wBAAC,EAAE,KAAK,MAA+B;AAChE,UAAM,gBAAgB;AAAA,MACpB,GAAI;AAAA,MACJ,UAAU,KAAK;AAAA,IACjB;AAEA,SAAK,cAAc,gBAAgB,QAAQ,8BAA8B,QAAQ,KAAK,UAAU,aAAa,CAAC;AAAA,EAChH,GAP2B;AAAA,EASpB,WAAW,EAAE,SAAS,GAAiC;AAC5D,WAAO,KAAK,cAAc,WAAW,EAAE,SAAS,CAAC;AAAA,EACnD;AACF;",
4
+ "sourcesContent": ["import { type RawData, WebSocketServer as WS, WebSocket } from 'ws';\nimport {\n type WebSocketOptions,\n WebSocketRedisSubscriberEvent,\n type WebSocketRoute,\n type WebSocketSubscriberDefinition,\n type WebSocketSubscriberHandlerContext,\n type WebSocketSubscriberMatcher,\n type WebSocketType,\n} from './websocket.interface.js';\nimport type RedisInstance from '../redis/instance.js';\nimport type QueueManager from '../queue/manager.js';\nimport type DatabaseInstance from '../database/instance.js';\nimport type { WebSocketServerProps } from './websocket-server.interface.js';\nimport WebSocketClientManager from './websocket-client-manager.js';\nimport { generateClientId, log } from './utils.js';\nimport WebSocketBase from './websocket-base.js';\nimport { Logger } from '../logger/index.js';\nimport path from 'path';\nimport { type WebApplicationConfig, baseDir } from '../index.js';\nimport WebSocketRoomManager from './websocket-room-manager.js';\nimport logger from '../logger/logger.js';\nimport type { FastifyInstance } from 'fastify';\nimport { WebSocketAuthService } from './websocket-auth.js';\nimport { File, Loader } from '../util/index.js';\n\nexport default class WebSocketServer extends WebSocketBase {\n protected defaultRoutes: WebSocketRoute[] = [\n {\n type: 'system',\n action: 'joinRoom',\n controllerName: 'system',\n },\n {\n type: 'system',\n action: 'leaveRoom',\n controllerName: 'system',\n },\n ];\n\n private server?: WS;\n\n private abortController = new AbortController();\n private workerId: number | null;\n private uniqueInstanceId: string;\n private applicationConfig: WebApplicationConfig;\n private options: WebSocketOptions;\n public clientManager = new WebSocketClientManager();\n private roomManager = new WebSocketRoomManager({\n clientManager: this.clientManager,\n });\n private authService: WebSocketAuthService;\n\n public get rooms() {\n return this.roomManager.rooms;\n }\n private redisInstance: RedisInstance;\n private queueManager: QueueManager;\n private databaseInstance: DatabaseInstance;\n private subscriberHandlersByChannel: Map<string, WebSocketSubscriberDefinition[]> = new Map();\n private subscriberMatcherHandlers: WebSocketSubscriberDefinition[] = [];\n private wildcardSubscriberHandlers: WebSocketSubscriberDefinition[] = [];\n\n /** Redis subscriber events */\n private redisSubscriberEvents: string[] = [\n WebSocketRedisSubscriberEvent.ClientConnected,\n WebSocketRedisSubscriberEvent.ClientJoinedRoom,\n WebSocketRedisSubscriberEvent.ClientLeftRoom,\n WebSocketRedisSubscriberEvent.ClientDisconnected,\n WebSocketRedisSubscriberEvent.DisconnectClient,\n WebSocketRedisSubscriberEvent.SendMessage,\n WebSocketRedisSubscriberEvent.SendMessageToAll,\n WebSocketRedisSubscriberEvent.MessageError,\n WebSocketRedisSubscriberEvent.QueueJobCompleted,\n WebSocketRedisSubscriberEvent.QueueJobError,\n WebSocketRedisSubscriberEvent.Custom,\n ];\n\n constructor(props: WebSocketServerProps) {\n super();\n\n this.uniqueInstanceId = props.uniqueInstanceId;\n this.applicationConfig = props.applicationConfig;\n this.options = props.options;\n this.redisInstance = props.redisInstance;\n this.queueManager = props.queueManager;\n this.databaseInstance = props.databaseInstance;\n this.routes = props.routes;\n this.workerId = props.workerId;\n this.authService = new WebSocketAuthService(props.applicationConfig);\n }\n\n public get type(): WebSocketType {\n return 'server';\n }\n\n private async validateWebSocketAuth(\n url: string,\n ): Promise<{ userId: number; payload: Record<string, unknown> } | null> {\n return this.authService.validateAuth(url);\n }\n\n public async load(): Promise<void> {\n const libraryControllersDirectory = path.join(baseDir, 'websocket', 'controllers', 'server');\n\n // Configure default routes\n await this.configureRoutes(this.defaultRoutes, libraryControllersDirectory);\n\n // Configure custom routes\n await this.configureRoutes(this.routes, this.options.controllersDirectory);\n\n await this.loadSubscriberHandlers();\n }\n\n private async loadSubscriberHandlers(): Promise<void> {\n this.subscriberHandlersByChannel.clear();\n this.subscriberMatcherHandlers = [];\n this.wildcardSubscriberHandlers = [];\n\n const config = this.options.subscriberHandlers;\n\n if (!config) {\n return;\n }\n\n const definitions: WebSocketSubscriberDefinition[] = [];\n\n if (config.directory) {\n const directoryExists = await File.pathExists(config.directory);\n\n if (!directoryExists) {\n logger.warn('WebSocket subscriber handlers directory not found', {\n Directory: config.directory,\n });\n } else {\n const modules = await Loader.loadModulesInDirectory<unknown>({\n directory: config.directory,\n extensions: ['.ts', '.js'],\n });\n\n for (const [moduleName, moduleExport] of Object.entries(modules)) {\n definitions.push(\n ...this.normalizeSubscriberExport(moduleExport, {\n source: 'file',\n moduleName,\n directory: config.directory,\n }),\n );\n }\n }\n }\n\n if (Array.isArray(config.handlers)) {\n for (const handler of config.handlers) {\n definitions.push(\n ...this.normalizeSubscriberExport(handler, {\n source: 'inline',\n }),\n );\n }\n }\n\n for (const definition of definitions) {\n this.registerSubscriberDefinition(definition);\n }\n\n if (definitions.length > 0) {\n logger.info('WebSocket subscriber handlers loaded', {\n Channels: Array.from(this.subscriberHandlersByChannel.keys()),\n Matchers: this.subscriberMatcherHandlers.length,\n Wildcards: this.wildcardSubscriberHandlers.length,\n });\n }\n }\n\n private normalizeSubscriberExport(\n value: unknown,\n metadata: { source: 'file' | 'inline'; moduleName?: string; directory?: string },\n ): WebSocketSubscriberDefinition[] {\n if (!value) {\n return [];\n }\n\n if (Array.isArray(value)) {\n return value.flatMap(entry => this.normalizeSubscriberExport(entry, metadata));\n }\n\n if (typeof value === 'function') {\n const moduleChannel = metadata.moduleName ?? value.name ?? 'anonymous';\n\n return [\n {\n name: metadata.moduleName ?? value.name,\n channels: [moduleChannel],\n handle: async context => {\n await value(context);\n },\n },\n ];\n }\n\n if (typeof value === 'object') {\n const definition = this.normalizeSubscriberObject(value as Record<string, unknown>, metadata);\n if (definition) {\n return [definition];\n }\n\n return Object.values(value as Record<string, unknown>).flatMap(entry =>\n this.normalizeSubscriberExport(entry, metadata),\n );\n }\n\n return [];\n }\n\n private normalizeSubscriberObject(\n value: Record<string, unknown>,\n metadata: { source: 'file' | 'inline'; moduleName?: string },\n ): WebSocketSubscriberDefinition | null {\n const handle = value.handle;\n\n if (typeof handle !== 'function') {\n return null;\n }\n\n let channelSource: unknown;\n if (Object.prototype.hasOwnProperty.call(value, 'channel')) {\n channelSource = value.channel;\n } else if (Object.prototype.hasOwnProperty.call(value, 'channels')) {\n channelSource = value.channels;\n }\n\n const channels = this.normalizeChannels(channelSource);\n\n let matcherSource: unknown;\n if (Object.prototype.hasOwnProperty.call(value, 'match')) {\n matcherSource = value.match;\n } else if (Object.prototype.hasOwnProperty.call(value, 'matcher')) {\n matcherSource = value.matcher;\n } else if (Object.prototype.hasOwnProperty.call(value, 'matchers')) {\n matcherSource = value.matchers;\n }\n\n const matchers = this.normalizeMatchers(matcherSource);\n\n if (channels.length === 0 && matchers.length === 0) {\n logger.warn('Skipping WebSocket subscriber handler without channels or matchers', {\n Source: metadata.source,\n Module: metadata.moduleName,\n });\n\n return null;\n }\n\n const definition: WebSocketSubscriberDefinition = {\n name: typeof value.name === 'string' ? value.name : metadata.moduleName,\n description: typeof value.description === 'string' ? value.description : undefined,\n priority: typeof value.priority === 'number' ? value.priority : undefined,\n channels,\n matchers,\n handle: handle as (context: WebSocketSubscriberHandlerContext) => unknown | Promise<unknown>,\n };\n\n return definition;\n }\n\n private normalizeChannels(input: unknown): string[] {\n if (typeof input === 'string') {\n const trimmed = input.trim();\n return trimmed.length > 0 ? [trimmed] : [];\n }\n\n if (Array.isArray(input)) {\n return input\n .filter((channel): channel is string => typeof channel === 'string')\n .map(channel => channel.trim())\n .filter(channel => channel.length > 0);\n }\n\n return [];\n }\n\n private normalizeMatchers(input: unknown): WebSocketSubscriberMatcher[] {\n if (!input) {\n return [];\n }\n\n const matchers: WebSocketSubscriberMatcher[] = [];\n const addMatcher = (matcher: unknown) => {\n if (matcher instanceof RegExp || typeof matcher === 'function') {\n matchers.push(matcher as WebSocketSubscriberMatcher);\n } else if (typeof matcher === 'string') {\n const trimmed = matcher.trim();\n if (trimmed.length > 0) {\n matchers.push(trimmed as WebSocketSubscriberMatcher);\n }\n }\n };\n\n if (Array.isArray(input)) {\n input.forEach(addMatcher);\n } else {\n addMatcher(input);\n }\n\n return matchers;\n }\n\n private registerSubscriberDefinition(definition: WebSocketSubscriberDefinition): void {\n const normalizedPriority = definition.priority ?? 0;\n const channels = definition.channels?.map(channel => channel.trim()).filter(Boolean) ?? [];\n\n const normalizedDefinition: WebSocketSubscriberDefinition = {\n ...definition,\n priority: normalizedPriority,\n channels,\n matchers: definition.matchers ?? [],\n };\n\n if (channels.length === 0 && normalizedDefinition.matchers?.length === 0) {\n logger.warn('Skipping WebSocket subscriber handler registration due to missing filters', {\n Handler: definition.name ?? '(anonymous)',\n });\n\n return;\n }\n\n if (channels.length > 0) {\n for (const channel of channels) {\n if (channel === '*') {\n this.wildcardSubscriberHandlers.push(normalizedDefinition);\n continue;\n }\n\n const channelHandlers = this.subscriberHandlersByChannel.get(channel) ?? [];\n channelHandlers.push(normalizedDefinition);\n this.subscriberHandlersByChannel.set(channel, channelHandlers);\n }\n }\n\n if (normalizedDefinition.matchers && normalizedDefinition.matchers.length > 0) {\n this.subscriberMatcherHandlers.push(normalizedDefinition);\n }\n }\n\n private doesDefinitionMatch(\n definition: WebSocketSubscriberDefinition,\n context: WebSocketSubscriberHandlerContext,\n ): boolean {\n if (!definition.matchers || definition.matchers.length === 0) {\n return false;\n }\n\n return definition.matchers.some(matcher => this.evaluateMatcher(matcher, context));\n }\n\n private evaluateMatcher(matcher: WebSocketSubscriberMatcher, context: WebSocketSubscriberHandlerContext): boolean {\n if (typeof matcher === 'string') {\n if (matcher === '*') {\n return true;\n }\n\n return matcher === context.channel;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(context.channel);\n }\n\n try {\n return Boolean(matcher(context));\n } catch (error) {\n logger.error({\n error,\n message: 'WebSocket subscriber matcher threw an error',\n meta: {\n Channel: context.channel,\n Matcher: String(matcher),\n },\n });\n\n return false;\n }\n }\n\n private async executeSubscriberHandlers(channel: string, message: any): Promise<void> {\n if (\n this.subscriberHandlersByChannel.size === 0 &&\n this.subscriberMatcherHandlers.length === 0 &&\n this.wildcardSubscriberHandlers.length === 0\n ) {\n return;\n }\n\n const context: WebSocketSubscriberHandlerContext = {\n channel,\n message,\n webSocketServer: this,\n databaseInstance: this.databaseInstance,\n redisInstance: this.redisInstance,\n queueManager: this.queueManager,\n };\n\n const candidates = new Set<WebSocketSubscriberDefinition>();\n\n const channelHandlers = this.subscriberHandlersByChannel.get(channel);\n\n if (channelHandlers) {\n channelHandlers.forEach(handler => candidates.add(handler));\n }\n\n this.wildcardSubscriberHandlers.forEach(handler => candidates.add(handler));\n\n for (const definition of this.subscriberMatcherHandlers) {\n if (this.doesDefinitionMatch(definition, context)) {\n candidates.add(definition);\n }\n }\n\n if (candidates.size === 0) {\n return;\n }\n\n const orderedHandlers = Array.from(candidates).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n\n for (const handler of orderedHandlers) {\n try {\n await handler.handle(context);\n } catch (error) {\n logger.error({\n error,\n message: 'WebSocket subscriber handler failed',\n meta: {\n Handler: handler.name ?? '(anonymous)',\n Channel: channel,\n },\n });\n }\n }\n }\n\n public async start({ fastifyServer }: { fastifyServer: FastifyInstance }): Promise<{ server: WS }> {\n return new Promise(resolve => {\n const server = new WS({\n noServer: true, // We're handling the server externally\n });\n\n this.server = server;\n\n // Ensure this is called after the server has been properly set up\n this.handleServerStart();\n\n fastifyServer.server.on('upgrade', async (request, socket, head) => {\n if (request.url?.startsWith('/ws')) {\n try {\n // Validate authentication token if provided\n const authenticatedUser = await this.validateWebSocketAuth(request.url);\n\n server.handleUpgrade(request, socket, head, ws => {\n server.emit('connection', ws, request, authenticatedUser);\n });\n } catch (error: any) {\n log('WebSocket authentication failed', { error: error.message });\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n }\n } else {\n socket.destroy();\n }\n });\n\n server.on('error', this.handleServerError);\n\n server.on(\n 'connection',\n (ws: WebSocket, request: any, authenticatedUser: { userId: number; payload: any } | null) => {\n this.handleServerClientConnection(ws, authenticatedUser);\n },\n );\n\n // Resolve the promise with the server instance\n resolve({ server });\n });\n }\n\n public async stop(): Promise<void> {\n // Abort all ongoing operations (intervals, etc.)\n this.abortController.abort();\n\n // Clean up Redis subscriber listeners\n this.redisInstance.subscriberClient?.removeListener('message', this.handleSubscriberMessage);\n\n // Unsubscribe from all Redis events\n this.redisSubscriberEvents.forEach(subscriberEventName => {\n this.redisInstance.subscriberClient?.unsubscribe(subscriberEventName);\n });\n\n // Close all client connections and clean up\n if (this.server) {\n this.server.clients.forEach(client => {\n client.removeAllListeners();\n client.close();\n });\n\n this.server.removeAllListeners();\n this.server.close();\n }\n\n // Clean up client manager and room manager\n this.clientManager.cleanup();\n this.roomManager.cleanup();\n\n // Reset managers\n this.clientManager = new WebSocketClientManager();\n this.roomManager = new WebSocketRoomManager({\n clientManager: this.clientManager,\n });\n\n // Create new AbortController for potential restart\n this.abortController = new AbortController();\n\n log('Server stopped');\n }\n\n protected getControllerDependencies(): {\n webSocketServer: WebSocketServer;\n redisInstance: RedisInstance;\n queueManager: QueueManager;\n databaseInstance: DatabaseInstance;\n } {\n return {\n webSocketServer: this,\n redisInstance: this.redisInstance,\n queueManager: this.queueManager,\n databaseInstance: this.databaseInstance,\n };\n }\n\n protected shouldPrintRoutes(): boolean {\n return this.options.debug?.printRoutes ?? false;\n }\n\n private handleServerStart = (): void => {\n if (!this.server) {\n throw new Error('WebSocket server not started');\n }\n\n if (this.options.disconnectInactiveClients?.enabled && this.options.disconnectInactiveClients.intervalCheckTime) {\n // Note: setInterval with signal option requires Node.js 15+\n // TypeScript types may not reflect this, so we use type assertion\n (setInterval as (fn: () => void, ms: number, options?: { signal: AbortSignal }) => NodeJS.Timeout)(\n () => this.checkInactiveClients(),\n this.options.disconnectInactiveClients.intervalCheckTime,\n { signal: this.abortController.signal },\n );\n }\n\n // Go through each event and subscribe to it\n this.redisSubscriberEvents.forEach(subscriberEventName => {\n // Subscribe to event\n this.redisInstance.subscriberClient?.subscribe(subscriberEventName);\n });\n\n // Handle subscriber message\n this.redisInstance.subscriberClient.on('message', this.handleSubscriberMessage);\n\n log('Server started', {\n Host: this.options.host,\n URL: this.options.url,\n });\n\n if (this.options.events?.onServerStarted) {\n this.options.events.onServerStarted({\n webSocketServer: this.server,\n });\n }\n };\n\n /**\n * Handle subscriber message.\n */\n private handleSubscriberMessage = async (channel: string, message: string): Promise<void> => {\n let parsedMessage: { [key: string]: any };\n\n try {\n parsedMessage = JSON.parse(message);\n } catch (error) {\n log('Failed to parse subscriber message', {\n Channel: channel,\n Message: message,\n Error: error,\n });\n\n return;\n }\n\n const includeSender = parsedMessage.includeSender === true;\n\n const isSameWorker = parsedMessage.workerId === this.workerId;\n\n // Check if message is from the same worker\n if (includeSender !== true && isSameWorker) {\n // Ignore the message if it's from the same worker\n return;\n }\n\n log('Incoming subscriber message', {\n Channel: channel,\n // 'Run Same Worker': parsedMessage.includeSender ? 'Yes' : 'No',\n 'Client ID': parsedMessage.clientId ?? '-',\n });\n\n switch (channel) {\n case WebSocketRedisSubscriberEvent.ClientConnected: {\n this.onClientConnect({\n clientId: parsedMessage.clientId,\n lastActivity: parsedMessage.lastActivity,\n user: parsedMessage.user,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientDisconnected: {\n this.onClientDisconnect({\n clientId: parsedMessage.clientId,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.DisconnectClient: {\n const clientToDisconnect = this.clientManager.getClient({\n clientId: parsedMessage.clientId,\n // requireWs: true,\n });\n\n if (clientToDisconnect) {\n this.clientManager.disconnectClient({\n clientId: parsedMessage.clientId,\n });\n\n // Remove client from rooms\n this.roomManager.removeClientFromAllRooms({\n clientId: parsedMessage.clientId,\n });\n }\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientJoinedRoom: {\n this.onJoinRoom({\n clientId: parsedMessage.clientId,\n roomName: parsedMessage.roomName,\n userData: parsedMessage.user,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientLeftRoom: {\n this.roomManager.removeClientFromRoom({\n roomName: parsedMessage.room,\n clientId: parsedMessage.clientId,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.SendMessage: {\n break;\n }\n case WebSocketRedisSubscriberEvent.SendMessageToAll: {\n this.broadcastToAllClients({ data: parsedMessage });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.MessageError: {\n this.sendMessageError({\n webSocketClientId: parsedMessage.clientId,\n error: parsedMessage.error,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.QueueJobCompleted: {\n // const parsedMessage = JSON.parse(message);\n\n break;\n }\n case WebSocketRedisSubscriberEvent.QueueJobError: {\n // For queue job errors, merge error information into data field\n parsedMessage.data = {\n ...(parsedMessage.data ?? {}),\n error: parsedMessage.error,\n };\n\n break;\n }\n case WebSocketRedisSubscriberEvent.Custom: {\n // Custom logic is being handled in the app\n\n break;\n }\n default: {\n log('Unknown subscriber message received', {\n Channel: channel,\n Message: message,\n });\n }\n }\n\n await this.executeSubscriberHandlers(channel, parsedMessage);\n };\n\n private handleServerError = (error: Error): void => {\n Logger.error({ error });\n };\n\n private handleServerClientConnection = (\n ws: WebSocket,\n authenticatedUser?: { userId: number; payload: any } | null,\n ): void => {\n const clientId = generateClientId();\n\n const lastActivity = Date.now();\n\n ws.on('message', (message: RawData) => this.handleClientMessage(ws, message));\n\n ws.on('close', () => {\n this.handleServerClientDisconnection(clientId);\n this.clientManager.removeClient(clientId);\n\n // Clean up event listeners to prevent memory leaks\n ws.removeAllListeners();\n });\n\n try {\n this.clientManager.addClient({\n clientId,\n ws,\n lastActivity,\n user: authenticatedUser,\n });\n\n // Let other workers know that the client has connected\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientConnected,\n JSON.stringify({\n clientId,\n lastActivity,\n workerId: this.workerId,\n user: authenticatedUser,\n }),\n );\n\n // Send authentication success message if user is authenticated\n if (authenticatedUser) {\n this.sendClientMessage(ws, {\n type: 'auth',\n action: 'authenticated',\n data: {\n userId: authenticatedUser.userId,\n message: 'Authentication successful',\n },\n });\n }\n } catch (error) {\n logger.error({ error });\n }\n };\n\n public leaveRoom({ ws, roomName }: { ws: WebSocket; roomName: string }): void {\n const clientId = this.clientManager.getClientId({ ws });\n\n if (!clientId) {\n log('Client ID not found when removing client from room');\n\n return;\n }\n\n // Check if client is in room\n const clientInRoom = this.roomManager.isClientInRoom({\n clientId,\n roomName,\n });\n\n if (!clientInRoom) {\n log('Client not in room when removing client from room', {\n 'Client ID': clientId || '-',\n 'Room Name': roomName,\n });\n\n return;\n }\n\n this.roomManager.removeClientFromRoom({\n roomName,\n clientId,\n });\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientLeftRoom,\n JSON.stringify({\n clientId,\n room: roomName,\n workerId: this.workerId,\n }),\n );\n\n // Optionally send a message to the client\n this.sendClientMessage(ws, {\n type: 'user',\n action: 'leftRoom',\n data: {\n roomName,\n },\n });\n }\n\n private onClientConnect({\n clientId,\n lastActivity,\n user,\n }: {\n clientId: string;\n lastActivity: number;\n user?: { userId: number; payload: any } | null;\n }): void {\n this.clientManager.addClient({\n clientId,\n ws: null,\n lastActivity,\n user,\n });\n }\n\n private onClientDisconnect({ clientId }: { clientId: string }): void {\n // Set client as disconnected\n this.clientManager.removeClient(clientId);\n\n // Remove client from rooms\n this.roomManager.removeClientFromAllRooms({ clientId });\n }\n\n private handleServerClientDisconnection = (clientId: string): void => {\n const client = this.clientManager.getClient({\n clientId,\n });\n\n if (!client) {\n log('Client not found when handling server client disconnection', {\n 'Client ID': clientId || '-',\n });\n\n return;\n }\n\n this.onClientDisconnect({ clientId });\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientDisconnected,\n JSON.stringify({\n clientId,\n workerId: this.workerId,\n }),\n );\n\n // log('Client disconnected', { ID: clientId });\n };\n\n private handleClientMessage = async (ws: WebSocket, message: RawData): Promise<void> => {\n try {\n const clientId = this.clientManager.getClientId({\n ws,\n });\n\n if (!clientId) {\n log('Client ID not found when handling server message');\n\n return;\n }\n\n // Handle server message\n const serverMessageResponse = await this.handleServerMessage(ws, message, clientId);\n\n if (serverMessageResponse) {\n this.sendClientMessage(ws, {\n type: serverMessageResponse.type,\n action: serverMessageResponse.action,\n response: serverMessageResponse?.response,\n });\n\n if (\n serverMessageResponse?.response &&\n typeof serverMessageResponse.response === 'object' &&\n 'error' in serverMessageResponse.response\n ) {\n // Log error but don't throw to prevent connection disruption\n Logger.error({\n error: serverMessageResponse.response.error,\n meta: {\n clientId,\n type: serverMessageResponse.type,\n action: serverMessageResponse.action,\n },\n });\n }\n }\n } catch (error) {\n Logger.error({ error });\n\n log('Error handling client message', {\n Error: error,\n });\n }\n };\n\n protected handleMessageError(clientId: string, error: string): void {\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.MessageError,\n JSON.stringify({\n includeSender: true,\n clientId,\n error,\n }),\n );\n }\n\n /**\n * Check and disconnect inactive clients based on configuration\n * This helps prevent stale connections from accumulating\n */\n private checkInactiveClients(): void {\n const config = this.options.disconnectInactiveClients;\n\n if (!config?.enabled || !config.inactiveTime) {\n return;\n }\n\n if (config.log) {\n log('Checking inactive clients...');\n }\n\n const now = Date.now();\n const clients = this.clientManager.getClients();\n\n for (const client of clients) {\n const inactiveTime = now - client.lastActivity;\n\n if (inactiveTime > config.inactiveTime) {\n this.clientManager.disconnectClient(client.clientId);\n\n if (config.log) {\n log('Disconnected inactive client', {\n 'Client ID': client.clientId,\n 'Inactive Time': `${inactiveTime}ms`,\n });\n }\n }\n }\n }\n\n /**\n * Broadcast a message to all connected WebSocket clients\n * @param data - The data to broadcast (will be JSON stringified)\n * @param excludeClientId - Optional client ID to exclude from broadcast\n */\n public broadcastToAllClients({\n data,\n excludeClientId,\n }: {\n data: { [key: string]: any };\n excludeClientId?: string;\n }): void {\n if (!this.server) {\n log('Server not started when broadcasting to all clients');\n return;\n }\n\n for (const client of this.server.clients) {\n if (client.readyState !== WebSocket.OPEN) {\n continue;\n }\n\n // Skip excluded client if specified\n if (excludeClientId) {\n const clientId = this.clientManager.getClientId({ ws: client });\n if (clientId === excludeClientId) {\n continue;\n }\n }\n\n client.send(JSON.stringify(data));\n }\n }\n\n public sendMessageError({ webSocketClientId, error }: { webSocketClientId: string; error: string }): void {\n const client = this.clientManager.getClient({\n clientId: webSocketClientId,\n });\n\n if (!client) {\n log('Client not found when sending message error', {\n 'Client ID': webSocketClientId || '-',\n Error: error,\n });\n\n return;\n } else if (!client.ws) {\n log('Client WebSocket not found when sending message error', {\n 'Client ID': webSocketClientId || '-',\n Error: error,\n });\n\n return;\n }\n\n this.sendClientMessage(client.ws, {\n type: 'error',\n action: 'message',\n data: {\n error,\n },\n });\n }\n\n // private getClientId({\n // client,\n // }: {\n // client: WebSocket;\n // }): string | undefined {\n // return [...this.connectedClients.entries()].find(\n // ([_, value]) => value.ws === client,\n // )?.[0];\n // }\n\n private onJoinRoom({ clientId, roomName, userData }: { clientId: string; roomName: string; userData: any }): void {\n const client = this.clientManager.getClient({\n clientId,\n });\n\n if (!client) {\n log('Client not found when joining room', {\n 'Client ID': clientId || '-',\n 'Room Name': roomName,\n });\n\n return;\n }\n\n // Check if client can join multiple rooms\n const canJoinMultipleRooms = this.options.rooms?.clientCanJoinMultipleRooms ?? true;\n\n if (!canJoinMultipleRooms && client.roomName) {\n // Remove client from current room before joining new one\n this.roomManager.removeClientFromRoom({\n roomName: client.roomName,\n clientId,\n broadcast: false, // Don't broadcast here, will broadcast after adding to new room\n });\n }\n\n // Update client with user in client manager\n this.clientManager.updateClient({\n clientId,\n key: 'user',\n data: userData,\n });\n\n this.roomManager.addClientToRoom({\n clientId,\n user: userData,\n roomName,\n });\n }\n\n public async joinRoom({\n ws,\n userId,\n userType,\n username,\n roomName,\n }: {\n ws: WebSocket;\n userId?: number;\n userType?: string;\n username?: string;\n roomName: string;\n }) {\n const clientId = this.clientManager.getClientId({ ws });\n\n if (!clientId) {\n // throw new Error('Client ID not found when joining room');\n\n logger.warn({\n message: 'Client ID not found when joining room',\n meta: {\n // 'WebSocket ID': ws?.id || '-',\n 'Room Name': roomName,\n },\n });\n\n return;\n }\n\n // Check if client is already in room\n const isClientInRoom = this.roomManager.isClientInRoom({\n clientId,\n roomName,\n });\n\n if (isClientInRoom) {\n // throw new Error('Client already in room when joining');\n\n logger.warn({\n message: 'Client already in room when joining',\n meta: {\n // 'WebSocket ID': ws?. || '-',\n 'Room Name': roomName,\n 'Client ID': clientId,\n },\n });\n\n return;\n }\n\n let userData: any = {};\n\n // // Get WebSocket client ID\n // const webSocketId = this.clientManager.getClientId({ ws });\n\n if (userId) {\n // Get user email from database\n const dbEntityManager = this.databaseInstance.getEntityManager();\n\n const getUserQuery = 'SELECT email FROM users WHERE id = ?';\n const getUserParams = [userId];\n\n const getUserResult = await dbEntityManager.execute(getUserQuery, getUserParams);\n\n if (!getUserResult || getUserResult.length === 0) {\n throw new Error('User not found in database');\n }\n\n const user = getUserResult[0];\n\n userData = {\n id: userId,\n ...user,\n };\n }\n\n // userData.uniqueId = webSocketId;\n\n if (username) {\n userData.username = username;\n }\n\n userData.userType = userType;\n\n // if user with same email is already connected, disconnect the previous connection\n // const existingClient =\n // this.clientManager.getClientByKey({\n // key: 'user.email',\n // value: user.email,\n // });\n\n // if (existingClient) {\n // if (existingClient.ws) {\n // this.clientManager.disconnectClient({\n // clientId: existingClient.clientId,\n // });\n // } else {\n // // Publish to Redis that we should disconnect this client\n // this.redisInstance.publisherClient.publish(\n // WebSocketRedisSubscriberEvent.DisconnectClient,\n // JSON.stringify({\n // clientId,\n // workerId: this.workerId,\n // }),\n // );\n // }\n // }\n\n this.onJoinRoom({\n clientId,\n roomName,\n userData,\n });\n\n // Let other workers know that the client has joined the room\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientJoinedRoom,\n JSON.stringify({\n clientId,\n user: userData,\n roomName,\n workerId: this.workerId,\n }),\n );\n\n return true;\n }\n\n public sendClientMessage = (ws: WebSocket, data: unknown, binary: boolean = false): void => {\n const webSocketMessage = JSON.stringify(data);\n\n ws.send(webSocketMessage, { binary });\n };\n\n public sendMessage = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.SendMessage,\n JSON.stringify(formattedData),\n );\n };\n\n public sendMessageToAll = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.SendMessageToAll,\n JSON.stringify(formattedData),\n );\n };\n\n public sendCustomMessage = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(WebSocketRedisSubscriberEvent.Custom, JSON.stringify(formattedData));\n };\n\n public getClients({ userType }: { userType?: string }): any[] {\n return this.clientManager.getClients({ userType });\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scpxl/nodejs-framework",
3
- "version": "1.0.30",
3
+ "version": "1.0.42",
4
4
  "description": "PXL Node.js Framework",
5
5
  "repository": {
6
6
  "type": "git",
@@ -103,7 +103,6 @@
103
103
  ],
104
104
  "scripts": {
105
105
  "dev": "node scripts/hmr-watcher-enhanced.js",
106
- "dev:legacy": "node scripts/hmr-watcher.js",
107
106
  "dev:import-map": "tsx src/index.ts",
108
107
  "clean": "del-cli dist",
109
108
  "build": "npm run clean && node esbuild.config.js",
@@ -149,8 +148,8 @@
149
148
  "@types/yargs": "^17.0.33",
150
149
  "@typescript-eslint/eslint-plugin": "^8.1.0",
151
150
  "@typescript-eslint/parser": "^8.1.0",
152
- "@vitest/coverage-v8": "^3.2.4",
153
- "@vitest/ui": "^3.2.4",
151
+ "@vitest/coverage-v8": "^4.0.6",
152
+ "@vitest/ui": "^4.0.6",
154
153
  "concurrently": "^9.1.2",
155
154
  "del-cli": "^7.0.0",
156
155
  "esbuild": "^0.25.5",
@@ -164,12 +163,12 @@
164
163
  "husky": "^9.1.7",
165
164
  "lint-staged": "^16.1.2",
166
165
  "prettier": "^3.1.1",
167
- "redis-memory-server": "^0.13.0",
166
+ "redis-memory-server": "^0.14.0",
168
167
  "simple-git": "^3.25.0",
169
168
  "tsx": "^4.19.2",
170
169
  "tsc-alias": "^1.8.16",
171
170
  "vitepress": "^1.6.4",
172
- "vitest": "^3.2.4"
171
+ "vitest": "^4.0.6"
173
172
  },
174
173
  "dependencies": {
175
174
  "@aws-sdk/client-s3": "^3.496.0",