@sentry/replay 7.64.0 → 7.65.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 +188 -130
- package/cjs/index.js.map +1 -1
- package/esm/index.js +188 -130
- package/esm/index.js.map +1 -1
- package/package.json +5 -5
- package/types/replay.d.ts +5 -1
- package/types/replay.d.ts.map +1 -1
- package/types/session/Session.d.ts.map +1 -1
- package/types/session/createSession.d.ts +3 -1
- package/types/session/createSession.d.ts.map +1 -1
- package/types/session/loadOrCreateSession.d.ts +10 -0
- package/types/session/loadOrCreateSession.d.ts.map +1 -0
- package/types/session/maybeRefreshSession.d.ts +11 -0
- package/types/session/maybeRefreshSession.d.ts.map +1 -0
- package/types/session/getSession.d.ts +0 -18
- package/types/session/getSession.d.ts.map +0 -1
package/cjs/index.js
CHANGED
|
@@ -4521,41 +4521,6 @@ function deleteSession() {
|
|
|
4521
4521
|
}
|
|
4522
4522
|
}
|
|
4523
4523
|
|
|
4524
|
-
/**
|
|
4525
|
-
* Given an initial timestamp and an expiry duration, checks to see if current
|
|
4526
|
-
* time should be considered as expired.
|
|
4527
|
-
*/
|
|
4528
|
-
function isExpired(
|
|
4529
|
-
initialTime,
|
|
4530
|
-
expiry,
|
|
4531
|
-
targetTime = +new Date(),
|
|
4532
|
-
) {
|
|
4533
|
-
// Always expired if < 0
|
|
4534
|
-
if (initialTime === null || expiry === undefined || expiry < 0) {
|
|
4535
|
-
return true;
|
|
4536
|
-
}
|
|
4537
|
-
|
|
4538
|
-
// Never expires if == 0
|
|
4539
|
-
if (expiry === 0) {
|
|
4540
|
-
return false;
|
|
4541
|
-
}
|
|
4542
|
-
|
|
4543
|
-
return initialTime + expiry <= targetTime;
|
|
4544
|
-
}
|
|
4545
|
-
|
|
4546
|
-
/**
|
|
4547
|
-
* Checks to see if session is expired
|
|
4548
|
-
*/
|
|
4549
|
-
function isSessionExpired(session, timeouts, targetTime = +new Date()) {
|
|
4550
|
-
return (
|
|
4551
|
-
// First, check that maximum session length has not been exceeded
|
|
4552
|
-
isExpired(session.started, timeouts.maxSessionLife, targetTime) ||
|
|
4553
|
-
// check that the idle timeout has not been exceeded (i.e. user has
|
|
4554
|
-
// performed an action within the last `sessionIdleExpire` ms)
|
|
4555
|
-
isExpired(session.lastActivity, timeouts.sessionIdleExpire, targetTime)
|
|
4556
|
-
);
|
|
4557
|
-
}
|
|
4558
|
-
|
|
4559
4524
|
/**
|
|
4560
4525
|
* Given a sample rate, returns true if replay should be sampled.
|
|
4561
4526
|
*
|
|
@@ -4598,6 +4563,7 @@ function makeSession(session) {
|
|
|
4598
4563
|
const segmentId = session.segmentId || 0;
|
|
4599
4564
|
const sampled = session.sampled;
|
|
4600
4565
|
const shouldRefresh = typeof session.shouldRefresh === 'boolean' ? session.shouldRefresh : true;
|
|
4566
|
+
const previousSessionId = session.previousSessionId;
|
|
4601
4567
|
|
|
4602
4568
|
return {
|
|
4603
4569
|
id,
|
|
@@ -4606,6 +4572,7 @@ function makeSession(session) {
|
|
|
4606
4572
|
segmentId,
|
|
4607
4573
|
sampled,
|
|
4608
4574
|
shouldRefresh,
|
|
4575
|
+
previousSessionId,
|
|
4609
4576
|
};
|
|
4610
4577
|
}
|
|
4611
4578
|
|
|
@@ -4621,10 +4588,14 @@ function getSessionSampleType(sessionSampleRate, allowBuffering) {
|
|
|
4621
4588
|
* that all replays will be saved to as attachments. Currently, we only expect
|
|
4622
4589
|
* one of these Sentry events per "replay session".
|
|
4623
4590
|
*/
|
|
4624
|
-
function createSession(
|
|
4591
|
+
function createSession(
|
|
4592
|
+
{ sessionSampleRate, allowBuffering, stickySession = false },
|
|
4593
|
+
{ previousSessionId } = {},
|
|
4594
|
+
) {
|
|
4625
4595
|
const sampled = getSessionSampleType(sessionSampleRate, allowBuffering);
|
|
4626
4596
|
const session = makeSession({
|
|
4627
4597
|
sampled,
|
|
4598
|
+
previousSessionId,
|
|
4628
4599
|
});
|
|
4629
4600
|
|
|
4630
4601
|
if (stickySession) {
|
|
@@ -4661,49 +4632,106 @@ function fetchSession(traceInternals) {
|
|
|
4661
4632
|
}
|
|
4662
4633
|
|
|
4663
4634
|
/**
|
|
4664
|
-
*
|
|
4635
|
+
* Given an initial timestamp and an expiry duration, checks to see if current
|
|
4636
|
+
* time should be considered as expired.
|
|
4637
|
+
*/
|
|
4638
|
+
function isExpired(
|
|
4639
|
+
initialTime,
|
|
4640
|
+
expiry,
|
|
4641
|
+
targetTime = +new Date(),
|
|
4642
|
+
) {
|
|
4643
|
+
// Always expired if < 0
|
|
4644
|
+
if (initialTime === null || expiry === undefined || expiry < 0) {
|
|
4645
|
+
return true;
|
|
4646
|
+
}
|
|
4647
|
+
|
|
4648
|
+
// Never expires if == 0
|
|
4649
|
+
if (expiry === 0) {
|
|
4650
|
+
return false;
|
|
4651
|
+
}
|
|
4652
|
+
|
|
4653
|
+
return initialTime + expiry <= targetTime;
|
|
4654
|
+
}
|
|
4655
|
+
|
|
4656
|
+
/**
|
|
4657
|
+
* Checks to see if session is expired
|
|
4658
|
+
*/
|
|
4659
|
+
function isSessionExpired(session, timeouts, targetTime = +new Date()) {
|
|
4660
|
+
return (
|
|
4661
|
+
// First, check that maximum session length has not been exceeded
|
|
4662
|
+
isExpired(session.started, timeouts.maxSessionLife, targetTime) ||
|
|
4663
|
+
// check that the idle timeout has not been exceeded (i.e. user has
|
|
4664
|
+
// performed an action within the last `sessionIdleExpire` ms)
|
|
4665
|
+
isExpired(session.lastActivity, timeouts.sessionIdleExpire, targetTime)
|
|
4666
|
+
);
|
|
4667
|
+
}
|
|
4668
|
+
|
|
4669
|
+
/**
|
|
4670
|
+
* Check a session, and either return it or a refreshed version of it.
|
|
4671
|
+
* The refreshed version may be unsampled.
|
|
4672
|
+
* You can check if the session has changed by comparing the session IDs.
|
|
4673
|
+
*/
|
|
4674
|
+
function maybeRefreshSession(
|
|
4675
|
+
session,
|
|
4676
|
+
{
|
|
4677
|
+
timeouts,
|
|
4678
|
+
traceInternals,
|
|
4679
|
+
}
|
|
4680
|
+
|
|
4681
|
+
,
|
|
4682
|
+
sessionOptions,
|
|
4683
|
+
) {
|
|
4684
|
+
// If not expired, all good, just keep the session
|
|
4685
|
+
if (!isSessionExpired(session, timeouts)) {
|
|
4686
|
+
return session;
|
|
4687
|
+
}
|
|
4688
|
+
|
|
4689
|
+
const isBuffering = session.sampled === 'buffer';
|
|
4690
|
+
|
|
4691
|
+
// If we are buffering & the session may be refreshed, just return it
|
|
4692
|
+
if (isBuffering && session.shouldRefresh) {
|
|
4693
|
+
return session;
|
|
4694
|
+
}
|
|
4695
|
+
|
|
4696
|
+
// If we are buffering & the session may not be refreshed (=it was converted to session previously already)
|
|
4697
|
+
// We return an unsampled new session
|
|
4698
|
+
if (isBuffering) {
|
|
4699
|
+
logInfoNextTick('[Replay] Session should not be refreshed', traceInternals);
|
|
4700
|
+
return makeSession({ sampled: false });
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4703
|
+
// Else, we are not buffering, and the session is expired, so we need to create a new one
|
|
4704
|
+
logInfoNextTick('[Replay] Session has expired, creating new one...', traceInternals);
|
|
4705
|
+
|
|
4706
|
+
const newSession = createSession(sessionOptions, { previousSessionId: session.id });
|
|
4707
|
+
|
|
4708
|
+
return newSession;
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
/**
|
|
4712
|
+
* Get or create a session, when initializing the replay.
|
|
4713
|
+
* Returns a session that may be unsampled.
|
|
4665
4714
|
*/
|
|
4666
|
-
function
|
|
4667
|
-
timeouts,
|
|
4715
|
+
function loadOrCreateSession(
|
|
4668
4716
|
currentSession,
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
traceInternals,
|
|
4673
|
-
}) {
|
|
4674
|
-
// If session exists and is passed, use it instead of always hitting session storage
|
|
4675
|
-
const session = currentSession || (stickySession && fetchSession(traceInternals));
|
|
4676
|
-
|
|
4677
|
-
if (session) {
|
|
4678
|
-
// If there is a session, check if it is valid (e.g. "last activity" time
|
|
4679
|
-
// should be within the "session idle time", and "session started" time is
|
|
4680
|
-
// within "max session time").
|
|
4681
|
-
const isExpired = isSessionExpired(session, timeouts);
|
|
4682
|
-
|
|
4683
|
-
if (!isExpired || (allowBuffering && session.shouldRefresh)) {
|
|
4684
|
-
return { type: 'saved', session };
|
|
4685
|
-
} else if (!session.shouldRefresh) {
|
|
4686
|
-
// This is the case if we have an error session that is completed
|
|
4687
|
-
// (=triggered an error). Session will continue as session-based replay,
|
|
4688
|
-
// and when this session is expired, it will not be renewed until user
|
|
4689
|
-
// reloads.
|
|
4690
|
-
const discardedSession = makeSession({ sampled: false });
|
|
4691
|
-
logInfoNextTick('[Replay] Session should not be refreshed', traceInternals);
|
|
4692
|
-
return { type: 'new', session: discardedSession };
|
|
4693
|
-
} else {
|
|
4694
|
-
logInfoNextTick('[Replay] Session has expired', traceInternals);
|
|
4695
|
-
}
|
|
4696
|
-
// Otherwise continue to create a new session
|
|
4717
|
+
{
|
|
4718
|
+
timeouts,
|
|
4719
|
+
traceInternals,
|
|
4697
4720
|
}
|
|
4698
4721
|
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4722
|
+
,
|
|
4723
|
+
sessionOptions,
|
|
4724
|
+
) {
|
|
4725
|
+
// If session exists and is passed, use it instead of always hitting session storage
|
|
4726
|
+
const existingSession = currentSession || (sessionOptions.stickySession && fetchSession(traceInternals));
|
|
4727
|
+
|
|
4728
|
+
// No session exists yet, just create a new one
|
|
4729
|
+
if (!existingSession) {
|
|
4730
|
+
logInfoNextTick('[Replay] Created new session', traceInternals);
|
|
4731
|
+
return createSession(sessionOptions);
|
|
4732
|
+
}
|
|
4705
4733
|
|
|
4706
|
-
return {
|
|
4734
|
+
return maybeRefreshSession(existingSession, { timeouts, traceInternals }, sessionOptions);
|
|
4707
4735
|
}
|
|
4708
4736
|
|
|
4709
4737
|
function isCustomEvent(event) {
|
|
@@ -7308,13 +7336,7 @@ class ReplayContainer {
|
|
|
7308
7336
|
|
|
7309
7337
|
// Otherwise if there is _any_ sample rate set, try to load an existing
|
|
7310
7338
|
// session, or create a new one.
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
if (!isSessionSampled) {
|
|
7314
|
-
// This should only occur if `errorSampleRate` is 0 and was unsampled for
|
|
7315
|
-
// session-based replay. In this case there is nothing to do.
|
|
7316
|
-
return;
|
|
7317
|
-
}
|
|
7339
|
+
this._initializeSessionForSampling();
|
|
7318
7340
|
|
|
7319
7341
|
if (!this.session) {
|
|
7320
7342
|
// This should not happen, something wrong has occurred
|
|
@@ -7322,14 +7344,16 @@ class ReplayContainer {
|
|
|
7322
7344
|
return;
|
|
7323
7345
|
}
|
|
7324
7346
|
|
|
7325
|
-
if (this.session.sampled
|
|
7326
|
-
//
|
|
7327
|
-
//
|
|
7328
|
-
|
|
7329
|
-
// prior SDK version.
|
|
7330
|
-
this.recordingMode = 'buffer';
|
|
7347
|
+
if (this.session.sampled === false) {
|
|
7348
|
+
// This should only occur if `errorSampleRate` is 0 and was unsampled for
|
|
7349
|
+
// session-based replay. In this case there is nothing to do.
|
|
7350
|
+
return;
|
|
7331
7351
|
}
|
|
7332
7352
|
|
|
7353
|
+
// If segmentId > 0, it means we've previously already captured this session
|
|
7354
|
+
// In this case, we still want to continue in `session` recording mode
|
|
7355
|
+
this.recordingMode = this.session.sampled === 'buffer' && this.session.segmentId === 0 ? 'buffer' : 'session';
|
|
7356
|
+
|
|
7333
7357
|
logInfoNextTick(
|
|
7334
7358
|
`[Replay] Starting replay in ${this.recordingMode} mode`,
|
|
7335
7359
|
this._options._experiments.traceInternals,
|
|
@@ -7356,19 +7380,20 @@ class ReplayContainer {
|
|
|
7356
7380
|
|
|
7357
7381
|
logInfoNextTick('[Replay] Starting replay in session mode', this._options._experiments.traceInternals);
|
|
7358
7382
|
|
|
7359
|
-
const
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7383
|
+
const session = loadOrCreateSession(
|
|
7384
|
+
this.session,
|
|
7385
|
+
{
|
|
7386
|
+
timeouts: this.timeouts,
|
|
7387
|
+
traceInternals: this._options._experiments.traceInternals,
|
|
7388
|
+
},
|
|
7389
|
+
{
|
|
7390
|
+
stickySession: this._options.stickySession,
|
|
7391
|
+
// This is intentional: create a new session-based replay when calling `start()`
|
|
7392
|
+
sessionSampleRate: 1,
|
|
7393
|
+
allowBuffering: false,
|
|
7394
|
+
},
|
|
7395
|
+
);
|
|
7370
7396
|
|
|
7371
|
-
session.previousSessionId = previousSessionId;
|
|
7372
7397
|
this.session = session;
|
|
7373
7398
|
|
|
7374
7399
|
this._initializeRecording();
|
|
@@ -7385,18 +7410,19 @@ class ReplayContainer {
|
|
|
7385
7410
|
|
|
7386
7411
|
logInfoNextTick('[Replay] Starting replay in buffer mode', this._options._experiments.traceInternals);
|
|
7387
7412
|
|
|
7388
|
-
const
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7413
|
+
const session = loadOrCreateSession(
|
|
7414
|
+
this.session,
|
|
7415
|
+
{
|
|
7416
|
+
timeouts: this.timeouts,
|
|
7417
|
+
traceInternals: this._options._experiments.traceInternals,
|
|
7418
|
+
},
|
|
7419
|
+
{
|
|
7420
|
+
stickySession: this._options.stickySession,
|
|
7421
|
+
sessionSampleRate: 0,
|
|
7422
|
+
allowBuffering: true,
|
|
7423
|
+
},
|
|
7424
|
+
);
|
|
7398
7425
|
|
|
7399
|
-
session.previousSessionId = previousSessionId;
|
|
7400
7426
|
this.session = session;
|
|
7401
7427
|
|
|
7402
7428
|
this.recordingMode = 'buffer';
|
|
@@ -7507,7 +7533,7 @@ class ReplayContainer {
|
|
|
7507
7533
|
* new DOM checkout.`
|
|
7508
7534
|
*/
|
|
7509
7535
|
resume() {
|
|
7510
|
-
if (!this._isPaused || !this.
|
|
7536
|
+
if (!this._isPaused || !this._checkSession()) {
|
|
7511
7537
|
return;
|
|
7512
7538
|
}
|
|
7513
7539
|
|
|
@@ -7615,7 +7641,7 @@ class ReplayContainer {
|
|
|
7615
7641
|
if (!this._stopRecording) {
|
|
7616
7642
|
// Create a new session, otherwise when the user action is flushed, it
|
|
7617
7643
|
// will get rejected due to an expired session.
|
|
7618
|
-
if (!this.
|
|
7644
|
+
if (!this._checkSession()) {
|
|
7619
7645
|
return;
|
|
7620
7646
|
}
|
|
7621
7647
|
|
|
@@ -7714,7 +7740,7 @@ class ReplayContainer {
|
|
|
7714
7740
|
|
|
7715
7741
|
// --- There is recent user activity --- //
|
|
7716
7742
|
// This will create a new session if expired, based on expiry length
|
|
7717
|
-
if (!this.
|
|
7743
|
+
if (!this._checkSession()) {
|
|
7718
7744
|
return;
|
|
7719
7745
|
}
|
|
7720
7746
|
|
|
@@ -7831,31 +7857,63 @@ class ReplayContainer {
|
|
|
7831
7857
|
|
|
7832
7858
|
/**
|
|
7833
7859
|
* Loads (or refreshes) the current session.
|
|
7860
|
+
*/
|
|
7861
|
+
_initializeSessionForSampling() {
|
|
7862
|
+
// Whenever there is _any_ error sample rate, we always allow buffering
|
|
7863
|
+
// Because we decide on sampling when an error occurs, we need to buffer at all times if sampling for errors
|
|
7864
|
+
const allowBuffering = this._options.errorSampleRate > 0;
|
|
7865
|
+
|
|
7866
|
+
const session = loadOrCreateSession(
|
|
7867
|
+
this.session,
|
|
7868
|
+
{
|
|
7869
|
+
timeouts: this.timeouts,
|
|
7870
|
+
traceInternals: this._options._experiments.traceInternals,
|
|
7871
|
+
},
|
|
7872
|
+
{
|
|
7873
|
+
stickySession: this._options.stickySession,
|
|
7874
|
+
sessionSampleRate: this._options.sessionSampleRate,
|
|
7875
|
+
allowBuffering,
|
|
7876
|
+
},
|
|
7877
|
+
);
|
|
7878
|
+
|
|
7879
|
+
this.session = session;
|
|
7880
|
+
}
|
|
7881
|
+
|
|
7882
|
+
/**
|
|
7883
|
+
* Checks and potentially refreshes the current session.
|
|
7834
7884
|
* Returns false if session is not recorded.
|
|
7835
7885
|
*/
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7886
|
+
_checkSession() {
|
|
7887
|
+
// If there is no session yet, we do not want to refresh anything
|
|
7888
|
+
// This should generally not happen, but to be safe....
|
|
7889
|
+
if (!this.session) {
|
|
7890
|
+
return false;
|
|
7891
|
+
}
|
|
7892
|
+
|
|
7893
|
+
const currentSession = this.session;
|
|
7894
|
+
|
|
7895
|
+
const newSession = maybeRefreshSession(
|
|
7896
|
+
currentSession,
|
|
7897
|
+
{
|
|
7898
|
+
timeouts: this.timeouts,
|
|
7899
|
+
traceInternals: this._options._experiments.traceInternals,
|
|
7900
|
+
},
|
|
7901
|
+
{
|
|
7902
|
+
stickySession: Boolean(this._options.stickySession),
|
|
7903
|
+
sessionSampleRate: this._options.sessionSampleRate,
|
|
7904
|
+
allowBuffering: this._options.errorSampleRate > 0,
|
|
7905
|
+
},
|
|
7906
|
+
);
|
|
7907
|
+
|
|
7908
|
+
const isNew = newSession.id !== currentSession.id;
|
|
7845
7909
|
|
|
7846
7910
|
// If session was newly created (i.e. was not loaded from storage), then
|
|
7847
7911
|
// enable flag to create the root replay
|
|
7848
|
-
if (
|
|
7912
|
+
if (isNew) {
|
|
7849
7913
|
this.setInitialState();
|
|
7914
|
+
this.session = newSession;
|
|
7850
7915
|
}
|
|
7851
7916
|
|
|
7852
|
-
const currentSessionId = this.getSessionId();
|
|
7853
|
-
if (session.id !== currentSessionId) {
|
|
7854
|
-
session.previousSessionId = currentSessionId;
|
|
7855
|
-
}
|
|
7856
|
-
|
|
7857
|
-
this.session = session;
|
|
7858
|
-
|
|
7859
7917
|
if (!this.session.sampled) {
|
|
7860
7918
|
void this.stop({ reason: 'session not refreshed' });
|
|
7861
7919
|
return false;
|