@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,11 @@
1
+ export default SessionTab;
2
+ type SessionTab = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const SessionTab: import("svelte").Component<{
7
+ tick?: number;
8
+ }, {}, "">;
9
+ type $$ComponentProps = {
10
+ tick?: number;
11
+ };
@@ -0,0 +1,267 @@
1
+ <!-- (its CSS injected into the dock's shadow root: see Devtools.svelte) -->
2
+ <svelte:options css="injected" />
3
+
4
+ <script>
5
+ /**
6
+ * The Timeline tab: CLIENT-realm events on a time axis, one lane per region (+ a page/nav lane), a
7
+ * hydrate.start→done bar per region. Server events are excluded on purpose — their clock is a
8
+ * different origin. The track width follows the window (bind:clientWidth), so it never overflows.
9
+ */
10
+ import { snapshot } from '@ogygia/core/events';
11
+ import { last_nav } from './page.js';
12
+ import { region_name, region_name_by_fp } from './regions.js';
13
+
14
+ let { tick = 0 } = $props();
15
+
16
+ const MARK = {
17
+ 'runtime.boot': { c: '#64748b', t: 'boot' },
18
+ 'region.connected': { c: '#64748b', t: 'connect' },
19
+ 'wake.scheduled': { c: '#38bdf8', t: 'sched' },
20
+ 'wake.fired': { c: '#2dd4bf', t: 'wake' },
21
+ 'region.hydrate.start': { c: '#f59e0b', t: 'hydrate' },
22
+ 'region.hydrate.done': { c: '#22c55e', t: 'done' },
23
+ 'region.hydrate.failed': { c: '#ef4444', t: 'fail' },
24
+ 'interaction.replay': { c: '#8b5cf6', t: 'replay' },
25
+ 'nav.start': { c: '#ec4899', t: 'nav' },
26
+ 'nav.finish': { c: '#ec4899', t: 'nav done' }
27
+ };
28
+ const LEGEND = [
29
+ 'wake.scheduled',
30
+ 'wake.fired',
31
+ 'region.hydrate.start',
32
+ 'region.hydrate.done',
33
+ 'interaction.replay',
34
+ 'nav.start'
35
+ ];
36
+ const LABEL_W = 118;
37
+
38
+ let wrapW = $state(560);
39
+ const trackW = $derived(Math.max(200, wrapW - LABEL_W - 12));
40
+
41
+ const model = $derived.by(() => {
42
+ void tick; // refresh with the panel tick
43
+ // THIS page: from the last in-app navigation on (the nav itself included). Every earlier page's
44
+ // islands stayed in the buffer: after a few navigations the axis spanned the whole session, one
45
+ // lane per island of every page visited, each squeezed to a dot.
46
+ // (a navigation that never swapped the page — a full load held back, one overtaken — starts nothing)
47
+ const all = snapshot().filter((e) => e.realm === 'client' && MARK[e.name]);
48
+ const nav = last_nav();
49
+ const from = nav
50
+ ? Math.max(
51
+ 0,
52
+ all.findIndex((e) => e.t >= nav.t)
53
+ )
54
+ : 0;
55
+ const events = all.slice(from);
56
+ if (events.length === 0) return { events: [], lanes: [], span: 0 };
57
+
58
+ // Group by region, keeping only the FIRST occurrence of each phase. A live region re-emits
59
+ // wake/hydrate on every tick — folding to first-per-phase keeps a 2-minute live tail from
60
+ // stretching the axis so the real initial-load hydrations collapse into a single dot.
61
+ const laneMap = new Map();
62
+ for (const e of events) {
63
+ const key = e.fp || 'page';
64
+ let lane = laneMap.get(key);
65
+ if (!lane) laneMap.set(key, (lane = { key, first: new Map(), entry: null }));
66
+ if (!lane.first.has(e.name)) lane.first.set(e.name, e);
67
+ if (e.entry && !lane.entry) lane.entry = e.entry;
68
+ }
69
+
70
+ // Axis spans only those first events — i.e. the initial hydration wave.
71
+ let t0 = Infinity;
72
+ let t1 = -Infinity;
73
+ for (const lane of laneMap.values())
74
+ for (const e of lane.first.values()) {
75
+ if (e.t < t0) t0 = e.t;
76
+ if (e.t > t1) t1 = e.t;
77
+ }
78
+ const span = Math.max(1, t1 - t0);
79
+ const x = (t) => ((t - t0) / span) * trackW;
80
+
81
+ const lanes = [];
82
+ for (const lane of laneMap.values()) {
83
+ const evs = [...lane.first.values()].sort((a, b) => a.t - b.t);
84
+ const label =
85
+ lane.key === 'page'
86
+ ? 'page / nav'
87
+ : lane.entry
88
+ ? region_name(lane.entry)
89
+ : region_name_by_fp(lane.key);
90
+ // Bar = schedule → done (or the earliest phase we saw → done). Open bar when hydrate hasn't
91
+ // finished yet, so a still-waking region reads differently from a done one.
92
+ const start =
93
+ lane.first.get('wake.scheduled') ||
94
+ lane.first.get('wake.fired') ||
95
+ lane.first.get('region.hydrate.start');
96
+ const done = lane.first.get('region.hydrate.done') || lane.first.get('region.hydrate.failed');
97
+ const bar = start
98
+ ? {
99
+ left: x(start.t),
100
+ width: done ? Math.max(3, x(done.t) - x(start.t)) : Math.max(3, trackW - x(start.t)),
101
+ open: !done,
102
+ failed: lane.first.has('region.hydrate.failed')
103
+ }
104
+ : null;
105
+ const dur = start && done ? done.t - start.t : null;
106
+ const dots = evs.map((e) => ({
107
+ left: x(e.t),
108
+ color: MARK[e.name].c,
109
+ title: `${MARK[e.name].t} · ${e.name} @ +${(e.t - t0).toFixed(1)}ms`
110
+ }));
111
+ lanes.push({ key: lane.key, label, bar, dots, dur, order: evs[0]?.t ?? 0 });
112
+ }
113
+ lanes.sort((a, b) => a.order - b.order);
114
+ return { events, lanes, span };
115
+ });
116
+ </script>
117
+
118
+ <h3>
119
+ timeline — client wake / hydrate {model.events.length ? `(${model.events.length} events)` : ''}
120
+ </h3>
121
+
122
+ {#if model.events.length === 0}
123
+ <div class="muted">no client events yet — interact with the page, then reopen.</div>
124
+ {:else}
125
+ <div class="wrap" bind:clientWidth={wrapW}>
126
+ {#each model.lanes as lane (lane.key)}
127
+ <div class="lane">
128
+ <div class="label" title={lane.key} style:width="{LABEL_W}px">{lane.label}</div>
129
+ <div class="track" style:width="{trackW}px">
130
+ {#if lane.bar}
131
+ <div
132
+ class="bar"
133
+ class:open={lane.bar.open}
134
+ class:failed={lane.bar.failed}
135
+ style:left="{lane.bar.left}px"
136
+ style:width="{lane.bar.width}px"
137
+ ></div>
138
+ {/if}
139
+ {#each lane.dots as d}<div
140
+ class="dot"
141
+ title={d.title}
142
+ style:left="{d.left}px"
143
+ style:background={d.color}
144
+ ></div>{/each}
145
+ {#if lane.dur != null}<span
146
+ class="dur"
147
+ style:left="{(lane.bar?.left ?? 0) + (lane.bar?.width ?? 0)}px"
148
+ >{lane.dur < 10 ? lane.dur.toFixed(1) : Math.round(lane.dur)} ms</span
149
+ >{/if}
150
+ </div>
151
+ </div>
152
+ {/each}
153
+ <div class="lane axis">
154
+ <div class="label" style:width="{LABEL_W}px"></div>
155
+ <div class="track" style:width="{trackW}px">
156
+ <span class="l">0 ms</span><span class="r"
157
+ >{model.span.toFixed(model.span < 100 ? 1 : 0)} ms total</span
158
+ >
159
+ </div>
160
+ </div>
161
+ </div>
162
+
163
+ <div class="legend">
164
+ {#each LEGEND as n}
165
+ <span class="li"
166
+ ><span class="dot inline" style:background={MARK[n].c}></span>{MARK[n].t}</span
167
+ >
168
+ {/each}
169
+ </div>
170
+ {/if}
171
+
172
+ <style>
173
+ h3 {
174
+ margin: 0 0 8px;
175
+ font-size: 12px;
176
+ color: #5eead4;
177
+ }
178
+ .muted {
179
+ color: #94a3b8;
180
+ }
181
+ .wrap {
182
+ width: 100%;
183
+ }
184
+ .lane {
185
+ display: flex;
186
+ align-items: center;
187
+ gap: 8px;
188
+ margin: 3px 0;
189
+ }
190
+ .label {
191
+ color: #94a3b8;
192
+ overflow: hidden;
193
+ text-overflow: ellipsis;
194
+ white-space: nowrap;
195
+ }
196
+ .track {
197
+ position: relative;
198
+ height: 18px;
199
+ border-left: 1px solid rgba(148, 163, 184, 0.25);
200
+ }
201
+ .bar {
202
+ position: absolute;
203
+ top: 7px;
204
+ height: 5px;
205
+ border-radius: 3px;
206
+ background: linear-gradient(90deg, rgba(56, 189, 248, 0.5), rgba(34, 197, 94, 0.6));
207
+ }
208
+ .bar.open {
209
+ background: repeating-linear-gradient(
210
+ 90deg,
211
+ rgba(245, 158, 11, 0.5),
212
+ rgba(245, 158, 11, 0.5) 4px,
213
+ transparent 4px,
214
+ transparent 8px
215
+ );
216
+ }
217
+ .bar.failed {
218
+ background: rgba(239, 68, 68, 0.6);
219
+ }
220
+ .dur {
221
+ position: absolute;
222
+ top: 3px;
223
+ margin-left: 5px;
224
+ font-size: 10px;
225
+ color: #94a3b8;
226
+ white-space: nowrap;
227
+ }
228
+ .dot {
229
+ position: absolute;
230
+ top: 5px;
231
+ width: 9px;
232
+ height: 9px;
233
+ margin-left: -4px;
234
+ border-radius: 50%;
235
+ border: 1px solid #0b1220;
236
+ }
237
+ .axis .track {
238
+ height: 14px;
239
+ border-left: none;
240
+ }
241
+ .axis .l {
242
+ position: absolute;
243
+ left: 0;
244
+ color: #94a3b8;
245
+ }
246
+ .axis .r {
247
+ position: absolute;
248
+ right: 0;
249
+ color: #94a3b8;
250
+ }
251
+ .legend {
252
+ margin-top: 10px;
253
+ display: flex;
254
+ flex-wrap: wrap;
255
+ gap: 10px;
256
+ }
257
+ .li {
258
+ color: #94a3b8;
259
+ }
260
+ .dot.inline {
261
+ position: static;
262
+ display: inline-block;
263
+ margin: 0 5px 0 0;
264
+ vertical-align: 0;
265
+ border: none;
266
+ }
267
+ </style>
@@ -0,0 +1,11 @@
1
+ export default TimelineTab;
2
+ type TimelineTab = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const TimelineTab: import("svelte").Component<{
7
+ tick?: number;
8
+ }, {}, "">;
9
+ type $$ComponentProps = {
10
+ tick?: number;
11
+ };
@@ -0,0 +1,149 @@
1
+ <!-- (its CSS injected into the dock's shadow root: see Devtools.svelte) -->
2
+ <svelte:options css="injected" />
3
+
4
+ <script>
5
+ /**
6
+ * The Wire tab (wire inspector, internal/notes/devtools.md, Rung 5 · 2): everything the SERVER
7
+ * shipped to this page across the island boundary — each island's devalue PROPS payload, the
8
+ * document seeds ($app/state page + remote query cache), and the signed capabilities minted for
9
+ * deferred holes. The data-crossing counterpart to the Bytes tab (which is code). Reads the
10
+ * server-realm events the handle folded into the stream via the `application/ogygia-devtools`
11
+ * side-channel; nothing here needs a socket — it's the same in-page bus.
12
+ */
13
+ import { snapshot } from '@ogygia/core/events';
14
+ import { short_chunk, kb, region_name } from './regions.js';
15
+
16
+ let { tick = 0 } = $props();
17
+
18
+ const model = $derived.by(() => {
19
+ void tick; // refresh with the panel tick
20
+ const ev = snapshot();
21
+ const renders = ev.filter((e) => e.name === 'server.region.rendered');
22
+ const seeds = ev.filter((e) => e.name === 'server.seed.injected');
23
+ const caps = ev.filter((e) => e.name === 'server.capability.minted');
24
+ const propsBytes = renders.reduce((s, r) => s + (r.propsBytes || 0), 0);
25
+ const seedBytes = seeds.reduce((s, r) => s + (r.bytes || 0), 0);
26
+ return { renders, seeds, caps, propsBytes, seedBytes, total: propsBytes + seedBytes };
27
+ });
28
+ </script>
29
+
30
+ <h3>wire inspector — what the server shipped to this page</h3>
31
+
32
+ {#if model.renders.length === 0 && model.seeds.length === 0 && model.caps.length === 0}
33
+ <div class="muted">
34
+ no server crossings recorded — needs a devtools build (the handle's side-channel feeds this).
35
+ </div>
36
+ {:else}
37
+ <div class="sec">islands · props payload</div>
38
+ <table>
39
+ <thead><tr><th>island</th><th>mode</th><th>fp</th><th>props</th></tr></thead>
40
+ <tbody>
41
+ {#each model.renders as r (r.seq)}
42
+ <tr>
43
+ <td title={r.entry || ''}>
44
+ {#if r.entry}
45
+ <span class="nm">{region_name(r.entry)}</span>
46
+ <span class="id">{short_chunk(r.entry)}</span>
47
+ {:else}—{/if}
48
+ </td>
49
+ <td>{r.mode}</td>
50
+ <td>{r.fp || '—'}</td>
51
+ <td>{r.propsBytes != null ? r.propsBytes + ' B' : '—'}</td>
52
+ </tr>
53
+ {:else}
54
+ <tr><td colspan="4" class="muted">none</td></tr>
55
+ {/each}
56
+ </tbody>
57
+ </table>
58
+
59
+ <div class="sec">document seeds</div>
60
+ <table>
61
+ <thead><tr><th>seed</th><th>bytes</th></tr></thead>
62
+ <tbody>
63
+ {#each model.seeds as s (s.seq)}
64
+ <tr><td>{s.kind}</td><td>{s.bytes} B</td></tr>
65
+ {:else}
66
+ <tr><td colspan="2" class="muted">none</td></tr>
67
+ {/each}
68
+ </tbody>
69
+ </table>
70
+
71
+ {#if model.caps.length}
72
+ <div class="sec">capabilities minted (deferred holes)</div>
73
+ <table>
74
+ <thead><tr><th>id</th><th>ttl</th></tr></thead>
75
+ <tbody>
76
+ {#each model.caps as c (c.seq)}
77
+ <tr
78
+ ><td title={c.id}>{short_chunk(c.id) || c.id}</td><td
79
+ >{c.ttl ? c.ttl + ' s' : 'no-store'}</td
80
+ ></tr
81
+ >
82
+ {/each}
83
+ </tbody>
84
+ </table>
85
+ {/if}
86
+
87
+ <div class="total">
88
+ <span>data across the wire</span>
89
+ <span
90
+ >{kb(model.total)}
91
+ <span class="muted">({model.propsBytes} B props + {model.seedBytes} B seeds)</span></span
92
+ >
93
+ </div>
94
+ {/if}
95
+
96
+ <style>
97
+ h3 {
98
+ margin: 0 0 8px;
99
+ font-size: 12px;
100
+ color: #5eead4;
101
+ }
102
+ .muted {
103
+ color: #94a3b8;
104
+ }
105
+ .sec {
106
+ margin: 12px 0 4px;
107
+ color: #94a3b8;
108
+ font-weight: 600;
109
+ }
110
+ .sec:first-of-type {
111
+ margin-top: 0;
112
+ }
113
+ table {
114
+ border-collapse: collapse;
115
+ width: 100%;
116
+ }
117
+ th,
118
+ td {
119
+ text-align: right;
120
+ padding: 2px 8px;
121
+ white-space: nowrap;
122
+ }
123
+ th:first-child,
124
+ td:first-child {
125
+ text-align: left;
126
+ }
127
+ thead th {
128
+ color: #94a3b8;
129
+ border-bottom: 1px solid rgba(148, 163, 184, 0.2);
130
+ }
131
+ .total {
132
+ display: flex;
133
+ justify-content: space-between;
134
+ margin-top: 12px;
135
+ padding-top: 6px;
136
+ border-top: 1px solid rgba(148, 163, 184, 0.2);
137
+ color: #5eead4;
138
+ font-weight: 600;
139
+ }
140
+ .nm {
141
+ color: #e2e8f0;
142
+ font-weight: 600;
143
+ }
144
+ .id {
145
+ margin-left: 7px;
146
+ color: #94a3b8;
147
+ font-size: 10px;
148
+ }
149
+ </style>
@@ -0,0 +1,11 @@
1
+ export default WireTab;
2
+ type WireTab = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const WireTab: import("svelte").Component<{
7
+ tick?: number;
8
+ }, {}, "">;
9
+ type $$ComponentProps = {
10
+ tick?: number;
11
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * THE BYTE LEDGER, EXACT — each island's JavaScript from the page's own island graph (the chunk list
3
+ * a build writes for every island entry, `script[data-ogygia-graph]`) and the browser's byte counts
4
+ * per file (resource timing). Pure: the graph, the sizes and the islands in; out:
5
+ *
6
+ * - per island: its files, what they weigh, the part ONLY it needs (`unique`: what removing the
7
+ * island would save) and the part it shares with other islands;
8
+ * - the page: every file counted once (not once per island that imports it);
9
+ * - the shared files, heaviest first, with the islands that use each.
10
+ *
11
+ * The runtime's own files are shared by every island and kept apart. A file the browser has not
12
+ * loaded yet (an island still asleep) has no size: it is counted as cold, not as zero.
13
+ */
14
+ export interface LedgerIsland {
15
+ /** absolute entry URL (its identity: the graph's key) */
16
+ entry: string;
17
+ /** the file it loads, absolute (its content-hashed location); absent → the entry itself */
18
+ file?: string;
19
+ name: string;
20
+ kind: string;
21
+ wake: string;
22
+ /** copies of it on the page */
23
+ count: number;
24
+ }
25
+ export interface FileSize {
26
+ /** bytes over the wire (compressed) */
27
+ wire: number;
28
+ /** decoded bytes */
29
+ raw: number;
30
+ }
31
+ export interface LedgerRow {
32
+ entry: string;
33
+ name: string;
34
+ kind: string;
35
+ wake: string;
36
+ count: number;
37
+ /** files its code needs (the entry and its chunks; not the runtime's) */
38
+ files: number;
39
+ /** the loaded ones' wire bytes */
40
+ wire: number;
41
+ /** only this island needs these (what removing it would save) */
42
+ unique: number;
43
+ /** shared with at least one other island on the page */
44
+ shared: number;
45
+ /** files not loaded yet (it is asleep): no size known */
46
+ cold: number;
47
+ /** the files themselves (their URLs): an island's card matches them against the modules loaded twice */
48
+ urls?: string[];
49
+ }
50
+ export interface Ledger {
51
+ rows: LedgerRow[];
52
+ /** every file once: the islands' and the runtime's */
53
+ page: {
54
+ files: number;
55
+ wire: number;
56
+ raw: number;
57
+ cold: number;
58
+ };
59
+ runtime: {
60
+ files: number;
61
+ wire: number;
62
+ };
63
+ /** shared files, heaviest first, with who uses them */
64
+ shared: {
65
+ url: string;
66
+ wire: number;
67
+ users: string[];
68
+ }[];
69
+ }
70
+ export declare function byte_ledger(islands: readonly LedgerIsland[], graph: ReadonlyMap<string, readonly string[]>, size: (url: string) => FileSize | null, runtime: readonly string[]): Ledger;
@@ -0,0 +1,82 @@
1
+ // src/devtools/bytes.ts
2
+ function byte_ledger(islands, graph, size, runtime) {
3
+ const runtime_set = new Set(runtime);
4
+ const files_of = /* @__PURE__ */ new Map();
5
+ const users = /* @__PURE__ */ new Map();
6
+ for (const i of islands) {
7
+ if (files_of.has(i.entry)) continue;
8
+ const list = [.../* @__PURE__ */ new Set([i.file ?? i.entry, ...graph.get(i.entry) ?? []])].filter((u) => !runtime_set.has(u));
9
+ files_of.set(i.entry, list);
10
+ for (const u of list) (users.get(u) ?? users.set(u, /* @__PURE__ */ new Set()).get(u)).add(i.entry);
11
+ }
12
+ const name_of = new Map(islands.map((i) => [i.entry, i.name]));
13
+ const rows = [];
14
+ const seen = /* @__PURE__ */ new Set();
15
+ for (const i of islands) {
16
+ if (seen.has(i.entry)) continue;
17
+ seen.add(i.entry);
18
+ const list = files_of.get(i.entry);
19
+ let wire = 0;
20
+ let unique = 0;
21
+ let shared = 0;
22
+ let cold = 0;
23
+ for (const u of list) {
24
+ const s = size(u);
25
+ if (!s) {
26
+ cold++;
27
+ continue;
28
+ }
29
+ wire += s.wire;
30
+ if ((users.get(u)?.size ?? 0) > 1) shared += s.wire;
31
+ else unique += s.wire;
32
+ }
33
+ rows.push({
34
+ entry: i.entry,
35
+ name: i.name,
36
+ kind: i.kind,
37
+ wake: i.wake,
38
+ count: i.count,
39
+ files: list.length,
40
+ wire,
41
+ unique,
42
+ shared,
43
+ cold,
44
+ urls: list
45
+ });
46
+ }
47
+ rows.sort((a, b) => b.unique - a.unique || b.wire - a.wire);
48
+ let page_wire = 0;
49
+ let page_raw = 0;
50
+ let page_cold = 0;
51
+ const all = /* @__PURE__ */ new Set([...users.keys(), ...runtime]);
52
+ for (const u of all) {
53
+ const s = size(u);
54
+ if (!s) page_cold++;
55
+ else {
56
+ page_wire += s.wire;
57
+ page_raw += s.raw;
58
+ }
59
+ }
60
+ let runtime_wire = 0;
61
+ for (const u of runtime) runtime_wire += size(u)?.wire ?? 0;
62
+ const shared = [...users].filter(([, who]) => who.size > 1).map(([url, who]) => ({
63
+ url,
64
+ wire: size(url)?.wire ?? 0,
65
+ users: [...who].map((e) => name_of.get(e) ?? e)
66
+ })).filter((s) => s.wire > 0).sort((a, b) => b.wire - a.wire);
67
+ return {
68
+ rows,
69
+ page: {
70
+ files: all.size,
71
+ wire: page_wire,
72
+ raw: page_raw,
73
+ cold: page_cold
74
+ },
75
+ runtime: {
76
+ files: runtime.length,
77
+ wire: runtime_wire
78
+ },
79
+ shared
80
+ };
81
+ }
82
+ export { byte_ledger };
@@ -0,0 +1,20 @@
1
+ import { StylesFinding } from "./styles.js";
2
+ export interface CacheProbe {
3
+ url: string;
4
+ /** the island's name, or "the runtime" */
5
+ name: string;
6
+ cache_control: string | null;
7
+ /** decoded bytes, when the browser loaded the file */
8
+ bytes: number;
9
+ }
10
+ /** Read one `cache-control`: whether it caches the file long enough to count. Split, not a regex. */
11
+ export declare function cached_long(cache_control: string | null): boolean;
12
+ /** Every content-named file the page loads (the islands' locations, the runtime's), each once. */
13
+ export declare function content_named_files(doc?: Document): {
14
+ url: string;
15
+ name: string;
16
+ }[];
17
+ /** Ask the server how it caches each file (HEAD, past the browser's cache), at most `limit` files. */
18
+ export declare function probe_cache(doc?: Document, limit?: number): Promise<CacheProbe[]>;
19
+ /** The finding: content-named files the host does not cache long, named, with the bytes at stake. */
20
+ export declare function cache_findings(probes: readonly CacheProbe[]): StylesFinding[];