benchforge 0.1.9 → 0.2.4

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 (253) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +99 -260
  3. package/bin/benchforge +1 -2
  4. package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
  5. package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
  6. package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
  7. package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
  8. package/dist/BenchRunner-DglX1NOn.d.mts +302 -0
  9. package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
  10. package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
  11. package/dist/Formatters-BWj3d4sv.mjs +95 -0
  12. package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
  13. package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
  14. package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
  15. package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
  16. package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
  17. package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
  18. package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
  19. package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
  20. package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
  21. package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
  22. package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
  23. package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
  24. package/dist/bin/benchforge.mjs +4 -5
  25. package/dist/bin/benchforge.mjs.map +1 -1
  26. package/dist/index.d.mts +731 -522
  27. package/dist/index.mjs +98 -3
  28. package/dist/index.mjs.map +1 -0
  29. package/dist/runners/WorkerScript.d.mts +12 -4
  30. package/dist/runners/WorkerScript.mjs +92 -120
  31. package/dist/runners/WorkerScript.mjs.map +1 -1
  32. package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
  33. package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
  34. package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
  35. package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
  36. package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
  37. package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
  38. package/dist/viewer/index.html +19 -0
  39. package/dist/viewer/speedscope/LICENSE +21 -0
  40. package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
  41. package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
  42. package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
  43. package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
  44. package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
  45. package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
  46. package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
  47. package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
  48. package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
  49. package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
  50. package/dist/viewer/speedscope/file-format-schema.json +274 -0
  51. package/dist/viewer/speedscope/index.html +19 -0
  52. package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
  53. package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
  54. package/dist/viewer/speedscope/release.txt +3 -0
  55. package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
  56. package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
  57. package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
  58. package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
  59. package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
  60. package/package.json +52 -26
  61. package/src/bin/benchforge.ts +2 -2
  62. package/src/cli/AnalyzeArchive.ts +232 -0
  63. package/src/cli/BrowserBench.ts +322 -0
  64. package/src/cli/CliArgs.ts +164 -48
  65. package/src/cli/CliExport.ts +179 -0
  66. package/src/cli/CliOptions.ts +147 -0
  67. package/src/cli/CliReport.ts +197 -0
  68. package/src/cli/FilterBenchmarks.ts +18 -30
  69. package/src/cli/RunBenchCLI.ts +138 -844
  70. package/src/cli/SuiteRunner.ts +160 -0
  71. package/src/cli/ViewerServer.ts +282 -0
  72. package/src/export/AllocExport.ts +121 -0
  73. package/src/export/ArchiveExport.ts +146 -0
  74. package/src/export/ArchiveFormat.ts +50 -0
  75. package/src/export/CoverageExport.ts +148 -0
  76. package/src/export/EditorUri.ts +10 -0
  77. package/src/export/PerfettoExport.ts +91 -126
  78. package/src/export/SpeedscopeTypes.ts +98 -0
  79. package/src/export/TimeExport.ts +115 -0
  80. package/src/index.ts +87 -62
  81. package/src/matrix/BenchMatrix.ts +230 -0
  82. package/src/matrix/CaseLoader.ts +8 -6
  83. package/src/matrix/MatrixDirRunner.ts +153 -0
  84. package/src/matrix/MatrixFilter.ts +55 -53
  85. package/src/matrix/MatrixInlineRunner.ts +50 -0
  86. package/src/matrix/MatrixReport.ts +94 -254
  87. package/src/matrix/VariantLoader.ts +9 -9
  88. package/src/profiling/browser/BenchLoop.ts +51 -0
  89. package/src/profiling/browser/BrowserCDP.ts +133 -0
  90. package/src/profiling/browser/BrowserGcStats.ts +33 -0
  91. package/src/profiling/browser/BrowserProfiler.ts +160 -0
  92. package/src/profiling/browser/CdpClient.ts +82 -0
  93. package/src/profiling/browser/CdpPage.ts +138 -0
  94. package/src/profiling/browser/ChromeLauncher.ts +158 -0
  95. package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
  96. package/src/profiling/browser/PageLoadMode.ts +61 -0
  97. package/src/profiling/node/CoverageSampler.ts +27 -0
  98. package/src/profiling/node/CoverageTypes.ts +23 -0
  99. package/src/profiling/node/HeapSampleReport.ts +261 -0
  100. package/src/{heap-sample → profiling/node}/HeapSampler.ts +55 -13
  101. package/src/profiling/node/ResolvedProfile.ts +98 -0
  102. package/src/profiling/node/TimeSampler.ts +57 -0
  103. package/src/report/BenchmarkReport.ts +146 -0
  104. package/src/report/Colors.ts +9 -0
  105. package/src/report/Formatters.ts +110 -0
  106. package/src/report/GcSections.ts +151 -0
  107. package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
  108. package/src/report/HtmlReport.ts +223 -0
  109. package/src/report/ParseStats.ts +73 -0
  110. package/src/report/StandardSections.ts +147 -0
  111. package/src/report/ViewerSections.ts +286 -0
  112. package/src/report/text/TableReport.ts +253 -0
  113. package/src/report/text/TextReport.ts +123 -0
  114. package/src/runners/AdaptiveWrapper.ts +167 -287
  115. package/src/runners/BenchRunner.ts +27 -22
  116. package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
  117. package/src/runners/CreateRunner.ts +5 -7
  118. package/src/runners/GcStats.ts +58 -61
  119. package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
  120. package/src/runners/MergeBatches.ts +123 -0
  121. package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
  122. package/src/runners/RunnerOrchestrator.ts +180 -296
  123. package/src/runners/RunnerUtils.ts +75 -1
  124. package/src/runners/SampleStats.ts +100 -0
  125. package/src/runners/TimingRunner.ts +244 -0
  126. package/src/runners/TimingUtils.ts +3 -2
  127. package/src/runners/WorkerScript.ts +162 -178
  128. package/src/stats/BootstrapDifference.ts +282 -0
  129. package/src/{PermutationTest.ts → stats/PermutationTest.ts} +31 -40
  130. package/src/stats/StatisticalUtils.ts +445 -0
  131. package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
  132. package/src/test/AdaptiveRunner.test.ts +39 -41
  133. package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
  134. package/src/test/AdaptiveStatistics.integration.ts +9 -41
  135. package/src/{tests → test}/BenchMatrix.test.ts +31 -28
  136. package/src/test/BenchmarkReport.test.ts +63 -13
  137. package/src/test/BrowserBench.e2e.test.ts +186 -17
  138. package/src/test/BrowserBench.test.ts +10 -5
  139. package/src/test/BuildTimeSection.test.ts +130 -0
  140. package/src/test/CapSamples.test.ts +82 -0
  141. package/src/test/CoverageExport.test.ts +115 -0
  142. package/src/test/CoverageSampler.test.ts +33 -0
  143. package/src/test/HeapAttribution.test.ts +51 -0
  144. package/src/{tests → test}/MatrixFilter.test.ts +16 -16
  145. package/src/{tests → test}/MatrixReport.test.ts +1 -1
  146. package/src/test/PermutationTest.test.ts +1 -1
  147. package/src/{tests → test}/RealDataValidation.test.ts +6 -6
  148. package/src/test/RunBenchCLI.test.ts +57 -56
  149. package/src/test/RunnerOrchestrator.test.ts +12 -12
  150. package/src/test/StatisticalUtils.test.ts +48 -12
  151. package/src/{table-util/test → test}/TableReport.test.ts +2 -2
  152. package/src/test/TestUtils.ts +35 -30
  153. package/src/test/TimeExport.test.ts +139 -0
  154. package/src/test/TimeSampler.test.ts +37 -0
  155. package/src/test/ViewerLive.e2e.test.ts +159 -0
  156. package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
  157. package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
  158. package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
  159. package/src/test/fixtures/cases/asyncCases.ts +9 -0
  160. package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
  161. package/src/test/fixtures/cases/variants/product.ts +2 -0
  162. package/src/test/fixtures/cases/variants/sum.ts +2 -0
  163. package/src/test/fixtures/discover/fast.ts +1 -0
  164. package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
  165. package/src/test/fixtures/invalid/bad.ts +1 -0
  166. package/src/test/fixtures/loader/fast.ts +1 -0
  167. package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
  168. package/src/test/fixtures/loader/stateful.ts +2 -0
  169. package/src/test/fixtures/stateful/stateful.ts +2 -0
  170. package/src/test/fixtures/variants/extra.ts +1 -0
  171. package/src/test/fixtures/variants/impl.ts +1 -0
  172. package/src/test/fixtures/worker/fast.ts +1 -0
  173. package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
  174. package/src/viewer/DateFormat.ts +30 -0
  175. package/src/viewer/Helpers.ts +23 -0
  176. package/src/viewer/LineData.ts +120 -0
  177. package/src/viewer/Providers.ts +191 -0
  178. package/src/viewer/ReportData.ts +123 -0
  179. package/src/viewer/State.ts +49 -0
  180. package/src/viewer/Theme.ts +15 -0
  181. package/src/viewer/components/App.tsx +73 -0
  182. package/src/viewer/components/DropZone.tsx +71 -0
  183. package/src/viewer/components/LazyPlot.ts +33 -0
  184. package/src/viewer/components/SamplesPanel.tsx +214 -0
  185. package/src/viewer/components/Shell.tsx +26 -0
  186. package/src/viewer/components/SourcePanel.tsx +216 -0
  187. package/src/viewer/components/SummaryPanel.tsx +332 -0
  188. package/src/viewer/components/TabBar.tsx +131 -0
  189. package/src/viewer/components/TabContent.tsx +46 -0
  190. package/src/viewer/components/ThemeToggle.tsx +50 -0
  191. package/src/viewer/index.html +20 -0
  192. package/src/viewer/main.tsx +4 -0
  193. package/src/viewer/plots/CIPlot.ts +313 -0
  194. package/src/{html/browser → viewer/plots}/HistogramKde.ts +42 -47
  195. package/src/viewer/plots/LegendUtils.ts +134 -0
  196. package/src/viewer/plots/PlotTypes.ts +85 -0
  197. package/src/viewer/plots/RenderPlots.ts +230 -0
  198. package/src/viewer/plots/SampleTimeSeries.ts +306 -0
  199. package/src/viewer/plots/SvgHelpers.ts +136 -0
  200. package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
  201. package/src/viewer/report.css +427 -0
  202. package/src/viewer/shell.css +357 -0
  203. package/src/viewer/tsconfig.json +11 -0
  204. package/dist/BenchRunner-CSKN9zPy.d.mts +0 -225
  205. package/dist/BrowserHeapSampler-DCeL42RE.mjs +0 -202
  206. package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
  207. package/dist/GcStats-ByEovUi1.mjs +0 -77
  208. package/dist/GcStats-ByEovUi1.mjs.map +0 -1
  209. package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
  210. package/dist/TimingUtils-ClclVQ7E.mjs +0 -597
  211. package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
  212. package/dist/browser/index.js +0 -914
  213. package/dist/src-Cf_LXwlp.mjs +0 -2873
  214. package/dist/src-Cf_LXwlp.mjs.map +0 -1
  215. package/src/BenchMatrix.ts +0 -380
  216. package/src/BenchmarkReport.ts +0 -156
  217. package/src/HtmlDataPrep.ts +0 -148
  218. package/src/StandardSections.ts +0 -261
  219. package/src/StatisticalUtils.ts +0 -176
  220. package/src/TypeUtil.ts +0 -8
  221. package/src/browser/BrowserGcStats.ts +0 -44
  222. package/src/browser/BrowserHeapSampler.ts +0 -271
  223. package/src/export/JsonExport.ts +0 -103
  224. package/src/export/JsonFormat.ts +0 -91
  225. package/src/heap-sample/HeapSampleReport.ts +0 -196
  226. package/src/html/HtmlReport.ts +0 -131
  227. package/src/html/HtmlTemplate.ts +0 -284
  228. package/src/html/Types.ts +0 -88
  229. package/src/html/browser/CIPlot.ts +0 -287
  230. package/src/html/browser/LegendUtils.ts +0 -163
  231. package/src/html/browser/RenderPlots.ts +0 -263
  232. package/src/html/browser/SampleTimeSeries.ts +0 -389
  233. package/src/html/browser/Types.ts +0 -96
  234. package/src/html/browser/index.ts +0 -1
  235. package/src/html/index.ts +0 -17
  236. package/src/runners/BasicRunner.ts +0 -364
  237. package/src/table-util/ConvergenceFormatters.ts +0 -19
  238. package/src/table-util/Formatters.ts +0 -152
  239. package/src/table-util/README.md +0 -70
  240. package/src/table-util/TableReport.ts +0 -293
  241. package/src/tests/fixtures/cases/asyncCases.ts +0 -7
  242. package/src/tests/fixtures/cases/variants/product.ts +0 -2
  243. package/src/tests/fixtures/cases/variants/sum.ts +0 -2
  244. package/src/tests/fixtures/discover/fast.ts +0 -1
  245. package/src/tests/fixtures/invalid/bad.ts +0 -1
  246. package/src/tests/fixtures/loader/fast.ts +0 -1
  247. package/src/tests/fixtures/loader/stateful.ts +0 -2
  248. package/src/tests/fixtures/stateful/stateful.ts +0 -2
  249. package/src/tests/fixtures/variants/extra.ts +0 -1
  250. package/src/tests/fixtures/variants/impl.ts +0 -1
  251. package/src/tests/fixtures/worker/fast.ts +0 -1
  252. package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
  253. package/src/{table-util/test → test}/TableValueExtractor.ts +9 -9
@@ -0,0 +1,197 @@
1
+ import type { MatrixResults } from "../matrix/BenchMatrix.ts";
2
+ import type { MatrixReportOptions } from "../matrix/MatrixReport.ts";
3
+ import { reportMatrixResults } from "../matrix/MatrixReport.ts";
4
+ import {
5
+ aggregateSites,
6
+ filterSites,
7
+ flattenProfile,
8
+ formatHeapReport,
9
+ formatRawSamples,
10
+ type HeapReportOptions,
11
+ } from "../profiling/node/HeapSampleReport.ts";
12
+ import { resolveProfile } from "../profiling/node/ResolvedProfile.ts";
13
+ import type { ReportGroup, ReportSection } from "../report/BenchmarkReport.ts";
14
+ import { groupReports, hasField } from "../report/BenchmarkReport.ts";
15
+ import colors from "../report/Colors.ts";
16
+ import { gcStatsSection } from "../report/GcSections.ts";
17
+ import {
18
+ adaptiveSections,
19
+ buildTimeSection,
20
+ formatTierSummary,
21
+ optSection,
22
+ runsSection,
23
+ totalTimeSection,
24
+ } from "../report/StandardSections.ts";
25
+ import { reportResults } from "../report/text/TextReport.ts";
26
+ import type { DefaultCliArgs } from "./CliArgs.ts";
27
+ import { cliComparisonOptions } from "./CliOptions.ts";
28
+
29
+ /** Options for defaultReport: custom sections replace the CLI-derived defaults. */
30
+ export interface DefaultReportOptions {
31
+ sections?: ReportSection[];
32
+ }
33
+
34
+ const { yellow, dim } = colors;
35
+
36
+ /** Show a transient status message on stderr, run a sync computation, then clear. */
37
+ export function withStatus<T>(msg: string, fn: () => T): T {
38
+ process.stderr.write(`◊ ${msg}...\r`);
39
+ const result = fn();
40
+ process.stderr.write("\r" + " ".repeat(40) + "\r");
41
+ return result;
42
+ }
43
+
44
+ /** Generate text report table with standard sections based on CLI args. */
45
+ export function defaultReport(
46
+ groups: ReportGroup[],
47
+ args: DefaultCliArgs,
48
+ opts?: DefaultReportOptions,
49
+ ): string {
50
+ const sections = opts?.sections?.length
51
+ ? opts.sections
52
+ : cliDefaultSections(groups, args);
53
+ return reportResults(groups, sections, cliComparisonOptions(args));
54
+ }
55
+
56
+ /** Log V8 optimization tier distribution and deoptimizations. */
57
+ export function reportOptStatus(groups: ReportGroup[]): void {
58
+ const optData = groups.flatMap(group =>
59
+ groupReports(group)
60
+ .filter(r => r.measuredResults.optStatus)
61
+ .map(({ name, measuredResults: m }) => ({
62
+ name,
63
+ opt: m.optStatus!,
64
+ samples: m.samples.length,
65
+ })),
66
+ );
67
+ if (optData.length === 0) return;
68
+
69
+ console.log(dim("\nV8 optimization:"));
70
+ for (const { name, opt, samples } of optData) {
71
+ const tierParts = formatTierSummary(opt, " ", ", ");
72
+ console.log(` ${name}: ${tierParts} ${dim(`(${samples} samples)`)}`);
73
+ }
74
+
75
+ const totalDeopts = optData.reduce((sum, d) => sum + d.opt.deoptCount, 0);
76
+ if (totalDeopts > 0) {
77
+ const plural = totalDeopts > 1 ? "s" : "";
78
+ console.log(yellow(` ⚠ ${totalDeopts} deoptimization${plural} detected`));
79
+ }
80
+ }
81
+
82
+ /** Print heap allocation profiles for each benchmark in the report groups. */
83
+ export function printHeapReports(
84
+ groups: ReportGroup[],
85
+ options: HeapReportOptions,
86
+ ): void {
87
+ for (const report of groups.flatMap(g => groupReports(g))) {
88
+ const { heapProfile } = report.measuredResults;
89
+ if (!heapProfile) continue;
90
+ console.log(dim(`\n─── Heap profile: ${report.name} ───`));
91
+ const resolved = resolveProfile(heapProfile);
92
+ const sites = flattenProfile(resolved);
93
+ const userSites = filterSites(sites, options.isUserCode);
94
+ const agg = aggregateSites(options.userOnly ? userSites : sites);
95
+ const { totalBytes, sortedSamples } = resolved;
96
+ const totalUserCode = userSites.reduce((sum, s) => sum + s.bytes, 0);
97
+ const sampleCount = sortedSamples?.length;
98
+ const heapOpts = {
99
+ ...options,
100
+ totalAll: totalBytes,
101
+ totalUserCode,
102
+ sampleCount,
103
+ };
104
+ console.log(formatHeapReport(agg, heapOpts));
105
+ if (options.raw) {
106
+ console.log(dim(`\n─── Raw samples: ${report.name} ───`));
107
+ console.log(formatRawSamples(resolved));
108
+ }
109
+ }
110
+ }
111
+
112
+ /** Format matrix benchmark results as text, applying default sections from CLI args. */
113
+ export function defaultMatrixReport(
114
+ results: MatrixResults[],
115
+ reportOptions?: MatrixReportOptions,
116
+ args?: DefaultCliArgs,
117
+ ): string {
118
+ const options = args
119
+ ? mergeMatrixDefaults(reportOptions, args, results)
120
+ : reportOptions;
121
+ return results.map(r => reportMatrixResults(r, options)).join("\n\n");
122
+ }
123
+
124
+ /** Convert MatrixResults to ReportGroup[] for the standard export pipeline. */
125
+ export function matrixToReportGroups(results: MatrixResults[]): ReportGroup[] {
126
+ return results.flatMap(matrix =>
127
+ matrix.variants.flatMap(variant =>
128
+ variant.cases.map(c => caseToReportGroup(variant.id, c)),
129
+ ),
130
+ );
131
+ }
132
+
133
+ /** Assemble report sections from CLI flags. Under --adaptive, the
134
+ * adaptive section provides its own time columns and `stats` is ignored. */
135
+ export function buildReportSections(
136
+ adaptive: boolean,
137
+ gcStats: boolean,
138
+ hasOptData: boolean,
139
+ stats?: string,
140
+ ): ReportSection[] {
141
+ return [
142
+ ...(adaptive
143
+ ? [...adaptiveSections, totalTimeSection]
144
+ : [buildTimeSection(stats)]),
145
+ ...(gcStats ? [gcStatsSection] : []),
146
+ ...(hasOptData ? [optSection] : []),
147
+ runsSection,
148
+ ];
149
+ }
150
+
151
+ /** Build sections from CLI feature flags (time/gc/opt/runs). */
152
+ function cliDefaultSections(
153
+ groups: ReportGroup[],
154
+ args: DefaultCliArgs,
155
+ ): ReportSection[] {
156
+ const { adaptive, "gc-stats": gcStats, "trace-opt": traceOpt, stats } = args;
157
+ const hasOpt = hasField(groups, "optStatus");
158
+ return buildReportSections(adaptive, gcStats, traceOpt && hasOpt, stats);
159
+ }
160
+
161
+ /** Apply default sections and extra columns for matrix reports. */
162
+ function mergeMatrixDefaults(
163
+ opts: MatrixReportOptions | undefined,
164
+ args: DefaultCliArgs,
165
+ results: MatrixResults[],
166
+ ): MatrixReportOptions {
167
+ const merged: MatrixReportOptions = { ...opts };
168
+ if (!merged.sections?.length) {
169
+ const groups = matrixToReportGroups(results);
170
+ const hasOpt = args["trace-opt"] && hasField(groups, "optStatus");
171
+ merged.sections = buildReportSections(
172
+ args.adaptive,
173
+ args["gc-stats"],
174
+ hasOpt,
175
+ args.stats,
176
+ );
177
+ }
178
+ if (!merged.comparison) merged.comparison = cliComparisonOptions(args);
179
+ return merged;
180
+ }
181
+
182
+ /** Wrap a single matrix case and its optional baseline into a ReportGroup. */
183
+ function caseToReportGroup(
184
+ variantId: string,
185
+ c: MatrixResults["variants"][0]["cases"][0],
186
+ ): ReportGroup {
187
+ const { metadata, baseline: baselineMeasured } = c;
188
+ const report = { name: variantId, measuredResults: c.measured, metadata };
189
+ const baseline = baselineMeasured
190
+ ? {
191
+ name: `${variantId} (baseline)`,
192
+ measuredResults: baselineMeasured,
193
+ metadata,
194
+ }
195
+ : undefined;
196
+ return { name: `${variantId} / ${c.caseId}`, reports: [report], baseline };
197
+ }
@@ -1,6 +1,6 @@
1
- import type { BenchGroup, BenchSuite } from "../Benchmark.ts";
1
+ import type { BenchSuite } from "../runners/BenchmarkSpec.ts";
2
2
 
3
- /** Filter benchmarks by name pattern */
3
+ /** Filter suite benchmarks by name pattern (substring or regex). */
4
4
  export function filterBenchmarks(
5
5
  suite: BenchSuite,
6
6
  filter?: string,
@@ -20,49 +20,37 @@ export function filterBenchmarks(
20
20
  : undefined,
21
21
  }))
22
22
  .filter(group => !removeEmpty || group.benchmarks.length > 0);
23
- validateFilteredSuite(groups, filter);
23
+ if (groups.every(g => g.benchmarks.length === 0)) {
24
+ throw new Error(`No benchmarks match filter: "${filter}"`);
25
+ }
24
26
  return { name: suite.name, groups };
25
27
  }
26
28
 
27
- /** Create regex from filter (literal unless regex-like) */
29
+ /** Create regex from filter string. Uses literal prefix match unless the string looks like regex. */
28
30
  function createFilterRegex(filter: string): RegExp {
31
+ const isSlashed = filter.startsWith("/") && filter.endsWith("/");
29
32
  const looksLikeRegex =
30
- (filter.startsWith("/") && filter.endsWith("/")) ||
31
- filter.includes("*") ||
32
- filter.includes("?") ||
33
- filter.includes("[") ||
34
- filter.includes("|") ||
33
+ isSlashed ||
34
+ /[*?[|]/.test(filter) ||
35
35
  filter.startsWith("^") ||
36
36
  filter.endsWith("$");
37
37
 
38
- if (looksLikeRegex) {
39
- const pattern =
40
- filter.startsWith("/") && filter.endsWith("/")
41
- ? filter.slice(1, -1)
42
- : filter;
43
- try {
44
- return new RegExp(pattern, "i");
45
- } catch {
46
- return new RegExp(escapeRegex(filter), "i");
47
- }
48
- }
38
+ if (!looksLikeRegex) return new RegExp("^" + escapeRegex(filter), "i");
49
39
 
50
- return new RegExp("^" + escapeRegex(filter), "i");
40
+ const pattern = isSlashed ? filter.slice(1, -1) : filter;
41
+ try {
42
+ return new RegExp(pattern, "i");
43
+ } catch {
44
+ return new RegExp(escapeRegex(filter), "i");
45
+ }
51
46
  }
52
47
 
53
- /** Strip case suffix like " [large]" from benchmark name for filtering */
48
+ /** Strip case suffix like " [large]" from benchmark name for filtering. */
54
49
  function stripCaseSuffix(name: string): string {
55
50
  return name.replace(/ \[.*?\]$/, "");
56
51
  }
57
52
 
58
- /** Escape regex special characters */
53
+ /** Escape special regex characters for literal matching. */
59
54
  function escapeRegex(str: string): string {
60
55
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
61
56
  }
62
-
63
- /** Ensure at least one benchmark matches filter */
64
- function validateFilteredSuite(groups: BenchGroup[], filter?: string): void {
65
- if (groups.every(g => g.benchmarks.length === 0)) {
66
- throw new Error(`No benchmarks match filter: "${filter}"`);
67
- }
68
- }