humanbehavior-js 0.4.20 → 0.4.22

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.
Files changed (53) hide show
  1. package/dist/cjs/angular/index.cjs +817 -19
  2. package/dist/cjs/angular/index.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +833 -19
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/cjs/react/index.cjs +818 -20
  6. package/dist/cjs/react/index.cjs.map +1 -1
  7. package/dist/cjs/remix/index.cjs +818 -20
  8. package/dist/cjs/remix/index.cjs.map +1 -1
  9. package/dist/cjs/svelte/index.cjs +817 -19
  10. package/dist/cjs/svelte/index.cjs.map +1 -1
  11. package/dist/cjs/vue/index.cjs +817 -19
  12. package/dist/cjs/vue/index.cjs.map +1 -1
  13. package/dist/esm/angular/index.js +817 -19
  14. package/dist/esm/angular/index.js.map +1 -1
  15. package/dist/esm/index.js +825 -20
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/react/index.js +818 -20
  18. package/dist/esm/react/index.js.map +1 -1
  19. package/dist/esm/remix/index.js +818 -20
  20. package/dist/esm/remix/index.js.map +1 -1
  21. package/dist/esm/svelte/index.js +817 -19
  22. package/dist/esm/svelte/index.js.map +1 -1
  23. package/dist/esm/vue/index.js +817 -19
  24. package/dist/esm/vue/index.js.map +1 -1
  25. package/dist/index.min.js +1 -1
  26. package/dist/index.min.js.map +1 -1
  27. package/dist/types/angular/index.d.ts +60 -1
  28. package/dist/types/index.d.ts +258 -3
  29. package/dist/types/react/index.d.ts +60 -1
  30. package/dist/types/remix/index.d.ts +60 -1
  31. package/dist/types/svelte/index.d.ts +60 -1
  32. package/package/canvas-recording-demo.html +1 -1
  33. package/package/simple-spa.html +1 -1
  34. package/package/src/angular/index.ts +3 -3
  35. package/package/src/react/index.tsx +2 -2
  36. package/package/src/svelte/index.ts +1 -1
  37. package/package/src/tracker.ts +2 -2
  38. package/package/src/vue/index.ts +1 -1
  39. package/package.json +1 -1
  40. package/simple-spa.html +164 -2
  41. package/src/angular/index.ts +3 -3
  42. package/src/api.ts +40 -0
  43. package/src/index.ts +7 -0
  44. package/src/react/index.tsx +2 -2
  45. package/src/svelte/index.ts +1 -1
  46. package/src/tracker.ts +193 -17
  47. package/src/utils/ip-detector.ts +158 -0
  48. package/src/utils/property-detector.ts +345 -0
  49. package/src/utils/property-manager.ts +274 -0
  50. package/src/vue/index.ts +1 -1
  51. package/canvas-recording-demo.html +0 -143
  52. package/clean-console-demo.html +0 -39
  53. package/simple-demo.html +0 -26
package/dist/esm/index.js CHANGED
@@ -12389,6 +12389,152 @@ const logWarn = (message, ...args) => logger.warn(message, ...args);
12389
12389
  const logInfo = (message, ...args) => logger.info(message, ...args);
12390
12390
  const logDebug = (message, ...args) => logger.debug(message, ...args);
12391
12391
 
12392
+ /**
12393
+ * IP Address Detection Utility
12394
+ * Attempts to get the client's public IP address using multiple methods
12395
+ */
12396
+ /**
12397
+ * Get IP address using STUN server (most reliable)
12398
+ */
12399
+ function getIPFromSTUN() {
12400
+ return __awaiter(this, void 0, void 0, function* () {
12401
+ try {
12402
+ const pc = new RTCPeerConnection({
12403
+ iceServers: [
12404
+ { urls: 'stun:stun.l.google.com:19302' },
12405
+ { urls: 'stun:stun1.l.google.com:19302' },
12406
+ { urls: 'stun:stun2.l.google.com:19302' }
12407
+ ]
12408
+ });
12409
+ return new Promise((resolve) => {
12410
+ const timeout = setTimeout(() => {
12411
+ pc.close();
12412
+ resolve(null);
12413
+ }, 5000);
12414
+ pc.createDataChannel('');
12415
+ pc.createOffer()
12416
+ .then(offer => pc.setLocalDescription(offer))
12417
+ .catch(() => resolve(null));
12418
+ pc.onicecandidate = (event) => {
12419
+ if (event.candidate) {
12420
+ const candidate = event.candidate.candidate;
12421
+ const match = candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3})/);
12422
+ if (match) {
12423
+ clearTimeout(timeout);
12424
+ pc.close();
12425
+ resolve(match[1]);
12426
+ }
12427
+ }
12428
+ };
12429
+ });
12430
+ }
12431
+ catch (error) {
12432
+ return null;
12433
+ }
12434
+ });
12435
+ }
12436
+ /**
12437
+ * Get IP address using public IP service (fallback)
12438
+ */
12439
+ function getIPFromPublicService() {
12440
+ return __awaiter(this, void 0, void 0, function* () {
12441
+ try {
12442
+ const response = yield fetch('https://api.ipify.org?format=json', {
12443
+ method: 'GET'
12444
+ });
12445
+ if (response.ok) {
12446
+ const data = yield response.json();
12447
+ return data.ip;
12448
+ }
12449
+ }
12450
+ catch (error) {
12451
+ // Try alternative service
12452
+ try {
12453
+ const response = yield fetch('https://httpbin.org/ip', {
12454
+ method: 'GET'
12455
+ });
12456
+ if (response.ok) {
12457
+ const data = yield response.json();
12458
+ return data.origin;
12459
+ }
12460
+ }
12461
+ catch (fallbackError) {
12462
+ // Last resort
12463
+ try {
12464
+ const response = yield fetch('https://api.myip.com', {
12465
+ method: 'GET'
12466
+ });
12467
+ if (response.ok) {
12468
+ const data = yield response.json();
12469
+ return data.ip;
12470
+ }
12471
+ }
12472
+ catch (lastError) {
12473
+ return null;
12474
+ }
12475
+ }
12476
+ }
12477
+ return null;
12478
+ });
12479
+ }
12480
+ /**
12481
+ * Get client's public IP address
12482
+ * Tries STUN first (most reliable), then falls back to public services
12483
+ */
12484
+ function getClientIP() {
12485
+ return __awaiter(this, void 0, void 0, function* () {
12486
+ const startTime = Date.now();
12487
+ // Try STUN first (most reliable and privacy-friendly)
12488
+ const stunIP = yield getIPFromSTUN();
12489
+ if (stunIP) {
12490
+ return {
12491
+ ip: stunIP,
12492
+ method: 'stun',
12493
+ timestamp: startTime
12494
+ };
12495
+ }
12496
+ // Fallback to public IP service
12497
+ const publicIP = yield getIPFromPublicService();
12498
+ if (publicIP) {
12499
+ return {
12500
+ ip: publicIP,
12501
+ method: 'public-service',
12502
+ timestamp: startTime
12503
+ };
12504
+ }
12505
+ return null;
12506
+ });
12507
+ }
12508
+ /**
12509
+ * Get IP address with caching to avoid repeated requests
12510
+ */
12511
+ let cachedIP = null;
12512
+ let cacheTimestamp = 0;
12513
+ const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
12514
+ function getCachedIP() {
12515
+ return __awaiter(this, void 0, void 0, function* () {
12516
+ const now = Date.now();
12517
+ // Return cached IP if still valid
12518
+ if (cachedIP && (now - cacheTimestamp) < CACHE_DURATION) {
12519
+ return cachedIP;
12520
+ }
12521
+ // Get fresh IP
12522
+ const ipInfo = yield getClientIP();
12523
+ if (ipInfo) {
12524
+ cachedIP = ipInfo;
12525
+ cacheTimestamp = now;
12526
+ }
12527
+ return ipInfo;
12528
+ });
12529
+ }
12530
+ /**
12531
+ * Clear IP cache (useful for testing or when network changes)
12532
+ */
12533
+ function clearIPCache() {
12534
+ cachedIP = null;
12535
+ cacheTimestamp = 0;
12536
+ }
12537
+
12392
12538
  const MAX_CHUNK_SIZE_BYTES = 1024 * 1024; // 1MB chunk size - more conservative
12393
12539
  function isChunkSizeExceeded(currentChunk, newEvent, sessionId) {
12394
12540
  const nextChunkSize = new TextEncoder().encode(JSON.stringify({
@@ -12492,6 +12638,45 @@ class HumanBehaviorAPI {
12492
12638
  }
12493
12639
  });
12494
12640
  }
12641
+ /**
12642
+ * Send IP address information to the server
12643
+ * This is called after successful initialization
12644
+ */
12645
+ sendIPInfo(sessionId) {
12646
+ return __awaiter(this, void 0, void 0, function* () {
12647
+ try {
12648
+ const ipInfo = yield getCachedIP();
12649
+ if (!ipInfo) {
12650
+ logWarn('No IP address available to send');
12651
+ return;
12652
+ }
12653
+ logDebug('Sending IP info:', ipInfo);
12654
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/ip-info`, {
12655
+ method: 'POST',
12656
+ headers: {
12657
+ 'Content-Type': 'application/json',
12658
+ 'Authorization': `Bearer ${this.apiKey}`
12659
+ },
12660
+ body: JSON.stringify({
12661
+ sessionId: sessionId,
12662
+ clientIP: ipInfo.ip,
12663
+ ipDetectionMethod: ipInfo.method,
12664
+ timestamp: ipInfo.timestamp
12665
+ })
12666
+ });
12667
+ if (!response.ok) {
12668
+ const errorText = yield response.text();
12669
+ logWarn('Failed to send IP info:', response.status, errorText);
12670
+ }
12671
+ else {
12672
+ logDebug('IP info sent successfully');
12673
+ }
12674
+ }
12675
+ catch (error) {
12676
+ logWarn('Error sending IP info:', error);
12677
+ }
12678
+ });
12679
+ }
12495
12680
  sendEvents(events, sessionId, userId) {
12496
12681
  return __awaiter(this, void 0, void 0, function* () {
12497
12682
  // ✅ SIMPLE VALIDATION FOR ALL EVENTS
@@ -12718,7 +12903,7 @@ class HumanBehaviorAPI {
12718
12903
  // This module provides methods to configure rrweb's built-in masking
12719
12904
  // Uses CSS selectors and classes for reliable redaction without event corruption
12720
12905
  // Check if we're in a browser environment
12721
- const isBrowser$1 = typeof window !== 'undefined';
12906
+ const isBrowser$2 = typeof window !== 'undefined';
12722
12907
  class RedactionManager {
12723
12908
  constructor(options) {
12724
12909
  this.redactedText = '[REDACTED]';
@@ -12877,7 +13062,7 @@ class RedactionManager {
12877
13062
  * Check if a DOM change should be redacted based on its ID
12878
13063
  */
12879
13064
  shouldRedactDOMChange(changeData) {
12880
- if (!isBrowser$1)
13065
+ if (!isBrowser$2)
12881
13066
  return false;
12882
13067
  try {
12883
13068
  // Check if this change has an ID that we can use to find the element
@@ -13020,7 +13205,7 @@ class RedactionManager {
13020
13205
  * Check if an event is from a field that should be redacted
13021
13206
  */
13022
13207
  isFieldSelected(eventData) {
13023
- if (!isBrowser$1)
13208
+ if (!isBrowser$2)
13024
13209
  return false;
13025
13210
  try {
13026
13211
  // For input events (source 5), we need to determine if this is a sensitive field
@@ -13171,6 +13356,485 @@ class RedactionManager {
13171
13356
  // Export a default instance
13172
13357
  const redactionManager = new RedactionManager();
13173
13358
 
13359
+ /**
13360
+ * Automatic Property Detection for HumanBehavior SDK
13361
+ * Captures device type, location, and initial referrer information
13362
+ */
13363
+ // Check if we're in a browser environment
13364
+ const isBrowser$1 = typeof window !== 'undefined';
13365
+ /**
13366
+ * Detect device type based on user agent and screen size
13367
+ */
13368
+ function detectDeviceType() {
13369
+ if (!isBrowser$1)
13370
+ return 'unknown';
13371
+ const userAgent = navigator.userAgent.toLowerCase();
13372
+ const screenWidth = window.screen.width;
13373
+ const screenHeight = window.screen.height;
13374
+ // Mobile detection
13375
+ if (/mobile|android|iphone|ipad|ipod|blackberry|windows phone/i.test(userAgent)) {
13376
+ if (/ipad/i.test(userAgent) || (screenWidth >= 768 && screenHeight >= 1024)) {
13377
+ return 'tablet';
13378
+ }
13379
+ return 'mobile';
13380
+ }
13381
+ // Desktop detection
13382
+ if (/windows|macintosh|linux/i.test(userAgent)) {
13383
+ return 'desktop';
13384
+ }
13385
+ return 'unknown';
13386
+ }
13387
+ /**
13388
+ * Extract browser information from user agent
13389
+ */
13390
+ function detectBrowser() {
13391
+ if (!isBrowser$1)
13392
+ return { browser: 'unknown', browser_version: 'unknown' };
13393
+ const userAgent = navigator.userAgent;
13394
+ // Chrome
13395
+ if (/chrome/i.test(userAgent) && !/edge/i.test(userAgent)) {
13396
+ const match = userAgent.match(/chrome\/(\d+)/i);
13397
+ return {
13398
+ browser: 'chrome',
13399
+ browser_version: match ? match[1] : 'unknown'
13400
+ };
13401
+ }
13402
+ // Firefox
13403
+ if (/firefox/i.test(userAgent)) {
13404
+ const match = userAgent.match(/firefox\/(\d+)/i);
13405
+ return {
13406
+ browser: 'firefox',
13407
+ browser_version: match ? match[1] : 'unknown'
13408
+ };
13409
+ }
13410
+ // Safari
13411
+ if (/safari/i.test(userAgent) && !/chrome/i.test(userAgent)) {
13412
+ const match = userAgent.match(/version\/(\d+)/i);
13413
+ return {
13414
+ browser: 'safari',
13415
+ browser_version: match ? match[1] : 'unknown'
13416
+ };
13417
+ }
13418
+ // Edge
13419
+ if (/edge/i.test(userAgent)) {
13420
+ const match = userAgent.match(/edge\/(\d+)/i);
13421
+ return {
13422
+ browser: 'edge',
13423
+ browser_version: match ? match[1] : 'unknown'
13424
+ };
13425
+ }
13426
+ // Internet Explorer
13427
+ if (/msie|trident/i.test(userAgent)) {
13428
+ const match = userAgent.match(/msie (\d+)/i) || userAgent.match(/rv:(\d+)/i);
13429
+ return {
13430
+ browser: 'ie',
13431
+ browser_version: match ? match[1] : 'unknown'
13432
+ };
13433
+ }
13434
+ return { browser: 'unknown', browser_version: 'unknown' };
13435
+ }
13436
+ /**
13437
+ * Extract operating system information from user agent
13438
+ */
13439
+ function detectOS() {
13440
+ if (!isBrowser$1)
13441
+ return { os: 'unknown', os_version: 'unknown' };
13442
+ const userAgent = navigator.userAgent;
13443
+ // Windows
13444
+ if (/windows/i.test(userAgent)) {
13445
+ const match = userAgent.match(/windows nt (\d+\.\d+)/i);
13446
+ let version = 'unknown';
13447
+ if (match) {
13448
+ const versionNum = parseFloat(match[1]);
13449
+ if (versionNum === 10.0)
13450
+ version = '10';
13451
+ else if (versionNum === 6.3)
13452
+ version = '8.1';
13453
+ else if (versionNum === 6.2)
13454
+ version = '8';
13455
+ else if (versionNum === 6.1)
13456
+ version = '7';
13457
+ else
13458
+ version = match[1];
13459
+ }
13460
+ return { os: 'windows', os_version: version };
13461
+ }
13462
+ // macOS
13463
+ if (/macintosh|mac os x/i.test(userAgent)) {
13464
+ const match = userAgent.match(/mac os x (\d+[._]\d+)/i);
13465
+ return {
13466
+ os: 'macos',
13467
+ os_version: match ? match[1].replace('_', '.') : 'unknown'
13468
+ };
13469
+ }
13470
+ // iOS
13471
+ if (/iphone|ipad|ipod/i.test(userAgent)) {
13472
+ const match = userAgent.match(/os (\d+[._]\d+)/i);
13473
+ return {
13474
+ os: 'ios',
13475
+ os_version: match ? match[1].replace('_', '.') : 'unknown'
13476
+ };
13477
+ }
13478
+ // Android
13479
+ if (/android/i.test(userAgent)) {
13480
+ const match = userAgent.match(/android (\d+\.\d+)/i);
13481
+ return {
13482
+ os: 'android',
13483
+ os_version: match ? match[1] : 'unknown'
13484
+ };
13485
+ }
13486
+ // Linux
13487
+ if (/linux/i.test(userAgent)) {
13488
+ return { os: 'linux', os_version: 'unknown' };
13489
+ }
13490
+ return { os: 'unknown', os_version: 'unknown' };
13491
+ }
13492
+ /**
13493
+ * Extract UTM parameters from URL
13494
+ */
13495
+ function extractUTMParams(url) {
13496
+ const urlObj = new URL(url);
13497
+ const utmParams = {};
13498
+ const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
13499
+ utmKeys.forEach(key => {
13500
+ const value = urlObj.searchParams.get(key);
13501
+ if (value) {
13502
+ utmParams[key] = value;
13503
+ }
13504
+ });
13505
+ return utmParams;
13506
+ }
13507
+ /**
13508
+ * Extract domain from URL
13509
+ */
13510
+ function extractDomain(url) {
13511
+ try {
13512
+ const urlObj = new URL(url);
13513
+ return urlObj.hostname;
13514
+ }
13515
+ catch (_a) {
13516
+ return '';
13517
+ }
13518
+ }
13519
+ /**
13520
+ * Get device information
13521
+ */
13522
+ function getDeviceInfo() {
13523
+ if (!isBrowser$1) {
13524
+ return {
13525
+ device_type: 'unknown',
13526
+ browser: 'unknown',
13527
+ browser_version: 'unknown',
13528
+ os: 'unknown',
13529
+ os_version: 'unknown',
13530
+ screen_resolution: 'unknown',
13531
+ viewport_size: 'unknown',
13532
+ color_depth: 0,
13533
+ timezone: 'unknown',
13534
+ language: 'unknown',
13535
+ languages: []
13536
+ };
13537
+ }
13538
+ const { browser, browser_version } = detectBrowser();
13539
+ const { os, os_version } = detectOS();
13540
+ return {
13541
+ device_type: detectDeviceType(),
13542
+ browser,
13543
+ browser_version,
13544
+ os,
13545
+ os_version,
13546
+ screen_resolution: `${window.screen.width}x${window.screen.height}`,
13547
+ viewport_size: `${window.innerWidth}x${window.innerHeight}`,
13548
+ color_depth: window.screen.colorDepth,
13549
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
13550
+ language: navigator.language,
13551
+ languages: [...(navigator.languages || [navigator.language])],
13552
+ raw_user_agent: navigator.userAgent
13553
+ };
13554
+ }
13555
+ /**
13556
+ * Get location information
13557
+ */
13558
+ function getLocationInfo() {
13559
+ if (!isBrowser$1) {
13560
+ return {
13561
+ current_url: '',
13562
+ pathname: '',
13563
+ search: '',
13564
+ hash: '',
13565
+ title: '',
13566
+ referrer: '',
13567
+ referrer_domain: '',
13568
+ initial_referrer: '',
13569
+ initial_referrer_domain: ''
13570
+ };
13571
+ }
13572
+ const currentUrl = window.location.href;
13573
+ const referrer = document.referrer;
13574
+ const utmParams = extractUTMParams(currentUrl);
13575
+ return Object.assign({ current_url: currentUrl, pathname: window.location.pathname, search: window.location.search, hash: window.location.hash, title: document.title, referrer, referrer_domain: extractDomain(referrer), initial_referrer: referrer, initial_referrer_domain: extractDomain(referrer), initial_host: window.location.hostname }, utmParams);
13576
+ }
13577
+ /**
13578
+ * Get all automatic properties
13579
+ */
13580
+ function getAutomaticProperties() {
13581
+ return Object.assign(Object.assign({}, getDeviceInfo()), getLocationInfo());
13582
+ }
13583
+ /**
13584
+ * Get initial properties that should be captured once per session
13585
+ */
13586
+ function getInitialProperties() {
13587
+ if (!isBrowser$1)
13588
+ return {};
13589
+ const locationInfo = getLocationInfo();
13590
+ return {
13591
+ initial_referrer: locationInfo.initial_referrer,
13592
+ initial_referrer_domain: locationInfo.initial_referrer_domain,
13593
+ initial_url: locationInfo.current_url,
13594
+ initial_pathname: locationInfo.pathname,
13595
+ initial_utm_source: locationInfo.utm_source,
13596
+ initial_utm_medium: locationInfo.utm_medium,
13597
+ initial_utm_campaign: locationInfo.utm_campaign,
13598
+ initial_utm_term: locationInfo.utm_term,
13599
+ initial_utm_content: locationInfo.utm_content
13600
+ };
13601
+ }
13602
+ /**
13603
+ * Get current page properties (changes with navigation)
13604
+ */
13605
+ function getCurrentPageProperties() {
13606
+ if (!isBrowser$1)
13607
+ return {};
13608
+ const locationInfo = getLocationInfo();
13609
+ return {
13610
+ current_url: locationInfo.current_url,
13611
+ pathname: locationInfo.pathname,
13612
+ search: locationInfo.search,
13613
+ hash: locationInfo.hash,
13614
+ title: locationInfo.title,
13615
+ referrer: locationInfo.referrer,
13616
+ referrer_domain: locationInfo.referrer_domain,
13617
+ utm_source: locationInfo.utm_source,
13618
+ utm_medium: locationInfo.utm_medium,
13619
+ utm_campaign: locationInfo.utm_campaign,
13620
+ utm_term: locationInfo.utm_term,
13621
+ utm_content: locationInfo.utm_content
13622
+ };
13623
+ }
13624
+
13625
+ /**
13626
+ * Property Manager for HumanBehavior SDK
13627
+ * Handles automatic properties, session properties, and user properties
13628
+ */
13629
+ class PropertyManager {
13630
+ constructor(config = {}) {
13631
+ this.sessionProperties = {};
13632
+ this.userProperties = {};
13633
+ this.initialProperties = {};
13634
+ this.isInitialized = false;
13635
+ this.config = Object.assign({ enableAutomaticProperties: true, enableSessionProperties: true, enableUserProperties: true, propertyDenylist: [] }, config);
13636
+ this.automaticProperties = getAutomaticProperties();
13637
+ this.initialize();
13638
+ }
13639
+ /**
13640
+ * Initialize the property manager
13641
+ */
13642
+ initialize() {
13643
+ if (this.isInitialized)
13644
+ return;
13645
+ // Capture initial properties once
13646
+ this.initialProperties = getInitialProperties();
13647
+ // Load session properties from sessionStorage
13648
+ this.loadSessionProperties();
13649
+ this.isInitialized = true;
13650
+ }
13651
+ /**
13652
+ * Get all properties for an event
13653
+ */
13654
+ getEventProperties(eventProperties = {}) {
13655
+ const properties = Object.assign({}, eventProperties);
13656
+ // Add automatic properties
13657
+ if (this.config.enableAutomaticProperties) {
13658
+ Object.assign(properties, this.getAutomaticProperties());
13659
+ }
13660
+ // Add session properties
13661
+ if (this.config.enableSessionProperties) {
13662
+ Object.assign(properties, this.sessionProperties);
13663
+ }
13664
+ // Add user properties
13665
+ if (this.config.enableUserProperties) {
13666
+ Object.assign(properties, this.userProperties);
13667
+ }
13668
+ // Add initial properties (only once per session)
13669
+ if (!this.sessionProperties['$initial_properties_captured']) {
13670
+ Object.assign(properties, this.initialProperties);
13671
+ this.setSessionProperty('$initial_properties_captured', true);
13672
+ }
13673
+ // Apply denylist
13674
+ this.applyDenylist(properties);
13675
+ return properties;
13676
+ }
13677
+ /**
13678
+ * Get automatic properties
13679
+ */
13680
+ getAutomaticProperties() {
13681
+ return Object.assign(Object.assign({}, this.automaticProperties), getCurrentPageProperties() // Always get fresh page properties
13682
+ );
13683
+ }
13684
+ /**
13685
+ * Get automatic properties with GeoIP data merged in
13686
+ */
13687
+ getAutomaticPropertiesWithGeoIP(geoIPProperties = {}) {
13688
+ return Object.assign(Object.assign(Object.assign({}, this.automaticProperties), getCurrentPageProperties()), geoIPProperties);
13689
+ }
13690
+ /**
13691
+ * Set a session property
13692
+ */
13693
+ setSessionProperty(key, value) {
13694
+ this.sessionProperties[key] = value;
13695
+ this.saveSessionProperties();
13696
+ }
13697
+ /**
13698
+ * Set multiple session properties
13699
+ */
13700
+ setSessionProperties(properties) {
13701
+ Object.assign(this.sessionProperties, properties);
13702
+ this.saveSessionProperties();
13703
+ }
13704
+ /**
13705
+ * Get a session property
13706
+ */
13707
+ getSessionProperty(key) {
13708
+ return this.sessionProperties[key];
13709
+ }
13710
+ /**
13711
+ * Remove a session property
13712
+ */
13713
+ removeSessionProperty(key) {
13714
+ delete this.sessionProperties[key];
13715
+ this.saveSessionProperties();
13716
+ }
13717
+ /**
13718
+ * Set a user property
13719
+ */
13720
+ setUserProperty(key, value) {
13721
+ this.userProperties[key] = value;
13722
+ }
13723
+ /**
13724
+ * Set multiple user properties
13725
+ */
13726
+ setUserProperties(properties) {
13727
+ Object.assign(this.userProperties, properties);
13728
+ }
13729
+ /**
13730
+ * Get a user property
13731
+ */
13732
+ getUserProperty(key) {
13733
+ return this.userProperties[key];
13734
+ }
13735
+ /**
13736
+ * Remove a user property
13737
+ */
13738
+ removeUserProperty(key) {
13739
+ delete this.userProperties[key];
13740
+ }
13741
+ /**
13742
+ * Set a property only if it hasn't been set before
13743
+ */
13744
+ setOnce(key, value, scope = 'user') {
13745
+ if (scope === 'session') {
13746
+ if (!(key in this.sessionProperties)) {
13747
+ this.setSessionProperty(key, value);
13748
+ }
13749
+ }
13750
+ else {
13751
+ if (!(key in this.userProperties)) {
13752
+ this.setUserProperty(key, value);
13753
+ }
13754
+ }
13755
+ }
13756
+ /**
13757
+ * Clear all session properties
13758
+ */
13759
+ clearSessionProperties() {
13760
+ this.sessionProperties = {};
13761
+ this.saveSessionProperties();
13762
+ }
13763
+ /**
13764
+ * Clear all user properties
13765
+ */
13766
+ clearUserProperties() {
13767
+ this.userProperties = {};
13768
+ }
13769
+ /**
13770
+ * Reset all properties
13771
+ */
13772
+ reset() {
13773
+ this.clearSessionProperties();
13774
+ this.clearUserProperties();
13775
+ this.initialProperties = {};
13776
+ this.isInitialized = false;
13777
+ this.initialize();
13778
+ }
13779
+ /**
13780
+ * Load session properties from sessionStorage
13781
+ */
13782
+ loadSessionProperties() {
13783
+ if (typeof sessionStorage === 'undefined')
13784
+ return;
13785
+ try {
13786
+ const stored = sessionStorage.getItem('hb_session_properties');
13787
+ if (stored) {
13788
+ this.sessionProperties = JSON.parse(stored);
13789
+ }
13790
+ }
13791
+ catch (error) {
13792
+ console.warn('Failed to load session properties:', error);
13793
+ }
13794
+ }
13795
+ /**
13796
+ * Save session properties to sessionStorage
13797
+ */
13798
+ saveSessionProperties() {
13799
+ if (typeof sessionStorage === 'undefined')
13800
+ return;
13801
+ try {
13802
+ sessionStorage.setItem('hb_session_properties', JSON.stringify(this.sessionProperties));
13803
+ }
13804
+ catch (error) {
13805
+ console.warn('Failed to save session properties:', error);
13806
+ }
13807
+ }
13808
+ /**
13809
+ * Apply property denylist
13810
+ */
13811
+ applyDenylist(properties) {
13812
+ if (!this.config.propertyDenylist || this.config.propertyDenylist.length === 0) {
13813
+ return;
13814
+ }
13815
+ this.config.propertyDenylist.forEach(deniedKey => {
13816
+ delete properties[deniedKey];
13817
+ });
13818
+ }
13819
+ /**
13820
+ * Update automatic properties (call when page changes)
13821
+ */
13822
+ updateAutomaticProperties() {
13823
+ this.automaticProperties = getAutomaticProperties();
13824
+ }
13825
+ /**
13826
+ * Get all properties for debugging
13827
+ */
13828
+ getAllProperties() {
13829
+ return {
13830
+ automatic: this.getAutomaticProperties(),
13831
+ session: Object.assign({}, this.sessionProperties),
13832
+ user: Object.assign({}, this.userProperties),
13833
+ initial: Object.assign({}, this.initialProperties)
13834
+ };
13835
+ }
13836
+ }
13837
+
13174
13838
  // Check if we're in a browser environment
13175
13839
  const isBrowser = typeof window !== 'undefined';
13176
13840
  class HumanBehaviorTracker {
@@ -13182,7 +13846,7 @@ class HumanBehaviorTracker {
13182
13846
  var _a;
13183
13847
  // ✅ SUPPRESS COMMON RRWEB ERRORS FOR CLEAN CONSOLE
13184
13848
  if (isBrowser && (options === null || options === void 0 ? void 0 : options.suppressConsoleErrors) !== false) {
13185
- // Suppress canvas security errors
13849
+ // Suppress canvas security errors and network errors
13186
13850
  const originalConsoleError = console.error;
13187
13851
  console.error = (...args) => {
13188
13852
  const message = args.join(' ');
@@ -13193,8 +13857,14 @@ class HumanBehaviorTracker {
13193
13857
  message.includes('CORS') ||
13194
13858
  message.includes('Access-Control-Allow-Origin') ||
13195
13859
  message.includes('Failed to load resource') ||
13196
- message.includes('net::ERR_BLOCKED_BY_CLIENT')) {
13197
- // Silently suppress these common rrweb errors
13860
+ message.includes('net::ERR_BLOCKED_BY_CLIENT') ||
13861
+ message.includes('NetworkError when attempting to fetch resource') ||
13862
+ message.includes('Failed to fetch') ||
13863
+ message.includes('TypeError: NetworkError') ||
13864
+ message.includes('HumanBehavior ERROR') ||
13865
+ message.includes('Failed to track custom event') ||
13866
+ message.includes('Error sending custom event')) {
13867
+ // Silently suppress these common errors
13198
13868
  return;
13199
13869
  }
13200
13870
  originalConsoleError.apply(console, args);
@@ -13208,8 +13878,12 @@ class HumanBehaviorTracker {
13208
13878
  message.includes('CORS') ||
13209
13879
  message.includes('Access-Control-Allow-Origin') ||
13210
13880
  message.includes('Failed to load resource') ||
13211
- message.includes('net::ERR_BLOCKED_BY_CLIENT')) {
13212
- // Silently suppress these common rrweb warnings
13881
+ message.includes('net::ERR_BLOCKED_BY_CLIENT') ||
13882
+ message.includes('NetworkError when attempting to fetch resource') ||
13883
+ message.includes('Failed to fetch') ||
13884
+ message.includes('Custom event network error') ||
13885
+ message.includes('Request blocked by ad blocker')) {
13886
+ // Silently suppress these common warnings
13213
13887
  return;
13214
13888
  }
13215
13889
  originalConsoleWarn.apply(console, args);
@@ -13221,7 +13895,9 @@ class HumanBehaviorTracker {
13221
13895
  message.includes('Tainted canvases') ||
13222
13896
  message.includes('toDataURL') ||
13223
13897
  message.includes('Cross-Origin') ||
13224
- message.includes('CORS')) {
13898
+ message.includes('CORS') ||
13899
+ message.includes('NetworkError') ||
13900
+ message.includes('Failed to fetch')) {
13225
13901
  event.preventDefault();
13226
13902
  return false;
13227
13903
  }
@@ -13237,7 +13913,10 @@ class HumanBehaviorTracker {
13237
13913
  this.configureLogging({ level: options.logLevel });
13238
13914
  }
13239
13915
  // Create new tracker instance
13240
- const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl);
13916
+ const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl, {
13917
+ enableAutomaticProperties: options === null || options === void 0 ? void 0 : options.enableAutomaticProperties,
13918
+ propertyDenylist: options === null || options === void 0 ? void 0 : options.propertyDenylist
13919
+ });
13241
13920
  // Store canvas recording preference
13242
13921
  tracker.recordCanvas = (_a = options === null || options === void 0 ? void 0 : options.recordCanvas) !== null && _a !== void 0 ? _a : false;
13243
13922
  // Set redacted fields if specified
@@ -13254,7 +13933,7 @@ class HumanBehaviorTracker {
13254
13933
  tracker.start();
13255
13934
  return tracker;
13256
13935
  }
13257
- constructor(apiKey, ingestionUrl) {
13936
+ constructor(apiKey, ingestionUrl, options) {
13258
13937
  this.eventIngestionQueue = [];
13259
13938
  this.userProperties = {};
13260
13939
  this.isProcessing = false;
@@ -13292,6 +13971,11 @@ class HumanBehaviorTracker {
13292
13971
  });
13293
13972
  this.apiKey = apiKey;
13294
13973
  this.redactionManager = new RedactionManager();
13974
+ // Initialize property manager
13975
+ this.propertyManager = new PropertyManager({
13976
+ enableAutomaticProperties: (options === null || options === void 0 ? void 0 : options.enableAutomaticProperties) !== false,
13977
+ propertyDenylist: (options === null || options === void 0 ? void 0 : options.propertyDenylist) || []
13978
+ });
13295
13979
  // Handle session restoration with improved continuity
13296
13980
  if (isBrowser) {
13297
13981
  const existingSessionId = localStorage.getItem(`human_behavior_session_id_${this.apiKey}`);
@@ -13330,7 +14014,28 @@ class HumanBehaviorTracker {
13330
14014
  try {
13331
14015
  const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
13332
14016
  logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
13333
- const { sessionId, endUserId } = yield this.api.init(this.sessionId, userId);
14017
+ // Get automatic properties for init
14018
+ const automaticProperties = this.propertyManager.getAutomaticProperties();
14019
+ // Create a custom init request with automatic properties
14020
+ const initResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/init`, {
14021
+ method: 'POST',
14022
+ headers: {
14023
+ 'Content-Type': 'application/json',
14024
+ 'Authorization': `Bearer ${this.apiKey}`,
14025
+ 'Referer': document.referrer || ''
14026
+ },
14027
+ body: JSON.stringify({
14028
+ sessionId: this.sessionId,
14029
+ endUserId: userId,
14030
+ entryURL: window.location.href,
14031
+ referrer: document.referrer,
14032
+ automaticProperties: automaticProperties
14033
+ })
14034
+ });
14035
+ if (!initResponse.ok) {
14036
+ throw new Error(`Failed to initialize: ${initResponse.statusText}`);
14037
+ }
14038
+ const { sessionId, endUserId } = yield initResponse.json();
13334
14039
  // Check if server returned a different session ID (for session continuity)
13335
14040
  if (sessionId !== this.sessionId) {
13336
14041
  logDebug(`Server returned different sessionId: ${sessionId} (client had: ${this.sessionId})`);
@@ -13342,6 +14047,10 @@ class HumanBehaviorTracker {
13342
14047
  }
13343
14048
  this.endUserId = endUserId;
13344
14049
  this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
14050
+ // Send IP information after successful initialization
14051
+ this.api.sendIPInfo(this.sessionId).catch(error => {
14052
+ logWarn('Failed to send IP info:', error);
14053
+ });
13345
14054
  // Only setup browser-specific handlers when in browser environment
13346
14055
  if (isBrowser) {
13347
14056
  this.setupPageUnloadHandler();
@@ -13462,6 +14171,8 @@ class HumanBehaviorTracker {
13462
14171
  return __awaiter(this, void 0, void 0, function* () {
13463
14172
  if (!this.initialized)
13464
14173
  return;
14174
+ // Update automatic properties for new page
14175
+ this.propertyManager.updateAutomaticProperties();
13465
14176
  try {
13466
14177
  const pageViewData = {
13467
14178
  url: url || window.location.href,
@@ -13471,11 +14182,13 @@ class HumanBehaviorTracker {
13471
14182
  referrer: document.referrer,
13472
14183
  timestamp: new Date().toISOString()
13473
14184
  };
14185
+ // Get enhanced properties with automatic properties
14186
+ const enhancedProperties = this.propertyManager.getEventProperties(pageViewData);
13474
14187
  // Add pageview event to the main event stream
13475
14188
  yield this.addEvent({
13476
14189
  type: 5, // Custom event type
13477
14190
  data: {
13478
- payload: Object.assign({ eventType: 'pageview' }, pageViewData)
14191
+ payload: Object.assign({ eventType: 'pageview' }, enhancedProperties)
13479
14192
  },
13480
14193
  timestamp: Date.now()
13481
14194
  });
@@ -13491,10 +14204,12 @@ class HumanBehaviorTracker {
13491
14204
  var _a, _b, _c, _d, _e;
13492
14205
  if (!this.initialized)
13493
14206
  return;
14207
+ // Get enhanced properties with automatic properties
14208
+ const enhancedProperties = this.propertyManager.getEventProperties(properties);
13494
14209
  try {
13495
14210
  // Send custom event directly to the API
13496
- yield this.api.sendCustomEvent(this.sessionId, eventName, properties);
13497
- logDebug(`Custom event tracked: ${eventName}`, properties);
14211
+ yield this.api.sendCustomEvent(this.sessionId, eventName, enhancedProperties);
14212
+ logDebug(`Custom event tracked: ${eventName}`, enhancedProperties);
13498
14213
  }
13499
14214
  catch (error) {
13500
14215
  logError('Failed to track custom event:', error);
@@ -13514,7 +14229,7 @@ class HumanBehaviorTracker {
13514
14229
  try {
13515
14230
  const customEventData = {
13516
14231
  eventName: eventName,
13517
- properties: properties || {},
14232
+ properties: enhancedProperties || {},
13518
14233
  timestamp: new Date().toISOString(),
13519
14234
  url: window.location.href,
13520
14235
  pathname: window.location.pathname
@@ -13814,8 +14529,25 @@ class HumanBehaviorTracker {
13814
14529
  // Store user properties
13815
14530
  this.userProperties = userProperties;
13816
14531
  logDebug('Identifying user:', { userProperties, originalEndUserId, sessionId: this.sessionId });
13817
- // Send user data with the original endUserId
13818
- yield this.api.sendUserData(originalEndUserId, userProperties, this.sessionId);
14532
+ // Get automatic properties and send with user data
14533
+ const automaticProperties = this.propertyManager.getAutomaticProperties();
14534
+ // Create a custom user request with automatic properties
14535
+ const userResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/user`, {
14536
+ method: 'POST',
14537
+ headers: {
14538
+ 'Content-Type': 'application/json',
14539
+ 'Authorization': `Bearer ${this.apiKey}`
14540
+ },
14541
+ body: JSON.stringify({
14542
+ userId: originalEndUserId,
14543
+ userAttributes: userProperties,
14544
+ sessionId: this.sessionId,
14545
+ automaticProperties: automaticProperties
14546
+ })
14547
+ });
14548
+ if (!userResponse.ok) {
14549
+ throw new Error(`Failed to identify user: ${userResponse.statusText}`);
14550
+ }
13819
14551
  // Don't update endUserId - keep it as the original UUID
13820
14552
  return originalEndUserId || '';
13821
14553
  });
@@ -13863,7 +14595,7 @@ class HumanBehaviorTracker {
13863
14595
  collectFonts: false, // Disable font collection to reduce errors
13864
14596
  inlineStylesheet: true, // Keep styles for proper session replay
13865
14597
  recordCrossOriginIframes: false, // Prevent cross-origin iframe errors
13866
- // ✅ CANVAS RECORDING - PostHog-style protection against overwhelm
14598
+ // ✅ CANVAS RECORDING - protection against overwhelm
13867
14599
  recordCanvas: this.recordCanvas, // Opt-in only
13868
14600
  sampling: this.recordCanvas ? { canvas: 4 } : undefined, // 4 FPS throttle
13869
14601
  dataURLOptions: this.recordCanvas ? {
@@ -14287,6 +15019,79 @@ class HumanBehaviorTracker {
14287
15019
  initialized: this.initialized
14288
15020
  };
14289
15021
  }
15022
+ // ===== PROPERTY MANAGEMENT METHODS =====
15023
+ /**
15024
+ * Set a session property that will be included in all events for this session
15025
+ */
15026
+ setSessionProperty(key, value) {
15027
+ this.propertyManager.setSessionProperty(key, value);
15028
+ }
15029
+ /**
15030
+ * Set multiple session properties
15031
+ */
15032
+ setSessionProperties(properties) {
15033
+ this.propertyManager.setSessionProperties(properties);
15034
+ }
15035
+ /**
15036
+ * Get a session property
15037
+ */
15038
+ getSessionProperty(key) {
15039
+ return this.propertyManager.getSessionProperty(key);
15040
+ }
15041
+ /**
15042
+ * Remove a session property
15043
+ */
15044
+ removeSessionProperty(key) {
15045
+ this.propertyManager.removeSessionProperty(key);
15046
+ }
15047
+ /**
15048
+ * Set a user property that will be included in all events
15049
+ */
15050
+ setUserProperty(key, value) {
15051
+ this.propertyManager.setUserProperty(key, value);
15052
+ }
15053
+ /**
15054
+ * Set multiple user properties
15055
+ */
15056
+ setUserProperties(properties) {
15057
+ this.propertyManager.setUserProperties(properties);
15058
+ }
15059
+ /**
15060
+ * Get a user property
15061
+ */
15062
+ getUserProperty(key) {
15063
+ return this.propertyManager.getUserProperty(key);
15064
+ }
15065
+ /**
15066
+ * Remove a user property
15067
+ */
15068
+ removeUserProperty(key) {
15069
+ this.propertyManager.removeUserProperty(key);
15070
+ }
15071
+ /**
15072
+ * Set a property only if it hasn't been set before
15073
+ */
15074
+ setOnce(key, value, scope = 'user') {
15075
+ this.propertyManager.setOnce(key, value, scope);
15076
+ }
15077
+ /**
15078
+ * Clear all session properties
15079
+ */
15080
+ clearSessionProperties() {
15081
+ this.propertyManager.clearSessionProperties();
15082
+ }
15083
+ /**
15084
+ * Clear all user properties
15085
+ */
15086
+ clearUserProperties() {
15087
+ this.propertyManager.clearUserProperties();
15088
+ }
15089
+ /**
15090
+ * Get all properties for debugging
15091
+ */
15092
+ getAllProperties() {
15093
+ return this.propertyManager.getAllProperties();
15094
+ }
14290
15095
  }
14291
15096
  // Only expose to window object in browser environments
14292
15097
  if (isBrowser) {
@@ -14305,5 +15110,5 @@ if (typeof window !== 'undefined') {
14305
15110
  window.HumanBehaviorTracker = HumanBehaviorTracker;
14306
15111
  }
14307
15112
 
14308
- export { HumanBehaviorAPI, HumanBehaviorTracker, LogLevel, MAX_CHUNK_SIZE_BYTES, RedactionManager, isChunkSizeExceeded, logDebug, logError, logInfo, logWarn, logger, redactionManager, splitLargeEvent, validateSingleEventSize };
15113
+ export { HumanBehaviorAPI, HumanBehaviorTracker, LogLevel, MAX_CHUNK_SIZE_BYTES, PropertyManager, RedactionManager, clearIPCache, getAutomaticProperties, getCachedIP, getClientIP, getCurrentPageProperties, getDeviceInfo, getInitialProperties, getLocationInfo, isChunkSizeExceeded, logDebug, logError, logInfo, logWarn, logger, redactionManager, splitLargeEvent, validateSingleEventSize };
14309
15114
  //# sourceMappingURL=index.js.map