humanbehavior-js 0.3.6 → 0.3.7

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.
@@ -261,7 +261,7 @@ declare class HumanBehaviorTracker {
261
261
  getCurrentUrl(): string;
262
262
  /**
263
263
  * Get current snapshot frequency info
264
- * Uses configured values (5 minutes, 1000 events) - PostHog-style
264
+ * Uses configured values (5 minutes, 1000 events)
265
265
  */
266
266
  getSnapshotFrequencyInfo(): {
267
267
  sessionDuration: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humanbehavior-js",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "SDK for HumanBehavior session and event recording",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -30,8 +30,7 @@
30
30
  "license": "ISC",
31
31
  "dependencies": {
32
32
  "@types/react": "^19.0.12",
33
- "humanbehavior-js": "^0.0.9",
34
- "rrweb": "^2.0.0-alpha.4",
33
+ "@rrweb/record": "^2.0.0-alpha.17",
35
34
  "uuid": "^11.1.0"
36
35
  },
37
36
  "peerDependencies": {
package/readme.md CHANGED
@@ -119,7 +119,7 @@ const status = tracker.getConnectionStatus();
119
119
 
120
120
  The SDK automatically handles session continuity:
121
121
 
122
- 1. **Session Restoration**: Automatically restores sessions within 30 minutes
122
+ 1. **Session Restoration**: Automatically restores sessions within 15 minutes
123
123
  2. **Activity Tracking**: Updates activity timestamp on user interactions
124
124
  3. **Cross-Page Persistence**: Session persists across page navigations
125
125
 
@@ -144,7 +144,7 @@ If you see `net::ERR_BLOCKED_BY_CLIENT` errors:
144
144
 
145
145
  ### Session Issues
146
146
 
147
- - Sessions automatically expire after 30 minutes of inactivity
147
+ - Sessions automatically expire after 15 minutes of inactivity
148
148
  - Each page load calls `/init` but reuses existing sessions when possible
149
149
  - Check browser console for session restoration logs
150
150
 
package/src/api.ts CHANGED
@@ -23,7 +23,16 @@ export function validateSingleEventSize(event: any, sessionId: string): void {
23
23
  }
24
24
  }
25
25
 
26
+
27
+
28
+
29
+
26
30
  export function splitLargeEvent(event: any, sessionId: string): any[] {
31
+ // ✅ SIMPLE VALIDATION
32
+ if (!event || typeof event !== 'object') {
33
+ return [];
34
+ }
35
+
27
36
  const eventSize = new TextEncoder().encode(JSON.stringify({
28
37
  sessionId,
29
38
  events: [event]
@@ -132,6 +141,9 @@ export class HumanBehaviorAPI {
132
141
  }
133
142
 
134
143
  async sendEvents(events: any[], sessionId: string, userId: string) {
144
+ // ✅ SIMPLE VALIDATION FOR ALL EVENTS
145
+ const validEvents = events.filter(event => event && typeof event === 'object');
146
+
135
147
  const response = await fetch(`${this.baseUrl}/api/ingestion/events`, {
136
148
  method: 'POST',
137
149
  headers: {
@@ -140,7 +152,7 @@ export class HumanBehaviorAPI {
140
152
  },
141
153
  body: JSON.stringify({
142
154
  sessionId,
143
- events: events,
155
+ events: validEvents,
144
156
  endUserId: userId
145
157
  })
146
158
  });
@@ -156,6 +168,11 @@ export class HumanBehaviorAPI {
156
168
  let currentChunk: any[] = [];
157
169
 
158
170
  for (const event of events) {
171
+ // ✅ SIMPLE VALIDATION FOR ALL EVENTS
172
+ if (!event || typeof event !== 'object') {
173
+ continue;
174
+ }
175
+
159
176
  if (isChunkSizeExceeded(currentChunk, event, sessionId)) {
160
177
  // If current chunk is not empty, send it first
161
178
  if (currentChunk.length > 0) {
@@ -226,7 +243,7 @@ export class HumanBehaviorAPI {
226
243
  userId: userId,
227
244
  userAttributes: userData,
228
245
  sessionId: sessionId,
229
- posthogName: userData.email || userData.name || null // Update posthogName with email
246
+ posthogName: userData.email || userData.name || null // Update user name with email
230
247
  };
231
248
 
232
249
  console.log('Sending user data to server:', payload);
package/src/tracker.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as rrweb from 'rrweb';
1
+ import { record } from '@rrweb/record';
2
2
  import { v1 as uuidv1 } from 'uuid';
3
3
  import { HumanBehaviorAPI } from './api';
4
4
  import { RedactionManager, RedactionOptions } from './redact';
@@ -128,10 +128,10 @@ export class HumanBehaviorTracker {
128
128
  if (isBrowser) {
129
129
  const existingSessionId = localStorage.getItem('human_behavior_session_id');
130
130
  const lastActivity = localStorage.getItem('human_behavior_last_activity');
131
- const thirtyMinutesAgo = Date.now() - (30 * 60 * 1000);
131
+ const fifteenMinutesAgo = Date.now() - (15 * 60 * 1000);
132
132
 
133
133
  // Check if we have an existing session that's still within the activity window
134
- if (existingSessionId && lastActivity && parseInt(lastActivity) > thirtyMinutesAgo) {
134
+ if (existingSessionId && lastActivity && parseInt(lastActivity) > fifteenMinutesAgo) {
135
135
  this.sessionId = existingSessionId;
136
136
  logDebug(`Reusing existing session: ${this.sessionId}`);
137
137
  // Update activity timestamp to extend the session window
@@ -754,9 +754,13 @@ export class HumanBehaviorTracker {
754
754
  // Enable console tracking
755
755
  this.enableConsoleTracking();
756
756
 
757
- // ✅ SIMPLIFIED RECORDING - Use rrweb's proven defaults
758
- // No complex adaptive logic - rrweb's defaults work well for most use cases
759
- const recordInstance = rrweb.record({
757
+ // ✅ DOM READY DETECTION
758
+ // Wait for DOM to be ready before starting recording
759
+ const startRecording = () => {
760
+ logDebug('🎯 DOM ready, starting session recording');
761
+
762
+ // ✅ HUMANBEHAVIOR RRWEB CONFIGURATION
763
+ const recordInstance = record({
760
764
  emit: (event) => {
761
765
  // ✅ DIRECT EVENT HANDLING - Let rrweb handle events natively
762
766
  this.addEvent(event);
@@ -766,28 +770,43 @@ export class HumanBehaviorTracker {
766
770
  logDebug(`🎯 FullSnapshot generated at ${new Date().toISOString()}`);
767
771
  }
768
772
  },
769
- inlineStylesheet: true,
770
- recordCanvas: true,
771
- collectFonts: true,
772
- inlineImages: true,
773
- blockClass: 'rr-block',
774
- ignoreClass: 'rr-ignore',
775
- maskTextClass: 'rr-mask',
776
-
777
- // ✅ RRWEB BUILT-IN MASKING - More reliable than custom redaction
778
- maskAllInputs: false, // Let users control this via selectors
773
+ // ✅ HUMANBEHAVIOR'S CUSTOM SETTINGS
779
774
  maskTextSelector: this.redactionManager.getMaskTextSelector() || undefined,
775
+ maskTextFn: undefined,
776
+ maskAllInputs: true, // HumanBehavior default
777
+ maskInputOptions: { password: true }, // HumanBehavior default
778
+ maskInputFn: undefined,
779
+ slimDOMOptions: {},
780
+ collectFonts: false, // HumanBehavior default
781
+ inlineStylesheet: true, // HumanBehavior default
782
+ recordCrossOriginIframes: false, // HumanBehavior default
780
783
 
781
- // ✅ FULLSNAPSHOT GENERATION - Use reasonable intervals (PostHog-style)
782
- checkoutEveryNms: 300000, // Take FullSnapshot every 5 minutes (like PostHog)
783
- checkoutEveryNth: 1000, // Take FullSnapshot every 1000 events
784
+ // ✅ CANVAS RECORDING - Disabled to prevent large data URIs
785
+ recordCanvas: false, // Disabled to prevent large data URIs
784
786
 
785
- // ✅ SELECTOR-BASED REDACTION - Users control via CSS selectors
786
- // No custom masking functions needed - rrweb handles this natively
787
+ // ✅ FULLSNAPSHOT GENERATION - Use reasonable intervals
788
+ checkoutEveryNms: 300000, // Take FullSnapshot every 5 minutes
789
+ checkoutEveryNth: 1000, // Take FullSnapshot every 1000 events
787
790
  });
788
791
 
789
792
  // Store the record instance for cleanup
790
793
  this.recordInstance = recordInstance;
794
+ };
795
+
796
+ // ✅ DOM READY DETECTION
797
+ logDebug(`🎯 DOM ready state: ${document.readyState}`);
798
+ if (document.readyState === 'complete') {
799
+ // DOM already ready, start immediately
800
+ logDebug('🎯 DOM already complete, starting recording immediately');
801
+ startRecording();
802
+ } else {
803
+ // Wait for DOM to be ready
804
+ logDebug('🎯 DOM not ready, waiting for DOMContentLoaded event');
805
+ document.addEventListener('DOMContentLoaded', () => {
806
+ logDebug('🎯 DOMContentLoaded fired, starting recording');
807
+ startRecording();
808
+ }, { once: true });
809
+ }
791
810
  }
792
811
 
793
812
  public async stop() {
@@ -822,11 +841,22 @@ export class HumanBehaviorTracker {
822
841
  // ✅ DIRECT EVENT HANDLING - No custom processing to avoid corruption
823
842
  // Events flow directly from rrweb to ingestion server
824
843
 
844
+ // ✅ EVENT VALIDATION
845
+ if (!event || typeof event !== 'object') {
846
+ logDebug('⚠️ Skipping invalid event:', event);
847
+ return;
848
+ }
849
+
825
850
  // ✅ LOG FULLSNAPSHOT STATUS FOR DEBUGGING
826
851
  if (event.type === 2) { // FullSnapshot
827
852
  const hasData = !!event.data;
828
853
  const hasNode = !!(event.data && event.data.node);
829
- logDebug(`[FIXED] FullSnapshot event: hasData=${hasData}, hasNode=${hasNode}, dataType=${event.data?.node?.type}`);
854
+
855
+ if (!hasData || !hasNode) {
856
+ logDebug(`⚠️ Empty FullSnapshot detected: hasData=${hasData}, hasNode=${hasNode} - continuing session`);
857
+ } else {
858
+ logDebug(`✅ Valid FullSnapshot: hasData=${hasData}, hasNode=${hasNode}, dataType=${event.data?.node?.type}`);
859
+ }
830
860
  }
831
861
 
832
862
  this.eventIngestionQueue.push(event); // Direct event handling
@@ -1072,7 +1102,7 @@ export class HumanBehaviorTracker {
1072
1102
 
1073
1103
  /**
1074
1104
  * Get current snapshot frequency info
1075
- * Uses configured values (5 minutes, 1000 events) - PostHog-style
1105
+ * Uses configured values (5 minutes, 1000 events)
1076
1106
  */
1077
1107
  public getSnapshotFrequencyInfo(): {
1078
1108
  sessionDuration: number;
@@ -1084,7 +1114,7 @@ export class HumanBehaviorTracker {
1084
1114
 
1085
1115
  return {
1086
1116
  sessionDuration,
1087
- currentInterval: 300000, // Configured - 5 minutes (PostHog-style)
1117
+ currentInterval: 300000, // Configured - 5 minutes
1088
1118
  currentThreshold: 1000, // Configured - 1000 events
1089
1119
  phase: 'configured' // Using explicit configuration
1090
1120
  };