noibu-react-native 0.2.3 → 0.2.5
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/dist/api/clientConfig.js +20 -27
- package/dist/api/helpCode.js +61 -87
- package/dist/api/metroplexSocket.js +72 -65
- package/dist/api/storedPageVisit.js +150 -208
- package/dist/constants.js +3 -7
- package/dist/entry/init.js +13 -15
- package/dist/monitors/{appNavigationMonitor.js → AppNavigationMonitor.js} +10 -19
- package/dist/monitors/BaseMonitor.js +23 -0
- package/dist/monitors/ClickMonitor.js +198 -0
- package/dist/monitors/ErrorMonitor.js +206 -0
- package/dist/monitors/KeyboardInputMonitor.js +60 -0
- package/dist/monitors/PageMonitor.js +98 -0
- package/dist/monitors/RequestMonitor.js +390 -0
- package/dist/monitors/http-tools/GqlErrorValidator.js +259 -0
- package/dist/monitors/{httpDataBundler.js → http-tools/HTTPDataBundler.js} +23 -102
- package/dist/pageVisit/{eventDebouncer.js → EventDebouncer.js} +36 -47
- package/dist/pageVisit/pageVisitEventError.js +3 -3
- package/dist/pageVisit/pageVisitEventHTTP.js +5 -4
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +22 -5
- package/dist/sessionRecorder/sessionRecorder.js +5 -2
- package/dist/src/api/clientConfig.d.ts +8 -13
- package/dist/src/api/clientConfig.test.d.ts +1 -0
- package/dist/src/api/helpCode.d.ts +10 -16
- package/dist/src/api/metroplexSocket.d.ts +52 -71
- package/dist/src/api/storedPageVisit.d.ts +12 -21
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/monitors/AppNavigationMonitor.d.ts +18 -0
- package/dist/src/monitors/BaseMonitor.d.ts +13 -0
- package/dist/src/monitors/BaseMonitor.test.d.ts +1 -0
- package/dist/src/monitors/ClickMonitor.d.ts +31 -0
- package/dist/src/monitors/ErrorMonitor.d.ts +63 -0
- package/dist/src/monitors/{keyboardInputMonitor.d.ts → KeyboardInputMonitor.d.ts} +7 -4
- package/dist/src/monitors/{pageMonitor.d.ts → PageMonitor.d.ts} +6 -8
- package/dist/src/monitors/RequestMonitor.d.ts +94 -0
- package/dist/src/monitors/http-tools/GqlErrorValidator.d.ts +59 -0
- package/dist/src/monitors/{httpDataBundler.d.ts → http-tools/HTTPDataBundler.d.ts} +13 -28
- package/dist/src/monitors/integrations/react-native-navigation-integration.d.ts +3 -2
- package/dist/src/pageVisit/{eventDebouncer.d.ts → EventDebouncer.d.ts} +3 -10
- package/dist/src/pageVisit/pageVisit.d.ts +1 -1
- package/dist/src/pageVisit/pageVisitEventHTTP.d.ts +3 -3
- package/dist/src/sessionRecorder/nativeSessionRecorderSubscription.d.ts +15 -0
- package/dist/src/storage/rnStorageProvider.d.ts +1 -1
- package/dist/src/storage/storage.d.ts +1 -1
- package/dist/src/storage/storageProvider.d.ts +2 -2
- package/dist/src/utils/function.d.ts +4 -5
- package/dist/src/utils/object.d.ts +3 -5
- package/dist/src/utils/polyfills.d.ts +1 -4
- package/dist/types/Metroplex.types.d.ts +73 -0
- package/dist/types/PageVisit.types.d.ts +2 -145
- package/dist/types/PageVisitErrors.types.d.ts +114 -0
- package/dist/types/PageVisitEvents.types.d.ts +91 -0
- package/dist/types/Storage.d.ts +1 -1
- package/dist/types/StoredPageVisit.types.d.ts +4 -45
- package/dist/utils/function.js +0 -1
- package/dist/utils/object.js +1 -0
- package/package.json +11 -7
- package/dist/monitors/clickMonitor.js +0 -258
- package/dist/monitors/errorMonitor.js +0 -202
- package/dist/monitors/gqlErrorValidator.js +0 -306
- package/dist/monitors/inputMonitor.js +0 -138
- package/dist/monitors/keyboardInputMonitor.js +0 -66
- package/dist/monitors/pageMonitor.js +0 -122
- package/dist/monitors/requestMonitor.js +0 -386
- package/dist/src/monitors/appNavigationMonitor.d.ts +0 -22
- package/dist/src/monitors/clickMonitor.d.ts +0 -44
- package/dist/src/monitors/errorMonitor.d.ts +0 -28
- package/dist/src/monitors/gqlErrorValidator.d.ts +0 -82
- package/dist/src/monitors/inputMonitor.d.ts +0 -34
- package/dist/src/monitors/requestMonitor.d.ts +0 -10
- package/dist/types/RRWeb.d.ts +0 -48
- package/dist/types/ReactNative.d.ts +0 -4
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { CLICK_EVENT_TYPE, CSS_CLASS_ATT_NAME, ECOMMERCE_EVENT_TYPE, ERROR_EVENT_TYPE, HTMLID_ATT_NAME, HTTP_DATA_PAYLOAD_ATT_NAME, HTTP_DATA_REQ_HEADERS_ATT_NAME, HTTP_DATA_RESP_HEADERS_ATT_NAME, HTTP_DATA_RESP_PAYLOAD_ATT_NAME, HTTP_EVENT_TYPE, HTTP_METHOD_ATT_NAME, HTTP_RESP_CODE_ATT_NAME, HTTP_RESP_LENGTH_ATT_NAME, HTTP_RESP_TIME_ATT_NAME, KEYBOARD_EVENT_TYPE, LOCATION_EVENT_TYPE, NETWORK_STATS_EVENT_TYPE, OCCURRED_AT_ATT_NAME, PAGE_EVENT_TYPE, PAGE_GROUPS_ATT_NAME, PAGE_TITLE_ATT_NAME, PV_SEQ_ATT_NAME, REF_URL_ATT_NAME, SOURCE_ATT_NAME, TAGNAME_ATT_NAME, TEXT_ATT_NAME, TITLE_EVENT_TYPE, TYPE_ATT_NAME, URL_ATT_NAME, USERSTEP_EVENT_TYPE } from '../src/constants';
|
|
2
|
+
import { Output } from './PageVisitErrors.types';
|
|
3
|
+
export interface EventPayloadWithTimestamp<T extends EventType = EventType> {
|
|
4
|
+
event: EventPayload<T>;
|
|
5
|
+
occurredAt: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PVEventMessage extends Partial<EventTypeMap> {
|
|
8
|
+
[OCCURRED_AT_ATT_NAME]: EventPayloadWithTimestamp['occurredAt'];
|
|
9
|
+
[TYPE_ATT_NAME]: EventType;
|
|
10
|
+
}
|
|
11
|
+
export interface EventTypeMap {
|
|
12
|
+
[LOCATION_EVENT_TYPE]: LocationEventPayload;
|
|
13
|
+
[TITLE_EVENT_TYPE]: TitleEventPayload;
|
|
14
|
+
[PAGE_EVENT_TYPE]: PageEventPayload;
|
|
15
|
+
[ERROR_EVENT_TYPE]: ValueOf<Output>;
|
|
16
|
+
[HTTP_EVENT_TYPE]: PVEventHTTPPayload;
|
|
17
|
+
[NETWORK_STATS_EVENT_TYPE]: NetworkStatsEventPayload;
|
|
18
|
+
[USERSTEP_EVENT_TYPE]: UserStepEventPayload;
|
|
19
|
+
[ECOMMERCE_EVENT_TYPE]: ECommerceEventPayload;
|
|
20
|
+
}
|
|
21
|
+
export type EventPayload<T extends EventType> = EventTypeMap[T];
|
|
22
|
+
export type EventType = typeof LOCATION_EVENT_TYPE | typeof TITLE_EVENT_TYPE | typeof PAGE_EVENT_TYPE | typeof ERROR_EVENT_TYPE | typeof HTTP_EVENT_TYPE | typeof NETWORK_STATS_EVENT_TYPE | typeof USERSTEP_EVENT_TYPE | typeof ECOMMERCE_EVENT_TYPE;
|
|
23
|
+
export interface SampleEventPayload {
|
|
24
|
+
[name: string]: {
|
|
25
|
+
values: [number, number][];
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface WebVitalEventPayload {
|
|
29
|
+
name: 'CLS' | 'FCP' | 'FID' | 'INP' | 'LCP' | 'TTFB';
|
|
30
|
+
rating: 'good' | 'needs-improvement' | 'poor';
|
|
31
|
+
attribution: string;
|
|
32
|
+
value: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ECommerceEventPayload {
|
|
35
|
+
data: string;
|
|
36
|
+
name: string;
|
|
37
|
+
platform: string;
|
|
38
|
+
}
|
|
39
|
+
export interface NetworkStatsEventPayload {
|
|
40
|
+
rtt: number;
|
|
41
|
+
effective_type: string;
|
|
42
|
+
downlink: number;
|
|
43
|
+
save_data: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface PageEventPayload {
|
|
46
|
+
type: string;
|
|
47
|
+
data?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface LocationEventPayload {
|
|
50
|
+
[PAGE_TITLE_ATT_NAME]: string;
|
|
51
|
+
[REF_URL_ATT_NAME]: string;
|
|
52
|
+
[PAGE_GROUPS_ATT_NAME]: string[];
|
|
53
|
+
[URL_ATT_NAME]: string;
|
|
54
|
+
}
|
|
55
|
+
export interface TitleEventPayload {
|
|
56
|
+
[PAGE_TITLE_ATT_NAME]: string;
|
|
57
|
+
[REF_URL_ATT_NAME]: string;
|
|
58
|
+
[PAGE_GROUPS_ATT_NAME]: string[];
|
|
59
|
+
[URL_ATT_NAME]: string;
|
|
60
|
+
}
|
|
61
|
+
export interface GtmPageTypeEventPayload {
|
|
62
|
+
key: string;
|
|
63
|
+
value: string;
|
|
64
|
+
}
|
|
65
|
+
export interface UserStepEventPayload {
|
|
66
|
+
[SOURCE_ATT_NAME]: string;
|
|
67
|
+
[TEXT_ATT_NAME]: string;
|
|
68
|
+
[TAGNAME_ATT_NAME]: string;
|
|
69
|
+
[HTMLID_ATT_NAME]: string;
|
|
70
|
+
[CSS_CLASS_ATT_NAME]: string;
|
|
71
|
+
}
|
|
72
|
+
export interface ClickEventPayload extends UserStepEventPayload {
|
|
73
|
+
[TYPE_ATT_NAME]: typeof CLICK_EVENT_TYPE;
|
|
74
|
+
}
|
|
75
|
+
export interface KeyboardEventPayload extends UserStepEventPayload {
|
|
76
|
+
[TYPE_ATT_NAME]: typeof KEYBOARD_EVENT_TYPE;
|
|
77
|
+
}
|
|
78
|
+
export interface PVEventHTTPPayload {
|
|
79
|
+
[HTTP_METHOD_ATT_NAME]: string;
|
|
80
|
+
[HTTP_RESP_CODE_ATT_NAME]: number;
|
|
81
|
+
[URL_ATT_NAME]: string;
|
|
82
|
+
[HTTP_RESP_TIME_ATT_NAME]: number;
|
|
83
|
+
[HTTP_RESP_LENGTH_ATT_NAME]?: number;
|
|
84
|
+
[PV_SEQ_ATT_NAME]?: number;
|
|
85
|
+
}
|
|
86
|
+
export interface HTTPDataBundle {
|
|
87
|
+
[HTTP_DATA_REQ_HEADERS_ATT_NAME]: Record<string, string>;
|
|
88
|
+
[HTTP_DATA_PAYLOAD_ATT_NAME]: string;
|
|
89
|
+
[HTTP_DATA_RESP_HEADERS_ATT_NAME]: Record<string, string>;
|
|
90
|
+
[HTTP_DATA_RESP_PAYLOAD_ATT_NAME]: string;
|
|
91
|
+
}
|
package/dist/types/Storage.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type StorageValue = unknown;
|
|
2
2
|
export interface IStorage {
|
|
3
3
|
isAvailable(): Promise<boolean>;
|
|
4
|
-
load
|
|
4
|
+
load(key: string): Promise<string | null>;
|
|
5
5
|
save(key: string, value: StorageValue): Promise<void>;
|
|
6
6
|
remove(key: string): Promise<void>;
|
|
7
7
|
calculateUsedSize(): Promise<number>;
|
|
@@ -1,52 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export interface PageVisitPart {
|
|
3
|
-
[PV_PART_COUNTER_ATT_NAME]: number;
|
|
4
|
-
[END_AT_ATT_NAME]: number;
|
|
5
|
-
[PV_EXP_VF_SEQ_ATT_NAME]?: number;
|
|
6
|
-
[PV_EXP_PART_COUNTER_ATT_NAME]?: number;
|
|
7
|
-
[PV_EVENTS_ATT_NAME]: Array<any>;
|
|
8
|
-
[SEQ_NUM_ATT_NAME]: number;
|
|
9
|
-
}
|
|
10
|
-
export interface PageVisitInfo {
|
|
11
|
-
[BROWSER_ID_ATT_NAME]: string;
|
|
12
|
-
[PV_ID_ATT_NAME]: string;
|
|
13
|
-
[VIDEO_METROPLEX_TYPE]: number;
|
|
14
|
-
[SCRIPT_ID_ATT_NAME]: string;
|
|
15
|
-
[SCRIPT_INSTANCE_ID_ATT_NAME]: string;
|
|
16
|
-
[METROPLEX_SOCKET_INSTANCE_ID_ATT_NAME]: string;
|
|
17
|
-
[SOCKET_INSTANCE_ID_ATT_NAME]: string;
|
|
18
|
-
[PV_SEQ_ATT_NAME]: number;
|
|
19
|
-
[IS_LAST_ATT_NAME]: boolean;
|
|
20
|
-
[CONN_COUNT_ATT_NAME]: number;
|
|
21
|
-
[ON_URL_ATT_NAME]: string;
|
|
22
|
-
[PAGE_GROUPS_ATT_NAME]: Array<string>;
|
|
23
|
-
[PAGE_TITLE_ATT_NAME]: string;
|
|
24
|
-
[REF_URL_ATT_NAME]: string;
|
|
25
|
-
[STARTED_AT_ATT_NAME]: string;
|
|
26
|
-
[COLLECT_VER_ATT_NAME]: number;
|
|
27
|
-
[LANG_ATT_NAME]?: string;
|
|
28
|
-
[VIDEO_RECORDER_ATT_NAME]: string;
|
|
29
|
-
}
|
|
1
|
+
import { PageVisitFrag, PageVisitInfo } from './Metroplex.types';
|
|
30
2
|
export interface PageVisitPartsBase {
|
|
31
3
|
pageVisitInfo: PageVisitInfo;
|
|
32
|
-
pageVisitFrags:
|
|
4
|
+
pageVisitFrags: PageVisitFrag[];
|
|
33
5
|
}
|
|
34
|
-
export interface
|
|
6
|
+
export interface ExportPVParts extends PageVisitPartsBase {
|
|
35
7
|
timestamp?: Date;
|
|
36
8
|
}
|
|
37
|
-
export interface
|
|
9
|
+
export interface ImportPVParts extends PageVisitPartsBase {
|
|
38
10
|
timestamp?: string;
|
|
39
11
|
}
|
|
40
|
-
export interface VideoFrag {
|
|
41
|
-
[VIDEO_FRAG_ATT_NAME]: string;
|
|
42
|
-
[PV_SEQ_ATT_NAME]: number;
|
|
43
|
-
[END_AT_ATT_NAME]: number;
|
|
44
|
-
[LENGTH_ATT_NAME]: number;
|
|
45
|
-
[SEQ_NUM_ATT_NAME]: number;
|
|
46
|
-
[CSS_URLS_ATT_NAME]: string[];
|
|
47
|
-
}
|
|
48
|
-
export interface CompletePageVisitParts {
|
|
49
|
-
[PAGE_VISIT_VID_FRAG_ATT_NAME]: Array<VideoFrag>;
|
|
50
|
-
[PAGE_VISIT_INFORMATION_ATT_NAME]: PageVisitInfo;
|
|
51
|
-
[PAGE_VISIT_PART_ATT_NAME]: Array<PageVisitPart>;
|
|
52
|
-
}
|
package/dist/utils/function.js
CHANGED
|
@@ -28,7 +28,6 @@ function getMaxSubstringAllowed(stringToVerify, length = MAX_STRING_LENGTH) {
|
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Processes the raw stack frames and creates a readable stack in a safe manner
|
|
31
|
-
* @param {StackFrame[]} rawFrames
|
|
32
31
|
*/
|
|
33
32
|
function processFrames(rawFrames) {
|
|
34
33
|
return rawFrames.map(frame => {
|
package/dist/utils/object.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noibu-react-native",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"targetNjsVersion": "1.0.104",
|
|
5
5
|
"description": "React-Native SDK for NoibuJS to collect errors in React-Native applications",
|
|
6
6
|
"main": "dist/entry/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"build": "node ./build.js",
|
|
18
18
|
"build:dev": "node ./build.watch.js",
|
|
19
19
|
"prepare": "npm run clean; npm run build;",
|
|
20
|
-
"test": "jest --coverage
|
|
20
|
+
"test": "jest --coverage",
|
|
21
|
+
"tsc": "tsc",
|
|
21
22
|
"lint": "eslint src -c .eslintrc.json --ext js,ts,jsx,tsx",
|
|
22
23
|
"lint_output": "eslint src -c .eslintrc.json --ext js,ts,jsx,tsx -f json > eslint_report.json",
|
|
23
24
|
"codecov": "codecov"
|
|
@@ -44,15 +45,18 @@
|
|
|
44
45
|
"react-native-uuid": "^2.0.1"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
48
|
+
"@babel/core": "^7.26.0",
|
|
49
|
+
"@babel/preset-env": "^7.26.0",
|
|
50
|
+
"@babel/preset-typescript": "^7.26.0",
|
|
47
51
|
"@jest/globals": "^29.7.0",
|
|
48
|
-
"@react-native-async-storage/async-storage": "1.19.0",
|
|
52
|
+
"@react-native-async-storage/async-storage": "^1.19.0",
|
|
49
53
|
"@rollup/plugin-commonjs": "^25.0.0",
|
|
50
54
|
"@rollup/plugin-json": "^6.0.0",
|
|
51
55
|
"@rollup/plugin-node-resolve": "^15.1.0",
|
|
52
56
|
"@rollup/plugin-replace": "^5.0.2",
|
|
53
57
|
"@rollup/plugin-typescript": "^11.1.1",
|
|
54
58
|
"@tsconfig/react-native": "^3.0.2",
|
|
55
|
-
"@types/jest": "^29.5.
|
|
59
|
+
"@types/jest": "^29.5.14",
|
|
56
60
|
"@types/node": "^20.2.3",
|
|
57
61
|
"@types/react": "16.14.62",
|
|
58
62
|
"@types/react-test-renderer": "^18.0.0",
|
|
@@ -71,14 +75,14 @@
|
|
|
71
75
|
"jest": "^29.7.0",
|
|
72
76
|
"prettier": "^2.8.8",
|
|
73
77
|
"react": "16.13.1",
|
|
74
|
-
"react-native": "
|
|
75
|
-
"react-native-navigation": "
|
|
78
|
+
"react-native": "0.63.0",
|
|
79
|
+
"react-native-navigation": "^7.40.3",
|
|
76
80
|
"react-native-url-polyfill": "1.3.0",
|
|
77
81
|
"react-native-uuid": "2.0.1",
|
|
78
82
|
"rimraf": "^5.0.1",
|
|
79
83
|
"rollup": "^4.27.4",
|
|
80
84
|
"rollup-plugin-dotenv": "^0.5.0",
|
|
81
|
-
"ts-jest": "^29.2.
|
|
85
|
+
"ts-jest": "^29.2.5",
|
|
82
86
|
"typescript": "^5.5.3"
|
|
83
87
|
}
|
|
84
88
|
}
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import Pressability from 'react-native/Libraries/Pressability/Pressability';
|
|
2
|
-
import { WHITELIST_HTML_ID_TEXT_REGEX, USERSTEP_EVENT_TYPE, SOURCE_ATT_NAME, TEXT_ATT_NAME, TAGNAME_ATT_NAME, HTMLID_ATT_NAME, TYPE_ATT_NAME, CLICK_EVENT_TYPE, CSS_CLASS_ATT_NAME } from '../constants.js';
|
|
3
|
-
import { PageVisit } from '../pageVisit/pageVisit.js';
|
|
4
|
-
import { updatePayload } from '../pageVisit/userStep.js';
|
|
5
|
-
import StoredMetrics from '../api/storedMetrics.js';
|
|
6
|
-
import { WHITELIST_TEXT_REGEX_STRING } from '../const_matchers.js';
|
|
7
|
-
import { maskTextInput } from '../utils/function.js';
|
|
8
|
-
import { timestampWrapper } from '../utils/date.js';
|
|
9
|
-
import ClientConfig from '../api/clientConfig.js';
|
|
10
|
-
|
|
11
|
-
/** @module ClickMonitor */
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/** Monitors the clicks which we capture and later process */
|
|
15
|
-
class ClickMonitor {
|
|
16
|
-
/**
|
|
17
|
-
* Creates an instance of the ClickMonitor instance
|
|
18
|
-
*/
|
|
19
|
-
constructor() {
|
|
20
|
-
// compile white list regex only once
|
|
21
|
-
this.textCapturedWhiteListRegex = new RegExp(
|
|
22
|
-
WHITELIST_TEXT_REGEX_STRING(),
|
|
23
|
-
'i',
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
this.htmlIDAllowListRegex = new RegExp(WHITELIST_HTML_ID_TEXT_REGEX, 'i');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** gets the singleton instance
|
|
30
|
-
* @returns {ClickMonitor}
|
|
31
|
-
*/
|
|
32
|
-
static getInstance() {
|
|
33
|
-
if (!this.instance) {
|
|
34
|
-
this.instance = new ClickMonitor();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return this.instance;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Starts monitoring clicks on every Press-able component */
|
|
41
|
-
monitorClicks() {
|
|
42
|
-
const onClickHandler = this._onClickHandle.bind(this);
|
|
43
|
-
|
|
44
|
-
if (!Pressability.prototype.originalCreateEventHandlers) {
|
|
45
|
-
Pressability.prototype.originalCreateEventHandlers =
|
|
46
|
-
Pressability.prototype.getEventHandlers;
|
|
47
|
-
|
|
48
|
-
Pressability.prototype.getEventHandlers = function () {
|
|
49
|
-
const ehs =
|
|
50
|
-
Pressability.prototype.originalCreateEventHandlers.call(this);
|
|
51
|
-
|
|
52
|
-
return Object.fromEntries(
|
|
53
|
-
Object.entries(ehs).map(([key, handler]) => [
|
|
54
|
-
key,
|
|
55
|
-
(event, ...args) => {
|
|
56
|
-
if (key === 'onResponderRelease') {
|
|
57
|
-
onClickHandler(event);
|
|
58
|
-
}
|
|
59
|
-
return handler(event, ...args);
|
|
60
|
-
},
|
|
61
|
-
]),
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Handles a single click event
|
|
69
|
-
* @param {{ _targetInst: RNNode }} event
|
|
70
|
-
*/
|
|
71
|
-
_onClickHandle(event) {
|
|
72
|
-
if (event) {
|
|
73
|
-
const { _targetInst: target } = event;
|
|
74
|
-
const targetClassName = target.elementType;
|
|
75
|
-
|
|
76
|
-
let text = '';
|
|
77
|
-
// if the tag name of the src element is image, then we need
|
|
78
|
-
// to process the image name, else we need to get the textual content
|
|
79
|
-
// todo process images
|
|
80
|
-
text = this._getTextualContentFromEl(target);
|
|
81
|
-
|
|
82
|
-
let textFromElement = this._trimText(text);
|
|
83
|
-
|
|
84
|
-
let tagName = '';
|
|
85
|
-
if (targetClassName) {
|
|
86
|
-
tagName = targetClassName.toLowerCase();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// id of element
|
|
90
|
-
let hid = target.memoizedProps.testID || '';
|
|
91
|
-
// in some bizarre cases, the html id of an element gets overriden
|
|
92
|
-
// to contain jquery objects. If the hid is an object, it's of no
|
|
93
|
-
// use to us.
|
|
94
|
-
if (typeof hid !== 'string') {
|
|
95
|
-
hid = '';
|
|
96
|
-
}
|
|
97
|
-
// if we find that the text matches analytic data used
|
|
98
|
-
// to find checkout starts, add to cart clicks, etc.
|
|
99
|
-
// we do not mask it.
|
|
100
|
-
if (
|
|
101
|
-
!this.textCapturedWhiteListRegex.test(textFromElement) &&
|
|
102
|
-
!this.htmlIDAllowListRegex.test(hid)
|
|
103
|
-
) {
|
|
104
|
-
if (tagName === 'input') {
|
|
105
|
-
if (
|
|
106
|
-
event.type &&
|
|
107
|
-
(event.type === 'button' || event.type === 'submit')
|
|
108
|
-
) ; else {
|
|
109
|
-
textFromElement = '*';
|
|
110
|
-
}
|
|
111
|
-
} else if (tagName === 'textarea') {
|
|
112
|
-
textFromElement = '*';
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
textFromElement = maskTextInput(textFromElement);
|
|
117
|
-
const tPayload = {
|
|
118
|
-
[SOURCE_ATT_NAME]: '',
|
|
119
|
-
[TEXT_ATT_NAME]: textFromElement,
|
|
120
|
-
[TAGNAME_ATT_NAME]: tagName,
|
|
121
|
-
[HTMLID_ATT_NAME]: hid,
|
|
122
|
-
[TYPE_ATT_NAME]: CLICK_EVENT_TYPE,
|
|
123
|
-
[CSS_CLASS_ATT_NAME]: '',
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
StoredMetrics.getInstance().addPvClick();
|
|
127
|
-
|
|
128
|
-
PageVisit.getInstance().addPageVisitEvents(
|
|
129
|
-
[
|
|
130
|
-
{
|
|
131
|
-
event: updatePayload(tPayload),
|
|
132
|
-
occurredAt: new Date(timestampWrapper(Date.now())).toISOString(),
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
USERSTEP_EVENT_TYPE,
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** Gets the textual content from an element, if any
|
|
141
|
-
* @param {} element
|
|
142
|
-
*/
|
|
143
|
-
_getTextualContentFromEl(element) {
|
|
144
|
-
return this._parseInnerContent(element, '', 100, { value: 0, limit: 100 });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Parse and trim text
|
|
148
|
-
* @param {} text
|
|
149
|
-
*/
|
|
150
|
-
_trimText(text) {
|
|
151
|
-
// regex to replace all whitespace with a single space and trim text
|
|
152
|
-
let parsedText = text.trim().replace(/\s+/g, ' ');
|
|
153
|
-
|
|
154
|
-
// if there is text that is longer than average sentence length,
|
|
155
|
-
// chances are that it is not a valid text so we need to further process it
|
|
156
|
-
if (parsedText.length > 100) {
|
|
157
|
-
const index = parsedText.lastIndexOf(' ', 97);
|
|
158
|
-
// We can not chop the text at exactly 97 characters since chopping in the
|
|
159
|
-
// middle of an encoded value may cause deserialization issues. Chop at the
|
|
160
|
-
// last ' ' character before the 97th character.
|
|
161
|
-
if (index > 0) {
|
|
162
|
-
parsedText = `${parsedText.substring(0, index)}...`;
|
|
163
|
-
} else {
|
|
164
|
-
// If there are no ' ' characters then the text is likely not valid so just
|
|
165
|
-
// return '...'.
|
|
166
|
-
parsedText = '...';
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return parsedText;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Recursively parses element's inner content and masks blocked css classes
|
|
174
|
-
* @param {NReactNative.Node} element
|
|
175
|
-
* @param {String} text
|
|
176
|
-
* @param {Number} textLimit
|
|
177
|
-
* @param {Object} counter
|
|
178
|
-
*/
|
|
179
|
-
_parseInnerContent(element, text, textLimit, counter) {
|
|
180
|
-
/* eslint-disable no-restricted-syntax */
|
|
181
|
-
/* eslint-disable no-param-reassign */
|
|
182
|
-
|
|
183
|
-
if (text.length >= textLimit) {
|
|
184
|
-
return text;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (counter.value >= counter.limit) {
|
|
188
|
-
return text;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
counter.value += 1;
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
ClickMonitor.getBlockedElements().includes(element.memoizedProps.testID)
|
|
195
|
-
) {
|
|
196
|
-
return `${text}${text ? ' ' : ''}*`;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// eslint-disable-next-line require-jsdoc
|
|
200
|
-
const walk = node => {
|
|
201
|
-
// Check if the node is a Text element
|
|
202
|
-
if (
|
|
203
|
-
node.elementType &&
|
|
204
|
-
node.elementType.displayName === 'Text' &&
|
|
205
|
-
node.memoizedProps &&
|
|
206
|
-
typeof node.memoizedProps.children === 'string'
|
|
207
|
-
) {
|
|
208
|
-
text = this._parseAndAppendText(text, [node.memoizedProps.children]);
|
|
209
|
-
if (text.length >= textLimit) return;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// If the node has children, traverse them
|
|
213
|
-
if (node.child) walk(node.child);
|
|
214
|
-
|
|
215
|
-
// After traversing children, traverse siblings
|
|
216
|
-
if (node.sibling) walk(node.sibling);
|
|
217
|
-
};
|
|
218
|
-
walk(element);
|
|
219
|
-
|
|
220
|
-
return text;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Gets selectors to prevent those elements from being recorded
|
|
225
|
-
*/
|
|
226
|
-
static getBlockedElements() {
|
|
227
|
-
const selectors = ClientConfig.getInstance().blockedElements;
|
|
228
|
-
const blockedElements = ['noibu-blocked'];
|
|
229
|
-
if (selectors && Array.isArray(selectors)) {
|
|
230
|
-
blockedElements.push(...selectors);
|
|
231
|
-
}
|
|
232
|
-
return blockedElements;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* normalize value and append to the resulting text if not empty
|
|
237
|
-
* @param {String} text
|
|
238
|
-
* @param {Array.<any>} values
|
|
239
|
-
*/
|
|
240
|
-
_parseAndAppendText(text, values) {
|
|
241
|
-
const goodValues = [];
|
|
242
|
-
for (const v of values) {
|
|
243
|
-
if (Number.isFinite(v) || typeof v === 'string') {
|
|
244
|
-
goodValues.push(v);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
for (let value of goodValues) {
|
|
249
|
-
value = `${value}`.trim().replace(/\s+/g, ' ');
|
|
250
|
-
if (value.length > 0) {
|
|
251
|
-
return text + (text ? ' ' : '') + value;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return text;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export { ClickMonitor };
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { asString, isStackTrace } from '../utils/function.js';
|
|
2
|
-
import { CONSOLE_FUNCTION_OVERRIDES, ERROR_EVENT_ERROR_TYPE, STACK_TRACE_SANITIZE_REGEXP, ERROR_EVENT_UNHANDLED_REJECTION_TYPE, ERROR_LOG_EVENT_ERROR_TYPE } from '../constants.js';
|
|
3
|
-
import { saveErrorToPagevisit } from '../pageVisit/pageVisitEventError.js';
|
|
4
|
-
import { replace } from '../utils/object.js';
|
|
5
|
-
|
|
6
|
-
/* eslint-disable @typescript-eslint/ban-types,prefer-arrow-callback */
|
|
7
|
-
/** @module ErrorMonitor */
|
|
8
|
-
let ignoreError = 0;
|
|
9
|
-
/**
|
|
10
|
-
* returns boolean that indicates wether we should
|
|
11
|
-
* ignore the next error event caught by the error event
|
|
12
|
-
* listener.
|
|
13
|
-
*/
|
|
14
|
-
function shouldIgnoreError() {
|
|
15
|
-
return ignoreError > 0;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* transform a log into an error since React hides component errors
|
|
19
|
-
* tps://reactjs.org/docs/error-boundaries.html
|
|
20
|
-
* @param {Error|any} errorLog
|
|
21
|
-
* @returns {boolean} true on success
|
|
22
|
-
*/
|
|
23
|
-
function processErrorLog(errorLog) {
|
|
24
|
-
// handle empty arguments
|
|
25
|
-
if (!errorLog) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
const { message, stack } = errorLog;
|
|
29
|
-
// for now, we only process error logs if they are a way to describe an error
|
|
30
|
-
if (!stack || !message) {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
saveErrorToPagevisit(ERROR_LOG_EVENT_ERROR_TYPE, { message, stack });
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Constructs error objects based on console args.
|
|
38
|
-
* Relies on isStackTrace() to determine if arg is a stack trace, otherwise the arg must be a message.
|
|
39
|
-
*
|
|
40
|
-
* If there are multiple stack traces or the number of error messages is not equal to one,
|
|
41
|
-
* it maps the stack traces to objects with their respective first lines as messages.
|
|
42
|
-
* Otherwise, if there is exactly one stack trace and one error message,
|
|
43
|
-
* it constructs an array with a single object containing the first stack trace and the first error message.
|
|
44
|
-
* @param {Array<string>} args
|
|
45
|
-
*/
|
|
46
|
-
function constructErrors(args) {
|
|
47
|
-
if (args.length === 0) {
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
if (args.length === 2) {
|
|
51
|
-
if (isStackTrace(args[0])) {
|
|
52
|
-
return [{ stack: args[0], message: args[1] }];
|
|
53
|
-
}
|
|
54
|
-
if (isStackTrace(args[1])) {
|
|
55
|
-
return [{ stack: args[1], message: args[0] }];
|
|
56
|
-
}
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
const stacks = [];
|
|
60
|
-
const messages = [];
|
|
61
|
-
args.forEach(sm => {
|
|
62
|
-
if (isStackTrace()) {
|
|
63
|
-
stacks.push(sm);
|
|
64
|
-
const lines = sm.split('\n');
|
|
65
|
-
if (isStackTrace(lines[0])) {
|
|
66
|
-
messages.push('_');
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
// we can use 1st line of stack trace as a message
|
|
70
|
-
messages.push(lines[0]);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
return stacks.map((stack, i) => ({ stack, message: messages[i] }));
|
|
75
|
-
}
|
|
76
|
-
/** iterates arguments to try to extract errors from them
|
|
77
|
-
* @param {Array<string | Error>} argsFromErrorLog
|
|
78
|
-
*/
|
|
79
|
-
function processErrorLogArguments(argsFromErrorLog) {
|
|
80
|
-
// tracks if arguments were processed at least once successfully, and if not, tries to reconstruct an Error
|
|
81
|
-
const collectedArgs = [];
|
|
82
|
-
// we may receive multiple arguments
|
|
83
|
-
argsFromErrorLog.forEach(arg => {
|
|
84
|
-
// ensure arg exists before attempting processing
|
|
85
|
-
if (!arg) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
// if we pass an array of elements in the console, we want to go through that
|
|
89
|
-
// away and make sure we are extracting the error object. We don't do nested arrays.
|
|
90
|
-
if (Array.isArray(arg)) {
|
|
91
|
-
collectedArgs.push(...arg);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
collectedArgs.push(arg);
|
|
95
|
-
});
|
|
96
|
-
const argsToBeReconstructedIntoErrors = collectedArgs.filter(arg => !processErrorLog(arg));
|
|
97
|
-
// if previous process calls return nothing,
|
|
98
|
-
// try to reconstruct error from the args
|
|
99
|
-
constructErrors(argsToBeReconstructedIntoErrors).forEach(processErrorLog);
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* wraps and replaces the addEventListener property
|
|
103
|
-
* of event targets to try and catch errors
|
|
104
|
-
* eventTargetString: event target name
|
|
105
|
-
*/
|
|
106
|
-
function configureEventListeners() {
|
|
107
|
-
// we wrap all logging to catch any potential errors passed in logs
|
|
108
|
-
CONSOLE_FUNCTION_OVERRIDES.forEach(consoleFunction => {
|
|
109
|
-
if (!window.console || !window.console[consoleFunction]) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
replace(window.console, consoleFunction, function (originalFunction) {
|
|
113
|
-
// We set nbuWrapper to the handler function so if this returns an error
|
|
114
|
-
// the trace message will start with nbuWrapper which would allow us to
|
|
115
|
-
// detect that this is a wrapped function and not a NoibuJS error
|
|
116
|
-
return function nbuWrapper() {
|
|
117
|
-
// ignoring this linting error since we don't want
|
|
118
|
-
// potentially overide the actual functioning of console.error
|
|
119
|
-
// eslint-disable-next-line prefer-rest-params
|
|
120
|
-
originalFunction.call(window.console, ...arguments);
|
|
121
|
-
// converting the arguments to an array so that we do not nest argument objects
|
|
122
|
-
// eslint-disable-next-line prefer-rest-params
|
|
123
|
-
processErrorLogArguments(Array.from(arguments));
|
|
124
|
-
};
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* handler for promise rejection failures
|
|
130
|
-
* @param error
|
|
131
|
-
*/
|
|
132
|
-
function onPromiseRejectionHandler(error) {
|
|
133
|
-
if (!error || !error.message || !error.stack) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
const sanitizedStack = error.stack
|
|
137
|
-
.split('\n')
|
|
138
|
-
.filter(line => !line.match(STACK_TRACE_SANITIZE_REGEXP))
|
|
139
|
-
.join('\n');
|
|
140
|
-
const payload = {
|
|
141
|
-
error: {
|
|
142
|
-
message: error.message,
|
|
143
|
-
stack: sanitizedStack,
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
saveErrorToPagevisit(ERROR_EVENT_UNHANDLED_REJECTION_TYPE, payload);
|
|
147
|
-
}
|
|
148
|
-
function configureHermesHooks() {
|
|
149
|
-
var _a;
|
|
150
|
-
if (typeof HermesInternal !== 'undefined' && HermesInternal !== null) {
|
|
151
|
-
(_a = HermesInternal.enablePromiseRejectionTracker) === null || _a === void 0 ? void 0 : _a.call(HermesInternal, {
|
|
152
|
-
allRejections: true,
|
|
153
|
-
});
|
|
154
|
-
/**
|
|
155
|
-
* This internal promise implementation method is populated only after enabling the promise tracker.
|
|
156
|
-
* It represents an improvement over the previous approach,
|
|
157
|
-
* which would lose stack context regarding the rejection because it ran asynchronously through setTimeout.
|
|
158
|
-
*
|
|
159
|
-
* This updated method ensures synchronous error capturing and retrieves the correct stack frames.
|
|
160
|
-
*/
|
|
161
|
-
replace(Promise, '_m', (originalFunction) => function nbuGlobalPromiseRejectWrapper(promise, error) {
|
|
162
|
-
if (error.message && error.stack) {
|
|
163
|
-
onPromiseRejectionHandler(error);
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
onPromiseRejectionHandler(new Error(asString(error)));
|
|
167
|
-
}
|
|
168
|
-
return originalFunction === null || originalFunction === void 0 ? void 0 : originalFunction(promise, error);
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Handles a single error event
|
|
174
|
-
*/
|
|
175
|
-
function onErrorHandler(error) {
|
|
176
|
-
if (!error || shouldIgnoreError()) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
saveErrorToPagevisit(ERROR_EVENT_ERROR_TYPE, {
|
|
180
|
-
error,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
function configureErrorUtilsHandler() {
|
|
184
|
-
if (typeof ErrorUtils === 'undefined') {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
const existingHandler = ErrorUtils.getGlobalHandler() || (() => { });
|
|
188
|
-
ErrorUtils.setGlobalHandler((error, ...rest) => {
|
|
189
|
-
onErrorHandler(error);
|
|
190
|
-
return existingHandler(error, ...rest);
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Monitors the errors happening on the window
|
|
195
|
-
*/
|
|
196
|
-
function monitorErrors() {
|
|
197
|
-
configureEventListeners();
|
|
198
|
-
configureHermesHooks();
|
|
199
|
-
configureErrorUtilsHandler();
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export { monitorErrors, onPromiseRejectionHandler, processErrorLogArguments };
|