humanbehavior-js 0.4.28 → 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.
- package/README.md +151 -0
- package/package.json +114 -79
- package/packages/angular/dist/index.d.ts +46 -0
- package/packages/angular/dist/index.d.ts.map +1 -0
- package/packages/angular/dist/index.js +2 -0
- package/packages/angular/dist/index.js.map +1 -0
- package/packages/angular/dist/index.mjs +2 -0
- package/packages/angular/dist/index.mjs.map +1 -0
- package/packages/browser/dist/index.d.ts +5 -0
- package/packages/browser/dist/index.d.ts.map +1 -0
- package/packages/browser/dist/index.iife.js +12095 -0
- package/packages/browser/dist/index.iife.js.map +1 -0
- package/packages/browser/dist/index.js +2 -0
- package/packages/browser/dist/index.js.map +1 -0
- package/packages/browser/dist/index.min.js +2 -0
- package/packages/browser/dist/index.min.js.map +1 -0
- package/packages/browser/dist/index.mjs +2 -0
- package/packages/browser/dist/index.mjs.map +1 -0
- package/packages/react/dist/browser.d.ts +2 -0
- package/packages/react/dist/browser.d.ts.map +1 -0
- package/packages/react/dist/index.d.ts +48 -0
- package/packages/react/dist/index.d.ts.map +1 -0
- package/packages/react/dist/index.js +2 -0
- package/packages/react/dist/index.js.map +1 -0
- package/packages/react/dist/index.mjs +2 -0
- package/packages/react/dist/index.mjs.map +1 -0
- package/packages/remix/dist/index.d.ts +8 -0
- package/packages/remix/dist/index.d.ts.map +1 -0
- package/packages/remix/dist/index.js +2 -0
- package/packages/remix/dist/index.js.map +1 -0
- package/packages/remix/dist/index.mjs +2 -0
- package/packages/remix/dist/index.mjs.map +1 -0
- package/packages/svelte/dist/index.d.ts +11 -0
- package/packages/svelte/dist/index.d.ts.map +1 -0
- package/packages/svelte/dist/index.js +2 -0
- package/packages/svelte/dist/index.js.map +1 -0
- package/packages/svelte/dist/index.mjs +2 -0
- package/packages/svelte/dist/index.mjs.map +1 -0
- package/{dist/types/vue → packages/vue/dist}/index.d.ts +4 -5
- package/packages/vue/dist/index.d.ts.map +1 -0
- package/packages/vue/dist/index.js +2 -0
- package/packages/vue/dist/index.js.map +1 -0
- package/packages/vue/dist/index.mjs +2 -0
- package/packages/vue/dist/index.mjs.map +1 -0
- package/packages/wizard/dist/ai/ai-install-wizard.d.ts +145 -0
- package/packages/wizard/dist/ai/ai-install-wizard.d.ts.map +1 -0
- package/packages/wizard/dist/ai/manual-framework-wizard.d.ts +52 -0
- package/packages/wizard/dist/ai/manual-framework-wizard.d.ts.map +1 -0
- package/packages/wizard/dist/cli/ai-auto-install.d.ts +27 -0
- package/packages/wizard/dist/cli/ai-auto-install.d.ts.map +1 -0
- package/{dist → packages/wizard/dist}/cli/ai-auto-install.js +821 -905
- package/packages/wizard/dist/cli/ai-auto-install.js.map +1 -0
- package/packages/wizard/dist/cli/auto-install.d.ts +26 -0
- package/packages/wizard/dist/cli/auto-install.d.ts.map +1 -0
- package/{dist → packages/wizard/dist}/cli/auto-install.js +821 -905
- package/packages/wizard/dist/cli/auto-install.js.map +1 -0
- package/{dist/types → packages/wizard/dist/core}/install-wizard.d.ts +6 -8
- package/packages/wizard/dist/core/install-wizard.d.ts.map +1 -0
- package/packages/wizard/dist/index.d.ts +18 -0
- package/packages/wizard/dist/index.d.ts.map +1 -0
- package/packages/wizard/dist/index.js +2 -0
- package/packages/wizard/dist/index.js.map +1 -0
- package/packages/wizard/dist/index.mjs +2 -0
- package/packages/wizard/dist/index.mjs.map +1 -0
- package/packages/wizard/dist/services/centralized-ai-service.d.ts +159 -0
- package/packages/wizard/dist/services/centralized-ai-service.d.ts.map +1 -0
- package/packages/wizard/dist/services/remote-ai-service.d.ts +58 -0
- package/packages/wizard/dist/services/remote-ai-service.d.ts.map +1 -0
- package/WIZARD_USAGE_GUIDE.md +0 -381
- package/dist/cjs/angular/index.cjs +0 -14979
- package/dist/cjs/angular/index.cjs.map +0 -1
- package/dist/cjs/index.cjs +0 -14964
- package/dist/cjs/index.cjs.map +0 -1
- package/dist/cjs/install-wizard.cjs +0 -1576
- package/dist/cjs/install-wizard.cjs.map +0 -1
- package/dist/cjs/react/index.cjs +0 -15103
- package/dist/cjs/react/index.cjs.map +0 -1
- package/dist/cjs/remix/index.cjs +0 -15077
- package/dist/cjs/remix/index.cjs.map +0 -1
- package/dist/cjs/svelte/index.cjs +0 -14933
- package/dist/cjs/svelte/index.cjs.map +0 -1
- package/dist/cjs/vue/index.cjs +0 -14942
- package/dist/cjs/vue/index.cjs.map +0 -1
- package/dist/cjs/wizard/index.cjs +0 -3490
- package/dist/cjs/wizard/index.cjs.map +0 -1
- package/dist/cli/ai-auto-install.js.map +0 -1
- package/dist/cli/auto-install.js.map +0 -1
- package/dist/esm/angular/index.js +0 -14975
- package/dist/esm/angular/index.js.map +0 -1
- package/dist/esm/index.js +0 -14941
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/install-wizard.js +0 -1553
- package/dist/esm/install-wizard.js.map +0 -1
- package/dist/esm/react/index.js +0 -15097
- package/dist/esm/react/index.js.map +0 -1
- package/dist/esm/remix/index.js +0 -15073
- package/dist/esm/remix/index.js.map +0 -1
- package/dist/esm/svelte/index.js +0 -14931
- package/dist/esm/svelte/index.js.map +0 -1
- package/dist/esm/vue/index.js +0 -14940
- package/dist/esm/vue/index.js.map +0 -1
- package/dist/esm/wizard/index.js +0 -3459
- package/dist/esm/wizard/index.js.map +0 -1
- package/dist/index.min.js +0 -2
- package/dist/index.min.js.map +0 -1
- package/dist/types/angular/index.d.ts +0 -357
- package/dist/types/index.d.ts +0 -644
- package/dist/types/react/index.d.ts +0 -345
- package/dist/types/remix/index.d.ts +0 -336
- package/dist/types/svelte/index.d.ts +0 -322
- package/dist/types/wizard/index.d.ts +0 -523
- package/readme.md +0 -335
- package/rollup.config.js +0 -422
- package/simple-spa.html +0 -1000
- package/src/angular/index.ts +0 -79
- package/src/api.ts +0 -416
- package/src/index.ts +0 -35
- package/src/react/AutoInstallWizard.tsx +0 -557
- package/src/react/browser.ts +0 -8
- package/src/react/index.tsx +0 -308
- package/src/redact.ts +0 -327
- package/src/remix/index.ts +0 -16
- package/src/svelte/index.ts +0 -14
- package/src/tracker.ts +0 -1587
- package/src/types/clack.d.ts +0 -31
- package/src/utils/ip-detector.ts +0 -158
- package/src/utils/logger.ts +0 -144
- package/src/utils/property-detector.ts +0 -345
- package/src/utils/property-manager.ts +0 -274
- package/src/vue/index.ts +0 -29
- package/src/wizard/README.md +0 -114
- package/src/wizard/ai/ai-install-wizard.ts +0 -897
- package/src/wizard/ai/manual-framework-wizard.ts +0 -238
- package/src/wizard/cli/ai-auto-install.ts +0 -241
- package/src/wizard/cli/auto-install.ts +0 -224
- package/src/wizard/core/install-wizard.ts +0 -1794
- package/src/wizard/index.ts +0 -23
- package/src/wizard/services/centralized-ai-service.ts +0 -668
- package/src/wizard/services/remote-ai-service.ts +0 -240
- package/tsconfig.json +0 -24
package/src/types/clack.d.ts
DELETED
|
@@ -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
|
-
}
|
package/src/utils/ip-detector.ts
DELETED
|
@@ -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
|
-
}
|
package/src/utils/logger.ts
DELETED
|
@@ -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
|
-
}
|