humanbehavior-js 0.4.20 → 0.4.21

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.
Files changed (53) hide show
  1. package/dist/cjs/angular/index.cjs +799 -13
  2. package/dist/cjs/angular/index.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +815 -13
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/cjs/react/index.cjs +800 -14
  6. package/dist/cjs/react/index.cjs.map +1 -1
  7. package/dist/cjs/remix/index.cjs +800 -14
  8. package/dist/cjs/remix/index.cjs.map +1 -1
  9. package/dist/cjs/svelte/index.cjs +799 -13
  10. package/dist/cjs/svelte/index.cjs.map +1 -1
  11. package/dist/cjs/vue/index.cjs +799 -13
  12. package/dist/cjs/vue/index.cjs.map +1 -1
  13. package/dist/esm/angular/index.js +799 -13
  14. package/dist/esm/angular/index.js.map +1 -1
  15. package/dist/esm/index.js +807 -14
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/react/index.js +800 -14
  18. package/dist/esm/react/index.js.map +1 -1
  19. package/dist/esm/remix/index.js +800 -14
  20. package/dist/esm/remix/index.js.map +1 -1
  21. package/dist/esm/svelte/index.js +799 -13
  22. package/dist/esm/svelte/index.js.map +1 -1
  23. package/dist/esm/vue/index.js +799 -13
  24. package/dist/esm/vue/index.js.map +1 -1
  25. package/dist/index.min.js +1 -1
  26. package/dist/index.min.js.map +1 -1
  27. package/dist/types/angular/index.d.ts +60 -1
  28. package/dist/types/index.d.ts +258 -3
  29. package/dist/types/react/index.d.ts +60 -1
  30. package/dist/types/remix/index.d.ts +60 -1
  31. package/dist/types/svelte/index.d.ts +60 -1
  32. package/package/canvas-recording-demo.html +1 -1
  33. package/package/simple-spa.html +1 -1
  34. package/package/src/angular/index.ts +3 -3
  35. package/package/src/react/index.tsx +2 -2
  36. package/package/src/svelte/index.ts +1 -1
  37. package/package/src/tracker.ts +2 -2
  38. package/package/src/vue/index.ts +1 -1
  39. package/package.json +1 -1
  40. package/simple-spa.html +164 -2
  41. package/src/angular/index.ts +3 -3
  42. package/src/api.ts +40 -0
  43. package/src/index.ts +7 -0
  44. package/src/react/index.tsx +2 -2
  45. package/src/svelte/index.ts +1 -1
  46. package/src/tracker.ts +175 -11
  47. package/src/utils/ip-detector.ts +158 -0
  48. package/src/utils/property-detector.ts +345 -0
  49. package/src/utils/property-manager.ts +274 -0
  50. package/src/vue/index.ts +1 -1
  51. package/canvas-recording-demo.html +0 -143
  52. package/clean-console-demo.html +0 -39
  53. package/simple-demo.html +0 -26
@@ -29,7 +29,7 @@ interface HumanBehaviorProviderProps {
29
29
  logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
30
30
  redactFields?: string[];
31
31
  suppressConsoleErrors?: boolean;
32
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
32
+ recordCanvas?: boolean; // Enable canvas recording with protection
33
33
  };
34
34
  }
35
35
 
@@ -262,7 +262,7 @@ export const useUserTracking = () => {
262
262
  };
263
263
  };
264
264
 
265
- // Automatic page tracking component (similar to PostHog's PostHogPageView)
265
+ // Automatic page tracking component
266
266
  function HumanBehaviorPageView() {
267
267
  const tracker = useContext(HumanBehaviorContext);
268
268
 
@@ -7,7 +7,7 @@ export const humanBehaviorStore = {
7
7
  logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
8
8
  redactFields?: string[];
9
9
  suppressConsoleErrors?: boolean;
10
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
10
+ recordCanvas?: boolean; // Enable canvas recording with protection
11
11
  }) => {
12
12
  return HumanBehaviorTracker.init(apiKey, options);
13
13
  }
@@ -62,7 +62,7 @@ export class HumanBehaviorTracker {
62
62
  redactFields?: string[];
63
63
  enableAutomaticTracking?: boolean;
64
64
  suppressConsoleErrors?: boolean; // New option to control error suppression
65
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
65
+ recordCanvas?: boolean; // Enable canvas recording with protection
66
66
  automaticTrackingOptions?: {
67
67
  trackButtons?: boolean;
68
68
  trackLinks?: boolean;
@@ -847,7 +847,7 @@ export class HumanBehaviorTracker {
847
847
  inlineStylesheet: true, // Keep styles for proper session replay
848
848
  recordCrossOriginIframes: false, // Prevent cross-origin iframe errors
849
849
 
850
- // ✅ CANVAS RECORDING - PostHog-style protection against overwhelm
850
+ // ✅ CANVAS RECORDING - protection against overwhelm
851
851
  recordCanvas: this.recordCanvas, // Opt-in only
852
852
  sampling: this.recordCanvas ? { canvas: 4 } : undefined, // 4 FPS throttle
853
853
  dataURLOptions: this.recordCanvas ? {
@@ -7,7 +7,7 @@ interface HumanBehaviorPluginOptions {
7
7
  logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
8
8
  redactFields?: string[];
9
9
  suppressConsoleErrors?: boolean;
10
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
10
+ recordCanvas?: boolean; // Enable canvas recording with protection
11
11
  }
12
12
 
13
13
  export const HumanBehaviorPlugin = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humanbehavior-js",
3
- "version": "0.4.20",
3
+ "version": "0.4.21",
4
4
  "description": "SDK for HumanBehavior session and event recording",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
package/simple-spa.html CHANGED
@@ -326,7 +326,8 @@
326
326
  console.log('Initializing tracker...');
327
327
  tracker = HumanBehaviorTracker.init('13c3e029-ca45-4a3c-a33b-f5dcb297e31c', {
328
328
  logLevel: 'debug',
329
- redactFields: ['password', 'credit_card']
329
+ redactFields: ['password', 'credit_card'],
330
+ enableAutomaticProperties: true
330
331
  });
331
332
 
332
333
  console.log('Tracker created, waiting for initialization...');
@@ -359,6 +360,12 @@
359
360
  // Update status periodically
360
361
  setInterval(updateStatus, 2000);
361
362
 
363
+ // Display automatic properties
364
+ displayAutomaticProperties();
365
+
366
+ // Display IP information
367
+ displayIPInfo();
368
+
362
369
  console.log('SPA App initialized successfully');
363
370
  addLog('SPA App initialized successfully');
364
371
 
@@ -581,7 +588,7 @@
581
588
  addLog(`endUserId changed: ${originalEndUserId !== newEndUserId ? 'YES (BAD)' : 'NO (GOOD)'}`);
582
589
  addLog(`User authenticated: ${userId}`);
583
590
 
584
- // The endUserId should stay the same, but posthogName will be updated with email
591
+ // The endUserId should stay the same, but user name will be updated
585
592
  addLog('✅ endUserId maintained for session continuity');
586
593
  addLog('✅ posthogName will be updated with email for UI display');
587
594
 
@@ -774,6 +781,161 @@
774
781
  }
775
782
  }
776
783
 
784
+ // Display automatic properties
785
+ function displayAutomaticProperties() {
786
+ if (!tracker) {
787
+ addLog('Tracker not available for automatic properties');
788
+ return;
789
+ }
790
+
791
+ try {
792
+ const allProperties = tracker.getAllProperties();
793
+
794
+ // Create a properties display section
795
+ const propertiesSection = document.createElement('div');
796
+ propertiesSection.className = 'demo-section';
797
+ propertiesSection.style.cssText = `
798
+ background: rgba(255, 255, 255, 0.1);
799
+ border-radius: 15px;
800
+ padding: 30px;
801
+ margin: 20px 0;
802
+ backdrop-filter: blur(5px);
803
+ `;
804
+
805
+ propertiesSection.innerHTML = `
806
+ <h2>🔍 Automatic Properties</h2>
807
+ <p>These properties are automatically captured by the HumanBehavior SDK:</p>
808
+
809
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
810
+ <div>
811
+ <h3>🖥️ Device Information</h3>
812
+ <div class="status" style="font-size: 12px; max-height: 200px; overflow-y: auto;">
813
+ ${Object.entries(allProperties.automatic)
814
+ .filter(([key]) => ['device_type', 'browser', 'browser_version', 'os', 'os_version', 'screen_resolution', 'viewport_size', 'color_depth', 'timezone', 'language', 'languages'].includes(key))
815
+ .map(([key, value]) => `<strong>${key}:</strong> ${JSON.stringify(value)}<br>`)
816
+ .join('')}
817
+ </div>
818
+ </div>
819
+
820
+ <div>
821
+ <h3>📍 Location Information</h3>
822
+ <div class="status" style="font-size: 12px; max-height: 200px; overflow-y: auto;">
823
+ ${Object.entries(allProperties.automatic)
824
+ .filter(([key]) => ['current_url', 'pathname', 'search', 'hash', 'title', 'referrer', 'referrer_domain', 'initial_referrer', 'initial_referrer_domain', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].includes(key))
825
+ .map(([key, value]) => `<strong>${key}:</strong> ${JSON.stringify(value)}<br>`)
826
+ .join('')}
827
+ </div>
828
+ </div>
829
+ </div>
830
+
831
+ <div style="margin-top: 20px;">
832
+ <h3>📌 Session Properties</h3>
833
+ <div class="status" style="font-size: 12px; max-height: 150px; overflow-y: auto;">
834
+ ${Object.keys(allProperties.session).length > 0 ?
835
+ Object.entries(allProperties.session).map(([key, value]) => `<strong>${key}:</strong> ${JSON.stringify(value)}<br>`).join('') :
836
+ '<em>No session properties set</em>'
837
+ }
838
+ </div>
839
+ </div>
840
+
841
+ <div style="margin-top: 20px;">
842
+ <h3>👤 User Properties</h3>
843
+ <div class="status" style="font-size: 12px; max-height: 150px; overflow-y: auto;">
844
+ ${Object.keys(allProperties.user).length > 0 ?
845
+ Object.entries(allProperties.user).map(([key, value]) => `<strong>${key}:</strong> ${JSON.stringify(value)}<br>`).join('') :
846
+ '<em>No user properties set</em>'
847
+ }
848
+ </div>
849
+ </div>
850
+
851
+ <div style="margin-top: 20px;">
852
+ <h3>🎯 Initial Properties</h3>
853
+ <div class="status" style="font-size: 12px; max-height: 150px; overflow-y: auto;">
854
+ ${Object.entries(allProperties.initial).map(([key, value]) => `<strong>${key}:</strong> ${JSON.stringify(value)}<br>`).join('')}
855
+ </div>
856
+ </div>
857
+ `;
858
+
859
+ // Insert the properties section at the top of the content
860
+ const contentDiv = document.getElementById('content');
861
+ if (contentDiv) {
862
+ contentDiv.insertBefore(propertiesSection, contentDiv.firstChild);
863
+ }
864
+
865
+ addLog('Automatic properties displayed');
866
+
867
+ } catch (error) {
868
+ console.error('Error displaying automatic properties:', error);
869
+ addLog('Error displaying automatic properties: ' + error.message);
870
+ }
871
+ }
872
+
873
+ // Display IP information
874
+ async function displayIPInfo() {
875
+ try {
876
+ // Create IP display section
877
+ const ipSection = document.createElement('div');
878
+ ipSection.className = 'demo-section';
879
+ ipSection.style.cssText = `
880
+ background: rgba(255, 255, 255, 0.1);
881
+ border-radius: 15px;
882
+ padding: 30px;
883
+ margin: 20px 0;
884
+ backdrop-filter: blur(5px);
885
+ `;
886
+
887
+ ipSection.innerHTML = `
888
+ <h2>🌐 IP Information</h2>
889
+ <p>Client IP address detection (for server-side geolocation):</p>
890
+ <div id="ip-info" style="font-size: 12px; max-height: 200px; overflow-y: auto;">
891
+ <em>Detecting IP address...</em>
892
+ </div>
893
+ `;
894
+
895
+ // Insert after properties section
896
+ const contentDiv = document.getElementById('content');
897
+ if (contentDiv) {
898
+ const propertiesSection = contentDiv.querySelector('.demo-section');
899
+ if (propertiesSection) {
900
+ propertiesSection.insertAdjacentElement('afterend', ipSection);
901
+ } else {
902
+ contentDiv.insertBefore(ipSection, contentDiv.firstChild);
903
+ }
904
+ }
905
+
906
+ // Try to get IP info using the SDK
907
+ try {
908
+ if (typeof HumanBehaviorTracker !== 'undefined') {
909
+ // Wait a bit for IP detection to complete
910
+ setTimeout(async () => {
911
+ const ipInfoDiv = document.getElementById('ip-info');
912
+ if (ipInfoDiv) {
913
+ ipInfoDiv.innerHTML = `
914
+ <strong>IP Detection Status:</strong> IP address is being sent to server for geolocation<br>
915
+ <strong>Method:</strong> STUN servers + public IP services<br>
916
+ <strong>Privacy:</strong> No precise GPS coordinates, only IP-based location<br>
917
+ <strong>Server Processing:</strong> City, country, and region determined server-side<br>
918
+ <br>
919
+ <em>Note: Actual IP address and geo data are processed on the server for privacy and accuracy.</em>
920
+ `;
921
+ }
922
+ }, 2000);
923
+ }
924
+ } catch (error) {
925
+ const ipInfoDiv = document.getElementById('ip-info');
926
+ if (ipInfoDiv) {
927
+ ipInfoDiv.innerHTML = `<em>IP detection not available: ${error.message}</em>`;
928
+ }
929
+ }
930
+
931
+ addLog('IP information display initialized');
932
+
933
+ } catch (error) {
934
+ console.error('Error displaying IP info:', error);
935
+ addLog('Error displaying IP info: ' + error.message);
936
+ }
937
+ }
938
+
777
939
  // Add log message
778
940
  function addLog(message, data = null) {
779
941
  const logElements = document.querySelectorAll('.logs');
@@ -8,7 +8,7 @@ export class HumanBehaviorModule {
8
8
  logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
9
9
  redactFields?: string[];
10
10
  suppressConsoleErrors?: boolean;
11
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
11
+ recordCanvas?: boolean; // Enable canvas recording with protection
12
12
  }) {
13
13
  return {
14
14
  ngModule: HumanBehaviorModule,
@@ -44,7 +44,7 @@ export class HumanBehaviorService {
44
44
  logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
45
45
  redactFields?: string[];
46
46
  suppressConsoleErrors?: boolean;
47
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
47
+ recordCanvas?: boolean; // Enable canvas recording with protection
48
48
  }) {
49
49
  this.tracker = HumanBehaviorTracker.init(apiKey, options);
50
50
  }
@@ -73,7 +73,7 @@ export function initializeHumanBehavior(apiKey: string, options?: {
73
73
  logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
74
74
  redactFields?: string[];
75
75
  suppressConsoleErrors?: boolean;
76
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
76
+ recordCanvas?: boolean; // Enable canvas recording with protection
77
77
  }): HumanBehaviorTracker {
78
78
  return HumanBehaviorTracker.init(apiKey, options);
79
79
  }
package/src/api.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { logError, logInfo, logDebug, logWarn } from './utils/logger';
2
+ import { getCachedIP, IPInfo } from './utils/ip-detector';
2
3
 
3
4
  export const MAX_CHUNK_SIZE_BYTES = 1024 * 1024; // 1MB chunk size - more conservative
4
5
 
@@ -140,6 +141,45 @@ export class HumanBehaviorAPI {
140
141
  }
141
142
  }
142
143
 
144
+ /**
145
+ * Send IP address information to the server
146
+ * This is called after successful initialization
147
+ */
148
+ public async sendIPInfo(sessionId: string): Promise<void> {
149
+ try {
150
+ const ipInfo = await getCachedIP();
151
+ if (!ipInfo) {
152
+ logWarn('No IP address available to send');
153
+ return;
154
+ }
155
+
156
+ logDebug('Sending IP info:', ipInfo);
157
+
158
+ const response = await fetch(`${this.baseUrl}/api/ingestion/ip-info`, {
159
+ method: 'POST',
160
+ headers: {
161
+ 'Content-Type': 'application/json',
162
+ 'Authorization': `Bearer ${this.apiKey}`
163
+ },
164
+ body: JSON.stringify({
165
+ sessionId: sessionId,
166
+ clientIP: ipInfo.ip,
167
+ ipDetectionMethod: ipInfo.method,
168
+ timestamp: ipInfo.timestamp
169
+ })
170
+ });
171
+
172
+ if (!response.ok) {
173
+ const errorText = await response.text();
174
+ logWarn('Failed to send IP info:', response.status, errorText);
175
+ } else {
176
+ logDebug('IP info sent successfully');
177
+ }
178
+ } catch (error) {
179
+ logWarn('Error sending IP info:', error);
180
+ }
181
+ }
182
+
143
183
  async sendEvents(events: any[], sessionId: string, userId: string) {
144
184
  // ✅ SIMPLE VALIDATION FOR ALL EVENTS
145
185
  const validEvents = events.filter(event => event && typeof event === 'object');
package/src/index.ts CHANGED
@@ -13,6 +13,13 @@ export * from './api';
13
13
  // Export redaction functionality
14
14
  export * from './redact';
15
15
 
16
+ // Export property management functionality
17
+ export * from './utils/property-detector';
18
+ export * from './utils/property-manager';
19
+
20
+ // Export IP detection functionality
21
+ export * from './utils/ip-detector';
22
+
16
23
  // Export logger functionality
17
24
  export * from './utils/logger';
18
25
 
@@ -29,7 +29,7 @@ interface HumanBehaviorProviderProps {
29
29
  logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
30
30
  redactFields?: string[];
31
31
  suppressConsoleErrors?: boolean;
32
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
32
+ recordCanvas?: boolean; // Enable canvas recording with protection
33
33
  };
34
34
  }
35
35
 
@@ -262,7 +262,7 @@ export const useUserTracking = () => {
262
262
  };
263
263
  };
264
264
 
265
- // Automatic page tracking component (similar to PostHog's PostHogPageView)
265
+ // Automatic page tracking component
266
266
  function HumanBehaviorPageView() {
267
267
  const tracker = useContext(HumanBehaviorContext);
268
268
 
@@ -7,7 +7,7 @@ export const humanBehaviorStore = {
7
7
  logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
8
8
  redactFields?: string[];
9
9
  suppressConsoleErrors?: boolean;
10
- recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
10
+ recordCanvas?: boolean; // Enable canvas recording with protection
11
11
  }) => {
12
12
  return HumanBehaviorTracker.init(apiKey, options);
13
13
  }