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
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Profile Human Reporter
3
+ *
4
+ * Human-readable reporter for profile command. Uses modestbench's synthwave
5
+ * ANSI theme to display profiled functions in an attractive, color-coded
6
+ * format.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+
11
+ import path from 'node:path';
12
+
13
+ import type { FilteredProfileData } from '../types/profiler.js';
14
+
15
+ import { ansiChars, colors } from '../utils/ansi.js';
16
+
17
+ /**
18
+ * Reporter options
19
+ */
20
+ interface ProfileReporterOptions {
21
+ /** Enable color output */
22
+ color?: boolean;
23
+
24
+ /** Group by file */
25
+ groupByFile?: boolean;
26
+ }
27
+
28
+ /**
29
+ * Human-readable profile reporter
30
+ */
31
+ export class ProfileHumanReporter {
32
+ private readonly groupByFile: boolean;
33
+
34
+ private readonly useColor: boolean;
35
+
36
+ constructor(options: ProfileReporterOptions = {}) {
37
+ this.useColor =
38
+ options.color ??
39
+ (process.stdout.isTTY &&
40
+ process.env.FORCE_COLOR !== '0' &&
41
+ process.env.NO_COLOR == null);
42
+
43
+ this.groupByFile = options.groupByFile ?? false;
44
+ }
45
+
46
+ /**
47
+ * Generate and print profile report
48
+ */
49
+ report(data: FilteredProfileData): void {
50
+ this.printHeader(data);
51
+ this.printLine();
52
+
53
+ if (this.groupByFile && data.groupedByFile) {
54
+ this.printGroupedResults(data);
55
+ } else {
56
+ this.printFlatResults(data);
57
+ }
58
+ }
59
+
60
+ private colorize(color: keyof typeof colors, text: string): string {
61
+ if (!this.useColor) {
62
+ return text;
63
+ }
64
+ return `${colors[color]}${text}${colors.reset}`;
65
+ }
66
+
67
+ /**
68
+ * Format file path - show relative path if within CWD, otherwise absolute
69
+ */
70
+ private formatPath(filePath: string): string {
71
+ const cwd = process.cwd();
72
+ const absolutePath = path.resolve(filePath);
73
+
74
+ // Check if the file is within the current working directory
75
+ if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
76
+ return path.relative(cwd, absolutePath);
77
+ }
78
+
79
+ return absolutePath;
80
+ }
81
+
82
+ private getPercentColor(percent: number): keyof typeof colors {
83
+ if (percent >= 10) {
84
+ return 'brightRed';
85
+ }
86
+ if (percent >= 5) {
87
+ return 'brightYellow';
88
+ }
89
+ if (percent >= 2) {
90
+ return 'brightCyan';
91
+ }
92
+ return 'white';
93
+ }
94
+
95
+ private printFlatResults(data: FilteredProfileData): void {
96
+ const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Benchmark Candidates'))}`;
97
+ this.printLine(header);
98
+ this.printLine();
99
+ this.printLine('Top functions by execution time:');
100
+ this.printLine();
101
+
102
+ for (const fn of data.functions) {
103
+ // Function name and percentage
104
+ const percentColor = this.getPercentColor(fn.percentage);
105
+ const percent = `${fn.percentage.toFixed(1)}%`;
106
+ const ticks = `(${fn.ticks.toLocaleString()} ticks)`;
107
+
108
+ this.printLine(
109
+ ` ${this.colorize('brightWhite', fn.name).padEnd(60)} ${this.colorize(percentColor, percent.padStart(6))} ${this.colorize('dim', ticks)}`,
110
+ );
111
+
112
+ // File and line
113
+ const displayPath = this.formatPath(fn.file);
114
+ const lineInfo = fn.line ? `:${fn.line}` : '';
115
+ this.printLine(
116
+ ` ${this.colorize('brightMagenta', this.colorize('bold', displayPath + lineInfo))}`,
117
+ );
118
+ this.printLine();
119
+ }
120
+
121
+ this.printSummary(data);
122
+ }
123
+
124
+ private printGroupedResults(data: FilteredProfileData): void {
125
+ if (!data.groupedByFile) {
126
+ return;
127
+ }
128
+
129
+ const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Grouped by File'))}`;
130
+ this.printLine(header);
131
+ this.printLine();
132
+
133
+ // Sort files by total percentage
134
+ const sortedFiles = Array.from(data.groupedByFile.entries()).sort(
135
+ (a, b) => {
136
+ const aTotal = a[1].reduce((sum, fn) => sum + fn.percentage, 0);
137
+ const bTotal = b[1].reduce((sum, fn) => sum + fn.percentage, 0);
138
+ return bTotal - aTotal;
139
+ },
140
+ );
141
+
142
+ for (const [file, functions] of sortedFiles) {
143
+ const totalPercent = functions.reduce(
144
+ (sum, fn) => sum + fn.percentage,
145
+ 0,
146
+ );
147
+ const totalTicks = functions.reduce((sum, fn) => sum + fn.ticks, 0);
148
+
149
+ const percentColor = this.getPercentColor(totalPercent);
150
+ const percent = `${totalPercent.toFixed(1)}%`;
151
+ const ticks = `(${totalTicks.toLocaleString()} ticks)`;
152
+
153
+ // File header
154
+ const displayPath = this.formatPath(file);
155
+ this.printLine(
156
+ `${this.colorize('magenta', ansiChars.block.dark)} ${this.colorize('brightMagenta', this.colorize('bold', displayPath)).padEnd(60)} ${this.colorize(percentColor, percent.padStart(6))} ${this.colorize('dim', ticks)}`,
157
+ );
158
+
159
+ // Functions in this file
160
+ for (const fn of functions) {
161
+ const fnPercent = `${fn.percentage.toFixed(1)}%`;
162
+ const fnTicks = `(${fn.ticks.toLocaleString()} ticks)`;
163
+ const lineInfo = fn.line ? `:${fn.line}` : '';
164
+
165
+ this.printLine(
166
+ ` ${this.colorize('magenta', ansiChars.smallSquare)} ${this.colorize('brightWhite', fn.name).padEnd(58)} ${this.colorize(this.getPercentColor(fn.percentage), fnPercent.padStart(6))} ${this.colorize('dim', fnTicks.padEnd(15))} ${this.colorize('dim', lineInfo)}`,
167
+ );
168
+ }
169
+
170
+ this.printLine();
171
+ }
172
+
173
+ this.printSummary(data);
174
+ }
175
+
176
+ private printHeader(data: FilteredProfileData): void {
177
+ const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Profile Analysis'))}`;
178
+ this.printLine(header);
179
+ this.printLine();
180
+
181
+ if (data.command) {
182
+ this.printLine(`Command: ${this.colorize('cyan', data.command)}`);
183
+ }
184
+
185
+ if (data.duration) {
186
+ const durationSec = (data.duration / 1000).toFixed(1);
187
+ this.printLine(`Duration: ${this.colorize('cyan', `${durationSec}s`)}`);
188
+ }
189
+
190
+ this.printLine(
191
+ `Total Ticks: ${this.colorize('cyan', data.totalTicks.toLocaleString())}`,
192
+ );
193
+ }
194
+
195
+ private printLine(text = ''): void {
196
+ console.log(text);
197
+ }
198
+
199
+ private printSummary(data: FilteredProfileData): void {
200
+ this.printLine(
201
+ `${this.colorize('dim', `... (showing top ${data.totalShown} of ${data.totalFiltered} user functions)`)}`,
202
+ );
203
+ }
204
+ }
@@ -9,13 +9,13 @@ import path from 'node:path';
9
9
 
10
10
  import type {
11
11
  BenchmarkRun,
12
+ BudgetSummary,
12
13
  FileResult,
13
- ProgressState,
14
14
  SuiteResult,
15
15
  TaskResult,
16
16
  } from '../types/index.js';
17
17
 
18
- import { BaseReporter } from './registry.js';
18
+ import { BaseReporter } from '../services/reporter-registry.js';
19
19
 
20
20
  /**
21
21
  * Basic symbols for plain text output
@@ -62,6 +62,78 @@ export class SimpleReporter extends BaseReporter {
62
62
  this.quiet = options.quiet ?? false;
63
63
  }
64
64
 
65
+ /**
66
+ * Format bytes in human-readable format
67
+ */
68
+ private static formatBytes(this: void, bytes: number): string {
69
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
70
+ let size = bytes;
71
+ let unitIndex = 0;
72
+
73
+ while (size >= 1024 && unitIndex < units.length - 1) {
74
+ size /= 1024;
75
+ unitIndex++;
76
+ }
77
+
78
+ return `${size.toFixed(1)} ${units[unitIndex]}`;
79
+ }
80
+
81
+ /**
82
+ * Format file path - show relative path if within CWD, otherwise absolute
83
+ */
84
+ private static formatPath(this: void, filePath: string): string {
85
+ const cwd = process.cwd();
86
+ const absolutePath = path.resolve(filePath);
87
+
88
+ // Check if the file is within the current working directory
89
+ if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
90
+ return path.relative(cwd, absolutePath);
91
+ }
92
+
93
+ return absolutePath;
94
+ }
95
+
96
+ /**
97
+ * Simple pluralization helper
98
+ */
99
+ private static pluralize(this: void, str: string, count: number): string {
100
+ return count === 1 ? str : `${str}s`;
101
+ }
102
+
103
+ onBudgetResult(summary: BudgetSummary): void {
104
+ if (summary.total === 0 || this.quiet) {
105
+ return;
106
+ }
107
+
108
+ console.log('== Performance Budgets');
109
+ console.log();
110
+
111
+ for (const result of summary.results) {
112
+ const icon = result.passed ? symbols.checkmark : symbols.cross;
113
+ console.log(` ${icon} ${result.taskId}`);
114
+
115
+ if (!result.passed && result.violations.length > 0) {
116
+ for (const violation of result.violations) {
117
+ console.log(` ${violation.message}`);
118
+ }
119
+ }
120
+ }
121
+
122
+ console.log();
123
+
124
+ if (summary.failed === 0) {
125
+ console.log(
126
+ ` ${symbols.checkmark} All ${summary.total} budget(s) passed`,
127
+ );
128
+ } else {
129
+ console.log(
130
+ ` ${symbols.cross} ${summary.failed} of ${summary.total} budget(s) failed`,
131
+ );
132
+ }
133
+
134
+ console.log();
135
+ }
136
+
65
137
  onEnd(run: BenchmarkRun): void {
66
138
  if (this.quiet) {
67
139
  return;
@@ -97,7 +169,7 @@ export class SimpleReporter extends BaseReporter {
97
169
  console.log(`- Files: ${totalFiles}`);
98
170
  console.log(`- Suites: ${totalSuites}`);
99
171
  console.log(
100
- `${symbols.approx} Duration: ${this.formatDuration(duration * 1e6)}`,
172
+ `${symbols.approx} Duration: ${BaseReporter.formatDuration(duration * 1e6)}`,
101
173
  );
102
174
  console.log();
103
175
 
@@ -111,7 +183,7 @@ export class SimpleReporter extends BaseReporter {
111
183
  console.log();
112
184
 
113
185
  for (const failure of this.failures) {
114
- const displayPath = this.formatPath(failure.file);
186
+ const displayPath = SimpleReporter.formatPath(failure.file);
115
187
  console.log(` ${displayPath} > ${failure.suite} > ${failure.task}`);
116
188
  console.log(` ${failure.error}`);
117
189
  console.log();
@@ -155,7 +227,7 @@ export class SimpleReporter extends BaseReporter {
155
227
  );
156
228
  } else {
157
229
  console.log(
158
- ` ${symbols.checkmark} ${totalPassed > 1 ? 'All ' : ''}${totalPassed} ${this.pluralize('task', totalPassed)} passed`,
230
+ ` ${symbols.checkmark} ${totalPassed > 1 ? 'All ' : ''}${totalPassed} ${SimpleReporter.pluralize('task', totalPassed)} passed`,
159
231
  );
160
232
  }
161
233
 
@@ -169,15 +241,10 @@ export class SimpleReporter extends BaseReporter {
169
241
  return;
170
242
  }
171
243
 
172
- const displayPath = this.formatPath(file);
244
+ const displayPath = SimpleReporter.formatPath(file);
173
245
  console.log(`-- ${displayPath}`);
174
246
  }
175
247
 
176
- onProgress(_state: ProgressState): void {
177
- // Simple reporter does not display progress bars
178
- return;
179
- }
180
-
181
248
  onStart(run: BenchmarkRun): void {
182
249
  this.startTime = Date.now();
183
250
  this.failures = []; // Reset failures for new run
@@ -197,7 +264,9 @@ export class SimpleReporter extends BaseReporter {
197
264
  console.log(
198
265
  ` cpu: ${run.environment.cpu.model} (${run.environment.cpu.cores} cores)`,
199
266
  );
200
- console.log(` mem: ${this.formatBytes(run.environment.memory.total)}`);
267
+ console.log(
268
+ ` mem: ${SimpleReporter.formatBytes(run.environment.memory.total)}`,
269
+ );
201
270
  console.log();
202
271
  }
203
272
 
@@ -231,7 +300,7 @@ export class SimpleReporter extends BaseReporter {
231
300
  console.log(` ${symbols.cross} ${failed} failed, ${passed} passed`);
232
301
  } else {
233
302
  console.log(
234
- ` ${symbols.checkmark} ${passed} ${this.pluralize('task', passed)} passed`,
303
+ ` ${symbols.checkmark} ${passed} ${SimpleReporter.pluralize('task', passed)} passed`,
235
304
  );
236
305
  }
237
306
  console.log();
@@ -275,44 +344,6 @@ export class SimpleReporter extends BaseReporter {
275
344
  }
276
345
  }
277
346
 
278
- /**
279
- * Format bytes in human-readable format
280
- */
281
- private formatBytes(bytes: number): string {
282
- const units = ['B', 'KB', 'MB', 'GB', 'TB'];
283
- let size = bytes;
284
- let unitIndex = 0;
285
-
286
- while (size >= 1024 && unitIndex < units.length - 1) {
287
- size /= 1024;
288
- unitIndex++;
289
- }
290
-
291
- return `${size.toFixed(1)} ${units[unitIndex]}`;
292
- }
293
-
294
- /**
295
- * Format file path - show relative path if within CWD, otherwise absolute
296
- */
297
- private formatPath(filePath: string): string {
298
- const cwd = process.cwd();
299
- const absolutePath = path.resolve(filePath);
300
-
301
- // Check if the file is within the current working directory
302
- if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
303
- return path.relative(cwd, absolutePath);
304
- }
305
-
306
- return absolutePath;
307
- }
308
-
309
- /**
310
- * Simple pluralization helper
311
- */
312
- private pluralize(str: string, count: number): string {
313
- return count === 1 ? str : `${str}s`;
314
- }
315
-
316
347
  /**
317
348
  * Print all task results in a suite with aligned columns
318
349
  */
@@ -364,9 +395,9 @@ export class SimpleReporter extends BaseReporter {
364
395
  };
365
396
  }
366
397
 
367
- const duration = this.formatDuration(result.mean * 1e9);
368
- const opsPerSec = this.formatOpsPerSecond(result.opsPerSecond);
369
- const rme = this.formatPercentage(result.marginOfError * 100);
398
+ const duration = BaseReporter.formatDuration(result.mean * 1e9);
399
+ const opsPerSec = BaseReporter.formatOpsPerSecond(result.opsPerSecond);
400
+ const rme = BaseReporter.formatPercentage(result.marginOfError * 100);
370
401
 
371
402
  return {
372
403
  durationLen: duration.length,
@@ -0,0 +1,199 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+
5
+ import type {
6
+ BaselineReference,
7
+ BaselineStorage,
8
+ BaselineSummaryData,
9
+ BenchmarkRun,
10
+ TaskId,
11
+ } from '../types/core.js';
12
+
13
+ import { validateBaselineStorage } from '../config/budget-schema.js';
14
+ import { StorageError } from '../errors/storage.js';
15
+ import { createTaskId } from '../types/core.js';
16
+
17
+ /**
18
+ * Service for managing named baselines
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+ export class BaselineStorageService {
23
+ private readonly storageDir: string;
24
+
25
+ private readonly storageFile: string;
26
+
27
+ constructor(storageDir: string = '.') {
28
+ this.storageDir = storageDir;
29
+ this.storageFile = join(storageDir, '.modestbench.baselines.json');
30
+ }
31
+
32
+ /**
33
+ * Delete a baseline
34
+ */
35
+ async deleteBaseline(name: string): Promise<void> {
36
+ let storage = await this.loadStorage();
37
+
38
+ if (storage.baselines[name]) {
39
+ delete storage.baselines[name];
40
+
41
+ // Clear default if it was the deleted baseline
42
+ if (storage.default === name) {
43
+ storage = { ...storage, default: undefined };
44
+ }
45
+
46
+ await this.saveStorage(storage);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Get a baseline by name
52
+ */
53
+ async getBaseline(name: string): Promise<BaselineReference | null> {
54
+ const storage = await this.loadStorage();
55
+ return storage.baselines[name] ?? null;
56
+ }
57
+
58
+ /**
59
+ * Get default baseline name
60
+ */
61
+ async getDefault(): Promise<null | string> {
62
+ const storage = await this.loadStorage();
63
+ return storage.default ?? null;
64
+ }
65
+
66
+ /**
67
+ * List all baselines
68
+ */
69
+ async listBaselines(): Promise<BaselineReference[]> {
70
+ const storage = await this.loadStorage();
71
+ return Object.values(storage.baselines).sort(
72
+ (a, b) => b.date.getTime() - a.date.getTime(),
73
+ );
74
+ }
75
+
76
+ /**
77
+ * Resolve baseline name (use provided or fall back to default)
78
+ */
79
+ async resolveBaselineName(name?: string): Promise<null | string> {
80
+ if (name) {
81
+ return name;
82
+ }
83
+
84
+ return await this.getDefault();
85
+ }
86
+
87
+ /**
88
+ * Save a benchmark run as a named baseline
89
+ */
90
+ async saveBaseline(
91
+ name: string,
92
+ run: BenchmarkRun,
93
+ metadata?: {
94
+ branch?: string;
95
+ commit?: string;
96
+ },
97
+ ): Promise<void> {
98
+ const storage = await this.loadStorage();
99
+
100
+ const baseline: BaselineReference = {
101
+ branch: metadata?.branch,
102
+ commit: metadata?.commit,
103
+ date:
104
+ run.startTime instanceof Date ? run.startTime : new Date(run.startTime),
105
+ name,
106
+ runId: run.id,
107
+ summary: this.extractSummary(run),
108
+ };
109
+
110
+ storage.baselines[name] = baseline;
111
+
112
+ await this.saveStorage(storage);
113
+ }
114
+
115
+ /**
116
+ * Set default baseline
117
+ */
118
+ async setDefault(name: string): Promise<void> {
119
+ let storage = await this.loadStorage();
120
+
121
+ if (!storage.baselines[name]) {
122
+ throw new StorageError(
123
+ `Baseline "${name}" does not exist. Cannot set as default.`,
124
+ );
125
+ }
126
+
127
+ storage = { ...storage, default: name };
128
+ await this.saveStorage(storage);
129
+ }
130
+
131
+ /**
132
+ * Extract task summary from benchmark run
133
+ */
134
+ private extractSummary(
135
+ run: BenchmarkRun,
136
+ ): Record<TaskId, BaselineSummaryData> {
137
+ const summary: Record<TaskId, BaselineSummaryData> = {};
138
+
139
+ for (const file of run.files) {
140
+ for (const suite of file.suites) {
141
+ for (const task of suite.tasks) {
142
+ if (!task.error) {
143
+ const taskId = createTaskId(file.filePath, suite.name, task.name);
144
+ summary[taskId] = {
145
+ mean: task.mean,
146
+ opsPerSecond: task.opsPerSecond,
147
+ p99: task.p99,
148
+ };
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ return summary;
155
+ }
156
+
157
+ /**
158
+ * Load baseline storage from disk
159
+ */
160
+ private async loadStorage(): Promise<BaselineStorage> {
161
+ if (!existsSync(this.storageFile)) {
162
+ return {
163
+ baselines: {},
164
+ version: '1.0.0',
165
+ };
166
+ }
167
+
168
+ try {
169
+ const content = await readFile(this.storageFile, 'utf-8');
170
+ const data = JSON.parse(content) as unknown;
171
+ return validateBaselineStorage(data);
172
+ } catch (error) {
173
+ throw new StorageError(
174
+ `Failed to load baseline storage from ${this.storageFile}`,
175
+ { cause: error },
176
+ );
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Save baseline storage to disk
182
+ */
183
+ private async saveStorage(storage: BaselineStorage): Promise<void> {
184
+ try {
185
+ // Ensure directory exists
186
+ if (!existsSync(this.storageDir)) {
187
+ await mkdir(this.storageDir, { recursive: true });
188
+ }
189
+
190
+ const content = JSON.stringify(storage, null, 2);
191
+ await writeFile(this.storageFile, content, 'utf-8');
192
+ } catch (error) {
193
+ throw new StorageError(
194
+ `Failed to save baseline storage to ${this.storageFile}`,
195
+ { cause: error },
196
+ );
197
+ }
198
+ }
199
+ }