@uploadista/react 0.0.15-beta.2 → 0.0.15-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.
Files changed (37) hide show
  1. package/dist/components/index.d.mts +2 -2
  2. package/dist/components/index.mjs +1 -1
  3. package/dist/hooks/index.d.mts +3 -3
  4. package/dist/hooks/index.mjs +1 -1
  5. package/dist/index.d.mts +76 -4
  6. package/dist/index.d.mts.map +1 -0
  7. package/dist/index.mjs +1 -1
  8. package/dist/{upload-zone-DFStubbe.mjs → upload-zone-pXt4LdcC.mjs} +2 -2
  9. package/dist/{upload-zone-DFStubbe.mjs.map → upload-zone-pXt4LdcC.mjs.map} +1 -1
  10. package/dist/{uploadista-provider-CrS6TmpJ.d.mts → uploadista-provider-k2P1_Ms-.d.mts} +4 -8
  11. package/dist/uploadista-provider-k2P1_Ms-.d.mts.map +1 -0
  12. package/dist/use-upload-DSYzF2Et.mjs +2 -0
  13. package/dist/use-upload-DSYzF2Et.mjs.map +1 -0
  14. package/dist/use-upload-metrics-DFVcWvCk.mjs +2 -0
  15. package/dist/use-upload-metrics-DFVcWvCk.mjs.map +1 -0
  16. package/dist/{use-upload-metrics-IXxUORce.d.mts → use-upload-metrics-Vho0Lzmq.d.mts} +250 -4
  17. package/dist/use-upload-metrics-Vho0Lzmq.d.mts.map +1 -0
  18. package/dist/{use-uploadista-client--ivZPO88.d.mts → use-uploadista-client-CRUhyWoy.d.mts} +5 -5
  19. package/dist/use-uploadista-client-CRUhyWoy.d.mts.map +1 -0
  20. package/package.json +5 -5
  21. package/src/components/uploadista-provider.tsx +16 -31
  22. package/src/contexts/flow-manager-context.tsx +230 -0
  23. package/src/hooks/event-utils.ts +44 -0
  24. package/src/hooks/index.ts +16 -0
  25. package/src/hooks/use-flow-events.ts +140 -0
  26. package/src/hooks/use-flow-upload.ts +47 -112
  27. package/src/hooks/use-upload-events.ts +195 -0
  28. package/src/hooks/use-uploadista-client.ts +1 -0
  29. package/src/hooks/use-uploadista-events.ts +41 -0
  30. package/src/index.ts +22 -0
  31. package/dist/uploadista-provider-CrS6TmpJ.d.mts.map +0 -1
  32. package/dist/use-upload-BNiPsNBv.mjs +0 -2
  33. package/dist/use-upload-BNiPsNBv.mjs.map +0 -1
  34. package/dist/use-upload-metrics-C1amBY1k.mjs +0 -2
  35. package/dist/use-upload-metrics-C1amBY1k.mjs.map +0 -1
  36. package/dist/use-upload-metrics-IXxUORce.d.mts.map +0 -1
  37. package/dist/use-uploadista-client--ivZPO88.d.mts.map +0 -1
@@ -1,6 +1,6 @@
1
- import { BrowserUploadInput, FlowUploadOptions, UploadistaClientOptions, createUploadistaClient } from "@uploadista/client-browser";
2
- import { FlowUploadState, FlowUploadStatus, UploadMetrics, UploadState, UploadStatus } from "@uploadista/client-core";
3
1
  import { UploadFile } from "@uploadista/core/types";
2
+ import { FlowUploadState, FlowUploadStatus, UploadMetrics, UploadState, UploadStatus } from "@uploadista/client-core";
3
+ import { BrowserUploadInput, FlowUploadOptions, UploadistaClientOptions, createUploadistaClient } from "@uploadista/client-browser";
4
4
 
5
5
  //#region src/hooks/use-drag-drop.d.ts
6
6
  interface DragDropOptions {
@@ -209,8 +209,8 @@ interface UseFlowUploadReturn<TOutput = UploadFile> {
209
209
  * The flow engine processes the uploaded file through a DAG of nodes, which can
210
210
  * perform operations like image optimization, storage saving, webhooks, etc.
211
211
  *
212
- * Must be used within an UploadistaProvider. Flow events (node start/end, flow complete)
213
- * are automatically subscribed through the provider context.
212
+ * Must be used within FlowManagerProvider (which must be within UploadistaProvider).
213
+ * Flow events are automatically routed by the provider to the appropriate manager.
214
214
  *
215
215
  * @template TOutput - Type of the final result from the flow (defaults to UploadFile)
216
216
  * @param options - Flow upload configuration including flow ID and event handlers
@@ -684,4 +684,4 @@ interface UseUploadistaClientReturn {
684
684
  declare function useUploadistaClient(options: UseUploadistaClientOptions): UseUploadistaClientReturn;
685
685
  //#endregion
686
686
  export { useFlowUpload as _, MultiUploadState as a, UseDragDropReturn as b, useMultiUpload as c, UseUploadOptions as d, UseUploadReturn as f, UseFlowUploadReturn as g, FlowUploadStatus as h, MultiUploadOptions as i, UploadState as l, FlowUploadState as m, UseUploadistaClientReturn as n, UploadItem as o, useUpload as p, useUploadistaClient as r, UseMultiUploadReturn as s, UseUploadistaClientOptions as t, UploadStatus as u, DragDropOptions as v, useDragDrop as x, DragDropState as y };
687
- //# sourceMappingURL=use-uploadista-client--ivZPO88.d.mts.map
687
+ //# sourceMappingURL=use-uploadista-client-CRUhyWoy.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-uploadista-client-CRUhyWoy.d.mts","names":[],"sources":["../src/hooks/use-drag-drop.ts","../src/hooks/use-flow-upload.ts","../src/hooks/use-upload.ts","../src/hooks/use-multi-upload.ts","../src/hooks/use-uploadista-client.ts"],"sourcesContent":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@uploadista/react",
3
3
  "type": "module",
4
- "version": "0.0.15-beta.2",
4
+ "version": "0.0.15-beta.4",
5
5
  "description": "React client for Uploadista",
6
6
  "license": "MIT",
7
7
  "author": "Uploadista",
@@ -22,16 +22,16 @@
22
22
  "dependencies": {
23
23
  "react": "19.2.0",
24
24
  "react-dom": "19.2.0",
25
- "@uploadista/core": "0.0.15-beta.2",
26
- "@uploadista/client-core": "0.0.15-beta.2",
27
- "@uploadista/client-browser": "0.0.15-beta.2"
25
+ "@uploadista/core": "0.0.15-beta.4",
26
+ "@uploadista/client-core": "0.0.15-beta.4",
27
+ "@uploadista/client-browser": "0.0.15-beta.4"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/react": "19.2.4",
31
31
  "@types/react-dom": "19.2.3",
32
32
  "tsdown": "0.16.4",
33
33
  "vitest": "4.0.8",
34
- "@uploadista/typescript-config": "0.0.15-beta.2"
34
+ "@uploadista/typescript-config": "0.0.15-beta.4"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "tsdown",
@@ -2,6 +2,7 @@
2
2
  import type { UploadistaEvent } from "@uploadista/client-browser";
3
3
  import type React from "react";
4
4
  import { createContext, useCallback, useContext, useMemo, useRef } from "react";
5
+ import { FlowManagerProvider } from "../contexts/flow-manager-context";
5
6
  import {
6
7
  type UseUploadistaClientOptions,
7
8
  type UseUploadistaClientReturn,
@@ -16,10 +17,10 @@ import {
16
17
  * @property baseUrl - API base URL for uploads
17
18
  * @property storageId - Default storage identifier
18
19
  * @property chunkSize - Upload chunk size in bytes
19
- * @property onEvent - Global event handler for all upload events
20
20
  * @property ... - All other UploadistaClientOptions
21
21
  */
22
- export interface UploadistaProviderProps extends UseUploadistaClientOptions {
22
+ export interface UploadistaProviderProps
23
+ extends Omit<UseUploadistaClientOptions, "onEvent"> {
23
24
  /**
24
25
  * Children components that will have access to the upload client
25
26
  */
@@ -53,9 +54,6 @@ const UploadistaContext = createContext<UploadistaContextValue | null>(null);
53
54
  * baseUrl="https://api.example.com"
54
55
  * storageId="my-storage"
55
56
  * chunkSize={1024 * 1024} // 1MB chunks
56
- * onEvent={(event) => {
57
- * console.log('Global upload event:', event);
58
- * }}
59
57
  * >
60
58
  * <UploadInterface />
61
59
  * </UploadistaProvider>
@@ -89,34 +87,21 @@ export function UploadistaProvider({
89
87
  new Set(),
90
88
  );
91
89
 
92
- // Wrap the original onEvent to broadcast to subscribers
93
- const wrappedOnEvent = useCallback(
94
- (event: UploadistaEvent) => {
95
- console.log("[UploadistaProvider] Received event:", event);
96
-
97
- // Call original handler if provided
98
- options.onEvent?.(event);
99
-
100
- // Broadcast to all subscribers
101
- console.log(
102
- "[UploadistaProvider] Broadcasting to",
103
- eventSubscribersRef.current.size,
104
- "subscribers",
105
- );
106
- eventSubscribersRef.current.forEach((handler) => {
107
- try {
108
- handler(event);
109
- } catch (err) {
110
- console.error("Error in event subscriber:", err);
111
- }
112
- });
113
- },
114
- [options.onEvent],
115
- );
90
+ // Event handler that broadcasts to all subscribers
91
+ const handleEvent = useCallback((event: UploadistaEvent) => {
92
+ // Broadcast to all subscribers
93
+ eventSubscribersRef.current.forEach((handler) => {
94
+ try {
95
+ handler(event);
96
+ } catch (err) {
97
+ console.error("Error in event subscriber:", err);
98
+ }
99
+ });
100
+ }, []);
116
101
 
117
102
  const uploadClient = useUploadistaClient({
118
103
  ...options,
119
- onEvent: wrappedOnEvent,
104
+ onEvent: handleEvent,
120
105
  });
121
106
 
122
107
  const subscribeToEvents = useCallback(
@@ -140,7 +125,7 @@ export function UploadistaProvider({
140
125
 
141
126
  return (
142
127
  <UploadistaContext.Provider value={contextValue}>
143
- {children}
128
+ <FlowManagerProvider>{children}</FlowManagerProvider>
144
129
  </UploadistaContext.Provider>
145
130
  );
146
131
  }
@@ -0,0 +1,230 @@
1
+ import type { UploadistaEvent } from "@uploadista/client-browser";
2
+ import {
3
+ FlowManager,
4
+ type FlowManagerCallbacks,
5
+ type FlowUploadState,
6
+ } from "@uploadista/client-core";
7
+ import { EventType, type FlowEvent } from "@uploadista/core/flow";
8
+ import { UploadEventType } from "@uploadista/core/types";
9
+ import type { ReactNode } from "react";
10
+ import {
11
+ createContext,
12
+ useCallback,
13
+ useContext,
14
+ useEffect,
15
+ useRef,
16
+ } from "react";
17
+ import { useUploadistaContext } from "../components/uploadista-provider";
18
+ import type { FlowUploadOptions } from "@uploadista/client-browser";
19
+
20
+ /**
21
+ * Type guard to check if an event is a flow event
22
+ */
23
+ function isFlowEvent(event: UploadistaEvent): event is FlowEvent {
24
+ const flowEvent = event as FlowEvent;
25
+ return (
26
+ flowEvent.eventType === EventType.FlowStart ||
27
+ flowEvent.eventType === EventType.FlowEnd ||
28
+ flowEvent.eventType === EventType.FlowError ||
29
+ flowEvent.eventType === EventType.NodeStart ||
30
+ flowEvent.eventType === EventType.NodeEnd ||
31
+ flowEvent.eventType === EventType.NodePause ||
32
+ flowEvent.eventType === EventType.NodeResume ||
33
+ flowEvent.eventType === EventType.NodeError
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Internal manager registry entry with ref counting
39
+ */
40
+ interface ManagerEntry<TOutput> {
41
+ manager: FlowManager<unknown, TOutput>;
42
+ refCount: number;
43
+ flowId: string;
44
+ }
45
+
46
+ /**
47
+ * Context value providing access to flow managers
48
+ */
49
+ interface FlowManagerContextValue {
50
+ /**
51
+ * Get or create a flow manager for the given flow ID.
52
+ * Increments ref count - must call releaseManager when done.
53
+ *
54
+ * @param flowId - Unique identifier for the flow
55
+ * @param callbacks - Callbacks for state changes and lifecycle events
56
+ * @param options - Flow configuration options
57
+ * @returns FlowManager instance
58
+ */
59
+ getManager: <TOutput = unknown>(
60
+ flowId: string,
61
+ callbacks: FlowManagerCallbacks<TOutput>,
62
+ options: FlowUploadOptions<TOutput>,
63
+ ) => FlowManager<unknown, TOutput>;
64
+
65
+ /**
66
+ * Release a flow manager reference.
67
+ * Decrements ref count and cleans up when reaching zero.
68
+ *
69
+ * @param flowId - Unique identifier for the flow to release
70
+ */
71
+ releaseManager: (flowId: string) => void;
72
+ }
73
+
74
+ const FlowManagerContext = createContext<FlowManagerContextValue | undefined>(
75
+ undefined,
76
+ );
77
+
78
+ /**
79
+ * Props for FlowManagerProvider
80
+ */
81
+ interface FlowManagerProviderProps {
82
+ children: ReactNode;
83
+ }
84
+
85
+ /**
86
+ * Provider that manages FlowManager instances with ref counting and event routing.
87
+ * Ensures managers persist across component re-renders and are only cleaned up
88
+ * when all consuming components unmount.
89
+ *
90
+ * This provider should be nested inside UploadistaProvider to access the upload client
91
+ * and event subscription system.
92
+ *
93
+ * @example
94
+ * ```tsx
95
+ * <UploadistaProvider baseUrl="https://api.example.com" storageId="default">
96
+ * <FlowManagerProvider>
97
+ * <App />
98
+ * </FlowManagerProvider>
99
+ * </UploadistaProvider>
100
+ * ```
101
+ */
102
+ export function FlowManagerProvider({ children }: FlowManagerProviderProps) {
103
+ const { client, subscribeToEvents } = useUploadistaContext();
104
+ const managersRef = useRef(
105
+ new Map<string, ManagerEntry<unknown>>(),
106
+ );
107
+
108
+ // Subscribe to all events and route to appropriate managers
109
+ useEffect(() => {
110
+ const unsubscribe = subscribeToEvents((event: UploadistaEvent) => {
111
+ // Route flow events to all managers (they filter by jobId internally)
112
+ if (isFlowEvent(event)) {
113
+ for (const entry of managersRef.current.values()) {
114
+ entry.manager.handleFlowEvent(event);
115
+ }
116
+ return;
117
+ }
118
+
119
+ // Route upload progress events to all managers
120
+ if (
121
+ "type" in event &&
122
+ event.type === UploadEventType.UPLOAD_PROGRESS &&
123
+ "data" in event
124
+ ) {
125
+ const uploadEvent = event as {
126
+ type: UploadEventType;
127
+ uploadId: string;
128
+ data: { progress: number; total: number | null };
129
+ };
130
+
131
+ for (const entry of managersRef.current.values()) {
132
+ entry.manager.handleUploadProgress(
133
+ uploadEvent.uploadId,
134
+ uploadEvent.data.progress,
135
+ uploadEvent.data.total,
136
+ );
137
+ }
138
+ }
139
+ });
140
+
141
+ return unsubscribe;
142
+ }, [subscribeToEvents]);
143
+
144
+ const getManager = useCallback(
145
+ <TOutput,>(
146
+ flowId: string,
147
+ callbacks: FlowManagerCallbacks<TOutput>,
148
+ options: FlowUploadOptions<TOutput>,
149
+ ): FlowManager<unknown, TOutput> => {
150
+ const existing = managersRef.current.get(flowId);
151
+
152
+ if (existing) {
153
+ // Increment ref count for existing manager
154
+ existing.refCount++;
155
+ return existing.manager as FlowManager<unknown, TOutput>;
156
+ }
157
+
158
+ // Create new manager using client from hook scope
159
+ const flowUploadFn = (
160
+ input: unknown,
161
+ flowConfig: FlowUploadOptions<TOutput>["flowConfig"],
162
+ internalOptions: unknown,
163
+ ) => {
164
+ return client.uploadWithFlow(input, flowConfig, internalOptions);
165
+ };
166
+
167
+ const manager = new FlowManager<unknown, TOutput>(
168
+ flowUploadFn,
169
+ callbacks,
170
+ options,
171
+ );
172
+
173
+ managersRef.current.set(flowId, {
174
+ manager: manager as FlowManager<unknown, unknown>,
175
+ refCount: 1,
176
+ flowId,
177
+ });
178
+
179
+ return manager;
180
+ },
181
+ [client],
182
+ );
183
+
184
+ const releaseManager = useCallback((flowId: string) => {
185
+ const existing = managersRef.current.get(flowId);
186
+ if (!existing) return;
187
+
188
+ existing.refCount--;
189
+
190
+ // Clean up when no more refs
191
+ if (existing.refCount <= 0) {
192
+ existing.manager.cleanup();
193
+ managersRef.current.delete(flowId);
194
+ }
195
+ }, []);
196
+
197
+ return (
198
+ <FlowManagerContext.Provider value={{ getManager, releaseManager }}>
199
+ {children}
200
+ </FlowManagerContext.Provider>
201
+ );
202
+ }
203
+
204
+ /**
205
+ * Hook to access the FlowManager context.
206
+ * Must be used within a FlowManagerProvider.
207
+ *
208
+ * @returns FlowManager context value with getManager and releaseManager functions
209
+ * @throws Error if used outside of FlowManagerProvider
210
+ *
211
+ * @example
212
+ * ```tsx
213
+ * function MyComponent() {
214
+ * const { getManager, releaseManager } = useFlowManagerContext();
215
+ * // Use to create managers...
216
+ * }
217
+ * ```
218
+ */
219
+ export function useFlowManagerContext(): FlowManagerContextValue {
220
+ const context = useContext(FlowManagerContext);
221
+
222
+ if (context === undefined) {
223
+ throw new Error(
224
+ "useFlowManagerContext must be used within a FlowManagerProvider. " +
225
+ "Make sure to wrap your component tree with <FlowManagerProvider>.",
226
+ );
227
+ }
228
+
229
+ return context;
230
+ }
@@ -0,0 +1,44 @@
1
+ import type { UploadistaEvent } from "@uploadista/client-browser";
2
+ import { EventType, type FlowEvent } from "@uploadista/core/flow";
3
+ import { UploadEventType, type UploadEvent } from "@uploadista/core/types";
4
+
5
+ /**
6
+ * Type guard to check if an event is a flow event
7
+ */
8
+ export function isFlowEvent(event: UploadistaEvent): event is FlowEvent {
9
+ if (!("eventType" in event)) return false;
10
+ const e = event as { eventType: unknown };
11
+ return (
12
+ e.eventType === EventType.JobStart ||
13
+ e.eventType === EventType.JobEnd ||
14
+ e.eventType === EventType.FlowStart ||
15
+ e.eventType === EventType.FlowEnd ||
16
+ e.eventType === EventType.FlowError ||
17
+ e.eventType === EventType.FlowPause ||
18
+ e.eventType === EventType.FlowCancel ||
19
+ e.eventType === EventType.NodeStart ||
20
+ e.eventType === EventType.NodeEnd ||
21
+ e.eventType === EventType.NodePause ||
22
+ e.eventType === EventType.NodeResume ||
23
+ e.eventType === EventType.NodeError ||
24
+ e.eventType === EventType.NodeStream ||
25
+ e.eventType === EventType.NodeResponse
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Type guard to check if an event is an upload event
31
+ */
32
+ export function isUploadEvent(event: UploadistaEvent): event is UploadEvent {
33
+ if (!("type" in event)) return false;
34
+ const e = event as { type: unknown };
35
+ return (
36
+ e.type === UploadEventType.UPLOAD_STARTED ||
37
+ e.type === UploadEventType.UPLOAD_PROGRESS ||
38
+ e.type === UploadEventType.UPLOAD_COMPLETE ||
39
+ e.type === UploadEventType.UPLOAD_FAILED ||
40
+ e.type === UploadEventType.UPLOAD_VALIDATION_SUCCESS ||
41
+ e.type === UploadEventType.UPLOAD_VALIDATION_FAILED ||
42
+ e.type === UploadEventType.UPLOAD_VALIDATION_WARNING
43
+ );
44
+ }
@@ -1,3 +1,19 @@
1
+ // Event Hooks
2
+ export { isFlowEvent, isUploadEvent } from "./event-utils";
3
+ export { useUploadistaEvents } from "./use-uploadista-events";
4
+ export type { UseFlowEventsOptions } from "./use-flow-events";
5
+ export { useFlowEvents } from "./use-flow-events";
6
+ export type {
7
+ UploadFailedEventData,
8
+ UploadFileEventData,
9
+ UploadProgressEventData,
10
+ UploadValidationFailedEventData,
11
+ UploadValidationSuccessEventData,
12
+ UploadValidationWarningEventData,
13
+ UseUploadEventsOptions,
14
+ } from "./use-upload-events";
15
+ export { useUploadEvents } from "./use-upload-events";
16
+
1
17
  // Flow Upload Hooks
2
18
 
3
19
  // Upload Hooks
@@ -0,0 +1,140 @@
1
+ import type {
2
+ FlowEventFlowCancel,
3
+ FlowEventFlowEnd,
4
+ FlowEventFlowError,
5
+ FlowEventFlowPause,
6
+ FlowEventFlowStart,
7
+ FlowEventJobEnd,
8
+ FlowEventJobStart,
9
+ FlowEventNodeEnd,
10
+ FlowEventNodeError,
11
+ FlowEventNodePause,
12
+ FlowEventNodeResume,
13
+ FlowEventNodeStart,
14
+ } from "@uploadista/core/flow";
15
+ import { EventType } from "@uploadista/core/flow";
16
+ import { useEffect } from "react";
17
+ import { useUploadistaContext } from "../components/uploadista-provider";
18
+ import { isFlowEvent } from "./event-utils";
19
+
20
+ /**
21
+ * Options for handling flow execution events.
22
+ *
23
+ * All callbacks are optional - only provide handlers for events you care about.
24
+ */
25
+ export interface UseFlowEventsOptions {
26
+ /** Called when a job starts execution */
27
+ onJobStart?: (event: FlowEventJobStart) => void;
28
+ /** Called when a job completes (success or failure) */
29
+ onJobEnd?: (event: FlowEventJobEnd) => void;
30
+ /** Called when a flow begins execution */
31
+ onFlowStart?: (event: FlowEventFlowStart) => void;
32
+ /** Called when a flow completes successfully */
33
+ onFlowEnd?: (event: FlowEventFlowEnd) => void;
34
+ /** Called when a flow encounters an error */
35
+ onFlowError?: (event: FlowEventFlowError) => void;
36
+ /** Called when a flow is paused by user request */
37
+ onFlowPause?: (event: FlowEventFlowPause) => void;
38
+ /** Called when a flow is cancelled by user request */
39
+ onFlowCancel?: (event: FlowEventFlowCancel) => void;
40
+ /** Called when a node starts processing */
41
+ onNodeStart?: (event: FlowEventNodeStart) => void;
42
+ /** Called when a node completes successfully */
43
+ onNodeEnd?: (event: FlowEventNodeEnd) => void;
44
+ /** Called when a node pauses (waiting for additional data) */
45
+ onNodePause?: (event: FlowEventNodePause) => void;
46
+ /** Called when a paused node resumes execution */
47
+ onNodeResume?: (event: FlowEventNodeResume) => void;
48
+ /** Called when a node encounters an error */
49
+ onNodeError?: (event: FlowEventNodeError) => void;
50
+ }
51
+
52
+ /**
53
+ * Structured hook for handling flow execution events with type-safe callbacks.
54
+ *
55
+ * This hook provides a clean API for listening to specific flow events without
56
+ * needing to manually filter events or use type guards.
57
+ *
58
+ * Must be used within UploadistaProvider.
59
+ *
60
+ * @param options - Object with optional callbacks for each flow event type
61
+ *
62
+ * @example
63
+ * ```tsx
64
+ * import { useFlowEvents } from '@uploadista/react';
65
+ *
66
+ * function FlowMonitor() {
67
+ * useFlowEvents({
68
+ * onFlowStart: (event) => {
69
+ * console.log('Flow started:', event.flowId);
70
+ * },
71
+ * onNodeStart: (event) => {
72
+ * console.log('Node started:', event.nodeName);
73
+ * },
74
+ * onNodeEnd: (event) => {
75
+ * console.log('Node completed:', event.nodeName, event.result);
76
+ * },
77
+ * onFlowEnd: (event) => {
78
+ * console.log('Flow completed with outputs:', event.outputs);
79
+ * },
80
+ * onFlowError: (event) => {
81
+ * console.error('Flow failed:', event.error);
82
+ * },
83
+ * });
84
+ *
85
+ * return <div>Monitoring flow execution...</div>;
86
+ * }
87
+ * ```
88
+ */
89
+ export function useFlowEvents(options: UseFlowEventsOptions): void {
90
+ const { subscribeToEvents } = useUploadistaContext();
91
+
92
+ useEffect(() => {
93
+ const unsubscribe = subscribeToEvents((event) => {
94
+ // Only handle flow events
95
+ if (!isFlowEvent(event)) return;
96
+
97
+ // Route to appropriate callback based on event type
98
+ switch (event.eventType) {
99
+ case EventType.JobStart:
100
+ options.onJobStart?.(event);
101
+ break;
102
+ case EventType.JobEnd:
103
+ options.onJobEnd?.(event);
104
+ break;
105
+ case EventType.FlowStart:
106
+ options.onFlowStart?.(event);
107
+ break;
108
+ case EventType.FlowEnd:
109
+ options.onFlowEnd?.(event);
110
+ break;
111
+ case EventType.FlowError:
112
+ options.onFlowError?.(event);
113
+ break;
114
+ case EventType.FlowPause:
115
+ options.onFlowPause?.(event);
116
+ break;
117
+ case EventType.FlowCancel:
118
+ options.onFlowCancel?.(event);
119
+ break;
120
+ case EventType.NodeStart:
121
+ options.onNodeStart?.(event);
122
+ break;
123
+ case EventType.NodeEnd:
124
+ options.onNodeEnd?.(event);
125
+ break;
126
+ case EventType.NodePause:
127
+ options.onNodePause?.(event);
128
+ break;
129
+ case EventType.NodeResume:
130
+ options.onNodeResume?.(event);
131
+ break;
132
+ case EventType.NodeError:
133
+ options.onNodeError?.(event);
134
+ break;
135
+ }
136
+ });
137
+
138
+ return unsubscribe;
139
+ }, [subscribeToEvents, options]);
140
+ }