@ogygia/devtools 0.0.0 → 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 +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 +105 -6
@@ -0,0 +1,132 @@
1
+ import { props_sidecar_of, tool_made } from "@ogygia/core/internal/instruments";
2
+ import { snapshot } from "@ogygia/core/events";
3
+ // src/devtools/regions.ts
4
+ /**
5
+ * Pure, UI-free helpers the devtools components share — region classification off the DOM attributes
6
+ * the compiler already emits, resource-timing byte lookups, and event queries over the bus buffer.
7
+ * No Svelte, no styling: the `.svelte` components own all presentation.
8
+ */
9
+ const JS_EXT_RE = /\.js$/;
10
+ function region_kind(el) {
11
+ if (el.getAttribute("render") === "defer") return "hole";
12
+ if (el.getAttribute("wake") === "none") return "lake";
13
+ return "island";
14
+ }
15
+ function region_info(el) {
16
+ const kind = region_kind(el);
17
+ const parent = el.hasAttribute("data-nested") ? el.parentElement?.closest("ogygia-region") ?? void 0 : void 0;
18
+ return {
19
+ el,
20
+ kind,
21
+ wake: el.getAttribute("wake") || (kind === "hole" ? "fetch" : "load"),
22
+ entry: el.getAttribute("entry"),
23
+ src: el.getAttribute("src"),
24
+ fp: el.getAttribute("data-og-fp"),
25
+ hydrated: el.hasAttribute("data-hydrated") || !!parent?.hasAttribute("data-hydrated"),
26
+ ...parent ? { rides: parent } : {}
27
+ };
28
+ }
29
+ /** Every `<ogygia-region>` on the page, as {@link RegionInfo}. */
30
+ function all_regions() {
31
+ return Array.from(document.querySelectorAll("ogygia-region"), region_info);
32
+ }
33
+ /**
34
+ * The raw devalue text of a region's `<script data-ogygia-props>` sidecar (the props that crossed the
35
+ * boundary), or null. Mirrors the runtime's own sibling walk (skips `<link>` CSS hints). Returned as
36
+ * TEXT so this module stays devalue-free; the detail view decodes it.
37
+ */
38
+ function region_props_sidecar(el) {
39
+ return props_sidecar_of(el)?.textContent ?? null;
40
+ }
41
+ /** basename of a URL/path (drops query + directory). */
42
+ function basename(url) {
43
+ return (url.split("?")[0].split("#")[0].split("/").pop() || url).trim();
44
+ }
45
+ /** basename without a trailing `.js`, for a compact chunk label. */
46
+ function short_chunk(url) {
47
+ if (!url) return "";
48
+ const b = basename(url).replace(JS_EXT_RE, "");
49
+ return b.length > 24 ? b.slice(0, 23) + "…" : b;
50
+ }
51
+ /** The island id from an entry URL, or '': `<id>` in the dev server's `virtual:ogygia/island/<id>.js`
52
+ * and in a build's `og-region.<id>.js` (the name maps key the bare id — with the prefix left on, every
53
+ * tab in a build showed hashes). */
54
+ function island_id(entry) {
55
+ if (!entry) return "";
56
+ const id = basename(entry).replace(JS_EXT_RE, "");
57
+ return id.startsWith(BUILT_PREFIX) ? id.slice(BUILT_PREFIX.length) : id;
58
+ }
59
+ const BUILT_PREFIX = "og-region.";
60
+ /**
61
+ * The compiler's `island id → component name` map (dev-only), published on `window` by the
62
+ * `virtual:ogygia/devtools-names` side-effect module the runtime/devtools boot pulls in. Empty off a
63
+ * devtools build. Lets every tab label a region "Counter" instead of a hashed entry.
64
+ */
65
+ function region_names() {
66
+ return typeof window !== "undefined" && window.__ogygia_region_names || {};
67
+ }
68
+ /** Component name for an island entry URL, or `short_chunk(entry)` when unknown. */
69
+ function region_name(entry) {
70
+ const id = island_id(entry);
71
+ return id && region_names()[id] || short_chunk(entry ?? null);
72
+ }
73
+ /**
74
+ * Transitive dev size for an island entry — the compiler's module-graph estimate (wrapper +
75
+ * component + everything imported), published on `window.__ogygia_region_bytes` by the dock's mount
76
+ * fetch. `null` when the island is still cold (never woken → no subgraph yet) or off a dev build.
77
+ */
78
+ function region_transitive(entry) {
79
+ const id = island_id(entry);
80
+ const map = typeof window !== "undefined" && window.__ogygia_region_bytes || null;
81
+ return id && map && map[id] || null;
82
+ }
83
+ /** Component name for a region fingerprint — joined to its live `<ogygia-region>` in the DOM. */
84
+ function region_name_by_fp(fp) {
85
+ if (!fp || typeof document === "undefined") return fp || "";
86
+ const entry = document.querySelector(`ogygia-region[data-og-fp="${CSS.escape(fp)}"]`)?.getAttribute("entry");
87
+ return (entry ? region_name(entry) : "") || fp;
88
+ }
89
+ /** kB with one decimal. */
90
+ function kb(bytes) {
91
+ return (bytes / 1024).toFixed(1) + " kB";
92
+ }
93
+ /** Resource-timing entry for a chunk, matched by hashed basename (unique per build). */
94
+ function timing_for(entry) {
95
+ if (typeof performance === "undefined") return void 0;
96
+ const base = basename(entry);
97
+ if (!base) return void 0;
98
+ return timing_index().get(base);
99
+ }
100
+ let index_len = -1;
101
+ let index = /* @__PURE__ */ new Map();
102
+ function timing_index() {
103
+ const res = performance.getEntriesByType("resource");
104
+ if (res.length !== index_len) {
105
+ index = /* @__PURE__ */ new Map();
106
+ for (const r of res) if (!tool_made(r)) index.set(basename(r.name), r);
107
+ index_len = res.length;
108
+ }
109
+ return index;
110
+ }
111
+ /** Over-the-wire + decoded bytes for a chunk entry (0/0 when not yet loaded or size hidden). */
112
+ function chunk_bytes(entry) {
113
+ const t = timing_for(entry);
114
+ if (!t) return {
115
+ wire: 0,
116
+ raw: 0,
117
+ loaded: false
118
+ };
119
+ const wire = t.encodedBodySize || t.transferSize || t.decodedBodySize || 0;
120
+ return {
121
+ wire,
122
+ raw: t.decodedBodySize || wire,
123
+ loaded: true
124
+ };
125
+ }
126
+ /** Latest buffered event matching a predicate (newest wins), or null. */
127
+ function latest_event(pred) {
128
+ const events = snapshot();
129
+ for (let i = events.length - 1; i >= 0; i--) if (pred(events[i])) return events[i];
130
+ return null;
131
+ }
132
+ export { all_regions, basename, chunk_bytes, island_id, kb, latest_event, region_info, region_kind, region_name, region_name_by_fp, region_names, region_props_sidecar, region_transitive, short_chunk, timing_for };
@@ -0,0 +1,31 @@
1
+ import { SessionClick, SessionData, SessionData as SessionData$1, SessionFrame, SessionInteraction, SessionNav, SessionRequest, SessionShift } from "@ogygia/core/insights";
2
+ /** Names for a session's islands: its own record first, then the page as it is now. */
3
+ export declare function session_names(s: SessionData$1, now: (fp: string) => string): (fp: string) => string;
4
+ /** A page's part of a session, saved on page hide so the next document picks the session up. */
5
+ interface SavedPart {
6
+ origin: number;
7
+ data: SessionData$1;
8
+ }
9
+ /** On a devtools boot: a session a previous page was recording goes on here. */
10
+ export declare function resume_session_if_any(): boolean;
11
+ /** Was a session saved by the page before (without resuming it). */
12
+ export declare function session_waiting(): boolean;
13
+ /** `button#buy.primary` — a short readable name for an element. */
14
+ export declare function describe_el(el: Element | null | undefined): string;
15
+ interface Recorder {
16
+ stop(): Promise<SessionData$1>;
17
+ started: number;
18
+ }
19
+ export declare function set_last_session(s: {
20
+ data: SessionData$1;
21
+ report: unknown;
22
+ } | null): void;
23
+ export declare function get_last_session(): {
24
+ data: SessionData$1;
25
+ report: unknown;
26
+ } | null;
27
+ export declare function last_session_version(): number;
28
+ export declare function recording(): Recorder | null;
29
+ /** Start recording (or go on with the parts earlier pages saved — `resume_session_if_any`). */
30
+ export declare function start_session(prior?: SavedPart[]): Recorder;
31
+ export type { SessionClick, SessionData, SessionFrame, SessionInteraction, SessionNav, SessionRequest, SessionShift };
@@ -0,0 +1,455 @@
1
+ import { all_regions, region_name } from "./regions.js";
2
+ import { add_sink, snapshot } from "@ogygia/core/events";
3
+ // src/devtools/session.ts
4
+ /**
5
+ * THE SESSION RECORDER — press record, use the page, press stop. Everything the browser can tell
6
+ * about that stretch is collected here (session-insights.ts turns it into findings):
7
+ * - every interaction (Event Timing, from 16 ms): its target, input delay / processing / paint;
8
+ * - long tasks, and long animation frames with the scripts that ran in them (where supported);
9
+ * - the main thread sampled (JS Self-Profiling, where the document allows it);
10
+ * - layout shifts, with whether input caused them, and the element that moved;
11
+ * - every request made (resource timing: size, time, status);
12
+ * - DOM changes per island (a MutationObserver), errors and rejections, console errors;
13
+ * - the heap before and after (where the browser exposes it);
14
+ * - the devtools bus: islands woken, hydrations, failures, recoveries, navigations, hole fetches.
15
+ * Elements are kept by WeakRef so the tab can point at them without holding the page alive.
16
+ */
17
+ const CLICKABLE = "a[href],button,input,select,textarea,label,summary,[role=\"button\"],[role=\"link\"],[role=\"tab\"],[role=\"menuitem\"],[role=\"checkbox\"],[role=\"switch\"],[role=\"option\"],[onclick],[tabindex]:not([tabindex=\"-1\"])";
18
+ /** What a visitor takes for a control: one in its ancestry, or a pointer cursor where they clicked. */
19
+ function looks_clickable(el) {
20
+ if (el.closest(CLICKABLE)) return true;
21
+ try {
22
+ return getComputedStyle(el).cursor === "pointer";
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+ /** Names for a session's islands: its own record first, then the page as it is now. */
28
+ function session_names(s, now) {
29
+ return (fp) => s.names?.[fp] || now(fp);
30
+ }
31
+ const SAVED_KEY = "ogygia:devtools:session";
32
+ /** Shift every time in a part by `d` ms (another document's clock onto this one's). */
33
+ function shift_part(s, d) {
34
+ const t = (xs) => xs.map((x) => ({
35
+ ...x,
36
+ t: Math.round((x.t + d) * 10) / 10
37
+ }));
38
+ return {
39
+ ...s,
40
+ from: s.from + d,
41
+ to: s.to + d,
42
+ interactions: t(s.interactions),
43
+ longtasks: t(s.longtasks),
44
+ frames: t(s.frames),
45
+ shifts: t(s.shifts),
46
+ requests: t(s.requests),
47
+ mut_log: t(s.mut_log),
48
+ clicks: t(s.clicks),
49
+ errors: t(s.errors),
50
+ navs: t(s.navs ?? []),
51
+ events: s.events.map((e) => ({
52
+ ...e,
53
+ t: e.t + d
54
+ })),
55
+ pages: t(s.pages)
56
+ };
57
+ }
58
+ /** Several pages' parts as one session on the current document's clock. */
59
+ function merge_parts(parts, current) {
60
+ const here = performance.timeOrigin;
61
+ const all = [...parts.map((p) => shift_part(p.data, p.origin - here)), current];
62
+ const cat = (k) => all.flatMap((p) => p[k]);
63
+ const muts = /* @__PURE__ */ new Map();
64
+ for (const p of all) for (const m of p.mutations) {
65
+ const k = m.fp ?? "";
66
+ const prev = muts.get(k);
67
+ muts.set(k, {
68
+ fp: m.fp,
69
+ count: (prev?.count ?? 0) + m.count,
70
+ el: m.el ?? prev?.el ?? null
71
+ });
72
+ }
73
+ return {
74
+ ...current,
75
+ from: all[0].from,
76
+ interactions: cat("interactions"),
77
+ longtasks: cat("longtasks"),
78
+ frames: cat("frames"),
79
+ shifts: cat("shifts"),
80
+ requests: cat("requests"),
81
+ mut_log: cat("mut_log"),
82
+ clicks: cat("clicks"),
83
+ errors: cat("errors"),
84
+ navs: all.flatMap((p) => p.navs ?? []),
85
+ events: cat("events"),
86
+ pages: cat("pages"),
87
+ mutations: [...muts.values()].sort((a, b) => b.count - a.count),
88
+ heap: current.heap,
89
+ names: Object.assign({}, ...all.map((p) => p.names ?? {}))
90
+ };
91
+ }
92
+ /** On a devtools boot: a session a previous page was recording goes on here. */
93
+ function resume_session_if_any() {
94
+ let parts = null;
95
+ try {
96
+ const raw = sessionStorage.getItem(SAVED_KEY);
97
+ if (raw) parts = JSON.parse(raw);
98
+ sessionStorage.removeItem(SAVED_KEY);
99
+ } catch {
100
+ parts = null;
101
+ }
102
+ if (!parts?.length || active) return false;
103
+ start_session(parts);
104
+ return true;
105
+ }
106
+ /** Was a session saved by the page before (without resuming it). */
107
+ function session_waiting() {
108
+ try {
109
+ return !!sessionStorage.getItem(SAVED_KEY);
110
+ } catch {
111
+ return false;
112
+ }
113
+ }
114
+ const ISLAND = "ogygia-region[data-og-fp]";
115
+ const r1 = (n) => Math.round(n * 10) / 10;
116
+ function island_of(node) {
117
+ return (node && node.nodeType === 1 ? node : node?.parentElement ?? null)?.closest?.(ISLAND) ?? null;
118
+ }
119
+ /** `button#buy.primary` — a short readable name for an element. */
120
+ function describe_el(el) {
121
+ if (!el) return "(unknown)";
122
+ let s = el.localName;
123
+ if (el.id) s += "#" + el.id;
124
+ const cls = typeof el.className === "string" ? el.className.trim().split(" ").filter((c) => c && !c.startsWith("svelte-")).slice(0, 2) : [];
125
+ if (cls.length) s += "." + cls.join(".");
126
+ const text = (el.textContent ?? "").trim().slice(0, 24);
127
+ return text ? `${s} "${text}${(el.textContent ?? "").trim().length > 24 ? "…" : ""}"` : s;
128
+ }
129
+ let active = null;
130
+ /** the last finished session (its data + report), for every tab: the Record tab writes it, the
131
+ * island detail reads its island's part (the report type lives in session-insights.ts) */
132
+ let last_session = null;
133
+ let last_version = 0;
134
+ function set_last_session(s) {
135
+ last_session = s;
136
+ last_version++;
137
+ }
138
+ function get_last_session() {
139
+ return last_session;
140
+ }
141
+ function last_session_version() {
142
+ return last_version;
143
+ }
144
+ function recording() {
145
+ return active;
146
+ }
147
+ /** Start recording (or go on with the parts earlier pages saved — `resume_session_if_any`). */
148
+ function start_session(prior) {
149
+ if (active) return active;
150
+ const from = performance.now();
151
+ const seq0 = snapshot().at(-1)?.seq ?? -1;
152
+ const interactions = [];
153
+ const longtasks = [];
154
+ const frames = [];
155
+ const shifts = [];
156
+ const errors = [];
157
+ const muts = /* @__PURE__ */ new Map();
158
+ const names = {};
159
+ const note_names = () => {
160
+ for (const r of all_regions()) if (r.fp && !names[r.fp]) names[r.fp] = region_name(r.entry);
161
+ };
162
+ note_names();
163
+ const off_nav = add_sink((e) => {
164
+ if (e.name === "nav.start") note_names();
165
+ });
166
+ const observers = [];
167
+ const watch = (type, cb, extra = {}) => {
168
+ try {
169
+ const po = new PerformanceObserver((l) => cb(l.getEntries()));
170
+ po.observe({
171
+ type,
172
+ ...extra
173
+ });
174
+ observers.push({
175
+ po,
176
+ cb
177
+ });
178
+ } catch {}
179
+ };
180
+ watch("event", (es) => {
181
+ for (const e of es) {
182
+ if (e.startTime < from || interactions.length >= 2e3) continue;
183
+ const target = e.target ?? null;
184
+ const el = target && target.nodeType === 1 ? target : null;
185
+ const island = island_of(target);
186
+ interactions.push({
187
+ t: r1(e.startTime),
188
+ name: e.name,
189
+ duration: r1(e.duration),
190
+ input_delay: r1(Math.max(0, e.processingStart - e.startTime)),
191
+ processing: r1(Math.max(0, e.processingEnd - e.processingStart)),
192
+ presentation: r1(Math.max(0, e.startTime + e.duration - e.processingEnd)),
193
+ id: e.interactionId ?? 0,
194
+ target: describe_el(el),
195
+ fp: island?.getAttribute("data-og-fp") ?? null,
196
+ el: el ? new WeakRef(el) : null
197
+ });
198
+ }
199
+ }, { durationThreshold: 16 });
200
+ watch("longtask", (es) => {
201
+ for (const e of es) if (e.startTime >= from && longtasks.length < 2e3) longtasks.push({
202
+ t: r1(e.startTime),
203
+ ms: r1(e.duration)
204
+ });
205
+ });
206
+ watch("long-animation-frame", (es) => {
207
+ for (const e of es) {
208
+ if (e.startTime < from || frames.length >= 1e3) continue;
209
+ frames.push({
210
+ t: r1(e.startTime),
211
+ ms: r1(e.duration),
212
+ blocking: r1(e.blockingDuration ?? 0),
213
+ scripts: (e.scripts ?? []).slice(0, 8).map((s) => ({
214
+ source: s.sourceURL ?? "",
215
+ fn: s.sourceFunctionName ?? "",
216
+ invoker: s.invoker ?? "",
217
+ ms: r1(s.duration ?? 0)
218
+ }))
219
+ });
220
+ }
221
+ });
222
+ watch("layout-shift", (es) => {
223
+ for (const e of es) {
224
+ if (e.startTime < from || shifts.length >= 500) continue;
225
+ const node = e.sources?.[0]?.node ?? null;
226
+ const el = node && node.nodeType === 1 ? node : node?.parentElement ?? null;
227
+ shifts.push({
228
+ t: r1(e.startTime),
229
+ value: Math.round((e.value ?? 0) * 1e4) / 1e4,
230
+ input: !!e.hadRecentInput,
231
+ fp: island_of(node)?.getAttribute("data-og-fp") ?? null,
232
+ el: el ? new WeakRef(el) : null
233
+ });
234
+ }
235
+ });
236
+ const mut_log = [];
237
+ const mo = new MutationObserver((records) => {
238
+ const now = r1(performance.now());
239
+ const seen = /* @__PURE__ */ new Set();
240
+ for (const m of records) {
241
+ const island = island_of(m.target);
242
+ muts.set(island, (muts.get(island) ?? 0) + 1);
243
+ const fp = island?.getAttribute("data-og-fp") ?? null;
244
+ if (!seen.has(fp) && mut_log.length < 2e4) {
245
+ seen.add(fp);
246
+ mut_log.push({
247
+ t: now,
248
+ fp
249
+ });
250
+ }
251
+ }
252
+ });
253
+ const clicks = [];
254
+ const on_click = (e) => {
255
+ if (clicks.length >= 2e3) return;
256
+ const el = e.target instanceof Element ? e.target : null;
257
+ if (el?.closest("[data-ogygia-devtools-host]")) return;
258
+ clicks.push({
259
+ t: r1(e.timeStamp),
260
+ target: describe_el(el),
261
+ fp: island_of(el)?.getAttribute("data-og-fp") ?? null,
262
+ el: el ? new WeakRef(el) : null,
263
+ ...el && looks_clickable(el) ? { clickable: true } : {}
264
+ });
265
+ };
266
+ addEventListener("click", on_click, {
267
+ capture: true,
268
+ passive: true
269
+ });
270
+ const navs = [];
271
+ let handling = null;
272
+ const on_input = (e) => {
273
+ const el = e.target instanceof Element ? e.target : null;
274
+ if (!el || el.closest("[data-ogygia-devtools-host]")) return;
275
+ handling = {
276
+ target: describe_el(el),
277
+ fp: island_of(el)?.getAttribute("data-og-fp") ?? null,
278
+ until: performance.now() + 50
279
+ };
280
+ };
281
+ const INPUTS = [
282
+ "click",
283
+ "submit",
284
+ "keydown",
285
+ "change"
286
+ ];
287
+ for (const type of INPUTS) addEventListener(type, on_input, {
288
+ capture: true,
289
+ passive: true
290
+ });
291
+ const navigation = globalThis.navigation;
292
+ const on_navigate = (e) => {
293
+ const n = e;
294
+ if (!n.destination || navs.length >= 200) return;
295
+ const now = performance.now();
296
+ const by = n.sourceElement ?? null;
297
+ const input = handling && now <= handling.until ? handling : null;
298
+ navs.push({
299
+ url: n.destination.url,
300
+ t: r1(now),
301
+ type: n.navigationType ?? "",
302
+ source: by ? describe_el(by) : input?.target ?? null,
303
+ fp: by ? island_of(by)?.getAttribute("data-og-fp") ?? null : input?.fp ?? null
304
+ });
305
+ };
306
+ navigation?.addEventListener("navigate", on_navigate);
307
+ const requests = [];
308
+ watch("resource", (es) => {
309
+ for (const r of es) {
310
+ if (r.startTime < from || requests.length >= 2e3 || r.name.includes("/__profiler/") || r.name.includes("/__ogygia_devtools")) continue;
311
+ const st = r.serverTiming ?? [];
312
+ requests.push({
313
+ url: r.name,
314
+ type: r.initiatorType,
315
+ t: r1(r.startTime),
316
+ ms: r1(r.duration),
317
+ bytes: r.transferSize || r.decodedBodySize || 0,
318
+ status: typeof r.responseStatus === "number" && r.responseStatus > 0 ? r.responseStatus : null,
319
+ ...st.length ? { server: st.slice(0, 8).map((x) => ({
320
+ name: x.name,
321
+ ms: r1(x.duration),
322
+ desc: x.description
323
+ })) } : {}
324
+ });
325
+ }
326
+ });
327
+ try {
328
+ mo.observe(document.body, {
329
+ subtree: true,
330
+ childList: true,
331
+ characterData: true,
332
+ attributes: true
333
+ });
334
+ } catch {}
335
+ const on_error = (e) => errors.length < 200 && errors.push({
336
+ t: r1(performance.now()),
337
+ message: String(e.message || e.error || "error").slice(0, 300)
338
+ });
339
+ const on_rej = (e) => errors.length < 200 && errors.push({
340
+ t: r1(performance.now()),
341
+ message: "unhandled rejection: " + String(e.reason?.message ?? e.reason).slice(0, 280)
342
+ });
343
+ addEventListener("error", on_error);
344
+ addEventListener("unhandledrejection", on_rej);
345
+ const console_error = console.error;
346
+ console.error = (...args) => {
347
+ if (errors.length < 200) errors.push({
348
+ t: r1(performance.now()),
349
+ message: "console.error: " + args.map((a) => a instanceof Error ? a.message : String(a)).join(" ").slice(0, 280)
350
+ });
351
+ console_error.apply(console, args);
352
+ };
353
+ let prof = null;
354
+ let cpu_off = null;
355
+ const Ctor = globalThis.Profiler;
356
+ if (!Ctor) cpu_off = "unsupported";
357
+ else try {
358
+ prof = new Ctor({
359
+ sampleInterval: 10,
360
+ maxBufferSize: 6e4
361
+ });
362
+ } catch {
363
+ cpu_off = "no-policy";
364
+ }
365
+ const heap0 = performance.memory?.usedJSHeapSize ?? null;
366
+ /** stop watching (the entries still queued count too) */
367
+ const detach = () => {
368
+ for (const { po, cb } of observers) try {
369
+ const left = po.takeRecords();
370
+ if (left.length) cb(left);
371
+ po.disconnect();
372
+ } catch {}
373
+ mo.disconnect();
374
+ off_nav();
375
+ removeEventListener("error", on_error);
376
+ removeEventListener("unhandledrejection", on_rej);
377
+ removeEventListener("click", on_click, { capture: true });
378
+ for (const type of INPUTS) removeEventListener(type, on_input, { capture: true });
379
+ navigation?.removeEventListener("navigate", on_navigate);
380
+ removeEventListener("pagehide", on_hide);
381
+ console.error = console_error;
382
+ };
383
+ /** this document's part of the session */
384
+ const collect = (cpu) => {
385
+ const heap1 = performance.memory?.usedJSHeapSize ?? null;
386
+ return {
387
+ from,
388
+ to: performance.now(),
389
+ interactions,
390
+ longtasks,
391
+ frames,
392
+ shifts,
393
+ requests,
394
+ mutations: [...muts].map(([el, count]) => ({
395
+ fp: el?.getAttribute("data-og-fp") ?? null,
396
+ count,
397
+ el: el ? new WeakRef(el) : null
398
+ })).sort((a, b) => b.count - a.count),
399
+ mut_log,
400
+ clicks,
401
+ errors,
402
+ navs,
403
+ heap: heap0 !== null && heap1 !== null ? {
404
+ from: heap0,
405
+ to: heap1
406
+ } : null,
407
+ events: snapshot().filter((e) => e.seq > seq0 && e.realm === "client"),
408
+ cpu,
409
+ cpu_off,
410
+ page: location.pathname,
411
+ pages: [{
412
+ url: location.pathname + location.search,
413
+ t: from
414
+ }],
415
+ names: (note_names(), { ...names })
416
+ };
417
+ };
418
+ const on_hide = (e) => {
419
+ if (e.persisted || active !== rec) return;
420
+ detach();
421
+ active = null;
422
+ const strip = (s) => JSON.parse(JSON.stringify({
423
+ ...s,
424
+ cpu: null
425
+ }, (k, v) => k === "el" ? null : v));
426
+ try {
427
+ sessionStorage.setItem(SAVED_KEY, JSON.stringify([...prior ?? [], {
428
+ origin: performance.timeOrigin,
429
+ data: strip(collect(null))
430
+ }]));
431
+ } catch {}
432
+ };
433
+ addEventListener("pagehide", on_hide);
434
+ const rec = {
435
+ started: prior?.length ? prior[0].data.from + (prior[0].origin - performance.timeOrigin) : from,
436
+ async stop() {
437
+ active = null;
438
+ detach();
439
+ let cpu = null;
440
+ if (prof) try {
441
+ cpu = await prof.stop();
442
+ } catch {
443
+ cpu = null;
444
+ }
445
+ const here = collect(cpu);
446
+ try {
447
+ sessionStorage.removeItem(SAVED_KEY);
448
+ } catch {}
449
+ return prior?.length ? merge_parts(prior, here) : here;
450
+ }
451
+ };
452
+ active = rec;
453
+ return rec;
454
+ }
455
+ export { describe_el, get_last_session, last_session_version, recording, resume_session_if_any, session_names, session_waiting, set_last_session, start_session };
@@ -0,0 +1,50 @@
1
+ import { PartedVital, VitalPart } from "@ogygia/core/insights";
2
+ export interface LoadSnapshot {
3
+ path: string;
4
+ /** epoch ms the picture was taken */
5
+ at: number;
6
+ /** `dev`: the dev server's own doing (its first compile, the CSS it adds with JavaScript) */
7
+ findings: {
8
+ code: string;
9
+ names: string[];
10
+ dev?: true;
11
+ }[];
12
+ /** `compile_ms`: on the dev server, the part of its load that was the server compiling its files
13
+ * on that first request (page-insights `dev_compile_ms`) — not the code's own cost */
14
+ islands: {
15
+ name: string;
16
+ load_ms: number;
17
+ hydrate_ms: number;
18
+ compile_ms?: number;
19
+ }[];
20
+ vitals: {
21
+ key: string;
22
+ value: number;
23
+ }[];
24
+ /** each split vital's parts (page-insights `vital_parts`); older pictures have none */
25
+ parts?: Partial<Record<PartedVital, VitalPart[]>>;
26
+ /** the dev server compiled the page on this load's request (its first byte waited on that) */
27
+ page_compiled?: true;
28
+ /** its layout shift came from the CSS the dev server adds with JavaScript */
29
+ cls_dev?: true;
30
+ }
31
+ export interface SinceLoad {
32
+ ago_ms: number;
33
+ fixed: string[];
34
+ added: string[];
35
+ moved: {
36
+ what: string;
37
+ a: number;
38
+ b: number;
39
+ unit: "ms" | "";
40
+ better: boolean;
41
+ part?: {
42
+ label: string;
43
+ a: number;
44
+ b: number;
45
+ };
46
+ }[];
47
+ /** what was left out of the compare, and why (the last load was the dev server's first compile) */
48
+ note?: string;
49
+ }
50
+ export declare function since_load(prev: LoadSnapshot, now: LoadSnapshot, at?: number): SinceLoad | null;