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/esm/index.js
CHANGED
|
@@ -4049,6 +4049,123 @@ function v1Bytes(rnds, msecs, nsecs, clockseq, node, buf, offset = 0) {
|
|
|
4049
4049
|
return buf;
|
|
4050
4050
|
}
|
|
4051
4051
|
|
|
4052
|
+
var LogLevel;
|
|
4053
|
+
(function (LogLevel) {
|
|
4054
|
+
LogLevel[LogLevel["NONE"] = 0] = "NONE";
|
|
4055
|
+
LogLevel[LogLevel["ERROR"] = 1] = "ERROR";
|
|
4056
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
4057
|
+
LogLevel[LogLevel["INFO"] = 3] = "INFO";
|
|
4058
|
+
LogLevel[LogLevel["DEBUG"] = 4] = "DEBUG";
|
|
4059
|
+
})(LogLevel || (LogLevel = {}));
|
|
4060
|
+
class Logger {
|
|
4061
|
+
constructor(config) {
|
|
4062
|
+
this.config = {
|
|
4063
|
+
level: LogLevel.ERROR, // Default to only errors in production
|
|
4064
|
+
enableConsole: true,
|
|
4065
|
+
enableStorage: false
|
|
4066
|
+
};
|
|
4067
|
+
this.isBrowser = typeof window !== 'undefined';
|
|
4068
|
+
if (config) {
|
|
4069
|
+
this.config = Object.assign(Object.assign({}, this.config), config);
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
setConfig(config) {
|
|
4073
|
+
this.config = Object.assign(Object.assign({}, this.config), config);
|
|
4074
|
+
}
|
|
4075
|
+
shouldLog(level) {
|
|
4076
|
+
return level <= this.config.level;
|
|
4077
|
+
}
|
|
4078
|
+
formatMessage(level, message, ...args) {
|
|
4079
|
+
const timestamp = new Date().toISOString();
|
|
4080
|
+
return `[HumanBehavior ${level}] ${timestamp}: ${message}`;
|
|
4081
|
+
}
|
|
4082
|
+
error(message, ...args) {
|
|
4083
|
+
if (!this.shouldLog(LogLevel.ERROR))
|
|
4084
|
+
return;
|
|
4085
|
+
const formattedMessage = this.formatMessage('ERROR', message);
|
|
4086
|
+
if (this.config.enableConsole) {
|
|
4087
|
+
console.error(formattedMessage, ...args);
|
|
4088
|
+
}
|
|
4089
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
4090
|
+
this.logToStorage(formattedMessage, args);
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
warn(message, ...args) {
|
|
4094
|
+
if (!this.shouldLog(LogLevel.WARN))
|
|
4095
|
+
return;
|
|
4096
|
+
const formattedMessage = this.formatMessage('WARN', message);
|
|
4097
|
+
if (this.config.enableConsole) {
|
|
4098
|
+
console.warn(formattedMessage, ...args);
|
|
4099
|
+
}
|
|
4100
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
4101
|
+
this.logToStorage(formattedMessage, args);
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
info(message, ...args) {
|
|
4105
|
+
if (!this.shouldLog(LogLevel.INFO))
|
|
4106
|
+
return;
|
|
4107
|
+
const formattedMessage = this.formatMessage('INFO', message);
|
|
4108
|
+
if (this.config.enableConsole) {
|
|
4109
|
+
console.log(formattedMessage, ...args);
|
|
4110
|
+
}
|
|
4111
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
4112
|
+
this.logToStorage(formattedMessage, args);
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
debug(message, ...args) {
|
|
4116
|
+
if (!this.shouldLog(LogLevel.DEBUG))
|
|
4117
|
+
return;
|
|
4118
|
+
const formattedMessage = this.formatMessage('DEBUG', message);
|
|
4119
|
+
if (this.config.enableConsole) {
|
|
4120
|
+
console.log(formattedMessage, ...args);
|
|
4121
|
+
}
|
|
4122
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
4123
|
+
this.logToStorage(formattedMessage, args);
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
logToStorage(message, args) {
|
|
4127
|
+
try {
|
|
4128
|
+
const logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
|
|
4129
|
+
const logEntry = {
|
|
4130
|
+
message,
|
|
4131
|
+
args: args.length > 0 ? args : undefined,
|
|
4132
|
+
timestamp: Date.now()
|
|
4133
|
+
};
|
|
4134
|
+
logs.push(logEntry);
|
|
4135
|
+
// Keep only last 1000 logs to prevent storage bloat
|
|
4136
|
+
if (logs.length > 1000) {
|
|
4137
|
+
logs.splice(0, logs.length - 1000);
|
|
4138
|
+
}
|
|
4139
|
+
localStorage.setItem('human_behavior_logs', JSON.stringify(logs));
|
|
4140
|
+
}
|
|
4141
|
+
catch (e) {
|
|
4142
|
+
// Silently fail if storage is not available
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
getLogs() {
|
|
4146
|
+
if (!this.isBrowser)
|
|
4147
|
+
return [];
|
|
4148
|
+
try {
|
|
4149
|
+
return JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
|
|
4150
|
+
}
|
|
4151
|
+
catch (e) {
|
|
4152
|
+
return [];
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
clearLogs() {
|
|
4156
|
+
if (this.isBrowser) {
|
|
4157
|
+
localStorage.removeItem('human_behavior_logs');
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
// Create singleton instance
|
|
4162
|
+
const logger = new Logger();
|
|
4163
|
+
// Export convenience methods
|
|
4164
|
+
const logError = (message, ...args) => logger.error(message, ...args);
|
|
4165
|
+
const logWarn = (message, ...args) => logger.warn(message, ...args);
|
|
4166
|
+
const logInfo = (message, ...args) => logger.info(message, ...args);
|
|
4167
|
+
const logDebug = (message, ...args) => logger.debug(message, ...args);
|
|
4168
|
+
|
|
4052
4169
|
const MAX_CHUNK_SIZE_BYTES = 1024 * 1024 * 10; // 10MB chunk size
|
|
4053
4170
|
function isChunkSizeExceeded(currentChunk, newEvent, sessionId) {
|
|
4054
4171
|
const nextChunkSize = new TextEncoder().encode(JSON.stringify({
|
|
@@ -4180,7 +4297,7 @@ class HumanBehaviorAPI {
|
|
|
4180
4297
|
return results.flat();
|
|
4181
4298
|
}
|
|
4182
4299
|
catch (error) {
|
|
4183
|
-
|
|
4300
|
+
logError('Error sending events:', error);
|
|
4184
4301
|
throw error;
|
|
4185
4302
|
}
|
|
4186
4303
|
});
|
|
@@ -4206,7 +4323,7 @@ class HumanBehaviorAPI {
|
|
|
4206
4323
|
return yield response.json();
|
|
4207
4324
|
}
|
|
4208
4325
|
catch (error) {
|
|
4209
|
-
|
|
4326
|
+
logError('Error sending user data:', error);
|
|
4210
4327
|
throw error;
|
|
4211
4328
|
}
|
|
4212
4329
|
});
|
|
@@ -4233,134 +4350,21 @@ class HumanBehaviorAPI {
|
|
|
4233
4350
|
return yield response.json();
|
|
4234
4351
|
}
|
|
4235
4352
|
catch (error) {
|
|
4236
|
-
|
|
4353
|
+
logError('Error authenticating user:', error);
|
|
4237
4354
|
throw error;
|
|
4238
4355
|
}
|
|
4239
4356
|
});
|
|
4240
4357
|
}
|
|
4241
|
-
|
|
4242
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4243
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/sessionComplete`, {
|
|
4244
|
-
method: 'POST',
|
|
4245
|
-
headers: {
|
|
4246
|
-
'Content-Type': 'application/json',
|
|
4247
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4248
|
-
},
|
|
4249
|
-
body: JSON.stringify({ sessionId })
|
|
4250
|
-
});
|
|
4251
|
-
if (!response.ok) {
|
|
4252
|
-
throw new Error(`Failed to send session complete: ${response.statusText}`);
|
|
4253
|
-
}
|
|
4254
|
-
});
|
|
4255
|
-
}
|
|
4256
|
-
sendCustomEvent(eventName, eventProperties, sessionId) {
|
|
4257
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4258
|
-
const maxRetries = 3;
|
|
4259
|
-
let retryCount = 0;
|
|
4260
|
-
while (retryCount < maxRetries) {
|
|
4261
|
-
try {
|
|
4262
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/customEvent`, {
|
|
4263
|
-
method: 'POST',
|
|
4264
|
-
headers: {
|
|
4265
|
-
'Content-Type': 'application/json',
|
|
4266
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4267
|
-
},
|
|
4268
|
-
body: JSON.stringify({
|
|
4269
|
-
name: eventName,
|
|
4270
|
-
properties: eventProperties,
|
|
4271
|
-
sessionId: sessionId,
|
|
4272
|
-
timestamp: new Date().toISOString()
|
|
4273
|
-
})
|
|
4274
|
-
});
|
|
4275
|
-
if (!response.ok) {
|
|
4276
|
-
throw new Error(`Failed to send custom event: ${response.statusText}`);
|
|
4277
|
-
}
|
|
4278
|
-
return yield response.json();
|
|
4279
|
-
}
|
|
4280
|
-
catch (error) {
|
|
4281
|
-
retryCount++;
|
|
4282
|
-
if (retryCount === maxRetries) {
|
|
4283
|
-
console.error('Error sending custom event after max retries:', error);
|
|
4284
|
-
throw error;
|
|
4285
|
-
}
|
|
4286
|
-
// Exponential backoff
|
|
4287
|
-
yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
|
|
4288
|
-
}
|
|
4289
|
-
}
|
|
4290
|
-
});
|
|
4291
|
-
}
|
|
4292
|
-
sendCustomEvents(events, sessionId) {
|
|
4293
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4294
|
-
const maxRetries = 3;
|
|
4295
|
-
let retryCount = 0;
|
|
4296
|
-
while (retryCount < maxRetries) {
|
|
4297
|
-
try {
|
|
4298
|
-
const response = yield fetch(`${this.baseUrl}/api/ingestion/customEvent/batch`, {
|
|
4299
|
-
method: 'POST',
|
|
4300
|
-
headers: {
|
|
4301
|
-
'Content-Type': 'application/json',
|
|
4302
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
4303
|
-
},
|
|
4304
|
-
body: JSON.stringify({
|
|
4305
|
-
events: events.map(event => (Object.assign(Object.assign({}, event), { sessionId: sessionId })))
|
|
4306
|
-
})
|
|
4307
|
-
});
|
|
4308
|
-
if (!response.ok) {
|
|
4309
|
-
throw new Error(`Failed to send custom events: ${response.statusText}`);
|
|
4310
|
-
}
|
|
4311
|
-
return yield response.json();
|
|
4312
|
-
}
|
|
4313
|
-
catch (error) {
|
|
4314
|
-
retryCount++;
|
|
4315
|
-
if (retryCount === maxRetries) {
|
|
4316
|
-
console.error('Error sending custom events after max retries:', error);
|
|
4317
|
-
throw error;
|
|
4318
|
-
}
|
|
4319
|
-
// Exponential backoff
|
|
4320
|
-
yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
|
|
4321
|
-
}
|
|
4322
|
-
}
|
|
4323
|
-
});
|
|
4324
|
-
}
|
|
4325
|
-
sendBeaconEvents(events, sessionId, isSessionComplete = false) {
|
|
4358
|
+
sendBeaconEvents(events, sessionId) {
|
|
4326
4359
|
const data = new URLSearchParams();
|
|
4327
4360
|
data.append('events', encodeURIComponent(JSON.stringify(events)));
|
|
4328
4361
|
data.append('sessionId', encodeURIComponent(sessionId));
|
|
4329
4362
|
data.append('timestamp', encodeURIComponent(Date.now().toString()));
|
|
4330
4363
|
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4331
|
-
if (isSessionComplete) {
|
|
4332
|
-
console.log('Session complete beacon sending');
|
|
4333
|
-
localStorage.setItem('koalaware_session_complete', Date.now().toString());
|
|
4334
|
-
data.append('sessionComplete', encodeURIComponent('true'));
|
|
4335
|
-
}
|
|
4336
4364
|
navigator.sendBeacon(`${this.baseUrl}/api/ingestion/events`, data);
|
|
4337
4365
|
// KoalawareTracker.logToStorage(`Sending events beacon: ${this.baseUrl}/api/ingestion/events`);
|
|
4338
4366
|
// KoalawareTracker.logToStorage(`Events beacon success: ${success}`);
|
|
4339
4367
|
}
|
|
4340
|
-
sendBeaconSessionComplete(sessionId) {
|
|
4341
|
-
const data = new URLSearchParams();
|
|
4342
|
-
data.append('sessionId', sessionId);
|
|
4343
|
-
data.append('apiKey', this.apiKey);
|
|
4344
|
-
data.append('sessionComplete', 'true');
|
|
4345
|
-
navigator.sendBeacon(`${this.baseUrl}/api/ingestion/sessionComplete`, data);
|
|
4346
|
-
// KoalawareTracker.logToStorage(`Sending completion beacon: ${this.baseUrl}/api/ingestion/sessionComplete`);
|
|
4347
|
-
// KoalawareTracker.logToStorage(`Complete beacon success: ${success}`);
|
|
4348
|
-
}
|
|
4349
|
-
sendBeaconCustomEvent(eventName, eventProperties, sessionId) {
|
|
4350
|
-
const data = new URLSearchParams();
|
|
4351
|
-
data.append('name', encodeURIComponent(eventName));
|
|
4352
|
-
data.append('properties', encodeURIComponent(JSON.stringify(eventProperties)));
|
|
4353
|
-
data.append('sessionId', encodeURIComponent(sessionId));
|
|
4354
|
-
data.append('timestamp', encodeURIComponent(new Date().toISOString()));
|
|
4355
|
-
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4356
|
-
return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/customEvent`, data);
|
|
4357
|
-
}
|
|
4358
|
-
sendBeaconCustomEvents(events, sessionId) {
|
|
4359
|
-
const data = new URLSearchParams();
|
|
4360
|
-
data.append('events', encodeURIComponent(JSON.stringify(events.map(event => (Object.assign(Object.assign({}, event), { sessionId: sessionId }))))));
|
|
4361
|
-
data.append('apiKey', encodeURIComponent(this.apiKey));
|
|
4362
|
-
return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/customEvent/batch`, data);
|
|
4363
|
-
}
|
|
4364
4368
|
}
|
|
4365
4369
|
|
|
4366
4370
|
// Redaction functionality for sensitive input fields
|
|
@@ -4393,18 +4397,18 @@ class RedactionManager {
|
|
|
4393
4397
|
this.userSelectedFields.clear();
|
|
4394
4398
|
fields.forEach(field => this.userSelectedFields.add(field));
|
|
4395
4399
|
if (fields.length > 0) {
|
|
4396
|
-
|
|
4400
|
+
logDebug(`Redaction: Active for ${fields.length} field(s):`, fields);
|
|
4397
4401
|
// Debug: Check if elements exist
|
|
4398
4402
|
fields.forEach(selector => {
|
|
4399
4403
|
const elements = document.querySelectorAll(selector);
|
|
4400
|
-
|
|
4404
|
+
logDebug(`Redaction: Found ${elements.length} element(s) for selector '${selector}'`);
|
|
4401
4405
|
elements.forEach((el, index) => {
|
|
4402
|
-
|
|
4406
|
+
logDebug(`Redaction: Element ${index} for '${selector}':`, el);
|
|
4403
4407
|
});
|
|
4404
4408
|
});
|
|
4405
4409
|
}
|
|
4406
4410
|
else {
|
|
4407
|
-
|
|
4411
|
+
logDebug('Redaction: Disabled - no fields selected');
|
|
4408
4412
|
}
|
|
4409
4413
|
}
|
|
4410
4414
|
/**
|
|
@@ -4434,7 +4438,7 @@ class RedactionManager {
|
|
|
4434
4438
|
if (processedEvent.data.source === 5) { // Input event
|
|
4435
4439
|
const shouldRedact = this.isFieldSelected(processedEvent.data);
|
|
4436
4440
|
if (shouldRedact) {
|
|
4437
|
-
|
|
4441
|
+
logDebug('Redaction: Processing input event for redaction');
|
|
4438
4442
|
this.redactInputEvent(processedEvent.data);
|
|
4439
4443
|
}
|
|
4440
4444
|
}
|
|
@@ -4460,30 +4464,30 @@ class RedactionManager {
|
|
|
4460
4464
|
if (!this.isFieldSelected(inputData)) {
|
|
4461
4465
|
return;
|
|
4462
4466
|
}
|
|
4463
|
-
|
|
4467
|
+
logDebug('Redaction: Redacting input event with text:', inputData.text);
|
|
4464
4468
|
// Redact all text-related properties that could contain input data
|
|
4465
4469
|
const textProperties = ['text', 'value', 'content', 'data', 'input', 'textContent'];
|
|
4466
4470
|
textProperties.forEach(prop => {
|
|
4467
4471
|
if (inputData[prop] !== undefined && typeof inputData[prop] === 'string') {
|
|
4468
4472
|
inputData[prop] = this.redactedText;
|
|
4469
|
-
|
|
4473
|
+
logDebug(`Redaction: Redacted property '${prop}'`);
|
|
4470
4474
|
}
|
|
4471
4475
|
});
|
|
4472
4476
|
// Also check for any other string properties that might contain input data
|
|
4473
4477
|
Object.keys(inputData).forEach(key => {
|
|
4474
4478
|
if (typeof inputData[key] === 'string' && inputData[key].length > 0) {
|
|
4475
4479
|
inputData[key] = this.redactedText;
|
|
4476
|
-
|
|
4480
|
+
logDebug(`Redaction: Redacted additional property '${key}'`);
|
|
4477
4481
|
}
|
|
4478
4482
|
});
|
|
4479
4483
|
// Handle nested objects that might contain text data
|
|
4480
4484
|
if (inputData.attributes && typeof inputData.attributes === 'object') {
|
|
4481
4485
|
if (inputData.attributes.value && typeof inputData.attributes.value === 'string') {
|
|
4482
4486
|
inputData.attributes.value = this.redactedText;
|
|
4483
|
-
|
|
4487
|
+
logDebug('Redaction: Redacted nested value attribute');
|
|
4484
4488
|
}
|
|
4485
4489
|
}
|
|
4486
|
-
|
|
4490
|
+
logDebug('Redaction: Input event redaction complete');
|
|
4487
4491
|
}
|
|
4488
4492
|
/**
|
|
4489
4493
|
* Redact sensitive data in DOM mutation events
|
|
@@ -4545,7 +4549,7 @@ class RedactionManager {
|
|
|
4545
4549
|
return false;
|
|
4546
4550
|
}
|
|
4547
4551
|
catch (e) {
|
|
4548
|
-
|
|
4552
|
+
logWarn('Error checking if DOM change should be redacted:', e);
|
|
4549
4553
|
return false;
|
|
4550
4554
|
}
|
|
4551
4555
|
}
|
|
@@ -4692,7 +4696,7 @@ class RedactionManager {
|
|
|
4692
4696
|
// Check if any of these elements are currently focused
|
|
4693
4697
|
for (const el of matchingElements) {
|
|
4694
4698
|
if (el === document.activeElement) {
|
|
4695
|
-
|
|
4699
|
+
logDebug('Redaction: Found focused element matching selector:', selector);
|
|
4696
4700
|
return true;
|
|
4697
4701
|
}
|
|
4698
4702
|
}
|
|
@@ -4702,7 +4706,7 @@ class RedactionManager {
|
|
|
4702
4706
|
// Look for any input element that might be the active one
|
|
4703
4707
|
const activeElement = document.activeElement;
|
|
4704
4708
|
if (activeElement && this.shouldRedactElement(activeElement)) {
|
|
4705
|
-
|
|
4709
|
+
logDebug('Redaction: Active element should be redacted');
|
|
4706
4710
|
return true;
|
|
4707
4711
|
}
|
|
4708
4712
|
return false;
|
|
@@ -4735,7 +4739,7 @@ class RedactionManager {
|
|
|
4735
4739
|
return false;
|
|
4736
4740
|
}
|
|
4737
4741
|
catch (e) {
|
|
4738
|
-
|
|
4742
|
+
logWarn('Error checking if field should be redacted:', e);
|
|
4739
4743
|
return false;
|
|
4740
4744
|
}
|
|
4741
4745
|
}
|
|
@@ -4779,6 +4783,40 @@ const redactionManager = new RedactionManager();
|
|
|
4779
4783
|
// Check if we're in a browser environment
|
|
4780
4784
|
const isBrowser = typeof window !== 'undefined';
|
|
4781
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
|
+
}
|
|
4782
4820
|
constructor(apiKey, ingestionUrl) {
|
|
4783
4821
|
this.eventIngestionQueue = [];
|
|
4784
4822
|
this.queueSizeBytes = 0;
|
|
@@ -4791,61 +4829,82 @@ class HumanBehaviorTracker {
|
|
|
4791
4829
|
this.endUserId = null;
|
|
4792
4830
|
this.initialized = false;
|
|
4793
4831
|
this.initializationPromise = null;
|
|
4832
|
+
// Console tracking properties
|
|
4833
|
+
this.originalConsole = null;
|
|
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;
|
|
4794
4843
|
if (!apiKey) {
|
|
4795
4844
|
throw new Error('Human Behavior API Key is required');
|
|
4796
4845
|
}
|
|
4797
|
-
//
|
|
4798
|
-
|
|
4799
|
-
// ========================================
|
|
4800
|
-
// Uncomment ONE of the following lines to select your server:
|
|
4801
|
-
// AWS Development Server
|
|
4802
|
-
const defaultIngestionUrl = 'http://3.137.217.33:3000';
|
|
4803
|
-
// Vercel Production Server
|
|
4804
|
-
// const defaultIngestionUrl = 'https://ingestion-server.vercel.app';
|
|
4805
|
-
// Local Development Server
|
|
4806
|
-
// const defaultIngestionUrl = 'http://localhost:3000';
|
|
4846
|
+
// Initialize API
|
|
4847
|
+
const defaultIngestionUrl = 'http://3.137.217.33:3000'; // AWS Development Server
|
|
4807
4848
|
this.api = new HumanBehaviorAPI({
|
|
4808
4849
|
apiKey: apiKey,
|
|
4809
4850
|
ingestionUrl: ingestionUrl || defaultIngestionUrl
|
|
4810
4851
|
});
|
|
4811
4852
|
this.apiKey = apiKey;
|
|
4812
4853
|
this.redactionManager = new RedactionManager();
|
|
4813
|
-
//
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
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();
|
|
4823
4873
|
}
|
|
4824
|
-
// Start initialization
|
|
4874
|
+
// Start initialization
|
|
4825
4875
|
this.initializationPromise = this.init();
|
|
4826
4876
|
}
|
|
4827
4877
|
init() {
|
|
4828
4878
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4829
4879
|
try {
|
|
4830
4880
|
const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
|
|
4881
|
+
logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
|
|
4831
4882
|
const { sessionId, endUserId } = yield this.api.init(this.sessionId, userId);
|
|
4832
|
-
|
|
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
|
+
}
|
|
4833
4892
|
this.endUserId = endUserId;
|
|
4834
4893
|
this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
|
|
4835
4894
|
// Only setup browser-specific handlers when in browser environment
|
|
4836
4895
|
if (isBrowser) {
|
|
4837
4896
|
this.setupPageUnloadHandler();
|
|
4838
|
-
this.
|
|
4897
|
+
this.setupNavigationTracking();
|
|
4839
4898
|
this.processRejectedEvents();
|
|
4840
4899
|
}
|
|
4841
4900
|
else {
|
|
4842
|
-
|
|
4901
|
+
logWarn('HumanBehaviorTracker initialized in a non-browser environment. Session tracking is disabled.');
|
|
4843
4902
|
}
|
|
4844
4903
|
this.initialized = true;
|
|
4845
|
-
|
|
4904
|
+
logInfo(`HumanBehaviorTracker initialized with sessionId: ${this.sessionId}, endUserId: ${endUserId}`);
|
|
4846
4905
|
}
|
|
4847
4906
|
catch (error) {
|
|
4848
|
-
|
|
4907
|
+
logError('Failed to initialize HumanBehaviorTracker:', error);
|
|
4849
4908
|
throw error;
|
|
4850
4909
|
}
|
|
4851
4910
|
});
|
|
@@ -4858,48 +4917,295 @@ class HumanBehaviorTracker {
|
|
|
4858
4917
|
yield this.initializationPromise;
|
|
4859
4918
|
});
|
|
4860
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
|
+
}
|
|
4861
5085
|
static logToStorage(message) {
|
|
5086
|
+
logInfo(message);
|
|
5087
|
+
}
|
|
5088
|
+
/**
|
|
5089
|
+
* Configure logging behavior for the SDK
|
|
5090
|
+
* @param config Logger configuration options
|
|
5091
|
+
*/
|
|
5092
|
+
static configureLogging(config) {
|
|
5093
|
+
const levelMap = {
|
|
5094
|
+
'none': 0,
|
|
5095
|
+
'error': 1,
|
|
5096
|
+
'warn': 2,
|
|
5097
|
+
'info': 3,
|
|
5098
|
+
'debug': 4
|
|
5099
|
+
};
|
|
5100
|
+
logger.setConfig({
|
|
5101
|
+
level: levelMap[config.level || 'error'],
|
|
5102
|
+
enableConsole: config.enableConsole !== false,
|
|
5103
|
+
enableStorage: config.enableStorage || false
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
/**
|
|
5107
|
+
* Enable console event tracking
|
|
5108
|
+
*/
|
|
5109
|
+
enableConsoleTracking() {
|
|
5110
|
+
if (!isBrowser || this.consoleTrackingEnabled)
|
|
5111
|
+
return;
|
|
5112
|
+
// Store original console methods
|
|
5113
|
+
this.originalConsole = {
|
|
5114
|
+
log: console.log,
|
|
5115
|
+
warn: console.warn,
|
|
5116
|
+
error: console.error
|
|
5117
|
+
};
|
|
5118
|
+
// Override console methods to capture ALL console output (including logger output)
|
|
5119
|
+
console.log = (...args) => {
|
|
5120
|
+
this.trackConsoleEvent('log', args);
|
|
5121
|
+
this.originalConsole.log(...args);
|
|
5122
|
+
};
|
|
5123
|
+
console.warn = (...args) => {
|
|
5124
|
+
this.trackConsoleEvent('warn', args);
|
|
5125
|
+
this.originalConsole.warn(...args);
|
|
5126
|
+
};
|
|
5127
|
+
console.error = (...args) => {
|
|
5128
|
+
this.trackConsoleEvent('error', args);
|
|
5129
|
+
this.originalConsole.error(...args);
|
|
5130
|
+
};
|
|
5131
|
+
this.consoleTrackingEnabled = true;
|
|
5132
|
+
logDebug('Console tracking enabled');
|
|
5133
|
+
}
|
|
5134
|
+
/**
|
|
5135
|
+
* Disable console event tracking
|
|
5136
|
+
*/
|
|
5137
|
+
disableConsoleTracking() {
|
|
5138
|
+
if (!isBrowser || !this.consoleTrackingEnabled)
|
|
5139
|
+
return;
|
|
5140
|
+
// Restore original console methods
|
|
5141
|
+
if (this.originalConsole) {
|
|
5142
|
+
console.log = this.originalConsole.log;
|
|
5143
|
+
console.warn = this.originalConsole.warn;
|
|
5144
|
+
console.error = this.originalConsole.error;
|
|
5145
|
+
}
|
|
5146
|
+
this.consoleTrackingEnabled = false;
|
|
5147
|
+
logDebug('Console tracking disabled');
|
|
5148
|
+
}
|
|
5149
|
+
trackConsoleEvent(level, args) {
|
|
5150
|
+
if (!this.initialized)
|
|
5151
|
+
return;
|
|
4862
5152
|
try {
|
|
4863
|
-
const
|
|
4864
|
-
|
|
4865
|
-
|
|
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
|
+
});
|
|
4866
5169
|
}
|
|
4867
|
-
catch (
|
|
4868
|
-
|
|
5170
|
+
catch (error) {
|
|
5171
|
+
logError('Error in trackConsoleEvent:', error);
|
|
4869
5172
|
}
|
|
4870
5173
|
}
|
|
4871
5174
|
setupPageUnloadHandler() {
|
|
4872
5175
|
if (!isBrowser)
|
|
4873
5176
|
return;
|
|
4874
|
-
|
|
5177
|
+
logDebug('Setting up page unload handler');
|
|
4875
5178
|
// Handle visibility changes for sending events
|
|
4876
5179
|
window.addEventListener('visibilitychange', () => {
|
|
4877
5180
|
// Only send events when page becomes hidden
|
|
4878
5181
|
if (document.visibilityState === 'hidden') {
|
|
4879
|
-
|
|
5182
|
+
logDebug('Page hidden - sending pending events');
|
|
4880
5183
|
this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
|
|
4881
5184
|
}
|
|
4882
5185
|
});
|
|
4883
5186
|
// Handle actual page unload/close
|
|
4884
5187
|
window.addEventListener('beforeunload', () => {
|
|
4885
|
-
// Update last activity time
|
|
4886
|
-
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
4887
5188
|
// Send final events
|
|
4888
5189
|
this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
|
|
4889
5190
|
});
|
|
4890
|
-
// Update activity timestamp
|
|
4891
|
-
|
|
5191
|
+
// Update activity timestamp on user interaction (not on page load)
|
|
5192
|
+
const updateActivity = () => {
|
|
4892
5193
|
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
4893
|
-
}
|
|
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);
|
|
4894
5200
|
}
|
|
4895
5201
|
viewLogs() {
|
|
4896
5202
|
try {
|
|
4897
|
-
const logs =
|
|
5203
|
+
const logs = logger.getLogs();
|
|
4898
5204
|
console.log('HumanBehavior Logs:', logs);
|
|
4899
|
-
|
|
5205
|
+
logger.clearLogs(); // Clear logs after viewing
|
|
4900
5206
|
}
|
|
4901
5207
|
catch (e) {
|
|
4902
|
-
|
|
5208
|
+
logError('Failed to read logs:', e);
|
|
4903
5209
|
}
|
|
4904
5210
|
}
|
|
4905
5211
|
addUserInfo(userProperties) {
|
|
@@ -4928,12 +5234,6 @@ class HumanBehaviorTracker {
|
|
|
4928
5234
|
yield this.api.sendUserAuth(this.endUserId, this.userProperties, this.sessionId, authFields);
|
|
4929
5235
|
});
|
|
4930
5236
|
}
|
|
4931
|
-
customEvent(eventName_1) {
|
|
4932
|
-
return __awaiter$1(this, arguments, void 0, function* (eventName, eventProperties = {}) {
|
|
4933
|
-
yield this.ensureInitialized();
|
|
4934
|
-
this.api.sendBeaconCustomEvent(eventName, eventProperties, this.sessionId);
|
|
4935
|
-
});
|
|
4936
|
-
}
|
|
4937
5237
|
start() {
|
|
4938
5238
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4939
5239
|
yield this.ensureInitialized();
|
|
@@ -4943,6 +5243,8 @@ class HumanBehaviorTracker {
|
|
|
4943
5243
|
this.flushInterval = window.setInterval(() => {
|
|
4944
5244
|
this.flush();
|
|
4945
5245
|
}, this.FLUSH_INTERVAL_MS);
|
|
5246
|
+
// Enable console tracking
|
|
5247
|
+
this.enableConsoleTracking();
|
|
4946
5248
|
// Start recording with redaction enabled
|
|
4947
5249
|
record({
|
|
4948
5250
|
emit: (event) => {
|
|
@@ -4966,6 +5268,10 @@ class HumanBehaviorTracker {
|
|
|
4966
5268
|
clearInterval(this.flushInterval);
|
|
4967
5269
|
this.flushInterval = null;
|
|
4968
5270
|
}
|
|
5271
|
+
// Disable console tracking
|
|
5272
|
+
this.disableConsoleTracking();
|
|
5273
|
+
// Cleanup navigation tracking
|
|
5274
|
+
this.cleanupNavigationTracking();
|
|
4969
5275
|
});
|
|
4970
5276
|
}
|
|
4971
5277
|
addEvent(event) {
|
|
@@ -4999,7 +5305,7 @@ class HumanBehaviorTracker {
|
|
|
4999
5305
|
this.sessionId = newSessionId;
|
|
5000
5306
|
}
|
|
5001
5307
|
catch (error) {
|
|
5002
|
-
|
|
5308
|
+
logError('Failed to process rejected events:', error);
|
|
5003
5309
|
}
|
|
5004
5310
|
finally {
|
|
5005
5311
|
this.isProcessingRejectedEvents = false;
|
|
@@ -5008,7 +5314,7 @@ class HumanBehaviorTracker {
|
|
|
5008
5314
|
}
|
|
5009
5315
|
flush() {
|
|
5010
5316
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5011
|
-
var _a;
|
|
5317
|
+
var _a, _b, _c, _d;
|
|
5012
5318
|
// Prevent concurrent flushes
|
|
5013
5319
|
if (this.isProcessing || !this.initialized) {
|
|
5014
5320
|
return;
|
|
@@ -5020,17 +5326,25 @@ class HumanBehaviorTracker {
|
|
|
5020
5326
|
this.eventIngestionQueue = [];
|
|
5021
5327
|
this.queueSizeBytes = 0;
|
|
5022
5328
|
if (eventsToProcess.length > 0) {
|
|
5023
|
-
|
|
5329
|
+
logDebug('Flushing events:', eventsToProcess);
|
|
5024
5330
|
try {
|
|
5025
5331
|
yield this.api.sendEvents(eventsToProcess, this.sessionId, this.endUserId);
|
|
5026
5332
|
}
|
|
5027
5333
|
catch (error) {
|
|
5028
5334
|
// If we get a 400 error, store events for retry
|
|
5029
5335
|
if ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('ERROR: Session already completed')) {
|
|
5030
|
-
|
|
5336
|
+
logInfo('Session expired, storing events for retry');
|
|
5031
5337
|
this.rejectedEvents.push(...eventsToProcess);
|
|
5032
5338
|
this.processRejectedEvents();
|
|
5033
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
|
+
}
|
|
5034
5348
|
else {
|
|
5035
5349
|
throw error;
|
|
5036
5350
|
}
|
|
@@ -5073,7 +5387,7 @@ class HumanBehaviorTracker {
|
|
|
5073
5387
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5074
5388
|
yield this.ensureInitialized();
|
|
5075
5389
|
if (!isBrowser) {
|
|
5076
|
-
|
|
5390
|
+
logWarn('Redaction is only available in browser environments');
|
|
5077
5391
|
return;
|
|
5078
5392
|
}
|
|
5079
5393
|
// Create a new redaction manager with the provided options
|
|
@@ -5086,7 +5400,7 @@ class HumanBehaviorTracker {
|
|
|
5086
5400
|
*/
|
|
5087
5401
|
setRedactedFields(fields) {
|
|
5088
5402
|
if (!isBrowser) {
|
|
5089
|
-
|
|
5403
|
+
logWarn('Redaction is only available in browser environments');
|
|
5090
5404
|
return;
|
|
5091
5405
|
}
|
|
5092
5406
|
this.redactionManager.setFieldsToRedact(fields);
|
|
@@ -5103,6 +5417,61 @@ class HumanBehaviorTracker {
|
|
|
5103
5417
|
getRedactedFields() {
|
|
5104
5418
|
return this.redactionManager.getSelectedFields();
|
|
5105
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
|
+
}
|
|
5106
5475
|
}
|
|
5107
5476
|
// Only expose to window object in browser environments
|
|
5108
5477
|
if (isBrowser) {
|
|
@@ -5117,5 +5486,5 @@ if (typeof window !== 'undefined') {
|
|
|
5117
5486
|
window.HumanBehaviorTracker = HumanBehaviorTracker;
|
|
5118
5487
|
}
|
|
5119
5488
|
|
|
5120
|
-
export { HumanBehaviorAPI, HumanBehaviorTracker, MAX_CHUNK_SIZE_BYTES, RedactionManager, HumanBehaviorTracker as default, isChunkSizeExceeded, redactionManager, validateSingleEventSize };
|
|
5489
|
+
export { HumanBehaviorAPI, HumanBehaviorTracker, LogLevel, MAX_CHUNK_SIZE_BYTES, RedactionManager, HumanBehaviorTracker as default, isChunkSizeExceeded, logDebug, logError, logInfo, logWarn, logger, redactionManager, validateSingleEventSize };
|
|
5121
5490
|
//# sourceMappingURL=index.js.map
|