humanbehavior-js 0.2.6 → 0.2.8
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 +125 -8
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/index.js.map +1 -1
- package/dist/esm/index.js +125 -9
- package/dist/esm/index.js.map +1 -1
- 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 +36 -4
- package/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/react/index.tsx +5 -0
- package/src/server.ts +51 -0
- package/src/tracker.ts +124 -33
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
|
@@ -210,8 +213,9 @@ declare class HumanBehaviorTracker {
|
|
|
210
213
|
*/
|
|
211
214
|
private deleteCookie;
|
|
212
215
|
/**
|
|
213
|
-
*
|
|
214
|
-
* This
|
|
216
|
+
* Clear user data and reset session when user signs out of the site
|
|
217
|
+
* This should be called when a user logs out of your application to prevent
|
|
218
|
+
* data contamination between different users
|
|
215
219
|
*/
|
|
216
220
|
logout(): void;
|
|
217
221
|
/**
|
|
@@ -240,6 +244,15 @@ declare class HumanBehaviorTracker {
|
|
|
240
244
|
* Get the current URL being tracked
|
|
241
245
|
*/
|
|
242
246
|
getCurrentUrl(): string;
|
|
247
|
+
/**
|
|
248
|
+
* Get current snapshot frequency info
|
|
249
|
+
*/
|
|
250
|
+
getSnapshotFrequencyInfo(): {
|
|
251
|
+
sessionDuration: number;
|
|
252
|
+
currentInterval: number;
|
|
253
|
+
currentThreshold: number;
|
|
254
|
+
phase: string;
|
|
255
|
+
};
|
|
243
256
|
/**
|
|
244
257
|
* Test if the tracker can reach the ingestion server
|
|
245
258
|
*/
|
|
@@ -297,6 +310,25 @@ declare class HumanBehaviorAPI {
|
|
|
297
310
|
}>): Promise<any>;
|
|
298
311
|
}
|
|
299
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Server-side utilities for HumanBehavior SDK
|
|
315
|
+
*/
|
|
316
|
+
interface ServerSideUserData {
|
|
317
|
+
email: string;
|
|
318
|
+
name?: string;
|
|
319
|
+
image?: string;
|
|
320
|
+
provider?: string;
|
|
321
|
+
[key: string]: any;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Identify user from server-side (NextAuth, Firebase, etc.)
|
|
325
|
+
* Use this in your auth events to track users immediately on sign-in
|
|
326
|
+
*/
|
|
327
|
+
declare function identifyUser(userData: ServerSideUserData, apiKey: string): Promise<{
|
|
328
|
+
success: boolean;
|
|
329
|
+
error?: string;
|
|
330
|
+
}>;
|
|
331
|
+
|
|
300
332
|
declare enum LogLevel {
|
|
301
333
|
NONE = 0,
|
|
302
334
|
ERROR = 1,
|
|
@@ -330,5 +362,5 @@ declare const logWarn: (message: string, ...args: any[]) => void;
|
|
|
330
362
|
declare const logInfo: (message: string, ...args: any[]) => void;
|
|
331
363
|
declare const logDebug: (message: string, ...args: any[]) => void;
|
|
332
364
|
|
|
333
|
-
export { HumanBehaviorAPI, HumanBehaviorTracker, LogLevel, MAX_CHUNK_SIZE_BYTES, RedactionManager, HumanBehaviorTracker as default, isChunkSizeExceeded, logDebug, logError, logInfo, logWarn, logger, redactionManager, splitLargeEvent, validateSingleEventSize };
|
|
334
|
-
export type { LoggerConfig, RedactionOptions };
|
|
365
|
+
export { HumanBehaviorAPI, HumanBehaviorTracker, LogLevel, MAX_CHUNK_SIZE_BYTES, RedactionManager, HumanBehaviorTracker as default, identifyUser, isChunkSizeExceeded, logDebug, logError, logInfo, logWarn, logger, redactionManager, splitLargeEvent, validateSingleEventSize };
|
|
366
|
+
export type { LoggerConfig, RedactionOptions, ServerSideUserData };
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/react/index.tsx
CHANGED
|
@@ -11,6 +11,7 @@ interface HumanBehaviorInterface {
|
|
|
11
11
|
addUserInfo: ({ userId, userProperties }: { userId?: string, userProperties: Record<string, any> }) => Promise<string>;
|
|
12
12
|
start: () => void;
|
|
13
13
|
stop: () => void;
|
|
14
|
+
logout: () => void;
|
|
14
15
|
viewLogs: () => void;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -173,6 +174,7 @@ const defaultImplementation: HumanBehaviorInterface = {
|
|
|
173
174
|
},
|
|
174
175
|
start: () => {},
|
|
175
176
|
stop: () => {},
|
|
177
|
+
logout: () => {},
|
|
176
178
|
viewLogs: () => {},
|
|
177
179
|
};
|
|
178
180
|
|
|
@@ -195,6 +197,9 @@ const createQueuingImplementation = (queueEvent: (event: any) => void): HumanBeh
|
|
|
195
197
|
stop: () => {
|
|
196
198
|
// Stop is a no-op when not initialized
|
|
197
199
|
},
|
|
200
|
+
logout: () => {
|
|
201
|
+
// Logout is a no-op when not initialized
|
|
202
|
+
},
|
|
198
203
|
viewLogs: () => {
|
|
199
204
|
logWarn('Logs are not available until HumanBehaviorTracker is initialized');
|
|
200
205
|
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side utilities for HumanBehavior SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ServerSideUserData {
|
|
6
|
+
email: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
image?: string;
|
|
9
|
+
provider?: string;
|
|
10
|
+
[key: string]: any; // Allow additional properties
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Identify user from server-side (NextAuth, Firebase, etc.)
|
|
15
|
+
* Use this in your auth events to track users immediately on sign-in
|
|
16
|
+
*/
|
|
17
|
+
export async function identifyUser(
|
|
18
|
+
userData: ServerSideUserData,
|
|
19
|
+
apiKey: string
|
|
20
|
+
): Promise<{ success: boolean; error?: string }> {
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch('https://ingest.humanbehavior.co/api/ingestion/user', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: {
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'Authorization': `Bearer ${apiKey}`
|
|
27
|
+
},
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
userId: userData.email,
|
|
30
|
+
userAttributes: userData,
|
|
31
|
+
sessionId: null, // Will be set by client-side session recording
|
|
32
|
+
posthogName: userData.email || userData.name || null // Update posthogName with email
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const errorText = await response.text();
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: `Failed to identify user: ${response.status} ${response.statusText} - ${errorText}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { success: true };
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
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,37 +762,70 @@ export class HumanBehaviorTracker {
|
|
|
759
762
|
// Enable console tracking
|
|
760
763
|
this.enableConsoleTracking();
|
|
761
764
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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');
|
|
779
810
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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;
|
|
793
829
|
}
|
|
794
830
|
|
|
795
831
|
public async stop() {
|
|
@@ -801,6 +837,18 @@ export class HumanBehaviorTracker {
|
|
|
801
837
|
this.flushInterval = null;
|
|
802
838
|
}
|
|
803
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
|
+
|
|
804
852
|
// Disable console tracking
|
|
805
853
|
this.disableConsoleTracking();
|
|
806
854
|
|
|
@@ -960,8 +1008,9 @@ export class HumanBehaviorTracker {
|
|
|
960
1008
|
}
|
|
961
1009
|
|
|
962
1010
|
/**
|
|
963
|
-
*
|
|
964
|
-
* This
|
|
1011
|
+
* Clear user data and reset session when user signs out of the site
|
|
1012
|
+
* This should be called when a user logs out of your application to prevent
|
|
1013
|
+
* data contamination between different users
|
|
965
1014
|
*/
|
|
966
1015
|
public logout(): void {
|
|
967
1016
|
if (!isBrowser) return;
|
|
@@ -979,7 +1028,14 @@ export class HumanBehaviorTracker {
|
|
|
979
1028
|
this.endUserId = null;
|
|
980
1029
|
this.userProperties = {};
|
|
981
1030
|
|
|
982
|
-
|
|
1031
|
+
// Generate a new session ID for the next user
|
|
1032
|
+
this.sessionId = uuidv1();
|
|
1033
|
+
if (isBrowser) {
|
|
1034
|
+
localStorage.setItem('human_behavior_session_id', this.sessionId);
|
|
1035
|
+
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
logInfo('User logged out - cleared all user data and started fresh session');
|
|
983
1039
|
} catch (error) {
|
|
984
1040
|
logError('Error during logout:', error);
|
|
985
1041
|
}
|
|
@@ -1041,6 +1097,41 @@ export class HumanBehaviorTracker {
|
|
|
1041
1097
|
return this.currentUrl;
|
|
1042
1098
|
}
|
|
1043
1099
|
|
|
1100
|
+
/**
|
|
1101
|
+
* Get current snapshot frequency info
|
|
1102
|
+
*/
|
|
1103
|
+
public getSnapshotFrequencyInfo(): {
|
|
1104
|
+
sessionDuration: number;
|
|
1105
|
+
currentInterval: number;
|
|
1106
|
+
currentThreshold: number;
|
|
1107
|
+
phase: string;
|
|
1108
|
+
} {
|
|
1109
|
+
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
|
+
|
|
1127
|
+
return {
|
|
1128
|
+
sessionDuration,
|
|
1129
|
+
currentInterval: interval,
|
|
1130
|
+
currentThreshold: threshold,
|
|
1131
|
+
phase
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1044
1135
|
/**
|
|
1045
1136
|
* Test if the tracker can reach the ingestion server
|
|
1046
1137
|
*/
|