@uploadista/react 0.0.20-beta.2 → 0.0.20-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/components/index.d.mts +3 -3
  2. package/dist/components/index.mjs +1 -1
  3. package/dist/flow-upload-list-SbpCaxRq.mjs +2 -0
  4. package/dist/flow-upload-list-SbpCaxRq.mjs.map +1 -0
  5. package/dist/hooks/index.d.mts +3 -3
  6. package/dist/hooks/index.mjs +1 -1
  7. package/dist/index.d.mts +6 -6
  8. package/dist/index.mjs +1 -1
  9. package/dist/{uploadista-provider-D-N-eL2l.d.mts → uploadista-provider-C1l0iBc9.d.mts} +512 -318
  10. package/dist/uploadista-provider-C1l0iBc9.d.mts.map +1 -0
  11. package/dist/use-upload-BvvGROMR.mjs +2 -0
  12. package/dist/use-upload-BvvGROMR.mjs.map +1 -0
  13. package/dist/{use-uploadista-client-m9nF-irM.d.mts → use-uploadista-client-DHbLSpIb.d.mts} +120 -286
  14. package/dist/use-uploadista-client-DHbLSpIb.d.mts.map +1 -0
  15. package/dist/use-uploadista-events-BwUD-2Ck.mjs +2 -0
  16. package/dist/use-uploadista-events-BwUD-2Ck.mjs.map +1 -0
  17. package/dist/{use-upload-metrics-DhzS4lhG.d.mts → use-uploadista-events-CtDXJYrR.d.mts} +169 -371
  18. package/dist/use-uploadista-events-CtDXJYrR.d.mts.map +1 -0
  19. package/package.json +6 -6
  20. package/src/components/flow-primitives.tsx +839 -0
  21. package/src/components/index.tsx +31 -13
  22. package/src/hooks/index.ts +25 -37
  23. package/src/index.ts +90 -81
  24. package/dist/upload-zone-BjWHuP7p.mjs +0 -6
  25. package/dist/upload-zone-BjWHuP7p.mjs.map +0 -1
  26. package/dist/uploadista-provider-D-N-eL2l.d.mts.map +0 -1
  27. package/dist/use-upload-BDHVhQsI.mjs +0 -2
  28. package/dist/use-upload-BDHVhQsI.mjs.map +0 -1
  29. package/dist/use-upload-metrics-Df90wIos.mjs +0 -2
  30. package/dist/use-upload-metrics-Df90wIos.mjs.map +0 -1
  31. package/dist/use-upload-metrics-DhzS4lhG.d.mts.map +0 -1
  32. package/dist/use-uploadista-client-m9nF-irM.d.mts.map +0 -1
  33. package/src/components/flow-input.tsx +0 -299
  34. package/src/components/flow-upload-zone.tsx +0 -441
  35. package/src/hooks/use-flow-execution.ts +0 -502
  36. package/src/hooks/use-flow-upload.ts +0 -299
@@ -1,502 +0,0 @@
1
- /**
2
- * Generic React hook for flexible flow execution with arbitrary input types.
3
- *
4
- * This hook provides a flexible interface for executing flows with different input types:
5
- * - File/Blob: Traditional chunked file upload
6
- * - string (URL): Direct file fetch from external URL
7
- * - object: Structured data for custom input nodes
8
- *
9
- * The hook uses an inputBuilder pattern to transform trigger data into flow inputs,
10
- * enabling dynamic input preparation and validation before flow execution.
11
- *
12
- * @module hooks/use-flow-execution
13
- *
14
- * @example
15
- * ```tsx
16
- * // URL-based flow execution
17
- * function UrlImageProcessor() {
18
- * const execution = useFlowExecution<string>({
19
- * flowConfig: {
20
- * flowId: "image-optimize",
21
- * storageId: "s3"
22
- * },
23
- * inputBuilder: async (url) => {
24
- * // Find the input node
25
- * const { inputNodes, single } = await client.findInputNode("image-optimize");
26
- * if (!single) throw new Error("Expected single input node");
27
- *
28
- * return {
29
- * [inputNodes[0].id]: {
30
- * operation: "url",
31
- * url,
32
- * metadata: { source: "external" }
33
- * }
34
- * };
35
- * },
36
- * onSuccess: (outputs) => console.log("Done:", outputs)
37
- * });
38
- *
39
- * return (
40
- * <button onClick={() => execution.execute("https://example.com/image.jpg")}>
41
- * Process URL
42
- * </button>
43
- * );
44
- * }
45
- * ```
46
- */
47
-
48
- import type { FlowUploadOptions } from "@uploadista/client-browser";
49
- import type {
50
- FlowInputs,
51
- FlowManager,
52
- FlowUploadState,
53
- FlowUploadStatus,
54
- } from "@uploadista/client-core";
55
- import type { TypedOutput } from "@uploadista/core/flow";
56
- import { useCallback, useEffect, useRef, useState } from "react";
57
- import { useUploadistaContext } from "../components/uploadista-provider";
58
- import { useFlowManagerContext } from "../contexts/flow-manager-context";
59
-
60
- // Re-export types for convenience
61
- export type { FlowUploadState, FlowUploadStatus };
62
-
63
- /**
64
- * Input builder function that transforms trigger data into flow inputs.
65
- *
66
- * The builder receives the trigger data passed to execute() and returns
67
- * a FlowInputs object mapping node IDs to their input data.
68
- *
69
- * @template TTrigger - The type of data passed to execute()
70
- * @param trigger - The trigger data (e.g., File, URL string, structured data)
71
- * @returns Promise resolving to FlowInputs mapping or the mapping directly
72
- *
73
- * @example
74
- * ```typescript
75
- * // File upload builder
76
- * const fileBuilder: InputBuilder<File> = async (file) => ({
77
- * "input-node": {
78
- * operation: "init",
79
- * storageId: "s3",
80
- * metadata: { originalName: file.name, size: file.size }
81
- * }
82
- * });
83
- *
84
- * // URL fetch builder
85
- * const urlBuilder: InputBuilder<string> = (url) => ({
86
- * "input-node": {
87
- * operation: "url",
88
- * url,
89
- * metadata: { source: "external" }
90
- * }
91
- * });
92
- * ```
93
- */
94
- export type InputBuilder<TTrigger = unknown> = (
95
- trigger: TTrigger,
96
- ) => Promise<FlowInputs> | FlowInputs;
97
-
98
- /**
99
- * Options for the useFlowExecution hook.
100
- *
101
- * @template TTrigger - The type of trigger data passed to execute()
102
- * @template TOutput - The expected output type from the flow
103
- *
104
- * @property flowConfig - Flow configuration (flowId, storageId, etc.)
105
- * @property inputBuilder - Function to build flow inputs from trigger data
106
- * @property onJobStart - Called when flow job is created
107
- * @property onProgress - Called during upload progress (if applicable)
108
- * @property onChunkComplete - Called when upload chunk completes (if applicable)
109
- * @property onSuccess - Called with typed outputs when flow succeeds
110
- * @property onFlowComplete - Called with all outputs when flow completes
111
- * @property onError - Called when execution fails
112
- * @property onAbort - Called when execution is aborted
113
- * @property onShouldRetry - Custom retry logic (if applicable)
114
- */
115
- export interface UseFlowExecutionOptions<
116
- TTrigger = unknown,
117
- TOutput = TypedOutput[],
118
- > {
119
- /**
120
- * Flow configuration
121
- */
122
- flowConfig: FlowUploadOptions["flowConfig"];
123
-
124
- /**
125
- * Function to build flow inputs from trigger data.
126
- * Can be async to perform validation, API calls, etc.
127
- */
128
- inputBuilder: InputBuilder<TTrigger>;
129
-
130
- /**
131
- * Called when the flow job starts
132
- */
133
- onJobStart?: (jobId: string) => void;
134
-
135
- /**
136
- * Called during upload progress (for file uploads)
137
- */
138
- onProgress?: (
139
- uploadId: string,
140
- bytesUploaded: number,
141
- totalBytes: number | null,
142
- ) => void;
143
-
144
- /**
145
- * Called when an upload chunk completes (for file uploads)
146
- */
147
- onChunkComplete?: (
148
- chunkSize: number,
149
- bytesAccepted: number,
150
- bytesTotal: number | null,
151
- ) => void;
152
-
153
- /**
154
- * Called when flow execution succeeds with final outputs
155
- */
156
- onSuccess?: (outputs: TOutput) => void;
157
-
158
- /**
159
- * Called when flow completes (alternative to onSuccess)
160
- */
161
- onFlowComplete?: (outputs: TypedOutput[]) => void;
162
-
163
- /**
164
- * Called when execution fails
165
- */
166
- onError?: (error: Error) => void;
167
-
168
- /**
169
- * Called when execution is aborted
170
- */
171
- onAbort?: () => void;
172
-
173
- /**
174
- * Custom retry logic (for file uploads)
175
- */
176
- onShouldRetry?: (error: Error, retryAttempt: number) => boolean;
177
- }
178
-
179
- /**
180
- * Return value from useFlowExecution hook.
181
- *
182
- * @template TTrigger - The type of trigger data passed to execute()
183
- *
184
- * @property state - Current execution state with progress and outputs
185
- * @property execute - Function to trigger flow execution
186
- * @property abort - Cancel the current execution
187
- * @property pause - Pause the current execution (for file uploads)
188
- * @property reset - Reset state to idle
189
- * @property isExecuting - True when execution is active
190
- * @property isUploadingFile - True during file upload phase
191
- * @property isProcessing - True during flow processing phase
192
- */
193
- export interface UseFlowExecutionReturn<TTrigger = unknown> {
194
- /**
195
- * Current execution state
196
- */
197
- state: FlowUploadState;
198
-
199
- /**
200
- * Execute the flow with trigger data
201
- */
202
- execute: (trigger: TTrigger) => Promise<void>;
203
-
204
- /**
205
- * Abort the current execution
206
- */
207
- abort: () => void;
208
-
209
- /**
210
- * Pause the current execution (if supported by input type)
211
- */
212
- pause: () => void;
213
-
214
- /**
215
- * Reset the execution state
216
- */
217
- reset: () => void;
218
-
219
- /**
220
- * Whether execution is active (uploading OR processing)
221
- */
222
- isExecuting: boolean;
223
-
224
- /**
225
- * Whether file upload is in progress
226
- */
227
- isUploadingFile: boolean;
228
-
229
- /**
230
- * Whether flow processing is in progress
231
- */
232
- isProcessing: boolean;
233
- }
234
-
235
- const initialState: FlowUploadState = {
236
- status: "idle",
237
- progress: 0,
238
- bytesUploaded: 0,
239
- totalBytes: null,
240
- error: null,
241
- jobId: null,
242
- flowStarted: false,
243
- currentNodeName: null,
244
- currentNodeType: null,
245
- flowOutputs: null,
246
- };
247
-
248
- /**
249
- * Generic React hook for flexible flow execution.
250
- *
251
- * Provides a flexible interface for executing flows with arbitrary input types
252
- * through an inputBuilder pattern. The builder transforms trigger data into
253
- * flow inputs, enabling support for files, URLs, structured data, and more.
254
- *
255
- * Must be used within FlowManagerProvider (which must be within UploadistaProvider).
256
- *
257
- * @template TTrigger - The type of trigger data passed to execute()
258
- * @template TOutput - The expected output type from the flow
259
- *
260
- * @param options - Flow execution configuration with inputBuilder
261
- * @returns Execution state and control methods
262
- *
263
- * @example
264
- * ```tsx
265
- * // URL-based image processing
266
- * const urlExecution = useFlowExecution<string>({
267
- * flowConfig: { flowId: "optimize", storageId: "s3" },
268
- * inputBuilder: async (url) => {
269
- * const { inputNodes } = await client.findInputNode("optimize");
270
- * return {
271
- * [inputNodes[0].id]: {
272
- * operation: "url",
273
- * url,
274
- * metadata: { source: "external" }
275
- * }
276
- * };
277
- * },
278
- * onSuccess: (outputs) => console.log("Processed:", outputs)
279
- * });
280
- *
281
- * // Execute with URL
282
- * await urlExecution.execute("https://example.com/image.jpg");
283
- *
284
- * // File upload (traditional pattern)
285
- * const fileExecution = useFlowExecution<File>({
286
- * flowConfig: { flowId: "optimize", storageId: "s3" },
287
- * inputBuilder: async (file) => {
288
- * const { inputNodes } = await client.findInputNode("optimize");
289
- * return {
290
- * [inputNodes[0].id]: {
291
- * operation: "init",
292
- * storageId: "s3",
293
- * metadata: {
294
- * originalName: file.name,
295
- * mimeType: file.type,
296
- * size: file.size
297
- * }
298
- * }
299
- * };
300
- * }
301
- * });
302
- *
303
- * // Execute with file
304
- * await fileExecution.execute(myFile);
305
- * ```
306
- */
307
- export function useFlowExecution<TTrigger = unknown, TOutput = TypedOutput[]>(
308
- options: UseFlowExecutionOptions<TTrigger, TOutput>,
309
- ): UseFlowExecutionReturn<TTrigger> {
310
- const { client } = useUploadistaContext();
311
- const { getManager, releaseManager } = useFlowManagerContext();
312
- const [state, setState] = useState<FlowUploadState>(initialState);
313
- const managerRef = useRef<FlowManager<unknown> | null>(null);
314
-
315
- // Store callbacks and inputBuilder in refs for stable access
316
- const callbacksRef = useRef(options);
317
- const inputBuilderRef = useRef(options.inputBuilder);
318
-
319
- // Update refs when options change
320
- useEffect(() => {
321
- callbacksRef.current = options;
322
- inputBuilderRef.current = options.inputBuilder;
323
- });
324
-
325
- // Get or create manager from context
326
- useEffect(() => {
327
- const flowId = options.flowConfig.flowId;
328
-
329
- // Create stable callback wrappers
330
- const stableCallbacks = {
331
- onStateChange: setState,
332
- onProgress: (
333
- uploadId: string,
334
- bytesUploaded: number,
335
- totalBytes: number | null,
336
- ) => {
337
- callbacksRef.current.onProgress?.(uploadId, bytesUploaded, totalBytes);
338
- },
339
- onChunkComplete: (
340
- chunkSize: number,
341
- bytesAccepted: number,
342
- bytesTotal: number | null,
343
- ) => {
344
- callbacksRef.current.onChunkComplete?.(
345
- chunkSize,
346
- bytesAccepted,
347
- bytesTotal,
348
- );
349
- },
350
- onFlowComplete: (outputs: TypedOutput[]) => {
351
- callbacksRef.current.onFlowComplete?.(outputs);
352
- },
353
- onSuccess: (outputs: TypedOutput[]) => {
354
- callbacksRef.current.onSuccess?.(outputs as TOutput);
355
- },
356
- onError: (error: Error) => {
357
- callbacksRef.current.onError?.(error);
358
- },
359
- onAbort: () => {
360
- callbacksRef.current.onAbort?.();
361
- },
362
- };
363
-
364
- // Get or create manager for this flow
365
- const manager = getManager(flowId, stableCallbacks, {
366
- flowConfig: options.flowConfig,
367
- });
368
- managerRef.current = manager;
369
-
370
- return () => {
371
- if (managerRef.current) {
372
- releaseManager(flowId);
373
- managerRef.current = null;
374
- }
375
- };
376
- }, [options.flowConfig.flowId, getManager, releaseManager, options]);
377
-
378
- /**
379
- * Execute the flow with trigger data.
380
- * Calls inputBuilder to transform trigger into flow inputs.
381
- */
382
- const execute = useCallback(async (trigger: TTrigger) => {
383
- try {
384
- // Build flow inputs from trigger data
385
- const flowInputs = await inputBuilderRef.current(trigger);
386
-
387
- // For now, we need to determine if this is a file upload or URL operation
388
- // by inspecting the flowInputs structure
389
- const firstInputNodeId = Object.keys(flowInputs)[0];
390
- if (!firstInputNodeId) {
391
- throw new Error("flowInputs must contain at least one input node");
392
- }
393
- const firstInput = flowInputs[firstInputNodeId];
394
-
395
- // Type guard: check if this is an init operation (file upload)
396
- const isFileUpload =
397
- typeof firstInput === "object" &&
398
- firstInput !== null &&
399
- "operation" in firstInput &&
400
- firstInput.operation === "init";
401
-
402
- if (isFileUpload) {
403
- // File upload path: use the standard upload mechanism
404
- // This requires the trigger to be a File/Blob
405
- if (managerRef.current) {
406
- await managerRef.current.upload(trigger as unknown);
407
- }
408
- } else {
409
- // Non-file path (URL, structured data, etc.)
410
- // Skip chunked upload and execute flow directly with provided inputs
411
- setState((prev) => ({
412
- ...prev,
413
- status: "processing",
414
- flowStarted: true,
415
- }));
416
-
417
- // Execute flow with the built inputs
418
- const result = await client.executeFlowWithInputs(
419
- options.flowConfig.flowId,
420
- flowInputs,
421
- {
422
- storageId: options.flowConfig.storageId,
423
- onJobStart: (jobId: string) => {
424
- setState((prev) => ({
425
- ...prev,
426
- jobId,
427
- }));
428
- callbacksRef.current.onJobStart?.(jobId);
429
- },
430
- },
431
- );
432
-
433
- // If job was created successfully, the FlowManager will handle
434
- // flow events via WebSocket and update state accordingly
435
- if (result.job?.id) {
436
- // State updates will come from flow events
437
- // Manager will receive FlowEnd event and update state to "success"
438
- } else {
439
- // No job created - treat as immediate completion
440
- setState((prev) => ({
441
- ...prev,
442
- status: "success",
443
- progress: 100,
444
- flowStarted: true,
445
- }));
446
- }
447
- }
448
- } catch (error) {
449
- // Handle inputBuilder errors
450
- const err = error instanceof Error ? error : new Error(String(error));
451
- setState((prev) => ({
452
- ...prev,
453
- status: "error",
454
- error: err,
455
- }));
456
- callbacksRef.current.onError?.(err);
457
- }
458
- }, []);
459
-
460
- /**
461
- * Abort the current execution
462
- */
463
- const abort = useCallback(() => {
464
- if (managerRef.current) {
465
- managerRef.current.abort();
466
- }
467
- }, []);
468
-
469
- /**
470
- * Pause the current execution
471
- */
472
- const pause = useCallback(() => {
473
- if (managerRef.current) {
474
- managerRef.current.pause();
475
- }
476
- }, []);
477
-
478
- /**
479
- * Reset the execution state
480
- */
481
- const reset = useCallback(() => {
482
- if (managerRef.current) {
483
- const flowId = options.flowConfig.flowId;
484
- managerRef.current.reset();
485
- managerRef.current.cleanup();
486
- releaseManager(flowId);
487
- managerRef.current = null;
488
- }
489
- setState(initialState);
490
- }, [options.flowConfig.flowId, releaseManager]);
491
-
492
- return {
493
- state,
494
- execute,
495
- abort,
496
- pause,
497
- reset,
498
- isExecuting: state.status === "uploading" || state.status === "processing",
499
- isUploadingFile: state.status === "uploading",
500
- isProcessing: state.status === "processing",
501
- };
502
- }