@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,206 @@
1
+ <script>import { fmt_bytes, fmt_ms } from "./format.js";
2
+ let { river } = $props();
3
+ const W = 1e3;
4
+ const COL_X = [
5
+ 40,
6
+ 300,
7
+ 560,
8
+ 820
9
+ ];
10
+ const NODE_W = 12;
11
+ const PAD = 8;
12
+ const MIN_H = 8;
13
+ const COLOR = [
14
+ "#5b8fd6",
15
+ "#4d9c6b",
16
+ "#d9a03d",
17
+ "#2ea043"
18
+ ];
19
+ const waste_keys = $derived(new Set(river.waste.map((w) => w.key)));
20
+ // layout: per column, node heights proportional to value (a column's own unit), stacked
21
+ const layout = $derived.by(() => {
22
+ const H_COL = 260;
23
+ const pos = new Map();
24
+ for (let col = 0; col < 4; col++) {
25
+ const nodes = river.nodes.filter((n) => n.col === col);
26
+ if (!nodes.length) continue;
27
+ const total = nodes.reduce((s, n) => s + Math.max(n.value, 0), 0) || 1;
28
+ const avail = H_COL - PAD * (nodes.length - 1);
29
+ let y = 20;
30
+ for (const n of nodes) {
31
+ const h = Math.max(MIN_H, Math.max(n.value, 0) / total * avail * .9);
32
+ pos.set(n.id, {
33
+ x: COL_X[col],
34
+ y,
35
+ h,
36
+ n
37
+ });
38
+ y += h + PAD;
39
+ }
40
+ }
41
+ // links: thickness per column pair, normalised to the biggest link of that pair; stacked on both ends
42
+ const out_y = new Map();
43
+ const in_y = new Map();
44
+ const paths = [];
45
+ for (let col = 0; col < 3; col++) {
46
+ const links = river.links.filter((l) => pos.get(l.from)?.n.col === col);
47
+ const max = Math.max(...links.map((l) => l.value), 1);
48
+ for (const l of links) {
49
+ const a = pos.get(l.from);
50
+ const b = pos.get(l.to);
51
+ if (!a || !b) continue;
52
+ const w = Math.max(2, l.value / max * Math.min(a.h, b.h, 40));
53
+ const y0 = a.y + (out_y.get(l.from) ?? 0) + w / 2;
54
+ const y1 = b.y + (in_y.get(l.to) ?? 0) + w / 2;
55
+ out_y.set(l.from, (out_y.get(l.from) ?? 0) + w);
56
+ in_y.set(l.to, (in_y.get(l.to) ?? 0) + w);
57
+ const x0 = a.x + NODE_W;
58
+ const x1 = b.x;
59
+ const c = (x1 - x0) / 2;
60
+ paths.push({
61
+ d: `M${x0},${y0} C${x0 + c},${y0} ${x1 - c},${y1} ${x1},${y1}`,
62
+ w,
63
+ l,
64
+ from: a.n,
65
+ to: b.n,
66
+ waste: b.n.kind === "key" && waste_keys.has(b.n.label)
67
+ });
68
+ }
69
+ }
70
+ const height = Math.max(...[...pos.values()].map((p) => p.y + p.h), 120) + 24;
71
+ return {
72
+ pos: [...pos.values()],
73
+ paths,
74
+ height
75
+ };
76
+ });
77
+ let tip = $state(null);
78
+ const move = (e) => {
79
+ if (tip) tip = {
80
+ text: tip.text,
81
+ x: e.clientX,
82
+ y: e.clientY
83
+ };
84
+ };
85
+ const fmt = (v, unit) => unit === "ms" ? `${fmt_ms(v)} ms` : fmt_bytes(v);
86
+ const unit_of = (n) => n.col <= 1 ? "ms" : "bytes";
87
+ const HEAD = [
88
+ "upstream calls (ms waited)",
89
+ "load functions",
90
+ "page.data keys (bytes in the seed)",
91
+ "islands (bytes they read)"
92
+ ];
93
+ </script>
94
+
95
+ <svg
96
+ viewBox="0 0 {W} {layout.height}"
97
+ class="river"
98
+ role="img"
99
+ aria-label="the data river"
100
+ onmouseleave={() => (tip = null)}
101
+ >
102
+ {#each HEAD as h, i (h)}
103
+ <text x={COL_X[i]} y="12" class="head">{h}</text>
104
+ {/each}
105
+ {#each layout.paths as p, i (i)}
106
+ <path
107
+ d={p.d}
108
+ stroke={p.waste ? '#ff7b72' : COLOR[p.from.col]}
109
+ stroke-width={p.w}
110
+ fill="none"
111
+ class="band"
112
+ role="presentation"
113
+ onmouseenter={(e) =>
114
+ (tip = {
115
+ text: `${p.from.label} → ${p.to.label}: ${fmt(p.l.value, p.l.unit)}${p.waste ? ' — no island reads this key' : ''}`,
116
+ x: e.clientX,
117
+ y: e.clientY
118
+ })}
119
+ onmousemove={move}
120
+ />
121
+ {/each}
122
+ {#each layout.pos as p (p.n.id)}
123
+ <rect
124
+ x={p.x}
125
+ y={p.y}
126
+ width={NODE_W}
127
+ height={p.h}
128
+ fill={p.n.kind === 'key' && waste_keys.has(p.n.label)
129
+ ? '#ff7b72'
130
+ : p.n.kind === 'unknown'
131
+ ? '#6b7280'
132
+ : COLOR[p.n.col]}
133
+ rx="2"
134
+ role="presentation"
135
+ onmouseenter={(e) =>
136
+ (tip = {
137
+ text: `${p.n.label}: ${fmt(p.n.value, unit_of(p.n))}${p.n.detail ? ` — ${p.n.detail}` : ''}`,
138
+ x: e.clientX,
139
+ y: e.clientY
140
+ })}
141
+ onmousemove={move}
142
+ />
143
+ <text
144
+ x={p.x + NODE_W + 5}
145
+ y={p.y + Math.min(p.h, 14) / 2 + 4}
146
+ class="label"
147
+ class:waste={p.n.kind === 'key' && waste_keys.has(p.n.label)}
148
+ >{p.n.label.length > 34 ? p.n.label.slice(0, 32) + '…' : p.n.label}
149
+ <tspan class="val">{fmt(p.n.value, unit_of(p.n))}</tspan></text
150
+ >
151
+ {/each}
152
+ </svg>
153
+ {#if tip}
154
+ <div class="tip" style="left:{tip.x + 12}px;top:{tip.y + 12}px">{tip.text}</div>
155
+ {/if}
156
+ {#each river.notes as n (n)}<p class="verdict">{n}</p>{/each}
157
+ <p class="hint">
158
+ Left to right: what each upstream call cost the load that made it, what each load put into <code
159
+ >page.data</code
160
+ >, how big each key is in the seed the browser downloads, and which island reads it. A load whose
161
+ source the profiler could not read shows its keys as unknown; a key in red ships to no island.
162
+ </p>
163
+
164
+ <style>
165
+ .river {
166
+ width: 100%;
167
+ height: auto;
168
+ display: block;
169
+ background: var(--bg-sunken);
170
+ border: 1px solid var(--line);
171
+ border-radius: 6px;
172
+ }
173
+ .head {
174
+ fill: var(--text-dim);
175
+ font-size: 10px;
176
+ text-transform: uppercase;
177
+ letter-spacing: 0.04em;
178
+ }
179
+ .band {
180
+ opacity: 0.45;
181
+ }
182
+ .band:hover {
183
+ opacity: 0.85;
184
+ }
185
+ .label {
186
+ fill: var(--text);
187
+ font-size: 11px;
188
+ }
189
+ .label.waste {
190
+ fill: var(--bad);
191
+ }
192
+ .val {
193
+ fill: var(--text-faint);
194
+ }
195
+ .tip {
196
+ position: fixed;
197
+ z-index: 20;
198
+ background: var(--bg-raised);
199
+ border: 1px solid var(--line-strong);
200
+ border-radius: 6px;
201
+ padding: 6px 8px;
202
+ font-size: 12px;
203
+ max-width: 380px;
204
+ pointer-events: none;
205
+ }
206
+ </style>
@@ -0,0 +1,13 @@
1
+ /**
2
+ * THE DATA RIVER — calls → loads → page.data keys → islands as a flow (river.ts). Band width is
3
+ * the amount: ms on the left half (what the loads waited for), bytes on the right (what the seed
4
+ * carries and who reads it). A key that no island reads ends in red: fetched and shipped for no
5
+ * one. A `wake:'load'` island; hover a band or a node.
6
+ */
7
+ import type { River } from '../river.js';
8
+ type $$ComponentProps = {
9
+ river: River;
10
+ };
11
+ declare const DataRiver: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type DataRiver = ReturnType<typeof DataRiver>;
13
+ export default DataRiver;
@@ -0,0 +1,207 @@
1
+ <script>/**
2
+ * DOM TIME TRAVEL — each island's markup as the server sent it, after it hydrated, and when the
3
+ * page went away, with the diff between them (dom-diff.ts), plus the visit's layout shifts drawn
4
+ * on a page-shaped map with the island that moved. Browser-only data: the beacon keeps the
5
+ * snapshots in this browser's store (they are big), never on the server. A `wake:'load'` island.
6
+ */
7
+ import { html_diff, shift_boxes } from "../dom-diff.js";
8
+ import { latest_visit } from "./store.js";
9
+ let { page, names = {}, visit = null } = $props();
10
+ let rec = $state(null);
11
+ let looked = $state(false);
12
+ // read once, at setup: the page's latest visit from this browser's store
13
+ // svelte-ignore state_referenced_locally
14
+ void latest_visit(page).then((r) => rec = r).finally(() => looked = true);
15
+ const v = $derived(rec?.visit ?? visit);
16
+ const boxes = $derived(v ? shift_boxes(v.shifts, v.viewport) : []);
17
+ const cls_total = $derived(boxes.reduce((s, b) => s + b.value, 0));
18
+ const name = (fp) => fp ? names[fp] ?? fp.slice(0, 8) : "outside any island";
19
+ let picked = $state(null);
20
+ let moment = $state("hydrated");
21
+ const snap = $derived(rec?.snapshots.find((s) => s.fp === picked) ?? rec?.snapshots[0] ?? null);
22
+ const diff = $derived(snap ? html_diff(snap.ssr, moment === "final" && snap.final !== undefined ? snap.final : snap.hydrated) : null);
23
+ const VW = 240;
24
+ const vh = $derived(v?.viewport ? Math.round(VW * v.viewport[1] / v.viewport[0]) : 160);
25
+ </script>
26
+
27
+ {#if v && (boxes.length || rec?.snapshots.length)}
28
+ <div class="travel">
29
+ <div class="map">
30
+ <h3 class="sub-h">
31
+ Layout shifts <span class="hint"
32
+ >{cls_total.toFixed(3)} CLS in this visit, where and whose</span
33
+ >
34
+ </h3>
35
+ <svg
36
+ viewBox="0 0 {VW} {vh}"
37
+ width={VW}
38
+ height={vh}
39
+ class="viewport"
40
+ role="img"
41
+ aria-label="layout shifts on the viewport"
42
+ >
43
+ <rect x="0" y="0" width={VW} height={vh} class="vp" />
44
+ {#each boxes as b, i (i)}
45
+ <rect
46
+ x={b.fx * VW}
47
+ y={b.fy * vh}
48
+ width={Math.max(b.w * VW, 2)}
49
+ height={Math.max(b.h * vh, 2)}
50
+ class="from"
51
+ />
52
+ <rect
53
+ x={b.x * VW}
54
+ y={b.y * vh}
55
+ width={Math.max(b.w * VW, 2)}
56
+ height={Math.max(b.h * vh, 2)}
57
+ class="to"
58
+ style="opacity:{0.35 + Math.min(0.6, b.value * 3)}"
59
+ >
60
+ <title>{name(b.fp)} · {b.value.toFixed(3)} at {Math.round(b.t)} ms</title>
61
+ </rect>
62
+ <line
63
+ x1={b.fx * VW + (b.w * VW) / 2}
64
+ y1={b.fy * vh + (b.h * vh) / 2}
65
+ x2={b.x * VW + (b.w * VW) / 2}
66
+ y2={b.y * vh + (b.h * vh) / 2}
67
+ class="arrow"
68
+ />
69
+ {/each}
70
+ </svg>
71
+ {#if boxes.length}
72
+ <ul class="shifts">
73
+ {#each boxes.slice(0, 8) as b, i (i)}
74
+ <li>
75
+ <b>{name(b.fp)}</b>
76
+ {b.value.toFixed(3)} <span class="hint">at {Math.round(b.t)} ms</span>
77
+ </li>
78
+ {/each}
79
+ </ul>
80
+ {:else}
81
+ <p class="hint">No layout shift in this visit.</p>
82
+ {/if}
83
+ </div>
84
+ <div class="diff">
85
+ <h3 class="sub-h">
86
+ Markup, then and now <span class="hint"
87
+ >{rec?.snapshots.length ?? 0} island{(rec?.snapshots.length ?? 0) === 1 ? '' : 's'} changed
88
+ between the server and the browser</span
89
+ >
90
+ </h3>
91
+ {#if rec?.snapshots.length}
92
+ <p class="pick">
93
+ {#each rec.snapshots as s (s.fp)}
94
+ <button class:on={snap?.fp === s.fp} onclick={() => (picked = s.fp)}
95
+ >{name(s.fp)}</button
96
+ >
97
+ {/each}
98
+ <span class="hint">· server →</span>
99
+ <button class:on={moment === 'hydrated'} onclick={() => (moment = 'hydrated')}
100
+ >hydrated</button
101
+ >
102
+ {#if snap?.final !== undefined}<button
103
+ class:on={moment === 'final'}
104
+ onclick={() => (moment = 'final')}>when the page left</button
105
+ >{/if}
106
+ </p>
107
+ {#if diff}
108
+ <p class="hint">
109
+ {diff.removed.toLocaleString()} chars gone, {diff.added.toLocaleString()} added, {Math.round(
110
+ diff.same_ratio * 100
111
+ )}% of the tokens unchanged{diff.truncated
112
+ ? ' (too large to diff token by token: shown as one removal and one addition)'
113
+ : ''}.
114
+ </p>
115
+ <pre class="d">{#each diff.ops as op, i (i)}<span class={op.kind}>{op.text}</span
116
+ >{/each}</pre>
117
+ {/if}
118
+ {:else}
119
+ <p class="hint">Every island's markup after hydration matched what the server sent.</p>
120
+ {/if}
121
+ </div>
122
+ </div>
123
+ {:else if looked}
124
+ <p class="hint">
125
+ No visit of <code>{page}</code> kept in this browser. Open the page once while logged in to the profiler;
126
+ the beacon stores the islands' markup before and after hydration and the layout shifts here.
127
+ </p>
128
+ {:else}
129
+ <p class="hint">Reading this browser's visit…</p>
130
+ {/if}
131
+
132
+ <style>
133
+ .travel {
134
+ display: grid;
135
+ grid-template-columns: 280px 1fr;
136
+ gap: 20px;
137
+ align-items: start;
138
+ }
139
+ @media (max-width: 800px) {
140
+ .travel {
141
+ grid-template-columns: 1fr;
142
+ }
143
+ }
144
+ .viewport {
145
+ display: block;
146
+ border: 1px solid var(--line);
147
+ border-radius: 4px;
148
+ background: var(--bg-sunken);
149
+ }
150
+ .vp {
151
+ fill: var(--bg-sunken);
152
+ }
153
+ .from {
154
+ fill: none;
155
+ stroke: #6b7280;
156
+ stroke-dasharray: 3 2;
157
+ }
158
+ .to {
159
+ fill: var(--bad);
160
+ stroke: var(--bad);
161
+ }
162
+ .arrow {
163
+ stroke: var(--warn);
164
+ stroke-width: 1;
165
+ }
166
+ .shifts {
167
+ margin: 6px 0 0;
168
+ padding-left: 18px;
169
+ font-size: 12px;
170
+ }
171
+ .pick button {
172
+ background: var(--bg-raised);
173
+ border: 1px solid var(--line-strong);
174
+ color: inherit;
175
+ border-radius: 4px;
176
+ padding: 2px 8px;
177
+ margin: 0 4px 4px 0;
178
+ cursor: pointer;
179
+ font: inherit;
180
+ font-size: 12px;
181
+ }
182
+ .pick button.on {
183
+ border-color: var(--c-blue);
184
+ }
185
+ .d {
186
+ max-height: 360px;
187
+ overflow: auto;
188
+ white-space: pre-wrap;
189
+ word-break: break-all;
190
+ font-size: 11px;
191
+ line-height: 1.4;
192
+ background: var(--bg-sunken);
193
+ border: 1px solid var(--line);
194
+ border-radius: 4px;
195
+ padding: 8px;
196
+ }
197
+ .d .add {
198
+ background: rgba(46, 160, 67, 0.35);
199
+ }
200
+ .d .del {
201
+ background: rgba(255, 123, 114, 0.35);
202
+ text-decoration: line-through;
203
+ }
204
+ .d .same {
205
+ color: var(--text-dim);
206
+ }
207
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { Visit } from '../visit.js';
2
+ type $$ComponentProps = {
3
+ page: string;
4
+ names?: Record<string, string>;
5
+ visit?: Visit | null;
6
+ };
7
+ declare const DomTravel: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type DomTravel = ReturnType<typeof DomTravel>;
9
+ export default DomTravel;
@@ -0,0 +1,52 @@
1
+ <script>/**
2
+ * Download a report file — the self-contained HTML (`as: 'html'`) or the agent JSON (`'json'`) —
3
+ * as a `wake:'load'` island. The report rides with the request (report-request.ts), so whichever
4
+ * instance answers can build the file: on a serverless host the one that made the report is
5
+ * usually gone, and a plain link by id 404s.
6
+ */
7
+ import { report_request } from "./report-request.js";
8
+ let { base, id, ogpB64 = "", as = "html", link = false } = $props();
9
+ let busy = $state(false);
10
+ let error = $state("");
11
+ const href = $derived(`${base}/report/${id}.${as}`);
12
+ const name = $derived(`ogygia-profile-${id}.${as}`);
13
+ function save(blob) {
14
+ const a = document.createElement("a");
15
+ a.href = URL.createObjectURL(blob);
16
+ a.download = name;
17
+ document.body.appendChild(a);
18
+ a.click();
19
+ a.remove();
20
+ setTimeout(() => URL.revokeObjectURL(a.href), 1500);
21
+ }
22
+ async function download(e) {
23
+ e.preventDefault();
24
+ if (busy) return;
25
+ busy = true;
26
+ error = "";
27
+ try {
28
+ const res = await report_request(base, id, as, ogpB64);
29
+ if (!res.ok) throw new Error((await res.text()).slice(0, 200) || String(res.status));
30
+ save(await res.blob());
31
+ } catch (err) {
32
+ error = err instanceof Error ? err.message : "download failed";
33
+ } finally {
34
+ busy = false;
35
+ }
36
+ }
37
+ </script>
38
+
39
+ {#if link}
40
+ <a {href} download={name} onclick={download} aria-busy={busy}>{busy ? 'building…' : 'JSON'}</a>
41
+ {:else}
42
+ <a
43
+ class="btn"
44
+ {href}
45
+ download={name}
46
+ onclick={download}
47
+ aria-busy={busy}
48
+ title="One HTML file with everything inlined — opens from disk, islands live. Built app only."
49
+ >{busy ? 'Building…' : 'Download'}<span class="sub">.html</span></a
50
+ >
51
+ {/if}
52
+ {#if error}<span class="hint warn" role="alert">Download failed: {error}</span>{/if}
@@ -0,0 +1,10 @@
1
+ type $$ComponentProps = {
2
+ base: string;
3
+ id: string;
4
+ ogpB64?: string;
5
+ as?: 'html' | 'json';
6
+ link?: boolean;
7
+ };
8
+ declare const DownloadHtml: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type DownloadHtml = ReturnType<typeof DownloadHtml>;
10
+ export default DownloadHtml;