create-fluxstack 1.0.13 → 1.0.15

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 (214) hide show
  1. package/.env.example +29 -29
  2. package/app/client/README.md +69 -69
  3. package/app/client/index.html +14 -13
  4. package/app/client/src/App.tsx +157 -524
  5. package/app/client/src/components/ErrorBoundary.tsx +107 -0
  6. package/app/client/src/components/ErrorDisplay.css +365 -0
  7. package/app/client/src/components/ErrorDisplay.tsx +258 -0
  8. package/app/client/src/components/FluxStackConfig.tsx +1321 -0
  9. package/app/client/src/components/HybridLiveCounter.tsx +140 -0
  10. package/app/client/src/components/LiveClock.tsx +286 -0
  11. package/app/client/src/components/MainLayout.tsx +390 -0
  12. package/app/client/src/components/SidebarNavigation.tsx +391 -0
  13. package/app/client/src/components/StateDemo.tsx +178 -0
  14. package/app/client/src/components/SystemMonitor.tsx +1038 -0
  15. package/app/client/src/components/Teste.tsx +104 -0
  16. package/app/client/src/components/UserProfile.tsx +809 -0
  17. package/app/client/src/hooks/useAuth.ts +39 -0
  18. package/app/client/src/hooks/useNotifications.ts +56 -0
  19. package/app/client/src/lib/eden-api.ts +189 -53
  20. package/app/client/src/lib/errors.ts +340 -0
  21. package/app/client/src/lib/hooks/useErrorHandler.ts +258 -0
  22. package/app/client/src/lib/index.ts +45 -0
  23. package/app/client/src/main.tsx +3 -2
  24. package/app/client/src/pages/ApiDocs.tsx +182 -0
  25. package/app/client/src/pages/Demo.tsx +174 -0
  26. package/app/client/src/pages/HybridLive.tsx +263 -0
  27. package/app/client/src/pages/Overview.tsx +155 -0
  28. package/app/client/src/store/README.md +43 -0
  29. package/app/client/src/store/index.ts +16 -0
  30. package/app/client/src/store/slices/uiSlice.ts +151 -0
  31. package/app/client/src/store/slices/userSlice.ts +161 -0
  32. package/app/client/src/test/README.md +257 -0
  33. package/app/client/src/test/setup.ts +70 -0
  34. package/app/client/src/test/types.ts +12 -0
  35. package/app/client/src/vite-env.d.ts +1 -1
  36. package/app/client/tsconfig.app.json +44 -43
  37. package/app/client/tsconfig.json +7 -7
  38. package/app/client/tsconfig.node.json +25 -25
  39. package/app/client/zustand-setup.md +65 -0
  40. package/app/server/controllers/users.controller.ts +68 -68
  41. package/app/server/index.ts +9 -1
  42. package/app/server/live/CounterComponent.ts +191 -0
  43. package/app/server/live/FluxStackConfig.ts +529 -0
  44. package/app/server/live/LiveClockComponent.ts +214 -0
  45. package/app/server/live/SidebarNavigation.ts +156 -0
  46. package/app/server/live/SystemMonitor.ts +594 -0
  47. package/app/server/live/SystemMonitorIntegration.ts +151 -0
  48. package/app/server/live/TesteComponent.ts +87 -0
  49. package/app/server/live/UserProfileComponent.ts +135 -0
  50. package/app/server/live/register-components.ts +28 -0
  51. package/app/server/middleware/auth.ts +136 -0
  52. package/app/server/middleware/errorHandling.ts +250 -0
  53. package/app/server/middleware/index.ts +10 -0
  54. package/app/server/middleware/rateLimit.ts +193 -0
  55. package/app/server/middleware/requestLogging.ts +215 -0
  56. package/app/server/middleware/validation.ts +270 -0
  57. package/app/server/routes/index.ts +14 -2
  58. package/app/server/routes/upload.ts +92 -0
  59. package/app/server/routes/users.routes.ts +2 -9
  60. package/app/server/services/NotificationService.ts +302 -0
  61. package/app/server/services/UserService.ts +222 -0
  62. package/app/server/services/index.ts +46 -0
  63. package/core/cli/commands/plugin-deps.ts +263 -0
  64. package/core/cli/generators/README.md +339 -0
  65. package/core/cli/generators/component.ts +770 -0
  66. package/core/cli/generators/controller.ts +299 -0
  67. package/core/cli/generators/index.ts +144 -0
  68. package/core/cli/generators/interactive.ts +228 -0
  69. package/core/cli/generators/prompts.ts +83 -0
  70. package/core/cli/generators/route.ts +513 -0
  71. package/core/cli/generators/service.ts +465 -0
  72. package/core/cli/generators/template-engine.ts +154 -0
  73. package/core/cli/generators/types.ts +71 -0
  74. package/core/cli/generators/utils.ts +192 -0
  75. package/core/cli/index.ts +69 -0
  76. package/core/cli/plugin-discovery.ts +16 -85
  77. package/core/client/fluxstack.ts +17 -0
  78. package/core/client/hooks/index.ts +7 -0
  79. package/core/client/hooks/state-validator.ts +130 -0
  80. package/core/client/hooks/useAuth.ts +49 -0
  81. package/core/client/hooks/useChunkedUpload.ts +258 -0
  82. package/core/client/hooks/useHybridLiveComponent.ts +967 -0
  83. package/core/client/hooks/useWebSocket.ts +373 -0
  84. package/core/client/index.ts +47 -0
  85. package/core/client/state/createStore.ts +193 -0
  86. package/core/client/state/index.ts +15 -0
  87. package/core/config/env-dynamic.ts +1 -1
  88. package/core/config/env.ts +2 -1
  89. package/core/config/runtime-config.ts +3 -3
  90. package/core/config/schema.ts +84 -49
  91. package/core/framework/server.ts +30 -0
  92. package/core/index.ts +25 -0
  93. package/core/live/ComponentRegistry.ts +399 -0
  94. package/core/live/types.ts +164 -0
  95. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1201 -0
  96. package/core/plugins/built-in/live-components/index.ts +27 -0
  97. package/core/plugins/built-in/logger/index.ts +1 -1
  98. package/core/plugins/built-in/monitoring/index.ts +1 -1
  99. package/core/plugins/built-in/static/index.ts +1 -1
  100. package/core/plugins/built-in/swagger/index.ts +1 -1
  101. package/core/plugins/built-in/vite/index.ts +1 -1
  102. package/core/plugins/dependency-manager.ts +384 -0
  103. package/core/plugins/index.ts +5 -1
  104. package/core/plugins/manager.ts +7 -3
  105. package/core/plugins/registry.ts +88 -10
  106. package/core/plugins/types.ts +11 -11
  107. package/core/server/framework.ts +43 -0
  108. package/core/server/index.ts +11 -1
  109. package/core/server/live/ComponentRegistry.ts +1017 -0
  110. package/core/server/live/FileUploadManager.ts +272 -0
  111. package/core/server/live/LiveComponentPerformanceMonitor.ts +930 -0
  112. package/core/server/live/SingleConnectionManager.ts +0 -0
  113. package/core/server/live/StateSignature.ts +644 -0
  114. package/core/server/live/WebSocketConnectionManager.ts +688 -0
  115. package/core/server/live/websocket-plugin.ts +435 -0
  116. package/core/server/middleware/errorHandling.ts +141 -0
  117. package/core/server/middleware/index.ts +16 -0
  118. package/core/server/plugins/static-files-plugin.ts +232 -0
  119. package/core/server/services/BaseService.ts +95 -0
  120. package/core/server/services/ServiceContainer.ts +144 -0
  121. package/core/server/services/index.ts +9 -0
  122. package/core/templates/create-project.ts +196 -33
  123. package/core/testing/index.ts +10 -0
  124. package/core/testing/setup.ts +74 -0
  125. package/core/types/build.ts +38 -14
  126. package/core/types/types.ts +319 -0
  127. package/core/utils/env-runtime.ts +7 -0
  128. package/core/utils/errors/handlers.ts +264 -39
  129. package/core/utils/errors/index.ts +528 -18
  130. package/core/utils/errors/middleware.ts +114 -0
  131. package/core/utils/logger/formatters.ts +222 -0
  132. package/core/utils/logger/index.ts +167 -48
  133. package/core/utils/logger/middleware.ts +253 -0
  134. package/core/utils/logger/performance.ts +384 -0
  135. package/core/utils/logger/transports.ts +365 -0
  136. package/create-fluxstack.ts +296 -296
  137. package/fluxstack.config.ts +17 -1
  138. package/package-template.json +66 -66
  139. package/package.json +31 -6
  140. package/public/README.md +16 -0
  141. package/vite.config.ts +29 -14
  142. package/.claude/settings.local.json +0 -74
  143. package/.github/workflows/ci-build-tests.yml +0 -480
  144. package/.github/workflows/dependency-management.yml +0 -324
  145. package/.github/workflows/release-validation.yml +0 -355
  146. package/.kiro/specs/fluxstack-architecture-optimization/design.md +0 -700
  147. package/.kiro/specs/fluxstack-architecture-optimization/requirements.md +0 -127
  148. package/.kiro/specs/fluxstack-architecture-optimization/tasks.md +0 -330
  149. package/CLAUDE.md +0 -200
  150. package/Dockerfile +0 -58
  151. package/Dockerfile.backend +0 -52
  152. package/Dockerfile.frontend +0 -54
  153. package/README-Docker.md +0 -85
  154. package/ai-context/00-QUICK-START.md +0 -86
  155. package/ai-context/README.md +0 -88
  156. package/ai-context/development/eden-treaty-guide.md +0 -362
  157. package/ai-context/development/patterns.md +0 -382
  158. package/ai-context/development/plugins-guide.md +0 -572
  159. package/ai-context/examples/crud-complete.md +0 -626
  160. package/ai-context/project/architecture.md +0 -399
  161. package/ai-context/project/overview.md +0 -213
  162. package/ai-context/recent-changes/eden-treaty-refactor.md +0 -281
  163. package/ai-context/recent-changes/type-inference-fix.md +0 -223
  164. package/ai-context/reference/environment-vars.md +0 -384
  165. package/ai-context/reference/troubleshooting.md +0 -407
  166. package/app/client/src/components/TestPage.tsx +0 -453
  167. package/bun.lock +0 -1063
  168. package/bunfig.toml +0 -16
  169. package/core/__tests__/integration.test.ts +0 -227
  170. package/core/build/index.ts +0 -186
  171. package/core/config/__tests__/config-loader.test.ts +0 -554
  172. package/core/config/__tests__/config-merger.test.ts +0 -657
  173. package/core/config/__tests__/env-converter.test.ts +0 -372
  174. package/core/config/__tests__/env-processor.test.ts +0 -431
  175. package/core/config/__tests__/env.test.ts +0 -452
  176. package/core/config/__tests__/integration.test.ts +0 -418
  177. package/core/config/__tests__/loader.test.ts +0 -331
  178. package/core/config/__tests__/schema.test.ts +0 -129
  179. package/core/config/__tests__/validator.test.ts +0 -318
  180. package/core/framework/__tests__/server.test.ts +0 -233
  181. package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
  182. package/core/plugins/__tests__/manager.test.ts +0 -398
  183. package/core/plugins/__tests__/monitoring.test.ts +0 -401
  184. package/core/plugins/__tests__/registry.test.ts +0 -335
  185. package/core/utils/__tests__/errors.test.ts +0 -139
  186. package/core/utils/__tests__/helpers.test.ts +0 -297
  187. package/core/utils/__tests__/logger.test.ts +0 -141
  188. package/create-test-app.ts +0 -156
  189. package/docker-compose.microservices.yml +0 -75
  190. package/docker-compose.simple.yml +0 -57
  191. package/docker-compose.yml +0 -71
  192. package/eslint.config.js +0 -23
  193. package/flux-cli.ts +0 -214
  194. package/nginx-lb.conf +0 -37
  195. package/publish.sh +0 -63
  196. package/run-clean.ts +0 -26
  197. package/run-env-tests.ts +0 -313
  198. package/tailwind.config.js +0 -34
  199. package/tests/__mocks__/api.ts +0 -56
  200. package/tests/fixtures/users.ts +0 -69
  201. package/tests/integration/api/users.routes.test.ts +0 -221
  202. package/tests/setup.ts +0 -29
  203. package/tests/unit/app/client/App-simple.test.tsx +0 -56
  204. package/tests/unit/app/client/App.test.tsx.skip +0 -237
  205. package/tests/unit/app/client/eden-api.test.ts +0 -186
  206. package/tests/unit/app/client/simple.test.tsx +0 -23
  207. package/tests/unit/app/controllers/users.controller.test.ts +0 -150
  208. package/tests/unit/core/create-project.test.ts.skip +0 -95
  209. package/tests/unit/core/framework.test.ts +0 -144
  210. package/tests/unit/core/plugins/logger.test.ts.skip +0 -268
  211. package/tests/unit/core/plugins/vite.test.ts.disabled +0 -188
  212. package/tests/utils/test-helpers.ts +0 -61
  213. package/vitest.config.ts +0 -50
  214. package/workspace.json +0 -6
@@ -0,0 +1,688 @@
1
+ // 🔌 FluxStack Enhanced WebSocket Connection Manager
2
+ // Advanced connection management with pooling, load balancing, and health monitoring
3
+
4
+ import { EventEmitter } from 'events'
5
+
6
+ export interface ConnectionConfig {
7
+ maxConnections: number
8
+ connectionTimeout: number
9
+ heartbeatInterval: number
10
+ reconnectAttempts: number
11
+ reconnectDelay: number
12
+ maxReconnectDelay: number
13
+ jitterFactor: number
14
+ loadBalancing: 'round-robin' | 'least-connections' | 'random'
15
+ healthCheckInterval: number
16
+ messageQueueSize: number
17
+ offlineQueueEnabled: boolean
18
+ }
19
+
20
+ export interface ConnectionMetrics {
21
+ id: string
22
+ connectedAt: Date
23
+ lastActivity: Date
24
+ messagesSent: number
25
+ messagesReceived: number
26
+ bytesTransferred: number
27
+ latency: number
28
+ status: 'connecting' | 'connected' | 'disconnecting' | 'disconnected' | 'error'
29
+ errorCount: number
30
+ reconnectCount: number
31
+ }
32
+
33
+ export interface ConnectionHealth {
34
+ id: string
35
+ status: 'healthy' | 'degraded' | 'unhealthy'
36
+ lastCheck: Date
37
+ issues: string[]
38
+ metrics: ConnectionMetrics
39
+ }
40
+
41
+ export interface QueuedMessage {
42
+ id: string
43
+ message: any
44
+ timestamp: number
45
+ priority: number
46
+ retryCount: number
47
+ maxRetries: number
48
+ }
49
+
50
+ export interface LoadBalancerStats {
51
+ strategy: string
52
+ totalConnections: number
53
+ activeConnections: number
54
+ averageLatency: number
55
+ messageDistribution: Record<string, number>
56
+ }
57
+
58
+ export class WebSocketConnectionManager extends EventEmitter {
59
+ private connections = new Map<string, any>() // connectionId -> websocket
60
+ private connectionMetrics = new Map<string, ConnectionMetrics>()
61
+ private connectionPools = new Map<string, Set<string>>() // poolId -> connectionIds
62
+ private messageQueues = new Map<string, QueuedMessage[]>() // connectionId -> queued messages
63
+ private healthCheckInterval: NodeJS.Timeout
64
+ private config: ConnectionConfig
65
+ private loadBalancerIndex = 0
66
+
67
+ constructor(config?: Partial<ConnectionConfig>) {
68
+ super()
69
+
70
+ this.config = {
71
+ maxConnections: 10000,
72
+ connectionTimeout: 30000,
73
+ heartbeatInterval: 30000,
74
+ reconnectAttempts: 5,
75
+ reconnectDelay: 1000,
76
+ maxReconnectDelay: 30000,
77
+ jitterFactor: 0.1,
78
+ loadBalancing: 'round-robin',
79
+ healthCheckInterval: 60000,
80
+ messageQueueSize: 1000,
81
+ offlineQueueEnabled: true,
82
+ ...config
83
+ }
84
+
85
+ this.setupHealthMonitoring()
86
+ this.setupHeartbeat()
87
+ }
88
+
89
+ /**
90
+ * Register a new WebSocket connection
91
+ */
92
+ registerConnection(ws: any, connectionId: string, poolId?: string): void {
93
+ if (this.connections.size >= this.config.maxConnections) {
94
+ throw new Error('Maximum connections exceeded')
95
+ }
96
+
97
+ // Create connection metrics
98
+ const metrics: ConnectionMetrics = {
99
+ id: connectionId,
100
+ connectedAt: new Date(),
101
+ lastActivity: new Date(),
102
+ messagesSent: 0,
103
+ messagesReceived: 0,
104
+ bytesTransferred: 0,
105
+ latency: 0,
106
+ status: 'connected',
107
+ errorCount: 0,
108
+ reconnectCount: 0
109
+ }
110
+
111
+ this.connections.set(connectionId, ws)
112
+ this.connectionMetrics.set(connectionId, metrics)
113
+
114
+ // Add to pool if specified
115
+ if (poolId) {
116
+ this.addToPool(connectionId, poolId)
117
+ }
118
+
119
+ // Initialize message queue
120
+ this.messageQueues.set(connectionId, [])
121
+
122
+ // Setup connection event handlers
123
+ this.setupConnectionHandlers(ws, connectionId)
124
+
125
+ console.log(`🔌 Connection registered: ${connectionId} (Pool: ${poolId || 'default'})`)
126
+ this.emit('connectionRegistered', { connectionId, poolId })
127
+ }
128
+
129
+ /**
130
+ * Setup connection event handlers
131
+ */
132
+ private setupConnectionHandlers(ws: any, connectionId: string): void {
133
+ const metrics = this.connectionMetrics.get(connectionId)
134
+ if (!metrics) return
135
+
136
+ // Handle incoming messages
137
+ ws.on('message', (data: any) => {
138
+ metrics.messagesReceived++
139
+ metrics.lastActivity = new Date()
140
+ metrics.bytesTransferred += Buffer.byteLength(data)
141
+
142
+ this.emit('messageReceived', { connectionId, data })
143
+ })
144
+
145
+ // Handle connection close
146
+ ws.on('close', () => {
147
+ metrics.status = 'disconnected'
148
+ this.handleConnectionClose(connectionId)
149
+ })
150
+
151
+ // Handle connection errors
152
+ ws.on('error', (error: Error) => {
153
+ metrics.errorCount++
154
+ metrics.status = 'error'
155
+ this.handleConnectionError(connectionId, error)
156
+ })
157
+
158
+ // Handle pong responses for latency measurement
159
+ ws.on('pong', () => {
160
+ const now = Date.now()
161
+ const pingTime = (ws as any)._pingTime
162
+ if (pingTime) {
163
+ metrics.latency = now - pingTime
164
+ delete (ws as any)._pingTime
165
+ }
166
+ })
167
+ }
168
+
169
+ /**
170
+ * Add connection to a pool
171
+ */
172
+ addToPool(connectionId: string, poolId: string): void {
173
+ if (!this.connectionPools.has(poolId)) {
174
+ this.connectionPools.set(poolId, new Set())
175
+ }
176
+
177
+ this.connectionPools.get(poolId)!.add(connectionId)
178
+ console.log(`🏊 Connection ${connectionId} added to pool ${poolId}`)
179
+ }
180
+
181
+ /**
182
+ * Remove connection from pool
183
+ */
184
+ removeFromPool(connectionId: string, poolId: string): void {
185
+ const pool = this.connectionPools.get(poolId)
186
+ if (pool) {
187
+ pool.delete(connectionId)
188
+ if (pool.size === 0) {
189
+ this.connectionPools.delete(poolId)
190
+ }
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Send message with load balancing and queuing
196
+ */
197
+ async sendMessage(
198
+ message: any,
199
+ target?: { connectionId?: string; poolId?: string },
200
+ options?: { priority?: number; maxRetries?: number; queueIfOffline?: boolean }
201
+ ): Promise<boolean> {
202
+ const { priority = 1, maxRetries = 3, queueIfOffline = true } = options || {}
203
+
204
+ let targetConnections: string[] = []
205
+
206
+ if (target?.connectionId) {
207
+ // Send to specific connection
208
+ targetConnections = [target.connectionId]
209
+ } else if (target?.poolId) {
210
+ // Send to pool using load balancing
211
+ targetConnections = this.selectConnectionsFromPool(target.poolId, 1)
212
+ } else {
213
+ // Broadcast to all connections
214
+ targetConnections = Array.from(this.connections.keys())
215
+ }
216
+
217
+ let successCount = 0
218
+
219
+ for (const connectionId of targetConnections) {
220
+ const success = await this.sendToConnection(connectionId, message, {
221
+ priority,
222
+ maxRetries,
223
+ queueIfOffline
224
+ })
225
+
226
+ if (success) successCount++
227
+ }
228
+
229
+ return successCount > 0
230
+ }
231
+
232
+ /**
233
+ * Send message to specific connection
234
+ */
235
+ private async sendToConnection(
236
+ connectionId: string,
237
+ message: any,
238
+ options: { priority: number; maxRetries: number; queueIfOffline: boolean }
239
+ ): Promise<boolean> {
240
+ const ws = this.connections.get(connectionId)
241
+ const metrics = this.connectionMetrics.get(connectionId)
242
+
243
+ if (!ws || !metrics) {
244
+ return false
245
+ }
246
+
247
+ // Check if connection is ready
248
+ if (ws.readyState !== 1) { // WebSocket.OPEN
249
+ if (options.queueIfOffline && this.config.offlineQueueEnabled) {
250
+ return this.queueMessage(connectionId, message, options)
251
+ }
252
+ return false
253
+ }
254
+
255
+ try {
256
+ const serializedMessage = JSON.stringify(message)
257
+ ws.send(serializedMessage)
258
+
259
+ // Update metrics
260
+ metrics.messagesSent++
261
+ metrics.lastActivity = new Date()
262
+ metrics.bytesTransferred += Buffer.byteLength(serializedMessage)
263
+
264
+ return true
265
+ } catch (error) {
266
+ console.error(`❌ Failed to send message to ${connectionId}:`, error)
267
+
268
+ // Queue message for retry if enabled
269
+ if (options.queueIfOffline) {
270
+ return this.queueMessage(connectionId, message, options)
271
+ }
272
+
273
+ return false
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Queue message for offline delivery
279
+ */
280
+ private queueMessage(
281
+ connectionId: string,
282
+ message: any,
283
+ options: { priority: number; maxRetries: number }
284
+ ): boolean {
285
+ const queue = this.messageQueues.get(connectionId)
286
+ if (!queue) return false
287
+
288
+ // Check queue size limit
289
+ if (queue.length >= this.config.messageQueueSize) {
290
+ // Remove oldest low-priority message
291
+ const lowPriorityIndex = queue.findIndex(msg => msg.priority <= options.priority)
292
+ if (lowPriorityIndex !== -1) {
293
+ queue.splice(lowPriorityIndex, 1)
294
+ } else {
295
+ return false // Queue full with higher priority messages
296
+ }
297
+ }
298
+
299
+ const queuedMessage: QueuedMessage = {
300
+ id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
301
+ message,
302
+ timestamp: Date.now(),
303
+ priority: options.priority,
304
+ retryCount: 0,
305
+ maxRetries: options.maxRetries
306
+ }
307
+
308
+ // Insert message in priority order
309
+ const insertIndex = queue.findIndex(msg => msg.priority < options.priority)
310
+ if (insertIndex === -1) {
311
+ queue.push(queuedMessage)
312
+ } else {
313
+ queue.splice(insertIndex, 0, queuedMessage)
314
+ }
315
+
316
+ console.log(`📬 Message queued for ${connectionId}: ${queuedMessage.id}`)
317
+ return true
318
+ }
319
+
320
+ /**
321
+ * Process queued messages for a connection
322
+ */
323
+ private async processMessageQueue(connectionId: string): Promise<void> {
324
+ const queue = this.messageQueues.get(connectionId)
325
+ const ws = this.connections.get(connectionId)
326
+
327
+ if (!queue || !ws || ws.readyState !== 1) return
328
+
329
+ const messagesToProcess = [...queue]
330
+ queue.length = 0 // Clear queue
331
+
332
+ for (const queuedMessage of messagesToProcess) {
333
+ try {
334
+ const success = await this.sendToConnection(connectionId, queuedMessage.message, {
335
+ priority: queuedMessage.priority,
336
+ maxRetries: queuedMessage.maxRetries - queuedMessage.retryCount,
337
+ queueIfOffline: false
338
+ })
339
+
340
+ if (!success) {
341
+ queuedMessage.retryCount++
342
+ if (queuedMessage.retryCount < queuedMessage.maxRetries) {
343
+ // Re-queue for retry
344
+ queue.push(queuedMessage)
345
+ } else {
346
+ console.warn(`❌ Message ${queuedMessage.id} exceeded max retries`)
347
+ }
348
+ } else {
349
+ console.log(`✅ Queued message delivered: ${queuedMessage.id}`)
350
+ }
351
+ } catch (error) {
352
+ console.error(`❌ Error processing queued message ${queuedMessage.id}:`, error)
353
+ }
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Select connections from pool using load balancing strategy
359
+ */
360
+ private selectConnectionsFromPool(poolId: string, count: number = 1): string[] {
361
+ const pool = this.connectionPools.get(poolId)
362
+ if (!pool || pool.size === 0) return []
363
+
364
+ const availableConnections = Array.from(pool).filter(connectionId => {
365
+ const ws = this.connections.get(connectionId)
366
+ return ws && ws.readyState === 1 // WebSocket.OPEN
367
+ })
368
+
369
+ if (availableConnections.length === 0) return []
370
+
371
+ switch (this.config.loadBalancing) {
372
+ case 'round-robin':
373
+ return this.roundRobinSelection(availableConnections, count)
374
+
375
+ case 'least-connections':
376
+ return this.leastConnectionsSelection(availableConnections, count)
377
+
378
+ case 'random':
379
+ return this.randomSelection(availableConnections, count)
380
+
381
+ default:
382
+ return this.roundRobinSelection(availableConnections, count)
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Round-robin load balancing
388
+ */
389
+ private roundRobinSelection(connections: string[], count: number): string[] {
390
+ const selected: string[] = []
391
+
392
+ for (let i = 0; i < count && i < connections.length; i++) {
393
+ const index = (this.loadBalancerIndex + i) % connections.length
394
+ selected.push(connections[index])
395
+ }
396
+
397
+ this.loadBalancerIndex = (this.loadBalancerIndex + count) % connections.length
398
+ return selected
399
+ }
400
+
401
+ /**
402
+ * Least connections load balancing
403
+ */
404
+ private leastConnectionsSelection(connections: string[], count: number): string[] {
405
+ const connectionLoads = connections.map(connectionId => {
406
+ const metrics = this.connectionMetrics.get(connectionId)
407
+ const queueSize = this.messageQueues.get(connectionId)?.length || 0
408
+ return {
409
+ connectionId,
410
+ load: (metrics?.messagesSent || 0) + queueSize
411
+ }
412
+ })
413
+
414
+ connectionLoads.sort((a, b) => a.load - b.load)
415
+ return connectionLoads.slice(0, count).map(item => item.connectionId)
416
+ }
417
+
418
+ /**
419
+ * Random load balancing
420
+ */
421
+ private randomSelection(connections: string[], count: number): string[] {
422
+ const shuffled = [...connections].sort(() => Math.random() - 0.5)
423
+ return shuffled.slice(0, count)
424
+ }
425
+
426
+ /**
427
+ * Handle connection close
428
+ */
429
+ private handleConnectionClose(connectionId: string): void {
430
+ console.log(`🔌 Connection closed: ${connectionId}`)
431
+
432
+ // Update metrics
433
+ const metrics = this.connectionMetrics.get(connectionId)
434
+ if (metrics) {
435
+ metrics.status = 'disconnected'
436
+ }
437
+
438
+ // Remove from pools
439
+ for (const [poolId, pool] of this.connectionPools) {
440
+ if (pool.has(connectionId)) {
441
+ this.removeFromPool(connectionId, poolId)
442
+ }
443
+ }
444
+
445
+ this.emit('connectionClosed', { connectionId })
446
+ }
447
+
448
+ /**
449
+ * Handle connection error
450
+ */
451
+ private handleConnectionError(connectionId: string, error: Error): void {
452
+ console.error(`❌ Connection error for ${connectionId}:`, error.message)
453
+
454
+ const metrics = this.connectionMetrics.get(connectionId)
455
+ if (metrics) {
456
+ metrics.errorCount++
457
+ }
458
+
459
+ this.emit('connectionError', { connectionId, error })
460
+ }
461
+
462
+ /**
463
+ * Setup health monitoring
464
+ */
465
+ private setupHealthMonitoring(): void {
466
+ this.healthCheckInterval = setInterval(() => {
467
+ this.performHealthChecks()
468
+ }, this.config.healthCheckInterval)
469
+ }
470
+
471
+ /**
472
+ * Setup heartbeat/ping mechanism
473
+ */
474
+ private setupHeartbeat(): void {
475
+ setInterval(() => {
476
+ this.sendHeartbeat()
477
+ }, this.config.heartbeatInterval)
478
+ }
479
+
480
+ /**
481
+ * Send heartbeat to all connections
482
+ */
483
+ private sendHeartbeat(): void {
484
+ for (const [connectionId, ws] of this.connections) {
485
+ if (ws.readyState === 1) { // WebSocket.OPEN
486
+ try {
487
+ (ws as any)._pingTime = Date.now()
488
+ ws.ping()
489
+ } catch (error) {
490
+ console.error(`❌ Heartbeat failed for ${connectionId}:`, error)
491
+ }
492
+ }
493
+ }
494
+ }
495
+
496
+ /**
497
+ * Perform health checks on all connections
498
+ */
499
+ private async performHealthChecks(): Promise<void> {
500
+ const healthChecks: ConnectionHealth[] = []
501
+ const now = Date.now()
502
+
503
+ for (const [connectionId, metrics] of this.connectionMetrics) {
504
+ const ws = this.connections.get(connectionId)
505
+ const issues: string[] = []
506
+ let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'
507
+
508
+ // Check connection state
509
+ if (!ws || ws.readyState !== 1) {
510
+ issues.push('Connection not open')
511
+ status = 'unhealthy'
512
+ }
513
+
514
+ // Check activity
515
+ const timeSinceActivity = now - metrics.lastActivity.getTime()
516
+ if (timeSinceActivity > this.config.heartbeatInterval * 2) {
517
+ issues.push('No activity for extended period')
518
+ status = status === 'healthy' ? 'degraded' : 'unhealthy'
519
+ }
520
+
521
+ // Check error rate
522
+ if (metrics.errorCount > 10) {
523
+ issues.push('High error rate')
524
+ status = 'unhealthy'
525
+ }
526
+
527
+ // Check latency
528
+ if (metrics.latency > 5000) { // 5 seconds
529
+ issues.push('High latency detected')
530
+ status = status === 'healthy' ? 'degraded' : status
531
+ }
532
+
533
+ healthChecks.push({
534
+ id: connectionId,
535
+ status,
536
+ lastCheck: new Date(),
537
+ issues,
538
+ metrics: { ...metrics }
539
+ })
540
+ }
541
+
542
+ // Handle unhealthy connections
543
+ const unhealthyConnections = healthChecks.filter(hc => hc.status === 'unhealthy')
544
+ for (const unhealthy of unhealthyConnections) {
545
+ await this.handleUnhealthyConnection(unhealthy.id)
546
+ }
547
+
548
+ this.emit('healthCheckCompleted', { healthChecks, unhealthyCount: unhealthyConnections.length })
549
+ }
550
+
551
+ /**
552
+ * Handle unhealthy connection
553
+ */
554
+ private async handleUnhealthyConnection(connectionId: string): Promise<void> {
555
+ console.warn(`⚠️ Handling unhealthy connection: ${connectionId}`)
556
+
557
+ const ws = this.connections.get(connectionId)
558
+ if (ws) {
559
+ try {
560
+ ws.close()
561
+ } catch (error) {
562
+ console.error(`❌ Error closing unhealthy connection ${connectionId}:`, error)
563
+ }
564
+ }
565
+
566
+ this.cleanupConnection(connectionId)
567
+ }
568
+
569
+ /**
570
+ * Cleanup connection resources
571
+ */
572
+ cleanupConnection(connectionId: string): void {
573
+ // Process any remaining queued messages
574
+ this.processMessageQueue(connectionId)
575
+
576
+ // Remove from all data structures
577
+ this.connections.delete(connectionId)
578
+ this.connectionMetrics.delete(connectionId)
579
+ this.messageQueues.delete(connectionId)
580
+
581
+ // Remove from pools
582
+ for (const [poolId, pool] of this.connectionPools) {
583
+ if (pool.has(connectionId)) {
584
+ this.removeFromPool(connectionId, poolId)
585
+ }
586
+ }
587
+
588
+ console.log(`🧹 Connection cleaned up: ${connectionId}`)
589
+ }
590
+
591
+ /**
592
+ * Get connection metrics
593
+ */
594
+ getConnectionMetrics(connectionId: string): ConnectionMetrics | null {
595
+ return this.connectionMetrics.get(connectionId) || null
596
+ }
597
+
598
+ /**
599
+ * Get all connection metrics
600
+ */
601
+ getAllConnectionMetrics(): ConnectionMetrics[] {
602
+ return Array.from(this.connectionMetrics.values())
603
+ }
604
+
605
+ /**
606
+ * Get pool statistics
607
+ */
608
+ getPoolStats(poolId: string): LoadBalancerStats | null {
609
+ const pool = this.connectionPools.get(poolId)
610
+ if (!pool) return null
611
+
612
+ const connections = Array.from(pool)
613
+ const activeConnections = connections.filter(connectionId => {
614
+ const ws = this.connections.get(connectionId)
615
+ return ws && ws.readyState === 1
616
+ })
617
+
618
+ const totalLatency = activeConnections.reduce((sum, connectionId) => {
619
+ const metrics = this.connectionMetrics.get(connectionId)
620
+ return sum + (metrics?.latency || 0)
621
+ }, 0)
622
+
623
+ const messageDistribution: Record<string, number> = {}
624
+ for (const connectionId of connections) {
625
+ const metrics = this.connectionMetrics.get(connectionId)
626
+ messageDistribution[connectionId] = metrics?.messagesSent || 0
627
+ }
628
+
629
+ return {
630
+ strategy: this.config.loadBalancing,
631
+ totalConnections: connections.length,
632
+ activeConnections: activeConnections.length,
633
+ averageLatency: activeConnections.length > 0 ? totalLatency / activeConnections.length : 0,
634
+ messageDistribution
635
+ }
636
+ }
637
+
638
+ /**
639
+ * Get overall system stats
640
+ */
641
+ getSystemStats() {
642
+ const totalConnections = this.connections.size
643
+ const activeConnections = Array.from(this.connections.values()).filter(ws => ws.readyState === 1).length
644
+ const totalPools = this.connectionPools.size
645
+ const totalQueuedMessages = Array.from(this.messageQueues.values()).reduce((sum, queue) => sum + queue.length, 0)
646
+
647
+ return {
648
+ totalConnections,
649
+ activeConnections,
650
+ totalPools,
651
+ totalQueuedMessages,
652
+ maxConnections: this.config.maxConnections,
653
+ connectionUtilization: (totalConnections / this.config.maxConnections) * 100
654
+ }
655
+ }
656
+
657
+ /**
658
+ * Shutdown connection manager
659
+ */
660
+ shutdown(): void {
661
+ console.log('🔌 Shutting down WebSocket Connection Manager...')
662
+
663
+ // Clear intervals
664
+ if (this.healthCheckInterval) {
665
+ clearInterval(this.healthCheckInterval)
666
+ }
667
+
668
+ // Close all connections
669
+ for (const [connectionId, ws] of this.connections) {
670
+ try {
671
+ ws.close()
672
+ } catch (error) {
673
+ console.error(`❌ Error closing connection ${connectionId}:`, error)
674
+ }
675
+ }
676
+
677
+ // Clear all data
678
+ this.connections.clear()
679
+ this.connectionMetrics.clear()
680
+ this.connectionPools.clear()
681
+ this.messageQueues.clear()
682
+
683
+ console.log('✅ WebSocket Connection Manager shutdown complete')
684
+ }
685
+ }
686
+
687
+ // Global connection manager instance
688
+ export const connectionManager = new WebSocketConnectionManager()