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