@multiplayer-app/session-recorder-browser 1.2.4 → 1.2.7

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
@@ -49,6 +49,11 @@ yarn add @multiplayer-app/session-recorder-browser @opentelemetry/api
49
49
  ## Set up web client:
50
50
 
51
51
  ### Quick start
52
+
53
+ Use the following code below to initialize and run the session recorder.
54
+
55
+ ### Initialize
56
+
52
57
  ```javascript
53
58
  import SessionRecorder from '@multiplayer-app/session-recorder-browser'
54
59
 
@@ -56,7 +61,7 @@ SessionRecorder.init({
56
61
  application: 'my-web-app',
57
62
  version: '1.0.0',
58
63
  environment: 'production',
59
- apiKey: '<YOUR_FRONTEND_OTEL_TOKEN>',
64
+ apiKey: 'MULTIPLAYER_API_KEY' // note: replace with your Multiplayer API key
60
65
  // IMPORTANT: in order to propagate OTLP headers to a backend
61
66
  // domain(s) with a different origin, add backend domain(s) below.
62
67
  // e.g. if you serve your website from www.example.com
@@ -65,34 +70,24 @@ SessionRecorder.init({
65
70
  // propagateTraceHeaderCorsUrls: [new RegExp('https://api.example.com', 'i')],
66
71
  })
67
72
 
68
-
69
73
  // add any key value pairs which should be associated with a session
70
74
  SessionRecorder.setSessionAttributes({
71
75
  userId: '12345',
72
- userName: 'Jane Doe',
76
+ userName: 'John Doe'
73
77
  })
74
-
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
78
  ```
84
79
 
85
80
  ### Advanced config
86
81
 
87
82
  ```javascript
88
- import SessionRecorder from '@multiplayer-app/debugger-browser'
83
+ import SessionRecorder from '@multiplayer-app/session-recorder-browser'
89
84
 
90
85
  SessionRecorder.init({
91
86
  version: '1.0.0', // optional: version of your application
92
87
  application: 'my-app', // name of your application
93
88
  environment: 'production',
94
- apiKey: 'your-api-key', // replace with your Multiplayer OTLP key
95
-
89
+ apiKey: 'MULTIPLAYER_API_KEY', // note: replace with your Multiplayer API key
90
+
96
91
  apiBaseUrl: 'https://api.multiplayer.app', // override API base URL if needed
97
92
  exporterEndpoint: 'https://otlp.multiplayer.app', // override OTLP collector URL if needed
98
93
 
@@ -113,14 +108,14 @@ SessionRecorder.init({
113
108
  // configures what percentage (0.00-1.00) of OTLP data
114
109
  // should be sent through `exporters`
115
110
  sampleTraceRatio: 0,
116
-
111
+
117
112
  // optional: exporters allow you to send
118
113
  // OTLP data to observability platforms
119
114
  exporters: [
120
115
  // example:
121
116
  // import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
122
117
  // new OTLPTraceExporter({
123
- // url: '<opentelemetry-collector-url>',
118
+ // url: '<opentelemetry-collector-url>',
124
119
  // })
125
120
  ],
126
121
 
@@ -148,7 +143,7 @@ SessionRecorder.init({
148
143
  maskTextClass: /sensitive|private/, // mask text in elements with these classes
149
144
  // CSS selector for text masking
150
145
  maskTextSelector: '.sensitive-data', // mask text in elements matching this selector
151
-
146
+
152
147
  // custom masking functions
153
148
  maskInput: (text, element) => {
154
149
  if (element.classList.contains('credit-card')) {
@@ -172,7 +167,7 @@ SessionRecorder.init({
172
167
  }
173
168
  return payload
174
169
  },
175
-
170
+
176
171
  isContentMaskingEnabled: true, // enable content masking in session recordings
177
172
  maskBody: (payload, span) => {
178
173
  // note: `payload` is already a copy of the original request/response content
@@ -208,26 +203,186 @@ SessionRecorder.init({
208
203
  headersToInclude: ['content-type', 'user-agent'],
209
204
  // list of headers to exclude from capturing
210
205
  headersToExclude: ['authorization', 'cookie']
211
- },
206
+ }
207
+ })
208
+ ```
212
209
 
213
- // advanced options
210
+ ### Manual session recording
214
211
 
215
- // allow Multiplayer Chrome to use post messages through the library
216
- usePostMessageFallback: false
212
+ Below is an example showing how to create a session recording in `MANUAL` mode. Manual session recordings stream and save all the data between calling `start` and `stop`.
213
+
214
+ ```javascript
215
+ // add any key value pairs which should be associated with a session
216
+ SessionRecorder.setSessionAttributes({
217
+ userId: '12345',
218
+ userName: 'John Doe'
217
219
  })
220
+ // optionally control via API (widget is enabled by default)
221
+ // if you're not using widget (see: `showWidget: true/false`)
222
+ // then you can programatically control the session recorder
223
+ // by using the methods below
224
+ SessionRecorder.start()
225
+ SessionRecorder.pause()
226
+ SessionRecorder.resume()
227
+ SessionRecorder.stop('Finished session') // optional: pass reason for stopping the session
228
+ ```
218
229
 
230
+ ### Continuous session recording
231
+
232
+ Below is an example showing how to create a session in `CONTINUOUS` mode. Continuous session recordings **stream** all the data received between calling `start` and `stop` -
233
+ but only **save** a rolling window data (90 seconds by default) when:
234
+
235
+ - an exception or error occurs;
236
+ - when `save` is called; or
237
+ - programmatically, when the auto-save attribute is attached to a span.
238
+
239
+ ```javascript
219
240
  // add any key value pairs which should be associated with a session
220
241
  SessionRecorder.setSessionAttributes({
221
242
  userId: '12345',
222
243
  userName: 'John Doe'
223
244
  })
245
+ // optionally control via API (widget is enabled by default)
246
+ // if you're not using widget (see: `showWidget: true/false`)
247
+ // then you can programatically control the session recorder
248
+ // by using the methods below
249
+ SessionRecorder.start(SessionType.CONTINUOUS)
250
+
251
+ // do something here
252
+
253
+ SessionRecorder.save()
254
+
255
+ // do something here
256
+
257
+ SessionRecorder.save()
258
+
259
+ SessionRecorder.stop('Finished session') // optional: pass reason for stopping the session
260
+ ```
261
+
262
+ Continuous session recordings may also be saved from within any service or component involved in a trace by adding the attributes below to a span:
263
+
264
+ ```javascript
265
+ import { trace, context } from "@opentelemetry/api"
266
+ import SessionRecorder from "@multiplayer-app/session-recorder-browser"
267
+
268
+ const activeContext = context.active()
269
+
270
+ const activeSpan = trace.getSpan(activeContext)
271
+
272
+ activeSpan.setAttribute(
273
+ SessionRecorder.ATTR_MULTIPLAYER_CONTINUOUS_SESSION_AUTO_SAVE,
274
+ true
275
+ )
276
+ activeSpan.setAttribute(
277
+ SessionRecorder.ATTR_MULTIPLAYER_CONTINUOUS_SESSION_AUTO_SAVE_REASON,
278
+ "Some reason"
279
+ )
280
+
281
+ ```
282
+
283
+ ## Session Recorder for Next.js
284
+
285
+ To integrate the MySessionRecorder component into your Next.js application, follow these steps:
286
+
287
+ - Create a new file (e.g., MySessionRecorder.js or MySessionRecorder.tsx) in your root directory or a components directory.
288
+
289
+ - Import the component
290
+
291
+ In the newly created file, add the following code:
292
+
293
+ ```javascript
294
+ 'use client' // Mark as Client Component
295
+ import { useEffect } from 'react'
296
+ import SessionRecorder from '@multiplayer-app/session-recorder-browser'
297
+
298
+ export default function MySessionRecorder() {
299
+ useEffect(() => {
300
+ if (typeof window !== 'undefined') {
301
+ SessionRecorder.init({
302
+ version: '{YOUR_APPLICATION_VERSION}',
303
+ application: '{YOUR_APPLICATION_NAME}',
304
+ environment: '{YOUR_APPLICATION_ENVIRONMENT}',
305
+ apiKey: '{YOUR_API_KEY}',
306
+ recordCanvas: true, // Enable canvas recording
307
+ masking: {
308
+ maskAllInputs: true,
309
+ maskInputOptions: {
310
+ password: true,
311
+ email: false,
312
+ tel: false
313
+ }
314
+ }
315
+ })
316
+
317
+ SessionRecorder.setSessionAttributes({
318
+ userId: '{userId}',
319
+ userName: '{userName}'
320
+ })
321
+ }
322
+ }, [])
323
+
324
+ return null // No UI output needed
325
+ }
326
+ ```
327
+
328
+ Replace the placeholders with the actual information.
329
+
330
+ Now, you can use the MySessionRecorder component in your application by adding it to your desired page or layout file:
331
+
332
+ ```javascript
333
+ import MySessionRecorder from './MySessionRecorder' // Adjust the path as necessary
334
+
335
+ export default function MyApp() {
336
+ return (
337
+ <>
338
+ <MySessionRecorder />
339
+ {/* Other components */}
340
+ </>
341
+ )
342
+ }
343
+ ```
344
+
345
+ ## Note
346
+
347
+ If frontend domain doesn't match to backend one, set backend domain to `propagateTraceHeaderCorsUrls` parameter:
348
+
349
+ ```javascript
350
+ import SessionRecorder from '@multiplayer-app/session-recorder-browser'
351
+
352
+ SessionRecorder.init({
353
+ version: '{YOUR_APPLICATION_VERSION}',
354
+ application: '{YOUR_APPLICATION_NAME}',
355
+ environment: '{YOUR_APPLICATION_ENVIRONMENT}',
356
+ apiKey: '{YOUR_API_KEY}',
357
+ propagateTraceHeaderCorsUrls: new RegExp(`https://your.backend.api.domain`, 'i')
358
+ })
359
+ ```
360
+
361
+ If frontend sends api requests to two or more different domains put them to `propagateTraceHeaderCorsUrls` as array:
362
+
363
+ ```javascript
364
+ import SessionRecorder from '@multiplayer-app/session-recorder-browser'
365
+
366
+ SessionRecorder.init({
367
+ version: '{YOUR_APPLICATION_VERSION}',
368
+ application: '{YOUR_APPLICATION_NAME}',
369
+ environment: '{YOUR_APPLICATION_ENVIRONMENT}',
370
+ apiKey: '{YOUR_API_KEY}',
371
+ propagateTraceHeaderCorsUrls: [
372
+ new RegExp(`https://your.backend.api.domain`, 'i'),
373
+ new RegExp(`https://another.backend.api.domain`, 'i')
374
+ ]
375
+ })
224
376
  ```
225
377
 
226
378
  ### Framework notes
227
379
 
228
380
  - Next.js: initialize the browser SDK in a Client Component (see example in the browser README). Ensure it runs only in the browser.
229
381
  - CORS: when your frontend calls multiple API domains, set `propagateTraceHeaderCorsUrls` to match them so parent/child spans correlate across services.
382
+ ## Documentation
383
+
384
+ 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/).
230
385
 
231
386
  ## License
232
387
 
233
- MIT see [LICENSE](./LICENSE).
388
+ This library is distributed under the [MIT License](./LICENSE).
@@ -25245,7 +25245,7 @@ const DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE = 100000;
25245
25245
  const SESSION_RESPONSE = 'multiplayer-debug-session-response';
25246
25246
  const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
25247
25247
  const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
25248
- const PACKAGE_VERSION_EXPORT = "1.2.4" || 0;
25248
+ const PACKAGE_VERSION_EXPORT = "1.2.7" || 0;
25249
25249
  // Regex patterns for OpenTelemetry ignore URLs
25250
25250
  const OTEL_IGNORE_URLS = [
25251
25251
  // Traces endpoint
@@ -25663,8 +25663,6 @@ __webpack_require__.r(__webpack_exports__);
25663
25663
  /* harmony export */ });
25664
25664
  /* harmony import */ var _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @multiplayer-app/session-recorder-common */ "../session-recorder-common/dist/esm/index-browser.js");
25665
25665
 
25666
-
25667
- const { schemify } = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_0__.SessionRecorderSdk;
25668
25666
  /**
25669
25667
  * Checks if the trace should be processed based on trace ID prefixes
25670
25668
  */
@@ -26798,6 +26796,8 @@ __webpack_require__.r(__webpack_exports__);
26798
26796
  /* harmony import */ var _index_scss__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./index.scss */ "./src/index.scss");
26799
26797
  /* harmony import */ var _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @multiplayer-app/session-recorder-common */ "../session-recorder-common/dist/esm/index-browser.js");
26800
26798
  /* harmony import */ var _sessionWidget_buttonStateConfigs__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./sessionWidget/buttonStateConfigs */ "./src/sessionWidget/buttonStateConfigs.ts");
26799
+ /* harmony import */ var lib0_observable__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! lib0/observable */ "../../node_modules/lib0/observable.js");
26800
+
26801
26801
 
26802
26802
 
26803
26803
 
@@ -26811,7 +26811,10 @@ __webpack_require__.r(__webpack_exports__);
26811
26811
 
26812
26812
 
26813
26813
 
26814
- class SessionRecorder {
26814
+ class SessionRecorder extends lib0_observable__WEBPACK_IMPORTED_MODULE_13__.Observable {
26815
+ get isInitialized() {
26816
+ return this._isInitialized;
26817
+ }
26815
26818
  get sessionId() {
26816
26819
  return this._sessionId;
26817
26820
  }
@@ -26840,6 +26843,8 @@ class SessionRecorder {
26840
26843
  this._sessionWidget.updateState(this._sessionState, this.continuousRecording);
26841
26844
  _services_messaging_service__WEBPACK_IMPORTED_MODULE_8__["default"].sendMessage('state-change', this._sessionState);
26842
26845
  (0,_utils__WEBPACK_IMPORTED_MODULE_2__.setStoredItem)(_config__WEBPACK_IMPORTED_MODULE_4__.SESSION_STATE_PROP_NAME, state);
26846
+ // Emit observable event to support React wrapper
26847
+ this.emit('state-change', [this._sessionState || _types__WEBPACK_IMPORTED_MODULE_3__.SessionState.stopped, this.sessionType]);
26843
26848
  }
26844
26849
  get session() {
26845
26850
  return this._session;
@@ -26878,12 +26883,13 @@ class SessionRecorder {
26878
26883
  */
26879
26884
  constructor() {
26880
26885
  var _a;
26881
- this._isInitialized = false;
26886
+ super();
26882
26887
  this._apiService = new _services_api_service__WEBPACK_IMPORTED_MODULE_9__.ApiService();
26883
26888
  this._tracer = new _otel__WEBPACK_IMPORTED_MODULE_0__.TracerBrowserSDK();
26884
26889
  this._recorder = new _rrweb__WEBPACK_IMPORTED_MODULE_1__.RecorderBrowserSDK();
26885
26890
  this._sessionWidget = new _sessionWidget__WEBPACK_IMPORTED_MODULE_7__.SessionWidget();
26886
26891
  this._startRequestController = null;
26892
+ this._isInitialized = false;
26887
26893
  // Session ID and state are stored in localStorage
26888
26894
  this._sessionId = null;
26889
26895
  this._sessionType = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_11__.SessionType.PLAIN;
@@ -26935,6 +26941,8 @@ class SessionRecorder {
26935
26941
  this._registerSessionLimitReach();
26936
26942
  this._registerSessionAutoCreation();
26937
26943
  _services_messaging_service__WEBPACK_IMPORTED_MODULE_8__["default"].sendMessage('state-change', this.sessionState);
26944
+ // Emit init observable event
26945
+ this.emit('init', []);
26938
26946
  }
26939
26947
  /**
26940
26948
  * Save the continuous recording session
@@ -27220,6 +27228,7 @@ class SessionRecorder {
27220
27228
  _start() {
27221
27229
  var _a;
27222
27230
  this.sessionState = _types__WEBPACK_IMPORTED_MODULE_3__.SessionState.started;
27231
+ this.sessionType = this.sessionType;
27223
27232
  this._tracer.start(this.sessionId, this.sessionType);
27224
27233
  this._recorder.start(this.sessionId, this.sessionType);
27225
27234
  if (this.session) {
@@ -28973,6 +28982,10 @@ const getNavigatorInfo = () => {
28973
28982
  cookiesEnabled,
28974
28983
  hardwareConcurrency,
28975
28984
  packageVersion,
28985
+ // System type identifier (previously in system tags)
28986
+ systemType: 'web',
28987
+ // Platform identifier (previously in system tags)
28988
+ platform: osInfo
28976
28989
  };
28977
28990
  };
28978
28991
 
@@ -30393,7 +30406,11 @@ __webpack_require__.r(__webpack_exports__);
30393
30406
  var SessionType;
30394
30407
  (function (SessionType) {
30395
30408
  SessionType["CONTINUOUS"] = "CONTINUOUS";
30409
+ /**
30410
+ * @deprecated Use MANUAL instead
30411
+ */
30396
30412
  SessionType["PLAIN"] = "MANUAL";
30413
+ SessionType["MANUAL"] = "MANUAL";
30397
30414
  })(SessionType || (SessionType = {}));
30398
30415
  //# sourceMappingURL=session-type.enum.js.map
30399
30416