humanbehavior-js 0.2.5 → 0.2.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.
@@ -109,6 +109,9 @@ declare class HumanBehaviorTracker {
109
109
  private originalReplaceState;
110
110
  private navigationListeners;
111
111
  private _connectionBlocked;
112
+ private recordInstance;
113
+ private frequencyUpdateInterval;
114
+ private sessionStartTime;
112
115
  /**
113
116
  * Initialize the HumanBehavior tracker
114
117
  * This is the main entry point - call this once per page
@@ -240,6 +243,15 @@ declare class HumanBehaviorTracker {
240
243
  * Get the current URL being tracked
241
244
  */
242
245
  getCurrentUrl(): string;
246
+ /**
247
+ * Get current snapshot frequency info
248
+ */
249
+ getSnapshotFrequencyInfo(): {
250
+ sessionDuration: number;
251
+ currentInterval: number;
252
+ currentThreshold: number;
253
+ phase: string;
254
+ };
243
255
  /**
244
256
  * Test if the tracker can reach the ingestion server
245
257
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humanbehavior-js",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "SDK for HumanBehavior session and event recording",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
package/src/tracker.ts CHANGED
@@ -47,6 +47,9 @@ export class HumanBehaviorTracker {
47
47
  private originalReplaceState: typeof history.replaceState | null = null;
48
48
  private navigationListeners: Array<() => void> = [];
49
49
  private _connectionBlocked: boolean = false;
50
+ private recordInstance: any = null;
51
+ private frequencyUpdateInterval: any = null;
52
+ private sessionStartTime: number = Date.now();
50
53
 
51
54
  /**
52
55
  * Initialize the HumanBehavior tracker
@@ -759,19 +762,70 @@ export class HumanBehaviorTracker {
759
762
  // Enable console tracking
760
763
  this.enableConsoleTracking();
761
764
 
762
- // Start recording with redaction enabled
763
- rrweb.record({
764
- emit: (event) => {
765
- this.addEvent(event);
766
- },
767
- inlineStylesheet: true,
768
- recordCanvas: true,
769
- collectFonts: true,
770
- inlineImages: true,
771
- blockClass: 'rr-block',
772
- ignoreClass: 'rr-ignore',
773
- maskTextClass: 'rr-ignore'
774
- });
765
+ // Adaptive snapshot configuration based on session duration
766
+ const sessionStartTime = Date.now();
767
+ let snapshotInterval = 5000; // Start with 5 seconds
768
+ let eventThreshold = 100; // Start with 100 events
769
+
770
+ // Function to update snapshot frequency based on session duration
771
+ const updateSnapshotFrequency = () => {
772
+ const sessionDuration = Date.now() - sessionStartTime;
773
+ const thirtyMinutes = 30 * 60 * 1000;
774
+ const twoHours = 2 * 60 * 60 * 1000;
775
+ const fourHours = 4 * 60 * 60 * 1000;
776
+
777
+ if (sessionDuration > twoHours) {
778
+ // After 2 hours, very infrequent snapshots
779
+ snapshotInterval = 30000; // 30 seconds
780
+ eventThreshold = 500; // 500 events
781
+ logDebug('Reduced snapshot frequency: 30s/500 events (2+ hours)');
782
+ } else if (sessionDuration > thirtyMinutes) {
783
+ // After 30 minutes, moderate frequency
784
+ snapshotInterval = 15000; // 15 seconds
785
+ eventThreshold = 300; // 300 events
786
+ logDebug('Reduced snapshot frequency: 15s/300 events (30+ minutes)');
787
+ }
788
+ // First 30 minutes: 5s/100 events (default)
789
+ };
790
+
791
+ // Update frequency every 5 minutes
792
+ const frequencyUpdateInterval = setInterval(updateSnapshotFrequency, 5 * 60 * 1000);
793
+
794
+ // Start recording with adaptive redaction enabled
795
+ const recordInstance = rrweb.record({
796
+ emit: (event) => {
797
+ // Add additional validation for FullSnapshot events
798
+ if (event.type === 2) { // FullSnapshot event
799
+ if (!event.data || !event.data.node) {
800
+ logWarn('rrweb generated malformed FullSnapshot event:', {
801
+ hasData: !!event.data,
802
+ hasNode: !!(event.data && event.data.node),
803
+ dataType: typeof event.data,
804
+ eventType: event.type,
805
+ timestamp: event.timestamp
806
+ });
807
+ // Don't skip - let the addEvent method handle it
808
+ } else {
809
+ logDebug('Valid FullSnapshot event received from rrweb');
810
+ }
811
+ }
812
+ this.addEvent(event);
813
+ },
814
+ inlineStylesheet: true,
815
+ recordCanvas: true,
816
+ collectFonts: true,
817
+ inlineImages: true,
818
+ blockClass: 'rr-block',
819
+ ignoreClass: 'rr-ignore',
820
+ maskTextClass: 'rr-ignore',
821
+ // Adaptive configuration
822
+ checkoutEveryNms: snapshotInterval,
823
+ checkoutEveryNth: eventThreshold
824
+ });
825
+
826
+ // Store the record instance and interval for cleanup
827
+ this.recordInstance = recordInstance;
828
+ this.frequencyUpdateInterval = frequencyUpdateInterval;
775
829
  }
776
830
 
777
831
  public async stop() {
@@ -783,6 +837,18 @@ export class HumanBehaviorTracker {
783
837
  this.flushInterval = null;
784
838
  }
785
839
 
840
+ // Cleanup adaptive snapshot intervals
841
+ if (this.frequencyUpdateInterval) {
842
+ clearInterval(this.frequencyUpdateInterval);
843
+ this.frequencyUpdateInterval = null;
844
+ }
845
+
846
+ // Stop rrweb recording
847
+ if (this.recordInstance) {
848
+ this.recordInstance();
849
+ this.recordInstance = null;
850
+ }
851
+
786
852
  // Disable console tracking
787
853
  this.disableConsoleTracking();
788
854
 
@@ -793,6 +859,19 @@ export class HumanBehaviorTracker {
793
859
  public async addEvent(event: any) {
794
860
  await this.ensureInitialized();
795
861
 
862
+ // Validate FullSnapshot events before processing
863
+ if (event.type === 2) { // FullSnapshot event
864
+ if (!event.data || !event.data.node) {
865
+ logWarn('Malformed FullSnapshot event detected, skipping:', {
866
+ hasData: !!event.data,
867
+ hasNode: !!(event.data && event.data.node),
868
+ dataType: typeof event.data,
869
+ eventType: event.type
870
+ });
871
+ return; // Skip malformed FullSnapshot events
872
+ }
873
+ }
874
+
796
875
  // Process event through redaction manager if active
797
876
  const processedEvent = this.redactionManager.processEvent(event);
798
877
 
@@ -1010,6 +1089,41 @@ export class HumanBehaviorTracker {
1010
1089
  return this.currentUrl;
1011
1090
  }
1012
1091
 
1092
+ /**
1093
+ * Get current snapshot frequency info
1094
+ */
1095
+ public getSnapshotFrequencyInfo(): {
1096
+ sessionDuration: number;
1097
+ currentInterval: number;
1098
+ currentThreshold: number;
1099
+ phase: string;
1100
+ } {
1101
+ const sessionDuration = Date.now() - this.sessionStartTime;
1102
+ const thirtyMinutes = 30 * 60 * 1000;
1103
+ const twoHours = 2 * 60 * 60 * 1000;
1104
+
1105
+ let phase = 'initial';
1106
+ let interval = 5000;
1107
+ let threshold = 100;
1108
+
1109
+ if (sessionDuration > twoHours) {
1110
+ phase = 'extended';
1111
+ interval = 30000;
1112
+ threshold = 500;
1113
+ } else if (sessionDuration > thirtyMinutes) {
1114
+ phase = 'moderate';
1115
+ interval = 15000;
1116
+ threshold = 300;
1117
+ }
1118
+
1119
+ return {
1120
+ sessionDuration,
1121
+ currentInterval: interval,
1122
+ currentThreshold: threshold,
1123
+ phase
1124
+ };
1125
+ }
1126
+
1013
1127
  /**
1014
1128
  * Test if the tracker can reach the ingestion server
1015
1129
  */