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.
- package/dist/cjs/angular/index.cjs +817 -19
- package/dist/cjs/angular/index.cjs.map +1 -1
- package/dist/cjs/index.cjs +833 -19
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/react/index.cjs +818 -20
- package/dist/cjs/react/index.cjs.map +1 -1
- package/dist/cjs/remix/index.cjs +818 -20
- package/dist/cjs/remix/index.cjs.map +1 -1
- package/dist/cjs/svelte/index.cjs +817 -19
- package/dist/cjs/svelte/index.cjs.map +1 -1
- package/dist/cjs/vue/index.cjs +817 -19
- package/dist/cjs/vue/index.cjs.map +1 -1
- package/dist/esm/angular/index.js +817 -19
- package/dist/esm/angular/index.js.map +1 -1
- package/dist/esm/index.js +825 -20
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/index.js +818 -20
- package/dist/esm/react/index.js.map +1 -1
- package/dist/esm/remix/index.js +818 -20
- package/dist/esm/remix/index.js.map +1 -1
- package/dist/esm/svelte/index.js +817 -19
- package/dist/esm/svelte/index.js.map +1 -1
- package/dist/esm/vue/index.js +817 -19
- package/dist/esm/vue/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/types/angular/index.d.ts +60 -1
- package/dist/types/index.d.ts +258 -3
- package/dist/types/react/index.d.ts +60 -1
- package/dist/types/remix/index.d.ts +60 -1
- package/dist/types/svelte/index.d.ts +60 -1
- package/package/canvas-recording-demo.html +1 -1
- package/package/simple-spa.html +1 -1
- package/package/src/angular/index.ts +3 -3
- package/package/src/react/index.tsx +2 -2
- package/package/src/svelte/index.ts +1 -1
- package/package/src/tracker.ts +2 -2
- package/package/src/vue/index.ts +1 -1
- package/package.json +1 -1
- package/simple-spa.html +164 -2
- package/src/angular/index.ts +3 -3
- package/src/api.ts +40 -0
- package/src/index.ts +7 -0
- package/src/react/index.tsx +2 -2
- package/src/svelte/index.ts +1 -1
- package/src/tracker.ts +193 -17
- package/src/utils/ip-detector.ts +158 -0
- package/src/utils/property-detector.ts +345 -0
- package/src/utils/property-manager.ts +274 -0
- package/src/vue/index.ts +1 -1
- package/canvas-recording-demo.html +0 -143
- package/clean-console-demo.html +0 -39
- package/simple-demo.html +0 -26
package/dist/cjs/index.cjs
CHANGED
|
@@ -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$
|
|
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$
|
|
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$
|
|
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 {
|
|
@@ -13184,7 +13848,7 @@ class HumanBehaviorTracker {
|
|
|
13184
13848
|
var _a;
|
|
13185
13849
|
// ✅ SUPPRESS COMMON RRWEB ERRORS FOR CLEAN CONSOLE
|
|
13186
13850
|
if (isBrowser && (options === null || options === void 0 ? void 0 : options.suppressConsoleErrors) !== false) {
|
|
13187
|
-
// Suppress canvas security errors
|
|
13851
|
+
// Suppress canvas security errors and network errors
|
|
13188
13852
|
const originalConsoleError = console.error;
|
|
13189
13853
|
console.error = (...args) => {
|
|
13190
13854
|
const message = args.join(' ');
|
|
@@ -13195,8 +13859,14 @@ class HumanBehaviorTracker {
|
|
|
13195
13859
|
message.includes('CORS') ||
|
|
13196
13860
|
message.includes('Access-Control-Allow-Origin') ||
|
|
13197
13861
|
message.includes('Failed to load resource') ||
|
|
13198
|
-
message.includes('net::ERR_BLOCKED_BY_CLIENT')
|
|
13199
|
-
|
|
13862
|
+
message.includes('net::ERR_BLOCKED_BY_CLIENT') ||
|
|
13863
|
+
message.includes('NetworkError when attempting to fetch resource') ||
|
|
13864
|
+
message.includes('Failed to fetch') ||
|
|
13865
|
+
message.includes('TypeError: NetworkError') ||
|
|
13866
|
+
message.includes('HumanBehavior ERROR') ||
|
|
13867
|
+
message.includes('Failed to track custom event') ||
|
|
13868
|
+
message.includes('Error sending custom event')) {
|
|
13869
|
+
// Silently suppress these common errors
|
|
13200
13870
|
return;
|
|
13201
13871
|
}
|
|
13202
13872
|
originalConsoleError.apply(console, args);
|
|
@@ -13210,8 +13880,12 @@ class HumanBehaviorTracker {
|
|
|
13210
13880
|
message.includes('CORS') ||
|
|
13211
13881
|
message.includes('Access-Control-Allow-Origin') ||
|
|
13212
13882
|
message.includes('Failed to load resource') ||
|
|
13213
|
-
message.includes('net::ERR_BLOCKED_BY_CLIENT')
|
|
13214
|
-
|
|
13883
|
+
message.includes('net::ERR_BLOCKED_BY_CLIENT') ||
|
|
13884
|
+
message.includes('NetworkError when attempting to fetch resource') ||
|
|
13885
|
+
message.includes('Failed to fetch') ||
|
|
13886
|
+
message.includes('Custom event network error') ||
|
|
13887
|
+
message.includes('Request blocked by ad blocker')) {
|
|
13888
|
+
// Silently suppress these common warnings
|
|
13215
13889
|
return;
|
|
13216
13890
|
}
|
|
13217
13891
|
originalConsoleWarn.apply(console, args);
|
|
@@ -13223,7 +13897,9 @@ class HumanBehaviorTracker {
|
|
|
13223
13897
|
message.includes('Tainted canvases') ||
|
|
13224
13898
|
message.includes('toDataURL') ||
|
|
13225
13899
|
message.includes('Cross-Origin') ||
|
|
13226
|
-
message.includes('CORS')
|
|
13900
|
+
message.includes('CORS') ||
|
|
13901
|
+
message.includes('NetworkError') ||
|
|
13902
|
+
message.includes('Failed to fetch')) {
|
|
13227
13903
|
event.preventDefault();
|
|
13228
13904
|
return false;
|
|
13229
13905
|
}
|
|
@@ -13239,7 +13915,10 @@ class HumanBehaviorTracker {
|
|
|
13239
13915
|
this.configureLogging({ level: options.logLevel });
|
|
13240
13916
|
}
|
|
13241
13917
|
// Create new tracker instance
|
|
13242
|
-
const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl
|
|
13918
|
+
const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl, {
|
|
13919
|
+
enableAutomaticProperties: options === null || options === void 0 ? void 0 : options.enableAutomaticProperties,
|
|
13920
|
+
propertyDenylist: options === null || options === void 0 ? void 0 : options.propertyDenylist
|
|
13921
|
+
});
|
|
13243
13922
|
// Store canvas recording preference
|
|
13244
13923
|
tracker.recordCanvas = (_a = options === null || options === void 0 ? void 0 : options.recordCanvas) !== null && _a !== void 0 ? _a : false;
|
|
13245
13924
|
// Set redacted fields if specified
|
|
@@ -13256,7 +13935,7 @@ class HumanBehaviorTracker {
|
|
|
13256
13935
|
tracker.start();
|
|
13257
13936
|
return tracker;
|
|
13258
13937
|
}
|
|
13259
|
-
constructor(apiKey, ingestionUrl) {
|
|
13938
|
+
constructor(apiKey, ingestionUrl, options) {
|
|
13260
13939
|
this.eventIngestionQueue = [];
|
|
13261
13940
|
this.userProperties = {};
|
|
13262
13941
|
this.isProcessing = false;
|
|
@@ -13294,6 +13973,11 @@ class HumanBehaviorTracker {
|
|
|
13294
13973
|
});
|
|
13295
13974
|
this.apiKey = apiKey;
|
|
13296
13975
|
this.redactionManager = new RedactionManager();
|
|
13976
|
+
// Initialize property manager
|
|
13977
|
+
this.propertyManager = new PropertyManager({
|
|
13978
|
+
enableAutomaticProperties: (options === null || options === void 0 ? void 0 : options.enableAutomaticProperties) !== false,
|
|
13979
|
+
propertyDenylist: (options === null || options === void 0 ? void 0 : options.propertyDenylist) || []
|
|
13980
|
+
});
|
|
13297
13981
|
// Handle session restoration with improved continuity
|
|
13298
13982
|
if (isBrowser) {
|
|
13299
13983
|
const existingSessionId = localStorage.getItem(`human_behavior_session_id_${this.apiKey}`);
|
|
@@ -13332,7 +14016,28 @@ class HumanBehaviorTracker {
|
|
|
13332
14016
|
try {
|
|
13333
14017
|
const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
|
|
13334
14018
|
logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
|
|
13335
|
-
|
|
14019
|
+
// Get automatic properties for init
|
|
14020
|
+
const automaticProperties = this.propertyManager.getAutomaticProperties();
|
|
14021
|
+
// Create a custom init request with automatic properties
|
|
14022
|
+
const initResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/init`, {
|
|
14023
|
+
method: 'POST',
|
|
14024
|
+
headers: {
|
|
14025
|
+
'Content-Type': 'application/json',
|
|
14026
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
14027
|
+
'Referer': document.referrer || ''
|
|
14028
|
+
},
|
|
14029
|
+
body: JSON.stringify({
|
|
14030
|
+
sessionId: this.sessionId,
|
|
14031
|
+
endUserId: userId,
|
|
14032
|
+
entryURL: window.location.href,
|
|
14033
|
+
referrer: document.referrer,
|
|
14034
|
+
automaticProperties: automaticProperties
|
|
14035
|
+
})
|
|
14036
|
+
});
|
|
14037
|
+
if (!initResponse.ok) {
|
|
14038
|
+
throw new Error(`Failed to initialize: ${initResponse.statusText}`);
|
|
14039
|
+
}
|
|
14040
|
+
const { sessionId, endUserId } = yield initResponse.json();
|
|
13336
14041
|
// Check if server returned a different session ID (for session continuity)
|
|
13337
14042
|
if (sessionId !== this.sessionId) {
|
|
13338
14043
|
logDebug(`Server returned different sessionId: ${sessionId} (client had: ${this.sessionId})`);
|
|
@@ -13344,6 +14049,10 @@ class HumanBehaviorTracker {
|
|
|
13344
14049
|
}
|
|
13345
14050
|
this.endUserId = endUserId;
|
|
13346
14051
|
this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
|
|
14052
|
+
// Send IP information after successful initialization
|
|
14053
|
+
this.api.sendIPInfo(this.sessionId).catch(error => {
|
|
14054
|
+
logWarn('Failed to send IP info:', error);
|
|
14055
|
+
});
|
|
13347
14056
|
// Only setup browser-specific handlers when in browser environment
|
|
13348
14057
|
if (isBrowser) {
|
|
13349
14058
|
this.setupPageUnloadHandler();
|
|
@@ -13464,6 +14173,8 @@ class HumanBehaviorTracker {
|
|
|
13464
14173
|
return __awaiter(this, void 0, void 0, function* () {
|
|
13465
14174
|
if (!this.initialized)
|
|
13466
14175
|
return;
|
|
14176
|
+
// Update automatic properties for new page
|
|
14177
|
+
this.propertyManager.updateAutomaticProperties();
|
|
13467
14178
|
try {
|
|
13468
14179
|
const pageViewData = {
|
|
13469
14180
|
url: url || window.location.href,
|
|
@@ -13473,11 +14184,13 @@ class HumanBehaviorTracker {
|
|
|
13473
14184
|
referrer: document.referrer,
|
|
13474
14185
|
timestamp: new Date().toISOString()
|
|
13475
14186
|
};
|
|
14187
|
+
// Get enhanced properties with automatic properties
|
|
14188
|
+
const enhancedProperties = this.propertyManager.getEventProperties(pageViewData);
|
|
13476
14189
|
// Add pageview event to the main event stream
|
|
13477
14190
|
yield this.addEvent({
|
|
13478
14191
|
type: 5, // Custom event type
|
|
13479
14192
|
data: {
|
|
13480
|
-
payload: Object.assign({ eventType: 'pageview' },
|
|
14193
|
+
payload: Object.assign({ eventType: 'pageview' }, enhancedProperties)
|
|
13481
14194
|
},
|
|
13482
14195
|
timestamp: Date.now()
|
|
13483
14196
|
});
|
|
@@ -13493,10 +14206,12 @@ class HumanBehaviorTracker {
|
|
|
13493
14206
|
var _a, _b, _c, _d, _e;
|
|
13494
14207
|
if (!this.initialized)
|
|
13495
14208
|
return;
|
|
14209
|
+
// Get enhanced properties with automatic properties
|
|
14210
|
+
const enhancedProperties = this.propertyManager.getEventProperties(properties);
|
|
13496
14211
|
try {
|
|
13497
14212
|
// Send custom event directly to the API
|
|
13498
|
-
yield this.api.sendCustomEvent(this.sessionId, eventName,
|
|
13499
|
-
logDebug(`Custom event tracked: ${eventName}`,
|
|
14213
|
+
yield this.api.sendCustomEvent(this.sessionId, eventName, enhancedProperties);
|
|
14214
|
+
logDebug(`Custom event tracked: ${eventName}`, enhancedProperties);
|
|
13500
14215
|
}
|
|
13501
14216
|
catch (error) {
|
|
13502
14217
|
logError('Failed to track custom event:', error);
|
|
@@ -13516,7 +14231,7 @@ class HumanBehaviorTracker {
|
|
|
13516
14231
|
try {
|
|
13517
14232
|
const customEventData = {
|
|
13518
14233
|
eventName: eventName,
|
|
13519
|
-
properties:
|
|
14234
|
+
properties: enhancedProperties || {},
|
|
13520
14235
|
timestamp: new Date().toISOString(),
|
|
13521
14236
|
url: window.location.href,
|
|
13522
14237
|
pathname: window.location.pathname
|
|
@@ -13816,8 +14531,25 @@ class HumanBehaviorTracker {
|
|
|
13816
14531
|
// Store user properties
|
|
13817
14532
|
this.userProperties = userProperties;
|
|
13818
14533
|
logDebug('Identifying user:', { userProperties, originalEndUserId, sessionId: this.sessionId });
|
|
13819
|
-
//
|
|
13820
|
-
|
|
14534
|
+
// Get automatic properties and send with user data
|
|
14535
|
+
const automaticProperties = this.propertyManager.getAutomaticProperties();
|
|
14536
|
+
// Create a custom user request with automatic properties
|
|
14537
|
+
const userResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/user`, {
|
|
14538
|
+
method: 'POST',
|
|
14539
|
+
headers: {
|
|
14540
|
+
'Content-Type': 'application/json',
|
|
14541
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
14542
|
+
},
|
|
14543
|
+
body: JSON.stringify({
|
|
14544
|
+
userId: originalEndUserId,
|
|
14545
|
+
userAttributes: userProperties,
|
|
14546
|
+
sessionId: this.sessionId,
|
|
14547
|
+
automaticProperties: automaticProperties
|
|
14548
|
+
})
|
|
14549
|
+
});
|
|
14550
|
+
if (!userResponse.ok) {
|
|
14551
|
+
throw new Error(`Failed to identify user: ${userResponse.statusText}`);
|
|
14552
|
+
}
|
|
13821
14553
|
// Don't update endUserId - keep it as the original UUID
|
|
13822
14554
|
return originalEndUserId || '';
|
|
13823
14555
|
});
|
|
@@ -13865,7 +14597,7 @@ class HumanBehaviorTracker {
|
|
|
13865
14597
|
collectFonts: false, // Disable font collection to reduce errors
|
|
13866
14598
|
inlineStylesheet: true, // Keep styles for proper session replay
|
|
13867
14599
|
recordCrossOriginIframes: false, // Prevent cross-origin iframe errors
|
|
13868
|
-
// ✅ CANVAS RECORDING -
|
|
14600
|
+
// ✅ CANVAS RECORDING - protection against overwhelm
|
|
13869
14601
|
recordCanvas: this.recordCanvas, // Opt-in only
|
|
13870
14602
|
sampling: this.recordCanvas ? { canvas: 4 } : undefined, // 4 FPS throttle
|
|
13871
14603
|
dataURLOptions: this.recordCanvas ? {
|
|
@@ -14289,6 +15021,79 @@ class HumanBehaviorTracker {
|
|
|
14289
15021
|
initialized: this.initialized
|
|
14290
15022
|
};
|
|
14291
15023
|
}
|
|
15024
|
+
// ===== PROPERTY MANAGEMENT METHODS =====
|
|
15025
|
+
/**
|
|
15026
|
+
* Set a session property that will be included in all events for this session
|
|
15027
|
+
*/
|
|
15028
|
+
setSessionProperty(key, value) {
|
|
15029
|
+
this.propertyManager.setSessionProperty(key, value);
|
|
15030
|
+
}
|
|
15031
|
+
/**
|
|
15032
|
+
* Set multiple session properties
|
|
15033
|
+
*/
|
|
15034
|
+
setSessionProperties(properties) {
|
|
15035
|
+
this.propertyManager.setSessionProperties(properties);
|
|
15036
|
+
}
|
|
15037
|
+
/**
|
|
15038
|
+
* Get a session property
|
|
15039
|
+
*/
|
|
15040
|
+
getSessionProperty(key) {
|
|
15041
|
+
return this.propertyManager.getSessionProperty(key);
|
|
15042
|
+
}
|
|
15043
|
+
/**
|
|
15044
|
+
* Remove a session property
|
|
15045
|
+
*/
|
|
15046
|
+
removeSessionProperty(key) {
|
|
15047
|
+
this.propertyManager.removeSessionProperty(key);
|
|
15048
|
+
}
|
|
15049
|
+
/**
|
|
15050
|
+
* Set a user property that will be included in all events
|
|
15051
|
+
*/
|
|
15052
|
+
setUserProperty(key, value) {
|
|
15053
|
+
this.propertyManager.setUserProperty(key, value);
|
|
15054
|
+
}
|
|
15055
|
+
/**
|
|
15056
|
+
* Set multiple user properties
|
|
15057
|
+
*/
|
|
15058
|
+
setUserProperties(properties) {
|
|
15059
|
+
this.propertyManager.setUserProperties(properties);
|
|
15060
|
+
}
|
|
15061
|
+
/**
|
|
15062
|
+
* Get a user property
|
|
15063
|
+
*/
|
|
15064
|
+
getUserProperty(key) {
|
|
15065
|
+
return this.propertyManager.getUserProperty(key);
|
|
15066
|
+
}
|
|
15067
|
+
/**
|
|
15068
|
+
* Remove a user property
|
|
15069
|
+
*/
|
|
15070
|
+
removeUserProperty(key) {
|
|
15071
|
+
this.propertyManager.removeUserProperty(key);
|
|
15072
|
+
}
|
|
15073
|
+
/**
|
|
15074
|
+
* Set a property only if it hasn't been set before
|
|
15075
|
+
*/
|
|
15076
|
+
setOnce(key, value, scope = 'user') {
|
|
15077
|
+
this.propertyManager.setOnce(key, value, scope);
|
|
15078
|
+
}
|
|
15079
|
+
/**
|
|
15080
|
+
* Clear all session properties
|
|
15081
|
+
*/
|
|
15082
|
+
clearSessionProperties() {
|
|
15083
|
+
this.propertyManager.clearSessionProperties();
|
|
15084
|
+
}
|
|
15085
|
+
/**
|
|
15086
|
+
* Clear all user properties
|
|
15087
|
+
*/
|
|
15088
|
+
clearUserProperties() {
|
|
15089
|
+
this.propertyManager.clearUserProperties();
|
|
15090
|
+
}
|
|
15091
|
+
/**
|
|
15092
|
+
* Get all properties for debugging
|
|
15093
|
+
*/
|
|
15094
|
+
getAllProperties() {
|
|
15095
|
+
return this.propertyManager.getAllProperties();
|
|
15096
|
+
}
|
|
14292
15097
|
}
|
|
14293
15098
|
// Only expose to window object in browser environments
|
|
14294
15099
|
if (isBrowser) {
|
|
@@ -14310,7 +15115,16 @@ if (typeof window !== 'undefined') {
|
|
|
14310
15115
|
exports.HumanBehaviorAPI = HumanBehaviorAPI;
|
|
14311
15116
|
exports.HumanBehaviorTracker = HumanBehaviorTracker;
|
|
14312
15117
|
exports.MAX_CHUNK_SIZE_BYTES = MAX_CHUNK_SIZE_BYTES;
|
|
15118
|
+
exports.PropertyManager = PropertyManager;
|
|
14313
15119
|
exports.RedactionManager = RedactionManager;
|
|
15120
|
+
exports.clearIPCache = clearIPCache;
|
|
15121
|
+
exports.getAutomaticProperties = getAutomaticProperties;
|
|
15122
|
+
exports.getCachedIP = getCachedIP;
|
|
15123
|
+
exports.getClientIP = getClientIP;
|
|
15124
|
+
exports.getCurrentPageProperties = getCurrentPageProperties;
|
|
15125
|
+
exports.getDeviceInfo = getDeviceInfo;
|
|
15126
|
+
exports.getInitialProperties = getInitialProperties;
|
|
15127
|
+
exports.getLocationInfo = getLocationInfo;
|
|
14314
15128
|
exports.isChunkSizeExceeded = isChunkSizeExceeded;
|
|
14315
15129
|
exports.logDebug = logDebug;
|
|
14316
15130
|
exports.logError = logError;
|