@workflow/web-shared 4.0.1-beta.8 → 4.1.0-beta.46
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/README.md +2 -0
- package/dist/api/workflow-api-client.d.ts +325 -85
- package/dist/api/workflow-api-client.d.ts.map +1 -1
- package/dist/api/workflow-api-client.js +370 -214
- package/dist/api/workflow-api-client.js.map +1 -1
- package/dist/api/workflow-server-actions.d.ts +136 -3
- package/dist/api/workflow-server-actions.d.ts.map +1 -1
- package/dist/api/workflow-server-actions.js +649 -116
- package/dist/api/workflow-server-actions.js.map +1 -1
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +18 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/error-card.d.ts +15 -0
- package/dist/components/ui/error-card.d.ts.map +1 -0
- package/dist/components/ui/error-card.js +14 -0
- package/dist/components/ui/error-card.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +3 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeleton.js +7 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/error-boundary.d.ts +28 -0
- package/dist/error-boundary.d.ts.map +1 -0
- package/dist/error-boundary.js +51 -0
- package/dist/error-boundary.js.map +1 -0
- package/dist/event-list-view.d.ts +13 -0
- package/dist/event-list-view.d.ts.map +1 -0
- package/dist/event-list-view.js +183 -0
- package/dist/event-list-view.js.map +1 -0
- package/dist/hook-actions.d.ts +59 -0
- package/dist/hook-actions.d.ts.map +1 -0
- package/dist/hook-actions.js +76 -0
- package/dist/hook-actions.js.map +1 -0
- package/dist/hooks/use-dark-mode.d.ts +9 -0
- package/dist/hooks/use-dark-mode.d.ts.map +1 -0
- package/dist/hooks/use-dark-mode.js +30 -0
- package/dist/hooks/use-dark-mode.js.map +1 -0
- package/dist/index.d.ts +14 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/event-analysis.d.ts +55 -0
- package/dist/lib/event-analysis.d.ts.map +1 -0
- package/dist/lib/event-analysis.js +161 -0
- package/dist/lib/event-analysis.js.map +1 -0
- package/dist/lib/utils.d.ts +44 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +109 -0
- package/dist/lib/utils.js.map +1 -1
- package/dist/run-trace-view.d.ts.map +1 -1
- package/dist/run-trace-view.js +1 -1
- package/dist/run-trace-view.js.map +1 -1
- package/dist/sidebar/attribute-panel.d.ts +12 -2
- package/dist/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/sidebar/attribute-panel.js +368 -23
- package/dist/sidebar/attribute-panel.js.map +1 -1
- package/dist/sidebar/conversation-view.d.ts +7 -0
- package/dist/sidebar/conversation-view.d.ts.map +1 -0
- package/dist/sidebar/conversation-view.js +125 -0
- package/dist/sidebar/conversation-view.js.map +1 -0
- package/dist/sidebar/detail-card.d.ts.map +1 -1
- package/dist/sidebar/detail-card.js +2 -2
- package/dist/sidebar/detail-card.js.map +1 -1
- package/dist/sidebar/entity-detail-panel.d.ts +12 -0
- package/dist/sidebar/entity-detail-panel.d.ts.map +1 -0
- package/dist/sidebar/entity-detail-panel.js +190 -0
- package/dist/sidebar/entity-detail-panel.js.map +1 -0
- package/dist/sidebar/events-list.d.ts +2 -1
- package/dist/sidebar/events-list.d.ts.map +1 -1
- package/dist/sidebar/events-list.js +11 -10
- package/dist/sidebar/events-list.js.map +1 -1
- package/dist/sidebar/resolve-hook-modal.d.ts +16 -0
- package/dist/sidebar/resolve-hook-modal.d.ts.map +1 -0
- package/dist/sidebar/resolve-hook-modal.js +74 -0
- package/dist/sidebar/resolve-hook-modal.js.map +1 -0
- package/dist/stream-viewer.d.ts +13 -0
- package/dist/stream-viewer.d.ts.map +1 -0
- package/dist/stream-viewer.js +109 -0
- package/dist/stream-viewer.js.map +1 -0
- package/dist/trace-viewer/components/markers.d.ts.map +1 -1
- package/dist/trace-viewer/components/markers.js +3 -2
- package/dist/trace-viewer/components/markers.js.map +1 -1
- package/dist/trace-viewer/components/node.d.ts.map +1 -1
- package/dist/trace-viewer/components/node.js +1 -0
- package/dist/trace-viewer/components/node.js.map +1 -1
- package/dist/trace-viewer/components/search.d.ts.map +1 -1
- package/dist/trace-viewer/components/search.js +1 -0
- package/dist/trace-viewer/components/search.js.map +1 -1
- package/dist/trace-viewer/components/span-detail-panel.js +2 -2
- package/dist/trace-viewer/components/span-detail-panel.js.map +1 -1
- package/dist/trace-viewer/context.d.ts.map +1 -1
- package/dist/trace-viewer/context.js +1 -0
- package/dist/trace-viewer/context.js.map +1 -1
- package/dist/trace-viewer/trace-viewer.module.css +47 -30
- package/dist/trace-viewer/types.d.ts +11 -0
- package/dist/trace-viewer/types.d.ts.map +1 -1
- package/dist/trace-viewer/util/timing.d.ts +7 -1
- package/dist/trace-viewer/util/timing.d.ts.map +1 -1
- package/dist/trace-viewer/util/timing.js +7 -12
- package/dist/trace-viewer/util/timing.js.map +1 -1
- package/dist/trace-viewer/util/tree.d.ts.map +1 -1
- package/dist/trace-viewer/util/tree.js +4 -0
- package/dist/trace-viewer/util/tree.js.map +1 -1
- package/dist/trace-viewer/util/use-immediate-style.d.ts.map +1 -1
- package/dist/trace-viewer/util/use-immediate-style.js +1 -0
- package/dist/trace-viewer/util/use-immediate-style.js.map +1 -1
- package/dist/trace-viewer/util/use-streaming-spans.d.ts.map +1 -1
- package/dist/trace-viewer/util/use-streaming-spans.js +2 -1
- package/dist/trace-viewer/util/use-streaming-spans.js.map +1 -1
- package/dist/trace-viewer/util/use-trackpad-zoom.d.ts.map +1 -1
- package/dist/trace-viewer/util/use-trackpad-zoom.js +1 -0
- package/dist/trace-viewer/util/use-trackpad-zoom.js.map +1 -1
- package/dist/trace-viewer/worker.js +1 -1
- package/dist/trace-viewer/worker.js.map +1 -1
- package/dist/workflow-trace-view.d.ts +3 -1
- package/dist/workflow-trace-view.d.ts.map +1 -1
- package/dist/workflow-trace-view.js +28 -11
- package/dist/workflow-trace-view.js.map +1 -1
- package/dist/workflow-traces/event-colors.d.ts +1 -1
- package/dist/workflow-traces/event-colors.js +2 -2
- package/dist/workflow-traces/event-colors.js.map +1 -1
- package/dist/workflow-traces/trace-colors.d.ts.map +1 -1
- package/dist/workflow-traces/trace-colors.js +1 -3
- package/dist/workflow-traces/trace-colors.js.map +1 -1
- package/dist/workflow-traces/trace-span-construction.d.ts +18 -3
- package/dist/workflow-traces/trace-span-construction.d.ts.map +1 -1
- package/dist/workflow-traces/trace-span-construction.js +84 -31
- package/dist/workflow-traces/trace-span-construction.js.map +1 -1
- package/dist/workflow-traces/trace-time-utils.d.ts +2 -2
- package/dist/workflow-traces/trace-time-utils.d.ts.map +1 -1
- package/dist/workflow-traces/trace-time-utils.js +9 -0
- package/dist/workflow-traces/trace-time-utils.js.map +1 -1
- package/package.json +26 -15
- package/server/README.md +1 -0
- package/server/package.json +4 -0
- package/dist/sidebar/workflow-detail-panel.d.ts +0 -8
- package/dist/sidebar/workflow-detail-panel.d.ts.map +0 -1
- package/dist/sidebar/workflow-detail-panel.js +0 -56
- package/dist/sidebar/workflow-detail-panel.js.map +0 -1
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { VERCEL_403_ERROR_MESSAGE } from '@workflow/errors';
|
|
2
3
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
4
|
import { getPaginationDisplay } from '../lib/utils';
|
|
4
|
-
import {
|
|
5
|
+
import { hookEventsToHookEntity, waitEventsToWaitEntity, } from '../workflow-traces/trace-span-construction';
|
|
6
|
+
import { cancelRun as cancelRunServerAction, fetchEvents, fetchEventsByCorrelationId, fetchHook, fetchHooks, fetchRun, fetchRuns, fetchStep, fetchSteps, fetchStreams, readStreamServerAction, recreateRun as recreateRunServerAction, reenqueueRun as reenqueueRunServerAction, resumeHook as resumeHookServerAction, wakeUpRun as wakeUpRunServerAction, } from './workflow-server-actions';
|
|
5
7
|
const MAX_ITEMS = 1000;
|
|
6
|
-
const LIVE_POLL_LIMIT =
|
|
8
|
+
const LIVE_POLL_LIMIT = 10;
|
|
9
|
+
const LIVE_STEP_UPDATE_INTERVAL_MS = 2000;
|
|
7
10
|
const LIVE_UPDATE_INTERVAL_MS = 5000;
|
|
8
11
|
/**
|
|
9
|
-
* Helper to convert ServerActionError to
|
|
12
|
+
* Helper to convert ServerActionError to WorkflowWebAPIError
|
|
10
13
|
*/
|
|
11
14
|
function createWorkflowAPIError(serverError) {
|
|
12
|
-
return new
|
|
15
|
+
return new WorkflowWebAPIError(serverError.message, {
|
|
13
16
|
cause: serverError.cause,
|
|
14
17
|
request: serverError.request,
|
|
15
18
|
layer: serverError.layer,
|
|
@@ -17,31 +20,50 @@ function createWorkflowAPIError(serverError) {
|
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
22
|
* Gets a user-facing error message from an error object.
|
|
20
|
-
* Handles both
|
|
23
|
+
* Handles both WorkflowWebAPIError and regular Error instances.
|
|
21
24
|
*/
|
|
22
25
|
export const getErrorMessage = (error) => {
|
|
23
26
|
if ('layer' in error && error.layer) {
|
|
24
|
-
if (error instanceof
|
|
27
|
+
if (error instanceof WorkflowWebAPIError) {
|
|
25
28
|
if (error.request?.status === 403) {
|
|
26
|
-
return
|
|
29
|
+
return VERCEL_403_ERROR_MESSAGE;
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
|
-
//
|
|
32
|
+
// WorkflowWebAPIError already has user-facing messages
|
|
30
33
|
return error.message;
|
|
31
34
|
}
|
|
32
35
|
return error instanceof Error ? error.message : 'An error occurred';
|
|
33
36
|
};
|
|
34
37
|
/**
|
|
35
|
-
* Helper to handle server action results and throw
|
|
38
|
+
* Helper to handle server action results and throw WorkflowWebAPIError on failure
|
|
36
39
|
*/
|
|
37
|
-
function unwrapServerActionResult(
|
|
40
|
+
export async function unwrapServerActionResult(promise) {
|
|
41
|
+
let result;
|
|
42
|
+
try {
|
|
43
|
+
result = await promise;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
result = {
|
|
47
|
+
success: false,
|
|
48
|
+
error: error,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
38
51
|
if (!result.success) {
|
|
52
|
+
console.error('[web-api-client] error', result.error);
|
|
39
53
|
if (!result.error) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
return {
|
|
55
|
+
error: new WorkflowWebAPIError('Unknown error occurred', {
|
|
56
|
+
layer: 'client',
|
|
57
|
+
}),
|
|
58
|
+
result: null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
error: createWorkflowAPIError(result.error),
|
|
63
|
+
result: null,
|
|
64
|
+
};
|
|
43
65
|
}
|
|
44
|
-
return result.data;
|
|
66
|
+
return { error: null, result: result.data };
|
|
45
67
|
}
|
|
46
68
|
/**
|
|
47
69
|
* Error instance for API and server-side errors.
|
|
@@ -57,12 +79,12 @@ function unwrapServerActionResult(result) {
|
|
|
57
79
|
* calling the server action, these fields will be populated:
|
|
58
80
|
* - `error.layer` will be 'server'
|
|
59
81
|
*/
|
|
60
|
-
export class
|
|
82
|
+
export class WorkflowWebAPIError extends Error {
|
|
61
83
|
request;
|
|
62
84
|
layer;
|
|
63
85
|
constructor(message, options) {
|
|
64
86
|
super(message, { cause: options?.cause });
|
|
65
|
-
this.name = '
|
|
87
|
+
this.name = 'WorkflowWebAPIError';
|
|
66
88
|
this.request = options?.request;
|
|
67
89
|
this.layer = options?.layer;
|
|
68
90
|
if (options?.cause instanceof Error) {
|
|
@@ -116,42 +138,14 @@ export function useWorkflowRuns(env, params) {
|
|
|
116
138
|
return;
|
|
117
139
|
}
|
|
118
140
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const result = unwrapServerActionResult(serverResult);
|
|
128
|
-
// Cache the result
|
|
129
|
-
pageCache.current.set(cacheKey, {
|
|
130
|
-
data: result.data,
|
|
131
|
-
cursor: result.cursor,
|
|
132
|
-
hasMore: result.hasMore,
|
|
133
|
-
});
|
|
134
|
-
setAllPageResults((prev) => {
|
|
135
|
-
const newMap = new Map(prev);
|
|
136
|
-
newMap.set(pageIndex, {
|
|
137
|
-
data: result.data,
|
|
138
|
-
isLoading: false,
|
|
139
|
-
error: null,
|
|
140
|
-
});
|
|
141
|
-
return newMap;
|
|
142
|
-
});
|
|
143
|
-
setCursor(result.cursor);
|
|
144
|
-
setHasMore(result.hasMore);
|
|
145
|
-
}
|
|
146
|
-
catch (err) {
|
|
147
|
-
const error = err instanceof WorkflowAPIError
|
|
148
|
-
? err
|
|
149
|
-
: err instanceof Error
|
|
150
|
-
? new WorkflowAPIError(err.message, {
|
|
151
|
-
cause: err,
|
|
152
|
-
layer: 'client',
|
|
153
|
-
})
|
|
154
|
-
: new WorkflowAPIError(String(err), { layer: 'client' });
|
|
141
|
+
const { error, result } = await unwrapServerActionResult(fetchRuns(env, {
|
|
142
|
+
cursor: pageCursor,
|
|
143
|
+
sortOrder,
|
|
144
|
+
limit: limit,
|
|
145
|
+
workflowName,
|
|
146
|
+
status,
|
|
147
|
+
}));
|
|
148
|
+
if (error) {
|
|
155
149
|
setAllPageResults((prev) => {
|
|
156
150
|
const newMap = new Map(prev);
|
|
157
151
|
newMap.set(pageIndex, {
|
|
@@ -161,7 +155,25 @@ export function useWorkflowRuns(env, params) {
|
|
|
161
155
|
});
|
|
162
156
|
return newMap;
|
|
163
157
|
});
|
|
158
|
+
return;
|
|
164
159
|
}
|
|
160
|
+
// Cache the result
|
|
161
|
+
pageCache.current.set(cacheKey, {
|
|
162
|
+
data: result.data,
|
|
163
|
+
cursor: result.cursor,
|
|
164
|
+
hasMore: result.hasMore,
|
|
165
|
+
});
|
|
166
|
+
setAllPageResults((prev) => {
|
|
167
|
+
const newMap = new Map(prev);
|
|
168
|
+
newMap.set(pageIndex, {
|
|
169
|
+
data: result.data,
|
|
170
|
+
isLoading: false,
|
|
171
|
+
error: null,
|
|
172
|
+
});
|
|
173
|
+
return newMap;
|
|
174
|
+
});
|
|
175
|
+
setCursor(result.cursor);
|
|
176
|
+
setHasMore(result.hasMore);
|
|
165
177
|
}, [env, workflowName, limit, sortOrder, status]);
|
|
166
178
|
// Initial load
|
|
167
179
|
// biome-ignore lint/correctness/useExhaustiveDependencies: Want to refetch first page on param change
|
|
@@ -204,6 +216,16 @@ export function useWorkflowRuns(env, params) {
|
|
|
204
216
|
// Force fetch first page
|
|
205
217
|
fetchPage(0, undefined, true);
|
|
206
218
|
}, [fetchPage]);
|
|
219
|
+
const refresh = useCallback(() => {
|
|
220
|
+
// Refetch current page without resetting state
|
|
221
|
+
// This preserves the existing data while loading, preventing flicker
|
|
222
|
+
const currentCursor = pageHistory[currentPage];
|
|
223
|
+
// Clear cache for current page to ensure fresh data
|
|
224
|
+
const cacheKey = currentCursor ?? 'initial';
|
|
225
|
+
pageCache.current.delete(cacheKey);
|
|
226
|
+
// Force fetch current page
|
|
227
|
+
fetchPage(currentPage, currentCursor, true);
|
|
228
|
+
}, [fetchPage, currentPage, pageHistory]);
|
|
207
229
|
const currentPageResult = allPageResults.get(currentPage) ?? {
|
|
208
230
|
data: null,
|
|
209
231
|
isLoading: true,
|
|
@@ -219,7 +241,7 @@ export function useWorkflowRuns(env, params) {
|
|
|
219
241
|
const isOnLastVisitedPage = currentPageNumber === maxPagesVisited;
|
|
220
242
|
const showPlus = isOnLastVisitedPage && hasMore;
|
|
221
243
|
const pageInfo = getPaginationDisplay(currentPageNumber, maxPagesVisited, showPlus);
|
|
222
|
-
|
|
244
|
+
const result = {
|
|
223
245
|
data: currentPageResult,
|
|
224
246
|
allData: Array.from(allPageResults.values()),
|
|
225
247
|
error: globalError,
|
|
@@ -231,8 +253,10 @@ export function useWorkflowRuns(env, params) {
|
|
|
231
253
|
hasNextPage: hasMore,
|
|
232
254
|
hasPreviousPage: currentPage > 0,
|
|
233
255
|
reload,
|
|
256
|
+
refresh,
|
|
234
257
|
pageInfo,
|
|
235
258
|
};
|
|
259
|
+
return result;
|
|
236
260
|
}
|
|
237
261
|
/**
|
|
238
262
|
* Returns a list of hooks with pagination control
|
|
@@ -280,41 +304,13 @@ export function useWorkflowHooks(env, params) {
|
|
|
280
304
|
return;
|
|
281
305
|
}
|
|
282
306
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const result = unwrapServerActionResult(serverResult);
|
|
291
|
-
// Cache the result
|
|
292
|
-
pageCache.current.set(cacheKey, {
|
|
293
|
-
data: result.data,
|
|
294
|
-
cursor: result.cursor,
|
|
295
|
-
hasMore: result.hasMore,
|
|
296
|
-
});
|
|
297
|
-
setAllPageResults((prev) => {
|
|
298
|
-
const newMap = new Map(prev);
|
|
299
|
-
newMap.set(pageIndex, {
|
|
300
|
-
data: result.data,
|
|
301
|
-
isLoading: false,
|
|
302
|
-
error: null,
|
|
303
|
-
});
|
|
304
|
-
return newMap;
|
|
305
|
-
});
|
|
306
|
-
setCursor(result.cursor);
|
|
307
|
-
setHasMore(result.hasMore);
|
|
308
|
-
}
|
|
309
|
-
catch (err) {
|
|
310
|
-
const error = err instanceof WorkflowAPIError
|
|
311
|
-
? err
|
|
312
|
-
: err instanceof Error
|
|
313
|
-
? new WorkflowAPIError(err.message, {
|
|
314
|
-
cause: err,
|
|
315
|
-
layer: 'client',
|
|
316
|
-
})
|
|
317
|
-
: new WorkflowAPIError(String(err), { layer: 'client' });
|
|
307
|
+
const { error, result } = await unwrapServerActionResult(fetchHooks(env, {
|
|
308
|
+
runId,
|
|
309
|
+
cursor: pageCursor,
|
|
310
|
+
sortOrder,
|
|
311
|
+
limit: limit,
|
|
312
|
+
}));
|
|
313
|
+
if (error) {
|
|
318
314
|
setAllPageResults((prev) => {
|
|
319
315
|
const newMap = new Map(prev);
|
|
320
316
|
newMap.set(pageIndex, {
|
|
@@ -324,7 +320,25 @@ export function useWorkflowHooks(env, params) {
|
|
|
324
320
|
});
|
|
325
321
|
return newMap;
|
|
326
322
|
});
|
|
323
|
+
return;
|
|
327
324
|
}
|
|
325
|
+
// Cache the result
|
|
326
|
+
pageCache.current.set(cacheKey, {
|
|
327
|
+
data: result.data,
|
|
328
|
+
cursor: result.cursor,
|
|
329
|
+
hasMore: result.hasMore,
|
|
330
|
+
});
|
|
331
|
+
setAllPageResults((prev) => {
|
|
332
|
+
const newMap = new Map(prev);
|
|
333
|
+
newMap.set(pageIndex, {
|
|
334
|
+
data: result.data,
|
|
335
|
+
isLoading: false,
|
|
336
|
+
error: null,
|
|
337
|
+
});
|
|
338
|
+
return newMap;
|
|
339
|
+
});
|
|
340
|
+
setCursor(result.cursor);
|
|
341
|
+
setHasMore(result.hasMore);
|
|
328
342
|
}, [env, runId, limit, sortOrder]);
|
|
329
343
|
// Initial load
|
|
330
344
|
useEffect(() => {
|
|
@@ -366,6 +380,16 @@ export function useWorkflowHooks(env, params) {
|
|
|
366
380
|
// Force fetch first page
|
|
367
381
|
fetchPage(0, undefined, true);
|
|
368
382
|
}, [fetchPage]);
|
|
383
|
+
const refresh = useCallback(() => {
|
|
384
|
+
// Refetch current page without resetting state
|
|
385
|
+
// This preserves the existing data while loading, preventing flicker
|
|
386
|
+
const currentCursor = pageHistory[currentPage];
|
|
387
|
+
// Clear cache for current page to ensure fresh data
|
|
388
|
+
const cacheKey = currentCursor ?? 'initial';
|
|
389
|
+
pageCache.current.delete(cacheKey);
|
|
390
|
+
// Force fetch current page
|
|
391
|
+
fetchPage(currentPage, currentCursor, true);
|
|
392
|
+
}, [fetchPage, currentPage, pageHistory]);
|
|
369
393
|
const currentPageResult = allPageResults.get(currentPage) ?? {
|
|
370
394
|
data: null,
|
|
371
395
|
isLoading: true,
|
|
@@ -393,6 +417,7 @@ export function useWorkflowHooks(env, params) {
|
|
|
393
417
|
hasNextPage: hasMore,
|
|
394
418
|
hasPreviousPage: currentPage > 0,
|
|
395
419
|
reload,
|
|
420
|
+
refresh,
|
|
396
421
|
pageInfo,
|
|
397
422
|
};
|
|
398
423
|
}
|
|
@@ -401,12 +426,15 @@ async function fetchAllSteps(env, runId) {
|
|
|
401
426
|
let stepsData = [];
|
|
402
427
|
let stepsCursor;
|
|
403
428
|
while (true) {
|
|
404
|
-
const
|
|
429
|
+
const { error, result } = await unwrapServerActionResult(fetchSteps(env, runId, {
|
|
405
430
|
cursor: stepsCursor,
|
|
406
431
|
sortOrder: 'asc',
|
|
407
432
|
limit: 100,
|
|
408
|
-
});
|
|
409
|
-
|
|
433
|
+
}));
|
|
434
|
+
// TODO: We're not handling errors well for infinite fetches
|
|
435
|
+
if (error) {
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
410
438
|
stepsData = [...stepsData, ...result.data];
|
|
411
439
|
if (!result.hasMore || !result.cursor || stepsData.length >= MAX_ITEMS) {
|
|
412
440
|
break;
|
|
@@ -420,13 +448,15 @@ async function fetchAllHooks(env, runId) {
|
|
|
420
448
|
let hooksData = [];
|
|
421
449
|
let hooksCursor;
|
|
422
450
|
while (true) {
|
|
423
|
-
const
|
|
451
|
+
const { error, result } = await unwrapServerActionResult(fetchHooks(env, {
|
|
424
452
|
runId,
|
|
425
453
|
cursor: hooksCursor,
|
|
426
454
|
sortOrder: 'asc',
|
|
427
455
|
limit: 100,
|
|
428
|
-
});
|
|
429
|
-
|
|
456
|
+
}));
|
|
457
|
+
if (error) {
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
430
460
|
hooksData = [...hooksData, ...result.data];
|
|
431
461
|
if (!result.hasMore || !result.cursor || hooksData.length >= MAX_ITEMS) {
|
|
432
462
|
break;
|
|
@@ -440,12 +470,14 @@ async function fetchAllEvents(env, runId) {
|
|
|
440
470
|
let eventsData = [];
|
|
441
471
|
let eventsCursor;
|
|
442
472
|
while (true) {
|
|
443
|
-
const
|
|
473
|
+
const { error, result } = await unwrapServerActionResult(fetchEvents(env, runId, {
|
|
444
474
|
cursor: eventsCursor,
|
|
445
475
|
sortOrder: 'asc',
|
|
446
476
|
limit: 1000,
|
|
447
|
-
});
|
|
448
|
-
|
|
477
|
+
}));
|
|
478
|
+
if (error) {
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
449
481
|
eventsData = [...eventsData, ...result.data];
|
|
450
482
|
if (!result.hasMore || !result.cursor || eventsData.length >= MAX_ITEMS) {
|
|
451
483
|
break;
|
|
@@ -465,6 +497,7 @@ export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
|
465
497
|
const [hooks, setHooks] = useState([]);
|
|
466
498
|
const [events, setEvents] = useState([]);
|
|
467
499
|
const [loading, setLoading] = useState(true);
|
|
500
|
+
const [auxiliaryDataLoading, setAuxiliaryDataLoading] = useState(false);
|
|
468
501
|
const [error, setError] = useState(null);
|
|
469
502
|
const [stepsCursor, setStepsCursor] = useState();
|
|
470
503
|
const [hooksCursor, setHooksCursor] = useState();
|
|
@@ -478,38 +511,41 @@ export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
|
478
511
|
}
|
|
479
512
|
isFetchingRef.current = true;
|
|
480
513
|
setLoading(true);
|
|
514
|
+
setAuxiliaryDataLoading(true);
|
|
481
515
|
setError(null);
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
516
|
+
const promises = [
|
|
517
|
+
unwrapServerActionResult(fetchRun(env, runId)).then(({ error, result }) => {
|
|
518
|
+
if (error) {
|
|
519
|
+
setError(error);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
setRun(result);
|
|
523
|
+
return result;
|
|
524
|
+
}),
|
|
525
|
+
fetchAllSteps(env, runId).then((result) => {
|
|
526
|
+
setSteps(result.data);
|
|
527
|
+
setStepsCursor(result.cursor);
|
|
528
|
+
}),
|
|
529
|
+
fetchAllHooks(env, runId).then((result) => {
|
|
530
|
+
setHooks(result.data);
|
|
531
|
+
setHooksCursor(result.cursor);
|
|
532
|
+
}),
|
|
533
|
+
fetchAllEvents(env, runId).then((result) => {
|
|
534
|
+
setEvents(result.data);
|
|
535
|
+
setEventsCursor(result.cursor);
|
|
536
|
+
}),
|
|
537
|
+
];
|
|
538
|
+
const results = await Promise.allSettled(promises);
|
|
539
|
+
setLoading(false);
|
|
540
|
+
setAuxiliaryDataLoading(false);
|
|
541
|
+
setInitialLoadCompleted(true);
|
|
542
|
+
isFetchingRef.current = false;
|
|
543
|
+
// Just doing the first error, but would be nice to show multiple
|
|
544
|
+
const error = results.find((result) => result.status === 'rejected')
|
|
545
|
+
?.reason;
|
|
546
|
+
if (error) {
|
|
507
547
|
setError(error);
|
|
508
|
-
|
|
509
|
-
finally {
|
|
510
|
-
setLoading(false);
|
|
511
|
-
isFetchingRef.current = false;
|
|
512
|
-
setInitialLoadCompleted(true);
|
|
548
|
+
return;
|
|
513
549
|
}
|
|
514
550
|
}, [env, runId]);
|
|
515
551
|
// Helper to merge steps by ID
|
|
@@ -534,22 +570,31 @@ export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
|
534
570
|
if (run?.completedAt) {
|
|
535
571
|
return false;
|
|
536
572
|
}
|
|
537
|
-
const
|
|
538
|
-
|
|
573
|
+
const { error, result } = await unwrapServerActionResult(fetchRun(env, runId));
|
|
574
|
+
if (error) {
|
|
575
|
+
setError(error);
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
539
578
|
setRun(result);
|
|
540
579
|
return true;
|
|
541
580
|
}, [env, runId, run?.completedAt]);
|
|
542
581
|
// Poll for new steps
|
|
543
582
|
const pollSteps = useCallback(async () => {
|
|
544
|
-
const
|
|
583
|
+
const { error, result } = await unwrapServerActionResult(fetchSteps(env, runId, {
|
|
545
584
|
cursor: stepsCursor,
|
|
546
585
|
sortOrder: 'asc',
|
|
547
586
|
limit: LIVE_POLL_LIMIT,
|
|
548
|
-
});
|
|
549
|
-
|
|
587
|
+
}));
|
|
588
|
+
if (error) {
|
|
589
|
+
setError(error);
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
550
592
|
if (result.data.length > 0) {
|
|
551
593
|
setSteps((prev) => mergeSteps(prev, result.data));
|
|
552
|
-
|
|
594
|
+
// We intentionally leave the cursor where it is, unless we're at the end of the page
|
|
595
|
+
// in which case we roll over. This is so that we re-fetch existing steps, to ensure
|
|
596
|
+
// their status gets updated.
|
|
597
|
+
if (result.cursor && result.hasMore) {
|
|
553
598
|
setStepsCursor(result.cursor);
|
|
554
599
|
}
|
|
555
600
|
return true;
|
|
@@ -558,13 +603,16 @@ export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
|
558
603
|
}, [env, runId, stepsCursor, mergeSteps]);
|
|
559
604
|
// Poll for new hooks
|
|
560
605
|
const pollHooks = useCallback(async () => {
|
|
561
|
-
const
|
|
606
|
+
const { error, result } = await unwrapServerActionResult(fetchHooks(env, {
|
|
562
607
|
runId,
|
|
563
608
|
cursor: hooksCursor,
|
|
564
609
|
sortOrder: 'asc',
|
|
565
610
|
limit: LIVE_POLL_LIMIT,
|
|
566
|
-
});
|
|
567
|
-
|
|
611
|
+
}));
|
|
612
|
+
if (error) {
|
|
613
|
+
setError(error);
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
568
616
|
if (result.data.length > 0) {
|
|
569
617
|
setHooks((prev) => mergeHooks(prev, result.data));
|
|
570
618
|
if (result.cursor) {
|
|
@@ -576,12 +624,15 @@ export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
|
576
624
|
}, [env, runId, hooksCursor, mergeHooks]);
|
|
577
625
|
// Poll for new events
|
|
578
626
|
const pollEvents = useCallback(async () => {
|
|
579
|
-
const
|
|
627
|
+
const { error, result } = await unwrapServerActionResult(fetchEvents(env, runId, {
|
|
580
628
|
cursor: eventsCursor,
|
|
581
629
|
sortOrder: 'asc',
|
|
582
630
|
limit: LIVE_POLL_LIMIT,
|
|
583
|
-
});
|
|
584
|
-
|
|
631
|
+
}));
|
|
632
|
+
if (error) {
|
|
633
|
+
setError(error);
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
585
636
|
if (result.data.length > 0) {
|
|
586
637
|
setEvents((prev) => mergeEvents(prev, result.data));
|
|
587
638
|
if (result.cursor) {
|
|
@@ -592,17 +643,17 @@ export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
|
592
643
|
return false;
|
|
593
644
|
}, [env, runId, eventsCursor, mergeEvents]);
|
|
594
645
|
// Update function for live polling
|
|
595
|
-
const update = useCallback(async () => {
|
|
646
|
+
const update = useCallback(async (stepsOnly = false) => {
|
|
596
647
|
if (isFetchingRef.current || !initialLoadCompleted) {
|
|
597
648
|
return { foundNewItems: false };
|
|
598
649
|
}
|
|
599
650
|
let foundNewItems = false;
|
|
600
651
|
try {
|
|
601
652
|
const [_, stepsUpdated, hooksUpdated, eventsUpdated] = await Promise.all([
|
|
602
|
-
pollRun(),
|
|
653
|
+
stepsOnly ? Promise.resolve(false) : pollRun(),
|
|
603
654
|
pollSteps(),
|
|
604
|
-
pollHooks(),
|
|
605
|
-
pollEvents(),
|
|
655
|
+
stepsOnly ? Promise.resolve(false) : pollHooks(),
|
|
656
|
+
stepsOnly ? Promise.resolve(false) : pollEvents(),
|
|
606
657
|
]);
|
|
607
658
|
foundNewItems = stepsUpdated || hooksUpdated || eventsUpdated;
|
|
608
659
|
}
|
|
@@ -623,7 +674,13 @@ export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
|
623
674
|
const interval = setInterval(() => {
|
|
624
675
|
update();
|
|
625
676
|
}, LIVE_UPDATE_INTERVAL_MS);
|
|
626
|
-
|
|
677
|
+
const stepInterval = setInterval(() => {
|
|
678
|
+
update(true);
|
|
679
|
+
}, LIVE_STEP_UPDATE_INTERVAL_MS);
|
|
680
|
+
return () => {
|
|
681
|
+
clearInterval(interval);
|
|
682
|
+
clearInterval(stepInterval);
|
|
683
|
+
};
|
|
627
684
|
}, [live, initialLoadCompleted, update, run?.completedAt]);
|
|
628
685
|
return {
|
|
629
686
|
run: run ?? {},
|
|
@@ -631,6 +688,7 @@ export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
|
631
688
|
hooks,
|
|
632
689
|
events,
|
|
633
690
|
loading,
|
|
691
|
+
auxiliaryDataLoading,
|
|
634
692
|
error,
|
|
635
693
|
update,
|
|
636
694
|
};
|
|
@@ -641,28 +699,37 @@ async function fetchResourceWithCorrelationId(env, resource, resourceId, options
|
|
|
641
699
|
let correlationId;
|
|
642
700
|
const resolveData = options.resolveData ?? 'all';
|
|
643
701
|
if (resource === 'run') {
|
|
644
|
-
const
|
|
645
|
-
|
|
702
|
+
const { error, result } = await unwrapServerActionResult(fetchRun(env, resourceId, resolveData));
|
|
703
|
+
if (error) {
|
|
704
|
+
throw error;
|
|
705
|
+
}
|
|
706
|
+
resourceData = result;
|
|
646
707
|
correlationId = resourceData.runId;
|
|
647
708
|
}
|
|
648
709
|
else if (resource === 'step') {
|
|
649
710
|
const { runId } = options;
|
|
650
711
|
if (!runId) {
|
|
651
|
-
throw new
|
|
712
|
+
throw new WorkflowWebAPIError('runId is required for step resource', {
|
|
652
713
|
layer: 'client',
|
|
653
714
|
});
|
|
654
715
|
}
|
|
655
|
-
const
|
|
656
|
-
|
|
716
|
+
const { error, result } = await unwrapServerActionResult(fetchStep(env, runId, resourceId, resolveData));
|
|
717
|
+
if (error) {
|
|
718
|
+
throw error;
|
|
719
|
+
}
|
|
720
|
+
resourceData = result;
|
|
657
721
|
correlationId = resourceData.stepId;
|
|
658
722
|
}
|
|
659
723
|
else if (resource === 'hook') {
|
|
660
|
-
const
|
|
661
|
-
|
|
724
|
+
const { error, result } = await unwrapServerActionResult(fetchHook(env, resourceId, resolveData));
|
|
725
|
+
if (error) {
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
resourceData = result;
|
|
662
729
|
correlationId = resourceData.hookId;
|
|
663
730
|
}
|
|
664
731
|
else {
|
|
665
|
-
throw new
|
|
732
|
+
throw new WorkflowWebAPIError(`Unknown resource type: ${resource}`, {
|
|
666
733
|
layer: 'client',
|
|
667
734
|
});
|
|
668
735
|
}
|
|
@@ -679,55 +746,55 @@ export function useWorkflowResourceData(env, resource, resourceId, options = {})
|
|
|
679
746
|
const [loading, setLoading] = useState(true);
|
|
680
747
|
const [error, setError] = useState(null);
|
|
681
748
|
const fetchData = useCallback(async () => {
|
|
682
|
-
setLoading(true);
|
|
683
749
|
setData(null);
|
|
684
750
|
setError(null);
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
}
|
|
751
|
+
if (resource === 'hook' || resource === 'sleep') {
|
|
752
|
+
const { error, result } = await unwrapServerActionResult(fetchEventsByCorrelationId(env, resourceId, {
|
|
753
|
+
sortOrder: 'asc',
|
|
754
|
+
limit: 100,
|
|
755
|
+
withData: true,
|
|
756
|
+
}));
|
|
757
|
+
if (error) {
|
|
758
|
+
setError(error);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const events = result.data;
|
|
762
|
+
const data = resource === 'hook'
|
|
763
|
+
? hookEventsToHookEntity(events)
|
|
764
|
+
: waitEventsToWaitEntity(events);
|
|
765
|
+
if (data === null) {
|
|
766
|
+
setError(new Error(`Failed to load ${resource} details: missing required event data`));
|
|
702
767
|
return;
|
|
703
768
|
}
|
|
704
|
-
|
|
769
|
+
setData(data);
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
setLoading(true);
|
|
773
|
+
// Fetch resource with full data
|
|
774
|
+
try {
|
|
705
775
|
const { data: resourceData } = await fetchResourceWithCorrelationId(env, resource, resourceId, {
|
|
706
776
|
runId,
|
|
707
777
|
});
|
|
708
778
|
setData(resourceData);
|
|
709
|
-
if (resource === 'run') {
|
|
710
|
-
setLoading(false);
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
// // Fetch events by correlation ID
|
|
714
|
-
// const eventsData = await fetchAllEventsByCorrelationId(
|
|
715
|
-
// env,
|
|
716
|
-
// correlationId
|
|
717
|
-
// );
|
|
718
|
-
// setEvents(eventsData);
|
|
719
779
|
}
|
|
720
|
-
catch (
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
780
|
+
catch (error) {
|
|
781
|
+
if (error instanceof Error) {
|
|
782
|
+
setError(error);
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
setError(new Error(String(error)));
|
|
786
|
+
}
|
|
787
|
+
return;
|
|
727
788
|
}
|
|
728
789
|
finally {
|
|
729
790
|
setLoading(false);
|
|
730
791
|
}
|
|
792
|
+
// // Fetch events by correlation ID
|
|
793
|
+
// const eventsData = await fetchAllEventsByCorrelationId(
|
|
794
|
+
// env,
|
|
795
|
+
// correlationId
|
|
796
|
+
// );
|
|
797
|
+
// setEvents(eventsData);
|
|
731
798
|
}, [env, resource, resourceId, runId]);
|
|
732
799
|
// Initial load
|
|
733
800
|
useEffect(() => {
|
|
@@ -753,45 +820,134 @@ export function useWorkflowResourceData(env, resource, resourceId, options = {})
|
|
|
753
820
|
* Cancel a workflow run
|
|
754
821
|
*/
|
|
755
822
|
export async function cancelRun(env, runId) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
760
|
-
catch (err) {
|
|
761
|
-
console.error('Error canceling run:', err);
|
|
762
|
-
if (err instanceof WorkflowAPIError) {
|
|
763
|
-
throw err;
|
|
764
|
-
}
|
|
765
|
-
throw new WorkflowAPIError(err instanceof Error ? err.message : 'Failed to cancel run', { cause: err, layer: 'client' });
|
|
823
|
+
const { error } = await unwrapServerActionResult(cancelRunServerAction(env, runId));
|
|
824
|
+
if (error) {
|
|
825
|
+
throw error;
|
|
766
826
|
}
|
|
767
827
|
}
|
|
768
828
|
/**
|
|
769
829
|
* Start a new workflow run
|
|
770
830
|
*/
|
|
771
831
|
export async function recreateRun(env, runId) {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
832
|
+
const { error, result: resultData } = await unwrapServerActionResult(recreateRunServerAction(env, runId));
|
|
833
|
+
if (error) {
|
|
834
|
+
throw error;
|
|
775
835
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
836
|
+
return resultData;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Wake up a workflow run by re-enqueuing it
|
|
840
|
+
*/
|
|
841
|
+
export async function reenqueueRun(env, runId) {
|
|
842
|
+
const { error } = await unwrapServerActionResult(reenqueueRunServerAction(env, runId));
|
|
843
|
+
if (error) {
|
|
844
|
+
throw error;
|
|
782
845
|
}
|
|
783
846
|
}
|
|
847
|
+
/**
|
|
848
|
+
* Wake up a workflow run by interrupting any pending sleep() calls
|
|
849
|
+
*/
|
|
850
|
+
export async function wakeUpRun(env, runId, options) {
|
|
851
|
+
const { error, result: resultData } = await unwrapServerActionResult(wakeUpRunServerAction(env, runId, options));
|
|
852
|
+
if (error) {
|
|
853
|
+
throw error;
|
|
854
|
+
}
|
|
855
|
+
return resultData;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Resume a hook by sending a JSON payload
|
|
859
|
+
*/
|
|
860
|
+
export async function resumeHook(env, token, payload) {
|
|
861
|
+
const { error, result: resultData } = await unwrapServerActionResult(resumeHookServerAction(env, token, payload));
|
|
862
|
+
if (error) {
|
|
863
|
+
throw error;
|
|
864
|
+
}
|
|
865
|
+
return resultData;
|
|
866
|
+
}
|
|
867
|
+
function isServerActionError(value) {
|
|
868
|
+
return (typeof value === 'object' &&
|
|
869
|
+
value !== null &&
|
|
870
|
+
'message' in value &&
|
|
871
|
+
'layer' in value &&
|
|
872
|
+
'cause' in value &&
|
|
873
|
+
'request' in value);
|
|
874
|
+
}
|
|
784
875
|
export async function readStream(env, streamId, startIndex) {
|
|
785
876
|
try {
|
|
786
|
-
const
|
|
787
|
-
|
|
877
|
+
const stream = await readStreamServerAction(env, streamId, startIndex);
|
|
878
|
+
if (!stream) {
|
|
879
|
+
throw new WorkflowWebAPIError('Failed to read stream', {
|
|
880
|
+
layer: 'client',
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
if (isServerActionError(stream)) {
|
|
884
|
+
throw new WorkflowWebAPIError(stream.message, {
|
|
885
|
+
layer: 'client',
|
|
886
|
+
cause: stream.cause,
|
|
887
|
+
request: stream.request,
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
return stream;
|
|
788
891
|
}
|
|
789
|
-
catch (
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
throw err;
|
|
892
|
+
catch (error) {
|
|
893
|
+
if (error instanceof WorkflowWebAPIError) {
|
|
894
|
+
throw error;
|
|
793
895
|
}
|
|
794
|
-
throw new
|
|
896
|
+
throw new WorkflowWebAPIError('Failed to read stream', {
|
|
897
|
+
layer: 'client',
|
|
898
|
+
cause: error,
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* List all stream IDs for a run
|
|
904
|
+
*/
|
|
905
|
+
export async function listStreams(env, runId) {
|
|
906
|
+
const { error, result } = await unwrapServerActionResult(fetchStreams(env, runId));
|
|
907
|
+
if (error) {
|
|
908
|
+
throw error;
|
|
795
909
|
}
|
|
910
|
+
return result;
|
|
911
|
+
}
|
|
912
|
+
const STREAMS_REFRESH_INTERVAL_MS = 10000;
|
|
913
|
+
/**
|
|
914
|
+
* Hook to fetch and manage stream list for a run
|
|
915
|
+
*/
|
|
916
|
+
export function useWorkflowStreams(env, runId, refreshInterval = STREAMS_REFRESH_INTERVAL_MS) {
|
|
917
|
+
const [streams, setStreams] = useState([]);
|
|
918
|
+
const [loading, setLoading] = useState(true);
|
|
919
|
+
const [error, setError] = useState(null);
|
|
920
|
+
const fetchData = useCallback(async () => {
|
|
921
|
+
setLoading(true);
|
|
922
|
+
setError(null);
|
|
923
|
+
try {
|
|
924
|
+
const result = await listStreams(env, runId);
|
|
925
|
+
setStreams(result);
|
|
926
|
+
}
|
|
927
|
+
catch (err) {
|
|
928
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
929
|
+
}
|
|
930
|
+
finally {
|
|
931
|
+
setLoading(false);
|
|
932
|
+
}
|
|
933
|
+
}, [env, runId]);
|
|
934
|
+
// Initial load
|
|
935
|
+
useEffect(() => {
|
|
936
|
+
fetchData();
|
|
937
|
+
}, [fetchData]);
|
|
938
|
+
// Auto-refresh interval
|
|
939
|
+
useEffect(() => {
|
|
940
|
+
if (!refreshInterval || refreshInterval <= 0) {
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
const interval = setInterval(fetchData, refreshInterval);
|
|
944
|
+
return () => clearInterval(interval);
|
|
945
|
+
}, [refreshInterval, fetchData]);
|
|
946
|
+
return {
|
|
947
|
+
streams,
|
|
948
|
+
loading,
|
|
949
|
+
error,
|
|
950
|
+
refresh: fetchData,
|
|
951
|
+
};
|
|
796
952
|
}
|
|
797
953
|
//# sourceMappingURL=workflow-api-client.js.map
|