Skip to content

Commit 29d090c

Browse files
authored
feat(instrumentation-replay): add experimental replay support (#1417)
1 parent 8838230 commit 29d090c

17 files changed

+815
-1
lines changed

experimental/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Next
44

5+
- Added support for experimental `ReplayInstrumentation` (`@grafana/faro-instrumentation-replay`).
6+
57
## 1.13.0
68

79
- Improvement (`@grafana/faro-*`) Add required Node engines to package.json ()
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# @grafana/faro-instrumentation-replay
2+
3+
Faro instrumentation for session replay with rrweb.
4+
5+
## Installation
6+
7+
```bash
8+
npm install @grafana/faro-instrumentation-replay
9+
```
10+
11+
## Usage
12+
13+
```typescript
14+
import { ReplayInstrumentation } from '@grafana/faro-instrumentation-replay';
15+
import { getWebInstrumentations, initializeFaro } from '@grafana/faro-web-sdk';
16+
17+
initializeFaro({
18+
url: 'https://your-faro-endpoint.com',
19+
instrumentations: [
20+
...getWebInstrumentations(),
21+
new ReplayInstrumentation({
22+
maskInputOptions: {
23+
password: true,
24+
email: true,
25+
},
26+
maskAllInputs: false,
27+
recordCrossOriginIframes: false,
28+
}),
29+
],
30+
});
31+
```
32+
33+
## Configuration Options
34+
35+
### Privacy & Masking Options
36+
37+
- **`maskAllInputs`** (default: `false`): Whether to mask all input elements
38+
- **`maskInputOptions`** (default: `{ password: true }`): Fine-grained control over which input types to mask.
39+
Available options:
40+
- `password` - Password inputs
41+
- `text` - Text inputs
42+
- `email` - Email inputs
43+
- `tel` - Telephone inputs
44+
- `number` - Number inputs
45+
- `search` - Search inputs
46+
- `url` - URL inputs
47+
- `date`, `datetime-local`, `month`, `week`, `time` - Date/time inputs
48+
- `color` - Color inputs
49+
- `range` - Range inputs
50+
- `textarea` - Textarea elements
51+
- `select` - Select dropdowns
52+
- **`maskTextSelector`**: Custom CSS selector to mask specific elements
53+
- **`blockSelector`**: CSS selector to completely block elements from recording
54+
- **`ignoreSelector`**: CSS selector to ignore specific elements
55+
56+
### Recording Options
57+
58+
- **`recordCrossOriginIframes`** (default: `false`): Whether to record cross-origin iframes
59+
- **`recordCanvas`** (default: `false`): Whether to record canvas elements
60+
- **`collectFonts`** (default: `false`): Whether to collect font files
61+
- **`inlineImages`** (default: `false`): Whether to inline images in the recording
62+
- **`inlineStylesheet`** (default: `false`): Whether to inline stylesheets
63+
64+
### Hooks
65+
66+
- **`beforeSend`**: Custom function to transform or filter events before they are sent.
67+
Return the modified event or `null`/`undefined` to skip sending
68+
69+
## Privacy and Security
70+
71+
This instrumentation records user interactions on your website. Make sure to:
72+
73+
1. **Enable appropriate masking options** - By default, only password inputs are masked.
74+
Configure `maskInputOptions` to mask additional sensitive fields
75+
2. **Use CSS selectors** - Use `maskTextSelector` to mask sensitive content, `blockSelector` to completely exclude elements
76+
3. **Implement filtering** - Use the `beforeSend` hook to filter or transform events before sending
77+
4. **Review your privacy policy** - Ensure you have proper user consent for session recording
78+
5. **Test your configuration** - Verify no sensitive information is captured in recordings
79+
80+
### Example: Advanced Privacy Configuration
81+
82+
```typescript
83+
new ReplayInstrumentation({
84+
// Mask all text and email inputs, but allow number inputs
85+
maskInputOptions: {
86+
password: true,
87+
text: true,
88+
email: true,
89+
tel: true,
90+
textarea: true,
91+
},
92+
// Mask elements with specific CSS classes
93+
maskTextSelector: '.sensitive-data, .pii',
94+
// Block elements completely from recording
95+
blockSelector: '.payment-form, .credit-card-info',
96+
// Ignore certain elements (won't be recorded at all)
97+
ignoreSelector: '.analytics-widget',
98+
// Filter or transform events before sending
99+
beforeSend: (event) => {
100+
// Example: Skip events that might contain sensitive data
101+
if (event.type === 3 && event.data?.source === 'CanvasMutation') {
102+
return null; // Skip this event
103+
}
104+
return event; // Send the event as-is
105+
},
106+
});
107+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const { jestBaseConfig } = require('../../jest.config.base.js');
2+
3+
module.exports = {
4+
...jestBaseConfig,
5+
roots: ['experimental/instrumentation-replay/src'],
6+
testEnvironment: 'jsdom',
7+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "@grafana/faro-instrumentation-replay",
3+
"version": "2.0.2",
4+
"description": "Faro instrumentation for session replay with rrweb",
5+
"keywords": [
6+
"observability",
7+
"apm",
8+
"rum",
9+
"dem",
10+
"session-replay",
11+
"rrweb",
12+
"browser"
13+
],
14+
"license": "Apache-2.0",
15+
"author": "Grafana Labs",
16+
"homepage": "https://github.com/grafana/faro-web-sdk",
17+
"repository": {
18+
"type": "git",
19+
"url": "https://github.com/grafana/faro-web-sdk.git",
20+
"directory": "experimental/instrumentation-replay"
21+
},
22+
"main": "./dist/cjs/index.js",
23+
"module": "./dist/esm/index.js",
24+
"types": "./dist/types/index.d.ts",
25+
"files": [
26+
"dist",
27+
"README.md",
28+
"LICENSE"
29+
],
30+
"scripts": {
31+
"start": "yarn watch",
32+
"build": "run-s 'build:*'",
33+
"build:compile": "run-p 'build:compile:*'",
34+
"build:compile:cjs": "tsc --build tsconfig.cjs.json",
35+
"build:compile:esm": "tsc --build tsconfig.esm.json",
36+
"build:compile:bundle": "run-s 'build:compile:bundle:*'",
37+
"build:compile:bundle:create": "rollup -c ./rollup.config.js",
38+
"build:compile:bundle:remove-extras": "rimraf dist/bundle/dist",
39+
"watch": "run-s watch:compile",
40+
"watch:compile": "yarn build:compile:cjs -w",
41+
"clean": "rimraf dist/ yarn-error.log",
42+
"quality": "run-s 'quality:*'",
43+
"quality:test": "jest",
44+
"quality:format": "prettier --cache --cache-location=../../.cache/prettier/webSessionReplay --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"",
45+
"quality:lint": "run-s 'quality:lint:*'",
46+
"quality:lint:eslint": "eslint --cache --cache-location ../../.cache/eslint/webSessionReplay \"./**/*.{js,jsx,ts,tsx}\"",
47+
"quality:lint:prettier": "prettier --cache --cache-location=../../.cache/prettier/webSessionReplay --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"",
48+
"quality:lint:md": "markdownlint README.md",
49+
"quality:circular-deps": "madge --circular ."
50+
},
51+
"dependencies": {
52+
"@grafana/faro-core": "^2.0.2",
53+
"rrweb": "^2.0.0-alpha.18"
54+
},
55+
"publishConfig": {
56+
"access": "public"
57+
}
58+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { getRollupConfigBase } = require('../../rollup.config.base.js');
2+
3+
module.exports = getRollupConfigBase('instrumentationReplay');
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { type ReplayInstrumentationOptions } from './types';
2+
3+
export const defaultReplayInstrumentationOptions: ReplayInstrumentationOptions = {
4+
maskAllInputs: false,
5+
maskInputOptions: {
6+
password: true,
7+
},
8+
maskTextSelector: undefined,
9+
blockSelector: undefined,
10+
ignoreSelector: undefined,
11+
collectFonts: false,
12+
inlineImages: false,
13+
inlineStylesheet: false,
14+
recordCanvas: false,
15+
recordCrossOriginIframes: false,
16+
beforeSend: undefined,
17+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { ReplayInstrumentation } from './instrumentation';
2+
export type { ReplayInstrumentationOptions, MaskInputOptions } from './types';

0 commit comments

Comments
 (0)