@flanksource/clicky-ui 0.3.47 → 0.3.49

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 (255) hide show
  1. package/README.md +49 -0
  2. package/dist/comments/CommentSidePanel.cjs +10 -4
  3. package/dist/comments/CommentSidePanel.cjs.map +1 -1
  4. package/dist/comments/CommentSidePanel.d.ts +2 -0
  5. package/dist/comments/CommentSidePanel.d.ts.map +1 -1
  6. package/dist/comments/CommentSidePanel.js +10 -4
  7. package/dist/comments/CommentSidePanel.js.map +1 -1
  8. package/dist/comments/CommentThread.cjs +2 -0
  9. package/dist/comments/CommentThread.cjs.map +1 -1
  10. package/dist/comments/CommentThread.d.ts +2 -1
  11. package/dist/comments/CommentThread.d.ts.map +1 -1
  12. package/dist/comments/CommentThread.js +2 -0
  13. package/dist/comments/CommentThread.js.map +1 -1
  14. package/dist/comments/CommentThreadList.cjs +3 -0
  15. package/dist/comments/CommentThreadList.cjs.map +1 -1
  16. package/dist/comments/CommentThreadList.d.ts +3 -1
  17. package/dist/comments/CommentThreadList.d.ts.map +1 -1
  18. package/dist/comments/CommentThreadList.js +3 -0
  19. package/dist/comments/CommentThreadList.js.map +1 -1
  20. package/dist/data/CodeLine.cjs +4 -2
  21. package/dist/data/CodeLine.cjs.map +1 -1
  22. package/dist/data/CodeLine.d.ts.map +1 -1
  23. package/dist/data/CodeLine.js +4 -2
  24. package/dist/data/CodeLine.js.map +1 -1
  25. package/dist/data/DataTable.cjs +18 -8
  26. package/dist/data/DataTable.cjs.map +1 -1
  27. package/dist/data/DataTable.d.ts +4 -0
  28. package/dist/data/DataTable.d.ts.map +1 -1
  29. package/dist/data/DataTable.js +18 -8
  30. package/dist/data/DataTable.js.map +1 -1
  31. package/dist/data/SymbolBadge.cjs +61 -0
  32. package/dist/data/SymbolBadge.cjs.map +1 -0
  33. package/dist/data/SymbolBadge.d.ts +21 -0
  34. package/dist/data/SymbolBadge.d.ts.map +1 -0
  35. package/dist/data/SymbolBadge.js +61 -0
  36. package/dist/data/SymbolBadge.js.map +1 -0
  37. package/dist/data/TaskManager.cjs +183 -159
  38. package/dist/data/TaskManager.cjs.map +1 -1
  39. package/dist/data/TaskManager.d.ts +3 -1
  40. package/dist/data/TaskManager.d.ts.map +1 -1
  41. package/dist/data/TaskManager.js +185 -161
  42. package/dist/data/TaskManager.js.map +1 -1
  43. package/dist/data/TaskManagerButton.cjs +1 -0
  44. package/dist/data/TaskManagerButton.cjs.map +1 -1
  45. package/dist/data/TaskManagerButton.js +1 -0
  46. package/dist/data/TaskManagerButton.js.map +1 -1
  47. package/dist/data/TaskProgress.cjs +13 -7
  48. package/dist/data/TaskProgress.cjs.map +1 -1
  49. package/dist/data/TaskProgress.d.ts +3 -1
  50. package/dist/data/TaskProgress.d.ts.map +1 -1
  51. package/dist/data/TaskProgress.js +13 -7
  52. package/dist/data/TaskProgress.js.map +1 -1
  53. package/dist/data/test-runner/TestDetailPanel.cjs +26 -12
  54. package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -1
  55. package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -1
  56. package/dist/data/test-runner/TestDetailPanel.js +27 -13
  57. package/dist/data/test-runner/TestDetailPanel.js.map +1 -1
  58. package/dist/data/test-runner/TestRunner.cjs +6 -0
  59. package/dist/data/test-runner/TestRunner.cjs.map +1 -1
  60. package/dist/data/test-runner/TestRunner.d.ts +3 -1
  61. package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
  62. package/dist/data/test-runner/TestRunner.js +6 -0
  63. package/dist/data/test-runner/TestRunner.js.map +1 -1
  64. package/dist/data/test-runner/TestTreeNode.cjs +2 -1
  65. package/dist/data/test-runner/TestTreeNode.cjs.map +1 -1
  66. package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -1
  67. package/dist/data/test-runner/TestTreeNode.js +2 -1
  68. package/dist/data/test-runner/TestTreeNode.js.map +1 -1
  69. package/dist/data/test-runner/adapter.cjs +4 -0
  70. package/dist/data/test-runner/adapter.cjs.map +1 -1
  71. package/dist/data/test-runner/adapter.d.ts +9 -0
  72. package/dist/data/test-runner/adapter.d.ts.map +1 -1
  73. package/dist/data/test-runner/adapter.js +4 -0
  74. package/dist/data/test-runner/adapter.js.map +1 -1
  75. package/dist/data/test-runner/context.cjs.map +1 -1
  76. package/dist/data/test-runner/context.d.ts +2 -0
  77. package/dist/data/test-runner/context.d.ts.map +1 -1
  78. package/dist/data/test-runner/context.js.map +1 -1
  79. package/dist/data/test-runner/index.d.ts +2 -2
  80. package/dist/data/test-runner/index.d.ts.map +1 -1
  81. package/dist/data/test-runner/marks.cjs +42 -1
  82. package/dist/data/test-runner/marks.cjs.map +1 -1
  83. package/dist/data/test-runner/marks.d.ts +10 -0
  84. package/dist/data/test-runner/marks.d.ts.map +1 -1
  85. package/dist/data/test-runner/marks.js +42 -1
  86. package/dist/data/test-runner/marks.js.map +1 -1
  87. package/dist/data/traces/ProcessTraceTree.cjs +93 -0
  88. package/dist/data/traces/ProcessTraceTree.cjs.map +1 -0
  89. package/dist/data/traces/ProcessTraceTree.d.ts +24 -0
  90. package/dist/data/traces/ProcessTraceTree.d.ts.map +1 -0
  91. package/dist/data/traces/ProcessTraceTree.js +93 -0
  92. package/dist/data/traces/ProcessTraceTree.js.map +1 -0
  93. package/dist/data/traces/Sparkline.cjs +23 -0
  94. package/dist/data/traces/Sparkline.cjs.map +1 -0
  95. package/dist/data/traces/Sparkline.d.ts +13 -0
  96. package/dist/data/traces/Sparkline.d.ts.map +1 -0
  97. package/dist/data/traces/Sparkline.js +23 -0
  98. package/dist/data/traces/Sparkline.js.map +1 -0
  99. package/dist/data/traces/SqlTraceTable.cjs +104 -0
  100. package/dist/data/traces/SqlTraceTable.cjs.map +1 -0
  101. package/dist/data/traces/SqlTraceTable.d.ts +19 -0
  102. package/dist/data/traces/SqlTraceTable.d.ts.map +1 -0
  103. package/dist/data/traces/SqlTraceTable.js +104 -0
  104. package/dist/data/traces/SqlTraceTable.js.map +1 -0
  105. package/dist/data/traces/TerminalCastPlayer.cjs +162 -0
  106. package/dist/data/traces/TerminalCastPlayer.cjs.map +1 -0
  107. package/dist/data/traces/TerminalCastPlayer.d.ts +21 -0
  108. package/dist/data/traces/TerminalCastPlayer.d.ts.map +1 -0
  109. package/dist/data/traces/TerminalCastPlayer.js +162 -0
  110. package/dist/data/traces/TerminalCastPlayer.js.map +1 -0
  111. package/dist/data/traces/TraceHarView.cjs +15 -0
  112. package/dist/data/traces/TraceHarView.cjs.map +1 -0
  113. package/dist/data/traces/TraceHarView.d.ts +13 -0
  114. package/dist/data/traces/TraceHarView.d.ts.map +1 -0
  115. package/dist/data/traces/TraceHarView.js +15 -0
  116. package/dist/data/traces/TraceHarView.js.map +1 -0
  117. package/dist/data/traces/TraceView.cjs +54 -0
  118. package/dist/data/traces/TraceView.cjs.map +1 -0
  119. package/dist/data/traces/TraceView.d.ts +18 -0
  120. package/dist/data/traces/TraceView.d.ts.map +1 -0
  121. package/dist/data/traces/TraceView.js +54 -0
  122. package/dist/data/traces/TraceView.js.map +1 -0
  123. package/dist/data/traces/cast.cjs +81 -0
  124. package/dist/data/traces/cast.cjs.map +1 -0
  125. package/dist/data/traces/cast.d.ts +9 -0
  126. package/dist/data/traces/cast.d.ts.map +1 -0
  127. package/dist/data/traces/cast.js +81 -0
  128. package/dist/data/traces/cast.js.map +1 -0
  129. package/dist/data/traces/fixtures.d.ts +8 -0
  130. package/dist/data/traces/fixtures.d.ts.map +1 -0
  131. package/dist/data/traces/har-adapter.cjs +83 -0
  132. package/dist/data/traces/har-adapter.cjs.map +1 -0
  133. package/dist/data/traces/har-adapter.d.ts +13 -0
  134. package/dist/data/traces/har-adapter.d.ts.map +1 -0
  135. package/dist/data/traces/har-adapter.js +83 -0
  136. package/dist/data/traces/har-adapter.js.map +1 -0
  137. package/dist/data/traces/index.d.ts +12 -0
  138. package/dist/data/traces/index.d.ts.map +1 -0
  139. package/dist/data/traces/process-tree.cjs +53 -0
  140. package/dist/data/traces/process-tree.cjs.map +1 -0
  141. package/dist/data/traces/process-tree.d.ts +21 -0
  142. package/dist/data/traces/process-tree.d.ts.map +1 -0
  143. package/dist/data/traces/process-tree.js +53 -0
  144. package/dist/data/traces/process-tree.js.map +1 -0
  145. package/dist/data/traces/sql-trace.cjs +39 -0
  146. package/dist/data/traces/sql-trace.cjs.map +1 -0
  147. package/dist/data/traces/sql-trace.d.ts +31 -0
  148. package/dist/data/traces/sql-trace.d.ts.map +1 -0
  149. package/dist/data/traces/sql-trace.js +39 -0
  150. package/dist/data/traces/sql-trace.js.map +1 -0
  151. package/dist/data/traces/terminal-screen.cjs +270 -0
  152. package/dist/data/traces/terminal-screen.cjs.map +1 -0
  153. package/dist/data/traces/terminal-screen.d.ts +45 -0
  154. package/dist/data/traces/terminal-screen.d.ts.map +1 -0
  155. package/dist/data/traces/terminal-screen.js +270 -0
  156. package/dist/data/traces/terminal-screen.js.map +1 -0
  157. package/dist/data/traces/terminal-timeline.cjs +48 -0
  158. package/dist/data/traces/terminal-timeline.cjs.map +1 -0
  159. package/dist/data/traces/terminal-timeline.d.ts +26 -0
  160. package/dist/data/traces/terminal-timeline.d.ts.map +1 -0
  161. package/dist/data/traces/terminal-timeline.js +48 -0
  162. package/dist/data/traces/terminal-timeline.js.map +1 -0
  163. package/dist/data/traces/types.d.ts +139 -0
  164. package/dist/data/traces/types.d.ts.map +1 -0
  165. package/dist/data/verification/VerificationResults.cjs +4 -0
  166. package/dist/data/verification/VerificationResults.cjs.map +1 -1
  167. package/dist/data/verification/VerificationResults.d.ts +3 -1
  168. package/dist/data/verification/VerificationResults.d.ts.map +1 -1
  169. package/dist/data/verification/VerificationResults.js +4 -0
  170. package/dist/data/verification/VerificationResults.js.map +1 -1
  171. package/dist/data/verification/adapterViews.cjs +12 -3
  172. package/dist/data/verification/adapterViews.cjs.map +1 -1
  173. package/dist/data/verification/adapterViews.d.ts +4 -1
  174. package/dist/data/verification/adapterViews.d.ts.map +1 -1
  175. package/dist/data/verification/adapterViews.js +12 -3
  176. package/dist/data/verification/adapterViews.js.map +1 -1
  177. package/dist/data/verification/adapters.cjs +53 -10
  178. package/dist/data/verification/adapters.cjs.map +1 -1
  179. package/dist/data/verification/adapters.d.ts +7 -1
  180. package/dist/data/verification/adapters.d.ts.map +1 -1
  181. package/dist/data/verification/adapters.js +53 -10
  182. package/dist/data/verification/adapters.js.map +1 -1
  183. package/dist/data/verification/index.d.ts +4 -2
  184. package/dist/data/verification/index.d.ts.map +1 -1
  185. package/dist/data/verification/stepKind.cjs +217 -0
  186. package/dist/data/verification/stepKind.cjs.map +1 -0
  187. package/dist/data/verification/stepKind.d.ts +87 -0
  188. package/dist/data/verification/stepKind.d.ts.map +1 -0
  189. package/dist/data/verification/stepKind.js +217 -0
  190. package/dist/data/verification/stepKind.js.map +1 -0
  191. package/dist/data/verification/stepKindViews.cjs +111 -0
  192. package/dist/data/verification/stepKindViews.cjs.map +1 -0
  193. package/dist/data/verification/stepKindViews.d.ts +18 -0
  194. package/dist/data/verification/stepKindViews.d.ts.map +1 -0
  195. package/dist/data/verification/stepKindViews.js +111 -0
  196. package/dist/data/verification/stepKindViews.js.map +1 -0
  197. package/dist/data/verification/verifyReportTests.cjs +1 -0
  198. package/dist/data/verification/verifyReportTests.cjs.map +1 -1
  199. package/dist/data/verification/verifyReportTests.d.ts +2 -1
  200. package/dist/data/verification/verifyReportTests.d.ts.map +1 -1
  201. package/dist/data/verification/verifyReportTests.js +1 -0
  202. package/dist/data/verification/verifyReportTests.js.map +1 -1
  203. package/dist/data/version-info.cjs +3 -3
  204. package/dist/data/version-info.cjs.map +1 -1
  205. package/dist/data/version-info.js +3 -3
  206. package/dist/data/version-info.js.map +1 -1
  207. package/dist/data.cjs +36 -0
  208. package/dist/data.cjs.map +1 -1
  209. package/dist/data.d.ts +2 -0
  210. package/dist/data.d.ts.map +1 -1
  211. package/dist/data.js +38 -2
  212. package/dist/data.js.map +1 -1
  213. package/dist/index.cjs +38 -0
  214. package/dist/index.cjs.map +1 -1
  215. package/dist/index.js +40 -2
  216. package/dist/index.js.map +1 -1
  217. package/dist/rpc/FilterForm.cjs +9 -0
  218. package/dist/rpc/FilterForm.cjs.map +1 -1
  219. package/dist/rpc/FilterForm.d.ts +5 -1
  220. package/dist/rpc/FilterForm.d.ts.map +1 -1
  221. package/dist/rpc/FilterForm.js +9 -0
  222. package/dist/rpc/FilterForm.js.map +1 -1
  223. package/dist/rpc/OperationArgsForm.cjs +153 -0
  224. package/dist/rpc/OperationArgsForm.cjs.map +1 -0
  225. package/dist/rpc/OperationArgsForm.d.ts +16 -0
  226. package/dist/rpc/OperationArgsForm.d.ts.map +1 -0
  227. package/dist/rpc/OperationArgsForm.js +153 -0
  228. package/dist/rpc/OperationArgsForm.js.map +1 -0
  229. package/dist/rpc/OperationScheduleDialog.cjs +61 -17
  230. package/dist/rpc/OperationScheduleDialog.cjs.map +1 -1
  231. package/dist/rpc/OperationScheduleDialog.d.ts +3 -0
  232. package/dist/rpc/OperationScheduleDialog.d.ts.map +1 -1
  233. package/dist/rpc/OperationScheduleDialog.js +62 -18
  234. package/dist/rpc/OperationScheduleDialog.js.map +1 -1
  235. package/dist/rpc/OperationSchedules.cjs +6 -1
  236. package/dist/rpc/OperationSchedules.cjs.map +1 -1
  237. package/dist/rpc/OperationSchedules.d.ts.map +1 -1
  238. package/dist/rpc/OperationSchedules.js +6 -1
  239. package/dist/rpc/OperationSchedules.js.map +1 -1
  240. package/dist/rpc/operation-schedule-model.cjs +0 -7
  241. package/dist/rpc/operation-schedule-model.cjs.map +1 -1
  242. package/dist/rpc/operation-schedule-model.d.ts +0 -2
  243. package/dist/rpc/operation-schedule-model.d.ts.map +1 -1
  244. package/dist/rpc/operation-schedule-model.js +0 -7
  245. package/dist/rpc/operation-schedule-model.js.map +1 -1
  246. package/dist/rpc/operation-schedule-types.d.ts +6 -1
  247. package/dist/rpc/operation-schedule-types.d.ts.map +1 -1
  248. package/dist/rpc.cjs +2 -0
  249. package/dist/rpc.cjs.map +1 -1
  250. package/dist/rpc.d.ts +1 -0
  251. package/dist/rpc.d.ts.map +1 -1
  252. package/dist/rpc.js +2 -0
  253. package/dist/rpc.js.map +1 -1
  254. package/dist/styles.css +1 -1
  255. package/package.json +1 -1
@@ -0,0 +1,162 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState, useEffect, useRef } from "react";
3
+ import { Button } from "../../components/button.js";
4
+ import { SegmentedControl } from "../../components/SegmentedControl.js";
5
+ import { cn } from "../../lib/utils.js";
6
+ import { AnsiHtml } from "../AnsiHtml.js";
7
+ import { Badge } from "../Badge.js";
8
+ import { Icon } from "../Icon.js";
9
+ import { parseCast } from "./cast.js";
10
+ import { screenToAnsi } from "./terminal-screen.js";
11
+ import { buildTimeline, stepCountAt, replayTo } from "./terminal-timeline.js";
12
+ import { UiPause } from "../../icons/components/UiPause.js";
13
+ import { UiPlay } from "../../icons/components/UiPlay.js";
14
+ import { UiSkipForward } from "../../icons/components/UiSkipForward.js";
15
+ const TICK_MS = 50;
16
+ const SPEEDS = ["1", "2", "4"];
17
+ const SPEED_OPTIONS = SPEEDS.map((speed) => ({ id: speed, label: `${speed}x` }));
18
+ const VIEW_OPTIONS = [
19
+ { id: "replay", label: "Replay" },
20
+ { id: "settled", label: "Settled screen" }
21
+ ];
22
+ function resolveEvents(events, castText) {
23
+ if (events === void 0 === (castText === void 0)) {
24
+ throw new Error("TerminalCastPlayer takes either events or castText, not both or neither");
25
+ }
26
+ return events ?? parseCast(castText);
27
+ }
28
+ function seconds(value) {
29
+ return `${value.toFixed(1)}s`;
30
+ }
31
+ function ExitBadge({ exit }) {
32
+ if (exit.exitCode === void 0) return /* @__PURE__ */ jsx(Badge, { variant: "outline", tone: "neutral", size: "sm", children: "exited" });
33
+ return /* @__PURE__ */ jsx(Badge, { variant: "soft", tone: exit.exitCode === 0 ? "success" : "danger", size: "sm", className: "font-mono", children: `exit ${exit.exitCode}` });
34
+ }
35
+ function useReplayScreen(timeline, position) {
36
+ const cache = useRef(void 0);
37
+ const count = stepCountAt(timeline.steps, position);
38
+ return useMemo(() => {
39
+ var _a;
40
+ const previous = ((_a = cache.current) == null ? void 0 : _a.timeline) === timeline ? cache.current.replay : void 0;
41
+ const replay = replayTo(previous, timeline, count);
42
+ cache.current = { timeline, replay };
43
+ return { ansi: screenToAnsi(replay.screen), cols: replay.screen.width, rows: replay.screen.height };
44
+ }, [timeline, count]);
45
+ }
46
+ function TerminalCastPlayer({
47
+ events,
48
+ castText,
49
+ autoPlay = false,
50
+ emptyLabel = "No terminal output recorded",
51
+ className
52
+ }) {
53
+ var _a, _b;
54
+ const timeline = useMemo(() => buildTimeline(resolveEvents(events, castText)), [events, castText]);
55
+ const [position, setPosition] = useState(0);
56
+ const [playing, setPlaying] = useState(autoPlay);
57
+ const [speed, setSpeed] = useState("1");
58
+ const [view, setView] = useState("replay");
59
+ const replay = useReplayScreen(timeline, position);
60
+ useEffect(() => {
61
+ if (!playing) return;
62
+ let last = Date.now();
63
+ const timer = setInterval(() => {
64
+ const now = Date.now();
65
+ const elapsed = (now - last) / 1e3;
66
+ last = now;
67
+ setPosition((current) => Math.min(timeline.duration, current + elapsed * Number(speed)));
68
+ }, TICK_MS);
69
+ return () => clearInterval(timer);
70
+ }, [playing, speed, timeline.duration]);
71
+ useEffect(() => {
72
+ if (playing && position >= timeline.duration) setPlaying(false);
73
+ }, [playing, position, timeline.duration]);
74
+ if (timeline.steps.length === 0 && timeline.settled === void 0) {
75
+ return /* @__PURE__ */ jsx("div", { className: "p-density-6 text-center text-muted-foreground text-sm", children: emptyLabel });
76
+ }
77
+ const hasSettled = timeline.settled !== void 0;
78
+ const showSettled = view === "settled" && hasSettled;
79
+ const play = () => {
80
+ if (position >= timeline.duration) setPosition(0);
81
+ setPlaying(true);
82
+ };
83
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex min-h-0 flex-col gap-density-2", className), children: [
84
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-density-2 text-xs text-muted-foreground", children: [
85
+ ((_a = timeline.exit) == null ? void 0 : _a.command) ? /* @__PURE__ */ jsx("code", { className: "font-mono text-foreground", children: timeline.exit.command }) : null,
86
+ timeline.exit ? /* @__PURE__ */ jsx(ExitBadge, { exit: timeline.exit }) : null,
87
+ ((_b = timeline.exit) == null ? void 0 : _b.truncated) ? /* @__PURE__ */ jsx("span", { title: "Size caps or a full buffer stopped some output events being recorded", children: /* @__PURE__ */ jsx(Badge, { variant: "soft", tone: "warning", size: "sm", children: "output truncated" }) }) : null,
88
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: `${timeline.width}x${timeline.height}` }),
89
+ /* @__PURE__ */ jsx("span", { className: "flex-1" }),
90
+ hasSettled && timeline.steps.length > 0 ? /* @__PURE__ */ jsx(SegmentedControl, { size: "sm", "aria-label": "Screen view", value: view, options: VIEW_OPTIONS, onChange: setView }) : null
91
+ ] }),
92
+ /* @__PURE__ */ jsx(
93
+ "div",
94
+ {
95
+ role: "region",
96
+ "aria-label": "Terminal screen",
97
+ "data-cols": showSettled ? void 0 : replay.cols,
98
+ className: "overflow-auto rounded border bg-black p-density-2",
99
+ children: /* @__PURE__ */ jsx(
100
+ AnsiHtml,
101
+ {
102
+ text: showSettled ? timeline.settled : replay.ansi,
103
+ className: "font-mono text-xs leading-5 whitespace-pre text-gray-100"
104
+ }
105
+ )
106
+ }
107
+ ),
108
+ timeline.steps.length > 0 && !showSettled ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-density-2", children: [
109
+ /* @__PURE__ */ jsx(
110
+ Button,
111
+ {
112
+ variant: "ghost",
113
+ size: "icon",
114
+ "aria-label": playing ? "Pause" : "Play",
115
+ onClick: playing ? () => setPlaying(false) : play,
116
+ children: /* @__PURE__ */ jsx(Icon, { icon: playing ? UiPause : UiPlay })
117
+ }
118
+ ),
119
+ /* @__PURE__ */ jsx(
120
+ "input",
121
+ {
122
+ type: "range",
123
+ "aria-label": "Seek",
124
+ className: "min-w-0 flex-1 accent-primary",
125
+ min: 0,
126
+ max: timeline.duration,
127
+ step: "any",
128
+ value: position,
129
+ onChange: (event) => setPosition(Number(event.target.value))
130
+ }
131
+ ),
132
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: `${seconds(position)} / ${seconds(timeline.duration)}` }),
133
+ /* @__PURE__ */ jsx(
134
+ SegmentedControl,
135
+ {
136
+ size: "sm",
137
+ "aria-label": "Playback speed",
138
+ value: speed,
139
+ options: SPEED_OPTIONS,
140
+ onChange: setSpeed
141
+ }
142
+ ),
143
+ /* @__PURE__ */ jsx(
144
+ Button,
145
+ {
146
+ variant: "ghost",
147
+ size: "icon",
148
+ "aria-label": "Jump to end",
149
+ onClick: () => {
150
+ setPlaying(false);
151
+ setPosition(timeline.duration);
152
+ },
153
+ children: /* @__PURE__ */ jsx(Icon, { icon: UiSkipForward })
154
+ }
155
+ )
156
+ ] }) : null
157
+ ] });
158
+ }
159
+ export {
160
+ TerminalCastPlayer
161
+ };
162
+ //# sourceMappingURL=TerminalCastPlayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TerminalCastPlayer.js","sources":["../../../src/data/traces/TerminalCastPlayer.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { Button } from \"../../components/button\";\nimport { SegmentedControl } from \"../../components/SegmentedControl\";\nimport { UiPause, UiPlay, UiSkipForward } from \"../../icons\";\nimport { cn } from \"../../lib/utils\";\nimport { AnsiHtml } from \"../AnsiHtml\";\nimport { Badge } from \"../Badge\";\nimport { Icon } from \"../Icon\";\nimport { parseCast } from \"./cast\";\nimport { screenToAnsi } from \"./terminal-screen\";\nimport {\n buildTimeline,\n replayTo,\n stepCountAt,\n type ScreenReplay,\n type TerminalTimeline,\n} from \"./terminal-timeline\";\nimport type { TerminalEvent } from \"./types\";\n\nconst TICK_MS = 50;\nconst SPEEDS = [\"1\", \"2\", \"4\"] as const;\ntype Speed = (typeof SPEEDS)[number];\ntype View = \"replay\" | \"settled\";\n\nconst SPEED_OPTIONS = SPEEDS.map((speed) => ({ id: speed, label: `${speed}x` }));\nconst VIEW_OPTIONS: { id: View; label: string }[] = [\n { id: \"replay\", label: \"Replay\" },\n { id: \"settled\", label: \"Settled screen\" },\n];\n\nexport type TerminalCastPlayerProps = {\n /** `terminal` trace records. Give this or `castText`. */\n events?: TerminalEvent[];\n /** An asciinema v2 cast, such as a downloaded `.cast`. Give this or `events`. */\n castText?: string;\n /** Start playing on mount. */\n autoPlay?: boolean;\n /** Shown when the session recorded no events. */\n emptyLabel?: string;\n /** Classes applied to the player root. */\n className?: string;\n};\n\nfunction resolveEvents(events: TerminalEvent[] | undefined, castText: string | undefined): TerminalEvent[] {\n if ((events === undefined) === (castText === undefined)) {\n throw new Error(\"TerminalCastPlayer takes either events or castText, not both or neither\");\n }\n return events ?? parseCast(castText!);\n}\n\nfunction seconds(value: number): string {\n return `${value.toFixed(1)}s`;\n}\n\nfunction ExitBadge({ exit }: { exit: TerminalEvent }) {\n if (exit.exitCode === undefined) return <Badge variant=\"outline\" tone=\"neutral\" size=\"sm\">exited</Badge>;\n return (\n <Badge variant=\"soft\" tone={exit.exitCode === 0 ? \"success\" : \"danger\"} size=\"sm\" className=\"font-mono\">\n {`exit ${exit.exitCode}`}\n </Badge>\n );\n}\n\n/** Feeds the timeline to a screen as the position moves, reusing the screen while moving forward. */\nfunction useReplayScreen(timeline: TerminalTimeline, position: number) {\n const cache = useRef<{ timeline: TerminalTimeline; replay: ScreenReplay } | undefined>(undefined);\n const count = stepCountAt(timeline.steps, position);\n return useMemo(() => {\n const previous = cache.current?.timeline === timeline ? cache.current.replay : undefined;\n const replay = replayTo(previous, timeline, count);\n cache.current = { timeline, replay };\n return { ansi: screenToAnsi(replay.screen), cols: replay.screen.width, rows: replay.screen.height };\n }, [timeline, count]);\n}\n\n/**\n * Replays a `terminal` trace: output events over time with play/pause, a\n * scrubber, 1x/2x/4x speed and jump-to-end, honouring resizes. A downloaded\n * asciinema cast renders the same way through `castText`. When the capture\n * settled a screen, it is offered as the \"Settled screen\" view.\n */\nexport function TerminalCastPlayer({\n events,\n castText,\n autoPlay = false,\n emptyLabel = \"No terminal output recorded\",\n className,\n}: TerminalCastPlayerProps) {\n const timeline = useMemo(() => buildTimeline(resolveEvents(events, castText)), [events, castText]);\n const [position, setPosition] = useState(0);\n const [playing, setPlaying] = useState(autoPlay);\n const [speed, setSpeed] = useState<Speed>(\"1\");\n const [view, setView] = useState<View>(\"replay\");\n const replay = useReplayScreen(timeline, position);\n\n useEffect(() => {\n if (!playing) return;\n let last = Date.now();\n const timer = setInterval(() => {\n const now = Date.now();\n const elapsed = (now - last) / 1000;\n last = now;\n setPosition((current) => Math.min(timeline.duration, current + elapsed * Number(speed)));\n }, TICK_MS);\n return () => clearInterval(timer);\n }, [playing, speed, timeline.duration]);\n\n useEffect(() => {\n if (playing && position >= timeline.duration) setPlaying(false);\n }, [playing, position, timeline.duration]);\n\n if (timeline.steps.length === 0 && timeline.settled === undefined) {\n return <div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>;\n }\n\n const hasSettled = timeline.settled !== undefined;\n const showSettled = view === \"settled\" && hasSettled;\n const play = () => {\n if (position >= timeline.duration) setPosition(0);\n setPlaying(true);\n };\n\n return (\n <div className={cn(\"flex min-h-0 flex-col gap-density-2\", className)}>\n <div className=\"flex flex-wrap items-center gap-density-2 text-xs text-muted-foreground\">\n {timeline.exit?.command ? <code className=\"font-mono text-foreground\">{timeline.exit.command}</code> : null}\n {timeline.exit ? <ExitBadge exit={timeline.exit} /> : null}\n {timeline.exit?.truncated ? (\n <span title=\"Size caps or a full buffer stopped some output events being recorded\">\n <Badge variant=\"soft\" tone=\"warning\" size=\"sm\">output truncated</Badge>\n </span>\n ) : null}\n <span className=\"tabular-nums\">{`${timeline.width}x${timeline.height}`}</span>\n <span className=\"flex-1\" />\n {hasSettled && timeline.steps.length > 0 ? (\n <SegmentedControl size=\"sm\" aria-label=\"Screen view\" value={view} options={VIEW_OPTIONS} onChange={setView} />\n ) : null}\n </div>\n\n <div\n role=\"region\"\n aria-label=\"Terminal screen\"\n data-cols={showSettled ? undefined : replay.cols}\n className=\"overflow-auto rounded border bg-black p-density-2\"\n >\n <AnsiHtml\n text={showSettled ? timeline.settled! : replay.ansi}\n className=\"font-mono text-xs leading-5 whitespace-pre text-gray-100\"\n />\n </div>\n\n {timeline.steps.length > 0 && !showSettled ? (\n <div className=\"flex items-center gap-density-2\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={playing ? \"Pause\" : \"Play\"}\n onClick={playing ? () => setPlaying(false) : play}\n >\n <Icon icon={playing ? UiPause : UiPlay} />\n </Button>\n <input\n type=\"range\"\n aria-label=\"Seek\"\n className=\"min-w-0 flex-1 accent-primary\"\n min={0}\n max={timeline.duration}\n step=\"any\"\n value={position}\n onChange={(event) => setPosition(Number(event.target.value))}\n />\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\">\n {`${seconds(position)} / ${seconds(timeline.duration)}`}\n </span>\n <SegmentedControl\n size=\"sm\"\n aria-label=\"Playback speed\"\n value={speed}\n options={SPEED_OPTIONS}\n onChange={setSpeed}\n />\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label=\"Jump to end\"\n onClick={() => {\n setPlaying(false);\n setPosition(timeline.duration);\n }}\n >\n <Icon icon={UiSkipForward} />\n </Button>\n </div>\n ) : null}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const HarPanel = require("../har/HarPanel.cjs");
6
+ const harAdapter = require("./har-adapter.cjs");
7
+ function TraceHarView({ rows, emptyLabel = "No HTTP exchanges recorded", ...panel }) {
8
+ const entries = react.useMemo(
9
+ () => rows.map((row, index) => harAdapter.exchangeToHarEntry(row, { id: `${row.startedAt}#${index}` })),
10
+ [rows]
11
+ );
12
+ return /* @__PURE__ */ jsxRuntime.jsx(HarPanel.HarPanel, { entries, emptyLabel, ...panel });
13
+ }
14
+ exports.TraceHarView = TraceHarView;
15
+ //# sourceMappingURL=TraceHarView.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceHarView.cjs","sources":["../../../src/data/traces/TraceHarView.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { HarPanel, type HarPanelProps } from \"../har/HarPanel\";\nimport { exchangeToHarEntry } from \"./har-adapter\";\nimport type { HttpExchangeRow } from \"./types\";\n\nexport type TraceHarViewProps = Omit<HarPanelProps, \"entries\"> & {\n /** `http` / `http_proxy` trace records. */\n rows: HttpExchangeRow[];\n};\n\n/**\n * `http` / `http_proxy` trace records rendered by the shared HarPanel. Bodies\n * the store parsed into structure are shown as JSON; a tunnelled CONNECT shows\n * its bytes in and out where a body would be.\n */\nexport function TraceHarView({ rows, emptyLabel = \"No HTTP exchanges recorded\", ...panel }: TraceHarViewProps) {\n const entries = useMemo(\n () => rows.map((row, index) => exchangeToHarEntry(row, { id: `${row.startedAt}#${index}` })),\n [rows],\n );\n return <HarPanel entries={entries} emptyLabel={emptyLabel} {...panel} />;\n}\n"],"names":["useMemo","exchangeToHarEntry","jsx","HarPanel"],"mappings":";;;;;;AAeO,SAAS,aAAa,EAAE,MAAM,aAAa,8BAA8B,GAAG,SAA4B;AAC7G,QAAM,UAAUA,MAAAA;AAAAA,IACd,MAAM,KAAK,IAAI,CAAC,KAAK,UAAUC,WAAAA,mBAAmB,KAAK,EAAE,IAAI,GAAG,IAAI,SAAS,IAAI,KAAK,GAAA,CAAI,CAAC;AAAA,IAC3F,CAAC,IAAI;AAAA,EAAA;AAEP,SAAOC,2BAAAA,IAACC,SAAAA,UAAA,EAAS,SAAkB,YAAyB,GAAG,OAAO;AACxE;;"}
@@ -0,0 +1,13 @@
1
+ import { HarPanelProps } from '../har/HarPanel.js';
2
+ import { HttpExchangeRow } from './types.js';
3
+ export type TraceHarViewProps = Omit<HarPanelProps, "entries"> & {
4
+ /** `http` / `http_proxy` trace records. */
5
+ rows: HttpExchangeRow[];
6
+ };
7
+ /**
8
+ * `http` / `http_proxy` trace records rendered by the shared HarPanel. Bodies
9
+ * the store parsed into structure are shown as JSON; a tunnelled CONNECT shows
10
+ * its bytes in and out where a body would be.
11
+ */
12
+ export declare function TraceHarView({ rows, emptyLabel, ...panel }: TraceHarViewProps): import("react").JSX.Element;
13
+ //# sourceMappingURL=TraceHarView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceHarView.d.ts","sourceRoot":"","sources":["../../../src/data/traces/TraceHarView.tsx"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG;IAC/D,2CAA2C;IAC3C,IAAI,EAAE,eAAe,EAAE,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,UAAyC,EAAE,GAAG,KAAK,EAAE,EAAE,iBAAiB,+BAM5G"}
@@ -0,0 +1,15 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { HarPanel } from "../har/HarPanel.js";
4
+ import { exchangeToHarEntry } from "./har-adapter.js";
5
+ function TraceHarView({ rows, emptyLabel = "No HTTP exchanges recorded", ...panel }) {
6
+ const entries = useMemo(
7
+ () => rows.map((row, index) => exchangeToHarEntry(row, { id: `${row.startedAt}#${index}` })),
8
+ [rows]
9
+ );
10
+ return /* @__PURE__ */ jsx(HarPanel, { entries, emptyLabel, ...panel });
11
+ }
12
+ export {
13
+ TraceHarView
14
+ };
15
+ //# sourceMappingURL=TraceHarView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceHarView.js","sources":["../../../src/data/traces/TraceHarView.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { HarPanel, type HarPanelProps } from \"../har/HarPanel\";\nimport { exchangeToHarEntry } from \"./har-adapter\";\nimport type { HttpExchangeRow } from \"./types\";\n\nexport type TraceHarViewProps = Omit<HarPanelProps, \"entries\"> & {\n /** `http` / `http_proxy` trace records. */\n rows: HttpExchangeRow[];\n};\n\n/**\n * `http` / `http_proxy` trace records rendered by the shared HarPanel. Bodies\n * the store parsed into structure are shown as JSON; a tunnelled CONNECT shows\n * its bytes in and out where a body would be.\n */\nexport function TraceHarView({ rows, emptyLabel = \"No HTTP exchanges recorded\", ...panel }: TraceHarViewProps) {\n const entries = useMemo(\n () => rows.map((row, index) => exchangeToHarEntry(row, { id: `${row.startedAt}#${index}` })),\n [rows],\n );\n return <HarPanel entries={entries} emptyLabel={emptyLabel} {...panel} />;\n}\n"],"names":[],"mappings":";;;;AAeO,SAAS,aAAa,EAAE,MAAM,aAAa,8BAA8B,GAAG,SAA4B;AAC7G,QAAM,UAAU;AAAA,IACd,MAAM,KAAK,IAAI,CAAC,KAAK,UAAU,mBAAmB,KAAK,EAAE,IAAI,GAAG,IAAI,SAAS,IAAI,KAAK,GAAA,CAAI,CAAC;AAAA,IAC3F,CAAC,IAAI;AAAA,EAAA;AAEP,SAAO,oBAAC,UAAA,EAAS,SAAkB,YAAyB,GAAG,OAAO;AACxE;"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const utils = require("../../lib/utils.cjs");
6
+ const dataTableUtils = require("../data-table-utils.cjs");
7
+ const DataTable = require("../DataTable.cjs");
8
+ const ProcessTraceTree = require("./ProcessTraceTree.cjs");
9
+ const SqlTraceTable = require("./SqlTraceTable.cjs");
10
+ const TerminalCastPlayer = require("./TerminalCastPlayer.cjs");
11
+ const TraceHarView = require("./TraceHarView.cjs");
12
+ function requireField(kind, rows, field) {
13
+ rows.forEach((row, index) => {
14
+ if (row[field] === void 0 || row[field] === null) {
15
+ throw new Error(`trace kind "${kind}" row ${index} has no "${field}"`);
16
+ }
17
+ });
18
+ }
19
+ function GenericTraceTable({ kind, rows }) {
20
+ const columns = react.useMemo(() => dataTableUtils.inferColumns(rows, { literalKeys: true }), [rows]);
21
+ return /* @__PURE__ */ jsxRuntime.jsx(DataTable.DataTable, { data: rows, columns, autoFilter: true, emptyMessage: `No ${kind} records` });
22
+ }
23
+ function ProcessTraceView({ rows, samples }) {
24
+ const [selectedKey, setSelectedKey] = react.useState(null);
25
+ return /* @__PURE__ */ jsxRuntime.jsx(ProcessTraceTree.ProcessTraceTree, { nodes: rows, selectedKey, onSelect: setSelectedKey, ...samples ? { samples } : {} });
26
+ }
27
+ function TraceView({ kind, rows, samples, className }) {
28
+ const body = (() => {
29
+ switch (kind) {
30
+ case "http":
31
+ case "http_proxy":
32
+ requireField(kind, rows, "request");
33
+ requireField(kind, rows, "response");
34
+ return /* @__PURE__ */ jsxRuntime.jsx(TraceHarView.TraceHarView, { rows });
35
+ case "pg_proxy":
36
+ case "sql_statements":
37
+ requireField(kind, rows, "sql");
38
+ return /* @__PURE__ */ jsxRuntime.jsx(SqlTraceTable.SqlTraceTable, { rows });
39
+ case "terminal":
40
+ requireField(kind, rows, "kind");
41
+ requireField(kind, rows, "offset");
42
+ return /* @__PURE__ */ jsxRuntime.jsx(TerminalCastPlayer.TerminalCastPlayer, { events: rows });
43
+ case "process_tree":
44
+ requireField(kind, rows, "key");
45
+ if (samples) requireField("process_samples", samples, "key");
46
+ return /* @__PURE__ */ jsxRuntime.jsx(ProcessTraceView, { rows, samples });
47
+ default:
48
+ return /* @__PURE__ */ jsxRuntime.jsx(GenericTraceTable, { kind, rows });
49
+ }
50
+ })();
51
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: utils.cn("flex h-full min-h-0 flex-col", className), children: body });
52
+ }
53
+ exports.TraceView = TraceView;
54
+ //# sourceMappingURL=TraceView.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceView.cjs","sources":["../../../src/data/traces/TraceView.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { inferColumns } from \"../data-table-utils\";\nimport { DataTable } from \"../DataTable\";\nimport { ProcessTraceTree } from \"./ProcessTraceTree\";\nimport { SqlTraceTable } from \"./SqlTraceTable\";\nimport { TerminalCastPlayer } from \"./TerminalCastPlayer\";\nimport { TraceHarView } from \"./TraceHarView\";\nimport type {\n HttpExchangeRow,\n ProcessNodeRow,\n ProcessSampleRow,\n SqlStatementRow,\n TerminalEvent,\n TraceRow,\n} from \"./types\";\n\nexport type TraceViewProps = {\n /** The record kind: `http`, `http_proxy`, `pg_proxy`, `sql_statements`, `terminal`, `process_tree`, or any other. */\n kind: string;\n /** The kind's records, as its stream stores them. */\n rows: TraceRow[];\n /** `process_samples` records of the same root; used by `process_tree` for CPU sparklines. */\n samples?: TraceRow[];\n /** Classes applied to the viewer root. */\n className?: string;\n};\n\n/** Each row of a kind with a dedicated viewer must carry the field that viewer cannot do without. */\nfunction requireField(kind: string, rows: TraceRow[], field: string): void {\n rows.forEach((row, index) => {\n if (row[field] === undefined || row[field] === null) {\n throw new Error(`trace kind \"${kind}\" row ${index} has no \"${field}\"`);\n }\n });\n}\n\nfunction GenericTraceTable({ kind, rows }: { kind: string; rows: TraceRow[] }) {\n const columns = useMemo(() => inferColumns(rows, { literalKeys: true }), [rows]);\n return <DataTable data={rows} columns={columns} autoFilter emptyMessage={`No ${kind} records`} />;\n}\n\nfunction ProcessTraceView({ rows, samples }: { rows: ProcessNodeRow[]; samples: ProcessSampleRow[] | undefined }) {\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n return <ProcessTraceTree nodes={rows} selectedKey={selectedKey} onSelect={setSelectedKey} {...(samples ? { samples } : {})} />;\n}\n\n/**\n * Renders a trace kind's records with its dedicated viewer: HTTP exchanges in\n * a HAR panel, SQL statements in a table, a terminal session in a cast player,\n * a process tree as a tree. Any other kind gets a table over the row keys.\n */\nexport function TraceView({ kind, rows, samples, className }: TraceViewProps) {\n const body = (() => {\n switch (kind) {\n case \"http\":\n case \"http_proxy\":\n requireField(kind, rows, \"request\");\n requireField(kind, rows, \"response\");\n return <TraceHarView rows={rows as HttpExchangeRow[]} />;\n case \"pg_proxy\":\n case \"sql_statements\":\n requireField(kind, rows, \"sql\");\n return <SqlTraceTable rows={rows as SqlStatementRow[]} />;\n case \"terminal\":\n requireField(kind, rows, \"kind\");\n requireField(kind, rows, \"offset\");\n return <TerminalCastPlayer events={rows as TerminalEvent[]} />;\n case \"process_tree\":\n requireField(kind, rows, \"key\");\n if (samples) requireField(\"process_samples\", samples, \"key\");\n return <ProcessTraceView rows={rows as ProcessNodeRow[]} samples={samples as ProcessSampleRow[] | undefined} />;\n default:\n return <GenericTraceTable kind={kind} rows={rows} />;\n }\n })();\n return <div className={cn(\"flex h-full min-h-0 flex-col\", className)}>{body}</div>;\n}\n"],"names":["useMemo","inferColumns","jsx","DataTable","useState","ProcessTraceTree","TraceHarView","SqlTraceTable","TerminalCastPlayer","cn"],"mappings":";;;;;;;;;;;AA6BA,SAAS,aAAa,MAAc,MAAkB,OAAqB;AACzE,OAAK,QAAQ,CAAC,KAAK,UAAU;AAC3B,QAAI,IAAI,KAAK,MAAM,UAAa,IAAI,KAAK,MAAM,MAAM;AACnD,YAAM,IAAI,MAAM,eAAe,IAAI,SAAS,KAAK,YAAY,KAAK,GAAG;AAAA,IACvE;AAAA,EACF,CAAC;AACH;AAEA,SAAS,kBAAkB,EAAE,MAAM,QAA4C;AAC7E,QAAM,UAAUA,MAAAA,QAAQ,MAAMC,eAAAA,aAAa,MAAM,EAAE,aAAa,KAAA,CAAM,GAAG,CAAC,IAAI,CAAC;AAC/E,SAAOC,2BAAAA,IAACC,UAAAA,WAAA,EAAU,MAAM,MAAM,SAAkB,YAAU,MAAC,cAAc,MAAM,IAAI,WAAA,CAAY;AACjG;AAEA,SAAS,iBAAiB,EAAE,MAAM,WAAgF;AAChH,QAAM,CAAC,aAAa,cAAc,IAAIC,MAAAA,SAAwB,IAAI;AAClE,SAAOF,2BAAAA,IAACG,iBAAAA,kBAAA,EAAiB,OAAO,MAAM,aAA0B,UAAU,gBAAiB,GAAI,UAAU,EAAE,QAAA,IAAY,CAAA,EAAC,CAAI;AAC9H;AAOO,SAAS,UAAU,EAAE,MAAM,MAAM,SAAS,aAA6B;AAC5E,QAAM,QAAQ,MAAM;AAClB,YAAQ,MAAA;AAAA,MACN,KAAK;AAAA,MACL,KAAK;AACH,qBAAa,MAAM,MAAM,SAAS;AAClC,qBAAa,MAAM,MAAM,UAAU;AACnC,eAAOH,+BAACI,aAAAA,gBAAa,MAAiC;AAAA,MACxD,KAAK;AAAA,MACL,KAAK;AACH,qBAAa,MAAM,MAAM,KAAK;AAC9B,eAAOJ,+BAACK,cAAAA,iBAAc,MAAiC;AAAA,MACzD,KAAK;AACH,qBAAa,MAAM,MAAM,MAAM;AAC/B,qBAAa,MAAM,MAAM,QAAQ;AACjC,eAAOL,2BAAAA,IAACM,mBAAAA,oBAAA,EAAmB,QAAQ,KAAA,CAAyB;AAAA,MAC9D,KAAK;AACH,qBAAa,MAAM,MAAM,KAAK;AAC9B,YAAI,QAAS,cAAa,mBAAmB,SAAS,KAAK;AAC3D,eAAON,2BAAAA,IAAC,kBAAA,EAAiB,MAAgC,QAAA,CAAoD;AAAA,MAC/G;AACE,eAAOA,2BAAAA,IAAC,mBAAA,EAAkB,MAAY,KAAA,CAAY;AAAA,IAAA;AAAA,EAExD,GAAA;AACA,wCAAQ,OAAA,EAAI,WAAWO,MAAAA,GAAG,gCAAgC,SAAS,GAAI,UAAA,MAAK;AAC9E;;"}
@@ -0,0 +1,18 @@
1
+ import { TraceRow } from './types.js';
2
+ export type TraceViewProps = {
3
+ /** The record kind: `http`, `http_proxy`, `pg_proxy`, `sql_statements`, `terminal`, `process_tree`, or any other. */
4
+ kind: string;
5
+ /** The kind's records, as its stream stores them. */
6
+ rows: TraceRow[];
7
+ /** `process_samples` records of the same root; used by `process_tree` for CPU sparklines. */
8
+ samples?: TraceRow[];
9
+ /** Classes applied to the viewer root. */
10
+ className?: string;
11
+ };
12
+ /**
13
+ * Renders a trace kind's records with its dedicated viewer: HTTP exchanges in
14
+ * a HAR panel, SQL statements in a table, a terminal session in a cast player,
15
+ * a process tree as a tree. Any other kind gets a table over the row keys.
16
+ */
17
+ export declare function TraceView({ kind, rows, samples, className }: TraceViewProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=TraceView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceView.d.ts","sourceRoot":"","sources":["../../../src/data/traces/TraceView.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAMV,QAAQ,EACT,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,cAAc,GAAG;IAC3B,qHAAqH;IACrH,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;IACrB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAqBF;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,cAAc,+BAyB3E"}
@@ -0,0 +1,54 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { cn } from "../../lib/utils.js";
4
+ import { inferColumns } from "../data-table-utils.js";
5
+ import { DataTable } from "../DataTable.js";
6
+ import { ProcessTraceTree } from "./ProcessTraceTree.js";
7
+ import { SqlTraceTable } from "./SqlTraceTable.js";
8
+ import { TerminalCastPlayer } from "./TerminalCastPlayer.js";
9
+ import { TraceHarView } from "./TraceHarView.js";
10
+ function requireField(kind, rows, field) {
11
+ rows.forEach((row, index) => {
12
+ if (row[field] === void 0 || row[field] === null) {
13
+ throw new Error(`trace kind "${kind}" row ${index} has no "${field}"`);
14
+ }
15
+ });
16
+ }
17
+ function GenericTraceTable({ kind, rows }) {
18
+ const columns = useMemo(() => inferColumns(rows, { literalKeys: true }), [rows]);
19
+ return /* @__PURE__ */ jsx(DataTable, { data: rows, columns, autoFilter: true, emptyMessage: `No ${kind} records` });
20
+ }
21
+ function ProcessTraceView({ rows, samples }) {
22
+ const [selectedKey, setSelectedKey] = useState(null);
23
+ return /* @__PURE__ */ jsx(ProcessTraceTree, { nodes: rows, selectedKey, onSelect: setSelectedKey, ...samples ? { samples } : {} });
24
+ }
25
+ function TraceView({ kind, rows, samples, className }) {
26
+ const body = (() => {
27
+ switch (kind) {
28
+ case "http":
29
+ case "http_proxy":
30
+ requireField(kind, rows, "request");
31
+ requireField(kind, rows, "response");
32
+ return /* @__PURE__ */ jsx(TraceHarView, { rows });
33
+ case "pg_proxy":
34
+ case "sql_statements":
35
+ requireField(kind, rows, "sql");
36
+ return /* @__PURE__ */ jsx(SqlTraceTable, { rows });
37
+ case "terminal":
38
+ requireField(kind, rows, "kind");
39
+ requireField(kind, rows, "offset");
40
+ return /* @__PURE__ */ jsx(TerminalCastPlayer, { events: rows });
41
+ case "process_tree":
42
+ requireField(kind, rows, "key");
43
+ if (samples) requireField("process_samples", samples, "key");
44
+ return /* @__PURE__ */ jsx(ProcessTraceView, { rows, samples });
45
+ default:
46
+ return /* @__PURE__ */ jsx(GenericTraceTable, { kind, rows });
47
+ }
48
+ })();
49
+ return /* @__PURE__ */ jsx("div", { className: cn("flex h-full min-h-0 flex-col", className), children: body });
50
+ }
51
+ export {
52
+ TraceView
53
+ };
54
+ //# sourceMappingURL=TraceView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceView.js","sources":["../../../src/data/traces/TraceView.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { inferColumns } from \"../data-table-utils\";\nimport { DataTable } from \"../DataTable\";\nimport { ProcessTraceTree } from \"./ProcessTraceTree\";\nimport { SqlTraceTable } from \"./SqlTraceTable\";\nimport { TerminalCastPlayer } from \"./TerminalCastPlayer\";\nimport { TraceHarView } from \"./TraceHarView\";\nimport type {\n HttpExchangeRow,\n ProcessNodeRow,\n ProcessSampleRow,\n SqlStatementRow,\n TerminalEvent,\n TraceRow,\n} from \"./types\";\n\nexport type TraceViewProps = {\n /** The record kind: `http`, `http_proxy`, `pg_proxy`, `sql_statements`, `terminal`, `process_tree`, or any other. */\n kind: string;\n /** The kind's records, as its stream stores them. */\n rows: TraceRow[];\n /** `process_samples` records of the same root; used by `process_tree` for CPU sparklines. */\n samples?: TraceRow[];\n /** Classes applied to the viewer root. */\n className?: string;\n};\n\n/** Each row of a kind with a dedicated viewer must carry the field that viewer cannot do without. */\nfunction requireField(kind: string, rows: TraceRow[], field: string): void {\n rows.forEach((row, index) => {\n if (row[field] === undefined || row[field] === null) {\n throw new Error(`trace kind \"${kind}\" row ${index} has no \"${field}\"`);\n }\n });\n}\n\nfunction GenericTraceTable({ kind, rows }: { kind: string; rows: TraceRow[] }) {\n const columns = useMemo(() => inferColumns(rows, { literalKeys: true }), [rows]);\n return <DataTable data={rows} columns={columns} autoFilter emptyMessage={`No ${kind} records`} />;\n}\n\nfunction ProcessTraceView({ rows, samples }: { rows: ProcessNodeRow[]; samples: ProcessSampleRow[] | undefined }) {\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n return <ProcessTraceTree nodes={rows} selectedKey={selectedKey} onSelect={setSelectedKey} {...(samples ? { samples } : {})} />;\n}\n\n/**\n * Renders a trace kind's records with its dedicated viewer: HTTP exchanges in\n * a HAR panel, SQL statements in a table, a terminal session in a cast player,\n * a process tree as a tree. Any other kind gets a table over the row keys.\n */\nexport function TraceView({ kind, rows, samples, className }: TraceViewProps) {\n const body = (() => {\n switch (kind) {\n case \"http\":\n case \"http_proxy\":\n requireField(kind, rows, \"request\");\n requireField(kind, rows, \"response\");\n return <TraceHarView rows={rows as HttpExchangeRow[]} />;\n case \"pg_proxy\":\n case \"sql_statements\":\n requireField(kind, rows, \"sql\");\n return <SqlTraceTable rows={rows as SqlStatementRow[]} />;\n case \"terminal\":\n requireField(kind, rows, \"kind\");\n requireField(kind, rows, \"offset\");\n return <TerminalCastPlayer events={rows as TerminalEvent[]} />;\n case \"process_tree\":\n requireField(kind, rows, \"key\");\n if (samples) requireField(\"process_samples\", samples, \"key\");\n return <ProcessTraceView rows={rows as ProcessNodeRow[]} samples={samples as ProcessSampleRow[] | undefined} />;\n default:\n return <GenericTraceTable kind={kind} rows={rows} />;\n }\n })();\n return <div className={cn(\"flex h-full min-h-0 flex-col\", className)}>{body}</div>;\n}\n"],"names":[],"mappings":";;;;;;;;;AA6BA,SAAS,aAAa,MAAc,MAAkB,OAAqB;AACzE,OAAK,QAAQ,CAAC,KAAK,UAAU;AAC3B,QAAI,IAAI,KAAK,MAAM,UAAa,IAAI,KAAK,MAAM,MAAM;AACnD,YAAM,IAAI,MAAM,eAAe,IAAI,SAAS,KAAK,YAAY,KAAK,GAAG;AAAA,IACvE;AAAA,EACF,CAAC;AACH;AAEA,SAAS,kBAAkB,EAAE,MAAM,QAA4C;AAC7E,QAAM,UAAU,QAAQ,MAAM,aAAa,MAAM,EAAE,aAAa,KAAA,CAAM,GAAG,CAAC,IAAI,CAAC;AAC/E,SAAO,oBAAC,WAAA,EAAU,MAAM,MAAM,SAAkB,YAAU,MAAC,cAAc,MAAM,IAAI,WAAA,CAAY;AACjG;AAEA,SAAS,iBAAiB,EAAE,MAAM,WAAgF;AAChH,QAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,IAAI;AAClE,SAAO,oBAAC,kBAAA,EAAiB,OAAO,MAAM,aAA0B,UAAU,gBAAiB,GAAI,UAAU,EAAE,QAAA,IAAY,CAAA,EAAC,CAAI;AAC9H;AAOO,SAAS,UAAU,EAAE,MAAM,MAAM,SAAS,aAA6B;AAC5E,QAAM,QAAQ,MAAM;AAClB,YAAQ,MAAA;AAAA,MACN,KAAK;AAAA,MACL,KAAK;AACH,qBAAa,MAAM,MAAM,SAAS;AAClC,qBAAa,MAAM,MAAM,UAAU;AACnC,eAAO,oBAAC,gBAAa,MAAiC;AAAA,MACxD,KAAK;AAAA,MACL,KAAK;AACH,qBAAa,MAAM,MAAM,KAAK;AAC9B,eAAO,oBAAC,iBAAc,MAAiC;AAAA,MACzD,KAAK;AACH,qBAAa,MAAM,MAAM,MAAM;AAC/B,qBAAa,MAAM,MAAM,QAAQ;AACjC,eAAO,oBAAC,oBAAA,EAAmB,QAAQ,KAAA,CAAyB;AAAA,MAC9D,KAAK;AACH,qBAAa,MAAM,MAAM,KAAK;AAC9B,YAAI,QAAS,cAAa,mBAAmB,SAAS,KAAK;AAC3D,eAAO,oBAAC,kBAAA,EAAiB,MAAgC,QAAA,CAAoD;AAAA,MAC/G;AACE,eAAO,oBAAC,mBAAA,EAAkB,MAAY,KAAA,CAAY;AAAA,IAAA;AAAA,EAExD,GAAA;AACA,6BAAQ,OAAA,EAAI,WAAW,GAAG,gCAAgC,SAAS,GAAI,UAAA,MAAK;AAC9E;"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const NO_EXIT_CODE = -1;
4
+ const IGNORED_EVENT_TYPES = /* @__PURE__ */ new Set(["i", "m"]);
5
+ function parseJson(text, what) {
6
+ try {
7
+ return JSON.parse(text);
8
+ } catch (cause) {
9
+ throw new Error(`cast ${what} is not valid JSON: ${cause.message}`);
10
+ }
11
+ }
12
+ function parseHeader(line) {
13
+ const header = parseJson(line, "header");
14
+ if (header === null || typeof header !== "object" || Array.isArray(header)) {
15
+ throw new Error("cast header is not a JSON object");
16
+ }
17
+ if (header.version !== 2) throw new Error(`cast header: only asciinema version 2 is supported, got ${String(header.version)}`);
18
+ if (typeof header.width !== "number" || typeof header.height !== "number") {
19
+ throw new Error("cast header has no numeric width and height");
20
+ }
21
+ return { ...header, width: header.width, height: header.height };
22
+ }
23
+ function parseResize(data, lineNumber) {
24
+ const match = typeof data === "string" ? /^(\d+)x(\d+)$/.exec(data) : null;
25
+ if (!match) throw new Error(`cast line ${lineNumber}: resize data must be "<width>x<height>", got ${JSON.stringify(data)}`);
26
+ return { width: Number(match[1]), height: Number(match[2]) };
27
+ }
28
+ function parseCast(text) {
29
+ const lines = text.split("\n").filter((line) => line.trim() !== "");
30
+ const [headerLine, ...eventLines] = lines;
31
+ if (headerLine === void 0) throw new Error("cast text is empty");
32
+ const header = parseHeader(headerLine);
33
+ let size = { width: header.width, height: header.height };
34
+ const events = [];
35
+ let lastOffset = 0;
36
+ eventLines.forEach((line, index) => {
37
+ const lineNumber = index + 2;
38
+ let entry;
39
+ try {
40
+ entry = JSON.parse(line);
41
+ } catch (cause) {
42
+ throw new Error(`cast line ${lineNumber} is not valid JSON: ${cause.message}`);
43
+ }
44
+ if (!Array.isArray(entry) || entry.length < 3 || typeof entry[0] !== "number" || typeof entry[1] !== "string") {
45
+ throw new Error(`cast line ${lineNumber} is not a [seconds, type, data] event`);
46
+ }
47
+ const [offset, type, data] = entry;
48
+ lastOffset = Math.max(lastOffset, offset);
49
+ if (IGNORED_EVENT_TYPES.has(type)) return;
50
+ if (type === "o") {
51
+ events.push({ offset, kind: "o", data: String(data), ...size });
52
+ } else if (type === "r") {
53
+ size = parseResize(data, lineNumber);
54
+ events.push({ offset, kind: "r", data: "", ...size });
55
+ } else {
56
+ throw new Error(`cast line ${lineNumber}: unknown event type "${type}"`);
57
+ }
58
+ });
59
+ const extras = header._gavel;
60
+ if (!extras) return events;
61
+ const duration = header.duration ?? lastOffset;
62
+ const snapshots = extras.snapshots ?? [];
63
+ for (const [milliseconds, screen] of snapshots) {
64
+ events.push({ offset: milliseconds / 1e3, kind: "snapshot", data: screen, ...size });
65
+ }
66
+ if (snapshots.length === 0 && extras.final !== void 0) {
67
+ events.push({ offset: duration, kind: "snapshot", data: extras.final, ...size });
68
+ }
69
+ events.push({
70
+ offset: duration,
71
+ kind: "exit",
72
+ data: "",
73
+ ...size,
74
+ ...extras.exit_code !== void 0 && extras.exit_code !== NO_EXIT_CODE ? { exitCode: extras.exit_code } : {},
75
+ ...extras.truncated ? { truncated: true } : {},
76
+ ...header.command ? { command: header.command } : {}
77
+ });
78
+ return events;
79
+ }
80
+ exports.parseCast = parseCast;
81
+ //# sourceMappingURL=cast.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast.cjs","sources":["../../../src/data/traces/cast.ts"],"sourcesContent":["import type { TerminalEvent } from \"./types\";\n\ntype CastHeader = {\n version?: number;\n width?: number;\n height?: number;\n duration?: number;\n command?: string;\n /** Gavel's extras on top of asciinema v2; players ignore keys they do not know. */\n _gavel?: {\n exit_code?: number;\n snapshots?: [number, string][];\n final?: string;\n truncated?: boolean;\n };\n};\n\nconst NO_EXIT_CODE = -1;\n/** Events asciinema defines that draw nothing on the screen. */\nconst IGNORED_EVENT_TYPES = new Set([\"i\", \"m\"]);\n\nfunction parseJson(text: string, what: string): unknown {\n try {\n return JSON.parse(text);\n } catch (cause) {\n throw new Error(`cast ${what} is not valid JSON: ${(cause as Error).message}`);\n }\n}\n\nfunction parseHeader(line: string): CastHeader & { width: number; height: number } {\n const header = parseJson(line, \"header\") as CastHeader | null;\n if (header === null || typeof header !== \"object\" || Array.isArray(header)) {\n throw new Error(\"cast header is not a JSON object\");\n }\n if (header.version !== 2) throw new Error(`cast header: only asciinema version 2 is supported, got ${String(header.version)}`);\n if (typeof header.width !== \"number\" || typeof header.height !== \"number\") {\n throw new Error(\"cast header has no numeric width and height\");\n }\n return { ...header, width: header.width, height: header.height };\n}\n\nfunction parseResize(data: unknown, lineNumber: number): { width: number; height: number } {\n const match = typeof data === \"string\" ? /^(\\d+)x(\\d+)$/.exec(data) : null;\n if (!match) throw new Error(`cast line ${lineNumber}: resize data must be \"<width>x<height>\", got ${JSON.stringify(data)}`);\n return { width: Number(match[1]), height: Number(match[2]) };\n}\n\n/**\n * Parses an asciinema v2 cast (a header object, then one `[seconds, type, data]`\n * array per line) into the terminal events `TerminalCastPlayer` replays. The\n * `_gavel` header extras a commons-db export carries become the `snapshot` and\n * `exit` events a live capture records.\n */\nexport function parseCast(text: string): TerminalEvent[] {\n const lines = text.split(\"\\n\").filter((line) => line.trim() !== \"\");\n const [headerLine, ...eventLines] = lines;\n if (headerLine === undefined) throw new Error(\"cast text is empty\");\n\n const header = parseHeader(headerLine);\n let size = { width: header.width, height: header.height };\n const events: TerminalEvent[] = [];\n let lastOffset = 0;\n\n eventLines.forEach((line, index) => {\n const lineNumber = index + 2;\n let entry: unknown;\n try {\n entry = JSON.parse(line);\n } catch (cause) {\n throw new Error(`cast line ${lineNumber} is not valid JSON: ${(cause as Error).message}`);\n }\n if (!Array.isArray(entry) || entry.length < 3 || typeof entry[0] !== \"number\" || typeof entry[1] !== \"string\") {\n throw new Error(`cast line ${lineNumber} is not a [seconds, type, data] event`);\n }\n const [offset, type, data] = entry as [number, string, unknown];\n lastOffset = Math.max(lastOffset, offset);\n if (IGNORED_EVENT_TYPES.has(type)) return;\n if (type === \"o\") {\n events.push({ offset, kind: \"o\", data: String(data), ...size });\n } else if (type === \"r\") {\n size = parseResize(data, lineNumber);\n events.push({ offset, kind: \"r\", data: \"\", ...size });\n } else {\n throw new Error(`cast line ${lineNumber}: unknown event type \"${type}\"`);\n }\n });\n\n const extras = header._gavel;\n if (!extras) return events;\n\n const duration = header.duration ?? lastOffset;\n const snapshots = extras.snapshots ?? [];\n for (const [milliseconds, screen] of snapshots) {\n events.push({ offset: milliseconds / 1000, kind: \"snapshot\", data: screen, ...size });\n }\n if (snapshots.length === 0 && extras.final !== undefined) {\n events.push({ offset: duration, kind: \"snapshot\", data: extras.final, ...size });\n }\n events.push({\n offset: duration,\n kind: \"exit\",\n data: \"\",\n ...size,\n ...(extras.exit_code !== undefined && extras.exit_code !== NO_EXIT_CODE ? { exitCode: extras.exit_code } : {}),\n ...(extras.truncated ? { truncated: true } : {}),\n ...(header.command ? { command: header.command } : {}),\n });\n return events;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { TerminalEvent } from './types.js';
2
+ /**
3
+ * Parses an asciinema v2 cast (a header object, then one `[seconds, type, data]`
4
+ * array per line) into the terminal events `TerminalCastPlayer` replays. The
5
+ * `_gavel` header extras a commons-db export carries become the `snapshot` and
6
+ * `exit` events a live capture records.
7
+ */
8
+ export declare function parseCast(text: string): TerminalEvent[];
9
+ //# sourceMappingURL=cast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast.d.ts","sourceRoot":"","sources":["../../../src/data/traces/cast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA+C7C;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAuDvD"}
@@ -0,0 +1,81 @@
1
+ const NO_EXIT_CODE = -1;
2
+ const IGNORED_EVENT_TYPES = /* @__PURE__ */ new Set(["i", "m"]);
3
+ function parseJson(text, what) {
4
+ try {
5
+ return JSON.parse(text);
6
+ } catch (cause) {
7
+ throw new Error(`cast ${what} is not valid JSON: ${cause.message}`);
8
+ }
9
+ }
10
+ function parseHeader(line) {
11
+ const header = parseJson(line, "header");
12
+ if (header === null || typeof header !== "object" || Array.isArray(header)) {
13
+ throw new Error("cast header is not a JSON object");
14
+ }
15
+ if (header.version !== 2) throw new Error(`cast header: only asciinema version 2 is supported, got ${String(header.version)}`);
16
+ if (typeof header.width !== "number" || typeof header.height !== "number") {
17
+ throw new Error("cast header has no numeric width and height");
18
+ }
19
+ return { ...header, width: header.width, height: header.height };
20
+ }
21
+ function parseResize(data, lineNumber) {
22
+ const match = typeof data === "string" ? /^(\d+)x(\d+)$/.exec(data) : null;
23
+ if (!match) throw new Error(`cast line ${lineNumber}: resize data must be "<width>x<height>", got ${JSON.stringify(data)}`);
24
+ return { width: Number(match[1]), height: Number(match[2]) };
25
+ }
26
+ function parseCast(text) {
27
+ const lines = text.split("\n").filter((line) => line.trim() !== "");
28
+ const [headerLine, ...eventLines] = lines;
29
+ if (headerLine === void 0) throw new Error("cast text is empty");
30
+ const header = parseHeader(headerLine);
31
+ let size = { width: header.width, height: header.height };
32
+ const events = [];
33
+ let lastOffset = 0;
34
+ eventLines.forEach((line, index) => {
35
+ const lineNumber = index + 2;
36
+ let entry;
37
+ try {
38
+ entry = JSON.parse(line);
39
+ } catch (cause) {
40
+ throw new Error(`cast line ${lineNumber} is not valid JSON: ${cause.message}`);
41
+ }
42
+ if (!Array.isArray(entry) || entry.length < 3 || typeof entry[0] !== "number" || typeof entry[1] !== "string") {
43
+ throw new Error(`cast line ${lineNumber} is not a [seconds, type, data] event`);
44
+ }
45
+ const [offset, type, data] = entry;
46
+ lastOffset = Math.max(lastOffset, offset);
47
+ if (IGNORED_EVENT_TYPES.has(type)) return;
48
+ if (type === "o") {
49
+ events.push({ offset, kind: "o", data: String(data), ...size });
50
+ } else if (type === "r") {
51
+ size = parseResize(data, lineNumber);
52
+ events.push({ offset, kind: "r", data: "", ...size });
53
+ } else {
54
+ throw new Error(`cast line ${lineNumber}: unknown event type "${type}"`);
55
+ }
56
+ });
57
+ const extras = header._gavel;
58
+ if (!extras) return events;
59
+ const duration = header.duration ?? lastOffset;
60
+ const snapshots = extras.snapshots ?? [];
61
+ for (const [milliseconds, screen] of snapshots) {
62
+ events.push({ offset: milliseconds / 1e3, kind: "snapshot", data: screen, ...size });
63
+ }
64
+ if (snapshots.length === 0 && extras.final !== void 0) {
65
+ events.push({ offset: duration, kind: "snapshot", data: extras.final, ...size });
66
+ }
67
+ events.push({
68
+ offset: duration,
69
+ kind: "exit",
70
+ data: "",
71
+ ...size,
72
+ ...extras.exit_code !== void 0 && extras.exit_code !== NO_EXIT_CODE ? { exitCode: extras.exit_code } : {},
73
+ ...extras.truncated ? { truncated: true } : {},
74
+ ...header.command ? { command: header.command } : {}
75
+ });
76
+ return events;
77
+ }
78
+ export {
79
+ parseCast
80
+ };
81
+ //# sourceMappingURL=cast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast.js","sources":["../../../src/data/traces/cast.ts"],"sourcesContent":["import type { TerminalEvent } from \"./types\";\n\ntype CastHeader = {\n version?: number;\n width?: number;\n height?: number;\n duration?: number;\n command?: string;\n /** Gavel's extras on top of asciinema v2; players ignore keys they do not know. */\n _gavel?: {\n exit_code?: number;\n snapshots?: [number, string][];\n final?: string;\n truncated?: boolean;\n };\n};\n\nconst NO_EXIT_CODE = -1;\n/** Events asciinema defines that draw nothing on the screen. */\nconst IGNORED_EVENT_TYPES = new Set([\"i\", \"m\"]);\n\nfunction parseJson(text: string, what: string): unknown {\n try {\n return JSON.parse(text);\n } catch (cause) {\n throw new Error(`cast ${what} is not valid JSON: ${(cause as Error).message}`);\n }\n}\n\nfunction parseHeader(line: string): CastHeader & { width: number; height: number } {\n const header = parseJson(line, \"header\") as CastHeader | null;\n if (header === null || typeof header !== \"object\" || Array.isArray(header)) {\n throw new Error(\"cast header is not a JSON object\");\n }\n if (header.version !== 2) throw new Error(`cast header: only asciinema version 2 is supported, got ${String(header.version)}`);\n if (typeof header.width !== \"number\" || typeof header.height !== \"number\") {\n throw new Error(\"cast header has no numeric width and height\");\n }\n return { ...header, width: header.width, height: header.height };\n}\n\nfunction parseResize(data: unknown, lineNumber: number): { width: number; height: number } {\n const match = typeof data === \"string\" ? /^(\\d+)x(\\d+)$/.exec(data) : null;\n if (!match) throw new Error(`cast line ${lineNumber}: resize data must be \"<width>x<height>\", got ${JSON.stringify(data)}`);\n return { width: Number(match[1]), height: Number(match[2]) };\n}\n\n/**\n * Parses an asciinema v2 cast (a header object, then one `[seconds, type, data]`\n * array per line) into the terminal events `TerminalCastPlayer` replays. The\n * `_gavel` header extras a commons-db export carries become the `snapshot` and\n * `exit` events a live capture records.\n */\nexport function parseCast(text: string): TerminalEvent[] {\n const lines = text.split(\"\\n\").filter((line) => line.trim() !== \"\");\n const [headerLine, ...eventLines] = lines;\n if (headerLine === undefined) throw new Error(\"cast text is empty\");\n\n const header = parseHeader(headerLine);\n let size = { width: header.width, height: header.height };\n const events: TerminalEvent[] = [];\n let lastOffset = 0;\n\n eventLines.forEach((line, index) => {\n const lineNumber = index + 2;\n let entry: unknown;\n try {\n entry = JSON.parse(line);\n } catch (cause) {\n throw new Error(`cast line ${lineNumber} is not valid JSON: ${(cause as Error).message}`);\n }\n if (!Array.isArray(entry) || entry.length < 3 || typeof entry[0] !== \"number\" || typeof entry[1] !== \"string\") {\n throw new Error(`cast line ${lineNumber} is not a [seconds, type, data] event`);\n }\n const [offset, type, data] = entry as [number, string, unknown];\n lastOffset = Math.max(lastOffset, offset);\n if (IGNORED_EVENT_TYPES.has(type)) return;\n if (type === \"o\") {\n events.push({ offset, kind: \"o\", data: String(data), ...size });\n } else if (type === \"r\") {\n size = parseResize(data, lineNumber);\n events.push({ offset, kind: \"r\", data: \"\", ...size });\n } else {\n throw new Error(`cast line ${lineNumber}: unknown event type \"${type}\"`);\n }\n });\n\n const extras = header._gavel;\n if (!extras) return events;\n\n const duration = header.duration ?? lastOffset;\n const snapshots = extras.snapshots ?? [];\n for (const [milliseconds, screen] of snapshots) {\n events.push({ offset: milliseconds / 1000, kind: \"snapshot\", data: screen, ...size });\n }\n if (snapshots.length === 0 && extras.final !== undefined) {\n events.push({ offset: duration, kind: \"snapshot\", data: extras.final, ...size });\n }\n events.push({\n offset: duration,\n kind: \"exit\",\n data: \"\",\n ...size,\n ...(extras.exit_code !== undefined && extras.exit_code !== NO_EXIT_CODE ? { exitCode: extras.exit_code } : {}),\n ...(extras.truncated ? { truncated: true } : {}),\n ...(header.command ? { command: header.command } : {}),\n });\n return events;\n}\n"],"names":[],"mappings":"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"}