@uploadista/react 0.0.20-beta.2 → 0.0.20-beta.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.
- package/dist/components/index.d.mts +3 -3
- package/dist/components/index.mjs +1 -1
- package/dist/flow-upload-list-D6j8JSP8.mjs +2 -0
- package/dist/flow-upload-list-D6j8JSP8.mjs.map +1 -0
- package/dist/hooks/index.d.mts +3 -3
- package/dist/hooks/index.mjs +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +1 -1
- package/dist/{uploadista-provider-D-N-eL2l.d.mts → uploadista-provider-Cb13AK7Z.d.mts} +515 -318
- package/dist/uploadista-provider-Cb13AK7Z.d.mts.map +1 -0
- package/dist/use-upload-BvvGROMR.mjs +2 -0
- package/dist/use-upload-BvvGROMR.mjs.map +1 -0
- package/dist/{use-uploadista-client-m9nF-irM.d.mts → use-uploadista-client-CkzVVmFT.d.mts} +121 -286
- package/dist/use-uploadista-client-CkzVVmFT.d.mts.map +1 -0
- package/dist/use-uploadista-events-BwUD-2Ck.mjs +2 -0
- package/dist/use-uploadista-events-BwUD-2Ck.mjs.map +1 -0
- package/dist/{use-upload-metrics-DhzS4lhG.d.mts → use-uploadista-events-CtDXJYrR.d.mts} +169 -371
- package/dist/use-uploadista-events-CtDXJYrR.d.mts.map +1 -0
- package/package.json +6 -6
- package/src/components/flow-primitives.tsx +843 -0
- package/src/components/index.tsx +31 -13
- package/src/hooks/index.ts +25 -37
- package/src/hooks/use-drag-drop.ts +1 -0
- package/src/index.ts +90 -81
- package/dist/upload-zone-BjWHuP7p.mjs +0 -6
- package/dist/upload-zone-BjWHuP7p.mjs.map +0 -1
- package/dist/uploadista-provider-D-N-eL2l.d.mts.map +0 -1
- package/dist/use-upload-BDHVhQsI.mjs +0 -2
- package/dist/use-upload-BDHVhQsI.mjs.map +0 -1
- package/dist/use-upload-metrics-Df90wIos.mjs +0 -2
- package/dist/use-upload-metrics-Df90wIos.mjs.map +0 -1
- package/dist/use-upload-metrics-DhzS4lhG.d.mts.map +0 -1
- package/dist/use-uploadista-client-m9nF-irM.d.mts.map +0 -1
- package/src/components/flow-input.tsx +0 -299
- package/src/components/flow-upload-zone.tsx +0 -441
- package/src/hooks/use-flow-execution.ts +0 -502
- package/src/hooks/use-flow-upload.ts +0 -299
|
@@ -1,209 +1,7 @@
|
|
|
1
|
+
import { FlowUploadState, FlowUploadStatus, InputExecutionState, UploadMetrics, UploadState, UploadStatus } from "@uploadista/client-core";
|
|
1
2
|
import { UploadFile } from "@uploadista/core/types";
|
|
2
|
-
import { FlowUploadState, FlowUploadState as FlowUploadState$1, FlowUploadStatus, InputExecutionState, UploadMetrics, UploadState, UploadStatus } from "@uploadista/client-core";
|
|
3
3
|
import { BrowserUploadInput, FlowUploadOptions, UploadistaClientOptions, createUploadistaClient } from "@uploadista/client-browser";
|
|
4
4
|
|
|
5
|
-
//#region src/hooks/use-flow.d.ts
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Input metadata discovered from the flow
|
|
9
|
-
*/
|
|
10
|
-
interface FlowInputMetadata {
|
|
11
|
-
/** Input node ID */
|
|
12
|
-
nodeId: string;
|
|
13
|
-
/** Human-readable node name */
|
|
14
|
-
nodeName: string;
|
|
15
|
-
/** Node description explaining what input is needed */
|
|
16
|
-
nodeDescription: string;
|
|
17
|
-
/** Input type ID from inputTypeRegistry - describes how clients interact with this node */
|
|
18
|
-
inputTypeId?: string;
|
|
19
|
-
/** Whether this input is required */
|
|
20
|
-
required: boolean;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Return value from the useFlow hook with upload control methods and state.
|
|
24
|
-
*
|
|
25
|
-
* @property state - Complete flow upload state with progress and outputs
|
|
26
|
-
* @property inputMetadata - Metadata about discovered input nodes (null until discovered)
|
|
27
|
-
* @property inputStates - Per-input execution state for multi-input flows
|
|
28
|
-
* @property inputs - Current input values set via setInput()
|
|
29
|
-
* @property setInput - Set an input value for a specific node (for progressive provision)
|
|
30
|
-
* @property execute - Execute the flow with current inputs (auto-detects types)
|
|
31
|
-
* @property upload - Convenience method for single-file upload (same as execute with one file input)
|
|
32
|
-
* @property abort - Cancel the current upload and flow execution
|
|
33
|
-
* @property pause - Pause the current upload
|
|
34
|
-
* @property reset - Reset state to idle (clears all data)
|
|
35
|
-
* @property isUploading - True when upload or processing is active
|
|
36
|
-
* @property isUploadingFile - True only during file upload phase
|
|
37
|
-
* @property isProcessing - True only during flow processing phase
|
|
38
|
-
* @property isDiscoveringInputs - True while discovering flow inputs
|
|
39
|
-
*/
|
|
40
|
-
interface UseFlowReturn {
|
|
41
|
-
/**
|
|
42
|
-
* Current upload state
|
|
43
|
-
*/
|
|
44
|
-
state: FlowUploadState$1;
|
|
45
|
-
/**
|
|
46
|
-
* Discovered input nodes metadata (null until discovery completes)
|
|
47
|
-
*/
|
|
48
|
-
inputMetadata: FlowInputMetadata[] | null;
|
|
49
|
-
/**
|
|
50
|
-
* Per-input execution state for multi-input flows
|
|
51
|
-
*/
|
|
52
|
-
inputStates: ReadonlyMap<string, InputExecutionState>;
|
|
53
|
-
/**
|
|
54
|
-
* Current inputs set via setInput()
|
|
55
|
-
*/
|
|
56
|
-
inputs: Record<string, unknown>;
|
|
57
|
-
/**
|
|
58
|
-
* Set an input value for a specific node.
|
|
59
|
-
* For progressive input provision before calling execute().
|
|
60
|
-
*
|
|
61
|
-
* @param nodeId - The input node ID
|
|
62
|
-
* @param value - The input value (File, URL string, or structured data)
|
|
63
|
-
*/
|
|
64
|
-
setInput: (nodeId: string, value: unknown) => void;
|
|
65
|
-
/**
|
|
66
|
-
* Execute the flow with current inputs.
|
|
67
|
-
* Automatically detects input types and routes appropriately.
|
|
68
|
-
* For single input, uses standard upload path.
|
|
69
|
-
* For multiple inputs, requires multiInputUploadFn.
|
|
70
|
-
*/
|
|
71
|
-
execute: () => Promise<void>;
|
|
72
|
-
/**
|
|
73
|
-
* Upload a single file through the flow (convenience method).
|
|
74
|
-
* Equivalent to setInput(firstNodeId, file) + execute().
|
|
75
|
-
*
|
|
76
|
-
* @param file - File or Blob to upload
|
|
77
|
-
*/
|
|
78
|
-
upload: (file: File | Blob) => Promise<void>;
|
|
79
|
-
/**
|
|
80
|
-
* Abort the current upload
|
|
81
|
-
*/
|
|
82
|
-
abort: () => void;
|
|
83
|
-
/**
|
|
84
|
-
* Pause the current upload
|
|
85
|
-
*/
|
|
86
|
-
pause: () => void;
|
|
87
|
-
/**
|
|
88
|
-
* Reset the upload state and clear all inputs
|
|
89
|
-
*/
|
|
90
|
-
reset: () => void;
|
|
91
|
-
/**
|
|
92
|
-
* Whether an upload or flow execution is in progress (uploading OR processing)
|
|
93
|
-
*/
|
|
94
|
-
isUploading: boolean;
|
|
95
|
-
/**
|
|
96
|
-
* Whether the file is currently being uploaded (chunks being sent)
|
|
97
|
-
*/
|
|
98
|
-
isUploadingFile: boolean;
|
|
99
|
-
/**
|
|
100
|
-
* Whether the flow is currently processing (after upload completes)
|
|
101
|
-
*/
|
|
102
|
-
isProcessing: boolean;
|
|
103
|
-
/**
|
|
104
|
-
* Whether the hook is discovering flow inputs
|
|
105
|
-
*/
|
|
106
|
-
isDiscoveringInputs: boolean;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* React hook for executing flows with single or multiple inputs.
|
|
110
|
-
* Automatically discovers input nodes and detects input types (File, URL, structured data).
|
|
111
|
-
* Supports progressive input provision via setInput() and execute().
|
|
112
|
-
*
|
|
113
|
-
* This is the unified flow hook that replaces useFlowUpload for advanced use cases.
|
|
114
|
-
* It provides:
|
|
115
|
-
* - Auto-discovery of flow input nodes
|
|
116
|
-
* - Automatic input type detection (file → upload, string → URL, object → data)
|
|
117
|
-
* - Progressive input provision via setInput()
|
|
118
|
-
* - Multi-input support with parallel coordination
|
|
119
|
-
* - Per-input state tracking
|
|
120
|
-
*
|
|
121
|
-
* Must be used within FlowManagerProvider (which must be within UploadistaProvider).
|
|
122
|
-
* Flow events are automatically routed by the provider to the appropriate manager.
|
|
123
|
-
*
|
|
124
|
-
* @param options - Flow upload configuration including flow ID and event handlers
|
|
125
|
-
* @returns Flow upload state and control methods
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```tsx
|
|
129
|
-
* // Single file upload (simple case)
|
|
130
|
-
* function SingleFileUploader() {
|
|
131
|
-
* const flow = useFlow({
|
|
132
|
-
* flowConfig: {
|
|
133
|
-
* flowId: "image-optimization",
|
|
134
|
-
* storageId: "s3-images",
|
|
135
|
-
* },
|
|
136
|
-
* onSuccess: (outputs) => {
|
|
137
|
-
* console.log("Flow outputs:", outputs);
|
|
138
|
-
* },
|
|
139
|
-
* });
|
|
140
|
-
*
|
|
141
|
-
* return (
|
|
142
|
-
* <div>
|
|
143
|
-
* <input
|
|
144
|
-
* type="file"
|
|
145
|
-
* onChange={(e) => {
|
|
146
|
-
* const file = e.target.files?.[0];
|
|
147
|
-
* if (file) flow.upload(file);
|
|
148
|
-
* }}
|
|
149
|
-
* />
|
|
150
|
-
* {flow.isUploading && <div>Progress: {flow.state.progress}%</div>}
|
|
151
|
-
* </div>
|
|
152
|
-
* );
|
|
153
|
-
* }
|
|
154
|
-
*
|
|
155
|
-
* // Multi-input with progressive provision
|
|
156
|
-
* function MultiInputFlow() {
|
|
157
|
-
* const flow = useFlow({
|
|
158
|
-
* flowConfig: {
|
|
159
|
-
* flowId: "multi-source-processing",
|
|
160
|
-
* storageId: "default",
|
|
161
|
-
* },
|
|
162
|
-
* });
|
|
163
|
-
*
|
|
164
|
-
* return (
|
|
165
|
-
* <div>
|
|
166
|
-
* {flow.inputMetadata?.map((input) => (
|
|
167
|
-
* <div key={input.nodeId}>
|
|
168
|
-
* <label>{input.nodeId}</label>
|
|
169
|
-
* {input.nodeType === "streaming-input-v1" ? (
|
|
170
|
-
* <input
|
|
171
|
-
* type="file"
|
|
172
|
-
* onChange={(e) => {
|
|
173
|
-
* const file = e.target.files?.[0];
|
|
174
|
-
* if (file) flow.setInput(input.nodeId, file);
|
|
175
|
-
* }}
|
|
176
|
-
* />
|
|
177
|
-
* ) : (
|
|
178
|
-
* <input
|
|
179
|
-
* type="url"
|
|
180
|
-
* onChange={(e) => flow.setInput(input.nodeId, e.target.value)}
|
|
181
|
-
* />
|
|
182
|
-
* )}
|
|
183
|
-
* </div>
|
|
184
|
-
* ))}
|
|
185
|
-
* <button onClick={flow.execute} disabled={flow.isUploading}>
|
|
186
|
-
* Execute Flow
|
|
187
|
-
* </button>
|
|
188
|
-
*
|
|
189
|
-
* {flow.isUploading && (
|
|
190
|
-
* <div>
|
|
191
|
-
* {Array.from(flow.inputStates.values()).map((inputState) => (
|
|
192
|
-
* <div key={inputState.nodeId}>
|
|
193
|
-
* {inputState.nodeId}: {inputState.status} ({inputState.progress}%)
|
|
194
|
-
* </div>
|
|
195
|
-
* ))}
|
|
196
|
-
* </div>
|
|
197
|
-
* )}
|
|
198
|
-
* </div>
|
|
199
|
-
* );
|
|
200
|
-
* }
|
|
201
|
-
* ```
|
|
202
|
-
*
|
|
203
|
-
* @see {@link useFlowUpload} for a simpler file-only upload hook
|
|
204
|
-
*/
|
|
205
|
-
declare function useFlow(options: FlowUploadOptions): UseFlowReturn;
|
|
206
|
-
//#endregion
|
|
207
5
|
//#region src/hooks/use-drag-drop.d.ts
|
|
208
6
|
interface DragDropOptions {
|
|
209
7
|
/**
|
|
@@ -282,6 +80,7 @@ interface UseDragDropReturn {
|
|
|
282
80
|
style: {
|
|
283
81
|
display: "none";
|
|
284
82
|
};
|
|
83
|
+
ref: React.RefObject<HTMLInputElement | null>;
|
|
285
84
|
};
|
|
286
85
|
/**
|
|
287
86
|
* Open file picker dialog
|
|
@@ -354,26 +153,77 @@ interface UseDragDropReturn {
|
|
|
354
153
|
*/
|
|
355
154
|
declare function useDragDrop(options?: DragDropOptions): UseDragDropReturn;
|
|
356
155
|
//#endregion
|
|
357
|
-
//#region src/hooks/use-flow
|
|
156
|
+
//#region src/hooks/use-flow.d.ts
|
|
157
|
+
/**
|
|
158
|
+
* Input metadata discovered from the flow
|
|
159
|
+
*/
|
|
160
|
+
interface FlowInputMetadata {
|
|
161
|
+
/** Input node ID */
|
|
162
|
+
nodeId: string;
|
|
163
|
+
/** Human-readable node name */
|
|
164
|
+
nodeName: string;
|
|
165
|
+
/** Node description explaining what input is needed */
|
|
166
|
+
nodeDescription: string;
|
|
167
|
+
/** Input type ID from inputTypeRegistry - describes how clients interact with this node */
|
|
168
|
+
inputTypeId?: string;
|
|
169
|
+
/** Whether this input is required */
|
|
170
|
+
required: boolean;
|
|
171
|
+
}
|
|
358
172
|
/**
|
|
359
|
-
* Return value from the
|
|
173
|
+
* Return value from the useFlow hook with upload control methods and state.
|
|
360
174
|
*
|
|
361
175
|
* @property state - Complete flow upload state with progress and outputs
|
|
362
|
-
* @property
|
|
176
|
+
* @property inputMetadata - Metadata about discovered input nodes (null until discovered)
|
|
177
|
+
* @property inputStates - Per-input execution state for multi-input flows
|
|
178
|
+
* @property inputs - Current input values set via setInput()
|
|
179
|
+
* @property setInput - Set an input value for a specific node (for progressive provision)
|
|
180
|
+
* @property execute - Execute the flow with current inputs (auto-detects types)
|
|
181
|
+
* @property upload - Convenience method for single-file upload (same as execute with one file input)
|
|
363
182
|
* @property abort - Cancel the current upload and flow execution
|
|
364
183
|
* @property pause - Pause the current upload
|
|
365
184
|
* @property reset - Reset state to idle (clears all data)
|
|
366
185
|
* @property isUploading - True when upload or processing is active
|
|
367
186
|
* @property isUploadingFile - True only during file upload phase
|
|
368
187
|
* @property isProcessing - True only during flow processing phase
|
|
188
|
+
* @property isDiscoveringInputs - True while discovering flow inputs
|
|
369
189
|
*/
|
|
370
|
-
interface
|
|
190
|
+
interface UseFlowReturn {
|
|
371
191
|
/**
|
|
372
192
|
* Current upload state
|
|
373
193
|
*/
|
|
374
194
|
state: FlowUploadState;
|
|
375
195
|
/**
|
|
376
|
-
*
|
|
196
|
+
* Discovered input nodes metadata (null until discovery completes)
|
|
197
|
+
*/
|
|
198
|
+
inputMetadata: FlowInputMetadata[] | null;
|
|
199
|
+
/**
|
|
200
|
+
* Per-input execution state for multi-input flows
|
|
201
|
+
*/
|
|
202
|
+
inputStates: ReadonlyMap<string, InputExecutionState>;
|
|
203
|
+
/**
|
|
204
|
+
* Current inputs set via setInput()
|
|
205
|
+
*/
|
|
206
|
+
inputs: Record<string, unknown>;
|
|
207
|
+
/**
|
|
208
|
+
* Set an input value for a specific node.
|
|
209
|
+
* For progressive input provision before calling execute().
|
|
210
|
+
*
|
|
211
|
+
* @param nodeId - The input node ID
|
|
212
|
+
* @param value - The input value (File, URL string, or structured data)
|
|
213
|
+
*/
|
|
214
|
+
setInput: (nodeId: string, value: unknown) => void;
|
|
215
|
+
/**
|
|
216
|
+
* Execute the flow with current inputs.
|
|
217
|
+
* Automatically detects input types and routes appropriately.
|
|
218
|
+
* For single input, uses standard upload path.
|
|
219
|
+
* For multiple inputs, requires multiInputUploadFn.
|
|
220
|
+
*/
|
|
221
|
+
execute: () => Promise<void>;
|
|
222
|
+
/**
|
|
223
|
+
* Upload a single file through the flow (convenience method).
|
|
224
|
+
* Equivalent to setInput(firstNodeId, file) + execute().
|
|
225
|
+
*
|
|
226
|
+
* @param file - File or Blob to upload
|
|
377
227
|
*/
|
|
378
228
|
upload: (file: File | Blob) => Promise<void>;
|
|
379
229
|
/**
|
|
@@ -385,7 +235,7 @@ interface UseFlowUploadReturn {
|
|
|
385
235
|
*/
|
|
386
236
|
pause: () => void;
|
|
387
237
|
/**
|
|
388
|
-
* Reset the upload state
|
|
238
|
+
* Reset the upload state and clear all inputs
|
|
389
239
|
*/
|
|
390
240
|
reset: () => void;
|
|
391
241
|
/**
|
|
@@ -400,18 +250,23 @@ interface UseFlowUploadReturn {
|
|
|
400
250
|
* Whether the flow is currently processing (after upload completes)
|
|
401
251
|
*/
|
|
402
252
|
isProcessing: boolean;
|
|
253
|
+
/**
|
|
254
|
+
* Whether the hook is discovering flow inputs
|
|
255
|
+
*/
|
|
256
|
+
isDiscoveringInputs: boolean;
|
|
403
257
|
}
|
|
404
258
|
/**
|
|
405
|
-
* React hook for
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
* This is a convenience wrapper around the generic useFlowExecution hook,
|
|
410
|
-
* specialized for the common case of file uploads. For more flexible input types
|
|
411
|
-
* (URLs, structured data), use useFlowExecution directly with an inputBuilder.
|
|
259
|
+
* React hook for executing flows with single or multiple inputs.
|
|
260
|
+
* Automatically discovers input nodes and detects input types (File, URL, structured data).
|
|
261
|
+
* Supports progressive input provision via setInput() and execute().
|
|
412
262
|
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
263
|
+
* This is the unified flow hook that replaces useFlowUpload for advanced use cases.
|
|
264
|
+
* It provides:
|
|
265
|
+
* - Auto-discovery of flow input nodes
|
|
266
|
+
* - Automatic input type detection (file → upload, string → URL, object → data)
|
|
267
|
+
* - Progressive input provision via setInput()
|
|
268
|
+
* - Multi-input support with parallel coordination
|
|
269
|
+
* - Per-input state tracking
|
|
415
270
|
*
|
|
416
271
|
* Must be used within FlowManagerProvider (which must be within UploadistaProvider).
|
|
417
272
|
* Flow events are automatically routed by the provider to the appropriate manager.
|
|
@@ -419,46 +274,17 @@ interface UseFlowUploadReturn {
|
|
|
419
274
|
* @param options - Flow upload configuration including flow ID and event handlers
|
|
420
275
|
* @returns Flow upload state and control methods
|
|
421
276
|
*
|
|
422
|
-
* @remarks
|
|
423
|
-
* **Future refactoring**: This hook could be implemented as a thin wrapper around
|
|
424
|
-
* useFlowExecution with a file-specific inputBuilder:
|
|
425
|
-
* ```typescript
|
|
426
|
-
* return useFlowExecution<File | Blob>({
|
|
427
|
-
* ...options,
|
|
428
|
-
* inputBuilder: async (file) => {
|
|
429
|
-
* const { inputNodes } = await client.findInputNode(options.flowConfig.flowId);
|
|
430
|
-
* return {
|
|
431
|
-
* [inputNodes[0].id]: {
|
|
432
|
-
* operation: "init",
|
|
433
|
-
* storageId: options.flowConfig.storageId,
|
|
434
|
-
* metadata: { originalName: file.name, mimeType: file.type, size: file.size }
|
|
435
|
-
* }
|
|
436
|
-
* };
|
|
437
|
-
* }
|
|
438
|
-
* });
|
|
439
|
-
* ```
|
|
440
|
-
*
|
|
441
277
|
* @example
|
|
442
278
|
* ```tsx
|
|
443
|
-
* //
|
|
444
|
-
* function
|
|
445
|
-
* const
|
|
279
|
+
* // Single file upload (simple case)
|
|
280
|
+
* function SingleFileUploader() {
|
|
281
|
+
* const flow = useFlow({
|
|
446
282
|
* flowConfig: {
|
|
447
|
-
* flowId: "image-optimization
|
|
283
|
+
* flowId: "image-optimization",
|
|
448
284
|
* storageId: "s3-images",
|
|
449
285
|
* },
|
|
450
286
|
* onSuccess: (outputs) => {
|
|
451
287
|
* console.log("Flow outputs:", outputs);
|
|
452
|
-
* // Access all outputs from the flow
|
|
453
|
-
* for (const output of outputs) {
|
|
454
|
-
* console.log(`${output.nodeId}:`, output.data);
|
|
455
|
-
* }
|
|
456
|
-
* },
|
|
457
|
-
* onFlowComplete: (outputs) => {
|
|
458
|
-
* console.log("All flow outputs:", outputs);
|
|
459
|
-
* },
|
|
460
|
-
* onError: (error) => {
|
|
461
|
-
* console.error("Upload or processing failed:", error);
|
|
462
288
|
* },
|
|
463
289
|
* });
|
|
464
290
|
*
|
|
@@ -466,58 +292,67 @@ interface UseFlowUploadReturn {
|
|
|
466
292
|
* <div>
|
|
467
293
|
* <input
|
|
468
294
|
* type="file"
|
|
469
|
-
* accept="image/*"
|
|
470
295
|
* onChange={(e) => {
|
|
471
296
|
* const file = e.target.files?.[0];
|
|
472
|
-
* if (file)
|
|
297
|
+
* if (file) flow.upload(file);
|
|
473
298
|
* }}
|
|
474
299
|
* />
|
|
300
|
+
* {flow.isUploading && <div>Progress: {flow.state.progress}%</div>}
|
|
301
|
+
* </div>
|
|
302
|
+
* );
|
|
303
|
+
* }
|
|
475
304
|
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
305
|
+
* // Multi-input with progressive provision
|
|
306
|
+
* function MultiInputFlow() {
|
|
307
|
+
* const flow = useFlow({
|
|
308
|
+
* flowConfig: {
|
|
309
|
+
* flowId: "multi-source-processing",
|
|
310
|
+
* storageId: "default",
|
|
311
|
+
* },
|
|
312
|
+
* });
|
|
479
313
|
*
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
314
|
+
* return (
|
|
315
|
+
* <div>
|
|
316
|
+
* {flow.inputMetadata?.map((input) => (
|
|
317
|
+
* <div key={input.nodeId}>
|
|
318
|
+
* <label>{input.nodeId}</label>
|
|
319
|
+
* {input.nodeType === "streaming-input-v1" ? (
|
|
320
|
+
* <input
|
|
321
|
+
* type="file"
|
|
322
|
+
* onChange={(e) => {
|
|
323
|
+
* const file = e.target.files?.[0];
|
|
324
|
+
* if (file) flow.setInput(input.nodeId, file);
|
|
325
|
+
* }}
|
|
326
|
+
* />
|
|
327
|
+
* ) : (
|
|
328
|
+
* <input
|
|
329
|
+
* type="url"
|
|
330
|
+
* onChange={(e) => flow.setInput(input.nodeId, e.target.value)}
|
|
331
|
+
* />
|
|
485
332
|
* )}
|
|
486
333
|
* </div>
|
|
487
|
-
* )}
|
|
334
|
+
* ))}
|
|
335
|
+
* <button onClick={flow.execute} disabled={flow.isUploading}>
|
|
336
|
+
* Execute Flow
|
|
337
|
+
* </button>
|
|
488
338
|
*
|
|
489
|
-
* {
|
|
339
|
+
* {flow.isUploading && (
|
|
490
340
|
* <div>
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
* {flowUpload.state.flowOutputs.map((output) => (
|
|
495
|
-
* <div key={output.nodeId}>{output.nodeId}: {JSON.stringify(output.data)}</div>
|
|
496
|
-
* ))}
|
|
341
|
+
* {Array.from(flow.inputStates.values()).map((inputState) => (
|
|
342
|
+
* <div key={inputState.nodeId}>
|
|
343
|
+
* {inputState.nodeId}: {inputState.status} ({inputState.progress}%)
|
|
497
344
|
* </div>
|
|
498
|
-
* )}
|
|
499
|
-
* </div>
|
|
500
|
-
* )}
|
|
501
|
-
*
|
|
502
|
-
* {flowUpload.state.status === "error" && (
|
|
503
|
-
* <div>
|
|
504
|
-
* <p>Error: {flowUpload.state.error?.message}</p>
|
|
505
|
-
* <button onClick={flowUpload.reset}>Try Again</button>
|
|
345
|
+
* ))}
|
|
506
346
|
* </div>
|
|
507
347
|
* )}
|
|
508
|
-
*
|
|
509
|
-
* {flowUpload.isUploading && (
|
|
510
|
-
* <button onClick={flowUpload.abort}>Cancel</button>
|
|
511
|
-
* )}
|
|
512
348
|
* </div>
|
|
513
349
|
* );
|
|
514
350
|
* }
|
|
515
351
|
* ```
|
|
516
352
|
*
|
|
517
|
-
* @see {@link
|
|
518
|
-
* @see {@link useUpload} for simple uploads without flow processing
|
|
353
|
+
* @see {@link useFlowUpload} for a simpler file-only upload hook
|
|
519
354
|
*/
|
|
520
|
-
declare function
|
|
355
|
+
declare function useFlow(options: FlowUploadOptions): UseFlowReturn;
|
|
521
356
|
//#endregion
|
|
522
357
|
//#region src/hooks/use-upload.d.ts
|
|
523
358
|
interface UseUploadOptions {
|
|
@@ -911,5 +746,5 @@ interface UseUploadistaClientReturn {
|
|
|
911
746
|
*/
|
|
912
747
|
declare function useUploadistaClient(options: UseUploadistaClientOptions): UseUploadistaClientReturn;
|
|
913
748
|
//#endregion
|
|
914
|
-
export {
|
|
915
|
-
//# sourceMappingURL=use-uploadista-client-
|
|
749
|
+
export { useDragDrop as C, UseDragDropReturn as S, InputExecutionState as _, MultiUploadState as a, DragDropOptions as b, useMultiUpload as c, UseUploadOptions as d, UseUploadReturn as f, FlowUploadStatus as g, FlowUploadState as h, MultiUploadOptions as i, UploadState as l, FlowInputMetadata as m, UseUploadistaClientReturn as n, UploadItem as o, useUpload as p, useUploadistaClient as r, UseMultiUploadReturn as s, UseUploadistaClientOptions as t, UploadStatus as u, UseFlowReturn as v, DragDropState as x, useFlow as y };
|
|
750
|
+
//# sourceMappingURL=use-uploadista-client-CkzVVmFT.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-uploadista-client-CkzVVmFT.d.mts","names":[],"sources":["../src/hooks/use-drag-drop.ts","../src/hooks/use-flow.ts","../src/hooks/use-upload.ts","../src/hooks/use-multi-upload.ts","../src/hooks/use-uploadista-client.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{o as e}from"./use-upload-BvvGROMR.mjs";import t,{useCallback as n,useEffect as r,useRef as i,useState as a}from"react";import{EventType as o}from"@uploadista/core/flow";import{UploadEventType as s}from"@uploadista/core/types";function c(e){if(!(`eventType`in e))return!1;let t=e;return t.eventType===o.JobStart||t.eventType===o.JobEnd||t.eventType===o.FlowStart||t.eventType===o.FlowEnd||t.eventType===o.FlowError||t.eventType===o.FlowPause||t.eventType===o.FlowCancel||t.eventType===o.NodeStart||t.eventType===o.NodeEnd||t.eventType===o.NodePause||t.eventType===o.NodeResume||t.eventType===o.NodeError||t.eventType===o.NodeStream||t.eventType===o.NodeResponse}function l(e){if(!(`type`in e))return!1;let t=e;return t.type===s.UPLOAD_STARTED||t.type===s.UPLOAD_PROGRESS||t.type===s.UPLOAD_COMPLETE||t.type===s.UPLOAD_FAILED||t.type===s.UPLOAD_VALIDATION_SUCCESS||t.type===s.UPLOAD_VALIDATION_FAILED||t.type===s.UPLOAD_VALIDATION_WARNING}function u(t){let{subscribeToEvents:n}=e();r(()=>n(e=>{if(c(e))switch(e.eventType){case o.JobStart:t.onJobStart?.(e);break;case o.JobEnd:t.onJobEnd?.(e);break;case o.FlowStart:t.onFlowStart?.(e);break;case o.FlowEnd:t.onFlowEnd?.(e);break;case o.FlowError:t.onFlowError?.(e);break;case o.FlowPause:t.onFlowPause?.(e);break;case o.FlowCancel:t.onFlowCancel?.(e);break;case o.NodeStart:t.onNodeStart?.(e);break;case o.NodeEnd:t.onNodeEnd?.(e);break;case o.NodePause:t.onNodePause?.(e);break;case o.NodeResume:t.onNodeResume?.(e);break;case o.NodeError:t.onNodeError?.(e);break}}),[n,t])}function d(t){let{subscribeToEvents:n}=e();r(()=>n(e=>{if(!l(e))return;let n=`flow`in e?e.flow:void 0;switch(e.type){case s.UPLOAD_STARTED:t.onUploadStarted?.({...e.data,flow:n});break;case s.UPLOAD_PROGRESS:t.onUploadProgress?.({...e.data,flow:n});break;case s.UPLOAD_COMPLETE:t.onUploadComplete?.({...e.data,flow:n});break;case s.UPLOAD_FAILED:t.onUploadFailed?.({...e.data,flow:n});break;case s.UPLOAD_VALIDATION_SUCCESS:t.onUploadValidationSuccess?.({...e.data,flow:n});break;case s.UPLOAD_VALIDATION_FAILED:t.onUploadValidationFailed?.({...e.data,flow:n});break;case s.UPLOAD_VALIDATION_WARNING:t.onUploadValidationWarning?.({...e.data,flow:n});break}}),[n,t])}const f={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 p(r={}){let{speedCalculationInterval:o=1e3,speedSampleSize:s=10,onMetricsUpdate:c,onFileStart:l,onFileProgress:u,onFileComplete:d}=r,p=e(),[m,h]=a(f),[g,_]=a([]),v=i([]),y=i(0),b=i(null),x=n((e,t)=>{let n={time:e,bytes:t};v.current.push(n),v.current.length>s&&(v.current=v.current.slice(-s));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}},[s]),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,l=null;a>0&&(l=(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:l,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),c?.(y)},[g,m.peakSpeed,x,c,p.client]),C=n(()=>(b.current&&clearInterval(b.current),b.current=setInterval(()=>{g.some(e=>!e.isComplete&&e.bytesUploaded>0)&&S()},o),()=>{b.current&&=(clearInterval(b.current),null)}),[o,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]),l?.(r),g.filter(e=>!e.isComplete).length===0&&C()},[g,l,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 u?.(s),s})),setTimeout(S,0)},[u,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 d?.(a),a})),setTimeout(S,0)},[d,S]),D=n(e=>{_(t=>t.filter(t=>t.id!==e)),setTimeout(S,0)},[S]),O=n(()=>{b.current&&=(clearInterval(b.current),null),h(f),_([]),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}}function m(t){let{subscribeToEvents:n}=e();r(()=>n(t),[n,t])}export{c as a,u as i,p as n,l as o,d as r,m as t};
|
|
2
|
+
//# sourceMappingURL=use-uploadista-events-BwUD-2Ck.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-uploadista-events-BwUD-2Ck.mjs","names":["initialMetrics: UploadMetrics","estimatedTimeRemaining: number | null","newMetrics: UploadMetrics","fileMetric: FileUploadMetrics"],"sources":["../src/hooks/event-utils.ts","../src/hooks/use-flow-events.ts","../src/hooks/use-upload-events.ts","../src/hooks/use-upload-metrics.ts","../src/hooks/use-uploadista-events.ts"],"sourcesContent":["import type { UploadistaEvent } from \"@uploadista/client-browser\";\nimport { EventType, type FlowEvent } from \"@uploadista/core/flow\";\nimport { UploadEventType, type UploadEvent } from \"@uploadista/core/types\";\n\n/**\n * Type guard to check if an event is a flow event\n */\nexport function isFlowEvent(event: UploadistaEvent): event is FlowEvent {\n if (!(\"eventType\" in event)) return false;\n const e = event as { eventType: unknown };\n return (\n e.eventType === EventType.JobStart ||\n e.eventType === EventType.JobEnd ||\n e.eventType === EventType.FlowStart ||\n e.eventType === EventType.FlowEnd ||\n e.eventType === EventType.FlowError ||\n e.eventType === EventType.FlowPause ||\n e.eventType === EventType.FlowCancel ||\n e.eventType === EventType.NodeStart ||\n e.eventType === EventType.NodeEnd ||\n e.eventType === EventType.NodePause ||\n e.eventType === EventType.NodeResume ||\n e.eventType === EventType.NodeError ||\n e.eventType === EventType.NodeStream ||\n e.eventType === EventType.NodeResponse\n );\n}\n\n/**\n * Type guard to check if an event is an upload event\n */\nexport function isUploadEvent(event: UploadistaEvent): event is UploadEvent {\n if (!(\"type\" in event)) return false;\n const e = event as { type: unknown };\n return (\n e.type === UploadEventType.UPLOAD_STARTED ||\n e.type === UploadEventType.UPLOAD_PROGRESS ||\n e.type === UploadEventType.UPLOAD_COMPLETE ||\n e.type === UploadEventType.UPLOAD_FAILED ||\n e.type === UploadEventType.UPLOAD_VALIDATION_SUCCESS ||\n e.type === UploadEventType.UPLOAD_VALIDATION_FAILED ||\n e.type === UploadEventType.UPLOAD_VALIDATION_WARNING\n );\n}\n","import type {\n FlowEventFlowCancel,\n FlowEventFlowEnd,\n FlowEventFlowError,\n FlowEventFlowPause,\n FlowEventFlowStart,\n FlowEventJobEnd,\n FlowEventJobStart,\n FlowEventNodeEnd,\n FlowEventNodeError,\n FlowEventNodePause,\n FlowEventNodeResume,\n FlowEventNodeStart,\n} from \"@uploadista/core/flow\";\nimport { EventType } from \"@uploadista/core/flow\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\nimport { isFlowEvent } from \"./event-utils\";\n\n/**\n * Options for handling flow execution events.\n *\n * All callbacks are optional - only provide handlers for events you care about.\n */\nexport interface UseFlowEventsOptions {\n /** Called when a job starts execution */\n onJobStart?: (event: FlowEventJobStart) => void;\n /** Called when a job completes (success or failure) */\n onJobEnd?: (event: FlowEventJobEnd) => void;\n /** Called when a flow begins execution */\n onFlowStart?: (event: FlowEventFlowStart) => void;\n /** Called when a flow completes successfully */\n onFlowEnd?: (event: FlowEventFlowEnd) => void;\n /** Called when a flow encounters an error */\n onFlowError?: (event: FlowEventFlowError) => void;\n /** Called when a flow is paused by user request */\n onFlowPause?: (event: FlowEventFlowPause) => void;\n /** Called when a flow is cancelled by user request */\n onFlowCancel?: (event: FlowEventFlowCancel) => void;\n /** Called when a node starts processing */\n onNodeStart?: (event: FlowEventNodeStart) => void;\n /** Called when a node completes successfully */\n onNodeEnd?: (event: FlowEventNodeEnd) => void;\n /** Called when a node pauses (waiting for additional data) */\n onNodePause?: (event: FlowEventNodePause) => void;\n /** Called when a paused node resumes execution */\n onNodeResume?: (event: FlowEventNodeResume) => void;\n /** Called when a node encounters an error */\n onNodeError?: (event: FlowEventNodeError) => void;\n}\n\n/**\n * Structured hook for handling flow execution events with type-safe callbacks.\n *\n * This hook provides a clean API for listening to specific flow events without\n * needing to manually filter events or use type guards.\n *\n * Must be used within UploadistaProvider.\n *\n * @param options - Object with optional callbacks for each flow event type\n *\n * @example\n * ```tsx\n * import { useFlowEvents } from '@uploadista/react';\n *\n * function FlowMonitor() {\n * useFlowEvents({\n * onFlowStart: (event) => {\n * console.log('Flow started:', event.flowId);\n * },\n * onNodeStart: (event) => {\n * console.log('Node started:', event.nodeName);\n * },\n * onNodeEnd: (event) => {\n * console.log('Node completed:', event.nodeName, event.result);\n * },\n * onFlowEnd: (event) => {\n * console.log('Flow completed with outputs:', event.outputs);\n * },\n * onFlowError: (event) => {\n * console.error('Flow failed:', event.error);\n * },\n * });\n *\n * return <div>Monitoring flow execution...</div>;\n * }\n * ```\n */\nexport function useFlowEvents(options: UseFlowEventsOptions): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents((event) => {\n // Only handle flow events\n if (!isFlowEvent(event)) return;\n\n // Route to appropriate callback based on event type\n switch (event.eventType) {\n case EventType.JobStart:\n options.onJobStart?.(event);\n break;\n case EventType.JobEnd:\n options.onJobEnd?.(event);\n break;\n case EventType.FlowStart:\n options.onFlowStart?.(event);\n break;\n case EventType.FlowEnd:\n options.onFlowEnd?.(event);\n break;\n case EventType.FlowError:\n options.onFlowError?.(event);\n break;\n case EventType.FlowPause:\n options.onFlowPause?.(event);\n break;\n case EventType.FlowCancel:\n options.onFlowCancel?.(event);\n break;\n case EventType.NodeStart:\n options.onNodeStart?.(event);\n break;\n case EventType.NodeEnd:\n options.onNodeEnd?.(event);\n break;\n case EventType.NodePause:\n options.onNodePause?.(event);\n break;\n case EventType.NodeResume:\n options.onNodeResume?.(event);\n break;\n case EventType.NodeError:\n options.onNodeError?.(event);\n break;\n }\n });\n\n return unsubscribe;\n }, [subscribeToEvents, options]);\n}\n","import { UploadEventType, type UploadEvent } from \"@uploadista/core/types\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\nimport { isUploadEvent } from \"./event-utils\";\n\n/**\n * Upload progress event data\n */\nexport interface UploadProgressEventData {\n id: string;\n progress: number;\n total: number;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload started/complete event data (contains full UploadFile)\n */\nexport interface UploadFileEventData {\n // This will contain the full UploadFile schema\n [key: string]: unknown;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload failed event data\n */\nexport interface UploadFailedEventData {\n id: string;\n error: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation success event data\n */\nexport interface UploadValidationSuccessEventData {\n id: string;\n validationType: \"checksum\" | \"mimetype\";\n algorithm?: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation failed event data\n */\nexport interface UploadValidationFailedEventData {\n id: string;\n reason: string;\n expected: string;\n actual: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation warning event data\n */\nexport interface UploadValidationWarningEventData {\n id: string;\n message: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Options for handling upload events.\n *\n * All callbacks are optional - only provide handlers for events you care about.\n */\nexport interface UseUploadEventsOptions {\n /** Called when an upload starts */\n onUploadStarted?: (data: UploadFileEventData) => void;\n /** Called with upload progress updates */\n onUploadProgress?: (data: UploadProgressEventData) => void;\n /** Called when an upload completes successfully */\n onUploadComplete?: (data: UploadFileEventData) => void;\n /** Called when an upload fails */\n onUploadFailed?: (data: UploadFailedEventData) => void;\n /** Called when upload validation succeeds */\n onUploadValidationSuccess?: (data: UploadValidationSuccessEventData) => void;\n /** Called when upload validation fails */\n onUploadValidationFailed?: (data: UploadValidationFailedEventData) => void;\n /** Called when upload validation produces a warning */\n onUploadValidationWarning?: (data: UploadValidationWarningEventData) => void;\n}\n\n/**\n * Structured hook for handling upload events with type-safe callbacks.\n *\n * This hook provides a clean API for listening to specific upload events without\n * needing to manually filter events or use type guards.\n *\n * Must be used within UploadistaProvider.\n *\n * @param options - Object with optional callbacks for each upload event type\n *\n * @example\n * ```tsx\n * import { useUploadEvents } from '@uploadista/react';\n *\n * function UploadMonitor() {\n * useUploadEvents({\n * onUploadStarted: (data) => {\n * console.log('Upload started:', data.id);\n * },\n * onUploadProgress: (data) => {\n * const percent = (data.progress / data.total) * 100;\n * console.log(`Upload progress: ${percent}%`);\n * },\n * onUploadComplete: (data) => {\n * console.log('Upload completed:', data);\n * },\n * onUploadFailed: (data) => {\n * console.error('Upload failed:', data.error);\n * },\n * });\n *\n * return <div>Monitoring uploads...</div>;\n * }\n * ```\n */\nexport function useUploadEvents(options: UseUploadEventsOptions): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents((event) => {\n // Only handle upload events\n if (!isUploadEvent(event)) return;\n\n // Route to appropriate callback based on event type\n // Note: flow context is at the top level of the event, not inside data\n const flowContext = \"flow\" in event ? event.flow : undefined;\n\n switch (event.type) {\n case UploadEventType.UPLOAD_STARTED:\n options.onUploadStarted?.({\n ...(event.data as unknown as Omit<UploadFileEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_PROGRESS:\n options.onUploadProgress?.({\n ...(event.data as unknown as Omit<\n UploadProgressEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_COMPLETE:\n options.onUploadComplete?.({\n ...(event.data as unknown as Omit<UploadFileEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_FAILED:\n options.onUploadFailed?.({\n ...(event.data as unknown as Omit<UploadFailedEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_SUCCESS:\n options.onUploadValidationSuccess?.({\n ...(event.data as unknown as Omit<\n UploadValidationSuccessEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_FAILED:\n options.onUploadValidationFailed?.({\n ...(event.data as unknown as Omit<\n UploadValidationFailedEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_WARNING:\n options.onUploadValidationWarning?.({\n ...(event.data as unknown as Omit<\n UploadValidationWarningEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n }\n });\n\n return unsubscribe;\n }, [subscribeToEvents, options]);\n}\n","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","import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport { useEffect } from \"react\";\nimport { useUploadistaContext } from \"../components/uploadista-provider\";\n\n/**\n * Simple hook that subscribes to all Uploadista events (both flow and upload events).\n *\n * This is a low-level hook that provides access to all events. For more structured\n * event handling, consider using `useFlowEvents` or `useUploadEvents` instead.\n *\n * Must be used within UploadistaProvider.\n *\n * @param callback - Function called for every event emitted by the Uploadista client\n *\n * @example\n * ```tsx\n * import { useUploadistaEvents, isFlowEvent, isUploadEvent } from '@uploadista/react';\n *\n * function MyComponent() {\n * useUploadistaEvents((event) => {\n * if (isFlowEvent(event)) {\n * console.log('Flow event:', event.eventType);\n * } else if (isUploadEvent(event)) {\n * console.log('Upload event:', event.type);\n * }\n * });\n *\n * return <div>Listening to all events...</div>;\n * }\n * ```\n */\nexport function useUploadistaEvents(\n callback: (event: UploadistaEvent) => void,\n): void {\n const { subscribeToEvents } = useUploadistaContext();\n\n useEffect(() => {\n const unsubscribe = subscribeToEvents(callback);\n return unsubscribe;\n }, [subscribeToEvents, callback]);\n}\n"],"mappings":"yOAOA,SAAgB,EAAY,EAA4C,CACtE,GAAI,EAAE,cAAe,GAAQ,MAAO,GACpC,IAAM,EAAI,EACV,OACE,EAAE,YAAc,EAAU,UAC1B,EAAE,YAAc,EAAU,QAC1B,EAAE,YAAc,EAAU,WAC1B,EAAE,YAAc,EAAU,SAC1B,EAAE,YAAc,EAAU,WAC1B,EAAE,YAAc,EAAU,WAC1B,EAAE,YAAc,EAAU,YAC1B,EAAE,YAAc,EAAU,WAC1B,EAAE,YAAc,EAAU,SAC1B,EAAE,YAAc,EAAU,WAC1B,EAAE,YAAc,EAAU,YAC1B,EAAE,YAAc,EAAU,WAC1B,EAAE,YAAc,EAAU,YAC1B,EAAE,YAAc,EAAU,aAO9B,SAAgB,EAAc,EAA8C,CAC1E,GAAI,EAAE,SAAU,GAAQ,MAAO,GAC/B,IAAM,EAAI,EACV,OACE,EAAE,OAAS,EAAgB,gBAC3B,EAAE,OAAS,EAAgB,iBAC3B,EAAE,OAAS,EAAgB,iBAC3B,EAAE,OAAS,EAAgB,eAC3B,EAAE,OAAS,EAAgB,2BAC3B,EAAE,OAAS,EAAgB,0BAC3B,EAAE,OAAS,EAAgB,0BC+C/B,SAAgB,EAAc,EAAqC,CACjE,GAAM,CAAE,qBAAsB,GAAsB,CAEpD,MACsB,EAAmB,GAAU,CAE1C,KAAY,EAAM,CAGvB,OAAQ,EAAM,UAAd,CACE,KAAK,EAAU,SACb,EAAQ,aAAa,EAAM,CAC3B,MACF,KAAK,EAAU,OACb,EAAQ,WAAW,EAAM,CACzB,MACF,KAAK,EAAU,UACb,EAAQ,cAAc,EAAM,CAC5B,MACF,KAAK,EAAU,QACb,EAAQ,YAAY,EAAM,CAC1B,MACF,KAAK,EAAU,UACb,EAAQ,cAAc,EAAM,CAC5B,MACF,KAAK,EAAU,UACb,EAAQ,cAAc,EAAM,CAC5B,MACF,KAAK,EAAU,WACb,EAAQ,eAAe,EAAM,CAC7B,MACF,KAAK,EAAU,UACb,EAAQ,cAAc,EAAM,CAC5B,MACF,KAAK,EAAU,QACb,EAAQ,YAAY,EAAM,CAC1B,MACF,KAAK,EAAU,UACb,EAAQ,cAAc,EAAM,CAC5B,MACF,KAAK,EAAU,WACb,EAAQ,eAAe,EAAM,CAC7B,MACF,KAAK,EAAU,UACb,EAAQ,cAAc,EAAM,CAC5B,QAEJ,CAGD,CAAC,EAAmB,EAAQ,CAAC,CCMlC,SAAgB,EAAgB,EAAuC,CACrE,GAAM,CAAE,qBAAsB,GAAsB,CAEpD,MACsB,EAAmB,GAAU,CAE/C,GAAI,CAAC,EAAc,EAAM,CAAE,OAI3B,IAAM,EAAc,SAAU,EAAQ,EAAM,KAAO,IAAA,GAEnD,OAAQ,EAAM,KAAd,CACE,KAAK,EAAgB,eACnB,EAAQ,kBAAkB,CACxB,GAAI,EAAM,KACV,KAAM,EACP,CAAC,CACF,MACF,KAAK,EAAgB,gBACnB,EAAQ,mBAAmB,CACzB,GAAI,EAAM,KAIV,KAAM,EACP,CAAC,CACF,MACF,KAAK,EAAgB,gBACnB,EAAQ,mBAAmB,CACzB,GAAI,EAAM,KACV,KAAM,EACP,CAAC,CACF,MACF,KAAK,EAAgB,cACnB,EAAQ,iBAAiB,CACvB,GAAI,EAAM,KACV,KAAM,EACP,CAAC,CACF,MACF,KAAK,EAAgB,0BACnB,EAAQ,4BAA4B,CAClC,GAAI,EAAM,KAIV,KAAM,EACP,CAAC,CACF,MACF,KAAK,EAAgB,yBACnB,EAAQ,2BAA2B,CACjC,GAAI,EAAM,KAIV,KAAM,EACP,CAAC,CACF,MACF,KAAK,EAAgB,0BACnB,EAAQ,4BAA4B,CAClC,GAAI,EAAM,KAIV,KAAM,EACP,CAAC,CACF,QAEJ,CAGD,CAAC,EAAmB,EAAQ,CAAC,CC3BlC,MAAMA,EAAgC,CACpC,mBAAoB,EACpB,WAAY,EACZ,aAAc,EACd,aAAc,EACd,uBAAwB,KACxB,WAAY,EACZ,eAAgB,EAChB,cAAe,EACf,SAAU,EACV,UAAW,EACX,UAAW,KACX,QAAS,KACT,cAAe,KACf,SAAU,CACR,kBAAmB,EACnB,sBAAuB,EACvB,iBAAkB,EAClB,gBAAiB,EAAE,CACnB,sBAAuB,CAAE,IAAK,IAAM,KAAM,IAAK,EAAI,KAAO,KAAM,CACjE,CACD,eAAgB,EAAE,CAClB,aAAc,EAAE,CACjB,CAsDD,SAAgB,EACd,EAAmC,EAAE,CACb,CACxB,GAAM,CACJ,2BAA2B,IAC3B,kBAAkB,GAClB,kBACA,cACA,iBACA,kBACE,EAEE,EAAe,GAAsB,CAErC,CAAC,EAAS,GAAc,EAAwB,EAAe,CAC/D,CAAC,EAAa,GAAkB,EAA8B,EAAE,CAAC,CAEjE,EAAkB,EAA+C,EAAE,CAAC,CACpE,EAAgB,EAAe,EAAE,CACjC,EAAc,EAAuB,KAAK,CAE1C,EAAiB,GACpB,EAAqB,IAA+B,CACnD,IAAM,EAAS,CAAE,KAAM,EAAa,MAAO,EAAoB,CAC/D,EAAgB,QAAQ,KAAK,EAAO,CAGhC,EAAgB,QAAQ,OAAS,IACnC,EAAgB,QAAU,EAAgB,QAAQ,MAChD,CAAC,EACF,EAIH,IAAI,EAAe,EACnB,GAAI,EAAgB,QAAQ,QAAU,EAAG,CACvC,IAAM,EACJ,EAAgB,QAAQ,EAAgB,QAAQ,OAAS,GACrD,EACJ,EAAgB,QAAQ,EAAgB,QAAQ,OAAS,GAC3D,GAAI,GAAU,EAAU,CACtB,IAAM,GAAY,EAAO,KAAO,EAAS,MAAQ,IAC3C,EAAY,EAAO,MAAQ,EAAS,MAC1C,EAAe,EAAW,EAAI,EAAY,EAAW,GAKzD,IAAI,EAAe,EACnB,GAAI,EAAgB,QAAQ,QAAU,EAAG,CACvC,IAAM,EAAQ,EAAgB,QAAQ,GAChC,EACJ,EAAgB,QAAQ,EAAgB,QAAQ,OAAS,GAC3D,GAAI,GAAS,EAAM,CACjB,IAAM,GAAa,EAAK,KAAO,EAAM,MAAQ,IACvC,EAAa,EAAK,MAAQ,EAAM,MACtC,EAAe,EAAY,EAAI,EAAa,EAAY,GAI5D,MAAO,CAAE,eAAc,eAAc,EAEvC,CAAC,EAAgB,CAClB,CAEK,EAAgB,MAAkB,CACtC,IAAM,EAAM,KAAK,KAAK,CAGhB,EAAa,EAAY,QAAQ,EAAK,IAAS,EAAM,EAAK,KAAM,EAAE,CAClE,EAAqB,EAAY,QACpC,EAAK,IAAS,EAAM,EAAK,cAC1B,EACD,CACK,EAAiB,EAAY,OAAQ,GAAS,EAAK,WAAW,CAAC,OAC/D,EAAgB,EAAY,OAC/B,GAAS,CAAC,EAAK,YAAc,EAAK,cAAgB,EACpD,CAAC,OAGI,CAAE,eAAc,gBAAiB,EACrC,EACA,EACD,CAGK,EACJ,EAAa,EAAI,KAAK,MAAO,EAAqB,EAAc,IAAI,CAAG,EAGrEC,EAAwC,KACxC,EAAe,IAEjB,GADuB,EAAa,GACO,EAAgB,KAI7D,IAAM,EAAc,EAAY,OAAQ,GAAS,EAAK,UAAY,EAAE,CAC9D,EACJ,EAAY,OAAS,EACjB,KAAK,IAAI,GAAG,EAAY,IAAK,GAAS,EAAK,UAAU,CAAC,CACtD,KAEA,EAAiB,EAAY,OAAQ,GAAS,EAAK,UAAY,KAAK,CACpE,EACJ,EAAe,OAAS,GAAK,IAAmB,EAAY,OACxD,KAAK,IACH,GAAG,EACA,IAAK,GAAS,EAAK,QAAQ,CAC3B,OAAQ,GAAS,IAAS,KAAK,CACnC,CACD,KAEA,EAAgB,GAAa,EAAU,EAAU,EAAY,KAE7DC,EAA4B,CAChC,qBACA,aACA,eACA,eACA,yBACA,WAAY,EAAY,OACxB,iBACA,gBACA,WACA,UAAW,KAAK,IAAI,EAAQ,UAAW,EAAa,CACpD,YACA,UACA,gBACA,SAAU,EAAa,OAAO,qBAAqB,CACnD,eAAgB,CAAC,EAAa,OAAO,eAAe,CAAC,QAAQ,CAC7D,aAAc,EAAa,OAAO,eAAe,CAAC,OACnD,CAED,EAAW,EAAW,CACtB,IAAkB,EAAW,EAC5B,CACD,EACA,EAAQ,UACR,EACA,EACA,EAAa,OACd,CAAC,CAGI,EAAwB,OACxB,EAAY,SACd,cAAc,EAAY,QAAQ,CAGpC,EAAY,QAAU,gBAAkB,CAEpC,EAAY,KAAM,GAAS,CAAC,EAAK,YAAc,EAAK,cAAgB,EAAE,EAEtE,GAAe,EAEhB,EAAyB,KAEf,CACX,AAEE,EAAY,WADZ,cAAc,EAAY,QAAQ,CACZ,QAGzB,CAAC,EAA0B,EAAe,EAAY,CAAC,CAEpD,EAAkB,GACrB,EAAY,EAAkB,IAAiB,CAG9C,IAAMC,EAAgC,CACpC,KACA,WACA,OACA,cAAe,EACf,SAAU,EACV,MAAO,EACP,UATU,KAAK,KAAK,CAUpB,QAAS,KACT,SAAU,KACV,WAAY,GACb,CAED,EAAgB,GACG,EAAK,KAAM,GAAS,EAAK,KAAO,EAAG,CAE3C,EAAK,IAAK,GAAU,EAAK,KAAO,EAAK,EAAa,EAAM,CAE1D,CAAC,GAAG,EAAM,EAAW,CAC5B,CAEF,IAAc,EAAW,CAGrB,EAAY,OAAQ,GAAS,CAAC,EAAK,WAAW,CAAC,SAAW,GAC5D,GAAuB,EAG3B,CAAC,EAAa,EAAa,EAAsB,CAClD,CAEK,EAAqB,GACxB,EAAY,IAA0B,CACrC,IAAM,EAAM,KAAK,KAAK,CAEtB,EAAgB,GACd,EAAK,IAAK,GAAS,CACjB,GAAI,EAAK,KAAO,EAAI,OAAO,EAE3B,IAAM,GAAY,EAAM,EAAK,WAAa,IACpC,EAAQ,EAAW,EAAI,EAAgB,EAAW,EAClD,EACJ,EAAK,KAAO,EAAI,KAAK,MAAO,EAAgB,EAAK,KAAQ,IAAI,CAAG,EAE5D,EAAc,CAClB,GAAG,EACH,gBACA,WACA,QACD,CAGD,OADA,IAAiB,EAAY,CACtB,GACP,CACH,CAGD,WAAW,EAAe,EAAE,EAE9B,CAAC,EAAgB,EAAc,CAChC,CAEK,EAAqB,EACxB,GAAe,CACd,IAAM,EAAM,KAAK,KAAK,CAEtB,EAAgB,GACd,EAAK,IAAK,GAAS,CACjB,GAAI,EAAK,KAAO,EAAI,OAAO,EAE3B,IAAM,EAAW,EAAM,EAAK,UACtB,EAAQ,EAAW,EAAK,EAAK,KAAO,EAAY,IAAO,EAEvD,EAAgB,CACpB,GAAG,EACH,cAAe,EAAK,KACpB,SAAU,IACV,QACA,QAAS,EACT,WACA,WAAY,GACb,CAGD,OADA,IAAiB,EAAc,CACxB,GACP,CACH,CAGD,WAAW,EAAe,EAAE,EAE9B,CAAC,EAAgB,EAAc,CAChC,CAEK,EAAa,EAChB,GAAe,CACd,EAAgB,GAAS,EAAK,OAAQ,GAAS,EAAK,KAAO,EAAG,CAAC,CAC/D,WAAW,EAAe,EAAE,EAE9B,CAAC,EAAc,CAChB,CAEK,EAAQ,MAAkB,CAC9B,AAEE,EAAY,WADZ,cAAc,EAAY,QAAQ,CACZ,MAGxB,EAAW,EAAe,CAC1B,EAAe,EAAE,CAAC,CAClB,EAAgB,QAAU,EAAE,CAC5B,EAAc,QAAU,GACvB,EAAE,CAAC,CAEA,EAAiB,EACpB,GACQ,EAAY,KAAM,GAAS,EAAK,KAAO,EAAG,CAEnD,CAAC,EAAY,CACd,CAEK,EAAgB,OACb,CACL,QAAS,EACT,MAAO,EACP,WAAY,KAAK,KAAK,CACvB,EACA,CAAC,EAAS,EAAY,CAAC,CAW1B,OARA,EAAM,kBACS,CACP,EAAY,SACd,cAAc,EAAY,QAAQ,EAGrC,EAAE,CAAC,CAEC,CACL,UACA,cACA,kBACA,qBACA,qBACA,aACA,QACA,iBACA,gBACD,CCxiBH,SAAgB,EACd,EACM,CACN,GAAM,CAAE,qBAAsB,GAAsB,CAEpD,MACsB,EAAkB,EAAS,CAE9C,CAAC,EAAmB,EAAS,CAAC"}
|