humanbehavior-js 0.0.9 → 0.1.0
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/index.js +345 -184
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/index.js +51 -77
- package/dist/cjs/react/index.js.map +1 -1
- package/dist/esm/index.js +345 -184
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/index.js +48 -77
- package/dist/esm/react/index.js.map +1 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +1 -1
- package/dist/types/index.d.ts +60 -12
- package/dist/types/react/index.d.ts +8 -9
- package/package.json +2 -1
- package/readme.md +127 -105
- package/simple-spa.html +544 -0
- package/src/api.ts +4 -134
- package/src/react/index.tsx +61 -28
- package/src/tracker.ts +404 -87
package/dist/cjs/index.js
CHANGED
|
@@ -4359,129 +4359,16 @@ class HumanBehaviorAPI {
|
|
|
4359
4359
|
}
|
|
4360
4360
|
});
|
|
4361
4361
|
}
|
|
4362
|
-
|
|
4363
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4364
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/sessionComplete`, {
|
|
4365
|
-
method: 'POST',
|
|
4366
|
-
headers: {
|
|
4367
|
-
'Content-Type': 'application/json',
|
|
4368
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4369
|
-
},
|
|
4370
|
-
body: JSON.stringify({ sessionId })
|
|
4371
|
-
});
|
|
4372
|
-
if (!response.ok) {
|
|
4373
|
-
throw new Error(`Failed to send session complete: ${response.statusText}`);
|
|
4374
|
-
}
|
|
4375
|
-
});
|
|
4376
|
-
}
|
|
4377
|
-
sendCustomEvent(eventName, eventProperties, sessionId) {
|
|
4378
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4379
|
-
const maxRetries = 3;
|
|
4380
|
-
let retryCount = 0;
|
|
4381
|
-
while (retryCount < maxRetries) {
|
|
4382
|
-
try {
|
|
4383
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/customEvent`, {
|
|
4384
|
-
method: 'POST',
|
|
4385
|
-
headers: {
|
|
4386
|
-
'Content-Type': 'application/json',
|
|
4387
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4388
|
-
},
|
|
4389
|
-
body: JSON.stringify({
|
|
4390
|
-
name: eventName,
|
|
4391
|
-
properties: eventProperties,
|
|
4392
|
-
sessionId: sessionId,
|
|
4393
|
-
timestamp: new Date().toISOString()
|
|
4394
|
-
})
|
|
4395
|
-
});
|
|
4396
|
-
if (!response.ok) {
|
|
4397
|
-
throw new Error(`Failed to send custom event: ${response.statusText}`);
|
|
4398
|
-
}
|
|
4399
|
-
return yield response.json();
|
|
4400
|
-
}
|
|
4401
|
-
catch (error) {
|
|
4402
|
-
retryCount++;
|
|
4403
|
-
if (retryCount === maxRetries) {
|
|
4404
|
-
logError('Error sending custom event after max retries:', error);
|
|
4405
|
-
throw error;
|
|
4406
|
-
}
|
|
4407
|
-
// Exponential backoff
|
|
4408
|
-
yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
|
|
4409
|
-
}
|
|
4410
|
-
}
|
|
4411
|
-
});
|
|
4412
|
-
}
|
|
4413
|
-
sendCustomEvents(events, sessionId) {
|
|
4414
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4415
|
-
const maxRetries = 3;
|
|
4416
|
-
let retryCount = 0;
|
|
4417
|
-
while (retryCount < maxRetries) {
|
|
4418
|
-
try {
|
|
4419
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/customEvent/batch`, {
|
|
4420
|
-
method: 'POST',
|
|
4421
|
-
headers: {
|
|
4422
|
-
'Content-Type': 'application/json',
|
|
4423
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4424
|
-
},
|
|
4425
|
-
body: JSON.stringify({
|
|
4426
|
-
events: events.map(event => (Object.assign(Object.assign({}, event), { sessionId: sessionId })))
|
|
4427
|
-
})
|
|
4428
|
-
});
|
|
4429
|
-
if (!response.ok) {
|
|
4430
|
-
throw new Error(`Failed to send custom events: ${response.statusText}`);
|
|
4431
|
-
}
|
|
4432
|
-
return yield response.json();
|
|
4433
|
-
}
|
|
4434
|
-
catch (error) {
|
|
4435
|
-
retryCount++;
|
|
4436
|
-
if (retryCount === maxRetries) {
|
|
4437
|
-
logError('Error sending custom events after max retries:', error);
|
|
4438
|
-
throw error;
|
|
4439
|
-
}
|
|
4440
|
-
// Exponential backoff
|
|
4441
|
-
yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
|
|
4442
|
-
}
|
|
4443
|
-
}
|
|
4444
|
-
});
|
|
4445
|
-
}
|
|
4446
|
-
sendBeaconEvents(events, sessionId, isSessionComplete = false) {
|
|
4362
|
+
sendBeaconEvents(events, sessionId) {
|
|
4447
4363
|
const data = new URLSearchParams();
|
|
4448
4364
|
data.append('events', encodeURIComponent(JSON.stringify(events)));
|
|
4449
4365
|
data.append('sessionId', encodeURIComponent(sessionId));
|
|
4450
4366
|
data.append('timestamp', encodeURIComponent(Date.now().toString()));
|
|
4451
4367
|
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4452
|
-
if (isSessionComplete) {
|
|
4453
|
-
logInfo('Session complete beacon sending');
|
|
4454
|
-
localStorage.setItem('koalaware_session_complete', Date.now().toString());
|
|
4455
|
-
data.append('sessionComplete', encodeURIComponent('true'));
|
|
4456
|
-
}
|
|
4457
4368
|
navigator.sendBeacon(`${this.baseUrl}/api/ingestion/events`, data);
|
|
4458
4369
|
// KoalawareTracker.logToStorage(`Sending events beacon: ${this.baseUrl}/api/ingestion/events`);
|
|
4459
4370
|
// KoalawareTracker.logToStorage(`Events beacon success: ${success}`);
|
|
4460
4371
|
}
|
|
4461
|
-
sendBeaconSessionComplete(sessionId) {
|
|
4462
|
-
const data = new URLSearchParams();
|
|
4463
|
-
data.append('sessionId', sessionId);
|
|
4464
|
-
data.append('apiKey', this.apiKey);
|
|
4465
|
-
data.append('sessionComplete', 'true');
|
|
4466
|
-
navigator.sendBeacon(`${this.baseUrl}/api/ingestion/sessionComplete`, data);
|
|
4467
|
-
// KoalawareTracker.logToStorage(`Sending completion beacon: ${this.baseUrl}/api/ingestion/sessionComplete`);
|
|
4468
|
-
// KoalawareTracker.logToStorage(`Complete beacon success: ${success}`);
|
|
4469
|
-
}
|
|
4470
|
-
sendBeaconCustomEvent(eventName, eventProperties, sessionId) {
|
|
4471
|
-
const data = new URLSearchParams();
|
|
4472
|
-
data.append('name', encodeURIComponent(eventName));
|
|
4473
|
-
data.append('properties', encodeURIComponent(JSON.stringify(eventProperties)));
|
|
4474
|
-
data.append('sessionId', encodeURIComponent(sessionId));
|
|
4475
|
-
data.append('timestamp', encodeURIComponent(new Date().toISOString()));
|
|
4476
|
-
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4477
|
-
return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/customEvent`, data);
|
|
4478
|
-
}
|
|
4479
|
-
sendBeaconCustomEvents(events, sessionId) {
|
|
4480
|
-
const data = new URLSearchParams();
|
|
4481
|
-
data.append('events', encodeURIComponent(JSON.stringify(events.map(event => (Object.assign(Object.assign({}, event), { sessionId: sessionId }))))));
|
|
4482
|
-
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4483
|
-
return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/customEvent/batch`, data);
|
|
4484
|
-
}
|
|
4485
4372
|
}
|
|
4486
4373
|
|
|
4487
4374
|
// Redaction functionality for sensitive input fields
|
|
@@ -4900,6 +4787,40 @@ const redactionManager = new RedactionManager();
|
|
|
4900
4787
|
// Check if we're in a browser environment
|
|
4901
4788
|
const isBrowser = typeof window !== 'undefined';
|
|
4902
4789
|
class HumanBehaviorTracker {
|
|
4790
|
+
/**
|
|
4791
|
+
* Initialize the HumanBehavior tracker
|
|
4792
|
+
* This is the main entry point - call this once per page
|
|
4793
|
+
*/
|
|
4794
|
+
static init(apiKey, options) {
|
|
4795
|
+
// Return existing instance if already initialized
|
|
4796
|
+
if (isBrowser && window.__humanBehaviorGlobalTracker) {
|
|
4797
|
+
logDebug('Tracker already initialized, returning existing instance');
|
|
4798
|
+
return window.__humanBehaviorGlobalTracker;
|
|
4799
|
+
}
|
|
4800
|
+
// Configure logging if specified
|
|
4801
|
+
if (options === null || options === void 0 ? void 0 : options.logLevel) {
|
|
4802
|
+
this.configureLogging({ level: options.logLevel });
|
|
4803
|
+
}
|
|
4804
|
+
// Create new tracker instance
|
|
4805
|
+
const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl);
|
|
4806
|
+
// Set redacted fields if specified
|
|
4807
|
+
if (options === null || options === void 0 ? void 0 : options.redactFields) {
|
|
4808
|
+
tracker.setRedactedFields(options.redactFields);
|
|
4809
|
+
}
|
|
4810
|
+
// Test connection (non-blocking)
|
|
4811
|
+
if (isBrowser) {
|
|
4812
|
+
const testUrl = tracker.api['baseUrl'] + '/api/ingestion/health';
|
|
4813
|
+
fetch(testUrl, { method: 'HEAD' })
|
|
4814
|
+
.then(() => logDebug('Connection test successful'))
|
|
4815
|
+
.catch((error) => {
|
|
4816
|
+
logWarn('Connection test failed - ad blocker may be active:', error.message);
|
|
4817
|
+
tracker._connectionBlocked = true;
|
|
4818
|
+
});
|
|
4819
|
+
}
|
|
4820
|
+
// Start tracking
|
|
4821
|
+
tracker.start();
|
|
4822
|
+
return tracker;
|
|
4823
|
+
}
|
|
4903
4824
|
constructor(apiKey, ingestionUrl) {
|
|
4904
4825
|
this.eventIngestionQueue = [];
|
|
4905
4826
|
this.queueSizeBytes = 0;
|
|
@@ -4914,60 +4835,77 @@ class HumanBehaviorTracker {
|
|
|
4914
4835
|
this.initializationPromise = null;
|
|
4915
4836
|
// Console tracking properties
|
|
4916
4837
|
this.originalConsole = null;
|
|
4917
|
-
this.originalLogger = null;
|
|
4918
4838
|
this.consoleTrackingEnabled = false;
|
|
4839
|
+
// Navigation tracking properties
|
|
4840
|
+
this.navigationTrackingEnabled = false;
|
|
4841
|
+
this.currentUrl = '';
|
|
4842
|
+
this.previousUrl = '';
|
|
4843
|
+
this.originalPushState = null;
|
|
4844
|
+
this.originalReplaceState = null;
|
|
4845
|
+
this.navigationListeners = [];
|
|
4846
|
+
this._connectionBlocked = false;
|
|
4919
4847
|
if (!apiKey) {
|
|
4920
4848
|
throw new Error('Human Behavior API Key is required');
|
|
4921
4849
|
}
|
|
4922
|
-
//
|
|
4923
|
-
|
|
4924
|
-
// ========================================
|
|
4925
|
-
// Uncomment ONE of the following lines to select your server:
|
|
4926
|
-
// AWS Development Server
|
|
4927
|
-
const defaultIngestionUrl = 'http://3.137.217.33:3000';
|
|
4928
|
-
// Vercel Production Server
|
|
4929
|
-
// const defaultIngestionUrl = 'https://ingestion-server.vercel.app';
|
|
4930
|
-
// Local Development Server
|
|
4931
|
-
// const defaultIngestionUrl = 'http://localhost:3000';
|
|
4850
|
+
// Initialize API
|
|
4851
|
+
const defaultIngestionUrl = 'http://3.137.217.33:3000'; // AWS Development Server
|
|
4932
4852
|
this.api = new HumanBehaviorAPI({
|
|
4933
4853
|
apiKey: apiKey,
|
|
4934
4854
|
ingestionUrl: ingestionUrl || defaultIngestionUrl
|
|
4935
4855
|
});
|
|
4936
4856
|
this.apiKey = apiKey;
|
|
4937
4857
|
this.redactionManager = new RedactionManager();
|
|
4938
|
-
//
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4858
|
+
// Handle session restoration
|
|
4859
|
+
if (isBrowser) {
|
|
4860
|
+
const existingSessionId = localStorage.getItem('human_behavior_session_id');
|
|
4861
|
+
const lastActivity = localStorage.getItem('human_behavior_last_activity');
|
|
4862
|
+
const thirtyMinutesAgo = Date.now() - (30 * 60 * 1000);
|
|
4863
|
+
if (existingSessionId && lastActivity && parseInt(lastActivity) > thirtyMinutesAgo) {
|
|
4864
|
+
this.sessionId = existingSessionId;
|
|
4865
|
+
logDebug(`Reusing existing session: ${this.sessionId}`);
|
|
4866
|
+
}
|
|
4867
|
+
else {
|
|
4868
|
+
this.sessionId = v1();
|
|
4869
|
+
logDebug(`Creating new session: ${this.sessionId}`);
|
|
4870
|
+
localStorage.setItem('human_behavior_session_id', this.sessionId);
|
|
4871
|
+
}
|
|
4872
|
+
this.currentUrl = window.location.href;
|
|
4873
|
+
window.__humanBehaviorGlobalTracker = this;
|
|
4874
|
+
}
|
|
4875
|
+
else {
|
|
4876
|
+
this.sessionId = v1();
|
|
4948
4877
|
}
|
|
4949
|
-
// Start initialization
|
|
4878
|
+
// Start initialization
|
|
4950
4879
|
this.initializationPromise = this.init();
|
|
4951
4880
|
}
|
|
4952
4881
|
init() {
|
|
4953
4882
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4954
4883
|
try {
|
|
4955
4884
|
const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
|
|
4885
|
+
logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
|
|
4956
4886
|
const { sessionId, endUserId } = yield this.api.init(this.sessionId, userId);
|
|
4957
|
-
|
|
4887
|
+
// Check if server returned a different session ID
|
|
4888
|
+
if (sessionId !== this.sessionId) {
|
|
4889
|
+
logDebug(`Server returned different sessionId: ${sessionId} (client had: ${this.sessionId})`);
|
|
4890
|
+
this.sessionId = sessionId;
|
|
4891
|
+
// Update localStorage with server's session ID
|
|
4892
|
+
if (isBrowser) {
|
|
4893
|
+
localStorage.setItem('human_behavior_session_id', this.sessionId);
|
|
4894
|
+
}
|
|
4895
|
+
}
|
|
4958
4896
|
this.endUserId = endUserId;
|
|
4959
4897
|
this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
|
|
4960
4898
|
// Only setup browser-specific handlers when in browser environment
|
|
4961
4899
|
if (isBrowser) {
|
|
4962
4900
|
this.setupPageUnloadHandler();
|
|
4963
|
-
this.
|
|
4901
|
+
this.setupNavigationTracking();
|
|
4964
4902
|
this.processRejectedEvents();
|
|
4965
4903
|
}
|
|
4966
4904
|
else {
|
|
4967
4905
|
logWarn('HumanBehaviorTracker initialized in a non-browser environment. Session tracking is disabled.');
|
|
4968
4906
|
}
|
|
4969
4907
|
this.initialized = true;
|
|
4970
|
-
logInfo(
|
|
4908
|
+
logInfo(`HumanBehaviorTracker initialized with sessionId: ${this.sessionId}, endUserId: ${endUserId}`);
|
|
4971
4909
|
}
|
|
4972
4910
|
catch (error) {
|
|
4973
4911
|
logError('Failed to initialize HumanBehaviorTracker:', error);
|
|
@@ -4983,6 +4921,171 @@ class HumanBehaviorTracker {
|
|
|
4983
4921
|
yield this.initializationPromise;
|
|
4984
4922
|
});
|
|
4985
4923
|
}
|
|
4924
|
+
/**
|
|
4925
|
+
* Setup navigation event tracking for SPA navigation
|
|
4926
|
+
*/
|
|
4927
|
+
setupNavigationTracking() {
|
|
4928
|
+
if (!isBrowser || this.navigationTrackingEnabled)
|
|
4929
|
+
return;
|
|
4930
|
+
this.navigationTrackingEnabled = true;
|
|
4931
|
+
logDebug('Setting up navigation tracking');
|
|
4932
|
+
// Store original history methods
|
|
4933
|
+
this.originalPushState = history.pushState;
|
|
4934
|
+
this.originalReplaceState = history.replaceState;
|
|
4935
|
+
// Override pushState to capture programmatic navigation
|
|
4936
|
+
history.pushState = (...args) => {
|
|
4937
|
+
this.previousUrl = this.currentUrl;
|
|
4938
|
+
this.currentUrl = window.location.href;
|
|
4939
|
+
// Call original method
|
|
4940
|
+
this.originalPushState.apply(history, args);
|
|
4941
|
+
// Track navigation event
|
|
4942
|
+
this.trackNavigationEvent('pushState', this.previousUrl, this.currentUrl);
|
|
4943
|
+
};
|
|
4944
|
+
// Override replaceState to capture programmatic navigation
|
|
4945
|
+
history.replaceState = (...args) => {
|
|
4946
|
+
this.previousUrl = this.currentUrl;
|
|
4947
|
+
this.currentUrl = window.location.href;
|
|
4948
|
+
// Call original method
|
|
4949
|
+
this.originalReplaceState.apply(history, args);
|
|
4950
|
+
// Track navigation event
|
|
4951
|
+
this.trackNavigationEvent('replaceState', this.previousUrl, this.currentUrl);
|
|
4952
|
+
};
|
|
4953
|
+
// Listen for popstate events (back/forward navigation)
|
|
4954
|
+
const popstateListener = () => {
|
|
4955
|
+
this.previousUrl = this.currentUrl;
|
|
4956
|
+
this.currentUrl = window.location.href;
|
|
4957
|
+
this.trackNavigationEvent('popstate', this.previousUrl, this.currentUrl);
|
|
4958
|
+
};
|
|
4959
|
+
window.addEventListener('popstate', popstateListener);
|
|
4960
|
+
this.navigationListeners.push(() => {
|
|
4961
|
+
window.removeEventListener('popstate', popstateListener);
|
|
4962
|
+
});
|
|
4963
|
+
// Listen for hashchange events
|
|
4964
|
+
const hashchangeListener = () => {
|
|
4965
|
+
this.previousUrl = this.currentUrl;
|
|
4966
|
+
this.currentUrl = window.location.href;
|
|
4967
|
+
this.trackNavigationEvent('hashchange', this.previousUrl, this.currentUrl);
|
|
4968
|
+
};
|
|
4969
|
+
window.addEventListener('hashchange', hashchangeListener);
|
|
4970
|
+
this.navigationListeners.push(() => {
|
|
4971
|
+
window.removeEventListener('hashchange', hashchangeListener);
|
|
4972
|
+
});
|
|
4973
|
+
// Track initial page load
|
|
4974
|
+
this.trackNavigationEvent('pageLoad', '', this.currentUrl);
|
|
4975
|
+
}
|
|
4976
|
+
/**
|
|
4977
|
+
* Track navigation events and send custom events
|
|
4978
|
+
*/
|
|
4979
|
+
trackNavigationEvent(type, fromUrl, toUrl) {
|
|
4980
|
+
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4981
|
+
if (!this.initialized)
|
|
4982
|
+
return;
|
|
4983
|
+
try {
|
|
4984
|
+
const navigationData = {
|
|
4985
|
+
type: type,
|
|
4986
|
+
from: fromUrl,
|
|
4987
|
+
to: toUrl,
|
|
4988
|
+
timestamp: new Date().toISOString(),
|
|
4989
|
+
pathname: window.location.pathname,
|
|
4990
|
+
search: window.location.search,
|
|
4991
|
+
hash: window.location.hash,
|
|
4992
|
+
referrer: document.referrer
|
|
4993
|
+
};
|
|
4994
|
+
// Add navigation event to the main event stream
|
|
4995
|
+
yield this.addEvent({
|
|
4996
|
+
type: 5, // Custom event type
|
|
4997
|
+
data: {
|
|
4998
|
+
payload: Object.assign({ eventType: 'navigation' }, navigationData)
|
|
4999
|
+
},
|
|
5000
|
+
timestamp: Date.now()
|
|
5001
|
+
});
|
|
5002
|
+
logDebug(`Navigation tracked: ${type} from ${fromUrl} to ${toUrl}`);
|
|
5003
|
+
}
|
|
5004
|
+
catch (error) {
|
|
5005
|
+
logError('Failed to track navigation event:', error);
|
|
5006
|
+
}
|
|
5007
|
+
});
|
|
5008
|
+
}
|
|
5009
|
+
/**
|
|
5010
|
+
* Track a page view event (PostHog-style)
|
|
5011
|
+
*/
|
|
5012
|
+
trackPageView(url) {
|
|
5013
|
+
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5014
|
+
if (!this.initialized)
|
|
5015
|
+
return;
|
|
5016
|
+
try {
|
|
5017
|
+
const pageViewData = {
|
|
5018
|
+
url: url || window.location.href,
|
|
5019
|
+
pathname: window.location.pathname,
|
|
5020
|
+
search: window.location.search,
|
|
5021
|
+
hash: window.location.hash,
|
|
5022
|
+
referrer: document.referrer,
|
|
5023
|
+
timestamp: new Date().toISOString()
|
|
5024
|
+
};
|
|
5025
|
+
// Add pageview event to the main event stream
|
|
5026
|
+
yield this.addEvent({
|
|
5027
|
+
type: 5, // Custom event type
|
|
5028
|
+
data: {
|
|
5029
|
+
payload: Object.assign({ eventType: 'pageview' }, pageViewData)
|
|
5030
|
+
},
|
|
5031
|
+
timestamp: Date.now()
|
|
5032
|
+
});
|
|
5033
|
+
logDebug(`Pageview tracked: ${pageViewData.url}`);
|
|
5034
|
+
}
|
|
5035
|
+
catch (error) {
|
|
5036
|
+
logError('Failed to track pageview event:', error);
|
|
5037
|
+
}
|
|
5038
|
+
});
|
|
5039
|
+
}
|
|
5040
|
+
/**
|
|
5041
|
+
* Track a custom event (PostHog-style)
|
|
5042
|
+
*/
|
|
5043
|
+
customEvent(eventName, properties) {
|
|
5044
|
+
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5045
|
+
if (!this.initialized)
|
|
5046
|
+
return;
|
|
5047
|
+
try {
|
|
5048
|
+
const customEventData = {
|
|
5049
|
+
eventName: eventName,
|
|
5050
|
+
properties: properties || {},
|
|
5051
|
+
timestamp: new Date().toISOString(),
|
|
5052
|
+
url: window.location.href,
|
|
5053
|
+
pathname: window.location.pathname
|
|
5054
|
+
};
|
|
5055
|
+
// Add custom event to the main event stream
|
|
5056
|
+
yield this.addEvent({
|
|
5057
|
+
type: 5, // Custom event type
|
|
5058
|
+
data: {
|
|
5059
|
+
payload: Object.assign({ eventType: 'custom' }, customEventData)
|
|
5060
|
+
},
|
|
5061
|
+
timestamp: Date.now()
|
|
5062
|
+
});
|
|
5063
|
+
logDebug(`Custom event tracked: ${eventName}`, properties);
|
|
5064
|
+
}
|
|
5065
|
+
catch (error) {
|
|
5066
|
+
logError('Failed to track custom event:', error);
|
|
5067
|
+
}
|
|
5068
|
+
});
|
|
5069
|
+
}
|
|
5070
|
+
/**
|
|
5071
|
+
* Cleanup navigation tracking
|
|
5072
|
+
*/
|
|
5073
|
+
cleanupNavigationTracking() {
|
|
5074
|
+
if (!this.navigationTrackingEnabled)
|
|
5075
|
+
return;
|
|
5076
|
+
// Restore original history methods
|
|
5077
|
+
if (this.originalPushState) {
|
|
5078
|
+
history.pushState = this.originalPushState;
|
|
5079
|
+
}
|
|
5080
|
+
if (this.originalReplaceState) {
|
|
5081
|
+
history.replaceState = this.originalReplaceState;
|
|
5082
|
+
}
|
|
5083
|
+
// Remove event listeners
|
|
5084
|
+
this.navigationListeners.forEach(cleanup => cleanup());
|
|
5085
|
+
this.navigationListeners = [];
|
|
5086
|
+
this.navigationTrackingEnabled = false;
|
|
5087
|
+
logDebug('Navigation tracking cleaned up');
|
|
5088
|
+
}
|
|
4986
5089
|
static logToStorage(message) {
|
|
4987
5090
|
logInfo(message);
|
|
4988
5091
|
}
|
|
@@ -5016,13 +5119,6 @@ class HumanBehaviorTracker {
|
|
|
5016
5119
|
warn: console.warn,
|
|
5017
5120
|
error: console.error
|
|
5018
5121
|
};
|
|
5019
|
-
// Store original logger methods
|
|
5020
|
-
this.originalLogger = {
|
|
5021
|
-
error: logError,
|
|
5022
|
-
warn: logWarn,
|
|
5023
|
-
info: logInfo,
|
|
5024
|
-
debug: logDebug
|
|
5025
|
-
};
|
|
5026
5122
|
// Override console methods to capture ALL console output (including logger output)
|
|
5027
5123
|
console.log = (...args) => {
|
|
5028
5124
|
this.trackConsoleEvent('log', args);
|
|
@@ -5037,44 +5133,47 @@ class HumanBehaviorTracker {
|
|
|
5037
5133
|
this.originalConsole.error(...args);
|
|
5038
5134
|
};
|
|
5039
5135
|
this.consoleTrackingEnabled = true;
|
|
5040
|
-
|
|
5136
|
+
logDebug('Console tracking enabled');
|
|
5041
5137
|
}
|
|
5042
5138
|
/**
|
|
5043
5139
|
* Disable console event tracking
|
|
5044
5140
|
*/
|
|
5045
5141
|
disableConsoleTracking() {
|
|
5046
|
-
if (!isBrowser || !this.consoleTrackingEnabled
|
|
5142
|
+
if (!isBrowser || !this.consoleTrackingEnabled)
|
|
5047
5143
|
return;
|
|
5048
5144
|
// Restore original console methods
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5145
|
+
if (this.originalConsole) {
|
|
5146
|
+
console.log = this.originalConsole.log;
|
|
5147
|
+
console.warn = this.originalConsole.warn;
|
|
5148
|
+
console.error = this.originalConsole.error;
|
|
5149
|
+
}
|
|
5052
5150
|
this.consoleTrackingEnabled = false;
|
|
5053
|
-
|
|
5054
|
-
this.originalLogger = null;
|
|
5151
|
+
logDebug('Console tracking disabled');
|
|
5055
5152
|
}
|
|
5056
|
-
/**
|
|
5057
|
-
* Track console events
|
|
5058
|
-
*/
|
|
5059
5153
|
trackConsoleEvent(level, args) {
|
|
5060
5154
|
if (!this.initialized)
|
|
5061
5155
|
return;
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5156
|
+
try {
|
|
5157
|
+
const consoleData = {
|
|
5158
|
+
level: level,
|
|
5159
|
+
message: args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' '),
|
|
5160
|
+
timestamp: new Date().toISOString(),
|
|
5161
|
+
url: window.location.href
|
|
5162
|
+
};
|
|
5163
|
+
// Add console event to the main event stream
|
|
5164
|
+
this.addEvent({
|
|
5165
|
+
type: 5, // Custom event type
|
|
5166
|
+
data: {
|
|
5167
|
+
payload: Object.assign({ eventType: 'console' }, consoleData)
|
|
5168
|
+
},
|
|
5169
|
+
timestamp: Date.now()
|
|
5170
|
+
}).catch(error => {
|
|
5171
|
+
logError('Failed to track console event:', error);
|
|
5172
|
+
});
|
|
5173
|
+
}
|
|
5174
|
+
catch (error) {
|
|
5175
|
+
logError('Error in trackConsoleEvent:', error);
|
|
5176
|
+
}
|
|
5078
5177
|
}
|
|
5079
5178
|
setupPageUnloadHandler() {
|
|
5080
5179
|
if (!isBrowser)
|
|
@@ -5090,15 +5189,18 @@ class HumanBehaviorTracker {
|
|
|
5090
5189
|
});
|
|
5091
5190
|
// Handle actual page unload/close
|
|
5092
5191
|
window.addEventListener('beforeunload', () => {
|
|
5093
|
-
// Update last activity time
|
|
5094
|
-
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
5095
5192
|
// Send final events
|
|
5096
5193
|
this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
|
|
5097
5194
|
});
|
|
5098
|
-
// Update activity timestamp
|
|
5099
|
-
|
|
5195
|
+
// Update activity timestamp on user interaction (not on page load)
|
|
5196
|
+
const updateActivity = () => {
|
|
5100
5197
|
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
5101
|
-
}
|
|
5198
|
+
};
|
|
5199
|
+
// Listen for user interactions to update activity timestamp
|
|
5200
|
+
window.addEventListener('click', updateActivity);
|
|
5201
|
+
window.addEventListener('keydown', updateActivity);
|
|
5202
|
+
window.addEventListener('scroll', updateActivity);
|
|
5203
|
+
window.addEventListener('mousemove', updateActivity);
|
|
5102
5204
|
}
|
|
5103
5205
|
viewLogs() {
|
|
5104
5206
|
try {
|
|
@@ -5136,12 +5238,6 @@ class HumanBehaviorTracker {
|
|
|
5136
5238
|
yield this.api.sendUserAuth(this.endUserId, this.userProperties, this.sessionId, authFields);
|
|
5137
5239
|
});
|
|
5138
5240
|
}
|
|
5139
|
-
customEvent(eventName_1) {
|
|
5140
|
-
return __awaiter$1(this, arguments, void 0, function* (eventName, eventProperties = {}) {
|
|
5141
|
-
yield this.ensureInitialized();
|
|
5142
|
-
this.api.sendBeaconCustomEvent(eventName, eventProperties, this.sessionId);
|
|
5143
|
-
});
|
|
5144
|
-
}
|
|
5145
5241
|
start() {
|
|
5146
5242
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5147
5243
|
yield this.ensureInitialized();
|
|
@@ -5178,6 +5274,8 @@ class HumanBehaviorTracker {
|
|
|
5178
5274
|
}
|
|
5179
5275
|
// Disable console tracking
|
|
5180
5276
|
this.disableConsoleTracking();
|
|
5277
|
+
// Cleanup navigation tracking
|
|
5278
|
+
this.cleanupNavigationTracking();
|
|
5181
5279
|
});
|
|
5182
5280
|
}
|
|
5183
5281
|
addEvent(event) {
|
|
@@ -5220,7 +5318,7 @@ class HumanBehaviorTracker {
|
|
|
5220
5318
|
}
|
|
5221
5319
|
flush() {
|
|
5222
5320
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5223
|
-
var _a;
|
|
5321
|
+
var _a, _b, _c, _d;
|
|
5224
5322
|
// Prevent concurrent flushes
|
|
5225
5323
|
if (this.isProcessing || !this.initialized) {
|
|
5226
5324
|
return;
|
|
@@ -5243,6 +5341,14 @@ class HumanBehaviorTracker {
|
|
|
5243
5341
|
this.rejectedEvents.push(...eventsToProcess);
|
|
5244
5342
|
this.processRejectedEvents();
|
|
5245
5343
|
}
|
|
5344
|
+
else if (((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('ERR_BLOCKED_BY_CLIENT')) ||
|
|
5345
|
+
((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('Failed to fetch')) ||
|
|
5346
|
+
((_d = error.message) === null || _d === void 0 ? void 0 : _d.includes('NetworkError'))) {
|
|
5347
|
+
// Handle ad blocker or network issues gracefully
|
|
5348
|
+
logWarn('Request blocked by ad blocker or network issue, storing events for retry');
|
|
5349
|
+
this.rejectedEvents.push(...eventsToProcess);
|
|
5350
|
+
// Don't process rejected events immediately to avoid spam
|
|
5351
|
+
}
|
|
5246
5352
|
else {
|
|
5247
5353
|
throw error;
|
|
5248
5354
|
}
|
|
@@ -5315,6 +5421,61 @@ class HumanBehaviorTracker {
|
|
|
5315
5421
|
getRedactedFields() {
|
|
5316
5422
|
return this.redactionManager.getSelectedFields();
|
|
5317
5423
|
}
|
|
5424
|
+
/**
|
|
5425
|
+
* Get the current session ID
|
|
5426
|
+
*/
|
|
5427
|
+
getSessionId() {
|
|
5428
|
+
return this.sessionId;
|
|
5429
|
+
}
|
|
5430
|
+
/**
|
|
5431
|
+
* Get the current URL being tracked
|
|
5432
|
+
*/
|
|
5433
|
+
getCurrentUrl() {
|
|
5434
|
+
return this.currentUrl;
|
|
5435
|
+
}
|
|
5436
|
+
/**
|
|
5437
|
+
* Test if the tracker can reach the ingestion server
|
|
5438
|
+
*/
|
|
5439
|
+
testConnection() {
|
|
5440
|
+
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5441
|
+
try {
|
|
5442
|
+
yield this.api.init(this.sessionId, this.endUserId);
|
|
5443
|
+
return { success: true };
|
|
5444
|
+
}
|
|
5445
|
+
catch (error) {
|
|
5446
|
+
return {
|
|
5447
|
+
success: false,
|
|
5448
|
+
error: error.message || 'Unknown error'
|
|
5449
|
+
};
|
|
5450
|
+
}
|
|
5451
|
+
});
|
|
5452
|
+
}
|
|
5453
|
+
/**
|
|
5454
|
+
* Get connection status and recommendations
|
|
5455
|
+
*/
|
|
5456
|
+
getConnectionStatus() {
|
|
5457
|
+
const recommendations = [];
|
|
5458
|
+
let blocked = false;
|
|
5459
|
+
// Check if we have rejected events (might indicate blocking)
|
|
5460
|
+
if (this.rejectedEvents.length > 0) {
|
|
5461
|
+
blocked = true;
|
|
5462
|
+
recommendations.push('Some requests may be blocked by ad blockers');
|
|
5463
|
+
}
|
|
5464
|
+
// Check if connection was blocked during initialization
|
|
5465
|
+
if (this._connectionBlocked) {
|
|
5466
|
+
blocked = true;
|
|
5467
|
+
recommendations.push('Initial connection test failed - ad blocker may be active');
|
|
5468
|
+
}
|
|
5469
|
+
// Check if we're in a browser environment
|
|
5470
|
+
if (typeof window === 'undefined') {
|
|
5471
|
+
recommendations.push('Not running in browser environment');
|
|
5472
|
+
}
|
|
5473
|
+
// Check if navigator.sendBeacon is available
|
|
5474
|
+
if (typeof navigator.sendBeacon === 'undefined') {
|
|
5475
|
+
recommendations.push('sendBeacon not available, using fetch fallback');
|
|
5476
|
+
}
|
|
5477
|
+
return { blocked, recommendations };
|
|
5478
|
+
}
|
|
5318
5479
|
}
|
|
5319
5480
|
// Only expose to window object in browser environments
|
|
5320
5481
|
if (isBrowser) {
|