@fixback/sdk 0.4.0 → 0.6.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.
Files changed (45) hide show
  1. package/README.md +12 -2
  2. package/dist/annotation.d.ts +10 -73
  3. package/dist/capture/extract-dom.d.ts +33 -0
  4. package/dist/connect.d.ts +47 -0
  5. package/dist/dom.d.ts +7 -19
  6. package/dist/element-picker.d.ts +1 -1
  7. package/dist/error-capture.d.ts +21 -93
  8. package/dist/fixback.umd.js +36 -215
  9. package/dist/fixback.umd.js.map +1 -1
  10. package/dist/identity.d.ts +6 -1
  11. package/dist/index.d.ts +17 -15
  12. package/dist/index.mjs +3598 -4681
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/init.d.ts +63 -97
  15. package/dist/mount.d.ts +46 -0
  16. package/dist/overlay/dom.d.ts +16 -0
  17. package/dist/overlay/icons.d.ts +23 -0
  18. package/dist/overlay/marking.d.ts +64 -0
  19. package/dist/overlay/send.d.ts +65 -0
  20. package/dist/overlay/view.d.ts +78 -0
  21. package/dist/overlay-styles.d.ts +1 -1
  22. package/dist/overlay.d.ts +40 -43
  23. package/dist/package.json +3 -0
  24. package/dist/report.d.ts +22 -111
  25. package/dist/reporter-session-store.d.ts +17 -0
  26. package/dist/session.d.ts +27 -0
  27. package/dist/submit.d.ts +56 -19
  28. package/dist/trace/instrument/beacon.d.ts +16 -0
  29. package/dist/trace/instrument/console.d.ts +11 -0
  30. package/dist/trace/instrument/fetch.d.ts +16 -0
  31. package/dist/trace/instrument/index.d.ts +64 -0
  32. package/dist/trace/instrument/navigation.d.ts +12 -0
  33. package/dist/trace/instrument/ui.d.ts +23 -0
  34. package/dist/trace/instrument/window.d.ts +66 -0
  35. package/dist/trace/instrument/xhr.d.ts +15 -0
  36. package/dist/version.d.ts +1 -1
  37. package/package.json +8 -5
  38. package/dist/auto-report-backoff.d.ts +0 -47
  39. package/dist/boot.d.ts +0 -74
  40. package/dist/breadcrumbs.d.ts +0 -448
  41. package/dist/invite.d.ts +0 -104
  42. package/dist/onboarding-styles.d.ts +0 -8
  43. package/dist/onboarding.d.ts +0 -44
  44. package/dist/screenshot.d.ts +0 -73
  45. package/dist/scrub.d.ts +0 -61
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The `XMLHttpRequest` network hook (spec #122 §D, ticket #139).
3
+ *
4
+ * Patches the prototype's `open`/`send` to record a rich network crumb when a
5
+ * request settles: method, scrubbed URL, status + statusText, a
6
+ * `performance.now()` duration, request size (only when the body is trivially
7
+ * sized), response size (from the `content-length` header only), content type, and
8
+ * a failure outcome distinguished by which terminal event fired (`error` /
9
+ * `timeout` / `abort` / `load`). The `send` body argument is **never read for
10
+ * content** — only its trivially-known size — so a body can never reach the buffer.
11
+ */
12
+ import { type BreadcrumbBuffer, type Teardown } from "@fixback/sdk-core";
13
+ import { type InstrumentWindow } from "./window";
14
+ /** Patch `XMLHttpRequest` to record a rich network crumb on settlement. See the module doc. */
15
+ export declare function instrumentXhr(buffer: BreadcrumbBuffer, win: InstrumentWindow, ignoreUrl: (url: string) => boolean, now: () => number, mono?: () => number): Teardown;
package/dist/version.d.ts CHANGED
@@ -10,4 +10,4 @@
10
10
  * can't slip through. Do not hand-edit this line to a value other than
11
11
  * `package.json`'s version.
12
12
  */
13
- export declare const SDK_VERSION = "0.4.0";
13
+ export declare const SDK_VERSION = "0.6.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fixback/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "The Fixback capture SDK — a boot-gated, self-isolating on-page feedback launcher.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,15 +35,15 @@
35
35
  "url": "git+https://github.com/wemuda/fixback.git",
36
36
  "directory": "packages/sdk"
37
37
  },
38
- "homepage": "https://github.com/wemuda/fixback/tree/master/packages/sdk#readme",
39
- "bugs": "https://github.com/wemuda/fixback/issues",
38
+ "homepage": "https://docs.fixback.dev/sdk/web",
39
+ "bugs": "https://docs.fixback.dev",
40
40
  "publishConfig": {
41
41
  "access": "public"
42
42
  },
43
43
  "dependencies": {
44
44
  "@rrweb/record": "^2.1.1",
45
45
  "@rrweb/types": "^2.1.1",
46
- "modern-screenshot": "^4.7.0"
46
+ "@fixback/sdk-core": "0.3.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "jsdom": "^30.0.1",
@@ -51,8 +51,11 @@
51
51
  "vite": "^8.2.1",
52
52
  "vitest": "^4.1.10"
53
53
  },
54
+ "engines": {
55
+ "node": ">=20.12"
56
+ },
54
57
  "scripts": {
55
- "build": "vite build && tsc -p tsconfig.build.json",
58
+ "build": "vite build && tsc -p tsconfig.build.json && node ../../scripts/finalize-cjs.mjs dist",
56
59
  "lint": "eslint .",
57
60
  "typecheck": "tsc --noEmit",
58
61
  "test": "vitest run"
@@ -1,47 +0,0 @@
1
- /**
2
- * Client-side backpressure for **automatic** error reports (spec 0003 §E/§H,
3
- * ticket #89). When ingest sheds `source: auto` load it answers `429` with a
4
- * `Retry-After`; the SDK honours it by holding a pause window during which further
5
- * `source: auto` reports are dropped without touching the network. **Manual**
6
- * reports — a human clicking Send — never consult this gate.
7
- *
8
- * This is the transport's counterpart to the server's per-Project token bucket
9
- * (`apps/api/src/ingest/auto-report-rate-limiter.ts`): one shared window per page,
10
- * so a 429 from one auto-report shed applies to the auto-reports that follow it.
11
- * The clock is injectable so the window is unit-tested deterministically, never on
12
- * wall time — mirroring the server limiter's `Clock`.
13
- */
14
- /** A source of the current time in epoch milliseconds — injectable for tests. */
15
- export type Clock = () => number;
16
- /** The hold window applied when a `429` carries no usable `Retry-After` (spec §E). */
17
- export declare const DEFAULT_RETRY_AFTER_SECONDS = 60;
18
- /**
19
- * Parse a `Retry-After` header into whole seconds to hold for. Handles both HTTP
20
- * forms — a delta-seconds integer and an HTTP-date (measured from `now`, rounded up
21
- * and clamped at zero) — and falls back to {@link DEFAULT_RETRY_AFTER_SECONDS} when
22
- * the header is absent, blank, or unparseable. Ingest sends the delta-seconds form;
23
- * the date form is handled for spec-completeness.
24
- */
25
- export declare function parseRetryAfter(header: string | null | undefined, now: number): number;
26
- /**
27
- * A single pause window for `source: auto` reports. `hold` opens (or extends) it
28
- * from a `429`'s `Retry-After`; `isPaused` reports whether it is still open. The
29
- * default instance in `submit.ts` is shared across a page's reports so the hold
30
- * persists across successive auto submissions.
31
- */
32
- export declare class AutoReportBackoff {
33
- private readonly now;
34
- /** Epoch ms until which `source: auto` reports are held; `0` when clear. */
35
- private pausedUntil;
36
- constructor(now?: Clock);
37
- /** Is the `source: auto` pause window currently open? */
38
- isPaused(): boolean;
39
- /** Whole seconds remaining in the pause window (`0` when clear). */
40
- retryAfterSeconds(): number;
41
- /**
42
- * Open (or extend) the window from a `429`'s `Retry-After` value, returning the
43
- * seconds it will hold for. The window only ever grows — a shorter later hold
44
- * never clips a longer one already in effect.
45
- */
46
- hold(retryAfterHeader: string | null | undefined): number;
47
- }
package/dist/boot.d.ts DELETED
@@ -1,74 +0,0 @@
1
- /**
2
- * The ingest **boot** wire-contract, vendored.
3
- *
4
- * The SDK deliberately does not import `@fixback/shared` — that package is
5
- * private and server-shaped (ticket #47). The boot request/response is small and
6
- * stable, so the exact slice the SDK needs is copied here. Keep it in lock-step
7
- * with the server: the request body accepted by `POST /api/ingest/boot`
8
- * (`apps/api/src/ingest/ingest.controller.ts`) and the `BootAnswer` returned by
9
- * `evaluateBoot` (`apps/api/src/ingest/reporter-identity.ts`).
10
- */
11
- /** A Project's Gate — who may submit. Mirrors the server's `ProjectGate`. */
12
- export type ProjectGate = "open" | "invited" | "internal";
13
- /** The trust tier a Reporter holds. Mirrors the server's `ReporterTier`. */
14
- export type ReporterTier = "public" | "invited" | "internal";
15
- /**
16
- * Optional identity evidence the SDK forwards to boot. None of it is a tier: the
17
- * server re-derives trust from this evidence and never honours a self-declared
18
- * tier, so the SDK does not send one.
19
- */
20
- export interface IdentityInputs {
21
- readonly signedIdentity?: string;
22
- readonly reporterId?: string;
23
- readonly anonymousId?: string;
24
- }
25
- /** The JSON body `POST /api/ingest/boot` accepts. */
26
- export interface BootRequest extends IdentityInputs {
27
- readonly key: string;
28
- }
29
- /**
30
- * The Project's effective console/network capture config, served on the boot answer
31
- * (spec #122 §L; ticket #138). Each flag is the per-project master toggle ANDed with
32
- * that stream's own toggle, so the SDK gates instrumentation on one boolean per
33
- * stream. Optional on the wire: an older server that does not send it (or a
34
- * malformed value) is treated as **capture on** — default-on, matching the server
35
- * default — and `init` options override whatever is served.
36
- */
37
- export interface CaptureConfig {
38
- readonly console: boolean;
39
- readonly network: boolean;
40
- /**
41
- * The Project's session-replay toggle (issue #189, ADR-0024). Optional on the
42
- * wire: a server predating replay omits it, which — like the other streams —
43
- * means **capture on**.
44
- */
45
- readonly replay?: boolean;
46
- }
47
- /**
48
- * The boot answer: whether this origin is allowlisted, the Project's Gate, the
49
- * caller's derived tier (`null` when a presented identity was refused), whether a
50
- * submission would be accepted right now, and the Project's capture config. The
51
- * launcher shows only when `canSubmit` is true.
52
- */
53
- export interface BootAnswer {
54
- readonly originAllowed: boolean;
55
- readonly gate: ProjectGate;
56
- readonly tier: ReporterTier | null;
57
- readonly canSubmit: boolean;
58
- /** The Project's console/network capture config; absent ⇒ default-on (#138). */
59
- readonly capture?: CaptureConfig;
60
- }
61
- /** Join an API base URL with the boot path, tolerating a trailing slash. */
62
- export declare function bootEndpoint(apiUrl: string): string;
63
- /**
64
- * Ask ingest whether a submission would be accepted for this key / origin / Gate.
65
- * Resolves to the boot answer, or `null` when Fixback could not be reached, the
66
- * key was refused, or the response was not a boot answer. It never throws: any
67
- * non-answer is treated by the caller as "do not show the launcher", so a Fixback
68
- * outage stays invisible to the host page (ticket #47: "fails quietly").
69
- *
70
- * The browser attaches the `Origin` header itself on this cross-origin request —
71
- * the server reads it to decide `originAllowed` — so the SDK neither sets nor
72
- * needs to set it. `fetchImpl` is injectable purely so the boot call is testable.
73
- */
74
- export declare function requestBoot(apiUrl: string, request: BootRequest, fetchImpl?: typeof fetch): Promise<BootAnswer | null>;
@@ -1,448 +0,0 @@
1
- /**
2
- * The thin trace **breadcrumb ring buffer** and its capture instrumentation
3
- * (spec 0003 §C; research `sentry-error-capture-findings.md` §7.5).
4
- *
5
- * A fixed-size FIFO buffer of the most recent activity — console, navigation,
6
- * network metadata, masked user actions, and the failing error — that rides on a
7
- * report as Evidence. It is trimmed exactly like Sentry (`crumbs.slice(-N)`),
8
- * defaults to a deliberately thin `N ≈ 30` because it ships on **every** payload,
9
- * and takes an optional age cap. A `beforeBreadcrumb(crumb) => crumb | null`
10
- * filter lets a Project mute or edit crumbs before they enter the buffer.
11
- *
12
- * Everything private is kept out **at the source**: `ui.input` records that an
13
- * input changed, never its value; `fetch`/`xhr` crumbs carry method + URL +
14
- * status only, **never** bodies; URLs are scrubbed as the crumb is built. The
15
- * `beforeSend` choke point (`scrub.ts`) is the final gate over the whole report.
16
- *
17
- * The SDK stays dependency-free and must never throw into the host page, so every
18
- * instrumentation hook is wrapped: a capture failure is swallowed and the original
19
- * behaviour (the real `console`, `fetch`, navigation) always runs.
20
- */
21
- /** Console-style severity a `console` crumb records. */
22
- export type BreadcrumbLevel = "log" | "info" | "warn" | "error" | "assert" | "debug";
23
- /** The kind of activity a crumb records. */
24
- export type BreadcrumbCategory = "console" | "navigation" | "fetch" | "xhr" | "beacon" | "ui.click" | "ui.input" | "error";
25
- /**
26
- * Which browser API issued a captured network request (spec #122 §D, ticket #139).
27
- * Every network crumb's `category` is one of these too, so a stream router and the
28
- * read model agree on what is a network entry.
29
- */
30
- export type NetworkApi = "fetch" | "xhr" | "beacon";
31
- /**
32
- * A network request's failure classification (spec #122 §D, ticket #139). `ok` and
33
- * the HTTP status classes come from a settled response; `network-error` / `timeout`
34
- * / `aborted` from how a request failed; `opaque-cors` from a cross-origin response
35
- * whose status is unreadable. The Network tab flags every non-`ok` outcome.
36
- */
37
- export type NetworkOutcome = "ok" | "http-4xx" | "http-5xx" | "network-error" | "timeout" | "aborted" | "opaque-cors";
38
- /**
39
- * A crumb's structured detail. Deliberately narrow: there is **no** field for a
40
- * request/response body or an input value, so those can never be recorded.
41
- */
42
- export interface BreadcrumbData {
43
- readonly url?: string;
44
- readonly method?: string;
45
- readonly status?: number;
46
- /** A masked CSS selector for a `ui.*` target — never its text or value. */
47
- readonly target?: string;
48
- readonly from?: string;
49
- readonly to?: string;
50
- readonly errorType?: string;
51
- }
52
- /**
53
- * The type tag on a structured console argument (spec #122 §C, decision D7). A
54
- * console call's arguments are preserved **type-tagged** rather than flattened to a
55
- * string, so an object/array argument can be inspected in the Console tab rather
56
- * than read as `[object Object]`: `string`/`number`/`bool`/`null` carry the value
57
- * directly, `json` a depth-/byte-capped JSON-safe clone, and `error` an Error's
58
- * `{ name, message, stack }`.
59
- */
60
- export type ConsoleArgType = "string" | "number" | "bool" | "null" | "json" | "error";
61
- /**
62
- * One structured console argument (spec #122 §C): a type tag plus a JSON-safe value.
63
- * `v` is always serializable — a `json` arg is depth-, breadth-, and string-capped at
64
- * assembly, and exotic values (bigint, symbol, function, circular refs) are rendered
65
- * to safe text — so a console crumb can never carry an unserializable or unbounded
66
- * value onto the wire.
67
- */
68
- export interface ConsoleArg {
69
- readonly t: ConsoleArgType;
70
- readonly v: unknown;
71
- }
72
- /** A `file:line` source location (spec #122 §C) — where a console call was made. */
73
- export interface SourceLocation {
74
- readonly file: string;
75
- readonly line: number;
76
- }
77
- /**
78
- * One entry in a trace stream. Alongside its semantic fields every entry carries a
79
- * stable {@link id} and a high-res monotonic {@link mono} timestamp — both stamped
80
- * by the buffer on `add` — so entries from the three independent streams order and
81
- * cross-link exactly (spec #122 §B, decision D9). `timestamp` stays epoch ms.
82
- */
83
- export interface Breadcrumb {
84
- readonly category: BreadcrumbCategory;
85
- readonly message?: string;
86
- readonly level?: BreadcrumbLevel;
87
- /** Epoch milliseconds when the crumb was recorded. */
88
- readonly timestamp: number;
89
- /** A stable id, unique within the buffer — assigned on `add` when not already set. */
90
- readonly id?: string;
91
- /** A high-res monotonic timestamp (`performance.now()`) — assigned on `add`. */
92
- readonly mono?: number;
93
- readonly data?: BreadcrumbData;
94
- /**
95
- * For a `console` crumb (spec #122 §C): the call's arguments preserved as
96
- * structured, type-tagged values (see {@link ConsoleArg}), so the Console tab can
97
- * render each argument expandably instead of a flattened string. `message` stays
98
- * the one-line preview. Absent on non-console crumbs.
99
- */
100
- readonly args?: readonly ConsoleArg[];
101
- /**
102
- * For a `console` crumb (spec #122 §C): the `file:line` the call was made from,
103
- * parsed best-effort from the call stack. Captured only for the levels that keep a
104
- * source — `warn`/`error`/`assert` (ticket #159; see {@link SOURCE_CAPTURE_LEVELS}) —
105
- * and only when the stack yields a usable location; a chatty app's `log`/`info`/`debug`
106
- * omit it so the hot-page capture cost stays low. Absent on non-console crumbs.
107
- */
108
- readonly source?: SourceLocation;
109
- /**
110
- * For an auto-captured `error` crumb only (spec #122 §F): the ids of the entries
111
- * immediately preceding the throw — a causal pointer into the same trace, so a
112
- * machine-filed crash names its lead-up. Absent on every other crumb.
113
- */
114
- readonly causedBy?: readonly string[];
115
- /**
116
- * For a network crumb (`fetch` / `xhr` / `beacon`) — spec #122 §D, ticket #139.
117
- * The rich request metadata the Network tab renders, lifted to the top level (the
118
- * console-enrichment precedent) so the read model shapes each into a
119
- * `NetworkTraceEntry`. **No field carries a request/response body or an arbitrary
120
- * header** — `respSize` derives from the `content-length` response header only and
121
- * `contentType` from `content-type`; nothing else is read. Absent on every other crumb.
122
- */
123
- readonly api?: NetworkApi;
124
- /** The request method (network crumb) — e.g. `GET`, `POST`. */
125
- readonly method?: string;
126
- /** The scrubbed request URL (network crumb) — query dropped, path PII redacted. */
127
- readonly url?: string;
128
- /** The final HTTP status (network crumb), when one was known; absent on a network error/beacon. */
129
- readonly status?: number;
130
- /** The HTTP status text (network crumb), when the response carried one. */
131
- readonly statusText?: string;
132
- /** Wall-clock duration of the request in ms (network crumb) — a `performance.now()` delta. */
133
- readonly durationMs?: number;
134
- /** Request body size in bytes (network crumb) — only when trivially known (string/Blob/ArrayBuffer), never by reading a stream. */
135
- readonly reqSize?: number;
136
- /** Response body size in bytes (network crumb) — from the `content-length` response header only. */
137
- readonly respSize?: number;
138
- /** Response content type (network crumb) — the `content-type` header's media type. */
139
- readonly contentType?: string;
140
- /** The request's failure classification (network crumb) — see {@link NetworkOutcome}. */
141
- readonly outcome?: NetworkOutcome;
142
- }
143
- /**
144
- * The three independent trace streams (spec #122 §A, decision D6). Each is its own
145
- * FIFO ring with its own size budget, so a chatty stream can't evict another's
146
- * lead-up. Every {@link BreadcrumbCategory} maps to exactly one stream via
147
- * {@link streamOf}.
148
- */
149
- export type TraceStream = "network" | "console" | "breadcrumbs";
150
- /**
151
- * Per-stream ring budgets — starting points from the grill (D6): network is the
152
- * chattiest, breadcrumbs the sparsest. Per-project tunable, never frozen.
153
- */
154
- export declare const DEFAULT_STREAM_BUDGETS: Readonly<Record<TraceStream, number>>;
155
- /**
156
- * The shared age cap (spec #122 §A): entries older than this are pruned from every
157
- * stream, whichever trims first. ~3 minutes and — unlike the thin buffer's
158
- * off-by-default cap — **on** by default, so an idle tab never ships stale context.
159
- */
160
- export declare const DEFAULT_MAX_AGE_MS: number;
161
- /**
162
- * Console levels captured by default (spec #122 §C, decision D7): **all** of them, so
163
- * the Console tab is a real console and not just an error log. Eviction priority (not
164
- * capture) is what keeps a chatty app's `log`/`info`/`debug` from burying the signal —
165
- * see {@link PINNED_CONSOLE_LEVELS}.
166
- */
167
- export declare const DEFAULT_CONSOLE_LEVELS: readonly BreadcrumbLevel[];
168
- /**
169
- * The console levels **pinned** against eviction (spec #122 §C, decision D7). When the
170
- * console stream overflows its budget, `warn`/`error`/`assert` are retained ahead of
171
- * `log`/`info`/`debug`, so an error is never evicted by a burst of chatter — the
172
- * low-priority levels fill the remainder and are dropped first.
173
- */
174
- export declare const PINNED_CONSOLE_LEVELS: readonly BreadcrumbLevel[];
175
- /**
176
- * The console levels for which `source` (`file:line`) is captured (ticket #159).
177
- *
178
- * Capturing a call site constructs a `new Error()` to read its stack on **every** console
179
- * call — a measurable hot-page cost when a chatty app logs in a tight loop (spec #122's
180
- * "hot-page performance budget" open question, handed onward from the grill). The
181
- * spec-sanctioned mitigation (decision D7) narrows source capture to the **levels that
182
- * matter for debugging** — the same `warn`/`error`/`assert` that are pinned against
183
- * eviction — so `log`/`info`/`debug` pay no per-call stack cost and simply omit `source`.
184
- * Full-fidelity `source` is preserved exactly where it earns its keep: on warnings,
185
- * errors, and failed assertions. Per-project tunability of this set is future work; today
186
- * it deliberately mirrors {@link PINNED_CONSOLE_LEVELS} so "what survives eviction" and
187
- * "what carries a call site" stay one idea.
188
- */
189
- export declare const SOURCE_CAPTURE_LEVELS: readonly BreadcrumbLevel[];
190
- /**
191
- * Which stream a crumb's category belongs to (spec #122 §A). Network APIs
192
- * (`fetch` / `xhr` / `beacon`) form the network stream, `console` its own, and
193
- * everything else (navigation, masked UI events, the failing error) breadcrumbs.
194
- */
195
- export declare function streamOf(category: string): TraceStream;
196
- /** Filters or edits each crumb before it enters the buffer; `null` drops it. */
197
- export type BeforeBreadcrumb = (crumb: Breadcrumb) => Breadcrumb | null;
198
- /** Configuration for {@link createBreadcrumbBuffer}. All values are optional. */
199
- export interface BreadcrumbBufferConfig {
200
- /**
201
- * Per-stream size budgets (oldest drop, independently per stream). Any stream
202
- * omitted falls back to {@link DEFAULT_STREAM_BUDGETS}.
203
- */
204
- readonly budgets?: Partial<Record<TraceStream, number>>;
205
- /**
206
- * Shared age cap in ms: entries older than this are dropped from every stream.
207
- * Defaults to {@link DEFAULT_MAX_AGE_MS} (on); pass `0` (or a non-positive value)
208
- * to disable age pruning.
209
- */
210
- readonly maxAgeMs?: number;
211
- /** A per-crumb filter (mute a category, edit, or drop by returning `null`). */
212
- readonly beforeBreadcrumb?: BeforeBreadcrumb | null;
213
- /** Epoch clock source, injectable for tests. Defaults to `Date.now`. */
214
- readonly now?: () => number;
215
- /** High-res monotonic clock, injectable for tests. Defaults to `performance.now`. */
216
- readonly mono?: () => number;
217
- /** Stable id generator, injectable for tests. Defaults to a per-buffer sequence. */
218
- readonly nextId?: () => string;
219
- }
220
- /** A live trace buffer. */
221
- export interface BreadcrumbBuffer {
222
- /** Record a crumb (subject to `beforeBreadcrumb`, id/mono stamping, size, and age trimming). */
223
- add(crumb: Breadcrumb): void;
224
- /** The current crumbs, merged across streams and ordered in time — a fresh array. */
225
- snapshot(): Breadcrumb[];
226
- /** Drop every crumb. */
227
- clear(): void;
228
- }
229
- /**
230
- * Create the per-stream trace buffer (spec #122 §A/§B). It holds **three
231
- * independent FIFO rings** — `network`, `console`, `breadcrumbs` — each trimmed to
232
- * its own budget so a chatty stream never evicts another's lead-up, plus a shared
233
- * age cap. On `add`, a crumb passes through `beforeBreadcrumb`, is stamped with a
234
- * stable `id` and a high-res `mono` timestamp (and an epoch `timestamp` if it has
235
- * none), routed to its stream, then that stream is pruned by age and trimmed to its
236
- * budget. `snapshot` prunes by age again, then merges the three streams into one
237
- * `mono`-ordered array for transport — so the wire keeps its single `trace` field.
238
- */
239
- export declare function createBreadcrumbBuffer(config?: BreadcrumbBufferConfig): BreadcrumbBuffer;
240
- /**
241
- * Longest scrubbed URL kept on a crumb (spec #122 §F): capped at assembly so an
242
- * over-long URL is truncated, never dropped and never allowed to bloat a report.
243
- */
244
- export declare const MAX_URL_LENGTH = 2048;
245
- /**
246
- * Structured console argument caps (spec #122 §C/§F), all applied **at assembly** so a
247
- * single console crumb can never bloat a report:
248
- * - {@link MAX_ARG_DEPTH} bounds how deep a `json` argument is cloned (deeper nodes
249
- * collapse to an `[Object]`/`[Array]` marker);
250
- * - {@link MAX_ARG_ITEMS} bounds how many keys/elements are kept at each level;
251
- * - {@link MAX_ARG_STRING_LENGTH} truncates a single over-long string value;
252
- * - {@link MAX_CONSOLE_ARGS_BYTES} bounds the serialized size of the whole args array
253
- * (trailing args are dropped to fit), kept well under ingest's per-entry byte cap.
254
- */
255
- export declare const MAX_ARG_DEPTH = 4;
256
- export declare const MAX_ARG_ITEMS = 100;
257
- export declare const MAX_ARG_STRING_LENGTH = 1024;
258
- export declare const MAX_CONSOLE_ARGS_BYTES = 4096;
259
- /** Classify one console argument into a type-tagged {@link ConsoleArg} (spec #122 §C). */
260
- export declare function toConsoleArg(value: unknown): ConsoleArg;
261
- /**
262
- * A `console` crumb from a captured call's level and arguments (spec #122 §C). The
263
- * `message` is the one-line preview (flattened, truncated); `args` preserves each
264
- * argument as a structured, type-tagged, size-capped value so the Console tab can
265
- * render objects/errors expandably; `source` is the best-effort `file:line`, attached
266
- * only when the stack yielded one. Args are omitted entirely for a no-argument call.
267
- */
268
- export declare function consoleCrumb(level: BreadcrumbLevel, args: readonly unknown[], timestamp: number, source?: SourceLocation): Breadcrumb;
269
- /** A `navigation` crumb; both URLs are scrubbed and length-capped as the crumb is built. */
270
- export declare function navigationCrumb(from: string, to: string, timestamp: number): Breadcrumb;
271
- /**
272
- * Classify an HTTP status into a {@link NetworkOutcome} (spec #122 §D). A missing or
273
- * `0` status is a `network-error` (a request that never got a response); otherwise
274
- * the 4xx/5xx classes flag failures and everything else is `ok`. Used both as the
275
- * fallback outcome and by the thin {@link fetchCrumb} / {@link xhrCrumb} builders.
276
- */
277
- export declare function outcomeFromStatus(status: number | undefined): NetworkOutcome;
278
- /**
279
- * The trivially-known byte size of a request body (spec #122 §D) — a string's UTF-8
280
- * length, a `Blob`'s `.size`, or an `ArrayBuffer`/typed-array `.byteLength`. Anything
281
- * that would require **reading** the body (a `ReadableStream`, `FormData`, a `Request`
282
- * with a stream body) returns `undefined` — the "never read the body" line holds.
283
- */
284
- export declare function trivialBodySize(body: unknown): number | undefined;
285
- /** Parse a `content-length` header into a non-negative byte count, or `undefined`. */
286
- export declare function parseContentLength(value: string | null | undefined): number | undefined;
287
- /** The media type from a `content-type` header (the part before any `;` parameters). */
288
- export declare function contentTypeOf(value: string | null | undefined): string | undefined;
289
- /** The rich metadata a network crumb records (spec #122 §D) — never a body or header. */
290
- export interface NetworkCrumbInput {
291
- readonly api: NetworkApi;
292
- readonly method: string;
293
- /** The raw request URL — scrubbed (query dropped, path PII redacted) as the crumb is built. */
294
- readonly url: string;
295
- readonly status?: number;
296
- readonly statusText?: string;
297
- readonly durationMs?: number;
298
- readonly reqSize?: number;
299
- readonly respSize?: number;
300
- readonly contentType?: string;
301
- readonly outcome: NetworkOutcome;
302
- }
303
- /**
304
- * A network crumb (`fetch` / `xhr` / `beacon`) with the rich, metadata-only fields
305
- * the Network tab renders (spec #122 §D, ticket #139): api, method, scrubbed URL,
306
- * status + statusText, duration, request/response sizes, content type, and a failure
307
- * outcome. The URL is scrubbed and length-capped at assembly. The shape has **no field
308
- * for a request/response body or an arbitrary header**, so neither can ever be
309
- * recorded; only a positive status, and fields that were actually supplied, ride along.
310
- */
311
- export declare function networkCrumb(input: NetworkCrumbInput, timestamp: number): Breadcrumb;
312
- /**
313
- * A thin `fetch` crumb from method + URL + status (outcome derived from the status).
314
- * The instrumentation uses {@link networkCrumb} directly to carry the full metadata;
315
- * this convenience builder seeds a network crumb from a status alone.
316
- */
317
- export declare function fetchCrumb(method: string, url: string, status: number | undefined, timestamp: number): Breadcrumb;
318
- /** A thin `xhr` crumb from method + URL + status (outcome derived from the status). */
319
- export declare function xhrCrumb(method: string, url: string, status: number | undefined, timestamp: number): Breadcrumb;
320
- /** A `ui.click` crumb: a masked target selector only — no text or value. */
321
- export declare function clickCrumb(target: Element, timestamp: number): Breadcrumb;
322
- /**
323
- * A `ui.input` crumb: records **that** an input changed and which field, never
324
- * the value typed into it. The `target` element's `.value` is never read.
325
- */
326
- export declare function inputCrumb(target: Element, timestamp: number): Breadcrumb;
327
- /**
328
- * An `error` crumb for the failing exception or rejection that ends the trace. When
329
- * `causedBy` is given (an auto-captured error), it rides on the crumb as the causal
330
- * pointer to the ids of the entries immediately preceding the throw (spec #122 §F).
331
- */
332
- export declare function errorCrumb(error: unknown, timestamp: number, causedBy?: readonly string[]): Breadcrumb;
333
- /** Detaches an installed instrumentation, restoring the original behaviour. */
334
- export type Teardown = () => void;
335
- type AnyFn = (...args: unknown[]) => unknown;
336
- type ConsoleLike = Partial<Record<BreadcrumbLevel, AnyFn>>;
337
- type FetchFn = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
338
- interface HistoryLike {
339
- pushState(data: unknown, unused: string, url?: string | URL | null): void;
340
- replaceState(data: unknown, unused: string, url?: string | URL | null): void;
341
- }
342
- interface XhrInstance {
343
- status: number;
344
- statusText?: string;
345
- open(method: string, url: string | URL, ...rest: unknown[]): void;
346
- send(body?: unknown): void;
347
- getResponseHeader?(name: string): string | null;
348
- addEventListener(type: string, listener: () => void): void;
349
- removeEventListener(type: string, listener: () => void): void;
350
- }
351
- interface XhrConstructor {
352
- new (): XhrInstance;
353
- prototype: XhrInstance;
354
- }
355
- /** The `navigator.sendBeacon` surface the beacon instrumentation wraps. */
356
- export interface BeaconNavigator {
357
- sendBeacon?: (url: string | URL, data?: BodyInit | null) => boolean;
358
- }
359
- /** The structural window surface the instrumentation reaches into. */
360
- export interface InstrumentWindow {
361
- fetch?: FetchFn;
362
- history?: HistoryLike;
363
- location?: {
364
- href: string;
365
- };
366
- XMLHttpRequest?: XhrConstructor;
367
- navigator?: BeaconNavigator;
368
- addEventListener(type: string, listener: (event: Event) => void, options?: boolean | AddEventListenerOptions): void;
369
- removeEventListener(type: string, listener: (event: Event) => void, options?: boolean | EventListenerOptions): void;
370
- }
371
- /** Options for {@link instrumentBreadcrumbs} and the individual installers. */
372
- export interface InstrumentOptions {
373
- readonly win?: InstrumentWindow;
374
- readonly doc?: Document;
375
- readonly consoleObj?: ConsoleLike;
376
- readonly consoleLevels?: readonly BreadcrumbLevel[];
377
- /** Skip URLs (e.g. the SDK's own ingest calls) so they never become crumbs. */
378
- readonly ignoreUrl?: (url: string) => boolean;
379
- readonly now?: () => number;
380
- /**
381
- * High-res monotonic clock (spec #122 §D) — used to measure a request's duration as
382
- * a delta around the wrapped call. Defaults to `performance.now`; injectable for tests.
383
- */
384
- readonly mono?: () => number;
385
- /**
386
- * Whether to instrument the **console** stream (spec #122 §L; ticket #138).
387
- * Defaults to `true`; pass `false` and `console` is never wrapped, so no console
388
- * entry is ever recorded — the per-project toggle stops the stream at its source.
389
- */
390
- readonly captureConsole?: boolean;
391
- /**
392
- * Whether to instrument the **network** stream — `fetch` and `XHR` (spec #122 §L;
393
- * ticket #138). Defaults to `true`; pass `false` and neither is wrapped, so no
394
- * network entry is ever recorded. Navigation and masked UI events are unaffected.
395
- */
396
- readonly captureNetwork?: boolean;
397
- }
398
- /**
399
- * The best-effort `file:line` a console call was made from (spec #122 §C). Parses the
400
- * frames of a stack, skipping `skipFrames` leading (SDK-internal) frames so the source
401
- * points at the host code that called `console.*`. Returns `undefined` when no frame
402
- * yields a usable location — the crumb then simply omits `source`.
403
- */
404
- export declare function sourceFromStack(stack: string | undefined, skipFrames?: number): SourceLocation | undefined;
405
- /** Wrap `console` methods so calls at the captured levels become crumbs. */
406
- export declare function instrumentConsole(buffer: BreadcrumbBuffer, consoleObj: ConsoleLike, levels: readonly BreadcrumbLevel[], now: () => number): Teardown;
407
- /**
408
- * Wrap `fetch` to record a rich network crumb on settlement (spec #122 §D, ticket
409
- * #139): method, scrubbed URL, status + statusText, a `performance.now()` duration,
410
- * request size (only when the body is trivially sized), response size (from the
411
- * `content-length` header only), content type, and a failure outcome. The **original**
412
- * outcome is returned untouched — the response is passed through **without its body
413
- * being read** (only two metadata headers are inspected), and a rejection is re-thrown
414
- * so the caller's `unhandledrejection` semantics are preserved.
415
- */
416
- export declare function instrumentFetch(buffer: BreadcrumbBuffer, win: InstrumentWindow, ignoreUrl: (url: string) => boolean, now: () => number, mono?: () => number): Teardown;
417
- /**
418
- * Patch `XMLHttpRequest` to record a rich network crumb when a request settles (spec
419
- * #122 §D, ticket #139): method, scrubbed URL, status + statusText, a `performance.now()`
420
- * duration, request size (only when the body is trivially sized), response size (from
421
- * the `content-length` header only), content type, and a failure outcome distinguished
422
- * by which terminal event fired (`error` / `timeout` / `abort` / `load`). The `send`
423
- * body argument is **never read for content** — only its trivially-known size — so a body
424
- * can never reach the buffer.
425
- */
426
- export declare function instrumentXhr(buffer: BreadcrumbBuffer, win: InstrumentWindow, ignoreUrl: (url: string) => boolean, now: () => number, mono?: () => number): Teardown;
427
- /**
428
- * Wrap `navigator.sendBeacon` to record a network crumb for each beacon (spec #122 §D,
429
- * ticket #139). `sendBeacon` is fire-and-forget with a **synchronous boolean** return —
430
- * `true` when the user agent queued the beacon, `false` when it declined — so wrapping
431
- * must return that boolean unchanged: the original is called first and its result both
432
- * classifies the crumb (`ok` / `network-error`) and is returned to the caller. Only the
433
- * URL and the trivially-known request size are recorded (no response exists to read);
434
- * capture failure is swallowed and the boolean still returned, and if the original itself
435
- * throws we record best-effort and re-throw so the contract is preserved exactly.
436
- */
437
- export declare function instrumentBeacon(buffer: BreadcrumbBuffer, win: InstrumentWindow, ignoreUrl: (url: string) => boolean, now: () => number): Teardown;
438
- /** Record a `navigation` crumb on `pushState`/`replaceState`/pop/hash changes. */
439
- export declare function instrumentNavigation(buffer: BreadcrumbBuffer, win: InstrumentWindow, now: () => number): Teardown;
440
- /** Listen (capture-phase) for clicks and input changes as masked crumbs. */
441
- export declare function instrumentUiEvents(buffer: BreadcrumbBuffer, doc: Document, now: () => number): Teardown;
442
- /**
443
- * Install every capture hook onto a window/document and return a single teardown
444
- * that removes them all. Each hook is independent and defensive: a failure in one
445
- * never blocks the others, and none can throw into the host page.
446
- */
447
- export declare function instrumentBreadcrumbs(buffer: BreadcrumbBuffer, options?: InstrumentOptions): Teardown;
448
- export {};