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 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 * 10; // 10MB chunk size
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
- throw new Error(`Single event size (${singleEventSize} bytes) exceeds maximum chunk size (${MAX_CHUNK_SIZE_BYTES} bytes)`);
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
- // Validate single event size
4285
- validateSingleEventSize(event, sessionId);
4286
- // Start new chunk with this event
4287
- currentChunk = [event];
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
- // Fallback: add to event stream if direct API call fails
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
- yield this.api.sendEvents(eventsToProcess, this.sessionId, this.endUserId);
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
- // If we get a 400 error, store events for retry
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
- logInfo('Session expired, storing events for retry');
5561
- this.rejectedEvents.push(...eventsToProcess);
5562
- this.processRejectedEvents();
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 (((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('ERR_BLOCKED_BY_CLIENT')) ||
5565
- ((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('Failed to fetch')) ||
5566
- ((_d = error.message) === null || _d === void 0 ? void 0 : _d.includes('NetworkError'))) {
5567
- // Handle ad blocker or network issues gracefully
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 rejected events (might indicate blocking)
5722
- if (this.rejectedEvents.length > 0) {
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