humanbehavior-js 0.4.20 → 0.4.21

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 +799 -13
  2. package/dist/cjs/angular/index.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +815 -13
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/cjs/react/index.cjs +800 -14
  6. package/dist/cjs/react/index.cjs.map +1 -1
  7. package/dist/cjs/remix/index.cjs +800 -14
  8. package/dist/cjs/remix/index.cjs.map +1 -1
  9. package/dist/cjs/svelte/index.cjs +799 -13
  10. package/dist/cjs/svelte/index.cjs.map +1 -1
  11. package/dist/cjs/vue/index.cjs +799 -13
  12. package/dist/cjs/vue/index.cjs.map +1 -1
  13. package/dist/esm/angular/index.js +799 -13
  14. package/dist/esm/angular/index.js.map +1 -1
  15. package/dist/esm/index.js +807 -14
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/react/index.js +800 -14
  18. package/dist/esm/react/index.js.map +1 -1
  19. package/dist/esm/remix/index.js +800 -14
  20. package/dist/esm/remix/index.js.map +1 -1
  21. package/dist/esm/svelte/index.js +799 -13
  22. package/dist/esm/svelte/index.js.map +1 -1
  23. package/dist/esm/vue/index.js +799 -13
  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 +175 -11
  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
@@ -12391,6 +12391,152 @@ const logWarn = (message, ...args) => logger.warn(message, ...args);
12391
12391
  const logInfo = (message, ...args) => logger.info(message, ...args);
12392
12392
  const logDebug = (message, ...args) => logger.debug(message, ...args);
12393
12393
 
12394
+ /**
12395
+ * IP Address Detection Utility
12396
+ * Attempts to get the client's public IP address using multiple methods
12397
+ */
12398
+ /**
12399
+ * Get IP address using STUN server (most reliable)
12400
+ */
12401
+ function getIPFromSTUN() {
12402
+ return __awaiter(this, void 0, void 0, function* () {
12403
+ try {
12404
+ const pc = new RTCPeerConnection({
12405
+ iceServers: [
12406
+ { urls: 'stun:stun.l.google.com:19302' },
12407
+ { urls: 'stun:stun1.l.google.com:19302' },
12408
+ { urls: 'stun:stun2.l.google.com:19302' }
12409
+ ]
12410
+ });
12411
+ return new Promise((resolve) => {
12412
+ const timeout = setTimeout(() => {
12413
+ pc.close();
12414
+ resolve(null);
12415
+ }, 5000);
12416
+ pc.createDataChannel('');
12417
+ pc.createOffer()
12418
+ .then(offer => pc.setLocalDescription(offer))
12419
+ .catch(() => resolve(null));
12420
+ pc.onicecandidate = (event) => {
12421
+ if (event.candidate) {
12422
+ const candidate = event.candidate.candidate;
12423
+ const match = candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3})/);
12424
+ if (match) {
12425
+ clearTimeout(timeout);
12426
+ pc.close();
12427
+ resolve(match[1]);
12428
+ }
12429
+ }
12430
+ };
12431
+ });
12432
+ }
12433
+ catch (error) {
12434
+ return null;
12435
+ }
12436
+ });
12437
+ }
12438
+ /**
12439
+ * Get IP address using public IP service (fallback)
12440
+ */
12441
+ function getIPFromPublicService() {
12442
+ return __awaiter(this, void 0, void 0, function* () {
12443
+ try {
12444
+ const response = yield fetch('https://api.ipify.org?format=json', {
12445
+ method: 'GET'
12446
+ });
12447
+ if (response.ok) {
12448
+ const data = yield response.json();
12449
+ return data.ip;
12450
+ }
12451
+ }
12452
+ catch (error) {
12453
+ // Try alternative service
12454
+ try {
12455
+ const response = yield fetch('https://httpbin.org/ip', {
12456
+ method: 'GET'
12457
+ });
12458
+ if (response.ok) {
12459
+ const data = yield response.json();
12460
+ return data.origin;
12461
+ }
12462
+ }
12463
+ catch (fallbackError) {
12464
+ // Last resort
12465
+ try {
12466
+ const response = yield fetch('https://api.myip.com', {
12467
+ method: 'GET'
12468
+ });
12469
+ if (response.ok) {
12470
+ const data = yield response.json();
12471
+ return data.ip;
12472
+ }
12473
+ }
12474
+ catch (lastError) {
12475
+ return null;
12476
+ }
12477
+ }
12478
+ }
12479
+ return null;
12480
+ });
12481
+ }
12482
+ /**
12483
+ * Get client's public IP address
12484
+ * Tries STUN first (most reliable), then falls back to public services
12485
+ */
12486
+ function getClientIP() {
12487
+ return __awaiter(this, void 0, void 0, function* () {
12488
+ const startTime = Date.now();
12489
+ // Try STUN first (most reliable and privacy-friendly)
12490
+ const stunIP = yield getIPFromSTUN();
12491
+ if (stunIP) {
12492
+ return {
12493
+ ip: stunIP,
12494
+ method: 'stun',
12495
+ timestamp: startTime
12496
+ };
12497
+ }
12498
+ // Fallback to public IP service
12499
+ const publicIP = yield getIPFromPublicService();
12500
+ if (publicIP) {
12501
+ return {
12502
+ ip: publicIP,
12503
+ method: 'public-service',
12504
+ timestamp: startTime
12505
+ };
12506
+ }
12507
+ return null;
12508
+ });
12509
+ }
12510
+ /**
12511
+ * Get IP address with caching to avoid repeated requests
12512
+ */
12513
+ let cachedIP = null;
12514
+ let cacheTimestamp = 0;
12515
+ const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
12516
+ function getCachedIP() {
12517
+ return __awaiter(this, void 0, void 0, function* () {
12518
+ const now = Date.now();
12519
+ // Return cached IP if still valid
12520
+ if (cachedIP && (now - cacheTimestamp) < CACHE_DURATION) {
12521
+ return cachedIP;
12522
+ }
12523
+ // Get fresh IP
12524
+ const ipInfo = yield getClientIP();
12525
+ if (ipInfo) {
12526
+ cachedIP = ipInfo;
12527
+ cacheTimestamp = now;
12528
+ }
12529
+ return ipInfo;
12530
+ });
12531
+ }
12532
+ /**
12533
+ * Clear IP cache (useful for testing or when network changes)
12534
+ */
12535
+ function clearIPCache() {
12536
+ cachedIP = null;
12537
+ cacheTimestamp = 0;
12538
+ }
12539
+
12394
12540
  const MAX_CHUNK_SIZE_BYTES = 1024 * 1024; // 1MB chunk size - more conservative
12395
12541
  function isChunkSizeExceeded(currentChunk, newEvent, sessionId) {
12396
12542
  const nextChunkSize = new TextEncoder().encode(JSON.stringify({
@@ -12494,6 +12640,45 @@ class HumanBehaviorAPI {
12494
12640
  }
12495
12641
  });
12496
12642
  }
12643
+ /**
12644
+ * Send IP address information to the server
12645
+ * This is called after successful initialization
12646
+ */
12647
+ sendIPInfo(sessionId) {
12648
+ return __awaiter(this, void 0, void 0, function* () {
12649
+ try {
12650
+ const ipInfo = yield getCachedIP();
12651
+ if (!ipInfo) {
12652
+ logWarn('No IP address available to send');
12653
+ return;
12654
+ }
12655
+ logDebug('Sending IP info:', ipInfo);
12656
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/ip-info`, {
12657
+ method: 'POST',
12658
+ headers: {
12659
+ 'Content-Type': 'application/json',
12660
+ 'Authorization': `Bearer ${this.apiKey}`
12661
+ },
12662
+ body: JSON.stringify({
12663
+ sessionId: sessionId,
12664
+ clientIP: ipInfo.ip,
12665
+ ipDetectionMethod: ipInfo.method,
12666
+ timestamp: ipInfo.timestamp
12667
+ })
12668
+ });
12669
+ if (!response.ok) {
12670
+ const errorText = yield response.text();
12671
+ logWarn('Failed to send IP info:', response.status, errorText);
12672
+ }
12673
+ else {
12674
+ logDebug('IP info sent successfully');
12675
+ }
12676
+ }
12677
+ catch (error) {
12678
+ logWarn('Error sending IP info:', error);
12679
+ }
12680
+ });
12681
+ }
12497
12682
  sendEvents(events, sessionId, userId) {
12498
12683
  return __awaiter(this, void 0, void 0, function* () {
12499
12684
  // ✅ SIMPLE VALIDATION FOR ALL EVENTS
@@ -12720,7 +12905,7 @@ class HumanBehaviorAPI {
12720
12905
  // This module provides methods to configure rrweb's built-in masking
12721
12906
  // Uses CSS selectors and classes for reliable redaction without event corruption
12722
12907
  // Check if we're in a browser environment
12723
- const isBrowser$1 = typeof window !== 'undefined';
12908
+ const isBrowser$2 = typeof window !== 'undefined';
12724
12909
  class RedactionManager {
12725
12910
  constructor(options) {
12726
12911
  this.redactedText = '[REDACTED]';
@@ -12879,7 +13064,7 @@ class RedactionManager {
12879
13064
  * Check if a DOM change should be redacted based on its ID
12880
13065
  */
12881
13066
  shouldRedactDOMChange(changeData) {
12882
- if (!isBrowser$1)
13067
+ if (!isBrowser$2)
12883
13068
  return false;
12884
13069
  try {
12885
13070
  // Check if this change has an ID that we can use to find the element
@@ -13022,7 +13207,7 @@ class RedactionManager {
13022
13207
  * Check if an event is from a field that should be redacted
13023
13208
  */
13024
13209
  isFieldSelected(eventData) {
13025
- if (!isBrowser$1)
13210
+ if (!isBrowser$2)
13026
13211
  return false;
13027
13212
  try {
13028
13213
  // For input events (source 5), we need to determine if this is a sensitive field
@@ -13173,6 +13358,485 @@ class RedactionManager {
13173
13358
  // Export a default instance
13174
13359
  const redactionManager = new RedactionManager();
13175
13360
 
13361
+ /**
13362
+ * Automatic Property Detection for HumanBehavior SDK
13363
+ * Captures device type, location, and initial referrer information
13364
+ */
13365
+ // Check if we're in a browser environment
13366
+ const isBrowser$1 = typeof window !== 'undefined';
13367
+ /**
13368
+ * Detect device type based on user agent and screen size
13369
+ */
13370
+ function detectDeviceType() {
13371
+ if (!isBrowser$1)
13372
+ return 'unknown';
13373
+ const userAgent = navigator.userAgent.toLowerCase();
13374
+ const screenWidth = window.screen.width;
13375
+ const screenHeight = window.screen.height;
13376
+ // Mobile detection
13377
+ if (/mobile|android|iphone|ipad|ipod|blackberry|windows phone/i.test(userAgent)) {
13378
+ if (/ipad/i.test(userAgent) || (screenWidth >= 768 && screenHeight >= 1024)) {
13379
+ return 'tablet';
13380
+ }
13381
+ return 'mobile';
13382
+ }
13383
+ // Desktop detection
13384
+ if (/windows|macintosh|linux/i.test(userAgent)) {
13385
+ return 'desktop';
13386
+ }
13387
+ return 'unknown';
13388
+ }
13389
+ /**
13390
+ * Extract browser information from user agent
13391
+ */
13392
+ function detectBrowser() {
13393
+ if (!isBrowser$1)
13394
+ return { browser: 'unknown', browser_version: 'unknown' };
13395
+ const userAgent = navigator.userAgent;
13396
+ // Chrome
13397
+ if (/chrome/i.test(userAgent) && !/edge/i.test(userAgent)) {
13398
+ const match = userAgent.match(/chrome\/(\d+)/i);
13399
+ return {
13400
+ browser: 'chrome',
13401
+ browser_version: match ? match[1] : 'unknown'
13402
+ };
13403
+ }
13404
+ // Firefox
13405
+ if (/firefox/i.test(userAgent)) {
13406
+ const match = userAgent.match(/firefox\/(\d+)/i);
13407
+ return {
13408
+ browser: 'firefox',
13409
+ browser_version: match ? match[1] : 'unknown'
13410
+ };
13411
+ }
13412
+ // Safari
13413
+ if (/safari/i.test(userAgent) && !/chrome/i.test(userAgent)) {
13414
+ const match = userAgent.match(/version\/(\d+)/i);
13415
+ return {
13416
+ browser: 'safari',
13417
+ browser_version: match ? match[1] : 'unknown'
13418
+ };
13419
+ }
13420
+ // Edge
13421
+ if (/edge/i.test(userAgent)) {
13422
+ const match = userAgent.match(/edge\/(\d+)/i);
13423
+ return {
13424
+ browser: 'edge',
13425
+ browser_version: match ? match[1] : 'unknown'
13426
+ };
13427
+ }
13428
+ // Internet Explorer
13429
+ if (/msie|trident/i.test(userAgent)) {
13430
+ const match = userAgent.match(/msie (\d+)/i) || userAgent.match(/rv:(\d+)/i);
13431
+ return {
13432
+ browser: 'ie',
13433
+ browser_version: match ? match[1] : 'unknown'
13434
+ };
13435
+ }
13436
+ return { browser: 'unknown', browser_version: 'unknown' };
13437
+ }
13438
+ /**
13439
+ * Extract operating system information from user agent
13440
+ */
13441
+ function detectOS() {
13442
+ if (!isBrowser$1)
13443
+ return { os: 'unknown', os_version: 'unknown' };
13444
+ const userAgent = navigator.userAgent;
13445
+ // Windows
13446
+ if (/windows/i.test(userAgent)) {
13447
+ const match = userAgent.match(/windows nt (\d+\.\d+)/i);
13448
+ let version = 'unknown';
13449
+ if (match) {
13450
+ const versionNum = parseFloat(match[1]);
13451
+ if (versionNum === 10.0)
13452
+ version = '10';
13453
+ else if (versionNum === 6.3)
13454
+ version = '8.1';
13455
+ else if (versionNum === 6.2)
13456
+ version = '8';
13457
+ else if (versionNum === 6.1)
13458
+ version = '7';
13459
+ else
13460
+ version = match[1];
13461
+ }
13462
+ return { os: 'windows', os_version: version };
13463
+ }
13464
+ // macOS
13465
+ if (/macintosh|mac os x/i.test(userAgent)) {
13466
+ const match = userAgent.match(/mac os x (\d+[._]\d+)/i);
13467
+ return {
13468
+ os: 'macos',
13469
+ os_version: match ? match[1].replace('_', '.') : 'unknown'
13470
+ };
13471
+ }
13472
+ // iOS
13473
+ if (/iphone|ipad|ipod/i.test(userAgent)) {
13474
+ const match = userAgent.match(/os (\d+[._]\d+)/i);
13475
+ return {
13476
+ os: 'ios',
13477
+ os_version: match ? match[1].replace('_', '.') : 'unknown'
13478
+ };
13479
+ }
13480
+ // Android
13481
+ if (/android/i.test(userAgent)) {
13482
+ const match = userAgent.match(/android (\d+\.\d+)/i);
13483
+ return {
13484
+ os: 'android',
13485
+ os_version: match ? match[1] : 'unknown'
13486
+ };
13487
+ }
13488
+ // Linux
13489
+ if (/linux/i.test(userAgent)) {
13490
+ return { os: 'linux', os_version: 'unknown' };
13491
+ }
13492
+ return { os: 'unknown', os_version: 'unknown' };
13493
+ }
13494
+ /**
13495
+ * Extract UTM parameters from URL
13496
+ */
13497
+ function extractUTMParams(url) {
13498
+ const urlObj = new URL(url);
13499
+ const utmParams = {};
13500
+ const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
13501
+ utmKeys.forEach(key => {
13502
+ const value = urlObj.searchParams.get(key);
13503
+ if (value) {
13504
+ utmParams[key] = value;
13505
+ }
13506
+ });
13507
+ return utmParams;
13508
+ }
13509
+ /**
13510
+ * Extract domain from URL
13511
+ */
13512
+ function extractDomain(url) {
13513
+ try {
13514
+ const urlObj = new URL(url);
13515
+ return urlObj.hostname;
13516
+ }
13517
+ catch (_a) {
13518
+ return '';
13519
+ }
13520
+ }
13521
+ /**
13522
+ * Get device information
13523
+ */
13524
+ function getDeviceInfo() {
13525
+ if (!isBrowser$1) {
13526
+ return {
13527
+ device_type: 'unknown',
13528
+ browser: 'unknown',
13529
+ browser_version: 'unknown',
13530
+ os: 'unknown',
13531
+ os_version: 'unknown',
13532
+ screen_resolution: 'unknown',
13533
+ viewport_size: 'unknown',
13534
+ color_depth: 0,
13535
+ timezone: 'unknown',
13536
+ language: 'unknown',
13537
+ languages: []
13538
+ };
13539
+ }
13540
+ const { browser, browser_version } = detectBrowser();
13541
+ const { os, os_version } = detectOS();
13542
+ return {
13543
+ device_type: detectDeviceType(),
13544
+ browser,
13545
+ browser_version,
13546
+ os,
13547
+ os_version,
13548
+ screen_resolution: `${window.screen.width}x${window.screen.height}`,
13549
+ viewport_size: `${window.innerWidth}x${window.innerHeight}`,
13550
+ color_depth: window.screen.colorDepth,
13551
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
13552
+ language: navigator.language,
13553
+ languages: [...(navigator.languages || [navigator.language])],
13554
+ raw_user_agent: navigator.userAgent
13555
+ };
13556
+ }
13557
+ /**
13558
+ * Get location information
13559
+ */
13560
+ function getLocationInfo() {
13561
+ if (!isBrowser$1) {
13562
+ return {
13563
+ current_url: '',
13564
+ pathname: '',
13565
+ search: '',
13566
+ hash: '',
13567
+ title: '',
13568
+ referrer: '',
13569
+ referrer_domain: '',
13570
+ initial_referrer: '',
13571
+ initial_referrer_domain: ''
13572
+ };
13573
+ }
13574
+ const currentUrl = window.location.href;
13575
+ const referrer = document.referrer;
13576
+ const utmParams = extractUTMParams(currentUrl);
13577
+ 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);
13578
+ }
13579
+ /**
13580
+ * Get all automatic properties
13581
+ */
13582
+ function getAutomaticProperties() {
13583
+ return Object.assign(Object.assign({}, getDeviceInfo()), getLocationInfo());
13584
+ }
13585
+ /**
13586
+ * Get initial properties that should be captured once per session
13587
+ */
13588
+ function getInitialProperties() {
13589
+ if (!isBrowser$1)
13590
+ return {};
13591
+ const locationInfo = getLocationInfo();
13592
+ return {
13593
+ initial_referrer: locationInfo.initial_referrer,
13594
+ initial_referrer_domain: locationInfo.initial_referrer_domain,
13595
+ initial_url: locationInfo.current_url,
13596
+ initial_pathname: locationInfo.pathname,
13597
+ initial_utm_source: locationInfo.utm_source,
13598
+ initial_utm_medium: locationInfo.utm_medium,
13599
+ initial_utm_campaign: locationInfo.utm_campaign,
13600
+ initial_utm_term: locationInfo.utm_term,
13601
+ initial_utm_content: locationInfo.utm_content
13602
+ };
13603
+ }
13604
+ /**
13605
+ * Get current page properties (changes with navigation)
13606
+ */
13607
+ function getCurrentPageProperties() {
13608
+ if (!isBrowser$1)
13609
+ return {};
13610
+ const locationInfo = getLocationInfo();
13611
+ return {
13612
+ current_url: locationInfo.current_url,
13613
+ pathname: locationInfo.pathname,
13614
+ search: locationInfo.search,
13615
+ hash: locationInfo.hash,
13616
+ title: locationInfo.title,
13617
+ referrer: locationInfo.referrer,
13618
+ referrer_domain: locationInfo.referrer_domain,
13619
+ utm_source: locationInfo.utm_source,
13620
+ utm_medium: locationInfo.utm_medium,
13621
+ utm_campaign: locationInfo.utm_campaign,
13622
+ utm_term: locationInfo.utm_term,
13623
+ utm_content: locationInfo.utm_content
13624
+ };
13625
+ }
13626
+
13627
+ /**
13628
+ * Property Manager for HumanBehavior SDK
13629
+ * Handles automatic properties, session properties, and user properties
13630
+ */
13631
+ class PropertyManager {
13632
+ constructor(config = {}) {
13633
+ this.sessionProperties = {};
13634
+ this.userProperties = {};
13635
+ this.initialProperties = {};
13636
+ this.isInitialized = false;
13637
+ this.config = Object.assign({ enableAutomaticProperties: true, enableSessionProperties: true, enableUserProperties: true, propertyDenylist: [] }, config);
13638
+ this.automaticProperties = getAutomaticProperties();
13639
+ this.initialize();
13640
+ }
13641
+ /**
13642
+ * Initialize the property manager
13643
+ */
13644
+ initialize() {
13645
+ if (this.isInitialized)
13646
+ return;
13647
+ // Capture initial properties once
13648
+ this.initialProperties = getInitialProperties();
13649
+ // Load session properties from sessionStorage
13650
+ this.loadSessionProperties();
13651
+ this.isInitialized = true;
13652
+ }
13653
+ /**
13654
+ * Get all properties for an event
13655
+ */
13656
+ getEventProperties(eventProperties = {}) {
13657
+ const properties = Object.assign({}, eventProperties);
13658
+ // Add automatic properties
13659
+ if (this.config.enableAutomaticProperties) {
13660
+ Object.assign(properties, this.getAutomaticProperties());
13661
+ }
13662
+ // Add session properties
13663
+ if (this.config.enableSessionProperties) {
13664
+ Object.assign(properties, this.sessionProperties);
13665
+ }
13666
+ // Add user properties
13667
+ if (this.config.enableUserProperties) {
13668
+ Object.assign(properties, this.userProperties);
13669
+ }
13670
+ // Add initial properties (only once per session)
13671
+ if (!this.sessionProperties['$initial_properties_captured']) {
13672
+ Object.assign(properties, this.initialProperties);
13673
+ this.setSessionProperty('$initial_properties_captured', true);
13674
+ }
13675
+ // Apply denylist
13676
+ this.applyDenylist(properties);
13677
+ return properties;
13678
+ }
13679
+ /**
13680
+ * Get automatic properties
13681
+ */
13682
+ getAutomaticProperties() {
13683
+ return Object.assign(Object.assign({}, this.automaticProperties), getCurrentPageProperties() // Always get fresh page properties
13684
+ );
13685
+ }
13686
+ /**
13687
+ * Get automatic properties with GeoIP data merged in
13688
+ */
13689
+ getAutomaticPropertiesWithGeoIP(geoIPProperties = {}) {
13690
+ return Object.assign(Object.assign(Object.assign({}, this.automaticProperties), getCurrentPageProperties()), geoIPProperties);
13691
+ }
13692
+ /**
13693
+ * Set a session property
13694
+ */
13695
+ setSessionProperty(key, value) {
13696
+ this.sessionProperties[key] = value;
13697
+ this.saveSessionProperties();
13698
+ }
13699
+ /**
13700
+ * Set multiple session properties
13701
+ */
13702
+ setSessionProperties(properties) {
13703
+ Object.assign(this.sessionProperties, properties);
13704
+ this.saveSessionProperties();
13705
+ }
13706
+ /**
13707
+ * Get a session property
13708
+ */
13709
+ getSessionProperty(key) {
13710
+ return this.sessionProperties[key];
13711
+ }
13712
+ /**
13713
+ * Remove a session property
13714
+ */
13715
+ removeSessionProperty(key) {
13716
+ delete this.sessionProperties[key];
13717
+ this.saveSessionProperties();
13718
+ }
13719
+ /**
13720
+ * Set a user property
13721
+ */
13722
+ setUserProperty(key, value) {
13723
+ this.userProperties[key] = value;
13724
+ }
13725
+ /**
13726
+ * Set multiple user properties
13727
+ */
13728
+ setUserProperties(properties) {
13729
+ Object.assign(this.userProperties, properties);
13730
+ }
13731
+ /**
13732
+ * Get a user property
13733
+ */
13734
+ getUserProperty(key) {
13735
+ return this.userProperties[key];
13736
+ }
13737
+ /**
13738
+ * Remove a user property
13739
+ */
13740
+ removeUserProperty(key) {
13741
+ delete this.userProperties[key];
13742
+ }
13743
+ /**
13744
+ * Set a property only if it hasn't been set before
13745
+ */
13746
+ setOnce(key, value, scope = 'user') {
13747
+ if (scope === 'session') {
13748
+ if (!(key in this.sessionProperties)) {
13749
+ this.setSessionProperty(key, value);
13750
+ }
13751
+ }
13752
+ else {
13753
+ if (!(key in this.userProperties)) {
13754
+ this.setUserProperty(key, value);
13755
+ }
13756
+ }
13757
+ }
13758
+ /**
13759
+ * Clear all session properties
13760
+ */
13761
+ clearSessionProperties() {
13762
+ this.sessionProperties = {};
13763
+ this.saveSessionProperties();
13764
+ }
13765
+ /**
13766
+ * Clear all user properties
13767
+ */
13768
+ clearUserProperties() {
13769
+ this.userProperties = {};
13770
+ }
13771
+ /**
13772
+ * Reset all properties
13773
+ */
13774
+ reset() {
13775
+ this.clearSessionProperties();
13776
+ this.clearUserProperties();
13777
+ this.initialProperties = {};
13778
+ this.isInitialized = false;
13779
+ this.initialize();
13780
+ }
13781
+ /**
13782
+ * Load session properties from sessionStorage
13783
+ */
13784
+ loadSessionProperties() {
13785
+ if (typeof sessionStorage === 'undefined')
13786
+ return;
13787
+ try {
13788
+ const stored = sessionStorage.getItem('hb_session_properties');
13789
+ if (stored) {
13790
+ this.sessionProperties = JSON.parse(stored);
13791
+ }
13792
+ }
13793
+ catch (error) {
13794
+ console.warn('Failed to load session properties:', error);
13795
+ }
13796
+ }
13797
+ /**
13798
+ * Save session properties to sessionStorage
13799
+ */
13800
+ saveSessionProperties() {
13801
+ if (typeof sessionStorage === 'undefined')
13802
+ return;
13803
+ try {
13804
+ sessionStorage.setItem('hb_session_properties', JSON.stringify(this.sessionProperties));
13805
+ }
13806
+ catch (error) {
13807
+ console.warn('Failed to save session properties:', error);
13808
+ }
13809
+ }
13810
+ /**
13811
+ * Apply property denylist
13812
+ */
13813
+ applyDenylist(properties) {
13814
+ if (!this.config.propertyDenylist || this.config.propertyDenylist.length === 0) {
13815
+ return;
13816
+ }
13817
+ this.config.propertyDenylist.forEach(deniedKey => {
13818
+ delete properties[deniedKey];
13819
+ });
13820
+ }
13821
+ /**
13822
+ * Update automatic properties (call when page changes)
13823
+ */
13824
+ updateAutomaticProperties() {
13825
+ this.automaticProperties = getAutomaticProperties();
13826
+ }
13827
+ /**
13828
+ * Get all properties for debugging
13829
+ */
13830
+ getAllProperties() {
13831
+ return {
13832
+ automatic: this.getAutomaticProperties(),
13833
+ session: Object.assign({}, this.sessionProperties),
13834
+ user: Object.assign({}, this.userProperties),
13835
+ initial: Object.assign({}, this.initialProperties)
13836
+ };
13837
+ }
13838
+ }
13839
+
13176
13840
  // Check if we're in a browser environment
13177
13841
  const isBrowser = typeof window !== 'undefined';
13178
13842
  class HumanBehaviorTracker {
@@ -13239,7 +13903,10 @@ class HumanBehaviorTracker {
13239
13903
  this.configureLogging({ level: options.logLevel });
13240
13904
  }
13241
13905
  // Create new tracker instance
13242
- const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl);
13906
+ const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl, {
13907
+ enableAutomaticProperties: options === null || options === void 0 ? void 0 : options.enableAutomaticProperties,
13908
+ propertyDenylist: options === null || options === void 0 ? void 0 : options.propertyDenylist
13909
+ });
13243
13910
  // Store canvas recording preference
13244
13911
  tracker.recordCanvas = (_a = options === null || options === void 0 ? void 0 : options.recordCanvas) !== null && _a !== void 0 ? _a : false;
13245
13912
  // Set redacted fields if specified
@@ -13256,7 +13923,7 @@ class HumanBehaviorTracker {
13256
13923
  tracker.start();
13257
13924
  return tracker;
13258
13925
  }
13259
- constructor(apiKey, ingestionUrl) {
13926
+ constructor(apiKey, ingestionUrl, options) {
13260
13927
  this.eventIngestionQueue = [];
13261
13928
  this.userProperties = {};
13262
13929
  this.isProcessing = false;
@@ -13294,6 +13961,11 @@ class HumanBehaviorTracker {
13294
13961
  });
13295
13962
  this.apiKey = apiKey;
13296
13963
  this.redactionManager = new RedactionManager();
13964
+ // Initialize property manager
13965
+ this.propertyManager = new PropertyManager({
13966
+ enableAutomaticProperties: (options === null || options === void 0 ? void 0 : options.enableAutomaticProperties) !== false,
13967
+ propertyDenylist: (options === null || options === void 0 ? void 0 : options.propertyDenylist) || []
13968
+ });
13297
13969
  // Handle session restoration with improved continuity
13298
13970
  if (isBrowser) {
13299
13971
  const existingSessionId = localStorage.getItem(`human_behavior_session_id_${this.apiKey}`);
@@ -13332,7 +14004,28 @@ class HumanBehaviorTracker {
13332
14004
  try {
13333
14005
  const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
13334
14006
  logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
13335
- const { sessionId, endUserId } = yield this.api.init(this.sessionId, userId);
14007
+ // Get automatic properties for init
14008
+ const automaticProperties = this.propertyManager.getAutomaticProperties();
14009
+ // Create a custom init request with automatic properties
14010
+ const initResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/init`, {
14011
+ method: 'POST',
14012
+ headers: {
14013
+ 'Content-Type': 'application/json',
14014
+ 'Authorization': `Bearer ${this.apiKey}`,
14015
+ 'Referer': document.referrer || ''
14016
+ },
14017
+ body: JSON.stringify({
14018
+ sessionId: this.sessionId,
14019
+ endUserId: userId,
14020
+ entryURL: window.location.href,
14021
+ referrer: document.referrer,
14022
+ automaticProperties: automaticProperties
14023
+ })
14024
+ });
14025
+ if (!initResponse.ok) {
14026
+ throw new Error(`Failed to initialize: ${initResponse.statusText}`);
14027
+ }
14028
+ const { sessionId, endUserId } = yield initResponse.json();
13336
14029
  // Check if server returned a different session ID (for session continuity)
13337
14030
  if (sessionId !== this.sessionId) {
13338
14031
  logDebug(`Server returned different sessionId: ${sessionId} (client had: ${this.sessionId})`);
@@ -13344,6 +14037,10 @@ class HumanBehaviorTracker {
13344
14037
  }
13345
14038
  this.endUserId = endUserId;
13346
14039
  this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
14040
+ // Send IP information after successful initialization
14041
+ this.api.sendIPInfo(this.sessionId).catch(error => {
14042
+ logWarn('Failed to send IP info:', error);
14043
+ });
13347
14044
  // Only setup browser-specific handlers when in browser environment
13348
14045
  if (isBrowser) {
13349
14046
  this.setupPageUnloadHandler();
@@ -13464,6 +14161,8 @@ class HumanBehaviorTracker {
13464
14161
  return __awaiter(this, void 0, void 0, function* () {
13465
14162
  if (!this.initialized)
13466
14163
  return;
14164
+ // Update automatic properties for new page
14165
+ this.propertyManager.updateAutomaticProperties();
13467
14166
  try {
13468
14167
  const pageViewData = {
13469
14168
  url: url || window.location.href,
@@ -13473,11 +14172,13 @@ class HumanBehaviorTracker {
13473
14172
  referrer: document.referrer,
13474
14173
  timestamp: new Date().toISOString()
13475
14174
  };
14175
+ // Get enhanced properties with automatic properties
14176
+ const enhancedProperties = this.propertyManager.getEventProperties(pageViewData);
13476
14177
  // Add pageview event to the main event stream
13477
14178
  yield this.addEvent({
13478
14179
  type: 5, // Custom event type
13479
14180
  data: {
13480
- payload: Object.assign({ eventType: 'pageview' }, pageViewData)
14181
+ payload: Object.assign({ eventType: 'pageview' }, enhancedProperties)
13481
14182
  },
13482
14183
  timestamp: Date.now()
13483
14184
  });
@@ -13493,10 +14194,12 @@ class HumanBehaviorTracker {
13493
14194
  var _a, _b, _c, _d, _e;
13494
14195
  if (!this.initialized)
13495
14196
  return;
14197
+ // Get enhanced properties with automatic properties
14198
+ const enhancedProperties = this.propertyManager.getEventProperties(properties);
13496
14199
  try {
13497
14200
  // Send custom event directly to the API
13498
- yield this.api.sendCustomEvent(this.sessionId, eventName, properties);
13499
- logDebug(`Custom event tracked: ${eventName}`, properties);
14201
+ yield this.api.sendCustomEvent(this.sessionId, eventName, enhancedProperties);
14202
+ logDebug(`Custom event tracked: ${eventName}`, enhancedProperties);
13500
14203
  }
13501
14204
  catch (error) {
13502
14205
  logError('Failed to track custom event:', error);
@@ -13516,7 +14219,7 @@ class HumanBehaviorTracker {
13516
14219
  try {
13517
14220
  const customEventData = {
13518
14221
  eventName: eventName,
13519
- properties: properties || {},
14222
+ properties: enhancedProperties || {},
13520
14223
  timestamp: new Date().toISOString(),
13521
14224
  url: window.location.href,
13522
14225
  pathname: window.location.pathname
@@ -13816,8 +14519,25 @@ class HumanBehaviorTracker {
13816
14519
  // Store user properties
13817
14520
  this.userProperties = userProperties;
13818
14521
  logDebug('Identifying user:', { userProperties, originalEndUserId, sessionId: this.sessionId });
13819
- // Send user data with the original endUserId
13820
- yield this.api.sendUserData(originalEndUserId, userProperties, this.sessionId);
14522
+ // Get automatic properties and send with user data
14523
+ const automaticProperties = this.propertyManager.getAutomaticProperties();
14524
+ // Create a custom user request with automatic properties
14525
+ const userResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/user`, {
14526
+ method: 'POST',
14527
+ headers: {
14528
+ 'Content-Type': 'application/json',
14529
+ 'Authorization': `Bearer ${this.apiKey}`
14530
+ },
14531
+ body: JSON.stringify({
14532
+ userId: originalEndUserId,
14533
+ userAttributes: userProperties,
14534
+ sessionId: this.sessionId,
14535
+ automaticProperties: automaticProperties
14536
+ })
14537
+ });
14538
+ if (!userResponse.ok) {
14539
+ throw new Error(`Failed to identify user: ${userResponse.statusText}`);
14540
+ }
13821
14541
  // Don't update endUserId - keep it as the original UUID
13822
14542
  return originalEndUserId || '';
13823
14543
  });
@@ -13865,7 +14585,7 @@ class HumanBehaviorTracker {
13865
14585
  collectFonts: false, // Disable font collection to reduce errors
13866
14586
  inlineStylesheet: true, // Keep styles for proper session replay
13867
14587
  recordCrossOriginIframes: false, // Prevent cross-origin iframe errors
13868
- // ✅ CANVAS RECORDING - PostHog-style protection against overwhelm
14588
+ // ✅ CANVAS RECORDING - protection against overwhelm
13869
14589
  recordCanvas: this.recordCanvas, // Opt-in only
13870
14590
  sampling: this.recordCanvas ? { canvas: 4 } : undefined, // 4 FPS throttle
13871
14591
  dataURLOptions: this.recordCanvas ? {
@@ -14289,6 +15009,79 @@ class HumanBehaviorTracker {
14289
15009
  initialized: this.initialized
14290
15010
  };
14291
15011
  }
15012
+ // ===== PROPERTY MANAGEMENT METHODS =====
15013
+ /**
15014
+ * Set a session property that will be included in all events for this session
15015
+ */
15016
+ setSessionProperty(key, value) {
15017
+ this.propertyManager.setSessionProperty(key, value);
15018
+ }
15019
+ /**
15020
+ * Set multiple session properties
15021
+ */
15022
+ setSessionProperties(properties) {
15023
+ this.propertyManager.setSessionProperties(properties);
15024
+ }
15025
+ /**
15026
+ * Get a session property
15027
+ */
15028
+ getSessionProperty(key) {
15029
+ return this.propertyManager.getSessionProperty(key);
15030
+ }
15031
+ /**
15032
+ * Remove a session property
15033
+ */
15034
+ removeSessionProperty(key) {
15035
+ this.propertyManager.removeSessionProperty(key);
15036
+ }
15037
+ /**
15038
+ * Set a user property that will be included in all events
15039
+ */
15040
+ setUserProperty(key, value) {
15041
+ this.propertyManager.setUserProperty(key, value);
15042
+ }
15043
+ /**
15044
+ * Set multiple user properties
15045
+ */
15046
+ setUserProperties(properties) {
15047
+ this.propertyManager.setUserProperties(properties);
15048
+ }
15049
+ /**
15050
+ * Get a user property
15051
+ */
15052
+ getUserProperty(key) {
15053
+ return this.propertyManager.getUserProperty(key);
15054
+ }
15055
+ /**
15056
+ * Remove a user property
15057
+ */
15058
+ removeUserProperty(key) {
15059
+ this.propertyManager.removeUserProperty(key);
15060
+ }
15061
+ /**
15062
+ * Set a property only if it hasn't been set before
15063
+ */
15064
+ setOnce(key, value, scope = 'user') {
15065
+ this.propertyManager.setOnce(key, value, scope);
15066
+ }
15067
+ /**
15068
+ * Clear all session properties
15069
+ */
15070
+ clearSessionProperties() {
15071
+ this.propertyManager.clearSessionProperties();
15072
+ }
15073
+ /**
15074
+ * Clear all user properties
15075
+ */
15076
+ clearUserProperties() {
15077
+ this.propertyManager.clearUserProperties();
15078
+ }
15079
+ /**
15080
+ * Get all properties for debugging
15081
+ */
15082
+ getAllProperties() {
15083
+ return this.propertyManager.getAllProperties();
15084
+ }
14292
15085
  }
14293
15086
  // Only expose to window object in browser environments
14294
15087
  if (isBrowser) {
@@ -14310,7 +15103,16 @@ if (typeof window !== 'undefined') {
14310
15103
  exports.HumanBehaviorAPI = HumanBehaviorAPI;
14311
15104
  exports.HumanBehaviorTracker = HumanBehaviorTracker;
14312
15105
  exports.MAX_CHUNK_SIZE_BYTES = MAX_CHUNK_SIZE_BYTES;
15106
+ exports.PropertyManager = PropertyManager;
14313
15107
  exports.RedactionManager = RedactionManager;
15108
+ exports.clearIPCache = clearIPCache;
15109
+ exports.getAutomaticProperties = getAutomaticProperties;
15110
+ exports.getCachedIP = getCachedIP;
15111
+ exports.getClientIP = getClientIP;
15112
+ exports.getCurrentPageProperties = getCurrentPageProperties;
15113
+ exports.getDeviceInfo = getDeviceInfo;
15114
+ exports.getInitialProperties = getInitialProperties;
15115
+ exports.getLocationInfo = getLocationInfo;
14314
15116
  exports.isChunkSizeExceeded = isChunkSizeExceeded;
14315
15117
  exports.logDebug = logDebug;
14316
15118
  exports.logError = logError;