@statsig/web-analytics 3.20.3 → 3.21.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 +2 -4
- package/src/AutoCapture.js +26 -7
- package/src/AutoCaptureEvent.d.ts +2 -0
- package/src/AutoCaptureEvent.js +3 -0
- package/src/AutoCaptureOptions.d.ts +14 -0
- package/src/AutoCaptureOptions.js +2 -0
- package/src/ConsoleLogManager.d.ts +27 -0
- package/src/ConsoleLogManager.js +99 -0
- package/src/utils/commonUtils.d.ts +3 -1
- package/src/utils/commonUtils.js +71 -4
- package/src/utils/eventUtils.d.ts +1 -0
- package/src/utils/eventUtils.js +11 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statsig/web-analytics",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.21.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.21.0",
|
|
13
|
+
"@statsig/js-client": "3.21.0",
|
|
14
14
|
"web-vitals": "5.0.3"
|
|
15
15
|
},
|
|
16
16
|
"jsdelivr": "./build/statsig-web-analytics.min.js",
|
package/src/AutoCapture.d.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { PrecomputedEvaluationsInterface, StatsigPlugin } from '@statsig/client-core';
|
|
2
|
-
import {
|
|
3
|
-
export type AutoCaptureOptions = {
|
|
4
|
-
eventFilterFunc?: (event: AutoCaptureEvent) => boolean;
|
|
5
|
-
};
|
|
2
|
+
import { AutoCaptureOptions } from './AutoCaptureOptions';
|
|
6
3
|
export declare class StatsigAutoCapturePlugin implements StatsigPlugin<PrecomputedEvaluationsInterface> {
|
|
7
4
|
private _options?;
|
|
8
5
|
readonly __plugin = "auto-capture";
|
|
@@ -22,6 +19,7 @@ export declare class AutoCapture {
|
|
|
22
19
|
private _pageViewLogged;
|
|
23
20
|
private _webVitalsManager;
|
|
24
21
|
private _deadClickManager;
|
|
22
|
+
private _consoleLogManager;
|
|
25
23
|
constructor(_client: PrecomputedEvaluationsInterface, options?: AutoCaptureOptions);
|
|
26
24
|
private _addEventHandlers;
|
|
27
25
|
private _addPageViewTracking;
|
package/src/AutoCapture.js
CHANGED
|
@@ -3,6 +3,7 @@ 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 ConsoleLogManager_1 = require("./ConsoleLogManager");
|
|
6
7
|
const DeadClickManager_1 = require("./DeadClickManager");
|
|
7
8
|
const EngagementManager_1 = require("./EngagementManager");
|
|
8
9
|
const RageClickManager_1 = require("./RageClickManager");
|
|
@@ -13,6 +14,8 @@ const metadataUtils_1 = require("./utils/metadataUtils");
|
|
|
13
14
|
const AUTO_EVENT_MAPPING = {
|
|
14
15
|
submit: AutoCaptureEvent_1.AutoCaptureEventName.FORM_SUBMIT,
|
|
15
16
|
click: AutoCaptureEvent_1.AutoCaptureEventName.CLICK,
|
|
17
|
+
copy: AutoCaptureEvent_1.AutoCaptureEventName.COPY,
|
|
18
|
+
cut: AutoCaptureEvent_1.AutoCaptureEventName.COPY,
|
|
16
19
|
};
|
|
17
20
|
class StatsigAutoCapturePlugin {
|
|
18
21
|
constructor(_options) {
|
|
@@ -39,7 +42,7 @@ function runStatsigAutoCapture(client, options) {
|
|
|
39
42
|
exports.runStatsigAutoCapture = runStatsigAutoCapture;
|
|
40
43
|
class AutoCapture {
|
|
41
44
|
constructor(_client, options) {
|
|
42
|
-
var _a, _b, _c;
|
|
45
|
+
var _a, _b, _c, _d;
|
|
43
46
|
this._client = _client;
|
|
44
47
|
this._disabledEvents = {};
|
|
45
48
|
this._previousLoggedPageViewUrl = null;
|
|
@@ -59,11 +62,12 @@ class AutoCapture {
|
|
|
59
62
|
this._rageClickManager = new RageClickManager_1.default();
|
|
60
63
|
this._webVitalsManager = new WebVitalsManager_1.WebVitalsManager(this._enqueueAutoCapture.bind(this));
|
|
61
64
|
this._deadClickManager = new DeadClickManager_1.default(this._enqueueAutoCapture.bind(this));
|
|
65
|
+
this._consoleLogManager = new ConsoleLogManager_1.ConsoleLogManager(this._enqueueAutoCapture.bind(this), (_c = options === null || options === void 0 ? void 0 : options.consoleLogAutoCaptureSettings) !== null && _c !== void 0 ? _c : { enabled: false });
|
|
62
66
|
this._eventFilterFunc = options === null || options === void 0 ? void 0 : options.eventFilterFunc;
|
|
63
67
|
const doc = (0, client_core_1._getDocumentSafe)();
|
|
64
68
|
if (!(0, client_core_1._isServerEnv)()) {
|
|
65
69
|
__STATSIG__ = (0, client_core_1._getStatsigGlobal)();
|
|
66
|
-
const instances = (
|
|
70
|
+
const instances = (_d = __STATSIG__.acInstances) !== null && _d !== void 0 ? _d : {};
|
|
67
71
|
instances[sdkKey] = this;
|
|
68
72
|
__STATSIG__.acInstances = instances;
|
|
69
73
|
}
|
|
@@ -95,6 +99,8 @@ class AutoCapture {
|
|
|
95
99
|
};
|
|
96
100
|
(0, commonUtils_1._registerEventHandler)(doc, 'click', (e) => eventHandler(e));
|
|
97
101
|
(0, commonUtils_1._registerEventHandler)(doc, 'submit', (e) => eventHandler(e));
|
|
102
|
+
(0, commonUtils_1._registerEventHandler)(doc, 'copy', (e) => eventHandler(e));
|
|
103
|
+
(0, commonUtils_1._registerEventHandler)(doc, 'cut', (e) => eventHandler(e));
|
|
98
104
|
(0, commonUtils_1._registerEventHandler)(win, 'error', (e) => eventHandler(e, false));
|
|
99
105
|
(0, commonUtils_1._registerEventHandler)(win, 'pagehide', () => this._tryLogPageViewEnd());
|
|
100
106
|
(0, commonUtils_1._registerEventHandler)(win, 'beforeunload', () => this._tryLogPageViewEnd());
|
|
@@ -115,7 +121,7 @@ class AutoCapture {
|
|
|
115
121
|
this._tryLogPageView();
|
|
116
122
|
}
|
|
117
123
|
_autoLogEvent(event) {
|
|
118
|
-
var _a;
|
|
124
|
+
var _a, _b, _c;
|
|
119
125
|
const eventType = (_a = event.type) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
120
126
|
if (eventType === 'error' && event instanceof ErrorEvent) {
|
|
121
127
|
this._logError(event);
|
|
@@ -125,20 +131,33 @@ class AutoCapture {
|
|
|
125
131
|
if (!target) {
|
|
126
132
|
return;
|
|
127
133
|
}
|
|
128
|
-
if (!(0, commonUtils_1._shouldLogEvent)(event, target)) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
134
|
const eventName = AUTO_EVENT_MAPPING[eventType];
|
|
132
135
|
if (!eventName) {
|
|
133
136
|
return;
|
|
134
137
|
}
|
|
135
|
-
const
|
|
138
|
+
const isCopyEvent = eventName === AutoCaptureEvent_1.AutoCaptureEventName.COPY;
|
|
139
|
+
if (!(0, commonUtils_1._shouldLogEvent)(event, target, isCopyEvent)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const metadata = {};
|
|
143
|
+
if (isCopyEvent) {
|
|
144
|
+
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();
|
|
145
|
+
if (!selectedText) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
metadata['selectedText'] = (0, commonUtils_1._sanitizeString)(selectedText);
|
|
149
|
+
const clipType = event.type || 'clipboard';
|
|
150
|
+
metadata['clipType'] = clipType;
|
|
151
|
+
}
|
|
152
|
+
const { value, metadata: eventMetadata } = (0, eventUtils_1._gatherEventData)(target);
|
|
153
|
+
Object.assign(metadata, eventMetadata);
|
|
136
154
|
const allMetadata = (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)());
|
|
137
155
|
this._enqueueAutoCapture(eventName, value, Object.assign(Object.assign({}, allMetadata), metadata));
|
|
138
156
|
}
|
|
139
157
|
_initialize() {
|
|
140
158
|
this._webVitalsManager.startTracking();
|
|
141
159
|
this._deadClickManager.startTracking();
|
|
160
|
+
this._consoleLogManager.startTracking();
|
|
142
161
|
this._engagementManager.startInactivityTracking(() => this._tryLogPageViewEnd(true));
|
|
143
162
|
this._addEventHandlers();
|
|
144
163
|
this._addPageViewTracking();
|
|
@@ -10,6 +10,8 @@ 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";
|
|
14
|
+
readonly CONSOLE_LOG: "statsig::log_line";
|
|
13
15
|
};
|
|
14
16
|
export type AutoCaptureEventName = (typeof AutoCaptureEventName)[keyof typeof AutoCaptureEventName] & string;
|
|
15
17
|
export type AutoCaptureEvent = StatsigEvent & {
|
package/src/AutoCaptureEvent.js
CHANGED
|
@@ -12,4 +12,7 @@ exports.AutoCaptureEventName = {
|
|
|
12
12
|
RAGE_CLICK: 'auto_capture::rage_click',
|
|
13
13
|
WEB_VITALS: 'auto_capture::web_vitals',
|
|
14
14
|
DEAD_CLICK: 'auto_capture::dead_click',
|
|
15
|
+
COPY: 'auto_capture::copy',
|
|
16
|
+
// log line is a special event name used to populate logs metrics explorer
|
|
17
|
+
CONSOLE_LOG: 'statsig::log_line',
|
|
15
18
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { LogLevel } from '@statsig/client-core';
|
|
2
|
+
import { AutoCaptureEvent } from './AutoCaptureEvent';
|
|
3
|
+
export type AutoCaptureOptions = {
|
|
4
|
+
eventFilterFunc?: (event: AutoCaptureEvent) => boolean;
|
|
5
|
+
consoleLogAutoCaptureSettings?: ConsoleLogAutoCaptureSettings;
|
|
6
|
+
};
|
|
7
|
+
export type ConsoleLogAutoCaptureSettings = {
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
logLevel?: LogLevel;
|
|
10
|
+
serviceName?: string;
|
|
11
|
+
serviceVersion?: string;
|
|
12
|
+
sampleRate?: number;
|
|
13
|
+
resourceMetadata?: Record<string, string | number | boolean>;
|
|
14
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AutoCaptureEventName } from './AutoCaptureEvent';
|
|
2
|
+
import { ConsoleLogAutoCaptureSettings } from './AutoCaptureOptions';
|
|
3
|
+
export type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
4
|
+
export type ConsoleLogEvent = {
|
|
5
|
+
eventName: 'statsig::console_log';
|
|
6
|
+
value: string;
|
|
7
|
+
metadata: {
|
|
8
|
+
timestamp: string;
|
|
9
|
+
log_level: LogLevel;
|
|
10
|
+
payload: string[];
|
|
11
|
+
trace: string[];
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export declare class ConsoleLogManager {
|
|
15
|
+
private _enqueueFn;
|
|
16
|
+
private _options;
|
|
17
|
+
private _restoreFns;
|
|
18
|
+
private _isTracking;
|
|
19
|
+
constructor(_enqueueFn: (eventName: AutoCaptureEventName, value: string, metadata: Record<string, unknown>) => void, _options: ConsoleLogAutoCaptureSettings);
|
|
20
|
+
startTracking(): void;
|
|
21
|
+
stopTracking(): void;
|
|
22
|
+
private _patchConsole;
|
|
23
|
+
private _enqueueConsoleLog;
|
|
24
|
+
private _shouldLog;
|
|
25
|
+
private _safeStringify;
|
|
26
|
+
private _getStackTrace;
|
|
27
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConsoleLogManager = void 0;
|
|
4
|
+
/* eslint-disable no-console */
|
|
5
|
+
const client_core_1 = require("@statsig/client-core");
|
|
6
|
+
const AutoCaptureEvent_1 = require("./AutoCaptureEvent");
|
|
7
|
+
const commonUtils_1 = require("./utils/commonUtils");
|
|
8
|
+
const metadataUtils_1 = require("./utils/metadataUtils");
|
|
9
|
+
class ConsoleLogManager {
|
|
10
|
+
constructor(_enqueueFn, _options) {
|
|
11
|
+
this._enqueueFn = _enqueueFn;
|
|
12
|
+
this._options = _options;
|
|
13
|
+
this._restoreFns = [];
|
|
14
|
+
this._isTracking = false;
|
|
15
|
+
}
|
|
16
|
+
startTracking() {
|
|
17
|
+
if (this._isTracking || !this._options.enabled)
|
|
18
|
+
return;
|
|
19
|
+
const win = (0, client_core_1._getWindowSafe)();
|
|
20
|
+
if (!win)
|
|
21
|
+
return;
|
|
22
|
+
this._isTracking = true;
|
|
23
|
+
this._patchConsole();
|
|
24
|
+
}
|
|
25
|
+
stopTracking() {
|
|
26
|
+
if (!this._isTracking)
|
|
27
|
+
return;
|
|
28
|
+
this._restoreFns.forEach((restore) => restore());
|
|
29
|
+
this._restoreFns = [];
|
|
30
|
+
this._isTracking = false;
|
|
31
|
+
}
|
|
32
|
+
_patchConsole() {
|
|
33
|
+
['log', 'info', 'warn', 'error', 'debug'].forEach((level) => {
|
|
34
|
+
if (!console[level])
|
|
35
|
+
return;
|
|
36
|
+
const original = console[level].bind(console);
|
|
37
|
+
let inStack = false;
|
|
38
|
+
const restore = (0, commonUtils_1.patch)(console, level, (originalFn) => {
|
|
39
|
+
return (...args) => {
|
|
40
|
+
originalFn(...args);
|
|
41
|
+
if (inStack)
|
|
42
|
+
return;
|
|
43
|
+
inStack = true;
|
|
44
|
+
try {
|
|
45
|
+
const payload = args.map((a) => this._safeStringify(a));
|
|
46
|
+
const trace = this._getStackTrace();
|
|
47
|
+
this._enqueueConsoleLog(level, payload, trace);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
original('console observer error:', err, ...args);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
inStack = false;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
this._restoreFns.push(restore);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
_enqueueConsoleLog(level, payload, trace) {
|
|
61
|
+
var _a, _b, _c;
|
|
62
|
+
if (!this._shouldLog())
|
|
63
|
+
return;
|
|
64
|
+
const metadata = Object.assign(Object.assign({ log_level: level, payload,
|
|
65
|
+
trace, timestamp: Date.now(), serviceName: (_a = this._options.serviceName) !== null && _a !== void 0 ? _a : '', serviceVersion: (_b = this._options.serviceVersion) !== null && _b !== void 0 ? _b : '' }, ((_c = this._options.resourceMetadata) !== null && _c !== void 0 ? _c : {})), (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)()));
|
|
66
|
+
this._enqueueFn(AutoCaptureEvent_1.AutoCaptureEventName.CONSOLE_LOG, (0, commonUtils_1._getSafeUrlString)(), metadata);
|
|
67
|
+
}
|
|
68
|
+
_shouldLog() {
|
|
69
|
+
if (!this._options.sampleRate ||
|
|
70
|
+
typeof this._options.sampleRate !== 'number' ||
|
|
71
|
+
this._options.sampleRate <= 0 ||
|
|
72
|
+
this._options.sampleRate >= 1) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return Math.random() < this._options.sampleRate;
|
|
76
|
+
}
|
|
77
|
+
_safeStringify(val) {
|
|
78
|
+
try {
|
|
79
|
+
if (typeof val === 'string')
|
|
80
|
+
return val;
|
|
81
|
+
if (typeof val === 'object' && val !== null)
|
|
82
|
+
return JSON.stringify(val);
|
|
83
|
+
return String(val);
|
|
84
|
+
}
|
|
85
|
+
catch (_a) {
|
|
86
|
+
return '[Unserializable]';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
_getStackTrace() {
|
|
90
|
+
var _a, _b;
|
|
91
|
+
try {
|
|
92
|
+
return (_b = (_a = new Error().stack) === null || _a === void 0 ? void 0 : _a.split('\n').slice(2)) !== null && _b !== void 0 ? _b : [];
|
|
93
|
+
}
|
|
94
|
+
catch (_c) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.ConsoleLogManager = ConsoleLogManager;
|
|
@@ -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,7 @@ 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;
|
|
21
|
+
export declare function patch(source: Record<string, unknown>, name: string, replacement: (original: (...args: unknown[]) => void) => (...args: unknown[]) => void): () => void;
|
|
20
22
|
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.patch = 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) {
|
|
@@ -154,3 +186,38 @@ function throttle(fn, limit) {
|
|
|
154
186
|
};
|
|
155
187
|
}
|
|
156
188
|
exports.throttle = throttle;
|
|
189
|
+
// copy from https://github.com/getsentry/sentry-javascript/blob/b2109071975af8bf0316d3b5b38f519bdaf5dc15/packages/utils/src/object.ts
|
|
190
|
+
function patch(source, name, replacement) {
|
|
191
|
+
try {
|
|
192
|
+
if (!source[name])
|
|
193
|
+
return () => {
|
|
194
|
+
// noop
|
|
195
|
+
};
|
|
196
|
+
const original = source[name];
|
|
197
|
+
const wrapped = replacement(original);
|
|
198
|
+
// Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work
|
|
199
|
+
// otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
|
|
200
|
+
if (typeof wrapped === 'function') {
|
|
201
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
202
|
+
wrapped.prototype = wrapped.prototype || {};
|
|
203
|
+
Object.defineProperties(wrapped, {
|
|
204
|
+
__statsig_original__: {
|
|
205
|
+
enumerable: false,
|
|
206
|
+
value: original,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
source[name] = wrapped;
|
|
211
|
+
return () => {
|
|
212
|
+
source[name] = original;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
return () => {
|
|
217
|
+
// noop
|
|
218
|
+
};
|
|
219
|
+
// This can throw if multiple fill happens on a global object like XMLHttpRequest
|
|
220
|
+
// Fixes https://github.com/getsentry/sentry-javascript/issues/2043
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
exports.patch = patch;
|
|
@@ -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 = {};
|