@statsig/web-analytics 3.17.2 → 3.18.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/package.json +3 -3
- package/src/AutoCapture.d.ts +3 -5
- package/src/AutoCapture.js +49 -58
- package/src/AutoCaptureEvent.d.ts +1 -0
- package/src/AutoCaptureEvent.js +1 -0
- package/src/EngagementManager.d.ts +23 -0
- package/src/EngagementManager.js +73 -0
- package/src/RageClickManager.d.ts +4 -0
- package/src/RageClickManager.js +27 -0
- package/src/{Utils.d.ts → commonUtils.d.ts} +4 -5
- package/src/{Utils.js → commonUtils.js} +23 -44
- package/src/eventUtils.d.ts +5 -0
- package/src/eventUtils.js +178 -0
- package/src/metadataUtils.d.ts +3 -0
- package/src/metadataUtils.js +124 -0
- package/src/payloadUtils.d.ts +0 -1
- package/src/payloadUtils.js +0 -73
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statsig/web-analytics",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"homepage": "https://github.com/statsig-io/js-client-monorepo",
|
|
6
6
|
"repository": {
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"directory": "packages/web-analytics"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@statsig/client-core": "3.
|
|
13
|
-
"@statsig/js-client": "3.
|
|
12
|
+
"@statsig/client-core": "3.18.0",
|
|
13
|
+
"@statsig/js-client": "3.18.0"
|
|
14
14
|
},
|
|
15
15
|
"jsdelivr": "./build/statsig-web-analytics.min.js",
|
|
16
16
|
"type": "commonjs",
|
package/src/AutoCapture.d.ts
CHANGED
|
@@ -13,26 +13,24 @@ export declare function runStatsigAutoCapture(client: PrecomputedEvaluationsInte
|
|
|
13
13
|
export declare class AutoCapture {
|
|
14
14
|
private _client;
|
|
15
15
|
private _errorBoundary;
|
|
16
|
-
private _startTime;
|
|
17
|
-
private _deepestScroll;
|
|
18
16
|
private _disabledEvents;
|
|
19
17
|
private _previousLoggedPageViewUrl;
|
|
20
18
|
private _eventFilterFunc?;
|
|
21
19
|
private _hasLoggedPageViewEnd;
|
|
22
|
-
private
|
|
20
|
+
private _engagementManager;
|
|
21
|
+
private _rageClickManager;
|
|
23
22
|
constructor(_client: PrecomputedEvaluationsInterface, options?: AutoCaptureOptions);
|
|
24
23
|
private _addEventHandlers;
|
|
25
24
|
private _addPageViewTracking;
|
|
26
25
|
private _autoLogEvent;
|
|
27
|
-
private _bumpInactiveTimer;
|
|
28
26
|
private _initialize;
|
|
29
27
|
private _logError;
|
|
30
28
|
private _logSessionStart;
|
|
31
29
|
private _tryLogPageView;
|
|
32
30
|
private _tryLogPageViewEnd;
|
|
31
|
+
private _logRageClick;
|
|
33
32
|
private _logPerformance;
|
|
34
33
|
private _enqueueAutoCapture;
|
|
35
|
-
private _scrollEventHandler;
|
|
36
34
|
private _isNewSession;
|
|
37
35
|
private _getSessionFromClient;
|
|
38
36
|
}
|
package/src/AutoCapture.js
CHANGED
|
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.AutoCapture = exports.runStatsigAutoCapture = exports.StatsigAutoCapturePlugin = void 0;
|
|
4
4
|
const client_core_1 = require("@statsig/client-core");
|
|
5
5
|
const AutoCaptureEvent_1 = require("./AutoCaptureEvent");
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
const EngagementManager_1 = require("./EngagementManager");
|
|
7
|
+
const RageClickManager_1 = require("./RageClickManager");
|
|
8
|
+
const commonUtils_1 = require("./commonUtils");
|
|
9
|
+
const eventUtils_1 = require("./eventUtils");
|
|
10
|
+
const metadataUtils_1 = require("./metadataUtils");
|
|
9
11
|
const AUTO_EVENT_MAPPING = {
|
|
10
12
|
submit: AutoCaptureEvent_1.AutoCaptureEventName.FORM_SUBMIT,
|
|
11
13
|
click: AutoCaptureEvent_1.AutoCaptureEventName.CLICK,
|
|
@@ -28,12 +30,9 @@ class AutoCapture {
|
|
|
28
30
|
constructor(_client, options) {
|
|
29
31
|
var _a, _b, _c;
|
|
30
32
|
this._client = _client;
|
|
31
|
-
this._startTime = Date.now();
|
|
32
|
-
this._deepestScroll = 0;
|
|
33
33
|
this._disabledEvents = {};
|
|
34
34
|
this._previousLoggedPageViewUrl = null;
|
|
35
35
|
this._hasLoggedPageViewEnd = false;
|
|
36
|
-
this._inactiveTimer = null;
|
|
37
36
|
const { sdkKey, errorBoundary, values } = _client.getContext();
|
|
38
37
|
this._disabledEvents = (_b = (_a = values === null || values === void 0 ? void 0 : values.auto_capture_settings) === null || _a === void 0 ? void 0 : _a.disabled_events) !== null && _b !== void 0 ? _b : {};
|
|
39
38
|
this._errorBoundary = errorBoundary;
|
|
@@ -44,6 +43,8 @@ class AutoCapture {
|
|
|
44
43
|
this._disabledEvents =
|
|
45
44
|
(_b = (_a = values === null || values === void 0 ? void 0 : values.auto_capture_settings) === null || _a === void 0 ? void 0 : _a.disabled_events) !== null && _b !== void 0 ? _b : this._disabledEvents;
|
|
46
45
|
});
|
|
46
|
+
this._engagementManager = new EngagementManager_1.EngagementManager();
|
|
47
|
+
this._rageClickManager = new RageClickManager_1.default();
|
|
47
48
|
this._eventFilterFunc = options === null || options === void 0 ? void 0 : options.eventFilterFunc;
|
|
48
49
|
const doc = (0, client_core_1._getDocumentSafe)();
|
|
49
50
|
if (!(0, client_core_1._isServerEnv)()) {
|
|
@@ -65,17 +66,24 @@ class AutoCapture {
|
|
|
65
66
|
return;
|
|
66
67
|
}
|
|
67
68
|
const eventHandler = (event, userAction = true) => {
|
|
68
|
-
|
|
69
|
+
var _a;
|
|
70
|
+
const e = event || ((_a = (0, client_core_1._getWindowSafe)()) === null || _a === void 0 ? void 0 : _a.event);
|
|
71
|
+
this._autoLogEvent(e);
|
|
69
72
|
if (userAction) {
|
|
70
|
-
this.
|
|
73
|
+
this._engagementManager.bumpInactiveTimer();
|
|
74
|
+
}
|
|
75
|
+
if (e.type === 'click' && e instanceof MouseEvent) {
|
|
76
|
+
const isRageClick = this._rageClickManager.isRageClick(e.clientX, e.clientY, Date.now());
|
|
77
|
+
if (isRageClick) {
|
|
78
|
+
this._logRageClick(e);
|
|
79
|
+
}
|
|
71
80
|
}
|
|
72
81
|
};
|
|
73
|
-
(0,
|
|
74
|
-
(0,
|
|
75
|
-
(0,
|
|
76
|
-
(0,
|
|
77
|
-
(0,
|
|
78
|
-
(0, Utils_1._registerEventHandler)(win, 'scroll', () => this._scrollEventHandler());
|
|
82
|
+
(0, commonUtils_1._registerEventHandler)(doc, 'click', (e) => eventHandler(e));
|
|
83
|
+
(0, commonUtils_1._registerEventHandler)(doc, 'submit', (e) => eventHandler(e));
|
|
84
|
+
(0, commonUtils_1._registerEventHandler)(win, 'error', (e) => eventHandler(e, false));
|
|
85
|
+
(0, commonUtils_1._registerEventHandler)(win, 'pagehide', () => this._tryLogPageViewEnd());
|
|
86
|
+
(0, commonUtils_1._registerEventHandler)(win, 'beforeunload', () => this._tryLogPageViewEnd());
|
|
79
87
|
}
|
|
80
88
|
_addPageViewTracking() {
|
|
81
89
|
const win = (0, client_core_1._getWindowSafe)();
|
|
@@ -83,7 +91,7 @@ class AutoCapture {
|
|
|
83
91
|
if (!win || !doc) {
|
|
84
92
|
return;
|
|
85
93
|
}
|
|
86
|
-
(0,
|
|
94
|
+
(0, commonUtils_1._registerEventHandler)(win, 'popstate', () => this._tryLogPageView());
|
|
87
95
|
window.history.pushState = new Proxy(window.history.pushState, {
|
|
88
96
|
apply: (target, thisArg, [state, unused, url]) => {
|
|
89
97
|
target.apply(thisArg, [state, unused, url]);
|
|
@@ -99,33 +107,23 @@ class AutoCapture {
|
|
|
99
107
|
this._logError(event);
|
|
100
108
|
return;
|
|
101
109
|
}
|
|
102
|
-
const target = (0,
|
|
110
|
+
const target = (0, commonUtils_1._getTargetNode)(event);
|
|
103
111
|
if (!target) {
|
|
104
112
|
return;
|
|
105
113
|
}
|
|
106
|
-
if (!(0,
|
|
114
|
+
if (!(0, commonUtils_1._shouldLogEvent)(event, target)) {
|
|
107
115
|
return;
|
|
108
116
|
}
|
|
109
117
|
const eventName = AUTO_EVENT_MAPPING[eventType];
|
|
110
118
|
if (!eventName) {
|
|
111
119
|
return;
|
|
112
120
|
}
|
|
113
|
-
const { value, metadata } = (0,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
_bumpInactiveTimer() {
|
|
117
|
-
const win = (0, client_core_1._getWindowSafe)();
|
|
118
|
-
if (!win) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (this._inactiveTimer) {
|
|
122
|
-
clearTimeout(this._inactiveTimer);
|
|
123
|
-
}
|
|
124
|
-
this._inactiveTimer = win.setTimeout(() => {
|
|
125
|
-
this._tryLogPageViewEnd(true);
|
|
126
|
-
}, PAGE_INACTIVE_TIMEOUT);
|
|
121
|
+
const { value, metadata } = (0, eventUtils_1._gatherEventData)(target);
|
|
122
|
+
const allMetadata = (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)());
|
|
123
|
+
this._enqueueAutoCapture(eventName, value, Object.assign(Object.assign({}, allMetadata), metadata));
|
|
127
124
|
}
|
|
128
125
|
_initialize() {
|
|
126
|
+
this._engagementManager.startInactivityTracking(() => this._tryLogPageViewEnd(true));
|
|
129
127
|
this._addEventHandlers();
|
|
130
128
|
this._addPageViewTracking();
|
|
131
129
|
this._logSessionStart();
|
|
@@ -159,40 +157,49 @@ class AutoCapture {
|
|
|
159
157
|
if (!this._isNewSession(session)) {
|
|
160
158
|
return;
|
|
161
159
|
}
|
|
162
|
-
this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.SESSION_START, (0,
|
|
160
|
+
this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.SESSION_START, (0, commonUtils_1._getSanitizedPageUrl)(), { sessionID: session.data.sessionID }, { flushImmediately: true });
|
|
163
161
|
}
|
|
164
162
|
catch (err) {
|
|
165
163
|
this._errorBoundary.logError('AC::logSession', err);
|
|
166
164
|
}
|
|
167
165
|
}
|
|
168
166
|
_tryLogPageView() {
|
|
169
|
-
const url = (0,
|
|
167
|
+
const url = (0, commonUtils_1._getSafeUrl)();
|
|
170
168
|
const last = this._previousLoggedPageViewUrl;
|
|
171
169
|
if (last && url.href === last.href) {
|
|
172
170
|
return;
|
|
173
171
|
}
|
|
172
|
+
this._engagementManager.setLastPageViewTime(Date.now());
|
|
174
173
|
this._previousLoggedPageViewUrl = url;
|
|
175
174
|
this._hasLoggedPageViewEnd = false;
|
|
176
|
-
const payload = (0,
|
|
177
|
-
this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.PAGE_VIEW, (0,
|
|
175
|
+
const payload = (0, metadataUtils_1._gatherAllMetadata)(url);
|
|
176
|
+
this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.PAGE_VIEW, (0, commonUtils_1._getSanitizedPageUrl)(), payload, {
|
|
178
177
|
flushImmediately: true,
|
|
179
178
|
addNewSessionMetadata: true,
|
|
180
179
|
});
|
|
181
|
-
this.
|
|
180
|
+
this._engagementManager.bumpInactiveTimer();
|
|
182
181
|
}
|
|
183
182
|
_tryLogPageViewEnd(dueToInactivity = false) {
|
|
184
183
|
if (this._hasLoggedPageViewEnd) {
|
|
185
184
|
return;
|
|
186
185
|
}
|
|
187
186
|
this._hasLoggedPageViewEnd = true;
|
|
188
|
-
this.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
dueToInactivity,
|
|
192
|
-
|
|
187
|
+
const scrollMetrics = this._engagementManager.getScrollMetrics();
|
|
188
|
+
const pageViewLength = this._engagementManager.getPageViewLength();
|
|
189
|
+
this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.PAGE_VIEW_END, (0, commonUtils_1._getSanitizedPageUrl)(), Object.assign(Object.assign({}, scrollMetrics), { pageViewLength,
|
|
190
|
+
dueToInactivity }), {
|
|
191
|
+
flushImmediately: true,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
_logRageClick(e) {
|
|
195
|
+
const { value, metadata } = (0, eventUtils_1._gatherEventData)(e.target);
|
|
196
|
+
this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.RAGE_CLICK, value, Object.assign(Object.assign({ x: e.clientX, y: e.clientY, timestamp: Date.now() }, (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)())), metadata));
|
|
193
197
|
}
|
|
194
198
|
_logPerformance() {
|
|
195
199
|
const win = (0, client_core_1._getWindowSafe)();
|
|
200
|
+
if (!win || !win.performance) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
196
203
|
if (typeof (win === null || win === void 0 ? void 0 : win.performance) === 'undefined' ||
|
|
197
204
|
typeof win.performance.getEntriesByType !== 'function' ||
|
|
198
205
|
typeof win.performance.getEntriesByName !== 'function') {
|
|
@@ -217,14 +224,7 @@ class AutoCapture {
|
|
|
217
224
|
fpEntries[0] instanceof PerformancePaintTiming) {
|
|
218
225
|
metadata['first_contentful_paint_time_ms'] = fpEntries[0].startTime;
|
|
219
226
|
}
|
|
220
|
-
|
|
221
|
-
if (networkInfo) {
|
|
222
|
-
metadata['effective_connection_type'] = networkInfo.effectiveType;
|
|
223
|
-
metadata['rtt_ms'] = networkInfo.rtt;
|
|
224
|
-
metadata['downlink_kbps'] = networkInfo.downlink;
|
|
225
|
-
metadata['save_data'] = networkInfo.saveData;
|
|
226
|
-
}
|
|
227
|
-
this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.PERFORMANCE, (0, Utils_1._getSanitizedPageUrl)(), metadata);
|
|
227
|
+
this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.PERFORMANCE, (0, commonUtils_1._getSanitizedPageUrl)(), Object.assign(Object.assign({}, metadata), (0, metadataUtils_1._getNetworkInfo)()));
|
|
228
228
|
}, 1);
|
|
229
229
|
}
|
|
230
230
|
_enqueueAutoCapture(eventName, value, metadata, options) {
|
|
@@ -258,15 +258,6 @@ class AutoCapture {
|
|
|
258
258
|
this._errorBoundary.logError('AC::enqueue', err);
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
|
-
_scrollEventHandler() {
|
|
262
|
-
var _a, _b, _c, _d;
|
|
263
|
-
const scrollHeight = (_b = (_a = (0, client_core_1._getDocumentSafe)()) === null || _a === void 0 ? void 0 : _a.body.scrollHeight) !== null && _b !== void 0 ? _b : 1;
|
|
264
|
-
const win = (0, client_core_1._getWindowSafe)();
|
|
265
|
-
const scrollY = (_c = win === null || win === void 0 ? void 0 : win.scrollY) !== null && _c !== void 0 ? _c : 1;
|
|
266
|
-
const innerHeight = (_d = win === null || win === void 0 ? void 0 : win.innerHeight) !== null && _d !== void 0 ? _d : 1;
|
|
267
|
-
this._deepestScroll = Math.max(this._deepestScroll, Math.min(100, Math.round(((scrollY + innerHeight) / scrollHeight) * 100)));
|
|
268
|
-
this._bumpInactiveTimer();
|
|
269
|
-
}
|
|
270
261
|
_isNewSession(session) {
|
|
271
262
|
// within the last second
|
|
272
263
|
return Math.abs(session.data.startTime - Date.now()) < 1000;
|
|
@@ -7,6 +7,7 @@ export declare const AutoCaptureEventName: {
|
|
|
7
7
|
readonly PERFORMANCE: "auto_capture::performance";
|
|
8
8
|
readonly FORM_SUBMIT: "auto_capture::form_submit";
|
|
9
9
|
readonly CLICK: "auto_capture::click";
|
|
10
|
+
readonly RAGE_CLICK: "auto_capture::rage_click";
|
|
10
11
|
};
|
|
11
12
|
export type AutoCaptureEventName = (typeof AutoCaptureEventName)[keyof typeof AutoCaptureEventName] & string;
|
|
12
13
|
export type AutoCaptureEvent = StatsigEvent & {
|
package/src/AutoCaptureEvent.js
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare class EngagementManager {
|
|
2
|
+
private _lastScrollY;
|
|
3
|
+
private _maxScrollY;
|
|
4
|
+
private _lastScrollPercentage;
|
|
5
|
+
private _maxScrollPercentage;
|
|
6
|
+
private _lastPageViewTime;
|
|
7
|
+
private _inactiveTimer;
|
|
8
|
+
private _onInactivityCallback;
|
|
9
|
+
constructor();
|
|
10
|
+
private _initializeScrollTracking;
|
|
11
|
+
private _handleScroll;
|
|
12
|
+
getScrollMetrics(): {
|
|
13
|
+
lastScrollY: number;
|
|
14
|
+
maxScrollY: number;
|
|
15
|
+
lastScrollPercentage: number;
|
|
16
|
+
maxScrollPercentage: number;
|
|
17
|
+
scrollDepth: number;
|
|
18
|
+
};
|
|
19
|
+
getPageViewLength(): number;
|
|
20
|
+
setLastPageViewTime(time: number): void;
|
|
21
|
+
startInactivityTracking(callback: () => void): void;
|
|
22
|
+
bumpInactiveTimer(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EngagementManager = void 0;
|
|
4
|
+
const client_core_1 = require("@statsig/client-core");
|
|
5
|
+
const PAGE_INACTIVE_TIMEOUT = 600000; // 10 minutes
|
|
6
|
+
class EngagementManager {
|
|
7
|
+
constructor() {
|
|
8
|
+
this._lastScrollY = 0;
|
|
9
|
+
this._maxScrollY = 0;
|
|
10
|
+
this._lastScrollPercentage = 0;
|
|
11
|
+
this._maxScrollPercentage = 0;
|
|
12
|
+
this._lastPageViewTime = Date.now();
|
|
13
|
+
this._inactiveTimer = null;
|
|
14
|
+
this._onInactivityCallback = null;
|
|
15
|
+
this._initializeScrollTracking();
|
|
16
|
+
}
|
|
17
|
+
_initializeScrollTracking() {
|
|
18
|
+
const win = (0, client_core_1._getWindowSafe)();
|
|
19
|
+
if (!win)
|
|
20
|
+
return;
|
|
21
|
+
win.addEventListener('scroll', () => this._handleScroll());
|
|
22
|
+
win.addEventListener('scrollend', () => this._handleScroll());
|
|
23
|
+
win.addEventListener('resize', () => this._handleScroll());
|
|
24
|
+
}
|
|
25
|
+
_handleScroll() {
|
|
26
|
+
const win = (0, client_core_1._getWindowSafe)();
|
|
27
|
+
const doc = (0, client_core_1._getDocumentSafe)();
|
|
28
|
+
if (!win || !doc)
|
|
29
|
+
return;
|
|
30
|
+
const scrollHeight = doc.body.scrollHeight;
|
|
31
|
+
const scrollY = win.scrollY || doc.documentElement.scrollTop || 0;
|
|
32
|
+
const innerHeight = win.innerHeight;
|
|
33
|
+
this._lastScrollY = scrollY;
|
|
34
|
+
this._maxScrollY = Math.max(this._maxScrollY, scrollY);
|
|
35
|
+
const currentScrollPercentage = Math.min(100, Math.round(((scrollY + innerHeight) / scrollHeight) * 100));
|
|
36
|
+
this._lastScrollPercentage = currentScrollPercentage;
|
|
37
|
+
this._maxScrollPercentage = Math.max(this._maxScrollPercentage, currentScrollPercentage);
|
|
38
|
+
this.bumpInactiveTimer();
|
|
39
|
+
}
|
|
40
|
+
getScrollMetrics() {
|
|
41
|
+
return {
|
|
42
|
+
lastScrollY: this._lastScrollY,
|
|
43
|
+
maxScrollY: this._maxScrollY,
|
|
44
|
+
lastScrollPercentage: this._lastScrollPercentage,
|
|
45
|
+
maxScrollPercentage: this._maxScrollPercentage,
|
|
46
|
+
scrollDepth: this._maxScrollPercentage, // deprecated
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
getPageViewLength() {
|
|
50
|
+
return Date.now() - this._lastPageViewTime;
|
|
51
|
+
}
|
|
52
|
+
setLastPageViewTime(time) {
|
|
53
|
+
this._lastPageViewTime = time;
|
|
54
|
+
}
|
|
55
|
+
startInactivityTracking(callback) {
|
|
56
|
+
this._onInactivityCallback = callback;
|
|
57
|
+
}
|
|
58
|
+
bumpInactiveTimer() {
|
|
59
|
+
const win = (0, client_core_1._getWindowSafe)();
|
|
60
|
+
if (!win) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (this._inactiveTimer) {
|
|
64
|
+
clearTimeout(this._inactiveTimer);
|
|
65
|
+
}
|
|
66
|
+
this._inactiveTimer = win.setTimeout(() => {
|
|
67
|
+
if (this._onInactivityCallback) {
|
|
68
|
+
this._onInactivityCallback();
|
|
69
|
+
}
|
|
70
|
+
}, PAGE_INACTIVE_TIMEOUT);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.EngagementManager = EngagementManager;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const RAGE_CLICK_THRESHOLD_PX = 30;
|
|
4
|
+
const RAGE_CLICK_TIMEOUT_MS = 1000;
|
|
5
|
+
const RAGE_CLICK_CLICK_COUNT = 3;
|
|
6
|
+
class RageClickManager {
|
|
7
|
+
constructor() {
|
|
8
|
+
this._clicks = [];
|
|
9
|
+
}
|
|
10
|
+
isRageClick(x, y, timestamp) {
|
|
11
|
+
// Remove clicks outside the timeout window
|
|
12
|
+
this._clicks = this._clicks.filter((click) => timestamp - click.timestamp < RAGE_CLICK_TIMEOUT_MS);
|
|
13
|
+
const isCloseEnough = (click) => {
|
|
14
|
+
const dx = x - click.x;
|
|
15
|
+
const dy = y - click.y;
|
|
16
|
+
return Math.abs(dx) + Math.abs(dy) <= RAGE_CLICK_THRESHOLD_PX;
|
|
17
|
+
};
|
|
18
|
+
// If previous clicks exist, check spatial threshold
|
|
19
|
+
if (this._clicks.length > 0 &&
|
|
20
|
+
!isCloseEnough(this._clicks[this._clicks.length - 1])) {
|
|
21
|
+
this._clicks = [];
|
|
22
|
+
}
|
|
23
|
+
this._clicks.push({ x, y, timestamp });
|
|
24
|
+
return this._clicks.length >= RAGE_CLICK_CLICK_COUNT;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.default = RageClickManager;
|
|
@@ -4,15 +4,14 @@ interface NetworkInformation {
|
|
|
4
4
|
rtt: number;
|
|
5
5
|
saveData: boolean;
|
|
6
6
|
}
|
|
7
|
-
export declare function
|
|
8
|
-
export declare function _gatherEventData(target: Element): {
|
|
9
|
-
value: string;
|
|
10
|
-
metadata: Record<string, string | null>;
|
|
11
|
-
};
|
|
7
|
+
export declare function _stripEmptyValues<T extends Record<string, string | number | null | undefined>>(obj: T): Partial<Record<keyof T, string | number>>;
|
|
12
8
|
export declare function _getTargetNode(e: Event): Element | null;
|
|
13
9
|
export declare function _shouldLogEvent(e: Event, el: Element): boolean;
|
|
14
10
|
export declare function _getSafeUrl(): URL;
|
|
15
11
|
export declare function _getSanitizedPageUrl(): string;
|
|
16
12
|
export declare function _registerEventHandler(element: Document | Window, eventType: string, handler: (event: Event) => void): void;
|
|
17
13
|
export declare function _getSafeNetworkInformation(): NetworkInformation | null;
|
|
14
|
+
export declare function _getSafeTimezone(): string | null;
|
|
15
|
+
export declare function _getSafeTimezoneOffset(): number | null;
|
|
16
|
+
export declare function _getAnchorNodeInHierarchy(node: Element | null): Element | null;
|
|
18
17
|
export {};
|
|
@@ -1,51 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._getSafeNetworkInformation = exports._registerEventHandler = exports._getSanitizedPageUrl = exports._getSafeUrl = exports._shouldLogEvent = exports._getTargetNode = exports.
|
|
3
|
+
exports._getAnchorNodeInHierarchy = exports._getSafeTimezoneOffset = exports._getSafeTimezone = exports._getSafeNetworkInformation = exports._registerEventHandler = exports._getSanitizedPageUrl = exports._getSafeUrl = exports._shouldLogEvent = exports._getTargetNode = exports._stripEmptyValues = void 0;
|
|
4
4
|
const client_core_1 = require("@statsig/client-core");
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
if (!el) {
|
|
8
|
-
return dataset;
|
|
9
|
-
}
|
|
10
|
-
const attr = el === null || el === void 0 ? void 0 : el.dataset;
|
|
11
|
-
if (!attr) {
|
|
12
|
-
return dataset;
|
|
13
|
-
}
|
|
14
|
-
for (const key in attr) {
|
|
15
|
-
dataset[`data-${key}`] = attr[key] || '';
|
|
16
|
-
}
|
|
17
|
-
return dataset;
|
|
18
|
-
}
|
|
19
|
-
exports._gatherDatasetProperties = _gatherDatasetProperties;
|
|
20
|
-
function _gatherEventData(target) {
|
|
21
|
-
var _a;
|
|
22
|
-
const tagName = target.tagName.toLowerCase();
|
|
23
|
-
const metadata = {};
|
|
24
|
-
const value = (0, client_core_1._getCurrentPageUrlSafe)() || '';
|
|
25
|
-
metadata['tagName'] = tagName;
|
|
26
|
-
if (tagName === 'form') {
|
|
27
|
-
metadata['action'] = target.getAttribute('action');
|
|
28
|
-
metadata['method'] = (_a = target.getAttribute('method')) !== null && _a !== void 0 ? _a : 'GET';
|
|
29
|
-
metadata['formName'] = target.getAttribute('name');
|
|
30
|
-
metadata['formId'] = target.getAttribute('id');
|
|
31
|
-
}
|
|
32
|
-
if (['input', 'select', 'textarea'].includes(tagName) &&
|
|
33
|
-
target.getAttribute('type') !== 'password') {
|
|
34
|
-
metadata['content'] = target.value;
|
|
35
|
-
metadata['inputName'] = target.getAttribute('name');
|
|
36
|
-
}
|
|
37
|
-
const anchor = _getAnchorNodeInHierarchy(target);
|
|
38
|
-
if (anchor) {
|
|
39
|
-
metadata['href'] = anchor.getAttribute('href');
|
|
40
|
-
}
|
|
41
|
-
if (tagName === 'button' || anchor) {
|
|
42
|
-
metadata['content'] = (target.textContent || '').trim();
|
|
43
|
-
const dataset = _gatherDatasetProperties(anchor || target);
|
|
44
|
-
Object.assign(metadata, dataset);
|
|
45
|
-
}
|
|
46
|
-
return { value, metadata };
|
|
5
|
+
function _stripEmptyValues(obj) {
|
|
6
|
+
return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value != null && value !== '' && value !== undefined));
|
|
47
7
|
}
|
|
48
|
-
exports.
|
|
8
|
+
exports._stripEmptyValues = _stripEmptyValues;
|
|
49
9
|
function _getTargetNode(e) {
|
|
50
10
|
if (!e) {
|
|
51
11
|
return null;
|
|
@@ -125,6 +85,24 @@ function _getSafeNetworkInformation() {
|
|
|
125
85
|
return connection;
|
|
126
86
|
}
|
|
127
87
|
exports._getSafeNetworkInformation = _getSafeNetworkInformation;
|
|
88
|
+
function _getSafeTimezone() {
|
|
89
|
+
try {
|
|
90
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports._getSafeTimezone = _getSafeTimezone;
|
|
97
|
+
function _getSafeTimezoneOffset() {
|
|
98
|
+
try {
|
|
99
|
+
return new Date().getTimezoneOffset();
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports._getSafeTimezoneOffset = _getSafeTimezoneOffset;
|
|
128
106
|
function _getAnchorNodeInHierarchy(node) {
|
|
129
107
|
if (!node) {
|
|
130
108
|
return null;
|
|
@@ -142,3 +120,4 @@ function _getAnchorNodeInHierarchy(node) {
|
|
|
142
120
|
}
|
|
143
121
|
return null;
|
|
144
122
|
}
|
|
123
|
+
exports._getAnchorNodeInHierarchy = _getAnchorNodeInHierarchy;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._getMetadataFromElement = exports._gatherEventData = void 0;
|
|
4
|
+
const client_core_1 = require("@statsig/client-core");
|
|
5
|
+
const commonUtils_1 = require("./commonUtils");
|
|
6
|
+
const MAX_ATTRIBUTE_LENGTH = 1000;
|
|
7
|
+
const MAX_CLASS_LIST_LENGTH = 100;
|
|
8
|
+
const MAX_SELECTOR_DEPTH = 50;
|
|
9
|
+
function _gatherEventData(target) {
|
|
10
|
+
const tagName = target.tagName.toLowerCase();
|
|
11
|
+
const metadata = {};
|
|
12
|
+
const value = (0, client_core_1._getCurrentPageUrlSafe)() || '';
|
|
13
|
+
metadata['tagName'] = tagName;
|
|
14
|
+
const elementMetadata = _getMetadataFromElement(target);
|
|
15
|
+
Object.assign(metadata, elementMetadata);
|
|
16
|
+
if (tagName === 'form') {
|
|
17
|
+
Object.assign(metadata, _getFormMetadata(target));
|
|
18
|
+
}
|
|
19
|
+
if (['input', 'select', 'textarea'].includes(tagName) &&
|
|
20
|
+
target.getAttribute('type') !== 'password') {
|
|
21
|
+
Object.assign(metadata, _getInputMetadata(target));
|
|
22
|
+
}
|
|
23
|
+
const anchor = (0, commonUtils_1._getAnchorNodeInHierarchy)(target);
|
|
24
|
+
if (anchor) {
|
|
25
|
+
Object.assign(metadata, _getAnchorMetadata(anchor));
|
|
26
|
+
}
|
|
27
|
+
if (tagName === 'button' || anchor) {
|
|
28
|
+
Object.assign(metadata, _getButtonMetadata(anchor || target));
|
|
29
|
+
}
|
|
30
|
+
return { value, metadata };
|
|
31
|
+
}
|
|
32
|
+
exports._gatherEventData = _gatherEventData;
|
|
33
|
+
function _getFormMetadata(target) {
|
|
34
|
+
var _a;
|
|
35
|
+
const metadata = {};
|
|
36
|
+
metadata['action'] = target.getAttribute('action');
|
|
37
|
+
metadata['method'] = (_a = target.getAttribute('method')) !== null && _a !== void 0 ? _a : 'GET';
|
|
38
|
+
metadata['formName'] = target.getAttribute('name');
|
|
39
|
+
metadata['formId'] = target.getAttribute('id');
|
|
40
|
+
return metadata;
|
|
41
|
+
}
|
|
42
|
+
function _getInputMetadata(target) {
|
|
43
|
+
const metadata = {};
|
|
44
|
+
metadata['content'] = target.value;
|
|
45
|
+
metadata['inputName'] = target.getAttribute('name');
|
|
46
|
+
return metadata;
|
|
47
|
+
}
|
|
48
|
+
function _getAnchorMetadata(anchor) {
|
|
49
|
+
const metadata = {};
|
|
50
|
+
metadata['href'] = anchor.getAttribute('href');
|
|
51
|
+
return metadata;
|
|
52
|
+
}
|
|
53
|
+
function _getButtonMetadata(target) {
|
|
54
|
+
const metadata = {};
|
|
55
|
+
metadata['content'] = (target.textContent || '').trim();
|
|
56
|
+
const dataset = _gatherDatasetProperties(target);
|
|
57
|
+
Object.assign(metadata, dataset);
|
|
58
|
+
return metadata;
|
|
59
|
+
}
|
|
60
|
+
function _gatherDatasetProperties(el) {
|
|
61
|
+
const dataset = {};
|
|
62
|
+
if (!el) {
|
|
63
|
+
return dataset;
|
|
64
|
+
}
|
|
65
|
+
const attr = el === null || el === void 0 ? void 0 : el.dataset;
|
|
66
|
+
if (!attr) {
|
|
67
|
+
return dataset;
|
|
68
|
+
}
|
|
69
|
+
for (const key in attr) {
|
|
70
|
+
dataset[`data-${key}`] = attr[key] || '';
|
|
71
|
+
}
|
|
72
|
+
return dataset;
|
|
73
|
+
}
|
|
74
|
+
function _truncateString(str, maxLength) {
|
|
75
|
+
if (!str)
|
|
76
|
+
return null;
|
|
77
|
+
return str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
|
|
78
|
+
}
|
|
79
|
+
function _getMetadataFromElement(target) {
|
|
80
|
+
const metadata = {};
|
|
81
|
+
const classList = Array.from(target.classList);
|
|
82
|
+
metadata['classList'] =
|
|
83
|
+
classList.length > 0 ? classList.slice(0, MAX_CLASS_LIST_LENGTH) : null;
|
|
84
|
+
metadata['class'] = _normalizeClassAttribute(_truncateString(target.getAttribute('class'), MAX_ATTRIBUTE_LENGTH) || '');
|
|
85
|
+
metadata['id'] = _truncateString(target.getAttribute('id'), MAX_ATTRIBUTE_LENGTH);
|
|
86
|
+
metadata['ariaLabel'] = _truncateString(target.getAttribute('aria-label'), MAX_ATTRIBUTE_LENGTH);
|
|
87
|
+
metadata['selector'] = _generateCssSelector(target);
|
|
88
|
+
return metadata;
|
|
89
|
+
}
|
|
90
|
+
exports._getMetadataFromElement = _getMetadataFromElement;
|
|
91
|
+
function _normalizeClassAttribute(className) {
|
|
92
|
+
return className.replace(/\s+/g, ' ').trim();
|
|
93
|
+
}
|
|
94
|
+
function hasNextSiblingWithSameTag(element) {
|
|
95
|
+
let sibling = element.nextElementSibling;
|
|
96
|
+
while (sibling) {
|
|
97
|
+
if (sibling.tagName === element.tagName) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
sibling = sibling.nextElementSibling;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
function getElementSelector(element) {
|
|
105
|
+
const tagName = element.tagName.toLowerCase();
|
|
106
|
+
// 1. Use ID if available
|
|
107
|
+
if (element.id) {
|
|
108
|
+
return `#${element.id}`;
|
|
109
|
+
}
|
|
110
|
+
// 2. Build class-based selector
|
|
111
|
+
let selector = tagName;
|
|
112
|
+
if (element.className && typeof element.className === 'string') {
|
|
113
|
+
const classes = element.className.trim().split(/\s+/);
|
|
114
|
+
if (classes.length > 0 && classes[0] !== '') {
|
|
115
|
+
selector += '.' + classes.join('.');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const parent = element.parentElement;
|
|
119
|
+
if (parent && parent.children.length > 1) {
|
|
120
|
+
let nthChild = 1;
|
|
121
|
+
let nthOfType = 1;
|
|
122
|
+
let sibling = element.previousElementSibling;
|
|
123
|
+
while (sibling) {
|
|
124
|
+
nthChild++;
|
|
125
|
+
if (sibling.tagName === element.tagName) {
|
|
126
|
+
nthOfType++;
|
|
127
|
+
}
|
|
128
|
+
sibling = sibling.previousElementSibling;
|
|
129
|
+
}
|
|
130
|
+
selector += `:nth-child(${nthChild})`;
|
|
131
|
+
// Only add nth-of-type if there are other elements with the same tag
|
|
132
|
+
if (nthOfType > 1 || hasNextSiblingWithSameTag(element)) {
|
|
133
|
+
selector += `:nth-of-type(${nthOfType})`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return selector;
|
|
137
|
+
}
|
|
138
|
+
function _generateCssSelector(element) {
|
|
139
|
+
if (!element) {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
// Handle case where element has no parent (e.g., detached element)
|
|
143
|
+
if (!element.parentNode) {
|
|
144
|
+
const tagName = element.tagName.toLowerCase();
|
|
145
|
+
if (element.id) {
|
|
146
|
+
return `#${element.id}`;
|
|
147
|
+
}
|
|
148
|
+
let selector = tagName;
|
|
149
|
+
if (element.className && typeof element.className === 'string') {
|
|
150
|
+
const classes = element.className.trim().split(/\s+/);
|
|
151
|
+
if (classes.length > 0 && classes[0] !== '') {
|
|
152
|
+
selector += '.' + classes.join('.');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return selector;
|
|
156
|
+
}
|
|
157
|
+
// Build the full selector path
|
|
158
|
+
const selectors = [];
|
|
159
|
+
let currentElement = element;
|
|
160
|
+
let depth = 0;
|
|
161
|
+
while (currentElement &&
|
|
162
|
+
currentElement.nodeType === Node.ELEMENT_NODE &&
|
|
163
|
+
depth < MAX_SELECTOR_DEPTH) {
|
|
164
|
+
const selector = getElementSelector(currentElement);
|
|
165
|
+
selectors.unshift(selector);
|
|
166
|
+
// Stop if we found an ID (since IDs should be unique)
|
|
167
|
+
if (currentElement.id) {
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
currentElement = currentElement.parentElement;
|
|
171
|
+
// Stop at document body to avoid going too far up
|
|
172
|
+
if (currentElement && currentElement.tagName.toLowerCase() === 'body') {
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
depth++;
|
|
176
|
+
}
|
|
177
|
+
return selectors.join(' > ');
|
|
178
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._getNetworkInfo = exports._gatherAllMetadata = exports._gatherCommonMetadata = void 0;
|
|
4
|
+
const client_core_1 = require("@statsig/client-core");
|
|
5
|
+
const commonUtils_1 = require("./commonUtils");
|
|
6
|
+
function _gatherCommonMetadata(url) {
|
|
7
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
8
|
+
const safeDoc = (0, client_core_1._getDocumentSafe)();
|
|
9
|
+
const safeWnd = (0, client_core_1._getWindowSafe)();
|
|
10
|
+
return (0, commonUtils_1._stripEmptyValues)(Object.assign({ title: safeDoc === null || safeDoc === void 0 ? void 0 : safeDoc.title, current_url: (_a = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.location) === null || _a === void 0 ? void 0 : _a.href, user_agent: ((_b = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _b === void 0 ? void 0 : _b.userAgent) &&
|
|
11
|
+
((_d = (_c = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _c === void 0 ? void 0 : _c.userAgent) === null || _d === void 0 ? void 0 : _d.length) > 200
|
|
12
|
+
? safeWnd.navigator.userAgent.substring(0, 200)
|
|
13
|
+
: (_e = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _e === void 0 ? void 0 : _e.userAgent, locale: (_f = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _f === void 0 ? void 0 : _f.language, hostname: url.hostname, pathname: url.pathname, screen_width: (_g = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.screen) === null || _g === void 0 ? void 0 : _g.width, screen_height: (_h = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.screen) === null || _h === void 0 ? void 0 : _h.height, viewport_width: safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerWidth, viewport_height: safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerHeight, timestamp: Date.now(), timezone: (0, commonUtils_1._getSafeTimezone)(), timezone_offset: (0, commonUtils_1._getSafeTimezoneOffset)() }, _getNetworkInfo()));
|
|
14
|
+
}
|
|
15
|
+
exports._gatherCommonMetadata = _gatherCommonMetadata;
|
|
16
|
+
function _gatherAllMetadata(url) {
|
|
17
|
+
const safeDoc = (0, client_core_1._getDocumentSafe)();
|
|
18
|
+
const safeWnd = (0, client_core_1._getWindowSafe)();
|
|
19
|
+
if (!safeDoc || !safeWnd) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
const referrerInfo = getReferrerInfo(safeDoc);
|
|
23
|
+
const commonInfo = _gatherCommonMetadata(url);
|
|
24
|
+
const campaignParams = getCampaignParams(url);
|
|
25
|
+
const queryParams = {};
|
|
26
|
+
url.searchParams.forEach((v, k) => {
|
|
27
|
+
queryParams[k] = v;
|
|
28
|
+
});
|
|
29
|
+
return Object.assign(Object.assign({}, commonInfo), (0, commonUtils_1._stripEmptyValues)(Object.assign(Object.assign(Object.assign({}, referrerInfo), campaignParams), queryParams)));
|
|
30
|
+
}
|
|
31
|
+
exports._gatherAllMetadata = _gatherAllMetadata;
|
|
32
|
+
function _getNetworkInfo() {
|
|
33
|
+
const networkInfo = (0, commonUtils_1._getSafeNetworkInformation)();
|
|
34
|
+
const result = {};
|
|
35
|
+
if ((networkInfo === null || networkInfo === void 0 ? void 0 : networkInfo.effectiveType) !== undefined) {
|
|
36
|
+
result['effective_connection_type'] = networkInfo.effectiveType;
|
|
37
|
+
}
|
|
38
|
+
if ((networkInfo === null || networkInfo === void 0 ? void 0 : networkInfo.rtt) !== undefined) {
|
|
39
|
+
result['rtt_ms'] = networkInfo.rtt;
|
|
40
|
+
}
|
|
41
|
+
if ((networkInfo === null || networkInfo === void 0 ? void 0 : networkInfo.downlink) !== undefined) {
|
|
42
|
+
result['downlink_mbps'] = networkInfo.downlink;
|
|
43
|
+
result['downlink_kbps'] = networkInfo.downlink * 1000; // deprecated
|
|
44
|
+
}
|
|
45
|
+
if ((networkInfo === null || networkInfo === void 0 ? void 0 : networkInfo.saveData) !== undefined) {
|
|
46
|
+
result['save_data'] = networkInfo.saveData;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
exports._getNetworkInfo = _getNetworkInfo;
|
|
51
|
+
function getReferrerInfo(safeDoc) {
|
|
52
|
+
const referrer = (safeDoc === null || safeDoc === void 0 ? void 0 : safeDoc.referrer) || '';
|
|
53
|
+
if (!referrer) {
|
|
54
|
+
return {
|
|
55
|
+
referrer: null,
|
|
56
|
+
referrer_domain: null,
|
|
57
|
+
referrer_path: null,
|
|
58
|
+
searchEngine: '',
|
|
59
|
+
searchQuery: '',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const url = new URL(referrer);
|
|
64
|
+
const host = url.hostname;
|
|
65
|
+
const searchEngine = ['google', 'bing', 'yahoo', 'duckduckgo', 'baidu'].find((e) => host.includes(e + '.')) || '';
|
|
66
|
+
const searchQuery = url.searchParams.get(searchEngine === 'yahoo' ? 'p' : 'q') || '';
|
|
67
|
+
return {
|
|
68
|
+
referrer,
|
|
69
|
+
referrer_domain: url.hostname,
|
|
70
|
+
referrer_path: url.pathname,
|
|
71
|
+
searchEngine,
|
|
72
|
+
searchQuery,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
return {
|
|
77
|
+
referrer: null,
|
|
78
|
+
referrer_domain: null,
|
|
79
|
+
referrer_path: null,
|
|
80
|
+
searchEngine: '',
|
|
81
|
+
searchQuery: '',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function getCampaignParams(url) {
|
|
86
|
+
const urlParams = url.searchParams;
|
|
87
|
+
const campaignParams = {};
|
|
88
|
+
const commonUtms = [
|
|
89
|
+
'utm_source',
|
|
90
|
+
'utm_medium',
|
|
91
|
+
'utm_campaign',
|
|
92
|
+
'utm_term',
|
|
93
|
+
'utm_content',
|
|
94
|
+
'msclkid', // Bing
|
|
95
|
+
'dclid', // DoubleClick
|
|
96
|
+
'fbclid', // Facebook
|
|
97
|
+
'gad_source', // Google
|
|
98
|
+
'gclid', // Google
|
|
99
|
+
'gclsrc', // Google
|
|
100
|
+
'wbraid', // Google
|
|
101
|
+
'utm_id', // Hubspot
|
|
102
|
+
'irclid', // Impact
|
|
103
|
+
'igshid', // Instagram
|
|
104
|
+
'_kx', // Klaviyo
|
|
105
|
+
'li_fat_id', // LinkedIn
|
|
106
|
+
'mc_cid', // Mailchimp
|
|
107
|
+
'mc_eid', // Mailchimp
|
|
108
|
+
'epik', // Pinterest
|
|
109
|
+
'qclid', // Quora
|
|
110
|
+
'rdt_cid', // Reddit
|
|
111
|
+
'sccid', // Snapchat
|
|
112
|
+
'ttc', // TikTok
|
|
113
|
+
'ttclid', // TikTok
|
|
114
|
+
'ttc_id', // TikTok
|
|
115
|
+
'twclid', // Twitter
|
|
116
|
+
];
|
|
117
|
+
commonUtms.forEach((p) => {
|
|
118
|
+
const val = urlParams.get(p);
|
|
119
|
+
if (val) {
|
|
120
|
+
campaignParams[p] = val;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return campaignParams;
|
|
124
|
+
}
|
package/src/payloadUtils.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function _gatherPageViewPayload(url: URL): Record<string, string | number>;
|
package/src/payloadUtils.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._gatherPageViewPayload = void 0;
|
|
4
|
-
const client_core_1 = require("@statsig/client-core");
|
|
5
|
-
function _gatherPageViewPayload(url) {
|
|
6
|
-
var _a, _b;
|
|
7
|
-
const safeDoc = (0, client_core_1._getDocumentSafe)();
|
|
8
|
-
const safeWnd = (0, client_core_1._getWindowSafe)();
|
|
9
|
-
if (!safeDoc || !safeWnd) {
|
|
10
|
-
return {};
|
|
11
|
-
}
|
|
12
|
-
const navigator = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator;
|
|
13
|
-
const referrer = (safeDoc === null || safeDoc === void 0 ? void 0 : safeDoc.referrer) || '';
|
|
14
|
-
let refUrl = new URL('empty:');
|
|
15
|
-
if (referrer) {
|
|
16
|
-
try {
|
|
17
|
-
refUrl = new URL(referrer || 'empty:');
|
|
18
|
-
}
|
|
19
|
-
catch (e) {
|
|
20
|
-
/* empty */
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
const searchInfo = getSearchInfo(refUrl);
|
|
24
|
-
const campaignParams = getCampaignParams(url);
|
|
25
|
-
const queryParams = {};
|
|
26
|
-
url.searchParams.forEach((v, k) => {
|
|
27
|
-
queryParams[k] = v;
|
|
28
|
-
});
|
|
29
|
-
return Object.assign(Object.assign(Object.assign(Object.assign({}, searchInfo), campaignParams), queryParams), { title: (safeDoc === null || safeDoc === void 0 ? void 0 : safeDoc.title) || '', locale: (navigator === null || navigator === void 0 ? void 0 : navigator.language) || 'unknown', hostname: url.hostname || 'unknown', pathname: url.pathname || 'unknown', referrer, screen_width: ((_a = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.screen) === null || _a === void 0 ? void 0 : _a.width) || 'unknown', screen_height: ((_b = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.screen) === null || _b === void 0 ? void 0 : _b.height) || 'unknown', viewport_width: (safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerWidth) || 'unknown', viewport_height: (safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerHeight) || 'unknown' });
|
|
30
|
-
}
|
|
31
|
-
exports._gatherPageViewPayload = _gatherPageViewPayload;
|
|
32
|
-
function getCampaignParams(url) {
|
|
33
|
-
const urlParams = url.searchParams;
|
|
34
|
-
const campaignParams = {};
|
|
35
|
-
const commonUtms = [
|
|
36
|
-
'utm_source',
|
|
37
|
-
'utm_medium',
|
|
38
|
-
'utm_campaign',
|
|
39
|
-
'utm_term',
|
|
40
|
-
'utm_content',
|
|
41
|
-
'gclid', // Google
|
|
42
|
-
'gclsrc', // Google
|
|
43
|
-
'dclid', // DoubleClick
|
|
44
|
-
'fbclid', // Facebook
|
|
45
|
-
'msclkid', // Bing
|
|
46
|
-
'mc_eid', // Mailchimp
|
|
47
|
-
'mc_cid', // Mailchimp
|
|
48
|
-
'twclid', // Twitter
|
|
49
|
-
'li_fat_id', // LinkedIn
|
|
50
|
-
'igshid', // Instagram
|
|
51
|
-
'utm_id', // Hubspot
|
|
52
|
-
'ttc', // TikTok
|
|
53
|
-
'ttclid', // TikTok
|
|
54
|
-
'ttc_id', // TikTok
|
|
55
|
-
];
|
|
56
|
-
commonUtms.forEach((p) => {
|
|
57
|
-
const val = urlParams.get(p);
|
|
58
|
-
if (val) {
|
|
59
|
-
campaignParams[p] = val;
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
return campaignParams;
|
|
63
|
-
}
|
|
64
|
-
function getSearchEngine(refUrl) {
|
|
65
|
-
const host = refUrl.hostname;
|
|
66
|
-
const engine = ['google', 'bing', 'yahoo', 'duckduckgo', 'baidu'].find((e) => host.includes(e + '.'));
|
|
67
|
-
return engine || '';
|
|
68
|
-
}
|
|
69
|
-
function getSearchInfo(refUrl) {
|
|
70
|
-
const searchEngine = getSearchEngine(refUrl);
|
|
71
|
-
const searchQuery = refUrl.searchParams.get(searchEngine === 'yahoo' ? 'p' : 'q') || '';
|
|
72
|
-
return { searchEngine, searchQuery };
|
|
73
|
-
}
|