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,1017 @@
1
+ // 🔥 FluxStack Live Components - Enhanced Component Registry
2
+
3
+ import type {
4
+ LiveComponent,
5
+ LiveMessage,
6
+ BroadcastMessage,
7
+ ComponentDefinition,
8
+ WebSocketData
9
+ } from '../../types/types'
10
+ import { stateSignature, type SignedState } from './StateSignature'
11
+ import { performanceMonitor } from './LiveComponentPerformanceMonitor'
12
+
13
+ // Enhanced interfaces for registry improvements
14
+ export interface ComponentMetadata {
15
+ id: string
16
+ name: string
17
+ version: string
18
+ mountedAt: Date
19
+ lastActivity: Date
20
+ state: 'mounting' | 'active' | 'inactive' | 'error' | 'destroying'
21
+ healthStatus: 'healthy' | 'degraded' | 'unhealthy'
22
+ dependencies: string[]
23
+ services: Map<string, any>
24
+ metrics: ComponentMetrics
25
+ migrationHistory: StateMigration[]
26
+ }
27
+
28
+ export interface ComponentMetrics {
29
+ renderCount: number
30
+ actionCount: number
31
+ errorCount: number
32
+ averageRenderTime: number
33
+ memoryUsage: number
34
+ lastRenderTime?: number
35
+ }
36
+
37
+ export interface StateMigration {
38
+ fromVersion: string
39
+ toVersion: string
40
+ migratedAt: Date
41
+ success: boolean
42
+ error?: string
43
+ }
44
+
45
+ export interface ComponentDependency {
46
+ name: string
47
+ version: string
48
+ required: boolean
49
+ factory: () => any
50
+ }
51
+
52
+ export interface ComponentHealthCheck {
53
+ componentId: string
54
+ status: 'healthy' | 'degraded' | 'unhealthy'
55
+ lastCheck: Date
56
+ issues: string[]
57
+ metrics: ComponentMetrics
58
+ }
59
+
60
+ export interface ServiceContainer {
61
+ register<T>(name: string, factory: () => T): void
62
+ resolve<T>(name: string): T
63
+ has(name: string): boolean
64
+ }
65
+
66
+ export class ComponentRegistry {
67
+ private components = new Map<string, LiveComponent>()
68
+ private definitions = new Map<string, ComponentDefinition>()
69
+ private metadata = new Map<string, ComponentMetadata>()
70
+ private rooms = new Map<string, Set<string>>() // roomId -> componentIds
71
+ private wsConnections = new Map<string, any>() // componentId -> websocket
72
+ private autoDiscoveredComponents = new Map<string, any>() // Auto-discovered component classes
73
+ private dependencies = new Map<string, ComponentDependency[]>()
74
+ private services: ServiceContainer
75
+ private healthCheckInterval: NodeJS.Timeout
76
+ private recoveryStrategies = new Map<string, () => Promise<void>>()
77
+
78
+ constructor() {
79
+ this.services = this.createServiceContainer()
80
+ this.setupHealthMonitoring()
81
+ this.setupRecoveryStrategies()
82
+ }
83
+
84
+ private createServiceContainer(): ServiceContainer {
85
+ const services = new Map<string, any>()
86
+
87
+ return {
88
+ register<T>(name: string, factory: () => T): void {
89
+ services.set(name, factory)
90
+ },
91
+ resolve<T>(name: string): T {
92
+ const factory = services.get(name)
93
+ if (!factory) {
94
+ throw new Error(`Service '${name}' not found`)
95
+ }
96
+ return factory()
97
+ },
98
+ has(name: string): boolean {
99
+ return services.has(name)
100
+ }
101
+ }
102
+ }
103
+
104
+ private setupHealthMonitoring(): void {
105
+ // Health check every 30 seconds
106
+ this.healthCheckInterval = setInterval(() => {
107
+ this.performHealthChecks()
108
+ }, 30000)
109
+ }
110
+
111
+ private setupRecoveryStrategies(): void {
112
+ // Default recovery strategy for unhealthy components
113
+ this.recoveryStrategies.set('default', async () => {
114
+ console.log('🔄 Executing default recovery strategy')
115
+ // Restart unhealthy components
116
+ for (const [componentId, metadata] of this.metadata) {
117
+ if (metadata.healthStatus === 'unhealthy') {
118
+ await this.recoverComponent(componentId)
119
+ }
120
+ }
121
+ })
122
+ }
123
+
124
+ // Register component definition with versioning support
125
+ registerComponent<TState>(definition: ComponentDefinition<TState>, version: string = '1.0.0') {
126
+ // Store version separately in metadata when component is mounted
127
+ this.definitions.set(definition.name, definition)
128
+ console.log(`📝 Registered component: ${definition.name} v${version}`)
129
+ }
130
+
131
+ // Register component dependencies
132
+ registerDependencies(componentName: string, dependencies: ComponentDependency[]): void {
133
+ this.dependencies.set(componentName, dependencies)
134
+ console.log(`🔗 Registered dependencies for ${componentName}:`, dependencies.map(d => d.name))
135
+ }
136
+
137
+ // Register service in DI container
138
+ registerService<T>(name: string, factory: () => T): void {
139
+ this.services.register(name, factory)
140
+ console.log(`🔧 Registered service: ${name}`)
141
+ }
142
+
143
+ // Register component class dynamically
144
+ registerComponentClass(name: string, componentClass: any) {
145
+ this.autoDiscoveredComponents.set(name, componentClass)
146
+ console.log(`🔍 Auto-discovered component: ${name}`)
147
+ }
148
+
149
+ // Auto-discover components from directory
150
+ async autoDiscoverComponents(componentsPath: string) {
151
+ try {
152
+ const fs = await import('fs')
153
+ const path = await import('path')
154
+
155
+ if (!fs.existsSync(componentsPath)) {
156
+ console.log(`⚠️ Components path not found: ${componentsPath}`)
157
+ return
158
+ }
159
+
160
+ const files = fs.readdirSync(componentsPath)
161
+
162
+ for (const file of files) {
163
+ if (file.endsWith('.ts') || file.endsWith('.js')) {
164
+ try {
165
+ const fullPath = path.join(componentsPath, file)
166
+ const module = await import(/* @vite-ignore */ fullPath)
167
+
168
+ // Look for exported classes that extend LiveComponent
169
+ Object.keys(module).forEach(exportName => {
170
+ const exportedItem = module[exportName]
171
+ if (typeof exportedItem === 'function' &&
172
+ exportedItem.prototype &&
173
+ this.isLiveComponentClass(exportedItem)) {
174
+
175
+ const componentName = exportName.replace(/Component$/, '')
176
+ this.registerComponentClass(componentName, exportedItem)
177
+ }
178
+ })
179
+ } catch (error) {
180
+ console.warn(`⚠️ Failed to load component from ${file}:`, error)
181
+ }
182
+ }
183
+ }
184
+ } catch (error) {
185
+ console.error('❌ Auto-discovery failed:', error)
186
+ }
187
+ }
188
+
189
+ // Check if a class extends LiveComponent
190
+ private isLiveComponentClass(cls: any): boolean {
191
+ try {
192
+ let prototype = cls.prototype
193
+ while (prototype) {
194
+ if (prototype.constructor.name === 'LiveComponent') {
195
+ return true
196
+ }
197
+ prototype = Object.getPrototypeOf(prototype)
198
+ }
199
+ return false
200
+ } catch {
201
+ return false
202
+ }
203
+ }
204
+
205
+ // Enhanced component mounting with lifecycle management
206
+ async mountComponent(
207
+ ws: any,
208
+ componentName: string,
209
+ props: any = {},
210
+ options?: { room?: string; userId?: string; version?: string }
211
+ ): Promise<{ componentId: string; initialState: any; signedState: any }> {
212
+ const startTime = Date.now()
213
+
214
+ try {
215
+ // Validate dependencies
216
+ await this.validateDependencies(componentName)
217
+
218
+ // Try to find component definition first
219
+ let definition = this.definitions.get(componentName)
220
+ let ComponentClass: any = null
221
+ let initialState: any = {}
222
+
223
+ if (definition) {
224
+ // Use registered definition
225
+ ComponentClass = definition.component
226
+ initialState = definition.initialState
227
+ } else {
228
+ // Try auto-discovered components
229
+ ComponentClass = this.autoDiscoveredComponents.get(componentName)
230
+ if (!ComponentClass) {
231
+ // Try variations of the name
232
+ const variations = [
233
+ componentName + 'Component',
234
+ componentName.charAt(0).toUpperCase() + componentName.slice(1) + 'Component',
235
+ componentName.charAt(0).toUpperCase() + componentName.slice(1)
236
+ ]
237
+
238
+ for (const variation of variations) {
239
+ ComponentClass = this.autoDiscoveredComponents.get(variation)
240
+ if (ComponentClass) break
241
+ }
242
+ }
243
+
244
+ if (!ComponentClass) {
245
+ const availableComponents = [
246
+ ...Array.from(this.definitions.keys()),
247
+ ...Array.from(this.autoDiscoveredComponents.keys())
248
+ ]
249
+ throw new Error(`Component '${componentName}' not found. Available: ${availableComponents.join(', ')}`)
250
+ }
251
+
252
+ // Create a default initial state for auto-discovered components
253
+ initialState = {}
254
+ }
255
+
256
+ // Create component instance with registry methods
257
+ const component = new ComponentClass(
258
+ { ...initialState, ...props },
259
+ ws,
260
+ options
261
+ )
262
+
263
+ // Inject registry methods
264
+ component.broadcastToRoom = (message: BroadcastMessage) => {
265
+ this.broadcastToRoom(message, component.id)
266
+ }
267
+
268
+ // Create and store component metadata
269
+ const metadata = this.createComponentMetadata(component.id, componentName, options?.version)
270
+ this.metadata.set(component.id, metadata)
271
+
272
+ // Inject services into component
273
+ const dependencies = this.dependencies.get(componentName) || []
274
+ for (const dep of dependencies) {
275
+ if (this.services.has(dep.name)) {
276
+ const service = this.services.resolve(dep.name)
277
+ metadata.services.set(dep.name, service)
278
+ // Inject service into component if it has a setter
279
+ if (typeof (component as any)[`set${dep.name}`] === 'function') {
280
+ (component as any)[`set${dep.name}`](service)
281
+ }
282
+ }
283
+ }
284
+
285
+ // Store component and connection
286
+ this.components.set(component.id, component)
287
+ this.wsConnections.set(component.id, ws)
288
+
289
+ // Subscribe to room if specified
290
+ if (options?.room) {
291
+ this.subscribeToRoom(component.id, options.room)
292
+ }
293
+
294
+ // Initialize WebSocket data if needed
295
+ if (!ws || typeof ws !== 'object') {
296
+ throw new Error('Invalid WebSocket object provided')
297
+ }
298
+
299
+ if (!ws.data) {
300
+ ws.data = {
301
+ components: new Map(),
302
+ subscriptions: new Set(),
303
+ userId: options?.userId
304
+ } as WebSocketData
305
+ }
306
+
307
+ // Ensure components map exists
308
+ if (!ws.data.components) {
309
+ ws.data.components = new Map()
310
+ }
311
+
312
+ ws.data.components.set(component.id, component)
313
+
314
+ // Update metadata state
315
+ metadata.state = 'active'
316
+ const renderTime = Date.now() - startTime
317
+ this.recordComponentMetrics(component.id, renderTime)
318
+
319
+ // Initialize performance monitoring
320
+ performanceMonitor.initializeComponent(component.id, componentName)
321
+ performanceMonitor.recordRenderTime(component.id, renderTime)
322
+
323
+ console.log(`🚀 Mounted component: ${componentName} (${component.id}) in ${renderTime}ms`)
324
+
325
+ // Send initial state to client with signature
326
+ const signedState = await stateSignature.signState(component.id, component.getSerializableState(), 1, {
327
+ compress: true,
328
+ backup: true
329
+ })
330
+ ;(component as any).emit('STATE_UPDATE', {
331
+ state: component.getSerializableState(),
332
+ signedState
333
+ })
334
+
335
+ // Return component ID with signed state for immediate persistence
336
+ return {
337
+ componentId: component.id,
338
+ initialState: component.getSerializableState(),
339
+ signedState
340
+ }
341
+ } catch (error: any) {
342
+ console.error(`❌ Failed to mount component ${componentName}:`, error)
343
+ throw error
344
+ }
345
+ }
346
+
347
+ // Re-hydrate component with signed client state
348
+ async rehydrateComponent(
349
+ componentId: string,
350
+ componentName: string,
351
+ signedState: SignedState,
352
+ ws: any,
353
+ options?: { room?: string; userId?: string }
354
+ ): Promise<{ success: boolean; newComponentId?: string; error?: string }> {
355
+ console.log('🔄 Attempting component re-hydration:', {
356
+ oldComponentId: componentId,
357
+ componentName,
358
+ signedState: {
359
+ timestamp: signedState.timestamp,
360
+ version: signedState.version,
361
+ signature: signedState.signature.substring(0, 16) + '...'
362
+ }
363
+ })
364
+
365
+ try {
366
+ // Validate signed state integrity
367
+ const validation = await stateSignature.validateState(signedState)
368
+ if (!validation.valid) {
369
+ console.warn('❌ State signature validation failed:', validation.error)
370
+ return {
371
+ success: false,
372
+ error: validation.error || 'Invalid state signature'
373
+ }
374
+ }
375
+
376
+ // Try to find component definition (same logic as mountComponent)
377
+ let definition = this.definitions.get(componentName)
378
+ let ComponentClass: any = null
379
+ let initialState: any = {}
380
+
381
+ if (definition) {
382
+ // Use registered definition
383
+ ComponentClass = definition.component
384
+ initialState = definition.initialState
385
+ } else {
386
+ // Try auto-discovered components
387
+ ComponentClass = this.autoDiscoveredComponents.get(componentName)
388
+ if (!ComponentClass) {
389
+ // Try variations of the name
390
+ const variations = [
391
+ componentName + 'Component',
392
+ componentName.charAt(0).toUpperCase() + componentName.slice(1) + 'Component',
393
+ componentName.charAt(0).toUpperCase() + componentName.slice(1)
394
+ ]
395
+
396
+ for (const variation of variations) {
397
+ ComponentClass = this.autoDiscoveredComponents.get(variation)
398
+ if (ComponentClass) break
399
+ }
400
+ }
401
+
402
+ if (!ComponentClass) {
403
+ const availableComponents = [
404
+ ...Array.from(this.definitions.keys()),
405
+ ...Array.from(this.autoDiscoveredComponents.keys())
406
+ ]
407
+ return {
408
+ success: false,
409
+ error: `Component '${componentName}' not found. Available: ${availableComponents.join(', ')}`
410
+ }
411
+ }
412
+ }
413
+
414
+ // Extract validated state
415
+ const clientState = await stateSignature.extractData(signedState)
416
+
417
+ // Create new component instance with client state (merge with initial state if from definition)
418
+ const finalState = definition ? { ...initialState, ...clientState } : clientState
419
+ const component = new ComponentClass(finalState, ws, options)
420
+
421
+ // Store component
422
+ this.components.set(component.id, component)
423
+ this.wsConnections.set(component.id, ws)
424
+
425
+ // Setup room if specified
426
+ if (options?.room) {
427
+ this.subscribeToRoom(component.id, options.room)
428
+ }
429
+
430
+ // Initialize WebSocket data
431
+ if (!ws.data) {
432
+ ws.data = {
433
+ components: new Map(),
434
+ subscriptions: new Set(),
435
+ userId: options?.userId
436
+ } as WebSocketData
437
+ }
438
+
439
+ // Ensure components map exists
440
+ if (!ws.data.components) {
441
+ ws.data.components = new Map()
442
+ }
443
+ ws.data.components.set(component.id, component)
444
+
445
+ console.log('✅ Component re-hydrated successfully:', {
446
+ oldComponentId: componentId,
447
+ newComponentId: component.id,
448
+ componentName,
449
+ stateVersion: signedState.version
450
+ })
451
+
452
+ // Send updated state to client (with new signature)
453
+ const newSignedState = await stateSignature.signState(
454
+ component.id,
455
+ component.getSerializableState(),
456
+ signedState.version + 1
457
+ )
458
+
459
+ component.emit('STATE_REHYDRATED', {
460
+ state: component.getSerializableState(),
461
+ signedState: newSignedState,
462
+ oldComponentId: componentId,
463
+ newComponentId: component.id
464
+ })
465
+
466
+ return {
467
+ success: true,
468
+ newComponentId: component.id
469
+ }
470
+
471
+ } catch (error: any) {
472
+ console.error('❌ Component re-hydration failed:', error.message)
473
+ return {
474
+ success: false,
475
+ error: error.message
476
+ }
477
+ }
478
+ }
479
+
480
+ // Unmount component
481
+ async unmountComponent(componentId: string) {
482
+ const component = this.components.get(componentId)
483
+ if (!component) return
484
+
485
+ // Cleanup
486
+ component.destroy()
487
+
488
+ // Remove from room subscriptions
489
+ this.unsubscribeFromAllRooms(componentId)
490
+
491
+ // Remove from maps
492
+ this.components.delete(componentId)
493
+ this.wsConnections.delete(componentId)
494
+
495
+ console.log(`🗑️ Unmounted component: ${componentId}`)
496
+ }
497
+
498
+ // Execute action on component
499
+ async executeAction(componentId: string, action: string, payload: any): Promise<any> {
500
+ const component = this.components.get(componentId)
501
+ if (!component) {
502
+ console.log(`🔄 Component '${componentId}' not found - triggering re-hydration request`)
503
+ // Return special error that triggers re-hydration on client
504
+ throw new Error(`COMPONENT_REHYDRATION_REQUIRED:${componentId}`)
505
+ }
506
+
507
+ try {
508
+ return await component.executeAction(action, payload)
509
+ } catch (error: any) {
510
+ console.error(`❌ Error executing action '${action}' on component '${componentId}':`, error.message)
511
+ throw error
512
+ }
513
+ }
514
+
515
+ // Update component property
516
+ updateProperty(componentId: string, property: string, value: any) {
517
+ const component = this.components.get(componentId)
518
+ if (!component) {
519
+ throw new Error(`Component '${componentId}' not found`)
520
+ }
521
+
522
+ // Update state
523
+ const updates = { [property]: value }
524
+ component.setState(updates)
525
+
526
+ console.log(`📝 Updated property '${property}' on component '${componentId}'`)
527
+ }
528
+
529
+ // Subscribe component to room
530
+ subscribeToRoom(componentId: string, roomId: string) {
531
+ if (!this.rooms.has(roomId)) {
532
+ this.rooms.set(roomId, new Set())
533
+ }
534
+
535
+ this.rooms.get(roomId)!.add(componentId)
536
+ console.log(`📡 Component '${componentId}' subscribed to room '${roomId}'`)
537
+ }
538
+
539
+ // Unsubscribe component from room
540
+ unsubscribeFromRoom(componentId: string, roomId: string) {
541
+ const room = this.rooms.get(roomId)
542
+ if (room) {
543
+ room.delete(componentId)
544
+ if (room.size === 0) {
545
+ this.rooms.delete(roomId)
546
+ }
547
+ }
548
+ console.log(`📡 Component '${componentId}' unsubscribed from room '${roomId}'`)
549
+ }
550
+
551
+ // Unsubscribe from all rooms
552
+ private unsubscribeFromAllRooms(componentId: string) {
553
+ for (const [roomId, components] of Array.from(this.rooms.entries())) {
554
+ if (components.has(componentId)) {
555
+ this.unsubscribeFromRoom(componentId, roomId)
556
+ }
557
+ }
558
+ }
559
+
560
+ // Broadcast message to room
561
+ broadcastToRoom(message: BroadcastMessage, senderComponentId?: string) {
562
+ if (!message.room) return
563
+
564
+ const roomComponents = this.rooms.get(message.room)
565
+ if (!roomComponents) return
566
+
567
+ const broadcastMessage: LiveMessage = {
568
+ type: 'BROADCAST',
569
+ componentId: senderComponentId || 'system',
570
+ payload: {
571
+ type: message.type,
572
+ data: message.payload
573
+ },
574
+ timestamp: Date.now(),
575
+ room: message.room
576
+ }
577
+
578
+ let broadcastCount = 0
579
+
580
+ for (const componentId of Array.from(roomComponents)) {
581
+ // Skip sender if excludeUser is specified
582
+ const component = this.components.get(componentId)
583
+ if (message.excludeUser && component?.userId === message.excludeUser) {
584
+ continue
585
+ }
586
+
587
+ const ws = this.wsConnections.get(componentId)
588
+ if (ws && ws.send) {
589
+ ws.send(JSON.stringify(broadcastMessage))
590
+ broadcastCount++
591
+ }
592
+ }
593
+
594
+ console.log(`📡 Broadcast '${message.type}' to room '${message.room}': ${broadcastCount} recipients`)
595
+ }
596
+
597
+ // Handle WebSocket message with enhanced metrics and lifecycle tracking
598
+ async handleMessage(ws: any, message: LiveMessage): Promise<any> {
599
+ const startTime = Date.now()
600
+
601
+ try {
602
+ // Update component activity
603
+ if (message.componentId) {
604
+ this.updateComponentActivity(message.componentId)
605
+ }
606
+
607
+ switch (message.type) {
608
+ case 'COMPONENT_MOUNT':
609
+ const mountResult = await this.mountComponent(
610
+ ws,
611
+ message.payload.component,
612
+ message.payload.props,
613
+ {
614
+ room: message.payload.room,
615
+ userId: message.userId
616
+ }
617
+ )
618
+ return { success: true, result: mountResult }
619
+
620
+ case 'COMPONENT_UNMOUNT':
621
+ await this.unmountComponent(message.componentId)
622
+ return { success: true }
623
+
624
+ case 'CALL_ACTION':
625
+ // Record action metrics
626
+ this.recordComponentMetrics(message.componentId, undefined, message.action)
627
+
628
+ // Execute action with performance monitoring
629
+ const actionStartTime = Date.now()
630
+ let actionError: Error | undefined
631
+
632
+ try {
633
+ const actionResult = await this.executeAction(
634
+ message.componentId,
635
+ message.action!,
636
+ message.payload
637
+ )
638
+
639
+ // Record successful action performance
640
+ const actionTime = Date.now() - actionStartTime
641
+ performanceMonitor.recordActionTime(message.componentId, message.action!, actionTime)
642
+
643
+ // If client expects response, return it
644
+ if (message.expectResponse) {
645
+ return { success: true, result: actionResult }
646
+ }
647
+
648
+ // Otherwise no return - if state changed, component will emit STATE_UPDATE automatically
649
+ return null
650
+ } catch (error) {
651
+ actionError = error as Error
652
+ const actionTime = Date.now() - actionStartTime
653
+ performanceMonitor.recordActionTime(message.componentId, message.action!, actionTime, actionError)
654
+ throw error
655
+ }
656
+
657
+
658
+ case 'PROPERTY_UPDATE':
659
+ this.updateProperty(
660
+ message.componentId,
661
+ message.property!,
662
+ message.payload.value
663
+ )
664
+ return { success: true }
665
+
666
+ default:
667
+ console.warn(`⚠️ Unknown message type: ${message.type}`)
668
+ return { success: false, error: 'Unknown message type' }
669
+ }
670
+ } catch (error: any) {
671
+ console.error('❌ Registry error:', error.message)
672
+
673
+ // Record error metrics if component ID is available
674
+ if (message.componentId) {
675
+ this.recordComponentError(message.componentId, error)
676
+ }
677
+
678
+ // Return error for handleActionCall to process
679
+ return { success: false, error: error.message }
680
+ } finally {
681
+ // Record processing time
682
+ const processingTime = Date.now() - startTime
683
+ if (message.componentId && processingTime > 0) {
684
+ this.recordComponentMetrics(message.componentId, processingTime)
685
+ }
686
+ }
687
+ }
688
+
689
+ // Cleanup when WebSocket disconnects
690
+ cleanupConnection(ws: any) {
691
+ if (!ws.data?.components) return
692
+
693
+ const componentsToCleanup = Array.from(ws.data.components.keys()) as string[]
694
+
695
+ console.log(`🧹 Cleaning up ${componentsToCleanup.length} components for disconnected WebSocket`)
696
+
697
+ for (const componentId of componentsToCleanup) {
698
+ this.cleanupComponent(componentId)
699
+ }
700
+
701
+ // Clear the WebSocket's component map
702
+ ws.data.components.clear()
703
+
704
+ console.log(`🧹 Cleaned up ${componentsToCleanup.length} components from disconnected WebSocket`)
705
+ }
706
+
707
+ // Get statistics
708
+ getStats() {
709
+ return {
710
+ components: this.components.size,
711
+ definitions: this.definitions.size,
712
+ rooms: this.rooms.size,
713
+ connections: this.wsConnections.size,
714
+ roomDetails: Object.fromEntries(
715
+ Array.from(this.rooms.entries()).map(([roomId, components]) => [
716
+ roomId,
717
+ components.size
718
+ ])
719
+ )
720
+ }
721
+ }
722
+
723
+ // Get component by ID
724
+ getComponent(componentId: string): LiveComponent | undefined {
725
+ return this.components.get(componentId)
726
+ }
727
+
728
+ // Get all components in room
729
+ getRoomComponents(roomId: string): LiveComponent[] {
730
+ const componentIds = this.rooms.get(roomId) || new Set()
731
+ return Array.from(componentIds)
732
+ .map(id => this.components.get(id))
733
+ .filter(Boolean) as LiveComponent[]
734
+ }
735
+ // Validate component dependencies
736
+ private async validateDependencies(componentName: string): Promise<void> {
737
+ const dependencies = this.dependencies.get(componentName)
738
+ if (!dependencies) return
739
+
740
+ for (const dep of dependencies) {
741
+ if (dep.required && !this.services.has(dep.name)) {
742
+ throw new Error(`Required dependency '${dep.name}' not found for component '${componentName}'`)
743
+ }
744
+ }
745
+ }
746
+
747
+ // Create component metadata
748
+ private createComponentMetadata(componentId: string, componentName: string, version: string = '1.0.0'): ComponentMetadata {
749
+ return {
750
+ id: componentId,
751
+ name: componentName,
752
+ version,
753
+ mountedAt: new Date(),
754
+ lastActivity: new Date(),
755
+ state: 'mounting',
756
+ healthStatus: 'healthy',
757
+ dependencies: this.dependencies.get(componentName)?.map(d => d.name) || [],
758
+ services: new Map(),
759
+ metrics: {
760
+ renderCount: 0,
761
+ actionCount: 0,
762
+ errorCount: 0,
763
+ averageRenderTime: 0,
764
+ memoryUsage: 0
765
+ },
766
+ migrationHistory: []
767
+ }
768
+ }
769
+
770
+ // Update component activity
771
+ updateComponentActivity(componentId: string): void {
772
+ const metadata = this.metadata.get(componentId)
773
+ if (metadata) {
774
+ metadata.lastActivity = new Date()
775
+ metadata.state = 'active'
776
+ }
777
+ }
778
+
779
+ // Record component metrics
780
+ recordComponentMetrics(componentId: string, renderTime?: number, action?: string): void {
781
+ const metadata = this.metadata.get(componentId)
782
+ if (!metadata) return
783
+
784
+ if (renderTime) {
785
+ metadata.metrics.renderCount++
786
+ metadata.metrics.averageRenderTime =
787
+ (metadata.metrics.averageRenderTime * (metadata.metrics.renderCount - 1) + renderTime) / metadata.metrics.renderCount
788
+ metadata.metrics.lastRenderTime = renderTime
789
+ }
790
+
791
+ if (action) {
792
+ metadata.metrics.actionCount++
793
+ }
794
+
795
+ this.updateComponentActivity(componentId)
796
+ }
797
+
798
+ // Record component error
799
+ recordComponentError(componentId: string, error: Error): void {
800
+ const metadata = this.metadata.get(componentId)
801
+ if (metadata) {
802
+ metadata.metrics.errorCount++
803
+ metadata.healthStatus = metadata.metrics.errorCount > 5 ? 'unhealthy' : 'degraded'
804
+ console.error(`❌ Component ${componentId} error:`, error.message)
805
+ }
806
+ }
807
+
808
+ // Perform health checks on all components
809
+ private async performHealthChecks(): Promise<void> {
810
+ const healthChecks: ComponentHealthCheck[] = []
811
+
812
+ for (const [componentId, metadata] of this.metadata) {
813
+ const component = this.components.get(componentId)
814
+ if (!component) continue
815
+
816
+ const issues: string[] = []
817
+ let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'
818
+
819
+ // Check if component is responsive
820
+ const timeSinceLastActivity = Date.now() - metadata.lastActivity.getTime()
821
+ if (timeSinceLastActivity > 300000) { // 5 minutes
822
+ issues.push('Component inactive for more than 5 minutes')
823
+ status = 'degraded'
824
+ }
825
+
826
+ // Check error rate
827
+ if (metadata.metrics.errorCount > 10) {
828
+ issues.push('High error rate detected')
829
+ status = 'unhealthy'
830
+ }
831
+
832
+ // Check memory usage (if available)
833
+ if (metadata.metrics.memoryUsage > 100 * 1024 * 1024) { // 100MB
834
+ issues.push('High memory usage detected')
835
+ status = 'degraded'
836
+ }
837
+
838
+ metadata.healthStatus = status
839
+
840
+ healthChecks.push({
841
+ componentId,
842
+ status,
843
+ lastCheck: new Date(),
844
+ issues,
845
+ metrics: { ...metadata.metrics }
846
+ })
847
+ }
848
+
849
+ // Log unhealthy components
850
+ const unhealthyComponents = healthChecks.filter(hc => hc.status === 'unhealthy')
851
+ if (unhealthyComponents.length > 0) {
852
+ console.warn(`⚠️ Found ${unhealthyComponents.length} unhealthy components:`,
853
+ unhealthyComponents.map(hc => hc.componentId))
854
+
855
+ // Trigger recovery if needed
856
+ await this.triggerRecovery()
857
+ }
858
+ }
859
+
860
+ // Trigger recovery for unhealthy components
861
+ private async triggerRecovery(): Promise<void> {
862
+ const defaultStrategy = this.recoveryStrategies.get('default')
863
+ if (defaultStrategy) {
864
+ try {
865
+ await defaultStrategy()
866
+ } catch (error) {
867
+ console.error('❌ Recovery strategy failed:', error)
868
+ }
869
+ }
870
+ }
871
+
872
+ // Recover a specific component
873
+ private async recoverComponent(componentId: string): Promise<void> {
874
+ const metadata = this.metadata.get(componentId)
875
+ const component = this.components.get(componentId)
876
+
877
+ if (!metadata || !component) return
878
+
879
+ try {
880
+ console.log(`🔄 Recovering component ${componentId}`)
881
+
882
+ // Reset error count
883
+ metadata.metrics.errorCount = 0
884
+ metadata.healthStatus = 'healthy'
885
+ metadata.state = 'active'
886
+
887
+ // Emit recovery event to client using the protected emit method
888
+ ;(component as any).emit('COMPONENT_RECOVERED', {
889
+ componentId,
890
+ timestamp: Date.now()
891
+ })
892
+
893
+ } catch (error) {
894
+ console.error(`❌ Failed to recover component ${componentId}:`, error)
895
+ metadata.state = 'error'
896
+ }
897
+ }
898
+
899
+ // Migrate component state to new version
900
+ async migrateComponentState(componentId: string, fromVersion: string, toVersion: string, migrationFn: (state: any) => any): Promise<boolean> {
901
+ const component = this.components.get(componentId)
902
+ const metadata = this.metadata.get(componentId)
903
+
904
+ if (!component || !metadata) return false
905
+
906
+ try {
907
+ console.log(`🔄 Migrating component ${componentId} from v${fromVersion} to v${toVersion}`)
908
+
909
+ const oldState = component.getSerializableState()
910
+ const newState = migrationFn(oldState)
911
+
912
+ // Update component state
913
+ component.setState(newState)
914
+
915
+ // Record migration
916
+ const migration: StateMigration = {
917
+ fromVersion,
918
+ toVersion,
919
+ migratedAt: new Date(),
920
+ success: true
921
+ }
922
+
923
+ metadata.migrationHistory.push(migration)
924
+ metadata.version = toVersion
925
+
926
+ console.log(`✅ Successfully migrated component ${componentId}`)
927
+ return true
928
+
929
+ } catch (error: any) {
930
+ console.error(`❌ Migration failed for component ${componentId}:`, error)
931
+
932
+ const migration: StateMigration = {
933
+ fromVersion,
934
+ toVersion,
935
+ migratedAt: new Date(),
936
+ success: false,
937
+ error: error.message
938
+ }
939
+
940
+ metadata?.migrationHistory.push(migration)
941
+ return false
942
+ }
943
+ }
944
+
945
+ // Get component health status
946
+ getComponentHealth(componentId: string): ComponentHealthCheck | null {
947
+ const metadata = this.metadata.get(componentId)
948
+ if (!metadata) return null
949
+
950
+ return {
951
+ componentId,
952
+ status: metadata.healthStatus,
953
+ lastCheck: new Date(),
954
+ issues: [],
955
+ metrics: { ...metadata.metrics }
956
+ }
957
+ }
958
+
959
+ // Get all component health statuses
960
+ getAllComponentHealth(): ComponentHealthCheck[] {
961
+ return Array.from(this.metadata.values()).map(metadata => ({
962
+ componentId: metadata.id,
963
+ status: metadata.healthStatus,
964
+ lastCheck: new Date(),
965
+ issues: [],
966
+ metrics: { ...metadata.metrics }
967
+ }))
968
+ }
969
+
970
+ // Cleanup method to be called on shutdown
971
+ cleanup(): void {
972
+ if (this.healthCheckInterval) {
973
+ clearInterval(this.healthCheckInterval)
974
+ }
975
+
976
+ // Cleanup all components
977
+ for (const [componentId] of this.components) {
978
+ this.cleanupComponent(componentId)
979
+ }
980
+ }
981
+
982
+ // Enhanced cleanup for individual components
983
+ private cleanupComponent(componentId: string): void {
984
+ const component = this.components.get(componentId)
985
+ const metadata = this.metadata.get(componentId)
986
+
987
+ if (component) {
988
+ try {
989
+ component.destroy()
990
+ } catch (error) {
991
+ console.error(`❌ Error destroying component ${componentId}:`, error)
992
+ }
993
+ }
994
+
995
+ if (metadata) {
996
+ metadata.state = 'destroying'
997
+ }
998
+
999
+ // Remove from performance monitoring
1000
+ performanceMonitor.removeComponent(componentId)
1001
+
1002
+ this.components.delete(componentId)
1003
+ this.metadata.delete(componentId)
1004
+ this.wsConnections.delete(componentId)
1005
+
1006
+ // Remove from rooms
1007
+ for (const [roomId, componentIds] of this.rooms) {
1008
+ componentIds.delete(componentId)
1009
+ if (componentIds.size === 0) {
1010
+ this.rooms.delete(roomId)
1011
+ }
1012
+ }
1013
+ }
1014
+ }
1015
+
1016
+ // Global registry instance
1017
+ export const componentRegistry = new ComponentRegistry()