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
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 {
@@ -13237,7 +13901,10 @@ class HumanBehaviorTracker {
13237
13901
  this.configureLogging({ level: options.logLevel });
13238
13902
  }
13239
13903
  // Create new tracker instance
13240
- const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl);
13904
+ const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl, {
13905
+ enableAutomaticProperties: options === null || options === void 0 ? void 0 : options.enableAutomaticProperties,
13906
+ propertyDenylist: options === null || options === void 0 ? void 0 : options.propertyDenylist
13907
+ });
13241
13908
  // Store canvas recording preference
13242
13909
  tracker.recordCanvas = (_a = options === null || options === void 0 ? void 0 : options.recordCanvas) !== null && _a !== void 0 ? _a : false;
13243
13910
  // Set redacted fields if specified
@@ -13254,7 +13921,7 @@ class HumanBehaviorTracker {
13254
13921
  tracker.start();
13255
13922
  return tracker;
13256
13923
  }
13257
- constructor(apiKey, ingestionUrl) {
13924
+ constructor(apiKey, ingestionUrl, options) {
13258
13925
  this.eventIngestionQueue = [];
13259
13926
  this.userProperties = {};
13260
13927
  this.isProcessing = false;
@@ -13292,6 +13959,11 @@ class HumanBehaviorTracker {
13292
13959
  });
13293
13960
  this.apiKey = apiKey;
13294
13961
  this.redactionManager = new RedactionManager();
13962
+ // Initialize property manager
13963
+ this.propertyManager = new PropertyManager({
13964
+ enableAutomaticProperties: (options === null || options === void 0 ? void 0 : options.enableAutomaticProperties) !== false,
13965
+ propertyDenylist: (options === null || options === void 0 ? void 0 : options.propertyDenylist) || []
13966
+ });
13295
13967
  // Handle session restoration with improved continuity
13296
13968
  if (isBrowser) {
13297
13969
  const existingSessionId = localStorage.getItem(`human_behavior_session_id_${this.apiKey}`);
@@ -13330,7 +14002,28 @@ class HumanBehaviorTracker {
13330
14002
  try {
13331
14003
  const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
13332
14004
  logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
13333
- const { sessionId, endUserId } = yield this.api.init(this.sessionId, userId);
14005
+ // Get automatic properties for init
14006
+ const automaticProperties = this.propertyManager.getAutomaticProperties();
14007
+ // Create a custom init request with automatic properties
14008
+ const initResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/init`, {
14009
+ method: 'POST',
14010
+ headers: {
14011
+ 'Content-Type': 'application/json',
14012
+ 'Authorization': `Bearer ${this.apiKey}`,
14013
+ 'Referer': document.referrer || ''
14014
+ },
14015
+ body: JSON.stringify({
14016
+ sessionId: this.sessionId,
14017
+ endUserId: userId,
14018
+ entryURL: window.location.href,
14019
+ referrer: document.referrer,
14020
+ automaticProperties: automaticProperties
14021
+ })
14022
+ });
14023
+ if (!initResponse.ok) {
14024
+ throw new Error(`Failed to initialize: ${initResponse.statusText}`);
14025
+ }
14026
+ const { sessionId, endUserId } = yield initResponse.json();
13334
14027
  // Check if server returned a different session ID (for session continuity)
13335
14028
  if (sessionId !== this.sessionId) {
13336
14029
  logDebug(`Server returned different sessionId: ${sessionId} (client had: ${this.sessionId})`);
@@ -13342,6 +14035,10 @@ class HumanBehaviorTracker {
13342
14035
  }
13343
14036
  this.endUserId = endUserId;
13344
14037
  this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
14038
+ // Send IP information after successful initialization
14039
+ this.api.sendIPInfo(this.sessionId).catch(error => {
14040
+ logWarn('Failed to send IP info:', error);
14041
+ });
13345
14042
  // Only setup browser-specific handlers when in browser environment
13346
14043
  if (isBrowser) {
13347
14044
  this.setupPageUnloadHandler();
@@ -13462,6 +14159,8 @@ class HumanBehaviorTracker {
13462
14159
  return __awaiter(this, void 0, void 0, function* () {
13463
14160
  if (!this.initialized)
13464
14161
  return;
14162
+ // Update automatic properties for new page
14163
+ this.propertyManager.updateAutomaticProperties();
13465
14164
  try {
13466
14165
  const pageViewData = {
13467
14166
  url: url || window.location.href,
@@ -13471,11 +14170,13 @@ class HumanBehaviorTracker {
13471
14170
  referrer: document.referrer,
13472
14171
  timestamp: new Date().toISOString()
13473
14172
  };
14173
+ // Get enhanced properties with automatic properties
14174
+ const enhancedProperties = this.propertyManager.getEventProperties(pageViewData);
13474
14175
  // Add pageview event to the main event stream
13475
14176
  yield this.addEvent({
13476
14177
  type: 5, // Custom event type
13477
14178
  data: {
13478
- payload: Object.assign({ eventType: 'pageview' }, pageViewData)
14179
+ payload: Object.assign({ eventType: 'pageview' }, enhancedProperties)
13479
14180
  },
13480
14181
  timestamp: Date.now()
13481
14182
  });
@@ -13491,10 +14192,12 @@ class HumanBehaviorTracker {
13491
14192
  var _a, _b, _c, _d, _e;
13492
14193
  if (!this.initialized)
13493
14194
  return;
14195
+ // Get enhanced properties with automatic properties
14196
+ const enhancedProperties = this.propertyManager.getEventProperties(properties);
13494
14197
  try {
13495
14198
  // Send custom event directly to the API
13496
- yield this.api.sendCustomEvent(this.sessionId, eventName, properties);
13497
- logDebug(`Custom event tracked: ${eventName}`, properties);
14199
+ yield this.api.sendCustomEvent(this.sessionId, eventName, enhancedProperties);
14200
+ logDebug(`Custom event tracked: ${eventName}`, enhancedProperties);
13498
14201
  }
13499
14202
  catch (error) {
13500
14203
  logError('Failed to track custom event:', error);
@@ -13514,7 +14217,7 @@ class HumanBehaviorTracker {
13514
14217
  try {
13515
14218
  const customEventData = {
13516
14219
  eventName: eventName,
13517
- properties: properties || {},
14220
+ properties: enhancedProperties || {},
13518
14221
  timestamp: new Date().toISOString(),
13519
14222
  url: window.location.href,
13520
14223
  pathname: window.location.pathname
@@ -13814,8 +14517,25 @@ class HumanBehaviorTracker {
13814
14517
  // Store user properties
13815
14518
  this.userProperties = userProperties;
13816
14519
  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);
14520
+ // Get automatic properties and send with user data
14521
+ const automaticProperties = this.propertyManager.getAutomaticProperties();
14522
+ // Create a custom user request with automatic properties
14523
+ const userResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/user`, {
14524
+ method: 'POST',
14525
+ headers: {
14526
+ 'Content-Type': 'application/json',
14527
+ 'Authorization': `Bearer ${this.apiKey}`
14528
+ },
14529
+ body: JSON.stringify({
14530
+ userId: originalEndUserId,
14531
+ userAttributes: userProperties,
14532
+ sessionId: this.sessionId,
14533
+ automaticProperties: automaticProperties
14534
+ })
14535
+ });
14536
+ if (!userResponse.ok) {
14537
+ throw new Error(`Failed to identify user: ${userResponse.statusText}`);
14538
+ }
13819
14539
  // Don't update endUserId - keep it as the original UUID
13820
14540
  return originalEndUserId || '';
13821
14541
  });
@@ -13863,7 +14583,7 @@ class HumanBehaviorTracker {
13863
14583
  collectFonts: false, // Disable font collection to reduce errors
13864
14584
  inlineStylesheet: true, // Keep styles for proper session replay
13865
14585
  recordCrossOriginIframes: false, // Prevent cross-origin iframe errors
13866
- // ✅ CANVAS RECORDING - PostHog-style protection against overwhelm
14586
+ // ✅ CANVAS RECORDING - protection against overwhelm
13867
14587
  recordCanvas: this.recordCanvas, // Opt-in only
13868
14588
  sampling: this.recordCanvas ? { canvas: 4 } : undefined, // 4 FPS throttle
13869
14589
  dataURLOptions: this.recordCanvas ? {
@@ -14287,6 +15007,79 @@ class HumanBehaviorTracker {
14287
15007
  initialized: this.initialized
14288
15008
  };
14289
15009
  }
15010
+ // ===== PROPERTY MANAGEMENT METHODS =====
15011
+ /**
15012
+ * Set a session property that will be included in all events for this session
15013
+ */
15014
+ setSessionProperty(key, value) {
15015
+ this.propertyManager.setSessionProperty(key, value);
15016
+ }
15017
+ /**
15018
+ * Set multiple session properties
15019
+ */
15020
+ setSessionProperties(properties) {
15021
+ this.propertyManager.setSessionProperties(properties);
15022
+ }
15023
+ /**
15024
+ * Get a session property
15025
+ */
15026
+ getSessionProperty(key) {
15027
+ return this.propertyManager.getSessionProperty(key);
15028
+ }
15029
+ /**
15030
+ * Remove a session property
15031
+ */
15032
+ removeSessionProperty(key) {
15033
+ this.propertyManager.removeSessionProperty(key);
15034
+ }
15035
+ /**
15036
+ * Set a user property that will be included in all events
15037
+ */
15038
+ setUserProperty(key, value) {
15039
+ this.propertyManager.setUserProperty(key, value);
15040
+ }
15041
+ /**
15042
+ * Set multiple user properties
15043
+ */
15044
+ setUserProperties(properties) {
15045
+ this.propertyManager.setUserProperties(properties);
15046
+ }
15047
+ /**
15048
+ * Get a user property
15049
+ */
15050
+ getUserProperty(key) {
15051
+ return this.propertyManager.getUserProperty(key);
15052
+ }
15053
+ /**
15054
+ * Remove a user property
15055
+ */
15056
+ removeUserProperty(key) {
15057
+ this.propertyManager.removeUserProperty(key);
15058
+ }
15059
+ /**
15060
+ * Set a property only if it hasn't been set before
15061
+ */
15062
+ setOnce(key, value, scope = 'user') {
15063
+ this.propertyManager.setOnce(key, value, scope);
15064
+ }
15065
+ /**
15066
+ * Clear all session properties
15067
+ */
15068
+ clearSessionProperties() {
15069
+ this.propertyManager.clearSessionProperties();
15070
+ }
15071
+ /**
15072
+ * Clear all user properties
15073
+ */
15074
+ clearUserProperties() {
15075
+ this.propertyManager.clearUserProperties();
15076
+ }
15077
+ /**
15078
+ * Get all properties for debugging
15079
+ */
15080
+ getAllProperties() {
15081
+ return this.propertyManager.getAllProperties();
15082
+ }
14290
15083
  }
14291
15084
  // Only expose to window object in browser environments
14292
15085
  if (isBrowser) {
@@ -14305,5 +15098,5 @@ if (typeof window !== 'undefined') {
14305
15098
  window.HumanBehaviorTracker = HumanBehaviorTracker;
14306
15099
  }
14307
15100
 
14308
- export { HumanBehaviorAPI, HumanBehaviorTracker, LogLevel, MAX_CHUNK_SIZE_BYTES, RedactionManager, isChunkSizeExceeded, logDebug, logError, logInfo, logWarn, logger, redactionManager, splitLargeEvent, validateSingleEventSize };
15101
+ 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
15102
  //# sourceMappingURL=index.js.map