@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.
package/dist/index.js CHANGED
@@ -24263,7 +24263,7 @@ const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
24263
24263
  const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
24264
24264
  const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
24265
24265
  const REMOTE_SESSION_RECORDING_STOP = 'remote-session-recording:stop';
24266
- const PACKAGE_VERSION_EXPORT = "2.0.79" || 0;
24266
+ const PACKAGE_VERSION_EXPORT = "2.0.80" || 0;
24267
24267
  // Regex patterns for OpenTelemetry ignore URLs
24268
24268
  const OTEL_IGNORE_URLS = [
24269
24269
  // Traces endpoint
@@ -25954,6 +25954,8 @@ if (typeof XMLHttpRequest !== 'undefined') {
25954
25954
  class RecorderBrowserSDK {
25955
25955
  constructor() {
25956
25956
  this.generation = 0;
25957
+ this.rrwebReady = false;
25958
+ this.pendingSnapshot = false;
25957
25959
  this.intervals = {
25958
25960
  restart: null,
25959
25961
  bufferSnapshot: null,
@@ -25962,12 +25964,18 @@ class RecorderBrowserSDK {
25962
25964
  this.stoppedAt = '';
25963
25965
  }
25964
25966
  /**
25965
- * Full snapshot.
25967
+ * Full snapshot. Defers when rrweb's internal `recording` flag is still
25968
+ * false (record() called while DOM is loading; init runs on DOMContentLoaded
25969
+ * /load). The deferred snapshot fires on rrweb's first emitted event.
25966
25970
  */
25967
25971
  takeFullSnapshot() {
25968
25972
  if (!this.stopFn) {
25969
25973
  return;
25970
25974
  }
25975
+ if (!this.rrwebReady) {
25976
+ this.pendingSnapshot = true;
25977
+ return;
25978
+ }
25971
25979
  rrweb__WEBPACK_IMPORTED_MODULE_2__.record.takeFullSnapshot();
25972
25980
  }
25973
25981
  /**
@@ -25990,11 +25998,27 @@ class RecorderBrowserSDK {
25990
25998
  }
25991
25999
  this.startedAt = new Date().toISOString();
25992
26000
  const gen = ++this.generation;
26001
+ this.rrwebReady = false;
26002
+ this.pendingSnapshot = false;
25993
26003
  this.stopFn = (0,rrweb__WEBPACK_IMPORTED_MODULE_2__.record)({
25994
26004
  ...this._buildRecordOptions(),
25995
26005
  emit: async (event) => {
25996
26006
  if (gen !== this.generation)
25997
26007
  return;
26008
+ // rrweb's init() ran (sync or via DOMContentLoaded/load) — safe to call
26009
+ // record.takeFullSnapshot() from now on. Flush any deferred snapshot.
26010
+ if (!this.rrwebReady) {
26011
+ this.rrwebReady = true;
26012
+ if (this.pendingSnapshot) {
26013
+ this.pendingSnapshot = false;
26014
+ try {
26015
+ rrweb__WEBPACK_IMPORTED_MODULE_2__.record.takeFullSnapshot();
26016
+ }
26017
+ catch (_e) {
26018
+ // unreachable in practice; first event implies recording=true
26019
+ }
26020
+ }
26021
+ }
25998
26022
  const ts = event.timestamp;
25999
26023
  if (!sessionId) {
26000
26024
  await this._handleBufferOnlyEvent(event, ts, gen);
@@ -26003,7 +26027,9 @@ class RecorderBrowserSDK {
26003
26027
  this._handleLiveSessionEvent(event, ts, sessionId, sessionType);
26004
26028
  },
26005
26029
  });
26006
- this.takeFullSnapshot();
26030
+ // rrweb's `record({...})` already emits the initial Meta+FullSnapshot pair
26031
+ // (synchronously when document is interactive/complete, otherwise deferred
26032
+ // to DOMContentLoaded/load). No explicit takeFullSnapshot() needed here.
26007
26033
  this._setupPeriodicSnapshots(sessionId, sessionType);
26008
26034
  }
26009
26035
  /**
@@ -26028,6 +26054,8 @@ class RecorderBrowserSDK {
26028
26054
  (_a = this.stopFn) === null || _a === void 0 ? void 0 : _a.call(this);
26029
26055
  this.stopFn = undefined;
26030
26056
  this.generation++;
26057
+ this.rrwebReady = false;
26058
+ this.pendingSnapshot = false;
26031
26059
  if (!((_b = this.config) === null || _b === void 0 ? void 0 : _b.useWebsocket)) {
26032
26060
  (_c = this.socketService) === null || _c === void 0 ? void 0 : _c.close();
26033
26061
  }
@@ -26445,10 +26473,16 @@ class CrashBufferService {
26445
26473
  await this.ready;
26446
26474
  const stoppedAt = now;
26447
26475
  const startedAt = Math.max(0, stoppedAt - this.windowMs);
26448
- // Always include a full snapshot "anchor" if one exists at/before the window start.
26476
+ // Always include a full snapshot "anchor" plus its preceding Meta.
26477
+ // rrweb emits Meta before FullSnapshot with separate Date.now() timestamps,
26478
+ // so on non-trivial pages Meta.ts < FullSnapshot.ts. Using the anchor's ts
26479
+ // as the window start would exclude the Meta and break replay (no viewport).
26449
26480
  const firstSnapshotAt = await this._safe(async () => {
26450
26481
  const anchor = await this.db.getLastRrwebFullSnapshotBefore(this.tabId, startedAt);
26451
- return typeof (anchor === null || anchor === void 0 ? void 0 : anchor.ts) === 'number' ? anchor.ts : startedAt;
26482
+ if (typeof (anchor === null || anchor === void 0 ? void 0 : anchor.ts) !== 'number')
26483
+ return startedAt;
26484
+ const anchorMeta = await this.db.getLastRrwebMetaBefore(this.tabId, anchor.ts);
26485
+ return typeof (anchorMeta === null || anchorMeta === void 0 ? void 0 : anchorMeta.ts) === 'number' ? anchorMeta.ts : anchor.ts;
26452
26486
  }, startedAt);
26453
26487
  const [allEvents, allSpans] = await Promise.all([
26454
26488
  this._safe(() => this.db.getRrwebEventsWindow(this.tabId, firstSnapshotAt, stoppedAt), []),