@devskin/browser-sdk 1.0.26 → 1.0.28

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.
@@ -511,7 +511,7 @@ class ErrorCollector {
511
511
  stack: error.stack,
512
512
  type: error.name || 'Error',
513
513
  timestamp: new Date().toISOString(),
514
- session_id: '', // Will be set by transport
514
+ sessionId: '', // Will be set by transport
515
515
  url: window.location.href,
516
516
  breadcrumbs: [...this.breadcrumbs],
517
517
  context: Object.assign(Object.assign({}, context), { userAgent: navigator.userAgent, viewport: {
@@ -534,7 +534,7 @@ class ErrorCollector {
534
534
  message: String(error),
535
535
  type: 'Error',
536
536
  timestamp: new Date().toISOString(),
537
- session_id: '',
537
+ sessionId: '',
538
538
  url: window.location.href,
539
539
  breadcrumbs: [...this.breadcrumbs],
540
540
  context,
@@ -13480,8 +13480,8 @@ class RRWebRecorder {
13480
13480
  // Send immediately to ensure FullSnapshot reaches backend first
13481
13481
  this.flush();
13482
13482
  }
13483
- else if (this.hasFullSnapshot && this.events.length >= 50) {
13484
- // After FullSnapshot, batch other events
13483
+ else if (this.hasFullSnapshot && this.events.length >= 20) {
13484
+ // After FullSnapshot, batch other events (reduced from 50 to 20)
13485
13485
  this.flush();
13486
13486
  }
13487
13487
  },
@@ -13520,13 +13520,13 @@ class RRWebRecorder {
13520
13520
  // Capture iframe content
13521
13521
  recordCrossOriginIframes: false, // Security: don't record cross-origin iframes
13522
13522
  });
13523
- // Set up periodic flush (every 10 seconds)
13523
+ // Set up periodic flush (every 2 seconds)
13524
13524
  // Only flush if we have FullSnapshot to ensure first batch is complete
13525
13525
  this.flushInterval = window.setInterval(() => {
13526
13526
  if (this.hasFullSnapshot && this.events.length > 0) {
13527
13527
  this.flush();
13528
13528
  }
13529
- }, 10000);
13529
+ }, 2000); // Reduced from 10s to 2s
13530
13530
  // Safety check: After 2 seconds, force a full snapshot if none captured
13531
13531
  setTimeout(() => {
13532
13532
  if (!this.hasFullSnapshot) {
@@ -13584,8 +13584,8 @@ class Transport {
13584
13584
  this.config = config;
13585
13585
  this.queue = [];
13586
13586
  this.flushInterval = null;
13587
- this.maxQueueSize = 50;
13588
- this.flushIntervalMs = 5000; // 5 seconds
13587
+ this.maxQueueSize = 20; // Reduced from 50
13588
+ this.flushIntervalMs = 2000; // 2 seconds (reduced from 5s)
13589
13589
  this.apiUrl = config.apiUrl || 'https://api.devskin.com';
13590
13590
  // Start periodic flush
13591
13591
  this.startPeriodicFlush();
@@ -13610,8 +13610,8 @@ class Transport {
13610
13610
  this.sendToBackend('/v1/analytics/identify', user);
13611
13611
  }
13612
13612
  startSession(session) {
13613
- // Send session start immediately
13614
- this.sendToBackend('/v1/analytics/session', session);
13613
+ // Send session start immediately to RUM endpoint
13614
+ this.sendToBackend('/v1/rum/sessions', session);
13615
13615
  }
13616
13616
  sendError(error) {
13617
13617
  this.enqueue('error', error);
@@ -13622,6 +13622,12 @@ class Transport {
13622
13622
  sendPerformanceMetric(metric) {
13623
13623
  this.enqueue('performance', metric);
13624
13624
  }
13625
+ sendPageView(pageViewData) {
13626
+ return __awaiter$1(this, void 0, void 0, function* () {
13627
+ // Send page view immediately to RUM endpoint (don't queue)
13628
+ this.sendToBackend('/v1/rum/page-views', pageViewData);
13629
+ });
13630
+ }
13625
13631
  sendRecordingEvents(sessionId, events) {
13626
13632
  return __awaiter$1(this, void 0, void 0, function* () {
13627
13633
  // Calculate payload size
@@ -13722,23 +13728,23 @@ class Transport {
13722
13728
  getEndpointForType(type) {
13723
13729
  switch (type) {
13724
13730
  case 'event':
13725
- return '/v1/analytics/events';
13731
+ return '/v1/rum/events';
13726
13732
  case 'error':
13727
- return '/v1/analytics/errors';
13733
+ return '/v1/errors/errors';
13728
13734
  case 'network':
13729
- return '/v1/analytics/network';
13735
+ return '/v1/rum/network-requests';
13730
13736
  case 'performance':
13731
- return '/v1/analytics/performance';
13737
+ return '/v1/rum/web-vitals';
13732
13738
  case 'heatmap':
13733
13739
  return '/v1/sdk/heatmap';
13734
13740
  default:
13735
- return '/v1/analytics/events';
13741
+ return '/v1/rum/events';
13736
13742
  }
13737
13743
  }
13738
13744
  sendToBackendXHR(endpoint, data) {
13739
13745
  return __awaiter$1(this, void 0, void 0, function* () {
13740
13746
  const url = `${this.apiUrl}${endpoint}`;
13741
- const payload = Object.assign(Object.assign({}, data), { apiKey: this.config.apiKey, appId: this.config.appId, environment: this.config.environment, release: this.config.release });
13747
+ const payload = Object.assign(Object.assign({}, data), { apiKey: this.config.apiKey, applicationId: this.config.appId, environment: this.config.environment, release: this.config.release });
13742
13748
  // Apply beforeSend hook if provided
13743
13749
  if (this.config.beforeSend) {
13744
13750
  const processed = this.config.beforeSend(payload);
@@ -13788,7 +13794,7 @@ class Transport {
13788
13794
  sendToBackend(endpoint_1, data_1) {
13789
13795
  return __awaiter$1(this, arguments, void 0, function* (endpoint, data, useBeacon = false) {
13790
13796
  const url = `${this.apiUrl}${endpoint}`;
13791
- const payload = Object.assign(Object.assign({}, data), { apiKey: this.config.apiKey, appId: this.config.appId, environment: this.config.environment, release: this.config.release });
13797
+ const payload = Object.assign(Object.assign({}, data), { apiKey: this.config.apiKey, applicationId: this.config.appId, environment: this.config.environment, release: this.config.release });
13792
13798
  // Apply beforeSend hook if provided
13793
13799
  if (this.config.beforeSend) {
13794
13800
  const processed = this.config.beforeSend(payload);
@@ -13972,15 +13978,15 @@ class DevSkinSDK {
13972
13978
  return;
13973
13979
  }
13974
13980
  const eventData = {
13975
- event_name: eventName,
13976
- event_type: 'track',
13981
+ eventName: eventName,
13982
+ eventType: 'track',
13977
13983
  timestamp: new Date().toISOString(),
13978
- session_id: this.sessionId,
13979
- user_id: this.userId || undefined,
13980
- anonymous_id: this.anonymousId || undefined,
13984
+ sessionId: this.sessionId,
13985
+ userId: this.userId || undefined,
13986
+ anonymousId: this.anonymousId || undefined,
13981
13987
  properties: Object.assign(Object.assign({}, properties), this.getContextData()),
13982
- page_url: window.location.href,
13983
- page_title: document.title,
13988
+ pageUrl: window.location.href,
13989
+ pageTitle: document.title,
13984
13990
  };
13985
13991
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendEvent(eventData);
13986
13992
  if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
@@ -13991,7 +13997,24 @@ class DevSkinSDK {
13991
13997
  * Track a page view
13992
13998
  */
13993
13999
  trackPageView(properties) {
14000
+ var _a, _b;
14001
+ if (!this.initialized) {
14002
+ console.warn('[DevSkin] SDK not initialized. Call init() first.');
14003
+ return;
14004
+ }
14005
+ // Generate unique page view ID
14006
+ const pageViewId = this.generateId();
14007
+ // Send to RUM page-views endpoint
14008
+ (_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));
14009
+ // Also track as analytics event for backwards compatibility
13994
14010
  this.track('page_view', Object.assign({ path: window.location.pathname, search: window.location.search, hash: window.location.hash, referrer: document.referrer }, properties));
14011
+ if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
14012
+ console.log('[DevSkin] Page view tracked:', {
14013
+ sessionId: this.sessionId,
14014
+ pageViewId: pageViewId,
14015
+ url: window.location.href,
14016
+ });
14017
+ }
13995
14018
  }
13996
14019
  /**
13997
14020
  * Identify a user
@@ -14004,10 +14027,10 @@ class DevSkinSDK {
14004
14027
  }
14005
14028
  this.userId = userId;
14006
14029
  const userData = {
14007
- user_id: userId,
14008
- anonymous_id: this.anonymousId || undefined,
14030
+ userId: userId,
14031
+ anonymousId: this.anonymousId || undefined,
14009
14032
  traits: Object.assign(Object.assign({}, traits), this.getContextData()),
14010
- session_id: this.sessionId,
14033
+ sessionId: this.sessionId,
14011
14034
  timestamp: new Date().toISOString(),
14012
14035
  };
14013
14036
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.identifyUser(userData);
@@ -14081,7 +14104,7 @@ class DevSkinSDK {
14081
14104
  // Store in sessionStorage (persists across page navigations in same tab)
14082
14105
  sessionStorage.setItem('devskin_session_id', this.sessionId);
14083
14106
  sessionStorage.setItem('devskin_session_start', this.sessionStartTime.toString());
14084
- const sessionData = Object.assign({ session_id: this.sessionId, user_id: this.userId || undefined, anonymous_id: this.anonymousId, started_at: new Date().toISOString() }, this.getContextData());
14107
+ const sessionData = Object.assign({ sessionId: this.sessionId, userId: this.userId || undefined, anonymousId: this.anonymousId, startedAt: new Date().toISOString(), platform: 'web' }, this.getContextData());
14085
14108
  (_b = this.transport) === null || _b === void 0 ? void 0 : _b.startSession(sessionData);
14086
14109
  if ((_c = this.config) === null || _c === void 0 ? void 0 : _c.debug) {
14087
14110
  console.log('[DevSkin] New session created:', this.sessionId);
@@ -14122,27 +14145,45 @@ class DevSkinSDK {
14122
14145
  });
14123
14146
  }
14124
14147
  setupUnloadTracking() {
14125
- // Use pagehide event which is more reliable than beforeunload
14126
- window.addEventListener('pagehide', (event) => {
14148
+ // CRITICAL: Flush data BEFORE page unloads to avoid losing final events
14149
+ // 1. visibilitychange - fires when tab is hidden (most reliable)
14150
+ document.addEventListener('visibilitychange', () => {
14127
14151
  var _a, _b;
14128
- // Only end session if tab is actually closing (not just navigating)
14129
- // persisted = false means the page is going into bfcache (navigation)
14130
- // persisted = true OR event doesn't exist means actual close
14152
+ if (document.hidden) {
14153
+ // User switched tabs or minimized - flush immediately
14154
+ (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop(); // Stop recording and flush
14155
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon
14156
+ }
14157
+ });
14158
+ // 2. pagehide - fires when page is being unloaded
14159
+ window.addEventListener('pagehide', (event) => {
14160
+ var _a, _b, _c;
14131
14161
  const isActualClose = !event.persisted;
14132
14162
  if (isActualClose) {
14163
+ // Tab is closing - end session
14133
14164
  this.track('page_unload');
14134
- // Only send session end if tab is actually closing
14135
14165
  if (this.sessionId && this.sessionStartTime) {
14136
14166
  const endedAt = new Date();
14137
14167
  const durationMs = Date.now() - this.sessionStartTime;
14138
- (_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()));
14168
+ (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession(Object.assign({ sessionId: this.sessionId, userId: this.userId || undefined, anonymousId: this.anonymousId, endedAt: endedAt.toISOString(), durationMs: durationMs, platform: 'web' }, this.getContextData()));
14139
14169
  // Clear session storage since session is ending
14140
14170
  sessionStorage.removeItem('devskin_session_id');
14141
14171
  sessionStorage.removeItem('devskin_session_start');
14142
14172
  }
14143
14173
  }
14144
- // Always flush pending data (whether navigation or close)
14145
- (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon for reliability
14174
+ else {
14175
+ // Navigation - track page change
14176
+ this.track('page_navigation');
14177
+ }
14178
+ // ALWAYS flush (whether navigation or close)
14179
+ (_b = this.rrwebRecorder) === null || _b === void 0 ? void 0 : _b.stop(); // Stop recording and flush remaining events
14180
+ (_c = this.transport) === null || _c === void 0 ? void 0 : _c.flush(true); // Use beacon for reliability
14181
+ });
14182
+ // 3. beforeunload - backup for older browsers
14183
+ window.addEventListener('beforeunload', () => {
14184
+ var _a, _b;
14185
+ (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop();
14186
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true);
14146
14187
  });
14147
14188
  }
14148
14189
  }