humanbehavior-js 0.3.2 → 0.3.4
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 +113 -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 +113 -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 +79 -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 configured values (5 minutes, 1000 events) - PostHog-style
|
|
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,40 @@ 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
|
-
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');
|
|
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
|
+
// ✅ DEBUG FULLSNAPSHOT GENERATION
|
|
765
|
+
if (event.type === 2) { // FullSnapshot
|
|
766
|
+
logDebug(`🎯 FullSnapshot generated at ${new Date().toISOString()}`);
|
|
810
767
|
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
768
|
+
},
|
|
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
|
|
779
|
+
maskTextSelector: this.redactionManager.getMaskTextSelector() || undefined,
|
|
780
|
+
|
|
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
|
+
|
|
785
|
+
// ✅ SELECTOR-BASED REDACTION - Users control via CSS selectors
|
|
786
|
+
// No custom masking functions needed - rrweb handles this natively
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
// Store the record instance for cleanup
|
|
790
|
+
this.recordInstance = recordInstance;
|
|
829
791
|
}
|
|
830
792
|
|
|
831
793
|
public async stop() {
|
|
@@ -837,12 +799,6 @@ export class HumanBehaviorTracker {
|
|
|
837
799
|
this.flushInterval = null;
|
|
838
800
|
}
|
|
839
801
|
|
|
840
|
-
// Cleanup adaptive snapshot intervals
|
|
841
|
-
if (this.frequencyUpdateInterval) {
|
|
842
|
-
clearInterval(this.frequencyUpdateInterval);
|
|
843
|
-
this.frequencyUpdateInterval = null;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
802
|
// Stop rrweb recording
|
|
847
803
|
if (this.recordInstance) {
|
|
848
804
|
this.recordInstance();
|
|
@@ -856,30 +812,30 @@ export class HumanBehaviorTracker {
|
|
|
856
812
|
this.cleanupNavigationTracking();
|
|
857
813
|
}
|
|
858
814
|
|
|
815
|
+
/**
|
|
816
|
+
* Add an event to the ingestion queue
|
|
817
|
+
* Events are sent directly without processing to avoid corruption
|
|
818
|
+
*/
|
|
859
819
|
public async addEvent(event: any) {
|
|
860
820
|
await this.ensureInitialized();
|
|
861
821
|
|
|
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
|
-
}
|
|
822
|
+
// ✅ DIRECT EVENT HANDLING - No custom processing to avoid corruption
|
|
823
|
+
// Events flow directly from rrweb to ingestion server
|
|
874
824
|
|
|
875
|
-
//
|
|
876
|
-
|
|
825
|
+
// ✅ LOG FULLSNAPSHOT STATUS FOR DEBUGGING
|
|
826
|
+
if (event.type === 2) { // FullSnapshot
|
|
827
|
+
const hasData = !!event.data;
|
|
828
|
+
const hasNode = !!(event.data && event.data.node);
|
|
829
|
+
logDebug(`[FIXED] FullSnapshot event: hasData=${hasData}, hasNode=${hasNode}, dataType=${event.data?.node?.type}`);
|
|
830
|
+
}
|
|
877
831
|
|
|
878
|
-
|
|
879
|
-
this.eventIngestionQueue.push(processedEvent);
|
|
880
|
-
this.queueSizeBytes += eventSize;
|
|
832
|
+
this.eventIngestionQueue.push(event); // Direct event handling
|
|
881
833
|
}
|
|
882
834
|
|
|
835
|
+
/**
|
|
836
|
+
* Flush events to the ingestion server
|
|
837
|
+
* Events are sent in chunks to handle large payloads efficiently
|
|
838
|
+
*/
|
|
883
839
|
private async flush() {
|
|
884
840
|
// Prevent concurrent flushes
|
|
885
841
|
if (this.isProcessing || !this.initialized) {
|
|
@@ -891,10 +847,16 @@ export class HumanBehaviorTracker {
|
|
|
891
847
|
// Swap the current queue with an empty one atomically
|
|
892
848
|
const eventsToProcess = this.eventIngestionQueue;
|
|
893
849
|
this.eventIngestionQueue = [];
|
|
894
|
-
this.queueSizeBytes = 0;
|
|
895
850
|
|
|
896
851
|
if (eventsToProcess.length > 0) {
|
|
897
852
|
logDebug('Flushing events:', eventsToProcess);
|
|
853
|
+
|
|
854
|
+
// ✅ LOG FULLSNAPSHOT STATUS FOR MONITORING
|
|
855
|
+
const fullSnapshots = eventsToProcess.filter(e => e.type === 2);
|
|
856
|
+
if (fullSnapshots.length > 0) {
|
|
857
|
+
logDebug(`[FIXED] Sending ${fullSnapshots.length} FullSnapshot(s) with valid data`);
|
|
858
|
+
}
|
|
859
|
+
|
|
898
860
|
try {
|
|
899
861
|
// Use chunked sending to handle large payloads
|
|
900
862
|
await this.api.sendEventsChunked(eventsToProcess, this.sessionId, this.endUserId!);
|
|
@@ -1058,15 +1020,26 @@ export class HumanBehaviorTracker {
|
|
|
1058
1020
|
|
|
1059
1021
|
/**
|
|
1060
1022
|
* Set specific fields to be redacted during session recording
|
|
1023
|
+
* Uses rrweb's built-in masking instead of custom redaction processing
|
|
1061
1024
|
* @param fields Array of CSS selectors for fields to redact (e.g., ['input[type="password"]', '#email-field'])
|
|
1062
1025
|
*/
|
|
1063
1026
|
public setRedactedFields(fields: string[]): void {
|
|
1064
|
-
if (!isBrowser) {
|
|
1065
|
-
logWarn('Redaction is only available in browser environments');
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
1027
|
this.redactionManager.setFieldsToRedact(fields);
|
|
1028
|
+
|
|
1029
|
+
// ✅ APPLY RRWEB MASKING CLASSES - More reliable than custom processing
|
|
1030
|
+
this.redactionManager.applyRedactionClasses();
|
|
1031
|
+
|
|
1032
|
+
// ✅ RESTART RECORDING WITH NEW SETTINGS - Ensures masking is applied
|
|
1033
|
+
if (this.recordInstance) {
|
|
1034
|
+
this.restartWithNewRedaction();
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
private restartWithNewRedaction(): void {
|
|
1039
|
+
if (this.recordInstance) {
|
|
1040
|
+
this.recordInstance(); // Stop current recording
|
|
1041
|
+
this.start(); // Restart with new redaction settings
|
|
1042
|
+
}
|
|
1070
1043
|
}
|
|
1071
1044
|
|
|
1072
1045
|
/**
|
|
@@ -1099,6 +1072,7 @@ export class HumanBehaviorTracker {
|
|
|
1099
1072
|
|
|
1100
1073
|
/**
|
|
1101
1074
|
* Get current snapshot frequency info
|
|
1075
|
+
* Uses configured values (5 minutes, 1000 events) - PostHog-style
|
|
1102
1076
|
*/
|
|
1103
1077
|
public getSnapshotFrequencyInfo(): {
|
|
1104
1078
|
sessionDuration: number;
|
|
@@ -1107,28 +1081,12 @@ export class HumanBehaviorTracker {
|
|
|
1107
1081
|
phase: string;
|
|
1108
1082
|
} {
|
|
1109
1083
|
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
1084
|
|
|
1127
1085
|
return {
|
|
1128
1086
|
sessionDuration,
|
|
1129
|
-
currentInterval:
|
|
1130
|
-
currentThreshold:
|
|
1131
|
-
phase
|
|
1087
|
+
currentInterval: 300000, // Configured - 5 minutes (PostHog-style)
|
|
1088
|
+
currentThreshold: 1000, // Configured - 1000 events
|
|
1089
|
+
phase: 'configured' // Using explicit configuration
|
|
1132
1090
|
};
|
|
1133
1091
|
}
|
|
1134
1092
|
|