create-fluxstack 1.0.12 → 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 (215) 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/loader.ts +8 -32
  90. package/core/config/runtime-config.ts +3 -3
  91. package/core/config/schema.ts +84 -49
  92. package/core/framework/server.ts +30 -0
  93. package/core/index.ts +25 -0
  94. package/core/live/ComponentRegistry.ts +399 -0
  95. package/core/live/types.ts +164 -0
  96. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1201 -0
  97. package/core/plugins/built-in/live-components/index.ts +27 -0
  98. package/core/plugins/built-in/logger/index.ts +1 -1
  99. package/core/plugins/built-in/monitoring/index.ts +1 -1
  100. package/core/plugins/built-in/static/index.ts +1 -1
  101. package/core/plugins/built-in/swagger/index.ts +1 -1
  102. package/core/plugins/built-in/vite/index.ts +1 -1
  103. package/core/plugins/dependency-manager.ts +384 -0
  104. package/core/plugins/index.ts +5 -1
  105. package/core/plugins/manager.ts +7 -3
  106. package/core/plugins/registry.ts +88 -10
  107. package/core/plugins/types.ts +11 -11
  108. package/core/server/framework.ts +43 -0
  109. package/core/server/index.ts +11 -1
  110. package/core/server/live/ComponentRegistry.ts +1017 -0
  111. package/core/server/live/FileUploadManager.ts +272 -0
  112. package/core/server/live/LiveComponentPerformanceMonitor.ts +930 -0
  113. package/core/server/live/SingleConnectionManager.ts +0 -0
  114. package/core/server/live/StateSignature.ts +644 -0
  115. package/core/server/live/WebSocketConnectionManager.ts +688 -0
  116. package/core/server/live/websocket-plugin.ts +435 -0
  117. package/core/server/middleware/errorHandling.ts +141 -0
  118. package/core/server/middleware/index.ts +16 -0
  119. package/core/server/plugins/static-files-plugin.ts +232 -0
  120. package/core/server/services/BaseService.ts +95 -0
  121. package/core/server/services/ServiceContainer.ts +144 -0
  122. package/core/server/services/index.ts +9 -0
  123. package/core/templates/create-project.ts +46 -2
  124. package/core/testing/index.ts +10 -0
  125. package/core/testing/setup.ts +74 -0
  126. package/core/types/build.ts +38 -14
  127. package/core/types/types.ts +319 -0
  128. package/core/utils/env-runtime.ts +7 -0
  129. package/core/utils/errors/handlers.ts +264 -39
  130. package/core/utils/errors/index.ts +528 -18
  131. package/core/utils/errors/middleware.ts +114 -0
  132. package/core/utils/logger/formatters.ts +222 -0
  133. package/core/utils/logger/index.ts +167 -48
  134. package/core/utils/logger/middleware.ts +253 -0
  135. package/core/utils/logger/performance.ts +384 -0
  136. package/core/utils/logger/transports.ts +365 -0
  137. package/create-fluxstack.ts +296 -296
  138. package/fluxstack.config.ts +17 -1
  139. package/package-template.json +66 -66
  140. package/package.json +31 -6
  141. package/public/README.md +16 -0
  142. package/vite.config.ts +29 -14
  143. package/.claude/settings.local.json +0 -74
  144. package/.github/workflows/ci-build-tests.yml +0 -480
  145. package/.github/workflows/dependency-management.yml +0 -324
  146. package/.github/workflows/release-validation.yml +0 -355
  147. package/.kiro/specs/fluxstack-architecture-optimization/design.md +0 -700
  148. package/.kiro/specs/fluxstack-architecture-optimization/requirements.md +0 -127
  149. package/.kiro/specs/fluxstack-architecture-optimization/tasks.md +0 -330
  150. package/CLAUDE.md +0 -200
  151. package/Dockerfile +0 -58
  152. package/Dockerfile.backend +0 -52
  153. package/Dockerfile.frontend +0 -54
  154. package/README-Docker.md +0 -85
  155. package/ai-context/00-QUICK-START.md +0 -86
  156. package/ai-context/README.md +0 -88
  157. package/ai-context/development/eden-treaty-guide.md +0 -362
  158. package/ai-context/development/patterns.md +0 -382
  159. package/ai-context/development/plugins-guide.md +0 -572
  160. package/ai-context/examples/crud-complete.md +0 -626
  161. package/ai-context/project/architecture.md +0 -399
  162. package/ai-context/project/overview.md +0 -213
  163. package/ai-context/recent-changes/eden-treaty-refactor.md +0 -281
  164. package/ai-context/recent-changes/type-inference-fix.md +0 -223
  165. package/ai-context/reference/environment-vars.md +0 -384
  166. package/ai-context/reference/troubleshooting.md +0 -407
  167. package/app/client/src/components/TestPage.tsx +0 -453
  168. package/bun.lock +0 -1063
  169. package/bunfig.toml +0 -16
  170. package/core/__tests__/integration.test.ts +0 -227
  171. package/core/build/index.ts +0 -186
  172. package/core/config/__tests__/config-loader.test.ts +0 -591
  173. package/core/config/__tests__/config-merger.test.ts +0 -657
  174. package/core/config/__tests__/env-converter.test.ts +0 -372
  175. package/core/config/__tests__/env-processor.test.ts +0 -431
  176. package/core/config/__tests__/env.test.ts +0 -452
  177. package/core/config/__tests__/integration.test.ts +0 -418
  178. package/core/config/__tests__/loader.test.ts +0 -331
  179. package/core/config/__tests__/schema.test.ts +0 -129
  180. package/core/config/__tests__/validator.test.ts +0 -318
  181. package/core/framework/__tests__/server.test.ts +0 -233
  182. package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
  183. package/core/plugins/__tests__/manager.test.ts +0 -398
  184. package/core/plugins/__tests__/monitoring.test.ts +0 -401
  185. package/core/plugins/__tests__/registry.test.ts +0 -335
  186. package/core/utils/__tests__/errors.test.ts +0 -139
  187. package/core/utils/__tests__/helpers.test.ts +0 -297
  188. package/core/utils/__tests__/logger.test.ts +0 -141
  189. package/create-test-app.ts +0 -156
  190. package/docker-compose.microservices.yml +0 -75
  191. package/docker-compose.simple.yml +0 -57
  192. package/docker-compose.yml +0 -71
  193. package/eslint.config.js +0 -23
  194. package/flux-cli.ts +0 -214
  195. package/nginx-lb.conf +0 -37
  196. package/publish.sh +0 -63
  197. package/run-clean.ts +0 -26
  198. package/run-env-tests.ts +0 -313
  199. package/tailwind.config.js +0 -34
  200. package/tests/__mocks__/api.ts +0 -56
  201. package/tests/fixtures/users.ts +0 -69
  202. package/tests/integration/api/users.routes.test.ts +0 -221
  203. package/tests/setup.ts +0 -29
  204. package/tests/unit/app/client/App-simple.test.tsx +0 -56
  205. package/tests/unit/app/client/App.test.tsx.skip +0 -237
  206. package/tests/unit/app/client/eden-api.test.ts +0 -186
  207. package/tests/unit/app/client/simple.test.tsx +0 -23
  208. package/tests/unit/app/controllers/users.controller.test.ts +0 -150
  209. package/tests/unit/core/create-project.test.ts.skip +0 -95
  210. package/tests/unit/core/framework.test.ts +0 -144
  211. package/tests/unit/core/plugins/logger.test.ts.skip +0 -268
  212. package/tests/unit/core/plugins/vite.test.ts.disabled +0 -188
  213. package/tests/utils/test-helpers.ts +0 -61
  214. package/vitest.config.ts +0 -50
  215. package/workspace.json +0 -6
@@ -0,0 +1,373 @@
1
+ // 🔥 WebSocket Hook for Live Components
2
+
3
+ import { useState, useEffect, useCallback, useRef } from 'react'
4
+ import type { WebSocketMessage, WebSocketResponse } from '../../types/types'
5
+
6
+ // Re-export types for easier importing
7
+ export type { WebSocketMessage, WebSocketResponse }
8
+
9
+ export interface UseWebSocketOptions {
10
+ url?: string
11
+ autoConnect?: boolean
12
+ reconnectInterval?: number
13
+ maxReconnectAttempts?: number
14
+ debug?: boolean
15
+ }
16
+
17
+ export interface UseWebSocketReturn {
18
+ connected: boolean
19
+ connecting: boolean
20
+ error: string | null
21
+ connectionId: string | null
22
+ sendMessage: (message: WebSocketMessage) => Promise<WebSocketResponse | null>
23
+ sendMessageAndWait: (message: WebSocketMessage, timeout?: number) => Promise<any>
24
+ close: () => void
25
+ reconnect: () => void
26
+ messageHistory: WebSocketResponse[]
27
+ lastMessage: WebSocketResponse | null
28
+ onMessage: (callback: (message: WebSocketResponse) => void) => () => void
29
+ }
30
+
31
+ export function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketReturn {
32
+ // Get WebSocket URL dynamically based on current environment
33
+ const getWebSocketUrl = () => {
34
+ if (typeof window === 'undefined') return 'ws://localhost:3000/api/live/ws'
35
+
36
+ const hostname = window.location.hostname
37
+ const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1'
38
+
39
+ // In production, use current origin with ws/wss protocol
40
+ if (!isLocalhost) {
41
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
42
+ const url = `${protocol}//${window.location.host}/api/live/ws`
43
+ console.log('🔗 [WebSocket] Production URL:', url)
44
+ return url
45
+ }
46
+
47
+ // In development, use backend server (port 3000 for integrated mode)
48
+ const url = 'ws://localhost:3000/api/live/ws'
49
+ console.log('🔗 [WebSocket] Development URL:', url)
50
+ return url
51
+ }
52
+
53
+ const {
54
+ url = getWebSocketUrl(),
55
+ autoConnect = true,
56
+ reconnectInterval = 300, // Reduced from 3000ms to 1000ms for faster reconnects
57
+ maxReconnectAttempts = 5,
58
+ debug = false
59
+ } = options
60
+
61
+ const [connected, setConnected] = useState(false)
62
+ const [connecting, setConnecting] = useState(false)
63
+ const [error, setError] = useState<string | null>(null)
64
+ const [connectionId, setConnectionId] = useState<string | null>(null)
65
+ const [messageHistory, setMessageHistory] = useState<WebSocketResponse[]>([])
66
+ const [lastMessage, setLastMessage] = useState<WebSocketResponse | null>(null)
67
+
68
+ // Request-Response system
69
+ const pendingRequests = useRef<Map<string, {
70
+ resolve: (value: any) => void
71
+ reject: (error: any) => void
72
+ timeout: NodeJS.Timeout
73
+ }>>(new Map())
74
+
75
+ // Message callbacks for real-time processing
76
+ const messageCallbacksRef = useRef<Set<(message: WebSocketResponse) => void>>(new Set())
77
+
78
+ // Generate unique request ID
79
+ const generateRequestId = useCallback(() => {
80
+ return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
81
+ }, [])
82
+
83
+ const wsRef = useRef<WebSocket | null>(null)
84
+ const reconnectAttempts = useRef(0)
85
+ const reconnectTimeout = useRef<number | null>(null)
86
+ const messageCallbacks = useRef<Map<string, (response: WebSocketResponse) => void>>(new Map())
87
+
88
+ const log = useCallback((message: string, data?: any) => {
89
+ if (debug) {
90
+ console.log(`[useWebSocket] ${message}`, data)
91
+ }
92
+ }, [debug])
93
+
94
+ const connect = useCallback(() => {
95
+ if (wsRef.current?.readyState === WebSocket.CONNECTING) {
96
+ log('WebSocket already connecting, skipping...')
97
+ return
98
+ }
99
+
100
+ if (wsRef.current?.readyState === WebSocket.OPEN) {
101
+ log('WebSocket already connected, skipping...')
102
+ return
103
+ }
104
+
105
+ setConnecting(true)
106
+ setError(null)
107
+ log('Connecting to WebSocket', { url })
108
+
109
+ try {
110
+ const ws = new WebSocket(url)
111
+ wsRef.current = ws
112
+
113
+ ws.onopen = () => {
114
+ setConnected(true)
115
+ setConnecting(false)
116
+ reconnectAttempts.current = 0
117
+ log('Connected to WebSocket')
118
+ }
119
+
120
+ ws.onmessage = (event) => {
121
+ try {
122
+ const response: WebSocketResponse = JSON.parse(event.data)
123
+ log('Received message', response)
124
+
125
+ // Handle connection establishment
126
+ if (response.type === 'CONNECTION_ESTABLISHED') {
127
+ setConnectionId(response.connectionId || null)
128
+ }
129
+
130
+ // Handle request-response system
131
+ if (response.requestId && pendingRequests.current.has(response.requestId)) {
132
+ const request = pendingRequests.current.get(response.requestId)!
133
+ clearTimeout(request.timeout)
134
+ pendingRequests.current.delete(response.requestId)
135
+
136
+ if (response.success !== false) {
137
+ request.resolve(response) // Pass full response, not just result
138
+ } else {
139
+ // Don't reject COMPONENT_REHYDRATION_REQUIRED - let client handle it
140
+ if (response.error?.includes?.('COMPONENT_REHYDRATION_REQUIRED')) {
141
+ request.resolve(response) // Return response so client can handle re-hydration
142
+ } else {
143
+ request.reject(new Error(response.error || 'Request failed'))
144
+ }
145
+ }
146
+ return // Don't process further for request-response
147
+ }
148
+
149
+ // Handle message callbacks (legacy)
150
+ if (response.type === 'MESSAGE_RESPONSE' && response.componentId) {
151
+ const callback = messageCallbacks.current.get(response.componentId)
152
+ if (callback) {
153
+ callback(response)
154
+ messageCallbacks.current.delete(response.componentId)
155
+ }
156
+ }
157
+
158
+ // Update message history and last message
159
+ setMessageHistory(prev => [...prev.slice(-99), response])
160
+ setLastMessage(response)
161
+
162
+ // Call all registered message callbacks immediately
163
+ messageCallbacksRef.current.forEach(callback => {
164
+ try {
165
+ callback(response)
166
+ } catch (error) {
167
+ log('Error in message callback', error)
168
+ }
169
+ })
170
+
171
+ } catch (error) {
172
+ log('Failed to parse WebSocket message', error)
173
+ setError('Failed to parse message')
174
+ }
175
+ }
176
+
177
+ ws.onclose = () => {
178
+ setConnected(false)
179
+ setConnecting(false)
180
+ setConnectionId(null)
181
+ log('WebSocket connection closed')
182
+
183
+ // Auto-reconnect logic
184
+ if (reconnectAttempts.current < maxReconnectAttempts) {
185
+ reconnectAttempts.current++
186
+ log(`Attempting to reconnect (${reconnectAttempts.current}/${maxReconnectAttempts})`)
187
+
188
+ reconnectTimeout.current = window.setTimeout(() => {
189
+ connect()
190
+ }, reconnectInterval)
191
+ } else {
192
+ setError('Max reconnection attempts reached')
193
+ }
194
+ }
195
+
196
+ ws.onerror = (error) => {
197
+ log('WebSocket error', error)
198
+ setError('WebSocket connection error')
199
+ setConnecting(false)
200
+ }
201
+
202
+ } catch (error) {
203
+ setConnecting(false)
204
+ setError(error instanceof Error ? error.message : 'Connection failed')
205
+ log('Failed to create WebSocket connection', error)
206
+ }
207
+ }, [url, reconnectInterval, maxReconnectAttempts, log])
208
+
209
+ const sendMessage = useCallback(async (message: WebSocketMessage): Promise<WebSocketResponse | null> => {
210
+ return new Promise((resolve, reject) => {
211
+ if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
212
+ reject(new Error('WebSocket is not connected'))
213
+ return
214
+ }
215
+
216
+ // CALL_ACTION doesn't expect response - send and resolve immediately
217
+ if (message.type === 'CALL_ACTION') {
218
+ try {
219
+ const messageWithTimestamp = { ...message, timestamp: Date.now() }
220
+ wsRef.current.send(JSON.stringify(messageWithTimestamp))
221
+ log('Sent message', messageWithTimestamp)
222
+ resolve(null) // No response expected
223
+ return
224
+ } catch (error) {
225
+ reject(error)
226
+ return
227
+ }
228
+ }
229
+
230
+ // Generate unique message ID for response tracking (other message types)
231
+ const messageId = `${message.componentId || 'msg'}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
232
+
233
+ // Set up callback for response
234
+ if (message.componentId) {
235
+ messageCallbacks.current.set(message.componentId, (response) => {
236
+ resolve(response)
237
+ })
238
+
239
+ // Timeout after 10 seconds
240
+ setTimeout(() => {
241
+ if (messageCallbacks.current.has(message.componentId!)) {
242
+ messageCallbacks.current.delete(message.componentId!)
243
+ reject(new Error('Message timeout'))
244
+ }
245
+ }, 10000)
246
+ }
247
+
248
+ try {
249
+ const messageWithTimestamp = {
250
+ ...message,
251
+ timestamp: Date.now()
252
+ }
253
+
254
+ wsRef.current.send(JSON.stringify(messageWithTimestamp))
255
+ log('Sent message', messageWithTimestamp)
256
+
257
+ // If no component ID, resolve immediately
258
+ if (!message.componentId) {
259
+ resolve(null)
260
+ }
261
+ } catch (error) {
262
+ if (message.componentId) {
263
+ messageCallbacks.current.delete(message.componentId)
264
+ }
265
+ reject(error)
266
+ }
267
+ })
268
+ }, [log])
269
+
270
+ // Send message and wait for response with unique ID
271
+ const sendMessageAndWait = useCallback(async (
272
+ message: WebSocketMessage,
273
+ timeout: number = 10000
274
+ ): Promise<any> => {
275
+ return new Promise((resolve, reject) => {
276
+ if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
277
+ reject(new Error('WebSocket is not connected'))
278
+ return
279
+ }
280
+
281
+ const requestId = generateRequestId()
282
+
283
+ // Set up timeout
284
+ const timeoutHandle = setTimeout(() => {
285
+ pendingRequests.current.delete(requestId)
286
+ reject(new Error(`Request timeout after ${timeout}ms`))
287
+ }, timeout)
288
+
289
+ // Store the pending request
290
+ pendingRequests.current.set(requestId, {
291
+ resolve,
292
+ reject,
293
+ timeout: timeoutHandle
294
+ })
295
+
296
+ try {
297
+ const messageWithRequestId = {
298
+ ...message,
299
+ requestId,
300
+ expectResponse: true,
301
+ timestamp: Date.now()
302
+ }
303
+
304
+ wsRef.current.send(JSON.stringify(messageWithRequestId))
305
+ log('Sent message with request ID', { requestId, message: messageWithRequestId })
306
+ } catch (error) {
307
+ // Cleanup on send error
308
+ clearTimeout(timeoutHandle)
309
+ pendingRequests.current.delete(requestId)
310
+ reject(error)
311
+ }
312
+ })
313
+ }, [log, generateRequestId])
314
+
315
+ const close = useCallback(() => {
316
+ if (reconnectTimeout.current) {
317
+ clearTimeout(reconnectTimeout.current)
318
+ reconnectTimeout.current = null
319
+ }
320
+
321
+ if (wsRef.current) {
322
+ wsRef.current.close()
323
+ wsRef.current = null
324
+ }
325
+
326
+ reconnectAttempts.current = maxReconnectAttempts // Prevent auto-reconnect
327
+ setConnected(false)
328
+ setConnecting(false)
329
+ setConnectionId(null)
330
+ log('WebSocket connection closed manually')
331
+ }, [maxReconnectAttempts, log])
332
+
333
+ const reconnect = useCallback(() => {
334
+ close()
335
+ reconnectAttempts.current = 0
336
+ setTimeout(connect, 50) // Reduced delay from 100ms to 50ms
337
+ }, [close, connect])
338
+
339
+ // Auto-connect on mount
340
+ useEffect(() => {
341
+ if (autoConnect) {
342
+ connect()
343
+ }
344
+
345
+ return () => {
346
+ close()
347
+ }
348
+ }, [autoConnect, connect, close])
349
+
350
+ // Register message callback
351
+ const onMessage = useCallback((callback: (message: WebSocketResponse) => void) => {
352
+ messageCallbacksRef.current.add(callback)
353
+
354
+ // Return cleanup function
355
+ return () => {
356
+ messageCallbacksRef.current.delete(callback)
357
+ }
358
+ }, [])
359
+
360
+ return {
361
+ connected,
362
+ connecting,
363
+ error,
364
+ connectionId,
365
+ sendMessage,
366
+ sendMessageAndWait,
367
+ close,
368
+ reconnect,
369
+ messageHistory,
370
+ lastMessage,
371
+ onMessage
372
+ }
373
+ }
@@ -0,0 +1,47 @@
1
+ // 🔥 FluxStack Client Core - Main Export
2
+
3
+ // Hooks
4
+ export { useWebSocket } from './hooks/useWebSocket'
5
+ export { useHybridLiveComponent } from './hooks/useHybridLiveComponent'
6
+ export { useChunkedUpload } from './hooks/useChunkedUpload'
7
+ export { StateValidator } from './hooks/state-validator'
8
+
9
+ // Re-export types from core/types/types.ts for convenience
10
+ export type {
11
+ // Live Components types
12
+ LiveMessage,
13
+ ComponentState,
14
+ LiveComponentInstance,
15
+ WebSocketData,
16
+ ComponentDefinition,
17
+ BroadcastMessage,
18
+ LiveComponent,
19
+
20
+ // WebSocket types
21
+ WebSocketMessage,
22
+ WebSocketResponse,
23
+
24
+ // Hybrid Live Component types
25
+ HybridState,
26
+ StateValidation,
27
+ StateConflict,
28
+ HybridComponentOptions,
29
+
30
+ // File Upload types
31
+ FileChunkData,
32
+ FileUploadStartMessage,
33
+ FileUploadChunkMessage,
34
+ FileUploadCompleteMessage,
35
+ FileUploadProgressResponse,
36
+ FileUploadCompleteResponse,
37
+ ActiveUpload,
38
+
39
+ // Utility types
40
+ ComponentActions,
41
+ ComponentProps,
42
+ ActionParameters,
43
+ ActionReturnType
44
+ } from '../types/types'
45
+
46
+ // Hook return types
47
+ export type { UseHybridLiveComponentReturn } from './hooks/useHybridLiveComponent'
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Store Factory
3
+ * Core FluxStack state management utilities
4
+ */
5
+
6
+ import { create } from 'zustand'
7
+ import { persist, createJSONStorage } from 'zustand/middleware'
8
+
9
+ export interface StoreOptions<T> {
10
+ name?: string
11
+ persist?: boolean
12
+ storage?: 'localStorage' | 'sessionStorage'
13
+ version?: number
14
+ migrate?: (persistedState: unknown, version: number) => T
15
+ }
16
+
17
+ /**
18
+ * Create a Zustand store with FluxStack conventions
19
+ */
20
+ export function createFluxStore<T>(
21
+ storeFactory: (set: any, get: any) => T,
22
+ options: StoreOptions<T> = {}
23
+ ) {
24
+ const { name, persist: shouldPersist = false, storage = 'localStorage', version = 1, migrate } = options
25
+
26
+ if (shouldPersist && name) {
27
+ return create<T>()(
28
+ persist(
29
+ storeFactory,
30
+ {
31
+ name,
32
+ storage: createJSONStorage(() =>
33
+ storage === 'localStorage' ? localStorage : sessionStorage
34
+ ),
35
+ version,
36
+ migrate: migrate as any,
37
+ onRehydrateStorage: () => (state) => {
38
+ console.log('FluxStack: Store rehydrated', name, state)
39
+ }
40
+ }
41
+ )
42
+ )
43
+ }
44
+
45
+ return create<T>()(storeFactory)
46
+ }
47
+
48
+ /**
49
+ * Base user store interface
50
+ */
51
+ export interface BaseUser {
52
+ id: string
53
+ email: string
54
+ name: string
55
+ role: 'admin' | 'user'
56
+ }
57
+
58
+ export interface BaseUserStore {
59
+ currentUser: BaseUser | null
60
+ isAuthenticated: boolean
61
+ isLoading: boolean
62
+ error: string | null
63
+ login: (credentials: { email: string; password: string }) => Promise<void>
64
+ register: (data: { email: string; password: string; name: string }) => Promise<void>
65
+ logout: () => void
66
+ updateProfile: (data: Partial<BaseUser>) => Promise<void>
67
+ clearError: () => void
68
+ setLoading: (loading: boolean) => void
69
+ }
70
+
71
+ /**
72
+ * Create user store with FluxStack conventions
73
+ */
74
+ export function createUserStore(options: StoreOptions<BaseUserStore> = {}) {
75
+ return createFluxStore<BaseUserStore>(
76
+ (set, get) => ({
77
+ currentUser: null,
78
+ isAuthenticated: false,
79
+ isLoading: false,
80
+ error: null,
81
+
82
+ login: async (credentials) => {
83
+ set({ isLoading: true, error: null })
84
+ try {
85
+ const response = await fetch('/api/auth/login', {
86
+ method: 'POST',
87
+ headers: { 'Content-Type': 'application/json' },
88
+ body: JSON.stringify(credentials)
89
+ })
90
+
91
+ if (!response.ok) {
92
+ const error = await response.json()
93
+ throw new Error(error.message || 'Login failed')
94
+ }
95
+
96
+ const { user } = await response.json()
97
+ set({
98
+ currentUser: user,
99
+ isAuthenticated: true,
100
+ isLoading: false
101
+ })
102
+ } catch (error) {
103
+ set({
104
+ error: error instanceof Error ? error.message : 'Login failed',
105
+ isLoading: false
106
+ })
107
+ throw error
108
+ }
109
+ },
110
+
111
+ register: async (data) => {
112
+ set({ isLoading: true, error: null })
113
+ try {
114
+ const response = await fetch('/api/auth/register', {
115
+ method: 'POST',
116
+ headers: { 'Content-Type': 'application/json' },
117
+ body: JSON.stringify(data)
118
+ })
119
+
120
+ if (!response.ok) {
121
+ const error = await response.json()
122
+ throw new Error(error.message || 'Registration failed')
123
+ }
124
+
125
+ const { user } = await response.json()
126
+ set({
127
+ currentUser: user,
128
+ isAuthenticated: true,
129
+ isLoading: false
130
+ })
131
+ } catch (error) {
132
+ set({
133
+ error: error instanceof Error ? error.message : 'Registration failed',
134
+ isLoading: false
135
+ })
136
+ throw error
137
+ }
138
+ },
139
+
140
+ logout: () => {
141
+ // Call logout API
142
+ fetch('/api/auth/logout', { method: 'POST' }).catch(console.error)
143
+
144
+ set({
145
+ currentUser: null,
146
+ isAuthenticated: false,
147
+ error: null
148
+ })
149
+ },
150
+
151
+ updateProfile: async (data) => {
152
+ const { currentUser } = get()
153
+ if (!currentUser) {
154
+ throw new Error('No user logged in')
155
+ }
156
+
157
+ set({ isLoading: true, error: null })
158
+ try {
159
+ const response = await fetch('/api/user/profile', {
160
+ method: 'PUT',
161
+ headers: { 'Content-Type': 'application/json' },
162
+ body: JSON.stringify(data)
163
+ })
164
+
165
+ if (!response.ok) {
166
+ const error = await response.json()
167
+ throw new Error(error.message || 'Profile update failed')
168
+ }
169
+
170
+ const { user } = await response.json()
171
+ set({
172
+ currentUser: user,
173
+ isLoading: false
174
+ })
175
+ } catch (error) {
176
+ set({
177
+ error: error instanceof Error ? error.message : 'Profile update failed',
178
+ isLoading: false
179
+ })
180
+ throw error
181
+ }
182
+ },
183
+
184
+ clearError: () => set({ error: null }),
185
+ setLoading: (loading) => set({ isLoading: loading })
186
+ }),
187
+ {
188
+ name: 'user-store',
189
+ persist: true,
190
+ ...options
191
+ }
192
+ )
193
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Core Client State Management
3
+ * FluxStack state utilities exports
4
+ */
5
+
6
+ export {
7
+ createFluxStore,
8
+ createUserStore
9
+ } from './createStore'
10
+
11
+ export type {
12
+ StoreOptions,
13
+ BaseUser,
14
+ BaseUserStore
15
+ } from './createStore'
@@ -237,7 +237,7 @@ export class DynamicEnvironmentProcessor {
237
237
  * Enhanced environment info with dynamic access
238
238
  */
239
239
  export function getDynamicEnvironmentInfo() {
240
- const nodeEnv = env.get('NODE_ENV', 'development')
240
+ const nodeEnv = env.get('NODE_ENV', 'development') as 'development' | 'production' | 'test'
241
241
 
242
242
  return {
243
243
  name: nodeEnv,
@@ -46,7 +46,8 @@ export class EnvConverter {
46
46
  }
47
47
 
48
48
  static toBoolean(value: string | undefined, defaultValue: boolean): boolean {
49
- if (!value) return defaultValue
49
+ if (value === undefined) return defaultValue
50
+ if (value === '') return false
50
51
  return ['true', '1', 'yes', 'on'].includes(value.toLowerCase())
51
52
  }
52
53