@oneuptime/react-native-replay 14.0.11 → 14.0.12

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/README.md CHANGED
@@ -196,8 +196,8 @@ Pass `debug: true` at startup to mirror those codes to the development console.
196
196
 
197
197
  Use the current consented session ID to correlate the application's own traces
198
198
  and logs with replay. The listener fires immediately when a session exists,
199
- again on every rotation, and with `null` when capture stops or consent is
200
- withdrawn:
199
+ again on every rotation, and with `null` when capture stops, consent is
200
+ withdrawn, or the session ends idle:
201
201
 
202
202
  ```ts
203
203
  const unsubscribe = OneUptimeReplay.onSessionChange((sessionId) => {
@@ -221,6 +221,12 @@ server response. AppState backgrounding records visibility, closes the current
221
221
  chunk, drains the outbox only when policy and consent permit uploading, and
222
222
  pauses sampling; foregrounding resumes with a fresh full snapshot.
223
223
 
224
+ Thirty minutes with no touch and no SDK call ends the session at that last
225
+ activity, so time in the background is never added to the end of a session.
226
+ An app left open in the foreground stops sampling then rather than starting a
227
+ session every half hour. The user's next touch, a return to the foreground,
228
+ or `captureSession()` starts a new session on a fresh full snapshot.
229
+
224
230
  Policy is revalidated without HTTP-cache reuse before every foreground resume,
225
231
  after identity changes, and at five-minute intervals while the app remains
226
232
  active. An explicit disable takes effect before more capture; a transient
package/dist/index.cjs CHANGED
@@ -98,7 +98,7 @@ var SESSION_REPLAY_MAX_OFFLINE_DELAY_MS = 3 * 24 * 60 * 60 * 1e3;
98
98
  var MOBILE_CAPTURE_INTERVAL_MS = 500;
99
99
  var MOBILE_RECORDER_KIND = "rn-view-tree";
100
100
  var SYNTHETIC_RRWEB_VERSION = "synthetic-1";
101
- var RECORDER_VERSION = true ? "14.0.11" : "0.0.0-dev";
101
+ var RECORDER_VERSION = true ? "14.0.12" : "0.0.0-dev";
102
102
  var SESSION_REPLAY_MOBILE_RECORDER_CAPABILITIES = [
103
103
  "mobile-view-tree",
104
104
  "mobile-touch-events",
@@ -150,6 +150,7 @@ var IDENTIFY_CUSTOM_EVENT_TAG = "oneuptime.identify";
150
150
  var TAGS_CUSTOM_EVENT_TAG = "oneuptime.tags";
151
151
  var VISIBILITY_CUSTOM_EVENT_TAG = "oneuptime.visibility";
152
152
  var TOUCH_CUSTOM_EVENT_TAG = "oneuptime.touch";
153
+ var SESSION_ROTATED_CUSTOM_EVENT_TAG = "oneuptime.session-rotated";
153
154
 
154
155
  // src/Sanitize.ts
155
156
  var MAX_TRAIT_KEYS = 20;
@@ -3120,6 +3121,19 @@ var MobileReplayRecorder = class {
3120
3121
  __publicField(this, "pendingStartCancellation", null);
3121
3122
  __publicField(this, "identityPersisted", false);
3122
3123
  __publicField(this, "sessionLastActivityUnixMs", 0);
3124
+ /*
3125
+ * The session went the idle window without activity, was sealed where it
3126
+ * ended, and nobody has come back since.
3127
+ *
3128
+ * Rotating on the spot instead (the old behaviour) started a session
3129
+ * nobody was in: an app left open in the foreground with the screen on
3130
+ * re-snapshots every minute, so it filed a new session every half hour
3131
+ * for as long as it stayed open. Now nothing is captured, uploaded or
3132
+ * handed to the host as a session id until the user is back - a touch,
3133
+ * the app returning to the foreground, or an explicit captureSession() -
3134
+ * and that starts the next session (see maybeRotateSession).
3135
+ */
3136
+ __publicField(this, "sealedForIdle", false);
3123
3137
  __publicField(this, "rotatingSession", false);
3124
3138
  __publicField(this, "rotationPromise", null);
3125
3139
  __publicField(this, "appStateQueue", Promise.resolve());
@@ -3207,6 +3221,7 @@ var MobileReplayRecorder = class {
3207
3221
  this.appStateQueue = Promise.resolve();
3208
3222
  this.rotationPromise = null;
3209
3223
  this.rotatingSession = false;
3224
+ this.sealedForIdle = false;
3210
3225
  this.policyRefreshPromise = null;
3211
3226
  this.lastPolicyRefreshAtUnixMs = 0;
3212
3227
  this.forceFullSnapshot = true;
@@ -3379,12 +3394,14 @@ var MobileReplayRecorder = class {
3379
3394
  this.diagnosticsStatus = "stopped";
3380
3395
  return;
3381
3396
  }
3382
- this.appendCustom(
3383
- VISIBILITY_CUSTOM_EVENT_TAG,
3384
- { state: "stopped" },
3385
- void 0,
3386
- true
3387
- );
3397
+ if (this.rotationReason(this.now()) !== "idle") {
3398
+ this.appendCustom(
3399
+ VISIBILITY_CUSTOM_EVENT_TAG,
3400
+ { state: "stopped" },
3401
+ void 0,
3402
+ true
3403
+ );
3404
+ }
3388
3405
  this.haltCapture();
3389
3406
  await this.closeCurrent(true);
3390
3407
  if (this.uploadActive) {
@@ -3547,6 +3564,9 @@ var MobileReplayRecorder = class {
3547
3564
  return;
3548
3565
  }
3549
3566
  this.route = next;
3567
+ if (this.sealedForIdle) {
3568
+ return;
3569
+ }
3550
3570
  this.appendCustom(
3551
3571
  ROUTE_CUSTOM_EVENT_TAG,
3552
3572
  {
@@ -3565,7 +3585,7 @@ var MobileReplayRecorder = class {
3565
3585
  if (!this.running || !this.acceptingEvents) {
3566
3586
  return;
3567
3587
  }
3568
- await this.maybeRotateSession(this.now());
3588
+ await this.maybeRotateForUser(this.now());
3569
3589
  const safeReason2 = truncate(
3570
3590
  (typeof reason === "string" ? reason.trim() : "") || "manual",
3571
3591
  MAX_CAPTURE_REASON_LENGTH
@@ -3582,6 +3602,9 @@ var MobileReplayRecorder = class {
3582
3602
  return;
3583
3603
  }
3584
3604
  await this.maybeRotateSession(this.now());
3605
+ if (this.sealedForIdle) {
3606
+ return;
3607
+ }
3585
3608
  const rawName = error instanceof Error && typeof error.name === "string" ? error.name.trim() : "";
3586
3609
  const name = SAFE_ERROR_NAMES.has(rawName) ? rawName : "Error";
3587
3610
  this.appendCustom(
@@ -3632,9 +3655,9 @@ var MobileReplayRecorder = class {
3632
3655
  this.lastTouchMoveAtUnixMs = touch.timestamp;
3633
3656
  }
3634
3657
  const nowUnixMs = this.now();
3635
- if (this.rotatingSession || this.rotationReason(nowUnixMs)) {
3658
+ if (this.rotatingSession || this.sealedForIdle || this.rotationReason(nowUnixMs)) {
3636
3659
  this.diagnostic("touch-dropped-session-rotation");
3637
- void this.maybeRotateSession(nowUnixMs);
3660
+ void this.maybeRotateForUser(nowUnixMs);
3638
3661
  return;
3639
3662
  }
3640
3663
  this.appendEvent(
@@ -3682,9 +3705,14 @@ var MobileReplayRecorder = class {
3682
3705
  )
3683
3706
  };
3684
3707
  }
3685
- /** Current consented replay session for host trace/log correlation. */
3708
+ /**
3709
+ * Current consented replay session for host trace/log correlation. None
3710
+ * while the session has ended idle and nobody is back: a background poll
3711
+ * on an idle app is no part of a recording that ended at the user's last
3712
+ * activity.
3713
+ */
3686
3714
  getSessionId() {
3687
- return this.running && this.identityPersisted ? this.identity?.sessionId ?? null : null;
3715
+ return this.running && this.identityPersisted && !this.sealedForIdle ? this.identity?.sessionId ?? null : null;
3688
3716
  }
3689
3717
  /** Fires immediately when a session exists and again on rotate/clear. */
3690
3718
  onSessionChange(listener) {
@@ -3739,7 +3767,7 @@ var MobileReplayRecorder = class {
3739
3767
  }
3740
3768
  async captureTick(force = false) {
3741
3769
  const generation = this.lifecycleGeneration;
3742
- if (!this.running || !this.acceptingEvents || !this.foreground || !this.rootTag || this.captureGeneration === generation || !this.options) {
3770
+ if (!this.running || !this.acceptingEvents || !this.foreground || !this.rootTag || this.sealedForIdle || this.captureGeneration === generation || !this.options) {
3743
3771
  return;
3744
3772
  }
3745
3773
  this.captureGeneration = generation;
@@ -3754,7 +3782,7 @@ var MobileReplayRecorder = class {
3754
3782
  return;
3755
3783
  }
3756
3784
  await this.maybeRotateSession(nowUnixMs);
3757
- if (!this.running || !this.acceptingEvents || !this.foreground || !this.isCurrentGeneration(generation)) {
3785
+ if (!this.running || !this.acceptingEvents || !this.foreground || !this.isCurrentGeneration(generation) || this.sealedForIdle) {
3758
3786
  return;
3759
3787
  }
3760
3788
  const capturedSessionId = this.identity?.sessionId;
@@ -3763,7 +3791,6 @@ var MobileReplayRecorder = class {
3763
3791
  return;
3764
3792
  }
3765
3793
  this.touchPrivacyMap = deriveTouchPrivacyMap(tree);
3766
- const size = getViewport();
3767
3794
  const shouldForce = force || this.forceFullSnapshot || nowUnixMs - this.lastFullSnapshotAtUnixMs >= SESSION_REPLAY_CHECKOUT_INTERVAL_MS;
3768
3795
  if (shouldForce && !this.chunkBuffer.isEmpty()) {
3769
3796
  await this.closeCurrent(false);
@@ -3771,22 +3798,7 @@ var MobileReplayRecorder = class {
3771
3798
  if (!this.running || !this.acceptingEvents || !this.foreground || !this.isCurrentGeneration(generation) || this.identity?.sessionId !== capturedSessionId) {
3772
3799
  return;
3773
3800
  }
3774
- const capture = this.serializer.capture(tree, {
3775
- timestamp: nowUnixMs,
3776
- url: toAppUrl(this.options.mobileAppIdentifier, this.route),
3777
- viewportWidth: size.width,
3778
- viewportHeight: size.height,
3779
- forceFullSnapshot: shouldForce
3780
- });
3781
- this.droppedEvents += capture.droppedNodes;
3782
- this.chunkBuffer.appendMany(capture.events, {
3783
- route: toAppUrl(this.options.mobileAppIdentifier, this.route),
3784
- fidelityNotices: capture.fidelityNotices
3785
- });
3786
- if (capture.hasFullSnapshot) {
3787
- this.lastFullSnapshotAtUnixMs = nowUnixMs;
3788
- this.forceFullSnapshot = false;
3789
- }
3801
+ this.appendCapture(tree, this.options, nowUnixMs, shouldForce);
3790
3802
  if (this.chunkBuffer.shouldFlush()) {
3791
3803
  await this.closeCurrent(false);
3792
3804
  }
@@ -3803,6 +3815,26 @@ var MobileReplayRecorder = class {
3803
3815
  }
3804
3816
  }
3805
3817
  }
3818
+ appendCapture(tree, options, nowUnixMs, forceFullSnapshot) {
3819
+ const size = getViewport();
3820
+ const url = toAppUrl(options.mobileAppIdentifier, this.route);
3821
+ const capture = this.serializer.capture(tree, {
3822
+ timestamp: nowUnixMs,
3823
+ url,
3824
+ viewportWidth: size.width,
3825
+ viewportHeight: size.height,
3826
+ forceFullSnapshot
3827
+ });
3828
+ this.droppedEvents += capture.droppedNodes;
3829
+ this.chunkBuffer.appendMany(capture.events, {
3830
+ route: url,
3831
+ fidelityNotices: capture.fidelityNotices
3832
+ });
3833
+ if (capture.hasFullSnapshot) {
3834
+ this.lastFullSnapshotAtUnixMs = nowUnixMs;
3835
+ this.forceFullSnapshot = false;
3836
+ }
3837
+ }
3806
3838
  appendCustom(tag, payload, signal, allowWhenQuiesced = false) {
3807
3839
  this.appendEvent(
3808
3840
  createCustomEvent(tag, payload, this.now()),
@@ -3814,6 +3846,9 @@ var MobileReplayRecorder = class {
3814
3846
  if (!this.running || !this.acceptingEvents && !allowWhenQuiesced) {
3815
3847
  return;
3816
3848
  }
3849
+ if (this.sealedForIdle) {
3850
+ return;
3851
+ }
3817
3852
  if (!Number.isFinite(event.timestamp)) {
3818
3853
  this.droppedEvents += 1;
3819
3854
  this.diagnostic("invalid-event-timestamp");
@@ -3842,7 +3877,7 @@ var MobileReplayRecorder = class {
3842
3877
  }
3843
3878
  }
3844
3879
  async closeCurrent(isFinal) {
3845
- const closesFinalChunkSlot = !isFinal && !this.rotatingSession && this.uploadActive && this.sessionStore?.getNextChunkIndex() === MAX_SESSION_REPLAY_CHUNKS_PER_SESSION - 1;
3880
+ const closesFinalChunkSlot = !isFinal && !this.rotatingSession && !this.sealedForIdle && this.uploadActive && this.sessionStore?.getNextChunkIndex() === MAX_SESSION_REPLAY_CHUNKS_PER_SESSION - 1;
3846
3881
  if (closesFinalChunkSlot) {
3847
3882
  this.rotatingSession = true;
3848
3883
  const rotationOptions = {};
@@ -3854,7 +3889,7 @@ var MobileReplayRecorder = class {
3854
3889
  }
3855
3890
  this.chunkBuffer.append(
3856
3891
  createCustomEvent(
3857
- "oneuptime.session-rotated",
3892
+ SESSION_ROTATED_CUSTOM_EVENT_TAG,
3858
3893
  { reason: "chunk-cap" },
3859
3894
  this.now()
3860
3895
  ),
@@ -4054,15 +4089,19 @@ var MobileReplayRecorder = class {
4054
4089
  if (!policyReady) {
4055
4090
  return;
4056
4091
  }
4057
- await this.maybeRotateSession(this.now());
4092
+ const sessionIdBeforeResume = this.identity?.sessionId;
4093
+ await this.maybeRotateSession(this.now(), true);
4058
4094
  if (!this.running || !this.isCurrentGeneration(generation)) {
4059
4095
  return;
4060
4096
  }
4061
4097
  this.appendCustom(VISIBILITY_CUSTOM_EVENT_TAG, { state: "visible" });
4062
- this.forceFullSnapshot = true;
4098
+ const openedOnSnapshot = this.identity?.sessionId !== sessionIdBeforeResume && this.lastFullSnapshotAtUnixMs > 0;
4099
+ if (!openedOnSnapshot) {
4100
+ this.forceFullSnapshot = true;
4101
+ }
4063
4102
  this.startTimers();
4064
4103
  this.diagnosticsStatus = this.consentState() === "Unknown" ? "consent-required" : "recording";
4065
- await this.captureTick(true);
4104
+ await this.captureTick(!openedOnSnapshot);
4066
4105
  if (this.uploadActive && this.running) {
4067
4106
  this.transport?.resume();
4068
4107
  }
@@ -4342,9 +4381,12 @@ var MobileReplayRecorder = class {
4342
4381
  if (!this.running || identityEpoch !== this.identityCancellationEpoch) {
4343
4382
  return;
4344
4383
  }
4384
+ if (!this.rotationPromise && !this.sealedForIdle && this.rotationReason(this.now()) === "idle") {
4385
+ await this.maybeRotateSession(this.now());
4386
+ }
4345
4387
  if (this.rotationPromise) {
4346
4388
  await this.rotationPromise;
4347
- } else {
4389
+ } else if (!this.sealedForIdle) {
4348
4390
  this.rotatingSession = true;
4349
4391
  const rotation = this.performSessionRotation(
4350
4392
  this.now(),
@@ -4679,16 +4721,24 @@ var MobileReplayRecorder = class {
4679
4721
  chunkIndex: 0
4680
4722
  };
4681
4723
  }
4724
+ /*
4725
+ * Idle before the duration cap, the order Common's SessionIdentity decides
4726
+ * them in. A session left for the idle window ended where it was left,
4727
+ * even when it has outlived the cap by the time anything notices, and
4728
+ * only an idle rotation dates its end there (see performSessionRotation):
4729
+ * called "duration", a session three hours old put in the background for
4730
+ * two more was sealed two hours after its footage.
4731
+ */
4682
4732
  rotationReason(nowUnixMs) {
4683
4733
  if (!this.identity) {
4684
4734
  return null;
4685
4735
  }
4686
- if (nowUnixMs - this.identity.sessionStartUnixMs >= SESSION_REPLAY_MAX_SESSION_MS) {
4687
- return "duration";
4688
- }
4689
4736
  if (nowUnixMs - this.sessionLastActivityUnixMs >= SESSION_REPLAY_IDLE_ROLLOVER_MS) {
4690
4737
  return "idle";
4691
4738
  }
4739
+ if (nowUnixMs - this.identity.sessionStartUnixMs >= SESSION_REPLAY_MAX_SESSION_MS) {
4740
+ return "duration";
4741
+ }
4692
4742
  return null;
4693
4743
  }
4694
4744
  runExternalActivity(nowUnixMs, action) {
@@ -4723,23 +4773,37 @@ var MobileReplayRecorder = class {
4723
4773
  }
4724
4774
  });
4725
4775
  }
4726
- async maybeRotateSession(nowUnixMs) {
4727
- if (this.rotationPromise) {
4776
+ /*
4777
+ * isUserBack: the caller knows the user is here - a touch, the app
4778
+ * returning to the foreground, an explicit captureSession(). An idle
4779
+ * session then rotates at once. Anything else (a capture tick, a route
4780
+ * change, a developer event) only finds out the session went idle, and
4781
+ * seals it to wait for them (see sealedForIdle).
4782
+ */
4783
+ async maybeRotateSession(nowUnixMs, isUserBack = false) {
4784
+ while (this.rotationPromise) {
4728
4785
  await this.rotationPromise;
4729
- return;
4786
+ if (!isUserBack || !this.sealedForIdle) {
4787
+ return;
4788
+ }
4730
4789
  }
4731
4790
  if (!this.running || this.rotatingSession || !this.identity || !this.config || !this.sessionStore || !this.options) {
4732
4791
  return;
4733
4792
  }
4734
- const reason = this.rotationReason(nowUnixMs);
4793
+ let reason;
4794
+ if (this.sealedForIdle) {
4795
+ if (!isUserBack) {
4796
+ return;
4797
+ }
4798
+ reason = "idle";
4799
+ } else {
4800
+ reason = this.rotationReason(nowUnixMs);
4801
+ }
4735
4802
  if (!reason) {
4736
4803
  return;
4737
4804
  }
4738
4805
  this.rotatingSession = true;
4739
- const operation = this.performSessionRotation(
4740
- nowUnixMs,
4741
- reason
4742
- );
4806
+ const operation = reason === "idle" && !isUserBack ? this.sealForIdle() : this.performSessionRotation(nowUnixMs, reason);
4743
4807
  this.rotationPromise = operation;
4744
4808
  try {
4745
4809
  await operation;
@@ -4750,8 +4814,60 @@ var MobileReplayRecorder = class {
4750
4814
  this.rotatingSession = false;
4751
4815
  }
4752
4816
  }
4817
+ /*
4818
+ * maybeRotateSession for a user who is here. On an app whose session
4819
+ * ended idle, the policy is brought up to date first: no capture tick has
4820
+ * refreshed it while the app sat idle, and the next session must not open
4821
+ * under one that has since changed (or turned replay off).
4822
+ */
4823
+ async maybeRotateForUser(nowUnixMs) {
4824
+ if (this.sealedForIdle && !this.rotatingSession) {
4825
+ const policyReady = await this.refreshRuntimePolicy(
4826
+ this.lifecycleGeneration,
4827
+ false,
4828
+ "periodic"
4829
+ );
4830
+ if (!policyReady) {
4831
+ return;
4832
+ }
4833
+ }
4834
+ await this.maybeRotateSession(nowUnixMs, true);
4835
+ }
4836
+ /*
4837
+ * End the session as idle, where it really ended, and wait for the user.
4838
+ *
4839
+ * Sealed with whatever footage is still buffered - dated by that footage,
4840
+ * never by now - and with no marker of its own: a marker stamped now
4841
+ * would date the session's end half an hour or more after anything the
4842
+ * player can show. With nothing buffered nothing is sent, and the server
4843
+ * ends the session at its last chunk.
4844
+ */
4845
+ async sealForIdle() {
4846
+ this.sealedForIdle = true;
4847
+ this.diagnostic("session-ended-idle", {
4848
+ idleForMs: Math.max(0, this.now() - this.sessionLastActivityUnixMs)
4849
+ });
4850
+ this.notifySessionChange();
4851
+ await this.closeCurrent(true);
4852
+ await this.closeQueue;
4853
+ if (!this.running) {
4854
+ return;
4855
+ }
4856
+ if (this.uploadActive) {
4857
+ await this.transport?.drain();
4858
+ } else {
4859
+ this.rollingBuffer.clear();
4860
+ }
4861
+ }
4753
4862
  async performSessionRotation(nowUnixMs, reason) {
4754
- this.appendCustom("oneuptime.session-rotated", { reason }, void 0, true);
4863
+ if (reason !== "idle") {
4864
+ this.appendCustom(
4865
+ SESSION_ROTATED_CUSTOM_EVENT_TAG,
4866
+ { reason },
4867
+ void 0,
4868
+ true
4869
+ );
4870
+ }
4755
4871
  await this.closeCurrent(true);
4756
4872
  await this.closeQueue;
4757
4873
  if (!this.running) {
@@ -4766,12 +4882,64 @@ var MobileReplayRecorder = class {
4766
4882
  return;
4767
4883
  }
4768
4884
  await this.completeSessionIdentityRotation(nowUnixMs, reason);
4885
+ if (reason === "idle" && this.running) {
4886
+ await this.openSessionAfterIdle();
4887
+ }
4888
+ }
4889
+ /*
4890
+ * The first things a session after an idle one records: a full snapshot
4891
+ * of the screen the user came back to, then the rotation marker - the
4892
+ * snapshot first, so chunk 0 is a seek anchor rather than a lone marker.
4893
+ *
4894
+ * Taken here, inside the rotation, rather than left to the next capture
4895
+ * tick: until the rotation settles, touches are dropped and every other
4896
+ * event waits for it (rotatingSession), so nothing can land ahead of the
4897
+ * snapshot. A user who has just come back is touching the screen, and
4898
+ * half a second of that was otherwise a chunk 0 with no screen in it.
4899
+ *
4900
+ * In the background or without a registered root there is no screen to
4901
+ * capture yet; the marker opens the session alone.
4902
+ */
4903
+ async openSessionAfterIdle() {
4904
+ const generation = this.lifecycleGeneration;
4905
+ const rootTag = this.rootTag;
4906
+ const options = this.options;
4907
+ const sessionId = this.identity?.sessionId;
4908
+ if (this.foreground && rootTag && options && sessionId) {
4909
+ const capturedAtUnixMs = this.now();
4910
+ let tree;
4911
+ try {
4912
+ tree = await this.nativeViewTree.captureViewTree(rootTag);
4913
+ } catch (error) {
4914
+ if (this.running && this.isCurrentGeneration(generation)) {
4915
+ this.disable("native-capture-failed", {
4916
+ name: error instanceof Error ? error.name : "Error"
4917
+ });
4918
+ this.haltCapture();
4919
+ }
4920
+ return;
4921
+ }
4922
+ if (!this.running || !this.isCurrentGeneration(generation) || this.identity?.sessionId !== sessionId) {
4923
+ return;
4924
+ }
4925
+ if (this.foreground) {
4926
+ this.touchPrivacyMap = deriveTouchPrivacyMap(tree);
4927
+ this.appendCapture(tree, options, capturedAtUnixMs, true);
4928
+ }
4929
+ }
4930
+ this.appendCustom(
4931
+ SESSION_ROTATED_CUSTOM_EVENT_TAG,
4932
+ { reason: "idle" },
4933
+ void 0,
4934
+ true
4935
+ );
4769
4936
  }
4770
4937
  async completeSessionIdentityRotation(nowUnixMs, reason) {
4771
4938
  if (!this.sessionStore || !this.config || !this.options) {
4772
4939
  return;
4773
4940
  }
4774
4941
  this.identity = this.identityPersisted ? await this.sessionStore.rotate(nowUnixMs) : this.ephemeralIdentity(nowUnixMs);
4942
+ this.sealedForIdle = false;
4775
4943
  this.notifySessionChange();
4776
4944
  this.sampled = this.identityPersisted ? deterministicSample(
4777
4945
  this.identity.sessionId,