@devskin/browser-sdk 1.0.41 → 1.0.42

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.
@@ -13469,11 +13469,12 @@ class RRWebRecorder {
13469
13469
  },
13470
13470
  // Configuration options
13471
13471
  sampling: {
13472
- // Mouse interactions
13473
- mousemove: this.config.sampleRate !== undefined
13474
- ? Math.floor(100 / this.config.sampleRate)
13475
- : true,
13476
- // Mouse interactions
13472
+ // Mouse movement sampling - throttle to reduce bandwidth
13473
+ // e.g. 0.1 = capture 10% of mouse movements (Math.floor(1/0.1) = 10, meaning capture 1 every 10 events)
13474
+ mousemove: this.config.mouseMoveSampleRate !== undefined
13475
+ ? Math.max(1, Math.floor(1 / this.config.mouseMoveSampleRate))
13476
+ : 10, // Default: capture 1 every 10 mouse movements (10%)
13477
+ // Mouse interactions (clicks, etc) - always capture
13477
13478
  mouseInteraction: true,
13478
13479
  // Scroll events
13479
13480
  scroll: 150, // Throttle scroll events to every 150ms
@@ -13888,6 +13889,72 @@ class DevSkinSDK {
13888
13889
  // private sessionRecorder: SessionRecorder | null = null; // Replaced by RRWebRecorder
13889
13890
  this.rrwebRecorder = null;
13890
13891
  }
13892
+ /**
13893
+ * Detect if the current visitor is a bot/crawler
13894
+ */
13895
+ isBot() {
13896
+ if (typeof navigator === 'undefined')
13897
+ return true;
13898
+ const botPatterns = [
13899
+ /bot/i,
13900
+ /crawler/i,
13901
+ /spider/i,
13902
+ /crawling/i,
13903
+ /google/i,
13904
+ /bing/i,
13905
+ /yahoo/i,
13906
+ /duckduckgo/i,
13907
+ /baiduspider/i,
13908
+ /yandex/i,
13909
+ /facebookexternalhit/i,
13910
+ /twitterbot/i,
13911
+ /rogerbot/i,
13912
+ /linkedinbot/i,
13913
+ /embedly/i,
13914
+ /quora link preview/i,
13915
+ /showyoubot/i,
13916
+ /outbrain/i,
13917
+ /pinterest/i,
13918
+ /slackbot/i,
13919
+ /vkShare/i,
13920
+ /W3C_Validator/i,
13921
+ /redditbot/i,
13922
+ /Applebot/i,
13923
+ /WhatsApp/i,
13924
+ /flipboard/i,
13925
+ /tumblr/i,
13926
+ /bitlybot/i,
13927
+ /SkypeUriPreview/i,
13928
+ /nuzzel/i,
13929
+ /Discordbot/i,
13930
+ /Google Page Speed/i,
13931
+ /Qwantify/i,
13932
+ /pinterestbot/i,
13933
+ /Bitrix link preview/i,
13934
+ /XING-contenttabreceiver/i,
13935
+ /Chrome-Lighthouse/i,
13936
+ /telegrambot/i,
13937
+ /Lighthouse/i,
13938
+ /GTmetrix/i,
13939
+ /PageSpeed/i,
13940
+ /HeadlessChrome/i,
13941
+ /PhantomJS/i,
13942
+ ];
13943
+ const userAgent = navigator.userAgent;
13944
+ // Check user agent against bot patterns
13945
+ if (botPatterns.some(pattern => pattern.test(userAgent))) {
13946
+ return true;
13947
+ }
13948
+ // Check for headless browsers
13949
+ if ('webdriver' in navigator && navigator.webdriver === true) {
13950
+ return true;
13951
+ }
13952
+ // Check for headless Chrome
13953
+ if (userAgent.includes('HeadlessChrome')) {
13954
+ return true;
13955
+ }
13956
+ return false;
13957
+ }
13891
13958
  /**
13892
13959
  * Initialize the DevSkin SDK
13893
13960
  * Uses requestIdleCallback to defer heavy initialization without blocking the page
@@ -13920,7 +13987,7 @@ class DevSkinSDK {
13920
13987
  // Wait for session creation to complete before starting collectors
13921
13988
  this.startSession().then(() => {
13922
13989
  // Session created, now safe to start collectors that send data
13923
- var _a, _b;
13990
+ var _a, _b, _c;
13924
13991
  if (this.config.captureWebVitals) {
13925
13992
  this.performanceCollector = new PerformanceCollector(this.config, this.transport);
13926
13993
  this.performanceCollector.start();
@@ -13947,36 +14014,53 @@ class DevSkinSDK {
13947
14014
  }
13948
14015
  // Initialize session recording with rrweb
13949
14016
  if ((_a = this.config.sessionRecording) === null || _a === void 0 ? void 0 : _a.enabled) {
13950
- // Use RRWebRecorder for complete DOM recording
13951
- // Pass sessionStartTime to ensure timestamp continuity across page navigations
13952
- this.rrwebRecorder = new RRWebRecorder(this.sessionId, {
13953
- enabled: true,
13954
- sampleRate: this.config.sessionRecording.sampling || 0.5,
13955
- blockClass: 'rr-block',
13956
- ignoreClass: this.config.sessionRecording.ignoreClass || 'rr-ignore',
13957
- maskAllInputs: this.config.sessionRecording.maskAllInputs !== undefined
13958
- ? this.config.sessionRecording.maskAllInputs
13959
- : true,
13960
- maskInputOptions: {
13961
- password: true,
13962
- email: true,
13963
- tel: true,
13964
- },
13965
- recordCanvas: this.config.sessionRecording.recordCanvas || false,
13966
- collectFonts: true,
13967
- inlineStylesheet: true,
13968
- checkoutEveryNms: 5 * 60 * 1000, // Every 5 minutes
13969
- checkoutEveryNth: 200, // Every 200 events
13970
- }, (events) => {
13971
- var _a;
13972
- // Send rrweb events to backend
13973
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendRecordingEvents(this.sessionId, events);
13974
- }, this.sessionStartTime // Pass session start time for timestamp continuity
13975
- );
13976
- // Start recording immediately (session already created)
13977
- this.rrwebRecorder.start();
13978
- if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
13979
- console.log('[DevSkin] RRWeb recording started for session:', this.sessionId);
14017
+ // Skip recording for bots/crawlers to avoid impacting SEO
14018
+ const isBot = this.isBot();
14019
+ // Apply session sampling - decide if this session should be recorded
14020
+ const samplingRate = this.config.sessionRecording.sampling || 1.0;
14021
+ const shouldRecord = !isBot && Math.random() < samplingRate;
14022
+ if (shouldRecord) {
14023
+ // Use RRWebRecorder for complete DOM recording
14024
+ // Pass sessionStartTime to ensure timestamp continuity across page navigations
14025
+ this.rrwebRecorder = new RRWebRecorder(this.sessionId, {
14026
+ enabled: true,
14027
+ mouseMoveSampleRate: 0.5, // Sample 10% of mouse movements to reduce bandwidth
14028
+ blockClass: 'rr-block',
14029
+ ignoreClass: this.config.sessionRecording.ignoreClass || 'rr-ignore',
14030
+ maskAllInputs: this.config.sessionRecording.maskAllInputs !== undefined
14031
+ ? this.config.sessionRecording.maskAllInputs
14032
+ : true,
14033
+ maskInputOptions: {
14034
+ password: true,
14035
+ email: true,
14036
+ tel: true,
14037
+ },
14038
+ recordCanvas: this.config.sessionRecording.recordCanvas || false,
14039
+ collectFonts: true,
14040
+ inlineStylesheet: true,
14041
+ checkoutEveryNms: 5 * 60 * 1000, // Every 5 minutes
14042
+ checkoutEveryNth: 200, // Every 200 events
14043
+ }, (events) => {
14044
+ var _a;
14045
+ // Send rrweb events to backend
14046
+ (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendRecordingEvents(this.sessionId, events);
14047
+ }, this.sessionStartTime // Pass session start time for timestamp continuity
14048
+ );
14049
+ // Start recording immediately (session already created)
14050
+ this.rrwebRecorder.start();
14051
+ if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
14052
+ console.log(`[DevSkin] RRWeb recording started for session: ${this.sessionId} (sampling: ${samplingRate * 100}%)`);
14053
+ }
14054
+ }
14055
+ else {
14056
+ if ((_c = this.config) === null || _c === void 0 ? void 0 : _c.debug) {
14057
+ if (isBot) {
14058
+ console.log(`[DevSkin] Session ${this.sessionId} skipped recording (bot/crawler detected for SEO optimization)`);
14059
+ }
14060
+ else {
14061
+ console.log(`[DevSkin] Session ${this.sessionId} not sampled for recording (sampling: ${samplingRate * 100}%)`);
14062
+ }
14063
+ }
13980
14064
  }
13981
14065
  }
13982
14066
  // Track initial page view