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
@@ -5,16 +5,11 @@
5
5
  * processing, CI/CD integration, and data analysis.
6
6
  */
7
7
 
8
- import { mkdirSync, writeFileSync } from 'node:fs';
9
- import { dirname } from 'node:path';
8
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
9
+ import { dirname, join } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
10
11
 
11
- import type {
12
- BenchmarkRun,
13
- FileResult,
14
- ProgressState,
15
- SuiteResult,
16
- TaskResult,
17
- } from '../types/index.js';
12
+ import type { BenchmarkRun, TaskResult } from '../types/index.js';
18
13
 
19
14
  import { ReporterOutputError } from '../errors/index.js';
20
15
  import { BaseReporter } from '../services/reporter-registry.js';
@@ -40,12 +35,30 @@ interface JsonOutput {
40
35
  };
41
36
  }
42
37
 
38
+ /**
39
+ * Cache the package version at module load time
40
+ *
41
+ * NOTE: This relies on package.json being at the same relative path from both
42
+ * src/ and dist/ directories (../../package.json). If the build output
43
+ * structure changes, this will break.
44
+ */
45
+ const cachedPackageVersion = (() => {
46
+ try {
47
+ const __dirname = dirname(fileURLToPath(import.meta.url));
48
+ const pkgPath = join(__dirname, '..', '..', 'package.json');
49
+ const pkgContent = readFileSync(pkgPath, 'utf8');
50
+ const pkg = JSON.parse(pkgContent) as { version: string };
51
+ return pkg.version;
52
+ } catch {
53
+ // Fallback if package.json cannot be read (shouldn't happen in normal use)
54
+ return 'unknown';
55
+ }
56
+ })();
57
+
43
58
  /**
44
59
  * JSON reporter for structured output
45
60
  */
46
61
  export class JsonReporter extends BaseReporter {
47
- private currentRun?: BenchmarkRun;
48
-
49
62
  private readonly includeMetadata: boolean;
50
63
 
51
64
  private readonly includeStatistics: boolean;
@@ -54,8 +67,6 @@ export class JsonReporter extends BaseReporter {
54
67
 
55
68
  private readonly prettyPrint: boolean;
56
69
 
57
- private readonly quiet: boolean;
58
-
59
70
  private statistics: {
60
71
  fastestTask?: TaskResult;
61
72
  slowestTask?: TaskResult;
@@ -74,8 +85,6 @@ export class JsonReporter extends BaseReporter {
74
85
  includeStatistics?: boolean;
75
86
  outputPath?: string;
76
87
  prettyPrint?: boolean;
77
- quiet?: boolean;
78
- verbose?: boolean;
79
88
  } = {},
80
89
  ) {
81
90
  super('json', options);
@@ -84,35 +93,6 @@ export class JsonReporter extends BaseReporter {
84
93
  this.prettyPrint = options.prettyPrint ?? true;
85
94
  this.includeStatistics = options.includeStatistics ?? true;
86
95
  this.includeMetadata = options.includeMetadata ?? true;
87
- this.quiet = options.quiet ?? false;
88
- }
89
-
90
- /**
91
- * Check if statistics are included
92
- */
93
- areStatisticsIncluded(): boolean {
94
- return this.includeStatistics;
95
- }
96
-
97
- /**
98
- * Get the output path (if configured)
99
- */
100
- getOutputPath(): string | undefined {
101
- return this.outputPath;
102
- }
103
-
104
- /**
105
- * Check if metadata is included
106
- */
107
- isMetadataIncluded(): boolean {
108
- return this.includeMetadata;
109
- }
110
-
111
- /**
112
- * Check if pretty printing is enabled
113
- */
114
- isPrettyPrintEnabled(): boolean {
115
- return this.prettyPrint;
116
96
  }
117
97
 
118
98
  async onEnd(run: BenchmarkRun): Promise<void> {
@@ -131,41 +111,16 @@ export class JsonReporter extends BaseReporter {
131
111
  console.error('JSON Reporter Error:', error.message);
132
112
  }
133
113
 
134
- onFileEnd(_result: FileResult): void {
135
- // No-op for JSON reporter
136
- }
137
-
138
- onFileStart(_file: string): void {
139
- // No-op for JSON reporter
140
- }
141
-
142
- onProgress(_state: ProgressState): void {
143
- // No-op for JSON reporter - we don't output progress in JSON format
144
- }
145
-
146
- onStart(run: BenchmarkRun): void {
147
- this.currentRun = run;
114
+ onStart(_run: BenchmarkRun): void {
148
115
  this.resetStatistics();
149
116
  }
150
117
 
151
- onSuiteEnd(_result: SuiteResult): void {
152
- // No-op for JSON reporter
153
- }
154
-
155
- onSuiteStart(_suite: string): void {
156
- // No-op for JSON reporter
157
- }
158
-
159
118
  onTaskResult(result: TaskResult): void {
160
119
  if (!result.error) {
161
120
  this.updateStatistics(result);
162
121
  }
163
122
  }
164
123
 
165
- onTaskStart(_task: string): void {
166
- // No-op for JSON reporter
167
- }
168
-
169
124
  /**
170
125
  * Build the complete JSON output structure
171
126
  */
@@ -174,7 +129,7 @@ export class JsonReporter extends BaseReporter {
174
129
  meta: {
175
130
  format: 'modestbench-json',
176
131
  timestamp: new Date().toISOString(),
177
- version: '0.1.0', // TODO: Get from package.json
132
+ version: cachedPackageVersion,
178
133
  },
179
134
  run: this.includeMetadata ? run : this.sanitizeRun(run),
180
135
  };
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Profile Human Reporter
3
+ *
4
+ * Human-readable reporter for profile command. Uses modestbench's synthwave
5
+ * ANSI theme to display profiled functions in an attractive, color-coded
6
+ * format.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+
11
+ import path from 'node:path';
12
+
13
+ import type { FilteredProfileData } from '../types/profiler.js';
14
+
15
+ import { ansiChars, colors } from '../utils/ansi.js';
16
+
17
+ /**
18
+ * Reporter options
19
+ */
20
+ interface ProfileReporterOptions {
21
+ /** Enable color output */
22
+ color?: boolean;
23
+
24
+ /** Group by file */
25
+ groupByFile?: boolean;
26
+ }
27
+
28
+ /**
29
+ * Human-readable profile reporter
30
+ */
31
+ export class ProfileHumanReporter {
32
+ private readonly groupByFile: boolean;
33
+
34
+ private readonly useColor: boolean;
35
+
36
+ constructor(options: ProfileReporterOptions = {}) {
37
+ this.useColor =
38
+ options.color ??
39
+ (process.stdout.isTTY &&
40
+ process.env.FORCE_COLOR !== '0' &&
41
+ process.env.NO_COLOR == null);
42
+
43
+ this.groupByFile = options.groupByFile ?? false;
44
+ }
45
+
46
+ /**
47
+ * Generate and print profile report
48
+ */
49
+ report(data: FilteredProfileData): void {
50
+ this.printHeader(data);
51
+ this.printLine();
52
+
53
+ if (this.groupByFile && data.groupedByFile) {
54
+ this.printGroupedResults(data);
55
+ } else {
56
+ this.printFlatResults(data);
57
+ }
58
+ }
59
+
60
+ private colorize(color: keyof typeof colors, text: string): string {
61
+ if (!this.useColor) {
62
+ return text;
63
+ }
64
+ return `${colors[color]}${text}${colors.reset}`;
65
+ }
66
+
67
+ /**
68
+ * Format file path - show relative path if within CWD, otherwise absolute
69
+ */
70
+ private formatPath(filePath: string): string {
71
+ const cwd = process.cwd();
72
+ const absolutePath = path.resolve(filePath);
73
+
74
+ // Check if the file is within the current working directory
75
+ if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
76
+ return path.relative(cwd, absolutePath);
77
+ }
78
+
79
+ return absolutePath;
80
+ }
81
+
82
+ private getPercentColor(percent: number): keyof typeof colors {
83
+ if (percent >= 10) {
84
+ return 'brightRed';
85
+ }
86
+ if (percent >= 5) {
87
+ return 'brightYellow';
88
+ }
89
+ if (percent >= 2) {
90
+ return 'brightCyan';
91
+ }
92
+ return 'white';
93
+ }
94
+
95
+ private printFlatResults(data: FilteredProfileData): void {
96
+ const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Benchmark Candidates'))}`;
97
+ this.printLine(header);
98
+ this.printLine();
99
+ this.printLine('Top functions by execution time:');
100
+ this.printLine();
101
+
102
+ for (const fn of data.functions) {
103
+ // Function name and percentage
104
+ const percentColor = this.getPercentColor(fn.percentage);
105
+ const percent = `${fn.percentage.toFixed(1)}%`;
106
+ const ticks = `(${fn.ticks.toLocaleString()} ticks)`;
107
+
108
+ this.printLine(
109
+ ` ${this.colorize('brightWhite', fn.name).padEnd(60)} ${this.colorize(percentColor, percent.padStart(6))} ${this.colorize('dim', ticks)}`,
110
+ );
111
+
112
+ // File and line
113
+ const displayPath = this.formatPath(fn.file);
114
+ const lineInfo = fn.line ? `:${fn.line}` : '';
115
+ this.printLine(
116
+ ` ${this.colorize('brightMagenta', this.colorize('bold', displayPath + lineInfo))}`,
117
+ );
118
+ this.printLine();
119
+ }
120
+
121
+ this.printSummary(data);
122
+ }
123
+
124
+ private printGroupedResults(data: FilteredProfileData): void {
125
+ if (!data.groupedByFile) {
126
+ return;
127
+ }
128
+
129
+ const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Grouped by File'))}`;
130
+ this.printLine(header);
131
+ this.printLine();
132
+
133
+ // Sort files by total percentage
134
+ const sortedFiles = Array.from(data.groupedByFile.entries()).sort(
135
+ (a, b) => {
136
+ const aTotal = a[1].reduce((sum, fn) => sum + fn.percentage, 0);
137
+ const bTotal = b[1].reduce((sum, fn) => sum + fn.percentage, 0);
138
+ return bTotal - aTotal;
139
+ },
140
+ );
141
+
142
+ for (const [file, functions] of sortedFiles) {
143
+ const totalPercent = functions.reduce(
144
+ (sum, fn) => sum + fn.percentage,
145
+ 0,
146
+ );
147
+ const totalTicks = functions.reduce((sum, fn) => sum + fn.ticks, 0);
148
+
149
+ const percentColor = this.getPercentColor(totalPercent);
150
+ const percent = `${totalPercent.toFixed(1)}%`;
151
+ const ticks = `(${totalTicks.toLocaleString()} ticks)`;
152
+
153
+ // File header
154
+ const displayPath = this.formatPath(file);
155
+ this.printLine(
156
+ `${this.colorize('magenta', ansiChars.block.dark)} ${this.colorize('brightMagenta', this.colorize('bold', displayPath)).padEnd(60)} ${this.colorize(percentColor, percent.padStart(6))} ${this.colorize('dim', ticks)}`,
157
+ );
158
+
159
+ // Functions in this file
160
+ for (const fn of functions) {
161
+ const fnPercent = `${fn.percentage.toFixed(1)}%`;
162
+ const fnTicks = `(${fn.ticks.toLocaleString()} ticks)`;
163
+ const lineInfo = fn.line ? `:${fn.line}` : '';
164
+
165
+ this.printLine(
166
+ ` ${this.colorize('magenta', ansiChars.smallSquare)} ${this.colorize('brightWhite', fn.name).padEnd(58)} ${this.colorize(this.getPercentColor(fn.percentage), fnPercent.padStart(6))} ${this.colorize('dim', fnTicks.padEnd(15))} ${this.colorize('dim', lineInfo)}`,
167
+ );
168
+ }
169
+
170
+ this.printLine();
171
+ }
172
+
173
+ this.printSummary(data);
174
+ }
175
+
176
+ private printHeader(data: FilteredProfileData): void {
177
+ const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Profile Analysis'))}`;
178
+ this.printLine(header);
179
+ this.printLine();
180
+
181
+ if (data.command) {
182
+ this.printLine(`Command: ${this.colorize('cyan', data.command)}`);
183
+ }
184
+
185
+ if (data.duration) {
186
+ const durationSec = (data.duration / 1000).toFixed(1);
187
+ this.printLine(`Duration: ${this.colorize('cyan', `${durationSec}s`)}`);
188
+ }
189
+
190
+ this.printLine(
191
+ `Total Ticks: ${this.colorize('cyan', data.totalTicks.toLocaleString())}`,
192
+ );
193
+ }
194
+
195
+ private printLine(text = ''): void {
196
+ console.log(text);
197
+ }
198
+
199
+ private printSummary(data: FilteredProfileData): void {
200
+ if (data.totalShown === 0) {
201
+ this.printLine(
202
+ `${this.colorize('dim', `No functions used at least ${data.minExecutionPercent}% of the ticks`)}`,
203
+ );
204
+ } else {
205
+ this.printLine(
206
+ `${this.colorize('dim', `... (showing top ${data.totalShown} of ${data.totalFiltered} user functions)`)}`,
207
+ );
208
+ }
209
+ }
210
+ }
@@ -9,8 +9,8 @@ import path from 'node:path';
9
9
 
10
10
  import type {
11
11
  BenchmarkRun,
12
+ BudgetSummary,
12
13
  FileResult,
13
- ProgressState,
14
14
  SuiteResult,
15
15
  TaskResult,
16
16
  } from '../types/index.js';
@@ -62,6 +62,78 @@ export class SimpleReporter extends BaseReporter {
62
62
  this.quiet = options.quiet ?? false;
63
63
  }
64
64
 
65
+ /**
66
+ * Format bytes in human-readable format
67
+ */
68
+ private static formatBytes(this: void, bytes: number): string {
69
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
70
+ let size = bytes;
71
+ let unitIndex = 0;
72
+
73
+ while (size >= 1024 && unitIndex < units.length - 1) {
74
+ size /= 1024;
75
+ unitIndex++;
76
+ }
77
+
78
+ return `${size.toFixed(1)} ${units[unitIndex]}`;
79
+ }
80
+
81
+ /**
82
+ * Format file path - show relative path if within CWD, otherwise absolute
83
+ */
84
+ private static formatPath(this: void, filePath: string): string {
85
+ const cwd = process.cwd();
86
+ const absolutePath = path.resolve(filePath);
87
+
88
+ // Check if the file is within the current working directory
89
+ if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
90
+ return path.relative(cwd, absolutePath);
91
+ }
92
+
93
+ return absolutePath;
94
+ }
95
+
96
+ /**
97
+ * Simple pluralization helper
98
+ */
99
+ private static pluralize(this: void, str: string, count: number): string {
100
+ return count === 1 ? str : `${str}s`;
101
+ }
102
+
103
+ onBudgetResult(summary: BudgetSummary): void {
104
+ if (summary.total === 0 || this.quiet) {
105
+ return;
106
+ }
107
+
108
+ console.log('== Performance Budgets');
109
+ console.log();
110
+
111
+ for (const result of summary.results) {
112
+ const icon = result.passed ? symbols.checkmark : symbols.cross;
113
+ console.log(` ${icon} ${result.taskId}`);
114
+
115
+ if (!result.passed && result.violations.length > 0) {
116
+ for (const violation of result.violations) {
117
+ console.log(` ${violation.message}`);
118
+ }
119
+ }
120
+ }
121
+
122
+ console.log();
123
+
124
+ if (summary.failed === 0) {
125
+ console.log(
126
+ ` ${symbols.checkmark} All ${summary.total} budget(s) passed`,
127
+ );
128
+ } else {
129
+ console.log(
130
+ ` ${symbols.cross} ${summary.failed} of ${summary.total} budget(s) failed`,
131
+ );
132
+ }
133
+
134
+ console.log();
135
+ }
136
+
65
137
  onEnd(run: BenchmarkRun): void {
66
138
  if (this.quiet) {
67
139
  return;
@@ -97,7 +169,7 @@ export class SimpleReporter extends BaseReporter {
97
169
  console.log(`- Files: ${totalFiles}`);
98
170
  console.log(`- Suites: ${totalSuites}`);
99
171
  console.log(
100
- `${symbols.approx} Duration: ${this.formatDuration(duration * 1e6)}`,
172
+ `${symbols.approx} Duration: ${BaseReporter.formatDuration(duration * 1e6)}`,
101
173
  );
102
174
  console.log();
103
175
 
@@ -111,7 +183,7 @@ export class SimpleReporter extends BaseReporter {
111
183
  console.log();
112
184
 
113
185
  for (const failure of this.failures) {
114
- const displayPath = this.formatPath(failure.file);
186
+ const displayPath = SimpleReporter.formatPath(failure.file);
115
187
  console.log(` ${displayPath} > ${failure.suite} > ${failure.task}`);
116
188
  console.log(` ${failure.error}`);
117
189
  console.log();
@@ -155,7 +227,7 @@ export class SimpleReporter extends BaseReporter {
155
227
  );
156
228
  } else {
157
229
  console.log(
158
- ` ${symbols.checkmark} ${totalPassed > 1 ? 'All ' : ''}${totalPassed} ${this.pluralize('task', totalPassed)} passed`,
230
+ ` ${symbols.checkmark} ${totalPassed > 1 ? 'All ' : ''}${totalPassed} ${SimpleReporter.pluralize('task', totalPassed)} passed`,
159
231
  );
160
232
  }
161
233
 
@@ -169,15 +241,10 @@ export class SimpleReporter extends BaseReporter {
169
241
  return;
170
242
  }
171
243
 
172
- const displayPath = this.formatPath(file);
244
+ const displayPath = SimpleReporter.formatPath(file);
173
245
  console.log(`-- ${displayPath}`);
174
246
  }
175
247
 
176
- onProgress(_state: ProgressState): void {
177
- // Simple reporter does not display progress bars
178
- return;
179
- }
180
-
181
248
  onStart(run: BenchmarkRun): void {
182
249
  this.startTime = Date.now();
183
250
  this.failures = []; // Reset failures for new run
@@ -197,7 +264,9 @@ export class SimpleReporter extends BaseReporter {
197
264
  console.log(
198
265
  ` cpu: ${run.environment.cpu.model} (${run.environment.cpu.cores} cores)`,
199
266
  );
200
- console.log(` mem: ${this.formatBytes(run.environment.memory.total)}`);
267
+ console.log(
268
+ ` mem: ${SimpleReporter.formatBytes(run.environment.memory.total)}`,
269
+ );
201
270
  console.log();
202
271
  }
203
272
 
@@ -231,7 +300,7 @@ export class SimpleReporter extends BaseReporter {
231
300
  console.log(` ${symbols.cross} ${failed} failed, ${passed} passed`);
232
301
  } else {
233
302
  console.log(
234
- ` ${symbols.checkmark} ${passed} ${this.pluralize('task', passed)} passed`,
303
+ ` ${symbols.checkmark} ${passed} ${SimpleReporter.pluralize('task', passed)} passed`,
235
304
  );
236
305
  }
237
306
  console.log();
@@ -260,8 +329,11 @@ export class SimpleReporter extends BaseReporter {
260
329
  return;
261
330
  }
262
331
 
263
- // Buffer the result for later printing with proper alignment
332
+ // Always buffer the result for suite summary (including aborted tasks)
264
333
  this.suiteResults.push(result);
334
+
335
+ // Note: Aborted tasks are still printed in simple reporter for completeness
336
+ // but they'll have zero stats
265
337
  }
266
338
 
267
339
  onTaskStart(task: string): void {
@@ -275,44 +347,6 @@ export class SimpleReporter extends BaseReporter {
275
347
  }
276
348
  }
277
349
 
278
- /**
279
- * Format bytes in human-readable format
280
- */
281
- private formatBytes(bytes: number): string {
282
- const units = ['B', 'KB', 'MB', 'GB', 'TB'];
283
- let size = bytes;
284
- let unitIndex = 0;
285
-
286
- while (size >= 1024 && unitIndex < units.length - 1) {
287
- size /= 1024;
288
- unitIndex++;
289
- }
290
-
291
- return `${size.toFixed(1)} ${units[unitIndex]}`;
292
- }
293
-
294
- /**
295
- * Format file path - show relative path if within CWD, otherwise absolute
296
- */
297
- private formatPath(filePath: string): string {
298
- const cwd = process.cwd();
299
- const absolutePath = path.resolve(filePath);
300
-
301
- // Check if the file is within the current working directory
302
- if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
303
- return path.relative(cwd, absolutePath);
304
- }
305
-
306
- return absolutePath;
307
- }
308
-
309
- /**
310
- * Simple pluralization helper
311
- */
312
- private pluralize(str: string, count: number): string {
313
- return count === 1 ? str : `${str}s`;
314
- }
315
-
316
350
  /**
317
351
  * Print all task results in a suite with aligned columns
318
352
  */
@@ -364,9 +398,9 @@ export class SimpleReporter extends BaseReporter {
364
398
  };
365
399
  }
366
400
 
367
- const duration = this.formatDuration(result.mean * 1e9);
368
- const opsPerSec = this.formatOpsPerSecond(result.opsPerSecond);
369
- const rme = this.formatPercentage(result.marginOfError * 100);
401
+ const duration = BaseReporter.formatDuration(result.mean * 1e9);
402
+ const opsPerSec = BaseReporter.formatOpsPerSecond(result.opsPerSecond);
403
+ const rme = BaseReporter.formatPercentage(result.marginOfError); // already a percentage
370
404
 
371
405
  return {
372
406
  durationLen: duration.length,