@electron-stagewright/plugin-budget 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +148 -2
- package/dist/budget.d.ts +134 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +129 -0
- package/dist/budget.js.map +1 -0
- package/dist/bundle.d.ts +15 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +64 -0
- package/dist/bundle.js.map +1 -0
- package/dist/check.d.ts +43 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +172 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +77 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +401 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/measure.d.ts +47 -0
- package/dist/measure.d.ts.map +1 -0
- package/dist/measure.js +139 -0
- package/dist/measure.js.map +1 -0
- package/dist/memory.d.ts +43 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +159 -0
- package/dist/memory.js.map +1 -0
- package/dist/plugin.d.ts +13 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +456 -0
- package/dist/plugin.js.map +1 -0
- package/dist/probe.d.ts +64 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +161 -0
- package/dist/probe.js.map +1 -0
- package/dist/render.d.ts +21 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +63 -0
- package/dist/render.js.map +1 -0
- package/dist/run.d.ts +218 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +218 -0
- package/dist/run.js.map +1 -0
- package/dist/session.d.ts +54 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +167 -0
- package/dist/session.js.map +1 -0
- package/package.json +60 -3
package/dist/probe.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bodies the plugin evaluates inside the app through the transport's `evaluate` seam. Each is a
|
|
3
|
+
* self-contained string: no imports, no closures, JSON-safe results.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
/** Timer lateness below this is scheduler jitter, not a blocked main thread. */
|
|
8
|
+
export const MIN_STALL_MS = 10;
|
|
9
|
+
export const SAMPLE_INTERVAL_MS = 10;
|
|
10
|
+
export const DEFAULT_IPC_INTERVAL_MS = 25;
|
|
11
|
+
export const DEFAULT_WINDOW_MS = 2_000;
|
|
12
|
+
/** An unread window stops itself after this, so a detached or abandoned app keeps no timer. */
|
|
13
|
+
export const MAX_WINDOW_MS = 600_000;
|
|
14
|
+
const STARTUP_READ_TIMEOUT_MS = 10_000;
|
|
15
|
+
const FIRST_WINDOW_TIMING = `(() => {
|
|
16
|
+
const entry = performance.getEntriesByType('navigation')[0];
|
|
17
|
+
return {
|
|
18
|
+
timeOrigin: performance.timeOrigin,
|
|
19
|
+
loadEventEnd: entry ? entry.loadEventEnd : 0,
|
|
20
|
+
type: entry ? entry.type : null,
|
|
21
|
+
url: entry ? entry.name : location.href,
|
|
22
|
+
};
|
|
23
|
+
})()`;
|
|
24
|
+
/**
|
|
25
|
+
* Main-process body. `facts` reads process metrics. `start` arms a 10 ms timer whose lateness is
|
|
26
|
+
* the blocked time (replacing any earlier window); `read` summarises it and, with `stop`, clears it.
|
|
27
|
+
* `startup` reads the navigation timing of the first window still open.
|
|
28
|
+
*/
|
|
29
|
+
export const MAIN_BODY = `
|
|
30
|
+
const KEY = '__swBudget';
|
|
31
|
+
const round = (value) => Math.round(value * 10) / 10;
|
|
32
|
+
if (arg.op === 'facts') {
|
|
33
|
+
return {
|
|
34
|
+
pid: process.pid,
|
|
35
|
+
creationTime: typeof process.getCreationTime === 'function' ? process.getCreationTime() : null,
|
|
36
|
+
electron: process.versions.electron || null,
|
|
37
|
+
platform: process.platform,
|
|
38
|
+
processes: electronApp.app.getAppMetrics().map((entry) => ({
|
|
39
|
+
pid: entry.pid,
|
|
40
|
+
type: entry.type,
|
|
41
|
+
name: entry.name || entry.serviceName || null,
|
|
42
|
+
workingSetKb: entry.memory.workingSetSize,
|
|
43
|
+
privateKb: typeof entry.memory.privateBytes === 'number' ? entry.memory.privateBytes : null,
|
|
44
|
+
})),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (arg.op === 'start') {
|
|
48
|
+
const previous = globalThis[KEY];
|
|
49
|
+
if (previous) clearInterval(previous.handle);
|
|
50
|
+
const state = { startedAt: performance.now(), last: performance.now(), stalls: [], handle: null };
|
|
51
|
+
state.handle = setInterval(() => {
|
|
52
|
+
const at = performance.now();
|
|
53
|
+
if (at - state.startedAt > ${MAX_WINDOW_MS}) {
|
|
54
|
+
clearInterval(state.handle);
|
|
55
|
+
if (globalThis[KEY] === state) delete globalThis[KEY];
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const stall = at - state.last - ${SAMPLE_INTERVAL_MS};
|
|
59
|
+
if (stall >= ${MIN_STALL_MS}) state.stalls.push(stall);
|
|
60
|
+
state.last = at;
|
|
61
|
+
}, ${SAMPLE_INTERVAL_MS});
|
|
62
|
+
if (typeof state.handle.unref === 'function') state.handle.unref();
|
|
63
|
+
globalThis[KEY] = state;
|
|
64
|
+
return { started: true };
|
|
65
|
+
}
|
|
66
|
+
if (arg.op === 'read') {
|
|
67
|
+
const state = globalThis[KEY];
|
|
68
|
+
if (!state) return null;
|
|
69
|
+
const at = performance.now();
|
|
70
|
+
const stalls = state.stalls.slice();
|
|
71
|
+
const tail = at - state.last - ${SAMPLE_INTERVAL_MS};
|
|
72
|
+
if (tail >= ${MIN_STALL_MS}) stalls.push(tail);
|
|
73
|
+
if (arg.stop) {
|
|
74
|
+
clearInterval(state.handle);
|
|
75
|
+
delete globalThis[KEY];
|
|
76
|
+
}
|
|
77
|
+
const windowMs = Math.max(at - state.startedAt, 1);
|
|
78
|
+
const blockedMs = stalls.reduce((sum, stall) => sum + stall, 0);
|
|
79
|
+
return {
|
|
80
|
+
windowMs: round(windowMs),
|
|
81
|
+
blockedMs: round(blockedMs),
|
|
82
|
+
blockedPercent: round((blockedMs / windowMs) * 100),
|
|
83
|
+
maxStallMs: round(stalls.reduce((max, stall) => Math.max(max, stall), 0)),
|
|
84
|
+
stallsOver50Ms: stalls.filter((stall) => stall > 50).length,
|
|
85
|
+
stallsOver100Ms: stalls.filter((stall) => stall > 100).length,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (arg.op === 'startup') {
|
|
89
|
+
const windows = electronApp.BrowserWindow.getAllWindows().filter((win) => !win.isDestroyed());
|
|
90
|
+
if (windows.length === 0) return { error: 'no window is open' };
|
|
91
|
+
const first = windows.reduce((a, b) => (b.id < a.id ? b : a));
|
|
92
|
+
const contents = first.webContents;
|
|
93
|
+
// executeJavaScript waits, unbounded, for a window that never loaded or is still loading.
|
|
94
|
+
if (!contents.getURL() || contents.isLoadingMainFrame()) {
|
|
95
|
+
return { error: 'the first window has not finished loading' };
|
|
96
|
+
}
|
|
97
|
+
const history = contents.navigationHistory;
|
|
98
|
+
let timer;
|
|
99
|
+
// A hung renderer must not hold the session's budget queue forever.
|
|
100
|
+
const timing = await Promise.race([
|
|
101
|
+
contents.executeJavaScript(${JSON.stringify(FIRST_WINDOW_TIMING)}),
|
|
102
|
+
new Promise((_, reject) => {
|
|
103
|
+
timer = setTimeout(() => reject(new Error('the first window did not answer')), ${STARTUP_READ_TIMEOUT_MS});
|
|
104
|
+
}),
|
|
105
|
+
]).finally(() => clearTimeout(timer));
|
|
106
|
+
const firstEntry =
|
|
107
|
+
history && typeof history.getEntryAtIndex === 'function' ? history.getEntryAtIndex(0) : null;
|
|
108
|
+
// Electron before 32 has no navigationHistory, only webContents.length() (deprecated after).
|
|
109
|
+
const historyLength =
|
|
110
|
+
!history && typeof contents.length === 'function' ? contents.length() : null;
|
|
111
|
+
return { ...timing, firstUrl: firstEntry ? firstEntry.url : null, historyLength };
|
|
112
|
+
}
|
|
113
|
+
throw new Error('unknown budget op');
|
|
114
|
+
`;
|
|
115
|
+
const IPC_KEY = '__swBudgetIpc';
|
|
116
|
+
const MAX_IPC_SAMPLES = 20_000;
|
|
117
|
+
/**
|
|
118
|
+
* Renderer body that starts a background loop timing one round trip of `probe` per interval.
|
|
119
|
+
* The expression is inlined, not evaluated at runtime, so a page CSP without `unsafe-eval` holds.
|
|
120
|
+
*/
|
|
121
|
+
export function buildIpcStartBody(probe) {
|
|
122
|
+
return `
|
|
123
|
+
const previous = window['${IPC_KEY}'];
|
|
124
|
+
if (previous) previous.stop = true;
|
|
125
|
+
const state = { samples: [], errors: 0, lastError: null, stop: false };
|
|
126
|
+
window['${IPC_KEY}'] = state;
|
|
127
|
+
const once = async () => { await (${probe}); };
|
|
128
|
+
const intervalMs = arg.intervalMs;
|
|
129
|
+
const begin = performance.now();
|
|
130
|
+
void (async () => {
|
|
131
|
+
while (
|
|
132
|
+
!state.stop &&
|
|
133
|
+
state.samples.length < ${MAX_IPC_SAMPLES} &&
|
|
134
|
+
performance.now() - begin < ${MAX_WINDOW_MS}
|
|
135
|
+
) {
|
|
136
|
+
const startedAt = performance.now();
|
|
137
|
+
try {
|
|
138
|
+
await once();
|
|
139
|
+
state.samples.push(performance.now() - startedAt);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
state.errors += 1;
|
|
142
|
+
state.lastError = String(error && error.message ? error.message : error);
|
|
143
|
+
if (state.samples.length === 0 && state.errors >= 20) break;
|
|
144
|
+
}
|
|
145
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
146
|
+
}
|
|
147
|
+
})();
|
|
148
|
+
return true;
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
/** Renderer body: read the probe samples and, with `stop`, end the loop. Null when none runs. */
|
|
152
|
+
export const IPC_READ_BODY = `
|
|
153
|
+
const state = window['${IPC_KEY}'];
|
|
154
|
+
if (!state) return null;
|
|
155
|
+
if (arg.stop) {
|
|
156
|
+
state.stop = true;
|
|
157
|
+
delete window['${IPC_KEY}'];
|
|
158
|
+
}
|
|
159
|
+
return { samples: state.samples.slice(), errors: state.errors, lastError: state.lastError };
|
|
160
|
+
`;
|
|
161
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,gFAAgF;AAChF,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,CAAA;AAC9B,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAA;AACpC,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAA;AACzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAA;AACtC,+FAA+F;AAC/F,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAA;AACpC,MAAM,uBAAuB,GAAG,MAAM,CAAA;AAmBtC,MAAM,mBAAmB,GAAG;;;;;;;;KAQvB,CAAA;AAEL;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;iCAwBQ,aAAa;;;;;sCAKR,kBAAkB;mBACrC,YAAY;;OAExB,kBAAkB;;;;;;;;;;mCAUU,kBAAkB;gBACrC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCA6BK,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC;;uFAEmB,uBAAuB;;;;;;;;;;;CAW7G,CAAA;AAoBD,MAAM,OAAO,GAAG,eAAe,CAAA;AAC/B,MAAM,eAAe,GAAG,MAAM,CAAA;AAE9B;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,OAAO;2BACkB,OAAO;;;UAGxB,OAAO;oCACmB,KAAK;;;;;;6BAMZ,eAAe;kCACV,aAAa;;;;;;;;;;;;;;;CAe9C,CAAA;AACD,CAAC;AAQD,iGAAiG;AACjG,MAAM,CAAC,MAAM,aAAa,GAAG;wBACL,OAAO;;;;mBAIZ,OAAO;;;CAGzB,CAAA"}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown tables for a check report and for a before/after comparison.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { CheckReport } from './check.js';
|
|
7
|
+
import { type BudgetRun, type SummaryRow } from './run.js';
|
|
8
|
+
export declare function renderCheckReport(report: CheckReport): string;
|
|
9
|
+
export interface ComparisonRow {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly phase?: SummaryRow['phase'];
|
|
12
|
+
readonly unit: SummaryRow['unit'];
|
|
13
|
+
readonly metric?: string;
|
|
14
|
+
readonly before?: number;
|
|
15
|
+
readonly after?: number;
|
|
16
|
+
readonly deltaPercent?: number;
|
|
17
|
+
}
|
|
18
|
+
/** Pair the rows of two runs. A memory row measured with different metrics is left unpaired. */
|
|
19
|
+
export declare function compareRuns(before: BudgetRun, after: BudgetRun): ComparisonRow[];
|
|
20
|
+
export declare function renderComparison(rows: readonly ComparisonRow[]): string;
|
|
21
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EAAgB,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AAYxE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAe7D;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;IACpC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,aAAa,EAAE,CAoBhF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAcvE"}
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown tables for a check report and for a before/after comparison.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { summarizeRun } from './run.js';
|
|
7
|
+
function table(header, rows) {
|
|
8
|
+
const line = (cells) => `| ${cells.join(' | ')} |`;
|
|
9
|
+
return [line(header), line(header.map(() => '---')), ...rows.map(line)].join('\n');
|
|
10
|
+
}
|
|
11
|
+
const label = (id, phase, metric) => [id, phase === undefined ? '' : `(${phase})`, metric === undefined ? '' : `[${metric}]`]
|
|
12
|
+
.filter((part) => part.length > 0)
|
|
13
|
+
.join(' ');
|
|
14
|
+
export function renderCheckReport(report) {
|
|
15
|
+
const verdict = report.passed ? 'PASS' : `FAIL (${report.reason ?? 'OVER_BUDGET'})`;
|
|
16
|
+
const { pass, fail, unmeasured } = report.summary;
|
|
17
|
+
const rows = report.results.map((result) => [
|
|
18
|
+
label(result.id, result.phase, result.metric),
|
|
19
|
+
result.actual === undefined ? '-' : `${result.actual} ${result.unit}`,
|
|
20
|
+
`${result.limit} ${result.unit}`,
|
|
21
|
+
`${result.allowed} ${result.unit}`,
|
|
22
|
+
result.status + (result.detail === undefined ? '' : `: ${result.detail}`),
|
|
23
|
+
]);
|
|
24
|
+
return [
|
|
25
|
+
`Budget check: ${verdict} - ${pass} pass, ${fail} fail, ${unmeasured} unmeasured`,
|
|
26
|
+
'',
|
|
27
|
+
table(['metric', 'actual', 'budget', 'allowed', 'status'], rows),
|
|
28
|
+
].join('\n');
|
|
29
|
+
}
|
|
30
|
+
/** Pair the rows of two runs. A memory row measured with different metrics is left unpaired. */
|
|
31
|
+
export function compareRuns(before, after) {
|
|
32
|
+
const key = (row) => `${row.id}@${row.phase ?? ''}@${row.metric ?? ''}`;
|
|
33
|
+
const beforeRows = new Map(summarizeRun(before).map((row) => [key(row), row]));
|
|
34
|
+
const afterRows = new Map(summarizeRun(after).map((row) => [key(row), row]));
|
|
35
|
+
return [...new Set([...beforeRows.keys(), ...afterRows.keys()])].map((rowKey) => {
|
|
36
|
+
const a = beforeRows.get(rowKey);
|
|
37
|
+
const b = afterRows.get(rowKey);
|
|
38
|
+
const row = (a ?? b);
|
|
39
|
+
return {
|
|
40
|
+
id: row.id,
|
|
41
|
+
unit: row.unit,
|
|
42
|
+
...(row.phase === undefined ? {} : { phase: row.phase }),
|
|
43
|
+
...(row.metric === undefined ? {} : { metric: row.metric }),
|
|
44
|
+
...(a === undefined ? {} : { before: a.value }),
|
|
45
|
+
...(b === undefined ? {} : { after: b.value }),
|
|
46
|
+
...(a === undefined || b === undefined || a.value === 0
|
|
47
|
+
? {}
|
|
48
|
+
: { deltaPercent: Math.round(((b.value - a.value) / a.value) * 1000) / 10 }),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export function renderComparison(rows) {
|
|
53
|
+
const value = (amount, unit) => amount === undefined ? '-' : `${amount} ${unit}`;
|
|
54
|
+
return table(['metric', 'before', 'after', 'delta'], rows.map((row) => [
|
|
55
|
+
label(row.id, row.phase, row.metric),
|
|
56
|
+
value(row.before, row.unit),
|
|
57
|
+
value(row.after, row.unit),
|
|
58
|
+
row.deltaPercent === undefined
|
|
59
|
+
? '-'
|
|
60
|
+
: `${row.deltaPercent > 0 ? '+' : ''}${row.deltaPercent}%`,
|
|
61
|
+
]));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run document: raw measurements of one build, and the flat summary `check` and `compare` read.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { type Phase, type ProcessType } from './budget.js';
|
|
8
|
+
export declare const RUN_FORMAT = "electron-stagewright-budget-run";
|
|
9
|
+
export declare const RUN_SCHEMA_VERSION = 1;
|
|
10
|
+
declare const stallSchema: z.ZodObject<{
|
|
11
|
+
windowMs: z.ZodNumber;
|
|
12
|
+
blockedMs: z.ZodNumber;
|
|
13
|
+
blockedPercent: z.ZodNumber;
|
|
14
|
+
maxStallMs: z.ZodNumber;
|
|
15
|
+
stallsOver50Ms: z.ZodNumber;
|
|
16
|
+
stallsOver100Ms: z.ZodNumber;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
declare const latencySchema: z.ZodObject<{
|
|
19
|
+
count: z.ZodNumber;
|
|
20
|
+
p50: z.ZodNumber;
|
|
21
|
+
p95: z.ZodNumber;
|
|
22
|
+
p99: z.ZodNumber;
|
|
23
|
+
max: z.ZodNumber;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
declare const processSchema: z.ZodObject<{
|
|
26
|
+
pid: z.ZodNumber;
|
|
27
|
+
type: z.ZodEnum<{
|
|
28
|
+
main: "main";
|
|
29
|
+
renderer: "renderer";
|
|
30
|
+
gpu: "gpu";
|
|
31
|
+
utility: "utility";
|
|
32
|
+
other: "other";
|
|
33
|
+
}>;
|
|
34
|
+
name: z.ZodOptional<z.ZodString>;
|
|
35
|
+
mb: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
workingSetMb: z.ZodNumber;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
declare const memorySchema: z.ZodObject<{
|
|
39
|
+
metric: z.ZodEnum<{
|
|
40
|
+
footprint: "footprint";
|
|
41
|
+
pss: "pss";
|
|
42
|
+
privateBytes: "privateBytes";
|
|
43
|
+
workingSet: "workingSet";
|
|
44
|
+
}>;
|
|
45
|
+
processes: z.ZodArray<z.ZodObject<{
|
|
46
|
+
pid: z.ZodNumber;
|
|
47
|
+
type: z.ZodEnum<{
|
|
48
|
+
main: "main";
|
|
49
|
+
renderer: "renderer";
|
|
50
|
+
gpu: "gpu";
|
|
51
|
+
utility: "utility";
|
|
52
|
+
other: "other";
|
|
53
|
+
}>;
|
|
54
|
+
name: z.ZodOptional<z.ZodString>;
|
|
55
|
+
mb: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
workingSetMb: z.ZodNumber;
|
|
57
|
+
}, z.core.$strip>>;
|
|
58
|
+
}, z.core.$strip>;
|
|
59
|
+
declare const measurementSchema: z.ZodObject<{
|
|
60
|
+
startup: z.ZodOptional<z.ZodObject<{
|
|
61
|
+
firstWindowMs: z.ZodNumber;
|
|
62
|
+
}, z.core.$strip>>;
|
|
63
|
+
memory: z.ZodOptional<z.ZodObject<{
|
|
64
|
+
metric: z.ZodEnum<{
|
|
65
|
+
footprint: "footprint";
|
|
66
|
+
pss: "pss";
|
|
67
|
+
privateBytes: "privateBytes";
|
|
68
|
+
workingSet: "workingSet";
|
|
69
|
+
}>;
|
|
70
|
+
processes: z.ZodArray<z.ZodObject<{
|
|
71
|
+
pid: z.ZodNumber;
|
|
72
|
+
type: z.ZodEnum<{
|
|
73
|
+
main: "main";
|
|
74
|
+
renderer: "renderer";
|
|
75
|
+
gpu: "gpu";
|
|
76
|
+
utility: "utility";
|
|
77
|
+
other: "other";
|
|
78
|
+
}>;
|
|
79
|
+
name: z.ZodOptional<z.ZodString>;
|
|
80
|
+
mb: z.ZodOptional<z.ZodNumber>;
|
|
81
|
+
workingSetMb: z.ZodNumber;
|
|
82
|
+
}, z.core.$strip>>;
|
|
83
|
+
}, z.core.$strip>>;
|
|
84
|
+
mainThread: z.ZodOptional<z.ZodObject<{
|
|
85
|
+
windowMs: z.ZodNumber;
|
|
86
|
+
blockedMs: z.ZodNumber;
|
|
87
|
+
blockedPercent: z.ZodNumber;
|
|
88
|
+
maxStallMs: z.ZodNumber;
|
|
89
|
+
stallsOver50Ms: z.ZodNumber;
|
|
90
|
+
stallsOver100Ms: z.ZodNumber;
|
|
91
|
+
}, z.core.$strip>>;
|
|
92
|
+
ipc: z.ZodOptional<z.ZodObject<{
|
|
93
|
+
count: z.ZodNumber;
|
|
94
|
+
p50: z.ZodNumber;
|
|
95
|
+
p95: z.ZodNumber;
|
|
96
|
+
p99: z.ZodNumber;
|
|
97
|
+
max: z.ZodNumber;
|
|
98
|
+
}, z.core.$strip>>;
|
|
99
|
+
unavailable: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
100
|
+
}, z.core.$strip>;
|
|
101
|
+
declare const bundleEntrySchema: z.ZodObject<{
|
|
102
|
+
id: z.ZodString;
|
|
103
|
+
path: z.ZodString;
|
|
104
|
+
gzip: z.ZodBoolean;
|
|
105
|
+
files: z.ZodNumber;
|
|
106
|
+
bytes: z.ZodNumber;
|
|
107
|
+
unavailable: z.ZodOptional<z.ZodString>;
|
|
108
|
+
}, z.core.$strip>;
|
|
109
|
+
export declare const runSchema: z.ZodObject<{
|
|
110
|
+
format: z.ZodLiteral<"electron-stagewright-budget-run">;
|
|
111
|
+
schema_version: z.ZodLiteral<1>;
|
|
112
|
+
created_at: z.ZodString;
|
|
113
|
+
platform: z.ZodString;
|
|
114
|
+
arch: z.ZodString;
|
|
115
|
+
electron_version: z.ZodOptional<z.ZodString>;
|
|
116
|
+
label: z.ZodOptional<z.ZodString>;
|
|
117
|
+
rounds: z.ZodArray<z.ZodObject<{
|
|
118
|
+
round: z.ZodNumber;
|
|
119
|
+
phase: z.ZodEnum<{
|
|
120
|
+
cold: "cold";
|
|
121
|
+
warm: "warm";
|
|
122
|
+
live: "live";
|
|
123
|
+
}>;
|
|
124
|
+
measurement: z.ZodObject<{
|
|
125
|
+
startup: z.ZodOptional<z.ZodObject<{
|
|
126
|
+
firstWindowMs: z.ZodNumber;
|
|
127
|
+
}, z.core.$strip>>;
|
|
128
|
+
memory: z.ZodOptional<z.ZodObject<{
|
|
129
|
+
metric: z.ZodEnum<{
|
|
130
|
+
footprint: "footprint";
|
|
131
|
+
pss: "pss";
|
|
132
|
+
privateBytes: "privateBytes";
|
|
133
|
+
workingSet: "workingSet";
|
|
134
|
+
}>;
|
|
135
|
+
processes: z.ZodArray<z.ZodObject<{
|
|
136
|
+
pid: z.ZodNumber;
|
|
137
|
+
type: z.ZodEnum<{
|
|
138
|
+
main: "main";
|
|
139
|
+
renderer: "renderer";
|
|
140
|
+
gpu: "gpu";
|
|
141
|
+
utility: "utility";
|
|
142
|
+
other: "other";
|
|
143
|
+
}>;
|
|
144
|
+
name: z.ZodOptional<z.ZodString>;
|
|
145
|
+
mb: z.ZodOptional<z.ZodNumber>;
|
|
146
|
+
workingSetMb: z.ZodNumber;
|
|
147
|
+
}, z.core.$strip>>;
|
|
148
|
+
}, z.core.$strip>>;
|
|
149
|
+
mainThread: z.ZodOptional<z.ZodObject<{
|
|
150
|
+
windowMs: z.ZodNumber;
|
|
151
|
+
blockedMs: z.ZodNumber;
|
|
152
|
+
blockedPercent: z.ZodNumber;
|
|
153
|
+
maxStallMs: z.ZodNumber;
|
|
154
|
+
stallsOver50Ms: z.ZodNumber;
|
|
155
|
+
stallsOver100Ms: z.ZodNumber;
|
|
156
|
+
}, z.core.$strip>>;
|
|
157
|
+
ipc: z.ZodOptional<z.ZodObject<{
|
|
158
|
+
count: z.ZodNumber;
|
|
159
|
+
p50: z.ZodNumber;
|
|
160
|
+
p95: z.ZodNumber;
|
|
161
|
+
p99: z.ZodNumber;
|
|
162
|
+
max: z.ZodNumber;
|
|
163
|
+
}, z.core.$strip>>;
|
|
164
|
+
unavailable: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
165
|
+
}, z.core.$strip>;
|
|
166
|
+
}, z.core.$strip>>;
|
|
167
|
+
bundle: z.ZodOptional<z.ZodObject<{
|
|
168
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
169
|
+
id: z.ZodString;
|
|
170
|
+
path: z.ZodString;
|
|
171
|
+
gzip: z.ZodBoolean;
|
|
172
|
+
files: z.ZodNumber;
|
|
173
|
+
bytes: z.ZodNumber;
|
|
174
|
+
unavailable: z.ZodOptional<z.ZodString>;
|
|
175
|
+
}, z.core.$strip>>;
|
|
176
|
+
}, z.core.$strip>>;
|
|
177
|
+
}, z.core.$strip>;
|
|
178
|
+
export type StallSummary = z.infer<typeof stallSchema>;
|
|
179
|
+
export type LatencySummary = z.infer<typeof latencySchema>;
|
|
180
|
+
export type ProcessMemory = z.infer<typeof processSchema>;
|
|
181
|
+
export type MemoryMeasurement = z.infer<typeof memorySchema>;
|
|
182
|
+
export type Measurement = z.infer<typeof measurementSchema>;
|
|
183
|
+
export type BundleEntryMeasurement = z.infer<typeof bundleEntrySchema>;
|
|
184
|
+
export type BudgetRun = z.infer<typeof runSchema>;
|
|
185
|
+
/** A run file that is missing or is not a run document. */
|
|
186
|
+
export declare class RunFileError extends Error {
|
|
187
|
+
readonly runPath: string;
|
|
188
|
+
constructor(message: string, runPath: string);
|
|
189
|
+
}
|
|
190
|
+
export declare function loadRun(runPath: string): Promise<BudgetRun>;
|
|
191
|
+
export declare function createRun(fields: Pick<BudgetRun, 'rounds'> & Partial<Pick<BudgetRun, 'bundle' | 'label'>> & {
|
|
192
|
+
readonly electronVersion?: string;
|
|
193
|
+
readonly now?: Date;
|
|
194
|
+
}): BudgetRun;
|
|
195
|
+
export declare function median(values: readonly number[]): number;
|
|
196
|
+
/** Nearest-rank percentiles; an empty set reports zeros with `count: 0`. */
|
|
197
|
+
export declare function summarizeLatencies(samples: readonly number[]): LatencySummary;
|
|
198
|
+
/**
|
|
199
|
+
* Megabytes per process type for one memory sample, in the sample's metric. A type with a pid the
|
|
200
|
+
* OS reader could not see is left out rather than undercounted.
|
|
201
|
+
*/
|
|
202
|
+
export declare function memoryByType(memory: MemoryMeasurement): Partial<Record<ProcessType, number>>;
|
|
203
|
+
/** One summarised value: the median over the rounds of one phase. */
|
|
204
|
+
export interface SummaryRow {
|
|
205
|
+
readonly id: string;
|
|
206
|
+
readonly phase?: Phase;
|
|
207
|
+
readonly value: number;
|
|
208
|
+
/** The unrounded median, which a check compares; `value` is for display. */
|
|
209
|
+
readonly exact: number;
|
|
210
|
+
readonly unit: 'ms' | 'MB' | '%' | 'KB';
|
|
211
|
+
/** The memory metric, so a table never shows megabytes without naming what they count. */
|
|
212
|
+
readonly metric?: string;
|
|
213
|
+
readonly samples: number;
|
|
214
|
+
}
|
|
215
|
+
/** Flatten a run into comparable rows (`memory.main`, `ipc.p99Ms`, `bundle.<id>`, ...). */
|
|
216
|
+
export declare function summarizeRun(run: BudgetRun): SummaryRow[];
|
|
217
|
+
export {};
|
|
218
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|