@multiplayer-app/session-recorder-browser 2.0.38 → 2.0.40

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.
@@ -25163,7 +25163,7 @@ const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
25163
25163
  const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
25164
25164
  const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
25165
25165
  const REMOTE_SESSION_RECORDING_STOP = 'remote-session-recording:stop';
25166
- const PACKAGE_VERSION_EXPORT = "2.0.38" || 0;
25166
+ const PACKAGE_VERSION_EXPORT = "2.0.40" || 0;
25167
25167
  // Regex patterns for OpenTelemetry ignore URLs
25168
25168
  const OTEL_IGNORE_URLS = [
25169
25169
  // Traces endpoint
@@ -26907,6 +26907,7 @@ __webpack_require__.r(__webpack_exports__);
26907
26907
 
26908
26908
  class RecorderBrowserSDK {
26909
26909
  constructor() {
26910
+ this.generation = 0;
26910
26911
  this.intervals = {
26911
26912
  restart: null,
26912
26913
  bufferSnapshot: null,
@@ -26942,12 +26943,15 @@ class RecorderBrowserSDK {
26942
26943
  throw new Error('Configuration not initialized. Call init() before start().');
26943
26944
  }
26944
26945
  this.startedAt = new Date().toISOString();
26946
+ const gen = ++this.generation;
26945
26947
  this.stopFn = (0,rrweb__WEBPACK_IMPORTED_MODULE_6__.record)({
26946
26948
  ...this._buildRecordOptions(),
26947
26949
  emit: async (event) => {
26950
+ if (gen !== this.generation)
26951
+ return;
26948
26952
  const ts = event.timestamp;
26949
26953
  if (!sessionId) {
26950
- await this._handleBufferOnlyEvent(event, ts);
26954
+ await this._handleBufferOnlyEvent(event, ts, gen);
26951
26955
  return;
26952
26956
  }
26953
26957
  this._handleLiveSessionEvent(event, ts, sessionId, sessionType);
@@ -26963,6 +26967,7 @@ class RecorderBrowserSDK {
26963
26967
  var _a;
26964
26968
  try {
26965
26969
  (_a = this.stopFn) === null || _a === void 0 ? void 0 : _a.call(this);
26970
+ this.stopFn = undefined;
26966
26971
  this.start(sessionId, sessionType);
26967
26972
  }
26968
26973
  catch (_e) {
@@ -26976,6 +26981,7 @@ class RecorderBrowserSDK {
26976
26981
  var _a, _b, _c;
26977
26982
  (_a = this.stopFn) === null || _a === void 0 ? void 0 : _a.call(this);
26978
26983
  this.stopFn = undefined;
26984
+ this.generation++;
26979
26985
  if (!((_b = this.config) === null || _b === void 0 ? void 0 : _b.useWebsocket)) {
26980
26986
  (_c = this.socketService) === null || _c === void 0 ? void 0 : _c.close();
26981
26987
  }
@@ -27015,13 +27021,15 @@ class RecorderBrowserSDK {
27015
27021
  * @param event - Event.
27016
27022
  * @param ts - Timestamp.
27017
27023
  */
27018
- async _handleBufferOnlyEvent(event, ts) {
27024
+ async _handleBufferOnlyEvent(event, ts, gen) {
27019
27025
  if (!this.crashBuffer)
27020
27026
  return;
27021
27027
  try {
27022
27028
  this._applyConsoleMasking(event);
27023
27029
  const packedEvent = (0,_rrweb_packer__WEBPACK_IMPORTED_MODULE_0__.pack)(event);
27024
27030
  this.stoppedAt = new Date(ts).toISOString();
27031
+ if (gen !== this.generation)
27032
+ return;
27025
27033
  await this.crashBuffer.appendEvent({
27026
27034
  ts,
27027
27035
  isFullSnapshot: event.type === _rrweb_types__WEBPACK_IMPORTED_MODULE_1__.EventType.FullSnapshot,
@@ -28531,10 +28539,14 @@ class SessionRecorder extends lib0_observable__WEBPACK_IMPORTED_MODULE_16__.Obse
28531
28539
  }
28532
28540
  _startBufferOnlyRecording() {
28533
28541
  var _a, _b;
28534
- if (this.sessionId ||
28535
- !this._crashBuffer ||
28536
- !((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) ||
28537
- this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_4__.SessionState.stopped) {
28542
+ // NOTE: `this.sessionId` is intentionally NOT checked. `_stop()` runs
28543
+ // before `_clearSession()` (the stopSession API call sits between them),
28544
+ // so the clear().then() chain fires while sessionId is still set.
28545
+ // Bailing on sessionId here meant rrweb never restarted after manual
28546
+ // stop, leaving the buffer with no FullSnapshot and silently breaking
28547
+ // exception-triggered flushBuffer. `_recorder.restart(null, ...)` passes
28548
+ // null explicitly, so it's safe regardless of `this.sessionId`.
28549
+ if (!this._crashBuffer || !((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) || this.sessionState !== _types__WEBPACK_IMPORTED_MODULE_4__.SessionState.stopped) {
28538
28550
  return;
28539
28551
  }
28540
28552
  void this._recorder.restart(null, _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_0__.SessionType.MANUAL);
@@ -28604,9 +28616,10 @@ class SessionRecorder extends lib0_observable__WEBPACK_IMPORTED_MODULE_16__.Obse
28604
28616
  async stop(comment) {
28605
28617
  try {
28606
28618
  this._checkOperation('stop');
28619
+ const sid = this.sessionId;
28607
28620
  this._stop();
28608
28621
  if (this.continuousRecording) {
28609
- await this._apiService.stopContinuousDebugSession(this.sessionId);
28622
+ await this._apiService.stopContinuousDebugSession(sid);
28610
28623
  this.sessionType = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_0__.SessionType.MANUAL;
28611
28624
  }
28612
28625
  else {
@@ -28614,10 +28627,9 @@ class SessionRecorder extends lib0_observable__WEBPACK_IMPORTED_MODULE_16__.Obse
28614
28627
  sessionAttributes: { comment },
28615
28628
  stoppedAt: this._recorder.stoppedAt,
28616
28629
  };
28617
- const response = await this._apiService.stopSession(this.sessionId, request);
28630
+ const response = await this._apiService.stopSession(sid, request);
28618
28631
  _eventBus__WEBPACK_IMPORTED_MODULE_7__.recorderEventBus.emit(_config__WEBPACK_IMPORTED_MODULE_5__.SESSION_RESPONSE, response);
28619
28632
  }
28620
- this._clearSession();
28621
28633
  }
28622
28634
  catch (error) {
28623
28635
  this.error = error.message;
@@ -28653,15 +28665,15 @@ class SessionRecorder extends lib0_observable__WEBPACK_IMPORTED_MODULE_16__.Obse
28653
28665
  async cancel() {
28654
28666
  try {
28655
28667
  this._checkOperation('cancel');
28668
+ const sid = this.sessionId;
28656
28669
  this._stop();
28657
28670
  if (this.continuousRecording) {
28658
- await this._apiService.stopContinuousDebugSession(this.sessionId);
28671
+ await this._apiService.stopContinuousDebugSession(sid);
28659
28672
  this.sessionType = _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_0__.SessionType.MANUAL;
28660
28673
  }
28661
28674
  else {
28662
- await this._apiService.cancelSession(this.sessionId);
28675
+ await this._apiService.cancelSession(sid);
28663
28676
  }
28664
- this._clearSession();
28665
28677
  }
28666
28678
  catch (error) {
28667
28679
  this.error = error.message;
@@ -28975,7 +28987,18 @@ class SessionRecorder extends lib0_observable__WEBPACK_IMPORTED_MODULE_16__.Obse
28975
28987
  this._tracer.stop();
28976
28988
  this._recorder.stop();
28977
28989
  this._navigationRecorder.stop();
28978
- this._startBufferOnlyRecording();
28990
+ // Clear session identity synchronously. The buffer-restart chain and the
28991
+ // error-span-appended listener both gate on `this.sessionId === null`;
28992
+ // deferring this to `_clearSession()` (after the network stopSession
28993
+ // call) left them seeing a stale id and silently no-oping. Callers that
28994
+ // need the id for the stop/cancel API must capture it before _stop().
28995
+ this.session = null;
28996
+ this.sessionId = null;
28997
+ // rrweb assigns new node IDs on each record() call, so the next buffer
28998
+ // segment must not carry events from the previous generation. Await the
28999
+ // clear so its IDB tx can't race past the fresh FullSnapshot.
29000
+ const cleared = this._crashBuffer ? this._crashBuffer.clear() : Promise.resolve();
29001
+ void cleared.catch(() => undefined).then(() => this._startBufferOnlyRecording());
28979
29002
  }
28980
29003
  /**
28981
29004
  * Pause the session tracing and recording