benchforge 0.1.11 → 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 -294
  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-BzyUfiyB.d.mts → BenchRunner-DglX1NOn.d.mts} +119 -66
  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 +711 -558
  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 +77 -105
  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 -27
  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 -51
  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 +132 -866
  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 +64 -99
  78. package/src/export/SpeedscopeTypes.ts +98 -0
  79. package/src/export/TimeExport.ts +115 -0
  80. package/src/index.ts +86 -67
  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 +49 -47
  85. package/src/matrix/MatrixInlineRunner.ts +50 -0
  86. package/src/matrix/MatrixReport.ts +90 -250
  87. package/src/matrix/VariantLoader.ts +5 -5
  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 +1 -2
  101. package/src/{heap-sample → profiling/node}/ResolvedProfile.ts +18 -9
  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 +116 -236
  115. package/src/runners/BenchRunner.ts +20 -15
  116. package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
  117. package/src/runners/CreateRunner.ts +5 -7
  118. package/src/runners/GcStats.ts +47 -50
  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 +127 -243
  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 +135 -151
  128. package/src/stats/BootstrapDifference.ts +282 -0
  129. package/src/{PermutationTest.ts → stats/PermutationTest.ts} +8 -17
  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 +2 -2
  135. package/src/{tests → test}/BenchMatrix.test.ts +19 -16
  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 +14 -14
  144. package/src/{tests → test}/MatrixFilter.test.ts +1 -1
  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 +39 -38
  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 +12 -7
  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 +33 -38
  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/BrowserHeapSampler-B6asLKWQ.mjs +0 -202
  205. package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +0 -1
  206. package/dist/GcStats-wX7Xyblu.mjs +0 -77
  207. package/dist/GcStats-wX7Xyblu.mjs.map +0 -1
  208. package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
  209. package/dist/TimingUtils-DwOwkc8G.mjs +0 -597
  210. package/dist/TimingUtils-DwOwkc8G.mjs.map +0 -1
  211. package/dist/browser/index.js +0 -914
  212. package/dist/src-B-DDaCa9.mjs +0 -3108
  213. package/dist/src-B-DDaCa9.mjs.map +0 -1
  214. package/src/BenchMatrix.ts +0 -380
  215. package/src/BenchmarkReport.ts +0 -161
  216. package/src/HtmlDataPrep.ts +0 -148
  217. package/src/StandardSections.ts +0 -261
  218. package/src/StatisticalUtils.ts +0 -175
  219. package/src/TypeUtil.ts +0 -8
  220. package/src/browser/BrowserGcStats.ts +0 -44
  221. package/src/browser/BrowserHeapSampler.ts +0 -271
  222. package/src/export/JsonExport.ts +0 -103
  223. package/src/export/JsonFormat.ts +0 -91
  224. package/src/export/SpeedscopeExport.ts +0 -202
  225. package/src/heap-sample/HeapSampleReport.ts +0 -269
  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 -157
  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 +0 -0
@@ -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
- /** Ensure at least one benchmark matches filter */
59
- function validateFilteredSuite(groups: BenchGroup[], filter?: string): void {
60
- if (groups.every(g => g.benchmarks.length === 0)) {
61
- throw new Error(`No benchmarks match filter: "${filter}"`);
62
- }
63
- }
64
-
65
- /** Escape regex special characters */
53
+ /** Escape special regex characters for literal matching. */
66
54
  function escapeRegex(str: string): string {
67
55
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68
56
  }