modestbench 0.2.0 → 0.3.1

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 (357) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +131 -34
  3. package/dist/cli/commands/analyze.cjs +60 -0
  4. package/dist/cli/commands/analyze.cjs.map +1 -0
  5. package/dist/cli/commands/analyze.d.cts +35 -0
  6. package/dist/cli/commands/analyze.d.cts.map +1 -0
  7. package/dist/cli/commands/analyze.d.ts +35 -0
  8. package/dist/cli/commands/analyze.d.ts.map +1 -0
  9. package/dist/cli/commands/analyze.js +56 -0
  10. package/dist/cli/commands/analyze.js.map +1 -0
  11. package/dist/cli/commands/baseline.cjs +404 -0
  12. package/dist/cli/commands/baseline.cjs.map +1 -0
  13. package/dist/cli/commands/baseline.d.cts +72 -0
  14. package/dist/cli/commands/baseline.d.cts.map +1 -0
  15. package/dist/cli/commands/baseline.d.ts +72 -0
  16. package/dist/cli/commands/baseline.d.ts.map +1 -0
  17. package/dist/cli/commands/baseline.js +396 -0
  18. package/dist/cli/commands/baseline.js.map +1 -0
  19. package/dist/cli/commands/history.d.cts +1 -1
  20. package/dist/cli/commands/history.d.cts.map +1 -1
  21. package/dist/cli/commands/history.d.ts +1 -1
  22. package/dist/cli/commands/history.d.ts.map +1 -1
  23. package/dist/cli/commands/init.cjs +99 -166
  24. package/dist/cli/commands/init.cjs.map +1 -1
  25. package/dist/cli/commands/init.d.cts +4 -4
  26. package/dist/cli/commands/init.d.cts.map +1 -1
  27. package/dist/cli/commands/init.d.ts +4 -4
  28. package/dist/cli/commands/init.d.ts.map +1 -1
  29. package/dist/cli/commands/init.js +99 -166
  30. package/dist/cli/commands/init.js.map +1 -1
  31. package/dist/cli/commands/run.cjs +146 -127
  32. package/dist/cli/commands/run.cjs.map +1 -1
  33. package/dist/cli/commands/run.d.cts +16 -3
  34. package/dist/cli/commands/run.d.cts.map +1 -1
  35. package/dist/cli/commands/run.d.ts +16 -3
  36. package/dist/cli/commands/run.d.ts.map +1 -1
  37. package/dist/cli/commands/run.js +145 -93
  38. package/dist/cli/commands/run.js.map +1 -1
  39. package/dist/cli/index.cjs +583 -394
  40. package/dist/cli/index.cjs.map +1 -1
  41. package/dist/cli/index.d.cts +4 -16
  42. package/dist/cli/index.d.cts.map +1 -1
  43. package/dist/cli/index.d.ts +4 -16
  44. package/dist/cli/index.d.ts.map +1 -1
  45. package/dist/cli/index.js +575 -386
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/config/budget-schema.cjs +172 -0
  48. package/dist/config/budget-schema.cjs.map +1 -0
  49. package/dist/config/budget-schema.d.cts +59 -0
  50. package/dist/config/budget-schema.d.cts.map +1 -0
  51. package/dist/config/budget-schema.d.ts +59 -0
  52. package/dist/config/budget-schema.d.ts.map +1 -0
  53. package/dist/config/budget-schema.js +166 -0
  54. package/dist/config/budget-schema.js.map +1 -0
  55. package/dist/config/schema.cjs +182 -2
  56. package/dist/config/schema.cjs.map +1 -1
  57. package/dist/config/schema.d.cts +122 -3
  58. package/dist/config/schema.d.cts.map +1 -1
  59. package/dist/config/schema.d.ts +122 -3
  60. package/dist/config/schema.d.ts.map +1 -1
  61. package/dist/config/schema.js +180 -1
  62. package/dist/config/schema.js.map +1 -1
  63. package/dist/constants.cjs +45 -2
  64. package/dist/constants.cjs.map +1 -1
  65. package/dist/constants.d.cts +41 -0
  66. package/dist/constants.d.cts.map +1 -1
  67. package/dist/constants.d.ts +41 -0
  68. package/dist/constants.d.ts.map +1 -1
  69. package/dist/constants.js +44 -1
  70. package/dist/constants.js.map +1 -1
  71. package/dist/core/engine.cjs +114 -23
  72. package/dist/core/engine.cjs.map +1 -1
  73. package/dist/core/engine.d.cts +7 -7
  74. package/dist/core/engine.d.cts.map +1 -1
  75. package/dist/core/engine.d.ts +7 -7
  76. package/dist/core/engine.d.ts.map +1 -1
  77. package/dist/core/engine.js +115 -24
  78. package/dist/core/engine.js.map +1 -1
  79. package/dist/core/engines/accurate-engine.cjs +171 -36
  80. package/dist/core/engines/accurate-engine.cjs.map +1 -1
  81. package/dist/core/engines/accurate-engine.d.cts +5 -0
  82. package/dist/core/engines/accurate-engine.d.cts.map +1 -1
  83. package/dist/core/engines/accurate-engine.d.ts +5 -0
  84. package/dist/core/engines/accurate-engine.d.ts.map +1 -1
  85. package/dist/core/engines/accurate-engine.js +171 -36
  86. package/dist/core/engines/accurate-engine.js.map +1 -1
  87. package/dist/core/engines/tinybench-engine.cjs +3 -2
  88. package/dist/core/engines/tinybench-engine.cjs.map +1 -1
  89. package/dist/core/engines/tinybench-engine.d.cts.map +1 -1
  90. package/dist/core/engines/tinybench-engine.d.ts.map +1 -1
  91. package/dist/core/engines/tinybench-engine.js +3 -2
  92. package/dist/core/engines/tinybench-engine.js.map +1 -1
  93. package/dist/core/output-path-resolver.cjs +8 -1
  94. package/dist/core/output-path-resolver.cjs.map +1 -1
  95. package/dist/core/output-path-resolver.d.cts.map +1 -1
  96. package/dist/core/output-path-resolver.d.ts.map +1 -1
  97. package/dist/core/output-path-resolver.js +9 -2
  98. package/dist/core/output-path-resolver.js.map +1 -1
  99. package/dist/errors/base.cjs +12 -3
  100. package/dist/errors/base.cjs.map +1 -1
  101. package/dist/errors/base.d.cts +7 -0
  102. package/dist/errors/base.d.cts.map +1 -1
  103. package/dist/errors/base.d.ts +7 -0
  104. package/dist/errors/base.d.ts.map +1 -1
  105. package/dist/errors/base.js +10 -2
  106. package/dist/errors/base.js.map +1 -1
  107. package/dist/errors/budget.cjs +37 -0
  108. package/dist/errors/budget.cjs.map +1 -0
  109. package/dist/errors/budget.d.cts +31 -0
  110. package/dist/errors/budget.d.cts.map +1 -0
  111. package/dist/errors/budget.d.ts +31 -0
  112. package/dist/errors/budget.d.ts.map +1 -0
  113. package/dist/errors/budget.js +33 -0
  114. package/dist/errors/budget.js.map +1 -0
  115. package/dist/errors/index.cjs +4 -1
  116. package/dist/errors/index.cjs.map +1 -1
  117. package/dist/errors/index.d.cts +1 -0
  118. package/dist/errors/index.d.cts.map +1 -1
  119. package/dist/errors/index.d.ts +1 -0
  120. package/dist/errors/index.d.ts.map +1 -1
  121. package/dist/errors/index.js +2 -0
  122. package/dist/errors/index.js.map +1 -1
  123. package/dist/index.cjs +13 -1
  124. package/dist/index.cjs.map +1 -1
  125. package/dist/index.d.cts +5 -0
  126. package/dist/index.d.cts.map +1 -1
  127. package/dist/index.d.ts +5 -0
  128. package/dist/index.d.ts.map +1 -1
  129. package/dist/index.js +7 -0
  130. package/dist/index.js.map +1 -1
  131. package/dist/reporters/csv.cjs +37 -17
  132. package/dist/reporters/csv.cjs.map +1 -1
  133. package/dist/reporters/csv.d.cts +3 -6
  134. package/dist/reporters/csv.d.cts.map +1 -1
  135. package/dist/reporters/csv.d.ts +3 -6
  136. package/dist/reporters/csv.d.ts.map +1 -1
  137. package/dist/reporters/csv.js +37 -17
  138. package/dist/reporters/csv.js.map +1 -1
  139. package/dist/reporters/human.cjs +290 -67
  140. package/dist/reporters/human.cjs.map +1 -1
  141. package/dist/reporters/human.d.cts +25 -13
  142. package/dist/reporters/human.d.cts.map +1 -1
  143. package/dist/reporters/human.d.ts +25 -13
  144. package/dist/reporters/human.d.ts.map +1 -1
  145. package/dist/reporters/human.js +290 -67
  146. package/dist/reporters/human.js.map +1 -1
  147. package/dist/reporters/json.cjs +23 -48
  148. package/dist/reporters/json.cjs.map +1 -1
  149. package/dist/reporters/json.d.cts +2 -28
  150. package/dist/reporters/json.d.cts.map +1 -1
  151. package/dist/reporters/json.d.ts +2 -28
  152. package/dist/reporters/json.d.ts.map +1 -1
  153. package/dist/reporters/json.js +25 -50
  154. package/dist/reporters/json.js.map +1 -1
  155. package/dist/reporters/profile-human.cjs +154 -0
  156. package/dist/reporters/profile-human.cjs.map +1 -0
  157. package/dist/reporters/profile-human.d.cts +44 -0
  158. package/dist/reporters/profile-human.d.cts.map +1 -0
  159. package/dist/reporters/profile-human.d.ts +44 -0
  160. package/dist/reporters/profile-human.d.ts.map +1 -0
  161. package/dist/reporters/profile-human.js +147 -0
  162. package/dist/reporters/profile-human.js.map +1 -0
  163. package/dist/reporters/simple.cjs +67 -45
  164. package/dist/reporters/simple.cjs.map +1 -1
  165. package/dist/reporters/simple.d.cts +14 -14
  166. package/dist/reporters/simple.d.cts.map +1 -1
  167. package/dist/reporters/simple.d.ts +14 -14
  168. package/dist/reporters/simple.d.ts.map +1 -1
  169. package/dist/reporters/simple.js +67 -45
  170. package/dist/reporters/simple.js.map +1 -1
  171. package/dist/schema/modestbench-config.schema.json +153 -0
  172. package/dist/services/baseline-storage.cjs +151 -0
  173. package/dist/services/baseline-storage.cjs.map +1 -0
  174. package/dist/services/baseline-storage.d.cts +55 -0
  175. package/dist/services/baseline-storage.d.cts.map +1 -0
  176. package/dist/services/baseline-storage.d.ts +55 -0
  177. package/dist/services/baseline-storage.d.ts.map +1 -0
  178. package/dist/services/baseline-storage.js +147 -0
  179. package/dist/services/baseline-storage.js.map +1 -0
  180. package/dist/services/budget-evaluator.cjs +146 -0
  181. package/dist/services/budget-evaluator.cjs.map +1 -0
  182. package/dist/services/budget-evaluator.d.cts +29 -0
  183. package/dist/services/budget-evaluator.d.cts.map +1 -0
  184. package/dist/services/budget-evaluator.d.ts +29 -0
  185. package/dist/services/budget-evaluator.d.ts.map +1 -0
  186. package/dist/services/budget-evaluator.js +142 -0
  187. package/dist/services/budget-evaluator.js.map +1 -0
  188. package/dist/services/config-manager.cjs +24 -10
  189. package/dist/services/config-manager.cjs.map +1 -1
  190. package/dist/services/config-manager.d.cts +6 -1
  191. package/dist/services/config-manager.d.cts.map +1 -1
  192. package/dist/services/config-manager.d.ts +6 -1
  193. package/dist/services/config-manager.d.ts.map +1 -1
  194. package/dist/services/config-manager.js +24 -10
  195. package/dist/services/config-manager.js.map +1 -1
  196. package/dist/services/file-loader.cjs +3 -6
  197. package/dist/services/file-loader.cjs.map +1 -1
  198. package/dist/services/file-loader.d.cts.map +1 -1
  199. package/dist/services/file-loader.d.ts.map +1 -1
  200. package/dist/services/file-loader.js +3 -6
  201. package/dist/services/file-loader.js.map +1 -1
  202. package/dist/services/profiler/profile-filter.cjs +116 -0
  203. package/dist/services/profiler/profile-filter.cjs.map +1 -0
  204. package/dist/services/profiler/profile-filter.d.cts +20 -0
  205. package/dist/services/profiler/profile-filter.d.cts.map +1 -0
  206. package/dist/services/profiler/profile-filter.d.ts +20 -0
  207. package/dist/services/profiler/profile-filter.d.ts.map +1 -0
  208. package/dist/services/profiler/profile-filter.js +112 -0
  209. package/dist/services/profiler/profile-filter.js.map +1 -0
  210. package/dist/services/profiler/profile-parser.cjs +139 -0
  211. package/dist/services/profiler/profile-parser.cjs.map +1 -0
  212. package/dist/services/profiler/profile-parser.d.cts +18 -0
  213. package/dist/services/profiler/profile-parser.d.cts.map +1 -0
  214. package/dist/services/profiler/profile-parser.d.ts +18 -0
  215. package/dist/services/profiler/profile-parser.d.ts.map +1 -0
  216. package/dist/services/profiler/profile-parser.js +132 -0
  217. package/dist/services/profiler/profile-parser.js.map +1 -0
  218. package/dist/services/profiler/profile-runner.cjs +90 -0
  219. package/dist/services/profiler/profile-runner.cjs.map +1 -0
  220. package/dist/services/profiler/profile-runner.d.cts +29 -0
  221. package/dist/services/profiler/profile-runner.d.cts.map +1 -0
  222. package/dist/services/profiler/profile-runner.d.ts +29 -0
  223. package/dist/services/profiler/profile-runner.d.ts.map +1 -0
  224. package/dist/services/profiler/profile-runner.js +86 -0
  225. package/dist/services/profiler/profile-runner.js.map +1 -0
  226. package/dist/services/progress-manager.cjs +10 -2
  227. package/dist/services/progress-manager.cjs.map +1 -1
  228. package/dist/services/progress-manager.d.cts +2 -0
  229. package/dist/services/progress-manager.d.cts.map +1 -1
  230. package/dist/services/progress-manager.d.ts +2 -0
  231. package/dist/services/progress-manager.d.ts.map +1 -1
  232. package/dist/services/progress-manager.js +10 -2
  233. package/dist/services/progress-manager.js.map +1 -1
  234. package/dist/services/reporter-registry.cjs +18 -24
  235. package/dist/services/reporter-registry.cjs.map +1 -1
  236. package/dist/services/reporter-registry.d.cts +18 -40
  237. package/dist/services/reporter-registry.d.cts.map +1 -1
  238. package/dist/services/reporter-registry.d.ts +18 -40
  239. package/dist/services/reporter-registry.d.ts.map +1 -1
  240. package/dist/services/reporter-registry.js +18 -24
  241. package/dist/services/reporter-registry.js.map +1 -1
  242. package/dist/types/budgets.cjs +8 -0
  243. package/dist/types/budgets.cjs.map +1 -0
  244. package/dist/types/budgets.d.cts +149 -0
  245. package/dist/types/budgets.d.cts.map +1 -0
  246. package/dist/types/budgets.d.ts +149 -0
  247. package/dist/types/budgets.d.ts.map +1 -0
  248. package/dist/types/budgets.js +7 -0
  249. package/dist/types/budgets.js.map +1 -0
  250. package/dist/types/cli.cjs +2 -11
  251. package/dist/types/cli.cjs.map +1 -1
  252. package/dist/types/cli.d.cts +3 -227
  253. package/dist/types/cli.d.cts.map +1 -1
  254. package/dist/types/cli.d.ts +3 -227
  255. package/dist/types/cli.d.ts.map +1 -1
  256. package/dist/types/cli.js +2 -11
  257. package/dist/types/cli.js.map +1 -1
  258. package/dist/types/core.cjs +6 -1
  259. package/dist/types/core.cjs.map +1 -1
  260. package/dist/types/core.d.cts +15 -2
  261. package/dist/types/core.d.cts.map +1 -1
  262. package/dist/types/core.d.ts +15 -2
  263. package/dist/types/core.d.ts.map +1 -1
  264. package/dist/types/core.js +2 -1
  265. package/dist/types/core.js.map +1 -1
  266. package/dist/types/index.cjs +5 -0
  267. package/dist/types/index.cjs.map +1 -1
  268. package/dist/types/index.d.cts +2 -0
  269. package/dist/types/index.d.cts.map +1 -1
  270. package/dist/types/index.d.ts +2 -0
  271. package/dist/types/index.d.ts.map +1 -1
  272. package/dist/types/index.js +2 -0
  273. package/dist/types/index.js.map +1 -1
  274. package/dist/types/interfaces.d.cts +19 -8
  275. package/dist/types/interfaces.d.cts.map +1 -1
  276. package/dist/types/interfaces.d.ts +19 -8
  277. package/dist/types/interfaces.d.ts.map +1 -1
  278. package/dist/types/profiler.cjs +11 -0
  279. package/dist/types/profiler.cjs.map +1 -0
  280. package/dist/types/profiler.d.cts +102 -0
  281. package/dist/types/profiler.d.cts.map +1 -0
  282. package/dist/types/profiler.d.ts +102 -0
  283. package/dist/types/profiler.d.ts.map +1 -0
  284. package/dist/types/profiler.js +10 -0
  285. package/dist/types/profiler.js.map +1 -0
  286. package/dist/types/utility.cjs.map +1 -1
  287. package/dist/types/utility.d.cts +0 -8
  288. package/dist/types/utility.d.cts.map +1 -1
  289. package/dist/types/utility.d.ts +0 -8
  290. package/dist/types/utility.d.ts.map +1 -1
  291. package/dist/types/utility.js.map +1 -1
  292. package/dist/utils/identifiers.cjs +32 -0
  293. package/dist/utils/identifiers.cjs.map +1 -0
  294. package/dist/utils/identifiers.d.cts +32 -0
  295. package/dist/utils/identifiers.d.cts.map +1 -0
  296. package/dist/utils/identifiers.d.ts +32 -0
  297. package/dist/utils/identifiers.d.ts.map +1 -0
  298. package/dist/utils/identifiers.js +27 -0
  299. package/dist/utils/identifiers.js.map +1 -0
  300. package/dist/utils/package.cjs +40 -0
  301. package/dist/utils/package.cjs.map +1 -0
  302. package/dist/utils/package.d.cts +15 -0
  303. package/dist/utils/package.d.cts.map +1 -0
  304. package/dist/utils/package.d.ts +15 -0
  305. package/dist/utils/package.d.ts.map +1 -0
  306. package/dist/utils/package.js +33 -0
  307. package/dist/utils/package.js.map +1 -0
  308. package/dist/utils/type-guards.cjs +48 -0
  309. package/dist/utils/type-guards.cjs.map +1 -0
  310. package/dist/utils/type-guards.d.cts +22 -0
  311. package/dist/utils/type-guards.d.cts.map +1 -0
  312. package/dist/utils/type-guards.d.ts +22 -0
  313. package/dist/utils/type-guards.d.ts.map +1 -0
  314. package/dist/utils/type-guards.js +43 -0
  315. package/dist/utils/type-guards.js.map +1 -0
  316. package/package.json +18 -19
  317. package/src/cli/commands/analyze.ts +101 -0
  318. package/src/cli/commands/baseline.ts +577 -0
  319. package/src/cli/commands/history.ts +1 -1
  320. package/src/cli/commands/init.ts +116 -194
  321. package/src/cli/commands/run.ts +183 -113
  322. package/src/cli/index.ts +425 -183
  323. package/src/config/budget-schema.ts +189 -0
  324. package/src/config/schema.ts +260 -1
  325. package/src/constants.ts +53 -1
  326. package/src/core/engine.ts +169 -22
  327. package/src/core/engines/accurate-engine.ts +195 -44
  328. package/src/core/engines/tinybench-engine.ts +3 -2
  329. package/src/core/output-path-resolver.ts +10 -2
  330. package/src/errors/base.ts +11 -2
  331. package/src/errors/budget.ts +38 -0
  332. package/src/errors/index.ts +3 -0
  333. package/src/index.ts +9 -0
  334. package/src/reporters/csv.ts +54 -25
  335. package/src/reporters/human.ts +434 -115
  336. package/src/reporters/json.ts +26 -71
  337. package/src/reporters/profile-human.ts +210 -0
  338. package/src/reporters/simple.ts +88 -54
  339. package/src/services/baseline-storage.ts +199 -0
  340. package/src/services/budget-evaluator.ts +182 -0
  341. package/src/services/config-manager.ts +24 -9
  342. package/src/services/file-loader.ts +3 -6
  343. package/src/services/profiler/profile-filter.ts +147 -0
  344. package/src/services/profiler/profile-parser.ts +194 -0
  345. package/src/services/profiler/profile-runner.ts +121 -0
  346. package/src/services/progress-manager.ts +12 -2
  347. package/src/services/reporter-registry.ts +46 -81
  348. package/src/types/budgets.ts +180 -0
  349. package/src/types/cli.ts +5 -238
  350. package/src/types/core.ts +52 -10
  351. package/src/types/index.ts +5 -0
  352. package/src/types/interfaces.ts +24 -6
  353. package/src/types/profiler.ts +135 -0
  354. package/src/types/utility.ts +0 -10
  355. package/src/utils/identifiers.ts +58 -0
  356. package/src/utils/package.ts +35 -0
  357. package/src/utils/type-guards.ts +51 -0
@@ -7,13 +7,17 @@
7
7
  */
8
8
 
9
9
  import { randomBytes } from 'node:crypto';
10
+ import { relative as pathRelative } from 'node:path';
10
11
 
11
12
  import type {
13
+ BaselineSummaryData,
12
14
  BenchmarkDefinition,
13
15
  BenchmarkEngine,
14
16
  BenchmarkRun,
15
17
  BenchmarkSuite,
16
18
  BenchmarkTask,
19
+ Budget,
20
+ BudgetSummary,
17
21
  CiInfo,
18
22
  ConfigurationManager,
19
23
  EnvironmentInfo,
@@ -26,7 +30,9 @@ import type {
26
30
  Reporter,
27
31
  ReporterRegistry,
28
32
  RunConfiguration,
33
+ RunId,
29
34
  SuiteResult,
35
+ TaskId,
30
36
  TaskResult,
31
37
  ValidationError,
32
38
  ValidationResult,
@@ -35,11 +41,15 @@ import type {
35
41
 
36
42
  import {
37
43
  BenchmarkExecutionError,
44
+ BudgetExceededError,
38
45
  FileDiscoveryError,
39
46
  SchemaValidationError,
40
47
  SetupError,
41
48
  StructureValidationError,
42
49
  } from '../errors/index.js';
50
+ import { BaselineStorageService } from '../services/baseline-storage.js';
51
+ import { BudgetEvaluator } from '../services/budget-evaluator.js';
52
+ import { createRunId, createTaskId } from '../types/index.js';
43
53
 
44
54
  /**
45
55
  * Dependencies required by the BenchmarkEngine
@@ -78,6 +88,20 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
78
88
  this.progressManager = dependencies.progressManager;
79
89
  }
80
90
 
91
+ /**
92
+ * Generate a unique run ID
93
+ *
94
+ * Uses crypto.randomBytes for cryptographically random 7-character IDs.
95
+ * Format: 7 lowercase alphanumeric characters (e.g., "k3m9x2p")
96
+ */
97
+ private static generateRunId(this: void): RunId {
98
+ // Generate random bytes, convert to hex, then to base36, take first 7 chars
99
+ const hex = randomBytes(4).toString('hex');
100
+ const num = parseInt(hex, 16);
101
+ const id = num.toString(36).padStart(7, '0').substring(0, 7);
102
+ return createRunId(id);
103
+ }
104
+
81
105
  /**
82
106
  * Discover benchmark files matching the pattern(s)
83
107
  */
@@ -136,7 +160,7 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
136
160
  }
137
161
 
138
162
  // 4. Initialize progress tracking
139
- const runId = this.generateRunId();
163
+ const runId = ModestBenchEngine.generateRunId();
140
164
 
141
165
  // Pre-calculate total tasks for progress tracking
142
166
  let totalTasks = 0;
@@ -212,9 +236,9 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
212
236
 
213
237
  // Register progress callbacks with reporters that support them
214
238
  for (const reporter of reporters) {
215
- if (typeof reporter.onProgress === 'function') {
239
+ if (reporter.onProgress) {
216
240
  this.progressManager.onProgress((state) => {
217
- void reporter.onProgress(state);
241
+ void reporter.onProgress?.(state);
218
242
  });
219
243
  }
220
244
  }
@@ -227,12 +251,17 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
227
251
 
228
252
  for (const filePath of files) {
229
253
  try {
230
- // Call reporter onFileStart
231
- await this.callReporters(reporters, 'onFileStart', filePath);
254
+ // Normalize file path to be relative to cwd
255
+ const cwd = config.cwd || process.cwd();
256
+ const relativePath = pathRelative(cwd, filePath);
257
+
258
+ // Call reporter onFileStart with relative path
259
+ await this.callReporters(reporters, 'onFileStart', relativePath);
232
260
 
233
261
  const fileResult = await this.executeBenchmarkFile(
234
262
  filePath,
235
263
  mergedConfig,
264
+ cwd,
236
265
  reporters,
237
266
  signal,
238
267
  );
@@ -246,6 +275,16 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
246
275
  currentFile: filePath,
247
276
  filesCompleted: fileResults.length,
248
277
  });
278
+
279
+ // Check for bail: stop execution if any task failed
280
+ if (mergedConfig.bail) {
281
+ const hasFailedTask = fileResult.suites.some((suite) =>
282
+ suite.tasks.some((task) => task.error),
283
+ );
284
+ if (hasFailedTask) {
285
+ break;
286
+ }
287
+ }
249
288
  } catch (error) {
250
289
  const fileError =
251
290
  error instanceof Error ? error : new Error(String(error));
@@ -267,6 +306,11 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
267
306
 
268
307
  // Call reporter onFileEnd for error case
269
308
  await this.callReporters(reporters, 'onFileEnd', errorResult);
309
+
310
+ // Check bail flag for file-level errors
311
+ if (mergedConfig.bail) {
312
+ break;
313
+ }
270
314
  }
271
315
  }
272
316
 
@@ -302,10 +346,103 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
302
346
  }
303
347
 
304
348
  const overallMean = totalOperations > 0 ? totalTime / totalOperations : 0;
349
+ // Evaluate budgets if configured
350
+ let budgetSummary: BudgetSummary | undefined;
351
+
352
+ if (config.budgets && Object.keys(config.budgets).length > 0) {
353
+ const evaluator = new BudgetEvaluator();
354
+ const baselineStorage = new BaselineStorageService(process.cwd());
355
+
356
+ // Collect task results
357
+ const taskResults = new Map<TaskId, TaskResult>();
358
+
359
+ for (const file of fileResults) {
360
+ for (const suite of file.suites) {
361
+ for (const task of suite.tasks) {
362
+ if (!task.error) {
363
+ // file.filePath is already relative to cwd
364
+ const taskId = createTaskId(
365
+ file.filePath,
366
+ suite.name,
367
+ task.name,
368
+ );
369
+ taskResults.set(taskId, task);
370
+ }
371
+ }
372
+ }
373
+ }
374
+
375
+ // Load baseline data if needed for relative budgets
376
+ let baselineData: Map<TaskId, BaselineSummaryData> | undefined;
377
+
378
+ // Check if any budgets use relative thresholds
379
+ const hasRelativeBudgets = Object.values(config.budgets).some(
380
+ (budget) => (budget as Budget).relative,
381
+ );
382
+
383
+ if (hasRelativeBudgets) {
384
+ const baselineName =
385
+ config.baseline || (await baselineStorage.getDefault());
386
+
387
+ if (baselineName) {
388
+ const baseline = await baselineStorage.getBaseline(baselineName);
389
+
390
+ if (baseline) {
391
+ // Cast keys to TaskId since they come from validated baseline storage
392
+ baselineData = new Map(
393
+ Object.entries(baseline.summary) as [
394
+ TaskId,
395
+ BaselineSummaryData,
396
+ ][],
397
+ );
398
+ } else {
399
+ console.warn(
400
+ `Warning: Baseline "${baselineName}" not found. Relative budgets will be skipped.`,
401
+ );
402
+ }
403
+ } else {
404
+ console.warn(
405
+ 'Warning: Relative budgets configured but no baseline specified. Relative budgets will be skipped.',
406
+ );
407
+ }
408
+ }
409
+
410
+ // Evaluate budgets
411
+ budgetSummary = evaluator.evaluateRun(
412
+ config.budgets as Record<string, Budget>,
413
+ taskResults,
414
+ baselineData,
415
+ );
416
+
417
+ // Notify reporters of budget results
418
+ for (const reporter of reporters) {
419
+ if (reporter.onBudgetResult) {
420
+ await reporter.onBudgetResult(budgetSummary);
421
+ }
422
+ }
423
+
424
+ // Handle budget failures based on budgetMode
425
+ if (budgetSummary.failed > 0) {
426
+ const mode = config.budgetMode || 'fail';
427
+
428
+ if (mode === 'fail') {
429
+ throw new BudgetExceededError(
430
+ `${budgetSummary.failed} of ${budgetSummary.total} budget(s) exceeded`,
431
+ budgetSummary,
432
+ );
433
+ } else if (mode === 'warn') {
434
+ console.warn(
435
+ `Warning: ${budgetSummary.failed} of ${budgetSummary.total} budget(s) exceeded`,
436
+ );
437
+ }
438
+ // mode === 'report': just include in output, don't fail
439
+ }
440
+ }
305
441
 
306
442
  const endTime = new Date();
307
443
  const finalRun: BenchmarkRun = {
308
444
  ...initialRun,
445
+ budgetSummary,
309
446
  duration: endTime.getTime() - startTime.getTime(),
310
447
  endTime,
311
448
  files: fileResults,
@@ -489,6 +626,7 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
489
626
  private async executeBenchmarkFile(
490
627
  filePath: string,
491
628
  config: ModestBenchConfig,
629
+ cwd: string,
492
630
  reporters: Reporter[] = [],
493
631
  signal?: AbortSignal,
494
632
  ): Promise<FileResult> {
@@ -514,7 +652,7 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
514
652
  benchmarkDef.suites,
515
653
  )) {
516
654
  // Use shared filtering logic
517
- const { anyTaskMatches, suiteMatches } =
655
+ const { anyTaskMatches, suiteMatches, tasksToRun } =
518
656
  this.getFilteredTasksForSuite(
519
657
  suiteData,
520
658
  fileTags,
@@ -527,6 +665,15 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
527
665
  continue;
528
666
  }
529
667
 
668
+ // Emit suite init with task names for pre-calculation
669
+ const taskNames = tasksToRun.map(([name]) => name);
670
+ await this.callReporters(
671
+ reporters,
672
+ 'onSuiteInit',
673
+ suiteName,
674
+ taskNames,
675
+ );
676
+
530
677
  await this.callReporters(reporters, 'onSuiteStart', suiteName);
531
678
  const suiteResult = await this.executeBenchmarkSuite(
532
679
  suiteName,
@@ -543,11 +690,14 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
543
690
 
544
691
  const endTime = new Date();
545
692
 
693
+ // Normalize file path to be relative to cwd
694
+ const relativePath = pathRelative(cwd, filePath);
695
+
546
696
  return {
547
697
  config: benchmarkDef.config,
548
698
  duration: endTime.getTime() - startTime.getTime(),
549
699
  endTime,
550
- filePath,
700
+ filePath: relativePath,
551
701
  startTime,
552
702
  suites: suiteResults,
553
703
  };
@@ -556,11 +706,14 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
556
706
  const executionError =
557
707
  error instanceof Error ? error : new Error(String(error));
558
708
 
709
+ // Normalize file path to be relative to cwd
710
+ const relativePath = pathRelative(cwd, filePath);
711
+
559
712
  return {
560
713
  duration: endTime.getTime() - startTime.getTime(),
561
714
  endTime,
562
715
  error: executionError,
563
- filePath,
716
+ filePath: relativePath,
564
717
  startTime,
565
718
  suites: [],
566
719
  };
@@ -624,6 +777,12 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
624
777
  }
625
778
  }
626
779
 
780
+ // Merge suite-level config with global config
781
+ // Suite-level config takes precedence over global config
782
+ const mergedConfig = suiteData.config
783
+ ? { ...config, ...suiteData.config }
784
+ : config;
785
+
627
786
  try {
628
787
  // Process each task that passed filtering
629
788
  for (const [taskName, taskData] of tasksToRun) {
@@ -632,13 +791,14 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
632
791
  // Mark task as in-progress (shows as 0.5 progress for current task)
633
792
  const currentState = this.progressManager.getState();
634
793
  this.progressManager.update({
794
+ currentTask: taskName,
635
795
  tasksCompleted: currentState.tasksCompleted + 0.5,
636
796
  });
637
797
 
638
798
  const taskResult = await this.executeBenchmarkTask(
639
799
  taskName,
640
800
  taskData,
641
- config,
801
+ mergedConfig,
642
802
  reporters,
643
803
  signal,
644
804
  );
@@ -697,19 +857,6 @@ export abstract class ModestBenchEngine implements BenchmarkEngine {
697
857
  }
698
858
  }
699
859
 
700
- /**
701
- * Generate a unique run ID
702
- *
703
- * Uses crypto.randomBytes for cryptographically random 7-character IDs.
704
- * Format: 7 lowercase alphanumeric characters (e.g., "k3m9x2p")
705
- */
706
- private generateRunId(): string {
707
- // Generate random bytes, convert to hex, then to base36, take first 7 chars
708
- const hex = randomBytes(4).toString('hex');
709
- const num = parseInt(hex, 16);
710
- return num.toString(36).padStart(7, '0').substring(0, 7);
711
- }
712
-
713
860
  /**
714
861
  * Get CI/CD information if available
715
862
  */