create-fluxstack 1.0.13 → 1.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 (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 +46 -2
  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,399 @@
1
+ // 🔥 FluxStack Live Components - Component Registry
2
+
3
+ import type {
4
+ LiveComponent,
5
+ LiveMessage,
6
+ BroadcastMessage,
7
+ ComponentDefinition,
8
+ WebSocketData
9
+ } from '../types/types'
10
+
11
+ export class ComponentRegistry {
12
+ private components = new Map<string, LiveComponent>()
13
+ private definitions = new Map<string, ComponentDefinition>()
14
+ private rooms = new Map<string, Set<string>>() // roomId -> componentIds
15
+ private wsConnections = new Map<string, any>() // componentId -> websocket
16
+ private autoDiscoveredComponents = new Map<string, any>() // Auto-discovered component classes
17
+
18
+ // Register component definition
19
+ registerComponent<TState>(definition: ComponentDefinition<TState>) {
20
+ this.definitions.set(definition.name, definition)
21
+ console.log(`📝 Registered component: ${definition.name}`)
22
+ }
23
+
24
+ // Register component class dynamically
25
+ registerComponentClass(name: string, componentClass: any) {
26
+ this.autoDiscoveredComponents.set(name, componentClass)
27
+ console.log(`🔍 Auto-discovered component: ${name}`)
28
+ }
29
+
30
+ // Auto-discover components from directory
31
+ async autoDiscoverComponents(componentsPath: string) {
32
+ try {
33
+ const fs = await import('fs')
34
+ const path = await import('path')
35
+
36
+ if (!fs.existsSync(componentsPath)) {
37
+ console.log(`⚠️ Components path not found: ${componentsPath}`)
38
+ return
39
+ }
40
+
41
+ const files = fs.readdirSync(componentsPath)
42
+
43
+ for (const file of files) {
44
+ if (file.endsWith('.ts') || file.endsWith('.js')) {
45
+ try {
46
+ const fullPath = path.join(componentsPath, file)
47
+ const module = await import(/* @vite-ignore */ fullPath)
48
+
49
+ // Look for exported classes that extend LiveComponent
50
+ Object.keys(module).forEach(exportName => {
51
+ const exportedItem = module[exportName]
52
+ if (typeof exportedItem === 'function' &&
53
+ exportedItem.prototype &&
54
+ this.isLiveComponentClass(exportedItem)) {
55
+
56
+ const componentName = exportName.replace(/Component$/, '')
57
+ this.registerComponentClass(componentName, exportedItem)
58
+ }
59
+ })
60
+ } catch (error) {
61
+ console.warn(`⚠️ Failed to load component from ${file}:`, error)
62
+ }
63
+ }
64
+ }
65
+ } catch (error) {
66
+ console.error('❌ Auto-discovery failed:', error)
67
+ }
68
+ }
69
+
70
+ // Check if a class extends LiveComponent
71
+ private isLiveComponentClass(cls: any): boolean {
72
+ try {
73
+ let prototype = cls.prototype
74
+ while (prototype) {
75
+ if (prototype.constructor.name === 'LiveComponent') {
76
+ return true
77
+ }
78
+ prototype = Object.getPrototypeOf(prototype)
79
+ }
80
+ return false
81
+ } catch {
82
+ return false
83
+ }
84
+ }
85
+
86
+ // Mount component instance
87
+ async mountComponent(
88
+ ws: any,
89
+ componentName: string,
90
+ props: any = {},
91
+ options?: { room?: string; userId?: string }
92
+ ): Promise<string> {
93
+ // Try to find component definition first
94
+ let definition = this.definitions.get(componentName)
95
+ let ComponentClass: any = null
96
+ let initialState: any = {}
97
+
98
+ if (definition) {
99
+ // Use registered definition
100
+ ComponentClass = definition.component
101
+ initialState = definition.initialState
102
+ } else {
103
+ // Try auto-discovered components
104
+ ComponentClass = this.autoDiscoveredComponents.get(componentName)
105
+ if (!ComponentClass) {
106
+ // Try variations of the name
107
+ const variations = [
108
+ componentName + 'Component',
109
+ componentName.charAt(0).toUpperCase() + componentName.slice(1) + 'Component',
110
+ componentName.charAt(0).toUpperCase() + componentName.slice(1)
111
+ ]
112
+
113
+ for (const variation of variations) {
114
+ ComponentClass = this.autoDiscoveredComponents.get(variation)
115
+ if (ComponentClass) break
116
+ }
117
+ }
118
+
119
+ if (!ComponentClass) {
120
+ const availableComponents = [
121
+ ...Array.from(this.definitions.keys()),
122
+ ...Array.from(this.autoDiscoveredComponents.keys())
123
+ ]
124
+ throw new Error(`Component '${componentName}' not found. Available: ${availableComponents.join(', ')}`)
125
+ }
126
+
127
+ // Create a default initial state for auto-discovered components
128
+ initialState = {}
129
+ }
130
+
131
+ // Create component instance with registry methods
132
+ const component = new ComponentClass(
133
+ { ...initialState, ...props },
134
+ ws,
135
+ options
136
+ )
137
+
138
+ // Inject registry methods
139
+ component.broadcastToRoom = (message: BroadcastMessage) => {
140
+ this.broadcastToRoom(message, component.id)
141
+ }
142
+
143
+ // Store component and connection
144
+ this.components.set(component.id, component)
145
+ this.wsConnections.set(component.id, ws)
146
+
147
+ // Subscribe to room if specified
148
+ if (options?.room) {
149
+ this.subscribeToRoom(component.id, options.room)
150
+ }
151
+
152
+ // Initialize WebSocket data if needed
153
+ if (!ws.data) {
154
+ ws.data = {
155
+ components: new Map(),
156
+ subscriptions: new Set(),
157
+ userId: options?.userId
158
+ } as WebSocketData
159
+ }
160
+
161
+ ws.data.components.set(component.id, component)
162
+
163
+ console.log(`🚀 Mounted component: ${componentName} (${component.id})`)
164
+
165
+ // Send initial state to client
166
+ component.emit('STATE_UPDATE', { state: component.getSerializableState() })
167
+
168
+ return component.id
169
+ }
170
+
171
+ // Unmount component
172
+ async unmountComponent(componentId: string) {
173
+ const component = this.components.get(componentId)
174
+ if (!component) return
175
+
176
+ // Cleanup
177
+ component.destroy()
178
+
179
+ // Remove from room subscriptions
180
+ this.unsubscribeFromAllRooms(componentId)
181
+
182
+ // Remove from maps
183
+ this.components.delete(componentId)
184
+ this.wsConnections.delete(componentId)
185
+
186
+ console.log(`🗑️ Unmounted component: ${componentId}`)
187
+ }
188
+
189
+ // Execute action on component
190
+ async executeAction(componentId: string, action: string, payload: any): Promise<any> {
191
+ const component = this.components.get(componentId)
192
+ if (!component) {
193
+ throw new Error(`Component '${componentId}' not found`)
194
+ }
195
+
196
+ try {
197
+ return await component.executeAction(action, payload)
198
+ } catch (error: any) {
199
+ console.error(`❌ Error executing action '${action}' on component '${componentId}':`, error.message)
200
+ throw error
201
+ }
202
+ }
203
+
204
+ // Update component property
205
+ updateProperty(componentId: string, property: string, value: any) {
206
+ const component = this.components.get(componentId)
207
+ if (!component) {
208
+ throw new Error(`Component '${componentId}' not found`)
209
+ }
210
+
211
+ // Update state
212
+ const updates = { [property]: value }
213
+ component.setState(updates)
214
+
215
+ console.log(`📝 Updated property '${property}' on component '${componentId}'`)
216
+ }
217
+
218
+ // Subscribe component to room
219
+ subscribeToRoom(componentId: string, roomId: string) {
220
+ if (!this.rooms.has(roomId)) {
221
+ this.rooms.set(roomId, new Set())
222
+ }
223
+
224
+ this.rooms.get(roomId)!.add(componentId)
225
+ console.log(`📡 Component '${componentId}' subscribed to room '${roomId}'`)
226
+ }
227
+
228
+ // Unsubscribe component from room
229
+ unsubscribeFromRoom(componentId: string, roomId: string) {
230
+ const room = this.rooms.get(roomId)
231
+ if (room) {
232
+ room.delete(componentId)
233
+ if (room.size === 0) {
234
+ this.rooms.delete(roomId)
235
+ }
236
+ }
237
+ console.log(`📡 Component '${componentId}' unsubscribed from room '${roomId}'`)
238
+ }
239
+
240
+ // Unsubscribe from all rooms
241
+ private unsubscribeFromAllRooms(componentId: string) {
242
+ for (const [roomId, components] of Array.from(this.rooms.entries())) {
243
+ if (components.has(componentId)) {
244
+ this.unsubscribeFromRoom(componentId, roomId)
245
+ }
246
+ }
247
+ }
248
+
249
+ // Broadcast message to room
250
+ broadcastToRoom(message: BroadcastMessage, senderComponentId?: string) {
251
+ if (!message.room) return
252
+
253
+ const roomComponents = this.rooms.get(message.room)
254
+ if (!roomComponents) return
255
+
256
+ const broadcastMessage: LiveMessage = {
257
+ type: 'BROADCAST',
258
+ componentId: senderComponentId || 'system',
259
+ payload: {
260
+ type: message.type,
261
+ data: message.payload
262
+ },
263
+ timestamp: Date.now(),
264
+ room: message.room
265
+ }
266
+
267
+ let broadcastCount = 0
268
+
269
+ for (const componentId of Array.from(roomComponents)) {
270
+ // Skip sender if excludeUser is specified
271
+ const component = this.components.get(componentId)
272
+ if (message.excludeUser && component?.userId === message.excludeUser) {
273
+ continue
274
+ }
275
+
276
+ const ws = this.wsConnections.get(componentId)
277
+ if (ws && ws.send) {
278
+ ws.send(JSON.stringify(broadcastMessage))
279
+ broadcastCount++
280
+ }
281
+ }
282
+
283
+ console.log(`📡 Broadcast '${message.type}' to room '${message.room}': ${broadcastCount} recipients`)
284
+ }
285
+
286
+ // Handle WebSocket message
287
+ async handleMessage(ws: any, message: LiveMessage): Promise<any> {
288
+ try {
289
+ switch (message.type) {
290
+ case 'COMPONENT_MOUNT':
291
+ const componentId = await this.mountComponent(
292
+ ws,
293
+ message.payload.component,
294
+ message.payload.props,
295
+ {
296
+ room: message.payload.room,
297
+ userId: message.userId
298
+ }
299
+ )
300
+ return { success: true, result: { componentId } }
301
+
302
+ case 'COMPONENT_UNMOUNT':
303
+ await this.unmountComponent(message.componentId)
304
+ return { success: true }
305
+
306
+ case 'CALL_ACTION':
307
+ // Execute action - response depends on expectResponse flag
308
+ const actionResult = await this.executeAction(
309
+ message.componentId,
310
+ message.action!,
311
+ message.payload
312
+ )
313
+
314
+ // If client expects response, return it
315
+ if (message.expectResponse) {
316
+ return { success: true, result: actionResult }
317
+ }
318
+
319
+ // Otherwise no return - if state changed, component will emit STATE_UPDATE automatically
320
+ return null
321
+
322
+ case 'PROPERTY_UPDATE':
323
+ this.updateProperty(
324
+ message.componentId,
325
+ message.property!,
326
+ message.payload.value
327
+ )
328
+ return { success: true }
329
+
330
+ default:
331
+ console.warn(`⚠️ Unknown message type: ${message.type}`)
332
+ return { success: false, error: 'Unknown message type' }
333
+ }
334
+ } catch (error: any) {
335
+ console.error('❌ Registry error:', error.message)
336
+
337
+ // Send error back to client
338
+ const errorMessage: LiveMessage = {
339
+ type: 'ERROR',
340
+ componentId: message.componentId || 'system',
341
+ payload: {
342
+ error: error.message,
343
+ action: message.action,
344
+ originalMessage: message.type
345
+ },
346
+ timestamp: Date.now()
347
+ }
348
+
349
+ ws.send(JSON.stringify(errorMessage))
350
+
351
+ return { success: false, error: error.message }
352
+ }
353
+ }
354
+
355
+ // Cleanup when WebSocket disconnects
356
+ cleanupConnection(ws: any) {
357
+ if (!ws.data?.components) return
358
+
359
+ const componentsToCleanup = Array.from(ws.data.components.keys()) as string[]
360
+
361
+ for (const componentId of componentsToCleanup) {
362
+ this.unmountComponent(componentId)
363
+ }
364
+
365
+ console.log(`🧹 Cleaned up ${componentsToCleanup.length} components from disconnected WebSocket`)
366
+ }
367
+
368
+ // Get statistics
369
+ getStats() {
370
+ return {
371
+ components: this.components.size,
372
+ definitions: this.definitions.size,
373
+ rooms: this.rooms.size,
374
+ connections: this.wsConnections.size,
375
+ roomDetails: Object.fromEntries(
376
+ Array.from(this.rooms.entries()).map(([roomId, components]) => [
377
+ roomId,
378
+ components.size
379
+ ])
380
+ )
381
+ }
382
+ }
383
+
384
+ // Get component by ID
385
+ getComponent(componentId: string): LiveComponent | undefined {
386
+ return this.components.get(componentId)
387
+ }
388
+
389
+ // Get all components in room
390
+ getRoomComponents(roomId: string): LiveComponent[] {
391
+ const componentIds = this.rooms.get(roomId) || new Set()
392
+ return Array.from(componentIds)
393
+ .map(id => this.components.get(id))
394
+ .filter(Boolean) as LiveComponent[]
395
+ }
396
+ }
397
+
398
+ // Global registry instance
399
+ export const componentRegistry = new ComponentRegistry()
@@ -0,0 +1,164 @@
1
+ // 🔥 FluxStack Live Components - Core Types
2
+
3
+ export interface LiveMessage {
4
+ type: 'COMPONENT_MOUNT' | 'COMPONENT_UNMOUNT' | 'COMPONENT_ACTION' | 'CALL_ACTION' | 'ACTION_RESPONSE' | 'PROPERTY_UPDATE' | 'STATE_UPDATE' | 'ERROR' | 'BROADCAST'
5
+ componentId: string
6
+ action?: string
7
+ property?: string
8
+ payload?: any
9
+ timestamp?: number
10
+ userId?: string
11
+ room?: string
12
+ // Request-Response system
13
+ requestId?: string
14
+ responseId?: string
15
+ expectResponse?: boolean
16
+ }
17
+
18
+ export interface ComponentState {
19
+ [key: string]: any
20
+ }
21
+
22
+ export interface LiveComponentInstance<TState = ComponentState, TActions = Record<string, Function>> {
23
+ id: string
24
+ state: TState
25
+ call: <T extends keyof TActions>(action: T, ...args: any[]) => Promise<any>
26
+ set: <K extends keyof TState>(property: K, value: TState[K]) => void
27
+ loading: boolean
28
+ errors: Record<string, string>
29
+ connected: boolean
30
+ room?: string
31
+ }
32
+
33
+ export interface WebSocketData {
34
+ components: Map<string, any>
35
+ userId?: string
36
+ subscriptions: Set<string>
37
+ }
38
+
39
+ export interface ComponentDefinition<TState = ComponentState> {
40
+ name: string
41
+ initialState: TState
42
+ component: new (initialState: TState, ws: any) => LiveComponent<TState>
43
+ }
44
+
45
+ export interface BroadcastMessage {
46
+ type: string
47
+ payload: any
48
+ room?: string
49
+ excludeUser?: string
50
+ }
51
+
52
+ export abstract class LiveComponent<TState = ComponentState> {
53
+ public readonly id: string
54
+ public state: TState
55
+ protected ws: any
56
+ protected room?: string
57
+ protected userId?: string
58
+ public broadcastToRoom: (message: BroadcastMessage) => void = () => {} // Will be injected by registry
59
+
60
+ constructor(initialState: TState, ws: any, options?: { room?: string; userId?: string }) {
61
+ this.id = this.generateId()
62
+ this.state = initialState
63
+ this.ws = ws
64
+ this.room = options?.room
65
+ this.userId = options?.userId
66
+ }
67
+
68
+ // State management
69
+ protected setState(updates: Partial<TState> | ((prev: TState) => Partial<TState>)) {
70
+ const newUpdates = typeof updates === 'function' ? updates(this.state) : updates
71
+ this.state = { ...this.state, ...newUpdates }
72
+ this.emit('STATE_UPDATE', { state: this.state })
73
+ }
74
+
75
+ // Execute action safely
76
+ public async executeAction(action: string, payload: any): Promise<any> {
77
+ try {
78
+ // Check if method exists
79
+ const method = (this as any)[action]
80
+ if (typeof method !== 'function') {
81
+ throw new Error(`Action '${action}' not found on component`)
82
+ }
83
+
84
+ // Execute method
85
+ const result = await method.call(this, payload)
86
+ return result
87
+ } catch (error: any) {
88
+ this.emit('ERROR', {
89
+ action,
90
+ error: error.message,
91
+ stack: error.stack
92
+ })
93
+ throw error
94
+ }
95
+ }
96
+
97
+ // Send message to client
98
+ protected emit(type: string, payload: any) {
99
+ const message: LiveMessage = {
100
+ type: type as any,
101
+ componentId: this.id,
102
+ payload,
103
+ timestamp: Date.now(),
104
+ userId: this.userId,
105
+ room: this.room
106
+ }
107
+
108
+ if (this.ws && this.ws.send) {
109
+ this.ws.send(JSON.stringify(message))
110
+ }
111
+ }
112
+
113
+ // Broadcast to all clients in room
114
+ protected broadcast(type: string, payload: any, excludeCurrentUser = false) {
115
+ const message: BroadcastMessage = {
116
+ type,
117
+ payload,
118
+ room: this.room,
119
+ excludeUser: excludeCurrentUser ? this.userId : undefined
120
+ }
121
+
122
+ // This will be handled by the registry
123
+ this.broadcastToRoom(message)
124
+ }
125
+
126
+ // Subscribe to room for multi-user features
127
+ protected async subscribeToRoom(roomId: string) {
128
+ this.room = roomId
129
+ // Registry will handle the actual subscription
130
+ }
131
+
132
+ // Unsubscribe from room
133
+ protected async unsubscribeFromRoom() {
134
+ this.room = undefined
135
+ // Registry will handle the actual unsubscription
136
+ }
137
+
138
+ // Generate unique ID
139
+ private generateId(): string {
140
+ return `live-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
141
+ }
142
+
143
+ // Cleanup when component is destroyed
144
+ public destroy() {
145
+ this.unsubscribeFromRoom()
146
+ // Override in subclasses for custom cleanup
147
+ }
148
+
149
+ // Get serializable state for client
150
+ public getSerializableState(): TState {
151
+ return this.state
152
+ }
153
+ }
154
+
155
+ // Utility types for better TypeScript experience
156
+ export type ComponentActions<T> = {
157
+ [K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never
158
+ }
159
+
160
+ export type ComponentProps<T extends LiveComponent> = T extends LiveComponent<infer TState> ? TState : never
161
+
162
+ export type ActionParameters<T, K extends keyof T> = T[K] extends (...args: infer P) => any ? P : never
163
+
164
+ export type ActionReturnType<T, K extends keyof T> = T[K] extends (...args: any[]) => infer R ? R : never