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
@@ -5,20 +5,50 @@ import type {
5
5
  FileUploadChunkMessage,
6
6
  FileUploadCompleteMessage,
7
7
  FileUploadProgressResponse,
8
- FileUploadCompleteResponse
9
- } from '@/core/types/types'
8
+ FileUploadCompleteResponse,
9
+ BinaryChunkHeader
10
+ } from '@core/types/types'
10
11
 
11
12
  export interface ChunkedUploadOptions {
12
13
  chunkSize?: number // Default 64KB (used as initial if adaptive is enabled)
13
14
  maxFileSize?: number // Default 50MB
14
15
  allowedTypes?: string[]
15
- sendMessageAndWait?: (message: any, timeout?: number) => Promise<any> // WebSocket send function
16
+ sendMessageAndWait?: (message: any, timeout?: number) => Promise<any> // WebSocket send function for JSON
17
+ sendBinaryAndWait?: (data: ArrayBuffer, requestId: string, timeout?: number) => Promise<any> // WebSocket send function for binary
16
18
  onProgress?: (progress: number, bytesUploaded: number, totalBytes: number) => void
17
19
  onComplete?: (response: FileUploadCompleteResponse) => void
18
20
  onError?: (error: string) => void
19
21
  // Adaptive chunking options
20
22
  adaptiveChunking?: boolean // Enable adaptive chunk sizing (default: false)
21
23
  adaptiveConfig?: Partial<AdaptiveChunkConfig> // Adaptive chunking configuration
24
+ // Binary protocol (more efficient, ~33% less data)
25
+ useBinaryProtocol?: boolean // Enable binary chunk protocol (default: true)
26
+ }
27
+
28
+ /**
29
+ * Creates a binary message with header + data
30
+ * Format: [4 bytes header length][JSON header][binary data]
31
+ */
32
+ function createBinaryChunkMessage(header: BinaryChunkHeader, chunkData: Uint8Array): ArrayBuffer {
33
+ const headerJson = JSON.stringify(header)
34
+ const headerBytes = new TextEncoder().encode(headerJson)
35
+
36
+ // Total size: 4 bytes (header length) + header + data
37
+ const totalSize = 4 + headerBytes.length + chunkData.length
38
+ const buffer = new ArrayBuffer(totalSize)
39
+ const view = new DataView(buffer)
40
+ const uint8View = new Uint8Array(buffer)
41
+
42
+ // Write header length (little-endian)
43
+ view.setUint32(0, headerBytes.length, true)
44
+
45
+ // Write header
46
+ uint8View.set(headerBytes, 4)
47
+
48
+ // Write chunk data
49
+ uint8View.set(chunkData, 4 + headerBytes.length)
50
+
51
+ return buffer
22
52
  }
23
53
 
24
54
  export interface ChunkedUploadState {
@@ -46,13 +76,18 @@ export function useChunkedUpload(componentId: string, options: ChunkedUploadOpti
46
76
  maxFileSize = 50 * 1024 * 1024, // 50MB default
47
77
  allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif'],
48
78
  sendMessageAndWait,
79
+ sendBinaryAndWait,
49
80
  onProgress,
50
81
  onComplete,
51
82
  onError,
52
83
  adaptiveChunking = false,
53
- adaptiveConfig
84
+ adaptiveConfig,
85
+ useBinaryProtocol = true // Default to binary for efficiency
54
86
  } = options
55
87
 
88
+ // Determine if we can use binary protocol
89
+ const canUseBinary = useBinaryProtocol && sendBinaryAndWait
90
+
56
91
  const abortControllerRef = useRef<AbortController | null>(null)
57
92
  const adaptiveSizerRef = useRef<AdaptiveChunkSizer | null>(null)
58
93
 
@@ -60,7 +95,7 @@ export function useChunkedUpload(componentId: string, options: ChunkedUploadOpti
60
95
  if (adaptiveChunking && !adaptiveSizerRef.current) {
61
96
  adaptiveSizerRef.current = new AdaptiveChunkSizer({
62
97
  initialChunkSize: chunkSize,
63
- minChunkSize: 16 * 1024, // 16KB min
98
+ minChunkSize: chunkSize, // Do not go below initial chunk size by default
64
99
  maxChunkSize: 1024 * 1024, // 1MB max
65
100
  ...adaptiveConfig
66
101
  })
@@ -109,7 +144,8 @@ export function useChunkedUpload(componentId: string, options: ChunkedUploadOpti
109
144
  uploadId,
110
145
  filename: file.name,
111
146
  size: file.size,
112
- adaptiveChunking
147
+ adaptiveChunking,
148
+ protocol: canUseBinary ? 'binary' : 'base64'
113
149
  })
114
150
 
115
151
  // Reset adaptive sizer for new upload
@@ -141,50 +177,64 @@ export function useChunkedUpload(componentId: string, options: ChunkedUploadOpti
141
177
 
142
178
  console.log('✅ Upload started successfully')
143
179
 
144
- // Read file as ArrayBuffer for dynamic chunking
145
- const fileArrayBuffer = await file.arrayBuffer()
146
- const fileData = new Uint8Array(fileArrayBuffer)
147
-
148
180
  let offset = 0
149
181
  let chunkIndex = 0
150
182
  const estimatedTotalChunks = Math.ceil(file.size / initialChunkSize)
151
183
 
152
- // Send chunks dynamically with adaptive sizing
153
- while (offset < fileData.length) {
184
+ // Send chunks dynamically with adaptive sizing (read slice per chunk)
185
+ while (offset < file.size) {
154
186
  if (abortControllerRef.current?.signal.aborted) {
155
187
  throw new Error('Upload cancelled')
156
188
  }
157
189
 
158
190
  // Get current chunk size (adaptive or fixed)
159
191
  const currentChunkSize = adaptiveSizerRef.current?.getChunkSize() ?? chunkSize
160
- const chunkEnd = Math.min(offset + currentChunkSize, fileData.length)
161
- const chunkBytes = fileData.slice(offset, chunkEnd)
162
-
163
- // Convert chunk to base64
164
- let binary = ''
165
- for (let j = 0; j < chunkBytes.length; j++) {
166
- binary += String.fromCharCode(chunkBytes[j])
167
- }
168
- const base64Chunk = btoa(binary)
192
+ const chunkEnd = Math.min(offset + currentChunkSize, file.size)
193
+ const sliceBuffer = await file.slice(offset, chunkEnd).arrayBuffer()
194
+ const chunkBytes = new Uint8Array(sliceBuffer)
169
195
 
170
196
  // Record chunk start time for adaptive sizing
171
197
  const chunkStartTime = adaptiveSizerRef.current?.recordChunkStart(chunkIndex) ?? 0
198
+ const requestId = `chunk-${uploadId}-${chunkIndex}`
172
199
 
173
- const chunkMessage: FileUploadChunkMessage = {
174
- type: 'FILE_UPLOAD_CHUNK',
175
- componentId,
176
- uploadId,
177
- chunkIndex,
178
- totalChunks: estimatedTotalChunks, // Approximate, will be recalculated
179
- data: base64Chunk,
180
- requestId: `chunk-${uploadId}-${chunkIndex}`
181
- }
182
-
183
- console.log(`📤 Sending chunk ${chunkIndex + 1} (size: ${chunkBytes.length} bytes)`)
200
+ console.log(`📤 Sending chunk ${chunkIndex + 1} (size: ${chunkBytes.length} bytes)${canUseBinary ? ' [binary]' : ' [base64]'}`)
184
201
 
185
202
  try {
186
- // Send chunk and wait for progress response
187
- const progressResponse = await sendMessageAndWait(chunkMessage, 10000) as FileUploadProgressResponse
203
+ let progressResponse: FileUploadProgressResponse | undefined
204
+
205
+ if (canUseBinary) {
206
+ // Binary protocol: Send header + raw bytes (more efficient)
207
+ const header: BinaryChunkHeader = {
208
+ type: 'FILE_UPLOAD_CHUNK',
209
+ componentId,
210
+ uploadId,
211
+ chunkIndex,
212
+ totalChunks: estimatedTotalChunks,
213
+ requestId
214
+ }
215
+
216
+ const binaryMessage = createBinaryChunkMessage(header, chunkBytes)
217
+ progressResponse = await sendBinaryAndWait!(binaryMessage, requestId, 10000) as FileUploadProgressResponse
218
+ } else {
219
+ // JSON protocol: Convert to base64 (legacy/fallback)
220
+ let binary = ''
221
+ for (let j = 0; j < chunkBytes.length; j++) {
222
+ binary += String.fromCharCode(chunkBytes[j])
223
+ }
224
+ const base64Chunk = btoa(binary)
225
+
226
+ const chunkMessage: FileUploadChunkMessage = {
227
+ type: 'FILE_UPLOAD_CHUNK',
228
+ componentId,
229
+ uploadId,
230
+ chunkIndex,
231
+ totalChunks: estimatedTotalChunks,
232
+ data: base64Chunk,
233
+ requestId
234
+ }
235
+
236
+ progressResponse = await sendMessageAndWait!(chunkMessage, 10000) as FileUploadProgressResponse
237
+ }
188
238
 
189
239
  if (progressResponse) {
190
240
  const { progress, bytesUploaded } = progressResponse
@@ -306,4 +356,4 @@ export function useChunkedUpload(componentId: string, options: ChunkedUploadOpti
306
356
  cancelUpload,
307
357
  reset
308
358
  }
309
- }
359
+ }
@@ -0,0 +1,87 @@
1
+ import { useMemo } from 'react'
2
+ import { useLiveComponents } from '../LiveComponentsProvider'
3
+ import { useChunkedUpload } from './useChunkedUpload'
4
+ import type { ChunkedUploadOptions } from './useChunkedUpload'
5
+ import type { FileUploadCompleteResponse } from '@core/types/types'
6
+
7
+ type LiveUploadActions = {
8
+ $componentId: string | null
9
+ startUpload: (payload: { fileName: string; fileSize: number; fileType: string }) => Promise<any>
10
+ updateProgress: (payload: { progress: number; bytesUploaded: number; totalBytes: number }) => Promise<any>
11
+ completeUpload: (payload: { fileUrl: string }) => Promise<any>
12
+ failUpload: (payload: { error: string }) => Promise<any>
13
+ reset: () => Promise<any>
14
+ }
15
+
16
+ export interface LiveChunkedUploadOptions extends Omit<ChunkedUploadOptions, 'sendMessageAndWait' | 'onProgress' | 'onComplete' | 'onError'> {
17
+ onProgress?: (progress: number, bytesUploaded: number, totalBytes: number) => void
18
+ onComplete?: (response: FileUploadCompleteResponse) => void
19
+ onError?: (error: string) => void
20
+ fileUrlResolver?: (fileUrl: string) => string
21
+ }
22
+
23
+ export function useLiveChunkedUpload(live: LiveUploadActions, options: LiveChunkedUploadOptions = {}) {
24
+ const { sendMessageAndWait, sendBinaryAndWait } = useLiveComponents()
25
+
26
+ const {
27
+ onProgress,
28
+ onComplete,
29
+ onError,
30
+ fileUrlResolver,
31
+ ...chunkedOptions
32
+ } = options
33
+
34
+ const componentId = live.$componentId ?? ''
35
+
36
+ const base = useChunkedUpload(componentId, {
37
+ ...chunkedOptions,
38
+ sendMessageAndWait,
39
+ sendBinaryAndWait, // Enable binary protocol for efficient uploads
40
+ onProgress: (pct, uploaded, total) => {
41
+ void live.updateProgress({ progress: pct, bytesUploaded: uploaded, totalBytes: total }).catch(() => {})
42
+ onProgress?.(pct, uploaded, total)
43
+ },
44
+ onComplete: (response) => {
45
+ const rawUrl = response.fileUrl || ''
46
+ const resolvedUrl = fileUrlResolver ? fileUrlResolver(rawUrl) : rawUrl
47
+ void live.completeUpload({ fileUrl: resolvedUrl }).catch(() => {})
48
+ onComplete?.(response)
49
+ },
50
+ onError: (error) => {
51
+ void live.failUpload({ error }).catch(() => {})
52
+ onError?.(error)
53
+ }
54
+ })
55
+
56
+ const uploadFile = useMemo(() => {
57
+ return async (file: File) => {
58
+ if (!live.$componentId) {
59
+ const msg = 'WebSocket not ready. Wait a moment and try again.'
60
+ void live.failUpload({ error: msg }).catch(() => {})
61
+ onError?.(msg)
62
+ return
63
+ }
64
+
65
+ await live.startUpload({
66
+ fileName: file.name,
67
+ fileSize: file.size,
68
+ fileType: file.type || 'application/octet-stream'
69
+ })
70
+
71
+ await base.uploadFile(file)
72
+ }
73
+ }, [base, live, onError])
74
+
75
+ const reset = useMemo(() => {
76
+ return async () => {
77
+ await live.reset()
78
+ base.reset()
79
+ }
80
+ }, [base, live])
81
+
82
+ return {
83
+ ...base,
84
+ uploadFile,
85
+ reset
86
+ }
87
+ }