dialogue-ts 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.cjs +160 -69
- package/dist/src/index.cjs.map +1 -1
- package/dist/src/index.d.cts +60 -2
- package/dist/src/index.d.ts +60 -2
- package/dist/src/index.js +158 -69
- package/dist/src/index.js.map +1 -1
- package/package.json +4 -2
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/create-dialogue.ts","../../src/history.ts","../../src/logger.ts","../../src/server.ts","../../src/client-handler.ts","../../src/rate-limiter.ts","../../src/room.ts","../../src/define-event.ts"],"sourcesContent":["import { Hono } from \"hono\";\nimport { Err, type Result } from \"slang-ts\";\nimport { createHistoryManager } from \"./history.ts\";\nimport { createDefaultLogger } from \"./logger.ts\";\nimport { setupServer } from \"./server.ts\";\nimport type {\n ClientRooms,\n ConnectedClient,\n Dialogue,\n DialogueConfig,\n EventDefinition,\n EventMessage,\n Logger,\n Room,\n RoomConfig,\n} from \"./types.ts\";\n\n/**\n * Creates a Dialogue instance from configuration.\n * This is the main entry point for the library.\n *\n * @param config - Dialogue configuration with rooms, events, and handlers\n * @returns Dialogue instance for triggering events, subscribing, and server control\n *\n * @example\n * // Basic setup with rooms defined upfront\n * const dialogue = createDialogue({\n * port: 3000,\n * rooms: {\n * chat: {\n * name: 'Chat Room',\n * events: [Message, Typing],\n * defaultSubscriptions: ['message'],\n * syncHistoryOnJoin: true,\n * }\n * },\n * hooks: {\n * clients: {\n * onConnected: (client) => {\n * client.join('chat')\n * },\n * onDisconnected: (client) => {\n * console.log('Client disconnected:', client.userId)\n * }\n * },\n * events: {\n * onCleanup: async (roomId, eventName, events) => {\n * await db.events.insertMany(events)\n * }\n * }\n * }\n * })\n *\n * // Use elsewhere in your app\n * dialogue.trigger('chat', Message, { text: 'Hello!' })\n *\n * // Start the server\n * await dialogue.start()\n */\nexport function createDialogue(config: DialogueConfig): Dialogue {\n const app = config.app ?? new Hono();\n const logger: Logger = config.logger ?? createDefaultLogger();\n\n // Create history manager with hooks for cleanup and external loading\n const historyManager = createHistoryManager({\n onCleanup: config.hooks?.events?.onCleanup,\n onLoad: config.hooks?.events?.onLoad,\n });\n\n const {\n io,\n roomManager,\n start,\n stop,\n getConnectedClient: _getConnectedClient,\n getAllConnectedClients,\n getClientsByUserId,\n getClientRooms: getClientRoomIds,\n isUserInRoom,\n } = setupServer(app, config, historyManager);\n\n const dialogue: Dialogue = {\n app,\n io,\n\n trigger<T>(\n roomId: string,\n event: EventDefinition<T>,\n data: T,\n from?: string,\n meta?: Record<string, unknown>\n ): Result<void, string> {\n const room = roomManager.get(roomId);\n if (!room) {\n const errorMsg = `Room '${roomId}' does not exist`;\n logger.warn({\n message: errorMsg,\n atFunction: \"dialogue.trigger\",\n data: { roomId, eventName: event.name },\n });\n return Err(errorMsg);\n }\n\n return room.trigger(event, data, from, meta);\n },\n\n on<T>(\n roomId: string,\n event: EventDefinition<T>,\n handler: (msg: EventMessage<T>) => void | Promise<void>\n ): () => void {\n const room = roomManager.get(roomId);\n if (!room) {\n logger.warn({\n message: `Room '${roomId}' does not exist`,\n atFunction: \"dialogue.on\",\n data: { roomId, eventName: event.name },\n });\n return () => {\n // No-op unsubscribe for non-existent room\n };\n }\n\n return room.on(event, handler);\n },\n\n room(id: string): Room | null {\n return roomManager.get(id);\n },\n\n rooms(): Room[] {\n return roomManager.all();\n },\n\n createRoom(id: string, config: RoomConfig): Room | null {\n // Check if room already exists\n if (roomManager.get(id)) {\n logger.warn({\n message: `Room '${id}' already exists`,\n atFunction: \"dialogue.createRoom\",\n data: { roomId: id },\n });\n return null;\n }\n\n const room = roomManager.register(id, config);\n\n // Broadcast room created event to all connected clients\n io.emit(\"dialogue:roomCreated\", {\n id: room.id,\n name: room.name,\n description: room.description,\n maxSize: room.maxSize,\n });\n\n logger.info({\n message: `Room '${id}' created`,\n atFunction: \"dialogue.createRoom\",\n data: { roomId: id, roomName: config.name },\n });\n\n return room;\n },\n\n deleteRoom(id: string): boolean {\n const deleted = roomManager.unregister(id);\n\n if (deleted) {\n // Broadcast room deleted event to all connected clients\n io.emit(\"dialogue:roomDeleted\", { roomId: id });\n }\n\n return deleted;\n },\n\n getClients(userId: string): ConnectedClient[] {\n return getClientsByUserId(userId);\n },\n\n getAllClients(): ConnectedClient[] {\n return getAllConnectedClients();\n },\n\n getClientRooms(userId: string): ClientRooms {\n const roomIds = getClientRoomIds(userId);\n const clients = getClientsByUserId(userId);\n\n return {\n ids: roomIds,\n forAll(callback: (roomId: string) => void): void {\n for (const roomId of roomIds) {\n callback(roomId);\n }\n },\n leaveAll(callback?: (roomId: string) => void): void {\n // Execute callback for each room before leaving\n if (callback) {\n for (const roomId of roomIds) {\n callback(roomId);\n }\n }\n\n // Leave all rooms for all connections\n for (const client of clients) {\n for (const roomId of client.rooms()) {\n client.leave(roomId);\n }\n }\n },\n };\n },\n\n isInRoom(userId: string, roomId: string): boolean {\n return isUserInRoom(userId, roomId);\n },\n\n start,\n stop,\n };\n\n return dialogue;\n}\n","import type { EventMessage } from \"./types.ts\";\n\n/**\n * Configuration for the history manager\n */\nexport interface HistoryManagerConfig {\n /** Called when events are evicted from in-memory history (oldest first) */\n onCleanup?: (\n roomId: string,\n eventName: string,\n events: EventMessage[]\n ) => void | Promise<void>;\n /** Called to load historical events beyond in-memory (for pagination) */\n onLoad?: (\n roomId: string,\n eventName: string,\n start: number,\n end: number\n ) => Promise<EventMessage[]>;\n}\n\n/**\n * History manager instance returned by createHistoryManager\n */\nexport interface HistoryManager {\n /** Add an event to history, evict oldest if exceeds limit */\n push(\n roomId: string,\n eventName: string,\n event: EventMessage,\n limit: number\n ): void;\n /** Get events (0 = newest, reads backward) */\n get(\n roomId: string,\n eventName: string,\n start: number,\n end: number\n ): EventMessage[];\n /** Get all events for a room across all event types, sorted newest first */\n getAll(roomId: string, limit?: number): EventMessage[];\n /** Get total count of events for a specific event type in a room */\n count(roomId: string, eventName: string): number;\n /** Clear all history for a room */\n clearRoom(roomId: string): void;\n /** Get event names that have history for a room */\n getEventNames(roomId: string): string[];\n}\n\n/**\n * Creates a history manager for storing events in memory.\n * Stores per-room, per-event-type with FIFO eviction (oldest first).\n *\n * @param config - Configuration with optional cleanup and load callbacks\n * @returns HistoryManager instance\n *\n * @example\n * const history = createHistoryManager({\n * onCleanup: async (roomId, eventName, events) => {\n * await db.events.insertMany(events);\n * }\n * });\n *\n * // Push an event\n * history.push(\"room-1\", \"message\", eventMessage, 50);\n *\n * // Get last 10 messages (newest first)\n * const recent = history.get(\"room-1\", \"message\", 0, 10);\n */\nexport function createHistoryManager(\n config: HistoryManagerConfig = {}\n): HistoryManager {\n // Map<roomId, Map<eventName, EventMessage[]>>\n // Events are stored with newest at the END of the array\n // When retrieving, we reverse the index logic to return newest first\n const store = new Map<string, Map<string, EventMessage[]>>();\n\n /**\n * Ensures the room and event type exist in the store\n */\n function ensureRoomEvent(roomId: string, eventName: string): EventMessage[] {\n let roomHistory = store.get(roomId);\n if (!roomHistory) {\n roomHistory = new Map();\n store.set(roomId, roomHistory);\n }\n\n let eventHistory = roomHistory.get(eventName);\n if (!eventHistory) {\n eventHistory = [];\n roomHistory.set(eventName, eventHistory);\n }\n\n return eventHistory;\n }\n\n return {\n push(\n roomId: string,\n eventName: string,\n event: EventMessage,\n limit: number\n ): void {\n const events = ensureRoomEvent(roomId, eventName);\n\n // Add new event to the end (newest)\n events.push(event);\n\n // Evict oldest (from the beginning) if over limit\n if (events.length > limit) {\n const evictCount = events.length - limit;\n const evicted = events.splice(0, evictCount);\n\n // Call cleanup hook if provided\n if (config.onCleanup && evicted.length > 0) {\n config.onCleanup(roomId, eventName, evicted);\n }\n }\n },\n\n get(\n roomId: string,\n eventName: string,\n start: number,\n end: number\n ): EventMessage[] {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return [];\n }\n\n const events = roomHistory.get(eventName);\n if (!events || events.length === 0) {\n return [];\n }\n\n // Events are stored oldest-to-newest\n // We want to return newest-first, so we reverse the indexing\n // start=0, end=10 means \"last 10 items\" = items from length-10 to length\n const len = events.length;\n const actualStart = Math.max(0, len - end);\n const actualEnd = len - start;\n\n if (actualStart >= actualEnd) {\n return [];\n }\n\n // Slice and reverse to get newest first\n return events.slice(actualStart, actualEnd).reverse();\n },\n\n getAll(roomId: string, limit?: number): EventMessage[] {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return [];\n }\n\n // Collect all events from all event types\n const allEvents: EventMessage[] = [];\n for (const events of roomHistory.values()) {\n allEvents.push(...events);\n }\n\n // Sort by timestamp descending (newest first)\n allEvents.sort((a, b) => b.timestamp - a.timestamp);\n\n // Apply limit if provided\n if (limit !== undefined && limit > 0) {\n return allEvents.slice(0, limit);\n }\n\n return allEvents;\n },\n\n count(roomId: string, eventName: string): number {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return 0;\n }\n\n const events = roomHistory.get(eventName);\n return events?.length ?? 0;\n },\n\n clearRoom(roomId: string): void {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return;\n }\n\n // Call cleanup for each event type before clearing\n if (config.onCleanup) {\n for (const [eventName, events] of roomHistory.entries()) {\n if (events.length > 0) {\n config.onCleanup(roomId, eventName, events);\n }\n }\n }\n\n store.delete(roomId);\n },\n\n getEventNames(roomId: string): string[] {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return [];\n }\n\n return Array.from(roomHistory.keys());\n },\n };\n}\n","import type { LogEntry, Logger } from \"./types.ts\";\n\n/**\n * Creates the default console-based logger for Dialogue.\n * Outputs structured log entries as JSON-like objects.\n *\n * @returns Logger implementation using console methods\n */\nexport function createDefaultLogger(): Logger {\n return {\n debug(entry: LogEntry): void {\n if (process.env.NODE_ENV === \"development\" || process.env.DEBUG) {\n console.debug(\"[Dialogue] [DEBUG]\", entry);\n }\n },\n\n info(entry: LogEntry): void {\n console.info(\"[Dialogue] [INFO]\", entry);\n },\n\n warn(entry: LogEntry): void {\n console.warn(\"[Dialogue] [WARN]\", entry);\n },\n\n error(entry: LogEntry): void {\n console.error(\"[Dialogue] [ERROR]\", entry);\n },\n };\n}\n\n/**\n * Creates a silent logger that does not output anything.\n * Useful for testing or when logging is not desired.\n *\n * @returns Logger implementation that does nothing\n */\nexport function createSilentLogger(): Logger {\n const noop = (): void => {\n // Intentionally empty - silent logger discards all log entries\n };\n return {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n };\n}\n","import { Server as BunEngine } from \"@socket.io/bun-engine\";\nimport type { Hono } from \"hono\";\nimport { cors as honoCors } from \"hono/cors\";\nimport { Server } from \"socket.io\";\nimport {\n createConnectedClient,\n extractUserFromSocket,\n} from \"./client-handler.ts\";\nimport type { HistoryManager } from \"./history.ts\";\nimport { createDefaultLogger } from \"./logger.ts\";\nimport { createRateLimiter } from \"./rate-limiter.ts\";\nimport { createRoomManager, type RoomManagerInstance } from \"./room.ts\";\nimport type {\n ConnectedClient,\n CorsConfig,\n DialogueConfig,\n DialogueContext,\n HooksConfig,\n Logger,\n Room,\n} from \"./types.ts\";\n\n/**\n * Builds Socket.IO CORS options from DialogueConfig cors setting.\n * Defaults to allowing all origins for ease of development.\n */\nfunction buildCorsOptions(cors: CorsConfig | boolean | undefined): {\n origin: string | string[] | boolean;\n methods?: string[];\n credentials?: boolean;\n} {\n // Default: allow all origins (development-friendly)\n if (cors === undefined || cors === true) {\n return {\n origin: true,\n methods: [\"GET\", \"POST\"],\n credentials: true,\n };\n }\n\n // Explicitly disabled\n if (cors === false) {\n return { origin: false };\n }\n\n // Custom config\n return {\n origin: cors.origin,\n methods: cors.methods ?? [\"GET\", \"POST\"],\n credentials: cors.credentials ?? true,\n };\n}\n\n/**\n * Builds Hono CORS middleware options from DialogueConfig cors setting.\n */\nfunction buildHonoCorsOptions(cors: CorsConfig | boolean | undefined): {\n origin: string | string[] | ((origin: string) => string | undefined);\n allowMethods: string[];\n credentials: boolean;\n} {\n // Default: allow all origins (development-friendly)\n if (cors === undefined || cors === true) {\n return {\n origin: \"*\",\n allowMethods: [\"GET\", \"POST\", \"OPTIONS\"],\n credentials: true,\n };\n }\n\n // Explicitly disabled - still need to return something valid\n if (cors === false) {\n return {\n origin: () => undefined,\n allowMethods: [\"GET\", \"POST\"],\n credentials: false,\n };\n }\n\n // Custom config\n const originValue = cors.origin;\n\n let resolvedOrigin:\n | string\n | string[]\n | ((origin: string) => string | undefined);\n if (originValue === true) {\n resolvedOrigin = \"*\";\n } else if (originValue === false) {\n resolvedOrigin = () => undefined;\n } else {\n resolvedOrigin = originValue;\n }\n\n return {\n origin: resolvedOrigin,\n allowMethods: cors.methods ?? [\"GET\", \"POST\", \"OPTIONS\"],\n credentials: cors.credentials ?? true,\n };\n}\n\n/**\n * Adds CORS headers to a response based on the request origin and config.\n */\nfunction addCorsHeaders(\n response: Response,\n request: Request,\n corsConfig: CorsConfig | boolean | undefined\n): Response {\n const origin = request.headers.get(\"Origin\");\n if (!origin) {\n return response;\n }\n\n const headers = new Headers(response.headers);\n\n // Determine allowed origin\n if (corsConfig === undefined || corsConfig === true) {\n headers.set(\"Access-Control-Allow-Origin\", origin);\n headers.set(\"Access-Control-Allow-Credentials\", \"true\");\n } else if (corsConfig === false) {\n return response;\n } else {\n const allowedOrigin = corsConfig.origin;\n if (allowedOrigin === true) {\n headers.set(\"Access-Control-Allow-Origin\", origin);\n } else if (typeof allowedOrigin === \"string\" && allowedOrigin === origin) {\n headers.set(\"Access-Control-Allow-Origin\", origin);\n } else if (Array.isArray(allowedOrigin) && allowedOrigin.includes(origin)) {\n headers.set(\"Access-Control-Allow-Origin\", origin);\n }\n\n if (corsConfig.credentials !== false) {\n headers.set(\"Access-Control-Allow-Credentials\", \"true\");\n }\n }\n\n headers.set(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n headers.set(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\n/**\n * Sends history to a client when they join a room (if syncHistoryOnJoin is enabled)\n */\nfunction sendHistoryOnJoin(\n socket: import(\"socket.io\").Socket,\n roomId: string,\n roomConfig: import(\"./types.ts\").RoomConfig | undefined,\n historyManager: HistoryManager | undefined\n): void {\n if (!(roomConfig?.syncHistoryOnJoin && historyManager)) {\n return;\n }\n\n const limit =\n typeof roomConfig.syncHistoryOnJoin === \"number\"\n ? roomConfig.syncHistoryOnJoin\n : undefined;\n\n const historyEvents = historyManager.getAll(roomId, limit);\n if (historyEvents.length > 0) {\n socket.emit(\"dialogue:history\", {\n roomId,\n events: historyEvents,\n });\n }\n}\n\n/**\n * Helper function to create a DialogueContext from current runtime state.\n * This function converts internal state (Maps) to the Record format expected by DialogueContext.\n */\nfunction createDialogueContext(\n io: Server,\n connectedClients: Map<string, ConnectedClient>,\n roomManager: RoomManagerInstance\n): DialogueContext {\n // Convert Map to Record for clients\n const clientsRecord: Record<string, ConnectedClient> = {};\n for (const [id, client] of connectedClients.entries()) {\n clientsRecord[id] = client;\n }\n\n // Convert room manager's rooms to Record\n const roomsRecord: Record<string, Room> = {};\n for (const room of roomManager.all()) {\n roomsRecord[room.id] = room;\n }\n\n return {\n io,\n clients: clientsRecord,\n rooms: roomsRecord,\n };\n}\n\n/**\n * Sets up the Socket.IO server and wires up all handlers.\n * Handles connection lifecycle and message routing.\n *\n * @param app - Hono app instance\n * @param config - Dialogue configuration\n * @param historyManager - Optional history manager for event storage\n * @returns Server components and lifecycle methods\n */\nexport function setupServer(\n app: Hono,\n config: DialogueConfig,\n historyManager?: HistoryManager\n): {\n io: Server;\n engine: BunEngine;\n roomManager: RoomManagerInstance;\n start: () => Promise<void>;\n stop: () => Promise<void>;\n getConnectedClient: (socketId: string) => ConnectedClient | undefined;\n getAllConnectedClients: () => ConnectedClient[];\n getClientsByUserId: (userId: string) => ConnectedClient[];\n getClientRooms: (userId: string) => string[];\n isUserInRoom: (userId: string, roomId: string) => boolean;\n} {\n const logger: Logger = config.logger ?? createDefaultLogger();\n const hooks: HooksConfig | undefined = config.hooks;\n\n /** Connected clients registry keyed by socket ID (instance-scoped) */\n const connectedClients = new Map<string, ConnectedClient>();\n\n /** Secondary index: userId -> Set of socket IDs for O(1) lookup (instance-scoped) */\n const userIdToSocketIds = new Map<string, Set<string>>();\n\n // Build CORS configuration for Socket.IO\n const corsOptions = buildCorsOptions(config.cors);\n\n // Apply Hono CORS middleware for HTTP requests (needed for Socket.IO polling)\n const honoCorsOptions = buildHonoCorsOptions(config.cors);\n app.use(\"*\", honoCors(honoCorsOptions));\n\n const io = new Server({ cors: corsOptions });\n const engine = new BunEngine();\n\n io.bind(engine);\n\n // Create a helper function to build context - will be passed to roomManager\n const getContextForHooks = (): DialogueContext => {\n return createDialogueContext(io, connectedClients, roomManager);\n };\n\n const roomManager = createRoomManager(\n io,\n logger,\n historyManager,\n hooks,\n getContextForHooks\n );\n\n // Rate limiter for history requests: 20 requests per minute per socket\n const historyRateLimiter = createRateLimiter({\n maxRequests: 20,\n windowMs: 60_000,\n });\n\n for (const [roomId, roomConfig] of Object.entries(config.rooms)) {\n roomManager.register(roomId, roomConfig);\n }\n\n // Wire socket authentication middleware if provided.\n // Runs during handshake — rejects connection before it enters connectedClients.\n if (hooks?.socket?.authenticate) {\n const authenticateHook = hooks.socket.authenticate;\n\n io.use(async (socket, next) => {\n const authData = socket.handshake.auth;\n\n // Build context for the authenticate hook\n // Note: At this point, the client hasn't been added to connectedClients yet\n const context = createDialogueContext(io, connectedClients, roomManager);\n\n try {\n const result = await Promise.resolve(\n authenticateHook({\n context,\n clientSocket: socket,\n authData,\n })\n );\n\n if (result.isErr) {\n logger.warn({\n message: `Authentication rejected: ${result.error}`,\n atFunction: \"setupServer.authenticate\",\n data: { socketId: socket.id },\n });\n return next(new Error(result.error));\n }\n\n // Store authenticated auth data on socket for the connection handler\n // The authData contains jwt field with claims\n socket.data = {\n ...socket.data,\n authenticatedAuthData: result.value,\n };\n\n return next();\n } catch (err) {\n logger.error({\n message: \"Error in authenticate hook\",\n atFunction: \"setupServer.authenticate\",\n data: err,\n });\n return next(new Error(\"Authentication failed\"));\n }\n });\n }\n\n io.on(\"connection\", (socket) => {\n // Use authenticated data from middleware if available, otherwise fall back to extraction\n let userId: string;\n let meta: Record<string, unknown>;\n let authData: import(\"./types.ts\").AuthData | undefined;\n\n if (socket.data?.authenticatedAuthData) {\n authData = socket.data\n .authenticatedAuthData as import(\"./types.ts\").AuthData;\n // Extract userId from JWT claims (sub field is standard for user ID)\n userId = authData.jwt.sub;\n // For now, use empty meta - can be extended later if needed\n meta = {};\n } else {\n const extracted = extractUserFromSocket(socket);\n userId = extracted.userId;\n meta = extracted.meta;\n }\n\n const client = createConnectedClient(\n socket,\n userId,\n meta,\n roomManager,\n logger\n );\n\n // Attach auth data to client if it exists\n if (authData) {\n // We need to update the client's auth field\n // Since ConnectedClient is readonly, we'll need to handle this differently\n // For now, we can cast and assign (TODO: refactor createConnectedClient to accept auth)\n (client as { auth?: import(\"./types.ts\").AuthData }).auth = authData;\n }\n\n connectedClients.set(socket.id, client);\n\n // Add to userId index\n const userSockets = userIdToSocketIds.get(client.userId) ?? new Set();\n userSockets.add(socket.id);\n userIdToSocketIds.set(client.userId, userSockets);\n\n socket.emit(\"dialogue:connected\", {\n clientId: client.id,\n userId: client.userId,\n });\n\n // Call onConnected hook if provided\n if (hooks?.clients?.onConnected) {\n Promise.resolve(hooks.clients.onConnected(client)).catch((err) => {\n logger.error({\n message: \"Error in onConnected hook\",\n atFunction: \"setupServer.onConnected\",\n data: err,\n });\n });\n }\n\n socket.on(\"dialogue:join\", (data: { roomId: string }) => {\n if (typeof data?.roomId !== \"string\") {\n return;\n }\n\n const room = roomManager.get(data.roomId);\n if (!room) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_NOT_FOUND\",\n message: `Room '${data.roomId}' does not exist`,\n });\n return;\n }\n\n // Run beforeJoin hook — can deny room access\n if (hooks?.clients?.beforeJoin) {\n // Build fresh context for this hook call\n const context = createDialogueContext(\n io,\n connectedClients,\n roomManager\n );\n\n const joinResult = hooks.clients.beforeJoin({\n context,\n client,\n roomId: data.roomId,\n room,\n });\n\n if (joinResult.isErr) {\n logger.warn({\n message: `Join denied for room '${data.roomId}': ${joinResult.error}`,\n atFunction: \"setupServer.beforeJoin\",\n data: {\n roomId: data.roomId,\n clientId: client.id,\n reason: joinResult.error,\n },\n });\n socket.emit(\"dialogue:error\", {\n code: \"JOIN_DENIED\",\n message: joinResult.error,\n });\n return;\n }\n }\n\n client.join(data.roomId);\n\n // Call onJoined hook if provided\n if (hooks?.clients?.onJoined) {\n Promise.resolve(hooks.clients.onJoined(client, data.roomId)).catch(\n (err) => {\n logger.error({\n message: \"Error in onJoined hook\",\n atFunction: \"setupServer.onJoined\",\n data: err,\n });\n }\n );\n }\n\n // Send history on join if syncHistoryOnJoin is enabled\n sendHistoryOnJoin(\n socket,\n data.roomId,\n config.rooms[data.roomId],\n historyManager\n );\n });\n\n socket.on(\"dialogue:leave\", (data: { roomId: string }) => {\n if (typeof data?.roomId === \"string\") {\n client.leave(data.roomId);\n\n // Call onLeft hook if provided\n if (hooks?.clients?.onLeft) {\n Promise.resolve(hooks.clients.onLeft(client, data.roomId)).catch(\n (err) => {\n logger.error({\n message: \"Error in onLeft hook\",\n atFunction: \"setupServer.onLeft\",\n data: err,\n });\n }\n );\n }\n }\n });\n\n socket.on(\n \"dialogue:subscribe\",\n (data: { roomId: string; eventName: string }) => {\n if (\n typeof data?.roomId === \"string\" &&\n typeof data?.eventName === \"string\"\n ) {\n client.subscribe(data.roomId, data.eventName);\n }\n }\n );\n\n socket.on(\"dialogue:subscribeAll\", (data: { roomId: string }) => {\n if (typeof data?.roomId === \"string\") {\n client.subscribeAll(data.roomId);\n }\n });\n\n socket.on(\n \"dialogue:unsubscribe\",\n (data: { roomId: string; eventName: string }) => {\n if (\n typeof data?.roomId === \"string\" &&\n typeof data?.eventName === \"string\"\n ) {\n client.unsubscribe(data.roomId, data.eventName);\n }\n }\n );\n\n // Handle history requests from clients (rate limited)\n socket.on(\n \"dialogue:getHistory\",\n async (data: {\n roomId: string;\n eventName?: string;\n start?: number;\n end?: number;\n }) => {\n // Rate limit check: 20 requests per minute per socket\n if (!historyRateLimiter.isAllowed(socket.id)) {\n socket.emit(\"dialogue:error\", {\n code: \"RATE_LIMITED\",\n message:\n \"Too many history requests. Please wait before trying again.\",\n });\n return;\n }\n\n if (typeof data?.roomId !== \"string\") {\n socket.emit(\"dialogue:error\", {\n code: \"INVALID_REQUEST\",\n message: \"roomId is required\",\n });\n return;\n }\n\n const room = roomManager.get(data.roomId);\n if (!room) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_NOT_FOUND\",\n message: `Room '${data.roomId}' does not exist`,\n });\n return;\n }\n\n const start = data.start ?? 0;\n const end = data.end ?? 50;\n\n // If no eventName provided, return empty array (use room.events to get types)\n if (!data.eventName) {\n socket.emit(\"dialogue:historyResponse\", {\n roomId: data.roomId,\n eventName: null,\n events: [],\n start,\n end,\n });\n return;\n }\n\n const events = await room.history(data.eventName, start, end);\n socket.emit(\"dialogue:historyResponse\", {\n roomId: data.roomId,\n eventName: data.eventName,\n events,\n start,\n end,\n });\n }\n );\n\n socket.on(\n \"dialogue:trigger\",\n (data: {\n roomId: string;\n event: string;\n data: unknown;\n meta?: Record<string, unknown>;\n }) => {\n if (\n typeof data?.roomId !== \"string\" ||\n typeof data?.event !== \"string\"\n ) {\n return;\n }\n\n const room = roomManager.get(data.roomId);\n if (!room) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_NOT_FOUND\",\n message: `Room '${data.roomId}' does not exist`,\n });\n return;\n }\n\n const eventDef = room.events.find((e) => e.name === data.event);\n const hasWildcard = room.events.some((e) => e.name === \"*\");\n\n if (!(eventDef || hasWildcard)) {\n socket.emit(\"dialogue:error\", {\n code: \"EVENT_NOT_ALLOWED\",\n message: `Event '${data.event}' is not allowed in room '${data.roomId}'`,\n });\n return;\n }\n\n const triggerEvent = eventDef ?? { name: data.event };\n const result = room.trigger(\n triggerEvent,\n data.data,\n client.userId,\n data.meta\n );\n\n if (result.isErr) {\n socket.emit(\"dialogue:error\", {\n code: \"VALIDATION_FAILED\",\n message: result.error,\n });\n }\n }\n );\n\n socket.on(\"dialogue:listRooms\", () => {\n const rooms = roomManager.all().map((room) => ({\n id: room.id,\n name: room.name,\n description: room.description,\n size: roomManager.getRoomSize(room.id),\n maxSize: room.maxSize,\n }));\n socket.emit(\"dialogue:rooms\", rooms);\n });\n\n socket.on(\n \"dialogue:createRoom\",\n (data: {\n id: string;\n name: string;\n description?: string;\n maxSize?: number;\n }) => {\n if (typeof data?.id !== \"string\" || typeof data?.name !== \"string\") {\n socket.emit(\"dialogue:error\", {\n code: \"INVALID_REQUEST\",\n message: \"Room id and name are required\",\n });\n return;\n }\n\n // Check if room already exists\n if (roomManager.get(data.id)) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_EXISTS\",\n message: `Room '${data.id}' already exists`,\n });\n return;\n }\n\n // Create the room with empty events (open room - any event allowed)\n const room = roomManager.register(data.id, {\n name: data.name,\n description: data.description,\n maxSize: data.maxSize,\n events: [],\n createdById: client.userId,\n });\n\n // Notify the creator\n socket.emit(\"dialogue:roomCreated\", {\n id: room.id,\n name: room.name,\n description: room.description,\n maxSize: room.maxSize,\n createdById: room.createdById,\n });\n\n // Broadcast to all other clients\n socket.broadcast.emit(\"dialogue:roomCreated\", {\n id: room.id,\n name: room.name,\n description: room.description,\n maxSize: room.maxSize,\n createdById: room.createdById,\n });\n\n logger.info({\n message: `Room '${data.id}' created by ${client.userId}`,\n atFunction: \"setupServer.createRoom\",\n data: { roomId: data.id, createdBy: client.userId },\n });\n }\n );\n\n socket.on(\"dialogue:deleteRoom\", (data: { roomId: string }) => {\n if (typeof data?.roomId !== \"string\") {\n return;\n }\n\n const room = roomManager.get(data.roomId);\n if (!room) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_NOT_FOUND\",\n message: `Room '${data.roomId}' does not exist`,\n });\n return;\n }\n\n // Only allow creator or add admin check here\n if (room.createdById && room.createdById !== client.userId) {\n socket.emit(\"dialogue:error\", {\n code: \"PERMISSION_DENIED\",\n message: \"Only the room creator can delete this room\",\n });\n return;\n }\n\n const deleted = roomManager.unregister(data.roomId);\n if (deleted) {\n // Broadcast deletion to all clients\n io.emit(\"dialogue:roomDeleted\", { roomId: data.roomId });\n\n logger.info({\n message: `Room '${data.roomId}' deleted by ${client.userId}`,\n atFunction: \"setupServer.deleteRoom\",\n data: { roomId: data.roomId, deletedBy: client.userId },\n });\n }\n });\n\n socket.on(\"disconnect\", () => {\n // Call onDisconnected hook before cleanup\n if (hooks?.clients?.onDisconnected) {\n Promise.resolve(hooks.clients.onDisconnected(client)).catch((err) => {\n logger.error({\n message: \"Error in onDisconnected hook\",\n atFunction: \"setupServer.onDisconnected\",\n data: err,\n });\n });\n }\n\n roomManager.removeFromAllRooms(client.id);\n connectedClients.delete(socket.id);\n\n // Remove from userId index\n const userSockets = userIdToSocketIds.get(client.userId);\n if (userSockets) {\n userSockets.delete(socket.id);\n if (userSockets.size === 0) {\n userIdToSocketIds.delete(client.userId);\n }\n }\n });\n });\n\n const { websocket } = engine.handler();\n\n const port = config.port ?? 3000;\n\n let bunServer: ReturnType<typeof Bun.serve> | null = null;\n\n return {\n io,\n engine,\n roomManager,\n\n start(): Promise<void> {\n bunServer = Bun.serve({\n port,\n idleTimeout: 30,\n\n async fetch(req: Request, server) {\n const url = new URL(req.url);\n\n if (url.pathname.startsWith(\"/socket.io\")) {\n // Handle OPTIONS preflight requests\n if (req.method === \"OPTIONS\") {\n return addCorsHeaders(\n new Response(null, { status: 204 }),\n req,\n config.cors\n );\n }\n\n const response = await engine.handleRequest(req, server);\n return addCorsHeaders(response, req, config.cors);\n }\n\n return app.fetch(req);\n },\n\n websocket,\n });\n\n logger.info({\n message: `Server running on http://localhost:${port}`,\n atFunction: \"setupServer.start\",\n data: { port },\n });\n return Promise.resolve();\n },\n\n stop(): Promise<void> {\n if (bunServer) {\n bunServer.stop();\n bunServer = null;\n }\n\n for (const client of connectedClients.values()) {\n client.disconnect();\n }\n connectedClients.clear();\n userIdToSocketIds.clear();\n\n io.close();\n logger.info({\n message: \"Server stopped\",\n atFunction: \"setupServer.stop\",\n data: null,\n });\n return Promise.resolve();\n },\n\n /**\n * Gets a connected client by socket ID\n */\n getConnectedClient(socketId: string): ConnectedClient | undefined {\n return connectedClients.get(socketId);\n },\n\n /**\n * Gets all connected clients\n */\n getAllConnectedClients(): ConnectedClient[] {\n return Array.from(connectedClients.values());\n },\n\n /**\n * Gets all connected clients for a specific user ID.\n * Returns array since a user may have multiple connections.\n */\n getClientsByUserId(userId: string): ConnectedClient[] {\n const socketIds = userIdToSocketIds.get(userId);\n if (!socketIds) {\n return [];\n }\n\n const clients: ConnectedClient[] = [];\n for (const socketId of socketIds) {\n const client = connectedClients.get(socketId);\n if (client) {\n clients.push(client);\n }\n }\n return clients;\n },\n\n /**\n * Gets all room IDs that a user is currently in.\n * Aggregates rooms across all connections for this user.\n */\n getClientRooms(userId: string): string[] {\n const clients = this.getClientsByUserId(userId);\n const roomSet = new Set<string>();\n\n for (const client of clients) {\n for (const roomId of client.rooms()) {\n roomSet.add(roomId);\n }\n }\n\n return Array.from(roomSet);\n },\n\n /**\n * Checks if a user is in a specific room (any of their connections)\n */\n isUserInRoom(userId: string, roomId: string): boolean {\n const clients = this.getClientsByUserId(userId);\n return clients.some((client) => client.rooms().includes(roomId));\n },\n };\n}\n","import { nanoid } from \"nanoid\";\nimport type { Socket } from \"socket.io\";\nimport type { RoomManagerInstance } from \"./room.ts\";\nimport type { ConnectedClient, Logger } from \"./types.ts\";\n\n/** Wildcard subscription symbol */\nconst WILDCARD = \"*\";\n\n/**\n * Creates a connected client wrapper for a socket connection.\n * Manages room membership and event subscriptions for the client.\n *\n * @param socket - The Socket.IO socket instance\n * @param userId - Application user ID from authentication\n * @param meta - Additional metadata (role, permissions, etc.)\n * @param roomManager - Room manager instance for coordination\n * @param logger - Logger instance\n * @returns ConnectedClient instance\n */\nexport function createConnectedClient(\n socket: Socket,\n userId: string,\n meta: Record<string, unknown>,\n roomManager: RoomManagerInstance,\n logger: Logger\n): ConnectedClient {\n const id = nanoid();\n const joinedRooms = new Set<string>();\n const subscriptions = new Map<string, Set<string>>();\n\n const client: ConnectedClient = {\n id,\n userId,\n socket,\n meta,\n\n join(roomId: string): void {\n const room = roomManager.get(roomId);\n if (!room) {\n logger.warn({\n message: `Room '${roomId}' does not exist`,\n atFunction: \"client.join\",\n data: { roomId, clientId: id },\n });\n return;\n }\n\n if (joinedRooms.has(roomId)) {\n // Already joined, but still emit confirmation for client requests\n socket.emit(\"dialogue:joined\", { roomId, roomName: room.name });\n return;\n }\n\n const added = roomManager.addParticipant(roomId, client);\n if (!added) {\n logger.warn({\n message: `Room '${roomId}' is full`,\n atFunction: \"client.join\",\n data: { roomId, clientId: id },\n });\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_FULL\",\n message: `Room '${roomId}' is at capacity`,\n });\n return;\n }\n\n joinedRooms.add(roomId);\n subscriptions.set(roomId, new Set());\n\n for (const eventName of room.defaultSubscriptions) {\n this.subscribe(roomId, eventName);\n }\n\n socket.emit(\"dialogue:joined\", { roomId, roomName: room.name });\n },\n\n leave(roomId: string): void {\n if (!joinedRooms.has(roomId)) {\n return;\n }\n\n roomManager.removeParticipant(roomId, id);\n joinedRooms.delete(roomId);\n subscriptions.delete(roomId);\n\n socket.emit(\"dialogue:left\", { roomId });\n },\n\n subscribe(roomId: string, eventName: string): void {\n if (!joinedRooms.has(roomId)) {\n logger.warn({\n message: `Cannot subscribe to '${eventName}' - not in room '${roomId}'`,\n atFunction: \"client.subscribe\",\n data: { roomId, eventName, clientId: id },\n });\n return;\n }\n\n const roomSubs = subscriptions.get(roomId);\n if (roomSubs) {\n roomSubs.add(eventName);\n }\n },\n\n subscribeAll(roomId: string): void {\n this.subscribe(roomId, WILDCARD);\n },\n\n unsubscribe(roomId: string, eventName: string): void {\n const roomSubs = subscriptions.get(roomId);\n if (roomSubs) {\n roomSubs.delete(eventName);\n }\n },\n\n rooms(): string[] {\n return Array.from(joinedRooms);\n },\n\n subscriptions(roomId: string): string[] {\n const roomSubs = subscriptions.get(roomId);\n return roomSubs ? Array.from(roomSubs) : [];\n },\n\n send<T>(event: string, data: T): void {\n socket.emit(event, data);\n },\n\n disconnect(): void {\n for (const roomId of joinedRooms) {\n roomManager.removeParticipant(roomId, id);\n }\n joinedRooms.clear();\n subscriptions.clear();\n socket.disconnect(true);\n },\n };\n\n return client;\n}\n\n/**\n * Checks if a client is subscribed to an event in a room.\n * Returns true if subscribed to the specific event or wildcard.\n */\nexport function isSubscribedToEvent(\n client: ConnectedClient,\n roomId: string,\n eventName: string\n): boolean {\n const subs = client.subscriptions(roomId);\n return subs.includes(eventName) || subs.includes(WILDCARD);\n}\n\n/**\n * Extracts user ID and metadata from socket handshake.\n * Override this for custom authentication strategies.\n */\nexport function extractUserFromSocket(socket: Socket): {\n userId: string;\n meta: Record<string, unknown>;\n} {\n const auth = socket.handshake.auth as Record<string, unknown>;\n\n let userId = socket.id;\n if (typeof auth.userId === \"string\") {\n userId = auth.userId;\n } else if (typeof auth.token === \"string\") {\n userId = auth.token;\n }\n\n const meta: Record<string, unknown> = {};\n\n if (typeof auth.role === \"string\") {\n meta.role = auth.role;\n }\n\n for (const [key, value] of Object.entries(auth)) {\n if (key !== \"userId\" && key !== \"token\") {\n meta[key] = value;\n }\n }\n\n return { userId, meta };\n}\n","/**\n * Rate limiter configuration\n */\nexport interface RateLimiterConfig {\n /** Maximum number of requests allowed in the window */\n maxRequests: number;\n /** Time window in milliseconds */\n windowMs: number;\n}\n\n/**\n * Rate limiter instance returned by createRateLimiter\n */\nexport interface RateLimiter {\n /**\n * Check if a request is allowed for the given key.\n * Returns true if allowed, false if rate limited.\n */\n isAllowed(key: string): boolean;\n /**\n * Get remaining requests for the given key\n */\n remaining(key: string): number;\n /**\n * Clear all rate limit data (useful for testing)\n */\n clear(): void;\n}\n\ninterface RateLimitEntry {\n count: number;\n resetAt: number;\n}\n\n/**\n * Creates a simple in-memory rate limiter using sliding window algorithm.\n * Tracks requests per key (typically socket ID or user ID) and enforces limits.\n *\n * @param config - Rate limiter configuration\n * @returns RateLimiter instance\n *\n * @example\n * const limiter = createRateLimiter({ maxRequests: 10, windowMs: 60000 });\n *\n * if (!limiter.isAllowed(socketId)) {\n * socket.emit(\"dialogue:error\", { code: \"RATE_LIMITED\" });\n * return;\n * }\n */\nexport function createRateLimiter(config: RateLimiterConfig): RateLimiter {\n const entries = new Map<string, RateLimitEntry>();\n\n // Cleanup expired entries periodically to prevent memory leaks\n const cleanupInterval = setInterval(() => {\n const now = Date.now();\n for (const [key, entry] of entries) {\n if (now >= entry.resetAt) {\n entries.delete(key);\n }\n }\n }, config.windowMs);\n\n // Prevent the interval from keeping the process alive\n cleanupInterval.unref?.();\n\n return {\n isAllowed(key: string): boolean {\n const now = Date.now();\n const entry = entries.get(key);\n\n // No existing entry or window expired - allow and reset\n if (!entry || now >= entry.resetAt) {\n entries.set(key, {\n count: 1,\n resetAt: now + config.windowMs,\n });\n return true;\n }\n\n // Within window - check count\n if (entry.count >= config.maxRequests) {\n return false;\n }\n\n // Increment and allow\n entry.count++;\n return true;\n },\n\n remaining(key: string): number {\n const now = Date.now();\n const entry = entries.get(key);\n\n if (!entry || now >= entry.resetAt) {\n return config.maxRequests;\n }\n\n return Math.max(0, config.maxRequests - entry.count);\n },\n\n clear(): void {\n entries.clear();\n },\n };\n}\n","import { Err, Ok, type Result } from \"slang-ts\";\nimport type { Server } from \"socket.io\";\nimport {\n getEventByName,\n isEventAllowed,\n validateEventData,\n} from \"./define-event.ts\";\nimport type { HistoryManager } from \"./history.ts\";\nimport type {\n ConnectedClient,\n DialogueContext,\n EventDefinition,\n EventMessage,\n HooksConfig,\n Logger,\n Room,\n RoomConfig,\n} from \"./types.ts\";\n\n/** Wildcard subscription matches all events */\nconst WILDCARD = \"*\";\n\n/**\n * Checks if a participant is subscribed to an event in a room.\n * Returns true for specific event subscription or wildcard.\n */\nfunction isParticipantSubscribed(\n participant: ConnectedClient,\n roomId: string,\n eventName: string\n): boolean {\n const subs = participant.subscriptions(roomId);\n return subs.includes(eventName) || subs.includes(WILDCARD);\n}\n\n/** Handler function for room events */\ntype EventHandler<T = unknown> = (msg: EventMessage<T>) => void | Promise<void>;\n\n/**\n * Creates a room instance with event management and participant tracking.\n * The room manages participants, subscriptions, and event broadcasting.\n *\n * @param id - Unique room identifier\n * @param config - Room configuration\n * @param _io - Socket.IO server instance (reserved for future use)\n * @param logger - Logger instance\n * @param historyManager - Optional history manager for event storage\n * @param hooks - Optional hooks config for event callbacks\n * @param externalParticipants - Optional shared participants Map (used by room manager)\n * @param getContext - Function to retrieve current DialogueContext for hook calls\n * @returns Room instance with methods\n */\nexport function createRoom(\n id: string,\n config: RoomConfig,\n _io: Server,\n logger: Logger,\n historyManager?: HistoryManager,\n hooks?: HooksConfig,\n externalParticipants?: Map<string, ConnectedClient>,\n getContext?: () => DialogueContext\n): Room {\n const participants =\n externalParticipants ?? new Map<string, ConnectedClient>();\n const eventHandlers = new Map<string, Set<EventHandler>>();\n\n /** Handles post-broadcast work: history storage, hooks, and server-side handlers */\n /**\n * Validates event is allowed and data is valid\n */\n function validateEvent<T>(\n event: EventDefinition<T>,\n data: T\n ): Result<T, string> {\n if (!isEventAllowed(event.name, config.events)) {\n const errorMsg = `Event '${event.name}' is not allowed in room '${id}'`;\n logger.warn({\n message: errorMsg,\n atFunction: \"room.trigger\",\n data: { eventName: event.name, roomId: id },\n });\n return Err(errorMsg);\n }\n\n const eventDef = getEventByName(event.name, config.events) ?? event;\n const validation = validateEventData(eventDef, data);\n\n if (validation.isErr) {\n logger.warn({\n message: validation.error,\n atFunction: \"room.trigger\",\n data: {\n eventName: event.name,\n roomId: id,\n validationError: validation.error,\n },\n });\n return Err(validation.error);\n }\n\n return Ok(validation.value as T);\n }\n\n /**\n * Runs beforeEach hook if configured\n */\n function runBeforeEachHook<T>(\n event: EventDefinition<T>,\n message: EventMessage<T>\n ): Result<EventMessage, string> {\n if (!(hooks?.events?.beforeEach && getContext)) {\n return Ok(message);\n }\n\n const context = getContext();\n const hookResult = hooks.events.beforeEach({\n context,\n roomId: id,\n message,\n from: message.from,\n });\n\n if (hookResult.isErr) {\n logger.debug({\n message: `Event '${event.name}' blocked by beforeEach: ${hookResult.error}`,\n atFunction: \"room.trigger.beforeEach\",\n data: {\n eventName: event.name,\n roomId: id,\n reason: hookResult.error,\n },\n });\n return Err(hookResult.error);\n }\n\n return Ok(hookResult.value);\n }\n\n /**\n * Broadcasts message to subscribed participants\n */\n function broadcastToParticipants(\n eventName: string,\n message: EventMessage\n ): number {\n let recipientCount = 0;\n for (const [, participant] of participants) {\n if (isParticipantSubscribed(participant, id, eventName)) {\n participant.socket.emit(\"dialogue:event\", message);\n recipientCount++;\n }\n }\n return recipientCount;\n }\n\n /**\n * Runs afterEach hook if configured\n */\n function runAfterEachHook(\n message: EventMessage,\n recipientCount: number\n ): void {\n if (!(hooks?.events?.afterEach && getContext)) {\n return;\n }\n\n const context = getContext();\n hooks.events.afterEach({\n context,\n roomId: id,\n message,\n recipientCount,\n });\n }\n\n function handlePostBroadcast(eventName: string, message: EventMessage): void {\n // Store in history if event has history enabled\n const eventDef = getEventByName(eventName, config.events);\n if (eventDef?.history?.enabled && historyManager) {\n historyManager.push(id, eventName, message, eventDef.history.limit);\n }\n\n // Call onTriggered hook if provided\n if (hooks?.events?.onTriggered) {\n Promise.resolve(hooks.events.onTriggered(id, message)).catch((err) => {\n logger.error({\n message: `Error in onTriggered hook for '${eventName}'`,\n atFunction: \"room.trigger.onTriggered\",\n data: err,\n });\n });\n }\n\n // Call server-side event handlers\n const handlers = eventHandlers.get(eventName);\n if (handlers) {\n for (const handler of handlers) {\n Promise.resolve(handler(message)).catch((err) => {\n logger.error({\n message: `Error in event handler for '${eventName}'`,\n atFunction: \"room.trigger.handler\",\n data: err,\n });\n });\n }\n }\n }\n\n const room: Room = {\n id,\n name: config.name,\n description: config.description,\n maxSize: config.maxSize,\n events: config.events,\n defaultSubscriptions: config.defaultSubscriptions ?? [],\n createdById: config.createdById,\n\n trigger<T>(\n event: EventDefinition<T>,\n data: T,\n from?: string,\n meta?: Record<string, unknown>\n ): Result<void, string> {\n const validation = validateEvent(event, data);\n if (validation.isErr) {\n return Err(validation.error);\n }\n\n const message: EventMessage<T> = {\n event: event.name,\n roomId: id,\n data: validation.value as T,\n from: from ?? \"system\",\n timestamp: Date.now(),\n ...(meta && { meta }),\n };\n\n const hookResult = runBeforeEachHook(event, message);\n if (hookResult.isErr) {\n return Err(hookResult.error);\n }\n\n const finalMessage = hookResult.value;\n const recipientCount = broadcastToParticipants(event.name, finalMessage);\n\n handlePostBroadcast(event.name, finalMessage);\n runAfterEachHook(finalMessage, recipientCount);\n\n return Ok(undefined);\n },\n\n on<T>(\n event: EventDefinition<T>,\n handler: (msg: EventMessage<T>) => void | Promise<void>\n ): () => void {\n let handlers = eventHandlers.get(event.name);\n if (!handlers) {\n handlers = new Set();\n eventHandlers.set(event.name, handlers);\n }\n\n handlers.add(handler as EventHandler);\n\n return () => {\n handlers?.delete(handler as EventHandler);\n if (handlers?.size === 0) {\n eventHandlers.delete(event.name);\n }\n };\n },\n\n size(): number {\n return participants.size;\n },\n\n isFull(): boolean {\n if (config.maxSize === undefined) {\n return false;\n }\n return participants.size >= config.maxSize;\n },\n\n participants(): ConnectedClient[] {\n return Array.from(participants.values());\n },\n\n async history(\n eventName: string,\n start: number,\n end: number\n ): Promise<EventMessage[]> {\n // Return empty array if no history manager\n if (!historyManager) {\n return [];\n }\n\n // Get from in-memory history first\n const inMemoryEvents = historyManager.get(id, eventName, start, end);\n const inMemoryCount = historyManager.count(id, eventName);\n\n // If we have enough in-memory or no onLoad hook, return what we have\n if (inMemoryEvents.length >= end - start || !hooks?.events?.onLoad) {\n return inMemoryEvents;\n }\n\n // If requesting beyond in-memory, try loading from external storage\n // Only load what's missing\n const missingStart = Math.max(start, inMemoryCount);\n const missingEnd = end;\n\n if (missingStart >= missingEnd) {\n return inMemoryEvents;\n }\n\n try {\n const externalEvents = await hooks.events.onLoad(\n id,\n eventName,\n missingStart - inMemoryCount,\n missingEnd - inMemoryCount\n );\n\n // Combine in-memory and external events\n return [...inMemoryEvents, ...externalEvents];\n } catch (err) {\n logger.error({\n message: `Error loading history for '${eventName}'`,\n atFunction: \"room.history.onLoad\",\n data: err,\n });\n return inMemoryEvents;\n }\n },\n };\n\n return room;\n}\n\n/**\n * Internal: Adds a participant to the room.\n * Used by the room manager, not directly by consumers.\n */\nexport function addParticipantToRoom(\n room: Room,\n client: ConnectedClient,\n participants: Map<string, ConnectedClient>\n): boolean {\n // Check capacity using the passed-in participants map (room manager's map)\n if (room.maxSize !== undefined && participants.size >= room.maxSize) {\n return false;\n }\n\n participants.set(client.id, client);\n client.socket.join(room.id);\n\n return true;\n}\n\n/**\n * Internal: Removes a participant from the room.\n */\nexport function removeParticipantFromRoom(\n room: Room,\n clientId: string,\n participants: Map<string, ConnectedClient>\n): void {\n const client = participants.get(clientId);\n if (client) {\n client.socket.leave(room.id);\n participants.delete(clientId);\n }\n}\n\n/**\n * Creates a room manager that coordinates all rooms.\n * Used internally by createDialogue.\n *\n * @param io - Socket.IO server instance\n * @param logger - Logger instance\n * @param historyManager - Optional history manager for event storage\n * @param hooks - Optional hooks config for lifecycle callbacks\n */\nexport function createRoomManager(\n io: Server,\n logger: Logger,\n historyManager?: HistoryManager,\n hooks?: HooksConfig,\n getContext?: () => DialogueContext\n) {\n const rooms = new Map<string, Room>();\n const roomParticipants = new Map<string, Map<string, ConnectedClient>>();\n\n return {\n /**\n * Registers a room from config\n */\n register(id: string, config: RoomConfig): Room {\n const participantsMap = new Map<string, ConnectedClient>();\n roomParticipants.set(id, participantsMap);\n const room = createRoom(\n id,\n config,\n io,\n logger,\n historyManager,\n hooks,\n participantsMap,\n getContext\n );\n rooms.set(id, room);\n\n // Call onCreated hook if provided\n if (hooks?.rooms?.onCreated) {\n Promise.resolve(hooks.rooms.onCreated(room)).catch((err) => {\n logger.error({\n message: `Error in onCreated hook for room '${id}'`,\n atFunction: \"roomManager.register.onCreated\",\n data: err,\n });\n });\n }\n\n return room;\n },\n\n /**\n * Gets a room by ID\n */\n get(id: string): Room | null {\n return rooms.get(id) ?? null;\n },\n\n /**\n * Gets all rooms\n */\n all(): Room[] {\n return Array.from(rooms.values());\n },\n\n /**\n * Adds a participant to a room\n */\n addParticipant(roomId: string, client: ConnectedClient): boolean {\n const room = rooms.get(roomId);\n const participants = roomParticipants.get(roomId);\n\n if (!(room && participants)) {\n return false;\n }\n\n return addParticipantToRoom(room, client, participants);\n },\n\n /**\n * Removes a participant from a room\n */\n removeParticipant(roomId: string, clientId: string): void {\n const room = rooms.get(roomId);\n const participants = roomParticipants.get(roomId);\n\n if (room && participants) {\n removeParticipantFromRoom(room, clientId, participants);\n }\n },\n\n /**\n * Removes a client from all rooms\n */\n removeFromAllRooms(clientId: string): void {\n for (const [roomId] of rooms) {\n this.removeParticipant(roomId, clientId);\n }\n },\n\n /**\n * Gets participants for a room\n */\n getParticipants(roomId: string): ConnectedClient[] {\n const participants = roomParticipants.get(roomId);\n return participants ? Array.from(participants.values()) : [];\n },\n\n /**\n * Gets room size\n */\n getRoomSize(roomId: string): number {\n return roomParticipants.get(roomId)?.size ?? 0;\n },\n\n /**\n * Unregisters (deletes) a room by ID.\n * Removes all participants and cleans up resources.\n * @returns true if room was deleted, false if it didn't exist\n */\n unregister(id: string): boolean {\n const room = rooms.get(id);\n if (!room) {\n return false;\n }\n\n // Remove all participants from the room\n const participants = roomParticipants.get(id);\n if (participants) {\n for (const client of participants.values()) {\n client.socket.leave(id);\n }\n participants.clear();\n }\n\n // Clear history for this room\n if (historyManager) {\n historyManager.clearRoom(id);\n }\n\n // Notify all sockets in the room that it's been deleted\n io.to(id).emit(\"dialogue:roomDeleted\", { roomId: id });\n\n rooms.delete(id);\n roomParticipants.delete(id);\n\n logger.info({\n message: `Room '${id}' deleted`,\n atFunction: \"roomManager.unregister\",\n data: { roomId: id },\n });\n\n // Call onDeleted hook if provided\n if (hooks?.rooms?.onDeleted) {\n Promise.resolve(hooks.rooms.onDeleted(id)).catch((err) => {\n logger.error({\n message: `Error in onDeleted hook for room '${id}'`,\n atFunction: \"roomManager.unregister.onDeleted\",\n data: err,\n });\n });\n }\n\n return true;\n },\n };\n}\n\nexport type RoomManagerInstance = ReturnType<typeof createRoomManager>;\n","import { Err, Ok, type Result } from \"slang-ts\";\nimport type { z } from \"zod\";\nimport type { EventDefinition, EventHistoryConfig } from \"./types.ts\";\n\n/**\n * Options for defining an event\n */\ninterface DefineEventOptions<T> {\n /** Zod schema for validating event data */\n schema?: z.ZodType<T>;\n /** Human-readable description of the event */\n description?: string;\n /** History configuration - when enabled, events are stored in memory */\n history?: EventHistoryConfig;\n}\n\n/**\n * Creates a typed event definition for use in rooms.\n * Events are immutable once created.\n *\n * @param name - Unique event name (e.g., 'message', 'order:updated')\n * @param options - Optional schema and description\n * @returns Frozen event definition object\n *\n * @example\n * // Simple event without validation\n * const Typing = defineEvent('typing')\n *\n * @example\n * // Event with Zod schema validation\n * const Message = defineEvent('message', {\n * schema: z.object({\n * text: z.string().min(1).max(1000),\n * senderId: z.string()\n * }),\n * description: 'Chat message sent by a user',\n * history: { enabled: true, limit: 50 }\n * })\n *\n * @example\n * // Event with inferred type from schema\n * const OrderUpdated = defineEvent('order:updated', {\n * schema: z.object({\n * orderId: z.string(),\n * status: z.enum(['pending', 'shipped', 'delivered'])\n * })\n * })\n * // Type of data is inferred as { orderId: string, status: 'pending' | 'shipped' | 'delivered' }\n */\nexport function defineEvent<T = unknown>(\n name: string,\n options?: DefineEventOptions<T>\n): EventDefinition<T> {\n const definition: EventDefinition<T> = {\n name,\n description: options?.description,\n schema: options?.schema,\n history: options?.history,\n };\n\n return Object.freeze(definition);\n}\n\n/**\n * Validates event data against its schema if one exists.\n * Returns a Result with either parsed data or error message.\n *\n * @param event - The event definition with optional schema\n * @param data - Data to validate\n * @returns Result<T, string> - Ok with data or Err with validation message\n */\nexport function validateEventData<T>(\n event: EventDefinition<T>,\n data: unknown\n): Result<T, string> {\n if (!event.schema) {\n return Ok(data as T);\n }\n\n const result = event.schema.safeParse(data);\n\n if (result.success) {\n return Ok(result.data);\n }\n\n const errorMessages = result.error.issues\n .map((issue) => `${issue.path.join(\".\")}: ${issue.message}`)\n .join(\", \");\n\n return Err(`Event '${event.name}' validation failed: ${errorMessages}`);\n}\n\n/**\n * Checks if an event is allowed in a room based on its event definitions.\n * - Empty array: No events allowed (reject all)\n * - Wildcard \"*\": All events allowed (accept all)\n * - Specific events: Only listed events allowed\n *\n * @param eventName - Name of the event to check\n * @param allowedEvents - List of allowed events for the room\n * @returns True if event is allowed (explicit match or wildcard)\n */\nexport function isEventAllowed(\n eventName: string,\n allowedEvents: EventDefinition<unknown>[]\n): boolean {\n // Empty array means no events allowed\n if (allowedEvents.length === 0) {\n return false;\n }\n\n // Check for wildcard or specific event match\n return allowedEvents.some((e) => e.name === \"*\" || e.name === eventName);\n}\n\n/**\n * Gets an event definition by name from a list of events.\n *\n * @param eventName - Name of the event to find\n * @param events - List of event definitions to search\n * @returns The event definition or undefined if not found\n */\nexport function getEventByName(\n eventName: string,\n events: EventDefinition<unknown>[]\n): EventDefinition<unknown> | undefined {\n return events.find((e) => e.name === eventName);\n}\n"],"mappings":"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","names":["Err","Err","Ok","WILDCARD","Err","Ok","userSockets","Err","config"]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/bun-adapter.ts","../../src/adapters/node-adapter.ts","../../src/adapters/index.ts","../../src/create-dialogue.ts","../../src/history.ts","../../src/logger.ts","../../src/server.ts","../../src/client-handler.ts","../../src/rate-limiter.ts","../../src/room.ts","../../src/define-event.ts"],"sourcesContent":["import { Server as BunEngine } from \"@socket.io/bun-engine\";\nimport type { Server } from \"socket.io\";\nimport type { CorsConfig } from \"../types.ts\";\nimport type { RuntimeAdapter, RuntimeStartOptions } from \"./types.ts\";\n\n/**\n * Adds CORS headers to a response based on the request origin and config.\n * Needed for Socket.IO polling transport on the Bun adapter since\n * Bun.serve handles fetch directly without Hono middleware for /socket.io.\n */\nfunction addCorsHeaders(\n response: Response,\n request: Request,\n corsConfig: CorsConfig | boolean | undefined\n): Response {\n const origin = request.headers.get(\"Origin\");\n if (!origin) {\n return response;\n }\n\n const headers = new Headers(response.headers);\n\n if (corsConfig === undefined || corsConfig === true) {\n headers.set(\"Access-Control-Allow-Origin\", origin);\n headers.set(\"Access-Control-Allow-Credentials\", \"true\");\n } else if (corsConfig === false) {\n return response;\n } else {\n const allowedOrigin = corsConfig.origin;\n if (allowedOrigin === true) {\n headers.set(\"Access-Control-Allow-Origin\", origin);\n } else if (typeof allowedOrigin === \"string\" && allowedOrigin === origin) {\n headers.set(\"Access-Control-Allow-Origin\", origin);\n } else if (Array.isArray(allowedOrigin) && allowedOrigin.includes(origin)) {\n headers.set(\"Access-Control-Allow-Origin\", origin);\n }\n\n if (corsConfig.credentials !== false) {\n headers.set(\"Access-Control-Allow-Credentials\", \"true\");\n }\n }\n\n headers.set(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n headers.set(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\n/**\n * Creates a runtime adapter for Bun.\n * Uses @socket.io/bun-engine for WebSocket transport and Bun.serve() for HTTP.\n */\nexport function createBunAdapter(): RuntimeAdapter {\n const engine = new BunEngine();\n let bunServer: ReturnType<typeof Bun.serve> | null = null;\n\n return {\n runtime: \"bun\",\n\n bind(io: Server): void {\n io.bind(engine);\n },\n\n start({\n port,\n app,\n corsConfig,\n logger,\n }: RuntimeStartOptions): Promise<void> {\n const { websocket } = engine.handler();\n\n bunServer = Bun.serve({\n port,\n idleTimeout: 30,\n\n async fetch(req: Request, server) {\n const url = new URL(req.url);\n\n if (url.pathname.startsWith(\"/socket.io\")) {\n if (req.method === \"OPTIONS\") {\n return addCorsHeaders(\n new Response(null, { status: 204 }),\n req,\n corsConfig\n );\n }\n\n const response = await engine.handleRequest(req, server);\n return addCorsHeaders(response, req, corsConfig);\n }\n\n return app.fetch(req);\n },\n\n websocket,\n });\n\n logger.info({\n message: `Server running on http://localhost:${port} (bun)`,\n atFunction: \"bunAdapter.start\",\n data: { port, runtime: \"bun\" },\n });\n\n return Promise.resolve();\n },\n\n stop(): Promise<void> {\n if (bunServer) {\n bunServer.stop();\n bunServer = null;\n }\n return Promise.resolve();\n },\n };\n}\n","import { createServer, type Server as HttpServer } from \"node:http\";\nimport { getRequestListener } from \"@hono/node-server\";\nimport type { Server } from \"socket.io\";\nimport type { RuntimeAdapter, RuntimeStartOptions } from \"./types.ts\";\n\n/**\n * Creates a runtime adapter for Node.js.\n * Uses Node's built-in http.createServer() and Socket.IO's native engine.io\n * (no special engine adapter needed — Socket.IO was designed for Node).\n */\nexport function createNodeAdapter(): RuntimeAdapter {\n let httpServer: HttpServer | null = null;\n let socketIoServer: Server | null = null;\n\n return {\n runtime: \"node\",\n\n bind(io: Server): void {\n // Socket.IO on Node doesn't need an explicit engine bind.\n // We store the reference and attach it in start() when the http server exists.\n socketIoServer = io;\n },\n\n start({ port, app, io, logger }: RuntimeStartOptions): Promise<void> {\n // Bridge Hono's fetch API to Node's req/res via @hono/node-server\n const requestListener = getRequestListener(app.fetch);\n\n httpServer = createServer(requestListener);\n\n // Attach Socket.IO to the Node http server.\n // Socket.IO uses its built-in engine.io for Node — handles both\n // long-polling and WebSocket upgrade automatically.\n io.attach(httpServer);\n\n return new Promise((resolve) => {\n httpServer?.listen(port, () => {\n logger.info({\n message: `Server running on http://localhost:${port} (node)`,\n atFunction: \"nodeAdapter.start\",\n data: { port, runtime: \"node\" },\n });\n resolve();\n });\n });\n },\n\n stop(): Promise<void> {\n return new Promise((resolve) => {\n if (socketIoServer) {\n socketIoServer = null;\n }\n\n if (httpServer) {\n httpServer.close(() => {\n httpServer = null;\n resolve();\n });\n } else {\n resolve();\n }\n });\n },\n };\n}\n","import { createBunAdapter } from \"./bun-adapter.ts\";\nimport { createNodeAdapter } from \"./node-adapter.ts\";\nimport type { Runtime, RuntimeAdapter } from \"./types.ts\";\n\n/**\n * Detects the current JavaScript runtime by checking for Bun globals.\n * Falls back to \"node\" if Bun is not detected.\n */\nexport function detectRuntime(): Runtime {\n // biome-ignore lint/suspicious/noExplicitAny: runtime detection requires checking unknown global\n if (typeof (globalThis as any).Bun !== \"undefined\") {\n return \"bun\";\n }\n return \"node\";\n}\n\n/**\n * Creates the appropriate runtime adapter based on the specified runtime.\n * Uses auto-detection if no runtime is specified.\n *\n * @param runtime - Explicit runtime choice, or auto-detected if omitted\n * @returns A RuntimeAdapter for the target runtime\n */\nexport function createRuntimeAdapter(runtime?: Runtime): RuntimeAdapter {\n const resolved = runtime ?? detectRuntime();\n\n const adapters: Record<Runtime, () => RuntimeAdapter> = {\n bun: createBunAdapter,\n node: createNodeAdapter,\n };\n\n return adapters[resolved]();\n}\n\nexport type { Runtime, RuntimeAdapter, RuntimeStartOptions } from \"./types.ts\";\n","import { Hono } from \"hono\";\nimport { Err, type Result } from \"slang-ts\";\nimport { createRuntimeAdapter } from \"./adapters/index.ts\";\nimport { createHistoryManager } from \"./history.ts\";\nimport { createDefaultLogger } from \"./logger.ts\";\nimport { setupServer } from \"./server.ts\";\nimport type {\n ClientRooms,\n ConnectedClient,\n Dialogue,\n DialogueConfig,\n EventDefinition,\n EventMessage,\n Logger,\n Room,\n RoomConfig,\n} from \"./types.ts\";\n\n/**\n * Creates a Dialogue instance from configuration.\n * This is the main entry point for the library.\n *\n * @param config - Dialogue configuration with rooms, events, and handlers\n * @returns Dialogue instance for triggering events, subscribing, and server control\n *\n * @example\n * // Basic setup with rooms defined upfront\n * const dialogue = createDialogue({\n * port: 3000,\n * rooms: {\n * chat: {\n * name: 'Chat Room',\n * events: [Message, Typing],\n * defaultSubscriptions: ['message'],\n * syncHistoryOnJoin: true,\n * }\n * },\n * hooks: {\n * clients: {\n * onConnected: (client) => {\n * client.join('chat')\n * },\n * onDisconnected: (client) => {\n * console.log('Client disconnected:', client.userId)\n * }\n * },\n * events: {\n * onCleanup: async (roomId, eventName, events) => {\n * await db.events.insertMany(events)\n * }\n * }\n * }\n * })\n *\n * // Use elsewhere in your app\n * dialogue.trigger('chat', Message, { text: 'Hello!' })\n *\n * // Start the server\n * await dialogue.start()\n */\nexport function createDialogue(config: DialogueConfig): Dialogue {\n const app = config.app ?? new Hono();\n const logger: Logger = config.logger ?? createDefaultLogger();\n\n // Create history manager with hooks for cleanup and external loading\n const historyManager = createHistoryManager({\n onCleanup: config.hooks?.events?.onCleanup,\n onLoad: config.hooks?.events?.onLoad,\n });\n\n // Create the runtime adapter (auto-detects if not specified)\n const adapter = createRuntimeAdapter(config.runtime);\n\n const {\n io,\n roomManager,\n start,\n stop,\n getConnectedClient: _getConnectedClient,\n getAllConnectedClients,\n getClientsByUserId,\n getClientRooms: getClientRoomIds,\n isUserInRoom,\n } = setupServer(app, config, adapter, historyManager);\n\n const dialogue: Dialogue = {\n app,\n io,\n\n trigger<T>(\n roomId: string,\n event: EventDefinition<T>,\n data: T,\n from?: string,\n meta?: Record<string, unknown>\n ): Result<void, string> {\n const room = roomManager.get(roomId);\n if (!room) {\n const errorMsg = `Room '${roomId}' does not exist`;\n logger.warn({\n message: errorMsg,\n atFunction: \"dialogue.trigger\",\n data: { roomId, eventName: event.name },\n });\n return Err(errorMsg);\n }\n\n return room.trigger(event, data, from, meta);\n },\n\n on<T>(\n roomId: string,\n event: EventDefinition<T>,\n handler: (msg: EventMessage<T>) => void | Promise<void>\n ): () => void {\n const room = roomManager.get(roomId);\n if (!room) {\n logger.warn({\n message: `Room '${roomId}' does not exist`,\n atFunction: \"dialogue.on\",\n data: { roomId, eventName: event.name },\n });\n return () => {\n // No-op unsubscribe for non-existent room\n };\n }\n\n return room.on(event, handler);\n },\n\n room(id: string): Room | null {\n return roomManager.get(id);\n },\n\n rooms(): Room[] {\n return roomManager.all();\n },\n\n createRoom(id: string, config: RoomConfig): Room | null {\n // Check if room already exists\n if (roomManager.get(id)) {\n logger.warn({\n message: `Room '${id}' already exists`,\n atFunction: \"dialogue.createRoom\",\n data: { roomId: id },\n });\n return null;\n }\n\n const room = roomManager.register(id, config);\n\n // Broadcast room created event to all connected clients\n io.emit(\"dialogue:roomCreated\", {\n id: room.id,\n name: room.name,\n description: room.description,\n maxSize: room.maxSize,\n });\n\n logger.info({\n message: `Room '${id}' created`,\n atFunction: \"dialogue.createRoom\",\n data: { roomId: id, roomName: config.name },\n });\n\n return room;\n },\n\n deleteRoom(id: string): boolean {\n const deleted = roomManager.unregister(id);\n\n if (deleted) {\n // Broadcast room deleted event to all connected clients\n io.emit(\"dialogue:roomDeleted\", { roomId: id });\n }\n\n return deleted;\n },\n\n getClients(userId: string): ConnectedClient[] {\n return getClientsByUserId(userId);\n },\n\n getAllClients(): ConnectedClient[] {\n return getAllConnectedClients();\n },\n\n getClientRooms(userId: string): ClientRooms {\n const roomIds = getClientRoomIds(userId);\n const clients = getClientsByUserId(userId);\n\n return {\n ids: roomIds,\n forAll(callback: (roomId: string) => void): void {\n for (const roomId of roomIds) {\n callback(roomId);\n }\n },\n leaveAll(callback?: (roomId: string) => void): void {\n // Execute callback for each room before leaving\n if (callback) {\n for (const roomId of roomIds) {\n callback(roomId);\n }\n }\n\n // Leave all rooms for all connections\n for (const client of clients) {\n for (const roomId of client.rooms()) {\n client.leave(roomId);\n }\n }\n },\n };\n },\n\n isInRoom(userId: string, roomId: string): boolean {\n return isUserInRoom(userId, roomId);\n },\n\n start,\n stop,\n };\n\n return dialogue;\n}\n","import type { EventMessage } from \"./types.ts\";\n\n/**\n * Configuration for the history manager\n */\nexport interface HistoryManagerConfig {\n /** Called when events are evicted from in-memory history (oldest first) */\n onCleanup?: (\n roomId: string,\n eventName: string,\n events: EventMessage[]\n ) => void | Promise<void>;\n /** Called to load historical events beyond in-memory (for pagination) */\n onLoad?: (\n roomId: string,\n eventName: string,\n start: number,\n end: number\n ) => Promise<EventMessage[]>;\n}\n\n/**\n * History manager instance returned by createHistoryManager\n */\nexport interface HistoryManager {\n /** Add an event to history, evict oldest if exceeds limit */\n push(\n roomId: string,\n eventName: string,\n event: EventMessage,\n limit: number\n ): void;\n /** Get events (0 = newest, reads backward) */\n get(\n roomId: string,\n eventName: string,\n start: number,\n end: number\n ): EventMessage[];\n /** Get all events for a room across all event types, sorted newest first */\n getAll(roomId: string, limit?: number): EventMessage[];\n /** Get total count of events for a specific event type in a room */\n count(roomId: string, eventName: string): number;\n /** Clear all history for a room */\n clearRoom(roomId: string): void;\n /** Get event names that have history for a room */\n getEventNames(roomId: string): string[];\n}\n\n/**\n * Creates a history manager for storing events in memory.\n * Stores per-room, per-event-type with FIFO eviction (oldest first).\n *\n * @param config - Configuration with optional cleanup and load callbacks\n * @returns HistoryManager instance\n *\n * @example\n * const history = createHistoryManager({\n * onCleanup: async (roomId, eventName, events) => {\n * await db.events.insertMany(events);\n * }\n * });\n *\n * // Push an event\n * history.push(\"room-1\", \"message\", eventMessage, 50);\n *\n * // Get last 10 messages (newest first)\n * const recent = history.get(\"room-1\", \"message\", 0, 10);\n */\nexport function createHistoryManager(\n config: HistoryManagerConfig = {}\n): HistoryManager {\n // Map<roomId, Map<eventName, EventMessage[]>>\n // Events are stored with newest at the END of the array\n // When retrieving, we reverse the index logic to return newest first\n const store = new Map<string, Map<string, EventMessage[]>>();\n\n /**\n * Ensures the room and event type exist in the store\n */\n function ensureRoomEvent(roomId: string, eventName: string): EventMessage[] {\n let roomHistory = store.get(roomId);\n if (!roomHistory) {\n roomHistory = new Map();\n store.set(roomId, roomHistory);\n }\n\n let eventHistory = roomHistory.get(eventName);\n if (!eventHistory) {\n eventHistory = [];\n roomHistory.set(eventName, eventHistory);\n }\n\n return eventHistory;\n }\n\n return {\n push(\n roomId: string,\n eventName: string,\n event: EventMessage,\n limit: number\n ): void {\n const events = ensureRoomEvent(roomId, eventName);\n\n // Add new event to the end (newest)\n events.push(event);\n\n // Evict oldest (from the beginning) if over limit\n if (events.length > limit) {\n const evictCount = events.length - limit;\n const evicted = events.splice(0, evictCount);\n\n // Call cleanup hook if provided\n if (config.onCleanup && evicted.length > 0) {\n config.onCleanup(roomId, eventName, evicted);\n }\n }\n },\n\n get(\n roomId: string,\n eventName: string,\n start: number,\n end: number\n ): EventMessage[] {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return [];\n }\n\n const events = roomHistory.get(eventName);\n if (!events || events.length === 0) {\n return [];\n }\n\n // Events are stored oldest-to-newest\n // We want to return newest-first, so we reverse the indexing\n // start=0, end=10 means \"last 10 items\" = items from length-10 to length\n const len = events.length;\n const actualStart = Math.max(0, len - end);\n const actualEnd = len - start;\n\n if (actualStart >= actualEnd) {\n return [];\n }\n\n // Slice and reverse to get newest first\n return events.slice(actualStart, actualEnd).reverse();\n },\n\n getAll(roomId: string, limit?: number): EventMessage[] {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return [];\n }\n\n // Collect all events from all event types\n const allEvents: EventMessage[] = [];\n for (const events of roomHistory.values()) {\n allEvents.push(...events);\n }\n\n // Sort by timestamp descending (newest first)\n allEvents.sort((a, b) => b.timestamp - a.timestamp);\n\n // Apply limit if provided\n if (limit !== undefined && limit > 0) {\n return allEvents.slice(0, limit);\n }\n\n return allEvents;\n },\n\n count(roomId: string, eventName: string): number {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return 0;\n }\n\n const events = roomHistory.get(eventName);\n return events?.length ?? 0;\n },\n\n clearRoom(roomId: string): void {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return;\n }\n\n // Call cleanup for each event type before clearing\n if (config.onCleanup) {\n for (const [eventName, events] of roomHistory.entries()) {\n if (events.length > 0) {\n config.onCleanup(roomId, eventName, events);\n }\n }\n }\n\n store.delete(roomId);\n },\n\n getEventNames(roomId: string): string[] {\n const roomHistory = store.get(roomId);\n if (!roomHistory) {\n return [];\n }\n\n return Array.from(roomHistory.keys());\n },\n };\n}\n","import type { LogEntry, Logger } from \"./types.ts\";\n\n/**\n * Creates the default console-based logger for Dialogue.\n * Outputs structured log entries as JSON-like objects.\n *\n * @returns Logger implementation using console methods\n */\nexport function createDefaultLogger(): Logger {\n return {\n debug(entry: LogEntry): void {\n if (process.env.NODE_ENV === \"development\" || process.env.DEBUG) {\n console.debug(\"[Dialogue] [DEBUG]\", entry);\n }\n },\n\n info(entry: LogEntry): void {\n console.info(\"[Dialogue] [INFO]\", entry);\n },\n\n warn(entry: LogEntry): void {\n console.warn(\"[Dialogue] [WARN]\", entry);\n },\n\n error(entry: LogEntry): void {\n console.error(\"[Dialogue] [ERROR]\", entry);\n },\n };\n}\n\n/**\n * Creates a silent logger that does not output anything.\n * Useful for testing or when logging is not desired.\n *\n * @returns Logger implementation that does nothing\n */\nexport function createSilentLogger(): Logger {\n const noop = (): void => {\n // Intentionally empty - silent logger discards all log entries\n };\n return {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n };\n}\n","import type { Hono } from \"hono\";\nimport { cors as honoCors } from \"hono/cors\";\nimport { Server } from \"socket.io\";\nimport type { RuntimeAdapter } from \"./adapters/types.ts\";\nimport {\n createConnectedClient,\n extractUserFromSocket,\n} from \"./client-handler.ts\";\nimport type { HistoryManager } from \"./history.ts\";\nimport { createDefaultLogger } from \"./logger.ts\";\nimport { createRateLimiter } from \"./rate-limiter.ts\";\nimport { createRoomManager, type RoomManagerInstance } from \"./room.ts\";\nimport type {\n ConnectedClient,\n CorsConfig,\n DialogueConfig,\n DialogueContext,\n HooksConfig,\n Logger,\n Room,\n} from \"./types.ts\";\n\n/**\n * Builds Socket.IO CORS options from DialogueConfig cors setting.\n * Defaults to allowing all origins for ease of development.\n */\nfunction buildCorsOptions(cors: CorsConfig | boolean | undefined): {\n origin: string | string[] | boolean;\n methods?: string[];\n credentials?: boolean;\n} {\n // Default: allow all origins (development-friendly)\n if (cors === undefined || cors === true) {\n return {\n origin: true,\n methods: [\"GET\", \"POST\"],\n credentials: true,\n };\n }\n\n // Explicitly disabled\n if (cors === false) {\n return { origin: false };\n }\n\n // Custom config\n return {\n origin: cors.origin,\n methods: cors.methods ?? [\"GET\", \"POST\"],\n credentials: cors.credentials ?? true,\n };\n}\n\n/**\n * Builds Hono CORS middleware options from DialogueConfig cors setting.\n */\nfunction buildHonoCorsOptions(cors: CorsConfig | boolean | undefined): {\n origin: string | string[] | ((origin: string) => string | undefined);\n allowMethods: string[];\n credentials: boolean;\n} {\n // Default: allow all origins (development-friendly)\n if (cors === undefined || cors === true) {\n return {\n origin: \"*\",\n allowMethods: [\"GET\", \"POST\", \"OPTIONS\"],\n credentials: true,\n };\n }\n\n // Explicitly disabled - still need to return something valid\n if (cors === false) {\n return {\n origin: () => undefined,\n allowMethods: [\"GET\", \"POST\"],\n credentials: false,\n };\n }\n\n // Custom config\n const originValue = cors.origin;\n\n let resolvedOrigin:\n | string\n | string[]\n | ((origin: string) => string | undefined);\n if (originValue === true) {\n resolvedOrigin = \"*\";\n } else if (originValue === false) {\n resolvedOrigin = () => undefined;\n } else {\n resolvedOrigin = originValue;\n }\n\n return {\n origin: resolvedOrigin,\n allowMethods: cors.methods ?? [\"GET\", \"POST\", \"OPTIONS\"],\n credentials: cors.credentials ?? true,\n };\n}\n\n/**\n * Sends history to a client when they join a room (if syncHistoryOnJoin is enabled)\n */\nfunction sendHistoryOnJoin(\n socket: import(\"socket.io\").Socket,\n roomId: string,\n roomConfig: import(\"./types.ts\").RoomConfig | undefined,\n historyManager: HistoryManager | undefined\n): void {\n if (!(roomConfig?.syncHistoryOnJoin && historyManager)) {\n return;\n }\n\n const limit =\n typeof roomConfig.syncHistoryOnJoin === \"number\"\n ? roomConfig.syncHistoryOnJoin\n : undefined;\n\n const historyEvents = historyManager.getAll(roomId, limit);\n if (historyEvents.length > 0) {\n socket.emit(\"dialogue:history\", {\n roomId,\n events: historyEvents,\n });\n }\n}\n\n/**\n * Helper function to create a DialogueContext from current runtime state.\n * This function converts internal state (Maps) to the Record format expected by DialogueContext.\n */\nfunction createDialogueContext(\n io: Server,\n connectedClients: Map<string, ConnectedClient>,\n roomManager: RoomManagerInstance\n): DialogueContext {\n // Convert Map to Record for clients\n const clientsRecord: Record<string, ConnectedClient> = {};\n for (const [id, client] of connectedClients.entries()) {\n clientsRecord[id] = client;\n }\n\n // Convert room manager's rooms to Record\n const roomsRecord: Record<string, Room> = {};\n for (const room of roomManager.all()) {\n roomsRecord[room.id] = room;\n }\n\n return {\n io,\n clients: clientsRecord,\n rooms: roomsRecord,\n };\n}\n\n/**\n * Sets up the Socket.IO server and wires up all handlers.\n * Runtime-agnostic — delegates HTTP server and engine binding to the provided adapter.\n *\n * @param app - Hono app instance\n * @param config - Dialogue configuration\n * @param adapter - Runtime adapter (bun or node) for HTTP server and engine binding\n * @param historyManager - Optional history manager for event storage\n * @returns Server components and lifecycle methods\n */\nexport function setupServer(\n app: Hono,\n config: DialogueConfig,\n adapter: RuntimeAdapter,\n historyManager?: HistoryManager\n): {\n io: Server;\n roomManager: RoomManagerInstance;\n start: () => Promise<void>;\n stop: () => Promise<void>;\n getConnectedClient: (socketId: string) => ConnectedClient | undefined;\n getAllConnectedClients: () => ConnectedClient[];\n getClientsByUserId: (userId: string) => ConnectedClient[];\n getClientRooms: (userId: string) => string[];\n isUserInRoom: (userId: string, roomId: string) => boolean;\n} {\n const logger: Logger = config.logger ?? createDefaultLogger();\n const hooks: HooksConfig | undefined = config.hooks;\n\n /** Connected clients registry keyed by socket ID (instance-scoped) */\n const connectedClients = new Map<string, ConnectedClient>();\n\n /** Secondary index: userId -> Set of socket IDs for O(1) lookup (instance-scoped) */\n const userIdToSocketIds = new Map<string, Set<string>>();\n\n // Build CORS configuration for Socket.IO\n const corsOptions = buildCorsOptions(config.cors);\n\n // Apply Hono CORS middleware for HTTP requests (needed for Socket.IO polling)\n const honoCorsOptions = buildHonoCorsOptions(config.cors);\n app.use(\"*\", honoCors(honoCorsOptions));\n\n const io = new Server({ cors: corsOptions });\n\n // Delegate engine binding to the runtime adapter\n adapter.bind(io);\n\n // Create a helper function to build context - will be passed to roomManager\n const getContextForHooks = (): DialogueContext => {\n return createDialogueContext(io, connectedClients, roomManager);\n };\n\n const roomManager = createRoomManager(\n io,\n logger,\n historyManager,\n hooks,\n getContextForHooks\n );\n\n // Rate limiter for history requests: 20 requests per minute per socket\n const historyRateLimiter = createRateLimiter({\n maxRequests: 20,\n windowMs: 60_000,\n });\n\n for (const [roomId, roomConfig] of Object.entries(config.rooms)) {\n roomManager.register(roomId, roomConfig);\n }\n\n // Wire socket authentication middleware if provided.\n // Runs during handshake — rejects connection before it enters connectedClients.\n if (hooks?.socket?.authenticate) {\n const authenticateHook = hooks.socket.authenticate;\n\n io.use(async (socket, next) => {\n const authData = socket.handshake.auth;\n\n // Build context for the authenticate hook\n // Note: At this point, the client hasn't been added to connectedClients yet\n const context = createDialogueContext(io, connectedClients, roomManager);\n\n try {\n const result = await Promise.resolve(\n authenticateHook({\n context,\n clientSocket: socket,\n authData,\n })\n );\n\n if (result.isErr) {\n logger.warn({\n message: `Authentication rejected: ${result.error}`,\n atFunction: \"setupServer.authenticate\",\n data: { socketId: socket.id },\n });\n return next(new Error(result.error));\n }\n\n // Store authenticated auth data on socket for the connection handler\n // The authData contains jwt field with claims\n socket.data = {\n ...socket.data,\n authenticatedAuthData: result.value,\n };\n\n return next();\n } catch (err) {\n logger.error({\n message: \"Error in authenticate hook\",\n atFunction: \"setupServer.authenticate\",\n data: err,\n });\n return next(new Error(\"Authentication failed\"));\n }\n });\n }\n\n io.on(\"connection\", (socket) => {\n // Use authenticated data from middleware if available, otherwise fall back to extraction\n let userId: string;\n let meta: Record<string, unknown>;\n let authData: import(\"./types.ts\").AuthData | undefined;\n\n if (socket.data?.authenticatedAuthData) {\n authData = socket.data\n .authenticatedAuthData as import(\"./types.ts\").AuthData;\n // Extract userId from JWT claims (sub field is standard for user ID)\n userId = authData.jwt.sub;\n // For now, use empty meta - can be extended later if needed\n meta = {};\n } else {\n const extracted = extractUserFromSocket(socket);\n userId = extracted.userId;\n meta = extracted.meta;\n }\n\n const client = createConnectedClient(\n socket,\n userId,\n meta,\n roomManager,\n logger\n );\n\n // Attach auth data to client if it exists\n if (authData) {\n // We need to update the client's auth field\n // Since ConnectedClient is readonly, we'll need to handle this differently\n // For now, we can cast and assign (TODO: refactor createConnectedClient to accept auth)\n (client as { auth?: import(\"./types.ts\").AuthData }).auth = authData;\n }\n\n connectedClients.set(socket.id, client);\n\n // Add to userId index\n const userSockets = userIdToSocketIds.get(client.userId) ?? new Set();\n userSockets.add(socket.id);\n userIdToSocketIds.set(client.userId, userSockets);\n\n socket.emit(\"dialogue:connected\", {\n clientId: client.id,\n userId: client.userId,\n });\n\n // Call onConnected hook if provided\n if (hooks?.clients?.onConnected) {\n Promise.resolve(hooks.clients.onConnected(client)).catch((err) => {\n logger.error({\n message: \"Error in onConnected hook\",\n atFunction: \"setupServer.onConnected\",\n data: err,\n });\n });\n }\n\n socket.on(\"dialogue:join\", (data: { roomId: string }) => {\n if (typeof data?.roomId !== \"string\") {\n return;\n }\n\n const room = roomManager.get(data.roomId);\n if (!room) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_NOT_FOUND\",\n message: `Room '${data.roomId}' does not exist`,\n });\n return;\n }\n\n // Run beforeJoin hook — can deny room access\n if (hooks?.clients?.beforeJoin) {\n // Build fresh context for this hook call\n const context = createDialogueContext(\n io,\n connectedClients,\n roomManager\n );\n\n const joinResult = hooks.clients.beforeJoin({\n context,\n client,\n roomId: data.roomId,\n room,\n });\n\n if (joinResult.isErr) {\n logger.warn({\n message: `Join denied for room '${data.roomId}': ${joinResult.error}`,\n atFunction: \"setupServer.beforeJoin\",\n data: {\n roomId: data.roomId,\n clientId: client.id,\n reason: joinResult.error,\n },\n });\n socket.emit(\"dialogue:error\", {\n code: \"JOIN_DENIED\",\n message: joinResult.error,\n });\n return;\n }\n }\n\n client.join(data.roomId);\n\n // Call onJoined hook if provided\n if (hooks?.clients?.onJoined) {\n Promise.resolve(hooks.clients.onJoined(client, data.roomId)).catch(\n (err) => {\n logger.error({\n message: \"Error in onJoined hook\",\n atFunction: \"setupServer.onJoined\",\n data: err,\n });\n }\n );\n }\n\n // Send history on join if syncHistoryOnJoin is enabled\n sendHistoryOnJoin(\n socket,\n data.roomId,\n config.rooms[data.roomId],\n historyManager\n );\n });\n\n socket.on(\"dialogue:leave\", (data: { roomId: string }) => {\n if (typeof data?.roomId === \"string\") {\n client.leave(data.roomId);\n\n // Call onLeft hook if provided\n if (hooks?.clients?.onLeft) {\n Promise.resolve(hooks.clients.onLeft(client, data.roomId)).catch(\n (err) => {\n logger.error({\n message: \"Error in onLeft hook\",\n atFunction: \"setupServer.onLeft\",\n data: err,\n });\n }\n );\n }\n }\n });\n\n socket.on(\n \"dialogue:subscribe\",\n (data: { roomId: string; eventName: string }) => {\n if (\n typeof data?.roomId === \"string\" &&\n typeof data?.eventName === \"string\"\n ) {\n client.subscribe(data.roomId, data.eventName);\n }\n }\n );\n\n socket.on(\"dialogue:subscribeAll\", (data: { roomId: string }) => {\n if (typeof data?.roomId === \"string\") {\n client.subscribeAll(data.roomId);\n }\n });\n\n socket.on(\n \"dialogue:unsubscribe\",\n (data: { roomId: string; eventName: string }) => {\n if (\n typeof data?.roomId === \"string\" &&\n typeof data?.eventName === \"string\"\n ) {\n client.unsubscribe(data.roomId, data.eventName);\n }\n }\n );\n\n // Handle history requests from clients (rate limited)\n socket.on(\n \"dialogue:getHistory\",\n async (data: {\n roomId: string;\n eventName?: string;\n start?: number;\n end?: number;\n }) => {\n // Rate limit check: 20 requests per minute per socket\n if (!historyRateLimiter.isAllowed(socket.id)) {\n socket.emit(\"dialogue:error\", {\n code: \"RATE_LIMITED\",\n message:\n \"Too many history requests. Please wait before trying again.\",\n });\n return;\n }\n\n if (typeof data?.roomId !== \"string\") {\n socket.emit(\"dialogue:error\", {\n code: \"INVALID_REQUEST\",\n message: \"roomId is required\",\n });\n return;\n }\n\n const room = roomManager.get(data.roomId);\n if (!room) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_NOT_FOUND\",\n message: `Room '${data.roomId}' does not exist`,\n });\n return;\n }\n\n const start = data.start ?? 0;\n const end = data.end ?? 50;\n\n // If no eventName provided, return empty array (use room.events to get types)\n if (!data.eventName) {\n socket.emit(\"dialogue:historyResponse\", {\n roomId: data.roomId,\n eventName: null,\n events: [],\n start,\n end,\n });\n return;\n }\n\n const events = await room.history(data.eventName, start, end);\n socket.emit(\"dialogue:historyResponse\", {\n roomId: data.roomId,\n eventName: data.eventName,\n events,\n start,\n end,\n });\n }\n );\n\n socket.on(\n \"dialogue:trigger\",\n (data: {\n roomId: string;\n event: string;\n data: unknown;\n meta?: Record<string, unknown>;\n }) => {\n if (\n typeof data?.roomId !== \"string\" ||\n typeof data?.event !== \"string\"\n ) {\n return;\n }\n\n const room = roomManager.get(data.roomId);\n if (!room) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_NOT_FOUND\",\n message: `Room '${data.roomId}' does not exist`,\n });\n return;\n }\n\n const eventDef = room.events.find((e) => e.name === data.event);\n const hasWildcard = room.events.some((e) => e.name === \"*\");\n\n if (!(eventDef || hasWildcard)) {\n socket.emit(\"dialogue:error\", {\n code: \"EVENT_NOT_ALLOWED\",\n message: `Event '${data.event}' is not allowed in room '${data.roomId}'`,\n });\n return;\n }\n\n const triggerEvent = eventDef ?? { name: data.event };\n const result = room.trigger(\n triggerEvent,\n data.data,\n client.userId,\n data.meta\n );\n\n if (result.isErr) {\n socket.emit(\"dialogue:error\", {\n code: \"VALIDATION_FAILED\",\n message: result.error,\n });\n }\n }\n );\n\n socket.on(\"dialogue:listRooms\", () => {\n const rooms = roomManager.all().map((room) => ({\n id: room.id,\n name: room.name,\n description: room.description,\n size: roomManager.getRoomSize(room.id),\n maxSize: room.maxSize,\n }));\n socket.emit(\"dialogue:rooms\", rooms);\n });\n\n socket.on(\n \"dialogue:createRoom\",\n (data: {\n id: string;\n name: string;\n description?: string;\n maxSize?: number;\n }) => {\n if (typeof data?.id !== \"string\" || typeof data?.name !== \"string\") {\n socket.emit(\"dialogue:error\", {\n code: \"INVALID_REQUEST\",\n message: \"Room id and name are required\",\n });\n return;\n }\n\n // Check if room already exists\n if (roomManager.get(data.id)) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_EXISTS\",\n message: `Room '${data.id}' already exists`,\n });\n return;\n }\n\n // Create the room with empty events (open room - any event allowed)\n const room = roomManager.register(data.id, {\n name: data.name,\n description: data.description,\n maxSize: data.maxSize,\n events: [],\n createdById: client.userId,\n });\n\n // Notify the creator\n socket.emit(\"dialogue:roomCreated\", {\n id: room.id,\n name: room.name,\n description: room.description,\n maxSize: room.maxSize,\n createdById: room.createdById,\n });\n\n // Broadcast to all other clients\n socket.broadcast.emit(\"dialogue:roomCreated\", {\n id: room.id,\n name: room.name,\n description: room.description,\n maxSize: room.maxSize,\n createdById: room.createdById,\n });\n\n logger.info({\n message: `Room '${data.id}' created by ${client.userId}`,\n atFunction: \"setupServer.createRoom\",\n data: { roomId: data.id, createdBy: client.userId },\n });\n }\n );\n\n socket.on(\"dialogue:deleteRoom\", (data: { roomId: string }) => {\n if (typeof data?.roomId !== \"string\") {\n return;\n }\n\n const room = roomManager.get(data.roomId);\n if (!room) {\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_NOT_FOUND\",\n message: `Room '${data.roomId}' does not exist`,\n });\n return;\n }\n\n // Only allow creator or add admin check here\n if (room.createdById && room.createdById !== client.userId) {\n socket.emit(\"dialogue:error\", {\n code: \"PERMISSION_DENIED\",\n message: \"Only the room creator can delete this room\",\n });\n return;\n }\n\n const deleted = roomManager.unregister(data.roomId);\n if (deleted) {\n // Broadcast deletion to all clients\n io.emit(\"dialogue:roomDeleted\", { roomId: data.roomId });\n\n logger.info({\n message: `Room '${data.roomId}' deleted by ${client.userId}`,\n atFunction: \"setupServer.deleteRoom\",\n data: { roomId: data.roomId, deletedBy: client.userId },\n });\n }\n });\n\n socket.on(\"disconnect\", () => {\n // Call onDisconnected hook before cleanup\n if (hooks?.clients?.onDisconnected) {\n Promise.resolve(hooks.clients.onDisconnected(client)).catch((err) => {\n logger.error({\n message: \"Error in onDisconnected hook\",\n atFunction: \"setupServer.onDisconnected\",\n data: err,\n });\n });\n }\n\n roomManager.removeFromAllRooms(client.id);\n connectedClients.delete(socket.id);\n\n // Remove from userId index\n const userSockets = userIdToSocketIds.get(client.userId);\n if (userSockets) {\n userSockets.delete(socket.id);\n if (userSockets.size === 0) {\n userIdToSocketIds.delete(client.userId);\n }\n }\n });\n });\n\n const port = config.port ?? 3000;\n\n return {\n io,\n roomManager,\n\n start(): Promise<void> {\n return adapter.start({\n port,\n app,\n io,\n corsConfig: config.cors,\n logger,\n });\n },\n\n stop(): Promise<void> {\n for (const client of connectedClients.values()) {\n client.disconnect();\n }\n connectedClients.clear();\n userIdToSocketIds.clear();\n\n io.close();\n\n logger.info({\n message: \"Server stopped\",\n atFunction: \"setupServer.stop\",\n data: null,\n });\n\n return adapter.stop();\n },\n\n /**\n * Gets a connected client by socket ID\n */\n getConnectedClient(socketId: string): ConnectedClient | undefined {\n return connectedClients.get(socketId);\n },\n\n /**\n * Gets all connected clients\n */\n getAllConnectedClients(): ConnectedClient[] {\n return Array.from(connectedClients.values());\n },\n\n /**\n * Gets all connected clients for a specific user ID.\n * Returns array since a user may have multiple connections.\n */\n getClientsByUserId(userId: string): ConnectedClient[] {\n const socketIds = userIdToSocketIds.get(userId);\n if (!socketIds) {\n return [];\n }\n\n const clients: ConnectedClient[] = [];\n for (const socketId of socketIds) {\n const client = connectedClients.get(socketId);\n if (client) {\n clients.push(client);\n }\n }\n return clients;\n },\n\n /**\n * Gets all room IDs that a user is currently in.\n * Aggregates rooms across all connections for this user.\n */\n getClientRooms(userId: string): string[] {\n const clients = this.getClientsByUserId(userId);\n const roomSet = new Set<string>();\n\n for (const client of clients) {\n for (const roomId of client.rooms()) {\n roomSet.add(roomId);\n }\n }\n\n return Array.from(roomSet);\n },\n\n /**\n * Checks if a user is in a specific room (any of their connections)\n */\n isUserInRoom(userId: string, roomId: string): boolean {\n const clients = this.getClientsByUserId(userId);\n return clients.some((client) => client.rooms().includes(roomId));\n },\n };\n}\n","import { nanoid } from \"nanoid\";\nimport type { Socket } from \"socket.io\";\nimport type { RoomManagerInstance } from \"./room.ts\";\nimport type { ConnectedClient, Logger } from \"./types.ts\";\n\n/** Wildcard subscription symbol */\nconst WILDCARD = \"*\";\n\n/**\n * Creates a connected client wrapper for a socket connection.\n * Manages room membership and event subscriptions for the client.\n *\n * @param socket - The Socket.IO socket instance\n * @param userId - Application user ID from authentication\n * @param meta - Additional metadata (role, permissions, etc.)\n * @param roomManager - Room manager instance for coordination\n * @param logger - Logger instance\n * @returns ConnectedClient instance\n */\nexport function createConnectedClient(\n socket: Socket,\n userId: string,\n meta: Record<string, unknown>,\n roomManager: RoomManagerInstance,\n logger: Logger\n): ConnectedClient {\n const id = nanoid();\n const joinedRooms = new Set<string>();\n const subscriptions = new Map<string, Set<string>>();\n\n const client: ConnectedClient = {\n id,\n userId,\n socket,\n meta,\n\n join(roomId: string): void {\n const room = roomManager.get(roomId);\n if (!room) {\n logger.warn({\n message: `Room '${roomId}' does not exist`,\n atFunction: \"client.join\",\n data: { roomId, clientId: id },\n });\n return;\n }\n\n if (joinedRooms.has(roomId)) {\n // Already joined, but still emit confirmation for client requests\n socket.emit(\"dialogue:joined\", { roomId, roomName: room.name });\n return;\n }\n\n const added = roomManager.addParticipant(roomId, client);\n if (!added) {\n logger.warn({\n message: `Room '${roomId}' is full`,\n atFunction: \"client.join\",\n data: { roomId, clientId: id },\n });\n socket.emit(\"dialogue:error\", {\n code: \"ROOM_FULL\",\n message: `Room '${roomId}' is at capacity`,\n });\n return;\n }\n\n joinedRooms.add(roomId);\n subscriptions.set(roomId, new Set());\n\n for (const eventName of room.defaultSubscriptions) {\n this.subscribe(roomId, eventName);\n }\n\n socket.emit(\"dialogue:joined\", { roomId, roomName: room.name });\n },\n\n leave(roomId: string): void {\n if (!joinedRooms.has(roomId)) {\n return;\n }\n\n roomManager.removeParticipant(roomId, id);\n joinedRooms.delete(roomId);\n subscriptions.delete(roomId);\n\n socket.emit(\"dialogue:left\", { roomId });\n },\n\n subscribe(roomId: string, eventName: string): void {\n if (!joinedRooms.has(roomId)) {\n logger.warn({\n message: `Cannot subscribe to '${eventName}' - not in room '${roomId}'`,\n atFunction: \"client.subscribe\",\n data: { roomId, eventName, clientId: id },\n });\n return;\n }\n\n const roomSubs = subscriptions.get(roomId);\n if (roomSubs) {\n roomSubs.add(eventName);\n }\n },\n\n subscribeAll(roomId: string): void {\n this.subscribe(roomId, WILDCARD);\n },\n\n unsubscribe(roomId: string, eventName: string): void {\n const roomSubs = subscriptions.get(roomId);\n if (roomSubs) {\n roomSubs.delete(eventName);\n }\n },\n\n rooms(): string[] {\n return Array.from(joinedRooms);\n },\n\n subscriptions(roomId: string): string[] {\n const roomSubs = subscriptions.get(roomId);\n return roomSubs ? Array.from(roomSubs) : [];\n },\n\n send<T>(event: string, data: T): void {\n socket.emit(event, data);\n },\n\n disconnect(): void {\n for (const roomId of joinedRooms) {\n roomManager.removeParticipant(roomId, id);\n }\n joinedRooms.clear();\n subscriptions.clear();\n socket.disconnect(true);\n },\n };\n\n return client;\n}\n\n/**\n * Checks if a client is subscribed to an event in a room.\n * Returns true if subscribed to the specific event or wildcard.\n */\nexport function isSubscribedToEvent(\n client: ConnectedClient,\n roomId: string,\n eventName: string\n): boolean {\n const subs = client.subscriptions(roomId);\n return subs.includes(eventName) || subs.includes(WILDCARD);\n}\n\n/**\n * Extracts user ID and metadata from socket handshake.\n * Override this for custom authentication strategies.\n */\nexport function extractUserFromSocket(socket: Socket): {\n userId: string;\n meta: Record<string, unknown>;\n} {\n const auth = socket.handshake.auth as Record<string, unknown>;\n\n let userId = socket.id;\n if (typeof auth.userId === \"string\") {\n userId = auth.userId;\n } else if (typeof auth.token === \"string\") {\n userId = auth.token;\n }\n\n const meta: Record<string, unknown> = {};\n\n if (typeof auth.role === \"string\") {\n meta.role = auth.role;\n }\n\n for (const [key, value] of Object.entries(auth)) {\n if (key !== \"userId\" && key !== \"token\") {\n meta[key] = value;\n }\n }\n\n return { userId, meta };\n}\n","/**\n * Rate limiter configuration\n */\nexport interface RateLimiterConfig {\n /** Maximum number of requests allowed in the window */\n maxRequests: number;\n /** Time window in milliseconds */\n windowMs: number;\n}\n\n/**\n * Rate limiter instance returned by createRateLimiter\n */\nexport interface RateLimiter {\n /**\n * Check if a request is allowed for the given key.\n * Returns true if allowed, false if rate limited.\n */\n isAllowed(key: string): boolean;\n /**\n * Get remaining requests for the given key\n */\n remaining(key: string): number;\n /**\n * Clear all rate limit data (useful for testing)\n */\n clear(): void;\n}\n\ninterface RateLimitEntry {\n count: number;\n resetAt: number;\n}\n\n/**\n * Creates a simple in-memory rate limiter using sliding window algorithm.\n * Tracks requests per key (typically socket ID or user ID) and enforces limits.\n *\n * @param config - Rate limiter configuration\n * @returns RateLimiter instance\n *\n * @example\n * const limiter = createRateLimiter({ maxRequests: 10, windowMs: 60000 });\n *\n * if (!limiter.isAllowed(socketId)) {\n * socket.emit(\"dialogue:error\", { code: \"RATE_LIMITED\" });\n * return;\n * }\n */\nexport function createRateLimiter(config: RateLimiterConfig): RateLimiter {\n const entries = new Map<string, RateLimitEntry>();\n\n // Cleanup expired entries periodically to prevent memory leaks\n const cleanupInterval = setInterval(() => {\n const now = Date.now();\n for (const [key, entry] of entries) {\n if (now >= entry.resetAt) {\n entries.delete(key);\n }\n }\n }, config.windowMs);\n\n // Prevent the interval from keeping the process alive\n cleanupInterval.unref?.();\n\n return {\n isAllowed(key: string): boolean {\n const now = Date.now();\n const entry = entries.get(key);\n\n // No existing entry or window expired - allow and reset\n if (!entry || now >= entry.resetAt) {\n entries.set(key, {\n count: 1,\n resetAt: now + config.windowMs,\n });\n return true;\n }\n\n // Within window - check count\n if (entry.count >= config.maxRequests) {\n return false;\n }\n\n // Increment and allow\n entry.count++;\n return true;\n },\n\n remaining(key: string): number {\n const now = Date.now();\n const entry = entries.get(key);\n\n if (!entry || now >= entry.resetAt) {\n return config.maxRequests;\n }\n\n return Math.max(0, config.maxRequests - entry.count);\n },\n\n clear(): void {\n entries.clear();\n },\n };\n}\n","import { Err, Ok, type Result } from \"slang-ts\";\nimport type { Server } from \"socket.io\";\nimport {\n getEventByName,\n isEventAllowed,\n validateEventData,\n} from \"./define-event.ts\";\nimport type { HistoryManager } from \"./history.ts\";\nimport type {\n ConnectedClient,\n DialogueContext,\n EventDefinition,\n EventMessage,\n HooksConfig,\n Logger,\n Room,\n RoomConfig,\n} from \"./types.ts\";\n\n/** Wildcard subscription matches all events */\nconst WILDCARD = \"*\";\n\n/**\n * Checks if a participant is subscribed to an event in a room.\n * Returns true for specific event subscription or wildcard.\n */\nfunction isParticipantSubscribed(\n participant: ConnectedClient,\n roomId: string,\n eventName: string\n): boolean {\n const subs = participant.subscriptions(roomId);\n return subs.includes(eventName) || subs.includes(WILDCARD);\n}\n\n/** Handler function for room events */\ntype EventHandler<T = unknown> = (msg: EventMessage<T>) => void | Promise<void>;\n\n/**\n * Creates a room instance with event management and participant tracking.\n * The room manages participants, subscriptions, and event broadcasting.\n *\n * @param id - Unique room identifier\n * @param config - Room configuration\n * @param _io - Socket.IO server instance (reserved for future use)\n * @param logger - Logger instance\n * @param historyManager - Optional history manager for event storage\n * @param hooks - Optional hooks config for event callbacks\n * @param externalParticipants - Optional shared participants Map (used by room manager)\n * @param getContext - Function to retrieve current DialogueContext for hook calls\n * @returns Room instance with methods\n */\nexport function createRoom(\n id: string,\n config: RoomConfig,\n _io: Server,\n logger: Logger,\n historyManager?: HistoryManager,\n hooks?: HooksConfig,\n externalParticipants?: Map<string, ConnectedClient>,\n getContext?: () => DialogueContext\n): Room {\n const participants =\n externalParticipants ?? new Map<string, ConnectedClient>();\n const eventHandlers = new Map<string, Set<EventHandler>>();\n\n /** Handles post-broadcast work: history storage, hooks, and server-side handlers */\n /**\n * Validates event is allowed and data is valid\n */\n function validateEvent<T>(\n event: EventDefinition<T>,\n data: T\n ): Result<T, string> {\n if (!isEventAllowed(event.name, config.events)) {\n const errorMsg = `Event '${event.name}' is not allowed in room '${id}'`;\n logger.warn({\n message: errorMsg,\n atFunction: \"room.trigger\",\n data: { eventName: event.name, roomId: id },\n });\n return Err(errorMsg);\n }\n\n const eventDef = getEventByName(event.name, config.events) ?? event;\n const validation = validateEventData(eventDef, data);\n\n if (validation.isErr) {\n logger.warn({\n message: validation.error,\n atFunction: \"room.trigger\",\n data: {\n eventName: event.name,\n roomId: id,\n validationError: validation.error,\n },\n });\n return Err(validation.error);\n }\n\n return Ok(validation.value as T);\n }\n\n /**\n * Runs beforeEach hook if configured\n */\n function runBeforeEachHook<T>(\n event: EventDefinition<T>,\n message: EventMessage<T>\n ): Result<EventMessage, string> {\n if (!(hooks?.events?.beforeEach && getContext)) {\n return Ok(message);\n }\n\n const context = getContext();\n const hookResult = hooks.events.beforeEach({\n context,\n roomId: id,\n message,\n from: message.from,\n });\n\n if (hookResult.isErr) {\n logger.debug({\n message: `Event '${event.name}' blocked by beforeEach: ${hookResult.error}`,\n atFunction: \"room.trigger.beforeEach\",\n data: {\n eventName: event.name,\n roomId: id,\n reason: hookResult.error,\n },\n });\n return Err(hookResult.error);\n }\n\n return Ok(hookResult.value);\n }\n\n /**\n * Broadcasts message to subscribed participants\n */\n function broadcastToParticipants(\n eventName: string,\n message: EventMessage\n ): number {\n let recipientCount = 0;\n for (const [, participant] of participants) {\n if (isParticipantSubscribed(participant, id, eventName)) {\n participant.socket.emit(\"dialogue:event\", message);\n recipientCount++;\n }\n }\n return recipientCount;\n }\n\n /**\n * Runs afterEach hook if configured\n */\n function runAfterEachHook(\n message: EventMessage,\n recipientCount: number\n ): void {\n if (!(hooks?.events?.afterEach && getContext)) {\n return;\n }\n\n const context = getContext();\n hooks.events.afterEach({\n context,\n roomId: id,\n message,\n recipientCount,\n });\n }\n\n function handlePostBroadcast(eventName: string, message: EventMessage): void {\n // Store in history if event has history enabled\n const eventDef = getEventByName(eventName, config.events);\n if (eventDef?.history?.enabled && historyManager) {\n historyManager.push(id, eventName, message, eventDef.history.limit);\n }\n\n // Call onTriggered hook if provided\n if (hooks?.events?.onTriggered) {\n Promise.resolve(hooks.events.onTriggered(id, message)).catch((err) => {\n logger.error({\n message: `Error in onTriggered hook for '${eventName}'`,\n atFunction: \"room.trigger.onTriggered\",\n data: err,\n });\n });\n }\n\n // Call server-side event handlers\n const handlers = eventHandlers.get(eventName);\n if (handlers) {\n for (const handler of handlers) {\n Promise.resolve(handler(message)).catch((err) => {\n logger.error({\n message: `Error in event handler for '${eventName}'`,\n atFunction: \"room.trigger.handler\",\n data: err,\n });\n });\n }\n }\n }\n\n const room: Room = {\n id,\n name: config.name,\n description: config.description,\n maxSize: config.maxSize,\n events: config.events,\n defaultSubscriptions: config.defaultSubscriptions ?? [],\n createdById: config.createdById,\n\n trigger<T>(\n event: EventDefinition<T>,\n data: T,\n from?: string,\n meta?: Record<string, unknown>\n ): Result<void, string> {\n const validation = validateEvent(event, data);\n if (validation.isErr) {\n return Err(validation.error);\n }\n\n const message: EventMessage<T> = {\n event: event.name,\n roomId: id,\n data: validation.value as T,\n from: from ?? \"system\",\n timestamp: Date.now(),\n ...(meta && { meta }),\n };\n\n const hookResult = runBeforeEachHook(event, message);\n if (hookResult.isErr) {\n return Err(hookResult.error);\n }\n\n const finalMessage = hookResult.value;\n const recipientCount = broadcastToParticipants(event.name, finalMessage);\n\n handlePostBroadcast(event.name, finalMessage);\n runAfterEachHook(finalMessage, recipientCount);\n\n return Ok(undefined);\n },\n\n on<T>(\n event: EventDefinition<T>,\n handler: (msg: EventMessage<T>) => void | Promise<void>\n ): () => void {\n let handlers = eventHandlers.get(event.name);\n if (!handlers) {\n handlers = new Set();\n eventHandlers.set(event.name, handlers);\n }\n\n handlers.add(handler as EventHandler);\n\n return () => {\n handlers?.delete(handler as EventHandler);\n if (handlers?.size === 0) {\n eventHandlers.delete(event.name);\n }\n };\n },\n\n size(): number {\n return participants.size;\n },\n\n isFull(): boolean {\n if (config.maxSize === undefined) {\n return false;\n }\n return participants.size >= config.maxSize;\n },\n\n participants(): ConnectedClient[] {\n return Array.from(participants.values());\n },\n\n async history(\n eventName: string,\n start: number,\n end: number\n ): Promise<EventMessage[]> {\n // Return empty array if no history manager\n if (!historyManager) {\n return [];\n }\n\n // Get from in-memory history first\n const inMemoryEvents = historyManager.get(id, eventName, start, end);\n const inMemoryCount = historyManager.count(id, eventName);\n\n // If we have enough in-memory or no onLoad hook, return what we have\n if (inMemoryEvents.length >= end - start || !hooks?.events?.onLoad) {\n return inMemoryEvents;\n }\n\n // If requesting beyond in-memory, try loading from external storage\n // Only load what's missing\n const missingStart = Math.max(start, inMemoryCount);\n const missingEnd = end;\n\n if (missingStart >= missingEnd) {\n return inMemoryEvents;\n }\n\n try {\n const externalEvents = await hooks.events.onLoad(\n id,\n eventName,\n missingStart - inMemoryCount,\n missingEnd - inMemoryCount\n );\n\n // Combine in-memory and external events\n return [...inMemoryEvents, ...externalEvents];\n } catch (err) {\n logger.error({\n message: `Error loading history for '${eventName}'`,\n atFunction: \"room.history.onLoad\",\n data: err,\n });\n return inMemoryEvents;\n }\n },\n };\n\n return room;\n}\n\n/**\n * Internal: Adds a participant to the room.\n * Used by the room manager, not directly by consumers.\n */\nexport function addParticipantToRoom(\n room: Room,\n client: ConnectedClient,\n participants: Map<string, ConnectedClient>\n): boolean {\n // Check capacity using the passed-in participants map (room manager's map)\n if (room.maxSize !== undefined && participants.size >= room.maxSize) {\n return false;\n }\n\n participants.set(client.id, client);\n client.socket.join(room.id);\n\n return true;\n}\n\n/**\n * Internal: Removes a participant from the room.\n */\nexport function removeParticipantFromRoom(\n room: Room,\n clientId: string,\n participants: Map<string, ConnectedClient>\n): void {\n const client = participants.get(clientId);\n if (client) {\n client.socket.leave(room.id);\n participants.delete(clientId);\n }\n}\n\n/**\n * Creates a room manager that coordinates all rooms.\n * Used internally by createDialogue.\n *\n * @param io - Socket.IO server instance\n * @param logger - Logger instance\n * @param historyManager - Optional history manager for event storage\n * @param hooks - Optional hooks config for lifecycle callbacks\n */\nexport function createRoomManager(\n io: Server,\n logger: Logger,\n historyManager?: HistoryManager,\n hooks?: HooksConfig,\n getContext?: () => DialogueContext\n) {\n const rooms = new Map<string, Room>();\n const roomParticipants = new Map<string, Map<string, ConnectedClient>>();\n\n return {\n /**\n * Registers a room from config\n */\n register(id: string, config: RoomConfig): Room {\n const participantsMap = new Map<string, ConnectedClient>();\n roomParticipants.set(id, participantsMap);\n const room = createRoom(\n id,\n config,\n io,\n logger,\n historyManager,\n hooks,\n participantsMap,\n getContext\n );\n rooms.set(id, room);\n\n // Call onCreated hook if provided\n if (hooks?.rooms?.onCreated) {\n Promise.resolve(hooks.rooms.onCreated(room)).catch((err) => {\n logger.error({\n message: `Error in onCreated hook for room '${id}'`,\n atFunction: \"roomManager.register.onCreated\",\n data: err,\n });\n });\n }\n\n return room;\n },\n\n /**\n * Gets a room by ID\n */\n get(id: string): Room | null {\n return rooms.get(id) ?? null;\n },\n\n /**\n * Gets all rooms\n */\n all(): Room[] {\n return Array.from(rooms.values());\n },\n\n /**\n * Adds a participant to a room\n */\n addParticipant(roomId: string, client: ConnectedClient): boolean {\n const room = rooms.get(roomId);\n const participants = roomParticipants.get(roomId);\n\n if (!(room && participants)) {\n return false;\n }\n\n return addParticipantToRoom(room, client, participants);\n },\n\n /**\n * Removes a participant from a room\n */\n removeParticipant(roomId: string, clientId: string): void {\n const room = rooms.get(roomId);\n const participants = roomParticipants.get(roomId);\n\n if (room && participants) {\n removeParticipantFromRoom(room, clientId, participants);\n }\n },\n\n /**\n * Removes a client from all rooms\n */\n removeFromAllRooms(clientId: string): void {\n for (const [roomId] of rooms) {\n this.removeParticipant(roomId, clientId);\n }\n },\n\n /**\n * Gets participants for a room\n */\n getParticipants(roomId: string): ConnectedClient[] {\n const participants = roomParticipants.get(roomId);\n return participants ? Array.from(participants.values()) : [];\n },\n\n /**\n * Gets room size\n */\n getRoomSize(roomId: string): number {\n return roomParticipants.get(roomId)?.size ?? 0;\n },\n\n /**\n * Unregisters (deletes) a room by ID.\n * Removes all participants and cleans up resources.\n * @returns true if room was deleted, false if it didn't exist\n */\n unregister(id: string): boolean {\n const room = rooms.get(id);\n if (!room) {\n return false;\n }\n\n // Remove all participants from the room\n const participants = roomParticipants.get(id);\n if (participants) {\n for (const client of participants.values()) {\n client.socket.leave(id);\n }\n participants.clear();\n }\n\n // Clear history for this room\n if (historyManager) {\n historyManager.clearRoom(id);\n }\n\n // Notify all sockets in the room that it's been deleted\n io.to(id).emit(\"dialogue:roomDeleted\", { roomId: id });\n\n rooms.delete(id);\n roomParticipants.delete(id);\n\n logger.info({\n message: `Room '${id}' deleted`,\n atFunction: \"roomManager.unregister\",\n data: { roomId: id },\n });\n\n // Call onDeleted hook if provided\n if (hooks?.rooms?.onDeleted) {\n Promise.resolve(hooks.rooms.onDeleted(id)).catch((err) => {\n logger.error({\n message: `Error in onDeleted hook for room '${id}'`,\n atFunction: \"roomManager.unregister.onDeleted\",\n data: err,\n });\n });\n }\n\n return true;\n },\n };\n}\n\nexport type RoomManagerInstance = ReturnType<typeof createRoomManager>;\n","import { Err, Ok, type Result } from \"slang-ts\";\nimport type { z } from \"zod\";\nimport type { EventDefinition, EventHistoryConfig } from \"./types.ts\";\n\n/**\n * Options for defining an event\n */\ninterface DefineEventOptions<T> {\n /** Zod schema for validating event data */\n schema?: z.ZodType<T>;\n /** Human-readable description of the event */\n description?: string;\n /** History configuration - when enabled, events are stored in memory */\n history?: EventHistoryConfig;\n}\n\n/**\n * Creates a typed event definition for use in rooms.\n * Events are immutable once created.\n *\n * @param name - Unique event name (e.g., 'message', 'order:updated')\n * @param options - Optional schema and description\n * @returns Frozen event definition object\n *\n * @example\n * // Simple event without validation\n * const Typing = defineEvent('typing')\n *\n * @example\n * // Event with Zod schema validation\n * const Message = defineEvent('message', {\n * schema: z.object({\n * text: z.string().min(1).max(1000),\n * senderId: z.string()\n * }),\n * description: 'Chat message sent by a user',\n * history: { enabled: true, limit: 50 }\n * })\n *\n * @example\n * // Event with inferred type from schema\n * const OrderUpdated = defineEvent('order:updated', {\n * schema: z.object({\n * orderId: z.string(),\n * status: z.enum(['pending', 'shipped', 'delivered'])\n * })\n * })\n * // Type of data is inferred as { orderId: string, status: 'pending' | 'shipped' | 'delivered' }\n */\nexport function defineEvent<T = unknown>(\n name: string,\n options?: DefineEventOptions<T>\n): EventDefinition<T> {\n const definition: EventDefinition<T> = {\n name,\n description: options?.description,\n schema: options?.schema,\n history: options?.history,\n };\n\n return Object.freeze(definition);\n}\n\n/**\n * Validates event data against its schema if one exists.\n * Returns a Result with either parsed data or error message.\n *\n * @param event - The event definition with optional schema\n * @param data - Data to validate\n * @returns Result<T, string> - Ok with data or Err with validation message\n */\nexport function validateEventData<T>(\n event: EventDefinition<T>,\n data: unknown\n): Result<T, string> {\n if (!event.schema) {\n return Ok(data as T);\n }\n\n const result = event.schema.safeParse(data);\n\n if (result.success) {\n return Ok(result.data);\n }\n\n const errorMessages = result.error.issues\n .map((issue) => `${issue.path.join(\".\")}: ${issue.message}`)\n .join(\", \");\n\n return Err(`Event '${event.name}' validation failed: ${errorMessages}`);\n}\n\n/**\n * Checks if an event is allowed in a room based on its event definitions.\n * - Empty array: No events allowed (reject all)\n * - Wildcard \"*\": All events allowed (accept all)\n * - Specific events: Only listed events allowed\n *\n * @param eventName - Name of the event to check\n * @param allowedEvents - List of allowed events for the room\n * @returns True if event is allowed (explicit match or wildcard)\n */\nexport function isEventAllowed(\n eventName: string,\n allowedEvents: EventDefinition<unknown>[]\n): boolean {\n // Empty array means no events allowed\n if (allowedEvents.length === 0) {\n return false;\n }\n\n // Check for wildcard or specific event match\n return allowedEvents.some((e) => e.name === \"*\" || e.name === eventName);\n}\n\n/**\n * Gets an event definition by name from a list of events.\n *\n * @param eventName - Name of the event to find\n * @param events - List of event definitions to search\n * @returns The event definition or undefined if not found\n */\nexport function getEventByName(\n eventName: string,\n events: EventDefinition<unknown>[]\n): EventDefinition<unknown> | undefined {\n return events.find((e) => e.name === eventName);\n}\n"],"mappings":"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","names":["Err","Err","Ok","WILDCARD","Err","Ok","userSockets","Err","config"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dialogue-ts",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "An event-based realtime communication library based on Socket.IO
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "An event-based realtime communication library based on Socket.IO and Hono, supporting Bun and Node.js runtimes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.cjs",
|
|
7
7
|
"module": "./dist/src/index.js",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"real-time",
|
|
39
39
|
"hono",
|
|
40
40
|
"bun",
|
|
41
|
+
"node",
|
|
41
42
|
"typescript",
|
|
42
43
|
"events",
|
|
43
44
|
"slang-ts"
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"zod": "^3.0.0 || ^4.0.0"
|
|
62
63
|
},
|
|
63
64
|
"dependencies": {
|
|
65
|
+
"@hono/node-server": "^1.19.9",
|
|
64
66
|
"@socket.io/bun-engine": "^0.1.0",
|
|
65
67
|
"hono": "^4.11.9",
|
|
66
68
|
"nanoid": "^5.1.6",
|