@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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -2
  3. package/dist/budget.d.ts +134 -0
  4. package/dist/budget.d.ts.map +1 -0
  5. package/dist/budget.js +129 -0
  6. package/dist/budget.js.map +1 -0
  7. package/dist/bundle.d.ts +15 -0
  8. package/dist/bundle.d.ts.map +1 -0
  9. package/dist/bundle.js +64 -0
  10. package/dist/bundle.js.map +1 -0
  11. package/dist/check.d.ts +43 -0
  12. package/dist/check.d.ts.map +1 -0
  13. package/dist/check.js +172 -0
  14. package/dist/check.js.map +1 -0
  15. package/dist/cli.d.ts +77 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +401 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/index.d.ts +24 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +17 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/measure.d.ts +47 -0
  24. package/dist/measure.d.ts.map +1 -0
  25. package/dist/measure.js +139 -0
  26. package/dist/measure.js.map +1 -0
  27. package/dist/memory.d.ts +43 -0
  28. package/dist/memory.d.ts.map +1 -0
  29. package/dist/memory.js +159 -0
  30. package/dist/memory.js.map +1 -0
  31. package/dist/plugin.d.ts +13 -0
  32. package/dist/plugin.d.ts.map +1 -0
  33. package/dist/plugin.js +456 -0
  34. package/dist/plugin.js.map +1 -0
  35. package/dist/probe.d.ts +64 -0
  36. package/dist/probe.d.ts.map +1 -0
  37. package/dist/probe.js +161 -0
  38. package/dist/probe.js.map +1 -0
  39. package/dist/render.d.ts +21 -0
  40. package/dist/render.d.ts.map +1 -0
  41. package/dist/render.js +63 -0
  42. package/dist/render.js.map +1 -0
  43. package/dist/run.d.ts +218 -0
  44. package/dist/run.d.ts.map +1 -0
  45. package/dist/run.js +218 -0
  46. package/dist/run.js.map +1 -0
  47. package/dist/session.d.ts +54 -0
  48. package/dist/session.d.ts.map +1 -0
  49. package/dist/session.js +167 -0
  50. package/dist/session.js.map +1 -0
  51. package/package.json +60 -3
package/dist/run.js 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 { readFile } from 'node:fs/promises';
7
+ import { z } from 'zod';
8
+ import { MEMORY_METRICS, PHASES, PROCESS_TYPES } from './budget.js';
9
+ export const RUN_FORMAT = 'electron-stagewright-budget-run';
10
+ export const RUN_SCHEMA_VERSION = 1;
11
+ const stallSchema = z.object({
12
+ windowMs: z.number(),
13
+ blockedMs: z.number(),
14
+ blockedPercent: z.number(),
15
+ maxStallMs: z.number(),
16
+ stallsOver50Ms: z.number(),
17
+ stallsOver100Ms: z.number(),
18
+ });
19
+ const latencySchema = z.object({
20
+ count: z.number(),
21
+ p50: z.number(),
22
+ p95: z.number(),
23
+ p99: z.number(),
24
+ max: z.number(),
25
+ });
26
+ const processSchema = z.object({
27
+ pid: z.number(),
28
+ type: z.enum(PROCESS_TYPES),
29
+ name: z.string().optional(),
30
+ // In the run's `metric`; absent when the OS reader could not see this pid.
31
+ mb: z.number().optional(),
32
+ workingSetMb: z.number(),
33
+ });
34
+ const memorySchema = z.object({
35
+ metric: z.enum(MEMORY_METRICS),
36
+ processes: z.array(processSchema),
37
+ });
38
+ const measurementSchema = z.object({
39
+ startup: z.object({ firstWindowMs: z.number() }).optional(),
40
+ memory: memorySchema.optional(),
41
+ mainThread: stallSchema.optional(),
42
+ ipc: latencySchema.optional(),
43
+ // Why a requested metric is missing, keyed by `startup` / `memory` / `mainThread` / `ipc`.
44
+ unavailable: z.record(z.string(), z.string()).optional(),
45
+ });
46
+ const bundleEntrySchema = z.object({
47
+ id: z.string(),
48
+ path: z.string(),
49
+ gzip: z.boolean(),
50
+ files: z.number(),
51
+ bytes: z.number(),
52
+ unavailable: z.string().optional(),
53
+ });
54
+ export const runSchema = z.object({
55
+ format: z.literal(RUN_FORMAT),
56
+ schema_version: z.literal(RUN_SCHEMA_VERSION),
57
+ created_at: z.string(),
58
+ platform: z.string(),
59
+ arch: z.string(),
60
+ electron_version: z.string().optional(),
61
+ label: z.string().optional(),
62
+ rounds: z.array(z.object({ round: z.number(), phase: z.enum(PHASES), measurement: measurementSchema })),
63
+ bundle: z.object({ entries: z.array(bundleEntrySchema) }).optional(),
64
+ });
65
+ /** A run file that is missing or is not a run document. */
66
+ export class RunFileError extends Error {
67
+ runPath;
68
+ constructor(message, runPath) {
69
+ super(message);
70
+ this.runPath = runPath;
71
+ this.name = 'RunFileError';
72
+ }
73
+ }
74
+ export async function loadRun(runPath) {
75
+ let raw;
76
+ try {
77
+ raw = JSON.parse(await readFile(runPath, 'utf8'));
78
+ }
79
+ catch (error) {
80
+ throw new RunFileError(`Cannot read run file ${runPath}: ${error instanceof Error ? error.message : String(error)}`, runPath);
81
+ }
82
+ const parsed = runSchema.safeParse(raw);
83
+ if (!parsed.success) {
84
+ const issues = parsed.error.issues
85
+ .map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`)
86
+ .join('; ');
87
+ throw new RunFileError(`${runPath} is not a ${RUN_FORMAT} document: ${issues}`, runPath);
88
+ }
89
+ return parsed.data;
90
+ }
91
+ export function createRun(fields) {
92
+ return {
93
+ format: RUN_FORMAT,
94
+ schema_version: RUN_SCHEMA_VERSION,
95
+ created_at: (fields.now ?? new Date()).toISOString(),
96
+ platform: process.platform,
97
+ arch: process.arch,
98
+ ...(fields.electronVersion === undefined ? {} : { electron_version: fields.electronVersion }),
99
+ ...(fields.label === undefined ? {} : { label: fields.label }),
100
+ rounds: fields.rounds,
101
+ ...(fields.bundle === undefined ? {} : { bundle: fields.bundle }),
102
+ };
103
+ }
104
+ const round1 = (value) => Math.round(value * 10) / 10;
105
+ export function median(values) {
106
+ const sorted = [...values].sort((a, b) => a - b);
107
+ const middle = Math.floor(sorted.length / 2);
108
+ const upper = sorted[middle] ?? 0;
109
+ return sorted.length % 2 === 1 ? upper : ((sorted[middle - 1] ?? upper) + upper) / 2;
110
+ }
111
+ /** Nearest-rank percentiles; an empty set reports zeros with `count: 0`. */
112
+ export function summarizeLatencies(samples) {
113
+ const sorted = samples.filter(Number.isFinite).sort((a, b) => a - b);
114
+ const at = (percentile) => round1(sorted[Math.min(sorted.length - 1, Math.ceil((percentile / 100) * sorted.length) - 1)] ?? 0);
115
+ return { count: sorted.length, p50: at(50), p95: at(95), p99: at(99), max: at(100) };
116
+ }
117
+ /**
118
+ * Megabytes per process type for one memory sample, in the sample's metric. A type with a pid the
119
+ * OS reader could not see is left out rather than undercounted.
120
+ */
121
+ export function memoryByType(memory) {
122
+ const totals = {};
123
+ const partial = new Set();
124
+ for (const entry of memory.processes) {
125
+ const mb = memory.metric === 'workingSet' ? entry.workingSetMb : entry.mb;
126
+ if (mb === undefined)
127
+ partial.add(entry.type);
128
+ else
129
+ totals[entry.type] = (totals[entry.type] ?? 0) + mb;
130
+ }
131
+ for (const type of partial)
132
+ delete totals[type];
133
+ return totals;
134
+ }
135
+ /** Sum of every type the sample holds, or undefined when one of them is unmeasured or none exist. */
136
+ function memoryTotal(memory, skipGpu) {
137
+ const types = new Set(memory.processes.map((entry) => entry.type));
138
+ if (skipGpu)
139
+ types.delete('gpu');
140
+ if (types.size === 0)
141
+ return undefined;
142
+ const totals = memoryByType(memory);
143
+ let sum = 0;
144
+ for (const type of types) {
145
+ const mb = totals[type];
146
+ if (mb === undefined)
147
+ return undefined;
148
+ sum += mb;
149
+ }
150
+ return sum;
151
+ }
152
+ /** Flatten a run into comparable rows (`memory.main`, `ipc.p99Ms`, `bundle.<id>`, ...). */
153
+ export function summarizeRun(run) {
154
+ const rows = [];
155
+ for (const phase of PHASES) {
156
+ const measurements = run.rounds.filter((r) => r.phase === phase).map((r) => r.measurement);
157
+ const push = (id, unit, values, metric) => {
158
+ const present = values.filter((value) => value !== undefined);
159
+ if (present.length === 0)
160
+ return;
161
+ const exact = median(present);
162
+ rows.push({
163
+ id,
164
+ phase,
165
+ value: round1(exact),
166
+ exact,
167
+ unit,
168
+ ...(metric === undefined ? {} : { metric }),
169
+ samples: present.length,
170
+ });
171
+ };
172
+ push('startup.firstWindowMs', 'ms', measurements.map((m) => m.startup?.firstWindowMs));
173
+ const memories = measurements.flatMap((m) => (m.memory === undefined ? [] : [m.memory]));
174
+ // Rounds whose OS reader failed fall back to another metric; the majority one is summarised.
175
+ const counts = new Map();
176
+ for (const memory of memories)
177
+ counts.set(memory.metric, (counts.get(memory.metric) ?? 0) + 1);
178
+ let primary;
179
+ for (const { metric } of memories) {
180
+ if (primary === undefined || (counts.get(metric) ?? 0) > (counts.get(primary) ?? 0)) {
181
+ primary = metric;
182
+ }
183
+ }
184
+ // Working set is always recorded, so a budget may name it on any platform.
185
+ for (const metric of new Set([primary, 'workingSet'])) {
186
+ if (metric === undefined)
187
+ continue;
188
+ const sameMetric = memories
189
+ .filter((memory) => memory.metric === primary)
190
+ .map((memory) => ({ ...memory, metric }));
191
+ const byType = sameMetric.map(memoryByType);
192
+ for (const type of PROCESS_TYPES) {
193
+ push(`memory.${type}`, 'MB', byType.map((totals) => totals[type]), metric);
194
+ }
195
+ push('memory.totalWithoutGpu', 'MB', sameMetric.map((memory) => memoryTotal(memory, true)), metric);
196
+ push('memory.total', 'MB', sameMetric.map((memory) => memoryTotal(memory, false)), metric);
197
+ }
198
+ push('mainThread.blockedPercent', '%', measurements.map((m) => m.mainThread?.blockedPercent));
199
+ push('mainThread.maxStallMs', 'ms', measurements.map((m) => m.mainThread?.maxStallMs));
200
+ const ipc = measurements.flatMap((m) => (m.ipc !== undefined && m.ipc.count > 0 ? [m.ipc] : []));
201
+ push('ipc.p95Ms', 'ms', ipc.map((latency) => latency.p95));
202
+ push('ipc.p99Ms', 'ms', ipc.map((latency) => latency.p99));
203
+ }
204
+ for (const entry of run.bundle?.entries ?? []) {
205
+ if (entry.unavailable !== undefined)
206
+ continue;
207
+ rows.push({
208
+ id: `bundle.${entry.id}`,
209
+ value: round1(entry.bytes / 1024),
210
+ exact: entry.bytes / 1024,
211
+ unit: 'KB',
212
+ ...(entry.gzip ? { metric: 'gzip' } : {}),
213
+ samples: 1,
214
+ });
215
+ }
216
+ return rows;
217
+ }
218
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Measure one live app session through the transport's `evaluate` seam.
3
+ *
4
+ * @module
5
+ */
6
+ import { type HostStartTimeReader, type OsMemoryReader } from './memory.js';
7
+ import { type Measurement } from './run.js';
8
+ /** The slice of a transport session the budget needs. */
9
+ export interface EvaluatingSession {
10
+ evaluate<T = unknown>(target: 'main' | 'renderer', body: string, arg?: unknown): Promise<T>;
11
+ }
12
+ export interface IpcProbe {
13
+ readonly probe: string;
14
+ readonly intervalMs?: number;
15
+ }
16
+ export declare const MAIN_EVAL_OFF = "main eval is off; start the server with --allow-eval=main (or bare --allow-eval)";
17
+ export declare const RENDERER_EVAL_OFF = "renderer eval is off; start the server with bare --allow-eval to measure it";
18
+ /**
19
+ * Open a measurement window: arm the main-thread sampler and, when a probe is given and renderer
20
+ * eval is allowed, the IPC probe loop. Returns why the IPC probe did not start, if it did not.
21
+ */
22
+ export declare function startWindow(session: EvaluatingSession, options: {
23
+ readonly ipc?: IpcProbe;
24
+ readonly allowRenderer: boolean;
25
+ /** Why the renderer is off, when it is not the eval grant. */
26
+ readonly rendererOff?: string;
27
+ }): Promise<{
28
+ ipc: boolean;
29
+ ipcUnavailable?: string;
30
+ }>;
31
+ export interface MeasureOptions {
32
+ readonly allowRenderer: boolean;
33
+ readonly rendererOff?: string;
34
+ /** Set when {@link startWindow} already opened a window; otherwise one is run for `windowMs`. */
35
+ readonly open?: {
36
+ readonly ipc: boolean;
37
+ readonly ipcUnavailable?: string;
38
+ };
39
+ readonly windowMs: number;
40
+ readonly ipc?: IpcProbe;
41
+ readonly sleep?: (ms: number) => Promise<void>;
42
+ readonly memoryReader?: OsMemoryReader;
43
+ readonly hostStartTime?: HostStartTimeReader;
44
+ /** The renderer grant alone; defaults to `allowRenderer`. */
45
+ readonly allowStartup?: boolean;
46
+ }
47
+ export interface SessionMeasurement {
48
+ readonly measurement: Measurement;
49
+ readonly electronVersion?: string;
50
+ readonly platform: string;
51
+ }
52
+ /** Close the window (running one first when none is open) and sample memory and startup. */
53
+ export declare function measureSession(session: EvaluatingSession, options: MeasureOptions): Promise<SessionMeasurement>;
54
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAA;AAWpB,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AAE/D,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CAC5F;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,eAAO,MAAM,aAAa,qFAC0D,CAAA;AACpF,eAAO,MAAM,iBAAiB,gFACiD,CAAA;AAI/E;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE;IACP,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B,GACA,OAAO,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBpD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,iGAAiG;IACjG,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,QAAQ,CAAC,YAAY,CAAC,EAAE,cAAc,CAAA;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,mBAAmB,CAAA;IAC5C,6DAA6D;IAC7D,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAChC;AA2BD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,4FAA4F;AAC5F,wBAAsB,cAAc,CAClC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAyG7B"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Measure one live app session through the transport's `evaluate` seam.
3
+ *
4
+ * @module
5
+ */
6
+ import { validateEvalContent } from '@electron-stagewright/core';
7
+ import { measureMemory, osMemoryReader, hostStartTimeReader, } from './memory.js';
8
+ import { DEFAULT_IPC_INTERVAL_MS, IPC_READ_BODY, MAIN_BODY, buildIpcStartBody, } from './probe.js';
9
+ import { summarizeLatencies } from './run.js';
10
+ export const MAIN_EVAL_OFF = 'main eval is off; start the server with --allow-eval=main (or bare --allow-eval)';
11
+ export const RENDERER_EVAL_OFF = 'renderer eval is off; start the server with bare --allow-eval to measure it';
12
+ const message = (error) => (error instanceof Error ? error.message : String(error));
13
+ /**
14
+ * Open a measurement window: arm the main-thread sampler and, when a probe is given and renderer
15
+ * eval is allowed, the IPC probe loop. Returns why the IPC probe did not start, if it did not.
16
+ */
17
+ export async function startWindow(session, options) {
18
+ await session.evaluate('main', MAIN_BODY, { op: 'start' });
19
+ if (options.ipc === undefined)
20
+ return { ipc: false };
21
+ if (!options.allowRenderer) {
22
+ return { ipc: false, ipcUnavailable: options.rendererOff ?? RENDERER_EVAL_OFF };
23
+ }
24
+ try {
25
+ // The probe is caller-supplied JavaScript: hold it to the same blocklist as the eval tools.
26
+ validateEvalContent(options.ipc.probe, { target: 'renderer' });
27
+ }
28
+ catch (error) {
29
+ return { ipc: false, ipcUnavailable: `the IPC probe was refused: ${message(error)}` };
30
+ }
31
+ try {
32
+ await session.evaluate('renderer', buildIpcStartBody(options.ipc.probe), {
33
+ intervalMs: options.ipc.intervalMs ?? DEFAULT_IPC_INTERVAL_MS,
34
+ });
35
+ return { ipc: true };
36
+ }
37
+ catch (error) {
38
+ return { ipc: false, ipcUnavailable: `the IPC probe did not start: ${message(error)}` };
39
+ }
40
+ }
41
+ /** `ps` start times have whole-second resolution. */
42
+ const START_TIME_TOLERANCE_MS = 2_000;
43
+ /**
44
+ * Pids come from the app, but the OS reader runs on this host. Refuse to read them when the
45
+ * host's process with the main pid did not start when the app's main process did.
46
+ */
47
+ async function hostMismatch(facts, reader) {
48
+ if (!isPid(facts.pid))
49
+ return 'the app reported an invalid main pid';
50
+ if (facts.creationTime === null || reader === undefined)
51
+ return undefined;
52
+ const started = await reader(facts.pid);
53
+ if (started === undefined)
54
+ return undefined;
55
+ if (started !== 'missing' && Math.abs(started - facts.creationTime) <= START_TIME_TOLERANCE_MS) {
56
+ return undefined;
57
+ }
58
+ return `pid ${facts.pid} on this host is not the app's main process (another pid namespace?)`;
59
+ }
60
+ /** App-reported pids reach host commands and /proc paths, so only plain pids pass. */
61
+ const isPid = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
62
+ /** Close the window (running one first when none is open) and sample memory and startup. */
63
+ export async function measureSession(session, options) {
64
+ const unavailable = {};
65
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
66
+ let open = options.open;
67
+ if (open === undefined) {
68
+ open = await startWindow(session, {
69
+ allowRenderer: options.allowRenderer,
70
+ ...(options.rendererOff === undefined ? {} : { rendererOff: options.rendererOff }),
71
+ ...(options.ipc === undefined ? {} : { ipc: options.ipc }),
72
+ });
73
+ await sleep(options.windowMs);
74
+ }
75
+ const stall = await session.evaluate('main', MAIN_BODY, {
76
+ op: 'read',
77
+ stop: true,
78
+ });
79
+ // The app restarted its main process mid-window, or the window outlived MAX_WINDOW_MS.
80
+ if (stall === null)
81
+ unavailable['mainThread'] = 'the measurement window was lost or expired';
82
+ let ipc;
83
+ if (open.ipcUnavailable !== undefined)
84
+ unavailable['ipc'] = open.ipcUnavailable;
85
+ if (open.ipc) {
86
+ try {
87
+ const read = await session.evaluate('renderer', IPC_READ_BODY, {
88
+ stop: true,
89
+ });
90
+ if (read === null) {
91
+ unavailable['ipc'] = 'the IPC probe was lost; the window navigated during the measurement';
92
+ }
93
+ else if (read.samples.length === 0) {
94
+ unavailable['ipc'] =
95
+ read.lastError === null
96
+ ? 'the IPC probe completed no round trip'
97
+ : `the IPC probe failed: ${read.lastError}`;
98
+ }
99
+ else {
100
+ ipc = summarizeLatencies(read.samples);
101
+ }
102
+ }
103
+ catch (error) {
104
+ unavailable['ipc'] = `could not read the IPC probe: ${message(error)}`;
105
+ }
106
+ }
107
+ const facts = await session.evaluate('main', MAIN_BODY, { op: 'facts' });
108
+ let reader = options.memoryReader ?? osMemoryReader(facts.platform);
109
+ const mismatch = reader === undefined
110
+ ? undefined
111
+ : await hostMismatch(facts, options.hostStartTime ?? hostStartTimeReader(facts.platform));
112
+ if (mismatch !== undefined) {
113
+ reader = () => Promise.reject(new Error(mismatch));
114
+ }
115
+ const { memory, error: memoryError } = await measureMemory(facts.processes.filter((entry) => isPid(entry.pid)), facts.platform, reader);
116
+ if (memoryError !== undefined)
117
+ unavailable['memory'] = memoryError;
118
+ let startup;
119
+ if (options.allowStartup === false) {
120
+ unavailable['startup'] = RENDERER_EVAL_OFF;
121
+ }
122
+ else if (options.allowStartup === undefined && !options.allowRenderer) {
123
+ unavailable['startup'] = options.rendererOff ?? RENDERER_EVAL_OFF;
124
+ }
125
+ else if (facts.creationTime === null) {
126
+ unavailable['startup'] = 'this Electron does not report the process creation time';
127
+ }
128
+ else {
129
+ try {
130
+ // Read from main so the first window counts, not whichever renderer is selected.
131
+ const timing = await session.evaluate('main', MAIN_BODY, {
132
+ op: 'startup',
133
+ });
134
+ if ('error' in timing)
135
+ throw new Error(timing.error);
136
+ const firstWindowMs = timing.timeOrigin + timing.loadEventEnd - facts.creationTime;
137
+ // A later document means the first one was left; pushState keeps the document.
138
+ const navigated = timing.firstUrl !== null ? timing.url !== timing.firstUrl : (timing.historyLength ?? 1) > 1;
139
+ if (timing.type !== 'navigate' || navigated) {
140
+ unavailable['startup'] = 'the first window was reloaded or navigated since launch';
141
+ }
142
+ else if (!(timing.loadEventEnd > 0) ||
143
+ !(firstWindowMs > 0) ||
144
+ !Number.isFinite(firstWindowMs)) {
145
+ unavailable['startup'] = 'the window has not finished loading';
146
+ }
147
+ else {
148
+ startup = { firstWindowMs: Math.round(firstWindowMs) };
149
+ }
150
+ }
151
+ catch (error) {
152
+ unavailable['startup'] = `could not read navigation timing: ${message(error)}`;
153
+ }
154
+ }
155
+ return {
156
+ measurement: {
157
+ ...(startup === undefined ? {} : { startup }),
158
+ memory,
159
+ ...(stall === null ? {} : { mainThread: stall }),
160
+ ...(ipc === undefined ? {} : { ipc }),
161
+ ...(Object.keys(unavailable).length === 0 ? {} : { unavailable }),
162
+ },
163
+ ...(facts.electron === null ? {} : { electronVersion: facts.electron }),
164
+ platform: facts.platform,
165
+ };
166
+ }
167
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE,OAAO,EACL,aAAa,EACb,cAAc,EACd,mBAAmB,GAGpB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,SAAS,EACT,iBAAiB,GAKlB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,kBAAkB,EAAoB,MAAM,UAAU,CAAA;AAY/D,MAAM,CAAC,MAAM,aAAa,GACxB,kFAAkF,CAAA;AACpF,MAAM,CAAC,MAAM,iBAAiB,GAC5B,6EAA6E,CAAA;AAE/E,MAAM,OAAO,GAAG,CAAC,KAAc,EAAU,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;AAEpG;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAA0B,EAC1B,OAKC;IAED,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;IAC1D,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;IACpD,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC3B,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,CAAC,WAAW,IAAI,iBAAiB,EAAE,CAAA;IACjF,CAAC;IACD,IAAI,CAAC;QACH,4FAA4F;QAC5F,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,8BAA8B,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAA;IACvF,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;YACvE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,uBAAuB;SAC9D,CAAC,CAAA;QACF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,gCAAgC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAA;IACzF,CAAC;AACH,CAAC;AAgBD,qDAAqD;AACrD,MAAM,uBAAuB,GAAG,KAAK,CAAA;AAErC;;;GAGG;AACH,KAAK,UAAU,YAAY,CACzB,KAAgB,EAChB,MAAuC;IAEvC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,sCAAsC,CAAA;IACpE,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzE,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACvC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC3C,IAAI,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,uBAAuB,EAAE,CAAC;QAC/F,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,OAAO,KAAK,CAAC,GAAG,sEAAsE,CAAA;AAC/F,CAAC;AAED,sFAAsF;AACtF,MAAM,KAAK,GAAG,CAAC,KAAc,EAAmB,EAAE,CAChD,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAA;AAQvE,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAA0B,EAC1B,OAAuB;IAEvB,MAAM,WAAW,GAA2B,EAAE,CAAA;IAC9C,MAAM,KAAK,GACT,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;IAE5F,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IACvB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE;YAChC,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;YAClF,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;SAC3D,CAAC,CAAA;QACF,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC/B,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAiB,MAAM,EAAE,SAAS,EAAE;QACtE,EAAE,EAAE,MAAM;QACV,IAAI,EAAE,IAAI;KACX,CAAC,CAAA;IACF,uFAAuF;IACvF,IAAI,KAAK,KAAK,IAAI;QAAE,WAAW,CAAC,YAAY,CAAC,GAAG,4CAA4C,CAAA;IAE5F,IAAI,GAAuB,CAAA;IAC3B,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS;QAAE,WAAW,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,cAAc,CAAA;IAC/E,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAuB,UAAU,EAAE,aAAa,EAAE;gBACnF,IAAI,EAAE,IAAI;aACX,CAAC,CAAA;YACF,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,WAAW,CAAC,KAAK,CAAC,GAAG,qEAAqE,CAAA;YAC5F,CAAC;iBAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACrC,WAAW,CAAC,KAAK,CAAC;oBAChB,IAAI,CAAC,SAAS,KAAK,IAAI;wBACrB,CAAC,CAAC,uCAAuC;wBACzC,CAAC,CAAC,yBAAyB,IAAI,CAAC,SAAS,EAAE,CAAA;YACjD,CAAC;iBAAM,CAAC;gBACN,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACxC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,CAAC,GAAG,iCAAiC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAA;QACxE,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAY,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;IACnF,IAAI,MAAM,GAAG,OAAO,CAAC,YAAY,IAAI,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IACnE,MAAM,QAAQ,GACZ,MAAM,KAAK,SAAS;QAClB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,MAAM,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,aAAa,IAAI,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC7F,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IACpD,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,aAAa,CACxD,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EACnD,KAAK,CAAC,QAAQ,EACd,MAAM,CACP,CAAA;IACD,IAAI,WAAW,KAAK,SAAS;QAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,WAAW,CAAA;IAElE,IAAI,OAA+B,CAAA;IACnC,IAAI,OAAO,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;QACnC,WAAW,CAAC,SAAS,CAAC,GAAG,iBAAiB,CAAA;IAC5C,CAAC;SAAM,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACxE,WAAW,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,WAAW,IAAI,iBAAiB,CAAA;IACnE,CAAC;SAAM,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QACvC,WAAW,CAAC,SAAS,CAAC,GAAG,yDAAyD,CAAA;IACpF,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,iFAAiF;YACjF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAqB,MAAM,EAAE,SAAS,EAAE;gBAC3E,EAAE,EAAE,SAAS;aACd,CAAC,CAAA;YACF,IAAI,OAAO,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACpD,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAA;YAClF,+EAA+E;YAC/E,MAAM,SAAS,GACb,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;YAC7F,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,SAAS,EAAE,CAAC;gBAC5C,WAAW,CAAC,SAAS,CAAC,GAAG,yDAAyD,CAAA;YACpF,CAAC;iBAAM,IACL,CAAC,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;gBAC1B,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC;gBACpB,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,EAC/B,CAAC;gBACD,WAAW,CAAC,SAAS,CAAC,GAAG,qCAAqC,CAAA;YAChE,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,EAAE,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAA;YACxD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,SAAS,CAAC,GAAG,qCAAqC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAA;QAChF,CAAC;IACH,CAAC;IAED,OAAO;QACL,WAAW,EAAE;YACX,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;YAC7C,MAAM;YACN,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;YAChD,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;YACrC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;SAClE;QACD,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QACvE,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@electron-stagewright/plugin-budget",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Performance budget plugin for Electron Stagewright: measure startup, per-process memory, main-thread blocking, IPC latency, and bundle size, and gate them against a budget file from an agent or CI.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "johnny4young <johnny.iv.young@gmail.com>",
8
+ "homepage": "https://electron-stagewright.github.io/electron-stagewright/",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/electron-stagewright/electron-stagewright.git",
12
+ "directory": "packages/plugin-budget"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/electron-stagewright/electron-stagewright/issues"
16
+ },
17
+ "keywords": [
18
+ "electron",
19
+ "mcp",
20
+ "stagewright",
21
+ "performance",
22
+ "performance-budget",
23
+ "memory",
24
+ "ai-agent"
25
+ ],
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "bin": {
29
+ "electron-stagewright-budget": "./dist/cli.js"
30
+ },
31
+ "files": [
32
+ "dist/",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js"
40
+ },
41
+ "./cli": {
42
+ "types": "./dist/cli.d.ts",
43
+ "import": "./dist/cli.js"
44
+ }
45
+ },
46
+ "dependencies": {
47
+ "zod": "^4.4.3",
48
+ "@electron-stagewright/core": "0.9.0"
49
+ },
50
+ "engines": {
51
+ "node": ">=24.0.0"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "devDependencies": {
57
+ "@electron-stagewright/testkit": "0.0.0"
58
+ },
59
+ "scripts": {
60
+ "build": "tsc -p tsconfig.json",
61
+ "typecheck": "tsc -p tsconfig.json --noEmit"
62
+ }
6
63
  }