@parsrun/realtime 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -38,8 +38,8 @@ declare class ChannelImpl implements Channel {
|
|
|
38
38
|
declare function createChannel(name: string, adapter: RealtimeAdapter, sessionId: string): Channel;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* @
|
|
42
|
-
* Edge-compatible realtime for Pars
|
|
41
|
+
* @module
|
|
42
|
+
* Edge-compatible realtime for Pars.
|
|
43
43
|
*
|
|
44
44
|
* Supports multiple adapters:
|
|
45
45
|
* - SSE (Server-Sent Events) - Works on all runtimes
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/adapters/sse.ts","../src/adapters/durable-objects.ts","../src/hono.ts","../src/core/channel.ts","../src/index.ts"],"sourcesContent":["/**\n * @parsrun/realtime - Type Definitions\n * Realtime communication types and interfaces\n */\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\n/**\n * Realtime adapter type\n */\nexport type RealtimeAdapterType = \"sse\" | \"durable-objects\" | \"memory\";\n\n/**\n * Realtime message\n */\nexport interface RealtimeMessage<T = unknown> {\n /** Unique message ID */\n id: string;\n /** Event type/name */\n event: string;\n /** Channel name */\n channel: string;\n /** Message payload */\n data: T;\n /** Sender ID (optional) */\n senderId?: string | undefined;\n /** Timestamp */\n timestamp: number;\n /** Metadata */\n metadata?: Record<string, unknown> | undefined;\n}\n\n/**\n * Presence user data\n */\nexport interface PresenceUser<T = unknown> {\n /** User ID */\n userId: string;\n /** Session ID */\n sessionId: string;\n /** Custom presence data */\n data: T;\n /** Join timestamp */\n joinedAt: number;\n /** Last activity timestamp */\n lastSeenAt: number;\n}\n\n/**\n * Channel info\n */\nexport interface ChannelInfo {\n /** Channel name */\n name: string;\n /** Number of subscribers */\n subscriberCount: number;\n /** Presence users */\n presence: PresenceUser[];\n /** Channel metadata */\n metadata?: Record<string, unknown> | undefined;\n}\n\n// ============================================================================\n// Events\n// ============================================================================\n\n/**\n * System event types\n */\nexport type SystemEventType =\n | \"connection:open\"\n | \"connection:close\"\n | \"connection:error\"\n | \"presence:join\"\n | \"presence:leave\"\n | \"presence:update\"\n | \"channel:subscribe\"\n | \"channel:unsubscribe\"\n | \"ping\"\n | \"pong\";\n\n/**\n * Message handler function\n */\nexport type MessageHandler<T = unknown> = (\n message: RealtimeMessage<T>\n) => void | Promise<void>;\n\n/**\n * Connection event handler\n */\nexport type ConnectionHandler = (\n event: ConnectionEvent\n) => void | Promise<void>;\n\n/**\n * Connection event\n */\nexport interface ConnectionEvent {\n type: \"open\" | \"close\" | \"error\";\n sessionId: string;\n userId?: string | undefined;\n error?: Error | undefined;\n timestamp: number;\n}\n\n/**\n * Presence event\n */\nexport interface PresenceEvent<T = unknown> {\n type: \"join\" | \"leave\" | \"update\";\n channel: string;\n user: PresenceUser<T>;\n timestamp: number;\n}\n\n// ============================================================================\n// Channel Interface\n// ============================================================================\n\n/**\n * Channel interface for realtime communication\n */\nexport interface Channel {\n /** Channel name */\n readonly name: string;\n\n /**\n * Broadcast message to all subscribers\n */\n broadcast<T = unknown>(event: string, data: T): Promise<void>;\n\n /**\n * Send message to specific user\n */\n send<T = unknown>(userId: string, event: string, data: T): Promise<void>;\n\n /**\n * Get current presence users\n */\n getPresence<T = unknown>(): Promise<PresenceUser<T>[]>;\n\n /**\n * Subscribe to channel messages\n */\n subscribe<T = unknown>(handler: MessageHandler<T>): () => void;\n\n /**\n * Subscribe to presence events\n */\n onPresence<T = unknown>(\n handler: (event: PresenceEvent<T>) => void\n ): () => void;\n\n /**\n * Get channel info\n */\n getInfo(): Promise<ChannelInfo>;\n}\n\n// ============================================================================\n// Realtime Service Interface\n// ============================================================================\n\n/**\n * Realtime service interface\n */\nexport interface RealtimeService {\n /** Adapter type */\n readonly adapterType: RealtimeAdapterType;\n\n /**\n * Get or create a channel\n */\n channel(name: string): Channel;\n\n /**\n * Broadcast to a channel\n */\n broadcast<T = unknown>(channel: string, event: string, data: T): Promise<void>;\n\n /**\n * Get channel info\n */\n getChannelInfo(channel: string): Promise<ChannelInfo | null>;\n\n /**\n * List active channels\n */\n listChannels(): Promise<string[]>;\n\n /**\n * Subscribe to connection events\n */\n onConnection(handler: ConnectionHandler): () => void;\n\n /**\n * Close all connections and cleanup\n */\n close(): Promise<void>;\n}\n\n// ============================================================================\n// Adapter Interface\n// ============================================================================\n\n/**\n * Realtime adapter interface (low-level)\n */\nexport interface RealtimeAdapter {\n /** Adapter type */\n readonly type: RealtimeAdapterType;\n\n /**\n * Add a subscriber to channel\n */\n subscribe(\n channel: string,\n sessionId: string,\n handler: MessageHandler\n ): Promise<void>;\n\n /**\n * Remove a subscriber from channel\n */\n unsubscribe(channel: string, sessionId: string): Promise<void>;\n\n /**\n * Publish message to channel\n */\n publish<T = unknown>(channel: string, message: RealtimeMessage<T>): Promise<void>;\n\n /**\n * Send to specific session\n */\n sendToSession<T = unknown>(\n sessionId: string,\n message: RealtimeMessage<T>\n ): Promise<boolean>;\n\n /**\n * Get subscribers for channel\n */\n getSubscribers(channel: string): Promise<string[]>;\n\n /**\n * Set presence for user in channel\n */\n setPresence<T = unknown>(\n channel: string,\n sessionId: string,\n userId: string,\n data: T\n ): Promise<void>;\n\n /**\n * Remove presence\n */\n removePresence(channel: string, sessionId: string): Promise<void>;\n\n /**\n * Get presence for channel\n */\n getPresence<T = unknown>(channel: string): Promise<PresenceUser<T>[]>;\n\n /**\n * Cleanup and close adapter\n */\n close(): Promise<void>;\n}\n\n// ============================================================================\n// SSE Types\n// ============================================================================\n\n/**\n * SSE connection\n */\nexport interface SSEConnection {\n /** Session ID */\n sessionId: string;\n /** User ID */\n userId?: string | undefined;\n /** Subscribed channels */\n channels: Set<string>;\n /** Response writer */\n writer: WritableStreamDefaultWriter<Uint8Array>;\n /** Connection state */\n state: \"open\" | \"closed\";\n /** Created timestamp */\n createdAt: number;\n /** Last ping timestamp */\n lastPingAt: number;\n}\n\n/**\n * SSE adapter options\n */\nexport interface SSEAdapterOptions {\n /** Ping interval in milliseconds (default: 30000) */\n pingInterval?: number | undefined;\n /** Connection timeout in milliseconds (default: 0 = no timeout) */\n connectionTimeout?: number | undefined;\n /** Max connections per channel (default: 1000) */\n maxConnectionsPerChannel?: number | undefined;\n /** Retry delay for client reconnection (default: 3000) */\n retryDelay?: number | undefined;\n}\n\n// ============================================================================\n// Durable Objects Types\n// ============================================================================\n\n/**\n * Durable Objects adapter options\n */\nexport interface DurableObjectsAdapterOptions {\n /** Durable Object namespace binding */\n namespace: DurableObjectNamespace;\n /** Channel prefix (default: \"channel:\") */\n channelPrefix?: string | undefined;\n}\n\n/**\n * Durable Object WebSocket session\n */\nexport interface DOWebSocketSession {\n /** Session ID */\n sessionId: string;\n /** User ID */\n userId?: string | undefined;\n /** WebSocket connection */\n webSocket: WebSocket;\n /** Presence data */\n presence?: unknown;\n /** Connection state */\n state: \"connecting\" | \"open\" | \"closing\" | \"closed\";\n /** Created timestamp */\n createdAt: number;\n}\n\n/**\n * Durable Object state stored in storage\n */\nexport interface DOChannelState {\n /** Channel name */\n name: string;\n /** Channel metadata */\n metadata?: Record<string, unknown> | undefined;\n /** Created timestamp */\n createdAt: number;\n}\n\n// ============================================================================\n// Config Types\n// ============================================================================\n\n/**\n * Realtime config\n */\nexport interface RealtimeConfig {\n /** Adapter type */\n adapter: RealtimeAdapterType;\n\n /** SSE adapter options */\n sse?: SSEAdapterOptions | undefined;\n\n /** Durable Objects adapter options */\n durableObjects?: DurableObjectsAdapterOptions | undefined;\n\n /** Enable debug logging */\n debug?: boolean | undefined;\n}\n\n// ============================================================================\n// Errors\n// ============================================================================\n\n/**\n * Realtime error\n */\nexport class RealtimeError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = \"RealtimeError\";\n }\n}\n\n/**\n * Common error codes\n */\nexport const RealtimeErrorCodes = {\n CONNECTION_FAILED: \"CONNECTION_FAILED\",\n CHANNEL_NOT_FOUND: \"CHANNEL_NOT_FOUND\",\n UNAUTHORIZED: \"UNAUTHORIZED\",\n MESSAGE_TOO_LARGE: \"MESSAGE_TOO_LARGE\",\n RATE_LIMITED: \"RATE_LIMITED\",\n ADAPTER_ERROR: \"ADAPTER_ERROR\",\n INVALID_MESSAGE: \"INVALID_MESSAGE\",\n} as const;\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Create message options\n */\nexport interface CreateMessageOptions<T = unknown> {\n event: string;\n channel: string;\n data: T;\n senderId?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n}\n\n/**\n * Create a realtime message\n */\nexport function createMessage<T = unknown>(\n options: CreateMessageOptions<T>\n): RealtimeMessage<T> {\n return {\n id: crypto.randomUUID(),\n event: options.event,\n channel: options.channel,\n data: options.data,\n senderId: options.senderId,\n timestamp: Date.now(),\n metadata: options.metadata,\n };\n}\n\n/**\n * Parse SSE event string\n */\nexport function parseSSEEvent(eventString: string): RealtimeMessage | null {\n try {\n const lines = eventString.split(\"\\n\");\n let event = \"message\";\n let data = \"\";\n let id = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event:\")) {\n event = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n data = line.slice(5).trim();\n } else if (line.startsWith(\"id:\")) {\n id = line.slice(3).trim();\n }\n }\n\n if (!data) return null;\n\n const parsed = JSON.parse(data);\n return {\n id: id || parsed.id || crypto.randomUUID(),\n event,\n channel: parsed.channel || \"\",\n data: parsed.data ?? parsed,\n senderId: parsed.senderId,\n timestamp: parsed.timestamp || Date.now(),\n metadata: parsed.metadata,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Format message as SSE event string\n */\nexport function formatSSEEvent(message: RealtimeMessage): string {\n const lines: string[] = [];\n\n lines.push(`id:${message.id}`);\n lines.push(`event:${message.event}`);\n lines.push(`data:${JSON.stringify(message)}`);\n lines.push(\"\"); // Empty line to end event\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","/**\n * @parsrun/realtime - SSE Adapter\n * Server-Sent Events adapter for all runtimes\n */\n\nimport type {\n MessageHandler,\n PresenceUser,\n RealtimeAdapter,\n RealtimeMessage,\n SSEAdapterOptions,\n SSEConnection,\n} from \"../types.js\";\nimport { formatSSEEvent, RealtimeError, RealtimeErrorCodes } from \"../types.js\";\n\n/**\n * Resolved SSE adapter options (all required)\n */\ninterface ResolvedSSEOptions {\n pingInterval: number;\n connectionTimeout: number;\n maxConnectionsPerChannel: number;\n retryDelay: number;\n}\n\n/**\n * Default SSE adapter options\n */\nconst DEFAULT_OPTIONS: ResolvedSSEOptions = {\n pingInterval: 30000,\n connectionTimeout: 0,\n maxConnectionsPerChannel: 1000,\n retryDelay: 3000,\n};\n\n/**\n * SSE Adapter\n * Works on all runtimes (Node, Deno, Bun, Cloudflare Workers)\n */\nexport class SSEAdapter implements RealtimeAdapter {\n readonly type = \"sse\" as const;\n\n private options: ResolvedSSEOptions;\n private connections: Map<string, SSEConnection> = new Map();\n private channelSubscribers: Map<string, Map<string, MessageHandler>> = new Map();\n private presence: Map<string, Map<string, PresenceUser>> = new Map();\n private pingIntervalId: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: SSEAdapterOptions = {}) {\n this.options = {\n pingInterval: options.pingInterval ?? DEFAULT_OPTIONS.pingInterval,\n connectionTimeout: options.connectionTimeout ?? DEFAULT_OPTIONS.connectionTimeout,\n maxConnectionsPerChannel: options.maxConnectionsPerChannel ?? DEFAULT_OPTIONS.maxConnectionsPerChannel,\n retryDelay: options.retryDelay ?? DEFAULT_OPTIONS.retryDelay,\n };\n\n // Start ping interval\n if (this.options.pingInterval > 0) {\n this.startPingInterval();\n }\n }\n\n /**\n * Create SSE response for a new connection\n */\n createConnection(\n sessionId: string,\n userId?: string\n ): { response: Response; connection: SSEConnection } {\n const { readable, writable } = new TransformStream<Uint8Array>();\n const writer = writable.getWriter();\n\n const connection: SSEConnection = {\n sessionId,\n userId,\n channels: new Set(),\n writer,\n state: \"open\",\n createdAt: Date.now(),\n lastPingAt: Date.now(),\n };\n\n this.connections.set(sessionId, connection);\n\n // Send initial retry directive\n const encoder = new TextEncoder();\n writer.write(encoder.encode(`retry:${this.options.retryDelay}\\n\\n`));\n\n const response = new Response(readable, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"X-Accel-Buffering\": \"no\", // Disable nginx buffering\n },\n });\n\n return { response, connection };\n }\n\n /**\n * Close a connection\n */\n async closeConnection(sessionId: string): Promise<void> {\n const connection = this.connections.get(sessionId);\n if (!connection) return;\n\n connection.state = \"closed\";\n\n // Remove from all channels\n for (const channel of connection.channels) {\n await this.unsubscribe(channel, sessionId);\n await this.removePresence(channel, sessionId);\n }\n\n // Close writer\n try {\n await connection.writer.close();\n } catch {\n // Ignore close errors\n }\n\n this.connections.delete(sessionId);\n }\n\n /**\n * Get a connection by session ID\n */\n getConnection(sessionId: string): SSEConnection | undefined {\n return this.connections.get(sessionId);\n }\n\n /**\n * Get all active connections\n */\n getConnections(): Map<string, SSEConnection> {\n return this.connections;\n }\n\n // ============================================================================\n // RealtimeAdapter Implementation\n // ============================================================================\n\n async subscribe(\n channel: string,\n sessionId: string,\n handler: MessageHandler\n ): Promise<void> {\n const connection = this.connections.get(sessionId);\n if (!connection) {\n throw new RealtimeError(\n \"Connection not found\",\n RealtimeErrorCodes.CONNECTION_FAILED\n );\n }\n\n // Check max connections per channel\n const subscribers = this.channelSubscribers.get(channel);\n if (\n subscribers &&\n subscribers.size >= this.options.maxConnectionsPerChannel\n ) {\n throw new RealtimeError(\n \"Channel subscriber limit reached\",\n RealtimeErrorCodes.RATE_LIMITED\n );\n }\n\n // Add to channel subscribers\n if (!this.channelSubscribers.has(channel)) {\n this.channelSubscribers.set(channel, new Map());\n }\n this.channelSubscribers.get(channel)!.set(sessionId, handler);\n\n // Track channel on connection\n connection.channels.add(channel);\n\n // Send subscription confirmation\n await this.sendToConnection(connection, {\n id: crypto.randomUUID(),\n event: \"channel:subscribe\",\n channel,\n data: { channel },\n timestamp: Date.now(),\n });\n }\n\n async unsubscribe(channel: string, sessionId: string): Promise<void> {\n const subscribers = this.channelSubscribers.get(channel);\n if (subscribers) {\n subscribers.delete(sessionId);\n if (subscribers.size === 0) {\n this.channelSubscribers.delete(channel);\n }\n }\n\n const connection = this.connections.get(sessionId);\n if (connection) {\n connection.channels.delete(channel);\n }\n }\n\n async publish<T = unknown>(\n channel: string,\n message: RealtimeMessage<T>\n ): Promise<void> {\n const subscribers = this.channelSubscribers.get(channel);\n if (!subscribers) return;\n\n const promises: Promise<void>[] = [];\n\n for (const [sessionId, handler] of subscribers) {\n const connection = this.connections.get(sessionId);\n if (connection && connection.state === \"open\") {\n // Send via SSE\n promises.push(this.sendToConnection(connection, message));\n\n // Call handler\n try {\n const result = handler(message);\n if (result instanceof Promise) {\n promises.push(result.then(() => {}));\n }\n } catch {\n // Ignore handler errors\n }\n }\n }\n\n await Promise.allSettled(promises);\n }\n\n async sendToSession<T = unknown>(\n sessionId: string,\n message: RealtimeMessage<T>\n ): Promise<boolean> {\n const connection = this.connections.get(sessionId);\n if (!connection || connection.state !== \"open\") {\n return false;\n }\n\n try {\n await this.sendToConnection(connection, message);\n return true;\n } catch {\n return false;\n }\n }\n\n async getSubscribers(channel: string): Promise<string[]> {\n const subscribers = this.channelSubscribers.get(channel);\n return subscribers ? Array.from(subscribers.keys()) : [];\n }\n\n async setPresence<T = unknown>(\n channel: string,\n sessionId: string,\n userId: string,\n data: T\n ): Promise<void> {\n if (!this.presence.has(channel)) {\n this.presence.set(channel, new Map());\n }\n\n const now = Date.now();\n const existing = this.presence.get(channel)!.get(sessionId);\n\n const user: PresenceUser<T> = {\n userId,\n sessionId,\n data,\n joinedAt: existing?.joinedAt ?? now,\n lastSeenAt: now,\n };\n\n this.presence.get(channel)!.set(sessionId, user as PresenceUser);\n\n // Broadcast presence update\n const eventType = existing ? \"presence:update\" : \"presence:join\";\n await this.publish(channel, {\n id: crypto.randomUUID(),\n event: eventType,\n channel,\n data: {\n type: existing ? \"update\" : \"join\",\n user,\n presence: await this.getPresence(channel),\n },\n timestamp: now,\n });\n }\n\n async removePresence(channel: string, sessionId: string): Promise<void> {\n const channelPresence = this.presence.get(channel);\n if (!channelPresence) return;\n\n const user = channelPresence.get(sessionId);\n if (!user) return;\n\n channelPresence.delete(sessionId);\n if (channelPresence.size === 0) {\n this.presence.delete(channel);\n }\n\n // Broadcast presence leave\n await this.publish(channel, {\n id: crypto.randomUUID(),\n event: \"presence:leave\",\n channel,\n data: {\n type: \"leave\",\n user,\n presence: await this.getPresence(channel),\n },\n timestamp: Date.now(),\n });\n }\n\n async getPresence<T = unknown>(channel: string): Promise<PresenceUser<T>[]> {\n const channelPresence = this.presence.get(channel);\n if (!channelPresence) return [];\n return Array.from(channelPresence.values()) as PresenceUser<T>[];\n }\n\n async close(): Promise<void> {\n // Stop ping interval\n if (this.pingIntervalId) {\n clearInterval(this.pingIntervalId);\n this.pingIntervalId = null;\n }\n\n // Close all connections\n const closePromises = Array.from(this.connections.keys()).map((sessionId) =>\n this.closeConnection(sessionId)\n );\n await Promise.allSettled(closePromises);\n\n // Clear all data\n this.connections.clear();\n this.channelSubscribers.clear();\n this.presence.clear();\n }\n\n // ============================================================================\n // Private Methods\n // ============================================================================\n\n private async sendToConnection<T = unknown>(\n connection: SSEConnection,\n message: RealtimeMessage<T>\n ): Promise<void> {\n if (connection.state !== \"open\") return;\n\n const encoder = new TextEncoder();\n const sseEvent = formatSSEEvent(message as RealtimeMessage);\n\n try {\n await connection.writer.write(encoder.encode(sseEvent));\n } catch (err) {\n // Connection likely closed\n connection.state = \"closed\";\n await this.closeConnection(connection.sessionId);\n }\n }\n\n private startPingInterval(): void {\n this.pingIntervalId = setInterval(() => {\n this.sendPingToAll();\n }, this.options.pingInterval);\n }\n\n private async sendPingToAll(): Promise<void> {\n const now = Date.now();\n const encoder = new TextEncoder();\n const pingEvent = `event:ping\\ndata:${now}\\n\\n`;\n\n for (const [sessionId, connection] of this.connections) {\n if (connection.state !== \"open\") continue;\n\n // Check connection timeout\n if (\n this.options.connectionTimeout > 0 &&\n now - connection.lastPingAt > this.options.connectionTimeout\n ) {\n await this.closeConnection(sessionId);\n continue;\n }\n\n try {\n await connection.writer.write(encoder.encode(pingEvent));\n connection.lastPingAt = now;\n } catch {\n // Connection likely closed\n await this.closeConnection(sessionId);\n }\n }\n }\n\n /**\n * Update last ping time (call when receiving client ping)\n */\n updateLastPing(sessionId: string): void {\n const connection = this.connections.get(sessionId);\n if (connection) {\n connection.lastPingAt = Date.now();\n }\n }\n\n /**\n * Get statistics\n */\n getStats(): {\n totalConnections: number;\n totalChannels: number;\n connectionsByChannel: Record<string, number>;\n } {\n const connectionsByChannel: Record<string, number> = {};\n\n for (const [channel, subscribers] of this.channelSubscribers) {\n connectionsByChannel[channel] = subscribers.size;\n }\n\n return {\n totalConnections: this.connections.size,\n totalChannels: this.channelSubscribers.size,\n connectionsByChannel,\n };\n }\n}\n\n/**\n * Create SSE adapter\n */\nexport function createSSEAdapter(options?: SSEAdapterOptions): SSEAdapter {\n return new SSEAdapter(options);\n}\n","/**\n * @parsrun/realtime - Durable Objects Adapter\n * Cloudflare Durable Objects adapter for WebSocket-based realtime\n */\n\nimport type {\n DOWebSocketSession,\n DurableObjectsAdapterOptions,\n MessageHandler,\n PresenceUser,\n RealtimeAdapter,\n RealtimeMessage,\n} from \"../types.js\";\nimport { createMessage } from \"../types.js\";\n\n// ============================================================================\n// Durable Object Class (Export for wrangler.toml binding)\n// ============================================================================\n\n/**\n * RealtimeChannel Durable Object\n * Manages a single realtime channel with WebSocket connections\n *\n * Usage in wrangler.toml:\n * ```toml\n * [durable_objects]\n * bindings = [{ name = \"REALTIME_CHANNELS\", class_name = \"RealtimeChannelDO\" }]\n *\n * [[migrations]]\n * tag = \"v1\"\n * new_classes = [\"RealtimeChannelDO\"]\n * ```\n */\nexport class RealtimeChannelDO implements DurableObject {\n private sessions: Map<string, DOWebSocketSession> = new Map();\n private presence: Map<string, PresenceUser> = new Map();\n private channelName: string = \"\";\n private state: DurableObjectState;\n\n constructor(\n state: DurableObjectState,\n _env: unknown\n ) {\n this.state = state;\n // Restore hibernated WebSocket sessions\n this.state.getWebSockets().forEach((ws) => {\n const meta = ws.deserializeAttachment() as {\n sessionId: string;\n userId?: string;\n } | null;\n if (meta) {\n this.sessions.set(meta.sessionId, {\n sessionId: meta.sessionId,\n userId: meta.userId,\n webSocket: ws,\n state: \"open\",\n createdAt: Date.now(),\n });\n }\n });\n }\n\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n // Extract channel name from path\n const pathParts = url.pathname.split(\"/\").filter(Boolean);\n const lastPart = pathParts[pathParts.length - 1];\n if (lastPart) {\n this.channelName = lastPart;\n }\n\n // WebSocket upgrade\n if (request.headers.get(\"Upgrade\") === \"websocket\") {\n return this.handleWebSocketUpgrade(request);\n }\n\n // REST API endpoints\n switch (url.pathname.split(\"/\").pop()) {\n case \"broadcast\":\n return this.handleBroadcast(request);\n case \"presence\":\n return this.handleGetPresence();\n case \"info\":\n return this.handleGetInfo();\n case \"send\":\n return this.handleSendToUser(request);\n default:\n return new Response(\"Not found\", { status: 404 });\n }\n }\n\n // ============================================================================\n // WebSocket Handling\n // ============================================================================\n\n private handleWebSocketUpgrade(request: Request): Response {\n const url = new URL(request.url);\n const sessionId = url.searchParams.get(\"sessionId\") || crypto.randomUUID();\n const userId = url.searchParams.get(\"userId\") || undefined;\n\n const pair = new WebSocketPair();\n const client = pair[0];\n const server = pair[1];\n\n // Store session metadata for hibernation\n server.serializeAttachment({ sessionId, userId });\n\n this.state.acceptWebSocket(server);\n\n const session: DOWebSocketSession = {\n sessionId,\n userId,\n webSocket: server,\n state: \"open\",\n createdAt: Date.now(),\n };\n\n this.sessions.set(sessionId, session);\n\n // Send connection confirmation\n server.send(\n JSON.stringify(\n createMessage({\n event: \"connection:open\",\n channel: this.channelName,\n data: { sessionId, userId },\n })\n )\n );\n\n return new Response(null, { status: 101, webSocket: client });\n }\n\n async webSocketMessage(\n ws: WebSocket,\n message: string | ArrayBuffer\n ): Promise<void> {\n const session = this.getSessionByWebSocket(ws);\n if (!session) return;\n\n try {\n const data =\n typeof message === \"string\"\n ? message\n : new TextDecoder().decode(message);\n\n const parsed = JSON.parse(data) as RealtimeMessage;\n await this.handleMessage(session, parsed);\n } catch {\n // Invalid message format, ignore\n }\n }\n\n async webSocketClose(\n ws: WebSocket,\n code: number,\n reason: string,\n _wasClean: boolean\n ): Promise<void> {\n const session = this.getSessionByWebSocket(ws);\n if (!session) return;\n\n // Remove presence\n this.presence.delete(session.sessionId);\n await this.broadcastPresenceUpdate();\n\n // Remove session\n this.sessions.delete(session.sessionId);\n\n // Broadcast leave event\n await this.broadcastToAll({\n event: \"connection:close\",\n channel: this.channelName,\n data: {\n sessionId: session.sessionId,\n userId: session.userId,\n code,\n reason,\n },\n });\n }\n\n async webSocketError(ws: WebSocket, _error: unknown): Promise<void> {\n const session = this.getSessionByWebSocket(ws);\n if (session) {\n session.state = \"closed\";\n this.sessions.delete(session.sessionId);\n this.presence.delete(session.sessionId);\n }\n }\n\n // ============================================================================\n // Message Handling\n // ============================================================================\n\n private async handleMessage(\n session: DOWebSocketSession,\n message: RealtimeMessage\n ): Promise<void> {\n switch (message.event) {\n case \"ping\":\n session.webSocket.send(\n JSON.stringify(\n createMessage({\n event: \"pong\",\n channel: this.channelName,\n data: { timestamp: Date.now() },\n })\n )\n );\n break;\n\n case \"presence:join\":\n await this.handlePresenceJoin(session, message.data);\n break;\n\n case \"presence:update\":\n await this.handlePresenceUpdate(session, message.data);\n break;\n\n case \"presence:leave\":\n await this.handlePresenceLeave(session);\n break;\n\n case \"broadcast\":\n // Broadcast to all except sender\n await this.broadcastToAll(\n {\n event: message.event,\n channel: this.channelName,\n data: message.data,\n senderId: session.sessionId,\n },\n session.sessionId\n );\n break;\n\n default:\n // Forward custom events\n await this.broadcastToAll(\n {\n event: message.event,\n channel: this.channelName,\n data: message.data,\n senderId: session.sessionId,\n },\n session.sessionId\n );\n }\n }\n\n // ============================================================================\n // Presence Handling\n // ============================================================================\n\n private async handlePresenceJoin(\n session: DOWebSocketSession,\n data: unknown\n ): Promise<void> {\n const now = Date.now();\n const user: PresenceUser = {\n userId: session.userId || session.sessionId,\n sessionId: session.sessionId,\n data,\n joinedAt: now,\n lastSeenAt: now,\n };\n\n this.presence.set(session.sessionId, user);\n session.presence = data;\n\n await this.broadcastPresenceUpdate();\n }\n\n private async handlePresenceUpdate(\n session: DOWebSocketSession,\n data: unknown\n ): Promise<void> {\n const existing = this.presence.get(session.sessionId);\n if (existing) {\n existing.data = data;\n existing.lastSeenAt = Date.now();\n session.presence = data;\n await this.broadcastPresenceUpdate();\n }\n }\n\n private async handlePresenceLeave(session: DOWebSocketSession): Promise<void> {\n this.presence.delete(session.sessionId);\n session.presence = undefined;\n await this.broadcastPresenceUpdate();\n }\n\n private async broadcastPresenceUpdate(): Promise<void> {\n const presenceList = Array.from(this.presence.values());\n\n await this.broadcastToAll({\n event: \"presence:sync\",\n channel: this.channelName,\n data: presenceList,\n });\n }\n\n // ============================================================================\n // REST API Handlers\n // ============================================================================\n\n private async handleBroadcast(request: Request): Promise<Response> {\n try {\n const body = (await request.json()) as { event: string; data: unknown };\n\n await this.broadcastToAll({\n event: body.event,\n channel: this.channelName,\n data: body.data,\n });\n\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (err) {\n return new Response(\n JSON.stringify({ success: false, error: String(err) }),\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n }\n\n private handleGetPresence(): Response {\n const presenceList = Array.from(this.presence.values());\n return new Response(JSON.stringify(presenceList), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n private handleGetInfo(): Response {\n return new Response(\n JSON.stringify({\n channel: this.channelName,\n connections: this.sessions.size,\n presence: this.presence.size,\n }),\n { headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n\n private async handleSendToUser(request: Request): Promise<Response> {\n try {\n const body = (await request.json()) as {\n userId: string;\n event: string;\n data: unknown;\n };\n\n let sent = false;\n for (const session of this.sessions.values()) {\n if (session.userId === body.userId && session.state === \"open\") {\n session.webSocket.send(\n JSON.stringify(\n createMessage({\n event: body.event,\n channel: this.channelName,\n data: body.data,\n })\n )\n );\n sent = true;\n }\n }\n\n return new Response(JSON.stringify({ success: true, sent }), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (err) {\n return new Response(\n JSON.stringify({ success: false, error: String(err) }),\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n }\n\n // ============================================================================\n // Helpers\n // ============================================================================\n\n private getSessionByWebSocket(ws: WebSocket): DOWebSocketSession | undefined {\n for (const session of this.sessions.values()) {\n if (session.webSocket === ws) {\n return session;\n }\n }\n return undefined;\n }\n\n private async broadcastToAll(\n messageData: {\n event: string;\n channel: string;\n data: unknown;\n senderId?: string;\n },\n excludeSessionId?: string\n ): Promise<void> {\n const message = createMessage(messageData);\n const payload = JSON.stringify(message);\n\n for (const session of this.sessions.values()) {\n if (session.sessionId === excludeSessionId) continue;\n if (session.state !== \"open\") continue;\n\n try {\n session.webSocket.send(payload);\n } catch {\n // Connection might be closed\n session.state = \"closed\";\n }\n }\n }\n}\n\n// ============================================================================\n// Durable Objects Adapter (Client-side)\n// ============================================================================\n\n/**\n * Durable Objects Adapter\n * Uses RealtimeChannelDO for channel management\n */\nexport class DurableObjectsAdapter implements RealtimeAdapter {\n readonly type = \"durable-objects\" as const;\n\n private namespace: DurableObjectNamespace;\n private channelPrefix: string;\n private localHandlers: Map<string, Map<string, MessageHandler>> = new Map();\n\n constructor(options: DurableObjectsAdapterOptions) {\n this.namespace = options.namespace;\n this.channelPrefix = options.channelPrefix ?? \"channel:\";\n }\n\n /**\n * Get Durable Object stub for a channel\n */\n getChannelStub(channel: string): DurableObjectStub {\n const id = this.namespace.idFromName(`${this.channelPrefix}${channel}`);\n return this.namespace.get(id);\n }\n\n /**\n * Get WebSocket URL for a channel\n */\n getWebSocketUrl(\n channel: string,\n sessionId: string,\n userId?: string,\n baseUrl?: string\n ): string {\n const base = baseUrl || \"wss://your-worker.your-subdomain.workers.dev\";\n const params = new URLSearchParams({ sessionId });\n if (userId) params.set(\"userId\", userId);\n return `${base}/realtime/${channel}?${params}`;\n }\n\n // ============================================================================\n // RealtimeAdapter Implementation\n // ============================================================================\n\n async subscribe(\n channel: string,\n sessionId: string,\n handler: MessageHandler\n ): Promise<void> {\n // Store handler locally for callback\n if (!this.localHandlers.has(channel)) {\n this.localHandlers.set(channel, new Map());\n }\n this.localHandlers.get(channel)!.set(sessionId, handler);\n\n // Note: Actual WebSocket subscription happens via WebSocket connection\n // This is used for server-side handler registration\n }\n\n async unsubscribe(channel: string, sessionId: string): Promise<void> {\n const handlers = this.localHandlers.get(channel);\n if (handlers) {\n handlers.delete(sessionId);\n if (handlers.size === 0) {\n this.localHandlers.delete(channel);\n }\n }\n }\n\n async publish<T = unknown>(\n channel: string,\n message: RealtimeMessage<T>\n ): Promise<void> {\n const stub = this.getChannelStub(channel);\n\n await stub.fetch(new Request(\"https://do/broadcast\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n event: message.event,\n data: message.data,\n }),\n }));\n\n // Call local handlers\n const handlers = this.localHandlers.get(channel);\n if (handlers) {\n for (const handler of handlers.values()) {\n try {\n await handler(message);\n } catch {\n // Ignore handler errors\n }\n }\n }\n }\n\n async sendToSession<T = unknown>(\n _sessionId: string,\n _message: RealtimeMessage<T>\n ): Promise<boolean> {\n // For DO adapter, we send via the REST API to a specific user\n // This requires knowing which channel the session is in\n // For simplicity, we broadcast with target metadata\n return false;\n }\n\n async getSubscribers(channel: string): Promise<string[]> {\n const handlers = this.localHandlers.get(channel);\n return handlers ? Array.from(handlers.keys()) : [];\n }\n\n async setPresence<T = unknown>(\n _channel: string,\n _sessionId: string,\n _userId: string,\n _data: T\n ): Promise<void> {\n // Presence is managed via WebSocket messages in DO\n // This is a no-op for the adapter\n }\n\n async removePresence(_channel: string, _sessionId: string): Promise<void> {\n // Presence is managed via WebSocket messages in DO\n }\n\n async getPresence<T = unknown>(channel: string): Promise<PresenceUser<T>[]> {\n const stub = this.getChannelStub(channel);\n const response = await stub.fetch(new Request(\"https://do/presence\"));\n return response.json();\n }\n\n async close(): Promise<void> {\n this.localHandlers.clear();\n }\n\n /**\n * Get channel info from Durable Object\n */\n async getChannelInfo(\n channel: string\n ): Promise<{ connections: number; presence: number }> {\n const stub = this.getChannelStub(channel);\n const response = await stub.fetch(new Request(\"https://do/info\"));\n return response.json();\n }\n\n /**\n * Send message to specific user in a channel\n */\n async sendToUser<T = unknown>(\n channel: string,\n userId: string,\n event: string,\n data: T\n ): Promise<boolean> {\n const stub = this.getChannelStub(channel);\n\n const response = await stub.fetch(\n new Request(\"https://do/send\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ userId, event, data }),\n })\n );\n\n const result = (await response.json()) as { sent: boolean };\n return result.sent;\n }\n}\n\n/**\n * Create Durable Objects adapter\n */\nexport function createDurableObjectsAdapter(\n options: DurableObjectsAdapterOptions\n): DurableObjectsAdapter {\n return new DurableObjectsAdapter(options);\n}\n","/**\n * @parsrun/realtime - Hono Integration\n * Middleware and routes for Hono framework\n */\n\nimport { Hono } from \"hono\";\nimport type { Context, MiddlewareHandler } from \"hono\";\nimport { SSEAdapter } from \"./adapters/sse.js\";\nimport type {\n RealtimeAdapter,\n SSEAdapterOptions,\n} from \"./types.js\";\nimport { createMessage } from \"./types.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Hono realtime context variables\n */\nexport interface RealtimeVariables {\n realtime: {\n adapter: RealtimeAdapter;\n sessionId: string;\n userId: string | undefined;\n };\n}\n\n/**\n * SSE route options\n */\nexport interface SSERouteOptions {\n /** Get session ID from context (default: query param or random) */\n getSessionId?: (c: Context) => string;\n /** Get user ID from context (default: from auth) */\n getUserId?: (c: Context) => string | undefined;\n /** Channels the user can subscribe to */\n getChannels?: (c: Context) => string[];\n /** Called when connection opens */\n onConnect?: (c: Context, sessionId: string) => void | Promise<void>;\n /** Called when connection closes */\n onDisconnect?: (c: Context, sessionId: string) => void | Promise<void>;\n}\n\n/**\n * Durable Objects route options\n */\nexport interface DORouteOptions {\n /** Durable Object namespace binding name */\n namespaceBinding: string;\n /** Channel prefix */\n channelPrefix?: string;\n /** Authorize connection */\n authorize?: (c: Context, channel: string) => boolean | Promise<boolean>;\n}\n\n// ============================================================================\n// SSE Middleware and Routes\n// ============================================================================\n\n/**\n * Create SSE realtime middleware\n * Adds SSE adapter to context\n */\nexport function sseMiddleware(\n options?: SSEAdapterOptions\n): MiddlewareHandler<{ Variables: RealtimeVariables }> {\n const adapter = new SSEAdapter(options);\n\n return async (c, next) => {\n const sessionId = c.req.query(\"sessionId\") || crypto.randomUUID();\n // Get userId from context if set by auth middleware (type-safe access)\n const vars = c.var as Record<string, unknown>;\n const userId = typeof vars[\"userId\"] === \"string\" ? vars[\"userId\"] : undefined;\n\n c.set(\"realtime\", {\n adapter,\n sessionId,\n userId,\n });\n\n await next();\n };\n}\n\n/**\n * Create SSE subscription endpoint\n * Client connects to this endpoint to receive events\n */\nexport function createSSEHandler(\n adapter: SSEAdapter,\n options: SSERouteOptions = {}\n): (c: Context) => Response | Promise<Response> {\n return async (c: Context) => {\n const sessionId = options.getSessionId?.(c) ||\n c.req.query(\"sessionId\") ||\n crypto.randomUUID();\n\n const vars = c.var as Record<string, unknown>;\n const userId = options.getUserId?.(c) ||\n (typeof vars[\"userId\"] === \"string\" ? vars[\"userId\"] : undefined);\n\n const channels = options.getChannels?.(c) ||\n c.req.query(\"channels\")?.split(\",\") ||\n [];\n\n // Create SSE connection\n const { response } = adapter.createConnection(sessionId, userId);\n\n // Subscribe to requested channels\n for (const channel of channels) {\n await adapter.subscribe(channel, sessionId, () => {\n // Handler is called but SSE sends via connection.writer\n });\n }\n\n // Call onConnect callback\n if (options.onConnect) {\n await options.onConnect(c, sessionId);\n }\n\n // Handle connection close\n c.req.raw.signal.addEventListener(\"abort\", async () => {\n await adapter.closeConnection(sessionId);\n if (options.onDisconnect) {\n await options.onDisconnect(c, sessionId);\n }\n });\n\n return response;\n };\n}\n\n/**\n * Create complete SSE routes for Hono\n */\nexport function createSSERoutes(\n adapter: SSEAdapter,\n options: SSERouteOptions = {}\n): Hono {\n const app = new Hono();\n\n // SSE subscription endpoint\n app.get(\"/subscribe\", createSSEHandler(adapter, options));\n\n // Subscribe to additional channel\n app.post(\"/subscribe/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const sessionId = c.req.query(\"sessionId\");\n\n if (!sessionId) {\n return c.json({ error: \"sessionId required\" }, 400);\n }\n\n const connection = adapter.getConnection(sessionId);\n if (!connection) {\n return c.json({ error: \"Connection not found\" }, 404);\n }\n\n await adapter.subscribe(channel, sessionId, () => {});\n\n return c.json({ success: true, channel });\n });\n\n // Unsubscribe from channel\n app.post(\"/unsubscribe/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const sessionId = c.req.query(\"sessionId\");\n\n if (!sessionId) {\n return c.json({ error: \"sessionId required\" }, 400);\n }\n\n await adapter.unsubscribe(channel, sessionId);\n\n return c.json({ success: true, channel });\n });\n\n // Broadcast to channel\n app.post(\"/broadcast/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const body = await c.req.json<{ event: string; data: unknown }>();\n\n const message = createMessage({\n event: body.event,\n channel,\n data: body.data,\n });\n\n await adapter.publish(channel, message);\n\n return c.json({ success: true });\n });\n\n // Get channel presence\n app.get(\"/presence/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const presence = await adapter.getPresence(channel);\n return c.json(presence);\n });\n\n // Set presence\n app.post(\"/presence/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const sessionId = c.req.query(\"sessionId\");\n const userId = c.req.query(\"userId\") || sessionId;\n const data = await c.req.json();\n\n if (!sessionId) {\n return c.json({ error: \"sessionId required\" }, 400);\n }\n\n await adapter.setPresence(channel, sessionId, userId!, data);\n\n return c.json({ success: true });\n });\n\n // Remove presence\n app.delete(\"/presence/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const sessionId = c.req.query(\"sessionId\");\n\n if (!sessionId) {\n return c.json({ error: \"sessionId required\" }, 400);\n }\n\n await adapter.removePresence(channel, sessionId);\n\n return c.json({ success: true });\n });\n\n // Get stats\n app.get(\"/stats\", (c) => {\n return c.json(adapter.getStats());\n });\n\n return app;\n}\n\n// ============================================================================\n// Durable Objects Routes\n// ============================================================================\n\n/**\n * Create Durable Objects realtime routes for Hono\n * Proxies requests to the appropriate Durable Object\n */\nexport function createDORoutes(options: DORouteOptions): Hono {\n const app = new Hono();\n const prefix = options.channelPrefix ?? \"channel:\";\n\n // WebSocket upgrade handler\n app.get(\"/ws/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n\n // Authorization check\n if (options.authorize) {\n const authorized = await options.authorize(c, channel);\n if (!authorized) {\n return c.json({ error: \"Unauthorized\" }, 403);\n }\n }\n\n // Get Durable Object namespace from env\n const env = c.env as Record<string, unknown>;\n const namespace = env[options.namespaceBinding] as DurableObjectNamespace;\n\n if (!namespace) {\n return c.json(\n { error: `Namespace ${options.namespaceBinding} not found` },\n 500\n );\n }\n\n // Get Durable Object stub\n const id = namespace.idFromName(`${prefix}${channel}`);\n const stub = namespace.get(id);\n\n // Forward the request to the Durable Object\n const url = new URL(c.req.url);\n url.pathname = `/ws/${channel}`;\n\n return stub.fetch(new Request(url.toString(), c.req.raw));\n });\n\n // Broadcast to channel\n app.post(\"/broadcast/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n\n const env = c.env as Record<string, unknown>;\n const namespace = env[options.namespaceBinding] as DurableObjectNamespace;\n\n if (!namespace) {\n return c.json(\n { error: `Namespace ${options.namespaceBinding} not found` },\n 500\n );\n }\n\n const id = namespace.idFromName(`${prefix}${channel}`);\n const stub = namespace.get(id);\n\n const body = await c.req.json();\n\n const response = await stub.fetch(\n new Request(\"https://do/broadcast\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n })\n );\n\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n });\n\n // Get channel presence\n app.get(\"/presence/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n\n const env = c.env as Record<string, unknown>;\n const namespace = env[options.namespaceBinding] as DurableObjectNamespace;\n\n if (!namespace) {\n return c.json(\n { error: `Namespace ${options.namespaceBinding} not found` },\n 500\n );\n }\n\n const id = namespace.idFromName(`${prefix}${channel}`);\n const stub = namespace.get(id);\n\n const response = await stub.fetch(new Request(\"https://do/presence\"));\n\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n });\n\n // Get channel info\n app.get(\"/info/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n\n const env = c.env as Record<string, unknown>;\n const namespace = env[options.namespaceBinding] as DurableObjectNamespace;\n\n if (!namespace) {\n return c.json(\n { error: `Namespace ${options.namespaceBinding} not found` },\n 500\n );\n }\n\n const id = namespace.idFromName(`${prefix}${channel}`);\n const stub = namespace.get(id);\n\n const response = await stub.fetch(new Request(\"https://do/info\"));\n\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n });\n\n return app;\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Broadcast helper for use in route handlers\n */\nexport async function broadcast(\n adapter: RealtimeAdapter,\n channel: string,\n event: string,\n data: unknown\n): Promise<void> {\n const message = createMessage({ event, channel, data });\n await adapter.publish(channel, message);\n}\n\n/**\n * Send to user helper\n */\nexport async function sendToUser(\n adapter: RealtimeAdapter,\n channel: string,\n userId: string,\n event: string,\n data: unknown\n): Promise<void> {\n const presence = await adapter.getPresence(channel);\n const userSessions = presence.filter((p) => p.userId === userId);\n\n const message = createMessage({ event, channel, data });\n\n for (const session of userSessions) {\n await adapter.sendToSession(session.sessionId, message);\n }\n}\n","/**\n * @parsrun/realtime - Channel Implementation\n * Channel abstraction for realtime communication\n */\n\nimport type {\n Channel,\n ChannelInfo,\n MessageHandler,\n PresenceEvent,\n PresenceUser,\n RealtimeAdapter,\n} from \"../types.js\";\nimport { createMessage } from \"../types.js\";\n\n/**\n * Channel implementation using an adapter\n */\nexport class ChannelImpl implements Channel {\n private presenceHandlers: Set<(event: PresenceEvent) => void> = new Set();\n\n constructor(\n public readonly name: string,\n private adapter: RealtimeAdapter,\n private sessionId: string\n ) {}\n\n async broadcast<T = unknown>(event: string, data: T): Promise<void> {\n const message = createMessage({\n event,\n channel: this.name,\n data,\n });\n\n await this.adapter.publish(this.name, message);\n }\n\n async send<T = unknown>(\n userId: string,\n event: string,\n data: T\n ): Promise<void> {\n const message = createMessage({\n event,\n channel: this.name,\n data,\n metadata: { targetUserId: userId },\n });\n\n // Find session for user and send\n const presence = await this.adapter.getPresence(this.name);\n\n for (const user of presence) {\n if (user.userId === userId) {\n await this.adapter.sendToSession(user.sessionId, message);\n }\n }\n }\n\n async getPresence<T = unknown>(): Promise<PresenceUser<T>[]> {\n return this.adapter.getPresence<T>(this.name);\n }\n\n subscribe<T = unknown>(handler: MessageHandler<T>): () => void {\n // Subscribe this session to the channel\n this.adapter.subscribe(this.name, this.sessionId, handler as MessageHandler);\n\n return () => {\n this.adapter.unsubscribe(this.name, this.sessionId);\n };\n }\n\n onPresence<T = unknown>(\n handler: (event: PresenceEvent<T>) => void\n ): () => void {\n this.presenceHandlers.add(handler as (event: PresenceEvent) => void);\n\n return () => {\n this.presenceHandlers.delete(handler as (event: PresenceEvent) => void);\n };\n }\n\n /**\n * Emit presence event to handlers\n */\n emitPresence<T = unknown>(event: PresenceEvent<T>): void {\n for (const handler of this.presenceHandlers) {\n try {\n handler(event as PresenceEvent);\n } catch {\n // Ignore handler errors\n }\n }\n }\n\n async getInfo(): Promise<ChannelInfo> {\n const subscribers = await this.adapter.getSubscribers(this.name);\n const presence = await this.adapter.getPresence(this.name);\n\n return {\n name: this.name,\n subscriberCount: subscribers.length,\n presence,\n };\n }\n}\n\n/**\n * Create a channel instance\n */\nexport function createChannel(\n name: string,\n adapter: RealtimeAdapter,\n sessionId: string\n): Channel {\n return new ChannelImpl(name, adapter, sessionId);\n}\n","/**\n * @parsrun/realtime\n * Edge-compatible realtime for Pars\n *\n * Supports multiple adapters:\n * - SSE (Server-Sent Events) - Works on all runtimes\n * - Durable Objects - Cloudflare Workers with WebSocket\n *\n * @example SSE Usage\n * ```typescript\n * import { createSSEAdapter, createSSERoutes } from '@parsrun/realtime';\n * import { Hono } from 'hono';\n *\n * const app = new Hono();\n * const sse = createSSEAdapter({ pingInterval: 30000 });\n *\n * // Mount realtime routes\n * app.route('/realtime', createSSERoutes(sse));\n *\n * // Broadcast from anywhere\n * await sse.publish('orders', createMessage({\n * event: 'order:created',\n * channel: 'orders',\n * data: { orderId: '123' }\n * }));\n * ```\n *\n * @example Durable Objects Usage\n * ```typescript\n * import { RealtimeChannelDO, createDORoutes } from '@parsrun/realtime';\n * import { Hono } from 'hono';\n *\n * const app = new Hono();\n *\n * // Mount DO routes\n * app.route('/realtime', createDORoutes({\n * namespaceBinding: 'REALTIME_CHANNELS'\n * }));\n *\n * // Export DO class for wrangler\n * export { RealtimeChannelDO };\n * ```\n *\n * @example Client-side SSE\n * ```typescript\n * const eventSource = new EventSource('/realtime/subscribe?channels=orders');\n *\n * eventSource.addEventListener('order:created', (e) => {\n * const data = JSON.parse(e.data);\n * console.log('New order:', data);\n * });\n * ```\n *\n * @example Client-side WebSocket (DO)\n * ```typescript\n * const ws = new WebSocket('wss://api.example.com/realtime/ws/orders');\n *\n * ws.onmessage = (e) => {\n * const message = JSON.parse(e.data);\n * console.log('Received:', message);\n * };\n *\n * // Join presence\n * ws.send(JSON.stringify({\n * event: 'presence:join',\n * data: { name: 'John', status: 'online' }\n * }));\n * ```\n */\n\n// Re-export types\nexport * from \"./types.js\";\n\n// Re-export adapters\nexport {\n SSEAdapter,\n createSSEAdapter,\n} from \"./adapters/sse.js\";\n\nexport {\n DurableObjectsAdapter,\n RealtimeChannelDO,\n createDurableObjectsAdapter,\n} from \"./adapters/durable-objects.js\";\n\n// Import for default export\nimport { createSSEAdapter } from \"./adapters/sse.js\";\nimport { createDurableObjectsAdapter } from \"./adapters/durable-objects.js\";\n\n// Re-export Hono integration\nexport {\n sseMiddleware,\n createSSEHandler,\n createSSERoutes,\n createDORoutes,\n broadcast,\n sendToUser,\n type RealtimeVariables,\n type SSERouteOptions,\n type DORouteOptions,\n} from \"./hono.js\";\n\n// Re-export core\nexport { ChannelImpl, createChannel } from \"./core/channel.js\";\n\n// ============================================================================\n// Factory Functions\n// ============================================================================\n\nimport type { RealtimeAdapter, RealtimeConfig } from \"./types.js\";\nimport { SSEAdapter } from \"./adapters/sse.js\";\nimport { DurableObjectsAdapter } from \"./adapters/durable-objects.js\";\n\n/**\n * Create a realtime adapter based on config\n */\nexport function createRealtimeAdapter(config: RealtimeConfig): RealtimeAdapter {\n switch (config.adapter) {\n case \"sse\":\n return new SSEAdapter(config.sse);\n\n case \"durable-objects\":\n if (!config.durableObjects) {\n throw new Error(\"durableObjects config required for durable-objects adapter\");\n }\n return new DurableObjectsAdapter(config.durableObjects);\n\n case \"memory\":\n // Memory adapter uses SSE without ping\n return new SSEAdapter({ pingInterval: 0 });\n\n default:\n throw new Error(`Unknown adapter type: ${config.adapter}`);\n }\n}\n\n// Default export\nexport default {\n createRealtimeAdapter,\n createSSEAdapter,\n createDurableObjectsAdapter,\n};\n"],"mappings":";AA+XO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YACE,SACgB,MACA,OAChB;AACA,UAAM,OAAO;AAHG;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,qBAAqB;AAAA,EAChC,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,iBAAiB;AACnB;AAoBO,SAAS,cACd,SACoB;AACpB,SAAO;AAAA,IACL,IAAI,OAAO,WAAW;AAAA,IACtB,OAAO,QAAQ;AAAA,IACf,SAAS,QAAQ;AAAA,IACjB,MAAM,QAAQ;AAAA,IACd,UAAU,QAAQ;AAAA,IAClB,WAAW,KAAK,IAAI;AAAA,IACpB,UAAU,QAAQ;AAAA,EACpB;AACF;AAKO,SAAS,cAAc,aAA6C;AACzE,MAAI;AACF,UAAM,QAAQ,YAAY,MAAM,IAAI;AACpC,QAAI,QAAQ;AACZ,QAAI,OAAO;AACX,QAAI,KAAK;AAET,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,WAAW,QAAQ,GAAG;AAC7B,gBAAQ,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,MAC7B,WAAW,KAAK,WAAW,OAAO,GAAG;AACnC,eAAO,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,MAC5B,WAAW,KAAK,WAAW,KAAK,GAAG;AACjC,aAAK,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,MAC1B;AAAA,IACF;AAEA,QAAI,CAAC,KAAM,QAAO;AAElB,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,WAAO;AAAA,MACL,IAAI,MAAM,OAAO,MAAM,OAAO,WAAW;AAAA,MACzC;AAAA,MACA,SAAS,OAAO,WAAW;AAAA,MAC3B,MAAM,OAAO,QAAQ;AAAA,MACrB,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO,aAAa,KAAK,IAAI;AAAA,MACxC,UAAU,OAAO;AAAA,IACnB;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,eAAe,SAAkC;AAC/D,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,MAAM,QAAQ,EAAE,EAAE;AAC7B,QAAM,KAAK,SAAS,QAAQ,KAAK,EAAE;AACnC,QAAM,KAAK,QAAQ,KAAK,UAAU,OAAO,CAAC,EAAE;AAC5C,QAAM,KAAK,EAAE;AAEb,SAAO,MAAM,KAAK,IAAI,IAAI;AAC5B;;;AC5cA,IAAM,kBAAsC;AAAA,EAC1C,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,0BAA0B;AAAA,EAC1B,YAAY;AACd;AAMO,IAAM,aAAN,MAA4C;AAAA,EACxC,OAAO;AAAA,EAER;AAAA,EACA,cAA0C,oBAAI,IAAI;AAAA,EAClD,qBAA+D,oBAAI,IAAI;AAAA,EACvE,WAAmD,oBAAI,IAAI;AAAA,EAC3D,iBAAwD;AAAA,EAEhE,YAAY,UAA6B,CAAC,GAAG;AAC3C,SAAK,UAAU;AAAA,MACb,cAAc,QAAQ,gBAAgB,gBAAgB;AAAA,MACtD,mBAAmB,QAAQ,qBAAqB,gBAAgB;AAAA,MAChE,0BAA0B,QAAQ,4BAA4B,gBAAgB;AAAA,MAC9E,YAAY,QAAQ,cAAc,gBAAgB;AAAA,IACpD;AAGA,QAAI,KAAK,QAAQ,eAAe,GAAG;AACjC,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,iBACE,WACA,QACmD;AACnD,UAAM,EAAE,UAAU,SAAS,IAAI,IAAI,gBAA4B;AAC/D,UAAM,SAAS,SAAS,UAAU;AAElC,UAAM,aAA4B;AAAA,MAChC;AAAA,MACA;AAAA,MACA,UAAU,oBAAI,IAAI;AAAA,MAClB;AAAA,MACA,OAAO;AAAA,MACP,WAAW,KAAK,IAAI;AAAA,MACpB,YAAY,KAAK,IAAI;AAAA,IACvB;AAEA,SAAK,YAAY,IAAI,WAAW,UAAU;AAG1C,UAAM,UAAU,IAAI,YAAY;AAChC,WAAO,MAAM,QAAQ,OAAO,SAAS,KAAK,QAAQ,UAAU;AAAA;AAAA,CAAM,CAAC;AAEnE,UAAM,WAAW,IAAI,SAAS,UAAU;AAAA,MACtC,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,qBAAqB;AAAA;AAAA,MACvB;AAAA,IACF,CAAC;AAED,WAAO,EAAE,UAAU,WAAW;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,WAAkC;AACtD,UAAM,aAAa,KAAK,YAAY,IAAI,SAAS;AACjD,QAAI,CAAC,WAAY;AAEjB,eAAW,QAAQ;AAGnB,eAAW,WAAW,WAAW,UAAU;AACzC,YAAM,KAAK,YAAY,SAAS,SAAS;AACzC,YAAM,KAAK,eAAe,SAAS,SAAS;AAAA,IAC9C;AAGA,QAAI;AACF,YAAM,WAAW,OAAO,MAAM;AAAA,IAChC,QAAQ;AAAA,IAER;AAEA,SAAK,YAAY,OAAO,SAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,WAA8C;AAC1D,WAAO,KAAK,YAAY,IAAI,SAAS;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,iBAA6C;AAC3C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UACJ,SACA,WACA,SACe;AACf,UAAM,aAAa,KAAK,YAAY,IAAI,SAAS;AACjD,QAAI,CAAC,YAAY;AACf,YAAM,IAAI;AAAA,QACR;AAAA,QACA,mBAAmB;AAAA,MACrB;AAAA,IACF;AAGA,UAAM,cAAc,KAAK,mBAAmB,IAAI,OAAO;AACvD,QACE,eACA,YAAY,QAAQ,KAAK,QAAQ,0BACjC;AACA,YAAM,IAAI;AAAA,QACR;AAAA,QACA,mBAAmB;AAAA,MACrB;AAAA,IACF;AAGA,QAAI,CAAC,KAAK,mBAAmB,IAAI,OAAO,GAAG;AACzC,WAAK,mBAAmB,IAAI,SAAS,oBAAI,IAAI,CAAC;AAAA,IAChD;AACA,SAAK,mBAAmB,IAAI,OAAO,EAAG,IAAI,WAAW,OAAO;AAG5D,eAAW,SAAS,IAAI,OAAO;AAG/B,UAAM,KAAK,iBAAiB,YAAY;AAAA,MACtC,IAAI,OAAO,WAAW;AAAA,MACtB,OAAO;AAAA,MACP;AAAA,MACA,MAAM,EAAE,QAAQ;AAAA,MAChB,WAAW,KAAK,IAAI;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY,SAAiB,WAAkC;AACnE,UAAM,cAAc,KAAK,mBAAmB,IAAI,OAAO;AACvD,QAAI,aAAa;AACf,kBAAY,OAAO,SAAS;AAC5B,UAAI,YAAY,SAAS,GAAG;AAC1B,aAAK,mBAAmB,OAAO,OAAO;AAAA,MACxC;AAAA,IACF;AAEA,UAAM,aAAa,KAAK,YAAY,IAAI,SAAS;AACjD,QAAI,YAAY;AACd,iBAAW,SAAS,OAAO,OAAO;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,MAAM,QACJ,SACA,SACe;AACf,UAAM,cAAc,KAAK,mBAAmB,IAAI,OAAO;AACvD,QAAI,CAAC,YAAa;AAElB,UAAM,WAA4B,CAAC;AAEnC,eAAW,CAAC,WAAW,OAAO,KAAK,aAAa;AAC9C,YAAM,aAAa,KAAK,YAAY,IAAI,SAAS;AACjD,UAAI,cAAc,WAAW,UAAU,QAAQ;AAE7C,iBAAS,KAAK,KAAK,iBAAiB,YAAY,OAAO,CAAC;AAGxD,YAAI;AACF,gBAAM,SAAS,QAAQ,OAAO;AAC9B,cAAI,kBAAkB,SAAS;AAC7B,qBAAS,KAAK,OAAO,KAAK,MAAM;AAAA,YAAC,CAAC,CAAC;AAAA,UACrC;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,WAAW,QAAQ;AAAA,EACnC;AAAA,EAEA,MAAM,cACJ,WACA,SACkB;AAClB,UAAM,aAAa,KAAK,YAAY,IAAI,SAAS;AACjD,QAAI,CAAC,cAAc,WAAW,UAAU,QAAQ;AAC9C,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,KAAK,iBAAiB,YAAY,OAAO;AAC/C,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,SAAoC;AACvD,UAAM,cAAc,KAAK,mBAAmB,IAAI,OAAO;AACvD,WAAO,cAAc,MAAM,KAAK,YAAY,KAAK,CAAC,IAAI,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,YACJ,SACA,WACA,QACA,MACe;AACf,QAAI,CAAC,KAAK,SAAS,IAAI,OAAO,GAAG;AAC/B,WAAK,SAAS,IAAI,SAAS,oBAAI,IAAI,CAAC;AAAA,IACtC;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WAAW,KAAK,SAAS,IAAI,OAAO,EAAG,IAAI,SAAS;AAE1D,UAAM,OAAwB;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,UAAU,YAAY;AAAA,MAChC,YAAY;AAAA,IACd;AAEA,SAAK,SAAS,IAAI,OAAO,EAAG,IAAI,WAAW,IAAoB;AAG/D,UAAM,YAAY,WAAW,oBAAoB;AACjD,UAAM,KAAK,QAAQ,SAAS;AAAA,MAC1B,IAAI,OAAO,WAAW;AAAA,MACtB,OAAO;AAAA,MACP;AAAA,MACA,MAAM;AAAA,QACJ,MAAM,WAAW,WAAW;AAAA,QAC5B;AAAA,QACA,UAAU,MAAM,KAAK,YAAY,OAAO;AAAA,MAC1C;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,eAAe,SAAiB,WAAkC;AACtE,UAAM,kBAAkB,KAAK,SAAS,IAAI,OAAO;AACjD,QAAI,CAAC,gBAAiB;AAEtB,UAAM,OAAO,gBAAgB,IAAI,SAAS;AAC1C,QAAI,CAAC,KAAM;AAEX,oBAAgB,OAAO,SAAS;AAChC,QAAI,gBAAgB,SAAS,GAAG;AAC9B,WAAK,SAAS,OAAO,OAAO;AAAA,IAC9B;AAGA,UAAM,KAAK,QAAQ,SAAS;AAAA,MAC1B,IAAI,OAAO,WAAW;AAAA,MACtB,OAAO;AAAA,MACP;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN;AAAA,QACA,UAAU,MAAM,KAAK,YAAY,OAAO;AAAA,MAC1C;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAyB,SAA6C;AAC1E,UAAM,kBAAkB,KAAK,SAAS,IAAI,OAAO;AACjD,QAAI,CAAC,gBAAiB,QAAO,CAAC;AAC9B,WAAO,MAAM,KAAK,gBAAgB,OAAO,CAAC;AAAA,EAC5C;AAAA,EAEA,MAAM,QAAuB;AAE3B,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAGA,UAAM,gBAAgB,MAAM,KAAK,KAAK,YAAY,KAAK,CAAC,EAAE;AAAA,MAAI,CAAC,cAC7D,KAAK,gBAAgB,SAAS;AAAA,IAChC;AACA,UAAM,QAAQ,WAAW,aAAa;AAGtC,SAAK,YAAY,MAAM;AACvB,SAAK,mBAAmB,MAAM;AAC9B,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBACZ,YACA,SACe;AACf,QAAI,WAAW,UAAU,OAAQ;AAEjC,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,WAAW,eAAe,OAA0B;AAE1D,QAAI;AACF,YAAM,WAAW,OAAO,MAAM,QAAQ,OAAO,QAAQ,CAAC;AAAA,IACxD,SAAS,KAAK;AAEZ,iBAAW,QAAQ;AACnB,YAAM,KAAK,gBAAgB,WAAW,SAAS;AAAA,IACjD;AAAA,EACF;AAAA,EAEQ,oBAA0B;AAChC,SAAK,iBAAiB,YAAY,MAAM;AACtC,WAAK,cAAc;AAAA,IACrB,GAAG,KAAK,QAAQ,YAAY;AAAA,EAC9B;AAAA,EAEA,MAAc,gBAA+B;AAC3C,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,YAAY;AAAA,OAAoB,GAAG;AAAA;AAAA;AAEzC,eAAW,CAAC,WAAW,UAAU,KAAK,KAAK,aAAa;AACtD,UAAI,WAAW,UAAU,OAAQ;AAGjC,UACE,KAAK,QAAQ,oBAAoB,KACjC,MAAM,WAAW,aAAa,KAAK,QAAQ,mBAC3C;AACA,cAAM,KAAK,gBAAgB,SAAS;AACpC;AAAA,MACF;AAEA,UAAI;AACF,cAAM,WAAW,OAAO,MAAM,QAAQ,OAAO,SAAS,CAAC;AACvD,mBAAW,aAAa;AAAA,MAC1B,QAAQ;AAEN,cAAM,KAAK,gBAAgB,SAAS;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,WAAyB;AACtC,UAAM,aAAa,KAAK,YAAY,IAAI,SAAS;AACjD,QAAI,YAAY;AACd,iBAAW,aAAa,KAAK,IAAI;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAIE;AACA,UAAM,uBAA+C,CAAC;AAEtD,eAAW,CAAC,SAAS,WAAW,KAAK,KAAK,oBAAoB;AAC5D,2BAAqB,OAAO,IAAI,YAAY;AAAA,IAC9C;AAEA,WAAO;AAAA,MACL,kBAAkB,KAAK,YAAY;AAAA,MACnC,eAAe,KAAK,mBAAmB;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,iBAAiB,SAAyC;AACxE,SAAO,IAAI,WAAW,OAAO;AAC/B;;;AClZO,IAAM,oBAAN,MAAiD;AAAA,EAC9C,WAA4C,oBAAI,IAAI;AAAA,EACpD,WAAsC,oBAAI,IAAI;AAAA,EAC9C,cAAsB;AAAA,EACtB;AAAA,EAER,YACE,OACA,MACA;AACA,SAAK,QAAQ;AAEb,SAAK,MAAM,cAAc,EAAE,QAAQ,CAAC,OAAO;AACzC,YAAM,OAAO,GAAG,sBAAsB;AAItC,UAAI,MAAM;AACR,aAAK,SAAS,IAAI,KAAK,WAAW;AAAA,UAChC,WAAW,KAAK;AAAA,UAChB,QAAQ,KAAK;AAAA,UACb,WAAW;AAAA,UACX,OAAO;AAAA,UACP,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAM,SAAqC;AAC/C,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAG/B,UAAM,YAAY,IAAI,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AACxD,UAAM,WAAW,UAAU,UAAU,SAAS,CAAC;AAC/C,QAAI,UAAU;AACZ,WAAK,cAAc;AAAA,IACrB;AAGA,QAAI,QAAQ,QAAQ,IAAI,SAAS,MAAM,aAAa;AAClD,aAAO,KAAK,uBAAuB,OAAO;AAAA,IAC5C;AAGA,YAAQ,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI,GAAG;AAAA,MACrC,KAAK;AACH,eAAO,KAAK,gBAAgB,OAAO;AAAA,MACrC,KAAK;AACH,eAAO,KAAK,kBAAkB;AAAA,MAChC,KAAK;AACH,eAAO,KAAK,cAAc;AAAA,MAC5B,KAAK;AACH,eAAO,KAAK,iBAAiB,OAAO;AAAA,MACtC;AACE,eAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,uBAAuB,SAA4B;AACzD,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,UAAM,YAAY,IAAI,aAAa,IAAI,WAAW,KAAK,OAAO,WAAW;AACzE,UAAM,SAAS,IAAI,aAAa,IAAI,QAAQ,KAAK;AAEjD,UAAM,OAAO,IAAI,cAAc;AAC/B,UAAM,SAAS,KAAK,CAAC;AACrB,UAAM,SAAS,KAAK,CAAC;AAGrB,WAAO,oBAAoB,EAAE,WAAW,OAAO,CAAC;AAEhD,SAAK,MAAM,gBAAgB,MAAM;AAEjC,UAAM,UAA8B;AAAA,MAClC;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,OAAO;AAAA,MACP,WAAW,KAAK,IAAI;AAAA,IACtB;AAEA,SAAK,SAAS,IAAI,WAAW,OAAO;AAGpC,WAAO;AAAA,MACL,KAAK;AAAA,QACH,cAAc;AAAA,UACZ,OAAO;AAAA,UACP,SAAS,KAAK;AAAA,UACd,MAAM,EAAE,WAAW,OAAO;AAAA,QAC5B,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,WAAW,OAAO,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAM,iBACJ,IACA,SACe;AACf,UAAM,UAAU,KAAK,sBAAsB,EAAE;AAC7C,QAAI,CAAC,QAAS;AAEd,QAAI;AACF,YAAM,OACJ,OAAO,YAAY,WACf,UACA,IAAI,YAAY,EAAE,OAAO,OAAO;AAEtC,YAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,YAAM,KAAK,cAAc,SAAS,MAAM;AAAA,IAC1C,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,eACJ,IACA,MACA,QACA,WACe;AACf,UAAM,UAAU,KAAK,sBAAsB,EAAE;AAC7C,QAAI,CAAC,QAAS;AAGd,SAAK,SAAS,OAAO,QAAQ,SAAS;AACtC,UAAM,KAAK,wBAAwB;AAGnC,SAAK,SAAS,OAAO,QAAQ,SAAS;AAGtC,UAAM,KAAK,eAAe;AAAA,MACxB,OAAO;AAAA,MACP,SAAS,KAAK;AAAA,MACd,MAAM;AAAA,QACJ,WAAW,QAAQ;AAAA,QACnB,QAAQ,QAAQ;AAAA,QAChB;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,eAAe,IAAe,QAAgC;AAClE,UAAM,UAAU,KAAK,sBAAsB,EAAE;AAC7C,QAAI,SAAS;AACX,cAAQ,QAAQ;AAChB,WAAK,SAAS,OAAO,QAAQ,SAAS;AACtC,WAAK,SAAS,OAAO,QAAQ,SAAS;AAAA,IACxC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,cACZ,SACA,SACe;AACf,YAAQ,QAAQ,OAAO;AAAA,MACrB,KAAK;AACH,gBAAQ,UAAU;AAAA,UAChB,KAAK;AAAA,YACH,cAAc;AAAA,cACZ,OAAO;AAAA,cACP,SAAS,KAAK;AAAA,cACd,MAAM,EAAE,WAAW,KAAK,IAAI,EAAE;AAAA,YAChC,CAAC;AAAA,UACH;AAAA,QACF;AACA;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,mBAAmB,SAAS,QAAQ,IAAI;AACnD;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,qBAAqB,SAAS,QAAQ,IAAI;AACrD;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,oBAAoB,OAAO;AACtC;AAAA,MAEF,KAAK;AAEH,cAAM,KAAK;AAAA,UACT;AAAA,YACE,OAAO,QAAQ;AAAA,YACf,SAAS,KAAK;AAAA,YACd,MAAM,QAAQ;AAAA,YACd,UAAU,QAAQ;AAAA,UACpB;AAAA,UACA,QAAQ;AAAA,QACV;AACA;AAAA,MAEF;AAEE,cAAM,KAAK;AAAA,UACT;AAAA,YACE,OAAO,QAAQ;AAAA,YACf,SAAS,KAAK;AAAA,YACd,MAAM,QAAQ;AAAA,YACd,UAAU,QAAQ;AAAA,UACpB;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,IACJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBACZ,SACA,MACe;AACf,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,OAAqB;AAAA,MACzB,QAAQ,QAAQ,UAAU,QAAQ;AAAA,MAClC,WAAW,QAAQ;AAAA,MACnB;AAAA,MACA,UAAU;AAAA,MACV,YAAY;AAAA,IACd;AAEA,SAAK,SAAS,IAAI,QAAQ,WAAW,IAAI;AACzC,YAAQ,WAAW;AAEnB,UAAM,KAAK,wBAAwB;AAAA,EACrC;AAAA,EAEA,MAAc,qBACZ,SACA,MACe;AACf,UAAM,WAAW,KAAK,SAAS,IAAI,QAAQ,SAAS;AACpD,QAAI,UAAU;AACZ,eAAS,OAAO;AAChB,eAAS,aAAa,KAAK,IAAI;AAC/B,cAAQ,WAAW;AACnB,YAAM,KAAK,wBAAwB;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,SAA4C;AAC5E,SAAK,SAAS,OAAO,QAAQ,SAAS;AACtC,YAAQ,WAAW;AACnB,UAAM,KAAK,wBAAwB;AAAA,EACrC;AAAA,EAEA,MAAc,0BAAyC;AACrD,UAAM,eAAe,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAEtD,UAAM,KAAK,eAAe;AAAA,MACxB,OAAO;AAAA,MACP,SAAS,KAAK;AAAA,MACd,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,gBAAgB,SAAqC;AACjE,QAAI;AACF,YAAM,OAAQ,MAAM,QAAQ,KAAK;AAEjC,YAAM,KAAK,eAAe;AAAA,QACxB,OAAO,KAAK;AAAA,QACZ,SAAS,KAAK;AAAA,QACd,MAAM,KAAK;AAAA,MACb,CAAC;AAED,aAAO,IAAI,SAAS,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC,GAAG;AAAA,QACrD,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,SAAS,OAAO,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,QACrD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,oBAA8B;AACpC,UAAM,eAAe,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AACtD,WAAO,IAAI,SAAS,KAAK,UAAU,YAAY,GAAG;AAAA,MAChD,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAChD,CAAC;AAAA,EACH;AAAA,EAEQ,gBAA0B;AAChC,WAAO,IAAI;AAAA,MACT,KAAK,UAAU;AAAA,QACb,SAAS,KAAK;AAAA,QACd,aAAa,KAAK,SAAS;AAAA,QAC3B,UAAU,KAAK,SAAS;AAAA,MAC1B,CAAC;AAAA,MACD,EAAE,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,SAAqC;AAClE,QAAI;AACF,YAAM,OAAQ,MAAM,QAAQ,KAAK;AAMjC,UAAI,OAAO;AACX,iBAAW,WAAW,KAAK,SAAS,OAAO,GAAG;AAC5C,YAAI,QAAQ,WAAW,KAAK,UAAU,QAAQ,UAAU,QAAQ;AAC9D,kBAAQ,UAAU;AAAA,YAChB,KAAK;AAAA,cACH,cAAc;AAAA,gBACZ,OAAO,KAAK;AAAA,gBACZ,SAAS,KAAK;AAAA,gBACd,MAAM,KAAK;AAAA,cACb,CAAC;AAAA,YACH;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,aAAO,IAAI,SAAS,KAAK,UAAU,EAAE,SAAS,MAAM,KAAK,CAAC,GAAG;AAAA,QAC3D,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,SAAS,OAAO,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,QACrD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAAsB,IAA+C;AAC3E,eAAW,WAAW,KAAK,SAAS,OAAO,GAAG;AAC5C,UAAI,QAAQ,cAAc,IAAI;AAC5B,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,eACZ,aAMA,kBACe;AACf,UAAM,UAAU,cAAc,WAAW;AACzC,UAAM,UAAU,KAAK,UAAU,OAAO;AAEtC,eAAW,WAAW,KAAK,SAAS,OAAO,GAAG;AAC5C,UAAI,QAAQ,cAAc,iBAAkB;AAC5C,UAAI,QAAQ,UAAU,OAAQ;AAE9B,UAAI;AACF,gBAAQ,UAAU,KAAK,OAAO;AAAA,MAChC,QAAQ;AAEN,gBAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACF;AAUO,IAAM,wBAAN,MAAuD;AAAA,EACnD,OAAO;AAAA,EAER;AAAA,EACA;AAAA,EACA,gBAA0D,oBAAI,IAAI;AAAA,EAE1E,YAAY,SAAuC;AACjD,SAAK,YAAY,QAAQ;AACzB,SAAK,gBAAgB,QAAQ,iBAAiB;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,SAAoC;AACjD,UAAM,KAAK,KAAK,UAAU,WAAW,GAAG,KAAK,aAAa,GAAG,OAAO,EAAE;AACtE,WAAO,KAAK,UAAU,IAAI,EAAE;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,gBACE,SACA,WACA,QACA,SACQ;AACR,UAAM,OAAO,WAAW;AACxB,UAAM,SAAS,IAAI,gBAAgB,EAAE,UAAU,CAAC;AAChD,QAAI,OAAQ,QAAO,IAAI,UAAU,MAAM;AACvC,WAAO,GAAG,IAAI,aAAa,OAAO,IAAI,MAAM;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UACJ,SACA,WACA,SACe;AAEf,QAAI,CAAC,KAAK,cAAc,IAAI,OAAO,GAAG;AACpC,WAAK,cAAc,IAAI,SAAS,oBAAI,IAAI,CAAC;AAAA,IAC3C;AACA,SAAK,cAAc,IAAI,OAAO,EAAG,IAAI,WAAW,OAAO;AAAA,EAIzD;AAAA,EAEA,MAAM,YAAY,SAAiB,WAAkC;AACnE,UAAM,WAAW,KAAK,cAAc,IAAI,OAAO;AAC/C,QAAI,UAAU;AACZ,eAAS,OAAO,SAAS;AACzB,UAAI,SAAS,SAAS,GAAG;AACvB,aAAK,cAAc,OAAO,OAAO;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,QACJ,SACA,SACe;AACf,UAAM,OAAO,KAAK,eAAe,OAAO;AAExC,UAAM,KAAK,MAAM,IAAI,QAAQ,wBAAwB;AAAA,MACnD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,OAAO,QAAQ;AAAA,QACf,MAAM,QAAQ;AAAA,MAChB,CAAC;AAAA,IACH,CAAC,CAAC;AAGF,UAAM,WAAW,KAAK,cAAc,IAAI,OAAO;AAC/C,QAAI,UAAU;AACZ,iBAAW,WAAW,SAAS,OAAO,GAAG;AACvC,YAAI;AACF,gBAAM,QAAQ,OAAO;AAAA,QACvB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cACJ,YACA,UACkB;AAIlB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,eAAe,SAAoC;AACvD,UAAM,WAAW,KAAK,cAAc,IAAI,OAAO;AAC/C,WAAO,WAAW,MAAM,KAAK,SAAS,KAAK,CAAC,IAAI,CAAC;AAAA,EACnD;AAAA,EAEA,MAAM,YACJ,UACA,YACA,SACA,OACe;AAAA,EAGjB;AAAA,EAEA,MAAM,eAAe,UAAkB,YAAmC;AAAA,EAE1E;AAAA,EAEA,MAAM,YAAyB,SAA6C;AAC1E,UAAM,OAAO,KAAK,eAAe,OAAO;AACxC,UAAM,WAAW,MAAM,KAAK,MAAM,IAAI,QAAQ,qBAAqB,CAAC;AACpE,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,cAAc,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eACJ,SACoD;AACpD,UAAM,OAAO,KAAK,eAAe,OAAO;AACxC,UAAM,WAAW,MAAM,KAAK,MAAM,IAAI,QAAQ,iBAAiB,CAAC;AAChE,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,SACA,QACA,OACA,MACkB;AAClB,UAAM,OAAO,KAAK,eAAe,OAAO;AAExC,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,IAAI,QAAQ,mBAAmB;AAAA,QAC7B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,OAAO,KAAK,CAAC;AAAA,MAC9C,CAAC;AAAA,IACH;AAEA,UAAM,SAAU,MAAM,SAAS,KAAK;AACpC,WAAO,OAAO;AAAA,EAChB;AACF;AAKO,SAAS,4BACd,SACuB;AACvB,SAAO,IAAI,sBAAsB,OAAO;AAC1C;;;ACrlBA,SAAS,YAAY;AA4Dd,SAAS,cACd,SACqD;AACrD,QAAM,UAAU,IAAI,WAAW,OAAO;AAEtC,SAAO,OAAO,GAAG,SAAS;AACxB,UAAM,YAAY,EAAE,IAAI,MAAM,WAAW,KAAK,OAAO,WAAW;AAEhE,UAAM,OAAO,EAAE;AACf,UAAM,SAAS,OAAO,KAAK,QAAQ,MAAM,WAAW,KAAK,QAAQ,IAAI;AAErE,MAAE,IAAI,YAAY;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,KAAK;AAAA,EACb;AACF;AAMO,SAAS,iBACd,SACA,UAA2B,CAAC,GACkB;AAC9C,SAAO,OAAO,MAAe;AAC3B,UAAM,YAAY,QAAQ,eAAe,CAAC,KACxC,EAAE,IAAI,MAAM,WAAW,KACvB,OAAO,WAAW;AAEpB,UAAM,OAAO,EAAE;AACf,UAAM,SAAS,QAAQ,YAAY,CAAC,MACjC,OAAO,KAAK,QAAQ,MAAM,WAAW,KAAK,QAAQ,IAAI;AAEzD,UAAM,WAAW,QAAQ,cAAc,CAAC,KACtC,EAAE,IAAI,MAAM,UAAU,GAAG,MAAM,GAAG,KAClC,CAAC;AAGH,UAAM,EAAE,SAAS,IAAI,QAAQ,iBAAiB,WAAW,MAAM;AAG/D,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ,UAAU,SAAS,WAAW,MAAM;AAAA,MAElD,CAAC;AAAA,IACH;AAGA,QAAI,QAAQ,WAAW;AACrB,YAAM,QAAQ,UAAU,GAAG,SAAS;AAAA,IACtC;AAGA,MAAE,IAAI,IAAI,OAAO,iBAAiB,SAAS,YAAY;AACrD,YAAM,QAAQ,gBAAgB,SAAS;AACvC,UAAI,QAAQ,cAAc;AACxB,cAAM,QAAQ,aAAa,GAAG,SAAS;AAAA,MACzC;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AACF;AAKO,SAAS,gBACd,SACA,UAA2B,CAAC,GACtB;AACN,QAAM,MAAM,IAAI,KAAK;AAGrB,MAAI,IAAI,cAAc,iBAAiB,SAAS,OAAO,CAAC;AAGxD,MAAI,KAAK,uBAAuB,OAAO,MAAM;AAC3C,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AACrC,UAAM,YAAY,EAAE,IAAI,MAAM,WAAW;AAEzC,QAAI,CAAC,WAAW;AACd,aAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,GAAG,GAAG;AAAA,IACpD;AAEA,UAAM,aAAa,QAAQ,cAAc,SAAS;AAClD,QAAI,CAAC,YAAY;AACf,aAAO,EAAE,KAAK,EAAE,OAAO,uBAAuB,GAAG,GAAG;AAAA,IACtD;AAEA,UAAM,QAAQ,UAAU,SAAS,WAAW,MAAM;AAAA,IAAC,CAAC;AAEpD,WAAO,EAAE,KAAK,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EAC1C,CAAC;AAGD,MAAI,KAAK,yBAAyB,OAAO,MAAM;AAC7C,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AACrC,UAAM,YAAY,EAAE,IAAI,MAAM,WAAW;AAEzC,QAAI,CAAC,WAAW;AACd,aAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,GAAG,GAAG;AAAA,IACpD;AAEA,UAAM,QAAQ,YAAY,SAAS,SAAS;AAE5C,WAAO,EAAE,KAAK,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EAC1C,CAAC;AAGD,MAAI,KAAK,uBAAuB,OAAO,MAAM;AAC3C,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AACrC,UAAM,OAAO,MAAM,EAAE,IAAI,KAAuC;AAEhE,UAAM,UAAU,cAAc;AAAA,MAC5B,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,MAAM,KAAK;AAAA,IACb,CAAC;AAED,UAAM,QAAQ,QAAQ,SAAS,OAAO;AAEtC,WAAO,EAAE,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,EACjC,CAAC;AAGD,MAAI,IAAI,sBAAsB,OAAO,MAAM;AACzC,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AACrC,UAAM,WAAW,MAAM,QAAQ,YAAY,OAAO;AAClD,WAAO,EAAE,KAAK,QAAQ;AAAA,EACxB,CAAC;AAGD,MAAI,KAAK,sBAAsB,OAAO,MAAM;AAC1C,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AACrC,UAAM,YAAY,EAAE,IAAI,MAAM,WAAW;AACzC,UAAM,SAAS,EAAE,IAAI,MAAM,QAAQ,KAAK;AACxC,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK;AAE9B,QAAI,CAAC,WAAW;AACd,aAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,GAAG,GAAG;AAAA,IACpD;AAEA,UAAM,QAAQ,YAAY,SAAS,WAAW,QAAS,IAAI;AAE3D,WAAO,EAAE,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,EACjC,CAAC;AAGD,MAAI,OAAO,sBAAsB,OAAO,MAAM;AAC5C,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AACrC,UAAM,YAAY,EAAE,IAAI,MAAM,WAAW;AAEzC,QAAI,CAAC,WAAW;AACd,aAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,GAAG,GAAG;AAAA,IACpD;AAEA,UAAM,QAAQ,eAAe,SAAS,SAAS;AAE/C,WAAO,EAAE,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,EACjC,CAAC;AAGD,MAAI,IAAI,UAAU,CAAC,MAAM;AACvB,WAAO,EAAE,KAAK,QAAQ,SAAS,CAAC;AAAA,EAClC,CAAC;AAED,SAAO;AACT;AAUO,SAAS,eAAe,SAA+B;AAC5D,QAAM,MAAM,IAAI,KAAK;AACrB,QAAM,SAAS,QAAQ,iBAAiB;AAGxC,MAAI,IAAI,gBAAgB,OAAO,MAAM;AACnC,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AAGrC,QAAI,QAAQ,WAAW;AACrB,YAAM,aAAa,MAAM,QAAQ,UAAU,GAAG,OAAO;AACrD,UAAI,CAAC,YAAY;AACf,eAAO,EAAE,KAAK,EAAE,OAAO,eAAe,GAAG,GAAG;AAAA,MAC9C;AAAA,IACF;AAGA,UAAM,MAAM,EAAE;AACd,UAAM,YAAY,IAAI,QAAQ,gBAAgB;AAE9C,QAAI,CAAC,WAAW;AACd,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,aAAa,QAAQ,gBAAgB,aAAa;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAGA,UAAM,KAAK,UAAU,WAAW,GAAG,MAAM,GAAG,OAAO,EAAE;AACrD,UAAM,OAAO,UAAU,IAAI,EAAE;AAG7B,UAAM,MAAM,IAAI,IAAI,EAAE,IAAI,GAAG;AAC7B,QAAI,WAAW,OAAO,OAAO;AAE7B,WAAO,KAAK,MAAM,IAAI,QAAQ,IAAI,SAAS,GAAG,EAAE,IAAI,GAAG,CAAC;AAAA,EAC1D,CAAC;AAGD,MAAI,KAAK,uBAAuB,OAAO,MAAM;AAC3C,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AAErC,UAAM,MAAM,EAAE;AACd,UAAM,YAAY,IAAI,QAAQ,gBAAgB;AAE9C,QAAI,CAAC,WAAW;AACd,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,aAAa,QAAQ,gBAAgB,aAAa;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAEA,UAAM,KAAK,UAAU,WAAW,GAAG,MAAM,GAAG,OAAO,EAAE;AACrD,UAAM,OAAO,UAAU,IAAI,EAAE;AAE7B,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK;AAE9B,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,IAAI,QAAQ,wBAAwB;AAAA,QAClC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B,CAAC;AAAA,IACH;AAEA,WAAO,IAAI,SAAS,SAAS,MAAM;AAAA,MACjC,QAAQ,SAAS;AAAA,MACjB,SAAS,SAAS;AAAA,IACpB,CAAC;AAAA,EACH,CAAC;AAGD,MAAI,IAAI,sBAAsB,OAAO,MAAM;AACzC,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AAErC,UAAM,MAAM,EAAE;AACd,UAAM,YAAY,IAAI,QAAQ,gBAAgB;AAE9C,QAAI,CAAC,WAAW;AACd,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,aAAa,QAAQ,gBAAgB,aAAa;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAEA,UAAM,KAAK,UAAU,WAAW,GAAG,MAAM,GAAG,OAAO,EAAE;AACrD,UAAM,OAAO,UAAU,IAAI,EAAE;AAE7B,UAAM,WAAW,MAAM,KAAK,MAAM,IAAI,QAAQ,qBAAqB,CAAC;AAEpE,WAAO,IAAI,SAAS,SAAS,MAAM;AAAA,MACjC,QAAQ,SAAS;AAAA,MACjB,SAAS,SAAS;AAAA,IACpB,CAAC;AAAA,EACH,CAAC;AAGD,MAAI,IAAI,kBAAkB,OAAO,MAAM;AACrC,UAAM,UAAU,EAAE,IAAI,MAAM,SAAS;AAErC,UAAM,MAAM,EAAE;AACd,UAAM,YAAY,IAAI,QAAQ,gBAAgB;AAE9C,QAAI,CAAC,WAAW;AACd,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,aAAa,QAAQ,gBAAgB,aAAa;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAEA,UAAM,KAAK,UAAU,WAAW,GAAG,MAAM,GAAG,OAAO,EAAE;AACrD,UAAM,OAAO,UAAU,IAAI,EAAE;AAE7B,UAAM,WAAW,MAAM,KAAK,MAAM,IAAI,QAAQ,iBAAiB,CAAC;AAEhE,WAAO,IAAI,SAAS,SAAS,MAAM;AAAA,MACjC,QAAQ,SAAS;AAAA,MACjB,SAAS,SAAS;AAAA,IACpB,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;AASA,eAAsB,UACpB,SACA,SACA,OACA,MACe;AACf,QAAM,UAAU,cAAc,EAAE,OAAO,SAAS,KAAK,CAAC;AACtD,QAAM,QAAQ,QAAQ,SAAS,OAAO;AACxC;AAKA,eAAsB,WACpB,SACA,SACA,QACA,OACA,MACe;AACf,QAAM,WAAW,MAAM,QAAQ,YAAY,OAAO;AAClD,QAAM,eAAe,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM;AAE/D,QAAM,UAAU,cAAc,EAAE,OAAO,SAAS,KAAK,CAAC;AAEtD,aAAW,WAAW,cAAc;AAClC,UAAM,QAAQ,cAAc,QAAQ,WAAW,OAAO;AAAA,EACxD;AACF;;;ACrYO,IAAM,cAAN,MAAqC;AAAA,EAG1C,YACkB,MACR,SACA,WACR;AAHgB;AACR;AACA;AAAA,EACP;AAAA,EANK,mBAAwD,oBAAI,IAAI;AAAA,EAQxE,MAAM,UAAuB,OAAe,MAAwB;AAClE,UAAM,UAAU,cAAc;AAAA,MAC5B;AAAA,MACA,SAAS,KAAK;AAAA,MACd;AAAA,IACF,CAAC;AAED,UAAM,KAAK,QAAQ,QAAQ,KAAK,MAAM,OAAO;AAAA,EAC/C;AAAA,EAEA,MAAM,KACJ,QACA,OACA,MACe;AACf,UAAM,UAAU,cAAc;AAAA,MAC5B;AAAA,MACA,SAAS,KAAK;AAAA,MACd;AAAA,MACA,UAAU,EAAE,cAAc,OAAO;AAAA,IACnC,CAAC;AAGD,UAAM,WAAW,MAAM,KAAK,QAAQ,YAAY,KAAK,IAAI;AAEzD,eAAW,QAAQ,UAAU;AAC3B,UAAI,KAAK,WAAW,QAAQ;AAC1B,cAAM,KAAK,QAAQ,cAAc,KAAK,WAAW,OAAO;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAuD;AAC3D,WAAO,KAAK,QAAQ,YAAe,KAAK,IAAI;AAAA,EAC9C;AAAA,EAEA,UAAuB,SAAwC;AAE7D,SAAK,QAAQ,UAAU,KAAK,MAAM,KAAK,WAAW,OAAyB;AAE3E,WAAO,MAAM;AACX,WAAK,QAAQ,YAAY,KAAK,MAAM,KAAK,SAAS;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,WACE,SACY;AACZ,SAAK,iBAAiB,IAAI,OAAyC;AAEnE,WAAO,MAAM;AACX,WAAK,iBAAiB,OAAO,OAAyC;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAA0B,OAA+B;AACvD,eAAW,WAAW,KAAK,kBAAkB;AAC3C,UAAI;AACF,gBAAQ,KAAsB;AAAA,MAChC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,UAAgC;AACpC,UAAM,cAAc,MAAM,KAAK,QAAQ,eAAe,KAAK,IAAI;AAC/D,UAAM,WAAW,MAAM,KAAK,QAAQ,YAAY,KAAK,IAAI;AAEzD,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,iBAAiB,YAAY;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,cACd,MACA,SACA,WACS;AACT,SAAO,IAAI,YAAY,MAAM,SAAS,SAAS;AACjD;;;ACAO,SAAS,sBAAsB,QAAyC;AAC7E,UAAQ,OAAO,SAAS;AAAA,IACtB,KAAK;AACH,aAAO,IAAI,WAAW,OAAO,GAAG;AAAA,IAElC,KAAK;AACH,UAAI,CAAC,OAAO,gBAAgB;AAC1B,cAAM,IAAI,MAAM,4DAA4D;AAAA,MAC9E;AACA,aAAO,IAAI,sBAAsB,OAAO,cAAc;AAAA,IAExD,KAAK;AAEH,aAAO,IAAI,WAAW,EAAE,cAAc,EAAE,CAAC;AAAA,IAE3C;AACE,YAAM,IAAI,MAAM,yBAAyB,OAAO,OAAO,EAAE;AAAA,EAC7D;AACF;AAGA,IAAO,gBAAQ;AAAA,EACb;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/adapters/sse.ts","../src/adapters/durable-objects.ts","../src/hono.ts","../src/core/channel.ts","../src/index.ts"],"sourcesContent":["/**\n * @parsrun/realtime - Type Definitions\n * Realtime communication types and interfaces\n */\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\n/**\n * Realtime adapter type\n */\nexport type RealtimeAdapterType = \"sse\" | \"durable-objects\" | \"memory\";\n\n/**\n * Realtime message\n */\nexport interface RealtimeMessage<T = unknown> {\n /** Unique message ID */\n id: string;\n /** Event type/name */\n event: string;\n /** Channel name */\n channel: string;\n /** Message payload */\n data: T;\n /** Sender ID (optional) */\n senderId?: string | undefined;\n /** Timestamp */\n timestamp: number;\n /** Metadata */\n metadata?: Record<string, unknown> | undefined;\n}\n\n/**\n * Presence user data\n */\nexport interface PresenceUser<T = unknown> {\n /** User ID */\n userId: string;\n /** Session ID */\n sessionId: string;\n /** Custom presence data */\n data: T;\n /** Join timestamp */\n joinedAt: number;\n /** Last activity timestamp */\n lastSeenAt: number;\n}\n\n/**\n * Channel info\n */\nexport interface ChannelInfo {\n /** Channel name */\n name: string;\n /** Number of subscribers */\n subscriberCount: number;\n /** Presence users */\n presence: PresenceUser[];\n /** Channel metadata */\n metadata?: Record<string, unknown> | undefined;\n}\n\n// ============================================================================\n// Events\n// ============================================================================\n\n/**\n * System event types\n */\nexport type SystemEventType =\n | \"connection:open\"\n | \"connection:close\"\n | \"connection:error\"\n | \"presence:join\"\n | \"presence:leave\"\n | \"presence:update\"\n | \"channel:subscribe\"\n | \"channel:unsubscribe\"\n | \"ping\"\n | \"pong\";\n\n/**\n * Message handler function\n */\nexport type MessageHandler<T = unknown> = (\n message: RealtimeMessage<T>\n) => void | Promise<void>;\n\n/**\n * Connection event handler\n */\nexport type ConnectionHandler = (\n event: ConnectionEvent\n) => void | Promise<void>;\n\n/**\n * Connection event\n */\nexport interface ConnectionEvent {\n type: \"open\" | \"close\" | \"error\";\n sessionId: string;\n userId?: string | undefined;\n error?: Error | undefined;\n timestamp: number;\n}\n\n/**\n * Presence event\n */\nexport interface PresenceEvent<T = unknown> {\n type: \"join\" | \"leave\" | \"update\";\n channel: string;\n user: PresenceUser<T>;\n timestamp: number;\n}\n\n// ============================================================================\n// Channel Interface\n// ============================================================================\n\n/**\n * Channel interface for realtime communication\n */\nexport interface Channel {\n /** Channel name */\n readonly name: string;\n\n /**\n * Broadcast message to all subscribers\n */\n broadcast<T = unknown>(event: string, data: T): Promise<void>;\n\n /**\n * Send message to specific user\n */\n send<T = unknown>(userId: string, event: string, data: T): Promise<void>;\n\n /**\n * Get current presence users\n */\n getPresence<T = unknown>(): Promise<PresenceUser<T>[]>;\n\n /**\n * Subscribe to channel messages\n */\n subscribe<T = unknown>(handler: MessageHandler<T>): () => void;\n\n /**\n * Subscribe to presence events\n */\n onPresence<T = unknown>(\n handler: (event: PresenceEvent<T>) => void\n ): () => void;\n\n /**\n * Get channel info\n */\n getInfo(): Promise<ChannelInfo>;\n}\n\n// ============================================================================\n// Realtime Service Interface\n// ============================================================================\n\n/**\n * Realtime service interface\n */\nexport interface RealtimeService {\n /** Adapter type */\n readonly adapterType: RealtimeAdapterType;\n\n /**\n * Get or create a channel\n */\n channel(name: string): Channel;\n\n /**\n * Broadcast to a channel\n */\n broadcast<T = unknown>(channel: string, event: string, data: T): Promise<void>;\n\n /**\n * Get channel info\n */\n getChannelInfo(channel: string): Promise<ChannelInfo | null>;\n\n /**\n * List active channels\n */\n listChannels(): Promise<string[]>;\n\n /**\n * Subscribe to connection events\n */\n onConnection(handler: ConnectionHandler): () => void;\n\n /**\n * Close all connections and cleanup\n */\n close(): Promise<void>;\n}\n\n// ============================================================================\n// Adapter Interface\n// ============================================================================\n\n/**\n * Realtime adapter interface (low-level)\n */\nexport interface RealtimeAdapter {\n /** Adapter type */\n readonly type: RealtimeAdapterType;\n\n /**\n * Add a subscriber to channel\n */\n subscribe(\n channel: string,\n sessionId: string,\n handler: MessageHandler\n ): Promise<void>;\n\n /**\n * Remove a subscriber from channel\n */\n unsubscribe(channel: string, sessionId: string): Promise<void>;\n\n /**\n * Publish message to channel\n */\n publish<T = unknown>(channel: string, message: RealtimeMessage<T>): Promise<void>;\n\n /**\n * Send to specific session\n */\n sendToSession<T = unknown>(\n sessionId: string,\n message: RealtimeMessage<T>\n ): Promise<boolean>;\n\n /**\n * Get subscribers for channel\n */\n getSubscribers(channel: string): Promise<string[]>;\n\n /**\n * Set presence for user in channel\n */\n setPresence<T = unknown>(\n channel: string,\n sessionId: string,\n userId: string,\n data: T\n ): Promise<void>;\n\n /**\n * Remove presence\n */\n removePresence(channel: string, sessionId: string): Promise<void>;\n\n /**\n * Get presence for channel\n */\n getPresence<T = unknown>(channel: string): Promise<PresenceUser<T>[]>;\n\n /**\n * Cleanup and close adapter\n */\n close(): Promise<void>;\n}\n\n// ============================================================================\n// SSE Types\n// ============================================================================\n\n/**\n * SSE connection\n */\nexport interface SSEConnection {\n /** Session ID */\n sessionId: string;\n /** User ID */\n userId?: string | undefined;\n /** Subscribed channels */\n channels: Set<string>;\n /** Response writer */\n writer: WritableStreamDefaultWriter<Uint8Array>;\n /** Connection state */\n state: \"open\" | \"closed\";\n /** Created timestamp */\n createdAt: number;\n /** Last ping timestamp */\n lastPingAt: number;\n}\n\n/**\n * SSE adapter options\n */\nexport interface SSEAdapterOptions {\n /** Ping interval in milliseconds (default: 30000) */\n pingInterval?: number | undefined;\n /** Connection timeout in milliseconds (default: 0 = no timeout) */\n connectionTimeout?: number | undefined;\n /** Max connections per channel (default: 1000) */\n maxConnectionsPerChannel?: number | undefined;\n /** Retry delay for client reconnection (default: 3000) */\n retryDelay?: number | undefined;\n}\n\n// ============================================================================\n// Durable Objects Types\n// ============================================================================\n\n/**\n * Durable Objects adapter options\n */\nexport interface DurableObjectsAdapterOptions {\n /** Durable Object namespace binding */\n namespace: DurableObjectNamespace;\n /** Channel prefix (default: \"channel:\") */\n channelPrefix?: string | undefined;\n}\n\n/**\n * Durable Object WebSocket session\n */\nexport interface DOWebSocketSession {\n /** Session ID */\n sessionId: string;\n /** User ID */\n userId?: string | undefined;\n /** WebSocket connection */\n webSocket: WebSocket;\n /** Presence data */\n presence?: unknown;\n /** Connection state */\n state: \"connecting\" | \"open\" | \"closing\" | \"closed\";\n /** Created timestamp */\n createdAt: number;\n}\n\n/**\n * Durable Object state stored in storage\n */\nexport interface DOChannelState {\n /** Channel name */\n name: string;\n /** Channel metadata */\n metadata?: Record<string, unknown> | undefined;\n /** Created timestamp */\n createdAt: number;\n}\n\n// ============================================================================\n// Config Types\n// ============================================================================\n\n/**\n * Realtime config\n */\nexport interface RealtimeConfig {\n /** Adapter type */\n adapter: RealtimeAdapterType;\n\n /** SSE adapter options */\n sse?: SSEAdapterOptions | undefined;\n\n /** Durable Objects adapter options */\n durableObjects?: DurableObjectsAdapterOptions | undefined;\n\n /** Enable debug logging */\n debug?: boolean | undefined;\n}\n\n// ============================================================================\n// Errors\n// ============================================================================\n\n/**\n * Realtime error\n */\nexport class RealtimeError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = \"RealtimeError\";\n }\n}\n\n/**\n * Common error codes\n */\nexport const RealtimeErrorCodes = {\n CONNECTION_FAILED: \"CONNECTION_FAILED\",\n CHANNEL_NOT_FOUND: \"CHANNEL_NOT_FOUND\",\n UNAUTHORIZED: \"UNAUTHORIZED\",\n MESSAGE_TOO_LARGE: \"MESSAGE_TOO_LARGE\",\n RATE_LIMITED: \"RATE_LIMITED\",\n ADAPTER_ERROR: \"ADAPTER_ERROR\",\n INVALID_MESSAGE: \"INVALID_MESSAGE\",\n} as const;\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Create message options\n */\nexport interface CreateMessageOptions<T = unknown> {\n event: string;\n channel: string;\n data: T;\n senderId?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n}\n\n/**\n * Create a realtime message\n */\nexport function createMessage<T = unknown>(\n options: CreateMessageOptions<T>\n): RealtimeMessage<T> {\n return {\n id: crypto.randomUUID(),\n event: options.event,\n channel: options.channel,\n data: options.data,\n senderId: options.senderId,\n timestamp: Date.now(),\n metadata: options.metadata,\n };\n}\n\n/**\n * Parse SSE event string\n */\nexport function parseSSEEvent(eventString: string): RealtimeMessage | null {\n try {\n const lines = eventString.split(\"\\n\");\n let event = \"message\";\n let data = \"\";\n let id = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event:\")) {\n event = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n data = line.slice(5).trim();\n } else if (line.startsWith(\"id:\")) {\n id = line.slice(3).trim();\n }\n }\n\n if (!data) return null;\n\n const parsed = JSON.parse(data);\n return {\n id: id || parsed.id || crypto.randomUUID(),\n event,\n channel: parsed.channel || \"\",\n data: parsed.data ?? parsed,\n senderId: parsed.senderId,\n timestamp: parsed.timestamp || Date.now(),\n metadata: parsed.metadata,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Format message as SSE event string\n */\nexport function formatSSEEvent(message: RealtimeMessage): string {\n const lines: string[] = [];\n\n lines.push(`id:${message.id}`);\n lines.push(`event:${message.event}`);\n lines.push(`data:${JSON.stringify(message)}`);\n lines.push(\"\"); // Empty line to end event\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","/**\n * @parsrun/realtime - SSE Adapter\n * Server-Sent Events adapter for all runtimes\n */\n\nimport type {\n MessageHandler,\n PresenceUser,\n RealtimeAdapter,\n RealtimeMessage,\n SSEAdapterOptions,\n SSEConnection,\n} from \"../types.js\";\nimport { formatSSEEvent, RealtimeError, RealtimeErrorCodes } from \"../types.js\";\n\n/**\n * Resolved SSE adapter options (all required)\n */\ninterface ResolvedSSEOptions {\n pingInterval: number;\n connectionTimeout: number;\n maxConnectionsPerChannel: number;\n retryDelay: number;\n}\n\n/**\n * Default SSE adapter options\n */\nconst DEFAULT_OPTIONS: ResolvedSSEOptions = {\n pingInterval: 30000,\n connectionTimeout: 0,\n maxConnectionsPerChannel: 1000,\n retryDelay: 3000,\n};\n\n/**\n * SSE Adapter\n * Works on all runtimes (Node, Deno, Bun, Cloudflare Workers)\n */\nexport class SSEAdapter implements RealtimeAdapter {\n readonly type = \"sse\" as const;\n\n private options: ResolvedSSEOptions;\n private connections: Map<string, SSEConnection> = new Map();\n private channelSubscribers: Map<string, Map<string, MessageHandler>> = new Map();\n private presence: Map<string, Map<string, PresenceUser>> = new Map();\n private pingIntervalId: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: SSEAdapterOptions = {}) {\n this.options = {\n pingInterval: options.pingInterval ?? DEFAULT_OPTIONS.pingInterval,\n connectionTimeout: options.connectionTimeout ?? DEFAULT_OPTIONS.connectionTimeout,\n maxConnectionsPerChannel: options.maxConnectionsPerChannel ?? DEFAULT_OPTIONS.maxConnectionsPerChannel,\n retryDelay: options.retryDelay ?? DEFAULT_OPTIONS.retryDelay,\n };\n\n // Start ping interval\n if (this.options.pingInterval > 0) {\n this.startPingInterval();\n }\n }\n\n /**\n * Create SSE response for a new connection\n */\n createConnection(\n sessionId: string,\n userId?: string\n ): { response: Response; connection: SSEConnection } {\n const { readable, writable } = new TransformStream<Uint8Array>();\n const writer = writable.getWriter();\n\n const connection: SSEConnection = {\n sessionId,\n userId,\n channels: new Set(),\n writer,\n state: \"open\",\n createdAt: Date.now(),\n lastPingAt: Date.now(),\n };\n\n this.connections.set(sessionId, connection);\n\n // Send initial retry directive\n const encoder = new TextEncoder();\n writer.write(encoder.encode(`retry:${this.options.retryDelay}\\n\\n`));\n\n const response = new Response(readable, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"X-Accel-Buffering\": \"no\", // Disable nginx buffering\n },\n });\n\n return { response, connection };\n }\n\n /**\n * Close a connection\n */\n async closeConnection(sessionId: string): Promise<void> {\n const connection = this.connections.get(sessionId);\n if (!connection) return;\n\n connection.state = \"closed\";\n\n // Remove from all channels\n for (const channel of connection.channels) {\n await this.unsubscribe(channel, sessionId);\n await this.removePresence(channel, sessionId);\n }\n\n // Close writer\n try {\n await connection.writer.close();\n } catch {\n // Ignore close errors\n }\n\n this.connections.delete(sessionId);\n }\n\n /**\n * Get a connection by session ID\n */\n getConnection(sessionId: string): SSEConnection | undefined {\n return this.connections.get(sessionId);\n }\n\n /**\n * Get all active connections\n */\n getConnections(): Map<string, SSEConnection> {\n return this.connections;\n }\n\n // ============================================================================\n // RealtimeAdapter Implementation\n // ============================================================================\n\n async subscribe(\n channel: string,\n sessionId: string,\n handler: MessageHandler\n ): Promise<void> {\n const connection = this.connections.get(sessionId);\n if (!connection) {\n throw new RealtimeError(\n \"Connection not found\",\n RealtimeErrorCodes.CONNECTION_FAILED\n );\n }\n\n // Check max connections per channel\n const subscribers = this.channelSubscribers.get(channel);\n if (\n subscribers &&\n subscribers.size >= this.options.maxConnectionsPerChannel\n ) {\n throw new RealtimeError(\n \"Channel subscriber limit reached\",\n RealtimeErrorCodes.RATE_LIMITED\n );\n }\n\n // Add to channel subscribers\n if (!this.channelSubscribers.has(channel)) {\n this.channelSubscribers.set(channel, new Map());\n }\n this.channelSubscribers.get(channel)!.set(sessionId, handler);\n\n // Track channel on connection\n connection.channels.add(channel);\n\n // Send subscription confirmation\n await this.sendToConnection(connection, {\n id: crypto.randomUUID(),\n event: \"channel:subscribe\",\n channel,\n data: { channel },\n timestamp: Date.now(),\n });\n }\n\n async unsubscribe(channel: string, sessionId: string): Promise<void> {\n const subscribers = this.channelSubscribers.get(channel);\n if (subscribers) {\n subscribers.delete(sessionId);\n if (subscribers.size === 0) {\n this.channelSubscribers.delete(channel);\n }\n }\n\n const connection = this.connections.get(sessionId);\n if (connection) {\n connection.channels.delete(channel);\n }\n }\n\n async publish<T = unknown>(\n channel: string,\n message: RealtimeMessage<T>\n ): Promise<void> {\n const subscribers = this.channelSubscribers.get(channel);\n if (!subscribers) return;\n\n const promises: Promise<void>[] = [];\n\n for (const [sessionId, handler] of subscribers) {\n const connection = this.connections.get(sessionId);\n if (connection && connection.state === \"open\") {\n // Send via SSE\n promises.push(this.sendToConnection(connection, message));\n\n // Call handler\n try {\n const result = handler(message);\n if (result instanceof Promise) {\n promises.push(result.then(() => {}));\n }\n } catch {\n // Ignore handler errors\n }\n }\n }\n\n await Promise.allSettled(promises);\n }\n\n async sendToSession<T = unknown>(\n sessionId: string,\n message: RealtimeMessage<T>\n ): Promise<boolean> {\n const connection = this.connections.get(sessionId);\n if (!connection || connection.state !== \"open\") {\n return false;\n }\n\n try {\n await this.sendToConnection(connection, message);\n return true;\n } catch {\n return false;\n }\n }\n\n async getSubscribers(channel: string): Promise<string[]> {\n const subscribers = this.channelSubscribers.get(channel);\n return subscribers ? Array.from(subscribers.keys()) : [];\n }\n\n async setPresence<T = unknown>(\n channel: string,\n sessionId: string,\n userId: string,\n data: T\n ): Promise<void> {\n if (!this.presence.has(channel)) {\n this.presence.set(channel, new Map());\n }\n\n const now = Date.now();\n const existing = this.presence.get(channel)!.get(sessionId);\n\n const user: PresenceUser<T> = {\n userId,\n sessionId,\n data,\n joinedAt: existing?.joinedAt ?? now,\n lastSeenAt: now,\n };\n\n this.presence.get(channel)!.set(sessionId, user as PresenceUser);\n\n // Broadcast presence update\n const eventType = existing ? \"presence:update\" : \"presence:join\";\n await this.publish(channel, {\n id: crypto.randomUUID(),\n event: eventType,\n channel,\n data: {\n type: existing ? \"update\" : \"join\",\n user,\n presence: await this.getPresence(channel),\n },\n timestamp: now,\n });\n }\n\n async removePresence(channel: string, sessionId: string): Promise<void> {\n const channelPresence = this.presence.get(channel);\n if (!channelPresence) return;\n\n const user = channelPresence.get(sessionId);\n if (!user) return;\n\n channelPresence.delete(sessionId);\n if (channelPresence.size === 0) {\n this.presence.delete(channel);\n }\n\n // Broadcast presence leave\n await this.publish(channel, {\n id: crypto.randomUUID(),\n event: \"presence:leave\",\n channel,\n data: {\n type: \"leave\",\n user,\n presence: await this.getPresence(channel),\n },\n timestamp: Date.now(),\n });\n }\n\n async getPresence<T = unknown>(channel: string): Promise<PresenceUser<T>[]> {\n const channelPresence = this.presence.get(channel);\n if (!channelPresence) return [];\n return Array.from(channelPresence.values()) as PresenceUser<T>[];\n }\n\n async close(): Promise<void> {\n // Stop ping interval\n if (this.pingIntervalId) {\n clearInterval(this.pingIntervalId);\n this.pingIntervalId = null;\n }\n\n // Close all connections\n const closePromises = Array.from(this.connections.keys()).map((sessionId) =>\n this.closeConnection(sessionId)\n );\n await Promise.allSettled(closePromises);\n\n // Clear all data\n this.connections.clear();\n this.channelSubscribers.clear();\n this.presence.clear();\n }\n\n // ============================================================================\n // Private Methods\n // ============================================================================\n\n private async sendToConnection<T = unknown>(\n connection: SSEConnection,\n message: RealtimeMessage<T>\n ): Promise<void> {\n if (connection.state !== \"open\") return;\n\n const encoder = new TextEncoder();\n const sseEvent = formatSSEEvent(message as RealtimeMessage);\n\n try {\n await connection.writer.write(encoder.encode(sseEvent));\n } catch (err) {\n // Connection likely closed\n connection.state = \"closed\";\n await this.closeConnection(connection.sessionId);\n }\n }\n\n private startPingInterval(): void {\n this.pingIntervalId = setInterval(() => {\n this.sendPingToAll();\n }, this.options.pingInterval);\n }\n\n private async sendPingToAll(): Promise<void> {\n const now = Date.now();\n const encoder = new TextEncoder();\n const pingEvent = `event:ping\\ndata:${now}\\n\\n`;\n\n for (const [sessionId, connection] of this.connections) {\n if (connection.state !== \"open\") continue;\n\n // Check connection timeout\n if (\n this.options.connectionTimeout > 0 &&\n now - connection.lastPingAt > this.options.connectionTimeout\n ) {\n await this.closeConnection(sessionId);\n continue;\n }\n\n try {\n await connection.writer.write(encoder.encode(pingEvent));\n connection.lastPingAt = now;\n } catch {\n // Connection likely closed\n await this.closeConnection(sessionId);\n }\n }\n }\n\n /**\n * Update last ping time (call when receiving client ping)\n */\n updateLastPing(sessionId: string): void {\n const connection = this.connections.get(sessionId);\n if (connection) {\n connection.lastPingAt = Date.now();\n }\n }\n\n /**\n * Get statistics\n */\n getStats(): {\n totalConnections: number;\n totalChannels: number;\n connectionsByChannel: Record<string, number>;\n } {\n const connectionsByChannel: Record<string, number> = {};\n\n for (const [channel, subscribers] of this.channelSubscribers) {\n connectionsByChannel[channel] = subscribers.size;\n }\n\n return {\n totalConnections: this.connections.size,\n totalChannels: this.channelSubscribers.size,\n connectionsByChannel,\n };\n }\n}\n\n/**\n * Create SSE adapter\n */\nexport function createSSEAdapter(options?: SSEAdapterOptions): SSEAdapter {\n return new SSEAdapter(options);\n}\n","/**\n * @parsrun/realtime - Durable Objects Adapter\n * Cloudflare Durable Objects adapter for WebSocket-based realtime\n */\n\nimport type {\n DOWebSocketSession,\n DurableObjectsAdapterOptions,\n MessageHandler,\n PresenceUser,\n RealtimeAdapter,\n RealtimeMessage,\n} from \"../types.js\";\nimport { createMessage } from \"../types.js\";\n\n// ============================================================================\n// Durable Object Class (Export for wrangler.toml binding)\n// ============================================================================\n\n/**\n * RealtimeChannel Durable Object\n * Manages a single realtime channel with WebSocket connections\n *\n * Usage in wrangler.toml:\n * ```toml\n * [durable_objects]\n * bindings = [{ name = \"REALTIME_CHANNELS\", class_name = \"RealtimeChannelDO\" }]\n *\n * [[migrations]]\n * tag = \"v1\"\n * new_classes = [\"RealtimeChannelDO\"]\n * ```\n */\nexport class RealtimeChannelDO implements DurableObject {\n private sessions: Map<string, DOWebSocketSession> = new Map();\n private presence: Map<string, PresenceUser> = new Map();\n private channelName: string = \"\";\n private state: DurableObjectState;\n\n constructor(\n state: DurableObjectState,\n _env: unknown\n ) {\n this.state = state;\n // Restore hibernated WebSocket sessions\n this.state.getWebSockets().forEach((ws) => {\n const meta = ws.deserializeAttachment() as {\n sessionId: string;\n userId?: string;\n } | null;\n if (meta) {\n this.sessions.set(meta.sessionId, {\n sessionId: meta.sessionId,\n userId: meta.userId,\n webSocket: ws,\n state: \"open\",\n createdAt: Date.now(),\n });\n }\n });\n }\n\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n // Extract channel name from path\n const pathParts = url.pathname.split(\"/\").filter(Boolean);\n const lastPart = pathParts[pathParts.length - 1];\n if (lastPart) {\n this.channelName = lastPart;\n }\n\n // WebSocket upgrade\n if (request.headers.get(\"Upgrade\") === \"websocket\") {\n return this.handleWebSocketUpgrade(request);\n }\n\n // REST API endpoints\n switch (url.pathname.split(\"/\").pop()) {\n case \"broadcast\":\n return this.handleBroadcast(request);\n case \"presence\":\n return this.handleGetPresence();\n case \"info\":\n return this.handleGetInfo();\n case \"send\":\n return this.handleSendToUser(request);\n default:\n return new Response(\"Not found\", { status: 404 });\n }\n }\n\n // ============================================================================\n // WebSocket Handling\n // ============================================================================\n\n private handleWebSocketUpgrade(request: Request): Response {\n const url = new URL(request.url);\n const sessionId = url.searchParams.get(\"sessionId\") || crypto.randomUUID();\n const userId = url.searchParams.get(\"userId\") || undefined;\n\n const pair = new WebSocketPair();\n const client = pair[0];\n const server = pair[1];\n\n // Store session metadata for hibernation\n server.serializeAttachment({ sessionId, userId });\n\n this.state.acceptWebSocket(server);\n\n const session: DOWebSocketSession = {\n sessionId,\n userId,\n webSocket: server,\n state: \"open\",\n createdAt: Date.now(),\n };\n\n this.sessions.set(sessionId, session);\n\n // Send connection confirmation\n server.send(\n JSON.stringify(\n createMessage({\n event: \"connection:open\",\n channel: this.channelName,\n data: { sessionId, userId },\n })\n )\n );\n\n return new Response(null, { status: 101, webSocket: client });\n }\n\n async webSocketMessage(\n ws: WebSocket,\n message: string | ArrayBuffer\n ): Promise<void> {\n const session = this.getSessionByWebSocket(ws);\n if (!session) return;\n\n try {\n const data =\n typeof message === \"string\"\n ? message\n : new TextDecoder().decode(message);\n\n const parsed = JSON.parse(data) as RealtimeMessage;\n await this.handleMessage(session, parsed);\n } catch {\n // Invalid message format, ignore\n }\n }\n\n async webSocketClose(\n ws: WebSocket,\n code: number,\n reason: string,\n _wasClean: boolean\n ): Promise<void> {\n const session = this.getSessionByWebSocket(ws);\n if (!session) return;\n\n // Remove presence\n this.presence.delete(session.sessionId);\n await this.broadcastPresenceUpdate();\n\n // Remove session\n this.sessions.delete(session.sessionId);\n\n // Broadcast leave event\n await this.broadcastToAll({\n event: \"connection:close\",\n channel: this.channelName,\n data: {\n sessionId: session.sessionId,\n userId: session.userId,\n code,\n reason,\n },\n });\n }\n\n async webSocketError(ws: WebSocket, _error: unknown): Promise<void> {\n const session = this.getSessionByWebSocket(ws);\n if (session) {\n session.state = \"closed\";\n this.sessions.delete(session.sessionId);\n this.presence.delete(session.sessionId);\n }\n }\n\n // ============================================================================\n // Message Handling\n // ============================================================================\n\n private async handleMessage(\n session: DOWebSocketSession,\n message: RealtimeMessage\n ): Promise<void> {\n switch (message.event) {\n case \"ping\":\n session.webSocket.send(\n JSON.stringify(\n createMessage({\n event: \"pong\",\n channel: this.channelName,\n data: { timestamp: Date.now() },\n })\n )\n );\n break;\n\n case \"presence:join\":\n await this.handlePresenceJoin(session, message.data);\n break;\n\n case \"presence:update\":\n await this.handlePresenceUpdate(session, message.data);\n break;\n\n case \"presence:leave\":\n await this.handlePresenceLeave(session);\n break;\n\n case \"broadcast\":\n // Broadcast to all except sender\n await this.broadcastToAll(\n {\n event: message.event,\n channel: this.channelName,\n data: message.data,\n senderId: session.sessionId,\n },\n session.sessionId\n );\n break;\n\n default:\n // Forward custom events\n await this.broadcastToAll(\n {\n event: message.event,\n channel: this.channelName,\n data: message.data,\n senderId: session.sessionId,\n },\n session.sessionId\n );\n }\n }\n\n // ============================================================================\n // Presence Handling\n // ============================================================================\n\n private async handlePresenceJoin(\n session: DOWebSocketSession,\n data: unknown\n ): Promise<void> {\n const now = Date.now();\n const user: PresenceUser = {\n userId: session.userId || session.sessionId,\n sessionId: session.sessionId,\n data,\n joinedAt: now,\n lastSeenAt: now,\n };\n\n this.presence.set(session.sessionId, user);\n session.presence = data;\n\n await this.broadcastPresenceUpdate();\n }\n\n private async handlePresenceUpdate(\n session: DOWebSocketSession,\n data: unknown\n ): Promise<void> {\n const existing = this.presence.get(session.sessionId);\n if (existing) {\n existing.data = data;\n existing.lastSeenAt = Date.now();\n session.presence = data;\n await this.broadcastPresenceUpdate();\n }\n }\n\n private async handlePresenceLeave(session: DOWebSocketSession): Promise<void> {\n this.presence.delete(session.sessionId);\n session.presence = undefined;\n await this.broadcastPresenceUpdate();\n }\n\n private async broadcastPresenceUpdate(): Promise<void> {\n const presenceList = Array.from(this.presence.values());\n\n await this.broadcastToAll({\n event: \"presence:sync\",\n channel: this.channelName,\n data: presenceList,\n });\n }\n\n // ============================================================================\n // REST API Handlers\n // ============================================================================\n\n private async handleBroadcast(request: Request): Promise<Response> {\n try {\n const body = (await request.json()) as { event: string; data: unknown };\n\n await this.broadcastToAll({\n event: body.event,\n channel: this.channelName,\n data: body.data,\n });\n\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (err) {\n return new Response(\n JSON.stringify({ success: false, error: String(err) }),\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n }\n\n private handleGetPresence(): Response {\n const presenceList = Array.from(this.presence.values());\n return new Response(JSON.stringify(presenceList), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n private handleGetInfo(): Response {\n return new Response(\n JSON.stringify({\n channel: this.channelName,\n connections: this.sessions.size,\n presence: this.presence.size,\n }),\n { headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n\n private async handleSendToUser(request: Request): Promise<Response> {\n try {\n const body = (await request.json()) as {\n userId: string;\n event: string;\n data: unknown;\n };\n\n let sent = false;\n for (const session of this.sessions.values()) {\n if (session.userId === body.userId && session.state === \"open\") {\n session.webSocket.send(\n JSON.stringify(\n createMessage({\n event: body.event,\n channel: this.channelName,\n data: body.data,\n })\n )\n );\n sent = true;\n }\n }\n\n return new Response(JSON.stringify({ success: true, sent }), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (err) {\n return new Response(\n JSON.stringify({ success: false, error: String(err) }),\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n }\n\n // ============================================================================\n // Helpers\n // ============================================================================\n\n private getSessionByWebSocket(ws: WebSocket): DOWebSocketSession | undefined {\n for (const session of this.sessions.values()) {\n if (session.webSocket === ws) {\n return session;\n }\n }\n return undefined;\n }\n\n private async broadcastToAll(\n messageData: {\n event: string;\n channel: string;\n data: unknown;\n senderId?: string;\n },\n excludeSessionId?: string\n ): Promise<void> {\n const message = createMessage(messageData);\n const payload = JSON.stringify(message);\n\n for (const session of this.sessions.values()) {\n if (session.sessionId === excludeSessionId) continue;\n if (session.state !== \"open\") continue;\n\n try {\n session.webSocket.send(payload);\n } catch {\n // Connection might be closed\n session.state = \"closed\";\n }\n }\n }\n}\n\n// ============================================================================\n// Durable Objects Adapter (Client-side)\n// ============================================================================\n\n/**\n * Durable Objects Adapter\n * Uses RealtimeChannelDO for channel management\n */\nexport class DurableObjectsAdapter implements RealtimeAdapter {\n readonly type = \"durable-objects\" as const;\n\n private namespace: DurableObjectNamespace;\n private channelPrefix: string;\n private localHandlers: Map<string, Map<string, MessageHandler>> = new Map();\n\n constructor(options: DurableObjectsAdapterOptions) {\n this.namespace = options.namespace;\n this.channelPrefix = options.channelPrefix ?? \"channel:\";\n }\n\n /**\n * Get Durable Object stub for a channel\n */\n getChannelStub(channel: string): DurableObjectStub {\n const id = this.namespace.idFromName(`${this.channelPrefix}${channel}`);\n return this.namespace.get(id);\n }\n\n /**\n * Get WebSocket URL for a channel\n */\n getWebSocketUrl(\n channel: string,\n sessionId: string,\n userId?: string,\n baseUrl?: string\n ): string {\n const base = baseUrl || \"wss://your-worker.your-subdomain.workers.dev\";\n const params = new URLSearchParams({ sessionId });\n if (userId) params.set(\"userId\", userId);\n return `${base}/realtime/${channel}?${params}`;\n }\n\n // ============================================================================\n // RealtimeAdapter Implementation\n // ============================================================================\n\n async subscribe(\n channel: string,\n sessionId: string,\n handler: MessageHandler\n ): Promise<void> {\n // Store handler locally for callback\n if (!this.localHandlers.has(channel)) {\n this.localHandlers.set(channel, new Map());\n }\n this.localHandlers.get(channel)!.set(sessionId, handler);\n\n // Note: Actual WebSocket subscription happens via WebSocket connection\n // This is used for server-side handler registration\n }\n\n async unsubscribe(channel: string, sessionId: string): Promise<void> {\n const handlers = this.localHandlers.get(channel);\n if (handlers) {\n handlers.delete(sessionId);\n if (handlers.size === 0) {\n this.localHandlers.delete(channel);\n }\n }\n }\n\n async publish<T = unknown>(\n channel: string,\n message: RealtimeMessage<T>\n ): Promise<void> {\n const stub = this.getChannelStub(channel);\n\n await stub.fetch(new Request(\"https://do/broadcast\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n event: message.event,\n data: message.data,\n }),\n }));\n\n // Call local handlers\n const handlers = this.localHandlers.get(channel);\n if (handlers) {\n for (const handler of handlers.values()) {\n try {\n await handler(message);\n } catch {\n // Ignore handler errors\n }\n }\n }\n }\n\n async sendToSession<T = unknown>(\n _sessionId: string,\n _message: RealtimeMessage<T>\n ): Promise<boolean> {\n // For DO adapter, we send via the REST API to a specific user\n // This requires knowing which channel the session is in\n // For simplicity, we broadcast with target metadata\n return false;\n }\n\n async getSubscribers(channel: string): Promise<string[]> {\n const handlers = this.localHandlers.get(channel);\n return handlers ? Array.from(handlers.keys()) : [];\n }\n\n async setPresence<T = unknown>(\n _channel: string,\n _sessionId: string,\n _userId: string,\n _data: T\n ): Promise<void> {\n // Presence is managed via WebSocket messages in DO\n // This is a no-op for the adapter\n }\n\n async removePresence(_channel: string, _sessionId: string): Promise<void> {\n // Presence is managed via WebSocket messages in DO\n }\n\n async getPresence<T = unknown>(channel: string): Promise<PresenceUser<T>[]> {\n const stub = this.getChannelStub(channel);\n const response = await stub.fetch(new Request(\"https://do/presence\"));\n return response.json();\n }\n\n async close(): Promise<void> {\n this.localHandlers.clear();\n }\n\n /**\n * Get channel info from Durable Object\n */\n async getChannelInfo(\n channel: string\n ): Promise<{ connections: number; presence: number }> {\n const stub = this.getChannelStub(channel);\n const response = await stub.fetch(new Request(\"https://do/info\"));\n return response.json();\n }\n\n /**\n * Send message to specific user in a channel\n */\n async sendToUser<T = unknown>(\n channel: string,\n userId: string,\n event: string,\n data: T\n ): Promise<boolean> {\n const stub = this.getChannelStub(channel);\n\n const response = await stub.fetch(\n new Request(\"https://do/send\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ userId, event, data }),\n })\n );\n\n const result = (await response.json()) as { sent: boolean };\n return result.sent;\n }\n}\n\n/**\n * Create Durable Objects adapter\n */\nexport function createDurableObjectsAdapter(\n options: DurableObjectsAdapterOptions\n): DurableObjectsAdapter {\n return new DurableObjectsAdapter(options);\n}\n","/**\n * @parsrun/realtime - Hono Integration\n * Middleware and routes for Hono framework\n */\n\nimport { Hono } from \"hono\";\nimport type { Context, MiddlewareHandler } from \"hono\";\nimport { SSEAdapter } from \"./adapters/sse.js\";\nimport type {\n RealtimeAdapter,\n SSEAdapterOptions,\n} from \"./types.js\";\nimport { createMessage } from \"./types.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Hono realtime context variables\n */\nexport interface RealtimeVariables {\n realtime: {\n adapter: RealtimeAdapter;\n sessionId: string;\n userId: string | undefined;\n };\n}\n\n/**\n * SSE route options\n */\nexport interface SSERouteOptions {\n /** Get session ID from context (default: query param or random) */\n getSessionId?: (c: Context) => string;\n /** Get user ID from context (default: from auth) */\n getUserId?: (c: Context) => string | undefined;\n /** Channels the user can subscribe to */\n getChannels?: (c: Context) => string[];\n /** Called when connection opens */\n onConnect?: (c: Context, sessionId: string) => void | Promise<void>;\n /** Called when connection closes */\n onDisconnect?: (c: Context, sessionId: string) => void | Promise<void>;\n}\n\n/**\n * Durable Objects route options\n */\nexport interface DORouteOptions {\n /** Durable Object namespace binding name */\n namespaceBinding: string;\n /** Channel prefix */\n channelPrefix?: string;\n /** Authorize connection */\n authorize?: (c: Context, channel: string) => boolean | Promise<boolean>;\n}\n\n// ============================================================================\n// SSE Middleware and Routes\n// ============================================================================\n\n/**\n * Create SSE realtime middleware\n * Adds SSE adapter to context\n */\nexport function sseMiddleware(\n options?: SSEAdapterOptions\n): MiddlewareHandler<{ Variables: RealtimeVariables }> {\n const adapter = new SSEAdapter(options);\n\n return async (c, next) => {\n const sessionId = c.req.query(\"sessionId\") || crypto.randomUUID();\n // Get userId from context if set by auth middleware (type-safe access)\n const vars = c.var as Record<string, unknown>;\n const userId = typeof vars[\"userId\"] === \"string\" ? vars[\"userId\"] : undefined;\n\n c.set(\"realtime\", {\n adapter,\n sessionId,\n userId,\n });\n\n await next();\n };\n}\n\n/**\n * Create SSE subscription endpoint\n * Client connects to this endpoint to receive events\n */\nexport function createSSEHandler(\n adapter: SSEAdapter,\n options: SSERouteOptions = {}\n): (c: Context) => Response | Promise<Response> {\n return async (c: Context) => {\n const sessionId = options.getSessionId?.(c) ||\n c.req.query(\"sessionId\") ||\n crypto.randomUUID();\n\n const vars = c.var as Record<string, unknown>;\n const userId = options.getUserId?.(c) ||\n (typeof vars[\"userId\"] === \"string\" ? vars[\"userId\"] : undefined);\n\n const channels = options.getChannels?.(c) ||\n c.req.query(\"channels\")?.split(\",\") ||\n [];\n\n // Create SSE connection\n const { response } = adapter.createConnection(sessionId, userId);\n\n // Subscribe to requested channels\n for (const channel of channels) {\n await adapter.subscribe(channel, sessionId, () => {\n // Handler is called but SSE sends via connection.writer\n });\n }\n\n // Call onConnect callback\n if (options.onConnect) {\n await options.onConnect(c, sessionId);\n }\n\n // Handle connection close\n c.req.raw.signal.addEventListener(\"abort\", async () => {\n await adapter.closeConnection(sessionId);\n if (options.onDisconnect) {\n await options.onDisconnect(c, sessionId);\n }\n });\n\n return response;\n };\n}\n\n/**\n * Create complete SSE routes for Hono\n */\nexport function createSSERoutes(\n adapter: SSEAdapter,\n options: SSERouteOptions = {}\n): Hono {\n const app = new Hono();\n\n // SSE subscription endpoint\n app.get(\"/subscribe\", createSSEHandler(adapter, options));\n\n // Subscribe to additional channel\n app.post(\"/subscribe/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const sessionId = c.req.query(\"sessionId\");\n\n if (!sessionId) {\n return c.json({ error: \"sessionId required\" }, 400);\n }\n\n const connection = adapter.getConnection(sessionId);\n if (!connection) {\n return c.json({ error: \"Connection not found\" }, 404);\n }\n\n await adapter.subscribe(channel, sessionId, () => {});\n\n return c.json({ success: true, channel });\n });\n\n // Unsubscribe from channel\n app.post(\"/unsubscribe/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const sessionId = c.req.query(\"sessionId\");\n\n if (!sessionId) {\n return c.json({ error: \"sessionId required\" }, 400);\n }\n\n await adapter.unsubscribe(channel, sessionId);\n\n return c.json({ success: true, channel });\n });\n\n // Broadcast to channel\n app.post(\"/broadcast/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const body = await c.req.json<{ event: string; data: unknown }>();\n\n const message = createMessage({\n event: body.event,\n channel,\n data: body.data,\n });\n\n await adapter.publish(channel, message);\n\n return c.json({ success: true });\n });\n\n // Get channel presence\n app.get(\"/presence/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const presence = await adapter.getPresence(channel);\n return c.json(presence);\n });\n\n // Set presence\n app.post(\"/presence/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const sessionId = c.req.query(\"sessionId\");\n const userId = c.req.query(\"userId\") || sessionId;\n const data = await c.req.json();\n\n if (!sessionId) {\n return c.json({ error: \"sessionId required\" }, 400);\n }\n\n await adapter.setPresence(channel, sessionId, userId!, data);\n\n return c.json({ success: true });\n });\n\n // Remove presence\n app.delete(\"/presence/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n const sessionId = c.req.query(\"sessionId\");\n\n if (!sessionId) {\n return c.json({ error: \"sessionId required\" }, 400);\n }\n\n await adapter.removePresence(channel, sessionId);\n\n return c.json({ success: true });\n });\n\n // Get stats\n app.get(\"/stats\", (c) => {\n return c.json(adapter.getStats());\n });\n\n return app;\n}\n\n// ============================================================================\n// Durable Objects Routes\n// ============================================================================\n\n/**\n * Create Durable Objects realtime routes for Hono\n * Proxies requests to the appropriate Durable Object\n */\nexport function createDORoutes(options: DORouteOptions): Hono {\n const app = new Hono();\n const prefix = options.channelPrefix ?? \"channel:\";\n\n // WebSocket upgrade handler\n app.get(\"/ws/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n\n // Authorization check\n if (options.authorize) {\n const authorized = await options.authorize(c, channel);\n if (!authorized) {\n return c.json({ error: \"Unauthorized\" }, 403);\n }\n }\n\n // Get Durable Object namespace from env\n const env = c.env as Record<string, unknown>;\n const namespace = env[options.namespaceBinding] as DurableObjectNamespace;\n\n if (!namespace) {\n return c.json(\n { error: `Namespace ${options.namespaceBinding} not found` },\n 500\n );\n }\n\n // Get Durable Object stub\n const id = namespace.idFromName(`${prefix}${channel}`);\n const stub = namespace.get(id);\n\n // Forward the request to the Durable Object\n const url = new URL(c.req.url);\n url.pathname = `/ws/${channel}`;\n\n return stub.fetch(new Request(url.toString(), c.req.raw));\n });\n\n // Broadcast to channel\n app.post(\"/broadcast/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n\n const env = c.env as Record<string, unknown>;\n const namespace = env[options.namespaceBinding] as DurableObjectNamespace;\n\n if (!namespace) {\n return c.json(\n { error: `Namespace ${options.namespaceBinding} not found` },\n 500\n );\n }\n\n const id = namespace.idFromName(`${prefix}${channel}`);\n const stub = namespace.get(id);\n\n const body = await c.req.json();\n\n const response = await stub.fetch(\n new Request(\"https://do/broadcast\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n })\n );\n\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n });\n\n // Get channel presence\n app.get(\"/presence/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n\n const env = c.env as Record<string, unknown>;\n const namespace = env[options.namespaceBinding] as DurableObjectNamespace;\n\n if (!namespace) {\n return c.json(\n { error: `Namespace ${options.namespaceBinding} not found` },\n 500\n );\n }\n\n const id = namespace.idFromName(`${prefix}${channel}`);\n const stub = namespace.get(id);\n\n const response = await stub.fetch(new Request(\"https://do/presence\"));\n\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n });\n\n // Get channel info\n app.get(\"/info/:channel\", async (c) => {\n const channel = c.req.param(\"channel\");\n\n const env = c.env as Record<string, unknown>;\n const namespace = env[options.namespaceBinding] as DurableObjectNamespace;\n\n if (!namespace) {\n return c.json(\n { error: `Namespace ${options.namespaceBinding} not found` },\n 500\n );\n }\n\n const id = namespace.idFromName(`${prefix}${channel}`);\n const stub = namespace.get(id);\n\n const response = await stub.fetch(new Request(\"https://do/info\"));\n\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n });\n\n return app;\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Broadcast helper for use in route handlers\n */\nexport async function broadcast(\n adapter: RealtimeAdapter,\n channel: string,\n event: string,\n data: unknown\n): Promise<void> {\n const message = createMessage({ event, channel, data });\n await adapter.publish(channel, message);\n}\n\n/**\n * Send to user helper\n */\nexport async function sendToUser(\n adapter: RealtimeAdapter,\n channel: string,\n userId: string,\n event: string,\n data: unknown\n): Promise<void> {\n const presence = await adapter.getPresence(channel);\n const userSessions = presence.filter((p) => p.userId === userId);\n\n const message = createMessage({ event, channel, data });\n\n for (const session of userSessions) {\n await adapter.sendToSession(session.sessionId, message);\n }\n}\n","/**\n * @parsrun/realtime - Channel Implementation\n * Channel abstraction for realtime communication\n */\n\nimport type {\n Channel,\n ChannelInfo,\n MessageHandler,\n PresenceEvent,\n PresenceUser,\n RealtimeAdapter,\n} from \"../types.js\";\nimport { createMessage } from \"../types.js\";\n\n/**\n * Channel implementation using an adapter\n */\nexport class ChannelImpl implements Channel {\n private presenceHandlers: Set<(event: PresenceEvent) => void> = new Set();\n\n constructor(\n public readonly name: string,\n private adapter: RealtimeAdapter,\n private sessionId: string\n ) {}\n\n async broadcast<T = unknown>(event: string, data: T): Promise<void> {\n const message = createMessage({\n event,\n channel: this.name,\n data,\n });\n\n await this.adapter.publish(this.name, message);\n }\n\n async send<T = unknown>(\n userId: string,\n event: string,\n data: T\n ): Promise<void> {\n const message = createMessage({\n event,\n channel: this.name,\n data,\n metadata: { targetUserId: userId },\n });\n\n // Find session for user and send\n const presence = await this.adapter.getPresence(this.name);\n\n for (const user of presence) {\n if (user.userId === userId) {\n await this.adapter.sendToSession(user.sessionId, message);\n }\n }\n }\n\n async getPresence<T = unknown>(): Promise<PresenceUser<T>[]> {\n return this.adapter.getPresence<T>(this.name);\n }\n\n subscribe<T = unknown>(handler: MessageHandler<T>): () => void {\n // Subscribe this session to the channel\n this.adapter.subscribe(this.name, this.sessionId, handler as MessageHandler);\n\n return () => {\n this.adapter.unsubscribe(this.name, this.sessionId);\n };\n }\n\n onPresence<T = unknown>(\n handler: (event: PresenceEvent<T>) => void\n ): () => void {\n this.presenceHandlers.add(handler as (event: PresenceEvent) => void);\n\n return () => {\n this.presenceHandlers.delete(handler as (event: PresenceEvent) => void);\n };\n }\n\n /**\n * Emit presence event to handlers\n */\n emitPresence<T = unknown>(event: PresenceEvent<T>): void {\n for (const handler of this.presenceHandlers) {\n try {\n handler(event as PresenceEvent);\n } catch {\n // Ignore handler errors\n }\n }\n }\n\n async getInfo(): Promise<ChannelInfo> {\n const subscribers = await this.adapter.getSubscribers(this.name);\n const presence = await this.adapter.getPresence(this.name);\n\n return {\n name: this.name,\n subscriberCount: subscribers.length,\n presence,\n };\n }\n}\n\n/**\n * Create a channel instance\n */\nexport function createChannel(\n name: string,\n adapter: RealtimeAdapter,\n sessionId: string\n): Channel {\n return new ChannelImpl(name, adapter, sessionId);\n}\n","/**\n * @module\n * Edge-compatible realtime for Pars.\n *\n * Supports multiple adapters:\n * - SSE (Server-Sent Events) - Works on all runtimes\n * - Durable Objects - Cloudflare Workers with WebSocket\n *\n * @example SSE Usage\n * ```typescript\n * import { createSSEAdapter, createSSERoutes } from '@parsrun/realtime';\n * import { Hono } from 'hono';\n *\n * const app = new Hono();\n * const sse = createSSEAdapter({ pingInterval: 30000 });\n *\n * // Mount realtime routes\n * app.route('/realtime', createSSERoutes(sse));\n *\n * // Broadcast from anywhere\n * await sse.publish('orders', createMessage({\n * event: 'order:created',\n * channel: 'orders',\n * data: { orderId: '123' }\n * }));\n * ```\n *\n * @example Durable Objects Usage\n * ```typescript\n * import { RealtimeChannelDO, createDORoutes } from '@parsrun/realtime';\n * import { Hono } from 'hono';\n *\n * const app = new Hono();\n *\n * // Mount DO routes\n * app.route('/realtime', createDORoutes({\n * namespaceBinding: 'REALTIME_CHANNELS'\n * }));\n *\n * // Export DO class for wrangler\n * export { RealtimeChannelDO };\n * ```\n *\n * @example Client-side SSE\n * ```typescript\n * const eventSource = new EventSource('/realtime/subscribe?channels=orders');\n *\n * eventSource.addEventListener('order:created', (e) => {\n * const data = JSON.parse(e.data);\n * console.log('New order:', data);\n * });\n * ```\n *\n * @example Client-side WebSocket (DO)\n * ```typescript\n * const ws = new WebSocket('wss://api.example.com/realtime/ws/orders');\n *\n * ws.onmessage = (e) => {\n * const message = JSON.parse(e.data);\n * console.log('Received:', message);\n * };\n *\n * // Join presence\n * ws.send(JSON.stringify({\n * event: 'presence:join',\n * data: { name: 'John', status: 'online' }\n * }));\n * ```\n */\n\n// Re-export types\nexport * from \"./types.js\";\n\n// Re-export adapters\nexport {\n SSEAdapter,\n createSSEAdapter,\n} from \"./adapters/sse.js\";\n\nexport {\n DurableObjectsAdapter,\n RealtimeChannelDO,\n createDurableObjectsAdapter,\n} from \"./adapters/durable-objects.js\";\n\n// Import for default export\nimport { createSSEAdapter } from \"./adapters/sse.js\";\nimport { createDurableObjectsAdapter } from \"./adapters/durable-objects.js\";\n\n// Re-export Hono integration\nexport {\n sseMiddleware,\n createSSEHandler,\n createSSERoutes,\n createDORoutes,\n broadcast,\n sendToUser,\n type RealtimeVariables,\n type SSERouteOptions,\n type DORouteOptions,\n} from \"./hono.js\";\n\n// Re-export core\nexport { ChannelImpl, createChannel } from \"./core/channel.js\";\n\n// ============================================================================\n// Factory Functions\n// ============================================================================\n\nimport type { RealtimeAdapter, RealtimeConfig } from \"./types.js\";\nimport { SSEAdapter } from \"./adapters/sse.js\";\nimport { DurableObjectsAdapter } from \"./adapters/durable-objects.js\";\n\n/**\n * Create a realtime adapter based on config\n */\nexport function createRealtimeAdapter(config: RealtimeConfig): RealtimeAdapter {\n switch (config.adapter) {\n case \"sse\":\n return new SSEAdapter(config.sse);\n\n case \"durable-objects\":\n if (!config.durableObjects) {\n throw new Error(\"durableObjects config required for durable-objects adapter\");\n }\n return new DurableObjectsAdapter(config.durableObjects);\n\n case \"memory\":\n // Memory adapter uses SSE without ping\n return new SSEAdapter({ pingInterval: 0 });\n\n default:\n throw new Error(`Unknown adapter type: ${config.adapter}`);\n }\n}\n\n// Default export\nexport default {\n createRealtimeAdapter,\n createSSEAdapter,\n createDurableObjectsAdapter,\n};\n"],"mappings":"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","names":[]}
|