@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.
- package/LICENSE +21 -0
- package/README.md +24 -1
- package/dist/client.d.ts +3 -0
- package/dist/client.js +24 -0
- package/dist/devtools/BoundaryOverlay.svelte +396 -0
- package/dist/devtools/BoundaryOverlay.svelte.d.ts +19 -0
- package/dist/devtools/Devtools.svelte +663 -0
- package/dist/devtools/Devtools.svelte.d.ts +15 -0
- package/dist/devtools/HubTab.svelte +206 -0
- package/dist/devtools/HubTab.svelte.d.ts +11 -0
- package/dist/devtools/HydrationTab.svelte +237 -0
- package/dist/devtools/HydrationTab.svelte.d.ts +13 -0
- package/dist/devtools/IslandDetail.svelte +615 -0
- package/dist/devtools/IslandDetail.svelte.d.ts +15 -0
- package/dist/devtools/LedgerTab.svelte +287 -0
- package/dist/devtools/LedgerTab.svelte.d.ts +11 -0
- package/dist/devtools/LensTab.svelte +290 -0
- package/dist/devtools/LensTab.svelte.d.ts +19 -0
- package/dist/devtools/NavTab.svelte +229 -0
- package/dist/devtools/NavTab.svelte.d.ts +11 -0
- package/dist/devtools/PageTab.svelte +1122 -0
- package/dist/devtools/PageTab.svelte.d.ts +15 -0
- package/dist/devtools/ProfilerTab.svelte +755 -0
- package/dist/devtools/ProfilerTab.svelte.d.ts +15 -0
- package/dist/devtools/PropsTree.svelte +155 -0
- package/dist/devtools/PropsTree.svelte.d.ts +15 -0
- package/dist/devtools/SessionTab.svelte +796 -0
- package/dist/devtools/SessionTab.svelte.d.ts +11 -0
- package/dist/devtools/TimelineTab.svelte +267 -0
- package/dist/devtools/TimelineTab.svelte.d.ts +11 -0
- package/dist/devtools/WireTab.svelte +149 -0
- package/dist/devtools/WireTab.svelte.d.ts +11 -0
- package/dist/devtools/bytes.d.ts +70 -0
- package/dist/devtools/bytes.js +82 -0
- package/dist/devtools/cache-headers.d.ts +20 -0
- package/dist/devtools/cache-headers.js +81 -0
- package/dist/devtools/highlight.d.ts +7 -0
- package/dist/devtools/highlight.js +83 -0
- package/dist/devtools/hydration.d.ts +48 -0
- package/dist/devtools/hydration.js +93 -0
- package/dist/devtools/index.d.ts +3 -0
- package/dist/devtools/index.js +3 -0
- package/dist/devtools/ledger-dom.d.ts +7 -0
- package/dist/devtools/ledger-dom.js +151 -0
- package/dist/devtools/leftovers.d.ts +77 -0
- package/dist/devtools/leftovers.js +319 -0
- package/dist/devtools/names.d.ts +1 -0
- package/dist/devtools/names.js +34 -0
- package/dist/devtools/page.d.ts +96 -0
- package/dist/devtools/page.js +957 -0
- package/dist/devtools/profile-anchors.d.ts +22 -0
- package/dist/devtools/profile-anchors.js +135 -0
- package/dist/devtools/profile-store.d.ts +175 -0
- package/dist/devtools/profile-store.js +239 -0
- package/dist/devtools/regions.d.ts +77 -0
- package/dist/devtools/regions.js +132 -0
- package/dist/devtools/session.d.ts +31 -0
- package/dist/devtools/session.js +455 -0
- package/dist/devtools/since-load.d.ts +50 -0
- package/dist/devtools/since-load.js +89 -0
- package/dist/devtools/source-lines.d.ts +24 -0
- package/dist/devtools/source-lines.js +106 -0
- package/dist/devtools/styles.d.ts +52 -0
- package/dist/devtools/styles.js +248 -0
- package/dist/devtools/testing-engine.d.ts +83 -0
- package/dist/devtools/testing-engine.js +220 -0
- package/dist/devtools/ui-app.d.ts +5 -0
- package/dist/devtools/ui-app.js +69 -0
- package/dist/devtools/ui.d.ts +50 -0
- package/dist/devtools/ui.js +175 -0
- package/dist/profiler/alloc.d.ts +75 -0
- package/dist/profiler/alloc.js +76 -0
- package/dist/profiler/analyze.d.ts +437 -0
- package/dist/profiler/analyze.js +1941 -0
- package/dist/profiler/app-path.d.ts +7 -0
- package/dist/profiler/app-path.js +26 -0
- package/dist/profiler/async-io.d.ts +58 -0
- package/dist/profiler/async-io.js +187 -0
- package/dist/profiler/beacon-standalone.d.ts +4 -0
- package/dist/profiler/beacon-standalone.js +409 -0
- package/dist/profiler/browser-findings.d.ts +25 -0
- package/dist/profiler/browser-findings.js +260 -0
- package/dist/profiler/byte-strip.d.ts +58 -0
- package/dist/profiler/byte-strip.js +228 -0
- package/dist/profiler/client-files.d.ts +16 -0
- package/dist/profiler/client-files.js +60 -0
- package/dist/profiler/client-windows.d.ts +49 -0
- package/dist/profiler/client-windows.js +95 -0
- package/dist/profiler/client.d.ts +40 -0
- package/dist/profiler/client.js +82 -0
- package/dist/profiler/compare.d.ts +308 -0
- package/dist/profiler/compare.js +733 -0
- package/dist/profiler/compress.d.ts +10 -0
- package/dist/profiler/compress.js +36 -0
- package/dist/profiler/contention.d.ts +60 -0
- package/dist/profiler/contention.js +86 -0
- package/dist/profiler/crypto.d.ts +19 -0
- package/dist/profiler/crypto.js +101 -0
- package/dist/profiler/doc-diff.d.ts +47 -0
- package/dist/profiler/doc-diff.js +214 -0
- package/dist/profiler/dom-diff.d.ts +43 -0
- package/dist/profiler/dom-diff.js +121 -0
- package/dist/profiler/drill.d.ts +89 -0
- package/dist/profiler/drill.js +553 -0
- package/dist/profiler/embedded-maps.d.ts +37 -0
- package/dist/profiler/embedded-maps.js +135 -0
- package/dist/profiler/env.d.ts +6 -0
- package/dist/profiler/env.js +10 -0
- package/dist/profiler/forecast.d.ts +76 -0
- package/dist/profiler/forecast.js +225 -0
- package/dist/profiler/frames.d.ts +11 -0
- package/dist/profiler/frames.js +42 -0
- package/dist/profiler/gc.d.ts +144 -0
- package/dist/profiler/gc.js +271 -0
- package/dist/profiler/hole-slots.d.ts +31 -0
- package/dist/profiler/hole-slots.js +90 -0
- package/dist/profiler/index.d.ts +288 -0
- package/dist/profiler/index.js +4921 -0
- package/dist/profiler/insights.d.ts +96 -0
- package/dist/profiler/insights.js +239 -0
- package/dist/profiler/ledger.d.ts +121 -0
- package/dist/profiler/ledger.js +440 -0
- package/dist/profiler/lineage.d.ts +81 -0
- package/dist/profiler/lineage.js +359 -0
- package/dist/profiler/module-map.d.ts +12 -0
- package/dist/profiler/module-map.js +94 -0
- package/dist/profiler/net.d.ts +169 -0
- package/dist/profiler/net.js +586 -0
- package/dist/profiler/on-demand.d.ts +11 -0
- package/dist/profiler/on-demand.js +15 -0
- package/dist/profiler/own-requests.d.ts +9 -0
- package/dist/profiler/own-requests.js +17 -0
- package/dist/profiler/patterns.d.ts +476 -0
- package/dist/profiler/patterns.js +2203 -0
- package/dist/profiler/profiler-router.d.ts +245 -0
- package/dist/profiler/profiler-router.js +113 -0
- package/dist/profiler/refresh-stub.d.ts +2 -0
- package/dist/profiler/refresh-stub.js +37 -0
- package/dist/profiler/report.d.ts +1404 -0
- package/dist/profiler/report.js +2638 -0
- package/dist/profiler/river.d.ts +65 -0
- package/dist/profiler/river.js +298 -0
- package/dist/profiler/route-files.d.ts +22 -0
- package/dist/profiler/route-files.js +55 -0
- package/dist/profiler/route-imports.d.ts +14 -0
- package/dist/profiler/route-imports.js +83 -0
- package/dist/profiler/saving.d.ts +22 -0
- package/dist/profiler/saving.js +42 -0
- package/dist/profiler/score.d.ts +109 -0
- package/dist/profiler/score.js +261 -0
- package/dist/profiler/session-cookie.d.ts +28 -0
- package/dist/profiler/session-cookie.js +45 -0
- package/dist/profiler/sink.d.ts +81 -0
- package/dist/profiler/sink.js +124 -0
- package/dist/profiler/site-fixes.d.ts +25 -0
- package/dist/profiler/site-fixes.js +62 -0
- package/dist/profiler/source-scan.d.ts +210 -0
- package/dist/profiler/source-scan.js +1724 -0
- package/dist/profiler/span.d.ts +90 -0
- package/dist/profiler/span.js +109 -0
- package/dist/profiler/stacks.d.ts +43 -0
- package/dist/profiler/stacks.js +107 -0
- package/dist/profiler/standalone.d.ts +27 -0
- package/dist/profiler/standalone.js +98 -0
- package/dist/profiler/steps.d.ts +39 -0
- package/dist/profiler/steps.js +74 -0
- package/dist/profiler/storage/index.d.ts +87 -0
- package/dist/profiler/storage/index.js +76 -0
- package/dist/profiler/storage/postgres.d.ts +12 -0
- package/dist/profiler/storage/postgres.js +94 -0
- package/dist/profiler/storage/redis.d.ts +19 -0
- package/dist/profiler/storage/redis.js +148 -0
- package/dist/profiler/storage/sqlite.d.ts +2 -0
- package/dist/profiler/storage/sqlite.js +85 -0
- package/dist/profiler/storage/visits.d.ts +4 -0
- package/dist/profiler/storage/visits.js +6 -0
- package/dist/profiler/stream-tail.d.ts +26 -0
- package/dist/profiler/stream-tail.js +74 -0
- package/dist/profiler/timeline.d.ts +279 -0
- package/dist/profiler/timeline.js +690 -0
- package/dist/profiler/ui/AllocStrip.svelte +146 -0
- package/dist/profiler/ui/AllocStrip.svelte.d.ts +17 -0
- package/dist/profiler/ui/Awaits.svelte +363 -0
- package/dist/profiler/ui/Awaits.svelte.d.ts +16 -0
- package/dist/profiler/ui/ByteStrip.svelte +182 -0
- package/dist/profiler/ui/ByteStrip.svelte.d.ts +18 -0
- package/dist/profiler/ui/Compare.svelte +24 -0
- package/dist/profiler/ui/Compare.svelte.d.ts +16 -0
- package/dist/profiler/ui/CompareBody.svelte +429 -0
- package/dist/profiler/ui/CompareBody.svelte.d.ts +8 -0
- package/dist/profiler/ui/ComponentsTable.svelte +265 -0
- package/dist/profiler/ui/ComponentsTable.svelte.d.ts +20 -0
- package/dist/profiler/ui/Dashboard.svelte +492 -0
- package/dist/profiler/ui/Dashboard.svelte.d.ts +31 -0
- package/dist/profiler/ui/DataRiver.svelte +206 -0
- package/dist/profiler/ui/DataRiver.svelte.d.ts +13 -0
- package/dist/profiler/ui/DomTravel.svelte +207 -0
- package/dist/profiler/ui/DomTravel.svelte.d.ts +9 -0
- package/dist/profiler/ui/DownloadHtml.svelte +52 -0
- package/dist/profiler/ui/DownloadHtml.svelte.d.ts +10 -0
- package/dist/profiler/ui/Drill.svelte +276 -0
- package/dist/profiler/ui/Drill.svelte.d.ts +12 -0
- package/dist/profiler/ui/ExportButton.svelte +59 -0
- package/dist/profiler/ui/ExportButton.svelte.d.ts +8 -0
- package/dist/profiler/ui/Flame.svelte +222 -0
- package/dist/profiler/ui/Flame.svelte.d.ts +12 -0
- package/dist/profiler/ui/FrameDetails.svelte +308 -0
- package/dist/profiler/ui/FrameDetails.svelte.d.ts +16 -0
- package/dist/profiler/ui/FunctionsTable.svelte +156 -0
- package/dist/profiler/ui/FunctionsTable.svelte.d.ts +17 -0
- package/dist/profiler/ui/IslandHeatmap.svelte +151 -0
- package/dist/profiler/ui/IslandHeatmap.svelte.d.ts +16 -0
- package/dist/profiler/ui/IslandsTable.svelte +448 -0
- package/dist/profiler/ui/IslandsTable.svelte.d.ts +16 -0
- package/dist/profiler/ui/KeepLocal.svelte +46 -0
- package/dist/profiler/ui/KeepLocal.svelte.d.ts +8 -0
- package/dist/profiler/ui/Ledger.svelte +196 -0
- package/dist/profiler/ui/Ledger.svelte.d.ts +18 -0
- package/dist/profiler/ui/LocalCompare.svelte +48 -0
- package/dist/profiler/ui/LocalCompare.svelte.d.ts +8 -0
- package/dist/profiler/ui/LocalReports.svelte +68 -0
- package/dist/profiler/ui/LocalReports.svelte.d.ts +7 -0
- package/dist/profiler/ui/Login.svelte +13 -0
- package/dist/profiler/ui/Login.svelte.d.ts +12 -0
- package/dist/profiler/ui/LoginForm.svelte +42 -0
- package/dist/profiler/ui/LoginForm.svelte.d.ts +7 -0
- package/dist/profiler/ui/Message.svelte +10 -0
- package/dist/profiler/ui/Message.svelte.d.ts +12 -0
- package/dist/profiler/ui/OnThisPage.svelte +137 -0
- package/dist/profiler/ui/OnThisPage.svelte.d.ts +3 -0
- package/dist/profiler/ui/OneClock.svelte +297 -0
- package/dist/profiler/ui/OneClock.svelte.d.ts +27 -0
- package/dist/profiler/ui/Panel.svelte +10 -0
- package/dist/profiler/ui/Panel.svelte.d.ts +16 -0
- package/dist/profiler/ui/Paths.svelte +269 -0
- package/dist/profiler/ui/Paths.svelte.d.ts +15 -0
- package/dist/profiler/ui/Patterns.svelte +364 -0
- package/dist/profiler/ui/Patterns.svelte.d.ts +15 -0
- package/dist/profiler/ui/PermalinkGate.svelte +143 -0
- package/dist/profiler/ui/PermalinkGate.svelte.d.ts +10 -0
- package/dist/profiler/ui/RenderSteps.svelte +218 -0
- package/dist/profiler/ui/RenderSteps.svelte.d.ts +13 -0
- package/dist/profiler/ui/Report.svelte +28 -0
- package/dist/profiler/ui/Report.svelte.d.ts +20 -0
- package/dist/profiler/ui/ReportBody.svelte +2592 -0
- package/dist/profiler/ui/ReportBody.svelte.d.ts +21 -0
- package/dist/profiler/ui/Run.svelte +17 -0
- package/dist/profiler/ui/Run.svelte.d.ts +14 -0
- package/dist/profiler/ui/RunView.svelte +146 -0
- package/dist/profiler/ui/RunView.svelte.d.ts +10 -0
- package/dist/profiler/ui/ScoreCard.svelte +197 -0
- package/dist/profiler/ui/ScoreCard.svelte.d.ts +7 -0
- package/dist/profiler/ui/Scrub.svelte +293 -0
- package/dist/profiler/ui/Scrub.svelte.d.ts +18 -0
- package/dist/profiler/ui/SeedExplainer.svelte +193 -0
- package/dist/profiler/ui/SeedExplainer.svelte.d.ts +16 -0
- package/dist/profiler/ui/ShareLink.svelte +148 -0
- package/dist/profiler/ui/ShareLink.svelte.d.ts +8 -0
- package/dist/profiler/ui/Shell.svelte +50 -0
- package/dist/profiler/ui/Shell.svelte.d.ts +27 -0
- package/dist/profiler/ui/Sidebar.svelte +312 -0
- package/dist/profiler/ui/Sidebar.svelte.d.ts +7 -0
- package/dist/profiler/ui/Site.svelte +20 -0
- package/dist/profiler/ui/Site.svelte.d.ts +23 -0
- package/dist/profiler/ui/SiteView.svelte +385 -0
- package/dist/profiler/ui/SiteView.svelte.d.ts +27 -0
- package/dist/profiler/ui/ThemeSwitch.svelte +78 -0
- package/dist/profiler/ui/ThemeSwitch.svelte.d.ts +3 -0
- package/dist/profiler/ui/Timeline.svelte +672 -0
- package/dist/profiler/ui/Timeline.svelte.d.ts +22 -0
- package/dist/profiler/ui/Treemap.svelte +278 -0
- package/dist/profiler/ui/Treemap.svelte.d.ts +20 -0
- package/dist/profiler/ui/Upload.svelte +19 -0
- package/dist/profiler/ui/Upload.svelte.d.ts +10 -0
- package/dist/profiler/ui/UploadForm.svelte +59 -0
- package/dist/profiler/ui/UploadForm.svelte.d.ts +6 -0
- package/dist/profiler/ui/WaitingTable.svelte +55 -0
- package/dist/profiler/ui/WaitingTable.svelte.d.ts +14 -0
- package/dist/profiler/ui/Waterfall.svelte +371 -0
- package/dist/profiler/ui/Waterfall.svelte.d.ts +7 -0
- package/dist/profiler/ui/WhatIf.svelte +87 -0
- package/dist/profiler/ui/WhatIf.svelte.d.ts +18 -0
- package/dist/profiler/ui/fake-progress.svelte.d.ts +15 -0
- package/dist/profiler/ui/fake-progress.svelte.js +38 -0
- package/dist/profiler/ui/format.d.ts +20 -0
- package/dist/profiler/ui/format.js +71 -0
- package/dist/profiler/ui/permalink.d.ts +9 -0
- package/dist/profiler/ui/permalink.js +108 -0
- package/dist/profiler/ui/press.d.ts +5 -0
- package/dist/profiler/ui/press.js +19 -0
- package/dist/profiler/ui/profiler.css +650 -0
- package/dist/profiler/ui/report-data.d.ts +189 -0
- package/dist/profiler/ui/report-data.js +393 -0
- package/dist/profiler/ui/report-request.d.ts +6 -0
- package/dist/profiler/ui/report-request.js +50 -0
- package/dist/profiler/ui/row-anchor.svelte.d.ts +12 -0
- package/dist/profiler/ui/row-anchor.svelte.js +44 -0
- package/dist/profiler/ui/sort.svelte.d.ts +14 -0
- package/dist/profiler/ui/sort.svelte.js +37 -0
- package/dist/profiler/ui/store.d.ts +45 -0
- package/dist/profiler/ui/store.js +92 -0
- package/dist/profiler/visit.d.ts +389 -0
- package/dist/profiler/visit.js +1002 -0
- package/dist/report.d.ts +5 -0
- package/dist/report.js +4 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +38 -0
- package/dist/vite.d.ts +12 -0
- package/dist/vite.js +78 -0
- package/package.json +105 -6
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { devtools_switched_on, testing_lean } from "@ogygia/core/insights";
|
|
2
|
+
// src/devtools/leftovers.ts
|
|
3
|
+
/**
|
|
4
|
+
* WHAT AN ISLAND LEFT RUNNING AFTER IT LEFT THE PAGE. The router keeps the document across
|
|
5
|
+
* navigations, so an interval an island started, or a listener it put on `window` or `document`, and
|
|
6
|
+
* never took back outlives the island: it keeps running (often on state that is gone), holds the
|
|
7
|
+
* island's closure in memory, and every navigation back to its page adds another one.
|
|
8
|
+
*
|
|
9
|
+
* Dev tool only (installed from the runtime's boot behind the DEVTOOLS gate, before any island
|
|
10
|
+
* wakes): `setInterval` / `clearInterval` and `addEventListener` / `removeEventListener` on `window`
|
|
11
|
+
* and `document` are watched. Every other target passes straight through (one identity check). Each
|
|
12
|
+
* watched registration keeps the first files of its stack; the island whose code made it is decided
|
|
13
|
+
* only when the report asks (`owned_leftovers`, pure). A registration counts as left behind when an
|
|
14
|
+
* instance of its island existed when it was made and none of the instances alive now did: the one
|
|
15
|
+
* that made it is gone, and it is still set.
|
|
16
|
+
*
|
|
17
|
+
* Only the island's own code, one or two calls away, owns a registration: Svelte's own listeners
|
|
18
|
+
* (its event delegation on `document`, set while a component's module evaluates) sit deeper in the
|
|
19
|
+
* stack, and are Svelte's to keep.
|
|
20
|
+
*/
|
|
21
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
22
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
23
|
+
const islands = /* @__PURE__ */ new Map();
|
|
24
|
+
let own_file = "";
|
|
25
|
+
let installed = false;
|
|
26
|
+
/** The file URL of each stack line (Chrome `at f (url:1:2)` / `at url:1:2`, Firefox and Safari
|
|
27
|
+
* `f@url:1:2`), without its position or query. No regex: this runs on every watched registration. */
|
|
28
|
+
function stack_files(stack) {
|
|
29
|
+
const out = [];
|
|
30
|
+
let from = 0;
|
|
31
|
+
while (from < stack.length) {
|
|
32
|
+
let end = stack.indexOf("\n", from);
|
|
33
|
+
if (end === -1) end = stack.length;
|
|
34
|
+
let line = stack.slice(from, end).trim();
|
|
35
|
+
from = end + 1;
|
|
36
|
+
if (line.endsWith(")")) {
|
|
37
|
+
const open = line.lastIndexOf("(");
|
|
38
|
+
if (open !== -1) line = line.slice(open + 1, -1);
|
|
39
|
+
} else {
|
|
40
|
+
const at = line.lastIndexOf("@");
|
|
41
|
+
if (at !== -1) line = line.slice(at + 1);
|
|
42
|
+
else if (line.startsWith("at ")) line = line.slice(3);
|
|
43
|
+
}
|
|
44
|
+
for (let k = 0; k < 2; k++) {
|
|
45
|
+
const c = line.lastIndexOf(":");
|
|
46
|
+
if (c === -1) break;
|
|
47
|
+
const tail = line.slice(c + 1);
|
|
48
|
+
if (!tail.length || tail.charCodeAt(0) < 48 || tail.charCodeAt(0) > 57) break;
|
|
49
|
+
line = line.slice(0, c);
|
|
50
|
+
}
|
|
51
|
+
const q = line.indexOf("?");
|
|
52
|
+
if (q !== -1) line = line.slice(0, q);
|
|
53
|
+
if (line.includes("://")) out.push(line);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/** The first `n` files above the watcher (the caller, and its caller). */
|
|
58
|
+
function callers(n = 2) {
|
|
59
|
+
const files = stack_files((/* @__PURE__ */ new Error()).stack ?? "");
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const f of files) {
|
|
62
|
+
if (f === own_file) continue;
|
|
63
|
+
out.push(f);
|
|
64
|
+
if (out.length === n) break;
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
const DEVTOOLS_LAZY = typeof __OGYGIA_DEVTOOLS_LAZY__ !== "undefined" ? __OGYGIA_DEVTOOLS_LAZY__ : false;
|
|
69
|
+
/** Watch intervals and the global listeners. Once per page; a no-op off the browser. */
|
|
70
|
+
function watch_leftovers() {
|
|
71
|
+
if (installed || typeof window === "undefined" || typeof EventTarget === "undefined") return;
|
|
72
|
+
if (DEVTOOLS_LAZY && !devtools_switched_on()) return;
|
|
73
|
+
if (testing_lean()) return;
|
|
74
|
+
installed = true;
|
|
75
|
+
own_file = stack_files((/* @__PURE__ */ new Error()).stack ?? "")[0] ?? "";
|
|
76
|
+
const set_interval = window.setInterval;
|
|
77
|
+
const clear_interval = window.clearInterval;
|
|
78
|
+
window.setInterval = function(fn, ms, ...args) {
|
|
79
|
+
if (typeof fn !== "function") return set_interval.call(window, fn, ms, ...args);
|
|
80
|
+
const reg = {
|
|
81
|
+
kind: "interval",
|
|
82
|
+
frames: callers(),
|
|
83
|
+
t: performance.now(),
|
|
84
|
+
fires: 0
|
|
85
|
+
};
|
|
86
|
+
const id = set_interval.call(window, function(...a) {
|
|
87
|
+
reg.fires++;
|
|
88
|
+
reg.last = performance.now();
|
|
89
|
+
return fn.apply(this, a);
|
|
90
|
+
}, ms, ...args);
|
|
91
|
+
intervals.set(id, reg);
|
|
92
|
+
return id;
|
|
93
|
+
};
|
|
94
|
+
window.clearInterval = function(id) {
|
|
95
|
+
intervals.delete(id);
|
|
96
|
+
return clear_interval.call(window, id);
|
|
97
|
+
};
|
|
98
|
+
const proto = EventTarget.prototype;
|
|
99
|
+
const add = proto.addEventListener;
|
|
100
|
+
const remove = proto.removeEventListener;
|
|
101
|
+
proto.addEventListener = function(type, fn, opts) {
|
|
102
|
+
const on = this ?? window;
|
|
103
|
+
if ((on === window || on === document) && fn) {
|
|
104
|
+
const o = typeof opts === "object" && opts ? opts : null;
|
|
105
|
+
if (!o?.once && !o?.signal?.aborted) note_listener(on, type, fn, o ? !!o.capture : !!opts, o?.signal);
|
|
106
|
+
}
|
|
107
|
+
if (fn && SCROLL_TYPES.has(type)) note_scroll_listener(on, type, fn, opts);
|
|
108
|
+
if (fn && type === "unload" && on === window) note_unload_listener(fn);
|
|
109
|
+
return add.call(on, type, fn, opts);
|
|
110
|
+
};
|
|
111
|
+
proto.removeEventListener = function(type, fn, opts) {
|
|
112
|
+
const on = this ?? window;
|
|
113
|
+
if ((on === window || on === document) && fn) forget_listener(on, type, fn, typeof opts === "object" && opts ? !!opts.capture : !!opts);
|
|
114
|
+
if (fn && SCROLL_TYPES.has(type)) forget_scroll_listener(on, type, fn);
|
|
115
|
+
if (fn && type === "unload" && on === window) {
|
|
116
|
+
const i = unload_regs.findIndex((r) => r.fn === fn);
|
|
117
|
+
if (i !== -1) unload_regs.splice(i, 1);
|
|
118
|
+
}
|
|
119
|
+
return remove.call(on, type, fn, opts);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const SCROLL_TYPES = /* @__PURE__ */ new Set([
|
|
123
|
+
"wheel",
|
|
124
|
+
"mousewheel",
|
|
125
|
+
"touchstart",
|
|
126
|
+
"touchmove"
|
|
127
|
+
]);
|
|
128
|
+
const scroll_regs = [];
|
|
129
|
+
function note_scroll_listener(on, type, fn, opts) {
|
|
130
|
+
const passive = typeof opts === "object" && opts ? opts.passive : void 0;
|
|
131
|
+
const root = on === window || on === document || on === document.documentElement || on === document.body;
|
|
132
|
+
if (passive === true || root && passive !== false || scroll_regs.length >= 40) return;
|
|
133
|
+
if (scroll_regs.some((r) => r.target === on && r.type === type && r.fn === fn)) return;
|
|
134
|
+
let desc = on === window ? "window" : on === document ? "document" : "";
|
|
135
|
+
if (!desc && on instanceof Element) {
|
|
136
|
+
let cls = "";
|
|
137
|
+
for (const c of on.classList) if (!c.startsWith("svelte-")) {
|
|
138
|
+
cls = c;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
desc = `${on.tagName.toLowerCase()}${on.id ? `#${on.id}` : cls ? `.${cls}` : ""}`;
|
|
142
|
+
}
|
|
143
|
+
const frames = callers(6);
|
|
144
|
+
if (!frames.length) return;
|
|
145
|
+
scroll_regs.push({
|
|
146
|
+
type,
|
|
147
|
+
on: desc || "an element",
|
|
148
|
+
forced: passive === false,
|
|
149
|
+
frames,
|
|
150
|
+
target: on,
|
|
151
|
+
fn
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function forget_scroll_listener(on, type, fn) {
|
|
155
|
+
const i = scroll_regs.findIndex((r) => r.target === on && r.type === type && r.fn === fn);
|
|
156
|
+
if (i !== -1) scroll_regs.splice(i, 1);
|
|
157
|
+
}
|
|
158
|
+
const unload_regs = [];
|
|
159
|
+
function note_unload_listener(fn) {
|
|
160
|
+
if (unload_regs.length >= 20 || unload_regs.some((r) => r.fn === fn)) return;
|
|
161
|
+
const frames = callers(6);
|
|
162
|
+
if (frames.length) unload_regs.push({
|
|
163
|
+
fn,
|
|
164
|
+
frames
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/** The window `unload` listeners still attached, as scroll registrations (the same owner rule). */
|
|
168
|
+
function unload_listeners() {
|
|
169
|
+
return unload_regs.map((r) => ({
|
|
170
|
+
type: "unload",
|
|
171
|
+
on: "window",
|
|
172
|
+
forced: false,
|
|
173
|
+
frames: r.frames
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
/** The scroll-holding listeners still attached (an element's, while it is in the page). */
|
|
177
|
+
function scroll_listeners() {
|
|
178
|
+
return scroll_regs.filter((r) => !(r.target instanceof Element) || r.target.isConnected).map(({ target: _t, fn: _f, ...r }) => r);
|
|
179
|
+
}
|
|
180
|
+
/** Each one's owner, the island whose file is the nearest of its callers (as owned_leftovers
|
|
181
|
+
* decides), else the first caller's file. PURE. */
|
|
182
|
+
function owned_scroll_listeners(regs, isls, name_of) {
|
|
183
|
+
const stem = (url) => {
|
|
184
|
+
const file = url.slice(url.lastIndexOf("/") + 1);
|
|
185
|
+
const dot = file.indexOf(".");
|
|
186
|
+
return dot === -1 ? file : file.slice(0, dot);
|
|
187
|
+
};
|
|
188
|
+
const named = isls.map((i) => ({
|
|
189
|
+
...i,
|
|
190
|
+
name: name_of(i.entry)
|
|
191
|
+
}));
|
|
192
|
+
return regs.map((r) => {
|
|
193
|
+
let owner = "";
|
|
194
|
+
for (const f of r.frames) {
|
|
195
|
+
const hit = named.find((i) => i.urls.includes(f)) ?? (f.endsWith(".svelte") ? named.find((i) => i.name === stem(f)) : void 0);
|
|
196
|
+
if (hit) {
|
|
197
|
+
owner = hit.name;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!owner) {
|
|
202
|
+
const f = r.frames[0] ?? "";
|
|
203
|
+
owner = f ? f.slice(f.lastIndexOf("/") + 1) : "a script";
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
owner,
|
|
207
|
+
type: r.type,
|
|
208
|
+
on: r.on,
|
|
209
|
+
forced: r.forced
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
function note_listener(on, type, fn, capture, signal) {
|
|
214
|
+
const list = listeners.get(fn) ?? [];
|
|
215
|
+
if (list.some((r) => r.on === on && r.type === type && r.capture === capture)) return;
|
|
216
|
+
const frames = callers();
|
|
217
|
+
list.push({
|
|
218
|
+
kind: "listener",
|
|
219
|
+
on,
|
|
220
|
+
target: on === window ? "window" : "document",
|
|
221
|
+
type,
|
|
222
|
+
capture,
|
|
223
|
+
frames,
|
|
224
|
+
t: performance.now(),
|
|
225
|
+
fires: 0
|
|
226
|
+
});
|
|
227
|
+
listeners.set(fn, list);
|
|
228
|
+
signal?.addEventListener("abort", () => forget_listener(on, type, fn, capture), { once: true });
|
|
229
|
+
}
|
|
230
|
+
function forget_listener(on, type, fn, capture) {
|
|
231
|
+
const list = listeners.get(fn);
|
|
232
|
+
if (!list) return;
|
|
233
|
+
const i = list.findIndex((r) => r.on === on && r.type === type && r.capture === capture);
|
|
234
|
+
if (i !== -1) list.splice(i, 1);
|
|
235
|
+
if (!list.length) listeners.delete(fn);
|
|
236
|
+
}
|
|
237
|
+
/** An island instance starts (its hydrate begins): `entry` and its file locations. */
|
|
238
|
+
function leftover_island_seen(el, entry, urls) {
|
|
239
|
+
let isl = islands.get(entry);
|
|
240
|
+
if (!isl) islands.set(entry, isl = {
|
|
241
|
+
urls,
|
|
242
|
+
first: performance.now(),
|
|
243
|
+
els: /* @__PURE__ */ new Map()
|
|
244
|
+
});
|
|
245
|
+
for (const u of urls) if (!isl.urls.includes(u)) isl.urls.push(u);
|
|
246
|
+
if (!isl.els.has(el)) isl.els.set(el, performance.now());
|
|
247
|
+
}
|
|
248
|
+
/** An island instance left the page (its region disconnected and its app was disposed). */
|
|
249
|
+
function leftover_island_gone(el, entry) {
|
|
250
|
+
const isl = islands.get(entry);
|
|
251
|
+
if (!isl || !isl.els.delete(el)) return;
|
|
252
|
+
isl.gone = performance.now();
|
|
253
|
+
}
|
|
254
|
+
/** Everything watched, as it stands now (for `owned_leftovers`). */
|
|
255
|
+
function leftover_state() {
|
|
256
|
+
const regs = [...intervals.values()];
|
|
257
|
+
for (const list of listeners.values()) for (const { on: _, ...r } of list) regs.push(r);
|
|
258
|
+
const out = [];
|
|
259
|
+
for (const [entry, isl] of islands) {
|
|
260
|
+
const live = [];
|
|
261
|
+
for (const [el, t] of isl.els) if (el.isConnected) live.push(t);
|
|
262
|
+
out.push({
|
|
263
|
+
entry,
|
|
264
|
+
urls: isl.urls,
|
|
265
|
+
live,
|
|
266
|
+
first: isl.first,
|
|
267
|
+
...isl.gone !== void 0 ? { gone: isl.gone } : {}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
regs,
|
|
272
|
+
islands: out
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Per island, what it left behind (PURE). A registration's owner is the island whose file is one of
|
|
277
|
+
* its first two callers: by location (a build's file, the entry), else by name (dev serves the
|
|
278
|
+
* component's own `.svelte` file, named as the island is). Left behind: an instance of that island
|
|
279
|
+
* was alive before it was made, and every instance alive now started after it.
|
|
280
|
+
*/
|
|
281
|
+
function owned_leftovers(regs, isls, name_of, now) {
|
|
282
|
+
const named = isls.map((i) => ({
|
|
283
|
+
...i,
|
|
284
|
+
name: name_of(i.entry)
|
|
285
|
+
}));
|
|
286
|
+
const stem = (url) => {
|
|
287
|
+
const file = url.slice(url.lastIndexOf("/") + 1);
|
|
288
|
+
const dot = file.indexOf(".");
|
|
289
|
+
return dot === -1 ? file : file.slice(0, dot);
|
|
290
|
+
};
|
|
291
|
+
const by_name = /* @__PURE__ */ new Map();
|
|
292
|
+
for (const r of regs) {
|
|
293
|
+
let owners = [];
|
|
294
|
+
for (const f of r.frames) {
|
|
295
|
+
owners = named.filter((i) => i.urls.includes(f));
|
|
296
|
+
if (!owners.length && f.endsWith(".svelte")) owners = named.filter((i) => i.name === stem(f));
|
|
297
|
+
if (owners.length) break;
|
|
298
|
+
}
|
|
299
|
+
if (!owners.length) continue;
|
|
300
|
+
if (!owners.some((i) => i.first <= r.t) || owners.some((i) => i.live.some((t) => t <= r.t))) continue;
|
|
301
|
+
if (!owners.some((i) => i.gone !== void 0 && i.gone >= r.t)) continue;
|
|
302
|
+
const name = owners[0].name;
|
|
303
|
+
const l = by_name.get(name) ?? {
|
|
304
|
+
name,
|
|
305
|
+
entry: owners[0].entry,
|
|
306
|
+
intervals: 0,
|
|
307
|
+
listeners: [],
|
|
308
|
+
fires: 0
|
|
309
|
+
};
|
|
310
|
+
if (r.kind === "interval") {
|
|
311
|
+
l.intervals++;
|
|
312
|
+
l.fires += r.fires;
|
|
313
|
+
if (r.last !== void 0) l.last_ago = Math.min(l.last_ago ?? Infinity, Math.round(now - r.last));
|
|
314
|
+
} else l.listeners.push(`${r.target} '${r.type}'`);
|
|
315
|
+
by_name.set(name, l);
|
|
316
|
+
}
|
|
317
|
+
return [...by_name.values()];
|
|
318
|
+
}
|
|
319
|
+
export { leftover_island_gone, leftover_island_seen, leftover_state, owned_leftovers, owned_scroll_listeners, scroll_listeners, stack_files, unload_listeners, watch_leftovers };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ensure_region_names(): Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { names } from "virtual:ogygia/devtools-meta";
|
|
2
|
+
// src/devtools/names.ts
|
|
3
|
+
/**
|
|
4
|
+
* The islands' component names for the dock and the testing API: a build carries them in
|
|
5
|
+
* `virtual:ogygia/devtools-meta`; the dev server serves them live (`/__ogygia_devtools_meta`, the
|
|
6
|
+
* registry as it is now). Both land on `window.__ogygia_region_names`, which regions.ts reads.
|
|
7
|
+
* Once per page; best effort (a hash stands in for a name it cannot find).
|
|
8
|
+
*/
|
|
9
|
+
let loading = null;
|
|
10
|
+
function ensure_region_names() {
|
|
11
|
+
if (loading) return loading;
|
|
12
|
+
loading = (async () => {
|
|
13
|
+
if (typeof window === "undefined") return;
|
|
14
|
+
if (names && Object.keys(names).length) {
|
|
15
|
+
window.__ogygia_region_names = {
|
|
16
|
+
...names,
|
|
17
|
+
...window.__ogygia_region_names
|
|
18
|
+
};
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const r = await fetch("/__ogygia_devtools_meta");
|
|
23
|
+
if (!r.ok) return;
|
|
24
|
+
const meta = await r.json();
|
|
25
|
+
if (meta?.names) window.__ogygia_region_names = {
|
|
26
|
+
...meta.names,
|
|
27
|
+
...window.__ogygia_region_names
|
|
28
|
+
};
|
|
29
|
+
if (meta?.bytes) window.__ogygia_region_bytes = meta.bytes;
|
|
30
|
+
} catch {}
|
|
31
|
+
})();
|
|
32
|
+
return loading;
|
|
33
|
+
}
|
|
34
|
+
export { ensure_region_names };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { StylesFinding, StylesReport } from "./styles.js";
|
|
2
|
+
import { CacheProbe } from "./cache-headers.js";
|
|
3
|
+
import { SinceLoad } from "./since-load.js";
|
|
4
|
+
import { CpuSummary, Failure, HeldOpen, HoleBatch, HoleFailure, HoleWait, PageInput, PageReport, PartedVital, RegionFact, VitalPart } from "@ogygia/core/insights";
|
|
5
|
+
import { BeaconPage } from "@ogygia/core/internal/instruments";
|
|
6
|
+
export interface PageView {
|
|
7
|
+
page: BeaconPage;
|
|
8
|
+
report: PageReport;
|
|
9
|
+
regions: RegionFact[];
|
|
10
|
+
/** the page-load CPU trace, analyzed (null: the browser could not sample, or a navigation since) */
|
|
11
|
+
cpu: CpuSummary | null;
|
|
12
|
+
/** the last in-app navigation: the report covers only what happened since it */
|
|
13
|
+
nav: {
|
|
14
|
+
to: string;
|
|
15
|
+
t: number;
|
|
16
|
+
} | null;
|
|
17
|
+
/** what this browser cannot measure (a finding needing it cannot appear here) */
|
|
18
|
+
unmeasured: string[];
|
|
19
|
+
/** against the previous load of this same page (this tab, this session): what changed */
|
|
20
|
+
since: SinceLoad | null;
|
|
21
|
+
/** the split vitals' parts (the document's own load only): what the next load's "since" compares */
|
|
22
|
+
parts?: Partial<Record<PartedVital, VitalPart[]>>;
|
|
23
|
+
/** after an in-app navigation: islands the router KEPT from the page before (the same island with
|
|
24
|
+
* the same props on both pages — still awake, no code, no hydrate). Without them, a page whose
|
|
25
|
+
* islands were all reused read as "nothing woke" */
|
|
26
|
+
kept?: {
|
|
27
|
+
fp: string;
|
|
28
|
+
name: string;
|
|
29
|
+
}[];
|
|
30
|
+
/** each hole's first answer since the page (or the navigation): its wait, split where the
|
|
31
|
+
* browser and the server timed it — the Page tab's hole waterfall */
|
|
32
|
+
holes?: HoleWait[];
|
|
33
|
+
/** holes whose answer never came (the waterfall's red rows) */
|
|
34
|
+
holes_failed?: HoleFailure[];
|
|
35
|
+
}
|
|
36
|
+
export declare function region_facts(): RegionFact[];
|
|
37
|
+
/** This page's hydration failures, each island once: after an in-app navigation, the ones since it,
|
|
38
|
+
* and an earlier one only while its region is still on the page and still not awake (a kept
|
|
39
|
+
* island). Every visit to a page with a broken island failed it again: without this, the tab listed
|
|
40
|
+
* one failure per visit, by fingerprint, on pages that do not have the island at all. */
|
|
41
|
+
export declare function failures(since?: number): Failure[];
|
|
42
|
+
/** How long each hole's fallback showed before its first answer (since the last navigation), and
|
|
43
|
+
* whether it sits on the first screen. */
|
|
44
|
+
export declare function hole_waits(since?: number, fcp?: number): HoleWait[];
|
|
45
|
+
/** Batch requests that carried fewer holes than they were sent for (since `since`). */
|
|
46
|
+
export declare function hole_batches(since?: number): HoleBatch[];
|
|
47
|
+
/** Holes whose answer never came: the last failure of each (no retry after it), unless an answer
|
|
48
|
+
* arrived since. In a build the runtime says nothing and the page just keeps the fallback. */
|
|
49
|
+
export declare function hole_failures(since?: number): HoleFailure[];
|
|
50
|
+
/** The CPU summary of a kept trace, against this page's island windows. */
|
|
51
|
+
export declare function cpu_of(page: BeaconPage, trace: unknown): CpuSummary | null;
|
|
52
|
+
/** The last in-app navigation (the router's body swap): where to, and when it started. */
|
|
53
|
+
/** This document's streamed promises, each by its `page.data` key (the seed) and when its resolve
|
|
54
|
+
* arrived (the inline bootstrap's stamp): what held the document open. Null: nothing streamed. */
|
|
55
|
+
export declare function held_open(): HeldOpen | null;
|
|
56
|
+
/** The router's last navigation that SWAPPED the page (its start time, its address). One that started
|
|
57
|
+
* and never finished — it fell back to a full load the browser did not make (a test holding document
|
|
58
|
+
* loads back), or a later one overtook it — changed nothing: this is still the page it was. */
|
|
59
|
+
export declare function last_nav(): {
|
|
60
|
+
to: string;
|
|
61
|
+
t: number;
|
|
62
|
+
} | null;
|
|
63
|
+
/**
|
|
64
|
+
* After an in-app navigation the beacon's visit keeps the whole document's story on one clock: the
|
|
65
|
+
* first page's islands, shifts and long tasks, then this page's. The tab is about THIS page, so its
|
|
66
|
+
* events are cut to those since the navigation and shifted to start there; the paints and the
|
|
67
|
+
* vitals stay the document's (a soft navigation paints nothing new the browser reports).
|
|
68
|
+
*/
|
|
69
|
+
export declare function since_nav(page: BeaconPage, t: number): PageInput;
|
|
70
|
+
/**
|
|
71
|
+
* What THIS browser cannot measure (Safari has no layout-shift, long-task or interaction timing;
|
|
72
|
+
* only Chromium has the JS sampler and long animation frames). A finding that needs one of them
|
|
73
|
+
* cannot appear here — its absence is not a clean bill, and the tab says so.
|
|
74
|
+
*/
|
|
75
|
+
export declare function unmeasured(cpu_off?: string | null): string[];
|
|
76
|
+
/** the load's CPU with the source's lines, in every list the tabs and findings quote */
|
|
77
|
+
export declare function summary_lines(s: CpuSummary | null): CpuSummary | null;
|
|
78
|
+
export declare function read_page(): PageView | null;
|
|
79
|
+
declare global {
|
|
80
|
+
interface Window {
|
|
81
|
+
__ogygia_page?: () => PageView | null;
|
|
82
|
+
/** the Record tab's last session report (tests read what the tab shows) */
|
|
83
|
+
__ogygia_session?: import("@ogygia/core/insights").SessionReport;
|
|
84
|
+
/** the Page tab's styles check, run fresh (tests read what the tab shows) */
|
|
85
|
+
__ogygia_styles?: () => Promise<{
|
|
86
|
+
report: StylesReport;
|
|
87
|
+
findings: StylesFinding[];
|
|
88
|
+
}>;
|
|
89
|
+
/** the Page tab's cache check (how the host caches the content-named files), run fresh */
|
|
90
|
+
__ogygia_cache?: () => Promise<{
|
|
91
|
+
probes: CacheProbe[];
|
|
92
|
+
findings: StylesFinding[];
|
|
93
|
+
}>;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export declare function install_page_hook(): void;
|