@devskin/browser-sdk 1.0.39 → 1.0.41

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.
@@ -3,13 +3,15 @@ import { Transport } from '../transport';
3
3
  export declare class HeatmapCollector {
4
4
  private config;
5
5
  private transport;
6
+ private anonymousId;
7
+ private sessionId;
6
8
  private clickData;
7
9
  private scrollData;
8
10
  private mouseMoveData;
9
11
  private maxScrollDepth;
10
12
  private flushInterval;
11
13
  private mouseMoveSampling;
12
- constructor(config: DevSkinConfig, transport: Transport);
14
+ constructor(config: DevSkinConfig, transport: Transport, anonymousId: string, sessionId: string);
13
15
  start(): void;
14
16
  stop(): void;
15
17
  private trackClicks;
@@ -1 +1 @@
1
- {"version":3,"file":"heatmap.d.ts","sourceRoot":"","sources":["../../src/collectors/heatmap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAkCzC,qBAAa,gBAAgB;IASzB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS;IATnB,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,iBAAiB,CAAO;gBAGtB,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,SAAS;IAG9B,KAAK,IAAI,IAAI;IAoCb,IAAI,IAAI,IAAI;IAQZ,OAAO,CAAC,WAAW;IA8CnB,OAAO,CAAC,gBAAgB;IA2CxB,OAAO,CAAC,kBAAkB;IAkC1B,OAAO,CAAC,KAAK;IAmCb,OAAO,CAAC,kBAAkB;CAc3B"}
1
+ {"version":3,"file":"heatmap.d.ts","sourceRoot":"","sources":["../../src/collectors/heatmap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAkCzC,qBAAa,gBAAgB;IASzB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,SAAS;IAXnB,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,iBAAiB,CAAO;gBAGtB,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM;IAG3B,KAAK,IAAI,IAAI;IAoCb,IAAI,IAAI,IAAI;IAQZ,OAAO,CAAC,WAAW;IA8CnB,OAAO,CAAC,gBAAgB;IA2CxB,OAAO,CAAC,kBAAkB;IAkC1B,OAAO,CAAC,KAAK;IAyCb,OAAO,CAAC,kBAAkB;CAc3B"}
@@ -861,9 +861,11 @@ class NetworkCollector {
861
861
  }
862
862
 
863
863
  class HeatmapCollector {
864
- constructor(config, transport) {
864
+ constructor(config, transport, anonymousId, sessionId) {
865
865
  this.config = config;
866
866
  this.transport = transport;
867
+ this.anonymousId = anonymousId;
868
+ this.sessionId = sessionId;
867
869
  this.clickData = [];
868
870
  this.scrollData = [];
869
871
  this.mouseMoveData = [];
@@ -1007,21 +1009,21 @@ class HeatmapCollector {
1007
1009
  // Send clicks individually (backend expects one click event per item)
1008
1010
  if (this.clickData.length > 0) {
1009
1011
  this.clickData.forEach(click => {
1010
- this.transport.sendHeatmapData(Object.assign({ type: 'click' }, click));
1012
+ this.transport.sendHeatmapData(Object.assign({ type: 'click', anonymousId: this.anonymousId, sessionId: this.sessionId }, click));
1011
1013
  });
1012
1014
  this.clickData = [];
1013
1015
  }
1014
1016
  // Send scroll data individually
1015
1017
  if (this.scrollData.length > 0) {
1016
1018
  this.scrollData.forEach(scroll => {
1017
- this.transport.sendHeatmapData(Object.assign({ type: 'scroll' }, scroll));
1019
+ this.transport.sendHeatmapData(Object.assign({ type: 'scroll', anonymousId: this.anonymousId, sessionId: this.sessionId }, scroll));
1018
1020
  });
1019
1021
  this.scrollData = [];
1020
1022
  }
1021
1023
  // Send mouse moves individually
1022
1024
  if (this.mouseMoveData.length > 0) {
1023
1025
  this.mouseMoveData.forEach(move => {
1024
- this.transport.sendHeatmapData(Object.assign({ type: 'mousemove' }, move));
1026
+ this.transport.sendHeatmapData(Object.assign({ type: 'mousemove', anonymousId: this.anonymousId, sessionId: this.sessionId }, move));
1025
1027
  });
1026
1028
  this.mouseMoveData = [];
1027
1029
  }
@@ -13876,6 +13878,7 @@ class DevSkinSDK {
13876
13878
  this.anonymousId = null;
13877
13879
  this.sessionStartTime = 0;
13878
13880
  this.initialized = false;
13881
+ this.initializing = false;
13879
13882
  this.heartbeatInterval = null;
13880
13883
  // Collectors
13881
13884
  this.deviceCollector = null;
@@ -13891,12 +13894,15 @@ class DevSkinSDK {
13891
13894
  }
13892
13895
  /**
13893
13896
  * Initialize the DevSkin SDK
13897
+ * Uses requestIdleCallback to defer heavy initialization without blocking the page
13894
13898
  */
13895
13899
  init(config) {
13896
- if (this.initialized) {
13897
- console.warn('[DevSkin] SDK already initialized');
13900
+ if (this.initialized || this.initializing) {
13901
+ console.warn('[DevSkin] SDK already initialized or initializing');
13898
13902
  return;
13899
13903
  }
13904
+ // Mark as initializing to prevent duplicate init() calls
13905
+ this.initializing = true;
13900
13906
  this.config = Object.assign({ debug: false, captureWebVitals: true, captureNetworkRequests: true, captureErrors: true, captureUserAgent: true, captureLocation: true, captureDevice: true, heatmapOptions: {
13901
13907
  enabled: true,
13902
13908
  trackClicks: true,
@@ -13906,99 +13912,118 @@ class DevSkinSDK {
13906
13912
  if (this.config.debug) {
13907
13913
  console.log('[DevSkin] Initializing SDK with config:', this.config);
13908
13914
  }
13909
- // Initialize transport
13915
+ // Initialize lightweight components immediately (needed for session context)
13910
13916
  this.transport = new Transport(this.config);
13911
- // Generate anonymous ID if not exists
13912
13917
  this.anonymousId = this.getOrCreateAnonymousId();
13913
- // Initialize collectors BEFORE starting session (so getContextData works)
13914
13918
  this.deviceCollector = new DeviceCollector(this.config);
13915
13919
  this.locationCollector = new LocationCollector(this.config);
13916
13920
  this.browserCollector = new BrowserCollector(this.config);
13917
- // Start session (will now include device/browser/location data)
13918
- // Wait for session creation to complete before starting collectors
13919
- this.startSession().then(() => {
13920
- // Session created, now safe to start collectors that send data
13921
- var _a, _b;
13922
- if (this.config.captureWebVitals) {
13923
- this.performanceCollector = new PerformanceCollector(this.config, this.transport);
13924
- this.performanceCollector.start();
13925
- }
13926
- if (this.config.captureErrors) {
13927
- this.errorCollector = new ErrorCollector(this.config, this.transport);
13928
- this.errorCollector.start();
13929
- }
13930
- if (this.config.captureNetworkRequests) {
13931
- this.networkCollector = new NetworkCollector(this.config, this.transport);
13932
- this.networkCollector.start();
13933
- }
13934
- // Initialize heatmap collector - SEMPRE habilitado
13935
- // Merge default heatmap config with user config
13936
- const heatmapConfig = Object.assign({ enabled: true, trackClicks: true, trackScroll: true, trackMouseMovement: true, mouseMoveSampling: 0.1 }, this.config.heatmapOptions);
13937
- this.config.heatmapOptions = heatmapConfig;
13938
- this.heatmapCollector = new HeatmapCollector(this.config, this.transport);
13939
- this.heatmapCollector.start();
13940
- // Initialize screenshot collector and capture page
13941
- this.screenshotCollector = new ScreenshotCollector(this.config, this.transport);
13942
- this.screenshotCollector.captureAndSend(this.sessionId, window.location.href);
13943
- if (this.config.debug) {
13944
- console.log('[DevSkin] Heatmap collection enabled (always on)');
13945
- }
13946
- // Initialize session recording with rrweb
13947
- if ((_a = this.config.sessionRecording) === null || _a === void 0 ? void 0 : _a.enabled) {
13948
- // Use RRWebRecorder for complete DOM recording
13949
- // Pass sessionStartTime to ensure timestamp continuity across page navigations
13950
- this.rrwebRecorder = new RRWebRecorder(this.sessionId, {
13951
- enabled: true,
13952
- sampleRate: this.config.sessionRecording.sampling || 0.5,
13953
- blockClass: 'rr-block',
13954
- ignoreClass: this.config.sessionRecording.ignoreClass || 'rr-ignore',
13955
- maskAllInputs: this.config.sessionRecording.maskAllInputs !== undefined
13956
- ? this.config.sessionRecording.maskAllInputs
13957
- : true,
13958
- maskInputOptions: {
13959
- password: true,
13960
- email: true,
13961
- tel: true,
13962
- },
13963
- recordCanvas: this.config.sessionRecording.recordCanvas || false,
13964
- collectFonts: true,
13965
- inlineStylesheet: true,
13966
- checkoutEveryNms: 5 * 60 * 1000, // Every 5 minutes
13967
- checkoutEveryNth: 200, // Every 200 events
13968
- }, (events) => {
13969
- var _a;
13970
- // Send rrweb events to backend
13971
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendRecordingEvents(this.sessionId, events);
13972
- }, this.sessionStartTime // Pass session start time for timestamp continuity
13973
- );
13974
- // Start recording immediately (session already created)
13975
- this.rrwebRecorder.start();
13976
- if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
13977
- console.log('[DevSkin] RRWeb recording started for session:', this.sessionId);
13921
+ // Defer heavy initialization to avoid blocking page load/rendering
13922
+ const initHeavyCollectors = () => {
13923
+ // Start session (will now include device/browser/location data)
13924
+ // Wait for session creation to complete before starting collectors
13925
+ this.startSession().then(() => {
13926
+ // Session created, now safe to start collectors that send data
13927
+ var _a, _b;
13928
+ if (this.config.captureWebVitals) {
13929
+ this.performanceCollector = new PerformanceCollector(this.config, this.transport);
13930
+ this.performanceCollector.start();
13931
+ }
13932
+ if (this.config.captureErrors) {
13933
+ this.errorCollector = new ErrorCollector(this.config, this.transport);
13934
+ this.errorCollector.start();
13935
+ }
13936
+ if (this.config.captureNetworkRequests) {
13937
+ this.networkCollector = new NetworkCollector(this.config, this.transport);
13938
+ this.networkCollector.start();
13939
+ }
13940
+ // Initialize heatmap collector - SEMPRE habilitado
13941
+ // Merge default heatmap config with user config
13942
+ const heatmapConfig = Object.assign({ enabled: true, trackClicks: true, trackScroll: true, trackMouseMovement: true, mouseMoveSampling: 0.1 }, this.config.heatmapOptions);
13943
+ this.config.heatmapOptions = heatmapConfig;
13944
+ this.heatmapCollector = new HeatmapCollector(this.config, this.transport, this.anonymousId, this.sessionId);
13945
+ this.heatmapCollector.start();
13946
+ // Initialize screenshot collector and capture page
13947
+ this.screenshotCollector = new ScreenshotCollector(this.config, this.transport);
13948
+ this.screenshotCollector.captureAndSend(this.sessionId, window.location.href);
13949
+ if (this.config.debug) {
13950
+ console.log('[DevSkin] Heatmap collection enabled (always on)');
13951
+ }
13952
+ // Initialize session recording with rrweb
13953
+ if ((_a = this.config.sessionRecording) === null || _a === void 0 ? void 0 : _a.enabled) {
13954
+ // Use RRWebRecorder for complete DOM recording
13955
+ // Pass sessionStartTime to ensure timestamp continuity across page navigations
13956
+ this.rrwebRecorder = new RRWebRecorder(this.sessionId, {
13957
+ enabled: true,
13958
+ sampleRate: this.config.sessionRecording.sampling || 0.5,
13959
+ blockClass: 'rr-block',
13960
+ ignoreClass: this.config.sessionRecording.ignoreClass || 'rr-ignore',
13961
+ maskAllInputs: this.config.sessionRecording.maskAllInputs !== undefined
13962
+ ? this.config.sessionRecording.maskAllInputs
13963
+ : true,
13964
+ maskInputOptions: {
13965
+ password: true,
13966
+ email: true,
13967
+ tel: true,
13968
+ },
13969
+ recordCanvas: this.config.sessionRecording.recordCanvas || false,
13970
+ collectFonts: true,
13971
+ inlineStylesheet: true,
13972
+ checkoutEveryNms: 5 * 60 * 1000, // Every 5 minutes
13973
+ checkoutEveryNth: 200, // Every 200 events
13974
+ }, (events) => {
13975
+ var _a;
13976
+ // Send rrweb events to backend
13977
+ (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendRecordingEvents(this.sessionId, events);
13978
+ }, this.sessionStartTime // Pass session start time for timestamp continuity
13979
+ );
13980
+ // Start recording immediately (session already created)
13981
+ this.rrwebRecorder.start();
13982
+ if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
13983
+ console.log('[DevSkin] RRWeb recording started for session:', this.sessionId);
13984
+ }
13978
13985
  }
13986
+ // Track initial page view
13987
+ this.trackPageView();
13988
+ // Start heartbeat to update session duration every 30 seconds
13989
+ this.startHeartbeat();
13990
+ }).catch((err) => {
13991
+ console.error('[DevSkin] Failed to create session:', err);
13992
+ });
13993
+ // Track page visibility changes
13994
+ this.setupVisibilityTracking();
13995
+ // Track page unload
13996
+ this.setupUnloadTracking();
13997
+ // Mark as fully initialized only after everything is loaded
13998
+ this.initialized = true;
13999
+ this.initializing = false;
14000
+ };
14001
+ // Use requestIdleCallback to defer heavy initialization (non-blocking)
14002
+ // Falls back to setTimeout for browsers that don't support it
14003
+ if (typeof window !== 'undefined') {
14004
+ if ('requestIdleCallback' in window) {
14005
+ window.requestIdleCallback(initHeavyCollectors, { timeout: 2000 });
13979
14006
  }
13980
- // Track initial page view
13981
- this.trackPageView();
13982
- // Start heartbeat to update session duration every 30 seconds
13983
- this.startHeartbeat();
13984
- }).catch((err) => {
13985
- console.error('[DevSkin] Failed to create session:', err);
13986
- });
13987
- // Track page visibility changes
13988
- this.setupVisibilityTracking();
13989
- // Track page unload
13990
- this.setupUnloadTracking();
13991
- this.initialized = true;
14007
+ else {
14008
+ // Fallback for older browsers
14009
+ setTimeout(initHeavyCollectors, 1);
14010
+ }
14011
+ }
14012
+ else {
14013
+ // Node.js environment (SSR)
14014
+ initHeavyCollectors();
14015
+ }
13992
14016
  if (this.config.debug) {
13993
- console.log('[DevSkin] SDK initialized successfully');
14017
+ console.log('[DevSkin] SDK initialization started (heavy collectors loading in background)');
13994
14018
  }
13995
14019
  }
13996
14020
  /**
13997
14021
  * Track a custom event
14022
+ * Works immediately after init() even if heavy collectors are still loading
13998
14023
  */
13999
14024
  track(eventName, properties) {
14000
- var _a, _b;
14001
- if (!this.initialized) {
14025
+ var _a, _b, _c, _d;
14026
+ if (!this.transport) {
14002
14027
  console.warn('[DevSkin] SDK not initialized. Call init() first.');
14003
14028
  return;
14004
14029
  }
@@ -14006,15 +14031,15 @@ class DevSkinSDK {
14006
14031
  eventName: eventName,
14007
14032
  eventType: 'track',
14008
14033
  timestamp: new Date().toISOString(),
14009
- sessionId: this.sessionId,
14010
- userId: this.userId || undefined,
14011
- anonymousId: this.anonymousId || undefined,
14034
+ sessionId: (_a = this.sessionId) !== null && _a !== void 0 ? _a : undefined,
14035
+ userId: (_b = this.userId) !== null && _b !== void 0 ? _b : undefined,
14036
+ anonymousId: (_c = this.anonymousId) !== null && _c !== void 0 ? _c : undefined,
14012
14037
  properties: Object.assign(Object.assign({}, properties), this.getContextData()),
14013
14038
  pageUrl: window.location.href,
14014
14039
  pageTitle: document.title,
14015
14040
  };
14016
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendEvent(eventData);
14017
- if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
14041
+ this.transport.sendEvent(eventData);
14042
+ if ((_d = this.config) === null || _d === void 0 ? void 0 : _d.debug) {
14018
14043
  console.log('[DevSkin] Event tracked:', eventData);
14019
14044
  }
14020
14045
  }
@@ -14043,23 +14068,24 @@ class DevSkinSDK {
14043
14068
  }
14044
14069
  /**
14045
14070
  * Identify a user
14071
+ * Works immediately after init() even if heavy collectors are still loading
14046
14072
  */
14047
14073
  identify(userId, traits) {
14048
- var _a, _b;
14049
- if (!this.initialized) {
14074
+ var _a, _b, _c;
14075
+ if (!this.transport) {
14050
14076
  console.warn('[DevSkin] SDK not initialized. Call init() first.');
14051
14077
  return;
14052
14078
  }
14053
14079
  this.userId = userId;
14054
14080
  const userData = {
14055
14081
  userId: userId,
14056
- anonymousId: this.anonymousId || undefined,
14082
+ anonymousId: (_a = this.anonymousId) !== null && _a !== void 0 ? _a : undefined,
14057
14083
  traits: Object.assign(Object.assign({}, traits), this.getContextData()),
14058
- sessionId: this.sessionId,
14084
+ sessionId: (_b = this.sessionId) !== null && _b !== void 0 ? _b : undefined,
14059
14085
  timestamp: new Date().toISOString(),
14060
14086
  };
14061
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.identifyUser(userData);
14062
- if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
14087
+ this.transport.identifyUser(userData);
14088
+ if ((_c = this.config) === null || _c === void 0 ? void 0 : _c.debug) {
14063
14089
  console.log('[DevSkin] User identified:', userData);
14064
14090
  }
14065
14091
  }