@uploadista/react 0.0.3 → 0.0.4

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.
@@ -0,0 +1,382 @@
1
+ import { BrowserUploadInput, MultiFlowUploadOptions, MultiFlowUploadState } from "@uploadista/client-browser";
2
+ import { ChunkMetrics as ChunkMetrics$1, PerformanceInsights as PerformanceInsights$1, UploadSessionMetrics as UploadSessionMetrics$1 } from "@uploadista/client-core";
3
+
4
+ //#region src/hooks/use-multi-flow-upload.d.ts
5
+
6
+ /**
7
+ * Return value from the useMultiFlowUpload hook with batch upload control methods.
8
+ *
9
+ * @property state - Aggregated state across all flow upload items
10
+ * @property addFiles - Add new files to the upload queue
11
+ * @property removeFile - Remove a file from the queue (aborts if uploading)
12
+ * @property startUpload - Begin uploading all pending files
13
+ * @property abortUpload - Cancel a specific upload by its ID
14
+ * @property abortAll - Cancel all active uploads
15
+ * @property clear - Remove all items and abort active uploads
16
+ * @property retryUpload - Retry a specific failed upload
17
+ * @property isUploading - True when any uploads are in progress
18
+ */
19
+ interface UseMultiFlowUploadReturn {
20
+ /**
21
+ * Current upload state
22
+ */
23
+ state: MultiFlowUploadState<BrowserUploadInput>;
24
+ /**
25
+ * Add files to upload queue
26
+ */
27
+ addFiles: (files: File[] | FileList) => void;
28
+ /**
29
+ * Remove a file from the queue
30
+ */
31
+ removeFile: (id: string) => void;
32
+ /**
33
+ * Start uploading all pending files
34
+ */
35
+ startUpload: () => void;
36
+ /**
37
+ * Abort a specific upload by ID
38
+ */
39
+ abortUpload: (id: string) => void;
40
+ /**
41
+ * Abort all active uploads
42
+ */
43
+ abortAll: () => void;
44
+ /**
45
+ * Clear all items (aborts any active uploads first)
46
+ */
47
+ clear: () => void;
48
+ /**
49
+ * Retry a specific failed upload by ID
50
+ */
51
+ retryUpload: (id: string) => void;
52
+ /**
53
+ * Whether uploads are in progress
54
+ */
55
+ isUploading: boolean;
56
+ }
57
+ /**
58
+ * React hook for uploading multiple files through a flow with concurrent upload management.
59
+ * Processes each file through the specified flow while respecting concurrency limits.
60
+ *
61
+ * Each file is uploaded and processed independently through the flow, with automatic
62
+ * queue management. Failed uploads can be retried individually, and uploads can be
63
+ * aborted at any time.
64
+ *
65
+ * Must be used within an UploadistaProvider. Flow events for each upload are automatically
66
+ * tracked and synchronized.
67
+ *
68
+ * @param options - Multi-flow upload configuration including flow config and concurrency settings
69
+ * @returns Multi-flow upload state and control methods
70
+ *
71
+ * @example
72
+ * ```tsx
73
+ * // Batch image upload with progress tracking
74
+ * function BatchImageUploader() {
75
+ * const multiFlowUpload = useMultiFlowUpload({
76
+ * flowConfig: {
77
+ * flowId: "image-optimization-flow",
78
+ * storageId: "s3-images",
79
+ * },
80
+ * maxConcurrent: 3, // Process 3 files at a time
81
+ * onItemSuccess: (item) => {
82
+ * console.log(`${item.file.name} uploaded successfully`);
83
+ * },
84
+ * onItemError: (item, error) => {
85
+ * console.error(`${item.file.name} failed:`, error);
86
+ * },
87
+ * onComplete: (items) => {
88
+ * const successful = items.filter(i => i.status === 'success');
89
+ * const failed = items.filter(i => i.status === 'error');
90
+ * console.log(`Batch complete: ${successful.length} successful, ${failed.length} failed`);
91
+ * },
92
+ * });
93
+ *
94
+ * return (
95
+ * <div>
96
+ * <input
97
+ * type="file"
98
+ * multiple
99
+ * accept="image/*"
100
+ * onChange={(e) => {
101
+ * if (e.target.files) {
102
+ * multiFlowUpload.addFiles(e.target.files);
103
+ * multiFlowUpload.startUpload();
104
+ * }
105
+ * }}
106
+ * />
107
+ *
108
+ * <div>
109
+ * <p>Overall Progress: {multiFlowUpload.state.totalProgress}%</p>
110
+ * <p>
111
+ * {multiFlowUpload.state.activeUploads} uploading,
112
+ * {multiFlowUpload.state.completedUploads} completed,
113
+ * {multiFlowUpload.state.failedUploads} failed
114
+ * </p>
115
+ * </div>
116
+ *
117
+ * <div>
118
+ * <button onClick={multiFlowUpload.startUpload} disabled={multiFlowUpload.isUploading}>
119
+ * Start All
120
+ * </button>
121
+ * <button onClick={multiFlowUpload.abortAll} disabled={!multiFlowUpload.isUploading}>
122
+ * Cancel All
123
+ * </button>
124
+ * <button onClick={multiFlowUpload.clear}>
125
+ * Clear List
126
+ * </button>
127
+ * </div>
128
+ *
129
+ * {multiFlowUpload.state.items.map((item) => (
130
+ * <div key={item.id} style={{
131
+ * border: '1px solid #ccc',
132
+ * padding: '1rem',
133
+ * marginBottom: '0.5rem'
134
+ * }}>
135
+ * <div>{item.file instanceof File ? item.file.name : 'File'}</div>
136
+ * <div>Status: {item.status}</div>
137
+ *
138
+ * {item.status === "uploading" && (
139
+ * <div>
140
+ * <progress value={item.progress} max={100} />
141
+ * <span>{item.progress}%</span>
142
+ * <button onClick={() => multiFlowUpload.abortUpload(item.id)}>
143
+ * Cancel
144
+ * </button>
145
+ * </div>
146
+ * )}
147
+ *
148
+ * {item.status === "error" && (
149
+ * <div>
150
+ * <p style={{ color: 'red' }}>{item.error?.message}</p>
151
+ * <button onClick={() => multiFlowUpload.retryUpload(item.id)}>
152
+ * Retry
153
+ * </button>
154
+ * <button onClick={() => multiFlowUpload.removeFile(item.id)}>
155
+ * Remove
156
+ * </button>
157
+ * </div>
158
+ * )}
159
+ *
160
+ * {item.status === "success" && (
161
+ * <div>
162
+ * <p style={{ color: 'green' }}>✓ Upload complete</p>
163
+ * <button onClick={() => multiFlowUpload.removeFile(item.id)}>
164
+ * Remove
165
+ * </button>
166
+ * </div>
167
+ * )}
168
+ * </div>
169
+ * ))}
170
+ * </div>
171
+ * );
172
+ * }
173
+ * ```
174
+ *
175
+ * @see {@link useFlowUpload} for single file flow uploads
176
+ * @see {@link useMultiUpload} for multi-file uploads without flow processing
177
+ */
178
+ declare function useMultiFlowUpload(options: MultiFlowUploadOptions<BrowserUploadInput>): UseMultiFlowUploadReturn;
179
+ //#endregion
180
+ //#region src/hooks/use-upload-metrics.d.ts
181
+ interface UploadMetrics {
182
+ /**
183
+ * Total bytes uploaded across all files
184
+ */
185
+ totalBytesUploaded: number;
186
+ /**
187
+ * Total bytes to upload across all files
188
+ */
189
+ totalBytes: number;
190
+ /**
191
+ * Overall upload speed in bytes per second
192
+ */
193
+ averageSpeed: number;
194
+ /**
195
+ * Current upload speed in bytes per second
196
+ */
197
+ currentSpeed: number;
198
+ /**
199
+ * Estimated time remaining in milliseconds
200
+ */
201
+ estimatedTimeRemaining: number | null;
202
+ /**
203
+ * Total number of files being tracked
204
+ */
205
+ totalFiles: number;
206
+ /**
207
+ * Number of files completed
208
+ */
209
+ completedFiles: number;
210
+ /**
211
+ * Number of files currently uploading
212
+ */
213
+ activeUploads: number;
214
+ /**
215
+ * Overall progress as percentage (0-100)
216
+ */
217
+ progress: number;
218
+ /**
219
+ * Peak upload speed achieved
220
+ */
221
+ peakSpeed: number;
222
+ /**
223
+ * Start time of the first upload
224
+ */
225
+ startTime: number | null;
226
+ /**
227
+ * End time of the last completed upload
228
+ */
229
+ endTime: number | null;
230
+ /**
231
+ * Total duration of all uploads
232
+ */
233
+ totalDuration: number | null;
234
+ /**
235
+ * Detailed performance insights from the upload client
236
+ */
237
+ insights: PerformanceInsights$1;
238
+ /**
239
+ * Session metrics for completed uploads
240
+ */
241
+ sessionMetrics: Partial<UploadSessionMetrics$1>[];
242
+ /**
243
+ * Detailed chunk metrics from recent uploads
244
+ */
245
+ chunkMetrics: ChunkMetrics$1[];
246
+ }
247
+ interface FileUploadMetrics {
248
+ id: string;
249
+ filename: string;
250
+ size: number;
251
+ bytesUploaded: number;
252
+ progress: number;
253
+ speed: number;
254
+ startTime: number;
255
+ endTime: number | null;
256
+ duration: number | null;
257
+ isComplete: boolean;
258
+ }
259
+ interface UseUploadMetricsOptions {
260
+ /**
261
+ * Interval for calculating current speed (in milliseconds)
262
+ */
263
+ speedCalculationInterval?: number;
264
+ /**
265
+ * Number of speed samples to keep for average calculation
266
+ */
267
+ speedSampleSize?: number;
268
+ /**
269
+ * Called when metrics are updated
270
+ */
271
+ onMetricsUpdate?: (metrics: UploadMetrics) => void;
272
+ /**
273
+ * Called when a file upload starts
274
+ */
275
+ onFileStart?: (fileMetrics: FileUploadMetrics) => void;
276
+ /**
277
+ * Called when a file upload progresses
278
+ */
279
+ onFileProgress?: (fileMetrics: FileUploadMetrics) => void;
280
+ /**
281
+ * Called when a file upload completes
282
+ */
283
+ onFileComplete?: (fileMetrics: FileUploadMetrics) => void;
284
+ }
285
+ interface UseUploadMetricsReturn {
286
+ /**
287
+ * Current overall metrics
288
+ */
289
+ metrics: UploadMetrics;
290
+ /**
291
+ * Individual file metrics
292
+ */
293
+ fileMetrics: FileUploadMetrics[];
294
+ /**
295
+ * Start tracking a new file upload
296
+ */
297
+ startFileUpload: (id: string, filename: string, size: number) => void;
298
+ /**
299
+ * Update progress for a file upload
300
+ */
301
+ updateFileProgress: (id: string, bytesUploaded: number) => void;
302
+ /**
303
+ * Mark a file upload as complete
304
+ */
305
+ completeFileUpload: (id: string) => void;
306
+ /**
307
+ * Remove a file from tracking
308
+ */
309
+ removeFile: (id: string) => void;
310
+ /**
311
+ * Reset all metrics
312
+ */
313
+ reset: () => void;
314
+ /**
315
+ * Get metrics for a specific file
316
+ */
317
+ getFileMetrics: (id: string) => FileUploadMetrics | undefined;
318
+ /**
319
+ * Export metrics as JSON
320
+ */
321
+ exportMetrics: () => {
322
+ overall: UploadMetrics;
323
+ files: FileUploadMetrics[];
324
+ exportTime: number;
325
+ };
326
+ }
327
+ /**
328
+ * React hook for tracking detailed upload metrics and performance statistics.
329
+ * Provides comprehensive monitoring of upload progress, speed, and timing data.
330
+ *
331
+ * @param options - Configuration and event handlers
332
+ * @returns Upload metrics state and control methods
333
+ *
334
+ * @example
335
+ * ```tsx
336
+ * const uploadMetrics = useUploadMetrics({
337
+ * speedCalculationInterval: 1000, // Update speed every second
338
+ * speedSampleSize: 10, // Keep last 10 speed samples for average
339
+ * onMetricsUpdate: (metrics) => {
340
+ * console.log(`Overall progress: ${metrics.progress}%`);
341
+ * console.log(`Speed: ${(metrics.currentSpeed / 1024).toFixed(1)} KB/s`);
342
+ * console.log(`ETA: ${metrics.estimatedTimeRemaining}ms`);
343
+ * },
344
+ * onFileComplete: (fileMetrics) => {
345
+ * console.log(`${fileMetrics.filename} completed in ${fileMetrics.duration}ms`);
346
+ * },
347
+ * });
348
+ *
349
+ * // Start tracking a file
350
+ * const handleFileStart = (file: File) => {
351
+ * uploadMetrics.startFileUpload(file.name, file.name, file.size);
352
+ * };
353
+ *
354
+ * // Update progress during upload
355
+ * const handleProgress = (fileId: string, bytesUploaded: number) => {
356
+ * uploadMetrics.updateFileProgress(fileId, bytesUploaded);
357
+ * };
358
+ *
359
+ * // Display metrics
360
+ * return (
361
+ * <div>
362
+ * <div>Overall Progress: {uploadMetrics.metrics.progress}%</div>
363
+ * <div>Speed: {(uploadMetrics.metrics.currentSpeed / 1024).toFixed(1)} KB/s</div>
364
+ * <div>Files: {uploadMetrics.metrics.completedFiles}/{uploadMetrics.metrics.totalFiles}</div>
365
+ *
366
+ * {uploadMetrics.metrics.estimatedTimeRemaining && (
367
+ * <div>ETA: {Math.round(uploadMetrics.metrics.estimatedTimeRemaining / 1000)}s</div>
368
+ * )}
369
+ *
370
+ * {uploadMetrics.fileMetrics.map((file) => (
371
+ * <div key={file.id}>
372
+ * {file.filename}: {file.progress}% ({(file.speed / 1024).toFixed(1)} KB/s)
373
+ * </div>
374
+ * ))}
375
+ * </div>
376
+ * );
377
+ * ```
378
+ */
379
+ declare function useUploadMetrics(options?: UseUploadMetricsOptions): UseUploadMetricsReturn;
380
+ //#endregion
381
+ export { useUploadMetrics as a, UseUploadMetricsReturn as i, UploadMetrics as n, UseMultiFlowUploadReturn as o, UseUploadMetricsOptions as r, useMultiFlowUpload as s, FileUploadMetrics as t };
382
+ //# sourceMappingURL=use-upload-metrics-9Q-8Hij9.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-upload-metrics-9Q-8Hij9.d.ts","names":[],"sources":["../src/hooks/use-multi-flow-upload.ts","../src/hooks/use-upload-metrics.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAuBA;;;;;;AAwKA;;;;AAE2B,UA1KV,wBAAA,CA0KU;;;;ECvLV,KAAA,EDiBR,oBCjBqB,CDiBA,kBCjBA,CAAA;EAqElB;;;EAUI,QAAA,EAAA,CAAA,KAAA,EDzDI,ICyDJ,EAAA,GDzDa,QCyDb,EAAA,GAAA,IAAA;EAAY;AAG5B;AAaA;EAc8B,UAAA,EAAA,CAAA,EAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAKA;;;EAUoB,WAAA,EAAA,GAAA,GAAA,IAAA;EAGjC;;;EAuCiB,WAAA,EAAA,CAAA,EAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAMrB;;;EAmFG,QAAA,EAAA,GAAA,GAAA,IAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBD1EhB,kBAAA,UACL,uBAAuB,sBAC/B;;;UCvLc,aAAA;;ADajB;;EAIS,kBAAA,EAAA,MAAA;EAKW;;;EA+JJ,UAAA,EAAA,MAAA;EACkB;;;EACP,YAAA,EAAA,MAAA;;;;ECvLV,YAAA,EAAA,MAAa;EAqElB;;;EAUI,sBAAA,EAAA,MAAA,GAAA,IAAA;EAAY;AAG5B;AAaA;EAc8B,UAAA,EAAA,MAAA;EAKA;;;EAUoB,cAAA,EAAA,MAAA;EAGjC;;;EAuCiB,aAAA,EAAA,MAAA;EAMrB;;;EAmFG,QAAA,EAAA,MAAA;;;;;;;;;;;;;;;;;;;;YA1LJ;;;;kBAKM,QAAQ;;;;gBAKV;;UAGC,iBAAA;;;;;;;;;;;;UAaA,uBAAA;;;;;;;;;;;;8BAca;;;;8BAKA;;;;iCAKG;;;;iCAKA;;UAGhB,sBAAA;;;;WAIN;;;;eAKI;;;;;;;;;;;;;;;;;;;;;;;;kCA8BmB;;;;;aAMrB;WACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkFK,gBAAA,WACL,0BACR"}
@@ -0,0 +1,2 @@
1
+ import{s as e}from"./use-upload-DpDLlhUA.js";import t,{useCallback as n,useRef as r,useState as i}from"react";const a={totalBytesUploaded:0,totalBytes:0,averageSpeed:0,currentSpeed:0,estimatedTimeRemaining:null,totalFiles:0,completedFiles:0,activeUploads:0,progress:0,peakSpeed:0,startTime:null,endTime:null,totalDuration:null,insights:{overallEfficiency:0,chunkingEffectiveness:0,networkStability:0,recommendations:[],optimalChunkSizeRange:{min:256*1024,max:2*1024*1024}},sessionMetrics:[],chunkMetrics:[]};function o(o={}){let{speedCalculationInterval:s=1e3,speedSampleSize:c=10,onMetricsUpdate:l,onFileStart:u,onFileProgress:d,onFileComplete:f}=o,p=e(),[m,h]=i(a),[g,_]=i([]),v=r([]),y=r(0),b=r(null),x=n((e,t)=>{let n={time:e,bytes:t};v.current.push(n),v.current.length>c&&(v.current=v.current.slice(-c));let r=0;if(v.current.length>=2){let e=v.current[v.current.length-1],t=v.current[v.current.length-2];if(e&&t){let n=(e.time-t.time)/1e3,i=e.bytes-t.bytes;r=n>0?i/n:0}}let i=0;if(v.current.length>=2){let e=v.current[0],t=v.current[v.current.length-1];if(e&&t){let n=(t.time-e.time)/1e3,r=t.bytes-e.bytes;i=n>0?r/n:0}}return{currentSpeed:r,averageSpeed:i}},[c]),S=n(()=>{let e=Date.now(),t=g.reduce((e,t)=>e+t.size,0),n=g.reduce((e,t)=>e+t.bytesUploaded,0),r=g.filter(e=>e.isComplete).length,i=g.filter(e=>!e.isComplete&&e.bytesUploaded>0).length,{currentSpeed:a,averageSpeed:o}=x(e,n),s=t>0?Math.round(n/t*100):0,c=null;a>0&&(c=(t-n)/a*1e3);let u=g.filter(e=>e.startTime>0),d=u.length>0?Math.min(...u.map(e=>e.startTime)):null,f=g.filter(e=>e.endTime!==null),_=f.length>0&&r===g.length?Math.max(...f.map(e=>e.endTime).filter(e=>e!==null)):null,v=d&&_?_-d:null,y={totalBytesUploaded:n,totalBytes:t,averageSpeed:o,currentSpeed:a,estimatedTimeRemaining:c,totalFiles:g.length,completedFiles:r,activeUploads:i,progress:s,peakSpeed:Math.max(m.peakSpeed,a),startTime:d,endTime:_,totalDuration:v,insights:p.client.getChunkingInsights(),sessionMetrics:[p.client.exportMetrics().session],chunkMetrics:p.client.exportMetrics().chunks};h(y),l?.(y)},[g,m.peakSpeed,x,l,p.client]),C=n(()=>(b.current&&clearInterval(b.current),b.current=setInterval(()=>{g.some(e=>!e.isComplete&&e.bytesUploaded>0)&&S()},s),()=>{b.current&&=(clearInterval(b.current),null)}),[s,S,g]),w=n((e,t,n)=>{let r={id:e,filename:t,size:n,bytesUploaded:0,progress:0,speed:0,startTime:Date.now(),endTime:null,duration:null,isComplete:!1};_(t=>t.find(t=>t.id===e)?t.map(t=>t.id===e?r:t):[...t,r]),u?.(r),g.filter(e=>!e.isComplete).length===0&&C()},[g,u,C]),T=n((e,t)=>{let n=Date.now();_(r=>r.map(r=>{if(r.id!==e)return r;let i=(n-r.startTime)/1e3,a=i>0?t/i:0,o=r.size>0?Math.round(t/r.size*100):0,s={...r,bytesUploaded:t,progress:o,speed:a};return d?.(s),s})),setTimeout(S,0)},[d,S]),E=n(e=>{let t=Date.now();_(n=>n.map(n=>{if(n.id!==e)return n;let r=t-n.startTime,i=r>0?n.size/r*1e3:0,a={...n,bytesUploaded:n.size,progress:100,speed:i,endTime:t,duration:r,isComplete:!0};return f?.(a),a})),setTimeout(S,0)},[f,S]),D=n(e=>{_(t=>t.filter(t=>t.id!==e)),setTimeout(S,0)},[S]),O=n(()=>{b.current&&=(clearInterval(b.current),null),h(a),_([]),v.current=[],y.current=0},[]),k=n(e=>g.find(t=>t.id===e),[g]),A=n(()=>({overall:m,files:g,exportTime:Date.now()}),[m,g]);return t.useEffect(()=>()=>{b.current&&clearInterval(b.current)},[]),{metrics:m,fileMetrics:g,startFileUpload:w,updateFileProgress:T,completeFileUpload:E,removeFile:D,reset:O,getFileMetrics:k,exportMetrics:A}}export{o as t};
2
+ //# sourceMappingURL=use-upload-metrics-Dhx_po4K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-upload-metrics-Dhx_po4K.js","names":["initialMetrics: UploadMetrics","estimatedTimeRemaining: number | null","newMetrics: UploadMetrics","fileMetric: FileUploadMetrics"],"sources":["../src/hooks/use-upload-metrics.ts"],"sourcesContent":["import type {\n ChunkMetrics,\n PerformanceInsights,\n UploadSessionMetrics,\n} from \"@uploadista/client-core\";\nimport React, { useCallback, useRef, useState } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\n\nexport type Timeout = ReturnType<typeof setInterval>;\n\nexport interface UploadMetrics {\n /**\n * Total bytes uploaded across all files\n */\n totalBytesUploaded: number;\n\n /**\n * Total bytes to upload across all files\n */\n totalBytes: number;\n\n /**\n * Overall upload speed in bytes per second\n */\n averageSpeed: number;\n\n /**\n * Current upload speed in bytes per second\n */\n currentSpeed: number;\n\n /**\n * Estimated time remaining in milliseconds\n */\n estimatedTimeRemaining: number | null;\n\n /**\n * Total number of files being tracked\n */\n totalFiles: number;\n\n /**\n * Number of files completed\n */\n completedFiles: number;\n\n /**\n * Number of files currently uploading\n */\n activeUploads: number;\n\n /**\n * Overall progress as percentage (0-100)\n */\n progress: number;\n\n /**\n * Peak upload speed achieved\n */\n peakSpeed: number;\n\n /**\n * Start time of the first upload\n */\n startTime: number | null;\n\n /**\n * End time of the last completed upload\n */\n endTime: number | null;\n\n /**\n * Total duration of all uploads\n */\n totalDuration: number | null;\n\n /**\n * Detailed performance insights from the upload client\n */\n insights: PerformanceInsights;\n\n /**\n * Session metrics for completed uploads\n */\n sessionMetrics: Partial<UploadSessionMetrics>[];\n\n /**\n * Detailed chunk metrics from recent uploads\n */\n chunkMetrics: ChunkMetrics[];\n}\n\nexport interface FileUploadMetrics {\n id: string;\n filename: string;\n size: number;\n bytesUploaded: number;\n progress: number;\n speed: number;\n startTime: number;\n endTime: number | null;\n duration: number | null;\n isComplete: boolean;\n}\n\nexport interface UseUploadMetricsOptions {\n /**\n * Interval for calculating current speed (in milliseconds)\n */\n speedCalculationInterval?: number;\n\n /**\n * Number of speed samples to keep for average calculation\n */\n speedSampleSize?: number;\n\n /**\n * Called when metrics are updated\n */\n onMetricsUpdate?: (metrics: UploadMetrics) => void;\n\n /**\n * Called when a file upload starts\n */\n onFileStart?: (fileMetrics: FileUploadMetrics) => void;\n\n /**\n * Called when a file upload progresses\n */\n onFileProgress?: (fileMetrics: FileUploadMetrics) => void;\n\n /**\n * Called when a file upload completes\n */\n onFileComplete?: (fileMetrics: FileUploadMetrics) => void;\n}\n\nexport interface UseUploadMetricsReturn {\n /**\n * Current overall metrics\n */\n metrics: UploadMetrics;\n\n /**\n * Individual file metrics\n */\n fileMetrics: FileUploadMetrics[];\n\n /**\n * Start tracking a new file upload\n */\n startFileUpload: (id: string, filename: string, size: number) => void;\n\n /**\n * Update progress for a file upload\n */\n updateFileProgress: (id: string, bytesUploaded: number) => void;\n\n /**\n * Mark a file upload as complete\n */\n completeFileUpload: (id: string) => void;\n\n /**\n * Remove a file from tracking\n */\n removeFile: (id: string) => void;\n\n /**\n * Reset all metrics\n */\n reset: () => void;\n\n /**\n * Get metrics for a specific file\n */\n getFileMetrics: (id: string) => FileUploadMetrics | undefined;\n\n /**\n * Export metrics as JSON\n */\n exportMetrics: () => {\n overall: UploadMetrics;\n files: FileUploadMetrics[];\n exportTime: number;\n };\n}\n\nconst initialMetrics: UploadMetrics = {\n totalBytesUploaded: 0,\n totalBytes: 0,\n averageSpeed: 0,\n currentSpeed: 0,\n estimatedTimeRemaining: null,\n totalFiles: 0,\n completedFiles: 0,\n activeUploads: 0,\n progress: 0,\n peakSpeed: 0,\n startTime: null,\n endTime: null,\n totalDuration: null,\n insights: {\n overallEfficiency: 0,\n chunkingEffectiveness: 0,\n networkStability: 0,\n recommendations: [],\n optimalChunkSizeRange: { min: 256 * 1024, max: 2 * 1024 * 1024 },\n },\n sessionMetrics: [],\n chunkMetrics: [],\n};\n\n/**\n * React hook for tracking detailed upload metrics and performance statistics.\n * Provides comprehensive monitoring of upload progress, speed, and timing data.\n *\n * @param options - Configuration and event handlers\n * @returns Upload metrics state and control methods\n *\n * @example\n * ```tsx\n * const uploadMetrics = useUploadMetrics({\n * speedCalculationInterval: 1000, // Update speed every second\n * speedSampleSize: 10, // Keep last 10 speed samples for average\n * onMetricsUpdate: (metrics) => {\n * console.log(`Overall progress: ${metrics.progress}%`);\n * console.log(`Speed: ${(metrics.currentSpeed / 1024).toFixed(1)} KB/s`);\n * console.log(`ETA: ${metrics.estimatedTimeRemaining}ms`);\n * },\n * onFileComplete: (fileMetrics) => {\n * console.log(`${fileMetrics.filename} completed in ${fileMetrics.duration}ms`);\n * },\n * });\n *\n * // Start tracking a file\n * const handleFileStart = (file: File) => {\n * uploadMetrics.startFileUpload(file.name, file.name, file.size);\n * };\n *\n * // Update progress during upload\n * const handleProgress = (fileId: string, bytesUploaded: number) => {\n * uploadMetrics.updateFileProgress(fileId, bytesUploaded);\n * };\n *\n * // Display metrics\n * return (\n * <div>\n * <div>Overall Progress: {uploadMetrics.metrics.progress}%</div>\n * <div>Speed: {(uploadMetrics.metrics.currentSpeed / 1024).toFixed(1)} KB/s</div>\n * <div>Files: {uploadMetrics.metrics.completedFiles}/{uploadMetrics.metrics.totalFiles}</div>\n *\n * {uploadMetrics.metrics.estimatedTimeRemaining && (\n * <div>ETA: {Math.round(uploadMetrics.metrics.estimatedTimeRemaining / 1000)}s</div>\n * )}\n *\n * {uploadMetrics.fileMetrics.map((file) => (\n * <div key={file.id}>\n * {file.filename}: {file.progress}% ({(file.speed / 1024).toFixed(1)} KB/s)\n * </div>\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useUploadMetrics(\n options: UseUploadMetricsOptions = {},\n): UseUploadMetricsReturn {\n const {\n speedCalculationInterval = 1000,\n speedSampleSize = 10,\n onMetricsUpdate,\n onFileStart,\n onFileProgress,\n onFileComplete,\n } = options;\n\n const uploadClient = useUploadistaContext();\n\n const [metrics, setMetrics] = useState<UploadMetrics>(initialMetrics);\n const [fileMetrics, setFileMetrics] = useState<FileUploadMetrics[]>([]);\n\n const speedSamplesRef = useRef<Array<{ time: number; bytes: number }>>([]);\n const lastUpdateRef = useRef<number>(0);\n const intervalRef = useRef<Timeout | null>(null);\n\n const calculateSpeed = useCallback(\n (currentTime: number, totalBytesUploaded: number) => {\n const sample = { time: currentTime, bytes: totalBytesUploaded };\n speedSamplesRef.current.push(sample);\n\n // Keep only recent samples\n if (speedSamplesRef.current.length > speedSampleSize) {\n speedSamplesRef.current = speedSamplesRef.current.slice(\n -speedSampleSize,\n );\n }\n\n // Calculate current speed (bytes per second)\n let currentSpeed = 0;\n if (speedSamplesRef.current.length >= 2) {\n const recent =\n speedSamplesRef.current[speedSamplesRef.current.length - 1];\n const previous =\n speedSamplesRef.current[speedSamplesRef.current.length - 2];\n if (recent && previous) {\n const timeDiff = (recent.time - previous.time) / 1000; // Convert to seconds\n const bytesDiff = recent.bytes - previous.bytes;\n currentSpeed = timeDiff > 0 ? bytesDiff / timeDiff : 0;\n }\n }\n\n // Calculate average speed\n let averageSpeed = 0;\n if (speedSamplesRef.current.length >= 2) {\n const first = speedSamplesRef.current[0];\n const last =\n speedSamplesRef.current[speedSamplesRef.current.length - 1];\n if (first && last) {\n const totalTime = (last.time - first.time) / 1000; // Convert to seconds\n const totalBytes = last.bytes - first.bytes;\n averageSpeed = totalTime > 0 ? totalBytes / totalTime : 0;\n }\n }\n\n return { currentSpeed, averageSpeed };\n },\n [speedSampleSize],\n );\n\n const updateMetrics = useCallback(() => {\n const now = Date.now();\n\n // Calculate totals from file metrics\n const totalBytes = fileMetrics.reduce((sum, file) => sum + file.size, 0);\n const totalBytesUploaded = fileMetrics.reduce(\n (sum, file) => sum + file.bytesUploaded,\n 0,\n );\n const completedFiles = fileMetrics.filter((file) => file.isComplete).length;\n const activeUploads = fileMetrics.filter(\n (file) => !file.isComplete && file.bytesUploaded > 0,\n ).length;\n\n // Calculate speeds\n const { currentSpeed, averageSpeed } = calculateSpeed(\n now,\n totalBytesUploaded,\n );\n\n // Calculate progress\n const progress =\n totalBytes > 0 ? Math.round((totalBytesUploaded / totalBytes) * 100) : 0;\n\n // Calculate estimated time remaining\n let estimatedTimeRemaining: number | null = null;\n if (currentSpeed > 0) {\n const remainingBytes = totalBytes - totalBytesUploaded;\n estimatedTimeRemaining = (remainingBytes / currentSpeed) * 1000; // Convert to milliseconds\n }\n\n // Find start and end times\n const activeTimes = fileMetrics.filter((file) => file.startTime > 0);\n const startTime =\n activeTimes.length > 0\n ? Math.min(...activeTimes.map((file) => file.startTime))\n : null;\n\n const completedTimes = fileMetrics.filter((file) => file.endTime !== null);\n const endTime =\n completedTimes.length > 0 && completedFiles === fileMetrics.length\n ? Math.max(\n ...completedTimes\n .map((file) => file.endTime)\n .filter((time) => time !== null),\n )\n : null;\n\n const totalDuration = startTime && endTime ? endTime - startTime : null;\n\n const newMetrics: UploadMetrics = {\n totalBytesUploaded,\n totalBytes,\n averageSpeed,\n currentSpeed,\n estimatedTimeRemaining,\n totalFiles: fileMetrics.length,\n completedFiles,\n activeUploads,\n progress,\n peakSpeed: Math.max(metrics.peakSpeed, currentSpeed),\n startTime,\n endTime,\n totalDuration,\n insights: uploadClient.client.getChunkingInsights(),\n sessionMetrics: [uploadClient.client.exportMetrics().session],\n chunkMetrics: uploadClient.client.exportMetrics().chunks,\n };\n\n setMetrics(newMetrics);\n onMetricsUpdate?.(newMetrics);\n }, [\n fileMetrics,\n metrics.peakSpeed,\n calculateSpeed,\n onMetricsUpdate,\n uploadClient.client,\n ]);\n\n // Set up periodic speed calculations\n const setupSpeedCalculation = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n }\n\n intervalRef.current = setInterval(() => {\n if (\n fileMetrics.some((file) => !file.isComplete && file.bytesUploaded > 0)\n ) {\n updateMetrics();\n }\n }, speedCalculationInterval);\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [speedCalculationInterval, updateMetrics, fileMetrics]);\n\n const startFileUpload = useCallback(\n (id: string, filename: string, size: number) => {\n const now = Date.now();\n\n const fileMetric: FileUploadMetrics = {\n id,\n filename,\n size,\n bytesUploaded: 0,\n progress: 0,\n speed: 0,\n startTime: now,\n endTime: null,\n duration: null,\n isComplete: false,\n };\n\n setFileMetrics((prev) => {\n const existing = prev.find((file) => file.id === id);\n if (existing) {\n return prev.map((file) => (file.id === id ? fileMetric : file));\n }\n return [...prev, fileMetric];\n });\n\n onFileStart?.(fileMetric);\n\n // Start speed calculation if this is the first active upload\n if (fileMetrics.filter((file) => !file.isComplete).length === 0) {\n setupSpeedCalculation();\n }\n },\n [fileMetrics, onFileStart, setupSpeedCalculation],\n );\n\n const updateFileProgress = useCallback(\n (id: string, bytesUploaded: number) => {\n const now = Date.now();\n\n setFileMetrics((prev) =>\n prev.map((file) => {\n if (file.id !== id) return file;\n\n const timeDiff = (now - file.startTime) / 1000; // seconds\n const speed = timeDiff > 0 ? bytesUploaded / timeDiff : 0;\n const progress =\n file.size > 0 ? Math.round((bytesUploaded / file.size) * 100) : 0;\n\n const updatedFile = {\n ...file,\n bytesUploaded,\n progress,\n speed,\n };\n\n onFileProgress?.(updatedFile);\n return updatedFile;\n }),\n );\n\n // Trigger metrics update\n setTimeout(updateMetrics, 0);\n },\n [onFileProgress, updateMetrics],\n );\n\n const completeFileUpload = useCallback(\n (id: string) => {\n const now = Date.now();\n\n setFileMetrics((prev) =>\n prev.map((file) => {\n if (file.id !== id) return file;\n\n const duration = now - file.startTime;\n const speed = duration > 0 ? (file.size / duration) * 1000 : 0; // bytes per second\n\n const completedFile = {\n ...file,\n bytesUploaded: file.size,\n progress: 100,\n speed,\n endTime: now,\n duration,\n isComplete: true,\n };\n\n onFileComplete?.(completedFile);\n return completedFile;\n }),\n );\n\n // Trigger metrics update\n setTimeout(updateMetrics, 0);\n },\n [onFileComplete, updateMetrics],\n );\n\n const removeFile = useCallback(\n (id: string) => {\n setFileMetrics((prev) => prev.filter((file) => file.id !== id));\n setTimeout(updateMetrics, 0);\n },\n [updateMetrics],\n );\n\n const reset = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n\n setMetrics(initialMetrics);\n setFileMetrics([]);\n speedSamplesRef.current = [];\n lastUpdateRef.current = 0;\n }, []);\n\n const getFileMetrics = useCallback(\n (id: string) => {\n return fileMetrics.find((file) => file.id === id);\n },\n [fileMetrics],\n );\n\n const exportMetrics = useCallback(() => {\n return {\n overall: metrics,\n files: fileMetrics,\n exportTime: Date.now(),\n };\n }, [metrics, fileMetrics]);\n\n // Cleanup on unmount\n React.useEffect(() => {\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n }\n };\n }, []);\n\n return {\n metrics,\n fileMetrics,\n startFileUpload,\n updateFileProgress,\n completeFileUpload,\n removeFile,\n reset,\n getFileMetrics,\n exportMetrics,\n };\n}\n"],"mappings":"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"}