@stacksjs/bun-router 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/dist/auth.d.ts +134 -0
  2. package/dist/cache/lru-cache.d.ts +139 -0
  3. package/dist/cache/middleware-memoization.d.ts +151 -0
  4. package/dist/cache/route-cache-warmer.d.ts +161 -0
  5. package/dist/cache/sqlite-cache.d.ts +209 -0
  6. package/dist/cache/streaming-cache.d.ts +132 -0
  7. package/dist/cli/colors.d.ts +15 -0
  8. package/dist/cli/index.d.ts +10 -0
  9. package/dist/cli/middleware.d.ts +31 -0
  10. package/dist/cli/openapi.d.ts +17 -0
  11. package/dist/cli/router.d.ts +15 -0
  12. package/dist/cli/routes.d.ts +29 -0
  13. package/dist/cli/utils.d.ts +74 -0
  14. package/dist/cli.d.ts +1 -1
  15. package/dist/cli.js +1 -1
  16. package/dist/config.d.ts +7 -0
  17. package/dist/container/container.d.ts +273 -0
  18. package/dist/container/contextual-binding.d.ts +240 -0
  19. package/dist/container/decorators.d.ts +141 -0
  20. package/dist/container/service-provider.d.ts +285 -0
  21. package/dist/development/hot-reload.d.ts +187 -0
  22. package/dist/development/index.d.ts +167 -0
  23. package/dist/development/performance-profiler.d.ts +217 -0
  24. package/dist/development/route-debugger.d.ts +154 -0
  25. package/dist/development/route-inspector.d.ts +211 -0
  26. package/dist/development/typescript-utilities.d.ts +209 -0
  27. package/dist/docs.d.ts +11 -0
  28. package/dist/errors/circuit-breaker.d.ts +195 -0
  29. package/dist/errors/error-handler.d.ts +88 -0
  30. package/dist/errors/error-reporting.d.ts +144 -0
  31. package/dist/errors/exceptions.d.ts +184 -0
  32. package/dist/errors/graceful-degradation.d.ts +154 -0
  33. package/dist/errors/index.d.ts +9 -0
  34. package/dist/errors/router-errors.d.ts +110 -0
  35. package/dist/file-serving/static-files.d.ts +143 -0
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.js +319 -205
  38. package/dist/middleware/auth.d.ts +63 -0
  39. package/dist/middleware/content_security_policy.d.ts +53 -0
  40. package/dist/middleware/cors.d.ts +5 -0
  41. package/dist/middleware/csrf.d.ts +7 -0
  42. package/dist/middleware/ddos_protection.d.ts +39 -0
  43. package/dist/middleware/file_security.d.ts +26 -0
  44. package/dist/middleware/file_upload.d.ts +38 -0
  45. package/dist/middleware/helmet.d.ts +54 -0
  46. package/dist/middleware/index.d.ts +44 -0
  47. package/dist/middleware/input_validation.d.ts +45 -0
  48. package/dist/middleware/json_body.d.ts +4 -0
  49. package/dist/middleware/performance_alerting.d.ts +87 -0
  50. package/dist/middleware/performance_dashboard.d.ts +87 -0
  51. package/dist/middleware/performance_monitor.d.ts +209 -0
  52. package/dist/middleware/pipeline.d.ts +131 -0
  53. package/dist/middleware/rate_limit.d.ts +36 -0
  54. package/dist/middleware/request_id.d.ts +4 -0
  55. package/dist/middleware/request_signing.d.ts +153 -0
  56. package/dist/middleware/request_tracer.d.ts +72 -0
  57. package/dist/middleware/response_cache.d.ts +97 -0
  58. package/dist/middleware/security.d.ts +69 -0
  59. package/dist/middleware/security_suite.d.ts +46 -0
  60. package/dist/middleware/session.d.ts +23 -0
  61. package/dist/model-binding/index.d.ts +2 -0
  62. package/dist/model-binding/model-middleware.d.ts +118 -0
  63. package/dist/model-binding/model-registry.d.ts +164 -0
  64. package/dist/model-binding.d.ts +185 -0
  65. package/dist/model-resolver-factory.d.ts +31 -0
  66. package/dist/observability/correlation.d.ts +187 -0
  67. package/dist/observability/health-checks.d.ts +186 -0
  68. package/dist/observability/index.d.ts +60 -0
  69. package/dist/observability/integration.d.ts +147 -0
  70. package/dist/observability/metrics.d.ts +184 -0
  71. package/dist/observability/tracing.d.ts +187 -0
  72. package/dist/optimization/bun-utilities.d.ts +224 -0
  73. package/dist/query-builder-integration.d.ts +36 -0
  74. package/dist/request/context.d.ts +34 -0
  75. package/dist/request/enhanced-request.d.ts +213 -0
  76. package/dist/request/macros.d.ts +300 -0
  77. package/dist/response/macros.d.ts +245 -0
  78. package/dist/response/response-factory.d.ts +133 -0
  79. package/dist/router/api-routes.d.ts +5 -0
  80. package/dist/router/file-based-routing.d.ts +118 -0
  81. package/dist/router/file-streaming.d.ts +5 -0
  82. package/dist/router/fluent-router.d.ts +315 -0
  83. package/dist/router/fluent-routing.d.ts +271 -0
  84. package/dist/router/group-organization.d.ts +5 -0
  85. package/dist/router/handler-resolver.d.ts +24 -0
  86. package/dist/router/http-methods.d.ts +10 -0
  87. package/dist/router/index.d.ts +113 -0
  88. package/dist/router/middleware-groups.d.ts +94 -0
  89. package/dist/router/middleware-integration.d.ts +112 -0
  90. package/dist/router/middleware.d.ts +5 -0
  91. package/dist/router/model-binding.d.ts +5 -0
  92. package/dist/router/optimized-route-matching.d.ts +6 -0
  93. package/dist/router/route-building.d.ts +5 -0
  94. package/dist/router/route-compiler.d.ts +153 -0
  95. package/dist/router/route-matching.d.ts +5 -0
  96. package/dist/router/route-trie.d.ts +114 -0
  97. package/dist/router/router.d.ts +276 -0
  98. package/dist/router/server.d.ts +5 -0
  99. package/dist/router/validation-integration.d.ts +170 -0
  100. package/dist/router/view-rendering.d.ts +5 -0
  101. package/dist/router/websocket.d.ts +5 -0
  102. package/dist/routing/route-caching.d.ts +129 -0
  103. package/dist/routing/route-throttling.d.ts +150 -0
  104. package/dist/routing/subdomain-routing.d.ts +206 -0
  105. package/dist/session/database-store.d.ts +51 -0
  106. package/dist/session/file-store.d.ts +22 -0
  107. package/dist/session/index.d.ts +106 -0
  108. package/dist/session/memory-store.d.ts +21 -0
  109. package/dist/session/redis-store.d.ts +31 -0
  110. package/dist/streaming/index.d.ts +3 -0
  111. package/dist/streaming/sse-handler.d.ts +138 -0
  112. package/dist/streaming/stream-handler.d.ts +114 -0
  113. package/dist/testing/auth-testing.d.ts +156 -0
  114. package/dist/testing/file-upload-testing.d.ts +190 -0
  115. package/dist/testing/index.d.ts +10 -0
  116. package/dist/testing/middleware-testing.d.ts +138 -0
  117. package/dist/testing/model-binding-testing.d.ts +186 -0
  118. package/dist/testing/performance-testing.d.ts +235 -0
  119. package/dist/testing/test-client.d.ts +117 -0
  120. package/dist/testing/test-request.d.ts +85 -0
  121. package/dist/testing/test-response.d.ts +90 -0
  122. package/dist/testing/types.d.ts +207 -0
  123. package/dist/testing/websocket-testing.d.ts +227 -0
  124. package/dist/types/controller-types.d.ts +208 -0
  125. package/dist/types/core.d.ts +543 -0
  126. package/dist/types/middleware-types.d.ts +227 -0
  127. package/dist/types/request-response-augmentation.d.ts +261 -0
  128. package/dist/types/route-inference.d.ts +168 -0
  129. package/dist/types.d.ts +1776 -0
  130. package/dist/url.d.ts +56 -0
  131. package/dist/utils/index.d.ts +1 -0
  132. package/dist/utils/query-preservation.d.ts +48 -0
  133. package/dist/utils.d.ts +69 -0
  134. package/dist/validation/validator.d.ts +140 -0
  135. package/dist/websocket/clustering.d.ts +185 -0
  136. package/package.json +3 -2
  137. package/src/auth.ts +469 -0
  138. package/src/cache/lru-cache.ts +457 -0
  139. package/src/cache/middleware-memoization.ts +531 -0
  140. package/src/cache/route-cache-warmer.ts +486 -0
  141. package/src/cache/sqlite-cache.ts +783 -0
  142. package/src/cache/streaming-cache.ts +572 -0
  143. package/src/cli/colors.ts +29 -0
  144. package/src/cli/index.ts +287 -0
  145. package/src/cli/middleware.ts +291 -0
  146. package/src/cli/openapi.ts +407 -0
  147. package/src/cli/router.ts +188 -0
  148. package/src/cli/routes.ts +265 -0
  149. package/src/cli/utils.ts +531 -0
  150. package/src/cli.ts +5 -0
  151. package/src/config.ts +322 -0
  152. package/src/container/container.ts +740 -0
  153. package/src/container/contextual-binding.ts +603 -0
  154. package/src/container/decorators.ts +359 -0
  155. package/src/container/service-provider.ts +596 -0
  156. package/src/development/hot-reload.ts +673 -0
  157. package/src/development/index.ts +499 -0
  158. package/src/development/performance-profiler.ts +717 -0
  159. package/src/development/route-debugger.ts +527 -0
  160. package/src/development/route-inspector.ts +749 -0
  161. package/src/development/typescript-utilities.ts +678 -0
  162. package/src/docs.ts +397 -0
  163. package/src/errors/circuit-breaker.ts +732 -0
  164. package/src/errors/error-handler.ts +569 -0
  165. package/src/errors/error-reporting.ts +672 -0
  166. package/src/errors/exceptions.ts +536 -0
  167. package/src/errors/graceful-degradation.ts +621 -0
  168. package/src/errors/index.ts +21 -0
  169. package/src/errors/router-errors.ts +632 -0
  170. package/src/file-serving/static-files.ts +581 -0
  171. package/src/index.ts +14 -0
  172. package/src/middleware/auth.ts +219 -0
  173. package/src/middleware/content_security_policy.ts +215 -0
  174. package/src/middleware/cors.ts +74 -0
  175. package/src/middleware/csrf.ts +108 -0
  176. package/src/middleware/ddos_protection.ts +255 -0
  177. package/src/middleware/file_security.ts +191 -0
  178. package/src/middleware/file_upload.ts +264 -0
  179. package/src/middleware/helmet.ts +268 -0
  180. package/src/middleware/index.ts +117 -0
  181. package/src/middleware/input_validation.ts +449 -0
  182. package/src/middleware/json_body.ts +37 -0
  183. package/src/middleware/performance_alerting.ts +538 -0
  184. package/src/middleware/performance_dashboard.ts +661 -0
  185. package/src/middleware/performance_monitor.ts +943 -0
  186. package/src/middleware/pipeline.ts +489 -0
  187. package/src/middleware/rate_limit.ts +245 -0
  188. package/src/middleware/request_id.ts +36 -0
  189. package/src/middleware/request_signing.ts +636 -0
  190. package/src/middleware/request_tracer.ts +636 -0
  191. package/src/middleware/response_cache.ts +743 -0
  192. package/src/middleware/security.ts +482 -0
  193. package/src/middleware/security_suite.ts +257 -0
  194. package/src/middleware/session.ts +91 -0
  195. package/src/model-binding/index.ts +18 -0
  196. package/src/model-binding/model-middleware.ts +425 -0
  197. package/src/model-binding/model-registry.ts +550 -0
  198. package/src/model-binding.ts +370 -0
  199. package/src/model-resolver-factory.ts +106 -0
  200. package/src/observability/correlation.ts +691 -0
  201. package/src/observability/health-checks.ts +729 -0
  202. package/src/observability/index.ts +184 -0
  203. package/src/observability/integration.ts +548 -0
  204. package/src/observability/metrics.ts +753 -0
  205. package/src/observability/tracing.ts +638 -0
  206. package/src/optimization/bun-utilities.ts +778 -0
  207. package/src/query-builder-integration.ts +137 -0
  208. package/src/request/context.ts +62 -0
  209. package/src/request/enhanced-request.ts +857 -0
  210. package/src/request/macros.ts +688 -0
  211. package/src/response/macros.ts +612 -0
  212. package/src/response/response-factory.ts +588 -0
  213. package/src/router/api-routes.ts +243 -0
  214. package/src/router/file-based-routing.ts +670 -0
  215. package/src/router/file-streaming.ts +372 -0
  216. package/src/router/fluent-router.ts +927 -0
  217. package/src/router/fluent-routing.ts +797 -0
  218. package/src/router/group-organization.ts +213 -0
  219. package/src/router/handler-resolver.ts +291 -0
  220. package/src/router/http-methods.ts +377 -0
  221. package/src/router/index.ts +185 -0
  222. package/src/router/middleware-groups.ts +222 -0
  223. package/src/router/middleware-integration.ts +399 -0
  224. package/src/router/middleware.ts +231 -0
  225. package/src/router/model-binding.ts +215 -0
  226. package/src/router/optimized-route-matching.ts +253 -0
  227. package/src/router/route-building.ts +212 -0
  228. package/src/router/route-compiler.ts +702 -0
  229. package/src/router/route-matching.ts +349 -0
  230. package/src/router/route-trie.ts +464 -0
  231. package/src/router/router.ts +1508 -0
  232. package/src/router/server.ts +406 -0
  233. package/src/router/validation-integration.ts +443 -0
  234. package/src/router/view-rendering.ts +233 -0
  235. package/src/router/websocket.ts +100 -0
  236. package/src/routing/route-caching.ts +402 -0
  237. package/src/routing/route-throttling.ts +469 -0
  238. package/src/routing/subdomain-routing.ts +492 -0
  239. package/src/session/database-store.ts +109 -0
  240. package/src/session/file-store.ts +148 -0
  241. package/src/session/index.ts +244 -0
  242. package/src/session/memory-store.ts +88 -0
  243. package/src/session/redis-store.ts +93 -0
  244. package/src/streaming/index.ts +17 -0
  245. package/src/streaming/sse-handler.ts +482 -0
  246. package/src/streaming/stream-handler.ts +552 -0
  247. package/src/testing/auth-testing.ts +446 -0
  248. package/src/testing/file-upload-testing.ts +543 -0
  249. package/src/testing/index.ts +10 -0
  250. package/src/testing/middleware-testing.ts +322 -0
  251. package/src/testing/model-binding-testing.ts +645 -0
  252. package/src/testing/performance-testing.ts +738 -0
  253. package/src/testing/test-client.ts +310 -0
  254. package/src/testing/test-request.ts +315 -0
  255. package/src/testing/test-response.ts +332 -0
  256. package/src/testing/types.ts +224 -0
  257. package/src/testing/websocket-testing.ts +590 -0
  258. package/src/types/controller-types.ts +385 -0
  259. package/src/types/core.ts +683 -0
  260. package/src/types/middleware-types.ts +419 -0
  261. package/src/types/request-response-augmentation.ts +489 -0
  262. package/src/types/route-inference.ts +357 -0
  263. package/src/types.ts +2049 -0
  264. package/src/url.ts +126 -0
  265. package/src/utils/index.ts +1 -0
  266. package/src/utils/query-preservation.ts +201 -0
  267. package/src/utils.ts +327 -0
  268. package/src/validation/validator.ts +685 -0
  269. package/src/websocket/clustering.ts +762 -0
@@ -0,0 +1,762 @@
1
+ /**
2
+ * WebSocket Clustering
3
+ *
4
+ * Native WebSocket clustering across workers with high-performance optimizations
5
+ */
6
+
7
+ import type { ServerWebSocket } from 'bun'
8
+ import type { Buffer } from 'node:buffer'
9
+ import process from 'node:process'
10
+
11
+ export interface WebSocketClusterConfig {
12
+ workers?: number
13
+ maxConnections?: number
14
+ heartbeatInterval?: number
15
+ compression?: boolean
16
+ backpressureLimit?: number
17
+ closeOnBackpressureLimit?: boolean
18
+ idleTimeout?: number
19
+ maxPayloadLength?: number
20
+ }
21
+
22
+ export interface ClusterMessage {
23
+ type: 'broadcast' | 'direct' | 'join' | 'leave' | 'heartbeat' | 'stats'
24
+ workerId?: string
25
+ targetId?: string
26
+ roomId?: string
27
+ data?: any
28
+ timestamp: number
29
+ }
30
+
31
+ export interface WebSocketConnection {
32
+ id: string
33
+ workerId: string
34
+ socket: ServerWebSocket<any>
35
+ rooms: Set<string>
36
+ metadata: Record<string, any>
37
+ lastHeartbeat: number
38
+ connected: boolean
39
+ }
40
+
41
+ export interface WebSocketRoom {
42
+ id: string
43
+ connections: Set<string>
44
+ metadata: Record<string, any>
45
+ created: number
46
+ }
47
+
48
+ export interface ClusterStats {
49
+ totalConnections: number
50
+ totalRooms: number
51
+ workerStats: Record<string, {
52
+ connections: number
53
+ rooms: number
54
+ memoryUsage: number
55
+ uptime: number
56
+ }>
57
+ messagesSent: number
58
+ messagesReceived: number
59
+ bytesTransferred: number
60
+ }
61
+
62
+ /**
63
+ * WebSocket cluster manager for distributing connections across workers
64
+ */
65
+ export class WebSocketCluster {
66
+ private config: Required<WebSocketClusterConfig>
67
+ private workers = new Map<string, Worker>()
68
+ private connections = new Map<string, WebSocketConnection>()
69
+ private rooms = new Map<string, WebSocketRoom>()
70
+ private messageHandlers = new Map<string, (...args: any[]) => any>()
71
+ private stats: ClusterStats
72
+ private heartbeatTimer?: ReturnType<typeof setInterval>
73
+ private isMainWorker: boolean
74
+ private workerId: string
75
+
76
+ constructor(config: WebSocketClusterConfig = {}) {
77
+ this.config = {
78
+ workers: config.workers || Math.max(1, Math.floor(navigator.hardwareConcurrency / 2)),
79
+ maxConnections: config.maxConnections || 10000,
80
+ heartbeatInterval: config.heartbeatInterval || 30000,
81
+ compression: config.compression ?? true,
82
+ backpressureLimit: config.backpressureLimit || 64 * 1024,
83
+ closeOnBackpressureLimit: config.closeOnBackpressureLimit ?? false,
84
+ idleTimeout: config.idleTimeout || 120000,
85
+ maxPayloadLength: config.maxPayloadLength || 16 * 1024 * 1024,
86
+ ...config,
87
+ }
88
+
89
+ this.workerId = this.generateWorkerId()
90
+ this.isMainWorker = !process.env.BUN_WORKER_ID
91
+ this.stats = this.initializeStats()
92
+
93
+ this.setupWorkerCommunication()
94
+ this.startHeartbeat()
95
+ }
96
+
97
+ /**
98
+ * Create WebSocket server with clustering support
99
+ */
100
+ createServer(options: {
101
+ port?: number
102
+ hostname?: string
103
+ onConnection?: (ws: ServerWebSocket<any>, request: Request) => void
104
+ onMessage?: (ws: ServerWebSocket<any>, message: string | Buffer) => void
105
+ onClose?: (ws: ServerWebSocket<any>, code?: number, reason?: string) => void
106
+ onError?: (ws: ServerWebSocket<any>, error: Error) => void
107
+ } = {}): any {
108
+ const server = Bun.serve({
109
+ port: options.port || 3000,
110
+ hostname: options.hostname || 'localhost',
111
+
112
+ fetch: (req, server) => {
113
+ const url = new URL(req.url)
114
+
115
+ if (url.pathname === '/ws') {
116
+ const success = server.upgrade(req, {
117
+ data: {
118
+ connectionId: this.generateConnectionId(),
119
+ workerId: this.workerId,
120
+ connectedAt: Date.now(),
121
+ rooms: new Set<string>(),
122
+ metadata: {},
123
+ } as any,
124
+ })
125
+
126
+ if (!success) {
127
+ return new Response('WebSocket upgrade failed', { status: 400 })
128
+ }
129
+
130
+ return undefined
131
+ }
132
+
133
+ if (url.pathname === '/ws/stats') {
134
+ return Response.json(this.getStats())
135
+ }
136
+
137
+ return new Response('Not Found', { status: 404 })
138
+ },
139
+
140
+ websocket: {
141
+
142
+ open: (ws) => {
143
+ const connectionId = (ws.data as any).connectionId
144
+ const connection: WebSocketConnection = {
145
+ id: connectionId,
146
+ workerId: this.workerId,
147
+ socket: ws,
148
+ rooms: new Set(),
149
+ metadata: (ws.data as any).metadata || {},
150
+ lastHeartbeat: Date.now(),
151
+ connected: true,
152
+ }
153
+
154
+ this.connections.set(connectionId, connection)
155
+ this.stats.totalConnections++
156
+ this.updateWorkerStats()
157
+
158
+ // Notify other workers about new connection
159
+ this.broadcastToWorkers({
160
+ type: 'join',
161
+ workerId: this.workerId,
162
+ data: { connectionId, metadata: connection.metadata },
163
+ timestamp: Date.now(),
164
+ })
165
+
166
+ options.onConnection?.(ws, (ws.data as any).request)
167
+ },
168
+
169
+ message: (ws, message) => {
170
+ const connectionId = (ws.data as any).connectionId
171
+ const connection = this.connections.get(connectionId)
172
+
173
+ if (!connection)
174
+ return
175
+
176
+ connection.lastHeartbeat = Date.now()
177
+ this.stats.messagesReceived++
178
+ this.stats.bytesTransferred += typeof message === 'string' ? message.length : message.byteLength
179
+
180
+ // Handle internal cluster messages
181
+ if (typeof message === 'string' && message.startsWith('__cluster__')) {
182
+ this.handleClusterMessage(connection, JSON.parse(message.slice(11)))
183
+ return
184
+ }
185
+
186
+ options.onMessage?.(ws, message)
187
+ },
188
+
189
+ close: (ws, code, reason) => {
190
+ const connectionId = (ws.data as any).connectionId
191
+ const connection = this.connections.get(connectionId)
192
+
193
+ if (connection) {
194
+ connection.connected = false
195
+
196
+ // Remove from all rooms
197
+ connection.rooms.forEach((roomId) => {
198
+ this.leaveRoom(connectionId, roomId)
199
+ })
200
+
201
+ this.connections.delete(connectionId)
202
+ this.stats.totalConnections--
203
+ this.updateWorkerStats()
204
+
205
+ // Notify other workers about disconnection
206
+ this.broadcastToWorkers({
207
+ type: 'leave',
208
+ workerId: this.workerId,
209
+ data: { connectionId },
210
+ timestamp: Date.now(),
211
+ })
212
+ }
213
+
214
+ options.onClose?.(ws, code, reason)
215
+ },
216
+
217
+ },
218
+ })
219
+
220
+ console.log(`WebSocket cluster worker ${this.workerId} listening on ${options.hostname || 'localhost'}:${options.port || 3000}`)
221
+ return server
222
+ }
223
+
224
+ /**
225
+ * Join a connection to a room
226
+ */
227
+ joinRoom(connectionId: string, roomId: string, metadata: Record<string, any> = {}): boolean {
228
+ const connection = this.connections.get(connectionId)
229
+ if (!connection || !connection.connected)
230
+ return false
231
+
232
+ connection.rooms.add(roomId)
233
+
234
+ if (!this.rooms.has(roomId)) {
235
+ this.rooms.set(roomId, {
236
+ id: roomId,
237
+ connections: new Set(),
238
+ metadata,
239
+ created: Date.now(),
240
+ })
241
+ this.stats.totalRooms++
242
+ }
243
+
244
+ const room = this.rooms.get(roomId)!
245
+ room.connections.add(connectionId)
246
+
247
+ // Notify other workers
248
+ this.broadcastToWorkers({
249
+ type: 'join',
250
+ workerId: this.workerId,
251
+ roomId,
252
+ data: { connectionId, metadata },
253
+ timestamp: Date.now(),
254
+ })
255
+
256
+ return true
257
+ }
258
+
259
+ /**
260
+ * Remove a connection from a room
261
+ */
262
+ leaveRoom(connectionId: string, roomId: string): boolean {
263
+ const connection = this.connections.get(connectionId)
264
+ if (!connection)
265
+ return false
266
+
267
+ connection.rooms.delete(roomId)
268
+
269
+ const room = this.rooms.get(roomId)
270
+ if (room) {
271
+ room.connections.delete(connectionId)
272
+
273
+ if (room.connections.size === 0) {
274
+ this.rooms.delete(roomId)
275
+ this.stats.totalRooms--
276
+ }
277
+ }
278
+
279
+ // Notify other workers
280
+ this.broadcastToWorkers({
281
+ type: 'leave',
282
+ workerId: this.workerId,
283
+ roomId,
284
+ data: { connectionId },
285
+ timestamp: Date.now(),
286
+ })
287
+
288
+ return true
289
+ }
290
+
291
+ /**
292
+ * Broadcast message to all connections in a room
293
+ */
294
+ broadcastToRoom(roomId: string, message: string | Buffer, excludeConnectionId?: string): number {
295
+ const room = this.rooms.get(roomId)
296
+ if (!room)
297
+ return 0
298
+
299
+ let sentCount = 0
300
+
301
+ // Send to local connections
302
+ room.connections.forEach((connectionId) => {
303
+ if (connectionId === excludeConnectionId)
304
+ return
305
+
306
+ const connection = this.connections.get(connectionId)
307
+ if (connection && connection.connected) {
308
+ try {
309
+ connection.socket.send(message)
310
+ sentCount++
311
+ this.stats.messagesSent++
312
+ this.stats.bytesTransferred += typeof message === 'string' ? message.length : message.byteLength
313
+ }
314
+ catch (error) {
315
+ console.error('Failed to send message to connection:', error)
316
+ }
317
+ }
318
+ })
319
+
320
+ // Broadcast to other workers
321
+ this.broadcastToWorkers({
322
+ type: 'broadcast',
323
+ workerId: this.workerId,
324
+ roomId,
325
+ data: { message: message.toString(), excludeConnectionId },
326
+ timestamp: Date.now(),
327
+ })
328
+
329
+ return sentCount
330
+ }
331
+
332
+ /**
333
+ * Send message to specific connection
334
+ */
335
+ sendToConnection(connectionId: string, message: string | Buffer): boolean {
336
+ const connection = this.connections.get(connectionId)
337
+
338
+ if (connection && connection.connected) {
339
+ try {
340
+ connection.socket.send(message)
341
+ this.stats.messagesSent++
342
+ this.stats.bytesTransferred += typeof message === 'string' ? message.length : message.byteLength
343
+ return true
344
+ }
345
+ catch (error) {
346
+ console.error('Failed to send message to connection:', error)
347
+ return false
348
+ }
349
+ }
350
+
351
+ // Try to send via other workers
352
+ this.broadcastToWorkers({
353
+ type: 'direct',
354
+ workerId: this.workerId,
355
+ targetId: connectionId,
356
+ data: { message: message.toString() },
357
+ timestamp: Date.now(),
358
+ })
359
+
360
+ return false
361
+ }
362
+
363
+ /**
364
+ * Get cluster statistics
365
+ */
366
+ getStats(): ClusterStats {
367
+ this.updateWorkerStats()
368
+ return { ...this.stats }
369
+ }
370
+
371
+ /**
372
+ * Get connections in a room
373
+ */
374
+ getRoomConnections(roomId: string): string[] {
375
+ const room = this.rooms.get(roomId)
376
+ return room ? Array.from(room.connections) : []
377
+ }
378
+
379
+ /**
380
+ * Get rooms for a connection
381
+ */
382
+ getConnectionRooms(connectionId: string): string[] {
383
+ const connection = this.connections.get(connectionId)
384
+ return connection ? Array.from(connection.rooms) : []
385
+ }
386
+
387
+ /**
388
+ * Close connection
389
+ */
390
+ closeConnection(connectionId: string, code?: number, reason?: string): boolean {
391
+ const connection = this.connections.get(connectionId)
392
+ if (!connection || !connection.connected)
393
+ return false
394
+
395
+ try {
396
+ connection.socket.close(code, reason)
397
+ return true
398
+ }
399
+ catch (error) {
400
+ console.error('Failed to close connection:', error)
401
+ return false
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Shutdown cluster
407
+ */
408
+ shutdown(): void {
409
+ if (this.heartbeatTimer) {
410
+ clearInterval(this.heartbeatTimer)
411
+ }
412
+
413
+ // Close all connections
414
+ this.connections.forEach((connection) => {
415
+ if (connection.connected) {
416
+ connection.socket.close(1001, 'Server shutdown')
417
+ }
418
+ })
419
+
420
+ // Terminate workers
421
+ this.workers.forEach((worker) => {
422
+ worker.terminate()
423
+ })
424
+
425
+ this.connections.clear()
426
+ this.rooms.clear()
427
+ this.workers.clear()
428
+ }
429
+
430
+ /**
431
+ * Generate unique connection ID
432
+ */
433
+ private generateConnectionId(): string {
434
+ return `conn_${this.workerId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
435
+ }
436
+
437
+ /**
438
+ * Generate unique worker ID
439
+ */
440
+ private generateWorkerId(): string {
441
+ return process.env.BUN_WORKER_ID || `worker_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
442
+ }
443
+
444
+ /**
445
+ * Initialize statistics
446
+ */
447
+ private initializeStats(): ClusterStats {
448
+ return {
449
+ totalConnections: 0,
450
+ totalRooms: 0,
451
+ workerStats: {},
452
+ messagesSent: 0,
453
+ messagesReceived: 0,
454
+ bytesTransferred: 0,
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Setup worker communication
460
+ */
461
+ private setupWorkerCommunication(): void {
462
+ if (this.isMainWorker) {
463
+ // Main worker spawns additional workers
464
+ for (let i = 1; i < this.config.workers; i++) {
465
+ const worker = new Worker(new URL(import.meta.url), {
466
+ env: { ...process.env, BUN_WORKER_ID: `worker_${i}` },
467
+ })
468
+
469
+ worker.onmessage = (event) => {
470
+ this.handleWorkerMessage(event.data)
471
+ }
472
+
473
+ this.workers.set(`worker_${i}`, worker)
474
+ }
475
+ }
476
+ else {
477
+ // Worker listens for messages from main worker
478
+ process.on('message', (message) => {
479
+ this.handleWorkerMessage(message as ClusterMessage)
480
+ })
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Handle messages from other workers
486
+ */
487
+ private handleWorkerMessage(message: ClusterMessage): void {
488
+ switch (message.type) {
489
+ case 'broadcast':
490
+ if (message.roomId && message.data?.message) {
491
+ const room = this.rooms.get(message.roomId)
492
+ if (room) {
493
+ room.connections.forEach((connectionId) => {
494
+ if (connectionId === message.data.excludeConnectionId)
495
+ return
496
+
497
+ const connection = this.connections.get(connectionId)
498
+ if (connection && connection.connected) {
499
+ connection.socket.send(message.data.message)
500
+ }
501
+ })
502
+ }
503
+ }
504
+ break
505
+
506
+ case 'direct':
507
+ if (message.targetId && message.data?.message) {
508
+ const connection = this.connections.get(message.targetId)
509
+ if (connection && connection.connected) {
510
+ connection.socket.send(message.data.message)
511
+ }
512
+ }
513
+ break
514
+
515
+ case 'join':
516
+ if (message.roomId && message.data?.connectionId) {
517
+ // Update room information from other workers
518
+ if (!this.rooms.has(message.roomId)) {
519
+ this.rooms.set(message.roomId, {
520
+ id: message.roomId,
521
+ connections: new Set(),
522
+ metadata: message.data.metadata || {},
523
+ created: Date.now(),
524
+ })
525
+ }
526
+ }
527
+ break
528
+
529
+ case 'leave':
530
+ if (message.roomId && message.data?.connectionId) {
531
+ const room = this.rooms.get(message.roomId)
532
+ if (room) {
533
+ room.connections.delete(message.data.connectionId)
534
+ if (room.connections.size === 0) {
535
+ this.rooms.delete(message.roomId)
536
+ }
537
+ }
538
+ }
539
+ break
540
+
541
+ case 'stats':
542
+ // Handle stats updates from other workers
543
+ if (message.data && message.workerId) {
544
+ this.stats.workerStats[message.workerId] = message.data
545
+ }
546
+ break
547
+ }
548
+ }
549
+
550
+ /**
551
+ * Handle cluster messages from connections
552
+ */
553
+ private handleClusterMessage(connection: WebSocketConnection, message: ClusterMessage): void {
554
+ switch (message.type) {
555
+ case 'join':
556
+ if (message.roomId) {
557
+ this.joinRoom(connection.id, message.roomId, message.data || {})
558
+ }
559
+ break
560
+
561
+ case 'leave':
562
+ if (message.roomId) {
563
+ this.leaveRoom(connection.id, message.roomId)
564
+ }
565
+ break
566
+
567
+ case 'broadcast':
568
+ if (message.roomId && message.data?.message) {
569
+ this.broadcastToRoom(message.roomId, message.data.message, connection.id)
570
+ }
571
+ break
572
+
573
+ case 'heartbeat':
574
+ connection.lastHeartbeat = Date.now()
575
+ connection.socket.send(`__cluster__${JSON.stringify({
576
+ type: 'heartbeat',
577
+ timestamp: Date.now(),
578
+ })}`)
579
+ break
580
+ }
581
+ }
582
+
583
+ /**
584
+ * Broadcast message to all workers
585
+ */
586
+ private broadcastToWorkers(message: ClusterMessage): void {
587
+ if (this.isMainWorker) {
588
+ this.workers.forEach((worker) => {
589
+ worker.postMessage(message)
590
+ })
591
+ }
592
+ else {
593
+ process.send?.(message)
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Start heartbeat timer
599
+ */
600
+ private startHeartbeat(): void {
601
+ this.heartbeatTimer = setInterval(() => {
602
+ const now = Date.now()
603
+ const timeout = this.config.heartbeatInterval * 2
604
+
605
+ // Check for stale connections
606
+ this.connections.forEach((connection, connectionId) => {
607
+ if (now - connection.lastHeartbeat > timeout) {
608
+ console.log(`Closing stale connection: ${connectionId}`)
609
+ this.closeConnection(connectionId, 1001, 'Connection timeout')
610
+ }
611
+ })
612
+
613
+ // Send stats to other workers
614
+ this.broadcastToWorkers({
615
+ type: 'stats',
616
+ workerId: this.workerId,
617
+ data: {
618
+ connections: this.connections.size,
619
+ rooms: this.rooms.size,
620
+ memoryUsage: process.memoryUsage().heapUsed,
621
+ uptime: process.uptime(),
622
+ },
623
+ timestamp: now,
624
+ })
625
+ }, this.config.heartbeatInterval)
626
+ }
627
+
628
+ /**
629
+ * Update worker statistics
630
+ */
631
+ private updateWorkerStats(): void {
632
+ this.stats.workerStats[this.workerId] = {
633
+ connections: this.connections.size,
634
+ rooms: this.rooms.size,
635
+ memoryUsage: process.memoryUsage().heapUsed,
636
+ uptime: process.uptime(),
637
+ }
638
+ }
639
+ }
640
+
641
+ /**
642
+ * WebSocket cluster factory
643
+ */
644
+ export class WebSocketClusterFactory {
645
+ /**
646
+ * Create WebSocket cluster with default configuration
647
+ */
648
+ static create(config?: WebSocketClusterConfig): WebSocketCluster {
649
+ return new WebSocketCluster(config)
650
+ }
651
+
652
+ /**
653
+ * Create high-performance cluster for production
654
+ */
655
+ static createHighPerformance(): WebSocketCluster {
656
+ return new WebSocketCluster({
657
+ workers: Math.max(2, navigator.hardwareConcurrency),
658
+ maxConnections: 50000,
659
+ heartbeatInterval: 60000,
660
+ compression: true,
661
+ backpressureLimit: 128 * 1024,
662
+ closeOnBackpressureLimit: true,
663
+ idleTimeout: 300000,
664
+ maxPayloadLength: 32 * 1024 * 1024,
665
+ })
666
+ }
667
+
668
+ /**
669
+ * Create development cluster with debugging
670
+ */
671
+ static createDevelopment(): WebSocketCluster {
672
+ return new WebSocketCluster({
673
+ workers: 1,
674
+ maxConnections: 1000,
675
+ heartbeatInterval: 10000,
676
+ compression: false,
677
+ backpressureLimit: 16 * 1024,
678
+ closeOnBackpressureLimit: false,
679
+ idleTimeout: 60000,
680
+ maxPayloadLength: 1024 * 1024,
681
+ })
682
+ }
683
+ }
684
+
685
+ /**
686
+ * WebSocket cluster helpers
687
+ */
688
+ export const WebSocketClusterHelpers = {
689
+ /**
690
+ * Create room-based chat system
691
+ */
692
+ createChatSystem: (cluster: WebSocketCluster): any => ({
693
+ joinRoom: (connectionId: string, roomId: string, userInfo: any) => {
694
+ return cluster.joinRoom(connectionId, roomId, { userInfo, joinedAt: Date.now() })
695
+ },
696
+
697
+ leaveRoom: (connectionId: string, roomId: string) => {
698
+ return cluster.leaveRoom(connectionId, roomId)
699
+ },
700
+
701
+ sendMessage: (roomId: string, message: any, fromConnectionId?: string) => {
702
+ const chatMessage = JSON.stringify({
703
+ type: 'chat_message',
704
+ roomId,
705
+ message,
706
+ timestamp: Date.now(),
707
+ from: fromConnectionId,
708
+ })
709
+ return cluster.broadcastToRoom(roomId, chatMessage, fromConnectionId)
710
+ },
711
+
712
+ getRoomUsers: (roomId: string) => {
713
+ return cluster.getRoomConnections(roomId)
714
+ },
715
+ }),
716
+
717
+ /**
718
+ * Create real-time notifications system
719
+ */
720
+ createNotificationSystem: (cluster: WebSocketCluster): any => ({
721
+ subscribe: (connectionId: string, topics: string[]) => {
722
+ topics.forEach((topic) => {
723
+ cluster.joinRoom(connectionId, `notification:${topic}`)
724
+ })
725
+ },
726
+
727
+ unsubscribe: (connectionId: string, topics: string[]) => {
728
+ topics.forEach((topic) => {
729
+ cluster.leaveRoom(connectionId, `notification:${topic}`)
730
+ })
731
+ },
732
+
733
+ notify: (topic: string, notification: any) => {
734
+ const message = JSON.stringify({
735
+ type: 'notification',
736
+ topic,
737
+ data: notification,
738
+ timestamp: Date.now(),
739
+ })
740
+ return cluster.broadcastToRoom(`notification:${topic}`, message)
741
+ },
742
+ }),
743
+
744
+ /**
745
+ * Create presence system
746
+ */
747
+ createPresenceSystem: (cluster: WebSocketCluster): any => ({
748
+ setPresence: (connectionId: string, status: 'online' | 'away' | 'busy' | 'offline', metadata?: any) => {
749
+ cluster.broadcastToRoom('presence', JSON.stringify({
750
+ type: 'presence_update',
751
+ connectionId,
752
+ status,
753
+ metadata,
754
+ timestamp: Date.now(),
755
+ }), connectionId)
756
+ },
757
+
758
+ getOnlineUsers: () => {
759
+ return cluster.getRoomConnections('presence')
760
+ },
761
+ }),
762
+ }