@stacksjs/bun-router 0.0.13 → 0.0.14

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 (273) hide show
  1. package/CHANGELOG.md +770 -0
  2. package/README.md +584 -261
  3. package/package.json +43 -47
  4. package/bin/cli.ts +0 -9
  5. package/dist/auth.d.ts +0 -134
  6. package/dist/cache/lru-cache.d.ts +0 -139
  7. package/dist/cache/middleware-memoization.d.ts +0 -151
  8. package/dist/cache/route-cache-warmer.d.ts +0 -161
  9. package/dist/cache/sqlite-cache.d.ts +0 -209
  10. package/dist/cache/streaming-cache.d.ts +0 -132
  11. package/dist/chunk-1mvtv6jw.js +0 -13012
  12. package/dist/cli/colors.d.ts +0 -15
  13. package/dist/cli/index.d.ts +0 -10
  14. package/dist/cli/middleware.d.ts +0 -31
  15. package/dist/cli/openapi.d.ts +0 -17
  16. package/dist/cli/router.d.ts +0 -15
  17. package/dist/cli/routes.d.ts +0 -29
  18. package/dist/cli/utils.d.ts +0 -74
  19. package/dist/cli.d.ts +0 -1
  20. package/dist/cli.js +0 -3195
  21. package/dist/config.d.ts +0 -7
  22. package/dist/container/container.d.ts +0 -273
  23. package/dist/container/contextual-binding.d.ts +0 -240
  24. package/dist/container/decorators.d.ts +0 -141
  25. package/dist/container/service-provider.d.ts +0 -285
  26. package/dist/development/hot-reload.d.ts +0 -187
  27. package/dist/development/index.d.ts +0 -167
  28. package/dist/development/performance-profiler.d.ts +0 -217
  29. package/dist/development/route-debugger.d.ts +0 -154
  30. package/dist/development/route-inspector.d.ts +0 -211
  31. package/dist/development/typescript-utilities.d.ts +0 -209
  32. package/dist/docs.d.ts +0 -11
  33. package/dist/errors/circuit-breaker.d.ts +0 -195
  34. package/dist/errors/error-handler.d.ts +0 -88
  35. package/dist/errors/error-reporting.d.ts +0 -144
  36. package/dist/errors/exceptions.d.ts +0 -184
  37. package/dist/errors/graceful-degradation.d.ts +0 -154
  38. package/dist/errors/index.d.ts +0 -9
  39. package/dist/errors/router-errors.d.ts +0 -110
  40. package/dist/file-serving/static-files.d.ts +0 -143
  41. package/dist/index.d.ts +0 -14
  42. package/dist/index.js +0 -13933
  43. package/dist/middleware/auth.d.ts +0 -63
  44. package/dist/middleware/content_security_policy.d.ts +0 -53
  45. package/dist/middleware/cors.d.ts +0 -5
  46. package/dist/middleware/csrf.d.ts +0 -7
  47. package/dist/middleware/ddos_protection.d.ts +0 -39
  48. package/dist/middleware/file_security.d.ts +0 -26
  49. package/dist/middleware/file_upload.d.ts +0 -38
  50. package/dist/middleware/helmet.d.ts +0 -54
  51. package/dist/middleware/index.d.ts +0 -44
  52. package/dist/middleware/input_validation.d.ts +0 -45
  53. package/dist/middleware/json_body.d.ts +0 -4
  54. package/dist/middleware/performance_alerting.d.ts +0 -87
  55. package/dist/middleware/performance_dashboard.d.ts +0 -87
  56. package/dist/middleware/performance_monitor.d.ts +0 -209
  57. package/dist/middleware/pipeline.d.ts +0 -131
  58. package/dist/middleware/rate_limit.d.ts +0 -36
  59. package/dist/middleware/request_id.d.ts +0 -4
  60. package/dist/middleware/request_signing.d.ts +0 -153
  61. package/dist/middleware/request_tracer.d.ts +0 -72
  62. package/dist/middleware/response_cache.d.ts +0 -97
  63. package/dist/middleware/security.d.ts +0 -69
  64. package/dist/middleware/security_suite.d.ts +0 -46
  65. package/dist/middleware/session.d.ts +0 -23
  66. package/dist/model-binding/index.d.ts +0 -2
  67. package/dist/model-binding/model-middleware.d.ts +0 -118
  68. package/dist/model-binding/model-registry.d.ts +0 -164
  69. package/dist/model-binding.d.ts +0 -185
  70. package/dist/model-resolver-factory.d.ts +0 -31
  71. package/dist/observability/correlation.d.ts +0 -187
  72. package/dist/observability/health-checks.d.ts +0 -186
  73. package/dist/observability/index.d.ts +0 -60
  74. package/dist/observability/integration.d.ts +0 -147
  75. package/dist/observability/metrics.d.ts +0 -184
  76. package/dist/observability/tracing.d.ts +0 -187
  77. package/dist/optimization/bun-utilities.d.ts +0 -224
  78. package/dist/query-builder-integration.d.ts +0 -36
  79. package/dist/request/context.d.ts +0 -34
  80. package/dist/request/enhanced-request.d.ts +0 -213
  81. package/dist/request/macros.d.ts +0 -300
  82. package/dist/response/macros.d.ts +0 -245
  83. package/dist/response/response-factory.d.ts +0 -133
  84. package/dist/router/api-routes.d.ts +0 -5
  85. package/dist/router/file-based-routing.d.ts +0 -118
  86. package/dist/router/file-streaming.d.ts +0 -5
  87. package/dist/router/fluent-router.d.ts +0 -315
  88. package/dist/router/fluent-routing.d.ts +0 -271
  89. package/dist/router/group-organization.d.ts +0 -5
  90. package/dist/router/handler-resolver.d.ts +0 -24
  91. package/dist/router/http-methods.d.ts +0 -10
  92. package/dist/router/index.d.ts +0 -114
  93. package/dist/router/middleware-groups.d.ts +0 -94
  94. package/dist/router/middleware-integration.d.ts +0 -112
  95. package/dist/router/middleware.d.ts +0 -5
  96. package/dist/router/model-binding.d.ts +0 -5
  97. package/dist/router/optimized-route-matching.d.ts +0 -6
  98. package/dist/router/route-building.d.ts +0 -5
  99. package/dist/router/route-compiler.d.ts +0 -153
  100. package/dist/router/route-matching.d.ts +0 -5
  101. package/dist/router/route-trie.d.ts +0 -114
  102. package/dist/router/router.d.ts +0 -294
  103. package/dist/router/server.d.ts +0 -5
  104. package/dist/router/validation-integration.d.ts +0 -170
  105. package/dist/router/view-rendering.d.ts +0 -5
  106. package/dist/router/websocket.d.ts +0 -5
  107. package/dist/routing/route-caching.d.ts +0 -129
  108. package/dist/routing/route-throttling.d.ts +0 -150
  109. package/dist/routing/subdomain-routing.d.ts +0 -206
  110. package/dist/session/database-store.d.ts +0 -51
  111. package/dist/session/file-store.d.ts +0 -22
  112. package/dist/session/index.d.ts +0 -106
  113. package/dist/session/memory-store.d.ts +0 -21
  114. package/dist/session/redis-store.d.ts +0 -31
  115. package/dist/streaming/index.d.ts +0 -3
  116. package/dist/streaming/sse-handler.d.ts +0 -138
  117. package/dist/streaming/stream-handler.d.ts +0 -114
  118. package/dist/testing/auth-testing.d.ts +0 -156
  119. package/dist/testing/file-upload-testing.d.ts +0 -190
  120. package/dist/testing/index.d.ts +0 -10
  121. package/dist/testing/middleware-testing.d.ts +0 -138
  122. package/dist/testing/model-binding-testing.d.ts +0 -186
  123. package/dist/testing/performance-testing.d.ts +0 -235
  124. package/dist/testing/test-client.d.ts +0 -117
  125. package/dist/testing/test-request.d.ts +0 -85
  126. package/dist/testing/test-response.d.ts +0 -90
  127. package/dist/testing/types.d.ts +0 -207
  128. package/dist/testing/websocket-testing.d.ts +0 -227
  129. package/dist/types/controller-types.d.ts +0 -208
  130. package/dist/types/core.d.ts +0 -543
  131. package/dist/types/middleware-types.d.ts +0 -227
  132. package/dist/types/request-response-augmentation.d.ts +0 -261
  133. package/dist/types/route-inference.d.ts +0 -168
  134. package/dist/types.d.ts +0 -1782
  135. package/dist/url.d.ts +0 -56
  136. package/dist/utils/index.d.ts +0 -1
  137. package/dist/utils/query-preservation.d.ts +0 -48
  138. package/dist/utils.d.ts +0 -69
  139. package/dist/validation/validator.d.ts +0 -140
  140. package/dist/websocket/clustering.d.ts +0 -185
  141. package/src/auth.ts +0 -469
  142. package/src/cache/lru-cache.ts +0 -457
  143. package/src/cache/middleware-memoization.ts +0 -531
  144. package/src/cache/route-cache-warmer.ts +0 -486
  145. package/src/cache/sqlite-cache.ts +0 -783
  146. package/src/cache/streaming-cache.ts +0 -572
  147. package/src/cli/colors.ts +0 -29
  148. package/src/cli/index.ts +0 -287
  149. package/src/cli/middleware.ts +0 -291
  150. package/src/cli/openapi.ts +0 -407
  151. package/src/cli/router.ts +0 -188
  152. package/src/cli/routes.ts +0 -265
  153. package/src/cli/utils.ts +0 -531
  154. package/src/cli.ts +0 -5
  155. package/src/config.ts +0 -322
  156. package/src/container/container.ts +0 -740
  157. package/src/container/contextual-binding.ts +0 -603
  158. package/src/container/decorators.ts +0 -359
  159. package/src/container/service-provider.ts +0 -596
  160. package/src/development/hot-reload.ts +0 -673
  161. package/src/development/index.ts +0 -499
  162. package/src/development/performance-profiler.ts +0 -717
  163. package/src/development/route-debugger.ts +0 -527
  164. package/src/development/route-inspector.ts +0 -749
  165. package/src/development/typescript-utilities.ts +0 -682
  166. package/src/docs.ts +0 -397
  167. package/src/errors/circuit-breaker.ts +0 -732
  168. package/src/errors/error-handler.ts +0 -569
  169. package/src/errors/error-reporting.ts +0 -672
  170. package/src/errors/exceptions.ts +0 -536
  171. package/src/errors/graceful-degradation.ts +0 -621
  172. package/src/errors/index.ts +0 -21
  173. package/src/errors/router-errors.ts +0 -632
  174. package/src/file-serving/static-files.ts +0 -581
  175. package/src/index.ts +0 -14
  176. package/src/middleware/auth.ts +0 -219
  177. package/src/middleware/content_security_policy.ts +0 -215
  178. package/src/middleware/cors.ts +0 -74
  179. package/src/middleware/csrf.ts +0 -108
  180. package/src/middleware/ddos_protection.ts +0 -255
  181. package/src/middleware/file_security.ts +0 -191
  182. package/src/middleware/file_upload.ts +0 -264
  183. package/src/middleware/helmet.ts +0 -268
  184. package/src/middleware/index.ts +0 -117
  185. package/src/middleware/input_validation.ts +0 -449
  186. package/src/middleware/json_body.ts +0 -37
  187. package/src/middleware/performance_alerting.ts +0 -538
  188. package/src/middleware/performance_dashboard.ts +0 -661
  189. package/src/middleware/performance_monitor.ts +0 -943
  190. package/src/middleware/pipeline.ts +0 -489
  191. package/src/middleware/rate_limit.ts +0 -245
  192. package/src/middleware/request_id.ts +0 -36
  193. package/src/middleware/request_signing.ts +0 -636
  194. package/src/middleware/request_tracer.ts +0 -636
  195. package/src/middleware/response_cache.ts +0 -743
  196. package/src/middleware/security.ts +0 -482
  197. package/src/middleware/security_suite.ts +0 -257
  198. package/src/middleware/session.ts +0 -91
  199. package/src/model-binding/index.ts +0 -18
  200. package/src/model-binding/model-middleware.ts +0 -425
  201. package/src/model-binding/model-registry.ts +0 -550
  202. package/src/model-binding.ts +0 -370
  203. package/src/model-resolver-factory.ts +0 -106
  204. package/src/observability/correlation.ts +0 -691
  205. package/src/observability/health-checks.ts +0 -729
  206. package/src/observability/index.ts +0 -184
  207. package/src/observability/integration.ts +0 -548
  208. package/src/observability/metrics.ts +0 -753
  209. package/src/observability/tracing.ts +0 -638
  210. package/src/optimization/bun-utilities.ts +0 -778
  211. package/src/query-builder-integration.ts +0 -137
  212. package/src/request/context.ts +0 -62
  213. package/src/request/enhanced-request.ts +0 -857
  214. package/src/request/macros.ts +0 -688
  215. package/src/response/macros.ts +0 -612
  216. package/src/response/response-factory.ts +0 -596
  217. package/src/router/api-routes.ts +0 -243
  218. package/src/router/file-based-routing.ts +0 -670
  219. package/src/router/file-streaming.ts +0 -372
  220. package/src/router/fluent-router.ts +0 -927
  221. package/src/router/fluent-routing.ts +0 -797
  222. package/src/router/group-organization.ts +0 -213
  223. package/src/router/handler-resolver.ts +0 -291
  224. package/src/router/http-methods.ts +0 -377
  225. package/src/router/index.ts +0 -187
  226. package/src/router/middleware-groups.ts +0 -222
  227. package/src/router/middleware-integration.ts +0 -399
  228. package/src/router/middleware.ts +0 -231
  229. package/src/router/model-binding.ts +0 -215
  230. package/src/router/optimized-route-matching.ts +0 -253
  231. package/src/router/route-building.ts +0 -212
  232. package/src/router/route-compiler.ts +0 -702
  233. package/src/router/route-matching.ts +0 -349
  234. package/src/router/route-trie.ts +0 -464
  235. package/src/router/router.ts +0 -1581
  236. package/src/router/server.ts +0 -462
  237. package/src/router/validation-integration.ts +0 -443
  238. package/src/router/view-rendering.ts +0 -233
  239. package/src/router/websocket.ts +0 -100
  240. package/src/routing/route-caching.ts +0 -402
  241. package/src/routing/route-throttling.ts +0 -469
  242. package/src/routing/subdomain-routing.ts +0 -492
  243. package/src/session/database-store.ts +0 -109
  244. package/src/session/file-store.ts +0 -148
  245. package/src/session/index.ts +0 -244
  246. package/src/session/memory-store.ts +0 -88
  247. package/src/session/redis-store.ts +0 -93
  248. package/src/streaming/index.ts +0 -17
  249. package/src/streaming/sse-handler.ts +0 -482
  250. package/src/streaming/stream-handler.ts +0 -552
  251. package/src/testing/auth-testing.ts +0 -446
  252. package/src/testing/file-upload-testing.ts +0 -543
  253. package/src/testing/index.ts +0 -10
  254. package/src/testing/middleware-testing.ts +0 -322
  255. package/src/testing/model-binding-testing.ts +0 -645
  256. package/src/testing/performance-testing.ts +0 -738
  257. package/src/testing/test-client.ts +0 -310
  258. package/src/testing/test-request.ts +0 -315
  259. package/src/testing/test-response.ts +0 -332
  260. package/src/testing/types.ts +0 -224
  261. package/src/testing/websocket-testing.ts +0 -590
  262. package/src/types/controller-types.ts +0 -385
  263. package/src/types/core.ts +0 -683
  264. package/src/types/middleware-types.ts +0 -419
  265. package/src/types/request-response-augmentation.ts +0 -489
  266. package/src/types/route-inference.ts +0 -357
  267. package/src/types.ts +0 -2055
  268. package/src/url.ts +0 -126
  269. package/src/utils/index.ts +0 -1
  270. package/src/utils/query-preservation.ts +0 -201
  271. package/src/utils.ts +0 -327
  272. package/src/validation/validator.ts +0 -685
  273. package/src/websocket/clustering.ts +0 -762
@@ -1,762 +0,0 @@
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
- }