@workflow/web-shared 5.0.0-beta.2 → 5.0.0-beta.4

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 (161) hide show
  1. package/dist/components/index.d.ts +2 -0
  2. package/dist/components/index.d.ts.map +1 -1
  3. package/dist/components/index.js +2 -0
  4. package/dist/components/index.js.map +1 -1
  5. package/dist/components/new-trace-viewer/components/copy-button.d.ts +9 -0
  6. package/dist/components/new-trace-viewer/components/copy-button.d.ts.map +1 -0
  7. package/dist/components/new-trace-viewer/components/copy-button.js +27 -0
  8. package/dist/components/new-trace-viewer/components/copy-button.js.map +1 -0
  9. package/dist/components/new-trace-viewer/components/event-list.d.ts +8 -0
  10. package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -0
  11. package/dist/components/new-trace-viewer/components/event-list.js +38 -0
  12. package/dist/components/new-trace-viewer/components/event-list.js.map +1 -0
  13. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts +18 -0
  14. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.d.ts.map +1 -0
  15. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js +54 -0
  16. package/dist/components/new-trace-viewer/components/middle-truncate/copy-selection.js.map +1 -0
  17. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts +20 -0
  18. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.d.ts.map +1 -0
  19. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js +81 -0
  20. package/dist/components/new-trace-viewer/components/middle-truncate/middle-truncate.js.map +1 -0
  21. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts +25 -0
  22. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.d.ts.map +1 -0
  23. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js +101 -0
  24. package/dist/components/new-trace-viewer/components/middle-truncate/truncate.js.map +1 -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/middle-truncate/use-middle-truncate.js.map +1 -0
  29. package/dist/components/new-trace-viewer/components/split-pane.d.ts +14 -0
  30. package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -0
  31. package/dist/components/new-trace-viewer/components/split-pane.js +99 -0
  32. package/dist/components/new-trace-viewer/components/split-pane.js.map +1 -0
  33. package/dist/components/new-trace-viewer/components/timeline.d.ts +31 -0
  34. package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -0
  35. package/dist/components/new-trace-viewer/components/timeline.js +138 -0
  36. package/dist/components/new-trace-viewer/components/timeline.js.map +1 -0
  37. package/dist/components/new-trace-viewer/context.d.ts +15 -0
  38. package/dist/components/new-trace-viewer/context.d.ts.map +1 -0
  39. package/dist/components/new-trace-viewer/context.js +47 -0
  40. package/dist/components/new-trace-viewer/context.js.map +1 -0
  41. package/dist/components/new-trace-viewer/detail-panel.d.ts +10 -0
  42. package/dist/components/new-trace-viewer/detail-panel.d.ts.map +1 -0
  43. package/dist/components/new-trace-viewer/detail-panel.js +12 -0
  44. package/dist/components/new-trace-viewer/detail-panel.js.map +1 -0
  45. package/dist/components/new-trace-viewer/icons.d.ts +6 -0
  46. package/dist/components/new-trace-viewer/icons.d.ts.map +1 -0
  47. package/dist/components/new-trace-viewer/icons.js +15 -0
  48. package/dist/components/new-trace-viewer/icons.js.map +1 -0
  49. package/dist/components/new-trace-viewer/trace-viewer.d.ts +8 -0
  50. package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -0
  51. package/dist/components/new-trace-viewer/trace-viewer.js +349 -0
  52. package/dist/components/new-trace-viewer/trace-viewer.js.map +1 -0
  53. package/dist/components/new-trace-viewer/types.d.ts +1 -0
  54. package/dist/components/new-trace-viewer/types.d.ts.map +1 -0
  55. package/dist/components/new-trace-viewer/types.js +2 -0
  56. package/dist/components/new-trace-viewer/types.js.map +1 -0
  57. package/dist/components/new-trace-viewer/util/constants.d.ts +1 -0
  58. package/dist/components/new-trace-viewer/util/constants.d.ts.map +1 -0
  59. package/dist/components/new-trace-viewer/util/constants.js +2 -0
  60. package/dist/components/new-trace-viewer/util/constants.js.map +1 -0
  61. package/dist/components/new-trace-viewer/utils.d.ts +42 -0
  62. package/dist/components/new-trace-viewer/utils.d.ts.map +1 -0
  63. package/dist/components/new-trace-viewer/utils.js +305 -0
  64. package/dist/components/new-trace-viewer/utils.js.map +1 -0
  65. package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
  66. package/dist/components/sidebar/attribute-panel.js +42 -23
  67. package/dist/components/sidebar/attribute-panel.js.map +1 -1
  68. package/dist/components/sidebar/copyable-data-block.js +1 -1
  69. package/dist/components/sidebar/copyable-data-block.js.map +1 -1
  70. package/dist/components/sidebar/detail-card.d.ts.map +1 -1
  71. package/dist/components/sidebar/detail-card.js +1 -1
  72. package/dist/components/sidebar/detail-card.js.map +1 -1
  73. package/dist/components/sidebar/entity-detail-panel.d.ts +1 -3
  74. package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
  75. package/dist/components/sidebar/entity-detail-panel.js +3 -25
  76. package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
  77. package/dist/components/sidebar/events-list.d.ts.map +1 -1
  78. package/dist/components/sidebar/events-list.js +2 -2
  79. package/dist/components/sidebar/events-list.js.map +1 -1
  80. package/dist/components/sidebar/sidebar-data-context.d.ts +29 -0
  81. package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -0
  82. package/dist/components/sidebar/sidebar-data-context.js +19 -0
  83. package/dist/components/sidebar/sidebar-data-context.js.map +1 -0
  84. package/dist/components/stream-viewer.d.ts +1 -1
  85. package/dist/components/stream-viewer.d.ts.map +1 -1
  86. package/dist/components/stream-viewer.js +2 -26
  87. package/dist/components/stream-viewer.js.map +1 -1
  88. package/dist/components/trace-viewer/util/timing.d.ts +1 -1
  89. package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
  90. package/dist/components/trace-viewer/util/timing.js +5 -8
  91. package/dist/components/trace-viewer/util/timing.js.map +1 -1
  92. package/dist/components/trace-viewer-new.d.ts +10 -0
  93. package/dist/components/trace-viewer-new.d.ts.map +1 -0
  94. package/dist/components/trace-viewer-new.js +23 -0
  95. package/dist/components/trace-viewer-new.js.map +1 -0
  96. package/dist/components/ui/data-inspector.d.ts +21 -0
  97. package/dist/components/ui/data-inspector.d.ts.map +1 -1
  98. package/dist/components/ui/data-inspector.js +99 -4
  99. package/dist/components/ui/data-inspector.js.map +1 -1
  100. package/dist/components/ui/inspector-theme.d.ts +9 -10
  101. package/dist/components/ui/inspector-theme.d.ts.map +1 -1
  102. package/dist/components/ui/inspector-theme.js +38 -71
  103. package/dist/components/ui/inspector-theme.js.map +1 -1
  104. package/dist/components/workflow-trace-view.d.ts +1 -3
  105. package/dist/components/workflow-trace-view.d.ts.map +1 -1
  106. package/dist/components/workflow-trace-view.js +4 -4
  107. package/dist/components/workflow-trace-view.js.map +1 -1
  108. package/dist/components/workflow-traces/trace-span-construction.d.ts +2 -2
  109. package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
  110. package/dist/components/workflow-traces/trace-span-construction.js +16 -3
  111. package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
  112. package/dist/index.d.ts +1 -0
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js.map +1 -1
  115. package/dist/lib/stream-display.d.ts +18 -0
  116. package/dist/lib/stream-display.d.ts.map +1 -0
  117. package/dist/lib/stream-display.js +30 -0
  118. package/dist/lib/stream-display.js.map +1 -0
  119. package/dist/lib/trace-builder.d.ts.map +1 -1
  120. package/dist/lib/trace-builder.js +2 -1
  121. package/dist/lib/trace-builder.js.map +1 -1
  122. package/dist/lib/utils.d.ts +9 -9
  123. package/dist/lib/utils.d.ts.map +1 -1
  124. package/dist/lib/utils.js +26 -31
  125. package/dist/lib/utils.js.map +1 -1
  126. package/dist/styles.css +470 -1
  127. package/package.json +3 -2
  128. package/src/components/index.ts +5 -0
  129. package/src/components/new-trace-viewer/components/copy-button.tsx +68 -0
  130. package/src/components/new-trace-viewer/components/event-list.tsx +116 -0
  131. package/src/components/new-trace-viewer/components/middle-truncate/copy-selection.ts +114 -0
  132. package/src/components/new-trace-viewer/components/middle-truncate/middle-truncate.tsx +157 -0
  133. package/src/components/new-trace-viewer/components/middle-truncate/truncate.ts +132 -0
  134. package/src/components/new-trace-viewer/components/middle-truncate/use-middle-truncate.ts +167 -0
  135. package/src/components/new-trace-viewer/components/split-pane.tsx +185 -0
  136. package/src/components/new-trace-viewer/components/timeline.tsx +382 -0
  137. package/src/components/new-trace-viewer/context.tsx +92 -0
  138. package/src/components/new-trace-viewer/detail-panel.tsx +58 -0
  139. package/src/components/new-trace-viewer/icons.tsx +79 -0
  140. package/src/components/new-trace-viewer/trace-viewer.tsx +645 -0
  141. package/src/components/new-trace-viewer/types.ts +0 -0
  142. package/src/components/new-trace-viewer/util/constants.ts +0 -0
  143. package/src/components/new-trace-viewer/utils.ts +450 -0
  144. package/src/components/sidebar/attribute-panel.tsx +43 -32
  145. package/src/components/sidebar/copyable-data-block.tsx +1 -1
  146. package/src/components/sidebar/detail-card.tsx +1 -19
  147. package/src/components/sidebar/entity-detail-panel.tsx +0 -71
  148. package/src/components/sidebar/events-list.tsx +9 -6
  149. package/src/components/sidebar/sidebar-data-context.tsx +62 -0
  150. package/src/components/stream-viewer.tsx +18 -40
  151. package/src/components/trace-viewer/util/timing.ts +5 -9
  152. package/src/components/trace-viewer-new.tsx +49 -0
  153. package/src/components/ui/data-inspector.tsx +235 -4
  154. package/src/components/ui/inspector-theme.ts +39 -81
  155. package/src/components/workflow-trace-view.tsx +2 -6
  156. package/src/components/workflow-traces/trace-span-construction.ts +22 -4
  157. package/src/index.ts +1 -0
  158. package/src/lib/stream-display.ts +48 -0
  159. package/src/lib/trace-builder.ts +2 -1
  160. package/src/lib/utils.ts +27 -32
  161. package/src/styles.css +470 -1
@@ -0,0 +1,30 @@
1
+ export function summarizeArrayBufferView(value) {
2
+ const ta = value;
3
+ const name = ta.constructor.name;
4
+ const preview = Array.from({ length: Math.min(ta.length, 8) }, (_, i) => ta[i]);
5
+ const suffix = ta.length > 8 ? ', …' : '';
6
+ return `${name}(${ta.length}) [${preview.join(', ')}${suffix}]`;
7
+ }
8
+ /**
9
+ * Decode a typed array as UTF-8 text when valid, otherwise return a compact
10
+ * raw-byte summary. Used by `DataInspector`'s `collapseRefs` pipeline so
11
+ * hydrated `Uint8Array` chunks (e.g. AI SDK text deltas) render as readable
12
+ * text while still exposing the underlying byte layout.
13
+ */
14
+ export function formatArrayBufferViewForDisplay(value) {
15
+ try {
16
+ const text = new TextDecoder('utf-8', { fatal: true }).decode(value);
17
+ return {
18
+ text,
19
+ decodedFrom: {
20
+ type: value.constructor.name,
21
+ encoding: 'utf-8',
22
+ rawSummary: summarizeArrayBufferView(value),
23
+ },
24
+ };
25
+ }
26
+ catch {
27
+ return { text: summarizeArrayBufferView(value) };
28
+ }
29
+ }
30
+ //# sourceMappingURL=stream-display.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-display.js","sourceRoot":"","sources":["../../src/lib/stream-display.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,wBAAwB,CAAC,KAAsB;IAC7D,MAAM,EAAE,GAAG,KAGU,CAAC;IACtB,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CACxB,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAClC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAChB,CAAC;IACF,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,+BAA+B,CAC7C,KAAsB;IAEtB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrE,OAAO;YACL,IAAI;YACJ,WAAW,EAAE;gBACX,IAAI,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI;gBAC5B,QAAQ,EAAE,OAAO;gBACjB,UAAU,EAAE,wBAAwB,CAAC,KAAK,CAAC;aAC5C;SACF,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC;IACnD,CAAC;AACH,CAAC","sourcesContent":["export interface DecodedStreamChunkSource {\n type: string;\n encoding: 'utf-8';\n rawSummary: string;\n}\n\nexport interface FormattedStreamChunkDisplay {\n text: string;\n decodedFrom?: DecodedStreamChunkSource;\n}\n\nexport function summarizeArrayBufferView(value: ArrayBufferView): string {\n const ta = value as unknown as {\n length: number;\n constructor: { name: string };\n } & ArrayLike<number>;\n const name = ta.constructor.name;\n const preview = Array.from(\n { length: Math.min(ta.length, 8) },\n (_, i) => ta[i]\n );\n const suffix = ta.length > 8 ? ', …' : '';\n return `${name}(${ta.length}) [${preview.join(', ')}${suffix}]`;\n}\n\n/**\n * Decode a typed array as UTF-8 text when valid, otherwise return a compact\n * raw-byte summary. Used by `DataInspector`'s `collapseRefs` pipeline so\n * hydrated `Uint8Array` chunks (e.g. AI SDK text deltas) render as readable\n * text while still exposing the underlying byte layout.\n */\nexport function formatArrayBufferViewForDisplay(\n value: ArrayBufferView\n): FormattedStreamChunkDisplay {\n try {\n const text = new TextDecoder('utf-8', { fatal: true }).decode(value);\n return {\n text,\n decodedFrom: {\n type: value.constructor.name,\n encoding: 'utf-8',\n rawSummary: summarizeArrayBufferView(value),\n },\n };\n } catch {\n return { text: summarizeArrayBufferView(value) };\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"trace-builder.d.ts","sourceRoot":"","sources":["../../src/lib/trace-builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kCAAkC,CAAC;AAc7D,eAAO,MAAM,WAAW,GAAI,WAAW,MAAM,YAAkC,CAAC;AAEhF,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,mDACkB,CAAC;AAEjE,eAAO,MAAM,oBAAoB,GAAI,WAAW,MAAM,oEAGvB,CAAC;AAMhC,MAAM,MAAM,aAAa,GAAG;IAC1B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACrC,cAAc,EAAE,KAAK,EAAE,CAAC;IACxB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAClC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;CAClC,CAAC;AAeF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,aAAa,CAgCvE;AAqED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EAAE,CAAC;IAClE,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,UAAU,CACxB,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,KAAK,EAAE,EACf,GAAG,EAAE,IAAI,GACR,aAAa,CA6Bf"}
1
+ {"version":3,"file":"trace-builder.d.ts","sourceRoot":"","sources":["../../src/lib/trace-builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kCAAkC,CAAC;AAc7D,eAAO,MAAM,WAAW,GAAI,WAAW,MAAM,YAAkC,CAAC;AAEhF,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,mDACkB,CAAC;AAEjE,eAAO,MAAM,oBAAoB,GAAI,WAAW,MAAM,oEAGvB,CAAC;AAMhC,MAAM,MAAM,aAAa,GAAG;IAC1B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACrC,cAAc,EAAE,KAAK,EAAE,CAAC;IACxB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAClC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;CAClC,CAAC;AAeF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,aAAa,CAgCvE;AAsED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EAAE,CAAC;IAClE,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,UAAU,CACxB,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,KAAK,EAAE,EACf,GAAG,EAAE,IAAI,GACR,aAAa,CA6Bf"}
@@ -72,6 +72,7 @@ function buildSpans(run, groupedEvents, now, latestKnownTime) {
72
72
  // unknown territory. Even when the run is completed, we may not have loaded
73
73
  // all events yet, so always use the latest event we've actually seen.
74
74
  const childMaxEnd = latestKnownTime;
75
+ const runMaxEnd = run.completedAt ?? now;
75
76
  const stepSpans = Array.from(groupedEvents.eventsByStepId.values())
76
77
  .map((events) => stepToSpan(events, childMaxEnd))
77
78
  .filter((span) => span !== null);
@@ -79,7 +80,7 @@ function buildSpans(run, groupedEvents, now, latestKnownTime) {
79
80
  .map((events) => hookToSpan(events, childMaxEnd))
80
81
  .filter((span) => span !== null);
81
82
  const waitSpans = Array.from(groupedEvents.timerEvents.values())
82
- .map((events) => waitToSpan(events, childMaxEnd))
83
+ .map((events) => waitToSpan(events, childMaxEnd, runMaxEnd))
83
84
  .filter((span) => span !== null);
84
85
  return {
85
86
  runSpan: runToSpan(run, groupedEvents.runLevelEvents, now),
@@ -1 +1 @@
1
- {"version":3,"file":"trace-builder.js","sourceRoot":"","sources":["../../src/lib/trace-builder.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Builds a complete trace from a WorkflowRun and its Events.\n *\n * This module groups raw events by correlation ID and entity type,\n * converts each group into an OpenTelemetry-style Span, and returns\n * a fully-formed Trace ready for the trace viewer.\n */\n\nimport type { Event, WorkflowRun } from '@workflow/world';\nimport type { Span } from '../components/trace-viewer/types';\nimport {\n hookToSpan,\n runToSpan,\n stepToSpan,\n waitToSpan,\n WORKFLOW_LIBRARY,\n} from '../components/workflow-traces/trace-span-construction';\nimport { otelTimeToMs } from '../components/workflow-traces/trace-time-utils';\n\n// ---------------------------------------------------------------------------\n// Event type classifiers\n// ---------------------------------------------------------------------------\n\nexport const isStepEvent = (eventType: string) => eventType.startsWith('step_');\n\nexport const isTimerEvent = (eventType: string) =>\n eventType === 'wait_created' || eventType === 'wait_completed';\n\nexport const isHookLifecycleEvent = (eventType: string) =>\n eventType === 'hook_received' ||\n eventType === 'hook_created' ||\n eventType === 'hook_disposed';\n\n// ---------------------------------------------------------------------------\n// Event grouping\n// ---------------------------------------------------------------------------\n\nexport type GroupedEvents = {\n eventsByStepId: Map<string, Event[]>;\n runLevelEvents: Event[];\n timerEvents: Map<string, Event[]>;\n hookEvents: Map<string, Event[]>;\n};\n\nfunction pushEvent(\n map: Map<string, Event[]>,\n correlationId: string,\n event: Event\n) {\n const existing = map.get(correlationId);\n if (existing) {\n existing.push(event);\n return;\n }\n map.set(correlationId, [event]);\n}\n\nexport function groupEventsByCorrelation(events: Event[]): GroupedEvents {\n const eventsByStepId = new Map<string, Event[]>();\n const runLevelEvents: Event[] = [];\n const timerEvents = new Map<string, Event[]>();\n const hookEvents = new Map<string, Event[]>();\n\n for (const event of events) {\n const correlationId = event.correlationId;\n if (!correlationId) {\n runLevelEvents.push(event);\n continue;\n }\n\n if (isTimerEvent(event.eventType)) {\n pushEvent(timerEvents, correlationId, event);\n continue;\n }\n\n if (isHookLifecycleEvent(event.eventType)) {\n pushEvent(hookEvents, correlationId, event);\n continue;\n }\n\n if (isStepEvent(event.eventType)) {\n pushEvent(eventsByStepId, correlationId, event);\n continue;\n }\n\n runLevelEvents.push(event);\n }\n\n return { eventsByStepId, runLevelEvents, timerEvents, hookEvents };\n}\n\n// ---------------------------------------------------------------------------\n// Trace construction\n// ---------------------------------------------------------------------------\n\n/**\n * Computes the latest known event time from the events list.\n * Active (in-progress) child spans are capped at this time instead of `now`,\n * so they only extend as far as our data goes.\n */\nfunction computeLatestKnownTime(events: Event[], run: WorkflowRun): Date {\n let latest = new Date(run.createdAt).getTime();\n for (const event of events) {\n const t = new Date(event.createdAt).getTime();\n if (t > latest) latest = t;\n }\n return new Date(latest);\n}\n\nfunction buildSpans(\n run: WorkflowRun,\n groupedEvents: GroupedEvents,\n now: Date,\n latestKnownTime: Date\n) {\n // Active child spans cap at latestKnownTime so they don't extend into\n // unknown territory. Even when the run is completed, we may not have loaded\n // all events yet, so always use the latest event we've actually seen.\n const childMaxEnd = latestKnownTime;\n\n const stepSpans = Array.from(groupedEvents.eventsByStepId.values())\n .map((events) => stepToSpan(events, childMaxEnd))\n .filter((span): span is Span => span !== null);\n\n const hookSpans = Array.from(groupedEvents.hookEvents.values())\n .map((events) => hookToSpan(events, childMaxEnd))\n .filter((span): span is Span => span !== null);\n\n const waitSpans = Array.from(groupedEvents.timerEvents.values())\n .map((events) => waitToSpan(events, childMaxEnd))\n .filter((span): span is Span => span !== null);\n\n return {\n runSpan: runToSpan(run, groupedEvents.runLevelEvents, now),\n spans: [...stepSpans, ...hookSpans, ...waitSpans],\n };\n}\n\nfunction cascadeSpans(runSpan: Span, spans: Span[]) {\n const sortedSpans = [\n runSpan,\n ...spans.slice().sort((a, b) => {\n const aStart = otelTimeToMs(a.startTime);\n const bStart = otelTimeToMs(b.startTime);\n return aStart - bStart;\n }),\n ];\n\n return sortedSpans.map((span, index) => {\n const parentSpanId =\n index === 0 ? undefined : String(sortedSpans[index - 1].spanId);\n return {\n ...span,\n parentSpanId,\n };\n });\n}\n\nexport interface TraceWithMeta {\n traceId: string;\n rootSpanId: string;\n spans: Span[];\n resources: { name: string; attributes: Record<string, string> }[];\n /** Duration in ms from trace start to the latest known event. */\n knownDurationMs: number;\n}\n\nexport function buildTrace(\n run: WorkflowRun,\n events: Event[],\n now: Date\n): TraceWithMeta {\n const groupedEvents = groupEventsByCorrelation(events);\n const latestKnownTime = computeLatestKnownTime(events, run);\n const { runSpan, spans } = buildSpans(\n run,\n groupedEvents,\n now,\n latestKnownTime\n );\n const sortedCascadingSpans = cascadeSpans(runSpan, spans);\n\n // Compute known duration relative to trace start\n const traceStartMs = otelTimeToMs(runSpan.startTime);\n const knownDurationMs = latestKnownTime.getTime() - traceStartMs;\n\n return {\n traceId: run.runId,\n rootSpanId: run.runId,\n spans: sortedCascadingSpans,\n resources: [\n {\n name: 'workflow',\n attributes: {\n 'service.name': WORKFLOW_LIBRARY.name,\n },\n },\n ],\n knownDurationMs: Math.max(0, knownDurationMs),\n };\n}\n"]}
1
+ {"version":3,"file":"trace-builder.js","sourceRoot":"","sources":["../../src/lib/trace-builder.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Builds a complete trace from a WorkflowRun and its Events.\n *\n * This module groups raw events by correlation ID and entity type,\n * converts each group into an OpenTelemetry-style Span, and returns\n * a fully-formed Trace ready for the trace viewer.\n */\n\nimport type { Event, WorkflowRun } from '@workflow/world';\nimport type { Span } from '../components/trace-viewer/types';\nimport {\n hookToSpan,\n runToSpan,\n stepToSpan,\n waitToSpan,\n WORKFLOW_LIBRARY,\n} from '../components/workflow-traces/trace-span-construction';\nimport { otelTimeToMs } from '../components/workflow-traces/trace-time-utils';\n\n// ---------------------------------------------------------------------------\n// Event type classifiers\n// ---------------------------------------------------------------------------\n\nexport const isStepEvent = (eventType: string) => eventType.startsWith('step_');\n\nexport const isTimerEvent = (eventType: string) =>\n eventType === 'wait_created' || eventType === 'wait_completed';\n\nexport const isHookLifecycleEvent = (eventType: string) =>\n eventType === 'hook_received' ||\n eventType === 'hook_created' ||\n eventType === 'hook_disposed';\n\n// ---------------------------------------------------------------------------\n// Event grouping\n// ---------------------------------------------------------------------------\n\nexport type GroupedEvents = {\n eventsByStepId: Map<string, Event[]>;\n runLevelEvents: Event[];\n timerEvents: Map<string, Event[]>;\n hookEvents: Map<string, Event[]>;\n};\n\nfunction pushEvent(\n map: Map<string, Event[]>,\n correlationId: string,\n event: Event\n) {\n const existing = map.get(correlationId);\n if (existing) {\n existing.push(event);\n return;\n }\n map.set(correlationId, [event]);\n}\n\nexport function groupEventsByCorrelation(events: Event[]): GroupedEvents {\n const eventsByStepId = new Map<string, Event[]>();\n const runLevelEvents: Event[] = [];\n const timerEvents = new Map<string, Event[]>();\n const hookEvents = new Map<string, Event[]>();\n\n for (const event of events) {\n const correlationId = event.correlationId;\n if (!correlationId) {\n runLevelEvents.push(event);\n continue;\n }\n\n if (isTimerEvent(event.eventType)) {\n pushEvent(timerEvents, correlationId, event);\n continue;\n }\n\n if (isHookLifecycleEvent(event.eventType)) {\n pushEvent(hookEvents, correlationId, event);\n continue;\n }\n\n if (isStepEvent(event.eventType)) {\n pushEvent(eventsByStepId, correlationId, event);\n continue;\n }\n\n runLevelEvents.push(event);\n }\n\n return { eventsByStepId, runLevelEvents, timerEvents, hookEvents };\n}\n\n// ---------------------------------------------------------------------------\n// Trace construction\n// ---------------------------------------------------------------------------\n\n/**\n * Computes the latest known event time from the events list.\n * Active (in-progress) child spans are capped at this time instead of `now`,\n * so they only extend as far as our data goes.\n */\nfunction computeLatestKnownTime(events: Event[], run: WorkflowRun): Date {\n let latest = new Date(run.createdAt).getTime();\n for (const event of events) {\n const t = new Date(event.createdAt).getTime();\n if (t > latest) latest = t;\n }\n return new Date(latest);\n}\n\nfunction buildSpans(\n run: WorkflowRun,\n groupedEvents: GroupedEvents,\n now: Date,\n latestKnownTime: Date\n) {\n // Active child spans cap at latestKnownTime so they don't extend into\n // unknown territory. Even when the run is completed, we may not have loaded\n // all events yet, so always use the latest event we've actually seen.\n const childMaxEnd = latestKnownTime;\n const runMaxEnd = run.completedAt ?? now;\n\n const stepSpans = Array.from(groupedEvents.eventsByStepId.values())\n .map((events) => stepToSpan(events, childMaxEnd))\n .filter((span): span is Span => span !== null);\n\n const hookSpans = Array.from(groupedEvents.hookEvents.values())\n .map((events) => hookToSpan(events, childMaxEnd))\n .filter((span): span is Span => span !== null);\n\n const waitSpans = Array.from(groupedEvents.timerEvents.values())\n .map((events) => waitToSpan(events, childMaxEnd, runMaxEnd))\n .filter((span): span is Span => span !== null);\n\n return {\n runSpan: runToSpan(run, groupedEvents.runLevelEvents, now),\n spans: [...stepSpans, ...hookSpans, ...waitSpans],\n };\n}\n\nfunction cascadeSpans(runSpan: Span, spans: Span[]) {\n const sortedSpans = [\n runSpan,\n ...spans.slice().sort((a, b) => {\n const aStart = otelTimeToMs(a.startTime);\n const bStart = otelTimeToMs(b.startTime);\n return aStart - bStart;\n }),\n ];\n\n return sortedSpans.map((span, index) => {\n const parentSpanId =\n index === 0 ? undefined : String(sortedSpans[index - 1].spanId);\n return {\n ...span,\n parentSpanId,\n };\n });\n}\n\nexport interface TraceWithMeta {\n traceId: string;\n rootSpanId: string;\n spans: Span[];\n resources: { name: string; attributes: Record<string, string> }[];\n /** Duration in ms from trace start to the latest known event. */\n knownDurationMs: number;\n}\n\nexport function buildTrace(\n run: WorkflowRun,\n events: Event[],\n now: Date\n): TraceWithMeta {\n const groupedEvents = groupEventsByCorrelation(events);\n const latestKnownTime = computeLatestKnownTime(events, run);\n const { runSpan, spans } = buildSpans(\n run,\n groupedEvents,\n now,\n latestKnownTime\n );\n const sortedCascadingSpans = cascadeSpans(runSpan, spans);\n\n // Compute known duration relative to trace start\n const traceStartMs = otelTimeToMs(runSpan.startTime);\n const knownDurationMs = latestKnownTime.getTime() - traceStartMs;\n\n return {\n traceId: run.runId,\n rootSpanId: run.runId,\n spans: sortedCascadingSpans,\n resources: [\n {\n name: 'workflow',\n attributes: {\n 'service.name': WORKFLOW_LIBRARY.name,\n },\n },\n ],\n knownDurationMs: Math.max(0, knownDurationMs),\n };\n}\n"]}
@@ -6,19 +6,19 @@ export declare function cn(...inputs: ClassValue[]): string;
6
6
  * Formats a duration in milliseconds to a human-readable string.
7
7
  *
8
8
  * @param ms - Duration in milliseconds
9
- * @param compact - If true, returns a single-unit format (e.g., "45s", "2.5m").
10
- * If false (default), returns multi-part format (e.g., "1h 30m", "2d 5h").
9
+ * @param compact - If true, returns a compact format (e.g., "380ms", "2m 30s").
10
+ * If false (default), returns multi-part format (e.g., "1m 13s", "2d 5h 3m 12s").
11
11
  *
12
- * Compact format:
13
- * - < 1s: shows milliseconds (e.g., "500ms")
12
+ * Compact format (timeline markers):
13
+ * - < 1s: shows milliseconds (e.g., "380ms")
14
14
  * - < 1m: shows seconds (e.g., "45s")
15
- * - < 1h: shows minutes (e.g., "45m")
16
- * - >= 1h: shows hours (e.g., "2.5h")
15
+ * - < 1h: shows minutes and seconds (e.g., "2m 30s")
16
+ * - >= 1h: shows hours and minutes (e.g., "2h 30m")
17
17
  *
18
18
  * Full format:
19
- * - < 1s: shows milliseconds (e.g., "500ms")
20
- * - < 1m: shows seconds (e.g., "45.5s")
21
- * - >= 1m: shows human-readable format (e.g., "1h 30m", "2d 5h")
19
+ * - < 1s: shows milliseconds (e.g., "380ms")
20
+ * - < 1m: shows seconds (e.g., "45s")
21
+ * - >= 1m: shows decomposed format with whole seconds (e.g., "1m 13s", "2m 13s")
22
22
  */
23
23
  export declare function formatDuration(ms: number, compact?: boolean): string;
24
24
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AACvC,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAC;AAG7C,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC;AAWD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,MAAM,CAiDlE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf,MAAM,CAKR;AAMD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,OAAO,GAAG,YAAY,EAAE,GAAG,IAAI,CAmB7E;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,YAAY,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,CAc/D"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AACvC,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAC;AAG7C,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC;AAOD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,MAAM,CAgDlE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf,MAAM,CAKR;AAMD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,OAAO,GAAG,YAAY,EAAE,GAAG,IAAI,CAmB7E;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,YAAY,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,CAc/D"}
package/dist/lib/utils.js CHANGED
@@ -3,9 +3,6 @@ import { twMerge } from 'tailwind-merge';
3
3
  export function cn(...inputs) {
4
4
  return twMerge(clsx(inputs));
5
5
  }
6
- const durationFormatter = new Intl.NumberFormat(undefined, {
7
- maximumFractionDigits: 2,
8
- });
9
6
  const MS_IN_SECOND = 1000;
10
7
  const MS_IN_MINUTE = 60 * MS_IN_SECOND;
11
8
  const MS_IN_HOUR = 60 * MS_IN_MINUTE;
@@ -14,48 +11,48 @@ const MS_IN_DAY = 24 * MS_IN_HOUR;
14
11
  * Formats a duration in milliseconds to a human-readable string.
15
12
  *
16
13
  * @param ms - Duration in milliseconds
17
- * @param compact - If true, returns a single-unit format (e.g., "45s", "2.5m").
18
- * If false (default), returns multi-part format (e.g., "1h 30m", "2d 5h").
14
+ * @param compact - If true, returns a compact format (e.g., "380ms", "2m 30s").
15
+ * If false (default), returns multi-part format (e.g., "1m 13s", "2d 5h 3m 12s").
19
16
  *
20
- * Compact format:
21
- * - < 1s: shows milliseconds (e.g., "500ms")
17
+ * Compact format (timeline markers):
18
+ * - < 1s: shows milliseconds (e.g., "380ms")
22
19
  * - < 1m: shows seconds (e.g., "45s")
23
- * - < 1h: shows minutes (e.g., "45m")
24
- * - >= 1h: shows hours (e.g., "2.5h")
20
+ * - < 1h: shows minutes and seconds (e.g., "2m 30s")
21
+ * - >= 1h: shows hours and minutes (e.g., "2h 30m")
25
22
  *
26
23
  * Full format:
27
- * - < 1s: shows milliseconds (e.g., "500ms")
28
- * - < 1m: shows seconds (e.g., "45.5s")
29
- * - >= 1m: shows human-readable format (e.g., "1h 30m", "2d 5h")
24
+ * - < 1s: shows milliseconds (e.g., "380ms")
25
+ * - < 1m: shows seconds (e.g., "45s")
26
+ * - >= 1m: shows decomposed format with whole seconds (e.g., "1m 13s", "2m 13s")
30
27
  */
31
28
  export function formatDuration(ms, compact = false) {
32
29
  if (ms === 0) {
33
- return '0';
30
+ return '0s';
34
31
  }
35
- // For durations less than 1 second, show milliseconds
36
32
  if (ms < MS_IN_SECOND) {
37
- return `${durationFormatter.format(ms)}ms`;
33
+ const roundedMs = Math.round(ms);
34
+ return roundedMs < MS_IN_SECOND ? `${roundedMs}ms` : '1s';
38
35
  }
39
- // For durations less than 1 minute, show seconds
40
- if (ms < MS_IN_MINUTE) {
41
- return `${durationFormatter.format(ms / MS_IN_SECOND)}s`;
36
+ const roundedMs = Math.round(ms / MS_IN_SECOND) * MS_IN_SECOND;
37
+ if (roundedMs < MS_IN_MINUTE) {
38
+ return `${Math.floor(roundedMs / MS_IN_SECOND)}s`;
42
39
  }
43
40
  // Compact format: multi-unit without decimals (e.g. "8m 20s", "2h 30m")
44
41
  if (compact) {
45
- if (ms < MS_IN_HOUR) {
46
- const m = Math.floor(ms / MS_IN_MINUTE);
47
- const s = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);
42
+ if (roundedMs < MS_IN_HOUR) {
43
+ const m = Math.floor(roundedMs / MS_IN_MINUTE);
44
+ const s = Math.floor((roundedMs % MS_IN_MINUTE) / MS_IN_SECOND);
48
45
  return s > 0 ? `${m}m ${s}s` : `${m}m`;
49
46
  }
50
- const h = Math.floor(ms / MS_IN_HOUR);
51
- const m = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);
47
+ const h = Math.floor(roundedMs / MS_IN_HOUR);
48
+ const m = Math.floor((roundedMs % MS_IN_HOUR) / MS_IN_MINUTE);
52
49
  return m > 0 ? `${h}h ${m}m` : `${h}h`;
53
50
  }
54
- // Full format: human-readable multi-part
55
- const days = Math.floor(ms / MS_IN_DAY);
56
- const hours = Math.floor((ms % MS_IN_DAY) / MS_IN_HOUR);
57
- const minutes = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);
58
- const seconds = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);
51
+ // Full format: decompose into larger units + whole seconds.
52
+ const days = Math.floor(roundedMs / MS_IN_DAY);
53
+ const hours = Math.floor((roundedMs % MS_IN_DAY) / MS_IN_HOUR);
54
+ const minutes = Math.floor((roundedMs % MS_IN_HOUR) / MS_IN_MINUTE);
55
+ const seconds = Math.floor((roundedMs % MS_IN_MINUTE) / MS_IN_SECOND);
59
56
  const parts = [];
60
57
  if (days > 0) {
61
58
  parts.push(`${days}d`);
@@ -66,9 +63,7 @@ export function formatDuration(ms, compact = false) {
66
63
  if (minutes > 0) {
67
64
  parts.push(`${minutes}m`);
68
65
  }
69
- if (hours <= 1 && (seconds > 0 || parts.length === 0)) {
70
- parts.push(`${seconds}s`);
71
- }
66
+ parts.push(`${seconds}s`);
72
67
  return parts.join(' ');
73
68
  }
74
69
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Step } from '@workflow/world';\nimport type { ModelMessage } from 'ai';\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nconst durationFormatter = new Intl.NumberFormat(undefined, {\n maximumFractionDigits: 2,\n});\n\nconst MS_IN_SECOND = 1000;\nconst MS_IN_MINUTE = 60 * MS_IN_SECOND;\nconst MS_IN_HOUR = 60 * MS_IN_MINUTE;\nconst MS_IN_DAY = 24 * MS_IN_HOUR;\n\n/**\n * Formats a duration in milliseconds to a human-readable string.\n *\n * @param ms - Duration in milliseconds\n * @param compact - If true, returns a single-unit format (e.g., \"45s\", \"2.5m\").\n * If false (default), returns multi-part format (e.g., \"1h 30m\", \"2d 5h\").\n *\n * Compact format:\n * - < 1s: shows milliseconds (e.g., \"500ms\")\n * - < 1m: shows seconds (e.g., \"45s\")\n * - < 1h: shows minutes (e.g., \"45m\")\n * - >= 1h: shows hours (e.g., \"2.5h\")\n *\n * Full format:\n * - < 1s: shows milliseconds (e.g., \"500ms\")\n * - < 1m: shows seconds (e.g., \"45.5s\")\n * - >= 1m: shows human-readable format (e.g., \"1h 30m\", \"2d 5h\")\n */\nexport function formatDuration(ms: number, compact = false): string {\n if (ms === 0) {\n return '0';\n }\n\n // For durations less than 1 second, show milliseconds\n if (ms < MS_IN_SECOND) {\n return `${durationFormatter.format(ms)}ms`;\n }\n\n // For durations less than 1 minute, show seconds\n if (ms < MS_IN_MINUTE) {\n return `${durationFormatter.format(ms / MS_IN_SECOND)}s`;\n }\n\n // Compact format: multi-unit without decimals (e.g. \"8m 20s\", \"2h 30m\")\n if (compact) {\n if (ms < MS_IN_HOUR) {\n const m = Math.floor(ms / MS_IN_MINUTE);\n const s = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);\n return s > 0 ? `${m}m ${s}s` : `${m}m`;\n }\n const h = Math.floor(ms / MS_IN_HOUR);\n const m = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);\n return m > 0 ? `${h}h ${m}m` : `${h}h`;\n }\n\n // Full format: human-readable multi-part\n const days = Math.floor(ms / MS_IN_DAY);\n const hours = Math.floor((ms % MS_IN_DAY) / MS_IN_HOUR);\n const minutes = Math.floor((ms % MS_IN_HOUR) / MS_IN_MINUTE);\n const seconds = Math.floor((ms % MS_IN_MINUTE) / MS_IN_SECOND);\n\n const parts: string[] = [];\n\n if (days > 0) {\n parts.push(`${days}d`);\n }\n if (hours > 0) {\n parts.push(`${hours}h`);\n }\n if (minutes > 0) {\n parts.push(`${minutes}m`);\n }\n if (hours <= 1 && (seconds > 0 || parts.length === 0)) {\n parts.push(`${seconds}s`);\n }\n\n return parts.join(' ');\n}\n\n/**\n * Returns a formatted pagination display string\n * @param currentPage - The current page number\n * @param totalPages - The total number of pages visited so far\n * @param hasMore - Whether there are more pages available\n * @returns Formatted string like \"Page 1 of 3+\" or \"Page 2 of 2\"\n */\nexport function getPaginationDisplay(\n currentPage: number,\n totalPages: number,\n hasMore: boolean\n): string {\n if (hasMore) {\n return `Page ${currentPage} of ${totalPages}+`;\n }\n return `Page ${currentPage} of ${totalPages}`;\n}\n\n// ============================================================================\n// Durable Agent Utilities\n// ============================================================================\n\n/**\n * Check if a step is a doStreamStep (LLM call with conversation input)\n */\nexport function isDoStreamStep(stepName: string): boolean {\n return stepName.endsWith('//doStreamStep');\n}\n\n/**\n * Extract the conversation from a hydrated doStreamStep input.\n * doStreamStep signature: (conversationPrompt, model, writable, tools, options)\n * So input[0] is the conversation.\n */\nexport function extractConversation(stepInput: unknown): ModelMessage[] | null {\n if (!Array.isArray(stepInput) || stepInput.length === 0) {\n return null;\n }\n\n const firstArg = stepInput[0];\n\n if (!Array.isArray(firstArg)) {\n return null;\n }\n\n // Validate it looks like ModelMessage[]\n if (\n !firstArg.every((msg) => msg && typeof msg === 'object' && 'role' in msg)\n ) {\n return null;\n }\n\n return firstArg as ModelMessage[];\n}\n\n/**\n * A doStreamStep with its conversation input extracted\n */\nexport interface StreamStep {\n stepId: string;\n stepName: string;\n displayName: string;\n conversation: ModelMessage[];\n}\n\n/**\n * Identifies all stream steps (doStreamStep) in a run and extracts their conversations.\n */\nexport function identifyStreamSteps(steps: Step[]): StreamStep[] {\n return steps\n .filter((step) => isDoStreamStep(step.stepName))\n .map((step) => {\n const functionName = step.stepName.split('//').pop() ?? 'unknown';\n const conversation = extractConversation(step.input) ?? [];\n\n return {\n stepId: step.stepId,\n stepName: step.stepName,\n displayName: functionName,\n conversation,\n };\n });\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Step } from '@workflow/world';\nimport type { ModelMessage } from 'ai';\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nconst MS_IN_SECOND = 1000;\nconst MS_IN_MINUTE = 60 * MS_IN_SECOND;\nconst MS_IN_HOUR = 60 * MS_IN_MINUTE;\nconst MS_IN_DAY = 24 * MS_IN_HOUR;\n\n/**\n * Formats a duration in milliseconds to a human-readable string.\n *\n * @param ms - Duration in milliseconds\n * @param compact - If true, returns a compact format (e.g., \"380ms\", \"2m 30s\").\n * If false (default), returns multi-part format (e.g., \"1m 13s\", \"2d 5h 3m 12s\").\n *\n * Compact format (timeline markers):\n * - < 1s: shows milliseconds (e.g., \"380ms\")\n * - < 1m: shows seconds (e.g., \"45s\")\n * - < 1h: shows minutes and seconds (e.g., \"2m 30s\")\n * - >= 1h: shows hours and minutes (e.g., \"2h 30m\")\n *\n * Full format:\n * - < 1s: shows milliseconds (e.g., \"380ms\")\n * - < 1m: shows seconds (e.g., \"45s\")\n * - >= 1m: shows decomposed format with whole seconds (e.g., \"1m 13s\", \"2m 13s\")\n */\nexport function formatDuration(ms: number, compact = false): string {\n if (ms === 0) {\n return '0s';\n }\n\n if (ms < MS_IN_SECOND) {\n const roundedMs = Math.round(ms);\n return roundedMs < MS_IN_SECOND ? `${roundedMs}ms` : '1s';\n }\n\n const roundedMs = Math.round(ms / MS_IN_SECOND) * MS_IN_SECOND;\n\n if (roundedMs < MS_IN_MINUTE) {\n return `${Math.floor(roundedMs / MS_IN_SECOND)}s`;\n }\n\n // Compact format: multi-unit without decimals (e.g. \"8m 20s\", \"2h 30m\")\n if (compact) {\n if (roundedMs < MS_IN_HOUR) {\n const m = Math.floor(roundedMs / MS_IN_MINUTE);\n const s = Math.floor((roundedMs % MS_IN_MINUTE) / MS_IN_SECOND);\n return s > 0 ? `${m}m ${s}s` : `${m}m`;\n }\n const h = Math.floor(roundedMs / MS_IN_HOUR);\n const m = Math.floor((roundedMs % MS_IN_HOUR) / MS_IN_MINUTE);\n return m > 0 ? `${h}h ${m}m` : `${h}h`;\n }\n\n // Full format: decompose into larger units + whole seconds.\n const days = Math.floor(roundedMs / MS_IN_DAY);\n const hours = Math.floor((roundedMs % MS_IN_DAY) / MS_IN_HOUR);\n const minutes = Math.floor((roundedMs % MS_IN_HOUR) / MS_IN_MINUTE);\n const seconds = Math.floor((roundedMs % MS_IN_MINUTE) / MS_IN_SECOND);\n\n const parts: string[] = [];\n\n if (days > 0) {\n parts.push(`${days}d`);\n }\n if (hours > 0) {\n parts.push(`${hours}h`);\n }\n if (minutes > 0) {\n parts.push(`${minutes}m`);\n }\n parts.push(`${seconds}s`);\n\n return parts.join(' ');\n}\n\n/**\n * Returns a formatted pagination display string\n * @param currentPage - The current page number\n * @param totalPages - The total number of pages visited so far\n * @param hasMore - Whether there are more pages available\n * @returns Formatted string like \"Page 1 of 3+\" or \"Page 2 of 2\"\n */\nexport function getPaginationDisplay(\n currentPage: number,\n totalPages: number,\n hasMore: boolean\n): string {\n if (hasMore) {\n return `Page ${currentPage} of ${totalPages}+`;\n }\n return `Page ${currentPage} of ${totalPages}`;\n}\n\n// ============================================================================\n// Durable Agent Utilities\n// ============================================================================\n\n/**\n * Check if a step is a doStreamStep (LLM call with conversation input)\n */\nexport function isDoStreamStep(stepName: string): boolean {\n return stepName.endsWith('//doStreamStep');\n}\n\n/**\n * Extract the conversation from a hydrated doStreamStep input.\n * doStreamStep signature: (conversationPrompt, model, writable, tools, options)\n * So input[0] is the conversation.\n */\nexport function extractConversation(stepInput: unknown): ModelMessage[] | null {\n if (!Array.isArray(stepInput) || stepInput.length === 0) {\n return null;\n }\n\n const firstArg = stepInput[0];\n\n if (!Array.isArray(firstArg)) {\n return null;\n }\n\n // Validate it looks like ModelMessage[]\n if (\n !firstArg.every((msg) => msg && typeof msg === 'object' && 'role' in msg)\n ) {\n return null;\n }\n\n return firstArg as ModelMessage[];\n}\n\n/**\n * A doStreamStep with its conversation input extracted\n */\nexport interface StreamStep {\n stepId: string;\n stepName: string;\n displayName: string;\n conversation: ModelMessage[];\n}\n\n/**\n * Identifies all stream steps (doStreamStep) in a run and extracts their conversations.\n */\nexport function identifyStreamSteps(steps: Step[]): StreamStep[] {\n return steps\n .filter((step) => isDoStreamStep(step.stepName))\n .map((step) => {\n const functionName = step.stepName.split('//').pop() ?? 'unknown';\n const conversation = extractConversation(step.input) ?? [];\n\n return {\n stepId: step.stepId,\n stepName: step.stepName,\n displayName: functionName,\n conversation,\n };\n });\n}\n"]}