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
@@ -1,293 +0,0 @@
1
- import pico from "picocolors";
2
- import type { Alignment, SpanningCellConfig, TableUserConfig } from "table";
3
- import { table } from "table";
4
- import { diffPercent } from "./Formatters.ts";
5
-
6
- const isTest = process.env.NODE_ENV === "test" || process.env.VITEST === "true";
7
- const { bold } = isTest ? { bold: (str: string) => str } : pico;
8
-
9
- /** Related table columns */
10
- export interface ColumnGroup<T> {
11
- groupTitle?: string;
12
- columns: AnyColumn<T>[];
13
- }
14
-
15
- export type AnyColumn<T> = Column<T> | DiffColumn<T>;
16
-
17
- /** Column with optional formatter */
18
- export interface Column<T> extends ColumnFormat<T> {
19
- formatter?: (value: unknown) => string | null;
20
- diffKey?: undefined;
21
- }
22
-
23
- /** Comparison column against baseline */
24
- interface DiffColumn<T> extends ColumnFormat<T> {
25
- diffFormatter?: (value: unknown, baseline: unknown) => string | null;
26
- formatter?: undefined;
27
-
28
- /** Key for comparison value against baseline */
29
- diffKey: keyof T;
30
- }
31
-
32
- interface ColumnFormat<T> {
33
- key: keyof T;
34
- title: string;
35
-
36
- alignment?: Alignment;
37
-
38
- width?: number;
39
- }
40
-
41
- /** Table headers and configuration */
42
- export interface TableSetup {
43
- headerRows: string[][];
44
- config: TableUserConfig;
45
- }
46
-
47
- /** Data rows with optional baseline */
48
- export interface ResultGroup<T extends Record<string, any>> {
49
- results: T[];
50
-
51
- baseline?: T;
52
- }
53
-
54
- /** Build formatted table with column groups and baselines */
55
- export function buildTable<T extends Record<string, any>>(
56
- columnGroups: ColumnGroup<T>[],
57
- resultGroups: ResultGroup<T>[],
58
- nameKey: keyof T = "name" as keyof T,
59
- ): string {
60
- const allRecords = flattenGroups(columnGroups, resultGroups, nameKey);
61
- return createTable(columnGroups, allRecords);
62
- }
63
-
64
- /** Convert columns and records to formatted table */
65
- function createTable<T extends Record<string, any>>(
66
- groups: ColumnGroup<T>[],
67
- records: T[],
68
- ): string {
69
- const dataRows = toRows(records, groups);
70
- const { headerRows, config } = setup(groups, dataRows);
71
- const allRows = [...headerRows, ...dataRows];
72
- return table(allRows, config);
73
- }
74
-
75
- /** Create header rows with group titles */
76
- function createGroupHeaders<T>(
77
- groups: ColumnGroup<T>[],
78
- numColumns: number,
79
- ): string[][] {
80
- if (!groups.some(g => g.groupTitle)) return [];
81
-
82
- const sectionRow = groups.flatMap(g => {
83
- const title = g.groupTitle ? [bold(g.groupTitle)] : [];
84
- return padWithBlanks(title, g.columns.length);
85
- });
86
- const blankRow = padWithBlanks([], numColumns);
87
- return [sectionRow, blankRow];
88
- }
89
-
90
- interface Lines {
91
- drawHorizontalLine: (index: number, size: number) => boolean;
92
- drawVerticalLine: (index: number, size: number) => boolean;
93
- }
94
-
95
- /** @return draw functions for horizontal/vertical table borders */
96
- function createLines<T>(groups: ColumnGroup<T>[]): Lines {
97
- const { sectionBorders, headerBottom } = calcBorders(groups);
98
-
99
- function drawVerticalLine(index: number, size: number): boolean {
100
- return index === 0 || index === size || sectionBorders.includes(index);
101
- }
102
- function drawHorizontalLine(index: number, size: number): boolean {
103
- return index === 0 || index === size || index === headerBottom;
104
- }
105
- return { drawHorizontalLine, drawVerticalLine };
106
- }
107
-
108
- /** @return spanning cell configs for group title headers */
109
- function createSectionSpans<T>(groups: ColumnGroup<T>[]): SpanningCellConfig[] {
110
- let col = 0;
111
- const alignment: Alignment = "center";
112
- return groups.map(g => {
113
- const colSpan = g.columns.length;
114
- const span = { row: 0, col, colSpan, alignment };
115
- col += colSpan;
116
- return span;
117
- });
118
- }
119
-
120
- /** @return bolded column title strings */
121
- function getTitles<T>(groups: ColumnGroup<T>[]): string[] {
122
- return groups.flatMap(g => g.columns.map(c => bold(c.title || " ")));
123
- }
124
-
125
- /** @return array padded with blank strings to the given length */
126
- function padWithBlanks(arr: string[], length: number): string[] {
127
- if (arr.length >= length) return arr;
128
- return [...arr, ...Array(length - arr.length).fill(" ")];
129
- }
130
-
131
- /** Convert records to string arrays for table */
132
- export function toRows<T extends Record<string, any>>(
133
- records: T[],
134
- groups: ColumnGroup<T>[],
135
- ): string[][] {
136
- const allColumns = groups.flatMap(group => group.columns);
137
-
138
- const rawRows = records.map(record =>
139
- allColumns.map(col => {
140
- const value = record[col.key];
141
- return col.formatter ? col.formatter(value) : value;
142
- }),
143
- );
144
-
145
- return rawRows.map(row => row.map(cell => cell ?? " "));
146
- }
147
-
148
- /** Add comparison values for diff columns */
149
- function addComparisons<T extends Record<string, any>>(
150
- groups: ColumnGroup<T>[],
151
- mainRecord: T,
152
- baselineRecord: T,
153
- ): T {
154
- const diffColumns = groups.flatMap(g => g.columns).filter(col => col.diffKey);
155
- const updatedMain = { ...mainRecord };
156
-
157
- for (const col of diffColumns) {
158
- const dcol = col as DiffColumn<T>;
159
- const diffKey = dcol.diffKey;
160
- const mainValue = mainRecord[diffKey];
161
- const baselineValue = baselineRecord[diffKey];
162
- const diffFormat = dcol.diffFormatter ?? diffPercent;
163
- const diffStr = diffFormat(mainValue, baselineValue);
164
- (updatedMain as any)[col.key] = diffStr;
165
- }
166
-
167
- return updatedMain;
168
- }
169
-
170
- /** Flatten groups with spacing */
171
- function flattenGroups<T extends Record<string, any>>(
172
- columnGroups: ColumnGroup<T>[],
173
- resultGroups: ResultGroup<T>[],
174
- nameKey: keyof T,
175
- ): T[] {
176
- return resultGroups.flatMap((group, i) => {
177
- const groupRecords = addBaseline(columnGroups, group, nameKey);
178
-
179
- const isLast = i === resultGroups.length - 1;
180
- return isLast ? groupRecords : [...groupRecords, {} as T];
181
- });
182
- }
183
-
184
- /** Process results with baseline comparisons */
185
- function addBaseline<T extends Record<string, any>>(
186
- columnGroups: ColumnGroup<T>[],
187
- group: ResultGroup<T>,
188
- nameKey: keyof T,
189
- ): T[] {
190
- const { results, baseline } = group;
191
-
192
- if (!baseline) return results;
193
-
194
- const diffResults = results.map(result =>
195
- addComparisons(columnGroups, result, baseline),
196
- );
197
-
198
- const markedBaseline = {
199
- ...baseline,
200
- [nameKey]: `--> ${baseline[nameKey]}`,
201
- };
202
-
203
- return [...diffResults, markedBaseline];
204
- }
205
-
206
- /** Calculate vertical lines between sections and header bottom position */
207
- function calcBorders<T>(groups: ColumnGroup<T>[]): {
208
- sectionBorders: number[];
209
- headerBottom: number;
210
- } {
211
- if (groups.length === 0) return { sectionBorders: [], headerBottom: 1 };
212
-
213
- const sectionBorders: number[] = [];
214
- let border = 0;
215
- for (const g of groups) {
216
- border += g.columns.length;
217
- sectionBorders.push(border);
218
- }
219
- return { sectionBorders, headerBottom: 3 };
220
- }
221
-
222
- /** Create headers and table configuration */
223
- function setup<T>(groups: ColumnGroup<T>[], dataRows: string[][]): TableSetup {
224
- const titles = getTitles(groups);
225
- const numColumns = titles.length;
226
-
227
- const sectionRows = createGroupHeaders(groups, numColumns);
228
- const headerRows = [...sectionRows, titles];
229
- const spanningCells = createSectionSpans(groups);
230
- const columnWidths = calcColumnWidths(groups, titles, dataRows);
231
- const config: TableUserConfig = {
232
- spanningCells,
233
- columns: columnWidths,
234
- ...createLines(groups),
235
- };
236
-
237
- return { headerRows, config };
238
- }
239
-
240
- /** Calculate column widths based on content, including group titles */
241
- function calcColumnWidths<T>(
242
- groups: ColumnGroup<T>[],
243
- titles: string[],
244
- dataRows: unknown[][],
245
- ): Record<number, { width: number; wrapWord: boolean }> {
246
- // First pass: calculate base widths from titles and data
247
- const widths: number[] = [];
248
- for (let i = 0; i < titles.length; i++) {
249
- const titleW = cellWidth(titles[i]);
250
- const maxDataW = dataRows.reduce(
251
- (max, row) => Math.max(max, cellWidth(row[i])),
252
- 0,
253
- );
254
- widths.push(Math.max(titleW, maxDataW));
255
- }
256
-
257
- // Second pass: ensure group titles fit (accounting for column separators)
258
- let colIndex = 0;
259
- for (const group of groups) {
260
- const groupW = cellWidth(group.groupTitle);
261
- if (groupW > 0) {
262
- const numCols = group.columns.length;
263
- const separatorWidth = (numCols - 1) * 3; // " | " between columns
264
- const currentWidth = widths
265
- .slice(colIndex, colIndex + numCols)
266
- .reduce((a, b) => a + b, 0);
267
- const needed = groupW - currentWidth - separatorWidth;
268
- if (needed > 0) {
269
- // Distribute extra width to last column in group
270
- widths[colIndex + numCols - 1] += needed;
271
- }
272
- }
273
- colIndex += group.columns.length;
274
- }
275
-
276
- // Convert to table config format
277
- return Object.fromEntries(
278
- widths.map((w, i) => [i, { width: w, wrapWord: false }]),
279
- );
280
- }
281
-
282
- // Regex to strip ANSI escape codes (ESC [ ... m sequences)
283
- const ansiEscapeRegex = new RegExp(
284
- String.fromCharCode(27) + "\\[[0-9;]*m",
285
- "g",
286
- );
287
-
288
- /** Get visible length of a cell value (strips ANSI escape codes) */
289
- function cellWidth(value: unknown): number {
290
- if (value == null) return 0;
291
- const str = String(value);
292
- return str.replace(ansiEscapeRegex, "").length;
293
- }
@@ -1,7 +0,0 @@
1
- /** Test cases module with async loadCase */
2
- export const cases = ["alpha", "beta"];
3
-
4
- export async function loadCase(id: string) {
5
- await Promise.resolve(); // simulate async
6
- return { data: id.toUpperCase(), metadata: { original: id } };
7
- }
@@ -1,2 +0,0 @@
1
- /** Variant that multiplies array elements */
2
- export const run = (arr: number[]) => arr.reduce((a, b) => a * b, 1);
@@ -1,2 +0,0 @@
1
- /** Variant that sums array elements */
2
- export const run = (arr: number[]) => arr.reduce((a, b) => a + b, 0);
@@ -1 +0,0 @@
1
- export const run = () => {};
@@ -1 +0,0 @@
1
- export const notRun = () => {};
@@ -1 +0,0 @@
1
- export const run = () => {};
@@ -1,2 +0,0 @@
1
- export const setup = data => ({ value: data });
2
- export const run = state => state.value;
@@ -1,2 +0,0 @@
1
- export const setup = data => ({ value: data });
2
- export const run = state => state.value;
@@ -1 +0,0 @@
1
- export const run = () => {};
@@ -1 +0,0 @@
1
- export const run = () => {};
@@ -1 +0,0 @@
1
- export const run = () => {};
@@ -55,15 +55,6 @@ function getGroupColumnIndex(
55
55
  );
56
56
  }
57
57
 
58
- /** Count total columns in groups before the target group index. */
59
- function countColumnsBeforeGroup(
60
- headers: string[],
61
- groupIndex: number,
62
- ): number {
63
- const perGroup = headers.map(col => splitColumns(col).length);
64
- return perGroup.slice(0, groupIndex).reduce((sum, n) => sum + n, 0);
65
- }
66
-
67
58
  /** Find a column's position index in a header line. */
68
59
  function getColumnIndex(
69
60
  header: string | undefined,
@@ -91,6 +82,15 @@ function splitColumnGroups(line: string): string[] {
91
82
  return line.split("│").map(col => col.trim());
92
83
  }
93
84
 
85
+ /** Count total columns in groups before the target group index. */
86
+ function countColumnsBeforeGroup(
87
+ headers: string[],
88
+ groupIndex: number,
89
+ ): number {
90
+ const perGroup = headers.map(col => splitColumns(col).length);
91
+ return perGroup.slice(0, groupIndex).reduce((sum, n) => sum + n, 0);
92
+ }
93
+
94
94
  /** Split on 2+ whitespace or '│' borders, so single-space titles like "L1 miss" survive. */
95
95
  function splitColumns(line: string): string[] {
96
96
  return line