@v-tilt/browser 1.0.10 → 1.0.11

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.
@@ -23,3 +23,24 @@ export declare function each<T>(obj: T[] | Record<string, T> | null | undefined,
23
23
  * This properly implements the default options for passive event listeners
24
24
  */
25
25
  export declare function addEventListener(element: Window | Document | Element | undefined, event: string, callback: EventListener, options?: AddEventListenerOptions): void;
26
+ /**
27
+ * Extend object with properties from another object
28
+ */
29
+ export declare function extend<T extends Record<string, any>>(target: T, source: Record<string, any> | null | undefined): T;
30
+ /**
31
+ * Extend array with additional items
32
+ * Mutates the base array and returns it (matches PostHog's behavior)
33
+ */
34
+ export declare function extendArray<T>(base: T[], ...additional: T[][]): T[];
35
+ /**
36
+ * Strip properties with empty values (null, undefined, empty string)
37
+ */
38
+ export declare function stripEmptyProperties<T extends Record<string, any>>(obj: T): Partial<T>;
39
+ /**
40
+ * Strip leading dollar sign from string
41
+ */
42
+ export declare function stripLeadingDollar(str: string): string;
43
+ /**
44
+ * Check if value is null
45
+ */
46
+ export declare function isNull(value: any): boolean;
@@ -6,6 +6,11 @@ exports.isValidPayload = isValidPayload;
6
6
  exports.isTestEnvironment = isTestEnvironment;
7
7
  exports.each = each;
8
8
  exports.addEventListener = addEventListener;
9
+ exports.extend = extend;
10
+ exports.extendArray = extendArray;
11
+ exports.stripEmptyProperties = stripEmptyProperties;
12
+ exports.stripLeadingDollar = stripLeadingDollar;
13
+ exports.isNull = isNull;
9
14
  /**
10
15
  * Generate uuid to identify the session. Random, not data-derived
11
16
  */
@@ -74,3 +79,56 @@ function addEventListener(element, event, callback, options) {
74
79
  const { capture = false, passive = true } = options !== null && options !== void 0 ? options : {};
75
80
  element === null || element === void 0 ? void 0 : element.addEventListener(event, callback, { capture, passive });
76
81
  }
82
+ /**
83
+ * Extend object with properties from another object
84
+ */
85
+ function extend(target, source) {
86
+ if (!source) {
87
+ return target;
88
+ }
89
+ for (const key in source) {
90
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
91
+ target[key] = source[key];
92
+ }
93
+ }
94
+ return target;
95
+ }
96
+ /**
97
+ * Extend array with additional items
98
+ * Mutates the base array and returns it (matches PostHog's behavior)
99
+ */
100
+ function extendArray(base, ...additional) {
101
+ for (const arr of additional) {
102
+ for (const item of arr) {
103
+ base.push(item);
104
+ }
105
+ }
106
+ return base;
107
+ }
108
+ /**
109
+ * Strip properties with empty values (null, undefined, empty string)
110
+ */
111
+ function stripEmptyProperties(obj) {
112
+ const result = {};
113
+ for (const key in obj) {
114
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
115
+ const value = obj[key];
116
+ if (value !== null && value !== undefined && value !== '') {
117
+ result[key] = value;
118
+ }
119
+ }
120
+ }
121
+ return result;
122
+ }
123
+ /**
124
+ * Strip leading dollar sign from string
125
+ */
126
+ function stripLeadingDollar(str) {
127
+ return str.startsWith('$') ? str.substring(1) : str;
128
+ }
129
+ /**
130
+ * Check if value is null
131
+ */
132
+ function isNull(value) {
133
+ return value === null;
134
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Request utilities
3
+ * Functions for parsing URLs, query parameters, and masking sensitive data
4
+ */
5
+ /**
6
+ * Convert string URL to HTMLAnchorElement for parsing
7
+ * IE11 doesn't support `new URL`, so we use anchor element
8
+ */
9
+ export declare function convertToURL(url: string): HTMLAnchorElement | null;
10
+ /**
11
+ * Get query parameter from URL
12
+ */
13
+ export declare function getQueryParam(url: string, param: string): string;
14
+ /**
15
+ * Mask query parameters in URL
16
+ */
17
+ export declare function maskQueryParams<T extends string | undefined>(url: T, maskedParams: string[] | undefined, mask: string): T extends string ? string : undefined;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ /**
3
+ * Request utilities
4
+ * Functions for parsing URLs, query parameters, and masking sensitive data
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.convertToURL = convertToURL;
8
+ exports.getQueryParam = getQueryParam;
9
+ exports.maskQueryParams = maskQueryParams;
10
+ const globals_1 = require("./globals");
11
+ /**
12
+ * Convert string URL to HTMLAnchorElement for parsing
13
+ * IE11 doesn't support `new URL`, so we use anchor element
14
+ */
15
+ function convertToURL(url) {
16
+ if (!globals_1.document) {
17
+ return null;
18
+ }
19
+ const anchor = globals_1.document.createElement('a');
20
+ anchor.href = url;
21
+ return anchor;
22
+ }
23
+ /**
24
+ * Get query parameter from URL
25
+ */
26
+ function getQueryParam(url, param) {
27
+ const withoutHash = url.split('#')[0] || '';
28
+ const queryParams = withoutHash.split(/\?(.*)/)[1] || '';
29
+ const cleanedQueryParams = queryParams.replace(/^\?+/g, '');
30
+ const queryParts = cleanedQueryParams.split('&');
31
+ for (let i = 0; i < queryParts.length; i++) {
32
+ const parts = queryParts[i].split('=');
33
+ if (parts[0] === param) {
34
+ if (parts.length < 2) {
35
+ return '';
36
+ }
37
+ let result = parts[1];
38
+ try {
39
+ result = decodeURIComponent(result);
40
+ }
41
+ catch (_a) {
42
+ // Skip decoding for malformed query param
43
+ }
44
+ return result.replace(/\+/g, ' ');
45
+ }
46
+ }
47
+ return '';
48
+ }
49
+ /**
50
+ * Mask query parameters in URL
51
+ */
52
+ function maskQueryParams(url, maskedParams, mask) {
53
+ if (!url || !maskedParams || !maskedParams.length) {
54
+ return url;
55
+ }
56
+ const splitHash = url.split('#');
57
+ const withoutHash = splitHash[0] || '';
58
+ const hash = splitHash[1];
59
+ const splitQuery = withoutHash.split('?');
60
+ const queryString = splitQuery[1];
61
+ const urlWithoutQueryAndHash = splitQuery[0];
62
+ const queryParts = (queryString || '').split('&');
63
+ const paramStrings = [];
64
+ for (let i = 0; i < queryParts.length; i++) {
65
+ const parts = queryParts[i].split('=');
66
+ const key = parts[0];
67
+ const value = parts.slice(1).join('=');
68
+ if (maskedParams.indexOf(key) !== -1) {
69
+ paramStrings.push(key + '=' + mask);
70
+ }
71
+ else if (key) {
72
+ paramStrings.push(key + (value ? '=' + value : ''));
73
+ }
74
+ }
75
+ const newQueryString = paramStrings.join('&');
76
+ const newUrl = urlWithoutQueryAndHash + (newQueryString ? '?' + newQueryString : '') + (hash ? '#' + hash : '');
77
+ return newUrl;
78
+ }
package/lib/vtilt.d.ts CHANGED
@@ -86,7 +86,8 @@ export declare class VTilt {
86
86
  /**
87
87
  * Capture an event
88
88
  * Automatically adds common properties to all events
89
- * ($current_url, $host, $pathname, $referrer, $referring_domain, $browser_language, etc.)
89
+ * ($current_url, $host, $pathname, $referrer, $referring_domain, $browser, $os, $device, $timezone, etc.)
90
+ * Only properties in EVENT_TO_PERSON_PROPERTIES are copied to person properties
90
91
  * Also adds title property for $pageview events only
91
92
  *
92
93
  * @param name - Event name
package/lib/vtilt.js CHANGED
@@ -93,6 +93,9 @@ class VTilt {
93
93
  name: name,
94
94
  });
95
95
  this.__loaded = true;
96
+ // Set initial person info: stores referrer and URL on first visit
97
+ const fullConfig = this.configManager.getConfig();
98
+ this.userManager.set_initial_person_info(fullConfig.mask_personal_data_properties, fullConfig.custom_personal_data_properties);
96
99
  // Initialize history autocapture
97
100
  this.historyAutocapture = new history_autocapture_1.HistoryAutocapture(this);
98
101
  this.historyAutocapture.startIfEnabled();
@@ -204,7 +207,8 @@ class VTilt {
204
207
  /**
205
208
  * Capture an event
206
209
  * Automatically adds common properties to all events
207
- * ($current_url, $host, $pathname, $referrer, $referring_domain, $browser_language, etc.)
210
+ * ($current_url, $host, $pathname, $referrer, $referring_domain, $browser, $os, $device, $timezone, etc.)
211
+ * Only properties in EVENT_TO_PERSON_PROPERTIES are copied to person properties
208
212
  * Also adds title property for $pageview events only
209
213
  *
210
214
  * @param name - Event name
@@ -221,11 +225,14 @@ class VTilt {
221
225
  }
222
226
  const url = this.buildUrl();
223
227
  // Add properties to all events
224
- // This includes: $current_url, $host, $pathname, $referrer, $referring_domain, $browser_language, etc.
228
+ // This includes: $current_url, $host, $pathname, $referrer, $referring_domain, $browser, $os, $device, $timezone, etc.
229
+ // (Only properties in EVENT_TO_PERSON_PROPERTIES are copied to person properties)
225
230
  const eventProperties = (0, event_utils_1.getEventProperties)();
226
231
  // Get person properties (includes $device_id and other user properties)
227
232
  // These are automatically included in all events
228
233
  const personProperties = this.userManager.getUserProperties();
234
+ // Get initial props: $initial_* properties from stored initial person info
235
+ const initialProps = this.userManager.get_initial_props();
229
236
  // Get session and window IDs
230
237
  // Both methods ensure IDs always exist (generate if needed)
231
238
  const session_id = this.sessionManager.getSessionId();
@@ -234,6 +241,17 @@ class VTilt {
234
241
  // This allows linking events with different distinct_ids that share the same anonymous_id
235
242
  // This is especially important for handling race conditions when $identify events arrive
236
243
  const anonymousId = this.userManager.getAnonymousId();
244
+ // Build $set and $set_once from initial props
245
+ // Initial props are added to $set_once (only first time, preserves first values)
246
+ // Regular event properties that match EVENT_TO_PERSON_PROPERTIES will be added to $set by server
247
+ const setOnce = {};
248
+ if (Object.keys(initialProps).length > 0) {
249
+ Object.assign(setOnce, initialProps);
250
+ }
251
+ // Merge with user-provided $set_once if present
252
+ if (payload.$set_once) {
253
+ Object.assign(setOnce, payload.$set_once);
254
+ }
237
255
  const enrichedPayload = {
238
256
  ...eventProperties, // Base properties for all events
239
257
  ...personProperties, // Person properties (includes $device_id)
@@ -242,6 +260,10 @@ class VTilt {
242
260
  // Always include $anon_distinct_id for identity linking (even for identified users)
243
261
  // This allows the server to merge identities proactively when events arrive out of order
244
262
  ...(anonymousId ? { $anon_distinct_id: anonymousId } : {}),
263
+ // Add $set_once with initial props
264
+ ...(Object.keys(setOnce).length > 0 ? { $set_once: setOnce } : {}),
265
+ // Merge user-provided $set if present
266
+ ...(payload.$set ? { $set: payload.$set } : {}),
245
267
  ...payload, // User-provided payload (can override base and person properties)
246
268
  };
247
269
  // Add title only to $pageview events
package/package.json CHANGED
@@ -1,61 +1,61 @@
1
- {
2
- "name": "@v-tilt/browser",
3
- "version": "1.0.10",
4
- "description": "vTilt browser tracking library",
5
- "main": "dist/main.js",
6
- "module": "dist/module.js",
7
- "types": "dist/module.d.ts",
8
- "files": [
9
- "lib/*",
10
- "dist/*"
11
- ],
12
- "publishConfig": {
13
- "access": "public"
14
- },
15
- "scripts": {
16
- "build": "tsc -b && rollup -c",
17
- "dev": "rollup -c -w",
18
- "type-check": "tsc --noEmit",
19
- "lint": "eslint src --ext .ts",
20
- "lint:fix": "eslint src --ext .ts --fix",
21
- "clean": "rimraf lib dist",
22
- "prepublishOnly": "pnpm run build"
23
- },
24
- "keywords": [
25
- "analytics",
26
- "tracking",
27
- "web-vitals",
28
- "performance"
29
- ],
30
- "author": "vTilt",
31
- "license": "MIT",
32
- "devDependencies": {
33
- "@babel/preset-env": "^7.28.3",
34
- "@rollup/plugin-babel": "^6.0.4",
35
- "@rollup/plugin-commonjs": "^25.0.8",
36
- "@rollup/plugin-json": "^6.1.0",
37
- "@rollup/plugin-node-resolve": "^15.3.1",
38
- "@rollup/plugin-terser": "^0.4.4",
39
- "@rollup/plugin-typescript": "^11.1.6",
40
- "@types/node": "^20.10.5",
41
- "@v-tilt/eslint-config": "workspace:*",
42
- "eslint": "^9.0.0",
43
- "rimraf": "^5.0.5",
44
- "rollup": "^4.9.1",
45
- "rollup-plugin-dts": "^6.2.3",
46
- "rollup-plugin-terser": "^7.0.2",
47
- "rollup-plugin-visualizer": "^6.0.3",
48
- "typescript": "^5.3.3"
49
- },
50
- "dependencies": {
51
- "web-vitals": "^3.5.0"
52
- },
53
- "peerDependencies": {
54
- "web-vitals": "^3.0.0"
55
- },
56
- "peerDependenciesMeta": {
57
- "web-vitals": {
58
- "optional": true
59
- }
60
- }
61
- }
1
+ {
2
+ "name": "@v-tilt/browser",
3
+ "version": "1.0.11",
4
+ "description": "vTilt browser tracking library",
5
+ "main": "dist/main.js",
6
+ "module": "dist/module.js",
7
+ "types": "dist/module.d.ts",
8
+ "files": [
9
+ "lib/*",
10
+ "dist/*"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc -b && rollup -c",
17
+ "dev": "rollup -c -w",
18
+ "type-check": "tsc --noEmit",
19
+ "lint": "eslint src --ext .ts",
20
+ "lint:fix": "eslint src --ext .ts --fix",
21
+ "clean": "rimraf lib dist",
22
+ "prepublishOnly": "pnpm run build"
23
+ },
24
+ "keywords": [
25
+ "analytics",
26
+ "tracking",
27
+ "web-vitals",
28
+ "performance"
29
+ ],
30
+ "author": "vTilt",
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "@babel/preset-env": "^7.28.3",
34
+ "@rollup/plugin-babel": "^6.0.4",
35
+ "@rollup/plugin-commonjs": "^25.0.8",
36
+ "@rollup/plugin-json": "^6.1.0",
37
+ "@rollup/plugin-node-resolve": "^15.3.1",
38
+ "@rollup/plugin-terser": "^0.4.4",
39
+ "@rollup/plugin-typescript": "^11.1.6",
40
+ "@types/node": "^20.10.5",
41
+ "@v-tilt/eslint-config": "workspace:*",
42
+ "eslint": "^9.0.0",
43
+ "rimraf": "^5.0.5",
44
+ "rollup": "^4.9.1",
45
+ "rollup-plugin-dts": "^6.2.3",
46
+ "rollup-plugin-terser": "^7.0.2",
47
+ "rollup-plugin-visualizer": "^6.0.3",
48
+ "typescript": "^5.3.3"
49
+ },
50
+ "dependencies": {
51
+ "web-vitals": "^3.5.0"
52
+ },
53
+ "peerDependencies": {
54
+ "web-vitals": "^3.0.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "web-vitals": {
58
+ "optional": true
59
+ }
60
+ }
61
+ }