@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,146 @@
|
|
|
1
|
+
<script>import { fmt_ms, CATEGORY_COLOR } from "./format.js";
|
|
2
|
+
let { alloc, gc = [] } = $props();
|
|
3
|
+
const W = 960;
|
|
4
|
+
const H = 90;
|
|
5
|
+
const s = $derived(alloc.samples);
|
|
6
|
+
const t_max = $derived(Math.max(s[s.length - 1]?.t ?? 1, 1));
|
|
7
|
+
const mbs = $derived(s.map((x) => x.mb));
|
|
8
|
+
const lo = $derived(Math.min(...mbs));
|
|
9
|
+
const hi = $derived(Math.max(...mbs));
|
|
10
|
+
const range = $derived(Math.max(hi - lo, 1));
|
|
11
|
+
const x_of = (t) => t / t_max * W;
|
|
12
|
+
const y_of = (mb) => H - 6 - (mb - lo) / range * (H - 14);
|
|
13
|
+
const path = $derived(s.map((p, i) => `${i ? "L" : "M"}${x_of(p.t).toFixed(1)},${y_of(p.mb).toFixed(1)}`).join(" "));
|
|
14
|
+
const win = $derived(alloc.window ?? null);
|
|
15
|
+
const color = (c) => CATEGORY_COLOR[c] ?? "#6b7280";
|
|
16
|
+
const on_window = (t) => win ? t >= win.offset_ms && t <= win.offset_ms + win.ms : false;
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<svg viewBox="0 0 {W} {H + 16}" class="chart" role="img" aria-label="used heap over the recording">
|
|
20
|
+
{#if win}
|
|
21
|
+
<rect
|
|
22
|
+
x={x_of(win.offset_ms)}
|
|
23
|
+
y="0"
|
|
24
|
+
width={Math.max(1, x_of(win.offset_ms + win.ms) - x_of(win.offset_ms))}
|
|
25
|
+
height={H}
|
|
26
|
+
fill="#6fe3b0"
|
|
27
|
+
opacity="0.1"
|
|
28
|
+
/>
|
|
29
|
+
{/if}
|
|
30
|
+
{#each gc as g, i (i)}
|
|
31
|
+
<rect
|
|
32
|
+
x={x_of(g.t)}
|
|
33
|
+
y="0"
|
|
34
|
+
width={Math.max(1, x_of(g.t + g.ms) - x_of(g.t))}
|
|
35
|
+
height={H}
|
|
36
|
+
fill={g.kind === 'major' ? '#f85149' : '#d29922'}
|
|
37
|
+
opacity="0.55"
|
|
38
|
+
>
|
|
39
|
+
<title>{g.kind} GC, {fmt_ms(g.ms)} ms at {fmt_ms(g.t)} ms</title>
|
|
40
|
+
</rect>
|
|
41
|
+
{/each}
|
|
42
|
+
{#each alloc.bursts as b, i (i)}
|
|
43
|
+
<rect
|
|
44
|
+
x={x_of(b.t0)}
|
|
45
|
+
y={H - 4}
|
|
46
|
+
width={Math.max(2, x_of(b.t1) - x_of(b.t0))}
|
|
47
|
+
height="4"
|
|
48
|
+
fill="#f0883e"
|
|
49
|
+
>
|
|
50
|
+
<title
|
|
51
|
+
>+{b.mb} MB in {fmt_ms(b.t1 - b.t0)} ms ({b.rate} MB/s){b.running[0]
|
|
52
|
+
? ` while ${b.running[0].label} ran`
|
|
53
|
+
: ''}</title
|
|
54
|
+
>
|
|
55
|
+
</rect>
|
|
56
|
+
{/each}
|
|
57
|
+
<path d={path} fill="none" stroke="#6ca8e0" stroke-width="1.5" />
|
|
58
|
+
<text x="2" y="11" font-size="10" fill="#6f8378">{Math.round(hi)} MB</text>
|
|
59
|
+
<text x="2" y={H - 8} font-size="10" fill="#6f8378">{Math.round(lo)} MB</text>
|
|
60
|
+
<text x={W - 2} y={H + 12} font-size="10" fill="#6f8378" text-anchor="end"
|
|
61
|
+
>{fmt_ms(t_max)} ms</text
|
|
62
|
+
>
|
|
63
|
+
{#if win}
|
|
64
|
+
<text x={x_of(win.offset_ms) + 3} y={H + 12} font-size="10" fill="#6fe3b0"
|
|
65
|
+
>the render this report explains</text
|
|
66
|
+
>
|
|
67
|
+
{/if}
|
|
68
|
+
</svg>
|
|
69
|
+
<p class="hint">
|
|
70
|
+
The heap grew <b>{alloc.grown_mb} MB</b> over the intervals that grew, sampled every ~{alloc.period_ms}
|
|
71
|
+
ms when the loop yielded (the longest stretch without a sample, a synchronous run, was {fmt_ms(
|
|
72
|
+
alloc.longest_gap_ms
|
|
73
|
+
)} ms). Orange marks under the line are the bursts below; red and yellow bars are the collections —
|
|
74
|
+
a burst with one inside allocated more than the line shows.
|
|
75
|
+
</p>
|
|
76
|
+
{#if alloc.bursts.length}
|
|
77
|
+
<table>
|
|
78
|
+
<thead
|
|
79
|
+
><tr
|
|
80
|
+
><th>when</th><th class="num">grew</th><th class="num">rate</th><th>what was running</th
|
|
81
|
+
></tr
|
|
82
|
+
></thead
|
|
83
|
+
>
|
|
84
|
+
<tbody>
|
|
85
|
+
{#each alloc.bursts as b, i (i)}
|
|
86
|
+
<tr>
|
|
87
|
+
<td class="num"
|
|
88
|
+
>{fmt_ms(b.t0)}–{fmt_ms(b.t1)} ms{#if on_window(b.t0)}<span
|
|
89
|
+
class="hint"
|
|
90
|
+
title="inside the render this report explains"
|
|
91
|
+
>
|
|
92
|
+
●</span
|
|
93
|
+
>{/if}</td
|
|
94
|
+
>
|
|
95
|
+
<td class="num"
|
|
96
|
+
><b>+{b.mb} MB</b>{#if b.gc}<span
|
|
97
|
+
class="hint"
|
|
98
|
+
title="a collection fell inside: the allocation was more than the growth"
|
|
99
|
+
>
|
|
100
|
+
+GC</span
|
|
101
|
+
>{/if}</td
|
|
102
|
+
>
|
|
103
|
+
<td class="num">{b.rate} MB/s</td>
|
|
104
|
+
<td class="fn">
|
|
105
|
+
{#each b.running as r, k (k)}
|
|
106
|
+
<span class="who"
|
|
107
|
+
><i style="background:{color(r.category)}"></i>{r.label}
|
|
108
|
+
<span class="hint"
|
|
109
|
+
>{Math.round(r.share * 100)}%{#if r.file}
|
|
110
|
+
· {r.file}{/if}</span
|
|
111
|
+
></span
|
|
112
|
+
>
|
|
113
|
+
{:else}
|
|
114
|
+
<span class="hint"
|
|
115
|
+
>nothing on the CPU (the growth came from a native side or between samples)</span
|
|
116
|
+
>
|
|
117
|
+
{/each}
|
|
118
|
+
</td>
|
|
119
|
+
</tr>
|
|
120
|
+
{/each}
|
|
121
|
+
</tbody>
|
|
122
|
+
</table>
|
|
123
|
+
{/if}
|
|
124
|
+
|
|
125
|
+
<style>
|
|
126
|
+
.chart {
|
|
127
|
+
width: 100%;
|
|
128
|
+
height: auto;
|
|
129
|
+
display: block;
|
|
130
|
+
background: var(--bg-sunken);
|
|
131
|
+
border: 1px solid var(--line);
|
|
132
|
+
border-radius: var(--r-sm);
|
|
133
|
+
}
|
|
134
|
+
.who {
|
|
135
|
+
display: inline-flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
gap: 5px;
|
|
138
|
+
margin-right: 12px;
|
|
139
|
+
}
|
|
140
|
+
.who i {
|
|
141
|
+
width: 8px;
|
|
142
|
+
height: 8px;
|
|
143
|
+
border-radius: 2px;
|
|
144
|
+
display: inline-block;
|
|
145
|
+
}
|
|
146
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHEN THE HEAP GREW — the used heap sampled finely over the capture (alloc.ts), the GC
|
|
3
|
+
* pauses as drops, the one window the report explains lit, and the fastest-growing
|
|
4
|
+
* stretches listed with what the CPU was running then. Static: nothing to click.
|
|
5
|
+
*/
|
|
6
|
+
import type { AllocTimeline } from '../alloc.js';
|
|
7
|
+
type $$ComponentProps = {
|
|
8
|
+
alloc: AllocTimeline;
|
|
9
|
+
gc?: {
|
|
10
|
+
t: number;
|
|
11
|
+
ms: number;
|
|
12
|
+
kind: string;
|
|
13
|
+
}[];
|
|
14
|
+
};
|
|
15
|
+
declare const AllocStrip: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
16
|
+
type AllocStrip = ReturnType<typeof AllocStrip>;
|
|
17
|
+
export default AllocStrip;
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
<script>import { fmt_ms } from "./format.js";
|
|
2
|
+
import { simulate_awaits } from "../insights.js";
|
|
3
|
+
let { nodes, edges, window_ms } = $props();
|
|
4
|
+
const W = 1100;
|
|
5
|
+
const LANE = 26;
|
|
6
|
+
const H = 18;
|
|
7
|
+
const PAD = 8;
|
|
8
|
+
const LEFT = 8;
|
|
9
|
+
const x = (t) => LEFT + t / Math.max(window_ms, .01) * (W - 2 * LEFT);
|
|
10
|
+
const lanes = $derived(nodes.reduce((m, n) => Math.max(m, n.lane + 1), 1));
|
|
11
|
+
const height = $derived(PAD * 2 + lanes * LANE);
|
|
12
|
+
const by_label = $derived.by(() => {
|
|
13
|
+
const m = new Map();
|
|
14
|
+
for (const n of nodes) (m.get(n.label) ?? m.set(n.label, []).get(n.label)).push(n);
|
|
15
|
+
return m;
|
|
16
|
+
});
|
|
17
|
+
// an edge joins the last box of `from` that ended before the first box of `to` starting after it
|
|
18
|
+
const placed = $derived(edges.map((e) => {
|
|
19
|
+
const froms = by_label.get(e.from) ?? [];
|
|
20
|
+
const tos = by_label.get(e.to) ?? [];
|
|
21
|
+
let best = null;
|
|
22
|
+
for (const b of tos) for (const a of froms) if (a.t1 <= b.t0 + .05 && (!best || a.t1 > best.a.t1)) best = {
|
|
23
|
+
a,
|
|
24
|
+
b
|
|
25
|
+
};
|
|
26
|
+
return best ? {
|
|
27
|
+
e,
|
|
28
|
+
...best
|
|
29
|
+
} : null;
|
|
30
|
+
}).filter((p) => !!p));
|
|
31
|
+
// fixed mid-tone series colours (SVG `fill` attributes can't take a CSS var); they read on both themes
|
|
32
|
+
const color = (kind) => kind === "net" ? "#6ca8e0" : "#e0a06f";
|
|
33
|
+
const short = (s, px) => {
|
|
34
|
+
const max = Math.max(3, Math.floor(px / 6.6));
|
|
35
|
+
return s.length > max ? s.slice(0, max - 1) + "…" : s;
|
|
36
|
+
};
|
|
37
|
+
let tip = $state(null);
|
|
38
|
+
const show = (text, ev) => tip = {
|
|
39
|
+
text,
|
|
40
|
+
x: ev.clientX,
|
|
41
|
+
y: ev.clientY
|
|
42
|
+
};
|
|
43
|
+
const serialized_ms = $derived(edges.reduce((s, e) => s + e.gap_ms, 0));
|
|
44
|
+
// WHAT IF: change a call and see the render under the model (insights.ts)
|
|
45
|
+
let changes = $state({});
|
|
46
|
+
const labels = $derived([...new Set(nodes.map((n) => n.label))]);
|
|
47
|
+
const sim = $derived(Object.keys(changes).length ? simulate_awaits(nodes, edges, window_ms, changes) : null);
|
|
48
|
+
const set_change = (label, c) => {
|
|
49
|
+
const next = { ...changes };
|
|
50
|
+
if (c === null || next[label] === c) delete next[label];
|
|
51
|
+
else next[label] = c;
|
|
52
|
+
changes = next;
|
|
53
|
+
};
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<div class="legend">
|
|
57
|
+
<span><i style="background:{color('net')}"></i> HTTP call</span>
|
|
58
|
+
<span><i style="background:{color('timer')}"></i> timer / file / socket</span>
|
|
59
|
+
<span
|
|
60
|
+
><i class="arrow"></i> started only once the other finished — the await that serialized them</span
|
|
61
|
+
>
|
|
62
|
+
<span class="hint"
|
|
63
|
+
>{edges.length} serialized start{edges.length === 1 ? '' : 's'} · boxes in the same column ran together</span
|
|
64
|
+
>
|
|
65
|
+
</div>
|
|
66
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
67
|
+
<div
|
|
68
|
+
class="scroll"
|
|
69
|
+
onmouseleave={() => (tip = null)}
|
|
70
|
+
tabindex="0"
|
|
71
|
+
role="region"
|
|
72
|
+
aria-label="the render's calls, scrolls sideways"
|
|
73
|
+
>
|
|
74
|
+
<svg
|
|
75
|
+
width={W}
|
|
76
|
+
{height}
|
|
77
|
+
viewBox="0 0 {W} {height}"
|
|
78
|
+
role="img"
|
|
79
|
+
aria-label="the render's calls and which one waited for which"
|
|
80
|
+
>
|
|
81
|
+
<defs>
|
|
82
|
+
<marker id="aw-head" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"
|
|
83
|
+
><path d="M0,0 L8,4 L0,8 z" fill="#e0834a" /></marker
|
|
84
|
+
>
|
|
85
|
+
</defs>
|
|
86
|
+
{#each nodes as n, i (i)}
|
|
87
|
+
<g
|
|
88
|
+
class="node"
|
|
89
|
+
transform="translate({x(n.t0)},{PAD + n.lane * LANE})"
|
|
90
|
+
onmouseenter={(ev) =>
|
|
91
|
+
show(
|
|
92
|
+
`${n.label} — ${fmt_ms(n.t1 - n.t0)} ms, from ${fmt_ms(n.t0)} to ${fmt_ms(n.t1)} ms${n.caller ? ` · from ${n.caller}` : ''}`,
|
|
93
|
+
ev
|
|
94
|
+
)}
|
|
95
|
+
onmousemove={(ev) =>
|
|
96
|
+
show(
|
|
97
|
+
`${n.label} — ${fmt_ms(n.t1 - n.t0)} ms, from ${fmt_ms(n.t0)} to ${fmt_ms(n.t1)} ms${n.caller ? ` · from ${n.caller}` : ''}`,
|
|
98
|
+
ev
|
|
99
|
+
)}
|
|
100
|
+
role="img"
|
|
101
|
+
aria-label="{n.label} — {fmt_ms(n.t1 - n.t0)} ms, from {fmt_ms(n.t0)} to {fmt_ms(n.t1)} ms"
|
|
102
|
+
>
|
|
103
|
+
<rect
|
|
104
|
+
width={Math.max(2, x(n.t1) - x(n.t0))}
|
|
105
|
+
height={H}
|
|
106
|
+
rx="3"
|
|
107
|
+
fill={color(n.kind)}
|
|
108
|
+
opacity="0.85"
|
|
109
|
+
/>
|
|
110
|
+
<text x="4" y="13">{short(n.label, x(n.t1) - x(n.t0) - 6)}</text>
|
|
111
|
+
</g>
|
|
112
|
+
{/each}
|
|
113
|
+
{#each placed as p, i (i)}
|
|
114
|
+
{@const x1 = x(p.a.t1)}
|
|
115
|
+
{@const y1 = PAD + p.a.lane * LANE + H / 2}
|
|
116
|
+
{@const x2 = x(p.b.t0)}
|
|
117
|
+
{@const y2 = PAD + p.b.lane * LANE + H / 2}
|
|
118
|
+
<path
|
|
119
|
+
class="edge"
|
|
120
|
+
d={p.a.lane === p.b.lane
|
|
121
|
+
? `M${x1},${y1} L${x2},${y2}`
|
|
122
|
+
: `M${x1},${y1} C${x1 + 12},${y1} ${x2 - 12},${y2} ${x2},${y2}`}
|
|
123
|
+
marker-end="url(#aw-head)"
|
|
124
|
+
onmouseenter={(ev) =>
|
|
125
|
+
show(
|
|
126
|
+
`${p.e.to} started ${fmt_ms(p.e.gap_ms)} ms after ${p.e.from} finished${p.e.at ? ` — awaited at ${p.e.at}` : ''}${p.e.callers && p.e.callers.length > 1 ? ` (${p.e.callers.join(' ← ')})` : ''}`,
|
|
127
|
+
ev
|
|
128
|
+
)}
|
|
129
|
+
onmousemove={(ev) =>
|
|
130
|
+
show(
|
|
131
|
+
`${p.e.to} started ${fmt_ms(p.e.gap_ms)} ms after ${p.e.from} finished${p.e.at ? ` — awaited at ${p.e.at}` : ''}`,
|
|
132
|
+
ev
|
|
133
|
+
)}
|
|
134
|
+
role="img"
|
|
135
|
+
aria-label="{p.e.to} started {fmt_ms(p.e.gap_ms)} ms after {p.e.from} finished"
|
|
136
|
+
/>
|
|
137
|
+
{/each}
|
|
138
|
+
</svg>
|
|
139
|
+
</div>
|
|
140
|
+
{#if tip}
|
|
141
|
+
<div
|
|
142
|
+
class="tip"
|
|
143
|
+
style="left:{Math.min(
|
|
144
|
+
tip.x + 14,
|
|
145
|
+
(typeof window !== 'undefined' ? window.innerWidth : 1200) - 420
|
|
146
|
+
)}px;top:{tip.y + 16}px"
|
|
147
|
+
>
|
|
148
|
+
{tip.text}
|
|
149
|
+
</div>
|
|
150
|
+
{/if}
|
|
151
|
+
{#if placed.length}
|
|
152
|
+
<ol class="sites">
|
|
153
|
+
{#each placed.slice(0, 12) as p, i (i)}
|
|
154
|
+
<li>
|
|
155
|
+
<b>{p.e.to}</b> waited for <b>{p.e.from}</b>{#if p.e.at}
|
|
156
|
+
— at <code>{p.e.at}</code>{/if}{#if p.e.gap_ms >= 1}
|
|
157
|
+
<span class="hint">({fmt_ms(p.e.gap_ms)} ms between)</span>{/if}
|
|
158
|
+
</li>
|
|
159
|
+
{/each}
|
|
160
|
+
{#if placed.length > 12}<li class="hint">and {placed.length - 12} more</li>{/if}
|
|
161
|
+
</ol>
|
|
162
|
+
<p class="hint">
|
|
163
|
+
{serialized_ms >= 1 ? `${fmt_ms(serialized_ms)} ms sat between serialized calls. ` : ''}Each
|
|
164
|
+
line is an <code>await</code> that could start its call earlier: move the call above the await it
|
|
165
|
+
waits behind, and the two overlap.
|
|
166
|
+
</p>
|
|
167
|
+
{:else}
|
|
168
|
+
<p class="hint">
|
|
169
|
+
No call waited for another: every call started while the previous ones were still in flight.
|
|
170
|
+
</p>
|
|
171
|
+
{/if}
|
|
172
|
+
|
|
173
|
+
{#if labels.length}
|
|
174
|
+
<details class="whatif">
|
|
175
|
+
<summary
|
|
176
|
+
>What if… <span class="hint"
|
|
177
|
+
>change a call and see the render under the model: parallel starts it with the call it
|
|
178
|
+
waited for, cache makes it instant, remove takes it out</span
|
|
179
|
+
></summary
|
|
180
|
+
>
|
|
181
|
+
<div class="whatif-grid">
|
|
182
|
+
{#each labels.slice(0, 40) as label (label)}
|
|
183
|
+
<div class="row">
|
|
184
|
+
<span class="lbl">{label}</span>
|
|
185
|
+
<button
|
|
186
|
+
class:on={changes[label] === 'parallel'}
|
|
187
|
+
onclick={() => set_change(label, 'parallel')}>parallel</button
|
|
188
|
+
>
|
|
189
|
+
<button class:on={changes[label] === 'cache'} onclick={() => set_change(label, 'cache')}
|
|
190
|
+
>cache</button
|
|
191
|
+
>
|
|
192
|
+
<button class:on={changes[label] === 'remove'} onclick={() => set_change(label, 'remove')}
|
|
193
|
+
>remove</button
|
|
194
|
+
>
|
|
195
|
+
</div>
|
|
196
|
+
{/each}
|
|
197
|
+
</div>
|
|
198
|
+
{#if sim}
|
|
199
|
+
<p class="result">
|
|
200
|
+
<b>{fmt_ms(sim.before_ms)} ms → {fmt_ms(sim.after_ms)} ms</b>
|
|
201
|
+
<span class={sim.delta_ms < 0 ? 'better' : 'worse'}
|
|
202
|
+
>({sim.delta_ms > 0 ? '+' : ''}{fmt_ms(sim.delta_ms)} ms)</span
|
|
203
|
+
>
|
|
204
|
+
<button class="link" onclick={() => (changes = {})}>reset</button>
|
|
205
|
+
</p>
|
|
206
|
+
{#if sim.chain.length}<p class="hint">
|
|
207
|
+
The longest chain of waits would then be: {sim.chain.join(' → ')}. The CPU between calls
|
|
208
|
+
is kept as recorded; only the waits move.
|
|
209
|
+
</p>{/if}
|
|
210
|
+
{/if}
|
|
211
|
+
</details>
|
|
212
|
+
{/if}
|
|
213
|
+
|
|
214
|
+
<style>
|
|
215
|
+
.whatif {
|
|
216
|
+
margin-top: 10px;
|
|
217
|
+
border: 1px solid var(--line);
|
|
218
|
+
border-radius: var(--r-md);
|
|
219
|
+
padding: 8px 12px;
|
|
220
|
+
}
|
|
221
|
+
.whatif summary {
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
font-weight: 600;
|
|
224
|
+
}
|
|
225
|
+
.whatif-grid {
|
|
226
|
+
display: grid;
|
|
227
|
+
grid-template-columns: 1fr;
|
|
228
|
+
gap: 3px;
|
|
229
|
+
margin-top: 8px;
|
|
230
|
+
max-height: 260px;
|
|
231
|
+
max-width: 560px;
|
|
232
|
+
overflow: auto;
|
|
233
|
+
}
|
|
234
|
+
.whatif .row {
|
|
235
|
+
display: grid;
|
|
236
|
+
grid-template-columns: minmax(0, 1fr) auto auto auto;
|
|
237
|
+
gap: 6px;
|
|
238
|
+
align-items: center;
|
|
239
|
+
font-size: 12px;
|
|
240
|
+
}
|
|
241
|
+
.whatif .lbl {
|
|
242
|
+
white-space: nowrap;
|
|
243
|
+
overflow: hidden;
|
|
244
|
+
text-overflow: ellipsis;
|
|
245
|
+
font-family: var(--font-mono);
|
|
246
|
+
}
|
|
247
|
+
.whatif button {
|
|
248
|
+
background: var(--bg-hover);
|
|
249
|
+
border: 1px solid var(--line-strong);
|
|
250
|
+
color: var(--text-dim);
|
|
251
|
+
border-radius: var(--r-sm);
|
|
252
|
+
padding: 1px 7px;
|
|
253
|
+
font: inherit;
|
|
254
|
+
font-size: 11px;
|
|
255
|
+
cursor: pointer;
|
|
256
|
+
}
|
|
257
|
+
.whatif button:hover {
|
|
258
|
+
border-color: var(--accent-line);
|
|
259
|
+
color: var(--text);
|
|
260
|
+
}
|
|
261
|
+
.whatif button.on {
|
|
262
|
+
border-color: var(--accent);
|
|
263
|
+
background: var(--accent-deep);
|
|
264
|
+
color: var(--accent-strong);
|
|
265
|
+
}
|
|
266
|
+
.whatif .result {
|
|
267
|
+
margin: 8px 0 2px;
|
|
268
|
+
font-size: 13px;
|
|
269
|
+
}
|
|
270
|
+
.whatif .link {
|
|
271
|
+
border: 0;
|
|
272
|
+
background: none;
|
|
273
|
+
text-decoration: underline;
|
|
274
|
+
margin-left: 8px;
|
|
275
|
+
color: var(--accent);
|
|
276
|
+
cursor: pointer;
|
|
277
|
+
}
|
|
278
|
+
.better {
|
|
279
|
+
color: var(--good);
|
|
280
|
+
}
|
|
281
|
+
.worse {
|
|
282
|
+
color: var(--bad);
|
|
283
|
+
}
|
|
284
|
+
.legend {
|
|
285
|
+
display: flex;
|
|
286
|
+
flex-wrap: wrap;
|
|
287
|
+
gap: 4px 14px;
|
|
288
|
+
margin: 6px 0 4px;
|
|
289
|
+
font-size: 11.5px;
|
|
290
|
+
color: var(--text-dim);
|
|
291
|
+
}
|
|
292
|
+
.legend span {
|
|
293
|
+
display: inline-flex;
|
|
294
|
+
align-items: center;
|
|
295
|
+
gap: 5px;
|
|
296
|
+
}
|
|
297
|
+
.legend i {
|
|
298
|
+
width: 12px;
|
|
299
|
+
height: 12px;
|
|
300
|
+
border-radius: 2px;
|
|
301
|
+
display: inline-block;
|
|
302
|
+
}
|
|
303
|
+
.legend i.arrow {
|
|
304
|
+
width: 18px;
|
|
305
|
+
height: 2px;
|
|
306
|
+
background: var(--c-orange);
|
|
307
|
+
}
|
|
308
|
+
.legend .hint {
|
|
309
|
+
flex-basis: 100%;
|
|
310
|
+
color: var(--text-faint);
|
|
311
|
+
}
|
|
312
|
+
.scroll {
|
|
313
|
+
overflow-x: auto;
|
|
314
|
+
background: var(--bg-sunken);
|
|
315
|
+
border: 1px solid var(--line);
|
|
316
|
+
border-radius: var(--r-md);
|
|
317
|
+
}
|
|
318
|
+
svg {
|
|
319
|
+
display: block;
|
|
320
|
+
font-family: var(--font-mono);
|
|
321
|
+
font-size: 10.5px;
|
|
322
|
+
}
|
|
323
|
+
.node text {
|
|
324
|
+
fill: #06120c;
|
|
325
|
+
font-weight: 600;
|
|
326
|
+
pointer-events: none;
|
|
327
|
+
}
|
|
328
|
+
.tip {
|
|
329
|
+
position: fixed;
|
|
330
|
+
pointer-events: none;
|
|
331
|
+
background: var(--bg-raised);
|
|
332
|
+
color: var(--text);
|
|
333
|
+
border: 1px solid var(--line-strong);
|
|
334
|
+
border-radius: var(--r-sm);
|
|
335
|
+
padding: 6px 10px;
|
|
336
|
+
font-size: 12px;
|
|
337
|
+
max-width: 480px;
|
|
338
|
+
z-index: 10;
|
|
339
|
+
box-shadow: var(--shadow-panel);
|
|
340
|
+
}
|
|
341
|
+
.edge {
|
|
342
|
+
fill: none;
|
|
343
|
+
stroke: var(--c-orange);
|
|
344
|
+
stroke-width: 1.6;
|
|
345
|
+
cursor: help;
|
|
346
|
+
}
|
|
347
|
+
.edge:hover {
|
|
348
|
+
stroke-width: 3;
|
|
349
|
+
}
|
|
350
|
+
.sites {
|
|
351
|
+
margin: 8px 0 0;
|
|
352
|
+
padding-left: 22px;
|
|
353
|
+
font-size: 12.5px;
|
|
354
|
+
line-height: 1.6;
|
|
355
|
+
}
|
|
356
|
+
.sites code {
|
|
357
|
+
font-size: 11.5px;
|
|
358
|
+
}
|
|
359
|
+
.hint {
|
|
360
|
+
color: var(--text-faint);
|
|
361
|
+
font-size: 12px;
|
|
362
|
+
}
|
|
363
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE CAUSALITY GRAPH of a render's waits (timeline.ts `awaits`): every call is a box on the
|
|
3
|
+
* render's clock, overlapping calls in their own lanes, and an arrow wherever a call started
|
|
4
|
+
* only once another had finished — the await that serialized them, labelled with the line that
|
|
5
|
+
* did the waiting. Parallel calls read as stacked boxes with no arrow between them. Hover a box
|
|
6
|
+
* or an arrow for the detail. A `wake:'load'` island.
|
|
7
|
+
*/
|
|
8
|
+
import type { AwaitNode, AwaitEdge } from '../timeline.js';
|
|
9
|
+
type $$ComponentProps = {
|
|
10
|
+
nodes: AwaitNode[];
|
|
11
|
+
edges: AwaitEdge[];
|
|
12
|
+
window_ms: number;
|
|
13
|
+
};
|
|
14
|
+
declare const Awaits: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
15
|
+
type Awaits = ReturnType<typeof Awaits>;
|
|
16
|
+
export default Awaits;
|