@sentry/replay 7.68.0 → 7.69.0

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/cjs/index.js CHANGED
@@ -4562,7 +4562,6 @@ function makeSession(session) {
4562
4562
  const lastActivity = session.lastActivity || now;
4563
4563
  const segmentId = session.segmentId || 0;
4564
4564
  const sampled = session.sampled;
4565
- const shouldRefresh = typeof session.shouldRefresh === 'boolean' ? session.shouldRefresh : true;
4566
4565
  const previousSessionId = session.previousSessionId;
4567
4566
 
4568
4567
  return {
@@ -4571,7 +4570,6 @@ function makeSession(session) {
4571
4570
  lastActivity,
4572
4571
  segmentId,
4573
4572
  sampled,
4574
- shouldRefresh,
4575
4573
  previousSessionId,
4576
4574
  };
4577
4575
  }
@@ -4673,47 +4671,22 @@ function isSessionExpired(
4673
4671
  );
4674
4672
  }
4675
4673
 
4676
- /**
4677
- * Check a session, and either return it or a refreshed version of it.
4678
- * The refreshed version may be unsampled.
4679
- * You can check if the session has changed by comparing the session IDs.
4680
- */
4681
- function maybeRefreshSession(
4674
+ /** If the session should be refreshed or not. */
4675
+ function shouldRefreshSession(
4682
4676
  session,
4683
- {
4684
- traceInternals,
4685
- maxReplayDuration,
4686
- sessionIdleExpire,
4687
- }
4688
-
4689
- ,
4690
- sessionOptions,
4677
+ { sessionIdleExpire, maxReplayDuration },
4691
4678
  ) {
4692
4679
  // If not expired, all good, just keep the session
4693
4680
  if (!isSessionExpired(session, { sessionIdleExpire, maxReplayDuration })) {
4694
- return session;
4695
- }
4696
-
4697
- const isBuffering = session.sampled === 'buffer';
4698
-
4699
- // If we are buffering & the session may be refreshed, just return it
4700
- if (isBuffering && session.shouldRefresh) {
4701
- return session;
4681
+ return false;
4702
4682
  }
4703
4683
 
4704
- // If we are buffering & the session may not be refreshed (=it was converted to session previously already)
4705
- // We return an unsampled new session
4706
- if (isBuffering) {
4707
- logInfoNextTick('[Replay] Session should not be refreshed', traceInternals);
4708
- return makeSession({ sampled: false });
4684
+ // If we are buffering & haven't ever flushed yet, always continue
4685
+ if (session.sampled === 'buffer' && session.segmentId === 0) {
4686
+ return false;
4709
4687
  }
4710
4688
 
4711
- // Else, we are not buffering, and the session is expired, so we need to create a new one
4712
- logInfoNextTick('[Replay] Session has expired, creating new one...', traceInternals);
4713
-
4714
- const newSession = createSession(sessionOptions, { previousSessionId: session.id });
4715
-
4716
- return newSession;
4689
+ return true;
4717
4690
  }
4718
4691
 
4719
4692
  /**
@@ -4721,26 +4694,30 @@ function maybeRefreshSession(
4721
4694
  * Returns a session that may be unsampled.
4722
4695
  */
4723
4696
  function loadOrCreateSession(
4724
- currentSession,
4725
4697
  {
4726
4698
  traceInternals,
4727
4699
  sessionIdleExpire,
4728
4700
  maxReplayDuration,
4701
+ previousSessionId,
4729
4702
  }
4730
4703
 
4731
4704
  ,
4732
4705
  sessionOptions,
4733
4706
  ) {
4734
- // If session exists and is passed, use it instead of always hitting session storage
4735
- const existingSession = currentSession || (sessionOptions.stickySession && fetchSession(traceInternals));
4707
+ const existingSession = sessionOptions.stickySession && fetchSession(traceInternals);
4736
4708
 
4737
4709
  // No session exists yet, just create a new one
4738
4710
  if (!existingSession) {
4739
- logInfoNextTick('[Replay] Created new session', traceInternals);
4740
- return createSession(sessionOptions);
4711
+ logInfoNextTick('[Replay] Creating new session', traceInternals);
4712
+ return createSession(sessionOptions, { previousSessionId });
4741
4713
  }
4742
4714
 
4743
- return maybeRefreshSession(existingSession, { sessionIdleExpire, traceInternals, maxReplayDuration }, sessionOptions);
4715
+ if (!shouldRefreshSession(existingSession, { sessionIdleExpire, maxReplayDuration })) {
4716
+ return existingSession;
4717
+ }
4718
+
4719
+ logInfoNextTick('[Replay] Session in sessionStorage is expired, creating new one...');
4720
+ return createSession(sessionOptions, { previousSessionId: existingSession.id });
4744
4721
  }
4745
4722
 
4746
4723
  const ReplayEventTypeCustom = 5;
@@ -4751,39 +4728,46 @@ function isCustomEvent(event) {
4751
4728
 
4752
4729
  /**
4753
4730
  * Add an event to the event buffer.
4731
+ * In contrast to `addEvent`, this does not return a promise & does not wait for the adding of the event to succeed/fail.
4732
+ * Instead this returns `true` if we tried to add the event, else false.
4733
+ * It returns `false` e.g. if we are paused, disabled, or out of the max replay duration.
4734
+ *
4754
4735
  * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
4755
4736
  */
4756
- async function addEvent(
4737
+ function addEventSync(replay, event, isCheckout) {
4738
+ if (!shouldAddEvent(replay, event)) {
4739
+ return false;
4740
+ }
4741
+
4742
+ void _addEvent(replay, event, isCheckout);
4743
+
4744
+ return true;
4745
+ }
4746
+
4747
+ /**
4748
+ * Add an event to the event buffer.
4749
+ * Resolves to `null` if no event was added, else to `void`.
4750
+ *
4751
+ * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
4752
+ */
4753
+ function addEvent(
4757
4754
  replay,
4758
4755
  event,
4759
4756
  isCheckout,
4760
4757
  ) {
4761
- if (!replay.eventBuffer) {
4762
- // This implies that `_isEnabled` is false
4763
- return null;
4764
- }
4765
-
4766
- if (replay.isPaused()) {
4767
- // Do not add to event buffer when recording is paused
4768
- return null;
4758
+ if (!shouldAddEvent(replay, event)) {
4759
+ return Promise.resolve(null);
4769
4760
  }
4770
4761
 
4771
- const timestampInMs = timestampToMs(event.timestamp);
4772
-
4773
- // Throw out events that happen more than 5 minutes ago. This can happen if
4774
- // page has been left open and idle for a long period of time and user
4775
- // comes back to trigger a new session. The performance entries rely on
4776
- // `performance.timeOrigin`, which is when the page first opened.
4777
- if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
4778
- return null;
4779
- }
4762
+ return _addEvent(replay, event, isCheckout);
4763
+ }
4780
4764
 
4781
- // Throw out events that are +60min from the initial timestamp
4782
- if (timestampInMs > replay.getContext().initialTimestamp + replay.getOptions().maxReplayDuration) {
4783
- logInfo(
4784
- `[Replay] Skipping event with timestamp ${timestampInMs} because it is after maxReplayDuration`,
4785
- replay.getOptions()._experiments.traceInternals,
4786
- );
4765
+ async function _addEvent(
4766
+ replay,
4767
+ event,
4768
+ isCheckout,
4769
+ ) {
4770
+ if (!replay.eventBuffer) {
4787
4771
  return null;
4788
4772
  }
4789
4773
 
@@ -4819,6 +4803,34 @@ async function addEvent(
4819
4803
  }
4820
4804
  }
4821
4805
 
4806
+ /** Exported only for tests. */
4807
+ function shouldAddEvent(replay, event) {
4808
+ if (!replay.eventBuffer || replay.isPaused() || !replay.isEnabled()) {
4809
+ return false;
4810
+ }
4811
+
4812
+ const timestampInMs = timestampToMs(event.timestamp);
4813
+
4814
+ // Throw out events that happen more than 5 minutes ago. This can happen if
4815
+ // page has been left open and idle for a long period of time and user
4816
+ // comes back to trigger a new session. The performance entries rely on
4817
+ // `performance.timeOrigin`, which is when the page first opened.
4818
+ if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
4819
+ return false;
4820
+ }
4821
+
4822
+ // Throw out events that are +60min from the initial timestamp
4823
+ if (timestampInMs > replay.getContext().initialTimestamp + replay.getOptions().maxReplayDuration) {
4824
+ logInfo(
4825
+ `[Replay] Skipping event with timestamp ${timestampInMs} because it is after maxReplayDuration`,
4826
+ replay.getOptions()._experiments.traceInternals,
4827
+ );
4828
+ return false;
4829
+ }
4830
+
4831
+ return true;
4832
+ }
4833
+
4822
4834
  function maybeApplyCallback(
4823
4835
  event,
4824
4836
  callback,
@@ -4940,7 +4952,7 @@ function isRrwebError(event, hint) {
4940
4952
  return false;
4941
4953
  }
4942
4954
 
4943
- // @ts-ignore this may be set by rrweb when it finds errors
4955
+ // @ts-expect-error this may be set by rrweb when it finds errors
4944
4956
  if (hint.originalException && hint.originalException.__rrweb__) {
4945
4957
  return true;
4946
4958
  }
@@ -5783,7 +5795,7 @@ function getAllowedHeaders(headers, allowedHeaders) {
5783
5795
  function _serializeFormData(formData) {
5784
5796
  // This is a bit simplified, but gives us a decent estimate
5785
5797
  // This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13'
5786
- // @ts-ignore passing FormData to URLSearchParams actually works
5798
+ // @ts-expect-error passing FormData to URLSearchParams actually works
5787
5799
  return new URLSearchParams(formData).toString();
5788
5800
  }
5789
5801
 
@@ -6403,9 +6415,7 @@ function addGlobalListeners(replay) {
6403
6415
  const scope = core.getCurrentHub().getScope();
6404
6416
  const client = core.getCurrentHub().getClient();
6405
6417
 
6406
- if (scope) {
6407
- scope.addScopeListener(handleScopeListener(replay));
6408
- }
6418
+ scope.addScopeListener(handleScopeListener(replay));
6409
6419
  utils.addInstrumentationHandler('dom', handleDomListener(replay));
6410
6420
  utils.addInstrumentationHandler('history', handleHistorySpanListener(replay));
6411
6421
  handleNetworkBreadcrumbs(replay);
@@ -6451,7 +6461,7 @@ async function addMemoryEntry(replay) {
6451
6461
  try {
6452
6462
  return Promise.all(
6453
6463
  createPerformanceSpans(replay, [
6454
- // @ts-ignore memory doesn't exist on type Performance as the API is non-standard (we check that it exists above)
6464
+ // @ts-expect-error memory doesn't exist on type Performance as the API is non-standard (we check that it exists above)
6455
6465
  createMemoryEntry(WINDOW.performance.memory),
6456
6466
  ]),
6457
6467
  );
@@ -6482,16 +6492,15 @@ function createMemoryEntry(memoryEntry) {
6482
6492
  }
6483
6493
 
6484
6494
  // Map entryType -> function to normalize data for event
6485
- // @ts-ignore TODO: entry type does not fit the create* functions entry type
6486
6495
  const ENTRY_TYPES
6487
6496
 
6488
6497
  = {
6489
- // @ts-ignore TODO: entry type does not fit the create* functions entry type
6498
+ // @ts-expect-error TODO: entry type does not fit the create* functions entry type
6490
6499
  resource: createResourceEntry,
6491
6500
  paint: createPaintEntry,
6492
- // @ts-ignore TODO: entry type does not fit the create* functions entry type
6501
+ // @ts-expect-error TODO: entry type does not fit the create* functions entry type
6493
6502
  navigation: createNavigationEntry,
6494
- // @ts-ignore TODO: entry type does not fit the create* functions entry type
6503
+ // @ts-expect-error TODO: entry type does not fit the create* functions entry type
6495
6504
  ['largest-contentful-paint']: createLargestContentfulPaint,
6496
6505
  };
6497
6506
 
@@ -6742,9 +6751,12 @@ function getHandleRecordingEmit(replay) {
6742
6751
  replay.setInitialState();
6743
6752
  }
6744
6753
 
6745
- // We need to clear existing events on a checkout, otherwise they are
6746
- // incremental event updates and should be appended
6747
- void addEvent(replay, event, isCheckout);
6754
+ // If the event is not added (e.g. due to being paused, disabled, or out of the max replay duration),
6755
+ // Skip all further steps
6756
+ if (!addEventSync(replay, event, isCheckout)) {
6757
+ // Return true to skip scheduling a debounced flush
6758
+ return true;
6759
+ }
6748
6760
 
6749
6761
  // Different behavior for full snapshots (type=2), ignore other event types
6750
6762
  // See https://github.com/rrweb-io/rrweb/blob/d8f9290ca496712aa1e7d472549480c4e7876594/packages/rrweb/src/types.ts#L16
@@ -6758,7 +6770,7 @@ function getHandleRecordingEmit(replay) {
6758
6770
  //
6759
6771
  // `isCheckout` is always true, but want to be explicit that it should
6760
6772
  // only be added for checkouts
6761
- void addSettingsEvent(replay, isCheckout);
6773
+ addSettingsEvent(replay, isCheckout);
6762
6774
 
6763
6775
  // If there is a previousSessionId after a full snapshot occurs, then
6764
6776
  // the replay session was started due to session expiration. The new session
@@ -6835,10 +6847,10 @@ function createOptionsEvent(replay) {
6835
6847
  function addSettingsEvent(replay, isCheckout) {
6836
6848
  // Only need to add this event when sending the first segment
6837
6849
  if (!isCheckout || !replay.session || replay.session.segmentId !== 0) {
6838
- return Promise.resolve(null);
6850
+ return;
6839
6851
  }
6840
6852
 
6841
- return addEvent(replay, createOptionsEvent(replay), false);
6853
+ addEventSync(replay, createOptionsEvent(replay), false);
6842
6854
  }
6843
6855
 
6844
6856
  /**
@@ -7047,7 +7059,7 @@ async function sendReplayRequest({
7047
7059
 
7048
7060
  try {
7049
7061
  // In case browsers don't allow this property to be writable
7050
- // @ts-ignore This needs lib es2022 and newer
7062
+ // @ts-expect-error This needs lib es2022 and newer
7051
7063
  error.cause = err;
7052
7064
  } catch (e) {
7053
7065
  // nothing to do
@@ -7118,7 +7130,7 @@ async function sendReplay(
7118
7130
 
7119
7131
  try {
7120
7132
  // In case browsers don't allow this property to be writable
7121
- // @ts-ignore This needs lib es2022 and newer
7133
+ // @ts-expect-error This needs lib es2022 and newer
7122
7134
  error.cause = err;
7123
7135
  } catch (e) {
7124
7136
  // nothing to do
@@ -7335,7 +7347,7 @@ class ReplayContainer {
7335
7347
  * Initializes the plugin based on sampling configuration. Should not be
7336
7348
  * called outside of constructor.
7337
7349
  */
7338
- initializeSampling() {
7350
+ initializeSampling(previousSessionId) {
7339
7351
  const { errorSampleRate, sessionSampleRate } = this._options;
7340
7352
 
7341
7353
  // If neither sample rate is > 0, then do nothing - user will need to call one of
@@ -7346,7 +7358,7 @@ class ReplayContainer {
7346
7358
 
7347
7359
  // Otherwise if there is _any_ sample rate set, try to load an existing
7348
7360
  // session, or create a new one.
7349
- this._initializeSessionForSampling();
7361
+ this._initializeSessionForSampling(previousSessionId);
7350
7362
 
7351
7363
  if (!this.session) {
7352
7364
  // This should not happen, something wrong has occurred
@@ -7391,7 +7403,6 @@ class ReplayContainer {
7391
7403
  logInfoNextTick('[Replay] Starting replay in session mode', this._options._experiments.traceInternals);
7392
7404
 
7393
7405
  const session = loadOrCreateSession(
7394
- this.session,
7395
7406
  {
7396
7407
  maxReplayDuration: this._options.maxReplayDuration,
7397
7408
  sessionIdleExpire: this.timeouts.sessionIdleExpire,
@@ -7422,7 +7433,6 @@ class ReplayContainer {
7422
7433
  logInfoNextTick('[Replay] Starting replay in buffer mode', this._options._experiments.traceInternals);
7423
7434
 
7424
7435
  const session = loadOrCreateSession(
7425
- this.session,
7426
7436
  {
7427
7437
  sessionIdleExpire: this.timeouts.sessionIdleExpire,
7428
7438
  maxReplayDuration: this._options.maxReplayDuration,
@@ -7491,15 +7501,16 @@ class ReplayContainer {
7491
7501
  return;
7492
7502
  }
7493
7503
 
7504
+ // We can't move `_isEnabled` after awaiting a flush, otherwise we can
7505
+ // enter into an infinite loop when `stop()` is called while flushing.
7506
+ this._isEnabled = false;
7507
+
7494
7508
  try {
7495
7509
  logInfo(
7496
7510
  `[Replay] Stopping Replay${reason ? ` triggered by ${reason}` : ''}`,
7497
7511
  this._options._experiments.traceInternals,
7498
7512
  );
7499
7513
 
7500
- // We can't move `_isEnabled` after awaiting a flush, otherwise we can
7501
- // enter into an infinite loop when `stop()` is called while flushing.
7502
- this._isEnabled = false;
7503
7514
  this._removeListeners();
7504
7515
  this.stopRecording();
7505
7516
 
@@ -7593,16 +7604,6 @@ class ReplayContainer {
7593
7604
 
7594
7605
  // Once this session ends, we do not want to refresh it
7595
7606
  if (this.session) {
7596
- this.session.shouldRefresh = false;
7597
-
7598
- // It's possible that the session lifespan is > max session lifespan
7599
- // because we have been buffering beyond max session lifespan (we ignore
7600
- // expiration given that `shouldRefresh` is true). Since we flip
7601
- // `shouldRefresh`, the session could be considered expired due to
7602
- // lifespan, which is not what we want. Update session start date to be
7603
- // the current timestamp, so that session is not considered to be
7604
- // expired. This means that max replay duration can be MAX_REPLAY_DURATION +
7605
- // (length of buffer), which we are ok with.
7606
7607
  this._updateUserActivity(activityTime);
7607
7608
  this._updateSessionActivity(activityTime);
7608
7609
  this._maybeSaveSession();
@@ -7730,8 +7731,6 @@ class ReplayContainer {
7730
7731
  * @hidden
7731
7732
  */
7732
7733
  checkAndHandleExpiredSession() {
7733
- const oldSessionId = this.getSessionId();
7734
-
7735
7734
  // Prevent starting a new session if the last user activity is older than
7736
7735
  // SESSION_IDLE_PAUSE_DURATION. Otherwise non-user activity can trigger a new
7737
7736
  // session+recording. This creates noisy replays that do not have much
@@ -7753,24 +7752,11 @@ class ReplayContainer {
7753
7752
  // --- There is recent user activity --- //
7754
7753
  // This will create a new session if expired, based on expiry length
7755
7754
  if (!this._checkSession()) {
7756
- return;
7757
- }
7758
-
7759
- // Session was expired if session ids do not match
7760
- const expired = oldSessionId !== this.getSessionId();
7761
-
7762
- if (!expired) {
7763
- return true;
7764
- }
7765
-
7766
- // Session is expired, trigger a full snapshot (which will create a new session)
7767
- if (this.isPaused()) {
7768
- this.resume();
7769
- } else {
7770
- this._triggerFullSnapshot();
7755
+ // Check session handles the refreshing itself
7756
+ return false;
7771
7757
  }
7772
7758
 
7773
- return false;
7759
+ return true;
7774
7760
  }
7775
7761
 
7776
7762
  /**
@@ -7810,7 +7796,8 @@ class ReplayContainer {
7810
7796
  });
7811
7797
 
7812
7798
  this.addUpdate(() => {
7813
- void addEvent(this, {
7799
+ // Return `false` if the event _was_ added, as that means we schedule a flush
7800
+ return !addEventSync(this, {
7814
7801
  type: ReplayEventTypeCustom,
7815
7802
  timestamp: breadcrumb.timestamp || 0,
7816
7803
  data: {
@@ -7858,6 +7845,7 @@ class ReplayContainer {
7858
7845
 
7859
7846
  // Need to set as enabled before we start recording, as `record()` can trigger a flush with a new checkout
7860
7847
  this._isEnabled = true;
7848
+ this._isPaused = false;
7861
7849
 
7862
7850
  this.startRecording();
7863
7851
  }
@@ -7874,17 +7862,17 @@ class ReplayContainer {
7874
7862
  /**
7875
7863
  * Loads (or refreshes) the current session.
7876
7864
  */
7877
- _initializeSessionForSampling() {
7865
+ _initializeSessionForSampling(previousSessionId) {
7878
7866
  // Whenever there is _any_ error sample rate, we always allow buffering
7879
7867
  // Because we decide on sampling when an error occurs, we need to buffer at all times if sampling for errors
7880
7868
  const allowBuffering = this._options.errorSampleRate > 0;
7881
7869
 
7882
7870
  const session = loadOrCreateSession(
7883
- this.session,
7884
7871
  {
7885
7872
  sessionIdleExpire: this.timeouts.sessionIdleExpire,
7886
7873
  maxReplayDuration: this._options.maxReplayDuration,
7887
7874
  traceInternals: this._options._experiments.traceInternals,
7875
+ previousSessionId,
7888
7876
  },
7889
7877
  {
7890
7878
  stickySession: this._options.stickySession,
@@ -7909,37 +7897,32 @@ class ReplayContainer {
7909
7897
 
7910
7898
  const currentSession = this.session;
7911
7899
 
7912
- const newSession = maybeRefreshSession(
7913
- currentSession,
7914
- {
7900
+ if (
7901
+ shouldRefreshSession(currentSession, {
7915
7902
  sessionIdleExpire: this.timeouts.sessionIdleExpire,
7916
- traceInternals: this._options._experiments.traceInternals,
7917
7903
  maxReplayDuration: this._options.maxReplayDuration,
7918
- },
7919
- {
7920
- stickySession: Boolean(this._options.stickySession),
7921
- sessionSampleRate: this._options.sessionSampleRate,
7922
- allowBuffering: this._options.errorSampleRate > 0,
7923
- },
7924
- );
7925
-
7926
- const isNew = newSession.id !== currentSession.id;
7927
-
7928
- // If session was newly created (i.e. was not loaded from storage), then
7929
- // enable flag to create the root replay
7930
- if (isNew) {
7931
- this.setInitialState();
7932
- this.session = newSession;
7933
- }
7934
-
7935
- if (!this.session.sampled) {
7936
- void this.stop({ reason: 'session not refreshed' });
7904
+ })
7905
+ ) {
7906
+ void this._refreshSession(currentSession);
7937
7907
  return false;
7938
7908
  }
7939
7909
 
7940
7910
  return true;
7941
7911
  }
7942
7912
 
7913
+ /**
7914
+ * Refresh a session with a new one.
7915
+ * This stops the current session (without forcing a flush, as that would never work since we are expired),
7916
+ * and then does a new sampling based on the refreshed session.
7917
+ */
7918
+ async _refreshSession(session) {
7919
+ if (!this._isEnabled) {
7920
+ return;
7921
+ }
7922
+ await this.stop({ reason: 'refresh session' });
7923
+ this.initializeSampling(session.id);
7924
+ }
7925
+
7943
7926
  /**
7944
7927
  * Adds listeners to record events for the replay
7945
7928
  */
@@ -8051,10 +8034,14 @@ class ReplayContainer {
8051
8034
 
8052
8035
  const expired = isSessionExpired(this.session, {
8053
8036
  maxReplayDuration: this._options.maxReplayDuration,
8054
- ...this.timeouts,
8037
+ sessionIdleExpire: this.timeouts.sessionIdleExpire,
8055
8038
  });
8056
8039
 
8057
- if (breadcrumb && !expired) {
8040
+ if (expired) {
8041
+ return;
8042
+ }
8043
+
8044
+ if (breadcrumb) {
8058
8045
  this._createCustomBreadcrumb(breadcrumb);
8059
8046
  }
8060
8047
 
@@ -8199,7 +8186,9 @@ class ReplayContainer {
8199
8186
  * Should never be called directly, only by `flush`
8200
8187
  */
8201
8188
  async _runFlush() {
8202
- if (!this.session || !this.eventBuffer) {
8189
+ const replayId = this.getSessionId();
8190
+
8191
+ if (!this.session || !this.eventBuffer || !replayId) {
8203
8192
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.error('[Replay] No session or eventBuffer found to flush.');
8204
8193
  return;
8205
8194
  }
@@ -8219,13 +8208,15 @@ class ReplayContainer {
8219
8208
  return;
8220
8209
  }
8221
8210
 
8211
+ // if this changed in the meanwhile, e.g. because the session was refreshed or similar, we abort here
8212
+ if (replayId !== this.getSessionId()) {
8213
+ return;
8214
+ }
8215
+
8222
8216
  try {
8223
8217
  // This uses the data from the eventBuffer, so we need to call this before `finish()
8224
8218
  this._updateInitialTimestampFromEventBuffer();
8225
8219
 
8226
- // Note this empties the event buffer regardless of outcome of sending replay
8227
- const recordingData = await this.eventBuffer.finish();
8228
-
8229
8220
  const timestamp = Date.now();
8230
8221
 
8231
8222
  // Check total duration again, to avoid sending outdated stuff
@@ -8235,14 +8226,14 @@ class ReplayContainer {
8235
8226
  throw new Error('Session is too long, not sending replay');
8236
8227
  }
8237
8228
 
8238
- // NOTE: Copy values from instance members, as it's possible they could
8239
- // change before the flush finishes.
8240
- const replayId = this.session.id;
8241
8229
  const eventContext = this._popEventContext();
8242
8230
  // Always increment segmentId regardless of outcome of sending replay
8243
8231
  const segmentId = this.session.segmentId++;
8244
8232
  this._maybeSaveSession();
8245
8233
 
8234
+ // Note this empties the event buffer regardless of outcome of sending replay
8235
+ const recordingData = await this.eventBuffer.finish();
8236
+
8246
8237
  await sendReplay({
8247
8238
  replayId,
8248
8239
  recordingData,