@ogygia/devtools 0.0.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +99 -6
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
<script>import { fmt_ms, fmt_bytes, ink_on } from "./format.js";
|
|
2
|
+
import { sortable } from "./sort.svelte.js";
|
|
3
|
+
import { pressable } from "./press.js";
|
|
4
|
+
import { row_id, follow_hash } from "./row-anchor.svelte.js";
|
|
5
|
+
let { rows, hasJs, hasClient } = $props();
|
|
6
|
+
let query = $state("");
|
|
7
|
+
const filtered = $derived.by(() => {
|
|
8
|
+
const q = query.trim().toLowerCase();
|
|
9
|
+
if (!q) return rows;
|
|
10
|
+
return rows.filter((r) => r.name.toLowerCase().includes(q) || r.entry.toLowerCase().includes(q) || r.wake.includes(q));
|
|
11
|
+
});
|
|
12
|
+
const s = sortable(() => filtered, "props_bytes");
|
|
13
|
+
let open = $state(null);
|
|
14
|
+
const max_props = $derived(Math.max(...rows.map((r) => r.props_bytes), 1));
|
|
15
|
+
const max_js = $derived(Math.max(...rows.map((r) => r.js_bytes ?? 0), 1));
|
|
16
|
+
const cols = $derived(9 + (hasJs ? 1 : 0) + (hasClient ? 1 : 0));
|
|
17
|
+
const WAKE_COLOR = {
|
|
18
|
+
load: "#e8734a",
|
|
19
|
+
idle: "#d9a03d",
|
|
20
|
+
visible: "#5b8fd6",
|
|
21
|
+
interaction: "#7ee787",
|
|
22
|
+
none: "#7d8590"
|
|
23
|
+
};
|
|
24
|
+
const wake_color = (w) => WAKE_COLOR[w] ?? "#b48ead";
|
|
25
|
+
const marks_text = (r) => {
|
|
26
|
+
const i = r.interactivity;
|
|
27
|
+
if (!i) return "—";
|
|
28
|
+
if (r.marks === 0) return "inert";
|
|
29
|
+
const parts = [];
|
|
30
|
+
if (i.handlers) parts.push(`${i.handlers} handler${i.handlers === 1 ? "" : "s"}`);
|
|
31
|
+
if (i.state) parts.push(`${i.state} $state`);
|
|
32
|
+
if (i.effects) parts.push(`${i.effects} $effect`);
|
|
33
|
+
if (i.binds) parts.push(`${i.binds} bind:`);
|
|
34
|
+
if (i.actions) parts.push(`${i.actions} use:`);
|
|
35
|
+
return parts.join(" · ");
|
|
36
|
+
};
|
|
37
|
+
// a link names the island by its component; rows open by fingerprint (two copies with different
|
|
38
|
+
// props are two rows): the link opens the first, and only the first carries the row's id
|
|
39
|
+
const first_fp = $derived.by(() => {
|
|
40
|
+
const m = new Map();
|
|
41
|
+
for (const r of rows) if (!m.has(r.name)) m.set(r.name, r.fp);
|
|
42
|
+
return m;
|
|
43
|
+
});
|
|
44
|
+
$effect(() => follow_hash("island", () => rows.map((r) => r.name), (k) => {
|
|
45
|
+
query = "";
|
|
46
|
+
open = first_fp.get(k) ?? null;
|
|
47
|
+
}));
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<div class="tools">
|
|
51
|
+
<input
|
|
52
|
+
type="search"
|
|
53
|
+
placeholder="filter islands…"
|
|
54
|
+
bind:value={query}
|
|
55
|
+
aria-label="filter islands"
|
|
56
|
+
/>
|
|
57
|
+
<span class="hint"
|
|
58
|
+
>{filtered.length} of {rows.length} · click a row for its modules, seed keys and advice</span
|
|
59
|
+
>
|
|
60
|
+
</div>
|
|
61
|
+
<table>
|
|
62
|
+
<thead>
|
|
63
|
+
<tr>
|
|
64
|
+
<th>island</th>
|
|
65
|
+
<th
|
|
66
|
+
class="num sort"
|
|
67
|
+
class:active={s.key === 'copies'}
|
|
68
|
+
onclick={() => s.click('copies')}
|
|
69
|
+
{@attach pressable}
|
|
70
|
+
aria-sort={s.aria('copies')}>copies<span class="arr">{s.arrow('copies')}</span></th
|
|
71
|
+
>
|
|
72
|
+
<th>wake</th>
|
|
73
|
+
<th
|
|
74
|
+
class="num sort"
|
|
75
|
+
class:active={s.key === 'ssr_ms'}
|
|
76
|
+
title="the component's server render time, per render"
|
|
77
|
+
onclick={() => s.click('ssr_ms')}
|
|
78
|
+
{@attach pressable}
|
|
79
|
+
aria-sort={s.aria('ssr_ms')}>SSR ms<span class="arr">{s.arrow('ssr_ms')}</span></th
|
|
80
|
+
>
|
|
81
|
+
<th
|
|
82
|
+
class="num sort"
|
|
83
|
+
class:active={s.key === 'props_bytes'}
|
|
84
|
+
title="the props sidecar as shipped (one per fingerprint)"
|
|
85
|
+
onclick={() => s.click('props_bytes')}
|
|
86
|
+
{@attach pressable}
|
|
87
|
+
aria-sort={s.aria('props_bytes')}>props<span class="arr">{s.arrow('props_bytes')}</span></th
|
|
88
|
+
>
|
|
89
|
+
<th title="JSON parses on the fast lane; devalue when a leaf needs it">lane</th>
|
|
90
|
+
<th
|
|
91
|
+
class="num sort"
|
|
92
|
+
class:active={s.key === 'refs'}
|
|
93
|
+
title="props that point into page.data instead of shipping again"
|
|
94
|
+
onclick={() => s.click('refs')}
|
|
95
|
+
{@attach pressable}
|
|
96
|
+
aria-sort={s.aria('refs')}>seed refs<span class="arr">{s.arrow('refs')}</span></th
|
|
97
|
+
>
|
|
98
|
+
{#if hasJs}<th
|
|
99
|
+
class="num sort"
|
|
100
|
+
class:active={s.key === 'js_sort'}
|
|
101
|
+
title="the island's module and its chunks, shared code included; '· only it' is the part no other island needs"
|
|
102
|
+
onclick={() => s.click('js_sort')}
|
|
103
|
+
{@attach pressable}
|
|
104
|
+
aria-sort={s.aria('js_sort')}>JS<span class="arr">{s.arrow('js_sort')}</span></th
|
|
105
|
+
>{/if}
|
|
106
|
+
<th title="what the build found in the island's components">interactivity</th>
|
|
107
|
+
{#if hasClient}<th
|
|
108
|
+
class="num sort"
|
|
109
|
+
class:active={s.key === 'client_ms'}
|
|
110
|
+
title="wake → hydrated in the browser, p50 (the runtime's beacon)"
|
|
111
|
+
onclick={() => s.click('client_ms')}
|
|
112
|
+
{@attach pressable}
|
|
113
|
+
aria-sort={s.aria('client_ms')}
|
|
114
|
+
>hydrate ms<span class="arr">{s.arrow('client_ms')}</span></th
|
|
115
|
+
>{/if}
|
|
116
|
+
<th><span class="sr-only">details</span></th>
|
|
117
|
+
</tr>
|
|
118
|
+
</thead>
|
|
119
|
+
<tbody>
|
|
120
|
+
{#each s.sorted as r (r.fp)}
|
|
121
|
+
<tr
|
|
122
|
+
class="row"
|
|
123
|
+
id={first_fp.get(r.name) === r.fp ? row_id('island', r.name) : undefined}
|
|
124
|
+
class:open={open === r.fp}
|
|
125
|
+
class:flag={!!r.advice}
|
|
126
|
+
onclick={() => (open = open === r.fp ? null : r.fp)}
|
|
127
|
+
{@attach pressable}
|
|
128
|
+
>
|
|
129
|
+
<td class="fn"><span class="caret">{open === r.fp ? '▾' : '▸'}</span><b>{r.name}</b></td>
|
|
130
|
+
<td class="num">{r.copies > 1 ? `×${r.copies}` : '1'}</td>
|
|
131
|
+
<td
|
|
132
|
+
><span
|
|
133
|
+
class="chip"
|
|
134
|
+
style="background:{wake_color(r.wake)};color:{ink_on(wake_color(r.wake))}"
|
|
135
|
+
>{r.wake}</span
|
|
136
|
+
></td
|
|
137
|
+
>
|
|
138
|
+
<td class="num">{r.ssr_ms === null ? '—' : fmt_ms(r.ssr_ms)}</td>
|
|
139
|
+
<td class="num bar-cell">
|
|
140
|
+
<div class="bar" style="width:{Math.max(2, (r.props_bytes / max_props) * 100)}%"></div>
|
|
141
|
+
<b>{fmt_bytes(r.props_bytes)}</b>
|
|
142
|
+
</td>
|
|
143
|
+
<td
|
|
144
|
+
>{#if r.json}<span class="lane json">json</span>{:else}<span
|
|
145
|
+
class="lane dev"
|
|
146
|
+
title={r.culprit ?? ''}>devalue</span
|
|
147
|
+
>{/if}</td
|
|
148
|
+
>
|
|
149
|
+
<td class="num">{r.refs ? `${r.refs} → ${r.ref_keys.join(', ')}` : '—'}</td>
|
|
150
|
+
{#if hasJs}
|
|
151
|
+
<td class="num bar-cell">
|
|
152
|
+
{#if r.js_bytes !== null}<div
|
|
153
|
+
class="bar js"
|
|
154
|
+
style="width:{Math.max(2, (r.js_bytes / max_js) * 100)}%"
|
|
155
|
+
></div>
|
|
156
|
+
<b>{fmt_bytes(r.js_bytes)}</b>{#if r.js_only !== null}<span
|
|
157
|
+
class="only"
|
|
158
|
+
title="what no other island on the page needs: what dropping or deferring it saves"
|
|
159
|
+
>
|
|
160
|
+
· {fmt_bytes(r.js_only)} only it</span
|
|
161
|
+
>{/if}{:else}—{/if}
|
|
162
|
+
</td>
|
|
163
|
+
{/if}
|
|
164
|
+
<td class="marks" class:inert={r.marks === 0}>{marks_text(r)}</td>
|
|
165
|
+
{#if hasClient}
|
|
166
|
+
<td class="num"
|
|
167
|
+
>{#if r.client}{fmt_ms(r.client.p50_ms)}<span class="hint">
|
|
168
|
+
({r.client.n})</span
|
|
169
|
+
>{#if r.client.recovered}<span
|
|
170
|
+
class="lane dev"
|
|
171
|
+
title="hydrations that discarded the server DOM and re-rendered"
|
|
172
|
+
>
|
|
173
|
+
re-rendered ×{r.client.recovered}</span
|
|
174
|
+
>{/if}{:else}—{/if}</td
|
|
175
|
+
>
|
|
176
|
+
{/if}
|
|
177
|
+
<td class="flagcell"
|
|
178
|
+
>{#if r.advice}<span class="dotflag" title={r.advice}>!</span>{/if}</td
|
|
179
|
+
>
|
|
180
|
+
</tr>
|
|
181
|
+
{#if open === r.fp}
|
|
182
|
+
<tr class="details">
|
|
183
|
+
<td colspan={cols}>
|
|
184
|
+
{#if r.advice}<p class="advice">{r.advice}</p>{/if}
|
|
185
|
+
<div class="grid">
|
|
186
|
+
<div>
|
|
187
|
+
<div class="k">entry</div>
|
|
188
|
+
<code class="mono">{r.entry}</code>
|
|
189
|
+
<div class="k">fingerprint{r.variants > 1 ? 's' : ''}</div>
|
|
190
|
+
<code class="mono"
|
|
191
|
+
>{r.fp}{#if r.variants > 1}
|
|
192
|
+
<span class="hint">and {r.variants - 1} more (each copy has its own props)</span
|
|
193
|
+
>{/if}</code
|
|
194
|
+
>
|
|
195
|
+
<div class="k">props</div>
|
|
196
|
+
<div>
|
|
197
|
+
{fmt_bytes(r.props_bytes)} on the wire{#if r.variants > 1}
|
|
198
|
+
over {r.variants} sidecars, {fmt_bytes(Math.round(r.props_bytes / r.variants))} each{/if}{#if r.canonical_bytes !== r.props_bytes}
|
|
199
|
+
· {fmt_bytes(r.canonical_bytes)} before seed references{/if}{#if r.copies > 1 && r.variants === 1}
|
|
200
|
+
· shared by {r.copies} copies{/if}
|
|
201
|
+
</div>
|
|
202
|
+
{#if r.culprit}<div class="k">devalue because of</div>
|
|
203
|
+
<code class="mono warn">{r.culprit}</code>{/if}
|
|
204
|
+
{#if r.ref_keys.length}<div class="k">points into page.data</div>
|
|
205
|
+
<div>{r.ref_keys.join(', ')}</div>{/if}
|
|
206
|
+
{#if r.client}
|
|
207
|
+
<div class="k">in the browser</div>
|
|
208
|
+
<div>
|
|
209
|
+
{r.client.n} hydration{r.client.n === 1 ? '' : 's'} seen · p50 {fmt_ms(
|
|
210
|
+
r.client.p50_ms
|
|
211
|
+
)} ms, max {fmt_ms(r.client.max_ms)} ms · module load p50 {fmt_ms(
|
|
212
|
+
r.client.load_p50_ms
|
|
213
|
+
)} ms{#if r.client.recovered}
|
|
214
|
+
· <span class="warn"
|
|
215
|
+
>{r.client.recovered} re-rendered after a hydration mismatch</span
|
|
216
|
+
>{/if}
|
|
217
|
+
</div>
|
|
218
|
+
{#if r.client.recovered && r.client.reason}<div class="k">
|
|
219
|
+
why it was recovered
|
|
220
|
+
</div>
|
|
221
|
+
<div class="warn">{r.client.reason}</div>{/if}
|
|
222
|
+
{/if}
|
|
223
|
+
</div>
|
|
224
|
+
<div>
|
|
225
|
+
<div class="k">
|
|
226
|
+
what the browser downloads to wake it{#if r.js_bytes !== null}
|
|
227
|
+
· {fmt_bytes(r.js_bytes)} in all{/if}
|
|
228
|
+
</div>
|
|
229
|
+
<p class="explain">
|
|
230
|
+
Every JavaScript file this island needs before it can run: its own code (marked <b
|
|
231
|
+
>this island</b
|
|
232
|
+
>) plus the shared files it imports — the Svelte runtime, ogygia's hydrate core,
|
|
233
|
+
shared components, libraries. The bundler names shared files by hash. The total is
|
|
234
|
+
what one visitor pays in JS for this island; the biggest file is where to look
|
|
235
|
+
when that is too much.
|
|
236
|
+
</p>
|
|
237
|
+
{#each r.modules as m (m.url)}
|
|
238
|
+
<div class="mod">
|
|
239
|
+
{#if m.bytes !== null}<div
|
|
240
|
+
class="bar js"
|
|
241
|
+
style="width:{Math.max(
|
|
242
|
+
1,
|
|
243
|
+
(m.bytes / Math.max(r.modules[0].bytes ?? 1, 1)) * 100
|
|
244
|
+
)}%"
|
|
245
|
+
></div>{/if}
|
|
246
|
+
<span class="mono"
|
|
247
|
+
>{m.url.replace(/^.*\/_app\/immutable\//, '')}{#if m.own}
|
|
248
|
+
<span class="own">this island</span>{/if}</span
|
|
249
|
+
>
|
|
250
|
+
<span class="num">{m.bytes === null ? '' : fmt_bytes(m.bytes)}</span>
|
|
251
|
+
</div>
|
|
252
|
+
{#if m.inside?.length}
|
|
253
|
+
<div class="inside">{m.inside.join(' · ')}</div>
|
|
254
|
+
{/if}
|
|
255
|
+
{/each}
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
</td>
|
|
259
|
+
</tr>
|
|
260
|
+
{/if}
|
|
261
|
+
{/each}
|
|
262
|
+
</tbody>
|
|
263
|
+
</table>
|
|
264
|
+
|
|
265
|
+
<style>
|
|
266
|
+
.tools {
|
|
267
|
+
display: flex;
|
|
268
|
+
gap: 12px;
|
|
269
|
+
align-items: center;
|
|
270
|
+
flex-wrap: wrap;
|
|
271
|
+
margin: 6px 0 8px;
|
|
272
|
+
}
|
|
273
|
+
.tools input {
|
|
274
|
+
font: inherit;
|
|
275
|
+
font-size: 13px;
|
|
276
|
+
background: var(--bg-raised);
|
|
277
|
+
color: var(--text);
|
|
278
|
+
border: 1px solid var(--line);
|
|
279
|
+
border-radius: 6px;
|
|
280
|
+
padding: 5px 10px;
|
|
281
|
+
min-width: 240px;
|
|
282
|
+
}
|
|
283
|
+
.tools .hint,
|
|
284
|
+
.hint {
|
|
285
|
+
color: var(--text-faint);
|
|
286
|
+
font-size: 12px;
|
|
287
|
+
}
|
|
288
|
+
tr.row {
|
|
289
|
+
cursor: pointer;
|
|
290
|
+
}
|
|
291
|
+
tr.row:hover td {
|
|
292
|
+
background: var(--bg-raised);
|
|
293
|
+
}
|
|
294
|
+
tr.row.open td {
|
|
295
|
+
border-bottom-color: transparent;
|
|
296
|
+
}
|
|
297
|
+
.caret {
|
|
298
|
+
color: var(--text-faint);
|
|
299
|
+
font-size: 10px;
|
|
300
|
+
margin-right: 4px;
|
|
301
|
+
}
|
|
302
|
+
.chip {
|
|
303
|
+
display: inline-block;
|
|
304
|
+
font-size: 11px;
|
|
305
|
+
color: var(--bg-sunken);
|
|
306
|
+
font-weight: 600;
|
|
307
|
+
border-radius: 999px;
|
|
308
|
+
padding: 0 8px;
|
|
309
|
+
line-height: 17px;
|
|
310
|
+
}
|
|
311
|
+
.lane {
|
|
312
|
+
font-size: 11px;
|
|
313
|
+
border-radius: 4px;
|
|
314
|
+
padding: 1px 6px;
|
|
315
|
+
}
|
|
316
|
+
.lane.json {
|
|
317
|
+
color: var(--good);
|
|
318
|
+
border: 1px solid #2a4a33;
|
|
319
|
+
}
|
|
320
|
+
.lane.dev {
|
|
321
|
+
color: var(--warn);
|
|
322
|
+
border: 1px solid #5a4a20;
|
|
323
|
+
}
|
|
324
|
+
.bar-cell .only {
|
|
325
|
+
position: relative;
|
|
326
|
+
opacity: 0.7;
|
|
327
|
+
font-weight: 400;
|
|
328
|
+
}
|
|
329
|
+
.bar-cell {
|
|
330
|
+
position: relative;
|
|
331
|
+
min-width: 90px;
|
|
332
|
+
}
|
|
333
|
+
.bar-cell .bar {
|
|
334
|
+
position: absolute;
|
|
335
|
+
left: 4px;
|
|
336
|
+
right: 4px;
|
|
337
|
+
bottom: 3px;
|
|
338
|
+
height: 3px;
|
|
339
|
+
max-width: calc(100% - 8px);
|
|
340
|
+
background: var(--c-orange);
|
|
341
|
+
border-radius: 2px;
|
|
342
|
+
opacity: 0.7;
|
|
343
|
+
}
|
|
344
|
+
.bar.js {
|
|
345
|
+
background: var(--c-blue);
|
|
346
|
+
}
|
|
347
|
+
.marks {
|
|
348
|
+
font-size: 12px;
|
|
349
|
+
color: var(--text-dim);
|
|
350
|
+
}
|
|
351
|
+
.marks.inert {
|
|
352
|
+
color: var(--warn);
|
|
353
|
+
}
|
|
354
|
+
.flagcell {
|
|
355
|
+
width: 20px;
|
|
356
|
+
}
|
|
357
|
+
.dotflag {
|
|
358
|
+
display: inline-block;
|
|
359
|
+
width: 16px;
|
|
360
|
+
height: 16px;
|
|
361
|
+
line-height: 16px;
|
|
362
|
+
text-align: center;
|
|
363
|
+
border-radius: 50%;
|
|
364
|
+
background: var(--warn);
|
|
365
|
+
color: var(--bg-sunken);
|
|
366
|
+
font-weight: 700;
|
|
367
|
+
font-size: 11px;
|
|
368
|
+
}
|
|
369
|
+
tr.details td {
|
|
370
|
+
background: var(--bg-raised);
|
|
371
|
+
padding: 10px 14px 12px;
|
|
372
|
+
border-bottom: 1px solid var(--line);
|
|
373
|
+
}
|
|
374
|
+
.advice {
|
|
375
|
+
margin: 0 0 8px;
|
|
376
|
+
padding: 6px 10px;
|
|
377
|
+
background: var(--bg-raised);
|
|
378
|
+
border-left: 3px solid var(--warn);
|
|
379
|
+
border-radius: 4px;
|
|
380
|
+
font-size: 12.5px;
|
|
381
|
+
}
|
|
382
|
+
.grid {
|
|
383
|
+
display: grid;
|
|
384
|
+
grid-template-columns: 1fr 1fr;
|
|
385
|
+
gap: 8px 24px;
|
|
386
|
+
font-size: 12.5px;
|
|
387
|
+
}
|
|
388
|
+
.k {
|
|
389
|
+
color: var(--text-faint);
|
|
390
|
+
font-size: 11px;
|
|
391
|
+
text-transform: uppercase;
|
|
392
|
+
letter-spacing: 0.04em;
|
|
393
|
+
margin-top: 6px;
|
|
394
|
+
}
|
|
395
|
+
.mono {
|
|
396
|
+
font-family: ui-monospace, monospace;
|
|
397
|
+
font-size: 11.5px;
|
|
398
|
+
word-break: break-all;
|
|
399
|
+
}
|
|
400
|
+
.mono.warn {
|
|
401
|
+
color: var(--warn);
|
|
402
|
+
}
|
|
403
|
+
.explain {
|
|
404
|
+
margin: 2px 0 6px;
|
|
405
|
+
color: var(--text-dim);
|
|
406
|
+
font-size: 12px;
|
|
407
|
+
line-height: 1.45;
|
|
408
|
+
}
|
|
409
|
+
.own {
|
|
410
|
+
font-size: 10.5px;
|
|
411
|
+
color: var(--c-orange);
|
|
412
|
+
border: 1px solid #5a3a2a;
|
|
413
|
+
border-radius: 999px;
|
|
414
|
+
padding: 0 6px;
|
|
415
|
+
margin-left: 6px;
|
|
416
|
+
}
|
|
417
|
+
.inside {
|
|
418
|
+
margin: -1px 0 4px 8px;
|
|
419
|
+
color: var(--text-faint);
|
|
420
|
+
font-size: 11px;
|
|
421
|
+
word-break: break-all;
|
|
422
|
+
}
|
|
423
|
+
.mod {
|
|
424
|
+
position: relative;
|
|
425
|
+
display: flex;
|
|
426
|
+
justify-content: space-between;
|
|
427
|
+
gap: 10px;
|
|
428
|
+
padding: 2px 4px;
|
|
429
|
+
}
|
|
430
|
+
.mod .bar {
|
|
431
|
+
position: absolute;
|
|
432
|
+
left: 0;
|
|
433
|
+
bottom: 0;
|
|
434
|
+
height: 2px;
|
|
435
|
+
border-radius: 1px;
|
|
436
|
+
opacity: 0.6;
|
|
437
|
+
}
|
|
438
|
+
.mod .num {
|
|
439
|
+
color: var(--text-dim);
|
|
440
|
+
font-variant-numeric: tabular-nums;
|
|
441
|
+
white-space: nowrap;
|
|
442
|
+
}
|
|
443
|
+
@media (max-width: 900px) {
|
|
444
|
+
.grid {
|
|
445
|
+
grid-template-columns: 1fr;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE ISLANDS TABLE — one row per island fingerprint on the profiled page, three sides joined:
|
|
3
|
+
* the server (SSR ms from the components table, props bytes and lane, seed references), the
|
|
4
|
+
* build (JS closure weight, interactivity markers) and the browser (hydration timings from the
|
|
5
|
+
* runtime's beacon). Sortable; a row opens to its modules with bytes, its seed keys, the devalue
|
|
6
|
+
* culprit, and the one line of advice the numbers support. A finding links here by `#island=`.
|
|
7
|
+
*/
|
|
8
|
+
import type { IslandRow } from './report-data.js';
|
|
9
|
+
type $$ComponentProps = {
|
|
10
|
+
rows: IslandRow[];
|
|
11
|
+
hasJs: boolean;
|
|
12
|
+
hasClient: boolean;
|
|
13
|
+
};
|
|
14
|
+
declare const IslandsTable: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
15
|
+
type IslandsTable = ReturnType<typeof IslandsTable>;
|
|
16
|
+
export default IslandsTable;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script>/**
|
|
2
|
+
* KEEP THIS REPORT — a tiny `wake:'load'` island on every server-rendered report page: if this
|
|
3
|
+
* browser does not hold the report yet, fetch its dump on idle and store it (ui/store.ts), so
|
|
4
|
+
* the report survives the server (a restart, or an ephemeral instance). The run page already
|
|
5
|
+
* keeps a fresh recording before opening it; this covers reports opened any other way (a header
|
|
6
|
+
* profile's link, a trap catch on the dashboard, an upload).
|
|
7
|
+
*/
|
|
8
|
+
import { get_report, put_report } from "./store.js";
|
|
9
|
+
import { report_request } from "./report-request.js";
|
|
10
|
+
let { base, id, ogpB64 = "" } = $props();
|
|
11
|
+
let state_ = $state("checking");
|
|
12
|
+
async function keep() {
|
|
13
|
+
try {
|
|
14
|
+
if (await get_report(id)) {
|
|
15
|
+
state_ = "kept";
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
state_ = "keeping";
|
|
19
|
+
// the page's .ogp bytes ride along (report-request.ts): this runs on idle, a separate request
|
|
20
|
+
// that a serverless host routes to any instance, rarely the one that rendered this page
|
|
21
|
+
const res = await report_request(base, id, "dump", ogpB64);
|
|
22
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
23
|
+
const dump = await res.json();
|
|
24
|
+
state_ = await put_report(dump) ? "kept" : "failed";
|
|
25
|
+
} catch {
|
|
26
|
+
state_ = "failed";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const idle = globalThis.requestIdleCallback;
|
|
30
|
+
if (idle) idle(() => void keep(), { timeout: 3e3 });
|
|
31
|
+
else setTimeout(() => void keep(), 500);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<span
|
|
35
|
+
class="keep hint"
|
|
36
|
+
title="Reports are kept in this browser's own storage so they outlive the server (an ephemeral host forgets them within seconds)"
|
|
37
|
+
>
|
|
38
|
+
{#if state_ === 'kept'}kept in this browser{:else if state_ === 'keeping'}keeping in this browser…{:else if state_ === 'failed'}could
|
|
39
|
+
not keep in this browser{:else} {/if}
|
|
40
|
+
</span>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.keep {
|
|
44
|
+
margin-left: 8px;
|
|
45
|
+
}
|
|
46
|
+
</style>
|