@ogygia/devtools 0.0.0-stage → 0.11.0

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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -2
  3. package/dist/client.d.ts +3 -0
  4. package/dist/client.js +24 -0
  5. package/dist/devtools/BoundaryOverlay.svelte +396 -0
  6. package/dist/devtools/BoundaryOverlay.svelte.d.ts +19 -0
  7. package/dist/devtools/Devtools.svelte +663 -0
  8. package/dist/devtools/Devtools.svelte.d.ts +15 -0
  9. package/dist/devtools/HubTab.svelte +206 -0
  10. package/dist/devtools/HubTab.svelte.d.ts +11 -0
  11. package/dist/devtools/HydrationTab.svelte +237 -0
  12. package/dist/devtools/HydrationTab.svelte.d.ts +13 -0
  13. package/dist/devtools/IslandDetail.svelte +615 -0
  14. package/dist/devtools/IslandDetail.svelte.d.ts +15 -0
  15. package/dist/devtools/LedgerTab.svelte +287 -0
  16. package/dist/devtools/LedgerTab.svelte.d.ts +11 -0
  17. package/dist/devtools/LensTab.svelte +290 -0
  18. package/dist/devtools/LensTab.svelte.d.ts +19 -0
  19. package/dist/devtools/NavTab.svelte +229 -0
  20. package/dist/devtools/NavTab.svelte.d.ts +11 -0
  21. package/dist/devtools/PageTab.svelte +1122 -0
  22. package/dist/devtools/PageTab.svelte.d.ts +15 -0
  23. package/dist/devtools/ProfilerTab.svelte +755 -0
  24. package/dist/devtools/ProfilerTab.svelte.d.ts +15 -0
  25. package/dist/devtools/PropsTree.svelte +155 -0
  26. package/dist/devtools/PropsTree.svelte.d.ts +15 -0
  27. package/dist/devtools/SessionTab.svelte +796 -0
  28. package/dist/devtools/SessionTab.svelte.d.ts +11 -0
  29. package/dist/devtools/TimelineTab.svelte +267 -0
  30. package/dist/devtools/TimelineTab.svelte.d.ts +11 -0
  31. package/dist/devtools/WireTab.svelte +149 -0
  32. package/dist/devtools/WireTab.svelte.d.ts +11 -0
  33. package/dist/devtools/bytes.d.ts +70 -0
  34. package/dist/devtools/bytes.js +82 -0
  35. package/dist/devtools/cache-headers.d.ts +20 -0
  36. package/dist/devtools/cache-headers.js +81 -0
  37. package/dist/devtools/highlight.d.ts +7 -0
  38. package/dist/devtools/highlight.js +83 -0
  39. package/dist/devtools/hydration.d.ts +48 -0
  40. package/dist/devtools/hydration.js +93 -0
  41. package/dist/devtools/index.d.ts +3 -0
  42. package/dist/devtools/index.js +3 -0
  43. package/dist/devtools/ledger-dom.d.ts +7 -0
  44. package/dist/devtools/ledger-dom.js +151 -0
  45. package/dist/devtools/leftovers.d.ts +77 -0
  46. package/dist/devtools/leftovers.js +319 -0
  47. package/dist/devtools/names.d.ts +1 -0
  48. package/dist/devtools/names.js +34 -0
  49. package/dist/devtools/page.d.ts +96 -0
  50. package/dist/devtools/page.js +957 -0
  51. package/dist/devtools/profile-anchors.d.ts +22 -0
  52. package/dist/devtools/profile-anchors.js +135 -0
  53. package/dist/devtools/profile-store.d.ts +175 -0
  54. package/dist/devtools/profile-store.js +239 -0
  55. package/dist/devtools/regions.d.ts +77 -0
  56. package/dist/devtools/regions.js +132 -0
  57. package/dist/devtools/session.d.ts +31 -0
  58. package/dist/devtools/session.js +455 -0
  59. package/dist/devtools/since-load.d.ts +50 -0
  60. package/dist/devtools/since-load.js +89 -0
  61. package/dist/devtools/source-lines.d.ts +24 -0
  62. package/dist/devtools/source-lines.js +106 -0
  63. package/dist/devtools/styles.d.ts +52 -0
  64. package/dist/devtools/styles.js +248 -0
  65. package/dist/devtools/testing-engine.d.ts +83 -0
  66. package/dist/devtools/testing-engine.js +220 -0
  67. package/dist/devtools/ui-app.d.ts +5 -0
  68. package/dist/devtools/ui-app.js +69 -0
  69. package/dist/devtools/ui.d.ts +50 -0
  70. package/dist/devtools/ui.js +175 -0
  71. package/dist/profiler/alloc.d.ts +75 -0
  72. package/dist/profiler/alloc.js +76 -0
  73. package/dist/profiler/analyze.d.ts +437 -0
  74. package/dist/profiler/analyze.js +1941 -0
  75. package/dist/profiler/app-path.d.ts +7 -0
  76. package/dist/profiler/app-path.js +26 -0
  77. package/dist/profiler/async-io.d.ts +58 -0
  78. package/dist/profiler/async-io.js +187 -0
  79. package/dist/profiler/beacon-standalone.d.ts +4 -0
  80. package/dist/profiler/beacon-standalone.js +409 -0
  81. package/dist/profiler/browser-findings.d.ts +25 -0
  82. package/dist/profiler/browser-findings.js +260 -0
  83. package/dist/profiler/byte-strip.d.ts +58 -0
  84. package/dist/profiler/byte-strip.js +228 -0
  85. package/dist/profiler/client-files.d.ts +16 -0
  86. package/dist/profiler/client-files.js +60 -0
  87. package/dist/profiler/client-windows.d.ts +49 -0
  88. package/dist/profiler/client-windows.js +95 -0
  89. package/dist/profiler/client.d.ts +40 -0
  90. package/dist/profiler/client.js +82 -0
  91. package/dist/profiler/compare.d.ts +308 -0
  92. package/dist/profiler/compare.js +733 -0
  93. package/dist/profiler/compress.d.ts +10 -0
  94. package/dist/profiler/compress.js +36 -0
  95. package/dist/profiler/contention.d.ts +60 -0
  96. package/dist/profiler/contention.js +86 -0
  97. package/dist/profiler/crypto.d.ts +19 -0
  98. package/dist/profiler/crypto.js +101 -0
  99. package/dist/profiler/doc-diff.d.ts +47 -0
  100. package/dist/profiler/doc-diff.js +214 -0
  101. package/dist/profiler/dom-diff.d.ts +43 -0
  102. package/dist/profiler/dom-diff.js +121 -0
  103. package/dist/profiler/drill.d.ts +89 -0
  104. package/dist/profiler/drill.js +553 -0
  105. package/dist/profiler/embedded-maps.d.ts +37 -0
  106. package/dist/profiler/embedded-maps.js +135 -0
  107. package/dist/profiler/env.d.ts +6 -0
  108. package/dist/profiler/env.js +10 -0
  109. package/dist/profiler/forecast.d.ts +76 -0
  110. package/dist/profiler/forecast.js +225 -0
  111. package/dist/profiler/frames.d.ts +11 -0
  112. package/dist/profiler/frames.js +42 -0
  113. package/dist/profiler/gc.d.ts +144 -0
  114. package/dist/profiler/gc.js +271 -0
  115. package/dist/profiler/hole-slots.d.ts +31 -0
  116. package/dist/profiler/hole-slots.js +90 -0
  117. package/dist/profiler/index.d.ts +288 -0
  118. package/dist/profiler/index.js +4921 -0
  119. package/dist/profiler/insights.d.ts +96 -0
  120. package/dist/profiler/insights.js +239 -0
  121. package/dist/profiler/ledger.d.ts +121 -0
  122. package/dist/profiler/ledger.js +440 -0
  123. package/dist/profiler/lineage.d.ts +81 -0
  124. package/dist/profiler/lineage.js +359 -0
  125. package/dist/profiler/module-map.d.ts +12 -0
  126. package/dist/profiler/module-map.js +94 -0
  127. package/dist/profiler/net.d.ts +169 -0
  128. package/dist/profiler/net.js +586 -0
  129. package/dist/profiler/on-demand.d.ts +11 -0
  130. package/dist/profiler/on-demand.js +15 -0
  131. package/dist/profiler/own-requests.d.ts +9 -0
  132. package/dist/profiler/own-requests.js +17 -0
  133. package/dist/profiler/patterns.d.ts +476 -0
  134. package/dist/profiler/patterns.js +2203 -0
  135. package/dist/profiler/profiler-router.d.ts +245 -0
  136. package/dist/profiler/profiler-router.js +113 -0
  137. package/dist/profiler/refresh-stub.d.ts +2 -0
  138. package/dist/profiler/refresh-stub.js +37 -0
  139. package/dist/profiler/report.d.ts +1404 -0
  140. package/dist/profiler/report.js +2638 -0
  141. package/dist/profiler/river.d.ts +65 -0
  142. package/dist/profiler/river.js +298 -0
  143. package/dist/profiler/route-files.d.ts +22 -0
  144. package/dist/profiler/route-files.js +55 -0
  145. package/dist/profiler/route-imports.d.ts +14 -0
  146. package/dist/profiler/route-imports.js +83 -0
  147. package/dist/profiler/saving.d.ts +22 -0
  148. package/dist/profiler/saving.js +42 -0
  149. package/dist/profiler/score.d.ts +109 -0
  150. package/dist/profiler/score.js +261 -0
  151. package/dist/profiler/session-cookie.d.ts +28 -0
  152. package/dist/profiler/session-cookie.js +45 -0
  153. package/dist/profiler/sink.d.ts +81 -0
  154. package/dist/profiler/sink.js +124 -0
  155. package/dist/profiler/site-fixes.d.ts +25 -0
  156. package/dist/profiler/site-fixes.js +62 -0
  157. package/dist/profiler/source-scan.d.ts +210 -0
  158. package/dist/profiler/source-scan.js +1724 -0
  159. package/dist/profiler/span.d.ts +90 -0
  160. package/dist/profiler/span.js +109 -0
  161. package/dist/profiler/stacks.d.ts +43 -0
  162. package/dist/profiler/stacks.js +107 -0
  163. package/dist/profiler/standalone.d.ts +27 -0
  164. package/dist/profiler/standalone.js +98 -0
  165. package/dist/profiler/steps.d.ts +39 -0
  166. package/dist/profiler/steps.js +74 -0
  167. package/dist/profiler/storage/index.d.ts +87 -0
  168. package/dist/profiler/storage/index.js +76 -0
  169. package/dist/profiler/storage/postgres.d.ts +12 -0
  170. package/dist/profiler/storage/postgres.js +94 -0
  171. package/dist/profiler/storage/redis.d.ts +19 -0
  172. package/dist/profiler/storage/redis.js +148 -0
  173. package/dist/profiler/storage/sqlite.d.ts +2 -0
  174. package/dist/profiler/storage/sqlite.js +85 -0
  175. package/dist/profiler/storage/visits.d.ts +4 -0
  176. package/dist/profiler/storage/visits.js +6 -0
  177. package/dist/profiler/stream-tail.d.ts +26 -0
  178. package/dist/profiler/stream-tail.js +74 -0
  179. package/dist/profiler/timeline.d.ts +279 -0
  180. package/dist/profiler/timeline.js +690 -0
  181. package/dist/profiler/ui/AllocStrip.svelte +146 -0
  182. package/dist/profiler/ui/AllocStrip.svelte.d.ts +17 -0
  183. package/dist/profiler/ui/Awaits.svelte +363 -0
  184. package/dist/profiler/ui/Awaits.svelte.d.ts +16 -0
  185. package/dist/profiler/ui/ByteStrip.svelte +182 -0
  186. package/dist/profiler/ui/ByteStrip.svelte.d.ts +18 -0
  187. package/dist/profiler/ui/Compare.svelte +24 -0
  188. package/dist/profiler/ui/Compare.svelte.d.ts +16 -0
  189. package/dist/profiler/ui/CompareBody.svelte +429 -0
  190. package/dist/profiler/ui/CompareBody.svelte.d.ts +8 -0
  191. package/dist/profiler/ui/ComponentsTable.svelte +265 -0
  192. package/dist/profiler/ui/ComponentsTable.svelte.d.ts +20 -0
  193. package/dist/profiler/ui/Dashboard.svelte +492 -0
  194. package/dist/profiler/ui/Dashboard.svelte.d.ts +31 -0
  195. package/dist/profiler/ui/DataRiver.svelte +206 -0
  196. package/dist/profiler/ui/DataRiver.svelte.d.ts +13 -0
  197. package/dist/profiler/ui/DomTravel.svelte +207 -0
  198. package/dist/profiler/ui/DomTravel.svelte.d.ts +9 -0
  199. package/dist/profiler/ui/DownloadHtml.svelte +52 -0
  200. package/dist/profiler/ui/DownloadHtml.svelte.d.ts +10 -0
  201. package/dist/profiler/ui/Drill.svelte +276 -0
  202. package/dist/profiler/ui/Drill.svelte.d.ts +12 -0
  203. package/dist/profiler/ui/ExportButton.svelte +59 -0
  204. package/dist/profiler/ui/ExportButton.svelte.d.ts +8 -0
  205. package/dist/profiler/ui/Flame.svelte +222 -0
  206. package/dist/profiler/ui/Flame.svelte.d.ts +12 -0
  207. package/dist/profiler/ui/FrameDetails.svelte +308 -0
  208. package/dist/profiler/ui/FrameDetails.svelte.d.ts +16 -0
  209. package/dist/profiler/ui/FunctionsTable.svelte +156 -0
  210. package/dist/profiler/ui/FunctionsTable.svelte.d.ts +17 -0
  211. package/dist/profiler/ui/IslandHeatmap.svelte +151 -0
  212. package/dist/profiler/ui/IslandHeatmap.svelte.d.ts +16 -0
  213. package/dist/profiler/ui/IslandsTable.svelte +448 -0
  214. package/dist/profiler/ui/IslandsTable.svelte.d.ts +16 -0
  215. package/dist/profiler/ui/KeepLocal.svelte +46 -0
  216. package/dist/profiler/ui/KeepLocal.svelte.d.ts +8 -0
  217. package/dist/profiler/ui/Ledger.svelte +196 -0
  218. package/dist/profiler/ui/Ledger.svelte.d.ts +18 -0
  219. package/dist/profiler/ui/LocalCompare.svelte +48 -0
  220. package/dist/profiler/ui/LocalCompare.svelte.d.ts +8 -0
  221. package/dist/profiler/ui/LocalReports.svelte +68 -0
  222. package/dist/profiler/ui/LocalReports.svelte.d.ts +7 -0
  223. package/dist/profiler/ui/Login.svelte +13 -0
  224. package/dist/profiler/ui/Login.svelte.d.ts +12 -0
  225. package/dist/profiler/ui/LoginForm.svelte +42 -0
  226. package/dist/profiler/ui/LoginForm.svelte.d.ts +7 -0
  227. package/dist/profiler/ui/Message.svelte +10 -0
  228. package/dist/profiler/ui/Message.svelte.d.ts +12 -0
  229. package/dist/profiler/ui/OnThisPage.svelte +137 -0
  230. package/dist/profiler/ui/OnThisPage.svelte.d.ts +3 -0
  231. package/dist/profiler/ui/OneClock.svelte +297 -0
  232. package/dist/profiler/ui/OneClock.svelte.d.ts +27 -0
  233. package/dist/profiler/ui/Panel.svelte +10 -0
  234. package/dist/profiler/ui/Panel.svelte.d.ts +16 -0
  235. package/dist/profiler/ui/Paths.svelte +269 -0
  236. package/dist/profiler/ui/Paths.svelte.d.ts +15 -0
  237. package/dist/profiler/ui/Patterns.svelte +364 -0
  238. package/dist/profiler/ui/Patterns.svelte.d.ts +15 -0
  239. package/dist/profiler/ui/PermalinkGate.svelte +143 -0
  240. package/dist/profiler/ui/PermalinkGate.svelte.d.ts +10 -0
  241. package/dist/profiler/ui/RenderSteps.svelte +218 -0
  242. package/dist/profiler/ui/RenderSteps.svelte.d.ts +13 -0
  243. package/dist/profiler/ui/Report.svelte +28 -0
  244. package/dist/profiler/ui/Report.svelte.d.ts +20 -0
  245. package/dist/profiler/ui/ReportBody.svelte +2592 -0
  246. package/dist/profiler/ui/ReportBody.svelte.d.ts +21 -0
  247. package/dist/profiler/ui/Run.svelte +17 -0
  248. package/dist/profiler/ui/Run.svelte.d.ts +14 -0
  249. package/dist/profiler/ui/RunView.svelte +146 -0
  250. package/dist/profiler/ui/RunView.svelte.d.ts +10 -0
  251. package/dist/profiler/ui/ScoreCard.svelte +197 -0
  252. package/dist/profiler/ui/ScoreCard.svelte.d.ts +7 -0
  253. package/dist/profiler/ui/Scrub.svelte +293 -0
  254. package/dist/profiler/ui/Scrub.svelte.d.ts +18 -0
  255. package/dist/profiler/ui/SeedExplainer.svelte +193 -0
  256. package/dist/profiler/ui/SeedExplainer.svelte.d.ts +16 -0
  257. package/dist/profiler/ui/ShareLink.svelte +148 -0
  258. package/dist/profiler/ui/ShareLink.svelte.d.ts +8 -0
  259. package/dist/profiler/ui/Shell.svelte +50 -0
  260. package/dist/profiler/ui/Shell.svelte.d.ts +27 -0
  261. package/dist/profiler/ui/Sidebar.svelte +312 -0
  262. package/dist/profiler/ui/Sidebar.svelte.d.ts +7 -0
  263. package/dist/profiler/ui/Site.svelte +20 -0
  264. package/dist/profiler/ui/Site.svelte.d.ts +23 -0
  265. package/dist/profiler/ui/SiteView.svelte +385 -0
  266. package/dist/profiler/ui/SiteView.svelte.d.ts +27 -0
  267. package/dist/profiler/ui/ThemeSwitch.svelte +78 -0
  268. package/dist/profiler/ui/ThemeSwitch.svelte.d.ts +3 -0
  269. package/dist/profiler/ui/Timeline.svelte +672 -0
  270. package/dist/profiler/ui/Timeline.svelte.d.ts +22 -0
  271. package/dist/profiler/ui/Treemap.svelte +278 -0
  272. package/dist/profiler/ui/Treemap.svelte.d.ts +20 -0
  273. package/dist/profiler/ui/Upload.svelte +19 -0
  274. package/dist/profiler/ui/Upload.svelte.d.ts +10 -0
  275. package/dist/profiler/ui/UploadForm.svelte +59 -0
  276. package/dist/profiler/ui/UploadForm.svelte.d.ts +6 -0
  277. package/dist/profiler/ui/WaitingTable.svelte +55 -0
  278. package/dist/profiler/ui/WaitingTable.svelte.d.ts +14 -0
  279. package/dist/profiler/ui/Waterfall.svelte +371 -0
  280. package/dist/profiler/ui/Waterfall.svelte.d.ts +7 -0
  281. package/dist/profiler/ui/WhatIf.svelte +87 -0
  282. package/dist/profiler/ui/WhatIf.svelte.d.ts +18 -0
  283. package/dist/profiler/ui/fake-progress.svelte.d.ts +15 -0
  284. package/dist/profiler/ui/fake-progress.svelte.js +38 -0
  285. package/dist/profiler/ui/format.d.ts +20 -0
  286. package/dist/profiler/ui/format.js +71 -0
  287. package/dist/profiler/ui/permalink.d.ts +9 -0
  288. package/dist/profiler/ui/permalink.js +108 -0
  289. package/dist/profiler/ui/press.d.ts +5 -0
  290. package/dist/profiler/ui/press.js +19 -0
  291. package/dist/profiler/ui/profiler.css +650 -0
  292. package/dist/profiler/ui/report-data.d.ts +189 -0
  293. package/dist/profiler/ui/report-data.js +393 -0
  294. package/dist/profiler/ui/report-request.d.ts +6 -0
  295. package/dist/profiler/ui/report-request.js +50 -0
  296. package/dist/profiler/ui/row-anchor.svelte.d.ts +12 -0
  297. package/dist/profiler/ui/row-anchor.svelte.js +44 -0
  298. package/dist/profiler/ui/sort.svelte.d.ts +14 -0
  299. package/dist/profiler/ui/sort.svelte.js +37 -0
  300. package/dist/profiler/ui/store.d.ts +45 -0
  301. package/dist/profiler/ui/store.js +92 -0
  302. package/dist/profiler/visit.d.ts +389 -0
  303. package/dist/profiler/visit.js +1002 -0
  304. package/dist/report.d.ts +5 -0
  305. package/dist/report.js +4 -0
  306. package/dist/server.d.ts +11 -0
  307. package/dist/server.js +38 -0
  308. package/dist/vite.d.ts +12 -0
  309. package/dist/vite.js +78 -0
  310. package/package.json +103 -3
@@ -0,0 +1,271 @@
1
+ import { categorize, clean_url, component_name_from_file, strip_bundler_suffix, with_script_url } from "./analyze.js";
2
+ // src/profiler/gc.ts
3
+ /**
4
+ * WHO CAUSED THE GC — each pause joined to the allocations that filled the heap before it.
5
+ *
6
+ * V8's sampling heap profiler, asked to include objects already collected, counts every sampled
7
+ * allocation whether it survived or not — the allocation stream that drives the collector. The
8
+ * recorder reads that profile every few tens of milliseconds while the window runs; the
9
+ * difference between two reads is what was allocated in between, by stack. A minor GC (a
10
+ * scavenge) fires when the young space fills, so the allocations since the previous pause caused
11
+ * it; a major GC fires when the old space grows, so the allocations since the previous major
12
+ * pause (their survivors were promoted) caused that one. Each pause gets its top allocators, each
13
+ * allocator gets the pause time it is responsible for, and the report can say "this 40 ms of GC
14
+ * is toProductVM's structuredClone in ProductCard", which no table of allocators can. Pure.
15
+ */
16
+ const GC_MINOR = 1;
17
+ const GC_MAJOR = 4;
18
+ const GC_INCREMENTAL = 8;
19
+ const GC_WEAKCB = 16;
20
+ const GC_FLAGS_FORCED = 4;
21
+ function gc_kind(kind) {
22
+ if (kind & GC_MAJOR) return "major";
23
+ if (kind & GC_MINOR) return "minor";
24
+ if (kind & GC_INCREMENTAL) return "incremental";
25
+ if (kind & GC_WEAKCB) return "weak";
26
+ return "other";
27
+ }
28
+ /** FNV-1a over a string, hex — a short stable key for a stack path */
29
+ function fnv(s, seed = 2166136261) {
30
+ let h = seed;
31
+ for (let i = 0; i < s.length; i++) {
32
+ h ^= s.charCodeAt(i);
33
+ h = Math.imul(h, 16777619) >>> 0;
34
+ }
35
+ return h;
36
+ }
37
+ /** The sampling profile flattened to allocation sites: every node with self bytes, keyed by the
38
+ * hash of its stack, with its cumulative self bytes. `dict` collects what each key is. */
39
+ function heap_sites(head, dict) {
40
+ const out = /* @__PURE__ */ new Map();
41
+ const stack = [];
42
+ const short = (url) => clean_url(url).split("/").slice(-2).join("/");
43
+ const visit = (node, parent_hash, app_above) => {
44
+ const f = with_script_url(node.callFrame);
45
+ const cat = categorize(f);
46
+ let name = f.functionName || "(anonymous)";
47
+ let is_comp = cat.category === "component";
48
+ if (is_comp) name = strip_bundler_suffix(name);
49
+ else if ((!f.functionName || f.functionName === "(anonymous)") && cat.category !== "dependency") {
50
+ const derived = component_name_from_file(clean_url(f.url));
51
+ if (derived) {
52
+ name = derived;
53
+ is_comp = true;
54
+ }
55
+ }
56
+ const h = fnv(`${name}|${f.url}|${f.lineNumber}`, parent_hash);
57
+ const key = h.toString(16).padStart(8, "0");
58
+ if (is_comp) stack.push(name);
59
+ const category = is_comp ? "component" : cat.category;
60
+ if (node.selfSize > 0) {
61
+ out.set(key, (out.get(key) ?? 0) + node.selfSize);
62
+ if (!dict[key]) dict[key] = {
63
+ key,
64
+ name,
65
+ url: f.url,
66
+ line: f.lineNumber + 1,
67
+ category,
68
+ component: stack[stack.length - 1] ?? null,
69
+ ...app_above ? {
70
+ caller: `${app_above.name} (${short(app_above.url)}:${app_above.line})`,
71
+ caller_name: app_above.name,
72
+ caller_url: app_above.url,
73
+ caller_line: app_above.line
74
+ } : {}
75
+ };
76
+ }
77
+ const own = (category === "app" || category === "component" || category === "dependency") && f.url ? {
78
+ name,
79
+ url: f.url,
80
+ line: f.lineNumber + 1
81
+ } : app_above;
82
+ for (const ch of node.children ?? []) visit(ch, h, own);
83
+ if (is_comp) stack.pop();
84
+ };
85
+ visit(head, 2166136261, void 0);
86
+ return out;
87
+ }
88
+ /** What was allocated between two reads: per site, the growth of its cumulative bytes. */
89
+ function slice_delta(prev, cur) {
90
+ const out = {};
91
+ for (const [k, v] of cur) {
92
+ const d = v - (prev.get(k) ?? 0);
93
+ if (d > 0) out[k] = d;
94
+ }
95
+ return out;
96
+ }
97
+ const r1 = (n) => Math.round(n * 10) / 10;
98
+ const r2 = (n) => Math.round(n * 100) / 100;
99
+ const mb = (b) => r1(b / 1048576);
100
+ function attribute_gc(input) {
101
+ const { slices, dict } = input;
102
+ const events = [...input.events].sort((a, b) => a.t - b.t);
103
+ const single = slices.length === 1;
104
+ const running_at = (t) => {
105
+ const segs = input.running ?? [];
106
+ let best;
107
+ for (const s of segs) {
108
+ if (s.category === "gc" || s.category === "idle" || s.category === "profiler") continue;
109
+ if (s.t0 <= t && t <= s.t1) return s;
110
+ if (s.t1 <= t && (!best || s.t1 > best.t1)) best = s;
111
+ }
112
+ return best && t - best.t1 <= 50 ? best : void 0;
113
+ };
114
+ const ours = (k) => {
115
+ const c = dict[k]?.category;
116
+ return c !== "v8" && c !== "gc" && c !== "profiler";
117
+ };
118
+ const profilers = (k) => dict[k]?.category === "profiler";
119
+ const ident_of = (k) => {
120
+ const d = dict[k];
121
+ return d ? `${d.name}|${d.url}|${d.line}|${d.caller ?? ""}|${d.component ?? ""}` : k;
122
+ };
123
+ const first_key = {};
124
+ const between = (from, to) => {
125
+ const acc = {};
126
+ let profiler_bytes = 0;
127
+ for (const s of slices) {
128
+ const len = s.t1 - s.t0;
129
+ if (len <= 0 || s.t1 <= from || s.t0 > to) continue;
130
+ const w = Math.max(0, Math.min(1, (Math.min(s.t1, to) - Math.max(s.t0, from)) / len));
131
+ if (!w) continue;
132
+ for (const [k, b] of Object.entries(s.bytes)) {
133
+ if (profilers(k)) {
134
+ profiler_bytes += b * w;
135
+ continue;
136
+ }
137
+ if (!ours(k)) continue;
138
+ const id = ident_of(k);
139
+ first_key[id] ??= k;
140
+ acc[id] = (acc[id] ?? 0) + b * w;
141
+ }
142
+ }
143
+ return {
144
+ app: acc,
145
+ profiler_bytes
146
+ };
147
+ };
148
+ const total_by_key = {};
149
+ for (const s of slices) for (const [k, b] of Object.entries(s.bytes)) {
150
+ if (!ours(k)) continue;
151
+ const id = ident_of(k);
152
+ first_key[id] ??= k;
153
+ total_by_key[id] = (total_by_key[id] ?? 0) + b;
154
+ }
155
+ const total_bytes = Object.values(total_by_key).reduce((a, b) => a + b, 0);
156
+ const gc_ms_by_key = {};
157
+ const top3_by_key = {};
158
+ const pauses = [];
159
+ let last_any = 0;
160
+ let last_major = 0;
161
+ let measured_total = 0;
162
+ let overhead_total = 0;
163
+ for (const e of events) {
164
+ const from = e.kind === "major" ? last_major : last_any;
165
+ const { app: cause, profiler_bytes } = between(from, e.t);
166
+ const allocated = Object.values(cause).reduce((a, b) => a + b, 0);
167
+ const overhead_share = allocated + profiler_bytes > 0 ? profiler_bytes / (allocated + profiler_bytes) : 0;
168
+ const ms_app = e.ms * (1 - overhead_share);
169
+ measured_total += e.ms;
170
+ overhead_total += e.ms - ms_app;
171
+ const forced = (e.flags & GC_FLAGS_FORCED) !== 0;
172
+ const top = single ? [] : Object.entries(cause).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([id, bytes]) => {
173
+ const d = dict[first_key[id]];
174
+ return {
175
+ key: first_key[id],
176
+ name: d?.name ?? id,
177
+ component: d?.component ?? null,
178
+ bytes: Math.round(bytes),
179
+ share: allocated ? r2(bytes / allocated) : 0
180
+ };
181
+ });
182
+ for (const [key, bytes] of Object.entries(cause)) gc_ms_by_key[key] = (gc_ms_by_key[key] ?? 0) + (allocated ? bytes / allocated * ms_app : 0);
183
+ for (const t of top.slice(0, 3)) top3_by_key[ident_of(t.key)] = (top3_by_key[ident_of(t.key)] ?? 0) + 1;
184
+ const since_ms = r1(e.t - from);
185
+ const run = running_at(e.t);
186
+ const about = single ? "≈" : "";
187
+ let why;
188
+ if (forced) why = "forced (something asked for a collection: the inspector, a gc() call, a low-memory signal)";
189
+ else if (!allocated) why = e.kind === "major" ? "the old space was full; nothing sampled was allocated since the last major pause (the pressure came from before the window)" : "nothing sampled was allocated since the last pause (an allocation the sampler missed, or pressure from before the window)";
190
+ else if (e.kind === "minor") why = `the young space filled: ${about}${mb(allocated)} MB allocated in the ${since_ms} ms since the last pause`;
191
+ else if (e.kind === "major") why = `the old space grew: ${about}${mb(allocated)} MB allocated since the last major pause, and what survived the minor ones was promoted into it`;
192
+ else if (e.kind === "incremental") why = `an incremental marking step of a major collection (${about}${mb(allocated)} MB allocated since the last pause)`;
193
+ else why = `a weak-callback pass (${about}${mb(allocated)} MB allocated since the last pause)`;
194
+ pauses.push({
195
+ t: r1(e.t),
196
+ ms: r2(ms_app),
197
+ ms_measured: r2(e.ms),
198
+ kind: e.kind,
199
+ forced,
200
+ since_ms,
201
+ allocated: Math.round(allocated),
202
+ ...single ? { estimated: true } : {},
203
+ why,
204
+ top,
205
+ ...run ? { running: {
206
+ label: run.label,
207
+ category: run.category,
208
+ ...run.file ? { file: run.file } : {}
209
+ } } : {}
210
+ });
211
+ last_any = e.t;
212
+ if (e.kind === "major") last_major = e.t;
213
+ }
214
+ const makers = Object.entries(total_by_key).map(([id, allocated]) => {
215
+ const key = first_key[id];
216
+ const d = dict[key];
217
+ return {
218
+ key,
219
+ name: d?.name ?? key,
220
+ url: d?.url ?? "",
221
+ line: d?.line ?? 0,
222
+ category: d?.category ?? "app",
223
+ component: d?.component ?? null,
224
+ ...d?.caller ? { caller: d.caller } : {},
225
+ ...d?.at ? { at: d.at } : {},
226
+ allocated,
227
+ share: total_bytes ? r2(allocated / total_bytes) : 0,
228
+ gc_ms: r2(gc_ms_by_key[id] ?? 0),
229
+ pauses: top3_by_key[id] ?? 0
230
+ };
231
+ }).filter((m) => m.category !== "gc" && m.category !== "v8" && m.category !== "profiler").sort((a, b) => b.gc_ms - a.gc_ms || b.allocated - a.allocated).slice(0, 60);
232
+ const comp = /* @__PURE__ */ new Map();
233
+ for (const [id, allocated] of Object.entries(total_by_key)) {
234
+ const name = dict[first_key[id]]?.component;
235
+ if (!name) continue;
236
+ const c = comp.get(name) ?? {
237
+ allocated: 0,
238
+ gc_ms: 0
239
+ };
240
+ c.allocated += allocated;
241
+ c.gc_ms += gc_ms_by_key[id] ?? 0;
242
+ comp.set(name, c);
243
+ }
244
+ const slice_ms = slices.length ? r1(slices.reduce((a, s) => a + (s.t1 - s.t0), 0) / slices.length) : 0;
245
+ return {
246
+ summary: {
247
+ count: events.length,
248
+ total_ms: r2(measured_total - overhead_total),
249
+ max_ms: r2(Math.max(0, ...pauses.map((p) => p.ms))),
250
+ measured_ms: r2(measured_total),
251
+ overhead_ms: r2(overhead_total),
252
+ minor: events.filter((e) => e.kind === "minor").length,
253
+ major: events.filter((e) => e.kind === "major").length,
254
+ incremental: events.filter((e) => e.kind === "incremental").length,
255
+ weak: events.filter((e) => e.kind === "weak").length,
256
+ allocated_mb: mb(total_bytes),
257
+ alloc_rate_mb_s: input.window_ms > 0 ? r1(total_bytes / 1048576 / (input.window_ms / 1e3)) : 0,
258
+ ...input.retained_mb !== void 0 ? { retained_mb: input.retained_mb } : {},
259
+ slices: slices.length,
260
+ slice_ms
261
+ },
262
+ pauses,
263
+ makers,
264
+ components: [...comp.entries()].map(([name, c]) => ({
265
+ name,
266
+ allocated: c.allocated,
267
+ gc_ms: r2(c.gc_ms)
268
+ })).sort((a, b) => b.gc_ms - a.gc_ms || b.allocated - a.allocated)
269
+ };
270
+ }
271
+ export { attribute_gc, gc_kind, heap_sites, slice_delta };
@@ -0,0 +1,31 @@
1
+ import { RequestEntry } from "./report.js";
2
+ export interface HoleSlotRow {
3
+ id: string;
4
+ /** the component and a props preview, from a page that minted it (else the id) */
5
+ name: string;
6
+ requests: number;
7
+ /** served from the render cache (no render, no slot) */
8
+ hits: number;
9
+ /** answered 500 (the render threw or timed out): no render time */
10
+ failed: number;
11
+ /** mean render per rendered request (its time in the slot), ms */
12
+ render_ms: number;
13
+ /** mean and worst wait for a slot per rendered request, ms */
14
+ queue_ms: number;
15
+ queue_max_ms: number;
16
+ /** its part of all the slot time spent (0–1): who held the slots */
17
+ slot_share: number;
18
+ }
19
+ export interface HoleSlots {
20
+ rows: HoleSlotRow[];
21
+ /** the slots per process */
22
+ slots: number;
23
+ /** rendered hole requests, and how many waited 50 ms or more for a slot */
24
+ rendered: number;
25
+ queued: number;
26
+ /** the mean wait of those that waited, ms */
27
+ queued_mean_ms: number;
28
+ /** one line for the dashboard, when holes waited; null when none did */
29
+ summary: string | null;
30
+ }
31
+ export declare function hole_slots(ring: readonly RequestEntry[]): HoleSlots | null;
@@ -0,0 +1,90 @@
1
+ import { REGION_RENDER_CONCURRENCY } from "@ogygia/core/internal/instruments";
2
+ // src/profiler/hole-slots.ts
3
+ /**
4
+ * HOLE RENDER SLOTS, across the server. A server process renders a few holes at a time
5
+ * (`REGION_RENDER_CONCURRENCY`), and a render holds its slot through its awaits: a hole waiting
6
+ * on slow data keeps the next hole — any visitor's — queued. The endpoint records each hole
7
+ * request's wait for a slot (`HoleRequestStats.queue_ms`); this reads the request log for which
8
+ * holes waited, and which held the slots while they did.
9
+ *
10
+ * On a host that runs one request per instance (AWS Lambda) nothing ever queues: the table still
11
+ * shows each hole's render and cache, and the summary stays silent.
12
+ */
13
+ const QUEUED_MS = 50;
14
+ const fmt = (ms) => ms >= 1e3 ? `${(ms / 1e3).toFixed(1)} s` : `${Math.round(ms)} ms`;
15
+ function hole_slots(ring) {
16
+ const names = /* @__PURE__ */ new Map();
17
+ const props = /* @__PURE__ */ new Map();
18
+ for (const e of ring) for (const h of e.og?.hole_rows ?? []) {
19
+ if (h.name && !names.has(h.id)) names.set(h.id, h.name);
20
+ const set = props.get(h.id) ?? props.set(h.id, /* @__PURE__ */ new Set()).get(h.id);
21
+ if (h.props) set.add(h.count > 1 ? `${h.props}×${h.count}` : h.props);
22
+ }
23
+ const by = /* @__PURE__ */ new Map();
24
+ let queued = 0;
25
+ let queued_sum = 0;
26
+ for (const e of ring) {
27
+ const h = e.hole;
28
+ if (!h) continue;
29
+ const s = by.get(h.id) ?? {
30
+ requests: 0,
31
+ hits: 0,
32
+ failed: 0,
33
+ rendered: 0,
34
+ render: 0,
35
+ queue: 0,
36
+ queue_max: 0
37
+ };
38
+ s.requests++;
39
+ s.name ??= h.name;
40
+ if (e.status >= 500) s.failed++;
41
+ else if (h.cache === "hit") s.hits++;
42
+ else {
43
+ const q = h.queue_ms ?? 0;
44
+ s.rendered++;
45
+ s.queue += q;
46
+ s.queue_max = Math.max(s.queue_max, q);
47
+ s.render += Math.max(0, e.ms - q);
48
+ if (q >= QUEUED_MS) {
49
+ queued++;
50
+ queued_sum += q;
51
+ }
52
+ }
53
+ by.set(h.id, s);
54
+ }
55
+ if (!by.size) return null;
56
+ const slot_total = [...by.values()].reduce((a, s) => a + s.render, 0) || 1;
57
+ const rows = [...by.entries()].map(([id, s]) => {
58
+ const name = s.name ?? names.get(id) ?? id;
59
+ const copies = props.get(id);
60
+ const one = copies?.size === 1 ? [...copies][0] : void 0;
61
+ return {
62
+ id,
63
+ name: one && !one.includes("×") ? `${name} ${one}` : name,
64
+ requests: s.requests,
65
+ hits: s.hits,
66
+ failed: s.failed,
67
+ render_ms: s.rendered ? s.render / s.rendered : 0,
68
+ queue_ms: s.rendered ? s.queue / s.rendered : 0,
69
+ queue_max_ms: s.queue_max,
70
+ slot_share: s.render / slot_total
71
+ };
72
+ }).sort((a, b) => b.slot_share - a.slot_share);
73
+ const rendered = rows.reduce((a, r) => a + r.requests - r.hits - r.failed, 0);
74
+ const queued_mean_ms = queued ? queued_sum / queued : 0;
75
+ let summary = null;
76
+ if (queued) {
77
+ const holders = rows.filter((r) => r.slot_share >= .2).slice(0, 3);
78
+ const held = holders.length ? ` The slots were held mostly by ${holders.map((r) => `${r.name} (${Math.round(r.slot_share * 100)}% of slot time, ${fmt(r.render_ms)} a render)`).join(", ")}.` : "";
79
+ summary = `${queued} of ${rendered} hole render${rendered === 1 ? "" : "s"} waited for a render slot, ${fmt(queued_mean_ms)} on average (the worst ${fmt(Math.max(...rows.map((r) => r.queue_max_ms)))}).` + held + ` A server process renders ${REGION_RENDER_CONCURRENCY} holes at a time, and each holds its slot while it awaits its data: make the slow ones answer sooner (a maxAge, faster data).`;
80
+ }
81
+ return {
82
+ rows,
83
+ slots: REGION_RENDER_CONCURRENCY,
84
+ rendered,
85
+ queued,
86
+ queued_mean_ms,
87
+ summary
88
+ };
89
+ }
90
+ export { hole_slots };
@@ -0,0 +1,288 @@
1
+ import { ProfilerStore } from "./storage/index.js";
2
+ import { Analysis, CpuProfile, FrameCategory, HeapAllocator } from "./analyze.js";
3
+ import { NetCall } from "./net.js";
4
+ import { IoOp } from "./async-io.js";
5
+ import { ClientIslandStat, ClientMarkStat, IslandHazard as IslandHazard$1, MemSample, PageVitals, ReportMeta, RequestEntry } from "./report.js";
6
+ import { Visit } from "./visit.js";
7
+ import { ClientWindows, InteractionCpu } from "./client-windows.js";
8
+ import { ByteStrip } from "./byte-strip.js";
9
+ import { River } from "./river.js";
10
+ import { GcAttribution } from "./gc.js";
11
+ import { Retained } from "./insights.js";
12
+ import { AllocTimeline } from "./alloc.js";
13
+ import { Contention } from "./contention.js";
14
+ import { Lineage } from "./lineage.js";
15
+ import { LedgerLine } from "./ledger.js";
16
+ import { DrillNode } from "./drill.js";
17
+ import { Forecast } from "./forecast.js";
18
+ import { HeapGrowth, Pattern } from "./patterns.js";
19
+ import { SpanRecord } from "./span.js";
20
+ import { PageAssets } from "@ogygia/core/insights";
21
+ import { Handle } from "@sveltejs/kit";
22
+ export interface ProfilerOptions {
23
+ /**
24
+ * Auth secret. Defaults to the OGYGIA_PROFILER_SECRET env var. Required outside
25
+ * dev — with no secret in production the profiler UI is disabled (the
26
+ * always-on request log still collects, invisibly).
27
+ */
28
+ secret?: string;
29
+ /** Base path for the UI. Default '/__profiler'. */
30
+ path?: string;
31
+ /** Sampling interval in microseconds for recordings. Default 500. */
32
+ sampleInterval?: number;
33
+ /** How many finished profiles to keep in memory (each gzipped). Default 6. */
34
+ maxReports?: number;
35
+ /** How many requests the rolling log keeps. Default 500. */
36
+ recentRequests?: number;
37
+ /**
38
+ * Patch fetch/http to attribute outbound calls. Default true. In production the
39
+ * per-request AsyncLocalStorage that does the attributing exists only while a profile
40
+ * is being recorded (idle requests pay nothing); in dev it is always on. Set false to
41
+ * skip the global fetch/http patch entirely: wall timing only, never any network view.
42
+ */
43
+ network?: boolean;
44
+ /**
45
+ * Add Server-Timing headers to every response. These expose internal render
46
+ * and network timings to every client, so the default is ON in dev and OFF
47
+ * in production — set true to force it on.
48
+ */
49
+ serverTiming?: boolean;
50
+ /** Take a heap allocation profile during recordings. Default true. */
51
+ heap?: boolean;
52
+ /** Master switch. Default true. */
53
+ enabled?: boolean;
54
+ /**
55
+ * DEMAND-ONLY. The profiler module (node:inspector, crypto, its UI-rendering path) is not imported,
56
+ * parsed or initialised until a request actually hits the profiler path — so until you open the
57
+ * dashboard the app pays EXACTLY what an app with no profiler pays: no cold-start import, no
58
+ * always-on request log, no per-request wrap. The trade is no history: the profiler starts
59
+ * collecting from the first visit onward, not before. Recommended on serverless (Amplify / Lambda),
60
+ * where every cold start would otherwise pay the profiler's import on its TTFB. Default false (the
61
+ * profiler mounts on the first request and its always-on log collects from boot). Off by default so
62
+ * nothing changes for existing setups.
63
+ */
64
+ onDemand?: boolean;
65
+ /**
66
+ * CATCH THE SLOW ONE. Keep a coarse sampler running in rolling windows and keep a report only
67
+ * when a request in the window took longer than `over` ms — the p99 request you cannot
68
+ * reproduce. Built to stay cheap: a 10 ms sampling interval, no per-request context, no stack
69
+ * capture, no heap sampling; the request is matched to the window by time. Off by default; off
70
+ * on serverless hosts (an instance dies before it catches anything). Stops after `keep` reports.
71
+ */
72
+ trap?: {
73
+ over: number;
74
+ interval?: number;
75
+ window?: number;
76
+ keep?: number;
77
+ /** request headers to KEEP with a caught request so "profile it again" can replay it with the
78
+ * same inputs (`['accept-language', 'x-tenant']`). Never cookies unless named here. */
79
+ replay?: string[];
80
+ };
81
+ /**
82
+ * A CPU PROFILE IN THE BROWSER for the profiler's own user: their documents get the
83
+ * `Document-Policy: js-profiling` header, the runtime samples the main thread while the page
84
+ * hydrates (Chromium's JS Self-Profiling API) and posts the trace through the beacon; the report
85
+ * shows it as components, functions and a flame graph for hydration. Default true; a visitor is
86
+ * never sampled (no tag, no policy, no profiler).
87
+ */
88
+ clientCpu?: boolean;
89
+ /**
90
+ * THE SINK — for a host whose instances live seconds (Amplify, Lambda). Each instance posts what
91
+ * it saw as NDJSON to this URL before it dies: one small row per request, one summary per
92
+ * always-on sampler window, a caught request's dump. `key` goes as a bearer token (default: the
93
+ * profiler secret). A long-lived host posts every `every` ms (default 10 s); a serverless one
94
+ * posts after each request. The site view (`<base>/site`) reads the rows back from any URL you
95
+ * serve them from: the request cloud and the layer cake of the whole site, over days.
96
+ */
97
+ sink?: {
98
+ url: string;
99
+ key?: string;
100
+ every?: number;
101
+ };
102
+ /**
103
+ * ALWAYS-ON SAMPLING. Every `every` seconds take one `window` ms coarse sample (10 ms interval)
104
+ * and fold it into a rolling "hot functions over the last hour" table on the dashboard. The
105
+ * accuracy comes from volume, not from one capture. Default off; off on serverless.
106
+ */
107
+ sample?: {
108
+ every?: number;
109
+ window?: number;
110
+ interval?: number;
111
+ };
112
+ /**
113
+ * WHERE REPORTS LIVE. Default: this instance's memory (lost on restart; each browser keeps its
114
+ * own copies). Not a `devtools({ profiler })` option: a store is a live object the build-time
115
+ * options cannot carry, so the app sets it at runtime with `setProfilerStore(store)` from
116
+ * `@ogygia/devtools/profiler/storage`, in hooks.server.ts, and the server half passes it in here
117
+ * when the profiler mounts. A store is `sqliteStore(...)`, `redisStore(...)` or `postgresStore(...)`
118
+ * from `@ogygia/devtools/profiler/storage/{sqlite,redis,postgres}`, or your own; every finished
119
+ * report is written there and read back from there, so it survives the instance that made it and
120
+ * a whole team shares one list. The store is dual-written: memory stays the hot cache, the store
121
+ * the source of truth.
122
+ */
123
+ store?: ProfilerStore;
124
+ }
125
+ /** The dashboard's always-on view. */
126
+ export interface SampledSummary {
127
+ every_s: number;
128
+ window_ms: number;
129
+ windows: number;
130
+ since: number | null;
131
+ sampled_ms: number;
132
+ busy_ms: number;
133
+ functions: {
134
+ key: string;
135
+ name: string;
136
+ url: string;
137
+ line: number;
138
+ category: FrameCategory;
139
+ pkg?: string;
140
+ self_ms: number;
141
+ windows: number;
142
+ }[];
143
+ }
144
+ /** The dashboard's trap view. */
145
+ export interface TrapStatus {
146
+ over: number;
147
+ window_ms: number;
148
+ caught: number;
149
+ keep: number;
150
+ armed: boolean;
151
+ }
152
+ interface UserTiming {
153
+ name: string;
154
+ count: number;
155
+ total_ms: number;
156
+ max_ms: number;
157
+ }
158
+ interface GcSummary {
159
+ count: number;
160
+ total_ms: number;
161
+ max_ms: number;
162
+ }
163
+ export interface StoredReport {
164
+ meta: ReportMeta;
165
+ analysis: Analysis;
166
+ heap: HeapAllocator[] | null;
167
+ /** bytes per component from the same heap sample */
168
+ heap_components?: {
169
+ name: string;
170
+ bytes: number;
171
+ }[] | null;
172
+ net: NetCall[];
173
+ spans?: SpanRecord[];
174
+ mem: MemSample[];
175
+ measures: UserTiming[];
176
+ gc: GcSummary | null;
177
+ io: IoOp[];
178
+ call_counts: Record<string, number>;
179
+ /** the raw .cpuprofile, gzipped — a 10s profile is multiple MB of JSON, so
180
+ * we keep it compressed (~10×) and inflate only on download */
181
+ raw: Buffer | string;
182
+ /** bytes per island module / preload href (page mode on a built app) */
183
+ weights?: Record<string, number>;
184
+ /** what is inside each of those chunks (the build's handoff), for the same hrefs */
185
+ contents?: Record<string, string[]>;
186
+ heavy?: Record<string, {
187
+ total: number;
188
+ top: {
189
+ name: string;
190
+ bytes: number;
191
+ }[];
192
+ }>;
193
+ barrels?: Record<string, {
194
+ name: string;
195
+ fanout: number;
196
+ }[]>;
197
+ /** each island entry's lines that draw differently in the browser (its own `.svelte` sources) */
198
+ hazards?: Record<string, IslandHazard$1[]>;
199
+ /** browser hydration timings carried by an uploaded dump (a live report joins the ring instead) */
200
+ client?: ClientIslandStat[];
201
+ /** the page's web vitals carried by an uploaded dump */
202
+ vitals?: PageVitals;
203
+ /** the app's browser marks carried by an uploaded dump */
204
+ client_marks?: ClientMarkStat[];
205
+ /** a caught request's inputs, so it can be profiled again in full */
206
+ replay?: {
207
+ path: string;
208
+ headers: Record<string, string>;
209
+ };
210
+ /** the browser's CPU profile of hydration carried by an uploaded dump */
211
+ client_cpu?: {
212
+ analysis: Analysis;
213
+ at: number;
214
+ sample_ms: number;
215
+ windows?: ClientWindows;
216
+ };
217
+ /** the slowest interaction's CPU (its wait, its handlers) carried by an uploaded dump */
218
+ interaction_cpu?: InteractionCpu;
219
+ /** the browser's picture of a visit to this page (the beacon), the latest at report time */
220
+ visit?: Visit;
221
+ /** the rendered document as a byte strip (page mode: the last run's body) */
222
+ strip?: ByteStrip;
223
+ /** every file the document loads at start, weighed (page mode on a build) */
224
+ assets?: PageAssets;
225
+ /** why the page was not weighed (a dev server) */
226
+ assets_missing?: string;
227
+ /** the data river (page mode): calls → loads → keys → islands */
228
+ river?: River;
229
+ /** who caused the GC: each pause joined to the allocations before it (gc.ts) */
230
+ gc_attr?: GcAttribution;
231
+ /** promises created in the window (count, sampled creators) */
232
+ promises?: {
233
+ count: number;
234
+ top: {
235
+ caller: string;
236
+ share: number;
237
+ }[];
238
+ };
239
+ /** what one more render left alive after a full collection, by allocation site */
240
+ retained?: Retained;
241
+ /** when the heap grew and what ran then (alloc.ts) */
242
+ alloc?: AllocTimeline;
243
+ /** the other requests the instance answered while the render ran (contention.ts) */
244
+ contention?: Contention;
245
+ /** which component reads which page.data key, from the sources (lineage.ts) */
246
+ lineage?: Lineage;
247
+ /** the app lines that cost the most, every cost joined per line (ledger.ts) */
248
+ ledger?: LedgerLine[];
249
+ /** the known slow shapes found on those lines, grouped (patterns.ts) */
250
+ patterns?: Pattern[];
251
+ /** one render's time as a tree that adds up: phase → owner / call → line (drill.ts) */
252
+ drill?: DrillNode;
253
+ /** one render after every fix named, estimated without counting a saving twice (forecast.ts) */
254
+ forecast?: Forecast;
255
+ /** a few more renders with a full collection after each: does the heap keep growing? */
256
+ growth?: HeapGrowth;
257
+ }
258
+ /**
259
+ * Chromium's JS Self-Profiling trace (`Profiler.stop()`) as a V8 cpuprofile: a stack is a chain of
260
+ * frames, so every distinct stack becomes one node whose parent is its parent stack; the samples
261
+ * are timestamps (ms) → time deltas (µs); a sample with no stack is idle. Bounded (samples,
262
+ * stacks); `null` for anything not shaped like a trace. Exported for the tests.
263
+ */
264
+ export declare function self_profile_to_cpuprofile(trace: unknown): CpuProfile | null;
265
+ /**
266
+ * Build a profiler handle. INTERNAL — the profiler is configured in vite.config.ts
267
+ * (`ogygia({ profiler: true })`) and `ogygia.handle()` constructs + mounts this itself.
268
+ * Kept as a named export only so the handle's lazy import can reach it; it is not a
269
+ * public entry point.
270
+ *
271
+ * See the class doc and README for what it captures. Visit `<path>` (default
272
+ * /__profiler) — in prod, log in (or send the x-profiler-key header).
273
+ */
274
+ export declare function profiler(options?: ProfilerOptions): Handle;
275
+ /** `report` with only the top-level keys in `fields` (comma-separated) and its identity; whole when unset. */
276
+ export declare function pick_fields(report: Record<string, unknown>, fields: string | null): Record<string, unknown>;
277
+ /**
278
+ * The module (src-relative, no extension: `lib/inferno/catalog`) a file imports `name` from — its
279
+ * `import { name } from '$lib/…'` or `'./…'` — or the file itself when it declares it. Undefined for
280
+ * a package import, or a name it cannot place. Plain text, no regex.
281
+ */
282
+ /**
283
+ * Is the route in `dir` prerendered? Kit takes the NEAREST `export const prerender`: the page's own
284
+ * files first (`+page.server`, `+page`), then each layout up the tree. `true` and `'auto'` prerender;
285
+ * `false` (or none found) does not. String search over the files (no regex, no parse).
286
+ */
287
+ export declare function route_prerendered(read: (file: string) => string | undefined, dir: string): boolean;
288
+ export type { Analysis, CpuProfile, HeapAllocator, NetCall, ReportMeta, RequestEntry };