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,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
 
@@ -177,7 +177,7 @@ export const runtimeConfig = {
177
177
  * Auto-detect environment and create appropriate config
178
178
  */
179
179
  auto(overrides?: Partial<FluxStackConfig>): FluxStackConfig {
180
- const environment = env.get('NODE_ENV', 'development')
180
+ const environment = env.get('NODE_ENV', 'development') as 'development' | 'production' | 'test'
181
181
 
182
182
  let config: FluxStackConfig
183
183
 
@@ -245,7 +245,7 @@ export const configHelpers = {
245
245
  * Get database URL with validation
246
246
  */
247
247
  getDatabaseUrl(): string | null {
248
- const url = env.get('DATABASE_URL')
248
+ const url = env.get('DATABASE_URL') as string | undefined
249
249
 
250
250
  if (url) {
251
251
  envValidation.validate('DATABASE_URL',
@@ -264,7 +264,7 @@ export const configHelpers = {
264
264
  const origins = env.array('CORS_ORIGINS')
265
265
 
266
266
  if (origins.length === 0) {
267
- const environment = env.get('NODE_ENV', 'development')
267
+ const environment = env.get('NODE_ENV', 'development') as 'development' | 'production' | 'test'
268
268
 
269
269
  if (environment === 'development') {
270
270
  return ['http://localhost:3000', 'http://localhost:5173']