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
|
@@ -12389,6 +12389,145 @@ 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
|
+
|
|
12392
12531
|
const MAX_CHUNK_SIZE_BYTES = 1024 * 1024; // 1MB chunk size - more conservative
|
|
12393
12532
|
function isChunkSizeExceeded(currentChunk, newEvent, sessionId) {
|
|
12394
12533
|
const nextChunkSize = new TextEncoder().encode(JSON.stringify({
|
|
@@ -12482,6 +12621,45 @@ class HumanBehaviorAPI {
|
|
|
12482
12621
|
}
|
|
12483
12622
|
});
|
|
12484
12623
|
}
|
|
12624
|
+
/**
|
|
12625
|
+
* Send IP address information to the server
|
|
12626
|
+
* This is called after successful initialization
|
|
12627
|
+
*/
|
|
12628
|
+
sendIPInfo(sessionId) {
|
|
12629
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
12630
|
+
try {
|
|
12631
|
+
const ipInfo = yield getCachedIP();
|
|
12632
|
+
if (!ipInfo) {
|
|
12633
|
+
logWarn('No IP address available to send');
|
|
12634
|
+
return;
|
|
12635
|
+
}
|
|
12636
|
+
logDebug('Sending IP info:', ipInfo);
|
|
12637
|
+
const response = yield fetch(`${this.baseUrl}/api/ingestion/ip-info`, {
|
|
12638
|
+
method: 'POST',
|
|
12639
|
+
headers: {
|
|
12640
|
+
'Content-Type': 'application/json',
|
|
12641
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
12642
|
+
},
|
|
12643
|
+
body: JSON.stringify({
|
|
12644
|
+
sessionId: sessionId,
|
|
12645
|
+
clientIP: ipInfo.ip,
|
|
12646
|
+
ipDetectionMethod: ipInfo.method,
|
|
12647
|
+
timestamp: ipInfo.timestamp
|
|
12648
|
+
})
|
|
12649
|
+
});
|
|
12650
|
+
if (!response.ok) {
|
|
12651
|
+
const errorText = yield response.text();
|
|
12652
|
+
logWarn('Failed to send IP info:', response.status, errorText);
|
|
12653
|
+
}
|
|
12654
|
+
else {
|
|
12655
|
+
logDebug('IP info sent successfully');
|
|
12656
|
+
}
|
|
12657
|
+
}
|
|
12658
|
+
catch (error) {
|
|
12659
|
+
logWarn('Error sending IP info:', error);
|
|
12660
|
+
}
|
|
12661
|
+
});
|
|
12662
|
+
}
|
|
12485
12663
|
sendEvents(events, sessionId, userId) {
|
|
12486
12664
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12487
12665
|
// ✅ SIMPLE VALIDATION FOR ALL EVENTS
|
|
@@ -12708,7 +12886,7 @@ class HumanBehaviorAPI {
|
|
|
12708
12886
|
// This module provides methods to configure rrweb's built-in masking
|
|
12709
12887
|
// Uses CSS selectors and classes for reliable redaction without event corruption
|
|
12710
12888
|
// Check if we're in a browser environment
|
|
12711
|
-
const isBrowser$
|
|
12889
|
+
const isBrowser$2 = typeof window !== 'undefined';
|
|
12712
12890
|
class RedactionManager {
|
|
12713
12891
|
constructor(options) {
|
|
12714
12892
|
this.redactedText = '[REDACTED]';
|
|
@@ -12867,7 +13045,7 @@ class RedactionManager {
|
|
|
12867
13045
|
* Check if a DOM change should be redacted based on its ID
|
|
12868
13046
|
*/
|
|
12869
13047
|
shouldRedactDOMChange(changeData) {
|
|
12870
|
-
if (!isBrowser$
|
|
13048
|
+
if (!isBrowser$2)
|
|
12871
13049
|
return false;
|
|
12872
13050
|
try {
|
|
12873
13051
|
// Check if this change has an ID that we can use to find the element
|
|
@@ -13010,7 +13188,7 @@ class RedactionManager {
|
|
|
13010
13188
|
* Check if an event is from a field that should be redacted
|
|
13011
13189
|
*/
|
|
13012
13190
|
isFieldSelected(eventData) {
|
|
13013
|
-
if (!isBrowser$
|
|
13191
|
+
if (!isBrowser$2)
|
|
13014
13192
|
return false;
|
|
13015
13193
|
try {
|
|
13016
13194
|
// For input events (source 5), we need to determine if this is a sensitive field
|
|
@@ -13161,6 +13339,485 @@ class RedactionManager {
|
|
|
13161
13339
|
// Export a default instance
|
|
13162
13340
|
new RedactionManager();
|
|
13163
13341
|
|
|
13342
|
+
/**
|
|
13343
|
+
* Automatic Property Detection for HumanBehavior SDK
|
|
13344
|
+
* Captures device type, location, and initial referrer information
|
|
13345
|
+
*/
|
|
13346
|
+
// Check if we're in a browser environment
|
|
13347
|
+
const isBrowser$1 = typeof window !== 'undefined';
|
|
13348
|
+
/**
|
|
13349
|
+
* Detect device type based on user agent and screen size
|
|
13350
|
+
*/
|
|
13351
|
+
function detectDeviceType() {
|
|
13352
|
+
if (!isBrowser$1)
|
|
13353
|
+
return 'unknown';
|
|
13354
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
13355
|
+
const screenWidth = window.screen.width;
|
|
13356
|
+
const screenHeight = window.screen.height;
|
|
13357
|
+
// Mobile detection
|
|
13358
|
+
if (/mobile|android|iphone|ipad|ipod|blackberry|windows phone/i.test(userAgent)) {
|
|
13359
|
+
if (/ipad/i.test(userAgent) || (screenWidth >= 768 && screenHeight >= 1024)) {
|
|
13360
|
+
return 'tablet';
|
|
13361
|
+
}
|
|
13362
|
+
return 'mobile';
|
|
13363
|
+
}
|
|
13364
|
+
// Desktop detection
|
|
13365
|
+
if (/windows|macintosh|linux/i.test(userAgent)) {
|
|
13366
|
+
return 'desktop';
|
|
13367
|
+
}
|
|
13368
|
+
return 'unknown';
|
|
13369
|
+
}
|
|
13370
|
+
/**
|
|
13371
|
+
* Extract browser information from user agent
|
|
13372
|
+
*/
|
|
13373
|
+
function detectBrowser() {
|
|
13374
|
+
if (!isBrowser$1)
|
|
13375
|
+
return { browser: 'unknown', browser_version: 'unknown' };
|
|
13376
|
+
const userAgent = navigator.userAgent;
|
|
13377
|
+
// Chrome
|
|
13378
|
+
if (/chrome/i.test(userAgent) && !/edge/i.test(userAgent)) {
|
|
13379
|
+
const match = userAgent.match(/chrome\/(\d+)/i);
|
|
13380
|
+
return {
|
|
13381
|
+
browser: 'chrome',
|
|
13382
|
+
browser_version: match ? match[1] : 'unknown'
|
|
13383
|
+
};
|
|
13384
|
+
}
|
|
13385
|
+
// Firefox
|
|
13386
|
+
if (/firefox/i.test(userAgent)) {
|
|
13387
|
+
const match = userAgent.match(/firefox\/(\d+)/i);
|
|
13388
|
+
return {
|
|
13389
|
+
browser: 'firefox',
|
|
13390
|
+
browser_version: match ? match[1] : 'unknown'
|
|
13391
|
+
};
|
|
13392
|
+
}
|
|
13393
|
+
// Safari
|
|
13394
|
+
if (/safari/i.test(userAgent) && !/chrome/i.test(userAgent)) {
|
|
13395
|
+
const match = userAgent.match(/version\/(\d+)/i);
|
|
13396
|
+
return {
|
|
13397
|
+
browser: 'safari',
|
|
13398
|
+
browser_version: match ? match[1] : 'unknown'
|
|
13399
|
+
};
|
|
13400
|
+
}
|
|
13401
|
+
// Edge
|
|
13402
|
+
if (/edge/i.test(userAgent)) {
|
|
13403
|
+
const match = userAgent.match(/edge\/(\d+)/i);
|
|
13404
|
+
return {
|
|
13405
|
+
browser: 'edge',
|
|
13406
|
+
browser_version: match ? match[1] : 'unknown'
|
|
13407
|
+
};
|
|
13408
|
+
}
|
|
13409
|
+
// Internet Explorer
|
|
13410
|
+
if (/msie|trident/i.test(userAgent)) {
|
|
13411
|
+
const match = userAgent.match(/msie (\d+)/i) || userAgent.match(/rv:(\d+)/i);
|
|
13412
|
+
return {
|
|
13413
|
+
browser: 'ie',
|
|
13414
|
+
browser_version: match ? match[1] : 'unknown'
|
|
13415
|
+
};
|
|
13416
|
+
}
|
|
13417
|
+
return { browser: 'unknown', browser_version: 'unknown' };
|
|
13418
|
+
}
|
|
13419
|
+
/**
|
|
13420
|
+
* Extract operating system information from user agent
|
|
13421
|
+
*/
|
|
13422
|
+
function detectOS() {
|
|
13423
|
+
if (!isBrowser$1)
|
|
13424
|
+
return { os: 'unknown', os_version: 'unknown' };
|
|
13425
|
+
const userAgent = navigator.userAgent;
|
|
13426
|
+
// Windows
|
|
13427
|
+
if (/windows/i.test(userAgent)) {
|
|
13428
|
+
const match = userAgent.match(/windows nt (\d+\.\d+)/i);
|
|
13429
|
+
let version = 'unknown';
|
|
13430
|
+
if (match) {
|
|
13431
|
+
const versionNum = parseFloat(match[1]);
|
|
13432
|
+
if (versionNum === 10.0)
|
|
13433
|
+
version = '10';
|
|
13434
|
+
else if (versionNum === 6.3)
|
|
13435
|
+
version = '8.1';
|
|
13436
|
+
else if (versionNum === 6.2)
|
|
13437
|
+
version = '8';
|
|
13438
|
+
else if (versionNum === 6.1)
|
|
13439
|
+
version = '7';
|
|
13440
|
+
else
|
|
13441
|
+
version = match[1];
|
|
13442
|
+
}
|
|
13443
|
+
return { os: 'windows', os_version: version };
|
|
13444
|
+
}
|
|
13445
|
+
// macOS
|
|
13446
|
+
if (/macintosh|mac os x/i.test(userAgent)) {
|
|
13447
|
+
const match = userAgent.match(/mac os x (\d+[._]\d+)/i);
|
|
13448
|
+
return {
|
|
13449
|
+
os: 'macos',
|
|
13450
|
+
os_version: match ? match[1].replace('_', '.') : 'unknown'
|
|
13451
|
+
};
|
|
13452
|
+
}
|
|
13453
|
+
// iOS
|
|
13454
|
+
if (/iphone|ipad|ipod/i.test(userAgent)) {
|
|
13455
|
+
const match = userAgent.match(/os (\d+[._]\d+)/i);
|
|
13456
|
+
return {
|
|
13457
|
+
os: 'ios',
|
|
13458
|
+
os_version: match ? match[1].replace('_', '.') : 'unknown'
|
|
13459
|
+
};
|
|
13460
|
+
}
|
|
13461
|
+
// Android
|
|
13462
|
+
if (/android/i.test(userAgent)) {
|
|
13463
|
+
const match = userAgent.match(/android (\d+\.\d+)/i);
|
|
13464
|
+
return {
|
|
13465
|
+
os: 'android',
|
|
13466
|
+
os_version: match ? match[1] : 'unknown'
|
|
13467
|
+
};
|
|
13468
|
+
}
|
|
13469
|
+
// Linux
|
|
13470
|
+
if (/linux/i.test(userAgent)) {
|
|
13471
|
+
return { os: 'linux', os_version: 'unknown' };
|
|
13472
|
+
}
|
|
13473
|
+
return { os: 'unknown', os_version: 'unknown' };
|
|
13474
|
+
}
|
|
13475
|
+
/**
|
|
13476
|
+
* Extract UTM parameters from URL
|
|
13477
|
+
*/
|
|
13478
|
+
function extractUTMParams(url) {
|
|
13479
|
+
const urlObj = new URL(url);
|
|
13480
|
+
const utmParams = {};
|
|
13481
|
+
const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
|
|
13482
|
+
utmKeys.forEach(key => {
|
|
13483
|
+
const value = urlObj.searchParams.get(key);
|
|
13484
|
+
if (value) {
|
|
13485
|
+
utmParams[key] = value;
|
|
13486
|
+
}
|
|
13487
|
+
});
|
|
13488
|
+
return utmParams;
|
|
13489
|
+
}
|
|
13490
|
+
/**
|
|
13491
|
+
* Extract domain from URL
|
|
13492
|
+
*/
|
|
13493
|
+
function extractDomain(url) {
|
|
13494
|
+
try {
|
|
13495
|
+
const urlObj = new URL(url);
|
|
13496
|
+
return urlObj.hostname;
|
|
13497
|
+
}
|
|
13498
|
+
catch (_a) {
|
|
13499
|
+
return '';
|
|
13500
|
+
}
|
|
13501
|
+
}
|
|
13502
|
+
/**
|
|
13503
|
+
* Get device information
|
|
13504
|
+
*/
|
|
13505
|
+
function getDeviceInfo() {
|
|
13506
|
+
if (!isBrowser$1) {
|
|
13507
|
+
return {
|
|
13508
|
+
device_type: 'unknown',
|
|
13509
|
+
browser: 'unknown',
|
|
13510
|
+
browser_version: 'unknown',
|
|
13511
|
+
os: 'unknown',
|
|
13512
|
+
os_version: 'unknown',
|
|
13513
|
+
screen_resolution: 'unknown',
|
|
13514
|
+
viewport_size: 'unknown',
|
|
13515
|
+
color_depth: 0,
|
|
13516
|
+
timezone: 'unknown',
|
|
13517
|
+
language: 'unknown',
|
|
13518
|
+
languages: []
|
|
13519
|
+
};
|
|
13520
|
+
}
|
|
13521
|
+
const { browser, browser_version } = detectBrowser();
|
|
13522
|
+
const { os, os_version } = detectOS();
|
|
13523
|
+
return {
|
|
13524
|
+
device_type: detectDeviceType(),
|
|
13525
|
+
browser,
|
|
13526
|
+
browser_version,
|
|
13527
|
+
os,
|
|
13528
|
+
os_version,
|
|
13529
|
+
screen_resolution: `${window.screen.width}x${window.screen.height}`,
|
|
13530
|
+
viewport_size: `${window.innerWidth}x${window.innerHeight}`,
|
|
13531
|
+
color_depth: window.screen.colorDepth,
|
|
13532
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
13533
|
+
language: navigator.language,
|
|
13534
|
+
languages: [...(navigator.languages || [navigator.language])],
|
|
13535
|
+
raw_user_agent: navigator.userAgent
|
|
13536
|
+
};
|
|
13537
|
+
}
|
|
13538
|
+
/**
|
|
13539
|
+
* Get location information
|
|
13540
|
+
*/
|
|
13541
|
+
function getLocationInfo() {
|
|
13542
|
+
if (!isBrowser$1) {
|
|
13543
|
+
return {
|
|
13544
|
+
current_url: '',
|
|
13545
|
+
pathname: '',
|
|
13546
|
+
search: '',
|
|
13547
|
+
hash: '',
|
|
13548
|
+
title: '',
|
|
13549
|
+
referrer: '',
|
|
13550
|
+
referrer_domain: '',
|
|
13551
|
+
initial_referrer: '',
|
|
13552
|
+
initial_referrer_domain: ''
|
|
13553
|
+
};
|
|
13554
|
+
}
|
|
13555
|
+
const currentUrl = window.location.href;
|
|
13556
|
+
const referrer = document.referrer;
|
|
13557
|
+
const utmParams = extractUTMParams(currentUrl);
|
|
13558
|
+
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);
|
|
13559
|
+
}
|
|
13560
|
+
/**
|
|
13561
|
+
* Get all automatic properties
|
|
13562
|
+
*/
|
|
13563
|
+
function getAutomaticProperties() {
|
|
13564
|
+
return Object.assign(Object.assign({}, getDeviceInfo()), getLocationInfo());
|
|
13565
|
+
}
|
|
13566
|
+
/**
|
|
13567
|
+
* Get initial properties that should be captured once per session
|
|
13568
|
+
*/
|
|
13569
|
+
function getInitialProperties() {
|
|
13570
|
+
if (!isBrowser$1)
|
|
13571
|
+
return {};
|
|
13572
|
+
const locationInfo = getLocationInfo();
|
|
13573
|
+
return {
|
|
13574
|
+
initial_referrer: locationInfo.initial_referrer,
|
|
13575
|
+
initial_referrer_domain: locationInfo.initial_referrer_domain,
|
|
13576
|
+
initial_url: locationInfo.current_url,
|
|
13577
|
+
initial_pathname: locationInfo.pathname,
|
|
13578
|
+
initial_utm_source: locationInfo.utm_source,
|
|
13579
|
+
initial_utm_medium: locationInfo.utm_medium,
|
|
13580
|
+
initial_utm_campaign: locationInfo.utm_campaign,
|
|
13581
|
+
initial_utm_term: locationInfo.utm_term,
|
|
13582
|
+
initial_utm_content: locationInfo.utm_content
|
|
13583
|
+
};
|
|
13584
|
+
}
|
|
13585
|
+
/**
|
|
13586
|
+
* Get current page properties (changes with navigation)
|
|
13587
|
+
*/
|
|
13588
|
+
function getCurrentPageProperties() {
|
|
13589
|
+
if (!isBrowser$1)
|
|
13590
|
+
return {};
|
|
13591
|
+
const locationInfo = getLocationInfo();
|
|
13592
|
+
return {
|
|
13593
|
+
current_url: locationInfo.current_url,
|
|
13594
|
+
pathname: locationInfo.pathname,
|
|
13595
|
+
search: locationInfo.search,
|
|
13596
|
+
hash: locationInfo.hash,
|
|
13597
|
+
title: locationInfo.title,
|
|
13598
|
+
referrer: locationInfo.referrer,
|
|
13599
|
+
referrer_domain: locationInfo.referrer_domain,
|
|
13600
|
+
utm_source: locationInfo.utm_source,
|
|
13601
|
+
utm_medium: locationInfo.utm_medium,
|
|
13602
|
+
utm_campaign: locationInfo.utm_campaign,
|
|
13603
|
+
utm_term: locationInfo.utm_term,
|
|
13604
|
+
utm_content: locationInfo.utm_content
|
|
13605
|
+
};
|
|
13606
|
+
}
|
|
13607
|
+
|
|
13608
|
+
/**
|
|
13609
|
+
* Property Manager for HumanBehavior SDK
|
|
13610
|
+
* Handles automatic properties, session properties, and user properties
|
|
13611
|
+
*/
|
|
13612
|
+
class PropertyManager {
|
|
13613
|
+
constructor(config = {}) {
|
|
13614
|
+
this.sessionProperties = {};
|
|
13615
|
+
this.userProperties = {};
|
|
13616
|
+
this.initialProperties = {};
|
|
13617
|
+
this.isInitialized = false;
|
|
13618
|
+
this.config = Object.assign({ enableAutomaticProperties: true, enableSessionProperties: true, enableUserProperties: true, propertyDenylist: [] }, config);
|
|
13619
|
+
this.automaticProperties = getAutomaticProperties();
|
|
13620
|
+
this.initialize();
|
|
13621
|
+
}
|
|
13622
|
+
/**
|
|
13623
|
+
* Initialize the property manager
|
|
13624
|
+
*/
|
|
13625
|
+
initialize() {
|
|
13626
|
+
if (this.isInitialized)
|
|
13627
|
+
return;
|
|
13628
|
+
// Capture initial properties once
|
|
13629
|
+
this.initialProperties = getInitialProperties();
|
|
13630
|
+
// Load session properties from sessionStorage
|
|
13631
|
+
this.loadSessionProperties();
|
|
13632
|
+
this.isInitialized = true;
|
|
13633
|
+
}
|
|
13634
|
+
/**
|
|
13635
|
+
* Get all properties for an event
|
|
13636
|
+
*/
|
|
13637
|
+
getEventProperties(eventProperties = {}) {
|
|
13638
|
+
const properties = Object.assign({}, eventProperties);
|
|
13639
|
+
// Add automatic properties
|
|
13640
|
+
if (this.config.enableAutomaticProperties) {
|
|
13641
|
+
Object.assign(properties, this.getAutomaticProperties());
|
|
13642
|
+
}
|
|
13643
|
+
// Add session properties
|
|
13644
|
+
if (this.config.enableSessionProperties) {
|
|
13645
|
+
Object.assign(properties, this.sessionProperties);
|
|
13646
|
+
}
|
|
13647
|
+
// Add user properties
|
|
13648
|
+
if (this.config.enableUserProperties) {
|
|
13649
|
+
Object.assign(properties, this.userProperties);
|
|
13650
|
+
}
|
|
13651
|
+
// Add initial properties (only once per session)
|
|
13652
|
+
if (!this.sessionProperties['$initial_properties_captured']) {
|
|
13653
|
+
Object.assign(properties, this.initialProperties);
|
|
13654
|
+
this.setSessionProperty('$initial_properties_captured', true);
|
|
13655
|
+
}
|
|
13656
|
+
// Apply denylist
|
|
13657
|
+
this.applyDenylist(properties);
|
|
13658
|
+
return properties;
|
|
13659
|
+
}
|
|
13660
|
+
/**
|
|
13661
|
+
* Get automatic properties
|
|
13662
|
+
*/
|
|
13663
|
+
getAutomaticProperties() {
|
|
13664
|
+
return Object.assign(Object.assign({}, this.automaticProperties), getCurrentPageProperties() // Always get fresh page properties
|
|
13665
|
+
);
|
|
13666
|
+
}
|
|
13667
|
+
/**
|
|
13668
|
+
* Get automatic properties with GeoIP data merged in
|
|
13669
|
+
*/
|
|
13670
|
+
getAutomaticPropertiesWithGeoIP(geoIPProperties = {}) {
|
|
13671
|
+
return Object.assign(Object.assign(Object.assign({}, this.automaticProperties), getCurrentPageProperties()), geoIPProperties);
|
|
13672
|
+
}
|
|
13673
|
+
/**
|
|
13674
|
+
* Set a session property
|
|
13675
|
+
*/
|
|
13676
|
+
setSessionProperty(key, value) {
|
|
13677
|
+
this.sessionProperties[key] = value;
|
|
13678
|
+
this.saveSessionProperties();
|
|
13679
|
+
}
|
|
13680
|
+
/**
|
|
13681
|
+
* Set multiple session properties
|
|
13682
|
+
*/
|
|
13683
|
+
setSessionProperties(properties) {
|
|
13684
|
+
Object.assign(this.sessionProperties, properties);
|
|
13685
|
+
this.saveSessionProperties();
|
|
13686
|
+
}
|
|
13687
|
+
/**
|
|
13688
|
+
* Get a session property
|
|
13689
|
+
*/
|
|
13690
|
+
getSessionProperty(key) {
|
|
13691
|
+
return this.sessionProperties[key];
|
|
13692
|
+
}
|
|
13693
|
+
/**
|
|
13694
|
+
* Remove a session property
|
|
13695
|
+
*/
|
|
13696
|
+
removeSessionProperty(key) {
|
|
13697
|
+
delete this.sessionProperties[key];
|
|
13698
|
+
this.saveSessionProperties();
|
|
13699
|
+
}
|
|
13700
|
+
/**
|
|
13701
|
+
* Set a user property
|
|
13702
|
+
*/
|
|
13703
|
+
setUserProperty(key, value) {
|
|
13704
|
+
this.userProperties[key] = value;
|
|
13705
|
+
}
|
|
13706
|
+
/**
|
|
13707
|
+
* Set multiple user properties
|
|
13708
|
+
*/
|
|
13709
|
+
setUserProperties(properties) {
|
|
13710
|
+
Object.assign(this.userProperties, properties);
|
|
13711
|
+
}
|
|
13712
|
+
/**
|
|
13713
|
+
* Get a user property
|
|
13714
|
+
*/
|
|
13715
|
+
getUserProperty(key) {
|
|
13716
|
+
return this.userProperties[key];
|
|
13717
|
+
}
|
|
13718
|
+
/**
|
|
13719
|
+
* Remove a user property
|
|
13720
|
+
*/
|
|
13721
|
+
removeUserProperty(key) {
|
|
13722
|
+
delete this.userProperties[key];
|
|
13723
|
+
}
|
|
13724
|
+
/**
|
|
13725
|
+
* Set a property only if it hasn't been set before
|
|
13726
|
+
*/
|
|
13727
|
+
setOnce(key, value, scope = 'user') {
|
|
13728
|
+
if (scope === 'session') {
|
|
13729
|
+
if (!(key in this.sessionProperties)) {
|
|
13730
|
+
this.setSessionProperty(key, value);
|
|
13731
|
+
}
|
|
13732
|
+
}
|
|
13733
|
+
else {
|
|
13734
|
+
if (!(key in this.userProperties)) {
|
|
13735
|
+
this.setUserProperty(key, value);
|
|
13736
|
+
}
|
|
13737
|
+
}
|
|
13738
|
+
}
|
|
13739
|
+
/**
|
|
13740
|
+
* Clear all session properties
|
|
13741
|
+
*/
|
|
13742
|
+
clearSessionProperties() {
|
|
13743
|
+
this.sessionProperties = {};
|
|
13744
|
+
this.saveSessionProperties();
|
|
13745
|
+
}
|
|
13746
|
+
/**
|
|
13747
|
+
* Clear all user properties
|
|
13748
|
+
*/
|
|
13749
|
+
clearUserProperties() {
|
|
13750
|
+
this.userProperties = {};
|
|
13751
|
+
}
|
|
13752
|
+
/**
|
|
13753
|
+
* Reset all properties
|
|
13754
|
+
*/
|
|
13755
|
+
reset() {
|
|
13756
|
+
this.clearSessionProperties();
|
|
13757
|
+
this.clearUserProperties();
|
|
13758
|
+
this.initialProperties = {};
|
|
13759
|
+
this.isInitialized = false;
|
|
13760
|
+
this.initialize();
|
|
13761
|
+
}
|
|
13762
|
+
/**
|
|
13763
|
+
* Load session properties from sessionStorage
|
|
13764
|
+
*/
|
|
13765
|
+
loadSessionProperties() {
|
|
13766
|
+
if (typeof sessionStorage === 'undefined')
|
|
13767
|
+
return;
|
|
13768
|
+
try {
|
|
13769
|
+
const stored = sessionStorage.getItem('hb_session_properties');
|
|
13770
|
+
if (stored) {
|
|
13771
|
+
this.sessionProperties = JSON.parse(stored);
|
|
13772
|
+
}
|
|
13773
|
+
}
|
|
13774
|
+
catch (error) {
|
|
13775
|
+
console.warn('Failed to load session properties:', error);
|
|
13776
|
+
}
|
|
13777
|
+
}
|
|
13778
|
+
/**
|
|
13779
|
+
* Save session properties to sessionStorage
|
|
13780
|
+
*/
|
|
13781
|
+
saveSessionProperties() {
|
|
13782
|
+
if (typeof sessionStorage === 'undefined')
|
|
13783
|
+
return;
|
|
13784
|
+
try {
|
|
13785
|
+
sessionStorage.setItem('hb_session_properties', JSON.stringify(this.sessionProperties));
|
|
13786
|
+
}
|
|
13787
|
+
catch (error) {
|
|
13788
|
+
console.warn('Failed to save session properties:', error);
|
|
13789
|
+
}
|
|
13790
|
+
}
|
|
13791
|
+
/**
|
|
13792
|
+
* Apply property denylist
|
|
13793
|
+
*/
|
|
13794
|
+
applyDenylist(properties) {
|
|
13795
|
+
if (!this.config.propertyDenylist || this.config.propertyDenylist.length === 0) {
|
|
13796
|
+
return;
|
|
13797
|
+
}
|
|
13798
|
+
this.config.propertyDenylist.forEach(deniedKey => {
|
|
13799
|
+
delete properties[deniedKey];
|
|
13800
|
+
});
|
|
13801
|
+
}
|
|
13802
|
+
/**
|
|
13803
|
+
* Update automatic properties (call when page changes)
|
|
13804
|
+
*/
|
|
13805
|
+
updateAutomaticProperties() {
|
|
13806
|
+
this.automaticProperties = getAutomaticProperties();
|
|
13807
|
+
}
|
|
13808
|
+
/**
|
|
13809
|
+
* Get all properties for debugging
|
|
13810
|
+
*/
|
|
13811
|
+
getAllProperties() {
|
|
13812
|
+
return {
|
|
13813
|
+
automatic: this.getAutomaticProperties(),
|
|
13814
|
+
session: Object.assign({}, this.sessionProperties),
|
|
13815
|
+
user: Object.assign({}, this.userProperties),
|
|
13816
|
+
initial: Object.assign({}, this.initialProperties)
|
|
13817
|
+
};
|
|
13818
|
+
}
|
|
13819
|
+
}
|
|
13820
|
+
|
|
13164
13821
|
// Check if we're in a browser environment
|
|
13165
13822
|
const isBrowser = typeof window !== 'undefined';
|
|
13166
13823
|
class HumanBehaviorTracker {
|
|
@@ -13172,7 +13829,7 @@ class HumanBehaviorTracker {
|
|
|
13172
13829
|
var _a;
|
|
13173
13830
|
// ✅ SUPPRESS COMMON RRWEB ERRORS FOR CLEAN CONSOLE
|
|
13174
13831
|
if (isBrowser && (options === null || options === void 0 ? void 0 : options.suppressConsoleErrors) !== false) {
|
|
13175
|
-
// Suppress canvas security errors
|
|
13832
|
+
// Suppress canvas security errors and network errors
|
|
13176
13833
|
const originalConsoleError = console.error;
|
|
13177
13834
|
console.error = (...args) => {
|
|
13178
13835
|
const message = args.join(' ');
|
|
@@ -13183,8 +13840,14 @@ class HumanBehaviorTracker {
|
|
|
13183
13840
|
message.includes('CORS') ||
|
|
13184
13841
|
message.includes('Access-Control-Allow-Origin') ||
|
|
13185
13842
|
message.includes('Failed to load resource') ||
|
|
13186
|
-
message.includes('net::ERR_BLOCKED_BY_CLIENT')
|
|
13187
|
-
|
|
13843
|
+
message.includes('net::ERR_BLOCKED_BY_CLIENT') ||
|
|
13844
|
+
message.includes('NetworkError when attempting to fetch resource') ||
|
|
13845
|
+
message.includes('Failed to fetch') ||
|
|
13846
|
+
message.includes('TypeError: NetworkError') ||
|
|
13847
|
+
message.includes('HumanBehavior ERROR') ||
|
|
13848
|
+
message.includes('Failed to track custom event') ||
|
|
13849
|
+
message.includes('Error sending custom event')) {
|
|
13850
|
+
// Silently suppress these common errors
|
|
13188
13851
|
return;
|
|
13189
13852
|
}
|
|
13190
13853
|
originalConsoleError.apply(console, args);
|
|
@@ -13198,8 +13861,12 @@ class HumanBehaviorTracker {
|
|
|
13198
13861
|
message.includes('CORS') ||
|
|
13199
13862
|
message.includes('Access-Control-Allow-Origin') ||
|
|
13200
13863
|
message.includes('Failed to load resource') ||
|
|
13201
|
-
message.includes('net::ERR_BLOCKED_BY_CLIENT')
|
|
13202
|
-
|
|
13864
|
+
message.includes('net::ERR_BLOCKED_BY_CLIENT') ||
|
|
13865
|
+
message.includes('NetworkError when attempting to fetch resource') ||
|
|
13866
|
+
message.includes('Failed to fetch') ||
|
|
13867
|
+
message.includes('Custom event network error') ||
|
|
13868
|
+
message.includes('Request blocked by ad blocker')) {
|
|
13869
|
+
// Silently suppress these common warnings
|
|
13203
13870
|
return;
|
|
13204
13871
|
}
|
|
13205
13872
|
originalConsoleWarn.apply(console, args);
|
|
@@ -13211,7 +13878,9 @@ class HumanBehaviorTracker {
|
|
|
13211
13878
|
message.includes('Tainted canvases') ||
|
|
13212
13879
|
message.includes('toDataURL') ||
|
|
13213
13880
|
message.includes('Cross-Origin') ||
|
|
13214
|
-
message.includes('CORS')
|
|
13881
|
+
message.includes('CORS') ||
|
|
13882
|
+
message.includes('NetworkError') ||
|
|
13883
|
+
message.includes('Failed to fetch')) {
|
|
13215
13884
|
event.preventDefault();
|
|
13216
13885
|
return false;
|
|
13217
13886
|
}
|
|
@@ -13227,7 +13896,10 @@ class HumanBehaviorTracker {
|
|
|
13227
13896
|
this.configureLogging({ level: options.logLevel });
|
|
13228
13897
|
}
|
|
13229
13898
|
// Create new tracker instance
|
|
13230
|
-
const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl
|
|
13899
|
+
const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl, {
|
|
13900
|
+
enableAutomaticProperties: options === null || options === void 0 ? void 0 : options.enableAutomaticProperties,
|
|
13901
|
+
propertyDenylist: options === null || options === void 0 ? void 0 : options.propertyDenylist
|
|
13902
|
+
});
|
|
13231
13903
|
// Store canvas recording preference
|
|
13232
13904
|
tracker.recordCanvas = (_a = options === null || options === void 0 ? void 0 : options.recordCanvas) !== null && _a !== void 0 ? _a : false;
|
|
13233
13905
|
// Set redacted fields if specified
|
|
@@ -13244,7 +13916,7 @@ class HumanBehaviorTracker {
|
|
|
13244
13916
|
tracker.start();
|
|
13245
13917
|
return tracker;
|
|
13246
13918
|
}
|
|
13247
|
-
constructor(apiKey, ingestionUrl) {
|
|
13919
|
+
constructor(apiKey, ingestionUrl, options) {
|
|
13248
13920
|
this.eventIngestionQueue = [];
|
|
13249
13921
|
this.userProperties = {};
|
|
13250
13922
|
this.isProcessing = false;
|
|
@@ -13282,6 +13954,11 @@ class HumanBehaviorTracker {
|
|
|
13282
13954
|
});
|
|
13283
13955
|
this.apiKey = apiKey;
|
|
13284
13956
|
this.redactionManager = new RedactionManager();
|
|
13957
|
+
// Initialize property manager
|
|
13958
|
+
this.propertyManager = new PropertyManager({
|
|
13959
|
+
enableAutomaticProperties: (options === null || options === void 0 ? void 0 : options.enableAutomaticProperties) !== false,
|
|
13960
|
+
propertyDenylist: (options === null || options === void 0 ? void 0 : options.propertyDenylist) || []
|
|
13961
|
+
});
|
|
13285
13962
|
// Handle session restoration with improved continuity
|
|
13286
13963
|
if (isBrowser) {
|
|
13287
13964
|
const existingSessionId = localStorage.getItem(`human_behavior_session_id_${this.apiKey}`);
|
|
@@ -13320,7 +13997,28 @@ class HumanBehaviorTracker {
|
|
|
13320
13997
|
try {
|
|
13321
13998
|
const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
|
|
13322
13999
|
logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
|
|
13323
|
-
|
|
14000
|
+
// Get automatic properties for init
|
|
14001
|
+
const automaticProperties = this.propertyManager.getAutomaticProperties();
|
|
14002
|
+
// Create a custom init request with automatic properties
|
|
14003
|
+
const initResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/init`, {
|
|
14004
|
+
method: 'POST',
|
|
14005
|
+
headers: {
|
|
14006
|
+
'Content-Type': 'application/json',
|
|
14007
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
14008
|
+
'Referer': document.referrer || ''
|
|
14009
|
+
},
|
|
14010
|
+
body: JSON.stringify({
|
|
14011
|
+
sessionId: this.sessionId,
|
|
14012
|
+
endUserId: userId,
|
|
14013
|
+
entryURL: window.location.href,
|
|
14014
|
+
referrer: document.referrer,
|
|
14015
|
+
automaticProperties: automaticProperties
|
|
14016
|
+
})
|
|
14017
|
+
});
|
|
14018
|
+
if (!initResponse.ok) {
|
|
14019
|
+
throw new Error(`Failed to initialize: ${initResponse.statusText}`);
|
|
14020
|
+
}
|
|
14021
|
+
const { sessionId, endUserId } = yield initResponse.json();
|
|
13324
14022
|
// Check if server returned a different session ID (for session continuity)
|
|
13325
14023
|
if (sessionId !== this.sessionId) {
|
|
13326
14024
|
logDebug(`Server returned different sessionId: ${sessionId} (client had: ${this.sessionId})`);
|
|
@@ -13332,6 +14030,10 @@ class HumanBehaviorTracker {
|
|
|
13332
14030
|
}
|
|
13333
14031
|
this.endUserId = endUserId;
|
|
13334
14032
|
this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
|
|
14033
|
+
// Send IP information after successful initialization
|
|
14034
|
+
this.api.sendIPInfo(this.sessionId).catch(error => {
|
|
14035
|
+
logWarn('Failed to send IP info:', error);
|
|
14036
|
+
});
|
|
13335
14037
|
// Only setup browser-specific handlers when in browser environment
|
|
13336
14038
|
if (isBrowser) {
|
|
13337
14039
|
this.setupPageUnloadHandler();
|
|
@@ -13452,6 +14154,8 @@ class HumanBehaviorTracker {
|
|
|
13452
14154
|
return __awaiter(this, void 0, void 0, function* () {
|
|
13453
14155
|
if (!this.initialized)
|
|
13454
14156
|
return;
|
|
14157
|
+
// Update automatic properties for new page
|
|
14158
|
+
this.propertyManager.updateAutomaticProperties();
|
|
13455
14159
|
try {
|
|
13456
14160
|
const pageViewData = {
|
|
13457
14161
|
url: url || window.location.href,
|
|
@@ -13461,11 +14165,13 @@ class HumanBehaviorTracker {
|
|
|
13461
14165
|
referrer: document.referrer,
|
|
13462
14166
|
timestamp: new Date().toISOString()
|
|
13463
14167
|
};
|
|
14168
|
+
// Get enhanced properties with automatic properties
|
|
14169
|
+
const enhancedProperties = this.propertyManager.getEventProperties(pageViewData);
|
|
13464
14170
|
// Add pageview event to the main event stream
|
|
13465
14171
|
yield this.addEvent({
|
|
13466
14172
|
type: 5, // Custom event type
|
|
13467
14173
|
data: {
|
|
13468
|
-
payload: Object.assign({ eventType: 'pageview' },
|
|
14174
|
+
payload: Object.assign({ eventType: 'pageview' }, enhancedProperties)
|
|
13469
14175
|
},
|
|
13470
14176
|
timestamp: Date.now()
|
|
13471
14177
|
});
|
|
@@ -13481,10 +14187,12 @@ class HumanBehaviorTracker {
|
|
|
13481
14187
|
var _a, _b, _c, _d, _e;
|
|
13482
14188
|
if (!this.initialized)
|
|
13483
14189
|
return;
|
|
14190
|
+
// Get enhanced properties with automatic properties
|
|
14191
|
+
const enhancedProperties = this.propertyManager.getEventProperties(properties);
|
|
13484
14192
|
try {
|
|
13485
14193
|
// Send custom event directly to the API
|
|
13486
|
-
yield this.api.sendCustomEvent(this.sessionId, eventName,
|
|
13487
|
-
logDebug(`Custom event tracked: ${eventName}`,
|
|
14194
|
+
yield this.api.sendCustomEvent(this.sessionId, eventName, enhancedProperties);
|
|
14195
|
+
logDebug(`Custom event tracked: ${eventName}`, enhancedProperties);
|
|
13488
14196
|
}
|
|
13489
14197
|
catch (error) {
|
|
13490
14198
|
logError('Failed to track custom event:', error);
|
|
@@ -13504,7 +14212,7 @@ class HumanBehaviorTracker {
|
|
|
13504
14212
|
try {
|
|
13505
14213
|
const customEventData = {
|
|
13506
14214
|
eventName: eventName,
|
|
13507
|
-
properties:
|
|
14215
|
+
properties: enhancedProperties || {},
|
|
13508
14216
|
timestamp: new Date().toISOString(),
|
|
13509
14217
|
url: window.location.href,
|
|
13510
14218
|
pathname: window.location.pathname
|
|
@@ -13804,8 +14512,25 @@ class HumanBehaviorTracker {
|
|
|
13804
14512
|
// Store user properties
|
|
13805
14513
|
this.userProperties = userProperties;
|
|
13806
14514
|
logDebug('Identifying user:', { userProperties, originalEndUserId, sessionId: this.sessionId });
|
|
13807
|
-
//
|
|
13808
|
-
|
|
14515
|
+
// Get automatic properties and send with user data
|
|
14516
|
+
const automaticProperties = this.propertyManager.getAutomaticProperties();
|
|
14517
|
+
// Create a custom user request with automatic properties
|
|
14518
|
+
const userResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/user`, {
|
|
14519
|
+
method: 'POST',
|
|
14520
|
+
headers: {
|
|
14521
|
+
'Content-Type': 'application/json',
|
|
14522
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
14523
|
+
},
|
|
14524
|
+
body: JSON.stringify({
|
|
14525
|
+
userId: originalEndUserId,
|
|
14526
|
+
userAttributes: userProperties,
|
|
14527
|
+
sessionId: this.sessionId,
|
|
14528
|
+
automaticProperties: automaticProperties
|
|
14529
|
+
})
|
|
14530
|
+
});
|
|
14531
|
+
if (!userResponse.ok) {
|
|
14532
|
+
throw new Error(`Failed to identify user: ${userResponse.statusText}`);
|
|
14533
|
+
}
|
|
13809
14534
|
// Don't update endUserId - keep it as the original UUID
|
|
13810
14535
|
return originalEndUserId || '';
|
|
13811
14536
|
});
|
|
@@ -13853,7 +14578,7 @@ class HumanBehaviorTracker {
|
|
|
13853
14578
|
collectFonts: false, // Disable font collection to reduce errors
|
|
13854
14579
|
inlineStylesheet: true, // Keep styles for proper session replay
|
|
13855
14580
|
recordCrossOriginIframes: false, // Prevent cross-origin iframe errors
|
|
13856
|
-
// ✅ CANVAS RECORDING -
|
|
14581
|
+
// ✅ CANVAS RECORDING - protection against overwhelm
|
|
13857
14582
|
recordCanvas: this.recordCanvas, // Opt-in only
|
|
13858
14583
|
sampling: this.recordCanvas ? { canvas: 4 } : undefined, // 4 FPS throttle
|
|
13859
14584
|
dataURLOptions: this.recordCanvas ? {
|
|
@@ -14277,6 +15002,79 @@ class HumanBehaviorTracker {
|
|
|
14277
15002
|
initialized: this.initialized
|
|
14278
15003
|
};
|
|
14279
15004
|
}
|
|
15005
|
+
// ===== PROPERTY MANAGEMENT METHODS =====
|
|
15006
|
+
/**
|
|
15007
|
+
* Set a session property that will be included in all events for this session
|
|
15008
|
+
*/
|
|
15009
|
+
setSessionProperty(key, value) {
|
|
15010
|
+
this.propertyManager.setSessionProperty(key, value);
|
|
15011
|
+
}
|
|
15012
|
+
/**
|
|
15013
|
+
* Set multiple session properties
|
|
15014
|
+
*/
|
|
15015
|
+
setSessionProperties(properties) {
|
|
15016
|
+
this.propertyManager.setSessionProperties(properties);
|
|
15017
|
+
}
|
|
15018
|
+
/**
|
|
15019
|
+
* Get a session property
|
|
15020
|
+
*/
|
|
15021
|
+
getSessionProperty(key) {
|
|
15022
|
+
return this.propertyManager.getSessionProperty(key);
|
|
15023
|
+
}
|
|
15024
|
+
/**
|
|
15025
|
+
* Remove a session property
|
|
15026
|
+
*/
|
|
15027
|
+
removeSessionProperty(key) {
|
|
15028
|
+
this.propertyManager.removeSessionProperty(key);
|
|
15029
|
+
}
|
|
15030
|
+
/**
|
|
15031
|
+
* Set a user property that will be included in all events
|
|
15032
|
+
*/
|
|
15033
|
+
setUserProperty(key, value) {
|
|
15034
|
+
this.propertyManager.setUserProperty(key, value);
|
|
15035
|
+
}
|
|
15036
|
+
/**
|
|
15037
|
+
* Set multiple user properties
|
|
15038
|
+
*/
|
|
15039
|
+
setUserProperties(properties) {
|
|
15040
|
+
this.propertyManager.setUserProperties(properties);
|
|
15041
|
+
}
|
|
15042
|
+
/**
|
|
15043
|
+
* Get a user property
|
|
15044
|
+
*/
|
|
15045
|
+
getUserProperty(key) {
|
|
15046
|
+
return this.propertyManager.getUserProperty(key);
|
|
15047
|
+
}
|
|
15048
|
+
/**
|
|
15049
|
+
* Remove a user property
|
|
15050
|
+
*/
|
|
15051
|
+
removeUserProperty(key) {
|
|
15052
|
+
this.propertyManager.removeUserProperty(key);
|
|
15053
|
+
}
|
|
15054
|
+
/**
|
|
15055
|
+
* Set a property only if it hasn't been set before
|
|
15056
|
+
*/
|
|
15057
|
+
setOnce(key, value, scope = 'user') {
|
|
15058
|
+
this.propertyManager.setOnce(key, value, scope);
|
|
15059
|
+
}
|
|
15060
|
+
/**
|
|
15061
|
+
* Clear all session properties
|
|
15062
|
+
*/
|
|
15063
|
+
clearSessionProperties() {
|
|
15064
|
+
this.propertyManager.clearSessionProperties();
|
|
15065
|
+
}
|
|
15066
|
+
/**
|
|
15067
|
+
* Clear all user properties
|
|
15068
|
+
*/
|
|
15069
|
+
clearUserProperties() {
|
|
15070
|
+
this.propertyManager.clearUserProperties();
|
|
15071
|
+
}
|
|
15072
|
+
/**
|
|
15073
|
+
* Get all properties for debugging
|
|
15074
|
+
*/
|
|
15075
|
+
getAllProperties() {
|
|
15076
|
+
return this.propertyManager.getAllProperties();
|
|
15077
|
+
}
|
|
14280
15078
|
}
|
|
14281
15079
|
// Only expose to window object in browser environments
|
|
14282
15080
|
if (isBrowser) {
|