Skip to content

API

ogygia/insights

Findings computed from the event layer.

On this page

This page is generated from the package’s own .d.ts files on every build, so the signatures, descriptions and examples below always match the source.

import { … } from 'ogygia/insights';

Exports analyze_cpu · analyze_page · analyze_session · apply_attr_rules · assets_diff · browser_of · compare · defer_keys · dev_compile_ms · devtools_switched_on · dynamic_imports · engine_of · explain_held_open · first_difference · hazard_fix · hazard_words · hole_segments · html_diff · hydration_hazards · inline_threshold_tune · is_trace · late_found · lcp_font · lcp_rivals · prebundled_names · rate · runtime_scripts · session_timeline · static_imports · switch_in_url · testing_lean · vital_parts · vitals_moved · weigh_assets · without_switch · write_switch · DEVTOOLS_COOKIE · DEVTOOLS_ON · DEVTOOLS_PARAM · DOM_LARGE · HOLE_SEG_LABEL · TESTING_COOKIE · TESTING_ENGINE_VERSION · TESTING_LEAN · TESTING_PARAM · AssetRef · AssetsDiff · AttrRule · CpuFn · CpuSummary · Failure · FpDrift · HeldOpen · HoleBatch · HoleFailure · HoleWait · HtmlDiff · InteractionCpuInput · IslandCode · IslandRow · KeptIsland · NavPhases · PageAssets · PageFinding · PageInput · PageInteraction · PageNav · PageReport · RatedVital · RegionFact · RestoreEvent · RuntimeScripts · ServerProfileBrief · SessionClick · SessionData · SessionFinding · SessionFrame · SessionInteraction · SessionNav · SessionReport · SessionRequest · SessionShift · TimelineItem · TimelineLane · VitalMove · VitalPart · Weight · AssetKind · CodeKind · HydrationStatus · IslandHazard · PartedVital · Ref

analyze_cpu

function

function analyze_cpu(trace: SelfProfileTrace, windows: IslandWindow[], longtasks: {
  t: number;
  ms: number;
}[], page_url: string): CpuSummary;

analyze_page

function

function analyze_page(page: PageInput, regions: RegionFact[], failures?: Failure[], now?: number, cpu?: CpuSummary | null): PageReport;

analyze_session

function

function analyze_session(s: SessionData, names: (fp: string) => string, page_url: string): SessionReport;

apply_attr_rules

function

Split changes into those rules ignore and those left.

function apply_attr_rules(changes: AttrChange[], rules: AttrRule[]): {
  left: AttrChange[];
  ignored: AttrChange[];
};

assets_diff

function

function assets_diff(a: WeighedLike, b: WeighedLike): AssetsDiff;

browser_of

function

The browser a page ran in, by name and major version (Safari 18, Chrome 141, Firefox 133, Edge 141), from its user-agent string by plain search; null when it says nothing known. Some findings are one engine's (Safari reports no layout shifts; a preload one engine reuses another fetches again): the profiler's report and the Page tab name which.

function browser_of(ua: string | undefined): string | null;

compare

function

Islands matched by entry and their place among its copies; a moved fingerprint, explained.

function compare(was: Kept[], now: Kept[]): FpDrift[];

defer_keys

function

The page seed's defer ids → the page.data key each stands for (top level and one level down): the seed (application/ogygia-page, the flat devalue form) holds a defer marker [name, i] per streamed promise, the marker's payload the id the streamed resolve script settles. From the whole document's HTML or the seed script's own text. indexOf only.

function defer_keys(html: string, marker?: string): Map<number, string>;

dev_compile_ms

function

On the dev server: how much of an island's code load was the server COMPILING files on this request — the time (overlaps counted once) the server took to answer each file fetched in its load window, when that took 80 ms or more (a transform: a warm dev server answers in a few). 0 off the dev server, or when the visit does not list the files.

function dev_compile_ms(page: Pick<PageInput, "dev" | "visit">, r: Pick<IslandRow, "t0" | "load_ms">): number;

devtools_switched_on

function

Is it on in this browser (false off the browser, or with cookies blocked).

function devtools_switched_on(): boolean;

dynamic_imports

function

import("x") / import('x') specifiers in a script's text (Kit's inline start).

function dynamic_imports(text: string): string[];

engine_of

function

The engine family a user agent runs on: what makes two browsers measure a page differently (Edge and Chrome are one; Safari reports no layout shifts and refetches a preload Chromium reuses).

function engine_of(ua: string | undefined): "chromium" | "webkit" | "gecko" | null;

explain_held_open

function

THE DOCUMENT HELD OPEN: the page painted early, but its HTML kept coming — and islands wake only once the whole document is in (DOMContentLoaded, the wake gate), so every island waited for its end, even one that reads nothing late. Usually a load's streamed promise: the response stays open until the last one settles. From the browser (the navigation's first and last byte, the paint, the islands' hydrate steps after the end) and, when known, what held it (held_open: each streamed promise by its page.data key and when it settled). With no visit, the profiled render alone says it (its chunks: the early part, then the tail after the longest pause).

function explain_held_open(page: PageInput, rows: readonly IslandRow[], name_of: (fp: string) => string): PageFinding | null;

first_difference

function

Where two strings first differ, as a short "server … / now …" pair (the markup-changed proof).

function first_difference(a: string, b: string, span?: number): {
  at: number;
  server: string;
  now: string;
} | null;

hazard_fix

function

The fix for those lines: what to do with each kind.

function hazard_fix(lines: readonly IslandHazard[]): string;

hazard_words

function

"In its own code, the likeliest: X.svelte:4 (…) awaits …; Y.svelte:8 reads Date.now( while rendering …" — said the same by the profiler's report and the devtools' Page tab

function hazard_words(lines: readonly IslandHazard[]): string;

hole_segments

function

The segments, in order. Without a timing, one: the wait. A request the document preloaded starts before the fallback counts: its segments start where it left.

function hole_segments(h: HoleTimes): {
  k: HoleSegKind;
  a: number;
  b: number;
}[];

html_diff

function

function html_diff(server: string, browser: string, context?: number, max_d?: number): HtmlDiff;

hydration_hazards

function

function hydration_hazards(source: string): Hazard[];

inline_threshold_tune

function

THE INLINE THRESHOLD THAT TAKES THE SMALL SHEETS OFF THE PAINT: Kit inlines its route stylesheets under inlineStyleThreshold (ogygia's island sheets follow the same setting), as <style> in the HTML — no request to wait on. Of the blocking build stylesheets (/_app/immutable/), the smallest first, while each is 8 KB or less and the HTML grows by 24 KB at most: the threshold that inlines them (just over the largest, in whole KB), how many, how many bytes. Null under two sheets. The profiler's page weight (style) and the Page tab's resources (css) both ask it.

function inline_threshold_tune(blocking: readonly {
  url: string;
  kind: string;
  bytes: number;
}[]): {
  threshold: number;
  files: number;
  bytes: number;
} | null;

is_trace

function

Is this a trace the analyzer can read.

function is_trace(x: unknown): x is SelfProfileTrace;

late_found

function

WHY THE BROWSER FOUND THE LARGEST PAINT'S FILE LATE, from what the visit saw: a CSS background (found only once its stylesheet is in and the element laid out), an image its island's code added as it woke (the request began after it hydrated: the server HTML had none), or an <img> in the HTML at the low priority an image starts at. The generic advice when none of them is it.

function late_found(p: NonNullable<NonNullable<PageInput["visit"]>["paints"]> | undefined, res: {
  start: number;
  req_start?: number;
} | undefined, islands: readonly PageIsland[], name_of: (fp: string | undefined) => string): string;

lcp_font

function

THE FONT THE LARGEST PAINT WAITED FOR: a text paint (no file of its own) that came within 200 ms of a font file landing after the first paint, whose face hides its text until then (font-display auto/block). The page's text was there all along, invisible: the font, not the server, set the LCP. Shared by the Page tab's split and the profiler's LCP gap.

function lcp_font(visit: PageInput["visit"] | null | undefined): {
  family: string;
  file: string;
  end: number;
} | null;

lcp_rivals

function

WHAT THE LARGEST PAINT'S DOWNLOAD SHARED THE NETWORK WITH: images far below the first screen, not lazy, whose own download overlapped the largest paint's file (or, a text paint, the wait from the first byte to the paint). Shared by the Page tab's split and the profiler's LCP gap.

function lcp_rivals(visit: PageInput["visit"] | null | undefined): {
  bytes: number;
  files: string[];
} | null;

prebundled_names

function

The packages a dev server's _metadata.json optimized keys name: svelte/internal/client → svelte, @scope/lib/x → @scope/lib, `ogygia >

function prebundled_names(keys: readonly string[]): string[];

rate

function

function rate(key: RatedVital["key"], value: number): Rating;

runtime_scripts

function

SCRIPTS LOADED AT RUNTIME — what the browser fetched that neither the HTML nor any module's imports name: a script that loads more scripts (a CDN component library fetching its parts, a tag manager, a runtime's on-demand chunk). The server cannot see them coming; the profiler user's own visit saw them. until: the end of "at start" on the visit's clock (a lazy island woken by a scroll later does not count). Null when there are none.

function runtime_scripts(assets: readonly {
  url: string;
}[], resources: readonly {
  url: string;
  type: string;
  start: number;
  size?: number;
  transfer?: number;
}[], origin: string, until: number): RuntimeScripts | null;

session_timeline

function

The session on one clock, lane by lane (the Record tab draws it). Capped per lane.

function session_timeline(s: SessionData, names: (fp: string) => string): TimelineLane[];

static_imports

function

The STATIC imports of a bundled module (import{a}from"./x.js", import"./y.js", export*from"./z.js"): specifiers after from / a bare import that look like module paths (relative or absolute, ending in .js/.mjs). Bundler output writes them plainly; a string that merely contains such text inflates nothing that does not exist — every hit is fetched, and a miss is dropped.

function static_imports(code: string): string[];

switch_in_url

function

What the URL asks for: true on, false off, null nothing (absent or another value).

function switch_in_url(url: URL): boolean | null;

testing_lean

function

Lean testing in this browser: the cookie, or the URL param (false off the browser).

function testing_lean(): boolean;

vital_parts

function

function vital_parts(page: PageInput, key: PartedVital): VitalPart[] | null;

vitals_moved

function

WHICH VITALS MOVED between two visits of a page, past the noise, and for a split vital the part that moved most. ONE rule for both tools: the profiler's "since your last profile" and the Page tab's "since your last load" read the same moves. parts(side, key) gives a side's parts.

function vitals_moved(a: Record<string, number | undefined>, b: Record<string, number | undefined>, parts: (side: "a" | "b", key: PartedVital) => VitalPart[] | null): VitalMove[];

weigh_assets

function

function weigh_assets(opts: WeighOptions): Promise<PageAssets>;

without_switch

function

The URL without the switch param (the address after it was applied).

function without_switch(url: URL): URL;

write_switch

function

Turn it on / off in this browser (the cookie; the caller updates what shows).

function write_switch(on: boolean): void;

const

THE DEVTOOLS SWITCH — whether devtools is on in this browser, in a BUILD with devtools (a QA or preview deploy; the dev server always has it on). One cookie decides all of it: the launcher shows, the page is measured from the start (runtime/beacon.ts), what islands leave running is watched (leftovers.ts), and the server writes its event side-channel (hooks.ts). Off — the default — a visitor sees nothing and pays nothing.

A developer turns it on for their own browser, and it stays on across navigations and reloads:

  • ?og-devtools=on / ?og-devtools=off on any page (the server sets the cookie on that very response, the browser too — a prerendered page has no server — and the address drops the param);
  • ogygia.devtools.on() / .off() in the console;
  • "hide" in the dock turns it off.

(The value is not the 1 an earlier launcher set on a plain click: a browser that clicked the old button once stays off until it opts in.)

A plain module: the server, the runtime and the launcher all read it.

DEVTOOLS_ON

const

const DEVTOOLS_ON = "on"

DEVTOOLS_PARAM

const

The URL param that flips it: ?og-devtools=on / ?og-devtools=off.

const DEVTOOLS_PARAM = "og-devtools"

DOM_LARGE

const

a page of this many elements or more is named (the beacon reports from the same count)

const DOM_LARGE = 1500

HOLE_SEG_LABEL

const

const HOLE_SEG_LABEL: Record<HoleSegKind, string>

const

LEAN TESTING: og_testing=lean (or ?og-testing=lean) beside the devtools switch — the testing API (window.__ogygia_testing) works in full, and nothing else of devtools runs: no launcher, no dock, no watcher of the page's timers, no server event side-channel in the HTML. The page loads what it loads with devtools off, plus the testing engine when a test first asks. An engine that does not know the cookie ignores it.

TESTING_ENGINE_VERSION

const

The report shape the page's testing engine answers (window.__ogygia_testing.version). The engine ships with the SITE's build and @ogygia/testing with the TEST's package, so the two can differ: the test side reads this from the page, fills in what it can for an older engine, and warns once.

Each version, and what it DETECTS differently (results across two versions compare only where nothing below changed):

1 — 0.8.2. 2 — hydration key, session navigations (with what started each), finding names, counts.cls, engine on every report, hydration t0 / done. Detection: a router navigation that never swapped the page no longer starts a new page for page(). Hydration status: unchanged from 1 (an island that reads clean on one and changed on the other differed in that run — input landing while it hydrated is the usual cause; 3 flags it as input). 3 — hydration change (attributes / markup), attributes, input, ignoreAttributes; page ogygia (what measuring cost). Detection: unchanged — changed means what it meant in 2; the new fields only say what kind of change it was.

const TESTING_ENGINE_VERSION = 3

TESTING_LEAN

const

const TESTING_LEAN = "lean"

TESTING_PARAM

const

const TESTING_PARAM = "og-testing"

AssetRef

interface

interface AssetRef {/*…*/}

AssetRef.url

url: string;

AssetRef.kind

kind: AssetKind;

AssetRef.blocking

the browser must have it before the first paint (a head stylesheet, a classic sync script)

blocking: boolean;

AssetRef.via

how the document asks for it (island: the runtime loads it to wake an island)

via: "script" | "modulepreload" | "preload" | "stylesheet" | "inline-import" | "img" | "static-import" | "island" | "route-node" | "runtime-phase";

AssetRef.lazy

loads later, on demand (an island that wakes when visible / on interaction), not at start

lazy?: boolean;

AssetRef.runtime

the ogygia runtime's own script (data-ogygia-runtime): the parts it loads itself are its own

runtime?: true;

AssetRef.phase

a part of the ogygia runtime it loads on demand as islands wake (and what that part imports)

phase?: true;

AssetsDiff

interface

interface AssetsDiff {/*…*/}

AssetsDiff.js

JS at start, decoded, then → now

js: {
    a: number;
    b: number;
  };

AssetsDiff.lazy_js

lazy_js: {
    a: number;
    b: number;
  };

AssetsDiff.added

added: AssetChange[];

AssetsDiff.removed

removed: AssetChange[];

AssetsDiff.grew

grew: AssetChange[];

AssetsDiff.shrank

shrank: AssetChange[];

AssetsDiff.to_start

the same file, now loaded at start (it loaded later before: an island's wake changed)

to_start: AssetChange[];

AssetsDiff.to_later

the same file, now loaded later (it loaded at start before)

to_later: AssetChange[];

AttrRule

interface

interface AttrRule {/*…*/}

AttrRule.tag

tag?: Pat;

AttrRule.name

name: Pat;

AttrRule.value

value?: Pat;

CpuFn

interface

interface CpuFn {/*…*/}

CpuFn.name

name: string;

CpuFn.file

file: string;

CpuFn.line

line: number | null;

CpuFn.kind

kind: CodeKind;

CpuFn.self_ms

self_ms: number;

CpuFn.total_ms

total_ms: number;

CpuSummary

interface

interface CpuSummary {/*…*/}

CpuSummary.window_ms

sampled span (first to last sample)

window_ms: number;

CpuSummary.busy_ms

busy_ms: number;

CpuSummary.by_kind

by_kind: {
    kind: CodeKind;
    ms: number;
  }[];

CpuSummary.fns

fns: CpuFn[];

CpuSummary.files

files: {
    file: string;
    kind: CodeKind;
    ms: number;
  }[];

CpuSummary.islands

per island fingerprint: CPU inside its hydrate window and the functions that took it

islands: Record<string, {
    ms: number;
    top: CpuFn[];
  }>;

CpuSummary.outside

long tasks outside every island's hydrate window

outside: {
    ms: number;
    top: CpuFn[];
  };

CpuSummary.interval_ms

the sampling interval seen (ms)

interval_ms: number;

CpuSummary.by_host

main-thread ms per other origin (the nearest frame with a script URL decides)

by_host?: Record<string, number>;

Failure

interface

interface Failure {/*…*/}

Failure.fp

fp?: string;

Failure.message

message: string;

Failure.span

its wake → the failure (page clock): the island was loading, and held its turn, meanwhile

span?: [number, number];

Failure.turn

when its hydrate step began (page clock): from here to the failure it held the thread, as a hydrating island does

turn?: number;

FpDrift

interface

interface FpDrift {/*…*/}

FpDrift.entry

entry: string;

FpDrift.fp_was

fp_was: string;

FpDrift.fp_now

fp_now: string;

FpDrift.path

the first differing path in the props (stamp, items[2].id), when the props parse

path?: string;

FpDrift.was

the values there, clipped (or the text around the first difference)

was?: string;

FpDrift.now

now?: string;

HeldOpen

interface

interface HeldOpen {/*…*/}

HeldOpen.side

side: "server" | "browser";

HeldOpen.keys

keys: {
    key: string | null;
    at: number;
  }[];

HeldOpen.early_ms

server side: the early part's last byte left at early_ms, the tail's (late_bytes) at late_ms

early_ms?: number;

HeldOpen.early_bytes

early_bytes?: number;

HeldOpen.late_ms

late_ms?: number;

HeldOpen.late_bytes

late_bytes?: number;

HeldOpen.islands

server side: the islands the page renders (the finding needs some to be about)

islands?: number;

HoleBatch

interface

A batch request (runtime/frame-nav.ts) that carried fewer holes than it was sent for: the rest fetched on their own right after it.

interface HoleBatch {/*…*/}

HoleBatch.sent

sent: number;

HoleBatch.delivered

delivered: number;

HoleBatch.status

the response's status (0: the request failed)

status: number;

HoleBatch.refused

refused?: "redirected" | "document";

HoleBatch.final_url

final_url?: string;

HoleBatch.names

the holes it was for (names)

names: string[];

HoleFailure

interface

interface HoleFailure {/*…*/}

HoleFailure.fp

fp?: string;

HoleFailure.name

name: string;

HoleFailure.endpoint

endpoint?: string;

HoleFailure.reason

reason: RegionRefusal | "error";

HoleFailure.final_url

final_url?: string;

HoleFailure.message

message?: string;

HoleFailure.attempts

attempts: number;

HoleWait

interface

interface HoleWait {/*…*/}

HoleWait.name

name: string;

HoleWait.wait_ms

ms from the hole's fetch start to its answer's swap

wait_ms: number;

HoleWait.below_fold

below the first screen (its fallback is not what the visitor looks at first)

below_fold: boolean;

HoleWait.server_ms

its server render, per request, when known (the profiler recorded the hole's requests, or the answer's Server-Timing said)

server_ms?: number;

HoleWait.server_queue_ms

its wait on the server for a render slot, before the render (Server-Timing / the profiler)

server_queue_ms?: number;

HoleWait.endpoint

the hole's endpoint (the devtools find its element by it: a hole has no fingerprint)

endpoint?: string;

HoleWait.shown_at

page times: when the fallback began to count (the paint, or the hole's own start), and its request as the browser timed it — left, first byte back, last byte. They split the wait.

shown_at?: number;

HoleWait.left_at

left_at?: number;

HoleWait.first_at

first_at?: number;

HoleWait.end_at

end_at?: number;

HoleWait.batch_size

it came in ONE batch request with the holes that started with it: how many (its own part landed when the server rendered it — first_at is that, out of order). No browser gate held it.

batch_size?: number;

HtmlDiff

interface

interface HtmlDiff {/*…*/}

HtmlDiff.hunks

hunks: DiffHunk[];

HtmlDiff.removed

tokens removed / added

removed: number;

HtmlDiff.added

added: number;

HtmlDiff.partial

the diff gave up (too many changes) and shows only where they start

partial: boolean;

InteractionCpuInput

interface

interface InteractionCpuInput {/*…*/}

InteractionCpuInput.t

t: number;

InteractionCpuInput.mapped

the frames' lines are the source's (mapped through a source map): else a line is the served code's, and only the function and its file are quoted

mapped?: boolean;

InteractionCpuInput.wait

wait: {
    ms: number;
    top: CpuFn[];
  } | null;

InteractionCpuInput.handler

handler: {
    ms: number;
    top: CpuFn[];
  } | null;

IslandCode

interface

interface IslandCode {/*…*/}

IslandCode.fp

fp?: string;

IslandCode.name

name: string;

IslandCode.bytes

the island's app code, served size in dev

bytes: number;

IslandCode.top

its heaviest modules

top: {
    file: string;
    bytes: number;
  }[];

IslandCode.barrels

re-export barrels still imported whole, with the app modules each drags in

barrels: {
    file: string;
    fanout: number;
  }[];

IslandCode.hazards

its components' lines that draw differently in the browser (the dev server read their sources)

hazards?: IslandHazard[];

IslandRow

interface

interface IslandRow {/*…*/}

IslandRow.fp

fp: string;

IslandRow.name

name: string;

IslandRow.wake

wake: string;

IslandRow.load_ms

wake began → module + hydrate core loaded

load_ms: number;

IslandRow.queue_ms

loaded → its scheduler turn (the queue behind other islands, and the DCL/paint gate)

queue_ms: number | null;

IslandRow.hydrate_ms

the synchronous hydrate step

hydrate_ms: number;

IslandRow.t0

t0: number;

IslandRow.done

done: number;

IslandRow.recovered

recovered: boolean;

IslandRow.changed

changed: boolean;

IslandRow.shift

layout shift (summed) this island caused within a moment of hydrating

shift: number;

IslandRow.longtask_ms

long-task ms that overlapped its hydration window

longtask_ms: number;

IslandRow.early_ms

the first interaction reached it before it was awake (ms early), when it did

early_ms: number | null;

IslandRow.below_fold

below_fold: boolean;

IslandRow.cpu_ms

main-thread CPU sampled inside its hydrate window (null: no trace)

cpu_ms: number | null;

KeptIsland

interface

One island as one load (or render) had it.

interface KeptIsland {/*…*/}

KeptIsland.entry

entry: string;

KeptIsland.fp

fp: string;

KeptIsland.props

props?: string;

interface

The document's steps before its first byte (ms).

the request until the server's first byte

PageAssets

interface

interface PageAssets {/*…*/}

PageAssets.assets

assets: WeighedAsset[];

PageAssets.unscoped

components whose styles shipped WITHOUT their scope (the build's fallback marker, in a stylesheet or an inline <style>): their rules apply to the whole page

unscoped?: (UnscopedStyles & {
    in: string;
  })[];

PageAssets.missed

references that could not be weighed (a 404, a timeout, the budget ran out)

missed: string[];

PageAssets.missed_why

each of those, and why: budget (time ran out before it), timeout, status 404, file cap, error: … — a total with only status misses can be trusted; budget / timeout ones vary

missed_why?: {
    url: string;
    why: string;
  }[];

PageAssets.html

the HTML itself

html: {
    bytes: number;
    wire: number;
  };

PageAssets.inline

inline <script> / <style> text in the document, decoded bytes

inline: {
    script: number;
    style: number;
  };

PageAssets.totals

totals: {
    js: number;
    js_wire: number;
    css: number;
    css_wire: number;
    font: number;
    image: number;
    wire: number;
    lazy_js: number;
    blocking: number;
    blocking_count: number;
    js_files: number;
  };

PageFinding

interface

interface PageFinding {/*…*/}

PageFinding.code

code: string;

PageFinding.severity

severity: Severity;

PageFinding.message

message: string;

PageFinding.fix

fix?: string;

PageFinding.fps

the islands it is about (to light up on the page)

fps: string[];

PageFinding.dev_compile

it is the dev server's own doing: its first compile of the page (a slow first byte while the page compiled on its first request), or the CSS it adds with JavaScript moving the page (a shift). A reload reads it differently: "since your last load" never calls it new or fixed

dev_compile?: true;

PageInput

interface

interface PageInput {/*…*/}

PageInput.dev

measured on the dev server (it compiles a page on its first request: a slow first byte there is not the app's)

dev?: boolean;

PageInput.server_profiles

DEVTOOLS ONLY (the profiler's report is itself a profile): the profiler's last run of this page, and of the pages it navigated to by path. Present (even empty) means: say what the server did, or how to find out

server_profiles?: Record<string, ServerProfileBrief>;

PageInput.server_profile

server_profile?: ServerProfileBrief;

PageInput.interaction_cpu

the slowest interaction's CPU, when a trace of it was taken

interaction_cpu?: InteractionCpuInput;

PageInput.held_open

what held the document open: its streamed promises by page.data key — when each settled (side 'server': left the server, ms into the render, from the profiled render's chunks; 'browser': arrived, ms from the navigation, stamped by the resolve global)

held_open?: HeldOpen;

PageInput.vitals

vitals: {
    ttfb?: number;
    fcp?: number;
    lcp?: number;
    cls?: number;
    inp?: number;
  };

PageInput.visit

visit: {
    nav?: {
      dcl?: number;
      load?: number;
      res_start?: number;
      start_hidden?: true;
      res_end?: number;
      size?: number;
      raw?: true;
      dom_interactive?: number;
      phases?: NavPhases;
      server_timing?: {
        name: string;
        ms: number;
        desc?: string;
      }[];
    };
    scripts?: {
      url: string;
      ms: number;
      count: number;
    }[];
    warnings?: {
      code: string;
      message: string;
      file?: string;
      fp?: string;
      t?: number;
    }[];
    paints?: {
      fcp?: number;
      lcp?: number;
      lcp_fp?: string;
      lcp_tag?: string;
      lcp_url?: string;
      lcp_replaced?: true;
      lcp_lazy?: true;
      lcp_priority?: "high" | "low";
      held?: {
        outcome: "arrived" | "input" | "cap" | "none";
        resources: number;
        waited_ms: number;
      };
    };
    resources?: {
      url: string;
      type: string;
      start: number;
      end: number;
      req_start?: number;
      res_start?: number;
      transfer?: number;
      size?: number;
      blocking?: boolean;
      weighed?: true;
    }[];
    resource_totals?: {
      type: string;
      count: number;
      transfer: number;
      size: number;
    }[];
    viewport?: [number, number];
    origin?: string;
    named?: string[];
    in_page?: string[];
    preload_misses?: PreloadMiss[];
    preloads_unused?: {
      url: string;
      as: string;
      bytes: number;
    }[];
    uncompressed?: {
      url: string;
      type: string;
      bytes: number;
    }[];
    font_faces?: {
      family: string;
      display: string;
      urls: string[];
    }[];
    images_oversized?: {
      url: string;
      natural: [number, number];
      shown: [number, number];
      dpr: number;
      bytes: number;
      fp?: string;
    }[];
    scroll_jank?: {
      start: number;
      ms: number;
      scripts: {
        url: string;
        fn: string;
        invoker: string;
        ms: number;
        island?: string;
      }[];
    }[];
    forced_layout?: {
      start: number;
      end: number;
      ms: number;
      url: string;
      fn: string;
    }[];
    images_eager_below?: {
      url: string;
      top: number;
      bytes: number;
      fp?: string;
    }[];
    images_unsized?: string[];
    dom?: {
      nodes: number;
      depth: number;
      deepest: string;
      widest: {
        at: string;
        children: number;
      };
      islands: {
        fp: string;
        nodes: number;
      }[];
    };
    entry_fallbacks?: {
      entry: string;
      src: string;
      recovered: boolean;
      name?: string;
    }[];
    refetched?: {
      url: string;
      how: "revalidated" | "downloaded";
      bytes: number;
      ms: number;
      entry?: string;
      runtime?: boolean;
      name?: string;
    }[];
    interaction?: PageInteraction;
    navs?: PageNav[];
  } | null;

PageInput.islands

islands: PageIsland[];

PageInput.firsts

firsts: {
    fp: string;
    t: number;
    type: string;
  }[];

PageInput.shifts

shifts: {
    t: number;
    value: number;
    fp?: string;
    tag?: string;
  }[];

PageInput.longtasks

longtasks: {
    t: number;
    ms: number;
  }[];

PageInput.longtasks_from

frames: the browser has no long-task timing — these are frames drawn more than 50 ms late

longtasks_from?: "frames";

PageInput.shifts_from

boxes: the browser has no layout-shift timing — the shifts are islands whose box changed as they woke, scored the way a layout shift is (an estimate)

shifts_from?: "boxes";

PageInput.snapshots

(from: a big island's window — where it starts in the markup without comments)

snapshots?: {
    fp: string;
    ssr: string;
    hydrated: string;
    final?: string;
    from?: number;
  }[];

PageInput.empty_slots

awake islands showing a children slot with nothing in it (read off the DOM; devtools only)

empty_slots?: string[];

PageInput.hole_failures

holes whose answer never came (the bus; devtools only)

hole_failures?: HoleFailure[];

PageInput.island_code

each island's app code in dev (the dev server's module graph; devtools only)

island_code?: IslandCode[];

PageInput.hole_waits

how long each hole's fallback showed before its answer (the bus; devtools only)

hole_waits?: HoleWait[];

PageInput.hole_batches

batch requests that did not carry all their holes (the bus; devtools only)

hole_batches?: HoleBatch[];

PageInput.restore_events

what the restorer of a server transform reported (runtime/restore.ts __og_restore_log)

restore_events?: RestoreEvent[];

PageInput.fp_drift

islands whose fingerprint moved between two loads of this page (fp-drift.ts)

fp_drift?: {
    name: string;
    fp?: string;
    path?: string;
    was?: string;
    now?: string;
  }[];

PageInput.prebundled

the dev server's pre-bundled packages (its .vite/deps/_metadata.json), read when the page loaded one: a package also served raw from its folder runs as two copies

prebundled?: string[];

PageInput.browser

the browser the page ran in (browser_of its user agent: Safari 26, Chrome 141)

browser?: string;

PageInput.dock_open_at

when the devtools' own dock began to show as the page loaded (open, remembered from the last page), page clock; absent when it was opened later or not at all

dock_open_at?: number;

PageInput.leftovers

what islands that left the page left running (devtools/leftovers.ts; devtools only)

leftovers?: {
    name: string;
    intervals: number;
    listeners: string[];
    fires: number;
    last_ago?: number;
  }[];

PageInput.scroll_blockers

wheel / touch listeners still attached that hold scrolling (not passive), by the island (or the file) that added each (devtools/leftovers.ts; devtools only)

scroll_blockers?: {
    owner: string;
    type: string;
    on: string;
    forced: boolean;
  }[];

PageInput.bfcache

what keeps the page out of the back/forward cache: who added a window unload listener, and the browser's own reasons when this load came from Back and was not restored (devtools only)

bfcache?: {
    unload: string[];
    not_restored?: string[];
  };

PageInteraction

interface

interface PageInteraction {/*…*/}

PageInteraction.name

name: string;

PageInteraction.t

t: number;

PageInteraction.ms

ms: number;

PageInteraction.delay

input → its handlers began (the main thread was busy)

delay: number;

PageInteraction.processing

the handlers

processing: number;

PageInteraction.presentation

handlers done → the next frame painted

presentation: number;

PageInteraction.target

target: string;

PageInteraction.fp

fp?: string;

PageInteraction.scripts

(forced: of a script's time, the browser laying the page out because it read sizes after a change)

scripts?: {
    url: string;
    fn: string;
    invoker: string;
    ms: number;
    phase: "delay" | "handler" | "paint";
    island?: string;
    forced?: number;
  }[];

interface

One in-app navigation (the router's body swap), on the page's clock.

the new page's HTML arrived

its stylesheets were in

the swap committed: the new page shows

the fetch split, from the page request's timing: the wait for the first byte, the download

the request began before the click (a hover prefetch)

the page request on the server (the profiler's request log): its ms, the CPU it burned, its outbound calls — what the server did with the wait

PageReport

interface

interface PageReport {/*…*/}

PageReport.vitals

vitals: RatedVital[];

PageReport.rows

rows: IslandRow[];

PageReport.findings

findings: PageFinding[];

PageReport.blocking

blocking resources before first paint, slowest first

blocking: {
    url: string;
    ms: number;
    bytes: number;
  }[];

PageReport.bytes

bytes: {
    type: string;
    count: number;
    transfer: number;
    size: number;
  }[];

PageReport.longtask_ms

longtask_ms: number;

PageReport.third_party

other origins on this page (null: none, or no origin to tell them by)

third_party: ThirdParty | null;

RatedVital

interface

interface RatedVital {/*…*/}

RatedVital.key

key: "ttfb" | "fcp" | "lcp" | "cls" | "inp";

RatedVital.label

label: string;

RatedVital.value

value: number;

RatedVital.rating

rating: Rating;

RegionFact

interface

A region on the page, as the tab read it off the DOM. top is document-relative (px).

interface RegionFact {/*…*/}

RegionFact.fp

fp: string;

RegionFact.name

name: string;

RegionFact.kind

kind: "island" | "lake" | "hole";

RegionFact.wake

wake: string;

RegionFact.hydrated

hydrated: boolean;

RegionFact.top

top?: number;

RegionFact.height

height?: number;

RegionFact.own_bytes

the wire bytes only this island loads (a build's island graph × the browser's sizes)

own_bytes?: number;

RegionFact.hidden

it draws nothing on this screen: no box of its own nor a child's (hidden by its CSS, or empty)

hidden?: true;

RegionFact.shows_at

the media query under which the page's CSS shows it (read off the sheets), when one does

shows_at?: string;

RegionFact.headless

it never draws: no text, only hidden marker elements — an island there for its effects

headless?: true;

RestoreEvent

interface

A restored host that went wrong: upgraded by its component before the restore reached it (late), or restored to markup that does not match Svelte's (mismatch, the dev check).

interface RestoreEvent {/*…*/}

RestoreEvent.kind

kind: "late" | "mismatch";

RestoreEvent.host

the host's tag

host: string;

RestoreEvent.t

page time

t: number;

RestoreEvent.diff

the first difference (mismatch)

diff?: string;

RestoreEvent.island

the island holding it, when one does

island?: string;

RuntimeScripts

interface

interface RuntimeScripts {/*…*/}

RuntimeScripts.files

files: number;

RuntimeScripts.bytes

decoded

bytes: number;

RuntimeScripts.wire

on the wire (a third of decoded where the browser saw none: a cached file)

wire: number;

RuntimeScripts.by

by who serves them: this app, or a CDN package / host

by: {
    who: string;
    files: number;
    bytes: number;
  }[];

RuntimeScripts.island_entries

of this app's, the island entry files (og-region.<id>.…js): islands no HTML named — the ones a hole's answer brought

island_entries?: number;

ServerProfileBrief

interface

THE PROFILER'S LAST RUN OF A PAGE, in brief (devtools: the dock's profile store, shared with the Profiler tab) — what the server did when it rendered that page

interface ServerProfileBrief {/*…*/}

ServerProfileBrief.ago_min

minutes since the run

ago_min: number;

ServerProfileBrief.render_ms

render_ms: number;

ServerProfileBrief.top

its top cost, in its own words (the report's first warning, else its heaviest component)

top: string | null;

ServerProfileBrief.calls

calls: number;

ServerProfileBrief.calls_ms

calls_ms: number;

ServerProfileBrief.no_store

the Cache-Control it saw the page answer, when that keeps the page out of the back/forward cache (no-store): the server's own answer, which the browser cannot read

no_store?: string;

ServerProfileBrief.prerendered

the page is prerendered: 'file' (the build's file answered, no render), 'route' (its route prerenders)

prerendered?: "file" | "route";

SessionClick

interface

interface SessionClick {/*…*/}

SessionClick.t

t: number;

SessionClick.target

target: string;

SessionClick.fp

fp: string | null;

SessionClick.el

el: WeakRef<Element> | null;

SessionClick.clickable

it looked like it does something (a control, a link, a clickable role, a pointer cursor): a click on text or a blank stretch that changes nothing is no dead click

clickable?: true;

SessionData

interface

interface SessionData {/*…*/}

SessionData.from

from: number;

SessionData.to

to: number;

SessionData.interactions

interactions: SessionInteraction[];

SessionData.longtasks

longtasks: {
    t: number;
    ms: number;
  }[];

SessionData.frames

frames: SessionFrame[];

SessionData.shifts

shifts: SessionShift[];

SessionData.requests

requests: SessionRequest[];

SessionData.mutations

mutations: {
    fp: string | null;
    count: number;
    el: WeakRef<Element> | null;
  }[];

SessionData.mut_log

when the DOM changed, and where (one stamp per island per batch of changes)

mut_log: {
    t: number;
    fp: string | null;
  }[];

SessionData.clicks

every click, fast ones too

clicks: SessionClick[];

SessionData.errors

errors: {
    t: number;
    message: string;
  }[];

SessionData.navs

navigations as they started, with what started them (absent in a part saved before this field)

navs?: SessionNav[];

SessionData.heap

heap: {
    from: number;
    to: number;
  } | null;

SessionData.events

events: DevtoolsEvent[];

SessionData.cpu

cpu: unknown | null;

SessionData.cpu_off

cpu_off: string | null;

SessionData.page

page: string;

SessionData.pages

the documents the session crossed (a full page load mid-session), each with when it began

pages: {
    url: string;
    t: number;
  }[];

SessionData.names

island fingerprint → component name, taken on each page (its islands are gone after it)

names: Record<string, string>;

SessionFinding

interface

interface SessionFinding {/*…*/}

SessionFinding.code

code: string;

SessionFinding.severity

severity: "error" | "warn" | "info";

SessionFinding.message

message: string;

SessionFinding.fix

fix?: string;

SessionFinding.refs

refs: Ref[];

SessionFinding.url

a same-origin GET the tab can profile on the server in one click (server-time findings)

url?: string;

SessionFrame

interface

interface SessionFrame {/*…*/}

SessionFrame.t

t: number;

SessionFrame.ms

ms: number;

SessionFrame.blocking

blocking: number;

SessionFrame.scripts

scripts: {
    source: string;
    fn: string;
    invoker: string;
    ms: number;
  }[];

SessionInteraction

interface

interface SessionInteraction {/*…*/}

SessionInteraction.t

t: number;

SessionInteraction.name

name: string;

SessionInteraction.duration

duration: number;

SessionInteraction.input_delay

input_delay: number;

SessionInteraction.processing

processing: number;

SessionInteraction.presentation

presentation: number;

SessionInteraction.id

id: number;

SessionInteraction.target

target: string;

SessionInteraction.fp

fp: string | null;

SessionInteraction.el

el: WeakRef<Element> | null;

SessionNav

interface

A navigation as it started: where to, when, and the element that started it (the link or form, or the input being handled then) with its island.

interface SessionNav {/*…*/}

SessionNav.url

url: string;

SessionNav.t

t: number;

SessionNav.type

type: string;

SessionNav.source

source: string | null;

SessionNav.fp

fp: string | null;

SessionReport

interface

interface SessionReport {/*…*/}

SessionReport.duration_ms

duration_ms: number;

SessionReport.counts

shifts and cls: the layout moving with no input (the shifts the session's CLS counts)

counts: {
    interactions: number;
    slow: number;
    longtasks: number;
    longtask_ms: number;
    requests: number;
    request_bytes: number;
    errors: number;
    shifts: number;
    cls: number;
    mutations: number;
    pages: number;
  };

SessionReport.findings

findings: SessionFinding[];

SessionReport.interactions

the slowest interactions, for the timeline

interactions: {
    i: number;
    t: number;
    name: string;
    duration: number;
    target: string;
    fp: string | null;
  }[];

SessionReport.by_island

each island's part: clicks, slow interactions, the requests its clicks caused (server split)

by_island: Record<string, IslandSession>;

SessionReport.requests

the slowest requests, with the server's own split when it sent one

requests: {
    url: string;
    ms: number;
    status: number | null;
    server_ms: number | null;
    cpu_ms: number | null;
    net_ms: number | null;
    up: string | null;
  }[];

SessionReport.failed

every request that answered an error status (type: the browser's initiator type)

failed: {
    url: string;
    status: number;
    type: string;
    t: number;
  }[];

SessionReport.by_element

the slowest interaction on each element the session touched, however quick (0: a click the browser did not time, under its 16 ms floor)

by_element: {
    target: string;
    duration: number;
    processing: number;
    t: number;
    fp: string | null;
  }[];

SessionReport.cpu

cpu: CpuSummary | null;

SessionRequest

interface

interface SessionRequest {/*…*/}

SessionRequest.url

url: string;

SessionRequest.type

type: string;

SessionRequest.t

t: number;

SessionRequest.ms

ms: number;

SessionRequest.bytes

bytes: number;

SessionRequest.status

status: number | null;

SessionRequest.server

the server's own account (Server-Timing: ogygia's profiler sends ssr / cpu / net / up1-3)

server?: {
    name: string;
    ms: number;
    desc: string;
  }[];

SessionShift

interface

interface SessionShift {/*…*/}

SessionShift.t

t: number;

SessionShift.value

value: number;

SessionShift.input

input: boolean;

SessionShift.fp

fp: string | null;

SessionShift.el

el: WeakRef<Element> | null;

TimelineItem

interface

One item on the session's one-clock timeline (ms from the session start).

interface TimelineItem {/*…*/}

TimelineItem.t0

t0: number;

TimelineItem.t1

t1: number;

TimelineItem.label

label: string;

TimelineItem.tone

tone: "ok" | "warn" | "bad" | "info";

TimelineItem.server

a request: the server's part and, inside it, the upstream wait (ms)

server?: {
    ms: number;
    up: number;
  };

TimelineItem.ref

ref?: Ref;

TimelineLane

interface

interface TimelineLane {/*…*/}

TimelineLane.key

key: "input" | "main" | "net" | "islands" | "layout" | "errors";

TimelineLane.label

label: string;

TimelineLane.items

items: TimelineItem[];

VitalMove

interface

interface VitalMove {/*…*/}

VitalMove.key

key: "ttfb" | "fcp" | "lcp" | "cls" | "inp";

VitalMove.a

a: number;

VitalMove.b

b: number;

VitalMove.part

the part of a split vital that moved most (≥ 50 ms, else left out)

part?: {
    label: string;
    a: number;
    b: number;
  };

VitalPart

interface

interface VitalPart {/*…*/}

VitalPart.key

key: string;

VitalPart.label

label: string;

VitalPart.ms

ms: number;

Weight

interface

interface Weight {/*…*/}

Weight.bytes

bytes: number;

Weight.wire

wire: number;

Weight.imports

a module's static imports (resolved to absolute paths), for the walk

imports?: string[];

Weight.dynamic

its dynamic imports (resolved) — followed only where the page names them (Kit's route nodes)

dynamic?: string[];

Weight.unscoped

a stylesheet's unscoped fallbacks (see unscoped-css.ts)

unscoped?: UnscopedStyles[];

Weight.vary

a font's Vary header (Safari refetches a font preload that varies by Origin when its

vary?: string;

AssetKind

type

type AssetKind = "script" | "style" | "font" | "image" | "other";

CodeKind

type

type CodeKind = "app" | "ogygia" | "svelte" | "dependency" | "page script" | "browser" | "devtools";

HydrationStatus

type

An island's hydration status: worked out in the browser (devtools/hydration.ts), read by the testing API.

type HydrationStatus = "failed" | "recovered" | "healed" | "changed" | "asleep" | "clean";

IslandHazard

type

a hazard with the file it is in (the profiler's island chunks, the dev server's module graph)

type IslandHazard = Hazard & {
  file: string;
};

PartedVital

type

A VITAL'S PARTS, as numbers (the same splits the explanations word): TTFB by its steps, FCP by first byte / HTML / blocking / render, LCP by first byte / delay / download / render, INP by the slowest interaction's wait / handlers / paint. What "since your last profile" (the profiler) and "since your last load" (the Page tab) compare, to say which part of a moved vital moved. Null when the visit cannot tell.

type PartedVital = "ttfb" | "fcp" | "lcp" | "inp";

Ref

type

type Ref = {
  kind: "interaction";
  i: number;
} | {
  kind: "click";
  i: number;
} | {
  kind: "shift";
  i: number;
} | {
  kind: "island";
  fp: string;
};