benchforge 0.1.11 → 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 -294
  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-BzyUfiyB.d.mts → BenchRunner-DglX1NOn.d.mts} +119 -66
  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 +711 -558
  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 +77 -105
  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 -27
  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 -51
  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 +132 -866
  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 +64 -99
  78. package/src/export/SpeedscopeTypes.ts +98 -0
  79. package/src/export/TimeExport.ts +115 -0
  80. package/src/index.ts +86 -67
  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 +49 -47
  85. package/src/matrix/MatrixInlineRunner.ts +50 -0
  86. package/src/matrix/MatrixReport.ts +90 -250
  87. package/src/matrix/VariantLoader.ts +5 -5
  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 +1 -2
  101. package/src/{heap-sample → profiling/node}/ResolvedProfile.ts +18 -9
  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 +116 -236
  115. package/src/runners/BenchRunner.ts +20 -15
  116. package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
  117. package/src/runners/CreateRunner.ts +5 -7
  118. package/src/runners/GcStats.ts +47 -50
  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 +127 -243
  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 +135 -151
  128. package/src/stats/BootstrapDifference.ts +282 -0
  129. package/src/{PermutationTest.ts → stats/PermutationTest.ts} +8 -17
  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 +2 -2
  135. package/src/{tests → test}/BenchMatrix.test.ts +19 -16
  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 +14 -14
  144. package/src/{tests → test}/MatrixFilter.test.ts +1 -1
  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 +39 -38
  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 +12 -7
  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 +33 -38
  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/BrowserHeapSampler-B6asLKWQ.mjs +0 -202
  205. package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +0 -1
  206. package/dist/GcStats-wX7Xyblu.mjs +0 -77
  207. package/dist/GcStats-wX7Xyblu.mjs.map +0 -1
  208. package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
  209. package/dist/TimingUtils-DwOwkc8G.mjs +0 -597
  210. package/dist/TimingUtils-DwOwkc8G.mjs.map +0 -1
  211. package/dist/browser/index.js +0 -914
  212. package/dist/src-B-DDaCa9.mjs +0 -3108
  213. package/dist/src-B-DDaCa9.mjs.map +0 -1
  214. package/src/BenchMatrix.ts +0 -380
  215. package/src/BenchmarkReport.ts +0 -161
  216. package/src/HtmlDataPrep.ts +0 -148
  217. package/src/StandardSections.ts +0 -261
  218. package/src/StatisticalUtils.ts +0 -175
  219. package/src/TypeUtil.ts +0 -8
  220. package/src/browser/BrowserGcStats.ts +0 -44
  221. package/src/browser/BrowserHeapSampler.ts +0 -271
  222. package/src/export/JsonExport.ts +0 -103
  223. package/src/export/JsonFormat.ts +0 -91
  224. package/src/export/SpeedscopeExport.ts +0 -202
  225. package/src/heap-sample/HeapSampleReport.ts +0 -269
  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 -157
  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 +0 -0
package/dist/index.mjs CHANGED
@@ -1,4 +1,99 @@
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";
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, 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 };
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-BzyUfiyB.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,16 +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-DwOwkc8G.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
- /** Log timing with consistent format */
8
- const logTiming = debugWorkerTiming ? _logTiming : () => {};
6
+ const logTiming = () => {};
9
7
  function _logTiming(operation, duration) {
10
- if (duration === void 0) console.log(`[Worker] ${operation}`);
11
- 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}`);
12
10
  }
13
- /** Send message and exit with duration log */
11
+ /** Send IPC message to parent then exit the worker process */
14
12
  function sendAndExit(msg, exitCode) {
15
13
  process.send(msg, void 0, void 0, (err) => {
16
14
  if (err) {
@@ -24,7 +22,12 @@ function sendAndExit(msg, exitCode) {
24
22
  /** Resolve benchmark function from message (variant dir, module path, or fnCode) */
25
23
  async function resolveBenchmarkFn(message) {
26
24
  if (message.variantDir && message.variantId) return importVariantModule(message);
27
- 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
+ }
28
31
  return {
29
32
  fn: reconstructFunction(message.fnCode),
30
33
  params: message.params
@@ -32,119 +35,88 @@ async function resolveBenchmarkFn(message) {
32
35
  }
33
36
  /** Import variant from directory and prepare benchmark function */
34
37
  async function importVariantModule(message) {
35
- const { variantDir, variantId, caseId, casesModule } = message;
36
- let { caseData } = message;
37
- const moduleUrl = variantModuleUrl(variantDir, variantId);
38
+ const { variantDir, variantId } = message;
38
39
  logTiming(`Importing variant ${variantId} from ${variantDir}`);
39
- if (casesModule && caseId) caseData = (await loadCaseFromModule(casesModule, caseId)).data;
40
- const { setup, run } = await import(moduleUrl);
41
- if (typeof run !== "function") throw new Error(`Variant '${variantId}' must export 'run' function`);
42
- if (typeof setup === "function") {
43
- logTiming(`Calling setup for ${variantId}`);
44
- const state = await setup(caseData);
45
- return {
46
- fn: () => run(state),
47
- params: void 0
48
- };
49
- }
50
- return {
51
- fn: () => run(caseData),
52
- params: void 0
53
- };
54
- }
55
- /** Import benchmark function and optionally run setup */
56
- async function importBenchmarkWithSetup(message) {
57
- const { modulePath, exportName, setupExportName, params } = message;
58
- logTiming(`Importing from ${modulePath}${exportName ? ` (${exportName})` : ""}`);
59
- const module = await import(modulePath);
60
- const fn = getModuleExport(module, exportName, modulePath);
61
- if (setupExportName) {
62
- logTiming(`Calling setup: ${setupExportName}`);
63
- return {
64
- fn,
65
- params: await getModuleExport(module, setupExportName, modulePath)(params)
66
- };
67
- }
68
- return {
69
- fn,
70
- params
71
- };
40
+ return resolveVariantFn({
41
+ ...message,
42
+ variantDir,
43
+ variantId
44
+ });
72
45
  }
73
- /** Reconstruct function from string code */
46
+ /** Eval serialized function body back into a callable */
74
47
  function reconstructFunction(fnCode) {
75
48
  const fn = eval(`(${fnCode})`);
76
49
  if (typeof fn !== "function") throw new Error("Reconstructed code is not a function");
77
50
  return fn;
78
51
  }
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
- }
86
- /** Get named or default export from module */
87
- function getModuleExport(module, exportName, modulePath) {
88
- const fn = exportName ? module[exportName] : module.default || module;
89
- if (typeof fn !== "function") {
90
- const name = exportName || "default";
91
- throw new Error(`Export '${name}' from ${modulePath} is not a function`);
92
- }
93
- return fn;
94
- }
95
- /** Create error message from exception */
96
- 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;
97
67
  return {
98
- type: "error",
99
- error: error instanceof Error ? error.message : String(error),
100
- stack: error instanceof Error ? error.stack : void 0
68
+ type: "result",
69
+ results: r.result,
70
+ ...state
101
71
  };
102
72
  }
103
- /**
104
- * Worker process for isolated benchmark execution.
105
- * Uses eval() safely in isolated child process with trusted code.
106
- */
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
+ }
107
102
  process.on("message", async (message) => {
108
103
  if (message.type !== "run") return;
109
104
  logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);
110
105
  try {
111
106
  const start = getPerfNow();
112
- const baseRunner = await createRunner(message.runnerName);
113
- const runner = message.options.adaptive ? createAdaptiveWrapper(baseRunner, message.options) : baseRunner;
107
+ const runner = await createBenchRunner(message.runnerName, message.options);
114
108
  logTiming("Runner created in", getElapsed(start));
115
109
  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
- }
110
+ const result = await runWithProfiling(message, runner);
111
+ logTiming("Benchmark execution took", getElapsed(benchStart));
112
+ sendAndExit(result, 0);
146
113
  } catch (error) {
147
- sendAndExit(createErrorMessage(error), 1);
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);
148
120
  }
149
121
  });
150
122
  setTimeout(() => {
@@ -152,7 +124,7 @@ setTimeout(() => {
152
124
  process.exit(1);
153
125
  }, maxLifetime);
154
126
  process.stdin.pause();
155
-
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\"; // 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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"}
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};