create-fluxstack 1.9.1 → 1.12.0

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 (259) hide show
  1. package/.dockerignore +1 -2
  2. package/Dockerfile +8 -8
  3. package/LIVE_COMPONENTS_REVIEW.md +781 -0
  4. package/LLMD/INDEX.md +64 -0
  5. package/LLMD/MAINTENANCE.md +197 -0
  6. package/LLMD/MIGRATION.md +156 -0
  7. package/LLMD/config/.gitkeep +1 -0
  8. package/LLMD/config/declarative-system.md +268 -0
  9. package/LLMD/config/environment-vars.md +327 -0
  10. package/LLMD/config/runtime-reload.md +401 -0
  11. package/LLMD/core/.gitkeep +1 -0
  12. package/LLMD/core/build-system.md +599 -0
  13. package/LLMD/core/framework-lifecycle.md +229 -0
  14. package/LLMD/core/plugin-system.md +451 -0
  15. package/LLMD/patterns/.gitkeep +1 -0
  16. package/LLMD/patterns/anti-patterns.md +297 -0
  17. package/LLMD/patterns/project-structure.md +264 -0
  18. package/LLMD/patterns/type-safety.md +440 -0
  19. package/LLMD/reference/.gitkeep +1 -0
  20. package/LLMD/reference/cli-commands.md +250 -0
  21. package/LLMD/reference/plugin-hooks.md +357 -0
  22. package/LLMD/reference/routing.md +39 -0
  23. package/LLMD/reference/troubleshooting.md +364 -0
  24. package/LLMD/resources/.gitkeep +1 -0
  25. package/LLMD/resources/controllers.md +465 -0
  26. package/LLMD/resources/live-components.md +703 -0
  27. package/LLMD/resources/live-rooms.md +482 -0
  28. package/LLMD/resources/live-upload.md +130 -0
  29. package/LLMD/resources/plugins-external.md +617 -0
  30. package/LLMD/resources/routes-eden.md +254 -0
  31. package/README.md +37 -17
  32. package/app/client/index.html +0 -1
  33. package/app/client/src/App.tsx +109 -156
  34. package/app/client/src/components/AppLayout.tsx +68 -0
  35. package/app/client/src/components/BackButton.tsx +13 -0
  36. package/app/client/src/components/DemoPage.tsx +20 -0
  37. package/app/client/src/components/LiveUploadWidget.tsx +204 -0
  38. package/app/client/src/lib/eden-api.ts +85 -65
  39. package/app/client/src/live/ChatDemo.tsx +107 -0
  40. package/app/client/src/live/CounterDemo.tsx +206 -0
  41. package/app/client/src/live/FormDemo.tsx +119 -0
  42. package/app/client/src/live/RoomChatDemo.tsx +242 -0
  43. package/app/client/src/live/UploadDemo.tsx +21 -0
  44. package/app/client/src/main.tsx +13 -10
  45. package/app/client/src/pages/ApiTestPage.tsx +108 -0
  46. package/app/client/src/pages/HomePage.tsx +76 -0
  47. package/app/client/src/vite-env.d.ts +1 -1
  48. package/app/server/app.ts +1 -4
  49. package/app/server/controllers/users.controller.ts +36 -44
  50. package/app/server/index.ts +24 -107
  51. package/app/server/live/LiveChat.ts +77 -0
  52. package/app/server/live/LiveCounter.ts +67 -0
  53. package/app/server/live/LiveForm.ts +63 -0
  54. package/app/server/live/LiveLocalCounter.ts +32 -0
  55. package/app/server/live/LiveRoomChat.ts +285 -0
  56. package/app/server/live/LiveUpload.ts +81 -0
  57. package/app/server/live/register-components.ts +19 -19
  58. package/app/server/routes/index.ts +3 -1
  59. package/app/server/routes/room.routes.ts +117 -0
  60. package/app/server/routes/users.routes.ts +35 -27
  61. package/app/shared/types/index.ts +14 -2
  62. package/config/app.config.ts +2 -62
  63. package/config/client.config.ts +2 -95
  64. package/config/database.config.ts +2 -99
  65. package/config/fluxstack.config.ts +25 -45
  66. package/config/index.ts +57 -38
  67. package/config/monitoring.config.ts +2 -114
  68. package/config/plugins.config.ts +2 -80
  69. package/config/server.config.ts +2 -68
  70. package/config/services.config.ts +2 -130
  71. package/config/system/app.config.ts +29 -0
  72. package/config/system/build.config.ts +49 -0
  73. package/config/system/client.config.ts +68 -0
  74. package/config/system/database.config.ts +17 -0
  75. package/config/system/fluxstack.config.ts +114 -0
  76. package/config/{logger.config.ts → system/logger.config.ts} +3 -1
  77. package/config/system/monitoring.config.ts +114 -0
  78. package/config/system/plugins.config.ts +84 -0
  79. package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
  80. package/config/system/server.config.ts +68 -0
  81. package/config/system/services.config.ts +46 -0
  82. package/config/{system.config.ts → system/system.config.ts} +1 -1
  83. package/core/build/bundler.ts +4 -1
  84. package/core/build/flux-plugins-generator.ts +325 -325
  85. package/core/build/index.ts +159 -27
  86. package/core/build/live-components-generator.ts +70 -3
  87. package/core/build/optimizer.ts +235 -235
  88. package/core/cli/command-registry.ts +6 -4
  89. package/core/cli/commands/build.ts +79 -0
  90. package/core/cli/commands/create.ts +54 -0
  91. package/core/cli/commands/dev.ts +101 -0
  92. package/core/cli/commands/help.ts +34 -0
  93. package/core/cli/commands/index.ts +34 -0
  94. package/core/cli/commands/make-plugin.ts +90 -0
  95. package/core/cli/commands/plugin-add.ts +197 -0
  96. package/core/cli/commands/plugin-deps.ts +2 -2
  97. package/core/cli/commands/plugin-list.ts +208 -0
  98. package/core/cli/commands/plugin-remove.ts +170 -0
  99. package/core/cli/generators/component.ts +769 -769
  100. package/core/cli/generators/controller.ts +1 -1
  101. package/core/cli/generators/index.ts +146 -146
  102. package/core/cli/generators/interactive.ts +227 -227
  103. package/core/cli/generators/plugin.ts +2 -2
  104. package/core/cli/generators/prompts.ts +82 -82
  105. package/core/cli/generators/route.ts +6 -6
  106. package/core/cli/generators/service.ts +2 -2
  107. package/core/cli/generators/template-engine.ts +4 -3
  108. package/core/cli/generators/types.ts +2 -2
  109. package/core/cli/generators/utils.ts +191 -191
  110. package/core/cli/index.ts +115 -558
  111. package/core/cli/plugin-discovery.ts +2 -2
  112. package/core/client/LiveComponentsProvider.tsx +63 -17
  113. package/core/client/api/eden.ts +183 -0
  114. package/core/client/api/index.ts +11 -0
  115. package/core/client/components/Live.tsx +104 -0
  116. package/core/client/fluxstack.ts +1 -9
  117. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -0
  118. package/core/client/hooks/state-validator.ts +1 -1
  119. package/core/client/hooks/useAuth.ts +48 -48
  120. package/core/client/hooks/useChunkedUpload.ts +170 -69
  121. package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
  122. package/core/client/hooks/useLiveComponent.ts +800 -0
  123. package/core/client/hooks/useLiveUpload.ts +71 -0
  124. package/core/client/hooks/useRoom.ts +409 -0
  125. package/core/client/hooks/useRoomProxy.ts +382 -0
  126. package/core/client/index.ts +18 -51
  127. package/core/client/standalone-entry.ts +8 -0
  128. package/core/client/standalone.ts +74 -53
  129. package/core/client/state/createStore.ts +192 -192
  130. package/core/client/state/index.ts +14 -14
  131. package/core/config/index.ts +70 -291
  132. package/core/config/schema.ts +42 -723
  133. package/core/framework/client.ts +131 -131
  134. package/core/framework/index.ts +7 -7
  135. package/core/framework/server.ts +227 -47
  136. package/core/framework/types.ts +2 -2
  137. package/core/index.ts +23 -4
  138. package/core/live/ComponentRegistry.ts +7 -3
  139. package/core/live/types.ts +77 -0
  140. package/core/plugins/built-in/index.ts +134 -131
  141. package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1074
  142. package/core/plugins/built-in/live-components/index.ts +1 -1
  143. package/core/plugins/built-in/monitoring/index.ts +111 -47
  144. package/core/plugins/built-in/static/index.ts +1 -1
  145. package/core/plugins/built-in/swagger/index.ts +68 -265
  146. package/core/plugins/built-in/vite/index.ts +94 -306
  147. package/core/plugins/built-in/vite/vite-dev.ts +82 -0
  148. package/core/plugins/config.ts +9 -7
  149. package/core/plugins/dependency-manager.ts +31 -1
  150. package/core/plugins/discovery.ts +19 -7
  151. package/core/plugins/executor.ts +2 -2
  152. package/core/plugins/index.ts +203 -203
  153. package/core/plugins/manager.ts +27 -39
  154. package/core/plugins/module-resolver.ts +19 -8
  155. package/core/plugins/registry.ts +309 -21
  156. package/core/plugins/types.ts +106 -55
  157. package/core/server/framework.ts +66 -43
  158. package/core/server/index.ts +15 -16
  159. package/core/server/live/ComponentRegistry.ts +91 -75
  160. package/core/server/live/FileUploadManager.ts +41 -31
  161. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  162. package/core/server/live/LiveRoomManager.ts +261 -0
  163. package/core/server/live/RoomEventBus.ts +234 -0
  164. package/core/server/live/RoomStateManager.ts +172 -0
  165. package/core/server/live/StateSignature.ts +643 -643
  166. package/core/server/live/WebSocketConnectionManager.ts +30 -19
  167. package/core/server/live/auto-generated-components.ts +41 -26
  168. package/core/server/live/index.ts +14 -0
  169. package/core/server/live/websocket-plugin.ts +233 -72
  170. package/core/server/middleware/elysia-helpers.ts +7 -2
  171. package/core/server/middleware/errorHandling.ts +1 -1
  172. package/core/server/middleware/index.ts +31 -31
  173. package/core/server/plugins/database.ts +180 -180
  174. package/core/server/plugins/static-files-plugin.ts +69 -260
  175. package/core/server/plugins/swagger.ts +33 -33
  176. package/core/server/rooms/RoomBroadcaster.ts +357 -0
  177. package/core/server/rooms/RoomSystem.ts +463 -0
  178. package/core/server/rooms/index.ts +13 -0
  179. package/core/server/services/BaseService.ts +1 -1
  180. package/core/server/services/ServiceContainer.ts +1 -1
  181. package/core/server/services/index.ts +8 -8
  182. package/core/templates/create-project.ts +12 -12
  183. package/core/testing/index.ts +9 -9
  184. package/core/testing/setup.ts +73 -73
  185. package/core/types/api.ts +168 -168
  186. package/core/types/build.ts +219 -218
  187. package/core/types/config.ts +56 -26
  188. package/core/types/index.ts +4 -4
  189. package/core/types/plugin.ts +107 -99
  190. package/core/types/types.ts +490 -14
  191. package/core/utils/build-logger.ts +324 -324
  192. package/core/utils/config-schema.ts +480 -480
  193. package/core/utils/env.ts +2 -8
  194. package/core/utils/errors/codes.ts +114 -114
  195. package/core/utils/errors/handlers.ts +36 -1
  196. package/core/utils/errors/index.ts +49 -5
  197. package/core/utils/errors/middleware.ts +113 -113
  198. package/core/utils/helpers.ts +6 -16
  199. package/core/utils/index.ts +17 -17
  200. package/core/utils/logger/colors.ts +114 -114
  201. package/core/utils/logger/config.ts +13 -9
  202. package/core/utils/logger/formatter.ts +82 -82
  203. package/core/utils/logger/group-logger.ts +101 -101
  204. package/core/utils/logger/index.ts +6 -1
  205. package/core/utils/logger/stack-trace.ts +3 -1
  206. package/core/utils/logger/startup-banner.ts +82 -66
  207. package/core/utils/logger/winston-logger.ts +152 -152
  208. package/core/utils/monitoring/index.ts +211 -211
  209. package/core/utils/sync-version.ts +66 -66
  210. package/core/utils/version.ts +1 -1
  211. package/create-fluxstack.ts +8 -7
  212. package/eslint.config.js +23 -23
  213. package/package.json +14 -15
  214. package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
  215. package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
  216. package/plugins/crypto-auth/client/components/index.ts +11 -11
  217. package/plugins/crypto-auth/client/index.ts +11 -11
  218. package/plugins/crypto-auth/config/index.ts +1 -1
  219. package/plugins/crypto-auth/index.ts +4 -4
  220. package/plugins/crypto-auth/package.json +65 -65
  221. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  222. package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
  223. package/plugins/crypto-auth/server/index.ts +21 -21
  224. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
  225. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
  226. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
  227. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
  228. package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
  229. package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
  230. package/plugins/crypto-auth/server/middlewares.ts +19 -19
  231. package/tsconfig.api-strict.json +16 -0
  232. package/tsconfig.json +10 -14
  233. package/{app/client/tsconfig.node.json → tsconfig.node.json} +1 -1
  234. package/types/global.d.ts +29 -29
  235. package/types/vitest.d.ts +8 -8
  236. package/vite.config.ts +38 -62
  237. package/vitest.config.live.ts +10 -9
  238. package/vitest.config.ts +29 -17
  239. package/workspace.json +5 -5
  240. package/app/client/README.md +0 -69
  241. package/app/client/SIMPLIFICATION.md +0 -140
  242. package/app/client/frontend-only.ts +0 -12
  243. package/app/client/tsconfig.app.json +0 -44
  244. package/app/client/tsconfig.json +0 -7
  245. package/app/client/zustand-setup.md +0 -65
  246. package/app/server/backend-only.ts +0 -18
  247. package/app/server/live/LiveClockComponent.ts +0 -215
  248. package/app/server/routes/env-test.ts +0 -110
  249. package/core/client/hooks/index.ts +0 -7
  250. package/core/client/hooks/useHybridLiveComponent.ts +0 -631
  251. package/core/client/hooks/useWebSocket.ts +0 -373
  252. package/core/config/env.ts +0 -546
  253. package/core/config/loader.ts +0 -522
  254. package/core/config/runtime-config.ts +0 -327
  255. package/core/config/validator.ts +0 -540
  256. package/core/server/backend-entry.ts +0 -51
  257. package/core/server/standalone.ts +0 -106
  258. package/core/utils/regenerate-files.ts +0 -69
  259. package/fluxstack.config.ts +0 -354
@@ -2,6 +2,7 @@
2
2
  // Advanced connection management with pooling, load balancing, and health monitoring
3
3
 
4
4
  import { EventEmitter } from 'events'
5
+ import type { FluxStackWebSocket } from '@core/types/types'
5
6
 
6
7
  export interface ConnectionConfig {
7
8
  maxConnections: number
@@ -56,11 +57,11 @@ export interface LoadBalancerStats {
56
57
  }
57
58
 
58
59
  export class WebSocketConnectionManager extends EventEmitter {
59
- private connections = new Map<string, any>() // connectionId -> websocket
60
+ private connections = new Map<string, FluxStackWebSocket>() // connectionId -> websocket
60
61
  private connectionMetrics = new Map<string, ConnectionMetrics>()
61
62
  private connectionPools = new Map<string, Set<string>>() // poolId -> connectionIds
62
63
  private messageQueues = new Map<string, QueuedMessage[]>() // connectionId -> queued messages
63
- private healthCheckInterval: NodeJS.Timeout
64
+ private healthCheckInterval!: NodeJS.Timeout
64
65
  private config: ConnectionConfig
65
66
  private loadBalancerIndex = 0
66
67
 
@@ -89,7 +90,7 @@ export class WebSocketConnectionManager extends EventEmitter {
89
90
  /**
90
91
  * Register a new WebSocket connection
91
92
  */
92
- registerConnection(ws: any, connectionId: string, poolId?: string): void {
93
+ registerConnection(ws: FluxStackWebSocket, connectionId: string, poolId?: string): void {
93
94
  if (this.connections.size >= this.config.maxConnections) {
94
95
  throw new Error('Maximum connections exceeded')
95
96
  }
@@ -129,26 +130,33 @@ export class WebSocketConnectionManager extends EventEmitter {
129
130
  /**
130
131
  * Setup connection event handlers
131
132
  */
132
- private setupConnectionHandlers(ws: any, connectionId: string): void {
133
+ private setupConnectionHandlers(ws: FluxStackWebSocket, connectionId: string): void {
133
134
  const metrics = this.connectionMetrics.get(connectionId)
134
135
  if (!metrics) return
135
136
 
136
137
  // Handle incoming messages
138
+ // Note: Bun/Elysia WebSockets use different event handling patterns
139
+ // This code provides compatibility layer for both Node.js style (on/addListener) and browser style (addEventListener)
140
+ const wsAny = ws as any
137
141
  const addListener = (event: string, handler: (...args: any[]) => void) => {
138
- if (typeof ws.on === 'function') {
139
- ws.on(event, handler)
140
- } else if (typeof ws.addEventListener === 'function') {
141
- ws.addEventListener(event as any, handler as any)
142
- } else if (typeof ws.addListener === 'function') {
143
- ws.addListener(event, handler)
142
+ if (typeof wsAny.on === 'function') {
143
+ wsAny.on(event, handler)
144
+ } else if (typeof wsAny.addEventListener === 'function') {
145
+ wsAny.addEventListener(event, handler)
146
+ } else if (typeof wsAny.addListener === 'function') {
147
+ wsAny.addListener(event, handler)
144
148
  }
145
149
  }
146
150
 
147
151
  addListener('message', (data: any) => {
148
152
  metrics.messagesReceived++
149
153
  metrics.lastActivity = new Date()
150
- metrics.bytesTransferred += Buffer.byteLength(data)
151
-
154
+ if (typeof data === 'string') {
155
+ metrics.bytesTransferred += Buffer.byteLength(data)
156
+ } else if (data instanceof Buffer) {
157
+ metrics.bytesTransferred += data.length
158
+ }
159
+
152
160
  this.emit('messageReceived', { connectionId, data })
153
161
  })
154
162
 
@@ -168,10 +176,10 @@ export class WebSocketConnectionManager extends EventEmitter {
168
176
  // Handle pong responses for latency measurement
169
177
  addListener('pong', () => {
170
178
  const now = Date.now()
171
- const pingTime = (ws as any)._pingTime
179
+ const pingTime = wsAny._pingTime
172
180
  if (pingTime) {
173
181
  metrics.latency = now - pingTime
174
- delete (ws as any)._pingTime
182
+ delete wsAny._pingTime
175
183
  }
176
184
  })
177
185
  }
@@ -205,7 +213,7 @@ export class WebSocketConnectionManager extends EventEmitter {
205
213
  * Send message with load balancing and queuing
206
214
  */
207
215
  async sendMessage(
208
- message: any,
216
+ message: any,
209
217
  target?: { connectionId?: string; poolId?: string },
210
218
  options?: { priority?: number; maxRetries?: number; queueIfOffline?: boolean }
211
219
  ): Promise<boolean> {
@@ -243,7 +251,7 @@ export class WebSocketConnectionManager extends EventEmitter {
243
251
  * Send message to specific connection
244
252
  */
245
253
  private async sendToConnection(
246
- connectionId: string,
254
+ connectionId: string,
247
255
  message: any,
248
256
  options: { priority: number; maxRetries: number; queueIfOffline: boolean }
249
257
  ): Promise<boolean> {
@@ -288,7 +296,7 @@ export class WebSocketConnectionManager extends EventEmitter {
288
296
  * Queue message for offline delivery
289
297
  */
290
298
  private queueMessage(
291
- connectionId: string,
299
+ connectionId: string,
292
300
  message: any,
293
301
  options: { priority: number; maxRetries: number }
294
302
  ): boolean {
@@ -494,8 +502,11 @@ export class WebSocketConnectionManager extends EventEmitter {
494
502
  for (const [connectionId, ws] of this.connections) {
495
503
  if (ws.readyState === 1) { // WebSocket.OPEN
496
504
  try {
497
- (ws as any)._pingTime = Date.now()
498
- ws.ping()
505
+ const wsAny = ws as any
506
+ wsAny._pingTime = Date.now()
507
+ if (typeof wsAny.ping === 'function') {
508
+ wsAny.ping()
509
+ }
499
510
  } catch (error) {
500
511
  console.error(`❌ Heartbeat failed for ${connectionId}:`, error)
501
512
  }
@@ -1,26 +1,41 @@
1
- // 🔥 Auto-generated Live Components Registration
2
- // This file is automatically generated during build time - DO NOT EDIT MANUALLY
3
- // Generated at: 2025-11-18T01:48:36.705Z
4
-
5
- import { LiveClockComponent } from "@/app/server/live/LiveClockComponent"
6
- import { componentRegistry } from "@/core/server/live/ComponentRegistry"
7
-
8
- // Register all components statically for production bundle
9
- function registerAllComponents() {
10
- try {
11
- // Auto-generated component registrations
12
- componentRegistry.registerComponentClass('LiveClock', LiveClockComponent)
13
-
14
- console.log('📝 Live components registered successfully! (1 components)')
15
- } catch (error) {
16
- console.warn('⚠️ Error registering components:', error)
17
- }
18
- }
19
-
20
- // Auto-register components
21
- registerAllComponents()
22
-
23
- // Export all components to ensure they're included in the bundle
24
- export {
25
- LiveClockComponent
26
- }
1
+ // 🔥 Auto-generated Live Components Registration
2
+ // This file is automatically generated during build time - DO NOT EDIT MANUALLY
3
+ // Generated at: 2026-02-09T22:13:45.496Z
4
+
5
+ import { LiveChat } from "@app/server/live/LiveChat"
6
+ import { LiveCounter } from "@app/server/live/LiveCounter"
7
+ import { LiveForm } from "@app/server/live/LiveForm"
8
+ import { LiveLocalCounter } from "@app/server/live/LiveLocalCounter"
9
+ import { LiveRoomChat } from "@app/server/live/LiveRoomChat"
10
+ import { LiveUpload } from "@app/server/live/LiveUpload"
11
+ import { componentRegistry } from "@core/server/live/ComponentRegistry"
12
+
13
+ // Register all components statically for production bundle
14
+ function registerAllComponents() {
15
+ try {
16
+ // Auto-generated component registrations
17
+ componentRegistry.registerComponentClass('LiveChat', LiveChat)
18
+ componentRegistry.registerComponentClass('LiveCounter', LiveCounter)
19
+ componentRegistry.registerComponentClass('LiveForm', LiveForm)
20
+ componentRegistry.registerComponentClass('LiveLocalCounter', LiveLocalCounter)
21
+ componentRegistry.registerComponentClass('LiveRoomChat', LiveRoomChat)
22
+ componentRegistry.registerComponentClass('LiveUpload', LiveUpload)
23
+
24
+ console.log('📝 Live components registered successfully! (6 components)')
25
+ } catch (error) {
26
+ console.warn('⚠️ Error registering components:', error)
27
+ }
28
+ }
29
+
30
+ // Auto-register components
31
+ registerAllComponents()
32
+
33
+ // Export all components to ensure they're included in the bundle
34
+ export {
35
+ LiveChat,
36
+ LiveCounter,
37
+ LiveForm,
38
+ LiveLocalCounter,
39
+ LiveRoomChat,
40
+ LiveUpload
41
+ }
@@ -0,0 +1,14 @@
1
+ // 🔥 FluxStack Live - Server Exports
2
+
3
+ export { roomState, createTypedRoomState } from './RoomStateManager'
4
+ export type { RoomStateData, RoomInfo } from './RoomStateManager'
5
+
6
+ export { roomEvents, createTypedRoomEventBus } from './RoomEventBus'
7
+ export type { EventHandler, RoomSubscription } from './RoomEventBus'
8
+
9
+ export { componentRegistry } from './ComponentRegistry'
10
+ export { liveComponentsPlugin } from './websocket-plugin'
11
+ export { connectionManager } from './WebSocketConnectionManager'
12
+ export { fileUploadManager } from './FileUploadManager'
13
+ export { stateSignature } from './StateSignature'
14
+ export { performanceMonitor } from './LiveComponentPerformanceMonitor'
@@ -4,8 +4,9 @@ import { componentRegistry } from './ComponentRegistry'
4
4
  import { fileUploadManager } from './FileUploadManager'
5
5
  import { connectionManager } from './WebSocketConnectionManager'
6
6
  import { performanceMonitor } from './LiveComponentPerformanceMonitor'
7
- import type { LiveMessage, FileUploadStartMessage, FileUploadChunkMessage, FileUploadCompleteMessage } from '@/core/plugins/types'
8
- import type { Plugin, PluginContext } from '@/core/index'
7
+ import { liveRoomManager, type RoomMessage } from './LiveRoomManager'
8
+ import type { LiveMessage, FileUploadStartMessage, FileUploadChunkMessage, FileUploadCompleteMessage, BinaryChunkHeader, FluxStackWebSocket, FluxStackWSData } from '@core/types/types'
9
+ import type { Plugin, PluginContext } from '@core/index'
9
10
  import { t, Elysia } from 'elysia'
10
11
  import path from 'path'
11
12
 
@@ -131,46 +132,38 @@ export const liveComponentsPlugin: Plugin = {
131
132
 
132
133
  // Create grouped routes for Live Components with documentation
133
134
  const liveRoutes = new Elysia({ prefix: '/api/live', tags: ['Live Components'] })
134
- // WebSocket route
135
+ // WebSocket route - supports both JSON and binary messages
135
136
  .ws('/ws', {
136
- body: t.Object({
137
- type: t.String(),
138
- componentId: t.String(),
139
- action: t.Optional(t.String()),
140
- payload: t.Optional(t.Any()),
141
- timestamp: t.Optional(t.Number()),
142
- userId: t.Optional(t.String()),
143
- room: t.Optional(t.String()),
144
- requestId: t.Optional(t.String()),
145
- expectResponse: t.Optional(t.Boolean()),
146
- // File upload specific fields
147
- uploadId: t.Optional(t.String()),
148
- filename: t.Optional(t.String()),
149
- fileType: t.Optional(t.String()),
150
- fileSize: t.Optional(t.Number()),
151
- chunkSize: t.Optional(t.Number()),
152
- chunkIndex: t.Optional(t.Number()),
153
- totalChunks: t.Optional(t.Number()),
154
- data: t.Optional(t.String()),
155
- hash: t.Optional(t.String())
156
- }),
137
+ // Use t.Any() to allow both JSON objects and binary data
138
+ // Binary messages will be ArrayBuffer/Uint8Array, JSON will be parsed objects
139
+ body: t.Any(),
157
140
 
158
141
  open(ws) {
142
+ const socket = ws as unknown as FluxStackWebSocket
159
143
  const connectionId = `ws-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
160
144
  console.log(`🔌 Live Components WebSocket connected: ${connectionId}`)
161
-
145
+
162
146
  // Register connection with enhanced connection manager
163
- connectionManager.registerConnection(ws, connectionId, 'live-components')
164
-
147
+ connectionManager.registerConnection(ws as unknown as FluxStackWebSocket, connectionId, 'live-components')
148
+
165
149
  // Initialize and store connection data in ws.data
166
- if (!ws.data) {
167
- ws.data = {}
150
+ const wsData: FluxStackWSData = {
151
+ connectionId,
152
+ components: new Map(),
153
+ subscriptions: new Set(),
154
+ connectedAt: new Date()
155
+ }
156
+
157
+ // Assign data to websocket (Elysia creates ws.data from context)
158
+ if (!socket.data) {
159
+ (socket as { data: FluxStackWSData }).data = wsData
160
+ } else {
161
+ socket.data.connectionId = connectionId
162
+ socket.data.components = new Map()
163
+ socket.data.subscriptions = new Set()
164
+ socket.data.connectedAt = new Date()
168
165
  }
169
- ws.data.connectionId = connectionId
170
- ws.data.components = new Map()
171
- ws.data.subscriptions = new Set()
172
- ws.data.connectedAt = new Date()
173
-
166
+
174
167
  // Send connection confirmation
175
168
  ws.send(JSON.stringify({
176
169
  type: 'CONNECTION_ESTABLISHED',
@@ -185,50 +178,96 @@ export const liveComponentsPlugin: Plugin = {
185
178
  }))
186
179
  },
187
180
 
188
- async message(ws, message: LiveMessage) {
181
+ async message(ws: unknown, rawMessage: LiveMessage | ArrayBuffer | Uint8Array) {
182
+ const socket = ws as FluxStackWebSocket
189
183
  try {
190
- // Add connection metadata
191
- message.timestamp = Date.now()
192
-
184
+ let message: LiveMessage
185
+ let binaryChunkData: Buffer | null = null
186
+
187
+ // Check if this is a binary message (file upload chunk)
188
+ if (rawMessage instanceof ArrayBuffer || rawMessage instanceof Uint8Array) {
189
+ // Binary protocol: [4 bytes header length][JSON header][binary data]
190
+ const buffer = rawMessage instanceof ArrayBuffer
191
+ ? Buffer.from(rawMessage)
192
+ : Buffer.from(rawMessage.buffer, rawMessage.byteOffset, rawMessage.byteLength)
193
+
194
+ // Read header length (first 4 bytes, little-endian)
195
+ const headerLength = buffer.readUInt32LE(0)
196
+
197
+ // Extract and parse JSON header
198
+ const headerJson = buffer.slice(4, 4 + headerLength).toString('utf-8')
199
+ const header = JSON.parse(headerJson) as BinaryChunkHeader
200
+
201
+ // Extract binary chunk data
202
+ binaryChunkData = buffer.slice(4 + headerLength)
203
+
204
+ console.log(`📦 Binary chunk received: ${binaryChunkData.length} bytes for upload ${header.uploadId}`)
205
+
206
+ // Create message with binary data attached
207
+ message = {
208
+ ...header,
209
+ data: binaryChunkData, // Buffer instead of base64 string
210
+ timestamp: Date.now()
211
+ } as unknown as LiveMessage
212
+ } else {
213
+ // Regular JSON message
214
+ message = rawMessage as LiveMessage
215
+ message.timestamp = Date.now()
216
+ }
217
+
193
218
  console.log(`📨 Received message:`, {
194
219
  type: message.type,
195
220
  componentId: message.componentId,
196
221
  action: message.action,
197
- requestId: message.requestId
222
+ requestId: message.requestId,
223
+ isBinary: binaryChunkData !== null
198
224
  })
199
225
 
200
226
  // Handle different message types
201
227
  switch (message.type) {
202
228
  case 'COMPONENT_MOUNT':
203
- await handleComponentMount(ws, message)
229
+ await handleComponentMount(socket, message)
204
230
  break
205
231
  case 'COMPONENT_REHYDRATE':
206
- await handleComponentRehydrate(ws, message)
232
+ await handleComponentRehydrate(socket, message)
207
233
  break
208
234
  case 'COMPONENT_UNMOUNT':
209
- await handleComponentUnmount(ws, message)
235
+ await handleComponentUnmount(socket, message)
210
236
  break
211
237
  case 'CALL_ACTION':
212
- await handleActionCall(ws, message)
238
+ await handleActionCall(socket, message)
213
239
  break
214
240
  case 'PROPERTY_UPDATE':
215
- await handlePropertyUpdate(ws, message)
241
+ await handlePropertyUpdate(socket, message)
216
242
  break
217
243
  case 'COMPONENT_PING':
218
- await handleComponentPing(ws, message)
244
+ await handleComponentPing(socket, message)
219
245
  break
220
246
  case 'FILE_UPLOAD_START':
221
- await handleFileUploadStart(ws, message as FileUploadStartMessage)
247
+ await handleFileUploadStart(socket, message as FileUploadStartMessage)
222
248
  break
223
249
  case 'FILE_UPLOAD_CHUNK':
224
- await handleFileUploadChunk(ws, message as FileUploadChunkMessage)
250
+ await handleFileUploadChunk(socket, message as FileUploadChunkMessage, binaryChunkData)
225
251
  break
226
252
  case 'FILE_UPLOAD_COMPLETE':
227
- await handleFileUploadComplete(ws, message as unknown as FileUploadCompleteMessage)
253
+ await handleFileUploadComplete(socket, message as unknown as FileUploadCompleteMessage)
254
+ break
255
+ // Room system messages
256
+ case 'ROOM_JOIN':
257
+ await handleRoomJoin(socket, message as unknown as RoomMessage)
258
+ break
259
+ case 'ROOM_LEAVE':
260
+ await handleRoomLeave(socket, message as unknown as RoomMessage)
261
+ break
262
+ case 'ROOM_EMIT':
263
+ await handleRoomEmit(socket, message as unknown as RoomMessage)
264
+ break
265
+ case 'ROOM_STATE_SET':
266
+ await handleRoomStateSet(socket, message as unknown as RoomMessage)
228
267
  break
229
268
  default:
230
269
  console.warn(`❌ Unknown message type: ${message.type}`)
231
- ws.send(JSON.stringify({
270
+ socket.send(JSON.stringify({
232
271
  type: 'ERROR',
233
272
  error: `Unknown message type: ${message.type}`,
234
273
  timestamp: Date.now()
@@ -236,7 +275,7 @@ export const liveComponentsPlugin: Plugin = {
236
275
  }
237
276
  } catch (error) {
238
277
  console.error('❌ WebSocket message error:', error)
239
- ws.send(JSON.stringify({
278
+ socket.send(JSON.stringify({
240
279
  type: 'ERROR',
241
280
  error: error instanceof Error ? error.message : 'Unknown error',
242
281
  timestamp: Date.now()
@@ -245,16 +284,17 @@ export const liveComponentsPlugin: Plugin = {
245
284
  },
246
285
 
247
286
  close(ws) {
248
- const connectionId = ws.data?.connectionId
287
+ const socket = ws as unknown as FluxStackWebSocket
288
+ const connectionId = socket.data?.connectionId
249
289
  console.log(`🔌 Live Components WebSocket disconnected: ${connectionId}`)
250
-
290
+
251
291
  // Cleanup connection in connection manager
252
292
  if (connectionId) {
253
293
  connectionManager.cleanupConnection(connectionId)
254
294
  }
255
-
295
+
256
296
  // Cleanup components for this connection
257
- componentRegistry.cleanupConnection(ws)
297
+ componentRegistry.cleanupConnection(socket)
258
298
  }
259
299
  })
260
300
 
@@ -456,9 +496,9 @@ export const liveComponentsPlugin: Plugin = {
456
496
  }
457
497
 
458
498
  // Handler functions for WebSocket messages
459
- async function handleComponentMount(ws: any, message: LiveMessage) {
499
+ async function handleComponentMount(ws: FluxStackWebSocket, message: LiveMessage) {
460
500
  const result = await componentRegistry.handleMessage(ws, message)
461
-
501
+
462
502
  if (result !== null) {
463
503
  const response = {
464
504
  type: 'COMPONENT_MOUNTED',
@@ -473,7 +513,7 @@ async function handleComponentMount(ws: any, message: LiveMessage) {
473
513
  }
474
514
  }
475
515
 
476
- async function handleComponentRehydrate(ws: any, message: LiveMessage) {
516
+ async function handleComponentRehydrate(ws: FluxStackWebSocket, message: LiveMessage) {
477
517
  console.log('🔄 Processing component re-hydration request:', {
478
518
  componentId: message.componentId,
479
519
  payload: message.payload
@@ -532,7 +572,7 @@ async function handleComponentRehydrate(ws: any, message: LiveMessage) {
532
572
  }
533
573
  }
534
574
 
535
- async function handleComponentUnmount(ws: any, message: LiveMessage) {
575
+ async function handleComponentUnmount(ws: FluxStackWebSocket, message: LiveMessage) {
536
576
  const result = await componentRegistry.handleMessage(ws, message)
537
577
 
538
578
  if (result !== null) {
@@ -547,7 +587,7 @@ async function handleComponentUnmount(ws: any, message: LiveMessage) {
547
587
  }
548
588
  }
549
589
 
550
- async function handleActionCall(ws: any, message: LiveMessage) {
590
+ async function handleActionCall(ws: FluxStackWebSocket, message: LiveMessage) {
551
591
  const result = await componentRegistry.handleMessage(ws, message)
552
592
 
553
593
  if (result !== null) {
@@ -565,7 +605,7 @@ async function handleActionCall(ws: any, message: LiveMessage) {
565
605
  }
566
606
  }
567
607
 
568
- async function handlePropertyUpdate(ws: any, message: LiveMessage) {
608
+ async function handlePropertyUpdate(ws: FluxStackWebSocket, message: LiveMessage) {
569
609
  const result = await componentRegistry.handleMessage(ws, message)
570
610
 
571
611
  if (result !== null) {
@@ -582,7 +622,7 @@ async function handlePropertyUpdate(ws: any, message: LiveMessage) {
582
622
  }
583
623
  }
584
624
 
585
- async function handleComponentPing(ws: any, message: LiveMessage) {
625
+ async function handleComponentPing(ws: FluxStackWebSocket, message: LiveMessage) {
586
626
  // Update component's last activity timestamp
587
627
  const updated = componentRegistry.updateComponentActivity(message.componentId)
588
628
 
@@ -599,7 +639,7 @@ async function handleComponentPing(ws: any, message: LiveMessage) {
599
639
  }
600
640
 
601
641
  // File Upload Handler Functions
602
- async function handleFileUploadStart(ws: any, message: FileUploadStartMessage) {
642
+ async function handleFileUploadStart(ws: FluxStackWebSocket, message: FileUploadStartMessage) {
603
643
  console.log('📤 Starting file upload:', message.uploadId)
604
644
 
605
645
  const result = await fileUploadManager.startUpload(message)
@@ -617,13 +657,19 @@ async function handleFileUploadStart(ws: any, message: FileUploadStartMessage) {
617
657
  ws.send(JSON.stringify(response))
618
658
  }
619
659
 
620
- async function handleFileUploadChunk(ws: any, message: FileUploadChunkMessage) {
621
- console.log(`📦 Receiving chunk ${message.chunkIndex + 1} for upload ${message.uploadId}`)
622
-
623
- const progressResponse = await fileUploadManager.receiveChunk(message, ws)
624
-
660
+ async function handleFileUploadChunk(ws: FluxStackWebSocket, message: FileUploadChunkMessage, binaryData: Buffer | null = null) {
661
+ console.log(`📦 Receiving chunk ${message.chunkIndex + 1} for upload ${message.uploadId}${binaryData ? ' (binary)' : ' (base64)'}`)
662
+
663
+ const progressResponse = await fileUploadManager.receiveChunk(message, ws, binaryData)
664
+
625
665
  if (progressResponse) {
626
- ws.send(JSON.stringify(progressResponse))
666
+ // Add requestId to response so client can correlate it
667
+ const responseWithRequestId = {
668
+ ...progressResponse,
669
+ requestId: message.requestId,
670
+ success: true
671
+ }
672
+ ws.send(JSON.stringify(responseWithRequestId))
627
673
  } else {
628
674
  // Send error response
629
675
  const errorResponse = {
@@ -632,15 +678,130 @@ async function handleFileUploadChunk(ws: any, message: FileUploadChunkMessage) {
632
678
  uploadId: message.uploadId,
633
679
  error: 'Failed to process chunk',
634
680
  requestId: message.requestId,
681
+ success: false,
635
682
  timestamp: Date.now()
636
683
  }
637
684
  ws.send(JSON.stringify(errorResponse))
638
685
  }
639
686
  }
640
687
 
641
- async function handleFileUploadComplete(ws: any, message: FileUploadCompleteMessage) {
688
+ async function handleFileUploadComplete(ws: FluxStackWebSocket, message: FileUploadCompleteMessage) {
642
689
  console.log('✅ Completing file upload:', message.uploadId)
643
-
690
+
644
691
  const completeResponse = await fileUploadManager.completeUpload(message)
645
- ws.send(JSON.stringify(completeResponse))
646
- }
692
+
693
+ // Add requestId to response so client can correlate it
694
+ const responseWithRequestId = {
695
+ ...completeResponse,
696
+ requestId: message.requestId
697
+ }
698
+
699
+ ws.send(JSON.stringify(responseWithRequestId))
700
+ }
701
+
702
+ // ===== Room System Handlers =====
703
+
704
+ async function handleRoomJoin(ws: FluxStackWebSocket, message: RoomMessage) {
705
+ console.log(`🚪 Component ${message.componentId} joining room ${message.roomId}`)
706
+
707
+ try {
708
+ const result = liveRoomManager.joinRoom(
709
+ message.componentId,
710
+ message.roomId,
711
+ ws,
712
+ message.data?.initialState
713
+ )
714
+
715
+ const response = {
716
+ type: 'ROOM_JOINED',
717
+ componentId: message.componentId,
718
+ roomId: message.roomId,
719
+ success: true,
720
+ state: result.state,
721
+ requestId: message.requestId,
722
+ timestamp: Date.now()
723
+ }
724
+
725
+ ws.send(JSON.stringify(response))
726
+ } catch (error: any) {
727
+ ws.send(JSON.stringify({
728
+ type: 'ERROR',
729
+ componentId: message.componentId,
730
+ roomId: message.roomId,
731
+ error: error.message,
732
+ requestId: message.requestId,
733
+ timestamp: Date.now()
734
+ }))
735
+ }
736
+ }
737
+
738
+ async function handleRoomLeave(ws: FluxStackWebSocket, message: RoomMessage) {
739
+ console.log(`🚶 Component ${message.componentId} leaving room ${message.roomId}`)
740
+
741
+ try {
742
+ liveRoomManager.leaveRoom(message.componentId, message.roomId)
743
+
744
+ const response = {
745
+ type: 'ROOM_LEFT',
746
+ componentId: message.componentId,
747
+ roomId: message.roomId,
748
+ success: true,
749
+ requestId: message.requestId,
750
+ timestamp: Date.now()
751
+ }
752
+
753
+ ws.send(JSON.stringify(response))
754
+ } catch (error: any) {
755
+ ws.send(JSON.stringify({
756
+ type: 'ERROR',
757
+ componentId: message.componentId,
758
+ roomId: message.roomId,
759
+ error: error.message,
760
+ requestId: message.requestId,
761
+ timestamp: Date.now()
762
+ }))
763
+ }
764
+ }
765
+
766
+ async function handleRoomEmit(ws: FluxStackWebSocket, message: RoomMessage) {
767
+ console.log(`📡 Component ${message.componentId} emitting '${message.event}' to room ${message.roomId}`)
768
+
769
+ try {
770
+ const count = liveRoomManager.emitToRoom(
771
+ message.roomId,
772
+ message.event!,
773
+ message.data,
774
+ message.componentId // Excluir quem enviou
775
+ )
776
+
777
+ console.log(` → Notified ${count} components`)
778
+ } catch (error: any) {
779
+ ws.send(JSON.stringify({
780
+ type: 'ERROR',
781
+ componentId: message.componentId,
782
+ roomId: message.roomId,
783
+ error: error.message,
784
+ timestamp: Date.now()
785
+ }))
786
+ }
787
+ }
788
+
789
+ async function handleRoomStateSet(ws: FluxStackWebSocket, message: RoomMessage) {
790
+ console.log(`📝 Component ${message.componentId} updating state in room ${message.roomId}`)
791
+
792
+ try {
793
+ liveRoomManager.setRoomState(
794
+ message.roomId,
795
+ message.data ?? {},
796
+ message.componentId // Excluir quem enviou
797
+ )
798
+ } catch (error: any) {
799
+ ws.send(JSON.stringify({
800
+ type: 'ERROR',
801
+ componentId: message.componentId,
802
+ roomId: message.roomId,
803
+ error: error.message,
804
+ timestamp: Date.now()
805
+ }))
806
+ }
807
+ }