@groundcover/browser 1.0.7-rc.0 → 1.0.8-rc.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/CHANGELOG.md +44 -0
- package/dist/index.d.mts +157 -67
- package/dist/index.d.ts +157 -67
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,50 @@ All notable changes to `@groundcover/browser` are documented here. This project
|
|
|
4
4
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the
|
|
5
5
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
|
|
6
6
|
|
|
7
|
+
## [1.0.8-rc.0]
|
|
8
|
+
|
|
9
|
+
Release candidate. Published under the `rc` dist-tag — `npm install @groundcover/browser@rc`.
|
|
10
|
+
|
|
11
|
+
The public API is unchanged: the generated type declarations are byte-identical to 1.0.7, the
|
|
12
|
+
published bundle contains the same 10 files, and the `/json/rum` wire format is unchanged (pinned by
|
|
13
|
+
a golden characterization suite). The two behaviour changes below are the reason this ships as an
|
|
14
|
+
RC rather than a patch.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **`sessionSampleRate: 0` now samples nothing.** It was previously treated as "unset" by a falsy
|
|
19
|
+
check, so configuring `0` sampled *every* session — the exact opposite of the intent. If you set
|
|
20
|
+
`sessionSampleRate: 0` today and rely on the current (inverted) behaviour, sessions will stop
|
|
21
|
+
being collected when you upgrade.
|
|
22
|
+
- **The session sampling verdict is now re-rolled per session.** It is documented as "decided once
|
|
23
|
+
per session", but nothing ever removed the stored decision, so in practice it was decided once per
|
|
24
|
+
browser-storage lifetime and every session after the first rotation inherited the original
|
|
25
|
+
verdict. The decision is now cleared when a session rotates (inactivity gap, max-duration cap, or
|
|
26
|
+
a new session id supplied by the host). Re-supplying the *same* session id is still a continuation
|
|
27
|
+
and keeps its verdict.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **Session rotation could stop permanently if a host hook threw.** The in-progress flag was set
|
|
32
|
+
before the host's rotation-context hook ran but only cleared in a `finally` that the hook sat
|
|
33
|
+
outside, so a throw left it stuck — silently disabling all further session rotation for the life
|
|
34
|
+
of the page. Not reachable through the browser SDK's own hook, but the surrounding error handling
|
|
35
|
+
swallowed the throw without resetting the flag, so it would have failed invisibly.
|
|
36
|
+
- **Configuration option groups were shared between SDK instances.** `privacy`, `tracing`,
|
|
37
|
+
`transport` and `replay` groups the caller didn't supply were held by reference from the shared
|
|
38
|
+
defaults, so multiple SDK instances in one page aliased the same objects (and the module-level
|
|
39
|
+
defaults themselves).
|
|
40
|
+
- **A failed batch send could raise an unhandled promise rejection** instead of logging, when
|
|
41
|
+
`options` was absent on the resolved config.
|
|
42
|
+
|
|
43
|
+
### Internal
|
|
44
|
+
|
|
45
|
+
- The platform-agnostic pipeline (config, session policy, event enrichment, batching, redaction,
|
|
46
|
+
transport) now lives in a private `@groundcover/rum-core` package, inlined into this bundle at
|
|
47
|
+
build time. It compiles with no DOM lib so platform coupling fails the build, and the network
|
|
48
|
+
send, storage, device metadata and view derivation are injected by this package. Groundwork for
|
|
49
|
+
the React Native SDK; no effect on the published browser artifact.
|
|
50
|
+
|
|
7
51
|
## [1.0.5]
|
|
8
52
|
|
|
9
53
|
### Added
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The event kinds the browser SDK ships today. This is the DEFAULT vocabulary,
|
|
3
|
+
* not the only permitted one — see `BaseEvent`.
|
|
4
|
+
*/
|
|
5
|
+
type KnownEventType = "dom.event" | "exception" | "network" | "log" | "pageload" | "navigation" | "custom" | "performance" | "replay";
|
|
6
|
+
/**
|
|
7
|
+
* `TType` is what lets a platform package name its own event kinds without
|
|
8
|
+
* editing core: React Native can pass `KnownEventType | "tap" | "screen_view"`
|
|
9
|
+
* and every core class below flows it through. It defaults to
|
|
10
|
+
* `KnownEventType`, so nothing that only deals in the shipped browser kinds
|
|
11
|
+
* has to spell it out.
|
|
12
|
+
*
|
|
13
|
+
* Shared shapes are still shared — the point is that the set of names is open,
|
|
14
|
+
* not that each platform reinvents the envelope.
|
|
15
|
+
*/
|
|
16
|
+
type BaseEvent<TType extends string = KnownEventType> = {
|
|
17
|
+
type: TType;
|
|
3
18
|
timestamp?: number;
|
|
4
19
|
end_timestamp?: number;
|
|
5
20
|
start_timestamp?: number;
|
|
@@ -10,11 +25,6 @@ type BaseEvent = {
|
|
|
10
25
|
traceId: string | undefined;
|
|
11
26
|
attributes?: Record<string, unknown>;
|
|
12
27
|
};
|
|
13
|
-
type LocationAttributes = {
|
|
14
|
-
path: string;
|
|
15
|
-
url: string;
|
|
16
|
-
title: string;
|
|
17
|
-
};
|
|
18
28
|
type UserIdentifiers = {
|
|
19
29
|
id: string;
|
|
20
30
|
email: string;
|
|
@@ -165,22 +175,20 @@ type ReplayEventAttributes = {
|
|
|
165
175
|
replay_full_snapshot_timestamp?: number;
|
|
166
176
|
};
|
|
167
177
|
|
|
168
|
-
type Event<T extends
|
|
178
|
+
type Event$1<T extends string, A> = Omit<BaseEvent<T>, "attributes"> & {
|
|
169
179
|
type: T;
|
|
170
|
-
attributes: A
|
|
171
|
-
location?: LocationAttributes;
|
|
172
|
-
};
|
|
180
|
+
attributes: A;
|
|
173
181
|
};
|
|
174
|
-
type EventTypes = {
|
|
175
|
-
"dom.event": Event<"dom.event", DomEventAttributes>;
|
|
176
|
-
exception: Event<"exception", ErrorEventAttributes>;
|
|
177
|
-
network: Event<"network", NetworkEventAttributes>;
|
|
178
|
-
log: Event<"log", LogEventAttributes>;
|
|
179
|
-
pageload: Event<"pageload", PageLoadEventAttributes>;
|
|
180
|
-
navigation: Event<"navigation", NavigationEventAttributes>;
|
|
181
|
-
custom: Event<"custom", UserDefinedEventAttributes>;
|
|
182
|
-
performance: Event<"performance", PerformanceEventAttributes>;
|
|
183
|
-
replay: Event<"replay", ReplayEventAttributes>;
|
|
182
|
+
type EventTypes$1 = {
|
|
183
|
+
"dom.event": Event$1<"dom.event", DomEventAttributes>;
|
|
184
|
+
exception: Event$1<"exception", ErrorEventAttributes>;
|
|
185
|
+
network: Event$1<"network", NetworkEventAttributes>;
|
|
186
|
+
log: Event$1<"log", LogEventAttributes>;
|
|
187
|
+
pageload: Event$1<"pageload", PageLoadEventAttributes>;
|
|
188
|
+
navigation: Event$1<"navigation", NavigationEventAttributes>;
|
|
189
|
+
custom: Event$1<"custom", UserDefinedEventAttributes>;
|
|
190
|
+
performance: Event$1<"performance", PerformanceEventAttributes>;
|
|
191
|
+
replay: Event$1<"replay", ReplayEventAttributes>;
|
|
184
192
|
};
|
|
185
193
|
type PrivacyLevel = "mask-sensitive" | "mask-all" | "allow";
|
|
186
194
|
/** The non-replay event surface a redactor field belongs to. */
|
|
@@ -205,18 +213,15 @@ interface RedactField {
|
|
|
205
213
|
* only cares about one field can't accidentally disable masking elsewhere.
|
|
206
214
|
*/
|
|
207
215
|
type Redactor = (field: RedactField) => string | number | boolean | null | undefined;
|
|
208
|
-
interface PrivacyOptions {
|
|
216
|
+
interface PrivacyOptions$1 {
|
|
209
217
|
/**
|
|
210
|
-
* Master switch for
|
|
211
|
-
* (
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* `'allow'` opts out.
|
|
218
|
+
* Master switch for data masking. `'mask-sensitive'` (default) masks only
|
|
219
|
+
* SENSITIVE data (sensitive-key matches, platform-flagged UI elements) and
|
|
220
|
+
* redacts network/log/error payloads while capturing the rest;
|
|
221
|
+
* `'mask-all'` also masks all captured input/text; `'allow'` opts out.
|
|
222
|
+
* Platform packages define what "sensitive UI element" means on their host.
|
|
216
223
|
*/
|
|
217
224
|
level?: PrivacyLevel;
|
|
218
|
-
/** CSS selectors whose text/inputs are always masked (replay + DOM), regardless of `level` except `'allow'`. */
|
|
219
|
-
maskSelectors?: string[];
|
|
220
225
|
/** Redact network request/response bodies. Default on unless `level` is `'allow'`. */
|
|
221
226
|
maskNetworkBodies?: boolean;
|
|
222
227
|
/** Redact network URL query params. Default on unless `level` is `'allow'`. */
|
|
@@ -233,13 +238,14 @@ interface PrivacyOptions {
|
|
|
233
238
|
* {@link Redactor}. For arbitrary whole-event changes use `enrichEvent`.
|
|
234
239
|
*/
|
|
235
240
|
redact?: Redactor;
|
|
236
|
-
/**
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Session-recording masking — replay is a platform feature (rrweb on the
|
|
243
|
+
* browser), so its masking surface is typed by the platform package: the
|
|
244
|
+
* browser SDK narrows this to `{ maskTextFn?, maskInputFn? }` over
|
|
245
|
+
* HTMLElement (see the browser package's `config/types.ts`). Core never
|
|
246
|
+
* reads it — it is resolved platform-side at record() time.
|
|
247
|
+
*/
|
|
248
|
+
replay?: unknown;
|
|
243
249
|
}
|
|
244
250
|
/** Distributed-tracing header propagation for outgoing network requests. */
|
|
245
251
|
interface TracingOptions {
|
|
@@ -266,44 +272,35 @@ interface TransportOptions {
|
|
|
266
272
|
/** Gzip-compress outgoing batches; offloaded to the Web Worker when available (default true). */
|
|
267
273
|
compression?: boolean;
|
|
268
274
|
}
|
|
269
|
-
|
|
270
|
-
* Session-replay (rrweb) recording controls. This is NOT a privacy surface —
|
|
271
|
-
* to mask sensitive content use `privacy`.
|
|
272
|
-
*/
|
|
273
|
-
interface ReplayOptions {
|
|
275
|
+
interface SDKOptions$1 {
|
|
274
276
|
/**
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* NOTE: this is a noise-reduction control, NOT a privacy control. To mask
|
|
281
|
-
* sensitive content use `privacy` instead.
|
|
277
|
+
* Which signal listeners to enable, by name. Empty array = all. The NAMES
|
|
278
|
+
* are platform-defined (each platform ships its own listener set) — the
|
|
279
|
+
* platform package narrows this to its literal union (see the browser
|
|
280
|
+
* package's `config/types.ts`).
|
|
282
281
|
*/
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
interface SDKOptions {
|
|
286
|
-
enabledEvents: Array<"dom" | "network" | "exceptions" | "logs" | "pageload" | "navigation" | "performance" | "replay">;
|
|
282
|
+
enabledEvents: string[];
|
|
287
283
|
eventSampleRate: number;
|
|
288
284
|
sessionSampleRate: number;
|
|
289
285
|
debug: boolean;
|
|
290
286
|
excludedUrls?: Array<string | RegExp>;
|
|
291
|
-
beforeSend?: (event: EventTypes[keyof EventTypes]) => boolean;
|
|
292
|
-
enrichEvent?: (event: EventTypes[keyof EventTypes]) => EventTypes[keyof EventTypes];
|
|
287
|
+
beforeSend?: (event: EventTypes$1[keyof EventTypes$1]) => boolean;
|
|
288
|
+
enrichEvent?: (event: EventTypes$1[keyof EventTypes$1]) => EventTypes$1[keyof EventTypes$1];
|
|
293
289
|
/**
|
|
294
290
|
* Unified privacy / data-masking config driving replay + non-replay masking.
|
|
295
291
|
* ON by default (`level: 'mask-sensitive'`); see {@link PrivacyOptions}.
|
|
296
292
|
*/
|
|
297
|
-
privacy?: PrivacyOptions;
|
|
293
|
+
privacy?: PrivacyOptions$1;
|
|
298
294
|
/** Distributed-tracing header propagation; see {@link TracingOptions}. */
|
|
299
295
|
tracing?: TracingOptions;
|
|
300
296
|
/** Batching & delivery of outgoing events; see {@link TransportOptions}. */
|
|
301
297
|
transport?: TransportOptions;
|
|
302
298
|
/**
|
|
303
|
-
* Session-
|
|
304
|
-
*
|
|
299
|
+
* Session-recording controls — a platform feature slot core never reads.
|
|
300
|
+
* The platform package types it (the browser SDK narrows this to its
|
|
301
|
+
* rrweb `ReplayOptions`; platforms without recording ignore it).
|
|
305
302
|
*/
|
|
306
|
-
replay?:
|
|
303
|
+
replay?: unknown;
|
|
307
304
|
/**
|
|
308
305
|
* Target maximum wall-clock duration of a session, in milliseconds.
|
|
309
306
|
*
|
|
@@ -343,7 +340,7 @@ interface SDKOptions {
|
|
|
343
340
|
*/
|
|
344
341
|
sessionMaxDuration?: number;
|
|
345
342
|
}
|
|
346
|
-
interface SDKConfigInput {
|
|
343
|
+
interface SDKConfigInput$1 {
|
|
347
344
|
apiKey: string;
|
|
348
345
|
cluster: string;
|
|
349
346
|
environment: string;
|
|
@@ -352,7 +349,7 @@ interface SDKConfigInput {
|
|
|
352
349
|
namespace?: string;
|
|
353
350
|
releaseId?: string;
|
|
354
351
|
user?: Partial<UserIdentifiers>;
|
|
355
|
-
options?: Partial<SDKOptions>;
|
|
352
|
+
options?: Partial<SDKOptions$1>;
|
|
356
353
|
sessionId?: string;
|
|
357
354
|
}
|
|
358
355
|
|
|
@@ -365,9 +362,9 @@ declare class SDKConfig {
|
|
|
365
362
|
releaseId?: string;
|
|
366
363
|
apiKey: string;
|
|
367
364
|
user: Partial<UserIdentifiers> | null;
|
|
368
|
-
options: Partial<SDKOptions>;
|
|
369
|
-
providedOptions?: Partial<SDKOptions>;
|
|
370
|
-
constructor(input?: SDKConfigInput);
|
|
365
|
+
options: Partial<SDKOptions$1>;
|
|
366
|
+
providedOptions?: Partial<SDKOptions$1>;
|
|
367
|
+
constructor(input?: SDKConfigInput$1);
|
|
371
368
|
private static readonly REMOVED_OPTION_KEYS;
|
|
372
369
|
private static stripRemovedOptions;
|
|
373
370
|
private static warnRemovedOptions;
|
|
@@ -376,13 +373,106 @@ declare class SDKConfig {
|
|
|
376
373
|
updateConfig(updates: Partial<SDKConfig>): void;
|
|
377
374
|
}
|
|
378
375
|
|
|
376
|
+
/**
|
|
377
|
+
* The view an event was captured on. Browser-owned: core has no notion of a
|
|
378
|
+
* "current view" — it merges whatever the platform's `enrichAttributes`
|
|
379
|
+
* contributor returns. `makeWebViewProvider` derives these (hash-route
|
|
380
|
+
* detection, query-param redaction) and the SessionManager contributes them
|
|
381
|
+
* under the `location` key.
|
|
382
|
+
*/
|
|
383
|
+
type LocationAttributes = {
|
|
384
|
+
path: string;
|
|
385
|
+
url: string;
|
|
386
|
+
title: string;
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* The browser's event shape. Identical to core's `Event` plus the `location`
|
|
390
|
+
* this platform stamps during enrichment — which is why `beforeSend` /
|
|
391
|
+
* `enrichEvent` below are re-declared over THIS map rather than core's.
|
|
392
|
+
*/
|
|
393
|
+
type Event<T extends string, A> = Omit<BaseEvent<T>, "attributes"> & {
|
|
394
|
+
type: T;
|
|
395
|
+
attributes: A & {
|
|
396
|
+
location?: LocationAttributes;
|
|
397
|
+
};
|
|
398
|
+
};
|
|
399
|
+
type EventTypes = {
|
|
400
|
+
"dom.event": Event<"dom.event", DomEventAttributes>;
|
|
401
|
+
exception: Event<"exception", ErrorEventAttributes>;
|
|
402
|
+
network: Event<"network", NetworkEventAttributes>;
|
|
403
|
+
log: Event<"log", LogEventAttributes>;
|
|
404
|
+
pageload: Event<"pageload", PageLoadEventAttributes>;
|
|
405
|
+
navigation: Event<"navigation", NavigationEventAttributes>;
|
|
406
|
+
custom: Event<"custom", UserDefinedEventAttributes>;
|
|
407
|
+
performance: Event<"performance", PerformanceEventAttributes>;
|
|
408
|
+
replay: Event<"replay", ReplayEventAttributes>;
|
|
409
|
+
};
|
|
410
|
+
/** The browser SDK's listener names for `enabledEvents`. */
|
|
411
|
+
type BrowserEventName = "dom" | "network" | "exceptions" | "logs" | "pageload" | "navigation" | "performance" | "replay";
|
|
412
|
+
/**
|
|
413
|
+
* Session-replay (rrweb) recording controls. This is NOT a privacy surface —
|
|
414
|
+
* to mask sensitive content use `privacy`.
|
|
415
|
+
*/
|
|
416
|
+
interface ReplayOptions {
|
|
417
|
+
/**
|
|
418
|
+
* CSS selectors whose matching elements (and their subtrees) are excluded
|
|
419
|
+
* from session recording. Matching elements appear in the replay as empty
|
|
420
|
+
* placeholders of their original size. Useful for skipping noisy DOM injected
|
|
421
|
+
* by browser extensions (e.g. Grammarly).
|
|
422
|
+
*
|
|
423
|
+
* NOTE: this is a noise-reduction control, NOT a privacy control. To mask
|
|
424
|
+
* sensitive content use `privacy` instead.
|
|
425
|
+
*/
|
|
426
|
+
blockedSelectors?: string[];
|
|
427
|
+
}
|
|
428
|
+
/** Session-recording (rrweb replay) masking — the only place replay can be masked. */
|
|
429
|
+
interface ReplayMaskingOptions {
|
|
430
|
+
/** rrweb `maskTextFn` pass-through. */
|
|
431
|
+
maskTextFn?: (text: string, el: HTMLElement | null) => string;
|
|
432
|
+
/** rrweb `maskInputFn` pass-through. */
|
|
433
|
+
maskInputFn?: (text: string, el: HTMLElement) => string;
|
|
434
|
+
}
|
|
435
|
+
interface PrivacyOptions extends Omit<PrivacyOptions$1, "replay"> {
|
|
436
|
+
/** CSS selectors whose text/inputs are always masked (replay + DOM), regardless of `level` except `'allow'`. */
|
|
437
|
+
maskSelectors?: string[];
|
|
438
|
+
/** Session-recording (rrweb replay) masking — the only place replay can be masked. */
|
|
439
|
+
replay?: ReplayMaskingOptions;
|
|
440
|
+
}
|
|
441
|
+
interface SDKOptions extends Omit<SDKOptions$1, "privacy" | "replay" | "enabledEvents" | "beforeSend" | "enrichEvent"> {
|
|
442
|
+
/**
|
|
443
|
+
* Inspect each event before it is queued; return false to drop it. Typed
|
|
444
|
+
* over the BROWSER event map, so `attributes.location` is visible here.
|
|
445
|
+
*/
|
|
446
|
+
beforeSend?: (event: EventTypes[keyof EventTypes]) => boolean;
|
|
447
|
+
/** Modify each event before it is queued. Typed over the browser event map. */
|
|
448
|
+
enrichEvent?: (event: EventTypes[keyof EventTypes]) => EventTypes[keyof EventTypes];
|
|
449
|
+
/** Which browser listeners to enable. Empty array = all. */
|
|
450
|
+
enabledEvents: BrowserEventName[];
|
|
451
|
+
/**
|
|
452
|
+
* Unified privacy / data-masking config driving replay + non-replay masking.
|
|
453
|
+
* ON by default (`level: 'mask-sensitive'`); see {@link PrivacyOptions}.
|
|
454
|
+
*/
|
|
455
|
+
privacy?: PrivacyOptions;
|
|
456
|
+
/**
|
|
457
|
+
* Session-replay recording controls (noise reduction). For replay MASKING use
|
|
458
|
+
* `privacy` instead. See {@link ReplayOptions}.
|
|
459
|
+
*/
|
|
460
|
+
replay?: ReplayOptions;
|
|
461
|
+
}
|
|
462
|
+
interface SDKConfigInput extends Omit<SDKConfigInput$1, "options"> {
|
|
463
|
+
options?: Partial<SDKOptions>;
|
|
464
|
+
}
|
|
465
|
+
|
|
379
466
|
declare class SessionManager {
|
|
380
|
-
private initialized;
|
|
381
467
|
private logger;
|
|
382
|
-
private
|
|
468
|
+
private readonly storage;
|
|
383
469
|
private idGenerator;
|
|
470
|
+
private core;
|
|
471
|
+
private hideWired;
|
|
472
|
+
private hideHandler;
|
|
384
473
|
constructor(config: SDKConfigInput);
|
|
385
|
-
private
|
|
474
|
+
private wireHideHandler;
|
|
475
|
+
private unwireHideHandler;
|
|
386
476
|
identifyUser(userIdentifier: Partial<UserIdentifiers>): void;
|
|
387
477
|
updateConfig(params: Partial<SDKConfig>): void;
|
|
388
478
|
sendCustomEvent(event: {
|