@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,14 @@
1
+ /**
2
+ * Reactive column sort shared by the profiler's tables (was report.ts's TABLE_SORT_JS DOM hack).
3
+ * Give it a getter for the rows and the initial numeric key; it owns the sort state and returns the
4
+ * sorted view + a click handler. Svelte runes in a `.svelte.ts` module — imported by the table
5
+ * islands, one instance each.
6
+ */
7
+ export interface Sortable<T> {
8
+ readonly key: string;
9
+ readonly sorted: T[];
10
+ click(k: string): void;
11
+ arrow(k: string): string;
12
+ aria(k: string): "ascending" | "descending" | "none";
13
+ }
14
+ export declare function sortable<T>(rows: () => T[], initial_key: string): Sortable<T>;
@@ -0,0 +1,37 @@
1
+ // src/profiler/ui/sort.svelte.ts
2
+ function sortable(rows, initial_key) {
3
+ let key = $state(initial_key);
4
+ let dir = $state("desc");
5
+ const sorted = $derived([...rows()].sort((a, b) => {
6
+ const x = Number(a[key]) || 0;
7
+ const y = Number(b[key]) || 0;
8
+ return dir === "asc" ? x - y : y - x;
9
+ }));
10
+ function click(k) {
11
+ if (key === k) dir = dir === "asc" ? "desc" : "asc";
12
+ else {
13
+ key = k;
14
+ dir = "desc";
15
+ }
16
+ }
17
+ /** The little arrow for a header: ▲/▼ when it's the active column, else empty. */
18
+ function arrow(k) {
19
+ return key === k ? dir === "asc" ? " ▲" : " ▼" : "";
20
+ }
21
+ /** The header's `aria-sort`: what a screen reader says the arrow means. */
22
+ function aria(k) {
23
+ return key === k ? dir === "asc" ? "ascending" : "descending" : "none";
24
+ }
25
+ return {
26
+ get key() {
27
+ return key;
28
+ },
29
+ get sorted() {
30
+ return sorted;
31
+ },
32
+ click,
33
+ arrow,
34
+ aria
35
+ };
36
+ }
37
+ export { sortable };
@@ -0,0 +1,45 @@
1
+ export interface StoredDump {
2
+ id: string;
3
+ page: string | null;
4
+ at: number;
5
+ label: string;
6
+ trigger: string;
7
+ /** the report's median render, ms (page mode) */
8
+ median: number | null;
9
+ /** the raw dump (`{ kind: 'ogygia-profiler-dump', meta, analysis, extras }`) */
10
+ dump: unknown;
11
+ }
12
+ export interface StoredVisit {
13
+ key: string;
14
+ page: string;
15
+ at: number;
16
+ visit: unknown;
17
+ snapshots: {
18
+ fp: string;
19
+ ssr: string;
20
+ hydrated: string;
21
+ final?: string;
22
+ from?: number;
23
+ }[];
24
+ }
25
+ /** Keep a report's dump. The newest MAX_REPORTS stay. */
26
+ export declare function put_report(dump: {
27
+ meta: {
28
+ id: string;
29
+ page?: string;
30
+ created: number;
31
+ trigger: string;
32
+ runs?: number[];
33
+ request?: {
34
+ path: string;
35
+ };
36
+ };
37
+ }): Promise<boolean>;
38
+ export declare function get_report(id: string): Promise<StoredDump | null>;
39
+ /** Every kept report, newest first, without the dumps (the list is cheap to draw). */
40
+ export declare function list_reports(): Promise<Omit<StoredDump, "dump">[]>;
41
+ export declare function delete_report(id: string): Promise<void>;
42
+ /** The visits of a page this browser made, newest first. */
43
+ export declare function list_visits(page: string, limit?: number): Promise<StoredVisit[]>;
44
+ export declare function latest_visit(page: string): Promise<StoredVisit | null>;
45
+ export declare function clear_all(): Promise<void>;
@@ -0,0 +1,92 @@
1
+ // src/profiler/ui/store.ts
2
+ /**
3
+ * THE BROWSER STORE — the profiler's memory on an ephemeral host. Reports (their dumps) and the
4
+ * beacon's visits live in this browser's IndexedDB, on the app's origin: a report page that the
5
+ * server no longer has renders from here; the compare page and the history line work across
6
+ * instances; the one-clock timeline joins a report to the visit this same browser made. The
7
+ * runtime's beacon writes visits into the same database (runtime/beacon.ts — one schema, two
8
+ * writers). Everything is best-effort: no IndexedDB, a private window, a full quota → empty.
9
+ */
10
+ const DB_NAME = "ogygia-profiler";
11
+ const DB_VERSION = 1;
12
+ const MAX_REPORTS = 40;
13
+ function open_db() {
14
+ return new Promise((resolve) => {
15
+ try {
16
+ if (typeof indexedDB === "undefined") return resolve(null);
17
+ const req = indexedDB.open(DB_NAME, DB_VERSION);
18
+ req.onupgradeneeded = () => {
19
+ const db = req.result;
20
+ if (!db.objectStoreNames.contains("reports")) db.createObjectStore("reports", { keyPath: "id" });
21
+ if (!db.objectStoreNames.contains("visits")) db.createObjectStore("visits", { keyPath: "key" });
22
+ };
23
+ req.onsuccess = () => resolve(req.result);
24
+ req.onerror = () => resolve(null);
25
+ req.onblocked = () => resolve(null);
26
+ } catch {
27
+ resolve(null);
28
+ }
29
+ });
30
+ }
31
+ function done(req) {
32
+ return new Promise((resolve) => {
33
+ req.onsuccess = () => resolve(req.result);
34
+ req.onerror = () => resolve(null);
35
+ });
36
+ }
37
+ async function with_store(name, mode, fn) {
38
+ const db = await open_db();
39
+ if (!db) return null;
40
+ try {
41
+ const out = await fn(db.transaction(name, mode).objectStore(name));
42
+ db.close();
43
+ return out;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+ /** Keep a report's dump. The newest MAX_REPORTS stay. */
49
+ async function put_report(dump) {
50
+ const m = dump.meta;
51
+ const median = m.runs?.length ? [...m.runs].sort((a, b) => a - b)[Math.floor(m.runs.length / 2)] : null;
52
+ const rec = {
53
+ id: m.id,
54
+ page: m.page ?? m.request?.path ?? null,
55
+ at: m.created,
56
+ label: m.page ?? m.request?.path ?? m.trigger,
57
+ trigger: m.trigger,
58
+ median,
59
+ dump
60
+ };
61
+ return await with_store("reports", "readwrite", async (s) => {
62
+ s.put(rec);
63
+ const keys = await done(s.getAll()) ?? [];
64
+ for (const old of keys.sort((a, b) => b.at - a.at).slice(MAX_REPORTS)) s.delete(old.id);
65
+ return true;
66
+ }) === true;
67
+ }
68
+ async function get_report(id) {
69
+ return await with_store("reports", "readonly", (s) => done(s.get(id))) ?? null;
70
+ }
71
+ /** Every kept report, newest first, without the dumps (the list is cheap to draw). */
72
+ async function list_reports() {
73
+ return (await with_store("reports", "readonly", (s) => done(s.getAll())) ?? []).sort((a, b) => b.at - a.at).map(({ dump: _d, ...rest }) => rest);
74
+ }
75
+ async function delete_report(id) {
76
+ await with_store("reports", "readwrite", async (s) => {
77
+ s.delete(id);
78
+ return true;
79
+ });
80
+ }
81
+ /** The visits of a page this browser made, newest first. */
82
+ async function list_visits(page, limit = 10) {
83
+ return (await with_store("visits", "readonly", (s) => done(s.getAll())) ?? []).filter((v) => v.page === page).sort((a, b) => b.at - a.at).slice(0, limit);
84
+ }
85
+ async function latest_visit(page) {
86
+ return (await list_visits(page, 1))[0] ?? null;
87
+ }
88
+ async function clear_all() {
89
+ await with_store("reports", "readwrite", async (s) => (s.clear(), true));
90
+ await with_store("visits", "readwrite", async (s) => (s.clear(), true));
91
+ }
92
+ export { clear_all, delete_report, get_report, latest_visit, list_reports, list_visits, put_report };
@@ -0,0 +1,389 @@
1
+ import { NavPhases, PageInteraction, PageNav } from "@ogygia/core/insights";
2
+ import { RegionRefusal } from "@ogygia/core/internal/instruments";
3
+ export interface VisitNav {
4
+ /** ms from navigation start */
5
+ req_start: number;
6
+ res_start: number;
7
+ res_end: number;
8
+ /** the browser gave the request and the first byte as 0 (Safari, a cross-origin-isolated page):
9
+ * `req_start` and `res_start` are then the HTML's end, standing in — no first byte to read */
10
+ start_hidden?: true;
11
+ dom_interactive?: number;
12
+ dcl?: number;
13
+ load?: number;
14
+ /** the document's bytes on the wire and decoded */
15
+ transfer?: number;
16
+ size?: number;
17
+ /** the document came down as large as it is: sent without gzip or brotli */
18
+ raw?: true;
19
+ protocol?: string;
20
+ /** the steps before the first byte (ms, each only when it took time) */
21
+ phases?: NavPhases;
22
+ /** the document's own Server-Timing entries */
23
+ server_timing?: {
24
+ name: string;
25
+ ms: number;
26
+ desc?: string;
27
+ }[];
28
+ }
29
+ export interface VisitResource {
30
+ url: string;
31
+ /** css | script | font | img | fetch | other */
32
+ type: string;
33
+ start: number;
34
+ end: number;
35
+ req_start?: number;
36
+ res_start?: number;
37
+ transfer?: number;
38
+ size?: number;
39
+ blocking?: boolean;
40
+ /** the size is not the browser's: it hid another origin's, and the report weighed the file (never
41
+ * sent by a beacon — the report sets it) */
42
+ weighed?: true;
43
+ }
44
+ export interface VisitIsland {
45
+ fp: string;
46
+ entry?: string;
47
+ /** wake began */
48
+ t0: number;
49
+ /** modules in hand */
50
+ loaded: number;
51
+ /** its scheduler turn came (after the module; islands hydrate one per task) */
52
+ turn?: number;
53
+ /** `data-hydrated` set */
54
+ done: number;
55
+ /** its effects had run (a microtask after `hydrate()`): the end of its own work, when later */
56
+ fx?: number;
57
+ recovered?: boolean;
58
+ /** the island's markup changed between the server and the hydrated DOM */
59
+ changed?: boolean;
60
+ /** another script edited it before it woke; the runtime put the server markup back */
61
+ healed?: boolean;
62
+ ssr_bytes?: number;
63
+ /** a recovered island whose arrived markup had Svelte's block markers unpaired [openers, closers]:
64
+ * rewritten on the way */
65
+ markers?: [number, number];
66
+ }
67
+ export interface VisitRegion {
68
+ fp: string;
69
+ entry?: string;
70
+ /** its `wake` attribute (none on a default, wake-at-load island) */
71
+ wake?: string;
72
+ /** a deferred hole */
73
+ defer?: boolean;
74
+ hydrated?: boolean;
75
+ /** it threw while it loaded or mounted: the error */
76
+ failed?: string;
77
+ /** a failed island's wake → its failure (page clock) */
78
+ failed_span?: [number, number];
79
+ /** when the failed island's hydrate step began (page clock): from here to the failure it held the thread */
80
+ failed_turn?: number;
81
+ /** document px */
82
+ top: number;
83
+ height: number;
84
+ /** it drew nothing on the visitor's screen: no box of its own nor a child's */
85
+ hidden?: true;
86
+ /** …and never draws: no text, only `hidden` marker elements (it is there for its effects) */
87
+ headless?: true;
88
+ }
89
+ export interface VisitShift {
90
+ t: number;
91
+ value: number;
92
+ /** the island whose node moved, when one did */
93
+ fp?: string;
94
+ /** the element that moved, told briefly */
95
+ tag?: string;
96
+ /** [x, y, w, h] before and after, in viewport px */
97
+ from?: [number, number, number, number];
98
+ to?: [number, number, number, number];
99
+ }
100
+ export interface Visit {
101
+ page: string;
102
+ /** epoch ms of navigation start */
103
+ at: number;
104
+ nav: VisitNav;
105
+ /** (`lcp_replaced`: its element was taken out while its island stayed — rendered again) */
106
+ paints: {
107
+ fcp?: number;
108
+ lcp?: number;
109
+ lcp_fp?: string;
110
+ lcp_url?: string;
111
+ lcp_tag?: string;
112
+ lcp_replaced?: true;
113
+ lcp_lazy?: true;
114
+ lcp_priority?: "high" | "low";
115
+ held?: {
116
+ outcome: "arrived" | "input" | "cap" | "none";
117
+ resources: number;
118
+ waited_ms: number;
119
+ };
120
+ };
121
+ resources: VisitResource[];
122
+ /** every file by type, when `resources` lists only some (the first 200, the blocking ones first) */
123
+ resource_totals?: {
124
+ type: string;
125
+ count: number;
126
+ transfer: number;
127
+ size: number;
128
+ }[];
129
+ /** how many files the page loaded in all */
130
+ resources_all?: number;
131
+ longtasks: {
132
+ t: number;
133
+ ms: number;
134
+ }[];
135
+ /** `frames`: the browser has no long-task timing — the long tasks are frames drawn 50 ms+ late */
136
+ longtasks_from?: "frames";
137
+ /** `boxes`: the browser has no layout-shift timing — the shifts are islands' boxes changing as they woke */
138
+ shifts_from?: "boxes";
139
+ islands: VisitIsland[];
140
+ /** the first interaction inside an island: fingerprint, when, what kind */
141
+ firsts: {
142
+ fp: string;
143
+ t: number;
144
+ type: string;
145
+ }[];
146
+ shifts: VisitShift[];
147
+ marks?: {
148
+ name: string;
149
+ t0?: number;
150
+ ms: number;
151
+ }[];
152
+ /** every region on the page as the visit ended (where it sits, how it wakes, whether it woke) */
153
+ regions?: VisitRegion[];
154
+ /** the vitals as of this message */
155
+ vitals?: {
156
+ ttfb?: number;
157
+ fcp?: number;
158
+ lcp?: number;
159
+ cls?: number;
160
+ inp?: number;
161
+ };
162
+ /** main-thread ms per script URL (long animation frames, from the page's start) */
163
+ scripts?: {
164
+ url: string;
165
+ ms: number;
166
+ count: number;
167
+ }[];
168
+ /** the page's origin as the browser had it */
169
+ origin?: string;
170
+ /** entry types the visiting browser cannot observe (Safari: `layout-shift`, `longtask`): what
171
+ * they would measure is unknown, not zero */
172
+ unsupported?: string[];
173
+ /** Svelte's hydration warnings (dev) */
174
+ warnings?: {
175
+ code: string;
176
+ message: string;
177
+ file?: string;
178
+ fp?: string;
179
+ }[];
180
+ /** holes whose answer never came, by island id (the report's hole rows name them) */
181
+ holes_failed?: {
182
+ id: string;
183
+ reason: RegionRefusal | "error";
184
+ final_path?: string;
185
+ message?: string;
186
+ attempts: number;
187
+ }[];
188
+ /** files a preload fetched and something else downloaded again: the preload went unused */
189
+ /** (`vary`: a font's Vary header, from the report's own weighing — never sent by a beacon) */
190
+ /** `hole`: the URL is a hole the runtime asked for (its preload is ogygia's own) */
191
+ preload_misses?: {
192
+ url: string;
193
+ type: string;
194
+ bytes: number;
195
+ as: string;
196
+ crossorigin: string | null;
197
+ vary?: string;
198
+ hole?: true;
199
+ }[];
200
+ /** the `@font-face` rules behind the fonts it fetched: family, font-display, the fetched files */
201
+ font_faces?: {
202
+ family: string;
203
+ display: string;
204
+ urls: string[];
205
+ }[];
206
+ /** images whose pixels are 4× or more what their box shows (the screen counted), files ≥ 50 KB */
207
+ images_oversized?: {
208
+ url: string;
209
+ natural: [number, number];
210
+ shown: [number, number];
211
+ dpr: number;
212
+ bytes: number;
213
+ fp?: string;
214
+ }[];
215
+ /** preloads (image, font, stylesheet) nothing on the page used 3 s after load */
216
+ preloads_unused?: {
217
+ url: string;
218
+ as: string;
219
+ bytes: number;
220
+ }[];
221
+ /** text files of 4 KB or more sent without gzip or brotli, the largest first */
222
+ uncompressed?: {
223
+ url: string;
224
+ type: string;
225
+ bytes: number;
226
+ }[];
227
+ /** images more than a screen and a half down, not lazy, 20 KB or more, fetched before load ended */
228
+ images_eager_below?: {
229
+ url: string;
230
+ top: number;
231
+ bytes: number;
232
+ fp?: string;
233
+ }[];
234
+ /** shown images the browser could hold no room for (computed aspect-ratio `auto`); empty: none */
235
+ images_unsized?: string[];
236
+ /** long frames (50 ms or more) that began while the page scrolled, with their two biggest scripts */
237
+ scroll_jank?: {
238
+ start: number;
239
+ ms: number;
240
+ scripts: {
241
+ url: string;
242
+ fn: string;
243
+ invoker: string;
244
+ ms: number;
245
+ }[];
246
+ }[];
247
+ /** scripts that forced style and layout (5 ms or more): their window, the forced ms, file, function */
248
+ forced_layout?: {
249
+ start: number;
250
+ end: number;
251
+ ms: number;
252
+ url: string;
253
+ fn: string;
254
+ }[];
255
+ /** the page's size in elements, when 1,500 or more (depth 0: past 60,000, only counted) */
256
+ dom?: {
257
+ nodes: number;
258
+ depth: number;
259
+ deepest: string;
260
+ widest: {
261
+ at: string;
262
+ children: number;
263
+ };
264
+ islands: {
265
+ fp: string;
266
+ nodes: number;
267
+ }[];
268
+ };
269
+ /** islands whose own file failed to load and fell back to their stable name */
270
+ entry_fallbacks?: {
271
+ entry: string;
272
+ src: string;
273
+ recovered: boolean;
274
+ }[];
275
+ /** content-named files (`/immutable/`) the browser fetched again: revalidated (a 304), or
276
+ * downloaded in full on a reload. `entry`: the island whose file it is; `runtime`: the runtime's */
277
+ refetched?: {
278
+ url: string;
279
+ how: "revalidated" | "downloaded";
280
+ bytes: number;
281
+ ms: number;
282
+ entry?: string;
283
+ hole?: string;
284
+ runtime?: boolean;
285
+ }[];
286
+ /** the slowest interaction (INP's), by phase, with the scripts of the long frames around it */
287
+ interaction?: PageInteraction;
288
+ /** the in-app navigations (router body swaps): start, page fetched, styles in, swap committed */
289
+ navs?: PageNav[];
290
+ /** holes whose first answer came: fetch start and swap (page time), and whether below the fold */
291
+ holes_answered?: {
292
+ id: string;
293
+ n: number;
294
+ start: number;
295
+ t: number;
296
+ below_fold: boolean;
297
+ left?: number;
298
+ first?: number;
299
+ end?: number;
300
+ queue?: number;
301
+ render?: number;
302
+ batch?: number;
303
+ p?: string;
304
+ }[];
305
+ /** batch requests that carried fewer holes than they were sent for (each then fetched alone) */
306
+ hole_batches?: {
307
+ sent: number;
308
+ delivered: number;
309
+ status: number;
310
+ refused?: "redirected" | "document";
311
+ final_url?: string;
312
+ ids: string[];
313
+ }[];
314
+ /** a server transform's restore gone wrong: a host upgraded first (`late`), or not Svelte's (`mismatch`) */
315
+ restores?: {
316
+ kind: "late" | "mismatch";
317
+ host: string;
318
+ t: number;
319
+ diff?: string;
320
+ island?: string;
321
+ }[];
322
+ viewport?: [number, number];
323
+ /** the devtools dock was open as the document loaded, from this page-clock time */
324
+ dock_open_at?: number;
325
+ ua?: string;
326
+ }
327
+ /** A beacon body's `visit` → a bounded Visit, or null when it is not one. */
328
+ export declare function parse_visit(page: unknown, raw: unknown): Visit | null;
329
+ /** Two records of the SAME visit (same page, same navigation start) folded into one. The runtime's
330
+ * beacon and the app's `mark()` client can be two module instances on one page, each posting its
331
+ * own half (islands here, marks there); a visit is also posted early and again on hide. Arrays
332
+ * union by identity, the later record's navigation and paints win where it has them. */
333
+ export declare function merge_visits(a: Visit, b: Visit): Visit;
334
+ export interface ClockBar {
335
+ id: string;
336
+ lane: string;
337
+ label: string;
338
+ t0: number;
339
+ t1: number;
340
+ /** what colours it: server-<phase> | ttfb | download | css | script | font | img | fetch | other | task | island-load | island-hydrate | first */
341
+ kind: string;
342
+ critical?: boolean;
343
+ detail?: string;
344
+ fp?: string;
345
+ blocking?: boolean;
346
+ }
347
+ export interface ClockLane {
348
+ name: string;
349
+ /** server | document | network | main | islands | holes */
350
+ group: string;
351
+ bars: ClockBar[];
352
+ }
353
+ export interface OneClock {
354
+ /** the clock's end, ms after navigation start */
355
+ end: number;
356
+ lanes: ClockLane[];
357
+ marks: {
358
+ label: string;
359
+ t: number;
360
+ kind: string;
361
+ }[];
362
+ /** the bars on the critical path to first paint and to LCP, in order */
363
+ critical: {
364
+ id: string;
365
+ label: string;
366
+ t0: number;
367
+ t1: number;
368
+ why: string;
369
+ }[];
370
+ /** the server's render window, placed so it ends at the first byte (null: no server timeline) */
371
+ server_at: {
372
+ t0: number;
373
+ t1: number;
374
+ clipped: boolean;
375
+ } | null;
376
+ notes: string[];
377
+ }
378
+ /** Lay a visit out on one clock next to the server's render of the same page. `phases` is the
379
+ * server timeline's phase split (each phase's CPU + wait, in order); `names` maps an island's
380
+ * fingerprint to its component name. */
381
+ export declare function one_clock(visit: Visit, server: {
382
+ window_ms: number;
383
+ phases: {
384
+ phase: string;
385
+ label?: string;
386
+ cpu_ms: number;
387
+ wait_ms: number;
388
+ }[];
389
+ } | null, names?: Record<string, string>, hole_names?: Record<string, string>): OneClock;