humanbehavior-js 0.4.27 → 0.5.0

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 (140) hide show
  1. package/README.md +151 -0
  2. package/package.json +114 -71
  3. package/packages/angular/dist/index.d.ts +46 -0
  4. package/packages/angular/dist/index.d.ts.map +1 -0
  5. package/packages/angular/dist/index.js +2 -0
  6. package/packages/angular/dist/index.js.map +1 -0
  7. package/packages/angular/dist/index.mjs +2 -0
  8. package/packages/angular/dist/index.mjs.map +1 -0
  9. package/packages/browser/dist/index.d.ts +5 -0
  10. package/packages/browser/dist/index.d.ts.map +1 -0
  11. package/packages/browser/dist/index.iife.js +12095 -0
  12. package/packages/browser/dist/index.iife.js.map +1 -0
  13. package/packages/browser/dist/index.js +2 -0
  14. package/packages/browser/dist/index.js.map +1 -0
  15. package/packages/browser/dist/index.min.js +2 -0
  16. package/packages/browser/dist/index.min.js.map +1 -0
  17. package/packages/browser/dist/index.mjs +2 -0
  18. package/packages/browser/dist/index.mjs.map +1 -0
  19. package/packages/react/dist/browser.d.ts +2 -0
  20. package/packages/react/dist/browser.d.ts.map +1 -0
  21. package/packages/react/dist/index.d.ts +48 -0
  22. package/packages/react/dist/index.d.ts.map +1 -0
  23. package/packages/react/dist/index.js +2 -0
  24. package/packages/react/dist/index.js.map +1 -0
  25. package/packages/react/dist/index.mjs +2 -0
  26. package/packages/react/dist/index.mjs.map +1 -0
  27. package/packages/remix/dist/index.d.ts +8 -0
  28. package/packages/remix/dist/index.d.ts.map +1 -0
  29. package/packages/remix/dist/index.js +2 -0
  30. package/packages/remix/dist/index.js.map +1 -0
  31. package/packages/remix/dist/index.mjs +2 -0
  32. package/packages/remix/dist/index.mjs.map +1 -0
  33. package/packages/svelte/dist/index.d.ts +11 -0
  34. package/packages/svelte/dist/index.d.ts.map +1 -0
  35. package/packages/svelte/dist/index.js +2 -0
  36. package/packages/svelte/dist/index.js.map +1 -0
  37. package/packages/svelte/dist/index.mjs +2 -0
  38. package/packages/svelte/dist/index.mjs.map +1 -0
  39. package/{dist/types/vue → packages/vue/dist}/index.d.ts +4 -5
  40. package/packages/vue/dist/index.d.ts.map +1 -0
  41. package/packages/vue/dist/index.js +2 -0
  42. package/packages/vue/dist/index.js.map +1 -0
  43. package/packages/vue/dist/index.mjs +2 -0
  44. package/packages/vue/dist/index.mjs.map +1 -0
  45. package/packages/wizard/dist/ai/ai-install-wizard.d.ts +145 -0
  46. package/packages/wizard/dist/ai/ai-install-wizard.d.ts.map +1 -0
  47. package/packages/wizard/dist/ai/manual-framework-wizard.d.ts +52 -0
  48. package/packages/wizard/dist/ai/manual-framework-wizard.d.ts.map +1 -0
  49. package/packages/wizard/dist/cli/ai-auto-install.d.ts +27 -0
  50. package/packages/wizard/dist/cli/ai-auto-install.d.ts.map +1 -0
  51. package/{dist → packages/wizard/dist}/cli/ai-auto-install.js +821 -905
  52. package/packages/wizard/dist/cli/ai-auto-install.js.map +1 -0
  53. package/packages/wizard/dist/cli/auto-install.d.ts +26 -0
  54. package/packages/wizard/dist/cli/auto-install.d.ts.map +1 -0
  55. package/{dist → packages/wizard/dist}/cli/auto-install.js +821 -905
  56. package/packages/wizard/dist/cli/auto-install.js.map +1 -0
  57. package/{dist/types → packages/wizard/dist/core}/install-wizard.d.ts +6 -8
  58. package/packages/wizard/dist/core/install-wizard.d.ts.map +1 -0
  59. package/packages/wizard/dist/index.d.ts +18 -0
  60. package/packages/wizard/dist/index.d.ts.map +1 -0
  61. package/packages/wizard/dist/index.js +2 -0
  62. package/packages/wizard/dist/index.js.map +1 -0
  63. package/packages/wizard/dist/index.mjs +2 -0
  64. package/packages/wizard/dist/index.mjs.map +1 -0
  65. package/packages/wizard/dist/services/centralized-ai-service.d.ts +159 -0
  66. package/packages/wizard/dist/services/centralized-ai-service.d.ts.map +1 -0
  67. package/packages/wizard/dist/services/remote-ai-service.d.ts +58 -0
  68. package/packages/wizard/dist/services/remote-ai-service.d.ts.map +1 -0
  69. package/WIZARD_USAGE_GUIDE.md +0 -381
  70. package/dist/cjs/angular/index.cjs +0 -14979
  71. package/dist/cjs/angular/index.cjs.map +0 -1
  72. package/dist/cjs/index.cjs +0 -14964
  73. package/dist/cjs/index.cjs.map +0 -1
  74. package/dist/cjs/install-wizard.cjs +0 -1576
  75. package/dist/cjs/install-wizard.cjs.map +0 -1
  76. package/dist/cjs/react/index.cjs +0 -15103
  77. package/dist/cjs/react/index.cjs.map +0 -1
  78. package/dist/cjs/remix/index.cjs +0 -15077
  79. package/dist/cjs/remix/index.cjs.map +0 -1
  80. package/dist/cjs/svelte/index.cjs +0 -14933
  81. package/dist/cjs/svelte/index.cjs.map +0 -1
  82. package/dist/cjs/vue/index.cjs +0 -14942
  83. package/dist/cjs/vue/index.cjs.map +0 -1
  84. package/dist/cjs/wizard/index.cjs +0 -3490
  85. package/dist/cjs/wizard/index.cjs.map +0 -1
  86. package/dist/cli/ai-auto-install.js.map +0 -1
  87. package/dist/cli/auto-install.js.map +0 -1
  88. package/dist/esm/angular/index.js +0 -14975
  89. package/dist/esm/angular/index.js.map +0 -1
  90. package/dist/esm/index.js +0 -14941
  91. package/dist/esm/index.js.map +0 -1
  92. package/dist/esm/install-wizard.js +0 -1553
  93. package/dist/esm/install-wizard.js.map +0 -1
  94. package/dist/esm/react/index.js +0 -15097
  95. package/dist/esm/react/index.js.map +0 -1
  96. package/dist/esm/remix/index.js +0 -15073
  97. package/dist/esm/remix/index.js.map +0 -1
  98. package/dist/esm/svelte/index.js +0 -14931
  99. package/dist/esm/svelte/index.js.map +0 -1
  100. package/dist/esm/vue/index.js +0 -14940
  101. package/dist/esm/vue/index.js.map +0 -1
  102. package/dist/esm/wizard/index.js +0 -3459
  103. package/dist/esm/wizard/index.js.map +0 -1
  104. package/dist/index.min.js +0 -2
  105. package/dist/index.min.js.map +0 -1
  106. package/dist/types/angular/index.d.ts +0 -357
  107. package/dist/types/index.d.ts +0 -644
  108. package/dist/types/react/index.d.ts +0 -345
  109. package/dist/types/remix/index.d.ts +0 -336
  110. package/dist/types/svelte/index.d.ts +0 -322
  111. package/dist/types/wizard/index.d.ts +0 -523
  112. package/readme.md +0 -335
  113. package/rollup.config.js +0 -422
  114. package/simple-spa.html +0 -1000
  115. package/src/angular/index.ts +0 -79
  116. package/src/api.ts +0 -416
  117. package/src/index.ts +0 -35
  118. package/src/react/AutoInstallWizard.tsx +0 -557
  119. package/src/react/browser.ts +0 -8
  120. package/src/react/index.tsx +0 -308
  121. package/src/redact.ts +0 -327
  122. package/src/remix/index.ts +0 -16
  123. package/src/svelte/index.ts +0 -14
  124. package/src/tracker.ts +0 -1587
  125. package/src/types/clack.d.ts +0 -31
  126. package/src/utils/ip-detector.ts +0 -158
  127. package/src/utils/logger.ts +0 -144
  128. package/src/utils/property-detector.ts +0 -345
  129. package/src/utils/property-manager.ts +0 -274
  130. package/src/vue/index.ts +0 -29
  131. package/src/wizard/README.md +0 -114
  132. package/src/wizard/ai/ai-install-wizard.ts +0 -897
  133. package/src/wizard/ai/manual-framework-wizard.ts +0 -238
  134. package/src/wizard/cli/ai-auto-install.ts +0 -241
  135. package/src/wizard/cli/auto-install.ts +0 -224
  136. package/src/wizard/core/install-wizard.ts +0 -1794
  137. package/src/wizard/index.ts +0 -23
  138. package/src/wizard/services/centralized-ai-service.ts +0 -668
  139. package/src/wizard/services/remote-ai-service.ts +0 -240
  140. package/tsconfig.json +0 -24
@@ -1,31 +0,0 @@
1
- declare module '@clack/prompts' {
2
- export function intro(message: string): void;
3
- export function outro(message: string): void;
4
- export function cancel(message: string): void;
5
- export function note(message: string, title?: string): void;
6
-
7
- export function text(options: {
8
- message: string;
9
- placeholder?: string;
10
- defaultValue?: string;
11
- validate?: (value: string) => string | undefined;
12
- }): Promise<string>;
13
-
14
- export function select(options: {
15
- message: string;
16
- options: Array<{
17
- label: string;
18
- value: string;
19
- hint?: string;
20
- }>;
21
- }): Promise<string>;
22
-
23
- export function confirm(options: {
24
- message: string;
25
- }): Promise<boolean>;
26
-
27
- export function spinner(): {
28
- start(message: string): void;
29
- stop(message?: string): void;
30
- };
31
- }
@@ -1,158 +0,0 @@
1
- /**
2
- * IP Address Detection Utility
3
- * Attempts to get the client's public IP address using multiple methods
4
- */
5
-
6
- export interface IPInfo {
7
- ip: string;
8
- method: 'stun' | 'public-service' | 'fallback';
9
- timestamp: number;
10
- }
11
-
12
- /**
13
- * Get IP address using STUN server (most reliable)
14
- */
15
- async function getIPFromSTUN(): Promise<string | null> {
16
- try {
17
- const pc = new RTCPeerConnection({
18
- iceServers: [
19
- { urls: 'stun:stun.l.google.com:19302' },
20
- { urls: 'stun:stun1.l.google.com:19302' },
21
- { urls: 'stun:stun2.l.google.com:19302' }
22
- ]
23
- });
24
-
25
- return new Promise((resolve) => {
26
- const timeout = setTimeout(() => {
27
- pc.close();
28
- resolve(null);
29
- }, 5000);
30
-
31
- pc.createDataChannel('');
32
- pc.createOffer()
33
- .then(offer => pc.setLocalDescription(offer))
34
- .catch(() => resolve(null));
35
-
36
- pc.onicecandidate = (event) => {
37
- if (event.candidate) {
38
- const candidate = event.candidate.candidate;
39
- const match = candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3})/);
40
- if (match) {
41
- clearTimeout(timeout);
42
- pc.close();
43
- resolve(match[1]);
44
- }
45
- }
46
- };
47
- });
48
- } catch (error) {
49
- return null;
50
- }
51
- }
52
-
53
- /**
54
- * Get IP address using public IP service (fallback)
55
- */
56
- async function getIPFromPublicService(): Promise<string | null> {
57
- try {
58
- const response = await fetch('https://api.ipify.org?format=json', {
59
- method: 'GET'
60
- });
61
-
62
- if (response.ok) {
63
- const data = await response.json();
64
- return data.ip;
65
- }
66
- } catch (error) {
67
- // Try alternative service
68
- try {
69
- const response = await fetch('https://httpbin.org/ip', {
70
- method: 'GET'
71
- });
72
-
73
- if (response.ok) {
74
- const data = await response.json();
75
- return data.origin;
76
- }
77
- } catch (fallbackError) {
78
- // Last resort
79
- try {
80
- const response = await fetch('https://api.myip.com', {
81
- method: 'GET'
82
- });
83
-
84
- if (response.ok) {
85
- const data = await response.json();
86
- return data.ip;
87
- }
88
- } catch (lastError) {
89
- return null;
90
- }
91
- }
92
- }
93
-
94
- return null;
95
- }
96
-
97
- /**
98
- * Get client's public IP address
99
- * Tries STUN first (most reliable), then falls back to public services
100
- */
101
- export async function getClientIP(): Promise<IPInfo | null> {
102
- const startTime = Date.now();
103
-
104
- // Try STUN first (most reliable and privacy-friendly)
105
- const stunIP = await getIPFromSTUN();
106
- if (stunIP) {
107
- return {
108
- ip: stunIP,
109
- method: 'stun',
110
- timestamp: startTime
111
- };
112
- }
113
-
114
- // Fallback to public IP service
115
- const publicIP = await getIPFromPublicService();
116
- if (publicIP) {
117
- return {
118
- ip: publicIP,
119
- method: 'public-service',
120
- timestamp: startTime
121
- };
122
- }
123
-
124
- return null;
125
- }
126
-
127
- /**
128
- * Get IP address with caching to avoid repeated requests
129
- */
130
- let cachedIP: IPInfo | null = null;
131
- let cacheTimestamp = 0;
132
- const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
133
-
134
- export async function getCachedIP(): Promise<IPInfo | null> {
135
- const now = Date.now();
136
-
137
- // Return cached IP if still valid
138
- if (cachedIP && (now - cacheTimestamp) < CACHE_DURATION) {
139
- return cachedIP;
140
- }
141
-
142
- // Get fresh IP
143
- const ipInfo = await getClientIP();
144
- if (ipInfo) {
145
- cachedIP = ipInfo;
146
- cacheTimestamp = now;
147
- }
148
-
149
- return ipInfo;
150
- }
151
-
152
- /**
153
- * Clear IP cache (useful for testing or when network changes)
154
- */
155
- export function clearIPCache(): void {
156
- cachedIP = null;
157
- cacheTimestamp = 0;
158
- }
@@ -1,144 +0,0 @@
1
- export enum LogLevel {
2
- NONE = 0,
3
- ERROR = 1,
4
- WARN = 2,
5
- INFO = 3,
6
- DEBUG = 4
7
- }
8
-
9
- export interface LoggerConfig {
10
- level: LogLevel;
11
- enableConsole: boolean;
12
- enableStorage: boolean;
13
- }
14
-
15
- class Logger {
16
- private config: LoggerConfig = {
17
- level: LogLevel.ERROR, // Default to only errors in production
18
- enableConsole: true,
19
- enableStorage: false
20
- };
21
-
22
- private isBrowser = typeof window !== 'undefined';
23
-
24
- constructor(config?: Partial<LoggerConfig>) {
25
- if (config) {
26
- this.config = { ...this.config, ...config };
27
- }
28
- }
29
-
30
- setConfig(config: Partial<LoggerConfig>): void {
31
- this.config = { ...this.config, ...config };
32
- }
33
-
34
- private shouldLog(level: LogLevel): boolean {
35
- return level <= this.config.level;
36
- }
37
-
38
- private formatMessage(level: string, message: string, ...args: any[]): string {
39
- const timestamp = new Date().toISOString();
40
- return `[HumanBehavior ${level}] ${timestamp}: ${message}`;
41
- }
42
-
43
- error(message: string, ...args: any[]): void {
44
- if (!this.shouldLog(LogLevel.ERROR)) return;
45
-
46
- const formattedMessage = this.formatMessage('ERROR', message);
47
-
48
- if (this.config.enableConsole) {
49
- console.error(formattedMessage, ...args);
50
- }
51
-
52
- if (this.config.enableStorage && this.isBrowser) {
53
- this.logToStorage(formattedMessage, args);
54
- }
55
- }
56
-
57
- warn(message: string, ...args: any[]): void {
58
- if (!this.shouldLog(LogLevel.WARN)) return;
59
-
60
- const formattedMessage = this.formatMessage('WARN', message);
61
-
62
- if (this.config.enableConsole) {
63
- console.warn(formattedMessage, ...args);
64
- }
65
-
66
- if (this.config.enableStorage && this.isBrowser) {
67
- this.logToStorage(formattedMessage, args);
68
- }
69
- }
70
-
71
- info(message: string, ...args: any[]): void {
72
- if (!this.shouldLog(LogLevel.INFO)) return;
73
-
74
- const formattedMessage = this.formatMessage('INFO', message);
75
-
76
- if (this.config.enableConsole) {
77
- console.log(formattedMessage, ...args);
78
- }
79
-
80
- if (this.config.enableStorage && this.isBrowser) {
81
- this.logToStorage(formattedMessage, args);
82
- }
83
- }
84
-
85
- debug(message: string, ...args: any[]): void {
86
- if (!this.shouldLog(LogLevel.DEBUG)) return;
87
-
88
- const formattedMessage = this.formatMessage('DEBUG', message);
89
-
90
- if (this.config.enableConsole) {
91
- console.log(formattedMessage, ...args);
92
- }
93
-
94
- if (this.config.enableStorage && this.isBrowser) {
95
- this.logToStorage(formattedMessage, args);
96
- }
97
- }
98
-
99
- private logToStorage(message: string, args: any[]): void {
100
- try {
101
- const logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
102
- const logEntry = {
103
- message,
104
- args: args.length > 0 ? args : undefined,
105
- timestamp: Date.now()
106
- };
107
- logs.push(logEntry);
108
-
109
- // Keep only last 1000 logs to prevent storage bloat
110
- if (logs.length > 1000) {
111
- logs.splice(0, logs.length - 1000);
112
- }
113
-
114
- localStorage.setItem('human_behavior_logs', JSON.stringify(logs));
115
- } catch (e) {
116
- // Silently fail if storage is not available
117
- }
118
- }
119
-
120
- getLogs(): any[] {
121
- if (!this.isBrowser) return [];
122
-
123
- try {
124
- return JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
125
- } catch (e) {
126
- return [];
127
- }
128
- }
129
-
130
- clearLogs(): void {
131
- if (this.isBrowser) {
132
- localStorage.removeItem('human_behavior_logs');
133
- }
134
- }
135
- }
136
-
137
- // Create singleton instance
138
- export const logger = new Logger();
139
-
140
- // Export convenience methods
141
- export const logError = (message: string, ...args: any[]) => logger.error(message, ...args);
142
- export const logWarn = (message: string, ...args: any[]) => logger.warn(message, ...args);
143
- export const logInfo = (message: string, ...args: any[]) => logger.info(message, ...args);
144
- export const logDebug = (message: string, ...args: any[]) => logger.debug(message, ...args);
@@ -1,345 +0,0 @@
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
- }