modestbench 0.1.0 → 0.3.0

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