create-fluxstack 1.10.1 → 1.12.1

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 (257) hide show
  1. package/.dockerignore +1 -2
  2. package/Dockerfile +8 -8
  3. package/LLMD/INDEX.md +64 -0
  4. package/LLMD/MAINTENANCE.md +197 -0
  5. package/LLMD/MIGRATION.md +156 -0
  6. package/LLMD/config/.gitkeep +1 -0
  7. package/LLMD/config/declarative-system.md +268 -0
  8. package/LLMD/config/environment-vars.md +327 -0
  9. package/LLMD/config/runtime-reload.md +401 -0
  10. package/LLMD/core/.gitkeep +1 -0
  11. package/LLMD/core/build-system.md +599 -0
  12. package/LLMD/core/framework-lifecycle.md +229 -0
  13. package/LLMD/core/plugin-system.md +451 -0
  14. package/LLMD/patterns/.gitkeep +1 -0
  15. package/LLMD/patterns/anti-patterns.md +297 -0
  16. package/LLMD/patterns/project-structure.md +264 -0
  17. package/LLMD/patterns/type-safety.md +440 -0
  18. package/LLMD/reference/.gitkeep +1 -0
  19. package/LLMD/reference/cli-commands.md +250 -0
  20. package/LLMD/reference/plugin-hooks.md +357 -0
  21. package/LLMD/reference/routing.md +39 -0
  22. package/LLMD/reference/troubleshooting.md +364 -0
  23. package/LLMD/resources/.gitkeep +1 -0
  24. package/LLMD/resources/controllers.md +465 -0
  25. package/LLMD/resources/live-components.md +703 -0
  26. package/LLMD/resources/live-rooms.md +482 -0
  27. package/LLMD/resources/live-upload.md +130 -0
  28. package/LLMD/resources/plugins-external.md +617 -0
  29. package/LLMD/resources/routes-eden.md +254 -0
  30. package/README.md +37 -17
  31. package/app/client/index.html +0 -1
  32. package/app/client/src/App.tsx +107 -150
  33. package/app/client/src/components/AppLayout.tsx +68 -0
  34. package/app/client/src/components/BackButton.tsx +13 -0
  35. package/app/client/src/components/DemoPage.tsx +20 -0
  36. package/app/client/src/components/LiveUploadWidget.tsx +204 -0
  37. package/app/client/src/lib/eden-api.ts +85 -60
  38. package/app/client/src/live/ChatDemo.tsx +107 -0
  39. package/app/client/src/live/CounterDemo.tsx +206 -0
  40. package/app/client/src/live/FormDemo.tsx +119 -0
  41. package/app/client/src/live/RoomChatDemo.tsx +161 -0
  42. package/app/client/src/live/UploadDemo.tsx +21 -0
  43. package/app/client/src/main.tsx +4 -1
  44. package/app/client/src/pages/ApiTestPage.tsx +108 -0
  45. package/app/client/src/pages/HomePage.tsx +76 -0
  46. package/app/server/app.ts +1 -4
  47. package/app/server/controllers/users.controller.ts +36 -44
  48. package/app/server/index.ts +25 -35
  49. package/app/server/live/LiveChat.ts +77 -0
  50. package/app/server/live/LiveCounter.ts +67 -0
  51. package/app/server/live/LiveForm.ts +63 -0
  52. package/app/server/live/LiveLocalCounter.ts +32 -0
  53. package/app/server/live/LiveRoomChat.ts +127 -0
  54. package/app/server/live/LiveUpload.ts +81 -0
  55. package/app/server/routes/index.ts +3 -1
  56. package/app/server/routes/room.routes.ts +117 -0
  57. package/app/server/routes/users.routes.ts +35 -27
  58. package/app/shared/types/index.ts +14 -2
  59. package/config/app.config.ts +2 -62
  60. package/config/client.config.ts +2 -95
  61. package/config/database.config.ts +2 -99
  62. package/config/fluxstack.config.ts +25 -45
  63. package/config/index.ts +57 -38
  64. package/config/monitoring.config.ts +2 -114
  65. package/config/plugins.config.ts +2 -80
  66. package/config/server.config.ts +2 -68
  67. package/config/services.config.ts +2 -130
  68. package/config/system/app.config.ts +29 -0
  69. package/config/system/build.config.ts +49 -0
  70. package/config/system/client.config.ts +68 -0
  71. package/config/system/database.config.ts +17 -0
  72. package/config/system/fluxstack.config.ts +114 -0
  73. package/config/{logger.config.ts → system/logger.config.ts} +3 -1
  74. package/config/system/monitoring.config.ts +114 -0
  75. package/config/system/plugins.config.ts +84 -0
  76. package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
  77. package/config/system/server.config.ts +68 -0
  78. package/config/system/services.config.ts +46 -0
  79. package/config/{system.config.ts → system/system.config.ts} +1 -1
  80. package/core/build/flux-plugins-generator.ts +325 -325
  81. package/core/build/index.ts +39 -27
  82. package/core/build/live-components-generator.ts +3 -3
  83. package/core/build/optimizer.ts +235 -235
  84. package/core/cli/command-registry.ts +6 -4
  85. package/core/cli/commands/build.ts +79 -0
  86. package/core/cli/commands/create.ts +54 -0
  87. package/core/cli/commands/dev.ts +101 -0
  88. package/core/cli/commands/help.ts +34 -0
  89. package/core/cli/commands/index.ts +34 -0
  90. package/core/cli/commands/make-plugin.ts +90 -0
  91. package/core/cli/commands/plugin-add.ts +197 -0
  92. package/core/cli/commands/plugin-deps.ts +2 -2
  93. package/core/cli/commands/plugin-list.ts +208 -0
  94. package/core/cli/commands/plugin-remove.ts +170 -0
  95. package/core/cli/generators/component.ts +769 -769
  96. package/core/cli/generators/controller.ts +1 -1
  97. package/core/cli/generators/index.ts +146 -146
  98. package/core/cli/generators/interactive.ts +227 -227
  99. package/core/cli/generators/plugin.ts +2 -2
  100. package/core/cli/generators/prompts.ts +82 -82
  101. package/core/cli/generators/route.ts +6 -6
  102. package/core/cli/generators/service.ts +2 -2
  103. package/core/cli/generators/template-engine.ts +4 -3
  104. package/core/cli/generators/types.ts +2 -2
  105. package/core/cli/generators/utils.ts +191 -191
  106. package/core/cli/index.ts +115 -686
  107. package/core/cli/plugin-discovery.ts +2 -2
  108. package/core/client/LiveComponentsProvider.tsx +60 -8
  109. package/core/client/api/eden.ts +183 -0
  110. package/core/client/api/index.ts +11 -0
  111. package/core/client/components/Live.tsx +104 -0
  112. package/core/client/fluxstack.ts +1 -9
  113. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
  114. package/core/client/hooks/state-validator.ts +1 -1
  115. package/core/client/hooks/useAuth.ts +48 -48
  116. package/core/client/hooks/useChunkedUpload.ts +85 -35
  117. package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
  118. package/core/client/hooks/useLiveComponent.ts +800 -0
  119. package/core/client/hooks/useLiveUpload.ts +71 -0
  120. package/core/client/hooks/useRoom.ts +409 -0
  121. package/core/client/hooks/useRoomProxy.ts +382 -0
  122. package/core/client/index.ts +17 -68
  123. package/core/client/standalone-entry.ts +8 -0
  124. package/core/client/standalone.ts +74 -53
  125. package/core/client/state/createStore.ts +192 -192
  126. package/core/client/state/index.ts +14 -14
  127. package/core/config/index.ts +70 -291
  128. package/core/config/schema.ts +42 -723
  129. package/core/framework/client.ts +131 -131
  130. package/core/framework/index.ts +7 -7
  131. package/core/framework/server.ts +47 -40
  132. package/core/framework/types.ts +2 -2
  133. package/core/index.ts +23 -4
  134. package/core/live/ComponentRegistry.ts +3 -3
  135. package/core/live/types.ts +77 -0
  136. package/core/plugins/built-in/index.ts +134 -134
  137. package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
  138. package/core/plugins/built-in/live-components/index.ts +1 -1
  139. package/core/plugins/built-in/monitoring/index.ts +111 -47
  140. package/core/plugins/built-in/static/index.ts +1 -1
  141. package/core/plugins/built-in/swagger/index.ts +68 -265
  142. package/core/plugins/built-in/vite/index.ts +85 -185
  143. package/core/plugins/built-in/vite/vite-dev.ts +10 -16
  144. package/core/plugins/config.ts +9 -7
  145. package/core/plugins/dependency-manager.ts +31 -1
  146. package/core/plugins/discovery.ts +19 -7
  147. package/core/plugins/executor.ts +2 -2
  148. package/core/plugins/index.ts +203 -203
  149. package/core/plugins/manager.ts +27 -39
  150. package/core/plugins/module-resolver.ts +19 -8
  151. package/core/plugins/registry.ts +255 -19
  152. package/core/plugins/types.ts +20 -53
  153. package/core/server/framework.ts +66 -43
  154. package/core/server/index.ts +15 -15
  155. package/core/server/live/ComponentRegistry.ts +78 -71
  156. package/core/server/live/FileUploadManager.ts +23 -10
  157. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  158. package/core/server/live/LiveRoomManager.ts +261 -0
  159. package/core/server/live/RoomEventBus.ts +234 -0
  160. package/core/server/live/RoomStateManager.ts +172 -0
  161. package/core/server/live/StateSignature.ts +643 -643
  162. package/core/server/live/WebSocketConnectionManager.ts +30 -19
  163. package/core/server/live/auto-generated-components.ts +21 -9
  164. package/core/server/live/index.ts +14 -0
  165. package/core/server/live/websocket-plugin.ts +214 -67
  166. package/core/server/middleware/elysia-helpers.ts +7 -2
  167. package/core/server/middleware/errorHandling.ts +1 -1
  168. package/core/server/middleware/index.ts +31 -31
  169. package/core/server/plugins/database.ts +180 -180
  170. package/core/server/plugins/static-files-plugin.ts +69 -69
  171. package/core/server/plugins/swagger.ts +1 -1
  172. package/core/server/rooms/RoomBroadcaster.ts +357 -0
  173. package/core/server/rooms/RoomSystem.ts +463 -0
  174. package/core/server/rooms/index.ts +13 -0
  175. package/core/server/services/BaseService.ts +1 -1
  176. package/core/server/services/ServiceContainer.ts +1 -1
  177. package/core/server/services/index.ts +8 -8
  178. package/core/templates/create-project.ts +12 -12
  179. package/core/testing/index.ts +9 -9
  180. package/core/testing/setup.ts +73 -73
  181. package/core/types/api.ts +168 -168
  182. package/core/types/build.ts +219 -219
  183. package/core/types/config.ts +56 -26
  184. package/core/types/index.ts +4 -4
  185. package/core/types/plugin.ts +107 -107
  186. package/core/types/types.ts +353 -14
  187. package/core/utils/build-logger.ts +324 -324
  188. package/core/utils/config-schema.ts +480 -480
  189. package/core/utils/env.ts +2 -8
  190. package/core/utils/errors/codes.ts +114 -114
  191. package/core/utils/errors/handlers.ts +36 -1
  192. package/core/utils/errors/index.ts +49 -5
  193. package/core/utils/errors/middleware.ts +113 -113
  194. package/core/utils/helpers.ts +6 -16
  195. package/core/utils/index.ts +17 -17
  196. package/core/utils/logger/colors.ts +114 -114
  197. package/core/utils/logger/config.ts +13 -9
  198. package/core/utils/logger/formatter.ts +82 -82
  199. package/core/utils/logger/group-logger.ts +101 -101
  200. package/core/utils/logger/index.ts +6 -1
  201. package/core/utils/logger/stack-trace.ts +3 -1
  202. package/core/utils/logger/startup-banner.ts +82 -82
  203. package/core/utils/logger/winston-logger.ts +152 -152
  204. package/core/utils/monitoring/index.ts +211 -211
  205. package/core/utils/sync-version.ts +66 -66
  206. package/core/utils/version.ts +1 -1
  207. package/create-fluxstack.ts +8 -7
  208. package/package.json +12 -13
  209. package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
  210. package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
  211. package/plugins/crypto-auth/client/components/index.ts +11 -11
  212. package/plugins/crypto-auth/client/index.ts +11 -11
  213. package/plugins/crypto-auth/config/index.ts +1 -1
  214. package/plugins/crypto-auth/index.ts +4 -4
  215. package/plugins/crypto-auth/package.json +65 -65
  216. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  217. package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
  218. package/plugins/crypto-auth/server/index.ts +21 -21
  219. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
  220. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
  221. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
  222. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
  223. package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
  224. package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
  225. package/tsconfig.api-strict.json +16 -0
  226. package/tsconfig.json +48 -52
  227. package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
  228. package/types/global.d.ts +29 -29
  229. package/types/vitest.d.ts +8 -8
  230. package/vite.config.ts +38 -62
  231. package/vitest.config.live.ts +10 -9
  232. package/vitest.config.ts +29 -17
  233. package/app/client/README.md +0 -69
  234. package/app/client/SIMPLIFICATION.md +0 -140
  235. package/app/client/frontend-only.ts +0 -12
  236. package/app/client/src/live/FileUploadExample.tsx +0 -359
  237. package/app/client/src/live/MinimalLiveClock.tsx +0 -47
  238. package/app/client/src/live/QuickUploadTest.tsx +0 -193
  239. package/app/client/tsconfig.app.json +0 -45
  240. package/app/client/tsconfig.json +0 -7
  241. package/app/client/zustand-setup.md +0 -65
  242. package/app/server/backend-only.ts +0 -18
  243. package/app/server/live/LiveClockComponent.ts +0 -215
  244. package/app/server/live/LiveFileUploadComponent.ts +0 -77
  245. package/app/server/routes/env-test.ts +0 -110
  246. package/core/client/hooks/index.ts +0 -7
  247. package/core/client/hooks/useHybridLiveComponent.ts +0 -685
  248. package/core/client/hooks/useTypedLiveComponent.ts +0 -133
  249. package/core/client/hooks/useWebSocket.ts +0 -361
  250. package/core/config/env.ts +0 -546
  251. package/core/config/loader.ts +0 -522
  252. package/core/config/runtime-config.ts +0 -327
  253. package/core/config/validator.ts +0 -540
  254. package/core/server/backend-entry.ts +0 -51
  255. package/core/server/standalone.ts +0 -106
  256. package/core/utils/regenerate-files.ts +0 -69
  257. package/fluxstack.config.ts +0 -354
@@ -1,8 +1,8 @@
1
1
  import { existsSync } from 'fs'
2
2
  import { join } from 'path'
3
- import type { FluxStack } from '@/core/plugins/types'
3
+ import type { FluxStack } from '@core/plugins/types'
4
4
  import { cliRegistry } from './command-registry'
5
- import { logger } from '@/core/utils/logger'
5
+ import { logger } from '@core/utils/logger'
6
6
 
7
7
  export class CliPluginDiscovery {
8
8
  private loadedPlugins = new Set<string>()
@@ -16,6 +16,9 @@ export interface LiveComponentsContextValue {
16
16
  // Send message and wait for specific response
17
17
  sendMessageAndWait: (message: WebSocketMessage, timeout?: number) => Promise<WebSocketResponse>
18
18
 
19
+ // Send binary data and wait for response (for file uploads)
20
+ sendBinaryAndWait: (data: ArrayBuffer, requestId: string, timeout?: number) => Promise<WebSocketResponse>
21
+
19
22
  // Register message listener for a component
20
23
  registerComponent: (componentId: string, callback: (message: WebSocketResponse) => void) => () => void
21
24
 
@@ -155,6 +158,25 @@ export function LiveComponentsProvider({
155
158
  return
156
159
  }
157
160
 
161
+ // Broadcast messages should go to ALL components (not just sender)
162
+ if (response.type === 'BROADCAST') {
163
+ // Send to all registered components in the same room
164
+ const registeredComponents = Array.from(componentCallbacksRef.current.keys())
165
+ log('📡 Broadcast routing:', {
166
+ sender: response.componentId,
167
+ registeredComponents,
168
+ totalRegistered: registeredComponents.length
169
+ })
170
+
171
+ componentCallbacksRef.current.forEach((callback, compId) => {
172
+ // Don't send back to the sender component
173
+ if (compId !== response.componentId) {
174
+ callback(response)
175
+ }
176
+ })
177
+ return
178
+ }
179
+
158
180
  // Route message to specific component
159
181
  if (response.componentId) {
160
182
  const callback = componentCallbacksRef.current.get(response.componentId)
@@ -165,14 +187,6 @@ export function LiveComponentsProvider({
165
187
  }
166
188
  }
167
189
 
168
- // Broadcast messages (no specific componentId)
169
- if (response.type === 'BROADCAST' && !response.componentId) {
170
- // Send to all registered components
171
- componentCallbacksRef.current.forEach(callback => {
172
- callback(response)
173
- })
174
- }
175
-
176
190
  } catch (error) {
177
191
  log('❌ Failed to parse message', error)
178
192
  setError('Failed to parse message')
@@ -331,6 +345,43 @@ export function LiveComponentsProvider({
331
345
  })
332
346
  }, [log, generateRequestId])
333
347
 
348
+ // Send binary data and wait for response (for file uploads)
349
+ const sendBinaryAndWait = useCallback(async (
350
+ data: ArrayBuffer,
351
+ requestId: string,
352
+ timeout: number = 10000
353
+ ): Promise<WebSocketResponse> => {
354
+ return new Promise((resolve, reject) => {
355
+ if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
356
+ reject(new Error('WebSocket is not connected'))
357
+ return
358
+ }
359
+
360
+ // Set up timeout
361
+ const timeoutHandle = setTimeout(() => {
362
+ pendingRequestsRef.current.delete(requestId)
363
+ reject(new Error(`Binary request timeout after ${timeout}ms`))
364
+ }, timeout)
365
+
366
+ // Store pending request
367
+ pendingRequestsRef.current.set(requestId, {
368
+ resolve,
369
+ reject,
370
+ timeout: timeoutHandle
371
+ })
372
+
373
+ try {
374
+ // Send as binary frame
375
+ wsRef.current.send(data)
376
+ log('📤 Sent binary data', { requestId, size: data.byteLength })
377
+ } catch (error) {
378
+ clearTimeout(timeoutHandle)
379
+ pendingRequestsRef.current.delete(requestId)
380
+ reject(error)
381
+ }
382
+ })
383
+ }, [log])
384
+
334
385
  // Register component callback
335
386
  const registerComponent = useCallback((
336
387
  componentId: string,
@@ -375,6 +426,7 @@ export function LiveComponentsProvider({
375
426
  connectionId,
376
427
  sendMessage,
377
428
  sendMessageAndWait,
429
+ sendBinaryAndWait,
378
430
  registerComponent,
379
431
  unregisterComponent,
380
432
  reconnect,
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Eden Treaty Client Factory
3
+ * Creates a type-safe API client with sensible defaults
4
+ */
5
+
6
+ import { treaty, type Treaty } from '@elysiajs/eden'
7
+ import type { Elysia } from 'elysia'
8
+
9
+ /**
10
+ * Any Elysia application type
11
+ * Used as constraint for the createEdenClient generic
12
+ */
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ type AnyElysiaApp = Elysia<any, any, any, any, any, any, any>
15
+
16
+ /**
17
+ * Configuration options for the Eden client
18
+ */
19
+ export interface EdenClientOptions {
20
+ /**
21
+ * Custom function to get the base URL
22
+ * @default Uses window.location.origin or 'http://localhost:3000'
23
+ */
24
+ getBaseUrl?: () => string
25
+
26
+ /**
27
+ * Custom function to get the auth token
28
+ * @default Reads from localStorage.getItem('accessToken')
29
+ */
30
+ getAuthToken?: () => string | null
31
+
32
+ /**
33
+ * Callback when a 401 Unauthorized response is received
34
+ * @default Removes accessToken from localStorage
35
+ */
36
+ onUnauthorized?: () => void
37
+
38
+ /**
39
+ * Enable request/response logging in development
40
+ * @default true in development, false in production
41
+ */
42
+ enableLogging?: boolean
43
+
44
+ /**
45
+ * localStorage key for the access token
46
+ * @default 'accessToken'
47
+ */
48
+ tokenKey?: string
49
+ }
50
+
51
+ /**
52
+ * Default base URL getter
53
+ */
54
+ export function getDefaultBaseUrl(): string {
55
+ if (typeof window === 'undefined') return 'http://localhost:3000'
56
+ return window.location.origin
57
+ }
58
+
59
+ /**
60
+ * Create a default auth token getter
61
+ */
62
+ function createDefaultGetAuthToken(tokenKey: string): () => string | null {
63
+ return () => {
64
+ if (typeof window === 'undefined') return null
65
+ return localStorage.getItem(tokenKey)
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Create a default unauthorized handler
71
+ */
72
+ function createDefaultOnUnauthorized(tokenKey: string): () => void {
73
+ return () => {
74
+ if (typeof window !== 'undefined') {
75
+ console.warn('🔒 Token expired')
76
+ localStorage.removeItem(tokenKey)
77
+ }
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Check if we're in development mode
83
+ */
84
+ function isDev(): boolean {
85
+ try {
86
+ return import.meta.env?.DEV ?? false
87
+ } catch {
88
+ return false
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Create an Eden Treaty client with authentication and logging
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * import { createEdenClient } from '@core/client/api/eden'
98
+ * import type { App } from '@server/app'
99
+ *
100
+ * // Basic usage
101
+ * export const api = createEdenClient<App>()
102
+ *
103
+ * // With custom options
104
+ * export const api = createEdenClient<App>({
105
+ * onUnauthorized: () => {
106
+ * window.location.href = '/login'
107
+ * }
108
+ * })
109
+ * ```
110
+ */
111
+ export function createEdenClient<TApp extends AnyElysiaApp>(
112
+ options: EdenClientOptions = {}
113
+ ) {
114
+ const {
115
+ getBaseUrl = getDefaultBaseUrl,
116
+ tokenKey = 'accessToken',
117
+ enableLogging = isDev(),
118
+ } = options
119
+
120
+ const getAuthToken = options.getAuthToken ?? createDefaultGetAuthToken(tokenKey)
121
+ const onUnauthorized = options.onUnauthorized ?? createDefaultOnUnauthorized(tokenKey)
122
+
123
+ const client = treaty<TApp>(getBaseUrl(), {
124
+ // Dynamic headers for every request
125
+ headers(_path, _options) {
126
+ const token = getAuthToken()
127
+ return token ? { Authorization: `Bearer ${token}` } : undefined
128
+ },
129
+
130
+ // Custom fetcher with logging and error handling
131
+ fetcher: (async (url: string | URL | Request, init?: RequestInit) => {
132
+ if (enableLogging) {
133
+ console.log(`🌐 ${init?.method ?? 'GET'} ${url}`)
134
+ }
135
+
136
+ const res = await fetch(url, init)
137
+
138
+ if (enableLogging) {
139
+ console.log(`📡 ${url} → ${res.status}`)
140
+ }
141
+
142
+ // Handle 401 Unauthorized
143
+ if (res.status === 401) {
144
+ onUnauthorized()
145
+ }
146
+
147
+ return res
148
+ }) as typeof fetch
149
+ })
150
+
151
+ // Return the .api property for direct access to endpoints
152
+ // Type inference works automatically from the TApp generic
153
+ return client.api as Treaty.Create<TApp>['api']
154
+ }
155
+
156
+ /**
157
+ * Extract user-friendly error message from Eden Treaty errors
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const { data, error } = await api.users.get()
162
+ * if (error) {
163
+ * toast.error(getErrorMessage(error))
164
+ * }
165
+ * ```
166
+ */
167
+ export function getErrorMessage(error: unknown): string {
168
+ // Eden Treaty error format
169
+ if (error && typeof error === 'object' && 'value' in error) {
170
+ const edenError = error as { value?: { message?: string; userMessage?: string } }
171
+ return edenError.value?.userMessage || edenError.value?.message || 'An error occurred'
172
+ }
173
+
174
+ // Standard Error
175
+ if (error instanceof Error) {
176
+ return error.message
177
+ }
178
+
179
+ return 'An unexpected error occurred'
180
+ }
181
+
182
+ // Re-export treaty for advanced usage
183
+ export { treaty } from '@elysiajs/eden'
@@ -0,0 +1,11 @@
1
+ /**
2
+ * FluxStack Client API utilities
3
+ */
4
+
5
+ export {
6
+ createEdenClient,
7
+ getErrorMessage,
8
+ getDefaultBaseUrl,
9
+ treaty,
10
+ type EdenClientOptions
11
+ } from './eden'
@@ -0,0 +1,104 @@
1
+ // 🔥 FluxStack Live - Hook para componentes real-time
2
+ //
3
+ // Uso:
4
+ // import { Live } from '@/core/client'
5
+ // import { LiveForm } from '@server/live/LiveForm'
6
+ //
7
+ // // Sem estado inicial - usa defaultState do componente
8
+ // const form = Live.use(LiveForm)
9
+ //
10
+ // // Com estado inicial parcial (override)
11
+ // const form = Live.use(LiveForm, { name: 'João' })
12
+ //
13
+ // return (
14
+ // <input {...form.$field('name', { syncOn: 'blur' })} />
15
+ // <button onClick={() => form.submit()}>Enviar</button>
16
+ // )
17
+ //
18
+ // 🔥 Broadcasts Tipados (Discriminated Union):
19
+ // // No servidor, defina a interface de broadcasts:
20
+ // export interface LiveFormBroadcasts {
21
+ // FORM_SUBMITTED: { formId: string; data: any }
22
+ // FIELD_CHANGED: { field: string; value: any }
23
+ // }
24
+ //
25
+ // // No cliente, use com tipagem automática (discriminated union):
26
+ // import { LiveForm, type LiveFormBroadcasts } from '@server/live/LiveForm'
27
+ //
28
+ // const form = Live.use(LiveForm)
29
+ // form.$onBroadcast<LiveFormBroadcasts>((event) => {
30
+ // switch (event.type) {
31
+ // case 'FORM_SUBMITTED':
32
+ // console.log(event.data.formId) // ✅ Tipado como string!
33
+ // break
34
+ // case 'FIELD_CHANGED':
35
+ // console.log(event.data.field) // ✅ Tipado como string!
36
+ // break
37
+ // }
38
+ // })
39
+
40
+ import { useLiveComponent } from '../hooks/useLiveComponent'
41
+ import type { UseLiveComponentOptions, LiveProxy, LiveProxyWithBroadcasts } from '../hooks/useLiveComponent'
42
+
43
+ // ===== Tipos para Inferência do Servidor =====
44
+
45
+ // Extrai o defaultState estático da classe
46
+ type ExtractDefaultState<T> = T extends { defaultState: infer S }
47
+ ? S extends Record<string, any> ? S : Record<string, any>
48
+ : Record<string, any>
49
+
50
+ // Extrai o State da classe do servidor (via instance.state)
51
+ type ExtractState<T> = T extends { new(...args: any[]): { state: infer S } }
52
+ ? S extends Record<string, any> ? S : Record<string, any>
53
+ : ExtractDefaultState<T>
54
+
55
+ // Extrai as Actions (métodos públicos async) da classe do servidor
56
+ type ExtractActions<T> = T extends { new(...args: any[]): infer Instance }
57
+ ? {
58
+ [K in keyof Instance as Instance[K] extends (...args: any[]) => Promise<any>
59
+ ? K extends 'setState' | 'getState' | 'getValue' | 'setValue' | 'setValues' | 'getSnapshot'
60
+ ? never
61
+ : K
62
+ : never
63
+ ]: Instance[K]
64
+ }
65
+ : Record<string, never>
66
+
67
+ // ===== Opções do Live.use() =====
68
+
69
+ interface LiveUseOptions<TState> extends UseLiveComponentOptions {
70
+ /** Estado inicial para o componente */
71
+ initialState?: Partial<TState>
72
+ }
73
+
74
+ // ===== Hook Principal =====
75
+
76
+ function useLive<
77
+ T extends { new(...args: any[]): any; defaultState?: Record<string, any>; componentName: string },
78
+ TBroadcasts extends Record<string, any> = Record<string, any>
79
+ >(
80
+ ComponentClass: T,
81
+ options?: LiveUseOptions<ExtractState<T>>
82
+ ): LiveProxyWithBroadcasts<ExtractState<T>, ExtractActions<T>, TBroadcasts> {
83
+ // Use static componentName (required for production builds with minification)
84
+ const componentName = ComponentClass.componentName
85
+
86
+ // Usa defaultState da classe se não passar initialState
87
+ const defaultState = (ComponentClass as any).defaultState || {}
88
+ const { initialState, ...restOptions } = options || {}
89
+ const mergedState = { ...defaultState, ...initialState } as ExtractState<T>
90
+
91
+ return useLiveComponent<ExtractState<T>, ExtractActions<T>, TBroadcasts>(
92
+ componentName,
93
+ mergedState,
94
+ restOptions
95
+ )
96
+ }
97
+
98
+ // ===== Export =====
99
+
100
+ export const Live = {
101
+ use: useLive
102
+ }
103
+
104
+ export default Live
@@ -4,14 +4,6 @@
4
4
  // Re-export everything from the main index
5
5
  export * from './index'
6
6
 
7
- // Convenience aliases for common hooks and utilities
8
- export {
9
- useHybridLiveComponent as useLive,
10
- useWebSocket as useWS,
11
- useChunkedUpload as useUpload,
12
- StateValidator as Validator
13
- } from './index'
14
-
15
7
  // Default export for easy importing
16
8
  import * as FluxStack from './index'
17
- export default FluxStack
9
+ export default FluxStack