modestbench 0.9.1 → 0.9.2
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/CHANGELOG.md +8 -0
- package/dist/cli/builder.cjs +259 -0
- package/dist/cli/builder.cjs.map +1 -0
- package/dist/cli/builder.d.cts +37 -0
- package/dist/cli/builder.d.cts.map +1 -0
- package/dist/cli/builder.d.ts +37 -0
- package/dist/cli/builder.d.ts.map +1 -0
- package/dist/cli/builder.js +255 -0
- package/dist/cli/builder.js.map +1 -0
- package/dist/cli/commands/baseline.cjs +4 -33
- package/dist/cli/commands/baseline.cjs.map +1 -1
- package/dist/cli/commands/baseline.d.cts.map +1 -1
- package/dist/cli/commands/baseline.d.ts.map +1 -1
- package/dist/cli/commands/baseline.js +2 -31
- package/dist/cli/commands/baseline.js.map +1 -1
- package/dist/cli/commands/history.cjs +2 -14
- package/dist/cli/commands/history.cjs.map +1 -1
- package/dist/cli/commands/history.d.cts.map +1 -1
- package/dist/cli/commands/history.d.ts.map +1 -1
- package/dist/cli/commands/history.js +1 -13
- package/dist/cli/commands/history.js.map +1 -1
- package/dist/cli/context.cjs +60 -0
- package/dist/cli/context.cjs.map +1 -0
- package/dist/cli/context.d.cts +28 -0
- package/dist/cli/context.d.cts.map +1 -0
- package/dist/cli/context.d.ts +28 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/context.js +56 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/handlers.cjs +74 -0
- package/dist/cli/handlers.cjs.map +1 -0
- package/dist/cli/handlers.d.cts +13 -0
- package/dist/cli/handlers.d.cts.map +1 -0
- package/dist/cli/handlers.d.ts +13 -0
- package/dist/cli/handlers.d.ts.map +1 -0
- package/dist/cli/handlers.js +70 -0
- package/dist/cli/handlers.js.map +1 -0
- package/dist/cli/index.cjs +12 -724
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +4 -39
- package/dist/cli/index.d.cts.map +1 -1
- package/dist/cli/index.d.ts +4 -39
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +9 -722
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/parsers/analyze.cjs +54 -0
- package/dist/cli/parsers/analyze.cjs.map +1 -0
- package/dist/cli/parsers/analyze.d.cts +37 -0
- package/dist/cli/parsers/analyze.d.cts.map +1 -0
- package/dist/cli/parsers/analyze.d.ts +37 -0
- package/dist/cli/parsers/analyze.d.ts.map +1 -0
- package/dist/cli/parsers/analyze.js +51 -0
- package/dist/cli/parsers/analyze.js.map +1 -0
- package/dist/cli/parsers/baseline.cjs +75 -0
- package/dist/cli/parsers/baseline.cjs.map +1 -0
- package/dist/cli/parsers/baseline.d.cts +59 -0
- package/dist/cli/parsers/baseline.d.cts.map +1 -0
- package/dist/cli/parsers/baseline.d.ts +59 -0
- package/dist/cli/parsers/baseline.d.ts.map +1 -0
- package/dist/cli/parsers/baseline.js +72 -0
- package/dist/cli/parsers/baseline.js.map +1 -0
- package/dist/cli/parsers/global.cjs +49 -0
- package/dist/cli/parsers/global.cjs.map +1 -0
- package/dist/cli/parsers/global.d.cts +45 -0
- package/dist/cli/parsers/global.d.cts.map +1 -0
- package/dist/cli/parsers/global.d.ts +45 -0
- package/dist/cli/parsers/global.d.ts.map +1 -0
- package/dist/cli/parsers/global.js +46 -0
- package/dist/cli/parsers/global.js.map +1 -0
- package/dist/cli/parsers/history.cjs +138 -0
- package/dist/cli/parsers/history.cjs.map +1 -0
- package/dist/cli/parsers/history.d.cts +108 -0
- package/dist/cli/parsers/history.d.cts.map +1 -0
- package/dist/cli/parsers/history.d.ts +108 -0
- package/dist/cli/parsers/history.d.ts.map +1 -0
- package/dist/cli/parsers/history.js +135 -0
- package/dist/cli/parsers/history.js.map +1 -0
- package/dist/cli/parsers/index.cjs +35 -0
- package/dist/cli/parsers/index.cjs.map +1 -0
- package/dist/cli/parsers/index.d.cts +15 -0
- package/dist/cli/parsers/index.d.cts.map +1 -0
- package/dist/cli/parsers/index.d.ts +15 -0
- package/dist/cli/parsers/index.d.ts.map +1 -0
- package/dist/cli/parsers/index.js +15 -0
- package/dist/cli/parsers/index.js.map +1 -0
- package/dist/cli/parsers/init.cjs +39 -0
- package/dist/cli/parsers/init.cjs.map +1 -0
- package/dist/cli/parsers/init.d.cts +32 -0
- package/dist/cli/parsers/init.d.cts.map +1 -0
- package/dist/cli/parsers/init.d.ts +32 -0
- package/dist/cli/parsers/init.d.ts.map +1 -0
- package/dist/cli/parsers/init.js +36 -0
- package/dist/cli/parsers/init.js.map +1 -0
- package/dist/cli/parsers/run.cjs +99 -0
- package/dist/cli/parsers/run.cjs.map +1 -0
- package/dist/cli/parsers/run.d.cts +62 -0
- package/dist/cli/parsers/run.d.cts.map +1 -0
- package/dist/cli/parsers/run.d.ts +62 -0
- package/dist/cli/parsers/run.d.ts.map +1 -0
- package/dist/cli/parsers/run.js +96 -0
- package/dist/cli/parsers/run.js.map +1 -0
- package/dist/cli/parsers/test.cjs +42 -0
- package/dist/cli/parsers/test.cjs.map +1 -0
- package/dist/cli/parsers/test.d.cts +31 -0
- package/dist/cli/parsers/test.d.cts.map +1 -0
- package/dist/cli/parsers/test.d.ts +31 -0
- package/dist/cli/parsers/test.d.ts.map +1 -0
- package/dist/cli/parsers/test.js +39 -0
- package/dist/cli/parsers/test.js.map +1 -0
- package/dist/cli/theme.cjs +35 -0
- package/dist/cli/theme.cjs.map +1 -0
- package/dist/cli/theme.d.cts +31 -0
- package/dist/cli/theme.d.cts.map +1 -0
- package/dist/cli/theme.d.ts +31 -0
- package/dist/cli/theme.d.ts.map +1 -0
- package/dist/cli/theme.js +32 -0
- package/dist/cli/theme.js.map +1 -0
- package/dist/errors/base.cjs +3 -12
- package/dist/errors/base.cjs.map +1 -1
- package/dist/errors/base.d.cts +0 -7
- package/dist/errors/base.d.cts.map +1 -1
- package/dist/errors/base.d.ts +0 -7
- package/dist/errors/base.d.ts.map +1 -1
- package/dist/errors/base.js +1 -9
- package/dist/errors/base.js.map +1 -1
- package/dist/services/profiler/profile-runner.cjs +11 -0
- package/dist/services/profiler/profile-runner.cjs.map +1 -1
- package/dist/services/profiler/profile-runner.d.cts +2 -0
- package/dist/services/profiler/profile-runner.d.cts.map +1 -1
- package/dist/services/profiler/profile-runner.d.ts +2 -0
- package/dist/services/profiler/profile-runner.d.ts.map +1 -1
- package/dist/services/profiler/profile-runner.js +11 -0
- package/dist/services/profiler/profile-runner.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/builder.ts +387 -0
- package/src/cli/commands/baseline.ts +7 -33
- package/src/cli/commands/history.ts +1 -16
- package/src/cli/context.ts +117 -0
- package/src/cli/handlers.ts +76 -0
- package/src/cli/index.ts +10 -1012
- package/src/cli/parsers/analyze.ts +61 -0
- package/src/cli/parsers/baseline.ts +92 -0
- package/src/cli/parsers/global.ts +51 -0
- package/src/cli/parsers/history.ts +168 -0
- package/src/cli/parsers/index.ts +28 -0
- package/src/cli/parsers/init.ts +45 -0
- package/src/cli/parsers/run.ts +118 -0
- package/src/cli/parsers/test.ts +46 -0
- package/src/cli/theme.ts +33 -0
- package/src/errors/base.ts +1 -10
- package/src/services/profiler/profile-runner.ts +15 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Builder
|
|
3
|
+
*
|
|
4
|
+
* Constructs the CLI using bargs, registering all commands, subcommands, and
|
|
5
|
+
* their handlers.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { bargs } from '@boneskull/bargs';
|
|
11
|
+
|
|
12
|
+
import type { CliContext } from './context.js';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
handleAnalyzeCommand as analyzeCommand,
|
|
16
|
+
type AnalyzeOptions,
|
|
17
|
+
} from './commands/analyze.js';
|
|
18
|
+
import {
|
|
19
|
+
handleAnalyzeCommand as handleBaselineAnalyzeCommand,
|
|
20
|
+
handleDeleteCommand as handleBaselineDeleteCommand,
|
|
21
|
+
handleListCommand as handleBaselineListCommand,
|
|
22
|
+
handleSetCommand as handleBaselineSetCommand,
|
|
23
|
+
handleShowCommand as handleBaselineShowCommand,
|
|
24
|
+
} from './commands/baseline.js';
|
|
25
|
+
import {
|
|
26
|
+
handleCleanCommand,
|
|
27
|
+
handleCompareCommand,
|
|
28
|
+
handleExportCommand,
|
|
29
|
+
handleListCommand,
|
|
30
|
+
handleShowCommand,
|
|
31
|
+
handleTrendsCommand,
|
|
32
|
+
} from './commands/history.js';
|
|
33
|
+
import { handleInitCommand as initCommand } from './commands/init.js';
|
|
34
|
+
import { handleRunCommand as runCommand } from './commands/run.js';
|
|
35
|
+
import {
|
|
36
|
+
handleTestCommand as testCommand,
|
|
37
|
+
type TestOptions,
|
|
38
|
+
} from './commands/test.js';
|
|
39
|
+
import { createCliContext } from './context.js';
|
|
40
|
+
import {
|
|
41
|
+
analyzeParser,
|
|
42
|
+
baselineAnalyzeParser,
|
|
43
|
+
baselineDeleteParser,
|
|
44
|
+
baselineListParser,
|
|
45
|
+
baselineSetParser,
|
|
46
|
+
baselineShowParser,
|
|
47
|
+
globalOptions,
|
|
48
|
+
historyCleanParser,
|
|
49
|
+
historyCompareParser,
|
|
50
|
+
historyExportParser,
|
|
51
|
+
historyListParser,
|
|
52
|
+
historyShowParser,
|
|
53
|
+
historyTrendsParser,
|
|
54
|
+
initParser,
|
|
55
|
+
quietOption,
|
|
56
|
+
runParser,
|
|
57
|
+
testParser,
|
|
58
|
+
} from './parsers/index.js';
|
|
59
|
+
import { synthwaveTheme } from './theme.js';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create the CLI builder with all commands registered
|
|
63
|
+
*
|
|
64
|
+
* @param abortController - Controller for aborting benchmark runs
|
|
65
|
+
* @returns Configured bargs CLI builder
|
|
66
|
+
*/
|
|
67
|
+
export const createCli = (abortController: AbortController) => {
|
|
68
|
+
return bargs('modestbench', {
|
|
69
|
+
description: 'A modern benchmark runner for Node.js',
|
|
70
|
+
theme: synthwaveTheme,
|
|
71
|
+
})
|
|
72
|
+
.globals(globalOptions)
|
|
73
|
+
.command(
|
|
74
|
+
'run',
|
|
75
|
+
runParser,
|
|
76
|
+
async ({ positionals, values }) => {
|
|
77
|
+
const [pattern] = positionals;
|
|
78
|
+
const context = await createCliContext(
|
|
79
|
+
values,
|
|
80
|
+
abortController,
|
|
81
|
+
values.engine,
|
|
82
|
+
);
|
|
83
|
+
const exitCode = await runCommand(context, {
|
|
84
|
+
bail: values.bail,
|
|
85
|
+
config: values.config,
|
|
86
|
+
cwd: values.cwd,
|
|
87
|
+
engine: values.engine,
|
|
88
|
+
exclude: values.exclude,
|
|
89
|
+
excludeTags: values.excludeTag,
|
|
90
|
+
iterations: values.iterations,
|
|
91
|
+
json: values.json,
|
|
92
|
+
jsonPretty: values.jsonPretty,
|
|
93
|
+
noColor: values.noColor,
|
|
94
|
+
outputDir: values.output,
|
|
95
|
+
outputFile: values.outputFile,
|
|
96
|
+
pattern,
|
|
97
|
+
progress: values.progress,
|
|
98
|
+
quiet: values.quiet,
|
|
99
|
+
reporters: values.reporter,
|
|
100
|
+
tags: values.tag,
|
|
101
|
+
time: values.time,
|
|
102
|
+
timeout: values.timeout,
|
|
103
|
+
verbose: values.verbose,
|
|
104
|
+
warmup: values.warmup,
|
|
105
|
+
});
|
|
106
|
+
process.exitCode = exitCode;
|
|
107
|
+
},
|
|
108
|
+
'Run benchmark files',
|
|
109
|
+
)
|
|
110
|
+
.command(
|
|
111
|
+
'history',
|
|
112
|
+
(history) =>
|
|
113
|
+
history
|
|
114
|
+
.globals(quietOption)
|
|
115
|
+
.command(
|
|
116
|
+
'list',
|
|
117
|
+
historyListParser,
|
|
118
|
+
async ({ values }) => {
|
|
119
|
+
const context = await createCliContext(values, abortController);
|
|
120
|
+
const exitCode = await handleListCommand(context, {
|
|
121
|
+
cwd: values.cwd,
|
|
122
|
+
format: values.format,
|
|
123
|
+
limit: values.limit,
|
|
124
|
+
pattern: values.pattern,
|
|
125
|
+
since: values.since,
|
|
126
|
+
tags: values.tag,
|
|
127
|
+
until: values.until,
|
|
128
|
+
verbose: values.verbose,
|
|
129
|
+
});
|
|
130
|
+
process.exitCode = exitCode;
|
|
131
|
+
},
|
|
132
|
+
'List recent benchmark runs',
|
|
133
|
+
)
|
|
134
|
+
.command(
|
|
135
|
+
'show',
|
|
136
|
+
historyShowParser,
|
|
137
|
+
async ({ positionals, values }) => {
|
|
138
|
+
const [runId] = positionals;
|
|
139
|
+
const context = await createCliContext(values, abortController);
|
|
140
|
+
const exitCode = await handleShowCommand(context, {
|
|
141
|
+
cwd: values.cwd,
|
|
142
|
+
format: values.format,
|
|
143
|
+
runId,
|
|
144
|
+
verbose: values.verbose,
|
|
145
|
+
});
|
|
146
|
+
process.exitCode = exitCode;
|
|
147
|
+
},
|
|
148
|
+
'Show detailed results for a specific run',
|
|
149
|
+
)
|
|
150
|
+
.command(
|
|
151
|
+
'compare',
|
|
152
|
+
historyCompareParser,
|
|
153
|
+
async ({ positionals, values }) => {
|
|
154
|
+
const [runId1, runId2] = positionals;
|
|
155
|
+
const context = await createCliContext(values, abortController);
|
|
156
|
+
const exitCode = await handleCompareCommand(context, {
|
|
157
|
+
cwd: values.cwd,
|
|
158
|
+
format: values.format,
|
|
159
|
+
runId1,
|
|
160
|
+
runId2,
|
|
161
|
+
verbose: values.verbose,
|
|
162
|
+
});
|
|
163
|
+
process.exitCode = exitCode;
|
|
164
|
+
},
|
|
165
|
+
'Compare two benchmark runs',
|
|
166
|
+
)
|
|
167
|
+
.command(
|
|
168
|
+
'trends',
|
|
169
|
+
historyTrendsParser,
|
|
170
|
+
async ({ positionals, values }) => {
|
|
171
|
+
const [pattern] = positionals;
|
|
172
|
+
const context = await createCliContext(values, abortController);
|
|
173
|
+
const exitCode = await handleTrendsCommand(context, {
|
|
174
|
+
all: values.all,
|
|
175
|
+
cwd: values.cwd,
|
|
176
|
+
format: values.format,
|
|
177
|
+
limit: values.limit,
|
|
178
|
+
pattern,
|
|
179
|
+
since: values.since,
|
|
180
|
+
tags: values.tag,
|
|
181
|
+
until: values.until,
|
|
182
|
+
verbose: values.verbose,
|
|
183
|
+
});
|
|
184
|
+
process.exitCode = exitCode;
|
|
185
|
+
},
|
|
186
|
+
'Show performance trends over time',
|
|
187
|
+
)
|
|
188
|
+
.command(
|
|
189
|
+
'clean',
|
|
190
|
+
historyCleanParser,
|
|
191
|
+
async ({ values }) => {
|
|
192
|
+
const context = await createCliContext(values, abortController);
|
|
193
|
+
const exitCode = await handleCleanCommand(context, {
|
|
194
|
+
confirm: values.yes,
|
|
195
|
+
cwd: values.cwd,
|
|
196
|
+
maxAge: values.maxAge,
|
|
197
|
+
maxRuns: values.maxRuns,
|
|
198
|
+
maxSize: values.maxSize,
|
|
199
|
+
quiet: values.quiet,
|
|
200
|
+
verbose: values.verbose,
|
|
201
|
+
});
|
|
202
|
+
process.exitCode = exitCode;
|
|
203
|
+
},
|
|
204
|
+
'Clean up old benchmark history',
|
|
205
|
+
)
|
|
206
|
+
.command(
|
|
207
|
+
'export',
|
|
208
|
+
historyExportParser,
|
|
209
|
+
async ({ values }) => {
|
|
210
|
+
const context = await createCliContext(values, abortController);
|
|
211
|
+
const exitCode = await handleExportCommand(context, {
|
|
212
|
+
cwd: values.cwd,
|
|
213
|
+
format: values.format,
|
|
214
|
+
outputPath: values.output,
|
|
215
|
+
quiet: Boolean(values.quiet),
|
|
216
|
+
since: values.since,
|
|
217
|
+
until: values.until,
|
|
218
|
+
verbose: values.verbose,
|
|
219
|
+
});
|
|
220
|
+
process.exitCode = exitCode;
|
|
221
|
+
},
|
|
222
|
+
'Export benchmark history to a file',
|
|
223
|
+
),
|
|
224
|
+
'View and manage benchmark history',
|
|
225
|
+
)
|
|
226
|
+
.command(
|
|
227
|
+
'baseline',
|
|
228
|
+
(baseline) =>
|
|
229
|
+
baseline
|
|
230
|
+
.globals(quietOption)
|
|
231
|
+
.command(
|
|
232
|
+
'set',
|
|
233
|
+
baselineSetParser,
|
|
234
|
+
async ({ positionals, values }) => {
|
|
235
|
+
const [name] = positionals;
|
|
236
|
+
const context = await createCliContext(values, abortController);
|
|
237
|
+
const exitCode = await handleBaselineSetCommand(context, {
|
|
238
|
+
branch: values.branch,
|
|
239
|
+
commit: values.commit,
|
|
240
|
+
cwd: values.cwd,
|
|
241
|
+
default: values.default,
|
|
242
|
+
name,
|
|
243
|
+
quiet: Boolean(values.quiet),
|
|
244
|
+
runId: values.runId,
|
|
245
|
+
verbose: values.verbose,
|
|
246
|
+
});
|
|
247
|
+
process.exitCode = exitCode;
|
|
248
|
+
},
|
|
249
|
+
'Save a benchmark run as a baseline',
|
|
250
|
+
)
|
|
251
|
+
.command(
|
|
252
|
+
'list',
|
|
253
|
+
baselineListParser,
|
|
254
|
+
async ({ values }) => {
|
|
255
|
+
const context = await createCliContext(values, abortController);
|
|
256
|
+
const exitCode = await handleBaselineListCommand(context, {
|
|
257
|
+
cwd: values.cwd,
|
|
258
|
+
format: values.format,
|
|
259
|
+
quiet: Boolean(values.quiet),
|
|
260
|
+
verbose: values.verbose,
|
|
261
|
+
});
|
|
262
|
+
process.exitCode = exitCode;
|
|
263
|
+
},
|
|
264
|
+
'List all saved baselines',
|
|
265
|
+
)
|
|
266
|
+
.command(
|
|
267
|
+
'show',
|
|
268
|
+
baselineShowParser,
|
|
269
|
+
async ({ positionals, values }) => {
|
|
270
|
+
const [name] = positionals;
|
|
271
|
+
const context = await createCliContext(values, abortController);
|
|
272
|
+
const exitCode = await handleBaselineShowCommand(context, {
|
|
273
|
+
cwd: values.cwd,
|
|
274
|
+
format: values.format,
|
|
275
|
+
name,
|
|
276
|
+
quiet: Boolean(values.quiet),
|
|
277
|
+
verbose: values.verbose,
|
|
278
|
+
});
|
|
279
|
+
process.exitCode = exitCode;
|
|
280
|
+
},
|
|
281
|
+
'Show baseline details',
|
|
282
|
+
)
|
|
283
|
+
.command(
|
|
284
|
+
'delete',
|
|
285
|
+
baselineDeleteParser,
|
|
286
|
+
async ({ positionals, values }) => {
|
|
287
|
+
const [name] = positionals;
|
|
288
|
+
const context = await createCliContext(values, abortController);
|
|
289
|
+
const exitCode = await handleBaselineDeleteCommand(context, {
|
|
290
|
+
cwd: values.cwd,
|
|
291
|
+
name,
|
|
292
|
+
quiet: Boolean(values.quiet),
|
|
293
|
+
verbose: values.verbose,
|
|
294
|
+
});
|
|
295
|
+
process.exitCode = exitCode;
|
|
296
|
+
},
|
|
297
|
+
'Delete a baseline',
|
|
298
|
+
)
|
|
299
|
+
.command(
|
|
300
|
+
'analyze',
|
|
301
|
+
baselineAnalyzeParser,
|
|
302
|
+
async ({ values }) => {
|
|
303
|
+
const context = await createCliContext(values, abortController);
|
|
304
|
+
const exitCode = await handleBaselineAnalyzeCommand(context, {
|
|
305
|
+
confidence: values.confidence,
|
|
306
|
+
cwd: values.cwd,
|
|
307
|
+
quiet: Boolean(values.quiet),
|
|
308
|
+
runs: values.runs,
|
|
309
|
+
verbose: values.verbose,
|
|
310
|
+
});
|
|
311
|
+
process.exitCode = exitCode;
|
|
312
|
+
},
|
|
313
|
+
'Analyze history and suggest performance budgets',
|
|
314
|
+
),
|
|
315
|
+
'Manage performance baselines',
|
|
316
|
+
)
|
|
317
|
+
.command(
|
|
318
|
+
'init',
|
|
319
|
+
initParser,
|
|
320
|
+
async ({ positionals, values }) => {
|
|
321
|
+
const [type] = positionals;
|
|
322
|
+
const context = await createCliContext(values, abortController);
|
|
323
|
+
const exitCode = await initCommand(context, {
|
|
324
|
+
configType: values.configType,
|
|
325
|
+
cwd: values.cwd,
|
|
326
|
+
examples: values.examples,
|
|
327
|
+
force: values.force,
|
|
328
|
+
quiet: values.quiet,
|
|
329
|
+
type,
|
|
330
|
+
verbose: values.verbose,
|
|
331
|
+
yes: values.yes,
|
|
332
|
+
});
|
|
333
|
+
process.exitCode = exitCode;
|
|
334
|
+
},
|
|
335
|
+
'Initialize a new benchmark project',
|
|
336
|
+
)
|
|
337
|
+
.command(
|
|
338
|
+
'analyze',
|
|
339
|
+
analyzeParser,
|
|
340
|
+
async ({ positionals, values }) => {
|
|
341
|
+
const [command] = positionals;
|
|
342
|
+
// Context not needed for analyze command currently
|
|
343
|
+
const context = {} as CliContext;
|
|
344
|
+
|
|
345
|
+
const options: AnalyzeOptions = {
|
|
346
|
+
color: !values.noColor,
|
|
347
|
+
command,
|
|
348
|
+
cwd: values.cwd || process.cwd(),
|
|
349
|
+
filterFile: values.filterFile,
|
|
350
|
+
groupByFile: values.groupByFile,
|
|
351
|
+
input: values.input,
|
|
352
|
+
minPercent: values.minPercent,
|
|
353
|
+
top: values.top,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
process.exitCode = await analyzeCommand(context, options);
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
aliases: ['profile'],
|
|
360
|
+
description: 'Analyze code execution and identify benchmark candidates',
|
|
361
|
+
},
|
|
362
|
+
)
|
|
363
|
+
.command(
|
|
364
|
+
'test',
|
|
365
|
+
testParser,
|
|
366
|
+
async ({ positionals, values }) => {
|
|
367
|
+
const [framework, files] = positionals;
|
|
368
|
+
const context = await createCliContext(values, abortController);
|
|
369
|
+
const options: TestOptions = {
|
|
370
|
+
bail: values.bail,
|
|
371
|
+
cwd: values.cwd,
|
|
372
|
+
framework,
|
|
373
|
+
iterations: values.iterations,
|
|
374
|
+
json: values.json,
|
|
375
|
+
noColor: values.noColor,
|
|
376
|
+
pattern: files,
|
|
377
|
+
quiet: values.quiet,
|
|
378
|
+
verbose: values.verbose,
|
|
379
|
+
warmup: values.warmup,
|
|
380
|
+
};
|
|
381
|
+
const exitCode = await testCommand(context, options);
|
|
382
|
+
process.exitCode = exitCode;
|
|
383
|
+
},
|
|
384
|
+
'Run test files as benchmarks',
|
|
385
|
+
)
|
|
386
|
+
.defaultCommand('run');
|
|
387
|
+
};
|
|
@@ -11,6 +11,10 @@ import type { CliContext } from '../index.js';
|
|
|
11
11
|
|
|
12
12
|
import { BaselineStorageService } from '../../services/baseline-storage.js';
|
|
13
13
|
import { createTaskId } from '../../types/index.js';
|
|
14
|
+
import {
|
|
15
|
+
formatDuration,
|
|
16
|
+
formatOpsPerSecond,
|
|
17
|
+
} from '../../utils/reporter-utils.js';
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* Options for baseline analyze command
|
|
@@ -62,38 +66,6 @@ interface BaselineShowOptions extends BaselineBaseOptions {
|
|
|
62
66
|
name: string;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
/**
|
|
66
|
-
* Format duration in human-readable format
|
|
67
|
-
*/
|
|
68
|
-
const formatDuration = (nanoseconds: number): string => {
|
|
69
|
-
if (nanoseconds < 1000) {
|
|
70
|
-
return `${nanoseconds.toFixed(2)}ns`;
|
|
71
|
-
}
|
|
72
|
-
if (nanoseconds < 1_000_000) {
|
|
73
|
-
return `${(nanoseconds / 1000).toFixed(2)}μs`;
|
|
74
|
-
}
|
|
75
|
-
if (nanoseconds < 1_000_000_000) {
|
|
76
|
-
return `${(nanoseconds / 1_000_000).toFixed(2)}ms`;
|
|
77
|
-
}
|
|
78
|
-
return `${(nanoseconds / 1_000_000_000).toFixed(2)}s`;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Format operations per second
|
|
83
|
-
*/
|
|
84
|
-
const formatOpsPerSec = (ops: number): string => {
|
|
85
|
-
if (ops < 1000) {
|
|
86
|
-
return `${ops.toFixed(2)} ops/sec`;
|
|
87
|
-
}
|
|
88
|
-
if (ops < 1_000_000) {
|
|
89
|
-
return `${(ops / 1000).toFixed(2)}K ops/sec`;
|
|
90
|
-
}
|
|
91
|
-
if (ops < 1_000_000_000) {
|
|
92
|
-
return `${(ops / 1_000_000).toFixed(2)}M ops/sec`;
|
|
93
|
-
}
|
|
94
|
-
return `${(ops / 1_000_000_000).toFixed(2)}B ops/sec`;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
69
|
/**
|
|
98
70
|
* Format date in readable format
|
|
99
71
|
*/
|
|
@@ -337,7 +309,9 @@ export const handleShowCommand = async (
|
|
|
337
309
|
for (const [taskId, data] of tasks) {
|
|
338
310
|
console.log(` ${taskId}`);
|
|
339
311
|
console.log(` Mean: ${formatDuration(data.mean)}`);
|
|
340
|
-
console.log(
|
|
312
|
+
console.log(
|
|
313
|
+
` Ops/sec: ${formatOpsPerSecond(data.opsPerSecond)}`,
|
|
314
|
+
);
|
|
341
315
|
if (data.p99) {
|
|
342
316
|
console.log(` P99: ${formatDuration(data.p99)}`);
|
|
343
317
|
}
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
parseDate,
|
|
23
23
|
} from '../../services/history/query.js';
|
|
24
24
|
import { TrendAnalysisService } from '../../services/history/trend-analysis.js';
|
|
25
|
+
import { formatBytes } from '../../utils/reporter-utils.js';
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Base options shared by all history subcommands
|
|
@@ -94,22 +95,6 @@ interface HistoryTrendsOptions extends BaseHistoryOptions {
|
|
|
94
95
|
until?: string | undefined;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
/**
|
|
98
|
-
* Format bytes in human-readable format
|
|
99
|
-
*/
|
|
100
|
-
const formatBytes = (bytes: number): string => {
|
|
101
|
-
const units = ['B', 'KB', 'MB', 'GB'];
|
|
102
|
-
let size = bytes;
|
|
103
|
-
let unitIndex = 0;
|
|
104
|
-
|
|
105
|
-
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
106
|
-
size /= 1024;
|
|
107
|
-
unitIndex++;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
98
|
/**
|
|
114
99
|
* Resolve a partial run ID to a full ID by checking prefix match
|
|
115
100
|
*
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Context
|
|
3
|
+
*
|
|
4
|
+
* Provides dependency injection container for CLI commands, including
|
|
5
|
+
* configuration manager, benchmark engine, history storage, and reporters.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { InferParserValues } from '@boneskull/bargs';
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
BenchmarkEngine,
|
|
14
|
+
ConfigurationManager,
|
|
15
|
+
Engine,
|
|
16
|
+
HistoryStorage,
|
|
17
|
+
ProgressManager,
|
|
18
|
+
ReporterRegistry,
|
|
19
|
+
} from '../types/index.js';
|
|
20
|
+
import type { globalOptions } from './parsers/global.js';
|
|
21
|
+
|
|
22
|
+
import { bootstrap } from '../bootstrap.js';
|
|
23
|
+
import { DEFAULT_ENGINE, Engines, ExitCodes, Reporters } from '../constants.js';
|
|
24
|
+
import { AccurateEngine, TinybenchEngine } from '../core/engines/index.js';
|
|
25
|
+
import {
|
|
26
|
+
CsvReporter,
|
|
27
|
+
HumanReporter,
|
|
28
|
+
JsonReporter,
|
|
29
|
+
NyanReporter,
|
|
30
|
+
SimpleReporter,
|
|
31
|
+
} from '../reporters/index.js';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* CLI context with initialized services
|
|
35
|
+
*/
|
|
36
|
+
export interface CliContext {
|
|
37
|
+
readonly abortController: AbortController;
|
|
38
|
+
readonly configManager: ConfigurationManager;
|
|
39
|
+
readonly engine: BenchmarkEngine;
|
|
40
|
+
readonly historyStorage: HistoryStorage;
|
|
41
|
+
readonly options: InferParserValues<typeof globalOptions>;
|
|
42
|
+
readonly progressManager: ProgressManager;
|
|
43
|
+
readonly reporterRegistry: ReporterRegistry;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create CLI context with dependency injection
|
|
48
|
+
*/
|
|
49
|
+
export const createCliContext = async (
|
|
50
|
+
options: InferParserValues<typeof globalOptions>,
|
|
51
|
+
abortController: AbortController,
|
|
52
|
+
engineType: Engine = DEFAULT_ENGINE,
|
|
53
|
+
): Promise<CliContext> => {
|
|
54
|
+
try {
|
|
55
|
+
const dependencies = bootstrap();
|
|
56
|
+
|
|
57
|
+
// Select engine based on type
|
|
58
|
+
const engine =
|
|
59
|
+
engineType === Engines.ACCURATE
|
|
60
|
+
? new AccurateEngine(dependencies)
|
|
61
|
+
: new TinybenchEngine(dependencies);
|
|
62
|
+
|
|
63
|
+
// Register built-in reporters
|
|
64
|
+
engine.registerReporter(
|
|
65
|
+
Reporters.HUMAN,
|
|
66
|
+
new HumanReporter({
|
|
67
|
+
color: !options.noColor,
|
|
68
|
+
verbose: options.verbose,
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
engine.registerReporter(
|
|
73
|
+
'json',
|
|
74
|
+
new JsonReporter({
|
|
75
|
+
prettyPrint: false,
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
engine.registerReporter(
|
|
80
|
+
'csv',
|
|
81
|
+
new CsvReporter({
|
|
82
|
+
includeHeaders: true,
|
|
83
|
+
includeMetadata: true,
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
engine.registerReporter(
|
|
88
|
+
'simple',
|
|
89
|
+
new SimpleReporter({
|
|
90
|
+
verbose: options.verbose,
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
engine.registerReporter(
|
|
95
|
+
'nyan',
|
|
96
|
+
new NyanReporter({
|
|
97
|
+
color: !options.noColor,
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
abortController,
|
|
103
|
+
configManager: engine.configManager,
|
|
104
|
+
engine,
|
|
105
|
+
historyStorage: engine.historyStorage,
|
|
106
|
+
options,
|
|
107
|
+
progressManager: engine.progressManager,
|
|
108
|
+
reporterRegistry: engine.reporterRegistry,
|
|
109
|
+
};
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(
|
|
112
|
+
'Failed to initialize ModestBench:',
|
|
113
|
+
error instanceof Error ? error.message : String(error),
|
|
114
|
+
);
|
|
115
|
+
process.exit(ExitCodes.CONFIG_ERROR);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Signal Handlers
|
|
3
|
+
*
|
|
4
|
+
* Handles process signals (SIGINT, SIGTERM, etc.) and uncaught errors for
|
|
5
|
+
* graceful shutdown of benchmark runs.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ABORT_TIMEOUT, ExitCodes } from '../constants.js';
|
|
11
|
+
import { isModestBenchError, UnknownError } from '../errors/index.js';
|
|
12
|
+
import { isError } from '../utils/type-guards.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Handle process signals gracefully
|
|
16
|
+
*/
|
|
17
|
+
export const setupSignalHandlers = (abortController: AbortController): void => {
|
|
18
|
+
let abortRequested = false;
|
|
19
|
+
|
|
20
|
+
const handleSignal = (signal: NodeJS.Signals) => {
|
|
21
|
+
if (abortRequested) {
|
|
22
|
+
// Second signal, force exit
|
|
23
|
+
console.log(`\nReceived ${signal} again, forcing exit...`);
|
|
24
|
+
process.exit(computeExitCode(signal));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(`\nReceived ${signal}, aborting benchmarks...`);
|
|
28
|
+
abortRequested = true;
|
|
29
|
+
abortController.abort();
|
|
30
|
+
|
|
31
|
+
// Give a short grace period for cleanup, then exit
|
|
32
|
+
setTimeout(() => {
|
|
33
|
+
console.log('\nBenchmark aborted.');
|
|
34
|
+
process.exit(computeExitCode(signal));
|
|
35
|
+
}, ABORT_TIMEOUT);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
process
|
|
39
|
+
.once('SIGINT', handleSignal)
|
|
40
|
+
.once('SIGQUIT', handleSignal)
|
|
41
|
+
.once('SIGTERM', handleSignal)
|
|
42
|
+
.once('uncaughtException', (error) => {
|
|
43
|
+
// Wrap non-ModestBench errors with UnknownError
|
|
44
|
+
const wrappedError: Error = isModestBenchError(error)
|
|
45
|
+
? error
|
|
46
|
+
: new UnknownError(error.message, { cause: error });
|
|
47
|
+
console.error(`${wrappedError}`);
|
|
48
|
+
process.exit(ExitCodes.RUNTIME_ERROR);
|
|
49
|
+
})
|
|
50
|
+
.once('unhandledRejection', (reason) => {
|
|
51
|
+
const wrappedError: Error = isModestBenchError(reason)
|
|
52
|
+
? reason
|
|
53
|
+
: new UnknownError(isError(reason) ? reason.message : String(reason), {
|
|
54
|
+
cause: reason,
|
|
55
|
+
});
|
|
56
|
+
console.error(`${wrappedError}`);
|
|
57
|
+
process.exit(ExitCodes.RUNTIME_ERROR);
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Compute the exit code based on the signal
|
|
63
|
+
*
|
|
64
|
+
* @param signal - The signal that caused the exit
|
|
65
|
+
* @returns The exit code
|
|
66
|
+
*/
|
|
67
|
+
const computeExitCode = (signal: NodeJS.Signals): number => {
|
|
68
|
+
switch (signal) {
|
|
69
|
+
case 'SIGINT':
|
|
70
|
+
return 130; // 128 + 2
|
|
71
|
+
case 'SIGQUIT':
|
|
72
|
+
return 131; // 128 + 3
|
|
73
|
+
default:
|
|
74
|
+
return 143; // 128 + 15 (SIGTERM)
|
|
75
|
+
}
|
|
76
|
+
};
|