@ogygia/devtools 0.0.0 → 0.11.1

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 +24 -1
  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 +99 -6
@@ -0,0 +1,146 @@
1
+ <script>import { fmt_ms, CATEGORY_COLOR } from "./format.js";
2
+ let { alloc, gc = [] } = $props();
3
+ const W = 960;
4
+ const H = 90;
5
+ const s = $derived(alloc.samples);
6
+ const t_max = $derived(Math.max(s[s.length - 1]?.t ?? 1, 1));
7
+ const mbs = $derived(s.map((x) => x.mb));
8
+ const lo = $derived(Math.min(...mbs));
9
+ const hi = $derived(Math.max(...mbs));
10
+ const range = $derived(Math.max(hi - lo, 1));
11
+ const x_of = (t) => t / t_max * W;
12
+ const y_of = (mb) => H - 6 - (mb - lo) / range * (H - 14);
13
+ const path = $derived(s.map((p, i) => `${i ? "L" : "M"}${x_of(p.t).toFixed(1)},${y_of(p.mb).toFixed(1)}`).join(" "));
14
+ const win = $derived(alloc.window ?? null);
15
+ const color = (c) => CATEGORY_COLOR[c] ?? "#6b7280";
16
+ const on_window = (t) => win ? t >= win.offset_ms && t <= win.offset_ms + win.ms : false;
17
+ </script>
18
+
19
+ <svg viewBox="0 0 {W} {H + 16}" class="chart" role="img" aria-label="used heap over the recording">
20
+ {#if win}
21
+ <rect
22
+ x={x_of(win.offset_ms)}
23
+ y="0"
24
+ width={Math.max(1, x_of(win.offset_ms + win.ms) - x_of(win.offset_ms))}
25
+ height={H}
26
+ fill="#6fe3b0"
27
+ opacity="0.1"
28
+ />
29
+ {/if}
30
+ {#each gc as g, i (i)}
31
+ <rect
32
+ x={x_of(g.t)}
33
+ y="0"
34
+ width={Math.max(1, x_of(g.t + g.ms) - x_of(g.t))}
35
+ height={H}
36
+ fill={g.kind === 'major' ? '#f85149' : '#d29922'}
37
+ opacity="0.55"
38
+ >
39
+ <title>{g.kind} GC, {fmt_ms(g.ms)} ms at {fmt_ms(g.t)} ms</title>
40
+ </rect>
41
+ {/each}
42
+ {#each alloc.bursts as b, i (i)}
43
+ <rect
44
+ x={x_of(b.t0)}
45
+ y={H - 4}
46
+ width={Math.max(2, x_of(b.t1) - x_of(b.t0))}
47
+ height="4"
48
+ fill="#f0883e"
49
+ >
50
+ <title
51
+ >+{b.mb} MB in {fmt_ms(b.t1 - b.t0)} ms ({b.rate} MB/s){b.running[0]
52
+ ? ` while ${b.running[0].label} ran`
53
+ : ''}</title
54
+ >
55
+ </rect>
56
+ {/each}
57
+ <path d={path} fill="none" stroke="#6ca8e0" stroke-width="1.5" />
58
+ <text x="2" y="11" font-size="10" fill="#6f8378">{Math.round(hi)} MB</text>
59
+ <text x="2" y={H - 8} font-size="10" fill="#6f8378">{Math.round(lo)} MB</text>
60
+ <text x={W - 2} y={H + 12} font-size="10" fill="#6f8378" text-anchor="end"
61
+ >{fmt_ms(t_max)} ms</text
62
+ >
63
+ {#if win}
64
+ <text x={x_of(win.offset_ms) + 3} y={H + 12} font-size="10" fill="#6fe3b0"
65
+ >the render this report explains</text
66
+ >
67
+ {/if}
68
+ </svg>
69
+ <p class="hint">
70
+ The heap grew <b>{alloc.grown_mb} MB</b> over the intervals that grew, sampled every ~{alloc.period_ms}
71
+ ms when the loop yielded (the longest stretch without a sample, a synchronous run, was {fmt_ms(
72
+ alloc.longest_gap_ms
73
+ )} ms). Orange marks under the line are the bursts below; red and yellow bars are the collections —
74
+ a burst with one inside allocated more than the line shows.
75
+ </p>
76
+ {#if alloc.bursts.length}
77
+ <table>
78
+ <thead
79
+ ><tr
80
+ ><th>when</th><th class="num">grew</th><th class="num">rate</th><th>what was running</th
81
+ ></tr
82
+ ></thead
83
+ >
84
+ <tbody>
85
+ {#each alloc.bursts as b, i (i)}
86
+ <tr>
87
+ <td class="num"
88
+ >{fmt_ms(b.t0)}–{fmt_ms(b.t1)} ms{#if on_window(b.t0)}<span
89
+ class="hint"
90
+ title="inside the render this report explains"
91
+ >
92
+ ●</span
93
+ >{/if}</td
94
+ >
95
+ <td class="num"
96
+ ><b>+{b.mb} MB</b>{#if b.gc}<span
97
+ class="hint"
98
+ title="a collection fell inside: the allocation was more than the growth"
99
+ >
100
+ +GC</span
101
+ >{/if}</td
102
+ >
103
+ <td class="num">{b.rate} MB/s</td>
104
+ <td class="fn">
105
+ {#each b.running as r, k (k)}
106
+ <span class="who"
107
+ ><i style="background:{color(r.category)}"></i>{r.label}
108
+ <span class="hint"
109
+ >{Math.round(r.share * 100)}%{#if r.file}
110
+ · {r.file}{/if}</span
111
+ ></span
112
+ >
113
+ {:else}
114
+ <span class="hint"
115
+ >nothing on the CPU (the growth came from a native side or between samples)</span
116
+ >
117
+ {/each}
118
+ </td>
119
+ </tr>
120
+ {/each}
121
+ </tbody>
122
+ </table>
123
+ {/if}
124
+
125
+ <style>
126
+ .chart {
127
+ width: 100%;
128
+ height: auto;
129
+ display: block;
130
+ background: var(--bg-sunken);
131
+ border: 1px solid var(--line);
132
+ border-radius: var(--r-sm);
133
+ }
134
+ .who {
135
+ display: inline-flex;
136
+ align-items: center;
137
+ gap: 5px;
138
+ margin-right: 12px;
139
+ }
140
+ .who i {
141
+ width: 8px;
142
+ height: 8px;
143
+ border-radius: 2px;
144
+ display: inline-block;
145
+ }
146
+ </style>
@@ -0,0 +1,17 @@
1
+ /**
2
+ * WHEN THE HEAP GREW — the used heap sampled finely over the capture (alloc.ts), the GC
3
+ * pauses as drops, the one window the report explains lit, and the fastest-growing
4
+ * stretches listed with what the CPU was running then. Static: nothing to click.
5
+ */
6
+ import type { AllocTimeline } from '../alloc.js';
7
+ type $$ComponentProps = {
8
+ alloc: AllocTimeline;
9
+ gc?: {
10
+ t: number;
11
+ ms: number;
12
+ kind: string;
13
+ }[];
14
+ };
15
+ declare const AllocStrip: import("svelte").Component<$$ComponentProps, {}, "">;
16
+ type AllocStrip = ReturnType<typeof AllocStrip>;
17
+ export default AllocStrip;
@@ -0,0 +1,363 @@
1
+ <script>import { fmt_ms } from "./format.js";
2
+ import { simulate_awaits } from "../insights.js";
3
+ let { nodes, edges, window_ms } = $props();
4
+ const W = 1100;
5
+ const LANE = 26;
6
+ const H = 18;
7
+ const PAD = 8;
8
+ const LEFT = 8;
9
+ const x = (t) => LEFT + t / Math.max(window_ms, .01) * (W - 2 * LEFT);
10
+ const lanes = $derived(nodes.reduce((m, n) => Math.max(m, n.lane + 1), 1));
11
+ const height = $derived(PAD * 2 + lanes * LANE);
12
+ const by_label = $derived.by(() => {
13
+ const m = new Map();
14
+ for (const n of nodes) (m.get(n.label) ?? m.set(n.label, []).get(n.label)).push(n);
15
+ return m;
16
+ });
17
+ // an edge joins the last box of `from` that ended before the first box of `to` starting after it
18
+ const placed = $derived(edges.map((e) => {
19
+ const froms = by_label.get(e.from) ?? [];
20
+ const tos = by_label.get(e.to) ?? [];
21
+ let best = null;
22
+ for (const b of tos) for (const a of froms) if (a.t1 <= b.t0 + .05 && (!best || a.t1 > best.a.t1)) best = {
23
+ a,
24
+ b
25
+ };
26
+ return best ? {
27
+ e,
28
+ ...best
29
+ } : null;
30
+ }).filter((p) => !!p));
31
+ // fixed mid-tone series colours (SVG `fill` attributes can't take a CSS var); they read on both themes
32
+ const color = (kind) => kind === "net" ? "#6ca8e0" : "#e0a06f";
33
+ const short = (s, px) => {
34
+ const max = Math.max(3, Math.floor(px / 6.6));
35
+ return s.length > max ? s.slice(0, max - 1) + "…" : s;
36
+ };
37
+ let tip = $state(null);
38
+ const show = (text, ev) => tip = {
39
+ text,
40
+ x: ev.clientX,
41
+ y: ev.clientY
42
+ };
43
+ const serialized_ms = $derived(edges.reduce((s, e) => s + e.gap_ms, 0));
44
+ // WHAT IF: change a call and see the render under the model (insights.ts)
45
+ let changes = $state({});
46
+ const labels = $derived([...new Set(nodes.map((n) => n.label))]);
47
+ const sim = $derived(Object.keys(changes).length ? simulate_awaits(nodes, edges, window_ms, changes) : null);
48
+ const set_change = (label, c) => {
49
+ const next = { ...changes };
50
+ if (c === null || next[label] === c) delete next[label];
51
+ else next[label] = c;
52
+ changes = next;
53
+ };
54
+ </script>
55
+
56
+ <div class="legend">
57
+ <span><i style="background:{color('net')}"></i> HTTP call</span>
58
+ <span><i style="background:{color('timer')}"></i> timer / file / socket</span>
59
+ <span
60
+ ><i class="arrow"></i> started only once the other finished — the await that serialized them</span
61
+ >
62
+ <span class="hint"
63
+ >{edges.length} serialized start{edges.length === 1 ? '' : 's'} · boxes in the same column ran together</span
64
+ >
65
+ </div>
66
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
67
+ <div
68
+ class="scroll"
69
+ onmouseleave={() => (tip = null)}
70
+ tabindex="0"
71
+ role="region"
72
+ aria-label="the render's calls, scrolls sideways"
73
+ >
74
+ <svg
75
+ width={W}
76
+ {height}
77
+ viewBox="0 0 {W} {height}"
78
+ role="img"
79
+ aria-label="the render's calls and which one waited for which"
80
+ >
81
+ <defs>
82
+ <marker id="aw-head" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"
83
+ ><path d="M0,0 L8,4 L0,8 z" fill="#e0834a" /></marker
84
+ >
85
+ </defs>
86
+ {#each nodes as n, i (i)}
87
+ <g
88
+ class="node"
89
+ transform="translate({x(n.t0)},{PAD + n.lane * LANE})"
90
+ onmouseenter={(ev) =>
91
+ show(
92
+ `${n.label} — ${fmt_ms(n.t1 - n.t0)} ms, from ${fmt_ms(n.t0)} to ${fmt_ms(n.t1)} ms${n.caller ? ` · from ${n.caller}` : ''}`,
93
+ ev
94
+ )}
95
+ onmousemove={(ev) =>
96
+ show(
97
+ `${n.label} — ${fmt_ms(n.t1 - n.t0)} ms, from ${fmt_ms(n.t0)} to ${fmt_ms(n.t1)} ms${n.caller ? ` · from ${n.caller}` : ''}`,
98
+ ev
99
+ )}
100
+ role="img"
101
+ aria-label="{n.label} — {fmt_ms(n.t1 - n.t0)} ms, from {fmt_ms(n.t0)} to {fmt_ms(n.t1)} ms"
102
+ >
103
+ <rect
104
+ width={Math.max(2, x(n.t1) - x(n.t0))}
105
+ height={H}
106
+ rx="3"
107
+ fill={color(n.kind)}
108
+ opacity="0.85"
109
+ />
110
+ <text x="4" y="13">{short(n.label, x(n.t1) - x(n.t0) - 6)}</text>
111
+ </g>
112
+ {/each}
113
+ {#each placed as p, i (i)}
114
+ {@const x1 = x(p.a.t1)}
115
+ {@const y1 = PAD + p.a.lane * LANE + H / 2}
116
+ {@const x2 = x(p.b.t0)}
117
+ {@const y2 = PAD + p.b.lane * LANE + H / 2}
118
+ <path
119
+ class="edge"
120
+ d={p.a.lane === p.b.lane
121
+ ? `M${x1},${y1} L${x2},${y2}`
122
+ : `M${x1},${y1} C${x1 + 12},${y1} ${x2 - 12},${y2} ${x2},${y2}`}
123
+ marker-end="url(#aw-head)"
124
+ onmouseenter={(ev) =>
125
+ show(
126
+ `${p.e.to} started ${fmt_ms(p.e.gap_ms)} ms after ${p.e.from} finished${p.e.at ? ` — awaited at ${p.e.at}` : ''}${p.e.callers && p.e.callers.length > 1 ? ` (${p.e.callers.join(' ← ')})` : ''}`,
127
+ ev
128
+ )}
129
+ onmousemove={(ev) =>
130
+ show(
131
+ `${p.e.to} started ${fmt_ms(p.e.gap_ms)} ms after ${p.e.from} finished${p.e.at ? ` — awaited at ${p.e.at}` : ''}`,
132
+ ev
133
+ )}
134
+ role="img"
135
+ aria-label="{p.e.to} started {fmt_ms(p.e.gap_ms)} ms after {p.e.from} finished"
136
+ />
137
+ {/each}
138
+ </svg>
139
+ </div>
140
+ {#if tip}
141
+ <div
142
+ class="tip"
143
+ style="left:{Math.min(
144
+ tip.x + 14,
145
+ (typeof window !== 'undefined' ? window.innerWidth : 1200) - 420
146
+ )}px;top:{tip.y + 16}px"
147
+ >
148
+ {tip.text}
149
+ </div>
150
+ {/if}
151
+ {#if placed.length}
152
+ <ol class="sites">
153
+ {#each placed.slice(0, 12) as p, i (i)}
154
+ <li>
155
+ <b>{p.e.to}</b> waited for <b>{p.e.from}</b>{#if p.e.at}
156
+ — at <code>{p.e.at}</code>{/if}{#if p.e.gap_ms >= 1}
157
+ <span class="hint">({fmt_ms(p.e.gap_ms)} ms between)</span>{/if}
158
+ </li>
159
+ {/each}
160
+ {#if placed.length > 12}<li class="hint">and {placed.length - 12} more</li>{/if}
161
+ </ol>
162
+ <p class="hint">
163
+ {serialized_ms >= 1 ? `${fmt_ms(serialized_ms)} ms sat between serialized calls. ` : ''}Each
164
+ line is an <code>await</code> that could start its call earlier: move the call above the await it
165
+ waits behind, and the two overlap.
166
+ </p>
167
+ {:else}
168
+ <p class="hint">
169
+ No call waited for another: every call started while the previous ones were still in flight.
170
+ </p>
171
+ {/if}
172
+
173
+ {#if labels.length}
174
+ <details class="whatif">
175
+ <summary
176
+ >What if… <span class="hint"
177
+ >change a call and see the render under the model: parallel starts it with the call it
178
+ waited for, cache makes it instant, remove takes it out</span
179
+ ></summary
180
+ >
181
+ <div class="whatif-grid">
182
+ {#each labels.slice(0, 40) as label (label)}
183
+ <div class="row">
184
+ <span class="lbl">{label}</span>
185
+ <button
186
+ class:on={changes[label] === 'parallel'}
187
+ onclick={() => set_change(label, 'parallel')}>parallel</button
188
+ >
189
+ <button class:on={changes[label] === 'cache'} onclick={() => set_change(label, 'cache')}
190
+ >cache</button
191
+ >
192
+ <button class:on={changes[label] === 'remove'} onclick={() => set_change(label, 'remove')}
193
+ >remove</button
194
+ >
195
+ </div>
196
+ {/each}
197
+ </div>
198
+ {#if sim}
199
+ <p class="result">
200
+ <b>{fmt_ms(sim.before_ms)} ms → {fmt_ms(sim.after_ms)} ms</b>
201
+ <span class={sim.delta_ms < 0 ? 'better' : 'worse'}
202
+ >({sim.delta_ms > 0 ? '+' : ''}{fmt_ms(sim.delta_ms)} ms)</span
203
+ >
204
+ <button class="link" onclick={() => (changes = {})}>reset</button>
205
+ </p>
206
+ {#if sim.chain.length}<p class="hint">
207
+ The longest chain of waits would then be: {sim.chain.join(' → ')}. The CPU between calls
208
+ is kept as recorded; only the waits move.
209
+ </p>{/if}
210
+ {/if}
211
+ </details>
212
+ {/if}
213
+
214
+ <style>
215
+ .whatif {
216
+ margin-top: 10px;
217
+ border: 1px solid var(--line);
218
+ border-radius: var(--r-md);
219
+ padding: 8px 12px;
220
+ }
221
+ .whatif summary {
222
+ cursor: pointer;
223
+ font-weight: 600;
224
+ }
225
+ .whatif-grid {
226
+ display: grid;
227
+ grid-template-columns: 1fr;
228
+ gap: 3px;
229
+ margin-top: 8px;
230
+ max-height: 260px;
231
+ max-width: 560px;
232
+ overflow: auto;
233
+ }
234
+ .whatif .row {
235
+ display: grid;
236
+ grid-template-columns: minmax(0, 1fr) auto auto auto;
237
+ gap: 6px;
238
+ align-items: center;
239
+ font-size: 12px;
240
+ }
241
+ .whatif .lbl {
242
+ white-space: nowrap;
243
+ overflow: hidden;
244
+ text-overflow: ellipsis;
245
+ font-family: var(--font-mono);
246
+ }
247
+ .whatif button {
248
+ background: var(--bg-hover);
249
+ border: 1px solid var(--line-strong);
250
+ color: var(--text-dim);
251
+ border-radius: var(--r-sm);
252
+ padding: 1px 7px;
253
+ font: inherit;
254
+ font-size: 11px;
255
+ cursor: pointer;
256
+ }
257
+ .whatif button:hover {
258
+ border-color: var(--accent-line);
259
+ color: var(--text);
260
+ }
261
+ .whatif button.on {
262
+ border-color: var(--accent);
263
+ background: var(--accent-deep);
264
+ color: var(--accent-strong);
265
+ }
266
+ .whatif .result {
267
+ margin: 8px 0 2px;
268
+ font-size: 13px;
269
+ }
270
+ .whatif .link {
271
+ border: 0;
272
+ background: none;
273
+ text-decoration: underline;
274
+ margin-left: 8px;
275
+ color: var(--accent);
276
+ cursor: pointer;
277
+ }
278
+ .better {
279
+ color: var(--good);
280
+ }
281
+ .worse {
282
+ color: var(--bad);
283
+ }
284
+ .legend {
285
+ display: flex;
286
+ flex-wrap: wrap;
287
+ gap: 4px 14px;
288
+ margin: 6px 0 4px;
289
+ font-size: 11.5px;
290
+ color: var(--text-dim);
291
+ }
292
+ .legend span {
293
+ display: inline-flex;
294
+ align-items: center;
295
+ gap: 5px;
296
+ }
297
+ .legend i {
298
+ width: 12px;
299
+ height: 12px;
300
+ border-radius: 2px;
301
+ display: inline-block;
302
+ }
303
+ .legend i.arrow {
304
+ width: 18px;
305
+ height: 2px;
306
+ background: var(--c-orange);
307
+ }
308
+ .legend .hint {
309
+ flex-basis: 100%;
310
+ color: var(--text-faint);
311
+ }
312
+ .scroll {
313
+ overflow-x: auto;
314
+ background: var(--bg-sunken);
315
+ border: 1px solid var(--line);
316
+ border-radius: var(--r-md);
317
+ }
318
+ svg {
319
+ display: block;
320
+ font-family: var(--font-mono);
321
+ font-size: 10.5px;
322
+ }
323
+ .node text {
324
+ fill: #06120c;
325
+ font-weight: 600;
326
+ pointer-events: none;
327
+ }
328
+ .tip {
329
+ position: fixed;
330
+ pointer-events: none;
331
+ background: var(--bg-raised);
332
+ color: var(--text);
333
+ border: 1px solid var(--line-strong);
334
+ border-radius: var(--r-sm);
335
+ padding: 6px 10px;
336
+ font-size: 12px;
337
+ max-width: 480px;
338
+ z-index: 10;
339
+ box-shadow: var(--shadow-panel);
340
+ }
341
+ .edge {
342
+ fill: none;
343
+ stroke: var(--c-orange);
344
+ stroke-width: 1.6;
345
+ cursor: help;
346
+ }
347
+ .edge:hover {
348
+ stroke-width: 3;
349
+ }
350
+ .sites {
351
+ margin: 8px 0 0;
352
+ padding-left: 22px;
353
+ font-size: 12.5px;
354
+ line-height: 1.6;
355
+ }
356
+ .sites code {
357
+ font-size: 11.5px;
358
+ }
359
+ .hint {
360
+ color: var(--text-faint);
361
+ font-size: 12px;
362
+ }
363
+ </style>
@@ -0,0 +1,16 @@
1
+ /**
2
+ * THE CAUSALITY GRAPH of a render's waits (timeline.ts `awaits`): every call is a box on the
3
+ * render's clock, overlapping calls in their own lanes, and an arrow wherever a call started
4
+ * only once another had finished — the await that serialized them, labelled with the line that
5
+ * did the waiting. Parallel calls read as stacked boxes with no arrow between them. Hover a box
6
+ * or an arrow for the detail. A `wake:'load'` island.
7
+ */
8
+ import type { AwaitNode, AwaitEdge } from '../timeline.js';
9
+ type $$ComponentProps = {
10
+ nodes: AwaitNode[];
11
+ edges: AwaitEdge[];
12
+ window_ms: number;
13
+ };
14
+ declare const Awaits: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ type Awaits = ReturnType<typeof Awaits>;
16
+ export default Awaits;