@sentry/replay 7.67.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 +144 -153
- package/cjs/index.js.map +1 -1
- package/esm/index.js +144 -153
- package/esm/index.js.map +1 -1
- package/package.json +6 -6
- package/types/replay.d.ts +7 -1
- package/types/replay.d.ts.map +1 -1
- package/types/session/Session.d.ts.map +1 -1
- package/types/session/loadOrCreateSession.d.ts +2 -1
- package/types/session/loadOrCreateSession.d.ts.map +1 -1
- package/types/session/shouldRefreshSession.d.ts +7 -0
- package/types/session/shouldRefreshSession.d.ts.map +1 -0
- package/types/types/replay.d.ts +0 -5
- package/types/types/replay.d.ts.map +1 -1
- package/types/util/addEvent.d.ts +13 -0
- package/types/util/addEvent.d.ts.map +1 -1
- package/types/util/addGlobalListeners.d.ts.map +1 -1
- package/types/util/createPerformanceEntries.d.ts.map +1 -1
- package/types/util/handleRecordingEmit.d.ts.map +1 -1
- package/types-ts3.8/constants.d.ts +25 -0
- package/types-ts3.8/coreHandlers/handleAfterSendEvent.d.ts +9 -0
- package/types-ts3.8/coreHandlers/handleClick.d.ts +39 -0
- package/types-ts3.8/coreHandlers/handleDom.d.ts +13 -0
- package/types-ts3.8/coreHandlers/handleFetch.d.ts +9 -0
- package/types-ts3.8/coreHandlers/handleGlobalEvent.d.ts +7 -0
- package/types-ts3.8/coreHandlers/handleHistory.d.ts +11 -0
- package/types-ts3.8/coreHandlers/handleKeyboardEvent.d.ts +7 -0
- package/types-ts3.8/coreHandlers/handleNetworkBreadcrumbs.d.ts +17 -0
- package/types-ts3.8/coreHandlers/handleScope.d.ts +13 -0
- package/types-ts3.8/coreHandlers/handleXhr.d.ts +9 -0
- package/types-ts3.8/coreHandlers/performanceObserver.d.ts +6 -0
- package/types-ts3.8/coreHandlers/util/addBreadcrumbEvent.d.ts +7 -0
- package/types-ts3.8/coreHandlers/util/addNetworkBreadcrumb.d.ts +4 -0
- package/types-ts3.8/coreHandlers/util/domUtils.d.ts +19 -0
- package/types-ts3.8/coreHandlers/util/fetchUtils.d.ts +23 -0
- package/types-ts3.8/coreHandlers/util/getAttributesToRecord.d.ts +5 -0
- package/types-ts3.8/coreHandlers/util/networkUtils.d.ts +23 -0
- package/types-ts3.8/coreHandlers/util/onWindowOpen.d.ts +8 -0
- package/types-ts3.8/coreHandlers/util/shouldSampleForBufferEvent.d.ts +9 -0
- package/types-ts3.8/coreHandlers/util/xhrUtils.d.ts +22 -0
- package/types-ts3.8/eventBuffer/EventBufferArray.d.ts +28 -0
- package/types-ts3.8/eventBuffer/EventBufferCompressionWorker.d.ts +50 -0
- package/types-ts3.8/eventBuffer/EventBufferProxy.d.ts +42 -0
- package/types-ts3.8/eventBuffer/WorkerHandler.d.ts +27 -0
- package/types-ts3.8/eventBuffer/error.d.ts +5 -0
- package/types-ts3.8/eventBuffer/index.d.ts +10 -0
- package/types-ts3.8/index.d.ts +4 -0
- package/types-ts3.8/integration.d.ts +73 -0
- package/types-ts3.8/replay.d.ts +314 -0
- package/types-ts3.8/session/Session.d.ts +8 -0
- package/types-ts3.8/session/clearSession.d.ts +6 -0
- package/types-ts3.8/session/createSession.d.ts +14 -0
- package/types-ts3.8/session/fetchSession.d.ts +6 -0
- package/types-ts3.8/session/index.d.ts +2 -0
- package/types-ts3.8/session/loadOrCreateSession.d.ts +12 -0
- package/types-ts3.8/session/saveSession.d.ts +6 -0
- package/types-ts3.8/session/shouldRefreshSession.d.ts +7 -0
- package/types-ts3.8/types/deprecated.d.ts +16 -0
- package/types-ts3.8/types/index.d.ts +6 -0
- package/types-ts3.8/types/performance.d.ts +124 -0
- package/types-ts3.8/types/replay.d.ts +417 -0
- package/types-ts3.8/types/replayFrame.d.ts +173 -0
- package/types-ts3.8/types/request.d.ts +15 -0
- package/types-ts3.8/types/rrweb.d.ts +37 -0
- package/types-ts3.8/util/addEvent.d.ts +20 -0
- package/types-ts3.8/util/addGlobalListeners.d.ts +6 -0
- package/types-ts3.8/util/addMemoryEntry.d.ts +7 -0
- package/types-ts3.8/util/createBreadcrumb.d.ts +6 -0
- package/types-ts3.8/util/createPerformanceEntries.d.ts +6 -0
- package/types-ts3.8/util/createPerformanceSpans.d.ts +6 -0
- package/types-ts3.8/util/createReplayEnvelope.d.ts +7 -0
- package/types-ts3.8/util/debounce.d.ts +30 -0
- package/types-ts3.8/util/dedupePerformanceEntries.d.ts +11 -0
- package/types-ts3.8/util/eventUtils.d.ts +8 -0
- package/types-ts3.8/util/getPrivacyOptions.d.ts +19 -0
- package/types-ts3.8/util/handleRecordingEmit.d.ts +14 -0
- package/types-ts3.8/util/hasSessionStorage.d.ts +3 -0
- package/types-ts3.8/util/isBrowser.d.ts +5 -0
- package/types-ts3.8/util/isExpired.d.ts +6 -0
- package/types-ts3.8/util/isRrwebError.d.ts +6 -0
- package/types-ts3.8/util/isSampled.d.ts +8 -0
- package/types-ts3.8/util/isSessionExpired.d.ts +10 -0
- package/types-ts3.8/util/log.d.ts +10 -0
- package/types-ts3.8/util/prepareRecordingData.d.ts +9 -0
- package/types-ts3.8/util/prepareReplayEvent.d.ts +15 -0
- package/types-ts3.8/util/sendReplay.d.ts +9 -0
- package/types-ts3.8/util/sendReplayRequest.d.ts +13 -0
- package/types-ts3.8/util/shouldFilterRequest.d.ts +7 -0
- package/types-ts3.8/util/throttle.d.ts +12 -0
- package/types-ts3.8/util/timestamp.d.ts +9 -0
- package/types-ts3.8/util/truncateJson/completeJson.d.ts +9 -0
- package/types-ts3.8/util/truncateJson/constants.d.ts +12 -0
- package/types-ts3.8/util/truncateJson/evaluateJson.d.ts +6 -0
- package/types-ts3.8/util/truncateJson/fixJson.d.ts +6 -0
- package/types/session/maybeRefreshSession.d.ts +0 -12
- package/types/session/maybeRefreshSession.d.ts.map +0 -1
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
|
-
|
|
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
|
|
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 &
|
|
4705
|
-
|
|
4706
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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]
|
|
4740
|
-
return createSession(sessionOptions);
|
|
4711
|
+
logInfoNextTick('[Replay] Creating new session', traceInternals);
|
|
4712
|
+
return createSession(sessionOptions, { previousSessionId });
|
|
4741
4713
|
}
|
|
4742
4714
|
|
|
4743
|
-
|
|
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
|
-
|
|
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
|
|
4762
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
6501
|
+
// @ts-expect-error TODO: entry type does not fit the create* functions entry type
|
|
6493
6502
|
navigation: createNavigationEntry,
|
|
6494
|
-
// @ts-
|
|
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
|
-
//
|
|
6746
|
-
//
|
|
6747
|
-
|
|
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
|
-
|
|
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
|
|
6850
|
+
return;
|
|
6839
6851
|
}
|
|
6840
6852
|
|
|
6841
|
-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
7759
|
+
return true;
|
|
7774
7760
|
}
|
|
7775
7761
|
|
|
7776
7762
|
/**
|
|
@@ -7810,7 +7796,8 @@ class ReplayContainer {
|
|
|
7810
7796
|
});
|
|
7811
7797
|
|
|
7812
7798
|
this.addUpdate(() => {
|
|
7813
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8037
|
+
sessionIdleExpire: this.timeouts.sessionIdleExpire,
|
|
8055
8038
|
});
|
|
8056
8039
|
|
|
8057
|
-
if (
|
|
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
|
-
|
|
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,
|