modestbench 0.0.3 → 0.2.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 (394) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +39 -31
  3. package/dist/bootstrap.cjs +10 -12
  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 -7
  8. package/dist/bootstrap.js.map +1 -1
  9. package/dist/cli/commands/history.cjs +108 -265
  10. package/dist/cli/commands/history.cjs.map +1 -1
  11. package/dist/cli/commands/history.d.cts +75 -12
  12. package/dist/cli/commands/history.d.cts.map +1 -1
  13. package/dist/cli/commands/history.d.ts +75 -12
  14. package/dist/cli/commands/history.d.ts.map +1 -1
  15. package/dist/cli/commands/history.js +105 -267
  16. package/dist/cli/commands/history.js.map +1 -1
  17. package/dist/cli/commands/init.cjs +5 -4
  18. package/dist/cli/commands/init.cjs.map +1 -1
  19. package/dist/cli/commands/init.d.cts.map +1 -1
  20. package/dist/cli/commands/init.d.ts.map +1 -1
  21. package/dist/cli/commands/init.js +5 -4
  22. package/dist/cli/commands/init.js.map +1 -1
  23. package/dist/cli/commands/run.cjs +32 -9
  24. package/dist/cli/commands/run.cjs.map +1 -1
  25. package/dist/cli/commands/run.d.cts +1 -0
  26. package/dist/cli/commands/run.d.cts.map +1 -1
  27. package/dist/cli/commands/run.d.ts +1 -0
  28. package/dist/cli/commands/run.d.ts.map +1 -1
  29. package/dist/cli/commands/run.js +32 -9
  30. package/dist/cli/commands/run.js.map +1 -1
  31. package/dist/cli/index.cjs +336 -103
  32. package/dist/cli/index.cjs.map +1 -1
  33. package/dist/cli/index.d.cts +1 -2
  34. package/dist/cli/index.d.cts.map +1 -1
  35. package/dist/cli/index.d.ts +1 -2
  36. package/dist/cli/index.d.ts.map +1 -1
  37. package/dist/cli/index.js +332 -99
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/constants.cjs +53 -1
  40. package/dist/constants.cjs.map +1 -1
  41. package/dist/constants.d.cts +36 -0
  42. package/dist/constants.d.cts.map +1 -1
  43. package/dist/constants.d.ts +36 -0
  44. package/dist/constants.d.ts.map +1 -1
  45. package/dist/constants.js +52 -0
  46. package/dist/constants.js.map +1 -1
  47. package/dist/core/engine.cjs +23 -43
  48. package/dist/core/engine.cjs.map +1 -1
  49. package/dist/core/engine.d.cts +4 -3
  50. package/dist/core/engine.d.cts.map +1 -1
  51. package/dist/core/engine.d.ts +4 -3
  52. package/dist/core/engine.d.ts.map +1 -1
  53. package/dist/core/engine.js +23 -43
  54. package/dist/core/engine.js.map +1 -1
  55. package/dist/core/engines/accurate-engine.cjs +2 -1
  56. package/dist/core/engines/accurate-engine.cjs.map +1 -1
  57. package/dist/core/engines/accurate-engine.d.cts.map +1 -1
  58. package/dist/core/engines/accurate-engine.d.ts.map +1 -1
  59. package/dist/core/engines/accurate-engine.js +2 -1
  60. package/dist/core/engines/accurate-engine.js.map +1 -1
  61. package/dist/core/engines/tinybench-engine.cjs +6 -5
  62. package/dist/core/engines/tinybench-engine.cjs.map +1 -1
  63. package/dist/core/engines/tinybench-engine.d.cts.map +1 -1
  64. package/dist/core/engines/tinybench-engine.d.ts.map +1 -1
  65. package/dist/core/engines/tinybench-engine.js +6 -5
  66. package/dist/core/engines/tinybench-engine.js.map +1 -1
  67. package/dist/core/output-path-resolver.cjs +34 -0
  68. package/dist/core/output-path-resolver.cjs.map +1 -0
  69. package/dist/core/output-path-resolver.d.cts +10 -0
  70. package/dist/core/output-path-resolver.d.cts.map +1 -0
  71. package/dist/core/output-path-resolver.d.ts +10 -0
  72. package/dist/core/output-path-resolver.d.ts.map +1 -0
  73. package/dist/core/output-path-resolver.js +30 -0
  74. package/dist/core/output-path-resolver.js.map +1 -0
  75. package/dist/errors/base.cjs +130 -0
  76. package/dist/errors/base.cjs.map +1 -0
  77. package/dist/errors/base.d.cts +97 -0
  78. package/dist/errors/base.d.cts.map +1 -0
  79. package/dist/errors/base.d.ts +97 -0
  80. package/dist/errors/base.d.ts.map +1 -0
  81. package/dist/errors/base.js +124 -0
  82. package/dist/errors/base.js.map +1 -0
  83. package/dist/errors/cli.cjs +58 -0
  84. package/dist/errors/cli.cjs.map +1 -0
  85. package/dist/errors/cli.d.cts +44 -0
  86. package/dist/errors/cli.d.cts.map +1 -0
  87. package/dist/errors/cli.d.ts +44 -0
  88. package/dist/errors/cli.d.ts.map +1 -0
  89. package/dist/errors/cli.js +52 -0
  90. package/dist/errors/cli.js.map +1 -0
  91. package/dist/errors/configuration.cjs +48 -0
  92. package/dist/errors/configuration.cjs.map +1 -0
  93. package/dist/errors/configuration.d.cts +41 -0
  94. package/dist/errors/configuration.d.cts.map +1 -0
  95. package/dist/errors/configuration.d.ts +41 -0
  96. package/dist/errors/configuration.d.ts.map +1 -0
  97. package/dist/errors/configuration.js +41 -0
  98. package/dist/errors/configuration.js.map +1 -0
  99. package/dist/errors/execution.cjs +65 -0
  100. package/dist/errors/execution.cjs.map +1 -0
  101. package/dist/errors/execution.d.cts +56 -0
  102. package/dist/errors/execution.d.cts.map +1 -0
  103. package/dist/errors/execution.d.ts +56 -0
  104. package/dist/errors/execution.d.ts.map +1 -0
  105. package/dist/errors/execution.js +56 -0
  106. package/dist/errors/execution.js.map +1 -0
  107. package/dist/errors/file.cjs +56 -0
  108. package/dist/errors/file.cjs.map +1 -0
  109. package/dist/errors/file.d.cts +48 -0
  110. package/dist/errors/file.d.cts.map +1 -0
  111. package/dist/errors/file.d.ts +48 -0
  112. package/dist/errors/file.d.ts.map +1 -0
  113. package/dist/errors/file.js +48 -0
  114. package/dist/errors/file.js.map +1 -0
  115. package/dist/errors/index.cjs +59 -0
  116. package/dist/errors/index.cjs.map +1 -0
  117. package/dist/errors/index.d.cts +16 -0
  118. package/dist/errors/index.d.cts.map +1 -0
  119. package/dist/errors/index.d.ts +16 -0
  120. package/dist/errors/index.d.ts.map +1 -0
  121. package/dist/errors/index.js +24 -0
  122. package/dist/errors/index.js.map +1 -0
  123. package/dist/errors/reporter.cjs +38 -0
  124. package/dist/errors/reporter.cjs.map +1 -0
  125. package/dist/errors/reporter.d.cts +32 -0
  126. package/dist/errors/reporter.d.cts.map +1 -0
  127. package/dist/errors/reporter.d.ts +32 -0
  128. package/dist/errors/reporter.d.ts.map +1 -0
  129. package/dist/errors/reporter.js +32 -0
  130. package/dist/errors/reporter.js.map +1 -0
  131. package/dist/errors/storage.cjs +55 -0
  132. package/dist/errors/storage.cjs.map +1 -0
  133. package/dist/errors/storage.d.cts +47 -0
  134. package/dist/errors/storage.d.cts.map +1 -0
  135. package/dist/errors/storage.d.ts +47 -0
  136. package/dist/errors/storage.d.ts.map +1 -0
  137. package/dist/errors/storage.js +47 -0
  138. package/dist/errors/storage.js.map +1 -0
  139. package/dist/errors/validation.cjs +38 -0
  140. package/dist/errors/validation.cjs.map +1 -0
  141. package/dist/errors/validation.d.cts +32 -0
  142. package/dist/errors/validation.d.cts.map +1 -0
  143. package/dist/errors/validation.d.ts +32 -0
  144. package/dist/errors/validation.d.ts.map +1 -0
  145. package/dist/errors/validation.js +32 -0
  146. package/dist/errors/validation.js.map +1 -0
  147. package/dist/formatters/history/base.cjs +9 -0
  148. package/dist/formatters/history/base.cjs.map +1 -0
  149. package/dist/formatters/history/base.d.cts +26 -0
  150. package/dist/formatters/history/base.d.cts.map +1 -0
  151. package/dist/formatters/history/base.d.ts +26 -0
  152. package/dist/formatters/history/base.d.ts.map +1 -0
  153. package/dist/formatters/history/base.js +8 -0
  154. package/dist/formatters/history/base.js.map +1 -0
  155. package/dist/formatters/history/compare.cjs +127 -0
  156. package/dist/formatters/history/compare.cjs.map +1 -0
  157. package/dist/formatters/history/compare.d.cts +21 -0
  158. package/dist/formatters/history/compare.d.cts.map +1 -0
  159. package/dist/formatters/history/compare.d.ts +21 -0
  160. package/dist/formatters/history/compare.d.ts.map +1 -0
  161. package/dist/formatters/history/compare.js +123 -0
  162. package/dist/formatters/history/compare.js.map +1 -0
  163. package/dist/formatters/history/list.cjs +74 -0
  164. package/dist/formatters/history/list.cjs.map +1 -0
  165. package/dist/formatters/history/list.d.cts +25 -0
  166. package/dist/formatters/history/list.d.cts.map +1 -0
  167. package/dist/formatters/history/list.d.ts +25 -0
  168. package/dist/formatters/history/list.d.ts.map +1 -0
  169. package/dist/formatters/history/list.js +70 -0
  170. package/dist/formatters/history/list.js.map +1 -0
  171. package/dist/formatters/history/show.cjs +98 -0
  172. package/dist/formatters/history/show.cjs.map +1 -0
  173. package/dist/formatters/history/show.d.cts +21 -0
  174. package/dist/formatters/history/show.d.cts.map +1 -0
  175. package/dist/formatters/history/show.d.ts +21 -0
  176. package/dist/formatters/history/show.d.ts.map +1 -0
  177. package/dist/formatters/history/show.js +94 -0
  178. package/dist/formatters/history/show.js.map +1 -0
  179. package/dist/formatters/history/trends.cjs +194 -0
  180. package/dist/formatters/history/trends.cjs.map +1 -0
  181. package/dist/formatters/history/trends.d.cts +22 -0
  182. package/dist/formatters/history/trends.d.cts.map +1 -0
  183. package/dist/formatters/history/trends.d.ts +22 -0
  184. package/dist/formatters/history/trends.d.ts.map +1 -0
  185. package/dist/formatters/history/trends.js +190 -0
  186. package/dist/formatters/history/trends.js.map +1 -0
  187. package/dist/formatters/history/visualization.cjs +79 -0
  188. package/dist/formatters/history/visualization.cjs.map +1 -0
  189. package/dist/formatters/history/visualization.d.cts +24 -0
  190. package/dist/formatters/history/visualization.d.cts.map +1 -0
  191. package/dist/formatters/history/visualization.d.ts +24 -0
  192. package/dist/formatters/history/visualization.d.ts.map +1 -0
  193. package/dist/formatters/history/visualization.js +74 -0
  194. package/dist/formatters/history/visualization.js.map +1 -0
  195. package/dist/index.cjs +17 -20
  196. package/dist/index.cjs.map +1 -1
  197. package/dist/index.d.cts +6 -6
  198. package/dist/index.d.cts.map +1 -1
  199. package/dist/index.d.ts +6 -6
  200. package/dist/index.d.ts.map +1 -1
  201. package/dist/index.js +9 -11
  202. package/dist/index.js.map +1 -1
  203. package/dist/reporters/csv.cjs +5 -4
  204. package/dist/reporters/csv.cjs.map +1 -1
  205. package/dist/reporters/csv.d.cts +1 -1
  206. package/dist/reporters/csv.d.cts.map +1 -1
  207. package/dist/reporters/csv.d.ts +1 -1
  208. package/dist/reporters/csv.d.ts.map +1 -1
  209. package/dist/reporters/csv.js +4 -3
  210. package/dist/reporters/csv.js.map +1 -1
  211. package/dist/reporters/human.cjs +24 -62
  212. package/dist/reporters/human.cjs.map +1 -1
  213. package/dist/reporters/human.d.cts +1 -1
  214. package/dist/reporters/human.d.cts.map +1 -1
  215. package/dist/reporters/human.d.ts +1 -1
  216. package/dist/reporters/human.d.ts.map +1 -1
  217. package/dist/reporters/human.js +3 -41
  218. package/dist/reporters/human.js.map +1 -1
  219. package/dist/reporters/json.cjs +5 -4
  220. package/dist/reporters/json.cjs.map +1 -1
  221. package/dist/reporters/json.d.cts +1 -1
  222. package/dist/reporters/json.d.cts.map +1 -1
  223. package/dist/reporters/json.d.ts +1 -1
  224. package/dist/reporters/json.d.ts.map +1 -1
  225. package/dist/reporters/json.js +4 -3
  226. package/dist/reporters/json.js.map +1 -1
  227. package/dist/reporters/simple.cjs +3 -3
  228. package/dist/reporters/simple.cjs.map +1 -1
  229. package/dist/reporters/simple.d.cts +1 -1
  230. package/dist/reporters/simple.d.cts.map +1 -1
  231. package/dist/reporters/simple.d.ts +1 -1
  232. package/dist/reporters/simple.d.ts.map +1 -1
  233. package/dist/reporters/simple.js +2 -2
  234. package/dist/reporters/simple.js.map +1 -1
  235. package/dist/{config/manager.cjs → services/config-manager.cjs} +10 -4
  236. package/dist/services/config-manager.cjs.map +1 -0
  237. package/dist/{config/manager.d.cts → services/config-manager.d.cts} +1 -1
  238. package/dist/services/config-manager.d.cts.map +1 -0
  239. package/dist/{config/manager.d.ts → services/config-manager.d.ts} +1 -1
  240. package/dist/services/config-manager.d.ts.map +1 -0
  241. package/dist/{config/manager.js → services/config-manager.js} +10 -4
  242. package/dist/services/config-manager.js.map +1 -0
  243. package/dist/{core/loader.cjs → services/file-loader.cjs} +18 -7
  244. package/dist/services/file-loader.cjs.map +1 -0
  245. package/dist/{core/loader.d.cts → services/file-loader.d.cts} +1 -1
  246. package/dist/services/file-loader.d.cts.map +1 -0
  247. package/dist/{core/loader.d.ts → services/file-loader.d.ts} +1 -1
  248. package/dist/services/file-loader.d.ts.map +1 -0
  249. package/dist/{core/loader.js → services/file-loader.js} +18 -7
  250. package/dist/services/file-loader.js.map +1 -0
  251. package/dist/services/history/comparison.cjs +124 -0
  252. package/dist/services/history/comparison.cjs.map +1 -0
  253. package/dist/services/history/comparison.d.cts +18 -0
  254. package/dist/services/history/comparison.d.cts.map +1 -0
  255. package/dist/services/history/comparison.d.ts +18 -0
  256. package/dist/services/history/comparison.d.ts.map +1 -0
  257. package/dist/services/history/comparison.js +120 -0
  258. package/dist/services/history/comparison.js.map +1 -0
  259. package/dist/services/history/models.cjs +9 -0
  260. package/dist/services/history/models.cjs.map +1 -0
  261. package/dist/services/history/models.d.cts +139 -0
  262. package/dist/services/history/models.d.cts.map +1 -0
  263. package/dist/services/history/models.d.ts +139 -0
  264. package/dist/services/history/models.d.ts.map +1 -0
  265. package/dist/services/history/models.js +8 -0
  266. package/dist/services/history/models.js.map +1 -0
  267. package/dist/services/history/query.cjs +97 -0
  268. package/dist/services/history/query.cjs.map +1 -0
  269. package/dist/services/history/query.d.cts +38 -0
  270. package/dist/services/history/query.d.cts.map +1 -0
  271. package/dist/services/history/query.d.ts +38 -0
  272. package/dist/services/history/query.d.ts.map +1 -0
  273. package/dist/services/history/query.js +92 -0
  274. package/dist/services/history/query.js.map +1 -0
  275. package/dist/services/history/trend-analysis.cjs +187 -0
  276. package/dist/services/history/trend-analysis.cjs.map +1 -0
  277. package/dist/services/history/trend-analysis.d.cts +34 -0
  278. package/dist/services/history/trend-analysis.d.cts.map +1 -0
  279. package/dist/services/history/trend-analysis.d.ts +34 -0
  280. package/dist/services/history/trend-analysis.d.ts.map +1 -0
  281. package/dist/services/history/trend-analysis.js +179 -0
  282. package/dist/services/history/trend-analysis.js.map +1 -0
  283. package/dist/{storage/history.cjs → services/history-storage.cjs} +33 -12
  284. package/dist/services/history-storage.cjs.map +1 -0
  285. package/dist/{storage/history.d.cts → services/history-storage.d.cts} +1 -1
  286. package/dist/services/history-storage.d.cts.map +1 -0
  287. package/dist/{storage/history.d.ts → services/history-storage.d.ts} +1 -1
  288. package/dist/services/history-storage.d.ts.map +1 -0
  289. package/dist/{storage/history.js → services/history-storage.js} +33 -12
  290. package/dist/services/history-storage.js.map +1 -0
  291. package/dist/{progress/manager.cjs → services/progress-manager.cjs} +1 -1
  292. package/dist/services/progress-manager.cjs.map +1 -0
  293. package/dist/{progress/manager.d.cts → services/progress-manager.d.cts} +1 -1
  294. package/dist/services/progress-manager.d.cts.map +1 -0
  295. package/dist/{progress/manager.d.ts → services/progress-manager.d.ts} +1 -1
  296. package/dist/services/progress-manager.d.ts.map +1 -0
  297. package/dist/{progress/manager.js → services/progress-manager.js} +1 -1
  298. package/dist/services/progress-manager.js.map +1 -0
  299. package/dist/{reporters/registry.cjs → services/reporter-registry.cjs} +4 -3
  300. package/dist/services/reporter-registry.cjs.map +1 -0
  301. package/dist/{reporters/registry.d.cts → services/reporter-registry.d.cts} +1 -1
  302. package/dist/services/reporter-registry.d.cts.map +1 -0
  303. package/dist/{reporters/registry.d.ts → services/reporter-registry.d.ts} +1 -1
  304. package/dist/services/reporter-registry.d.ts.map +1 -0
  305. package/dist/{reporters/registry.js → services/reporter-registry.js} +4 -3
  306. package/dist/services/reporter-registry.js.map +1 -0
  307. package/dist/types/cli.d.cts +3 -0
  308. package/dist/types/cli.d.cts.map +1 -1
  309. package/dist/types/cli.d.ts +3 -0
  310. package/dist/types/cli.d.ts.map +1 -1
  311. package/dist/types/interfaces.d.cts +1 -34
  312. package/dist/types/interfaces.d.cts.map +1 -1
  313. package/dist/types/interfaces.d.ts +1 -34
  314. package/dist/types/interfaces.d.ts.map +1 -1
  315. package/dist/utils/ansi.cjs +61 -0
  316. package/dist/utils/ansi.cjs.map +1 -0
  317. package/dist/utils/ansi.d.cts +53 -0
  318. package/dist/utils/ansi.d.cts.map +1 -0
  319. package/dist/utils/ansi.d.ts +53 -0
  320. package/dist/utils/ansi.d.ts.map +1 -0
  321. package/dist/utils/ansi.js +57 -0
  322. package/dist/utils/ansi.js.map +1 -0
  323. package/package.json +10 -8
  324. package/src/bootstrap.ts +5 -7
  325. package/src/cli/commands/history.ts +195 -341
  326. package/src/cli/commands/init.ts +14 -4
  327. package/src/cli/commands/run.ts +52 -7
  328. package/src/cli/index.ts +393 -119
  329. package/src/constants.ts +60 -0
  330. package/src/core/engine.ts +40 -48
  331. package/src/core/engines/accurate-engine.ts +4 -1
  332. package/src/core/engines/tinybench-engine.ts +12 -5
  333. package/src/core/output-path-resolver.ts +38 -0
  334. package/src/errors/base.ts +152 -0
  335. package/src/errors/cli.ts +59 -0
  336. package/src/errors/configuration.ts +45 -0
  337. package/src/errors/execution.ts +62 -0
  338. package/src/errors/file.ts +53 -0
  339. package/src/errors/index.ts +71 -0
  340. package/src/errors/reporter.ts +35 -0
  341. package/src/errors/storage.ts +52 -0
  342. package/src/errors/validation.ts +35 -0
  343. package/src/formatters/history/base.ts +28 -0
  344. package/src/formatters/history/compare.ts +186 -0
  345. package/src/formatters/history/list.ts +101 -0
  346. package/src/formatters/history/show.ts +155 -0
  347. package/src/formatters/history/trends.ts +281 -0
  348. package/src/formatters/history/visualization.ts +93 -0
  349. package/src/index.ts +10 -14
  350. package/src/reporters/csv.ts +5 -3
  351. package/src/reporters/human.ts +3 -43
  352. package/src/reporters/json.ts +5 -3
  353. package/src/reporters/simple.ts +2 -2
  354. package/src/{config/manager.ts → services/config-manager.ts} +13 -3
  355. package/src/{core/loader.ts → services/file-loader.ts} +28 -6
  356. package/src/services/history/comparison.ts +130 -0
  357. package/src/services/history/models.ts +148 -0
  358. package/src/services/history/query.ts +116 -0
  359. package/src/services/history/trend-analysis.ts +238 -0
  360. package/src/{storage/history.ts → services/history-storage.ts} +58 -11
  361. package/src/{reporters/registry.ts → services/reporter-registry.ts} +9 -2
  362. package/src/types/cli.ts +3 -0
  363. package/src/types/interfaces.ts +0 -43
  364. package/src/utils/ansi.ts +59 -0
  365. package/dist/config/manager.cjs.map +0 -1
  366. package/dist/config/manager.d.cts.map +0 -1
  367. package/dist/config/manager.d.ts.map +0 -1
  368. package/dist/config/manager.js.map +0 -1
  369. package/dist/core/error-manager.cjs +0 -303
  370. package/dist/core/error-manager.cjs.map +0 -1
  371. package/dist/core/error-manager.d.cts +0 -77
  372. package/dist/core/error-manager.d.cts.map +0 -1
  373. package/dist/core/error-manager.d.ts +0 -77
  374. package/dist/core/error-manager.d.ts.map +0 -1
  375. package/dist/core/error-manager.js +0 -299
  376. package/dist/core/error-manager.js.map +0 -1
  377. package/dist/core/loader.cjs.map +0 -1
  378. package/dist/core/loader.d.cts.map +0 -1
  379. package/dist/core/loader.d.ts.map +0 -1
  380. package/dist/core/loader.js.map +0 -1
  381. package/dist/progress/manager.cjs.map +0 -1
  382. package/dist/progress/manager.d.cts.map +0 -1
  383. package/dist/progress/manager.d.ts.map +0 -1
  384. package/dist/progress/manager.js.map +0 -1
  385. package/dist/reporters/registry.cjs.map +0 -1
  386. package/dist/reporters/registry.d.cts.map +0 -1
  387. package/dist/reporters/registry.d.ts.map +0 -1
  388. package/dist/reporters/registry.js.map +0 -1
  389. package/dist/storage/history.cjs.map +0 -1
  390. package/dist/storage/history.d.cts.map +0 -1
  391. package/dist/storage/history.d.ts.map +0 -1
  392. package/dist/storage/history.js.map +0 -1
  393. package/src/core/error-manager.ts +0 -372
  394. /package/src/{progress/manager.ts → services/progress-manager.ts} +0 -0
@@ -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,18 +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
- // Error handling
18
- export { ModestBenchErrorManager } from './core/error-manager.js';
19
-
20
- export { BenchmarkFileLoader } from './core/loader.js';
21
-
22
15
  // Statistical utilities
23
16
  export {
24
17
  calculateStatistics,
@@ -26,21 +19,24 @@ export {
26
19
  type SampleStatistics,
27
20
  } from './core/stats-utils.js';
28
21
 
29
- // Progress tracking
30
- export { ModestBenchProgressManager } from './progress/manager.js';
22
+ // Error classes
23
+ export * from './errors/index.js';
24
+
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
 
30
+ // Services
31
+ export { ModestBenchConfigurationManager } from './services/config-manager.js';
32
+ export { BenchmarkFileLoader } from './services/file-loader.js';
33
+ export { FileHistoryStorage } from './services/history-storage.js';
34
+ export { ModestBenchProgressManager } from './services/progress-manager.js';
36
35
  export {
37
36
  BaseReporter,
38
37
  CompositeReporter,
39
38
  ModestBenchReporterRegistry,
40
- } from './reporters/registry.js';
41
-
42
- // Storage
43
- export { FileHistoryStorage } from './storage/history.js';
39
+ } from './services/reporter-registry.js';
44
40
 
45
41
  // Export all types
46
42
  export * from './types/index.js';
@@ -17,7 +17,8 @@ import type {
17
17
  TaskResult,
18
18
  } from '../types/index.js';
19
19
 
20
- import { BaseReporter } from './registry.js';
20
+ import { ReporterOutputError } from '../errors/index.js';
21
+ import { BaseReporter } from '../services/reporter-registry.js';
21
22
 
22
23
  /**
23
24
  * CSV column definitions for task results
@@ -340,7 +341,7 @@ export class CsvReporter extends BaseReporter {
340
341
  */
341
342
  private async writeToFile(csvContent: string): Promise<void> {
342
343
  if (!this.outputPath) {
343
- throw new Error('Output path not specified');
344
+ throw new ReporterOutputError('Output path not specified');
344
345
  }
345
346
 
346
347
  try {
@@ -351,8 +352,9 @@ export class CsvReporter extends BaseReporter {
351
352
  // Write CSV file
352
353
  writeFileSync(this.outputPath, csvContent, 'utf8');
353
354
  } catch (error) {
354
- throw new Error(
355
+ throw new ReporterOutputError(
355
356
  `Failed to write CSV output to ${this.outputPath}: ${error instanceof Error ? error.message : String(error)}`,
357
+ { cause: error },
356
358
  );
357
359
  }
358
360
  }
@@ -15,48 +15,8 @@ import type {
15
15
  TaskResult,
16
16
  } from '../types/index.js';
17
17
 
18
- import { BaseReporter } from './registry.js';
19
-
20
- /**
21
- * ANSI color codes for terminal output
22
- */
23
- const colors = {
24
- bold: '\x1b[1m',
25
- brightBlue: '\x1b[94m',
26
- brightCyan: '\x1b[96m',
27
- brightMagenta: '\x1b[95m',
28
- brightRed: '\x1b[91m',
29
- brightWhite: '\x1b[97m',
30
- cyan: '\x1b[36m',
31
- dim: '\x1b[2m',
32
- gray: '\x1b[90m',
33
- green: '\x1b[32m',
34
- magenta: '\x1b[35m',
35
- red: '\x1b[31m',
36
- reset: '\x1b[0m',
37
- underline: '\x1b[4m',
38
- white: '\x1b[37m',
39
- } as const;
40
-
41
- /**
42
- * CP437-inspired ANSI art characters
43
- */
44
- const ansiChars = {
45
- approx: '≈',
46
- // Block elements for gradients
47
- block: {
48
- dark: '▓',
49
- full: '█',
50
- light: '░',
51
- medium: '▒',
52
- },
53
- bullet: '•',
54
- // Symbols
55
- checkmark: '√',
56
- cross: '×',
57
- plusMinus: '±',
58
- smallSquare: '▪',
59
- } as const;
18
+ import { BaseReporter } from '../services/reporter-registry.js';
19
+ import { ansiChars, colors } from '../utils/ansi.js';
60
20
 
61
21
  /**
62
22
  * Human-readable console reporter with colorized output
@@ -157,7 +117,7 @@ export class HumanReporter extends BaseReporter {
157
117
  );
158
118
  } else {
159
119
  this.printLine(
160
- `${this.colorize('brightCyan', ansiChars.checkmark + ' All tests passed:')} ${this.colorize('brightWhite', String(totalPassed))}`,
120
+ `${this.colorize('brightCyan', ansiChars.checkmark + ' All tasks passed:')} ${this.colorize('brightWhite', String(totalPassed))}`,
161
121
  );
162
122
  }
163
123
  this.printLine(
@@ -16,7 +16,8 @@ import type {
16
16
  TaskResult,
17
17
  } from '../types/index.js';
18
18
 
19
- import { BaseReporter } from './registry.js';
19
+ import { ReporterOutputError } from '../errors/index.js';
20
+ import { BaseReporter } from '../services/reporter-registry.js';
20
21
 
21
22
  /**
22
23
  * JSON output structure for benchmark results
@@ -266,7 +267,7 @@ export class JsonReporter extends BaseReporter {
266
267
  */
267
268
  private async writeToFile(output: JsonOutput): Promise<void> {
268
269
  if (!this.outputPath) {
269
- throw new Error('Output path not specified');
270
+ throw new ReporterOutputError('Output path not specified');
270
271
  }
271
272
 
272
273
  try {
@@ -281,8 +282,9 @@ export class JsonReporter extends BaseReporter {
281
282
 
282
283
  writeFileSync(this.outputPath, jsonString, 'utf8');
283
284
  } catch (error) {
284
- throw new Error(
285
+ throw new ReporterOutputError(
285
286
  `Failed to write JSON output to ${this.outputPath}: ${error instanceof Error ? error.message : String(error)}`,
287
+ { cause: error },
286
288
  );
287
289
  }
288
290
  }
@@ -15,7 +15,7 @@ import type {
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
@@ -91,7 +91,7 @@ export class SimpleReporter extends BaseReporter {
91
91
  console.log(`${symbols.cross} Failed: ${totalFailed}`);
92
92
  console.log(`${symbols.checkmark} Passed: ${totalPassed}`);
93
93
  } else {
94
- console.log(`${symbols.checkmark} All tests passed: ${totalPassed}`);
94
+ console.log(`${symbols.checkmark} All tasks passed: ${totalPassed}`);
95
95
  }
96
96
 
97
97
  console.log(`- Files: ${totalFiles}`);
@@ -9,6 +9,7 @@
9
9
  import { cosmiconfig } from 'cosmiconfig';
10
10
  import { resolve } from 'node:path';
11
11
 
12
+ import type { ModestBenchError } from '../errors/base.js';
12
13
  import type {
13
14
  ConfigurationManager,
14
15
  ModestBenchConfig,
@@ -17,7 +18,9 @@ import type {
17
18
  ValidationWarning,
18
19
  } from '../types/index.js';
19
20
 
20
- import { safeParseConfig } from './schema.js';
21
+ import { safeParseConfig } from '../config/schema.js';
22
+ import { ErrorCodes } from '../constants.js';
23
+ import { ConfigLoadError, ConfigValidationError } from '../errors/index.js';
21
24
 
22
25
  /**
23
26
  * Get the default reporter based on TTY status and environment
@@ -175,15 +178,22 @@ export class ModestBenchConfigurationManager implements ConfigurationManager {
175
178
  // 3. Validate final configuration
176
179
  const validation = this.validate(finalConfig);
177
180
  if (!validation.valid) {
178
- throw new Error(
181
+ throw new ConfigValidationError(
179
182
  `Configuration validation failed: ${validation.errors.map((e) => e.message).join(', ')}`,
180
183
  );
181
184
  }
182
185
 
183
186
  return finalConfig;
184
187
  } catch (error) {
185
- throw new Error(
188
+ // Re-throw our custom errors
189
+ if (
190
+ (error as ModestBenchError).code === ErrorCodes.CONFIG_VALIDATION_FAILED
191
+ ) {
192
+ throw error;
193
+ }
194
+ throw new ConfigLoadError(
186
195
  `Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`,
196
+ { cause: error },
187
197
  );
188
198
  }
189
199
  }
@@ -22,7 +22,12 @@ import {
22
22
  BENCHMARK_FILE_EXTENSIONS,
23
23
  BENCHMARK_FILE_PATTERN,
24
24
  } from '../constants.js';
25
- import { benchmarkFileSchema } from './benchmark-schema.js';
25
+ import { benchmarkFileSchema } from '../core/benchmark-schema.js';
26
+ import {
27
+ FileDiscoveryError,
28
+ FileLoadError,
29
+ StructureValidationError,
30
+ } from '../errors/index.js';
26
31
 
27
32
  /**
28
33
  * File change notification for watch functionality
@@ -106,8 +111,9 @@ export class BenchmarkFileLoader implements FileLoader {
106
111
 
107
112
  return supportedFiles.sort();
108
113
  } catch (error) {
109
- throw new Error(
114
+ throw new FileDiscoveryError(
110
115
  `File discovery failed: ${error instanceof Error ? error.message : String(error)}`,
116
+ { cause: error },
111
117
  );
112
118
  }
113
119
  }
@@ -120,7 +126,7 @@ export class BenchmarkFileLoader implements FileLoader {
120
126
  // Basic file checks (existence, extension)
121
127
  const basicValidation = await this.validate(filePath);
122
128
  if (!basicValidation.valid) {
123
- throw new Error(
129
+ throw new FileLoadError(
124
130
  `Invalid benchmark file: ${basicValidation.errors.map((e) => e.message).join(', ')}`,
125
131
  );
126
132
  }
@@ -160,7 +166,7 @@ export class BenchmarkFileLoader implements FileLoader {
160
166
  // Validate the loaded exports structure with Zod
161
167
  const structureValidation = this.validateExports(filePath, exports);
162
168
  if (!structureValidation.valid || !structureValidation.data) {
163
- throw new Error(
169
+ throw new StructureValidationError(
164
170
  `Invalid benchmark structure: ${structureValidation.errors.map((e) => e.message).join(', ')}`,
165
171
  );
166
172
  }
@@ -185,8 +191,16 @@ export class BenchmarkFileLoader implements FileLoader {
185
191
  },
186
192
  };
187
193
  } catch (error) {
188
- throw new Error(
194
+ // Re-throw our custom errors
195
+ if (
196
+ error instanceof FileLoadError ||
197
+ error instanceof StructureValidationError
198
+ ) {
199
+ throw error;
200
+ }
201
+ throw new FileLoadError(
189
202
  `Failed to load file ${filePath}: ${error instanceof Error ? error.message : String(error)}`,
203
+ { cause: error },
190
204
  );
191
205
  }
192
206
  }
@@ -199,8 +213,16 @@ export class BenchmarkFileLoader implements FileLoader {
199
213
  const loadPromises = filePaths.map((filePath) => this.load(filePath));
200
214
  return await Promise.all(loadPromises);
201
215
  } catch (error) {
202
- throw new Error(
216
+ // Re-throw our custom errors (from individual load calls)
217
+ if (
218
+ error instanceof FileLoadError ||
219
+ error instanceof StructureValidationError
220
+ ) {
221
+ throw error;
222
+ }
223
+ throw new FileLoadError(
203
224
  `Failed to load files: ${error instanceof Error ? error.message : String(error)}`,
225
+ { cause: error },
204
226
  );
205
227
  }
206
228
  }