@sentry/replay 7.61.1 → 7.62.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
@@ -4027,21 +4027,45 @@ function logInfo(message, shouldAddBreadcrumb) {
4027
4027
  utils.logger.info(message);
4028
4028
 
4029
4029
  if (shouldAddBreadcrumb) {
4030
- const hub = core.getCurrentHub();
4031
- hub.addBreadcrumb(
4032
- {
4033
- category: 'console',
4034
- data: {
4035
- logger: 'replay',
4036
- },
4037
- level: 'info',
4038
- message,
4039
- },
4040
- { level: 'info' },
4041
- );
4030
+ addBreadcrumb(message);
4042
4031
  }
4043
4032
  }
4044
4033
 
4034
+ /**
4035
+ * Log a message, and add a breadcrumb in the next tick.
4036
+ * This is necessary when the breadcrumb may be added before the replay is initialized.
4037
+ */
4038
+ function logInfoNextTick(message, shouldAddBreadcrumb) {
4039
+ if (!(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
4040
+ return;
4041
+ }
4042
+
4043
+ utils.logger.info(message);
4044
+
4045
+ if (shouldAddBreadcrumb) {
4046
+ // Wait a tick here to avoid race conditions for some initial logs
4047
+ // which may be added before replay is initialized
4048
+ setTimeout(() => {
4049
+ addBreadcrumb(message);
4050
+ }, 0);
4051
+ }
4052
+ }
4053
+
4054
+ function addBreadcrumb(message) {
4055
+ const hub = core.getCurrentHub();
4056
+ hub.addBreadcrumb(
4057
+ {
4058
+ category: 'console',
4059
+ data: {
4060
+ logger: 'replay',
4061
+ },
4062
+ level: 'info',
4063
+ message,
4064
+ },
4065
+ { level: 'info' },
4066
+ );
4067
+ }
4068
+
4045
4069
  /** This error indicates that the event buffer size exceeded the limit.. */
4046
4070
  class EventBufferSizeExceededError extends Error {
4047
4071
  constructor() {
@@ -4056,9 +4080,12 @@ class EventBufferSizeExceededError extends Error {
4056
4080
  class EventBufferArray {
4057
4081
  /** All the events that are buffered to be sent. */
4058
4082
 
4083
+ /** @inheritdoc */
4084
+
4059
4085
  constructor() {
4060
4086
  this.events = [];
4061
4087
  this._totalSize = 0;
4088
+ this.hasCheckout = false;
4062
4089
  }
4063
4090
 
4064
4091
  /** @inheritdoc */
@@ -4103,6 +4130,7 @@ class EventBufferArray {
4103
4130
  clear() {
4104
4131
  this.events = [];
4105
4132
  this._totalSize = 0;
4133
+ this.hasCheckout = false;
4106
4134
  }
4107
4135
 
4108
4136
  /** @inheritdoc */
@@ -4222,11 +4250,13 @@ class WorkerHandler {
4222
4250
  * Exported only for testing.
4223
4251
  */
4224
4252
  class EventBufferCompressionWorker {
4253
+ /** @inheritdoc */
4225
4254
 
4226
4255
  constructor(worker) {
4227
4256
  this._worker = new WorkerHandler(worker);
4228
4257
  this._earliestTimestamp = null;
4229
4258
  this._totalSize = 0;
4259
+ this.hasCheckout = false;
4230
4260
  }
4231
4261
 
4232
4262
  /** @inheritdoc */
@@ -4286,6 +4316,8 @@ class EventBufferCompressionWorker {
4286
4316
  clear() {
4287
4317
  this._earliestTimestamp = null;
4288
4318
  this._totalSize = 0;
4319
+ this.hasCheckout = false;
4320
+
4289
4321
  // We do not wait on this, as we assume the order of messages is consistent for the worker
4290
4322
  void this._worker.postMessage('clear');
4291
4323
  }
@@ -4340,6 +4372,15 @@ class EventBufferProxy {
4340
4372
  return this._used.hasEvents;
4341
4373
  }
4342
4374
 
4375
+ /** @inheritdoc */
4376
+ get hasCheckout() {
4377
+ return this._used.hasCheckout;
4378
+ }
4379
+ /** @inheritdoc */
4380
+ set hasCheckout(value) {
4381
+ this._used.hasCheckout = value;
4382
+ }
4383
+
4343
4384
  /** @inheritDoc */
4344
4385
  destroy() {
4345
4386
  this._fallback.destroy();
@@ -4601,7 +4642,7 @@ function fetchSession(traceInternals) {
4601
4642
 
4602
4643
  const sessionObj = JSON.parse(sessionStringFromStorage) ;
4603
4644
 
4604
- logInfo('[Replay] Loading existing session', traceInternals);
4645
+ logInfoNextTick('[Replay] Loading existing session', traceInternals);
4605
4646
 
4606
4647
  return makeSession(sessionObj);
4607
4648
  } catch (e) {
@@ -4637,10 +4678,10 @@ function getSession({
4637
4678
  // and when this session is expired, it will not be renewed until user
4638
4679
  // reloads.
4639
4680
  const discardedSession = makeSession({ sampled: false });
4640
- logInfo('[Replay] Session should not be refreshed', traceInternals);
4681
+ logInfoNextTick('[Replay] Session should not be refreshed', traceInternals);
4641
4682
  return { type: 'new', session: discardedSession };
4642
4683
  } else {
4643
- logInfo('[Replay] Session has expired', traceInternals);
4684
+ logInfoNextTick('[Replay] Session has expired', traceInternals);
4644
4685
  }
4645
4686
  // Otherwise continue to create a new session
4646
4687
  }
@@ -4650,7 +4691,7 @@ function getSession({
4650
4691
  sessionSampleRate,
4651
4692
  allowBuffering,
4652
4693
  });
4653
- logInfo('[Replay] Created new session', traceInternals);
4694
+ logInfoNextTick('[Replay] Created new session', traceInternals);
4654
4695
 
4655
4696
  return { type: 'new', session: newSession };
4656
4697
  }
@@ -4689,8 +4730,9 @@ async function addEvent(
4689
4730
  }
4690
4731
 
4691
4732
  try {
4692
- if (isCheckout) {
4733
+ if (isCheckout && replay.recordingMode === 'buffer') {
4693
4734
  replay.eventBuffer.clear();
4735
+ replay.eventBuffer.hasCheckout = true;
4694
4736
  }
4695
4737
 
4696
4738
  const replayOptions = replay.getOptions();
@@ -7266,7 +7308,10 @@ class ReplayContainer {
7266
7308
  this.recordingMode = 'buffer';
7267
7309
  }
7268
7310
 
7269
- logInfo(`[Replay] Starting replay in ${this.recordingMode} mode`, this._options._experiments.traceInternals);
7311
+ logInfoNextTick(
7312
+ `[Replay] Starting replay in ${this.recordingMode} mode`,
7313
+ this._options._experiments.traceInternals,
7314
+ );
7270
7315
 
7271
7316
  this._initializeRecording();
7272
7317
  }
@@ -7287,7 +7332,7 @@ class ReplayContainer {
7287
7332
  throw new Error('Replay buffering is in progress, call `flush()` to save the replay');
7288
7333
  }
7289
7334
 
7290
- logInfo('[Replay] Starting replay in session mode', this._options._experiments.traceInternals);
7335
+ logInfoNextTick('[Replay] Starting replay in session mode', this._options._experiments.traceInternals);
7291
7336
 
7292
7337
  const previousSessionId = this.session && this.session.id;
7293
7338
 
@@ -7316,7 +7361,7 @@ class ReplayContainer {
7316
7361
  throw new Error('Replay recording is already in progress');
7317
7362
  }
7318
7363
 
7319
- logInfo('[Replay] Starting replay in buffer mode', this._options._experiments.traceInternals);
7364
+ logInfoNextTick('[Replay] Starting replay in buffer mode', this._options._experiments.traceInternals);
7320
7365
 
7321
7366
  const previousSessionId = this.session && this.session.id;
7322
7367
 
@@ -8141,6 +8186,9 @@ class ReplayContainer {
8141
8186
  const now = Date.now();
8142
8187
  const duration = now - start;
8143
8188
 
8189
+ // A flush is about to happen, cancel any queued flushes
8190
+ this._debouncedFlush.cancel();
8191
+
8144
8192
  // If session is too short, or too long (allow some wiggle room over maxSessionLife), do not send it
8145
8193
  // This _should_ not happen, but it may happen if flush is triggered due to a page activity change or similar
8146
8194
  const tooShort = duration < this._options.minReplayDuration;
@@ -8150,6 +8198,7 @@ class ReplayContainer {
8150
8198
  `[Replay] Session duration (${Math.floor(duration / 1000)}s) is too ${
8151
8199
  tooShort ? 'short' : 'long'
8152
8200
  }, not sending replay.`,
8201
+ this._options._experiments.traceInternals,
8153
8202
  );
8154
8203
 
8155
8204
  if (tooShort) {
@@ -8158,8 +8207,11 @@ class ReplayContainer {
8158
8207
  return;
8159
8208
  }
8160
8209
 
8161
- // A flush is about to happen, cancel any queued flushes
8162
- this._debouncedFlush.cancel();
8210
+ const eventBuffer = this.eventBuffer;
8211
+ if (eventBuffer && this.session.segmentId === 0 && !eventBuffer.hasCheckout) {
8212
+ logInfo('[Replay] Flushing initial segment without checkout.', this._options._experiments.traceInternals);
8213
+ // TODO FN: Evaluate if we want to stop here, or remove this again?
8214
+ }
8163
8215
 
8164
8216
  // this._flushLock acts as a lock so that future calls to `_flush()`
8165
8217
  // will be blocked until this promise resolves