@workflow/web-shared 4.0.1-beta.5
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/LICENSE.md +21 -0
- package/README.md +76 -0
- package/dist/api/workflow-api-client.d.ts +303 -0
- package/dist/api/workflow-api-client.d.ts.map +1 -0
- package/dist/api/workflow-api-client.js +797 -0
- package/dist/api/workflow-api-client.js.map +1 -0
- package/dist/api/workflow-server-actions.d.ts +97 -0
- package/dist/api/workflow-server-actions.d.ts.map +1 -0
- package/dist/api/workflow-server-actions.js +329 -0
- package/dist/api/workflow-server-actions.js.map +1 -0
- package/dist/components/ui/alert.d.ts +9 -0
- package/dist/components/ui/alert.d.ts.map +1 -0
- package/dist/components/ui/alert.jsx +22 -0
- package/dist/components/ui/alert.jsx.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +11 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +19 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/run-trace-view.d.ts +8 -0
- package/dist/run-trace-view.d.ts.map +1 -0
- package/dist/run-trace-view.jsx +25 -0
- package/dist/run-trace-view.jsx.map +1 -0
- package/dist/sidebar/attribute-panel.d.ts +11 -0
- package/dist/sidebar/attribute-panel.d.ts.map +1 -0
- package/dist/sidebar/attribute-panel.jsx +153 -0
- package/dist/sidebar/attribute-panel.jsx.map +1 -0
- package/dist/sidebar/detail-card.d.ts +6 -0
- package/dist/sidebar/detail-card.d.ts.map +1 -0
- package/dist/sidebar/detail-card.jsx +13 -0
- package/dist/sidebar/detail-card.jsx.map +1 -0
- package/dist/sidebar/events-list.d.ts +8 -0
- package/dist/sidebar/events-list.d.ts.map +1 -0
- package/dist/sidebar/events-list.jsx +67 -0
- package/dist/sidebar/events-list.jsx.map +1 -0
- package/dist/sidebar/workflow-detail-panel.d.ts +8 -0
- package/dist/sidebar/workflow-detail-panel.d.ts.map +1 -0
- package/dist/sidebar/workflow-detail-panel.jsx +59 -0
- package/dist/sidebar/workflow-detail-panel.jsx.map +1 -0
- package/dist/trace-viewer/components/map.d.ts +8 -0
- package/dist/trace-viewer/components/map.d.ts.map +1 -0
- package/dist/trace-viewer/components/map.jsx +164 -0
- package/dist/trace-viewer/components/map.jsx.map +1 -0
- package/dist/trace-viewer/components/markers.d.ts +22 -0
- package/dist/trace-viewer/components/markers.d.ts.map +1 -0
- package/dist/trace-viewer/components/markers.jsx +400 -0
- package/dist/trace-viewer/components/markers.jsx.map +1 -0
- package/dist/trace-viewer/components/node.d.ts +31 -0
- package/dist/trace-viewer/components/node.d.ts.map +1 -0
- package/dist/trace-viewer/components/node.jsx +116 -0
- package/dist/trace-viewer/components/node.jsx.map +1 -0
- package/dist/trace-viewer/components/search-input.d.ts +9 -0
- package/dist/trace-viewer/components/search-input.d.ts.map +1 -0
- package/dist/trace-viewer/components/search-input.jsx +16 -0
- package/dist/trace-viewer/components/search-input.jsx.map +1 -0
- package/dist/trace-viewer/components/search.d.ts +3 -0
- package/dist/trace-viewer/components/search.d.ts.map +1 -0
- package/dist/trace-viewer/components/search.jsx +27 -0
- package/dist/trace-viewer/components/search.jsx.map +1 -0
- package/dist/trace-viewer/components/span-detail-panel.d.ts +10 -0
- package/dist/trace-viewer/components/span-detail-panel.d.ts.map +1 -0
- package/dist/trace-viewer/components/span-detail-panel.jsx +388 -0
- package/dist/trace-viewer/components/span-detail-panel.jsx.map +1 -0
- package/dist/trace-viewer/components/ui.d.ts +28 -0
- package/dist/trace-viewer/components/ui.d.ts.map +1 -0
- package/dist/trace-viewer/components/ui.jsx +54 -0
- package/dist/trace-viewer/components/ui.jsx.map +1 -0
- package/dist/trace-viewer/components/zoom-button.d.ts +3 -0
- package/dist/trace-viewer/components/zoom-button.d.ts.map +1 -0
- package/dist/trace-viewer/components/zoom-button.jsx +40 -0
- package/dist/trace-viewer/components/zoom-button.jsx.map +1 -0
- package/dist/trace-viewer/components/zoom-icons.d.ts +11 -0
- package/dist/trace-viewer/components/zoom-icons.d.ts.map +1 -0
- package/dist/trace-viewer/components/zoom-icons.jsx +23 -0
- package/dist/trace-viewer/components/zoom-icons.jsx.map +1 -0
- package/dist/trace-viewer/context.d.ts +183 -0
- package/dist/trace-viewer/context.d.ts.map +1 -0
- package/dist/trace-viewer/context.jsx +326 -0
- package/dist/trace-viewer/context.jsx.map +1 -0
- package/dist/trace-viewer/index.d.ts +5 -0
- package/dist/trace-viewer/index.d.ts.map +1 -0
- package/dist/trace-viewer/index.jsx +4 -0
- package/dist/trace-viewer/index.jsx.map +1 -0
- package/dist/trace-viewer/trace-viewer.d.ts +22 -0
- package/dist/trace-viewer/trace-viewer.d.ts.map +1 -0
- package/dist/trace-viewer/trace-viewer.jsx +311 -0
- package/dist/trace-viewer/trace-viewer.jsx.map +1 -0
- package/dist/trace-viewer/trace-viewer.module.css +1275 -0
- package/dist/trace-viewer/types.d.ts +201 -0
- package/dist/trace-viewer/types.d.ts.map +1 -0
- package/dist/trace-viewer/types.js +2 -0
- package/dist/trace-viewer/types.js.map +1 -0
- package/dist/trace-viewer/util/constants.d.ts +9 -0
- package/dist/trace-viewer/util/constants.d.ts.map +1 -0
- package/dist/trace-viewer/util/constants.js +9 -0
- package/dist/trace-viewer/util/constants.js.map +1 -0
- package/dist/trace-viewer/util/scrollbar-width.d.ts +2 -0
- package/dist/trace-viewer/util/scrollbar-width.d.ts.map +1 -0
- package/dist/trace-viewer/util/scrollbar-width.js +14 -0
- package/dist/trace-viewer/util/scrollbar-width.js.map +1 -0
- package/dist/trace-viewer/util/timing.d.ts +8 -0
- package/dist/trace-viewer/util/timing.d.ts.map +1 -0
- package/dist/trace-viewer/util/timing.js +29 -0
- package/dist/trace-viewer/util/timing.js.map +1 -0
- package/dist/trace-viewer/util/tree.d.ts +13 -0
- package/dist/trace-viewer/util/tree.d.ts.map +1 -0
- package/dist/trace-viewer/util/tree.js +223 -0
- package/dist/trace-viewer/util/tree.js.map +1 -0
- package/dist/trace-viewer/util/use-immediate-style.d.ts +15 -0
- package/dist/trace-viewer/util/use-immediate-style.d.ts.map +1 -0
- package/dist/trace-viewer/util/use-immediate-style.js +22 -0
- package/dist/trace-viewer/util/use-immediate-style.js.map +1 -0
- package/dist/trace-viewer/util/use-streaming-spans.d.ts +8 -0
- package/dist/trace-viewer/util/use-streaming-spans.d.ts.map +1 -0
- package/dist/trace-viewer/util/use-streaming-spans.js +332 -0
- package/dist/trace-viewer/util/use-streaming-spans.js.map +1 -0
- package/dist/trace-viewer/util/use-trackpad-zoom.d.ts +6 -0
- package/dist/trace-viewer/util/use-trackpad-zoom.d.ts.map +1 -0
- package/dist/trace-viewer/util/use-trackpad-zoom.jsx +38 -0
- package/dist/trace-viewer/util/use-trackpad-zoom.jsx.map +1 -0
- package/dist/trace-viewer/worker.d.ts +2 -0
- package/dist/trace-viewer/worker.d.ts.map +1 -0
- package/dist/trace-viewer/worker.js +107 -0
- package/dist/trace-viewer/worker.js.map +1 -0
- package/dist/workflow-trace-view.d.ts +12 -0
- package/dist/workflow-trace-view.d.ts.map +1 -0
- package/dist/workflow-trace-view.jsx +129 -0
- package/dist/workflow-trace-view.jsx.map +1 -0
- package/dist/workflow-traces/event-colors.d.ts +31 -0
- package/dist/workflow-traces/event-colors.d.ts.map +1 -0
- package/dist/workflow-traces/event-colors.js +68 -0
- package/dist/workflow-traces/event-colors.js.map +1 -0
- package/dist/workflow-traces/trace-colors.d.ts +15 -0
- package/dist/workflow-traces/trace-colors.d.ts.map +1 -0
- package/dist/workflow-traces/trace-colors.js +89 -0
- package/dist/workflow-traces/trace-colors.js.map +1 -0
- package/dist/workflow-traces/trace-span-construction.d.ts +31 -0
- package/dist/workflow-traces/trace-span-construction.d.ts.map +1 -0
- package/dist/workflow-traces/trace-span-construction.js +173 -0
- package/dist/workflow-traces/trace-span-construction.js.map +1 -0
- package/dist/workflow-traces/trace-time-utils.d.ts +13 -0
- package/dist/workflow-traces/trace-time-utils.d.ts.map +1 -0
- package/dist/workflow-traces/trace-time-utils.js +34 -0
- package/dist/workflow-traces/trace-time-utils.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { getPaginationDisplay } from '../lib/utils';
|
|
4
|
+
import { cancelRun as cancelRunServerAction, fetchEvents, fetchEventsByCorrelationId, fetchHook, fetchHooks, fetchRun, fetchRuns, fetchStep, fetchSteps, readStreamServerAction, recreateRun as recreateRunServerAction, } from './workflow-server-actions';
|
|
5
|
+
const MAX_ITEMS = 1000;
|
|
6
|
+
const LIVE_POLL_LIMIT = 5;
|
|
7
|
+
const LIVE_UPDATE_INTERVAL_MS = 5000;
|
|
8
|
+
/**
|
|
9
|
+
* Helper to convert ServerActionError to WorkflowAPIError
|
|
10
|
+
*/
|
|
11
|
+
function createWorkflowAPIError(serverError) {
|
|
12
|
+
return new WorkflowAPIError(serverError.message, {
|
|
13
|
+
cause: serverError.cause,
|
|
14
|
+
request: serverError.request,
|
|
15
|
+
layer: serverError.layer,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Gets a user-facing error message from an error object.
|
|
20
|
+
* Handles both WorkflowAPIError and regular Error instances.
|
|
21
|
+
*/
|
|
22
|
+
export const getErrorMessage = (error) => {
|
|
23
|
+
if ('layer' in error && error.layer) {
|
|
24
|
+
if (error instanceof WorkflowAPIError) {
|
|
25
|
+
if (error.request?.status === 403) {
|
|
26
|
+
return 'Your current Vercel account does not have access to this data. Please use `vercel login` to log in, or use `vercel switch` to ensure you can access the correct team.';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// WorkflowAPIError already has user-facing messages
|
|
30
|
+
return error.message;
|
|
31
|
+
}
|
|
32
|
+
return error instanceof Error ? error.message : 'An error occurred';
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Helper to handle server action results and throw WorkflowAPIError on failure
|
|
36
|
+
*/
|
|
37
|
+
function unwrapServerActionResult(result) {
|
|
38
|
+
if (!result.success) {
|
|
39
|
+
if (!result.error) {
|
|
40
|
+
throw new WorkflowAPIError('Unknown error occurred', { layer: 'client' });
|
|
41
|
+
}
|
|
42
|
+
throw createWorkflowAPIError(result.error);
|
|
43
|
+
}
|
|
44
|
+
return result.data;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Error instance for API and server-side errors.
|
|
48
|
+
* `error.message` will be a user-facing error message, to be displayed in UI.
|
|
49
|
+
* `error.cause` will be a developer-facing error message, to be displayed in logs.
|
|
50
|
+
*
|
|
51
|
+
* If the error originates from an HTTP request made from a server action,
|
|
52
|
+
* these fields will be populated:
|
|
53
|
+
* - `error.request` will be a JSON-serializable object representing the request made.
|
|
54
|
+
* - `error.layer` will be 'API'
|
|
55
|
+
*
|
|
56
|
+
* If the error originates from inside the server action, or there's an error with
|
|
57
|
+
* calling the server action, these fields will be populated:
|
|
58
|
+
* - `error.layer` will be 'server'
|
|
59
|
+
*/
|
|
60
|
+
export class WorkflowAPIError extends Error {
|
|
61
|
+
request;
|
|
62
|
+
layer;
|
|
63
|
+
constructor(message, options) {
|
|
64
|
+
super(message, { cause: options?.cause });
|
|
65
|
+
this.name = 'WorkflowAPIError';
|
|
66
|
+
this.request = options?.request;
|
|
67
|
+
this.layer = options?.layer;
|
|
68
|
+
if (options?.cause instanceof Error) {
|
|
69
|
+
this.stack = `${this.stack}\nCaused by: ${options.cause.stack}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns a list of runs with pagination control
|
|
75
|
+
*/
|
|
76
|
+
export function useWorkflowRuns(env, params) {
|
|
77
|
+
const { workflowName, status, limit = 10, sortOrder = 'desc' } = params;
|
|
78
|
+
const [cursor, setCursor] = useState(undefined);
|
|
79
|
+
const [hasMore, setHasMore] = useState(false);
|
|
80
|
+
const [currentPage, setCurrentPage] = useState(0);
|
|
81
|
+
const [pageHistory, setPageHistory] = useState([
|
|
82
|
+
undefined,
|
|
83
|
+
]);
|
|
84
|
+
const [maxPagesVisited, setMaxPagesVisited] = useState(1);
|
|
85
|
+
// Store PageResult for each page
|
|
86
|
+
const [allPageResults, setAllPageResults] = useState(new Map([[0, { data: null, isLoading: true, error: null }]]));
|
|
87
|
+
// Cache for fetched pages - key is cursor (or 'initial' for first page)
|
|
88
|
+
const pageCache = useRef(new Map());
|
|
89
|
+
const fetchPage = useCallback(async (pageIndex, pageCursor, force = false) => {
|
|
90
|
+
const cacheKey = pageCursor ?? 'initial';
|
|
91
|
+
// Set loading state for this page
|
|
92
|
+
setAllPageResults((prev) => {
|
|
93
|
+
const newMap = new Map(prev);
|
|
94
|
+
newMap.set(pageIndex, {
|
|
95
|
+
data: prev.get(pageIndex)?.data ?? null,
|
|
96
|
+
isLoading: true,
|
|
97
|
+
error: null,
|
|
98
|
+
});
|
|
99
|
+
return newMap;
|
|
100
|
+
});
|
|
101
|
+
// Check cache first unless force reload
|
|
102
|
+
if (!force && pageCache.current.has(cacheKey)) {
|
|
103
|
+
const cached = pageCache.current.get(cacheKey);
|
|
104
|
+
if (cached) {
|
|
105
|
+
setAllPageResults((prev) => {
|
|
106
|
+
const newMap = new Map(prev);
|
|
107
|
+
newMap.set(pageIndex, {
|
|
108
|
+
data: cached.data,
|
|
109
|
+
isLoading: false,
|
|
110
|
+
error: null,
|
|
111
|
+
});
|
|
112
|
+
return newMap;
|
|
113
|
+
});
|
|
114
|
+
setCursor(cached.cursor);
|
|
115
|
+
setHasMore(cached.hasMore);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const serverResult = await fetchRuns(env, {
|
|
121
|
+
cursor: pageCursor,
|
|
122
|
+
sortOrder,
|
|
123
|
+
limit: limit,
|
|
124
|
+
workflowName,
|
|
125
|
+
status,
|
|
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' });
|
|
155
|
+
setAllPageResults((prev) => {
|
|
156
|
+
const newMap = new Map(prev);
|
|
157
|
+
newMap.set(pageIndex, {
|
|
158
|
+
data: prev.get(pageIndex)?.data ?? null,
|
|
159
|
+
isLoading: false,
|
|
160
|
+
error,
|
|
161
|
+
});
|
|
162
|
+
return newMap;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}, [env, workflowName, limit, sortOrder, status]);
|
|
166
|
+
// Initial load
|
|
167
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: Want to refetch first page on param change
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
fetchPage(0, undefined, true);
|
|
170
|
+
}, [fetchPage, sortOrder, env, limit, workflowName, status]);
|
|
171
|
+
const nextPage = useCallback(() => {
|
|
172
|
+
if (hasMore && cursor) {
|
|
173
|
+
const newPage = currentPage + 1;
|
|
174
|
+
setPageHistory((prev) => [...prev, cursor]);
|
|
175
|
+
setCurrentPage(newPage);
|
|
176
|
+
setMaxPagesVisited((prev) => Math.max(prev, newPage + 1));
|
|
177
|
+
// Initialize next page if not already loaded
|
|
178
|
+
if (!allPageResults.has(newPage)) {
|
|
179
|
+
setAllPageResults((prev) => {
|
|
180
|
+
const newMap = new Map(prev);
|
|
181
|
+
newMap.set(newPage, { data: null, isLoading: true, error: null });
|
|
182
|
+
return newMap;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
fetchPage(newPage, cursor);
|
|
186
|
+
}
|
|
187
|
+
}, [hasMore, cursor, fetchPage, currentPage, allPageResults]);
|
|
188
|
+
const previousPage = useCallback(() => {
|
|
189
|
+
if (currentPage > 0) {
|
|
190
|
+
const newPage = currentPage - 1;
|
|
191
|
+
const prevCursor = pageHistory[newPage];
|
|
192
|
+
setCurrentPage(newPage);
|
|
193
|
+
fetchPage(newPage, prevCursor);
|
|
194
|
+
}
|
|
195
|
+
}, [currentPage, pageHistory, fetchPage]);
|
|
196
|
+
const reload = useCallback(() => {
|
|
197
|
+
// Clear cache and results
|
|
198
|
+
pageCache.current.clear();
|
|
199
|
+
setAllPageResults(new Map([[0, { data: null, isLoading: true, error: null }]]));
|
|
200
|
+
// Reset to first page
|
|
201
|
+
setCurrentPage(0);
|
|
202
|
+
setPageHistory([undefined]);
|
|
203
|
+
setMaxPagesVisited(1);
|
|
204
|
+
// Force fetch first page
|
|
205
|
+
fetchPage(0, undefined, true);
|
|
206
|
+
}, [fetchPage]);
|
|
207
|
+
const currentPageResult = allPageResults.get(currentPage) ?? {
|
|
208
|
+
data: null,
|
|
209
|
+
isLoading: true,
|
|
210
|
+
error: null,
|
|
211
|
+
};
|
|
212
|
+
// Compute global error (any page has error)
|
|
213
|
+
const globalError = Array.from(allPageResults.values()).find((p) => p.error)?.error ?? null;
|
|
214
|
+
// Compute global loading (any page is loading)
|
|
215
|
+
const globalLoading = Array.from(allPageResults.values()).some((p) => p.isLoading);
|
|
216
|
+
const totalPages = hasMore ? currentPage + 2 : currentPage + 1;
|
|
217
|
+
const currentPageNumber = currentPage + 1;
|
|
218
|
+
// Only show "+" if we're on the last visited page AND there are more pages
|
|
219
|
+
const isOnLastVisitedPage = currentPageNumber === maxPagesVisited;
|
|
220
|
+
const showPlus = isOnLastVisitedPage && hasMore;
|
|
221
|
+
const pageInfo = getPaginationDisplay(currentPageNumber, maxPagesVisited, showPlus);
|
|
222
|
+
return {
|
|
223
|
+
data: currentPageResult,
|
|
224
|
+
allData: Array.from(allPageResults.values()),
|
|
225
|
+
error: globalError,
|
|
226
|
+
isLoading: globalLoading,
|
|
227
|
+
currentPage,
|
|
228
|
+
totalPages,
|
|
229
|
+
nextPage,
|
|
230
|
+
previousPage,
|
|
231
|
+
hasNextPage: hasMore,
|
|
232
|
+
hasPreviousPage: currentPage > 0,
|
|
233
|
+
reload,
|
|
234
|
+
pageInfo,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Returns a list of hooks with pagination control
|
|
239
|
+
*/
|
|
240
|
+
export function useWorkflowHooks(env, params) {
|
|
241
|
+
const { runId, limit = 10, sortOrder = 'desc' } = params;
|
|
242
|
+
const [cursor, setCursor] = useState(undefined);
|
|
243
|
+
const [hasMore, setHasMore] = useState(false);
|
|
244
|
+
const [currentPage, setCurrentPage] = useState(0);
|
|
245
|
+
const [pageHistory, setPageHistory] = useState([
|
|
246
|
+
undefined,
|
|
247
|
+
]);
|
|
248
|
+
const [maxPagesVisited, setMaxPagesVisited] = useState(1);
|
|
249
|
+
// Store PageResult for each page
|
|
250
|
+
const [allPageResults, setAllPageResults] = useState(new Map([[0, { data: null, isLoading: true, error: null }]]));
|
|
251
|
+
// Cache for fetched pages - key is cursor (or 'initial' for first page)
|
|
252
|
+
const pageCache = useRef(new Map());
|
|
253
|
+
const fetchPage = useCallback(async (pageIndex, pageCursor, force = false) => {
|
|
254
|
+
const cacheKey = pageCursor ?? 'initial';
|
|
255
|
+
// Set loading state for this page
|
|
256
|
+
setAllPageResults((prev) => {
|
|
257
|
+
const newMap = new Map(prev);
|
|
258
|
+
newMap.set(pageIndex, {
|
|
259
|
+
data: prev.get(pageIndex)?.data ?? null,
|
|
260
|
+
isLoading: true,
|
|
261
|
+
error: null,
|
|
262
|
+
});
|
|
263
|
+
return newMap;
|
|
264
|
+
});
|
|
265
|
+
// Check cache first unless force reload
|
|
266
|
+
if (!force && pageCache.current.has(cacheKey)) {
|
|
267
|
+
const cached = pageCache.current.get(cacheKey);
|
|
268
|
+
if (cached) {
|
|
269
|
+
setAllPageResults((prev) => {
|
|
270
|
+
const newMap = new Map(prev);
|
|
271
|
+
newMap.set(pageIndex, {
|
|
272
|
+
data: cached.data,
|
|
273
|
+
isLoading: false,
|
|
274
|
+
error: null,
|
|
275
|
+
});
|
|
276
|
+
return newMap;
|
|
277
|
+
});
|
|
278
|
+
setCursor(cached.cursor);
|
|
279
|
+
setHasMore(cached.hasMore);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
const serverResult = await fetchHooks(env, {
|
|
285
|
+
runId,
|
|
286
|
+
cursor: pageCursor,
|
|
287
|
+
sortOrder,
|
|
288
|
+
limit: limit,
|
|
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' });
|
|
318
|
+
setAllPageResults((prev) => {
|
|
319
|
+
const newMap = new Map(prev);
|
|
320
|
+
newMap.set(pageIndex, {
|
|
321
|
+
data: prev.get(pageIndex)?.data ?? null,
|
|
322
|
+
isLoading: false,
|
|
323
|
+
error,
|
|
324
|
+
});
|
|
325
|
+
return newMap;
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}, [env, runId, limit, sortOrder]);
|
|
329
|
+
// Initial load
|
|
330
|
+
useEffect(() => {
|
|
331
|
+
fetchPage(0, undefined);
|
|
332
|
+
}, [fetchPage]);
|
|
333
|
+
const nextPage = useCallback(() => {
|
|
334
|
+
if (hasMore && cursor) {
|
|
335
|
+
const newPage = currentPage + 1;
|
|
336
|
+
setPageHistory((prev) => [...prev, cursor]);
|
|
337
|
+
setCurrentPage(newPage);
|
|
338
|
+
setMaxPagesVisited((prev) => Math.max(prev, newPage + 1));
|
|
339
|
+
// Initialize next page if not already loaded
|
|
340
|
+
if (!allPageResults.has(newPage)) {
|
|
341
|
+
setAllPageResults((prev) => {
|
|
342
|
+
const newMap = new Map(prev);
|
|
343
|
+
newMap.set(newPage, { data: null, isLoading: true, error: null });
|
|
344
|
+
return newMap;
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
fetchPage(newPage, cursor);
|
|
348
|
+
}
|
|
349
|
+
}, [hasMore, cursor, fetchPage, currentPage, allPageResults]);
|
|
350
|
+
const previousPage = useCallback(() => {
|
|
351
|
+
if (currentPage > 0) {
|
|
352
|
+
const newPage = currentPage - 1;
|
|
353
|
+
const prevCursor = pageHistory[newPage];
|
|
354
|
+
setCurrentPage(newPage);
|
|
355
|
+
fetchPage(newPage, prevCursor);
|
|
356
|
+
}
|
|
357
|
+
}, [currentPage, pageHistory, fetchPage]);
|
|
358
|
+
const reload = useCallback(() => {
|
|
359
|
+
// Clear cache and results
|
|
360
|
+
pageCache.current.clear();
|
|
361
|
+
setAllPageResults(new Map([[0, { data: null, isLoading: true, error: null }]]));
|
|
362
|
+
// Reset to first page
|
|
363
|
+
setCurrentPage(0);
|
|
364
|
+
setPageHistory([undefined]);
|
|
365
|
+
setMaxPagesVisited(1);
|
|
366
|
+
// Force fetch first page
|
|
367
|
+
fetchPage(0, undefined, true);
|
|
368
|
+
}, [fetchPage]);
|
|
369
|
+
const currentPageResult = allPageResults.get(currentPage) ?? {
|
|
370
|
+
data: null,
|
|
371
|
+
isLoading: true,
|
|
372
|
+
error: null,
|
|
373
|
+
};
|
|
374
|
+
// Compute global error (any page has error)
|
|
375
|
+
const globalError = Array.from(allPageResults.values()).find((p) => p.error)?.error ?? null;
|
|
376
|
+
// Compute global loading (any page is loading)
|
|
377
|
+
const globalLoading = Array.from(allPageResults.values()).some((p) => p.isLoading);
|
|
378
|
+
const totalPages = hasMore ? currentPage + 2 : currentPage + 1;
|
|
379
|
+
const currentPageNumber = currentPage + 1;
|
|
380
|
+
// Only show "+" if we're on the last visited page AND there are more pages
|
|
381
|
+
const isOnLastVisitedPage = currentPageNumber === maxPagesVisited;
|
|
382
|
+
const showPlus = isOnLastVisitedPage && hasMore;
|
|
383
|
+
const pageInfo = getPaginationDisplay(currentPageNumber, maxPagesVisited, showPlus);
|
|
384
|
+
return {
|
|
385
|
+
data: currentPageResult,
|
|
386
|
+
allData: Array.from(allPageResults.values()),
|
|
387
|
+
error: globalError,
|
|
388
|
+
isLoading: globalLoading,
|
|
389
|
+
currentPage,
|
|
390
|
+
totalPages,
|
|
391
|
+
nextPage,
|
|
392
|
+
previousPage,
|
|
393
|
+
hasNextPage: hasMore,
|
|
394
|
+
hasPreviousPage: currentPage > 0,
|
|
395
|
+
reload,
|
|
396
|
+
pageInfo,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
// Helper function to exhaustively fetch steps
|
|
400
|
+
async function fetchAllSteps(env, runId) {
|
|
401
|
+
let stepsData = [];
|
|
402
|
+
let stepsCursor;
|
|
403
|
+
while (true) {
|
|
404
|
+
const serverResult = await fetchSteps(env, runId, {
|
|
405
|
+
cursor: stepsCursor,
|
|
406
|
+
sortOrder: 'asc',
|
|
407
|
+
limit: 100,
|
|
408
|
+
});
|
|
409
|
+
const result = unwrapServerActionResult(serverResult);
|
|
410
|
+
stepsData = [...stepsData, ...result.data];
|
|
411
|
+
if (!result.hasMore || !result.cursor || stepsData.length >= MAX_ITEMS) {
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
stepsCursor = result.cursor;
|
|
415
|
+
}
|
|
416
|
+
return { data: stepsData, cursor: stepsCursor };
|
|
417
|
+
}
|
|
418
|
+
// Helper function to exhaustively fetch hooks
|
|
419
|
+
async function fetchAllHooks(env, runId) {
|
|
420
|
+
let hooksData = [];
|
|
421
|
+
let hooksCursor;
|
|
422
|
+
while (true) {
|
|
423
|
+
const serverResult = await fetchHooks(env, {
|
|
424
|
+
runId,
|
|
425
|
+
cursor: hooksCursor,
|
|
426
|
+
sortOrder: 'asc',
|
|
427
|
+
limit: 100,
|
|
428
|
+
});
|
|
429
|
+
const result = unwrapServerActionResult(serverResult);
|
|
430
|
+
hooksData = [...hooksData, ...result.data];
|
|
431
|
+
if (!result.hasMore || !result.cursor || hooksData.length >= MAX_ITEMS) {
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
hooksCursor = result.cursor;
|
|
435
|
+
}
|
|
436
|
+
return { data: hooksData, cursor: hooksCursor };
|
|
437
|
+
}
|
|
438
|
+
// Helper function to exhaustively fetch events
|
|
439
|
+
async function fetchAllEvents(env, runId) {
|
|
440
|
+
let eventsData = [];
|
|
441
|
+
let eventsCursor;
|
|
442
|
+
while (true) {
|
|
443
|
+
const serverResult = await fetchEvents(env, runId, {
|
|
444
|
+
cursor: eventsCursor,
|
|
445
|
+
sortOrder: 'asc',
|
|
446
|
+
limit: 1000,
|
|
447
|
+
});
|
|
448
|
+
const result = unwrapServerActionResult(serverResult);
|
|
449
|
+
eventsData = [...eventsData, ...result.data];
|
|
450
|
+
if (!result.hasMore || !result.cursor || eventsData.length >= MAX_ITEMS) {
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
eventsCursor = result.cursor;
|
|
454
|
+
}
|
|
455
|
+
return { data: eventsData, cursor: eventsCursor };
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Returns (and keeps up-to-date) all data related to a run.
|
|
459
|
+
* Items returned will _not_ have resolved data (like input/output values).
|
|
460
|
+
*/
|
|
461
|
+
export function useWorkflowTraceViewerData(env, runId, options = {}) {
|
|
462
|
+
const { live = false } = options;
|
|
463
|
+
const [run, setRun] = useState(null);
|
|
464
|
+
const [steps, setSteps] = useState([]);
|
|
465
|
+
const [hooks, setHooks] = useState([]);
|
|
466
|
+
const [events, setEvents] = useState([]);
|
|
467
|
+
const [loading, setLoading] = useState(true);
|
|
468
|
+
const [error, setError] = useState(null);
|
|
469
|
+
const [stepsCursor, setStepsCursor] = useState();
|
|
470
|
+
const [hooksCursor, setHooksCursor] = useState();
|
|
471
|
+
const [eventsCursor, setEventsCursor] = useState();
|
|
472
|
+
const isFetchingRef = useRef(false);
|
|
473
|
+
const [initialLoadCompleted, setInitialLoadCompleted] = useState(false);
|
|
474
|
+
// Fetch all data for a run
|
|
475
|
+
const fetchAllData = useCallback(async () => {
|
|
476
|
+
if (isFetchingRef.current) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
isFetchingRef.current = true;
|
|
480
|
+
setLoading(true);
|
|
481
|
+
setError(null);
|
|
482
|
+
try {
|
|
483
|
+
// Fetch run
|
|
484
|
+
const runServerResult = await fetchRun(env, runId);
|
|
485
|
+
const runData = unwrapServerActionResult(runServerResult);
|
|
486
|
+
setRun(runData);
|
|
487
|
+
// TODO: Do these in parallel
|
|
488
|
+
// Fetch steps exhaustively
|
|
489
|
+
const stepsResult = await fetchAllSteps(env, runId);
|
|
490
|
+
setSteps(stepsResult.data);
|
|
491
|
+
setStepsCursor(stepsResult.cursor);
|
|
492
|
+
// Fetch hooks exhaustively
|
|
493
|
+
const hooksResult = await fetchAllHooks(env, runId);
|
|
494
|
+
setHooks(hooksResult.data);
|
|
495
|
+
setHooksCursor(hooksResult.cursor);
|
|
496
|
+
// Fetch events exhaustively
|
|
497
|
+
const eventsResult = await fetchAllEvents(env, runId);
|
|
498
|
+
setEvents(eventsResult.data);
|
|
499
|
+
setEventsCursor(eventsResult.cursor);
|
|
500
|
+
}
|
|
501
|
+
catch (err) {
|
|
502
|
+
const error = err instanceof WorkflowAPIError
|
|
503
|
+
? err
|
|
504
|
+
: err instanceof Error
|
|
505
|
+
? new WorkflowAPIError(err.message, { cause: err, layer: 'client' })
|
|
506
|
+
: new WorkflowAPIError(String(err), { layer: 'client' });
|
|
507
|
+
setError(error);
|
|
508
|
+
}
|
|
509
|
+
finally {
|
|
510
|
+
setLoading(false);
|
|
511
|
+
isFetchingRef.current = false;
|
|
512
|
+
setInitialLoadCompleted(true);
|
|
513
|
+
}
|
|
514
|
+
}, [env, runId]);
|
|
515
|
+
// Helper to merge steps by ID
|
|
516
|
+
const mergeSteps = useCallback((prev, newData) => {
|
|
517
|
+
const combined = [...prev, ...newData];
|
|
518
|
+
const uniqueById = new Map(combined.map((s) => [s.stepId, s]));
|
|
519
|
+
return Array.from(uniqueById.values());
|
|
520
|
+
}, []);
|
|
521
|
+
// Helper to merge hooks by ID
|
|
522
|
+
const mergeHooks = useCallback((prev, newData) => {
|
|
523
|
+
const combined = [...prev, ...newData];
|
|
524
|
+
const uniqueById = new Map(combined.map((h) => [h.hookId, h]));
|
|
525
|
+
return Array.from(uniqueById.values());
|
|
526
|
+
}, []);
|
|
527
|
+
// Helper to merge events by ID
|
|
528
|
+
const mergeEvents = useCallback((prev, newData) => {
|
|
529
|
+
const combined = [...prev, ...newData];
|
|
530
|
+
const uniqueById = new Map(combined.map((e) => [e.eventId, e]));
|
|
531
|
+
return Array.from(uniqueById.values());
|
|
532
|
+
}, []);
|
|
533
|
+
const pollRun = useCallback(async () => {
|
|
534
|
+
if (run?.completedAt) {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
const serverResult = await fetchRun(env, runId);
|
|
538
|
+
const result = unwrapServerActionResult(serverResult);
|
|
539
|
+
setRun(result);
|
|
540
|
+
return true;
|
|
541
|
+
}, [env, runId, run?.completedAt]);
|
|
542
|
+
// Poll for new steps
|
|
543
|
+
const pollSteps = useCallback(async () => {
|
|
544
|
+
const serverResult = await fetchSteps(env, runId, {
|
|
545
|
+
cursor: stepsCursor,
|
|
546
|
+
sortOrder: 'asc',
|
|
547
|
+
limit: LIVE_POLL_LIMIT,
|
|
548
|
+
});
|
|
549
|
+
const result = unwrapServerActionResult(serverResult);
|
|
550
|
+
if (result.data.length > 0) {
|
|
551
|
+
setSteps((prev) => mergeSteps(prev, result.data));
|
|
552
|
+
if (result.cursor) {
|
|
553
|
+
setStepsCursor(result.cursor);
|
|
554
|
+
}
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
557
|
+
return false;
|
|
558
|
+
}, [env, runId, stepsCursor, mergeSteps]);
|
|
559
|
+
// Poll for new hooks
|
|
560
|
+
const pollHooks = useCallback(async () => {
|
|
561
|
+
const serverResult = await fetchHooks(env, {
|
|
562
|
+
runId,
|
|
563
|
+
cursor: hooksCursor,
|
|
564
|
+
sortOrder: 'asc',
|
|
565
|
+
limit: LIVE_POLL_LIMIT,
|
|
566
|
+
});
|
|
567
|
+
const result = unwrapServerActionResult(serverResult);
|
|
568
|
+
if (result.data.length > 0) {
|
|
569
|
+
setHooks((prev) => mergeHooks(prev, result.data));
|
|
570
|
+
if (result.cursor) {
|
|
571
|
+
setHooksCursor(result.cursor);
|
|
572
|
+
}
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
return false;
|
|
576
|
+
}, [env, runId, hooksCursor, mergeHooks]);
|
|
577
|
+
// Poll for new events
|
|
578
|
+
const pollEvents = useCallback(async () => {
|
|
579
|
+
const serverResult = await fetchEvents(env, runId, {
|
|
580
|
+
cursor: eventsCursor,
|
|
581
|
+
sortOrder: 'asc',
|
|
582
|
+
limit: LIVE_POLL_LIMIT,
|
|
583
|
+
});
|
|
584
|
+
const result = unwrapServerActionResult(serverResult);
|
|
585
|
+
if (result.data.length > 0) {
|
|
586
|
+
setEvents((prev) => mergeEvents(prev, result.data));
|
|
587
|
+
if (result.cursor) {
|
|
588
|
+
setEventsCursor(result.cursor);
|
|
589
|
+
}
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
return false;
|
|
593
|
+
}, [env, runId, eventsCursor, mergeEvents]);
|
|
594
|
+
// Update function for live polling
|
|
595
|
+
const update = useCallback(async () => {
|
|
596
|
+
if (isFetchingRef.current || !initialLoadCompleted) {
|
|
597
|
+
return { foundNewItems: false };
|
|
598
|
+
}
|
|
599
|
+
let foundNewItems = false;
|
|
600
|
+
try {
|
|
601
|
+
const [_, stepsUpdated, hooksUpdated, eventsUpdated] = await Promise.all([
|
|
602
|
+
pollRun(),
|
|
603
|
+
pollSteps(),
|
|
604
|
+
pollHooks(),
|
|
605
|
+
pollEvents(),
|
|
606
|
+
]);
|
|
607
|
+
foundNewItems = stepsUpdated || hooksUpdated || eventsUpdated;
|
|
608
|
+
}
|
|
609
|
+
catch (err) {
|
|
610
|
+
console.error('Update error:', err);
|
|
611
|
+
}
|
|
612
|
+
return { foundNewItems };
|
|
613
|
+
}, [pollSteps, pollHooks, pollEvents, initialLoadCompleted, pollRun]);
|
|
614
|
+
// Initial load
|
|
615
|
+
useEffect(() => {
|
|
616
|
+
fetchAllData();
|
|
617
|
+
}, [fetchAllData]);
|
|
618
|
+
// Live polling
|
|
619
|
+
useEffect(() => {
|
|
620
|
+
if (!live || !initialLoadCompleted || run?.completedAt) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
const interval = setInterval(() => {
|
|
624
|
+
update();
|
|
625
|
+
}, LIVE_UPDATE_INTERVAL_MS);
|
|
626
|
+
return () => clearInterval(interval);
|
|
627
|
+
}, [live, initialLoadCompleted, update, run?.completedAt]);
|
|
628
|
+
return {
|
|
629
|
+
run: run ?? {},
|
|
630
|
+
steps,
|
|
631
|
+
hooks,
|
|
632
|
+
events,
|
|
633
|
+
loading,
|
|
634
|
+
error,
|
|
635
|
+
update,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
// Helper function to fetch resource and get correlation ID
|
|
639
|
+
async function fetchResourceWithCorrelationId(env, resource, resourceId, options = {}) {
|
|
640
|
+
let resourceData;
|
|
641
|
+
let correlationId;
|
|
642
|
+
const resolveData = options.resolveData ?? 'all';
|
|
643
|
+
if (resource === 'run') {
|
|
644
|
+
const serverResult = await fetchRun(env, resourceId, resolveData);
|
|
645
|
+
resourceData = unwrapServerActionResult(serverResult);
|
|
646
|
+
correlationId = resourceData.runId;
|
|
647
|
+
}
|
|
648
|
+
else if (resource === 'step') {
|
|
649
|
+
const { runId } = options;
|
|
650
|
+
if (!runId) {
|
|
651
|
+
throw new WorkflowAPIError('runId is required for step resource', {
|
|
652
|
+
layer: 'client',
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
const serverResult = await fetchStep(env, runId, resourceId, resolveData);
|
|
656
|
+
resourceData = unwrapServerActionResult(serverResult);
|
|
657
|
+
correlationId = resourceData.stepId;
|
|
658
|
+
}
|
|
659
|
+
else if (resource === 'hook') {
|
|
660
|
+
const serverResult = await fetchHook(env, resourceId, resolveData);
|
|
661
|
+
resourceData = unwrapServerActionResult(serverResult);
|
|
662
|
+
correlationId = resourceData.hookId;
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
throw new WorkflowAPIError(`Unknown resource type: ${resource}`, {
|
|
666
|
+
layer: 'client',
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
return { data: resourceData, correlationId };
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Returns (and keeps up-to-date) data inherent to a specific run/step/hook,
|
|
673
|
+
* resolving input/output/metadata, AND loading all related events with full event data.
|
|
674
|
+
*/
|
|
675
|
+
export function useWorkflowResourceData(env, resource, resourceId, options = {}) {
|
|
676
|
+
const { refreshInterval = 0, runId } = options;
|
|
677
|
+
const [data, setData] = useState(null);
|
|
678
|
+
// const [events, setEvents] = useState<Event[]>([]);
|
|
679
|
+
const [loading, setLoading] = useState(true);
|
|
680
|
+
const [error, setError] = useState(null);
|
|
681
|
+
const fetchData = useCallback(async () => {
|
|
682
|
+
setLoading(true);
|
|
683
|
+
setData(null);
|
|
684
|
+
setError(null);
|
|
685
|
+
if (resource === 'sleep') {
|
|
686
|
+
const events = await fetchEventsByCorrelationId(env, resourceId, {
|
|
687
|
+
sortOrder: 'asc',
|
|
688
|
+
limit: 100,
|
|
689
|
+
withData: true,
|
|
690
|
+
});
|
|
691
|
+
const eventsData = unwrapServerActionResult(events);
|
|
692
|
+
const waitStartEvent = eventsData.data.find((event) => event.eventType === 'wait_created');
|
|
693
|
+
if (waitStartEvent) {
|
|
694
|
+
setData({
|
|
695
|
+
waitId: waitStartEvent.correlationId,
|
|
696
|
+
runId: waitStartEvent.runId,
|
|
697
|
+
createdAt: waitStartEvent.createdAt,
|
|
698
|
+
resumeAt: waitStartEvent.eventData.resumeAt,
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
try {
|
|
704
|
+
// Fetch resource with full data
|
|
705
|
+
const { data: resourceData } = await fetchResourceWithCorrelationId(env, resource, resourceId, {
|
|
706
|
+
runId,
|
|
707
|
+
});
|
|
708
|
+
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
|
+
}
|
|
720
|
+
catch (err) {
|
|
721
|
+
const error = err instanceof WorkflowAPIError
|
|
722
|
+
? err
|
|
723
|
+
: err instanceof Error
|
|
724
|
+
? new WorkflowAPIError(err.message, { cause: err, layer: 'client' })
|
|
725
|
+
: new WorkflowAPIError(String(err), { layer: 'client' });
|
|
726
|
+
setError(error);
|
|
727
|
+
}
|
|
728
|
+
finally {
|
|
729
|
+
setLoading(false);
|
|
730
|
+
}
|
|
731
|
+
}, [env, resource, resourceId, runId]);
|
|
732
|
+
// Initial load
|
|
733
|
+
useEffect(() => {
|
|
734
|
+
fetchData();
|
|
735
|
+
}, [fetchData]);
|
|
736
|
+
// Refresh interval
|
|
737
|
+
useEffect(() => {
|
|
738
|
+
if (!refreshInterval || refreshInterval <= 0) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const interval = setInterval(fetchData, refreshInterval);
|
|
742
|
+
return () => clearInterval(interval);
|
|
743
|
+
}, [refreshInterval, fetchData]);
|
|
744
|
+
return {
|
|
745
|
+
data,
|
|
746
|
+
// events,
|
|
747
|
+
loading,
|
|
748
|
+
error,
|
|
749
|
+
refresh: fetchData,
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Cancel a workflow run
|
|
754
|
+
*/
|
|
755
|
+
export async function cancelRun(env, runId) {
|
|
756
|
+
try {
|
|
757
|
+
const result = await cancelRunServerAction(env, runId);
|
|
758
|
+
unwrapServerActionResult(result);
|
|
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' });
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Start a new workflow run
|
|
770
|
+
*/
|
|
771
|
+
export async function recreateRun(env, runId) {
|
|
772
|
+
try {
|
|
773
|
+
const result = await recreateRunServerAction(env, runId);
|
|
774
|
+
return unwrapServerActionResult(result);
|
|
775
|
+
}
|
|
776
|
+
catch (err) {
|
|
777
|
+
console.error('Error starting run:', err);
|
|
778
|
+
if (err instanceof WorkflowAPIError) {
|
|
779
|
+
throw err;
|
|
780
|
+
}
|
|
781
|
+
throw new WorkflowAPIError(err instanceof Error ? err.message : 'Failed to start run', { cause: err, layer: 'client' });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
export async function readStream(env, streamId, startIndex) {
|
|
785
|
+
try {
|
|
786
|
+
const result = await readStreamServerAction(env, streamId, startIndex);
|
|
787
|
+
return unwrapServerActionResult(result);
|
|
788
|
+
}
|
|
789
|
+
catch (err) {
|
|
790
|
+
console.error('Error reading stream:', err);
|
|
791
|
+
if (err instanceof WorkflowAPIError) {
|
|
792
|
+
throw err;
|
|
793
|
+
}
|
|
794
|
+
throw new WorkflowAPIError(err instanceof Error ? err.message : 'Failed to read stream', { cause: err, layer: 'client' });
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
//# sourceMappingURL=workflow-api-client.js.map
|