@multiplayer-app/session-recorder-browser 1.2.6 → 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 +179 -24
- package/dist/browser/index.js +22 -5
- package/dist/browser/index.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/session-recorder.d.ts.map +1 -1
- package/dist/exporters/index.js +1 -1
- package/dist/exporters/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/otel/helpers.d.ts.map +1 -1
- package/dist/services/api.service.d.ts.map +1 -1
- package/dist/sessionRecorder.d.ts.map +1 -1
- package/dist/types/sessionRecorder.d.ts.map +1 -1
- package/dist/utils/navigator.d.ts.map +1 -1
- package/package.json +3 -3
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: '
|
|
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: '
|
|
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/
|
|
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: '
|
|
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
|
-
|
|
210
|
+
### Manual session recording
|
|
214
211
|
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
388
|
+
This library is distributed under the [MIT License](./LICENSE).
|
package/dist/browser/index.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|