@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.
@@ -517,7 +517,7 @@
517
517
  stack: error.stack,
518
518
  type: error.name || 'Error',
519
519
  timestamp: new Date().toISOString(),
520
- session_id: '', // Will be set by transport
520
+ sessionId: '', // Will be set by transport
521
521
  url: window.location.href,
522
522
  breadcrumbs: [...this.breadcrumbs],
523
523
  context: Object.assign(Object.assign({}, context), { userAgent: navigator.userAgent, viewport: {
@@ -540,7 +540,7 @@
540
540
  message: String(error),
541
541
  type: 'Error',
542
542
  timestamp: new Date().toISOString(),
543
- session_id: '',
543
+ sessionId: '',
544
544
  url: window.location.href,
545
545
  breadcrumbs: [...this.breadcrumbs],
546
546
  context,
@@ -13486,8 +13486,8 @@
13486
13486
  // Send immediately to ensure FullSnapshot reaches backend first
13487
13487
  this.flush();
13488
13488
  }
13489
- else if (this.hasFullSnapshot && this.events.length >= 50) {
13490
- // After FullSnapshot, batch other events
13489
+ else if (this.hasFullSnapshot && this.events.length >= 20) {
13490
+ // After FullSnapshot, batch other events (reduced from 50 to 20)
13491
13491
  this.flush();
13492
13492
  }
13493
13493
  },
@@ -13526,13 +13526,13 @@
13526
13526
  // Capture iframe content
13527
13527
  recordCrossOriginIframes: false, // Security: don't record cross-origin iframes
13528
13528
  });
13529
- // Set up periodic flush (every 10 seconds)
13529
+ // Set up periodic flush (every 2 seconds)
13530
13530
  // Only flush if we have FullSnapshot to ensure first batch is complete
13531
13531
  this.flushInterval = window.setInterval(() => {
13532
13532
  if (this.hasFullSnapshot && this.events.length > 0) {
13533
13533
  this.flush();
13534
13534
  }
13535
- }, 10000);
13535
+ }, 2000); // Reduced from 10s to 2s
13536
13536
  // Safety check: After 2 seconds, force a full snapshot if none captured
13537
13537
  setTimeout(() => {
13538
13538
  if (!this.hasFullSnapshot) {
@@ -13590,8 +13590,8 @@
13590
13590
  this.config = config;
13591
13591
  this.queue = [];
13592
13592
  this.flushInterval = null;
13593
- this.maxQueueSize = 50;
13594
- this.flushIntervalMs = 5000; // 5 seconds
13593
+ this.maxQueueSize = 20; // Reduced from 50
13594
+ this.flushIntervalMs = 2000; // 2 seconds (reduced from 5s)
13595
13595
  this.apiUrl = config.apiUrl || 'https://api.devskin.com';
13596
13596
  // Start periodic flush
13597
13597
  this.startPeriodicFlush();
@@ -13616,8 +13616,8 @@
13616
13616
  this.sendToBackend('/v1/analytics/identify', user);
13617
13617
  }
13618
13618
  startSession(session) {
13619
- // Send session start immediately
13620
- this.sendToBackend('/v1/analytics/session', session);
13619
+ // Send session start immediately to RUM endpoint
13620
+ this.sendToBackend('/v1/rum/sessions', session);
13621
13621
  }
13622
13622
  sendError(error) {
13623
13623
  this.enqueue('error', error);
@@ -13628,6 +13628,12 @@
13628
13628
  sendPerformanceMetric(metric) {
13629
13629
  this.enqueue('performance', metric);
13630
13630
  }
13631
+ sendPageView(pageViewData) {
13632
+ return __awaiter$1(this, void 0, void 0, function* () {
13633
+ // Send page view immediately to RUM endpoint (don't queue)
13634
+ this.sendToBackend('/v1/rum/page-views', pageViewData);
13635
+ });
13636
+ }
13631
13637
  sendRecordingEvents(sessionId, events) {
13632
13638
  return __awaiter$1(this, void 0, void 0, function* () {
13633
13639
  // Calculate payload size
@@ -13728,23 +13734,23 @@
13728
13734
  getEndpointForType(type) {
13729
13735
  switch (type) {
13730
13736
  case 'event':
13731
- return '/v1/analytics/events';
13737
+ return '/v1/rum/events';
13732
13738
  case 'error':
13733
- return '/v1/analytics/errors';
13739
+ return '/v1/errors/errors';
13734
13740
  case 'network':
13735
- return '/v1/analytics/network';
13741
+ return '/v1/rum/network-requests';
13736
13742
  case 'performance':
13737
- return '/v1/analytics/performance';
13743
+ return '/v1/rum/web-vitals';
13738
13744
  case 'heatmap':
13739
13745
  return '/v1/sdk/heatmap';
13740
13746
  default:
13741
- return '/v1/analytics/events';
13747
+ return '/v1/rum/events';
13742
13748
  }
13743
13749
  }
13744
13750
  sendToBackendXHR(endpoint, data) {
13745
13751
  return __awaiter$1(this, void 0, void 0, function* () {
13746
13752
  const url = `${this.apiUrl}${endpoint}`;
13747
- const payload = Object.assign(Object.assign({}, data), { apiKey: this.config.apiKey, appId: this.config.appId, environment: this.config.environment, release: this.config.release });
13753
+ const payload = Object.assign(Object.assign({}, data), { apiKey: this.config.apiKey, applicationId: this.config.appId, environment: this.config.environment, release: this.config.release });
13748
13754
  // Apply beforeSend hook if provided
13749
13755
  if (this.config.beforeSend) {
13750
13756
  const processed = this.config.beforeSend(payload);
@@ -13794,7 +13800,7 @@
13794
13800
  sendToBackend(endpoint_1, data_1) {
13795
13801
  return __awaiter$1(this, arguments, void 0, function* (endpoint, data, useBeacon = false) {
13796
13802
  const url = `${this.apiUrl}${endpoint}`;
13797
- const payload = Object.assign(Object.assign({}, data), { apiKey: this.config.apiKey, appId: this.config.appId, environment: this.config.environment, release: this.config.release });
13803
+ const payload = Object.assign(Object.assign({}, data), { apiKey: this.config.apiKey, applicationId: this.config.appId, environment: this.config.environment, release: this.config.release });
13798
13804
  // Apply beforeSend hook if provided
13799
13805
  if (this.config.beforeSend) {
13800
13806
  const processed = this.config.beforeSend(payload);
@@ -13978,15 +13984,15 @@
13978
13984
  return;
13979
13985
  }
13980
13986
  const eventData = {
13981
- event_name: eventName,
13982
- event_type: 'track',
13987
+ eventName: eventName,
13988
+ eventType: 'track',
13983
13989
  timestamp: new Date().toISOString(),
13984
- session_id: this.sessionId,
13985
- user_id: this.userId || undefined,
13986
- anonymous_id: this.anonymousId || undefined,
13990
+ sessionId: this.sessionId,
13991
+ userId: this.userId || undefined,
13992
+ anonymousId: this.anonymousId || undefined,
13987
13993
  properties: Object.assign(Object.assign({}, properties), this.getContextData()),
13988
- page_url: window.location.href,
13989
- page_title: document.title,
13994
+ pageUrl: window.location.href,
13995
+ pageTitle: document.title,
13990
13996
  };
13991
13997
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendEvent(eventData);
13992
13998
  if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
@@ -13997,7 +14003,24 @@
13997
14003
  * Track a page view
13998
14004
  */
13999
14005
  trackPageView(properties) {
14006
+ var _a, _b;
14007
+ if (!this.initialized) {
14008
+ console.warn('[DevSkin] SDK not initialized. Call init() first.');
14009
+ return;
14010
+ }
14011
+ // Generate unique page view ID
14012
+ const pageViewId = this.generateId();
14013
+ // Send to RUM page-views endpoint
14014
+ (_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));
14015
+ // Also track as analytics event for backwards compatibility
14000
14016
  this.track('page_view', Object.assign({ path: window.location.pathname, search: window.location.search, hash: window.location.hash, referrer: document.referrer }, properties));
14017
+ if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
14018
+ console.log('[DevSkin] Page view tracked:', {
14019
+ sessionId: this.sessionId,
14020
+ pageViewId: pageViewId,
14021
+ url: window.location.href,
14022
+ });
14023
+ }
14001
14024
  }
14002
14025
  /**
14003
14026
  * Identify a user
@@ -14010,10 +14033,10 @@
14010
14033
  }
14011
14034
  this.userId = userId;
14012
14035
  const userData = {
14013
- user_id: userId,
14014
- anonymous_id: this.anonymousId || undefined,
14036
+ userId: userId,
14037
+ anonymousId: this.anonymousId || undefined,
14015
14038
  traits: Object.assign(Object.assign({}, traits), this.getContextData()),
14016
- session_id: this.sessionId,
14039
+ sessionId: this.sessionId,
14017
14040
  timestamp: new Date().toISOString(),
14018
14041
  };
14019
14042
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.identifyUser(userData);
@@ -14087,7 +14110,7 @@
14087
14110
  // Store in sessionStorage (persists across page navigations in same tab)
14088
14111
  sessionStorage.setItem('devskin_session_id', this.sessionId);
14089
14112
  sessionStorage.setItem('devskin_session_start', this.sessionStartTime.toString());
14090
- const sessionData = Object.assign({ session_id: this.sessionId, user_id: this.userId || undefined, anonymous_id: this.anonymousId, started_at: new Date().toISOString() }, this.getContextData());
14113
+ const sessionData = Object.assign({ sessionId: this.sessionId, userId: this.userId || undefined, anonymousId: this.anonymousId, startedAt: new Date().toISOString(), platform: 'web' }, this.getContextData());
14091
14114
  (_b = this.transport) === null || _b === void 0 ? void 0 : _b.startSession(sessionData);
14092
14115
  if ((_c = this.config) === null || _c === void 0 ? void 0 : _c.debug) {
14093
14116
  console.log('[DevSkin] New session created:', this.sessionId);
@@ -14128,27 +14151,45 @@
14128
14151
  });
14129
14152
  }
14130
14153
  setupUnloadTracking() {
14131
- // Use pagehide event which is more reliable than beforeunload
14132
- window.addEventListener('pagehide', (event) => {
14154
+ // CRITICAL: Flush data BEFORE page unloads to avoid losing final events
14155
+ // 1. visibilitychange - fires when tab is hidden (most reliable)
14156
+ document.addEventListener('visibilitychange', () => {
14133
14157
  var _a, _b;
14134
- // Only end session if tab is actually closing (not just navigating)
14135
- // persisted = false means the page is going into bfcache (navigation)
14136
- // persisted = true OR event doesn't exist means actual close
14158
+ if (document.hidden) {
14159
+ // User switched tabs or minimized - flush immediately
14160
+ (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop(); // Stop recording and flush
14161
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon
14162
+ }
14163
+ });
14164
+ // 2. pagehide - fires when page is being unloaded
14165
+ window.addEventListener('pagehide', (event) => {
14166
+ var _a, _b, _c;
14137
14167
  const isActualClose = !event.persisted;
14138
14168
  if (isActualClose) {
14169
+ // Tab is closing - end session
14139
14170
  this.track('page_unload');
14140
- // Only send session end if tab is actually closing
14141
14171
  if (this.sessionId && this.sessionStartTime) {
14142
14172
  const endedAt = new Date();
14143
14173
  const durationMs = Date.now() - this.sessionStartTime;
14144
- (_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()));
14174
+ (_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()));
14145
14175
  // Clear session storage since session is ending
14146
14176
  sessionStorage.removeItem('devskin_session_id');
14147
14177
  sessionStorage.removeItem('devskin_session_start');
14148
14178
  }
14149
14179
  }
14150
- // Always flush pending data (whether navigation or close)
14151
- (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon for reliability
14180
+ else {
14181
+ // Navigation - track page change
14182
+ this.track('page_navigation');
14183
+ }
14184
+ // ALWAYS flush (whether navigation or close)
14185
+ (_b = this.rrwebRecorder) === null || _b === void 0 ? void 0 : _b.stop(); // Stop recording and flush remaining events
14186
+ (_c = this.transport) === null || _c === void 0 ? void 0 : _c.flush(true); // Use beacon for reliability
14187
+ });
14188
+ // 3. beforeunload - backup for older browsers
14189
+ window.addEventListener('beforeunload', () => {
14190
+ var _a, _b;
14191
+ (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop();
14192
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true);
14152
14193
  });
14153
14194
  }
14154
14195
  }