humanbehavior-js 0.4.19 → 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 (66) 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/install-wizard.cjs +56 -10
  6. package/dist/cjs/install-wizard.cjs.map +1 -1
  7. package/dist/cjs/react/index.cjs +800 -14
  8. package/dist/cjs/react/index.cjs.map +1 -1
  9. package/dist/cjs/remix/index.cjs +800 -14
  10. package/dist/cjs/remix/index.cjs.map +1 -1
  11. package/dist/cjs/svelte/index.cjs +799 -13
  12. package/dist/cjs/svelte/index.cjs.map +1 -1
  13. package/dist/cjs/vue/index.cjs +799 -13
  14. package/dist/cjs/vue/index.cjs.map +1 -1
  15. package/dist/cjs/wizard/index.cjs +56 -10
  16. package/dist/cjs/wizard/index.cjs.map +1 -1
  17. package/dist/cli/ai-auto-install.js +56 -10
  18. package/dist/cli/ai-auto-install.js.map +1 -1
  19. package/dist/cli/auto-install.js +56 -10
  20. package/dist/cli/auto-install.js.map +1 -1
  21. package/dist/esm/angular/index.js +799 -13
  22. package/dist/esm/angular/index.js.map +1 -1
  23. package/dist/esm/index.js +807 -14
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/install-wizard.js +56 -10
  26. package/dist/esm/install-wizard.js.map +1 -1
  27. package/dist/esm/react/index.js +800 -14
  28. package/dist/esm/react/index.js.map +1 -1
  29. package/dist/esm/remix/index.js +800 -14
  30. package/dist/esm/remix/index.js.map +1 -1
  31. package/dist/esm/svelte/index.js +799 -13
  32. package/dist/esm/svelte/index.js.map +1 -1
  33. package/dist/esm/vue/index.js +799 -13
  34. package/dist/esm/vue/index.js.map +1 -1
  35. package/dist/esm/wizard/index.js +56 -10
  36. package/dist/esm/wizard/index.js.map +1 -1
  37. package/dist/index.min.js +1 -1
  38. package/dist/index.min.js.map +1 -1
  39. package/dist/types/angular/index.d.ts +60 -1
  40. package/dist/types/index.d.ts +258 -3
  41. package/dist/types/react/index.d.ts +60 -1
  42. package/dist/types/remix/index.d.ts +60 -1
  43. package/dist/types/svelte/index.d.ts +60 -1
  44. package/package/canvas-recording-demo.html +1 -1
  45. package/package/simple-spa.html +1 -1
  46. package/package/src/angular/index.ts +3 -3
  47. package/package/src/react/index.tsx +2 -2
  48. package/package/src/svelte/index.ts +1 -1
  49. package/package/src/tracker.ts +2 -2
  50. package/package/src/vue/index.ts +1 -1
  51. package/package.json +1 -1
  52. package/simple-spa.html +164 -2
  53. package/src/angular/index.ts +3 -3
  54. package/src/api.ts +40 -0
  55. package/src/index.ts +7 -0
  56. package/src/react/index.tsx +2 -2
  57. package/src/svelte/index.ts +1 -1
  58. package/src/tracker.ts +175 -11
  59. package/src/utils/ip-detector.ts +158 -0
  60. package/src/utils/property-detector.ts +345 -0
  61. package/src/utils/property-manager.ts +274 -0
  62. package/src/vue/index.ts +1 -1
  63. package/src/wizard/core/install-wizard.ts +60 -10
  64. package/canvas-recording-demo.html +0 -143
  65. package/clean-console-demo.html +0 -39
  66. package/simple-demo.html +0 -26
@@ -0,0 +1,345 @@
1
+ /**
2
+ * Automatic Property Detection for HumanBehavior SDK
3
+ * Captures device type, location, and initial referrer information
4
+ */
5
+
6
+ // Check if we're in a browser environment
7
+ const isBrowser = typeof window !== 'undefined';
8
+
9
+ export interface DeviceInfo {
10
+ device_type: string;
11
+ browser: string;
12
+ browser_version: string;
13
+ os: string;
14
+ os_version: string;
15
+ device_model?: string;
16
+ screen_resolution: string;
17
+ viewport_size: string;
18
+ color_depth: number;
19
+ timezone: string;
20
+ language: string;
21
+ languages: string[];
22
+ raw_user_agent?: string;
23
+ }
24
+
25
+ export interface LocationInfo {
26
+ current_url: string;
27
+ pathname: string;
28
+ search: string;
29
+ hash: string;
30
+ title: string;
31
+ referrer: string;
32
+ referrer_domain: string;
33
+ initial_referrer: string;
34
+ initial_referrer_domain: string;
35
+ initial_host?: string;
36
+ utm_source?: string;
37
+ utm_medium?: string;
38
+ utm_campaign?: string;
39
+ utm_term?: string;
40
+ utm_content?: string;
41
+ }
42
+
43
+ export interface AutomaticProperties extends DeviceInfo, LocationInfo {}
44
+
45
+ /**
46
+ * Detect device type based on user agent and screen size
47
+ */
48
+ function detectDeviceType(): string {
49
+ if (!isBrowser) return 'unknown';
50
+
51
+ const userAgent = navigator.userAgent.toLowerCase();
52
+ const screenWidth = window.screen.width;
53
+ const screenHeight = window.screen.height;
54
+
55
+ // Mobile detection
56
+ if (/mobile|android|iphone|ipad|ipod|blackberry|windows phone/i.test(userAgent)) {
57
+ if (/ipad/i.test(userAgent) || (screenWidth >= 768 && screenHeight >= 1024)) {
58
+ return 'tablet';
59
+ }
60
+ return 'mobile';
61
+ }
62
+
63
+ // Desktop detection
64
+ if (/windows|macintosh|linux/i.test(userAgent)) {
65
+ return 'desktop';
66
+ }
67
+
68
+ return 'unknown';
69
+ }
70
+
71
+ /**
72
+ * Extract browser information from user agent
73
+ */
74
+ function detectBrowser(): { browser: string; browser_version: string } {
75
+ if (!isBrowser) return { browser: 'unknown', browser_version: 'unknown' };
76
+
77
+ const userAgent = navigator.userAgent;
78
+
79
+ // Chrome
80
+ if (/chrome/i.test(userAgent) && !/edge/i.test(userAgent)) {
81
+ const match = userAgent.match(/chrome\/(\d+)/i);
82
+ return {
83
+ browser: 'chrome',
84
+ browser_version: match ? match[1] : 'unknown'
85
+ };
86
+ }
87
+
88
+ // Firefox
89
+ if (/firefox/i.test(userAgent)) {
90
+ const match = userAgent.match(/firefox\/(\d+)/i);
91
+ return {
92
+ browser: 'firefox',
93
+ browser_version: match ? match[1] : 'unknown'
94
+ };
95
+ }
96
+
97
+ // Safari
98
+ if (/safari/i.test(userAgent) && !/chrome/i.test(userAgent)) {
99
+ const match = userAgent.match(/version\/(\d+)/i);
100
+ return {
101
+ browser: 'safari',
102
+ browser_version: match ? match[1] : 'unknown'
103
+ };
104
+ }
105
+
106
+ // Edge
107
+ if (/edge/i.test(userAgent)) {
108
+ const match = userAgent.match(/edge\/(\d+)/i);
109
+ return {
110
+ browser: 'edge',
111
+ browser_version: match ? match[1] : 'unknown'
112
+ };
113
+ }
114
+
115
+ // Internet Explorer
116
+ if (/msie|trident/i.test(userAgent)) {
117
+ const match = userAgent.match(/msie (\d+)/i) || userAgent.match(/rv:(\d+)/i);
118
+ return {
119
+ browser: 'ie',
120
+ browser_version: match ? match[1] : 'unknown'
121
+ };
122
+ }
123
+
124
+ return { browser: 'unknown', browser_version: 'unknown' };
125
+ }
126
+
127
+ /**
128
+ * Extract operating system information from user agent
129
+ */
130
+ function detectOS(): { os: string; os_version: string } {
131
+ if (!isBrowser) return { os: 'unknown', os_version: 'unknown' };
132
+
133
+ const userAgent = navigator.userAgent;
134
+
135
+ // Windows
136
+ if (/windows/i.test(userAgent)) {
137
+ const match = userAgent.match(/windows nt (\d+\.\d+)/i);
138
+ let version = 'unknown';
139
+ if (match) {
140
+ const versionNum = parseFloat(match[1]);
141
+ if (versionNum === 10.0) version = '10';
142
+ else if (versionNum === 6.3) version = '8.1';
143
+ else if (versionNum === 6.2) version = '8';
144
+ else if (versionNum === 6.1) version = '7';
145
+ else version = match[1];
146
+ }
147
+ return { os: 'windows', os_version: version };
148
+ }
149
+
150
+ // macOS
151
+ if (/macintosh|mac os x/i.test(userAgent)) {
152
+ const match = userAgent.match(/mac os x (\d+[._]\d+)/i);
153
+ return {
154
+ os: 'macos',
155
+ os_version: match ? match[1].replace('_', '.') : 'unknown'
156
+ };
157
+ }
158
+
159
+ // iOS
160
+ if (/iphone|ipad|ipod/i.test(userAgent)) {
161
+ const match = userAgent.match(/os (\d+[._]\d+)/i);
162
+ return {
163
+ os: 'ios',
164
+ os_version: match ? match[1].replace('_', '.') : 'unknown'
165
+ };
166
+ }
167
+
168
+ // Android
169
+ if (/android/i.test(userAgent)) {
170
+ const match = userAgent.match(/android (\d+\.\d+)/i);
171
+ return {
172
+ os: 'android',
173
+ os_version: match ? match[1] : 'unknown'
174
+ };
175
+ }
176
+
177
+ // Linux
178
+ if (/linux/i.test(userAgent)) {
179
+ return { os: 'linux', os_version: 'unknown' };
180
+ }
181
+
182
+ return { os: 'unknown', os_version: 'unknown' };
183
+ }
184
+
185
+ /**
186
+ * Extract UTM parameters from URL
187
+ */
188
+ function extractUTMParams(url: string): Record<string, string> {
189
+ const urlObj = new URL(url);
190
+ const utmParams: Record<string, string> = {};
191
+
192
+ const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
193
+
194
+ utmKeys.forEach(key => {
195
+ const value = urlObj.searchParams.get(key);
196
+ if (value) {
197
+ utmParams[key] = value;
198
+ }
199
+ });
200
+
201
+ return utmParams;
202
+ }
203
+
204
+ /**
205
+ * Extract domain from URL
206
+ */
207
+ function extractDomain(url: string): string {
208
+ try {
209
+ const urlObj = new URL(url);
210
+ return urlObj.hostname;
211
+ } catch {
212
+ return '';
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Get device information
218
+ */
219
+ export function getDeviceInfo(): DeviceInfo {
220
+ if (!isBrowser) {
221
+ return {
222
+ device_type: 'unknown',
223
+ browser: 'unknown',
224
+ browser_version: 'unknown',
225
+ os: 'unknown',
226
+ os_version: 'unknown',
227
+ screen_resolution: 'unknown',
228
+ viewport_size: 'unknown',
229
+ color_depth: 0,
230
+ timezone: 'unknown',
231
+ language: 'unknown',
232
+ languages: []
233
+ };
234
+ }
235
+
236
+ const { browser, browser_version } = detectBrowser();
237
+ const { os, os_version } = detectOS();
238
+
239
+ return {
240
+ device_type: detectDeviceType(),
241
+ browser,
242
+ browser_version,
243
+ os,
244
+ os_version,
245
+ screen_resolution: `${window.screen.width}x${window.screen.height}`,
246
+ viewport_size: `${window.innerWidth}x${window.innerHeight}`,
247
+ color_depth: window.screen.colorDepth,
248
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
249
+ language: navigator.language,
250
+ languages: [...(navigator.languages || [navigator.language])],
251
+ raw_user_agent: navigator.userAgent
252
+ };
253
+ }
254
+
255
+ /**
256
+ * Get location information
257
+ */
258
+ export function getLocationInfo(): LocationInfo {
259
+ if (!isBrowser) {
260
+ return {
261
+ current_url: '',
262
+ pathname: '',
263
+ search: '',
264
+ hash: '',
265
+ title: '',
266
+ referrer: '',
267
+ referrer_domain: '',
268
+ initial_referrer: '',
269
+ initial_referrer_domain: ''
270
+ };
271
+ }
272
+
273
+ const currentUrl = window.location.href;
274
+ const referrer = document.referrer;
275
+ const utmParams = extractUTMParams(currentUrl);
276
+
277
+ return {
278
+ current_url: currentUrl,
279
+ pathname: window.location.pathname,
280
+ search: window.location.search,
281
+ hash: window.location.hash,
282
+ title: document.title,
283
+ referrer,
284
+ referrer_domain: extractDomain(referrer),
285
+ initial_referrer: referrer,
286
+ initial_referrer_domain: extractDomain(referrer),
287
+ initial_host: window.location.hostname,
288
+ ...utmParams
289
+ };
290
+ }
291
+
292
+ /**
293
+ * Get all automatic properties
294
+ */
295
+ export function getAutomaticProperties(): AutomaticProperties {
296
+ return {
297
+ ...getDeviceInfo(),
298
+ ...getLocationInfo()
299
+ };
300
+ }
301
+
302
+ /**
303
+ * Get initial properties that should be captured once per session
304
+ */
305
+ export function getInitialProperties(): Record<string, any> {
306
+ if (!isBrowser) return {};
307
+
308
+ const locationInfo = getLocationInfo();
309
+
310
+ return {
311
+ initial_referrer: locationInfo.initial_referrer,
312
+ initial_referrer_domain: locationInfo.initial_referrer_domain,
313
+ initial_url: locationInfo.current_url,
314
+ initial_pathname: locationInfo.pathname,
315
+ initial_utm_source: locationInfo.utm_source,
316
+ initial_utm_medium: locationInfo.utm_medium,
317
+ initial_utm_campaign: locationInfo.utm_campaign,
318
+ initial_utm_term: locationInfo.utm_term,
319
+ initial_utm_content: locationInfo.utm_content
320
+ };
321
+ }
322
+
323
+ /**
324
+ * Get current page properties (changes with navigation)
325
+ */
326
+ export function getCurrentPageProperties(): Record<string, any> {
327
+ if (!isBrowser) return {};
328
+
329
+ const locationInfo = getLocationInfo();
330
+
331
+ return {
332
+ current_url: locationInfo.current_url,
333
+ pathname: locationInfo.pathname,
334
+ search: locationInfo.search,
335
+ hash: locationInfo.hash,
336
+ title: locationInfo.title,
337
+ referrer: locationInfo.referrer,
338
+ referrer_domain: locationInfo.referrer_domain,
339
+ utm_source: locationInfo.utm_source,
340
+ utm_medium: locationInfo.utm_medium,
341
+ utm_campaign: locationInfo.utm_campaign,
342
+ utm_term: locationInfo.utm_term,
343
+ utm_content: locationInfo.utm_content
344
+ };
345
+ }
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Property Manager for HumanBehavior SDK
3
+ * Handles automatic properties, session properties, and user properties
4
+ */
5
+
6
+ import { getAutomaticProperties, getInitialProperties, getCurrentPageProperties, AutomaticProperties } from './property-detector';
7
+
8
+ export interface Properties {
9
+ [key: string]: any;
10
+ }
11
+
12
+ export interface PropertyManagerConfig {
13
+ enableAutomaticProperties?: boolean;
14
+ enableSessionProperties?: boolean;
15
+ enableUserProperties?: boolean;
16
+ propertyDenylist?: string[];
17
+ }
18
+
19
+ export class PropertyManager {
20
+ private config: PropertyManagerConfig;
21
+ private automaticProperties: AutomaticProperties;
22
+ private sessionProperties: Properties = {};
23
+ private userProperties: Properties = {};
24
+ private initialProperties: Properties = {};
25
+ private isInitialized: boolean = false;
26
+
27
+ constructor(config: PropertyManagerConfig = {}) {
28
+ this.config = {
29
+ enableAutomaticProperties: true,
30
+ enableSessionProperties: true,
31
+ enableUserProperties: true,
32
+ propertyDenylist: [],
33
+ ...config
34
+ };
35
+
36
+ this.automaticProperties = getAutomaticProperties();
37
+ this.initialize();
38
+ }
39
+
40
+ /**
41
+ * Initialize the property manager
42
+ */
43
+ private initialize(): void {
44
+ if (this.isInitialized) return;
45
+
46
+ // Capture initial properties once
47
+ this.initialProperties = getInitialProperties();
48
+
49
+ // Load session properties from sessionStorage
50
+ this.loadSessionProperties();
51
+
52
+ this.isInitialized = true;
53
+ }
54
+
55
+ /**
56
+ * Get all properties for an event
57
+ */
58
+ public getEventProperties(eventProperties: Properties = {}): Properties {
59
+ const properties: Properties = { ...eventProperties };
60
+
61
+ // Add automatic properties
62
+ if (this.config.enableAutomaticProperties) {
63
+ Object.assign(properties, this.getAutomaticProperties());
64
+ }
65
+
66
+ // Add session properties
67
+ if (this.config.enableSessionProperties) {
68
+ Object.assign(properties, this.sessionProperties);
69
+ }
70
+
71
+ // Add user properties
72
+ if (this.config.enableUserProperties) {
73
+ Object.assign(properties, this.userProperties);
74
+ }
75
+
76
+ // Add initial properties (only once per session)
77
+ if (!this.sessionProperties['$initial_properties_captured']) {
78
+ Object.assign(properties, this.initialProperties);
79
+ this.setSessionProperty('$initial_properties_captured', true);
80
+ }
81
+
82
+ // Apply denylist
83
+ this.applyDenylist(properties);
84
+
85
+ return properties;
86
+ }
87
+
88
+ /**
89
+ * Get automatic properties
90
+ */
91
+ public getAutomaticProperties(): Properties {
92
+ return {
93
+ ...this.automaticProperties,
94
+ ...getCurrentPageProperties() // Always get fresh page properties
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Get automatic properties with GeoIP data merged in
100
+ */
101
+ public getAutomaticPropertiesWithGeoIP(geoIPProperties: Record<string, any> = {}): Properties {
102
+ return {
103
+ ...this.automaticProperties,
104
+ ...getCurrentPageProperties(), // Always get fresh page properties
105
+ ...geoIPProperties
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Set a session property
111
+ */
112
+ public setSessionProperty(key: string, value: any): void {
113
+ this.sessionProperties[key] = value;
114
+ this.saveSessionProperties();
115
+ }
116
+
117
+ /**
118
+ * Set multiple session properties
119
+ */
120
+ public setSessionProperties(properties: Properties): void {
121
+ Object.assign(this.sessionProperties, properties);
122
+ this.saveSessionProperties();
123
+ }
124
+
125
+ /**
126
+ * Get a session property
127
+ */
128
+ public getSessionProperty(key: string): any {
129
+ return this.sessionProperties[key];
130
+ }
131
+
132
+ /**
133
+ * Remove a session property
134
+ */
135
+ public removeSessionProperty(key: string): void {
136
+ delete this.sessionProperties[key];
137
+ this.saveSessionProperties();
138
+ }
139
+
140
+ /**
141
+ * Set a user property
142
+ */
143
+ public setUserProperty(key: string, value: any): void {
144
+ this.userProperties[key] = value;
145
+ }
146
+
147
+ /**
148
+ * Set multiple user properties
149
+ */
150
+ public setUserProperties(properties: Properties): void {
151
+ Object.assign(this.userProperties, properties);
152
+ }
153
+
154
+ /**
155
+ * Get a user property
156
+ */
157
+ public getUserProperty(key: string): any {
158
+ return this.userProperties[key];
159
+ }
160
+
161
+ /**
162
+ * Remove a user property
163
+ */
164
+ public removeUserProperty(key: string): void {
165
+ delete this.userProperties[key];
166
+ }
167
+
168
+ /**
169
+ * Set a property only if it hasn't been set before
170
+ */
171
+ public setOnce(key: string, value: any, scope: 'session' | 'user' = 'user'): void {
172
+ if (scope === 'session') {
173
+ if (!(key in this.sessionProperties)) {
174
+ this.setSessionProperty(key, value);
175
+ }
176
+ } else {
177
+ if (!(key in this.userProperties)) {
178
+ this.setUserProperty(key, value);
179
+ }
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Clear all session properties
185
+ */
186
+ public clearSessionProperties(): void {
187
+ this.sessionProperties = {};
188
+ this.saveSessionProperties();
189
+ }
190
+
191
+ /**
192
+ * Clear all user properties
193
+ */
194
+ public clearUserProperties(): void {
195
+ this.userProperties = {};
196
+ }
197
+
198
+ /**
199
+ * Reset all properties
200
+ */
201
+ public reset(): void {
202
+ this.clearSessionProperties();
203
+ this.clearUserProperties();
204
+ this.initialProperties = {};
205
+ this.isInitialized = false;
206
+ this.initialize();
207
+ }
208
+
209
+ /**
210
+ * Load session properties from sessionStorage
211
+ */
212
+ private loadSessionProperties(): void {
213
+ if (typeof sessionStorage === 'undefined') return;
214
+
215
+ try {
216
+ const stored = sessionStorage.getItem('hb_session_properties');
217
+ if (stored) {
218
+ this.sessionProperties = JSON.parse(stored);
219
+ }
220
+ } catch (error) {
221
+ console.warn('Failed to load session properties:', error);
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Save session properties to sessionStorage
227
+ */
228
+ private saveSessionProperties(): void {
229
+ if (typeof sessionStorage === 'undefined') return;
230
+
231
+ try {
232
+ sessionStorage.setItem('hb_session_properties', JSON.stringify(this.sessionProperties));
233
+ } catch (error) {
234
+ console.warn('Failed to save session properties:', error);
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Apply property denylist
240
+ */
241
+ private applyDenylist(properties: Properties): void {
242
+ if (!this.config.propertyDenylist || this.config.propertyDenylist.length === 0) {
243
+ return;
244
+ }
245
+
246
+ this.config.propertyDenylist.forEach(deniedKey => {
247
+ delete properties[deniedKey];
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Update automatic properties (call when page changes)
253
+ */
254
+ public updateAutomaticProperties(): void {
255
+ this.automaticProperties = getAutomaticProperties();
256
+ }
257
+
258
+ /**
259
+ * Get all properties for debugging
260
+ */
261
+ public getAllProperties(): {
262
+ automatic: Properties;
263
+ session: Properties;
264
+ user: Properties;
265
+ initial: Properties;
266
+ } {
267
+ return {
268
+ automatic: this.getAutomaticProperties(),
269
+ session: { ...this.sessionProperties },
270
+ user: { ...this.userProperties },
271
+ initial: { ...this.initialProperties }
272
+ };
273
+ }
274
+ }
package/src/vue/index.ts CHANGED
@@ -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 = {