@statsig/web-analytics 3.20.3 → 3.20.4
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statsig/web-analytics",
|
|
3
|
-
"version": "3.20.
|
|
3
|
+
"version": "3.20.4",
|
|
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.20.
|
|
13
|
-
"@statsig/js-client": "3.20.
|
|
12
|
+
"@statsig/client-core": "3.20.4",
|
|
13
|
+
"@statsig/js-client": "3.20.4",
|
|
14
14
|
"web-vitals": "5.0.3"
|
|
15
15
|
},
|
|
16
16
|
"jsdelivr": "./build/statsig-web-analytics.min.js",
|
package/src/AutoCapture.js
CHANGED
|
@@ -13,6 +13,8 @@ const metadataUtils_1 = require("./utils/metadataUtils");
|
|
|
13
13
|
const AUTO_EVENT_MAPPING = {
|
|
14
14
|
submit: AutoCaptureEvent_1.AutoCaptureEventName.FORM_SUBMIT,
|
|
15
15
|
click: AutoCaptureEvent_1.AutoCaptureEventName.CLICK,
|
|
16
|
+
copy: AutoCaptureEvent_1.AutoCaptureEventName.COPY,
|
|
17
|
+
cut: AutoCaptureEvent_1.AutoCaptureEventName.COPY,
|
|
16
18
|
};
|
|
17
19
|
class StatsigAutoCapturePlugin {
|
|
18
20
|
constructor(_options) {
|
|
@@ -95,6 +97,8 @@ class AutoCapture {
|
|
|
95
97
|
};
|
|
96
98
|
(0, commonUtils_1._registerEventHandler)(doc, 'click', (e) => eventHandler(e));
|
|
97
99
|
(0, commonUtils_1._registerEventHandler)(doc, 'submit', (e) => eventHandler(e));
|
|
100
|
+
(0, commonUtils_1._registerEventHandler)(doc, 'copy', (e) => eventHandler(e));
|
|
101
|
+
(0, commonUtils_1._registerEventHandler)(doc, 'cut', (e) => eventHandler(e));
|
|
98
102
|
(0, commonUtils_1._registerEventHandler)(win, 'error', (e) => eventHandler(e, false));
|
|
99
103
|
(0, commonUtils_1._registerEventHandler)(win, 'pagehide', () => this._tryLogPageViewEnd());
|
|
100
104
|
(0, commonUtils_1._registerEventHandler)(win, 'beforeunload', () => this._tryLogPageViewEnd());
|
|
@@ -115,7 +119,7 @@ class AutoCapture {
|
|
|
115
119
|
this._tryLogPageView();
|
|
116
120
|
}
|
|
117
121
|
_autoLogEvent(event) {
|
|
118
|
-
var _a;
|
|
122
|
+
var _a, _b, _c;
|
|
119
123
|
const eventType = (_a = event.type) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
120
124
|
if (eventType === 'error' && event instanceof ErrorEvent) {
|
|
121
125
|
this._logError(event);
|
|
@@ -125,14 +129,26 @@ class AutoCapture {
|
|
|
125
129
|
if (!target) {
|
|
126
130
|
return;
|
|
127
131
|
}
|
|
128
|
-
if (!(0, commonUtils_1._shouldLogEvent)(event, target)) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
132
|
const eventName = AUTO_EVENT_MAPPING[eventType];
|
|
132
133
|
if (!eventName) {
|
|
133
134
|
return;
|
|
134
135
|
}
|
|
135
|
-
const
|
|
136
|
+
const isCopyEvent = eventName === AutoCaptureEvent_1.AutoCaptureEventName.COPY;
|
|
137
|
+
if (!(0, commonUtils_1._shouldLogEvent)(event, target, isCopyEvent)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const metadata = {};
|
|
141
|
+
if (isCopyEvent) {
|
|
142
|
+
const selectedText = (_c = (_b = (0, client_core_1._getWindowSafe)()) === null || _b === void 0 ? void 0 : _b.getSelection()) === null || _c === void 0 ? void 0 : _c.toString();
|
|
143
|
+
if (!selectedText) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
metadata['selectedText'] = (0, commonUtils_1._sanitizeString)(selectedText);
|
|
147
|
+
const clipType = event.type || 'clipboard';
|
|
148
|
+
metadata['clipType'] = clipType;
|
|
149
|
+
}
|
|
150
|
+
const { value, metadata: eventMetadata } = (0, eventUtils_1._gatherEventData)(target);
|
|
151
|
+
Object.assign(metadata, eventMetadata);
|
|
136
152
|
const allMetadata = (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)());
|
|
137
153
|
this._enqueueAutoCapture(eventName, value, Object.assign(Object.assign({}, allMetadata), metadata));
|
|
138
154
|
}
|
|
@@ -10,6 +10,7 @@ export declare const AutoCaptureEventName: {
|
|
|
10
10
|
readonly RAGE_CLICK: "auto_capture::rage_click";
|
|
11
11
|
readonly WEB_VITALS: "auto_capture::web_vitals";
|
|
12
12
|
readonly DEAD_CLICK: "auto_capture::dead_click";
|
|
13
|
+
readonly COPY: "auto_capture::copy";
|
|
13
14
|
};
|
|
14
15
|
export type AutoCaptureEventName = (typeof AutoCaptureEventName)[keyof typeof AutoCaptureEventName] & string;
|
|
15
16
|
export type AutoCaptureEvent = StatsigEvent & {
|
package/src/AutoCaptureEvent.js
CHANGED
|
@@ -7,7 +7,7 @@ interface NetworkInformation {
|
|
|
7
7
|
export declare const interactiveElements: string[];
|
|
8
8
|
export declare function _stripEmptyValues<T extends Record<string, string | number | null | undefined>>(obj: T): Partial<Record<keyof T, string | number>>;
|
|
9
9
|
export declare function _getTargetNode(e: Event): Element | null;
|
|
10
|
-
export declare function _shouldLogEvent(e: Event, el: Element): boolean;
|
|
10
|
+
export declare function _shouldLogEvent(e: Event, el: Element, isCopyEvent?: boolean): boolean;
|
|
11
11
|
export declare function _getSafeUrl(): URL;
|
|
12
12
|
export declare function _getSafeUrlString(): string;
|
|
13
13
|
export declare function _getSanitizedPageUrl(): string;
|
|
@@ -16,5 +16,6 @@ export declare function _getSafeNetworkInformation(): NetworkInformation | null;
|
|
|
16
16
|
export declare function _getSafeTimezone(): string | null;
|
|
17
17
|
export declare function _getSafeTimezoneOffset(): number | null;
|
|
18
18
|
export declare function _getAnchorNodeInHierarchy(node: Element | null): Element | null;
|
|
19
|
+
export declare function _sanitizeString(maybeString: string | null | undefined): string | null;
|
|
19
20
|
export declare function throttle<T extends (...args: unknown[]) => void>(fn: T, limit: number): T;
|
|
20
21
|
export {};
|
package/src/utils/commonUtils.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.throttle = exports._getAnchorNodeInHierarchy = exports._getSafeTimezoneOffset = exports._getSafeTimezone = exports._getSafeNetworkInformation = exports._registerEventHandler = exports._getSanitizedPageUrl = exports._getSafeUrlString = exports._getSafeUrl = exports._shouldLogEvent = exports._getTargetNode = exports._stripEmptyValues = exports.interactiveElements = void 0;
|
|
3
|
+
exports.throttle = exports._sanitizeString = exports._getAnchorNodeInHierarchy = exports._getSafeTimezoneOffset = exports._getSafeTimezone = exports._getSafeNetworkInformation = exports._registerEventHandler = exports._getSanitizedPageUrl = exports._getSafeUrlString = exports._getSafeUrl = exports._shouldLogEvent = exports._getTargetNode = exports._stripEmptyValues = exports.interactiveElements = void 0;
|
|
4
4
|
const client_core_1 = require("@statsig/client-core");
|
|
5
|
+
const coreCCPattern = `(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})`;
|
|
6
|
+
const CC_REGEX = new RegExp(`^(?:${coreCCPattern})$`);
|
|
7
|
+
const coreSSNPattern = `\\d{3}-?\\d{2}-?\\d{4}`;
|
|
8
|
+
const SSN_REGEX = new RegExp(`^(${coreSSNPattern})$`);
|
|
5
9
|
exports.interactiveElements = [
|
|
6
10
|
'button',
|
|
7
11
|
'a',
|
|
@@ -30,7 +34,7 @@ function _getTargetNode(e) {
|
|
|
30
34
|
return target;
|
|
31
35
|
}
|
|
32
36
|
exports._getTargetNode = _getTargetNode;
|
|
33
|
-
function _shouldLogEvent(e, el) {
|
|
37
|
+
function _shouldLogEvent(e, el, isCopyEvent = false) {
|
|
34
38
|
if (!e || !el || el.nodeType !== 1) {
|
|
35
39
|
return false;
|
|
36
40
|
}
|
|
@@ -40,15 +44,19 @@ function _shouldLogEvent(e, el) {
|
|
|
40
44
|
if (classList.contains('statsig-no-capture')) {
|
|
41
45
|
return false;
|
|
42
46
|
}
|
|
47
|
+
if (isCopyEvent) {
|
|
48
|
+
// We don't want to force strict event filtering for copy events
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
43
51
|
switch (tagName) {
|
|
44
52
|
case 'html':
|
|
45
53
|
return false;
|
|
46
54
|
case 'form':
|
|
47
|
-
return eventType
|
|
55
|
+
return ['submit'].indexOf(eventType) >= 0;
|
|
48
56
|
case 'input':
|
|
49
57
|
case 'select':
|
|
50
58
|
case 'textarea':
|
|
51
|
-
return ['change'].
|
|
59
|
+
return ['change', 'click'].indexOf(eventType) >= 0;
|
|
52
60
|
default:
|
|
53
61
|
if (eventType === 'click') {
|
|
54
62
|
if (tagName === 'button') {
|
|
@@ -143,6 +151,30 @@ function _getAnchorNodeInHierarchy(node) {
|
|
|
143
151
|
return null;
|
|
144
152
|
}
|
|
145
153
|
exports._getAnchorNodeInHierarchy = _getAnchorNodeInHierarchy;
|
|
154
|
+
function _sanitizeString(maybeString) {
|
|
155
|
+
if (!maybeString) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return maybeString
|
|
159
|
+
.replace(/<[^>]*>/g, '')
|
|
160
|
+
.trim()
|
|
161
|
+
.split(/(\s+)/)
|
|
162
|
+
.filter((s) => _shouldCaptureTextValue(s))
|
|
163
|
+
.join('')
|
|
164
|
+
.replace(/[\r\n]/g, ' ')
|
|
165
|
+
.replace(/[ ]+/g, ' ')
|
|
166
|
+
.substring(0, 255);
|
|
167
|
+
}
|
|
168
|
+
exports._sanitizeString = _sanitizeString;
|
|
169
|
+
function _shouldCaptureTextValue(text) {
|
|
170
|
+
if (CC_REGEX.test((text || '').replace(/[- ]/g, ''))) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (SSN_REGEX.test((text || '').replace(/[- ]/g, ''))) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
146
178
|
function throttle(fn, limit) {
|
|
147
179
|
let lastCall = 0;
|
|
148
180
|
return function (...args) {
|
|
@@ -2,4 +2,5 @@ export declare function _gatherEventData(target: Element): {
|
|
|
2
2
|
value: string;
|
|
3
3
|
metadata: Record<string, unknown>;
|
|
4
4
|
};
|
|
5
|
+
export declare function _gatherCopyEventData(e: Event): Record<string, unknown>;
|
|
5
6
|
export declare function _getMetadataFromElement(target: Element): Record<string, unknown>;
|
package/src/utils/eventUtils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._getMetadataFromElement = exports._gatherEventData = void 0;
|
|
3
|
+
exports._getMetadataFromElement = exports._gatherCopyEventData = exports._gatherEventData = void 0;
|
|
4
4
|
const client_core_1 = require("@statsig/client-core");
|
|
5
5
|
const commonUtils_1 = require("./commonUtils");
|
|
6
6
|
const MAX_ATTRIBUTE_LENGTH = 1000;
|
|
@@ -30,6 +30,16 @@ function _gatherEventData(target) {
|
|
|
30
30
|
return { value, metadata };
|
|
31
31
|
}
|
|
32
32
|
exports._gatherEventData = _gatherEventData;
|
|
33
|
+
function _gatherCopyEventData(e) {
|
|
34
|
+
var _a, _b;
|
|
35
|
+
const selectedText = (_b = (_a = (0, client_core_1._getWindowSafe)()) === null || _a === void 0 ? void 0 : _a.getSelection()) === null || _b === void 0 ? void 0 : _b.toString();
|
|
36
|
+
const metadata = {};
|
|
37
|
+
metadata['selectedText'] = (0, commonUtils_1._sanitizeString)(selectedText);
|
|
38
|
+
const clipType = e.type || 'clipboard';
|
|
39
|
+
metadata['clipType'] = clipType;
|
|
40
|
+
return metadata;
|
|
41
|
+
}
|
|
42
|
+
exports._gatherCopyEventData = _gatherCopyEventData;
|
|
33
43
|
function _getFormMetadata(target) {
|
|
34
44
|
var _a;
|
|
35
45
|
const metadata = {};
|