@devskin/browser-sdk 1.0.34 → 1.0.36

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.
@@ -13423,15 +13423,20 @@ record.takeFullSnapshot = (isCheckout) => {
13423
13423
  record.mirror = mirror;
13424
13424
 
13425
13425
  class RRWebRecorder {
13426
- constructor(sessionId, config, onEventsReady) {
13426
+ constructor(sessionId, config, onEventsReady, sessionStartTime = 0) {
13427
13427
  this.stopFn = null;
13428
13428
  this.events = [];
13429
13429
  this.onEventsReady = null;
13430
13430
  this.flushInterval = null;
13431
13431
  this.hasFullSnapshot = false;
13432
+ this.sessionStartTime = 0; // Session start time (for relative timestamps)
13433
+ this.recordingStartTime = 0; // When this recorder instance started
13432
13434
  this.sessionId = sessionId;
13433
13435
  this.config = config;
13434
13436
  this.onEventsReady = onEventsReady;
13437
+ this.sessionStartTime = sessionStartTime || Date.now();
13438
+ this.recordingStartTime = Date.now();
13439
+ console.log(`[RRWeb] Recording initialized - session started at ${this.sessionStartTime}, recording started at ${this.recordingStartTime}`);
13435
13440
  }
13436
13441
  start() {
13437
13442
  if (!this.config.enabled) {
@@ -13445,6 +13450,13 @@ class RRWebRecorder {
13445
13450
  try {
13446
13451
  this.stopFn = record({
13447
13452
  emit: (event) => {
13453
+ // Convert absolute timestamps to relative to session start
13454
+ // This ensures continuity across page navigations within same session
13455
+ const originalTimestamp = event.timestamp;
13456
+ event.timestamp = event.timestamp - this.sessionStartTime;
13457
+ if (this.config.enabled && event.type === 2) {
13458
+ console.log(`[RRWeb] FullSnapshot - original: ${originalTimestamp}, relative: ${event.timestamp}, session start: ${this.sessionStartTime}`);
13459
+ }
13448
13460
  this.events.push(event);
13449
13461
  // Check if this is a FullSnapshot (type 2)
13450
13462
  if (event.type === 2) {
@@ -13930,6 +13942,7 @@ class DevSkinSDK {
13930
13942
  // Initialize session recording with rrweb
13931
13943
  if ((_a = this.config.sessionRecording) === null || _a === void 0 ? void 0 : _a.enabled) {
13932
13944
  // Use RRWebRecorder for complete DOM recording
13945
+ // Pass sessionStartTime to ensure timestamp continuity across page navigations
13933
13946
  this.rrwebRecorder = new RRWebRecorder(this.sessionId, {
13934
13947
  enabled: true,
13935
13948
  sampleRate: this.config.sessionRecording.sampling || 0.5,
@@ -13952,7 +13965,8 @@ class DevSkinSDK {
13952
13965
  var _a;
13953
13966
  // Send rrweb events to backend
13954
13967
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendRecordingEvents(this.sessionId, events);
13955
- });
13968
+ }, this.sessionStartTime // Pass session start time for timestamp continuity
13969
+ );
13956
13970
  // Start recording immediately (session already created)
13957
13971
  this.rrwebRecorder.start();
13958
13972
  if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
@@ -14178,6 +14192,7 @@ class DevSkinSDK {
14178
14192
  }
14179
14193
  setupUnloadTracking() {
14180
14194
  // CRITICAL: Flush data BEFORE page unloads to avoid losing final events
14195
+ // IMPORTANT: NEVER clear sessionStorage - it expires naturally when tab closes
14181
14196
  // 1. visibilitychange - fires when tab is hidden (most reliable)
14182
14197
  document.addEventListener('visibilitychange', () => {
14183
14198
  var _a, _b;
@@ -14189,30 +14204,23 @@ class DevSkinSDK {
14189
14204
  }
14190
14205
  });
14191
14206
  // 2. pagehide - fires when page is being unloaded
14192
- window.addEventListener('pagehide', (event) => {
14207
+ window.addEventListener('pagehide', () => {
14193
14208
  var _a, _b;
14194
- const isActualClose = !event.persisted;
14195
- if (isActualClose) {
14196
- // Tab is closing - end session
14197
- this.track('page_unload');
14198
- this.updateSessionDuration(true); // true = session ending
14199
- // Clear session storage since session is ending
14200
- sessionStorage.removeItem('devskin_session_id');
14201
- sessionStorage.removeItem('devskin_session_start');
14202
- }
14203
- else {
14204
- // Navigation - track page change
14205
- this.track('page_navigation');
14206
- this.updateSessionDuration();
14207
- }
14208
- // ALWAYS flush (whether navigation or close)
14209
+ // Track navigation (we can't distinguish between page navigation and tab close reliably)
14210
+ this.track('page_navigation');
14211
+ // Update duration but DON'T mark as ending (let heartbeat timeout handle session expiry)
14212
+ this.updateSessionDuration(false);
14213
+ // NEVER clear sessionStorage - it persists across navigations in same tab
14214
+ // and expires automatically when tab actually closes
14215
+ // Flush data before page unloads
14209
14216
  (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop(); // Stop recording and flush remaining events
14210
14217
  (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon for reliability
14211
14218
  });
14212
14219
  // 3. beforeunload - backup for older browsers
14213
14220
  window.addEventListener('beforeunload', () => {
14214
14221
  var _a, _b;
14215
- this.updateSessionDuration(true);
14222
+ // Update duration but DON'T mark as ending
14223
+ this.updateSessionDuration(false);
14216
14224
  (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop();
14217
14225
  (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true);
14218
14226
  });