@workflow/web-shared 5.0.0-beta.26 → 5.0.0-beta.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/new-trace-viewer/components/detail-panel-width.d.ts +34 -0
- package/dist/components/new-trace-viewer/components/detail-panel-width.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/detail-panel-width.js +67 -0
- package/dist/components/new-trace-viewer/components/detail-panel.d.ts +14 -0
- package/dist/components/new-trace-viewer/components/detail-panel.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/detail-panel.js +122 -0
- package/dist/components/new-trace-viewer/components/draggable-border.d.ts +31 -0
- package/dist/components/new-trace-viewer/components/draggable-border.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/draggable-border.js +140 -0
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +1 -1
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +2 -2
- package/dist/components/new-trace-viewer/components/pane-constants.d.ts +14 -0
- package/dist/components/new-trace-viewer/components/pane-constants.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/pane-constants.js +16 -0
- package/dist/components/new-trace-viewer/components/split-pane.d.ts +1 -4
- package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/split-pane.js +18 -16
- package/dist/components/new-trace-viewer/components/timeline.d.ts +2 -2
- package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +5 -2
- package/dist/components/new-trace-viewer/components/use-element-width.d.ts +8 -0
- package/dist/components/new-trace-viewer/components/use-element-width.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/use-element-width.js +25 -0
- package/dist/components/new-trace-viewer/context.d.ts +2 -0
- package/dist/components/new-trace-viewer/context.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/context.js +3 -2
- package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/trace-viewer.js +10 -90
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +38 -35
- package/dist/components/sidebar/attributes-block.d.ts +18 -7
- package/dist/components/sidebar/attributes-block.d.ts.map +1 -1
- package/dist/components/sidebar/attributes-block.js +76 -18
- package/dist/components/sidebar/events-list.d.ts +1 -1
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +9 -10
- package/dist/components/trace-viewer/components/map.d.ts +2 -2
- package/dist/components/trace-viewer/components/map.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/node.d.ts +7 -7
- package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
- package/dist/components/ui/collapsible.d.ts +46 -0
- package/dist/components/ui/collapsible.d.ts.map +1 -0
- package/dist/components/ui/collapsible.js +66 -0
- package/dist/components/ui/data-inspector.js +2 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/lib/hydration.d.ts +12 -2
- package/dist/lib/hydration.d.ts.map +1 -1
- package/dist/lib/hydration.js +41 -18
- package/package.json +6 -6
- package/src/components/new-trace-viewer/components/detail-panel-width.test.ts +123 -0
- package/src/components/new-trace-viewer/components/detail-panel-width.ts +76 -0
- package/src/components/new-trace-viewer/components/detail-panel.tsx +272 -0
- package/src/components/new-trace-viewer/components/draggable-border.tsx +216 -0
- package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +1 -1
- package/src/components/new-trace-viewer/components/pane-constants.ts +19 -0
- package/src/components/new-trace-viewer/components/split-pane.tsx +44 -55
- package/src/components/new-trace-viewer/components/trace-viewer-skeleton.tsx +4 -1
- package/src/components/new-trace-viewer/components/use-element-width.ts +29 -0
- package/src/components/new-trace-viewer/context.tsx +4 -1
- package/src/components/new-trace-viewer/trace-viewer.tsx +18 -199
- package/src/components/sidebar/attribute-panel.tsx +106 -105
- package/src/components/sidebar/attributes-block.tsx +170 -50
- package/src/components/sidebar/events-list.tsx +107 -92
- package/src/components/ui/collapsible.tsx +219 -0
- package/src/index.ts +8 -7
- package/src/lib/hydration.ts +48 -25
- package/dist/components/sidebar/detail-card.d.ts +0 -13
- package/dist/components/sidebar/detail-card.d.ts.map +0 -1
- package/dist/components/sidebar/detail-card.js +0 -36
- package/src/components/sidebar/detail-card.tsx +0 -143
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { Event } from '@workflow/world';
|
|
4
4
|
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getEventDataRefFields,
|
|
7
|
+
hasEncryptedFields,
|
|
8
|
+
isExpiredMarker,
|
|
9
|
+
} from '../../lib/hydration';
|
|
10
|
+
import {
|
|
11
|
+
Collapsible,
|
|
12
|
+
CollapsibleContent,
|
|
13
|
+
CollapsibleRoot,
|
|
14
|
+
CollapsibleTrigger,
|
|
15
|
+
} from '../ui/collapsible';
|
|
6
16
|
import { RunClickContext, StreamClickContext } from '../ui/data-inspector';
|
|
7
17
|
import { ErrorCard } from '../ui/error-card';
|
|
8
18
|
import { ErrorStackBlock, isStructuredError } from '../ui/error-stack-block';
|
|
@@ -10,7 +20,6 @@ import { Skeleton } from '../ui/skeleton';
|
|
|
10
20
|
import { TimestampTooltip } from '../ui/timestamp-tooltip';
|
|
11
21
|
import { AttrSetEventBlock } from './attributes-block';
|
|
12
22
|
import { CopyableDataBlock, EncryptedDataBlock } from './copyable-data-block';
|
|
13
|
-
import { DetailCard } from './detail-card';
|
|
14
23
|
|
|
15
24
|
/**
|
|
16
25
|
* Event types whose eventData contains an error field with a StructuredError.
|
|
@@ -143,20 +152,9 @@ function EventItem({
|
|
|
143
152
|
const displayPayload = isLoading ? loadedData : mergedDisplay;
|
|
144
153
|
|
|
145
154
|
return (
|
|
146
|
-
<
|
|
155
|
+
<CollapsibleRoot
|
|
147
156
|
variant="card"
|
|
148
|
-
|
|
149
|
-
summary={
|
|
150
|
-
<div className="flex w-full items-center justify-between gap-3">
|
|
151
|
-
<span className="text-gray-1000 text-label-12 font-mono">
|
|
152
|
-
{event.eventType}
|
|
153
|
-
</span>
|
|
154
|
-
<span className="shrink-0 text-label-13 text-gray-900">
|
|
155
|
-
{displayedCreatedAtTime}
|
|
156
|
-
</span>
|
|
157
|
-
</div>
|
|
158
|
-
}
|
|
159
|
-
onToggle={
|
|
157
|
+
onOpenChange={
|
|
160
158
|
canHaveData
|
|
161
159
|
? (open) => {
|
|
162
160
|
if (open) handleExpand();
|
|
@@ -164,65 +162,79 @@ function EventItem({
|
|
|
164
162
|
: undefined
|
|
165
163
|
}
|
|
166
164
|
>
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
{formatEventTimestamp(occurredAt)}
|
|
175
|
-
</span>
|
|
176
|
-
</TimestampTooltip>
|
|
177
|
-
</div>
|
|
178
|
-
)}
|
|
179
|
-
<div className="flex items-center justify-between gap-2 py-2 px-3">
|
|
180
|
-
<span className="text-label-12 text-gray-900">Event ID</span>
|
|
181
|
-
<span className="max-w-[70%] truncate text-right text-label-12 font-mono">
|
|
182
|
-
{event.eventId}
|
|
165
|
+
<CollapsibleTrigger className="px-3 py-2">
|
|
166
|
+
<div className="flex w-full items-center justify-between gap-3">
|
|
167
|
+
<span className="text-gray-1000 text-label-12 font-mono">
|
|
168
|
+
{event.eventType}
|
|
169
|
+
</span>
|
|
170
|
+
<span className="shrink-0 text-label-13 text-gray-900">
|
|
171
|
+
{displayedCreatedAtTime}
|
|
183
172
|
</span>
|
|
184
173
|
</div>
|
|
185
|
-
|
|
174
|
+
</CollapsibleTrigger>
|
|
175
|
+
<CollapsibleContent>
|
|
176
|
+
{/* Event attributes */}
|
|
177
|
+
<div className="flex flex-col bg-background-200 [&:has(+_*)]:border-b [&:has(+_*)]:border-gray-alpha-400">
|
|
178
|
+
{showSeparateEventOccurrenceTimestamps && occurredAt && (
|
|
179
|
+
<div className="flex items-center justify-between gap-2 py-2 px-3">
|
|
180
|
+
<span className="text-label-12 text-gray-900">Occurred</span>
|
|
181
|
+
<TimestampTooltip date={occurredAt}>
|
|
182
|
+
<span className="max-w-[70%] truncate text-right text-label-12 font-mono">
|
|
183
|
+
{formatEventTimestamp(occurredAt)}
|
|
184
|
+
</span>
|
|
185
|
+
</TimestampTooltip>
|
|
186
|
+
</div>
|
|
187
|
+
)}
|
|
186
188
|
<div className="flex items-center justify-between gap-2 py-2 px-3">
|
|
187
|
-
<span className="text-label-12 text-gray-900">
|
|
189
|
+
<span className="text-label-12 text-gray-900">Event ID</span>
|
|
188
190
|
<span className="max-w-[70%] truncate text-right text-label-12 font-mono">
|
|
189
|
-
{event.
|
|
191
|
+
{event.eventId}
|
|
190
192
|
</span>
|
|
191
193
|
</div>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
{event.correlationId && (
|
|
195
|
+
<div className="flex items-center justify-between gap-2 py-2 px-3">
|
|
196
|
+
<span className="text-label-12 text-gray-900">
|
|
197
|
+
Correlation ID
|
|
198
|
+
</span>
|
|
199
|
+
<span className="max-w-[70%] truncate text-right text-label-12 font-mono">
|
|
200
|
+
{event.correlationId}
|
|
201
|
+
</span>
|
|
202
|
+
</div>
|
|
203
|
+
)}
|
|
201
204
|
</div>
|
|
202
|
-
)}
|
|
203
205
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
206
|
+
{/* Loading state */}
|
|
207
|
+
{isLoading && (
|
|
208
|
+
<div className="p-3">
|
|
209
|
+
<Skeleton className="h-4 w-[35%]" />
|
|
210
|
+
<Skeleton className="mt-2 h-4 w-[90%]" />
|
|
211
|
+
<Skeleton className="mt-2 h-4 w-[75%]" />
|
|
212
|
+
</div>
|
|
213
|
+
)}
|
|
212
214
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
215
|
+
{/* Error state */}
|
|
216
|
+
{loadError && (
|
|
217
|
+
<ErrorCard
|
|
218
|
+
title="Failed to load event data"
|
|
219
|
+
details={loadError}
|
|
220
|
+
className="mt-2"
|
|
221
|
+
/>
|
|
222
|
+
)}
|
|
223
|
+
|
|
224
|
+
{/* Event data */}
|
|
225
|
+
{displayPayload != null && (
|
|
226
|
+
<div className="[&>div]:border-none [&>div]:rounded-none">
|
|
227
|
+
<EventDataBlock eventType={event.eventType} data={displayPayload} />
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
</CollapsibleContent>
|
|
231
|
+
</CollapsibleRoot>
|
|
220
232
|
);
|
|
221
233
|
}
|
|
222
234
|
|
|
223
235
|
/**
|
|
224
236
|
* Check if an eventData object has only expired marker values in ref/payload
|
|
225
|
-
* fields for this event type (see {@link
|
|
237
|
+
* fields for this event type (see {@link getEventDataRefFields}). Other keys
|
|
226
238
|
* (e.g. `resumeAt`, `stepName`) are ignored.
|
|
227
239
|
*/
|
|
228
240
|
function hasOnlyExpiredFields(data: unknown, eventType: string): boolean {
|
|
@@ -230,7 +242,7 @@ function hasOnlyExpiredFields(data: unknown, eventType: string): boolean {
|
|
|
230
242
|
return false;
|
|
231
243
|
}
|
|
232
244
|
const record = data as Record<string, unknown>;
|
|
233
|
-
const refKeys =
|
|
245
|
+
const refKeys = getEventDataRefFields(eventType);
|
|
234
246
|
const presentKeys = refKeys.filter((k) => k in record);
|
|
235
247
|
return (
|
|
236
248
|
presentKeys.length > 0 &&
|
|
@@ -341,41 +353,44 @@ export function EventsList({
|
|
|
341
353
|
const hasEvents = sortedEvents.length > 0 && !error;
|
|
342
354
|
|
|
343
355
|
if (!hasEvents && !isLoading) {
|
|
344
|
-
return <
|
|
356
|
+
return <Collapsible label="Events" disabled />;
|
|
345
357
|
}
|
|
346
358
|
|
|
347
359
|
return (
|
|
348
360
|
<RunClickContext.Provider value={onRunClick}>
|
|
349
361
|
<StreamClickContext.Provider value={onStreamClick}>
|
|
350
|
-
<
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
362
|
+
<CollapsibleRoot defaultOpen>
|
|
363
|
+
<CollapsibleTrigger>Events</CollapsibleTrigger>
|
|
364
|
+
<CollapsibleContent className="mb-0">
|
|
365
|
+
{isLoading ? (
|
|
366
|
+
<div className="flex flex-col -mx-4">
|
|
367
|
+
{[0, 1, 2].map((i) => (
|
|
368
|
+
<div
|
|
369
|
+
key={i}
|
|
370
|
+
className="flex items-center justify-between gap-3 bg-background-200 px-4 py-2"
|
|
371
|
+
>
|
|
372
|
+
<Skeleton className="h-4 w-32 rounded" />
|
|
373
|
+
<Skeleton className="h-3 w-16 rounded" />
|
|
374
|
+
</div>
|
|
375
|
+
))}
|
|
376
|
+
</div>
|
|
377
|
+
) : (
|
|
378
|
+
<div className="flex flex-col -mx-4">
|
|
379
|
+
{sortedEvents.map((event) => (
|
|
380
|
+
<EventItem
|
|
381
|
+
key={event.eventId}
|
|
382
|
+
event={event}
|
|
383
|
+
onLoadEventData={onLoadEventData}
|
|
384
|
+
encryptionKey={encryptionKey}
|
|
385
|
+
showSeparateEventOccurrenceTimestamps={
|
|
386
|
+
showSeparateEventOccurrenceTimestamps
|
|
387
|
+
}
|
|
388
|
+
/>
|
|
389
|
+
))}
|
|
390
|
+
</div>
|
|
391
|
+
)}
|
|
392
|
+
</CollapsibleContent>
|
|
393
|
+
</CollapsibleRoot>
|
|
379
394
|
</StreamClickContext.Provider>
|
|
380
395
|
</RunClickContext.Provider>
|
|
381
396
|
);
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ChevronDown, ChevronRight } from 'lucide-react';
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
type SyntheticEvent,
|
|
8
|
+
useContext,
|
|
9
|
+
useState,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import { cn } from '../../lib/cn';
|
|
12
|
+
|
|
13
|
+
type CollapsibleVariant = 'section' | 'card';
|
|
14
|
+
|
|
15
|
+
type CollapsibleContextValue = {
|
|
16
|
+
variant: CollapsibleVariant;
|
|
17
|
+
disabled: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const CollapsibleContext = createContext<CollapsibleContextValue | null>(null);
|
|
21
|
+
|
|
22
|
+
function useCollapsibleContext(part: string): CollapsibleContextValue {
|
|
23
|
+
const context = useContext(CollapsibleContext);
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`<Collapsible${part}> must be rendered inside <CollapsibleRoot>`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return context;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type CollapsibleRootProps = {
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
variant?: CollapsibleVariant;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
defaultOpen?: boolean;
|
|
37
|
+
onOpenChange?: (open: boolean) => void;
|
|
38
|
+
className?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Low-level collapsible container. Use this together with `CollapsibleTrigger`
|
|
43
|
+
* and `CollapsibleContent` when you need to override the styling of individual
|
|
44
|
+
* parts. For the common case, prefer the all-in-one `Collapsible`.
|
|
45
|
+
*/
|
|
46
|
+
export function CollapsibleRoot({
|
|
47
|
+
children,
|
|
48
|
+
variant = 'section',
|
|
49
|
+
disabled = false,
|
|
50
|
+
defaultOpen = false,
|
|
51
|
+
onOpenChange,
|
|
52
|
+
className,
|
|
53
|
+
}: CollapsibleRootProps) {
|
|
54
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
55
|
+
|
|
56
|
+
const handleToggle = (event: SyntheticEvent<HTMLDetailsElement>) => {
|
|
57
|
+
// React bubbles the `toggle` event even though the native one doesn't, so
|
|
58
|
+
// a nested <details> (e.g. an event card inside the Events section)
|
|
59
|
+
// collapsing would otherwise flip this card. Only react to direct toggles.
|
|
60
|
+
if (event.target !== event.currentTarget) return;
|
|
61
|
+
const next = event.currentTarget.open;
|
|
62
|
+
setOpen(next);
|
|
63
|
+
onOpenChange?.(next);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const details = (
|
|
67
|
+
<details
|
|
68
|
+
data-slot="collapsible"
|
|
69
|
+
data-state={open ? 'open' : 'closed'}
|
|
70
|
+
open={open}
|
|
71
|
+
onToggle={disabled ? undefined : handleToggle}
|
|
72
|
+
className={cn(
|
|
73
|
+
variant === 'card'
|
|
74
|
+
? 'group/card border-gray-alpha-400 last:border-b'
|
|
75
|
+
: 'group',
|
|
76
|
+
className
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
<CollapsibleContext.Provider value={{ variant, disabled }}>
|
|
80
|
+
{children}
|
|
81
|
+
</CollapsibleContext.Provider>
|
|
82
|
+
</details>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (variant === 'card') {
|
|
86
|
+
return details;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<section className="-mx-4 border-t border-gray-alpha-400 px-4 py-2">
|
|
91
|
+
{details}
|
|
92
|
+
</section>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
type CollapsibleTriggerProps = {
|
|
97
|
+
children: ReactNode;
|
|
98
|
+
className?: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/** The clickable header of a `CollapsibleRoot`. */
|
|
102
|
+
export function CollapsibleTrigger({
|
|
103
|
+
children,
|
|
104
|
+
className,
|
|
105
|
+
}: CollapsibleTriggerProps) {
|
|
106
|
+
const { variant, disabled } = useCollapsibleContext('Trigger');
|
|
107
|
+
|
|
108
|
+
if (variant === 'card') {
|
|
109
|
+
return (
|
|
110
|
+
<summary
|
|
111
|
+
data-slot="collapsible-trigger"
|
|
112
|
+
className={cn(
|
|
113
|
+
'flex cursor-pointer list-none items-center gap-1.5 border-t border-gray-alpha-400 bg-background-200 px-3 py-4 hover:bg-gray-100 [&::-webkit-details-marker]:hidden',
|
|
114
|
+
className
|
|
115
|
+
)}
|
|
116
|
+
>
|
|
117
|
+
<ChevronRight
|
|
118
|
+
size={14}
|
|
119
|
+
className="shrink-0 text-gray-700 group-hover/card:text-gray-1000 group-open/card:rotate-90"
|
|
120
|
+
/>
|
|
121
|
+
{children}
|
|
122
|
+
</summary>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Shared row metrics keep every header the same height regardless of variant.
|
|
127
|
+
const row =
|
|
128
|
+
'flex h-9 items-center gap-2 -mx-2 px-2 text-heading-14 font-medium list-none [&::-webkit-details-marker]:hidden';
|
|
129
|
+
|
|
130
|
+
if (disabled) {
|
|
131
|
+
return (
|
|
132
|
+
<summary
|
|
133
|
+
data-slot="collapsible-trigger"
|
|
134
|
+
className={cn(row, 'pointer-events-none text-gray-700', className)}
|
|
135
|
+
>
|
|
136
|
+
<span className="min-w-0 flex-1">{children}</span>
|
|
137
|
+
</summary>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<summary
|
|
143
|
+
data-slot="collapsible-trigger"
|
|
144
|
+
className={cn(
|
|
145
|
+
row,
|
|
146
|
+
'group/trigger cursor-pointer rounded hover:bg-gray-alpha-100',
|
|
147
|
+
className
|
|
148
|
+
)}
|
|
149
|
+
>
|
|
150
|
+
<span className="relative isolate h-3.5 w-3.5 shrink-0 text-gray-700 group-hover/trigger:text-gray-1000">
|
|
151
|
+
<ChevronRight
|
|
152
|
+
size={14}
|
|
153
|
+
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-100 group-open:opacity-0"
|
|
154
|
+
/>
|
|
155
|
+
<ChevronDown
|
|
156
|
+
size={14}
|
|
157
|
+
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0 group-open:opacity-100"
|
|
158
|
+
/>
|
|
159
|
+
</span>
|
|
160
|
+
<span className="min-w-0 flex-1">{children}</span>
|
|
161
|
+
</summary>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
type CollapsibleContentProps = {
|
|
166
|
+
children: ReactNode;
|
|
167
|
+
className?: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/** The collapsible body of a `CollapsibleRoot`. */
|
|
171
|
+
export function CollapsibleContent({
|
|
172
|
+
children,
|
|
173
|
+
className,
|
|
174
|
+
}: CollapsibleContentProps) {
|
|
175
|
+
const { variant } = useCollapsibleContext('Content');
|
|
176
|
+
return (
|
|
177
|
+
<div
|
|
178
|
+
data-slot="collapsible-content"
|
|
179
|
+
className={cn(variant === 'section' && 'mt-2 mb-3', className)}
|
|
180
|
+
>
|
|
181
|
+
{children}
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
type CollapsibleProps = {
|
|
187
|
+
/** Header content shown in the trigger row. */
|
|
188
|
+
label: ReactNode;
|
|
189
|
+
/** Body content. Omit for a header-only (e.g. disabled or skeleton) row. */
|
|
190
|
+
children?: ReactNode;
|
|
191
|
+
disabled?: boolean;
|
|
192
|
+
defaultOpen?: boolean;
|
|
193
|
+
onOpenChange?: (open: boolean) => void;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* All-in-one collapsible section: a labeled header plus body content. This is
|
|
198
|
+
* the component to reach for in most cases. When you need to override the
|
|
199
|
+
* styling of a specific part, compose `CollapsibleRoot`, `CollapsibleTrigger`,
|
|
200
|
+
* and `CollapsibleContent` directly instead.
|
|
201
|
+
*/
|
|
202
|
+
export function Collapsible({
|
|
203
|
+
label,
|
|
204
|
+
children,
|
|
205
|
+
disabled,
|
|
206
|
+
defaultOpen,
|
|
207
|
+
onOpenChange,
|
|
208
|
+
}: CollapsibleProps) {
|
|
209
|
+
return (
|
|
210
|
+
<CollapsibleRoot
|
|
211
|
+
disabled={disabled}
|
|
212
|
+
defaultOpen={defaultOpen}
|
|
213
|
+
onOpenChange={onOpenChange}
|
|
214
|
+
>
|
|
215
|
+
<CollapsibleTrigger>{label}</CollapsibleTrigger>
|
|
216
|
+
{children != null && <CollapsibleContent>{children}</CollapsibleContent>}
|
|
217
|
+
</CollapsibleRoot>
|
|
218
|
+
);
|
|
219
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -14,13 +14,6 @@ export {
|
|
|
14
14
|
waitEventsToWaitEntity,
|
|
15
15
|
} from './components/workflow-traces/trace-span-construction';
|
|
16
16
|
export type { EventAnalysis } from './lib/event-analysis';
|
|
17
|
-
export {
|
|
18
|
-
parseExactWorkflowSearchId,
|
|
19
|
-
looksLikeWorkflowIdSearchInput,
|
|
20
|
-
type ExactWorkflowSearchId,
|
|
21
|
-
type ExactWorkflowSearchIdKind,
|
|
22
|
-
type ExactIdSearchResult,
|
|
23
|
-
} from './lib/exact-event-search-id';
|
|
24
17
|
export {
|
|
25
18
|
analyzeEvents,
|
|
26
19
|
hasPendingHooksFromEvents,
|
|
@@ -40,6 +33,13 @@ export {
|
|
|
40
33
|
materializeSteps,
|
|
41
34
|
materializeWaits,
|
|
42
35
|
} from './lib/event-materialization';
|
|
36
|
+
export {
|
|
37
|
+
type ExactIdSearchResult,
|
|
38
|
+
type ExactWorkflowSearchId,
|
|
39
|
+
type ExactWorkflowSearchIdKind,
|
|
40
|
+
looksLikeWorkflowIdSearchInput,
|
|
41
|
+
parseExactWorkflowSearchId,
|
|
42
|
+
} from './lib/exact-event-search-id';
|
|
43
43
|
export type { Revivers, StreamRef } from './lib/hydration';
|
|
44
44
|
export {
|
|
45
45
|
CLASS_INSTANCE_REF_TYPE,
|
|
@@ -49,6 +49,7 @@ export {
|
|
|
49
49
|
getWebRevivers,
|
|
50
50
|
hasEncryptedFields,
|
|
51
51
|
hydrateResourceIO,
|
|
52
|
+
hydrateResourceIOAsync,
|
|
52
53
|
hydrateResourceIOWithKey,
|
|
53
54
|
isClassInstanceRef,
|
|
54
55
|
isEncryptedMarker,
|
package/src/lib/hydration.ts
CHANGED
|
@@ -18,6 +18,20 @@ import {
|
|
|
18
18
|
} from '@workflow/core/serialization-format';
|
|
19
19
|
import { EVENT_DATA_REF_FIELDS } from '@workflow/world';
|
|
20
20
|
|
|
21
|
+
const V4_EXTRA_EVENT_DATA_REF_FIELDS: Record<string, string[]> = {
|
|
22
|
+
run_started: ['input'],
|
|
23
|
+
step_started: ['input'],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function getEventDataRefFields(eventType: string): string[] {
|
|
27
|
+
return [
|
|
28
|
+
...new Set([
|
|
29
|
+
...(EVENT_DATA_REF_FIELDS[eventType] ?? []),
|
|
30
|
+
...(V4_EXTRA_EVENT_DATA_REF_FIELDS[eventType] ?? []),
|
|
31
|
+
]),
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
// Re-export types and utilities that consumers need
|
|
22
36
|
export {
|
|
23
37
|
CLASS_INSTANCE_REF_TYPE,
|
|
@@ -446,7 +460,7 @@ function replaceEncryptedAndExpiredWithMarkers<T>(resource: T): T {
|
|
|
446
460
|
if (result.eventData && typeof result.eventData === 'object') {
|
|
447
461
|
const eventType =
|
|
448
462
|
typeof result.eventType === 'string' ? result.eventType : '';
|
|
449
|
-
const refKeys =
|
|
463
|
+
const refKeys = getEventDataRefFields(eventType);
|
|
450
464
|
const ed = { ...(result.eventData as Record<string, unknown>) };
|
|
451
465
|
for (const key of refKeys) {
|
|
452
466
|
if (key in ed) {
|
|
@@ -465,17 +479,31 @@ function replaceEncryptedAndExpiredWithMarkers<T>(resource: T): T {
|
|
|
465
479
|
* When a key is provided, encrypted fields are decrypted before hydration.
|
|
466
480
|
* This is the async version used when the user clicks "Decrypt" in the web UI.
|
|
467
481
|
*
|
|
468
|
-
* Handles both top-level fields (input, output, metadata) and nested
|
|
469
|
-
*
|
|
482
|
+
* Handles both top-level fields (input, output, metadata) and nested eventData
|
|
483
|
+
* payload fields for that event type.
|
|
470
484
|
*/
|
|
471
485
|
export async function hydrateResourceIOWithKey<T>(
|
|
472
486
|
resource: T,
|
|
473
487
|
key: Uint8Array
|
|
488
|
+
): Promise<T> {
|
|
489
|
+
return hydrateResourceIOAsync(resource, key);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Async hydration for web display.
|
|
494
|
+
*
|
|
495
|
+
* This follows the same resource-field mapping as {@link hydrateResourceIO},
|
|
496
|
+
* but can also inflate compressed browser payloads through the registered
|
|
497
|
+
* zstd WASM decoder. When a key is provided, encrypted fields are decrypted
|
|
498
|
+
* first and then inflated/hydrated.
|
|
499
|
+
*/
|
|
500
|
+
export async function hydrateResourceIOAsync<T>(
|
|
501
|
+
resource: T,
|
|
502
|
+
key?: Uint8Array
|
|
474
503
|
): Promise<T> {
|
|
475
504
|
const { hydrateDataWithKey } = await import(
|
|
476
505
|
'@workflow/core/serialization-format'
|
|
477
506
|
);
|
|
478
|
-
const { importKey } = await import('@workflow/core/encryption');
|
|
479
507
|
// Payloads may be zstd-compressed (the Web DecompressionStream has no zstd);
|
|
480
508
|
// register the WASM-backed browser decoder before hydrating. Idempotent and
|
|
481
509
|
// lazy — the WASM is only compiled when a zstd payload is actually decoded.
|
|
@@ -483,25 +511,24 @@ export async function hydrateResourceIOWithKey<T>(
|
|
|
483
511
|
'./zstd-browser-decoder.js'
|
|
484
512
|
);
|
|
485
513
|
ensureZstdDecoderRegistered();
|
|
486
|
-
const cryptoKey =
|
|
514
|
+
const cryptoKey = key
|
|
515
|
+
? await import('@workflow/core/encryption').then(({ importKey }) =>
|
|
516
|
+
importKey(key)
|
|
517
|
+
)
|
|
518
|
+
: undefined;
|
|
487
519
|
const revivers = getRevivers();
|
|
488
520
|
|
|
489
|
-
|
|
490
|
-
async function decryptField(
|
|
491
|
-
value: unknown,
|
|
492
|
-
rev: Revivers,
|
|
493
|
-
k: Awaited<ReturnType<typeof importKey>>
|
|
494
|
-
): Promise<unknown> {
|
|
521
|
+
async function hydrateField(value: unknown): Promise<unknown> {
|
|
495
522
|
// Already-hydrated: encrypted marker with stored bytes
|
|
496
523
|
if (isEncryptedMarker(value)) {
|
|
497
524
|
const raw = (value as any).__encryptedData as Uint8Array;
|
|
498
|
-
return hydrateDataWithKey(raw,
|
|
525
|
+
return cryptoKey ? hydrateDataWithKey(raw, revivers, cryptoKey) : value;
|
|
499
526
|
}
|
|
500
|
-
// Raw encrypted
|
|
527
|
+
// Raw Uint8Array: may be encrypted, compressed, or plain devalue.
|
|
501
528
|
if (value instanceof Uint8Array) {
|
|
502
|
-
return hydrateDataWithKey(value,
|
|
529
|
+
return hydrateDataWithKey(value, revivers, cryptoKey);
|
|
503
530
|
}
|
|
504
|
-
// Not
|
|
531
|
+
// Not serialized — return as-is.
|
|
505
532
|
return value;
|
|
506
533
|
}
|
|
507
534
|
|
|
@@ -511,29 +538,25 @@ export async function hydrateResourceIOWithKey<T>(
|
|
|
511
538
|
// Decrypt + hydrate top-level serialized fields (runs, steps, hooks)
|
|
512
539
|
for (const field of ['input', 'output', 'metadata', 'error']) {
|
|
513
540
|
if (field in result) {
|
|
514
|
-
result[field] = await
|
|
541
|
+
result[field] = await hydrateField(result[field]);
|
|
515
542
|
}
|
|
516
543
|
}
|
|
517
544
|
|
|
518
|
-
//
|
|
545
|
+
// Hydrate eventData subfields (events)
|
|
519
546
|
if (result.eventData && typeof result.eventData === 'object') {
|
|
520
547
|
const eventType =
|
|
521
548
|
typeof result.eventType === 'string' ? result.eventType : '';
|
|
522
|
-
const refKeys =
|
|
549
|
+
const refKeys = getEventDataRefFields(eventType);
|
|
523
550
|
const eventData = { ...(result.eventData as Record<string, unknown>) };
|
|
524
551
|
for (const field of refKeys) {
|
|
525
552
|
if (field in eventData) {
|
|
526
|
-
eventData[field] = await
|
|
527
|
-
eventData[field],
|
|
528
|
-
revivers,
|
|
529
|
-
cryptoKey
|
|
530
|
-
);
|
|
553
|
+
eventData[field] = await hydrateField(eventData[field]);
|
|
531
554
|
}
|
|
532
555
|
}
|
|
533
556
|
result.eventData = eventData;
|
|
534
557
|
}
|
|
535
558
|
|
|
536
|
-
return result as T;
|
|
559
|
+
return replaceEncryptedAndExpiredWithMarkers(result as T);
|
|
537
560
|
}
|
|
538
561
|
|
|
539
562
|
/**
|
|
@@ -552,7 +575,7 @@ export function hasEncryptedFields(resource: unknown): boolean {
|
|
|
552
575
|
|
|
553
576
|
if (r.eventData && typeof r.eventData === 'object') {
|
|
554
577
|
const eventType = typeof r.eventType === 'string' ? r.eventType : '';
|
|
555
|
-
const refKeys =
|
|
578
|
+
const refKeys = getEventDataRefFields(eventType);
|
|
556
579
|
const ed = r.eventData as Record<string, unknown>;
|
|
557
580
|
for (const key of refKeys) {
|
|
558
581
|
if (key in ed && isEncryptedMarker(ed[key])) return true;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
2
|
-
export declare function DetailCard({ summary, children, onToggle, disabled, defaultOpen, variant, trailing, summaryClassName, contentClassName, }: {
|
|
3
|
-
summary: ReactNode;
|
|
4
|
-
children?: ReactNode;
|
|
5
|
-
onToggle?: (open: boolean) => void;
|
|
6
|
-
disabled?: boolean;
|
|
7
|
-
defaultOpen?: boolean;
|
|
8
|
-
variant?: 'section' | 'card';
|
|
9
|
-
trailing?: ReactNode;
|
|
10
|
-
summaryClassName?: string;
|
|
11
|
-
contentClassName?: string;
|
|
12
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
-
//# sourceMappingURL=detail-card.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"detail-card.d.ts","sourceRoot":"","sources":["../../../src/components/sidebar/detail-card.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAGjD,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,QAAgB,EAChB,WAAmB,EACnB,OAAmB,EACnB,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,GACjB,EAAE;IACD,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,2CAsHA"}
|