@groundcover/browser 2.0.0 → 2.1.0-rc.1
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 +39 -0
- package/README.md +72 -2
- package/dist/index.d.mts +47 -2
- package/dist/index.d.ts +47 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,45 @@ 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
|
+
## [2.1.0-rc.1] — 2026-10-07
|
|
8
|
+
|
|
9
|
+
Second release candidate of `2.1.0`. Tunes canvas capture's default from measurements; nothing
|
|
10
|
+
changes unless `options.replay.canvas.enabled` is set.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **`replay.canvas.fps` defaults to 1** (was 4). rrweb re-encodes every canvas on every tick whether
|
|
15
|
+
or not it changed, so the rate drives CPU even on a static page, while bytes grow only when content
|
|
16
|
+
changes. On a six-chart test dashboard, 1 fps used about 12% of a CPU core against about 35% at
|
|
17
|
+
4 fps, with the same bytes. Set `fps` explicitly for animated canvases.
|
|
18
|
+
- **README: canvas capture recipes** for dashboards, animated canvases, constrained bandwidth and
|
|
19
|
+
CPU-sensitive pages, with measured costs for scale.
|
|
20
|
+
|
|
21
|
+
## [2.1.0-rc.0] — 2026-10-06
|
|
22
|
+
|
|
23
|
+
First release candidate of `2.1.0`, on top of `2.0.0`. Adds opt-in canvas capture to session replay;
|
|
24
|
+
nothing changes unless `options.replay.canvas.enabled` is set.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **Session replay can capture `<canvas>`-rendered content** (e.g. chart data series that replayed
|
|
29
|
+
as empty frames) via `options.replay.canvas`. Off by default. `enabled` maps to rrweb
|
|
30
|
+
`recordCanvas`, `fps` (default 4, clamped to 1–15 with a warning) to `sampling.canvas`, and
|
|
31
|
+
`imageType` / `imageQuality` (default `image/webp` at 0.6) to `dataURLOptions`. Canvas frames are
|
|
32
|
+
pixel snapshots: they increase replay bandwidth and page CPU, and are **not** covered by `privacy`
|
|
33
|
+
text/input masking. WebGL capture is best-effort only (the page's context must use
|
|
34
|
+
`preserveDrawingBuffer: true`).
|
|
35
|
+
- **Changing `replay.canvas` through `updateConfig` restarts the active recording**, like
|
|
36
|
+
`replay.blockedSelectors` and `privacy`. A partial update merges into the current canvas options,
|
|
37
|
+
so `{ canvas: { fps: 8 } }` keeps capture enabled.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **rrweb recorder errors stay inside the SDK.** Observer failures that fire after `record()`
|
|
42
|
+
returns are routed through the SDK's error handler instead of reaching the host page.
|
|
43
|
+
- **`updateConfig` restarts the recording only for keys the update itself sets.** An inherited
|
|
44
|
+
(non-own) `privacy` or `replay` key no longer splits the recording without changing the config.
|
|
45
|
+
|
|
7
46
|
## [2.0.0] — 2026-10-06
|
|
8
47
|
|
|
9
48
|
Stable release of the `2.0.0-rc.0` and `2.0.0-rc.1` line. **Contains breaking changes from `1.x`** —
|
package/README.md
CHANGED
|
@@ -179,17 +179,86 @@ content use [`privacy`](#privacy--data-masking).
|
|
|
179
179
|
| Option | Type | Default | Description |
|
|
180
180
|
|---|---|---|---|
|
|
181
181
|
| `blockedSelectors` | `string[]` | — | CSS selectors whose elements (and subtrees) are excluded from recording. Useful for noisy extension-injected DOM (e.g. Grammarly). |
|
|
182
|
+
| `canvas` | `ReplayCanvasOptions` | off | **Opt-in** capture of `<canvas>`-rendered content (charts, WebGL widgets). Off by default — see [Canvas capture](#canvas-capture-opt-in) for the fields and tradeoffs. |
|
|
182
183
|
|
|
183
184
|
> ⚠️ Don’t confuse `options.replay.blockedSelectors` (recording noise reduction) with
|
|
184
185
|
> `options.privacy.replay.*` (rrweb masking callbacks). They live at different levels and serve
|
|
185
186
|
> different purposes.
|
|
186
187
|
|
|
188
|
+
#### Canvas capture (opt-in)
|
|
189
|
+
|
|
190
|
+
By default rrweb only serializes the DOM/SVG around a `<canvas>`, so anything **drawn to a canvas**
|
|
191
|
+
— chart data series, WebGL scenes — is **not** recorded and replays as an empty frame. Set
|
|
192
|
+
`options.replay.canvas.enabled` to capture canvas pixels as periodic image snapshots.
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
groundcover.init({
|
|
196
|
+
// …
|
|
197
|
+
options: {
|
|
198
|
+
enabledEvents: ["replay" /* … */],
|
|
199
|
+
replay: {
|
|
200
|
+
canvas: {
|
|
201
|
+
enabled: true, // default false
|
|
202
|
+
fps: 1, // default 1, clamped to [1, 15]
|
|
203
|
+
imageType: "image/webp", // default 'image/webp' ('image/jpeg' | 'image/png')
|
|
204
|
+
imageQuality: 0.6, // default 0.6 (lossy formats only)
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
| Field | Type | Default | Maps to rrweb |
|
|
212
|
+
|---|---|---|---|
|
|
213
|
+
| `enabled` | `boolean` | `false` | `recordCanvas` |
|
|
214
|
+
| `fps` | `number` | `1` (clamped `[1, 15]`) | `sampling.canvas` |
|
|
215
|
+
| `imageType` | `'image/webp' \| 'image/jpeg' \| 'image/png'` | `'image/webp'` | `dataURLOptions.type` |
|
|
216
|
+
| `imageQuality` | `number` (0–1) | `0.6` | `dataURLOptions.quality` |
|
|
217
|
+
|
|
218
|
+
> ⚠️ **Canvas capture is expensive and off by default for good reason.** Enable it only when you
|
|
219
|
+
> knowingly accept the tradeoffs:
|
|
220
|
+
> - **Bandwidth** — every snapshot is a serialized image, so replay payloads grow substantially on
|
|
221
|
+
> canvas-heavy pages. Keep `fps` low and prefer lossy `image/webp`/`image/jpeg`.
|
|
222
|
+
> - **CPU** — every canvas on the page is re-encoded every `1/fps` seconds, changed or not, on the
|
|
223
|
+
> recorded (end-user) device. `fps` is the main CPU lever.
|
|
224
|
+
> - **Privacy** — canvas pixels **bypass `privacy` text/input masking entirely**. Whatever is drawn
|
|
225
|
+
> is captured verbatim; do not enable canvas capture on views that render sensitive data to a
|
|
226
|
+
> canvas.
|
|
227
|
+
|
|
228
|
+
> **WebGL:** capture is **best-effort and not officially supported** by this option. Snapshotting a
|
|
229
|
+
> WebGL/WebGL2 canvas only yields pixels if the page created its context with
|
|
230
|
+
> `preserveDrawingBuffer: true`; otherwise frames may come back blank. 2D canvases (the common chart
|
|
231
|
+
> case) are captured reliably.
|
|
232
|
+
|
|
233
|
+
##### Recipes
|
|
234
|
+
|
|
235
|
+
Canvas capture's cost depends on the page: how many canvases, how big they are, and how often they
|
|
236
|
+
redraw. Start from the closest recipe and adjust:
|
|
237
|
+
|
|
238
|
+
| Page | Config | Why |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| Dashboards and charts that redraw every few seconds or on interaction | `{ enabled: true }` | The 1 fps default costs the least CPU, and charts that change slower than once a second send the same bytes at any rate. |
|
|
241
|
+
| Animated canvases (live charts, maps, games) | `{ enabled: true, fps: 4, imageQuality: 0.4 }` | Smoother playback. Every changed canvas sends a full image per frame, so bytes grow with `fps` × the number of animated canvases. |
|
|
242
|
+
| Constrained bandwidth | `{ enabled: true, imageQuality: 0.3 }` plus `replay.blockedSelectors` | Lower quality shrinks each image; blocked canvases are skipped entirely. |
|
|
243
|
+
| CPU-sensitive pages (low-end devices) | `{ enabled: true, imageType: "image/png" }` | PNG encodes much faster than webp but produces about twice the bytes. |
|
|
244
|
+
|
|
245
|
+
For scale, on a test dashboard with six canvas charts on a 2× display, canvas capture added about
|
|
246
|
+
280 KB/min while the charts were static and about 1 MB/min while they refreshed every 10 s. The
|
|
247
|
+
recorder used about 12% of a CPU core at 1 fps and about 35% at 4 fps, with the same bytes. PNG at
|
|
248
|
+
4 fps used about 10%.
|
|
249
|
+
|
|
250
|
+
Changing `replay.canvas` (or `replay.blockedSelectors`) via `updateConfig` restarts the active
|
|
251
|
+
recording so the new options take effect at rrweb `record()` time. A partial `canvas` update merges
|
|
252
|
+
into the current one, so `{ canvas: { fps: 8 } }` keeps capture enabled; pass `enabled: false` to
|
|
253
|
+
turn it off.
|
|
254
|
+
|
|
187
255
|
### Updating config at runtime
|
|
188
256
|
|
|
189
257
|
`updateConfig` accepts these optional fields: `cluster`, `environment`, `namespace`, `releaseId`,
|
|
190
258
|
`dsn`, `apiKey`, `options`, and `user`. `appId` cannot be updated. `sessionId` is not accepted;
|
|
191
|
-
use the separate `setSessionId()` API. Nested groups merge one level deep (and `tracing.origin
|
|
192
|
-
`privacy.replay` one level deeper), so a partial update preserves the other keys in
|
|
259
|
+
use the separate `setSessionId()` API. Nested groups merge one level deep (and `tracing.origin`,
|
|
260
|
+
`privacy.replay` and `replay.canvas` one level deeper), so a partial update preserves the other keys in
|
|
261
|
+
the group:
|
|
193
262
|
|
|
194
263
|
```ts
|
|
195
264
|
groundcover.updateConfig({
|
|
@@ -320,6 +389,7 @@ record-time masking from your [`privacy`](#privacy--data-masking) config:
|
|
|
320
389
|
- `privacy.level` / `privacy.maskSelectors` → rrweb `maskAllInputs` + `maskTextSelector`
|
|
321
390
|
- `privacy.replay.maskTextFn` / `maskInputFn` → rrweb `maskTextFn` / `maskInputFn`
|
|
322
391
|
- `options.replay.blockedSelectors` → rrweb `blockSelector` (noise reduction)
|
|
392
|
+
- `options.replay.canvas` → rrweb `recordCanvas` + `sampling.canvas` + `dataURLOptions` (opt-in, off by default — see [Canvas capture](#canvas-capture-opt-in))
|
|
323
393
|
|
|
324
394
|
Replay recording does **not** start automatically — even when `replay` is in `enabledEvents`. You
|
|
325
395
|
must start it explicitly (e.g. after obtaining user consent):
|
package/dist/index.d.mts
CHANGED
|
@@ -324,6 +324,45 @@ type EventTypes = {
|
|
|
324
324
|
};
|
|
325
325
|
/** The browser SDK's listener names for `enabledEvents`. */
|
|
326
326
|
type BrowserEventName = "dom" | "network" | "exceptions" | "logs" | "pageload" | "navigation" | "performance" | "replay";
|
|
327
|
+
/**
|
|
328
|
+
* Opt-in canvas capture for session replay. Charts and other `<canvas>`-drawn
|
|
329
|
+
* content (2D or WebGL) are NOT recorded by default; rrweb only serializes the
|
|
330
|
+
* DOM/SVG around them, so canvas-rendered data series replay as empty frames.
|
|
331
|
+
* Enabling this makes rrweb snapshot canvas pixels.
|
|
332
|
+
*
|
|
333
|
+
* ⚠️ HIGH COST — off by default for good reason:
|
|
334
|
+
* - Bandwidth: canvas frames serialize as images, inflating replay payloads.
|
|
335
|
+
* - CPU: snapshotting canvases is costly on the recorded (customer) page.
|
|
336
|
+
* - Privacy: canvas pixels bypass text/input masking entirely — whatever is
|
|
337
|
+
* drawn is captured verbatim, regardless of the `privacy` config.
|
|
338
|
+
*/
|
|
339
|
+
interface ReplayCanvasOptions {
|
|
340
|
+
/**
|
|
341
|
+
* Enable canvas capture. Defaults to `false` (canvas is not recorded). Maps
|
|
342
|
+
* to rrweb's `recordCanvas`.
|
|
343
|
+
*/
|
|
344
|
+
enabled?: boolean;
|
|
345
|
+
/**
|
|
346
|
+
* Snapshot frame rate, in frames per second. Defaults to 1 and is clamped to
|
|
347
|
+
* [1, 15]; out-of-range values are clamped with a `console.warn`. Maps to
|
|
348
|
+
* rrweb's `sampling.canvas`. Every canvas is re-encoded on every tick, so a
|
|
349
|
+
* higher rate costs CPU even when nothing changes; it adds bytes only for
|
|
350
|
+
* canvases that change faster than the rate. Raise it for animated canvases.
|
|
351
|
+
*/
|
|
352
|
+
fps?: number;
|
|
353
|
+
/**
|
|
354
|
+
* Image format for canvas snapshots. Defaults to `'image/webp'` (much smaller
|
|
355
|
+
* than rrweb's default lossless `image/png`). Maps to rrweb
|
|
356
|
+
* `dataURLOptions.type`.
|
|
357
|
+
*/
|
|
358
|
+
imageType?: "image/webp" | "image/jpeg" | "image/png";
|
|
359
|
+
/**
|
|
360
|
+
* Lossy compression quality in [0, 1] for `image/webp` / `image/jpeg`.
|
|
361
|
+
* Defaults to 0.6 (ignored for `image/png`). Maps to rrweb
|
|
362
|
+
* `dataURLOptions.quality`.
|
|
363
|
+
*/
|
|
364
|
+
imageQuality?: number;
|
|
365
|
+
}
|
|
327
366
|
/**
|
|
328
367
|
* Session-replay (rrweb) recording controls. This is NOT a privacy surface —
|
|
329
368
|
* to mask sensitive content use `privacy`.
|
|
@@ -339,6 +378,12 @@ interface ReplayOptions {
|
|
|
339
378
|
* sensitive content use `privacy` instead.
|
|
340
379
|
*/
|
|
341
380
|
blockedSelectors?: string[];
|
|
381
|
+
/**
|
|
382
|
+
* Opt-in canvas capture (chart data series, WebGL-backed widgets). Off by
|
|
383
|
+
* default. Capturing canvas is bandwidth-, CPU-, and privacy-sensitive — see
|
|
384
|
+
* {@link ReplayCanvasOptions}.
|
|
385
|
+
*/
|
|
386
|
+
canvas?: ReplayCanvasOptions;
|
|
342
387
|
}
|
|
343
388
|
/** Session-recording (rrweb replay) masking — the only place replay can be masked. */
|
|
344
389
|
interface ReplayMaskingOptions {
|
|
@@ -369,8 +414,8 @@ interface SDKOptions extends Omit<SDKOptions$1, "privacy" | "enabledEvents" | "b
|
|
|
369
414
|
*/
|
|
370
415
|
privacy?: PrivacyOptions;
|
|
371
416
|
/**
|
|
372
|
-
* Session-replay recording controls (noise reduction
|
|
373
|
-
* `privacy` instead. See {@link ReplayOptions}.
|
|
417
|
+
* Session-replay recording controls (noise reduction, opt-in canvas capture).
|
|
418
|
+
* For replay MASKING use `privacy` instead. See {@link ReplayOptions}.
|
|
374
419
|
*/
|
|
375
420
|
replay?: ReplayOptions;
|
|
376
421
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -324,6 +324,45 @@ type EventTypes = {
|
|
|
324
324
|
};
|
|
325
325
|
/** The browser SDK's listener names for `enabledEvents`. */
|
|
326
326
|
type BrowserEventName = "dom" | "network" | "exceptions" | "logs" | "pageload" | "navigation" | "performance" | "replay";
|
|
327
|
+
/**
|
|
328
|
+
* Opt-in canvas capture for session replay. Charts and other `<canvas>`-drawn
|
|
329
|
+
* content (2D or WebGL) are NOT recorded by default; rrweb only serializes the
|
|
330
|
+
* DOM/SVG around them, so canvas-rendered data series replay as empty frames.
|
|
331
|
+
* Enabling this makes rrweb snapshot canvas pixels.
|
|
332
|
+
*
|
|
333
|
+
* ⚠️ HIGH COST — off by default for good reason:
|
|
334
|
+
* - Bandwidth: canvas frames serialize as images, inflating replay payloads.
|
|
335
|
+
* - CPU: snapshotting canvases is costly on the recorded (customer) page.
|
|
336
|
+
* - Privacy: canvas pixels bypass text/input masking entirely — whatever is
|
|
337
|
+
* drawn is captured verbatim, regardless of the `privacy` config.
|
|
338
|
+
*/
|
|
339
|
+
interface ReplayCanvasOptions {
|
|
340
|
+
/**
|
|
341
|
+
* Enable canvas capture. Defaults to `false` (canvas is not recorded). Maps
|
|
342
|
+
* to rrweb's `recordCanvas`.
|
|
343
|
+
*/
|
|
344
|
+
enabled?: boolean;
|
|
345
|
+
/**
|
|
346
|
+
* Snapshot frame rate, in frames per second. Defaults to 1 and is clamped to
|
|
347
|
+
* [1, 15]; out-of-range values are clamped with a `console.warn`. Maps to
|
|
348
|
+
* rrweb's `sampling.canvas`. Every canvas is re-encoded on every tick, so a
|
|
349
|
+
* higher rate costs CPU even when nothing changes; it adds bytes only for
|
|
350
|
+
* canvases that change faster than the rate. Raise it for animated canvases.
|
|
351
|
+
*/
|
|
352
|
+
fps?: number;
|
|
353
|
+
/**
|
|
354
|
+
* Image format for canvas snapshots. Defaults to `'image/webp'` (much smaller
|
|
355
|
+
* than rrweb's default lossless `image/png`). Maps to rrweb
|
|
356
|
+
* `dataURLOptions.type`.
|
|
357
|
+
*/
|
|
358
|
+
imageType?: "image/webp" | "image/jpeg" | "image/png";
|
|
359
|
+
/**
|
|
360
|
+
* Lossy compression quality in [0, 1] for `image/webp` / `image/jpeg`.
|
|
361
|
+
* Defaults to 0.6 (ignored for `image/png`). Maps to rrweb
|
|
362
|
+
* `dataURLOptions.quality`.
|
|
363
|
+
*/
|
|
364
|
+
imageQuality?: number;
|
|
365
|
+
}
|
|
327
366
|
/**
|
|
328
367
|
* Session-replay (rrweb) recording controls. This is NOT a privacy surface —
|
|
329
368
|
* to mask sensitive content use `privacy`.
|
|
@@ -339,6 +378,12 @@ interface ReplayOptions {
|
|
|
339
378
|
* sensitive content use `privacy` instead.
|
|
340
379
|
*/
|
|
341
380
|
blockedSelectors?: string[];
|
|
381
|
+
/**
|
|
382
|
+
* Opt-in canvas capture (chart data series, WebGL-backed widgets). Off by
|
|
383
|
+
* default. Capturing canvas is bandwidth-, CPU-, and privacy-sensitive — see
|
|
384
|
+
* {@link ReplayCanvasOptions}.
|
|
385
|
+
*/
|
|
386
|
+
canvas?: ReplayCanvasOptions;
|
|
342
387
|
}
|
|
343
388
|
/** Session-recording (rrweb replay) masking — the only place replay can be masked. */
|
|
344
389
|
interface ReplayMaskingOptions {
|
|
@@ -369,8 +414,8 @@ interface SDKOptions extends Omit<SDKOptions$1, "privacy" | "enabledEvents" | "b
|
|
|
369
414
|
*/
|
|
370
415
|
privacy?: PrivacyOptions;
|
|
371
416
|
/**
|
|
372
|
-
* Session-replay recording controls (noise reduction
|
|
373
|
-
* `privacy` instead. See {@link ReplayOptions}.
|
|
417
|
+
* Session-replay recording controls (noise reduction, opt-in canvas capture).
|
|
418
|
+
* For replay MASKING use `privacy` instead. See {@link ReplayOptions}.
|
|
374
419
|
*/
|
|
375
420
|
replay?: ReplayOptions;
|
|
376
421
|
}
|