@uploadista/react 0.0.13 → 0.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 (25) hide show
  1. package/dist/components/index.d.mts +2 -2
  2. package/dist/components/index.mjs +1 -1
  3. package/dist/hooks/index.d.mts +2 -2
  4. package/dist/hooks/index.mjs +1 -1
  5. package/dist/index.d.mts +3 -3
  6. package/dist/index.mjs +1 -1
  7. package/dist/{upload-zone-CsaXN9yD.mjs → upload-zone-8khVLLYF.mjs} +2 -2
  8. package/dist/{upload-zone-CsaXN9yD.mjs.map → upload-zone-8khVLLYF.mjs.map} +1 -1
  9. package/dist/{uploadista-provider-Cj34PdHg.d.mts → uploadista-provider-CrS6TmpJ.d.mts} +2 -2
  10. package/dist/{uploadista-provider-Cj34PdHg.d.mts.map → uploadista-provider-CrS6TmpJ.d.mts.map} +1 -1
  11. package/dist/{use-upload-metrics-2HKWi6GZ.mjs → use-upload-metrics-BE7UcAaz.mjs} +2 -2
  12. package/dist/{use-upload-metrics-2HKWi6GZ.mjs.map → use-upload-metrics-BE7UcAaz.mjs.map} +1 -1
  13. package/dist/{use-upload-metrics-DESSCpN5.d.mts → use-upload-metrics-IXxUORce.d.mts} +10 -10
  14. package/dist/{use-upload-metrics-DESSCpN5.d.mts.map → use-upload-metrics-IXxUORce.d.mts.map} +1 -1
  15. package/dist/use-upload-xcqz090n.mjs +2 -0
  16. package/dist/use-upload-xcqz090n.mjs.map +1 -0
  17. package/dist/{use-uploadista-client-Dp8_ZO7d.d.mts → use-uploadista-client--ivZPO88.d.mts} +21 -110
  18. package/dist/use-uploadista-client--ivZPO88.d.mts.map +1 -0
  19. package/package.json +7 -7
  20. package/src/hooks/use-flow-upload.ts +84 -342
  21. package/src/hooks/use-multi-upload.ts +2 -6
  22. package/src/hooks/use-upload.ts +78 -239
  23. package/dist/use-upload-CvPqdohl.mjs +0 -2
  24. package/dist/use-upload-CvPqdohl.mjs.map +0 -1
  25. package/dist/use-uploadista-client-Dp8_ZO7d.d.mts.map +0 -1
@@ -1,30 +1,16 @@
1
- import type {
2
- BrowserUploadInput,
3
- ChunkMetrics,
4
- PerformanceInsights,
5
- UploadistaEvent,
6
- UploadSessionMetrics,
7
- } from "@uploadista/client-browser";
1
+ import type { BrowserUploadInput } from "@uploadista/client-browser";
2
+ import type { UploadMetrics, UploadOptions } from "@uploadista/client-core";
3
+ import {
4
+ UploadManager,
5
+ type UploadState,
6
+ type UploadStatus,
7
+ } from "@uploadista/client-core";
8
8
  import type { UploadFile } from "@uploadista/core/types";
9
- import { UploadEventType } from "@uploadista/core/types";
10
9
  import { useCallback, useEffect, useRef, useState } from "react";
11
10
  import { useUploadistaContext } from "../components/uploadista-provider";
12
11
 
13
- export type UploadStatus =
14
- | "idle"
15
- | "uploading"
16
- | "success"
17
- | "error"
18
- | "aborted";
19
-
20
- export interface UploadState {
21
- status: UploadStatus;
22
- progress: number;
23
- bytesUploaded: number;
24
- totalBytes: number | null;
25
- error: Error | null;
26
- result: UploadFile | null;
27
- }
12
+ // Re-export types from core for convenience
13
+ export type { UploadState, UploadStatus };
28
14
 
29
15
  export interface UseUploadOptions {
30
16
  /**
@@ -44,15 +30,23 @@ export interface UseUploadOptions {
44
30
 
45
31
  /**
46
32
  * Called when upload progress updates
33
+ *
34
+ * @param uploadId - The unique identifier for this upload
35
+ * @param bytesUploaded - Number of bytes uploaded
36
+ * @param totalBytes - Total bytes to upload, null if unknown
47
37
  */
48
38
  onProgress?: (
49
- progress: number,
39
+ uploadId: string,
50
40
  bytesUploaded: number,
51
41
  totalBytes: number | null,
52
42
  ) => void;
53
43
 
54
44
  /**
55
45
  * Called when a chunk completes
46
+ *
47
+ * @param chunkSize - Size of the completed chunk
48
+ * @param bytesAccepted - Total bytes accepted so far
49
+ * @param bytesTotal - Total bytes to upload, null if unknown
56
50
  */
57
51
  onChunkComplete?: (
58
52
  chunkSize: number,
@@ -62,11 +56,15 @@ export interface UseUploadOptions {
62
56
 
63
57
  /**
64
58
  * Called when upload succeeds
59
+ *
60
+ * @param result - The uploaded file result
65
61
  */
66
62
  onSuccess?: (result: UploadFile) => void;
67
63
 
68
64
  /**
69
65
  * Called when upload fails
66
+ *
67
+ * @param error - The error that caused the failure
70
68
  */
71
69
  onError?: (error: Error) => void;
72
70
 
@@ -77,41 +75,14 @@ export interface UseUploadOptions {
77
75
 
78
76
  /**
79
77
  * Custom retry logic
78
+ *
79
+ * @param error - The error that triggered the retry check
80
+ * @param retryAttempt - The current retry attempt number
81
+ * @returns true to retry, false to fail
80
82
  */
81
83
  onShouldRetry?: (error: Error, retryAttempt: number) => boolean;
82
84
  }
83
85
 
84
- export interface UploadMetrics {
85
- /**
86
- * Get performance insights from the upload client
87
- */
88
- getInsights: () => PerformanceInsights;
89
-
90
- /**
91
- * Export detailed metrics from the upload client
92
- */
93
- exportMetrics: () => {
94
- session: Partial<UploadSessionMetrics>;
95
- chunks: ChunkMetrics[];
96
- insights: PerformanceInsights;
97
- };
98
-
99
- /**
100
- * Get current network metrics
101
- */
102
- getNetworkMetrics: () => unknown;
103
-
104
- /**
105
- * Get current network condition
106
- */
107
- getNetworkCondition: () => unknown;
108
-
109
- /**
110
- * Reset all metrics
111
- */
112
- resetMetrics: () => void;
113
- }
114
-
115
86
  export interface UseUploadReturn {
116
87
  /**
117
88
  * Current upload state
@@ -154,15 +125,6 @@ export interface UseUploadReturn {
154
125
  metrics: UploadMetrics;
155
126
  }
156
127
 
157
- const initialState: UploadState = {
158
- status: "idle",
159
- progress: 0,
160
- bytesUploaded: 0,
161
- totalBytes: null,
162
- error: null,
163
- result: null,
164
- };
165
-
166
128
  /**
167
129
  * React hook for managing individual file uploads with full state management.
168
130
  * Provides upload progress tracking, error handling, abort functionality, and retry logic.
@@ -178,7 +140,10 @@ const initialState: UploadState = {
178
140
  * const upload = useUpload({
179
141
  * onSuccess: (result) => console.log('Upload complete:', result),
180
142
  * onError: (error) => console.error('Upload failed:', error),
181
- * onProgress: (progress) => console.log('Progress:', progress + '%'),
143
+ * onProgress: (uploadId, bytesUploaded, totalBytes) => {
144
+ * const progress = totalBytes ? Math.round((bytesUploaded / totalBytes) * 100) : 0;
145
+ * console.log(`Upload ${uploadId}: ${progress}% (${bytesUploaded}/${totalBytes} bytes)`);
146
+ * },
182
147
  * });
183
148
  *
184
149
  * return (
@@ -199,195 +164,69 @@ const initialState: UploadState = {
199
164
  * }
200
165
  * ```
201
166
  */
167
+ const initialState: UploadState = {
168
+ status: "idle",
169
+ progress: 0,
170
+ bytesUploaded: 0,
171
+ totalBytes: null,
172
+ error: null,
173
+ result: null,
174
+ };
175
+
202
176
  export function useUpload(options: UseUploadOptions = {}): UseUploadReturn {
203
177
  const uploadClient = useUploadistaContext();
204
178
  const [state, setState] = useState<UploadState>(initialState);
205
- const abortControllerRef = useRef<{ abort: () => void } | null>(null);
206
- const lastFileRef = useRef<BrowserUploadInput | null>(null);
207
-
208
- const updateState = useCallback((update: Partial<UploadState>) => {
209
- setState((prev) => ({ ...prev, ...update }));
210
- }, []);
211
-
212
- const reset = useCallback(() => {
213
- if (abortControllerRef.current) {
214
- abortControllerRef.current.abort();
215
- abortControllerRef.current = null;
216
- }
217
- setState(initialState);
218
- lastFileRef.current = null;
219
- }, []);
220
-
221
- const abort = useCallback(() => {
222
- if (abortControllerRef.current) {
223
- abortControllerRef.current.abort();
224
- abortControllerRef.current = null;
225
- }
226
-
227
- updateState({
228
- status: "aborted",
229
- });
179
+ const managerRef = useRef<UploadManager<
180
+ BrowserUploadInput,
181
+ UploadOptions
182
+ > | null>(null);
230
183
 
231
- options.onAbort?.();
232
- }, [options, updateState]);
233
-
234
- const upload = useCallback(
235
- (file: BrowserUploadInput) => {
236
- // Reset any previous state but keep the file reference for retries
237
- setState({
238
- ...initialState,
239
- status: "uploading",
240
- totalBytes: file instanceof File ? file.size : null,
241
- });
242
-
243
- lastFileRef.current = file;
244
-
245
- // Start the upload and handle the promise
246
- const uploadPromise = uploadClient.client.upload(file, {
184
+ // Create UploadManager instance
185
+ useEffect(() => {
186
+ managerRef.current = new UploadManager(
187
+ (file: BrowserUploadInput, opts: UploadOptions) =>
188
+ uploadClient.client.upload(file, opts),
189
+ {
190
+ onStateChange: setState,
191
+ onProgress: options.onProgress,
192
+ onChunkComplete: options.onChunkComplete,
193
+ onSuccess: options.onSuccess,
194
+ onError: options.onError,
195
+ onAbort: options.onAbort,
196
+ },
197
+ {
247
198
  metadata: options.metadata,
248
199
  uploadLengthDeferred: options.uploadLengthDeferred,
249
200
  uploadSize: options.uploadSize,
250
-
251
- onStart: ({ uploadId }) => {
252
- currentUploadIdRef.current = uploadId;
253
- },
254
-
255
- onProgress: (
256
- _uploadId: string,
257
- bytesUploaded: number,
258
- totalBytes: number | null,
259
- ) => {
260
- const progress = totalBytes
261
- ? Math.round((bytesUploaded / totalBytes) * 100)
262
- : 0;
263
-
264
- updateState({
265
- progress,
266
- bytesUploaded,
267
- totalBytes,
268
- });
269
-
270
- options.onProgress?.(progress, bytesUploaded, totalBytes);
271
- },
272
-
273
- onChunkComplete: (
274
- chunkSize: number,
275
- bytesAccepted: number,
276
- bytesTotal: number | null,
277
- ) => {
278
- options.onChunkComplete?.(chunkSize, bytesAccepted, bytesTotal);
279
- },
280
-
281
- onSuccess: (result: UploadFile) => {
282
- updateState({
283
- status: "success",
284
- result,
285
- progress: 100,
286
- bytesUploaded: result.size || 0,
287
- totalBytes: result.size || null,
288
- });
289
-
290
- options.onSuccess?.(result);
291
- abortControllerRef.current = null;
292
- },
293
-
294
- onError: (error: Error) => {
295
- updateState({
296
- status: "error",
297
- error,
298
- });
299
-
300
- options.onError?.(error);
301
- abortControllerRef.current = null;
302
- },
303
-
304
201
  onShouldRetry: options.onShouldRetry,
305
- });
306
-
307
- // Handle the promise to get the abort controller
308
- uploadPromise
309
- .then((controller) => {
310
- abortControllerRef.current = controller;
311
- })
312
- .catch((error) => {
313
- updateState({
314
- status: "error",
315
- error: error as Error,
316
- });
317
-
318
- options.onError?.(error as Error);
319
- abortControllerRef.current = null;
320
- });
321
- },
322
- [uploadClient, options, updateState],
323
- );
324
-
325
- const retry = useCallback(() => {
326
- if (
327
- lastFileRef.current &&
328
- (state.status === "error" || state.status === "aborted")
329
- ) {
330
- upload(lastFileRef.current);
331
- }
332
- }, [upload, state.status]);
333
-
334
- // Store current upload ID for event matching
335
- const currentUploadIdRef = useRef<string | null>(null);
336
-
337
- // Subscribe to events from context (WebSocket events)
338
- useEffect(() => {
339
- const unsubscribe = uploadClient.subscribeToEvents(
340
- (event: UploadistaEvent) => {
341
- // Handle upload progress events
342
- const uploadEvent = event as {
343
- type: string;
344
- data?: { id: string; progress: number; total: number };
345
- };
346
- if (
347
- uploadEvent.type === UploadEventType.UPLOAD_PROGRESS &&
348
- uploadEvent.data
349
- ) {
350
- const {
351
- id: uploadId,
352
- progress: bytesUploaded,
353
- total: totalBytes,
354
- } = uploadEvent.data;
202
+ },
203
+ );
355
204
 
356
- if (uploadId !== currentUploadIdRef.current) {
357
- return;
358
- }
205
+ return () => {
206
+ managerRef.current?.cleanup();
207
+ };
208
+ }, [uploadClient, options]);
359
209
 
360
- // Update state for this upload
361
- // Note: We update for all uploads since we don't track upload IDs in single upload mode
362
- const progress = totalBytes
363
- ? Math.round((bytesUploaded / totalBytes) * 100)
364
- : 0;
210
+ // Wrap manager methods with useCallback
211
+ const upload = useCallback((file: BrowserUploadInput) => {
212
+ managerRef.current?.upload(file);
213
+ }, []);
365
214
 
366
- setState((prev) => {
367
- // Only update if we're currently uploading
368
- if (prev.status === "uploading") {
369
- return {
370
- ...prev,
371
- progress,
372
- bytesUploaded,
373
- totalBytes,
374
- };
375
- }
376
- return prev;
377
- });
215
+ const abort = useCallback(() => {
216
+ managerRef.current?.abort();
217
+ }, []);
378
218
 
379
- options.onProgress?.(progress, bytesUploaded, totalBytes);
380
- }
381
- },
382
- );
219
+ const reset = useCallback(() => {
220
+ managerRef.current?.reset();
221
+ }, []);
383
222
 
384
- return unsubscribe;
385
- }, [uploadClient, options]);
223
+ const retry = useCallback(() => {
224
+ managerRef.current?.retry();
225
+ }, []);
386
226
 
227
+ // Derive computed values from state
387
228
  const isUploading = state.status === "uploading";
388
- const canRetry =
389
- (state.status === "error" || state.status === "aborted") &&
390
- lastFileRef.current !== null;
229
+ const canRetry = managerRef.current?.canRetry() ?? false;
391
230
 
392
231
  // Create metrics object that delegates to the upload client
393
232
  const metrics: UploadMetrics = {
@@ -1,2 +0,0 @@
1
- import{createContext as e,useCallback as t,useContext as n,useEffect as r,useMemo as i,useRef as a,useState as o}from"react";import{createUploadistaClient as s}from"@uploadista/client-browser";import{jsx as c}from"react/jsx-runtime";import{EventType as l}from"@uploadista/core/flow";import{UploadEventType as u}from"@uploadista/core/types";function d(e){let t=a(e);return t.current=e,{client:i(()=>s({baseUrl:e.baseUrl,storageId:e.storageId,uploadistaBasePath:e.uploadistaBasePath,chunkSize:e.chunkSize,storeFingerprintForResuming:e.storeFingerprintForResuming,retryDelays:e.retryDelays,parallelUploads:e.parallelUploads,parallelChunkSize:e.parallelChunkSize,uploadStrategy:e.uploadStrategy,smartChunking:e.smartChunking,networkMonitoring:e.networkMonitoring,uploadMetrics:e.uploadMetrics,connectionPooling:e.connectionPooling,auth:e.auth,onEvent:e.onEvent}),[e]),config:e}}const f=e(null);function p({children:e,...n}){let r=a(new Set),o=t(e=>{console.log(`[UploadistaProvider] Received event:`,e),n.onEvent?.(e),console.log(`[UploadistaProvider] Broadcasting to`,r.current.size,`subscribers`),r.current.forEach(t=>{try{t(e)}catch(e){console.error(`Error in event subscriber:`,e)}})},[n.onEvent]),s=d({...n,onEvent:o}),l=t(e=>(r.current.add(e),()=>{r.current.delete(e)}),[]),u=i(()=>({...s,subscribeToEvents:l}),[s,l]);return c(f.Provider,{value:u,children:e})}function m(){let e=n(f);if(e===null)throw Error(`useUploadistaContext must be used within an UploadistaProvider. Make sure to wrap your component tree with <UploadistaProvider>.`);return e}function h(e){let n=m(),[r,i]=o([]),s=a(new Map),c=a([]),l=a(0),u=e.maxConcurrent??3,d=t(e=>{if(e.length===0)return 0;let t=e.reduce((e,t)=>e+t.progress,0);return Math.round(t/e.length)},[]),f=t(async()=>{if(l.current>=u||c.current.length===0)return;let t=c.current.shift();if(!t)return;let a=r.find(e=>e.id===t);if(!a||a.status!==`pending`){f();return}l.current++,i(e=>e.map(e=>e.id===t?{...e,status:`uploading`}:e));try{let{abort:r,jobId:o}=await n.client.uploadWithFlow(a.file,e.flowConfig,{onJobStart:e=>{i(n=>n.map(n=>n.id===t?{...n,jobId:e}:n))},onProgress:(n,r,a)=>{let o=a?Math.round(r/a*100):0;i(n=>{let i=n.map(e=>e.id===t?{...e,progress:o,bytesUploaded:r,totalBytes:a||0}:e),s=i.find(e=>e.id===t);return s&&e.onItemProgress?.(s),i})},onSuccess:n=>{i(r=>{let i=r.map(e=>e.id===t?{...e,status:`success`,result:n,progress:100}:e),a=i.find(e=>e.id===t);return a&&e.onItemSuccess?.(a),i.every(e=>e.status===`success`||e.status===`error`||e.status===`aborted`)&&e.onComplete?.(i),i}),s.current.delete(t),l.current--,f()},onError:n=>{i(r=>{let i=r.map(e=>e.id===t?{...e,status:`error`,error:n}:e),a=i.find(e=>e.id===t);return a&&e.onItemError?.(a,n),i.every(e=>e.status===`success`||e.status===`error`||e.status===`aborted`)&&e.onComplete?.(i),i}),s.current.delete(t),l.current--,f()},onShouldRetry:e.onShouldRetry});s.current.set(t,r),i(e=>e.map(e=>e.id===t?{...e,jobId:o}:e))}catch(e){i(n=>n.map(n=>n.id===t?{...n,status:`error`,error:e}:n)),l.current--,f()}},[n,r,u,e]),p=t(e=>{let t=Array.from(e).map(e=>({id:`${Date.now()}-${Math.random().toString(36).substr(2,9)}`,file:e,status:`pending`,progress:0,bytesUploaded:0,totalBytes:e.size,error:null,result:null,jobId:null}));i(e=>[...e,...t])},[]),h=t(e=>{let t=s.current.get(e);t&&(t(),s.current.delete(e)),i(t=>t.filter(t=>t.id!==e)),c.current=c.current.filter(t=>t!==e)},[]),g=t(()=>{let e=r.filter(e=>e.status===`pending`);c.current.push(...e.map(e=>e.id));for(let e=0;e<u;e++)f()},[r,u,f]),_=t(e=>{let t=s.current.get(e);t&&(t(),s.current.delete(e),i(t=>t.map(t=>t.id===e?{...t,status:`aborted`}:t)),l.current--,f())},[f]),v=t(()=>{for(let e of s.current.values())e();s.current.clear(),c.current=[],l.current=0,i(e=>e.map(e=>e.status===`uploading`?{...e,status:`aborted`}:e))},[]),y=t(()=>{v(),i([])},[v]),b=t(e=>{i(t=>t.map(t=>t.id===e?{...t,status:`pending`,progress:0,bytesUploaded:0,error:null}:t)),c.current.push(e),f()},[f]),x={items:r,totalProgress:d(r),activeUploads:r.filter(e=>e.status===`uploading`).length,completedUploads:r.filter(e=>e.status===`success`).length,failedUploads:r.filter(e=>e.status===`error`).length};return{state:x,addFiles:p,removeFile:h,startUpload:g,abortUpload:_,abortAll:v,clear:y,retryUpload:b,isUploading:x.activeUploads>0}}const g={isDragging:!1,isOver:!1,isValid:!0,errors:[]};function _(e={}){let{accept:n,maxFiles:r,maxFileSize:i,multiple:s=!0,validator:c,onFilesReceived:l,onValidationError:u,onDragStateChange:d}=e,[f,p]=o(g),m=a(null),h=a(0),_=t(e=>{p(t=>({...t,...e}))},[]),v=t(e=>{let t=[];r&&e.length>r&&t.push(`Maximum ${r} files allowed. You selected ${e.length} files.`);for(let r of e){if(i&&r.size>i){let e=(i/(1024*1024)).toFixed(1),n=(r.size/(1024*1024)).toFixed(1);t.push(`File "${r.name}" (${n}MB) exceeds maximum size of ${e}MB.`)}n&&n.length>0&&(n.some(e=>{if(e.startsWith(`.`))return r.name.toLowerCase().endsWith(e.toLowerCase());if(e.endsWith(`/*`)){let t=e.slice(0,-2);return r.type.startsWith(t)}else return r.type===e})||t.push(`File "${r.name}" type "${r.type}" is not accepted. Accepted types: ${n.join(`, `)}.`))}if(c){let n=c(e);n&&t.push(...n)}return t},[n,r,i,c]),y=t(e=>{let t=Array.from(e),n=v(t);n.length>0?(_({errors:n,isValid:!1}),u?.(n)):(_({errors:[],isValid:!0}),l?.(t))},[v,_,l,u]),b=t(e=>{let t=[];if(e.items)for(let n=0;n<e.items.length;n++){let r=e.items[n];if(r&&r.kind===`file`){let e=r.getAsFile();e&&t.push(e)}}else for(let n=0;n<e.files.length;n++){let r=e.files[n];r&&t.push(r)}return t},[]),x=t(e=>{e.preventDefault(),e.stopPropagation(),h.current++,h.current===1&&(_({isDragging:!0,isOver:!0}),d?.(!0))},[_,d]),S=t(e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer&&(e.dataTransfer.dropEffect=`copy`)},[]),C=t(e=>{e.preventDefault(),e.stopPropagation(),h.current--,h.current===0&&(_({isDragging:!1,isOver:!1,errors:[]}),d?.(!1))},[_,d]),w=t(e=>{if(e.preventDefault(),e.stopPropagation(),h.current=0,_({isDragging:!1,isOver:!1}),d?.(!1),e.dataTransfer){let t=b(e.dataTransfer);t.length>0&&y(t)}},[_,d,b,y]),T=t(()=>{m.current?.click()},[]),E=t(e=>{e.target.files&&e.target.files.length>0&&y(Array.from(e.target.files)),e.target.value=``},[y]),D=t(()=>{p(g),h.current=0},[]);return{state:f,dragHandlers:{onDragEnter:x,onDragOver:S,onDragLeave:C,onDrop:w},inputProps:{type:`file`,multiple:s,accept:n?.join(`, `),onChange:E,style:{display:`none`},ref:m},openFilePicker:T,processFiles:y,reset:D}}function v(e){let t=e;return t.eventType===l.FlowStart||t.eventType===l.FlowEnd||t.eventType===l.FlowError||t.eventType===l.NodeStart||t.eventType===l.NodeEnd||t.eventType===l.NodePause||t.eventType===l.NodeResume||t.eventType===l.NodeError}const y={status:`idle`,progress:0,bytesUploaded:0,totalBytes:null,error:null,result:null,jobId:null,flowStarted:!1,currentNodeName:null,currentNodeType:null,flowOutputs:null};function b(e){let n=m(),[i,s]=o(y),c=a(null),d=a(null),f=a(e.onSuccess),p=a(e.onError),h=a(e.onFlowComplete),g=a(e.flowConfig.outputNodeId);r(()=>{f.current=e.onSuccess,p.current=e.onError,h.current=e.onFlowComplete,g.current=e.flowConfig.outputNodeId},[e.onSuccess,e.onError,e.onFlowComplete,e.flowConfig.outputNodeId]);let _=a(null);r(()=>{_.current=i.jobId},[i.jobId]);let b=t(e=>{if(console.log(`[useFlowUpload] Received event:`,e,`Current jobId:`,_.current),!_.current||e.jobId!==_.current){console.log(`[useFlowUpload] Ignoring event - jobId mismatch`);return}switch(console.log(`[useFlowUpload] Processing event type:`,e.eventType),e.eventType){case l.FlowStart:console.log(`[useFlowUpload] Flow started`),s(e=>({...e,flowStarted:!0,status:`processing`}));break;case l.NodeStart:console.log(`[useFlowUpload] Node started:`,e.nodeName),s(t=>({...t,status:`processing`,currentNodeName:e.nodeName,currentNodeType:e.nodeType}));break;case l.NodePause:console.log(`[useFlowUpload] Node paused (waiting for upload):`,e.nodeName),s(t=>({...t,status:`uploading`,currentNodeName:e.nodeName}));break;case l.NodeResume:console.log(`[useFlowUpload] Node resumed (upload complete):`,e.nodeName),s(t=>({...t,status:`processing`,currentNodeName:e.nodeName,currentNodeType:e.nodeType}));break;case l.NodeEnd:console.log(`[useFlowUpload] Node ended:`,e.nodeName),s(e=>({...e,status:e.status===`uploading`?`processing`:e.status,currentNodeName:null,currentNodeType:null}));break;case l.FlowEnd:console.log(`[useFlowUpload] Flow ended, processing outputs`),s(t=>{let n=e.result||null;console.log(`[useFlowUpload] Flow outputs:`,n),n&&h.current&&(console.log(`[useFlowUpload] Calling onFlowComplete with outputs:`,n),h.current(n));let r=null;return n&&(g.current&&g.current in n?(r=n[g.current],console.log(`[useFlowUpload] Extracted output from specified node:`,g.current)):(r=Object.values(n)[0],console.log(`[useFlowUpload] Extracted output from first node`))),r&&f.current?(console.log(`[useFlowUpload] Calling onSuccess with result:`,r),f.current(r)):!r&&f.current&&console.warn(`[useFlowUpload] No result available for onSuccess`),{...t,status:`success`,currentNodeName:null,currentNodeType:null,result:r,flowOutputs:n}});break;case l.FlowError:console.log(`[useFlowUpload] Flow error:`,e.error),s(t=>({...t,status:`error`,error:Error(e.error)})),p.current?.(Error(e.error));break;case l.NodeError:console.log(`[useFlowUpload] Node error:`,e.error),s(t=>({...t,status:`error`,error:Error(e.error)})),p.current?.(Error(e.error));break}},[]);return r(()=>(console.log(`[useFlowUpload] Subscribing to events from context`),n.subscribeToEvents(e=>{if(v(e)){b(e);return}let t=e;if(t.type===u.UPLOAD_PROGRESS&&t.flow?.jobId===_.current&&t.data){let{progress:e,total:n}=t.data,r=n?Math.round(e/n*100):0;console.log(`[useFlowUpload] Upload progress event:`,{progress:r,bytesUploaded:e,totalBytes:n,jobId:t.flow.jobId}),s(t=>({...t,progress:r,bytesUploaded:e,totalBytes:n}))}})),[n,b]),{state:i,upload:t(async t=>{_.current=null,s({...y,status:`uploading`,totalBytes:t.size});try{let{abort:r,pause:i}=await n.client.uploadWithFlow(t,e.flowConfig,{onJobStart:e=>{_.current=e,s(t=>({...t,jobId:e}))},onProgress:(t,n,r)=>{let i=r?Math.round(n/r*100):0;s(e=>({...e,progress:i,bytesUploaded:n,totalBytes:r})),e.onProgress?.(i,n,r)},onChunkComplete:e.onChunkComplete,onSuccess:e=>{s(e=>({...e,progress:100}))},onError:t=>{s(e=>({...e,status:`error`,error:t})),e.onError?.(t)},onShouldRetry:e.onShouldRetry});c.current=r,d.current=i}catch(t){s(e=>({...e,status:`error`,error:t})),e.onError?.(t)}},[n,e]),abort:t(()=>{c.current&&(c.current(),c.current=null,s(e=>({...e,status:`aborted`})),e.onAbort?.())},[e]),pause:t(()=>{d.current&&=(d.current(),null)},[]),reset:t(()=>{s(y),c.current=null,_.current=null},[]),isUploading:i.status===`uploading`||i.status===`processing`,isUploadingFile:i.status===`uploading`,isProcessing:i.status===`processing`}}function x(e={}){let n=m(),{maxConcurrent:r=3}=e,[i,s]=o([]),c=a([]),l=a(0),u=a(new Set),d=a(new Map);c.current=i;let f=t(()=>`upload-${Date.now()}-${l.current++}`,[]),p=t((e,t)=>{s(n=>{let r=n.map(n=>n.id===e?{...n,state:{...n.state,...t}}:n);return c.current=r,r})},[]),h=t(()=>{let t=c.current;if(t.every(e=>[`success`,`error`,`aborted`].includes(e.state.status))&&t.length>0){let n=t.filter(e=>e.state.status===`success`),r=t.filter(e=>[`error`,`aborted`].includes(e.state.status));e.onComplete?.({successful:n,failed:r,total:t.length})}},[e]),g=t(()=>{if(u.current.size>=r)return;let t=c.current.find(e=>e.state.status===`idle`&&!u.current.has(e.id));t&&(async()=>{u.current.add(t.id),e.onUploadStart?.(t),p(t.id,{status:`uploading`});try{let r=await n.client.upload(t.file,{metadata:e.metadata,uploadLengthDeferred:e.uploadLengthDeferred,uploadSize:e.uploadSize,onProgress:(n,r,i)=>{let a=i?Math.round(r/i*100):0;p(t.id,{progress:a,bytesUploaded:r,totalBytes:i}),e.onUploadProgress?.(t,a,r,i)},onChunkComplete:()=>{},onSuccess:n=>{p(t.id,{status:`success`,result:n,progress:100});let r={...t,state:{...t.state,status:`success`,result:n}};e.onUploadSuccess?.(r,n),u.current.delete(t.id),d.current.delete(t.id),g(),h()},onError:n=>{p(t.id,{status:`error`,error:n});let r={...t,state:{...t.state,status:`error`,error:n}};e.onUploadError?.(r,n),u.current.delete(t.id),d.current.delete(t.id),g(),h()},onShouldRetry:e.onShouldRetry});d.current.set(t.id,r)}catch(n){p(t.id,{status:`error`,error:n});let r={...t,state:{...t.state,status:`error`,error:n}};e.onUploadError?.(r,n),u.current.delete(t.id),d.current.delete(t.id),g(),h()}})()},[r,n,e,p,h]),_={total:i.length,completed:i.filter(e=>[`success`,`error`,`aborted`].includes(e.state.status)).length,successful:i.filter(e=>e.state.status===`success`).length,failed:i.filter(e=>[`error`,`aborted`].includes(e.state.status)).length,uploading:i.filter(e=>e.state.status===`uploading`).length,progress:i.length>0?Math.round(i.reduce((e,t)=>e+t.state.progress,0)/i.length):0,totalBytesUploaded:i.reduce((e,t)=>e+t.state.bytesUploaded,0),totalBytes:i.reduce((e,t)=>e+(t.state.totalBytes||0),0),isUploading:i.some(e=>e.state.status===`uploading`),isComplete:i.length>0&&i.every(e=>[`success`,`error`,`aborted`].includes(e.state.status))},v=t(e=>{let t=e.map(e=>({id:f(),file:e,state:{status:`idle`,progress:0,bytesUploaded:0,totalBytes:e instanceof File?e.size:null,error:null,result:null}}));console.log(`addFiles: Adding`,t.length,`files`);let n=[...c.current,...t];c.current=n,console.log(`addFiles: Updated itemsRef.current to`,n.length,`items`),s(n)},[f]),y=t(e=>{let t=c.current.find(t=>t.id===e);if(t&&t.state.status===`uploading`){let t=d.current.get(e);t&&(t.abort(),d.current.delete(e))}s(t=>{let n=t.filter(t=>t.id!==e);return c.current=n,n}),u.current.delete(e)},[]),b=t(e=>{let t=c.current.find(t=>t.id===e);if(t&&t.state.status===`uploading`){let t=d.current.get(e);t&&(t.abort(),d.current.delete(e)),u.current.delete(e),s(t=>{let n=t.map(t=>t.id===e?{...t,state:{...t.state,status:`aborted`}}:t);return c.current=n,n}),g()}},[g]),x=t(e=>{let t=c.current.find(t=>t.id===e);t&&[`error`,`aborted`].includes(t.state.status)&&(s(t=>{let n=t.map(t=>t.id===e?{...t,state:{...t.state,status:`idle`,error:null}}:t);return c.current=n,n}),setTimeout(()=>g(),0))},[g]),S=t(()=>{let e=c.current;console.log(`Starting all uploads`,e);let t=e.filter(e=>e.state.status===`idle`),n=r-u.current.size,i=t.slice(0,n);for(let e of i)console.log(`Starting next upload`,e),g()},[r,g]),C=t(()=>{c.current.filter(e=>e.state.status===`uploading`).forEach(e=>{let t=d.current.get(e.id);t&&(t.abort(),d.current.delete(e.id))}),u.current.clear(),s(e=>{let t=e.map(e=>e.state.status===`uploading`?{...e,state:{...e.state,status:`aborted`}}:e);return c.current=t,t})},[]);return{state:_,items:i,addFiles:v,removeItem:y,removeFile:y,startAll:S,abortUpload:b,abortAll:C,retryUpload:x,retryFailed:t(()=>{let e=c.current.filter(e=>[`error`,`aborted`].includes(e.state.status));e.length>0&&(s(t=>{let n=t.map(t=>e.some(e=>e.id===t.id)?{...t,state:{...t.state,status:`idle`,error:null}}:t);return c.current=n,n}),setTimeout(S,0))},[S]),clearCompleted:t(()=>{s(e=>{let t=e.filter(e=>![`success`,`error`,`aborted`].includes(e.state.status));return c.current=t,t})},[]),clearAll:t(()=>{C(),s([]),c.current=[],u.current.clear()},[C]),getItemsByStatus:t(e=>c.current.filter(t=>t.state.status===e),[]),metrics:{getInsights:()=>n.client.getChunkingInsights(),exportMetrics:()=>n.client.exportMetrics(),getNetworkMetrics:()=>n.client.getNetworkMetrics(),getNetworkCondition:()=>n.client.getNetworkCondition(),resetMetrics:()=>n.client.resetMetrics()}}}const S={status:`idle`,progress:0,bytesUploaded:0,totalBytes:null,error:null,result:null};function C(e={}){let n=m(),[i,s]=o(S),c=a(null),l=a(null),d=t(e=>{s(t=>({...t,...e}))},[]),f=t(()=>{c.current&&=(c.current.abort(),null),s(S),l.current=null},[]),p=t(()=>{c.current&&=(c.current.abort(),null),d({status:`aborted`}),e.onAbort?.()},[e,d]),h=t(t=>{s({...S,status:`uploading`,totalBytes:t instanceof File?t.size:null}),l.current=t,n.client.upload(t,{metadata:e.metadata,uploadLengthDeferred:e.uploadLengthDeferred,uploadSize:e.uploadSize,onStart:({uploadId:e})=>{_.current=e},onProgress:(t,n,r)=>{let i=r?Math.round(n/r*100):0;d({progress:i,bytesUploaded:n,totalBytes:r}),e.onProgress?.(i,n,r)},onChunkComplete:(t,n,r)=>{e.onChunkComplete?.(t,n,r)},onSuccess:t=>{d({status:`success`,result:t,progress:100,bytesUploaded:t.size||0,totalBytes:t.size||null}),e.onSuccess?.(t),c.current=null},onError:t=>{d({status:`error`,error:t}),e.onError?.(t),c.current=null},onShouldRetry:e.onShouldRetry}).then(e=>{c.current=e}).catch(t=>{d({status:`error`,error:t}),e.onError?.(t),c.current=null})},[n,e,d]),g=t(()=>{l.current&&(i.status===`error`||i.status===`aborted`)&&h(l.current)},[h,i.status]),_=a(null);return r(()=>n.subscribeToEvents(t=>{let n=t;if(n.type===u.UPLOAD_PROGRESS&&n.data){let{id:t,progress:r,total:i}=n.data;if(t!==_.current)return;let a=i?Math.round(r/i*100):0;s(e=>e.status===`uploading`?{...e,progress:a,bytesUploaded:r,totalBytes:i}:e),e.onProgress?.(a,r,i)}}),[n,e]),{state:i,upload:h,abort:p,reset:f,retry:g,isUploading:i.status===`uploading`,canRetry:(i.status===`error`||i.status===`aborted`)&&l.current!==null,metrics:{getInsights:()=>n.client.getChunkingInsights(),exportMetrics:()=>n.client.exportMetrics(),getNetworkMetrics:()=>n.client.getNetworkMetrics(),getNetworkCondition:()=>n.client.getNetworkCondition(),resetMetrics:()=>n.client.resetMetrics()}}}export{h as a,d as c,_ as i,x as n,p as o,b as r,m as s,C as t};
2
- //# sourceMappingURL=use-upload-CvPqdohl.mjs.map