@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.
- package/dist/data/DataTable.cjs +18 -8
- package/dist/data/DataTable.cjs.map +1 -1
- package/dist/data/DataTable.d.ts +4 -0
- package/dist/data/DataTable.d.ts.map +1 -1
- package/dist/data/DataTable.js +18 -8
- package/dist/data/DataTable.js.map +1 -1
- package/dist/data/TaskManager.cjs +183 -159
- package/dist/data/TaskManager.cjs.map +1 -1
- package/dist/data/TaskManager.d.ts +3 -1
- package/dist/data/TaskManager.d.ts.map +1 -1
- package/dist/data/TaskManager.js +185 -161
- package/dist/data/TaskManager.js.map +1 -1
- package/dist/data/TaskManagerButton.cjs +1 -0
- package/dist/data/TaskManagerButton.cjs.map +1 -1
- package/dist/data/TaskManagerButton.js +1 -0
- package/dist/data/TaskManagerButton.js.map +1 -1
- package/dist/data/TaskProgress.cjs +13 -7
- package/dist/data/TaskProgress.cjs.map +1 -1
- package/dist/data/TaskProgress.d.ts +3 -1
- package/dist/data/TaskProgress.d.ts.map +1 -1
- package/dist/data/TaskProgress.js +13 -7
- package/dist/data/TaskProgress.js.map +1 -1
- package/dist/data/traces/ProcessTraceTree.cjs +93 -0
- package/dist/data/traces/ProcessTraceTree.cjs.map +1 -0
- package/dist/data/traces/ProcessTraceTree.d.ts +24 -0
- package/dist/data/traces/ProcessTraceTree.d.ts.map +1 -0
- package/dist/data/traces/ProcessTraceTree.js +93 -0
- package/dist/data/traces/ProcessTraceTree.js.map +1 -0
- package/dist/data/traces/Sparkline.cjs +23 -0
- package/dist/data/traces/Sparkline.cjs.map +1 -0
- package/dist/data/traces/Sparkline.d.ts +13 -0
- package/dist/data/traces/Sparkline.d.ts.map +1 -0
- package/dist/data/traces/Sparkline.js +23 -0
- package/dist/data/traces/Sparkline.js.map +1 -0
- package/dist/data/traces/SqlTraceTable.cjs +104 -0
- package/dist/data/traces/SqlTraceTable.cjs.map +1 -0
- package/dist/data/traces/SqlTraceTable.d.ts +19 -0
- package/dist/data/traces/SqlTraceTable.d.ts.map +1 -0
- package/dist/data/traces/SqlTraceTable.js +104 -0
- package/dist/data/traces/SqlTraceTable.js.map +1 -0
- package/dist/data/traces/TerminalCastPlayer.cjs +162 -0
- package/dist/data/traces/TerminalCastPlayer.cjs.map +1 -0
- package/dist/data/traces/TerminalCastPlayer.js +162 -0
- package/dist/data/traces/TerminalCastPlayer.js.map +1 -0
- package/dist/data/traces/TraceHarView.cjs +15 -0
- package/dist/data/traces/TraceHarView.cjs.map +1 -0
- package/dist/data/traces/TraceHarView.js +15 -0
- package/dist/data/traces/TraceHarView.js.map +1 -0
- package/dist/data/traces/TraceView.cjs +54 -0
- package/dist/data/traces/TraceView.cjs.map +1 -0
- package/dist/data/traces/TraceView.d.ts +18 -0
- package/dist/data/traces/TraceView.d.ts.map +1 -0
- package/dist/data/traces/TraceView.js +54 -0
- package/dist/data/traces/TraceView.js.map +1 -0
- package/dist/data/traces/cast.cjs +81 -0
- package/dist/data/traces/cast.cjs.map +1 -0
- package/dist/data/traces/cast.js +81 -0
- package/dist/data/traces/cast.js.map +1 -0
- package/dist/data/traces/har-adapter.cjs +83 -0
- package/dist/data/traces/har-adapter.cjs.map +1 -0
- package/dist/data/traces/har-adapter.js +83 -0
- package/dist/data/traces/har-adapter.js.map +1 -0
- package/dist/data/traces/index.d.ts +12 -0
- package/dist/data/traces/index.d.ts.map +1 -0
- package/dist/data/traces/process-tree.cjs +53 -0
- package/dist/data/traces/process-tree.cjs.map +1 -0
- package/dist/data/traces/process-tree.d.ts +21 -0
- package/dist/data/traces/process-tree.d.ts.map +1 -0
- package/dist/data/traces/process-tree.js +53 -0
- package/dist/data/traces/process-tree.js.map +1 -0
- package/dist/data/traces/sql-trace.cjs +39 -0
- package/dist/data/traces/sql-trace.cjs.map +1 -0
- package/dist/data/traces/sql-trace.d.ts +31 -0
- package/dist/data/traces/sql-trace.d.ts.map +1 -0
- package/dist/data/traces/sql-trace.js +39 -0
- package/dist/data/traces/sql-trace.js.map +1 -0
- package/dist/data/traces/terminal-screen.cjs +270 -0
- package/dist/data/traces/terminal-screen.cjs.map +1 -0
- package/dist/data/traces/terminal-screen.js +270 -0
- package/dist/data/traces/terminal-screen.js.map +1 -0
- package/dist/data/traces/terminal-timeline.cjs +48 -0
- package/dist/data/traces/terminal-timeline.cjs.map +1 -0
- package/dist/data/traces/terminal-timeline.js +48 -0
- package/dist/data/traces/terminal-timeline.js.map +1 -0
- package/dist/data/traces/types.d.ts +1 -3
- package/dist/data/traces/types.d.ts.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.cjs.map +1 -1
- package/dist/data/version-info.js +3 -3
- package/dist/data/version-info.js.map +1 -1
- package/dist/data.cjs +22 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +1 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +22 -0
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +22 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -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,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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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const format = require("../../lib/format.cjs");
|
|
4
|
+
const REQUEST_BODY_PATH = "request.postData.text";
|
|
5
|
+
const RESPONSE_BODY_PATH = "response.content.text";
|
|
6
|
+
function bodyText(body) {
|
|
7
|
+
if (body === void 0 || body === null) return void 0;
|
|
8
|
+
return typeof body === "string" ? body : JSON.stringify(body, null, 2);
|
|
9
|
+
}
|
|
10
|
+
function mimeTypeOf(body, declared) {
|
|
11
|
+
if (declared) return declared;
|
|
12
|
+
return body !== void 0 && body !== null && typeof body !== "string" ? "application/json" : void 0;
|
|
13
|
+
}
|
|
14
|
+
function headers(list) {
|
|
15
|
+
return list ? { headers: list } : {};
|
|
16
|
+
}
|
|
17
|
+
function requireObject(value, part, row) {
|
|
18
|
+
if (value === void 0 || value === null || typeof value !== "object") {
|
|
19
|
+
throw new Error(`HTTP exchange ${row.method ?? "?"} ${row.url ?? "?"} has no ${part} object`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function harRequest(request, bodySize) {
|
|
24
|
+
var _a, _b, _c;
|
|
25
|
+
const text = bodyText((_a = request.postData) == null ? void 0 : _a.text);
|
|
26
|
+
return {
|
|
27
|
+
method: request.method,
|
|
28
|
+
url: request.url,
|
|
29
|
+
...request.httpVersion ? { httpVersion: request.httpVersion } : {},
|
|
30
|
+
...headers(request.headers),
|
|
31
|
+
...request.queryString ? { queryString: request.queryString } : {},
|
|
32
|
+
...text !== void 0 ? { postData: { mimeType: mimeTypeOf((_b = request.postData) == null ? void 0 : _b.text, (_c = request.postData) == null ? void 0 : _c.mimeType) ?? "", text } } : {},
|
|
33
|
+
...request.headersSize !== void 0 ? { headersSize: request.headersSize } : {},
|
|
34
|
+
...bodySize !== void 0 ? { bodySize } : {}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function harResponse(response, text, mimeType, bodySize, truncated) {
|
|
38
|
+
var _a;
|
|
39
|
+
return {
|
|
40
|
+
status: response.status,
|
|
41
|
+
...response.statusText ? { statusText: response.statusText } : {},
|
|
42
|
+
...response.httpVersion ? { httpVersion: response.httpVersion } : {},
|
|
43
|
+
...headers(response.headers),
|
|
44
|
+
content: {
|
|
45
|
+
size: ((_a = response.content) == null ? void 0 : _a.size) ?? bodySize,
|
|
46
|
+
...mimeType ? { mimeType } : {},
|
|
47
|
+
...text !== void 0 ? { text } : {},
|
|
48
|
+
...truncated ? { truncated } : {}
|
|
49
|
+
},
|
|
50
|
+
...response.redirectURL ? { redirectURL: response.redirectURL } : {},
|
|
51
|
+
...response.headersSize !== void 0 ? { headersSize: response.headersSize } : {},
|
|
52
|
+
bodySize
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function tunnelSummary(row) {
|
|
56
|
+
return `Tunnelled CONNECT relayed without decrypting it: ${format.formatBytes(row.bytesIn ?? 0)} in from the server, ${format.formatBytes(row.bytesOut ?? 0)} out from the client.`;
|
|
57
|
+
}
|
|
58
|
+
function exchangeToHarEntry(row, options = {}) {
|
|
59
|
+
var _a, _b, _c, _d;
|
|
60
|
+
const request = requireObject(row.request, "request", row);
|
|
61
|
+
const response = requireObject(row.response, "response", row);
|
|
62
|
+
const cut = new Set(row.truncated ?? []);
|
|
63
|
+
const tunnelled = row.tunnelled === true;
|
|
64
|
+
const responseBody = tunnelled ? tunnelSummary(row) : bodyText((_a = response.content) == null ? void 0 : _a.text);
|
|
65
|
+
const responseMime = tunnelled ? "text/plain" : mimeTypeOf((_b = response.content) == null ? void 0 : _b.text, (_c = response.content) == null ? void 0 : _c.mimeType);
|
|
66
|
+
const responseSize = tunnelled ? row.bytesIn ?? 0 : response.bodySize ?? ((_d = response.content) == null ? void 0 : _d.size) ?? 0;
|
|
67
|
+
return {
|
|
68
|
+
startedDateTime: row.startedAt,
|
|
69
|
+
time: row.durationMs,
|
|
70
|
+
request: harRequest(request, tunnelled ? row.bytesOut ?? 0 : request.bodySize),
|
|
71
|
+
response: harResponse(response, responseBody, responseMime, responseSize, cut.has(RESPONSE_BODY_PATH)),
|
|
72
|
+
...row.timings ? { timings: row.timings } : {},
|
|
73
|
+
...options.id ? { _id: options.id } : {},
|
|
74
|
+
...row.error ? { _error: row.error } : {},
|
|
75
|
+
...row.serverIPAddress ? { serverIPAddress: row.serverIPAddress } : {},
|
|
76
|
+
...tunnelled ? { _tunnelled: true, _bytesIn: row.bytesIn ?? 0, _bytesOut: row.bytesOut ?? 0 } : {},
|
|
77
|
+
...row.comment ? { _comment: row.comment } : {},
|
|
78
|
+
...row.encoding ? { _encoding: row.encoding } : {},
|
|
79
|
+
...cut.has(REQUEST_BODY_PATH) ? { _requestBodyTruncated: true } : {}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
exports.exchangeToHarEntry = exchangeToHarEntry;
|
|
83
|
+
//# sourceMappingURL=har-adapter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"har-adapter.cjs","sources":["../../../src/data/traces/har-adapter.ts"],"sourcesContent":["import { formatBytes } from \"../../lib/format\";\nimport type { HAREntry, HARHeader, HARRequest, HARResponse } from \"../har/types\";\nimport type { HttpExchangeRequest, HttpExchangeResponse, HttpExchangeRow, TraceBodyText } from \"./types\";\n\nconst REQUEST_BODY_PATH = \"request.postData.text\";\nconst RESPONSE_BODY_PATH = \"response.content.text\";\n\nexport type ExchangeToHarEntryOptions = {\n /** Stable per-request id, so an expanded HarPanel row survives a re-render. */\n id?: string;\n};\n\n/** A body as text: stored text verbatim, a body the store parsed into structure as indented JSON. */\nfunction bodyText(body: TraceBodyText | null | undefined): string | undefined {\n if (body === undefined || body === null) return undefined;\n return typeof body === \"string\" ? body : JSON.stringify(body, null, 2);\n}\n\nfunction mimeTypeOf(body: TraceBodyText | null | undefined, declared: string | undefined): string | undefined {\n if (declared) return declared;\n return body !== undefined && body !== null && typeof body !== \"string\" ? \"application/json\" : undefined;\n}\n\nfunction headers(list: HARHeader[] | null | undefined): { headers?: HARHeader[] } {\n return list ? { headers: list } : {};\n}\n\nfunction requireObject<T>(value: T | undefined | null, part: string, row: HttpExchangeRow): T {\n if (value === undefined || value === null || typeof value !== \"object\") {\n throw new Error(`HTTP exchange ${row.method ?? \"?\"} ${row.url ?? \"?\"} has no ${part} object`);\n }\n return value;\n}\n\nfunction harRequest(request: HttpExchangeRequest, bodySize: number | undefined): HARRequest {\n const text = bodyText(request.postData?.text);\n return {\n method: request.method,\n url: request.url,\n ...(request.httpVersion ? { httpVersion: request.httpVersion } : {}),\n ...headers(request.headers),\n ...(request.queryString ? { queryString: request.queryString } : {}),\n ...(text !== undefined\n ? { postData: { mimeType: mimeTypeOf(request.postData?.text, request.postData?.mimeType) ?? \"\", text } }\n : {}),\n ...(request.headersSize !== undefined ? { headersSize: request.headersSize } : {}),\n ...(bodySize !== undefined ? { bodySize } : {}),\n };\n}\n\nfunction harResponse(\n response: HttpExchangeResponse,\n text: string | undefined,\n mimeType: string | undefined,\n bodySize: number,\n truncated: boolean,\n): HARResponse {\n return {\n status: response.status,\n ...(response.statusText ? { statusText: response.statusText } : {}),\n ...(response.httpVersion ? { httpVersion: response.httpVersion } : {}),\n ...headers(response.headers),\n content: {\n size: response.content?.size ?? bodySize,\n ...(mimeType ? { mimeType } : {}),\n ...(text !== undefined ? { text } : {}),\n ...(truncated ? { truncated } : {}),\n },\n ...(response.redirectURL ? { redirectURL: response.redirectURL } : {}),\n ...(response.headersSize !== undefined ? { headersSize: response.headersSize } : {}),\n bodySize,\n };\n}\n\nfunction tunnelSummary(row: HttpExchangeRow): string {\n return `Tunnelled CONNECT relayed without decrypting it: ${formatBytes(row.bytesIn ?? 0)} in from the server, ${formatBytes(row.bytesOut ?? 0)} out from the client.`;\n}\n\n/**\n * Adapts one `http` / `http_proxy` trace row to the HAR entry `HarPanel`\n * renders. A tunnelled CONNECT has no request or response body to show, so its\n * byte counts stand in for the response body and fill the size columns.\n */\nexport function exchangeToHarEntry(row: HttpExchangeRow, options: ExchangeToHarEntryOptions = {}): HAREntry {\n const request = requireObject(row.request, \"request\", row);\n const response = requireObject(row.response, \"response\", row);\n const cut = new Set(row.truncated ?? []);\n const tunnelled = row.tunnelled === true;\n\n const responseBody = tunnelled ? tunnelSummary(row) : bodyText(response.content?.text);\n const responseMime = tunnelled ? \"text/plain\" : mimeTypeOf(response.content?.text, response.content?.mimeType);\n const responseSize = tunnelled ? (row.bytesIn ?? 0) : (response.bodySize ?? response.content?.size ?? 0);\n\n return {\n startedDateTime: row.startedAt,\n time: row.durationMs,\n request: harRequest(request, tunnelled ? (row.bytesOut ?? 0) : request.bodySize),\n response: harResponse(response, responseBody, responseMime, responseSize, cut.has(RESPONSE_BODY_PATH)),\n ...(row.timings ? { timings: row.timings } : {}),\n ...(options.id ? { _id: options.id } : {}),\n ...(row.error ? { _error: row.error } : {}),\n ...(row.serverIPAddress ? { serverIPAddress: row.serverIPAddress } : {}),\n ...(tunnelled ? { _tunnelled: true, _bytesIn: row.bytesIn ?? 0, _bytesOut: row.bytesOut ?? 0 } : {}),\n ...(row.comment ? { _comment: row.comment } : {}),\n ...(row.encoding ? { _encoding: row.encoding } : {}),\n ...(cut.has(REQUEST_BODY_PATH) ? { _requestBodyTruncated: true } : {}),\n };\n}\n"],"names":["formatBytes"],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { formatBytes } from "../../lib/format.js";
|
|
2
|
+
const REQUEST_BODY_PATH = "request.postData.text";
|
|
3
|
+
const RESPONSE_BODY_PATH = "response.content.text";
|
|
4
|
+
function bodyText(body) {
|
|
5
|
+
if (body === void 0 || body === null) return void 0;
|
|
6
|
+
return typeof body === "string" ? body : JSON.stringify(body, null, 2);
|
|
7
|
+
}
|
|
8
|
+
function mimeTypeOf(body, declared) {
|
|
9
|
+
if (declared) return declared;
|
|
10
|
+
return body !== void 0 && body !== null && typeof body !== "string" ? "application/json" : void 0;
|
|
11
|
+
}
|
|
12
|
+
function headers(list) {
|
|
13
|
+
return list ? { headers: list } : {};
|
|
14
|
+
}
|
|
15
|
+
function requireObject(value, part, row) {
|
|
16
|
+
if (value === void 0 || value === null || typeof value !== "object") {
|
|
17
|
+
throw new Error(`HTTP exchange ${row.method ?? "?"} ${row.url ?? "?"} has no ${part} object`);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
function harRequest(request, bodySize) {
|
|
22
|
+
var _a, _b, _c;
|
|
23
|
+
const text = bodyText((_a = request.postData) == null ? void 0 : _a.text);
|
|
24
|
+
return {
|
|
25
|
+
method: request.method,
|
|
26
|
+
url: request.url,
|
|
27
|
+
...request.httpVersion ? { httpVersion: request.httpVersion } : {},
|
|
28
|
+
...headers(request.headers),
|
|
29
|
+
...request.queryString ? { queryString: request.queryString } : {},
|
|
30
|
+
...text !== void 0 ? { postData: { mimeType: mimeTypeOf((_b = request.postData) == null ? void 0 : _b.text, (_c = request.postData) == null ? void 0 : _c.mimeType) ?? "", text } } : {},
|
|
31
|
+
...request.headersSize !== void 0 ? { headersSize: request.headersSize } : {},
|
|
32
|
+
...bodySize !== void 0 ? { bodySize } : {}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function harResponse(response, text, mimeType, bodySize, truncated) {
|
|
36
|
+
var _a;
|
|
37
|
+
return {
|
|
38
|
+
status: response.status,
|
|
39
|
+
...response.statusText ? { statusText: response.statusText } : {},
|
|
40
|
+
...response.httpVersion ? { httpVersion: response.httpVersion } : {},
|
|
41
|
+
...headers(response.headers),
|
|
42
|
+
content: {
|
|
43
|
+
size: ((_a = response.content) == null ? void 0 : _a.size) ?? bodySize,
|
|
44
|
+
...mimeType ? { mimeType } : {},
|
|
45
|
+
...text !== void 0 ? { text } : {},
|
|
46
|
+
...truncated ? { truncated } : {}
|
|
47
|
+
},
|
|
48
|
+
...response.redirectURL ? { redirectURL: response.redirectURL } : {},
|
|
49
|
+
...response.headersSize !== void 0 ? { headersSize: response.headersSize } : {},
|
|
50
|
+
bodySize
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function tunnelSummary(row) {
|
|
54
|
+
return `Tunnelled CONNECT relayed without decrypting it: ${formatBytes(row.bytesIn ?? 0)} in from the server, ${formatBytes(row.bytesOut ?? 0)} out from the client.`;
|
|
55
|
+
}
|
|
56
|
+
function exchangeToHarEntry(row, options = {}) {
|
|
57
|
+
var _a, _b, _c, _d;
|
|
58
|
+
const request = requireObject(row.request, "request", row);
|
|
59
|
+
const response = requireObject(row.response, "response", row);
|
|
60
|
+
const cut = new Set(row.truncated ?? []);
|
|
61
|
+
const tunnelled = row.tunnelled === true;
|
|
62
|
+
const responseBody = tunnelled ? tunnelSummary(row) : bodyText((_a = response.content) == null ? void 0 : _a.text);
|
|
63
|
+
const responseMime = tunnelled ? "text/plain" : mimeTypeOf((_b = response.content) == null ? void 0 : _b.text, (_c = response.content) == null ? void 0 : _c.mimeType);
|
|
64
|
+
const responseSize = tunnelled ? row.bytesIn ?? 0 : response.bodySize ?? ((_d = response.content) == null ? void 0 : _d.size) ?? 0;
|
|
65
|
+
return {
|
|
66
|
+
startedDateTime: row.startedAt,
|
|
67
|
+
time: row.durationMs,
|
|
68
|
+
request: harRequest(request, tunnelled ? row.bytesOut ?? 0 : request.bodySize),
|
|
69
|
+
response: harResponse(response, responseBody, responseMime, responseSize, cut.has(RESPONSE_BODY_PATH)),
|
|
70
|
+
...row.timings ? { timings: row.timings } : {},
|
|
71
|
+
...options.id ? { _id: options.id } : {},
|
|
72
|
+
...row.error ? { _error: row.error } : {},
|
|
73
|
+
...row.serverIPAddress ? { serverIPAddress: row.serverIPAddress } : {},
|
|
74
|
+
...tunnelled ? { _tunnelled: true, _bytesIn: row.bytesIn ?? 0, _bytesOut: row.bytesOut ?? 0 } : {},
|
|
75
|
+
...row.comment ? { _comment: row.comment } : {},
|
|
76
|
+
...row.encoding ? { _encoding: row.encoding } : {},
|
|
77
|
+
...cut.has(REQUEST_BODY_PATH) ? { _requestBodyTruncated: true } : {}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
exchangeToHarEntry
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=har-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"har-adapter.js","sources":["../../../src/data/traces/har-adapter.ts"],"sourcesContent":["import { formatBytes } from \"../../lib/format\";\nimport type { HAREntry, HARHeader, HARRequest, HARResponse } from \"../har/types\";\nimport type { HttpExchangeRequest, HttpExchangeResponse, HttpExchangeRow, TraceBodyText } from \"./types\";\n\nconst REQUEST_BODY_PATH = \"request.postData.text\";\nconst RESPONSE_BODY_PATH = \"response.content.text\";\n\nexport type ExchangeToHarEntryOptions = {\n /** Stable per-request id, so an expanded HarPanel row survives a re-render. */\n id?: string;\n};\n\n/** A body as text: stored text verbatim, a body the store parsed into structure as indented JSON. */\nfunction bodyText(body: TraceBodyText | null | undefined): string | undefined {\n if (body === undefined || body === null) return undefined;\n return typeof body === \"string\" ? body : JSON.stringify(body, null, 2);\n}\n\nfunction mimeTypeOf(body: TraceBodyText | null | undefined, declared: string | undefined): string | undefined {\n if (declared) return declared;\n return body !== undefined && body !== null && typeof body !== \"string\" ? \"application/json\" : undefined;\n}\n\nfunction headers(list: HARHeader[] | null | undefined): { headers?: HARHeader[] } {\n return list ? { headers: list } : {};\n}\n\nfunction requireObject<T>(value: T | undefined | null, part: string, row: HttpExchangeRow): T {\n if (value === undefined || value === null || typeof value !== \"object\") {\n throw new Error(`HTTP exchange ${row.method ?? \"?\"} ${row.url ?? \"?\"} has no ${part} object`);\n }\n return value;\n}\n\nfunction harRequest(request: HttpExchangeRequest, bodySize: number | undefined): HARRequest {\n const text = bodyText(request.postData?.text);\n return {\n method: request.method,\n url: request.url,\n ...(request.httpVersion ? { httpVersion: request.httpVersion } : {}),\n ...headers(request.headers),\n ...(request.queryString ? { queryString: request.queryString } : {}),\n ...(text !== undefined\n ? { postData: { mimeType: mimeTypeOf(request.postData?.text, request.postData?.mimeType) ?? \"\", text } }\n : {}),\n ...(request.headersSize !== undefined ? { headersSize: request.headersSize } : {}),\n ...(bodySize !== undefined ? { bodySize } : {}),\n };\n}\n\nfunction harResponse(\n response: HttpExchangeResponse,\n text: string | undefined,\n mimeType: string | undefined,\n bodySize: number,\n truncated: boolean,\n): HARResponse {\n return {\n status: response.status,\n ...(response.statusText ? { statusText: response.statusText } : {}),\n ...(response.httpVersion ? { httpVersion: response.httpVersion } : {}),\n ...headers(response.headers),\n content: {\n size: response.content?.size ?? bodySize,\n ...(mimeType ? { mimeType } : {}),\n ...(text !== undefined ? { text } : {}),\n ...(truncated ? { truncated } : {}),\n },\n ...(response.redirectURL ? { redirectURL: response.redirectURL } : {}),\n ...(response.headersSize !== undefined ? { headersSize: response.headersSize } : {}),\n bodySize,\n };\n}\n\nfunction tunnelSummary(row: HttpExchangeRow): string {\n return `Tunnelled CONNECT relayed without decrypting it: ${formatBytes(row.bytesIn ?? 0)} in from the server, ${formatBytes(row.bytesOut ?? 0)} out from the client.`;\n}\n\n/**\n * Adapts one `http` / `http_proxy` trace row to the HAR entry `HarPanel`\n * renders. A tunnelled CONNECT has no request or response body to show, so its\n * byte counts stand in for the response body and fill the size columns.\n */\nexport function exchangeToHarEntry(row: HttpExchangeRow, options: ExchangeToHarEntryOptions = {}): HAREntry {\n const request = requireObject(row.request, \"request\", row);\n const response = requireObject(row.response, \"response\", row);\n const cut = new Set(row.truncated ?? []);\n const tunnelled = row.tunnelled === true;\n\n const responseBody = tunnelled ? tunnelSummary(row) : bodyText(response.content?.text);\n const responseMime = tunnelled ? \"text/plain\" : mimeTypeOf(response.content?.text, response.content?.mimeType);\n const responseSize = tunnelled ? (row.bytesIn ?? 0) : (response.bodySize ?? response.content?.size ?? 0);\n\n return {\n startedDateTime: row.startedAt,\n time: row.durationMs,\n request: harRequest(request, tunnelled ? (row.bytesOut ?? 0) : request.bodySize),\n response: harResponse(response, responseBody, responseMime, responseSize, cut.has(RESPONSE_BODY_PATH)),\n ...(row.timings ? { timings: row.timings } : {}),\n ...(options.id ? { _id: options.id } : {}),\n ...(row.error ? { _error: row.error } : {}),\n ...(row.serverIPAddress ? { serverIPAddress: row.serverIPAddress } : {}),\n ...(tunnelled ? { _tunnelled: true, _bytesIn: row.bytesIn ?? 0, _bytesOut: row.bytesOut ?? 0 } : {}),\n ...(row.comment ? { _comment: row.comment } : {}),\n ...(row.encoding ? { _encoding: row.encoding } : {}),\n ...(cut.has(REQUEST_BODY_PATH) ? { _requestBodyTruncated: true } : {}),\n };\n}\n"],"names":[],"mappings":";AAIA,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAQ3B,SAAS,SAAS,MAA4D;AAC5E,MAAI,SAAS,UAAa,SAAS,KAAM,QAAO;AAChD,SAAO,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AACvE;AAEA,SAAS,WAAW,MAAwC,UAAkD;AAC5G,MAAI,SAAU,QAAO;AACrB,SAAO,SAAS,UAAa,SAAS,QAAQ,OAAO,SAAS,WAAW,qBAAqB;AAChG;AAEA,SAAS,QAAQ,MAAiE;AAChF,SAAO,OAAO,EAAE,SAAS,KAAA,IAAS,CAAA;AACpC;AAEA,SAAS,cAAiB,OAA6B,MAAc,KAAyB;AAC5F,MAAI,UAAU,UAAa,UAAU,QAAQ,OAAO,UAAU,UAAU;AACtE,UAAM,IAAI,MAAM,iBAAiB,IAAI,UAAU,GAAG,IAAI,IAAI,OAAO,GAAG,WAAW,IAAI,SAAS;AAAA,EAC9F;AACA,SAAO;AACT;AAEA,SAAS,WAAW,SAA8B,UAA0C;;AAC1F,QAAM,OAAO,UAAS,aAAQ,aAAR,mBAAkB,IAAI;AAC5C,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,KAAK,QAAQ;AAAA,IACb,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAA,IAAgB,CAAA;AAAA,IACjE,GAAG,QAAQ,QAAQ,OAAO;AAAA,IAC1B,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAA,IAAgB,CAAA;AAAA,IACjE,GAAI,SAAS,SACT,EAAE,UAAU,EAAE,UAAU,YAAW,aAAQ,aAAR,mBAAkB,OAAM,aAAQ,aAAR,mBAAkB,QAAQ,KAAK,IAAI,KAAA,EAAK,IACnG,CAAA;AAAA,IACJ,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,QAAQ,YAAA,IAAgB,CAAA;AAAA,IAC/E,GAAI,aAAa,SAAY,EAAE,aAAa,CAAA;AAAA,EAAC;AAEjD;AAEA,SAAS,YACP,UACA,MACA,UACA,UACA,WACa;;AACb,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB,GAAI,SAAS,aAAa,EAAE,YAAY,SAAS,WAAA,IAAe,CAAA;AAAA,IAChE,GAAI,SAAS,cAAc,EAAE,aAAa,SAAS,YAAA,IAAgB,CAAA;AAAA,IACnE,GAAG,QAAQ,SAAS,OAAO;AAAA,IAC3B,SAAS;AAAA,MACP,QAAM,cAAS,YAAT,mBAAkB,SAAQ;AAAA,MAChC,GAAI,WAAW,EAAE,SAAA,IAAa,CAAA;AAAA,MAC9B,GAAI,SAAS,SAAY,EAAE,KAAA,IAAS,CAAA;AAAA,MACpC,GAAI,YAAY,EAAE,cAAc,CAAA;AAAA,IAAC;AAAA,IAEnC,GAAI,SAAS,cAAc,EAAE,aAAa,SAAS,YAAA,IAAgB,CAAA;AAAA,IACnE,GAAI,SAAS,gBAAgB,SAAY,EAAE,aAAa,SAAS,YAAA,IAAgB,CAAA;AAAA,IACjF;AAAA,EAAA;AAEJ;AAEA,SAAS,cAAc,KAA8B;AACnD,SAAO,oDAAoD,YAAY,IAAI,WAAW,CAAC,CAAC,wBAAwB,YAAY,IAAI,YAAY,CAAC,CAAC;AAChJ;AAOO,SAAS,mBAAmB,KAAsB,UAAqC,IAAc;;AAC1G,QAAM,UAAU,cAAc,IAAI,SAAS,WAAW,GAAG;AACzD,QAAM,WAAW,cAAc,IAAI,UAAU,YAAY,GAAG;AAC5D,QAAM,MAAM,IAAI,IAAI,IAAI,aAAa,CAAA,CAAE;AACvC,QAAM,YAAY,IAAI,cAAc;AAEpC,QAAM,eAAe,YAAY,cAAc,GAAG,IAAI,UAAS,cAAS,YAAT,mBAAkB,IAAI;AACrF,QAAM,eAAe,YAAY,eAAe,YAAW,cAAS,YAAT,mBAAkB,OAAM,cAAS,YAAT,mBAAkB,QAAQ;AAC7G,QAAM,eAAe,YAAa,IAAI,WAAW,IAAM,SAAS,cAAY,cAAS,YAAT,mBAAkB,SAAQ;AAEtG,SAAO;AAAA,IACL,iBAAiB,IAAI;AAAA,IACrB,MAAM,IAAI;AAAA,IACV,SAAS,WAAW,SAAS,YAAa,IAAI,YAAY,IAAK,QAAQ,QAAQ;AAAA,IAC/E,UAAU,YAAY,UAAU,cAAc,cAAc,cAAc,IAAI,IAAI,kBAAkB,CAAC;AAAA,IACrG,GAAI,IAAI,UAAU,EAAE,SAAS,IAAI,QAAA,IAAY,CAAA;AAAA,IAC7C,GAAI,QAAQ,KAAK,EAAE,KAAK,QAAQ,GAAA,IAAO,CAAA;AAAA,IACvC,GAAI,IAAI,QAAQ,EAAE,QAAQ,IAAI,MAAA,IAAU,CAAA;AAAA,IACxC,GAAI,IAAI,kBAAkB,EAAE,iBAAiB,IAAI,gBAAA,IAAoB,CAAA;AAAA,IACrE,GAAI,YAAY,EAAE,YAAY,MAAM,UAAU,IAAI,WAAW,GAAG,WAAW,IAAI,YAAY,EAAA,IAAM,CAAA;AAAA,IACjG,GAAI,IAAI,UAAU,EAAE,UAAU,IAAI,QAAA,IAAY,CAAA;AAAA,IAC9C,GAAI,IAAI,WAAW,EAAE,WAAW,IAAI,SAAA,IAAa,CAAA;AAAA,IACjD,GAAI,IAAI,IAAI,iBAAiB,IAAI,EAAE,uBAAuB,SAAS,CAAA;AAAA,EAAC;AAExE;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { TraceView, type TraceViewProps } from './TraceView.js';
|
|
2
|
+
export { TraceHarView, type TraceHarViewProps } from './TraceHarView.js';
|
|
3
|
+
export { exchangeToHarEntry, type ExchangeToHarEntryOptions } from './har-adapter.js';
|
|
4
|
+
export { SqlTraceTable, type SqlTraceTableProps } from './SqlTraceTable.js';
|
|
5
|
+
export { groupStatements, formatStatementDuration, type SqlDisplayRow } from './sql-trace.js';
|
|
6
|
+
export { TerminalCastPlayer, type TerminalCastPlayerProps } from './TerminalCastPlayer.js';
|
|
7
|
+
export { parseCast } from './cast.js';
|
|
8
|
+
export { ProcessTraceTree, type ProcessTraceTreeProps } from './ProcessTraceTree.js';
|
|
9
|
+
export { buildProcessForest, flattenForest, type ProcessTreeNode } from './process-tree.js';
|
|
10
|
+
export { Sparkline, type SparklineProps } from './Sparkline.js';
|
|
11
|
+
export type { HttpExchangeRequest, HttpExchangeResponse, HttpExchangeRow, ProcessNodeRow, ProcessSampleRow, SqlStatementRow, TerminalEvent, TerminalEventKind, TraceBodyText, TraceRow, } from './types.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/data/traces/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,QAAQ,GACT,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const byStart = (a, b) => Date.parse(a.row.startedAt) - Date.parse(b.row.startedAt) || a.row.pid - b.row.pid;
|
|
4
|
+
function buildProcessForest(rows) {
|
|
5
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
6
|
+
for (const row of rows) {
|
|
7
|
+
if (nodes.has(row.key)) throw new Error(`process tree key "${row.key}" appears twice`);
|
|
8
|
+
nodes.set(row.key, { row, children: [], exited: row.status === "exited" || Boolean(row.exitedAt) });
|
|
9
|
+
}
|
|
10
|
+
const roots = [];
|
|
11
|
+
for (const node of nodes.values()) {
|
|
12
|
+
const parent = node.row.parentKey ? nodes.get(node.row.parentKey) : void 0;
|
|
13
|
+
(parent ? parent.children : roots).push(node);
|
|
14
|
+
}
|
|
15
|
+
roots.sort((a, b) => Number(Boolean(b.row.isRoot)) - Number(Boolean(a.row.isRoot)) || byStart(a, b));
|
|
16
|
+
const reached = flattenForest(roots);
|
|
17
|
+
if (reached.length !== nodes.size) {
|
|
18
|
+
const lost = [...nodes.keys()].filter((key) => !reached.some((node) => node.row.key === key));
|
|
19
|
+
throw new Error(`process tree has a parent cycle among keys ${lost.map((key) => `"${key}"`).join(", ")}`);
|
|
20
|
+
}
|
|
21
|
+
for (const node of nodes.values()) node.children.sort(byStart);
|
|
22
|
+
return roots;
|
|
23
|
+
}
|
|
24
|
+
function flattenForest(roots) {
|
|
25
|
+
const flat = [];
|
|
26
|
+
const visit = (node) => {
|
|
27
|
+
flat.push(node);
|
|
28
|
+
node.children.forEach(visit);
|
|
29
|
+
};
|
|
30
|
+
roots.forEach(visit);
|
|
31
|
+
return flat;
|
|
32
|
+
}
|
|
33
|
+
function cpuSeriesByKey(samples) {
|
|
34
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const sample of samples) byKey.set(sample.key, [...byKey.get(sample.key) ?? [], sample]);
|
|
36
|
+
return new Map(
|
|
37
|
+
[...byKey].map(([key, list]) => [key, list.sort((a, b) => a.tick - b.tick).map((sample) => sample.cpuPercent)])
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
function sparklinePoints(values, width, height) {
|
|
41
|
+
if (values.length < 2) return void 0;
|
|
42
|
+
const max = Math.max(...values);
|
|
43
|
+
return values.map((value, index) => {
|
|
44
|
+
const x = index / (values.length - 1) * width;
|
|
45
|
+
const y = max === 0 ? height : height - value / max * height;
|
|
46
|
+
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
|
47
|
+
}).join(" ");
|
|
48
|
+
}
|
|
49
|
+
exports.buildProcessForest = buildProcessForest;
|
|
50
|
+
exports.cpuSeriesByKey = cpuSeriesByKey;
|
|
51
|
+
exports.flattenForest = flattenForest;
|
|
52
|
+
exports.sparklinePoints = sparklinePoints;
|
|
53
|
+
//# sourceMappingURL=process-tree.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-tree.cjs","sources":["../../../src/data/traces/process-tree.ts"],"sourcesContent":["import type { ProcessNodeRow, ProcessSampleRow } from \"./types\";\n\nexport type ProcessTreeNode = {\n row: ProcessNodeRow;\n children: ProcessTreeNode[];\n /** The process has exited: by its status, or because an exit time was recorded. */\n exited: boolean;\n};\n\nconst byStart = (a: ProcessTreeNode, b: ProcessTreeNode) =>\n Date.parse(a.row.startedAt) - Date.parse(b.row.startedAt) || a.row.pid - b.row.pid;\n\n/**\n * Builds the forest a `process_tree` stream describes: each row under its\n * `parentKey`. A row whose parent was never recorded (the capture began below\n * it) becomes a root. The stream holds each process once, so a repeated key or a\n * parent cycle is a corrupt capture and throws.\n */\nexport function buildProcessForest(rows: ProcessNodeRow[]): ProcessTreeNode[] {\n const nodes = new Map<string, ProcessTreeNode>();\n for (const row of rows) {\n if (nodes.has(row.key)) throw new Error(`process tree key \"${row.key}\" appears twice`);\n nodes.set(row.key, { row, children: [], exited: row.status === \"exited\" || Boolean(row.exitedAt) });\n }\n\n const roots: ProcessTreeNode[] = [];\n for (const node of nodes.values()) {\n const parent = node.row.parentKey ? nodes.get(node.row.parentKey) : undefined;\n (parent ? parent.children : roots).push(node);\n }\n\n roots.sort((a, b) => Number(Boolean(b.row.isRoot)) - Number(Boolean(a.row.isRoot)) || byStart(a, b));\n const reached = flattenForest(roots);\n if (reached.length !== nodes.size) {\n const lost = [...nodes.keys()].filter((key) => !reached.some((node) => node.row.key === key));\n throw new Error(`process tree has a parent cycle among keys ${lost.map((key) => `\"${key}\"`).join(\", \")}`);\n }\n for (const node of nodes.values()) node.children.sort(byStart);\n return roots;\n}\n\n/** The forest depth-first, parents before children. */\nexport function flattenForest(roots: ProcessTreeNode[]): ProcessTreeNode[] {\n const flat: ProcessTreeNode[] = [];\n const visit = (node: ProcessTreeNode) => {\n flat.push(node);\n node.children.forEach(visit);\n };\n roots.forEach(visit);\n return flat;\n}\n\n/** Each process's CPU % over its samples, in tick order. */\nexport function cpuSeriesByKey(samples: ProcessSampleRow[]): Map<string, number[]> {\n const byKey = new Map<string, ProcessSampleRow[]>();\n for (const sample of samples) byKey.set(sample.key, [...(byKey.get(sample.key) ?? []), sample]);\n return new Map(\n [...byKey].map(([key, list]) => [key, list.sort((a, b) => a.tick - b.tick).map((sample) => sample.cpuPercent)]),\n );\n}\n\n/** SVG polyline points for `values` in a `width` x `height` box, zero at the bottom and the maximum at the top. */\nexport function sparklinePoints(values: number[], width: number, height: number): string | undefined {\n if (values.length < 2) return undefined;\n const max = Math.max(...values);\n return values\n .map((value, index) => {\n const x = (index / (values.length - 1)) * width;\n const y = max === 0 ? height : height - (value / max) * height;\n return `${x.toFixed(1)},${y.toFixed(1)}`;\n })\n .join(\" \");\n}\n"],"names":[],"mappings":";;AASA,MAAM,UAAU,CAAC,GAAoB,MACnC,KAAK,MAAM,EAAE,IAAI,SAAS,IAAI,KAAK,MAAM,EAAE,IAAI,SAAS,KAAK,EAAE,IAAI,MAAM,EAAE,IAAI;AAQ1E,SAAS,mBAAmB,MAA2C;AAC5E,QAAM,4BAAY,IAAA;AAClB,aAAW,OAAO,MAAM;AACtB,QAAI,MAAM,IAAI,IAAI,GAAG,EAAG,OAAM,IAAI,MAAM,qBAAqB,IAAI,GAAG,iBAAiB;AACrF,UAAM,IAAI,IAAI,KAAK,EAAE,KAAK,UAAU,CAAA,GAAI,QAAQ,IAAI,WAAW,YAAY,QAAQ,IAAI,QAAQ,GAAG;AAAA,EACpG;AAEA,QAAM,QAA2B,CAAA;AACjC,aAAW,QAAQ,MAAM,UAAU;AACjC,UAAM,SAAS,KAAK,IAAI,YAAY,MAAM,IAAI,KAAK,IAAI,SAAS,IAAI;AACpE,KAAC,SAAS,OAAO,WAAW,OAAO,KAAK,IAAI;AAAA,EAC9C;AAEA,QAAM,KAAK,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAE,IAAI,MAAM,CAAC,IAAI,OAAO,QAAQ,EAAE,IAAI,MAAM,CAAC,KAAK,QAAQ,GAAG,CAAC,CAAC;AACnG,QAAM,UAAU,cAAc,KAAK;AACnC,MAAI,QAAQ,WAAW,MAAM,MAAM;AACjC,UAAM,OAAO,CAAC,GAAG,MAAM,KAAA,CAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,KAAK,CAAC,SAAS,KAAK,IAAI,QAAQ,GAAG,CAAC;AAC5F,UAAM,IAAI,MAAM,8CAA8C,KAAK,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EAC1G;AACA,aAAW,QAAQ,MAAM,OAAA,EAAU,MAAK,SAAS,KAAK,OAAO;AAC7D,SAAO;AACT;AAGO,SAAS,cAAc,OAA6C;AACzE,QAAM,OAA0B,CAAA;AAChC,QAAM,QAAQ,CAAC,SAA0B;AACvC,SAAK,KAAK,IAAI;AACd,SAAK,SAAS,QAAQ,KAAK;AAAA,EAC7B;AACA,QAAM,QAAQ,KAAK;AACnB,SAAO;AACT;AAGO,SAAS,eAAe,SAAoD;AACjF,QAAM,4BAAY,IAAA;AAClB,aAAW,UAAU,QAAS,OAAM,IAAI,OAAO,KAAK,CAAC,GAAI,MAAM,IAAI,OAAO,GAAG,KAAK,CAAA,GAAK,MAAM,CAAC;AAC9F,SAAO,IAAI;AAAA,IACT,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,OAAO,UAAU,CAAC,CAAC;AAAA,EAAA;AAElH;AAGO,SAAS,gBAAgB,QAAkB,OAAe,QAAoC;AACnG,MAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,QAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,SAAO,OACJ,IAAI,CAAC,OAAO,UAAU;AACrB,UAAM,IAAK,SAAS,OAAO,SAAS,KAAM;AAC1C,UAAM,IAAI,QAAQ,IAAI,SAAS,SAAU,QAAQ,MAAO;AACxD,WAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,EACxC,CAAC,EACA,KAAK,GAAG;AACb;;;;;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ProcessNodeRow, ProcessSampleRow } from './types.js';
|
|
2
|
+
export type ProcessTreeNode = {
|
|
3
|
+
row: ProcessNodeRow;
|
|
4
|
+
children: ProcessTreeNode[];
|
|
5
|
+
/** The process has exited: by its status, or because an exit time was recorded. */
|
|
6
|
+
exited: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Builds the forest a `process_tree` stream describes: each row under its
|
|
10
|
+
* `parentKey`. A row whose parent was never recorded (the capture began below
|
|
11
|
+
* it) becomes a root. The stream holds each process once, so a repeated key or a
|
|
12
|
+
* parent cycle is a corrupt capture and throws.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildProcessForest(rows: ProcessNodeRow[]): ProcessTreeNode[];
|
|
15
|
+
/** The forest depth-first, parents before children. */
|
|
16
|
+
export declare function flattenForest(roots: ProcessTreeNode[]): ProcessTreeNode[];
|
|
17
|
+
/** Each process's CPU % over its samples, in tick order. */
|
|
18
|
+
export declare function cpuSeriesByKey(samples: ProcessSampleRow[]): Map<string, number[]>;
|
|
19
|
+
/** SVG polyline points for `values` in a `width` x `height` box, zero at the bottom and the maximum at the top. */
|
|
20
|
+
export declare function sparklinePoints(values: number[], width: number, height: number): string | undefined;
|
|
21
|
+
//# sourceMappingURL=process-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-tree.d.ts","sourceRoot":"","sources":["../../../src/data/traces/process-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,cAAc,CAAC;IACpB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,mFAAmF;IACnF,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAKF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,eAAe,EAAE,CAqB5E;AAED,uDAAuD;AACvD,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAQzE;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAMjF;AAED,mHAAmH;AACnH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUnG"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const byStart = (a, b) => Date.parse(a.row.startedAt) - Date.parse(b.row.startedAt) || a.row.pid - b.row.pid;
|
|
2
|
+
function buildProcessForest(rows) {
|
|
3
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
4
|
+
for (const row of rows) {
|
|
5
|
+
if (nodes.has(row.key)) throw new Error(`process tree key "${row.key}" appears twice`);
|
|
6
|
+
nodes.set(row.key, { row, children: [], exited: row.status === "exited" || Boolean(row.exitedAt) });
|
|
7
|
+
}
|
|
8
|
+
const roots = [];
|
|
9
|
+
for (const node of nodes.values()) {
|
|
10
|
+
const parent = node.row.parentKey ? nodes.get(node.row.parentKey) : void 0;
|
|
11
|
+
(parent ? parent.children : roots).push(node);
|
|
12
|
+
}
|
|
13
|
+
roots.sort((a, b) => Number(Boolean(b.row.isRoot)) - Number(Boolean(a.row.isRoot)) || byStart(a, b));
|
|
14
|
+
const reached = flattenForest(roots);
|
|
15
|
+
if (reached.length !== nodes.size) {
|
|
16
|
+
const lost = [...nodes.keys()].filter((key) => !reached.some((node) => node.row.key === key));
|
|
17
|
+
throw new Error(`process tree has a parent cycle among keys ${lost.map((key) => `"${key}"`).join(", ")}`);
|
|
18
|
+
}
|
|
19
|
+
for (const node of nodes.values()) node.children.sort(byStart);
|
|
20
|
+
return roots;
|
|
21
|
+
}
|
|
22
|
+
function flattenForest(roots) {
|
|
23
|
+
const flat = [];
|
|
24
|
+
const visit = (node) => {
|
|
25
|
+
flat.push(node);
|
|
26
|
+
node.children.forEach(visit);
|
|
27
|
+
};
|
|
28
|
+
roots.forEach(visit);
|
|
29
|
+
return flat;
|
|
30
|
+
}
|
|
31
|
+
function cpuSeriesByKey(samples) {
|
|
32
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const sample of samples) byKey.set(sample.key, [...byKey.get(sample.key) ?? [], sample]);
|
|
34
|
+
return new Map(
|
|
35
|
+
[...byKey].map(([key, list]) => [key, list.sort((a, b) => a.tick - b.tick).map((sample) => sample.cpuPercent)])
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
function sparklinePoints(values, width, height) {
|
|
39
|
+
if (values.length < 2) return void 0;
|
|
40
|
+
const max = Math.max(...values);
|
|
41
|
+
return values.map((value, index) => {
|
|
42
|
+
const x = index / (values.length - 1) * width;
|
|
43
|
+
const y = max === 0 ? height : height - value / max * height;
|
|
44
|
+
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
|
45
|
+
}).join(" ");
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
buildProcessForest,
|
|
49
|
+
cpuSeriesByKey,
|
|
50
|
+
flattenForest,
|
|
51
|
+
sparklinePoints
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=process-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-tree.js","sources":["../../../src/data/traces/process-tree.ts"],"sourcesContent":["import type { ProcessNodeRow, ProcessSampleRow } from \"./types\";\n\nexport type ProcessTreeNode = {\n row: ProcessNodeRow;\n children: ProcessTreeNode[];\n /** The process has exited: by its status, or because an exit time was recorded. */\n exited: boolean;\n};\n\nconst byStart = (a: ProcessTreeNode, b: ProcessTreeNode) =>\n Date.parse(a.row.startedAt) - Date.parse(b.row.startedAt) || a.row.pid - b.row.pid;\n\n/**\n * Builds the forest a `process_tree` stream describes: each row under its\n * `parentKey`. A row whose parent was never recorded (the capture began below\n * it) becomes a root. The stream holds each process once, so a repeated key or a\n * parent cycle is a corrupt capture and throws.\n */\nexport function buildProcessForest(rows: ProcessNodeRow[]): ProcessTreeNode[] {\n const nodes = new Map<string, ProcessTreeNode>();\n for (const row of rows) {\n if (nodes.has(row.key)) throw new Error(`process tree key \"${row.key}\" appears twice`);\n nodes.set(row.key, { row, children: [], exited: row.status === \"exited\" || Boolean(row.exitedAt) });\n }\n\n const roots: ProcessTreeNode[] = [];\n for (const node of nodes.values()) {\n const parent = node.row.parentKey ? nodes.get(node.row.parentKey) : undefined;\n (parent ? parent.children : roots).push(node);\n }\n\n roots.sort((a, b) => Number(Boolean(b.row.isRoot)) - Number(Boolean(a.row.isRoot)) || byStart(a, b));\n const reached = flattenForest(roots);\n if (reached.length !== nodes.size) {\n const lost = [...nodes.keys()].filter((key) => !reached.some((node) => node.row.key === key));\n throw new Error(`process tree has a parent cycle among keys ${lost.map((key) => `\"${key}\"`).join(\", \")}`);\n }\n for (const node of nodes.values()) node.children.sort(byStart);\n return roots;\n}\n\n/** The forest depth-first, parents before children. */\nexport function flattenForest(roots: ProcessTreeNode[]): ProcessTreeNode[] {\n const flat: ProcessTreeNode[] = [];\n const visit = (node: ProcessTreeNode) => {\n flat.push(node);\n node.children.forEach(visit);\n };\n roots.forEach(visit);\n return flat;\n}\n\n/** Each process's CPU % over its samples, in tick order. */\nexport function cpuSeriesByKey(samples: ProcessSampleRow[]): Map<string, number[]> {\n const byKey = new Map<string, ProcessSampleRow[]>();\n for (const sample of samples) byKey.set(sample.key, [...(byKey.get(sample.key) ?? []), sample]);\n return new Map(\n [...byKey].map(([key, list]) => [key, list.sort((a, b) => a.tick - b.tick).map((sample) => sample.cpuPercent)]),\n );\n}\n\n/** SVG polyline points for `values` in a `width` x `height` box, zero at the bottom and the maximum at the top. */\nexport function sparklinePoints(values: number[], width: number, height: number): string | undefined {\n if (values.length < 2) return undefined;\n const max = Math.max(...values);\n return values\n .map((value, index) => {\n const x = (index / (values.length - 1)) * width;\n const y = max === 0 ? height : height - (value / max) * height;\n return `${x.toFixed(1)},${y.toFixed(1)}`;\n })\n .join(\" \");\n}\n"],"names":[],"mappings":"AASA,MAAM,UAAU,CAAC,GAAoB,MACnC,KAAK,MAAM,EAAE,IAAI,SAAS,IAAI,KAAK,MAAM,EAAE,IAAI,SAAS,KAAK,EAAE,IAAI,MAAM,EAAE,IAAI;AAQ1E,SAAS,mBAAmB,MAA2C;AAC5E,QAAM,4BAAY,IAAA;AAClB,aAAW,OAAO,MAAM;AACtB,QAAI,MAAM,IAAI,IAAI,GAAG,EAAG,OAAM,IAAI,MAAM,qBAAqB,IAAI,GAAG,iBAAiB;AACrF,UAAM,IAAI,IAAI,KAAK,EAAE,KAAK,UAAU,CAAA,GAAI,QAAQ,IAAI,WAAW,YAAY,QAAQ,IAAI,QAAQ,GAAG;AAAA,EACpG;AAEA,QAAM,QAA2B,CAAA;AACjC,aAAW,QAAQ,MAAM,UAAU;AACjC,UAAM,SAAS,KAAK,IAAI,YAAY,MAAM,IAAI,KAAK,IAAI,SAAS,IAAI;AACpE,KAAC,SAAS,OAAO,WAAW,OAAO,KAAK,IAAI;AAAA,EAC9C;AAEA,QAAM,KAAK,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAE,IAAI,MAAM,CAAC,IAAI,OAAO,QAAQ,EAAE,IAAI,MAAM,CAAC,KAAK,QAAQ,GAAG,CAAC,CAAC;AACnG,QAAM,UAAU,cAAc,KAAK;AACnC,MAAI,QAAQ,WAAW,MAAM,MAAM;AACjC,UAAM,OAAO,CAAC,GAAG,MAAM,KAAA,CAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,KAAK,CAAC,SAAS,KAAK,IAAI,QAAQ,GAAG,CAAC;AAC5F,UAAM,IAAI,MAAM,8CAA8C,KAAK,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EAC1G;AACA,aAAW,QAAQ,MAAM,OAAA,EAAU,MAAK,SAAS,KAAK,OAAO;AAC7D,SAAO;AACT;AAGO,SAAS,cAAc,OAA6C;AACzE,QAAM,OAA0B,CAAA;AAChC,QAAM,QAAQ,CAAC,SAA0B;AACvC,SAAK,KAAK,IAAI;AACd,SAAK,SAAS,QAAQ,KAAK;AAAA,EAC7B;AACA,QAAM,QAAQ,KAAK;AACnB,SAAO;AACT;AAGO,SAAS,eAAe,SAAoD;AACjF,QAAM,4BAAY,IAAA;AAClB,aAAW,UAAU,QAAS,OAAM,IAAI,OAAO,KAAK,CAAC,GAAI,MAAM,IAAI,OAAO,GAAG,KAAK,CAAA,GAAK,MAAM,CAAC;AAC9F,SAAO,IAAI;AAAA,IACT,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,OAAO,UAAU,CAAC,CAAC;AAAA,EAAA;AAElH;AAGO,SAAS,gBAAgB,QAAkB,OAAe,QAAoC;AACnG,MAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,QAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,SAAO,OACJ,IAAI,CAAC,OAAO,UAAU;AACrB,UAAM,IAAK,SAAS,OAAO,SAAS,KAAM;AAC1C,UAAM,IAAI,QAAQ,IAAI,SAAS,SAAU,QAAQ,MAAO;AACxD,WAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,EACxC,CAAC,EACA,KAAK,GAAG;AACb;"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const format = require("../../lib/format.cjs");
|
|
4
|
+
function formatStatementDuration(ms) {
|
|
5
|
+
if (ms >= 100) return format.formatDuration(ms);
|
|
6
|
+
return `${Number(ms.toFixed(1))} ms`;
|
|
7
|
+
}
|
|
8
|
+
function sumKnownRows(statements) {
|
|
9
|
+
const known = statements.filter((statement) => statement.rows >= 0);
|
|
10
|
+
return known.length === 0 ? -1 : known.reduce((total, statement) => total + statement.rows, 0);
|
|
11
|
+
}
|
|
12
|
+
function toDisplayRow(id, statements) {
|
|
13
|
+
var _a;
|
|
14
|
+
const [first] = statements;
|
|
15
|
+
return {
|
|
16
|
+
id,
|
|
17
|
+
startedAt: first.startedAt,
|
|
18
|
+
op: first.op,
|
|
19
|
+
tables: first.tables,
|
|
20
|
+
sql: first.sql,
|
|
21
|
+
durationMs: statements.reduce((total, statement) => total + statement.durationMs, 0),
|
|
22
|
+
rows: sumKnownRows(statements),
|
|
23
|
+
error: ((_a = statements.find((statement) => statement.error)) == null ? void 0 : _a.error) ?? "",
|
|
24
|
+
failed: statements.filter((statement) => statement.error).length,
|
|
25
|
+
count: statements.length,
|
|
26
|
+
occurrences: statements
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function groupStatements(statements, options) {
|
|
30
|
+
const groups = /* @__PURE__ */ new Map();
|
|
31
|
+
statements.forEach((statement, index) => {
|
|
32
|
+
const key = options.group && statement.repeatKey ? `repeat:${statement.repeatKey}` : `row:${index}`;
|
|
33
|
+
groups.set(key, [...groups.get(key) ?? [], statement]);
|
|
34
|
+
});
|
|
35
|
+
return [...groups].map(([key, members]) => toDisplayRow(key, members));
|
|
36
|
+
}
|
|
37
|
+
exports.formatStatementDuration = formatStatementDuration;
|
|
38
|
+
exports.groupStatements = groupStatements;
|
|
39
|
+
//# sourceMappingURL=sql-trace.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-trace.cjs","sources":["../../../src/data/traces/sql-trace.ts"],"sourcesContent":["import { formatDuration } from \"../../lib/format\";\nimport type { SqlStatementRow } from \"./types\";\n\n/** One table row: a statement, or every execution of statements sharing a `repeatKey`. */\nexport type SqlDisplayRow = {\n id: string;\n startedAt: string;\n op: string;\n tables: string[];\n sql: string;\n /** Total of the executions. */\n durationMs: number;\n /** Rows the executions returned, or -1 when no driver could tell. */\n rows: number;\n /** The first error among the executions, or \"\". */\n error: string;\n /** How many executions returned an error. */\n failed: number;\n /** How many executions the row stands for. */\n count: number;\n occurrences: SqlStatementRow[];\n};\n\n/** A statement's duration: sub-100ms values keep a decimal (a fast query is 0.9 ms, not 1 ms), longer ones read as elsewhere. */\nexport function formatStatementDuration(ms: number): string {\n if (ms >= 100) return formatDuration(ms);\n return `${Number(ms.toFixed(1))} ms`;\n}\n\nfunction sumKnownRows(statements: SqlStatementRow[]): number {\n const known = statements.filter((statement) => statement.rows >= 0);\n return known.length === 0 ? -1 : known.reduce((total, statement) => total + statement.rows, 0);\n}\n\nfunction toDisplayRow(id: string, statements: SqlStatementRow[]): SqlDisplayRow {\n const [first] = statements as [SqlStatementRow, ...SqlStatementRow[]];\n return {\n id,\n startedAt: first.startedAt,\n op: first.op,\n tables: first.tables,\n sql: first.sql,\n durationMs: statements.reduce((total, statement) => total + statement.durationMs, 0),\n rows: sumKnownRows(statements),\n error: statements.find((statement) => statement.error)?.error ?? \"\",\n failed: statements.filter((statement) => statement.error).length,\n count: statements.length,\n occurrences: statements,\n };\n}\n\n/**\n * Turns statements into table rows. With `group`, statements sharing a\n * non-empty `repeatKey` (the same statement run with different values) become\n * one row, placed where the first of them ran.\n */\nexport function groupStatements(statements: SqlStatementRow[], options: { group: boolean }): SqlDisplayRow[] {\n const groups = new Map<string, SqlStatementRow[]>();\n statements.forEach((statement, index) => {\n const key = options.group && statement.repeatKey ? `repeat:${statement.repeatKey}` : `row:${index}`;\n groups.set(key, [...(groups.get(key) ?? []), statement]);\n });\n return [...groups].map(([key, members]) => toDisplayRow(key, members));\n}\n"],"names":["formatDuration"],"mappings":";;;AAwBO,SAAS,wBAAwB,IAAoB;AAC1D,MAAI,MAAM,IAAK,QAAOA,OAAAA,eAAe,EAAE;AACvC,SAAO,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC;AACjC;AAEA,SAAS,aAAa,YAAuC;AAC3D,QAAM,QAAQ,WAAW,OAAO,CAAC,cAAc,UAAU,QAAQ,CAAC;AAClE,SAAO,MAAM,WAAW,IAAI,KAAK,MAAM,OAAO,CAAC,OAAO,cAAc,QAAQ,UAAU,MAAM,CAAC;AAC/F;AAEA,SAAS,aAAa,IAAY,YAA8C;;AAC9E,QAAM,CAAC,KAAK,IAAI;AAChB,SAAO;AAAA,IACL;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,IAAI,MAAM;AAAA,IACV,QAAQ,MAAM;AAAA,IACd,KAAK,MAAM;AAAA,IACX,YAAY,WAAW,OAAO,CAAC,OAAO,cAAc,QAAQ,UAAU,YAAY,CAAC;AAAA,IACnF,MAAM,aAAa,UAAU;AAAA,IAC7B,SAAO,gBAAW,KAAK,CAAC,cAAc,UAAU,KAAK,MAA9C,mBAAiD,UAAS;AAAA,IACjE,QAAQ,WAAW,OAAO,CAAC,cAAc,UAAU,KAAK,EAAE;AAAA,IAC1D,OAAO,WAAW;AAAA,IAClB,aAAa;AAAA,EAAA;AAEjB;AAOO,SAAS,gBAAgB,YAA+B,SAA8C;AAC3G,QAAM,6BAAa,IAAA;AACnB,aAAW,QAAQ,CAAC,WAAW,UAAU;AACvC,UAAM,MAAM,QAAQ,SAAS,UAAU,YAAY,UAAU,UAAU,SAAS,KAAK,OAAO,KAAK;AACjG,WAAO,IAAI,KAAK,CAAC,GAAI,OAAO,IAAI,GAAG,KAAK,IAAK,SAAS,CAAC;AAAA,EACzD,CAAC;AACD,SAAO,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,OAAO,MAAM,aAAa,KAAK,OAAO,CAAC;AACvE;;;"}
|