humanbehavior-js 0.2.3 → 0.2.5

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.
@@ -194,6 +194,10 @@ declare class HumanBehaviorTracker {
194
194
  userId?: string;
195
195
  userProperties: Record<string, any>;
196
196
  }): Promise<string>;
197
+ /**
198
+ * Get current user attributes
199
+ */
200
+ getUserAttributes(): Record<string, any>;
197
201
  start(): Promise<void>;
198
202
  stop(): Promise<void>;
199
203
  addEvent(event: any): Promise<void>;
@@ -20,7 +20,10 @@ declare const useRedaction: () => {
20
20
  getRedactedFields: () => string[];
21
21
  };
22
22
  declare const useUserTracking: () => {
23
- addUserInfo: (userId: string, userProperties: Record<string, any>) => Promise<{
23
+ addUserInfo: ({ userId, userProperties }: {
24
+ userId?: string;
25
+ userProperties: Record<string, any>;
26
+ }) => Promise<{
24
27
  success: boolean;
25
28
  error?: undefined;
26
29
  } | {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humanbehavior-js",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "SDK for HumanBehavior session and event recording",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "repository": {
53
53
  "type": "git",
54
- "url": "git+https://github.com/humanbehavior-gh/HumanBehavior.git"
54
+ "url": "git+https://github.com/humanbehavior-gh/humanbehavior-js.git"
55
55
  },
56
56
  "keywords": [
57
57
  "session-recording",
package/simple-spa.html CHANGED
@@ -228,6 +228,57 @@
228
228
  <button class="button secondary" onclick="trackCustomEvent('demo_button_2', {button: 'demo2'})">Demo Button 2</button>
229
229
  <button class="button success" onclick="trackCustomEvent('demo_button_3', {button: 'demo3'})">Demo Button 3</button>
230
230
 
231
+ <h3>User Authentication Test</h3>
232
+ <button class="button" onclick="testUserAuthentication()">
233
+ Test User Authentication
234
+ </button>
235
+ <p style="font-size: 12px; color: #666; margin-top: 5px;">
236
+ Tests the new behavior where endUserId stays the same but posthogName gets updated with email
237
+ </p>
238
+
239
+ <h3>Real User Authentication</h3>
240
+ <form onsubmit="handleAuthSubmit(event)" style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; margin: 15px 0;">
241
+ <div style="margin-bottom: 15px;">
242
+ <label for="authEmail" style="display: block; margin-bottom: 5px; font-weight: 600;">Email:</label>
243
+ <input type="email" id="authEmail" placeholder="your.email@example.com" required
244
+ style="width: 100%; padding: 10px; border-radius: 5px; border: none; background: rgba(255,255,255,0.9);">
245
+ </div>
246
+
247
+ <div style="margin-bottom: 15px;">
248
+ <label for="authName" style="display: block; margin-bottom: 5px; font-weight: 600;">Name:</label>
249
+ <input type="text" id="authName" placeholder="Your Name" required
250
+ style="width: 100%; padding: 10px; border-radius: 5px; border: none; background: rgba(255,255,255,0.9);">
251
+ </div>
252
+
253
+ <div style="margin-bottom: 15px;">
254
+ <label for="authPlan" style="display: block; margin-bottom: 5px; font-weight: 600;">Plan:</label>
255
+ <select id="authPlan" style="width: 100%; padding: 10px; border-radius: 5px; border: none; background: rgba(255,255,255,0.9);">
256
+ <option value="free">Free</option>
257
+ <option value="basic">Basic</option>
258
+ <option value="premium">Premium</option>
259
+ <option value="enterprise">Enterprise</option>
260
+ </select>
261
+ </div>
262
+
263
+ <div style="margin-bottom: 15px;">
264
+ <label for="authCompany" style="display: block; margin-bottom: 5px; font-weight: 600;">Company (Optional):</label>
265
+ <input type="text" id="authCompany" placeholder="Your Company"
266
+ style="width: 100%; padding: 10px; border-radius: 5px; border: none; background: rgba(255,255,255,0.9);">
267
+ </div>
268
+
269
+ <button type="submit" class="button success" style="width: 100%;">
270
+ 🔐 Authenticate User
271
+ </button>
272
+ </form>
273
+
274
+ <div style="margin-top: 15px; display: flex; gap: 10px;">
275
+ <button class="button secondary" onclick="showAuthStatus()" style="flex: 1;">
276
+ 📊 Show Auth Status
277
+ </button>
278
+ </div>
279
+
280
+ <div id="authResult" style="margin-top: 15px; padding: 10px; border-radius: 5px; display: none;"></div>
281
+
231
282
  <div class="logs" id="demoLogs">
232
283
  <div>Demo events will appear here...</div>
233
284
  </div>
@@ -498,7 +549,45 @@
498
549
  addLog('customEvent method not available');
499
550
  }
500
551
  } catch (error) {
501
- addLog(`Error tracking event: ${error.message}`);
552
+ console.error('Error tracking custom event:', error);
553
+ addLog('Error tracking custom event: ' + error.message);
554
+ }
555
+ }
556
+
557
+ // Test user authentication (new behavior)
558
+ async function testUserAuthentication() {
559
+ if (!tracker) {
560
+ addLog('Tracker not available');
561
+ return;
562
+ }
563
+
564
+ try {
565
+ const originalEndUserId = tracker.getSessionId();
566
+ addLog(`Original endUserId: ${originalEndUserId}`);
567
+
568
+ // Simulate user sign-in
569
+ const userId = await tracker.addUserInfo({
570
+ userId: 'user123@example.com',
571
+ userProperties: {
572
+ email: 'user123@example.com',
573
+ name: 'John Doe',
574
+ plan: 'premium',
575
+ signupDate: new Date().toISOString()
576
+ }
577
+ });
578
+
579
+ const newEndUserId = tracker.getSessionId();
580
+ addLog(`New endUserId: ${newEndUserId}`);
581
+ addLog(`endUserId changed: ${originalEndUserId !== newEndUserId ? 'YES (BAD)' : 'NO (GOOD)'}`);
582
+ addLog(`User authenticated: ${userId}`);
583
+
584
+ // The endUserId should stay the same, but posthogName will be updated with email
585
+ addLog('✅ endUserId maintained for session continuity');
586
+ addLog('✅ posthogName will be updated with email for UI display');
587
+
588
+ } catch (error) {
589
+ console.error('Error testing user authentication:', error);
590
+ addLog('Error testing user authentication: ' + error.message);
502
591
  }
503
592
  }
504
593
 
@@ -524,6 +613,60 @@
524
613
 
525
614
  input.value = '';
526
615
  }
616
+
617
+ // Handle authentication form submission
618
+ async function handleAuthSubmit(event) {
619
+ event.preventDefault();
620
+ const email = document.getElementById('authEmail').value;
621
+ const name = document.getElementById('authName').value;
622
+ const plan = document.getElementById('authPlan').value;
623
+ const company = document.getElementById('authCompany').value;
624
+
625
+ if (!email || !name) {
626
+ alert('Email and Name are required for authentication.');
627
+ return;
628
+ }
629
+
630
+ try {
631
+ const userId = await tracker.addUserInfo({
632
+ userId: email, // Use email as userId
633
+ userProperties: {
634
+ email: email,
635
+ name: name,
636
+ plan: plan,
637
+ company: company,
638
+ signupDate: new Date().toISOString()
639
+ }
640
+ });
641
+
642
+ const newEndUserId = tracker.getSessionId();
643
+ addLog(`New endUserId after authentication: ${newEndUserId}`);
644
+ addLog(`endUserId changed: ${tracker.getSessionId() !== newEndUserId ? 'YES (BAD)' : 'NO (GOOD)'}`);
645
+ addLog(`User authenticated: ${userId}`);
646
+
647
+ const authResultDiv = document.getElementById('authResult');
648
+ authResultDiv.style.display = 'block';
649
+ authResultDiv.innerHTML = `
650
+ <strong>Authentication Successful!</strong><br>
651
+ New End User ID: ${newEndUserId}<br>
652
+ Original End User ID: ${tracker.getSessionId()}<br>
653
+ endUserId changed: ${tracker.getSessionId() !== newEndUserId ? 'YES (BAD)' : 'NO (GOOD)'}
654
+ `;
655
+ authResultDiv.style.color = 'green';
656
+
657
+ } catch (error) {
658
+ console.error('Error authenticating user:', error);
659
+ addLog('Error authenticating user: ' + error.message);
660
+
661
+ const authResultDiv = document.getElementById('authResult');
662
+ authResultDiv.style.display = 'block';
663
+ authResultDiv.innerHTML = `
664
+ <strong>Authentication Failed!</strong><br>
665
+ Error: ${error.message}
666
+ `;
667
+ authResultDiv.style.color = 'red';
668
+ }
669
+ }
527
670
 
528
671
  // View logs
529
672
  function viewLogs() {
@@ -571,8 +714,7 @@
571
714
  try {
572
715
  const userInfo = tracker.getUserInfo ? tracker.getUserInfo() : null;
573
716
  if (userInfo) {
574
- const infoText = `
575
- <strong>Detailed User Information:</strong><br>
717
+ const infoText = ` <strong>Detailed User Information:</strong><br>
576
718
  End User ID: ${userInfo.endUserId || 'Not set'}<br>
577
719
  Session ID: ${userInfo.sessionId}<br>
578
720
  Is Preexisting User: ${userInfo.isPreexistingUser ? 'Yes' : 'No'}<br>
@@ -593,6 +735,44 @@
593
735
  addLog(`Error getting user info: ${error.message}`);
594
736
  }
595
737
  }
738
+
739
+ // Show current authentication status
740
+ function showAuthStatus() {
741
+ if (!tracker) {
742
+ addLog('Tracker not available');
743
+ return;
744
+ }
745
+ try {
746
+ const userInfo = tracker.getUserInfo ? tracker.getUserInfo() : null;
747
+ const userAttributes = tracker.getUserAttributes ? tracker.getUserAttributes() : {};
748
+
749
+ if (userInfo) {
750
+ const statusText = `
751
+ <strong>Current Authentication Status:</strong><br>
752
+ End User ID: ${userInfo.endUserId || 'Not set'}<br>
753
+ Session ID: ${userInfo.sessionId}<br>
754
+ Is Preexisting User: ${userInfo.isPreexistingUser ? 'Yes' : 'No'}<br>
755
+ Initialized: ${userInfo.initialized ? 'Yes' : 'No'}<br>
756
+ <br>
757
+ <strong>User Attributes:</strong><br>
758
+ ${Object.keys(userAttributes).length > 0 ?
759
+ Object.entries(userAttributes).map(([key, value]) => `${key}: ${value}`).join('<br>') :
760
+ 'No user attributes set'
761
+ }
762
+ <br><br>
763
+ <strong>Cookie Check:</strong><br>
764
+ End User Cookie: ${tracker.getCookie ? tracker.getCookie(`human_behavior_end_user_id_13c3e029-ca45-4a3c-a33b-f5dcb297e31c`) : 'Method not available'}<br>
765
+ Session Cookie: ${tracker.getCookie ? tracker.getCookie('human_behavior_session_id') : 'Method not available'}
766
+ `;
767
+ addLog('Authentication status displayed', { userInfo, userAttributes });
768
+ alert(statusText);
769
+ } else {
770
+ addLog('getUserInfo method not available for status');
771
+ }
772
+ } catch (error) {
773
+ addLog(`Error getting authentication status: ${error.message}`);
774
+ }
775
+ }
596
776
 
597
777
  // Add log message
598
778
  function addLog(message, data = null) {
@@ -655,4 +835,4 @@
655
835
  }, 5000); // Check every 5 seconds
656
836
  </script>
657
837
  </body>
658
- </html>
838
+ </html>
package/src/api.ts CHANGED
@@ -222,24 +222,31 @@ export class HumanBehaviorAPI {
222
222
 
223
223
  async sendUserData(userId: string, userData: Record<string, any>, sessionId: string) {
224
224
  try {
225
+ const payload = {
226
+ userId: userId,
227
+ userAttributes: userData,
228
+ sessionId: sessionId,
229
+ posthogName: userData.email || userData.name || null // Update posthogName with email
230
+ };
231
+
232
+ console.log('Sending user data to server:', payload);
233
+
225
234
  const response = await fetch(`${this.baseUrl}/api/ingestion/user`, {
226
235
  method: 'POST',
227
236
  headers: {
228
237
  'Content-Type': 'application/json',
229
238
  'Authorization': `Bearer ${this.apiKey}`
230
239
  },
231
- body: JSON.stringify({
232
- userId: userId,
233
- userAttributes: userData,
234
- sessionId: sessionId
235
- })
240
+ body: JSON.stringify(payload)
236
241
  });
237
242
 
238
243
  if (!response.ok) {
239
244
  throw new Error(`Failed to send user data: ${response.statusText} with API key: ${this.apiKey}`);
240
245
  }
241
246
 
242
- return await response.json();
247
+ const result = await response.json();
248
+ console.log('Server response:', result);
249
+ return result;
243
250
  } catch (error) {
244
251
  logError('Error sending user data:', error);
245
252
  throw error;
@@ -8,7 +8,7 @@ const isBrowser = () => typeof window !== 'undefined';
8
8
  // Define the public interface that components will interact with
9
9
  interface HumanBehaviorInterface {
10
10
  addEvent: (event: any) => void;
11
- addUserInfo: (userProperties: Record<string, any>) => Promise<void>;
11
+ addUserInfo: ({ userId, userProperties }: { userId?: string, userProperties: Record<string, any> }) => Promise<string>;
12
12
  start: () => void;
13
13
  stop: () => void;
14
14
  viewLogs: () => void;
@@ -122,9 +122,9 @@ export const HumanBehaviorProvider = ({ apiKey, client, children, options }: Hum
122
122
  if (currentQueue.length > 0) {
123
123
  for (const event of currentQueue) {
124
124
  if (event.type === 'identify') {
125
- logDebug('Processing queued identify event', event.userProperties);
125
+ logDebug('Processing queued identify event', event);
126
126
  try {
127
- await tracker.addUserInfo(event.userProperties);
127
+ await tracker.addUserInfo({ userId: event.userId, userProperties: event.userProperties });
128
128
  } catch (error) {
129
129
  logError('Failed to process queued user info:', error);
130
130
  }
@@ -165,10 +165,12 @@ export const HumanBehaviorProvider = ({ apiKey, client, children, options }: Hum
165
165
  );
166
166
  };
167
167
 
168
- // No-op implementation for server-side
169
- const serverSideImplementation: HumanBehaviorInterface = {
168
+ // Default implementation for when tracker is not available
169
+ const defaultImplementation: HumanBehaviorInterface = {
170
170
  addEvent: () => {},
171
- addUserInfo: async () => {},
171
+ addUserInfo: async ({ userId, userProperties }: { userId?: string, userProperties: Record<string, any> }) => {
172
+ return userId || '';
173
+ },
172
174
  start: () => {},
173
175
  stop: () => {},
174
176
  viewLogs: () => {},
@@ -179,11 +181,13 @@ const createQueuingImplementation = (queueEvent: (event: any) => void): HumanBeh
179
181
  addEvent: (event: any) => {
180
182
  queueEvent(event);
181
183
  },
182
- addUserInfo: async (userProperties: Record<string, any>) => {
184
+ addUserInfo: async ({ userId, userProperties }: { userId?: string, userProperties: Record<string, any> }) => {
183
185
  queueEvent({
184
186
  type: 'identify',
187
+ userId,
185
188
  userProperties,
186
189
  });
190
+ return userId || ''; // Return userId or empty string to match interface
187
191
  },
188
192
  start: () => {
189
193
  // Start will be called automatically when initialized
@@ -231,7 +235,7 @@ export const useRedaction = () => {
231
235
  export const useUserTracking = () => {
232
236
  const tracker = useHumanBehavior();
233
237
 
234
- const addUserInfo = useCallback(async (userId: string, userProperties: Record<string, any>) => {
238
+ const addUserInfo = useCallback(async ({ userId, userProperties }: { userId?: string, userProperties: Record<string, any> }) => {
235
239
  try {
236
240
  await tracker.addUserInfo({ userId, userProperties });
237
241
  return { success: true };
package/src/tracker.ts CHANGED
@@ -725,19 +725,26 @@ export class HumanBehaviorTracker {
725
725
  { userId, userProperties }: { userId?: string, userProperties: Record<string, any> }
726
726
  ): Promise<string> {
727
727
  await this.ensureInitialized();
728
- // Determine the userId to use
729
- const resolvedUserId = userId || userProperties.email;
730
- if (!resolvedUserId) {
731
- return this.endUserId || '';
732
- }
728
+
729
+ // Keep the original endUserId (UUID) - don't change it
730
+ const originalEndUserId = this.endUserId;
731
+
732
+ // Store user properties
733
733
  this.userProperties = userProperties;
734
- await this.api.sendUserData(resolvedUserId, userProperties, this.sessionId);
735
- // Update endUserId and cookie if changed
736
- if (resolvedUserId !== this.endUserId) {
737
- this.endUserId = resolvedUserId;
738
- this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, resolvedUserId, 365);
739
- }
740
- return this.endUserId || '';
734
+
735
+ // Send user data with the original endUserId
736
+ await this.api.sendUserData(originalEndUserId!, userProperties, this.sessionId);
737
+
738
+ // Don't update endUserId - keep it as the original UUID
739
+ // The posthogName will be updated on the server side with the email
740
+
741
+ return originalEndUserId || '';
742
+ }
743
+ /**
744
+ * Get current user attributes
745
+ */
746
+ public getUserAttributes(): Record<string, any> {
747
+ return { ...this.userProperties };
741
748
  }
742
749
 
743
750
  public async start() {