@workflow/web-shared 5.0.0-beta.0 → 5.0.0-beta.10

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 (275) hide show
  1. package/README.md +1 -1
  2. package/dist/components/error-boundary.js +1 -1
  3. package/dist/components/event-list-view.d.ts +18 -1
  4. package/dist/components/event-list-view.d.ts.map +1 -1
  5. package/dist/components/event-list-view.js +242 -142
  6. package/dist/components/hook-actions.js +1 -1
  7. package/dist/components/index.d.ts +4 -1
  8. package/dist/components/index.d.ts.map +1 -1
  9. package/dist/components/index.js +5 -2
  10. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  11. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  12. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  13. package/dist/components/new-trace-viewer/components/event-list.d.ts +10 -0
  14. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  15. package/dist/components/new-trace-viewer/components/event-list.js +39 -0
  16. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
  17. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  18. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  19. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
  20. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  21. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  22. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
  23. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  24. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
  25. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts +20 -0
  26. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.d.ts.map +1 -0
  27. package/dist/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.js +114 -0
  28. package/dist/components/new-trace-viewer/components/split-pane.d.ts +14 -0
  29. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  30. package/dist/components/new-trace-viewer/components/split-pane.js +99 -0
  31. package/dist/components/new-trace-viewer/components/timeline.d.ts +34 -0
  32. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  33. package/dist/components/new-trace-viewer/components/timeline.js +223 -0
  34. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  35. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  36. package/dist/components/new-trace-viewer/context.js +47 -0
  37. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  38. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  39. package/dist/components/new-trace-viewer/detail-panel.js +13 -0
  40. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  41. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  42. package/dist/components/new-trace-viewer/icons.js +15 -0
  43. package/dist/components/new-trace-viewer/search.d.ts +19 -0
  44. package/dist/components/new-trace-viewer/search.d.ts.map +1 -0
  45. package/dist/components/new-trace-viewer/search.js +134 -0
  46. package/dist/components/new-trace-viewer/trace-viewer.d.ts +8 -0
  47. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  48. package/dist/components/new-trace-viewer/trace-viewer.js +387 -0
  49. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  50. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  51. package/dist/components/new-trace-viewer/types.js +2 -0
  52. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  53. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  54. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  55. package/dist/components/new-trace-viewer/utils.d.ts +46 -0
  56. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  57. package/dist/components/new-trace-viewer/utils.js +330 -0
  58. package/dist/components/run-trace-view.d.ts +2 -1
  59. package/dist/components/run-trace-view.d.ts.map +1 -1
  60. package/dist/components/run-trace-view.js +3 -3
  61. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  62. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  63. package/dist/components/sidebar/attribute-panel.js +131 -70
  64. package/dist/components/sidebar/conversation-view.js +1 -1
  65. package/dist/components/sidebar/copyable-data-block.d.ts +1 -0
  66. package/dist/components/sidebar/copyable-data-block.d.ts.map +1 -1
  67. package/dist/components/sidebar/copyable-data-block.js +16 -15
  68. package/dist/components/sidebar/detail-card.d.ts +5 -6
  69. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  70. package/dist/components/sidebar/detail-card.js +32 -16
  71. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  72. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  73. package/dist/components/sidebar/entity-detail-panel.js +20 -37
  74. package/dist/components/sidebar/events-list.d.ts +1 -1
  75. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  76. package/dist/components/sidebar/events-list.js +46 -32
  77. package/dist/components/sidebar/resolve-hook-modal.js +1 -1
  78. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  79. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  80. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  81. package/dist/components/stream-viewer.d.ts +1 -1
  82. package/dist/components/stream-viewer.d.ts.map +1 -1
  83. package/dist/components/stream-viewer.js +3 -27
  84. package/dist/components/trace-viewer/components/map.js +1 -1
  85. package/dist/components/trace-viewer/components/markers.js +1 -1
  86. package/dist/components/trace-viewer/components/node.js +1 -1
  87. package/dist/components/trace-viewer/components/search-input.js +1 -1
  88. package/dist/components/trace-viewer/components/search.js +1 -1
  89. package/dist/components/trace-viewer/components/span-content.js +1 -1
  90. package/dist/components/trace-viewer/components/span-detail-panel.js +1 -1
  91. package/dist/components/trace-viewer/components/span-segments.js +1 -1
  92. package/dist/components/trace-viewer/components/span-strategies.js +1 -1
  93. package/dist/components/trace-viewer/components/ui.js +1 -1
  94. package/dist/components/trace-viewer/components/zoom-button.js +1 -1
  95. package/dist/components/trace-viewer/components/zoom-icons.js +1 -1
  96. package/dist/components/trace-viewer/context.js +1 -1
  97. package/dist/components/trace-viewer/index.js +1 -1
  98. package/dist/components/trace-viewer/trace-viewer.js +1 -1
  99. package/dist/components/trace-viewer/types.js +1 -1
  100. package/dist/components/trace-viewer/util/constants.js +1 -1
  101. package/dist/components/trace-viewer/util/scrollbar-width.js +1 -1
  102. package/dist/components/trace-viewer/util/timing.d.ts +1 -1
  103. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  104. package/dist/components/trace-viewer/util/timing.js +6 -9
  105. package/dist/components/trace-viewer/util/tree.js +1 -1
  106. package/dist/components/trace-viewer/util/use-immediate-style.js +1 -1
  107. package/dist/components/trace-viewer/util/use-streaming-spans.js +1 -1
  108. package/dist/components/trace-viewer/util/use-trackpad-zoom.js +1 -1
  109. package/dist/components/trace-viewer/worker.js +1 -1
  110. package/dist/components/trace-viewer-new.d.ts +10 -0
  111. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  112. package/dist/components/trace-viewer-new.js +23 -0
  113. package/dist/components/ui/alert.js +1 -1
  114. package/dist/components/ui/button.d.ts +10 -0
  115. package/dist/components/ui/button.d.ts.map +1 -0
  116. package/dist/components/ui/button.js +46 -0
  117. package/dist/components/ui/card.js +1 -1
  118. package/dist/components/ui/data-inspector.d.ts +39 -1
  119. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  120. package/dist/components/ui/data-inspector.js +207 -22
  121. package/dist/components/ui/decrypt-button.js +1 -1
  122. package/dist/components/ui/error-card.js +1 -1
  123. package/dist/components/ui/error-stack-block.d.ts.map +1 -1
  124. package/dist/components/ui/error-stack-block.js +27 -19
  125. package/dist/components/ui/icon-button.d.ts +13 -0
  126. package/dist/components/ui/icon-button.d.ts.map +1 -0
  127. package/dist/components/ui/icon-button.js +29 -0
  128. package/dist/components/ui/inspector-theme.d.ts +9 -10
  129. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  130. package/dist/components/ui/inspector-theme.js +39 -72
  131. package/dist/components/ui/load-more-button.js +1 -1
  132. package/dist/components/ui/menu-dropdown.js +1 -1
  133. package/dist/components/ui/skeleton.js +1 -1
  134. package/dist/components/ui/spinner.js +1 -1
  135. package/dist/components/ui/timestamp-tooltip.js +1 -1
  136. package/dist/components/workflow-trace-view.d.ts +3 -1
  137. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  138. package/dist/components/workflow-trace-view.js +10 -5
  139. package/dist/components/workflow-traces/event-colors.js +1 -1
  140. package/dist/components/workflow-traces/trace-colors.js +1 -1
  141. package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
  142. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  143. package/dist/components/workflow-traces/trace-span-construction.js +19 -6
  144. package/dist/components/workflow-traces/trace-time-utils.js +1 -1
  145. package/dist/hooks/use-dark-mode.js +1 -1
  146. package/dist/hooks/use-reduced-motion.d.ts +10 -0
  147. package/dist/hooks/use-reduced-motion.d.ts.map +1 -0
  148. package/dist/hooks/use-reduced-motion.js +29 -0
  149. package/dist/index.d.ts +3 -1
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.js +3 -2
  152. package/dist/lib/event-analysis.js +1 -1
  153. package/dist/lib/event-materialization.js +1 -1
  154. package/dist/lib/exact-event-search-id.d.ts +25 -0
  155. package/dist/lib/exact-event-search-id.d.ts.map +1 -0
  156. package/dist/lib/exact-event-search-id.js +39 -0
  157. package/dist/lib/hydration.d.ts +15 -2
  158. package/dist/lib/hydration.d.ts.map +1 -1
  159. package/dist/lib/hydration.js +168 -16
  160. package/dist/lib/stream-display.d.ts +18 -0
  161. package/dist/lib/stream-display.d.ts.map +1 -0
  162. package/dist/lib/stream-display.js +30 -0
  163. package/dist/lib/toast.js +1 -1
  164. package/dist/lib/trace-builder.d.ts.map +1 -1
  165. package/dist/lib/trace-builder.js +3 -2
  166. package/dist/lib/utils.d.ts +9 -9
  167. package/dist/lib/utils.d.ts.map +1 -1
  168. package/dist/lib/utils.js +27 -32
  169. package/dist/styles.css +475 -1
  170. package/package.json +6 -4
  171. package/src/components/event-list-view.tsx +447 -282
  172. package/src/components/index.ts +12 -1
  173. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  174. package/src/components/new-trace-viewer/components/event-list.tsx +127 -0
  175. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  176. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  177. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  178. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  179. package/src/components/new-trace-viewer/components/split-pane.tsx +185 -0
  180. package/src/components/new-trace-viewer/components/timeline.tsx +552 -0
  181. package/src/components/new-trace-viewer/context.tsx +92 -0
  182. package/src/components/new-trace-viewer/detail-panel.tsx +55 -0
  183. package/src/components/new-trace-viewer/icons.tsx +79 -0
  184. package/src/components/new-trace-viewer/search.ts +178 -0
  185. package/src/components/new-trace-viewer/trace-viewer.tsx +694 -0
  186. package/src/components/new-trace-viewer/types.ts +0 -0
  187. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  188. package/src/components/new-trace-viewer/utils.ts +482 -0
  189. package/src/components/run-trace-view.tsx +3 -0
  190. package/src/components/sidebar/attribute-panel.tsx +240 -196
  191. package/src/components/sidebar/copyable-data-block.tsx +49 -27
  192. package/src/components/sidebar/detail-card.tsx +119 -59
  193. package/src/components/sidebar/entity-detail-panel.tsx +92 -148
  194. package/src/components/sidebar/events-list.tsx +93 -108
  195. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  196. package/src/components/stream-viewer.tsx +18 -40
  197. package/src/components/trace-viewer/util/timing.ts +5 -9
  198. package/src/components/trace-viewer-new.tsx +49 -0
  199. package/src/components/ui/button.tsx +68 -0
  200. package/src/components/ui/data-inspector.tsx +427 -31
  201. package/src/components/ui/error-stack-block.tsx +39 -30
  202. package/src/components/ui/icon-button.tsx +57 -0
  203. package/src/components/ui/inspector-theme.ts +39 -81
  204. package/src/components/workflow-trace-view.tsx +12 -2
  205. package/src/components/workflow-traces/trace-span-construction.ts +24 -6
  206. package/src/hooks/use-reduced-motion.ts +34 -0
  207. package/src/index.ts +9 -0
  208. package/src/lib/exact-event-search-id.ts +67 -0
  209. package/src/lib/hydration.ts +182 -14
  210. package/src/lib/stream-display.ts +48 -0
  211. package/src/lib/trace-builder.ts +2 -1
  212. package/src/lib/utils.ts +27 -32
  213. package/src/styles.css +475 -1
  214. package/dist/components/error-boundary.js.map +0 -1
  215. package/dist/components/event-list-view.js.map +0 -1
  216. package/dist/components/hook-actions.js.map +0 -1
  217. package/dist/components/index.js.map +0 -1
  218. package/dist/components/run-trace-view.js.map +0 -1
  219. package/dist/components/sidebar/attribute-panel.js.map +0 -1
  220. package/dist/components/sidebar/conversation-view.js.map +0 -1
  221. package/dist/components/sidebar/copyable-data-block.js.map +0 -1
  222. package/dist/components/sidebar/detail-card.js.map +0 -1
  223. package/dist/components/sidebar/entity-detail-panel.js.map +0 -1
  224. package/dist/components/sidebar/events-list.js.map +0 -1
  225. package/dist/components/sidebar/resolve-hook-modal.js.map +0 -1
  226. package/dist/components/stream-viewer.js.map +0 -1
  227. package/dist/components/trace-viewer/components/map.js.map +0 -1
  228. package/dist/components/trace-viewer/components/markers.js.map +0 -1
  229. package/dist/components/trace-viewer/components/node.js.map +0 -1
  230. package/dist/components/trace-viewer/components/search-input.js.map +0 -1
  231. package/dist/components/trace-viewer/components/search.js.map +0 -1
  232. package/dist/components/trace-viewer/components/span-content.js.map +0 -1
  233. package/dist/components/trace-viewer/components/span-detail-panel.js.map +0 -1
  234. package/dist/components/trace-viewer/components/span-segments.js.map +0 -1
  235. package/dist/components/trace-viewer/components/span-strategies.js.map +0 -1
  236. package/dist/components/trace-viewer/components/ui.js.map +0 -1
  237. package/dist/components/trace-viewer/components/zoom-button.js.map +0 -1
  238. package/dist/components/trace-viewer/components/zoom-icons.js.map +0 -1
  239. package/dist/components/trace-viewer/context.js.map +0 -1
  240. package/dist/components/trace-viewer/index.js.map +0 -1
  241. package/dist/components/trace-viewer/trace-viewer.js.map +0 -1
  242. package/dist/components/trace-viewer/types.js.map +0 -1
  243. package/dist/components/trace-viewer/util/constants.js.map +0 -1
  244. package/dist/components/trace-viewer/util/scrollbar-width.js.map +0 -1
  245. package/dist/components/trace-viewer/util/timing.js.map +0 -1
  246. package/dist/components/trace-viewer/util/tree.js.map +0 -1
  247. package/dist/components/trace-viewer/util/use-immediate-style.js.map +0 -1
  248. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +0 -1
  249. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
  250. package/dist/components/trace-viewer/worker.js.map +0 -1
  251. package/dist/components/ui/alert.js.map +0 -1
  252. package/dist/components/ui/card.js.map +0 -1
  253. package/dist/components/ui/data-inspector.js.map +0 -1
  254. package/dist/components/ui/decrypt-button.js.map +0 -1
  255. package/dist/components/ui/error-card.js.map +0 -1
  256. package/dist/components/ui/error-stack-block.js.map +0 -1
  257. package/dist/components/ui/inspector-theme.js.map +0 -1
  258. package/dist/components/ui/load-more-button.js.map +0 -1
  259. package/dist/components/ui/menu-dropdown.js.map +0 -1
  260. package/dist/components/ui/skeleton.js.map +0 -1
  261. package/dist/components/ui/spinner.js.map +0 -1
  262. package/dist/components/ui/timestamp-tooltip.js.map +0 -1
  263. package/dist/components/workflow-trace-view.js.map +0 -1
  264. package/dist/components/workflow-traces/event-colors.js.map +0 -1
  265. package/dist/components/workflow-traces/trace-colors.js.map +0 -1
  266. package/dist/components/workflow-traces/trace-span-construction.js.map +0 -1
  267. package/dist/components/workflow-traces/trace-time-utils.js.map +0 -1
  268. package/dist/hooks/use-dark-mode.js.map +0 -1
  269. package/dist/index.js.map +0 -1
  270. package/dist/lib/event-analysis.js.map +0 -1
  271. package/dist/lib/event-materialization.js.map +0 -1
  272. package/dist/lib/hydration.js.map +0 -1
  273. package/dist/lib/toast.js.map +0 -1
  274. package/dist/lib/trace-builder.js.map +0 -1
  275. package/dist/lib/utils.js.map +0 -1
@@ -11,9 +11,12 @@ import {
11
11
  hydrateResourceIO as hydrateResourceIOGeneric,
12
12
  isEncryptedData,
13
13
  isExpiredStub,
14
+ isRunRef,
14
15
  observabilityRevivers,
15
16
  type Revivers,
17
+ serializedInstanceToRef,
16
18
  } from '@workflow/core/serialization-format';
19
+ import { EVENT_DATA_REF_FIELDS } from '@workflow/world';
17
20
 
18
21
  // Re-export types and utilities that consumers need
19
22
  export {
@@ -23,11 +26,15 @@ export {
23
26
  extractStreamIds,
24
27
  isClassInstanceRef,
25
28
  isEncryptedData,
29
+ isRunRef,
26
30
  isStreamId,
27
31
  isStreamRef,
28
32
  type Revivers,
33
+ RUN_REF_TYPE,
34
+ type RunRef,
29
35
  STREAM_REF_TYPE,
30
36
  type StreamRef,
37
+ serializedInstanceToRef,
31
38
  truncateId,
32
39
  } from '@workflow/core/serialization-format';
33
40
 
@@ -51,12 +58,60 @@ function base64ToArrayBuffer(base64: string): ArrayBuffer {
51
58
  // Web revivers (browser-safe, no Buffer dependency)
52
59
  // ---------------------------------------------------------------------------
53
60
 
61
+ /**
62
+ * Build a reviver for one of the built-in `Error` subclasses (e.g.
63
+ * `TypeError`, `RangeError`). The constructor for the named subclass is
64
+ * resolved off `globalThis` at call time so the produced instance has the
65
+ * correct prototype chain in the consumer realm. Falls back to a generic
66
+ * `Error` (with `name` set) if the global isn't available, which keeps the
67
+ * o11y UI rendering even on exotic browsers.
68
+ *
69
+ * `cause` is passed through `ErrorOptions` to the constructor when present,
70
+ * matching `getCommonRevivers` in `@workflow/core` so the resulting `cause`
71
+ * property has the same semantics (non-enumerable, set by the engine) as a
72
+ * freshly thrown Error in the consumer realm. The `'cause' in value` check
73
+ * preserves the distinction between "no cause" and "cause is undefined".
74
+ */
75
+ function makeWebErrorSubclassReviver(
76
+ name:
77
+ | 'EvalError'
78
+ | 'RangeError'
79
+ | 'ReferenceError'
80
+ | 'SyntaxError'
81
+ | 'TypeError'
82
+ | 'URIError'
83
+ ) {
84
+ return (value: { message: string; stack?: string; cause?: unknown }) => {
85
+ const opts = 'cause' in value ? { cause: value.cause } : undefined;
86
+ const Ctor = (globalThis as Record<string, any>)[name] as
87
+ | ErrorConstructor
88
+ | undefined;
89
+ let error: Error;
90
+ if (typeof Ctor === 'function') {
91
+ error = new Ctor(value.message, opts);
92
+ } else {
93
+ // Fallback path: no built-in subclass available (exotic env). Construct
94
+ // a plain Error with the right `name` and copy `cause` manually since
95
+ // the base Error constructor is what we actually called.
96
+ error = Object.assign(new Error(value.message, opts), { name });
97
+ }
98
+ if (value.stack !== undefined) error.stack = value.stack;
99
+ return error;
100
+ };
101
+ }
102
+
54
103
  /**
55
104
  * Get the web-specific revivers for hydrating serialized data.
56
105
  *
57
106
  * Uses `atob()` for base64 decoding (no Node.js Buffer dependency).
58
107
  * All types are revived as real instances (Date, Map, Set, URL,
59
108
  * URLSearchParams, Headers, Error, etc.).
109
+ *
110
+ * NOTE: this set must mirror the keys in `SerializableSpecial` (see
111
+ * `@workflow/core/serialization/types`). Any reducer key added on the
112
+ * serialization side that isn't covered here will cause `devalue.unflatten`
113
+ * to throw `Unknown type X`, which `hydrateResourceIO` swallows and
114
+ * surfaces as a "Failed to load resource details" banner in the o11y UI.
60
115
  */
61
116
  export function getWebRevivers(): Revivers {
62
117
  function reviveArrayBuffer(value: string): ArrayBuffer {
@@ -76,10 +131,93 @@ export function getWebRevivers(): Revivers {
76
131
  BigUint64Array: (value: string) =>
77
132
  new BigUint64Array(reviveArrayBuffer(value)),
78
133
  Date: (value) => new Date(value),
134
+
135
+ // Error family. The reducer side (see
136
+ // `packages/core/src/serialization/reducers/common.ts`) emits a tagged
137
+ // entry for each built-in Error subclass plus the workflow-specific
138
+ // `FatalError` / `RetryableError` and `AggregateError`. Without
139
+ // matching revivers here, `devalue.unflatten` throws "Unknown type X"
140
+ // — which surfaces in the web o11y UI as "Failed to load resource
141
+ // details: Unknown type FatalError".
79
142
  Error: (value) => {
143
+ const opts = 'cause' in value ? { cause: value.cause } : undefined;
144
+ const error = new Error(value.message, opts);
145
+ error.name = value.name;
146
+ if (value.stack !== undefined) error.stack = value.stack;
147
+ return error;
148
+ },
149
+ EvalError: makeWebErrorSubclassReviver('EvalError'),
150
+ RangeError: makeWebErrorSubclassReviver('RangeError'),
151
+ ReferenceError: makeWebErrorSubclassReviver('ReferenceError'),
152
+ SyntaxError: makeWebErrorSubclassReviver('SyntaxError'),
153
+ TypeError: makeWebErrorSubclassReviver('TypeError'),
154
+ URIError: makeWebErrorSubclassReviver('URIError'),
155
+ AggregateError: (value) => {
156
+ const opts = 'cause' in value ? { cause: value.cause } : undefined;
157
+ const Ctor = (
158
+ globalThis as { AggregateError?: AggregateErrorConstructor }
159
+ ).AggregateError;
160
+ const error =
161
+ typeof Ctor === 'function'
162
+ ? new Ctor(value.errors, value.message, opts)
163
+ : Object.assign(new Error(value.message, opts), {
164
+ name: 'AggregateError',
165
+ errors: value.errors,
166
+ });
167
+ if (value.stack !== undefined) error.stack = value.stack;
168
+ return error;
169
+ },
170
+ // `FatalError` and `RetryableError` are not built-in browser globals,
171
+ // so we can't resolve a constructor from globalThis. The web o11y UI
172
+ // doesn't need `instanceof FatalError` to pass (no user code runs
173
+ // here) — it just needs `name`, `message`, `stack`, and any extra
174
+ // enumerable fields to render. Construct a plain `Error` with `name`
175
+ // set; ObjectInspector reads `constructor.name` for the displayed
176
+ // class label, but we don't have the real class, so we emit a tagged
177
+ // Error whose `name` field carries the class identity. This matches
178
+ // how the existing base `Error` reviver presents unknown subclasses.
179
+ FatalError: (value) => {
180
+ const opts = 'cause' in value ? { cause: value.cause } : undefined;
181
+ const error = new Error(value.message, opts);
182
+ error.name = 'FatalError';
183
+ if (value.stack !== undefined) error.stack = value.stack;
184
+ return error;
185
+ },
186
+ RetryableError: (value) => {
187
+ const opts = 'cause' in value ? { cause: value.cause } : undefined;
188
+ const error = new Error(value.message, opts) as Error & {
189
+ retryAfter?: Date;
190
+ };
191
+ error.name = 'RetryableError';
192
+ if (value.stack !== undefined) error.stack = value.stack;
193
+ // `retryAfter` is serialized as an epoch ms number (see the runtime
194
+ // RetryableError reducer for the rationale around realm-safety).
195
+ // Rehydrate as a Date so o11y consumers can render it directly.
196
+ // Guard against payloads from older runtime versions that predate
197
+ // the field — without this check, `new Date(undefined)` would
198
+ // produce an Invalid Date rather than omitting the property.
199
+ if (value.retryAfter != null) {
200
+ error.retryAfter = new Date(value.retryAfter);
201
+ }
202
+ return error;
203
+ },
204
+ DOMException: (value) => {
205
+ // Modern browsers and Node 18+ expose `DOMException` on globalThis.
206
+ // `AbortController.abort()` with no argument synthesizes one as the
207
+ // signal's reason, so this is a common payload for any aborted step.
208
+ const G = globalThis as { DOMException?: typeof DOMException };
209
+ if (typeof G.DOMException === 'function') {
210
+ const e = new G.DOMException(value.message, value.name);
211
+ if (value.stack !== undefined) e.stack = value.stack;
212
+ if ('cause' in value) (e as { cause?: unknown }).cause = value.cause;
213
+ return e;
214
+ }
80
215
  const error = new Error(value.message);
81
216
  error.name = value.name;
82
- error.stack = value.stack;
217
+ if (value.stack !== undefined) error.stack = value.stack;
218
+ if ('cause' in value) {
219
+ (error as Error & { cause?: unknown }).cause = value.cause;
220
+ }
83
221
  return error;
84
222
  },
85
223
  Float32Array: (value: string) => new Float32Array(reviveArrayBuffer(value)),
@@ -136,6 +274,11 @@ export function getWebRevivers(): Revivers {
136
274
  // react-inspector shows the class name (it reads constructor.name).
137
275
  Class: (value) => `<class:${extractClassName(value.classId)}>`,
138
276
  Instance: (value) => {
277
+ // Run instances are rendered as clickable RunRef badges
278
+ const runRef = serializedInstanceToRef(value);
279
+ if (isRunRef(runRef)) {
280
+ return runRef;
281
+ }
139
282
  const className = extractClassName(value.classId);
140
283
  const data = value.data;
141
284
  const props =
@@ -274,9 +417,14 @@ function replaceEncryptedAndExpiredWithMarkers<T>(resource: T): T {
274
417
  }
275
418
 
276
419
  if (result.eventData && typeof result.eventData === 'object') {
420
+ const eventType =
421
+ typeof result.eventType === 'string' ? result.eventType : '';
422
+ const refKeys = EVENT_DATA_REF_FIELDS[eventType] ?? [];
277
423
  const ed = { ...(result.eventData as Record<string, unknown>) };
278
- for (const key of EVENT_DATA_SERIALIZED_FIELDS) {
279
- ed[key] = toDisplayMarker(ed[key]);
424
+ for (const key of refKeys) {
425
+ if (key in ed) {
426
+ ed[key] = toDisplayMarker(ed[key]);
427
+ }
280
428
  }
281
429
  result.eventData = ed;
282
430
  }
@@ -284,15 +432,6 @@ function replaceEncryptedAndExpiredWithMarkers<T>(resource: T): T {
284
432
  return result as T;
285
433
  }
286
434
 
287
- /** Known serialized subfields within eventData, matching hydrateEventData in core */
288
- const EVENT_DATA_SERIALIZED_FIELDS = [
289
- 'result',
290
- 'input',
291
- 'output',
292
- 'metadata',
293
- 'payload',
294
- ];
295
-
296
435
  /**
297
436
  * Hydrate resource data with decryption support.
298
437
  *
@@ -300,7 +439,7 @@ const EVENT_DATA_SERIALIZED_FIELDS = [
300
439
  * This is the async version used when the user clicks "Decrypt" in the web UI.
301
440
  *
302
441
  * Handles both top-level fields (input, output, metadata) and nested
303
- * eventData subfields (result, input, output, metadata, payload).
442
+ * eventData subfields per `EVENT_DATA_REF_FIELDS` from `@workflow/world` for that event type.
304
443
  */
305
444
  export async function hydrateResourceIOWithKey<T>(
306
445
  resource: T,
@@ -344,8 +483,11 @@ export async function hydrateResourceIOWithKey<T>(
344
483
 
345
484
  // Decrypt + hydrate eventData subfields (events)
346
485
  if (result.eventData && typeof result.eventData === 'object') {
486
+ const eventType =
487
+ typeof result.eventType === 'string' ? result.eventType : '';
488
+ const refKeys = EVENT_DATA_REF_FIELDS[eventType] ?? [];
347
489
  const eventData = { ...(result.eventData as Record<string, unknown>) };
348
- for (const field of EVENT_DATA_SERIALIZED_FIELDS) {
490
+ for (const field of refKeys) {
349
491
  if (field in eventData) {
350
492
  eventData[field] = await decryptField(
351
493
  eventData[field],
@@ -359,3 +501,29 @@ export async function hydrateResourceIOWithKey<T>(
359
501
 
360
502
  return result as T;
361
503
  }
504
+
505
+ /**
506
+ * Check whether a hydrated resource (event, step, run, etc.) contains any
507
+ * encrypted display markers. Inspects the standard top-level fields
508
+ * (`input`, `output`, `error`, `metadata`) as well as the event-type-specific
509
+ * `eventData` ref fields.
510
+ */
511
+ export function hasEncryptedFields(resource: unknown): boolean {
512
+ if (!resource || typeof resource !== 'object') return false;
513
+ const r = resource as Record<string, unknown>;
514
+
515
+ for (const key of ['input', 'output', 'metadata', 'error']) {
516
+ if (isEncryptedMarker(r[key])) return true;
517
+ }
518
+
519
+ if (r.eventData && typeof r.eventData === 'object') {
520
+ const eventType = typeof r.eventType === 'string' ? r.eventType : '';
521
+ const refKeys = EVENT_DATA_REF_FIELDS[eventType] ?? [];
522
+ const ed = r.eventData as Record<string, unknown>;
523
+ for (const key of refKeys) {
524
+ if (key in ed && isEncryptedMarker(ed[key])) return true;
525
+ }
526
+ }
527
+
528
+ return false;
529
+ }
@@ -0,0 +1,48 @@
1
+ export interface DecodedStreamChunkSource {
2
+ type: string;
3
+ encoding: 'utf-8';
4
+ rawSummary: string;
5
+ }
6
+
7
+ export interface FormattedStreamChunkDisplay {
8
+ text: string;
9
+ decodedFrom?: DecodedStreamChunkSource;
10
+ }
11
+
12
+ export function summarizeArrayBufferView(value: ArrayBufferView): string {
13
+ const ta = value as unknown as {
14
+ length: number;
15
+ constructor: { name: string };
16
+ } & ArrayLike<number>;
17
+ const name = ta.constructor.name;
18
+ const preview = Array.from(
19
+ { length: Math.min(ta.length, 8) },
20
+ (_, i) => ta[i]
21
+ );
22
+ const suffix = ta.length > 8 ? ', …' : '';
23
+ return `${name}(${ta.length}) [${preview.join(', ')}${suffix}]`;
24
+ }
25
+
26
+ /**
27
+ * Decode a typed array as UTF-8 text when valid, otherwise return a compact
28
+ * raw-byte summary. Used by `DataInspector`'s `collapseRefs` pipeline so
29
+ * hydrated `Uint8Array` chunks (e.g. AI SDK text deltas) render as readable
30
+ * text while still exposing the underlying byte layout.
31
+ */
32
+ export function formatArrayBufferViewForDisplay(
33
+ value: ArrayBufferView
34
+ ): FormattedStreamChunkDisplay {
35
+ try {
36
+ const text = new TextDecoder('utf-8', { fatal: true }).decode(value);
37
+ return {
38
+ text,
39
+ decodedFrom: {
40
+ type: value.constructor.name,
41
+ encoding: 'utf-8',
42
+ rawSummary: summarizeArrayBufferView(value),
43
+ },
44
+ };
45
+ } catch {
46
+ return { text: summarizeArrayBufferView(value) };
47
+ }
48
+ }
@@ -117,6 +117,7 @@ function buildSpans(
117
117
  // unknown territory. Even when the run is completed, we may not have loaded
118
118
  // all events yet, so always use the latest event we've actually seen.
119
119
  const childMaxEnd = latestKnownTime;
120
+ const runMaxEnd = run.completedAt ?? now;
120
121
 
121
122
  const stepSpans = Array.from(groupedEvents.eventsByStepId.values())
122
123
  .map((events) => stepToSpan(events, childMaxEnd))
@@ -127,7 +128,7 @@ function buildSpans(
127
128
  .filter((span): span is Span => span !== null);
128
129
 
129
130
  const waitSpans = Array.from(groupedEvents.timerEvents.values())
130
- .map((events) => waitToSpan(events, childMaxEnd))
131
+ .map((events) => waitToSpan(events, childMaxEnd, runMaxEnd))
131
132
  .filter((span): span is Span => span !== null);
132
133
 
133
134
  return {
package/src/lib/utils.ts CHANGED
@@ -7,10 +7,6 @@ export function cn(...inputs: ClassValue[]) {
7
7
  return twMerge(clsx(inputs));
8
8
  }
9
9
 
10
- const durationFormatter = new Intl.NumberFormat(undefined, {
11
- maximumFractionDigits: 2,
12
- });
13
-
14
10
  const MS_IN_SECOND = 1000;
15
11
  const MS_IN_MINUTE = 60 * MS_IN_SECOND;
16
12
  const MS_IN_HOUR = 60 * MS_IN_MINUTE;
@@ -20,52 +16,53 @@ const MS_IN_DAY = 24 * MS_IN_HOUR;
20
16
  * Formats a duration in milliseconds to a human-readable string.
21
17
  *
22
18
  * @param ms - Duration in milliseconds
23
- * @param compact - If true, returns a single-unit format (e.g., "45s", "2.5m").
24
- * If false (default), returns multi-part format (e.g., "1h 30m", "2d 5h").
19
+ * @param compact - If true, returns a compact format (e.g., "380ms", "2m 30s").
20
+ * If false (default), returns multi-part format (e.g., "1m 13s", "2d 5h 3m 12s").
25
21
  *
26
- * Compact format:
27
- * - < 1s: shows milliseconds (e.g., "500ms")
22
+ * Compact format (timeline markers):
23
+ * - < 1s: shows milliseconds (e.g., "380ms")
28
24
  * - < 1m: shows seconds (e.g., "45s")
29
- * - < 1h: shows minutes (e.g., "45m")
30
- * - >= 1h: shows hours (e.g., "2.5h")
25
+ * - < 1h: shows minutes and seconds (e.g., "2m 30s")
26
+ * - >= 1h: shows hours and minutes (e.g., "2h 30m")
31
27
  *
32
28
  * Full format:
33
- * - < 1s: shows milliseconds (e.g., "500ms")
34
- * - < 1m: shows seconds (e.g., "45.5s")
35
- * - >= 1m: shows human-readable format (e.g., "1h 30m", "2d 5h")
29
+ * - < 1s: shows milliseconds (e.g., "380ms")
30
+ * - < 1m: shows seconds (e.g., "45s")
31
+ * - >= 1m: shows decomposed format with whole seconds (e.g., "1m 13s", "2m 13s")
36
32
  */
37
33
  export function formatDuration(ms: number, compact = false): string {
38
34
  if (ms === 0) {
39
- return '0';
35
+ return '0s';
40
36
  }
41
37
 
42
- // For durations less than 1 second, show milliseconds
43
38
  if (ms < MS_IN_SECOND) {
44
- return `${durationFormatter.format(ms)}ms`;
39
+ const roundedMs = Math.round(ms);
40
+ return roundedMs < MS_IN_SECOND ? `${roundedMs}ms` : '1s';
45
41
  }
46
42
 
47
- // For durations less than 1 minute, show seconds
48
- if (ms < MS_IN_MINUTE) {
49
- return `${durationFormatter.format(ms / MS_IN_SECOND)}s`;
43
+ const roundedMs = Math.round(ms / MS_IN_SECOND) * MS_IN_SECOND;
44
+
45
+ if (roundedMs < MS_IN_MINUTE) {
46
+ return `${Math.floor(roundedMs / MS_IN_SECOND)}s`;
50
47
  }
51
48
 
52
49
  // Compact format: multi-unit without decimals (e.g. "8m 20s", "2h 30m")
53
50
  if (compact) {
54
- if (ms < MS_IN_HOUR) {
55
- const m = Math.floor(ms / MS_IN_MINUTE);
56
- const s = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);
51
+ if (roundedMs < MS_IN_HOUR) {
52
+ const m = Math.floor(roundedMs / MS_IN_MINUTE);
53
+ const s = Math.floor((roundedMs % MS_IN_MINUTE) / MS_IN_SECOND);
57
54
  return s > 0 ? `${m}m ${s}s` : `${m}m`;
58
55
  }
59
- const h = Math.floor(ms / MS_IN_HOUR);
60
- const m = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);
56
+ const h = Math.floor(roundedMs / MS_IN_HOUR);
57
+ const m = Math.floor((roundedMs % MS_IN_HOUR) / MS_IN_MINUTE);
61
58
  return m > 0 ? `${h}h ${m}m` : `${h}h`;
62
59
  }
63
60
 
64
- // Full format: human-readable multi-part
65
- const days = Math.floor(ms / MS_IN_DAY);
66
- const hours = Math.floor((ms % MS_IN_DAY) / MS_IN_HOUR);
67
- const minutes = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);
68
- const seconds = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);
61
+ // Full format: decompose into larger units + whole seconds.
62
+ const days = Math.floor(roundedMs / MS_IN_DAY);
63
+ const hours = Math.floor((roundedMs % MS_IN_DAY) / MS_IN_HOUR);
64
+ const minutes = Math.floor((roundedMs % MS_IN_HOUR) / MS_IN_MINUTE);
65
+ const seconds = Math.floor((roundedMs % MS_IN_MINUTE) / MS_IN_SECOND);
69
66
 
70
67
  const parts: string[] = [];
71
68
 
@@ -78,9 +75,7 @@ export function formatDuration(ms: number, compact = false): string {
78
75
  if (minutes > 0) {
79
76
  parts.push(`${minutes}m`);
80
77
  }
81
- if (hours <= 1 && (seconds > 0 || parts.length === 0)) {
82
- parts.push(`${seconds}s`);
83
- }
78
+ parts.push(`${seconds}s`);
84
79
 
85
80
  return parts.join(' ');
86
81
  }