benchforge 0.1.8 → 0.1.11

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 (68) hide show
  1. package/README.md +69 -42
  2. package/dist/{BenchRunner-CSKN9zPy.d.mts → BenchRunner-BzyUfiyB.d.mts} +32 -8
  3. package/dist/{BrowserHeapSampler-DCeL42RE.mjs → BrowserHeapSampler-B6asLKWQ.mjs} +57 -57
  4. package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +1 -0
  5. package/dist/{GcStats-ByEovUi1.mjs → GcStats-wX7Xyblu.mjs} +15 -15
  6. package/dist/GcStats-wX7Xyblu.mjs.map +1 -0
  7. package/dist/HeapSampler-B8dtKHn1.mjs.map +1 -1
  8. package/dist/{TimingUtils-ClclVQ7E.mjs → TimingUtils-DwOwkc8G.mjs} +225 -225
  9. package/dist/TimingUtils-DwOwkc8G.mjs.map +1 -0
  10. package/dist/bin/benchforge.mjs +1 -1
  11. package/dist/browser/index.js +210 -210
  12. package/dist/index.d.mts +106 -48
  13. package/dist/index.mjs +3 -3
  14. package/dist/runners/WorkerScript.d.mts +1 -1
  15. package/dist/runners/WorkerScript.mjs +66 -66
  16. package/dist/runners/WorkerScript.mjs.map +1 -1
  17. package/dist/{src-HfimYuW_.mjs → src-B-DDaCa9.mjs} +1250 -991
  18. package/dist/src-B-DDaCa9.mjs.map +1 -0
  19. package/package.json +4 -3
  20. package/src/BenchMatrix.ts +125 -125
  21. package/src/BenchmarkReport.ts +50 -45
  22. package/src/HtmlDataPrep.ts +21 -21
  23. package/src/PermutationTest.ts +24 -24
  24. package/src/StandardSections.ts +45 -45
  25. package/src/StatisticalUtils.ts +60 -61
  26. package/src/browser/BrowserGcStats.ts +5 -5
  27. package/src/browser/BrowserHeapSampler.ts +63 -63
  28. package/src/cli/CliArgs.ts +20 -6
  29. package/src/cli/FilterBenchmarks.ts +5 -5
  30. package/src/cli/RunBenchCLI.ts +533 -476
  31. package/src/export/JsonExport.ts +10 -10
  32. package/src/export/PerfettoExport.ts +74 -74
  33. package/src/export/SpeedscopeExport.ts +202 -0
  34. package/src/heap-sample/HeapSampleReport.ts +143 -70
  35. package/src/heap-sample/HeapSampler.ts +55 -12
  36. package/src/heap-sample/ResolvedProfile.ts +89 -0
  37. package/src/html/HtmlReport.ts +33 -33
  38. package/src/html/HtmlTemplate.ts +67 -67
  39. package/src/html/browser/CIPlot.ts +50 -50
  40. package/src/html/browser/HistogramKde.ts +13 -13
  41. package/src/html/browser/LegendUtils.ts +48 -48
  42. package/src/html/browser/RenderPlots.ts +98 -98
  43. package/src/html/browser/SampleTimeSeries.ts +79 -79
  44. package/src/index.ts +6 -0
  45. package/src/matrix/MatrixFilter.ts +6 -6
  46. package/src/matrix/MatrixReport.ts +96 -96
  47. package/src/matrix/VariantLoader.ts +5 -5
  48. package/src/runners/AdaptiveWrapper.ts +151 -151
  49. package/src/runners/BasicRunner.ts +175 -175
  50. package/src/runners/BenchRunner.ts +8 -8
  51. package/src/runners/GcStats.ts +22 -22
  52. package/src/runners/RunnerOrchestrator.ts +168 -168
  53. package/src/runners/WorkerScript.ts +96 -96
  54. package/src/table-util/Formatters.ts +41 -36
  55. package/src/table-util/TableReport.ts +122 -122
  56. package/src/table-util/test/TableValueExtractor.ts +9 -9
  57. package/src/test/AdaptiveStatistics.integration.ts +7 -39
  58. package/src/test/HeapAttribution.test.ts +51 -0
  59. package/src/test/RunBenchCLI.test.ts +36 -11
  60. package/src/test/TestUtils.ts +24 -24
  61. package/src/test/fixtures/fn-export-bench.ts +3 -0
  62. package/src/test/fixtures/suite-export-bench.ts +16 -0
  63. package/src/tests/BenchMatrix.test.ts +12 -12
  64. package/src/tests/MatrixFilter.test.ts +15 -15
  65. package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
  66. package/dist/GcStats-ByEovUi1.mjs.map +0 -1
  67. package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
  68. package/dist/src-HfimYuW_.mjs.map +0 -1
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as MeasuredResults, i as BenchmarkSpec, n as BenchGroup, r as BenchSuite, t as RunnerOptions } from "./BenchRunner-CSKN9zPy.mjs";
1
+ import { a as MeasuredResults, i as BenchmarkSpec, n as BenchGroup, o as HeapProfile, r as BenchSuite, t as RunnerOptions } from "./BenchRunner-BzyUfiyB.mjs";
2
2
  import { Alignment } from "table";
3
3
  import { Argv, InferredOptionTypes } from "yargs";
4
4
  import * as node_http0 from "node:http";
@@ -59,8 +59,6 @@ interface MatrixResults {
59
59
  name: string;
60
60
  variants: VariantResult[];
61
61
  }
62
- /** @return true if variant is a StatefulVariant (has setup + run) */
63
- declare function isStatefulVariant<T, S>(v: Variant<T, S>): v is StatefulVariant<T, S>;
64
62
  /** Options for runMatrix */
65
63
  interface RunMatrixOptions {
66
64
  iterations?: number;
@@ -81,6 +79,8 @@ interface RunMatrixOptions {
81
79
  heapInterval?: number;
82
80
  heapDepth?: number;
83
81
  }
82
+ /** @return true if variant is a StatefulVariant (has setup + run) */
83
+ declare function isStatefulVariant<T, S>(v: Variant<T, S>): v is StatefulVariant<T, S>;
84
84
  /** Run a BenchMatrix with inline variants or variantDir */
85
85
  declare function runMatrix<T>(matrix: BenchMatrix<T>, options?: RunMatrixOptions): Promise<MatrixResults>;
86
86
  //#endregion
@@ -104,7 +104,7 @@ interface ColumnFormat<T> {
104
104
  alignment?: Alignment;
105
105
  width?: number;
106
106
  }
107
- /** Table headers and configuration */
107
+ /** Build formatted table with column groups and baselines */
108
108
  //#endregion
109
109
  //#region src/BenchmarkReport.d.ts
110
110
  /** Benchmark results with optional baseline for comparison */
@@ -138,8 +138,10 @@ declare function reportResults<S extends ReadonlyArray<ResultsMapper<any>>>(grou
138
138
  //#endregion
139
139
  //#region src/cli/CliArgs.d.ts
140
140
  type Configure<T> = (yargs: Argv) => Argv<T>;
141
- /** CLI args type inferred from cliOptions */
142
- type DefaultCliArgs = InferredOptionTypes<typeof cliOptions>;
141
+ /** CLI args type inferred from cliOptions, plus optional file positional */
142
+ type DefaultCliArgs = InferredOptionTypes<typeof cliOptions> & {
143
+ file?: string;
144
+ };
143
145
  declare const cliOptions: {
144
146
  readonly time: {
145
147
  readonly type: "number";
@@ -217,11 +219,21 @@ declare const cliOptions: {
217
219
  readonly requiresArg: true;
218
220
  readonly describe: "export benchmark data to JSON file";
219
221
  };
220
- readonly perfetto: {
222
+ readonly "export-perfetto": {
221
223
  readonly type: "string";
222
224
  readonly requiresArg: true;
223
225
  readonly describe: "export Perfetto trace file (view at ui.perfetto.dev)";
224
226
  };
227
+ readonly speedscope: {
228
+ readonly type: "boolean";
229
+ readonly default: false;
230
+ readonly describe: "open heap profile in speedscope (via npx)";
231
+ };
232
+ readonly "export-speedscope": {
233
+ readonly type: "string";
234
+ readonly requiresArg: true;
235
+ readonly describe: "export heap profile as speedscope JSON";
236
+ };
225
237
  readonly "trace-opt": {
226
238
  readonly type: "boolean";
227
239
  readonly default: false;
@@ -286,6 +298,11 @@ declare const cliOptions: {
286
298
  readonly default: false;
287
299
  readonly describe: "verbose output with file:// paths and line numbers";
288
300
  };
301
+ readonly "heap-raw": {
302
+ readonly type: "boolean";
303
+ readonly default: false;
304
+ readonly describe: "dump every raw heap sample (ordinal, size, stack)";
305
+ };
289
306
  readonly "heap-user-only": {
290
307
  readonly type: "boolean";
291
308
  readonly default: false;
@@ -422,13 +439,14 @@ interface CallFrame {
422
439
  fn: string;
423
440
  url: string;
424
441
  line: number;
425
- col: number;
442
+ col?: number;
426
443
  }
427
444
  type UserCodeFilter = (site: CallFrame) => boolean;
428
445
  interface HeapReportOptions {
429
446
  topN: number;
430
447
  stackDepth?: number;
431
448
  verbose?: boolean;
449
+ raw?: boolean;
432
450
  userOnly?: boolean;
433
451
  isUserCode?: UserCodeFilter;
434
452
  totalAll?: number;
@@ -451,16 +469,29 @@ interface MatrixReportOptions {
451
469
  sections?: ResultsMapper[];
452
470
  variantTitle?: string;
453
471
  }
454
- /** Format matrix results as one table per case */
455
- declare function reportMatrixResults(results: MatrixResults, options?: MatrixReportOptions): string;
456
472
  /** GC statistics columns - derived from gcStatsSection for consistency */
457
473
  declare const gcStatsColumns: ExtraColumn[];
458
474
  /** GC pause time column */
459
475
  declare const gcPauseColumn: ExtraColumn;
460
476
  /** Heap sampling total bytes column */
461
477
  declare const heapTotalColumn: ExtraColumn;
478
+ /** Format matrix results as one table per case */
479
+ declare function reportMatrixResults(results: MatrixResults, options?: MatrixReportOptions): string;
462
480
  //#endregion
463
481
  //#region src/cli/RunBenchCLI.d.ts
482
+ interface ExportOptions {
483
+ results: ReportGroup[];
484
+ args: DefaultCliArgs;
485
+ sections?: any[];
486
+ suiteName?: string;
487
+ currentVersion?: GitVersion;
488
+ baselineVersion?: GitVersion;
489
+ }
490
+ interface MatrixExportOptions {
491
+ sections?: any[];
492
+ currentVersion?: GitVersion;
493
+ baselineVersion?: GitVersion;
494
+ }
464
495
  /** Parse CLI with custom configuration */
465
496
  declare function parseBenchArgs<T = DefaultCliArgs>(configureArgs?: Configure<T>): T & DefaultCliArgs;
466
497
  /** Run suite with CLI arguments */
@@ -477,14 +508,6 @@ declare function runDefaultBench(suite?: BenchSuite, configureArgs?: Configure<a
477
508
  declare function reportOptStatus(groups: ReportGroup[]): void;
478
509
  /** @return true if any result has the specified field with a defined value */
479
510
  declare function hasField(results: ReportGroup[], field: keyof MeasuredResults): boolean;
480
- interface ExportOptions {
481
- results: ReportGroup[];
482
- args: DefaultCliArgs;
483
- sections?: any[];
484
- suiteName?: string;
485
- currentVersion?: GitVersion;
486
- baselineVersion?: GitVersion;
487
- }
488
511
  /** Export reports (HTML, JSON, Perfetto) based on CLI args */
489
512
  declare function exportReports(options: ExportOptions): Promise<void>;
490
513
  /** Run matrix suite with CLI arguments.
@@ -499,11 +522,6 @@ declare function defaultMatrixReport(results: MatrixResults[], reportOptions?: M
499
522
  declare function runDefaultMatrixBench(suite: MatrixSuite, configureArgs?: Configure<any>, reportOptions?: MatrixReportOptions): Promise<void>;
500
523
  /** Convert MatrixResults to ReportGroup[] for export compatibility */
501
524
  declare function matrixToReportGroups(results: MatrixResults[]): ReportGroup[];
502
- interface MatrixExportOptions {
503
- sections?: any[];
504
- currentVersion?: GitVersion;
505
- baselineVersion?: GitVersion;
506
- }
507
525
  /** Run matrix benchmarks, display table, and generate exports */
508
526
  declare function matrixBenchExports(suite: MatrixSuite, args: DefaultCliArgs, reportOptions?: MatrixReportOptions, exportOptions?: MatrixExportOptions): Promise<void>;
509
527
  //#endregion
@@ -593,6 +611,43 @@ interface BenchmarkResult {
593
611
  /** Export benchmark results to Perfetto-compatible trace file */
594
612
  declare function exportPerfettoTrace(groups: ReportGroup[], outputPath: string, args: DefaultCliArgs): void;
595
613
  //#endregion
614
+ //#region src/export/SpeedscopeExport.d.ts
615
+ /** speedscope file format (https://www.speedscope.app/file-format-schema.json) */
616
+ interface SpeedscopeFile {
617
+ $schema: "https://www.speedscope.app/file-format-schema.json";
618
+ shared: {
619
+ frames: SpeedscopeFrame[];
620
+ };
621
+ profiles: SpeedscopeProfile[];
622
+ name?: string;
623
+ exporter?: string;
624
+ }
625
+ interface SpeedscopeFrame {
626
+ name: string;
627
+ file?: string;
628
+ line?: number;
629
+ col?: number;
630
+ }
631
+ interface SpeedscopeProfile {
632
+ type: "sampled";
633
+ name: string;
634
+ unit: "bytes";
635
+ startValue: number;
636
+ endValue: number;
637
+ samples: number[][];
638
+ weights: number[];
639
+ }
640
+ /** Export heap profiles from benchmark results to speedscope JSON format.
641
+ * Creates one speedscope profile per benchmark that has a heapProfile.
642
+ * @returns resolved output path, or undefined if no profiles were found */
643
+ declare function exportSpeedscope(groups: ReportGroup[], outputPath: string): string | undefined;
644
+ /** Export to a temp file and open in speedscope via npx */
645
+ declare function exportAndLaunchSpeedscope(groups: ReportGroup[]): void;
646
+ /** Launch speedscope viewer on a file via npx */
647
+ declare function launchSpeedscope(filePath: string): void;
648
+ /** Convert a single HeapProfile to speedscope format (for standalone use) */
649
+ declare function heapProfileToSpeedscope(name: string, profile: HeapProfile): SpeedscopeFile;
650
+ //#endregion
596
651
  //#region src/HtmlDataPrep.d.ts
597
652
  interface PrepareHtmlOptions {
598
653
  cliArgs?: Record<string, unknown>;
@@ -622,13 +677,13 @@ interface MatrixFilter {
622
677
  case?: string;
623
678
  variant?: string;
624
679
  }
625
- /** Parse filter string: "case/variant", "case/", "/variant", or "case" */
626
- declare function parseMatrixFilter(filter: string): MatrixFilter;
627
680
  /** Filtered matrix with explicit case and variant lists */
628
681
  interface FilteredMatrix<T = unknown> extends BenchMatrix<T> {
629
682
  filteredCases?: string[];
630
683
  filteredVariants?: string[];
631
684
  }
685
+ /** Parse filter string: "case/variant", "case/", "/variant", or "case" */
686
+ declare function parseMatrixFilter(filter: string): MatrixFilter;
632
687
  /** Apply filter to a matrix, merging with existing filters via intersection */
633
688
  declare function filterMatrix<T>(matrix: FilteredMatrix<T>, filter?: MatrixFilter): Promise<FilteredMatrix<T>>;
634
689
  //#endregion
@@ -638,13 +693,9 @@ interface TimeStats {
638
693
  p50?: number;
639
694
  p99?: number;
640
695
  }
641
- /** Section: mean, p50, p99 timing */
642
- declare const timeSection: ResultsMapper<TimeStats>;
643
696
  interface GcSectionStats {
644
697
  gc?: number;
645
698
  }
646
- /** Section: GC time as fraction of total benchmark time (Node performance hooks) */
647
- declare const gcSection: ResultsMapper<GcSectionStats>;
648
699
  interface GcStatsInfo {
649
700
  allocPerIter?: number;
650
701
  collected?: number;
@@ -653,42 +704,46 @@ interface GcStatsInfo {
653
704
  promoPercent?: number;
654
705
  pausePerIter?: number;
655
706
  }
656
- /** Section: detailed GC stats from --trace-gc-nvp (allocation, promotion, pauses) */
657
- declare const gcStatsSection: ResultsMapper<GcStatsInfo>;
658
707
  interface CpuStats {
659
708
  cpuCacheMiss?: number;
660
709
  cpuStall?: number;
661
710
  }
662
- /** Section: CPU L1 cache miss rate and stall rate (requires @mitata/counters) */
663
- declare const cpuSection: ResultsMapper<CpuStats>;
664
711
  interface RunStats {
665
712
  runs?: number;
666
713
  }
667
- /** Section: number of sample iterations */
668
- declare const runsSection: ResultsMapper<RunStats>;
669
- /** Section: total sampling duration in seconds (brackets if >= 30s) */
670
- declare const totalTimeSection: ResultsMapper<{
671
- totalTime?: number;
672
- }>;
673
714
  interface AdaptiveStats {
674
715
  median?: number;
675
716
  mean?: number;
676
717
  p99?: number;
677
718
  convergence?: number;
678
719
  }
720
+ interface OptStats {
721
+ tiers?: string;
722
+ deopt?: number;
723
+ }
724
+ /** Section: mean, p50, p99 timing */
725
+ declare const timeSection: ResultsMapper<TimeStats>;
726
+ /** Section: GC time as fraction of total benchmark time (Node performance hooks) */
727
+ declare const gcSection: ResultsMapper<GcSectionStats>;
728
+ /** Section: detailed GC stats from --trace-gc-nvp (allocation, promotion, pauses) */
729
+ declare const gcStatsSection: ResultsMapper<GcStatsInfo>;
730
+ /** Section: CPU L1 cache miss rate and stall rate (requires @mitata/counters) */
731
+ declare const cpuSection: ResultsMapper<CpuStats>;
732
+ /** Section: number of sample iterations */
733
+ declare const runsSection: ResultsMapper<RunStats>;
734
+ /** Section: total sampling duration in seconds (brackets if >= 30s) */
735
+ declare const totalTimeSection: ResultsMapper<{
736
+ totalTime?: number;
737
+ }>;
679
738
  /** Section: median, mean, p99, and convergence for adaptive mode */
680
739
  declare const adaptiveSection: ResultsMapper<AdaptiveStats>;
740
+ /** Section: V8 optimization tier distribution and deopt count */
741
+ declare const optSection: ResultsMapper<OptStats>;
681
742
  /** Build generic sections based on CLI flags */
682
743
  declare function buildGenericSections(args: {
683
744
  "gc-stats"?: boolean;
684
745
  "heap-sample"?: boolean;
685
746
  }): ResultsMapper[];
686
- interface OptStats {
687
- tiers?: string;
688
- deopt?: number;
689
- }
690
- /** Section: V8 optimization tier distribution and deopt count */
691
- declare const optSection: ResultsMapper<OptStats>;
692
747
  //#endregion
693
748
  //#region src/StatisticalUtils.d.ts
694
749
  /** @return mean of values */
@@ -703,10 +758,13 @@ declare function formatConvergence(v: unknown): string;
703
758
  declare function timeMs(ms: unknown): string | null;
704
759
  /** Format integer with thousand separators */
705
760
  declare function integer(x: unknown): string | null;
706
- /** Format bytes with appropriate units (B, KB, MB, GB) */
707
- declare function formatBytes(bytes: unknown): string | null;
761
+ /** Format bytes with appropriate units (B, KB, MB, GB).
762
+ * Use `space: true` for human-readable console output (`1.5 KB`). */
763
+ declare function formatBytes(bytes: unknown, opts?: {
764
+ space?: boolean;
765
+ }): string | null;
708
766
  /** @return truncated string with ellipsis if over maxLen */
709
767
  declare function truncate(str: string, maxLen?: number): string;
710
768
  //#endregion
711
- export { type AnyVariant, type BenchGroup, type BenchMatrix, type BenchSuite, BenchmarkGroup, BenchmarkJsonData, type BenchmarkReport, BenchmarkResult, type BenchmarkSpec, BenchmarkSuite, type CaseResult, type CasesModule, type Configure, type DefaultCliArgs, type ExportOptions, type ExtraColumn, type FilteredMatrix, type GitVersion, type HtmlReportOptions, type LoadedCase, type MatrixDefaults, type MatrixExportOptions, type MatrixFilter, type MatrixReportOptions, type MatrixResults, type MatrixSuite, type MeasuredResults, type PrepareHtmlOptions, type ReportColumnGroup, type ReportData, type ReportGroup, type ResultsMapper, type RunMatrixOptions, type RunnerOptions, type StatefulVariant, type UnknownRecord, type Variant, type VariantFn, type VariantResult, adaptiveSection, average, benchExports, buildGenericSections, cliToMatrixOptions, cpuSection, defaultCliArgs, defaultMatrixReport, defaultReport, exportPerfettoTrace, exportReports, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, gcPauseColumn, gcSection, gcStatsColumns, gcStatsSection, generateHtmlReport, getBaselineVersion, getCurrentGitVersion, hasField, heapTotalColumn, integer, isStatefulVariant, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
769
+ export { type AnyVariant, type BenchGroup, type BenchMatrix, type BenchSuite, BenchmarkGroup, BenchmarkJsonData, type BenchmarkReport, BenchmarkResult, type BenchmarkSpec, BenchmarkSuite, type CaseResult, type CasesModule, type Configure, type DefaultCliArgs, type ExportOptions, type ExtraColumn, type FilteredMatrix, type GitVersion, type HtmlReportOptions, type LoadedCase, type MatrixDefaults, type MatrixExportOptions, type MatrixFilter, type MatrixReportOptions, type MatrixResults, type MatrixSuite, type MeasuredResults, type PrepareHtmlOptions, type ReportColumnGroup, type ReportData, type ReportGroup, type ResultsMapper, type RunMatrixOptions, type RunnerOptions, type StatefulVariant, type UnknownRecord, type Variant, type VariantFn, type VariantResult, adaptiveSection, average, benchExports, buildGenericSections, cliToMatrixOptions, cpuSection, defaultCliArgs, defaultMatrixReport, defaultReport, exportAndLaunchSpeedscope, exportPerfettoTrace, exportReports, exportSpeedscope, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, gcPauseColumn, gcSection, gcStatsColumns, gcStatsSection, generateHtmlReport, getBaselineVersion, getCurrentGitVersion, hasField, heapProfileToSpeedscope, heapTotalColumn, integer, isStatefulVariant, launchSpeedscope, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
712
770
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as timeSection, B as parseCliArgs, C as adaptiveSection, D as gcStatsSection, E as gcSection, F as generateHtmlReport, G as truncate, H as formatBytes, I as formatDateWithTimezone, J as loadCaseData, K as isStatefulVariant, L as prepareHtmlData, M as formatConvergence, N as filterMatrix, O as optSection, P as parseMatrixFilter, R as exportPerfettoTrace, S as reportMatrixResults, T as cpuSection, U as integer, V as reportResults, W as timeMs, Y as loadCasesModule, _ as runDefaultMatrixBench, a as cliToMatrixOptions, b as gcStatsColumns, c as exportReports, d as matrixToReportGroups, f as parseBenchArgs, g as runDefaultBench, h as runBenchmarks, i as benchExports, j as totalTimeSection, k as runsSection, l as hasField, m as reportOptStatus, n as getBaselineVersion, o as defaultMatrixReport, p as printHeapReports, q as runMatrix, r as getCurrentGitVersion, s as defaultReport, t as formatGitVersion, u as matrixBenchExports, v as runMatrixSuite, w as buildGenericSections, x as heapTotalColumn, y as gcPauseColumn, z as defaultCliArgs } from "./src-HfimYuW_.mjs";
2
- import { o as average } from "./TimingUtils-ClclVQ7E.mjs";
1
+ import { $ as loadCasesModule, A as timeSection, B as heapProfileToSpeedscope, C as adaptiveSection, D as gcStatsSection, E as gcSection, F as generateHtmlReport, G as reportResults, H as exportPerfettoTrace, I as formatDateWithTimezone, J as timeMs, K as formatBytes, L as prepareHtmlData, M as formatConvergence, N as filterMatrix, O as optSection, P as parseMatrixFilter, Q as loadCaseData, R as exportAndLaunchSpeedscope, S as reportMatrixResults, T as cpuSection, U as defaultCliArgs, V as launchSpeedscope, W as parseCliArgs, X as isStatefulVariant, Y as truncate, Z as runMatrix, _ as runDefaultMatrixBench, a as cliToMatrixOptions, b as gcStatsColumns, c as exportReports, d as matrixToReportGroups, f as parseBenchArgs, g as runDefaultBench, h as runBenchmarks, i as benchExports, j as totalTimeSection, k as runsSection, l as hasField, m as reportOptStatus, n as getBaselineVersion, o as defaultMatrixReport, p as printHeapReports, q as integer, r as getCurrentGitVersion, s as defaultReport, t as formatGitVersion, u as matrixBenchExports, v as runMatrixSuite, w as buildGenericSections, x as heapTotalColumn, y as gcPauseColumn, z as exportSpeedscope } from "./src-B-DDaCa9.mjs";
2
+ import { o as average } from "./TimingUtils-DwOwkc8G.mjs";
3
3
 
4
- export { adaptiveSection, average, benchExports, buildGenericSections, cliToMatrixOptions, cpuSection, defaultCliArgs, defaultMatrixReport, defaultReport, exportPerfettoTrace, exportReports, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, gcPauseColumn, gcSection, gcStatsColumns, gcStatsSection, generateHtmlReport, getBaselineVersion, getCurrentGitVersion, hasField, heapTotalColumn, integer, isStatefulVariant, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
4
+ export { adaptiveSection, average, benchExports, buildGenericSections, cliToMatrixOptions, cpuSection, defaultCliArgs, defaultMatrixReport, defaultReport, exportAndLaunchSpeedscope, exportPerfettoTrace, exportReports, exportSpeedscope, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, gcPauseColumn, gcSection, gcStatsColumns, gcStatsSection, generateHtmlReport, getBaselineVersion, getCurrentGitVersion, hasField, heapProfileToSpeedscope, heapTotalColumn, integer, isStatefulVariant, launchSpeedscope, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
@@ -1,4 +1,4 @@
1
- import { a as MeasuredResults, i as BenchmarkSpec, o as HeapProfile, t as RunnerOptions } from "../BenchRunner-CSKN9zPy.mjs";
1
+ import { a as MeasuredResults, i as BenchmarkSpec, o as HeapProfile, t as RunnerOptions } from "../BenchRunner-BzyUfiyB.mjs";
2
2
 
3
3
  //#region src/runners/CreateRunner.d.ts
4
4
  type KnownRunner = "basic";
@@ -1,61 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { a as createAdaptiveWrapper, d as variantModuleUrl, i as createRunner, n as getElapsed, r as getPerfNow, t as debugWorkerTiming } from "../TimingUtils-ClclVQ7E.mjs";
2
+ import { a as createAdaptiveWrapper, d as variantModuleUrl, i as createRunner, n as getElapsed, r as getPerfNow, t as debugWorkerTiming } from "../TimingUtils-DwOwkc8G.mjs";
3
3
 
4
4
  //#region src/runners/WorkerScript.ts
5
5
  const workerStartTime = getPerfNow();
6
6
  const maxLifetime = 300 * 1e3;
7
- /**
8
- * Worker process for isolated benchmark execution.
9
- * Uses eval() safely in isolated child process with trusted code.
10
- */
11
- process.on("message", async (message) => {
12
- if (message.type !== "run") return;
13
- logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);
14
- try {
15
- const start = getPerfNow();
16
- const baseRunner = await createRunner(message.runnerName);
17
- const runner = message.options.adaptive ? createAdaptiveWrapper(baseRunner, message.options) : baseRunner;
18
- logTiming("Runner created in", getElapsed(start));
19
- const benchStart = getPerfNow();
20
- if (message.options.heapSample) {
21
- const { withHeapSampling } = await import("../HeapSampler-B8dtKHn1.mjs");
22
- const { result: results, profile: heapProfile } = await withHeapSampling({
23
- samplingInterval: message.options.heapInterval,
24
- stackDepth: message.options.heapDepth
25
- }, async () => {
26
- const { fn, params } = await resolveBenchmarkFn(message);
27
- return runner.runBench({
28
- ...message.spec,
29
- fn
30
- }, message.options, params);
31
- });
32
- logTiming("Benchmark execution took", getElapsed(benchStart));
33
- sendAndExit({
34
- type: "result",
35
- results,
36
- heapProfile
37
- }, 0);
38
- } else {
39
- const { fn, params } = await resolveBenchmarkFn(message);
40
- const results = await runner.runBench({
41
- ...message.spec,
42
- fn
43
- }, message.options, params);
44
- logTiming("Benchmark execution took", getElapsed(benchStart));
45
- sendAndExit({
46
- type: "result",
47
- results
48
- }, 0);
49
- }
50
- } catch (error) {
51
- sendAndExit(createErrorMessage(error), 1);
52
- }
53
- });
54
- setTimeout(() => {
55
- console.error("WorkerScript: Maximum lifetime exceeded, exiting");
56
- process.exit(1);
57
- }, maxLifetime);
58
- process.stdin.pause();
59
7
  /** Log timing with consistent format */
60
8
  const logTiming = debugWorkerTiming ? _logTiming : () => {};
61
9
  function _logTiming(operation, duration) {
@@ -104,13 +52,6 @@ async function importVariantModule(message) {
104
52
  params: void 0
105
53
  };
106
54
  }
107
- /** Load case data from a cases module */
108
- async function loadCaseFromModule(casesModuleUrl, caseId) {
109
- logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);
110
- const module = await import(casesModuleUrl);
111
- if (typeof module.loadCase === "function") return module.loadCase(caseId);
112
- return { data: caseId };
113
- }
114
55
  /** Import benchmark function and optionally run setup */
115
56
  async function importBenchmarkWithSetup(message) {
116
57
  const { modulePath, exportName, setupExportName, params } = message;
@@ -129,6 +70,19 @@ async function importBenchmarkWithSetup(message) {
129
70
  params
130
71
  };
131
72
  }
73
+ /** Reconstruct function from string code */
74
+ function reconstructFunction(fnCode) {
75
+ const fn = eval(`(${fnCode})`);
76
+ if (typeof fn !== "function") throw new Error("Reconstructed code is not a function");
77
+ return fn;
78
+ }
79
+ /** Load case data from a cases module */
80
+ async function loadCaseFromModule(casesModuleUrl, caseId) {
81
+ logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);
82
+ const module = await import(casesModuleUrl);
83
+ if (typeof module.loadCase === "function") return module.loadCase(caseId);
84
+ return { data: caseId };
85
+ }
132
86
  /** Get named or default export from module */
133
87
  function getModuleExport(module, exportName, modulePath) {
134
88
  const fn = exportName ? module[exportName] : module.default || module;
@@ -138,12 +92,6 @@ function getModuleExport(module, exportName, modulePath) {
138
92
  }
139
93
  return fn;
140
94
  }
141
- /** Reconstruct function from string code */
142
- function reconstructFunction(fnCode) {
143
- const fn = eval(`(${fnCode})`);
144
- if (typeof fn !== "function") throw new Error("Reconstructed code is not a function");
145
- return fn;
146
- }
147
95
  /** Create error message from exception */
148
96
  function createErrorMessage(error) {
149
97
  return {
@@ -152,6 +100,58 @@ function createErrorMessage(error) {
152
100
  stack: error instanceof Error ? error.stack : void 0
153
101
  };
154
102
  }
103
+ /**
104
+ * Worker process for isolated benchmark execution.
105
+ * Uses eval() safely in isolated child process with trusted code.
106
+ */
107
+ process.on("message", async (message) => {
108
+ if (message.type !== "run") return;
109
+ logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);
110
+ try {
111
+ const start = getPerfNow();
112
+ const baseRunner = await createRunner(message.runnerName);
113
+ const runner = message.options.adaptive ? createAdaptiveWrapper(baseRunner, message.options) : baseRunner;
114
+ logTiming("Runner created in", getElapsed(start));
115
+ const benchStart = getPerfNow();
116
+ if (message.options.heapSample) {
117
+ const { withHeapSampling } = await import("../HeapSampler-B8dtKHn1.mjs");
118
+ const { result: results, profile: heapProfile } = await withHeapSampling({
119
+ samplingInterval: message.options.heapInterval,
120
+ stackDepth: message.options.heapDepth
121
+ }, async () => {
122
+ const { fn, params } = await resolveBenchmarkFn(message);
123
+ return runner.runBench({
124
+ ...message.spec,
125
+ fn
126
+ }, message.options, params);
127
+ });
128
+ logTiming("Benchmark execution took", getElapsed(benchStart));
129
+ sendAndExit({
130
+ type: "result",
131
+ results,
132
+ heapProfile
133
+ }, 0);
134
+ } else {
135
+ const { fn, params } = await resolveBenchmarkFn(message);
136
+ const results = await runner.runBench({
137
+ ...message.spec,
138
+ fn
139
+ }, message.options, params);
140
+ logTiming("Benchmark execution took", getElapsed(benchStart));
141
+ sendAndExit({
142
+ type: "result",
143
+ results
144
+ }, 0);
145
+ }
146
+ } catch (error) {
147
+ sendAndExit(createErrorMessage(error), 1);
148
+ }
149
+ });
150
+ setTimeout(() => {
151
+ console.error("WorkerScript: Maximum lifetime exceeded, exiting");
152
+ process.exit(1);
153
+ }, maxLifetime);
154
+ process.stdin.pause();
155
155
 
156
156
  //#endregion
157
157
  export { };
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerScript.mjs","names":[],"sources":["../../src/runners/WorkerScript.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { BenchmarkFunction, BenchmarkSpec } from \"../Benchmark.ts\";\nimport type { HeapProfile } from \"../heap-sample/HeapSampler.ts\";\nimport type { MeasuredResults } from \"../MeasuredResults.ts\";\nimport { variantModuleUrl } from \"../matrix/VariantLoader.ts\";\nimport {\n type AdaptiveOptions,\n createAdaptiveWrapper,\n} from \"./AdaptiveWrapper.ts\";\nimport type { RunnerOptions } from \"./BenchRunner.ts\";\nimport { createRunner, type KnownRunner } from \"./CreateRunner.ts\";\nimport { debugWorkerTiming, getElapsed, getPerfNow } from \"./TimingUtils.ts\";\n\nconst workerStartTime = getPerfNow();\nconst maxLifetime = 5 * 60 * 1000; // 5 minutes\n\n/** Message sent to worker process to start a benchmark run. */\nexport interface RunMessage {\n type: \"run\";\n spec: BenchmarkSpec;\n runnerName: KnownRunner;\n options: RunnerOptions;\n fnCode?: string; // Made optional - either fnCode or modulePath is required\n modulePath?: string; // Path to module for dynamic import\n exportName?: string; // Export name from module\n setupExportName?: string; // Setup function export name - called once, result passed to fn\n params?: unknown;\n // Variant directory mode (BenchMatrix)\n variantDir?: string; // Directory URL containing variant .ts files\n variantId?: string; // Variant filename (without .ts)\n caseData?: unknown; // Data to pass to variant\n caseId?: string; // Case identifier\n casesModule?: string; // URL to cases module (exports cases[] and loadCase())\n}\n\n/** Message returned from worker process with benchmark results. */\nexport interface ResultMessage {\n type: \"result\";\n results: MeasuredResults[];\n heapProfile?: HeapProfile;\n}\n\n/** Message returned from worker process when benchmark fails. */\nexport interface ErrorMessage {\n type: \"error\";\n error: string;\n stack?: string;\n}\n\nexport type WorkerMessage = RunMessage | ResultMessage | ErrorMessage;\n\n/**\n * Worker process for isolated benchmark execution.\n * Uses eval() safely in isolated child process with trusted code.\n */\nprocess.on(\"message\", async (message: RunMessage) => {\n if (message.type !== \"run\") return;\n\n logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);\n\n try {\n const start = getPerfNow();\n const baseRunner = await createRunner(message.runnerName);\n\n const runner = (message.options as any).adaptive\n ? createAdaptiveWrapper(baseRunner, message.options as AdaptiveOptions)\n : baseRunner;\n\n logTiming(\"Runner created in\", getElapsed(start));\n\n const benchStart = getPerfNow();\n\n // Run with heap sampling if enabled (covers module import + execution)\n if (message.options.heapSample) {\n const { withHeapSampling } = await import(\n \"../heap-sample/HeapSampler.ts\"\n );\n const heapOpts = {\n samplingInterval: message.options.heapInterval,\n stackDepth: message.options.heapDepth,\n };\n const { result: results, profile: heapProfile } = await withHeapSampling(\n heapOpts,\n async () => {\n const { fn, params } = await resolveBenchmarkFn(message);\n return runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n },\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results, heapProfile }, 0);\n } else {\n const { fn, params } = await resolveBenchmarkFn(message);\n const results = await runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results }, 0);\n }\n } catch (error) {\n sendAndExit(createErrorMessage(error), 1);\n }\n});\n\n// Exit after 5 minutes to prevent zombie processes\nsetTimeout(() => {\n console.error(\"WorkerScript: Maximum lifetime exceeded, exiting\");\n process.exit(1);\n}, maxLifetime);\n\nprocess.stdin.pause();\n\n/** Log timing with consistent format */\nconst logTiming = debugWorkerTiming ? _logTiming : () => {};\nfunction _logTiming(operation: string, duration?: number) {\n if (duration === undefined) {\n console.log(`[Worker] ${operation}`);\n } else {\n console.log(`[Worker] ${operation} ${duration.toFixed(1)}ms`);\n }\n}\n\n/** Send message and exit with duration log */\nfunction sendAndExit(msg: ResultMessage | ErrorMessage, exitCode: number) {\n process.send!(msg, undefined, undefined, (err: Error | null): void => {\n if (err) {\n const kind = msg.type === \"result\" ? \"results\" : \"error message\";\n console.error(`[Worker] Error sending ${kind}:`, err);\n }\n const suffix = exitCode === 0 ? \"\" : \" (error)\";\n logTiming(`Total worker duration${suffix}:`, getElapsed(workerStartTime));\n process.exit(exitCode);\n });\n}\n\ninterface BenchmarkImportResult {\n fn: BenchmarkFunction;\n params: unknown;\n}\n\n/** Resolve benchmark function from message (variant dir, module path, or fnCode) */\nasync function resolveBenchmarkFn(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n if (message.variantDir && message.variantId) {\n return importVariantModule(message);\n }\n if (message.modulePath) {\n return importBenchmarkWithSetup(message);\n }\n return { fn: reconstructFunction(message.fnCode!), params: message.params };\n}\n\n/** Import variant from directory and prepare benchmark function */\nasync function importVariantModule(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { variantDir, variantId, caseId, casesModule } = message;\n let { caseData } = message;\n const moduleUrl = variantModuleUrl(variantDir!, variantId!);\n logTiming(`Importing variant ${variantId} from ${variantDir}`);\n\n if (casesModule && caseId) {\n caseData = (await loadCaseFromModule(casesModule, caseId)).data;\n }\n\n const module = await import(moduleUrl);\n const { setup, run } = module;\n\n if (typeof run !== \"function\") {\n throw new Error(`Variant '${variantId}' must export 'run' function`);\n }\n\n // Stateful variant: setup returns state, run receives state\n if (typeof setup === \"function\") {\n logTiming(`Calling setup for ${variantId}`);\n const state = await setup(caseData);\n return { fn: () => run(state), params: undefined };\n }\n\n // Stateless variant: run receives caseData directly\n return { fn: () => run(caseData), params: undefined };\n}\n\n/** Load case data from a cases module */\nasync function loadCaseFromModule(\n casesModuleUrl: string,\n caseId: string,\n): Promise<{ data: unknown; metadata?: Record<string, unknown> }> {\n logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);\n const module = await import(casesModuleUrl);\n if (typeof module.loadCase === \"function\") {\n return module.loadCase(caseId);\n }\n return { data: caseId };\n}\n\n/** Import benchmark function and optionally run setup */\nasync function importBenchmarkWithSetup(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { modulePath, exportName, setupExportName, params } = message;\n logTiming(\n `Importing from ${modulePath}${exportName ? ` (${exportName})` : \"\"}`,\n );\n const module = await import(modulePath!);\n\n const fn = getModuleExport(module, exportName, modulePath!);\n\n if (setupExportName) {\n logTiming(`Calling setup: ${setupExportName}`);\n const setupFn = getModuleExport(module, setupExportName, modulePath!);\n const setupResult = await setupFn(params);\n return { fn, params: setupResult };\n }\n\n return { fn, params };\n}\n\n/** Get named or default export from module */\nfunction getModuleExport(\n module: any,\n exportName: string | undefined,\n modulePath: string,\n): BenchmarkFunction {\n const fn = exportName ? module[exportName] : module.default || module;\n if (typeof fn !== \"function\") {\n const name = exportName || \"default\";\n throw new Error(`Export '${name}' from ${modulePath} is not a function`);\n }\n return fn;\n}\n\n/** Reconstruct function from string code */\nfunction reconstructFunction(fnCode: string): BenchmarkFunction {\n // biome-ignore lint/security/noGlobalEval: Necessary for worker process isolation, code is from trusted source\n const fn = eval(`(${fnCode})`); // eslint-disable-line no-eval\n if (typeof fn !== \"function\") {\n throw new Error(\"Reconstructed code is not a function\");\n }\n return fn;\n}\n\n/** Create error message from exception */\nfunction createErrorMessage(error: unknown): ErrorMessage {\n return {\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WorkerScript.mjs","names":[],"sources":["../../src/runners/WorkerScript.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { BenchmarkFunction, BenchmarkSpec } from \"../Benchmark.ts\";\nimport type { HeapProfile } from \"../heap-sample/HeapSampler.ts\";\nimport type { MeasuredResults } from \"../MeasuredResults.ts\";\nimport { variantModuleUrl } from \"../matrix/VariantLoader.ts\";\nimport {\n type AdaptiveOptions,\n createAdaptiveWrapper,\n} from \"./AdaptiveWrapper.ts\";\nimport type { RunnerOptions } from \"./BenchRunner.ts\";\nimport { createRunner, type KnownRunner } from \"./CreateRunner.ts\";\nimport { debugWorkerTiming, getElapsed, getPerfNow } from \"./TimingUtils.ts\"; // 5 minutes\n\n/** Message sent to worker process to start a benchmark run. */\nexport interface RunMessage {\n type: \"run\";\n spec: BenchmarkSpec;\n runnerName: KnownRunner;\n options: RunnerOptions;\n fnCode?: string; // Made optional - either fnCode or modulePath is required\n modulePath?: string; // Path to module for dynamic import\n exportName?: string; // Export name from module\n setupExportName?: string; // Setup function export name - called once, result passed to fn\n params?: unknown;\n // Variant directory mode (BenchMatrix)\n variantDir?: string; // Directory URL containing variant .ts files\n variantId?: string; // Variant filename (without .ts)\n caseData?: unknown; // Data to pass to variant\n caseId?: string; // Case identifier\n casesModule?: string; // URL to cases module (exports cases[] and loadCase())\n}\n\n/** Message returned from worker process with benchmark results. */\nexport interface ResultMessage {\n type: \"result\";\n results: MeasuredResults[];\n heapProfile?: HeapProfile;\n}\n\n/** Message returned from worker process when benchmark fails. */\nexport interface ErrorMessage {\n type: \"error\";\n error: string;\n stack?: string;\n}\n\nexport type WorkerMessage = RunMessage | ResultMessage | ErrorMessage;\n\ninterface BenchmarkImportResult {\n fn: BenchmarkFunction;\n params: unknown;\n}\n\nconst workerStartTime = getPerfNow();\nconst maxLifetime = 5 * 60 * 1000;\n\n/** Log timing with consistent format */\nconst logTiming = debugWorkerTiming ? _logTiming : () => {};\nfunction _logTiming(operation: string, duration?: number) {\n if (duration === undefined) {\n console.log(`[Worker] ${operation}`);\n } else {\n console.log(`[Worker] ${operation} ${duration.toFixed(1)}ms`);\n }\n}\n\n/** Send message and exit with duration log */\nfunction sendAndExit(msg: ResultMessage | ErrorMessage, exitCode: number) {\n process.send!(msg, undefined, undefined, (err: Error | null): void => {\n if (err) {\n const kind = msg.type === \"result\" ? \"results\" : \"error message\";\n console.error(`[Worker] Error sending ${kind}:`, err);\n }\n const suffix = exitCode === 0 ? \"\" : \" (error)\";\n logTiming(`Total worker duration${suffix}:`, getElapsed(workerStartTime));\n process.exit(exitCode);\n });\n}\n\n/** Resolve benchmark function from message (variant dir, module path, or fnCode) */\nasync function resolveBenchmarkFn(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n if (message.variantDir && message.variantId) {\n return importVariantModule(message);\n }\n if (message.modulePath) {\n return importBenchmarkWithSetup(message);\n }\n return { fn: reconstructFunction(message.fnCode!), params: message.params };\n}\n\n/** Import variant from directory and prepare benchmark function */\nasync function importVariantModule(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { variantDir, variantId, caseId, casesModule } = message;\n let { caseData } = message;\n const moduleUrl = variantModuleUrl(variantDir!, variantId!);\n logTiming(`Importing variant ${variantId} from ${variantDir}`);\n\n if (casesModule && caseId) {\n caseData = (await loadCaseFromModule(casesModule, caseId)).data;\n }\n\n const module = await import(moduleUrl);\n const { setup, run } = module;\n\n if (typeof run !== \"function\") {\n throw new Error(`Variant '${variantId}' must export 'run' function`);\n }\n\n // Stateful variant: setup returns state, run receives state\n if (typeof setup === \"function\") {\n logTiming(`Calling setup for ${variantId}`);\n const state = await setup(caseData);\n return { fn: () => run(state), params: undefined };\n }\n\n // Stateless variant: run receives caseData directly\n return { fn: () => run(caseData), params: undefined };\n}\n\n/** Import benchmark function and optionally run setup */\nasync function importBenchmarkWithSetup(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { modulePath, exportName, setupExportName, params } = message;\n logTiming(\n `Importing from ${modulePath}${exportName ? ` (${exportName})` : \"\"}`,\n );\n const module = await import(modulePath!);\n\n const fn = getModuleExport(module, exportName, modulePath!);\n\n if (setupExportName) {\n logTiming(`Calling setup: ${setupExportName}`);\n const setupFn = getModuleExport(module, setupExportName, modulePath!);\n const setupResult = await setupFn(params);\n return { fn, params: setupResult };\n }\n\n return { fn, params };\n}\n\n/** Reconstruct function from string code */\nfunction reconstructFunction(fnCode: string): BenchmarkFunction {\n // biome-ignore lint/security/noGlobalEval: Necessary for worker process isolation, code is from trusted source\n const fn = eval(`(${fnCode})`); // eslint-disable-line no-eval\n if (typeof fn !== \"function\") {\n throw new Error(\"Reconstructed code is not a function\");\n }\n return fn;\n}\n\n/** Load case data from a cases module */\nasync function loadCaseFromModule(\n casesModuleUrl: string,\n caseId: string,\n): Promise<{ data: unknown; metadata?: Record<string, unknown> }> {\n logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);\n const module = await import(casesModuleUrl);\n if (typeof module.loadCase === \"function\") {\n return module.loadCase(caseId);\n }\n return { data: caseId };\n}\n\n/** Get named or default export from module */\nfunction getModuleExport(\n module: any,\n exportName: string | undefined,\n modulePath: string,\n): BenchmarkFunction {\n const fn = exportName ? module[exportName] : module.default || module;\n if (typeof fn !== \"function\") {\n const name = exportName || \"default\";\n throw new Error(`Export '${name}' from ${modulePath} is not a function`);\n }\n return fn;\n}\n\n/** Create error message from exception */\nfunction createErrorMessage(error: unknown): ErrorMessage {\n return {\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n}\n\n/**\n * Worker process for isolated benchmark execution.\n * Uses eval() safely in isolated child process with trusted code.\n */\nprocess.on(\"message\", async (message: RunMessage) => {\n if (message.type !== \"run\") return;\n\n logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);\n\n try {\n const start = getPerfNow();\n const baseRunner = await createRunner(message.runnerName);\n\n const runner = (message.options as any).adaptive\n ? createAdaptiveWrapper(baseRunner, message.options as AdaptiveOptions)\n : baseRunner;\n\n logTiming(\"Runner created in\", getElapsed(start));\n\n const benchStart = getPerfNow();\n\n // Run with heap sampling if enabled (covers module import + execution)\n if (message.options.heapSample) {\n const { withHeapSampling } = await import(\n \"../heap-sample/HeapSampler.ts\"\n );\n const heapOpts = {\n samplingInterval: message.options.heapInterval,\n stackDepth: message.options.heapDepth,\n };\n const { result: results, profile: heapProfile } = await withHeapSampling(\n heapOpts,\n async () => {\n const { fn, params } = await resolveBenchmarkFn(message);\n return runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n },\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results, heapProfile }, 0);\n } else {\n const { fn, params } = await resolveBenchmarkFn(message);\n const results = await runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results }, 0);\n }\n } catch (error) {\n sendAndExit(createErrorMessage(error), 1);\n }\n});\n\n// Exit after 5 minutes to prevent zombie processes\nsetTimeout(() => {\n console.error(\"WorkerScript: Maximum lifetime exceeded, exiting\");\n process.exit(1);\n}, maxLifetime);\n\nprocess.stdin.pause();\n"],"mappings":"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"}