humanbehavior-js 0.1.0 → 0.1.2
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 +101 -21
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +101 -21
- package/dist/esm/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 +15 -1
- package/package.json +1 -1
- package/simple-demo.html +26 -0
- package/simple-spa.html +50 -0
- package/src/api.ts +1 -11
- package/src/tracker.ts +109 -20
package/dist/types/index.d.ts
CHANGED
|
@@ -176,7 +176,7 @@ declare class HumanBehaviorTracker {
|
|
|
176
176
|
private processRejectedEvents;
|
|
177
177
|
private flush;
|
|
178
178
|
private setCookie;
|
|
179
|
-
|
|
179
|
+
getCookie(name: string): string | null;
|
|
180
180
|
/**
|
|
181
181
|
* Start redaction functionality for sensitive input fields
|
|
182
182
|
* @param options Optional configuration for redaction behavior
|
|
@@ -217,6 +217,20 @@ declare class HumanBehaviorTracker {
|
|
|
217
217
|
blocked: boolean;
|
|
218
218
|
recommendations: string[];
|
|
219
219
|
};
|
|
220
|
+
/**
|
|
221
|
+
* Check if the current user is a preexisting user
|
|
222
|
+
* Returns true if the user has an existing endUserId cookie from a previous session
|
|
223
|
+
*/
|
|
224
|
+
isPreexistingUser(): boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Get user information including whether they are preexisting
|
|
227
|
+
*/
|
|
228
|
+
getUserInfo(): {
|
|
229
|
+
endUserId: string | null;
|
|
230
|
+
sessionId: string;
|
|
231
|
+
isPreexistingUser: boolean;
|
|
232
|
+
initialized: boolean;
|
|
233
|
+
};
|
|
220
234
|
}
|
|
221
235
|
|
|
222
236
|
declare const MAX_CHUNK_SIZE_BYTES: number;
|
package/package.json
CHANGED
package/simple-demo.html
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>HumanBehavior Simple Demo</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>HumanBehavior Tracker Demo</h1>
|
|
10
|
+
<p>This page initializes the HumanBehavior tracker. Check the console for status.</p>
|
|
11
|
+
|
|
12
|
+
<button>Click me to test recording</button>
|
|
13
|
+
<input type="text" placeholder="Type something">
|
|
14
|
+
<input type="password" placeholder="Password (will be redacted)">
|
|
15
|
+
|
|
16
|
+
<script src="./dist/index.min.js"></script>
|
|
17
|
+
<script>
|
|
18
|
+
// Initialize the tracker
|
|
19
|
+
const tracker = HumanBehaviorTracker.init('13c3e029-ca45-4a3c-a33b-f5dcb297e31c', {
|
|
20
|
+
redactFields: ['input[type="password"]']
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
console.log('HumanBehavior tracker initialized:', tracker);
|
|
24
|
+
</script>
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|
package/simple-spa.html
CHANGED
|
@@ -160,6 +160,7 @@
|
|
|
160
160
|
<strong>Session Info:</strong><br>
|
|
161
161
|
Session ID: <span id="sessionId">Loading...</span><br>
|
|
162
162
|
End User ID: <span id="endUserId">Loading...</span><br>
|
|
163
|
+
User Type: <span id="userType">Loading...</span><br>
|
|
163
164
|
Current URL: <span id="currentUrl">Loading...</span>
|
|
164
165
|
</div>
|
|
165
166
|
|
|
@@ -197,12 +198,16 @@
|
|
|
197
198
|
<div class="status">
|
|
198
199
|
<strong>Current Session:</strong><br>
|
|
199
200
|
Session ID: <span id="sessionId-about">Loading...</span><br>
|
|
201
|
+
User Type: <span id="userType-about">Loading...</span><br>
|
|
200
202
|
Page Load Time: <span id="pageLoadTime">Loading...</span>
|
|
201
203
|
</div>
|
|
202
204
|
|
|
203
205
|
<button class="button" onclick="trackCustomEvent('about_page_viewed', {section: 'about'})">
|
|
204
206
|
Track About Page View
|
|
205
207
|
</button>
|
|
208
|
+
<button class="button secondary" onclick="showUserInfo()">
|
|
209
|
+
Show Detailed User Info
|
|
210
|
+
</button>
|
|
206
211
|
`
|
|
207
212
|
},
|
|
208
213
|
|
|
@@ -239,6 +244,7 @@
|
|
|
239
244
|
Initialized: <span id="trackerInitialized">Loading...</span><br>
|
|
240
245
|
Session ID: <span id="sessionId-status">Loading...</span><br>
|
|
241
246
|
End User ID: <span id="endUserId-status">Loading...</span><br>
|
|
247
|
+
User Type: <span id="userType-status">Loading...</span><br>
|
|
242
248
|
Current URL: <span id="currentUrl-status">Loading...</span><br>
|
|
243
249
|
Connection Status: <span id="connectionStatus">Loading...</span>
|
|
244
250
|
</div>
|
|
@@ -418,6 +424,17 @@
|
|
|
418
424
|
el.textContent = endUserId;
|
|
419
425
|
});
|
|
420
426
|
|
|
427
|
+
// Update user type (preexisting vs new)
|
|
428
|
+
const userTypeElements = document.querySelectorAll('#userType, #userType-about, #userType-status');
|
|
429
|
+
let userType = 'Unknown';
|
|
430
|
+
if (tracker.isPreexistingUser) {
|
|
431
|
+
const isPreexisting = tracker.isPreexistingUser();
|
|
432
|
+
userType = isPreexisting ? '🔄 Preexisting User' : '🆕 New User';
|
|
433
|
+
}
|
|
434
|
+
userTypeElements.forEach(el => {
|
|
435
|
+
el.textContent = userType;
|
|
436
|
+
});
|
|
437
|
+
|
|
421
438
|
const currentUrlElements = document.querySelectorAll('#currentUrl, #currentUrl-status');
|
|
422
439
|
const currentUrl = window.location.href;
|
|
423
440
|
currentUrlElements.forEach(el => {
|
|
@@ -522,6 +539,39 @@
|
|
|
522
539
|
});
|
|
523
540
|
}
|
|
524
541
|
|
|
542
|
+
// Show detailed user information
|
|
543
|
+
function showUserInfo() {
|
|
544
|
+
if (!tracker) {
|
|
545
|
+
addLog('Tracker not available');
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
try {
|
|
550
|
+
const userInfo = tracker.getUserInfo ? tracker.getUserInfo() : null;
|
|
551
|
+
if (userInfo) {
|
|
552
|
+
const infoText = `
|
|
553
|
+
<strong>Detailed User Information:</strong><br>
|
|
554
|
+
End User ID: ${userInfo.endUserId || 'Not set'}<br>
|
|
555
|
+
Session ID: ${userInfo.sessionId}<br>
|
|
556
|
+
Is Preexisting User: ${userInfo.isPreexistingUser ? 'Yes' : 'No'}<br>
|
|
557
|
+
Initialized: ${userInfo.initialized ? 'Yes' : 'No'}<br>
|
|
558
|
+
<br>
|
|
559
|
+
<strong>Cookie Check:</strong><br>
|
|
560
|
+
End User Cookie: ${tracker.getCookie ? tracker.getCookie(`human_behavior_end_user_id_13c3e029-ca45-4a3c-a33b-f5dcb297e31c`) : 'Method not available'}<br>
|
|
561
|
+
Session Cookie: ${tracker.getCookie ? tracker.getCookie('human_behavior_session_id') : 'Method not available'}
|
|
562
|
+
`;
|
|
563
|
+
|
|
564
|
+
// Show in an alert or add to logs
|
|
565
|
+
addLog('Detailed user info displayed', userInfo);
|
|
566
|
+
alert(infoText);
|
|
567
|
+
} else {
|
|
568
|
+
addLog('getUserInfo method not available');
|
|
569
|
+
}
|
|
570
|
+
} catch (error) {
|
|
571
|
+
addLog(`Error getting user info: ${error.message}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
525
575
|
// Add log message
|
|
526
576
|
function addLog(message, data = null) {
|
|
527
577
|
const logElements = document.querySelectorAll('.logs');
|
package/src/api.ts
CHANGED
|
@@ -199,7 +199,7 @@ export class HumanBehaviorAPI {
|
|
|
199
199
|
if (!response.ok) {
|
|
200
200
|
throw new Error(`Failed to authenticate user: ${response.statusText} with API key: ${this.apiKey}`);
|
|
201
201
|
}
|
|
202
|
-
|
|
202
|
+
// Returns: { success: true, message: '...', userId: '...' }
|
|
203
203
|
return await response.json();
|
|
204
204
|
} catch (error) {
|
|
205
205
|
logError('Error authenticating user:', error);
|
|
@@ -207,10 +207,6 @@ export class HumanBehaviorAPI {
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
210
|
public sendBeaconEvents(events: any[], sessionId: string) {
|
|
215
211
|
const data = new URLSearchParams()
|
|
216
212
|
data.append('events', encodeURIComponent(JSON.stringify(events)))
|
|
@@ -218,15 +214,9 @@ export class HumanBehaviorAPI {
|
|
|
218
214
|
data.append('timestamp', encodeURIComponent(Date.now().toString()))
|
|
219
215
|
data.append('apiKey', encodeURIComponent(this.apiKey))
|
|
220
216
|
|
|
221
|
-
|
|
222
217
|
const success = navigator.sendBeacon(
|
|
223
218
|
`${this.baseUrl}/api/ingestion/events`,
|
|
224
219
|
data
|
|
225
220
|
);
|
|
226
|
-
|
|
227
|
-
// KoalawareTracker.logToStorage(`Sending events beacon: ${this.baseUrl}/api/ingestion/events`);
|
|
228
|
-
// KoalawareTracker.logToStorage(`Events beacon success: ${success}`);
|
|
229
221
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
222
|
}
|
package/src/tracker.ts
CHANGED
|
@@ -80,7 +80,7 @@ export class HumanBehaviorTracker {
|
|
|
80
80
|
|
|
81
81
|
// Test connection (non-blocking)
|
|
82
82
|
if (isBrowser) {
|
|
83
|
-
const testUrl = tracker.api['baseUrl'] + '/api/
|
|
83
|
+
const testUrl = tracker.api['baseUrl'] + '/api/health';
|
|
84
84
|
fetch(testUrl, { method: 'HEAD' })
|
|
85
85
|
.then(() => logDebug('Connection test successful'))
|
|
86
86
|
.catch((error) => {
|
|
@@ -109,19 +109,29 @@ export class HumanBehaviorTracker {
|
|
|
109
109
|
this.apiKey = apiKey;
|
|
110
110
|
this.redactionManager = new RedactionManager();
|
|
111
111
|
|
|
112
|
-
// Handle session restoration
|
|
112
|
+
// Handle session restoration with improved continuity
|
|
113
113
|
if (isBrowser) {
|
|
114
114
|
const existingSessionId = localStorage.getItem('human_behavior_session_id');
|
|
115
115
|
const lastActivity = localStorage.getItem('human_behavior_last_activity');
|
|
116
116
|
const thirtyMinutesAgo = Date.now() - (30 * 60 * 1000);
|
|
117
117
|
|
|
118
|
+
// Check if we have an existing session that's still within the activity window
|
|
118
119
|
if (existingSessionId && lastActivity && parseInt(lastActivity) > thirtyMinutesAgo) {
|
|
119
120
|
this.sessionId = existingSessionId;
|
|
120
121
|
logDebug(`Reusing existing session: ${this.sessionId}`);
|
|
122
|
+
// Update activity timestamp to extend the session window
|
|
123
|
+
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
121
124
|
} else {
|
|
125
|
+
// Clear old session data if it's expired
|
|
126
|
+
if (existingSessionId) {
|
|
127
|
+
logDebug(`Session expired, clearing old session: ${existingSessionId}`);
|
|
128
|
+
localStorage.removeItem('human_behavior_session_id');
|
|
129
|
+
localStorage.removeItem('human_behavior_last_activity');
|
|
130
|
+
}
|
|
122
131
|
this.sessionId = uuidv1();
|
|
123
132
|
logDebug(`Creating new session: ${this.sessionId}`);
|
|
124
133
|
localStorage.setItem('human_behavior_session_id', this.sessionId);
|
|
134
|
+
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
125
135
|
}
|
|
126
136
|
|
|
127
137
|
this.currentUrl = window.location.href;
|
|
@@ -141,11 +151,11 @@ export class HumanBehaviorTracker {
|
|
|
141
151
|
|
|
142
152
|
const { sessionId, endUserId } = await this.api.init(this.sessionId, userId);
|
|
143
153
|
|
|
144
|
-
// Check if server returned a different session ID
|
|
154
|
+
// Check if server returned a different session ID (for session continuity)
|
|
145
155
|
if (sessionId !== this.sessionId) {
|
|
146
156
|
logDebug(`Server returned different sessionId: ${sessionId} (client had: ${this.sessionId})`);
|
|
147
157
|
this.sessionId = sessionId;
|
|
148
|
-
// Update localStorage with server's session ID
|
|
158
|
+
// Update localStorage with server's session ID for continuity
|
|
149
159
|
if (isBrowser) {
|
|
150
160
|
localStorage.setItem('human_behavior_session_id', this.sessionId);
|
|
151
161
|
}
|
|
@@ -405,8 +415,6 @@ export class HumanBehaviorTracker {
|
|
|
405
415
|
error: console.error
|
|
406
416
|
};
|
|
407
417
|
|
|
408
|
-
|
|
409
|
-
|
|
410
418
|
// Override console methods to capture ALL console output (including logger output)
|
|
411
419
|
console.log = (...args) => {
|
|
412
420
|
this.trackConsoleEvent('log', args);
|
|
@@ -538,7 +546,12 @@ export class HumanBehaviorTracker {
|
|
|
538
546
|
if (!this.userProperties || Object.keys(this.userProperties).length === 0) {
|
|
539
547
|
throw new Error('No user info available. Call addUserInfo() first.');
|
|
540
548
|
}
|
|
541
|
-
await this.api.sendUserAuth(this.endUserId, this.userProperties, this.sessionId, authFields);
|
|
549
|
+
const response = await this.api.sendUserAuth(this.endUserId, this.userProperties, this.sessionId, authFields);
|
|
550
|
+
if (response && response.userId && response.userId !== this.endUserId) {
|
|
551
|
+
// Update endUserId and cookie if backend returns a new userId
|
|
552
|
+
this.endUserId = response.userId;
|
|
553
|
+
this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, response.userId, 365);
|
|
554
|
+
}
|
|
542
555
|
}
|
|
543
556
|
|
|
544
557
|
public async start() {
|
|
@@ -561,6 +574,7 @@ export class HumanBehaviorTracker {
|
|
|
561
574
|
inlineStylesheet: true,
|
|
562
575
|
recordCanvas: true,
|
|
563
576
|
collectFonts: true,
|
|
577
|
+
inlineImages: true,
|
|
564
578
|
blockClass: 'rr-block',
|
|
565
579
|
ignoreClass: 'rr-ignore',
|
|
566
580
|
maskTextClass: 'rr-ignore'
|
|
@@ -661,25 +675,69 @@ export class HumanBehaviorTracker {
|
|
|
661
675
|
}
|
|
662
676
|
}
|
|
663
677
|
|
|
664
|
-
// Add helper methods for cookie management
|
|
678
|
+
// Add helper methods for cookie management with localStorage fallback
|
|
665
679
|
private setCookie(name: string, value: string, daysToExpire: number) {
|
|
666
680
|
if (!isBrowser) return;
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
681
|
+
|
|
682
|
+
try {
|
|
683
|
+
// Try to set cookie first
|
|
684
|
+
const date = new Date();
|
|
685
|
+
date.setTime(date.getTime() + (daysToExpire * 24 * 60 * 60 * 1000));
|
|
686
|
+
const expires = `expires=${date.toUTCString()}`;
|
|
687
|
+
document.cookie = `${name}=${value};${expires};path=/;SameSite=Lax`;
|
|
688
|
+
|
|
689
|
+
// Also store in localStorage as backup
|
|
690
|
+
localStorage.setItem(name, value);
|
|
691
|
+
logDebug(`Set cookie and localStorage: ${name}`);
|
|
692
|
+
} catch (error) {
|
|
693
|
+
// If cookie fails, use localStorage only
|
|
694
|
+
try {
|
|
695
|
+
localStorage.setItem(name, value);
|
|
696
|
+
logDebug(`Cookie blocked, using localStorage: ${name}`);
|
|
697
|
+
} catch (localStorageError) {
|
|
698
|
+
logError('Failed to store user ID in both cookie and localStorage:', localStorageError);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
671
701
|
}
|
|
672
702
|
|
|
673
|
-
|
|
703
|
+
public getCookie(name: string): string | null {
|
|
674
704
|
if (!isBrowser) return null;
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
705
|
+
|
|
706
|
+
try {
|
|
707
|
+
// Try to get from cookie first
|
|
708
|
+
const nameEQ = name + "=";
|
|
709
|
+
const ca = document.cookie.split(';');
|
|
710
|
+
for (let i = 0; i < ca.length; i++) {
|
|
711
|
+
let c = ca[i];
|
|
712
|
+
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
|
713
|
+
if (c.indexOf(nameEQ) === 0) {
|
|
714
|
+
const cookieValue = c.substring(nameEQ.length, c.length);
|
|
715
|
+
logDebug(`Found cookie: ${name}`);
|
|
716
|
+
return cookieValue;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// If cookie not found, try localStorage
|
|
721
|
+
const localStorageValue = localStorage.getItem(name);
|
|
722
|
+
if (localStorageValue) {
|
|
723
|
+
logDebug(`Cookie not found, using localStorage: ${name}`);
|
|
724
|
+
return localStorageValue;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return null;
|
|
728
|
+
} catch (error) {
|
|
729
|
+
// If cookie access fails, try localStorage
|
|
730
|
+
try {
|
|
731
|
+
const localStorageValue = localStorage.getItem(name);
|
|
732
|
+
if (localStorageValue) {
|
|
733
|
+
logDebug(`Cookie access failed, using localStorage: ${name}`);
|
|
734
|
+
return localStorageValue;
|
|
735
|
+
}
|
|
736
|
+
} catch (localStorageError) {
|
|
737
|
+
logError('Failed to access both cookie and localStorage:', localStorageError);
|
|
738
|
+
}
|
|
739
|
+
return null;
|
|
681
740
|
}
|
|
682
|
-
return null;
|
|
683
741
|
}
|
|
684
742
|
|
|
685
743
|
/**
|
|
@@ -787,6 +845,37 @@ export class HumanBehaviorTracker {
|
|
|
787
845
|
|
|
788
846
|
return { blocked, recommendations };
|
|
789
847
|
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Check if the current user is a preexisting user
|
|
851
|
+
* Returns true if the user has an existing endUserId cookie from a previous session
|
|
852
|
+
*/
|
|
853
|
+
public isPreexistingUser(): boolean {
|
|
854
|
+
if (!isBrowser) {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Check if there's an existing endUserId cookie for this API key
|
|
859
|
+
const existingEndUserId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
|
|
860
|
+
return existingEndUserId !== null && existingEndUserId !== this.endUserId;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Get user information including whether they are preexisting
|
|
865
|
+
*/
|
|
866
|
+
public getUserInfo(): {
|
|
867
|
+
endUserId: string | null;
|
|
868
|
+
sessionId: string;
|
|
869
|
+
isPreexistingUser: boolean;
|
|
870
|
+
initialized: boolean;
|
|
871
|
+
} {
|
|
872
|
+
return {
|
|
873
|
+
endUserId: this.endUserId,
|
|
874
|
+
sessionId: this.sessionId,
|
|
875
|
+
isPreexistingUser: this.isPreexistingUser(),
|
|
876
|
+
initialized: this.initialized
|
|
877
|
+
};
|
|
878
|
+
}
|
|
790
879
|
}
|
|
791
880
|
|
|
792
881
|
// Only expose to window object in browser environments
|