modestbench 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (357) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +131 -34
  3. package/dist/cli/commands/analyze.cjs +60 -0
  4. package/dist/cli/commands/analyze.cjs.map +1 -0
  5. package/dist/cli/commands/analyze.d.cts +35 -0
  6. package/dist/cli/commands/analyze.d.cts.map +1 -0
  7. package/dist/cli/commands/analyze.d.ts +35 -0
  8. package/dist/cli/commands/analyze.d.ts.map +1 -0
  9. package/dist/cli/commands/analyze.js +56 -0
  10. package/dist/cli/commands/analyze.js.map +1 -0
  11. package/dist/cli/commands/baseline.cjs +404 -0
  12. package/dist/cli/commands/baseline.cjs.map +1 -0
  13. package/dist/cli/commands/baseline.d.cts +72 -0
  14. package/dist/cli/commands/baseline.d.cts.map +1 -0
  15. package/dist/cli/commands/baseline.d.ts +72 -0
  16. package/dist/cli/commands/baseline.d.ts.map +1 -0
  17. package/dist/cli/commands/baseline.js +396 -0
  18. package/dist/cli/commands/baseline.js.map +1 -0
  19. package/dist/cli/commands/history.d.cts +1 -1
  20. package/dist/cli/commands/history.d.cts.map +1 -1
  21. package/dist/cli/commands/history.d.ts +1 -1
  22. package/dist/cli/commands/history.d.ts.map +1 -1
  23. package/dist/cli/commands/init.cjs +99 -166
  24. package/dist/cli/commands/init.cjs.map +1 -1
  25. package/dist/cli/commands/init.d.cts +4 -4
  26. package/dist/cli/commands/init.d.cts.map +1 -1
  27. package/dist/cli/commands/init.d.ts +4 -4
  28. package/dist/cli/commands/init.d.ts.map +1 -1
  29. package/dist/cli/commands/init.js +99 -166
  30. package/dist/cli/commands/init.js.map +1 -1
  31. package/dist/cli/commands/run.cjs +146 -127
  32. package/dist/cli/commands/run.cjs.map +1 -1
  33. package/dist/cli/commands/run.d.cts +16 -3
  34. package/dist/cli/commands/run.d.cts.map +1 -1
  35. package/dist/cli/commands/run.d.ts +16 -3
  36. package/dist/cli/commands/run.d.ts.map +1 -1
  37. package/dist/cli/commands/run.js +145 -93
  38. package/dist/cli/commands/run.js.map +1 -1
  39. package/dist/cli/index.cjs +583 -394
  40. package/dist/cli/index.cjs.map +1 -1
  41. package/dist/cli/index.d.cts +4 -16
  42. package/dist/cli/index.d.cts.map +1 -1
  43. package/dist/cli/index.d.ts +4 -16
  44. package/dist/cli/index.d.ts.map +1 -1
  45. package/dist/cli/index.js +575 -386
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/config/budget-schema.cjs +172 -0
  48. package/dist/config/budget-schema.cjs.map +1 -0
  49. package/dist/config/budget-schema.d.cts +59 -0
  50. package/dist/config/budget-schema.d.cts.map +1 -0
  51. package/dist/config/budget-schema.d.ts +59 -0
  52. package/dist/config/budget-schema.d.ts.map +1 -0
  53. package/dist/config/budget-schema.js +166 -0
  54. package/dist/config/budget-schema.js.map +1 -0
  55. package/dist/config/schema.cjs +182 -2
  56. package/dist/config/schema.cjs.map +1 -1
  57. package/dist/config/schema.d.cts +122 -3
  58. package/dist/config/schema.d.cts.map +1 -1
  59. package/dist/config/schema.d.ts +122 -3
  60. package/dist/config/schema.d.ts.map +1 -1
  61. package/dist/config/schema.js +180 -1
  62. package/dist/config/schema.js.map +1 -1
  63. package/dist/constants.cjs +45 -2
  64. package/dist/constants.cjs.map +1 -1
  65. package/dist/constants.d.cts +41 -0
  66. package/dist/constants.d.cts.map +1 -1
  67. package/dist/constants.d.ts +41 -0
  68. package/dist/constants.d.ts.map +1 -1
  69. package/dist/constants.js +44 -1
  70. package/dist/constants.js.map +1 -1
  71. package/dist/core/engine.cjs +114 -23
  72. package/dist/core/engine.cjs.map +1 -1
  73. package/dist/core/engine.d.cts +7 -7
  74. package/dist/core/engine.d.cts.map +1 -1
  75. package/dist/core/engine.d.ts +7 -7
  76. package/dist/core/engine.d.ts.map +1 -1
  77. package/dist/core/engine.js +115 -24
  78. package/dist/core/engine.js.map +1 -1
  79. package/dist/core/engines/accurate-engine.cjs +171 -36
  80. package/dist/core/engines/accurate-engine.cjs.map +1 -1
  81. package/dist/core/engines/accurate-engine.d.cts +5 -0
  82. package/dist/core/engines/accurate-engine.d.cts.map +1 -1
  83. package/dist/core/engines/accurate-engine.d.ts +5 -0
  84. package/dist/core/engines/accurate-engine.d.ts.map +1 -1
  85. package/dist/core/engines/accurate-engine.js +171 -36
  86. package/dist/core/engines/accurate-engine.js.map +1 -1
  87. package/dist/core/engines/tinybench-engine.cjs +3 -2
  88. package/dist/core/engines/tinybench-engine.cjs.map +1 -1
  89. package/dist/core/engines/tinybench-engine.d.cts.map +1 -1
  90. package/dist/core/engines/tinybench-engine.d.ts.map +1 -1
  91. package/dist/core/engines/tinybench-engine.js +3 -2
  92. package/dist/core/engines/tinybench-engine.js.map +1 -1
  93. package/dist/core/output-path-resolver.cjs +8 -1
  94. package/dist/core/output-path-resolver.cjs.map +1 -1
  95. package/dist/core/output-path-resolver.d.cts.map +1 -1
  96. package/dist/core/output-path-resolver.d.ts.map +1 -1
  97. package/dist/core/output-path-resolver.js +9 -2
  98. package/dist/core/output-path-resolver.js.map +1 -1
  99. package/dist/errors/base.cjs +12 -3
  100. package/dist/errors/base.cjs.map +1 -1
  101. package/dist/errors/base.d.cts +7 -0
  102. package/dist/errors/base.d.cts.map +1 -1
  103. package/dist/errors/base.d.ts +7 -0
  104. package/dist/errors/base.d.ts.map +1 -1
  105. package/dist/errors/base.js +10 -2
  106. package/dist/errors/base.js.map +1 -1
  107. package/dist/errors/budget.cjs +37 -0
  108. package/dist/errors/budget.cjs.map +1 -0
  109. package/dist/errors/budget.d.cts +31 -0
  110. package/dist/errors/budget.d.cts.map +1 -0
  111. package/dist/errors/budget.d.ts +31 -0
  112. package/dist/errors/budget.d.ts.map +1 -0
  113. package/dist/errors/budget.js +33 -0
  114. package/dist/errors/budget.js.map +1 -0
  115. package/dist/errors/index.cjs +4 -1
  116. package/dist/errors/index.cjs.map +1 -1
  117. package/dist/errors/index.d.cts +1 -0
  118. package/dist/errors/index.d.cts.map +1 -1
  119. package/dist/errors/index.d.ts +1 -0
  120. package/dist/errors/index.d.ts.map +1 -1
  121. package/dist/errors/index.js +2 -0
  122. package/dist/errors/index.js.map +1 -1
  123. package/dist/index.cjs +13 -1
  124. package/dist/index.cjs.map +1 -1
  125. package/dist/index.d.cts +5 -0
  126. package/dist/index.d.cts.map +1 -1
  127. package/dist/index.d.ts +5 -0
  128. package/dist/index.d.ts.map +1 -1
  129. package/dist/index.js +7 -0
  130. package/dist/index.js.map +1 -1
  131. package/dist/reporters/csv.cjs +37 -17
  132. package/dist/reporters/csv.cjs.map +1 -1
  133. package/dist/reporters/csv.d.cts +3 -6
  134. package/dist/reporters/csv.d.cts.map +1 -1
  135. package/dist/reporters/csv.d.ts +3 -6
  136. package/dist/reporters/csv.d.ts.map +1 -1
  137. package/dist/reporters/csv.js +37 -17
  138. package/dist/reporters/csv.js.map +1 -1
  139. package/dist/reporters/human.cjs +290 -67
  140. package/dist/reporters/human.cjs.map +1 -1
  141. package/dist/reporters/human.d.cts +25 -13
  142. package/dist/reporters/human.d.cts.map +1 -1
  143. package/dist/reporters/human.d.ts +25 -13
  144. package/dist/reporters/human.d.ts.map +1 -1
  145. package/dist/reporters/human.js +290 -67
  146. package/dist/reporters/human.js.map +1 -1
  147. package/dist/reporters/json.cjs +23 -48
  148. package/dist/reporters/json.cjs.map +1 -1
  149. package/dist/reporters/json.d.cts +2 -28
  150. package/dist/reporters/json.d.cts.map +1 -1
  151. package/dist/reporters/json.d.ts +2 -28
  152. package/dist/reporters/json.d.ts.map +1 -1
  153. package/dist/reporters/json.js +25 -50
  154. package/dist/reporters/json.js.map +1 -1
  155. package/dist/reporters/profile-human.cjs +154 -0
  156. package/dist/reporters/profile-human.cjs.map +1 -0
  157. package/dist/reporters/profile-human.d.cts +44 -0
  158. package/dist/reporters/profile-human.d.cts.map +1 -0
  159. package/dist/reporters/profile-human.d.ts +44 -0
  160. package/dist/reporters/profile-human.d.ts.map +1 -0
  161. package/dist/reporters/profile-human.js +147 -0
  162. package/dist/reporters/profile-human.js.map +1 -0
  163. package/dist/reporters/simple.cjs +67 -45
  164. package/dist/reporters/simple.cjs.map +1 -1
  165. package/dist/reporters/simple.d.cts +14 -14
  166. package/dist/reporters/simple.d.cts.map +1 -1
  167. package/dist/reporters/simple.d.ts +14 -14
  168. package/dist/reporters/simple.d.ts.map +1 -1
  169. package/dist/reporters/simple.js +67 -45
  170. package/dist/reporters/simple.js.map +1 -1
  171. package/dist/schema/modestbench-config.schema.json +153 -0
  172. package/dist/services/baseline-storage.cjs +151 -0
  173. package/dist/services/baseline-storage.cjs.map +1 -0
  174. package/dist/services/baseline-storage.d.cts +55 -0
  175. package/dist/services/baseline-storage.d.cts.map +1 -0
  176. package/dist/services/baseline-storage.d.ts +55 -0
  177. package/dist/services/baseline-storage.d.ts.map +1 -0
  178. package/dist/services/baseline-storage.js +147 -0
  179. package/dist/services/baseline-storage.js.map +1 -0
  180. package/dist/services/budget-evaluator.cjs +146 -0
  181. package/dist/services/budget-evaluator.cjs.map +1 -0
  182. package/dist/services/budget-evaluator.d.cts +29 -0
  183. package/dist/services/budget-evaluator.d.cts.map +1 -0
  184. package/dist/services/budget-evaluator.d.ts +29 -0
  185. package/dist/services/budget-evaluator.d.ts.map +1 -0
  186. package/dist/services/budget-evaluator.js +142 -0
  187. package/dist/services/budget-evaluator.js.map +1 -0
  188. package/dist/services/config-manager.cjs +24 -10
  189. package/dist/services/config-manager.cjs.map +1 -1
  190. package/dist/services/config-manager.d.cts +6 -1
  191. package/dist/services/config-manager.d.cts.map +1 -1
  192. package/dist/services/config-manager.d.ts +6 -1
  193. package/dist/services/config-manager.d.ts.map +1 -1
  194. package/dist/services/config-manager.js +24 -10
  195. package/dist/services/config-manager.js.map +1 -1
  196. package/dist/services/file-loader.cjs +3 -6
  197. package/dist/services/file-loader.cjs.map +1 -1
  198. package/dist/services/file-loader.d.cts.map +1 -1
  199. package/dist/services/file-loader.d.ts.map +1 -1
  200. package/dist/services/file-loader.js +3 -6
  201. package/dist/services/file-loader.js.map +1 -1
  202. package/dist/services/profiler/profile-filter.cjs +116 -0
  203. package/dist/services/profiler/profile-filter.cjs.map +1 -0
  204. package/dist/services/profiler/profile-filter.d.cts +20 -0
  205. package/dist/services/profiler/profile-filter.d.cts.map +1 -0
  206. package/dist/services/profiler/profile-filter.d.ts +20 -0
  207. package/dist/services/profiler/profile-filter.d.ts.map +1 -0
  208. package/dist/services/profiler/profile-filter.js +112 -0
  209. package/dist/services/profiler/profile-filter.js.map +1 -0
  210. package/dist/services/profiler/profile-parser.cjs +139 -0
  211. package/dist/services/profiler/profile-parser.cjs.map +1 -0
  212. package/dist/services/profiler/profile-parser.d.cts +18 -0
  213. package/dist/services/profiler/profile-parser.d.cts.map +1 -0
  214. package/dist/services/profiler/profile-parser.d.ts +18 -0
  215. package/dist/services/profiler/profile-parser.d.ts.map +1 -0
  216. package/dist/services/profiler/profile-parser.js +132 -0
  217. package/dist/services/profiler/profile-parser.js.map +1 -0
  218. package/dist/services/profiler/profile-runner.cjs +90 -0
  219. package/dist/services/profiler/profile-runner.cjs.map +1 -0
  220. package/dist/services/profiler/profile-runner.d.cts +29 -0
  221. package/dist/services/profiler/profile-runner.d.cts.map +1 -0
  222. package/dist/services/profiler/profile-runner.d.ts +29 -0
  223. package/dist/services/profiler/profile-runner.d.ts.map +1 -0
  224. package/dist/services/profiler/profile-runner.js +86 -0
  225. package/dist/services/profiler/profile-runner.js.map +1 -0
  226. package/dist/services/progress-manager.cjs +10 -2
  227. package/dist/services/progress-manager.cjs.map +1 -1
  228. package/dist/services/progress-manager.d.cts +2 -0
  229. package/dist/services/progress-manager.d.cts.map +1 -1
  230. package/dist/services/progress-manager.d.ts +2 -0
  231. package/dist/services/progress-manager.d.ts.map +1 -1
  232. package/dist/services/progress-manager.js +10 -2
  233. package/dist/services/progress-manager.js.map +1 -1
  234. package/dist/services/reporter-registry.cjs +18 -24
  235. package/dist/services/reporter-registry.cjs.map +1 -1
  236. package/dist/services/reporter-registry.d.cts +18 -40
  237. package/dist/services/reporter-registry.d.cts.map +1 -1
  238. package/dist/services/reporter-registry.d.ts +18 -40
  239. package/dist/services/reporter-registry.d.ts.map +1 -1
  240. package/dist/services/reporter-registry.js +18 -24
  241. package/dist/services/reporter-registry.js.map +1 -1
  242. package/dist/types/budgets.cjs +8 -0
  243. package/dist/types/budgets.cjs.map +1 -0
  244. package/dist/types/budgets.d.cts +149 -0
  245. package/dist/types/budgets.d.cts.map +1 -0
  246. package/dist/types/budgets.d.ts +149 -0
  247. package/dist/types/budgets.d.ts.map +1 -0
  248. package/dist/types/budgets.js +7 -0
  249. package/dist/types/budgets.js.map +1 -0
  250. package/dist/types/cli.cjs +2 -11
  251. package/dist/types/cli.cjs.map +1 -1
  252. package/dist/types/cli.d.cts +3 -227
  253. package/dist/types/cli.d.cts.map +1 -1
  254. package/dist/types/cli.d.ts +3 -227
  255. package/dist/types/cli.d.ts.map +1 -1
  256. package/dist/types/cli.js +2 -11
  257. package/dist/types/cli.js.map +1 -1
  258. package/dist/types/core.cjs +6 -1
  259. package/dist/types/core.cjs.map +1 -1
  260. package/dist/types/core.d.cts +15 -2
  261. package/dist/types/core.d.cts.map +1 -1
  262. package/dist/types/core.d.ts +15 -2
  263. package/dist/types/core.d.ts.map +1 -1
  264. package/dist/types/core.js +2 -1
  265. package/dist/types/core.js.map +1 -1
  266. package/dist/types/index.cjs +5 -0
  267. package/dist/types/index.cjs.map +1 -1
  268. package/dist/types/index.d.cts +2 -0
  269. package/dist/types/index.d.cts.map +1 -1
  270. package/dist/types/index.d.ts +2 -0
  271. package/dist/types/index.d.ts.map +1 -1
  272. package/dist/types/index.js +2 -0
  273. package/dist/types/index.js.map +1 -1
  274. package/dist/types/interfaces.d.cts +19 -8
  275. package/dist/types/interfaces.d.cts.map +1 -1
  276. package/dist/types/interfaces.d.ts +19 -8
  277. package/dist/types/interfaces.d.ts.map +1 -1
  278. package/dist/types/profiler.cjs +11 -0
  279. package/dist/types/profiler.cjs.map +1 -0
  280. package/dist/types/profiler.d.cts +102 -0
  281. package/dist/types/profiler.d.cts.map +1 -0
  282. package/dist/types/profiler.d.ts +102 -0
  283. package/dist/types/profiler.d.ts.map +1 -0
  284. package/dist/types/profiler.js +10 -0
  285. package/dist/types/profiler.js.map +1 -0
  286. package/dist/types/utility.cjs.map +1 -1
  287. package/dist/types/utility.d.cts +0 -8
  288. package/dist/types/utility.d.cts.map +1 -1
  289. package/dist/types/utility.d.ts +0 -8
  290. package/dist/types/utility.d.ts.map +1 -1
  291. package/dist/types/utility.js.map +1 -1
  292. package/dist/utils/identifiers.cjs +32 -0
  293. package/dist/utils/identifiers.cjs.map +1 -0
  294. package/dist/utils/identifiers.d.cts +32 -0
  295. package/dist/utils/identifiers.d.cts.map +1 -0
  296. package/dist/utils/identifiers.d.ts +32 -0
  297. package/dist/utils/identifiers.d.ts.map +1 -0
  298. package/dist/utils/identifiers.js +27 -0
  299. package/dist/utils/identifiers.js.map +1 -0
  300. package/dist/utils/package.cjs +40 -0
  301. package/dist/utils/package.cjs.map +1 -0
  302. package/dist/utils/package.d.cts +15 -0
  303. package/dist/utils/package.d.cts.map +1 -0
  304. package/dist/utils/package.d.ts +15 -0
  305. package/dist/utils/package.d.ts.map +1 -0
  306. package/dist/utils/package.js +33 -0
  307. package/dist/utils/package.js.map +1 -0
  308. package/dist/utils/type-guards.cjs +48 -0
  309. package/dist/utils/type-guards.cjs.map +1 -0
  310. package/dist/utils/type-guards.d.cts +22 -0
  311. package/dist/utils/type-guards.d.cts.map +1 -0
  312. package/dist/utils/type-guards.d.ts +22 -0
  313. package/dist/utils/type-guards.d.ts.map +1 -0
  314. package/dist/utils/type-guards.js +43 -0
  315. package/dist/utils/type-guards.js.map +1 -0
  316. package/package.json +18 -19
  317. package/src/cli/commands/analyze.ts +101 -0
  318. package/src/cli/commands/baseline.ts +577 -0
  319. package/src/cli/commands/history.ts +1 -1
  320. package/src/cli/commands/init.ts +116 -194
  321. package/src/cli/commands/run.ts +183 -113
  322. package/src/cli/index.ts +425 -183
  323. package/src/config/budget-schema.ts +189 -0
  324. package/src/config/schema.ts +260 -1
  325. package/src/constants.ts +53 -1
  326. package/src/core/engine.ts +169 -22
  327. package/src/core/engines/accurate-engine.ts +195 -44
  328. package/src/core/engines/tinybench-engine.ts +3 -2
  329. package/src/core/output-path-resolver.ts +10 -2
  330. package/src/errors/base.ts +11 -2
  331. package/src/errors/budget.ts +38 -0
  332. package/src/errors/index.ts +3 -0
  333. package/src/index.ts +9 -0
  334. package/src/reporters/csv.ts +54 -25
  335. package/src/reporters/human.ts +434 -115
  336. package/src/reporters/json.ts +26 -71
  337. package/src/reporters/profile-human.ts +210 -0
  338. package/src/reporters/simple.ts +88 -54
  339. package/src/services/baseline-storage.ts +199 -0
  340. package/src/services/budget-evaluator.ts +182 -0
  341. package/src/services/config-manager.ts +24 -9
  342. package/src/services/file-loader.ts +3 -6
  343. package/src/services/profiler/profile-filter.ts +147 -0
  344. package/src/services/profiler/profile-parser.ts +194 -0
  345. package/src/services/profiler/profile-runner.ts +121 -0
  346. package/src/services/progress-manager.ts +12 -2
  347. package/src/services/reporter-registry.ts +46 -81
  348. package/src/types/budgets.ts +180 -0
  349. package/src/types/cli.ts +5 -238
  350. package/src/types/core.ts +52 -10
  351. package/src/types/index.ts +5 -0
  352. package/src/types/interfaces.ts +24 -6
  353. package/src/types/profiler.ts +135 -0
  354. package/src/types/utility.ts +0 -10
  355. package/src/utils/identifiers.ts +58 -0
  356. package/src/utils/package.ts +35 -0
  357. package/src/utils/type-guards.ts +51 -0
@@ -10,31 +10,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
10
10
  return (mod && mod.__esModule) ? mod : { "default": mod };
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.main = exports.cli = exports.ExitCodes = void 0;
13
+ exports.main = exports.cli = void 0;
14
14
  const node_fs_1 = require("node:fs");
15
15
  const node_url_1 = require("node:url");
16
16
  const yargs_1 = __importDefault(require("yargs"));
17
17
  const helpers_1 = require("yargs/helpers");
18
18
  const bootstrap_js_1 = require("../bootstrap.cjs");
19
+ const constants_js_1 = require("../constants.cjs");
19
20
  const index_js_1 = require("../core/engines/index.cjs");
21
+ const base_js_1 = require("../errors/base.cjs");
20
22
  const index_js_2 = require("../errors/index.cjs");
21
23
  const index_js_3 = require("../reporters/index.cjs");
22
24
  // Import commands
25
+ const analyze_js_1 = require("./commands/analyze.cjs");
26
+ const baseline_js_1 = require("./commands/baseline.cjs");
23
27
  const history_js_1 = require("./commands/history.cjs");
24
28
  const init_js_1 = require("./commands/init.cjs");
25
29
  const run_js_1 = require("./commands/run.cjs");
26
- /**
27
- * Exit codes for the CLI
28
- */
29
- exports.ExitCodes = {
30
- BENCHMARK_FAILURES: 1,
31
- CONFIG_ERROR: 2,
32
- DISCOVERY_ERROR: 3,
33
- RUNTIME_ERROR: 5,
34
- SUCCESS: 0,
35
- UNKNOWN_ERROR: 99,
36
- VALIDATION_ERROR: 4,
37
- };
38
30
  /**
39
31
  * Initialize and run the CLI
40
32
  */
@@ -43,7 +35,7 @@ const cli = (argv) => {
43
35
  setupSignalHandlers(abortController);
44
36
  (0, exports.main)(argv, abortController).catch((error) => {
45
37
  console.error('CLI error:', error);
46
- process.exit(exports.ExitCodes.UNKNOWN_ERROR);
38
+ process.exit(constants_js_1.ExitCodes.UNKNOWN_ERROR);
47
39
  });
48
40
  };
49
41
  exports.cli = cli;
@@ -65,34 +57,34 @@ const main = async (argv, abortController) => {
65
57
  })
66
58
  .option('verbose', {
67
59
  alias: 'v',
68
- default: false,
60
+ defaultDescription: String(run_js_1.RUN_COMMAND_DEFAULTS.verbose),
69
61
  description: 'Enable verbose output',
70
62
  global: true,
71
63
  type: 'boolean',
72
64
  })
73
65
  .option('no-color', {
74
- default: false,
66
+ defaultDescription: 'false',
75
67
  description: 'Disable colored output',
76
68
  global: true,
77
69
  type: 'boolean',
78
70
  })
79
71
  .option('progress', {
80
- default: true,
72
+ defaultDescription: 'true',
81
73
  description: 'Show animated progress bar',
82
74
  global: true,
83
75
  type: 'boolean',
84
76
  })
85
77
  .option('json', {
86
- default: false,
78
+ defaultDescription: 'false',
87
79
  description: 'Output results in JSON format',
88
80
  global: true,
89
81
  type: 'boolean',
90
82
  })
91
83
  .option('cwd', {
92
- default: process.cwd(),
93
84
  defaultDescription: '.',
94
85
  description: 'Working directory',
95
86
  global: true,
87
+ normalize: true,
96
88
  type: 'string',
97
89
  })
98
90
  .help()
@@ -100,136 +92,122 @@ const main = async (argv, abortController) => {
100
92
  .version()
101
93
  .alias('version', 'V')
102
94
  .strict()
103
- .demandCommand(1, 'You must specify a command')
95
+ .demandCommand(1)
104
96
  .recommendCommands()
105
97
  .completion()
106
98
  .wrap(Math.min(120, cli.terminalWidth()))
107
- .command(['$0 [pattern..]', 'run [pattern..]'], 'Run benchmark files', (yargs) => {
108
- return yargs
109
- .positional('pattern', {
110
- array: true,
111
- default: ['./bench/**/*.bench.{js,mjs,cjs,ts}'],
112
- describe: 'File paths, directory paths, or glob patterns for benchmark files',
113
- type: 'string',
114
- })
115
- .option('config', {
116
- alias: 'c',
117
- description: 'Path to configuration file',
118
- type: 'string',
119
- })
120
- .option('reporters', {
121
- alias: 'r',
122
- coerce: (value) => {
123
- // Handle comma-separated values
124
- if (Array.isArray(value)) {
125
- return value.flatMap((v) => v.split(',').map((s) => s.trim()));
126
- }
127
- return value.split(',').map((s) => s.trim());
128
- },
129
- default: ['human'],
130
- description: 'Output reporters to use (human,json,csv)',
131
- type: 'array',
132
- })
133
- .option('output', {
134
- alias: 'o',
135
- description: 'Output directory for reports',
136
- type: 'string',
137
- })
138
- .option('output-file', {
139
- alias: 'of',
140
- description: 'Custom filename for reporter output (use with single reporter only)',
141
- requiresArg: true,
142
- type: 'string',
143
- })
144
- .option('iterations', {
145
- alias: 'i',
146
- description: 'Number of iterations per benchmark',
147
- type: 'number',
148
- })
149
- .option('time', {
150
- alias: 't',
151
- description: 'Time budget per benchmark in milliseconds',
152
- type: 'number',
153
- })
154
- .option('warmup', {
155
- alias: 'w',
156
- description: 'Number of warmup iterations',
157
- type: 'number',
158
- })
159
- .option('limit-by', {
160
- choices: ['time', 'iterations', 'any', 'all'],
161
- description: 'How to limit benchmarks: time (time budget), iterations (sample count), any (either threshold), all (both thresholds)',
162
- type: 'string',
163
- })
164
- .option('bail', {
165
- alias: 'b',
166
- default: false,
167
- description: 'Stop on first failure',
168
- type: 'boolean',
169
- })
170
- .option('exclude', {
171
- coerce: (value) => {
172
- // Handle comma-separated values
173
- if (Array.isArray(value)) {
174
- return value.flatMap((v) => v.split(',').map((s) => s.trim()));
175
- }
176
- return value.split(',').map((s) => s.trim());
177
- },
178
- description: 'Exclude patterns (comma-separated)',
179
- type: 'array',
180
- })
181
- .option('timeout', {
182
- description: 'Timeout per benchmark in milliseconds',
183
- type: 'number',
184
- })
185
- .option('quiet', {
186
- alias: 'q',
187
- default: false,
188
- description: 'Minimal output',
189
- type: 'boolean',
190
- })
191
- .option('tags', {
192
- coerce: (value) => {
193
- // Handle comma-separated values
194
- if (Array.isArray(value)) {
195
- return value.flatMap((v) => v.split(',').map((s) => s.trim()));
196
- }
197
- return value.split(',').map((s) => s.trim());
198
- },
199
- description: 'Include only benchmarks with any of these tags',
200
- type: 'array',
201
- })
202
- .option('exclude-tags', {
203
- coerce: (value) => {
204
- // Handle comma-separated values
205
- if (Array.isArray(value)) {
206
- return value.flatMap((v) => v.split(',').map((s) => s.trim()));
207
- }
208
- return value.split(',').map((s) => s.trim());
209
- },
210
- description: 'Exclude benchmarks with any of these tags',
211
- type: 'array',
212
- })
213
- .option('engine', {
214
- alias: 'e',
215
- choices: ['tinybench', 'accurate'],
216
- default: 'tinybench',
217
- description: 'Benchmark engine: tinybench (default) or accurate (requires --allow-natives-syntax)',
218
- type: 'string',
219
- })
220
- .example([
221
- ['$0 run', 'Run benchmarks in current directory and bench/'],
222
- ['$0 run benchmarks/', 'Run all benchmarks in a directory'],
223
- ['$0 run src/perf/', 'Run benchmarks in specific directory'],
224
- ['$0 run "src/**/*.bench.js"', 'Run specific glob pattern'],
225
- ['$0 run file1.bench.js file2.bench.js', 'Run specific files'],
226
- ['$0 run benchmarks/ tests/perf/', 'Run multiple directories'],
227
- ['$0 run --reporters json,csv', 'Use multiple reporters'],
228
- ['$0 run --iterations 1000', 'Set iteration count'],
229
- ['$0 run --engine accurate', 'Use high-accuracy engine'],
230
- ['$0 run --bail', 'Stop on first failure'],
231
- ]);
232
- }, async (argv) => {
99
+ .command(['$0 [pattern..]', 'run [pattern..]'], 'Run benchmark files', (yargs) => yargs
100
+ .positional('pattern', {
101
+ array: true,
102
+ defaultDescription: '(auto-discovered from bench/ directory)',
103
+ describe: 'File paths, directory paths, or glob patterns for benchmark files',
104
+ type: 'string',
105
+ })
106
+ .option('config', {
107
+ alias: 'c',
108
+ description: 'Path to configuration file',
109
+ type: 'string',
110
+ })
111
+ .option('reporter', {
112
+ alias: 'r',
113
+ array: true,
114
+ choices: Object.values(constants_js_1.Reporters).sort(),
115
+ defaultDescription: constants_js_1.DEFAULT_REPORTER,
116
+ description: 'Output reporters to use (human,json,csv)',
117
+ type: 'string',
118
+ })
119
+ .option('output', {
120
+ alias: 'o',
121
+ description: 'Output directory for reports',
122
+ type: 'string',
123
+ })
124
+ .option('output-file', {
125
+ alias: ['of', 'file'],
126
+ description: 'Custom filename for reporter output (use with single reporter only)',
127
+ requiresArg: true,
128
+ type: 'string',
129
+ })
130
+ .option('iterations', {
131
+ alias: 'i',
132
+ description: 'Number of iterations per benchmark',
133
+ type: 'number',
134
+ })
135
+ .option('time', {
136
+ alias: 't',
137
+ description: 'Time budget per benchmark in milliseconds',
138
+ type: 'number',
139
+ })
140
+ .option('warmup', {
141
+ alias: ['w', 'warm'],
142
+ description: 'Number of warmup iterations',
143
+ type: 'number',
144
+ })
145
+ .option('limit-by', {
146
+ alias: ['l', 'limit'],
147
+ choices: ['time', 'iterations', 'any', 'all'],
148
+ description: 'How to limit benchmarks: time (time budget), iterations (sample count), any (either threshold), all (both thresholds)',
149
+ type: 'string',
150
+ })
151
+ .option('bail', {
152
+ alias: 'b',
153
+ defaultDescription: String(run_js_1.RUN_COMMAND_DEFAULTS.bail),
154
+ description: 'Stop on first failure',
155
+ type: 'boolean',
156
+ })
157
+ .option('exclude', {
158
+ alias: 'X',
159
+ array: true,
160
+ description: 'Exclude patterns (comma-separated)',
161
+ type: 'string',
162
+ })
163
+ .option('timeout', {
164
+ description: 'Timeout per benchmark in milliseconds',
165
+ type: 'number',
166
+ })
167
+ .option('quiet', {
168
+ alias: 'q',
169
+ defaultDescription: String(run_js_1.RUN_COMMAND_DEFAULTS.quiet),
170
+ description: 'Minimal output',
171
+ type: 'boolean',
172
+ })
173
+ .option('tag', {
174
+ array: true,
175
+ description: 'Include only benchmarks with any of these tags',
176
+ type: 'string',
177
+ })
178
+ .option('exclude-tag', {
179
+ alias: 'T',
180
+ array: true,
181
+ description: 'Exclude benchmarks with any of these tags',
182
+ type: 'string',
183
+ })
184
+ .option('engine', {
185
+ alias: 'e',
186
+ choices: Object.values(constants_js_1.Engines),
187
+ defaultDescription: constants_js_1.DEFAULT_ENGINE,
188
+ description: 'Benchmark engine: tinybench (default) or accurate (requires --allow-natives-syntax)',
189
+ type: 'string',
190
+ })
191
+ .example([
192
+ ['$0 run', 'Run benchmarks in current directory and bench/'],
193
+ ['$0 run benchmarks/', 'Run all benchmarks in a directory'],
194
+ ['$0 run src/perf/', 'Run benchmarks in specific directory'],
195
+ ['$0 run "src/**/*.bench.js"', 'Run specific glob pattern'],
196
+ ['$0 run file1.bench.js file2.bench.js', 'Run specific files'],
197
+ ['$0 run benchmarks/ tests/perf/', 'Run multiple directories'],
198
+ ['$0 run -r json -r csv', 'Use multiple reporters'],
199
+ ['$0 run --iterations 1000', 'Set iteration count'],
200
+ ['$0 run --engine accurate', 'Use high-accuracy engine'],
201
+ ['$0 run --bail', 'Stop on first failure'],
202
+ ])
203
+ .check((argv) => {
204
+ if (argv.reporter &&
205
+ argv.reporter.length > 1 &&
206
+ argv['output-file']) {
207
+ throw new Error('--output-file can only be used with a single reporter. Use --output <dir> for multiple reporters.');
208
+ }
209
+ return true;
210
+ }), async (argv) => {
233
211
  const context = await createCliContext(argv, abortController, argv.engine);
234
212
  const exitCode = await (0, run_js_1.handleRunCommand)(context, {
235
213
  bail: argv.bail,
@@ -237,7 +215,7 @@ const main = async (argv, abortController) => {
237
215
  cwd: argv.cwd,
238
216
  engine: argv.engine,
239
217
  exclude: argv.exclude,
240
- excludeTags: argv['exclude-tags'],
218
+ excludeTags: argv['exclude-tag'],
241
219
  iterations: argv.iterations,
242
220
  json: argv.json,
243
221
  noColor: argv.noColor,
@@ -246,8 +224,8 @@ const main = async (argv, abortController) => {
246
224
  pattern: argv.pattern,
247
225
  progress: argv.progress,
248
226
  quiet: argv.quiet,
249
- reporters: argv.reporters,
250
- tags: argv.tags,
227
+ reporters: argv.reporter,
228
+ tags: argv.tag,
251
229
  time: argv.time,
252
230
  timeout: argv.timeout,
253
231
  verbose: argv.verbose,
@@ -255,344 +233,503 @@ const main = async (argv, abortController) => {
255
233
  });
256
234
  process.exit(exitCode);
257
235
  })
258
- .command('history', 'View and manage benchmark history', (yargs) => {
236
+ .command('history', 'View and manage benchmark history', (yargs) => yargs
237
+ .command('list', 'List recent benchmark runs', (yargs) => yargs
238
+ .option('since', {
239
+ description: 'Show runs since date (ISO 8601 or relative like "1 week ago")',
240
+ type: 'string',
241
+ })
242
+ .option('until', {
243
+ description: 'Show runs until date (ISO 8601 or relative like "1 day ago")',
244
+ type: 'string',
245
+ })
246
+ .option('pattern', {
247
+ description: 'Filter by benchmark name pattern',
248
+ type: 'string',
249
+ })
250
+ .option('tag', {
251
+ alias: 't',
252
+ array: true,
253
+ description: 'Filter by tags (comma-separated)',
254
+ type: 'string',
255
+ })
256
+ .option('limit', {
257
+ defaultDescription: '10',
258
+ description: 'Maximum number of results',
259
+ type: 'number',
260
+ })
261
+ .option('format', {
262
+ choices: ['human', 'json', 'csv'],
263
+ defaultDescription: 'human',
264
+ description: 'Output format',
265
+ type: 'string',
266
+ })
267
+ .example([
268
+ ['$0 history list', 'List recent benchmark runs'],
269
+ [
270
+ '$0 history list --since "1 week ago"',
271
+ 'List runs from last week',
272
+ ],
273
+ ['$0 history list --limit 20', 'List 20 most recent runs'],
274
+ ['$0 history list --format json', 'List runs in JSON format'],
275
+ ]), async (argv) => {
276
+ const context = await createCliContext(argv, abortController);
277
+ const exitCode = await (0, history_js_1.handleListCommand)(context, {
278
+ cwd: argv.cwd,
279
+ format: argv.format,
280
+ limit: argv.limit,
281
+ pattern: argv.pattern,
282
+ since: argv.since,
283
+ tags: argv.tag,
284
+ until: argv.until,
285
+ verbose: argv.verbose,
286
+ });
287
+ process.exit(exitCode);
288
+ })
289
+ .command('show <run-id>', 'Show detailed results for a specific run', (yargs) => yargs
290
+ .positional('run-id', {
291
+ demandOption: true,
292
+ describe: 'ID of the benchmark run to show',
293
+ type: 'string',
294
+ })
295
+ .option('format', {
296
+ choices: ['human', 'json', 'csv'],
297
+ defaultDescription: 'human',
298
+ description: 'Output format',
299
+ type: 'string',
300
+ })
301
+ .example([
302
+ [
303
+ '$0 history show abc123',
304
+ 'Show detailed results for run abc123',
305
+ ],
306
+ [
307
+ '$0 history show abc123 --format json',
308
+ 'Show run in JSON format',
309
+ ],
310
+ ]), async (argv) => {
311
+ const context = await createCliContext(argv, abortController);
312
+ const exitCode = await (0, history_js_1.handleShowCommand)(context, {
313
+ cwd: argv.cwd,
314
+ format: argv.format,
315
+ runId: argv['run-id'],
316
+ verbose: argv.verbose,
317
+ });
318
+ process.exit(exitCode);
319
+ })
320
+ .command('compare <run-id1> <run-id2>', 'Compare two benchmark runs', (yargs) => yargs
321
+ .positional('run-id1', {
322
+ demandOption: true,
323
+ describe: 'ID of the first benchmark run',
324
+ type: 'string',
325
+ })
326
+ .positional('run-id2', {
327
+ demandOption: true,
328
+ describe: 'ID of the second benchmark run',
329
+ type: 'string',
330
+ })
331
+ .option('format', {
332
+ choices: ['human', 'json'],
333
+ defaultDescription: 'human',
334
+ description: 'Output format',
335
+ type: 'string',
336
+ })
337
+ .example([
338
+ ['$0 history compare abc123 def456', 'Compare two runs'],
339
+ [
340
+ '$0 history compare abc123 def456 --format json',
341
+ 'Compare in JSON format',
342
+ ],
343
+ ]), async (argv) => {
344
+ const context = await createCliContext(argv, abortController);
345
+ const exitCode = await (0, history_js_1.handleCompareCommand)(context, {
346
+ cwd: argv.cwd,
347
+ format: argv.format,
348
+ runId1: argv['run-id1'],
349
+ runId2: argv['run-id2'],
350
+ verbose: argv.verbose,
351
+ });
352
+ process.exit(exitCode);
353
+ })
354
+ .command('trends [pattern]', 'Show performance trends over time', (yargs) => yargs
355
+ .positional('pattern', {
356
+ describe: 'Filter by benchmark name pattern',
357
+ type: 'string',
358
+ })
359
+ .option('since', {
360
+ description: 'Show trends since date (ISO 8601 or relative like "1 week ago")',
361
+ type: 'string',
362
+ })
363
+ .option('until', {
364
+ description: 'Show trends until date (ISO 8601 or relative like "1 day ago")',
365
+ type: 'string',
366
+ })
367
+ .option('tag', {
368
+ alias: 't',
369
+ array: true,
370
+ description: 'Filter by tags (comma-separated)',
371
+ type: 'string',
372
+ })
373
+ .option('limit', {
374
+ description: 'Maximum number of runs to analyze',
375
+ type: 'number',
376
+ })
377
+ .option('all', {
378
+ alias: 'a',
379
+ defaultDescription: 'false',
380
+ description: 'Analyze all runs (ignore limit)',
381
+ type: 'boolean',
382
+ })
383
+ .option('format', {
384
+ choices: ['human', 'json'],
385
+ defaultDescription: 'human',
386
+ description: 'Output format',
387
+ type: 'string',
388
+ })
389
+ .example([
390
+ [
391
+ '$0 history trends',
392
+ 'Show performance trends for all benchmarks',
393
+ ],
394
+ [
395
+ '$0 history trends --since "1 month ago"',
396
+ 'Show trends from last month',
397
+ ],
398
+ [
399
+ '$0 history trends "array-*"',
400
+ 'Show trends for array benchmarks',
401
+ ],
402
+ [
403
+ '$0 history trends --format json',
404
+ 'Output trends in JSON format',
405
+ ],
406
+ ]), async (argv) => {
407
+ const context = await createCliContext(argv, abortController);
408
+ const exitCode = await (0, history_js_1.handleTrendsCommand)(context, {
409
+ all: argv.all,
410
+ cwd: argv.cwd,
411
+ format: argv.format,
412
+ limit: argv.limit,
413
+ pattern: argv.pattern,
414
+ since: argv.since,
415
+ tags: argv.tag,
416
+ until: argv.until,
417
+ verbose: argv.verbose,
418
+ });
419
+ process.exit(exitCode);
420
+ })
421
+ .command('clean', 'Clean up old benchmark history', (yargs) => yargs
422
+ .option('max-age', {
423
+ description: 'Remove runs older than this many days',
424
+ type: 'number',
425
+ })
426
+ .option('max-runs', {
427
+ description: 'Keep only this many most recent runs',
428
+ type: 'number',
429
+ })
430
+ .option('max-size', {
431
+ description: 'Keep history under this size in bytes',
432
+ type: 'number',
433
+ })
434
+ .option('yes', {
435
+ alias: 'y',
436
+ description: 'Confirm cleanup without prompting',
437
+ type: 'boolean',
438
+ })
439
+ .option('quiet', {
440
+ default: false,
441
+ description: 'Minimal output',
442
+ type: 'boolean',
443
+ })
444
+ .check((argv) => {
445
+ if (!argv['max-age'] &&
446
+ !argv['max-runs'] &&
447
+ !argv['max-size']) {
448
+ throw new Error('At least one cleanup criterion must be specified (--max-age, --max-runs, or --max-size)');
449
+ }
450
+ return true;
451
+ })
452
+ .example([
453
+ [
454
+ '$0 history clean --max-runs 50 --yes',
455
+ 'Keep only latest 50 runs',
456
+ ],
457
+ [
458
+ '$0 history clean --max-age 30',
459
+ 'Preview removing runs older than 30 days',
460
+ ],
461
+ [
462
+ '$0 history clean --max-size 10485760',
463
+ 'Keep history under 10MB',
464
+ ],
465
+ ]), async (argv) => {
466
+ const context = await createCliContext(argv, abortController);
467
+ const exitCode = await (0, history_js_1.handleCleanCommand)(context, {
468
+ confirm: argv.yes,
469
+ cwd: argv.cwd,
470
+ maxAge: argv['max-age'],
471
+ maxRuns: argv['max-runs'],
472
+ maxSize: argv['max-size'],
473
+ quiet: argv.quiet,
474
+ verbose: argv.verbose,
475
+ });
476
+ process.exit(exitCode);
477
+ })
478
+ .command('export', 'Export benchmark history to a file', (yargs) => yargs
479
+ .option('format', {
480
+ choices: ['json', 'csv'],
481
+ defaultDescription: 'json',
482
+ description: 'Export format',
483
+ type: 'string',
484
+ })
485
+ .option('output', {
486
+ alias: 'o',
487
+ demandOption: true,
488
+ description: 'Output file path',
489
+ type: 'string',
490
+ })
491
+ .option('since', {
492
+ description: 'Export runs since date',
493
+ type: 'string',
494
+ })
495
+ .option('until', {
496
+ description: 'Export runs until date',
497
+ type: 'string',
498
+ })
499
+ .example([
500
+ [
501
+ '$0 history export -o history.json',
502
+ 'Export all history to JSON',
503
+ ],
504
+ [
505
+ '$0 history export -o history.csv --format csv',
506
+ 'Export to CSV',
507
+ ],
508
+ [
509
+ '$0 history export -o recent.json --since "1 week ago"',
510
+ 'Export recent runs',
511
+ ],
512
+ ]), async (argv) => {
513
+ const context = await createCliContext(argv, abortController);
514
+ const exitCode = await (0, history_js_1.handleExportCommand)(context, {
515
+ cwd: argv.cwd,
516
+ format: argv.format,
517
+ outputPath: argv.output,
518
+ quiet: Boolean(argv.quiet),
519
+ since: argv.since,
520
+ until: argv.until,
521
+ verbose: argv.verbose,
522
+ });
523
+ process.exitCode = exitCode;
524
+ })
525
+ .demandCommand(1, 'You must specify a history subcommand')
526
+ .strict()
527
+ .example([
528
+ ['$0 history list', 'List recent benchmark runs'],
529
+ ['$0 history show <run-id>', 'Show detailed results'],
530
+ ['$0 history compare <run-id1> <run-id2>', 'Compare two runs'],
531
+ ['$0 history trends', 'Show performance trends'],
532
+ ['$0 history clean --max-runs 50', 'Keep only latest 50 runs'],
533
+ ['$0 history export -o data.json', 'Export history'],
534
+ ]))
535
+ .command('baseline', 'Manage performance baselines', (yargs) => {
259
536
  return yargs
260
- .command('list', 'List recent benchmark runs', (yargs) => {
537
+ .command('set <name>', 'Save a benchmark run as a baseline', (yargs) => {
261
538
  return yargs
262
- .option('since', {
263
- description: 'Show runs since date (ISO 8601 or relative like "1 week ago")',
539
+ .positional('name', {
540
+ describe: 'Name for the baseline',
264
541
  type: 'string',
265
542
  })
266
- .option('until', {
267
- description: 'Show runs until date (ISO 8601 or relative like "1 day ago")',
543
+ .option('run-id', {
544
+ description: 'Specific run ID to save (default: most recent)',
268
545
  type: 'string',
269
546
  })
270
- .option('pattern', {
271
- description: 'Filter by benchmark name pattern',
547
+ .option('commit', {
548
+ description: 'Git commit SHA (40 characters)',
272
549
  type: 'string',
273
550
  })
274
- .option('tags', {
275
- description: 'Filter by tags (comma-separated)',
276
- type: 'array',
277
- })
278
- .option('limit', {
279
- default: 10,
280
- description: 'Maximum number of results',
281
- type: 'number',
282
- })
283
- .option('format', {
284
- choices: ['human', 'json', 'csv'],
285
- default: 'human',
286
- description: 'Output format',
551
+ .option('branch', {
552
+ description: 'Git branch name',
287
553
  type: 'string',
288
554
  })
289
- .example([
290
- ['$0 history list', 'List recent benchmark runs'],
291
- [
292
- '$0 history list --since "1 week ago"',
293
- 'List runs from last week',
294
- ],
295
- ['$0 history list --limit 20', 'List 20 most recent runs'],
296
- ['$0 history list --format json', 'List runs in JSON format'],
297
- ]);
298
- }, async (argv) => {
299
- const context = await createCliContext(argv, abortController);
300
- const exitCode = await (0, history_js_1.handleListCommand)(context, {
301
- cwd: argv.cwd,
302
- format: argv.format,
303
- limit: argv.limit,
304
- pattern: argv.pattern,
305
- quiet: Boolean(argv.quiet),
306
- since: argv.since,
307
- tags: argv.tags,
308
- until: argv.until,
309
- verbose: argv.verbose,
310
- });
311
- process.exit(exitCode);
312
- })
313
- .command('show <run-id>', 'Show detailed results for a specific run', (yargs) => {
314
- return yargs
315
- .positional('run-id', {
316
- describe: 'ID of the benchmark run to show',
317
- type: 'string',
318
- })
319
- .option('format', {
320
- choices: ['human', 'json', 'csv'],
321
- default: 'human',
322
- description: 'Output format',
323
- type: 'string',
555
+ .option('default', {
556
+ defaultDescription: 'false',
557
+ description: 'Set as default baseline',
558
+ type: 'boolean',
324
559
  })
325
560
  .example([
326
561
  [
327
- '$0 history show abc123',
328
- 'Show detailed results for run abc123',
562
+ '$0 baseline set production-v1.0',
563
+ 'Save most recent run as baseline',
329
564
  ],
565
+ ['$0 baseline set v1.0 --default', 'Save and set as default'],
330
566
  [
331
- '$0 history show abc123 --format json',
332
- 'Show run in JSON format',
567
+ '$0 baseline set v1.0 --commit abc123...',
568
+ 'Save with commit info',
333
569
  ],
334
570
  ]);
335
571
  }, async (argv) => {
336
572
  const context = await createCliContext(argv, abortController);
337
- const exitCode = await (0, history_js_1.handleShowCommand)(context, {
573
+ const exitCode = await (0, baseline_js_1.handleSetCommand)(context, {
574
+ branch: argv.branch,
575
+ commit: argv.commit,
338
576
  cwd: argv.cwd,
339
- format: argv.format,
577
+ default: argv.default,
578
+ name: String(argv.name),
340
579
  quiet: Boolean(argv.quiet),
341
- runId: String(argv['run-id']),
580
+ runId: argv['run-id'],
342
581
  verbose: argv.verbose,
343
582
  });
344
583
  process.exit(exitCode);
345
584
  })
346
- .command('compare <run-id1> <run-id2>', 'Compare two benchmark runs', (yargs) => {
585
+ .command('list', 'List all saved baselines', (yargs) => {
347
586
  return yargs
348
- .positional('run-id1', {
349
- describe: 'ID of the first benchmark run',
350
- type: 'string',
351
- })
352
- .positional('run-id2', {
353
- describe: 'ID of the second benchmark run',
354
- type: 'string',
355
- })
356
587
  .option('format', {
357
588
  choices: ['human', 'json'],
358
- default: 'human',
589
+ defaultDescription: 'human',
359
590
  description: 'Output format',
360
591
  type: 'string',
361
592
  })
362
593
  .example([
363
- ['$0 history compare abc123 def456', 'Compare two runs'],
364
- [
365
- '$0 history compare abc123 def456 --format json',
366
- 'Compare in JSON format',
367
- ],
594
+ ['$0 baseline list', 'List all baselines'],
595
+ ['$0 baseline list --format json', 'List in JSON format'],
368
596
  ]);
369
597
  }, async (argv) => {
370
598
  const context = await createCliContext(argv, abortController);
371
- const exitCode = await (0, history_js_1.handleCompareCommand)(context, {
599
+ const exitCode = await (0, baseline_js_1.handleListCommand)(context, {
372
600
  cwd: argv.cwd,
373
601
  format: argv.format,
374
602
  quiet: Boolean(argv.quiet),
375
- runId1: String(argv['run-id1']),
376
- runId2: String(argv['run-id2']),
377
603
  verbose: argv.verbose,
378
604
  });
379
605
  process.exit(exitCode);
380
606
  })
381
- .command('trends [pattern]', 'Show performance trends over time', (yargs) => {
607
+ .command('show <name>', 'Show baseline details', (yargs) => {
382
608
  return yargs
383
- .positional('pattern', {
384
- describe: 'Filter by benchmark name pattern',
385
- type: 'string',
386
- })
387
- .option('since', {
388
- description: 'Show trends since date (ISO 8601 or relative like "1 week ago")',
609
+ .positional('name', {
610
+ describe: 'Baseline name to show',
389
611
  type: 'string',
390
- })
391
- .option('until', {
392
- description: 'Show trends until date (ISO 8601 or relative like "1 day ago")',
393
- type: 'string',
394
- })
395
- .option('tags', {
396
- description: 'Filter by tags (comma-separated)',
397
- type: 'array',
398
- })
399
- .option('limit', {
400
- description: 'Maximum number of runs to analyze',
401
- type: 'number',
402
- })
403
- .option('all', {
404
- alias: 'a',
405
- default: false,
406
- description: 'Analyze all runs (ignore limit)',
407
- type: 'boolean',
408
612
  })
409
613
  .option('format', {
410
614
  choices: ['human', 'json'],
411
- default: 'human',
615
+ defaultDescription: 'human',
412
616
  description: 'Output format',
413
617
  type: 'string',
414
618
  })
415
619
  .example([
620
+ ['$0 baseline show production-v1.0', 'Show baseline details'],
416
621
  [
417
- '$0 history trends',
418
- 'Show performance trends for all benchmarks',
419
- ],
420
- [
421
- '$0 history trends --since "1 month ago"',
422
- 'Show trends from last month',
423
- ],
424
- [
425
- '$0 history trends "array-*"',
426
- 'Show trends for array benchmarks',
427
- ],
428
- [
429
- '$0 history trends --format json',
430
- 'Output trends in JSON format',
622
+ '$0 baseline show v1.0 --format json',
623
+ 'Show in JSON format',
431
624
  ],
432
625
  ]);
433
626
  }, async (argv) => {
434
627
  const context = await createCliContext(argv, abortController);
435
- const exitCode = await (0, history_js_1.handleTrendsCommand)(context, {
436
- all: Boolean(argv.all),
628
+ const exitCode = await (0, baseline_js_1.handleShowCommand)(context, {
437
629
  cwd: argv.cwd,
438
630
  format: argv.format,
439
- limit: argv.limit,
440
- pattern: argv.pattern,
631
+ name: String(argv.name),
441
632
  quiet: Boolean(argv.quiet),
442
- since: argv.since,
443
- tags: argv.tags,
444
- until: argv.until,
445
633
  verbose: argv.verbose,
446
634
  });
447
635
  process.exit(exitCode);
448
636
  })
449
- .command('clean', 'Clean up old benchmark history', (yargs) => {
637
+ .command('delete <name>', 'Delete a baseline', (yargs) => {
450
638
  return yargs
451
- .option('max-age', {
452
- description: 'Remove runs older than this many days',
453
- type: 'number',
454
- })
455
- .option('max-runs', {
456
- description: 'Keep only this many most recent runs',
457
- type: 'number',
458
- })
459
- .option('max-size', {
460
- description: 'Keep history under this size in bytes',
461
- type: 'number',
462
- })
463
- .option('confirm', {
464
- default: false,
465
- description: 'Confirm cleanup without prompting',
466
- type: 'boolean',
467
- })
468
- .check((argv) => {
469
- if (!argv['max-age'] &&
470
- !argv['max-runs'] &&
471
- !argv['max-size']) {
472
- throw new Error('At least one cleanup criterion must be specified (--max-age, --max-runs, or --max-size)');
473
- }
474
- return true;
639
+ .positional('name', {
640
+ describe: 'Baseline name to delete',
641
+ type: 'string',
475
642
  })
476
643
  .example([
477
- [
478
- '$0 history clean --max-runs 50 --confirm',
479
- 'Keep only latest 50 runs',
480
- ],
481
- [
482
- '$0 history clean --max-age 30',
483
- 'Preview removing runs older than 30 days',
484
- ],
485
- [
486
- '$0 history clean --max-size 10485760',
487
- 'Keep history under 10MB',
488
- ],
644
+ ['$0 baseline delete old-baseline', 'Delete a baseline'],
489
645
  ]);
490
646
  }, async (argv) => {
491
647
  const context = await createCliContext(argv, abortController);
492
- const exitCode = await (0, history_js_1.handleCleanCommand)(context, {
493
- confirm: argv.confirm,
648
+ const exitCode = await (0, baseline_js_1.handleDeleteCommand)(context, {
494
649
  cwd: argv.cwd,
495
- maxAge: argv['max-age'],
496
- maxRuns: argv['max-runs'],
497
- maxSize: argv['max-size'],
650
+ name: String(argv.name),
498
651
  quiet: Boolean(argv.quiet),
499
652
  verbose: argv.verbose,
500
653
  });
501
654
  process.exit(exitCode);
502
655
  })
503
- .command('export', 'Export benchmark history to a file', (yargs) => {
656
+ .command('analyze', 'Analyze history and suggest performance budgets', (yargs) => {
504
657
  return yargs
505
- .option('format', {
506
- choices: ['json', 'csv'],
507
- default: 'json',
508
- description: 'Export format',
509
- type: 'string',
510
- })
511
- .option('output', {
512
- alias: 'o',
513
- demandOption: true,
514
- description: 'Output file path',
515
- type: 'string',
516
- })
517
- .option('since', {
518
- description: 'Export runs since date',
519
- type: 'string',
658
+ .option('runs', {
659
+ defaultDescription: '10',
660
+ description: 'Number of recent runs to analyze',
661
+ type: 'number',
520
662
  })
521
- .option('until', {
522
- description: 'Export runs until date',
523
- type: 'string',
663
+ .option('confidence', {
664
+ defaultDescription: '0.95',
665
+ description: 'Confidence level (0.5-0.999, default 0.95)',
666
+ type: 'number',
524
667
  })
525
668
  .example([
526
669
  [
527
- '$0 history export -o history.json',
528
- 'Export all history to JSON',
529
- ],
530
- [
531
- '$0 history export -o history.csv --format csv',
532
- 'Export to CSV',
670
+ '$0 baseline analyze',
671
+ 'Analyze last 10 runs with 95% confidence',
533
672
  ],
673
+ ['$0 baseline analyze --runs 20', 'Analyze last 20 runs'],
534
674
  [
535
- '$0 history export -o recent.json --since "1 week ago"',
536
- 'Export recent runs',
675
+ '$0 baseline analyze --confidence 0.90',
676
+ 'Use 90% confidence level',
537
677
  ],
538
678
  ]);
539
679
  }, async (argv) => {
540
680
  const context = await createCliContext(argv, abortController);
541
- const exitCode = await (0, history_js_1.handleExportCommand)(context, {
681
+ const exitCode = await (0, baseline_js_1.handleAnalyzeCommand)(context, {
682
+ confidence: argv.confidence,
542
683
  cwd: argv.cwd,
543
- format: argv.format,
544
- outputPath: argv.output,
545
684
  quiet: Boolean(argv.quiet),
546
- since: argv.since,
547
- until: argv.until,
685
+ runs: argv.runs,
548
686
  verbose: argv.verbose,
549
687
  });
550
688
  process.exit(exitCode);
551
689
  })
552
- .demandCommand(1, 'You must specify a history subcommand')
690
+ .demandCommand(1, 'You must specify a baseline subcommand')
553
691
  .strict()
554
692
  .example([
555
- ['$0 history list', 'List recent benchmark runs'],
556
- ['$0 history show <run-id>', 'Show detailed results'],
557
- ['$0 history compare <run-id1> <run-id2>', 'Compare two runs'],
558
- ['$0 history trends', 'Show performance trends'],
559
- ['$0 history clean --max-runs 50', 'Keep only latest 50 runs'],
560
- ['$0 history export -o data.json', 'Export history'],
693
+ ['$0 baseline set production-v1.0', 'Save current run as baseline'],
694
+ ['$0 baseline list', 'List all baselines'],
695
+ ['$0 baseline show production-v1.0', 'Show baseline details'],
696
+ ['$0 baseline delete old-baseline', 'Delete a baseline'],
697
+ ['$0 baseline analyze', 'Suggest budgets from history'],
561
698
  ]);
562
699
  })
563
700
  .command('init [type]', 'Initialize a new benchmark project', (yargs) => {
564
701
  return yargs
565
702
  .positional('type', {
566
703
  choices: ['basic', 'advanced', 'library'],
567
- default: 'basic',
704
+ defaultDescription: 'basic',
568
705
  describe: 'Type of project to initialize',
569
706
  type: 'string',
570
707
  })
571
708
  .option('examples', {
572
- default: true,
709
+ defaultDescription: 'true',
573
710
  description: 'Include example benchmark files',
574
711
  type: 'boolean',
575
712
  })
576
713
  .option('config-type', {
577
714
  choices: ['json', 'yaml', 'js', 'ts'],
578
- default: 'json',
715
+ defaultDescription: 'json',
579
716
  description: 'Configuration file format',
580
717
  type: 'string',
581
718
  })
582
719
  .option('force', {
583
- default: false,
720
+ defaultDescription: 'false',
584
721
  description: 'Overwrite existing files',
585
722
  type: 'boolean',
586
723
  })
587
724
  .option('yes', {
588
725
  alias: 'y',
589
- default: false,
726
+ defaultDescription: 'false',
590
727
  description: 'Accept all prompts automatically',
591
728
  type: 'boolean',
592
729
  })
593
730
  .option('quiet', {
594
731
  alias: 'q',
595
- default: false,
732
+ defaultDescription: 'false',
596
733
  description: 'Minimal output',
597
734
  type: 'boolean',
598
735
  })
@@ -614,33 +751,87 @@ const main = async (argv, abortController) => {
614
751
  cwd: argv.cwd,
615
752
  examples: argv.examples,
616
753
  force: argv.force,
617
- quiet: Boolean(argv.quiet),
754
+ quiet: argv.quiet,
618
755
  type: argv.type,
619
756
  verbose: argv.verbose,
620
757
  yes: argv.yes,
621
758
  });
622
- process.exit(exitCode);
759
+ process.exitCode = exitCode;
623
760
  })
624
- .fail((msg, err, yargsInstance) => {
761
+ .command(['analyze [command]', 'profile [command]'], 'Analyze code execution and identify benchmark candidates', (yargs) => {
762
+ return yargs
763
+ .positional('command', {
764
+ description: 'Command to analyze (e.g., "npm test")',
765
+ type: 'string',
766
+ })
767
+ .option('input', {
768
+ alias: 'i',
769
+ description: 'Path to existing *.cpuprofile file',
770
+ type: 'string',
771
+ })
772
+ .option('filter-file', {
773
+ description: 'Filter functions by file glob pattern',
774
+ type: 'string',
775
+ })
776
+ .option('min-percent', {
777
+ alias: ['m', 'min'],
778
+ default: 0.5,
779
+ description: 'Minimum execution percentage to show',
780
+ type: 'number',
781
+ })
782
+ .option('top', {
783
+ alias: 'n',
784
+ default: 25,
785
+ description: 'Number of top functions to show',
786
+ type: 'number',
787
+ })
788
+ .option('group-by-file', {
789
+ default: false,
790
+ description: 'Group results by file',
791
+ type: 'boolean',
792
+ })
793
+ .check((argv) => {
794
+ if (!argv.command && !argv.input) {
795
+ throw new Error('Either [command] or --input must be provided');
796
+ }
797
+ return true;
798
+ });
799
+ }, async (argv) => {
800
+ // Context not needed for analyze command currently
801
+ const context = {};
802
+ const options = {
803
+ color: !argv.noColor,
804
+ command: argv.command,
805
+ cwd: argv.cwd || process.cwd(),
806
+ filterFile: argv.filterFile,
807
+ groupByFile: argv.groupByFile,
808
+ input: argv.input,
809
+ minPercent: argv.minPercent,
810
+ top: argv.top,
811
+ };
812
+ process.exitCode = await (0, analyze_js_1.handleAnalyzeCommand)(context, options);
813
+ })
814
+ .fail((msg, err, yargs) => {
625
815
  if (err) {
626
816
  console.error('Error:', err.message);
627
817
  if (process.env.DEBUG) {
628
818
  console.error(err.stack);
629
819
  }
630
820
  // Show help for file discovery errors (similar to usage errors)
631
- if (err.name === 'FileDiscoveryError') {
821
+ if ((0, index_js_2.isModestBenchError)(err) &&
822
+ err.code === constants_js_1.ErrorCodes.FILE_DISCOVERY_FAILED) {
632
823
  console.error();
633
- yargsInstance.showHelp();
634
- process.exit(exports.ExitCodes.DISCOVERY_ERROR);
824
+ yargs.showHelp();
825
+ process.exit(constants_js_1.ExitCodes.DISCOVERY_ERROR);
635
826
  }
636
- process.exit(exports.ExitCodes.RUNTIME_ERROR);
827
+ process.exit(constants_js_1.ExitCodes.RUNTIME_ERROR);
637
828
  }
638
829
  else {
639
830
  // Show help for usage errors (unknown arguments, etc.)
640
831
  console.error(msg);
641
832
  console.error();
642
- yargsInstance.showHelp();
643
- process.exit(exports.ExitCodes.CONFIG_ERROR);
833
+ yargs.showHelp();
834
+ process.exit(constants_js_1.ExitCodes.CONFIG_ERROR);
644
835
  }
645
836
  })
646
837
  .parse();
@@ -650,22 +841,22 @@ const main = async (argv, abortController) => {
650
841
  if (process.env.DEBUG) {
651
842
  console.error(error);
652
843
  }
653
- process.exit(exports.ExitCodes.UNKNOWN_ERROR);
844
+ process.exit(constants_js_1.ExitCodes.UNKNOWN_ERROR);
654
845
  }
655
846
  };
656
847
  exports.main = main;
657
848
  /**
658
849
  * Create CLI context with dependency injection
659
850
  */
660
- const createCliContext = async (options, abortController, engineType = 'tinybench') => {
851
+ const createCliContext = async (options, abortController, engineType = constants_js_1.DEFAULT_ENGINE) => {
661
852
  try {
662
853
  const dependencies = (0, bootstrap_js_1.bootstrap)();
663
854
  // Select engine based on type
664
- const engine = engineType === 'accurate'
855
+ const engine = engineType === constants_js_1.Engines.ACCURATE
665
856
  ? new index_js_1.AccurateEngine(dependencies)
666
857
  : new index_js_1.TinybenchEngine(dependencies);
667
858
  // Register built-in reporters
668
- engine.registerReporter('human', new index_js_3.HumanReporter({
859
+ engine.registerReporter(constants_js_1.Reporters.HUMAN, new index_js_3.HumanReporter({
669
860
  color: !options.noColor,
670
861
  verbose: options.verbose,
671
862
  }));
@@ -691,7 +882,7 @@ const createCliContext = async (options, abortController, engineType = 'tinybenc
691
882
  }
692
883
  catch (error) {
693
884
  console.error('Failed to initialize ModestBench:', error instanceof Error ? error.message : String(error));
694
- process.exit(exports.ExitCodes.CONFIG_ERROR);
885
+ process.exit(constants_js_1.ExitCodes.CONFIG_ERROR);
695
886
  }
696
887
  };
697
888
  /**
@@ -712,26 +903,24 @@ const setupSignalHandlers = (abortController) => {
712
903
  setTimeout(() => {
713
904
  console.log('\nBenchmark aborted.');
714
905
  process.exit(computeExitCode(signal));
715
- }, 100);
906
+ }, constants_js_1.ABORT_TIMEOUT);
716
907
  };
717
- process.on('SIGINT', () => handleSignal('SIGINT'));
718
- process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
719
- process.on('SIGTERM', () => handleSignal('SIGTERM'));
720
- process.once('uncaughtException', (error) => {
908
+ process
909
+ .once('SIGINT', handleSignal)
910
+ .once('SIGQUIT', handleSignal)
911
+ .once('SIGTERM', handleSignal)
912
+ .once('uncaughtException', (error) => {
721
913
  // Wrap non-ModestBench errors with UnknownError
722
914
  const wrappedError = (0, index_js_2.isModestBenchError)(error)
723
915
  ? error
724
- : new index_js_2.UnknownError(error instanceof Error ? error.message : String(error), { cause: error });
725
- console.error(wrappedError.toString());
726
- process.exit(exports.ExitCodes.RUNTIME_ERROR);
727
- });
728
- process.once('unhandledRejection', (reason) => {
729
- // Wrap non-ModestBench errors with UnknownError
730
- const wrappedError = (0, index_js_2.isModestBenchError)(reason)
731
- ? reason
732
- : new index_js_2.UnknownError(reason instanceof Error ? reason.message : String(reason), { cause: reason });
733
- console.error(wrappedError.toString());
734
- process.exit(exports.ExitCodes.RUNTIME_ERROR);
916
+ : new index_js_2.UnknownError(error.message, { cause: error });
917
+ console.error(`${wrappedError}`);
918
+ process.exit(constants_js_1.ExitCodes.RUNTIME_ERROR);
919
+ })
920
+ .once('unhandledRejection', (reason) => {
921
+ const wrappedError = new index_js_2.UnknownError((0, base_js_1.isError)(reason) ? reason.message : String(reason), { cause: reason });
922
+ console.error(`${wrappedError}`);
923
+ process.exit(constants_js_1.ExitCodes.RUNTIME_ERROR);
735
924
  });
736
925
  };
737
926
  // Run CLI if this file is executed directly