@multiplayer-app/session-recorder-browser 1.2.1 → 1.2.2

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 CHANGED
@@ -1,101 +1,104 @@
1
- # Multiplayer Session Recorder
2
-
3
- The Multiplayer **Session Recorder** is a powerful tool that offers deep session replays with insights spanning frontend screens, platform traces, metrics, and logs. It helps your team pinpoint and resolve bugs faster by providing a complete picture of your backend system architecture. No more wasted hours combing through APM data; the Multiplayer Session Recorder does it all in one place.
4
-
5
- ## Key Features
6
-
7
- - **Reduced Inefficiencies**: Effortlessly capture the exact steps to reproduce an issue along with backend data in one click. No more hunting through scattered documentation, APM data, logs, or traces.
8
- - **Faster Cross-Team Alignment**: Engineers can share session links containing all relevant information, eliminating the need for long tickets or clarifying issues through back-and-forth communication.
9
- - **Uninterrupted Deep Work**: All system information—from architecture diagrams to API designs—is consolidated in one place. Minimize context switching and stay focused on what matters.
10
-
11
- ## Getting Started
1
+ ![Description](../../docs/img/header-js.png)
2
+
3
+ <div align="center">
4
+ <a href="https://github.com/multiplayer-app/multiplayer-session-recorder-javascript">
5
+ <img src="https://img.shields.io/github/stars/multiplayer-app/multiplayer-session-recorder-javascript?style=social&label=Star&maxAge=2592000" alt="GitHub stars">
6
+ </a>
7
+ <a href="https://github.com/multiplayer-app/multiplayer-session-recorder-javascript/blob/main/LICENSE">
8
+ <img src="https://img.shields.io/github/license/multiplayer-app/multiplayer-session-recorder-javascript" alt="License">
9
+ </a>
10
+ <a href="https://multiplayer.app">
11
+ <img src="https://img.shields.io/badge/Visit-multiplayer.app-blue" alt="Visit Multiplayer">
12
+ </a>
13
+
14
+ </div>
15
+ <div>
16
+ <p align="center">
17
+ <a href="https://x.com/trymultiplayer">
18
+ <img src="https://img.shields.io/badge/Follow%20on%20X-000000?style=for-the-badge&logo=x&logoColor=white" alt="Follow on X" />
19
+ </a>
20
+ <a href="https://www.linkedin.com/company/multiplayer-app/">
21
+ <img src="https://img.shields.io/badge/Follow%20on%20LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" alt="Follow on LinkedIn" />
22
+ </a>
23
+ <a href="https://discord.com/invite/q9K3mDzfrx">
24
+ <img src="https://img.shields.io/badge/Join%20our%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Join our Discord" />
25
+ </a>
26
+ </p>
27
+ </div>
28
+
29
+ # Multiplayer Full Stack Session Recorder
30
+
31
+ The Multiplayer Full Stack Session Recorder is a powerful tool that offers deep session replays with insights spanning frontend screens, platform traces, metrics, and logs. It helps your team pinpoint and resolve bugs faster by providing a complete picture of your backend system architecture. No more wasted hours combing through APM data; the Multiplayer Full Stack Session Recorder does it all in one place.
32
+
33
+ ## What you get
34
+
35
+ - Full stack replays: browser screen recording correlated with OTLP traces and logs
36
+ - One‑click shareable sessions: Engineers can share session links containing all relevant information, eliminating the need for long tickets or clarifying issues through back-and-forth communication.
37
+ - Privacy by default: input/text masking and trace payload/header masking
38
+ - Flexible: works with any web app; Node SDK for backend correlation
39
+ - Lightweight widget: start/pause/stop/save controls for your users or QA
12
40
 
13
41
  ### Installation
14
42
 
15
- You can install the Multiplayer Session Recorder using npm or yarn:
16
-
17
43
  ```bash
18
- npm install @multiplayer-app/session-recorder-browser
44
+ npm i @multiplayer-app/session-recorder-browser @opentelemetry/api
19
45
  # or
20
- yarn add @multiplayer-app/session-recorder-browser
46
+ yarn add @multiplayer-app/session-recorder-browser @opentelemetry/api
21
47
  ```
22
48
 
23
- ### Basic Setup
24
-
25
- To initialize the Multiplayer Session Recorder in your application, follow the steps below.
26
-
27
- #### Import the Session Recorder
49
+ ## Set up web client:
28
50
 
51
+ ### Quick start
29
52
  ```javascript
30
53
  import SessionRecorder from '@multiplayer-app/session-recorder-browser'
31
- ```
32
-
33
- #### Initialization
34
54
 
35
- Use the following code to initialize the session recorder with your application details:
36
-
37
- ```javascript
38
55
  SessionRecorder.init({
39
- version: '{YOUR_APPLICATION_VERSION}',
40
- application: '{YOUR_APPLICATION_NAME}',
41
- environment: '{YOUR_APPLICATION_ENVIRONMENT}',
42
- apiKey: '{YOUR_API_KEY}'
56
+ application: 'my-web-app',
57
+ version: '1.0.0',
58
+ environment: 'production',
59
+ apiKey: '<YOUR_FRONTEND_OTEL_TOKEN>',
60
+ // IMPORTANT: in order to propagate OTLP headers to a backend
61
+ // domain(s) with a different origin, add backend domain(s) below.
62
+ // e.g. if you serve your website from www.example.com
63
+ // and your backend domain is at api.example.com set value as shown below:
64
+ // format: string|RegExp|Array
65
+ // propagateTraceHeaderCorsUrls: [new RegExp('https://api.example.com', 'i')],
43
66
  })
44
- ```
45
67
 
46
- Replace the placeholders with your application’s version, name, environment, and API key (OpenTelemetry Frontend Token).
47
68
 
48
- #### Add User attributes
49
-
50
- To track user-specific attributes in session replays, add the following:
51
-
52
- ```javascript
69
+ // add any key value pairs which should be associated with a session
53
70
  SessionRecorder.setSessionAttributes({
54
- userId: '{userId}',
55
- userName: '{userName}'
71
+ userId: '12345',
72
+ userName: 'Jane Doe',
56
73
  })
57
- ```
58
-
59
- Replace the placeholders with the actual user information (e.g., user ID and username).
60
-
61
- ## Dependencies
62
-
63
- This library relies on the following packages:
64
74
 
65
- - **[rrweb](https://github.com/rrweb-io/rrweb)**: Provides the frontend session replay functionality, recording the user’s interactions with the app.
66
- - **[OpenTelemetry](https://opentelemetry.io/)**: Used to capture backend traces, metrics, and logs that integrate seamlessly with the session replays for comprehensive debugging.
67
-
68
- ## Configuration Options
69
-
70
- The Session Recorder supports various configuration options with sensible defaults:
71
-
72
- ### Default Values
73
-
74
- - `showWidget`: `true` - Show the recording widget by default
75
- - `recordCanvas`: `false` - Disable canvas recording by default
76
- - `docTraceRatio`: `0.15` - 15% of traces for auto-documentation
77
- - `sampleTraceRatio`: `0.15` - 15% sampling ratio
78
- - `schemifyDocSpanPayload`: `true` - Enable payload schematization
79
- - `maxCapturingHttpPayloadSize`: `100000` - 100KB max payload size
80
- - `usePostMessageFallback`: `false` - Disable post message fallback
81
- - `widgetButtonPlacement`: `'bottom-right'` - Default widget position
82
- - `masking.maskAllInputs`: `true` - Mask all inputs by default
83
- - `masking.isMaskingEnabled`: `true` - Enable masking for debug span payload by default
84
- - `captureBody`: `true` - Capture body in traces by default
85
- - `captureHeaders`: `true` - Capture headers in traces by default
75
+ // optionally control via API (widget is enabled by default)
76
+ // if you're not using widget (see: `showWidget: true/false`)
77
+ // then you can programatically control the session recorder
78
+ // by using the methods below
79
+ SessionRecorder.start()
80
+ SessionRecorder.pause()
81
+ SessionRecorder.resume()
82
+ SessionRecorder.stop('Finished session') // optional: pass reason for stopping the session
83
+ ```
86
84
 
87
- ## Example Usage
85
+ ### Advanced config
88
86
 
89
87
  ```javascript
90
88
  import SessionRecorder from '@multiplayer-app/debugger-browser'
91
89
 
92
90
  SessionRecorder.init({
93
- version: '1.0.0',
94
- application: 'my-app',
91
+ version: '1.0.0', // optional: version of your application
92
+ application: 'my-app', // name of your application
95
93
  environment: 'production',
96
- apiKey: 'your-api-key',
97
- showWidget: true,
98
- recordCanvas: true,
94
+ apiKey: 'your-api-key', // replace with your Multiplayer OTLP key
95
+
96
+ apiBaseUrl: 'https://api.multiplayer.app', // override API base URL if needed
97
+ exporterEndpoint: 'https://otlp.multiplayer.app', // override OTLP collector URL if needed
98
+
99
+ showWidget: true, // show in‑app recording widget (default: true)
100
+ recordCanvas: true, // record canvas elements (default: false)
101
+ // Add domains to not capture OTLP data in the session recording
99
102
  ignoreUrls: [
100
103
  /https:\/\/domain\.to\.ignore\/.*/, // can be regex or string
101
104
  /https:\/\/another\.domain\.to\.ignore\/.*/
@@ -105,31 +108,48 @@ SessionRecorder.init({
105
108
  new RegExp('https://your.backend.api.domain', 'i'), // can be regex or string
106
109
  new RegExp('https://another.backend.api.domain', 'i')
107
110
  ],
108
- docTraceRatio: 0.15, // 15% of traces will be sent for auto-documentation
109
- sampleTraceRatio: 0.15, // 15% sampling ratio
110
- schemifyDocSpanPayload: true,
111
+
112
+ // sample trace ratio used when session recording is not active.
113
+ // configures what percentage (0.00-1.00) of OTLP data
114
+ // should be sent through `exporters`
115
+ sampleTraceRatio: 0,
116
+
117
+ // optional: exporters allow you to send
118
+ // OTLP data to observability platforms
119
+ exporters: [
120
+ // example:
121
+ // import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
122
+ // new OTLPTraceExporter({
123
+ // url: '<opentelemetry-collector-url>',
124
+ // })
125
+ ],
126
+
127
+ captureBody: true, // capture request/response content
128
+ captureHeaders: true, // capture request/response header content
129
+
130
+ // set the maximum request/response content size (in bytes) that will be captured
131
+ // any request/response content greater than size will be not included in session recordings
111
132
  maxCapturingHttpPayloadSize: 100000,
112
- usePostMessageFallback: false, // Enable post message fallback if needed
113
- exporterApiBaseUrl: 'https://api.multiplayer.app', // Custom API base URL (optional)
114
- captureBody: true, // Capture body in traces
115
- captureHeaders: true, // Capture headers in traces
116
- // Configure masking for sensitive data in session recordings
133
+
134
+ // configure masking for sensitive data in session recordings
117
135
  masking: {
118
- maskAllInputs: true, // Masks all input fields by default
136
+ maskAllInputs: false, // masks all input fields
119
137
  maskInputOptions: {
120
- password: true, // Always mask password fields
121
- email: false, // Don't mask email fields by default
122
- tel: false, // Don't mask telephone fields by default
123
- number: false, // Don't mask number fields by default
124
- url: false, // Don't mask URL fields by default
125
- search: false, // Don't mask search fields by default
126
- textarea: false // Don't mask textarea elements by default
138
+ password: true, // mask password fields
139
+ email: false, // mask email fields
140
+ tel: false, // mask telephone fields
141
+ number: false, // mask number fields
142
+ url: false, // mask URL fields
143
+ search: false, // mask search fields
144
+ textarea: false // mask textarea elements
127
145
  },
128
- // Class-based masking
129
- maskTextClass: /sensitive|private/, // Mask text in elements with these classes
146
+
147
+ // class-based masking
148
+ maskTextClass: /sensitive|private/, // mask text in elements with these classes
130
149
  // CSS selector for text masking
131
- maskTextSelector: '.sensitive-data', // Mask text in elements matching this selector
132
- // Custom masking functions
150
+ maskTextSelector: '.sensitive-data', // mask text in elements matching this selector
151
+
152
+ // custom masking functions
133
153
  maskInput: (text, element) => {
134
154
  if (element.classList.contains('credit-card')) {
135
155
  return '****-****-****-' + text.slice(-4)
@@ -144,417 +164,70 @@ SessionRecorder.init({
144
164
  return '***MASKED***'
145
165
  },
146
166
  maskConsoleEvent: (payload) => {
147
- // Custom console event masking
148
167
  if (payload && payload.payload && payload.payload.args) {
149
- // Mask sensitive console arguments
168
+ // mask sensitive console arguments
150
169
  payload.payload.args = payload.payload.args.map((arg) =>
151
170
  typeof arg === 'string' && arg.includes('password') ? '***MASKED***' : arg
152
171
  )
153
172
  }
154
173
  return payload
155
174
  },
156
- isMaskingEnabled: true, // Enable masking for debug span payload in traces
175
+
176
+ isContentMaskingEnabled: true, // enable content masking in session recordings
157
177
  maskBody: (payload, span) => {
158
- // Custom trace payload masking
178
+ // note: `payload` is already a copy of the original request/response content
159
179
  if (payload && typeof payload === 'object') {
160
- const maskedPayload = { ...payload }
161
- // Mask sensitive trace data
162
- if (maskedPayload.requestHeaders) {
163
- maskedPayload.requestHeaders = '***MASKED***'
180
+ // mask sensitive data
181
+ if (payload.requestHeaders) {
182
+ payload.requestHeaders = '***MASKED***'
164
183
  }
165
- if (maskedPayload.responseBody) {
166
- maskedPayload.responseBody = '***MASKED***'
184
+ if (payload.responseBody) {
185
+ payload.responseBody = '***MASKED***'
167
186
  }
168
- return maskedPayload
169
187
  }
170
188
  return payload
171
189
  },
172
190
  maskHeaders: (headers, span) => {
173
- // Custom headers masking
191
+ // note: `headers` is already a copy of the original request/response content
174
192
  if (headers && typeof headers === 'object') {
175
- const maskedHeaders = { ...headers }
176
- // Mask sensitive headers
177
- if (maskedHeaders.authorization) {
178
- maskedHeaders.authorization = '***MASKED***'
193
+ // mask sensitive headers
194
+ if (headers.authorization) {
195
+ headers.authorization = '***MASKED***'
179
196
  }
180
- if (maskedHeaders.cookie) {
181
- maskedHeaders.cookie = '***MASKED***'
197
+ if (headers.cookie) {
198
+ headers.cookie = '***MASKED***'
182
199
  }
183
- return maskedHeaders
184
200
  }
185
201
  return headers
186
202
  },
187
- // List of body fields to mask in traces
203
+ // list of field names to mask in request/response content
188
204
  maskBodyFieldsList: ['password', 'token', 'secret'],
189
- // List of headers to mask in traces
205
+ // list of headers to mask in request/response headers
190
206
  maskHeadersList: ['authorization', 'cookie', 'x-api-key'],
191
- // List of headers to include in traces (if specified, only these headers will be captured)
207
+ // list of headers to capture. An empty array will capture all headers
192
208
  headersToInclude: ['content-type', 'user-agent'],
193
- // List of headers to exclude from traces
209
+ // list of headers to exclude from capturing
194
210
  headersToExclude: ['authorization', 'cookie']
195
- }
196
- })
197
-
198
- SessionRecorder.setSessionAttributes({
199
- userId: '12345',
200
- userName: 'John Doe'
201
- })
202
- ```
203
-
204
- ## API Methods
205
-
206
- The Session Recorder provides several methods for controlling session recording:
207
-
208
- ### Session Control
209
-
210
- - `SessionRecorder.start(type?, session?)` - Start a new session with optional existing session
211
- - `type`: Optional `SessionType.PLAIN` or `SessionType.CONTINUOUS`, default: `SessionType.PLAIN`
212
- - `session`: Optional existing session object
213
- - `SessionRecorder.stop(comment?)` - Stop the current session with optional comment
214
- - `SessionRecorder.pause()` - Pause the current session
215
- - `SessionRecorder.resume()` - Resume the current session
216
- - `SessionRecorder.cancel()` - Cancel the current session
217
- - `SessionRecorder.save()` - Save the continuous recording session
218
-
219
- ### Configuration
220
-
221
- - `SessionRecorder.setSessionAttributes(attributes)` - Set session metadata
222
- - `SessionRecorder.recordingButtonClickHandler = handler` - Set custom click handler
223
-
224
- ### Properties
225
-
226
- - `SessionRecorder.sessionId` - Get current session ID (readonly)
227
- - `SessionRecorder.sessionType` - Get current session type (readonly)
228
- - `SessionRecorder.sessionState` - Get current session state (readonly)
229
- - `SessionRecorder.session` - Get current session object (readonly)
230
- - `SessionRecorder.sessionAttributes` - Get current session attributes (readonly)
231
- - `SessionRecorder.error` - Get/set error message
232
- - `SessionRecorder.sessionWidgetButtonElement` - Get the widget button element (readonly)
233
-
234
- ### Session Types
235
-
236
- - `SessionType.PLAIN` - Standard session recording
237
- - `SessionType.CONTINUOUS` - Continuous recording session
238
-
239
- ### Session States
240
-
241
- - `SessionState.started` - Session is currently recording
242
- - `SessionState.paused` - Session is paused
243
- - `SessionState.stopped` - Session is stopped
244
-
245
- ### Session Attributes
246
-
247
- You can set various session attributes for better tracking:
248
-
249
- ```javascript
250
- SessionRecorder.setSessionAttributes({
251
- userId: '12345',
252
- userName: 'John Doe',
253
- userEmail: 'john@example.com',
254
- accountId: 'acc_123',
255
- accountName: 'Enterprise Account'
256
- })
257
- ```
258
-
259
- ## Masking Configuration
260
-
261
- The Session Recorder includes comprehensive masking options to protect sensitive data during session recordings. You can configure masking behavior through the `masking` option:
262
-
263
- ### Basic Masking Options
264
-
265
- - `maskAllInputs`: If `true`, masks all input fields in the recording (default: `true`)
266
- - `isMaskingEnabled`: If `true`, enables masking for debug span payload in traces (default: `true`)
267
-
268
- ### Input Type Masking
269
-
270
- You can control masking for specific input types:
271
-
272
- ```javascript
273
- maskInputOptions: {
274
- password: true, // Always mask password fields (default: true)
275
- email: false, // Don't mask email fields by default
276
- tel: false, // Don't mask telephone fields by default
277
- number: false, // Don't mask number fields by default
278
- url: false, // Don't mask URL fields by default
279
- search: false, // Don't mask search fields by default
280
- textarea: false, // Don't mask textarea elements by default
281
- select: false, // Don't mask select elements by default
282
- // ...other types
283
- }
284
- ```
285
-
286
- ### CSS Selector Masking
287
-
288
- You can mask specific elements using CSS selectors:
289
-
290
- ```javascript
291
- masking: {
292
- // Mask text in elements matching this selector
293
- maskTextSelector: '.sensitive-data, [data-private="true"], .user-profile .email',
294
- }
295
- ```
296
-
297
- ### Class-Based Masking
298
-
299
- You can mask text based on CSS classes using string or RegExp patterns:
300
-
301
- ```javascript
302
- masking: {
303
- maskTextClass: 'sensitive', // Mask text in elements with class 'sensitive'
304
- }
305
- ```
306
-
307
- Or with RegExp pattern:
308
-
309
- ```javascript
310
- masking: {
311
- maskTextClass: /private|confidential/, // Mask text in elements with classes 'private' or 'confidential'
312
- }
313
- ```
314
-
315
- ### Custom Masking Functions
316
-
317
- For advanced masking scenarios, you can provide custom functions:
318
-
319
- ```javascript
320
- masking: {
321
- // Custom function for input masking
322
- maskInput: (text, element) => {
323
- // Custom logic to mask input text
324
- if (element.classList.contains('credit-card')) {
325
- return '****-****-****-' + text.slice(-4);
326
- }
327
- return '***MASKED***';
328
- },
329
-
330
- // Custom function for text masking
331
- maskText: (text, element) => {
332
- // Custom logic to mask text content
333
- if (element.dataset.type === 'email') {
334
- const [local, domain] = text.split('@');
335
- return local.charAt(0) + '***@' + domain;
336
- }
337
- return '***MASKED***';
338
- },
339
-
340
- // Custom function for masking body in traces
341
- maskBody: (payload, span) => {
342
- // Custom logic to mask sensitive data in trace payloads
343
- if (payload && typeof payload === 'object') {
344
- const maskedPayload = { ...payload };
345
- // Mask sensitive fields
346
- if (maskedPayload.headers) {
347
- maskedPayload.headers = '***MASKED***';
348
- }
349
- if (maskedPayload.body) {
350
- maskedPayload.body = '***MASKED***';
351
- }
352
- return maskedPayload;
353
- }
354
- return payload;
355
- },
356
- // Custom function for masking headers in traces
357
- maskHeaders: (headers, span) => {
358
- // Custom logic to mask sensitive headers
359
- if (headers && typeof headers === 'object') {
360
- const maskedHeaders = { ...headers };
361
- // Mask sensitive headers
362
- if (maskedHeaders.authorization) {
363
- maskedHeaders.authorization = '***MASKED***';
364
- }
365
- if (maskedHeaders.cookie) {
366
- maskedHeaders.cookie = '***MASKED***';
367
- }
368
- return maskedHeaders;
369
- }
370
- return headers;
371
211
  },
372
- }
373
- ```
374
-
375
- ### Example: Comprehensive Masking Setup
376
-
377
- ```javascript
378
- SessionRecorder.init({
379
- // ... other options
380
- masking: {
381
- maskAllInputs: true,
382
- maskInputOptions: {
383
- password: true,
384
- email: true, // Mask email fields for privacy
385
- tel: true, // Mask telephone fields for privacy
386
- number: false, // Allow number fields
387
- url: false, // Allow URL fields
388
- search: false, // Allow search fields
389
- textarea: false // Allow textarea elements
390
- // ...other types
391
- },
392
- maskTextClass: /sensitive|private|confidential/, // Mask text in elements with these classes
393
- maskTextSelector: '.user-email, .user-phone, .credit-card, [data-sensitive="true"]', // Mask text in elements matching this selector
394
- maskInput: (text, element) => {
395
- // Custom credit card masking
396
- if (element.classList.contains('credit-card')) {
397
- return '****-****-****-' + text.slice(-4)
398
- }
399
- return '***MASKED***'
400
- },
401
- maskText: (text, element) => {
402
- // Custom email masking
403
- if (element.dataset.type === 'email') {
404
- const [local, domain] = text.split('@')
405
- return local.charAt(0) + '***@' + domain
406
- }
407
- return '***MASKED***'
408
- },
409
- maskConsoleEvent: (payload) => {
410
- // Custom console event masking
411
- if (payload && payload.payload && payload.payload.args) {
412
- payload.payload.args = payload.payload.args.map((arg) =>
413
- typeof arg === 'string' && arg.includes('password') ? '***MASKED***' : arg
414
- )
415
- }
416
- return payload
417
- },
418
- isMaskingEnabled: true, // Enable masking for debug span payload in traces
419
- maskBody: (payload, span) => {
420
- // Custom trace payload masking
421
- if (payload && typeof payload === 'object') {
422
- const maskedPayload = { ...payload }
423
- // Mask sensitive trace data
424
- if (maskedPayload.requestHeaders) {
425
- maskedPayload.requestHeaders = '***MASKED***'
426
- }
427
- if (maskedPayload.responseBody) {
428
- maskedPayload.responseBody = '***MASKED***'
429
- }
430
- return maskedPayload
431
- }
432
- return payload
433
- },
434
- maskHeaders: (headers, span) => {
435
- // Custom headers masking
436
- if (headers && typeof headers === 'object') {
437
- const maskedHeaders = { ...headers }
438
- // Mask sensitive headers
439
- if (maskedHeaders.authorization) {
440
- maskedHeaders.authorization = '***MASKED***'
441
- }
442
- if (maskedHeaders.cookie) {
443
- maskedHeaders.cookie = '***MASKED***'
444
- }
445
- return maskedHeaders
446
- },
447
- // List of body fields to mask in traces
448
- maskBodyFieldsList: ['password', 'token', 'secret'],
449
- // List of headers to mask in traces
450
- maskHeadersList: ['authorization', 'cookie', 'x-api-key'],
451
- // List of headers to include in traces (if specified, only these headers will be captured)
452
- headersToInclude: ['content-type', 'user-agent'],
453
- // List of headers to exclude from traces
454
- headersToExclude: ['authorization', 'cookie']
455
- }
456
- })
457
- ```
458
-
459
- ## Session Recorder for Next.js
460
-
461
- To integrate the MySessionRecorder component into your Next.js application, follow these steps:
462
-
463
- - Create a new file (e.g., MySessionRecorder.js or MySessionRecorder.tsx) in your root directory or a components directory.
464
-
465
- - Import the component
466
-
467
- In the newly created file, add the following code:
468
-
469
- ```javascript
470
- 'use client' // Mark as Client Component
471
- import { useEffect } from 'react'
472
- import SessionRecorder from '@multiplayer-app/session-recorder-browser'
473
-
474
- export default function MySessionRecorder() {
475
- useEffect(() => {
476
- if (typeof window !== 'undefined') {
477
- SessionRecorder.init({
478
- version: '{YOUR_APPLICATION_VERSION}',
479
- application: '{YOUR_APPLICATION_NAME}',
480
- environment: '{YOUR_APPLICATION_ENVIRONMENT}',
481
- apiKey: '{YOUR_API_KEY}',
482
- recordCanvas: true, // Enable canvas recording
483
- masking: {
484
- maskAllInputs: true,
485
- maskInputOptions: {
486
- password: true,
487
- email: false,
488
- tel: false
489
- }
490
- }
491
- })
492
-
493
- SessionRecorder.setSessionAttributes({
494
- userId: '{userId}',
495
- userName: '{userName}'
496
- })
497
- }
498
- }, [])
499
-
500
- return null // No UI output needed
501
- }
502
- ```
503
212
 
504
- Replace the placeholders with the actual information.
213
+ // advanced options
505
214
 
506
- Now, you can use the MySessionRecorder component in your application by adding it to your desired page or layout file:
507
-
508
- ```javascript
509
- import MySessionRecorder from './MySessionRecorder' // Adjust the path as necessary
510
-
511
- export default function MyApp() {
512
- return (
513
- <>
514
- <MySessionRecorder />
515
- {/* Other components */}
516
- </>
517
- )
518
- }
519
- ```
520
-
521
- ## Note
522
-
523
- If frontend domain doesn't match to backend one, set backend domain to `propagateTraceHeaderCorsUrls` parameter:
524
-
525
- ```javascript
526
- import SessionRecorder from '@multiplayer-app/session-recorder-browser'
527
-
528
- SessionRecorder.init({
529
- version: '{YOUR_APPLICATION_VERSION}',
530
- application: '{YOUR_APPLICATION_NAME}',
531
- environment: '{YOUR_APPLICATION_ENVIRONMENT}',
532
- apiKey: '{YOUR_API_KEY}',
533
- propagateTraceHeaderCorsUrls: new RegExp(`https://your.backend.api.domain`, 'i')
215
+ // allow Multiplayer Chrome to use post messages through the library
216
+ usePostMessageFallback: false
534
217
  })
535
- ```
536
-
537
- If frontend sends api requests to two or more different domains put them to `propagateTraceHeaderCorsUrls` as array:
538
-
539
- ```javascript
540
- import SessionRecorder from '@multiplayer-app/session-recorder-browser'
541
218
 
542
- SessionRecorder.init({
543
- version: '{YOUR_APPLICATION_VERSION}',
544
- application: '{YOUR_APPLICATION_NAME}',
545
- environment: '{YOUR_APPLICATION_ENVIRONMENT}',
546
- apiKey: '{YOUR_API_KEY}',
547
- propagateTraceHeaderCorsUrls: [
548
- new RegExp(`https://your.backend.api.domain`, 'i'),
549
- new RegExp(`https://another.backend.api.domain`, 'i')
550
- ]
219
+ // add any key value pairs which should be associated with a session
220
+ SessionRecorder.setSessionAttributes({
221
+ userId: '12345',
222
+ userName: 'John Doe'
551
223
  })
552
224
  ```
553
225
 
554
- ## Documentation
226
+ ### Framework notes
555
227
 
556
- For more details on how the Multiplayer Session Recorder integrates with your backend architecture and system auto-documentation, check out our [official documentation](https://www.multiplayer.app/docs/features/system-auto-documentation/).
228
+ - Next.js: initialize the browser SDK in a Client Component (see example in the browser README). Ensure it runs only in the browser.
229
+ - CORS: when your frontend calls multiple API domains, set `propagateTraceHeaderCorsUrls` to match them so parent/child spans correlate across services.
557
230
 
558
231
  ## License
559
232
 
560
- This library is distributed under the [MIT License](LICENSE).
233
+ MIT see [LICENSE](./LICENSE).