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.
- package/LICENSE +20 -0
- package/README.md +99 -294
- package/bin/benchforge +1 -2
- package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
- package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
- package/dist/{BenchRunner-BzyUfiyB.d.mts → BenchRunner-DglX1NOn.d.mts} +119 -66
- package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
- package/dist/Formatters-BWj3d4sv.mjs +95 -0
- package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
- package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
- package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
- package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
- package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
- package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
- package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
- package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
- package/dist/bin/benchforge.mjs +4 -5
- package/dist/bin/benchforge.mjs.map +1 -1
- package/dist/index.d.mts +711 -558
- package/dist/index.mjs +98 -3
- package/dist/index.mjs.map +1 -0
- package/dist/runners/WorkerScript.d.mts +12 -4
- package/dist/runners/WorkerScript.mjs +77 -105
- package/dist/runners/WorkerScript.mjs.map +1 -1
- package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
- package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
- package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
- package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
- package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
- package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
- package/dist/viewer/index.html +19 -0
- package/dist/viewer/speedscope/LICENSE +21 -0
- package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
- package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
- package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
- package/dist/viewer/speedscope/file-format-schema.json +274 -0
- package/dist/viewer/speedscope/index.html +19 -0
- package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
- package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
- package/dist/viewer/speedscope/release.txt +3 -0
- package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
- package/package.json +52 -27
- package/src/bin/benchforge.ts +2 -2
- package/src/cli/AnalyzeArchive.ts +232 -0
- package/src/cli/BrowserBench.ts +322 -0
- package/src/cli/CliArgs.ts +164 -51
- package/src/cli/CliExport.ts +179 -0
- package/src/cli/CliOptions.ts +147 -0
- package/src/cli/CliReport.ts +197 -0
- package/src/cli/FilterBenchmarks.ts +18 -30
- package/src/cli/RunBenchCLI.ts +132 -866
- package/src/cli/SuiteRunner.ts +160 -0
- package/src/cli/ViewerServer.ts +282 -0
- package/src/export/AllocExport.ts +121 -0
- package/src/export/ArchiveExport.ts +146 -0
- package/src/export/ArchiveFormat.ts +50 -0
- package/src/export/CoverageExport.ts +148 -0
- package/src/export/EditorUri.ts +10 -0
- package/src/export/PerfettoExport.ts +64 -99
- package/src/export/SpeedscopeTypes.ts +98 -0
- package/src/export/TimeExport.ts +115 -0
- package/src/index.ts +86 -67
- package/src/matrix/BenchMatrix.ts +230 -0
- package/src/matrix/CaseLoader.ts +8 -6
- package/src/matrix/MatrixDirRunner.ts +153 -0
- package/src/matrix/MatrixFilter.ts +49 -47
- package/src/matrix/MatrixInlineRunner.ts +50 -0
- package/src/matrix/MatrixReport.ts +90 -250
- package/src/matrix/VariantLoader.ts +5 -5
- package/src/profiling/browser/BenchLoop.ts +51 -0
- package/src/profiling/browser/BrowserCDP.ts +133 -0
- package/src/profiling/browser/BrowserGcStats.ts +33 -0
- package/src/profiling/browser/BrowserProfiler.ts +160 -0
- package/src/profiling/browser/CdpClient.ts +82 -0
- package/src/profiling/browser/CdpPage.ts +138 -0
- package/src/profiling/browser/ChromeLauncher.ts +158 -0
- package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
- package/src/profiling/browser/PageLoadMode.ts +61 -0
- package/src/profiling/node/CoverageSampler.ts +27 -0
- package/src/profiling/node/CoverageTypes.ts +23 -0
- package/src/profiling/node/HeapSampleReport.ts +261 -0
- package/src/{heap-sample → profiling/node}/HeapSampler.ts +1 -2
- package/src/{heap-sample → profiling/node}/ResolvedProfile.ts +18 -9
- package/src/profiling/node/TimeSampler.ts +57 -0
- package/src/report/BenchmarkReport.ts +146 -0
- package/src/report/Colors.ts +9 -0
- package/src/report/Formatters.ts +110 -0
- package/src/report/GcSections.ts +151 -0
- package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
- package/src/report/HtmlReport.ts +223 -0
- package/src/report/ParseStats.ts +73 -0
- package/src/report/StandardSections.ts +147 -0
- package/src/report/ViewerSections.ts +286 -0
- package/src/report/text/TableReport.ts +253 -0
- package/src/report/text/TextReport.ts +123 -0
- package/src/runners/AdaptiveWrapper.ts +116 -236
- package/src/runners/BenchRunner.ts +20 -15
- package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
- package/src/runners/CreateRunner.ts +5 -7
- package/src/runners/GcStats.ts +47 -50
- package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
- package/src/runners/MergeBatches.ts +123 -0
- package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
- package/src/runners/RunnerOrchestrator.ts +127 -243
- package/src/runners/RunnerUtils.ts +75 -1
- package/src/runners/SampleStats.ts +100 -0
- package/src/runners/TimingRunner.ts +244 -0
- package/src/runners/TimingUtils.ts +3 -2
- package/src/runners/WorkerScript.ts +135 -151
- package/src/stats/BootstrapDifference.ts +282 -0
- package/src/{PermutationTest.ts → stats/PermutationTest.ts} +8 -17
- package/src/stats/StatisticalUtils.ts +445 -0
- package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
- package/src/test/AdaptiveRunner.test.ts +39 -41
- package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
- package/src/test/AdaptiveStatistics.integration.ts +2 -2
- package/src/{tests → test}/BenchMatrix.test.ts +19 -16
- package/src/test/BenchmarkReport.test.ts +63 -13
- package/src/test/BrowserBench.e2e.test.ts +186 -17
- package/src/test/BrowserBench.test.ts +10 -5
- package/src/test/BuildTimeSection.test.ts +130 -0
- package/src/test/CapSamples.test.ts +82 -0
- package/src/test/CoverageExport.test.ts +115 -0
- package/src/test/CoverageSampler.test.ts +33 -0
- package/src/test/HeapAttribution.test.ts +14 -14
- package/src/{tests → test}/MatrixFilter.test.ts +1 -1
- package/src/{tests → test}/MatrixReport.test.ts +1 -1
- package/src/test/PermutationTest.test.ts +1 -1
- package/src/{tests → test}/RealDataValidation.test.ts +6 -6
- package/src/test/RunBenchCLI.test.ts +39 -38
- package/src/test/RunnerOrchestrator.test.ts +12 -12
- package/src/test/StatisticalUtils.test.ts +48 -12
- package/src/{table-util/test → test}/TableReport.test.ts +2 -2
- package/src/test/TestUtils.ts +12 -7
- package/src/test/TimeExport.test.ts +139 -0
- package/src/test/TimeSampler.test.ts +37 -0
- package/src/test/ViewerLive.e2e.test.ts +159 -0
- package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
- package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
- package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
- package/src/test/fixtures/cases/asyncCases.ts +9 -0
- package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
- package/src/test/fixtures/cases/variants/product.ts +2 -0
- package/src/test/fixtures/cases/variants/sum.ts +2 -0
- package/src/test/fixtures/discover/fast.ts +1 -0
- package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
- package/src/test/fixtures/invalid/bad.ts +1 -0
- package/src/test/fixtures/loader/fast.ts +1 -0
- package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
- package/src/test/fixtures/loader/stateful.ts +2 -0
- package/src/test/fixtures/stateful/stateful.ts +2 -0
- package/src/test/fixtures/variants/extra.ts +1 -0
- package/src/test/fixtures/variants/impl.ts +1 -0
- package/src/test/fixtures/worker/fast.ts +1 -0
- package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
- package/src/viewer/DateFormat.ts +30 -0
- package/src/viewer/Helpers.ts +23 -0
- package/src/viewer/LineData.ts +120 -0
- package/src/viewer/Providers.ts +191 -0
- package/src/viewer/ReportData.ts +123 -0
- package/src/viewer/State.ts +49 -0
- package/src/viewer/Theme.ts +15 -0
- package/src/viewer/components/App.tsx +73 -0
- package/src/viewer/components/DropZone.tsx +71 -0
- package/src/viewer/components/LazyPlot.ts +33 -0
- package/src/viewer/components/SamplesPanel.tsx +214 -0
- package/src/viewer/components/Shell.tsx +26 -0
- package/src/viewer/components/SourcePanel.tsx +216 -0
- package/src/viewer/components/SummaryPanel.tsx +332 -0
- package/src/viewer/components/TabBar.tsx +131 -0
- package/src/viewer/components/TabContent.tsx +46 -0
- package/src/viewer/components/ThemeToggle.tsx +50 -0
- package/src/viewer/index.html +20 -0
- package/src/viewer/main.tsx +4 -0
- package/src/viewer/plots/CIPlot.ts +313 -0
- package/src/{html/browser → viewer/plots}/HistogramKde.ts +33 -38
- package/src/viewer/plots/LegendUtils.ts +134 -0
- package/src/viewer/plots/PlotTypes.ts +85 -0
- package/src/viewer/plots/RenderPlots.ts +230 -0
- package/src/viewer/plots/SampleTimeSeries.ts +306 -0
- package/src/viewer/plots/SvgHelpers.ts +136 -0
- package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
- package/src/viewer/report.css +427 -0
- package/src/viewer/shell.css +357 -0
- package/src/viewer/tsconfig.json +11 -0
- package/dist/BrowserHeapSampler-B6asLKWQ.mjs +0 -202
- package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +0 -1
- package/dist/GcStats-wX7Xyblu.mjs +0 -77
- package/dist/GcStats-wX7Xyblu.mjs.map +0 -1
- package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
- package/dist/TimingUtils-DwOwkc8G.mjs +0 -597
- package/dist/TimingUtils-DwOwkc8G.mjs.map +0 -1
- package/dist/browser/index.js +0 -914
- package/dist/src-B-DDaCa9.mjs +0 -3108
- package/dist/src-B-DDaCa9.mjs.map +0 -1
- package/src/BenchMatrix.ts +0 -380
- package/src/BenchmarkReport.ts +0 -161
- package/src/HtmlDataPrep.ts +0 -148
- package/src/StandardSections.ts +0 -261
- package/src/StatisticalUtils.ts +0 -175
- package/src/TypeUtil.ts +0 -8
- package/src/browser/BrowserGcStats.ts +0 -44
- package/src/browser/BrowserHeapSampler.ts +0 -271
- package/src/export/JsonExport.ts +0 -103
- package/src/export/JsonFormat.ts +0 -91
- package/src/export/SpeedscopeExport.ts +0 -202
- package/src/heap-sample/HeapSampleReport.ts +0 -269
- package/src/html/HtmlReport.ts +0 -131
- package/src/html/HtmlTemplate.ts +0 -284
- package/src/html/Types.ts +0 -88
- package/src/html/browser/CIPlot.ts +0 -287
- package/src/html/browser/LegendUtils.ts +0 -163
- package/src/html/browser/RenderPlots.ts +0 -263
- package/src/html/browser/SampleTimeSeries.ts +0 -389
- package/src/html/browser/Types.ts +0 -96
- package/src/html/browser/index.ts +0 -1
- package/src/html/index.ts +0 -17
- package/src/runners/BasicRunner.ts +0 -364
- package/src/table-util/ConvergenceFormatters.ts +0 -19
- package/src/table-util/Formatters.ts +0 -157
- package/src/table-util/README.md +0 -70
- package/src/table-util/TableReport.ts +0 -293
- package/src/tests/fixtures/cases/asyncCases.ts +0 -7
- package/src/tests/fixtures/cases/variants/product.ts +0 -2
- package/src/tests/fixtures/cases/variants/sum.ts +0 -2
- package/src/tests/fixtures/discover/fast.ts +0 -1
- package/src/tests/fixtures/invalid/bad.ts +0 -1
- package/src/tests/fixtures/loader/fast.ts +0 -1
- package/src/tests/fixtures/loader/stateful.ts +0 -2
- package/src/tests/fixtures/stateful/stateful.ts +0 -2
- package/src/tests/fixtures/variants/extra.ts +0 -1
- package/src/tests/fixtures/variants/impl.ts +0 -1
- package/src/tests/fixtures/worker/fast.ts +0 -1
- /package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
- /package/src/{table-util/test → test}/TableValueExtractor.ts +0 -0
package/src/table-util/README.md
DELETED
|
@@ -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 +0,0 @@
|
|
|
1
|
-
export const run = () => {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const notRun = () => {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const run = () => {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const run = () => {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const run = () => {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const run = () => {};
|
|
File without changes
|
|
File without changes
|