@multiplayer-app/session-recorder-browser 2.0.79 → 2.0.80

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.79" || 0;
25166
+ const PACKAGE_VERSION_EXPORT = "2.0.80" || 0;
25167
25167
  // Regex patterns for OpenTelemetry ignore URLs
25168
25168
  const OTEL_IGNORE_URLS = [
25169
25169
  // Traces endpoint
@@ -26910,6 +26910,8 @@ __webpack_require__.r(__webpack_exports__);
26910
26910
  class RecorderBrowserSDK {
26911
26911
  constructor() {
26912
26912
  this.generation = 0;
26913
+ this.rrwebReady = false;
26914
+ this.pendingSnapshot = false;
26913
26915
  this.intervals = {
26914
26916
  restart: null,
26915
26917
  bufferSnapshot: null,
@@ -26918,12 +26920,18 @@ class RecorderBrowserSDK {
26918
26920
  this.stoppedAt = '';
26919
26921
  }
26920
26922
  /**
26921
- * Full snapshot.
26923
+ * Full snapshot. Defers when rrweb's internal `recording` flag is still
26924
+ * false (record() called while DOM is loading; init runs on DOMContentLoaded
26925
+ * /load). The deferred snapshot fires on rrweb's first emitted event.
26922
26926
  */
26923
26927
  takeFullSnapshot() {
26924
26928
  if (!this.stopFn) {
26925
26929
  return;
26926
26930
  }
26931
+ if (!this.rrwebReady) {
26932
+ this.pendingSnapshot = true;
26933
+ return;
26934
+ }
26927
26935
  rrweb__WEBPACK_IMPORTED_MODULE_2__.record.takeFullSnapshot();
26928
26936
  }
26929
26937
  /**
@@ -26946,11 +26954,27 @@ class RecorderBrowserSDK {
26946
26954
  }
26947
26955
  this.startedAt = new Date().toISOString();
26948
26956
  const gen = ++this.generation;
26957
+ this.rrwebReady = false;
26958
+ this.pendingSnapshot = false;
26949
26959
  this.stopFn = (0,rrweb__WEBPACK_IMPORTED_MODULE_2__.record)({
26950
26960
  ...this._buildRecordOptions(),
26951
26961
  emit: async (event) => {
26952
26962
  if (gen !== this.generation)
26953
26963
  return;
26964
+ // rrweb's init() ran (sync or via DOMContentLoaded/load) — safe to call
26965
+ // record.takeFullSnapshot() from now on. Flush any deferred snapshot.
26966
+ if (!this.rrwebReady) {
26967
+ this.rrwebReady = true;
26968
+ if (this.pendingSnapshot) {
26969
+ this.pendingSnapshot = false;
26970
+ try {
26971
+ rrweb__WEBPACK_IMPORTED_MODULE_2__.record.takeFullSnapshot();
26972
+ }
26973
+ catch (_e) {
26974
+ // unreachable in practice; first event implies recording=true
26975
+ }
26976
+ }
26977
+ }
26954
26978
  const ts = event.timestamp;
26955
26979
  if (!sessionId) {
26956
26980
  await this._handleBufferOnlyEvent(event, ts, gen);
@@ -26959,7 +26983,9 @@ class RecorderBrowserSDK {
26959
26983
  this._handleLiveSessionEvent(event, ts, sessionId, sessionType);
26960
26984
  },
26961
26985
  });
26962
- this.takeFullSnapshot();
26986
+ // rrweb's `record({...})` already emits the initial Meta+FullSnapshot pair
26987
+ // (synchronously when document is interactive/complete, otherwise deferred
26988
+ // to DOMContentLoaded/load). No explicit takeFullSnapshot() needed here.
26963
26989
  this._setupPeriodicSnapshots(sessionId, sessionType);
26964
26990
  }
26965
26991
  /**
@@ -26984,6 +27010,8 @@ class RecorderBrowserSDK {
26984
27010
  (_a = this.stopFn) === null || _a === void 0 ? void 0 : _a.call(this);
26985
27011
  this.stopFn = undefined;
26986
27012
  this.generation++;
27013
+ this.rrwebReady = false;
27014
+ this.pendingSnapshot = false;
26987
27015
  if (!((_b = this.config) === null || _b === void 0 ? void 0 : _b.useWebsocket)) {
26988
27016
  (_c = this.socketService) === null || _c === void 0 ? void 0 : _c.close();
26989
27017
  }
@@ -27405,10 +27433,16 @@ class CrashBufferService {
27405
27433
  await this.ready;
27406
27434
  const stoppedAt = now;
27407
27435
  const startedAt = Math.max(0, stoppedAt - this.windowMs);
27408
- // Always include a full snapshot "anchor" if one exists at/before the window start.
27436
+ // Always include a full snapshot "anchor" plus its preceding Meta.
27437
+ // rrweb emits Meta before FullSnapshot with separate Date.now() timestamps,
27438
+ // so on non-trivial pages Meta.ts < FullSnapshot.ts. Using the anchor's ts
27439
+ // as the window start would exclude the Meta and break replay (no viewport).
27409
27440
  const firstSnapshotAt = await this._safe(async () => {
27410
27441
  const anchor = await this.db.getLastRrwebFullSnapshotBefore(this.tabId, startedAt);
27411
- return typeof (anchor === null || anchor === void 0 ? void 0 : anchor.ts) === 'number' ? anchor.ts : startedAt;
27442
+ if (typeof (anchor === null || anchor === void 0 ? void 0 : anchor.ts) !== 'number')
27443
+ return startedAt;
27444
+ const anchorMeta = await this.db.getLastRrwebMetaBefore(this.tabId, anchor.ts);
27445
+ return typeof (anchorMeta === null || anchorMeta === void 0 ? void 0 : anchorMeta.ts) === 'number' ? anchorMeta.ts : anchor.ts;
27412
27446
  }, startedAt);
27413
27447
  const [allEvents, allSpans] = await Promise.all([
27414
27448
  this._safe(() => this.db.getRrwebEventsWindow(this.tabId, firstSnapshotAt, stoppedAt), []),