@electron-stagewright/plugin-budget 0.0.0-stage → 0.2.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 +214 -2
- package/dist/budget.d.ts +143 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +134 -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-api.d.ts +15 -0
- package/dist/check-api.d.ts.map +1 -0
- package/dist/check-api.js +11 -0
- package/dist/check-api.js.map +1 -0
- package/dist/check.d.ts +50 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +198 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +85 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +464 -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 +81 -0
- package/dist/measure.d.ts.map +1 -0
- package/dist/measure.js +269 -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 +74 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +181 -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 +267 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +257 -0
- package/dist/run.js.map +1 -0
- package/dist/session.d.ts +79 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +263 -0
- package/dist/session.js.map +1 -0
- package/package.json +67 -3
package/dist/cli.js
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `electron-stagewright-budget`: the CI face of the budget plugin.
|
|
4
|
+
*
|
|
5
|
+
* Exit codes: 0 within budget, 1 over budget or nothing measured, 2 usage, unreadable input, or any
|
|
6
|
+
* other error.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { realpathSync } from 'node:fs';
|
|
11
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { pathToFileURL } from 'node:url';
|
|
14
|
+
import { BudgetFileError, PHASES, loadBudget } from './budget.js';
|
|
15
|
+
import { measureBundle } from './bundle.js';
|
|
16
|
+
import { checkRun } from './check.js';
|
|
17
|
+
import { DEFAULT_ROUNDS, measureApp, } from './measure.js';
|
|
18
|
+
import { compareRuns, renderCheckReport, renderComparison } from './render.js';
|
|
19
|
+
import { RunFileError, createRun, loadRun, mergeRuns } from './run.js';
|
|
20
|
+
export const BUDGET_EXIT_CODES = { SUCCESS: 0, OVER_BUDGET: 1, USAGE: 2 };
|
|
21
|
+
const PROCESS_IO = {
|
|
22
|
+
stdout: (text) => process.stdout.write(text),
|
|
23
|
+
stderr: (text) => process.stderr.write(text),
|
|
24
|
+
};
|
|
25
|
+
class UsageError extends Error {
|
|
26
|
+
}
|
|
27
|
+
export function formatBudgetCliHelp() {
|
|
28
|
+
return [
|
|
29
|
+
'Usage:',
|
|
30
|
+
' electron-stagewright-budget measure (--main <main.js> | --executable <path>) [--arg <v>]...',
|
|
31
|
+
' [--env K=V]... [--budget <file>] [--rounds 3]',
|
|
32
|
+
' [--phases cold,warm] [--settle-ms 3000] [--window-ms 2000]',
|
|
33
|
+
' [--workload <script.mjs> [--workload-timeout-ms 120000]]',
|
|
34
|
+
' [--before-round <script.mjs>] [--skip-failed-rounds]',
|
|
35
|
+
' [--memory-sample-ms 1000]',
|
|
36
|
+
' [--label <text>] [--out <run.json>] [--json]',
|
|
37
|
+
' [--check [--no-tolerance] [--require-measurement]',
|
|
38
|
+
' [--phase cold|warm] [--min-samples N]]',
|
|
39
|
+
' electron-stagewright-budget check <run.json>... --budget <file> [--no-tolerance]',
|
|
40
|
+
' [--require-measurement] [--phase cold|warm|live]',
|
|
41
|
+
' [--min-samples N] [--json]',
|
|
42
|
+
' electron-stagewright-budget compare <before.json> <after.json> [--json]',
|
|
43
|
+
' electron-stagewright-budget bundle --budget <file> [--root <dir>] [--out <run.json>]',
|
|
44
|
+
' [--no-tolerance] [--json]',
|
|
45
|
+
'',
|
|
46
|
+
'measure Launch the app on a throwaway profile and measure cold and warm rounds.',
|
|
47
|
+
' --workload is an ES module whose default export, async ({ dispatch, sessionId,',
|
|
48
|
+
' phase }), drives the app inside the measurement window. Without --out or',
|
|
49
|
+
' --check the run document goes to stdout. --phase reads every metric from',
|
|
50
|
+
' that phase; a metric whose phase was not measured is unmeasured.',
|
|
51
|
+
' --before-round is an ES module whose default export, async ({ round, profile }),',
|
|
52
|
+
' prepares each round and may return { env, args } for its launches.',
|
|
53
|
+
' --memory-sample-ms samples memory during the window for a peak; a budget whose',
|
|
54
|
+
' memory aggregate is peak turns it on.',
|
|
55
|
+
'check Compare measured runs to a budget file; several runs are merged first.',
|
|
56
|
+
'compare Print a before/after table of two runs.',
|
|
57
|
+
'bundle Measure the bundle entries of a budget and gate them; needs no Electron.',
|
|
58
|
+
'',
|
|
59
|
+
'--no-tolerance Ignore thresholdPercent; the limit itself gates.',
|
|
60
|
+
' (--strict is a deprecated alias.)',
|
|
61
|
+
'--require-measurement Fail when a budgeted metric was not measured.',
|
|
62
|
+
'--min-samples N A runtime metric needs N measured rounds, or it is unmeasured.',
|
|
63
|
+
'',
|
|
64
|
+
'Exit codes: 0 within budget, 1 over budget or nothing measured,',
|
|
65
|
+
' 2 usage, unreadable input, or any other error.',
|
|
66
|
+
].join('\n');
|
|
67
|
+
}
|
|
68
|
+
function positiveInt(flag, value, min = 1) {
|
|
69
|
+
const parsed = Number(value);
|
|
70
|
+
if (!Number.isSafeInteger(parsed) || parsed < min) {
|
|
71
|
+
throw new UsageError(`${flag} expects an integer of at least ${min}`);
|
|
72
|
+
}
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
function parseMeasureArgs(rest) {
|
|
76
|
+
const single = new Map();
|
|
77
|
+
const args = [];
|
|
78
|
+
const env = {};
|
|
79
|
+
const flags = new Set();
|
|
80
|
+
const valueFlags = [
|
|
81
|
+
'--main',
|
|
82
|
+
'--executable',
|
|
83
|
+
'--budget',
|
|
84
|
+
'--rounds',
|
|
85
|
+
'--phases',
|
|
86
|
+
'--settle-ms',
|
|
87
|
+
'--window-ms',
|
|
88
|
+
'--workload',
|
|
89
|
+
'--workload-timeout-ms',
|
|
90
|
+
'--before-round',
|
|
91
|
+
'--memory-sample-ms',
|
|
92
|
+
'--min-samples',
|
|
93
|
+
'--phase',
|
|
94
|
+
'--label',
|
|
95
|
+
'--out',
|
|
96
|
+
];
|
|
97
|
+
const booleanFlags = [
|
|
98
|
+
'--check',
|
|
99
|
+
'--no-tolerance',
|
|
100
|
+
'--strict',
|
|
101
|
+
'--require-measurement',
|
|
102
|
+
'--skip-failed-rounds',
|
|
103
|
+
'--json',
|
|
104
|
+
];
|
|
105
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
106
|
+
const token = rest[index];
|
|
107
|
+
// Scanned here, not over all of argv, so `--arg --help` is forwarded to the app.
|
|
108
|
+
if (token === '--help' || token === '-h')
|
|
109
|
+
return { command: 'help' };
|
|
110
|
+
if (booleanFlags.includes(token)) {
|
|
111
|
+
flags.add(token);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (token !== '--arg' && token !== '--env' && !valueFlags.includes(token)) {
|
|
115
|
+
throw new UsageError(`Unknown option for measure: ${token}`);
|
|
116
|
+
}
|
|
117
|
+
const value = rest[index + 1];
|
|
118
|
+
// --arg forwards anything, including empty values and values that start with dashes.
|
|
119
|
+
if (value === undefined ||
|
|
120
|
+
(token !== '--arg' && (value.length === 0 || value.startsWith('--')))) {
|
|
121
|
+
throw new UsageError(`${token} expects a value`);
|
|
122
|
+
}
|
|
123
|
+
index += 1;
|
|
124
|
+
if (token === '--arg') {
|
|
125
|
+
args.push(value);
|
|
126
|
+
}
|
|
127
|
+
else if (token === '--env') {
|
|
128
|
+
const separator = value.indexOf('=');
|
|
129
|
+
if (separator <= 0)
|
|
130
|
+
throw new UsageError('--env expects KEY=VALUE');
|
|
131
|
+
env[value.slice(0, separator)] = value.slice(separator + 1);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
if (single.has(token))
|
|
135
|
+
throw new UsageError(`${token} may be given only once`);
|
|
136
|
+
single.set(token, value);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const main = single.get('--main');
|
|
140
|
+
const executablePath = single.get('--executable');
|
|
141
|
+
if (main === undefined && executablePath === undefined) {
|
|
142
|
+
throw new UsageError('measure requires --main <main.js> or --executable <path>');
|
|
143
|
+
}
|
|
144
|
+
const phases = [
|
|
145
|
+
...new Set((single.get('--phases') ?? 'cold,warm').split(',').map((phase) => phase.trim())),
|
|
146
|
+
];
|
|
147
|
+
if (phases.some((phase) => phase !== 'cold' && phase !== 'warm')) {
|
|
148
|
+
throw new UsageError('--phases expects cold, warm, or cold,warm');
|
|
149
|
+
}
|
|
150
|
+
const phase = single.get('--phase');
|
|
151
|
+
if (phase !== undefined && !phases.includes(phase)) {
|
|
152
|
+
throw new UsageError('--phase must name a measured phase: cold or warm');
|
|
153
|
+
}
|
|
154
|
+
const check = flags.has('--check');
|
|
155
|
+
const budgetPath = single.get('--budget');
|
|
156
|
+
if (check && budgetPath === undefined)
|
|
157
|
+
throw new UsageError('--check requires --budget <file>');
|
|
158
|
+
const noTolerance = flags.has('--no-tolerance') || flags.has('--strict');
|
|
159
|
+
if (!check &&
|
|
160
|
+
(noTolerance || flags.has('--require-measurement') || phase || single.has('--min-samples'))) {
|
|
161
|
+
throw new UsageError('--no-tolerance, --require-measurement, --min-samples, and --phase apply only with --check');
|
|
162
|
+
}
|
|
163
|
+
const optional = (key, value) => value === undefined ? {} : { [key]: value };
|
|
164
|
+
const number = (flag, min) => {
|
|
165
|
+
const value = single.get(flag);
|
|
166
|
+
return value === undefined ? undefined : positiveInt(flag, value, min);
|
|
167
|
+
};
|
|
168
|
+
const rounds = number('--rounds', 1) ?? DEFAULT_ROUNDS;
|
|
169
|
+
const minSamples = number('--min-samples', 1);
|
|
170
|
+
if (minSamples !== undefined && minSamples > rounds) {
|
|
171
|
+
throw new UsageError(`--min-samples ${minSamples} can never be met by --rounds ${rounds}`);
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
command: 'measure',
|
|
175
|
+
...optional('main', main === undefined ? undefined : path.resolve(main)),
|
|
176
|
+
...optional('executablePath', executablePath === undefined ? undefined : path.resolve(executablePath)),
|
|
177
|
+
args,
|
|
178
|
+
env,
|
|
179
|
+
...optional('budgetPath', budgetPath),
|
|
180
|
+
rounds,
|
|
181
|
+
phases: phases,
|
|
182
|
+
...optional('settleMs', number('--settle-ms', 0)),
|
|
183
|
+
...optional('windowMs', number('--window-ms', 250)),
|
|
184
|
+
...optional('workloadPath', single.get('--workload')),
|
|
185
|
+
...optional('workloadTimeoutMs', number('--workload-timeout-ms', 1000)),
|
|
186
|
+
...optional('beforeRoundPath', single.get('--before-round')),
|
|
187
|
+
...optional('memorySampleMs', number('--memory-sample-ms', 100)),
|
|
188
|
+
skipFailedRounds: flags.has('--skip-failed-rounds'),
|
|
189
|
+
...optional('minSamples', minSamples),
|
|
190
|
+
...optional('phase', phase),
|
|
191
|
+
...optional('label', single.get('--label')),
|
|
192
|
+
...optional('outPath', single.get('--out')),
|
|
193
|
+
check,
|
|
194
|
+
noTolerance,
|
|
195
|
+
requireMeasurement: flags.has('--require-measurement'),
|
|
196
|
+
json: flags.has('--json'),
|
|
197
|
+
...(flags.has('--strict') ? { deprecatedStrict: true } : {}),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/** Strict parser: an unknown flag or a missing value is a usage error. */
|
|
201
|
+
export function parseBudgetCliArgs(argv) {
|
|
202
|
+
const [command, ...rest] = argv;
|
|
203
|
+
if (command === undefined || command === 'help' || command === '--help' || command === '-h') {
|
|
204
|
+
if (command === undefined)
|
|
205
|
+
throw new UsageError('Expected a subcommand: measure, check, compare, bundle');
|
|
206
|
+
return { command: 'help' };
|
|
207
|
+
}
|
|
208
|
+
if (command === 'measure')
|
|
209
|
+
return parseMeasureArgs(rest);
|
|
210
|
+
if (command !== 'check' && command !== 'compare' && command !== 'bundle') {
|
|
211
|
+
throw new UsageError(`Unknown subcommand: ${command}`);
|
|
212
|
+
}
|
|
213
|
+
if (rest.includes('--help') || rest.includes('-h'))
|
|
214
|
+
return { command: 'help' };
|
|
215
|
+
const valueFlags = {
|
|
216
|
+
check: ['--budget', '--phase', '--min-samples'],
|
|
217
|
+
compare: [],
|
|
218
|
+
bundle: ['--budget', '--root', '--out'],
|
|
219
|
+
};
|
|
220
|
+
const booleanFlags = {
|
|
221
|
+
check: ['--no-tolerance', '--strict', '--require-measurement', '--json'],
|
|
222
|
+
compare: ['--json'],
|
|
223
|
+
bundle: ['--no-tolerance', '--strict', '--json'],
|
|
224
|
+
};
|
|
225
|
+
const values = new Map();
|
|
226
|
+
const flags = new Set();
|
|
227
|
+
const positional = [];
|
|
228
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
229
|
+
const token = rest[index];
|
|
230
|
+
if (valueFlags[command].includes(token)) {
|
|
231
|
+
const value = rest[index + 1];
|
|
232
|
+
if (value === undefined || value.length === 0 || value.startsWith('--')) {
|
|
233
|
+
throw new UsageError(`${token} expects a value`);
|
|
234
|
+
}
|
|
235
|
+
values.set(token, value);
|
|
236
|
+
index += 1;
|
|
237
|
+
}
|
|
238
|
+
else if (booleanFlags[command].includes(token)) {
|
|
239
|
+
flags.add(token);
|
|
240
|
+
}
|
|
241
|
+
else if (token.startsWith('-')) {
|
|
242
|
+
throw new UsageError(`Unknown option for ${command}: ${token}`);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
positional.push(token);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const requireBudget = () => {
|
|
249
|
+
const budgetPath = values.get('--budget');
|
|
250
|
+
if (budgetPath === undefined)
|
|
251
|
+
throw new UsageError(`${command} requires --budget <file>`);
|
|
252
|
+
return budgetPath;
|
|
253
|
+
};
|
|
254
|
+
const json = flags.has('--json');
|
|
255
|
+
const noTolerance = flags.has('--no-tolerance') || flags.has('--strict');
|
|
256
|
+
const deprecatedStrict = flags.has('--strict') ? { deprecatedStrict: true } : {};
|
|
257
|
+
if (command === 'compare') {
|
|
258
|
+
const [beforePath, afterPath, extra] = positional;
|
|
259
|
+
if (beforePath === undefined || afterPath === undefined || extra !== undefined) {
|
|
260
|
+
throw new UsageError('compare expects exactly two run files');
|
|
261
|
+
}
|
|
262
|
+
return { command, beforePath, afterPath, json };
|
|
263
|
+
}
|
|
264
|
+
if (command === 'bundle') {
|
|
265
|
+
if (positional.length > 0)
|
|
266
|
+
throw new UsageError(`Unexpected argument: ${positional[0]}`);
|
|
267
|
+
const root = values.get('--root');
|
|
268
|
+
const outPath = values.get('--out');
|
|
269
|
+
return {
|
|
270
|
+
command,
|
|
271
|
+
budgetPath: requireBudget(),
|
|
272
|
+
...(root === undefined ? {} : { root }),
|
|
273
|
+
...(outPath === undefined ? {} : { outPath }),
|
|
274
|
+
noTolerance,
|
|
275
|
+
json,
|
|
276
|
+
...deprecatedStrict,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
if (positional.length === 0)
|
|
280
|
+
throw new UsageError('check expects at least one run file');
|
|
281
|
+
if (new Set(positional.map((file) => path.resolve(file))).size !== positional.length) {
|
|
282
|
+
throw new UsageError('check lists the same run file twice');
|
|
283
|
+
}
|
|
284
|
+
const minSamples = values.get('--min-samples');
|
|
285
|
+
const phase = values.get('--phase');
|
|
286
|
+
if (phase !== undefined && !PHASES.includes(phase)) {
|
|
287
|
+
throw new UsageError(`--phase expects one of ${PHASES.join(', ')}`);
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
command,
|
|
291
|
+
runPaths: positional,
|
|
292
|
+
budgetPath: requireBudget(),
|
|
293
|
+
...(minSamples === undefined ? {} : { minSamples: positiveInt('--min-samples', minSamples) }),
|
|
294
|
+
noTolerance,
|
|
295
|
+
requireMeasurement: flags.has('--require-measurement'),
|
|
296
|
+
...(phase === undefined ? {} : { phase: phase }),
|
|
297
|
+
json,
|
|
298
|
+
...deprecatedStrict,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function emitReport(report, json, io) {
|
|
302
|
+
io.stdout(`${json ? JSON.stringify(report) : renderCheckReport(report)}\n`);
|
|
303
|
+
return report.passed ? BUDGET_EXIT_CODES.SUCCESS : BUDGET_EXIT_CODES.OVER_BUDGET;
|
|
304
|
+
}
|
|
305
|
+
async function writeRun(outPath, run) {
|
|
306
|
+
await mkdir(path.dirname(path.resolve(outPath)), { recursive: true });
|
|
307
|
+
await writeFile(outPath, `${JSON.stringify(run, null, 2)}\n`);
|
|
308
|
+
}
|
|
309
|
+
async function loadScript(what, scriptPath) {
|
|
310
|
+
let module;
|
|
311
|
+
try {
|
|
312
|
+
module = (await import(pathToFileURL(path.resolve(scriptPath)).href));
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
throw new UsageError(`Cannot load ${what} ${scriptPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
316
|
+
}
|
|
317
|
+
if (typeof module.default !== 'function') {
|
|
318
|
+
throw new UsageError(`${what} ${scriptPath} must default-export an async function`);
|
|
319
|
+
}
|
|
320
|
+
return module.default;
|
|
321
|
+
}
|
|
322
|
+
async function runMeasure(options, io, deps) {
|
|
323
|
+
const budget = options.budgetPath === undefined ? undefined : await loadBudget(options.budgetPath);
|
|
324
|
+
const workload = options.workloadPath === undefined
|
|
325
|
+
? undefined
|
|
326
|
+
: await loadScript('workload', options.workloadPath);
|
|
327
|
+
const beforeRound = options.beforeRoundPath === undefined
|
|
328
|
+
? undefined
|
|
329
|
+
: await loadScript('before-round script', options.beforeRoundPath);
|
|
330
|
+
let run = await deps.measureApp({
|
|
331
|
+
launch: {
|
|
332
|
+
...(options.main === undefined ? {} : { main: options.main }),
|
|
333
|
+
...(options.executablePath === undefined ? {} : { executablePath: options.executablePath }),
|
|
334
|
+
args: options.args,
|
|
335
|
+
env: options.env,
|
|
336
|
+
},
|
|
337
|
+
rounds: options.rounds,
|
|
338
|
+
phases: options.phases,
|
|
339
|
+
...(options.settleMs === undefined ? {} : { settleMs: options.settleMs }),
|
|
340
|
+
...(options.windowMs === undefined ? {} : { windowMs: options.windowMs }),
|
|
341
|
+
...(budget === undefined ? {} : { budget }),
|
|
342
|
+
...(workload === undefined ? {} : { workload }),
|
|
343
|
+
...(options.workloadTimeoutMs === undefined
|
|
344
|
+
? {}
|
|
345
|
+
: { workloadTimeoutMs: options.workloadTimeoutMs }),
|
|
346
|
+
...(options.label === undefined ? {} : { label: options.label }),
|
|
347
|
+
...(beforeRound === undefined ? {} : { beforeRound }),
|
|
348
|
+
...(options.memorySampleMs === undefined ? {} : { memorySampleMs: options.memorySampleMs }),
|
|
349
|
+
...(options.skipFailedRounds ? { failedRounds: 'skip' } : {}),
|
|
350
|
+
onProgress: (message) => io.stderr(`${message}\n`),
|
|
351
|
+
});
|
|
352
|
+
// A run with no round would later read as "nothing over budget"; never write one.
|
|
353
|
+
if (run.rounds.length === 0) {
|
|
354
|
+
io.stderr('No round produced a measurement; no run was written.\n');
|
|
355
|
+
return BUDGET_EXIT_CODES.OVER_BUDGET;
|
|
356
|
+
}
|
|
357
|
+
if (budget?.bundle !== undefined && options.budgetPath !== undefined) {
|
|
358
|
+
const root = path.dirname(path.resolve(options.budgetPath));
|
|
359
|
+
run = { ...run, bundle: await measureBundle(root, budget.bundle.entries) };
|
|
360
|
+
}
|
|
361
|
+
if (options.outPath !== undefined)
|
|
362
|
+
await writeRun(options.outPath, run);
|
|
363
|
+
if (options.check && budget !== undefined) {
|
|
364
|
+
const report = checkRun(run, budget, {
|
|
365
|
+
ignoreThreshold: options.noTolerance,
|
|
366
|
+
requireMeasurement: options.requireMeasurement,
|
|
367
|
+
...(options.phase === undefined ? {} : { phase: options.phase }),
|
|
368
|
+
...(options.minSamples === undefined ? {} : { minSamples: options.minSamples }),
|
|
369
|
+
});
|
|
370
|
+
return emitReport(report, options.json, io);
|
|
371
|
+
}
|
|
372
|
+
if (options.outPath === undefined)
|
|
373
|
+
io.stdout(`${JSON.stringify(run, null, 2)}\n`);
|
|
374
|
+
else
|
|
375
|
+
io.stderr(`Wrote ${options.outPath}\n`);
|
|
376
|
+
return BUDGET_EXIT_CODES.SUCCESS;
|
|
377
|
+
}
|
|
378
|
+
/** Execute already-parsed options. Throws the typed file errors; the caller maps them to exit 2. */
|
|
379
|
+
export async function runBudgetCli(options, io = PROCESS_IO, deps = { measureApp }) {
|
|
380
|
+
if (options.command === 'help') {
|
|
381
|
+
io.stdout(`${formatBudgetCliHelp()}\n`);
|
|
382
|
+
return BUDGET_EXIT_CODES.SUCCESS;
|
|
383
|
+
}
|
|
384
|
+
if ('deprecatedStrict' in options && options.deprecatedStrict) {
|
|
385
|
+
io.stderr('--strict is deprecated: it drops thresholdPercent and never meant "fail instead of warn". Use --no-tolerance.\n');
|
|
386
|
+
}
|
|
387
|
+
if (options.command === 'measure')
|
|
388
|
+
return runMeasure(options, io, deps);
|
|
389
|
+
if (options.command === 'compare') {
|
|
390
|
+
const rows = compareRuns(await loadRun(options.beforePath), await loadRun(options.afterPath));
|
|
391
|
+
if (rows.length === 0) {
|
|
392
|
+
io.stderr('Neither run holds a measurement; nothing to compare.\n');
|
|
393
|
+
return BUDGET_EXIT_CODES.OVER_BUDGET;
|
|
394
|
+
}
|
|
395
|
+
io.stdout(`${options.json ? JSON.stringify({ rows }) : renderComparison(rows)}\n`);
|
|
396
|
+
return BUDGET_EXIT_CODES.SUCCESS;
|
|
397
|
+
}
|
|
398
|
+
const budget = await loadBudget(options.budgetPath);
|
|
399
|
+
if (options.command === 'check') {
|
|
400
|
+
const runs = await Promise.all(options.runPaths.map((runPath) => loadRun(runPath)));
|
|
401
|
+
let run;
|
|
402
|
+
try {
|
|
403
|
+
run = mergeRuns(runs);
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
throw new UsageError(error instanceof Error ? error.message : String(error));
|
|
407
|
+
}
|
|
408
|
+
const report = checkRun(run, budget, {
|
|
409
|
+
ignoreThreshold: options.noTolerance,
|
|
410
|
+
requireMeasurement: options.requireMeasurement,
|
|
411
|
+
...(options.phase === undefined ? {} : { phase: options.phase }),
|
|
412
|
+
...(options.minSamples === undefined ? {} : { minSamples: options.minSamples }),
|
|
413
|
+
});
|
|
414
|
+
return emitReport(report, options.json, io);
|
|
415
|
+
}
|
|
416
|
+
if (budget.bundle === undefined) {
|
|
417
|
+
throw new UsageError(`${options.budgetPath} has no "bundle" section to measure`);
|
|
418
|
+
}
|
|
419
|
+
const root = path.resolve(options.root ?? path.dirname(options.budgetPath));
|
|
420
|
+
const run = createRun({ rounds: [], bundle: await measureBundle(root, budget.bundle.entries) });
|
|
421
|
+
if (options.outPath !== undefined)
|
|
422
|
+
await writeRun(options.outPath, run);
|
|
423
|
+
const bundleOnly = {
|
|
424
|
+
...(budget.thresholdPercent === undefined ? {} : { thresholdPercent: budget.thresholdPercent }),
|
|
425
|
+
bundle: budget.bundle,
|
|
426
|
+
};
|
|
427
|
+
// Every entry was asked for by name here, so one that matched nothing fails the command.
|
|
428
|
+
const report = checkRun(run, bundleOnly, {
|
|
429
|
+
ignoreThreshold: options.noTolerance,
|
|
430
|
+
requireMeasurement: true,
|
|
431
|
+
});
|
|
432
|
+
return emitReport(report, options.json, io);
|
|
433
|
+
}
|
|
434
|
+
/** Parse and execute; never throws. */
|
|
435
|
+
export async function runBudgetCliCommand(argv, io = PROCESS_IO, deps = { measureApp }) {
|
|
436
|
+
try {
|
|
437
|
+
return await runBudgetCli(parseBudgetCliArgs(argv), io, deps);
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
const known = error instanceof UsageError ||
|
|
441
|
+
error instanceof BudgetFileError ||
|
|
442
|
+
error instanceof RunFileError;
|
|
443
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
444
|
+
io.stderr(`${known ? 'usage' : 'fatal'}: ${message}\n`);
|
|
445
|
+
if (error instanceof UsageError)
|
|
446
|
+
io.stderr(`\n${formatBudgetCliHelp()}\n`);
|
|
447
|
+
// Exit 1 means over budget; a crash must never read as a measured verdict.
|
|
448
|
+
return BUDGET_EXIT_CODES.USAGE;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function isMainEntryPoint(moduleUrl, entryPath) {
|
|
452
|
+
if (entryPath === undefined)
|
|
453
|
+
return false;
|
|
454
|
+
try {
|
|
455
|
+
return moduleUrl === pathToFileURL(realpathSync(entryPath)).href;
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (isMainEntryPoint(import.meta.url, process.argv[1])) {
|
|
462
|
+
process.exitCode = await runBudgetCliCommand(process.argv.slice(2));
|
|
463
|
+
}
|
|
464
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@electron-stagewright/plugin-budget`: performance budgets for Electron apps.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { budgetPlugin } from './plugin.js';
|
|
7
|
+
export default budgetPlugin;
|
|
8
|
+
export { budgetPlugin, createBudgetPlugin } from './plugin.js';
|
|
9
|
+
export { BUDGET_PLATFORMS, BudgetFileError, MEMORY_METRICS, PHASES, PROCESS_TYPES, budgetSchema, loadBudget, parseBudget, } from './budget.js';
|
|
10
|
+
export type { Budget, BudgetPlatform, BundleEntryBudget, MemoryMetric, MemoryPlatformBudget, Phase, ProcessType, } from './budget.js';
|
|
11
|
+
export { measureBundle } from './bundle.js';
|
|
12
|
+
export { checkRun } from './check.js';
|
|
13
|
+
export type { CheckOptions, CheckReport, CheckResult, CheckStatus } from './check.js';
|
|
14
|
+
export { compareRuns, renderCheckReport, renderComparison } from './render.js';
|
|
15
|
+
export type { ComparisonRow } from './render.js';
|
|
16
|
+
export { RUN_FORMAT, RUN_SCHEMA_VERSION, RunFileError, createRun, loadRun, memoryByType, mergeRuns, runSchema, summarizeLatencies, summarizeRun, } from './run.js';
|
|
17
|
+
export type { BudgetRun, BundleEntryMeasurement, LatencySummary, Measurement, MemoryMeasurement, ProcessMemory, StallSummary, SummaryRow, } from './run.js';
|
|
18
|
+
export { classifyProcess, measureMemory, platformMetric } from './memory.js';
|
|
19
|
+
export type { AppProcessMetric, OsMemoryReader } from './memory.js';
|
|
20
|
+
export { measureSession, startWindow } from './session.js';
|
|
21
|
+
export type { EvaluatingSession, IpcProbe, MeasureOptions, SessionMeasurement } from './session.js';
|
|
22
|
+
export { measureApp } from './measure.js';
|
|
23
|
+
export type { BeforeRound, LaunchPhase, MeasureAppOptions, RoundSetup, Workload, WorkloadContext, } from './measure.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAe,YAAY,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,MAAM,EACN,aAAa,EACb,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,KAAK,EACL,WAAW,GACZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACrF,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9E,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,OAAO,EACP,YAAY,EACZ,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,YAAY,GACb,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,SAAS,EACT,sBAAsB,EACtB,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,UAAU,GACX,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5E,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACnE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1D,YAAY,EAAE,iBAAiB,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACnG,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EACV,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,QAAQ,EACR,eAAe,GAChB,MAAM,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@electron-stagewright/plugin-budget`: performance budgets for Electron apps.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { budgetPlugin } from './plugin.js';
|
|
7
|
+
export default budgetPlugin;
|
|
8
|
+
export { budgetPlugin, createBudgetPlugin } from './plugin.js';
|
|
9
|
+
export { BUDGET_PLATFORMS, BudgetFileError, MEMORY_METRICS, PHASES, PROCESS_TYPES, budgetSchema, loadBudget, parseBudget, } from './budget.js';
|
|
10
|
+
export { measureBundle } from './bundle.js';
|
|
11
|
+
export { checkRun } from './check.js';
|
|
12
|
+
export { compareRuns, renderCheckReport, renderComparison } from './render.js';
|
|
13
|
+
export { RUN_FORMAT, RUN_SCHEMA_VERSION, RunFileError, createRun, loadRun, memoryByType, mergeRuns, runSchema, summarizeLatencies, summarizeRun, } from './run.js';
|
|
14
|
+
export { classifyProcess, measureMemory, platformMetric } from './memory.js';
|
|
15
|
+
export { measureSession, startWindow } from './session.js';
|
|
16
|
+
export { measureApp } from './measure.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAe,YAAY,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,MAAM,EACN,aAAa,EACb,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,aAAa,CAAA;AAUpB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE9E,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,OAAO,EACP,YAAY,EACZ,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,YAAY,GACb,MAAM,UAAU,CAAA;AAWjB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5E,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launch an app and measure it over cold and warm rounds on a throwaway profile.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { Budget } from './budget.js';
|
|
7
|
+
import { type BudgetRun, type MemoryMeasurement } from './run.js';
|
|
8
|
+
import { sampleMemory, type MemorySource } from './session.js';
|
|
9
|
+
export type LaunchPhase = 'cold' | 'warm';
|
|
10
|
+
/** What a workload script receives: the tool dispatcher of the server driving the app. */
|
|
11
|
+
export interface WorkloadContext {
|
|
12
|
+
readonly sessionId: string;
|
|
13
|
+
readonly phase: LaunchPhase;
|
|
14
|
+
dispatch(tool: string, args?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
15
|
+
}
|
|
16
|
+
export type Workload = (context: WorkloadContext) => Promise<void>;
|
|
17
|
+
/** What `beforeRound` may add to the launches of one round. */
|
|
18
|
+
export interface RoundSetup {
|
|
19
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
20
|
+
readonly args?: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Runs before each round's first launch, to hand it fresh state (a database copy, a seeded
|
|
24
|
+
* fixture). `profile` is the round's empty user-data directory.
|
|
25
|
+
*/
|
|
26
|
+
export type BeforeRound = (context: {
|
|
27
|
+
readonly round: number;
|
|
28
|
+
readonly profile: string;
|
|
29
|
+
}) => RoundSetup | void | Promise<RoundSetup | void>;
|
|
30
|
+
export interface MeasureAppOptions {
|
|
31
|
+
/** `electron_launch` arguments: `main` and/or `executablePath`, plus `args`, `env`, `cwd`. */
|
|
32
|
+
readonly launch: {
|
|
33
|
+
readonly main?: string;
|
|
34
|
+
readonly executablePath?: string;
|
|
35
|
+
readonly args?: readonly string[];
|
|
36
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
37
|
+
readonly cwd?: string;
|
|
38
|
+
};
|
|
39
|
+
readonly rounds?: number;
|
|
40
|
+
readonly phases?: readonly LaunchPhase[];
|
|
41
|
+
/** Wait after the window loads before the measurement window opens. */
|
|
42
|
+
readonly settleMs?: number;
|
|
43
|
+
readonly windowMs?: number;
|
|
44
|
+
/** Supplies the IPC probe and the default window length. */
|
|
45
|
+
readonly budget?: Budget;
|
|
46
|
+
/** Runs inside the measurement window instead of idling for `windowMs`. */
|
|
47
|
+
readonly workload?: Workload;
|
|
48
|
+
/** A workload still running after this fails the measurement. */
|
|
49
|
+
readonly workloadTimeoutMs?: number;
|
|
50
|
+
readonly label?: string;
|
|
51
|
+
readonly onProgress?: (message: string) => void;
|
|
52
|
+
readonly beforeRound?: BeforeRound;
|
|
53
|
+
/**
|
|
54
|
+
* Sample memory this often during the window and record the highest sample outside the GPU as
|
|
55
|
+
* `memoryPeak`. Defaults to 1000 when the budget's memory block for this platform reads `peak`.
|
|
56
|
+
*/
|
|
57
|
+
readonly memorySampleMs?: number;
|
|
58
|
+
/**
|
|
59
|
+
* `skip` reports a round whose launch or measurement failed and goes on to the next one; pair
|
|
60
|
+
* it with `checkRun`'s `minSamples`. Default `throw`.
|
|
61
|
+
*/
|
|
62
|
+
readonly failedRounds?: 'throw' | 'skip';
|
|
63
|
+
}
|
|
64
|
+
export declare const DEFAULT_ROUNDS = 3;
|
|
65
|
+
export declare const DEFAULT_SETTLE_MS = 3000;
|
|
66
|
+
export declare const DEFAULT_WORKLOAD_TIMEOUT_MS = 120000;
|
|
67
|
+
export declare const DEFAULT_MEMORY_SAMPLE_MS = 1000;
|
|
68
|
+
/**
|
|
69
|
+
* Samples memory until stopped; `peak` keeps the heaviest sample outside the GPU, or returns
|
|
70
|
+
* undefined when no sample during the window succeeded.
|
|
71
|
+
*/
|
|
72
|
+
export declare function startSampler(session: Parameters<typeof sampleMemory>[0], source: MemorySource, intervalMs: number | undefined): {
|
|
73
|
+
stop(): Promise<void>;
|
|
74
|
+
peak(end: MemoryMeasurement | undefined): MemoryMeasurement | undefined;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Measure `rounds` rounds. Each round gets an empty profile: the first launch on it is `cold`,
|
|
78
|
+
* the relaunch `warm`. Asking only for `warm` still does the cold launch, unmeasured.
|
|
79
|
+
*/
|
|
80
|
+
export declare function measureApp(options: MeasureAppOptions): Promise<BudgetRun>;
|
|
81
|
+
//# sourceMappingURL=measure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../src/measure.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,EAAa,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5E,OAAO,EAGL,YAAY,EAGZ,KAAK,YAAY,EAClB,MAAM,cAAc,CAAA;AAErB,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,CAAA;AAEzC,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACzF;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAElE,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB,KAAK,UAAU,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;AAEpD,MAAM,WAAW,iBAAiB;IAChC,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;QACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;QAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;QAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;IACD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;IACxC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAA;IAClC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CACzC;AAED,eAAO,MAAM,cAAc,IAAI,CAAA;AAC/B,eAAO,MAAM,iBAAiB,OAAQ,CAAA;AACtC,eAAO,MAAM,2BAA2B,SAAU,CAAA;AAClD,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAqD7C;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,EAC3C,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B;IACD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,IAAI,CAAC,GAAG,EAAE,iBAAiB,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAA;CACxE,CAsCA;AAkID;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,CA2C/E"}
|