/** * Standard read/write helpers for benchmark results and baselines. * * Results shape (written by benchmark tests): * { * "suite": { "name": "...", "unit": "ms", "direction": "smaller" }, * "entries": { * "": { mean, median, p50, p90, p99, stdDev, min, max, count, timings, ...meta } * } * } * * Baseline shape (committed per suite): * { * "thresholdPercent": 20, * "entries": { * "": { mean, p50 } * } * } */ import { existsSync, readFileSync, writeFileSync } from 'fs'; import { summarize } from './stats'; export type Direction = 'smaller' | 'bigger'; export type Unit = 'ms' | 's' | 'ops/s' | 'bytes' | '%' | 'count'; export interface SuiteMeta { name: string; unit: Unit; direction: Direction; } export interface ResultEntry { mean: number; median: number; p50: number; p90: number; p99: number; stdDev: number; min: number; max: number; count: number; timings: number[]; [key: string]: any; } export interface ResultsFile { suite: SuiteMeta; entries: Record; } export interface BaselineEntry { mean: number; p50: number; } export interface BaselineFile { thresholdPercent: number; entries: Record; } export function readResults(filePath: string): ResultsFile { if (!existsSync(filePath)) { throw new Error(`Results file not found: ${filePath}`); } return JSON.parse(readFileSync(filePath, 'utf-8')); } export function writeResults(filePath: string, suite: SuiteMeta, entries: Record) { const data: ResultsFile = { suite, entries }; writeFileSync(filePath, JSON.stringify(data, null, 2)); } export function buildResultEntry(timings: number[], meta: Record = {}): ResultEntry { return { ...summarize(timings), timings, ...meta }; } export function readBaseline(filePath: string): BaselineFile { if (!existsSync(filePath)) { throw new Error(`Baseline file not found: ${filePath}`); } return JSON.parse(readFileSync(filePath, 'utf-8')); } export function writeBaseline(filePath: string, results: ResultsFile, thresholdPercent: number) { const entries: Record = {}; for (const [key, data] of Object.entries(results.entries)) { entries[key] = { mean: data.mean, p50: data.p50 }; } const data: BaselineFile = { thresholdPercent, entries }; writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n'); }