@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,245 @@
|
|
|
1
|
+
import { SiteFix } from "./site-fixes.js";
|
|
2
|
+
import { HoleSlots } from "./hole-slots.js";
|
|
3
|
+
import { Comparison, Since } from "./compare.js";
|
|
4
|
+
import { Analysis } from "./analyze.js";
|
|
5
|
+
import { ReportExtras, ReportMeta, RequestEntry, RouteAgg } from "./report.js";
|
|
6
|
+
import { SampledSummary, StoredReport, TrapStatus } from "./index.js";
|
|
7
|
+
import { SinkRow } from "./sink.js";
|
|
8
|
+
import { Ctx, PageDef, Router } from "@ogygia/router";
|
|
9
|
+
/** One page's profile history: every page-mode report of it, oldest first, by median run. */
|
|
10
|
+
export interface PageHistory {
|
|
11
|
+
page: string;
|
|
12
|
+
points: {
|
|
13
|
+
id: string;
|
|
14
|
+
created: number;
|
|
15
|
+
median: number;
|
|
16
|
+
score?: number;
|
|
17
|
+
}[];
|
|
18
|
+
}
|
|
19
|
+
/** What the router calls into the host for. Return types here ARE the components' `data` types. */
|
|
20
|
+
export interface ProfilerDeps {
|
|
21
|
+
base: string;
|
|
22
|
+
/** Table-wide guard: a deny Response short-circuits every route (v2 load-returns-Response), else
|
|
23
|
+
* `undefined` = allow. The load below turns allow into the seeded `{ base }`. */
|
|
24
|
+
auth_guard(c: Ctx): Promise<Response | undefined>;
|
|
25
|
+
/** The runtime's hydration beacon (POST, authed): browser-side island timings, joined by fingerprint. */
|
|
26
|
+
beacon(c: Ctx): Promise<Response>;
|
|
27
|
+
/** A recording window you start and stop (POST, authed): the server's CPU, calls and requests
|
|
28
|
+
* while a test (or the devtools Record tab) drives the app; stop answers the report's JSON. */
|
|
29
|
+
window_start(c: Ctx): Promise<Response>;
|
|
30
|
+
window_stop(c: Ctx): Promise<Response>;
|
|
31
|
+
/** Is this request logged in? The public bare report page gates its SERVER data on this (a share
|
|
32
|
+
* `#fragment` renders without it). */
|
|
33
|
+
authed(c: Ctx): Promise<boolean>;
|
|
34
|
+
dashboard(c: Ctx): {
|
|
35
|
+
base: string;
|
|
36
|
+
recent: RequestEntry[];
|
|
37
|
+
routes: RouteAgg[];
|
|
38
|
+
/** the request tag the slowest-routes table is split by (`?by=tenant`), when any */
|
|
39
|
+
by: string | null;
|
|
40
|
+
/** every tag key seen in the log (`tag()`), for the split-by control */
|
|
41
|
+
tag_keys: string[];
|
|
42
|
+
reports: ReportMeta[];
|
|
43
|
+
/** report id → its biggest slow pattern (what to fix first) and how many more it found */
|
|
44
|
+
top_fix: Record<string, {
|
|
45
|
+
title: string;
|
|
46
|
+
save_ms: number;
|
|
47
|
+
wait: boolean;
|
|
48
|
+
more: number;
|
|
49
|
+
}>;
|
|
50
|
+
/** the lines that slow two or more profiled pages, what fixing each gives back per page and in all */
|
|
51
|
+
site_fixes: SiteFix[];
|
|
52
|
+
/** the holes the server rendered: render, cache, and who held the render slots others waited for */
|
|
53
|
+
holes: HoleSlots | null;
|
|
54
|
+
recording: boolean;
|
|
55
|
+
dev: boolean;
|
|
56
|
+
rss_mb: number;
|
|
57
|
+
inflight: number;
|
|
58
|
+
/** page-mode reports grouped by page (the history sparklines + compare pairs) */
|
|
59
|
+
history: PageHistory[];
|
|
60
|
+
/** the trap (catch the slow one), when configured */
|
|
61
|
+
trap: TrapStatus | null;
|
|
62
|
+
/** the always-on sampler's rolling hot-functions table, when configured */
|
|
63
|
+
sampled: SampledSummary | null;
|
|
64
|
+
/** why the background recorders are off (a serverless host), when they are */
|
|
65
|
+
background_note: string | null;
|
|
66
|
+
};
|
|
67
|
+
/** Two stored reports side by side (404 when either expired). */
|
|
68
|
+
/** `cmp` is null when this server no longer holds one of the reports: the page then compares
|
|
69
|
+
* from the browser's store (the ids come back so it can) */
|
|
70
|
+
compare(a: string | undefined, b: string | undefined): {
|
|
71
|
+
base: string;
|
|
72
|
+
cmp: Comparison | null;
|
|
73
|
+
a: string;
|
|
74
|
+
b: string;
|
|
75
|
+
};
|
|
76
|
+
/** the whole-site pictures: this instance's rows, the sink's status, a URL the browser reads rows from */
|
|
77
|
+
site(c: Ctx): {
|
|
78
|
+
base: string;
|
|
79
|
+
rows: SinkRow[];
|
|
80
|
+
sink: {
|
|
81
|
+
url: string;
|
|
82
|
+
last: {
|
|
83
|
+
at: number;
|
|
84
|
+
ok: boolean;
|
|
85
|
+
rows: number;
|
|
86
|
+
error?: string;
|
|
87
|
+
} | null;
|
|
88
|
+
buffered: number;
|
|
89
|
+
} | null;
|
|
90
|
+
from: string | null;
|
|
91
|
+
ephemeral: boolean;
|
|
92
|
+
};
|
|
93
|
+
/** A caught request profiled again in full page mode with the inputs it was caught with. */
|
|
94
|
+
replay(id: string | undefined, c: Ctx): Promise<Response>;
|
|
95
|
+
/** DEV ONLY: a few lines of a local source file around a line (the row's source peek). */
|
|
96
|
+
source(c: Ctx): Promise<Response>;
|
|
97
|
+
run_page(c: Ctx): Response | {
|
|
98
|
+
base: string;
|
|
99
|
+
path: string;
|
|
100
|
+
runs: number;
|
|
101
|
+
format: string;
|
|
102
|
+
against?: string;
|
|
103
|
+
};
|
|
104
|
+
record_page(c: Ctx): Promise<Response>;
|
|
105
|
+
reset(c: Ctx): Response;
|
|
106
|
+
/** A tiny live-status poll for the sidebar (and, later, a hosted dashboard): is a recording
|
|
107
|
+
* running right now, how many requests are in flight, the instance's memory, how many reports
|
|
108
|
+
* this instance holds. Cheap and side-effect-free. */
|
|
109
|
+
status(c: Ctx): {
|
|
110
|
+
recording: boolean;
|
|
111
|
+
inflight: number;
|
|
112
|
+
rss_mb: number;
|
|
113
|
+
reports: number;
|
|
114
|
+
};
|
|
115
|
+
/** `session_problem`: why a login that just succeeded still has no session (null otherwise). */
|
|
116
|
+
login_props(c: Ctx): {
|
|
117
|
+
base: string;
|
|
118
|
+
next: string;
|
|
119
|
+
session_problem: string | null;
|
|
120
|
+
};
|
|
121
|
+
login(c: Ctx): Promise<Response>;
|
|
122
|
+
logout(c: Ctx): Response;
|
|
123
|
+
upload(c: Ctx): Promise<Response>;
|
|
124
|
+
report_stored(id: string | undefined): StoredReport | undefined;
|
|
125
|
+
/** Like `report_stored`, but falls through to the configured storage backend (SQLite/Redis/
|
|
126
|
+
* Postgres) on a memory miss and reconstructs the report from its stored dump — so a report
|
|
127
|
+
* survives the instance that made it. Memory-only when no store is configured. */
|
|
128
|
+
report_load(id: string | undefined): Promise<StoredReport | undefined>;
|
|
129
|
+
/** The reports the backend holds (summaries), for the sidebar's shared list. Empty with no store. */
|
|
130
|
+
list_stored(limit?: number): Promise<{
|
|
131
|
+
id: string;
|
|
132
|
+
label: string;
|
|
133
|
+
page?: string;
|
|
134
|
+
created: number;
|
|
135
|
+
}[]>;
|
|
136
|
+
/** The login page for THIS url (`?next=` back here) when the UI is secret-gated; null when a
|
|
137
|
+
* login makes no sense (dev is open; no secret = no UI). */
|
|
138
|
+
login_url(c: Ctx): string | null;
|
|
139
|
+
report_view(stored: StoredReport): Promise<{
|
|
140
|
+
a: Analysis;
|
|
141
|
+
meta: ReportMeta;
|
|
142
|
+
base: string;
|
|
143
|
+
extras: ReportExtras;
|
|
144
|
+
ogpB64?: string;
|
|
145
|
+
/** this page's other page-mode reports (page mode only) and the one just before this */
|
|
146
|
+
history: PageHistory | null;
|
|
147
|
+
prev: string | null;
|
|
148
|
+
/** against the previous profile of this page, when this server still holds it */
|
|
149
|
+
since: Since | null;
|
|
150
|
+
/** recorded on a dev server — the source peek is available */
|
|
151
|
+
dev: boolean;
|
|
152
|
+
}>;
|
|
153
|
+
report_json(stored: StoredReport): Response | Promise<Response>;
|
|
154
|
+
report_dump_json(stored: StoredReport): Response;
|
|
155
|
+
/** ONE self-contained HTML file of the report (styles, runtime and island chunks inlined) —
|
|
156
|
+
* opens from disk with the islands live. A built app only; dev answers 400. */
|
|
157
|
+
report_html(stored: StoredReport, c: Ctx): Promise<Response>;
|
|
158
|
+
/** A report's standalone file, agent JSON or plain dump, built from the report the request
|
|
159
|
+
* carries (the page's .ogp bytes or the browser's kept copy): works on any instance. */
|
|
160
|
+
report_posted(c: Ctx, as: "html" | "json" | "dump" | "ogp"): Promise<Response>;
|
|
161
|
+
report_raw(stored: StoredReport): Promise<Response>;
|
|
162
|
+
}
|
|
163
|
+
type CtxFn<R> = (c: Ctx) => R;
|
|
164
|
+
type ProfilerPage<Load, Actions = unknown> = PageDef<readonly [], Load, Actions, unknown, unknown>;
|
|
165
|
+
/** Build the profiler router. Auth is the table-wide `load` (a redirect/deny short-circuits every
|
|
166
|
+
* route; on allow it seeds `{ base }`); thrown `error()` renders the root `Message` boundary.
|
|
167
|
+
* `/report/[id]` is PUBLIC (a share `#fragment` decrypts client-side); its `.json`/`.dump`/`/raw`
|
|
168
|
+
* siblings keep the suffix so they stay behind the guard's server-data gate. Endpoints are
|
|
169
|
+
* the plain `{ GET }` object form (bare handlers; `c.params.id` reads from the table key). */
|
|
170
|
+
export declare function build_profiler_router(d: ProfilerDeps): Router<{
|
|
171
|
+
readonly "/": ProfilerPage<(c: Ctx) => ReturnType<ProfilerDeps["dashboard"]>>;
|
|
172
|
+
readonly "/run": ProfilerPage<(c: Ctx) => ReturnType<ProfilerDeps["run_page"]>>;
|
|
173
|
+
readonly "/page": {
|
|
174
|
+
readonly GET: CtxFn<Promise<Response>>;
|
|
175
|
+
};
|
|
176
|
+
readonly "/reset": {
|
|
177
|
+
readonly GET: CtxFn<Response>;
|
|
178
|
+
};
|
|
179
|
+
readonly "/status.json": {
|
|
180
|
+
readonly GET: CtxFn<Response>;
|
|
181
|
+
};
|
|
182
|
+
readonly "/beacon": {
|
|
183
|
+
readonly POST: CtxFn<Promise<Response>>;
|
|
184
|
+
};
|
|
185
|
+
readonly "/window/start": {
|
|
186
|
+
readonly POST: CtxFn<Promise<Response>>;
|
|
187
|
+
};
|
|
188
|
+
readonly "/window/stop": {
|
|
189
|
+
readonly POST: CtxFn<Promise<Response>>;
|
|
190
|
+
};
|
|
191
|
+
readonly "/login": ProfilerPage<(c: Ctx) => ReturnType<ProfilerDeps["login_props"]>, {
|
|
192
|
+
default: CtxFn<Promise<Response>>;
|
|
193
|
+
}>;
|
|
194
|
+
readonly "/logout": {
|
|
195
|
+
readonly GET: CtxFn<Response>;
|
|
196
|
+
};
|
|
197
|
+
readonly "/view": ProfilerPage<() => {
|
|
198
|
+
base: string;
|
|
199
|
+
}, {
|
|
200
|
+
default: CtxFn<Promise<Response>>;
|
|
201
|
+
}>;
|
|
202
|
+
readonly "/reports.json": {
|
|
203
|
+
readonly GET: CtxFn<Promise<Response>>;
|
|
204
|
+
};
|
|
205
|
+
readonly "/report/[id]": ProfilerPage<(c: Ctx) => Promise<{
|
|
206
|
+
report: Awaited<ReturnType<ProfilerDeps["report_view"]>>;
|
|
207
|
+
base: string;
|
|
208
|
+
login: null;
|
|
209
|
+
exists: boolean;
|
|
210
|
+
} | {
|
|
211
|
+
report: null;
|
|
212
|
+
base: string;
|
|
213
|
+
login: string | null;
|
|
214
|
+
exists: boolean;
|
|
215
|
+
}>>;
|
|
216
|
+
readonly "/report/[id].json": {
|
|
217
|
+
readonly GET: CtxFn<Promise<Response>>;
|
|
218
|
+
readonly POST: CtxFn<Promise<Response>>;
|
|
219
|
+
};
|
|
220
|
+
readonly "/report/[id].html": {
|
|
221
|
+
readonly GET: CtxFn<Promise<Response>>;
|
|
222
|
+
readonly POST: CtxFn<Promise<Response>>;
|
|
223
|
+
};
|
|
224
|
+
readonly "/report/[id].dump": {
|
|
225
|
+
readonly GET: CtxFn<Promise<Response>>;
|
|
226
|
+
readonly POST: CtxFn<Promise<Response>>;
|
|
227
|
+
};
|
|
228
|
+
readonly "/report/[id].ogp": {
|
|
229
|
+
readonly POST: CtxFn<Promise<Response>>;
|
|
230
|
+
};
|
|
231
|
+
readonly "/report/[id]/raw": {
|
|
232
|
+
readonly GET: CtxFn<Promise<Response>>;
|
|
233
|
+
};
|
|
234
|
+
readonly "/compare/[a]/[b]": ProfilerPage<(c: Ctx) => ReturnType<ProfilerDeps["compare"]>>;
|
|
235
|
+
readonly "/replay/[id]": {
|
|
236
|
+
readonly GET: CtxFn<Promise<Response>>;
|
|
237
|
+
};
|
|
238
|
+
readonly "/site": ProfilerPage<(c: Ctx) => ReturnType<ProfilerDeps["site"]>>;
|
|
239
|
+
readonly "/source": {
|
|
240
|
+
readonly GET: CtxFn<Promise<Response>>;
|
|
241
|
+
};
|
|
242
|
+
}>;
|
|
243
|
+
/** The whole profiler route tree as a typed `path → { data, params, form, search }` map — components index it. */
|
|
244
|
+
export type ProfilerRoutes = ReturnType<typeof build_profiler_router>["$infer"];
|
|
245
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { error, page, routes } from "@ogygia/router";
|
|
2
|
+
import Dashboard from "./ui/Dashboard.svelte";
|
|
3
|
+
import Report from "./ui/Report.svelte";
|
|
4
|
+
import Run from "./ui/Run.svelte";
|
|
5
|
+
import Login from "./ui/Login.svelte";
|
|
6
|
+
import Upload from "./ui/Upload.svelte";
|
|
7
|
+
import Message from "./ui/Message.svelte";
|
|
8
|
+
import Compare from "./ui/Compare.svelte";
|
|
9
|
+
import Site from "./ui/Site.svelte";
|
|
10
|
+
// src/profiler/profiler-router.ts
|
|
11
|
+
/**
|
|
12
|
+
* The profiler's route tree — dogfood for `@ogygia/router` v2 (internal/notes/router-v2.md). Extracted
|
|
13
|
+
* from ProfilerHost so its `$infer` type is importable: the host passes bound methods across the typed
|
|
14
|
+
* `ProfilerDeps` boundary, and each UI component types its props by indexing the map —
|
|
15
|
+
* `let { data }: ProfilerRoutes['/'] = $props()` — instead of re-declaring the shape.
|
|
16
|
+
*
|
|
17
|
+
* v2 idioms on show: a flat table (the whole sitemap in one glance), the auth GUARD as the table-wide
|
|
18
|
+
* `load` (no chrome component — the profiler pages render their own Shell), a root `error` boundary,
|
|
19
|
+
* bare `{ GET }` endpoint objects, and thrown `error()` for the expired-report 404s.
|
|
20
|
+
*
|
|
21
|
+
* The one unavoidable ceremony is `V` below: each UI component types its props by indexing
|
|
22
|
+
* `ProfilerRoutes` (`ProfilerRoutes['/']`), so referencing a component VALUE here — while `$infer`
|
|
23
|
+
* (hence `ProfilerRoutes`) is still being computed — makes svelte2tsx resolve that component's own
|
|
24
|
+
* props mid-computation and report `$$ComponentProps circularly references itself` (props then fall
|
|
25
|
+
* back to `any`, silently). Erasing the refs to a bare `Component` record breaks the self-reference;
|
|
26
|
+
* `$infer` still comes from the LOADS, which keep their real return types. This is the same reason Kit
|
|
27
|
+
* generates `./$types` in a component-free module — a programmatic router has no codegen to hide it.
|
|
28
|
+
*/
|
|
29
|
+
/** Prop-erased component refs — see the file header. Referenced as `V.Dashboard` in the table so
|
|
30
|
+
* `$infer` never resolves a component's own (self-referential) props. Real types survive on `data`. */
|
|
31
|
+
const V = {
|
|
32
|
+
Dashboard,
|
|
33
|
+
Report,
|
|
34
|
+
Run,
|
|
35
|
+
Login,
|
|
36
|
+
Upload,
|
|
37
|
+
Message,
|
|
38
|
+
Compare,
|
|
39
|
+
Site
|
|
40
|
+
};
|
|
41
|
+
/** Shared lookup for the report representations: the stored report (memory or the storage backend),
|
|
42
|
+
* or a thrown 404. */
|
|
43
|
+
async function report_or_404(d, id, fn) {
|
|
44
|
+
const s = await d.report_load(id);
|
|
45
|
+
if (!s) error(404, "That report has expired.");
|
|
46
|
+
return await fn(s);
|
|
47
|
+
}
|
|
48
|
+
/** Build the profiler router. Auth is the table-wide `load` (a redirect/deny short-circuits every
|
|
49
|
+
* route; on allow it seeds `{ base }`); thrown `error()` renders the root `Message` boundary.
|
|
50
|
+
* `/report/[id]` is PUBLIC (a share `#fragment` decrypts client-side); its `.json`/`.dump`/`/raw`
|
|
51
|
+
* siblings keep the suffix so they stay behind the guard's server-data gate. Endpoints are
|
|
52
|
+
* the plain `{ GET }` object form (bare handlers; `c.params.id` reads from the table key). */
|
|
53
|
+
function build_profiler_router(d) {
|
|
54
|
+
return routes({
|
|
55
|
+
"/": page(V.Dashboard, { load: (c) => d.dashboard(c) }),
|
|
56
|
+
"/run": page(V.Run, { load: (c) => d.run_page(c) }),
|
|
57
|
+
"/page": { GET: (c) => d.record_page(c) },
|
|
58
|
+
"/reset": { GET: (c) => d.reset(c) },
|
|
59
|
+
"/status.json": { GET: (c) => c.json(d.status(c)) },
|
|
60
|
+
"/beacon": { POST: (c) => d.beacon(c) },
|
|
61
|
+
"/window/start": { POST: (c) => d.window_start(c) },
|
|
62
|
+
"/window/stop": { POST: (c) => d.window_stop(c) },
|
|
63
|
+
"/login": page(V.Login, {
|
|
64
|
+
load: (c) => d.login_props(c),
|
|
65
|
+
actions: { default: (c) => d.login(c) }
|
|
66
|
+
}),
|
|
67
|
+
"/logout": { GET: (c) => d.logout(c) },
|
|
68
|
+
"/view": page(V.Upload, {
|
|
69
|
+
load: () => ({ base: d.base }),
|
|
70
|
+
actions: { default: (c) => d.upload(c) }
|
|
71
|
+
}),
|
|
72
|
+
"/reports.json": { GET: async (c) => c.json(await d.list_stored()) },
|
|
73
|
+
"/report/[id]": page(V.Report, { load: async (c) => {
|
|
74
|
+
const stored = await d.authed(c) ? await d.report_load(c.params.id) : d.report_stored(c.params.id);
|
|
75
|
+
if (stored && await d.authed(c)) return {
|
|
76
|
+
report: await d.report_view(stored),
|
|
77
|
+
base: d.base,
|
|
78
|
+
login: null,
|
|
79
|
+
exists: true
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
report: null,
|
|
83
|
+
base: d.base,
|
|
84
|
+
login: d.login_url(c),
|
|
85
|
+
exists: !!stored
|
|
86
|
+
};
|
|
87
|
+
} }),
|
|
88
|
+
"/report/[id].json": {
|
|
89
|
+
GET: (c) => report_or_404(d, c.params.id, d.report_json),
|
|
90
|
+
POST: (c) => d.report_posted(c, "json")
|
|
91
|
+
},
|
|
92
|
+
"/report/[id].html": {
|
|
93
|
+
GET: (c) => report_or_404(d, c.params.id, (s) => d.report_html(s, c)),
|
|
94
|
+
POST: (c) => d.report_posted(c, "html")
|
|
95
|
+
},
|
|
96
|
+
"/report/[id].dump": {
|
|
97
|
+
GET: (c) => report_or_404(d, c.params.id, d.report_dump_json),
|
|
98
|
+
POST: (c) => d.report_posted(c, "dump")
|
|
99
|
+
},
|
|
100
|
+
"/report/[id].ogp": { POST: (c) => d.report_posted(c, "ogp") },
|
|
101
|
+
"/report/[id]/raw": { GET: (c) => report_or_404(d, c.params.id, d.report_raw) },
|
|
102
|
+
"/compare/[a]/[b]": page(V.Compare, { load: (c) => d.compare(c.params.a, c.params.b) }),
|
|
103
|
+
"/replay/[id]": { GET: (c) => d.replay(c.params.id, c) },
|
|
104
|
+
"/site": page(V.Site, { load: (c) => d.site(c) }),
|
|
105
|
+
"/source": { GET: (c) => d.source(c) }
|
|
106
|
+
}, {
|
|
107
|
+
base: d.base,
|
|
108
|
+
error: V.Message,
|
|
109
|
+
load: async (c) => await d.auth_guard(c) ?? { base: d.base },
|
|
110
|
+
miss: () => error(404, "Unknown profiler page.")
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
export { build_profiler_router };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/profiler/refresh-stub.ts
|
|
2
|
+
/**
|
|
3
|
+
* A REDIRECT WRITTEN AS A PAGE. A prerendered route that redirects has no server to answer 3xx, so
|
|
4
|
+
* the build writes a stub in its place — `<meta http-equiv="refresh" content="0;url=/to">` (and a
|
|
5
|
+
* script that sets `location.href`) — and the host answers it 200. Profiled as it is, it read as
|
|
6
|
+
* "only 118 bytes: not a render". The page it sends the visitor to is the one to profile.
|
|
7
|
+
*/
|
|
8
|
+
/** the largest body read as a stub: a real page with a refresh tag is a page, not a redirect */
|
|
9
|
+
const STUB_MAX_BYTES = 2048;
|
|
10
|
+
/** The path a redirect stub sends the visitor to (`/to?q`), or null when `body` is not one. */
|
|
11
|
+
function refresh_target(body, origin) {
|
|
12
|
+
if (!body || body.length > STUB_MAX_BYTES) return null;
|
|
13
|
+
const lower = body.toLowerCase();
|
|
14
|
+
const at = lower.indexOf("http-equiv=\"refresh\"");
|
|
15
|
+
if (at < 0) return null;
|
|
16
|
+
const open = lower.lastIndexOf("<meta", at);
|
|
17
|
+
const close = lower.indexOf(">", at);
|
|
18
|
+
if (open < 0 || close < 0) return null;
|
|
19
|
+
const tag = body.slice(open, close);
|
|
20
|
+
const c = tag.toLowerCase().indexOf("content=\"");
|
|
21
|
+
if (c < 0) return null;
|
|
22
|
+
const content = tag.slice(c + 9, tag.indexOf("\"", c + 9));
|
|
23
|
+
const u = content.toLowerCase().indexOf("url=");
|
|
24
|
+
if (u < 0) return null;
|
|
25
|
+
let to = content.slice(u + 4).trim();
|
|
26
|
+
const q = to[0];
|
|
27
|
+
if (q === "'" || q === "\"") to = to.endsWith(q) && to.length > 1 ? to.slice(1, -1) : to.slice(1);
|
|
28
|
+
if (!to) return null;
|
|
29
|
+
try {
|
|
30
|
+
const url = new URL(to, origin);
|
|
31
|
+
if (url.origin !== new URL(origin).origin) return null;
|
|
32
|
+
return url.pathname + url.search;
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export { refresh_target };
|