noibu-react-native 0.2.2 → 0.2.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/README.md +1 -1
- package/dist/api/clientConfig.js +225 -217
- package/dist/api/helpCode.js +61 -87
- package/dist/api/metroplexSocket.js +460 -463
- package/dist/api/storedPageVisit.js +150 -208
- package/dist/constants.js +10 -2
- package/dist/entry/init.js +65 -63
- package/dist/monitors/{appNavigationMonitor.js → AppNavigationMonitor.js} +12 -22
- 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/http-tools/HTTPDataBundler.js +458 -0
- package/dist/monitors/integrations/react-native-navigation-integration.js +4 -2
- package/dist/pageVisit/EventDebouncer.js +99 -0
- package/dist/pageVisit/pageVisitEventError.js +2 -2
- package/dist/pageVisit/pageVisitEventHTTP.js +79 -93
- package/dist/react/ErrorBoundary.js +18 -15
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +3 -2
- package/dist/sessionRecorder/sessionRecorder.js +152 -151
- package/dist/{api → src/api}/clientConfig.d.ts +2 -2
- package/dist/{api → src/api}/helpCode.d.ts +10 -16
- package/dist/{api → src/api}/metroplexSocket.d.ts +48 -67
- package/dist/{api → src/api}/storedPageVisit.d.ts +12 -21
- package/dist/{constants.d.ts → src/constants.d.ts} +45 -0
- package/dist/{entry → src/entry}/init.d.ts +1 -1
- package/dist/src/monitors/AppNavigationMonitor.d.ts +18 -0
- package/dist/src/monitors/ClickMonitor.d.ts +31 -0
- package/dist/src/monitors/ErrorMonitor.d.ts +63 -0
- package/dist/{monitors/keyboardInputMonitor.d.ts → src/monitors/KeyboardInputMonitor.d.ts} +7 -4
- package/dist/{monitors/pageMonitor.d.ts → src/monitors/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/http-tools/HTTPDataBundler.d.ts +112 -0
- package/dist/{monitors → src/monitors}/integrations/react-native-navigation-integration.d.ts +3 -2
- package/dist/src/pageVisit/EventDebouncer.d.ts +24 -0
- package/dist/{pageVisit → src/pageVisit}/pageVisit.d.ts +1 -1
- package/dist/src/pageVisit/pageVisitEventHTTP.d.ts +25 -0
- package/dist/{sessionRecorder → src/sessionRecorder}/types.d.ts +1 -1
- package/dist/{storage → src/storage}/rnStorageProvider.d.ts +1 -1
- package/dist/{storage → src/storage}/storage.d.ts +2 -2
- package/dist/{storage → src/storage}/storageProvider.d.ts +3 -3
- package/dist/{utils → src/utils}/function.d.ts +27 -7
- package/dist/{utils → src/utils}/object.d.ts +11 -8
- package/dist/src/utils/piiRedactor.d.ts +11 -0
- package/dist/src/utils/polyfills.d.ts +4 -0
- package/dist/storage/rnStorageProvider.js +7 -4
- package/dist/storage/storage.js +43 -35
- package/dist/storage/storageProvider.js +23 -19
- package/dist/types/Config.d.ts +24 -20
- package/dist/types/Metroplex.types.d.ts +73 -0
- package/dist/types/Monitor.d.ts +11 -0
- package/dist/types/Monitor.js +19 -0
- package/dist/types/PageVisit.types.d.ts +8 -0
- package/dist/types/PageVisitErrors.types.d.ts +114 -0
- package/dist/types/PageVisitEvents.types.d.ts +91 -0
- package/dist/types/PageVisitMetrics.types.d.ts +27 -0
- package/dist/types/Storage.d.ts +1 -1
- package/dist/types/StoredPageVisit.types.d.ts +4 -47
- package/dist/types/WrappedObjects.d.ts +6 -0
- package/dist/utils/function.js +110 -77
- package/dist/utils/object.js +59 -6
- package/dist/utils/piiRedactor.js +98 -0
- package/dist/utils/polyfills.js +24 -0
- package/package.json +8 -8
- package/dist/monitors/appNavigationMonitor.d.ts +0 -22
- package/dist/monitors/clickMonitor.d.ts +0 -44
- package/dist/monitors/clickMonitor.js +0 -251
- package/dist/monitors/errorMonitor.d.ts +0 -28
- package/dist/monitors/errorMonitor.js +0 -180
- package/dist/monitors/gqlErrorValidator.d.ts +0 -82
- package/dist/monitors/gqlErrorValidator.js +0 -306
- package/dist/monitors/httpDataBundler.d.ts +0 -161
- package/dist/monitors/httpDataBundler.js +0 -725
- package/dist/monitors/inputMonitor.d.ts +0 -34
- 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.d.ts +0 -10
- package/dist/monitors/requestMonitor.js +0 -401
- package/dist/pageVisit/pageVisitEventHTTP.d.ts +0 -18
- package/dist/types/PageVisit.d.ts +0 -22
- package/dist/types/ReactNative.d.ts +0 -4
- package/dist/types/globals.d.ts +0 -45
- /package/dist/{api → src/api}/inputManager.d.ts +0 -0
- /package/dist/{api → src/api}/storedMetrics.d.ts +0 -0
- /package/dist/{const_matchers.d.ts → src/const_matchers.d.ts} +0 -0
- /package/dist/{entry → src/entry}/index.d.ts +0 -0
- /package/dist/{pageVisit → src/pageVisit}/pageVisitEventError.d.ts +0 -0
- /package/dist/{pageVisit → src/pageVisit}/userStep.d.ts +0 -0
- /package/dist/{react → src/react}/ErrorBoundary.d.ts +0 -0
- /package/dist/{sessionRecorder → src/sessionRecorder}/nativeSessionRecorderSubscription.d.ts +0 -0
- /package/dist/{sessionRecorder → src/sessionRecorder}/sessionRecorder.d.ts +0 -0
- /package/dist/{utils → src/utils}/date.d.ts +0 -0
- /package/dist/{utils → src/utils}/eventlistener.d.ts +0 -0
- /package/dist/{utils → src/utils}/log.d.ts +0 -0
- /package/dist/{utils → src/utils}/performance.d.ts +0 -0
- /package/dist/{utils → src/utils}/stacktrace-parser.d.ts +0 -0
package/dist/utils/function.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { __awaiter } from 'tslib';
|
|
1
2
|
import { Platform } from 'react-native';
|
|
2
3
|
import { parseStack } from './stacktrace-parser.js';
|
|
3
|
-
import { MAX_STRING_LENGTH, MAX_BEACON_PAYLOAD_SIZE, REQUIRED_DATA_PROCESSING_URLS, PII_EMAIL_PATTERN, PII_REDACTION_REPLACEMENT_STRING, PII_DIGIT_PATTERN, DEFAULT_STACK_FRAME_FIELD_VALUE } from '../constants.js';
|
|
4
|
+
import { MAX_STRING_LENGTH, MAX_BEACON_PAYLOAD_SIZE, REQUIRED_DATA_PROCESSING_URLS, PII_EMAIL_PATTERN, PII_REDACTION_REPLACEMENT_STRING, PII_DIGIT_PATTERN, JS_STACK_LINE_ATT_NAME, DEFAULT_STACK_FRAME_FIELD_VALUE, JS_STACK_METHOD_ATT_NAME, JS_STACK_FILE_ATT_NAME } from '../constants.js';
|
|
4
5
|
import { noibuLog } from './log.js';
|
|
5
6
|
import { unwrapNoibuWrapped } from './object.js';
|
|
6
7
|
|
|
@@ -8,9 +9,9 @@ import { unwrapNoibuWrapped } from './object.js';
|
|
|
8
9
|
* Returns a stack trace frame with default filed values
|
|
9
10
|
*/
|
|
10
11
|
const getDefaultFrame = () => ({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
[JS_STACK_LINE_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
13
|
+
[JS_STACK_METHOD_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
14
|
+
[JS_STACK_FILE_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
14
15
|
});
|
|
15
16
|
/**
|
|
16
17
|
* returns a string that satisfies a max length
|
|
@@ -27,7 +28,6 @@ function getMaxSubstringAllowed(stringToVerify, length = MAX_STRING_LENGTH) {
|
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
29
30
|
* Processes the raw stack frames and creates a readable stack in a safe manner
|
|
30
|
-
* @param {StackFrame[]} rawFrames
|
|
31
31
|
*/
|
|
32
32
|
function processFrames(rawFrames) {
|
|
33
33
|
return rawFrames.map(frame => {
|
|
@@ -60,13 +60,7 @@ function processFrames(rawFrames) {
|
|
|
60
60
|
* @param errObj error to extract stack from
|
|
61
61
|
*/
|
|
62
62
|
function getJSStack(errObj) {
|
|
63
|
-
let frames = [
|
|
64
|
-
{
|
|
65
|
-
line: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
66
|
-
mname: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
67
|
-
file: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
68
|
-
},
|
|
69
|
-
];
|
|
63
|
+
let frames = [getDefaultFrame()];
|
|
70
64
|
// if the errObj type is not an object or null
|
|
71
65
|
// return a default frame
|
|
72
66
|
if (typeof errObj !== 'object' || !errObj || !errObj.stack) {
|
|
@@ -85,10 +79,9 @@ function getJSStack(errObj) {
|
|
|
85
79
|
/**
|
|
86
80
|
* Checks if possiblyStacktrace has any stack frames present
|
|
87
81
|
*/
|
|
88
|
-
function isStackTrace(
|
|
82
|
+
function isStackTrace(_possiblyStacktrace) {
|
|
89
83
|
try {
|
|
90
|
-
// todo
|
|
91
|
-
// return parseStack(possiblyStacktrace).length > 0;
|
|
84
|
+
// todo implement for react native
|
|
92
85
|
return false;
|
|
93
86
|
}
|
|
94
87
|
catch (e) {
|
|
@@ -126,52 +119,54 @@ function stringifyJSON(jsonObject) {
|
|
|
126
119
|
* @param timeout
|
|
127
120
|
* @param sendAndForget
|
|
128
121
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// a send-and-forget request is made by using the beacon API (fetch + keepalive)
|
|
136
|
-
if (sendAndForget) {
|
|
137
|
-
const stringData = stringifyJSON(data);
|
|
138
|
-
const currentPayloadSize = new Blob([stringData]).size;
|
|
139
|
-
// if we have a large object or fetch is not available, we skip sending the message
|
|
140
|
-
if (currentPayloadSize > MAX_BEACON_PAYLOAD_SIZE) {
|
|
141
|
-
return Promise.resolve();
|
|
122
|
+
function makeRequest(method, url, data, headers, timeout, sendAndForget) {
|
|
123
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
const ua = Object.keys(headers).findLast(k => k.toLowerCase() === 'user-agent');
|
|
125
|
+
const headersWithUa = Object.assign({}, headers);
|
|
126
|
+
if (!headers[ua]) {
|
|
127
|
+
headersWithUa['User-Agent'] = yield getUserAgent();
|
|
142
128
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
return new Promise((resolve, reject) => {
|
|
152
|
-
const xhr = new XMLHttpRequest();
|
|
153
|
-
xhr.open(method, url);
|
|
154
|
-
xhr.timeout = timeout;
|
|
155
|
-
Object.keys(headersWithUa).forEach(header => {
|
|
156
|
-
xhr.setRequestHeader(header, headers[header]);
|
|
157
|
-
});
|
|
158
|
-
xhr.onload = () => {
|
|
159
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
160
|
-
resolve(xhr.response);
|
|
129
|
+
// a send-and-forget request is made by using the beacon API (fetch + keepalive)
|
|
130
|
+
if (sendAndForget) {
|
|
131
|
+
const stringData = stringifyJSON(data);
|
|
132
|
+
const currentPayloadSize = new Blob([stringData]).size;
|
|
133
|
+
// if we have a large object or fetch is not available, we skip sending the message
|
|
134
|
+
if (currentPayloadSize > MAX_BEACON_PAYLOAD_SIZE) {
|
|
135
|
+
return Promise.resolve();
|
|
161
136
|
}
|
|
162
|
-
|
|
137
|
+
return unwrapNoibuWrapped(fetch)(url, {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers: headersWithUa,
|
|
140
|
+
body: stringifyJSON(data),
|
|
141
|
+
// keep alive outlives the current page, its the same as beacon
|
|
142
|
+
keepalive: true,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
const xhr = new XMLHttpRequest();
|
|
147
|
+
xhr.open(method, url);
|
|
148
|
+
xhr.timeout = timeout;
|
|
149
|
+
Object.keys(headersWithUa).forEach(header => {
|
|
150
|
+
xhr.setRequestHeader(header, headers[header]);
|
|
151
|
+
});
|
|
152
|
+
xhr.onload = () => {
|
|
153
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
154
|
+
resolve(xhr.response);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
reject(new Error(`Custom Request failed: ${xhr.statusText}`));
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
xhr.onerror = () => {
|
|
163
161
|
reject(new Error(`Custom Request failed: ${xhr.statusText}`));
|
|
162
|
+
};
|
|
163
|
+
if (data) {
|
|
164
|
+
xhr.send(stringifyJSON(data));
|
|
164
165
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
};
|
|
169
|
-
if (data) {
|
|
170
|
-
xhr.send(stringifyJSON(data));
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
xhr.send();
|
|
174
|
-
}
|
|
166
|
+
else {
|
|
167
|
+
xhr.send();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
175
170
|
});
|
|
176
171
|
}
|
|
177
172
|
/**
|
|
@@ -190,20 +185,22 @@ let userAgentCache = '';
|
|
|
190
185
|
* Fakes the user agent retrieval, since there are no good libraries that support both expo and plain RN
|
|
191
186
|
* caches the result for the session
|
|
192
187
|
*/
|
|
193
|
-
|
|
194
|
-
|
|
188
|
+
function getUserAgent() {
|
|
189
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
190
|
+
if (userAgentCache) {
|
|
191
|
+
return userAgentCache;
|
|
192
|
+
}
|
|
193
|
+
noibuLog('getUserAgent start');
|
|
194
|
+
if (Platform.OS === 'android') {
|
|
195
|
+
const { Brand, Model, Release } = Platform.constants;
|
|
196
|
+
userAgentCache = `Mozilla/5.0 (Linux; Android ${Release}; ${Brand} ${Model}; React Native ${Platform.Version}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36`;
|
|
197
|
+
}
|
|
198
|
+
else if (Platform.OS === 'ios') {
|
|
199
|
+
userAgentCache = `Mozilla/5.0 (iPhone; CPU iPhone OS ${Platform.constants.osVersion} like Mac OS X; React Native ${Platform.Version}) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1`;
|
|
200
|
+
}
|
|
201
|
+
noibuLog('getUserAgent end', { userAgentCache });
|
|
195
202
|
return userAgentCache;
|
|
196
|
-
}
|
|
197
|
-
noibuLog('getUserAgent start');
|
|
198
|
-
if (Platform.OS === 'android') {
|
|
199
|
-
const { Brand, Model, Release } = Platform.constants;
|
|
200
|
-
userAgentCache = `Mozilla/5.0 (Linux; Android ${Release}; ${Brand} ${Model}; React Native ${Platform.Version}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36`;
|
|
201
|
-
}
|
|
202
|
-
else if (Platform.OS === 'ios') {
|
|
203
|
-
userAgentCache = `Mozilla/5.0 (iPhone; CPU iPhone OS ${Platform.constants.osVersion} like Mac OS X; React Native ${Platform.Version}) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1`;
|
|
204
|
-
}
|
|
205
|
-
noibuLog('getUserAgent end', { userAgentCache });
|
|
206
|
-
return userAgentCache;
|
|
203
|
+
});
|
|
207
204
|
}
|
|
208
205
|
/**
|
|
209
206
|
* isInvalidURLConfig will verify that Collect is being initializes with
|
|
@@ -272,14 +269,50 @@ function isInstanceOf(instance, type) {
|
|
|
272
269
|
/**
|
|
273
270
|
* To grab the video recorder type based on the device we run the app on.
|
|
274
271
|
*/
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
272
|
+
function getVideoRecorderType() {
|
|
273
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
274
|
+
if (Platform.OS === 'android') {
|
|
275
|
+
return 'AndroidNative';
|
|
276
|
+
}
|
|
277
|
+
if (Platform.OS === 'ios') {
|
|
278
|
+
return 'IOSNative';
|
|
279
|
+
}
|
|
280
|
+
return '';
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
/** String.trim, but safe */
|
|
284
|
+
function safeTrim(text) {
|
|
285
|
+
if (typeof text !== 'string') {
|
|
286
|
+
return '';
|
|
278
287
|
}
|
|
279
|
-
|
|
280
|
-
|
|
288
|
+
return text.trim();
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Tries to get the stack trace from the given error object and returns it.
|
|
292
|
+
* If the error object does not have a stack trace, an empty string is returned.
|
|
293
|
+
*
|
|
294
|
+
* @param {Error} error - The error object from which to retrieve the stack trace.
|
|
295
|
+
* @returns {string} The stack trace of the error, if available. Otherwise, an empty string is returned.
|
|
296
|
+
*/
|
|
297
|
+
function tryGetStackTrace(error) {
|
|
298
|
+
let result = '';
|
|
299
|
+
try {
|
|
300
|
+
if (error && error.stack) {
|
|
301
|
+
result = `(stack: ${error.stack})`;
|
|
302
|
+
}
|
|
281
303
|
}
|
|
282
|
-
|
|
304
|
+
catch (_a) {
|
|
305
|
+
// non-standard property - that's fine
|
|
306
|
+
}
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Checks whether the given value is a string or an instance of String.
|
|
311
|
+
* @param {*} value - The value to be checked.
|
|
312
|
+
* @returns {boolean} Returns true if the value is a string or an instance of String, otherwise returns false.
|
|
313
|
+
*/
|
|
314
|
+
function isString(value) {
|
|
315
|
+
return typeof value === 'string' || value instanceof String;
|
|
283
316
|
}
|
|
284
317
|
|
|
285
|
-
export { asString, getJSStack, getMaxSubstringAllowed, getUserAgent, getVideoRecorderType, isInstanceOf, isInvalidURLConfig, isNoibuJSAlreadyLoaded, isStackTrace, isValidURL, makeRequest, maskTextInput, processFrames, stringifyJSON };
|
|
318
|
+
export { asString, getJSStack, getMaxSubstringAllowed, getUserAgent, getVideoRecorderType, isInstanceOf, isInvalidURLConfig, isNoibuJSAlreadyLoaded, isStackTrace, isString, isValidURL, makeRequest, maskTextInput, processFrames, safeTrim, stringifyJSON, tryGetStackTrace };
|
package/dist/utils/object.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
1
2
|
/** @module Object */
|
|
2
3
|
/**
|
|
3
4
|
* Replaces an attribute value found in an object with another value
|
|
@@ -51,9 +52,7 @@ function unwrapNoibuWrapped(anything) {
|
|
|
51
52
|
* Checks whether the prototype's property is writeable. If it is not,
|
|
52
53
|
* checks whether the property can be made writeable. If it can, it is
|
|
53
54
|
* set to writeable.
|
|
54
|
-
*
|
|
55
|
-
* @param {string} property
|
|
56
|
-
* @returns {boolean} Whether the property on the prototype is (or is now) writeable
|
|
55
|
+
* returns Whether the property on the prototype is (or is now) writeable
|
|
57
56
|
*/
|
|
58
57
|
const propWriteableOrMadeWriteable = (proto, property) => {
|
|
59
58
|
if (!proto ||
|
|
@@ -65,9 +64,9 @@ const propWriteableOrMadeWriteable = (proto, property) => {
|
|
|
65
64
|
// has under the open property
|
|
66
65
|
const propDescriptor = Object.getOwnPropertyDescriptor(proto, property);
|
|
67
66
|
// Checking if the open property is read-only
|
|
68
|
-
if (!propDescriptor
|
|
67
|
+
if (!(propDescriptor === null || propDescriptor === void 0 ? void 0 : propDescriptor.writable)) {
|
|
69
68
|
// Checking if we can write to it
|
|
70
|
-
if (propDescriptor
|
|
69
|
+
if (propDescriptor === null || propDescriptor === void 0 ? void 0 : propDescriptor.configurable) {
|
|
71
70
|
// Making it writable to wrap it
|
|
72
71
|
Object.defineProperty(proto, property, {
|
|
73
72
|
writable: true,
|
|
@@ -117,5 +116,59 @@ const iterateObjectRecursively = (instance, visit, limit = { depth: 5 }) => {
|
|
|
117
116
|
};
|
|
118
117
|
iterate(instance, 1);
|
|
119
118
|
};
|
|
119
|
+
const safeEntries = (obj) => {
|
|
120
|
+
const collection = [];
|
|
121
|
+
if (!obj) {
|
|
122
|
+
return collection;
|
|
123
|
+
}
|
|
124
|
+
if (obj instanceof Headers) {
|
|
125
|
+
obj.forEach((value, key) => {
|
|
126
|
+
collection.push([key, value]);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else if (typeof obj === 'object') {
|
|
130
|
+
collection.push(...Object.entries(obj));
|
|
131
|
+
}
|
|
132
|
+
return collection;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Determines if the given instance is iterable.
|
|
136
|
+
* An object is considered iterable if it has a method whose key is `Symbol.iterator`.
|
|
137
|
+
* This method checks if `instance[Symbol.iterator]` is a function, and if so, returns `true`.
|
|
138
|
+
* If `instance` is `null` or `undefined`, or if accessing `instance[Symbol.iterator]` throws an error,
|
|
139
|
+
* this method returns `false`.
|
|
140
|
+
*/
|
|
141
|
+
function isIterable(instance) {
|
|
142
|
+
if (!instance) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
return typeof instance[Symbol.iterator] === 'function';
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Replaces the behaviour of Object.fromEntries() as it is not supported on all browsers
|
|
154
|
+
* @param {Iterable} entries The iterable to parse into an object
|
|
155
|
+
* @returns An object containing the same key/values as the iterable passed
|
|
156
|
+
*/
|
|
157
|
+
const safeFromEntries = (entries) => {
|
|
158
|
+
if (!isIterable(entries)) {
|
|
159
|
+
return {};
|
|
160
|
+
}
|
|
161
|
+
const obj = {};
|
|
162
|
+
try {
|
|
163
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
164
|
+
for (const [key, value] of entries) {
|
|
165
|
+
obj[key] = value;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
// noting we can do - give up
|
|
170
|
+
}
|
|
171
|
+
return obj;
|
|
172
|
+
};
|
|
120
173
|
|
|
121
|
-
export { iterateObjectRecursively, propWriteableOrMadeWriteable, replace, unwrapNoibuWrapped };
|
|
174
|
+
export { iterateObjectRecursively, propWriteableOrMadeWriteable, replace, safeEntries, safeFromEntries, unwrapNoibuWrapped };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { iterateObjectRecursively } from './object.js';
|
|
2
|
+
import { HTTP_PII_BLOCKING_PATTERNS, PII_REDACTION_REPLACEMENT_STRING } from '../constants.js';
|
|
3
|
+
import { stringifyJSON } from './function.js';
|
|
4
|
+
|
|
5
|
+
const fuzzyFieldsToRedact = [
|
|
6
|
+
'password',
|
|
7
|
+
'address',
|
|
8
|
+
'credit',
|
|
9
|
+
'postal',
|
|
10
|
+
'token',
|
|
11
|
+
'phone',
|
|
12
|
+
'mobile',
|
|
13
|
+
'expiry',
|
|
14
|
+
'account',
|
|
15
|
+
'email',
|
|
16
|
+
];
|
|
17
|
+
const exactFieldsToRedact = [
|
|
18
|
+
'firstname',
|
|
19
|
+
'lastname',
|
|
20
|
+
'street',
|
|
21
|
+
'fullname',
|
|
22
|
+
'creditcard',
|
|
23
|
+
'postcode',
|
|
24
|
+
'zipcode',
|
|
25
|
+
'city',
|
|
26
|
+
'town',
|
|
27
|
+
'county',
|
|
28
|
+
'cc',
|
|
29
|
+
'cardtype',
|
|
30
|
+
'cardnumber',
|
|
31
|
+
'email',
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Redacts one string, returns redacted value or false if nothing to redact
|
|
35
|
+
*/
|
|
36
|
+
function shouldRedact(field) {
|
|
37
|
+
// check for exact and fuzzy matches
|
|
38
|
+
return (exactFieldsToRedact.some(v => field === v) ||
|
|
39
|
+
fuzzyFieldsToRedact.some(v => field.indexOf(v) >= 0));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Try to parse content as a JSON object and
|
|
43
|
+
* iterate it recursively removing PII.
|
|
44
|
+
* Returns original content if nothing was changes or error is thrown.
|
|
45
|
+
* @param {String} content
|
|
46
|
+
*/
|
|
47
|
+
function tryParseObjectAndRemovePII(content) {
|
|
48
|
+
const first = content[0];
|
|
49
|
+
const isParsable = first === '{' || first === '[';
|
|
50
|
+
if (!isParsable) {
|
|
51
|
+
return content;
|
|
52
|
+
}
|
|
53
|
+
let redacted = false;
|
|
54
|
+
try {
|
|
55
|
+
const instance = JSON.parse(content);
|
|
56
|
+
iterateObjectRecursively(instance, (current, property) => {
|
|
57
|
+
if (typeof property !== 'string') {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const propertyLowerCase = property.toLowerCase();
|
|
61
|
+
const shouldRedactField = shouldRedact(propertyLowerCase);
|
|
62
|
+
if (shouldRedactField) {
|
|
63
|
+
redacted = true;
|
|
64
|
+
return PII_REDACTION_REPLACEMENT_STRING;
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
});
|
|
68
|
+
return redacted ? stringifyJSON(instance) : content;
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
return content;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Takes a string and redacts any PII we're able to detect
|
|
76
|
+
*
|
|
77
|
+
* content - the string from which we want to redact PII
|
|
78
|
+
* returns the string with any PII we're able to detect redacted.
|
|
79
|
+
*/
|
|
80
|
+
function removePII(content) {
|
|
81
|
+
/* eslint-disable no-param-reassign */
|
|
82
|
+
// In order to be fail-safe, let's return null if we won't be able to remove PII.
|
|
83
|
+
if (typeof content !== 'string') {
|
|
84
|
+
return '';
|
|
85
|
+
}
|
|
86
|
+
if (!content.length) {
|
|
87
|
+
return content;
|
|
88
|
+
}
|
|
89
|
+
content = tryParseObjectAndRemovePII(content);
|
|
90
|
+
// Go through each of the PII matching patterns
|
|
91
|
+
HTTP_PII_BLOCKING_PATTERNS.forEach(pattern => {
|
|
92
|
+
// Replace all matches with the appropriate number of asterisks
|
|
93
|
+
content = content.replace(pattern, PII_REDACTION_REPLACEMENT_STRING);
|
|
94
|
+
});
|
|
95
|
+
return content;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { removePII, shouldRedact };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
var _a, _b;
|
|
2
|
+
/**
|
|
3
|
+
* In case Promise.all is not available, use this polyfill
|
|
4
|
+
*/
|
|
5
|
+
const promiseAll = ((_b = (_a = Promise === null || Promise === void 0 ? void 0 : Promise.all) === null || _a === void 0 ? void 0 : _a.bind) === null || _b === void 0 ? void 0 : _b.call(_a, Promise)) ||
|
|
6
|
+
((values) => {
|
|
7
|
+
return new Promise(function (resolve, reject) {
|
|
8
|
+
const result = [];
|
|
9
|
+
let total = 0;
|
|
10
|
+
values.forEach((item, index) => {
|
|
11
|
+
Promise.resolve(item)
|
|
12
|
+
.then(res => {
|
|
13
|
+
result[index] = res;
|
|
14
|
+
total += 1;
|
|
15
|
+
if (total === values.length) {
|
|
16
|
+
resolve(result);
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
.catch(reject);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export { promiseAll };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noibu-react-native",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"build:dev": "node ./build.watch.js",
|
|
19
19
|
"prepare": "npm run clean; npm run build;",
|
|
20
20
|
"test": "jest --coverage --passWithNoTests",
|
|
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"
|
|
@@ -38,13 +39,12 @@
|
|
|
38
39
|
"@react-native-async-storage/async-storage": "^1.19.0",
|
|
39
40
|
"fflate": "^0.8.2",
|
|
40
41
|
"react": ">=16.13.1 <=18",
|
|
41
|
-
"react-native": "
|
|
42
|
+
"react-native": ">=0.63.0",
|
|
42
43
|
"react-native-navigation": ">=2.29.0",
|
|
43
44
|
"react-native-url-polyfill": "^1.3.0",
|
|
44
45
|
"react-native-uuid": "^2.0.1"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@babel/preset-env": "^7.24.8",
|
|
48
48
|
"@jest/globals": "^29.7.0",
|
|
49
49
|
"@react-native-async-storage/async-storage": "1.19.0",
|
|
50
50
|
"@rollup/plugin-commonjs": "^25.0.0",
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
"@types/jest": "^29.5.1",
|
|
57
57
|
"@types/node": "^20.2.3",
|
|
58
58
|
"@types/react": "16.14.62",
|
|
59
|
-
"@types/react-native": "0.63.50",
|
|
60
59
|
"@types/react-test-renderer": "^18.0.0",
|
|
61
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
61
|
+
"@typescript-eslint/parser": "^6.21.0",
|
|
62
62
|
"babel-jest": "^29.7.0",
|
|
63
63
|
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
|
64
64
|
"codecov": "^3.8.3",
|
|
@@ -72,12 +72,12 @@
|
|
|
72
72
|
"jest": "^29.7.0",
|
|
73
73
|
"prettier": "^2.8.8",
|
|
74
74
|
"react": "16.13.1",
|
|
75
|
-
"react-native": "
|
|
76
|
-
"react-native-navigation": "
|
|
75
|
+
"react-native": "0.63.0",
|
|
76
|
+
"react-native-navigation": "^7.40.3",
|
|
77
77
|
"react-native-url-polyfill": "1.3.0",
|
|
78
78
|
"react-native-uuid": "2.0.1",
|
|
79
79
|
"rimraf": "^5.0.1",
|
|
80
|
-
"rollup": "^
|
|
80
|
+
"rollup": "^4.27.4",
|
|
81
81
|
"rollup-plugin-dotenv": "^0.5.0",
|
|
82
82
|
"ts-jest": "^29.2.3",
|
|
83
83
|
"typescript": "^5.5.3"
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Attaches corresponding listener to the passed navigation integration
|
|
3
|
-
*/
|
|
4
|
-
export declare class AppNavigationMonitor {
|
|
5
|
-
private static instance;
|
|
6
|
-
/**
|
|
7
|
-
* guesses which navigation is used in app, and registers a listener if found
|
|
8
|
-
*/
|
|
9
|
-
constructor();
|
|
10
|
-
/**
|
|
11
|
-
* handler for updating navigation breadcrumbs and notifying metro of location change
|
|
12
|
-
*/
|
|
13
|
-
onNavigation(breadcrumbs: string[]): void;
|
|
14
|
-
/**
|
|
15
|
-
* Gets the singleton instance
|
|
16
|
-
*/
|
|
17
|
-
static getInstance(): AppNavigationMonitor;
|
|
18
|
-
/**
|
|
19
|
-
* Called when the event needs to be emitted
|
|
20
|
-
*/
|
|
21
|
-
private reportLocationChange;
|
|
22
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/** Monitors the clicks which we capture and later process */
|
|
2
|
-
export class ClickMonitor {
|
|
3
|
-
/** gets the singleton instance
|
|
4
|
-
* @returns {ClickMonitor}
|
|
5
|
-
*/
|
|
6
|
-
static getInstance(): ClickMonitor;
|
|
7
|
-
/**
|
|
8
|
-
* Gets selectors to prevent those elements from being recorded
|
|
9
|
-
*/
|
|
10
|
-
static getBlockedElements(): string[];
|
|
11
|
-
textCapturedWhiteListRegex: RegExp;
|
|
12
|
-
htmlIDAllowListRegex: RegExp;
|
|
13
|
-
/** Starts monitoring clicks on every Press-able component */
|
|
14
|
-
monitorClicks(): void;
|
|
15
|
-
/**
|
|
16
|
-
* Handles a single click event
|
|
17
|
-
* @param {{ _targetInst: RNNode }} event
|
|
18
|
-
*/
|
|
19
|
-
_onClickHandle(event: {
|
|
20
|
-
_targetInst: RNNode;
|
|
21
|
-
}): void;
|
|
22
|
-
/** Gets the textual content from an element, if any
|
|
23
|
-
* @param {} element
|
|
24
|
-
*/
|
|
25
|
-
_getTextualContentFromEl(element: any): string;
|
|
26
|
-
/** Parse and trim text
|
|
27
|
-
* @param {} text
|
|
28
|
-
*/
|
|
29
|
-
_trimText(text: any): any;
|
|
30
|
-
/**
|
|
31
|
-
* Recursively parses element's inner content and masks blocked css classes
|
|
32
|
-
* @param {NReactNative.Node} element
|
|
33
|
-
* @param {String} text
|
|
34
|
-
* @param {Number} textLimit
|
|
35
|
-
* @param {Object} counter
|
|
36
|
-
*/
|
|
37
|
-
_parseInnerContent(element: NReactNative.Node, text: string, textLimit: number, counter: Object): string;
|
|
38
|
-
/**
|
|
39
|
-
* normalize value and append to the resulting text if not empty
|
|
40
|
-
* @param {String} text
|
|
41
|
-
* @param {Array.<any>} values
|
|
42
|
-
*/
|
|
43
|
-
_parseAndAppendText(text: string, values: Array<any>): string;
|
|
44
|
-
}
|