humanbehavior-js 0.1.7 → 0.1.9
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 +74 -57
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/index.js +13 -2
- package/dist/cjs/react/index.js.map +1 -1
- package/dist/esm/index.js +74 -58
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/index.js +13 -2
- 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 +3 -5
- package/package.json +2 -1
- package/readme.md +2 -0
- package/simple-spa.html +75 -11
- package/src/api.ts +64 -10
- package/src/react/index.tsx +4 -3
- package/src/tracker.ts +22 -43
package/dist/cjs/index.js
CHANGED
|
@@ -4170,7 +4170,7 @@ const logWarn = (message, ...args) => logger.warn(message, ...args);
|
|
|
4170
4170
|
const logInfo = (message, ...args) => logger.info(message, ...args);
|
|
4171
4171
|
const logDebug = (message, ...args) => logger.debug(message, ...args);
|
|
4172
4172
|
|
|
4173
|
-
const MAX_CHUNK_SIZE_BYTES = 1024 * 1024
|
|
4173
|
+
const MAX_CHUNK_SIZE_BYTES = 1024 * 1024; // 1MB chunk size - more conservative
|
|
4174
4174
|
function isChunkSizeExceeded(currentChunk, newEvent, sessionId) {
|
|
4175
4175
|
const nextChunkSize = new TextEncoder().encode(JSON.stringify({
|
|
4176
4176
|
sessionId,
|
|
@@ -4184,9 +4184,42 @@ function validateSingleEventSize(event, sessionId) {
|
|
|
4184
4184
|
events: [event]
|
|
4185
4185
|
})).length;
|
|
4186
4186
|
if (singleEventSize > MAX_CHUNK_SIZE_BYTES) {
|
|
4187
|
-
|
|
4187
|
+
// Instead of throwing, log a warning and suggest reducing event size
|
|
4188
|
+
console.warn(`Single event size (${singleEventSize} bytes) exceeds maximum chunk size (${MAX_CHUNK_SIZE_BYTES} bytes). Consider reducing event data size.`);
|
|
4188
4189
|
}
|
|
4189
4190
|
}
|
|
4191
|
+
function splitLargeEvent(event, sessionId) {
|
|
4192
|
+
const eventSize = new TextEncoder().encode(JSON.stringify({
|
|
4193
|
+
sessionId,
|
|
4194
|
+
events: [event]
|
|
4195
|
+
})).length;
|
|
4196
|
+
if (eventSize <= MAX_CHUNK_SIZE_BYTES) {
|
|
4197
|
+
return [event];
|
|
4198
|
+
}
|
|
4199
|
+
// If event is too large, try to split it by removing large properties
|
|
4200
|
+
const simplifiedEvent = Object.assign({}, event);
|
|
4201
|
+
// Remove potentially large properties
|
|
4202
|
+
const largeProperties = ['screenshot', 'html', 'dom', 'fullText', 'innerHTML', 'outerHTML'];
|
|
4203
|
+
largeProperties.forEach(prop => {
|
|
4204
|
+
if (simplifiedEvent[prop]) {
|
|
4205
|
+
delete simplifiedEvent[prop];
|
|
4206
|
+
}
|
|
4207
|
+
});
|
|
4208
|
+
// Check if simplified event is now small enough
|
|
4209
|
+
const simplifiedSize = new TextEncoder().encode(JSON.stringify({
|
|
4210
|
+
sessionId,
|
|
4211
|
+
events: [simplifiedEvent]
|
|
4212
|
+
})).length;
|
|
4213
|
+
if (simplifiedSize <= MAX_CHUNK_SIZE_BYTES) {
|
|
4214
|
+
return [simplifiedEvent];
|
|
4215
|
+
}
|
|
4216
|
+
// If still too large, create a minimal event
|
|
4217
|
+
const minimalEvent = Object.assign({ type: event.type, timestamp: event.timestamp, url: event.url, pathname: event.pathname }, Object.fromEntries(Object.entries(event).filter(([key, value]) => !largeProperties.includes(key) &&
|
|
4218
|
+
typeof value !== 'object' &&
|
|
4219
|
+
typeof value !== 'string' ||
|
|
4220
|
+
(typeof value === 'string' && value.length < 1000))));
|
|
4221
|
+
return [minimalEvent];
|
|
4222
|
+
}
|
|
4190
4223
|
class HumanBehaviorAPI {
|
|
4191
4224
|
constructor({ apiKey, ingestionUrl }) {
|
|
4192
4225
|
this.apiKey = apiKey;
|
|
@@ -4255,7 +4288,7 @@ class HumanBehaviorAPI {
|
|
|
4255
4288
|
}
|
|
4256
4289
|
});
|
|
4257
4290
|
}
|
|
4258
|
-
sendEventsChunked(events, sessionId) {
|
|
4291
|
+
sendEventsChunked(events, sessionId, userId) {
|
|
4259
4292
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
4260
4293
|
try {
|
|
4261
4294
|
const results = [];
|
|
@@ -4272,7 +4305,8 @@ class HumanBehaviorAPI {
|
|
|
4272
4305
|
},
|
|
4273
4306
|
body: JSON.stringify({
|
|
4274
4307
|
sessionId,
|
|
4275
|
-
events: currentChunk
|
|
4308
|
+
events: currentChunk,
|
|
4309
|
+
endUserId: userId
|
|
4276
4310
|
})
|
|
4277
4311
|
});
|
|
4278
4312
|
if (!response.ok) {
|
|
@@ -4281,10 +4315,10 @@ class HumanBehaviorAPI {
|
|
|
4281
4315
|
results.push(yield response.json());
|
|
4282
4316
|
currentChunk = [];
|
|
4283
4317
|
}
|
|
4284
|
-
//
|
|
4285
|
-
|
|
4286
|
-
// Start new chunk with
|
|
4287
|
-
currentChunk =
|
|
4318
|
+
// Handle large events by splitting them
|
|
4319
|
+
const splitEvents = splitLargeEvent(event, sessionId);
|
|
4320
|
+
// Start new chunk with the split events
|
|
4321
|
+
currentChunk = splitEvents;
|
|
4288
4322
|
}
|
|
4289
4323
|
else {
|
|
4290
4324
|
// Add event to current chunk
|
|
@@ -4301,7 +4335,8 @@ class HumanBehaviorAPI {
|
|
|
4301
4335
|
},
|
|
4302
4336
|
body: JSON.stringify({
|
|
4303
4337
|
sessionId,
|
|
4304
|
-
events: currentChunk
|
|
4338
|
+
events: currentChunk,
|
|
4339
|
+
endUserId: userId
|
|
4305
4340
|
})
|
|
4306
4341
|
});
|
|
4307
4342
|
if (!response.ok) {
|
|
@@ -4401,7 +4436,7 @@ class HumanBehaviorAPI {
|
|
|
4401
4436
|
})
|
|
4402
4437
|
});
|
|
4403
4438
|
if (!response.ok) {
|
|
4404
|
-
throw new Error(`Failed to send custom event: ${response.statusText}`);
|
|
4439
|
+
throw new Error(`Failed to send custom event: ${response.status} ${response.statusText}`);
|
|
4405
4440
|
}
|
|
4406
4441
|
return yield response.json();
|
|
4407
4442
|
}
|
|
@@ -4895,8 +4930,6 @@ class HumanBehaviorTracker {
|
|
|
4895
4930
|
constructor(apiKey, ingestionUrl) {
|
|
4896
4931
|
this.eventIngestionQueue = [];
|
|
4897
4932
|
this.queueSizeBytes = 0;
|
|
4898
|
-
this.rejectedEvents = [];
|
|
4899
|
-
this.isProcessingRejectedEvents = false;
|
|
4900
4933
|
this.userProperties = {};
|
|
4901
4934
|
this.isProcessing = false;
|
|
4902
4935
|
this.flushInterval = null;
|
|
@@ -4982,7 +5015,6 @@ class HumanBehaviorTracker {
|
|
|
4982
5015
|
if (isBrowser) {
|
|
4983
5016
|
this.setupPageUnloadHandler();
|
|
4984
5017
|
this.setupNavigationTracking();
|
|
4985
|
-
this.processRejectedEvents();
|
|
4986
5018
|
}
|
|
4987
5019
|
else {
|
|
4988
5020
|
logWarn('HumanBehaviorTracker initialized in a non-browser environment. Session tracking is disabled.');
|
|
@@ -5125,6 +5157,7 @@ class HumanBehaviorTracker {
|
|
|
5125
5157
|
*/
|
|
5126
5158
|
customEvent(eventName, properties) {
|
|
5127
5159
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5160
|
+
var _a, _b, _c, _d, _e;
|
|
5128
5161
|
if (!this.initialized)
|
|
5129
5162
|
return;
|
|
5130
5163
|
try {
|
|
@@ -5134,7 +5167,19 @@ class HumanBehaviorTracker {
|
|
|
5134
5167
|
}
|
|
5135
5168
|
catch (error) {
|
|
5136
5169
|
logError('Failed to track custom event:', error);
|
|
5137
|
-
//
|
|
5170
|
+
// Handle specific error types - check for any custom event failure
|
|
5171
|
+
if (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('500')) ||
|
|
5172
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('Internal Server Error')) ||
|
|
5173
|
+
((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('Failed to send custom event'))) {
|
|
5174
|
+
logWarn('Custom event endpoint failed, using fallback');
|
|
5175
|
+
}
|
|
5176
|
+
else if ((_d = error.message) === null || _d === void 0 ? void 0 : _d.includes('ERR_BLOCKED_BY_CLIENT')) {
|
|
5177
|
+
logWarn('Custom event request blocked by ad blocker, using fallback');
|
|
5178
|
+
}
|
|
5179
|
+
else if ((_e = error.message) === null || _e === void 0 ? void 0 : _e.includes('Failed to fetch')) {
|
|
5180
|
+
logWarn('Custom event network error, using fallback');
|
|
5181
|
+
}
|
|
5182
|
+
// Always try fallback for any custom event error
|
|
5138
5183
|
try {
|
|
5139
5184
|
const customEventData = {
|
|
5140
5185
|
eventName: eventName,
|
|
@@ -5508,37 +5553,9 @@ class HumanBehaviorTracker {
|
|
|
5508
5553
|
this.queueSizeBytes += eventSize;
|
|
5509
5554
|
});
|
|
5510
5555
|
}
|
|
5511
|
-
processRejectedEvents() {
|
|
5512
|
-
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5513
|
-
if (this.isProcessingRejectedEvents || this.rejectedEvents.length === 0)
|
|
5514
|
-
return;
|
|
5515
|
-
this.isProcessingRejectedEvents = true;
|
|
5516
|
-
try {
|
|
5517
|
-
// Create a new session ID for rejected events
|
|
5518
|
-
const newSessionId = v1();
|
|
5519
|
-
if (isBrowser) {
|
|
5520
|
-
localStorage.setItem('human_behavior_session_id', newSessionId);
|
|
5521
|
-
}
|
|
5522
|
-
// Try to send rejected events with new session ID using beacon
|
|
5523
|
-
// sendBeacon returns true if the request was queued successfully
|
|
5524
|
-
this.api.sendBeaconEvents(this.rejectedEvents, newSessionId);
|
|
5525
|
-
// Clear rejected events and update session ID
|
|
5526
|
-
// Note: We can't verify if the beacon data was actually sent,
|
|
5527
|
-
// but we clear the events to prevent duplicate sending attempts
|
|
5528
|
-
this.rejectedEvents = [];
|
|
5529
|
-
this.sessionId = newSessionId;
|
|
5530
|
-
}
|
|
5531
|
-
catch (error) {
|
|
5532
|
-
logError('Failed to process rejected events:', error);
|
|
5533
|
-
}
|
|
5534
|
-
finally {
|
|
5535
|
-
this.isProcessingRejectedEvents = false;
|
|
5536
|
-
}
|
|
5537
|
-
});
|
|
5538
|
-
}
|
|
5539
5556
|
flush() {
|
|
5540
5557
|
return __awaiter$1(this, void 0, void 0, function* () {
|
|
5541
|
-
var _a, _b, _c, _d;
|
|
5558
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5542
5559
|
// Prevent concurrent flushes
|
|
5543
5560
|
if (this.isProcessing || !this.initialized) {
|
|
5544
5561
|
return;
|
|
@@ -5552,22 +5569,21 @@ class HumanBehaviorTracker {
|
|
|
5552
5569
|
if (eventsToProcess.length > 0) {
|
|
5553
5570
|
logDebug('Flushing events:', eventsToProcess);
|
|
5554
5571
|
try {
|
|
5555
|
-
|
|
5572
|
+
// Use chunked sending to handle large payloads
|
|
5573
|
+
yield this.api.sendEventsChunked(eventsToProcess, this.sessionId, this.endUserId);
|
|
5556
5574
|
}
|
|
5557
5575
|
catch (error) {
|
|
5558
|
-
//
|
|
5576
|
+
// Handle specific error types with graceful degradation
|
|
5559
5577
|
if ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('ERROR: Session already completed')) {
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5578
|
+
logWarn('Session expired, events will be lost');
|
|
5579
|
+
}
|
|
5580
|
+
else if (((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('413')) || ((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('Content Too Large'))) {
|
|
5581
|
+
logWarn('Payload too large, events will be lost');
|
|
5563
5582
|
}
|
|
5564
|
-
else if (((
|
|
5565
|
-
((
|
|
5566
|
-
((
|
|
5567
|
-
|
|
5568
|
-
logWarn('Request blocked by ad blocker or network issue, storing events for retry');
|
|
5569
|
-
this.rejectedEvents.push(...eventsToProcess);
|
|
5570
|
-
// Don't process rejected events immediately to avoid spam
|
|
5583
|
+
else if (((_d = error.message) === null || _d === void 0 ? void 0 : _d.includes('ERR_BLOCKED_BY_CLIENT')) ||
|
|
5584
|
+
((_e = error.message) === null || _e === void 0 ? void 0 : _e.includes('Failed to fetch')) ||
|
|
5585
|
+
((_f = error.message) === null || _f === void 0 ? void 0 : _f.includes('NetworkError'))) {
|
|
5586
|
+
logWarn('Request blocked by ad blocker or network issue, events will be lost');
|
|
5571
5587
|
}
|
|
5572
5588
|
else {
|
|
5573
5589
|
throw error;
|
|
@@ -5718,8 +5734,8 @@ class HumanBehaviorTracker {
|
|
|
5718
5734
|
getConnectionStatus() {
|
|
5719
5735
|
const recommendations = [];
|
|
5720
5736
|
let blocked = false;
|
|
5721
|
-
// Check if we have
|
|
5722
|
-
if (this.
|
|
5737
|
+
// Check if we have queued events (might indicate blocking)
|
|
5738
|
+
if (this.eventIngestionQueue.length > 0) {
|
|
5723
5739
|
blocked = true;
|
|
5724
5740
|
recommendations.push('Some requests may be blocked by ad blockers');
|
|
5725
5741
|
}
|
|
@@ -5787,5 +5803,6 @@ exports.logInfo = logInfo;
|
|
|
5787
5803
|
exports.logWarn = logWarn;
|
|
5788
5804
|
exports.logger = logger;
|
|
5789
5805
|
exports.redactionManager = redactionManager;
|
|
5806
|
+
exports.splitLargeEvent = splitLargeEvent;
|
|
5790
5807
|
exports.validateSingleEventSize = validateSingleEventSize;
|
|
5791
5808
|
//# sourceMappingURL=index.js.map
|