@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,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE DATA RIVER — where the page's data comes from and where it goes: upstream calls (ms) feed
|
|
3
|
+
* load functions, load functions produce `page.data` keys (bytes in the seed), keys are read by
|
|
4
|
+
* islands. A wide band is a lot; a band that ends at a key nothing reads is data fetched and
|
|
5
|
+
* shipped for no one. Pure: the host gathers the inputs (the timeline's calls per load lane, the
|
|
6
|
+
* keys each load returns from its source, the keys each island reads from the build, the seed's
|
|
7
|
+
* bytes per key from the rendered document) and this builds the four-column flow.
|
|
8
|
+
*/
|
|
9
|
+
export interface RiverNode {
|
|
10
|
+
id: string;
|
|
11
|
+
/** 0 calls · 1 loads · 2 keys · 3 islands */
|
|
12
|
+
col: 0 | 1 | 2 | 3;
|
|
13
|
+
label: string;
|
|
14
|
+
/** the column's unit: ms for calls and loads, bytes for keys and islands */
|
|
15
|
+
value: number;
|
|
16
|
+
kind: "call" | "load" | "key" | "island" | "unknown";
|
|
17
|
+
detail?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface RiverLink {
|
|
20
|
+
from: string;
|
|
21
|
+
to: string;
|
|
22
|
+
value: number;
|
|
23
|
+
unit: "ms" | "bytes";
|
|
24
|
+
}
|
|
25
|
+
export interface River {
|
|
26
|
+
nodes: RiverNode[];
|
|
27
|
+
links: RiverLink[];
|
|
28
|
+
/** keys in the seed no island reads (only when at least one island names its keys) */
|
|
29
|
+
waste: {
|
|
30
|
+
key: string;
|
|
31
|
+
bytes: number;
|
|
32
|
+
}[];
|
|
33
|
+
notes: string[];
|
|
34
|
+
}
|
|
35
|
+
export interface RiverInput {
|
|
36
|
+
/** the timeline's outbound calls, each on the load lane (file) that made it */
|
|
37
|
+
calls: {
|
|
38
|
+
lane: string | null;
|
|
39
|
+
label: string;
|
|
40
|
+
ms: number;
|
|
41
|
+
}[];
|
|
42
|
+
/** each load lane and the keys its source returns (null: could not tell) */
|
|
43
|
+
lanes: {
|
|
44
|
+
file: string;
|
|
45
|
+
keys: string[] | null;
|
|
46
|
+
ms?: number;
|
|
47
|
+
}[];
|
|
48
|
+
/** each island and the page.data keys it reads (null: reads everything) */
|
|
49
|
+
islands: {
|
|
50
|
+
name: string;
|
|
51
|
+
keys: string[] | null;
|
|
52
|
+
}[];
|
|
53
|
+
/** the seed's bytes per top-level page.data key */
|
|
54
|
+
seed: Record<string, number>;
|
|
55
|
+
}
|
|
56
|
+
export declare function build_river(input: RiverInput): River;
|
|
57
|
+
/** The top-level keys a `load` function's `return { … }` object literals name — the union over
|
|
58
|
+
* every return; null when a return spreads something or returns a non-literal (the keys are then
|
|
59
|
+
* whatever that value has, which the source alone cannot say). */
|
|
60
|
+
export declare function load_return_keys(source: string): string[] | null;
|
|
61
|
+
/** The seed's bytes per top-level `page.data` key, from the seed script's text — plain JSON
|
|
62
|
+
* (`{ data: {…} }` or the keys themselves), or devalue's flat node array (`[{root…}, node, …]`,
|
|
63
|
+
* where an object's values and an array's items are node indices): a key's bytes are then the
|
|
64
|
+
* nodes reachable from it, so a node two keys share counts for both. */
|
|
65
|
+
export declare function seed_key_bytes(seed_json: string): Record<string, number>;
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// src/profiler/river.ts
|
|
2
|
+
const short_call = (label) => {
|
|
3
|
+
const m = /^([A-Z]+) (?:https?:\/\/)?([^/?#]+)?(\/[^?#\s]*)?/.exec(label);
|
|
4
|
+
if (!m) return label.slice(0, 60);
|
|
5
|
+
const path = m[3] ?? "/";
|
|
6
|
+
return `${m[1]} ${path.length > 40 ? "…" + path.slice(-38) : path}`;
|
|
7
|
+
};
|
|
8
|
+
function build_river(input) {
|
|
9
|
+
const nodes = [];
|
|
10
|
+
const links = [];
|
|
11
|
+
const notes = [];
|
|
12
|
+
const node = (n) => {
|
|
13
|
+
nodes.push(n);
|
|
14
|
+
return n.id;
|
|
15
|
+
};
|
|
16
|
+
const lane_ids = /* @__PURE__ */ new Map();
|
|
17
|
+
for (const l of input.lanes) lane_ids.set(l.file, node({
|
|
18
|
+
id: `load:${l.file}`,
|
|
19
|
+
col: 1,
|
|
20
|
+
label: l.file,
|
|
21
|
+
value: 0,
|
|
22
|
+
kind: "load",
|
|
23
|
+
detail: l.keys ? `returns ${l.keys.join(", ")}` : "returns keys the profiler could not read from its source"
|
|
24
|
+
}));
|
|
25
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
26
|
+
for (const c of input.calls) {
|
|
27
|
+
const k = `${c.lane ?? ""}|${short_call(c.label)}`;
|
|
28
|
+
const g = grouped.get(k);
|
|
29
|
+
if (g) {
|
|
30
|
+
g.ms += c.ms;
|
|
31
|
+
g.n++;
|
|
32
|
+
} else grouped.set(k, {
|
|
33
|
+
lane: c.lane,
|
|
34
|
+
label: short_call(c.label),
|
|
35
|
+
ms: c.ms,
|
|
36
|
+
n: 1
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
let other_lane = null;
|
|
40
|
+
for (const [k, g] of grouped) {
|
|
41
|
+
const id = node({
|
|
42
|
+
id: `call:${k}`,
|
|
43
|
+
col: 0,
|
|
44
|
+
label: g.n > 1 ? `${g.label} ×${g.n}` : g.label,
|
|
45
|
+
value: g.ms,
|
|
46
|
+
kind: "call",
|
|
47
|
+
detail: `${Math.round(g.ms)} ms waiting${g.n > 1 ? ` over ${g.n} calls` : ""}`
|
|
48
|
+
});
|
|
49
|
+
let to = g.lane ? lane_ids.get(g.lane) : void 0;
|
|
50
|
+
if (!to) {
|
|
51
|
+
other_lane ??= node({
|
|
52
|
+
id: "load:?",
|
|
53
|
+
col: 1,
|
|
54
|
+
label: "outside a load",
|
|
55
|
+
value: 0,
|
|
56
|
+
kind: "unknown",
|
|
57
|
+
detail: "a call made outside any load function (a hook, a component)"
|
|
58
|
+
});
|
|
59
|
+
to = other_lane;
|
|
60
|
+
}
|
|
61
|
+
links.push({
|
|
62
|
+
from: id,
|
|
63
|
+
to,
|
|
64
|
+
value: g.ms,
|
|
65
|
+
unit: "ms"
|
|
66
|
+
});
|
|
67
|
+
nodes.find((n) => n.id === to).value += g.ms;
|
|
68
|
+
}
|
|
69
|
+
const key_ids = /* @__PURE__ */ new Map();
|
|
70
|
+
const key_node = (key) => {
|
|
71
|
+
let id = key_ids.get(key);
|
|
72
|
+
if (!id) key_ids.set(key, id = node({
|
|
73
|
+
id: `key:${key}`,
|
|
74
|
+
col: 2,
|
|
75
|
+
label: key,
|
|
76
|
+
value: input.seed[key] ?? 0,
|
|
77
|
+
kind: "key",
|
|
78
|
+
detail: input.seed[key] !== void 0 ? `${Math.round(input.seed[key] / 1024 * 10) / 10} KB in the seed` : "not in the seed (no island reads it, or it never shipped)"
|
|
79
|
+
}));
|
|
80
|
+
return id;
|
|
81
|
+
};
|
|
82
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
83
|
+
for (const l of input.lanes) {
|
|
84
|
+
const from = lane_ids.get(l.file);
|
|
85
|
+
if (l.keys) for (const k of l.keys) {
|
|
86
|
+
claimed.add(k);
|
|
87
|
+
links.push({
|
|
88
|
+
from,
|
|
89
|
+
to: key_node(k),
|
|
90
|
+
value: Math.max(input.seed[k] ?? 0, 1),
|
|
91
|
+
unit: "bytes"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const unclaimed = Object.keys(input.seed).filter((k) => !claimed.has(k));
|
|
96
|
+
if (unclaimed.length) {
|
|
97
|
+
const from = input.lanes.find((l) => l.keys === null) ? lane_ids.get(input.lanes.find((l) => l.keys === null).file) : node({
|
|
98
|
+
id: "load:above",
|
|
99
|
+
col: 1,
|
|
100
|
+
label: "a load outside the window",
|
|
101
|
+
value: 0,
|
|
102
|
+
kind: "unknown",
|
|
103
|
+
detail: "a layout load that ran earlier, or one whose source the profiler could not read"
|
|
104
|
+
});
|
|
105
|
+
for (const k of unclaimed) links.push({
|
|
106
|
+
from,
|
|
107
|
+
to: key_node(k),
|
|
108
|
+
value: Math.max(input.seed[k] ?? 0, 1),
|
|
109
|
+
unit: "bytes"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const named = input.islands.some((i) => i.keys && i.keys.length);
|
|
113
|
+
const read = /* @__PURE__ */ new Set();
|
|
114
|
+
for (const i of input.islands) {
|
|
115
|
+
const id = node({
|
|
116
|
+
id: `island:${i.name}`,
|
|
117
|
+
col: 3,
|
|
118
|
+
label: i.name,
|
|
119
|
+
value: 0,
|
|
120
|
+
kind: "island",
|
|
121
|
+
detail: i.keys ? `reads ${i.keys.length ? i.keys.join(", ") : "nothing from page.data"}` : "reads page.data through a helper the build could not follow: the whole seed ships for it"
|
|
122
|
+
});
|
|
123
|
+
const keys = i.keys ?? Object.keys(input.seed);
|
|
124
|
+
for (const k of keys) {
|
|
125
|
+
if (input.seed[k] === void 0 && !key_ids.has(k)) continue;
|
|
126
|
+
read.add(k);
|
|
127
|
+
const bytes = Math.max(input.seed[k] ?? 0, 1);
|
|
128
|
+
links.push({
|
|
129
|
+
from: key_node(k),
|
|
130
|
+
to: id,
|
|
131
|
+
value: bytes,
|
|
132
|
+
unit: "bytes"
|
|
133
|
+
});
|
|
134
|
+
nodes.find((n) => n.id === id).value += bytes;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const waste = named ? Object.entries(input.seed).filter(([k]) => !read.has(k)).map(([key, bytes]) => ({
|
|
138
|
+
key,
|
|
139
|
+
bytes
|
|
140
|
+
})).sort((a, b) => b.bytes - a.bytes) : [];
|
|
141
|
+
if (waste.length) notes.push(`${waste.length} key${waste.length === 1 ? "" : "s"} in the seed that no island reads: ${waste.slice(0, 4).map((w) => `${w.key} (${Math.round(w.bytes / 1024 * 10) / 10} KB)`).join(", ")}.`);
|
|
142
|
+
if (!named && input.islands.length) notes.push("No island names the keys it reads, so every key ships to every island. Read page.data keys directly in the island (or pass them as props) and the seed shrinks to what is used.");
|
|
143
|
+
return {
|
|
144
|
+
nodes,
|
|
145
|
+
links,
|
|
146
|
+
waste,
|
|
147
|
+
notes
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const LOAD_HEAD_RE = /export\s+(?:async\s+)?(?:function\s+load\s*\(|const\s+load\s*(?::[^=]+)?=)/;
|
|
151
|
+
const KEY_RE = /^(?:(?:"([^"]*)")|(?:'([^']*)')|([A-Za-z_$][\w$]*))\s*(?::|,|$|\})/;
|
|
152
|
+
/** The top-level keys a `load` function's `return { … }` object literals name — the union over
|
|
153
|
+
* every return; null when a return spreads something or returns a non-literal (the keys are then
|
|
154
|
+
* whatever that value has, which the source alone cannot say). */
|
|
155
|
+
function load_return_keys(source) {
|
|
156
|
+
const head = LOAD_HEAD_RE.exec(source);
|
|
157
|
+
if (!head) return null;
|
|
158
|
+
const body = source.slice(head.index);
|
|
159
|
+
const keys = /* @__PURE__ */ new Set();
|
|
160
|
+
let found = false;
|
|
161
|
+
let unknown = false;
|
|
162
|
+
const RETURN_RE = /\breturn\s*(\{|\(|[^\s;{(])/g;
|
|
163
|
+
let m;
|
|
164
|
+
while (m = RETURN_RE.exec(body)) {
|
|
165
|
+
found = true;
|
|
166
|
+
let at = m.index + m[0].length - 1;
|
|
167
|
+
if (body[at] === "(") {
|
|
168
|
+
let j = at + 1;
|
|
169
|
+
while (j < body.length && /\s/.test(body[j])) j++;
|
|
170
|
+
if (body[j] !== "{") {
|
|
171
|
+
unknown = true;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
at = j;
|
|
175
|
+
}
|
|
176
|
+
if (body[at] !== "{") {
|
|
177
|
+
unknown = true;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
let depth = 0;
|
|
181
|
+
let i = at;
|
|
182
|
+
let expect_key = true;
|
|
183
|
+
while (i < body.length) {
|
|
184
|
+
const ch = body[i];
|
|
185
|
+
if (depth === 1 && expect_key && !/\s/.test(ch) && !(ch === "/" && (body[i + 1] === "/" || body[i + 1] === "*"))) {
|
|
186
|
+
if (body.startsWith("...", i)) {
|
|
187
|
+
unknown = true;
|
|
188
|
+
expect_key = false;
|
|
189
|
+
i += 3;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const km = KEY_RE.exec(body.slice(i, i + 200));
|
|
193
|
+
if (km) {
|
|
194
|
+
keys.add(km[1] ?? km[2] ?? km[3]);
|
|
195
|
+
expect_key = false;
|
|
196
|
+
i += km[0].length - (km[0].endsWith("}") || km[0].endsWith(",") ? 1 : 0);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
expect_key = false;
|
|
200
|
+
}
|
|
201
|
+
if (ch === "\"" || ch === "'" || ch === "`") {
|
|
202
|
+
const q = ch;
|
|
203
|
+
i++;
|
|
204
|
+
while (i < body.length && body[i] !== q) i += body[i] === "\\" ? 2 : 1;
|
|
205
|
+
i++;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (ch === "/" && body[i + 1] === "/") {
|
|
209
|
+
while (i < body.length && body[i] !== "\n") i++;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (ch === "/" && body[i + 1] === "*") {
|
|
213
|
+
i = body.indexOf("*/", i + 2);
|
|
214
|
+
if (i === -1) i = body.length;
|
|
215
|
+
i += 2;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (ch === "{" || ch === "(" || ch === "[") {
|
|
219
|
+
depth++;
|
|
220
|
+
if (depth === 1) expect_key = true;
|
|
221
|
+
i++;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (ch === "}" || ch === ")" || ch === "]") {
|
|
225
|
+
depth--;
|
|
226
|
+
if (depth === 0) break;
|
|
227
|
+
i++;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (depth === 1 && ch === ",") expect_key = true;
|
|
231
|
+
i++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!found || unknown) return null;
|
|
235
|
+
return [...keys];
|
|
236
|
+
}
|
|
237
|
+
/** The seed's bytes per top-level `page.data` key, from the seed script's text — plain JSON
|
|
238
|
+
* (`{ data: {…} }` or the keys themselves), or devalue's flat node array (`[{root…}, node, …]`,
|
|
239
|
+
* where an object's values and an array's items are node indices): a key's bytes are then the
|
|
240
|
+
* nodes reachable from it, so a node two keys share counts for both. */
|
|
241
|
+
function seed_key_bytes(seed_json) {
|
|
242
|
+
try {
|
|
243
|
+
const data = JSON.parse(seed_json);
|
|
244
|
+
if (Array.isArray(data) && data.length && data[0] && typeof data[0] === "object" && !Array.isArray(data[0])) return devalue_key_bytes(data);
|
|
245
|
+
const obj = (data && typeof data === "object" && !Array.isArray(data) ? data : null) ?? null;
|
|
246
|
+
if (!obj) return {};
|
|
247
|
+
const inner = (obj.data && typeof obj.data === "object" ? obj.data : null) ?? obj.page?.data ?? obj;
|
|
248
|
+
const out = {};
|
|
249
|
+
for (const [k, v] of Object.entries(inner)) out[k] = JSON.stringify(v)?.length ?? 0;
|
|
250
|
+
return out;
|
|
251
|
+
} catch {
|
|
252
|
+
return {};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** devalue's typed arrays: `["Date", "…"]`, `["Map", i, j…]`, `["Set", i…]`, `["Object", i]`,
|
|
256
|
+
* `["RegExp", …]`, `["BigInt", …]`, `["null"]` — the first element names the type, the rest are
|
|
257
|
+
* indices for the container kinds and plain values for the others */
|
|
258
|
+
const DEVALUE_CONTAINERS = /* @__PURE__ */ new Set([
|
|
259
|
+
"Map",
|
|
260
|
+
"Set",
|
|
261
|
+
"Object",
|
|
262
|
+
"Array"
|
|
263
|
+
]);
|
|
264
|
+
function devalue_key_bytes(nodes) {
|
|
265
|
+
const root = nodes[0];
|
|
266
|
+
const data_ix = typeof root.data === "number" ? root.data : void 0;
|
|
267
|
+
const data = data_ix !== void 0 && data_ix >= 0 ? nodes[data_ix] : root;
|
|
268
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return {};
|
|
269
|
+
const size = nodes.map((n) => JSON.stringify(n)?.length ?? 0);
|
|
270
|
+
const refs = (n) => {
|
|
271
|
+
if (Array.isArray(n)) {
|
|
272
|
+
if (typeof n[0] === "string" && !DEVALUE_CONTAINERS.has(n[0])) return [];
|
|
273
|
+
return (typeof n[0] === "string" ? n.slice(1) : n).filter((x) => typeof x === "number" && x >= 0 && Number.isInteger(x));
|
|
274
|
+
}
|
|
275
|
+
if (n && typeof n === "object") return Object.values(n).filter((x) => typeof x === "number" && x >= 0 && Number.isInteger(x));
|
|
276
|
+
return [];
|
|
277
|
+
};
|
|
278
|
+
const out = {};
|
|
279
|
+
for (const [key, ix] of Object.entries(data)) {
|
|
280
|
+
if (typeof ix !== "number" || ix < 0 || !Number.isInteger(ix)) {
|
|
281
|
+
out[key] = 0;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
const seen = /* @__PURE__ */ new Set();
|
|
285
|
+
const stack = [ix];
|
|
286
|
+
let bytes = 0;
|
|
287
|
+
while (stack.length) {
|
|
288
|
+
const i = stack.pop();
|
|
289
|
+
if (seen.has(i) || i >= nodes.length) continue;
|
|
290
|
+
seen.add(i);
|
|
291
|
+
bytes += size[i];
|
|
292
|
+
for (const r of refs(nodes[i])) if (!seen.has(r)) stack.push(r);
|
|
293
|
+
}
|
|
294
|
+
out[key] = bytes;
|
|
295
|
+
}
|
|
296
|
+
return out;
|
|
297
|
+
}
|
|
298
|
+
export { build_river, load_return_keys, seed_key_bytes };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHICH ROUTE A FILE BELONGS TO — a page or layout file under `src/routes` (or its built chunk under
|
|
3
|
+
* `entries/pages`), and whether it is ANOTHER route's than the one profiled: a visitor of another
|
|
4
|
+
* page whose work landed in the same window. Pure and import-free: the CPU analysis, the report and
|
|
5
|
+
* the report's browser side all ask it.
|
|
6
|
+
*/
|
|
7
|
+
/** a route's page or layout file, by its folder under `src/routes` (`form: 'src'`) or its built
|
|
8
|
+
* chunk under `entries/pages` (`'built'`: `[id]` written `_id_`); undefined for any other file */
|
|
9
|
+
export declare function route_file_of(path: string): {
|
|
10
|
+
dir: string;
|
|
11
|
+
page: boolean;
|
|
12
|
+
form: "src" | "built";
|
|
13
|
+
} | undefined;
|
|
14
|
+
/** a route id's own folder and the folders above it (whose layouts it renders in), per form */
|
|
15
|
+
export declare function route_dirs(route: string): {
|
|
16
|
+
own: (form: "src" | "built") => string;
|
|
17
|
+
above: (form: "src" | "built") => string[];
|
|
18
|
+
};
|
|
19
|
+
/** A file that is ANOTHER route's page or layout (not `route`'s own page, nor a layout above it):
|
|
20
|
+
* another visitor's request. `path` absolute, `file://`, or src-relative (`routes/x/+page.server.ts`).
|
|
21
|
+
* Endpoints and shared code are never another route's by this. */
|
|
22
|
+
export declare function another_routes_file(route: string, path: string): boolean;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/profiler/route-files.ts
|
|
2
|
+
/**
|
|
3
|
+
* WHICH ROUTE A FILE BELONGS TO — a page or layout file under `src/routes` (or its built chunk under
|
|
4
|
+
* `entries/pages`), and whether it is ANOTHER route's than the one profiled: a visitor of another
|
|
5
|
+
* page whose work landed in the same window. Pure and import-free: the CPU analysis, the report and
|
|
6
|
+
* the report's browser side all ask it.
|
|
7
|
+
*/
|
|
8
|
+
/** a route's page or layout file, by its folder under `src/routes` (`form: 'src'`) or its built
|
|
9
|
+
* chunk under `entries/pages` (`'built'`: `[id]` written `_id_`); undefined for any other file */
|
|
10
|
+
function route_file_of(path) {
|
|
11
|
+
const read = (marker, form, page, layout) => {
|
|
12
|
+
const at = path.lastIndexOf(marker);
|
|
13
|
+
if (at === -1) return void 0;
|
|
14
|
+
const rel = path.slice(at + marker.length);
|
|
15
|
+
const slash = rel.lastIndexOf("/");
|
|
16
|
+
const base = rel.slice(slash + 1);
|
|
17
|
+
const dir = slash === -1 ? "" : rel.slice(0, slash);
|
|
18
|
+
if (base.startsWith(page)) return {
|
|
19
|
+
dir,
|
|
20
|
+
page: true,
|
|
21
|
+
form
|
|
22
|
+
};
|
|
23
|
+
if (layout.some((l) => base.startsWith(l))) return {
|
|
24
|
+
dir,
|
|
25
|
+
page: false,
|
|
26
|
+
form
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
return read("/src/routes/", "src", "+page", ["+layout", "+error"]) ?? read("/entries/pages/", "built", "_page", ["_layout", "_error"]);
|
|
30
|
+
}
|
|
31
|
+
/** a route id's own folder and the folders above it (whose layouts it renders in), per form */
|
|
32
|
+
function route_dirs(route) {
|
|
33
|
+
const segs = route.split("/").filter(Boolean);
|
|
34
|
+
const built = segs.map((s) => s.split("[").join("_").split("]").join("_"));
|
|
35
|
+
const prefixes = (list) => list.map((_, i) => list.slice(0, i + 1).join("/"));
|
|
36
|
+
const src_above = ["", ...prefixes(segs)];
|
|
37
|
+
const built_above = ["", ...prefixes(built)];
|
|
38
|
+
return {
|
|
39
|
+
own: (form) => (form === "src" ? segs : built).join("/"),
|
|
40
|
+
above: (form) => form === "src" ? src_above : built_above
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** A file that is ANOTHER route's page or layout (not `route`'s own page, nor a layout above it):
|
|
44
|
+
* another visitor's request. `path` absolute, `file://`, or src-relative (`routes/x/+page.server.ts`).
|
|
45
|
+
* Endpoints and shared code are never another route's by this. */
|
|
46
|
+
function another_routes_file(route, path) {
|
|
47
|
+
let p = path.startsWith("file://") ? path.slice(7) : path;
|
|
48
|
+
if (p.startsWith("routes/")) p = "/src/" + p;
|
|
49
|
+
else if (p.startsWith("src/routes/")) p = "/" + p;
|
|
50
|
+
const f = route_file_of(p);
|
|
51
|
+
if (!f) return false;
|
|
52
|
+
const mine = route_dirs(route);
|
|
53
|
+
return f.page ? f.dir !== mine.own(f.form) : !mine.above(f.form).includes(f.dir);
|
|
54
|
+
}
|
|
55
|
+
export { another_routes_file, route_dirs, route_file_of };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE `<Name>.svelte` A ROUTE IMPORTS: from its `+page.svelte` / `+layout.svelte` files (src-relative
|
|
3
|
+
* paths), each import of a file with that name, resolved: `$lib/hell/X.svelte` → `lib/hell/X.svelte`,
|
|
4
|
+
* `./X.svelte` beside the route file. What tells two same-named components apart (a component is
|
|
5
|
+
* named after its file, and a `Button.svelte` can live in several folders).
|
|
6
|
+
*/
|
|
7
|
+
export declare function route_imports(files: Iterable<string>, read: (file: string) => string | undefined, name: string): Set<string>;
|
|
8
|
+
/**
|
|
9
|
+
* THE APP MODULES A SERVER FILE IMPORTS: each `$lib/…` or relative import of a script module
|
|
10
|
+
* (`.svelte` files and packages left out), src-relative and resolved against the file's folder —
|
|
11
|
+
* the candidates to read, each as written (no extension) and with `.ts` / `.js` / `/index.ts` /
|
|
12
|
+
* `/index.js`. One file's `import … from '…'` and `export … from '…'` lines; no regex.
|
|
13
|
+
*/
|
|
14
|
+
export declare function module_imports(file: string, src: string): string[];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// src/profiler/route-imports.ts
|
|
2
|
+
/**
|
|
3
|
+
* THE `<Name>.svelte` A ROUTE IMPORTS: from its `+page.svelte` / `+layout.svelte` files (src-relative
|
|
4
|
+
* paths), each import of a file with that name, resolved: `$lib/hell/X.svelte` → `lib/hell/X.svelte`,
|
|
5
|
+
* `./X.svelte` beside the route file. What tells two same-named components apart (a component is
|
|
6
|
+
* named after its file, and a `Button.svelte` can live in several folders).
|
|
7
|
+
*/
|
|
8
|
+
function route_imports(files, read, name) {
|
|
9
|
+
const out = /* @__PURE__ */ new Set();
|
|
10
|
+
const leaf = `/${name}.svelte`;
|
|
11
|
+
for (const file of files) {
|
|
12
|
+
if (!file.endsWith("/+page.svelte") && !file.endsWith("/+layout.svelte")) continue;
|
|
13
|
+
const src = read(file);
|
|
14
|
+
if (!src) continue;
|
|
15
|
+
const dir = file.slice(0, file.lastIndexOf("/"));
|
|
16
|
+
let after_from = false;
|
|
17
|
+
for (const line of src.split("\n")) {
|
|
18
|
+
const t = line.trim();
|
|
19
|
+
const was_after = after_from;
|
|
20
|
+
if (t) after_from = t.endsWith("from") || t === "import";
|
|
21
|
+
if (!line.includes(leaf)) continue;
|
|
22
|
+
if (!was_after && !line.includes("import") && !line.includes("from")) continue;
|
|
23
|
+
for (const q of ["'", "\""]) {
|
|
24
|
+
const end = line.indexOf(`${leaf}${q}`);
|
|
25
|
+
if (end === -1) continue;
|
|
26
|
+
const start = line.lastIndexOf(q, end) + 1;
|
|
27
|
+
const spec = line.slice(start, end + leaf.length);
|
|
28
|
+
if (spec.startsWith("$lib/")) out.add("lib/" + spec.slice(5));
|
|
29
|
+
else if (spec.startsWith(".")) {
|
|
30
|
+
const parts = dir.split("/");
|
|
31
|
+
for (const seg of spec.split("/")) if (seg === "..") parts.pop();
|
|
32
|
+
else if (seg !== ".") parts.push(seg);
|
|
33
|
+
out.add(parts.join("/"));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* THE APP MODULES A SERVER FILE IMPORTS: each `$lib/…` or relative import of a script module
|
|
42
|
+
* (`.svelte` files and packages left out), src-relative and resolved against the file's folder —
|
|
43
|
+
* the candidates to read, each as written (no extension) and with `.ts` / `.js` / `/index.ts` /
|
|
44
|
+
* `/index.js`. One file's `import … from '…'` and `export … from '…'` lines; no regex.
|
|
45
|
+
*/
|
|
46
|
+
function module_imports(file, src) {
|
|
47
|
+
const dir = file.slice(0, file.lastIndexOf("/"));
|
|
48
|
+
const out = [];
|
|
49
|
+
const seen = /* @__PURE__ */ new Set();
|
|
50
|
+
for (const line of src.split("\n")) {
|
|
51
|
+
const t = line.trim();
|
|
52
|
+
if (!(t.startsWith("import") || t.startsWith("export") || t.startsWith("}") || t.startsWith("'") || t.startsWith("\""))) continue;
|
|
53
|
+
for (const q of ["'", "\""]) {
|
|
54
|
+
const a = line.indexOf(q);
|
|
55
|
+
const b = a === -1 ? -1 : line.indexOf(q, a + 1);
|
|
56
|
+
if (b === -1) continue;
|
|
57
|
+
const spec = line.slice(a + 1, b);
|
|
58
|
+
if (spec.endsWith(".svelte") || !(spec.startsWith("$lib/") || spec.startsWith("./") || spec.startsWith("../"))) continue;
|
|
59
|
+
let base;
|
|
60
|
+
if (spec.startsWith("$lib/")) base = "lib/" + spec.slice(5);
|
|
61
|
+
else {
|
|
62
|
+
const parts = dir ? dir.split("/") : [];
|
|
63
|
+
for (const seg of spec.split("/")) if (seg === "..") parts.pop();
|
|
64
|
+
else if (seg !== ".") parts.push(seg);
|
|
65
|
+
base = parts.join("/");
|
|
66
|
+
}
|
|
67
|
+
const stem = base.endsWith(".js") ? base.slice(0, -3) : base;
|
|
68
|
+
for (const c of base.endsWith(".ts") ? [base] : [
|
|
69
|
+
stem + ".ts",
|
|
70
|
+
stem + ".js",
|
|
71
|
+
base,
|
|
72
|
+
stem + "/index.ts",
|
|
73
|
+
stem + "/index.js"
|
|
74
|
+
]) if (!seen.has(c)) {
|
|
75
|
+
seen.add(c);
|
|
76
|
+
out.push(c);
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
export { module_imports, route_imports };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a set of savings (each already one per group: no two share a line) takes off one render of
|
|
3
|
+
* `now` ms together. EACH RESOURCE CAPPED BY WHAT THE RENDER USED: every saving on the clock (the
|
|
4
|
+
* wait fixes and the waits deleted) takes off at most the render's waiting; every CPU saving at most
|
|
5
|
+
* its CPU. Past that they overlap in a way the lines do not show.
|
|
6
|
+
*/
|
|
7
|
+
export declare function saving_of(set: readonly {
|
|
8
|
+
ms: number;
|
|
9
|
+
wait: boolean;
|
|
10
|
+
kind: string;
|
|
11
|
+
}[], caps: {
|
|
12
|
+
wait?: number;
|
|
13
|
+
cpu?: number;
|
|
14
|
+
}, now: number): {
|
|
15
|
+
cpu: number;
|
|
16
|
+
wait: number;
|
|
17
|
+
del: number;
|
|
18
|
+
/** of `del`, the CPU part (deleted work that ran, not waited) */
|
|
19
|
+
del_cpu: number;
|
|
20
|
+
after: number;
|
|
21
|
+
clamped: boolean;
|
|
22
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/profiler/saving.ts
|
|
2
|
+
/**
|
|
3
|
+
* What a set of savings (each already one per group: no two share a line) takes off one render of
|
|
4
|
+
* `now` ms together. EACH RESOURCE CAPPED BY WHAT THE RENDER USED: every saving on the clock (the
|
|
5
|
+
* wait fixes and the waits deleted) takes off at most the render's waiting; every CPU saving at most
|
|
6
|
+
* its CPU. Past that they overlap in a way the lines do not show.
|
|
7
|
+
*/
|
|
8
|
+
function saving_of(set, caps, now) {
|
|
9
|
+
let cpu = 0;
|
|
10
|
+
let wait = 0;
|
|
11
|
+
let del_cpu = 0;
|
|
12
|
+
let del_wait = 0;
|
|
13
|
+
for (const s of set) if (s.kind === "unread-work") {
|
|
14
|
+
if (s.wait) del_wait += s.ms;
|
|
15
|
+
else del_cpu += s.ms;
|
|
16
|
+
} else if (s.wait) wait += s.ms;
|
|
17
|
+
else cpu += s.ms;
|
|
18
|
+
let clamped = false;
|
|
19
|
+
const cap = (a, b, limit) => {
|
|
20
|
+
if (limit === void 0 || !(limit >= 0) || a + b <= limit) return [a, b];
|
|
21
|
+
clamped = true;
|
|
22
|
+
const k = limit / (a + b);
|
|
23
|
+
return [a * k, b * k];
|
|
24
|
+
};
|
|
25
|
+
[wait, del_wait] = cap(wait, del_wait, caps.wait);
|
|
26
|
+
[cpu, del_cpu] = cap(cpu, del_cpu, caps.cpu);
|
|
27
|
+
const del = del_cpu + del_wait;
|
|
28
|
+
let total = cpu + wait + del;
|
|
29
|
+
if (total > now * .97) {
|
|
30
|
+
total = now * .97;
|
|
31
|
+
clamped = true;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
cpu,
|
|
35
|
+
wait,
|
|
36
|
+
del,
|
|
37
|
+
del_cpu,
|
|
38
|
+
after: now - total,
|
|
39
|
+
clamped
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export { saving_of };
|