benchforge 0.1.9 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +99 -260
  3. package/bin/benchforge +1 -2
  4. package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
  5. package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
  6. package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
  7. package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
  8. package/dist/BenchRunner-DglX1NOn.d.mts +302 -0
  9. package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
  10. package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
  11. package/dist/Formatters-BWj3d4sv.mjs +95 -0
  12. package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
  13. package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
  14. package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
  15. package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
  16. package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
  17. package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
  18. package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
  19. package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
  20. package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
  21. package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
  22. package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
  23. package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
  24. package/dist/bin/benchforge.mjs +4 -5
  25. package/dist/bin/benchforge.mjs.map +1 -1
  26. package/dist/index.d.mts +731 -522
  27. package/dist/index.mjs +98 -3
  28. package/dist/index.mjs.map +1 -0
  29. package/dist/runners/WorkerScript.d.mts +12 -4
  30. package/dist/runners/WorkerScript.mjs +92 -120
  31. package/dist/runners/WorkerScript.mjs.map +1 -1
  32. package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
  33. package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
  34. package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
  35. package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
  36. package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
  37. package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
  38. package/dist/viewer/index.html +19 -0
  39. package/dist/viewer/speedscope/LICENSE +21 -0
  40. package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
  41. package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
  42. package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
  43. package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
  44. package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
  45. package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
  46. package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
  47. package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
  48. package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
  49. package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
  50. package/dist/viewer/speedscope/file-format-schema.json +274 -0
  51. package/dist/viewer/speedscope/index.html +19 -0
  52. package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
  53. package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
  54. package/dist/viewer/speedscope/release.txt +3 -0
  55. package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
  56. package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
  57. package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
  58. package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
  59. package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
  60. package/package.json +52 -26
  61. package/src/bin/benchforge.ts +2 -2
  62. package/src/cli/AnalyzeArchive.ts +232 -0
  63. package/src/cli/BrowserBench.ts +322 -0
  64. package/src/cli/CliArgs.ts +164 -48
  65. package/src/cli/CliExport.ts +179 -0
  66. package/src/cli/CliOptions.ts +147 -0
  67. package/src/cli/CliReport.ts +197 -0
  68. package/src/cli/FilterBenchmarks.ts +18 -30
  69. package/src/cli/RunBenchCLI.ts +138 -844
  70. package/src/cli/SuiteRunner.ts +160 -0
  71. package/src/cli/ViewerServer.ts +282 -0
  72. package/src/export/AllocExport.ts +121 -0
  73. package/src/export/ArchiveExport.ts +146 -0
  74. package/src/export/ArchiveFormat.ts +50 -0
  75. package/src/export/CoverageExport.ts +148 -0
  76. package/src/export/EditorUri.ts +10 -0
  77. package/src/export/PerfettoExport.ts +91 -126
  78. package/src/export/SpeedscopeTypes.ts +98 -0
  79. package/src/export/TimeExport.ts +115 -0
  80. package/src/index.ts +87 -62
  81. package/src/matrix/BenchMatrix.ts +230 -0
  82. package/src/matrix/CaseLoader.ts +8 -6
  83. package/src/matrix/MatrixDirRunner.ts +153 -0
  84. package/src/matrix/MatrixFilter.ts +55 -53
  85. package/src/matrix/MatrixInlineRunner.ts +50 -0
  86. package/src/matrix/MatrixReport.ts +94 -254
  87. package/src/matrix/VariantLoader.ts +9 -9
  88. package/src/profiling/browser/BenchLoop.ts +51 -0
  89. package/src/profiling/browser/BrowserCDP.ts +133 -0
  90. package/src/profiling/browser/BrowserGcStats.ts +33 -0
  91. package/src/profiling/browser/BrowserProfiler.ts +160 -0
  92. package/src/profiling/browser/CdpClient.ts +82 -0
  93. package/src/profiling/browser/CdpPage.ts +138 -0
  94. package/src/profiling/browser/ChromeLauncher.ts +158 -0
  95. package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
  96. package/src/profiling/browser/PageLoadMode.ts +61 -0
  97. package/src/profiling/node/CoverageSampler.ts +27 -0
  98. package/src/profiling/node/CoverageTypes.ts +23 -0
  99. package/src/profiling/node/HeapSampleReport.ts +261 -0
  100. package/src/{heap-sample → profiling/node}/HeapSampler.ts +55 -13
  101. package/src/profiling/node/ResolvedProfile.ts +98 -0
  102. package/src/profiling/node/TimeSampler.ts +57 -0
  103. package/src/report/BenchmarkReport.ts +146 -0
  104. package/src/report/Colors.ts +9 -0
  105. package/src/report/Formatters.ts +110 -0
  106. package/src/report/GcSections.ts +151 -0
  107. package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
  108. package/src/report/HtmlReport.ts +223 -0
  109. package/src/report/ParseStats.ts +73 -0
  110. package/src/report/StandardSections.ts +147 -0
  111. package/src/report/ViewerSections.ts +286 -0
  112. package/src/report/text/TableReport.ts +253 -0
  113. package/src/report/text/TextReport.ts +123 -0
  114. package/src/runners/AdaptiveWrapper.ts +167 -287
  115. package/src/runners/BenchRunner.ts +27 -22
  116. package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
  117. package/src/runners/CreateRunner.ts +5 -7
  118. package/src/runners/GcStats.ts +58 -61
  119. package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
  120. package/src/runners/MergeBatches.ts +123 -0
  121. package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
  122. package/src/runners/RunnerOrchestrator.ts +180 -296
  123. package/src/runners/RunnerUtils.ts +75 -1
  124. package/src/runners/SampleStats.ts +100 -0
  125. package/src/runners/TimingRunner.ts +244 -0
  126. package/src/runners/TimingUtils.ts +3 -2
  127. package/src/runners/WorkerScript.ts +162 -178
  128. package/src/stats/BootstrapDifference.ts +282 -0
  129. package/src/{PermutationTest.ts → stats/PermutationTest.ts} +31 -40
  130. package/src/stats/StatisticalUtils.ts +445 -0
  131. package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
  132. package/src/test/AdaptiveRunner.test.ts +39 -41
  133. package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
  134. package/src/test/AdaptiveStatistics.integration.ts +9 -41
  135. package/src/{tests → test}/BenchMatrix.test.ts +31 -28
  136. package/src/test/BenchmarkReport.test.ts +63 -13
  137. package/src/test/BrowserBench.e2e.test.ts +186 -17
  138. package/src/test/BrowserBench.test.ts +10 -5
  139. package/src/test/BuildTimeSection.test.ts +130 -0
  140. package/src/test/CapSamples.test.ts +82 -0
  141. package/src/test/CoverageExport.test.ts +115 -0
  142. package/src/test/CoverageSampler.test.ts +33 -0
  143. package/src/test/HeapAttribution.test.ts +51 -0
  144. package/src/{tests → test}/MatrixFilter.test.ts +16 -16
  145. package/src/{tests → test}/MatrixReport.test.ts +1 -1
  146. package/src/test/PermutationTest.test.ts +1 -1
  147. package/src/{tests → test}/RealDataValidation.test.ts +6 -6
  148. package/src/test/RunBenchCLI.test.ts +57 -56
  149. package/src/test/RunnerOrchestrator.test.ts +12 -12
  150. package/src/test/StatisticalUtils.test.ts +48 -12
  151. package/src/{table-util/test → test}/TableReport.test.ts +2 -2
  152. package/src/test/TestUtils.ts +35 -30
  153. package/src/test/TimeExport.test.ts +139 -0
  154. package/src/test/TimeSampler.test.ts +37 -0
  155. package/src/test/ViewerLive.e2e.test.ts +159 -0
  156. package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
  157. package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
  158. package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
  159. package/src/test/fixtures/cases/asyncCases.ts +9 -0
  160. package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
  161. package/src/test/fixtures/cases/variants/product.ts +2 -0
  162. package/src/test/fixtures/cases/variants/sum.ts +2 -0
  163. package/src/test/fixtures/discover/fast.ts +1 -0
  164. package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
  165. package/src/test/fixtures/invalid/bad.ts +1 -0
  166. package/src/test/fixtures/loader/fast.ts +1 -0
  167. package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
  168. package/src/test/fixtures/loader/stateful.ts +2 -0
  169. package/src/test/fixtures/stateful/stateful.ts +2 -0
  170. package/src/test/fixtures/variants/extra.ts +1 -0
  171. package/src/test/fixtures/variants/impl.ts +1 -0
  172. package/src/test/fixtures/worker/fast.ts +1 -0
  173. package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
  174. package/src/viewer/DateFormat.ts +30 -0
  175. package/src/viewer/Helpers.ts +23 -0
  176. package/src/viewer/LineData.ts +120 -0
  177. package/src/viewer/Providers.ts +191 -0
  178. package/src/viewer/ReportData.ts +123 -0
  179. package/src/viewer/State.ts +49 -0
  180. package/src/viewer/Theme.ts +15 -0
  181. package/src/viewer/components/App.tsx +73 -0
  182. package/src/viewer/components/DropZone.tsx +71 -0
  183. package/src/viewer/components/LazyPlot.ts +33 -0
  184. package/src/viewer/components/SamplesPanel.tsx +214 -0
  185. package/src/viewer/components/Shell.tsx +26 -0
  186. package/src/viewer/components/SourcePanel.tsx +216 -0
  187. package/src/viewer/components/SummaryPanel.tsx +332 -0
  188. package/src/viewer/components/TabBar.tsx +131 -0
  189. package/src/viewer/components/TabContent.tsx +46 -0
  190. package/src/viewer/components/ThemeToggle.tsx +50 -0
  191. package/src/viewer/index.html +20 -0
  192. package/src/viewer/main.tsx +4 -0
  193. package/src/viewer/plots/CIPlot.ts +313 -0
  194. package/src/{html/browser → viewer/plots}/HistogramKde.ts +42 -47
  195. package/src/viewer/plots/LegendUtils.ts +134 -0
  196. package/src/viewer/plots/PlotTypes.ts +85 -0
  197. package/src/viewer/plots/RenderPlots.ts +230 -0
  198. package/src/viewer/plots/SampleTimeSeries.ts +306 -0
  199. package/src/viewer/plots/SvgHelpers.ts +136 -0
  200. package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
  201. package/src/viewer/report.css +427 -0
  202. package/src/viewer/shell.css +357 -0
  203. package/src/viewer/tsconfig.json +11 -0
  204. package/dist/BenchRunner-CSKN9zPy.d.mts +0 -225
  205. package/dist/BrowserHeapSampler-DCeL42RE.mjs +0 -202
  206. package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
  207. package/dist/GcStats-ByEovUi1.mjs +0 -77
  208. package/dist/GcStats-ByEovUi1.mjs.map +0 -1
  209. package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
  210. package/dist/TimingUtils-ClclVQ7E.mjs +0 -597
  211. package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
  212. package/dist/browser/index.js +0 -914
  213. package/dist/src-Cf_LXwlp.mjs +0 -2873
  214. package/dist/src-Cf_LXwlp.mjs.map +0 -1
  215. package/src/BenchMatrix.ts +0 -380
  216. package/src/BenchmarkReport.ts +0 -156
  217. package/src/HtmlDataPrep.ts +0 -148
  218. package/src/StandardSections.ts +0 -261
  219. package/src/StatisticalUtils.ts +0 -176
  220. package/src/TypeUtil.ts +0 -8
  221. package/src/browser/BrowserGcStats.ts +0 -44
  222. package/src/browser/BrowserHeapSampler.ts +0 -271
  223. package/src/export/JsonExport.ts +0 -103
  224. package/src/export/JsonFormat.ts +0 -91
  225. package/src/heap-sample/HeapSampleReport.ts +0 -196
  226. package/src/html/HtmlReport.ts +0 -131
  227. package/src/html/HtmlTemplate.ts +0 -284
  228. package/src/html/Types.ts +0 -88
  229. package/src/html/browser/CIPlot.ts +0 -287
  230. package/src/html/browser/LegendUtils.ts +0 -163
  231. package/src/html/browser/RenderPlots.ts +0 -263
  232. package/src/html/browser/SampleTimeSeries.ts +0 -389
  233. package/src/html/browser/Types.ts +0 -96
  234. package/src/html/browser/index.ts +0 -1
  235. package/src/html/index.ts +0 -17
  236. package/src/runners/BasicRunner.ts +0 -364
  237. package/src/table-util/ConvergenceFormatters.ts +0 -19
  238. package/src/table-util/Formatters.ts +0 -152
  239. package/src/table-util/README.md +0 -70
  240. package/src/table-util/TableReport.ts +0 -293
  241. package/src/tests/fixtures/cases/asyncCases.ts +0 -7
  242. package/src/tests/fixtures/cases/variants/product.ts +0 -2
  243. package/src/tests/fixtures/cases/variants/sum.ts +0 -2
  244. package/src/tests/fixtures/discover/fast.ts +0 -1
  245. package/src/tests/fixtures/invalid/bad.ts +0 -1
  246. package/src/tests/fixtures/loader/fast.ts +0 -1
  247. package/src/tests/fixtures/loader/stateful.ts +0 -2
  248. package/src/tests/fixtures/stateful/stateful.ts +0 -2
  249. package/src/tests/fixtures/variants/extra.ts +0 -1
  250. package/src/tests/fixtures/variants/impl.ts +0 -1
  251. package/src/tests/fixtures/worker/fast.ts +0 -1
  252. package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
  253. package/src/{table-util/test → test}/TableValueExtractor.ts +9 -9
package/dist/index.mjs CHANGED
@@ -1,4 +1,99 @@
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 { A as gcStatsSection, C as buildTimeSection, D as totalTimeSection, E as timeSection, M as browserCliArgs, N as defaultCliArgs, O as gcSection, P as parseCliArgs, S as buildGenericSections, T as runsSection, _ as reportMatrixResults, a as runDefaultBench, b as prepareHtmlData, c as runBenchmarks, d as exportReports, f as defaultMatrixReport, g as reportOptStatus, h as printHeapReports, i as parseBenchArgs, j as exportPerfettoTrace, k as gcSections, l as filterMatrix, m as matrixToReportGroups, n as dispatchCli, o as runDefaultMatrixBench, p as defaultReport, r as matrixBenchExports, s as runMatrixSuite, t as benchExports, u as parseMatrixFilter, v as reportResults, w as optSection, x as adaptiveSections, y as cliToMatrixOptions } from "./RunBenchCLI-C17DrJz8.mjs";
2
+ import { a as archiveBenchmark, c as exportSpeedscope, l as heapProfileToSpeedscope, p as computeColumnValues, s as buildSpeedscopeFile, v as hasField } from "./ViewerServer-BJhdnxlN.mjs";
3
+ import { d as maxBootstrapInput, g as percentile, o as computeStat, p as median, t as average, u as isBootstrappable } from "./StatisticalUtils-BD92crgM.mjs";
4
+ import { c as timeMs, l as truncate, n as formatBytes, o as integer, r as formatConvergence } from "./Formatters-BWj3d4sv.mjs";
5
+ import { h as loadCasesModule, m as loadCaseData, n as runMatrix, t as isStatefulVariant } from "./BenchMatrix-BZVrBB_h.mjs";
6
+ import { existsSync, readFileSync, statSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { execSync } from "node:child_process";
9
+ //#region src/viewer/DateFormat.ts
10
+ /** Format ISO date as local time with UTC: "Jan 9, 2026, 3:45 PM (2026-01-09T23:45:00Z)" */
11
+ function formatDateWithTimezone(isoDate) {
12
+ const date = new Date(isoDate);
13
+ return `${date.toLocaleString("en-US", {
14
+ month: "short",
15
+ day: "numeric",
16
+ year: "numeric",
17
+ hour: "numeric",
18
+ minute: "2-digit"
19
+ })} (${date.toISOString().replace(".000Z", "Z")})`;
20
+ }
21
+ /** Format relative time: "5m ago", "2h ago", "yesterday", "3 days ago" */
22
+ function formatRelativeTime(isoDate) {
23
+ const date = new Date(isoDate);
24
+ const diffMs = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
25
+ const diffMins = Math.floor(diffMs / 6e4);
26
+ const diffHours = Math.floor(diffMs / 36e5);
27
+ const diffDays = Math.floor(diffMs / 864e5);
28
+ if (diffMins < 1) return "just now";
29
+ if (diffMins < 60) return `${diffMins}m ago`;
30
+ if (diffHours < 24) return `${diffHours}h ago`;
31
+ if (diffDays === 1) return "yesterday";
32
+ if (diffDays < 30) return `${diffDays} days ago`;
33
+ return date.toLocaleDateString("en-US", {
34
+ month: "short",
35
+ day: "numeric"
36
+ });
37
+ }
38
+ //#endregion
39
+ //#region src/report/GitUtils.ts
40
+ /** Get current git version info. For dirty repos, uses most recent modified file date. */
41
+ function getCurrentGitVersion() {
42
+ try {
43
+ const exec = (cmd) => execSync(cmd, { encoding: "utf-8" }).trim();
44
+ const hash = exec("git rev-parse --short HEAD");
45
+ const commitDate = exec("git log -1 --format=%aI");
46
+ const dirty = exec("git status --porcelain").length > 0;
47
+ return {
48
+ hash,
49
+ date: dirty ? getMostRecentModifiedDate(".") ?? commitDate : commitDate,
50
+ dirty
51
+ };
52
+ } catch {
53
+ return;
54
+ }
55
+ }
56
+ /** Read baseline version from .baseline-version file */
57
+ function getBaselineVersion(baselineDir = "_baseline") {
58
+ const versionFile = join(baselineDir, ".baseline-version");
59
+ if (!existsSync(versionFile)) return void 0;
60
+ try {
61
+ const content = readFileSync(versionFile, "utf-8");
62
+ const data = JSON.parse(content);
63
+ return {
64
+ hash: data.hash,
65
+ date: data.date
66
+ };
67
+ } catch {
68
+ return;
69
+ }
70
+ }
71
+ /** Format git version for display: "abc1234 (Jan 9, 2026, 3:45 PM)" or "abc1234*" if dirty */
72
+ function formatGitVersion(version) {
73
+ return `${version.dirty ? `${version.hash}*` : version.hash} (${formatDateWithTimezone(version.date)})`;
74
+ }
75
+ /** Get most recent modified file date in a directory (for dirty repos) */
76
+ function getMostRecentModifiedDate(dir) {
77
+ try {
78
+ const files = execSync("git status --porcelain", {
79
+ encoding: "utf-8",
80
+ cwd: dir
81
+ }).trim().split("\n").filter(Boolean).map((l) => l.slice(3));
82
+ if (!files.length) return void 0;
83
+ const mtime = (f) => {
84
+ try {
85
+ return statSync(join(dir, f)).mtimeMs;
86
+ } catch {
87
+ return 0;
88
+ }
89
+ };
90
+ const mostRecent = Math.max(0, ...files.map(mtime));
91
+ return mostRecent > 0 ? new Date(mostRecent).toISOString() : void 0;
92
+ } catch {
93
+ return;
94
+ }
95
+ }
96
+ //#endregion
97
+ export { adaptiveSections, archiveBenchmark, average, benchExports, browserCliArgs, buildGenericSections, buildSpeedscopeFile, buildTimeSection, cliToMatrixOptions, computeColumnValues, computeStat, defaultCliArgs, defaultMatrixReport, defaultReport, dispatchCli, exportPerfettoTrace, exportReports, exportSpeedscope, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, formatRelativeTime, gcSection, gcSections, gcStatsSection, getBaselineVersion, getCurrentGitVersion, hasField, heapProfileToSpeedscope, integer, isBootstrappable, isStatefulVariant, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, maxBootstrapInput, median, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, percentile, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
3
98
 
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 };
99
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/viewer/DateFormat.ts","../src/report/GitUtils.ts"],"sourcesContent":["/** Format ISO date as local time with UTC: \"Jan 9, 2026, 3:45 PM (2026-01-09T23:45:00Z)\" */\nexport function formatDateWithTimezone(isoDate: string): string {\n const date = new Date(isoDate);\n const local = date.toLocaleString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const utc = date.toISOString().replace(\".000Z\", \"Z\");\n return `${local} (${utc})`;\n}\n\n/** Format relative time: \"5m ago\", \"2h ago\", \"yesterday\", \"3 days ago\" */\nexport function formatRelativeTime(isoDate: string): string {\n const date = new Date(isoDate);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n\n if (diffMins < 1) return \"just now\";\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays === 1) return \"yesterday\";\n if (diffDays < 30) return `${diffDays} days ago`;\n return date.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" });\n}\n","import { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { formatDateWithTimezone } from \"../viewer/DateFormat.ts\";\n\n/** Git commit hash, date, and dirty status for version tracking */\nexport interface GitVersion {\n hash: string;\n date: string;\n dirty?: boolean;\n}\n\n/** Get current git version info. For dirty repos, uses most recent modified file date. */\nexport function getCurrentGitVersion(): GitVersion | undefined {\n try {\n const exec = (cmd: string) => execSync(cmd, { encoding: \"utf-8\" }).trim();\n const hash = exec(\"git rev-parse --short HEAD\");\n const commitDate = exec(\"git log -1 --format=%aI\");\n const dirty = exec(\"git status --porcelain\").length > 0;\n const date = dirty\n ? (getMostRecentModifiedDate(\".\") ?? commitDate)\n : commitDate;\n return { hash, date, dirty };\n } catch {\n return undefined;\n }\n}\n\n/** Read baseline version from .baseline-version file */\nexport function getBaselineVersion(\n baselineDir = \"_baseline\",\n): GitVersion | undefined {\n const versionFile = join(baselineDir, \".baseline-version\");\n if (!existsSync(versionFile)) return undefined;\n\n try {\n const content = readFileSync(versionFile, \"utf-8\");\n const data = JSON.parse(content);\n return { hash: data.hash, date: data.date };\n } catch {\n return undefined;\n }\n}\n\n/** Format git version for display: \"abc1234 (Jan 9, 2026, 3:45 PM)\" or \"abc1234*\" if dirty */\nexport function formatGitVersion(version: GitVersion): string {\n const hashDisplay = version.dirty ? `${version.hash}*` : version.hash;\n const dateDisplay = formatDateWithTimezone(version.date);\n return `${hashDisplay} (${dateDisplay})`;\n}\n\n/** Get most recent modified file date in a directory (for dirty repos) */\nexport function getMostRecentModifiedDate(dir: string): string | undefined {\n try {\n const raw = execSync(\"git status --porcelain\", {\n encoding: \"utf-8\",\n cwd: dir,\n });\n const files = raw\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .map(l => l.slice(3));\n if (!files.length) return undefined;\n\n const mtime = (f: string): number => {\n try {\n return statSync(join(dir, f)).mtimeMs;\n } catch {\n return 0;\n }\n };\n const mostRecent = Math.max(0, ...files.map(mtime));\n return mostRecent > 0 ? new Date(mostRecent).toISOString() : undefined;\n } catch {\n return undefined;\n }\n}\n"],"mappings":";;;;;;;;;;AACA,SAAgB,uBAAuB,SAAyB;CAC9D,MAAM,OAAO,IAAI,KAAK,QAAQ;AAS9B,QAAO,GARO,KAAK,eAAe,SAAS;EACzC,OAAO;EACP,KAAK;EACL,MAAM;EACN,MAAM;EACN,QAAQ;EACT,CAAC,CAEc,IADJ,KAAK,aAAa,CAAC,QAAQ,SAAS,IAAI,CAC5B;;;AAI1B,SAAgB,mBAAmB,SAAyB;CAC1D,MAAM,OAAO,IAAI,KAAK,QAAQ;CAE9B,MAAM,0BADM,IAAI,MAAM,EACH,SAAS,GAAG,KAAK,SAAS;CAC7C,MAAM,WAAW,KAAK,MAAM,SAAS,IAAM;CAC3C,MAAM,YAAY,KAAK,MAAM,SAAS,KAAQ;CAC9C,MAAM,WAAW,KAAK,MAAM,SAAS,MAAS;AAE9C,KAAI,WAAW,EAAG,QAAO;AACzB,KAAI,WAAW,GAAI,QAAO,GAAG,SAAS;AACtC,KAAI,YAAY,GAAI,QAAO,GAAG,UAAU;AACxC,KAAI,aAAa,EAAG,QAAO;AAC3B,KAAI,WAAW,GAAI,QAAO,GAAG,SAAS;AACtC,QAAO,KAAK,mBAAmB,SAAS;EAAE,OAAO;EAAS,KAAK;EAAW,CAAC;;;;;ACf7E,SAAgB,uBAA+C;AAC7D,KAAI;EACF,MAAM,QAAQ,QAAgB,SAAS,KAAK,EAAE,UAAU,SAAS,CAAC,CAAC,MAAM;EACzE,MAAM,OAAO,KAAK,6BAA6B;EAC/C,MAAM,aAAa,KAAK,0BAA0B;EAClD,MAAM,QAAQ,KAAK,yBAAyB,CAAC,SAAS;AAItD,SAAO;GAAE;GAAM,MAHF,QACR,0BAA0B,IAAI,IAAI,aACnC;GACiB;GAAO;SACtB;AACN;;;;AAKJ,SAAgB,mBACd,cAAc,aACU;CACxB,MAAM,cAAc,KAAK,aAAa,oBAAoB;AAC1D,KAAI,CAAC,WAAW,YAAY,CAAE,QAAO,KAAA;AAErC,KAAI;EACF,MAAM,UAAU,aAAa,aAAa,QAAQ;EAClD,MAAM,OAAO,KAAK,MAAM,QAAQ;AAChC,SAAO;GAAE,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM;SACrC;AACN;;;;AAKJ,SAAgB,iBAAiB,SAA6B;AAG5D,QAAO,GAFa,QAAQ,QAAQ,GAAG,QAAQ,KAAK,KAAK,QAAQ,KAE3C,IADF,uBAAuB,QAAQ,KAAK,CAClB;;;AAIxC,SAAgB,0BAA0B,KAAiC;AACzE,KAAI;EAKF,MAAM,QAJM,SAAS,0BAA0B;GAC7C,UAAU;GACV,KAAK;GACN,CAAC,CAEC,MAAM,CACN,MAAM,KAAK,CACX,OAAO,QAAQ,CACf,KAAI,MAAK,EAAE,MAAM,EAAE,CAAC;AACvB,MAAI,CAAC,MAAM,OAAQ,QAAO,KAAA;EAE1B,MAAM,SAAS,MAAsB;AACnC,OAAI;AACF,WAAO,SAAS,KAAK,KAAK,EAAE,CAAC,CAAC;WACxB;AACN,WAAO;;;EAGX,MAAM,aAAa,KAAK,IAAI,GAAG,GAAG,MAAM,IAAI,MAAM,CAAC;AACnD,SAAO,aAAa,IAAI,IAAI,KAAK,WAAW,CAAC,aAAa,GAAG,KAAA;SACvD;AACN"}
@@ -1,7 +1,7 @@
1
- import { a as MeasuredResults, i as BenchmarkSpec, o as HeapProfile, t as RunnerOptions } from "../BenchRunner-CSKN9zPy.mjs";
1
+ import { a as MeasuredResults, c as HeapProfile, i as BenchmarkSpec, l as CoverageData, s as TimeProfile, t as RunnerOptions } from "../BenchRunner-DglX1NOn.mjs";
2
2
 
3
3
  //#region src/runners/CreateRunner.d.ts
4
- type KnownRunner = "basic";
4
+ type KnownRunner = "timing";
5
5
  //#endregion
6
6
  //#region src/runners/WorkerScript.d.ts
7
7
  /** Message sent to worker process to start a benchmark run. */
@@ -10,24 +10,32 @@ interface RunMessage {
10
10
  spec: BenchmarkSpec;
11
11
  runnerName: KnownRunner;
12
12
  options: RunnerOptions;
13
+ /** Serialized function body (mutually exclusive with modulePath) */
13
14
  fnCode?: string;
14
15
  modulePath?: string;
16
+ /** Defaults to default export */
15
17
  exportName?: string;
18
+ /** Called once before benchmarking; result passed as params to fn */
16
19
  setupExportName?: string;
17
20
  params?: unknown;
21
+ /** Directory URL containing variant .ts files (BenchMatrix mode) */
18
22
  variantDir?: string;
23
+ /** Variant filename without .ts extension */
19
24
  variantId?: string;
20
25
  caseData?: unknown;
21
26
  caseId?: string;
27
+ /** Module URL exporting cases[] and loadCase() */
22
28
  casesModule?: string;
23
29
  }
24
- /** Message returned from worker process with benchmark results. */
30
+ /** Benchmark results returned from worker process. */
25
31
  interface ResultMessage {
26
32
  type: "result";
27
33
  results: MeasuredResults[];
28
34
  heapProfile?: HeapProfile;
35
+ timeProfile?: TimeProfile;
36
+ coverage?: CoverageData;
29
37
  }
30
- /** Message returned from worker process when benchmark fails. */
38
+ /** Error returned from worker process when benchmark fails. */
31
39
  interface ErrorMessage {
32
40
  type: "error";
33
41
  error: string;
@@ -1,68 +1,14 @@
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";
3
-
2
+ import { a as getPerfNow, c as resolveVariantFn, i as getElapsed, o as createBenchRunner, s as importBenchFn } from "../BenchMatrix-BZVrBB_h.mjs";
4
3
  //#region src/runners/WorkerScript.ts
5
4
  const workerStartTime = getPerfNow();
6
5
  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
- /** Log timing with consistent format */
60
- const logTiming = debugWorkerTiming ? _logTiming : () => {};
6
+ const logTiming = () => {};
61
7
  function _logTiming(operation, duration) {
62
- if (duration === void 0) console.log(`[Worker] ${operation}`);
63
- else console.log(`[Worker] ${operation} ${duration.toFixed(1)}ms`);
8
+ const suffix = duration !== void 0 ? ` ${duration.toFixed(1)}ms` : "";
9
+ console.log(`[Worker] ${operation}${suffix}`);
64
10
  }
65
- /** Send message and exit with duration log */
11
+ /** Send IPC message to parent then exit the worker process */
66
12
  function sendAndExit(msg, exitCode) {
67
13
  process.send(msg, void 0, void 0, (err) => {
68
14
  if (err) {
@@ -76,7 +22,12 @@ function sendAndExit(msg, exitCode) {
76
22
  /** Resolve benchmark function from message (variant dir, module path, or fnCode) */
77
23
  async function resolveBenchmarkFn(message) {
78
24
  if (message.variantDir && message.variantId) return importVariantModule(message);
79
- if (message.modulePath) return importBenchmarkWithSetup(message);
25
+ if (message.modulePath) {
26
+ const { modulePath, exportName, setupExportName, params } = message;
27
+ logTiming(`Importing from ${modulePath}${exportName ? ` (${exportName})` : ""}`);
28
+ if (setupExportName) logTiming(`Calling setup: ${setupExportName}`);
29
+ return importBenchFn(modulePath, exportName, setupExportName, params);
30
+ }
80
31
  return {
81
32
  fn: reconstructFunction(message.fnCode),
82
33
  params: message.params
@@ -84,75 +35,96 @@ async function resolveBenchmarkFn(message) {
84
35
  }
85
36
  /** Import variant from directory and prepare benchmark function */
86
37
  async function importVariantModule(message) {
87
- const { variantDir, variantId, caseId, casesModule } = message;
88
- let { caseData } = message;
89
- const moduleUrl = variantModuleUrl(variantDir, variantId);
38
+ const { variantDir, variantId } = message;
90
39
  logTiming(`Importing variant ${variantId} from ${variantDir}`);
91
- if (casesModule && caseId) caseData = (await loadCaseFromModule(casesModule, caseId)).data;
92
- const { setup, run } = await import(moduleUrl);
93
- if (typeof run !== "function") throw new Error(`Variant '${variantId}' must export 'run' function`);
94
- if (typeof setup === "function") {
95
- logTiming(`Calling setup for ${variantId}`);
96
- const state = await setup(caseData);
97
- return {
98
- fn: () => run(state),
99
- params: void 0
100
- };
101
- }
102
- return {
103
- fn: () => run(caseData),
104
- params: void 0
105
- };
106
- }
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
- /** Import benchmark function and optionally run setup */
115
- async function importBenchmarkWithSetup(message) {
116
- const { modulePath, exportName, setupExportName, params } = message;
117
- logTiming(`Importing from ${modulePath}${exportName ? ` (${exportName})` : ""}`);
118
- const module = await import(modulePath);
119
- const fn = getModuleExport(module, exportName, modulePath);
120
- if (setupExportName) {
121
- logTiming(`Calling setup: ${setupExportName}`);
122
- return {
123
- fn,
124
- params: await getModuleExport(module, setupExportName, modulePath)(params)
125
- };
126
- }
127
- return {
128
- fn,
129
- params
130
- };
131
- }
132
- /** Get named or default export from module */
133
- function getModuleExport(module, exportName, modulePath) {
134
- const fn = exportName ? module[exportName] : module.default || module;
135
- if (typeof fn !== "function") {
136
- const name = exportName || "default";
137
- throw new Error(`Export '${name}' from ${modulePath} is not a function`);
138
- }
139
- return fn;
40
+ return resolveVariantFn({
41
+ ...message,
42
+ variantDir,
43
+ variantId
44
+ });
140
45
  }
141
- /** Reconstruct function from string code */
46
+ /** Eval serialized function body back into a callable */
142
47
  function reconstructFunction(fnCode) {
143
48
  const fn = eval(`(${fnCode})`);
144
49
  if (typeof fn !== "function") throw new Error("Reconstructed code is not a function");
145
50
  return fn;
146
51
  }
147
- /** Create error message from exception */
148
- function createErrorMessage(error) {
52
+ /** Run benchmark with optional heap, time, and coverage profiling */
53
+ async function runWithProfiling(message, runner) {
54
+ const state = {};
55
+ const runBench = buildProfilingChain(message, runner, state);
56
+ if (!message.options.callCounts) return {
57
+ type: "result",
58
+ results: await runBench(),
59
+ ...state
60
+ };
61
+ const { withCoverageProfiling } = await import("../CoverageSampler-D5T9DRqe.mjs");
62
+ const r = await withCoverageProfiling(async (session) => {
63
+ state.profilerSession = session;
64
+ return runBench();
65
+ });
66
+ state.coverage = r.coverage;
149
67
  return {
150
- type: "error",
151
- error: error instanceof Error ? error.message : String(error),
152
- stack: error instanceof Error ? error.stack : void 0
68
+ type: "result",
69
+ results: r.result,
70
+ ...state
153
71
  };
154
72
  }
155
-
73
+ /** Build nested profiling wrappers: outer heap, inner time */
74
+ function buildProfilingChain(message, runner, state) {
75
+ const { alloc, profile, profileInterval, allocInterval, allocDepth } = message.options;
76
+ const run = async () => {
77
+ const { fn, params } = await resolveBenchmarkFn(message);
78
+ return runner.runBench({
79
+ ...message.spec,
80
+ fn
81
+ }, message.options, params);
82
+ };
83
+ const runMaybeWithTime = profile ? async () => {
84
+ const { withTimeProfiling } = await import("../TimeSampler-Ds8n7l2B.mjs");
85
+ const r = await withTimeProfiling({
86
+ interval: profileInterval,
87
+ session: state.profilerSession
88
+ }, run);
89
+ state.timeProfile = r.profile;
90
+ return r.result;
91
+ } : run;
92
+ return alloc ? async () => {
93
+ const { withHeapSampling } = await import("../HeapSampler-Dq-hpXem.mjs");
94
+ const r = await withHeapSampling({
95
+ samplingInterval: allocInterval,
96
+ stackDepth: allocDepth
97
+ }, runMaybeWithTime);
98
+ state.heapProfile = r.profile;
99
+ return r.result;
100
+ } : runMaybeWithTime;
101
+ }
102
+ process.on("message", async (message) => {
103
+ if (message.type !== "run") return;
104
+ logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);
105
+ try {
106
+ const start = getPerfNow();
107
+ const runner = await createBenchRunner(message.runnerName, message.options);
108
+ logTiming("Runner created in", getElapsed(start));
109
+ const benchStart = getPerfNow();
110
+ const result = await runWithProfiling(message, runner);
111
+ logTiming("Benchmark execution took", getElapsed(benchStart));
112
+ sendAndExit(result, 0);
113
+ } catch (error) {
114
+ const err = error instanceof Error ? error : void 0;
115
+ sendAndExit({
116
+ type: "error",
117
+ error: err?.message ?? String(error),
118
+ stack: err?.stack
119
+ }, 1);
120
+ }
121
+ });
122
+ setTimeout(() => {
123
+ console.error("WorkerScript: Maximum lifetime exceeded, exiting");
124
+ process.exit(1);
125
+ }, maxLifetime);
126
+ process.stdin.pause();
156
127
  //#endregion
157
- export { };
128
+ export {};
129
+
158
130
  //# sourceMappingURL=WorkerScript.mjs.map
@@ -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 { Session } from \"node:inspector/promises\";\nimport type { CoverageData } from \"../profiling/node/CoverageTypes.ts\";\nimport type { HeapProfile } from \"../profiling/node/HeapSampler.ts\";\nimport type { TimeProfile } from \"../profiling/node/TimeSampler.ts\";\nimport type { BenchmarkFunction, BenchmarkSpec } from \"./BenchmarkSpec.ts\";\nimport type { BenchRunner, RunnerOptions } from \"./BenchRunner.ts\";\nimport type { KnownRunner } from \"./CreateRunner.ts\";\nimport type { MeasuredResults } from \"./MeasuredResults.ts\";\nimport {\n createBenchRunner,\n importBenchFn,\n resolveVariantFn,\n} from \"./RunnerUtils.ts\";\nimport { debugWorkerTiming, getElapsed, getPerfNow } from \"./TimingUtils.ts\";\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 /** Serialized function body (mutually exclusive with modulePath) */\n fnCode?: string;\n modulePath?: string;\n /** Defaults to default export */\n exportName?: string;\n /** Called once before benchmarking; result passed as params to fn */\n setupExportName?: string;\n params?: unknown;\n\n /** Directory URL containing variant .ts files (BenchMatrix mode) */\n variantDir?: string;\n /** Variant filename without .ts extension */\n variantId?: string;\n caseData?: unknown;\n caseId?: string;\n /** Module URL exporting cases[] and loadCase() */\n casesModule?: string;\n}\n\n/** Benchmark results returned from worker process. */\nexport interface ResultMessage {\n type: \"result\";\n results: MeasuredResults[];\n heapProfile?: HeapProfile;\n timeProfile?: TimeProfile;\n coverage?: CoverageData;\n}\n\n/** Error 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\n/** Profiling state accumulated during worker benchmark execution */\ninterface ProfilingState {\n heapProfile?: HeapProfile;\n timeProfile?: TimeProfile;\n coverage?: CoverageData;\n /** Shared session so TimeSampler doesn't reset coverage counters */\n profilerSession?: Session;\n}\n\nconst workerStartTime = getPerfNow();\nconst maxLifetime = 5 * 60 * 1000;\n\nconst logTiming = debugWorkerTiming ? _logTiming : () => {};\nfunction _logTiming(operation: string, duration?: number) {\n const suffix = duration !== undefined ? ` ${duration.toFixed(1)}ms` : \"\";\n console.log(`[Worker] ${operation}${suffix}`);\n}\n\n/** Send IPC message to parent then exit the worker process */\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 const { modulePath, exportName, setupExportName, params } = message;\n logTiming(\n `Importing from ${modulePath}${exportName ? ` (${exportName})` : \"\"}`,\n );\n if (setupExportName) logTiming(`Calling setup: ${setupExportName}`);\n return importBenchFn(modulePath, exportName, setupExportName, params);\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 } = message;\n logTiming(`Importing variant ${variantId} from ${variantDir}`);\n return resolveVariantFn({\n ...message,\n variantDir: variantDir!,\n variantId: variantId!,\n });\n}\n\n/** Eval serialized function body back into a callable */\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/** Run benchmark with optional heap, time, and coverage profiling */\nasync function runWithProfiling(\n message: RunMessage,\n runner: BenchRunner,\n): Promise<ResultMessage> {\n const state: ProfilingState = {};\n const runBench = buildProfilingChain(message, runner, state);\n\n if (!message.options.callCounts) {\n const results = await runBench();\n return { type: \"result\", results, ...state };\n }\n\n const { withCoverageProfiling } = await import(\n \"../profiling/node/CoverageSampler.ts\"\n );\n const r = await withCoverageProfiling(async session => {\n state.profilerSession = session;\n return runBench();\n });\n state.coverage = r.coverage;\n return { type: \"result\", results: r.result, ...state };\n}\n\n/** Build nested profiling wrappers: outer heap, inner time */\nfunction buildProfilingChain(\n message: RunMessage,\n runner: BenchRunner,\n state: ProfilingState,\n): () => Promise<MeasuredResults[]> {\n const { alloc, profile, profileInterval, allocInterval, allocDepth } =\n message.options;\n\n const run = async () => {\n const { fn, params } = await resolveBenchmarkFn(message);\n return runner.runBench({ ...message.spec, fn }, message.options, params);\n };\n\n const runMaybeWithTime = profile\n ? async () => {\n const { withTimeProfiling } = await import(\n \"../profiling/node/TimeSampler.ts\"\n );\n const opts = {\n interval: profileInterval,\n session: state.profilerSession,\n };\n const r = await withTimeProfiling(opts, run);\n state.timeProfile = r.profile;\n return r.result;\n }\n : run;\n\n return alloc\n ? async () => {\n const { withHeapSampling } = await import(\n \"../profiling/node/HeapSampler.ts\"\n );\n const heapOpts = {\n samplingInterval: allocInterval,\n stackDepth: allocDepth,\n };\n const r = await withHeapSampling(heapOpts, runMaybeWithTime);\n state.heapProfile = r.profile;\n return r.result;\n }\n : runMaybeWithTime;\n}\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 runner = await createBenchRunner(message.runnerName, message.options);\n logTiming(\"Runner created in\", getElapsed(start));\n\n const benchStart = getPerfNow();\n const result = await runWithProfiling(message, runner);\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit(result, 0);\n } catch (error) {\n const err = error instanceof Error ? error : undefined;\n sendAndExit(\n {\n type: \"error\",\n error: err?.message ?? String(error),\n stack: err?.stack,\n },\n 1,\n );\n }\n});\n\n// Prevent zombie processes\nsetTimeout(() => {\n console.error(\"WorkerScript: Maximum lifetime exceeded, exiting\");\n process.exit(1);\n}, maxLifetime);\n\n// Prevent stdin from keeping the worker process alive\nprocess.stdin.pause();\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import{t as e}from"./index-Br9bp_cX.js";var t=`http://www.w3.org/2000/svg`,n=e=>e.replace(/[A-Z]/g,e=>`-`+e.toLowerCase()),r=e=>document.createElementNS(t,e);function i(e,t){for(let[r,i]of Object.entries(t))e.setAttribute(n(r),i)}function a(e,n){let r=document.createElementNS(t,`svg`);return r.setAttribute(`width`,String(e)),r.setAttribute(`height`,String(n)),e&&n&&r.setAttribute(`viewBox`,`0 0 ${e} ${n}`),r}function o(e,n,r,a,o){let s=document.createElementNS(t,`rect`);return s.setAttribute(`x`,String(e)),s.setAttribute(`y`,String(n)),s.setAttribute(`width`,String(r)),s.setAttribute(`height`,String(a)),i(s,o),s}function s(e,n,r,a,o){let s=document.createElementNS(t,`line`);return s.setAttribute(`x1`,String(e)),s.setAttribute(`y1`,String(n)),s.setAttribute(`x2`,String(r)),s.setAttribute(`y2`,String(a)),i(s,o),s}function c(e,n,r,i=`start`,a=`9`,o=`#666`,s=`400`){let c=document.createElementNS(t,`text`);return c.setAttribute(`x`,String(e)),c.setAttribute(`y`,String(n)),c.setAttribute(`text-anchor`,i),c.setAttribute(`font-size`,a),c.setAttribute(`font-weight`,s),c.setAttribute(`fill`,o),c.textContent=r,c}function l(e,n){let r=document.createElementNS(t,`path`);return r.setAttribute(`d`,e),i(r,n),r}function u(e){let t=`ci-sketch`;if(e.querySelector(`#${t}`))return t;let n=f(e),a=r(`filter`);i(a,{id:t,x:`-5%`,y:`-5%`,width:`110%`,height:`110%`});let o=r(`feTurbulence`);i(o,{type:`turbulence`,baseFrequency:`0.06`,numOctaves:`4`,seed:`1`,result:`noise`});let s=r(`feDisplacementMap`);return i(s,{in:`SourceGraphic`,in2:`noise`,scale:`10`,xChannelSelector:`R`,yChannelSelector:`G`}),a.appendChild(o),a.appendChild(s),n.appendChild(a),t}function d(e){let t=`margin-hatch`;if(e.querySelector(`#${t}`))return t;let n=f(e),a=r(`pattern`);i(a,{id:t,patternUnits:`userSpaceOnUse`,width:`5`,height:`5`,patternTransform:`rotate(45)`});let o=r(`line`);return i(o,{x1:`0`,y1:`0`,x2:`0`,y2:`5`}),o.classList.add(`margin-hatch-stroke`),a.appendChild(o),n.appendChild(a),t}function f(e){let n=e.querySelector(`defs`);return n||(n=document.createElementNS(t,`defs`),e.insertBefore(n,e.firstChild)),n}var p={top:22,right:12,bottom:22,left:12},m={width:260,height:85,title:`Δ%`,smooth:!0,direction:`uncertain`,includeZero:!0},h={faster:{fill:`#bbf7d0`,stroke:`#22c55e`},slower:{fill:`#fee2e2`,stroke:`#ef4444`},uncertain:{fill:`#dbeafe`,stroke:`#3b82f6`},equivalent:{fill:`#dcfce7`,stroke:`#86efac`}};function g(e,t,n,r={}){let i={...m,...r},c=v(i.width,i.height,!!i.pointLabel),l=a(c.width,c.height);if(!e?.length)return l;let{fill:d,stroke:f}=h[i.direction],{includeZero:p,equivMargin:g}=i,_=y(e,t,c,p,g,n),{margin:E,plot:D}=c,O=_.x(n);b(l,i,E,O),g&&p&&x(l,g,_,c);let k=_.x(t[0]),A=o(k,E.top,_.x(t[1])-k,D.h,{fill:d}),j=p?`ci-region-strong`:`ci-region`;return A.classList.add(j,`ci-${i.direction}`),i.ciReliable===!1&&(A.classList.add(`ci-unreliable`),A.setAttribute(`filter`,`url(#${u(l)})`)),l.appendChild(A),i.smooth?S(l,e,_,f):C(l,e,_,c,f),w(l,_,c,p),l.appendChild(s(O,E.top,O,E.top+D.h,{stroke:f,strokeWidth:`2`})),T(l,t,_,c,i),l}function _(e,t={}){return e.histogram?g(e.histogram,e.ci,e.percent,{title:e.label,direction:e.direction,ciLevel:e.ciLevel,ciReliable:e.ciReliable,...t}):a(0,0)}function v(e,t,n){let r=t<p.top+p.bottom+10?{top:4,right:6,bottom:4,left:6}:{...p,top:n?30:p.top};return{width:e,height:t,margin:r,plot:{w:e-r.left-r.right,h:t-r.top-r.bottom}}}function y(e,t,n,r,i,a){let{margin:o,plot:s}=n,c=e.map(e=>e.x),l=r?[0]:[],u=i?[-i,i]:[],d=a==null?[]:[a],f=Math.min(...c,t[0],...l,...u,...d),p=Math.max(...c,t[1],...l,...u,...d),m=Math.max(...e.map(e=>e.count)),h=p-f||1;return{x:e=>o.left+(e-f)/h*s.w,y:e=>o.top+s.h-e/m*s.h}}function b(e,t,n,r){if(t.title&&e.appendChild(c(n.left,14,t.title,`start`,`13`,`currentColor`,`600`)),t.pointLabel){let i=c(r,n.top-6,t.pointLabel,`middle`,`15`,`currentColor`,`700`);e.appendChild(i)}}function x(e,t,n,r){let{margin:i,plot:a}=r,s=n.x(-t),c=n.x(t),l=`url(#${d(e)})`,u=a.h/3,f=o(s,i.top+(a.h-u)/2,c-s,u,{fill:l,strokeWidth:`1.5`});f.classList.add(`margin-zone`),e.appendChild(f)}function S(e,t,n,r){let i=E([...t].sort((e,t)=>e.x-t.x),2),a=i.map(e=>`${n.x(e.x)},${n.y(e.count)}`),o=n.y(0),s=n.x(i[0].x),c=n.x(i.at(-1).x),u=l(`M${s},${o}L${a.join(`L`)}L${c},${o}Z`,{fill:r});u.classList.add(`dist-fill`),e.appendChild(u);let d=l(`M${a.join(`L`)}`,{stroke:r,fill:`none`,strokeWidth:`1.5`});d.classList.add(`dist-stroke`),e.appendChild(d)}function C(e,t,n,r,i){let a=[...t].sort((e,t)=>e.x-t.x),s=a.length>1?a[1].x-a[0].x:1,c=s/(n.x(a.at(-1).x)-n.x(a[0].x)+s)*r.plot.w*.9,l=n.y(0),u={fill:i,opacity:`0.6`};for(let t of a){let r=n.y(t.count);e.appendChild(o(n.x(t.x)-c/2,r,c,l-r,u))}}function w(e,t,n,r){let{margin:i,plot:a}=n,o=t.x(0),c=o>=i.left&&o<=n.width-i.right;!r||!c||e.appendChild(s(o,i.top-4,o,i.top+a.h+4,{stroke:`#000`,strokeWidth:`1`}))}function T(t,n,r,i,a){if(i.margin.bottom<15)return;let o=i.height-4,s=a.ciLabels?.[0]??e(n[0],0),l=a.ciLabels?.[1]??e(n[1],0),u=r.x(n[0]),d=r.x(n[1]),f=Math.max(s.length,l.length)*6;(!a.includeZero||d-u>=f)&&(t.appendChild(c(u,o,s,`middle`,`11`)),t.appendChild(c(d,o,l,`middle`,`11`)))}function E(e,t){return e.map((n,r)=>{let i=0,a=0;for(let n=0;n<e.length;n++){let o=Math.exp(-((r-n)**2)/(2*t**2));i+=e[n].count*o,a+=o}return{x:n.x,count:i/a}})}export{_ as createCIPlot,g as createDistributionPlot};
@@ -0,0 +1 @@
1
+ import{n as e,r as t}from"./index-Br9bp_cX.js";import{_ as n,a as r,b as i,d as a,f as o,g as s,h as c,l,m as u,o as d,t as f,u as p,v as m,y as h}from"./LegendUtils-BJpbn_jr.js";var g=Array.prototype,_=g.slice;g.map;function v(e){return()=>e}function y(e,t,n){let r;for(;;){let i=s(e,t,n);if(i===r||i===0||!isFinite(i))return[e,t];i>0?(e=Math.floor(e/i)*i,t=Math.ceil(t/i)*i):i<0&&(e=Math.ceil(e*i)/i,t=Math.floor(t*i)/i),r=i}}function b(){var e=m,t=h,r=c;function a(a){Array.isArray(a)||(a=Array.from(a));var o,c=a.length,l,u,d=Array(c);for(o=0;o<c;++o)d[o]=e(a[o],o,a);var f=t(d),p=f[0],m=f[1],g=r(d,p,m);if(!Array.isArray(g)){let e=m,r=+g;if(t===h&&([p,m]=y(p,m,r)),g=n(p,m,r),g[0]<=p&&(u=s(p,m,r)),g[g.length-1]>=m)if(e>=m&&t===h){let e=s(p,m,r);isFinite(e)&&(e>0?m=(Math.floor(m/e)+1)*e:e<0&&(m=(Math.ceil(m*-e)+1)/-e))}else g.pop()}for(var _=g.length,v=0,b=_;g[v]<=p;)++v;for(;g[b-1]>m;)--b;(v||b<_)&&(g=g.slice(v,b),_=b-v);var x=Array(_+1),S;for(o=0;o<=_;++o)S=x[o]=[],S.x0=o>0?g[o-1]:p,S.x1=o<_?g[o]:m;if(isFinite(u)){if(u>0)for(o=0;o<c;++o)(l=d[o])!=null&&p<=l&&l<=m&&x[Math.min(_,Math.floor((l-p)/u))].push(a[o]);else if(u<0){for(o=0;o<c;++o)if((l=d[o])!=null&&p<=l&&l<=m){let e=Math.floor((p-l)*u);x[Math.min(_,e+(g[e]<=l))].push(a[o])}}}else for(o=0;o<c;++o)(l=d[o])!=null&&p<=l&&l<=m&&x[i(g,l,0,_)].push(a[o]);return x}return a.value=function(t){return arguments.length?(e=typeof t==`function`?t:v(t),a):e},a.domain=function(e){return arguments.length?(t=typeof e==`function`?e:v([e[0],e[1]]),a):t},a.thresholds=function(e){return arguments.length?(r=typeof e==`function`?e:v(Array.isArray(e)?_.call(e):e),a):r},a}function x(n,i){let{unitSuffix:a,convertValue:o,formatValue:s}=e(n.map(e=>e.value)),{barData:c,binMin:u,binMax:p,yMax:m}=S(n.map(e=>({...e,value:o(e.value)})),i),{colorMap:h,legendItems:g}=C(i);return r({...t,x:{label:`Time (${a})`,labelAnchor:`center`,domain:[u,p],labelOffset:45,tickFormat:s,ticks:5},y:{label:`Count`,labelAnchor:`top`,labelArrow:!1,grid:!0,domain:[0,m]},marks:[d(c,{x1:`x1`,x2:`x2`,y:`count`,fill:e=>h.get(e.benchmark),fillOpacity:.6}),l([0]),...f({xMin:u,xMax:p,yMax:m},g)]})}function S(e,n){let r=e.map(e=>e.value).sort((e,t)=>e-t),i=o(r,.01),s=o(r,.99),c=(s-i)/25,l=a(1,25).map(e=>i+e*c),d=b().domain([i,s]).thresholds(l).value(e=>e.value)(e),f=n.length,p=(s-i)/t.width,m=d.flatMap(e=>{let t=new Map;for(let n of e)t.set(n.benchmark,(t.get(n.benchmark)||0)+1);let r=e.x1-e.x0,i=Math.min(r*.5,p*8),a=e.x0+i/2,o=(r-i)/f;return n.map((e,n)=>{let r=a+n*o,i=a+(n+1)*o;return{benchmark:e,count:t.get(e)||0,x1:r,x2:i}})});return{barData:m,binMin:i,binMax:s,yMax:(u(m,e=>e.count)||1)*1.15}}function C(e){let t=p,n=e=>t[e%10];return{colorMap:new Map(e.map((e,t)=>[e,n(t)])),legendItems:e.map((e,t)=>({color:n(t),label:e,style:`vertical-bar`}))}}export{x as createHistogramKde};