humanbehavior-js 0.3.2 → 0.3.3
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 +106 -112
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/index.js +8 -0
- package/dist/cjs/react/index.js.map +1 -1
- package/dist/esm/index.js +106 -112
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/index.js +8 -0
- 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 +28 -11
- package/package.json +1 -1
- package/src/react/index.tsx +11 -0
- package/src/redact.ts +58 -13
- package/src/tracker.ts +70 -121
package/dist/types/index.d.ts
CHANGED
|
@@ -9,7 +9,8 @@ declare class RedactionManager {
|
|
|
9
9
|
private excludeSelectors;
|
|
10
10
|
constructor(options?: RedactionOptions);
|
|
11
11
|
/**
|
|
12
|
-
* Set specific fields to be redacted
|
|
12
|
+
* Set specific fields to be redacted using CSS selectors
|
|
13
|
+
* These selectors are used to configure rrweb's built-in masking
|
|
13
14
|
* @param fields Array of CSS selectors for fields to redact
|
|
14
15
|
*/
|
|
15
16
|
setFieldsToRedact(fields: string[]): void;
|
|
@@ -23,6 +24,8 @@ declare class RedactionManager {
|
|
|
23
24
|
getSelectedFields(): string[];
|
|
24
25
|
/**
|
|
25
26
|
* Process an event and redact sensitive data if needed
|
|
27
|
+
* NOTE: This method is no longer used - events are handled directly by rrweb
|
|
28
|
+
* Kept for backward compatibility but not called in the current implementation
|
|
26
29
|
*/
|
|
27
30
|
processEvent(event: any): any;
|
|
28
31
|
/**
|
|
@@ -66,9 +69,20 @@ declare class RedactionManager {
|
|
|
66
69
|
*/
|
|
67
70
|
private isFieldSelected;
|
|
68
71
|
/**
|
|
69
|
-
*
|
|
72
|
+
* Get CSS selectors for rrweb masking configuration
|
|
73
|
+
* Used to configure rrweb's maskTextSelector option
|
|
70
74
|
*/
|
|
71
|
-
|
|
75
|
+
getMaskTextSelector(): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Check if an element should be redacted (for rrweb maskTextFn/maskInputFn)
|
|
78
|
+
*/
|
|
79
|
+
shouldRedactElement(element: HTMLElement): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Apply rrweb masking classes to DOM elements
|
|
82
|
+
* Adds 'rr-mask' class to elements that should be redacted
|
|
83
|
+
* This enables rrweb's built-in masking functionality
|
|
84
|
+
*/
|
|
85
|
+
applyRedactionClasses(): void;
|
|
72
86
|
/**
|
|
73
87
|
* Get the original value of a redacted element (for debugging)
|
|
74
88
|
*/
|
|
@@ -88,7 +102,6 @@ declare global {
|
|
|
88
102
|
}
|
|
89
103
|
declare class HumanBehaviorTracker {
|
|
90
104
|
private eventIngestionQueue;
|
|
91
|
-
private queueSizeBytes;
|
|
92
105
|
private sessionId;
|
|
93
106
|
private userProperties;
|
|
94
107
|
private isProcessing;
|
|
@@ -110,7 +123,6 @@ declare class HumanBehaviorTracker {
|
|
|
110
123
|
private navigationListeners;
|
|
111
124
|
private _connectionBlocked;
|
|
112
125
|
private recordInstance;
|
|
113
|
-
private frequencyUpdateInterval;
|
|
114
126
|
private sessionStartTime;
|
|
115
127
|
/**
|
|
116
128
|
* Initialize the HumanBehavior tracker
|
|
@@ -140,13 +152,7 @@ declare class HumanBehaviorTracker {
|
|
|
140
152
|
* Track navigation events and send custom events
|
|
141
153
|
*/
|
|
142
154
|
trackNavigationEvent(type: string, fromUrl: string, toUrl: string): Promise<void>;
|
|
143
|
-
/**
|
|
144
|
-
* Track a page view event (PostHog-style)
|
|
145
|
-
*/
|
|
146
155
|
trackPageView(url?: string): Promise<void>;
|
|
147
|
-
/**
|
|
148
|
-
* Track a custom event (PostHog-style)
|
|
149
|
-
*/
|
|
150
156
|
customEvent(eventName: string, properties?: Record<string, any>): Promise<void>;
|
|
151
157
|
/**
|
|
152
158
|
* Setup automatic tracking for buttons, links, and forms
|
|
@@ -203,7 +209,15 @@ declare class HumanBehaviorTracker {
|
|
|
203
209
|
getUserAttributes(): Record<string, any>;
|
|
204
210
|
start(): Promise<void>;
|
|
205
211
|
stop(): Promise<void>;
|
|
212
|
+
/**
|
|
213
|
+
* Add an event to the ingestion queue
|
|
214
|
+
* Events are sent directly without processing to avoid corruption
|
|
215
|
+
*/
|
|
206
216
|
addEvent(event: any): Promise<void>;
|
|
217
|
+
/**
|
|
218
|
+
* Flush events to the ingestion server
|
|
219
|
+
* Events are sent in chunks to handle large payloads efficiently
|
|
220
|
+
*/
|
|
207
221
|
private flush;
|
|
208
222
|
private setCookie;
|
|
209
223
|
getCookie(name: string): string | null;
|
|
@@ -225,9 +239,11 @@ declare class HumanBehaviorTracker {
|
|
|
225
239
|
redact(options?: RedactionOptions): Promise<void>;
|
|
226
240
|
/**
|
|
227
241
|
* Set specific fields to be redacted during session recording
|
|
242
|
+
* Uses rrweb's built-in masking instead of custom redaction processing
|
|
228
243
|
* @param fields Array of CSS selectors for fields to redact (e.g., ['input[type="password"]', '#email-field'])
|
|
229
244
|
*/
|
|
230
245
|
setRedactedFields(fields: string[]): void;
|
|
246
|
+
private restartWithNewRedaction;
|
|
231
247
|
/**
|
|
232
248
|
* Check if redaction is currently active
|
|
233
249
|
*/
|
|
@@ -246,6 +262,7 @@ declare class HumanBehaviorTracker {
|
|
|
246
262
|
getCurrentUrl(): string;
|
|
247
263
|
/**
|
|
248
264
|
* Get current snapshot frequency info
|
|
265
|
+
* Uses rrweb's sensible defaults (5 seconds, 100 events)
|
|
249
266
|
*/
|
|
250
267
|
getSnapshotFrequencyInfo(): {
|
|
251
268
|
sessionDuration: number;
|
package/package.json
CHANGED
package/src/react/index.tsx
CHANGED
|
@@ -111,6 +111,17 @@ export const HumanBehaviorProvider = ({ apiKey, client, children, options }: Hum
|
|
|
111
111
|
apiKeyRef.current.trim(),
|
|
112
112
|
'https://ingest.humanbehavior.co'
|
|
113
113
|
);
|
|
114
|
+
|
|
115
|
+
// ✅ APPLY LOGGING CONFIGURATION FROM OPTIONS
|
|
116
|
+
if (options?.logLevel) {
|
|
117
|
+
HumanBehaviorTracker.configureLogging({ level: options.logLevel });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ✅ APPLY REDACTION FIELDS FROM OPTIONS
|
|
121
|
+
if (options?.redactFields && options.redactFields.length > 0) {
|
|
122
|
+
tracker.setRedactedFields(options.redactFields);
|
|
123
|
+
}
|
|
124
|
+
|
|
114
125
|
setHumanBehavior(tracker);
|
|
115
126
|
|
|
116
127
|
// Wait for initialization to complete
|
package/src/redact.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Redaction functionality for sensitive input fields
|
|
2
|
-
// This module provides methods to
|
|
2
|
+
// This module provides methods to configure rrweb's built-in masking
|
|
3
|
+
// Uses CSS selectors and classes for reliable redaction without event corruption
|
|
3
4
|
|
|
4
5
|
import { logDebug, logWarn } from './utils/logger';
|
|
5
6
|
|
|
@@ -33,7 +34,8 @@ export class RedactionManager {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
|
-
* Set specific fields to be redacted
|
|
37
|
+
* Set specific fields to be redacted using CSS selectors
|
|
38
|
+
* These selectors are used to configure rrweb's built-in masking
|
|
37
39
|
* @param fields Array of CSS selectors for fields to redact
|
|
38
40
|
*/
|
|
39
41
|
public setFieldsToRedact(fields: string[]): void {
|
|
@@ -72,6 +74,8 @@ export class RedactionManager {
|
|
|
72
74
|
|
|
73
75
|
/**
|
|
74
76
|
* Process an event and redact sensitive data if needed
|
|
77
|
+
* NOTE: This method is no longer used - events are handled directly by rrweb
|
|
78
|
+
* Kept for backward compatibility but not called in the current implementation
|
|
75
79
|
*/
|
|
76
80
|
public processEvent(event: any): any {
|
|
77
81
|
// Only process if we have fields selected for redaction
|
|
@@ -431,26 +435,67 @@ export class RedactionManager {
|
|
|
431
435
|
}
|
|
432
436
|
|
|
433
437
|
/**
|
|
434
|
-
*
|
|
438
|
+
* Get CSS selectors for rrweb masking configuration
|
|
439
|
+
* Used to configure rrweb's maskTextSelector option
|
|
435
440
|
*/
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
if (element.matches(excludeSelector) || element.closest(excludeSelector)) {
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
441
|
+
public getMaskTextSelector(): string | null {
|
|
442
|
+
if (this.userSelectedFields.size === 0) {
|
|
443
|
+
return null;
|
|
442
444
|
}
|
|
445
|
+
return Array.from(this.userSelectedFields).join(',');
|
|
446
|
+
}
|
|
443
447
|
|
|
444
|
-
|
|
448
|
+
/**
|
|
449
|
+
* Check if an element should be redacted (for rrweb maskTextFn/maskInputFn)
|
|
450
|
+
*/
|
|
451
|
+
public shouldRedactElement(element: HTMLElement): boolean {
|
|
452
|
+
if (this.userSelectedFields.size === 0) {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Check if any selector matches this element
|
|
445
457
|
for (const selector of this.userSelectedFields) {
|
|
446
|
-
|
|
447
|
-
|
|
458
|
+
try {
|
|
459
|
+
if (element.matches(selector)) {
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
} catch (e) {
|
|
463
|
+
// Invalid selector, skip
|
|
464
|
+
logWarn(`Invalid selector: ${selector}`);
|
|
448
465
|
}
|
|
449
466
|
}
|
|
450
|
-
|
|
451
467
|
return false;
|
|
452
468
|
}
|
|
453
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Apply rrweb masking classes to DOM elements
|
|
472
|
+
* Adds 'rr-mask' class to elements that should be redacted
|
|
473
|
+
* This enables rrweb's built-in masking functionality
|
|
474
|
+
*/
|
|
475
|
+
public applyRedactionClasses(): void {
|
|
476
|
+
if (this.userSelectedFields.size === 0) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Remove existing redaction classes
|
|
481
|
+
document.querySelectorAll('.rr-mask').forEach(element => {
|
|
482
|
+
element.classList.remove('rr-mask');
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Add redaction classes to matching elements
|
|
486
|
+
this.userSelectedFields.forEach(selector => {
|
|
487
|
+
try {
|
|
488
|
+
const elements = document.querySelectorAll(selector);
|
|
489
|
+
elements.forEach(element => {
|
|
490
|
+
element.classList.add('rr-mask');
|
|
491
|
+
});
|
|
492
|
+
logDebug(`Applied rr-mask class to ${elements.length} element(s) for selector: ${selector}`);
|
|
493
|
+
} catch (e) {
|
|
494
|
+
logWarn(`Invalid selector: ${selector}`);
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
454
499
|
/**
|
|
455
500
|
* Get the original value of a redacted element (for debugging)
|
|
456
501
|
*/
|
package/src/tracker.ts
CHANGED
|
@@ -17,8 +17,6 @@ declare global {
|
|
|
17
17
|
|
|
18
18
|
export class HumanBehaviorTracker {
|
|
19
19
|
private eventIngestionQueue: any[] = [];
|
|
20
|
-
private queueSizeBytes: number = 0;
|
|
21
|
-
|
|
22
20
|
private sessionId!: string;
|
|
23
21
|
private userProperties: Record<string, any> = {};
|
|
24
22
|
private isProcessing: boolean = false;
|
|
@@ -48,7 +46,6 @@ export class HumanBehaviorTracker {
|
|
|
48
46
|
private navigationListeners: Array<() => void> = [];
|
|
49
47
|
private _connectionBlocked: boolean = false;
|
|
50
48
|
private recordInstance: any = null;
|
|
51
|
-
private frequencyUpdateInterval: any = null;
|
|
52
49
|
private sessionStartTime: number = Date.now();
|
|
53
50
|
|
|
54
51
|
/**
|
|
@@ -85,6 +82,8 @@ export class HumanBehaviorTracker {
|
|
|
85
82
|
// Set redacted fields if specified
|
|
86
83
|
if (options?.redactFields) {
|
|
87
84
|
tracker.setRedactedFields(options.redactFields);
|
|
85
|
+
// ✅ Apply redaction classes to existing elements
|
|
86
|
+
tracker.redactionManager.applyRedactionClasses();
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
// Setup automatic tracking if enabled
|
|
@@ -304,9 +303,6 @@ export class HumanBehaviorTracker {
|
|
|
304
303
|
}
|
|
305
304
|
}
|
|
306
305
|
|
|
307
|
-
/**
|
|
308
|
-
* Track a page view event (PostHog-style)
|
|
309
|
-
*/
|
|
310
306
|
public async trackPageView(url?: string): Promise<void> {
|
|
311
307
|
if (!this.initialized) return;
|
|
312
308
|
|
|
@@ -338,9 +334,6 @@ export class HumanBehaviorTracker {
|
|
|
338
334
|
}
|
|
339
335
|
}
|
|
340
336
|
|
|
341
|
-
/**
|
|
342
|
-
* Track a custom event (PostHog-style)
|
|
343
|
-
*/
|
|
344
337
|
public async customEvent(eventName: string, properties?: Record<string, any>): Promise<void> {
|
|
345
338
|
if (!this.initialized) return;
|
|
346
339
|
|
|
@@ -739,7 +732,6 @@ export class HumanBehaviorTracker {
|
|
|
739
732
|
await this.api.sendUserData(originalEndUserId!, userProperties, this.sessionId);
|
|
740
733
|
|
|
741
734
|
// Don't update endUserId - keep it as the original UUID
|
|
742
|
-
// The posthogName will be updated on the server side with the email
|
|
743
735
|
|
|
744
736
|
return originalEndUserId || '';
|
|
745
737
|
}
|
|
@@ -762,70 +754,31 @@ export class HumanBehaviorTracker {
|
|
|
762
754
|
// Enable console tracking
|
|
763
755
|
this.enableConsoleTracking();
|
|
764
756
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
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({
|
|
760
|
+
emit: (event) => {
|
|
761
|
+
// ✅ DIRECT EVENT HANDLING - Let rrweb handle events natively
|
|
762
|
+
this.addEvent(event);
|
|
763
|
+
},
|
|
764
|
+
inlineStylesheet: true,
|
|
765
|
+
recordCanvas: true,
|
|
766
|
+
collectFonts: true,
|
|
767
|
+
inlineImages: true,
|
|
768
|
+
blockClass: 'rr-block',
|
|
769
|
+
ignoreClass: 'rr-ignore',
|
|
770
|
+
maskTextClass: 'rr-mask',
|
|
771
|
+
|
|
772
|
+
// ✅ RRWEB BUILT-IN MASKING - More reliable than custom redaction
|
|
773
|
+
maskAllInputs: false, // Let users control this via selectors
|
|
774
|
+
maskTextSelector: this.redactionManager.getMaskTextSelector() || undefined,
|
|
775
|
+
|
|
776
|
+
// ✅ SELECTOR-BASED REDACTION - Users control via CSS selectors
|
|
777
|
+
// No custom masking functions needed - rrweb handles this natively
|
|
778
|
+
});
|
|
776
779
|
|
|
777
|
-
|
|
778
|
-
|
|
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;
|
|
780
|
+
// Store the record instance for cleanup
|
|
781
|
+
this.recordInstance = recordInstance;
|
|
829
782
|
}
|
|
830
783
|
|
|
831
784
|
public async stop() {
|
|
@@ -837,12 +790,6 @@ export class HumanBehaviorTracker {
|
|
|
837
790
|
this.flushInterval = null;
|
|
838
791
|
}
|
|
839
792
|
|
|
840
|
-
// Cleanup adaptive snapshot intervals
|
|
841
|
-
if (this.frequencyUpdateInterval) {
|
|
842
|
-
clearInterval(this.frequencyUpdateInterval);
|
|
843
|
-
this.frequencyUpdateInterval = null;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
793
|
// Stop rrweb recording
|
|
847
794
|
if (this.recordInstance) {
|
|
848
795
|
this.recordInstance();
|
|
@@ -856,30 +803,30 @@ export class HumanBehaviorTracker {
|
|
|
856
803
|
this.cleanupNavigationTracking();
|
|
857
804
|
}
|
|
858
805
|
|
|
806
|
+
/**
|
|
807
|
+
* Add an event to the ingestion queue
|
|
808
|
+
* Events are sent directly without processing to avoid corruption
|
|
809
|
+
*/
|
|
859
810
|
public async addEvent(event: any) {
|
|
860
811
|
await this.ensureInitialized();
|
|
861
812
|
|
|
862
|
-
//
|
|
863
|
-
|
|
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
|
-
}
|
|
813
|
+
// ✅ DIRECT EVENT HANDLING - No custom processing to avoid corruption
|
|
814
|
+
// Events flow directly from rrweb to ingestion server
|
|
874
815
|
|
|
875
|
-
//
|
|
876
|
-
|
|
816
|
+
// ✅ LOG FULLSNAPSHOT STATUS FOR DEBUGGING
|
|
817
|
+
if (event.type === 2) { // FullSnapshot
|
|
818
|
+
const hasData = !!event.data;
|
|
819
|
+
const hasNode = !!(event.data && event.data.node);
|
|
820
|
+
logDebug(`[FIXED] FullSnapshot event: hasData=${hasData}, hasNode=${hasNode}, dataType=${event.data?.node?.type}`);
|
|
821
|
+
}
|
|
877
822
|
|
|
878
|
-
|
|
879
|
-
this.eventIngestionQueue.push(processedEvent);
|
|
880
|
-
this.queueSizeBytes += eventSize;
|
|
823
|
+
this.eventIngestionQueue.push(event); // Direct event handling
|
|
881
824
|
}
|
|
882
825
|
|
|
826
|
+
/**
|
|
827
|
+
* Flush events to the ingestion server
|
|
828
|
+
* Events are sent in chunks to handle large payloads efficiently
|
|
829
|
+
*/
|
|
883
830
|
private async flush() {
|
|
884
831
|
// Prevent concurrent flushes
|
|
885
832
|
if (this.isProcessing || !this.initialized) {
|
|
@@ -891,10 +838,16 @@ export class HumanBehaviorTracker {
|
|
|
891
838
|
// Swap the current queue with an empty one atomically
|
|
892
839
|
const eventsToProcess = this.eventIngestionQueue;
|
|
893
840
|
this.eventIngestionQueue = [];
|
|
894
|
-
this.queueSizeBytes = 0;
|
|
895
841
|
|
|
896
842
|
if (eventsToProcess.length > 0) {
|
|
897
843
|
logDebug('Flushing events:', eventsToProcess);
|
|
844
|
+
|
|
845
|
+
// ✅ LOG FULLSNAPSHOT STATUS FOR MONITORING
|
|
846
|
+
const fullSnapshots = eventsToProcess.filter(e => e.type === 2);
|
|
847
|
+
if (fullSnapshots.length > 0) {
|
|
848
|
+
logDebug(`[FIXED] Sending ${fullSnapshots.length} FullSnapshot(s) with valid data`);
|
|
849
|
+
}
|
|
850
|
+
|
|
898
851
|
try {
|
|
899
852
|
// Use chunked sending to handle large payloads
|
|
900
853
|
await this.api.sendEventsChunked(eventsToProcess, this.sessionId, this.endUserId!);
|
|
@@ -1058,15 +1011,26 @@ export class HumanBehaviorTracker {
|
|
|
1058
1011
|
|
|
1059
1012
|
/**
|
|
1060
1013
|
* Set specific fields to be redacted during session recording
|
|
1014
|
+
* Uses rrweb's built-in masking instead of custom redaction processing
|
|
1061
1015
|
* @param fields Array of CSS selectors for fields to redact (e.g., ['input[type="password"]', '#email-field'])
|
|
1062
1016
|
*/
|
|
1063
1017
|
public setRedactedFields(fields: string[]): void {
|
|
1064
|
-
if (!isBrowser) {
|
|
1065
|
-
logWarn('Redaction is only available in browser environments');
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
1018
|
this.redactionManager.setFieldsToRedact(fields);
|
|
1019
|
+
|
|
1020
|
+
// ✅ APPLY RRWEB MASKING CLASSES - More reliable than custom processing
|
|
1021
|
+
this.redactionManager.applyRedactionClasses();
|
|
1022
|
+
|
|
1023
|
+
// ✅ RESTART RECORDING WITH NEW SETTINGS - Ensures masking is applied
|
|
1024
|
+
if (this.recordInstance) {
|
|
1025
|
+
this.restartWithNewRedaction();
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
private restartWithNewRedaction(): void {
|
|
1030
|
+
if (this.recordInstance) {
|
|
1031
|
+
this.recordInstance(); // Stop current recording
|
|
1032
|
+
this.start(); // Restart with new redaction settings
|
|
1033
|
+
}
|
|
1070
1034
|
}
|
|
1071
1035
|
|
|
1072
1036
|
/**
|
|
@@ -1099,6 +1063,7 @@ export class HumanBehaviorTracker {
|
|
|
1099
1063
|
|
|
1100
1064
|
/**
|
|
1101
1065
|
* Get current snapshot frequency info
|
|
1066
|
+
* Uses rrweb's sensible defaults (5 seconds, 100 events)
|
|
1102
1067
|
*/
|
|
1103
1068
|
public getSnapshotFrequencyInfo(): {
|
|
1104
1069
|
sessionDuration: number;
|
|
@@ -1107,28 +1072,12 @@ export class HumanBehaviorTracker {
|
|
|
1107
1072
|
phase: string;
|
|
1108
1073
|
} {
|
|
1109
1074
|
const sessionDuration = Date.now() - this.sessionStartTime;
|
|
1110
|
-
const thirtyMinutes = 30 * 60 * 1000;
|
|
1111
|
-
const twoHours = 2 * 60 * 60 * 1000;
|
|
1112
|
-
|
|
1113
|
-
let phase = 'initial';
|
|
1114
|
-
let interval = 5000;
|
|
1115
|
-
let threshold = 100;
|
|
1116
|
-
|
|
1117
|
-
if (sessionDuration > twoHours) {
|
|
1118
|
-
phase = 'extended';
|
|
1119
|
-
interval = 30000;
|
|
1120
|
-
threshold = 500;
|
|
1121
|
-
} else if (sessionDuration > thirtyMinutes) {
|
|
1122
|
-
phase = 'moderate';
|
|
1123
|
-
interval = 15000;
|
|
1124
|
-
threshold = 300;
|
|
1125
|
-
}
|
|
1126
1075
|
|
|
1127
1076
|
return {
|
|
1128
1077
|
sessionDuration,
|
|
1129
|
-
currentInterval:
|
|
1130
|
-
currentThreshold:
|
|
1131
|
-
phase
|
|
1078
|
+
currentInterval: 5000, // rrweb default - 5 seconds
|
|
1079
|
+
currentThreshold: 100, // rrweb default - 100 events
|
|
1080
|
+
phase: 'default' // Using rrweb's proven defaults
|
|
1132
1081
|
};
|
|
1133
1082
|
}
|
|
1134
1083
|
|