modestbench 0.0.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 (275) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE.md +55 -0
  3. package/README.md +699 -0
  4. package/dist/bootstrap.cjs +37 -0
  5. package/dist/bootstrap.cjs.map +1 -0
  6. package/dist/bootstrap.d.cts +17 -0
  7. package/dist/bootstrap.d.cts.map +1 -0
  8. package/dist/bootstrap.d.ts +17 -0
  9. package/dist/bootstrap.d.ts.map +1 -0
  10. package/dist/bootstrap.js +33 -0
  11. package/dist/bootstrap.js.map +1 -0
  12. package/dist/cli/commands/history.cjs +459 -0
  13. package/dist/cli/commands/history.cjs.map +1 -0
  14. package/dist/cli/commands/history.d.cts +34 -0
  15. package/dist/cli/commands/history.d.cts.map +1 -0
  16. package/dist/cli/commands/history.d.ts +34 -0
  17. package/dist/cli/commands/history.d.ts.map +1 -0
  18. package/dist/cli/commands/history.js +422 -0
  19. package/dist/cli/commands/history.js.map +1 -0
  20. package/dist/cli/commands/init.cjs +566 -0
  21. package/dist/cli/commands/init.cjs.map +1 -0
  22. package/dist/cli/commands/init.d.cts +26 -0
  23. package/dist/cli/commands/init.d.cts.map +1 -0
  24. package/dist/cli/commands/init.d.ts +26 -0
  25. package/dist/cli/commands/init.d.ts.map +1 -0
  26. package/dist/cli/commands/init.js +562 -0
  27. package/dist/cli/commands/init.js.map +1 -0
  28. package/dist/cli/commands/run.cjs +285 -0
  29. package/dist/cli/commands/run.cjs.map +1 -0
  30. package/dist/cli/commands/run.d.cts +37 -0
  31. package/dist/cli/commands/run.d.cts.map +1 -0
  32. package/dist/cli/commands/run.d.ts +37 -0
  33. package/dist/cli/commands/run.d.ts.map +1 -0
  34. package/dist/cli/commands/run.js +248 -0
  35. package/dist/cli/commands/run.js.map +1 -0
  36. package/dist/cli/index.cjs +523 -0
  37. package/dist/cli/index.cjs.map +1 -0
  38. package/dist/cli/index.d.cts +58 -0
  39. package/dist/cli/index.d.cts.map +1 -0
  40. package/dist/cli/index.d.ts +58 -0
  41. package/dist/cli/index.d.ts.map +1 -0
  42. package/dist/cli/index.js +515 -0
  43. package/dist/cli/index.js.map +1 -0
  44. package/dist/config/manager.cjs +370 -0
  45. package/dist/config/manager.cjs.map +1 -0
  46. package/dist/config/manager.d.cts +46 -0
  47. package/dist/config/manager.d.cts.map +1 -0
  48. package/dist/config/manager.d.ts +46 -0
  49. package/dist/config/manager.d.ts.map +1 -0
  50. package/dist/config/manager.js +333 -0
  51. package/dist/config/manager.js.map +1 -0
  52. package/dist/config/schema.cjs +182 -0
  53. package/dist/config/schema.cjs.map +1 -0
  54. package/dist/config/schema.d.cts +51 -0
  55. package/dist/config/schema.d.cts.map +1 -0
  56. package/dist/config/schema.d.ts +51 -0
  57. package/dist/config/schema.d.ts.map +1 -0
  58. package/dist/config/schema.js +145 -0
  59. package/dist/config/schema.js.map +1 -0
  60. package/dist/constants.cjs +22 -0
  61. package/dist/constants.cjs.map +1 -0
  62. package/dist/constants.d.cts +10 -0
  63. package/dist/constants.d.cts.map +1 -0
  64. package/dist/constants.d.ts +10 -0
  65. package/dist/constants.d.ts.map +1 -0
  66. package/dist/constants.js +19 -0
  67. package/dist/constants.js.map +1 -0
  68. package/dist/core/benchmark-schema.cjs +135 -0
  69. package/dist/core/benchmark-schema.cjs.map +1 -0
  70. package/dist/core/benchmark-schema.d.cts +139 -0
  71. package/dist/core/benchmark-schema.d.cts.map +1 -0
  72. package/dist/core/benchmark-schema.d.ts +139 -0
  73. package/dist/core/benchmark-schema.d.ts.map +1 -0
  74. package/dist/core/benchmark-schema.js +132 -0
  75. package/dist/core/benchmark-schema.js.map +1 -0
  76. package/dist/core/engine.cjs +669 -0
  77. package/dist/core/engine.cjs.map +1 -0
  78. package/dist/core/engine.d.cts +128 -0
  79. package/dist/core/engine.d.cts.map +1 -0
  80. package/dist/core/engine.d.ts +128 -0
  81. package/dist/core/engine.d.ts.map +1 -0
  82. package/dist/core/engine.js +632 -0
  83. package/dist/core/engine.js.map +1 -0
  84. package/dist/core/engines/accurate-engine.cjs +292 -0
  85. package/dist/core/engines/accurate-engine.cjs.map +1 -0
  86. package/dist/core/engines/accurate-engine.d.cts +63 -0
  87. package/dist/core/engines/accurate-engine.d.cts.map +1 -0
  88. package/dist/core/engines/accurate-engine.d.ts +63 -0
  89. package/dist/core/engines/accurate-engine.d.ts.map +1 -0
  90. package/dist/core/engines/accurate-engine.js +288 -0
  91. package/dist/core/engines/accurate-engine.js.map +1 -0
  92. package/dist/core/engines/index.cjs +21 -0
  93. package/dist/core/engines/index.cjs.map +1 -0
  94. package/dist/core/engines/index.d.cts +16 -0
  95. package/dist/core/engines/index.d.cts.map +1 -0
  96. package/dist/core/engines/index.d.ts +16 -0
  97. package/dist/core/engines/index.d.ts.map +1 -0
  98. package/dist/core/engines/index.js +16 -0
  99. package/dist/core/engines/index.js.map +1 -0
  100. package/dist/core/engines/tinybench-engine.cjs +286 -0
  101. package/dist/core/engines/tinybench-engine.cjs.map +1 -0
  102. package/dist/core/engines/tinybench-engine.d.cts +18 -0
  103. package/dist/core/engines/tinybench-engine.d.cts.map +1 -0
  104. package/dist/core/engines/tinybench-engine.d.ts +18 -0
  105. package/dist/core/engines/tinybench-engine.d.ts.map +1 -0
  106. package/dist/core/engines/tinybench-engine.js +282 -0
  107. package/dist/core/engines/tinybench-engine.js.map +1 -0
  108. package/dist/core/error-manager.cjs +303 -0
  109. package/dist/core/error-manager.cjs.map +1 -0
  110. package/dist/core/error-manager.d.cts +77 -0
  111. package/dist/core/error-manager.d.cts.map +1 -0
  112. package/dist/core/error-manager.d.ts +77 -0
  113. package/dist/core/error-manager.d.ts.map +1 -0
  114. package/dist/core/error-manager.js +299 -0
  115. package/dist/core/error-manager.js.map +1 -0
  116. package/dist/core/loader.cjs +287 -0
  117. package/dist/core/loader.cjs.map +1 -0
  118. package/dist/core/loader.d.cts +55 -0
  119. package/dist/core/loader.d.cts.map +1 -0
  120. package/dist/core/loader.d.ts +55 -0
  121. package/dist/core/loader.d.ts.map +1 -0
  122. package/dist/core/loader.js +250 -0
  123. package/dist/core/loader.js.map +1 -0
  124. package/dist/core/stats-utils.cjs +99 -0
  125. package/dist/core/stats-utils.cjs.map +1 -0
  126. package/dist/core/stats-utils.d.cts +50 -0
  127. package/dist/core/stats-utils.d.cts.map +1 -0
  128. package/dist/core/stats-utils.d.ts +50 -0
  129. package/dist/core/stats-utils.d.ts.map +1 -0
  130. package/dist/core/stats-utils.js +94 -0
  131. package/dist/core/stats-utils.js.map +1 -0
  132. package/dist/index.cjs +64 -0
  133. package/dist/index.cjs.map +1 -0
  134. package/dist/index.d.cts +22 -0
  135. package/dist/index.d.cts.map +1 -0
  136. package/dist/index.d.ts +22 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +30 -0
  139. package/dist/index.js.map +1 -0
  140. package/dist/progress/manager.cjs +325 -0
  141. package/dist/progress/manager.cjs.map +1 -0
  142. package/dist/progress/manager.d.cts +125 -0
  143. package/dist/progress/manager.d.cts.map +1 -0
  144. package/dist/progress/manager.d.ts +125 -0
  145. package/dist/progress/manager.d.ts.map +1 -0
  146. package/dist/progress/manager.js +321 -0
  147. package/dist/progress/manager.js.map +1 -0
  148. package/dist/reporters/csv.cjs +250 -0
  149. package/dist/reporters/csv.cjs.map +1 -0
  150. package/dist/reporters/csv.d.cts +92 -0
  151. package/dist/reporters/csv.d.cts.map +1 -0
  152. package/dist/reporters/csv.d.ts +92 -0
  153. package/dist/reporters/csv.d.ts.map +1 -0
  154. package/dist/reporters/csv.js +246 -0
  155. package/dist/reporters/csv.js.map +1 -0
  156. package/dist/reporters/human.cjs +516 -0
  157. package/dist/reporters/human.cjs.map +1 -0
  158. package/dist/reporters/human.d.cts +86 -0
  159. package/dist/reporters/human.d.cts.map +1 -0
  160. package/dist/reporters/human.d.ts +86 -0
  161. package/dist/reporters/human.d.ts.map +1 -0
  162. package/dist/reporters/human.js +509 -0
  163. package/dist/reporters/human.js.map +1 -0
  164. package/dist/reporters/index.cjs +17 -0
  165. package/dist/reporters/index.cjs.map +1 -0
  166. package/dist/reporters/index.d.cts +10 -0
  167. package/dist/reporters/index.d.cts.map +1 -0
  168. package/dist/reporters/index.d.ts +10 -0
  169. package/dist/reporters/index.d.ts.map +1 -0
  170. package/dist/reporters/index.js +10 -0
  171. package/dist/reporters/index.js.map +1 -0
  172. package/dist/reporters/json.cjs +215 -0
  173. package/dist/reporters/json.cjs.map +1 -0
  174. package/dist/reporters/json.d.cts +79 -0
  175. package/dist/reporters/json.d.cts.map +1 -0
  176. package/dist/reporters/json.d.ts +79 -0
  177. package/dist/reporters/json.d.ts.map +1 -0
  178. package/dist/reporters/json.js +211 -0
  179. package/dist/reporters/json.js.map +1 -0
  180. package/dist/reporters/registry.cjs +255 -0
  181. package/dist/reporters/registry.cjs.map +1 -0
  182. package/dist/reporters/registry.d.cts +155 -0
  183. package/dist/reporters/registry.d.cts.map +1 -0
  184. package/dist/reporters/registry.d.ts +155 -0
  185. package/dist/reporters/registry.d.ts.map +1 -0
  186. package/dist/reporters/registry.js +249 -0
  187. package/dist/reporters/registry.js.map +1 -0
  188. package/dist/reporters/simple.cjs +328 -0
  189. package/dist/reporters/simple.cjs.map +1 -0
  190. package/dist/reporters/simple.d.cts +51 -0
  191. package/dist/reporters/simple.d.cts.map +1 -0
  192. package/dist/reporters/simple.d.ts +51 -0
  193. package/dist/reporters/simple.d.ts.map +1 -0
  194. package/dist/reporters/simple.js +321 -0
  195. package/dist/reporters/simple.js.map +1 -0
  196. package/dist/schema/modestbench-config.schema.json +162 -0
  197. package/dist/storage/history.cjs +456 -0
  198. package/dist/storage/history.cjs.map +1 -0
  199. package/dist/storage/history.d.cts +99 -0
  200. package/dist/storage/history.d.cts.map +1 -0
  201. package/dist/storage/history.d.ts +99 -0
  202. package/dist/storage/history.d.ts.map +1 -0
  203. package/dist/storage/history.js +452 -0
  204. package/dist/storage/history.js.map +1 -0
  205. package/dist/types/cli.cjs +21 -0
  206. package/dist/types/cli.cjs.map +1 -0
  207. package/dist/types/cli.d.cts +296 -0
  208. package/dist/types/cli.d.cts.map +1 -0
  209. package/dist/types/cli.d.ts +296 -0
  210. package/dist/types/cli.d.ts.map +1 -0
  211. package/dist/types/cli.js +18 -0
  212. package/dist/types/cli.js.map +1 -0
  213. package/dist/types/core.cjs +14 -0
  214. package/dist/types/core.cjs.map +1 -0
  215. package/dist/types/core.d.cts +380 -0
  216. package/dist/types/core.d.cts.map +1 -0
  217. package/dist/types/core.d.ts +380 -0
  218. package/dist/types/core.d.ts.map +1 -0
  219. package/dist/types/core.js +13 -0
  220. package/dist/types/core.js.map +1 -0
  221. package/dist/types/index.cjs +27 -0
  222. package/dist/types/index.cjs.map +1 -0
  223. package/dist/types/index.d.cts +11 -0
  224. package/dist/types/index.d.cts.map +1 -0
  225. package/dist/types/index.d.ts +11 -0
  226. package/dist/types/index.d.ts.map +1 -0
  227. package/dist/types/index.js +11 -0
  228. package/dist/types/index.js.map +1 -0
  229. package/dist/types/interfaces.cjs +10 -0
  230. package/dist/types/interfaces.cjs.map +1 -0
  231. package/dist/types/interfaces.d.cts +381 -0
  232. package/dist/types/interfaces.d.cts.map +1 -0
  233. package/dist/types/interfaces.d.ts +381 -0
  234. package/dist/types/interfaces.d.ts.map +1 -0
  235. package/dist/types/interfaces.js +9 -0
  236. package/dist/types/interfaces.js.map +1 -0
  237. package/dist/types/utility.cjs +92 -0
  238. package/dist/types/utility.cjs.map +1 -0
  239. package/dist/types/utility.d.cts +330 -0
  240. package/dist/types/utility.d.cts.map +1 -0
  241. package/dist/types/utility.d.ts +330 -0
  242. package/dist/types/utility.d.ts.map +1 -0
  243. package/dist/types/utility.js +78 -0
  244. package/dist/types/utility.js.map +1 -0
  245. package/package.json +211 -0
  246. package/src/bootstrap.ts +35 -0
  247. package/src/cli/commands/history.ts +569 -0
  248. package/src/cli/commands/init.ts +658 -0
  249. package/src/cli/commands/run.ts +346 -0
  250. package/src/cli/index.ts +642 -0
  251. package/src/config/manager.ts +387 -0
  252. package/src/config/schema.ts +188 -0
  253. package/src/constants.ts +21 -0
  254. package/src/core/benchmark-schema.ts +185 -0
  255. package/src/core/engine.ts +888 -0
  256. package/src/core/engines/accurate-engine.ts +408 -0
  257. package/src/core/engines/index.ts +16 -0
  258. package/src/core/engines/tinybench-engine.ts +335 -0
  259. package/src/core/error-manager.ts +372 -0
  260. package/src/core/loader.ts +324 -0
  261. package/src/core/stats-utils.ts +135 -0
  262. package/src/index.ts +46 -0
  263. package/src/progress/manager.ts +415 -0
  264. package/src/reporters/csv.ts +368 -0
  265. package/src/reporters/human.ts +707 -0
  266. package/src/reporters/index.ts +10 -0
  267. package/src/reporters/json.ts +302 -0
  268. package/src/reporters/registry.ts +349 -0
  269. package/src/reporters/simple.ts +459 -0
  270. package/src/storage/history.ts +600 -0
  271. package/src/types/cli.ts +312 -0
  272. package/src/types/core.ts +414 -0
  273. package/src/types/index.ts +18 -0
  274. package/src/types/interfaces.ts +451 -0
  275. package/src/types/utility.ts +446 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * ModestBench Simple Console Reporter
3
+ *
4
+ * Provides plain text output without colors, ANSI codes, or decorative
5
+ * elements. Ideal for CI/CD environments, piping, or non-TTY outputs.
6
+ */
7
+
8
+ import path from 'node:path';
9
+
10
+ import type {
11
+ BenchmarkRun,
12
+ FileResult,
13
+ ProgressState,
14
+ SuiteResult,
15
+ TaskResult,
16
+ } from '../types/index.js';
17
+
18
+ import { BaseReporter } from './registry.js';
19
+
20
+ /**
21
+ * Basic symbols for plain text output
22
+ */
23
+ const symbols = {
24
+ approx: '≈',
25
+ checkmark: '√',
26
+ cross: '×',
27
+ plusMinus: '±',
28
+ } as const;
29
+
30
+ /**
31
+ * Simple console reporter with plain text output (no colors or progress bars)
32
+ */
33
+ export class SimpleReporter extends BaseReporter {
34
+ private currentFile = '';
35
+
36
+ private currentSuite = '';
37
+
38
+ private failures: Array<{
39
+ error: string;
40
+ file: string;
41
+ suite: string;
42
+ task: string;
43
+ }> = [];
44
+
45
+ private readonly quiet: boolean;
46
+
47
+ private startTime = 0;
48
+
49
+ private suiteResults: TaskResult[] = [];
50
+
51
+ private readonly verbose: boolean;
52
+
53
+ constructor(
54
+ options: {
55
+ quiet?: boolean;
56
+ verbose?: boolean;
57
+ } = {},
58
+ ) {
59
+ super('simple', options);
60
+
61
+ this.verbose = options.verbose ?? false;
62
+ this.quiet = options.quiet ?? false;
63
+ }
64
+
65
+ onEnd(run: BenchmarkRun): void {
66
+ if (this.quiet) {
67
+ return;
68
+ }
69
+
70
+ const duration = Date.now() - this.startTime;
71
+ const totalFiles = run.files.length;
72
+
73
+ // Calculate totals across all files
74
+ let totalSuites = 0;
75
+ let totalPassed = 0;
76
+ let totalFailed = 0;
77
+
78
+ for (const file of run.files) {
79
+ totalSuites += file.suites.length;
80
+ for (const suite of file.suites) {
81
+ totalPassed += suite.tasks.filter((t: TaskResult) => !t.error).length;
82
+ totalFailed += suite.tasks.filter((t: TaskResult) => t.error).length;
83
+ }
84
+ }
85
+
86
+ // Results header
87
+ console.log('== Results');
88
+ console.log();
89
+
90
+ if (totalFailed > 0) {
91
+ console.log(`${symbols.cross} Failed: ${totalFailed}`);
92
+ console.log(`${symbols.checkmark} Passed: ${totalPassed}`);
93
+ } else {
94
+ console.log(`${symbols.checkmark} All tests passed: ${totalPassed}`);
95
+ }
96
+
97
+ console.log(`- Files: ${totalFiles}`);
98
+ console.log(`- Suites: ${totalSuites}`);
99
+ console.log(
100
+ `${symbols.approx} Duration: ${this.formatDuration(duration * 1e6)}`,
101
+ );
102
+ console.log();
103
+
104
+ if (totalFailed > 0) {
105
+ console.log(`${symbols.cross.repeat(3)} Some benchmarks failed`);
106
+
107
+ // Display failed tasks with details
108
+ if (this.failures.length > 0) {
109
+ console.log();
110
+ console.log('Failed Tasks:');
111
+ console.log();
112
+
113
+ for (const failure of this.failures) {
114
+ const displayPath = this.formatPath(failure.file);
115
+ console.log(` ${displayPath} > ${failure.suite} > ${failure.task}`);
116
+ console.log(` ${failure.error}`);
117
+ console.log();
118
+ }
119
+ }
120
+ } else {
121
+ console.log('All benchmarks completed successfully!');
122
+ }
123
+ }
124
+
125
+ onError(error: Error): void {
126
+ if (this.quiet) {
127
+ return;
128
+ }
129
+
130
+ console.error(`${symbols.cross.repeat(3)} Error:`, error.message);
131
+
132
+ if (this.verbose && error.stack) {
133
+ console.error(error.stack);
134
+ }
135
+ }
136
+
137
+ onFileEnd(result: FileResult): void {
138
+ if (this.quiet) {
139
+ return;
140
+ }
141
+
142
+ const totalTasks = result.suites.reduce(
143
+ (sum, suite) => sum + suite.tasks.length,
144
+ 0,
145
+ );
146
+ const totalPassed = result.suites.reduce(
147
+ (sum, suite) => sum + suite.tasks.filter((t) => !t.error).length,
148
+ 0,
149
+ );
150
+ const totalFailed = totalTasks - totalPassed;
151
+
152
+ if (totalFailed > 0) {
153
+ console.log(
154
+ ` ${symbols.cross} ${totalFailed} failed, ${totalPassed} passed`,
155
+ );
156
+ } else {
157
+ console.log(
158
+ ` ${symbols.checkmark} ${totalPassed > 1 ? 'All ' : ''}${totalPassed} ${this.pluralize('task', totalPassed)} passed`,
159
+ );
160
+ }
161
+
162
+ console.log();
163
+ }
164
+
165
+ onFileStart(file: string): void {
166
+ this.currentFile = file;
167
+
168
+ if (this.quiet) {
169
+ return;
170
+ }
171
+
172
+ const displayPath = this.formatPath(file);
173
+ console.log(`-- ${displayPath}`);
174
+ }
175
+
176
+ onProgress(_state: ProgressState): void {
177
+ // Simple reporter does not display progress bars
178
+ return;
179
+ }
180
+
181
+ onStart(run: BenchmarkRun): void {
182
+ this.startTime = Date.now();
183
+ this.failures = []; // Reset failures for new run
184
+
185
+ if (this.quiet) {
186
+ return;
187
+ }
188
+
189
+ console.log('modestbench');
190
+ console.log();
191
+
192
+ if (run.environment) {
193
+ console.log(` node.js: ${run.environment.nodeVersion}`);
194
+ console.log(
195
+ ` platform: ${run.environment.platform} ${run.environment.arch}`,
196
+ );
197
+ console.log(
198
+ ` cpu: ${run.environment.cpu.model} (${run.environment.cpu.cores} cores)`,
199
+ );
200
+ console.log(` mem: ${this.formatBytes(run.environment.memory.total)}`);
201
+ console.log();
202
+ }
203
+
204
+ if (run.git) {
205
+ console.log(` Git: ${run.git.commit}`);
206
+ }
207
+
208
+ if (run.ci) {
209
+ console.log(` CI: ${run.ci.provider}`);
210
+ console.log();
211
+ }
212
+ }
213
+
214
+ onSuiteEnd(result: SuiteResult): void {
215
+ if (this.quiet) {
216
+ return;
217
+ }
218
+
219
+ // Print all buffered task results with aligned columns
220
+ this.printAlignedSuiteResults();
221
+
222
+ // Skip displaying summary for the implicit "default" suite
223
+ if (result.name === 'default') {
224
+ return;
225
+ }
226
+
227
+ const passed = result.tasks.filter((t) => !t.error).length;
228
+ const failed = result.tasks.filter((t) => t.error).length;
229
+
230
+ if (failed > 0) {
231
+ console.log(` ${symbols.cross} ${failed} failed, ${passed} passed`);
232
+ } else {
233
+ console.log(
234
+ ` ${symbols.checkmark} ${passed} ${this.pluralize('task', passed)} passed`,
235
+ );
236
+ }
237
+ console.log();
238
+ }
239
+
240
+ onSuiteStart(suite: string): void {
241
+ this.currentSuite = suite;
242
+
243
+ if (this.quiet) {
244
+ return;
245
+ }
246
+
247
+ this.suiteResults = []; // Reset buffer for new suite
248
+
249
+ // Skip displaying the implicit "default" suite header
250
+ if (suite === 'default') {
251
+ return;
252
+ }
253
+
254
+ console.log();
255
+ console.log(` -- ${suite}`);
256
+ }
257
+
258
+ onTaskResult(result: TaskResult): void {
259
+ if (this.quiet) {
260
+ return;
261
+ }
262
+
263
+ // Buffer the result for later printing with proper alignment
264
+ this.suiteResults.push(result);
265
+ }
266
+
267
+ onTaskStart(task: string): void {
268
+ if (this.quiet) {
269
+ return;
270
+ }
271
+
272
+ // Only show static markers in verbose mode
273
+ if (this.verbose) {
274
+ console.log(` - ${task}`);
275
+ }
276
+ }
277
+
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
+ /**
317
+ * Print all task results in a suite with aligned columns
318
+ */
319
+ private printAlignedSuiteResults(): void {
320
+ if (this.suiteResults.length === 0) {
321
+ return;
322
+ }
323
+
324
+ const MAX_NAME_WIDTH = 60;
325
+ const BASE_INDENT = ' '; // 4 spaces
326
+ const separator = '-'; // Simple text separator instead of bullet
327
+
328
+ // Prepare formatted data for each task
329
+ interface FormattedTask {
330
+ durationLen: number;
331
+ durationStr: string;
332
+ error: boolean;
333
+ errorMessage?: string;
334
+ iterations: number;
335
+ name: string;
336
+ nameLength: number;
337
+ opsPerSecLen: number;
338
+ opsPerSecStr: string;
339
+ rmeLen: number;
340
+ rmeStr: string;
341
+ status: string;
342
+ }
343
+
344
+ const formatted: FormattedTask[] = this.suiteResults.map((result) => {
345
+ const status = result.error ? symbols.cross : symbols.checkmark;
346
+
347
+ const name = result.name.trim();
348
+ const nameLength = name.length;
349
+
350
+ if (result.error) {
351
+ return {
352
+ durationLen: 0,
353
+ durationStr: '',
354
+ error: true,
355
+ errorMessage: result.error?.message || String(result.error),
356
+ iterations: 0,
357
+ name,
358
+ nameLength,
359
+ opsPerSecLen: 0,
360
+ opsPerSecStr: '',
361
+ rmeLen: 0,
362
+ rmeStr: '',
363
+ status,
364
+ };
365
+ }
366
+
367
+ const duration = this.formatDuration(result.mean * 1e9);
368
+ const opsPerSec = this.formatOpsPerSecond(result.opsPerSecond);
369
+ const rme = this.formatPercentage(result.marginOfError * 100);
370
+
371
+ return {
372
+ durationLen: duration.length,
373
+ durationStr: duration,
374
+ error: false,
375
+ iterations: result.iterations,
376
+ name,
377
+ nameLength,
378
+ opsPerSecLen: opsPerSec.length,
379
+ opsPerSecStr: opsPerSec,
380
+ rmeLen: rme.length,
381
+ rmeStr: rme,
382
+ status,
383
+ };
384
+ });
385
+
386
+ // Find max widths
387
+ const nonWrappingTasks = formatted.filter(
388
+ (t) => t.nameLength <= MAX_NAME_WIDTH,
389
+ );
390
+ const maxNameLen =
391
+ nonWrappingTasks.length > 0
392
+ ? Math.max(...nonWrappingTasks.map((t) => t.nameLength))
393
+ : 40; // Default if all tasks wrap
394
+
395
+ const maxDurationLen = Math.max(
396
+ ...formatted.filter((t) => !t.error).map((t) => t.durationLen),
397
+ 0,
398
+ );
399
+ const maxRmeLen = Math.max(
400
+ ...formatted.filter((t) => !t.error).map((t) => t.rmeLen),
401
+ 0,
402
+ );
403
+ const maxOpsLen = Math.max(
404
+ ...formatted.filter((t) => !t.error).map((t) => t.opsPerSecLen),
405
+ 0,
406
+ );
407
+
408
+ // Calculate the position where numbers start for unwrapped lines
409
+ // BASE_INDENT (4) + status (1 char) + space (1) + maxNameLen + ": " (2) = 8 + maxNameLen
410
+ const numbersStartPos = BASE_INDENT.length + 2 + maxNameLen + 2;
411
+
412
+ // Print each task with aligned columns
413
+ for (const task of formatted) {
414
+ if (task.error) {
415
+ // Track failure for end summary
416
+ this.failures.push({
417
+ error: task.errorMessage || 'Unknown error',
418
+ file: this.currentFile,
419
+ suite: this.currentSuite,
420
+ task: task.name,
421
+ });
422
+
423
+ console.log(`${BASE_INDENT}${task.status} ${task.name} FAILED`);
424
+ } else if (task.nameLength > MAX_NAME_WIDTH) {
425
+ // Long name - wrap to next line, but align numbers with unwrapped lines
426
+ console.log(`${BASE_INDENT}${task.status} ${task.name}:`);
427
+
428
+ // Calculate padding to align with unwrapped lines
429
+ // We need to get to numbersStartPos from the beginning of the line
430
+ const leadingPad = ' '.repeat(numbersStartPos);
431
+ const durationPad = ' '.repeat(maxDurationLen - task.durationLen);
432
+ const rmePad = ' '.repeat(maxRmeLen - task.rmeLen);
433
+ const opsPad = ' '.repeat(maxOpsLen - task.opsPerSecLen);
434
+
435
+ console.log(
436
+ `${leadingPad}${durationPad}${task.durationStr} ${separator} ${symbols.plusMinus}${rmePad}${task.rmeStr} ${separator} ${opsPad}${task.opsPerSecStr}`,
437
+ );
438
+
439
+ if (this.verbose && task.iterations > 0) {
440
+ console.log(` ${task.iterations} iterations`);
441
+ }
442
+ } else {
443
+ // Normal length - align on same line
444
+ const namePad = ' '.repeat(maxNameLen - task.nameLength);
445
+ const durationPad = ' '.repeat(maxDurationLen - task.durationLen);
446
+ const rmePad = ' '.repeat(maxRmeLen - task.rmeLen);
447
+ const opsPad = ' '.repeat(maxOpsLen - task.opsPerSecLen);
448
+
449
+ console.log(
450
+ `${BASE_INDENT}${task.status} ${task.name}${namePad}: ${durationPad}${task.durationStr} ${separator} ${symbols.plusMinus}${rmePad}${task.rmeStr} ${separator} ${opsPad}${task.opsPerSecStr}`,
451
+ );
452
+
453
+ if (this.verbose && task.iterations > 0) {
454
+ console.log(` ${task.iterations} iterations`);
455
+ }
456
+ }
457
+ }
458
+ }
459
+ }