/** * Browser-entry integration — the "plug the browser straight in" surface. Pure, tree-shakeable * normalizers from raw Web Performance API output onto the rumcap model, plus `entrySink()` (a * ready-made `PerformanceObserver` callback that feeds an `Encoder`) and `environmentSnapshot()`. * * WHY THIS LIVES IN THE LIBRARY: the model is spec-canonical and sentinel-free, but real browser * output is neither — it reports "didn't happen" as `0`/`''`/`-1` and still ships experimental field * spellings. Mapping one onto the other is knowledge about the FORMAT, not about any one capture * pipeline, and hand-rolled copies get it subtly wrong (a kept `-1` would even make `pack` throw on * its non-negative varuint). So the mapping is written once, here, grounded against the real Chrome * captures under `samples/` and covered by the corpus tests. * * Inputs are accepted STRUCTURALLY — a live `PerformanceEntry` or its `toJSON()` form (so stored raw * entries can be replayed in Node) — and every field read is runtime-guarded: garbage in a field * degrades to "absent", never to a crash inside a user's page. Live-only attribution (LCP `element`, * layout-shift `sources[].node`, interaction `target`) is read when present and skipped otherwise. * * On-page cost: this module is imported by nothing else in the library (the `Encoder` dependency is * type-only), so consumers who feed pre-normalized models tree-shake all of it away. * * Sentinel policy (the rules the model documents, applied in one place): * - PHASE timestamps where the platform uses 0 for "did not occur / withheld" (resource/navigation * phases, LCP + element-timing renderTime/loadTime, paint + LoAF milestones) -> absent. * - DURATIONS keep 0 — a measured zero is real (blockingDuration, server-timing duration, ...). * - STRINGS where the spec's default/'none' is `''` (sourceURL, invoker, container*, nextHopProtocol, * LCP id/url, ...) -> absent. The one deliberate exception: `deliveryType`, whose `''` is a * spec-defined vocabulary value ("no special delivery") the model keeps. * - COUNTS/SIZES keep 0 (transferSize 0 is a real cached/opaque answer; responseStatus 0 is the * spec's documented opaque-response value and the model keeps it). */ import { type RelMs } from './time.js'; import type { Encoder } from './encoder.js'; import type { ResourceTimingEntry, NavigationTimingEntry, PaintTime, LcpEntry, LayoutShiftEntry, ElementTimingEntry, InteractionEntry, LongTaskEntry, LoafEntry, MarkEntry, MeasureEntry, VisibilityStateEntry, ErrorEntry, EnvironmentStream } from './streams/index.js'; /** A raw entry: a live PerformanceEntry or its toJSON() form. Fields are read guarded, never trusted. */ export type RawEntry = object; /** * A short, structural CSS path for a live element — tag / #id / first classes / :nth-of-type, at most * five levels, stopping at the first #id. Deliberately NEVER element text content, so the default * attribution posture matches the `structural-only` redaction vocabulary in the capture config. */ export declare function structuralSelector(el: unknown): string | undefined; /** PerformanceResourceTiming → the model entry. Covers every field the model knows. */ export declare function normalizeResource(entry: RawEntry): ResourceTimingEntry; /** PerformanceNavigationTiming → the model entry (resource phases + document milestones). */ export declare function normalizeNavigation(entry: RawEntry): NavigationTimingEntry; /** A `paint` entry → the model milestone (which slot it fills comes from `entry.name`; see entrySink). */ export declare function normalizePaint(entry: RawEntry): PaintTime; /** A `largest-contentful-paint` entry → one LCP candidate. */ export declare function normalizeLcp(entry: RawEntry): LcpEntry; /** A `layout-shift` entry → the model shift (sources' nodes become structural selectors). */ export declare function normalizeLayoutShift(entry: RawEntry): LayoutShiftEntry; /** An `event` / `first-input` entry → the model interaction (`target` becomes a selector). */ export declare function normalizeInteraction(entry: RawEntry): InteractionEntry; /** A `longtask` entry → the model task (attribution '' defaults → absent). */ export declare function normalizeLongTask(entry: RawEntry): LongTaskEntry; /** A `long-animation-frame` entry → the model frame (scripts included). */ export declare function normalizeLoaf(entry: RawEntry): LoafEntry; /** An `element` (Element Timing) entry → the model entry. */ export declare function normalizeElementTiming(entry: RawEntry): ElementTimingEntry; /** A `mark` entry → the model mark (live `detail` kept; it does not survive toJSON()). */ export declare function normalizeMark(entry: RawEntry): MarkEntry; /** A `measure` entry → the model measure. */ export declare function normalizeMeasure(entry: RawEntry): MeasureEntry; /** A `visibility-state` entry → the model transition. */ export declare function normalizeVisibility(entry: RawEntry): VisibilityStateEntry; /** * A window `error` event → the model entry. `startTime` defaults to `performance.now()` at call time * (within the dispatch task of the throw) — deliberately NOT `event.timeStamp`, whose clock base has * varied across engines; a slightly-late unambiguous time beats a precisely-wrong one. * `message`/`stack` are PII-bearing — the redaction pass, not this mapper, is where policy applies. */ export declare function normalizeErrorEvent(event: object, startTime?: RelMs): ErrorEntry; /** A window `unhandledrejection` event → the model entry (reason coerced defensively). */ export declare function normalizeRejection(event: object, startTime?: RelMs): ErrorEntry; /** * Snapshot the environment/device context from the live globals (UA, UA-CH low-entropy, device memory, * connection, viewport/screen geometry, self-profiler availability). Reads only what exists — under * Node or a worker the missing fields are simply absent. High-entropy UA-CH needs an async permission * prompt-able call, so it is deliberately left to the caller. */ export declare function environmentSnapshot(): EnvironmentStream; /** What the sink accepts: an observer's entry list, an array/iterable of entries, or one entry. */ export type EntrySinkInput = { getEntries(): unknown; } | Iterable | RawEntry; /** * Build the one feed function that connects `PerformanceObserver` output to an `Encoder` — usable * directly as the observer callback: * * ```ts * const sink = entrySink(enc); * for (const type of ['navigation', 'resource', 'paint', 'largest-contentful-paint', 'layout-shift', * 'event', 'first-input', 'longtask', 'long-animation-frame', 'element', * 'mark', 'measure', 'visibility-state']) { * try { new PerformanceObserver(sink).observe({ type, buffered: true, * ...(type === 'event' ? { durationThreshold: 16 } : {}) }); } catch { /* unsupported *\/ } * } * ``` * * It routes by `entryType`, normalizes (this module's mappers), and handles the stateful quirks a * per-entry mapping can't: paint milestones and LCP candidates accumulate into their singleton streams; * the double-delivered first interaction (`event` + its threshold-exempt `first-input` copy) is merged * onto one recorded entry; a re-delivered `navigation` singleton replaces the provisional one (a * buffered document-start observer sees it twice — the last, complete entry wins); an observer's * `droppedEntriesCount` (buffer overflow before `observe()`) is recorded as a manifest loss note. * Unknown entry types are ignored. After the encoder is finished, entries are dropped silently — a * late observer delivery must not throw inside the page. */ export declare function entrySink(enc: Encoder): (entries: EntrySinkInput, observer?: unknown, options?: unknown) => void; //# sourceMappingURL=browser.d.ts.map