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