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