@multiplayer-app/session-recorder-browser 2.0.87 → 2.0.89
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/dist/config/constants.d.ts +2 -1
- package/dist/config/constants.js +3 -1
- package/dist/index.js +119 -42
- package/dist/index.umd.js +119 -42
- package/dist/listeners.js +23 -5
- package/dist/session-recorder.d.ts +1 -0
- package/dist/session-recorder.js +92 -38
- package/package.json +2 -2
|
@@ -11,9 +11,10 @@ export declare const SESSION_UNSUBSCRIBE_EVENT = "debug-session:unsubscribe";
|
|
|
11
11
|
export declare const SESSION_AUTO_CREATED = "debug-session:auto-created";
|
|
12
12
|
export declare const SESSION_ADD_EVENT = "debug-session:rrweb:add-event";
|
|
13
13
|
export declare const SESSION_SAVE_BUFFER_EVENT = "debug-session:save-buffer";
|
|
14
|
+
export declare const SESSION_READY_EVENT = "debug-session:ready";
|
|
15
|
+
export declare const SESSION_READY_EVENT_LEGACY = "debug-session-ready";
|
|
14
16
|
export declare const SOCKET_SET_USER_EVENT = "socket:set-user";
|
|
15
17
|
export declare const DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE = 100000;
|
|
16
|
-
export declare const SESSION_READY_EVENT = "debug-session-ready";
|
|
17
18
|
export declare const CONTINUOUS_DEBUGGING_TIMEOUT = 60000;
|
|
18
19
|
export declare const DEBUG_SESSION_MAX_DURATION_SECONDS: number;
|
|
19
20
|
export declare const REMOTE_SESSION_RECORDING_START = "remote-session-recording:start";
|
package/dist/config/constants.js
CHANGED
|
@@ -12,9 +12,11 @@ export const SESSION_AUTO_CREATED = 'debug-session:auto-created';
|
|
|
12
12
|
export const SESSION_ADD_EVENT = 'debug-session:rrweb:add-event';
|
|
13
13
|
// Backend-triggered flush of client-side crash buffer
|
|
14
14
|
export const SESSION_SAVE_BUFFER_EVENT = 'debug-session:save-buffer';
|
|
15
|
+
export const SESSION_READY_EVENT = 'debug-session:ready';
|
|
16
|
+
// Deprecated event name for backwards compatibility
|
|
17
|
+
export const SESSION_READY_EVENT_LEGACY = 'debug-session-ready';
|
|
15
18
|
export const SOCKET_SET_USER_EVENT = 'socket:set-user';
|
|
16
19
|
export const DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE = 100000;
|
|
17
|
-
export const SESSION_READY_EVENT = 'debug-session-ready';
|
|
18
20
|
export const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
|
|
19
21
|
export const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
|
|
20
22
|
export const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
|
package/dist/index.js
CHANGED
|
@@ -7030,6 +7030,7 @@ module.exports = {
|
|
|
7030
7030
|
/* harmony export */ SESSION_ID_PROP_NAME: () => (/* binding */ SESSION_ID_PROP_NAME),
|
|
7031
7031
|
/* harmony export */ SESSION_PROP_NAME: () => (/* binding */ SESSION_PROP_NAME),
|
|
7032
7032
|
/* harmony export */ SESSION_READY_EVENT: () => (/* binding */ SESSION_READY_EVENT),
|
|
7033
|
+
/* harmony export */ SESSION_READY_EVENT_LEGACY: () => (/* binding */ SESSION_READY_EVENT_LEGACY),
|
|
7033
7034
|
/* harmony export */ SESSION_SAVE_BUFFER_EVENT: () => (/* binding */ SESSION_SAVE_BUFFER_EVENT),
|
|
7034
7035
|
/* harmony export */ SESSION_STARTED_EVENT: () => (/* binding */ SESSION_STARTED_EVENT),
|
|
7035
7036
|
/* harmony export */ SESSION_STATE_PROP_NAME: () => (/* binding */ SESSION_STATE_PROP_NAME),
|
|
@@ -7054,14 +7055,16 @@ const SESSION_AUTO_CREATED = 'debug-session:auto-created';
|
|
|
7054
7055
|
const SESSION_ADD_EVENT = 'debug-session:rrweb:add-event';
|
|
7055
7056
|
// Backend-triggered flush of client-side crash buffer
|
|
7056
7057
|
const SESSION_SAVE_BUFFER_EVENT = 'debug-session:save-buffer';
|
|
7058
|
+
const SESSION_READY_EVENT = 'debug-session:ready';
|
|
7059
|
+
// Deprecated event name for backwards compatibility
|
|
7060
|
+
const SESSION_READY_EVENT_LEGACY = 'debug-session-ready';
|
|
7057
7061
|
const SOCKET_SET_USER_EVENT = 'socket:set-user';
|
|
7058
7062
|
const DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE = 100000;
|
|
7059
|
-
const SESSION_READY_EVENT = 'debug-session-ready';
|
|
7060
7063
|
const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
|
|
7061
7064
|
const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
|
|
7062
7065
|
const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
|
|
7063
7066
|
const REMOTE_SESSION_RECORDING_STOP = 'remote-session-recording:stop';
|
|
7064
|
-
const PACKAGE_VERSION_EXPORT = "2.0.
|
|
7067
|
+
const PACKAGE_VERSION_EXPORT = "2.0.89" || 0;
|
|
7065
7068
|
// Regex patterns for OpenTelemetry ignore URLs
|
|
7066
7069
|
const OTEL_IGNORE_URLS = [
|
|
7067
7070
|
// Traces endpoint
|
|
@@ -7199,6 +7202,7 @@ const BASE_CONFIG = {
|
|
|
7199
7202
|
/* harmony export */ SESSION_ID_PROP_NAME: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_ID_PROP_NAME),
|
|
7200
7203
|
/* harmony export */ SESSION_PROP_NAME: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_PROP_NAME),
|
|
7201
7204
|
/* harmony export */ SESSION_READY_EVENT: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_READY_EVENT),
|
|
7205
|
+
/* harmony export */ SESSION_READY_EVENT_LEGACY: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_READY_EVENT_LEGACY),
|
|
7202
7206
|
/* harmony export */ SESSION_SAVE_BUFFER_EVENT: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_SAVE_BUFFER_EVENT),
|
|
7203
7207
|
/* harmony export */ SESSION_STARTED_EVENT: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_STARTED_EVENT),
|
|
7204
7208
|
/* harmony export */ SESSION_STATE_PROP_NAME: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_STATE_PROP_NAME),
|
|
@@ -7459,24 +7463,43 @@ const isBrowserExtension = isBrowser && SESSION_RECORDER_INJECTED in globalObj;
|
|
|
7459
7463
|
/* harmony export */ setupListeners: () => (/* binding */ setupListeners)
|
|
7460
7464
|
/* harmony export */ });
|
|
7461
7465
|
/* harmony import */ var _services_messaging_service__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./services/messaging.service */ "./src/services/messaging.service.ts");
|
|
7462
|
-
/* harmony import */ var
|
|
7466
|
+
/* harmony import */ var _config_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config/constants */ "./src/config/constants.ts");
|
|
7467
|
+
/* harmony import */ var _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @multiplayer-app/session-recorder-common */ "../session-recorder-common/dist/esm/index-browser.js");
|
|
7468
|
+
|
|
7463
7469
|
|
|
7464
7470
|
|
|
7465
7471
|
function setupListeners(sessionRecorder) {
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7472
|
+
const announceState = () => {
|
|
7473
|
+
// Send continuous flag first so it is applied before the consumer reacts to
|
|
7474
|
+
// the 'ready' message (postMessage preserves delivery order).
|
|
7475
|
+
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].sendMessage('continuous-debugging', sessionRecorder.continuousRecording);
|
|
7476
|
+
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].sendMessage('ready', {
|
|
7477
|
+
session: sessionRecorder.session,
|
|
7478
|
+
sessionState: sessionRecorder.sessionState,
|
|
7479
|
+
// Lets the extension popup detect libs too old to support on-demand state
|
|
7480
|
+
// sync (the `get-state` request below).
|
|
7481
|
+
version: _config_constants__WEBPACK_IMPORTED_MODULE_1__.PACKAGE_VERSION_EXPORT,
|
|
7482
|
+
});
|
|
7483
|
+
};
|
|
7484
|
+
// Announce current state on load...
|
|
7485
|
+
announceState();
|
|
7486
|
+
// ...and whenever the extension asks (e.g. the popup was opened after the
|
|
7487
|
+
// recorder had already initialized, so it missed the initial announcement).
|
|
7488
|
+
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('get-state', () => {
|
|
7489
|
+
announceState();
|
|
7470
7490
|
});
|
|
7471
7491
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('init', (payload) => {
|
|
7472
7492
|
sessionRecorder.init(payload);
|
|
7473
7493
|
});
|
|
7474
7494
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('start', (payload) => {
|
|
7475
|
-
sessionRecorder.start(
|
|
7495
|
+
sessionRecorder.start(_multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_2__.SessionType.MANUAL, payload);
|
|
7476
7496
|
});
|
|
7477
7497
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('end', (payload) => {
|
|
7478
7498
|
sessionRecorder.stop(payload);
|
|
7479
7499
|
});
|
|
7500
|
+
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('stop', (payload) => {
|
|
7501
|
+
sessionRecorder.stop(payload);
|
|
7502
|
+
});
|
|
7480
7503
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('pause', () => {
|
|
7481
7504
|
sessionRecorder.pause();
|
|
7482
7505
|
});
|
|
@@ -7488,7 +7511,7 @@ function setupListeners(sessionRecorder) {
|
|
|
7488
7511
|
});
|
|
7489
7512
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('toggle-continuous-debugging', (payload) => {
|
|
7490
7513
|
if (payload.enabled) {
|
|
7491
|
-
sessionRecorder.start(
|
|
7514
|
+
sessionRecorder.start(_multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_2__.SessionType.CONTINUOUS, payload.session);
|
|
7492
7515
|
}
|
|
7493
7516
|
else {
|
|
7494
7517
|
sessionRecorder.stop();
|
|
@@ -10278,7 +10301,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10278
10301
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_10__["default"].sendMessage('state-change', this._sessionState);
|
|
10279
10302
|
(0,_utils__WEBPACK_IMPORTED_MODULE_4__.setStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_STATE_PROP_NAME, state);
|
|
10280
10303
|
// Emit observable event to support React wrapper
|
|
10281
|
-
this.emit('state-change', [
|
|
10304
|
+
this.emit('state-change', [
|
|
10305
|
+
this._sessionState || _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.stopped,
|
|
10306
|
+
this.sessionType,
|
|
10307
|
+
]);
|
|
10282
10308
|
}
|
|
10283
10309
|
get session() {
|
|
10284
10310
|
return this._session;
|
|
@@ -10339,6 +10365,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10339
10365
|
// Session ID and state are stored in sessionStorage (with fallback) to avoid multi-tab conflicts
|
|
10340
10366
|
this._sessionId = null;
|
|
10341
10367
|
this._sessionType = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL;
|
|
10368
|
+
// Guards against overlapping start() calls. `_checkOperation('start')` only
|
|
10369
|
+
// rejects once sessionState is `started`, but `_createSessionAndStart` is
|
|
10370
|
+
// async, so two rapid starts could both pass and create duplicate sessions.
|
|
10371
|
+
this._startInProgress = false;
|
|
10342
10372
|
this._sessionState = null;
|
|
10343
10373
|
this._session = null;
|
|
10344
10374
|
this._sessionAttributes = null;
|
|
@@ -10349,10 +10379,18 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10349
10379
|
this._error = '';
|
|
10350
10380
|
// Safety: avoid accessing storage in SSR/non-browser environments
|
|
10351
10381
|
const isBrowser = typeof window !== 'undefined';
|
|
10352
|
-
const sessionLocal = isBrowser
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
const
|
|
10382
|
+
const sessionLocal = isBrowser
|
|
10383
|
+
? (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_PROP_NAME, true)
|
|
10384
|
+
: null;
|
|
10385
|
+
const sessionIdLocal = isBrowser
|
|
10386
|
+
? (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_ID_PROP_NAME)
|
|
10387
|
+
: null;
|
|
10388
|
+
const sessionStateLocal = isBrowser
|
|
10389
|
+
? (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_STATE_PROP_NAME)
|
|
10390
|
+
: null;
|
|
10391
|
+
const sessionTypeLocal = isBrowser
|
|
10392
|
+
? (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_TYPE_PROP_NAME)
|
|
10393
|
+
: null;
|
|
10356
10394
|
if ((0,_utils__WEBPACK_IMPORTED_MODULE_4__.isSessionActive)(sessionLocal, sessionTypeLocal)) {
|
|
10357
10395
|
this.session = sessionLocal;
|
|
10358
10396
|
this.sessionId = sessionIdLocal;
|
|
@@ -10384,7 +10422,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10384
10422
|
// GC: remove orphaned crash buffers from old tabs.
|
|
10385
10423
|
// Keep TTL large to avoid any accidental data loss.
|
|
10386
10424
|
void this._bufferDb.sweepStaleTabs(10 * 60 * 60 * 1000);
|
|
10387
|
-
(0,_patch__WEBPACK_IMPORTED_MODULE_7__.setMaxCapturingHttpPayloadSize)(this._configs.maxCapturingHttpPayloadSize ||
|
|
10425
|
+
(0,_patch__WEBPACK_IMPORTED_MODULE_7__.setMaxCapturingHttpPayloadSize)(this._configs.maxCapturingHttpPayloadSize ||
|
|
10426
|
+
_config__WEBPACK_IMPORTED_MODULE_6__.DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE);
|
|
10388
10427
|
(0,_patch__WEBPACK_IMPORTED_MODULE_7__.setShouldRecordHttpData)(this._configs.captureBody, this._configs.captureHeaders);
|
|
10389
10428
|
this._setupCrashBuffer();
|
|
10390
10429
|
this._tracer.init(this._configs);
|
|
@@ -10406,7 +10445,9 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10406
10445
|
if (this._configs.apiKey) {
|
|
10407
10446
|
this._recorder.init(this._configs, this._socketService);
|
|
10408
10447
|
}
|
|
10409
|
-
if (this.sessionId &&
|
|
10448
|
+
if (this.sessionId &&
|
|
10449
|
+
(this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
10450
|
+
this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused)) {
|
|
10410
10451
|
this._start();
|
|
10411
10452
|
}
|
|
10412
10453
|
else {
|
|
@@ -10460,7 +10501,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10460
10501
|
return;
|
|
10461
10502
|
let hasFocus = true;
|
|
10462
10503
|
try {
|
|
10463
|
-
hasFocus =
|
|
10504
|
+
hasFocus =
|
|
10505
|
+
typeof document.hasFocus === 'function' ? document.hasFocus() : true;
|
|
10464
10506
|
}
|
|
10465
10507
|
catch (_e) {
|
|
10466
10508
|
hasFocus = true;
|
|
@@ -10482,7 +10524,9 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10482
10524
|
// stop, leaving the buffer with no FullSnapshot and silently breaking
|
|
10483
10525
|
// exception-triggered flushBuffer. `_recorder.restart(null, ...)` passes
|
|
10484
10526
|
// null explicitly, so it's safe regardless of `this.sessionId`.
|
|
10485
|
-
if (!this._crashBuffer ||
|
|
10527
|
+
if (!this._crashBuffer ||
|
|
10528
|
+
!((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) ||
|
|
10529
|
+
this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.stopped) {
|
|
10486
10530
|
return;
|
|
10487
10531
|
}
|
|
10488
10532
|
void this._recorder.restart(null, _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL);
|
|
@@ -10531,9 +10575,13 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10531
10575
|
* @param session - the session to start
|
|
10532
10576
|
*/
|
|
10533
10577
|
start(type = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL, session) {
|
|
10578
|
+
if (this._startInProgress)
|
|
10579
|
+
return;
|
|
10534
10580
|
this._checkOperation('start');
|
|
10581
|
+
this._startInProgress = true;
|
|
10535
10582
|
// If continuous recording is disabled, force plain mode
|
|
10536
|
-
if (type === _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.CONTINUOUS &&
|
|
10583
|
+
if (type === _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.CONTINUOUS &&
|
|
10584
|
+
!this._configs.showContinuousRecording) {
|
|
10537
10585
|
type = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL;
|
|
10538
10586
|
}
|
|
10539
10587
|
this.sessionType = type;
|
|
@@ -10564,7 +10612,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10564
10612
|
stoppedAt: this._recorder.stoppedAt,
|
|
10565
10613
|
};
|
|
10566
10614
|
const response = await this._apiService.stopSession(sid, request);
|
|
10567
|
-
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.
|
|
10615
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_READY_EVENT_LEGACY, response._id);
|
|
10616
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_READY_EVENT, response);
|
|
10568
10617
|
}
|
|
10569
10618
|
}
|
|
10570
10619
|
catch (error) {
|
|
@@ -10660,8 +10709,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10660
10709
|
this.error = (e === null || e === void 0 ? void 0 : e.message) || 'Failed to capture exception';
|
|
10661
10710
|
}
|
|
10662
10711
|
}
|
|
10663
|
-
async _flushBuffer(
|
|
10664
|
-
if (!
|
|
10712
|
+
async _flushBuffer(session, force = false) {
|
|
10713
|
+
if (!session || !this._crashBuffer || this._isFlushingBuffer) {
|
|
10665
10714
|
return null;
|
|
10666
10715
|
}
|
|
10667
10716
|
this._isFlushingBuffer = true;
|
|
@@ -10672,8 +10721,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10672
10721
|
}
|
|
10673
10722
|
await Promise.all([
|
|
10674
10723
|
this._tracer.exportTraces(spans.map((s) => s.span)),
|
|
10675
|
-
this._apiService.exportEvents(
|
|
10676
|
-
|
|
10724
|
+
this._apiService.exportEvents(session._id, {
|
|
10725
|
+
events: events.map((e) => e.event),
|
|
10726
|
+
}),
|
|
10727
|
+
this._apiService.updateSessionAttributes(session._id, {
|
|
10677
10728
|
startedAt: this._toCrashBufferSessionIso(startedAt),
|
|
10678
10729
|
stoppedAt: this._toCrashBufferSessionIso(stoppedAt),
|
|
10679
10730
|
sessionAttributes: this.sessionAttributes,
|
|
@@ -10688,7 +10739,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10688
10739
|
finally {
|
|
10689
10740
|
await this._crashBuffer.clear();
|
|
10690
10741
|
this._isFlushingBuffer = false;
|
|
10691
|
-
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.
|
|
10742
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_READY_EVENT_LEGACY, session._id);
|
|
10743
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_READY_EVENT, session);
|
|
10692
10744
|
}
|
|
10693
10745
|
}
|
|
10694
10746
|
_toCrashBufferSessionIso(ts) {
|
|
@@ -10777,7 +10829,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10777
10829
|
* Handle the safe stop event
|
|
10778
10830
|
*/
|
|
10779
10831
|
_handleStop(comment) {
|
|
10780
|
-
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
10832
|
+
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
10833
|
+
this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused) {
|
|
10781
10834
|
this.stop(comment);
|
|
10782
10835
|
}
|
|
10783
10836
|
}
|
|
@@ -10801,7 +10854,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10801
10854
|
* Handle the safe cancel event
|
|
10802
10855
|
*/
|
|
10803
10856
|
_handleCancel() {
|
|
10804
|
-
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
10857
|
+
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
10858
|
+
this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused) {
|
|
10805
10859
|
this.cancel();
|
|
10806
10860
|
}
|
|
10807
10861
|
}
|
|
@@ -10809,7 +10863,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10809
10863
|
* Handle the safe save event
|
|
10810
10864
|
*/
|
|
10811
10865
|
_handleSave() {
|
|
10812
|
-
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started &&
|
|
10866
|
+
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started &&
|
|
10867
|
+
this.continuousRecording) {
|
|
10813
10868
|
this.save();
|
|
10814
10869
|
}
|
|
10815
10870
|
}
|
|
@@ -10854,12 +10909,12 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10854
10909
|
}
|
|
10855
10910
|
});
|
|
10856
10911
|
this._socketService.on(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_SAVE_BUFFER_EVENT, (payload) => {
|
|
10857
|
-
var _a, _b
|
|
10912
|
+
var _a, _b;
|
|
10858
10913
|
if (this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.stopped)
|
|
10859
10914
|
return;
|
|
10860
|
-
this._flushBuffer(
|
|
10861
|
-
if ((
|
|
10862
|
-
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_AUTO_CREATED, (
|
|
10915
|
+
this._flushBuffer(payload === null || payload === void 0 ? void 0 : payload.debugSession, true);
|
|
10916
|
+
if ((_a = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _a === void 0 ? void 0 : _a.url) {
|
|
10917
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_AUTO_CREATED, (_b = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _b === void 0 ? void 0 : _b.url);
|
|
10863
10918
|
}
|
|
10864
10919
|
});
|
|
10865
10920
|
}
|
|
@@ -10867,7 +10922,7 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10867
10922
|
try {
|
|
10868
10923
|
const session = await this._apiService.createErrorSession({ span });
|
|
10869
10924
|
if (session === null || session === void 0 ? void 0 : session._id) {
|
|
10870
|
-
this._flushBuffer(session
|
|
10925
|
+
this._flushBuffer(session);
|
|
10871
10926
|
}
|
|
10872
10927
|
if (session === null || session === void 0 ? void 0 : session.url) {
|
|
10873
10928
|
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_AUTO_CREATED, session.url);
|
|
@@ -10887,18 +10942,25 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10887
10942
|
sessionAttributes: this.sessionAttributes,
|
|
10888
10943
|
resourceAttributes: (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getNavigatorInfo)(),
|
|
10889
10944
|
name: this._getSessionName(),
|
|
10890
|
-
...(this._userAttributes
|
|
10945
|
+
...(this._userAttributes
|
|
10946
|
+
? { userAttributes: this._userAttributes }
|
|
10947
|
+
: {}),
|
|
10891
10948
|
};
|
|
10892
|
-
const request = !this.continuousRecording
|
|
10949
|
+
const request = !this.continuousRecording
|
|
10950
|
+
? payload
|
|
10951
|
+
: { debugSessionData: payload };
|
|
10893
10952
|
const session = this.continuousRecording
|
|
10894
10953
|
? await this._apiService.startContinuousDebugSession(request, signal)
|
|
10895
10954
|
: await this._apiService.startSession(request, signal);
|
|
10896
10955
|
if (session) {
|
|
10897
|
-
session.sessionType = this.continuousRecording
|
|
10956
|
+
session.sessionType = this.continuousRecording
|
|
10957
|
+
? _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.CONTINUOUS
|
|
10958
|
+
: _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL;
|
|
10898
10959
|
this._setupSessionAndStart(session, false);
|
|
10899
10960
|
}
|
|
10900
10961
|
}
|
|
10901
10962
|
catch (error) {
|
|
10963
|
+
this._startInProgress = false;
|
|
10902
10964
|
this.error = error.message;
|
|
10903
10965
|
this.sessionState = _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.stopped;
|
|
10904
10966
|
if (this.continuousRecording) {
|
|
@@ -10911,13 +10973,17 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10911
10973
|
*/
|
|
10912
10974
|
_start() {
|
|
10913
10975
|
var _a, _b;
|
|
10976
|
+
this._startInProgress = false;
|
|
10914
10977
|
this.sessionState = _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started;
|
|
10915
10978
|
// eslint-disable-next-line no-self-assign
|
|
10916
10979
|
this.sessionType = this.sessionType;
|
|
10917
10980
|
this._tracer.start(this.sessionId, this.sessionType);
|
|
10918
10981
|
// Ensure we switch from buffer-only recording to session recording cleanly.
|
|
10919
10982
|
void this._recorder.restart(this.sessionId, this.sessionType);
|
|
10920
|
-
this._navigationRecorder.start({
|
|
10983
|
+
this._navigationRecorder.start({
|
|
10984
|
+
sessionId: this.sessionId,
|
|
10985
|
+
sessionType: this.sessionType,
|
|
10986
|
+
});
|
|
10921
10987
|
if (this.session) {
|
|
10922
10988
|
this._socketService.subscribeToSession(this.session);
|
|
10923
10989
|
this._sessionWidget.seconds = (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getTimeDifferenceInSeconds)((_a = this.session) === null || _a === void 0 ? void 0 : _a.startedAt);
|
|
@@ -10945,8 +11011,12 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10945
11011
|
// rrweb assigns new node IDs on each record() call, so the next buffer
|
|
10946
11012
|
// segment must not carry events from the previous generation. Await the
|
|
10947
11013
|
// clear so its IDB tx can't race past the fresh FullSnapshot.
|
|
10948
|
-
const cleared = this._crashBuffer
|
|
10949
|
-
|
|
11014
|
+
const cleared = this._crashBuffer
|
|
11015
|
+
? this._crashBuffer.clear()
|
|
11016
|
+
: Promise.resolve();
|
|
11017
|
+
void cleared
|
|
11018
|
+
.catch(() => undefined)
|
|
11019
|
+
.then(() => this._startBufferOnlyRecording());
|
|
10950
11020
|
}
|
|
10951
11021
|
/**
|
|
10952
11022
|
* Pause the session tracing and recording
|
|
@@ -10982,7 +11052,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
10982
11052
|
* @param sessionId - the session ID to set or clear
|
|
10983
11053
|
*/
|
|
10984
11054
|
_setSession(session) {
|
|
10985
|
-
this.session = {
|
|
11055
|
+
this.session = {
|
|
11056
|
+
...session,
|
|
11057
|
+
startedAt: session.startedAt || new Date().toISOString(),
|
|
11058
|
+
};
|
|
10986
11059
|
this.sessionId = (session === null || session === void 0 ? void 0 : session.shortId) || (session === null || session === void 0 ? void 0 : session._id);
|
|
10987
11060
|
}
|
|
10988
11061
|
_clearSession() {
|
|
@@ -11005,7 +11078,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
11005
11078
|
}
|
|
11006
11079
|
break;
|
|
11007
11080
|
case 'stop':
|
|
11008
|
-
if (this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused &&
|
|
11081
|
+
if (this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused &&
|
|
11082
|
+
this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started) {
|
|
11009
11083
|
throw new Error('Cannot stop. Session is not currently started.');
|
|
11010
11084
|
}
|
|
11011
11085
|
break;
|
|
@@ -11083,7 +11157,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
11083
11157
|
*/
|
|
11084
11158
|
_getSessionName(date = new Date()) {
|
|
11085
11159
|
var _a, _b, _c;
|
|
11086
|
-
const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) ||
|
|
11160
|
+
const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) ||
|
|
11161
|
+
((_b = this._userAttributes) === null || _b === void 0 ? void 0 : _b.userName) ||
|
|
11162
|
+
((_c = this._userAttributes) === null || _c === void 0 ? void 0 : _c.name) ||
|
|
11163
|
+
'';
|
|
11087
11164
|
return userName
|
|
11088
11165
|
? `${userName}'s session on ${(0,_utils__WEBPACK_IMPORTED_MODULE_4__.getFormattedDate)(date, { month: 'short', day: 'numeric' })}`
|
|
11089
11166
|
: `Session on ${(0,_utils__WEBPACK_IMPORTED_MODULE_4__.getFormattedDate)(date)}`;
|
package/dist/index.umd.js
CHANGED
|
@@ -21463,6 +21463,7 @@ module.exports = {
|
|
|
21463
21463
|
/* harmony export */ SESSION_ID_PROP_NAME: () => (/* binding */ SESSION_ID_PROP_NAME),
|
|
21464
21464
|
/* harmony export */ SESSION_PROP_NAME: () => (/* binding */ SESSION_PROP_NAME),
|
|
21465
21465
|
/* harmony export */ SESSION_READY_EVENT: () => (/* binding */ SESSION_READY_EVENT),
|
|
21466
|
+
/* harmony export */ SESSION_READY_EVENT_LEGACY: () => (/* binding */ SESSION_READY_EVENT_LEGACY),
|
|
21466
21467
|
/* harmony export */ SESSION_SAVE_BUFFER_EVENT: () => (/* binding */ SESSION_SAVE_BUFFER_EVENT),
|
|
21467
21468
|
/* harmony export */ SESSION_STARTED_EVENT: () => (/* binding */ SESSION_STARTED_EVENT),
|
|
21468
21469
|
/* harmony export */ SESSION_STATE_PROP_NAME: () => (/* binding */ SESSION_STATE_PROP_NAME),
|
|
@@ -21487,14 +21488,16 @@ const SESSION_AUTO_CREATED = 'debug-session:auto-created';
|
|
|
21487
21488
|
const SESSION_ADD_EVENT = 'debug-session:rrweb:add-event';
|
|
21488
21489
|
// Backend-triggered flush of client-side crash buffer
|
|
21489
21490
|
const SESSION_SAVE_BUFFER_EVENT = 'debug-session:save-buffer';
|
|
21491
|
+
const SESSION_READY_EVENT = 'debug-session:ready';
|
|
21492
|
+
// Deprecated event name for backwards compatibility
|
|
21493
|
+
const SESSION_READY_EVENT_LEGACY = 'debug-session-ready';
|
|
21490
21494
|
const SOCKET_SET_USER_EVENT = 'socket:set-user';
|
|
21491
21495
|
const DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE = 100000;
|
|
21492
|
-
const SESSION_READY_EVENT = 'debug-session-ready';
|
|
21493
21496
|
const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
|
|
21494
21497
|
const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
|
|
21495
21498
|
const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
|
|
21496
21499
|
const REMOTE_SESSION_RECORDING_STOP = 'remote-session-recording:stop';
|
|
21497
|
-
const PACKAGE_VERSION_EXPORT = "2.0.
|
|
21500
|
+
const PACKAGE_VERSION_EXPORT = "2.0.89" || 0;
|
|
21498
21501
|
// Regex patterns for OpenTelemetry ignore URLs
|
|
21499
21502
|
const OTEL_IGNORE_URLS = [
|
|
21500
21503
|
// Traces endpoint
|
|
@@ -21634,6 +21637,7 @@ const BASE_CONFIG = {
|
|
|
21634
21637
|
/* harmony export */ SESSION_ID_PROP_NAME: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_ID_PROP_NAME),
|
|
21635
21638
|
/* harmony export */ SESSION_PROP_NAME: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_PROP_NAME),
|
|
21636
21639
|
/* harmony export */ SESSION_READY_EVENT: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_READY_EVENT),
|
|
21640
|
+
/* harmony export */ SESSION_READY_EVENT_LEGACY: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_READY_EVENT_LEGACY),
|
|
21637
21641
|
/* harmony export */ SESSION_SAVE_BUFFER_EVENT: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_SAVE_BUFFER_EVENT),
|
|
21638
21642
|
/* harmony export */ SESSION_STARTED_EVENT: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_STARTED_EVENT),
|
|
21639
21643
|
/* harmony export */ SESSION_STATE_PROP_NAME: () => (/* reexport safe */ _constants__WEBPACK_IMPORTED_MODULE_0__.SESSION_STATE_PROP_NAME),
|
|
@@ -21900,24 +21904,43 @@ const isBrowserExtension = isBrowser && SESSION_RECORDER_INJECTED in globalObj;
|
|
|
21900
21904
|
/* harmony export */ setupListeners: () => (/* binding */ setupListeners)
|
|
21901
21905
|
/* harmony export */ });
|
|
21902
21906
|
/* harmony import */ var _services_messaging_service__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./services/messaging.service */ "./src/services/messaging.service.ts");
|
|
21903
|
-
/* harmony import */ var
|
|
21907
|
+
/* harmony import */ var _config_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config/constants */ "./src/config/constants.ts");
|
|
21908
|
+
/* harmony import */ var _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @multiplayer-app/session-recorder-common */ "../session-recorder-common/dist/esm/index-browser.js");
|
|
21909
|
+
|
|
21904
21910
|
|
|
21905
21911
|
|
|
21906
21912
|
function setupListeners(sessionRecorder) {
|
|
21907
|
-
|
|
21908
|
-
|
|
21909
|
-
|
|
21910
|
-
|
|
21913
|
+
const announceState = () => {
|
|
21914
|
+
// Send continuous flag first so it is applied before the consumer reacts to
|
|
21915
|
+
// the 'ready' message (postMessage preserves delivery order).
|
|
21916
|
+
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].sendMessage('continuous-debugging', sessionRecorder.continuousRecording);
|
|
21917
|
+
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].sendMessage('ready', {
|
|
21918
|
+
session: sessionRecorder.session,
|
|
21919
|
+
sessionState: sessionRecorder.sessionState,
|
|
21920
|
+
// Lets the extension popup detect libs too old to support on-demand state
|
|
21921
|
+
// sync (the `get-state` request below).
|
|
21922
|
+
version: _config_constants__WEBPACK_IMPORTED_MODULE_1__.PACKAGE_VERSION_EXPORT,
|
|
21923
|
+
});
|
|
21924
|
+
};
|
|
21925
|
+
// Announce current state on load...
|
|
21926
|
+
announceState();
|
|
21927
|
+
// ...and whenever the extension asks (e.g. the popup was opened after the
|
|
21928
|
+
// recorder had already initialized, so it missed the initial announcement).
|
|
21929
|
+
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('get-state', () => {
|
|
21930
|
+
announceState();
|
|
21911
21931
|
});
|
|
21912
21932
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('init', (payload) => {
|
|
21913
21933
|
sessionRecorder.init(payload);
|
|
21914
21934
|
});
|
|
21915
21935
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('start', (payload) => {
|
|
21916
|
-
sessionRecorder.start(
|
|
21936
|
+
sessionRecorder.start(_multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_2__.SessionType.MANUAL, payload);
|
|
21917
21937
|
});
|
|
21918
21938
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('end', (payload) => {
|
|
21919
21939
|
sessionRecorder.stop(payload);
|
|
21920
21940
|
});
|
|
21941
|
+
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('stop', (payload) => {
|
|
21942
|
+
sessionRecorder.stop(payload);
|
|
21943
|
+
});
|
|
21921
21944
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('pause', () => {
|
|
21922
21945
|
sessionRecorder.pause();
|
|
21923
21946
|
});
|
|
@@ -21929,7 +21952,7 @@ function setupListeners(sessionRecorder) {
|
|
|
21929
21952
|
});
|
|
21930
21953
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_0__["default"].on('toggle-continuous-debugging', (payload) => {
|
|
21931
21954
|
if (payload.enabled) {
|
|
21932
|
-
sessionRecorder.start(
|
|
21955
|
+
sessionRecorder.start(_multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_2__.SessionType.CONTINUOUS, payload.session);
|
|
21933
21956
|
}
|
|
21934
21957
|
else {
|
|
21935
21958
|
sessionRecorder.stop();
|
|
@@ -24739,7 +24762,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
24739
24762
|
_services_messaging_service__WEBPACK_IMPORTED_MODULE_10__["default"].sendMessage('state-change', this._sessionState);
|
|
24740
24763
|
(0,_utils__WEBPACK_IMPORTED_MODULE_4__.setStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_STATE_PROP_NAME, state);
|
|
24741
24764
|
// Emit observable event to support React wrapper
|
|
24742
|
-
this.emit('state-change', [
|
|
24765
|
+
this.emit('state-change', [
|
|
24766
|
+
this._sessionState || _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.stopped,
|
|
24767
|
+
this.sessionType,
|
|
24768
|
+
]);
|
|
24743
24769
|
}
|
|
24744
24770
|
get session() {
|
|
24745
24771
|
return this._session;
|
|
@@ -24800,6 +24826,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
24800
24826
|
// Session ID and state are stored in sessionStorage (with fallback) to avoid multi-tab conflicts
|
|
24801
24827
|
this._sessionId = null;
|
|
24802
24828
|
this._sessionType = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL;
|
|
24829
|
+
// Guards against overlapping start() calls. `_checkOperation('start')` only
|
|
24830
|
+
// rejects once sessionState is `started`, but `_createSessionAndStart` is
|
|
24831
|
+
// async, so two rapid starts could both pass and create duplicate sessions.
|
|
24832
|
+
this._startInProgress = false;
|
|
24803
24833
|
this._sessionState = null;
|
|
24804
24834
|
this._session = null;
|
|
24805
24835
|
this._sessionAttributes = null;
|
|
@@ -24810,10 +24840,18 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
24810
24840
|
this._error = '';
|
|
24811
24841
|
// Safety: avoid accessing storage in SSR/non-browser environments
|
|
24812
24842
|
const isBrowser = typeof window !== 'undefined';
|
|
24813
|
-
const sessionLocal = isBrowser
|
|
24814
|
-
|
|
24815
|
-
|
|
24816
|
-
const
|
|
24843
|
+
const sessionLocal = isBrowser
|
|
24844
|
+
? (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_PROP_NAME, true)
|
|
24845
|
+
: null;
|
|
24846
|
+
const sessionIdLocal = isBrowser
|
|
24847
|
+
? (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_ID_PROP_NAME)
|
|
24848
|
+
: null;
|
|
24849
|
+
const sessionStateLocal = isBrowser
|
|
24850
|
+
? (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_STATE_PROP_NAME)
|
|
24851
|
+
: null;
|
|
24852
|
+
const sessionTypeLocal = isBrowser
|
|
24853
|
+
? (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getStoredItem)(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_TYPE_PROP_NAME)
|
|
24854
|
+
: null;
|
|
24817
24855
|
if ((0,_utils__WEBPACK_IMPORTED_MODULE_4__.isSessionActive)(sessionLocal, sessionTypeLocal)) {
|
|
24818
24856
|
this.session = sessionLocal;
|
|
24819
24857
|
this.sessionId = sessionIdLocal;
|
|
@@ -24845,7 +24883,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
24845
24883
|
// GC: remove orphaned crash buffers from old tabs.
|
|
24846
24884
|
// Keep TTL large to avoid any accidental data loss.
|
|
24847
24885
|
void this._bufferDb.sweepStaleTabs(10 * 60 * 60 * 1000);
|
|
24848
|
-
(0,_patch__WEBPACK_IMPORTED_MODULE_7__.setMaxCapturingHttpPayloadSize)(this._configs.maxCapturingHttpPayloadSize ||
|
|
24886
|
+
(0,_patch__WEBPACK_IMPORTED_MODULE_7__.setMaxCapturingHttpPayloadSize)(this._configs.maxCapturingHttpPayloadSize ||
|
|
24887
|
+
_config__WEBPACK_IMPORTED_MODULE_6__.DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE);
|
|
24849
24888
|
(0,_patch__WEBPACK_IMPORTED_MODULE_7__.setShouldRecordHttpData)(this._configs.captureBody, this._configs.captureHeaders);
|
|
24850
24889
|
this._setupCrashBuffer();
|
|
24851
24890
|
this._tracer.init(this._configs);
|
|
@@ -24867,7 +24906,9 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
24867
24906
|
if (this._configs.apiKey) {
|
|
24868
24907
|
this._recorder.init(this._configs, this._socketService);
|
|
24869
24908
|
}
|
|
24870
|
-
if (this.sessionId &&
|
|
24909
|
+
if (this.sessionId &&
|
|
24910
|
+
(this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
24911
|
+
this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused)) {
|
|
24871
24912
|
this._start();
|
|
24872
24913
|
}
|
|
24873
24914
|
else {
|
|
@@ -24921,7 +24962,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
24921
24962
|
return;
|
|
24922
24963
|
let hasFocus = true;
|
|
24923
24964
|
try {
|
|
24924
|
-
hasFocus =
|
|
24965
|
+
hasFocus =
|
|
24966
|
+
typeof document.hasFocus === 'function' ? document.hasFocus() : true;
|
|
24925
24967
|
}
|
|
24926
24968
|
catch (_e) {
|
|
24927
24969
|
hasFocus = true;
|
|
@@ -24943,7 +24985,9 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
24943
24985
|
// stop, leaving the buffer with no FullSnapshot and silently breaking
|
|
24944
24986
|
// exception-triggered flushBuffer. `_recorder.restart(null, ...)` passes
|
|
24945
24987
|
// null explicitly, so it's safe regardless of `this.sessionId`.
|
|
24946
|
-
if (!this._crashBuffer ||
|
|
24988
|
+
if (!this._crashBuffer ||
|
|
24989
|
+
!((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) ||
|
|
24990
|
+
this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.stopped) {
|
|
24947
24991
|
return;
|
|
24948
24992
|
}
|
|
24949
24993
|
void this._recorder.restart(null, _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL);
|
|
@@ -24992,9 +25036,13 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
24992
25036
|
* @param session - the session to start
|
|
24993
25037
|
*/
|
|
24994
25038
|
start(type = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL, session) {
|
|
25039
|
+
if (this._startInProgress)
|
|
25040
|
+
return;
|
|
24995
25041
|
this._checkOperation('start');
|
|
25042
|
+
this._startInProgress = true;
|
|
24996
25043
|
// If continuous recording is disabled, force plain mode
|
|
24997
|
-
if (type === _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.CONTINUOUS &&
|
|
25044
|
+
if (type === _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.CONTINUOUS &&
|
|
25045
|
+
!this._configs.showContinuousRecording) {
|
|
24998
25046
|
type = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL;
|
|
24999
25047
|
}
|
|
25000
25048
|
this.sessionType = type;
|
|
@@ -25025,7 +25073,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25025
25073
|
stoppedAt: this._recorder.stoppedAt,
|
|
25026
25074
|
};
|
|
25027
25075
|
const response = await this._apiService.stopSession(sid, request);
|
|
25028
|
-
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.
|
|
25076
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_READY_EVENT_LEGACY, response._id);
|
|
25077
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_READY_EVENT, response);
|
|
25029
25078
|
}
|
|
25030
25079
|
}
|
|
25031
25080
|
catch (error) {
|
|
@@ -25121,8 +25170,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25121
25170
|
this.error = (e === null || e === void 0 ? void 0 : e.message) || 'Failed to capture exception';
|
|
25122
25171
|
}
|
|
25123
25172
|
}
|
|
25124
|
-
async _flushBuffer(
|
|
25125
|
-
if (!
|
|
25173
|
+
async _flushBuffer(session, force = false) {
|
|
25174
|
+
if (!session || !this._crashBuffer || this._isFlushingBuffer) {
|
|
25126
25175
|
return null;
|
|
25127
25176
|
}
|
|
25128
25177
|
this._isFlushingBuffer = true;
|
|
@@ -25133,8 +25182,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25133
25182
|
}
|
|
25134
25183
|
await Promise.all([
|
|
25135
25184
|
this._tracer.exportTraces(spans.map((s) => s.span)),
|
|
25136
|
-
this._apiService.exportEvents(
|
|
25137
|
-
|
|
25185
|
+
this._apiService.exportEvents(session._id, {
|
|
25186
|
+
events: events.map((e) => e.event),
|
|
25187
|
+
}),
|
|
25188
|
+
this._apiService.updateSessionAttributes(session._id, {
|
|
25138
25189
|
startedAt: this._toCrashBufferSessionIso(startedAt),
|
|
25139
25190
|
stoppedAt: this._toCrashBufferSessionIso(stoppedAt),
|
|
25140
25191
|
sessionAttributes: this.sessionAttributes,
|
|
@@ -25149,7 +25200,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25149
25200
|
finally {
|
|
25150
25201
|
await this._crashBuffer.clear();
|
|
25151
25202
|
this._isFlushingBuffer = false;
|
|
25152
|
-
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.
|
|
25203
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_READY_EVENT_LEGACY, session._id);
|
|
25204
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_READY_EVENT, session);
|
|
25153
25205
|
}
|
|
25154
25206
|
}
|
|
25155
25207
|
_toCrashBufferSessionIso(ts) {
|
|
@@ -25238,7 +25290,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25238
25290
|
* Handle the safe stop event
|
|
25239
25291
|
*/
|
|
25240
25292
|
_handleStop(comment) {
|
|
25241
|
-
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
25293
|
+
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
25294
|
+
this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused) {
|
|
25242
25295
|
this.stop(comment);
|
|
25243
25296
|
}
|
|
25244
25297
|
}
|
|
@@ -25262,7 +25315,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25262
25315
|
* Handle the safe cancel event
|
|
25263
25316
|
*/
|
|
25264
25317
|
_handleCancel() {
|
|
25265
|
-
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
25318
|
+
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started ||
|
|
25319
|
+
this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused) {
|
|
25266
25320
|
this.cancel();
|
|
25267
25321
|
}
|
|
25268
25322
|
}
|
|
@@ -25270,7 +25324,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25270
25324
|
* Handle the safe save event
|
|
25271
25325
|
*/
|
|
25272
25326
|
_handleSave() {
|
|
25273
|
-
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started &&
|
|
25327
|
+
if (this.sessionState === _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started &&
|
|
25328
|
+
this.continuousRecording) {
|
|
25274
25329
|
this.save();
|
|
25275
25330
|
}
|
|
25276
25331
|
}
|
|
@@ -25315,12 +25370,12 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25315
25370
|
}
|
|
25316
25371
|
});
|
|
25317
25372
|
this._socketService.on(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_SAVE_BUFFER_EVENT, (payload) => {
|
|
25318
|
-
var _a, _b
|
|
25373
|
+
var _a, _b;
|
|
25319
25374
|
if (this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.stopped)
|
|
25320
25375
|
return;
|
|
25321
|
-
this._flushBuffer(
|
|
25322
|
-
if ((
|
|
25323
|
-
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_AUTO_CREATED, (
|
|
25376
|
+
this._flushBuffer(payload === null || payload === void 0 ? void 0 : payload.debugSession, true);
|
|
25377
|
+
if ((_a = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _a === void 0 ? void 0 : _a.url) {
|
|
25378
|
+
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_AUTO_CREATED, (_b = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _b === void 0 ? void 0 : _b.url);
|
|
25324
25379
|
}
|
|
25325
25380
|
});
|
|
25326
25381
|
}
|
|
@@ -25328,7 +25383,7 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25328
25383
|
try {
|
|
25329
25384
|
const session = await this._apiService.createErrorSession({ span });
|
|
25330
25385
|
if (session === null || session === void 0 ? void 0 : session._id) {
|
|
25331
|
-
this._flushBuffer(session
|
|
25386
|
+
this._flushBuffer(session);
|
|
25332
25387
|
}
|
|
25333
25388
|
if (session === null || session === void 0 ? void 0 : session.url) {
|
|
25334
25389
|
_eventBus__WEBPACK_IMPORTED_MODULE_8__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_6__.SESSION_AUTO_CREATED, session.url);
|
|
@@ -25348,18 +25403,25 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25348
25403
|
sessionAttributes: this.sessionAttributes,
|
|
25349
25404
|
resourceAttributes: (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getNavigatorInfo)(),
|
|
25350
25405
|
name: this._getSessionName(),
|
|
25351
|
-
...(this._userAttributes
|
|
25406
|
+
...(this._userAttributes
|
|
25407
|
+
? { userAttributes: this._userAttributes }
|
|
25408
|
+
: {}),
|
|
25352
25409
|
};
|
|
25353
|
-
const request = !this.continuousRecording
|
|
25410
|
+
const request = !this.continuousRecording
|
|
25411
|
+
? payload
|
|
25412
|
+
: { debugSessionData: payload };
|
|
25354
25413
|
const session = this.continuousRecording
|
|
25355
25414
|
? await this._apiService.startContinuousDebugSession(request, signal)
|
|
25356
25415
|
: await this._apiService.startSession(request, signal);
|
|
25357
25416
|
if (session) {
|
|
25358
|
-
session.sessionType = this.continuousRecording
|
|
25417
|
+
session.sessionType = this.continuousRecording
|
|
25418
|
+
? _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.CONTINUOUS
|
|
25419
|
+
: _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionType.MANUAL;
|
|
25359
25420
|
this._setupSessionAndStart(session, false);
|
|
25360
25421
|
}
|
|
25361
25422
|
}
|
|
25362
25423
|
catch (error) {
|
|
25424
|
+
this._startInProgress = false;
|
|
25363
25425
|
this.error = error.message;
|
|
25364
25426
|
this.sessionState = _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.stopped;
|
|
25365
25427
|
if (this.continuousRecording) {
|
|
@@ -25372,13 +25434,17 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25372
25434
|
*/
|
|
25373
25435
|
_start() {
|
|
25374
25436
|
var _a, _b;
|
|
25437
|
+
this._startInProgress = false;
|
|
25375
25438
|
this.sessionState = _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started;
|
|
25376
25439
|
// eslint-disable-next-line no-self-assign
|
|
25377
25440
|
this.sessionType = this.sessionType;
|
|
25378
25441
|
this._tracer.start(this.sessionId, this.sessionType);
|
|
25379
25442
|
// Ensure we switch from buffer-only recording to session recording cleanly.
|
|
25380
25443
|
void this._recorder.restart(this.sessionId, this.sessionType);
|
|
25381
|
-
this._navigationRecorder.start({
|
|
25444
|
+
this._navigationRecorder.start({
|
|
25445
|
+
sessionId: this.sessionId,
|
|
25446
|
+
sessionType: this.sessionType,
|
|
25447
|
+
});
|
|
25382
25448
|
if (this.session) {
|
|
25383
25449
|
this._socketService.subscribeToSession(this.session);
|
|
25384
25450
|
this._sessionWidget.seconds = (0,_utils__WEBPACK_IMPORTED_MODULE_4__.getTimeDifferenceInSeconds)((_a = this.session) === null || _a === void 0 ? void 0 : _a.startedAt);
|
|
@@ -25406,8 +25472,12 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25406
25472
|
// rrweb assigns new node IDs on each record() call, so the next buffer
|
|
25407
25473
|
// segment must not carry events from the previous generation. Await the
|
|
25408
25474
|
// clear so its IDB tx can't race past the fresh FullSnapshot.
|
|
25409
|
-
const cleared = this._crashBuffer
|
|
25410
|
-
|
|
25475
|
+
const cleared = this._crashBuffer
|
|
25476
|
+
? this._crashBuffer.clear()
|
|
25477
|
+
: Promise.resolve();
|
|
25478
|
+
void cleared
|
|
25479
|
+
.catch(() => undefined)
|
|
25480
|
+
.then(() => this._startBufferOnlyRecording());
|
|
25411
25481
|
}
|
|
25412
25482
|
/**
|
|
25413
25483
|
* Pause the session tracing and recording
|
|
@@ -25443,7 +25513,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25443
25513
|
* @param sessionId - the session ID to set or clear
|
|
25444
25514
|
*/
|
|
25445
25515
|
_setSession(session) {
|
|
25446
|
-
this.session = {
|
|
25516
|
+
this.session = {
|
|
25517
|
+
...session,
|
|
25518
|
+
startedAt: session.startedAt || new Date().toISOString(),
|
|
25519
|
+
};
|
|
25447
25520
|
this.sessionId = (session === null || session === void 0 ? void 0 : session.shortId) || (session === null || session === void 0 ? void 0 : session._id);
|
|
25448
25521
|
}
|
|
25449
25522
|
_clearSession() {
|
|
@@ -25466,7 +25539,8 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25466
25539
|
}
|
|
25467
25540
|
break;
|
|
25468
25541
|
case 'stop':
|
|
25469
|
-
if (this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused &&
|
|
25542
|
+
if (this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.paused &&
|
|
25543
|
+
this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_5__.SessionState.started) {
|
|
25470
25544
|
throw new Error('Cannot stop. Session is not currently started.');
|
|
25471
25545
|
}
|
|
25472
25546
|
break;
|
|
@@ -25544,7 +25618,10 @@ class SessionRecorder extends _observable__WEBPACK_IMPORTED_MODULE_0__.Observabl
|
|
|
25544
25618
|
*/
|
|
25545
25619
|
_getSessionName(date = new Date()) {
|
|
25546
25620
|
var _a, _b, _c;
|
|
25547
|
-
const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) ||
|
|
25621
|
+
const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) ||
|
|
25622
|
+
((_b = this._userAttributes) === null || _b === void 0 ? void 0 : _b.userName) ||
|
|
25623
|
+
((_c = this._userAttributes) === null || _c === void 0 ? void 0 : _c.name) ||
|
|
25624
|
+
'';
|
|
25548
25625
|
return userName
|
|
25549
25626
|
? `${userName}'s session on ${(0,_utils__WEBPACK_IMPORTED_MODULE_4__.getFormattedDate)(date, { month: 'short', day: 'numeric' })}`
|
|
25550
25627
|
: `Session on ${(0,_utils__WEBPACK_IMPORTED_MODULE_4__.getFormattedDate)(date)}`;
|
package/dist/listeners.js
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import messagingService from './services/messaging.service';
|
|
2
|
-
import {
|
|
2
|
+
import { PACKAGE_VERSION_EXPORT } from './config/constants';
|
|
3
|
+
import { SessionType, } from '@multiplayer-app/session-recorder-common';
|
|
3
4
|
export function setupListeners(sessionRecorder) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const announceState = () => {
|
|
6
|
+
// Send continuous flag first so it is applied before the consumer reacts to
|
|
7
|
+
// the 'ready' message (postMessage preserves delivery order).
|
|
8
|
+
messagingService.sendMessage('continuous-debugging', sessionRecorder.continuousRecording);
|
|
9
|
+
messagingService.sendMessage('ready', {
|
|
10
|
+
session: sessionRecorder.session,
|
|
11
|
+
sessionState: sessionRecorder.sessionState,
|
|
12
|
+
// Lets the extension popup detect libs too old to support on-demand state
|
|
13
|
+
// sync (the `get-state` request below).
|
|
14
|
+
version: PACKAGE_VERSION_EXPORT,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
// Announce current state on load...
|
|
18
|
+
announceState();
|
|
19
|
+
// ...and whenever the extension asks (e.g. the popup was opened after the
|
|
20
|
+
// recorder had already initialized, so it missed the initial announcement).
|
|
21
|
+
messagingService.on('get-state', () => {
|
|
22
|
+
announceState();
|
|
8
23
|
});
|
|
9
24
|
messagingService.on('init', (payload) => {
|
|
10
25
|
sessionRecorder.init(payload);
|
|
@@ -15,6 +30,9 @@ export function setupListeners(sessionRecorder) {
|
|
|
15
30
|
messagingService.on('end', (payload) => {
|
|
16
31
|
sessionRecorder.stop(payload);
|
|
17
32
|
});
|
|
33
|
+
messagingService.on('stop', (payload) => {
|
|
34
|
+
sessionRecorder.stop(payload);
|
|
35
|
+
});
|
|
18
36
|
messagingService.on('pause', () => {
|
|
19
37
|
sessionRecorder.pause();
|
|
20
38
|
});
|
|
@@ -27,6 +27,7 @@ export declare class SessionRecorder extends Observable<SessionRecorderEvents> i
|
|
|
27
27
|
get sessionType(): SessionType;
|
|
28
28
|
set sessionType(sessionType: SessionType);
|
|
29
29
|
get continuousRecording(): boolean;
|
|
30
|
+
private _startInProgress;
|
|
30
31
|
private _sessionState;
|
|
31
32
|
get sessionState(): SessionState | null;
|
|
32
33
|
set sessionState(state: SessionState | null);
|
package/dist/session-recorder.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Observable } from './observable';
|
|
2
|
-
import { SessionType } from '@multiplayer-app/session-recorder-common';
|
|
2
|
+
import { SessionType, } from '@multiplayer-app/session-recorder-common';
|
|
3
3
|
import { TracerBrowserSDK } from './otel';
|
|
4
4
|
import { RecorderBrowserSDK } from './rrweb';
|
|
5
5
|
import { getStoredItem, setStoredItem, getNavigatorInfo, getFormattedDate, getTimeDifferenceInSeconds, isSessionActive, getOrCreateTabId, } from './utils';
|
|
6
|
-
import { SessionState } from './types';
|
|
7
|
-
import { BASE_CONFIG, SESSION_PROP_NAME, SESSION_ID_PROP_NAME, SESSION_TYPE_PROP_NAME, SESSION_STATE_PROP_NAME, DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE, getSessionRecorderConfig, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_SAVE_BUFFER_EVENT, SESSION_READY_EVENT, } from './config';
|
|
8
|
-
import { setShouldRecordHttpData, setMaxCapturingHttpPayloadSize } from './patch';
|
|
6
|
+
import { SessionState, } from './types';
|
|
7
|
+
import { BASE_CONFIG, SESSION_PROP_NAME, SESSION_ID_PROP_NAME, SESSION_TYPE_PROP_NAME, SESSION_STATE_PROP_NAME, DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE, getSessionRecorderConfig, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_SAVE_BUFFER_EVENT, SESSION_READY_EVENT_LEGACY, SESSION_READY_EVENT, } from './config';
|
|
8
|
+
import { setShouldRecordHttpData, setMaxCapturingHttpPayloadSize, } from './patch';
|
|
9
9
|
import { recorderEventBus } from './eventBus';
|
|
10
10
|
import { SessionWidget } from './sessionWidget';
|
|
11
11
|
import messagingService from './services/messaging.service';
|
|
12
|
-
import { ApiService } from './services/api.service';
|
|
12
|
+
import { ApiService, } from './services/api.service';
|
|
13
13
|
import { SocketService } from './services/socket.service';
|
|
14
14
|
import { IndexedDBService } from './services/indexedDb.service';
|
|
15
15
|
import { CrashBufferService } from './services/crashBuffer.service';
|
|
@@ -51,7 +51,10 @@ export class SessionRecorder extends Observable {
|
|
|
51
51
|
messagingService.sendMessage('state-change', this._sessionState);
|
|
52
52
|
setStoredItem(SESSION_STATE_PROP_NAME, state);
|
|
53
53
|
// Emit observable event to support React wrapper
|
|
54
|
-
this.emit('state-change', [
|
|
54
|
+
this.emit('state-change', [
|
|
55
|
+
this._sessionState || SessionState.stopped,
|
|
56
|
+
this.sessionType,
|
|
57
|
+
]);
|
|
55
58
|
}
|
|
56
59
|
get session() {
|
|
57
60
|
return this._session;
|
|
@@ -112,6 +115,10 @@ export class SessionRecorder extends Observable {
|
|
|
112
115
|
// Session ID and state are stored in sessionStorage (with fallback) to avoid multi-tab conflicts
|
|
113
116
|
this._sessionId = null;
|
|
114
117
|
this._sessionType = SessionType.MANUAL;
|
|
118
|
+
// Guards against overlapping start() calls. `_checkOperation('start')` only
|
|
119
|
+
// rejects once sessionState is `started`, but `_createSessionAndStart` is
|
|
120
|
+
// async, so two rapid starts could both pass and create duplicate sessions.
|
|
121
|
+
this._startInProgress = false;
|
|
115
122
|
this._sessionState = null;
|
|
116
123
|
this._session = null;
|
|
117
124
|
this._sessionAttributes = null;
|
|
@@ -122,10 +129,18 @@ export class SessionRecorder extends Observable {
|
|
|
122
129
|
this._error = '';
|
|
123
130
|
// Safety: avoid accessing storage in SSR/non-browser environments
|
|
124
131
|
const isBrowser = typeof window !== 'undefined';
|
|
125
|
-
const sessionLocal = isBrowser
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
132
|
+
const sessionLocal = isBrowser
|
|
133
|
+
? getStoredItem(SESSION_PROP_NAME, true)
|
|
134
|
+
: null;
|
|
135
|
+
const sessionIdLocal = isBrowser
|
|
136
|
+
? getStoredItem(SESSION_ID_PROP_NAME)
|
|
137
|
+
: null;
|
|
138
|
+
const sessionStateLocal = isBrowser
|
|
139
|
+
? getStoredItem(SESSION_STATE_PROP_NAME)
|
|
140
|
+
: null;
|
|
141
|
+
const sessionTypeLocal = isBrowser
|
|
142
|
+
? getStoredItem(SESSION_TYPE_PROP_NAME)
|
|
143
|
+
: null;
|
|
129
144
|
if (isSessionActive(sessionLocal, sessionTypeLocal)) {
|
|
130
145
|
this.session = sessionLocal;
|
|
131
146
|
this.sessionId = sessionIdLocal;
|
|
@@ -157,7 +172,8 @@ export class SessionRecorder extends Observable {
|
|
|
157
172
|
// GC: remove orphaned crash buffers from old tabs.
|
|
158
173
|
// Keep TTL large to avoid any accidental data loss.
|
|
159
174
|
void this._bufferDb.sweepStaleTabs(10 * 60 * 60 * 1000);
|
|
160
|
-
setMaxCapturingHttpPayloadSize(this._configs.maxCapturingHttpPayloadSize ||
|
|
175
|
+
setMaxCapturingHttpPayloadSize(this._configs.maxCapturingHttpPayloadSize ||
|
|
176
|
+
DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE);
|
|
161
177
|
setShouldRecordHttpData(this._configs.captureBody, this._configs.captureHeaders);
|
|
162
178
|
this._setupCrashBuffer();
|
|
163
179
|
this._tracer.init(this._configs);
|
|
@@ -179,7 +195,9 @@ export class SessionRecorder extends Observable {
|
|
|
179
195
|
if (this._configs.apiKey) {
|
|
180
196
|
this._recorder.init(this._configs, this._socketService);
|
|
181
197
|
}
|
|
182
|
-
if (this.sessionId &&
|
|
198
|
+
if (this.sessionId &&
|
|
199
|
+
(this.sessionState === SessionState.started ||
|
|
200
|
+
this.sessionState === SessionState.paused)) {
|
|
183
201
|
this._start();
|
|
184
202
|
}
|
|
185
203
|
else {
|
|
@@ -233,7 +251,8 @@ export class SessionRecorder extends Observable {
|
|
|
233
251
|
return;
|
|
234
252
|
let hasFocus = true;
|
|
235
253
|
try {
|
|
236
|
-
hasFocus =
|
|
254
|
+
hasFocus =
|
|
255
|
+
typeof document.hasFocus === 'function' ? document.hasFocus() : true;
|
|
237
256
|
}
|
|
238
257
|
catch (_e) {
|
|
239
258
|
hasFocus = true;
|
|
@@ -255,7 +274,9 @@ export class SessionRecorder extends Observable {
|
|
|
255
274
|
// stop, leaving the buffer with no FullSnapshot and silently breaking
|
|
256
275
|
// exception-triggered flushBuffer. `_recorder.restart(null, ...)` passes
|
|
257
276
|
// null explicitly, so it's safe regardless of `this.sessionId`.
|
|
258
|
-
if (!this._crashBuffer ||
|
|
277
|
+
if (!this._crashBuffer ||
|
|
278
|
+
!((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) ||
|
|
279
|
+
this.sessionState !== SessionState.stopped) {
|
|
259
280
|
return;
|
|
260
281
|
}
|
|
261
282
|
void this._recorder.restart(null, SessionType.MANUAL);
|
|
@@ -304,9 +325,13 @@ export class SessionRecorder extends Observable {
|
|
|
304
325
|
* @param session - the session to start
|
|
305
326
|
*/
|
|
306
327
|
start(type = SessionType.MANUAL, session) {
|
|
328
|
+
if (this._startInProgress)
|
|
329
|
+
return;
|
|
307
330
|
this._checkOperation('start');
|
|
331
|
+
this._startInProgress = true;
|
|
308
332
|
// If continuous recording is disabled, force plain mode
|
|
309
|
-
if (type === SessionType.CONTINUOUS &&
|
|
333
|
+
if (type === SessionType.CONTINUOUS &&
|
|
334
|
+
!this._configs.showContinuousRecording) {
|
|
310
335
|
type = SessionType.MANUAL;
|
|
311
336
|
}
|
|
312
337
|
this.sessionType = type;
|
|
@@ -337,7 +362,8 @@ export class SessionRecorder extends Observable {
|
|
|
337
362
|
stoppedAt: this._recorder.stoppedAt,
|
|
338
363
|
};
|
|
339
364
|
const response = await this._apiService.stopSession(sid, request);
|
|
340
|
-
recorderEventBus.emit(
|
|
365
|
+
recorderEventBus.emit(SESSION_READY_EVENT_LEGACY, response._id);
|
|
366
|
+
recorderEventBus.emit(SESSION_READY_EVENT, response);
|
|
341
367
|
}
|
|
342
368
|
}
|
|
343
369
|
catch (error) {
|
|
@@ -433,8 +459,8 @@ export class SessionRecorder extends Observable {
|
|
|
433
459
|
this.error = (e === null || e === void 0 ? void 0 : e.message) || 'Failed to capture exception';
|
|
434
460
|
}
|
|
435
461
|
}
|
|
436
|
-
async _flushBuffer(
|
|
437
|
-
if (!
|
|
462
|
+
async _flushBuffer(session, force = false) {
|
|
463
|
+
if (!session || !this._crashBuffer || this._isFlushingBuffer) {
|
|
438
464
|
return null;
|
|
439
465
|
}
|
|
440
466
|
this._isFlushingBuffer = true;
|
|
@@ -445,8 +471,10 @@ export class SessionRecorder extends Observable {
|
|
|
445
471
|
}
|
|
446
472
|
await Promise.all([
|
|
447
473
|
this._tracer.exportTraces(spans.map((s) => s.span)),
|
|
448
|
-
this._apiService.exportEvents(
|
|
449
|
-
|
|
474
|
+
this._apiService.exportEvents(session._id, {
|
|
475
|
+
events: events.map((e) => e.event),
|
|
476
|
+
}),
|
|
477
|
+
this._apiService.updateSessionAttributes(session._id, {
|
|
450
478
|
startedAt: this._toCrashBufferSessionIso(startedAt),
|
|
451
479
|
stoppedAt: this._toCrashBufferSessionIso(stoppedAt),
|
|
452
480
|
sessionAttributes: this.sessionAttributes,
|
|
@@ -461,7 +489,8 @@ export class SessionRecorder extends Observable {
|
|
|
461
489
|
finally {
|
|
462
490
|
await this._crashBuffer.clear();
|
|
463
491
|
this._isFlushingBuffer = false;
|
|
464
|
-
recorderEventBus.emit(
|
|
492
|
+
recorderEventBus.emit(SESSION_READY_EVENT_LEGACY, session._id);
|
|
493
|
+
recorderEventBus.emit(SESSION_READY_EVENT, session);
|
|
465
494
|
}
|
|
466
495
|
}
|
|
467
496
|
_toCrashBufferSessionIso(ts) {
|
|
@@ -550,7 +579,8 @@ export class SessionRecorder extends Observable {
|
|
|
550
579
|
* Handle the safe stop event
|
|
551
580
|
*/
|
|
552
581
|
_handleStop(comment) {
|
|
553
|
-
if (this.sessionState === SessionState.started ||
|
|
582
|
+
if (this.sessionState === SessionState.started ||
|
|
583
|
+
this.sessionState === SessionState.paused) {
|
|
554
584
|
this.stop(comment);
|
|
555
585
|
}
|
|
556
586
|
}
|
|
@@ -574,7 +604,8 @@ export class SessionRecorder extends Observable {
|
|
|
574
604
|
* Handle the safe cancel event
|
|
575
605
|
*/
|
|
576
606
|
_handleCancel() {
|
|
577
|
-
if (this.sessionState === SessionState.started ||
|
|
607
|
+
if (this.sessionState === SessionState.started ||
|
|
608
|
+
this.sessionState === SessionState.paused) {
|
|
578
609
|
this.cancel();
|
|
579
610
|
}
|
|
580
611
|
}
|
|
@@ -582,7 +613,8 @@ export class SessionRecorder extends Observable {
|
|
|
582
613
|
* Handle the safe save event
|
|
583
614
|
*/
|
|
584
615
|
_handleSave() {
|
|
585
|
-
if (this.sessionState === SessionState.started &&
|
|
616
|
+
if (this.sessionState === SessionState.started &&
|
|
617
|
+
this.continuousRecording) {
|
|
586
618
|
this.save();
|
|
587
619
|
}
|
|
588
620
|
}
|
|
@@ -627,12 +659,12 @@ export class SessionRecorder extends Observable {
|
|
|
627
659
|
}
|
|
628
660
|
});
|
|
629
661
|
this._socketService.on(SESSION_SAVE_BUFFER_EVENT, (payload) => {
|
|
630
|
-
var _a, _b
|
|
662
|
+
var _a, _b;
|
|
631
663
|
if (this.sessionState !== SessionState.stopped)
|
|
632
664
|
return;
|
|
633
|
-
this._flushBuffer(
|
|
634
|
-
if ((
|
|
635
|
-
recorderEventBus.emit(SESSION_AUTO_CREATED, (
|
|
665
|
+
this._flushBuffer(payload === null || payload === void 0 ? void 0 : payload.debugSession, true);
|
|
666
|
+
if ((_a = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _a === void 0 ? void 0 : _a.url) {
|
|
667
|
+
recorderEventBus.emit(SESSION_AUTO_CREATED, (_b = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _b === void 0 ? void 0 : _b.url);
|
|
636
668
|
}
|
|
637
669
|
});
|
|
638
670
|
}
|
|
@@ -640,7 +672,7 @@ export class SessionRecorder extends Observable {
|
|
|
640
672
|
try {
|
|
641
673
|
const session = await this._apiService.createErrorSession({ span });
|
|
642
674
|
if (session === null || session === void 0 ? void 0 : session._id) {
|
|
643
|
-
this._flushBuffer(session
|
|
675
|
+
this._flushBuffer(session);
|
|
644
676
|
}
|
|
645
677
|
if (session === null || session === void 0 ? void 0 : session.url) {
|
|
646
678
|
recorderEventBus.emit(SESSION_AUTO_CREATED, session.url);
|
|
@@ -660,18 +692,25 @@ export class SessionRecorder extends Observable {
|
|
|
660
692
|
sessionAttributes: this.sessionAttributes,
|
|
661
693
|
resourceAttributes: getNavigatorInfo(),
|
|
662
694
|
name: this._getSessionName(),
|
|
663
|
-
...(this._userAttributes
|
|
695
|
+
...(this._userAttributes
|
|
696
|
+
? { userAttributes: this._userAttributes }
|
|
697
|
+
: {}),
|
|
664
698
|
};
|
|
665
|
-
const request = !this.continuousRecording
|
|
699
|
+
const request = !this.continuousRecording
|
|
700
|
+
? payload
|
|
701
|
+
: { debugSessionData: payload };
|
|
666
702
|
const session = this.continuousRecording
|
|
667
703
|
? await this._apiService.startContinuousDebugSession(request, signal)
|
|
668
704
|
: await this._apiService.startSession(request, signal);
|
|
669
705
|
if (session) {
|
|
670
|
-
session.sessionType = this.continuousRecording
|
|
706
|
+
session.sessionType = this.continuousRecording
|
|
707
|
+
? SessionType.CONTINUOUS
|
|
708
|
+
: SessionType.MANUAL;
|
|
671
709
|
this._setupSessionAndStart(session, false);
|
|
672
710
|
}
|
|
673
711
|
}
|
|
674
712
|
catch (error) {
|
|
713
|
+
this._startInProgress = false;
|
|
675
714
|
this.error = error.message;
|
|
676
715
|
this.sessionState = SessionState.stopped;
|
|
677
716
|
if (this.continuousRecording) {
|
|
@@ -684,13 +723,17 @@ export class SessionRecorder extends Observable {
|
|
|
684
723
|
*/
|
|
685
724
|
_start() {
|
|
686
725
|
var _a, _b;
|
|
726
|
+
this._startInProgress = false;
|
|
687
727
|
this.sessionState = SessionState.started;
|
|
688
728
|
// eslint-disable-next-line no-self-assign
|
|
689
729
|
this.sessionType = this.sessionType;
|
|
690
730
|
this._tracer.start(this.sessionId, this.sessionType);
|
|
691
731
|
// Ensure we switch from buffer-only recording to session recording cleanly.
|
|
692
732
|
void this._recorder.restart(this.sessionId, this.sessionType);
|
|
693
|
-
this._navigationRecorder.start({
|
|
733
|
+
this._navigationRecorder.start({
|
|
734
|
+
sessionId: this.sessionId,
|
|
735
|
+
sessionType: this.sessionType,
|
|
736
|
+
});
|
|
694
737
|
if (this.session) {
|
|
695
738
|
this._socketService.subscribeToSession(this.session);
|
|
696
739
|
this._sessionWidget.seconds = getTimeDifferenceInSeconds((_a = this.session) === null || _a === void 0 ? void 0 : _a.startedAt);
|
|
@@ -718,8 +761,12 @@ export class SessionRecorder extends Observable {
|
|
|
718
761
|
// rrweb assigns new node IDs on each record() call, so the next buffer
|
|
719
762
|
// segment must not carry events from the previous generation. Await the
|
|
720
763
|
// clear so its IDB tx can't race past the fresh FullSnapshot.
|
|
721
|
-
const cleared = this._crashBuffer
|
|
722
|
-
|
|
764
|
+
const cleared = this._crashBuffer
|
|
765
|
+
? this._crashBuffer.clear()
|
|
766
|
+
: Promise.resolve();
|
|
767
|
+
void cleared
|
|
768
|
+
.catch(() => undefined)
|
|
769
|
+
.then(() => this._startBufferOnlyRecording());
|
|
723
770
|
}
|
|
724
771
|
/**
|
|
725
772
|
* Pause the session tracing and recording
|
|
@@ -755,7 +802,10 @@ export class SessionRecorder extends Observable {
|
|
|
755
802
|
* @param sessionId - the session ID to set or clear
|
|
756
803
|
*/
|
|
757
804
|
_setSession(session) {
|
|
758
|
-
this.session = {
|
|
805
|
+
this.session = {
|
|
806
|
+
...session,
|
|
807
|
+
startedAt: session.startedAt || new Date().toISOString(),
|
|
808
|
+
};
|
|
759
809
|
this.sessionId = (session === null || session === void 0 ? void 0 : session.shortId) || (session === null || session === void 0 ? void 0 : session._id);
|
|
760
810
|
}
|
|
761
811
|
_clearSession() {
|
|
@@ -778,7 +828,8 @@ export class SessionRecorder extends Observable {
|
|
|
778
828
|
}
|
|
779
829
|
break;
|
|
780
830
|
case 'stop':
|
|
781
|
-
if (this.sessionState !== SessionState.paused &&
|
|
831
|
+
if (this.sessionState !== SessionState.paused &&
|
|
832
|
+
this.sessionState !== SessionState.started) {
|
|
782
833
|
throw new Error('Cannot stop. Session is not currently started.');
|
|
783
834
|
}
|
|
784
835
|
break;
|
|
@@ -856,7 +907,10 @@ export class SessionRecorder extends Observable {
|
|
|
856
907
|
*/
|
|
857
908
|
_getSessionName(date = new Date()) {
|
|
858
909
|
var _a, _b, _c;
|
|
859
|
-
const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) ||
|
|
910
|
+
const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) ||
|
|
911
|
+
((_b = this._userAttributes) === null || _b === void 0 ? void 0 : _b.userName) ||
|
|
912
|
+
((_c = this._userAttributes) === null || _c === void 0 ? void 0 : _c.name) ||
|
|
913
|
+
'';
|
|
860
914
|
return userName
|
|
861
915
|
? `${userName}'s session on ${getFormattedDate(date, { month: 'short', day: 'numeric' })}`
|
|
862
916
|
: `Session on ${getFormattedDate(date)}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplayer-app/session-recorder-browser",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.89",
|
|
4
4
|
"description": "Multiplayer Fullstack Session Recorder for Browser",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Multiplayer Software, Inc.",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"webpack-cli": "5.1.4"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@multiplayer-app/session-recorder-common": "2.0.
|
|
78
|
+
"@multiplayer-app/session-recorder-common": "2.0.89",
|
|
79
79
|
"@opentelemetry/core": "2.0.1",
|
|
80
80
|
"@opentelemetry/exporter-trace-otlp-http": "0.203.0",
|
|
81
81
|
"@opentelemetry/instrumentation": "0.203.0",
|