@sentry/replay 7.61.0 → 7.61.1
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 +171 -116
- package/cjs/index.js.map +1 -1
- package/esm/index.js +171 -116
- package/esm/index.js.map +1 -1
- package/package.json +5 -5
- package/types/coreHandlers/handleAfterSendEvent.d.ts.map +1 -1
- package/types/coreHandlers/handleClick.d.ts.map +1 -1
- package/types/coreHandlers/handleGlobalEvent.d.ts.map +1 -1
- package/types/eventBuffer/EventBufferArray.d.ts.map +1 -1
- package/types/eventBuffer/EventBufferCompressionWorker.d.ts.map +1 -1
- package/types/eventBuffer/EventBufferProxy.d.ts.map +1 -1
- package/types/eventBuffer/WorkerHandler.d.ts.map +1 -1
- package/types/eventBuffer/index.d.ts.map +1 -1
- package/types/integration.d.ts.map +1 -1
- package/types/replay.d.ts.map +1 -1
- package/types/session/Session.d.ts.map +1 -1
- package/types/session/createSession.d.ts.map +1 -1
- package/types/session/fetchSession.d.ts +1 -1
- package/types/session/fetchSession.d.ts.map +1 -1
- package/types/session/getSession.d.ts +2 -1
- package/types/session/getSession.d.ts.map +1 -1
- package/types/types/replayFrame.d.ts +4 -3
- package/types/types/replayFrame.d.ts.map +1 -1
- package/types/util/handleRecordingEmit.d.ts.map +1 -1
- package/types/util/log.d.ts +5 -0
- package/types/util/log.d.ts.map +1 -0
- package/types/util/sendReplayRequest.d.ts.map +1 -1
package/esm/index.js
CHANGED
|
@@ -3402,17 +3402,17 @@ function handleClick(clickDetector, clickBreadcrumb, node) {
|
|
|
3402
3402
|
/** A click detector class that can be used to detect slow or rage clicks on elements. */
|
|
3403
3403
|
class ClickDetector {
|
|
3404
3404
|
// protected for testing
|
|
3405
|
-
__init() {this._lastMutation = 0;}
|
|
3406
|
-
__init2() {this._lastScroll = 0;}
|
|
3407
|
-
|
|
3408
|
-
__init3() {this._clicks = [];}
|
|
3409
3405
|
|
|
3410
3406
|
constructor(
|
|
3411
3407
|
replay,
|
|
3412
3408
|
slowClickConfig,
|
|
3413
3409
|
// Just for easier testing
|
|
3414
3410
|
_addBreadcrumbEvent = addBreadcrumbEvent,
|
|
3415
|
-
) {
|
|
3411
|
+
) {
|
|
3412
|
+
this._lastMutation = 0;
|
|
3413
|
+
this._lastScroll = 0;
|
|
3414
|
+
this._clicks = [];
|
|
3415
|
+
|
|
3416
3416
|
// We want everything in s, but options are in ms
|
|
3417
3417
|
this._timeout = slowClickConfig.timeout / 1000;
|
|
3418
3418
|
this._threshold = slowClickConfig.threshold / 1000;
|
|
@@ -4014,6 +4014,32 @@ function t(t){let e=t.length;for(;--e>=0;)t[e]=0}const e=new Uint8Array([0,0,0,0
|
|
|
4014
4014
|
|
|
4015
4015
|
function e(){const e=new Blob([r]);return URL.createObjectURL(e)}
|
|
4016
4016
|
|
|
4017
|
+
/**
|
|
4018
|
+
* Log a message in debug mode, and add a breadcrumb when _experiment.traceInternals is enabled.
|
|
4019
|
+
*/
|
|
4020
|
+
function logInfo(message, shouldAddBreadcrumb) {
|
|
4021
|
+
if (!(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
|
|
4025
|
+
logger.info(message);
|
|
4026
|
+
|
|
4027
|
+
if (shouldAddBreadcrumb) {
|
|
4028
|
+
const hub = getCurrentHub();
|
|
4029
|
+
hub.addBreadcrumb(
|
|
4030
|
+
{
|
|
4031
|
+
category: 'console',
|
|
4032
|
+
data: {
|
|
4033
|
+
logger: 'replay',
|
|
4034
|
+
},
|
|
4035
|
+
level: 'info',
|
|
4036
|
+
message,
|
|
4037
|
+
},
|
|
4038
|
+
{ level: 'info' },
|
|
4039
|
+
);
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
|
|
4017
4043
|
/** This error indicates that the event buffer size exceeded the limit.. */
|
|
4018
4044
|
class EventBufferSizeExceededError extends Error {
|
|
4019
4045
|
constructor() {
|
|
@@ -4028,10 +4054,9 @@ class EventBufferSizeExceededError extends Error {
|
|
|
4028
4054
|
class EventBufferArray {
|
|
4029
4055
|
/** All the events that are buffered to be sent. */
|
|
4030
4056
|
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
constructor() {EventBufferArray.prototype.__init.call(this);
|
|
4057
|
+
constructor() {
|
|
4034
4058
|
this.events = [];
|
|
4059
|
+
this._totalSize = 0;
|
|
4035
4060
|
}
|
|
4036
4061
|
|
|
4037
4062
|
/** @inheritdoc */
|
|
@@ -4140,7 +4165,7 @@ class WorkerHandler {
|
|
|
4140
4165
|
* Destroy the worker.
|
|
4141
4166
|
*/
|
|
4142
4167
|
destroy() {
|
|
4143
|
-
(
|
|
4168
|
+
logInfo('[Replay] Destroying compression worker');
|
|
4144
4169
|
this._worker.terminate();
|
|
4145
4170
|
}
|
|
4146
4171
|
|
|
@@ -4196,11 +4221,10 @@ class WorkerHandler {
|
|
|
4196
4221
|
*/
|
|
4197
4222
|
class EventBufferCompressionWorker {
|
|
4198
4223
|
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
constructor(worker) {EventBufferCompressionWorker.prototype.__init.call(this);
|
|
4224
|
+
constructor(worker) {
|
|
4202
4225
|
this._worker = new WorkerHandler(worker);
|
|
4203
4226
|
this._earliestTimestamp = null;
|
|
4227
|
+
this._totalSize = 0;
|
|
4204
4228
|
}
|
|
4205
4229
|
|
|
4206
4230
|
/** @inheritdoc */
|
|
@@ -4359,7 +4383,7 @@ class EventBufferProxy {
|
|
|
4359
4383
|
} catch (error) {
|
|
4360
4384
|
// If the worker fails to load, we fall back to the simple buffer.
|
|
4361
4385
|
// Nothing more to do from our side here
|
|
4362
|
-
(
|
|
4386
|
+
logInfo('[Replay] Failed to load the compression worker, falling back to simple buffer');
|
|
4363
4387
|
return;
|
|
4364
4388
|
}
|
|
4365
4389
|
|
|
@@ -4398,16 +4422,16 @@ function createEventBuffer({ useCompression }) {
|
|
|
4398
4422
|
try {
|
|
4399
4423
|
const workerUrl = e();
|
|
4400
4424
|
|
|
4401
|
-
(
|
|
4425
|
+
logInfo('[Replay] Using compression worker');
|
|
4402
4426
|
const worker = new Worker(workerUrl);
|
|
4403
4427
|
return new EventBufferProxy(worker);
|
|
4404
4428
|
} catch (error) {
|
|
4405
|
-
(
|
|
4429
|
+
logInfo('[Replay] Failed to create compression worker');
|
|
4406
4430
|
// Fall back to use simple event buffer array
|
|
4407
4431
|
}
|
|
4408
4432
|
}
|
|
4409
4433
|
|
|
4410
|
-
(
|
|
4434
|
+
logInfo('[Replay] Using simple buffer');
|
|
4411
4435
|
return new EventBufferArray();
|
|
4412
4436
|
}
|
|
4413
4437
|
|
|
@@ -4520,6 +4544,7 @@ function makeSession(session) {
|
|
|
4520
4544
|
const lastActivity = session.lastActivity || now;
|
|
4521
4545
|
const segmentId = session.segmentId || 0;
|
|
4522
4546
|
const sampled = session.sampled;
|
|
4547
|
+
const shouldRefresh = typeof session.shouldRefresh === 'boolean' ? session.shouldRefresh : true;
|
|
4523
4548
|
|
|
4524
4549
|
return {
|
|
4525
4550
|
id,
|
|
@@ -4527,7 +4552,7 @@ function makeSession(session) {
|
|
|
4527
4552
|
lastActivity,
|
|
4528
4553
|
segmentId,
|
|
4529
4554
|
sampled,
|
|
4530
|
-
shouldRefresh
|
|
4555
|
+
shouldRefresh,
|
|
4531
4556
|
};
|
|
4532
4557
|
}
|
|
4533
4558
|
|
|
@@ -4549,8 +4574,6 @@ function createSession({ sessionSampleRate, allowBuffering, stickySession = fals
|
|
|
4549
4574
|
sampled,
|
|
4550
4575
|
});
|
|
4551
4576
|
|
|
4552
|
-
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log(`[Replay] Creating new session: ${session.id}`);
|
|
4553
|
-
|
|
4554
4577
|
if (stickySession) {
|
|
4555
4578
|
saveSession(session);
|
|
4556
4579
|
}
|
|
@@ -4561,7 +4584,7 @@ function createSession({ sessionSampleRate, allowBuffering, stickySession = fals
|
|
|
4561
4584
|
/**
|
|
4562
4585
|
* Fetches a session from storage
|
|
4563
4586
|
*/
|
|
4564
|
-
function fetchSession() {
|
|
4587
|
+
function fetchSession(traceInternals) {
|
|
4565
4588
|
if (!hasSessionStorage()) {
|
|
4566
4589
|
return null;
|
|
4567
4590
|
}
|
|
@@ -4576,6 +4599,8 @@ function fetchSession() {
|
|
|
4576
4599
|
|
|
4577
4600
|
const sessionObj = JSON.parse(sessionStringFromStorage) ;
|
|
4578
4601
|
|
|
4602
|
+
logInfo('[Replay] Loading existing session', traceInternals);
|
|
4603
|
+
|
|
4579
4604
|
return makeSession(sessionObj);
|
|
4580
4605
|
} catch (e) {
|
|
4581
4606
|
return null;
|
|
@@ -4591,9 +4616,10 @@ function getSession({
|
|
|
4591
4616
|
stickySession,
|
|
4592
4617
|
sessionSampleRate,
|
|
4593
4618
|
allowBuffering,
|
|
4619
|
+
traceInternals,
|
|
4594
4620
|
}) {
|
|
4595
4621
|
// If session exists and is passed, use it instead of always hitting session storage
|
|
4596
|
-
const session = currentSession || (stickySession && fetchSession());
|
|
4622
|
+
const session = currentSession || (stickySession && fetchSession(traceInternals));
|
|
4597
4623
|
|
|
4598
4624
|
if (session) {
|
|
4599
4625
|
// If there is a session, check if it is valid (e.g. "last activity" time
|
|
@@ -4609,9 +4635,10 @@ function getSession({
|
|
|
4609
4635
|
// and when this session is expired, it will not be renewed until user
|
|
4610
4636
|
// reloads.
|
|
4611
4637
|
const discardedSession = makeSession({ sampled: false });
|
|
4638
|
+
logInfo('[Replay] Session should not be refreshed', traceInternals);
|
|
4612
4639
|
return { type: 'new', session: discardedSession };
|
|
4613
4640
|
} else {
|
|
4614
|
-
(
|
|
4641
|
+
logInfo('[Replay] Session has expired', traceInternals);
|
|
4615
4642
|
}
|
|
4616
4643
|
// Otherwise continue to create a new session
|
|
4617
4644
|
}
|
|
@@ -4621,6 +4648,7 @@ function getSession({
|
|
|
4621
4648
|
sessionSampleRate,
|
|
4622
4649
|
allowBuffering,
|
|
4623
4650
|
});
|
|
4651
|
+
logInfo('[Replay] Created new session', traceInternals);
|
|
4624
4652
|
|
|
4625
4653
|
return { type: 'new', session: newSession };
|
|
4626
4654
|
}
|
|
@@ -4727,7 +4755,7 @@ function handleAfterSendEvent(replay) {
|
|
|
4727
4755
|
const enforceStatusCode = isBaseTransportSend();
|
|
4728
4756
|
|
|
4729
4757
|
return (event, sendResponse) => {
|
|
4730
|
-
if (!isErrorEvent(event) && !isTransactionEvent(event)) {
|
|
4758
|
+
if (!replay.isEnabled() || (!isErrorEvent(event) && !isTransactionEvent(event))) {
|
|
4731
4759
|
return;
|
|
4732
4760
|
}
|
|
4733
4761
|
|
|
@@ -4740,36 +4768,47 @@ function handleAfterSendEvent(replay) {
|
|
|
4740
4768
|
return;
|
|
4741
4769
|
}
|
|
4742
4770
|
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
if (isTransactionEvent(event) && event.contexts && event.contexts.trace && event.contexts.trace.trace_id) {
|
|
4746
|
-
replay.getContext().traceIds.add(event.contexts.trace.trace_id );
|
|
4771
|
+
if (isTransactionEvent(event)) {
|
|
4772
|
+
handleTransactionEvent(replay, event);
|
|
4747
4773
|
return;
|
|
4748
4774
|
}
|
|
4749
4775
|
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
}
|
|
4776
|
+
handleErrorEvent(replay, event);
|
|
4777
|
+
};
|
|
4778
|
+
}
|
|
4754
4779
|
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
// XXX: There is also a race condition where it's possible to capture an
|
|
4758
|
-
// error to Sentry before Replay SDK has loaded, but response returns after
|
|
4759
|
-
// it was loaded, and this gets called.
|
|
4760
|
-
if (event.event_id) {
|
|
4761
|
-
replay.getContext().errorIds.add(event.event_id);
|
|
4762
|
-
}
|
|
4780
|
+
function handleTransactionEvent(replay, event) {
|
|
4781
|
+
const replayContext = replay.getContext();
|
|
4763
4782
|
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4783
|
+
// Collect traceIds in _context regardless of `recordingMode`
|
|
4784
|
+
// In error mode, _context gets cleared on every checkout
|
|
4785
|
+
// We limit to max. 100 transactions linked
|
|
4786
|
+
if (event.contexts && event.contexts.trace && event.contexts.trace.trace_id && replayContext.traceIds.size < 100) {
|
|
4787
|
+
replayContext.traceIds.add(event.contexts.trace.trace_id );
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4790
|
+
|
|
4791
|
+
function handleErrorEvent(replay, event) {
|
|
4792
|
+
const replayContext = replay.getContext();
|
|
4793
|
+
|
|
4794
|
+
// Add error to list of errorIds of replay. This is ok to do even if not
|
|
4795
|
+
// sampled because context will get reset at next checkout.
|
|
4796
|
+
// XXX: There is also a race condition where it's possible to capture an
|
|
4797
|
+
// error to Sentry before Replay SDK has loaded, but response returns after
|
|
4798
|
+
// it was loaded, and this gets called.
|
|
4799
|
+
// We limit to max. 100 errors linked
|
|
4800
|
+
if (event.event_id && replayContext.errorIds.size < 100) {
|
|
4801
|
+
replayContext.errorIds.add(event.event_id);
|
|
4802
|
+
}
|
|
4803
|
+
|
|
4804
|
+
// If error event is tagged with replay id it means it was sampled (when in buffer mode)
|
|
4805
|
+
// Need to be very careful that this does not cause an infinite loop
|
|
4806
|
+
if (replay.recordingMode === 'buffer' && event.tags && event.tags.replayId) {
|
|
4807
|
+
setTimeout(() => {
|
|
4808
|
+
// Capture current event buffer as new replay
|
|
4809
|
+
void replay.sendBufferedReplayOrFlush();
|
|
4810
|
+
});
|
|
4811
|
+
}
|
|
4773
4812
|
}
|
|
4774
4813
|
|
|
4775
4814
|
function isBaseTransportSend() {
|
|
@@ -4845,6 +4884,11 @@ function handleGlobalEventListener(
|
|
|
4845
4884
|
const afterSendHandler = includeAfterSendEventHandling ? handleAfterSendEvent(replay) : undefined;
|
|
4846
4885
|
|
|
4847
4886
|
return (event, hint) => {
|
|
4887
|
+
// Do nothing if replay has been disabled
|
|
4888
|
+
if (!replay.isEnabled()) {
|
|
4889
|
+
return event;
|
|
4890
|
+
}
|
|
4891
|
+
|
|
4848
4892
|
if (isReplayEvent(event)) {
|
|
4849
4893
|
// Replays have separate set of breadcrumbs, do not include breadcrumbs
|
|
4850
4894
|
// from core SDK
|
|
@@ -6626,10 +6670,10 @@ function getHandleRecordingEmit(replay) {
|
|
|
6626
6670
|
if (replay.recordingMode === 'buffer' && replay.session && replay.eventBuffer) {
|
|
6627
6671
|
const earliestEvent = replay.eventBuffer.getEarliestTimestamp();
|
|
6628
6672
|
if (earliestEvent) {
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6673
|
+
logInfo(
|
|
6674
|
+
`[Replay] Updating session start time to earliest event in buffer to ${new Date(earliestEvent)}`,
|
|
6675
|
+
replay.getOptions()._experiments.traceInternals,
|
|
6676
|
+
);
|
|
6633
6677
|
|
|
6634
6678
|
replay.session.started = earliestEvent;
|
|
6635
6679
|
|
|
@@ -6843,7 +6887,7 @@ async function sendReplayRequest({
|
|
|
6843
6887
|
if (!replayEvent) {
|
|
6844
6888
|
// Taken from baseclient's `_processEvent` method, where this is handled for errors/transactions
|
|
6845
6889
|
client.recordDroppedEvent('event_processor', 'replay', baseEvent);
|
|
6846
|
-
(
|
|
6890
|
+
logInfo('An event processor returned `null`, will not send event.');
|
|
6847
6891
|
return;
|
|
6848
6892
|
}
|
|
6849
6893
|
|
|
@@ -7056,12 +7100,10 @@ function throttle(
|
|
|
7056
7100
|
* The main replay container class, which holds all the state and methods for recording and sending replays.
|
|
7057
7101
|
*/
|
|
7058
7102
|
class ReplayContainer {
|
|
7059
|
-
__init() {this.eventBuffer = null;}
|
|
7060
7103
|
|
|
7061
7104
|
/**
|
|
7062
7105
|
* List of PerformanceEntry from PerformanceObserver
|
|
7063
7106
|
*/
|
|
7064
|
-
__init2() {this.performanceEvents = [];}
|
|
7065
7107
|
|
|
7066
7108
|
/**
|
|
7067
7109
|
* Recording can happen in one of three modes:
|
|
@@ -7070,7 +7112,6 @@ class ReplayContainer {
|
|
|
7070
7112
|
* - having replaysOnErrorSampleRate > 0 to capture replay when an error occurs
|
|
7071
7113
|
* - or calling `flush()` to send the replay
|
|
7072
7114
|
*/
|
|
7073
|
-
__init3() {this.recordingMode = 'session';}
|
|
7074
7115
|
|
|
7075
7116
|
/**
|
|
7076
7117
|
* The current or last active transcation.
|
|
@@ -7081,62 +7122,60 @@ class ReplayContainer {
|
|
|
7081
7122
|
* These are here so we can overwrite them in tests etc.
|
|
7082
7123
|
* @hidden
|
|
7083
7124
|
*/
|
|
7084
|
-
__init4() {this.timeouts = {
|
|
7085
|
-
sessionIdlePause: SESSION_IDLE_PAUSE_DURATION,
|
|
7086
|
-
sessionIdleExpire: SESSION_IDLE_EXPIRE_DURATION,
|
|
7087
|
-
maxSessionLife: MAX_SESSION_LIFE,
|
|
7088
|
-
}; }
|
|
7089
7125
|
|
|
7090
7126
|
/**
|
|
7091
7127
|
* Options to pass to `rrweb.record()`
|
|
7092
7128
|
*/
|
|
7093
7129
|
|
|
7094
|
-
__init5() {this._performanceObserver = null;}
|
|
7095
|
-
|
|
7096
|
-
__init6() {this._flushLock = null;}
|
|
7097
|
-
|
|
7098
7130
|
/**
|
|
7099
7131
|
* Timestamp of the last user activity. This lives across sessions.
|
|
7100
7132
|
*/
|
|
7101
|
-
__init7() {this._lastActivity = Date.now();}
|
|
7102
7133
|
|
|
7103
7134
|
/**
|
|
7104
7135
|
* Is the integration currently active?
|
|
7105
7136
|
*/
|
|
7106
|
-
__init8() {this._isEnabled = false;}
|
|
7107
7137
|
|
|
7108
7138
|
/**
|
|
7109
7139
|
* Paused is a state where:
|
|
7110
7140
|
* - DOM Recording is not listening at all
|
|
7111
7141
|
* - Nothing will be added to event buffer (e.g. core SDK events)
|
|
7112
7142
|
*/
|
|
7113
|
-
__init9() {this._isPaused = false;}
|
|
7114
7143
|
|
|
7115
7144
|
/**
|
|
7116
7145
|
* Have we attached listeners to the core SDK?
|
|
7117
7146
|
* Note we have to track this as there is no way to remove instrumentation handlers.
|
|
7118
7147
|
*/
|
|
7119
|
-
__init10() {this._hasInitializedCoreListeners = false;}
|
|
7120
7148
|
|
|
7121
7149
|
/**
|
|
7122
7150
|
* Function to stop recording
|
|
7123
7151
|
*/
|
|
7124
|
-
__init11() {this._stopRecording = null;}
|
|
7125
|
-
|
|
7126
|
-
__init12() {this._context = {
|
|
7127
|
-
errorIds: new Set(),
|
|
7128
|
-
traceIds: new Set(),
|
|
7129
|
-
urls: [],
|
|
7130
|
-
initialTimestamp: Date.now(),
|
|
7131
|
-
initialUrl: '',
|
|
7132
|
-
};}
|
|
7133
7152
|
|
|
7134
7153
|
constructor({
|
|
7135
7154
|
options,
|
|
7136
7155
|
recordingOptions,
|
|
7137
7156
|
}
|
|
7138
7157
|
|
|
7139
|
-
) {ReplayContainer.prototype.__init.call(this);ReplayContainer.prototype.__init2.call(this);ReplayContainer.prototype.__init3.call(this);ReplayContainer.prototype.__init4.call(this);ReplayContainer.prototype.__init5.call(this);ReplayContainer.prototype.__init6.call(this);
|
|
7158
|
+
) {ReplayContainer.prototype.__init.call(this);ReplayContainer.prototype.__init2.call(this);ReplayContainer.prototype.__init3.call(this);ReplayContainer.prototype.__init4.call(this);ReplayContainer.prototype.__init5.call(this);ReplayContainer.prototype.__init6.call(this);
|
|
7159
|
+
this.eventBuffer = null;
|
|
7160
|
+
this.performanceEvents = [];
|
|
7161
|
+
this.recordingMode = 'session';
|
|
7162
|
+
this.timeouts = {
|
|
7163
|
+
sessionIdlePause: SESSION_IDLE_PAUSE_DURATION,
|
|
7164
|
+
sessionIdleExpire: SESSION_IDLE_EXPIRE_DURATION,
|
|
7165
|
+
maxSessionLife: MAX_SESSION_LIFE,
|
|
7166
|
+
} ;
|
|
7167
|
+
this._lastActivity = Date.now();
|
|
7168
|
+
this._isEnabled = false;
|
|
7169
|
+
this._isPaused = false;
|
|
7170
|
+
this._hasInitializedCoreListeners = false;
|
|
7171
|
+
this._context = {
|
|
7172
|
+
errorIds: new Set(),
|
|
7173
|
+
traceIds: new Set(),
|
|
7174
|
+
urls: [],
|
|
7175
|
+
initialTimestamp: Date.now(),
|
|
7176
|
+
initialUrl: '',
|
|
7177
|
+
};
|
|
7178
|
+
|
|
7140
7179
|
this._recordingOptions = recordingOptions;
|
|
7141
7180
|
this._options = options;
|
|
7142
7181
|
|
|
@@ -7225,6 +7264,8 @@ class ReplayContainer {
|
|
|
7225
7264
|
this.recordingMode = 'buffer';
|
|
7226
7265
|
}
|
|
7227
7266
|
|
|
7267
|
+
logInfo(`[Replay] Starting replay in ${this.recordingMode} mode`, this._options._experiments.traceInternals);
|
|
7268
|
+
|
|
7228
7269
|
this._initializeRecording();
|
|
7229
7270
|
}
|
|
7230
7271
|
|
|
@@ -7244,6 +7285,8 @@ class ReplayContainer {
|
|
|
7244
7285
|
throw new Error('Replay buffering is in progress, call `flush()` to save the replay');
|
|
7245
7286
|
}
|
|
7246
7287
|
|
|
7288
|
+
logInfo('[Replay] Starting replay in session mode', this._options._experiments.traceInternals);
|
|
7289
|
+
|
|
7247
7290
|
const previousSessionId = this.session && this.session.id;
|
|
7248
7291
|
|
|
7249
7292
|
const { session } = getSession({
|
|
@@ -7253,6 +7296,7 @@ class ReplayContainer {
|
|
|
7253
7296
|
// This is intentional: create a new session-based replay when calling `start()`
|
|
7254
7297
|
sessionSampleRate: 1,
|
|
7255
7298
|
allowBuffering: false,
|
|
7299
|
+
traceInternals: this._options._experiments.traceInternals,
|
|
7256
7300
|
});
|
|
7257
7301
|
|
|
7258
7302
|
session.previousSessionId = previousSessionId;
|
|
@@ -7270,6 +7314,8 @@ class ReplayContainer {
|
|
|
7270
7314
|
throw new Error('Replay recording is already in progress');
|
|
7271
7315
|
}
|
|
7272
7316
|
|
|
7317
|
+
logInfo('[Replay] Starting replay in buffer mode', this._options._experiments.traceInternals);
|
|
7318
|
+
|
|
7273
7319
|
const previousSessionId = this.session && this.session.id;
|
|
7274
7320
|
|
|
7275
7321
|
const { session } = getSession({
|
|
@@ -7278,6 +7324,7 @@ class ReplayContainer {
|
|
|
7278
7324
|
currentSession: this.session,
|
|
7279
7325
|
sessionSampleRate: 0,
|
|
7280
7326
|
allowBuffering: true,
|
|
7327
|
+
traceInternals: this._options._experiments.traceInternals,
|
|
7281
7328
|
});
|
|
7282
7329
|
|
|
7283
7330
|
session.previousSessionId = previousSessionId;
|
|
@@ -7338,15 +7385,10 @@ class ReplayContainer {
|
|
|
7338
7385
|
}
|
|
7339
7386
|
|
|
7340
7387
|
try {
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
// Else, use the regular debug output
|
|
7346
|
-
// eslint-disable-next-line
|
|
7347
|
-
const log = this.getOptions()._experiments.traceInternals ? console.warn : logger.log;
|
|
7348
|
-
log(msg);
|
|
7349
|
-
}
|
|
7388
|
+
logInfo(
|
|
7389
|
+
`[Replay] Stopping Replay${reason ? ` triggered by ${reason}` : ''}`,
|
|
7390
|
+
this._options._experiments.traceInternals,
|
|
7391
|
+
);
|
|
7350
7392
|
|
|
7351
7393
|
// We can't move `_isEnabled` after awaiting a flush, otherwise we can
|
|
7352
7394
|
// enter into an infinite loop when `stop()` is called while flushing.
|
|
@@ -7379,8 +7421,14 @@ class ReplayContainer {
|
|
|
7379
7421
|
* not as thorough of a shutdown as `stop()`.
|
|
7380
7422
|
*/
|
|
7381
7423
|
pause() {
|
|
7424
|
+
if (this._isPaused) {
|
|
7425
|
+
return;
|
|
7426
|
+
}
|
|
7427
|
+
|
|
7382
7428
|
this._isPaused = true;
|
|
7383
7429
|
this.stopRecording();
|
|
7430
|
+
|
|
7431
|
+
logInfo('[Replay] Pausing replay', this._options._experiments.traceInternals);
|
|
7384
7432
|
}
|
|
7385
7433
|
|
|
7386
7434
|
/**
|
|
@@ -7390,12 +7438,14 @@ class ReplayContainer {
|
|
|
7390
7438
|
* new DOM checkout.`
|
|
7391
7439
|
*/
|
|
7392
7440
|
resume() {
|
|
7393
|
-
if (!this._loadAndCheckSession()) {
|
|
7441
|
+
if (!this._isPaused || !this._loadAndCheckSession()) {
|
|
7394
7442
|
return;
|
|
7395
7443
|
}
|
|
7396
7444
|
|
|
7397
7445
|
this._isPaused = false;
|
|
7398
7446
|
this.startRecording();
|
|
7447
|
+
|
|
7448
|
+
logInfo('[Replay] Resuming replay', this._options._experiments.traceInternals);
|
|
7399
7449
|
}
|
|
7400
7450
|
|
|
7401
7451
|
/**
|
|
@@ -7412,9 +7462,7 @@ class ReplayContainer {
|
|
|
7412
7462
|
|
|
7413
7463
|
const activityTime = Date.now();
|
|
7414
7464
|
|
|
7415
|
-
|
|
7416
|
-
const log = this.getOptions()._experiments.traceInternals ? console.info : logger.info;
|
|
7417
|
-
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && log(`[Replay] Converting buffer to session, starting at ${activityTime}`);
|
|
7465
|
+
logInfo('[Replay] Converting buffer to session', this._options._experiments.traceInternals);
|
|
7418
7466
|
|
|
7419
7467
|
// Allow flush to complete before resuming as a session recording, otherwise
|
|
7420
7468
|
// the checkout from `startRecording` may be included in the payload.
|
|
@@ -7428,10 +7476,12 @@ class ReplayContainer {
|
|
|
7428
7476
|
return;
|
|
7429
7477
|
}
|
|
7430
7478
|
|
|
7431
|
-
//
|
|
7479
|
+
// To avoid race conditions where this is called multiple times, we check here again that we are still buffering
|
|
7480
|
+
if ((this.recordingMode ) === 'session') {
|
|
7481
|
+
return;
|
|
7482
|
+
}
|
|
7432
7483
|
|
|
7433
|
-
//
|
|
7434
|
-
// starting a new recording
|
|
7484
|
+
// Re-start recording in session-mode
|
|
7435
7485
|
this.recordingMode = 'session';
|
|
7436
7486
|
|
|
7437
7487
|
// Once this session ends, we do not want to refresh it
|
|
@@ -7448,7 +7498,6 @@ class ReplayContainer {
|
|
|
7448
7498
|
// (length of buffer), which we are ok with.
|
|
7449
7499
|
this._updateUserActivity(activityTime);
|
|
7450
7500
|
this._updateSessionActivity(activityTime);
|
|
7451
|
-
this.session.started = activityTime;
|
|
7452
7501
|
this._maybeSaveSession();
|
|
7453
7502
|
}
|
|
7454
7503
|
|
|
@@ -7722,6 +7771,7 @@ class ReplayContainer {
|
|
|
7722
7771
|
currentSession: this.session,
|
|
7723
7772
|
sessionSampleRate: this._options.sessionSampleRate,
|
|
7724
7773
|
allowBuffering: this._options.errorSampleRate > 0 || this.recordingMode === 'buffer',
|
|
7774
|
+
traceInternals: this._options._experiments.traceInternals,
|
|
7725
7775
|
});
|
|
7726
7776
|
|
|
7727
7777
|
// If session was newly created (i.e. was not loaded from storage), then
|
|
@@ -7738,7 +7788,7 @@ class ReplayContainer {
|
|
|
7738
7788
|
this.session = session;
|
|
7739
7789
|
|
|
7740
7790
|
if (!this.session.sampled) {
|
|
7741
|
-
void this.stop('session
|
|
7791
|
+
void this.stop('session not refreshed');
|
|
7742
7792
|
return false;
|
|
7743
7793
|
}
|
|
7744
7794
|
|
|
@@ -7794,7 +7844,7 @@ class ReplayContainer {
|
|
|
7794
7844
|
|
|
7795
7845
|
if (this._performanceObserver) {
|
|
7796
7846
|
this._performanceObserver.disconnect();
|
|
7797
|
-
this._performanceObserver =
|
|
7847
|
+
this._performanceObserver = undefined;
|
|
7798
7848
|
}
|
|
7799
7849
|
} catch (err) {
|
|
7800
7850
|
this._handleException(err);
|
|
@@ -7807,7 +7857,7 @@ class ReplayContainer {
|
|
|
7807
7857
|
* be hidden. Likewise, moving a different window to cover the contents of the
|
|
7808
7858
|
* page will also trigger a change to a hidden state.
|
|
7809
7859
|
*/
|
|
7810
|
-
|
|
7860
|
+
__init() {this._handleVisibilityChange = () => {
|
|
7811
7861
|
if (WINDOW.document.visibilityState === 'visible') {
|
|
7812
7862
|
this._doChangeToForegroundTasks();
|
|
7813
7863
|
} else {
|
|
@@ -7818,7 +7868,7 @@ class ReplayContainer {
|
|
|
7818
7868
|
/**
|
|
7819
7869
|
* Handle when page is blurred
|
|
7820
7870
|
*/
|
|
7821
|
-
|
|
7871
|
+
__init2() {this._handleWindowBlur = () => {
|
|
7822
7872
|
const breadcrumb = createBreadcrumb({
|
|
7823
7873
|
category: 'ui.blur',
|
|
7824
7874
|
});
|
|
@@ -7831,7 +7881,7 @@ class ReplayContainer {
|
|
|
7831
7881
|
/**
|
|
7832
7882
|
* Handle when page is focused
|
|
7833
7883
|
*/
|
|
7834
|
-
|
|
7884
|
+
__init3() {this._handleWindowFocus = () => {
|
|
7835
7885
|
const breadcrumb = createBreadcrumb({
|
|
7836
7886
|
category: 'ui.focus',
|
|
7837
7887
|
});
|
|
@@ -7842,7 +7892,7 @@ class ReplayContainer {
|
|
|
7842
7892
|
};}
|
|
7843
7893
|
|
|
7844
7894
|
/** Ensure page remains active when a key is pressed. */
|
|
7845
|
-
|
|
7895
|
+
__init4() {this._handleKeyboardEvent = (event) => {
|
|
7846
7896
|
handleKeyboardEvent(this, event);
|
|
7847
7897
|
};}
|
|
7848
7898
|
|
|
@@ -7880,7 +7930,7 @@ class ReplayContainer {
|
|
|
7880
7930
|
// If the user has come back to the page within SESSION_IDLE_PAUSE_DURATION
|
|
7881
7931
|
// ms, we will re-use the existing session, otherwise create a new
|
|
7882
7932
|
// session
|
|
7883
|
-
(
|
|
7933
|
+
logInfo('[Replay] Document has become active, but session has expired');
|
|
7884
7934
|
return;
|
|
7885
7935
|
}
|
|
7886
7936
|
|
|
@@ -7895,7 +7945,7 @@ class ReplayContainer {
|
|
|
7895
7945
|
*/
|
|
7896
7946
|
_triggerFullSnapshot(checkout = true) {
|
|
7897
7947
|
try {
|
|
7898
|
-
(
|
|
7948
|
+
logInfo('[Replay] Taking full rrweb snapshot');
|
|
7899
7949
|
record.takeFullSnapshot(checkout);
|
|
7900
7950
|
} catch (err) {
|
|
7901
7951
|
this._handleException(err);
|
|
@@ -8065,7 +8115,7 @@ class ReplayContainer {
|
|
|
8065
8115
|
* Flush recording data to Sentry. Creates a lock so that only a single flush
|
|
8066
8116
|
* can be active at a time. Do not call this directly.
|
|
8067
8117
|
*/
|
|
8068
|
-
|
|
8118
|
+
__init5() {this._flush = async ({
|
|
8069
8119
|
force = false,
|
|
8070
8120
|
}
|
|
8071
8121
|
|
|
@@ -8091,14 +8141,18 @@ class ReplayContainer {
|
|
|
8091
8141
|
|
|
8092
8142
|
// If session is too short, or too long (allow some wiggle room over maxSessionLife), do not send it
|
|
8093
8143
|
// This _should_ not happen, but it may happen if flush is triggered due to a page activity change or similar
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
(
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8144
|
+
const tooShort = duration < this._options.minReplayDuration;
|
|
8145
|
+
const tooLong = duration > this.timeouts.maxSessionLife + 5000;
|
|
8146
|
+
if (tooShort || tooLong) {
|
|
8147
|
+
logInfo(
|
|
8148
|
+
`[Replay] Session duration (${Math.floor(duration / 1000)}s) is too ${
|
|
8149
|
+
tooShort ? 'short' : 'long'
|
|
8150
|
+
}, not sending replay.`,
|
|
8151
|
+
);
|
|
8101
8152
|
|
|
8153
|
+
if (tooShort) {
|
|
8154
|
+
this._debouncedFlush();
|
|
8155
|
+
}
|
|
8102
8156
|
return;
|
|
8103
8157
|
}
|
|
8104
8158
|
|
|
@@ -8110,7 +8164,7 @@ class ReplayContainer {
|
|
|
8110
8164
|
if (!this._flushLock) {
|
|
8111
8165
|
this._flushLock = this._runFlush();
|
|
8112
8166
|
await this._flushLock;
|
|
8113
|
-
this._flushLock =
|
|
8167
|
+
this._flushLock = undefined;
|
|
8114
8168
|
return;
|
|
8115
8169
|
}
|
|
8116
8170
|
|
|
@@ -8137,7 +8191,7 @@ class ReplayContainer {
|
|
|
8137
8191
|
}
|
|
8138
8192
|
|
|
8139
8193
|
/** Handler for rrweb.record.onMutation */
|
|
8140
|
-
|
|
8194
|
+
__init6() {this._onMutationHandler = (mutations) => {
|
|
8141
8195
|
const count = mutations.length;
|
|
8142
8196
|
|
|
8143
8197
|
const mutationLimit = this._options.mutationLimit;
|
|
@@ -8287,7 +8341,6 @@ class Replay {
|
|
|
8287
8341
|
/**
|
|
8288
8342
|
* @inheritDoc
|
|
8289
8343
|
*/
|
|
8290
|
-
__init() {this.name = Replay.id;}
|
|
8291
8344
|
|
|
8292
8345
|
/**
|
|
8293
8346
|
* Options to pass to `rrweb.record()`
|
|
@@ -8347,7 +8400,9 @@ class Replay {
|
|
|
8347
8400
|
maskTextSelector,
|
|
8348
8401
|
// eslint-disable-next-line deprecation/deprecation
|
|
8349
8402
|
ignoreClass,
|
|
8350
|
-
} = {}) {
|
|
8403
|
+
} = {}) {
|
|
8404
|
+
this.name = Replay.id;
|
|
8405
|
+
|
|
8351
8406
|
this._recordingOptions = {
|
|
8352
8407
|
maskAllInputs,
|
|
8353
8408
|
maskAllText,
|