modestbench 0.0.1
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/CHANGELOG.md +45 -0
- package/LICENSE.md +55 -0
- package/README.md +699 -0
- package/dist/bootstrap.cjs +37 -0
- package/dist/bootstrap.cjs.map +1 -0
- package/dist/bootstrap.d.cts +17 -0
- package/dist/bootstrap.d.cts.map +1 -0
- package/dist/bootstrap.d.ts +17 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +33 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cli/commands/history.cjs +459 -0
- package/dist/cli/commands/history.cjs.map +1 -0
- package/dist/cli/commands/history.d.cts +34 -0
- package/dist/cli/commands/history.d.cts.map +1 -0
- package/dist/cli/commands/history.d.ts +34 -0
- package/dist/cli/commands/history.d.ts.map +1 -0
- package/dist/cli/commands/history.js +422 -0
- package/dist/cli/commands/history.js.map +1 -0
- package/dist/cli/commands/init.cjs +566 -0
- package/dist/cli/commands/init.cjs.map +1 -0
- package/dist/cli/commands/init.d.cts +26 -0
- package/dist/cli/commands/init.d.cts.map +1 -0
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +562 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/run.cjs +285 -0
- package/dist/cli/commands/run.cjs.map +1 -0
- package/dist/cli/commands/run.d.cts +37 -0
- package/dist/cli/commands/run.d.cts.map +1 -0
- package/dist/cli/commands/run.d.ts +37 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +248 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/index.cjs +523 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +58 -0
- package/dist/cli/index.d.cts.map +1 -0
- package/dist/cli/index.d.ts +58 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +515 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/manager.cjs +370 -0
- package/dist/config/manager.cjs.map +1 -0
- package/dist/config/manager.d.cts +46 -0
- package/dist/config/manager.d.cts.map +1 -0
- package/dist/config/manager.d.ts +46 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +333 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/schema.cjs +182 -0
- package/dist/config/schema.cjs.map +1 -0
- package/dist/config/schema.d.cts +51 -0
- package/dist/config/schema.d.cts.map +1 -0
- package/dist/config/schema.d.ts +51 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +145 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/constants.cjs +22 -0
- package/dist/constants.cjs.map +1 -0
- package/dist/constants.d.cts +10 -0
- package/dist/constants.d.cts.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +19 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/benchmark-schema.cjs +135 -0
- package/dist/core/benchmark-schema.cjs.map +1 -0
- package/dist/core/benchmark-schema.d.cts +139 -0
- package/dist/core/benchmark-schema.d.cts.map +1 -0
- package/dist/core/benchmark-schema.d.ts +139 -0
- package/dist/core/benchmark-schema.d.ts.map +1 -0
- package/dist/core/benchmark-schema.js +132 -0
- package/dist/core/benchmark-schema.js.map +1 -0
- package/dist/core/engine.cjs +669 -0
- package/dist/core/engine.cjs.map +1 -0
- package/dist/core/engine.d.cts +128 -0
- package/dist/core/engine.d.cts.map +1 -0
- package/dist/core/engine.d.ts +128 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +632 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/engines/accurate-engine.cjs +292 -0
- package/dist/core/engines/accurate-engine.cjs.map +1 -0
- package/dist/core/engines/accurate-engine.d.cts +63 -0
- package/dist/core/engines/accurate-engine.d.cts.map +1 -0
- package/dist/core/engines/accurate-engine.d.ts +63 -0
- package/dist/core/engines/accurate-engine.d.ts.map +1 -0
- package/dist/core/engines/accurate-engine.js +288 -0
- package/dist/core/engines/accurate-engine.js.map +1 -0
- package/dist/core/engines/index.cjs +21 -0
- package/dist/core/engines/index.cjs.map +1 -0
- package/dist/core/engines/index.d.cts +16 -0
- package/dist/core/engines/index.d.cts.map +1 -0
- package/dist/core/engines/index.d.ts +16 -0
- package/dist/core/engines/index.d.ts.map +1 -0
- package/dist/core/engines/index.js +16 -0
- package/dist/core/engines/index.js.map +1 -0
- package/dist/core/engines/tinybench-engine.cjs +286 -0
- package/dist/core/engines/tinybench-engine.cjs.map +1 -0
- package/dist/core/engines/tinybench-engine.d.cts +18 -0
- package/dist/core/engines/tinybench-engine.d.cts.map +1 -0
- package/dist/core/engines/tinybench-engine.d.ts +18 -0
- package/dist/core/engines/tinybench-engine.d.ts.map +1 -0
- package/dist/core/engines/tinybench-engine.js +282 -0
- package/dist/core/engines/tinybench-engine.js.map +1 -0
- package/dist/core/error-manager.cjs +303 -0
- package/dist/core/error-manager.cjs.map +1 -0
- package/dist/core/error-manager.d.cts +77 -0
- package/dist/core/error-manager.d.cts.map +1 -0
- package/dist/core/error-manager.d.ts +77 -0
- package/dist/core/error-manager.d.ts.map +1 -0
- package/dist/core/error-manager.js +299 -0
- package/dist/core/error-manager.js.map +1 -0
- package/dist/core/loader.cjs +287 -0
- package/dist/core/loader.cjs.map +1 -0
- package/dist/core/loader.d.cts +55 -0
- package/dist/core/loader.d.cts.map +1 -0
- package/dist/core/loader.d.ts +55 -0
- package/dist/core/loader.d.ts.map +1 -0
- package/dist/core/loader.js +250 -0
- package/dist/core/loader.js.map +1 -0
- package/dist/core/stats-utils.cjs +99 -0
- package/dist/core/stats-utils.cjs.map +1 -0
- package/dist/core/stats-utils.d.cts +50 -0
- package/dist/core/stats-utils.d.cts.map +1 -0
- package/dist/core/stats-utils.d.ts +50 -0
- package/dist/core/stats-utils.d.ts.map +1 -0
- package/dist/core/stats-utils.js +94 -0
- package/dist/core/stats-utils.js.map +1 -0
- package/dist/index.cjs +64 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/progress/manager.cjs +325 -0
- package/dist/progress/manager.cjs.map +1 -0
- package/dist/progress/manager.d.cts +125 -0
- package/dist/progress/manager.d.cts.map +1 -0
- package/dist/progress/manager.d.ts +125 -0
- package/dist/progress/manager.d.ts.map +1 -0
- package/dist/progress/manager.js +321 -0
- package/dist/progress/manager.js.map +1 -0
- package/dist/reporters/csv.cjs +250 -0
- package/dist/reporters/csv.cjs.map +1 -0
- package/dist/reporters/csv.d.cts +92 -0
- package/dist/reporters/csv.d.cts.map +1 -0
- package/dist/reporters/csv.d.ts +92 -0
- package/dist/reporters/csv.d.ts.map +1 -0
- package/dist/reporters/csv.js +246 -0
- package/dist/reporters/csv.js.map +1 -0
- package/dist/reporters/human.cjs +516 -0
- package/dist/reporters/human.cjs.map +1 -0
- package/dist/reporters/human.d.cts +86 -0
- package/dist/reporters/human.d.cts.map +1 -0
- package/dist/reporters/human.d.ts +86 -0
- package/dist/reporters/human.d.ts.map +1 -0
- package/dist/reporters/human.js +509 -0
- package/dist/reporters/human.js.map +1 -0
- package/dist/reporters/index.cjs +17 -0
- package/dist/reporters/index.cjs.map +1 -0
- package/dist/reporters/index.d.cts +10 -0
- package/dist/reporters/index.d.cts.map +1 -0
- package/dist/reporters/index.d.ts +10 -0
- package/dist/reporters/index.d.ts.map +1 -0
- package/dist/reporters/index.js +10 -0
- package/dist/reporters/index.js.map +1 -0
- package/dist/reporters/json.cjs +215 -0
- package/dist/reporters/json.cjs.map +1 -0
- package/dist/reporters/json.d.cts +79 -0
- package/dist/reporters/json.d.cts.map +1 -0
- package/dist/reporters/json.d.ts +79 -0
- package/dist/reporters/json.d.ts.map +1 -0
- package/dist/reporters/json.js +211 -0
- package/dist/reporters/json.js.map +1 -0
- package/dist/reporters/registry.cjs +255 -0
- package/dist/reporters/registry.cjs.map +1 -0
- package/dist/reporters/registry.d.cts +155 -0
- package/dist/reporters/registry.d.cts.map +1 -0
- package/dist/reporters/registry.d.ts +155 -0
- package/dist/reporters/registry.d.ts.map +1 -0
- package/dist/reporters/registry.js +249 -0
- package/dist/reporters/registry.js.map +1 -0
- package/dist/reporters/simple.cjs +328 -0
- package/dist/reporters/simple.cjs.map +1 -0
- package/dist/reporters/simple.d.cts +51 -0
- package/dist/reporters/simple.d.cts.map +1 -0
- package/dist/reporters/simple.d.ts +51 -0
- package/dist/reporters/simple.d.ts.map +1 -0
- package/dist/reporters/simple.js +321 -0
- package/dist/reporters/simple.js.map +1 -0
- package/dist/schema/modestbench-config.schema.json +162 -0
- package/dist/storage/history.cjs +456 -0
- package/dist/storage/history.cjs.map +1 -0
- package/dist/storage/history.d.cts +99 -0
- package/dist/storage/history.d.cts.map +1 -0
- package/dist/storage/history.d.ts +99 -0
- package/dist/storage/history.d.ts.map +1 -0
- package/dist/storage/history.js +452 -0
- package/dist/storage/history.js.map +1 -0
- package/dist/types/cli.cjs +21 -0
- package/dist/types/cli.cjs.map +1 -0
- package/dist/types/cli.d.cts +296 -0
- package/dist/types/cli.d.cts.map +1 -0
- package/dist/types/cli.d.ts +296 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/cli.js +18 -0
- package/dist/types/cli.js.map +1 -0
- package/dist/types/core.cjs +14 -0
- package/dist/types/core.cjs.map +1 -0
- package/dist/types/core.d.cts +380 -0
- package/dist/types/core.d.cts.map +1 -0
- package/dist/types/core.d.ts +380 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/core.js +13 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/index.cjs +27 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +11 -0
- package/dist/types/index.d.cts.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/interfaces.cjs +10 -0
- package/dist/types/interfaces.cjs.map +1 -0
- package/dist/types/interfaces.d.cts +381 -0
- package/dist/types/interfaces.d.cts.map +1 -0
- package/dist/types/interfaces.d.ts +381 -0
- package/dist/types/interfaces.d.ts.map +1 -0
- package/dist/types/interfaces.js +9 -0
- package/dist/types/interfaces.js.map +1 -0
- package/dist/types/utility.cjs +92 -0
- package/dist/types/utility.cjs.map +1 -0
- package/dist/types/utility.d.cts +330 -0
- package/dist/types/utility.d.cts.map +1 -0
- package/dist/types/utility.d.ts +330 -0
- package/dist/types/utility.d.ts.map +1 -0
- package/dist/types/utility.js +78 -0
- package/dist/types/utility.js.map +1 -0
- package/package.json +211 -0
- package/src/bootstrap.ts +35 -0
- package/src/cli/commands/history.ts +569 -0
- package/src/cli/commands/init.ts +658 -0
- package/src/cli/commands/run.ts +346 -0
- package/src/cli/index.ts +642 -0
- package/src/config/manager.ts +387 -0
- package/src/config/schema.ts +188 -0
- package/src/constants.ts +21 -0
- package/src/core/benchmark-schema.ts +185 -0
- package/src/core/engine.ts +888 -0
- package/src/core/engines/accurate-engine.ts +408 -0
- package/src/core/engines/index.ts +16 -0
- package/src/core/engines/tinybench-engine.ts +335 -0
- package/src/core/error-manager.ts +372 -0
- package/src/core/loader.ts +324 -0
- package/src/core/stats-utils.ts +135 -0
- package/src/index.ts +46 -0
- package/src/progress/manager.ts +415 -0
- package/src/reporters/csv.ts +368 -0
- package/src/reporters/human.ts +707 -0
- package/src/reporters/index.ts +10 -0
- package/src/reporters/json.ts +302 -0
- package/src/reporters/registry.ts +349 -0
- package/src/reporters/simple.ts +459 -0
- package/src/storage/history.ts +600 -0
- package/src/types/cli.ts +312 -0
- package/src/types/core.ts +414 -0
- package/src/types/index.ts +18 -0
- package/src/types/interfaces.ts +451 -0
- package/src/types/utility.ts +446 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModestBench CSV Reporter
|
|
3
|
+
*
|
|
4
|
+
* Outputs benchmark results in CSV format for data analysis and visualization.
|
|
5
|
+
* Provides structured tabular data suitable for spreadsheets and statistical
|
|
6
|
+
* tools.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { dirname } from 'node:path';
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
BenchmarkRun,
|
|
14
|
+
FileResult,
|
|
15
|
+
ProgressState,
|
|
16
|
+
SuiteResult,
|
|
17
|
+
TaskResult,
|
|
18
|
+
} from '../types/index.js';
|
|
19
|
+
|
|
20
|
+
import { BaseReporter } from './registry.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* CSV column definitions for task results
|
|
24
|
+
*/
|
|
25
|
+
interface CsvRow {
|
|
26
|
+
readonly arch: string;
|
|
27
|
+
readonly ciProvider?: string | undefined;
|
|
28
|
+
readonly cpuCores: number;
|
|
29
|
+
readonly cpuModel: string;
|
|
30
|
+
readonly error?: string | undefined;
|
|
31
|
+
readonly file: string;
|
|
32
|
+
readonly gitBranch?: string | undefined;
|
|
33
|
+
readonly gitCommit?: string | undefined;
|
|
34
|
+
readonly iterations: number;
|
|
35
|
+
readonly marginOfError: number;
|
|
36
|
+
readonly max: number;
|
|
37
|
+
readonly mean: number;
|
|
38
|
+
readonly min: number;
|
|
39
|
+
readonly nodeVersion: string;
|
|
40
|
+
readonly opsPerSecond: number;
|
|
41
|
+
readonly p95: number;
|
|
42
|
+
readonly p99: number;
|
|
43
|
+
readonly platform: string;
|
|
44
|
+
readonly stdDev: number;
|
|
45
|
+
readonly suite: string;
|
|
46
|
+
readonly task: string;
|
|
47
|
+
readonly timestamp: string;
|
|
48
|
+
readonly totalMemory: number;
|
|
49
|
+
readonly variance: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* CSV reporter for structured tabular output
|
|
54
|
+
*/
|
|
55
|
+
export class CsvReporter extends BaseReporter {
|
|
56
|
+
private currentFile = '';
|
|
57
|
+
|
|
58
|
+
private currentRun?: BenchmarkRun;
|
|
59
|
+
|
|
60
|
+
private currentSuite = '';
|
|
61
|
+
|
|
62
|
+
private readonly delimiter: string;
|
|
63
|
+
|
|
64
|
+
private readonly includeHeaders: boolean;
|
|
65
|
+
|
|
66
|
+
private readonly includeMetadata: boolean;
|
|
67
|
+
|
|
68
|
+
private readonly outputPath?: string | undefined;
|
|
69
|
+
|
|
70
|
+
private readonly quiet: boolean;
|
|
71
|
+
|
|
72
|
+
private readonly quote: string;
|
|
73
|
+
|
|
74
|
+
private rows: CsvRow[] = [];
|
|
75
|
+
|
|
76
|
+
constructor(
|
|
77
|
+
options: {
|
|
78
|
+
delimiter?: string;
|
|
79
|
+
includeHeaders?: boolean;
|
|
80
|
+
includeMetadata?: boolean;
|
|
81
|
+
outputPath?: string;
|
|
82
|
+
quiet?: boolean;
|
|
83
|
+
quote?: string;
|
|
84
|
+
verbose?: boolean;
|
|
85
|
+
} = {},
|
|
86
|
+
) {
|
|
87
|
+
super('csv', options);
|
|
88
|
+
|
|
89
|
+
this.outputPath = options.outputPath;
|
|
90
|
+
this.includeHeaders = options.includeHeaders ?? true;
|
|
91
|
+
this.includeMetadata = options.includeMetadata ?? true;
|
|
92
|
+
this.delimiter = options.delimiter ?? ',';
|
|
93
|
+
this.quote = options.quote ?? '"';
|
|
94
|
+
this.quiet = options.quiet ?? false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if headers are included
|
|
99
|
+
*/
|
|
100
|
+
areHeadersIncluded(): boolean {
|
|
101
|
+
return this.includeHeaders;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get the delimiter character
|
|
106
|
+
*/
|
|
107
|
+
getDelimiter(): string {
|
|
108
|
+
return this.delimiter;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get the output path (if configured)
|
|
113
|
+
*/
|
|
114
|
+
getOutputPath(): string | undefined {
|
|
115
|
+
return this.outputPath;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get the quote character
|
|
120
|
+
*/
|
|
121
|
+
getQuote(): string {
|
|
122
|
+
return this.quote;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get the number of rows collected
|
|
127
|
+
*/
|
|
128
|
+
getRowCount(): number {
|
|
129
|
+
return this.rows.length;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if metadata is included
|
|
134
|
+
*/
|
|
135
|
+
isMetadataIncluded(): boolean {
|
|
136
|
+
return this.includeMetadata;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async onEnd(_run: BenchmarkRun): Promise<void> {
|
|
140
|
+
const csvContent = this.generateCsv();
|
|
141
|
+
|
|
142
|
+
if (this.outputPath) {
|
|
143
|
+
await this.writeToFile(csvContent);
|
|
144
|
+
} else {
|
|
145
|
+
this.writeToStdout(csvContent);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
onError(error: Error): void {
|
|
150
|
+
console.error('CSV Reporter Error:', error.message);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
onFileEnd(_result: FileResult): void {
|
|
154
|
+
// No-op for CSV reporter
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
onFileStart(file: string): void {
|
|
158
|
+
this.currentFile = file;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
onProgress(_state: ProgressState): void {
|
|
162
|
+
// No-op for CSV reporter
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
onStart(run: BenchmarkRun): void {
|
|
166
|
+
this.currentRun = run;
|
|
167
|
+
this.rows = [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
onSuiteEnd(_result: SuiteResult): void {
|
|
171
|
+
// No-op for CSV reporter
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
onSuiteStart(suite: string): void {
|
|
175
|
+
this.currentSuite = suite;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
onTaskResult(result: TaskResult): void {
|
|
179
|
+
if (!this.currentRun) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const row: CsvRow = {
|
|
184
|
+
arch: this.currentRun.environment.arch,
|
|
185
|
+
ciProvider: this.currentRun.ci?.provider,
|
|
186
|
+
cpuCores: this.currentRun.environment.cpu.cores,
|
|
187
|
+
cpuModel: this.currentRun.environment.cpu.model,
|
|
188
|
+
error: result.error?.message,
|
|
189
|
+
file: this.currentFile,
|
|
190
|
+
gitBranch: this.currentRun.git?.branch,
|
|
191
|
+
gitCommit: this.currentRun.git?.commit,
|
|
192
|
+
iterations: result.iterations,
|
|
193
|
+
marginOfError: result.marginOfError,
|
|
194
|
+
max: result.max,
|
|
195
|
+
mean: result.mean,
|
|
196
|
+
min: result.min,
|
|
197
|
+
nodeVersion: this.currentRun.environment.nodeVersion,
|
|
198
|
+
opsPerSecond: result.opsPerSecond,
|
|
199
|
+
p95: result.p95,
|
|
200
|
+
p99: result.p99,
|
|
201
|
+
platform: this.currentRun.environment.platform,
|
|
202
|
+
stdDev: result.stdDev,
|
|
203
|
+
suite: this.currentSuite,
|
|
204
|
+
task: result.name,
|
|
205
|
+
timestamp: new Date().toISOString(),
|
|
206
|
+
totalMemory: this.currentRun.environment.memory.total,
|
|
207
|
+
variance: result.variance,
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
this.rows.push(row);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
onTaskStart(_task: string): void {
|
|
214
|
+
// No-op for CSV reporter
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Escape a field value for CSV format
|
|
219
|
+
*/
|
|
220
|
+
private escapeField(value: string): string {
|
|
221
|
+
// If value contains delimiter, quote, or newline, wrap in quotes
|
|
222
|
+
if (
|
|
223
|
+
value.includes(this.delimiter) ||
|
|
224
|
+
value.includes(this.quote) ||
|
|
225
|
+
value.includes('\n') ||
|
|
226
|
+
value.includes('\r')
|
|
227
|
+
) {
|
|
228
|
+
// Escape any existing quotes by doubling them
|
|
229
|
+
const escaped = value.replace(
|
|
230
|
+
new RegExp(this.quote, 'g'),
|
|
231
|
+
this.quote + this.quote,
|
|
232
|
+
);
|
|
233
|
+
return this.quote + escaped + this.quote;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return value;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Generate CSV content from collected rows
|
|
241
|
+
*/
|
|
242
|
+
private generateCsv(): string {
|
|
243
|
+
if (this.rows.length === 0) {
|
|
244
|
+
return this.includeHeaders ? this.generateHeaders() : '';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const lines: string[] = [];
|
|
248
|
+
|
|
249
|
+
if (this.includeHeaders) {
|
|
250
|
+
lines.push(this.generateHeaders());
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
for (const row of this.rows) {
|
|
254
|
+
lines.push(this.generateRow(row));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return lines.join('\n') + '\n';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Generate CSV headers
|
|
262
|
+
*/
|
|
263
|
+
private generateHeaders(): string {
|
|
264
|
+
const headers = [
|
|
265
|
+
'file',
|
|
266
|
+
'suite',
|
|
267
|
+
'task',
|
|
268
|
+
'mean',
|
|
269
|
+
'stdDev',
|
|
270
|
+
'min',
|
|
271
|
+
'max',
|
|
272
|
+
'iterations',
|
|
273
|
+
'opsPerSecond',
|
|
274
|
+
'marginOfError',
|
|
275
|
+
'variance',
|
|
276
|
+
'p95',
|
|
277
|
+
'p99',
|
|
278
|
+
'error',
|
|
279
|
+
'timestamp',
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
if (this.includeMetadata) {
|
|
283
|
+
headers.push(
|
|
284
|
+
'nodeVersion',
|
|
285
|
+
'platform',
|
|
286
|
+
'arch',
|
|
287
|
+
'cpuModel',
|
|
288
|
+
'cpuCores',
|
|
289
|
+
'totalMemory',
|
|
290
|
+
'gitCommit',
|
|
291
|
+
'gitBranch',
|
|
292
|
+
'ciProvider',
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return headers.map((h) => this.escapeField(h)).join(this.delimiter);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Generate a CSV row from a data row
|
|
301
|
+
*/
|
|
302
|
+
private generateRow(row: CsvRow): string {
|
|
303
|
+
const values = [
|
|
304
|
+
row.file || '',
|
|
305
|
+
row.suite || '',
|
|
306
|
+
row.task || '',
|
|
307
|
+
(row.mean ?? 0).toString(),
|
|
308
|
+
(row.stdDev ?? 0).toString(),
|
|
309
|
+
(row.min ?? 0).toString(),
|
|
310
|
+
(row.max ?? 0).toString(),
|
|
311
|
+
(row.iterations ?? 0).toString(),
|
|
312
|
+
(row.opsPerSecond ?? 0).toString(),
|
|
313
|
+
(row.marginOfError ?? 0).toString(),
|
|
314
|
+
(row.variance ?? 0).toString(),
|
|
315
|
+
(row.p95 ?? 0).toString(),
|
|
316
|
+
(row.p99 ?? 0).toString(),
|
|
317
|
+
row.error || '',
|
|
318
|
+
row.timestamp || '',
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
if (this.includeMetadata) {
|
|
322
|
+
values.push(
|
|
323
|
+
row.nodeVersion || '',
|
|
324
|
+
row.platform || '',
|
|
325
|
+
row.arch || '',
|
|
326
|
+
row.cpuModel || '',
|
|
327
|
+
(row.cpuCores ?? 0).toString(),
|
|
328
|
+
(row.totalMemory ?? 0).toString(),
|
|
329
|
+
row.gitCommit || '',
|
|
330
|
+
row.gitBranch || '',
|
|
331
|
+
row.ciProvider || '',
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return values.map((v) => this.escapeField(v)).join(this.delimiter);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Write CSV content to file
|
|
340
|
+
*/
|
|
341
|
+
private async writeToFile(csvContent: string): Promise<void> {
|
|
342
|
+
if (!this.outputPath) {
|
|
343
|
+
throw new Error('Output path not specified');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
// Ensure directory exists
|
|
348
|
+
const dir = dirname(this.outputPath);
|
|
349
|
+
mkdirSync(dir, { recursive: true });
|
|
350
|
+
|
|
351
|
+
// Write CSV file
|
|
352
|
+
writeFileSync(this.outputPath, csvContent, 'utf8');
|
|
353
|
+
} catch (error) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
`Failed to write CSV output to ${this.outputPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Write CSV content to stdout
|
|
362
|
+
*/
|
|
363
|
+
private writeToStdout(csvContent: string): void {
|
|
364
|
+
// Always write to stdout when no output path is specified
|
|
365
|
+
// The quiet flag only affects progress messages (stderr), not data output
|
|
366
|
+
console.log(csvContent);
|
|
367
|
+
}
|
|
368
|
+
}
|