@workflow/web-shared 5.0.0-beta.1 → 5.0.0-beta.11

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 (298) 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 +219 -125
  6. package/dist/components/hook-actions.js +1 -1
  7. package/dist/components/index.d.ts +5 -1
  8. package/dist/components/index.d.ts.map +1 -1
  9. package/dist/components/index.js +6 -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 +44 -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 +15 -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 +105 -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 +221 -0
  34. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts +2 -0
  35. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
  36. package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +19 -0
  37. package/dist/components/new-trace-viewer/components/use-row-window.d.ts +20 -0
  38. package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +1 -0
  39. package/dist/components/new-trace-viewer/components/use-row-window.js +69 -0
  40. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  41. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  42. package/dist/components/new-trace-viewer/context.js +47 -0
  43. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  44. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  45. package/dist/components/new-trace-viewer/detail-panel.js +13 -0
  46. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  47. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  48. package/dist/components/new-trace-viewer/icons.js +15 -0
  49. package/dist/components/new-trace-viewer/search.d.ts +19 -0
  50. package/dist/components/new-trace-viewer/search.d.ts.map +1 -0
  51. package/dist/components/new-trace-viewer/search.js +134 -0
  52. package/dist/components/new-trace-viewer/trace-viewer.d.ts +11 -0
  53. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  54. package/dist/components/new-trace-viewer/trace-viewer.js +400 -0
  55. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  56. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  57. package/dist/components/new-trace-viewer/types.js +2 -0
  58. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  59. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  60. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  61. package/dist/components/new-trace-viewer/utils.d.ts +46 -0
  62. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  63. package/dist/components/new-trace-viewer/utils.js +330 -0
  64. package/dist/components/run-trace-view.d.ts +2 -1
  65. package/dist/components/run-trace-view.d.ts.map +1 -1
  66. package/dist/components/run-trace-view.js +3 -3
  67. package/dist/components/sidebar/attribute-panel.d.ts +7 -1
  68. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  69. package/dist/components/sidebar/attribute-panel.js +131 -70
  70. package/dist/components/sidebar/conversation-view.js +1 -1
  71. package/dist/components/sidebar/copyable-data-block.d.ts +1 -0
  72. package/dist/components/sidebar/copyable-data-block.d.ts.map +1 -1
  73. package/dist/components/sidebar/copyable-data-block.js +16 -15
  74. package/dist/components/sidebar/detail-card.d.ts +5 -6
  75. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  76. package/dist/components/sidebar/detail-card.js +32 -16
  77. package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
  78. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  79. package/dist/components/sidebar/entity-detail-panel.js +20 -37
  80. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  81. package/dist/components/sidebar/events-list.js +21 -15
  82. package/dist/components/sidebar/resolve-hook-modal.js +1 -1
  83. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  84. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  85. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  86. package/dist/components/stream-viewer.d.ts +1 -1
  87. package/dist/components/stream-viewer.d.ts.map +1 -1
  88. package/dist/components/stream-viewer.js +3 -27
  89. package/dist/components/trace-viewer/components/map.js +1 -1
  90. package/dist/components/trace-viewer/components/markers.js +1 -1
  91. package/dist/components/trace-viewer/components/node.js +1 -1
  92. package/dist/components/trace-viewer/components/search-input.js +1 -1
  93. package/dist/components/trace-viewer/components/search.js +1 -1
  94. package/dist/components/trace-viewer/components/span-content.js +1 -1
  95. package/dist/components/trace-viewer/components/span-detail-panel.js +1 -1
  96. package/dist/components/trace-viewer/components/span-segments.js +1 -1
  97. package/dist/components/trace-viewer/components/span-strategies.js +1 -1
  98. package/dist/components/trace-viewer/components/ui.js +1 -1
  99. package/dist/components/trace-viewer/components/zoom-button.js +1 -1
  100. package/dist/components/trace-viewer/components/zoom-icons.js +1 -1
  101. package/dist/components/trace-viewer/context.js +1 -1
  102. package/dist/components/trace-viewer/index.js +1 -1
  103. package/dist/components/trace-viewer/trace-viewer.js +1 -1
  104. package/dist/components/trace-viewer/types.js +1 -1
  105. package/dist/components/trace-viewer/util/constants.js +1 -1
  106. package/dist/components/trace-viewer/util/scrollbar-width.js +1 -1
  107. package/dist/components/trace-viewer/util/timing.d.ts +1 -1
  108. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  109. package/dist/components/trace-viewer/util/timing.js +6 -9
  110. package/dist/components/trace-viewer/util/tree.js +1 -1
  111. package/dist/components/trace-viewer/util/use-immediate-style.js +1 -1
  112. package/dist/components/trace-viewer/util/use-streaming-spans.js +1 -1
  113. package/dist/components/trace-viewer/util/use-trackpad-zoom.js +1 -1
  114. package/dist/components/trace-viewer/worker.js +1 -1
  115. package/dist/components/trace-viewer-new.d.ts +14 -0
  116. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  117. package/dist/components/trace-viewer-new.js +22 -0
  118. package/dist/components/ui/alert.js +1 -1
  119. package/dist/components/ui/button.d.ts +10 -0
  120. package/dist/components/ui/button.d.ts.map +1 -0
  121. package/dist/components/ui/button.js +46 -0
  122. package/dist/components/ui/card.js +1 -1
  123. package/dist/components/ui/data-inspector.d.ts +39 -1
  124. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  125. package/dist/components/ui/data-inspector.js +207 -22
  126. package/dist/components/ui/decrypt-button.js +1 -1
  127. package/dist/components/ui/error-card.js +1 -1
  128. package/dist/components/ui/error-stack-block.d.ts.map +1 -1
  129. package/dist/components/ui/error-stack-block.js +27 -19
  130. package/dist/components/ui/icon-button.d.ts +19 -0
  131. package/dist/components/ui/icon-button.d.ts.map +1 -0
  132. package/dist/components/ui/icon-button.js +29 -0
  133. package/dist/components/ui/inspector-theme.d.ts +9 -10
  134. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  135. package/dist/components/ui/inspector-theme.js +39 -72
  136. package/dist/components/ui/kbd.d.ts +6 -0
  137. package/dist/components/ui/kbd.d.ts.map +1 -0
  138. package/dist/components/ui/kbd.js +6 -0
  139. package/dist/components/ui/load-more-button.js +1 -1
  140. package/dist/components/ui/menu-dropdown.js +1 -1
  141. package/dist/components/ui/skeleton.js +1 -1
  142. package/dist/components/ui/spinner.js +1 -1
  143. package/dist/components/ui/timestamp-tooltip.js +1 -1
  144. package/dist/components/ui/tooltip.d.ts +10 -0
  145. package/dist/components/ui/tooltip.d.ts.map +1 -0
  146. package/dist/components/ui/tooltip.js +12 -0
  147. package/dist/components/workflow-trace-view.d.ts +3 -1
  148. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  149. package/dist/components/workflow-trace-view.js +10 -5
  150. package/dist/components/workflow-traces/event-colors.js +1 -1
  151. package/dist/components/workflow-traces/trace-colors.js +1 -1
  152. package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
  153. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  154. package/dist/components/workflow-traces/trace-span-construction.js +19 -6
  155. package/dist/components/workflow-traces/trace-time-utils.js +1 -1
  156. package/dist/hooks/use-dark-mode.js +1 -1
  157. package/dist/hooks/use-intersection-observer.d.ts +17 -0
  158. package/dist/hooks/use-intersection-observer.d.ts.map +1 -0
  159. package/dist/hooks/use-intersection-observer.js +29 -0
  160. package/dist/hooks/use-load-more-on-scroll.d.ts +21 -0
  161. package/dist/hooks/use-load-more-on-scroll.d.ts.map +1 -0
  162. package/dist/hooks/use-load-more-on-scroll.js +28 -0
  163. package/dist/hooks/use-reduced-motion.d.ts +10 -0
  164. package/dist/hooks/use-reduced-motion.d.ts.map +1 -0
  165. package/dist/hooks/use-reduced-motion.js +29 -0
  166. package/dist/index.d.ts +3 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +3 -2
  169. package/dist/lib/event-analysis.js +1 -1
  170. package/dist/lib/event-materialization.js +1 -1
  171. package/dist/lib/exact-event-search-id.d.ts +25 -0
  172. package/dist/lib/exact-event-search-id.d.ts.map +1 -0
  173. package/dist/lib/exact-event-search-id.js +39 -0
  174. package/dist/lib/hydration.d.ts +14 -1
  175. package/dist/lib/hydration.d.ts.map +1 -1
  176. package/dist/lib/hydration.js +157 -4
  177. package/dist/lib/stream-display.d.ts +18 -0
  178. package/dist/lib/stream-display.d.ts.map +1 -0
  179. package/dist/lib/stream-display.js +30 -0
  180. package/dist/lib/toast.js +1 -1
  181. package/dist/lib/trace-builder.d.ts.map +1 -1
  182. package/dist/lib/trace-builder.js +3 -2
  183. package/dist/lib/utils.d.ts +9 -9
  184. package/dist/lib/utils.d.ts.map +1 -1
  185. package/dist/lib/utils.js +27 -32
  186. package/dist/styles.css +475 -1
  187. package/package.json +6 -4
  188. package/src/components/event-list-view.tsx +421 -262
  189. package/src/components/index.ts +13 -1
  190. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  191. package/src/components/new-trace-viewer/components/event-list.tsx +135 -0
  192. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  193. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  194. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  195. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  196. package/src/components/new-trace-viewer/components/split-pane.tsx +198 -0
  197. package/src/components/new-trace-viewer/components/timeline.tsx +560 -0
  198. package/src/components/new-trace-viewer/components/trace-viewer-skeleton.tsx +94 -0
  199. package/src/components/new-trace-viewer/components/use-row-window.ts +93 -0
  200. package/src/components/new-trace-viewer/context.tsx +90 -0
  201. package/src/components/new-trace-viewer/detail-panel.tsx +55 -0
  202. package/src/components/new-trace-viewer/icons.tsx +79 -0
  203. package/src/components/new-trace-viewer/search.ts +178 -0
  204. package/src/components/new-trace-viewer/trace-viewer.tsx +762 -0
  205. package/src/components/new-trace-viewer/types.ts +0 -0
  206. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  207. package/src/components/new-trace-viewer/utils.ts +482 -0
  208. package/src/components/run-trace-view.tsx +3 -0
  209. package/src/components/sidebar/attribute-panel.tsx +240 -196
  210. package/src/components/sidebar/copyable-data-block.tsx +49 -27
  211. package/src/components/sidebar/detail-card.tsx +119 -59
  212. package/src/components/sidebar/entity-detail-panel.tsx +92 -148
  213. package/src/components/sidebar/events-list.tsx +51 -76
  214. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  215. package/src/components/stream-viewer.tsx +18 -40
  216. package/src/components/trace-viewer/util/timing.ts +5 -9
  217. package/src/components/trace-viewer-new.tsx +57 -0
  218. package/src/components/ui/button.tsx +68 -0
  219. package/src/components/ui/data-inspector.tsx +427 -31
  220. package/src/components/ui/error-stack-block.tsx +39 -30
  221. package/src/components/ui/icon-button.tsx +53 -0
  222. package/src/components/ui/inspector-theme.ts +39 -81
  223. package/src/components/ui/kbd.tsx +21 -0
  224. package/src/components/ui/tooltip.tsx +47 -0
  225. package/src/components/workflow-trace-view.tsx +12 -2
  226. package/src/components/workflow-traces/trace-span-construction.ts +24 -6
  227. package/src/hooks/use-intersection-observer.ts +57 -0
  228. package/src/hooks/use-load-more-on-scroll.ts +49 -0
  229. package/src/hooks/use-reduced-motion.ts +34 -0
  230. package/src/index.ts +9 -0
  231. package/src/lib/exact-event-search-id.ts +67 -0
  232. package/src/lib/hydration.ts +169 -1
  233. package/src/lib/stream-display.ts +48 -0
  234. package/src/lib/trace-builder.ts +2 -1
  235. package/src/lib/utils.ts +27 -32
  236. package/src/styles.css +475 -1
  237. package/dist/components/error-boundary.js.map +0 -1
  238. package/dist/components/event-list-view.js.map +0 -1
  239. package/dist/components/hook-actions.js.map +0 -1
  240. package/dist/components/index.js.map +0 -1
  241. package/dist/components/run-trace-view.js.map +0 -1
  242. package/dist/components/sidebar/attribute-panel.js.map +0 -1
  243. package/dist/components/sidebar/conversation-view.js.map +0 -1
  244. package/dist/components/sidebar/copyable-data-block.js.map +0 -1
  245. package/dist/components/sidebar/detail-card.js.map +0 -1
  246. package/dist/components/sidebar/entity-detail-panel.js.map +0 -1
  247. package/dist/components/sidebar/events-list.js.map +0 -1
  248. package/dist/components/sidebar/resolve-hook-modal.js.map +0 -1
  249. package/dist/components/stream-viewer.js.map +0 -1
  250. package/dist/components/trace-viewer/components/map.js.map +0 -1
  251. package/dist/components/trace-viewer/components/markers.js.map +0 -1
  252. package/dist/components/trace-viewer/components/node.js.map +0 -1
  253. package/dist/components/trace-viewer/components/search-input.js.map +0 -1
  254. package/dist/components/trace-viewer/components/search.js.map +0 -1
  255. package/dist/components/trace-viewer/components/span-content.js.map +0 -1
  256. package/dist/components/trace-viewer/components/span-detail-panel.js.map +0 -1
  257. package/dist/components/trace-viewer/components/span-segments.js.map +0 -1
  258. package/dist/components/trace-viewer/components/span-strategies.js.map +0 -1
  259. package/dist/components/trace-viewer/components/ui.js.map +0 -1
  260. package/dist/components/trace-viewer/components/zoom-button.js.map +0 -1
  261. package/dist/components/trace-viewer/components/zoom-icons.js.map +0 -1
  262. package/dist/components/trace-viewer/context.js.map +0 -1
  263. package/dist/components/trace-viewer/index.js.map +0 -1
  264. package/dist/components/trace-viewer/trace-viewer.js.map +0 -1
  265. package/dist/components/trace-viewer/types.js.map +0 -1
  266. package/dist/components/trace-viewer/util/constants.js.map +0 -1
  267. package/dist/components/trace-viewer/util/scrollbar-width.js.map +0 -1
  268. package/dist/components/trace-viewer/util/timing.js.map +0 -1
  269. package/dist/components/trace-viewer/util/tree.js.map +0 -1
  270. package/dist/components/trace-viewer/util/use-immediate-style.js.map +0 -1
  271. package/dist/components/trace-viewer/util/use-streaming-spans.js.map +0 -1
  272. package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
  273. package/dist/components/trace-viewer/worker.js.map +0 -1
  274. package/dist/components/ui/alert.js.map +0 -1
  275. package/dist/components/ui/card.js.map +0 -1
  276. package/dist/components/ui/data-inspector.js.map +0 -1
  277. package/dist/components/ui/decrypt-button.js.map +0 -1
  278. package/dist/components/ui/error-card.js.map +0 -1
  279. package/dist/components/ui/error-stack-block.js.map +0 -1
  280. package/dist/components/ui/inspector-theme.js.map +0 -1
  281. package/dist/components/ui/load-more-button.js.map +0 -1
  282. package/dist/components/ui/menu-dropdown.js.map +0 -1
  283. package/dist/components/ui/skeleton.js.map +0 -1
  284. package/dist/components/ui/spinner.js.map +0 -1
  285. package/dist/components/ui/timestamp-tooltip.js.map +0 -1
  286. package/dist/components/workflow-trace-view.js.map +0 -1
  287. package/dist/components/workflow-traces/event-colors.js.map +0 -1
  288. package/dist/components/workflow-traces/trace-colors.js.map +0 -1
  289. package/dist/components/workflow-traces/trace-span-construction.js.map +0 -1
  290. package/dist/components/workflow-traces/trace-time-utils.js.map +0 -1
  291. package/dist/hooks/use-dark-mode.js.map +0 -1
  292. package/dist/index.js.map +0 -1
  293. package/dist/lib/event-analysis.js.map +0 -1
  294. package/dist/lib/event-materialization.js.map +0 -1
  295. package/dist/lib/hydration.js.map +0 -1
  296. package/dist/lib/toast.js.map +0 -1
  297. package/dist/lib/trace-builder.js.map +0 -1
  298. package/dist/lib/utils.js.map +0 -1
@@ -0,0 +1,53 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+ import * as React from 'react';
3
+ import { cn } from '../../lib/utils';
4
+
5
+ const iconButtonVariants = cva(
6
+ [
7
+ 'm-0 inline-flex shrink-0 appearance-none items-center justify-center border-0 bg-transparent p-0 align-baseline font-inherit no-underline [background:none] [-webkit-appearance:none] [-webkit-tap-highlight-color:transparent]',
8
+ 'transition-colors duration-150 ease-in-out',
9
+ 'enabled:cursor-pointer disabled:cursor-not-allowed disabled:opacity-40',
10
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--ds-focus-color)] focus-visible:outline-offset-2',
11
+ ],
12
+ {
13
+ variants: {
14
+ variant: {
15
+ tertiary:
16
+ 'text-gray-900 enabled:hover:bg-gray-alpha-200 enabled:hover:text-gray-1000 enabled:active:bg-gray-alpha-200 focus-visible:text-gray-1000',
17
+ muted:
18
+ 'text-gray-900 enabled:hover:bg-gray-alpha-200 enabled:hover:text-gray-1000 enabled:active:bg-gray-alpha-200 focus-visible:text-gray-1000',
19
+ },
20
+ size: {
21
+ tiny: 'h-6 w-6 rounded-[4px] [&_svg]:h-4 [&_svg]:w-4',
22
+ small: 'h-8 w-8 rounded-md [&_svg]:h-4 [&_svg]:w-4',
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: 'tertiary',
27
+ size: 'tiny',
28
+ },
29
+ }
30
+ );
31
+
32
+ export type IconButtonProps = Omit<
33
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
34
+ 'type'
35
+ > &
36
+ VariantProps<typeof iconButtonVariants> & {
37
+ 'aria-label': string;
38
+ type?: 'button' | 'submit' | 'reset';
39
+ };
40
+
41
+ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
42
+ ({ className, variant, size, type = 'button', ...props }, ref) => (
43
+ <button
44
+ ref={ref}
45
+ type={type}
46
+ className={cn(iconButtonVariants({ variant, size, className }))}
47
+ {...props}
48
+ />
49
+ )
50
+ );
51
+ IconButton.displayName = 'IconButton';
52
+
53
+ export { iconButtonVariants };
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * Shared theme configuration for react-inspector's ObjectInspector.
3
3
  *
4
- * Color choices are inspired by Node.js `util.inspect()` ANSI stylizing:
5
- * - property names: unstyled (default foreground dark text on light, light text on dark)
6
- * - strings / symbols: green
7
- * - numbers / booleans / bigint: yellow
8
- * - null: bold (foreground)
9
- * - undefined: grey
10
- * - regexp: red
11
- * - functions (special): cyan
12
- * - date: magenta
4
+ * Colors follow Geist's Shiki JSON palette so the inspector reads the same
5
+ * as highlighted code blocks across the product:
6
+ * - property names / punctuation: --ds-gray-1000 (default foreground)
7
+ * - strings / numbers / booleans: --ds-green-900
8
+ * - null / undefined: --ds-gray-900 (muted)
9
+ * - regexp / function: --ds-purple-900
10
+ * - date: --ds-pink-900
13
11
  *
14
- * See: https://github.com/nodejs/node/blob/main/lib/internal/util/inspect.js
12
+ * Because the `--ds-*` tokens adapt to theme automatically, the light and
13
+ * dark objects are intentionally identical.
15
14
  */
16
15
 
17
16
  // ---------------------------------------------------------------------------
@@ -25,11 +24,11 @@ export interface InspectorThemeExtended {
25
24
  }
26
25
 
27
26
  export const inspectorThemeExtendedLight: InspectorThemeExtended = {
28
- OBJECT_VALUE_DATE_COLOR: '#a21caf', // fuchsia-700
27
+ OBJECT_VALUE_DATE_COLOR: 'var(--ds-pink-900)',
29
28
  };
30
29
 
31
30
  export const inspectorThemeExtendedDark: InspectorThemeExtended = {
32
- OBJECT_VALUE_DATE_COLOR: '#e879f9', // fuchsia-400
31
+ OBJECT_VALUE_DATE_COLOR: 'var(--ds-pink-900)',
33
32
  };
34
33
 
35
34
  // ---------------------------------------------------------------------------
@@ -57,96 +56,55 @@ const shared = {
57
56
  // Light theme
58
57
  // ---------------------------------------------------------------------------
59
58
 
60
- export const inspectorThemeLight = {
59
+ const geistTheme = {
61
60
  ...shared,
62
61
 
63
62
  // Base text
64
63
  BASE_COLOR: 'var(--ds-gray-1000)',
65
64
 
66
- // Property names — unstyled, same as base foreground (Node: no style)
67
- OBJECT_NAME_COLOR: 'var(--ds-gray-900)',
65
+ // Property names — default foreground (matches JSON key color in Geist Shiki)
66
+ OBJECT_NAME_COLOR: 'var(--ds-gray-1000)',
68
67
 
69
- // Strings & symbols — green (Node: 'green')
70
- OBJECT_VALUE_STRING_COLOR: '#16a34a', // green-600
71
- OBJECT_VALUE_SYMBOL_COLOR: '#16a34a',
68
+ // Strings & symbols — green
69
+ OBJECT_VALUE_STRING_COLOR: 'var(--ds-green-900)',
70
+ OBJECT_VALUE_SYMBOL_COLOR: 'var(--ds-green-900)',
72
71
 
73
- // Numbers & booleans — yellow/amber (Node: 'yellow')
74
- OBJECT_VALUE_NUMBER_COLOR: '#b45309', // amber-700 (readable on white)
75
- OBJECT_VALUE_BOOLEAN_COLOR: '#b45309',
72
+ // Numbers & booleans — green (Geist JSON tokens)
73
+ OBJECT_VALUE_NUMBER_COLOR: 'var(--ds-green-900)',
74
+ OBJECT_VALUE_BOOLEAN_COLOR: 'var(--ds-green-900)',
76
75
 
77
- // null — bold foreground (Node: 'bold')
76
+ // null — muted foreground
78
77
  OBJECT_VALUE_NULL_COLOR: 'var(--ds-gray-900)',
79
78
 
80
- // undefined — grey (Node: 'grey')
81
- OBJECT_VALUE_UNDEFINED_COLOR: 'var(--ds-gray-500)',
79
+ // undefined — muted foreground
80
+ OBJECT_VALUE_UNDEFINED_COLOR: 'var(--ds-gray-900)',
82
81
 
83
- // RegExp — red (Node regexp base uses green/red/yellow palette)
84
- OBJECT_VALUE_REGEXP_COLOR: '#dc2626', // red-600
82
+ // RegExp — purple
83
+ OBJECT_VALUE_REGEXP_COLOR: 'var(--ds-purple-900)',
85
84
 
86
- // Functions — cyan (Node: 'special' → 'cyan')
87
- OBJECT_VALUE_FUNCTION_PREFIX_COLOR: '#0891b2', // cyan-600
85
+ // Functions — purple
86
+ OBJECT_VALUE_FUNCTION_PREFIX_COLOR: 'var(--ds-purple-900)',
88
87
 
89
- // HTML (less relevant for data inspection, but reasonable defaults)
90
- HTML_TAG_COLOR: 'var(--ds-gray-500)',
91
- HTML_TAGNAME_COLOR: '#0891b2',
92
- HTML_ATTRIBUTE_NAME_COLOR: '#b45309',
93
- HTML_ATTRIBUTE_VALUE_COLOR: '#16a34a',
94
- HTML_COMMENT_COLOR: 'var(--ds-gray-400)',
95
- HTML_DOCTYPE_COLOR: 'var(--ds-gray-400)',
88
+ // HTML (rarely used here, kept consistent with the palette)
89
+ HTML_TAG_COLOR: 'var(--ds-gray-900)',
90
+ HTML_TAGNAME_COLOR: 'var(--ds-blue-900)',
91
+ HTML_ATTRIBUTE_NAME_COLOR: 'var(--ds-amber-900)',
92
+ HTML_ATTRIBUTE_VALUE_COLOR: 'var(--ds-green-900)',
93
+ HTML_COMMENT_COLOR: 'var(--ds-gray-700)',
94
+ HTML_DOCTYPE_COLOR: 'var(--ds-gray-700)',
96
95
 
97
96
  // Structural
98
- ARROW_COLOR: 'var(--ds-gray-500)',
97
+ ARROW_COLOR: 'var(--ds-gray-700)',
99
98
  TABLE_BORDER_COLOR: 'var(--ds-gray-300)',
100
99
  TABLE_TH_BACKGROUND_COLOR: 'var(--ds-gray-100)',
101
100
  TABLE_TH_HOVER_COLOR: 'var(--ds-gray-200)',
102
- TABLE_SORT_ICON_COLOR: 'var(--ds-gray-500)',
101
+ TABLE_SORT_ICON_COLOR: 'var(--ds-gray-700)',
103
102
  };
104
103
 
104
+ export const inspectorThemeLight = geistTheme;
105
+
105
106
  // ---------------------------------------------------------------------------
106
107
  // Dark theme
107
108
  // ---------------------------------------------------------------------------
108
109
 
109
- export const inspectorThemeDark = {
110
- ...shared,
111
-
112
- // Base text
113
- BASE_COLOR: 'var(--ds-gray-1000)',
114
-
115
- // Property names — white/light foreground (Node: unstyled = white in dark terminal)
116
- OBJECT_NAME_COLOR: 'var(--ds-gray-900)',
117
-
118
- // Strings & symbols — green (Node: 'green')
119
- OBJECT_VALUE_STRING_COLOR: '#4ade80', // green-400
120
- OBJECT_VALUE_SYMBOL_COLOR: '#4ade80',
121
-
122
- // Numbers & booleans — yellow (Node: 'yellow')
123
- OBJECT_VALUE_NUMBER_COLOR: '#facc15', // yellow-400
124
- OBJECT_VALUE_BOOLEAN_COLOR: '#facc15',
125
-
126
- // null — bold foreground / white (Node: 'bold')
127
- OBJECT_VALUE_NULL_COLOR: 'var(--ds-gray-1000)',
128
-
129
- // undefined — grey (Node: 'grey')
130
- OBJECT_VALUE_UNDEFINED_COLOR: 'var(--ds-gray-500)',
131
-
132
- // RegExp — red (Node regexp palette)
133
- OBJECT_VALUE_REGEXP_COLOR: '#f87171', // red-400
134
-
135
- // Functions — cyan (Node: 'special' → 'cyan')
136
- OBJECT_VALUE_FUNCTION_PREFIX_COLOR: '#22d3ee', // cyan-400
137
-
138
- // HTML
139
- HTML_TAG_COLOR: 'var(--ds-gray-500)',
140
- HTML_TAGNAME_COLOR: '#22d3ee',
141
- HTML_ATTRIBUTE_NAME_COLOR: '#facc15',
142
- HTML_ATTRIBUTE_VALUE_COLOR: '#4ade80',
143
- HTML_COMMENT_COLOR: 'var(--ds-gray-500)',
144
- HTML_DOCTYPE_COLOR: 'var(--ds-gray-500)',
145
-
146
- // Structural
147
- ARROW_COLOR: 'var(--ds-gray-500)',
148
- TABLE_BORDER_COLOR: 'var(--ds-gray-300)',
149
- TABLE_TH_BACKGROUND_COLOR: 'var(--ds-gray-100)',
150
- TABLE_TH_HOVER_COLOR: 'var(--ds-gray-200)',
151
- TABLE_SORT_ICON_COLOR: 'var(--ds-gray-500)',
152
- };
110
+ export const inspectorThemeDark = geistTheme;
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from 'react';
2
+ import { cn } from '../../lib/utils';
3
+
4
+ export function Kbd({
5
+ children,
6
+ className,
7
+ }: {
8
+ children: ReactNode;
9
+ className?: string;
10
+ }): ReactNode {
11
+ return (
12
+ <kbd
13
+ className={cn(
14
+ 'inline-flex h-5 min-w-5 items-center justify-center rounded bg-background-100/20 px-1 text-center font-sans text-xs leading-none',
15
+ className
16
+ )}
17
+ >
18
+ {children}
19
+ </kbd>
20
+ );
21
+ }
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+
3
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
4
+ import * as React from 'react';
5
+ import { cn } from '../../lib/utils';
6
+
7
+ const TooltipProvider = TooltipPrimitive.Provider;
8
+
9
+ const Tooltip = TooltipPrimitive.Root;
10
+
11
+ const TooltipTrigger = TooltipPrimitive.Trigger;
12
+
13
+ const TooltipContent = React.forwardRef<
14
+ React.ElementRef<typeof TooltipPrimitive.Content>,
15
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & {
16
+ showArrow?: boolean;
17
+ }
18
+ >(
19
+ (
20
+ { className, sideOffset = 8, showArrow = true, children, ...props },
21
+ ref
22
+ ) => (
23
+ <TooltipPrimitive.Portal>
24
+ <TooltipPrimitive.Content
25
+ ref={ref}
26
+ sideOffset={sideOffset}
27
+ className={cn(
28
+ 'z-[99999] flex items-center gap-1 rounded-[10px] bg-gray-1000 px-2 py-1.5 !text-label-13 text-background-100 shadow-md select-none',
29
+ className
30
+ )}
31
+ {...props}
32
+ >
33
+ {children}
34
+ {showArrow ? (
35
+ <TooltipPrimitive.Arrow
36
+ width={11}
37
+ height={5}
38
+ className="fill-gray-1000"
39
+ />
40
+ ) : null}
41
+ </TooltipPrimitive.Content>
42
+ </TooltipPrimitive.Portal>
43
+ )
44
+ );
45
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
46
+
47
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
@@ -17,6 +17,7 @@ import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react';
17
17
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
18
18
  import { createPortal } from 'react-dom';
19
19
  import { useToast } from '../lib/toast';
20
+ import { buildTrace, type TraceWithMeta } from '../lib/trace-builder';
20
21
  import { ErrorBoundary } from './error-boundary';
21
22
  import {
22
23
  EntityDetailPanel,
@@ -36,7 +37,6 @@ import {
36
37
  getCustomSpanClassName,
37
38
  getCustomSpanEventClassName,
38
39
  } from './workflow-traces/trace-colors';
39
- import { buildTrace, type TraceWithMeta } from '../lib/trace-builder';
40
40
 
41
41
  /**
42
42
  * While a run is live, continuously grow root.duration and rescale so the
@@ -767,6 +767,7 @@ export const WorkflowTraceViewer = ({
767
767
  onResolveHook,
768
768
  onCancelRun,
769
769
  onStreamClick,
770
+ onRunClick,
770
771
  onSpanSelect,
771
772
  onLoadEventData,
772
773
  onLoadMoreSpans,
@@ -796,6 +797,8 @@ export const WorkflowTraceViewer = ({
796
797
  onCancelRun?: (runId: string) => Promise<void>;
797
798
  /** Callback when a stream reference is clicked in the detail panel */
798
799
  onStreamClick?: (streamId: string) => void;
800
+ /** Callback when a run reference is clicked in the detail panel */
801
+ onRunClick?: (runId: string) => void;
799
802
  /** Callback when a span is selected. */
800
803
  onSpanSelect?: (info: SpanSelectionInfo) => void;
801
804
  /** Callback to load event data for a specific event (lazy loading in sidebar) */
@@ -903,6 +906,12 @@ export const WorkflowTraceViewer = ({
903
906
  });
904
907
  }, [events, selectedSpan?.spanId]);
905
908
 
909
+ // Reset selected span when navigating to a different run
910
+ useEffect(() => {
911
+ setSelectedSpan(null);
912
+ setDeselectTrigger((n) => n + 1);
913
+ }, [run?.runId]);
914
+
906
915
  const handleClose = useCallback(() => {
907
916
  setSelectedSpan(null);
908
917
  setDeselectTrigger((n) => n + 1);
@@ -965,7 +974,7 @@ export const WorkflowTraceViewer = ({
965
974
  }
966
975
 
967
976
  return (
968
- <div className="relative w-full h-full flex">
977
+ <div className="relative w-full h-full flex flex-row">
969
978
  {/* Timeline (takes remaining space) */}
970
979
  <div className="flex-1 min-w-0 relative">
971
980
  <TraceViewerContextProvider
@@ -1140,6 +1149,7 @@ export const WorkflowTraceViewer = ({
1140
1149
  <EntityDetailPanel
1141
1150
  run={run}
1142
1151
  onStreamClick={onStreamClick}
1152
+ onRunClick={onRunClick}
1143
1153
  spanDetailData={spanDetailData ?? null}
1144
1154
  spanDetailError={spanDetailError}
1145
1155
  spanDetailLoading={spanDetailLoading}
@@ -59,7 +59,7 @@ export const waitEventsToWaitEntity = (
59
59
  waitId: string;
60
60
  runId: string;
61
61
  createdAt: Date;
62
- resumeAt: Date;
62
+ resumeAt?: Date;
63
63
  completedAt?: Date;
64
64
  } | null => {
65
65
  const startEvent = events.find((event) => event.eventType === 'wait_created');
@@ -73,7 +73,9 @@ export const waitEventsToWaitEntity = (
73
73
  waitId: startEvent.correlationId,
74
74
  runId: startEvent.runId,
75
75
  createdAt: startEvent.createdAt,
76
- resumeAt: startEvent.eventData?.resumeAt,
76
+ resumeAt: startEvent.eventData?.resumeAt
77
+ ? new Date(startEvent.eventData.resumeAt)
78
+ : undefined,
77
79
  completedAt: completedEvent?.createdAt,
78
80
  };
79
81
  };
@@ -81,13 +83,29 @@ export const waitEventsToWaitEntity = (
81
83
  /**
82
84
  * Converts a workflow Wait to an OpenTelemetry Span
83
85
  */
84
- export function waitToSpan(events: Event[], maxEndTime: Date): Span | null {
86
+ export function waitToSpan(
87
+ events: Event[],
88
+ maxEndTime: Date,
89
+ fallbackEndTime = maxEndTime
90
+ ): Span | null {
85
91
  const wait = waitEventsToWaitEntity(events);
86
92
  if (!wait) {
87
93
  return null;
88
94
  }
89
95
  const startTime = wait.createdAt;
90
- const endTime = wait.completedAt ?? maxEndTime;
96
+ const startMs = startTime.getTime();
97
+ let endTime = wait.completedAt;
98
+ if (!endTime) {
99
+ const fallbackCap =
100
+ wait.resumeAt && wait.resumeAt.getTime() < fallbackEndTime.getTime()
101
+ ? wait.resumeAt
102
+ : fallbackEndTime;
103
+ endTime =
104
+ maxEndTime.getTime() > startMs &&
105
+ maxEndTime.getTime() < fallbackCap.getTime()
106
+ ? maxEndTime
107
+ : fallbackCap;
108
+ }
91
109
  const start = dateToOtelTime(startTime);
92
110
  const end = dateToOtelTime(endTime);
93
111
  const duration = calculateDuration(startTime, endTime);
@@ -225,7 +243,7 @@ export function stepToSpan(stepEvents: Event[], maxEndTime: Date): Span | null {
225
243
 
226
244
  return {
227
245
  spanId: String(step.stepId),
228
- name: parsedName?.shortName ?? '',
246
+ name: parsedName?.shortName ?? String(step.stepName),
229
247
  kind: 1, // INTERNAL span kind
230
248
  resource,
231
249
  library: WORKFLOW_LIBRARY,
@@ -296,7 +314,7 @@ export function hookToSpan(hookEvents: Event[], maxEndTime: Date): Span | null {
296
314
 
297
315
  return {
298
316
  spanId: String(hook.hookId),
299
- name: String(hook.hookId),
317
+ name: hook.token ?? String(hook.hookId),
300
318
  kind: 1, // INTERNAL span kind
301
319
  resource: 'hook',
302
320
  library: WORKFLOW_LIBRARY,
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+
3
+ import type { RefObject } from 'react';
4
+ import { useEffect, useState } from 'react';
5
+
6
+ interface UseIntersectionObserverArgs
7
+ extends Omit<IntersectionObserverInit, 'root'> {
8
+ /**
9
+ * Scroll container the target is observed within. Defaults to the viewport.
10
+ * Passed as a ref so it resolves after commit (when the node exists).
11
+ */
12
+ rootRef?: RefObject<Element | null>;
13
+ freezeOnceVisible?: boolean;
14
+ }
15
+
16
+ /**
17
+ * Returns the latest IntersectionObserverEntry for `elementRef`.
18
+ * Ported from vercel/front's `@vercel/hooks` `useIntersectionObserver`, with
19
+ * `root` threaded as a ref so we can observe inside a custom scroll container.
20
+ */
21
+ export function useIntersectionObserver(
22
+ elementRef: RefObject<Element | null>,
23
+ {
24
+ threshold = 0,
25
+ rootRef,
26
+ rootMargin = '0%',
27
+ freezeOnceVisible = false,
28
+ }: UseIntersectionObserverArgs = {}
29
+ ): IntersectionObserverEntry | undefined {
30
+ const [entry, setEntry] = useState<IntersectionObserverEntry>();
31
+
32
+ const frozen = entry?.isIntersecting && freezeOnceVisible;
33
+
34
+ useEffect(() => {
35
+ const node = elementRef?.current;
36
+
37
+ const hasIOSupport = !!window.IntersectionObserver;
38
+
39
+ if (!hasIOSupport || frozen || !node) return;
40
+
41
+ const updateEntry = ([entry]: IntersectionObserverEntry[]): void => {
42
+ setEntry(entry);
43
+ };
44
+
45
+ const observer = new IntersectionObserver(updateEntry, {
46
+ threshold,
47
+ root: rootRef?.current ?? null,
48
+ rootMargin,
49
+ });
50
+
51
+ observer.observe(node);
52
+
53
+ return () => observer.disconnect();
54
+ }, [elementRef, threshold, rootRef, rootMargin, frozen]);
55
+
56
+ return entry;
57
+ }
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+
3
+ import type { RefObject } from 'react';
4
+ import { useEffect, useRef } from 'react';
5
+ import { useIntersectionObserver } from './use-intersection-observer';
6
+
7
+ interface UseLoadMoreOnScrollOptions {
8
+ hasMore: boolean;
9
+ isLoadingMore: boolean;
10
+ /** Scroll container the sentinel lives in. Defaults to the viewport. */
11
+ rootRef?: RefObject<Element | null>;
12
+ rootMargin?: string;
13
+ }
14
+
15
+ /**
16
+ * Triggers `loadMore` when a sentinel element scrolls into view.
17
+ * Returns a ref to attach to an invisible sentinel `<div>` placed after the
18
+ * list content. Ported from vercel/front's `@vercel/hooks`
19
+ * `useLoadMoreOnScroll`.
20
+ *
21
+ * Because it keys off `isIntersecting`/`isLoadingMore`, it self-continues:
22
+ * if the sentinel is still in view once a page finishes loading, the next
23
+ * page is requested automatically.
24
+ */
25
+ export function useLoadMoreOnScroll(
26
+ loadMore: () => void,
27
+ {
28
+ hasMore,
29
+ isLoadingMore,
30
+ rootRef,
31
+ rootMargin = '400px',
32
+ }: UseLoadMoreOnScrollOptions
33
+ ) {
34
+ const sentinelRef = useRef<HTMLDivElement>(null);
35
+ const entry = useIntersectionObserver(sentinelRef, { rootRef, rootMargin });
36
+
37
+ const loadMoreRef = useRef(loadMore);
38
+ useEffect(() => {
39
+ loadMoreRef.current = loadMore;
40
+ }, [loadMore]);
41
+
42
+ useEffect(() => {
43
+ if (entry?.isIntersecting && hasMore && !isLoadingMore) {
44
+ loadMoreRef.current();
45
+ }
46
+ }, [entry?.isIntersecting, hasMore, isLoadingMore]);
47
+
48
+ return sentinelRef;
49
+ }
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+
5
+ const QUERY = '(prefers-reduced-motion: reduce)';
6
+
7
+ /**
8
+ * Hook that detects whether the user has requested reduced motion and reacts
9
+ * to changes. Mirrors the `prefers-reduced-motion: reduce` media query so
10
+ * components can skip or shorten animations for users with motion
11
+ * sensitivities (e.g. vestibular disorders).
12
+ *
13
+ * @returns `true` if the user prefers reduced motion, `false` otherwise
14
+ */
15
+ export const useReducedMotion = (): boolean => {
16
+ const [reduced, setReduced] = useState(() => {
17
+ if (typeof window === 'undefined' || !window.matchMedia) return false;
18
+ return window.matchMedia(QUERY).matches;
19
+ });
20
+
21
+ useEffect(() => {
22
+ if (typeof window === 'undefined' || !window.matchMedia) return;
23
+
24
+ const media = window.matchMedia(QUERY);
25
+ const onChange = (): void => setReduced(media.matches);
26
+
27
+ media.addEventListener('change', onChange);
28
+ onChange();
29
+
30
+ return () => media.removeEventListener('change', onChange);
31
+ }, []);
32
+
33
+ return reduced;
34
+ };
package/src/index.ts CHANGED
@@ -14,6 +14,13 @@ 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';
17
24
  export {
18
25
  analyzeEvents,
19
26
  hasPendingHooksFromEvents,
@@ -40,6 +47,7 @@ export {
40
47
  ENCRYPTED_PLACEHOLDER,
41
48
  extractStreamIds,
42
49
  getWebRevivers,
50
+ hasEncryptedFields,
43
51
  hydrateResourceIO,
44
52
  hydrateResourceIOWithKey,
45
53
  isClassInstanceRef,
@@ -49,6 +57,7 @@ export {
49
57
  STREAM_REF_TYPE,
50
58
  truncateId,
51
59
  } from './lib/hydration';
60
+ export type { DecodedStreamChunkSource } from './lib/stream-display';
52
61
  export type { ToastAdapter } from './lib/toast';
53
62
  export { ToastProvider, useToast } from './lib/toast';
54
63
  export type { StreamStep } from './lib/utils';
@@ -0,0 +1,67 @@
1
+ import type { Event } from '@workflow/world';
2
+
3
+ const WORKFLOW_ULID_BODY = '[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}';
4
+
5
+ const STEP_ID_PATTERN = new RegExp(`^step_(${WORKFLOW_ULID_BODY})$`, 'i');
6
+ const WAIT_ID_PATTERN = new RegExp(`^wait_(${WORKFLOW_ULID_BODY})$`, 'i');
7
+ const HOOK_ID_PATTERN = new RegExp(`^hook_(${WORKFLOW_ULID_BODY})$`, 'i');
8
+ const EVENT_ID_PATTERN = new RegExp(`^evnt_(${WORKFLOW_ULID_BODY})$`, 'i');
9
+
10
+ const WORKFLOW_ID_PREFIX_PATTERN = /^(step_|wait_|hook_|evnt_|wrun_)/i;
11
+
12
+ export type ExactWorkflowSearchIdKind = 'step' | 'wait' | 'hook' | 'event';
13
+
14
+ export type ExactWorkflowSearchId = {
15
+ kind: ExactWorkflowSearchIdKind;
16
+ id: string;
17
+ };
18
+
19
+ export type ExactIdSearchResult =
20
+ | { status: 'ok'; events: Event[]; truncated?: boolean }
21
+ | { status: 'not_found' }
22
+ | { status: 'error'; message: string };
23
+
24
+ function matchPrefixedId(
25
+ pattern: RegExp,
26
+ prefix: 'step' | 'wait' | 'hook' | 'evnt',
27
+ kind: ExactWorkflowSearchIdKind,
28
+ query: string
29
+ ): ExactWorkflowSearchId | null {
30
+ const match = query.match(pattern);
31
+ if (!match) {
32
+ return null;
33
+ }
34
+ return { kind, id: `${prefix}_${match[1].toUpperCase()}` };
35
+ }
36
+
37
+ /**
38
+ * Returns a parsed workflow ID when `query` is a full step, wait, hook, or event ID.
39
+ * Partial IDs and run IDs (`wrun_`) are ignored. ULID bodies are matched case-insensitively
40
+ * and normalized to uppercase in the returned ID.
41
+ */
42
+ export function parseExactWorkflowSearchId(
43
+ query: string
44
+ ): ExactWorkflowSearchId | null {
45
+ const trimmed = query.trim();
46
+ if (!trimmed) {
47
+ return null;
48
+ }
49
+
50
+ return (
51
+ matchPrefixedId(STEP_ID_PATTERN, 'step', 'step', trimmed) ??
52
+ matchPrefixedId(WAIT_ID_PATTERN, 'wait', 'wait', trimmed) ??
53
+ matchPrefixedId(HOOK_ID_PATTERN, 'hook', 'hook', trimmed) ??
54
+ matchPrefixedId(EVENT_ID_PATTERN, 'evnt', 'event', trimmed)
55
+ );
56
+ }
57
+
58
+ /** True when input looks like the user is attempting an ID search (including partial). */
59
+ export function looksLikeWorkflowIdSearchInput(query: string): boolean {
60
+ const trimmed = query.trim();
61
+ if (!WORKFLOW_ID_PREFIX_PATTERN.test(trimmed)) {
62
+ return false;
63
+ }
64
+ // Distinguish IDs (contain digits) from event-type strings like step_started.
65
+ // Assumes workflow event types do not include digits in their names.
66
+ return /\d/.test(trimmed);
67
+ }