@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,492 @@
1
+ <script>/**
2
+ * The profiler dashboard (`/__profiler`): live request log, slowest routes, saved reports, and the
3
+ * "profile a page" form. Static server render — no island; it's a data snapshot. Ports report.ts's
4
+ * render_dashboard.
5
+ */
6
+ import { fmt_ms, label_of } from "./format.js";
7
+ import Shell from "./Shell.svelte";
8
+ import LocalReports from "./LocalReports.svelte" with { wake: "load" };
9
+ let { data } = $props();
10
+ const { base, recent, routes, reports, top_fix, site_fixes, holes, recording, dev, rss_mb, inflight, history, by, tag_keys, trap, sampled, background_note } = $derived(data);
11
+ const sampled_max = $derived(Math.max(...sampled?.functions.map((f) => f.self_ms) ?? [0], .01));
12
+ const time = (ms) => new Date(ms).toLocaleTimeString();
13
+ let recent_desc = $derived(recent.slice(-40).reverse());
14
+ let top_routes = $derived(routes.slice(0, 20));
15
+ // the previous page-mode report of the same page, for each report — the "compare" link
16
+ const prev_of = $derived.by(() => {
17
+ const m = new Map();
18
+ for (const h of history) for (let i = 1; i < h.points.length; i++) m.set(h.points[i].id, h.points[i - 1].id);
19
+ return m;
20
+ });
21
+ const tracked = $derived(history.filter((h) => h.points.length >= 2));
22
+ const spark = (pts) => {
23
+ const w = 160, h = 28, pad = 3;
24
+ const max = Math.max(...pts.map((p) => p.median), 1);
25
+ const step = pts.length > 1 ? (w - 2 * pad) / (pts.length - 1) : 0;
26
+ return {
27
+ w,
28
+ h,
29
+ poly: pts.map((p, i) => `${pad + i * step},${h - pad - p.median / max * (h - 2 * pad)}`).join(" ")
30
+ };
31
+ };
32
+ const delta_pct = (pts) => {
33
+ const a = pts[pts.length - 2].median;
34
+ const b = pts[pts.length - 1].median;
35
+ return a > 0 ? (b - a) / a * 100 : 0;
36
+ };
37
+ </script>
38
+
39
+ <svelte:head><title>profiler — dashboard</title></svelte:head>
40
+
41
+ <Shell {base}>
42
+ <div class="head">
43
+ <h1>
44
+ SSR profiler
45
+ <small>live since server start</small>
46
+ </h1>
47
+ <div class="actions">
48
+ <a
49
+ class="btn"
50
+ href="{base}/site"
51
+ title="Every request as a dot, and where the site's time goes per route — from this instance, or from a sink over days"
52
+ >The whole site</a
53
+ >
54
+ <a class="btn" href="{base}/view">Import<span class="sub">.ogp</span></a>
55
+ <a
56
+ class="btn danger"
57
+ class:recording
58
+ href="{base}/reset"
59
+ title="Stop any running or stuck recording and clear the profiler's state.">Reset</a
60
+ >
61
+ {#if !dev}<a class="btn" href="{base}/logout">Lock</a>{/if}
62
+ </div>
63
+ </div>
64
+
65
+ <div class="summary">
66
+ <div class="stat"><b>{rss_mb} MB</b><span>memory (rss)</span></div>
67
+ <div class="stat"><b>{inflight}</b><span>requests in flight</span></div>
68
+ <div class="stat"><b>{reports.length}</b><span>reports held</span></div>
69
+ <div class="stat"><b>{routes.length}</b><span>routes seen</span></div>
70
+ {#if tracked.length}<div class="stat">
71
+ <b>{tracked.length}</b><span>pages tracked over time</span>
72
+ </div>{/if}
73
+ </div>
74
+
75
+ {#if recording}
76
+ <p class="verdict">
77
+ A profile is running right now. Refresh in a moment — or hit <b>Reset</b> above if a run got stuck.
78
+ </p>
79
+ {/if}
80
+
81
+ <div class="grid">
82
+ <section class="panel">
83
+ <h2>Profile a page</h2>
84
+ <p class="hint">
85
+ A path on this site. It renders through your real server a few times and shows exactly where
86
+ the time went — components, functions, allocations, and outbound calls.
87
+ </p>
88
+ <form class="inline" action="{base}/run" method="get">
89
+ <label>path <input name="p" placeholder="/some/slow/page" size="24" required /></label>
90
+ <label>renders <input name="runs" value="5" size="3" /></label>
91
+ <label
92
+ class="ogp-opt"
93
+ title="Recommended on serverless: the report can't be kept in memory across invocations. Download the encrypted .ogp, then open it via Import."
94
+ >
95
+ <input type="checkbox" name="format" value="ogp" /> <code>.ogp</code>
96
+ </label>
97
+ <button class="primary">Profile</button>
98
+ </form>
99
+ <p class="hint">
100
+ On a <b>serverless</b> host, tick <code>.ogp</code> — the profile streams back as an
101
+ encrypted file, then <a href="{base}/view">open it here</a>. Or profile one live request
102
+ with the <code>x-profile: &lt;secret&gt;</code> header.
103
+ </p>
104
+ </section>
105
+ <section class="panel">
106
+ <h2>Getting started</h2>
107
+ <p class="hint">
108
+ Type a path and hit <b>Profile</b> to record a page. Every recording shows up in the sidebar
109
+ and, with a store configured, is shared across instances. Watch a route's median over time
110
+ under
111
+ <b>Pages over time</b>; a jump is a regression you can open a compare on.
112
+ </p>
113
+ <p class="hint">
114
+ The <b>whole site</b> view plots every request and where each route spends its time.
115
+ <b>Reset</b>
116
+ clears a stuck recording if the site drags after profiling.
117
+ </p>
118
+ </section>
119
+ </div>
120
+
121
+ <LocalReports {base} server_ids={reports.map((r) => r.id)} />
122
+
123
+ {#if trap || sampled || background_note}
124
+ <h2>In the background</h2>
125
+ {#if background_note}<p class="verdict">{background_note}</p>{/if}
126
+ {#if trap}
127
+ <p class="hint">
128
+ <b>Catch the slow one:</b>
129
+ {trap.armed ? 'armed' : 'done'} — a coarse sampler runs in {Math.round(
130
+ trap.window_ms / 1000
131
+ )} s windows and keeps the window when a request crosses <b>{trap.over} ms</b>. Caught {trap.caught}
132
+ of {trap.keep}.{#if trap.caught}
133
+ The reports are in the list below, labelled "caught".{/if}
134
+ </p>
135
+ {/if}
136
+ {#if sampled}
137
+ <p class="hint">
138
+ <b>Always-on sampling:</b> one {Math.round((sampled.window_ms / 1000) * 10) / 10} s window every
139
+ {sampled.every_s} s, folded into the table below — {sampled.windows} window{sampled.windows ===
140
+ 1
141
+ ? ''
142
+ : 's'} so far{#if sampled.since}, since {time(sampled.since)}{/if},
143
+ {fmt_ms(sampled.busy_ms)} ms busy of {fmt_ms(sampled.sampled_ms)} ms sampled. Accuracy from volume:
144
+ a function that is hot here is hot in real traffic, whatever one recording says.
145
+ </p>
146
+ {#if sampled.functions.length}
147
+ <table>
148
+ <thead
149
+ ><tr
150
+ ><th>function</th><th>where</th><th class="num">self ms (summed)</th><th class="num"
151
+ >in windows</th
152
+ ></tr
153
+ ></thead
154
+ >
155
+ <tbody>
156
+ {#each sampled.functions.slice(0, 25) as f (f.key)}
157
+ <tr>
158
+ <td class="fn"
159
+ ><b>{f.name}</b>{#if f.pkg}<span class="hint"> · {f.pkg}</span>{/if}</td
160
+ >
161
+ <td class="file"
162
+ >{f.url}{#if f.line > 0}:{f.line}{/if}</td
163
+ >
164
+ <td class="num bar-cell"
165
+ ><div class="bar" style="width:{(f.self_ms / sampled_max) * 100}%"></div>
166
+ <b>{fmt_ms(f.self_ms)}</b></td
167
+ >
168
+ <td class="num">{f.windows}</td>
169
+ </tr>
170
+ {/each}
171
+ </tbody>
172
+ </table>
173
+ {:else}
174
+ <p class="hint">No window sampled yet.</p>
175
+ {/if}
176
+ {/if}
177
+ {/if}
178
+
179
+ <div class="grid">
180
+ {#if tracked.length}
181
+ <section class="panel wide">
182
+ <h2>Pages over time</h2>
183
+ <p class="hint">
184
+ Every page profiled more than once: its median render per run, oldest to newest, and its
185
+ page score then and now. A jump is a regression — open the compare to see which component
186
+ or function moved.
187
+ </p>
188
+ <table>
189
+ <thead>
190
+ <tr
191
+ ><th>page</th><th>runs</th><th class="num">latest</th><th class="num">vs previous</th
192
+ ><th class="num">score</th><th><span class="sr-only">actions</span></th></tr
193
+ >
194
+ </thead>
195
+ <tbody>
196
+ {#each tracked as h (h.page)}
197
+ {@const g = spark(h.points)}
198
+ {@const d = delta_pct(h.points)}
199
+ {@const last = h.points[h.points.length - 1]}
200
+ {@const before = h.points[h.points.length - 2]}
201
+ <tr>
202
+ <td class="fn">{h.page}</td>
203
+ <td
204
+ ><svg width={g.w} height={g.h} viewBox="0 0 {g.w} {g.h}"
205
+ ><polyline
206
+ points={g.poly}
207
+ fill="none"
208
+ stroke="var(--c-blue)"
209
+ stroke-width="1.5"
210
+ /></svg
211
+ ></td
212
+ >
213
+ <td class="num"><a href="{base}/report/{last.id}">{fmt_ms(last.median)} ms</a></td>
214
+ <td
215
+ class="num"
216
+ style="color:{d > 5 ? 'var(--bad)' : d < -5 ? 'var(--good)' : 'inherit'}"
217
+ >{d > 0 ? '+' : ''}{d.toFixed(0)}%</td
218
+ >
219
+ <td class="num" title="page score, before → now"
220
+ >{#if last.score !== undefined}{#if before.score !== undefined && before.score !== last.score}<span
221
+ class="hint">{before.score} →</span
222
+ >
223
+ {/if}<b
224
+ style="color:{before.score !== undefined && last.score < before.score
225
+ ? 'var(--bad)'
226
+ : before.score !== undefined && last.score > before.score
227
+ ? 'var(--good)'
228
+ : 'inherit'}">{last.score}</b
229
+ >{/if}</td
230
+ >
231
+ <td><a href="{base}/compare/{before.id}/{last.id}">compare</a></td>
232
+ </tr>
233
+ {/each}
234
+ </tbody>
235
+ </table>
236
+ </section>
237
+ {/if}
238
+
239
+ {#if site_fixes?.length}
240
+ <!-- FIX ONCE, FASTER EVERYWHERE: lines the latest reports of two or more pages share -->
241
+ <section class="panel wide">
242
+ <h2>Fix once, faster on every page</h2>
243
+ <p class="hint">
244
+ Slow lines that show in the latest report of two or more pages. One change to each makes
245
+ all of them faster: what it gives back per render, per page and in all.
246
+ </p>
247
+ <table>
248
+ <thead>
249
+ <tr><th>line</th><th>what</th><th>pages</th><th class="num">in all</th></tr>
250
+ </thead>
251
+ <tbody>
252
+ {#each site_fixes as f (f.kind + f.where + f.line)}
253
+ <tr>
254
+ <td><code title={f.code}>{f.where}:{f.line}</code></td>
255
+ <td>{f.title}</td>
256
+ <td class="pages">
257
+ {#each f.pages as p, i (p.page)}{i ? ' · ' : ''}<a href="{base}/report/{p.report}"
258
+ >{p.page}</a
259
+ > <span class="save">~{fmt_ms(p.ms)} ms</span>{/each}
260
+ </td>
261
+ <td class="num"><b>~{fmt_ms(f.total_ms)} ms</b></td>
262
+ </tr>
263
+ {/each}
264
+ </tbody>
265
+ </table>
266
+ </section>
267
+ {/if}
268
+
269
+ {#if reports.length}
270
+ <section class="panel wide">
271
+ <h2>Reports</h2>
272
+ <table>
273
+ <thead>
274
+ <tr
275
+ ><th>report</th><th>when</th><th class="num">window</th><th class="num">requests</th
276
+ ><th>biggest fix</th><th><span class="sr-only">actions</span></th></tr
277
+ >
278
+ </thead>
279
+ <tbody>
280
+ {#each reports as r (r.id)}
281
+ <tr>
282
+ <td><a href="{base}/report/{r.id}">{label_of(r)}</a></td>
283
+ <td>{time(r.created)}</td>
284
+ <td class="num">{fmt_ms(r.duration_ms)} ms</td>
285
+ <td class="num">{r.requests.length}</td>
286
+ <td class="fix">
287
+ {#if top_fix?.[r.id]}
288
+ {@const t = top_fix[r.id]}
289
+ <a href="{base}/report/{r.id}#pattern-0">{t.title}</a>
290
+ {#if t.save_ms >= 0.1}<span class="save"
291
+ >~{fmt_ms(t.save_ms)} ms{t.wait ? ' of waiting' : ''}</span
292
+ >{/if}{#if t.more}<span class="more"> · {t.more} more</span>{/if}
293
+ {:else}—{/if}
294
+ </td>
295
+ <td
296
+ >{#if prev_of.has(r.id)}<a href="{base}/compare/{prev_of.get(r.id)}/{r.id}"
297
+ >compare with previous</a
298
+ >{/if}</td
299
+ >
300
+ </tr>
301
+ {/each}
302
+ </tbody>
303
+ </table>
304
+ </section>
305
+ {/if}
306
+
307
+ {#if holes}
308
+ <section class="panel wide" data-hole-slots>
309
+ <h2>
310
+ Holes on this server <span class="hint" style="font-weight:400"
311
+ >({holes.rendered} rendered, {holes.slots} render slots per process)</span
312
+ >
313
+ </h2>
314
+ {#if holes.summary}
315
+ <p class="warn">{holes.summary}</p>
316
+ {:else}
317
+ <p class="hint">
318
+ No hole waited for a render slot. Each render holds one of the {holes.slots} slots through
319
+ its awaits, so slow holes would make the next ones wait.
320
+ </p>
321
+ {/if}
322
+ <table>
323
+ <thead>
324
+ <tr
325
+ ><th>hole</th><th class="num">requests</th><th class="num">cache hits</th><th
326
+ class="num">failed</th
327
+ ><th class="num">render ms</th><th
328
+ class="num"
329
+ title="mean wait for a render slot per rendered request (worst in brackets)"
330
+ >slot wait ms</th
331
+ ><th class="num" title="its part of all the time the render slots were held"
332
+ >slot time</th
333
+ ></tr
334
+ >
335
+ </thead>
336
+ <tbody>
337
+ {#each holes.rows as h (h.id)}
338
+ <tr>
339
+ <td class="fn">{h.name}</td>
340
+ <td class="num">{h.requests}</td>
341
+ <td class="num">{h.hits}</td>
342
+ <td class="num">{h.failed}</td>
343
+ <td class="num">{h.requests - h.hits - h.failed ? fmt_ms(h.render_ms) : '—'}</td>
344
+ <td class="num"
345
+ >{h.queue_ms ? fmt_ms(h.queue_ms) : '0'}{#if h.queue_max_ms >= 50}
346
+ <span class="hint">({fmt_ms(h.queue_max_ms)})</span>{/if}</td
347
+ >
348
+ <td class="num">{Math.round(h.slot_share * 100)}%</td>
349
+ </tr>
350
+ {/each}
351
+ </tbody>
352
+ </table>
353
+ </section>
354
+ {/if}
355
+
356
+ <section class="panel wide">
357
+ <h2>Slowest routes</h2>
358
+ <p class="hint">
359
+ Wall-clock per request since server start. p95 is the slow tail. "net p50" is time inside
360
+ outbound calls — when it tracks the total, the route is waiting on other services, not
361
+ computing.
362
+ </p>
363
+ {#if tag_keys.length}
364
+ <p class="hint">
365
+ Split by tag (<code>tag()</code> from @ogygia/devtools/profiler):
366
+ <a href={base} class:active={!by}>none</a>
367
+ {#each tag_keys as k (k)}· <a
368
+ href="{base}?by={encodeURIComponent(k)}"
369
+ class:active={by === k}>{k}</a
370
+ >{/each}
371
+ </p>
372
+ {/if}
373
+ {#if top_routes.length}
374
+ <table>
375
+ <thead>
376
+ <tr
377
+ ><th>route</th><th class="num">hits</th><th class="num">p50 ms</th><th class="num"
378
+ >p95 ms</th
379
+ ><th class="num">max ms</th><th class="num">net p50</th></tr
380
+ >
381
+ </thead>
382
+ <tbody>
383
+ {#each top_routes as r (r.route)}
384
+ <tr>
385
+ <td class="fn">{r.route}</td>
386
+ <td class="num">{r.count}</td>
387
+ <td class="num">{fmt_ms(r.p50)}</td>
388
+ <td class="num">{fmt_ms(r.p95)}</td>
389
+ <td class="num">{fmt_ms(r.max)}</td>
390
+ <td class="num">{fmt_ms(r.net_p50)}</td>
391
+ </tr>
392
+ {/each}
393
+ </tbody>
394
+ </table>
395
+ {:else}
396
+ <p class="hint">No requests seen yet — load some pages, then refresh.</p>
397
+ {/if}
398
+ </section>
399
+
400
+ <section class="panel wide">
401
+ <h2>Recent requests</h2>
402
+ {#if recent_desc.length}
403
+ <table>
404
+ <thead>
405
+ <tr
406
+ ><th>when</th><th>method</th><th>path</th><th>route</th>{#if tag_keys.length}<th
407
+ >tags</th
408
+ >{/if}<th class="num">status</th><th class="num">net ms</th><th class="num"
409
+ >total ms</th
410
+ ></tr
411
+ >
412
+ </thead>
413
+ <tbody>
414
+ {#each recent_desc as e (e.ts + e.path)}
415
+ <tr>
416
+ <td>{time(e.ts)}</td>
417
+ <td>{e.method}</td>
418
+ <td class="fn"
419
+ >{e.path}{#if e.internal}<span class="warn"> (profiler)</span>{/if}</td
420
+ >
421
+ <td class="file">{e.route ?? '—'}</td>
422
+ {#if tag_keys.length}<td class="file"
423
+ >{e.tags
424
+ ? Object.entries(e.tags)
425
+ .map(([k, v]) => `${k}=${v}`)
426
+ .join(' ')
427
+ : '—'}</td
428
+ >{/if}
429
+ <td class="num">{e.status || '—'}</td>
430
+ <td class="num"
431
+ >{#if e.net_count}{fmt_ms(e.net_ms)}
432
+ <span class="hint">({e.net_count})</span>{:else}—{/if}</td
433
+ >
434
+ <td class="num"><b>{fmt_ms(e.ms)}</b></td>
435
+ </tr>
436
+ {/each}
437
+ </tbody>
438
+ </table>
439
+ {:else}
440
+ <p class="hint">Nothing yet.</p>
441
+ {/if}
442
+ </section>
443
+ </div>
444
+ </Shell>
445
+
446
+ <style>
447
+ td.fix {
448
+ font-size: 12.5px;
449
+ max-width: 360px;
450
+ }
451
+ td.fix .save {
452
+ color: var(--c-orange);
453
+ font-weight: 600;
454
+ margin-left: 6px;
455
+ white-space: nowrap;
456
+ }
457
+ td.fix .more {
458
+ color: var(--text-faint);
459
+ }
460
+ .head {
461
+ display: flex;
462
+ align-items: flex-start;
463
+ justify-content: space-between;
464
+ gap: 16px;
465
+ flex-wrap: wrap;
466
+ }
467
+ .head .actions {
468
+ margin-top: 4px;
469
+ }
470
+ .ogp-opt {
471
+ display: inline-flex;
472
+ align-items: center;
473
+ gap: 5px;
474
+ font-size: 12.5px;
475
+ color: var(--text-dim);
476
+ }
477
+ .bar-cell {
478
+ position: relative;
479
+ min-width: 120px;
480
+ }
481
+ .bar-cell .bar {
482
+ position: absolute;
483
+ left: 4px;
484
+ right: 4px;
485
+ bottom: 3px;
486
+ height: 3px;
487
+ max-width: calc(100% - 8px);
488
+ background: var(--accent);
489
+ border-radius: 2px;
490
+ opacity: 0.7;
491
+ }
492
+ </style>
@@ -0,0 +1,31 @@
1
+ declare const Dashboard: import("svelte").Component<{
2
+ data: {
3
+ base: string;
4
+ recent: import("../report.js").RequestEntry[];
5
+ routes: import("../report.js").RouteAgg[];
6
+ by: string | null;
7
+ tag_keys: string[];
8
+ reports: import("../report.js").ReportMeta[];
9
+ top_fix: Record<string, {
10
+ title: string;
11
+ save_ms: number;
12
+ wait: boolean;
13
+ more: number;
14
+ }>;
15
+ site_fixes: import("../site-fixes.js").SiteFix[];
16
+ holes: import("../hole-slots.js").HoleSlots | null;
17
+ recording: boolean;
18
+ dev: boolean;
19
+ rss_mb: number;
20
+ inflight: number;
21
+ history: import("../profiler-router.js").PageHistory[];
22
+ trap: import("../index.js").TrapStatus | null;
23
+ sampled: import("../index.js").SampledSummary | null;
24
+ background_note: string | null;
25
+ };
26
+ form: null;
27
+ params: {};
28
+ search: {};
29
+ }, {}, "">;
30
+ type Dashboard = ReturnType<typeof Dashboard>;
31
+ export default Dashboard;