@multiplayer-app/session-recorder-browser 2.0.78 → 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.umd.js CHANGED
@@ -24436,7 +24436,7 @@ const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
24436
24436
  const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
24437
24437
  const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
24438
24438
  const REMOTE_SESSION_RECORDING_STOP = 'remote-session-recording:stop';
24439
- const PACKAGE_VERSION_EXPORT = "2.0.78" || 0;
24439
+ const PACKAGE_VERSION_EXPORT = "2.0.80" || 0;
24440
24440
  // Regex patterns for OpenTelemetry ignore URLs
24441
24441
  const OTEL_IGNORE_URLS = [
24442
24442
  // Traces endpoint
@@ -26147,6 +26147,8 @@ if (typeof XMLHttpRequest !== 'undefined') {
26147
26147
  class RecorderBrowserSDK {
26148
26148
  constructor() {
26149
26149
  this.generation = 0;
26150
+ this.rrwebReady = false;
26151
+ this.pendingSnapshot = false;
26150
26152
  this.intervals = {
26151
26153
  restart: null,
26152
26154
  bufferSnapshot: null,
@@ -26155,12 +26157,18 @@ class RecorderBrowserSDK {
26155
26157
  this.stoppedAt = '';
26156
26158
  }
26157
26159
  /**
26158
- * Full snapshot.
26160
+ * Full snapshot. Defers when rrweb's internal `recording` flag is still
26161
+ * false (record() called while DOM is loading; init runs on DOMContentLoaded
26162
+ * /load). The deferred snapshot fires on rrweb's first emitted event.
26159
26163
  */
26160
26164
  takeFullSnapshot() {
26161
26165
  if (!this.stopFn) {
26162
26166
  return;
26163
26167
  }
26168
+ if (!this.rrwebReady) {
26169
+ this.pendingSnapshot = true;
26170
+ return;
26171
+ }
26164
26172
  rrweb__WEBPACK_IMPORTED_MODULE_2__.record.takeFullSnapshot();
26165
26173
  }
26166
26174
  /**
@@ -26183,11 +26191,27 @@ class RecorderBrowserSDK {
26183
26191
  }
26184
26192
  this.startedAt = new Date().toISOString();
26185
26193
  const gen = ++this.generation;
26194
+ this.rrwebReady = false;
26195
+ this.pendingSnapshot = false;
26186
26196
  this.stopFn = (0,rrweb__WEBPACK_IMPORTED_MODULE_2__.record)({
26187
26197
  ...this._buildRecordOptions(),
26188
26198
  emit: async (event) => {
26189
26199
  if (gen !== this.generation)
26190
26200
  return;
26201
+ // rrweb's init() ran (sync or via DOMContentLoaded/load) — safe to call
26202
+ // record.takeFullSnapshot() from now on. Flush any deferred snapshot.
26203
+ if (!this.rrwebReady) {
26204
+ this.rrwebReady = true;
26205
+ if (this.pendingSnapshot) {
26206
+ this.pendingSnapshot = false;
26207
+ try {
26208
+ rrweb__WEBPACK_IMPORTED_MODULE_2__.record.takeFullSnapshot();
26209
+ }
26210
+ catch (_e) {
26211
+ // unreachable in practice; first event implies recording=true
26212
+ }
26213
+ }
26214
+ }
26191
26215
  const ts = event.timestamp;
26192
26216
  if (!sessionId) {
26193
26217
  await this._handleBufferOnlyEvent(event, ts, gen);
@@ -26196,7 +26220,9 @@ class RecorderBrowserSDK {
26196
26220
  this._handleLiveSessionEvent(event, ts, sessionId, sessionType);
26197
26221
  },
26198
26222
  });
26199
- this.takeFullSnapshot();
26223
+ // rrweb's `record({...})` already emits the initial Meta+FullSnapshot pair
26224
+ // (synchronously when document is interactive/complete, otherwise deferred
26225
+ // to DOMContentLoaded/load). No explicit takeFullSnapshot() needed here.
26200
26226
  this._setupPeriodicSnapshots(sessionId, sessionType);
26201
26227
  }
26202
26228
  /**
@@ -26221,6 +26247,8 @@ class RecorderBrowserSDK {
26221
26247
  (_a = this.stopFn) === null || _a === void 0 ? void 0 : _a.call(this);
26222
26248
  this.stopFn = undefined;
26223
26249
  this.generation++;
26250
+ this.rrwebReady = false;
26251
+ this.pendingSnapshot = false;
26224
26252
  if (!((_b = this.config) === null || _b === void 0 ? void 0 : _b.useWebsocket)) {
26225
26253
  (_c = this.socketService) === null || _c === void 0 ? void 0 : _c.close();
26226
26254
  }
@@ -26640,10 +26668,16 @@ class CrashBufferService {
26640
26668
  await this.ready;
26641
26669
  const stoppedAt = now;
26642
26670
  const startedAt = Math.max(0, stoppedAt - this.windowMs);
26643
- // Always include a full snapshot "anchor" if one exists at/before the window start.
26671
+ // Always include a full snapshot "anchor" plus its preceding Meta.
26672
+ // rrweb emits Meta before FullSnapshot with separate Date.now() timestamps,
26673
+ // so on non-trivial pages Meta.ts < FullSnapshot.ts. Using the anchor's ts
26674
+ // as the window start would exclude the Meta and break replay (no viewport).
26644
26675
  const firstSnapshotAt = await this._safe(async () => {
26645
26676
  const anchor = await this.db.getLastRrwebFullSnapshotBefore(this.tabId, startedAt);
26646
- return typeof (anchor === null || anchor === void 0 ? void 0 : anchor.ts) === 'number' ? anchor.ts : startedAt;
26677
+ if (typeof (anchor === null || anchor === void 0 ? void 0 : anchor.ts) !== 'number')
26678
+ return startedAt;
26679
+ const anchorMeta = await this.db.getLastRrwebMetaBefore(this.tabId, anchor.ts);
26680
+ return typeof (anchorMeta === null || anchorMeta === void 0 ? void 0 : anchorMeta.ts) === 'number' ? anchorMeta.ts : anchor.ts;
26647
26681
  }, startedAt);
26648
26682
  const [allEvents, allSpans] = await Promise.all([
26649
26683
  this._safe(() => this.db.getRrwebEventsWindow(this.tabId, firstSnapshotAt, stoppedAt), []),