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