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