benchforge 0.1.9 → 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 (66) hide show
  1. package/README.md +40 -6
  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 +102 -46
  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-Cf_LXwlp.mjs → src-B-DDaCa9.mjs} +1225 -990
  18. package/dist/src-B-DDaCa9.mjs.map +1 -0
  19. package/package.json +2 -1
  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 +6 -3
  29. package/src/cli/FilterBenchmarks.ts +5 -5
  30. package/src/cli/RunBenchCLI.ts +526 -498
  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 +18 -18
  60. package/src/test/TestUtils.ts +24 -24
  61. package/src/tests/BenchMatrix.test.ts +12 -12
  62. package/src/tests/MatrixFilter.test.ts +15 -15
  63. package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
  64. package/dist/GcStats-ByEovUi1.mjs.map +0 -1
  65. package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
  66. package/dist/src-Cf_LXwlp.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 */
@@ -219,11 +219,21 @@ declare const cliOptions: {
219
219
  readonly requiresArg: true;
220
220
  readonly describe: "export benchmark data to JSON file";
221
221
  };
222
- readonly perfetto: {
222
+ readonly "export-perfetto": {
223
223
  readonly type: "string";
224
224
  readonly requiresArg: true;
225
225
  readonly describe: "export Perfetto trace file (view at ui.perfetto.dev)";
226
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
+ };
227
237
  readonly "trace-opt": {
228
238
  readonly type: "boolean";
229
239
  readonly default: false;
@@ -288,6 +298,11 @@ declare const cliOptions: {
288
298
  readonly default: false;
289
299
  readonly describe: "verbose output with file:// paths and line numbers";
290
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
+ };
291
306
  readonly "heap-user-only": {
292
307
  readonly type: "boolean";
293
308
  readonly default: false;
@@ -424,13 +439,14 @@ interface CallFrame {
424
439
  fn: string;
425
440
  url: string;
426
441
  line: number;
427
- col: number;
442
+ col?: number;
428
443
  }
429
444
  type UserCodeFilter = (site: CallFrame) => boolean;
430
445
  interface HeapReportOptions {
431
446
  topN: number;
432
447
  stackDepth?: number;
433
448
  verbose?: boolean;
449
+ raw?: boolean;
434
450
  userOnly?: boolean;
435
451
  isUserCode?: UserCodeFilter;
436
452
  totalAll?: number;
@@ -453,16 +469,29 @@ interface MatrixReportOptions {
453
469
  sections?: ResultsMapper[];
454
470
  variantTitle?: string;
455
471
  }
456
- /** Format matrix results as one table per case */
457
- declare function reportMatrixResults(results: MatrixResults, options?: MatrixReportOptions): string;
458
472
  /** GC statistics columns - derived from gcStatsSection for consistency */
459
473
  declare const gcStatsColumns: ExtraColumn[];
460
474
  /** GC pause time column */
461
475
  declare const gcPauseColumn: ExtraColumn;
462
476
  /** Heap sampling total bytes column */
463
477
  declare const heapTotalColumn: ExtraColumn;
478
+ /** Format matrix results as one table per case */
479
+ declare function reportMatrixResults(results: MatrixResults, options?: MatrixReportOptions): string;
464
480
  //#endregion
465
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
+ }
466
495
  /** Parse CLI with custom configuration */
467
496
  declare function parseBenchArgs<T = DefaultCliArgs>(configureArgs?: Configure<T>): T & DefaultCliArgs;
468
497
  /** Run suite with CLI arguments */
@@ -479,14 +508,6 @@ declare function runDefaultBench(suite?: BenchSuite, configureArgs?: Configure<a
479
508
  declare function reportOptStatus(groups: ReportGroup[]): void;
480
509
  /** @return true if any result has the specified field with a defined value */
481
510
  declare function hasField(results: ReportGroup[], field: keyof MeasuredResults): boolean;
482
- interface ExportOptions {
483
- results: ReportGroup[];
484
- args: DefaultCliArgs;
485
- sections?: any[];
486
- suiteName?: string;
487
- currentVersion?: GitVersion;
488
- baselineVersion?: GitVersion;
489
- }
490
511
  /** Export reports (HTML, JSON, Perfetto) based on CLI args */
491
512
  declare function exportReports(options: ExportOptions): Promise<void>;
492
513
  /** Run matrix suite with CLI arguments.
@@ -501,11 +522,6 @@ declare function defaultMatrixReport(results: MatrixResults[], reportOptions?: M
501
522
  declare function runDefaultMatrixBench(suite: MatrixSuite, configureArgs?: Configure<any>, reportOptions?: MatrixReportOptions): Promise<void>;
502
523
  /** Convert MatrixResults to ReportGroup[] for export compatibility */
503
524
  declare function matrixToReportGroups(results: MatrixResults[]): ReportGroup[];
504
- interface MatrixExportOptions {
505
- sections?: any[];
506
- currentVersion?: GitVersion;
507
- baselineVersion?: GitVersion;
508
- }
509
525
  /** Run matrix benchmarks, display table, and generate exports */
510
526
  declare function matrixBenchExports(suite: MatrixSuite, args: DefaultCliArgs, reportOptions?: MatrixReportOptions, exportOptions?: MatrixExportOptions): Promise<void>;
511
527
  //#endregion
@@ -595,6 +611,43 @@ interface BenchmarkResult {
595
611
  /** Export benchmark results to Perfetto-compatible trace file */
596
612
  declare function exportPerfettoTrace(groups: ReportGroup[], outputPath: string, args: DefaultCliArgs): void;
597
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
598
651
  //#region src/HtmlDataPrep.d.ts
599
652
  interface PrepareHtmlOptions {
600
653
  cliArgs?: Record<string, unknown>;
@@ -624,13 +677,13 @@ interface MatrixFilter {
624
677
  case?: string;
625
678
  variant?: string;
626
679
  }
627
- /** Parse filter string: "case/variant", "case/", "/variant", or "case" */
628
- declare function parseMatrixFilter(filter: string): MatrixFilter;
629
680
  /** Filtered matrix with explicit case and variant lists */
630
681
  interface FilteredMatrix<T = unknown> extends BenchMatrix<T> {
631
682
  filteredCases?: string[];
632
683
  filteredVariants?: string[];
633
684
  }
685
+ /** Parse filter string: "case/variant", "case/", "/variant", or "case" */
686
+ declare function parseMatrixFilter(filter: string): MatrixFilter;
634
687
  /** Apply filter to a matrix, merging with existing filters via intersection */
635
688
  declare function filterMatrix<T>(matrix: FilteredMatrix<T>, filter?: MatrixFilter): Promise<FilteredMatrix<T>>;
636
689
  //#endregion
@@ -640,13 +693,9 @@ interface TimeStats {
640
693
  p50?: number;
641
694
  p99?: number;
642
695
  }
643
- /** Section: mean, p50, p99 timing */
644
- declare const timeSection: ResultsMapper<TimeStats>;
645
696
  interface GcSectionStats {
646
697
  gc?: number;
647
698
  }
648
- /** Section: GC time as fraction of total benchmark time (Node performance hooks) */
649
- declare const gcSection: ResultsMapper<GcSectionStats>;
650
699
  interface GcStatsInfo {
651
700
  allocPerIter?: number;
652
701
  collected?: number;
@@ -655,42 +704,46 @@ interface GcStatsInfo {
655
704
  promoPercent?: number;
656
705
  pausePerIter?: number;
657
706
  }
658
- /** Section: detailed GC stats from --trace-gc-nvp (allocation, promotion, pauses) */
659
- declare const gcStatsSection: ResultsMapper<GcStatsInfo>;
660
707
  interface CpuStats {
661
708
  cpuCacheMiss?: number;
662
709
  cpuStall?: number;
663
710
  }
664
- /** Section: CPU L1 cache miss rate and stall rate (requires @mitata/counters) */
665
- declare const cpuSection: ResultsMapper<CpuStats>;
666
711
  interface RunStats {
667
712
  runs?: number;
668
713
  }
669
- /** Section: number of sample iterations */
670
- declare const runsSection: ResultsMapper<RunStats>;
671
- /** Section: total sampling duration in seconds (brackets if >= 30s) */
672
- declare const totalTimeSection: ResultsMapper<{
673
- totalTime?: number;
674
- }>;
675
714
  interface AdaptiveStats {
676
715
  median?: number;
677
716
  mean?: number;
678
717
  p99?: number;
679
718
  convergence?: number;
680
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
+ }>;
681
738
  /** Section: median, mean, p99, and convergence for adaptive mode */
682
739
  declare const adaptiveSection: ResultsMapper<AdaptiveStats>;
740
+ /** Section: V8 optimization tier distribution and deopt count */
741
+ declare const optSection: ResultsMapper<OptStats>;
683
742
  /** Build generic sections based on CLI flags */
684
743
  declare function buildGenericSections(args: {
685
744
  "gc-stats"?: boolean;
686
745
  "heap-sample"?: boolean;
687
746
  }): ResultsMapper[];
688
- interface OptStats {
689
- tiers?: string;
690
- deopt?: number;
691
- }
692
- /** Section: V8 optimization tier distribution and deopt count */
693
- declare const optSection: ResultsMapper<OptStats>;
694
747
  //#endregion
695
748
  //#region src/StatisticalUtils.d.ts
696
749
  /** @return mean of values */
@@ -705,10 +758,13 @@ declare function formatConvergence(v: unknown): string;
705
758
  declare function timeMs(ms: unknown): string | null;
706
759
  /** Format integer with thousand separators */
707
760
  declare function integer(x: unknown): string | null;
708
- /** Format bytes with appropriate units (B, KB, MB, GB) */
709
- 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;
710
766
  /** @return truncated string with ellipsis if over maxLen */
711
767
  declare function truncate(str: string, maxLen?: number): string;
712
768
  //#endregion
713
- 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 };
714
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-Cf_LXwlp.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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"}