humanbehavior-js 0.0.8 → 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 +558 -184
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/index.js +197 -86
- package/dist/cjs/react/index.js.map +1 -1
- package/dist/esm/index.js +554 -185
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/index.js +195 -87
- 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 +115 -10
- package/dist/types/react/index.d.ts +8 -9
- package/package.json +3 -2
- package/readme.md +127 -105
- package/simple-spa.html +544 -0
- package/src/api.ts +9 -137
- package/src/index.ts +3 -0
- package/src/react/index.tsx +137 -81
- package/src/redact.ts +19 -17
- package/src/tracker.ts +498 -62
- package/src/utils/logger.ts +144 -0
package/dist/cjs/index.js
CHANGED
|
@@ -4053,6 +4053,123 @@ function v1Bytes(rnds, msecs, nsecs, clockseq, node, buf, offset = 0) {
|
|
|
4053
4053
|
return buf;
|
|
4054
4054
|
}
|
|
4055
4055
|
|
|
4056
|
+
exports.LogLevel = void 0;
|
|
4057
|
+
(function (LogLevel) {
|
|
4058
|
+
LogLevel[LogLevel["NONE"] = 0] = "NONE";
|
|
4059
|
+
LogLevel[LogLevel["ERROR"] = 1] = "ERROR";
|
|
4060
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
4061
|
+
LogLevel[LogLevel["INFO"] = 3] = "INFO";
|
|
4062
|
+
LogLevel[LogLevel["DEBUG"] = 4] = "DEBUG";
|
|
4063
|
+
})(exports.LogLevel || (exports.LogLevel = {}));
|
|
4064
|
+
class Logger {
|
|
4065
|
+
constructor(config) {
|
|
4066
|
+
this.config = {
|
|
4067
|
+
level: exports.LogLevel.ERROR, // Default to only errors in production
|
|
4068
|
+
enableConsole: true,
|
|
4069
|
+
enableStorage: false
|
|
4070
|
+
};
|
|
4071
|
+
this.isBrowser = typeof window !== 'undefined';
|
|
4072
|
+
if (config) {
|
|
4073
|
+
this.config = Object.assign(Object.assign({}, this.config), config);
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
setConfig(config) {
|
|
4077
|
+
this.config = Object.assign(Object.assign({}, this.config), config);
|
|
4078
|
+
}
|
|
4079
|
+
shouldLog(level) {
|
|
4080
|
+
return level <= this.config.level;
|
|
4081
|
+
}
|
|
4082
|
+
formatMessage(level, message, ...args) {
|
|
4083
|
+
const timestamp = new Date().toISOString();
|
|
4084
|
+
return `[HumanBehavior ${level}] ${timestamp}: ${message}`;
|
|
4085
|
+
}
|
|
4086
|
+
error(message, ...args) {
|
|
4087
|
+
if (!this.shouldLog(exports.LogLevel.ERROR))
|
|
4088
|
+
return;
|
|
4089
|
+
const formattedMessage = this.formatMessage('ERROR', message);
|
|
4090
|
+
if (this.config.enableConsole) {
|
|
4091
|
+
console.error(formattedMessage, ...args);
|
|
4092
|
+
}
|
|
4093
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
4094
|
+
this.logToStorage(formattedMessage, args);
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
warn(message, ...args) {
|
|
4098
|
+
if (!this.shouldLog(exports.LogLevel.WARN))
|
|
4099
|
+
return;
|
|
4100
|
+
const formattedMessage = this.formatMessage('WARN', message);
|
|
4101
|
+
if (this.config.enableConsole) {
|
|
4102
|
+
console.warn(formattedMessage, ...args);
|
|
4103
|
+
}
|
|
4104
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
4105
|
+
this.logToStorage(formattedMessage, args);
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
info(message, ...args) {
|
|
4109
|
+
if (!this.shouldLog(exports.LogLevel.INFO))
|
|
4110
|
+
return;
|
|
4111
|
+
const formattedMessage = this.formatMessage('INFO', message);
|
|
4112
|
+
if (this.config.enableConsole) {
|
|
4113
|
+
console.log(formattedMessage, ...args);
|
|
4114
|
+
}
|
|
4115
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
4116
|
+
this.logToStorage(formattedMessage, args);
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
debug(message, ...args) {
|
|
4120
|
+
if (!this.shouldLog(exports.LogLevel.DEBUG))
|
|
4121
|
+
return;
|
|
4122
|
+
const formattedMessage = this.formatMessage('DEBUG', message);
|
|
4123
|
+
if (this.config.enableConsole) {
|
|
4124
|
+
console.log(formattedMessage, ...args);
|
|
4125
|
+
}
|
|
4126
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
4127
|
+
this.logToStorage(formattedMessage, args);
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
logToStorage(message, args) {
|
|
4131
|
+
try {
|
|
4132
|
+
const logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
|
|
4133
|
+
const logEntry = {
|
|
4134
|
+
message,
|
|
4135
|
+
args: args.length > 0 ? args : undefined,
|
|
4136
|
+
timestamp: Date.now()
|
|
4137
|
+
};
|
|
4138
|
+
logs.push(logEntry);
|
|
4139
|
+
// Keep only last 1000 logs to prevent storage bloat
|
|
4140
|
+
if (logs.length > 1000) {
|
|
4141
|
+
logs.splice(0, logs.length - 1000);
|
|
4142
|
+
}
|
|
4143
|
+
localStorage.setItem('human_behavior_logs', JSON.stringify(logs));
|
|
4144
|
+
}
|
|
4145
|
+
catch (e) {
|
|
4146
|
+
// Silently fail if storage is not available
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
getLogs() {
|
|
4150
|
+
if (!this.isBrowser)
|
|
4151
|
+
return [];
|
|
4152
|
+
try {
|
|
4153
|
+
return JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
|
|
4154
|
+
}
|
|
4155
|
+
catch (e) {
|
|
4156
|
+
return [];
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
clearLogs() {
|
|
4160
|
+
if (this.isBrowser) {
|
|
4161
|
+
localStorage.removeItem('human_behavior_logs');
|
|
4162
|
+
}
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
// Create singleton instance
|
|
4166
|
+
const logger = new Logger();
|
|
4167
|
+
// Export convenience methods
|
|
4168
|
+
const logError = (message, ...args) => logger.error(message, ...args);
|
|
4169
|
+
const logWarn = (message, ...args) => logger.warn(message, ...args);
|
|
4170
|
+
const logInfo = (message, ...args) => logger.info(message, ...args);
|
|
4171
|
+
const logDebug = (message, ...args) => logger.debug(message, ...args);
|
|
4172
|
+
|
|
4056
4173
|
const MAX_CHUNK_SIZE_BYTES = 1024 * 1024 * 10; // 10MB chunk size
|
|
4057
4174
|
function isChunkSizeExceeded(currentChunk, newEvent, sessionId) {
|
|
4058
4175
|
const nextChunkSize = new TextEncoder().encode(JSON.stringify({
|
|
@@ -4184,7 +4301,7 @@ class HumanBehaviorAPI {
|
|
|
4184
4301
|
return results.flat();
|
|
4185
4302
|
}
|
|
4186
4303
|
catch (error) {
|
|
4187
|
-
|
|
4304
|
+
logError('Error sending events:', error);
|
|
4188
4305
|
throw error;
|
|
4189
4306
|
}
|
|
4190
4307
|
});
|
|
@@ -4210,7 +4327,7 @@ class HumanBehaviorAPI {
|
|
|
4210
4327
|
return yield response.json();
|
|
4211
4328
|
}
|
|
4212
4329
|
catch (error) {
|
|
4213
|
-
|
|
4330
|
+
logError('Error sending user data:', error);
|
|
4214
4331
|
throw error;
|
|
4215
4332
|
}
|
|
4216
4333
|
});
|
|
@@ -4237,134 +4354,21 @@ class HumanBehaviorAPI {
|
|
|
4237
4354
|
return yield response.json();
|
|
4238
4355
|
}
|
|
4239
4356
|
catch (error) {
|
|
4240
|
-
|
|
4357
|
+
logError('Error authenticating user:', error);
|
|
4241
4358
|
throw error;
|
|
4242
4359
|
}
|
|
4243
4360
|
});
|
|
4244
4361
|
}
|
|
4245
|
-
|
|
4246
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4247
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/sessionComplete`, {
|
|
4248
|
-
method: 'POST',
|
|
4249
|
-
headers: {
|
|
4250
|
-
'Content-Type': 'application/json',
|
|
4251
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4252
|
-
},
|
|
4253
|
-
body: JSON.stringify({ sessionId })
|
|
4254
|
-
});
|
|
4255
|
-
if (!response.ok) {
|
|
4256
|
-
throw new Error(`Failed to send session complete: ${response.statusText}`);
|
|
4257
|
-
}
|
|
4258
|
-
});
|
|
4259
|
-
}
|
|
4260
|
-
sendCustomEvent(eventName, eventProperties, sessionId) {
|
|
4261
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4262
|
-
const maxRetries = 3;
|
|
4263
|
-
let retryCount = 0;
|
|
4264
|
-
while (retryCount < maxRetries) {
|
|
4265
|
-
try {
|
|
4266
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/customEvent`, {
|
|
4267
|
-
method: 'POST',
|
|
4268
|
-
headers: {
|
|
4269
|
-
'Content-Type': 'application/json',
|
|
4270
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4271
|
-
},
|
|
4272
|
-
body: JSON.stringify({
|
|
4273
|
-
name: eventName,
|
|
4274
|
-
properties: eventProperties,
|
|
4275
|
-
sessionId: sessionId,
|
|
4276
|
-
timestamp: new Date().toISOString()
|
|
4277
|
-
})
|
|
4278
|
-
});
|
|
4279
|
-
if (!response.ok) {
|
|
4280
|
-
throw new Error(`Failed to send custom event: ${response.statusText}`);
|
|
4281
|
-
}
|
|
4282
|
-
return yield response.json();
|
|
4283
|
-
}
|
|
4284
|
-
catch (error) {
|
|
4285
|
-
retryCount++;
|
|
4286
|
-
if (retryCount === maxRetries) {
|
|
4287
|
-
console.error('Error sending custom event after max retries:', error);
|
|
4288
|
-
throw error;
|
|
4289
|
-
}
|
|
4290
|
-
// Exponential backoff
|
|
4291
|
-
yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
|
|
4292
|
-
}
|
|
4293
|
-
}
|
|
4294
|
-
});
|
|
4295
|
-
}
|
|
4296
|
-
sendCustomEvents(events, sessionId) {
|
|
4297
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4298
|
-
const maxRetries = 3;
|
|
4299
|
-
let retryCount = 0;
|
|
4300
|
-
while (retryCount < maxRetries) {
|
|
4301
|
-
try {
|
|
4302
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/customEvent/batch`, {
|
|
4303
|
-
method: 'POST',
|
|
4304
|
-
headers: {
|
|
4305
|
-
'Content-Type': 'application/json',
|
|
4306
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4307
|
-
},
|
|
4308
|
-
body: JSON.stringify({
|
|
4309
|
-
events: events.map(event => (Object.assign(Object.assign({}, event), { sessionId: sessionId })))
|
|
4310
|
-
})
|
|
4311
|
-
});
|
|
4312
|
-
if (!response.ok) {
|
|
4313
|
-
throw new Error(`Failed to send custom events: ${response.statusText}`);
|
|
4314
|
-
}
|
|
4315
|
-
return yield response.json();
|
|
4316
|
-
}
|
|
4317
|
-
catch (error) {
|
|
4318
|
-
retryCount++;
|
|
4319
|
-
if (retryCount === maxRetries) {
|
|
4320
|
-
console.error('Error sending custom events after max retries:', error);
|
|
4321
|
-
throw error;
|
|
4322
|
-
}
|
|
4323
|
-
// Exponential backoff
|
|
4324
|
-
yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
|
|
4325
|
-
}
|
|
4326
|
-
}
|
|
4327
|
-
});
|
|
4328
|
-
}
|
|
4329
|
-
sendBeaconEvents(events, sessionId, isSessionComplete = false) {
|
|
4362
|
+
sendBeaconEvents(events, sessionId) {
|
|
4330
4363
|
const data = new URLSearchParams();
|
|
4331
4364
|
data.append('events', encodeURIComponent(JSON.stringify(events)));
|
|
4332
4365
|
data.append('sessionId', encodeURIComponent(sessionId));
|
|
4333
4366
|
data.append('timestamp', encodeURIComponent(Date.now().toString()));
|
|
4334
4367
|
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4335
|
-
if (isSessionComplete) {
|
|
4336
|
-
console.log('Session complete beacon sending');
|
|
4337
|
-
localStorage.setItem('koalaware_session_complete', Date.now().toString());
|
|
4338
|
-
data.append('sessionComplete', encodeURIComponent('true'));
|
|
4339
|
-
}
|
|
4340
4368
|
navigator.sendBeacon(`${this.baseUrl}/api/ingestion/events`, data);
|
|
4341
4369
|
// KoalawareTracker.logToStorage(`Sending events beacon: ${this.baseUrl}/api/ingestion/events`);
|
|
4342
4370
|
// KoalawareTracker.logToStorage(`Events beacon success: ${success}`);
|
|
4343
4371
|
}
|
|
4344
|
-
sendBeaconSessionComplete(sessionId) {
|
|
4345
|
-
const data = new URLSearchParams();
|
|
4346
|
-
data.append('sessionId', sessionId);
|
|
4347
|
-
data.append('apiKey', this.apiKey);
|
|
4348
|
-
data.append('sessionComplete', 'true');
|
|
4349
|
-
navigator.sendBeacon(`${this.baseUrl}/api/ingestion/sessionComplete`, data);
|
|
4350
|
-
// KoalawareTracker.logToStorage(`Sending completion beacon: ${this.baseUrl}/api/ingestion/sessionComplete`);
|
|
4351
|
-
// KoalawareTracker.logToStorage(`Complete beacon success: ${success}`);
|
|
4352
|
-
}
|
|
4353
|
-
sendBeaconCustomEvent(eventName, eventProperties, sessionId) {
|
|
4354
|
-
const data = new URLSearchParams();
|
|
4355
|
-
data.append('name', encodeURIComponent(eventName));
|
|
4356
|
-
data.append('properties', encodeURIComponent(JSON.stringify(eventProperties)));
|
|
4357
|
-
data.append('sessionId', encodeURIComponent(sessionId));
|
|
4358
|
-
data.append('timestamp', encodeURIComponent(new Date().toISOString()));
|
|
4359
|
-
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4360
|
-
return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/customEvent`, data);
|
|
4361
|
-
}
|
|
4362
|
-
sendBeaconCustomEvents(events, sessionId) {
|
|
4363
|
-
const data = new URLSearchParams();
|
|
4364
|
-
data.append('events', encodeURIComponent(JSON.stringify(events.map(event => (Object.assign(Object.assign({}, event), { sessionId: sessionId }))))));
|
|
4365
|
-
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4366
|
-
return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/customEvent/batch`, data);
|
|
4367
|
-
}
|
|
4368
4372
|
}
|
|
4369
4373
|
|
|
4370
4374
|
// Redaction functionality for sensitive input fields
|
|
@@ -4397,18 +4401,18 @@ class RedactionManager {
|
|
|
4397
4401
|
this.userSelectedFields.clear();
|
|
4398
4402
|
fields.forEach(field => this.userSelectedFields.add(field));
|
|
4399
4403
|
if (fields.length > 0) {
|
|
4400
|
-
|
|
4404
|
+
logDebug(`Redaction: Active for ${fields.length} field(s):`, fields);
|
|
4401
4405
|
// Debug: Check if elements exist
|
|
4402
4406
|
fields.forEach(selector => {
|
|
4403
4407
|
const elements = document.querySelectorAll(selector);
|
|
4404
|
-
|
|
4408
|
+
logDebug(`Redaction: Found ${elements.length} element(s) for selector '${selector}'`);
|
|
4405
4409
|
elements.forEach((el, index) => {
|
|
4406
|
-
|
|
4410
|
+
logDebug(`Redaction: Element ${index} for '${selector}':`, el);
|
|
4407
4411
|
});
|
|
4408
4412
|
});
|
|
4409
4413
|
}
|
|
4410
4414
|
else {
|
|
4411
|
-
|
|
4415
|
+
logDebug('Redaction: Disabled - no fields selected');
|
|
4412
4416
|
}
|
|
4413
4417
|
}
|
|
4414
4418
|
/**
|
|
@@ -4438,7 +4442,7 @@ class RedactionManager {
|
|
|
4438
4442
|
if (processedEvent.data.source === 5) { // Input event
|
|
4439
4443
|
const shouldRedact = this.isFieldSelected(processedEvent.data);
|
|
4440
4444
|
if (shouldRedact) {
|
|
4441
|
-
|
|
4445
|
+
logDebug('Redaction: Processing input event for redaction');
|
|
4442
4446
|
this.redactInputEvent(processedEvent.data);
|
|
4443
4447
|
}
|
|
4444
4448
|
}
|
|
@@ -4464,30 +4468,30 @@ class RedactionManager {
|
|
|
4464
4468
|
if (!this.isFieldSelected(inputData)) {
|
|
4465
4469
|
return;
|
|
4466
4470
|
}
|
|
4467
|
-
|
|
4471
|
+
logDebug('Redaction: Redacting input event with text:', inputData.text);
|
|
4468
4472
|
// Redact all text-related properties that could contain input data
|
|
4469
4473
|
const textProperties = ['text', 'value', 'content', 'data', 'input', 'textContent'];
|
|
4470
4474
|
textProperties.forEach(prop => {
|
|
4471
4475
|
if (inputData[prop] !== undefined && typeof inputData[prop] === 'string') {
|
|
4472
4476
|
inputData[prop] = this.redactedText;
|
|
4473
|
-
|
|
4477
|
+
logDebug(`Redaction: Redacted property '${prop}'`);
|
|
4474
4478
|
}
|
|
4475
4479
|
});
|
|
4476
4480
|
// Also check for any other string properties that might contain input data
|
|
4477
4481
|
Object.keys(inputData).forEach(key => {
|
|
4478
4482
|
if (typeof inputData[key] === 'string' && inputData[key].length > 0) {
|
|
4479
4483
|
inputData[key] = this.redactedText;
|
|
4480
|
-
|
|
4484
|
+
logDebug(`Redaction: Redacted additional property '${key}'`);
|
|
4481
4485
|
}
|
|
4482
4486
|
});
|
|
4483
4487
|
// Handle nested objects that might contain text data
|
|
4484
4488
|
if (inputData.attributes && typeof inputData.attributes === 'object') {
|
|
4485
4489
|
if (inputData.attributes.value && typeof inputData.attributes.value === 'string') {
|
|
4486
4490
|
inputData.attributes.value = this.redactedText;
|
|
4487
|
-
|
|
4491
|
+
logDebug('Redaction: Redacted nested value attribute');
|
|
4488
4492
|
}
|
|
4489
4493
|
}
|
|
4490
|
-
|
|
4494
|
+
logDebug('Redaction: Input event redaction complete');
|
|
4491
4495
|
}
|
|
4492
4496
|
/**
|
|
4493
4497
|
* Redact sensitive data in DOM mutation events
|
|
@@ -4549,7 +4553,7 @@ class RedactionManager {
|
|
|
4549
4553
|
return false;
|
|
4550
4554
|
}
|
|
4551
4555
|
catch (e) {
|
|
4552
|
-
|
|
4556
|
+
logWarn('Error checking if DOM change should be redacted:', e);
|
|
4553
4557
|
return false;
|
|
4554
4558
|
}
|
|
4555
4559
|
}
|
|
@@ -4696,7 +4700,7 @@ class RedactionManager {
|
|
|
4696
4700
|
// Check if any of these elements are currently focused
|
|
4697
4701
|
for (const el of matchingElements) {
|
|
4698
4702
|
if (el === document.activeElement) {
|
|
4699
|
-
|
|
4703
|
+
logDebug('Redaction: Found focused element matching selector:', selector);
|
|
4700
4704
|
return true;
|
|
4701
4705
|
}
|
|
4702
4706
|
}
|
|
@@ -4706,7 +4710,7 @@ class RedactionManager {
|
|
|
4706
4710
|
// Look for any input element that might be the active one
|
|
4707
4711
|
const activeElement = document.activeElement;
|
|
4708
4712
|
if (activeElement && this.shouldRedactElement(activeElement)) {
|
|
4709
|
-
|
|
4713
|
+
logDebug('Redaction: Active element should be redacted');
|
|
4710
4714
|
return true;
|
|
4711
4715
|
}
|
|
4712
4716
|
return false;
|
|
@@ -4739,7 +4743,7 @@ class RedactionManager {
|
|
|
4739
4743
|
return false;
|
|
4740
4744
|
}
|
|
4741
4745
|
catch (e) {
|
|
4742
|
-
|
|
4746
|
+
logWarn('Error checking if field should be redacted:', e);
|
|
4743
4747
|
return false;
|
|
4744
4748
|
}
|
|
4745
4749
|
}
|
|
@@ -4783,6 +4787,40 @@ const redactionManager = new RedactionManager();
|
|
|
4783
4787
|
// Check if we're in a browser environment
|
|
4784
4788
|
const isBrowser = typeof window !== 'undefined';
|
|
4785
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
|
+
}
|
|
4786
4824
|
constructor(apiKey, ingestionUrl) {
|
|
4787
4825
|
this.eventIngestionQueue = [];
|
|
4788
4826
|
this.queueSizeBytes = 0;
|
|
@@ -4795,61 +4833,82 @@ class HumanBehaviorTracker {
|
|
|
4795
4833
|
this.endUserId = null;
|
|
4796
4834
|
this.initialized = false;
|
|
4797
4835
|
this.initializationPromise = null;
|
|
4836
|
+
// Console tracking properties
|
|
4837
|
+
this.originalConsole = null;
|
|
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;
|
|
4798
4847
|
if (!apiKey) {
|
|
4799
4848
|
throw new Error('Human Behavior API Key is required');
|
|
4800
4849
|
}
|
|
4801
|
-
//
|
|
4802
|
-
|
|
4803
|
-
// ========================================
|
|
4804
|
-
// Uncomment ONE of the following lines to select your server:
|
|
4805
|
-
// AWS Development Server
|
|
4806
|
-
const defaultIngestionUrl = 'http://3.137.217.33:3000';
|
|
4807
|
-
// Vercel Production Server
|
|
4808
|
-
// const defaultIngestionUrl = 'https://ingestion-server.vercel.app';
|
|
4809
|
-
// Local Development Server
|
|
4810
|
-
// const defaultIngestionUrl = 'http://localhost:3000';
|
|
4850
|
+
// Initialize API
|
|
4851
|
+
const defaultIngestionUrl = 'http://3.137.217.33:3000'; // AWS Development Server
|
|
4811
4852
|
this.api = new HumanBehaviorAPI({
|
|
4812
4853
|
apiKey: apiKey,
|
|
4813
4854
|
ingestionUrl: ingestionUrl || defaultIngestionUrl
|
|
4814
4855
|
});
|
|
4815
4856
|
this.apiKey = apiKey;
|
|
4816
4857
|
this.redactionManager = new RedactionManager();
|
|
4817
|
-
//
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
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();
|
|
4827
4877
|
}
|
|
4828
|
-
// Start initialization
|
|
4878
|
+
// Start initialization
|
|
4829
4879
|
this.initializationPromise = this.init();
|
|
4830
4880
|
}
|
|
4831
4881
|
init() {
|
|
4832
4882
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4833
4883
|
try {
|
|
4834
4884
|
const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
|
|
4885
|
+
logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
|
|
4835
4886
|
const { sessionId, endUserId } = yield this.api.init(this.sessionId, userId);
|
|
4836
|
-
|
|
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
|
+
}
|
|
4837
4896
|
this.endUserId = endUserId;
|
|
4838
4897
|
this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
|
|
4839
4898
|
// Only setup browser-specific handlers when in browser environment
|
|
4840
4899
|
if (isBrowser) {
|
|
4841
4900
|
this.setupPageUnloadHandler();
|
|
4842
|
-
this.
|
|
4901
|
+
this.setupNavigationTracking();
|
|
4843
4902
|
this.processRejectedEvents();
|
|
4844
4903
|
}
|
|
4845
4904
|
else {
|
|
4846
|
-
|
|
4905
|
+
logWarn('HumanBehaviorTracker initialized in a non-browser environment. Session tracking is disabled.');
|
|
4847
4906
|
}
|
|
4848
4907
|
this.initialized = true;
|
|
4849
|
-
|
|
4908
|
+
logInfo(`HumanBehaviorTracker initialized with sessionId: ${this.sessionId}, endUserId: ${endUserId}`);
|
|
4850
4909
|
}
|
|
4851
4910
|
catch (error) {
|
|
4852
|
-
|
|
4911
|
+
logError('Failed to initialize HumanBehaviorTracker:', error);
|
|
4853
4912
|
throw error;
|
|
4854
4913
|
}
|
|
4855
4914
|
});
|
|
@@ -4862,48 +4921,295 @@ class HumanBehaviorTracker {
|
|
|
4862
4921
|
yield this.initializationPromise;
|
|
4863
4922
|
});
|
|
4864
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
|
+
}
|
|
4865
5089
|
static logToStorage(message) {
|
|
5090
|
+
logInfo(message);
|
|
5091
|
+
}
|
|
5092
|
+
/**
|
|
5093
|
+
* Configure logging behavior for the SDK
|
|
5094
|
+
* @param config Logger configuration options
|
|
5095
|
+
*/
|
|
5096
|
+
static configureLogging(config) {
|
|
5097
|
+
const levelMap = {
|
|
5098
|
+
'none': 0,
|
|
5099
|
+
'error': 1,
|
|
5100
|
+
'warn': 2,
|
|
5101
|
+
'info': 3,
|
|
5102
|
+
'debug': 4
|
|
5103
|
+
};
|
|
5104
|
+
logger.setConfig({
|
|
5105
|
+
level: levelMap[config.level || 'error'],
|
|
5106
|
+
enableConsole: config.enableConsole !== false,
|
|
5107
|
+
enableStorage: config.enableStorage || false
|
|
5108
|
+
});
|
|
5109
|
+
}
|
|
5110
|
+
/**
|
|
5111
|
+
* Enable console event tracking
|
|
5112
|
+
*/
|
|
5113
|
+
enableConsoleTracking() {
|
|
5114
|
+
if (!isBrowser || this.consoleTrackingEnabled)
|
|
5115
|
+
return;
|
|
5116
|
+
// Store original console methods
|
|
5117
|
+
this.originalConsole = {
|
|
5118
|
+
log: console.log,
|
|
5119
|
+
warn: console.warn,
|
|
5120
|
+
error: console.error
|
|
5121
|
+
};
|
|
5122
|
+
// Override console methods to capture ALL console output (including logger output)
|
|
5123
|
+
console.log = (...args) => {
|
|
5124
|
+
this.trackConsoleEvent('log', args);
|
|
5125
|
+
this.originalConsole.log(...args);
|
|
5126
|
+
};
|
|
5127
|
+
console.warn = (...args) => {
|
|
5128
|
+
this.trackConsoleEvent('warn', args);
|
|
5129
|
+
this.originalConsole.warn(...args);
|
|
5130
|
+
};
|
|
5131
|
+
console.error = (...args) => {
|
|
5132
|
+
this.trackConsoleEvent('error', args);
|
|
5133
|
+
this.originalConsole.error(...args);
|
|
5134
|
+
};
|
|
5135
|
+
this.consoleTrackingEnabled = true;
|
|
5136
|
+
logDebug('Console tracking enabled');
|
|
5137
|
+
}
|
|
5138
|
+
/**
|
|
5139
|
+
* Disable console event tracking
|
|
5140
|
+
*/
|
|
5141
|
+
disableConsoleTracking() {
|
|
5142
|
+
if (!isBrowser || !this.consoleTrackingEnabled)
|
|
5143
|
+
return;
|
|
5144
|
+
// Restore original console methods
|
|
5145
|
+
if (this.originalConsole) {
|
|
5146
|
+
console.log = this.originalConsole.log;
|
|
5147
|
+
console.warn = this.originalConsole.warn;
|
|
5148
|
+
console.error = this.originalConsole.error;
|
|
5149
|
+
}
|
|
5150
|
+
this.consoleTrackingEnabled = false;
|
|
5151
|
+
logDebug('Console tracking disabled');
|
|
5152
|
+
}
|
|
5153
|
+
trackConsoleEvent(level, args) {
|
|
5154
|
+
if (!this.initialized)
|
|
5155
|
+
return;
|
|
4866
5156
|
try {
|
|
4867
|
-
const
|
|
4868
|
-
|
|
4869
|
-
|
|
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
|
+
});
|
|
4870
5173
|
}
|
|
4871
|
-
catch (
|
|
4872
|
-
|
|
5174
|
+
catch (error) {
|
|
5175
|
+
logError('Error in trackConsoleEvent:', error);
|
|
4873
5176
|
}
|
|
4874
5177
|
}
|
|
4875
5178
|
setupPageUnloadHandler() {
|
|
4876
5179
|
if (!isBrowser)
|
|
4877
5180
|
return;
|
|
4878
|
-
|
|
5181
|
+
logDebug('Setting up page unload handler');
|
|
4879
5182
|
// Handle visibility changes for sending events
|
|
4880
5183
|
window.addEventListener('visibilitychange', () => {
|
|
4881
5184
|
// Only send events when page becomes hidden
|
|
4882
5185
|
if (document.visibilityState === 'hidden') {
|
|
4883
|
-
|
|
5186
|
+
logDebug('Page hidden - sending pending events');
|
|
4884
5187
|
this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
|
|
4885
5188
|
}
|
|
4886
5189
|
});
|
|
4887
5190
|
// Handle actual page unload/close
|
|
4888
5191
|
window.addEventListener('beforeunload', () => {
|
|
4889
|
-
// Update last activity time
|
|
4890
|
-
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
4891
5192
|
// Send final events
|
|
4892
5193
|
this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
|
|
4893
5194
|
});
|
|
4894
|
-
// Update activity timestamp
|
|
4895
|
-
|
|
5195
|
+
// Update activity timestamp on user interaction (not on page load)
|
|
5196
|
+
const updateActivity = () => {
|
|
4896
5197
|
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
4897
|
-
}
|
|
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);
|
|
4898
5204
|
}
|
|
4899
5205
|
viewLogs() {
|
|
4900
5206
|
try {
|
|
4901
|
-
const logs =
|
|
5207
|
+
const logs = logger.getLogs();
|
|
4902
5208
|
console.log('HumanBehavior Logs:', logs);
|
|
4903
|
-
|
|
5209
|
+
logger.clearLogs(); // Clear logs after viewing
|
|
4904
5210
|
}
|
|
4905
5211
|
catch (e) {
|
|
4906
|
-
|
|
5212
|
+
logError('Failed to read logs:', e);
|
|
4907
5213
|
}
|
|
4908
5214
|
}
|
|
4909
5215
|
addUserInfo(userProperties) {
|
|
@@ -4932,12 +5238,6 @@ class HumanBehaviorTracker {
|
|
|
4932
5238
|
yield this.api.sendUserAuth(this.endUserId, this.userProperties, this.sessionId, authFields);
|
|
4933
5239
|
});
|
|
4934
5240
|
}
|
|
4935
|
-
customEvent(eventName_1) {
|
|
4936
|
-
return __awaiter$1(this, arguments, void 0, function* (eventName, eventProperties = {}) {
|
|
4937
|
-
yield this.ensureInitialized();
|
|
4938
|
-
this.api.sendBeaconCustomEvent(eventName, eventProperties, this.sessionId);
|
|
4939
|
-
});
|
|
4940
|
-
}
|
|
4941
5241
|
start() {
|
|
4942
5242
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4943
5243
|
yield this.ensureInitialized();
|
|
@@ -4947,6 +5247,8 @@ class HumanBehaviorTracker {
|
|
|
4947
5247
|
this.flushInterval = window.setInterval(() => {
|
|
4948
5248
|
this.flush();
|
|
4949
5249
|
}, this.FLUSH_INTERVAL_MS);
|
|
5250
|
+
// Enable console tracking
|
|
5251
|
+
this.enableConsoleTracking();
|
|
4950
5252
|
// Start recording with redaction enabled
|
|
4951
5253
|
record({
|
|
4952
5254
|
emit: (event) => {
|
|
@@ -4970,6 +5272,10 @@ class HumanBehaviorTracker {
|
|
|
4970
5272
|
clearInterval(this.flushInterval);
|
|
4971
5273
|
this.flushInterval = null;
|
|
4972
5274
|
}
|
|
5275
|
+
// Disable console tracking
|
|
5276
|
+
this.disableConsoleTracking();
|
|
5277
|
+
// Cleanup navigation tracking
|
|
5278
|
+
this.cleanupNavigationTracking();
|
|
4973
5279
|
});
|
|
4974
5280
|
}
|
|
4975
5281
|
addEvent(event) {
|
|
@@ -5003,7 +5309,7 @@ class HumanBehaviorTracker {
|
|
|
5003
5309
|
this.sessionId = newSessionId;
|
|
5004
5310
|
}
|
|
5005
5311
|
catch (error) {
|
|
5006
|
-
|
|
5312
|
+
logError('Failed to process rejected events:', error);
|
|
5007
5313
|
}
|
|
5008
5314
|
finally {
|
|
5009
5315
|
this.isProcessingRejectedEvents = false;
|
|
@@ -5012,7 +5318,7 @@ class HumanBehaviorTracker {
|
|
|
5012
5318
|
}
|
|
5013
5319
|
flush() {
|
|
5014
5320
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5015
|
-
var _a;
|
|
5321
|
+
var _a, _b, _c, _d;
|
|
5016
5322
|
// Prevent concurrent flushes
|
|
5017
5323
|
if (this.isProcessing || !this.initialized) {
|
|
5018
5324
|
return;
|
|
@@ -5024,17 +5330,25 @@ class HumanBehaviorTracker {
|
|
|
5024
5330
|
this.eventIngestionQueue = [];
|
|
5025
5331
|
this.queueSizeBytes = 0;
|
|
5026
5332
|
if (eventsToProcess.length > 0) {
|
|
5027
|
-
|
|
5333
|
+
logDebug('Flushing events:', eventsToProcess);
|
|
5028
5334
|
try {
|
|
5029
5335
|
yield this.api.sendEvents(eventsToProcess, this.sessionId, this.endUserId);
|
|
5030
5336
|
}
|
|
5031
5337
|
catch (error) {
|
|
5032
5338
|
// If we get a 400 error, store events for retry
|
|
5033
5339
|
if ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('ERROR: Session already completed')) {
|
|
5034
|
-
|
|
5340
|
+
logInfo('Session expired, storing events for retry');
|
|
5035
5341
|
this.rejectedEvents.push(...eventsToProcess);
|
|
5036
5342
|
this.processRejectedEvents();
|
|
5037
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
|
+
}
|
|
5038
5352
|
else {
|
|
5039
5353
|
throw error;
|
|
5040
5354
|
}
|
|
@@ -5077,7 +5391,7 @@ class HumanBehaviorTracker {
|
|
|
5077
5391
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5078
5392
|
yield this.ensureInitialized();
|
|
5079
5393
|
if (!isBrowser) {
|
|
5080
|
-
|
|
5394
|
+
logWarn('Redaction is only available in browser environments');
|
|
5081
5395
|
return;
|
|
5082
5396
|
}
|
|
5083
5397
|
// Create a new redaction manager with the provided options
|
|
@@ -5090,7 +5404,7 @@ class HumanBehaviorTracker {
|
|
|
5090
5404
|
*/
|
|
5091
5405
|
setRedactedFields(fields) {
|
|
5092
5406
|
if (!isBrowser) {
|
|
5093
|
-
|
|
5407
|
+
logWarn('Redaction is only available in browser environments');
|
|
5094
5408
|
return;
|
|
5095
5409
|
}
|
|
5096
5410
|
this.redactionManager.setFieldsToRedact(fields);
|
|
@@ -5107,6 +5421,61 @@ class HumanBehaviorTracker {
|
|
|
5107
5421
|
getRedactedFields() {
|
|
5108
5422
|
return this.redactionManager.getSelectedFields();
|
|
5109
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
|
+
}
|
|
5110
5479
|
}
|
|
5111
5480
|
// Only expose to window object in browser environments
|
|
5112
5481
|
if (isBrowser) {
|
|
@@ -5127,6 +5496,11 @@ exports.MAX_CHUNK_SIZE_BYTES = MAX_CHUNK_SIZE_BYTES;
|
|
|
5127
5496
|
exports.RedactionManager = RedactionManager;
|
|
5128
5497
|
exports.default = HumanBehaviorTracker;
|
|
5129
5498
|
exports.isChunkSizeExceeded = isChunkSizeExceeded;
|
|
5499
|
+
exports.logDebug = logDebug;
|
|
5500
|
+
exports.logError = logError;
|
|
5501
|
+
exports.logInfo = logInfo;
|
|
5502
|
+
exports.logWarn = logWarn;
|
|
5503
|
+
exports.logger = logger;
|
|
5130
5504
|
exports.redactionManager = redactionManager;
|
|
5131
5505
|
exports.validateSingleEventSize = validateSingleEventSize;
|
|
5132
5506
|
//# sourceMappingURL=index.js.map
|