noibu-react-native 0.0.4 → 0.0.7
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/README.md +22 -7
- package/dist/api/clientConfig.d.ts +101 -0
- package/dist/api/clientConfig.js +317 -388
- package/dist/api/helpCode.js +2 -2
- package/dist/api/inputManager.js +3 -9
- package/dist/api/metroplexSocket.js +10 -34
- package/dist/api/storedMetrics.js +3 -2
- package/dist/api/storedPageVisit.js +3 -3
- package/dist/constants.d.ts +7 -40
- package/dist/constants.js +8 -65
- package/dist/entry/index.d.ts +2 -3
- package/dist/entry/index.js +1 -9
- package/dist/entry/init.d.ts +5 -0
- package/dist/entry/init.js +60 -75
- package/dist/monitors/AppNavigationMonitor.d.ts +23 -0
- package/dist/monitors/AppNavigationMonitor.js +63 -0
- package/dist/monitors/clickMonitor.js +10 -57
- package/dist/monitors/errorMonitor.js +1 -1
- package/dist/monitors/gqlErrorValidator.js +3 -3
- package/dist/monitors/httpDataBundler.js +13 -12
- package/dist/monitors/integrations/react-native-navigation-integration.d.ts +19 -0
- package/dist/monitors/integrations/react-native-navigation-integration.js +38 -0
- package/dist/monitors/keyboardInputMonitor.js +0 -1
- package/dist/monitors/requestMonitor.js +10 -8
- package/dist/pageVisit/pageVisitEventError/pageVisitEventError.js +10 -8
- package/dist/storage/storage.d.ts +3 -2
- package/dist/storage/storageProvider.d.ts +6 -5
- package/dist/types/Config.d.ts +27 -0
- package/dist/types/NavigationIntegration.d.ts +7 -0
- package/dist/types/PageVisit.d.ts +22 -0
- package/dist/types/ReactNative.d.ts +4 -0
- package/dist/types/Storage.d.ts +14 -0
- package/dist/types/globals.d.ts +34 -0
- package/dist/utils/date.js +2 -2
- package/dist/utils/eventlistener.js +3 -3
- package/dist/utils/function.d.ts +93 -0
- package/dist/utils/function.js +210 -320
- package/dist/utils/stacktrace-parser.d.ts +6 -8
- package/dist/utils/stacktrace-parser.js +5 -5
- package/package.json +5 -1
- package/dist/monitors/elementMonitor.js +0 -177
- package/dist/monitors/locationChangeMonitor.js +0 -18
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attaches corresponding listener to the passed navigation integration
|
|
3
|
+
*/
|
|
4
|
+
export declare class AppNavigationMonitor {
|
|
5
|
+
private static instance;
|
|
6
|
+
private breadcrumbs;
|
|
7
|
+
/**
|
|
8
|
+
* guesses which navigation is used in app, and registers a listener if found
|
|
9
|
+
*/
|
|
10
|
+
constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Gets the singleton instance
|
|
13
|
+
*/
|
|
14
|
+
static getInstance(): AppNavigationMonitor;
|
|
15
|
+
/**
|
|
16
|
+
* gets current global url
|
|
17
|
+
*/
|
|
18
|
+
get globalUrl(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Called when the event needs to be emitted
|
|
21
|
+
*/
|
|
22
|
+
private reportLocationChange;
|
|
23
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { SEVERITY, URL_ATT_NAME, LOCATION_EVENT_TYPE } from '../constants.js';
|
|
2
|
+
import { getMaxSubstringAllowed } from '../utils/function.js';
|
|
3
|
+
import { InputMonitor } from './inputMonitor.js';
|
|
4
|
+
import { ReactNativeNavigationIntegration } from './integrations/react-native-navigation-integration.js';
|
|
5
|
+
import ClientConfig from '../api/clientConfig.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Attaches corresponding listener to the passed navigation integration
|
|
9
|
+
*/
|
|
10
|
+
class AppNavigationMonitor {
|
|
11
|
+
static instance;
|
|
12
|
+
breadcrumbs = [];
|
|
13
|
+
/**
|
|
14
|
+
* guesses which navigation is used in app, and registers a listener if found
|
|
15
|
+
*/
|
|
16
|
+
constructor() {
|
|
17
|
+
try {
|
|
18
|
+
// eslint-disable-next-line global-require,@typescript-eslint/no-var-requires,import/no-extraneous-dependencies
|
|
19
|
+
const rnNavigation = require('react-native-navigation')?.Navigation;
|
|
20
|
+
if (rnNavigation) {
|
|
21
|
+
new ReactNativeNavigationIntegration().register(rnNavigation, breadcrumbs => {
|
|
22
|
+
this.breadcrumbs = breadcrumbs;
|
|
23
|
+
this.reportLocationChange();
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(`AppNavigationMonitor: ${e}`, false, SEVERITY.error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Gets the singleton instance
|
|
33
|
+
*/
|
|
34
|
+
static getInstance() {
|
|
35
|
+
if (!AppNavigationMonitor.instance) {
|
|
36
|
+
AppNavigationMonitor.instance = new AppNavigationMonitor();
|
|
37
|
+
}
|
|
38
|
+
return AppNavigationMonitor.instance;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* gets current global url
|
|
42
|
+
*/
|
|
43
|
+
get globalUrl() {
|
|
44
|
+
const globalUrl = new URL('https://localhost');
|
|
45
|
+
globalUrl.hostname = ClientConfig.getInstance().customerDomain;
|
|
46
|
+
if (this.breadcrumbs.length) {
|
|
47
|
+
globalUrl.pathname = this.breadcrumbs.join('/');
|
|
48
|
+
}
|
|
49
|
+
return getMaxSubstringAllowed(globalUrl.toString());
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Called when the event needs to be emitted
|
|
53
|
+
*/
|
|
54
|
+
reportLocationChange() {
|
|
55
|
+
const payload = {
|
|
56
|
+
[URL_ATT_NAME]: this.globalUrl,
|
|
57
|
+
};
|
|
58
|
+
// storing the location change in the page visit queue
|
|
59
|
+
InputMonitor.getInstance().addEvent(payload, LOCATION_EVENT_TYPE);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { AppNavigationMonitor };
|
|
@@ -4,14 +4,12 @@ import { PageVisit } from '../pageVisit/pageVisit.js';
|
|
|
4
4
|
import { updatePayload } from '../pageVisit/userStep/userStep.js';
|
|
5
5
|
import StoredMetrics from '../api/storedMetrics.js';
|
|
6
6
|
import { WHITELIST_TEXT_REGEX_STRING } from '../const_matchers.js';
|
|
7
|
-
import {
|
|
7
|
+
import { maskTextInput, getBlockedElements } from '../utils/function.js';
|
|
8
8
|
import { timestampWrapper } from '../utils/date.js';
|
|
9
9
|
|
|
10
10
|
/** @module ClickMonitor */
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
const maxParentIteration = 5;
|
|
14
|
-
|
|
15
13
|
/** Monitors the clicks which we capture and later process */
|
|
16
14
|
class ClickMonitor {
|
|
17
15
|
/**
|
|
@@ -67,10 +65,9 @@ class ClickMonitor {
|
|
|
67
65
|
|
|
68
66
|
/**
|
|
69
67
|
* Handles a single click event
|
|
70
|
-
* @param {} event
|
|
68
|
+
* @param {{ _targetInst: RNNode }} event
|
|
71
69
|
*/
|
|
72
70
|
_onClickHandle(event) {
|
|
73
|
-
const blockedCSS = getBlockedCSSForCurrentDomain();
|
|
74
71
|
if (event) {
|
|
75
72
|
const { _targetInst: target } = event;
|
|
76
73
|
const targetClassName = target.elementType;
|
|
@@ -80,7 +77,7 @@ class ClickMonitor {
|
|
|
80
77
|
// to process the image name, else we need to get the textual content
|
|
81
78
|
// todo process images
|
|
82
79
|
|
|
83
|
-
text = this._getTextualContentFromEl(target
|
|
80
|
+
text = this._getTextualContentFromEl(target);
|
|
84
81
|
|
|
85
82
|
let textFromElement = this._trimText(text);
|
|
86
83
|
|
|
@@ -140,54 +137,11 @@ class ClickMonitor {
|
|
|
140
137
|
}
|
|
141
138
|
}
|
|
142
139
|
|
|
143
|
-
/**
|
|
144
|
-
* parseTextFromParentElement will parse the parents of an element to try
|
|
145
|
-
* and find textual content if no text can be extracted from the clicked element
|
|
146
|
-
* @param {} element
|
|
147
|
-
* @param {Array.<String>} blockedCSS
|
|
148
|
-
*/
|
|
149
|
-
_parseTextFromParentElement(element, blockedCSS) {
|
|
150
|
-
let iteratableElement = element;
|
|
151
|
-
const parentElements = [];
|
|
152
|
-
let parentIterations = 0;
|
|
153
|
-
while (iteratableElement) {
|
|
154
|
-
if (
|
|
155
|
-
parentIterations >= maxParentIteration ||
|
|
156
|
-
!iteratableElement.parentNode
|
|
157
|
-
) {
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
iteratableElement = iteratableElement.parentNode;
|
|
161
|
-
parentElements.push(iteratableElement);
|
|
162
|
-
parentIterations += 1;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
for (let i = 0; i < parentElements.length; i += 1) {
|
|
166
|
-
const el = parentElements[i];
|
|
167
|
-
// we only get the text content if the clicked element has a button parent
|
|
168
|
-
if (el && el.tagName === 'BUTTON') {
|
|
169
|
-
// we disable the linter because we use 1 level recursion.
|
|
170
|
-
// eslint-disable-next-line no-use-before-define
|
|
171
|
-
return this._getTextualContentFromEl(el, false, blockedCSS);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return '';
|
|
176
|
-
}
|
|
177
|
-
|
|
178
140
|
/** Gets the textual content from an element, if any
|
|
179
141
|
* @param {} element
|
|
180
|
-
* @param {} parseParent
|
|
181
|
-
* @param {Array.<String>} blockedCSS
|
|
182
142
|
*/
|
|
183
|
-
_getTextualContentFromEl(element
|
|
184
|
-
return this._parseInnerContent(
|
|
185
|
-
element,
|
|
186
|
-
'',
|
|
187
|
-
100,
|
|
188
|
-
{ value: 0, limit: 100 },
|
|
189
|
-
blockedCSS,
|
|
190
|
-
);
|
|
143
|
+
_getTextualContentFromEl(element) {
|
|
144
|
+
return this._parseInnerContent(element, '', 100, { value: 0, limit: 100 });
|
|
191
145
|
}
|
|
192
146
|
|
|
193
147
|
/** Parse and trim text
|
|
@@ -207,7 +161,7 @@ class ClickMonitor {
|
|
|
207
161
|
if (index > 0) {
|
|
208
162
|
parsedText = `${parsedText.substring(0, index)}...`;
|
|
209
163
|
} else {
|
|
210
|
-
// If
|
|
164
|
+
// If there are no ' ' characters then the text is likely not valid so just
|
|
211
165
|
// return '...'.
|
|
212
166
|
parsedText = '...';
|
|
213
167
|
}
|
|
@@ -217,13 +171,12 @@ class ClickMonitor {
|
|
|
217
171
|
|
|
218
172
|
/**
|
|
219
173
|
* Recursively parses element's inner content and masks blocked css classes
|
|
220
|
-
* @param {
|
|
174
|
+
* @param {NReactNative.Node} element
|
|
221
175
|
* @param {String} text
|
|
222
176
|
* @param {Number} textLimit
|
|
223
177
|
* @param {Object} counter
|
|
224
|
-
* @param {Array.<String>} blockedCSS
|
|
225
178
|
*/
|
|
226
|
-
_parseInnerContent(element, text, textLimit, counter
|
|
179
|
+
_parseInnerContent(element, text, textLimit, counter) {
|
|
227
180
|
/* eslint-disable no-restricted-syntax */
|
|
228
181
|
/* eslint-disable no-param-reassign */
|
|
229
182
|
|
|
@@ -237,7 +190,7 @@ class ClickMonitor {
|
|
|
237
190
|
|
|
238
191
|
counter.value += 1;
|
|
239
192
|
|
|
240
|
-
if (
|
|
193
|
+
if (getBlockedElements().includes(element.memoizedProps.testID)) {
|
|
241
194
|
return `${text}${text ? ' ' : ''}*`;
|
|
242
195
|
}
|
|
243
196
|
|
|
@@ -260,7 +213,7 @@ class ClickMonitor {
|
|
|
260
213
|
/**
|
|
261
214
|
* normalize value and append to the resulting text if not empty
|
|
262
215
|
* @param {String} text
|
|
263
|
-
* @param {Array.<
|
|
216
|
+
* @param {Array.<any>} values
|
|
264
217
|
*/
|
|
265
218
|
_parseAndAppendText(text, values) {
|
|
266
219
|
const goodValues = [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONTENT_TYPE,
|
|
1
|
+
import { CONTENT_TYPE, SEVERITY } from '../constants.js';
|
|
2
2
|
import { isInstanceOf, getMaxSubstringAllowed } from '../utils/function.js';
|
|
3
3
|
import ClientConfig from '../api/clientConfig.js';
|
|
4
4
|
|
|
@@ -285,7 +285,7 @@ class GqlErrorValidator {
|
|
|
285
285
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
286
286
|
`GQL parse error: ${message}`,
|
|
287
287
|
false,
|
|
288
|
-
|
|
288
|
+
SEVERITY.error,
|
|
289
289
|
);
|
|
290
290
|
}
|
|
291
291
|
|
|
@@ -298,7 +298,7 @@ class GqlErrorValidator {
|
|
|
298
298
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
299
299
|
`GQL error validation warning: ${message}`,
|
|
300
300
|
false,
|
|
301
|
-
|
|
301
|
+
SEVERITY.error,
|
|
302
302
|
);
|
|
303
303
|
}
|
|
304
304
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { HUMAN_READABLE_CONTENT_TYPE_REGEX, DEFAULT_WEBSITE_SUBDOMAIN_PATTERN,
|
|
2
|
-
import { getProperGlobalUrl, checkHttpDataCollectionEnabled, getHttpPayloadAllowedURLs } from '../utils/function.js';
|
|
1
|
+
import { HUMAN_READABLE_CONTENT_TYPE_REGEX, DEFAULT_WEBSITE_SUBDOMAIN_PATTERN, SEVERITY, HTTP_BODY_NULL_STRING, HTTP_DATA_REQ_HEADERS_ATT_NAME, HTTP_DATA_PAYLOAD_ATT_NAME, HTTP_DATA_RESP_HEADERS_ATT_NAME, HTTP_DATA_RESP_PAYLOAD_ATT_NAME, HTTP_BODY_DROPPED_LENGTH_MSG, HTTP_BODY_DROPPED_TYPE_MSG, MAX_HTTP_DATA_PAYLOAD_LENGTH, CONTENT_TYPE, CONTENT_LENGTH, BLOCKED_HTTP_HEADER_KEYS, PII_REDACTION_REPLACEMENT_STRING, HTTP_PII_BLOCKING_PATTERNS } from '../constants.js';
|
|
3
2
|
import ClientConfig from '../api/clientConfig.js';
|
|
4
3
|
import StoredMetrics from '../api/storedMetrics.js';
|
|
5
4
|
import { safeFromEntries, iterateObjectRecursively } from '../utils/object.js';
|
|
5
|
+
import { checkHttpDataCollectionEnabled, getHttpPayloadAllowedURLs } from '../utils/function.js';
|
|
6
|
+
import { AppNavigationMonitor } from './AppNavigationMonitor.js';
|
|
6
7
|
|
|
7
8
|
/** @module HTTPDataBundler */
|
|
8
9
|
|
|
@@ -19,7 +20,7 @@ class HTTPDataBundler {
|
|
|
19
20
|
);
|
|
20
21
|
|
|
21
22
|
// pull out the domain hostname
|
|
22
|
-
const initialURL =
|
|
23
|
+
const initialURL = AppNavigationMonitor.getInstance().globalUrl;
|
|
23
24
|
this.initialURLPartsReversed = [];
|
|
24
25
|
if (initialURL && initialURL.length > 0) {
|
|
25
26
|
try {
|
|
@@ -39,7 +40,7 @@ class HTTPDataBundler {
|
|
|
39
40
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
40
41
|
`Unable to determine hostname for initial URL: ${e}`,
|
|
41
42
|
false,
|
|
42
|
-
|
|
43
|
+
SEVERITY.warn,
|
|
43
44
|
);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -50,11 +51,11 @@ class HTTPDataBundler {
|
|
|
50
51
|
const allowedURLs = getHttpPayloadAllowedURLs();
|
|
51
52
|
this.httpDataAllowedAbsoluteRegex = HTTPDataBundler.buildAllowedRegex(
|
|
52
53
|
allowedURLs,
|
|
53
|
-
|
|
54
|
+
'absolute',
|
|
54
55
|
);
|
|
55
56
|
this.httpDataAllowedRelativeRegex = HTTPDataBundler.buildAllowedRegex(
|
|
56
57
|
allowedURLs,
|
|
57
|
-
|
|
58
|
+
'relative',
|
|
58
59
|
);
|
|
59
60
|
// track unique requests and only capture each once
|
|
60
61
|
// TODO: disabled for beta. NOI-4253
|
|
@@ -100,14 +101,14 @@ class HTTPDataBundler {
|
|
|
100
101
|
/**
|
|
101
102
|
* Builds the HTTP payload allowed regexes for full and relative URLs
|
|
102
103
|
* @param allowedURLs A list of allowed URLs
|
|
103
|
-
* @param absolute Use only absolute URLs if true, use only relative URL if false
|
|
104
|
+
* @param {'absolute' | 'relative'} strategy Use only absolute URLs if true, use only relative URL if false
|
|
104
105
|
* @returns a regex of allowed URLs
|
|
105
106
|
*/
|
|
106
|
-
static buildAllowedRegex(allowedURLs,
|
|
107
|
+
static buildAllowedRegex(allowedURLs, strategy) {
|
|
107
108
|
if (!allowedURLs) return null;
|
|
108
109
|
const allowedURLsFiltered = allowedURLs.filter(url => {
|
|
109
110
|
const isAbsolute = HTTPDataBundler.isAbsoluteURL(url);
|
|
110
|
-
return absolute ? isAbsolute : !isAbsolute;
|
|
111
|
+
return strategy === 'absolute' ? isAbsolute : !isAbsolute;
|
|
111
112
|
});
|
|
112
113
|
if (allowedURLsFiltered.length > 0) {
|
|
113
114
|
const lowerCasedURLs = allowedURLsFiltered.map(url =>
|
|
@@ -189,7 +190,7 @@ class HTTPDataBundler {
|
|
|
189
190
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
190
191
|
`Unable to stringify JSON response: ${e}`,
|
|
191
192
|
false,
|
|
192
|
-
|
|
193
|
+
SEVERITY.warn,
|
|
193
194
|
);
|
|
194
195
|
return null;
|
|
195
196
|
}
|
|
@@ -221,7 +222,7 @@ class HTTPDataBundler {
|
|
|
221
222
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
222
223
|
`Unable to determine hostname for request URL: ${e}`,
|
|
223
224
|
false,
|
|
224
|
-
|
|
225
|
+
SEVERITY.warn,
|
|
225
226
|
);
|
|
226
227
|
|
|
227
228
|
return false;
|
|
@@ -568,7 +569,7 @@ ${HTTPDataBundler.getInstance().contentLength(headers)}`;
|
|
|
568
569
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
569
570
|
`Unable to stringify request body: ${e}`,
|
|
570
571
|
false,
|
|
571
|
-
|
|
572
|
+
SEVERITY.warn,
|
|
572
573
|
);
|
|
573
574
|
}
|
|
574
575
|
return null;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NavigationDelegate } from 'react-native-navigation/lib/dist/src/NavigationDelegate';
|
|
2
|
+
import { NavigationIntegration } from '../../types/NavigationIntegration';
|
|
3
|
+
/**
|
|
4
|
+
* react-native-navigation adapter
|
|
5
|
+
*/
|
|
6
|
+
export declare class ReactNativeNavigationIntegration implements NavigationIntegration {
|
|
7
|
+
private stack;
|
|
8
|
+
private stackPointers;
|
|
9
|
+
/**
|
|
10
|
+
* attaches provided listeners to the integration
|
|
11
|
+
*/
|
|
12
|
+
register(navigation: NavigationDelegate, onNavigation: (breadcrumbs: string[]) => void): void;
|
|
13
|
+
/**
|
|
14
|
+
* Listens to ComponentWillAppear events
|
|
15
|
+
* @param onNavigation
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
private getListener;
|
|
19
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-native-navigation adapter
|
|
3
|
+
*/
|
|
4
|
+
class ReactNativeNavigationIntegration {
|
|
5
|
+
stack = [];
|
|
6
|
+
stackPointers = {};
|
|
7
|
+
/**
|
|
8
|
+
* attaches provided listeners to the integration
|
|
9
|
+
*/
|
|
10
|
+
register(navigation, onNavigation) {
|
|
11
|
+
navigation
|
|
12
|
+
.events()
|
|
13
|
+
.registerComponentWillAppearListener(this.getListener(onNavigation));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Listens to ComponentWillAppear events
|
|
17
|
+
* @param onNavigation
|
|
18
|
+
* @private
|
|
19
|
+
*/
|
|
20
|
+
getListener(onNavigation) {
|
|
21
|
+
return (event) => {
|
|
22
|
+
if (this.stackPointers[event.componentName] === undefined) {
|
|
23
|
+
this.stackPointers[event.componentName] = this.stack.push(event.componentName);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.stack.forEach((id, i) => {
|
|
27
|
+
if (i >= this.stackPointers[event.componentName]) {
|
|
28
|
+
delete this.stackPointers[id];
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
this.stack.length = this.stackPointers[event.componentName];
|
|
32
|
+
}
|
|
33
|
+
onNavigation(this.stack.slice(1)); // slice 1 to skip root component
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { ReactNativeNavigationIntegration };
|
|
@@ -4,9 +4,11 @@ import { PageVisitEventHTTP, isHttpCodeFailure } from '../pageVisit/pageVisitEve
|
|
|
4
4
|
import { propWriteableOrMadeWriteable, replace } from '../utils/object.js';
|
|
5
5
|
import 'react-native-device-info';
|
|
6
6
|
import 'react-native-localize';
|
|
7
|
-
import { PV_SEQ_ATT_NAME, XML_HTTP_REQUEST_ERROR_TYPE, GQL_ERROR_TYPE,
|
|
7
|
+
import { PV_SEQ_ATT_NAME, XML_HTTP_REQUEST_ERROR_TYPE, GQL_ERROR_TYPE, SEVERITY, RESPONSE_ERROR_TYPE, HTTP_METHOD_ATT_NAME, HTTP_RESP_CODE_ATT_NAME, URL_ATT_NAME, HTTP_RESP_TIME_ATT_NAME, HTTP_RESP_LENGTH_ATT_NAME, FETCH_EXCEPTION_ERROR_TYPE } from '../constants.js';
|
|
8
8
|
import ClientConfig from '../api/clientConfig.js';
|
|
9
|
+
import 'react-native-uuid';
|
|
9
10
|
import { addSafeEventListener } from '../utils/eventlistener.js';
|
|
11
|
+
import '@react-native-async-storage/async-storage';
|
|
10
12
|
import { HTTPDataBundler } from './httpDataBundler.js';
|
|
11
13
|
import GqlErrorValidator from './gqlErrorValidator.js';
|
|
12
14
|
|
|
@@ -180,7 +182,7 @@ async function _buildHttpEventDataObjectsForFetch(
|
|
|
180
182
|
};
|
|
181
183
|
|
|
182
184
|
let httpData = null;
|
|
183
|
-
// Only get the bodies of the request and response
|
|
185
|
+
// Only get the bodies of the request and response if the URL is one we care about.
|
|
184
186
|
if (HTTPDataBundler.getInstance().shouldContinueForURL(url)) {
|
|
185
187
|
// add response payload length, if any
|
|
186
188
|
if (response && response.headers) {
|
|
@@ -307,7 +309,7 @@ function wrapXMLHTTPSend(proto) {
|
|
|
307
309
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
308
310
|
`Error in XHR.send() wrapper: ${e}`,
|
|
309
311
|
false,
|
|
310
|
-
|
|
312
|
+
SEVERITY.error,
|
|
311
313
|
);
|
|
312
314
|
}
|
|
313
315
|
return originalFunction.call(this, data);
|
|
@@ -349,7 +351,7 @@ function wrapXMLHTTPOpen(proto, shouldHandleLoadend) {
|
|
|
349
351
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
350
352
|
`Unable to set custom properties on XHR object: ${error}`,
|
|
351
353
|
false,
|
|
352
|
-
|
|
354
|
+
SEVERITY.warn,
|
|
353
355
|
);
|
|
354
356
|
}
|
|
355
357
|
|
|
@@ -398,7 +400,7 @@ function wrapXMLHTTPOpen(proto, shouldHandleLoadend) {
|
|
|
398
400
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
399
401
|
`Error in XHR.open() wrapper: ${e}`,
|
|
400
402
|
false,
|
|
401
|
-
|
|
403
|
+
SEVERITY.error,
|
|
402
404
|
);
|
|
403
405
|
}
|
|
404
406
|
return originalFunction.call(this, method, url, async, user, password);
|
|
@@ -433,7 +435,7 @@ function wrapXMLHTTPSetRequestHeader(proto) {
|
|
|
433
435
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
434
436
|
`Error in XHR.setRequestHeader() wrapper: ${error}`,
|
|
435
437
|
false,
|
|
436
|
-
|
|
438
|
+
SEVERITY.error,
|
|
437
439
|
);
|
|
438
440
|
}
|
|
439
441
|
return originalFunction.call(this, header, value);
|
|
@@ -536,7 +538,7 @@ function setupGlobalFetchWrapper() {
|
|
|
536
538
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
537
539
|
`Error in fetch() wrapper: ${e}`,
|
|
538
540
|
false,
|
|
539
|
-
|
|
541
|
+
SEVERITY.error,
|
|
540
542
|
);
|
|
541
543
|
}
|
|
542
544
|
|
|
@@ -611,7 +613,7 @@ function setupGlobalFetchWrapper() {
|
|
|
611
613
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
612
614
|
`Error in custom fetch() callback: ${e}`,
|
|
613
615
|
false,
|
|
614
|
-
|
|
616
|
+
SEVERITY.error,
|
|
615
617
|
);
|
|
616
618
|
}
|
|
617
619
|
})
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { isValidURL, getOnURL,
|
|
2
|
-
import { EVENT_ERROR_TYPE, URL_ATT_NAME, ERROR_EVENT_TYPE, ERROR_EVENT_ERROR_TYPE, CUSTOM_ERROR_EVENT_TYPE, ERROR_EVENT_UNHANDLED_REJECTION_TYPE, ERROR_LOG_EVENT_ERROR_TYPE, FETCH_EXCEPTION_ERROR_TYPE, WRAPPED_EXCEPTION_ERROR_TYPE, GQL_ERROR_TYPE, RESPONSE_ERROR_TYPE, XML_HTTP_REQUEST_ERROR_TYPE, ERROR_SOURCE_ATT_NAME, TYPE_ATT_NAME, JS_EVENT_TYPE, JS_ERROR_ATT_NAME, JS_STACK_FRAMES_ATT_NAME, JS_STACK_FILE_ATT_NAME, JS_STACK_METHOD_ATT_NAME,
|
|
1
|
+
import { isValidURL, getOnURL, getJSStack, stringifyJSON, getMaxSubstringAllowed } from '../../utils/function.js';
|
|
2
|
+
import { EVENT_ERROR_TYPE, URL_ATT_NAME, ERROR_EVENT_TYPE, ERROR_EVENT_ERROR_TYPE, CUSTOM_ERROR_EVENT_TYPE, ERROR_EVENT_UNHANDLED_REJECTION_TYPE, ERROR_LOG_EVENT_ERROR_TYPE, FETCH_EXCEPTION_ERROR_TYPE, WRAPPED_EXCEPTION_ERROR_TYPE, GQL_ERROR_TYPE, RESPONSE_ERROR_TYPE, XML_HTTP_REQUEST_ERROR_TYPE, ERROR_SOURCE_ATT_NAME, TYPE_ATT_NAME, JS_EVENT_TYPE, JS_ERROR_ATT_NAME, JS_STACK_FRAMES_ATT_NAME, JS_STACK_FILE_ATT_NAME, JS_STACK_METHOD_ATT_NAME, SEVERITY, JS_STACK_MESSAGE_ATT_NAME, HTTP_EVENT_TYPE, NOIBU_INPUT_URLS, HTTP_CODE_ATT_NAME, PV_SEQ_ATT_NAME, GQL_EVENT_TYPE, GQL_ERROR_ATT_NAME } from '../../constants.js';
|
|
3
3
|
import blacklisedDomains from './blacklistedDomains.js';
|
|
4
4
|
import ClientConfig from '../../api/clientConfig.js';
|
|
5
5
|
import { InputMonitor } from '../../monitors/inputMonitor.js';
|
|
6
6
|
import StoredMetrics from '../../api/storedMetrics.js';
|
|
7
|
+
import { AppNavigationMonitor } from '../../monitors/AppNavigationMonitor.js';
|
|
7
8
|
|
|
8
9
|
/** @module PageVisitEventError */
|
|
9
10
|
|
|
@@ -69,7 +70,9 @@ function getPVErrorFromXMLHttpRequest(errPayload, httpDataSeqNum) {
|
|
|
69
70
|
*/
|
|
70
71
|
function getPVErrorFromErrorEvent(errPayload) {
|
|
71
72
|
return {
|
|
72
|
-
[URL_ATT_NAME]: getOnURL(
|
|
73
|
+
[URL_ATT_NAME]: getOnURL(
|
|
74
|
+
errPayload.filename || AppNavigationMonitor.getInstance().globalUrl,
|
|
75
|
+
),
|
|
73
76
|
[TYPE_ATT_NAME]: JS_EVENT_TYPE,
|
|
74
77
|
[JS_ERROR_ATT_NAME]: getJSStack(errPayload.error),
|
|
75
78
|
};
|
|
@@ -80,8 +83,7 @@ function getPVErrorFromErrorEvent(errPayload) {
|
|
|
80
83
|
*/
|
|
81
84
|
function getPVErrorFromErrorLog(errPayload) {
|
|
82
85
|
return {
|
|
83
|
-
|
|
84
|
-
[URL_ATT_NAME]: getOnURL(getProperGlobalUrl()), // todo should be current navigation
|
|
86
|
+
[URL_ATT_NAME]: getOnURL(AppNavigationMonitor.getInstance().globalUrl),
|
|
85
87
|
[TYPE_ATT_NAME]: JS_EVENT_TYPE,
|
|
86
88
|
[JS_ERROR_ATT_NAME]: getJSStack(errPayload),
|
|
87
89
|
};
|
|
@@ -218,7 +220,7 @@ function isCollectError(pvError) {
|
|
|
218
220
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
219
221
|
pvError,
|
|
220
222
|
false,
|
|
221
|
-
|
|
223
|
+
SEVERITY.error,
|
|
222
224
|
);
|
|
223
225
|
return true;
|
|
224
226
|
}
|
|
@@ -236,7 +238,7 @@ function isCollectError(pvError) {
|
|
|
236
238
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
237
239
|
pvError,
|
|
238
240
|
false,
|
|
239
|
-
|
|
241
|
+
SEVERITY.error,
|
|
240
242
|
);
|
|
241
243
|
return true;
|
|
242
244
|
}
|
|
@@ -255,7 +257,7 @@ function isCollectError(pvError) {
|
|
|
255
257
|
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
256
258
|
pvError,
|
|
257
259
|
false,
|
|
258
|
-
|
|
260
|
+
SEVERITY.error,
|
|
259
261
|
);
|
|
260
262
|
return true;
|
|
261
263
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { IStorage, StorageValue } from '../types/Storage';
|
|
1
2
|
/**
|
|
2
3
|
* Encapsulates storage api
|
|
3
4
|
*/
|
|
4
|
-
export default class Storage implements
|
|
5
|
+
export default class Storage implements IStorage {
|
|
5
6
|
private readonly _isRNStorageAvailable;
|
|
6
7
|
private readonly _rnStorageError;
|
|
7
8
|
private static _instance;
|
|
@@ -21,7 +22,7 @@ export default class Storage implements Noibu.Storage {
|
|
|
21
22
|
/** Loads value from storage */
|
|
22
23
|
load<R = unknown>(key: string): Promise<R | null>;
|
|
23
24
|
/** Saves value to storage */
|
|
24
|
-
save(key: string, value:
|
|
25
|
+
save(key: string, value: StorageValue): Promise<void>;
|
|
25
26
|
/**
|
|
26
27
|
* Removes value from storage
|
|
27
28
|
* @param {String} key
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
+
import { Provider, StorageValue } from '../types/Storage';
|
|
1
2
|
/**
|
|
2
3
|
* Base implementation for LocalStorage and SessionStorage
|
|
3
4
|
*/
|
|
4
5
|
export default abstract class StorageProvider {
|
|
5
|
-
_provider:
|
|
6
|
+
_provider: Provider;
|
|
6
7
|
/** Creates new instance based on provided provider type */
|
|
7
|
-
constructor(provider:
|
|
8
|
+
constructor(provider: Provider);
|
|
8
9
|
/** Checks if provider is available */
|
|
9
|
-
static isAvailable(resolver: () =>
|
|
10
|
+
static isAvailable(resolver: () => Provider): Promise<{
|
|
10
11
|
result: boolean;
|
|
11
12
|
error: unknown;
|
|
12
13
|
}>;
|
|
13
14
|
/**
|
|
14
15
|
* Loads value from storage
|
|
15
16
|
*/
|
|
16
|
-
load<R =
|
|
17
|
+
load<R = StorageValue>(key: string): Promise<R | null>;
|
|
17
18
|
/** Saves value to storage */
|
|
18
|
-
save(key: string, value:
|
|
19
|
+
save(key: string, value: StorageValue): Promise<void>;
|
|
19
20
|
/**
|
|
20
21
|
* Removes value from storage
|
|
21
22
|
* @param {String} key
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface Config {
|
|
2
|
+
sel: string[];
|
|
3
|
+
scriptID: string;
|
|
4
|
+
njs_version: string;
|
|
5
|
+
nid_cookie: boolean;
|
|
6
|
+
http_data_collection: boolean;
|
|
7
|
+
http_re: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface CustomerConfig {
|
|
10
|
+
blockedElements?: Config['sel'];
|
|
11
|
+
listOfUrlsToCollectHttpDataFrom?: Config['http_re'];
|
|
12
|
+
enableHttpDataCollection?: Config['http_data_collection'];
|
|
13
|
+
domain: UrlConfig['domain'];
|
|
14
|
+
}
|
|
15
|
+
export interface StoredConfig {
|
|
16
|
+
BrowserId: string;
|
|
17
|
+
LastActive?: Date;
|
|
18
|
+
pvId?: string;
|
|
19
|
+
CurrentPageVisitCount: number;
|
|
20
|
+
ClientUnlockTime?: Date;
|
|
21
|
+
DisabledStatus: boolean;
|
|
22
|
+
}
|
|
23
|
+
export type UrlConfig = {
|
|
24
|
+
metroplexSocketBase: string;
|
|
25
|
+
metroplexHTTPBase: string;
|
|
26
|
+
domain: string;
|
|
27
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NavigationDelegate } from 'react-native-navigation/lib/dist/src/NavigationDelegate';
|
|
2
|
+
/**
|
|
3
|
+
* interface enforces constructor signature
|
|
4
|
+
*/
|
|
5
|
+
export interface NavigationIntegration {
|
|
6
|
+
register(navigation: NavigationDelegate, onNavigation: (breadcrumbs: string[]) => void): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type Event = {
|
|
2
|
+
type: string;
|
|
3
|
+
};
|
|
4
|
+
export type EventPayload = Partial<{
|
|
5
|
+
txt: string;
|
|
6
|
+
src: string;
|
|
7
|
+
hid: string;
|
|
8
|
+
tag: string;
|
|
9
|
+
type: string;
|
|
10
|
+
class: string;
|
|
11
|
+
pvp: string;
|
|
12
|
+
}>;
|
|
13
|
+
export interface JError {
|
|
14
|
+
frames: JStackFrame[];
|
|
15
|
+
msg: string;
|
|
16
|
+
}
|
|
17
|
+
export interface JStackFrame {
|
|
18
|
+
column?: number;
|
|
19
|
+
line: string;
|
|
20
|
+
mname: string;
|
|
21
|
+
file: string;
|
|
22
|
+
}
|