@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,440 @@
|
|
|
1
|
+
import { REGEXP_FRAME, calls_name, function_body, import_aliases, tokens } from "./source-scan.js";
|
|
2
|
+
import { app_relative } from "./app-path.js";
|
|
3
|
+
// src/profiler/ledger.ts
|
|
4
|
+
/** the package of a `node_modules`-relative path: `@acme/ui/hydrate/x.mjs:1` → `@acme/ui` */
|
|
5
|
+
function pkg_of(file) {
|
|
6
|
+
const parts = file.split("/");
|
|
7
|
+
if (!parts[0] || !file.includes("/")) return "";
|
|
8
|
+
return parts[0].startsWith("@") && parts[1] ? parts[0] + "/" + parts[1] : parts[0];
|
|
9
|
+
}
|
|
10
|
+
/** the framework the app runs on: SvelteKit, Svelte, ogygia itself, Vite */
|
|
11
|
+
const is_framework_pkg = (p) => p.startsWith("@sveltejs/") || p === "svelte" || p.startsWith("ogygia") || p === "@ogygia/core" || p === "vite";
|
|
12
|
+
/** After `renders` more renders a leaking line holds about `renders` × what one render left; one
|
|
13
|
+
* filling a bounded cache holds its capacity's worth. At 80 % of `renders`× or more it grew with
|
|
14
|
+
* EVERY render measured. The limit, said plainly: a cache holding at least `renders` entries is
|
|
15
|
+
* indistinguishable from a leak in that many renders. */
|
|
16
|
+
const grows_of = (grown, one, renders) => {
|
|
17
|
+
if (renders < GROWS_MIN_RENDERS || !(one > 0)) return void 0;
|
|
18
|
+
const ratio = grown / (one * renders);
|
|
19
|
+
return ratio >= .85 ? renders >= LEAK_MIN_RENDERS ? true : void 0 : ratio <= .5 ? false : void 0;
|
|
20
|
+
};
|
|
21
|
+
const GROWS_MIN_RENDERS = 5;
|
|
22
|
+
/** A LEAK verdict needs 6 renders. The check is cut short on a tight time budget, and over 5 a
|
|
23
|
+
* cache that keeps 4 entries holds 4 of the 5 renders' worth: a leak's shape, told apart only by
|
|
24
|
+
* sampling noise (the slow-patterns key's bounded-cache decoy read "grows" in 1 run of 3).
|
|
25
|
+
* "Levels off" still reads from 5: a leak never looks flat. */
|
|
26
|
+
const LEAK_MIN_RENDERS = 6;
|
|
27
|
+
/** The steadier verdict: the same sampler read after `h` renders (`half`) and after `n` (`full`).
|
|
28
|
+
* A leaking line holds `n/h` times as much at the end as halfway; one feeding a bounded cache
|
|
29
|
+
* that filled holds about the same. The sampled objects are the same objects in both reads, so
|
|
30
|
+
* one sample's noise cancels; the middle band is still no verdict. */
|
|
31
|
+
const grows_between = (half, full, h, n) => {
|
|
32
|
+
if (n < GROWS_MIN_RENDERS || h < 1 || h >= n) return void 0;
|
|
33
|
+
if (!(half > 0)) return void 0;
|
|
34
|
+
const expected = n / h;
|
|
35
|
+
const ratio = full / half;
|
|
36
|
+
if (ratio >= 1 + .85 * (expected - 1)) return n >= LEAK_MIN_RENDERS ? true : void 0;
|
|
37
|
+
if (ratio <= 1 + .5 * (expected - 1)) return false;
|
|
38
|
+
};
|
|
39
|
+
/** "grows" from the halfway read, CHECKED against the one-render amount: a leak holds about one
|
|
40
|
+
* render's worth per render (all `renders` of them), a cache with a size limit holds its capacity.
|
|
41
|
+
* The two sampled reads can land far enough apart by chance to read as growth (a 4-entry cache,
|
|
42
|
+
* sampled low halfway and high at the end); a line holding well under the renders' worth is not
|
|
43
|
+
* leaking — no verdict then. Stays "grows" when the one-render amount is unknown.
|
|
44
|
+
* One render's worth is the LARGER of two estimates: the separate one-render pass, and the
|
|
45
|
+
* halfway read over its renders (the same sampler as the end read, so their noise is shared).
|
|
46
|
+
* A low draw on one of them no longer makes a filled cache look like a leak. */
|
|
47
|
+
const confirmed = (between, grown, one, renders, half) => {
|
|
48
|
+
if (between !== true || !(one > 0) || renders <= 0) return between;
|
|
49
|
+
return grown / (Math.max(one, half && half.renders > 0 ? half.bytes / half.renders : 0) * renders) >= .75 ? true : void 0;
|
|
50
|
+
};
|
|
51
|
+
/** a callee a line can be charged for: library code, never Svelte's markup internals or V8's own */
|
|
52
|
+
const LIB = /* @__PURE__ */ new Set(["dependency", "node"]);
|
|
53
|
+
const APP = /* @__PURE__ */ new Set(["app", "component"]);
|
|
54
|
+
/** words before a `(` that are not calls */
|
|
55
|
+
const CONTROL = /* @__PURE__ */ new Set([
|
|
56
|
+
"if",
|
|
57
|
+
"for",
|
|
58
|
+
"while",
|
|
59
|
+
"switch",
|
|
60
|
+
"catch",
|
|
61
|
+
"return",
|
|
62
|
+
"typeof",
|
|
63
|
+
"await",
|
|
64
|
+
"function"
|
|
65
|
+
]);
|
|
66
|
+
const ID_START = (s) => {
|
|
67
|
+
const c = s.charCodeAt(0);
|
|
68
|
+
return c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 36;
|
|
69
|
+
};
|
|
70
|
+
/** the calls that run a regular expression */
|
|
71
|
+
const REGEXP_METHODS = [
|
|
72
|
+
"test",
|
|
73
|
+
"exec",
|
|
74
|
+
"match",
|
|
75
|
+
"matchAll",
|
|
76
|
+
"replace",
|
|
77
|
+
"replaceAll",
|
|
78
|
+
"split",
|
|
79
|
+
"search"
|
|
80
|
+
];
|
|
81
|
+
/** A readable path: from `src/` when the path has one, the package-relative path inside
|
|
82
|
+
* `node_modules`, else the last three segments. */
|
|
83
|
+
function short_source(path) {
|
|
84
|
+
const p = path.startsWith("file://") ? path.slice(7) : path;
|
|
85
|
+
const nm = p.lastIndexOf("/node_modules/");
|
|
86
|
+
if (nm !== -1) return p.slice(nm + 14);
|
|
87
|
+
const own = app_relative(p);
|
|
88
|
+
if (own !== void 0) return own;
|
|
89
|
+
const src = p.lastIndexOf("/src/");
|
|
90
|
+
if (src !== -1) return p.slice(src + 1);
|
|
91
|
+
const parts = p.split("/");
|
|
92
|
+
return parts.length > 3 ? parts.slice(-3).join("/") : p;
|
|
93
|
+
}
|
|
94
|
+
/** a source path the reader could open and edit: not a dependency, not Node's own, not the
|
|
95
|
+
* framework's generated build output (a compiled route chunk nobody edits), and not ogygia's own
|
|
96
|
+
* source reached through a workspace link (`packages/core/src/…`: no node_modules segment, but
|
|
97
|
+
* the library all the same, as analyze's categorize already says) */
|
|
98
|
+
const is_app_path = (path) => !!path && !path.includes("/node_modules/") && !path.startsWith("node:") && (!path.includes("/.svelte-kit/") || path.includes("/.svelte-kit/output/")) && !path.includes("/ogygia/src/") && !path.includes("/ogygia/dist/") && !path.includes("/packages/core/src/") && !path.includes("/packages/core/dist/") && !path.includes("/devtools/src/profiler/") && !path.includes("/devtools/dist/profiler/");
|
|
99
|
+
const r2 = (n) => Math.round(n * 100) / 100;
|
|
100
|
+
function build_ledger(input) {
|
|
101
|
+
const by = /* @__PURE__ */ new Map();
|
|
102
|
+
const acc = (path, file, line) => {
|
|
103
|
+
const k = path + "\0" + line;
|
|
104
|
+
let a = by.get(k);
|
|
105
|
+
if (!a) by.set(k, a = {
|
|
106
|
+
path,
|
|
107
|
+
file,
|
|
108
|
+
line,
|
|
109
|
+
cpu_ms: 0,
|
|
110
|
+
alloc_bytes: 0,
|
|
111
|
+
gc_ms: 0,
|
|
112
|
+
retained_bytes: 0,
|
|
113
|
+
who: /* @__PURE__ */ new Map(),
|
|
114
|
+
fn_ms: 0,
|
|
115
|
+
lib_ms: 0,
|
|
116
|
+
libs: [],
|
|
117
|
+
grown_bytes: 0,
|
|
118
|
+
half_bytes: 0,
|
|
119
|
+
merged_ms: 0
|
|
120
|
+
});
|
|
121
|
+
return a;
|
|
122
|
+
};
|
|
123
|
+
const credit = (a, name, weight) => a.who.set(name, (a.who.get(name) ?? 0) + weight);
|
|
124
|
+
for (const f of input.functions) {
|
|
125
|
+
if (!APP.has(f.category) || !f.lines?.length || !is_app_path(f.path)) continue;
|
|
126
|
+
for (const l of f.lines) {
|
|
127
|
+
if (l.line <= 0 || l.ms <= 0) continue;
|
|
128
|
+
const a = acc(f.path, f.url || short_source(f.path), l.line);
|
|
129
|
+
a.cpu_ms += l.ms;
|
|
130
|
+
credit(a, f.name, l.ms);
|
|
131
|
+
if (l.ms > a.fn_ms) {
|
|
132
|
+
a.fn = f.key;
|
|
133
|
+
a.fn_ms = l.ms;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (input.source && input.uninlined?.length) {
|
|
138
|
+
const src = input.source;
|
|
139
|
+
const un_by_name = /* @__PURE__ */ new Map();
|
|
140
|
+
/** the un-inlined render's self time per line of each app function */
|
|
141
|
+
const un_lines = /* @__PURE__ */ new Map();
|
|
142
|
+
for (const f of input.uninlined) {
|
|
143
|
+
if (!APP.has(f.category) || f.total_ms <= 0 || !is_app_path(f.path) || f.name.startsWith("(")) continue;
|
|
144
|
+
const list = un_by_name.get(f.name) ?? [];
|
|
145
|
+
list.push({
|
|
146
|
+
path: f.path,
|
|
147
|
+
ms: f.total_ms
|
|
148
|
+
});
|
|
149
|
+
un_by_name.set(f.name, list);
|
|
150
|
+
if (f.lines?.length) {
|
|
151
|
+
const m = un_lines.get(f.name + "\0" + f.path) ?? /* @__PURE__ */ new Map();
|
|
152
|
+
for (const l of f.lines) m.set(l.line, (m.get(l.line) ?? 0) + l.ms);
|
|
153
|
+
un_lines.set(f.name + "\0" + f.path, m);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const renders = Math.max(1, input.renders ?? 1);
|
|
157
|
+
const main_ms = /* @__PURE__ */ new Map();
|
|
158
|
+
for (const f of input.functions) if (APP.has(f.category)) main_ms.set(f.name + "\0" + f.path, (main_ms.get(f.name + "\0" + f.path) ?? 0) + f.total_ms);
|
|
159
|
+
const kept_frame = (name, path, un_ms) => (main_ms.get(name + "\0" + path) ?? 0) / renders >= un_ms * .25;
|
|
160
|
+
/** an app function called on this line that ran with no frame of its own: its un-inlined time */
|
|
161
|
+
const inlined_calls = (line, caller) => {
|
|
162
|
+
const out = [];
|
|
163
|
+
const t = tokens(line);
|
|
164
|
+
for (let k = 0; k + 1 < t.length; k++) {
|
|
165
|
+
if (t[k + 1] !== "(" || t[k - 1] === "function" || t[k] === caller) continue;
|
|
166
|
+
const hits = un_by_name.get(t[k]);
|
|
167
|
+
if (hits?.length !== 1 || kept_frame(t[k], hits[0].path, hits[0].ms) || out.some((o) => o.name === t[k])) continue;
|
|
168
|
+
out.push({
|
|
169
|
+
name: t[k],
|
|
170
|
+
ms: hits[0].ms
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return out;
|
|
174
|
+
};
|
|
175
|
+
const touched = /* @__PURE__ */ new Set();
|
|
176
|
+
for (const a of Array.from(by.values())) {
|
|
177
|
+
if (a.cpu_ms <= 0 || touched.has(a)) continue;
|
|
178
|
+
const caller = a.fn?.split(" ")[0];
|
|
179
|
+
const here = src(a.path, a.line, a.line)?.lines[0];
|
|
180
|
+
const own = here ? inlined_calls(here, caller) : [];
|
|
181
|
+
const caller_lines = caller ? un_lines.get(caller + "\0" + a.path) : void 0;
|
|
182
|
+
if (!caller_lines && !own.length) continue;
|
|
183
|
+
const own_ms = (caller_lines?.get(a.line) ?? 0) + own.reduce((s, c) => s + c.ms, 0);
|
|
184
|
+
const win = src(a.path, Math.max(1, a.line - 2), a.line + 1);
|
|
185
|
+
if (!win) continue;
|
|
186
|
+
const beside = [];
|
|
187
|
+
for (let i = 0; i < win.lines.length; i++) {
|
|
188
|
+
const at = win.start + i;
|
|
189
|
+
if (at === a.line) continue;
|
|
190
|
+
const calls = inlined_calls(win.lines[i], caller);
|
|
191
|
+
if (calls.length) beside.push({
|
|
192
|
+
line: at,
|
|
193
|
+
calls
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (!beside.length) continue;
|
|
197
|
+
const total = own_ms + beside.reduce((s, b) => s + b.calls.reduce((x, c) => x + c.ms, 0), 0);
|
|
198
|
+
if (total <= 0) continue;
|
|
199
|
+
const pool = a.cpu_ms;
|
|
200
|
+
for (const b of beside) {
|
|
201
|
+
const ms = pool * b.calls.reduce((x, c) => x + c.ms, 0) / total;
|
|
202
|
+
if (ms <= 0) continue;
|
|
203
|
+
const to = acc(a.path, a.file, b.line);
|
|
204
|
+
to.cpu_ms += ms;
|
|
205
|
+
a.cpu_ms -= ms;
|
|
206
|
+
for (const c of b.calls) credit(to, c.name, ms / b.calls.length);
|
|
207
|
+
to.fn ??= a.fn;
|
|
208
|
+
if (!to.merged || ms > to.merged_ms) {
|
|
209
|
+
to.merged = {
|
|
210
|
+
from: a.line,
|
|
211
|
+
callee: b.calls[0].name
|
|
212
|
+
};
|
|
213
|
+
to.merged_ms = ms;
|
|
214
|
+
}
|
|
215
|
+
touched.add(to);
|
|
216
|
+
}
|
|
217
|
+
touched.add(a);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (input.source) {
|
|
221
|
+
const pkg_by_key = /* @__PURE__ */ new Map();
|
|
222
|
+
for (const f of input.functions) if (f.pkg) pkg_by_key.set(f.key, f.pkg);
|
|
223
|
+
const pkg_for = (c) => c.pkg ?? pkg_by_key.get(c.key) ?? (c.category === "node" ? "Node" : pkg_of(c.file));
|
|
224
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
225
|
+
const aliases_of = (path) => {
|
|
226
|
+
let al = aliases.get(path);
|
|
227
|
+
if (!al) aliases.set(path, al = import_aliases(input.source, path));
|
|
228
|
+
return al;
|
|
229
|
+
};
|
|
230
|
+
const un = input.uninlined ?? [];
|
|
231
|
+
const un_by_fn = /* @__PURE__ */ new Map();
|
|
232
|
+
const un_by_key = /* @__PURE__ */ new Map();
|
|
233
|
+
for (const u of un) {
|
|
234
|
+
if (!APP.has(u.category)) continue;
|
|
235
|
+
if (u.key) un_by_key.set(u.key, u);
|
|
236
|
+
un_by_fn.set(u.name + "\0" + u.path, u);
|
|
237
|
+
}
|
|
238
|
+
/** where `f` really entered package `pkg`, from the inlining-off render: the app lines (in
|
|
239
|
+
* `f`, or in the app functions it calls, three deep) that call into that package — each
|
|
240
|
+
* given a share of `ms` by what it called there. Empty when that render cannot say */
|
|
241
|
+
const entered_by = (f, pkg, ms) => {
|
|
242
|
+
const found = [];
|
|
243
|
+
const start = un_by_fn.get(f.name + "\0" + f.path);
|
|
244
|
+
if (!start) return only_call(f, ms);
|
|
245
|
+
const seen = /* @__PURE__ */ new Set([start]);
|
|
246
|
+
let level = [start];
|
|
247
|
+
for (let depth = 0; depth < 4 && level.length && !found.length; depth++) {
|
|
248
|
+
const next = [];
|
|
249
|
+
for (const g of level) {
|
|
250
|
+
if (!g.callees?.length || !g.line || !is_app_path(g.path)) continue;
|
|
251
|
+
const body = function_body(input.source, g.path, g.line);
|
|
252
|
+
if (!body) continue;
|
|
253
|
+
for (const d of g.callees) {
|
|
254
|
+
if (APP.has(d.category)) {
|
|
255
|
+
const u = un_by_key.get(d.key);
|
|
256
|
+
if (u && !seen.has(u)) {
|
|
257
|
+
seen.add(u);
|
|
258
|
+
next.push(u);
|
|
259
|
+
}
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (!LIB.has(d.category) || d.name.startsWith("(") || pkg_for(d) !== pkg) continue;
|
|
263
|
+
const names = [d.name, ...aliases_of(g.path).get(d.name) ?? []];
|
|
264
|
+
const lines = [];
|
|
265
|
+
for (let i = 0; i < body.lines.length; i++) if (calls_name(body.lines[i], names)) lines.push(body.start + i);
|
|
266
|
+
for (const line of lines) found.push({
|
|
267
|
+
path: g.path,
|
|
268
|
+
file: g.url || short_source(g.path),
|
|
269
|
+
line,
|
|
270
|
+
w: d.ms / lines.length,
|
|
271
|
+
fn: g.key ?? f.key
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
level = next;
|
|
276
|
+
}
|
|
277
|
+
const total = found.reduce((s, x) => s + x.w, 0);
|
|
278
|
+
return total > 0 ? found.map((x) => ({
|
|
279
|
+
path: x.path,
|
|
280
|
+
file: x.file,
|
|
281
|
+
line: x.line,
|
|
282
|
+
ms: ms * x.w / total,
|
|
283
|
+
fn: x.fn
|
|
284
|
+
})) : only_call(f, ms);
|
|
285
|
+
};
|
|
286
|
+
/** the one line of `f`'s body that calls anything, when there is exactly one (a loop that
|
|
287
|
+
* awaits a timer per turn, a wrapper around one library call): the library time came from
|
|
288
|
+
* there, whatever V8 inlined. The inlining-off render is one render; a short function can
|
|
289
|
+
* have no sample in it */
|
|
290
|
+
const only_call = (f, ms) => {
|
|
291
|
+
const body = function_body(input.source, f.path, f.line);
|
|
292
|
+
if (!body) return [];
|
|
293
|
+
let at = -1;
|
|
294
|
+
for (let i = 0; i < body.lines.length; i++) {
|
|
295
|
+
const t = tokens(body.lines[i]);
|
|
296
|
+
let calls = false;
|
|
297
|
+
for (let k = 0; k + 1 < t.length && !calls; k++) calls = t[k + 1] === "(" && t[k - 1] !== "function" && !CONTROL.has(t[k]) && ID_START(t[k]);
|
|
298
|
+
if (!calls) continue;
|
|
299
|
+
if (i === 0 && t.includes("function")) continue;
|
|
300
|
+
if (at !== -1) return [];
|
|
301
|
+
at = body.start + i;
|
|
302
|
+
}
|
|
303
|
+
return at === -1 ? [] : [{
|
|
304
|
+
path: f.path,
|
|
305
|
+
file: f.url || short_source(f.path),
|
|
306
|
+
line: at,
|
|
307
|
+
ms,
|
|
308
|
+
fn: f.key
|
|
309
|
+
}];
|
|
310
|
+
};
|
|
311
|
+
for (const f of input.functions) {
|
|
312
|
+
if (!APP.has(f.category) || !f.callees?.length || f.line <= 0 || !is_app_path(f.path)) continue;
|
|
313
|
+
const libs = f.callees.filter((c) => LIB.has(c.category) && !c.name.startsWith("(") && !is_framework_pkg(pkg_for(c)));
|
|
314
|
+
if (!libs.length) continue;
|
|
315
|
+
const body = function_body(input.source, f.path, f.line);
|
|
316
|
+
if (!body) continue;
|
|
317
|
+
let al = aliases.get(f.path);
|
|
318
|
+
if (!al) aliases.set(f.path, al = import_aliases(input.source, f.path));
|
|
319
|
+
for (const c of libs) {
|
|
320
|
+
const names = [c.name, ...al.get(c.name) ?? []];
|
|
321
|
+
const at = [];
|
|
322
|
+
const re = c.name.startsWith("RegExp: ") ? c.name.slice(REGEXP_FRAME.length) : null;
|
|
323
|
+
if (re !== null) {
|
|
324
|
+
for (let i = 0; i < body.lines.length; i++) if (body.lines[i].includes(`/${re}/`)) at.push(body.start + i);
|
|
325
|
+
if (!at.length) {
|
|
326
|
+
for (let i = 0; i < body.lines.length; i++) if (calls_name(body.lines[i], REGEXP_METHODS)) at.push(body.start + i);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
for (let i = 0; re === null && i < body.lines.length; i++) if (calls_name(body.lines[i], names)) at.push(body.start + i);
|
|
330
|
+
const targets = at.map((line) => ({
|
|
331
|
+
path: f.path,
|
|
332
|
+
file: f.url || short_source(f.path),
|
|
333
|
+
line,
|
|
334
|
+
ms: c.ms / at.length,
|
|
335
|
+
fn: f.key
|
|
336
|
+
}));
|
|
337
|
+
if (!targets.length) targets.push(...entered_by(f, pkg_for(c), c.ms));
|
|
338
|
+
for (const t of targets) {
|
|
339
|
+
const a = acc(t.path, t.file, t.line);
|
|
340
|
+
a.lib_ms += t.ms;
|
|
341
|
+
a.libs.push({
|
|
342
|
+
name: c.name,
|
|
343
|
+
pkg: pkg_for(c),
|
|
344
|
+
ms: r2(t.ms),
|
|
345
|
+
...targets.length > 1 ? { shared: targets.length } : {}
|
|
346
|
+
});
|
|
347
|
+
credit(a, c.name, t.ms);
|
|
348
|
+
if (!a.fn) a.fn = t.fn;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const hottest = /* @__PURE__ */ new Map();
|
|
354
|
+
for (const f of input.functions) {
|
|
355
|
+
if (!APP.has(f.category) || !f.lines?.length || f.line <= 0 || !is_app_path(f.path)) continue;
|
|
356
|
+
let best = f.lines[0];
|
|
357
|
+
for (const l of f.lines) if (l.ms > best.ms) best = l;
|
|
358
|
+
if (best.line > 0 && best.ms > 0) hottest.set(f.path + "\0" + f.line, {
|
|
359
|
+
file: f.url || short_source(f.path),
|
|
360
|
+
line: best.line
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
const mem_row = (at) => {
|
|
364
|
+
const h = hottest.get(at.path + "\0" + at.line);
|
|
365
|
+
if (!h) return acc(at.path, short_source(at.path), at.line);
|
|
366
|
+
const a = acc(at.path, h.file, h.line);
|
|
367
|
+
a.mem_in_fn = true;
|
|
368
|
+
return a;
|
|
369
|
+
};
|
|
370
|
+
const entry_of_pkg = /* @__PURE__ */ new Map();
|
|
371
|
+
for (const a of by.values()) for (const lc of a.libs) {
|
|
372
|
+
const e = lc.pkg ? entry_of_pkg.get(lc.pkg) : void 0;
|
|
373
|
+
if (lc.pkg && (!e || lc.ms > e.ms)) entry_of_pkg.set(lc.pkg, {
|
|
374
|
+
a,
|
|
375
|
+
ms: lc.ms
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const pkg_row = (url) => {
|
|
379
|
+
const i = url ? url.lastIndexOf("/node_modules/") : -1;
|
|
380
|
+
if (!url || i === -1) return void 0;
|
|
381
|
+
const pkg = pkg_of(url.slice(i + 14));
|
|
382
|
+
const e = entry_of_pkg.get(pkg);
|
|
383
|
+
if (e) e.a.mem_via = pkg;
|
|
384
|
+
return e?.a;
|
|
385
|
+
};
|
|
386
|
+
const row_for = (at, url) => at && at.line > 0 && is_app_path(at.path) ? mem_row(at) : !at ? pkg_row(url) : void 0;
|
|
387
|
+
for (const m of input.makers ?? []) {
|
|
388
|
+
const a = row_for(m.at, m.url);
|
|
389
|
+
if (!a) continue;
|
|
390
|
+
a.alloc_bytes += m.allocated;
|
|
391
|
+
a.gc_ms += m.gc_ms;
|
|
392
|
+
credit(a, m.name, m.allocated / 1e6);
|
|
393
|
+
}
|
|
394
|
+
for (const s of input.retained ?? []) {
|
|
395
|
+
const a = row_for(s.at, s.url);
|
|
396
|
+
if (!a) continue;
|
|
397
|
+
a.retained_bytes += s.bytes;
|
|
398
|
+
credit(a, s.name, s.bytes / 1e6);
|
|
399
|
+
}
|
|
400
|
+
for (const s of input.grown?.sites ?? []) {
|
|
401
|
+
const a = row_for(s.at, s.url);
|
|
402
|
+
if (a) a.grown_bytes += s.bytes;
|
|
403
|
+
}
|
|
404
|
+
for (const s of input.grown?.half?.sites ?? []) {
|
|
405
|
+
const a = row_for(s.at, s.url);
|
|
406
|
+
if (a) a.half_bytes += s.bytes;
|
|
407
|
+
}
|
|
408
|
+
const rows = [...by.values()];
|
|
409
|
+
const tot_cpu = rows.reduce((s, r) => s + r.cpu_ms + r.lib_ms, 0) || 1;
|
|
410
|
+
const tot_alloc = rows.reduce((s, r) => s + r.alloc_bytes, 0) || 1;
|
|
411
|
+
const tot_ret = rows.reduce((s, r) => s + r.retained_bytes, 0) || 1;
|
|
412
|
+
const raw = (r) => (r.cpu_ms + r.lib_ms) / tot_cpu + r.alloc_bytes / tot_alloc + r.retained_bytes / tot_ret;
|
|
413
|
+
rows.sort((a, b) => raw(b) - raw(a));
|
|
414
|
+
return rows.slice(0, input.limit ?? 40).map((r) => {
|
|
415
|
+
const code = input.code?.(r.path, r.line)?.trim();
|
|
416
|
+
return {
|
|
417
|
+
path: r.path,
|
|
418
|
+
file: r.file,
|
|
419
|
+
line: r.line,
|
|
420
|
+
...code ? { code: code.length > 140 ? code.slice(0, 139) + "…" : code } : {},
|
|
421
|
+
cpu_ms: r2(r.cpu_ms),
|
|
422
|
+
alloc_bytes: Math.round(r.alloc_bytes),
|
|
423
|
+
gc_ms: r2(r.gc_ms),
|
|
424
|
+
retained_bytes: Math.round(r.retained_bytes),
|
|
425
|
+
who: [...r.who].sort((a, b) => b[1] - a[1]).slice(0, 4).map(([n]) => n),
|
|
426
|
+
...r.fn ? { fn: r.fn } : {},
|
|
427
|
+
...r.mem_in_fn && (r.alloc_bytes > 0 || r.retained_bytes > 0) ? { mem_in_fn: true } : {},
|
|
428
|
+
...r.mem_via && (r.alloc_bytes > 0 || r.retained_bytes > 0) ? { mem_via: r.mem_via } : {},
|
|
429
|
+
...input.grown && r.retained_bytes > 0 ? { grows: input.grown.half && r.half_bytes > 0 ? confirmed(grows_between(r.half_bytes, r.grown_bytes, input.grown.half.renders, input.grown.renders), r.grown_bytes, r.retained_bytes, input.grown.renders, {
|
|
430
|
+
bytes: r.half_bytes,
|
|
431
|
+
renders: input.grown.half.renders
|
|
432
|
+
}) : grows_of(r.grown_bytes, r.retained_bytes, input.grown.renders) } : {},
|
|
433
|
+
lib_ms: r2(r.lib_ms),
|
|
434
|
+
...r.libs.length ? { libs: r.libs.sort((a, b) => b.ms - a.ms).slice(0, 4) } : {},
|
|
435
|
+
...r.merged ? { merged: r.merged } : {},
|
|
436
|
+
score: r2(raw(r))
|
|
437
|
+
};
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
export { build_ledger, confirmed, grows_between, grows_of, is_app_path, is_framework_pkg, pkg_of, short_source };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `page.data` keys a component source reads, or `null` when it takes the whole object in a
|
|
3
|
+
* way that cannot be named (`...data`, `data` passed on, `Object.keys(data)`). `names` are the
|
|
4
|
+
* identifiers that hold the data (`data` by default; `page.data` / `$page.data` always count).
|
|
5
|
+
*/
|
|
6
|
+
export declare function data_reads(source: string, names?: readonly string[]): string[] | null;
|
|
7
|
+
/** The 1-based line of the first place the source takes the data WHOLE (`const d = $page.data`,
|
|
8
|
+
* `fn(data)`, `...data`), or null when it only reads named keys. Same scan as `data_reads`. */
|
|
9
|
+
export declare function whole_read_line(source: string, names?: readonly string[]): number | null;
|
|
10
|
+
/** The source with its comments (`//`, block, HTML) and string contents blanked to spaces, so
|
|
11
|
+
* a word in prose or a URL never reads as code; positions are kept. A character walk. */
|
|
12
|
+
export declare function blank_comments(src: string): string;
|
|
13
|
+
/** the `let { data } = $props()` / `export let data` names a component gives its data prop —
|
|
14
|
+
* plus any rename (`let { data: page_data } = $props()`) */
|
|
15
|
+
export declare function data_prop_names(src: string): string[];
|
|
16
|
+
/** `island`: an island names it (must ship) · `server`: read on the server, not shipped (fine) ·
|
|
17
|
+
* `server-only`: read on the server only, yet shipped · `unread`: nobody reads it · `unknown`:
|
|
18
|
+
* a reader could not be scanned, or an island takes the page whole */
|
|
19
|
+
export type KeyVerdict = "island" | "server" | "server-only" | "unread" | "unknown";
|
|
20
|
+
export interface LineageKey {
|
|
21
|
+
key: string;
|
|
22
|
+
/** the load file that returns it, when its source said */
|
|
23
|
+
from: string | null;
|
|
24
|
+
/** bytes the seed shipped for it (0: not shipped) */
|
|
25
|
+
shipped_bytes: number;
|
|
26
|
+
/** who reads it */
|
|
27
|
+
readers: {
|
|
28
|
+
name: string;
|
|
29
|
+
island: boolean;
|
|
30
|
+
}[];
|
|
31
|
+
verdict: KeyVerdict;
|
|
32
|
+
/** the waiting the load that produced it did (its upstream calls), when known */
|
|
33
|
+
load_wait_ms: number | null;
|
|
34
|
+
}
|
|
35
|
+
export interface LineageComponent {
|
|
36
|
+
name: string;
|
|
37
|
+
file: string;
|
|
38
|
+
island: boolean;
|
|
39
|
+
/** the keys it reads; null when it could not be told */
|
|
40
|
+
reads: string[] | null;
|
|
41
|
+
/** it reads the whole object (a spread, a pass-through): every key may be read */
|
|
42
|
+
whole: boolean;
|
|
43
|
+
/** where (1-based line in `file`) it first takes the object whole, when the source said */
|
|
44
|
+
whole_line?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface Lineage {
|
|
47
|
+
keys: LineageKey[];
|
|
48
|
+
components: LineageComponent[];
|
|
49
|
+
/** keys nobody reads, by the wait behind them */
|
|
50
|
+
unread: LineageKey[];
|
|
51
|
+
/** keys shipped in the seed but read only on the server */
|
|
52
|
+
server_only: LineageKey[];
|
|
53
|
+
notes: string[];
|
|
54
|
+
/** every load's returned keys with the lines (1-based, in `from`) whose values end up in each
|
|
55
|
+
* (source-scan `key_sources`): what a wait made on one of those lines was for */
|
|
56
|
+
sources?: {
|
|
57
|
+
key: string;
|
|
58
|
+
from: string;
|
|
59
|
+
lines: number[];
|
|
60
|
+
names?: string[];
|
|
61
|
+
}[];
|
|
62
|
+
}
|
|
63
|
+
export interface LineageInput {
|
|
64
|
+
/** every component of the page with its reads (server components scanned; islands from the build) */
|
|
65
|
+
components: {
|
|
66
|
+
name: string;
|
|
67
|
+
file: string;
|
|
68
|
+
island: boolean;
|
|
69
|
+
reads: string[] | null;
|
|
70
|
+
whole_line?: number;
|
|
71
|
+
}[];
|
|
72
|
+
/** the loads and the keys they return, plus the waiting each did */
|
|
73
|
+
lanes: {
|
|
74
|
+
file: string;
|
|
75
|
+
keys: string[] | null;
|
|
76
|
+
wait_ms?: number;
|
|
77
|
+
}[];
|
|
78
|
+
/** bytes per key in the seed */
|
|
79
|
+
seed: Record<string, number>;
|
|
80
|
+
}
|
|
81
|
+
export declare function build_lineage(input: LineageInput): Lineage | undefined;
|