@v-tilt/browser 1.0.9 → 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.
@@ -1,63 +1,248 @@
1
1
  "use strict";
2
+ /**
3
+ * Event utilities
4
+ * Functions for extracting event properties, campaign parameters, and person info
5
+ */
2
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.COOKIE_CAMPAIGN_PARAMS = exports.MASKED = exports.EVENT_TO_PERSON_PROPERTIES = exports.CAMPAIGN_PARAMS = exports.PERSONAL_DATA_CAMPAIGN_PARAMS = void 0;
8
+ exports.getCampaignParams = getCampaignParams;
9
+ exports.getSearchInfo = getSearchInfo;
3
10
  exports.getBrowserLanguage = getBrowserLanguage;
4
11
  exports.getBrowserLanguagePrefix = getBrowserLanguagePrefix;
5
12
  exports.getReferrer = getReferrer;
6
13
  exports.getReferringDomain = getReferringDomain;
14
+ exports.getReferrerInfo = getReferrerInfo;
15
+ exports.getPersonInfo = getPersonInfo;
16
+ exports.getPersonPropsFromInfo = getPersonPropsFromInfo;
17
+ exports.getInitialPersonPropsFromInfo = getInitialPersonPropsFromInfo;
7
18
  exports.getTimezone = getTimezone;
8
19
  exports.getTimezoneOffset = getTimezoneOffset;
9
20
  exports.getEventProperties = getEventProperties;
21
+ const request_utils_1 = require("./request-utils");
22
+ const index_1 = require("./index");
23
+ const index_2 = require("./index");
10
24
  const globals_1 = require("./globals");
11
25
  const user_agent_utils_1 = require("./user-agent-utils");
26
+ const URL_REGEX_PREFIX = 'https?://(.*)';
12
27
  // Library version - should match package.json version
13
- const LIB_VERSION = "1.0.7"; // TODO: Auto-import from package.json
28
+ const LIB_VERSION = '1.0.7'; // TODO: Auto-import from package.json
29
+ // Campaign parameters that could be considered personal data (e.g., GDPR)
30
+ // These can be masked in URLs and properties before being sent
31
+ exports.PERSONAL_DATA_CAMPAIGN_PARAMS = [
32
+ 'gclid', // google ads
33
+ 'gclsrc', // google ads 360
34
+ 'dclid', // google display ads
35
+ 'gbraid', // google ads, web to app
36
+ 'wbraid', // google ads, app to web
37
+ 'fbclid', // facebook
38
+ 'msclkid', // microsoft
39
+ 'twclid', // twitter
40
+ 'li_fat_id', // linkedin
41
+ 'igshid', // instagram
42
+ 'ttclid', // tiktok
43
+ 'rdt_cid', // reddit
44
+ 'epik', // pinterest
45
+ 'qclid', // quora
46
+ 'sccid', // snapchat
47
+ 'irclid', // impact
48
+ '_kx', // klaviyo
49
+ ];
50
+ exports.CAMPAIGN_PARAMS = (0, index_2.extendArray)([
51
+ 'utm_source',
52
+ 'utm_medium',
53
+ 'utm_campaign',
54
+ 'utm_content',
55
+ 'utm_term',
56
+ 'gad_source', // google ads source
57
+ 'mc_cid', // mailchimp campaign id
58
+ ], exports.PERSONAL_DATA_CAMPAIGN_PARAMS);
59
+ // Properties that should be automatically copied from events to person properties
60
+ exports.EVENT_TO_PERSON_PROPERTIES = [
61
+ // Mobile app properties
62
+ '$app_build',
63
+ '$app_name',
64
+ '$app_namespace',
65
+ '$app_version',
66
+ // Web browser properties
67
+ '$browser',
68
+ '$browser_version',
69
+ '$device_type',
70
+ '$current_url',
71
+ '$pathname',
72
+ '$os',
73
+ '$os_name', // Special case: treated as alias of $os
74
+ '$os_version',
75
+ '$referring_domain',
76
+ '$referrer',
77
+ '$screen_height',
78
+ '$screen_width',
79
+ '$viewport_height',
80
+ '$viewport_width',
81
+ '$raw_user_agent',
82
+ ];
83
+ exports.MASKED = '<masked>';
84
+ // Campaign params that can be read from cookies (currently not implemented)
85
+ exports.COOKIE_CAMPAIGN_PARAMS = [
86
+ 'li_fat_id', // linkedin
87
+ ];
14
88
  /**
15
- * Get browser language
16
- * Returns the browser's language setting (e.g., "en-US")
89
+ * Get campaign parameters from URL
90
+ * Extracts UTM and other campaign tracking parameters from current page URL
91
+ * Masks personal data parameters if configured
17
92
  */
93
+ function getCampaignParams(customTrackedParams, maskPersonalDataProperties, customPersonalDataProperties) {
94
+ if (!globals_1.document) {
95
+ return {};
96
+ }
97
+ const paramsToMask = maskPersonalDataProperties
98
+ ? (0, index_2.extendArray)([], exports.PERSONAL_DATA_CAMPAIGN_PARAMS, customPersonalDataProperties || [])
99
+ : [];
100
+ // Initially get campaign params from the URL
101
+ const urlCampaignParams = _getCampaignParamsFromUrl((0, request_utils_1.maskQueryParams)(globals_1.document.URL, paramsToMask, exports.MASKED), customTrackedParams);
102
+ // But we can also get some of them from the cookie store
103
+ // For now, we'll skip cookie-based campaign params (would need cookie store implementation)
104
+ const cookieCampaignParams = {};
105
+ // Prefer the values found in the urlCampaignParams if possible
106
+ // `extend` will override the values if found in the second argument
107
+ return (0, index_2.extend)(cookieCampaignParams, urlCampaignParams);
108
+ }
109
+ function _getCampaignParamsFromUrl(url, customParams) {
110
+ const campaign_keywords = exports.CAMPAIGN_PARAMS.concat(customParams || []);
111
+ const params = {};
112
+ (0, index_2.each)(campaign_keywords, function (kwkey) {
113
+ const kw = (0, request_utils_1.getQueryParam)(url, kwkey);
114
+ params[kwkey] = kw ? kw : null;
115
+ });
116
+ return params;
117
+ }
118
+ function _getSearchEngine(referrer) {
119
+ if (!referrer) {
120
+ return null;
121
+ }
122
+ else {
123
+ if (referrer.search(URL_REGEX_PREFIX + 'google.([^/?]*)') === 0) {
124
+ return 'google';
125
+ }
126
+ else if (referrer.search(URL_REGEX_PREFIX + 'bing.com') === 0) {
127
+ return 'bing';
128
+ }
129
+ else if (referrer.search(URL_REGEX_PREFIX + 'yahoo.com') === 0) {
130
+ return 'yahoo';
131
+ }
132
+ else if (referrer.search(URL_REGEX_PREFIX + 'duckduckgo.com') === 0) {
133
+ return 'duckduckgo';
134
+ }
135
+ else {
136
+ return null;
137
+ }
138
+ }
139
+ }
140
+ function _getSearchInfoFromReferrer(referrer) {
141
+ const search = _getSearchEngine(referrer);
142
+ const param = search != 'yahoo' ? 'q' : 'p';
143
+ const ret = {};
144
+ if (!(0, index_1.isNull)(search)) {
145
+ ret['$search_engine'] = search;
146
+ const keyword = globals_1.document ? (0, request_utils_1.getQueryParam)(globals_1.document.referrer, param) : '';
147
+ if (keyword.length) {
148
+ ret['ph_keyword'] = keyword;
149
+ }
150
+ }
151
+ return ret;
152
+ }
153
+ function getSearchInfo() {
154
+ const referrer = globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.referrer;
155
+ if (!referrer) {
156
+ return {};
157
+ }
158
+ return _getSearchInfoFromReferrer(referrer);
159
+ }
18
160
  function getBrowserLanguage() {
19
- if (typeof globals_1.navigator === "undefined") {
161
+ if (typeof navigator === 'undefined') {
20
162
  return undefined;
21
163
  }
22
- return (globals_1.navigator.language || // Any modern browser
23
- globals_1.navigator.userLanguage // IE11
164
+ return (navigator.language || // Any modern browser
165
+ navigator.userLanguage // IE11
24
166
  );
25
167
  }
26
- /**
27
- * Get browser language prefix
28
- * Returns the language code without region (e.g., "en" from "en-US")
29
- */
30
168
  function getBrowserLanguagePrefix() {
31
169
  const lang = getBrowserLanguage();
32
- return typeof lang === "string" ? lang.split("-")[0] : undefined;
170
+ return typeof lang === 'string' ? lang.split('-')[0] : undefined;
33
171
  }
34
- /**
35
- * Get referrer
36
- * Returns document.referrer or '$direct' if no referrer
37
- */
38
172
  function getReferrer() {
39
- return (globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.referrer) || "$direct";
173
+ return (globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.referrer) || '$direct';
40
174
  }
41
- /**
42
- * Get referring domain
43
- * Returns the hostname of the referrer URL or '$direct' if no referrer
44
- */
45
175
  function getReferringDomain() {
176
+ var _a;
46
177
  if (!(globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.referrer)) {
47
- return "$direct";
178
+ return '$direct';
48
179
  }
49
- try {
50
- const url = new URL(globals_1.document.referrer);
51
- return url.host || "$direct";
180
+ return ((_a = (0, request_utils_1.convertToURL)(globals_1.document.referrer)) === null || _a === void 0 ? void 0 : _a.host) || '$direct';
181
+ }
182
+ /**
183
+ * Get referrer information
184
+ * Returns current referrer and referring domain
185
+ */
186
+ function getReferrerInfo() {
187
+ return {
188
+ $referrer: getReferrer(),
189
+ $referring_domain: getReferringDomain(),
190
+ };
191
+ }
192
+ /**
193
+ * Get person info for initial storage
194
+ * Extracts referrer and URL info, masks personal data if configured
195
+ * Returns compact format (r: referrer, u: url) for storage efficiency
196
+ */
197
+ function getPersonInfo(maskPersonalDataProperties, customPersonalDataProperties) {
198
+ const paramsToMask = maskPersonalDataProperties
199
+ ? (0, index_2.extendArray)([], exports.PERSONAL_DATA_CAMPAIGN_PARAMS, customPersonalDataProperties || [])
200
+ : [];
201
+ const url = globals_1.location === null || globals_1.location === void 0 ? void 0 : globals_1.location.href.substring(0, 1000);
202
+ // Compact format for storage efficiency (stored in localStorage)
203
+ return {
204
+ r: getReferrer().substring(0, 1000),
205
+ u: url ? (0, request_utils_1.maskQueryParams)(url, paramsToMask, exports.MASKED) : undefined,
206
+ };
207
+ }
208
+ /**
209
+ * Convert person info to person properties
210
+ * Extracts referrer, URL, campaign params, and search info from stored person info
211
+ */
212
+ function getPersonPropsFromInfo(info) {
213
+ var _a;
214
+ const { r: referrer, u: url } = info;
215
+ const referring_domain = referrer == null ? undefined : referrer == '$direct' ? '$direct' : (_a = (0, request_utils_1.convertToURL)(referrer)) === null || _a === void 0 ? void 0 : _a.host;
216
+ const props = {
217
+ $referrer: referrer,
218
+ $referring_domain: referring_domain,
219
+ };
220
+ if (url) {
221
+ props['$current_url'] = url;
222
+ const location = (0, request_utils_1.convertToURL)(url);
223
+ props['$host'] = location === null || location === void 0 ? void 0 : location.host;
224
+ props['$pathname'] = location === null || location === void 0 ? void 0 : location.pathname;
225
+ const campaignParams = _getCampaignParamsFromUrl(url);
226
+ (0, index_2.extend)(props, campaignParams);
52
227
  }
53
- catch (_a) {
54
- return "$direct";
228
+ if (referrer) {
229
+ const searchInfo = _getSearchInfoFromReferrer(referrer);
230
+ (0, index_2.extend)(props, searchInfo);
55
231
  }
232
+ return props;
56
233
  }
57
234
  /**
58
- * Get timezone
59
- * Returns the timezone (e.g., "America/New_York")
235
+ * Convert person info to initial person properties
236
+ * Generates $initial_* properties from person info (preserves first values)
60
237
  */
238
+ function getInitialPersonPropsFromInfo(info) {
239
+ const personProps = getPersonPropsFromInfo(info);
240
+ const props = {};
241
+ (0, index_2.each)(personProps, function (val, key) {
242
+ props[`$initial_${(0, index_1.stripLeadingDollar)(String(key))}`] = val;
243
+ });
244
+ return props;
245
+ }
61
246
  function getTimezone() {
62
247
  try {
63
248
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -66,10 +251,6 @@ function getTimezone() {
66
251
  return undefined;
67
252
  }
68
253
  }
69
- /**
70
- * Get timezone offset
71
- * Returns the timezone offset in minutes
72
- */
73
254
  function getTimezoneOffset() {
74
255
  try {
75
256
  return new Date().getTimezoneOffset();
@@ -78,100 +259,42 @@ function getTimezoneOffset() {
78
259
  return undefined;
79
260
  }
80
261
  }
81
- /**
82
- * Generate insert ID for deduplication
83
- */
84
- function generateInsertId() {
85
- return (Math.random().toString(36).substring(2, 10) +
86
- Math.random().toString(36).substring(2, 10));
87
- }
88
262
  /**
89
263
  * Get event properties that should be added to all events
264
+ * Returns all event context properties (browser, device, URL, etc.) plus event metadata
265
+ * Note: Only properties in EVENT_TO_PERSON_PROPERTIES are copied to person properties
90
266
  */
91
- function getEventProperties() {
92
- const props = {};
267
+ function getEventProperties(maskPersonalDataProperties, customPersonalDataProperties) {
93
268
  if (!globals_1.userAgent) {
94
- return props;
269
+ return {};
95
270
  }
96
- // Device/OS properties
271
+ const paramsToMask = maskPersonalDataProperties
272
+ ? (0, index_2.extendArray)([], exports.PERSONAL_DATA_CAMPAIGN_PARAMS, customPersonalDataProperties || [])
273
+ : [];
97
274
  const [os_name, os_version] = (0, user_agent_utils_1.detectOS)(globals_1.userAgent);
98
- if (os_name) {
99
- props.$os = os_name;
100
- }
101
- if (os_version) {
102
- props.$os_version = os_version;
103
- }
104
- const browser = (0, user_agent_utils_1.detectBrowser)(globals_1.userAgent, globals_1.navigator === null || globals_1.navigator === void 0 ? void 0 : globals_1.navigator.vendor);
105
- if (browser) {
106
- props.$browser = browser;
107
- }
108
- const browserVersion = (0, user_agent_utils_1.detectBrowserVersion)(globals_1.userAgent, globals_1.navigator === null || globals_1.navigator === void 0 ? void 0 : globals_1.navigator.vendor);
109
- if (browserVersion) {
110
- props.$browser_version = browserVersion;
111
- }
112
- const device = (0, user_agent_utils_1.detectDevice)(globals_1.userAgent);
113
- if (device) {
114
- props.$device = device;
115
- }
116
- const deviceType = (0, user_agent_utils_1.detectDeviceType)(globals_1.userAgent);
117
- if (deviceType) {
118
- props.$device_type = deviceType;
119
- }
120
- // Timezone properties
121
- const timezone = getTimezone();
122
- if (timezone) {
123
- props.$timezone = timezone;
124
- }
125
- const timezoneOffset = getTimezoneOffset();
126
- if (timezoneOffset !== undefined) {
127
- props.$timezone_offset = timezoneOffset;
128
- }
129
- // URL properties (added to all events)
130
- if (globals_1.location) {
131
- props.$current_url = globals_1.location.href;
132
- props.$host = globals_1.location.host;
133
- props.$pathname = globals_1.location.pathname;
134
- }
135
- // User agent
136
- if (globals_1.userAgent) {
137
- props.$raw_user_agent =
138
- globals_1.userAgent.length > 1000 ? globals_1.userAgent.substring(0, 997) + "..." : globals_1.userAgent;
139
- }
140
- // Browser language (added to all events)
141
- const browserLanguage = getBrowserLanguage();
142
- const browserLanguagePrefix = getBrowserLanguagePrefix();
143
- if (browserLanguage) {
144
- props.$browser_language = browserLanguage;
145
- }
146
- if (browserLanguagePrefix) {
147
- props.$browser_language_prefix = browserLanguagePrefix;
148
- }
149
- // Screen/viewport properties
150
- if (globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.screen) {
151
- if (globals_1.window.screen.height) {
152
- props.$screen_height = globals_1.window.screen.height;
153
- }
154
- if (globals_1.window.screen.width) {
155
- props.$screen_width = globals_1.window.screen.width;
156
- }
157
- }
158
- if (globals_1.window) {
159
- if (globals_1.window.innerHeight) {
160
- props.$viewport_height = globals_1.window.innerHeight;
161
- }
162
- if (globals_1.window.innerWidth) {
163
- props.$viewport_width = globals_1.window.innerWidth;
164
- }
165
- }
166
- // Library info
167
- props.$lib = "web";
168
- props.$lib_version = LIB_VERSION;
169
- // Insert ID for deduplication
170
- props.$insert_id = generateInsertId();
171
- // Timestamp (epoch time in seconds)
172
- props.$time = Date.now() / 1000;
173
- // Referrer properties (added to all events)
174
- props.$referrer = getReferrer();
175
- props.$referring_domain = getReferringDomain();
176
- return props;
275
+ return (0, index_2.extend)((0, index_2.stripEmptyProperties)({
276
+ $os: os_name,
277
+ $os_version: os_version,
278
+ $browser: (0, user_agent_utils_1.detectBrowser)(globals_1.userAgent, navigator.vendor),
279
+ $device: (0, user_agent_utils_1.detectDevice)(globals_1.userAgent),
280
+ $device_type: (0, user_agent_utils_1.detectDeviceType)(globals_1.userAgent),
281
+ $timezone: getTimezone(),
282
+ $timezone_offset: getTimezoneOffset(),
283
+ }), {
284
+ $current_url: (0, request_utils_1.maskQueryParams)(globals_1.location === null || globals_1.location === void 0 ? void 0 : globals_1.location.href, paramsToMask, exports.MASKED),
285
+ $host: globals_1.location === null || globals_1.location === void 0 ? void 0 : globals_1.location.host,
286
+ $pathname: globals_1.location === null || globals_1.location === void 0 ? void 0 : globals_1.location.pathname,
287
+ $raw_user_agent: globals_1.userAgent.length > 1000 ? globals_1.userAgent.substring(0, 997) + '...' : globals_1.userAgent,
288
+ $browser_version: (0, user_agent_utils_1.detectBrowserVersion)(globals_1.userAgent, navigator.vendor),
289
+ $browser_language: getBrowserLanguage(),
290
+ $browser_language_prefix: getBrowserLanguagePrefix(),
291
+ $screen_height: globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.screen.height,
292
+ $screen_width: globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.screen.width,
293
+ $viewport_height: globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.innerHeight,
294
+ $viewport_width: globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.innerWidth,
295
+ $lib: 'web',
296
+ $lib_version: LIB_VERSION,
297
+ $insert_id: Math.random().toString(36).substring(2, 10) + Math.random().toString(36).substring(2, 10),
298
+ $time: Date.now() / 1000, // epoch time in seconds
299
+ });
177
300
  }
@@ -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