@devskin/browser-sdk 1.0.25 → 1.0.27

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.
@@ -13484,8 +13484,8 @@ class RRWebRecorder {
13484
13484
  // Send immediately to ensure FullSnapshot reaches backend first
13485
13485
  this.flush();
13486
13486
  }
13487
- else if (this.hasFullSnapshot && this.events.length >= 50) {
13488
- // After FullSnapshot, batch other events
13487
+ else if (this.hasFullSnapshot && this.events.length >= 20) {
13488
+ // After FullSnapshot, batch other events (reduced from 50 to 20)
13489
13489
  this.flush();
13490
13490
  }
13491
13491
  },
@@ -13524,13 +13524,13 @@ class RRWebRecorder {
13524
13524
  // Capture iframe content
13525
13525
  recordCrossOriginIframes: false, // Security: don't record cross-origin iframes
13526
13526
  });
13527
- // Set up periodic flush (every 10 seconds)
13527
+ // Set up periodic flush (every 2 seconds)
13528
13528
  // Only flush if we have FullSnapshot to ensure first batch is complete
13529
13529
  this.flushInterval = window.setInterval(() => {
13530
13530
  if (this.hasFullSnapshot && this.events.length > 0) {
13531
13531
  this.flush();
13532
13532
  }
13533
- }, 10000);
13533
+ }, 2000); // Reduced from 10s to 2s
13534
13534
  // Safety check: After 2 seconds, force a full snapshot if none captured
13535
13535
  setTimeout(() => {
13536
13536
  if (!this.hasFullSnapshot) {
@@ -13588,8 +13588,8 @@ class Transport {
13588
13588
  this.config = config;
13589
13589
  this.queue = [];
13590
13590
  this.flushInterval = null;
13591
- this.maxQueueSize = 50;
13592
- this.flushIntervalMs = 5000; // 5 seconds
13591
+ this.maxQueueSize = 20; // Reduced from 50
13592
+ this.flushIntervalMs = 2000; // 2 seconds (reduced from 5s)
13593
13593
  this.apiUrl = config.apiUrl || 'https://api.devskin.com';
13594
13594
  // Start periodic flush
13595
13595
  this.startPeriodicFlush();
@@ -13614,8 +13614,8 @@ class Transport {
13614
13614
  this.sendToBackend('/v1/analytics/identify', user);
13615
13615
  }
13616
13616
  startSession(session) {
13617
- // Send session start immediately
13618
- this.sendToBackend('/v1/analytics/session', session);
13617
+ // Send session start immediately to RUM endpoint
13618
+ this.sendToBackend('/v1/rum/sessions', session);
13619
13619
  }
13620
13620
  sendError(error) {
13621
13621
  this.enqueue('error', error);
@@ -13626,6 +13626,12 @@ class Transport {
13626
13626
  sendPerformanceMetric(metric) {
13627
13627
  this.enqueue('performance', metric);
13628
13628
  }
13629
+ sendPageView(pageViewData) {
13630
+ return __awaiter$1(this, void 0, void 0, function* () {
13631
+ // Send page view immediately to RUM endpoint (don't queue)
13632
+ this.sendToBackend('/v1/rum/page-views', pageViewData);
13633
+ });
13634
+ }
13629
13635
  sendRecordingEvents(sessionId, events) {
13630
13636
  return __awaiter$1(this, void 0, void 0, function* () {
13631
13637
  // Calculate payload size
@@ -13726,17 +13732,17 @@ class Transport {
13726
13732
  getEndpointForType(type) {
13727
13733
  switch (type) {
13728
13734
  case 'event':
13729
- return '/v1/analytics/events';
13735
+ return '/v1/rum/events';
13730
13736
  case 'error':
13731
- return '/v1/analytics/errors';
13737
+ return '/v1/errors/errors';
13732
13738
  case 'network':
13733
- return '/v1/analytics/network';
13739
+ return '/v1/rum/network-requests';
13734
13740
  case 'performance':
13735
- return '/v1/analytics/performance';
13741
+ return '/v1/rum/web-vitals';
13736
13742
  case 'heatmap':
13737
13743
  return '/v1/sdk/heatmap';
13738
13744
  default:
13739
- return '/v1/analytics/events';
13745
+ return '/v1/rum/events';
13740
13746
  }
13741
13747
  }
13742
13748
  sendToBackendXHR(endpoint, data) {
@@ -13995,7 +14001,24 @@ class DevSkinSDK {
13995
14001
  * Track a page view
13996
14002
  */
13997
14003
  trackPageView(properties) {
14004
+ var _a, _b;
14005
+ if (!this.initialized) {
14006
+ console.warn('[DevSkin] SDK not initialized. Call init() first.');
14007
+ return;
14008
+ }
14009
+ // Generate unique page view ID
14010
+ const pageViewId = this.generateId();
14011
+ // Send to RUM page-views endpoint
14012
+ (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendPageView(Object.assign({ sessionId: this.sessionId, pageViewId: pageViewId, url: window.location.href, path: window.location.pathname, queryParams: window.location.search, referrer: document.referrer, title: document.title, timestamp: new Date().toISOString() }, properties));
14013
+ // Also track as analytics event for backwards compatibility
13998
14014
  this.track('page_view', Object.assign({ path: window.location.pathname, search: window.location.search, hash: window.location.hash, referrer: document.referrer }, properties));
14015
+ if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
14016
+ console.log('[DevSkin] Page view tracked:', {
14017
+ sessionId: this.sessionId,
14018
+ pageViewId: pageViewId,
14019
+ url: window.location.href,
14020
+ });
14021
+ }
13999
14022
  }
14000
14023
  /**
14001
14024
  * Identify a user
@@ -14064,11 +14087,32 @@ class DevSkinSDK {
14064
14087
  * Private methods
14065
14088
  */
14066
14089
  startSession() {
14067
- var _a;
14090
+ var _a, _b, _c;
14091
+ // Check if there's an active session (stored in sessionStorage to persist across page navigations)
14092
+ const existingSessionId = sessionStorage.getItem('devskin_session_id');
14093
+ const existingSessionStart = sessionStorage.getItem('devskin_session_start');
14094
+ if (existingSessionId && existingSessionStart) {
14095
+ // Resume existing session
14096
+ this.sessionId = existingSessionId;
14097
+ this.sessionStartTime = parseInt(existingSessionStart, 10);
14098
+ if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.debug) {
14099
+ console.log('[DevSkin] Resuming existing session:', this.sessionId);
14100
+ }
14101
+ // Send page view but DON'T create a new session
14102
+ // The session is already created, just continue it
14103
+ return;
14104
+ }
14105
+ // Create new session
14068
14106
  this.sessionId = this.generateId();
14069
14107
  this.sessionStartTime = Date.now();
14108
+ // Store in sessionStorage (persists across page navigations in same tab)
14109
+ sessionStorage.setItem('devskin_session_id', this.sessionId);
14110
+ sessionStorage.setItem('devskin_session_start', this.sessionStartTime.toString());
14070
14111
  const sessionData = Object.assign({ session_id: this.sessionId, user_id: this.userId || undefined, anonymous_id: this.anonymousId, started_at: new Date().toISOString() }, this.getContextData());
14071
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession(sessionData);
14112
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.startSession(sessionData);
14113
+ if ((_c = this.config) === null || _c === void 0 ? void 0 : _c.debug) {
14114
+ console.log('[DevSkin] New session created:', this.sessionId);
14115
+ }
14072
14116
  }
14073
14117
  getContextData() {
14074
14118
  const context = {};
@@ -14105,17 +14149,45 @@ class DevSkinSDK {
14105
14149
  });
14106
14150
  }
14107
14151
  setupUnloadTracking() {
14108
- window.addEventListener('beforeunload', () => {
14152
+ // CRITICAL: Flush data BEFORE page unloads to avoid losing final events
14153
+ // 1. visibilitychange - fires when tab is hidden (most reliable)
14154
+ document.addEventListener('visibilitychange', () => {
14109
14155
  var _a, _b;
14110
- this.track('page_unload');
14111
- // Send session end update with duration AND context data
14112
- if (this.sessionId && this.sessionStartTime) {
14113
- const endedAt = new Date();
14114
- const durationMs = Date.now() - this.sessionStartTime;
14115
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession(Object.assign({ session_id: this.sessionId, user_id: this.userId || undefined, anonymous_id: this.anonymousId, ended_at: endedAt.toISOString(), duration_ms: durationMs }, this.getContextData()));
14156
+ if (document.hidden) {
14157
+ // User switched tabs or minimized - flush immediately
14158
+ (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop(); // Stop recording and flush
14159
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon
14160
+ }
14161
+ });
14162
+ // 2. pagehide - fires when page is being unloaded
14163
+ window.addEventListener('pagehide', (event) => {
14164
+ var _a, _b, _c;
14165
+ const isActualClose = !event.persisted;
14166
+ if (isActualClose) {
14167
+ // Tab is closing - end session
14168
+ this.track('page_unload');
14169
+ if (this.sessionId && this.sessionStartTime) {
14170
+ const endedAt = new Date();
14171
+ const durationMs = Date.now() - this.sessionStartTime;
14172
+ (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession(Object.assign({ session_id: this.sessionId, user_id: this.userId || undefined, anonymous_id: this.anonymousId, ended_at: endedAt.toISOString(), duration_ms: durationMs }, this.getContextData()));
14173
+ // Clear session storage since session is ending
14174
+ sessionStorage.removeItem('devskin_session_id');
14175
+ sessionStorage.removeItem('devskin_session_start');
14176
+ }
14177
+ }
14178
+ else {
14179
+ // Navigation - track page change
14180
+ this.track('page_navigation');
14116
14181
  }
14117
- // Send any pending data
14118
- (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon for reliability
14182
+ // ALWAYS flush (whether navigation or close)
14183
+ (_b = this.rrwebRecorder) === null || _b === void 0 ? void 0 : _b.stop(); // Stop recording and flush remaining events
14184
+ (_c = this.transport) === null || _c === void 0 ? void 0 : _c.flush(true); // Use beacon for reliability
14185
+ });
14186
+ // 3. beforeunload - backup for older browsers
14187
+ window.addEventListener('beforeunload', () => {
14188
+ var _a, _b;
14189
+ (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop();
14190
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true);
14119
14191
  });
14120
14192
  }
14121
14193
  }