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
package/README.md ADDED
@@ -0,0 +1,699 @@
1
+ <p align="center">
2
+ <a href="/"><img src="./assets/logo-512.png" width="512px" align="center" alt="modestbench: a full-ass benchmarking framework for Node.js"/></a>
3
+ <h1 align="center"><span class="modestbench"><code>modestbench</code><span></h1>
4
+ <p align="center">
5
+ <em>“A full-ass benchmarking framework for Node.js”</em>
6
+ <br/>
7
+ <small>by <a href="https://github.com/boneskull" title="@boneskull on GitHub">@boneskull</a></small>
8
+ </p>
9
+ </p>
10
+
11
+ ## Features
12
+
13
+ - **Fast & Accurate**: High-precision timing with statistical analysis
14
+ - **Multiple Output Formats**: Human-readable, JSON, and CSV reports (at the same time!!)
15
+ - **Historical Tracking**: Store and compare benchmark results over time
16
+ - **Tagging System**: Organize and filter benchmarks by categories
17
+ - **CLI & API**: Command-line interface and programmatic API
18
+ - **TypeScript Support**: Full type safety
19
+
20
+ In summary, **modestbench** wraps [tinybench][] and enhances it with a bunch of crap so you don't have to think.
21
+
22
+ ## Quick Start
23
+
24
+ ### Installation
25
+
26
+ The usual suspects:
27
+
28
+ ```bash
29
+ npm install --save-dev modestbench
30
+ ```
31
+
32
+ ### Optional: Initialize a Project
33
+
34
+ The `modestbench` CLI provides a `init` command. This command:
35
+
36
+ 1. Generates a configuration file in a format of your choosing
37
+ 2. Creates an example benchmark file
38
+ 3. Appends `.modestbench/` to `.gitignore` to exclude historical data from version control
39
+
40
+ ```bash
41
+ # Initialize with examples and configuration
42
+ modestbench init
43
+
44
+ # Or specify project type and config format
45
+ modestbench init advanced --config-type typescript
46
+ ```
47
+
48
+ **Project Types:**
49
+
50
+ - `basic` - Simple setup for small projects (100 iterations, human reporter)
51
+ - `advanced` - Feature-rich with multiple reporters and structured output (1000 iterations, warmup, human + JSON reporters)
52
+ - `library` - Optimized for library performance testing (5000 iterations, high warmup, human + JSON reporters, organized suite structure)
53
+
54
+ ### My First Benchmark
55
+
56
+ > **_PRO TIP_**: The convention for **modestbench** benchmark files is to use the `.bench.js` or `.bench.ts` extension.
57
+
58
+ **modestbench** supports two formats for defining benchmarks:
59
+
60
+ #### Simplified Format (Recommended for Simple Cases)
61
+
62
+ For quick benchmarks with just a few tasks, you can use the simplified format:
63
+
64
+ ```javascript
65
+ // benchmarks/example.bench.js
66
+ export default {
67
+ 'Array.push()': () => {
68
+ const arr = [];
69
+ for (let i = 0; i < 1000; i++) {
70
+ arr.push(i);
71
+ }
72
+ return arr;
73
+ },
74
+
75
+ 'Array spread': () => {
76
+ let arr = [];
77
+ for (let i = 0; i < 1000; i++) {
78
+ arr = [...arr, i];
79
+ }
80
+ return arr;
81
+ },
82
+ };
83
+ ```
84
+
85
+ #### Suite-Based Format (For Complex Projects)
86
+
87
+ When you need to organize benchmarks into groups with setup/teardown hooks:
88
+
89
+ ```javascript
90
+ // benchmarks/example.bench.js
91
+ export default {
92
+ suites: {
93
+ 'Array Operations': {
94
+ benchmarks: {
95
+ 'Array.push()': () => {
96
+ const arr = [];
97
+ for (let i = 0; i < 1000; i++) {
98
+ arr.push(i);
99
+ }
100
+ return arr;
101
+ },
102
+
103
+ 'Array spread': () => {
104
+ let arr = [];
105
+ for (let i = 0; i < 1000; i++) {
106
+ arr = [...arr, i];
107
+ }
108
+ return arr;
109
+ },
110
+ },
111
+ },
112
+ },
113
+ };
114
+ ```
115
+
116
+ **When to use each format:**
117
+
118
+ > - **Simplified format**: Quick benchmarks, single file with related tasks, no setup/teardown needed
119
+ > - **Suite format**: Complex projects, multiple groups of benchmarks, need setup/teardown hooks, or want explicit organization
120
+
121
+ ### Running Your First Benchmarks
122
+
123
+ ```bash
124
+ # Run all benchmarks
125
+ modestbench run
126
+
127
+ # Run with specific options
128
+ modestbench run --iterations 5000 --reporters human,json
129
+ ```
130
+
131
+ ### View Results
132
+
133
+ ```text
134
+ 🚀 ModestBench
135
+
136
+ Environment:
137
+ Node.js: v24.10.0
138
+ Platform: darwin arm64
139
+ CPU: Apple M4 Max (16 cores)
140
+ Memory: 48.0 GB
141
+
142
+ Found 1 benchmark file(s)
143
+
144
+ ▶ benchmarks/example.bench.js
145
+
146
+ ▶ Array Operations
147
+ ✓ Array.push()
148
+ 810.05μs ±2.45% (1.23M ops/sec)
149
+ ✓ Array spread
150
+ 81.01ms ±4.12% (12.34K ops/sec)
151
+ ✓ 2 passed
152
+
153
+ ✓ All 2 tasks passed
154
+
155
+ 📊 Results
156
+
157
+ ✓ All tests passed: 2
158
+ 📁 Files: 1
159
+ 📊 Suites: 1
160
+ ⏱️ Duration: 1.82s
161
+
162
+ 🎉 All benchmarks completed successfully!
163
+ ```
164
+
165
+ ## Getting Started
166
+
167
+ Jump to:
168
+
169
+ - [Quick Start](#quick-start) - Basic concepts and your first benchmark
170
+ - [Configuration](#configuration) - Project and runtime configuration options
171
+ - [Advanced Features](#advanced-features) - Multiple suites, async operations, and tagging
172
+ - [Integration Examples](#integration-examples) - CI/CD integration and performance monitoring
173
+ - [Programmatic API](#programmatic-api) - Using **modestbench** programmatically
174
+
175
+ See the **[examples directory](examples/README.md)** for additional guides and sample code.
176
+
177
+ ## CLI Commands
178
+
179
+ ### Run Benchmarks
180
+
181
+ Run benchmarks with sensible defaults:
182
+
183
+ ```bash
184
+ # Run benchmarks in current directory and bench/ (top-level only)
185
+ modestbench run
186
+
187
+ # Run all benchmarks in a directory (searches recursively)
188
+ modestbench run benchmarks/
189
+
190
+ # Run benchmarks from multiple directories
191
+ modestbench run src/perf/ tests/benchmarks/
192
+
193
+ # Run specific files
194
+ modestbench run benchmarks/critical.bench.js
195
+
196
+ # Mix files, directories, and glob patterns
197
+ modestbench run file.bench.js benchmarks/ "tests/**/*.bench.ts"
198
+
199
+ # With options
200
+ modestbench run \
201
+ --config ./config.json \
202
+ --iterations 2000 \
203
+ --reporters human,json,csv \
204
+ --output ./results \
205
+ --tags performance,algorithm \
206
+ --concurrent
207
+ ```
208
+
209
+ **Supported file extensions:**
210
+
211
+ - JavaScript: `.js`, `.mjs`, `.cjs`
212
+ - TypeScript: `.ts`, `.mts`, `.cts`
213
+
214
+ #### Controlling Benchmark Limits
215
+
216
+ The `--limit-by` flag controls whether benchmarks are limited by time, iteration count, or both:
217
+
218
+ ```bash
219
+ # Limit by iteration count (fast, predictable sample size)
220
+ modestbench run --iterations 100
221
+
222
+ # Limit by time budget (ensures consistent time investment)
223
+ modestbench run --time 5000
224
+
225
+ # Limit by whichever comes first (safety bounds)
226
+ modestbench run --iterations 1000 --time 10000
227
+
228
+ # Explicit control (overrides smart defaults)
229
+ modestbench run --iterations 500 --time 5000 --limit-by time
230
+
231
+ # Require both thresholds (rare, for statistical rigor)
232
+ modestbench run --iterations 100 --time 2000 --limit-by all
233
+ ```
234
+
235
+ **Smart Defaults:**
236
+
237
+ - Only `--iterations` provided → limits by iteration count (fast)
238
+ - Only `--time` provided → limits by time budget
239
+ - Both provided → stops at whichever comes first (`any` mode)
240
+ - Neither provided → uses default iterations (100) with iterations mode
241
+
242
+ **Modes:**
243
+
244
+ - `iterations`: Stop after N samples (time budget set to 1ms)
245
+ - `time`: Run for T milliseconds (collect as many samples as possible)
246
+ - `any`: Stop when either threshold is reached (defaults to iterations behavior for fast completion)
247
+ - `all`: Require both time AND iterations thresholds (tinybench default behavior)
248
+
249
+ #### Filtering by Tags
250
+
251
+ Run specific subsets of benchmarks using tags:
252
+
253
+ ```bash
254
+ # Run only benchmarks tagged with 'fast'
255
+ modestbench run --tags fast
256
+
257
+ # Run benchmarks with multiple tags (OR logic - matches ANY)
258
+ modestbench run --tags string,array,algorithm
259
+
260
+ # Exclude specific benchmarks
261
+ modestbench run --exclude-tags slow,experimental
262
+
263
+ # Combine: run fast benchmarks except experimental ones
264
+ modestbench run --tags fast --exclude-tags experimental
265
+ ```
266
+
267
+ **Key Features:**
268
+
269
+ - Tags cascade from file → suite → task levels
270
+ - `--tags` uses OR logic (matches ANY specified tag)
271
+ - `--exclude-tags` takes precedence over `--tags`
272
+ - Suite setup/teardown only runs if at least one task matches
273
+
274
+ See [Tagging and Filtering](#tagging-and-filtering) for detailed examples.
275
+
276
+ ### History Management
277
+
278
+ **modestbench** automatically tracks benchmark results over time in a local `.modestbench/` directory. This history enables you to:
279
+
280
+ - **Track performance trends** - See how your code's performance changes across commits
281
+ - **Detect regressions** - Compare current results against previous runs to catch slowdowns
282
+ - **Analyze improvements** - Validate that optimizations actually improve performance
283
+ - **Document progress** - Export historical data for reports and analysis
284
+
285
+ ```bash
286
+ # List recent runs
287
+ modestbench history list
288
+
289
+ # Show detailed results
290
+ modestbench history show <run-id>
291
+
292
+ # Compare two runs
293
+ modestbench history compare <run-id-1> <run-id-2>
294
+
295
+ # Export historical data
296
+ modestbench history export --format csv --output results.csv
297
+
298
+ # Clean old data
299
+ modestbench history clean --older-than 30d
300
+ ```
301
+
302
+ ## Configuration
303
+
304
+ ### Project Configuration
305
+
306
+ Create `modestbench.config.json`:
307
+
308
+ ```jsonc
309
+ {
310
+ "bail": false, // Stop execution on first failure
311
+ "exclude": ["node_modules/**"], // Patterns to exclude from discovery
312
+ "excludeTags": ["slow", "experimental"], // Tags to exclude from execution
313
+ "iterations": 1000, // Number of samples per benchmark
314
+ "limitBy": "iterations", // Limit mode: 'iterations', 'time', 'any', 'all'
315
+ "outputDir": "./benchmark-results", // Directory for results and reports
316
+ "pattern": "benchmarks/**/*.bench.{js,ts}", // Glob pattern to discover benchmark files
317
+ "quiet": false, // Minimal output mode
318
+ "reporters": ["human", "json"], // Output reporters to use
319
+ "tags": ["fast", "critical"], // Tags to include (if empty, all benchmarks run)
320
+ "time": 5000, // Time budget in ms per benchmark
321
+ "timeout": 30000, // Task timeout in ms
322
+ "verbose": false, // Detailed output with debugging info
323
+ "warmup": 50, // Warmup iterations before measurement
324
+ }
325
+ ```
326
+
327
+ **Configuration Options:**
328
+
329
+ - `pattern` - Glob pattern(s) to discover benchmark files (can be string or array)
330
+ - `exclude` - Glob patterns for files/directories to exclude from discovery
331
+ - `excludeTags` - Array of tags to exclude from execution; benchmarks with ANY of these tags will be skipped (default: [])
332
+ - `iterations` - Number of samples to collect per benchmark task (default: 100)
333
+ - `time` - Time budget in milliseconds per benchmark task (default: 1000)
334
+ - `limitBy` - How to limit benchmarks: `"iterations"` (sample count), `"time"` (time budget), `"any"` (whichever comes first), or `"all"` (both thresholds required)
335
+ - `warmup` - Number of warmup iterations before measurement begins (default: 0)
336
+ - `timeout` - Maximum time in milliseconds for a single task before timing out (default: 30000)
337
+ - `bail` - Stop execution on first benchmark failure (default: false)
338
+ - `reporters` - Array of reporter names to use for output (available: `"human"`, `"json"`, `"csv"`)
339
+ - `outputDir` - Directory path for saving benchmark results and reports
340
+ - `quiet` - Minimal output mode, suppresses non-essential messages (default: false)
341
+ - `tags` - Array of tags to include; if non-empty, only benchmarks with ANY of these tags will run (default: [])
342
+ - `verbose` - Detailed output mode with additional debugging information (default: false)
343
+
344
+ > **Note:** Smart defaults apply for `limitBy` based on which options you provide. See [Controlling Benchmark Limits](#controlling-benchmark-limits) for details.
345
+
346
+ ### Configuration File Support
347
+
348
+ **modestbench** supports multiple configuration file formats, powered by [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig):
349
+
350
+ - **JSON**: `modestbench.config.json`, `.modestbenchrc.json`, `.modestbenchrc`
351
+ - **YAML**: `modestbench.config.yaml`, `modestbench.config.yml`, `.modestbenchrc.yaml`, `.modestbenchrc.yml`
352
+ - **JavaScript**: `modestbench.config.js`, `modestbench.config.mjs`, `.modestbenchrc.js`, `.modestbenchrc.mjs`
353
+ - **TypeScript**: `modestbench.config.ts`
354
+ - **package.json**: Use a `"modestbench"` field
355
+
356
+ Generate a configuration file using:
357
+
358
+ ```bash
359
+ modestbench init --config-type json # JSON format
360
+ modestbench init --config-type yaml # YAML format
361
+ modestbench init --config-type js # JavaScript format
362
+ modestbench init --config-type ts # TypeScript format
363
+ ```
364
+
365
+ **Configuration Discovery**: **modestbench** automatically searches for configuration files in the current directory and parent directories, following standard conventions.
366
+
367
+ ## Output Formats
368
+
369
+ ### Human-Readable (Default)
370
+
371
+ Real-time progress bars with color-coded results and performance summaries.
372
+
373
+ ### JSON Output
374
+
375
+ Structured data perfect for programmatic analysis and integration:
376
+
377
+ ```json
378
+ {
379
+ "results": [
380
+ {
381
+ "file": "example.bench.js",
382
+ "suite": "Array Operations",
383
+ "task": "Array.push()",
384
+ "hz": 1234567.89,
385
+ "stats": {
386
+ "mean": 0.00081,
387
+ "stdDev": 0.00002,
388
+ "marginOfError": 2.45
389
+ }
390
+ }
391
+ ],
392
+ "run": {
393
+ "id": "run-2025-10-07-001",
394
+ "timestamp": "2025-10-07T10:30:00.000Z",
395
+ "duration": 15420,
396
+ "status": "completed"
397
+ }
398
+ }
399
+ ```
400
+
401
+ ### CSV Export
402
+
403
+ Tabular data for spreadsheet analysis and historical tracking.
404
+
405
+ ## Advanced Features
406
+
407
+ ### Multiple Suites
408
+
409
+ ```javascript
410
+ const state = {
411
+ data: [],
412
+ sortedData: [],
413
+ };
414
+
415
+ export default {
416
+ suites: {
417
+ Sorting: {
418
+ setup() {
419
+ state.data = generateTestData(1000);
420
+ },
421
+ benchmarks: {
422
+ // Shorthand syntax for simple benchmarks
423
+ 'Quick Sort': () => quickSort(state.data),
424
+ 'Merge Sort': () => mergeSort(state.data),
425
+ },
426
+ },
427
+
428
+ Searching: {
429
+ setup() {
430
+ state.sortedData = generateSortedData(10000);
431
+ },
432
+ benchmarks: {
433
+ 'Binary Search': () => binarySearch(state.sortedData, 5000),
434
+ 'Linear Search': () => linearSearch(state.sortedData, 5000),
435
+ },
436
+ },
437
+ },
438
+ };
439
+ ```
440
+
441
+ ### Async Operations
442
+
443
+ ```javascript
444
+ export default {
445
+ suites: {
446
+ 'Async Performance': {
447
+ benchmarks: {
448
+ // Shorthand syntax works with async functions too
449
+ 'Promise.resolve()': async () => {
450
+ return await Promise.resolve('test');
451
+ },
452
+
453
+ // Full syntax when you need config, tags, or metadata
454
+ 'Fetch Simulation': {
455
+ async fn() {
456
+ const response = await simulateApiCall();
457
+ return response.json();
458
+ },
459
+ config: {
460
+ iterations: 100, // Fewer iterations for slow operations
461
+ },
462
+ },
463
+ },
464
+ },
465
+ },
466
+ };
467
+ ```
468
+
469
+ ### Tagging and Filtering
470
+
471
+ **modestbench** supports a powerful tagging system that lets you organize and selectively run benchmarks. Tags can be applied at three levels: file, suite, and task. Tags automatically cascade from parent to child, so tasks inherit tags from their suite and file.
472
+
473
+ #### Adding Tags
474
+
475
+ Tags can be added at any level:
476
+
477
+ ```javascript
478
+ export default {
479
+ // File-level tags (inherited by all suites and tasks)
480
+ tags: ['performance', 'core'],
481
+
482
+ suites: {
483
+ 'String Operations': {
484
+ // Suite-level tags (inherited by all tasks in this suite)
485
+ tags: ['string', 'fast'],
486
+
487
+ benchmarks: {
488
+ // Task inherits: ['performance', 'core', 'string', 'fast', 'regex']
489
+ 'RegExp Test': {
490
+ fn: () => /pattern/.test(str),
491
+ tags: ['regex'], // Task-specific tags
492
+ },
493
+
494
+ // Task inherits: ['performance', 'core', 'string', 'fast']
495
+ 'String Includes': () => str.includes('pattern'),
496
+ },
497
+ },
498
+ },
499
+ };
500
+ ```
501
+
502
+ #### Filtering Benchmarks
503
+
504
+ Use `--tags` to include only benchmarks with specific tags (OR logic - matches ANY tag):
505
+
506
+ ```bash
507
+ # Run only fast algorithms
508
+ modestbench run --tags fast
509
+
510
+ # Run benchmarks tagged with 'string' OR 'array'
511
+ modestbench run --tags string,array
512
+
513
+ # Multiple tags can be space-separated too
514
+ modestbench run --tags fast optimized
515
+ ```
516
+
517
+ Use `--exclude-tags` to skip benchmarks with specific tags:
518
+
519
+ ```bash
520
+ # Exclude slow benchmarks
521
+ modestbench run --exclude-tags slow
522
+
523
+ # Exclude experimental and unstable benchmarks
524
+ modestbench run --exclude-tags experimental,unstable
525
+ ```
526
+
527
+ Combine both to fine-tune your benchmark runs (exclusion takes precedence):
528
+
529
+ ```bash
530
+ # Run fast benchmarks, but exclude experimental ones
531
+ modestbench run --tags fast --exclude-tags experimental
532
+
533
+ # Run algorithm benchmarks but skip slow reference implementations
534
+ modestbench run --tags algorithm --exclude-tags slow,reference
535
+ ```
536
+
537
+ #### Tag Cascading Example
538
+
539
+ ```javascript
540
+ export default {
541
+ tags: ['file-level'], // All tasks get this tag
542
+
543
+ suites: {
544
+ 'Fast Suite': {
545
+ tags: ['fast'], // Tasks get: ['file-level', 'fast']
546
+ benchmarks: {
547
+ 'Task A': {
548
+ fn: () => {},
549
+ tags: ['math'], // This task has: ['file-level', 'fast', 'math']
550
+ },
551
+ 'Task B': () => {}, // This task has: ['file-level', 'fast']
552
+ },
553
+ },
554
+ },
555
+ };
556
+ ```
557
+
558
+ **Filtering Behavior:**
559
+
560
+ - `--tags math` → Runs only Task A (matches 'math')
561
+ - `--tags fast` → Runs both Task A and Task B (both have 'fast')
562
+ - `--tags file-level` → Runs both tasks (both inherit 'file-level')
563
+ - `--exclude-tags math` → Runs only Task B (Task A excluded)
564
+
565
+ #### Suite Lifecycle with Filtering
566
+
567
+ Suite `setup()` and `teardown()` only run if at least one task in the suite matches the filter. This prevents unnecessary setup work for filtered-out suites.
568
+
569
+ ## Integration Examples
570
+
571
+ ### GitHub Actions
572
+
573
+ ```yaml
574
+ name: Performance Tests
575
+ on: [push, pull_request]
576
+
577
+ jobs:
578
+ benchmark:
579
+ runs-on: ubuntu-latest
580
+ steps:
581
+ - uses: actions/checkout@v3
582
+ - uses: actions/setup-node@v3
583
+ with:
584
+ node-version: 18
585
+
586
+ - run: npm ci
587
+ - run: npm run build
588
+
589
+ - name: Run Benchmarks
590
+ run: |
591
+ modestbench run \
592
+ --reporters json,csv \
593
+ --output ./results
594
+
595
+ - name: Upload Results
596
+ uses: actions/upload-artifact@v3
597
+ with:
598
+ name: benchmark-results
599
+ path: ./results/
600
+ ```
601
+
602
+ ### Performance Regression Detection
603
+
604
+ ```javascript
605
+ // scripts/check-regression.js
606
+ import { execSync } from 'child_process';
607
+ import { readFileSync } from 'fs';
608
+
609
+ // Run current benchmarks
610
+ execSync('modestbench run --reporters json --output ./current');
611
+ const current = JSON.parse(readFileSync('./current/results.json'));
612
+
613
+ // Load baseline results
614
+ const baseline = JSON.parse(readFileSync('./baseline/results.json'));
615
+
616
+ // Check for significant regressions
617
+ for (const result of current.results) {
618
+ const baselineResult = baseline.results.find(
619
+ (r) => r.file === result.file && r.task === result.task,
620
+ );
621
+
622
+ if (baselineResult) {
623
+ const regression = (baselineResult.hz - result.hz) / baselineResult.hz;
624
+ if (regression > 0.1) {
625
+ // 10% regression threshold
626
+ console.error(
627
+ `Performance regression detected in ${result.task}: ${(regression * 100).toFixed(1)}%`,
628
+ );
629
+ process.exit(1);
630
+ }
631
+ }
632
+ }
633
+
634
+ console.log('No performance regressions detected ✅');
635
+ ```
636
+
637
+ ## Programmatic API
638
+
639
+ ```typescript
640
+ import { modestbench, HumanReporter } from 'modestbench';
641
+
642
+ // initialize the engine
643
+ const engine = modestbench();
644
+
645
+ engine.registerReporter('human', new HumanReporter());
646
+
647
+ // Execute benchmarks
648
+ const result = await engine.execute({
649
+ pattern: '**/*.bench.js',
650
+ iterations: 1000,
651
+ });
652
+ ```
653
+
654
+ ## Contributing
655
+
656
+ We welcome contributions! Please see our [Contributing Guide][contributing] for details.
657
+
658
+ ### Development Setup
659
+
660
+ ```bash
661
+ # Clone the repository
662
+ git clone https://github.com/boneskull/modestbench.git
663
+ cd modestbench
664
+
665
+ # Install dependencies
666
+ npm install
667
+
668
+ # Run tests
669
+ npm test
670
+
671
+ # Build the project
672
+ npm run build
673
+
674
+ # Run examples
675
+ npm run examples
676
+ ```
677
+
678
+ ## Acknowledgments
679
+
680
+ - Built on top of the small-but-mighty benchmarking library, [tinybench][]
681
+ - Interface inspired by good ol' [Benchmark.js][]
682
+ - Built with [zshy][] for dual ESM/CJS modules
683
+
684
+ ## Resources
685
+
686
+ - [Issue Tracker][bugs]
687
+ - [Discussions][]
688
+
689
+ ## License
690
+
691
+ Copyright © 2025 [Christopher Hiller](https://github.com/boneskull). Licensed under the [Blue Oak Model License 1.0.0][license].
692
+
693
+ [license]: https://blueoakcouncil.org/license/1.0.0
694
+ [tinybench]: https://github.com/tinylibs/tinybench
695
+ [benchmark.js]: https://benchmarkjs.com/
696
+ [bugs]: https://github.com/boneskull/modestbench/issues
697
+ [discussions]: https://github.com/boneskull/modestbench/discussions
698
+ [zshy]: https://github.com/colinhacks/zshy
699
+ [contributing]: CONTRIBUTING.md