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
package/dist/cli/index.js
CHANGED
|
@@ -2,643 +2,20 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* ModestBench CLI Entry Point
|
|
4
4
|
*
|
|
5
|
-
* Command-line interface using bargs for command parsing and routing.
|
|
6
|
-
*
|
|
5
|
+
* Command-line interface using bargs for command parsing and routing. This
|
|
6
|
+
* module provides the main entry points and re-exports key types.
|
|
7
7
|
*
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { BargsError, HelpError } from '@boneskull/bargs';
|
|
11
11
|
import { realpathSync } from 'node:fs';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Import commands
|
|
20
|
-
import { handleAnalyzeCommand as analyzeCommand, } from "./commands/analyze.js";
|
|
21
|
-
import { handleAnalyzeCommand as handleBaselineAnalyzeCommand, handleDeleteCommand as handleBaselineDeleteCommand, handleListCommand as handleBaselineListCommand, handleSetCommand as handleBaselineSetCommand, handleShowCommand as handleBaselineShowCommand, } from "./commands/baseline.js";
|
|
22
|
-
import { handleCleanCommand, handleCompareCommand, handleExportCommand, handleListCommand, handleShowCommand, handleTrendsCommand, } from "./commands/history.js";
|
|
23
|
-
import { handleInitCommand as initCommand } from "./commands/init.js";
|
|
24
|
-
import { handleRunCommand as runCommand } from "./commands/run.js";
|
|
25
|
-
import { handleTestCommand as testCommand, } from "./commands/test.js";
|
|
26
|
-
// ============================================================================
|
|
27
|
-
// Global Options Parser
|
|
28
|
-
// ============================================================================
|
|
29
|
-
const globalOptions = opt.options({
|
|
30
|
-
config: opt.string({
|
|
31
|
-
aliases: ['c'],
|
|
32
|
-
description: 'Path to configuration file',
|
|
33
|
-
}),
|
|
34
|
-
cwd: opt.string({
|
|
35
|
-
description: 'Working directory',
|
|
36
|
-
}),
|
|
37
|
-
json: opt.boolean({
|
|
38
|
-
description: 'Output results in JSON format',
|
|
39
|
-
}),
|
|
40
|
-
'no-color': opt.boolean({
|
|
41
|
-
description: 'Disable colored output',
|
|
42
|
-
}),
|
|
43
|
-
progress: opt.boolean({
|
|
44
|
-
description: 'Show animated progress bar',
|
|
45
|
-
}),
|
|
46
|
-
verbose: opt.boolean({
|
|
47
|
-
aliases: ['v'],
|
|
48
|
-
description: 'Enable verbose output',
|
|
49
|
-
}),
|
|
50
|
-
});
|
|
51
|
-
// ============================================================================
|
|
52
|
-
// History Command Parsers
|
|
53
|
-
// ============================================================================
|
|
54
|
-
const historyListParser = opt.options({
|
|
55
|
-
format: opt.enum(['human', 'json', 'csv'], {
|
|
56
|
-
description: 'Output format',
|
|
57
|
-
}),
|
|
58
|
-
limit: opt.number({
|
|
59
|
-
description: 'Maximum number of results',
|
|
60
|
-
}),
|
|
61
|
-
pattern: opt.string({
|
|
62
|
-
description: 'Filter by benchmark name pattern',
|
|
63
|
-
}),
|
|
64
|
-
since: opt.string({
|
|
65
|
-
description: 'Show runs since date (ISO 8601 or relative like "1 week ago")',
|
|
66
|
-
}),
|
|
67
|
-
tag: opt.array('string', {
|
|
68
|
-
aliases: ['t'],
|
|
69
|
-
description: 'Filter by tags',
|
|
70
|
-
}),
|
|
71
|
-
until: opt.string({
|
|
72
|
-
description: 'Show runs until date (ISO 8601 or relative like "1 day ago")',
|
|
73
|
-
}),
|
|
74
|
-
});
|
|
75
|
-
const historyShowParser = merge(opt.options({
|
|
76
|
-
format: opt.enum(['human', 'json', 'csv'], {
|
|
77
|
-
description: 'Output format',
|
|
78
|
-
}),
|
|
79
|
-
}), pos.positionals(pos.string({
|
|
80
|
-
description: 'ID of the benchmark run to show',
|
|
81
|
-
name: 'run-id',
|
|
82
|
-
required: true,
|
|
83
|
-
})));
|
|
84
|
-
const historyCompareParser = merge(opt.options({
|
|
85
|
-
format: opt.enum(['human', 'json'], {
|
|
86
|
-
description: 'Output format',
|
|
87
|
-
}),
|
|
88
|
-
}), pos.positionals(pos.string({
|
|
89
|
-
description: 'ID of the first benchmark run',
|
|
90
|
-
name: 'run-id1',
|
|
91
|
-
required: true,
|
|
92
|
-
}), pos.string({
|
|
93
|
-
description: 'ID of the second benchmark run',
|
|
94
|
-
name: 'run-id2',
|
|
95
|
-
required: true,
|
|
96
|
-
})));
|
|
97
|
-
const historyTrendsParser = merge(opt.options({
|
|
98
|
-
all: opt.boolean({
|
|
99
|
-
aliases: ['a'],
|
|
100
|
-
description: 'Analyze all runs (ignore limit)',
|
|
101
|
-
}),
|
|
102
|
-
format: opt.enum(['human', 'json'], {
|
|
103
|
-
description: 'Output format',
|
|
104
|
-
}),
|
|
105
|
-
limit: opt.number({
|
|
106
|
-
description: 'Maximum number of runs to analyze',
|
|
107
|
-
}),
|
|
108
|
-
since: opt.string({
|
|
109
|
-
description: 'Show trends since date (ISO 8601 or relative like "1 week ago")',
|
|
110
|
-
}),
|
|
111
|
-
tag: opt.array('string', {
|
|
112
|
-
aliases: ['t'],
|
|
113
|
-
description: 'Filter by tags',
|
|
114
|
-
}),
|
|
115
|
-
until: opt.string({
|
|
116
|
-
description: 'Show trends until date (ISO 8601 or relative like "1 day ago")',
|
|
117
|
-
}),
|
|
118
|
-
}), pos.positionals(pos.string({
|
|
119
|
-
description: 'Filter by benchmark name pattern',
|
|
120
|
-
name: 'pattern',
|
|
121
|
-
})));
|
|
122
|
-
const historyCleanParser = map(opt.options({
|
|
123
|
-
'max-age': opt.number({
|
|
124
|
-
description: 'Remove runs older than this many days',
|
|
125
|
-
}),
|
|
126
|
-
'max-runs': opt.number({
|
|
127
|
-
description: 'Keep only this many most recent runs',
|
|
128
|
-
}),
|
|
129
|
-
'max-size': opt.number({
|
|
130
|
-
description: 'Keep history under this size in bytes',
|
|
131
|
-
}),
|
|
132
|
-
yes: opt.boolean({
|
|
133
|
-
aliases: ['y'],
|
|
134
|
-
description: 'Confirm cleanup without prompting',
|
|
135
|
-
}),
|
|
136
|
-
}), ({ positionals, values }) => {
|
|
137
|
-
if (!values['max-age'] && !values['max-runs'] && !values['max-size']) {
|
|
138
|
-
throw new Error('At least one cleanup criterion must be specified (--max-age, --max-runs, or --max-size)');
|
|
139
|
-
}
|
|
140
|
-
return { positionals, values };
|
|
141
|
-
});
|
|
142
|
-
const historyExportParser = opt.options({
|
|
143
|
-
format: opt.enum(['json', 'csv'], {
|
|
144
|
-
description: 'Export format',
|
|
145
|
-
}),
|
|
146
|
-
output: opt.string({
|
|
147
|
-
aliases: ['o'],
|
|
148
|
-
description: 'Output file path',
|
|
149
|
-
required: true,
|
|
150
|
-
}),
|
|
151
|
-
since: opt.string({
|
|
152
|
-
description: 'Export runs since date',
|
|
153
|
-
}),
|
|
154
|
-
until: opt.string({
|
|
155
|
-
description: 'Export runs until date',
|
|
156
|
-
}),
|
|
157
|
-
});
|
|
158
|
-
// ============================================================================
|
|
159
|
-
// Baseline Command Parsers
|
|
160
|
-
// ============================================================================
|
|
161
|
-
const baselineSetParser = merge(opt.options({
|
|
162
|
-
branch: opt.string({
|
|
163
|
-
description: 'Git branch name',
|
|
164
|
-
}),
|
|
165
|
-
commit: opt.string({
|
|
166
|
-
description: 'Git commit SHA (40 characters)',
|
|
167
|
-
}),
|
|
168
|
-
default: opt.boolean({
|
|
169
|
-
description: 'Set as default baseline',
|
|
170
|
-
}),
|
|
171
|
-
'run-id': opt.string({
|
|
172
|
-
description: 'Specific run ID to save (default: most recent)',
|
|
173
|
-
}),
|
|
174
|
-
}), pos.positionals(pos.string({
|
|
175
|
-
description: 'Name for the baseline',
|
|
176
|
-
name: 'name',
|
|
177
|
-
required: true,
|
|
178
|
-
})));
|
|
179
|
-
const baselineListParser = opt.options({
|
|
180
|
-
format: opt.enum(['human', 'json'], {
|
|
181
|
-
description: 'Output format',
|
|
182
|
-
}),
|
|
183
|
-
});
|
|
184
|
-
const baselineShowParser = merge(opt.options({
|
|
185
|
-
format: opt.enum(['human', 'json'], {
|
|
186
|
-
description: 'Output format',
|
|
187
|
-
}),
|
|
188
|
-
}), pos.positionals(pos.string({
|
|
189
|
-
description: 'Baseline name to show',
|
|
190
|
-
name: 'name',
|
|
191
|
-
required: true,
|
|
192
|
-
})));
|
|
193
|
-
const baselineDeleteParser = pos.positionals(pos.string({
|
|
194
|
-
description: 'Baseline name to delete',
|
|
195
|
-
name: 'name',
|
|
196
|
-
required: true,
|
|
197
|
-
}));
|
|
198
|
-
const baselineAnalyzeParser = opt.options({
|
|
199
|
-
confidence: opt.number({
|
|
200
|
-
description: 'Confidence level (0.5-0.999, default 0.95)',
|
|
201
|
-
}),
|
|
202
|
-
runs: opt.number({
|
|
203
|
-
description: 'Number of recent runs to analyze',
|
|
204
|
-
}),
|
|
205
|
-
});
|
|
206
|
-
// ============================================================================
|
|
207
|
-
// Run Command Parser
|
|
208
|
-
// ============================================================================
|
|
209
|
-
const runParserBase = merge(opt.options({
|
|
210
|
-
bail: opt.boolean({
|
|
211
|
-
aliases: ['b'],
|
|
212
|
-
description: 'Stop on first failure',
|
|
213
|
-
}),
|
|
214
|
-
engine: opt.enum([Engines.TINYBENCH, Engines.ACCURATE], {
|
|
215
|
-
aliases: ['e'],
|
|
216
|
-
description: 'Benchmark engine: tinybench (default) or accurate (requires --allow-natives-syntax)',
|
|
217
|
-
}),
|
|
218
|
-
exclude: opt.array('string', {
|
|
219
|
-
aliases: ['X'],
|
|
220
|
-
description: 'Exclude patterns',
|
|
221
|
-
}),
|
|
222
|
-
'exclude-tag': opt.array('string', {
|
|
223
|
-
aliases: ['T'],
|
|
224
|
-
description: 'Exclude benchmarks with any of these tags',
|
|
225
|
-
}),
|
|
226
|
-
iterations: opt.number({
|
|
227
|
-
aliases: ['i'],
|
|
228
|
-
description: 'Number of iterations per benchmark',
|
|
229
|
-
}),
|
|
230
|
-
'json-pretty': opt.boolean({
|
|
231
|
-
description: 'Pretty-print JSON output (only affects json reporter)',
|
|
232
|
-
}),
|
|
233
|
-
'limit-by': opt.enum(['time', 'iterations', 'any', 'all'], {
|
|
234
|
-
aliases: ['l', 'limit'],
|
|
235
|
-
description: 'How to limit benchmarks: time (time budget), iterations (sample count), any (either threshold), all (both thresholds)',
|
|
236
|
-
}),
|
|
237
|
-
output: opt.string({
|
|
238
|
-
aliases: ['o'],
|
|
239
|
-
description: 'Output directory for reports',
|
|
240
|
-
}),
|
|
241
|
-
'output-file': opt.string({
|
|
242
|
-
aliases: ['of', 'file'],
|
|
243
|
-
description: 'Custom filename for reporter output (use with single reporter only)',
|
|
244
|
-
}),
|
|
245
|
-
quiet: opt.boolean({
|
|
246
|
-
aliases: ['q'],
|
|
247
|
-
description: 'Minimal output',
|
|
248
|
-
}),
|
|
249
|
-
reporter: opt.array([
|
|
250
|
-
Reporters.HUMAN,
|
|
251
|
-
Reporters.JSON,
|
|
252
|
-
Reporters.CSV,
|
|
253
|
-
Reporters.NYAN,
|
|
254
|
-
Reporters.SIMPLE,
|
|
255
|
-
], {
|
|
256
|
-
aliases: ['r'],
|
|
257
|
-
default: [DEFAULT_REPORTER],
|
|
258
|
-
description: 'Output reporters to use (human,json,csv)',
|
|
259
|
-
}),
|
|
260
|
-
tag: opt.array('string', {
|
|
261
|
-
description: 'Include only benchmarks with any of these tags',
|
|
262
|
-
}),
|
|
263
|
-
time: opt.number({
|
|
264
|
-
aliases: ['t'],
|
|
265
|
-
description: 'Time budget per benchmark in milliseconds',
|
|
266
|
-
}),
|
|
267
|
-
timeout: opt.number({
|
|
268
|
-
description: 'Timeout per benchmark in milliseconds',
|
|
269
|
-
}),
|
|
270
|
-
warmup: opt.number({
|
|
271
|
-
aliases: ['w', 'warm'],
|
|
272
|
-
description: 'Number of warmup iterations',
|
|
273
|
-
}),
|
|
274
|
-
}), pos.positionals(pos.variadic('string', {
|
|
275
|
-
description: 'File paths, directory paths, or glob patterns for benchmark files',
|
|
276
|
-
name: 'pattern',
|
|
277
|
-
})));
|
|
278
|
-
// Add validation via map()
|
|
279
|
-
const runParser = map(runParserBase, ({ positionals, values }) => {
|
|
280
|
-
if (values.reporter && values.reporter.length > 1 && values['output-file']) {
|
|
281
|
-
throw new Error('--output-file can only be used with a single reporter. Use --output <dir> for multiple reporters.');
|
|
282
|
-
}
|
|
283
|
-
return { positionals, values };
|
|
284
|
-
});
|
|
285
|
-
// ============================================================================
|
|
286
|
-
// Init Command Parser
|
|
287
|
-
// ============================================================================
|
|
288
|
-
const initParser = merge(opt.options({
|
|
289
|
-
'config-type': opt.enum(['json', 'yaml', 'js', 'ts'], {
|
|
290
|
-
description: 'Configuration file format',
|
|
291
|
-
}),
|
|
292
|
-
examples: opt.boolean({
|
|
293
|
-
description: 'Include example benchmark files',
|
|
294
|
-
}),
|
|
295
|
-
force: opt.boolean({
|
|
296
|
-
description: 'Overwrite existing files',
|
|
297
|
-
}),
|
|
298
|
-
quiet: opt.boolean({
|
|
299
|
-
aliases: ['q'],
|
|
300
|
-
description: 'Minimal output',
|
|
301
|
-
}),
|
|
302
|
-
yes: opt.boolean({
|
|
303
|
-
aliases: ['y'],
|
|
304
|
-
description: 'Accept all prompts automatically',
|
|
305
|
-
}),
|
|
306
|
-
}), pos.positionals(pos.enum(['basic', 'advanced', 'library'], {
|
|
307
|
-
description: 'Type of project to initialize',
|
|
308
|
-
name: 'type',
|
|
309
|
-
})));
|
|
310
|
-
// ============================================================================
|
|
311
|
-
// Analyze Command Parser
|
|
312
|
-
// ============================================================================
|
|
313
|
-
const analyzeParserBase = merge(opt.options({
|
|
314
|
-
'filter-file': opt.string({
|
|
315
|
-
description: 'Filter functions by file glob pattern',
|
|
316
|
-
}),
|
|
317
|
-
'group-by-file': opt.boolean({
|
|
318
|
-
description: 'Group results by file',
|
|
319
|
-
}),
|
|
320
|
-
input: opt.string({
|
|
321
|
-
aliases: ['i'],
|
|
322
|
-
description: 'Path to existing *.cpuprofile file',
|
|
323
|
-
}),
|
|
324
|
-
'min-percent': opt.number({
|
|
325
|
-
aliases: ['m', 'min'],
|
|
326
|
-
default: 0.5,
|
|
327
|
-
description: 'Minimum execution percentage to show',
|
|
328
|
-
}),
|
|
329
|
-
top: opt.number({
|
|
330
|
-
aliases: ['n'],
|
|
331
|
-
default: 25,
|
|
332
|
-
description: 'Number of top functions to show',
|
|
333
|
-
}),
|
|
334
|
-
}), pos.positionals(pos.string({
|
|
335
|
-
description: 'Command to analyze (e.g., "npm test")',
|
|
336
|
-
name: 'command',
|
|
337
|
-
})));
|
|
338
|
-
// Add validation
|
|
339
|
-
const analyzeParser = map(analyzeParserBase, ({ positionals, values }) => {
|
|
340
|
-
const [command] = positionals;
|
|
341
|
-
if (!command && !values.input) {
|
|
342
|
-
throw new Error('Either [command] or --input must be provided');
|
|
343
|
-
}
|
|
344
|
-
return { positionals, values };
|
|
345
|
-
});
|
|
346
|
-
// ============================================================================
|
|
347
|
-
// Test Command Parser
|
|
348
|
-
// ============================================================================
|
|
349
|
-
const testParser = merge(opt.options({
|
|
350
|
-
bail: opt.boolean({
|
|
351
|
-
aliases: ['b'],
|
|
352
|
-
description: 'Stop on first failure',
|
|
353
|
-
}),
|
|
354
|
-
iterations: opt.number({
|
|
355
|
-
aliases: ['i'],
|
|
356
|
-
default: 100,
|
|
357
|
-
description: 'Number of iterations per test',
|
|
358
|
-
}),
|
|
359
|
-
quiet: opt.boolean({
|
|
360
|
-
aliases: ['q'],
|
|
361
|
-
description: 'Minimal output',
|
|
362
|
-
}),
|
|
363
|
-
warmup: opt.number({
|
|
364
|
-
aliases: ['w'],
|
|
365
|
-
default: 5,
|
|
366
|
-
description: 'Number of warmup iterations',
|
|
367
|
-
}),
|
|
368
|
-
}), pos.positionals(pos.enum(['ava', 'jest', 'mocha', 'node-test'], {
|
|
369
|
-
description: 'Test framework to use',
|
|
370
|
-
name: 'framework',
|
|
371
|
-
required: true,
|
|
372
|
-
}), pos.variadic('string', {
|
|
373
|
-
description: 'Test file paths or glob patterns',
|
|
374
|
-
name: 'files',
|
|
375
|
-
})));
|
|
376
|
-
// ============================================================================
|
|
377
|
-
// Subcommand-specific options
|
|
378
|
-
// ============================================================================
|
|
379
|
-
/**
|
|
380
|
-
* Additional global options for history and baseline subcommands
|
|
381
|
-
*/
|
|
382
|
-
const quietOption = opt.options({
|
|
383
|
-
quiet: opt.boolean({
|
|
384
|
-
description: 'Minimal output',
|
|
385
|
-
}),
|
|
386
|
-
});
|
|
387
|
-
// ============================================================================
|
|
388
|
-
// Main CLI Builder
|
|
389
|
-
// ============================================================================
|
|
390
|
-
/**
|
|
391
|
-
* Synthwave-inspired theme for CLI help output
|
|
392
|
-
*
|
|
393
|
-
* Matches the retro aesthetic used in modestbench reporters
|
|
394
|
-
*/
|
|
395
|
-
const synthwaveTheme = {
|
|
396
|
-
colors: {
|
|
397
|
-
command: ansi.brightMagenta,
|
|
398
|
-
defaultText: ansi.dim,
|
|
399
|
-
defaultValue: ansi.brightYellow,
|
|
400
|
-
description: ansi.brightWhite,
|
|
401
|
-
epilog: ansi.brightWhite,
|
|
402
|
-
example: ansi.cyan,
|
|
403
|
-
flag: ansi.brightCyan,
|
|
404
|
-
positional: ansi.brightMagenta,
|
|
405
|
-
scriptName: ansi.brightCyan + ansi.bold,
|
|
406
|
-
sectionHeader: ansi.magenta + ansi.bold,
|
|
407
|
-
type: ansi.brightWhite + ansi.dim,
|
|
408
|
-
url: ansi.brightCyan + ansi.underline,
|
|
409
|
-
usage: ansi.white,
|
|
410
|
-
},
|
|
411
|
-
};
|
|
412
|
-
const createCli = (abortController) => {
|
|
413
|
-
return bargs('modestbench', {
|
|
414
|
-
description: 'A modern benchmark runner for Node.js',
|
|
415
|
-
theme: synthwaveTheme,
|
|
416
|
-
})
|
|
417
|
-
.globals(globalOptions)
|
|
418
|
-
.command('run', runParser, async ({ positionals, values }) => {
|
|
419
|
-
const [pattern] = positionals;
|
|
420
|
-
const context = await createCliContext(values, abortController, values.engine);
|
|
421
|
-
const exitCode = await runCommand(context, {
|
|
422
|
-
bail: values.bail,
|
|
423
|
-
config: values.config,
|
|
424
|
-
cwd: values.cwd,
|
|
425
|
-
engine: values.engine,
|
|
426
|
-
exclude: values.exclude,
|
|
427
|
-
excludeTags: values['exclude-tag'],
|
|
428
|
-
iterations: values.iterations,
|
|
429
|
-
json: values.json,
|
|
430
|
-
jsonPretty: values['json-pretty'],
|
|
431
|
-
noColor: values['no-color'],
|
|
432
|
-
outputDir: values.output,
|
|
433
|
-
outputFile: values['output-file'],
|
|
434
|
-
pattern,
|
|
435
|
-
progress: values.progress,
|
|
436
|
-
quiet: values.quiet,
|
|
437
|
-
reporters: values.reporter,
|
|
438
|
-
tags: values.tag,
|
|
439
|
-
time: values.time,
|
|
440
|
-
timeout: values.timeout,
|
|
441
|
-
verbose: values.verbose,
|
|
442
|
-
warmup: values.warmup,
|
|
443
|
-
});
|
|
444
|
-
process.exit(exitCode);
|
|
445
|
-
}, 'Run benchmark files')
|
|
446
|
-
.command('history', (history) => history
|
|
447
|
-
.globals(quietOption)
|
|
448
|
-
.command('list', historyListParser, async ({ values }) => {
|
|
449
|
-
const context = await createCliContext(values, abortController);
|
|
450
|
-
const exitCode = await handleListCommand(context, {
|
|
451
|
-
cwd: values.cwd,
|
|
452
|
-
format: values.format,
|
|
453
|
-
limit: values.limit,
|
|
454
|
-
pattern: values.pattern,
|
|
455
|
-
since: values.since,
|
|
456
|
-
tags: values.tag,
|
|
457
|
-
until: values.until,
|
|
458
|
-
verbose: values.verbose,
|
|
459
|
-
});
|
|
460
|
-
process.exit(exitCode);
|
|
461
|
-
}, 'List recent benchmark runs')
|
|
462
|
-
.command('show', historyShowParser, async ({ positionals, values }) => {
|
|
463
|
-
const [runId] = positionals;
|
|
464
|
-
const context = await createCliContext(values, abortController);
|
|
465
|
-
const exitCode = await handleShowCommand(context, {
|
|
466
|
-
cwd: values.cwd,
|
|
467
|
-
format: values.format,
|
|
468
|
-
runId,
|
|
469
|
-
verbose: values.verbose,
|
|
470
|
-
});
|
|
471
|
-
process.exit(exitCode);
|
|
472
|
-
}, 'Show detailed results for a specific run')
|
|
473
|
-
.command('compare', historyCompareParser, async ({ positionals, values }) => {
|
|
474
|
-
const [runId1, runId2] = positionals;
|
|
475
|
-
const context = await createCliContext(values, abortController);
|
|
476
|
-
const exitCode = await handleCompareCommand(context, {
|
|
477
|
-
cwd: values.cwd,
|
|
478
|
-
format: values.format,
|
|
479
|
-
runId1,
|
|
480
|
-
runId2,
|
|
481
|
-
verbose: values.verbose,
|
|
482
|
-
});
|
|
483
|
-
process.exit(exitCode);
|
|
484
|
-
}, 'Compare two benchmark runs')
|
|
485
|
-
.command('trends', historyTrendsParser, async ({ positionals, values }) => {
|
|
486
|
-
const [pattern] = positionals;
|
|
487
|
-
const context = await createCliContext(values, abortController);
|
|
488
|
-
const exitCode = await handleTrendsCommand(context, {
|
|
489
|
-
all: values.all,
|
|
490
|
-
cwd: values.cwd,
|
|
491
|
-
format: values.format,
|
|
492
|
-
limit: values.limit,
|
|
493
|
-
pattern,
|
|
494
|
-
since: values.since,
|
|
495
|
-
tags: values.tag,
|
|
496
|
-
until: values.until,
|
|
497
|
-
verbose: values.verbose,
|
|
498
|
-
});
|
|
499
|
-
process.exit(exitCode);
|
|
500
|
-
}, 'Show performance trends over time')
|
|
501
|
-
.command('clean', historyCleanParser, async ({ values }) => {
|
|
502
|
-
const context = await createCliContext(values, abortController);
|
|
503
|
-
const exitCode = await handleCleanCommand(context, {
|
|
504
|
-
confirm: values.yes,
|
|
505
|
-
cwd: values.cwd,
|
|
506
|
-
maxAge: values['max-age'],
|
|
507
|
-
maxRuns: values['max-runs'],
|
|
508
|
-
maxSize: values['max-size'],
|
|
509
|
-
quiet: values.quiet,
|
|
510
|
-
verbose: values.verbose,
|
|
511
|
-
});
|
|
512
|
-
process.exit(exitCode);
|
|
513
|
-
}, 'Clean up old benchmark history')
|
|
514
|
-
.command('export', historyExportParser, async ({ values }) => {
|
|
515
|
-
const context = await createCliContext(values, abortController);
|
|
516
|
-
const exitCode = await handleExportCommand(context, {
|
|
517
|
-
cwd: values.cwd,
|
|
518
|
-
format: values.format,
|
|
519
|
-
outputPath: values.output,
|
|
520
|
-
quiet: Boolean(values.quiet),
|
|
521
|
-
since: values.since,
|
|
522
|
-
until: values.until,
|
|
523
|
-
verbose: values.verbose,
|
|
524
|
-
});
|
|
525
|
-
process.exitCode = exitCode;
|
|
526
|
-
}, 'Export benchmark history to a file'), 'View and manage benchmark history')
|
|
527
|
-
.command('baseline', (baseline) => baseline
|
|
528
|
-
.globals(quietOption)
|
|
529
|
-
.command('set', baselineSetParser, async ({ positionals, values }) => {
|
|
530
|
-
const [name] = positionals;
|
|
531
|
-
const context = await createCliContext(values, abortController);
|
|
532
|
-
const exitCode = await handleBaselineSetCommand(context, {
|
|
533
|
-
branch: values.branch,
|
|
534
|
-
commit: values.commit,
|
|
535
|
-
cwd: values.cwd,
|
|
536
|
-
default: values.default,
|
|
537
|
-
name,
|
|
538
|
-
quiet: Boolean(values.quiet),
|
|
539
|
-
runId: values['run-id'],
|
|
540
|
-
verbose: values.verbose,
|
|
541
|
-
});
|
|
542
|
-
process.exit(exitCode);
|
|
543
|
-
}, 'Save a benchmark run as a baseline')
|
|
544
|
-
.command('list', baselineListParser, async ({ values }) => {
|
|
545
|
-
const context = await createCliContext(values, abortController);
|
|
546
|
-
const exitCode = await handleBaselineListCommand(context, {
|
|
547
|
-
cwd: values.cwd,
|
|
548
|
-
format: values.format,
|
|
549
|
-
quiet: Boolean(values.quiet),
|
|
550
|
-
verbose: values.verbose,
|
|
551
|
-
});
|
|
552
|
-
process.exit(exitCode);
|
|
553
|
-
}, 'List all saved baselines')
|
|
554
|
-
.command('show', baselineShowParser, async ({ positionals, values }) => {
|
|
555
|
-
const [name] = positionals;
|
|
556
|
-
const context = await createCliContext(values, abortController);
|
|
557
|
-
const exitCode = await handleBaselineShowCommand(context, {
|
|
558
|
-
cwd: values.cwd,
|
|
559
|
-
format: values.format,
|
|
560
|
-
name,
|
|
561
|
-
quiet: Boolean(values.quiet),
|
|
562
|
-
verbose: values.verbose,
|
|
563
|
-
});
|
|
564
|
-
process.exit(exitCode);
|
|
565
|
-
}, 'Show baseline details')
|
|
566
|
-
.command('delete', baselineDeleteParser, async ({ positionals, values }) => {
|
|
567
|
-
const [name] = positionals;
|
|
568
|
-
const context = await createCliContext(values, abortController);
|
|
569
|
-
const exitCode = await handleBaselineDeleteCommand(context, {
|
|
570
|
-
cwd: values.cwd,
|
|
571
|
-
name,
|
|
572
|
-
quiet: Boolean(values.quiet),
|
|
573
|
-
verbose: values.verbose,
|
|
574
|
-
});
|
|
575
|
-
process.exit(exitCode);
|
|
576
|
-
}, 'Delete a baseline')
|
|
577
|
-
.command('analyze', baselineAnalyzeParser, async ({ values }) => {
|
|
578
|
-
const context = await createCliContext(values, abortController);
|
|
579
|
-
const exitCode = await handleBaselineAnalyzeCommand(context, {
|
|
580
|
-
confidence: values.confidence,
|
|
581
|
-
cwd: values.cwd,
|
|
582
|
-
quiet: Boolean(values.quiet),
|
|
583
|
-
runs: values.runs,
|
|
584
|
-
verbose: values.verbose,
|
|
585
|
-
});
|
|
586
|
-
process.exit(exitCode);
|
|
587
|
-
}, 'Analyze history and suggest performance budgets'), 'Manage performance baselines')
|
|
588
|
-
.command('init', initParser, async ({ positionals, values }) => {
|
|
589
|
-
const [type] = positionals;
|
|
590
|
-
const context = await createCliContext(values, abortController);
|
|
591
|
-
const exitCode = await initCommand(context, {
|
|
592
|
-
configType: values['config-type'],
|
|
593
|
-
cwd: values.cwd,
|
|
594
|
-
examples: values.examples,
|
|
595
|
-
force: values.force,
|
|
596
|
-
quiet: values.quiet,
|
|
597
|
-
type,
|
|
598
|
-
verbose: values.verbose,
|
|
599
|
-
yes: values.yes,
|
|
600
|
-
});
|
|
601
|
-
process.exitCode = exitCode;
|
|
602
|
-
}, 'Initialize a new benchmark project')
|
|
603
|
-
.command('analyze', analyzeParser, async ({ positionals, values }) => {
|
|
604
|
-
const [command] = positionals;
|
|
605
|
-
// Context not needed for analyze command currently
|
|
606
|
-
const context = {};
|
|
607
|
-
const options = {
|
|
608
|
-
color: !values['no-color'],
|
|
609
|
-
command,
|
|
610
|
-
cwd: values.cwd || process.cwd(),
|
|
611
|
-
filterFile: values['filter-file'],
|
|
612
|
-
groupByFile: values['group-by-file'],
|
|
613
|
-
input: values.input,
|
|
614
|
-
minPercent: values['min-percent'],
|
|
615
|
-
top: values.top,
|
|
616
|
-
};
|
|
617
|
-
process.exitCode = await analyzeCommand(context, options);
|
|
618
|
-
}, {
|
|
619
|
-
aliases: ['profile'],
|
|
620
|
-
description: 'Analyze code execution and identify benchmark candidates',
|
|
621
|
-
})
|
|
622
|
-
.command('test', testParser, async ({ positionals, values }) => {
|
|
623
|
-
const [framework, files] = positionals;
|
|
624
|
-
const context = await createCliContext(values, abortController);
|
|
625
|
-
const options = {
|
|
626
|
-
bail: values.bail,
|
|
627
|
-
cwd: values.cwd,
|
|
628
|
-
framework,
|
|
629
|
-
iterations: values.iterations,
|
|
630
|
-
json: values.json,
|
|
631
|
-
noColor: values['no-color'],
|
|
632
|
-
pattern: files,
|
|
633
|
-
quiet: values.quiet,
|
|
634
|
-
verbose: values.verbose,
|
|
635
|
-
warmup: values.warmup,
|
|
636
|
-
};
|
|
637
|
-
const exitCode = await testCommand(context, options);
|
|
638
|
-
process.exit(exitCode);
|
|
639
|
-
}, 'Run test files as benchmarks')
|
|
640
|
-
.defaultCommand('run');
|
|
641
|
-
};
|
|
13
|
+
import { ErrorCodes, ExitCodes } from "../constants.js";
|
|
14
|
+
import { isModestBenchError } from "../errors/index.js";
|
|
15
|
+
import { createCli } from "./builder.js";
|
|
16
|
+
import { setupSignalHandlers } from "./handlers.js";
|
|
17
|
+
// Re-export types and utilities for external use
|
|
18
|
+
export { createCliContext } from "./context.js";
|
|
642
19
|
/**
|
|
643
20
|
* Initialize and run the CLI
|
|
644
21
|
*/
|
|
@@ -696,87 +73,6 @@ export const main = async (argv, abortController) => {
|
|
|
696
73
|
process.exit(ExitCodes.UNKNOWN_ERROR);
|
|
697
74
|
}
|
|
698
75
|
};
|
|
699
|
-
/**
|
|
700
|
-
* Create CLI context with dependency injection
|
|
701
|
-
*/
|
|
702
|
-
const createCliContext = async (options, abortController, engineType = DEFAULT_ENGINE) => {
|
|
703
|
-
try {
|
|
704
|
-
const dependencies = bootstrap();
|
|
705
|
-
// Select engine based on type
|
|
706
|
-
const engine = engineType === Engines.ACCURATE
|
|
707
|
-
? new AccurateEngine(dependencies)
|
|
708
|
-
: new TinybenchEngine(dependencies);
|
|
709
|
-
// Register built-in reporters
|
|
710
|
-
engine.registerReporter(Reporters.HUMAN, new HumanReporter({
|
|
711
|
-
color: !options['no-color'],
|
|
712
|
-
verbose: options.verbose,
|
|
713
|
-
}));
|
|
714
|
-
engine.registerReporter('json', new JsonReporter({
|
|
715
|
-
prettyPrint: false,
|
|
716
|
-
}));
|
|
717
|
-
engine.registerReporter('csv', new CsvReporter({
|
|
718
|
-
includeHeaders: true,
|
|
719
|
-
includeMetadata: true,
|
|
720
|
-
}));
|
|
721
|
-
engine.registerReporter('simple', new SimpleReporter({
|
|
722
|
-
verbose: options.verbose,
|
|
723
|
-
}));
|
|
724
|
-
engine.registerReporter('nyan', new NyanReporter({
|
|
725
|
-
color: !options['no-color'],
|
|
726
|
-
}));
|
|
727
|
-
return {
|
|
728
|
-
abortController,
|
|
729
|
-
configManager: engine.configManager,
|
|
730
|
-
engine,
|
|
731
|
-
historyStorage: engine.historyStorage,
|
|
732
|
-
options,
|
|
733
|
-
progressManager: engine.progressManager,
|
|
734
|
-
reporterRegistry: engine.reporterRegistry,
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
catch (error) {
|
|
738
|
-
console.error('Failed to initialize ModestBench:', error instanceof Error ? error.message : String(error));
|
|
739
|
-
process.exit(ExitCodes.CONFIG_ERROR);
|
|
740
|
-
}
|
|
741
|
-
};
|
|
742
|
-
/**
|
|
743
|
-
* Handle process signals gracefully
|
|
744
|
-
*/
|
|
745
|
-
const setupSignalHandlers = (abortController) => {
|
|
746
|
-
let abortRequested = false;
|
|
747
|
-
const handleSignal = (signal) => {
|
|
748
|
-
if (abortRequested) {
|
|
749
|
-
// Second signal, force exit
|
|
750
|
-
console.log(`\nReceived ${signal} again, forcing exit...`);
|
|
751
|
-
process.exit(computeExitCode(signal));
|
|
752
|
-
}
|
|
753
|
-
console.log(`\nReceived ${signal}, aborting benchmarks...`);
|
|
754
|
-
abortRequested = true;
|
|
755
|
-
abortController.abort();
|
|
756
|
-
// Give a short grace period for cleanup, then exit
|
|
757
|
-
setTimeout(() => {
|
|
758
|
-
console.log('\nBenchmark aborted.');
|
|
759
|
-
process.exit(computeExitCode(signal));
|
|
760
|
-
}, ABORT_TIMEOUT);
|
|
761
|
-
};
|
|
762
|
-
process
|
|
763
|
-
.once('SIGINT', handleSignal)
|
|
764
|
-
.once('SIGQUIT', handleSignal)
|
|
765
|
-
.once('SIGTERM', handleSignal)
|
|
766
|
-
.once('uncaughtException', (error) => {
|
|
767
|
-
// Wrap non-ModestBench errors with UnknownError
|
|
768
|
-
const wrappedError = isModestBenchError(error)
|
|
769
|
-
? error
|
|
770
|
-
: new UnknownError(error.message, { cause: error });
|
|
771
|
-
console.error(`${wrappedError}`);
|
|
772
|
-
process.exit(ExitCodes.RUNTIME_ERROR);
|
|
773
|
-
})
|
|
774
|
-
.once('unhandledRejection', (reason) => {
|
|
775
|
-
const wrappedError = new UnknownError(isError(reason) ? reason.message : String(reason), { cause: reason });
|
|
776
|
-
console.error(`${wrappedError}`);
|
|
777
|
-
process.exit(ExitCodes.RUNTIME_ERROR);
|
|
778
|
-
});
|
|
779
|
-
};
|
|
780
76
|
// Run CLI if this file is executed directly
|
|
781
77
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
782
78
|
const argPath = process.argv[1];
|
|
@@ -794,13 +90,4 @@ catch {
|
|
|
794
90
|
cli();
|
|
795
91
|
}
|
|
796
92
|
}
|
|
797
|
-
/**
|
|
798
|
-
* Compute the exit code based on the signal
|
|
799
|
-
*
|
|
800
|
-
* @param signal - The signal that caused the exit
|
|
801
|
-
* @returns The exit code
|
|
802
|
-
*/
|
|
803
|
-
const computeExitCode = (signal) => {
|
|
804
|
-
return 128 + (signal === 'SIGINT' ? 2 : signal === 'SIGQUIT' ? 3 : 15);
|
|
805
|
-
};
|
|
806
93
|
//# sourceMappingURL=index.js.map
|