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
@@ -1,70 +0,0 @@
1
- # TableReport
2
-
3
- Utilities for creating formatted text-based tables.
4
- Under the hood, TableReport uses the npm [table](https://www.npmjs.com/package/table) library.
5
-
6
- ### Features
7
-
8
- * **Column Grouping:** Group related columns under a common header.
9
- * **Difference Columns:** Automatically generate columns that show the percentage difference between a value and a baseline value.
10
- * **Custom Formatting:** Declaratively provide custom formatters for columns.
11
- * **Simplified Configuration:** A higher-level API for the `table` library.
12
-
13
- ## `TableReport.ts`
14
-
15
- The `buildTable` function in `TableReport.ts` is the main entry point for creating a table.
16
- It takes a configuration object for columns and an array of data records for rows.
17
-
18
- ### Example
19
-
20
- Here's a simplified example of how to use `buildTable`:
21
-
22
- ```typescript
23
- import { buildTable, ColumnGroup } from './TableReport';
24
- import { integer, floatPrecision } from './Formatters';
25
-
26
- interface MyData {
27
- name: string;
28
- value: number;
29
- score: number;
30
- }
31
-
32
- const data: MyData[] = [
33
- { name: 'test A', value: 123, score: 45.6 },
34
- { name: 'test B', value: 456, score: 78.9 },
35
- ];
36
-
37
- const baselineData: MyData[] = [
38
- { name: 'test A', value: 100, score: 50.0 },
39
- { name: 'test B', value: 500, score: 75.0 },
40
- ];
41
-
42
- const tableConfig: ColumnGroup<MyData>[] = [
43
- {
44
- columns: [{ key: 'name', title: 'Name' }],
45
- },
46
- {
47
- groupTitle: 'Metrics',
48
- columns: [
49
- { key: 'value', title: 'Value', formatter: integer },
50
- { key: 'value_diff', title: 'Δ%', diffKey: 'value' },
51
- { key: 'score', title: 'Score', formatter: floatPrecision(1) },
52
- { key: 'score_diff', title: 'Δ%', diffKey: 'score' },
53
- ],
54
- },
55
- ];
56
-
57
- const table = buildTable(tableConfig, data, baselineData);
58
- console.log(table);
59
- ```
60
-
61
- For a more complex example, see `BenchmarkReport.ts`, specifically the `mostlyFullRow` and `tableConfig` variables.
62
-
63
- ## `Formatters.ts`
64
-
65
- This file contains various utility functions for formatting numbers and strings, such as:
66
-
67
- * `float`, `integer`: Format numbers to a specific precision.
68
- * `percent`: Format a number as a percentage.
69
- * `diffPercent`, `diffPercentNegative`: Format the percentage difference between two numbers, with color-coding for positive/negative changes.
70
- * `bytes`, `duration`, `rate`: Format numbers with appropriate units.
@@ -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
- /** Related table columns */
7
- export interface ColumnGroup<T> {
8
- groupTitle?: string;
9
- columns: AnyColumn<T>[];
10
- }
11
-
12
- export type AnyColumn<T> = Column<T> | DiffColumn<T>;
13
-
14
- /** Column with optional formatter */
15
- export interface Column<T> extends ColumnFormat<T> {
16
- formatter?: (value: unknown) => string | null;
17
- diffKey?: undefined;
18
- }
19
-
20
- /** Table headers and configuration */
21
- export interface TableSetup {
22
- headerRows: string[][];
23
- config: TableUserConfig;
24
- }
25
-
26
- /** Data rows with optional baseline */
27
- export interface ResultGroup<T extends Record<string, any>> {
28
- results: T[];
29
-
30
- baseline?: T;
31
- }
32
-
33
- /** Comparison column against baseline */
34
- interface DiffColumn<T> extends ColumnFormat<T> {
35
- diffFormatter?: (value: unknown, baseline: unknown) => string | null;
36
- formatter?: undefined;
37
-
38
- /** Key for comparison value against baseline */
39
- diffKey: keyof T;
40
- }
41
-
42
- interface ColumnFormat<T> {
43
- key: keyof T;
44
- title: string;
45
-
46
- alignment?: Alignment;
47
-
48
- width?: number;
49
- }
50
-
51
- interface Lines {
52
- drawHorizontalLine: (index: number, size: number) => boolean;
53
- drawVerticalLine: (index: number, size: number) => boolean;
54
- }
55
-
56
- const isTest = process.env.NODE_ENV === "test" || process.env.VITEST === "true";
57
- const { bold } = isTest ? { bold: (str: string) => str } : pico;
58
-
59
- // Regex to strip ANSI escape codes (ESC [ ... m sequences)
60
- const ansiEscapeRegex = new RegExp(
61
- String.fromCharCode(27) + "\\[[0-9;]*m",
62
- "g",
63
- );
64
-
65
- /** Build formatted table with column groups and baselines */
66
- export function buildTable<T extends Record<string, any>>(
67
- columnGroups: ColumnGroup<T>[],
68
- resultGroups: ResultGroup<T>[],
69
- nameKey: keyof T = "name" as keyof T,
70
- ): string {
71
- const allRecords = flattenGroups(columnGroups, resultGroups, nameKey);
72
- return createTable(columnGroups, allRecords);
73
- }
74
-
75
- /** Convert records to string arrays for table */
76
- export function toRows<T extends Record<string, any>>(
77
- records: T[],
78
- groups: ColumnGroup<T>[],
79
- ): string[][] {
80
- const allColumns = groups.flatMap(group => group.columns);
81
-
82
- const rawRows = records.map(record =>
83
- allColumns.map(col => {
84
- const value = record[col.key];
85
- return col.formatter ? col.formatter(value) : value;
86
- }),
87
- );
88
-
89
- return rawRows.map(row => row.map(cell => cell ?? " "));
90
- }
91
-
92
- /** Flatten groups with spacing */
93
- function flattenGroups<T extends Record<string, any>>(
94
- columnGroups: ColumnGroup<T>[],
95
- resultGroups: ResultGroup<T>[],
96
- nameKey: keyof T,
97
- ): T[] {
98
- return resultGroups.flatMap((group, i) => {
99
- const groupRecords = addBaseline(columnGroups, group, nameKey);
100
-
101
- const isLast = i === resultGroups.length - 1;
102
- return isLast ? groupRecords : [...groupRecords, {} as T];
103
- });
104
- }
105
-
106
- /** Convert columns and records to formatted table */
107
- function createTable<T extends Record<string, any>>(
108
- groups: ColumnGroup<T>[],
109
- records: T[],
110
- ): string {
111
- const dataRows = toRows(records, groups);
112
- const { headerRows, config } = setup(groups, dataRows);
113
- const allRows = [...headerRows, ...dataRows];
114
- return table(allRows, config);
115
- }
116
-
117
- /** Process results with baseline comparisons */
118
- function addBaseline<T extends Record<string, any>>(
119
- columnGroups: ColumnGroup<T>[],
120
- group: ResultGroup<T>,
121
- nameKey: keyof T,
122
- ): T[] {
123
- const { results, baseline } = group;
124
-
125
- if (!baseline) return results;
126
-
127
- const diffResults = results.map(result =>
128
- addComparisons(columnGroups, result, baseline),
129
- );
130
-
131
- const markedBaseline = {
132
- ...baseline,
133
- [nameKey]: `--> ${baseline[nameKey]}`,
134
- };
135
-
136
- return [...diffResults, markedBaseline];
137
- }
138
-
139
- /** Create headers and table configuration */
140
- function setup<T>(groups: ColumnGroup<T>[], dataRows: string[][]): TableSetup {
141
- const titles = getTitles(groups);
142
- const numColumns = titles.length;
143
-
144
- const sectionRows = createGroupHeaders(groups, numColumns);
145
- const headerRows = [...sectionRows, titles];
146
- const spanningCells = createSectionSpans(groups);
147
- const columnWidths = calcColumnWidths(groups, titles, dataRows);
148
- const config: TableUserConfig = {
149
- spanningCells,
150
- columns: columnWidths,
151
- ...createLines(groups),
152
- };
153
-
154
- return { headerRows, config };
155
- }
156
-
157
- /** Add comparison values for diff columns */
158
- function addComparisons<T extends Record<string, any>>(
159
- groups: ColumnGroup<T>[],
160
- mainRecord: T,
161
- baselineRecord: T,
162
- ): T {
163
- const diffColumns = groups.flatMap(g => g.columns).filter(col => col.diffKey);
164
- const updatedMain = { ...mainRecord };
165
-
166
- for (const col of diffColumns) {
167
- const dcol = col as DiffColumn<T>;
168
- const diffKey = dcol.diffKey;
169
- const mainValue = mainRecord[diffKey];
170
- const baselineValue = baselineRecord[diffKey];
171
- const diffFormat = dcol.diffFormatter ?? diffPercent;
172
- const diffStr = diffFormat(mainValue, baselineValue);
173
- (updatedMain as any)[col.key] = diffStr;
174
- }
175
-
176
- return updatedMain;
177
- }
178
-
179
- /** @return bolded column title strings */
180
- function getTitles<T>(groups: ColumnGroup<T>[]): string[] {
181
- return groups.flatMap(g => g.columns.map(c => bold(c.title || " ")));
182
- }
183
-
184
- /** Create header rows with group titles */
185
- function createGroupHeaders<T>(
186
- groups: ColumnGroup<T>[],
187
- numColumns: number,
188
- ): string[][] {
189
- if (!groups.some(g => g.groupTitle)) return [];
190
-
191
- const sectionRow = groups.flatMap(g => {
192
- const title = g.groupTitle ? [bold(g.groupTitle)] : [];
193
- return padWithBlanks(title, g.columns.length);
194
- });
195
- const blankRow = padWithBlanks([], numColumns);
196
- return [sectionRow, blankRow];
197
- }
198
-
199
- /** @return spanning cell configs for group title headers */
200
- function createSectionSpans<T>(groups: ColumnGroup<T>[]): SpanningCellConfig[] {
201
- let col = 0;
202
- const alignment: Alignment = "center";
203
- return groups.map(g => {
204
- const colSpan = g.columns.length;
205
- const span = { row: 0, col, colSpan, alignment };
206
- col += colSpan;
207
- return span;
208
- });
209
- }
210
-
211
- /** Calculate column widths based on content, including group titles */
212
- function calcColumnWidths<T>(
213
- groups: ColumnGroup<T>[],
214
- titles: string[],
215
- dataRows: unknown[][],
216
- ): Record<number, { width: number; wrapWord: boolean }> {
217
- // First pass: calculate base widths from titles and data
218
- const widths: number[] = [];
219
- for (let i = 0; i < titles.length; i++) {
220
- const titleW = cellWidth(titles[i]);
221
- const maxDataW = dataRows.reduce(
222
- (max, row) => Math.max(max, cellWidth(row[i])),
223
- 0,
224
- );
225
- widths.push(Math.max(titleW, maxDataW));
226
- }
227
-
228
- // Second pass: ensure group titles fit (accounting for column separators)
229
- let colIndex = 0;
230
- for (const group of groups) {
231
- const groupW = cellWidth(group.groupTitle);
232
- if (groupW > 0) {
233
- const numCols = group.columns.length;
234
- const separatorWidth = (numCols - 1) * 3; // " | " between columns
235
- const currentWidth = widths
236
- .slice(colIndex, colIndex + numCols)
237
- .reduce((a, b) => a + b, 0);
238
- const needed = groupW - currentWidth - separatorWidth;
239
- if (needed > 0) {
240
- // Distribute extra width to last column in group
241
- widths[colIndex + numCols - 1] += needed;
242
- }
243
- }
244
- colIndex += group.columns.length;
245
- }
246
-
247
- // Convert to table config format
248
- return Object.fromEntries(
249
- widths.map((w, i) => [i, { width: w, wrapWord: false }]),
250
- );
251
- }
252
-
253
- /** @return draw functions for horizontal/vertical table borders */
254
- function createLines<T>(groups: ColumnGroup<T>[]): Lines {
255
- const { sectionBorders, headerBottom } = calcBorders(groups);
256
-
257
- function drawVerticalLine(index: number, size: number): boolean {
258
- return index === 0 || index === size || sectionBorders.includes(index);
259
- }
260
- function drawHorizontalLine(index: number, size: number): boolean {
261
- return index === 0 || index === size || index === headerBottom;
262
- }
263
- return { drawHorizontalLine, drawVerticalLine };
264
- }
265
-
266
- /** @return array padded with blank strings to the given length */
267
- function padWithBlanks(arr: string[], length: number): string[] {
268
- if (arr.length >= length) return arr;
269
- return [...arr, ...Array(length - arr.length).fill(" ")];
270
- }
271
-
272
- /** Get visible length of a cell value (strips ANSI escape codes) */
273
- function cellWidth(value: unknown): number {
274
- if (value == null) return 0;
275
- const str = String(value);
276
- return str.replace(ansiEscapeRegex, "").length;
277
- }
278
-
279
- /** Calculate vertical lines between sections and header bottom position */
280
- function calcBorders<T>(groups: ColumnGroup<T>[]): {
281
- sectionBorders: number[];
282
- headerBottom: number;
283
- } {
284
- if (groups.length === 0) return { sectionBorders: [], headerBottom: 1 };
285
-
286
- const sectionBorders: number[] = [];
287
- let border = 0;
288
- for (const g of groups) {
289
- border += g.columns.length;
290
- sectionBorders.push(border);
291
- }
292
- return { sectionBorders, headerBottom: 3 };
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 = () => {};