@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,615 @@
1
+ <!-- (its CSS injected into the dock's shadow root: see Devtools.svelte) -->
2
+ <svelte:options css="injected" />
3
+
4
+ <script>
5
+ /**
6
+ * The per-island detail view — "inspect element" for an ogygia region. Selecting a region (a Lens
7
+ * roster row, or picking it on the page) opens this over the tab body: one focused card that pulls
8
+ * together what the separate tabs each show a slice of — the region's identity, its full cost
9
+ * breakdown (JS-with-deps / entry chunk / props / server HTML), and its client LIFECYCLE as a
10
+ * mini-timeline (connected → scheduled → woke → hydrate → done, with per-phase offsets). A pure
11
+ * consumer of the DOM + bus, keyed by the selected `<ogygia-region>` element.
12
+ */
13
+ import { parse } from 'devalue';
14
+ import { onDestroy } from 'svelte';
15
+ import { highlight, clear_highlight } from './highlight.js';
16
+ import {
17
+ region_info,
18
+ region_name,
19
+ region_transitive,
20
+ chunk_bytes,
21
+ latest_event,
22
+ region_props_sidecar
23
+ } from './regions.js';
24
+ import { snapshot } from '@ogygia/core/events';
25
+ import { read_page } from './page.js';
26
+ import { profile_for, profiles_version, island_row } from './profile-store.js';
27
+ import { get_last_session, last_session_version } from './session.js';
28
+ import PropsTree from './PropsTree.svelte';
29
+ import { island_ledger } from './ledger-dom.js';
30
+
31
+ let { el, tick = 0, onclose } = $props();
32
+
33
+ const kb = (n) => (n < 1024 ? n + ' B' : (n / 1024).toFixed(1) + ' KB');
34
+
35
+ // Every transportable crosses under ONE devalue custom type ('OgygiaRef'). Decode it to a labelled
36
+ // placeholder — NOT the live instance (`wire.resolve` has side effects) — so the viewer stays inert.
37
+ const REVIVERS = {
38
+ OgygiaRef: (d) => ({ __ogRef: true, kind: d && d.k, id: d && d.i, tag: d && d.t }),
39
+ // A seed reference (seed-refs.ts): shown as the path it points at, not resolved — inert.
40
+ OgygiaSeedRef: (p) => ({ __ogSeedRef: true, path: Array.isArray(p) ? p.join('.') : String(p) })
41
+ };
42
+ function decode_props(text) {
43
+ if (!text || text.length < 3) return null;
44
+ try {
45
+ const v = parse(text, REVIVERS);
46
+ return v && typeof v === 'object' && Object.keys(v).length ? v : null;
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ // Client lifecycle phases, in order, with the Timeline's colours.
53
+ const PHASES = [
54
+ { name: 'region.connected', label: 'connected', c: '#64748b' },
55
+ { name: 'wake.scheduled', label: 'scheduled', c: '#38bdf8' },
56
+ { name: 'wake.fired', label: 'woke', c: '#2dd4bf' },
57
+ { name: 'region.hydrate.start', label: 'hydrate', c: '#f59e0b' },
58
+ { name: 'region.hydrate.done', label: 'done', c: '#22c55e' },
59
+ { name: 'interaction.replay', label: 'replay', c: '#8b5cf6' }
60
+ ];
61
+
62
+ // its line of the exact byte ledger (a build: the page's island graph × the browser's sizes)
63
+ // the modules in this island's files that the page also loads in another file (the last profile's
64
+ // list, matched to its files by path): which copy is its, and what the second costs
65
+ const twice_here = $derived.by(() => {
66
+ profiles_version();
67
+ const urls = ledger?.row.urls ?? [];
68
+ const list = profile_for(location.pathname)?.assets?.twice ?? [];
69
+ if (!urls.length || !list.length) return [];
70
+ const paths = urls.map((u) => {
71
+ try {
72
+ return new URL(u).pathname;
73
+ } catch {
74
+ return u;
75
+ }
76
+ });
77
+ const out = [];
78
+ for (const d of list) {
79
+ const mine = d.copies.find((c) => paths.some((p) => p.endsWith(c.file)));
80
+ if (mine) out.push({ name: d.name, extra: d.extra, mine });
81
+ }
82
+ return out;
83
+ });
84
+ const ledger = $derived.by(() => {
85
+ void tick;
86
+ return island_ledger(el?.getAttribute('entry'));
87
+ });
88
+
89
+ const model = $derived.by(() => {
90
+ void tick;
91
+ const info = region_info(el);
92
+ const t = region_transitive(info.entry);
93
+ const chunk = info.entry ? chunk_bytes(info.entry) : null;
94
+ const ev = snapshot();
95
+ const rendered = info.fp
96
+ ? latest_event((e) => e.name === 'server.region.rendered' && e.fp === info.fp)
97
+ : null;
98
+ const marks = [];
99
+ for (const ph of PHASES) {
100
+ const e = ev.find((x) => x.name === ph.name && x.fp === info.fp);
101
+ if (e) marks.push({ ...ph, t: e.t });
102
+ }
103
+ marks.sort((a, b) => a.t - b.t);
104
+ const t0 = marks.length ? marks[0].t : 0;
105
+ const start = marks.find((m) => m.name === 'region.hydrate.start');
106
+ const done = marks.find((m) => m.name === 'region.hydrate.done');
107
+ const hydrateMs = start && done ? done.t - start.t : ((rendered && rendered.ms) ?? null);
108
+ // what the browser measured of this island (the Page tab's row + the findings that name it)
109
+ let browser = null;
110
+ try {
111
+ const view = info.fp ? read_page() : null;
112
+ if (view) {
113
+ const row = view.report.rows.find((r) => r.fp === info.fp) ?? null;
114
+ const findings = view.report.findings.filter((f) => f.fps.includes(info.fp));
115
+ if (row || findings.length) browser = { row, findings };
116
+ }
117
+ } catch {
118
+ browser = null;
119
+ }
120
+ // what happened to it in the last recorded session (the Record tab): clicks, slow handlers, the
121
+ // requests its clicks caused and the server's split of each
122
+ last_session_version();
123
+ const sess = get_last_session();
124
+ const in_session = info.fp && sess ? (sess.report?.by_island?.[info.fp] ?? null) : null;
125
+ // what the last SSR profile of this page measured for it (the Profiler tab's run)
126
+ profiles_version();
127
+ const prof = profile_for(location.pathname);
128
+ const server = island_row(prof, info.fp, info.entry);
129
+ return {
130
+ info,
131
+ name: region_name(info.entry),
132
+ t,
133
+ chunk,
134
+ rendered,
135
+ marks: marks.map((m) => ({ ...m, off: m.t - t0 })),
136
+ hydrateMs,
137
+ browser,
138
+ server: server ? { row: server, at: prof.at, same: server.fp === info.fp } : null,
139
+ session: in_session,
140
+ props: decode_props(region_props_sidecar(el))
141
+ };
142
+ });
143
+
144
+ // scroll to it AND mark it for a moment: an island already on screen gave no sign at all
145
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
146
+ let flash;
147
+ function locate() {
148
+ highlight([el], model.name, true);
149
+ clearTimeout(flash);
150
+ flash = setTimeout(clear_highlight, 1400);
151
+ }
152
+ onDestroy(() => clearTimeout(flash));
153
+ </script>
154
+
155
+ <div class="detail" data-og-detail>
156
+ <div class="bar">
157
+ <button class="back" onclick={onclose} title="back to the roster">‹ back</button>
158
+ <span class="dot og-{model.info.kind}"></span>
159
+ <b class="name">{model.name}</b>
160
+ <span class="k"
161
+ >{model.info.kind}{model.info.kind === 'island' ? ' · ' + model.info.wake : ''}</span
162
+ >
163
+ <button class="loc" onclick={locate}>scroll to it ›</button>
164
+ </div>
165
+
166
+ <div class="sec">lifecycle</div>
167
+ {#if model.marks.length}
168
+ <div class="life">
169
+ {#each model.marks as m, i}
170
+ {#if i > 0}<span class="arm"></span>{/if}
171
+ <div class="ph" title="{m.name} @ +{m.off.toFixed(1)}ms">
172
+ <span class="pd" style:background={m.c}></span><span class="pl">{m.label}</span>
173
+ <span class="off">+{m.off.toFixed(1)}ms</span>
174
+ </div>
175
+ {/each}
176
+ </div>
177
+ {#if model.hydrateMs != null}
178
+ <div class="hyd">hydrated in <b>{model.hydrateMs.toFixed(1)} ms</b></div>
179
+ {/if}
180
+ {:else}
181
+ <div class="muted">
182
+ {model.info.kind === 'island'
183
+ ? model.info.hydrated
184
+ ? 'hydrated (no timing captured this session)'
185
+ : 'cold — has not woken yet'
186
+ : 'no client lifecycle — pure server HTML'}
187
+ </div>
188
+ {/if}
189
+
190
+ {#if model.browser}
191
+ <div class="sec">in the browser <span class="secn">— measured on this visit</span></div>
192
+ <div class="rows" data-og-detail-browser>
193
+ {#if model.browser.row}
194
+ {@const r = model.browser.row}
195
+ <div class="row">
196
+ <span class="rk">module load</span><span class="v">{r.load_ms} ms</span>
197
+ </div>
198
+ {#if r.queue_ms !== null}<div class="row">
199
+ <span class="rk">waited for its turn</span><span class="v">{r.queue_ms} ms</span>
200
+ </div>{/if}
201
+ <div class="row">
202
+ <span class="rk">hydrate step</span><span class="v"
203
+ >{r.hydrate_ms} ms{#if r.longtask_ms}<span class="muted">
204
+ · {r.longtask_ms} ms in long tasks</span
205
+ >{/if}</span
206
+ >
207
+ </div>
208
+ <div class="row">
209
+ <span class="rk">awake at</span><span class="v"
210
+ >{Math.round(r.done)} ms<span class="muted"> after navigation</span></span
211
+ >
212
+ </div>
213
+ {#if r.shift}<div class="row">
214
+ <span class="rk">layout shift</span><span class="v">{r.shift}</span>
215
+ </div>{/if}
216
+ {/if}
217
+ {#each model.browser.findings as f, i (f.code + i)}
218
+ <div class="find {f.severity}">{f.message}</div>
219
+ {/each}
220
+ </div>
221
+ {/if}
222
+
223
+ {#if model.session}
224
+ {@const ss = model.session}
225
+ <div class="sec">
226
+ in the last session <span class="secn">— what using it did (Record tab)</span>
227
+ </div>
228
+ <div class="rows" data-og-detail-session>
229
+ <div class="row">
230
+ <span class="rk">clicks</span><span class="v"
231
+ >{ss.clicks}{#if ss.mutations}<span class="muted">
232
+ · {ss.mutations} DOM changes</span
233
+ >{/if}</span
234
+ >
235
+ </div>
236
+ {#each ss.slow as x, i (i)}
237
+ <div class="row">
238
+ <span class="rk">slow interaction</span><span class="v"
239
+ >{Math.round(x.duration)} ms<span class="muted">
240
+ · handlers {Math.round(x.processing)} ms · {x.target}</span
241
+ ></span
242
+ >
243
+ </div>
244
+ {/each}
245
+ {#each ss.requests.slice(0, 5) as q, i (q.url + i)}
246
+ <div class="row">
247
+ <span class="rk">asked the server</span>
248
+ <span class="v"
249
+ >{q.url} <b>{Math.round(q.ms)} ms</b>{#if q.status && q.status >= 400}<span
250
+ class="muted"
251
+ >
252
+ · {q.status}</span
253
+ >{/if}{#if q.server_ms !== null}<span class="muted">
254
+ · {Math.round(q.server_ms)} ms on the server{q.net_ms
255
+ ? `, ${Math.round(q.net_ms)} ms waiting`
256
+ : ''}{q.up ? ` (${q.up})` : ''}</span
257
+ >{/if}</span
258
+ >
259
+ </div>
260
+ {/each}
261
+ </div>
262
+ {/if}
263
+
264
+ {#if model.server}
265
+ {@const s = model.server.row}
266
+ <div class="sec">
267
+ on the server <span class="secn"
268
+ >— last profile, {new Date(model.server.at).toLocaleTimeString()}{model.server.same
269
+ ? ''
270
+ : ' (same island, other props)'}</span
271
+ >
272
+ </div>
273
+ <div class="rows" data-og-detail-server>
274
+ <div class="row">
275
+ <span class="rk">render</span><span class="v"
276
+ >{s.ssr_ms !== null ? s.ssr_ms + ' ms' : 'not measured'}</span
277
+ >
278
+ </div>
279
+ {#if s.props_bytes !== null}<div class="row">
280
+ <span class="rk">props</span><span class="v"
281
+ >{kb(s.props_bytes)}{#if s.culprit}<span class="muted">
282
+ · not plain JSON: {s.culprit}</span
283
+ >{/if}</span
284
+ >
285
+ </div>{/if}
286
+ {#if s.seed_refs}<div class="row">
287
+ <span class="rk">seed references</span><span class="v">{s.seed_refs}</span>
288
+ </div>{/if}
289
+ {#if s.client_p50_ms !== null}<div class="row">
290
+ <span class="rk">hydrate (beacon)</span><span class="v"
291
+ >{s.client_p50_ms} ms{#if s.recovered}<span class="muted">
292
+ · recovered {s.recovered}×</span
293
+ >{/if}</span
294
+ >
295
+ </div>{/if}
296
+ {#if s.hazard && !model.t?.hazards?.length}
297
+ <!-- a build has no dev-server walk: the profiler's reading of its sources says it -->
298
+ <div class="row" data-og-detail-hazard>
299
+ <span class="rk">browser-only</span><span class="v"
300
+ >{s.hazard.file.split('/').pop()}:{s.hazard.line}
301
+ <span class="muted"
302
+ >· {s.hazard.kind === 'await'
303
+ ? 'awaits at the top of its script'
304
+ : `reads ${s.hazard.reads ?? 'a browser-only value'} ${s.hazard.guard ? 'in a guard' : 'while rendering'}`}</span
305
+ ></span
306
+ >
307
+ </div>
308
+ {/if}
309
+ </div>
310
+ {/if}
311
+
312
+ <div class="sec">cost</div>
313
+ <div class="rows">
314
+ {#if ledger}
315
+ <!-- a build: its files, weighed by the browser — what only it needs, and whom it shares with -->
316
+ <div class="row" data-og-detail-bytes>
317
+ <span class="rk">its code</span><span class="v"
318
+ >{kb(ledger.row.wire)} in {ledger.row.files} files{#if ledger.row.cold}<span
319
+ class="muted"
320
+ >
321
+ · {ledger.row.cold} not loaded yet</span
322
+ >{/if}</span
323
+ >
324
+ </div>
325
+ <div class="row">
326
+ <span class="rk">only it</span><span class="v"
327
+ >{kb(ledger.row.unique)}<span class="muted"> · what removing it would save</span></span
328
+ >
329
+ </div>
330
+ {#if ledger.row.shared}
331
+ <div class="row">
332
+ <span class="rk">shared</span><span class="v"
333
+ >{kb(ledger.row.shared)}{#if ledger.shares_with.length}<span class="muted">
334
+ · with {ledger.shares_with.slice(0, 4).join(', ')}{ledger.shares_with.length > 4
335
+ ? ` and ${ledger.shares_with.length - 4} more`
336
+ : ''}</span
337
+ >{/if}</span
338
+ >
339
+ </div>
340
+ {/if}
341
+ {#if twice_here.length}
342
+ <!-- a module in its files that the page loads a second copy of, elsewhere (the last profile) -->
343
+ <div class="row" data-og-detail-twice>
344
+ <span class="rk">loaded twice</span><span class="v"
345
+ >{#each twice_here.slice(0, 3) as d, i (d.name)}{i ? ' · ' : ''}{d.name}
346
+ <span class="muted"
347
+ >{kb(d.extra)}{d.mine.from ? `, its copy from ${d.mine.from}` : ''}</span
348
+ >{/each}{#if twice_here.length > 3}<span class="muted">
349
+ and {twice_here.length - 3} more</span
350
+ >{/if}</span
351
+ >
352
+ </div>
353
+ {/if}
354
+ {/if}
355
+ {#if model.t}
356
+ <div class="row">
357
+ <span class="rk">js + deps</span><span class="v"
358
+ >{kb(model.t.bytes)}<span class="muted"> · {model.t.modules} mod</span></span
359
+ >
360
+ </div>
361
+ {#if model.t.top?.length}
362
+ <!-- where the island's weight is: its heaviest modules, as shares (dev code, unminified) -->
363
+ <div class="row" data-og-detail-top>
364
+ <span class="rk">heaviest</span><span class="v"
365
+ >{#each model.t.top.slice(0, 3) as m, i (m.file)}{i ? ' · ' : ''}{m.file
366
+ .split('/')
367
+ .pop()}
368
+ <span class="muted">{Math.round((m.bytes / model.t.bytes) * 100)}%</span>{/each}</span
369
+ >
370
+ </div>
371
+ {/if}
372
+ {#if model.t.barrels?.length}
373
+ <div class="row" data-og-detail-barrel>
374
+ <span class="rk">barrel</span><span class="v"
375
+ >{model.t.barrels[0].file}
376
+ <span class="muted">· {model.t.barrels[0].fanout} modules ride along</span></span
377
+ >
378
+ </div>
379
+ {/if}
380
+ {#if model.t.hazards?.length}
381
+ <!-- its lines that draw differently in the browser (the dev server read its sources): a
382
+ value only the browser has, read while rendering; an await at the top of its script -->
383
+ {@const h = model.t.hazards[0]}
384
+ <div class="row" data-og-detail-hazard>
385
+ <span class="rk">browser-only</span><span class="v"
386
+ >{h.file.split('/').pop()}:{h.line}
387
+ <span class="muted"
388
+ >· {h.kind === 'await'
389
+ ? 'awaits at the top of its script'
390
+ : `reads ${h.reads ?? 'a browser-only value'} while rendering`}{model.t.hazards
391
+ .length > 1
392
+ ? ` · ${model.t.hazards.length - 1} more`
393
+ : ''}</span
394
+ ></span
395
+ >
396
+ </div>
397
+ {/if}
398
+ {/if}
399
+ {#if model.chunk?.loaded}
400
+ <div class="row">
401
+ <span class="rk">entry chunk</span><span class="v"
402
+ >{kb(model.chunk.wire)}<span class="muted"> wire · {kb(model.chunk.raw)} raw</span></span
403
+ >
404
+ </div>
405
+ {/if}
406
+ {#if model.rendered?.propsBytes != null}
407
+ <div class="row">
408
+ <span class="rk">props payload</span><span class="v">{model.rendered.propsBytes} B</span>
409
+ </div>
410
+ {/if}
411
+ {#if model.rendered?.htmlBytes != null}
412
+ <div class="row">
413
+ <span class="rk">server HTML</span><span class="v">{model.rendered.htmlBytes} B</span>
414
+ </div>
415
+ {/if}
416
+ </div>
417
+
418
+ {#if model.props}
419
+ <div class="sec">props <span class="secn">— the data that crossed the boundary</span></div>
420
+ <div class="props" data-og-props>
421
+ {#each Object.entries(model.props) as [k, v] (k)}
422
+ <PropsTree value={v} name={k} />
423
+ {/each}
424
+ </div>
425
+ {/if}
426
+
427
+ <div class="sec">identity</div>
428
+ <div class="rows">
429
+ <div class="row">
430
+ <span class="rk">mode</span><span class="v">{model.rendered?.mode ?? model.info.kind}</span>
431
+ </div>
432
+ <div class="row">
433
+ <span class="rk">state</span><span class="v"
434
+ >{model.info.kind === 'island'
435
+ ? model.info.hydrated
436
+ ? 'hydrated'
437
+ : 'cold'
438
+ : model.info.hydrated
439
+ ? 'filled'
440
+ : 'pending'}</span
441
+ >
442
+ </div>
443
+ <div class="row">
444
+ <span class="rk">server-rendered</span><span class="v">{model.rendered ? 'yes' : 'no'}</span>
445
+ </div>
446
+ {#if model.info.fp}<div class="row">
447
+ <span class="rk">fingerprint</span><span class="v mono">{model.info.fp}</span>
448
+ </div>{/if}
449
+ {#if model.info.entry}<div class="row">
450
+ <span class="rk">entry</span><span class="v mono" title={model.info.entry}
451
+ >{model.info.entry.split('/').pop()}</span
452
+ >
453
+ </div>{/if}
454
+ </div>
455
+ </div>
456
+
457
+ <style>
458
+ .detail {
459
+ font:
460
+ 11px/1.5 ui-monospace,
461
+ SFMono-Regular,
462
+ Menlo,
463
+ monospace;
464
+ }
465
+ .bar {
466
+ display: flex;
467
+ align-items: center;
468
+ gap: 8px;
469
+ padding-bottom: 10px;
470
+ margin-bottom: 4px;
471
+ border-bottom: 1px solid rgba(148, 163, 184, 0.18);
472
+ }
473
+ .back {
474
+ padding: 3px 9px;
475
+ border-radius: 6px;
476
+ border: 1px solid rgba(148, 163, 184, 0.3);
477
+ background: #0d1526;
478
+ color: #94a3b8;
479
+ cursor: pointer;
480
+ font: inherit;
481
+ }
482
+ .back:hover {
483
+ color: #e2e8f0;
484
+ }
485
+ .dot {
486
+ width: 9px;
487
+ height: 9px;
488
+ border-radius: 50%;
489
+ flex: none;
490
+ }
491
+ .dot.og-island {
492
+ background: #14b8a6;
493
+ }
494
+ .dot.og-lake {
495
+ background: #f59e0b;
496
+ }
497
+ .dot.og-hole {
498
+ background: #8b5cf6;
499
+ }
500
+ .name {
501
+ color: #e2e8f0;
502
+ font-size: 13px;
503
+ font-weight: 700;
504
+ }
505
+ .k {
506
+ color: #94a3b8;
507
+ }
508
+ .loc {
509
+ margin-left: auto;
510
+ padding: 3px 9px;
511
+ border-radius: 6px;
512
+ border: 1px solid rgba(94, 234, 212, 0.3);
513
+ background: rgba(20, 184, 166, 0.12);
514
+ color: #5eead4;
515
+ cursor: pointer;
516
+ font: inherit;
517
+ }
518
+ .sec {
519
+ margin: 12px 0 6px;
520
+ color: #94a3b8;
521
+ font-weight: 600;
522
+ text-transform: uppercase;
523
+ font-size: 10px;
524
+ letter-spacing: 0.04em;
525
+ }
526
+ .secn {
527
+ text-transform: none;
528
+ letter-spacing: 0;
529
+ color: #94a3b8;
530
+ font-weight: 400;
531
+ }
532
+ .props {
533
+ padding: 8px 10px;
534
+ border-radius: 8px;
535
+ background: rgba(148, 163, 184, 0.06);
536
+ border: 1px solid rgba(148, 163, 184, 0.14);
537
+ max-height: 240px;
538
+ overflow: auto;
539
+ }
540
+ .life {
541
+ display: flex;
542
+ align-items: center;
543
+ flex-wrap: wrap;
544
+ gap: 4px;
545
+ }
546
+ .ph {
547
+ display: inline-flex;
548
+ align-items: center;
549
+ gap: 5px;
550
+ padding: 3px 8px;
551
+ border-radius: 999px;
552
+ background: rgba(148, 163, 184, 0.1);
553
+ }
554
+ .pd {
555
+ width: 7px;
556
+ height: 7px;
557
+ border-radius: 50%;
558
+ }
559
+ .pl {
560
+ color: #e2e8f0;
561
+ }
562
+ .off {
563
+ color: #94a3b8;
564
+ font-size: 10px;
565
+ }
566
+ .arm {
567
+ width: 10px;
568
+ height: 1px;
569
+ background: rgba(148, 163, 184, 0.4);
570
+ }
571
+ .hyd {
572
+ margin-top: 8px;
573
+ color: #94a3b8;
574
+ }
575
+ .hyd b {
576
+ color: #5eead4;
577
+ }
578
+ .rows {
579
+ display: flex;
580
+ flex-direction: column;
581
+ gap: 3px;
582
+ }
583
+ .row {
584
+ display: flex;
585
+ justify-content: space-between;
586
+ gap: 14px;
587
+ padding: 2px 0;
588
+ }
589
+ .rk {
590
+ color: #94a3b8;
591
+ }
592
+ .v {
593
+ color: #e2e8f0;
594
+ }
595
+ .muted {
596
+ color: #94a3b8;
597
+ }
598
+ .mono {
599
+ color: #cbd5e1;
600
+ }
601
+ .find {
602
+ margin-top: 4px;
603
+ padding: 4px 8px;
604
+ border-left: 3px solid #38bdf8;
605
+ background: rgba(148, 163, 184, 0.07);
606
+ border-radius: 4px;
607
+ line-height: 1.5;
608
+ }
609
+ .find.warn {
610
+ border-left-color: #f59e0b;
611
+ }
612
+ .find.error {
613
+ border-left-color: #ef4444;
614
+ }
615
+ </style>
@@ -0,0 +1,15 @@
1
+ export default IslandDetail;
2
+ type IslandDetail = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const IslandDetail: import("svelte").Component<{
7
+ el: any;
8
+ tick?: number;
9
+ onclose: any;
10
+ }, {}, "">;
11
+ type $$ComponentProps = {
12
+ el: any;
13
+ tick?: number;
14
+ onclose: any;
15
+ };