@pydantic/logfire-session-replay 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.
- package/README.md +61 -5
- package/dist/index-BxzwrVOz.d.cts +220 -0
- package/dist/index-BxzwrVOz.d.ts +220 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/integration.d.cts +8 -10
- package/dist/integration.d.ts +8 -10
- package/package.json +2 -2
- package/dist/index-DARiwDKI.d.cts +0 -170
- package/dist/index-DARiwDKI.d.ts +0 -170
package/README.md
CHANGED
|
@@ -158,6 +158,14 @@ configured cadence. After five minutes without activity, background events use
|
|
|
158
158
|
the greater of `flushIntervalMs` and five minutes. After the replay reaches its
|
|
159
159
|
minimum duration, reaching `maxBufferBytes` still flushes immediately.
|
|
160
160
|
|
|
161
|
+
Normal recording takes a full-DOM checkpoint on the first recorded incremental
|
|
162
|
+
event after five minutes since the previous full snapshot. Each checkpoint
|
|
163
|
+
starts a new upload chunk with its `Meta` and `FullSnapshot` pair. This does not
|
|
164
|
+
change the upload cadence. Error-buffered recording retains its two-minute
|
|
165
|
+
checkpoint interval. If `minSessionDurationMs` exceeds five minutes, normal
|
|
166
|
+
recording uses that longer checkpoint interval so replacement snapshots cannot
|
|
167
|
+
restart the minimum-duration gate before it completes.
|
|
168
|
+
|
|
161
169
|
Replays must contain at least `minSessionDurationMs` of recorded events (10
|
|
162
170
|
seconds by default) before they are uploaded. While waiting, events stay
|
|
163
171
|
buffered. The initial `Meta` and `FullSnapshot` anchor is retained even when it
|
|
@@ -197,6 +205,46 @@ Security Policy blocks fflate's worker compressor, replay retries the same batch
|
|
|
197
205
|
with synchronous gzip and remembers the fallback for that replay controller.
|
|
198
206
|
This preserves the batch, but compression may briefly use the main thread.
|
|
199
207
|
|
|
208
|
+
## Upload Failures
|
|
209
|
+
|
|
210
|
+
Ordinary uploads wait in an in-memory queue and are sent in sequence order.
|
|
211
|
+
Network errors, timeouts, `408`, `425`, `429`, and `5xx` responses are retried
|
|
212
|
+
with the same sequence number and the same compressed bytes. The backoff starts
|
|
213
|
+
at 500 ms, doubles up to 8 s, and uses jitter. Each chunk has a 30-second retry
|
|
214
|
+
budget that includes the time of its requests. `Retry-After` on `429` and `503`
|
|
215
|
+
can lengthen a wait, never shorten it, and is honored while it fits that budget.
|
|
216
|
+
Other `4xx` responses are not retried. The receiver must accept a repeated
|
|
217
|
+
`(sessionId, seq)` as the same chunk, because a retry can follow a request that
|
|
218
|
+
the server stored but whose response was lost.
|
|
219
|
+
|
|
220
|
+
The queue holds at most 2,000,000 bytes, or twice `maxBufferBytes` when that is
|
|
221
|
+
larger. It counts uncompressed events until they are compressed, and compressed
|
|
222
|
+
bodies afterwards. An empty queue always admits the next batch, even when that
|
|
223
|
+
batch is larger than the limit, because an initial full snapshot can exceed it
|
|
224
|
+
and must not be lost. Otherwise, a batch that does not fit is discarded without
|
|
225
|
+
a sequence number, and the chunks already queued are still delivered.
|
|
226
|
+
|
|
227
|
+
A lost chunk is reported to `onError` as a `ReplayUploadError`:
|
|
228
|
+
|
|
229
|
+
- `reason`: `'unconfirmed'` when retries ran out after a request reached the
|
|
230
|
+
network (the server may still hold the chunk), `'rejected'` for a terminal
|
|
231
|
+
HTTP status, or `'not-sent'` when no request left the browser (full queue,
|
|
232
|
+
compression failure, or a failing `headers` or `token` callback).
|
|
233
|
+
- `seq`: the lost chunk, or `undefined` for a batch the full queue rejected.
|
|
234
|
+
- `droppedSeqs`: later chunks that were discarded without upload because they
|
|
235
|
+
depended on the lost one.
|
|
236
|
+
- `status`, `sessionId`, and `cause`.
|
|
237
|
+
|
|
238
|
+
After a loss, replay discards the events that depend on the missing DOM state
|
|
239
|
+
and takes a fresh full snapshot, so playback resumes correctly after the gap.
|
|
240
|
+
Chunks that already start from a later full snapshot are kept. Replay takes at
|
|
241
|
+
most one of these recovery snapshots per minute.
|
|
242
|
+
|
|
243
|
+
`stop()` gives each unsent chunk one last attempt and resolves within about 10
|
|
244
|
+
seconds, even when uploads hang. The queue lives in memory only: chunks that are
|
|
245
|
+
still queued when the page closes or reloads are lost, and a reloaded page does
|
|
246
|
+
not retry them.
|
|
247
|
+
|
|
200
248
|
## Privacy
|
|
201
249
|
|
|
202
250
|
The recorder masks all rendered text and input values by default
|
|
@@ -227,8 +275,8 @@ The recorder uses a Sentry-style two-rate model:
|
|
|
227
275
|
|
|
228
276
|
- `sessionSampleRate`: records the current browser session continuously
|
|
229
277
|
- `onErrorSampleRate`: for sessions not selected by `sessionSampleRate`, keeps
|
|
230
|
-
an in-memory replay buffer and promotes it
|
|
231
|
-
or `
|
|
278
|
+
an in-memory replay buffer and promotes it for an uncaught `window.error` or
|
|
279
|
+
`unhandledrejection`, or for an error passed to `captureError()`
|
|
232
280
|
|
|
233
281
|
Buffer mode is bounded by `maxBufferBytes`, measured as the UTF-8 byte sum of
|
|
234
282
|
event JSON before envelope metadata and compression. It keeps the latest full
|
|
@@ -244,8 +292,14 @@ properties. A sampled-off session keeps only a lightweight session-id monitor;
|
|
|
244
292
|
it does not start rrweb or patch console, network, or navigation globals, and a
|
|
245
293
|
later sampled session can begin recording without reloading the page.
|
|
246
294
|
|
|
247
|
-
|
|
248
|
-
|
|
295
|
+
Pass a caught error to `replay.captureError(error)` to add it to the replay and
|
|
296
|
+
promote the buffer, as an uncaught error does. For an error that occurred
|
|
297
|
+
earlier, pass `{ timestamp }` in epoch milliseconds; the error event records it. An error object is added once,
|
|
298
|
+
even when it is also uncaught later. `console.error` does not promote the buffer.
|
|
299
|
+
|
|
300
|
+
Set `captureGlobalErrors: false` when another layer listens for uncaught errors
|
|
301
|
+
and forwards them through `captureError()`. The replay then installs no `error`
|
|
302
|
+
or `unhandledrejection` listeners.
|
|
249
303
|
|
|
250
304
|
`stop()` is awaitable and flushes the final chunk before resolving.
|
|
251
305
|
|
|
@@ -277,7 +331,9 @@ logfire.configure({
|
|
|
277
331
|
```
|
|
278
332
|
|
|
279
333
|
The browser package owns RUM session identity, span correlation attributes, and
|
|
280
|
-
cleanup ordering.
|
|
334
|
+
cleanup ordering. It also reports uncaught errors as spans and passes every
|
|
335
|
+
`reportError()` call to `captureError()`, so a handled error that the
|
|
336
|
+
application reports promotes an error-buffered replay. Call `startSessionReplay()` directly only for standalone or
|
|
281
337
|
advanced integrations that do not use `@pydantic/logfire-browser`.
|
|
282
338
|
|
|
283
339
|
## Local Development Notes
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
interface RrwebEvent {
|
|
3
|
+
type: number;
|
|
4
|
+
data: unknown;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
}
|
|
7
|
+
declare const EventType: {
|
|
8
|
+
readonly DomContentLoaded: 0;
|
|
9
|
+
readonly Load: 1;
|
|
10
|
+
readonly FullSnapshot: 2;
|
|
11
|
+
readonly IncrementalSnapshot: 3;
|
|
12
|
+
readonly Meta: 4;
|
|
13
|
+
readonly Custom: 5;
|
|
14
|
+
readonly Plugin: 6;
|
|
15
|
+
};
|
|
16
|
+
declare const IncrementalSource: {
|
|
17
|
+
readonly Mutation: 0;
|
|
18
|
+
readonly MouseMove: 1;
|
|
19
|
+
readonly MouseInteraction: 2;
|
|
20
|
+
readonly Scroll: 3;
|
|
21
|
+
readonly ViewportResize: 4;
|
|
22
|
+
readonly Input: 5;
|
|
23
|
+
readonly TouchMove: 6;
|
|
24
|
+
readonly MediaInteraction: 7;
|
|
25
|
+
readonly Drag: 12;
|
|
26
|
+
readonly Selection: 14;
|
|
27
|
+
};
|
|
28
|
+
declare const MouseInteractions: {
|
|
29
|
+
readonly MouseUp: 0;
|
|
30
|
+
readonly MouseDown: 1;
|
|
31
|
+
readonly Click: 2;
|
|
32
|
+
readonly ContextMenu: 3;
|
|
33
|
+
readonly DblClick: 4;
|
|
34
|
+
};
|
|
35
|
+
declare const CustomTag: {
|
|
36
|
+
readonly Error: "logfire.error";
|
|
37
|
+
readonly Console: "logfire.console";
|
|
38
|
+
readonly Network: "logfire.network";
|
|
39
|
+
readonly Navigation: "logfire.navigation";
|
|
40
|
+
};
|
|
41
|
+
declare const CHUNK_ENVELOPE_VERSION: 1;
|
|
42
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
43
|
+
type ConsoleLevel = "log" | "info" | "warn" | "error" | "debug";
|
|
44
|
+
type SamplingMode = "full" | "buffer" | "off";
|
|
45
|
+
type SessionAttributeValue = string | number | boolean;
|
|
46
|
+
type SessionAttributesInput = Record<string, SessionAttributeValue | undefined>;
|
|
47
|
+
type SessionAttributes = Record<string, SessionAttributeValue>;
|
|
48
|
+
interface ConsolePayload {
|
|
49
|
+
level: ConsoleLevel;
|
|
50
|
+
args: string[];
|
|
51
|
+
source?: string;
|
|
52
|
+
}
|
|
53
|
+
interface NetworkPayload {
|
|
54
|
+
method: string;
|
|
55
|
+
url: string;
|
|
56
|
+
status: number;
|
|
57
|
+
durationMs: number;
|
|
58
|
+
failed?: boolean;
|
|
59
|
+
reqBytes?: number;
|
|
60
|
+
resBytes?: number;
|
|
61
|
+
}
|
|
62
|
+
interface NavigationPayload {
|
|
63
|
+
url: string;
|
|
64
|
+
kind: "push" | "replace" | "pop";
|
|
65
|
+
}
|
|
66
|
+
/** User identity reported by the application. Name and email are optional personal data. */
|
|
67
|
+
interface ReplayUser {
|
|
68
|
+
id: string;
|
|
69
|
+
name?: string;
|
|
70
|
+
email?: string;
|
|
71
|
+
}
|
|
72
|
+
interface ChunkMeta {
|
|
73
|
+
seq: number;
|
|
74
|
+
firstTimestamp: number;
|
|
75
|
+
lastTimestamp: number;
|
|
76
|
+
eventCount: number;
|
|
77
|
+
clickCount: number;
|
|
78
|
+
keypressCount: number;
|
|
79
|
+
errorCount: number;
|
|
80
|
+
hasFullSnapshot: boolean;
|
|
81
|
+
urls: string[];
|
|
82
|
+
distinctId?: string;
|
|
83
|
+
user?: ReplayUser;
|
|
84
|
+
sessionAttributes?: SessionAttributes;
|
|
85
|
+
}
|
|
86
|
+
interface ChunkEnvelope {
|
|
87
|
+
version: typeof CHUNK_ENVELOPE_VERSION;
|
|
88
|
+
meta: ChunkMeta;
|
|
89
|
+
events: RrwebEvent[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Early Access session replay recorder configuration. Keep browser replay
|
|
93
|
+
* rollout behind an application flag.
|
|
94
|
+
*/
|
|
95
|
+
interface SessionReplayConfig {
|
|
96
|
+
/**
|
|
97
|
+
* Browser-safe replay upload endpoint, either direct Logfire ingest with a
|
|
98
|
+
* restricted frontend application token or an application-owned proxy. The
|
|
99
|
+
* SDK posts to `${replayUrl}/${recordingId}?seq=${seq}`. A replay continues
|
|
100
|
+
* across same-tab reloads/navigations; a new tab gets a separate recording id.
|
|
101
|
+
*/
|
|
102
|
+
replayUrl: string;
|
|
103
|
+
/**
|
|
104
|
+
* Headers added to each replay upload. Direct Logfire ingest uses the same
|
|
105
|
+
* restricted frontend application headers as traces and metrics.
|
|
106
|
+
*/
|
|
107
|
+
headers?: () => MaybePromise<Record<string, string>>;
|
|
108
|
+
/**
|
|
109
|
+
* Convenience token source. A non-empty value removes any case-insensitive
|
|
110
|
+
* Authorization entry returned by `headers`, then sets
|
|
111
|
+
* `Authorization: Bearer ${token}`. Only restricted frontend application
|
|
112
|
+
* tokens are safe in browsers.
|
|
113
|
+
*/
|
|
114
|
+
token?: string | (() => MaybePromise<string>);
|
|
115
|
+
/**
|
|
116
|
+
* Optional external session id source. The browser SDK integration should
|
|
117
|
+
* pass its RUM session id here. This controls sampling and session rotation;
|
|
118
|
+
* it is not used as the upload/playback id (see `getRecordingId()`).
|
|
119
|
+
*/
|
|
120
|
+
getSessionId?: () => string | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* Returns low-cardinality, non-PII dimensions to snapshot once per replay
|
|
123
|
+
* session. Invalid or oversized entries are omitted.
|
|
124
|
+
*/
|
|
125
|
+
getSessionAttributes?: () => SessionAttributesInput;
|
|
126
|
+
/** Probability of recording each new browser session in full. */
|
|
127
|
+
sessionSampleRate?: number;
|
|
128
|
+
/** Probability of buffering each otherwise-unsampled session for uncaught error promotion. */
|
|
129
|
+
onErrorSampleRate?: number;
|
|
130
|
+
/** Mask all rendered DOM text. Defaults to true. */
|
|
131
|
+
maskAllText?: boolean;
|
|
132
|
+
maskAllInputs?: boolean;
|
|
133
|
+
maskTextSelector?: string;
|
|
134
|
+
blockSelector?: string;
|
|
135
|
+
/** Batching interval while the user is active. Defaults to 5 seconds. */
|
|
136
|
+
flushIntervalMs?: number;
|
|
137
|
+
maxBufferBytes?: number;
|
|
138
|
+
/** Minimum recording duration before a replay is uploaded. Defaults to 10 seconds. */
|
|
139
|
+
minSessionDurationMs?: number;
|
|
140
|
+
sessionIdleTimeoutMs?: number;
|
|
141
|
+
/** Maximum recording duration before the session ID rotates. Defaults to 2 hours. */
|
|
142
|
+
maxSessionDurationMs?: number;
|
|
143
|
+
distinctId?: string;
|
|
144
|
+
getDistinctId?: () => string | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Returns the current user, snapshotted for every chunk as `meta.user`. Only
|
|
147
|
+
* a non-empty `id`, `name` and `email` are kept; name and email are optional
|
|
148
|
+
* personal data. Without `getDistinctId`, the same snapshot's `id` becomes
|
|
149
|
+
* `meta.distinctId`.
|
|
150
|
+
*/
|
|
151
|
+
getUser?: () => ReplayUser | undefined;
|
|
152
|
+
captureConsole?: boolean;
|
|
153
|
+
captureNetwork?: boolean;
|
|
154
|
+
captureNavigation?: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Listen for uncaught `error` and `unhandledrejection` events, add them to the replay, and
|
|
157
|
+
* promote an error-sampled buffer. Defaults to true. The browser SDK turns this off when it
|
|
158
|
+
* captures global errors itself and forwards them through `captureError()`.
|
|
159
|
+
*/
|
|
160
|
+
captureGlobalErrors?: boolean;
|
|
161
|
+
ignoreUrlPatterns?: RegExp[];
|
|
162
|
+
redactUrlPatterns?: RegExp[];
|
|
163
|
+
onError?: (error: unknown) => void;
|
|
164
|
+
fetchImpl?: typeof fetch;
|
|
165
|
+
now?: () => number;
|
|
166
|
+
random?: () => number;
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/uploadError.d.ts
|
|
170
|
+
/**
|
|
171
|
+
* - `unconfirmed`: retries ended after a request reached the network; the server may hold the chunk.
|
|
172
|
+
* - `rejected`: the server answered with a terminal HTTP status.
|
|
173
|
+
* - `not-sent`: no request reached the network (queue full, compression, headers or token failed).
|
|
174
|
+
*/
|
|
175
|
+
type ReplayUploadFailureReason = "unconfirmed" | "rejected" | "not-sent";
|
|
176
|
+
interface ReplayUploadErrorInit {
|
|
177
|
+
cause: unknown;
|
|
178
|
+
droppedSeqs?: readonly number[];
|
|
179
|
+
reason: ReplayUploadFailureReason;
|
|
180
|
+
seq: number | undefined;
|
|
181
|
+
sessionId: string;
|
|
182
|
+
status?: number | undefined;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Reported through `onError` when a replay chunk is not confirmed by the server.
|
|
186
|
+
* Recording continues from a fresh full snapshot, so `droppedSeqs` lists the
|
|
187
|
+
* later chunks that were discarded because they depended on the lost one.
|
|
188
|
+
*/
|
|
189
|
+
declare class ReplayUploadError extends Error {
|
|
190
|
+
readonly droppedSeqs: readonly number[];
|
|
191
|
+
readonly reason: ReplayUploadFailureReason;
|
|
192
|
+
readonly seq: number | undefined;
|
|
193
|
+
readonly sessionId: string;
|
|
194
|
+
readonly status: number | undefined;
|
|
195
|
+
constructor(init: ReplayUploadErrorInit);
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/index.d.ts
|
|
199
|
+
interface SessionReplay {
|
|
200
|
+
readonly recording: boolean;
|
|
201
|
+
readonly mode: "full" | "buffer" | "off";
|
|
202
|
+
getSessionId(): string;
|
|
203
|
+
/**
|
|
204
|
+
* Add an error to the replay and promote an error-sampled buffer, as an uncaught error does.
|
|
205
|
+
* The same error object is added once, however many times it is captured. Pass the time the
|
|
206
|
+
* error occurred, in epoch milliseconds, when it is captured later; the error event records it
|
|
207
|
+
* as `timestamp`.
|
|
208
|
+
*/
|
|
209
|
+
captureError(error: unknown, options?: CaptureErrorOptions): void;
|
|
210
|
+
/** The current DOM recording's upload/playback id; distinct from the RUM session id. */
|
|
211
|
+
getRecordingId(): string;
|
|
212
|
+
flush(): Promise<void>;
|
|
213
|
+
stop(): Promise<void>;
|
|
214
|
+
}
|
|
215
|
+
interface CaptureErrorOptions {
|
|
216
|
+
timestamp?: number;
|
|
217
|
+
}
|
|
218
|
+
declare function startSessionReplay(config: SessionReplayConfig): SessionReplay;
|
|
219
|
+
//#endregion
|
|
220
|
+
export { SessionReplayConfig as C, SessionAttributesInput as S, ReplayUser as _, ReplayUploadErrorInit as a, SessionAttributeValue as b, ChunkEnvelope as c, CustomTag as d, EventType as f, NetworkPayload as g, NavigationPayload as h, ReplayUploadError as i, ChunkMeta as l, MouseInteractions as m, SessionReplay as n, ReplayUploadFailureReason as o, IncrementalSource as p, startSessionReplay as r, CHUNK_ENVELOPE_VERSION as s, CaptureErrorOptions as t, ConsolePayload as u, RrwebEvent as v, SessionAttributes as x, SamplingMode as y };
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
interface RrwebEvent {
|
|
3
|
+
type: number;
|
|
4
|
+
data: unknown;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
}
|
|
7
|
+
declare const EventType: {
|
|
8
|
+
readonly DomContentLoaded: 0;
|
|
9
|
+
readonly Load: 1;
|
|
10
|
+
readonly FullSnapshot: 2;
|
|
11
|
+
readonly IncrementalSnapshot: 3;
|
|
12
|
+
readonly Meta: 4;
|
|
13
|
+
readonly Custom: 5;
|
|
14
|
+
readonly Plugin: 6;
|
|
15
|
+
};
|
|
16
|
+
declare const IncrementalSource: {
|
|
17
|
+
readonly Mutation: 0;
|
|
18
|
+
readonly MouseMove: 1;
|
|
19
|
+
readonly MouseInteraction: 2;
|
|
20
|
+
readonly Scroll: 3;
|
|
21
|
+
readonly ViewportResize: 4;
|
|
22
|
+
readonly Input: 5;
|
|
23
|
+
readonly TouchMove: 6;
|
|
24
|
+
readonly MediaInteraction: 7;
|
|
25
|
+
readonly Drag: 12;
|
|
26
|
+
readonly Selection: 14;
|
|
27
|
+
};
|
|
28
|
+
declare const MouseInteractions: {
|
|
29
|
+
readonly MouseUp: 0;
|
|
30
|
+
readonly MouseDown: 1;
|
|
31
|
+
readonly Click: 2;
|
|
32
|
+
readonly ContextMenu: 3;
|
|
33
|
+
readonly DblClick: 4;
|
|
34
|
+
};
|
|
35
|
+
declare const CustomTag: {
|
|
36
|
+
readonly Error: "logfire.error";
|
|
37
|
+
readonly Console: "logfire.console";
|
|
38
|
+
readonly Network: "logfire.network";
|
|
39
|
+
readonly Navigation: "logfire.navigation";
|
|
40
|
+
};
|
|
41
|
+
declare const CHUNK_ENVELOPE_VERSION: 1;
|
|
42
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
43
|
+
type ConsoleLevel = "log" | "info" | "warn" | "error" | "debug";
|
|
44
|
+
type SamplingMode = "full" | "buffer" | "off";
|
|
45
|
+
type SessionAttributeValue = string | number | boolean;
|
|
46
|
+
type SessionAttributesInput = Record<string, SessionAttributeValue | undefined>;
|
|
47
|
+
type SessionAttributes = Record<string, SessionAttributeValue>;
|
|
48
|
+
interface ConsolePayload {
|
|
49
|
+
level: ConsoleLevel;
|
|
50
|
+
args: string[];
|
|
51
|
+
source?: string;
|
|
52
|
+
}
|
|
53
|
+
interface NetworkPayload {
|
|
54
|
+
method: string;
|
|
55
|
+
url: string;
|
|
56
|
+
status: number;
|
|
57
|
+
durationMs: number;
|
|
58
|
+
failed?: boolean;
|
|
59
|
+
reqBytes?: number;
|
|
60
|
+
resBytes?: number;
|
|
61
|
+
}
|
|
62
|
+
interface NavigationPayload {
|
|
63
|
+
url: string;
|
|
64
|
+
kind: "push" | "replace" | "pop";
|
|
65
|
+
}
|
|
66
|
+
/** User identity reported by the application. Name and email are optional personal data. */
|
|
67
|
+
interface ReplayUser {
|
|
68
|
+
id: string;
|
|
69
|
+
name?: string;
|
|
70
|
+
email?: string;
|
|
71
|
+
}
|
|
72
|
+
interface ChunkMeta {
|
|
73
|
+
seq: number;
|
|
74
|
+
firstTimestamp: number;
|
|
75
|
+
lastTimestamp: number;
|
|
76
|
+
eventCount: number;
|
|
77
|
+
clickCount: number;
|
|
78
|
+
keypressCount: number;
|
|
79
|
+
errorCount: number;
|
|
80
|
+
hasFullSnapshot: boolean;
|
|
81
|
+
urls: string[];
|
|
82
|
+
distinctId?: string;
|
|
83
|
+
user?: ReplayUser;
|
|
84
|
+
sessionAttributes?: SessionAttributes;
|
|
85
|
+
}
|
|
86
|
+
interface ChunkEnvelope {
|
|
87
|
+
version: typeof CHUNK_ENVELOPE_VERSION;
|
|
88
|
+
meta: ChunkMeta;
|
|
89
|
+
events: RrwebEvent[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Early Access session replay recorder configuration. Keep browser replay
|
|
93
|
+
* rollout behind an application flag.
|
|
94
|
+
*/
|
|
95
|
+
interface SessionReplayConfig {
|
|
96
|
+
/**
|
|
97
|
+
* Browser-safe replay upload endpoint, either direct Logfire ingest with a
|
|
98
|
+
* restricted frontend application token or an application-owned proxy. The
|
|
99
|
+
* SDK posts to `${replayUrl}/${recordingId}?seq=${seq}`. A replay continues
|
|
100
|
+
* across same-tab reloads/navigations; a new tab gets a separate recording id.
|
|
101
|
+
*/
|
|
102
|
+
replayUrl: string;
|
|
103
|
+
/**
|
|
104
|
+
* Headers added to each replay upload. Direct Logfire ingest uses the same
|
|
105
|
+
* restricted frontend application headers as traces and metrics.
|
|
106
|
+
*/
|
|
107
|
+
headers?: () => MaybePromise<Record<string, string>>;
|
|
108
|
+
/**
|
|
109
|
+
* Convenience token source. A non-empty value removes any case-insensitive
|
|
110
|
+
* Authorization entry returned by `headers`, then sets
|
|
111
|
+
* `Authorization: Bearer ${token}`. Only restricted frontend application
|
|
112
|
+
* tokens are safe in browsers.
|
|
113
|
+
*/
|
|
114
|
+
token?: string | (() => MaybePromise<string>);
|
|
115
|
+
/**
|
|
116
|
+
* Optional external session id source. The browser SDK integration should
|
|
117
|
+
* pass its RUM session id here. This controls sampling and session rotation;
|
|
118
|
+
* it is not used as the upload/playback id (see `getRecordingId()`).
|
|
119
|
+
*/
|
|
120
|
+
getSessionId?: () => string | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* Returns low-cardinality, non-PII dimensions to snapshot once per replay
|
|
123
|
+
* session. Invalid or oversized entries are omitted.
|
|
124
|
+
*/
|
|
125
|
+
getSessionAttributes?: () => SessionAttributesInput;
|
|
126
|
+
/** Probability of recording each new browser session in full. */
|
|
127
|
+
sessionSampleRate?: number;
|
|
128
|
+
/** Probability of buffering each otherwise-unsampled session for uncaught error promotion. */
|
|
129
|
+
onErrorSampleRate?: number;
|
|
130
|
+
/** Mask all rendered DOM text. Defaults to true. */
|
|
131
|
+
maskAllText?: boolean;
|
|
132
|
+
maskAllInputs?: boolean;
|
|
133
|
+
maskTextSelector?: string;
|
|
134
|
+
blockSelector?: string;
|
|
135
|
+
/** Batching interval while the user is active. Defaults to 5 seconds. */
|
|
136
|
+
flushIntervalMs?: number;
|
|
137
|
+
maxBufferBytes?: number;
|
|
138
|
+
/** Minimum recording duration before a replay is uploaded. Defaults to 10 seconds. */
|
|
139
|
+
minSessionDurationMs?: number;
|
|
140
|
+
sessionIdleTimeoutMs?: number;
|
|
141
|
+
/** Maximum recording duration before the session ID rotates. Defaults to 2 hours. */
|
|
142
|
+
maxSessionDurationMs?: number;
|
|
143
|
+
distinctId?: string;
|
|
144
|
+
getDistinctId?: () => string | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Returns the current user, snapshotted for every chunk as `meta.user`. Only
|
|
147
|
+
* a non-empty `id`, `name` and `email` are kept; name and email are optional
|
|
148
|
+
* personal data. Without `getDistinctId`, the same snapshot's `id` becomes
|
|
149
|
+
* `meta.distinctId`.
|
|
150
|
+
*/
|
|
151
|
+
getUser?: () => ReplayUser | undefined;
|
|
152
|
+
captureConsole?: boolean;
|
|
153
|
+
captureNetwork?: boolean;
|
|
154
|
+
captureNavigation?: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Listen for uncaught `error` and `unhandledrejection` events, add them to the replay, and
|
|
157
|
+
* promote an error-sampled buffer. Defaults to true. The browser SDK turns this off when it
|
|
158
|
+
* captures global errors itself and forwards them through `captureError()`.
|
|
159
|
+
*/
|
|
160
|
+
captureGlobalErrors?: boolean;
|
|
161
|
+
ignoreUrlPatterns?: RegExp[];
|
|
162
|
+
redactUrlPatterns?: RegExp[];
|
|
163
|
+
onError?: (error: unknown) => void;
|
|
164
|
+
fetchImpl?: typeof fetch;
|
|
165
|
+
now?: () => number;
|
|
166
|
+
random?: () => number;
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/uploadError.d.ts
|
|
170
|
+
/**
|
|
171
|
+
* - `unconfirmed`: retries ended after a request reached the network; the server may hold the chunk.
|
|
172
|
+
* - `rejected`: the server answered with a terminal HTTP status.
|
|
173
|
+
* - `not-sent`: no request reached the network (queue full, compression, headers or token failed).
|
|
174
|
+
*/
|
|
175
|
+
type ReplayUploadFailureReason = "unconfirmed" | "rejected" | "not-sent";
|
|
176
|
+
interface ReplayUploadErrorInit {
|
|
177
|
+
cause: unknown;
|
|
178
|
+
droppedSeqs?: readonly number[];
|
|
179
|
+
reason: ReplayUploadFailureReason;
|
|
180
|
+
seq: number | undefined;
|
|
181
|
+
sessionId: string;
|
|
182
|
+
status?: number | undefined;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Reported through `onError` when a replay chunk is not confirmed by the server.
|
|
186
|
+
* Recording continues from a fresh full snapshot, so `droppedSeqs` lists the
|
|
187
|
+
* later chunks that were discarded because they depended on the lost one.
|
|
188
|
+
*/
|
|
189
|
+
declare class ReplayUploadError extends Error {
|
|
190
|
+
readonly droppedSeqs: readonly number[];
|
|
191
|
+
readonly reason: ReplayUploadFailureReason;
|
|
192
|
+
readonly seq: number | undefined;
|
|
193
|
+
readonly sessionId: string;
|
|
194
|
+
readonly status: number | undefined;
|
|
195
|
+
constructor(init: ReplayUploadErrorInit);
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/index.d.ts
|
|
199
|
+
interface SessionReplay {
|
|
200
|
+
readonly recording: boolean;
|
|
201
|
+
readonly mode: "full" | "buffer" | "off";
|
|
202
|
+
getSessionId(): string;
|
|
203
|
+
/**
|
|
204
|
+
* Add an error to the replay and promote an error-sampled buffer, as an uncaught error does.
|
|
205
|
+
* The same error object is added once, however many times it is captured. Pass the time the
|
|
206
|
+
* error occurred, in epoch milliseconds, when it is captured later; the error event records it
|
|
207
|
+
* as `timestamp`.
|
|
208
|
+
*/
|
|
209
|
+
captureError(error: unknown, options?: CaptureErrorOptions): void;
|
|
210
|
+
/** The current DOM recording's upload/playback id; distinct from the RUM session id. */
|
|
211
|
+
getRecordingId(): string;
|
|
212
|
+
flush(): Promise<void>;
|
|
213
|
+
stop(): Promise<void>;
|
|
214
|
+
}
|
|
215
|
+
interface CaptureErrorOptions {
|
|
216
|
+
timestamp?: number;
|
|
217
|
+
}
|
|
218
|
+
declare function startSessionReplay(config: SessionReplayConfig): SessionReplay;
|
|
219
|
+
//#endregion
|
|
220
|
+
export { SessionReplayConfig as C, SessionAttributesInput as S, ReplayUser as _, ReplayUploadErrorInit as a, SessionAttributeValue as b, ChunkEnvelope as c, CustomTag as d, EventType as f, NetworkPayload as g, NavigationPayload as h, ReplayUploadError as i, ChunkMeta as l, MouseInteractions as m, SessionReplay as n, ReplayUploadFailureReason as o, IncrementalSource as p, startSessionReplay as r, CHUNK_ENVELOPE_VERSION as s, CaptureErrorOptions as t, ConsolePayload as u, RrwebEvent as v, SessionAttributes as x, SamplingMode as y };
|