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