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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewerServer-BJhdnxlN.mjs","names":[],"sources":["../src/profiling/node/ResolvedProfile.ts","../src/stats/BootstrapDifference.ts","../src/report/BenchmarkReport.ts","../src/export/SpeedscopeTypes.ts","../src/export/AllocExport.ts","../src/export/ArchiveFormat.ts","../src/export/ArchiveExport.ts","../src/cli/ViewerServer.ts"],"sourcesContent":["import type {\n CallFrame,\n HeapProfile,\n HeapSample,\n ProfileNode,\n} from \"./HeapSampler.ts\";\n\n/** A call frame with display-ready 1-indexed source positions */\nexport interface ResolvedFrame {\n /** Function name, \"(anonymous)\" when empty */\n name: string;\n\n /** Script URL or file path, \"\" when unknown */\n url: string;\n\n /** 1-indexed line number */\n line: number;\n\n /** 1-indexed column number (undefined when unknown) */\n col?: number;\n}\n\n/** A profile node with its resolved call stack from root to this node */\nexport interface ResolvedNode {\n /** The call frame at this node */\n frame: ResolvedFrame;\n\n /** Call stack from root to this node (inclusive) */\n stack: ResolvedFrame[];\n\n /** Bytes allocated directly at this node */\n selfSize: number;\n\n /** V8 node ID, used to match {@link HeapSample.nodeId} */\n nodeId: number;\n}\n\n/** Pre-resolved heap profile: single tree walk produces all derived data */\nexport interface ResolvedProfile {\n /** All nodes from the profile tree, flattened */\n nodes: ResolvedNode[];\n\n /** nodeId -> ResolvedNode lookup */\n nodeMap: Map<number, ResolvedNode>;\n\n /** Nodes with selfSize > 0, sorted by selfSize descending */\n allocationNodes: ResolvedNode[];\n\n /** Samples sorted by ordinal (temporal order), if available */\n sortedSamples: HeapSample[] | undefined;\n\n /** Total bytes across all nodes (sum of selfSize) */\n totalBytes: number;\n}\n\n/** Convert a V8 0-indexed CallFrame to a display-ready 1-indexed ResolvedFrame */\nexport function resolveCallFrame(cf: CallFrame): ResolvedFrame {\n return {\n name: cf.functionName || \"(anonymous)\",\n url: cf.url || \"\",\n line: cf.lineNumber + 1,\n col: cf.columnNumber != null ? cf.columnNumber + 1 : undefined,\n };\n}\n\n/** Walk a HeapProfile tree once, producing a fully resolved intermediate form */\nexport function resolveProfile(profile: HeapProfile): ResolvedProfile {\n const nodes: ResolvedNode[] = [];\n const nodeMap = new Map<number, ResolvedNode>();\n let totalBytes = 0;\n\n function walk(node: ProfileNode, parentStack: ResolvedFrame[]): void {\n const frame = resolveCallFrame(node.callFrame);\n const stack = [...parentStack, frame];\n const resolved: ResolvedNode = {\n frame,\n stack,\n selfSize: node.selfSize,\n nodeId: node.id,\n };\n nodes.push(resolved);\n nodeMap.set(node.id, resolved);\n totalBytes += node.selfSize;\n for (const child of node.children ?? []) walk(child, stack);\n }\n\n walk(profile.head, []);\n\n const allocationNodes = nodes\n .filter(n => n.selfSize > 0)\n .sort((a, b) => b.selfSize - a.selfSize);\n\n const sortedSamples = profile.samples\n ? [...profile.samples].sort((a, b) => a.ordinal - b.ordinal)\n : undefined;\n\n return { nodes, nodeMap, allocationNodes, sortedSamples, totalBytes };\n}\n","import type {\n BootstrapResult,\n CIDirection,\n DifferenceCI,\n HistogramBin,\n StatKind,\n} from \"./StatisticalUtils.ts\";\nimport {\n average,\n bootstrapSamples,\n computeInterval,\n createResample,\n defaultConfidence,\n isBootstrappable,\n maxBootstrapInput,\n maxOf,\n minOf,\n percentile,\n prepareBlocks,\n quickSelect,\n resampleInto,\n statKindToFn,\n subsample,\n} from \"./StatisticalUtils.ts\";\n\n/** Options for blockDifferenceCI (extends DiffOptions with block parameters) */\nexport type BlockDiffOptions = DiffOptions & {\n /** Block boundaries for the second sample array (defaults to blocksA) */\n blocksB?: number[];\n /** Disable Tukey trimming of outlier batches */\n noBatchTrim?: boolean;\n};\n\n/** Options for difference CI functions */\ntype DiffOptions = {\n /** Number of bootstrap resamples (default: 10000) */\n resamples?: number;\n /** Confidence level 0-1 (default: 0.95) */\n confidence?: number;\n /** Equivalence margin in percent. CI within [-margin, +margin] ==> \"equivalent\" */\n equivMargin?: number;\n};\n\ntype BinnedCI = {\n estimate: number;\n ci: [number, number];\n histogram: HistogramBin[];\n};\n\ninterface DiffOp {\n origIndex: number;\n execIndex: number;\n computeA: (buf: number[]) => number;\n computeB: (buf: number[]) => number;\n pointEstimate: (s: number[]) => number;\n}\n\n/** @return sample-level bootstrap CI for percentage difference between baseline (a) and current (b). */\nexport function sampleDifferenceCI(\n a: number[],\n b: number[],\n statFn: (s: number[]) => number,\n options: DiffOptions = {},\n): DifferenceCI {\n const { resamples = bootstrapSamples, confidence: conf = defaultConfidence } =\n options;\n const baseVal = statFn(a);\n const currVal = statFn(b);\n const observedPct = ((currVal - baseVal) / baseVal) * 100;\n\n const subA = subsample(a, maxBootstrapInput);\n const subB = subsample(b, maxBootstrapInput);\n const bufA = new Array(subA.length);\n const bufB = new Array(subB.length);\n const diffs = Array.from({ length: resamples }, () => {\n resampleInto(subA, bufA);\n resampleInto(subB, bufB);\n const base = statFn(bufA);\n return ((statFn(bufB) - base) / base) * 100;\n });\n const ci = computeInterval(diffs, conf);\n const capped = subA !== a || subB !== b;\n return {\n percent: observedPct,\n ci,\n direction: classifyDirection(ci, observedPct, options.equivMargin),\n histogram: binValues(diffs),\n ciLevel: \"sample\",\n ...(capped && { subsampled: Math.max(a.length, b.length) }),\n };\n}\n\n/** Shared-resample difference CI: one resample pair per iteration, all stats computed.\n * @return DifferenceCI[] in same order as input stats. */\nexport function multiSampleDifferenceCI(\n a: number[],\n b: number[],\n stats: StatKind[],\n options: DiffOptions = {},\n): DifferenceCI[] {\n const { resamples = bootstrapSamples, confidence: conf = defaultConfidence } =\n options;\n const subA = subsample(a, maxBootstrapInput);\n const subB = subsample(b, maxBootstrapInput);\n const bufA = new Array(subA.length);\n const bufB = new Array(subB.length);\n const ops = buildDiffOps(stats, subA.length, subB.length);\n const allDiffs = ops.map(() => new Array<number>(resamples));\n\n // Point estimates from original data\n const baseVals = ops.map(op => op.pointEstimate(a));\n const currVals = ops.map(op => op.pointEstimate(b));\n const observedPcts = ops.map(\n (_, j) => ((currVals[j] - baseVals[j]) / baseVals[j]) * 100,\n );\n\n for (let i = 0; i < resamples; i++) {\n resampleInto(subA, bufA);\n resampleInto(subB, bufB);\n for (let j = 0; j < ops.length; j++) {\n const base = ops[j].computeA(bufA);\n const curr = ops[j].computeB(bufB);\n allDiffs[j][i] = ((curr - base) / base) * 100;\n }\n }\n\n const capped = subA !== a || subB !== b;\n const results = new Array<DifferenceCI>(stats.length);\n for (const op of ops) {\n const j = op.execIndex;\n const ci = computeInterval(allDiffs[j], conf);\n results[op.origIndex] = {\n percent: observedPcts[j],\n ci,\n direction: classifyDirection(ci, observedPcts[j], options.equivMargin),\n histogram: binValues(allDiffs[j]),\n ciLevel: \"sample\",\n ...(capped && { subsampled: Math.max(a.length, b.length) }),\n };\n }\n return results;\n}\n\n/** Difference CIs for multiple stats, dispatching block vs sample automatically.\n * Returns undefined for non-bootstrappable stats (min/max). */\nexport function diffCIs(\n a: number[],\n aOffsets: number[] | undefined,\n b: number[],\n bOffsets: number[] | undefined,\n stats: StatKind[],\n options: BlockDiffOptions = {},\n): (DifferenceCI | undefined)[] {\n const bsStats = stats.filter(isBootstrappable);\n if (bsStats.length === 0) return stats.map(() => undefined);\n\n const hasBlocks =\n (aOffsets?.length ?? 0) >= 2 && (bOffsets?.length ?? 0) >= 2;\n const bsResults = hasBlocks\n ? bsStats.map(s =>\n blockDifferenceCI(a, aOffsets!, b, statKindToFn(s), {\n ...options,\n blocksB: bOffsets!,\n }),\n )\n : multiSampleDifferenceCI(a, b, bsStats, options);\n\n const results: (DifferenceCI | undefined)[] = new Array(stats.length);\n let bi = 0;\n for (let i = 0; i < stats.length; i++) {\n results[i] = isBootstrappable(stats[i]) ? bsResults[bi++] : undefined;\n }\n return results;\n}\n\n/** @return block bootstrap CI for percentage difference between baseline (a) and current (b).\n * Tukey-trims outlier batches, then resamples per-block statFn values. Requires 2+ blocks. */\nexport function blockDifferenceCI(\n a: number[],\n blocksA: number[],\n b: number[],\n statFn: (s: number[]) => number,\n options: BlockDiffOptions = {},\n): DifferenceCI {\n const { resamples = bootstrapSamples, confidence: conf = defaultConfidence } =\n options;\n const bB = options.blocksB ?? blocksA;\n const noTrim = options.noBatchTrim;\n const sideA = prepareBlocks(a, blocksA, statFn, noTrim);\n const sideB = prepareBlocks(b, bB, statFn, noTrim);\n\n const baseVal = statFn(sideA.filtered);\n const currVal = statFn(sideB.filtered);\n const observedPct = ((currVal - baseVal) / baseVal) * 100;\n\n const drawA = () => average(createResample(sideA.blockVals));\n const drawB = () => average(createResample(sideB.blockVals));\n const diffs = Array.from({ length: resamples }, () => {\n const base = drawA();\n return ((drawB() - base) / base) * 100;\n });\n const ci = computeInterval(diffs, conf);\n return {\n percent: observedPct,\n ci,\n direction: classifyDirection(ci, observedPct, options.equivMargin),\n histogram: binValues(diffs),\n trimmed: [sideA.trimCount, sideB.trimCount],\n ciLevel: \"block\",\n };\n}\n\n/** @return binned CI with histogram from a BootstrapResult */\nexport function binBootstrapResult(result: BootstrapResult): BinnedCI {\n const { estimate, ci, samples } = result;\n return { estimate, ci, histogram: binValues(samples) };\n}\n\n/** @return CI direction, with optional equivalence margin (in percent) */\nfunction classifyDirection(\n ci: [number, number],\n observed: number,\n margin?: number,\n): CIDirection {\n const withinMargin =\n margin != null && margin > 0 && ci[0] >= -margin && ci[1] <= margin;\n if (withinMargin) return \"equivalent\";\n const excludesZero = ci[0] > 0 || ci[1] < 0;\n if (excludesZero) return observed < 0 ? \"faster\" : \"slower\";\n return \"uncertain\";\n}\n\n/** @return values binned into histogram for compact visualization */\nfunction binValues(values: number[], binCount = 30): HistogramBin[] {\n let min = values[0];\n let max = values[0];\n for (let i = 1; i < values.length; i++) {\n if (values[i] < min) min = values[i];\n if (values[i] > max) max = values[i];\n }\n if (min === max) return [{ x: min, count: values.length }];\n\n const step = (max - min) / binCount;\n const counts = new Array(binCount).fill(0);\n for (const v of values) {\n const bin = Math.min(Math.floor((v - min) / step), binCount - 1);\n counts[bin]++;\n }\n return counts.map((count, i) => ({ x: min + (i + 0.5) * step, count }));\n}\n\n/** Build diff operations: mean/min/max first (non-destructive), then percentiles ascending.\n * Each side (A, B) gets its own quickSelect k values since sample sizes may differ. */\nfunction buildDiffOps(stats: StatKind[], nA: number, nB: number): DiffOp[] {\n const uniform = (order: number, i: number, fn: (s: number[]) => number) => ({\n order,\n origIndex: i,\n execIndex: 0,\n computeA: fn,\n computeB: fn,\n pointEstimate: fn,\n });\n const entries = stats.map((s, i) => {\n if (s === \"mean\") return uniform(-3, i, average);\n if (s === \"min\") return uniform(-2, i, minOf);\n if (s === \"max\") return uniform(-1, i, maxOf);\n const p = s.percentile;\n const kA = Math.max(0, Math.ceil(nA * p) - 1);\n const kB = Math.max(0, Math.ceil(nB * p) - 1);\n return {\n order: p,\n origIndex: i,\n execIndex: 0,\n computeA: (buf: number[]) => quickSelect(buf, kA),\n computeB: (buf: number[]) => quickSelect(buf, kB),\n pointEstimate: (v: number[]) => percentile(v, p),\n };\n });\n entries.sort((a, b) => a.order - b.order);\n for (let i = 0; i < entries.length; i++) entries[i].execIndex = i;\n return entries;\n}\n","import type { MeasuredResults } from \"../runners/MeasuredResults.ts\";\nimport { diffCIs } from \"../stats/BootstrapDifference.ts\";\nimport {\n computeStat,\n type DifferenceCI,\n flipCI,\n type StatKind,\n swapDirection,\n} from \"../stats/StatisticalUtils.ts\";\n\nimport type { AnyColumn } from \"./text/TableReport.ts\";\n\n/** Options that affect baseline comparison statistics */\nexport interface ComparisonOptions {\n /** Equivalence margin in percent (0 to disable) */\n equivMargin?: number;\n /** Disable Tukey trimming of outlier batches */\n noBatchTrim?: boolean;\n}\n\n/** Benchmark results with optional baseline for comparison */\nexport interface ReportGroup {\n name: string;\n reports: BenchmarkReport[];\n baseline?: BenchmarkReport;\n}\n\n/** Results from a single benchmark run */\nexport interface BenchmarkReport {\n name: string;\n measuredResults: MeasuredResults;\n metadata?: UnknownRecord;\n}\n\n/** A titled group of related columns (one per report section) */\nexport interface ReportSection {\n title: string;\n columns: ReportColumn[];\n}\n\n/** A table column with optional comparison behavior */\nexport type ReportColumn = AnyColumn<Record<string, unknown>> & {\n /** Add diff column after this column when baseline exists */\n comparable?: boolean;\n /** Set true for throughput metrics where higher values are better (e.g., lines/sec) */\n higherIsBetter?: boolean;\n /** Stat descriptor: framework computes value from samples via computeStat */\n statKind?: StatKind;\n /** Accessor for non-sample data (e.g., run count, metadata fields) */\n value?: (results: MeasuredResults, metadata?: UnknownRecord) => unknown;\n /** Convert a timing-domain value to display domain (e.g., ms to lines/sec) */\n toDisplay?: (\n timingValue: number,\n metadata?: Record<string, unknown>,\n ) => number;\n};\n\nexport type UnknownRecord = Record<string, unknown>;\n\n/** Compute column values for a section from results + metadata.\n * statKind columns: computeStat(samples, kind), then toDisplay.\n * value columns: call the accessor directly. */\nexport function computeColumnValues(\n section: ReportSection,\n results: MeasuredResults,\n metadata?: UnknownRecord,\n): UnknownRecord {\n return Object.fromEntries(\n section.columns.map(col => {\n const key = col.key ?? col.title;\n if (col.value) return [key, col.value(results, metadata)];\n if (col.statKind) {\n const raw = computeStat(results.samples, col.statKind);\n return [key, col.toDisplay ? col.toDisplay(raw, metadata) : raw];\n }\n return [key, undefined];\n }),\n );\n}\n\n/** Run each section's computeColumnValues and merge into one record */\nexport function extractSectionValues(\n measuredResults: MeasuredResults,\n sections: ReadonlyArray<ReportSection>,\n metadata?: UnknownRecord,\n): UnknownRecord {\n const entries = sections.flatMap(s =>\n Object.entries(computeColumnValues(s, measuredResults, metadata)),\n );\n return Object.fromEntries(entries);\n}\n\n/** All reports in a group, including the baseline if present */\nexport function groupReports(group: ReportGroup): BenchmarkReport[] {\n return group.baseline ? [...group.reports, group.baseline] : group.reports;\n}\n\n/** True if any result in the groups has the specified field with a defined value */\nexport function hasField(\n groups: ReportGroup[],\n field: keyof MeasuredResults,\n): boolean {\n return groups.some(group =>\n groupReports(group).some(\n ({ measuredResults }) => measuredResults[field] !== undefined,\n ),\n );\n}\n\n/** @return true if the first comparable column in sections has higherIsBetter set */\nexport function isHigherIsBetter(sections: ReportSection[]): boolean {\n return (\n sections.flatMap(s => s.columns).find(c => c.comparable)?.higherIsBetter ??\n false\n );\n}\n\n/** @return the first comparable column with a statKind across all sections */\nexport function findPrimaryColumn(\n sections?: ReportSection[],\n): ReportColumn | undefined {\n if (!sections) return undefined;\n return sections.flatMap(s => s.columns).find(c => c.comparable && c.statKind);\n}\n\n/** Bootstrap difference CI for a column, using batch structure when available */\nexport function computeDiffCI(\n baseline: MeasuredResults | undefined,\n current: MeasuredResults,\n statKind: StatKind,\n comparison?: ComparisonOptions,\n higherIsBetter?: boolean,\n): DifferenceCI | undefined {\n if (!baseline?.samples?.length || !current.samples?.length) return undefined;\n const { equivMargin, noBatchTrim } = comparison ?? {};\n const rawCIs = diffCIs(\n baseline.samples,\n baseline.batchOffsets,\n current.samples,\n current.batchOffsets,\n [statKind],\n { equivMargin, noBatchTrim },\n );\n if (!rawCIs[0]) return undefined;\n return higherIsBetter ? swapDirection(flipCI(rawCIs[0])) : rawCIs[0];\n}\n","/** Shared speedscope file format types and frame interning utilities. */\n\n/** speedscope file format (https://www.speedscope.app/file-format-schema.json) */\nexport interface SpeedscopeFile {\n $schema: \"https://www.speedscope.app/file-format-schema.json\";\n shared: { frames: SpeedscopeFrame[] };\n profiles: SpeedscopeProfile[];\n name?: string;\n exporter?: string;\n}\n\n/** A single call frame with optional source location */\nexport interface SpeedscopeFrame {\n name: string;\n file?: string;\n line?: number;\n col?: number;\n}\n\n/** Union of heap and time profile shapes (unit differs) */\nexport type SpeedscopeProfile = SpeedscopeHeapProfile | SpeedscopeTimeProfile;\n\n/** Heap allocation profile weighted by bytes */\nexport interface SpeedscopeHeapProfile {\n type: \"sampled\";\n name: string;\n unit: \"bytes\";\n startValue: number;\n endValue: number;\n samples: number[][];\n weights: number[];\n}\n\n/** CPU time profile weighted by microseconds */\nexport interface SpeedscopeTimeProfile {\n type: \"sampled\";\n name: string;\n unit: \"microseconds\";\n startValue: number;\n endValue: number;\n samples: number[][];\n weights: number[];\n}\n\n/** Shared mutable state for frame interning across profiles. */\nexport interface FrameContext {\n frames: SpeedscopeFrame[];\n index: Map<string, number>;\n}\n\n/** Create an empty FrameContext for building speedscope profiles. */\nexport function frameContext(): FrameContext {\n return { frames: [], index: new Map() };\n}\n\n/** Wrap profiles in a SpeedscopeFile envelope */\nexport function speedscopeFile(\n ctx: FrameContext,\n profiles: SpeedscopeProfile[],\n): SpeedscopeFile {\n return {\n $schema: \"https://www.speedscope.app/file-format-schema.json\",\n shared: { frames: ctx.frames },\n profiles,\n exporter: \"benchforge\",\n };\n}\n\n/** Intern a call frame, returning its index in the shared frames array.\n * All values should be 1-indexed (caller converts from V8's 0-indexed if needed). */\nexport function internFrame(\n name: string,\n url: string,\n line: number,\n col: number | undefined | null,\n ctx: FrameContext,\n): number {\n const key = `${name}\\0${url}\\0${line}\\0${col}`;\n\n const existing = ctx.index.get(key);\n if (existing !== undefined) return existing;\n\n const idx = ctx.frames.length;\n const entry: SpeedscopeFrame = { name: displayName(name, url, line) };\n if (url) entry.file = url;\n if (line > 0) entry.line = line;\n if (col != null) entry.col = col;\n ctx.frames.push(entry);\n ctx.index.set(key, idx);\n return idx;\n}\n\n/** Display name for a frame: named functions use their name, anonymous get a location hint */\nfunction displayName(name: string, url: string, line: number): string {\n if (name !== \"(anonymous)\") return name;\n const file = url?.split(\"/\").pop();\n return file ? `(anonymous ${file}:${line})` : \"(anonymous)\";\n}\n","/** Heap profile export to Speedscope format. */\n\nimport { writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { HeapProfile } from \"../profiling/node/HeapSampler.ts\";\nimport {\n type ResolvedProfile,\n resolveProfile,\n} from \"../profiling/node/ResolvedProfile.ts\";\nimport { groupReports, type ReportGroup } from \"../report/BenchmarkReport.ts\";\nimport {\n type FrameContext,\n frameContext,\n internFrame,\n type SpeedscopeFile,\n type SpeedscopeHeapProfile,\n speedscopeFile,\n} from \"./SpeedscopeTypes.ts\";\n\n/** Export heap profiles to speedscope JSON. Returns output path, or undefined if no profiles. */\nexport function exportSpeedscope(\n groups: ReportGroup[],\n outputPath: string,\n): string | undefined {\n const file = buildSpeedscopeFile(groups);\n if (!file) {\n console.log(\"No heap profiles to export.\");\n return undefined;\n }\n\n const absPath = resolve(outputPath);\n writeFileSync(absPath, JSON.stringify(file));\n console.log(`Speedscope profile exported to: ${outputPath}`);\n return absPath;\n}\n\n/** Convert a single HeapProfile to speedscope format. */\nexport function heapProfileToSpeedscope(\n name: string,\n profile: HeapProfile,\n): SpeedscopeFile {\n const ctx = frameContext();\n const p = buildProfile(name, resolveProfile(profile), ctx);\n return speedscopeFile(ctx, [p]);\n}\n\n/** Build SpeedscopeFile from report groups. Returns undefined if no profiles found. */\nexport function buildSpeedscopeFile(\n groups: ReportGroup[],\n): SpeedscopeFile | undefined {\n const ctx = frameContext();\n const profiles: SpeedscopeHeapProfile[] = [];\n\n for (const group of groups) {\n for (const report of groupReports(group)) {\n const { heapProfile } = report.measuredResults;\n if (!heapProfile) continue;\n const resolved = resolveProfile(heapProfile);\n profiles.push(buildProfile(report.name, resolved, ctx));\n }\n }\n\n if (profiles.length === 0) return undefined;\n\n return speedscopeFile(ctx, profiles);\n}\n\n/** Build a single speedscope profile from a resolved heap profile. */\nfunction buildProfile(\n name: string,\n resolved: ResolvedProfile,\n ctx: FrameContext,\n): SpeedscopeHeapProfile {\n type Frame = { name: string; url: string; line: number; col?: number | null };\n const intern = (f: Frame) => internFrame(f.name, f.url, f.line, f.col, ctx);\n\n const nodeStacks = new Map(\n resolved.nodes.map(node => [node.nodeId, node.stack.map(intern)] as const),\n );\n\n if (!resolved.sortedSamples?.length) {\n console.error(\n `Speedscope export: no samples in heap profile for \"${name}\", skipping`,\n );\n return emptyProfile(name);\n }\n\n const samples: number[][] = [];\n const weights: number[] = [];\n for (const sample of resolved.sortedSamples) {\n const stack = nodeStacks.get(sample.nodeId);\n if (stack) {\n samples.push(stack);\n weights.push(sample.size);\n }\n }\n\n const totalBytes = weights.reduce((sum, w) => sum + w, 0);\n return {\n type: \"sampled\",\n name,\n unit: \"bytes\",\n startValue: 0,\n endValue: totalBytes,\n samples,\n weights,\n };\n}\n\n/** Placeholder profile with no samples (used when heap data is missing). */\nfunction emptyProfile(name: string): SpeedscopeHeapProfile {\n return {\n type: \"sampled\",\n name,\n unit: \"bytes\",\n startValue: 0,\n endValue: 0,\n samples: [],\n weights: [],\n };\n}\n","/** Serialized `.benchforge` archive format. */\n\nimport type { ReportData } from \"../viewer/ReportData.ts\";\nimport type { LineCoverage } from \"./CoverageExport.ts\";\nimport type { SpeedscopeFile } from \"./SpeedscopeTypes.ts\";\n\nexport interface BenchforgeArchive {\n /** Archive format version. */\n schema: number;\n\n /** Heap allocation profile in Speedscope format. */\n allocProfile?: SpeedscopeFile;\n\n /** CPU time profile in Speedscope format. */\n timeProfile?: SpeedscopeFile;\n\n /** Per-line coverage data keyed by source URL. */\n coverage?: Record<string, LineCoverage[]>;\n\n /** Benchmark report with suite results and statistics. */\n report?: ReportData;\n\n /** Source file contents keyed by file URL. */\n sources: Record<string, string>;\n\n /** Archive creation metadata. */\n metadata: ArchiveMetadata;\n}\n\nexport interface ArchiveMetadata {\n /** ISO timestamp (colons/periods replaced with dashes for filename safety). */\n timestamp: string;\n\n /** Benchforge package version. */\n benchforgeVersion: string;\n}\n\nexport const archiveSchemaVersion = 2;\n\n/** Migrate a parsed archive from older schema versions to current. */\nexport function migrateArchive(\n raw: Record<string, unknown>,\n): Partial<BenchforgeArchive> {\n const schema = (raw.schema as number) ?? 0;\n if (schema <= 1 && \"profile\" in raw && !(\"allocProfile\" in raw)) {\n raw.allocProfile = raw.profile;\n delete raw.profile;\n }\n return raw as Partial<BenchforgeArchive>;\n}\n","/** .benchforge archive creation, source collection, and archive filename derivation. */\n\nimport { writeFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ReportGroup } from \"../report/BenchmarkReport.ts\";\nimport type { ReportData } from \"../viewer/ReportData.ts\";\nimport { buildSpeedscopeFile } from \"./AllocExport.ts\";\nimport {\n archiveSchemaVersion,\n type BenchforgeArchive,\n} from \"./ArchiveFormat.ts\";\nimport type { LineCoverage } from \"./CoverageExport.ts\";\nimport type { SpeedscopeFile } from \"./SpeedscopeTypes.ts\";\n\nexport interface ArchiveOptions {\n groups: ReportGroup[];\n reportData?: ReportData;\n timeProfileData?: string;\n coverageData?: string;\n outputPath?: string;\n}\n\nexport interface ArchiveInput {\n allocProfile?: SpeedscopeFile;\n timeProfile?: SpeedscopeFile;\n coverage?: Record<string, LineCoverage[]>;\n report?: ReportData;\n sources: Record<string, string>;\n}\n\n/** Build a .benchforge archive file. Returns output path, or undefined if nothing to archive. */\nexport async function archiveBenchmark(\n options: ArchiveOptions,\n): Promise<string | undefined> {\n const { groups, reportData, timeProfileData, coverageData, outputPath } =\n options;\n const allocProfile = buildSpeedscopeFile(groups) ?? undefined;\n const timeProfile = timeProfileData ? JSON.parse(timeProfileData) : undefined;\n if (!allocProfile && !timeProfile && !reportData) {\n console.log(\"No data to archive.\");\n return undefined;\n }\n\n const allFrames = collectProfileFrames(allocProfile, timeProfile);\n const sources = allFrames.length ? await collectSources(allFrames) : {};\n const coverage = coverageData ? JSON.parse(coverageData) : undefined;\n const input: ArchiveInput = {\n allocProfile,\n timeProfile,\n coverage,\n report: reportData,\n sources,\n };\n const { archive, timestamp } = buildArchiveObject(input);\n const filename = outputPath || defaultArchiveName(allocProfile, timestamp);\n const absPath = resolve(filename);\n writeFileSync(absPath, JSON.stringify(archive));\n console.log(`Archive written to: ${filename}`);\n return absPath;\n}\n\nexport function buildArchiveObject(input: ArchiveInput): {\n archive: BenchforgeArchive;\n timestamp: string;\n} {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const archive = {\n schema: archiveSchemaVersion,\n allocProfile: input.allocProfile,\n timeProfile: input.timeProfile,\n coverage: input.coverage,\n report: input.report,\n sources: input.sources,\n metadata: {\n timestamp,\n benchforgeVersion: process.env.npm_package_version || \"unknown\",\n },\n };\n return { archive, timestamp };\n}\n\nexport function collectProfileFrames(\n allocProfile: SpeedscopeFile | null | undefined,\n timeProfile: { shared?: { frames: { file?: string }[] } } | null | undefined,\n): { file?: string }[] {\n const heapFrames = allocProfile?.shared?.frames ?? [];\n const timeFrames = timeProfile?.shared?.frames ?? [];\n return [...heapFrames, ...timeFrames];\n}\n\n/** Fetch source code for all unique file URLs in profile frames. */\nexport async function collectSources(\n frames: { file?: string }[],\n cache?: Map<string, string>,\n): Promise<Record<string, string>> {\n const urls = new Set(frames.map(f => f.file).filter((u): u is string => !!u));\n\n const sources: Record<string, string> = {};\n for (const url of urls) {\n const cached = cache?.get(url);\n const text = cached ?? (await fetchSource(url));\n if (text === undefined) continue;\n sources[url] = text;\n if (!cached) cache?.set(url, text);\n }\n\n return sources;\n}\n\n/** Derive archive filename from profile (or generic fallback). */\nexport function defaultArchiveName(\n profile: SpeedscopeFile | null | undefined,\n timestamp: string,\n): string {\n return profile\n ? archiveFileName(profile, timestamp)\n : `benchforge-${timestamp}.benchforge`;\n}\n\n/** Fetch source text from a file:// or http(s):// URL. */\nexport async function fetchSource(url: string): Promise<string | undefined> {\n try {\n if (url.startsWith(\"file://\")) {\n return await readFile(fileURLToPath(url), \"utf-8\");\n }\n const resp = await fetch(url, { signal: AbortSignal.timeout(5000) });\n if (!resp.ok) return undefined;\n return await resp.text();\n } catch {\n return undefined;\n }\n}\n\n/** Derive an archive filename from the profile name (sanitizes URLs to safe filenames). */\nfunction archiveFileName(file: SpeedscopeFile, timestamp: string): string {\n const raw = file.profiles[0]?.name || \"profile\";\n const sanitized = raw\n .replace(/^https?:\\/\\//, \"\")\n .replace(/[^a-zA-Z0-9._-]+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n const base = sanitized || \"profile\";\n return `${base}-${timestamp}.benchforge`;\n}\n","import { readFile } from \"node:fs/promises\";\nimport {\n createServer,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from \"node:http\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport open from \"open\";\nimport sirv from \"sirv\";\nimport {\n buildArchiveObject,\n collectProfileFrames,\n collectSources,\n defaultArchiveName,\n fetchSource,\n} from \"../export/ArchiveExport.ts\";\nimport {\n archiveSchemaVersion,\n migrateArchive,\n} from \"../export/ArchiveFormat.ts\";\n\nexport interface ViewerServerOptions {\n /** Speedscope JSON profile data (allocation) */\n profileData?: string;\n /** Speedscope JSON profile data (time/CPU) */\n timeProfileData?: string;\n /** Per-function coverage data (JSON-serialized Record<url, LineCoverage[]>) */\n coverageData?: string;\n /** HTML report JSON data */\n reportData?: string;\n /** Editor URI prefix for Cmd+Shift+click (e.g. \"vscode://file\") */\n editorUri?: string;\n /** Port to listen on (default 3939) */\n port?: number;\n /** Open browser on start (default true) */\n open?: boolean;\n /** Pre-loaded sources (e.g. from an archive) to seed the source cache */\n sources?: Record<string, string>;\n}\n\ntype RouteHandler = (\n res: ServerResponse,\n query: string,\n method: string,\n) => Promise<void> | void;\n\n/** Start the viewer HTTP server and open in browser. */\nexport async function startViewerServer(\n options: ViewerServerOptions,\n): Promise<{ server: Server; port: number; close: () => void }> {\n const port = options.port ?? 3939;\n\n const sourceCache = new Map(Object.entries(options.sources ?? {}));\n\n const assets = sirv(join(packageRoot(), \"dist/viewer\"), { single: true });\n const handler = createRequestHandler(options, sourceCache, assets);\n const server = createServer(handler);\n\n const bound = await tryListen(server, port);\n const url = `http://localhost:${bound.port}`;\n if (options.open !== false) await open(url);\n console.log(`Viewer: ${url}`);\n\n const close = () => {\n bound.server.closeAllConnections();\n bound.server.close();\n };\n return { server: bound.server, port: bound.port, close };\n}\n\n/** Open a .benchforge archive in the viewer. */\nexport async function viewArchive(filePath: string): Promise<void> {\n const absPath = resolve(filePath);\n const content = await readFile(absPath, \"utf-8\");\n const raw = JSON.parse(content);\n\n const schema = raw.schema ?? 0;\n if (schema > archiveSchemaVersion) {\n const msg = `Archive schema version ${schema} is newer than supported (${archiveSchemaVersion}).`;\n console.error(`${msg} Please update benchforge to view this archive.`);\n process.exit(1);\n }\n\n const archive = migrateArchive(raw);\n const sources = archive.sources as Record<string, string> | undefined;\n const { close } = await startViewerServer({\n profileData: optionalJson(archive.allocProfile),\n timeProfileData: optionalJson(archive.timeProfile),\n coverageData: optionalJson(archive.coverage),\n reportData: optionalJson(archive.report),\n sources,\n });\n\n await waitForCtrlC();\n close();\n}\n\n/** Serialize a value to JSON if truthy, otherwise return undefined. */\nexport function optionalJson(v: unknown): string | undefined {\n return v ? JSON.stringify(v) : undefined;\n}\n\n/** Wait for Ctrl+C (SIGINT) before resolving. */\nexport function waitForCtrlC(): Promise<void> {\n return new Promise(resolve => {\n console.log(\"\\nPress Ctrl+C to exit\");\n process.once(\"SIGINT\", () => {\n console.log();\n resolve();\n });\n });\n}\n\n/** Resolve the package root (dev: src/cli/ ==> up 2, dist: dist/ ==> up 1). */\nfunction packageRoot(): string {\n const thisDir = dirname(fileURLToPath(import.meta.url));\n if (basename(thisDir) === \"cli\") return join(thisDir, \"../..\");\n return join(thisDir, \"..\");\n}\n\n/** Build HTTP request handler with API routes and static asset fallback. */\nfunction createRequestHandler(\n ctx: ViewerServerOptions,\n sourceCache: Map<string, string>,\n assets: ReturnType<typeof sirv>,\n): (req: IncomingMessage, res: ServerResponse) => void {\n const routes: Record<string, RouteHandler> = {\n \"/api/config\": res => {\n const config = {\n editorUri: ctx.editorUri || null,\n hasReport: !!ctx.reportData,\n hasProfile: !!ctx.profileData,\n hasTimeProfile: !!ctx.timeProfileData,\n hasCoverage: !!ctx.coverageData,\n };\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify(config));\n },\n \"/api/report-data\": res => sendJson(res, ctx.reportData, \"report data\"),\n \"/api/coverage\": res => sendJson(res, ctx.coverageData, \"coverage data\"),\n \"/api/profile\": res => sendJson(res, ctx.profileData, \"profile data\", true),\n \"/api/profile/alloc\": res =>\n sendJson(res, ctx.profileData, \"profile data\", true),\n \"/api/profile/time\": res =>\n sendJson(res, ctx.timeProfileData, \"time profile data\", true),\n \"/api/source\": (res, query) => handleSourceRequest(res, query, sourceCache),\n \"/api/archive\": (res, _q, method) => {\n if (method !== \"POST\") {\n res.statusCode = 405;\n return void res.end(\"Method not allowed\");\n }\n return handleArchiveRequest(res, ctx, sourceCache);\n },\n };\n\n return async (req, res) => {\n const url = req.url || \"/\";\n const qIdx = url.indexOf(\"?\");\n const pathname = qIdx >= 0 ? url.slice(0, qIdx) : url;\n const query = qIdx >= 0 ? url.slice(qIdx + 1) : \"\";\n\n const handler = routes[pathname];\n if (handler) {\n await handler(res, query, req.method || \"GET\");\n return;\n }\n\n assets(req, res, () => {\n res.statusCode = 404;\n res.end(\"Not found\");\n });\n };\n}\n\n/** Listen on port, retrying on next port if EADDRINUSE. */\nfunction tryListen(\n server: Server,\n port: number,\n maxRetries = 10,\n): Promise<{ server: Server; port: number }> {\n return new Promise((resolve, reject) => {\n let attempt = 0;\n const listen = (p: number) => {\n server.once(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\" && attempt < maxRetries) {\n attempt++;\n listen(p + 1);\n } else {\n reject(err);\n }\n });\n server.listen(p, () => {\n server.removeAllListeners(\"error\");\n const addr = server.address();\n const listenPort = typeof addr === \"object\" && addr ? addr.port : p;\n resolve({ server, port: listenPort });\n });\n };\n listen(port);\n });\n}\n\n/** Send pre-serialized JSON or 404 if data is absent. */\nfunction sendJson(\n res: ServerResponse,\n data: string | undefined,\n label: string,\n cors = false,\n): void {\n if (!data) {\n res.statusCode = 404;\n res.end(`No ${label}`);\n return;\n }\n res.setHeader(\"Content-Type\", \"application/json\");\n if (cors) res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(data);\n}\n\n/** Fetch source text by URL query param, caching for subsequent requests. */\nasync function handleSourceRequest(\n res: ServerResponse,\n query: string,\n cache: Map<string, string>,\n): Promise<void> {\n const params = new URLSearchParams(query);\n const sourceUrl = params.get(\"url\");\n if (!sourceUrl) {\n res.statusCode = 400;\n res.end(\"Missing url parameter\");\n return;\n }\n try {\n let source = cache.get(sourceUrl);\n if (source === undefined) {\n source = await fetchSource(sourceUrl);\n if (source === undefined) throw new Error(\"not found\");\n cache.set(sourceUrl, source);\n }\n res.setHeader(\"Content-Type\", \"text/plain; charset=utf-8\");\n res.end(source);\n } catch {\n res.statusCode = 404;\n res.end(\"Source unavailable\");\n }\n}\n\n/** Build a .benchforge archive from current session data and send as download. */\nasync function handleArchiveRequest(\n res: ServerResponse,\n ctx: ViewerServerOptions,\n sourceCache: Map<string, string>,\n): Promise<void> {\n try {\n const parse = (s?: string) => (s ? JSON.parse(s) : undefined);\n const profile = parse(ctx.profileData);\n const timeProfile = parse(ctx.timeProfileData);\n const coverage = parse(ctx.coverageData);\n const report = parse(ctx.reportData);\n const allFrames = collectProfileFrames(profile, timeProfile);\n const sources = allFrames.length\n ? await collectSources(allFrames, sourceCache)\n : Object.fromEntries(sourceCache);\n const { archive, timestamp } = buildArchiveObject({\n allocProfile: profile,\n timeProfile,\n coverage,\n report,\n sources,\n });\n const body = JSON.stringify(archive);\n const filename = defaultArchiveName(profile, timestamp);\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Content-Disposition\", `attachment; filename=\"${filename}\"`);\n res.end(body);\n } catch {\n res.statusCode = 500;\n res.end(\"Archive failed\");\n }\n}\n"],"mappings":";;;;;;;;;;AAwDA,SAAgB,iBAAiB,IAA8B;AAC7D,QAAO;EACL,MAAM,GAAG,gBAAgB;EACzB,KAAK,GAAG,OAAO;EACf,MAAM,GAAG,aAAa;EACtB,KAAK,GAAG,gBAAgB,OAAO,GAAG,eAAe,IAAI,KAAA;EACtD;;;AAIH,SAAgB,eAAe,SAAuC;CACpE,MAAM,QAAwB,EAAE;CAChC,MAAM,0BAAU,IAAI,KAA2B;CAC/C,IAAI,aAAa;CAEjB,SAAS,KAAK,MAAmB,aAAoC;EACnE,MAAM,QAAQ,iBAAiB,KAAK,UAAU;EAC9C,MAAM,QAAQ,CAAC,GAAG,aAAa,MAAM;EACrC,MAAM,WAAyB;GAC7B;GACA;GACA,UAAU,KAAK;GACf,QAAQ,KAAK;GACd;AACD,QAAM,KAAK,SAAS;AACpB,UAAQ,IAAI,KAAK,IAAI,SAAS;AAC9B,gBAAc,KAAK;AACnB,OAAK,MAAM,SAAS,KAAK,YAAY,EAAE,CAAE,MAAK,OAAO,MAAM;;AAG7D,MAAK,QAAQ,MAAM,EAAE,CAAC;AAUtB,QAAO;EAAE;EAAO;EAAS,iBARD,MACrB,QAAO,MAAK,EAAE,WAAW,EAAE,CAC3B,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS;EAMA,eAJpB,QAAQ,UAC1B,CAAC,GAAG,QAAQ,QAAQ,CAAC,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,QAAQ,GAC1D,KAAA;EAEqD;EAAY;;;;;;ACFvE,SAAgB,wBACd,GACA,GACA,OACA,UAAuB,EAAE,EACT;CAChB,MAAM,EAAE,YAAY,kBAAkB,YAAY,OAAO,sBACvD;CACF,MAAM,OAAO,UAAU,GAAG,kBAAkB;CAC5C,MAAM,OAAO,UAAU,GAAG,kBAAkB;CAC5C,MAAM,OAAO,IAAI,MAAM,KAAK,OAAO;CACnC,MAAM,OAAO,IAAI,MAAM,KAAK,OAAO;CACnC,MAAM,MAAM,aAAa,OAAO,KAAK,QAAQ,KAAK,OAAO;CACzD,MAAM,WAAW,IAAI,UAAU,IAAI,MAAc,UAAU,CAAC;CAG5D,MAAM,WAAW,IAAI,KAAI,OAAM,GAAG,cAAc,EAAE,CAAC;CACnD,MAAM,WAAW,IAAI,KAAI,OAAM,GAAG,cAAc,EAAE,CAAC;CACnD,MAAM,eAAe,IAAI,KACtB,GAAG,OAAQ,SAAS,KAAK,SAAS,MAAM,SAAS,KAAM,IACzD;AAED,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,eAAa,MAAM,KAAK;AACxB,eAAa,MAAM,KAAK;AACxB,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;GACnC,MAAM,OAAO,IAAI,GAAG,SAAS,KAAK;GAClC,MAAM,OAAO,IAAI,GAAG,SAAS,KAAK;AAClC,YAAS,GAAG,MAAO,OAAO,QAAQ,OAAQ;;;CAI9C,MAAM,SAAS,SAAS,KAAK,SAAS;CACtC,MAAM,UAAU,IAAI,MAAoB,MAAM,OAAO;AACrD,MAAK,MAAM,MAAM,KAAK;EACpB,MAAM,IAAI,GAAG;EACb,MAAM,KAAK,gBAAgB,SAAS,IAAI,KAAK;AAC7C,UAAQ,GAAG,aAAa;GACtB,SAAS,aAAa;GACtB;GACA,WAAW,kBAAkB,IAAI,aAAa,IAAI,QAAQ,YAAY;GACtE,WAAW,UAAU,SAAS,GAAG;GACjC,SAAS;GACT,GAAI,UAAU,EAAE,YAAY,KAAK,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE;GAC3D;;AAEH,QAAO;;;;AAKT,SAAgB,QACd,GACA,UACA,GACA,UACA,OACA,UAA4B,EAAE,EACA;CAC9B,MAAM,UAAU,MAAM,OAAO,iBAAiB;AAC9C,KAAI,QAAQ,WAAW,EAAG,QAAO,MAAM,UAAU,KAAA,EAAU;CAI3D,MAAM,aADH,UAAU,UAAU,MAAM,MAAM,UAAU,UAAU,MAAM,IAEzD,QAAQ,KAAI,MACV,kBAAkB,GAAG,UAAW,GAAG,aAAa,EAAE,EAAE;EAClD,GAAG;EACH,SAAS;EACV,CAAC,CACH,GACD,wBAAwB,GAAG,GAAG,SAAS,QAAQ;CAEnD,MAAM,UAAwC,IAAI,MAAM,MAAM,OAAO;CACrE,IAAI,KAAK;AACT,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,SAAQ,KAAK,iBAAiB,MAAM,GAAG,GAAG,UAAU,QAAQ,KAAA;AAE9D,QAAO;;;;AAKT,SAAgB,kBACd,GACA,SACA,GACA,QACA,UAA4B,EAAE,EAChB;CACd,MAAM,EAAE,YAAY,kBAAkB,YAAY,OAAO,sBACvD;CACF,MAAM,KAAK,QAAQ,WAAW;CAC9B,MAAM,SAAS,QAAQ;CACvB,MAAM,QAAQ,cAAc,GAAG,SAAS,QAAQ,OAAO;CACvD,MAAM,QAAQ,cAAc,GAAG,IAAI,QAAQ,OAAO;CAElD,MAAM,UAAU,OAAO,MAAM,SAAS;CAEtC,MAAM,eADU,OAAO,MAAM,SAAS,GACN,WAAW,UAAW;CAEtD,MAAM,cAAc,QAAQ,eAAe,MAAM,UAAU,CAAC;CAC5D,MAAM,cAAc,QAAQ,eAAe,MAAM,UAAU,CAAC;CAC5D,MAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,WAAW,QAAQ;EACpD,MAAM,OAAO,OAAO;AACpB,UAAS,OAAO,GAAG,QAAQ,OAAQ;GACnC;CACF,MAAM,KAAK,gBAAgB,OAAO,KAAK;AACvC,QAAO;EACL,SAAS;EACT;EACA,WAAW,kBAAkB,IAAI,aAAa,QAAQ,YAAY;EAClE,WAAW,UAAU,MAAM;EAC3B,SAAS,CAAC,MAAM,WAAW,MAAM,UAAU;EAC3C,SAAS;EACV;;;AAIH,SAAgB,mBAAmB,QAAmC;CACpE,MAAM,EAAE,UAAU,IAAI,YAAY;AAClC,QAAO;EAAE;EAAU;EAAI,WAAW,UAAU,QAAQ;EAAE;;;AAIxD,SAAS,kBACP,IACA,UACA,QACa;AAGb,KADE,UAAU,QAAQ,SAAS,KAAK,GAAG,MAAM,CAAC,UAAU,GAAG,MAAM,OAC7C,QAAO;AAEzB,KADqB,GAAG,KAAK,KAAK,GAAG,KAAK,EACxB,QAAO,WAAW,IAAI,WAAW;AACnD,QAAO;;;AAIT,SAAS,UAAU,QAAkB,WAAW,IAAoB;CAClE,IAAI,MAAM,OAAO;CACjB,IAAI,MAAM,OAAO;AACjB,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,MAAI,OAAO,KAAK,IAAK,OAAM,OAAO;AAClC,MAAI,OAAO,KAAK,IAAK,OAAM,OAAO;;AAEpC,KAAI,QAAQ,IAAK,QAAO,CAAC;EAAE,GAAG;EAAK,OAAO,OAAO;EAAQ,CAAC;CAE1D,MAAM,QAAQ,MAAM,OAAO;CAC3B,MAAM,SAAS,IAAI,MAAM,SAAS,CAAC,KAAK,EAAE;AAC1C,MAAK,MAAM,KAAK,QAAQ;EACtB,MAAM,MAAM,KAAK,IAAI,KAAK,OAAO,IAAI,OAAO,KAAK,EAAE,WAAW,EAAE;AAChE,SAAO;;AAET,QAAO,OAAO,KAAK,OAAO,OAAO;EAAE,GAAG,OAAO,IAAI,MAAO;EAAM;EAAO,EAAE;;;;AAKzE,SAAS,aAAa,OAAmB,IAAY,IAAsB;CACzE,MAAM,WAAW,OAAe,GAAW,QAAiC;EAC1E;EACA,WAAW;EACX,WAAW;EACX,UAAU;EACV,UAAU;EACV,eAAe;EAChB;CACD,MAAM,UAAU,MAAM,KAAK,GAAG,MAAM;AAClC,MAAI,MAAM,OAAQ,QAAO,QAAQ,IAAI,GAAG,QAAQ;AAChD,MAAI,MAAM,MAAO,QAAO,QAAQ,IAAI,GAAG,MAAM;AAC7C,MAAI,MAAM,MAAO,QAAO,QAAQ,IAAI,GAAG,MAAM;EAC7C,MAAM,IAAI,EAAE;EACZ,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,EAAE,GAAG,EAAE;EAC7C,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,EAAE,GAAG,EAAE;AAC7C,SAAO;GACL,OAAO;GACP,WAAW;GACX,WAAW;GACX,WAAW,QAAkB,YAAY,KAAK,GAAG;GACjD,WAAW,QAAkB,YAAY,KAAK,GAAG;GACjD,gBAAgB,MAAgB,WAAW,GAAG,EAAE;GACjD;GACD;AACF,SAAQ,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AACzC,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,IAAK,SAAQ,GAAG,YAAY;AAChE,QAAO;;;;;;;AC1NT,SAAgB,oBACd,SACA,SACA,UACe;AACf,QAAO,OAAO,YACZ,QAAQ,QAAQ,KAAI,QAAO;EACzB,MAAM,MAAM,IAAI,OAAO,IAAI;AAC3B,MAAI,IAAI,MAAO,QAAO,CAAC,KAAK,IAAI,MAAM,SAAS,SAAS,CAAC;AACzD,MAAI,IAAI,UAAU;GAChB,MAAM,MAAM,YAAY,QAAQ,SAAS,IAAI,SAAS;AACtD,UAAO,CAAC,KAAK,IAAI,YAAY,IAAI,UAAU,KAAK,SAAS,GAAG,IAAI;;AAElE,SAAO,CAAC,KAAK,KAAA,EAAU;GACvB,CACH;;;AAIH,SAAgB,qBACd,iBACA,UACA,UACe;CACf,MAAM,UAAU,SAAS,SAAQ,MAC/B,OAAO,QAAQ,oBAAoB,GAAG,iBAAiB,SAAS,CAAC,CAClE;AACD,QAAO,OAAO,YAAY,QAAQ;;;AAIpC,SAAgB,aAAa,OAAuC;AAClE,QAAO,MAAM,WAAW,CAAC,GAAG,MAAM,SAAS,MAAM,SAAS,GAAG,MAAM;;;AAIrE,SAAgB,SACd,QACA,OACS;AACT,QAAO,OAAO,MAAK,UACjB,aAAa,MAAM,CAAC,MACjB,EAAE,sBAAsB,gBAAgB,WAAW,KAAA,EACrD,CACF;;;AAIH,SAAgB,iBAAiB,UAAoC;AACnE,QACE,SAAS,SAAQ,MAAK,EAAE,QAAQ,CAAC,MAAK,MAAK,EAAE,WAAW,EAAE,kBAC1D;;;AAKJ,SAAgB,kBACd,UAC0B;AAC1B,KAAI,CAAC,SAAU,QAAO,KAAA;AACtB,QAAO,SAAS,SAAQ,MAAK,EAAE,QAAQ,CAAC,MAAK,MAAK,EAAE,cAAc,EAAE,SAAS;;;AAI/E,SAAgB,cACd,UACA,SACA,UACA,YACA,gBAC0B;AAC1B,KAAI,CAAC,UAAU,SAAS,UAAU,CAAC,QAAQ,SAAS,OAAQ,QAAO,KAAA;CACnE,MAAM,EAAE,aAAa,gBAAgB,cAAc,EAAE;CACrD,MAAM,SAAS,QACb,SAAS,SACT,SAAS,cACT,QAAQ,SACR,QAAQ,cACR,CAAC,SAAS,EACV;EAAE;EAAa;EAAa,CAC7B;AACD,KAAI,CAAC,OAAO,GAAI,QAAO,KAAA;AACvB,QAAO,iBAAiB,cAAc,OAAO,OAAO,GAAG,CAAC,GAAG,OAAO;;;;;AC7FpE,SAAgB,eAA6B;AAC3C,QAAO;EAAE,QAAQ,EAAE;EAAE,uBAAO,IAAI,KAAK;EAAE;;;AAIzC,SAAgB,eACd,KACA,UACgB;AAChB,QAAO;EACL,SAAS;EACT,QAAQ,EAAE,QAAQ,IAAI,QAAQ;EAC9B;EACA,UAAU;EACX;;;;AAKH,SAAgB,YACd,MACA,KACA,MACA,KACA,KACQ;CACR,MAAM,MAAM,GAAG,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;CAEzC,MAAM,WAAW,IAAI,MAAM,IAAI,IAAI;AACnC,KAAI,aAAa,KAAA,EAAW,QAAO;CAEnC,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,QAAyB,EAAE,MAAM,YAAY,MAAM,KAAK,KAAK,EAAE;AACrE,KAAI,IAAK,OAAM,OAAO;AACtB,KAAI,OAAO,EAAG,OAAM,OAAO;AAC3B,KAAI,OAAO,KAAM,OAAM,MAAM;AAC7B,KAAI,OAAO,KAAK,MAAM;AACtB,KAAI,MAAM,IAAI,KAAK,IAAI;AACvB,QAAO;;;AAIT,SAAS,YAAY,MAAc,KAAa,MAAsB;AACpE,KAAI,SAAS,cAAe,QAAO;CACnC,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC,KAAK;AAClC,QAAO,OAAO,cAAc,KAAK,GAAG,KAAK,KAAK;;;;;;AC5EhD,SAAgB,iBACd,QACA,YACoB;CACpB,MAAM,OAAO,oBAAoB,OAAO;AACxC,KAAI,CAAC,MAAM;AACT,UAAQ,IAAI,8BAA8B;AAC1C;;CAGF,MAAM,UAAU,QAAQ,WAAW;AACnC,eAAc,SAAS,KAAK,UAAU,KAAK,CAAC;AAC5C,SAAQ,IAAI,mCAAmC,aAAa;AAC5D,QAAO;;;AAIT,SAAgB,wBACd,MACA,SACgB;CAChB,MAAM,MAAM,cAAc;AAE1B,QAAO,eAAe,KAAK,CADjB,aAAa,MAAM,eAAe,QAAQ,EAAE,IAAI,CAC5B,CAAC;;;AAIjC,SAAgB,oBACd,QAC4B;CAC5B,MAAM,MAAM,cAAc;CAC1B,MAAM,WAAoC,EAAE;AAE5C,MAAK,MAAM,SAAS,OAClB,MAAK,MAAM,UAAU,aAAa,MAAM,EAAE;EACxC,MAAM,EAAE,gBAAgB,OAAO;AAC/B,MAAI,CAAC,YAAa;EAClB,MAAM,WAAW,eAAe,YAAY;AAC5C,WAAS,KAAK,aAAa,OAAO,MAAM,UAAU,IAAI,CAAC;;AAI3D,KAAI,SAAS,WAAW,EAAG,QAAO,KAAA;AAElC,QAAO,eAAe,KAAK,SAAS;;;AAItC,SAAS,aACP,MACA,UACA,KACuB;CAEvB,MAAM,UAAU,MAAa,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI;CAE3E,MAAM,aAAa,IAAI,IACrB,SAAS,MAAM,KAAI,SAAQ,CAAC,KAAK,QAAQ,KAAK,MAAM,IAAI,OAAO,CAAC,CAAU,CAC3E;AAED,KAAI,CAAC,SAAS,eAAe,QAAQ;AACnC,UAAQ,MACN,sDAAsD,KAAK,aAC5D;AACD,SAAO,aAAa,KAAK;;CAG3B,MAAM,UAAsB,EAAE;CAC9B,MAAM,UAAoB,EAAE;AAC5B,MAAK,MAAM,UAAU,SAAS,eAAe;EAC3C,MAAM,QAAQ,WAAW,IAAI,OAAO,OAAO;AAC3C,MAAI,OAAO;AACT,WAAQ,KAAK,MAAM;AACnB,WAAQ,KAAK,OAAO,KAAK;;;AAK7B,QAAO;EACL,MAAM;EACN;EACA,MAAM;EACN,YAAY;EACZ,UANiB,QAAQ,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE;EAOvD;EACA;EACD;;;AAIH,SAAS,aAAa,MAAqC;AACzD,QAAO;EACL,MAAM;EACN;EACA,MAAM;EACN,YAAY;EACZ,UAAU;EACV,SAAS,EAAE;EACX,SAAS,EAAE;EACZ;;;;;AC/EH,SAAgB,eACd,KAC4B;AAE5B,MADgB,IAAI,UAAqB,MAC3B,KAAK,aAAa,OAAO,EAAE,kBAAkB,MAAM;AAC/D,MAAI,eAAe,IAAI;AACvB,SAAO,IAAI;;AAEb,QAAO;;;;;;ACfT,eAAsB,iBACpB,SAC6B;CAC7B,MAAM,EAAE,QAAQ,YAAY,iBAAiB,cAAc,eACzD;CACF,MAAM,eAAe,oBAAoB,OAAO,IAAI,KAAA;CACpD,MAAM,cAAc,kBAAkB,KAAK,MAAM,gBAAgB,GAAG,KAAA;AACpE,KAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,YAAY;AAChD,UAAQ,IAAI,sBAAsB;AAClC;;CAGF,MAAM,YAAY,qBAAqB,cAAc,YAAY;CACjE,MAAM,UAAU,UAAU,SAAS,MAAM,eAAe,UAAU,GAAG,EAAE;CASvE,MAAM,EAAE,SAAS,cAAc,mBAPH;EAC1B;EACA;EACA,UAJe,eAAe,KAAK,MAAM,aAAa,GAAG,KAAA;EAKzD,QAAQ;EACR;EACD,CACuD;CACxD,MAAM,WAAW,cAAc,mBAAmB,cAAc,UAAU;CAC1E,MAAM,UAAU,QAAQ,SAAS;AACjC,eAAc,SAAS,KAAK,UAAU,QAAQ,CAAC;AAC/C,SAAQ,IAAI,uBAAuB,WAAW;AAC9C,QAAO;;AAGT,SAAgB,mBAAmB,OAGjC;CACA,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa,CAAC,QAAQ,SAAS,IAAI;AAahE,QAAO;EAAE,SAZO;GACd,QAAA;GACA,cAAc,MAAM;GACpB,aAAa,MAAM;GACnB,UAAU,MAAM;GAChB,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,UAAU;IACR;IACA,mBAAmB,QAAQ,IAAI,uBAAuB;IACvD;GACF;EACiB;EAAW;;AAG/B,SAAgB,qBACd,cACA,aACqB;CACrB,MAAM,aAAa,cAAc,QAAQ,UAAU,EAAE;CACrD,MAAM,aAAa,aAAa,QAAQ,UAAU,EAAE;AACpD,QAAO,CAAC,GAAG,YAAY,GAAG,WAAW;;;AAIvC,eAAsB,eACpB,QACA,OACiC;CACjC,MAAM,OAAO,IAAI,IAAI,OAAO,KAAI,MAAK,EAAE,KAAK,CAAC,QAAQ,MAAmB,CAAC,CAAC,EAAE,CAAC;CAE7E,MAAM,UAAkC,EAAE;AAC1C,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,SAAS,OAAO,IAAI,IAAI;EAC9B,MAAM,OAAO,UAAW,MAAM,YAAY,IAAI;AAC9C,MAAI,SAAS,KAAA,EAAW;AACxB,UAAQ,OAAO;AACf,MAAI,CAAC,OAAQ,QAAO,IAAI,KAAK,KAAK;;AAGpC,QAAO;;;AAIT,SAAgB,mBACd,SACA,WACQ;AACR,QAAO,UACH,gBAAgB,SAAS,UAAU,GACnC,cAAc,UAAU;;;AAI9B,eAAsB,YAAY,KAA0C;AAC1E,KAAI;AACF,MAAI,IAAI,WAAW,UAAU,CAC3B,QAAO,MAAM,SAAS,cAAc,IAAI,EAAE,QAAQ;EAEpD,MAAM,OAAO,MAAM,MAAM,KAAK,EAAE,QAAQ,YAAY,QAAQ,IAAK,EAAE,CAAC;AACpE,MAAI,CAAC,KAAK,GAAI,QAAO,KAAA;AACrB,SAAO,MAAM,KAAK,MAAM;SAClB;AACN;;;;AAKJ,SAAS,gBAAgB,MAAsB,WAA2B;AAQxE,QAAO,IAPK,KAAK,SAAS,IAAI,QAAQ,WAEnC,QAAQ,gBAAgB,GAAG,CAC3B,QAAQ,qBAAqB,IAAI,CACjC,QAAQ,OAAO,IAAI,CACnB,QAAQ,UAAU,GAAG,IACE,UACX,GAAG,UAAU;;;;;AC/F9B,eAAsB,kBACpB,SAC8D;CAC9D,MAAM,OAAO,QAAQ,QAAQ;CAQ7B,MAAM,QAAQ,MAAM,UAFL,aADC,qBAAqB,SAHjB,IAAI,IAAI,OAAO,QAAQ,QAAQ,WAAW,EAAE,CAAC,CAAC,EAEnD,KAAK,KAAK,aAAa,EAAE,cAAc,EAAE,EAAE,QAAQ,MAAM,CAAC,CACP,CAC9B,EAEE,KAAK;CAC3C,MAAM,MAAM,oBAAoB,MAAM;AACtC,KAAI,QAAQ,SAAS,MAAO,OAAM,KAAK,IAAI;AAC3C,SAAQ,IAAI,WAAW,MAAM;CAE7B,MAAM,cAAc;AAClB,QAAM,OAAO,qBAAqB;AAClC,QAAM,OAAO,OAAO;;AAEtB,QAAO;EAAE,QAAQ,MAAM;EAAQ,MAAM,MAAM;EAAM;EAAO;;;AAI1D,eAAsB,YAAY,UAAiC;CAEjE,MAAM,UAAU,MAAM,SADN,QAAQ,SAAS,EACO,QAAQ;CAChD,MAAM,MAAM,KAAK,MAAM,QAAQ;CAE/B,MAAM,SAAS,IAAI,UAAU;AAC7B,KAAI,SAAA,GAA+B;EACjC,MAAM,MAAM,0BAA0B,OAAO;AAC7C,UAAQ,MAAM,GAAG,IAAI,iDAAiD;AACtE,UAAQ,KAAK,EAAE;;CAGjB,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,UAAU,QAAQ;CACxB,MAAM,EAAE,UAAU,MAAM,kBAAkB;EACxC,aAAa,aAAa,QAAQ,aAAa;EAC/C,iBAAiB,aAAa,QAAQ,YAAY;EAClD,cAAc,aAAa,QAAQ,SAAS;EAC5C,YAAY,aAAa,QAAQ,OAAO;EACxC;EACD,CAAC;AAEF,OAAM,cAAc;AACpB,QAAO;;;AAIT,SAAgB,aAAa,GAAgC;AAC3D,QAAO,IAAI,KAAK,UAAU,EAAE,GAAG,KAAA;;;AAIjC,SAAgB,eAA8B;AAC5C,QAAO,IAAI,SAAQ,YAAW;AAC5B,UAAQ,IAAI,yBAAyB;AACrC,UAAQ,KAAK,gBAAgB;AAC3B,WAAQ,KAAK;AACb,YAAS;IACT;GACF;;;AAIJ,SAAS,cAAsB;CAC7B,MAAM,UAAU,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AACvD,KAAI,SAAS,QAAQ,KAAK,MAAO,QAAO,KAAK,SAAS,QAAQ;AAC9D,QAAO,KAAK,SAAS,KAAK;;;AAI5B,SAAS,qBACP,KACA,aACA,QACqD;CACrD,MAAM,SAAuC;EAC3C,gBAAe,QAAO;GACpB,MAAM,SAAS;IACb,WAAW,IAAI,aAAa;IAC5B,WAAW,CAAC,CAAC,IAAI;IACjB,YAAY,CAAC,CAAC,IAAI;IAClB,gBAAgB,CAAC,CAAC,IAAI;IACtB,aAAa,CAAC,CAAC,IAAI;IACpB;AACD,OAAI,UAAU,gBAAgB,mBAAmB;AACjD,OAAI,IAAI,KAAK,UAAU,OAAO,CAAC;;EAEjC,qBAAoB,QAAO,SAAS,KAAK,IAAI,YAAY,cAAc;EACvE,kBAAiB,QAAO,SAAS,KAAK,IAAI,cAAc,gBAAgB;EACxE,iBAAgB,QAAO,SAAS,KAAK,IAAI,aAAa,gBAAgB,KAAK;EAC3E,uBAAsB,QACpB,SAAS,KAAK,IAAI,aAAa,gBAAgB,KAAK;EACtD,sBAAqB,QACnB,SAAS,KAAK,IAAI,iBAAiB,qBAAqB,KAAK;EAC/D,gBAAgB,KAAK,UAAU,oBAAoB,KAAK,OAAO,YAAY;EAC3E,iBAAiB,KAAK,IAAI,WAAW;AACnC,OAAI,WAAW,QAAQ;AACrB,QAAI,aAAa;AACL,QAAI,IAAI,qBAAqB;AAAzC;;AAEF,UAAO,qBAAqB,KAAK,KAAK,YAAY;;EAErD;AAED,QAAO,OAAO,KAAK,QAAQ;EACzB,MAAM,MAAM,IAAI,OAAO;EACvB,MAAM,OAAO,IAAI,QAAQ,IAAI;EAC7B,MAAM,WAAW,QAAQ,IAAI,IAAI,MAAM,GAAG,KAAK,GAAG;EAClD,MAAM,QAAQ,QAAQ,IAAI,IAAI,MAAM,OAAO,EAAE,GAAG;EAEhD,MAAM,UAAU,OAAO;AACvB,MAAI,SAAS;AACX,SAAM,QAAQ,KAAK,OAAO,IAAI,UAAU,MAAM;AAC9C;;AAGF,SAAO,KAAK,WAAW;AACrB,OAAI,aAAa;AACjB,OAAI,IAAI,YAAY;IACpB;;;;AAKN,SAAS,UACP,QACA,MACA,aAAa,IAC8B;AAC3C,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,UAAU;EACd,MAAM,UAAU,MAAc;AAC5B,UAAO,KAAK,UAAU,QAA+B;AACnD,QAAI,IAAI,SAAS,gBAAgB,UAAU,YAAY;AACrD;AACA,YAAO,IAAI,EAAE;UAEb,QAAO,IAAI;KAEb;AACF,UAAO,OAAO,SAAS;AACrB,WAAO,mBAAmB,QAAQ;IAClC,MAAM,OAAO,OAAO,SAAS;AAE7B,YAAQ;KAAE;KAAQ,MADC,OAAO,SAAS,YAAY,OAAO,KAAK,OAAO;KAC9B,CAAC;KACrC;;AAEJ,SAAO,KAAK;GACZ;;;AAIJ,SAAS,SACP,KACA,MACA,OACA,OAAO,OACD;AACN,KAAI,CAAC,MAAM;AACT,MAAI,aAAa;AACjB,MAAI,IAAI,MAAM,QAAQ;AACtB;;AAEF,KAAI,UAAU,gBAAgB,mBAAmB;AACjD,KAAI,KAAM,KAAI,UAAU,+BAA+B,IAAI;AAC3D,KAAI,IAAI,KAAK;;;AAIf,eAAe,oBACb,KACA,OACA,OACe;CAEf,MAAM,YADS,IAAI,gBAAgB,MAAM,CAChB,IAAI,MAAM;AACnC,KAAI,CAAC,WAAW;AACd,MAAI,aAAa;AACjB,MAAI,IAAI,wBAAwB;AAChC;;AAEF,KAAI;EACF,IAAI,SAAS,MAAM,IAAI,UAAU;AACjC,MAAI,WAAW,KAAA,GAAW;AACxB,YAAS,MAAM,YAAY,UAAU;AACrC,OAAI,WAAW,KAAA,EAAW,OAAM,IAAI,MAAM,YAAY;AACtD,SAAM,IAAI,WAAW,OAAO;;AAE9B,MAAI,UAAU,gBAAgB,4BAA4B;AAC1D,MAAI,IAAI,OAAO;SACT;AACN,MAAI,aAAa;AACjB,MAAI,IAAI,qBAAqB;;;;AAKjC,eAAe,qBACb,KACA,KACA,aACe;AACf,KAAI;EACF,MAAM,SAAS,MAAgB,IAAI,KAAK,MAAM,EAAE,GAAG,KAAA;EACnD,MAAM,UAAU,MAAM,IAAI,YAAY;EACtC,MAAM,cAAc,MAAM,IAAI,gBAAgB;EAC9C,MAAM,WAAW,MAAM,IAAI,aAAa;EACxC,MAAM,SAAS,MAAM,IAAI,WAAW;EACpC,MAAM,YAAY,qBAAqB,SAAS,YAAY;EAI5D,MAAM,EAAE,SAAS,cAAc,mBAAmB;GAChD,cAAc;GACd;GACA;GACA;GACA,SARc,UAAU,SACtB,MAAM,eAAe,WAAW,YAAY,GAC5C,OAAO,YAAY,YAAY;GAOlC,CAAC;EACF,MAAM,OAAO,KAAK,UAAU,QAAQ;EACpC,MAAM,WAAW,mBAAmB,SAAS,UAAU;AACvD,MAAI,UAAU,gBAAgB,mBAAmB;AACjD,MAAI,UAAU,uBAAuB,yBAAyB,SAAS,GAAG;AAC1E,MAAI,IAAI,KAAK;SACP;AACN,MAAI,aAAa;AACjB,MAAI,IAAI,iBAAiB"}
@@ -0,0 +1,2 @@
1
+ import { r as viewArchive } from "./ViewerServer-BJhdnxlN.mjs";
2
+ export { viewArchive };
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { g as runDefaultBench } from "../src-Cf_LXwlp.mjs";
3
-
2
+ import { n as dispatchCli } from "../RunBenchCLI-C17DrJz8.mjs";
4
3
  //#region src/bin/benchforge.ts
5
- await runDefaultBench();
6
-
4
+ await dispatchCli();
7
5
  //#endregion
8
- export { };
6
+ export {};
7
+
9
8
  //# sourceMappingURL=benchforge.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"benchforge.mjs","names":[],"sources":["../../src/bin/benchforge.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runDefaultBench } from \"../index.ts\";\n\nawait runDefaultBench();\n"],"mappings":";;;;AAGA,MAAM,iBAAiB"}
1
+ {"version":3,"file":"benchforge.mjs","names":[],"sources":["../../src/bin/benchforge.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { dispatchCli } from \"../cli/RunBenchCLI.ts\";\n\nawait dispatchCli();\n"],"mappings":";;;AAGA,MAAM,aAAa"}