@workflow/web-shared 5.0.0-beta.29 → 5.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/components/new-trace-viewer/components/trace-shortcut-helper.js +2 -2
  2. package/dist/components/new-trace-viewer/trace-viewer.js +2 -2
  3. package/dist/components/sidebar/attributes-block.js +2 -2
  4. package/dist/components/sidebar/entity-detail-panel.d.ts +2 -2
  5. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  6. package/dist/components/sidebar/entity-detail-panel.js +4 -5
  7. package/dist/components/sidebar/events-list.d.ts +2 -2
  8. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  9. package/dist/components/sidebar/events-list.js +15 -36
  10. package/dist/components/sidebar/sidebar-data-context.d.ts +1 -1
  11. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -1
  12. package/dist/components/sidebar/sidebar-data-context.js +1 -1
  13. package/dist/components/workflow-trace-view.d.ts +1 -1
  14. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  15. package/dist/components/workflow-trace-view.js +1 -1
  16. package/dist/components/workflow-traces/event-colors.d.ts +1 -1
  17. package/dist/components/workflow-traces/event-colors.d.ts.map +1 -1
  18. package/dist/components/workflow-traces/event-colors.js +4 -7
  19. package/dist/components/workflow-traces/trace-colors.d.ts.map +1 -1
  20. package/dist/components/workflow-traces/trace-colors.js +3 -4
  21. package/dist/components/workflow-traces/trace-span-construction.d.ts +1 -1
  22. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  23. package/dist/components/workflow-traces/trace-span-construction.js +3 -3
  24. package/dist/lib/event-analysis.d.ts +1 -1
  25. package/dist/lib/event-analysis.d.ts.map +1 -1
  26. package/dist/lib/event-analysis.js +3 -2
  27. package/dist/lib/event-materialization.d.ts +1 -1
  28. package/dist/lib/event-materialization.d.ts.map +1 -1
  29. package/dist/lib/event-materialization.js +8 -7
  30. package/dist/lib/hydration.d.ts +0 -1
  31. package/dist/lib/hydration.d.ts.map +1 -1
  32. package/dist/lib/hydration.js +2 -14
  33. package/dist/lib/trace-builder.d.ts +1 -4
  34. package/dist/lib/trace-builder.d.ts.map +1 -1
  35. package/dist/lib/trace-builder.js +8 -15
  36. package/package.json +3 -3
  37. package/src/components/new-trace-viewer/components/trace-shortcut-helper.tsx +1 -1
  38. package/src/components/new-trace-viewer/trace-viewer.tsx +5 -6
  39. package/src/components/sidebar/attributes-block.tsx +1 -1
  40. package/src/components/sidebar/entity-detail-panel.tsx +9 -9
  41. package/src/components/sidebar/events-list.tsx +17 -50
  42. package/src/components/sidebar/sidebar-data-context.tsx +1 -4
  43. package/src/components/workflow-trace-view.tsx +1 -4
  44. package/src/components/workflow-traces/event-colors.ts +3 -11
  45. package/src/components/workflow-traces/trace-colors.ts +6 -6
  46. package/src/components/workflow-traces/trace-span-construction.ts +6 -6
  47. package/src/lib/event-analysis.ts +6 -4
  48. package/src/lib/event-materialization.ts +13 -7
  49. package/src/lib/hydration.ts +1 -15
  50. package/src/lib/trace-builder.ts +13 -21
@@ -1,12 +1,8 @@
1
1
  'use client';
2
2
 
3
- import type { Event } from '@workflow/world';
3
+ import { type Event, getEventDataRefFields } from '@workflow/world';
4
4
  import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
5
- import {
6
- getEventDataRefFields,
7
- hasEncryptedFields,
8
- isExpiredMarker,
9
- } from '../../lib/hydration';
5
+ import { hasEncryptedFields, isExpiredMarker } from '../../lib/hydration';
10
6
  import {
11
7
  Collapsible,
12
8
  CollapsibleContent,
@@ -21,29 +17,6 @@ import { TimestampTooltip } from '../ui/timestamp-tooltip';
21
17
  import { AttrSetEventBlock } from './attributes-block';
22
18
  import { CopyableDataBlock, EncryptedDataBlock } from './copyable-data-block';
23
19
 
24
- /**
25
- * Event types whose eventData contains an error field with a StructuredError.
26
- */
27
- const ERROR_EVENT_TYPES = new Set(['step_failed', 'step_retrying']);
28
-
29
- /**
30
- * Event types that carry user-serialized data in their eventData field.
31
- */
32
- const DATA_EVENT_TYPES = new Set([
33
- 'step_created',
34
- 'step_completed',
35
- 'step_failed',
36
- 'step_retrying',
37
- 'hook_created',
38
- 'hook_received',
39
- 'run_created',
40
- 'run_completed',
41
- 'run_failed',
42
- 'wait_created',
43
- 'wait_completed',
44
- 'attr_set',
45
- ]);
46
-
47
20
  const parseDateValue = (value: unknown): Date | null => {
48
21
  if (value == null) return null;
49
22
 
@@ -76,10 +49,7 @@ function EventItem({
76
49
  showSeparateEventOccurrenceTimestamps = false,
77
50
  }: {
78
51
  event: Event;
79
- onLoadEventData?: (
80
- correlationId: string,
81
- eventId: string
82
- ) => Promise<unknown | null>;
52
+ onLoadEventData?: (event: Event) => Promise<unknown | null>;
83
53
  /** When this changes (e.g., Decrypt was clicked), invalidate cached data */
84
54
  encryptionKey?: Uint8Array;
85
55
  /** Show occurredAt separately instead of folding it into the Created timestamp. */
@@ -96,19 +66,20 @@ function EventItem({
96
66
  const existingData =
97
67
  'eventData' in event && event.eventData != null ? event.eventData : null;
98
68
  const mergedDisplay = loadedData ?? existingData;
99
- const canHaveData = DATA_EVENT_TYPES.has(event.eventType);
69
+ const canHaveData =
70
+ existingData !== null || getEventDataRefFields(event.eventType).length > 0;
100
71
 
101
72
  const loadEventData = useCallback(
102
- async (options?: { force?: boolean }) => {
103
- if (!onLoadEventData || !event.correlationId || !event.eventId) return;
104
- if (!options?.force && loadedDataRef.current !== null) {
73
+ async (force: boolean) => {
74
+ if (!onLoadEventData) return;
75
+ if (!force && loadedDataRef.current !== null) {
105
76
  return;
106
77
  }
107
78
 
108
79
  try {
109
80
  setIsLoading(true);
110
81
  setLoadError(null);
111
- const data = await onLoadEventData(event.correlationId, event.eventId);
82
+ const data = await onLoadEventData(event);
112
83
  loadedDataRef.current = data;
113
84
  setLoadedData(data);
114
85
  } catch (err) {
@@ -117,13 +88,13 @@ function EventItem({
117
88
  setIsLoading(false);
118
89
  }
119
90
  },
120
- [onLoadEventData, event.correlationId, event.eventId]
91
+ [onLoadEventData, event]
121
92
  );
122
93
 
123
- const handleExpand = useCallback(async () => {
94
+ const handleExpand = useCallback(() => {
124
95
  if (isLoading) return;
125
96
  wasExpandedRef.current = true;
126
- await loadEventData();
97
+ void loadEventData(false);
127
98
  }, [isLoading, loadEventData]);
128
99
 
129
100
  // When the encryption key changes and this event was previously expanded,
@@ -132,7 +103,7 @@ function EventItem({
132
103
  if (!encryptionKey || !wasExpandedRef.current) return;
133
104
  loadedDataRef.current = null;
134
105
  setLoadedData(null);
135
- void loadEventData({ force: true });
106
+ void loadEventData(true);
136
107
  }, [encryptionKey, loadEventData]);
137
108
 
138
109
  const createdAt = new Date(event.createdAt);
@@ -289,11 +260,10 @@ function EventDataBlock({
289
260
  return <AttrSetEventBlock data={data} />;
290
261
  }
291
262
 
292
- // For error events (step_failed, step_retrying), the eventData has the shape
293
- // { error: StructuredError, stack?: string, ... }. Check both the top-level
294
- // value and the nested `error` field for a stack trace.
263
+ // Error events carry a StructuredError in eventData.error. Check both the
264
+ // nested field and the top-level value for legacy payloads.
295
265
  if (
296
- ERROR_EVENT_TYPES.has(eventType) &&
266
+ getEventDataRefFields(eventType).includes('error') &&
297
267
  data != null &&
298
268
  typeof data === 'object'
299
269
  ) {
@@ -328,10 +298,7 @@ export function EventsList({
328
298
  events: Event[];
329
299
  isLoading?: boolean;
330
300
  error?: Error | null;
331
- onLoadEventData?: (
332
- correlationId: string,
333
- eventId: string
334
- ) => Promise<unknown | null>;
301
+ onLoadEventData?: (event: Event) => Promise<unknown | null>;
335
302
  onStreamClick?: (streamId: string) => void;
336
303
  onRunClick?: (runId: string) => void;
337
304
  /** When provided, signals that decryption is active (triggers re-load of expanded events) */
@@ -14,10 +14,7 @@ export interface SidebarDataContextValue {
14
14
  runId: string,
15
15
  correlationId: string
16
16
  ) => Promise<{ stoppedCount: number }>;
17
- onLoadEventData?: (
18
- correlationId: string,
19
- eventId: string
20
- ) => Promise<unknown | null>;
17
+ onLoadEventData?: (event: Event) => Promise<unknown | null>;
21
18
  onResolveHook?: (
22
19
  hookToken: string,
23
20
  payload: unknown,
@@ -801,10 +801,7 @@ export const WorkflowTraceViewer = ({
801
801
  /** Callback when a run reference is clicked in the detail panel */
802
802
  onRunClick?: (runId: string) => void;
803
803
  /** Callback to load event data for a specific event (lazy loading in sidebar) */
804
- onLoadEventData?: (
805
- correlationId: string,
806
- eventId: string
807
- ) => Promise<unknown | null>;
804
+ onLoadEventData?: (event: Event) => Promise<unknown | null>;
808
805
  /** Load next trace page when vertical scroll reaches bottom. */
809
806
  onLoadMoreSpans?: () => void | Promise<void>;
810
807
  /** Whether trace pagination has more data to load. */
@@ -2,7 +2,7 @@
2
2
  * Color utilities for workflow event markers
3
3
  */
4
4
 
5
- import type { Event } from '@workflow/world';
5
+ import { type Event, isHookLifecycleEventType } from '@workflow/world';
6
6
 
7
7
  export interface EventColorPalette {
8
8
  /** Color of the diamond/marker itself */
@@ -51,11 +51,7 @@ export function getEventColor(
51
51
  }
52
52
 
53
53
  // Webhook-related - Purple
54
- if (
55
- eventType === 'hook_created' ||
56
- eventType === 'hook_received' ||
57
- eventType === 'hook_disposed'
58
- ) {
54
+ if (isHookLifecycleEventType(eventType)) {
59
55
  return {
60
56
  color: 'var(--ds-purple-600)',
61
57
  background: 'var(--ds-purple-100)',
@@ -93,11 +89,7 @@ export function getEventColor(
93
89
  */
94
90
  export function shouldShowVerticalLine(eventType: Event['eventType']): boolean {
95
91
  // Show vertical lines for hook-related events
96
- if (
97
- eventType === 'hook_created' ||
98
- eventType === 'hook_received' ||
99
- eventType === 'hook_disposed'
100
- ) {
92
+ if (isHookLifecycleEventType(eventType)) {
101
93
  return true;
102
94
  }
103
95
 
@@ -2,7 +2,11 @@
2
2
  * Color utilities for workflow traces
3
3
  */
4
4
 
5
- import type { Step, WorkflowRun } from '@workflow/world';
5
+ import {
6
+ isHookLifecycleEventType,
7
+ type Step,
8
+ type WorkflowRun,
9
+ } from '@workflow/world';
6
10
  import styles from '../trace-viewer/trace-viewer.module.css';
7
11
  import type { SpanNode, SpanNodeEvent } from '../trace-viewer/types';
8
12
 
@@ -99,11 +103,7 @@ export const getCustomSpanEventClassName = (
99
103
  }
100
104
 
101
105
  // Webhook-related events - Purple
102
- if (
103
- eventName === 'hook_created' ||
104
- eventName === 'hook_received' ||
105
- eventName === 'hook_disposed'
106
- ) {
106
+ if (isHookLifecycleEventType(eventName)) {
107
107
  return styles.eventHook;
108
108
  }
109
109
 
@@ -3,7 +3,11 @@
3
3
  */
4
4
 
5
5
  import { parseStepName, parseWorkflowName } from '@workflow/utils/parse-name';
6
- import type { Event, WorkflowRun } from '@workflow/world';
6
+ import {
7
+ type Event,
8
+ isTerminalStepEventType,
9
+ type WorkflowRun,
10
+ } from '@workflow/world';
7
11
  import type { Span, SpanEvent } from '../trace-viewer/types';
8
12
  import { shouldShowVerticalLine } from './event-colors';
9
13
  import { calculateDuration, dateToOtelTime } from './trace-time-utils';
@@ -269,11 +273,7 @@ export function stepToSpan(stepEvents: Event[], maxEndTime: Date): Span | null {
269
273
  const completedEvent = stepEvents
270
274
  .slice()
271
275
  .reverse()
272
- .find(
273
- (event) =>
274
- event.eventType === 'step_completed' ||
275
- event.eventType === 'step_failed'
276
- );
276
+ .find((event) => isTerminalStepEventType(event.eventType));
277
277
  endTime = getEventTimestamp(completedEvent) ?? new Date(step.completedAt);
278
278
  }
279
279
 
@@ -3,7 +3,11 @@
3
3
  * Used by run-actions and trace viewer components.
4
4
  */
5
5
 
6
- import type { Event, WorkflowRunStatus } from '@workflow/world';
6
+ import {
7
+ type Event,
8
+ isTerminalWorkflowRunStatus,
9
+ type WorkflowRunStatus,
10
+ } from '@workflow/world';
7
11
 
8
12
  // Time thresholds for Re-enqueue button visibility
9
13
  const STEP_ACTIVITY_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
@@ -140,9 +144,7 @@ export function analyzeEvents(events: Event[] | undefined): EventAnalysis {
140
144
  export function isTerminalStatus(
141
145
  status: WorkflowRunStatus | undefined
142
146
  ): boolean {
143
- return (
144
- status === 'completed' || status === 'failed' || status === 'cancelled'
145
- );
147
+ return status ? isTerminalWorkflowRunStatus(status) : false;
146
148
  }
147
149
 
148
150
  /**
@@ -10,7 +10,13 @@
10
10
  * of making separate API calls for each entity type.
11
11
  */
12
12
 
13
- import type { Event, StepStatus } from '@workflow/world';
13
+ import {
14
+ type Event,
15
+ isHookLifecycleEventType,
16
+ isStepEventType,
17
+ isWaitEventType,
18
+ type StepStatus,
19
+ } from '@workflow/world';
14
20
 
15
21
  // ---------------------------------------------------------------------------
16
22
  // Materialized entity types
@@ -60,18 +66,18 @@ export interface MaterializedEntities {
60
66
  }
61
67
 
62
68
  // ---------------------------------------------------------------------------
63
- // Helper: group events by correlationId prefix
69
+ // Helper: group events by correlationId
64
70
  // ---------------------------------------------------------------------------
65
71
 
66
72
  function groupByCorrelationId(
67
73
  events: Event[],
68
- prefixes: string[]
74
+ matchesEventType: (eventType: string) => boolean
69
75
  ): Map<string, Event[]> {
70
76
  const groups = new Map<string, Event[]>();
71
77
  for (const event of events) {
72
78
  const cid = event.correlationId;
73
79
  if (!cid) continue;
74
- if (!prefixes.some((p) => cid.startsWith(p))) continue;
80
+ if (!matchesEventType(event.eventType)) continue;
75
81
  const existing = groups.get(cid);
76
82
  if (existing) {
77
83
  existing.push(event);
@@ -101,7 +107,7 @@ function getEventTimestamp(event: Event | undefined): Date | undefined {
101
107
  * step_created event with no completion yet.
102
108
  */
103
109
  export function materializeSteps(events: Event[]): MaterializedStep[] {
104
- const groups = groupByCorrelationId(events, ['step_']);
110
+ const groups = groupByCorrelationId(events, isStepEventType);
105
111
  const steps: MaterializedStep[] = [];
106
112
 
107
113
  for (const [correlationId, stepEvents] of groups) {
@@ -169,7 +175,7 @@ export function materializeSteps(events: Event[]): MaterializedStep[] {
169
175
  * Group hook_* events by correlationId and build Hook-like entities.
170
176
  */
171
177
  export function materializeHooks(events: Event[]): MaterializedHook[] {
172
- const groups = groupByCorrelationId(events, ['hook_']);
178
+ const groups = groupByCorrelationId(events, isHookLifecycleEventType);
173
179
  const hooks: MaterializedHook[] = [];
174
180
 
175
181
  for (const [correlationId, hookEvents] of groups) {
@@ -208,7 +214,7 @@ export function materializeHooks(events: Event[]): MaterializedHook[] {
208
214
  * Group wait_* events by correlationId and build Wait-like entities.
209
215
  */
210
216
  export function materializeWaits(events: Event[]): MaterializedWait[] {
211
- const groups = groupByCorrelationId(events, ['wait_']);
217
+ const groups = groupByCorrelationId(events, isWaitEventType);
212
218
  const waits: MaterializedWait[] = [];
213
219
 
214
220
  for (const [correlationId, waitEvents] of groups) {
@@ -16,21 +16,7 @@ import {
16
16
  type Revivers,
17
17
  serializedInstanceToRef,
18
18
  } from '@workflow/core/serialization-format';
19
- import { EVENT_DATA_REF_FIELDS } from '@workflow/world';
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
- }
19
+ import { getEventDataRefFields } from '@workflow/world';
34
20
 
35
21
  // Re-export types and utilities that consumers need
36
22
  export {
@@ -6,7 +6,13 @@
6
6
  * a fully-formed Trace ready for the trace viewer.
7
7
  */
8
8
 
9
- import type { Event, WorkflowRun } from '@workflow/world';
9
+ import {
10
+ type Event,
11
+ isHookLifecycleEventType,
12
+ isStepEventType,
13
+ isWaitEventType,
14
+ type WorkflowRun,
15
+ } from '@workflow/world';
10
16
  import type { Span } from '../components/trace-viewer/types';
11
17
  import {
12
18
  getEventTimestamp,
@@ -18,20 +24,6 @@ import {
18
24
  } from '../components/workflow-traces/trace-span-construction';
19
25
  import { otelTimeToMs } from '../components/workflow-traces/trace-time-utils';
20
26
 
21
- // ---------------------------------------------------------------------------
22
- // Event type classifiers
23
- // ---------------------------------------------------------------------------
24
-
25
- export const isStepEvent = (eventType: string) => eventType.startsWith('step_');
26
-
27
- export const isTimerEvent = (eventType: string) =>
28
- eventType === 'wait_created' || eventType === 'wait_completed';
29
-
30
- export const isHookLifecycleEvent = (eventType: string) =>
31
- eventType === 'hook_received' ||
32
- eventType === 'hook_created' ||
33
- eventType === 'hook_disposed';
34
-
35
27
  /**
36
28
  * Events that belong to the run root span rather than a child entity span.
37
29
  * Mirrors the fallthrough logic in {@link groupEventsByCorrelation}: events
@@ -41,9 +33,9 @@ export const isHookLifecycleEvent = (eventType: string) =>
41
33
  */
42
34
  export const isRunLevelEvent = (event: Event): boolean =>
43
35
  !event.correlationId ||
44
- (!isTimerEvent(event.eventType) &&
45
- !isHookLifecycleEvent(event.eventType) &&
46
- !isStepEvent(event.eventType));
36
+ (!isWaitEventType(event.eventType) &&
37
+ !isHookLifecycleEventType(event.eventType) &&
38
+ !isStepEventType(event.eventType));
47
39
 
48
40
  /**
49
41
  * Filter the raw event list down to the events for a selected span.
@@ -100,17 +92,17 @@ export function groupEventsByCorrelation(events: Event[]): GroupedEvents {
100
92
  continue;
101
93
  }
102
94
 
103
- if (isTimerEvent(event.eventType)) {
95
+ if (isWaitEventType(event.eventType)) {
104
96
  pushEvent(timerEvents, correlationId, event);
105
97
  continue;
106
98
  }
107
99
 
108
- if (isHookLifecycleEvent(event.eventType)) {
100
+ if (isHookLifecycleEventType(event.eventType)) {
109
101
  pushEvent(hookEvents, correlationId, event);
110
102
  continue;
111
103
  }
112
104
 
113
- if (isStepEvent(event.eventType)) {
105
+ if (isStepEventType(event.eventType)) {
114
106
  pushEvent(eventsByStepId, correlationId, event);
115
107
  continue;
116
108
  }