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,130 @@
1
+ /**
2
+ * Benchmark Comparison Service
3
+ *
4
+ * Handles comparison logic between two benchmark runs, calculating performance
5
+ * differences and categorizing tasks.
6
+ */
7
+
8
+ import type { BenchmarkRun } from '../../types/index.js';
9
+ import type { CompareResult, TaskComparison } from './models.js';
10
+
11
+ /**
12
+ * Service for comparing benchmark runs
13
+ */
14
+ export class ComparisonService {
15
+ /**
16
+ * Compare two benchmark runs and produce detailed comparison result
17
+ */
18
+ compareRuns(run1: BenchmarkRun, run2: BenchmarkRun): CompareResult {
19
+ // Build task maps for comparison
20
+ const tasksMap1 = new Map<string, TaskComparison>();
21
+ const tasksMap2 = new Map<string, TaskComparison>();
22
+
23
+ // Extract tasks from run1
24
+ for (const file of run1.files) {
25
+ for (const suite of file.suites) {
26
+ for (const task of suite.tasks) {
27
+ if (!task.error) {
28
+ const key = `${file.filePath}::${suite.name}::${task.name}`;
29
+ tasksMap1.set(key, {
30
+ file: file.filePath,
31
+ inBoth: false,
32
+ percentChange: 0,
33
+ run1: {
34
+ cv: task.cv,
35
+ iterations: task.iterations,
36
+ max: task.max,
37
+ mean: task.mean,
38
+ min: task.min,
39
+ },
40
+ suite: suite.name,
41
+ task: task.name,
42
+ });
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ // Extract tasks from run2 and merge
49
+ for (const file of run2.files) {
50
+ for (const suite of file.suites) {
51
+ for (const task of suite.tasks) {
52
+ if (!task.error) {
53
+ const key = `${file.filePath}::${suite.name}::${task.name}`;
54
+ const existing = tasksMap1.get(key);
55
+
56
+ if (existing && existing.run1) {
57
+ // Task exists in both runs - calculate comparison
58
+ const percentChange =
59
+ ((task.mean - existing.run1.mean) / existing.run1.mean) * 100;
60
+
61
+ tasksMap1.set(key, {
62
+ ...existing,
63
+ inBoth: true,
64
+ percentChange,
65
+ run2: {
66
+ cv: task.cv,
67
+ iterations: task.iterations,
68
+ max: task.max,
69
+ mean: task.mean,
70
+ min: task.min,
71
+ },
72
+ });
73
+ } else {
74
+ // Task only in run2
75
+ tasksMap2.set(key, {
76
+ file: file.filePath,
77
+ inBoth: false,
78
+ percentChange: 0,
79
+ run2: {
80
+ cv: task.cv,
81
+ iterations: task.iterations,
82
+ max: task.max,
83
+ mean: task.mean,
84
+ min: task.min,
85
+ },
86
+ suite: suite.name,
87
+ task: task.name,
88
+ });
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ // Separate tasks into categories
96
+ const tasksInBoth: TaskComparison[] = [];
97
+ const tasksOnlyIn1: TaskComparison[] = [];
98
+ const tasksOnlyIn2: TaskComparison[] = [];
99
+
100
+ for (const task of tasksMap1.values()) {
101
+ if (task.inBoth) {
102
+ tasksInBoth.push(task);
103
+ } else {
104
+ tasksOnlyIn1.push(task);
105
+ }
106
+ }
107
+
108
+ for (const task of tasksMap2.values()) {
109
+ tasksOnlyIn2.push(task);
110
+ }
111
+
112
+ return {
113
+ run1: {
114
+ endTime: run1.endTime,
115
+ id: run1.id,
116
+ startTime: run1.startTime,
117
+ summary: run1.summary,
118
+ },
119
+ run2: {
120
+ endTime: run2.endTime,
121
+ id: run2.id,
122
+ startTime: run2.startTime,
123
+ summary: run2.summary,
124
+ },
125
+ tasksInBoth,
126
+ tasksOnlyInRun1: tasksOnlyIn1,
127
+ tasksOnlyInRun2: tasksOnlyIn2,
128
+ };
129
+ }
130
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Data Models for History Services
3
+ *
4
+ * Type definitions for history command data structures, shared between
5
+ * services, formatters, and CLI handlers.
6
+ */
7
+
8
+ import type { BenchmarkRun } from '../../types/index.js';
9
+
10
+ /**
11
+ * Result from comparing two benchmark runs
12
+ */
13
+ export interface CompareResult {
14
+ run1: {
15
+ endTime: Date;
16
+ id: string;
17
+ startTime: Date;
18
+ summary: {
19
+ failedTasks: number;
20
+ passedTasks: number;
21
+ totalFiles: number;
22
+ totalTasks: number;
23
+ };
24
+ };
25
+ run2: {
26
+ endTime: Date;
27
+ id: string;
28
+ startTime: Date;
29
+ summary: {
30
+ failedTasks: number;
31
+ passedTasks: number;
32
+ totalFiles: number;
33
+ totalTasks: number;
34
+ };
35
+ };
36
+ tasksInBoth: TaskComparison[];
37
+ tasksOnlyInRun1: TaskComparison[];
38
+ tasksOnlyInRun2: TaskComparison[];
39
+ }
40
+
41
+ /**
42
+ * Distribution bucket for histogram visualization
43
+ */
44
+ export interface DistributionBucket {
45
+ count: number;
46
+ label: string;
47
+ max: number;
48
+ min: number;
49
+ }
50
+
51
+ /**
52
+ * Result from listing historical runs
53
+ */
54
+ export interface HistoryListResult {
55
+ runs: Array<{
56
+ duration: number;
57
+ id: string;
58
+ startTime: Date;
59
+ summary: {
60
+ failedTasks: number;
61
+ passedTasks: number;
62
+ totalFiles: number;
63
+ totalTasks: number;
64
+ };
65
+ }>;
66
+ totalCount: number;
67
+ }
68
+
69
+ /**
70
+ * Show command result (just wraps BenchmarkRun for consistency)
71
+ */
72
+ export type ShowResult = BenchmarkRun;
73
+
74
+ /**
75
+ * Task comparison result between two runs
76
+ */
77
+ export interface TaskComparison {
78
+ file: string;
79
+ inBoth: boolean;
80
+ percentChange: number;
81
+ run1?: {
82
+ cv: number;
83
+ iterations: number;
84
+ max: number;
85
+ mean: number;
86
+ min: number;
87
+ };
88
+ run2?: {
89
+ cv: number;
90
+ iterations: number;
91
+ max: number;
92
+ mean: number;
93
+ min: number;
94
+ };
95
+ suite: string;
96
+ task: string;
97
+ }
98
+
99
+ /**
100
+ * Complete trend analysis for a single task
101
+ */
102
+ export interface TrendData {
103
+ confidence: number;
104
+ dataPoints: TrendDataPoint[];
105
+ percentChange: number;
106
+ runs: number;
107
+ statistics: TrendStatistics;
108
+ task: string;
109
+ trend: 'degrading' | 'improving' | 'stable';
110
+ }
111
+
112
+ /**
113
+ * Single data point in a trend series
114
+ */
115
+ export interface TrendDataPoint {
116
+ date: Date;
117
+ mean: number;
118
+ }
119
+
120
+ /**
121
+ * Result from analyzing performance trends
122
+ */
123
+ export interface TrendsResult {
124
+ lowConfidenceRegressions: TrendData[];
125
+ regressions: TrendData[];
126
+ runs: number;
127
+ summary: {
128
+ degradingTasks: number;
129
+ improvingTasks: number;
130
+ stableTasks: number;
131
+ totalTasks: number;
132
+ };
133
+ timespan: {
134
+ end: Date;
135
+ start: Date;
136
+ };
137
+ trends: TrendData[];
138
+ }
139
+
140
+ /**
141
+ * Statistical metrics for trend analysis
142
+ */
143
+ export interface TrendStatistics {
144
+ mean: number;
145
+ median: number;
146
+ stdDeviation: number;
147
+ variance: number;
148
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * History Query Service
3
+ *
4
+ * Handles querying benchmark run history with date parsing and filtering.
5
+ */
6
+
7
+ import type {
8
+ BenchmarkRun,
9
+ HistoryQuery,
10
+ HistoryStorage,
11
+ } from '../../types/index.js';
12
+
13
+ /**
14
+ * Service for querying historical benchmark runs
15
+ */
16
+ export class HistoryQueryService {
17
+ constructor(private readonly storage: HistoryStorage) {}
18
+
19
+ /**
20
+ * Query runs with automatic date string parsing
21
+ */
22
+ async queryWithDateParsing(options: {
23
+ limit?: number;
24
+ pattern?: string;
25
+ since?: string;
26
+ tags?: string[];
27
+ until?: string;
28
+ }): Promise<BenchmarkRun[]> {
29
+ // Build query object all at once
30
+ const query: Partial<HistoryQuery> = {
31
+ ...(options.since && { since: parseDate(options.since) }),
32
+ ...(options.until && { until: parseDate(options.until) }),
33
+ ...(options.pattern && { pattern: options.pattern }),
34
+ ...(options.tags && options.tags.length > 0 && { tags: options.tags }),
35
+ ...(options.limit && { limit: options.limit }),
36
+ };
37
+
38
+ return await this.storage.queryRuns(query);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Parse date string (ISO 8601 or relative)
44
+ *
45
+ * Supports:
46
+ *
47
+ * - ISO 8601: "2025-10-24T12:00:00Z", "2025-10-24"
48
+ * - Relative: "1 day ago", "3 weeks ago", "2 hours ago"
49
+ * - Shorthand: "1d", "2w", "3m", "6h"
50
+ *
51
+ * @param dateStr - Date string to parse
52
+ * @returns Parsed Date object
53
+ * @throws Error if date format is invalid
54
+ */
55
+ export const parseDate = (dateStr: string): Date => {
56
+ if (!dateStr || dateStr.trim() === '') {
57
+ throw new Error(`Invalid date format: "${dateStr}"`);
58
+ }
59
+
60
+ // Try parsing as ISO 8601 first
61
+ const isoDate = new Date(dateStr);
62
+ if (!isNaN(isoDate.getTime())) {
63
+ return isoDate;
64
+ }
65
+
66
+ // Parse relative dates like "1 week ago", "3 days ago"
67
+ const relativePattern = /^(\d+)\s+(hour|day|week|month)s?\s+ago$/i;
68
+ const relativeMatch = dateStr.trim().match(relativePattern);
69
+
70
+ if (relativeMatch && relativeMatch[1] && relativeMatch[2]) {
71
+ const amount = parseInt(relativeMatch[1], 10);
72
+ const unit = relativeMatch[2].toLowerCase();
73
+
74
+ if (amount <= 0) {
75
+ throw new Error(`Invalid date format: "${dateStr}"`);
76
+ }
77
+
78
+ const now = new Date();
79
+ const msPerUnit: Record<string, number> = {
80
+ day: 24 * 60 * 60 * 1000,
81
+ hour: 60 * 60 * 1000,
82
+ month: 30 * 24 * 60 * 60 * 1000, // Approximate
83
+ week: 7 * 24 * 60 * 60 * 1000,
84
+ };
85
+
86
+ const offset = amount * (msPerUnit[unit] || 0);
87
+ return new Date(now.getTime() - offset);
88
+ }
89
+
90
+ // Parse shorthand formats like "1d", "2w", "3m", "6h"
91
+ // cspell:ignore hdwm
92
+ const shorthandPattern = /^(\d+)([hdwm])$/i;
93
+ const shorthandMatch = dateStr.trim().match(shorthandPattern);
94
+
95
+ if (shorthandMatch && shorthandMatch[1] && shorthandMatch[2]) {
96
+ const amount = parseInt(shorthandMatch[1], 10);
97
+ const unit = shorthandMatch[2].toLowerCase();
98
+
99
+ if (amount <= 0) {
100
+ throw new Error(`Invalid date format: "${dateStr}"`);
101
+ }
102
+
103
+ const now = new Date();
104
+ const msPerUnit: Record<string, number> = {
105
+ d: 24 * 60 * 60 * 1000,
106
+ h: 60 * 60 * 1000,
107
+ m: 30 * 24 * 60 * 60 * 1000, // Approximate month
108
+ w: 7 * 24 * 60 * 60 * 1000,
109
+ };
110
+
111
+ const offset = amount * (msPerUnit[unit] || 0);
112
+ return new Date(now.getTime() - offset);
113
+ }
114
+
115
+ throw new Error(`Invalid date format: "${dateStr}"`);
116
+ };
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Trend Analysis Service
3
+ *
4
+ * Statistical analysis of benchmark performance trends over time, including
5
+ * regression detection and trend classification.
6
+ */
7
+
8
+ import type { BenchmarkRun } from '../../types/index.js';
9
+ import type {
10
+ TrendData,
11
+ TrendDataPoint,
12
+ TrendsResult,
13
+ TrendStatistics,
14
+ } from './models.js';
15
+
16
+ /**
17
+ * Service for analyzing performance trends
18
+ */
19
+ export class TrendAnalysisService {
20
+ /**
21
+ * Analyze trends across multiple benchmark runs
22
+ */
23
+ analyzeTrends(runs: BenchmarkRun[]): TrendsResult {
24
+ // Build trend data for each task across runs
25
+ const taskTrendsMap = new Map<string, TrendDataPoint[]>();
26
+
27
+ for (const run of runs) {
28
+ for (const file of run.files) {
29
+ for (const suite of file.suites) {
30
+ for (const task of suite.tasks) {
31
+ if (!task.error) {
32
+ const key = `${file.filePath}::${suite.name}::${task.name}`;
33
+ const dataPoints = taskTrendsMap.get(key) || [];
34
+ dataPoints.push({
35
+ date: new Date(run.startTime),
36
+ mean: task.mean,
37
+ });
38
+ taskTrendsMap.set(key, dataPoints);
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+
45
+ // Calculate trends for each task
46
+ const trendsData: TrendData[] = [];
47
+
48
+ for (const [key, dataPoints] of taskTrendsMap.entries()) {
49
+ // Sort by date (oldest first)
50
+ dataPoints.sort((a, b) => a.date.getTime() - b.date.getTime());
51
+
52
+ const [_filePath, suiteName, taskName] = key.split('::');
53
+
54
+ const statistics = calculateStatistics(dataPoints);
55
+ const trend = calculateTrend(dataPoints);
56
+ const percentChange = calculatePercentChange(dataPoints);
57
+
58
+ trendsData.push({
59
+ confidence: 95, // Fixed confidence level for now
60
+ dataPoints,
61
+ percentChange,
62
+ runs: dataPoints.length,
63
+ statistics,
64
+ task: `${suiteName} › ${taskName}`,
65
+ trend,
66
+ });
67
+ }
68
+
69
+ // Sort by absolute percent change (most significant first)
70
+ trendsData.sort(
71
+ (a, b) => Math.abs(b.percentChange) - Math.abs(a.percentChange),
72
+ );
73
+
74
+ // Calculate summary statistics
75
+ const summary = {
76
+ degradingTasks: trendsData.filter((t) => t.trend === 'degrading').length,
77
+ improvingTasks: trendsData.filter((t) => t.trend === 'improving').length,
78
+ stableTasks: trendsData.filter((t) => t.trend === 'stable').length,
79
+ totalTasks: trendsData.length,
80
+ };
81
+
82
+ // Get timespan
83
+ const firstRun = runs[runs.length - 1];
84
+ const lastRun = runs[0];
85
+ const timespan = {
86
+ end: lastRun ? new Date(lastRun.startTime) : new Date(),
87
+ start: firstRun ? new Date(firstRun.startTime) : new Date(),
88
+ };
89
+
90
+ // Detect regressions (require minimum 5 runs for confidence)
91
+ const minRunsForRegression = 5;
92
+ const regressions = trendsData.filter((t) =>
93
+ detectRegression(t, 5, minRunsForRegression),
94
+ );
95
+
96
+ // Also track low-confidence potential regressions (2-4 runs showing degradation)
97
+ const lowConfidenceRegressions = trendsData.filter(
98
+ (t) =>
99
+ t.trend === 'degrading' &&
100
+ t.percentChange >= 5 &&
101
+ t.runs < minRunsForRegression &&
102
+ t.runs >= 2,
103
+ );
104
+
105
+ return {
106
+ lowConfidenceRegressions,
107
+ regressions,
108
+ runs: runs.length,
109
+ summary,
110
+ timespan,
111
+ trends: trendsData,
112
+ };
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Calculate percent change from first to last data point
118
+ */
119
+ export const calculatePercentChange = (
120
+ dataPoints: TrendDataPoint[],
121
+ ): number => {
122
+ if (dataPoints.length === 0 || dataPoints.length === 1) {
123
+ return 0;
124
+ }
125
+
126
+ const firstPoint = dataPoints[0];
127
+ const lastPoint = dataPoints[dataPoints.length - 1];
128
+
129
+ if (!firstPoint || !lastPoint) {
130
+ return 0;
131
+ }
132
+
133
+ const first = firstPoint.mean;
134
+ const last = lastPoint.mean;
135
+
136
+ if (first === 0) {
137
+ return 0; // Avoid division by zero
138
+ }
139
+
140
+ return ((last - first) / first) * 100;
141
+ };
142
+
143
+ /**
144
+ * Calculate statistical metrics from data points
145
+ */
146
+ export const calculateStatistics = (
147
+ dataPoints: TrendDataPoint[],
148
+ ): TrendStatistics => {
149
+ if (dataPoints.length === 0) {
150
+ throw new Error('Cannot calculate statistics for empty data points array');
151
+ }
152
+
153
+ const values = dataPoints.map((dp) => dp.mean);
154
+ const n = values.length;
155
+
156
+ // Calculate mean
157
+ const mean = values.reduce((sum, val) => sum + val, 0) / n;
158
+
159
+ // Calculate median
160
+ const sorted = [...values].sort((a, b) => a - b);
161
+ const median =
162
+ n % 2 === 0
163
+ ? ((sorted[n / 2 - 1] ?? 0) + (sorted[n / 2] ?? 0)) / 2
164
+ : (sorted[Math.floor(n / 2)] ?? 0);
165
+
166
+ // Calculate variance and standard deviation
167
+ const variance =
168
+ n === 1
169
+ ? 0
170
+ : values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / n;
171
+ const stdDeviation = Math.sqrt(variance);
172
+
173
+ return {
174
+ mean,
175
+ median,
176
+ stdDeviation,
177
+ variance,
178
+ };
179
+ };
180
+
181
+ /**
182
+ * Calculate trend direction from data points using linear regression
183
+ */
184
+ export const calculateTrend = (
185
+ dataPoints: TrendDataPoint[],
186
+ ): 'degrading' | 'improving' | 'stable' => {
187
+ if (dataPoints.length === 0) {
188
+ return 'stable';
189
+ }
190
+
191
+ if (dataPoints.length === 1) {
192
+ return 'stable';
193
+ }
194
+
195
+ // Simple linear regression to determine slope
196
+ const n = dataPoints.length;
197
+ const x = Array.from({ length: n }, (_, i) => i); // Time indices
198
+ const y = dataPoints.map((dp) => dp.mean);
199
+
200
+ const sumX = x.reduce((a, b) => a + b, 0);
201
+ const sumY = y.reduce((a, b) => a + b, 0);
202
+ const sumXY = x.reduce((sum, xi, i) => sum + xi * (y[i] ?? 0), 0);
203
+ const sumXX = x.reduce((sum, xi) => sum + xi * xi, 0);
204
+
205
+ // Calculate slope: (n*sumXY - sumX*sumY) / (n*sumXX - sumX*sumX)
206
+ const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
207
+
208
+ // Determine significance threshold (5% of mean)
209
+ const meanValue = sumY / n;
210
+ const significanceThreshold = Math.abs(meanValue * 0.05);
211
+
212
+ // Classify trend based on slope
213
+ if (Math.abs(slope) < significanceThreshold / n) {
214
+ return 'stable';
215
+ } else if (slope < 0) {
216
+ // Negative slope = values decreasing = performance improving
217
+ return 'improving';
218
+ } else {
219
+ // Positive slope = values increasing = performance degrading
220
+ return 'degrading';
221
+ }
222
+ };
223
+
224
+ /**
225
+ * Detect if a trend represents a performance regression
226
+ */
227
+ export const detectRegression = (
228
+ trendData: TrendData,
229
+ threshold: number,
230
+ minRuns: number,
231
+ ): boolean => {
232
+ // Regression is a degrading trend with percent change exceeding threshold
233
+ return (
234
+ trendData.trend === 'degrading' &&
235
+ trendData.percentChange >= threshold &&
236
+ trendData.runs >= minRuns
237
+ );
238
+ };