@flanksource/clicky-ui 0.3.48 → 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 (102) hide show
  1. package/dist/data/DataTable.cjs +18 -8
  2. package/dist/data/DataTable.cjs.map +1 -1
  3. package/dist/data/DataTable.d.ts +4 -0
  4. package/dist/data/DataTable.d.ts.map +1 -1
  5. package/dist/data/DataTable.js +18 -8
  6. package/dist/data/DataTable.js.map +1 -1
  7. package/dist/data/TaskManager.cjs +183 -159
  8. package/dist/data/TaskManager.cjs.map +1 -1
  9. package/dist/data/TaskManager.d.ts +3 -1
  10. package/dist/data/TaskManager.d.ts.map +1 -1
  11. package/dist/data/TaskManager.js +185 -161
  12. package/dist/data/TaskManager.js.map +1 -1
  13. package/dist/data/TaskManagerButton.cjs +1 -0
  14. package/dist/data/TaskManagerButton.cjs.map +1 -1
  15. package/dist/data/TaskManagerButton.js +1 -0
  16. package/dist/data/TaskManagerButton.js.map +1 -1
  17. package/dist/data/TaskProgress.cjs +13 -7
  18. package/dist/data/TaskProgress.cjs.map +1 -1
  19. package/dist/data/TaskProgress.d.ts +3 -1
  20. package/dist/data/TaskProgress.d.ts.map +1 -1
  21. package/dist/data/TaskProgress.js +13 -7
  22. package/dist/data/TaskProgress.js.map +1 -1
  23. package/dist/data/traces/ProcessTraceTree.cjs +93 -0
  24. package/dist/data/traces/ProcessTraceTree.cjs.map +1 -0
  25. package/dist/data/traces/ProcessTraceTree.d.ts +24 -0
  26. package/dist/data/traces/ProcessTraceTree.d.ts.map +1 -0
  27. package/dist/data/traces/ProcessTraceTree.js +93 -0
  28. package/dist/data/traces/ProcessTraceTree.js.map +1 -0
  29. package/dist/data/traces/Sparkline.cjs +23 -0
  30. package/dist/data/traces/Sparkline.cjs.map +1 -0
  31. package/dist/data/traces/Sparkline.d.ts +13 -0
  32. package/dist/data/traces/Sparkline.d.ts.map +1 -0
  33. package/dist/data/traces/Sparkline.js +23 -0
  34. package/dist/data/traces/Sparkline.js.map +1 -0
  35. package/dist/data/traces/SqlTraceTable.cjs +104 -0
  36. package/dist/data/traces/SqlTraceTable.cjs.map +1 -0
  37. package/dist/data/traces/SqlTraceTable.d.ts +19 -0
  38. package/dist/data/traces/SqlTraceTable.d.ts.map +1 -0
  39. package/dist/data/traces/SqlTraceTable.js +104 -0
  40. package/dist/data/traces/SqlTraceTable.js.map +1 -0
  41. package/dist/data/traces/TerminalCastPlayer.cjs +162 -0
  42. package/dist/data/traces/TerminalCastPlayer.cjs.map +1 -0
  43. package/dist/data/traces/TerminalCastPlayer.js +162 -0
  44. package/dist/data/traces/TerminalCastPlayer.js.map +1 -0
  45. package/dist/data/traces/TraceHarView.cjs +15 -0
  46. package/dist/data/traces/TraceHarView.cjs.map +1 -0
  47. package/dist/data/traces/TraceHarView.js +15 -0
  48. package/dist/data/traces/TraceHarView.js.map +1 -0
  49. package/dist/data/traces/TraceView.cjs +54 -0
  50. package/dist/data/traces/TraceView.cjs.map +1 -0
  51. package/dist/data/traces/TraceView.d.ts +18 -0
  52. package/dist/data/traces/TraceView.d.ts.map +1 -0
  53. package/dist/data/traces/TraceView.js +54 -0
  54. package/dist/data/traces/TraceView.js.map +1 -0
  55. package/dist/data/traces/cast.cjs +81 -0
  56. package/dist/data/traces/cast.cjs.map +1 -0
  57. package/dist/data/traces/cast.js +81 -0
  58. package/dist/data/traces/cast.js.map +1 -0
  59. package/dist/data/traces/har-adapter.cjs +83 -0
  60. package/dist/data/traces/har-adapter.cjs.map +1 -0
  61. package/dist/data/traces/har-adapter.js +83 -0
  62. package/dist/data/traces/har-adapter.js.map +1 -0
  63. package/dist/data/traces/index.d.ts +12 -0
  64. package/dist/data/traces/index.d.ts.map +1 -0
  65. package/dist/data/traces/process-tree.cjs +53 -0
  66. package/dist/data/traces/process-tree.cjs.map +1 -0
  67. package/dist/data/traces/process-tree.d.ts +21 -0
  68. package/dist/data/traces/process-tree.d.ts.map +1 -0
  69. package/dist/data/traces/process-tree.js +53 -0
  70. package/dist/data/traces/process-tree.js.map +1 -0
  71. package/dist/data/traces/sql-trace.cjs +39 -0
  72. package/dist/data/traces/sql-trace.cjs.map +1 -0
  73. package/dist/data/traces/sql-trace.d.ts +31 -0
  74. package/dist/data/traces/sql-trace.d.ts.map +1 -0
  75. package/dist/data/traces/sql-trace.js +39 -0
  76. package/dist/data/traces/sql-trace.js.map +1 -0
  77. package/dist/data/traces/terminal-screen.cjs +270 -0
  78. package/dist/data/traces/terminal-screen.cjs.map +1 -0
  79. package/dist/data/traces/terminal-screen.js +270 -0
  80. package/dist/data/traces/terminal-screen.js.map +1 -0
  81. package/dist/data/traces/terminal-timeline.cjs +48 -0
  82. package/dist/data/traces/terminal-timeline.cjs.map +1 -0
  83. package/dist/data/traces/terminal-timeline.js +48 -0
  84. package/dist/data/traces/terminal-timeline.js.map +1 -0
  85. package/dist/data/traces/types.d.ts +1 -3
  86. package/dist/data/traces/types.d.ts.map +1 -1
  87. package/dist/data/version-info.cjs +3 -3
  88. package/dist/data/version-info.cjs.map +1 -1
  89. package/dist/data/version-info.js +3 -3
  90. package/dist/data/version-info.js.map +1 -1
  91. package/dist/data.cjs +22 -0
  92. package/dist/data.cjs.map +1 -1
  93. package/dist/data.d.ts +1 -0
  94. package/dist/data.d.ts.map +1 -1
  95. package/dist/data.js +22 -0
  96. package/dist/data.js.map +1 -1
  97. package/dist/index.cjs +22 -0
  98. package/dist/index.cjs.map +1 -1
  99. package/dist/index.js +22 -0
  100. package/dist/index.js.map +1 -1
  101. package/dist/styles.css +1 -1
  102. package/package.json +1 -1
@@ -0,0 +1,93 @@
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 format = require("../../lib/format.cjs");
6
+ const utils$1 = require("../../lib/utils.cjs");
7
+ const utils = require("../diagnostics/utils.cjs");
8
+ const Icon = require("../Icon.cjs");
9
+ const Tree = require("../Tree.cjs");
10
+ const processTree = require("./process-tree.cjs");
11
+ const Sparkline = require("./Sparkline.cjs");
12
+ const UiServerProcess = require("../../icons/components/UiServerProcess.cjs");
13
+ const UiDebug = require("../../icons/components/UiDebug.cjs");
14
+ const OPEN_DEPTH = 2;
15
+ function lifetimeMs(row) {
16
+ const end = row.exitedAt ?? row.lastSeenAt;
17
+ return Date.parse(end) - Date.parse(row.startedAt);
18
+ }
19
+ function ProcessRow({ node, selected, series }) {
20
+ const { row, exited } = node;
21
+ return /* @__PURE__ */ jsxRuntime.jsxs(
22
+ "div",
23
+ {
24
+ "data-process-key": row.key,
25
+ ...exited ? { "data-exited": "true" } : {},
26
+ className: utils$1.cn("flex min-w-0 flex-1 items-center gap-1.5", exited && "opacity-60"),
27
+ title: row.command,
28
+ children: [
29
+ /* @__PURE__ */ jsxRuntime.jsx(
30
+ Icon.Icon,
31
+ {
32
+ icon: row.isRoot ? UiServerProcess.UiServerProcess : UiDebug.UiDebug,
33
+ className: row.isRoot ? "text-base text-blue-600" : "text-base text-muted-foreground"
34
+ }
35
+ ),
36
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils$1.cn("truncate", selected ? "font-semibold text-primary" : "font-medium text-foreground"), children: utils.processLabel({ pid: row.pid, name: row.name, command: row.command }) }),
37
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
38
+ "pid ",
39
+ row.pid
40
+ ] }),
41
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1" }),
42
+ series ? /* @__PURE__ */ jsxRuntime.jsx(Sparkline.Sparkline, { values: series, label: `CPU % over ${series.length} samples` }) : null,
43
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: utils$1.cn("inline-flex shrink-0 items-center gap-1 text-xs", utils.processStateColor(row.status)), children: [
44
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: utils.processStateIcon(row.status) }),
45
+ row.status
46
+ ] }),
47
+ exited ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
48
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Lifetime", children: format.formatDuration(lifetimeMs(row)) }),
49
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Peak CPU", children: `peak ${row.peakCpuPercent.toFixed(1)}%` }),
50
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Peak RSS", children: `peak ${format.formatBytes(row.peakRssBytes)}` })
51
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
52
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: `Peak ${row.peakCpuPercent.toFixed(1)}%`, children: `${row.cpuPercent.toFixed(1)}%` }),
53
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: `Peak ${format.formatBytes(row.peakRssBytes)}`, children: format.formatBytes(row.rssBytes) })
54
+ ] })
55
+ ]
56
+ }
57
+ );
58
+ }
59
+ function ProcessTraceTree({
60
+ nodes,
61
+ samples,
62
+ onSelect,
63
+ selectedKey,
64
+ emptyLabel = "No processes recorded",
65
+ className
66
+ }) {
67
+ const roots = react.useMemo(() => processTree.buildProcessForest(nodes), [nodes]);
68
+ const series = react.useMemo(() => processTree.cpuSeriesByKey(samples ?? []), [samples]);
69
+ const selected = react.useMemo(
70
+ () => selectedKey ? processTree.flattenForest(roots).find((node) => node.row.key === selectedKey) ?? null : null,
71
+ [roots, selectedKey]
72
+ );
73
+ return /* @__PURE__ */ jsxRuntime.jsx(
74
+ Tree.Tree,
75
+ {
76
+ roots,
77
+ ...className ? { className } : {},
78
+ ariaLabel: "Process tree",
79
+ empty: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-density-6 text-center text-muted-foreground text-sm", children: emptyLabel }),
80
+ getChildren: (node) => node.children,
81
+ getKey: (node) => node.row.key,
82
+ getSearchText: (node) => `${node.row.name} ${node.row.command} ${node.row.pid}`,
83
+ selected,
84
+ revealSelected: true,
85
+ defaultOpen: (_node, depth) => depth < OPEN_DEPTH,
86
+ onSelect: (node) => onSelect(node.row.key),
87
+ renderRow: ({ node, selected: isSelected }) => /* @__PURE__ */ jsxRuntime.jsx(ProcessRow, { node, selected: isSelected, series: series.get(node.row.key) }),
88
+ rowClass: (_node, isSelected) => isSelected ? "bg-primary/10 border-l-2 border-primary" : "hover:bg-accent"
89
+ }
90
+ );
91
+ }
92
+ exports.ProcessTraceTree = ProcessTraceTree;
93
+ //# sourceMappingURL=ProcessTraceTree.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessTraceTree.cjs","sources":["../../../src/data/traces/ProcessTraceTree.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { UiDebug, UiServerProcess } from \"../../icons\";\nimport { formatBytes, formatDuration } from \"../../lib/format\";\nimport { cn } from \"../../lib/utils\";\nimport { processLabel, processStateColor, processStateIcon } from \"../diagnostics/utils\";\nimport { Icon } from \"../Icon\";\nimport { Tree } from \"../Tree\";\nimport { buildProcessForest, cpuSeriesByKey, flattenForest, type ProcessTreeNode } from \"./process-tree\";\nimport { Sparkline } from \"./Sparkline\";\nimport type { ProcessNodeRow, ProcessSampleRow } from \"./types\";\n\n/** Roots and their direct children start open; deeper levels open on demand or to reveal the selection. */\nconst OPEN_DEPTH = 2;\n\nexport type ProcessTraceTreeProps = {\n /** `process_tree` trace records, one per process. */\n nodes: ProcessNodeRow[];\n /** `process_samples` records; a process with at least two shows a CPU sparkline. */\n samples?: ProcessSampleRow[];\n /** Called with the `key` of the clicked process. */\n onSelect: (key: string) => void;\n /** `key` of the selected process. */\n selectedKey?: string | null;\n /** Shown when there are no processes. */\n emptyLabel?: string;\n /** Classes applied to the tree root. */\n className?: string;\n};\n\nfunction lifetimeMs(row: ProcessNodeRow): number {\n const end = row.exitedAt ?? row.lastSeenAt;\n return Date.parse(end) - Date.parse(row.startedAt);\n}\n\nfunction ProcessRow({ node, selected, series }: { node: ProcessTreeNode; selected: boolean; series: number[] | undefined }) {\n const { row, exited } = node;\n return (\n <div\n data-process-key={row.key}\n {...(exited ? { \"data-exited\": \"true\" } : {})}\n className={cn(\"flex min-w-0 flex-1 items-center gap-1.5\", exited && \"opacity-60\")}\n title={row.command}\n >\n <Icon\n icon={row.isRoot ? UiServerProcess : UiDebug}\n className={row.isRoot ? \"text-base text-blue-600\" : \"text-base text-muted-foreground\"}\n />\n <span className={cn(\"truncate\", selected ? \"font-semibold text-primary\" : \"font-medium text-foreground\")}>\n {processLabel({ pid: row.pid, name: row.name, command: row.command })}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">pid {row.pid}</span>\n <span className=\"flex-1\" />\n {series ? <Sparkline values={series} label={`CPU % over ${series.length} samples`} /> : null}\n <span className={cn(\"inline-flex shrink-0 items-center gap-1 text-xs\", processStateColor(row.status))}>\n <Icon icon={processStateIcon(row.status)} />\n {row.status}\n </span>\n {exited ? (\n <>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Lifetime\">\n {formatDuration(lifetimeMs(row))}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Peak CPU\">\n {`peak ${row.peakCpuPercent.toFixed(1)}%`}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Peak RSS\">\n {`peak ${formatBytes(row.peakRssBytes)}`}\n </span>\n </>\n ) : (\n <>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title={`Peak ${row.peakCpuPercent.toFixed(1)}%`}>\n {`${row.cpuPercent.toFixed(1)}%`}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title={`Peak ${formatBytes(row.peakRssBytes)}`}>\n {formatBytes(row.rssBytes)}\n </span>\n </>\n )}\n </div>\n );\n}\n\n/**\n * The process tree of a `process_tree` trace: a forest keyed by `key` /\n * `parentKey`, where a process whose parent was never recorded is a root.\n * Exited processes are dimmed and show their lifetime and peak usage. It is\n * built on the same `Tree` primitive and process helpers as `DiagnosticsTree`,\n * which cannot take a forest, key selection or exit information.\n */\nexport function ProcessTraceTree({\n nodes,\n samples,\n onSelect,\n selectedKey,\n emptyLabel = \"No processes recorded\",\n className,\n}: ProcessTraceTreeProps) {\n const roots = useMemo(() => buildProcessForest(nodes), [nodes]);\n const series = useMemo(() => cpuSeriesByKey(samples ?? []), [samples]);\n const selected = useMemo(\n () => (selectedKey ? (flattenForest(roots).find((node) => node.row.key === selectedKey) ?? null) : null),\n [roots, selectedKey],\n );\n\n return (\n <Tree<ProcessTreeNode>\n roots={roots}\n {...(className ? { className } : {})}\n ariaLabel=\"Process tree\"\n empty={<div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>}\n getChildren={(node) => node.children}\n getKey={(node) => node.row.key}\n getSearchText={(node) => `${node.row.name} ${node.row.command} ${node.row.pid}`}\n selected={selected}\n revealSelected\n defaultOpen={(_node, depth) => depth < OPEN_DEPTH}\n onSelect={(node) => onSelect(node.row.key)}\n renderRow={({ node, selected: isSelected }) => (\n <ProcessRow node={node} selected={isSelected} series={series.get(node.row.key)} />\n )}\n rowClass={(_node, isSelected) =>\n isSelected ? \"bg-primary/10 border-l-2 border-primary\" : \"hover:bg-accent\"\n }\n />\n );\n}\n"],"names":["jsxs","cn","jsx","Icon","UiServerProcess","UiDebug","processLabel","Sparkline","processStateColor","processStateIcon","Fragment","formatDuration","formatBytes","useMemo","buildProcessForest","cpuSeriesByKey","flattenForest","Tree"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { ProcessNodeRow, ProcessSampleRow } from './types.js';
2
+ export type ProcessTraceTreeProps = {
3
+ /** `process_tree` trace records, one per process. */
4
+ nodes: ProcessNodeRow[];
5
+ /** `process_samples` records; a process with at least two shows a CPU sparkline. */
6
+ samples?: ProcessSampleRow[];
7
+ /** Called with the `key` of the clicked process. */
8
+ onSelect: (key: string) => void;
9
+ /** `key` of the selected process. */
10
+ selectedKey?: string | null;
11
+ /** Shown when there are no processes. */
12
+ emptyLabel?: string;
13
+ /** Classes applied to the tree root. */
14
+ className?: string;
15
+ };
16
+ /**
17
+ * The process tree of a `process_tree` trace: a forest keyed by `key` /
18
+ * `parentKey`, where a process whose parent was never recorded is a root.
19
+ * Exited processes are dimmed and show their lifetime and peak usage. It is
20
+ * built on the same `Tree` primitive and process helpers as `DiagnosticsTree`,
21
+ * which cannot take a forest, key selection or exit information.
22
+ */
23
+ export declare function ProcessTraceTree({ nodes, samples, onSelect, selectedKey, emptyLabel, className, }: ProcessTraceTreeProps): import("react").JSX.Element;
24
+ //# sourceMappingURL=ProcessTraceTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessTraceTree.d.ts","sourceRoot":"","sources":["../../../src/data/traces/ProcessTraceTree.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAKhE,MAAM,MAAM,qBAAqB,GAAG;IAClC,qDAAqD;IACrD,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,oFAAoF;IACpF,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwDF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,WAAW,EACX,UAAoC,EACpC,SAAS,GACV,EAAE,qBAAqB,+BA6BvB"}
@@ -0,0 +1,93 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { formatDuration, formatBytes } from "../../lib/format.js";
4
+ import { cn } from "../../lib/utils.js";
5
+ import { processLabel, processStateIcon, processStateColor } from "../diagnostics/utils.js";
6
+ import { Icon } from "../Icon.js";
7
+ import { Tree } from "../Tree.js";
8
+ import { buildProcessForest, cpuSeriesByKey, flattenForest } from "./process-tree.js";
9
+ import { Sparkline } from "./Sparkline.js";
10
+ import { UiServerProcess } from "../../icons/components/UiServerProcess.js";
11
+ import { UiDebug } from "../../icons/components/UiDebug.js";
12
+ const OPEN_DEPTH = 2;
13
+ function lifetimeMs(row) {
14
+ const end = row.exitedAt ?? row.lastSeenAt;
15
+ return Date.parse(end) - Date.parse(row.startedAt);
16
+ }
17
+ function ProcessRow({ node, selected, series }) {
18
+ const { row, exited } = node;
19
+ return /* @__PURE__ */ jsxs(
20
+ "div",
21
+ {
22
+ "data-process-key": row.key,
23
+ ...exited ? { "data-exited": "true" } : {},
24
+ className: cn("flex min-w-0 flex-1 items-center gap-1.5", exited && "opacity-60"),
25
+ title: row.command,
26
+ children: [
27
+ /* @__PURE__ */ jsx(
28
+ Icon,
29
+ {
30
+ icon: row.isRoot ? UiServerProcess : UiDebug,
31
+ className: row.isRoot ? "text-base text-blue-600" : "text-base text-muted-foreground"
32
+ }
33
+ ),
34
+ /* @__PURE__ */ jsx("span", { className: cn("truncate", selected ? "font-semibold text-primary" : "font-medium text-foreground"), children: processLabel({ pid: row.pid, name: row.name, command: row.command }) }),
35
+ /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
36
+ "pid ",
37
+ row.pid
38
+ ] }),
39
+ /* @__PURE__ */ jsx("span", { className: "flex-1" }),
40
+ series ? /* @__PURE__ */ jsx(Sparkline, { values: series, label: `CPU % over ${series.length} samples` }) : null,
41
+ /* @__PURE__ */ jsxs("span", { className: cn("inline-flex shrink-0 items-center gap-1 text-xs", processStateColor(row.status)), children: [
42
+ /* @__PURE__ */ jsx(Icon, { icon: processStateIcon(row.status) }),
43
+ row.status
44
+ ] }),
45
+ exited ? /* @__PURE__ */ jsxs(Fragment, { children: [
46
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Lifetime", children: formatDuration(lifetimeMs(row)) }),
47
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Peak CPU", children: `peak ${row.peakCpuPercent.toFixed(1)}%` }),
48
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Peak RSS", children: `peak ${formatBytes(row.peakRssBytes)}` })
49
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
50
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: `Peak ${row.peakCpuPercent.toFixed(1)}%`, children: `${row.cpuPercent.toFixed(1)}%` }),
51
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: `Peak ${formatBytes(row.peakRssBytes)}`, children: formatBytes(row.rssBytes) })
52
+ ] })
53
+ ]
54
+ }
55
+ );
56
+ }
57
+ function ProcessTraceTree({
58
+ nodes,
59
+ samples,
60
+ onSelect,
61
+ selectedKey,
62
+ emptyLabel = "No processes recorded",
63
+ className
64
+ }) {
65
+ const roots = useMemo(() => buildProcessForest(nodes), [nodes]);
66
+ const series = useMemo(() => cpuSeriesByKey(samples ?? []), [samples]);
67
+ const selected = useMemo(
68
+ () => selectedKey ? flattenForest(roots).find((node) => node.row.key === selectedKey) ?? null : null,
69
+ [roots, selectedKey]
70
+ );
71
+ return /* @__PURE__ */ jsx(
72
+ Tree,
73
+ {
74
+ roots,
75
+ ...className ? { className } : {},
76
+ ariaLabel: "Process tree",
77
+ empty: /* @__PURE__ */ jsx("div", { className: "p-density-6 text-center text-muted-foreground text-sm", children: emptyLabel }),
78
+ getChildren: (node) => node.children,
79
+ getKey: (node) => node.row.key,
80
+ getSearchText: (node) => `${node.row.name} ${node.row.command} ${node.row.pid}`,
81
+ selected,
82
+ revealSelected: true,
83
+ defaultOpen: (_node, depth) => depth < OPEN_DEPTH,
84
+ onSelect: (node) => onSelect(node.row.key),
85
+ renderRow: ({ node, selected: isSelected }) => /* @__PURE__ */ jsx(ProcessRow, { node, selected: isSelected, series: series.get(node.row.key) }),
86
+ rowClass: (_node, isSelected) => isSelected ? "bg-primary/10 border-l-2 border-primary" : "hover:bg-accent"
87
+ }
88
+ );
89
+ }
90
+ export {
91
+ ProcessTraceTree
92
+ };
93
+ //# sourceMappingURL=ProcessTraceTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessTraceTree.js","sources":["../../../src/data/traces/ProcessTraceTree.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { UiDebug, UiServerProcess } from \"../../icons\";\nimport { formatBytes, formatDuration } from \"../../lib/format\";\nimport { cn } from \"../../lib/utils\";\nimport { processLabel, processStateColor, processStateIcon } from \"../diagnostics/utils\";\nimport { Icon } from \"../Icon\";\nimport { Tree } from \"../Tree\";\nimport { buildProcessForest, cpuSeriesByKey, flattenForest, type ProcessTreeNode } from \"./process-tree\";\nimport { Sparkline } from \"./Sparkline\";\nimport type { ProcessNodeRow, ProcessSampleRow } from \"./types\";\n\n/** Roots and their direct children start open; deeper levels open on demand or to reveal the selection. */\nconst OPEN_DEPTH = 2;\n\nexport type ProcessTraceTreeProps = {\n /** `process_tree` trace records, one per process. */\n nodes: ProcessNodeRow[];\n /** `process_samples` records; a process with at least two shows a CPU sparkline. */\n samples?: ProcessSampleRow[];\n /** Called with the `key` of the clicked process. */\n onSelect: (key: string) => void;\n /** `key` of the selected process. */\n selectedKey?: string | null;\n /** Shown when there are no processes. */\n emptyLabel?: string;\n /** Classes applied to the tree root. */\n className?: string;\n};\n\nfunction lifetimeMs(row: ProcessNodeRow): number {\n const end = row.exitedAt ?? row.lastSeenAt;\n return Date.parse(end) - Date.parse(row.startedAt);\n}\n\nfunction ProcessRow({ node, selected, series }: { node: ProcessTreeNode; selected: boolean; series: number[] | undefined }) {\n const { row, exited } = node;\n return (\n <div\n data-process-key={row.key}\n {...(exited ? { \"data-exited\": \"true\" } : {})}\n className={cn(\"flex min-w-0 flex-1 items-center gap-1.5\", exited && \"opacity-60\")}\n title={row.command}\n >\n <Icon\n icon={row.isRoot ? UiServerProcess : UiDebug}\n className={row.isRoot ? \"text-base text-blue-600\" : \"text-base text-muted-foreground\"}\n />\n <span className={cn(\"truncate\", selected ? \"font-semibold text-primary\" : \"font-medium text-foreground\")}>\n {processLabel({ pid: row.pid, name: row.name, command: row.command })}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">pid {row.pid}</span>\n <span className=\"flex-1\" />\n {series ? <Sparkline values={series} label={`CPU % over ${series.length} samples`} /> : null}\n <span className={cn(\"inline-flex shrink-0 items-center gap-1 text-xs\", processStateColor(row.status))}>\n <Icon icon={processStateIcon(row.status)} />\n {row.status}\n </span>\n {exited ? (\n <>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Lifetime\">\n {formatDuration(lifetimeMs(row))}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Peak CPU\">\n {`peak ${row.peakCpuPercent.toFixed(1)}%`}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Peak RSS\">\n {`peak ${formatBytes(row.peakRssBytes)}`}\n </span>\n </>\n ) : (\n <>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title={`Peak ${row.peakCpuPercent.toFixed(1)}%`}>\n {`${row.cpuPercent.toFixed(1)}%`}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title={`Peak ${formatBytes(row.peakRssBytes)}`}>\n {formatBytes(row.rssBytes)}\n </span>\n </>\n )}\n </div>\n );\n}\n\n/**\n * The process tree of a `process_tree` trace: a forest keyed by `key` /\n * `parentKey`, where a process whose parent was never recorded is a root.\n * Exited processes are dimmed and show their lifetime and peak usage. It is\n * built on the same `Tree` primitive and process helpers as `DiagnosticsTree`,\n * which cannot take a forest, key selection or exit information.\n */\nexport function ProcessTraceTree({\n nodes,\n samples,\n onSelect,\n selectedKey,\n emptyLabel = \"No processes recorded\",\n className,\n}: ProcessTraceTreeProps) {\n const roots = useMemo(() => buildProcessForest(nodes), [nodes]);\n const series = useMemo(() => cpuSeriesByKey(samples ?? []), [samples]);\n const selected = useMemo(\n () => (selectedKey ? (flattenForest(roots).find((node) => node.row.key === selectedKey) ?? null) : null),\n [roots, selectedKey],\n );\n\n return (\n <Tree<ProcessTreeNode>\n roots={roots}\n {...(className ? { className } : {})}\n ariaLabel=\"Process tree\"\n empty={<div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>}\n getChildren={(node) => node.children}\n getKey={(node) => node.row.key}\n getSearchText={(node) => `${node.row.name} ${node.row.command} ${node.row.pid}`}\n selected={selected}\n revealSelected\n defaultOpen={(_node, depth) => depth < OPEN_DEPTH}\n onSelect={(node) => onSelect(node.row.key)}\n renderRow={({ node, selected: isSelected }) => (\n <ProcessRow node={node} selected={isSelected} series={series.get(node.row.key)} />\n )}\n rowClass={(_node, isSelected) =>\n isSelected ? \"bg-primary/10 border-l-2 border-primary\" : \"hover:bg-accent\"\n }\n />\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const utils = require("../../lib/utils.cjs");
5
+ const processTree = require("./process-tree.cjs");
6
+ function Sparkline({ values, label, width = 64, height = 16, className }) {
7
+ const points = processTree.sparklinePoints(values, width, height);
8
+ if (!points) return null;
9
+ return /* @__PURE__ */ jsxRuntime.jsx(
10
+ "svg",
11
+ {
12
+ role: "img",
13
+ "aria-label": label,
14
+ viewBox: `0 0 ${width} ${height}`,
15
+ width,
16
+ height,
17
+ className: utils.cn("shrink-0 overflow-visible text-primary", className),
18
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points, fill: "none", stroke: "currentColor", strokeWidth: 1.25, strokeLinejoin: "round" })
19
+ }
20
+ );
21
+ }
22
+ exports.Sparkline = Sparkline;
23
+ //# sourceMappingURL=Sparkline.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sparkline.cjs","sources":["../../../src/data/traces/Sparkline.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { sparklinePoints } from \"./process-tree\";\n\nexport type SparklineProps = {\n /** Values in order; at least two are needed to draw a line. */\n values: number[];\n /** Accessible description of what the line plots. */\n label: string;\n width?: number;\n height?: number;\n /** Classes applied to the svg; the stroke follows the text colour. */\n className?: string;\n};\n\n/** A dependency-free inline SVG line, scaled so the largest value touches the top. */\nexport function Sparkline({ values, label, width = 64, height = 16, className }: SparklineProps) {\n const points = sparklinePoints(values, width, height);\n if (!points) return null;\n return (\n <svg\n role=\"img\"\n aria-label={label}\n viewBox={`0 0 ${width} ${height}`}\n width={width}\n height={height}\n className={cn(\"shrink-0 overflow-visible text-primary\", className)}\n >\n <polyline points={points} fill=\"none\" stroke=\"currentColor\" strokeWidth={1.25} strokeLinejoin=\"round\" />\n </svg>\n );\n}\n"],"names":["sparklinePoints","jsx","cn"],"mappings":";;;;;AAeO,SAAS,UAAU,EAAE,QAAQ,OAAO,QAAQ,IAAI,SAAS,IAAI,aAA6B;AAC/F,QAAM,SAASA,YAAAA,gBAAgB,QAAQ,OAAO,MAAM;AACpD,MAAI,CAAC,OAAQ,QAAO;AACpB,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,SAAS,OAAO,KAAK,IAAI,MAAM;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,WAAWC,MAAAA,GAAG,0CAA0C,SAAS;AAAA,MAEjE,UAAAD,2BAAAA,IAAC,YAAA,EAAS,QAAgB,MAAK,QAAO,QAAO,gBAAe,aAAa,MAAM,gBAAe,QAAA,CAAQ;AAAA,IAAA;AAAA,EAAA;AAG5G;;"}
@@ -0,0 +1,13 @@
1
+ export type SparklineProps = {
2
+ /** Values in order; at least two are needed to draw a line. */
3
+ values: number[];
4
+ /** Accessible description of what the line plots. */
5
+ label: string;
6
+ width?: number;
7
+ height?: number;
8
+ /** Classes applied to the svg; the stroke follows the text colour. */
9
+ className?: string;
10
+ };
11
+ /** A dependency-free inline SVG line, scaled so the largest value touches the top. */
12
+ export declare function Sparkline({ values, label, width, height, className }: SparklineProps): import("react").JSX.Element | null;
13
+ //# sourceMappingURL=Sparkline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sparkline.d.ts","sourceRoot":"","sources":["../../../src/data/traces/Sparkline.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG;IAC3B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAU,EAAE,MAAW,EAAE,SAAS,EAAE,EAAE,cAAc,sCAe9F"}
@@ -0,0 +1,23 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/utils.js";
3
+ import { sparklinePoints } from "./process-tree.js";
4
+ function Sparkline({ values, label, width = 64, height = 16, className }) {
5
+ const points = sparklinePoints(values, width, height);
6
+ if (!points) return null;
7
+ return /* @__PURE__ */ jsx(
8
+ "svg",
9
+ {
10
+ role: "img",
11
+ "aria-label": label,
12
+ viewBox: `0 0 ${width} ${height}`,
13
+ width,
14
+ height,
15
+ className: cn("shrink-0 overflow-visible text-primary", className),
16
+ children: /* @__PURE__ */ jsx("polyline", { points, fill: "none", stroke: "currentColor", strokeWidth: 1.25, strokeLinejoin: "round" })
17
+ }
18
+ );
19
+ }
20
+ export {
21
+ Sparkline
22
+ };
23
+ //# sourceMappingURL=Sparkline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sparkline.js","sources":["../../../src/data/traces/Sparkline.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { sparklinePoints } from \"./process-tree\";\n\nexport type SparklineProps = {\n /** Values in order; at least two are needed to draw a line. */\n values: number[];\n /** Accessible description of what the line plots. */\n label: string;\n width?: number;\n height?: number;\n /** Classes applied to the svg; the stroke follows the text colour. */\n className?: string;\n};\n\n/** A dependency-free inline SVG line, scaled so the largest value touches the top. */\nexport function Sparkline({ values, label, width = 64, height = 16, className }: SparklineProps) {\n const points = sparklinePoints(values, width, height);\n if (!points) return null;\n return (\n <svg\n role=\"img\"\n aria-label={label}\n viewBox={`0 0 ${width} ${height}`}\n width={width}\n height={height}\n className={cn(\"shrink-0 overflow-visible text-primary\", className)}\n >\n <polyline points={points} fill=\"none\" stroke=\"currentColor\" strokeWidth={1.25} strokeLinejoin=\"round\" />\n </svg>\n );\n}\n"],"names":[],"mappings":";;;AAeO,SAAS,UAAU,EAAE,QAAQ,OAAO,QAAQ,IAAI,SAAS,IAAI,aAA6B;AAC/F,QAAM,SAAS,gBAAgB,QAAQ,OAAO,MAAM;AACpD,MAAI,CAAC,OAAQ,QAAO;AACpB,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,SAAS,OAAO,KAAK,IAAI,MAAM;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,WAAW,GAAG,0CAA0C,SAAS;AAAA,MAEjE,UAAA,oBAAC,YAAA,EAAS,QAAgB,MAAK,QAAO,QAAO,gBAAe,aAAa,MAAM,gBAAe,QAAA,CAAQ;AAAA,IAAA;AAAA,EAAA;AAG5G;"}
@@ -0,0 +1,104 @@
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 Switch = require("../../components/Switch.cjs");
6
+ const utils = require("../../lib/utils.cjs");
7
+ const Badge = require("../Badge.cjs");
8
+ const CodeBlock = require("../CodeBlock.cjs");
9
+ const DataTable = require("../DataTable.cjs");
10
+ const JsonView = require("../JsonView.cjs");
11
+ const sqlTrace = require("./sql-trace.cjs");
12
+ const COLUMNS = [
13
+ { key: "startedAt", label: "Time", kind: "timestamp", shrink: true },
14
+ {
15
+ key: "op",
16
+ label: "Op",
17
+ shrink: true,
18
+ filterable: true,
19
+ render: (value) => /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { variant: "soft", tone: "neutral", size: "xs", className: "font-mono", children: String(value) })
20
+ },
21
+ { key: "tables", label: "Tables", kind: "tags", shrink: true },
22
+ {
23
+ key: "sql",
24
+ label: "SQL",
25
+ grow: true,
26
+ render: (_value, row) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-density-2", children: [
27
+ /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlock, { inline: true, language: "sql", source: row.sql }),
28
+ row.count > 1 ? /* @__PURE__ */ jsxRuntime.jsx("span", { title: `${row.count} executions`, children: /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { variant: "soft", tone: "info", size: "xs", children: `×${row.count}` }) }) : null
29
+ ] })
30
+ },
31
+ {
32
+ key: "durationMs",
33
+ label: "Duration",
34
+ align: "right",
35
+ shrink: true,
36
+ render: (value, row) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", title: row.count > 1 ? `${row.count} executions in total` : void 0, children: sqlTrace.formatStatementDuration(Number(value)) }),
37
+ sortValue: (value) => Number(value)
38
+ },
39
+ {
40
+ key: "rows",
41
+ label: "Rows",
42
+ align: "right",
43
+ shrink: true,
44
+ render: (value) => Number(value) < 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "—" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: String(value) }),
45
+ sortValue: (value) => Number(value)
46
+ },
47
+ {
48
+ key: "error",
49
+ label: "Error",
50
+ grow: true,
51
+ render: (value, row) => value ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate text-destructive", title: String(value), children: [
52
+ String(value),
53
+ row.count > 1 ? ` (${row.failed} of ${row.count} failed)` : ""
54
+ ] }) : null
55
+ }
56
+ ];
57
+ function StatementDetails({ row }) {
58
+ var _a;
59
+ const [first] = row.occurrences;
60
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-density-3", children: [
61
+ /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlock, { language: "sql", source: row.sql, copyable: true }),
62
+ row.error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
63
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 font-semibold text-foreground", children: "Error" }),
64
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap break-all text-destructive", children: row.error })
65
+ ] }) : null,
66
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground", children: [
67
+ first.connection,
68
+ " · ",
69
+ first.driver,
70
+ ((_a = first.truncated) == null ? void 0 : _a.length) ? ` · truncated: ${first.truncated.join(", ")}` : ""
71
+ ] }),
72
+ row.count > 1 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1 text-xs", "aria-label": "Executions", children: row.occurrences.map((occurrence, index) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: utils.cn("flex items-baseline gap-density-3", occurrence.error && "text-destructive"), children: [
73
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums text-muted-foreground", children: occurrence.startedAt }),
74
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: sqlTrace.formatStatementDuration(occurrence.durationMs) }),
75
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: occurrence.rows < 0 ? "— rows" : `${occurrence.rows} rows` }),
76
+ occurrence.args ? /* @__PURE__ */ jsxRuntime.jsx("code", { className: "font-mono", children: JSON.stringify(occurrence.args) }) : null,
77
+ occurrence.error ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: occurrence.error }) : null
78
+ ] }, index)) }) : first.args ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
79
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 font-semibold text-foreground", children: "Arguments" }),
80
+ /* @__PURE__ */ jsxRuntime.jsx(JsonView.JsonView, { data: first.args })
81
+ ] }) : null
82
+ ] });
83
+ }
84
+ function SqlTraceTable({ rows, groupRepeated = true, emptyLabel = "No SQL statements recorded", className }) {
85
+ const [group, setGroup] = react.useState(groupRepeated);
86
+ const displayRows = react.useMemo(() => sqlTrace.groupStatements(rows, { group }), [rows, group]);
87
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex h-full min-h-0 flex-col gap-density-2", className), children: [
88
+ /* @__PURE__ */ jsxRuntime.jsx(Switch.Switch, { checked: group, onChange: setGroup, label: "Group repeated statements", className: "self-start px-density-2" }),
89
+ /* @__PURE__ */ jsxRuntime.jsx(
90
+ DataTable.DataTable,
91
+ {
92
+ data: displayRows,
93
+ columns: COLUMNS,
94
+ autoFilter: true,
95
+ emptyMessage: emptyLabel,
96
+ globalFilterPlaceholder: "Filter SQL, table, or error…",
97
+ getRowId: (row) => row.id,
98
+ renderExpandedRow: (row) => /* @__PURE__ */ jsxRuntime.jsx(StatementDetails, { row })
99
+ }
100
+ )
101
+ ] });
102
+ }
103
+ exports.SqlTraceTable = SqlTraceTable;
104
+ //# sourceMappingURL=SqlTraceTable.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlTraceTable.cjs","sources":["../../../src/data/traces/SqlTraceTable.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { Switch } from \"../../components/Switch\";\nimport { cn } from \"../../lib/utils\";\nimport { Badge } from \"../Badge\";\nimport { CodeBlock } from \"../CodeBlock\";\nimport { DataTable, type DataTableColumn } from \"../DataTable\";\nimport { JsonView } from \"../JsonView\";\nimport { formatStatementDuration, groupStatements, type SqlDisplayRow } from \"./sql-trace\";\nimport type { SqlStatementRow } from \"./types\";\n\nexport type SqlTraceTableProps = {\n /** `pg_proxy` / `sql_statements` trace records. */\n rows: SqlStatementRow[];\n /** Start with statements sharing a `repeatKey` collapsed into one counted row. Defaults to true. */\n groupRepeated?: boolean;\n /** Shown when there are no statements. */\n emptyLabel?: string;\n /** Classes applied to the table root. */\n className?: string;\n};\n\nconst COLUMNS: DataTableColumn<SqlDisplayRow>[] = [\n { key: \"startedAt\", label: \"Time\", kind: \"timestamp\", shrink: true },\n {\n key: \"op\",\n label: \"Op\",\n shrink: true,\n filterable: true,\n render: (value) => (\n <Badge variant=\"soft\" tone=\"neutral\" size=\"xs\" className=\"font-mono\">\n {String(value)}\n </Badge>\n ),\n },\n { key: \"tables\", label: \"Tables\", kind: \"tags\", shrink: true },\n {\n key: \"sql\",\n label: \"SQL\",\n grow: true,\n render: (_value, row) => (\n <span className=\"flex min-w-0 items-center gap-density-2\">\n <CodeBlock inline language=\"sql\" source={row.sql} />\n {row.count > 1 ? (\n <span title={`${row.count} executions`}>\n <Badge variant=\"soft\" tone=\"info\" size=\"xs\">\n {`×${row.count}`}\n </Badge>\n </span>\n ) : null}\n </span>\n ),\n },\n {\n key: \"durationMs\",\n label: \"Duration\",\n align: \"right\",\n shrink: true,\n render: (value, row) => (\n <span className=\"tabular-nums\" title={row.count > 1 ? `${row.count} executions in total` : undefined}>\n {formatStatementDuration(Number(value))}\n </span>\n ),\n sortValue: (value) => Number(value),\n },\n {\n key: \"rows\",\n label: \"Rows\",\n align: \"right\",\n shrink: true,\n render: (value) => (Number(value) < 0 ? <span className=\"text-muted-foreground\">—</span> : <span className=\"tabular-nums\">{String(value)}</span>),\n sortValue: (value) => Number(value),\n },\n {\n key: \"error\",\n label: \"Error\",\n grow: true,\n render: (value, row) =>\n value ? (\n <span className=\"truncate text-destructive\" title={String(value)}>\n {String(value)}\n {row.count > 1 ? ` (${row.failed} of ${row.count} failed)` : \"\"}\n </span>\n ) : null,\n },\n];\n\nfunction StatementDetails({ row }: { row: SqlDisplayRow }) {\n const [first] = row.occurrences as [SqlStatementRow, ...SqlStatementRow[]];\n return (\n <div className=\"space-y-density-3\">\n <CodeBlock language=\"sql\" source={row.sql} copyable />\n {row.error ? (\n <div>\n <div className=\"mb-1 font-semibold text-foreground\">Error</div>\n <pre className=\"whitespace-pre-wrap break-all text-destructive\">{row.error}</pre>\n </div>\n ) : null}\n <div className=\"text-xs text-muted-foreground\">\n {first.connection} · {first.driver}\n {first.truncated?.length ? ` · truncated: ${first.truncated.join(\", \")}` : \"\"}\n </div>\n {row.count > 1 ? (\n <ul className=\"space-y-1 text-xs\" aria-label=\"Executions\">\n {row.occurrences.map((occurrence, index) => (\n <li key={index} className={cn(\"flex items-baseline gap-density-3\", occurrence.error && \"text-destructive\")}>\n <span className=\"tabular-nums text-muted-foreground\">{occurrence.startedAt}</span>\n <span className=\"tabular-nums\">{formatStatementDuration(occurrence.durationMs)}</span>\n <span className=\"tabular-nums\">{occurrence.rows < 0 ? \"— rows\" : `${occurrence.rows} rows`}</span>\n {occurrence.args ? <code className=\"font-mono\">{JSON.stringify(occurrence.args)}</code> : null}\n {occurrence.error ? <span>{occurrence.error}</span> : null}\n </li>\n ))}\n </ul>\n ) : first.args ? (\n <div>\n <div className=\"mb-1 font-semibold text-foreground\">Arguments</div>\n <JsonView data={first.args} />\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * `pg_proxy` / `sql_statements` records as a DataTable: time, op, tables, SQL,\n * duration, rows and error. Statements that differ only by their values share a\n * `repeatKey` and collapse into one row with a count badge and their total\n * duration; the switch above the table shows each execution instead.\n */\nexport function SqlTraceTable({ rows, groupRepeated = true, emptyLabel = \"No SQL statements recorded\", className }: SqlTraceTableProps) {\n const [group, setGroup] = useState(groupRepeated);\n const displayRows = useMemo(() => groupStatements(rows, { group }), [rows, group]);\n return (\n <div className={cn(\"flex h-full min-h-0 flex-col gap-density-2\", className)}>\n <Switch checked={group} onChange={setGroup} label=\"Group repeated statements\" className=\"self-start px-density-2\" />\n <DataTable\n data={displayRows}\n columns={COLUMNS}\n autoFilter\n emptyMessage={emptyLabel}\n globalFilterPlaceholder=\"Filter SQL, table, or error…\"\n getRowId={(row) => row.id}\n renderExpandedRow={(row) => <StatementDetails row={row} />}\n />\n </div>\n );\n}\n"],"names":["jsx","Badge","jsxs","CodeBlock","formatStatementDuration","cn","JsonView","useState","useMemo","groupStatements","Switch","DataTable"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { SqlStatementRow } from './types.js';
2
+ export type SqlTraceTableProps = {
3
+ /** `pg_proxy` / `sql_statements` trace records. */
4
+ rows: SqlStatementRow[];
5
+ /** Start with statements sharing a `repeatKey` collapsed into one counted row. Defaults to true. */
6
+ groupRepeated?: boolean;
7
+ /** Shown when there are no statements. */
8
+ emptyLabel?: string;
9
+ /** Classes applied to the table root. */
10
+ className?: string;
11
+ };
12
+ /**
13
+ * `pg_proxy` / `sql_statements` records as a DataTable: time, op, tables, SQL,
14
+ * duration, rows and error. Statements that differ only by their values share a
15
+ * `repeatKey` and collapse into one row with a count badge and their total
16
+ * duration; the switch above the table shows each execution instead.
17
+ */
18
+ export declare function SqlTraceTable({ rows, groupRepeated, emptyLabel, className }: SqlTraceTableProps): import("react").JSX.Element;
19
+ //# sourceMappingURL=SqlTraceTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlTraceTable.d.ts","sourceRoot":"","sources":["../../../src/data/traces/SqlTraceTable.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,mDAAmD;IACnD,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,oGAAoG;IACpG,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwGF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,aAAoB,EAAE,UAAyC,EAAE,SAAS,EAAE,EAAE,kBAAkB,+BAiBrI"}
@@ -0,0 +1,104 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useState, useMemo } from "react";
3
+ import { Switch } from "../../components/Switch.js";
4
+ import { cn } from "../../lib/utils.js";
5
+ import { Badge } from "../Badge.js";
6
+ import { CodeBlock } from "../CodeBlock.js";
7
+ import { DataTable } from "../DataTable.js";
8
+ import { JsonView } from "../JsonView.js";
9
+ import { groupStatements, formatStatementDuration } from "./sql-trace.js";
10
+ const COLUMNS = [
11
+ { key: "startedAt", label: "Time", kind: "timestamp", shrink: true },
12
+ {
13
+ key: "op",
14
+ label: "Op",
15
+ shrink: true,
16
+ filterable: true,
17
+ render: (value) => /* @__PURE__ */ jsx(Badge, { variant: "soft", tone: "neutral", size: "xs", className: "font-mono", children: String(value) })
18
+ },
19
+ { key: "tables", label: "Tables", kind: "tags", shrink: true },
20
+ {
21
+ key: "sql",
22
+ label: "SQL",
23
+ grow: true,
24
+ render: (_value, row) => /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-density-2", children: [
25
+ /* @__PURE__ */ jsx(CodeBlock, { inline: true, language: "sql", source: row.sql }),
26
+ row.count > 1 ? /* @__PURE__ */ jsx("span", { title: `${row.count} executions`, children: /* @__PURE__ */ jsx(Badge, { variant: "soft", tone: "info", size: "xs", children: `×${row.count}` }) }) : null
27
+ ] })
28
+ },
29
+ {
30
+ key: "durationMs",
31
+ label: "Duration",
32
+ align: "right",
33
+ shrink: true,
34
+ render: (value, row) => /* @__PURE__ */ jsx("span", { className: "tabular-nums", title: row.count > 1 ? `${row.count} executions in total` : void 0, children: formatStatementDuration(Number(value)) }),
35
+ sortValue: (value) => Number(value)
36
+ },
37
+ {
38
+ key: "rows",
39
+ label: "Rows",
40
+ align: "right",
41
+ shrink: true,
42
+ render: (value) => Number(value) < 0 ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "—" }) : /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: String(value) }),
43
+ sortValue: (value) => Number(value)
44
+ },
45
+ {
46
+ key: "error",
47
+ label: "Error",
48
+ grow: true,
49
+ render: (value, row) => value ? /* @__PURE__ */ jsxs("span", { className: "truncate text-destructive", title: String(value), children: [
50
+ String(value),
51
+ row.count > 1 ? ` (${row.failed} of ${row.count} failed)` : ""
52
+ ] }) : null
53
+ }
54
+ ];
55
+ function StatementDetails({ row }) {
56
+ var _a;
57
+ const [first] = row.occurrences;
58
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-density-3", children: [
59
+ /* @__PURE__ */ jsx(CodeBlock, { language: "sql", source: row.sql, copyable: true }),
60
+ row.error ? /* @__PURE__ */ jsxs("div", { children: [
61
+ /* @__PURE__ */ jsx("div", { className: "mb-1 font-semibold text-foreground", children: "Error" }),
62
+ /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap break-all text-destructive", children: row.error })
63
+ ] }) : null,
64
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
65
+ first.connection,
66
+ " · ",
67
+ first.driver,
68
+ ((_a = first.truncated) == null ? void 0 : _a.length) ? ` · truncated: ${first.truncated.join(", ")}` : ""
69
+ ] }),
70
+ row.count > 1 ? /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-xs", "aria-label": "Executions", children: row.occurrences.map((occurrence, index) => /* @__PURE__ */ jsxs("li", { className: cn("flex items-baseline gap-density-3", occurrence.error && "text-destructive"), children: [
71
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-muted-foreground", children: occurrence.startedAt }),
72
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: formatStatementDuration(occurrence.durationMs) }),
73
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: occurrence.rows < 0 ? "— rows" : `${occurrence.rows} rows` }),
74
+ occurrence.args ? /* @__PURE__ */ jsx("code", { className: "font-mono", children: JSON.stringify(occurrence.args) }) : null,
75
+ occurrence.error ? /* @__PURE__ */ jsx("span", { children: occurrence.error }) : null
76
+ ] }, index)) }) : first.args ? /* @__PURE__ */ jsxs("div", { children: [
77
+ /* @__PURE__ */ jsx("div", { className: "mb-1 font-semibold text-foreground", children: "Arguments" }),
78
+ /* @__PURE__ */ jsx(JsonView, { data: first.args })
79
+ ] }) : null
80
+ ] });
81
+ }
82
+ function SqlTraceTable({ rows, groupRepeated = true, emptyLabel = "No SQL statements recorded", className }) {
83
+ const [group, setGroup] = useState(groupRepeated);
84
+ const displayRows = useMemo(() => groupStatements(rows, { group }), [rows, group]);
85
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 flex-col gap-density-2", className), children: [
86
+ /* @__PURE__ */ jsx(Switch, { checked: group, onChange: setGroup, label: "Group repeated statements", className: "self-start px-density-2" }),
87
+ /* @__PURE__ */ jsx(
88
+ DataTable,
89
+ {
90
+ data: displayRows,
91
+ columns: COLUMNS,
92
+ autoFilter: true,
93
+ emptyMessage: emptyLabel,
94
+ globalFilterPlaceholder: "Filter SQL, table, or error…",
95
+ getRowId: (row) => row.id,
96
+ renderExpandedRow: (row) => /* @__PURE__ */ jsx(StatementDetails, { row })
97
+ }
98
+ )
99
+ ] });
100
+ }
101
+ export {
102
+ SqlTraceTable
103
+ };
104
+ //# sourceMappingURL=SqlTraceTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlTraceTable.js","sources":["../../../src/data/traces/SqlTraceTable.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { Switch } from \"../../components/Switch\";\nimport { cn } from \"../../lib/utils\";\nimport { Badge } from \"../Badge\";\nimport { CodeBlock } from \"../CodeBlock\";\nimport { DataTable, type DataTableColumn } from \"../DataTable\";\nimport { JsonView } from \"../JsonView\";\nimport { formatStatementDuration, groupStatements, type SqlDisplayRow } from \"./sql-trace\";\nimport type { SqlStatementRow } from \"./types\";\n\nexport type SqlTraceTableProps = {\n /** `pg_proxy` / `sql_statements` trace records. */\n rows: SqlStatementRow[];\n /** Start with statements sharing a `repeatKey` collapsed into one counted row. Defaults to true. */\n groupRepeated?: boolean;\n /** Shown when there are no statements. */\n emptyLabel?: string;\n /** Classes applied to the table root. */\n className?: string;\n};\n\nconst COLUMNS: DataTableColumn<SqlDisplayRow>[] = [\n { key: \"startedAt\", label: \"Time\", kind: \"timestamp\", shrink: true },\n {\n key: \"op\",\n label: \"Op\",\n shrink: true,\n filterable: true,\n render: (value) => (\n <Badge variant=\"soft\" tone=\"neutral\" size=\"xs\" className=\"font-mono\">\n {String(value)}\n </Badge>\n ),\n },\n { key: \"tables\", label: \"Tables\", kind: \"tags\", shrink: true },\n {\n key: \"sql\",\n label: \"SQL\",\n grow: true,\n render: (_value, row) => (\n <span className=\"flex min-w-0 items-center gap-density-2\">\n <CodeBlock inline language=\"sql\" source={row.sql} />\n {row.count > 1 ? (\n <span title={`${row.count} executions`}>\n <Badge variant=\"soft\" tone=\"info\" size=\"xs\">\n {`×${row.count}`}\n </Badge>\n </span>\n ) : null}\n </span>\n ),\n },\n {\n key: \"durationMs\",\n label: \"Duration\",\n align: \"right\",\n shrink: true,\n render: (value, row) => (\n <span className=\"tabular-nums\" title={row.count > 1 ? `${row.count} executions in total` : undefined}>\n {formatStatementDuration(Number(value))}\n </span>\n ),\n sortValue: (value) => Number(value),\n },\n {\n key: \"rows\",\n label: \"Rows\",\n align: \"right\",\n shrink: true,\n render: (value) => (Number(value) < 0 ? <span className=\"text-muted-foreground\">—</span> : <span className=\"tabular-nums\">{String(value)}</span>),\n sortValue: (value) => Number(value),\n },\n {\n key: \"error\",\n label: \"Error\",\n grow: true,\n render: (value, row) =>\n value ? (\n <span className=\"truncate text-destructive\" title={String(value)}>\n {String(value)}\n {row.count > 1 ? ` (${row.failed} of ${row.count} failed)` : \"\"}\n </span>\n ) : null,\n },\n];\n\nfunction StatementDetails({ row }: { row: SqlDisplayRow }) {\n const [first] = row.occurrences as [SqlStatementRow, ...SqlStatementRow[]];\n return (\n <div className=\"space-y-density-3\">\n <CodeBlock language=\"sql\" source={row.sql} copyable />\n {row.error ? (\n <div>\n <div className=\"mb-1 font-semibold text-foreground\">Error</div>\n <pre className=\"whitespace-pre-wrap break-all text-destructive\">{row.error}</pre>\n </div>\n ) : null}\n <div className=\"text-xs text-muted-foreground\">\n {first.connection} · {first.driver}\n {first.truncated?.length ? ` · truncated: ${first.truncated.join(\", \")}` : \"\"}\n </div>\n {row.count > 1 ? (\n <ul className=\"space-y-1 text-xs\" aria-label=\"Executions\">\n {row.occurrences.map((occurrence, index) => (\n <li key={index} className={cn(\"flex items-baseline gap-density-3\", occurrence.error && \"text-destructive\")}>\n <span className=\"tabular-nums text-muted-foreground\">{occurrence.startedAt}</span>\n <span className=\"tabular-nums\">{formatStatementDuration(occurrence.durationMs)}</span>\n <span className=\"tabular-nums\">{occurrence.rows < 0 ? \"— rows\" : `${occurrence.rows} rows`}</span>\n {occurrence.args ? <code className=\"font-mono\">{JSON.stringify(occurrence.args)}</code> : null}\n {occurrence.error ? <span>{occurrence.error}</span> : null}\n </li>\n ))}\n </ul>\n ) : first.args ? (\n <div>\n <div className=\"mb-1 font-semibold text-foreground\">Arguments</div>\n <JsonView data={first.args} />\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * `pg_proxy` / `sql_statements` records as a DataTable: time, op, tables, SQL,\n * duration, rows and error. Statements that differ only by their values share a\n * `repeatKey` and collapse into one row with a count badge and their total\n * duration; the switch above the table shows each execution instead.\n */\nexport function SqlTraceTable({ rows, groupRepeated = true, emptyLabel = \"No SQL statements recorded\", className }: SqlTraceTableProps) {\n const [group, setGroup] = useState(groupRepeated);\n const displayRows = useMemo(() => groupStatements(rows, { group }), [rows, group]);\n return (\n <div className={cn(\"flex h-full min-h-0 flex-col gap-density-2\", className)}>\n <Switch checked={group} onChange={setGroup} label=\"Group repeated statements\" className=\"self-start px-density-2\" />\n <DataTable\n data={displayRows}\n columns={COLUMNS}\n autoFilter\n emptyMessage={emptyLabel}\n globalFilterPlaceholder=\"Filter SQL, table, or error…\"\n getRowId={(row) => row.id}\n renderExpandedRow={(row) => <StatementDetails row={row} />}\n />\n </div>\n );\n}\n"],"names":[],"mappings":"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"}