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,101 @@
1
+ /**
2
+ * List Formatter
3
+ *
4
+ * Formats historical run listings in human, JSON, and CSV formats.
5
+ */
6
+
7
+ import type { HistoryListResult } from '../../services/history/models.js';
8
+ import type { HistoryFormatter } from './base.js';
9
+
10
+ import { ansiChars, colorize } from '../../utils/ansi.js';
11
+
12
+ /**
13
+ * Formatter for history list command
14
+ */
15
+ export class HistoryListFormatter
16
+ implements HistoryFormatter<HistoryListResult>
17
+ {
18
+ /**
19
+ * Format as CSV
20
+ */
21
+ formatCsv(data: HistoryListResult): string {
22
+ const lines: string[] = ['id,startTime,duration,files,tasks,passed,failed'];
23
+
24
+ for (const run of data.runs) {
25
+ lines.push(
26
+ `${run.id},${run.startTime.toISOString()},${run.duration},${run.summary.totalFiles},${run.summary.totalTasks},${run.summary.passedTasks},${run.summary.failedTasks}`,
27
+ );
28
+ }
29
+
30
+ return lines.join('\n');
31
+ }
32
+
33
+ /**
34
+ * Format as human-readable list
35
+ */
36
+ formatHuman(data: HistoryListResult): string {
37
+ if (data.runs.length === 0) {
38
+ return colorize('dim', 'No historical data found matching criteria.');
39
+ }
40
+
41
+ const lines: string[] = [
42
+ colorize('brightMagenta', colorize('bold', '\nRecent Benchmark Runs')),
43
+ '',
44
+ ];
45
+
46
+ for (const run of data.runs) {
47
+ const dateStr = run.startTime.toLocaleString();
48
+ const durationStr = `${(run.duration / 1000).toFixed(1)}s`;
49
+
50
+ // Status with colors and symbols
51
+ const hasFailures = run.summary.failedTasks > 0;
52
+ const statusIcon = hasFailures
53
+ ? colorize('brightRed', ansiChars.cross)
54
+ : colorize('brightCyan', ansiChars.checkmark);
55
+
56
+ const passedStr = colorize(
57
+ 'brightCyan',
58
+ `${run.summary.passedTasks} passed`,
59
+ );
60
+ const statusStr = hasFailures
61
+ ? `${passedStr}, ${colorize('brightRed', `${run.summary.failedTasks} failed`)}`
62
+ : passedStr;
63
+
64
+ // Run ID in bright white and bold, date dimmed, duration in magenta
65
+ lines.push(
66
+ ` ${statusIcon} ${colorize('brightWhite', colorize('bold', run.id))} ${colorize('dim', ansiChars.bullet)} ${colorize('gray', dateStr)} ${colorize('dim', ansiChars.bullet)} ${colorize('brightMagenta', durationStr)}`,
67
+ );
68
+
69
+ // Files and tasks info
70
+ lines.push(
71
+ ` ${colorize('dim', `${run.summary.totalFiles} files, ${run.summary.totalTasks} tasks:`)} ${statusStr}`,
72
+ );
73
+ lines.push('');
74
+ }
75
+
76
+ return lines.join('\n');
77
+ }
78
+
79
+ /**
80
+ * Format as JSON array
81
+ */
82
+ formatJson(data: HistoryListResult): string {
83
+ if (data.runs.length === 0) {
84
+ return '[]';
85
+ }
86
+
87
+ return JSON.stringify(
88
+ data.runs.map((run) => ({
89
+ duration: run.duration,
90
+ failed: run.summary.failedTasks,
91
+ files: run.summary.totalFiles,
92
+ id: run.id,
93
+ passed: run.summary.passedTasks,
94
+ startTime: run.startTime,
95
+ tasks: run.summary.totalTasks,
96
+ })),
97
+ null,
98
+ 2,
99
+ );
100
+ }
101
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Show Formatter
3
+ *
4
+ * Formats detailed benchmark run display in human and JSON formats.
5
+ */
6
+
7
+ import { relative } from 'node:path';
8
+
9
+ import type { ShowResult } from '../../services/history/models.js';
10
+ import type { HistoryFormatter } from './base.js';
11
+
12
+ import { ansiChars, colorize } from '../../utils/ansi.js';
13
+
14
+ /**
15
+ * Formatter for history show command
16
+ */
17
+ export class HistoryShowFormatter implements HistoryFormatter<ShowResult> {
18
+ /**
19
+ * Format as human-readable detailed view
20
+ */
21
+ formatHuman(data: ShowResult): string {
22
+ const lines: string[] = [];
23
+
24
+ // Header with run ID
25
+ lines.push(
26
+ colorize(
27
+ 'cyan',
28
+ colorize(
29
+ 'bold',
30
+ `\nBenchmark Run: ${colorize('brightWhite', colorize('bold', data.id))}`,
31
+ ),
32
+ ),
33
+ );
34
+
35
+ // Run details (indented by 2 spaces)
36
+ lines.push(
37
+ ` ${colorize('brightCyan', ansiChars.bullet)} ${colorize('white', data.startTime.toLocaleString())}`,
38
+ );
39
+ lines.push(
40
+ ` ${colorize('brightCyan', ansiChars.bullet)} ${colorize('white', 'Duration:')} ${colorize('magenta', `${(data.duration / 1000).toFixed(1)}s`)}`,
41
+ );
42
+ lines.push(
43
+ ` ${colorize('brightCyan', ansiChars.bullet)} Node.js ${colorize('brightWhite', data.environment.nodeVersion)} on ${colorize('brightWhite', data.environment.platform)} (${colorize('brightWhite', data.environment.arch)})`,
44
+ );
45
+
46
+ // CPU and system info
47
+ lines.push(
48
+ ` ${colorize('brightCyan', ansiChars.bullet)} ${colorize('brightWhite', String(data.environment.cpu.cores))} cores @ ${colorize('brightWhite', `${data.environment.cpu.speed}MHz`)} on ${colorize('brightWhite', data.environment.cpu.model)}`,
49
+ );
50
+
51
+ if (data.git) {
52
+ lines.push(
53
+ ` ${colorize('brightCyan', ansiChars.bullet)} ${colorize('brightBlue', data.git.branch)}@${colorize('dim', data.git.commit.substring(0, 8))}`,
54
+ );
55
+ }
56
+
57
+ // Summary section
58
+ lines.push('');
59
+ lines.push(colorize('cyan', 'Summary'));
60
+ lines.push(
61
+ ` ${colorize('dim', ansiChars.smallSquare)} Files: ${colorize('brightWhite', String(data.summary.totalFiles))}`,
62
+ );
63
+ lines.push(
64
+ ` ${colorize('dim', ansiChars.smallSquare)} Suites: ${colorize('brightWhite', String(data.summary.totalSuites))}`,
65
+ );
66
+ lines.push(
67
+ ` ${colorize('dim', ansiChars.smallSquare)} Tasks: ${colorize('brightWhite', String(data.summary.totalTasks))}`,
68
+ );
69
+
70
+ lines.push(
71
+ ` ${colorize('dim', ansiChars.smallSquare)} Passed: ${colorize('brightCyan', String(data.summary.passedTasks))}`,
72
+ );
73
+
74
+ if (data.summary.failedTasks > 0) {
75
+ lines.push(
76
+ ` ${colorize('dim', ansiChars.smallSquare)} Failed: ${colorize('brightRed', colorize('bold', String(data.summary.failedTasks)))}`,
77
+ );
78
+ }
79
+
80
+ // Detailed results
81
+ lines.push('');
82
+ lines.push(colorize('cyan', 'Results'));
83
+ lines.push('');
84
+
85
+ for (const file of data.files) {
86
+ // Display filepath as relative if within cwd, otherwise absolute
87
+ const displayPath = relative(process.cwd(), file.filePath);
88
+ const finalPath = displayPath.startsWith('..')
89
+ ? file.filePath
90
+ : displayPath;
91
+
92
+ lines.push(
93
+ `${colorize('dim', ansiChars.bullet)} ${colorize('brightMagenta', colorize('bold', finalPath))}`,
94
+ );
95
+
96
+ for (const suite of file.suites) {
97
+ lines.push(
98
+ ` ${colorize('dim', ansiChars.bullet)} ${colorize('brightWhite', suite.name)}`,
99
+ );
100
+
101
+ for (const task of suite.tasks) {
102
+ const statusIcon = task.error
103
+ ? colorize('brightRed', ansiChars.cross)
104
+ : colorize('brightCyan', ansiChars.checkmark);
105
+
106
+ if (task.error) {
107
+ lines.push(
108
+ ` ${statusIcon} ${colorize('white', task.name)} ${colorize('dim', ansiChars.bullet)} ${colorize('brightRed', 'failed')}`,
109
+ );
110
+ } else {
111
+ const mean = formatTime(task.mean);
112
+ const opsStr = task.opsPerSecond.toLocaleString('en-US', {
113
+ maximumFractionDigits: 0,
114
+ });
115
+ const rmeStr = task.marginOfError.toFixed(2);
116
+
117
+ lines.push(
118
+ ` ${statusIcon} ${colorize('white', task.name)}: ${colorize('brightMagenta', mean)} ${colorize('dim', ansiChars.bullet)} ${colorize('brightBlue', `${ansiChars.plusMinus}${rmeStr}`)} ${colorize('dim', ansiChars.bullet)} ${colorize('magenta', opsStr)} ops/sec`,
119
+ );
120
+
121
+ if (task.iterations > 0) {
122
+ lines.push(
123
+ ` ${colorize('dim', `${task.iterations} iterations, cv: ${task.cv.toFixed(1)}%`)}`,
124
+ );
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ lines.push('');
131
+ }
132
+
133
+ return lines.join('\n');
134
+ }
135
+
136
+ /**
137
+ * Format as complete JSON
138
+ */
139
+ formatJson(data: ShowResult): string {
140
+ return JSON.stringify(data, null, 2);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Format nanoseconds as a human-readable time string
146
+ */
147
+ const formatTime = (ns: number): string => {
148
+ if (ns < 1000) {
149
+ return `${ns.toFixed(2)}ns`;
150
+ }
151
+ if (ns < 1000000) {
152
+ return `${(ns / 1000).toFixed(3)}µs`;
153
+ }
154
+ return `${(ns / 1000000).toFixed(3)}ms`;
155
+ };
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Trends Formatter
3
+ *
4
+ * Formats performance trend analysis with visualizations in human and JSON
5
+ * formats.
6
+ */
7
+
8
+ import { stripVTControlCharacters } from 'node:util';
9
+
10
+ import type {
11
+ DistributionBucket,
12
+ TrendsResult,
13
+ } from '../../services/history/models.js';
14
+ import type { HistoryFormatter } from './base.js';
15
+
16
+ import { colorize } from '../../utils/ansi.js';
17
+ import { generateBarChart, generateSparkline } from './visualization.js';
18
+
19
+ /**
20
+ * Nanoseconds per millisecond conversion constant
21
+ */
22
+ const NS_PER_MS = 1_000_000;
23
+
24
+ /**
25
+ * Nanoseconds per microsecond conversion constant
26
+ */
27
+ const NS_PER_US = 1_000;
28
+
29
+ /**
30
+ * Intelligently format a time range with appropriate precision Displays
31
+ * microseconds for small values (< 1ms) for better clarity
32
+ */
33
+ const formatTimeRange = (minNs: number, maxNs: number): string => {
34
+ // If both values are below 1ms, show in microseconds for better precision
35
+ if (maxNs < NS_PER_MS) {
36
+ const minUs = minNs / NS_PER_US;
37
+ const maxUs = maxNs / NS_PER_US;
38
+ return `${minUs.toFixed(2)}-${maxUs.toFixed(2)}µs`;
39
+ }
40
+
41
+ // Otherwise show in milliseconds
42
+ const minMs = minNs / NS_PER_MS;
43
+ const maxMs = maxNs / NS_PER_MS;
44
+ return `${minMs.toFixed(3)}-${maxMs.toFixed(3)}ms`;
45
+ };
46
+
47
+ /**
48
+ * Calculate visible string length, stripping ANSI escape codes
49
+ */
50
+ const getVisualLength = (str: string): number =>
51
+ stripVTControlCharacters(str).length;
52
+
53
+ /**
54
+ * Formatter for history trends command
55
+ */
56
+ export class HistoryTrendsFormatter implements HistoryFormatter<TrendsResult> {
57
+ /**
58
+ * Format as human-readable trends with visualizations
59
+ */
60
+ formatHuman(data: TrendsResult): string {
61
+ const lines: string[] = [];
62
+
63
+ lines.push(
64
+ colorize(
65
+ 'brightMagenta',
66
+ colorize('bold', `\nPerformance Trends (${data.runs} runs)`),
67
+ ),
68
+ );
69
+
70
+ if (data.timespan.start && data.timespan.end) {
71
+ lines.push(
72
+ colorize(
73
+ 'dim',
74
+ `Time range: ${data.timespan.start.toLocaleDateString()} to ${data.timespan.end.toLocaleDateString()}`,
75
+ ),
76
+ );
77
+ }
78
+ lines.push('');
79
+
80
+ // Summary statistics
81
+ lines.push(colorize('brightBlue', 'Summary:'));
82
+ lines.push(
83
+ ` ${colorize('brightCyan', '▲')} ${data.summary.improvingTasks} improving ${colorize('brightRed', '▼')} ${data.summary.degradingTasks} degrading ${colorize('dim', '→')} ${data.summary.stableTasks} stable`,
84
+ );
85
+ lines.push('');
86
+
87
+ // Task Performance Summary Table
88
+ lines.push(colorize('brightMagenta', 'Task Performance Summary:'));
89
+ lines.push('');
90
+
91
+ for (const trendData of data.trends) {
92
+ // Show top 20
93
+ const trendIcon =
94
+ trendData.trend === 'improving'
95
+ ? colorize('brightCyan', '▲')
96
+ : trendData.trend === 'degrading'
97
+ ? colorize('brightRed', '▼')
98
+ : colorize('dim', '→');
99
+
100
+ const changeColor =
101
+ trendData.percentChange < -5
102
+ ? 'brightCyan'
103
+ : trendData.percentChange > 5
104
+ ? 'brightRed'
105
+ : 'dim';
106
+
107
+ const changeSign = trendData.percentChange >= 0 ? '+' : '';
108
+ const changeStr = `${changeSign}${trendData.percentChange.toFixed(1)}%`;
109
+
110
+ // Generate sparkline - scale with number of data points (min 12, max 20)
111
+ const values = trendData.dataPoints.map((dp) => dp.mean);
112
+ const sparklineWidth = Math.min(20, Math.max(12, trendData.runs));
113
+ const sparkline = generateSparkline(values, sparklineWidth);
114
+ const sparklineColor =
115
+ trendData.trend === 'improving'
116
+ ? 'brightCyan'
117
+ : trendData.trend === 'degrading'
118
+ ? 'brightRed'
119
+ : 'cyan';
120
+
121
+ const taskName = colorize('white', trendData.task);
122
+ const percentDisplay = colorize(changeColor, changeStr.padStart(8));
123
+ const sparklineDisplay = colorize(sparklineColor, sparkline);
124
+
125
+ // Layout: icon (2) + task name + padding + percent (8) + spaces (2) + sparkline
126
+ // Position percent+graph at visual column 60 for consistent alignment
127
+ const prefix = ` ${trendIcon} ${taskName}`;
128
+ const prefixVisualLength = getVisualLength(prefix);
129
+ const targetColumn = 60;
130
+
131
+ if (prefixVisualLength > targetColumn) {
132
+ // Task name is too long, wrap to next line
133
+ lines.push(prefix);
134
+ lines.push(
135
+ `${' '.repeat(targetColumn)}${percentDisplay} ${sparklineDisplay}`,
136
+ );
137
+ } else {
138
+ // Fit on one line with padding
139
+ const padding = Math.max(1, targetColumn - prefixVisualLength);
140
+ const paddingStr = ' '.repeat(padding);
141
+ lines.push(
142
+ `${prefix}${paddingStr}${percentDisplay} ${sparklineDisplay}`,
143
+ );
144
+ }
145
+ }
146
+
147
+ // Show all trends (no limit)
148
+
149
+ lines.push('');
150
+
151
+ // Show regressions if any
152
+ if (data.regressions.length > 0) {
153
+ lines.push(
154
+ colorize('brightRed', colorize('bold', 'Regressions Detected:')),
155
+ );
156
+ lines.push('');
157
+
158
+ for (const regression of data.regressions) {
159
+ lines.push(
160
+ ` ${colorize('brightRed', '▼')} ${colorize('white', regression.task)}: ${colorize('brightRed', `${regression.percentChange.toFixed(1)}% slower`)} (${regression.runs} runs)`,
161
+ );
162
+ }
163
+
164
+ lines.push('');
165
+ }
166
+
167
+ // Show low-confidence regressions (insufficient data)
168
+ if (data.lowConfidenceRegressions.length > 0) {
169
+ lines.push(
170
+ colorize(
171
+ 'brightYellow',
172
+ colorize('bold', '! Potential Regressions (insufficient data):'),
173
+ ),
174
+ );
175
+ lines.push('');
176
+
177
+ for (const regression of data.lowConfidenceRegressions) {
178
+ lines.push(
179
+ ` ${colorize('brightYellow', '!')} ${colorize('white', regression.task)}: ${colorize('brightYellow', `${regression.percentChange.toFixed(1)}% slower`)} (${regression.runs} run${regression.runs !== 1 ? 's' : ''})`,
180
+ );
181
+ }
182
+
183
+ lines.push('');
184
+ }
185
+
186
+ // Show performance distribution for most important task (highest RME) if we have enough data
187
+ // Find task with highest relative margin of error - the most variable/unreliable one
188
+ const mostImportantTrend = data.trends
189
+ .filter((t) => t.runs >= 5)
190
+ .sort((a, b) => {
191
+ const rmeA = (a.statistics.stdDeviation / a.statistics.mean) * 100;
192
+ const rmeB = (b.statistics.stdDeviation / b.statistics.mean) * 100;
193
+ return rmeB - rmeA; // Descending order (highest RME first)
194
+ })[0];
195
+
196
+ if (mostImportantTrend) {
197
+ lines.push(
198
+ colorize(
199
+ 'brightMagenta',
200
+ 'Performance Distribution (most variable task):',
201
+ ),
202
+ );
203
+ lines.push(colorize('white', mostImportantTrend.task));
204
+ const cv = (
205
+ (mostImportantTrend.statistics.stdDeviation /
206
+ mostImportantTrend.statistics.mean) *
207
+ 100
208
+ ).toFixed(1);
209
+ lines.push(colorize('dim', ` Variability: ${cv}%`));
210
+ lines.push('');
211
+
212
+ // Create distribution buckets
213
+ const values = mostImportantTrend.dataPoints.map((dp) => dp.mean);
214
+ const min = Math.min(...values);
215
+ const max = Math.max(...values);
216
+ const range = max - min;
217
+ const numBuckets = Math.min(5, mostImportantTrend.runs);
218
+ const bucketSize = range / numBuckets;
219
+
220
+ const buckets: DistributionBucket[] = [];
221
+ for (let i = 0; i < numBuckets; i++) {
222
+ const bucketMin = min + i * bucketSize;
223
+ const bucketMax = min + (i + 1) * bucketSize;
224
+ const count = values.filter(
225
+ (v) =>
226
+ v >= bucketMin &&
227
+ (i === numBuckets - 1 ? v <= bucketMax : v < bucketMax),
228
+ ).length;
229
+
230
+ const label = formatTimeRange(bucketMin, bucketMax);
231
+
232
+ buckets.push({
233
+ count,
234
+ label,
235
+ max: bucketMax,
236
+ min: bucketMin,
237
+ });
238
+ }
239
+
240
+ // Filter out empty buckets for cleaner display
241
+ const nonEmptyBuckets = buckets.filter((b) => b.count > 0);
242
+ const chart = generateBarChart(nonEmptyBuckets, 25);
243
+ for (const line of chart) {
244
+ lines.push(colorize('brightCyan', line));
245
+ }
246
+
247
+ lines.push('');
248
+ lines.push(
249
+ colorize(
250
+ 'dim',
251
+ ` Mean: ${(mostImportantTrend.statistics.mean / NS_PER_MS).toFixed(3)}ms Median: ${(mostImportantTrend.statistics.median / NS_PER_MS).toFixed(3)}ms StdDev: ${(mostImportantTrend.statistics.stdDeviation / NS_PER_MS).toFixed(3)}ms`,
252
+ ),
253
+ );
254
+ lines.push('');
255
+ }
256
+
257
+ return lines.join('\n');
258
+ }
259
+
260
+ /**
261
+ * Format as JSON
262
+ */
263
+ formatJson(data: TrendsResult): string {
264
+ return JSON.stringify(
265
+ {
266
+ regressions: data.regressions,
267
+ summary: {
268
+ degradingTasks: data.summary.degradingTasks,
269
+ improvingTasks: data.summary.improvingTasks,
270
+ runs: data.runs,
271
+ stableTasks: data.summary.stableTasks,
272
+ timespan: data.timespan,
273
+ totalTasks: data.summary.totalTasks,
274
+ },
275
+ trends: data.trends,
276
+ },
277
+ null,
278
+ 2,
279
+ );
280
+ }
281
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Visualization Utilities for History Formatters
3
+ *
4
+ * Pure functions for generating ASCII visualizations (sparklines, bar charts)
5
+ * for trend analysis and performance distribution displays.
6
+ */
7
+
8
+ import type { DistributionBucket } from '../../services/history/models.js';
9
+
10
+ /**
11
+ * Generate bar chart histogram from distribution buckets
12
+ *
13
+ * @param distribution - Array of distribution buckets with counts
14
+ * @param maxWidth - Maximum width of bars (default 20)
15
+ * @returns Array of formatted bar chart lines
16
+ */
17
+ export const generateBarChart = (
18
+ distribution: DistributionBucket[],
19
+ maxWidth = 20,
20
+ ): string[] => {
21
+ if (distribution.length === 0) {
22
+ return [];
23
+ }
24
+
25
+ // Find maximum count for scaling
26
+ const maxCount = Math.max(...distribution.map((b) => b.count));
27
+
28
+ if (maxCount === 0) {
29
+ return distribution.map((bucket) => ` ${bucket.label} (0 runs)`);
30
+ }
31
+
32
+ // Block characters for bar visualization
33
+ const fullBlock = '█';
34
+ const lightBlock = '░';
35
+
36
+ return distribution.map((bucket) => {
37
+ const ratio = bucket.count / maxCount;
38
+ const barLength = Math.round(ratio * maxWidth);
39
+ const fullBlocks = fullBlock.repeat(barLength);
40
+ const emptyBlocks = lightBlock.repeat(maxWidth - barLength);
41
+
42
+ return ` ${fullBlocks}${emptyBlocks} ${bucket.label} (${bucket.count} run${bucket.count !== 1 ? 's' : ''})`;
43
+ });
44
+ };
45
+
46
+ /**
47
+ * Generate ASCII sparkline from values
48
+ *
49
+ * @param values - Array of numeric values to visualize
50
+ * @param width - Maximum width of sparkline (downsamples if needed)
51
+ * @returns ASCII sparkline string using block characters
52
+ */
53
+ export const generateSparkline = (values: number[], width?: number): string => {
54
+ if (values.length === 0) {
55
+ return '';
56
+ }
57
+
58
+ // Sparkline characters from lowest to highest
59
+ const sparkChars = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
60
+
61
+ // Downsample if width is specified and values exceed it
62
+ let processedValues = values;
63
+ if (width && values.length > width) {
64
+ const step = values.length / width;
65
+ processedValues = [];
66
+ for (let i = 0; i < width; i++) {
67
+ const idx = Math.floor(i * step);
68
+ processedValues.push(values[idx] ?? 0);
69
+ }
70
+ }
71
+
72
+ // Find min and max for scaling
73
+ const min = Math.min(...processedValues);
74
+ const max = Math.max(...processedValues);
75
+ const range = max - min;
76
+
77
+ // Handle case where all values are the same
78
+ if (range === 0) {
79
+ return (sparkChars[4] ?? '▄').repeat(processedValues.length); // Use middle character
80
+ }
81
+
82
+ // Map each value to a sparkline character
83
+ return processedValues
84
+ .map((value) => {
85
+ const normalized = (value - min) / range;
86
+ const index = Math.min(
87
+ Math.floor(normalized * sparkChars.length),
88
+ sparkChars.length - 1,
89
+ );
90
+ return sparkChars[index] ?? '▄';
91
+ })
92
+ .join('');
93
+ };
package/src/index.ts CHANGED
@@ -7,15 +7,11 @@
7
7
  */
8
8
 
9
9
  export { bootstrap as modestbench } from './bootstrap.js';
10
- // Configuration management
11
- export { ModestBenchConfigurationManager } from './config/manager.js';
12
10
 
13
- // Core engine and loader
11
+ // Core engine
14
12
  export { ModestBenchEngine } from './core/engine.js';
15
13
  export { AccurateEngine, TinybenchEngine } from './core/engines/index.js';
16
14
 
17
- export { BenchmarkFileLoader } from './core/loader.js';
18
-
19
15
  // Statistical utilities
20
16
  export {
21
17
  calculateStatistics,
@@ -26,21 +22,30 @@ export {
26
22
  // Error classes
27
23
  export * from './errors/index.js';
28
24
 
29
- // Progress tracking
30
- export { ModestBenchProgressManager } from './progress/manager.js';
31
25
  // Reporters
32
26
  export { CsvReporter } from './reporters/csv.js';
33
27
  export { HumanReporter } from './reporters/human.js';
34
28
  export { JsonReporter } from './reporters/json.js';
35
-
29
+ export { ProfileHumanReporter } from './reporters/profile-human.js';
30
+
31
+ // Services
32
+ export { ModestBenchConfigurationManager } from './services/config-manager.js';
33
+ export { BenchmarkFileLoader } from './services/file-loader.js';
34
+ export { FileHistoryStorage } from './services/history-storage.js';
35
+ // Profiler services
36
+ export { filterProfile } from './services/profiler/profile-filter.js';
37
+ export { parseProfile } from './services/profiler/profile-parser.js';
38
+
39
+ export { runWithProfiling } from './services/profiler/profile-runner.js';
40
+ export { ModestBenchProgressManager } from './services/progress-manager.js';
36
41
  export {
37
42
  BaseReporter,
38
43
  CompositeReporter,
39
44
  ModestBenchReporterRegistry,
40
- } from './reporters/registry.js';
41
-
42
- // Storage
43
- export { FileHistoryStorage } from './storage/history.js';
45
+ } from './services/reporter-registry.js';
44
46
 
45
47
  // Export all types
46
48
  export * from './types/index.js';
49
+
50
+ // Utilities
51
+ export { findPackageRoot } from './utils/package.js';