humanbehavior-js 0.4.20 → 0.4.22
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/dist/cjs/angular/index.cjs +817 -19
- package/dist/cjs/angular/index.cjs.map +1 -1
- package/dist/cjs/index.cjs +833 -19
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/react/index.cjs +818 -20
- package/dist/cjs/react/index.cjs.map +1 -1
- package/dist/cjs/remix/index.cjs +818 -20
- package/dist/cjs/remix/index.cjs.map +1 -1
- package/dist/cjs/svelte/index.cjs +817 -19
- package/dist/cjs/svelte/index.cjs.map +1 -1
- package/dist/cjs/vue/index.cjs +817 -19
- package/dist/cjs/vue/index.cjs.map +1 -1
- package/dist/esm/angular/index.js +817 -19
- package/dist/esm/angular/index.js.map +1 -1
- package/dist/esm/index.js +825 -20
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/index.js +818 -20
- package/dist/esm/react/index.js.map +1 -1
- package/dist/esm/remix/index.js +818 -20
- package/dist/esm/remix/index.js.map +1 -1
- package/dist/esm/svelte/index.js +817 -19
- package/dist/esm/svelte/index.js.map +1 -1
- package/dist/esm/vue/index.js +817 -19
- package/dist/esm/vue/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/types/angular/index.d.ts +60 -1
- package/dist/types/index.d.ts +258 -3
- package/dist/types/react/index.d.ts +60 -1
- package/dist/types/remix/index.d.ts +60 -1
- package/dist/types/svelte/index.d.ts +60 -1
- package/package/canvas-recording-demo.html +1 -1
- package/package/simple-spa.html +1 -1
- package/package/src/angular/index.ts +3 -3
- package/package/src/react/index.tsx +2 -2
- package/package/src/svelte/index.ts +1 -1
- package/package/src/tracker.ts +2 -2
- package/package/src/vue/index.ts +1 -1
- package/package.json +1 -1
- package/simple-spa.html +164 -2
- package/src/angular/index.ts +3 -3
- package/src/api.ts +40 -0
- package/src/index.ts +7 -0
- package/src/react/index.tsx +2 -2
- package/src/svelte/index.ts +1 -1
- package/src/tracker.ts +193 -17
- package/src/utils/ip-detector.ts +158 -0
- package/src/utils/property-detector.ts +345 -0
- package/src/utils/property-manager.ts +274 -0
- package/src/vue/index.ts +1 -1
- package/canvas-recording-demo.html +0 -143
- package/clean-console-demo.html +0 -39
- 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
|
|
10
|
+
recordCanvas?: boolean; // Enable canvas recording with protection
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const HumanBehaviorPlugin = {
|