noibu-react-native 0.0.9 → 0.1.2
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 +0 -4
- package/dist/api/clientConfig.js +9 -1
- package/dist/api/inputManager.js +1 -1
- package/dist/api/metroplexSocket.js +15 -3
- package/dist/api/storedMetrics.js +2 -1
- package/dist/constants.d.ts +8 -0
- package/dist/constants.js +10 -2
- package/dist/entry/index.js +2 -0
- package/dist/entry/init.js +7 -0
- package/dist/monitors/clickMonitor.js +1 -1
- package/dist/monitors/errorMonitor.d.ts +5 -2
- package/dist/monitors/errorMonitor.js +54 -18
- package/dist/monitors/httpDataBundler.js +62 -15
- package/dist/monitors/keyboardInputMonitor.js +1 -1
- package/dist/monitors/requestMonitor.js +47 -310
- package/dist/pageVisit/{pageVisitEventError/pageVisitEventError.js → pageVisitEventError.js} +22 -22
- package/dist/pageVisit/{pageVisitEventHTTP/pageVisitEventHTTP.js → pageVisitEventHTTP.js} +15 -7
- package/dist/pageVisit/{userStep/userStep.js → userStep.js} +2 -2
- package/dist/react/ErrorBoundary.js +1 -1
- package/dist/types/globals.d.ts +12 -2
- package/dist/utils/function.js +9 -1
- package/dist/utils/log.d.ts +5 -0
- package/dist/utils/log.js +15 -0
- package/dist/utils/object.d.ts +41 -0
- package/dist/utils/object.js +85 -108
- package/dist/utils/performance.d.ts +6 -0
- package/dist/utils/performance.js +1 -2
- package/dist/utils/stacktrace-parser.js +1 -49
- package/package.json +1 -1
- package/dist/pageVisit/pageVisitEventError/blacklistedDomains.js +0 -9
|
@@ -1,78 +1,16 @@
|
|
|
1
1
|
import 'react-native/Libraries/Network/fetch';
|
|
2
|
-
import { saveErrorToPagevisit } from '../pageVisit/pageVisitEventError
|
|
3
|
-
import { PageVisitEventHTTP, isHttpCodeFailure } from '../pageVisit/pageVisitEventHTTP
|
|
2
|
+
import { saveErrorToPagevisit } from '../pageVisit/pageVisitEventError.js';
|
|
3
|
+
import { PageVisitEventHTTP, isHttpCodeFailure } from '../pageVisit/pageVisitEventHTTP.js';
|
|
4
4
|
import { propWriteableOrMadeWriteable, replace } from '../utils/object.js';
|
|
5
|
-
import '
|
|
6
|
-
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';
|
|
7
|
-
import { addSafeEventListener } from '../utils/eventlistener.js';
|
|
8
|
-
import { HTTPDataBundler } from './httpDataBundler.js';
|
|
5
|
+
import { SEVERITY, PV_SEQ_ATT_NAME, RESPONSE_ERROR_TYPE, GQL_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';
|
|
9
6
|
import ClientConfig from '../api/clientConfig.js';
|
|
7
|
+
import { HTTPDataBundler } from './httpDataBundler.js';
|
|
10
8
|
import GqlErrorValidator from './gqlErrorValidator.js';
|
|
9
|
+
import { noibuLog } from '../utils/log.js';
|
|
11
10
|
|
|
12
11
|
/** @module RequestMonitor */
|
|
13
12
|
// todo no idea why this works but it fixes fetch replacement
|
|
14
13
|
|
|
15
|
-
/**
|
|
16
|
-
* Takes an XHR object, method, url, response time, and payload, and returns the parameters to pass
|
|
17
|
-
* the PageVisitEventHTTP constructor.
|
|
18
|
-
* @param {object} xhrObj
|
|
19
|
-
* @param {string} method
|
|
20
|
-
* @param {string} url
|
|
21
|
-
* @param {number} respTime
|
|
22
|
-
* @param {*} rawPayload
|
|
23
|
-
* @returns HTTP event and data
|
|
24
|
-
*/
|
|
25
|
-
function _buildHttpEventDataObjectsForXHR(
|
|
26
|
-
xhrObj,
|
|
27
|
-
method,
|
|
28
|
-
url,
|
|
29
|
-
respTime,
|
|
30
|
-
rawPayload = null,
|
|
31
|
-
) {
|
|
32
|
-
const httpEvent = {
|
|
33
|
-
[HTTP_METHOD_ATT_NAME]: method,
|
|
34
|
-
[HTTP_RESP_CODE_ATT_NAME]: xhrObj.status,
|
|
35
|
-
[URL_ATT_NAME]: url,
|
|
36
|
-
[HTTP_RESP_TIME_ATT_NAME]: respTime,
|
|
37
|
-
};
|
|
38
|
-
let httpData = null;
|
|
39
|
-
|
|
40
|
-
if (HTTPDataBundler.getInstance().shouldContinueForURL(url)) {
|
|
41
|
-
const responseHeaders = HTTPDataBundler.headersMapFromString(
|
|
42
|
-
xhrObj.getAllResponseHeaders(),
|
|
43
|
-
);
|
|
44
|
-
// add response payload length, if any
|
|
45
|
-
const contentLength =
|
|
46
|
-
HTTPDataBundler.getInstance().contentLength(responseHeaders);
|
|
47
|
-
if (contentLength > 0) {
|
|
48
|
-
httpEvent[HTTP_RESP_LENGTH_ATT_NAME] = contentLength;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
let responseBody = '';
|
|
52
|
-
if (HTTPDataBundler.getInstance().shouldCollectPayloadForURL(url)) {
|
|
53
|
-
// Set descriptive body if content is wrong type or too long.
|
|
54
|
-
const droppedResponseText =
|
|
55
|
-
HTTPDataBundler.getInstance().dropPayloadIfNecessaryFromHeaders(
|
|
56
|
-
responseHeaders,
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
responseBody =
|
|
60
|
-
droppedResponseText ||
|
|
61
|
-
HTTPDataBundler.responseStringFromXHRResponseType(xhrObj);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
httpData = HTTPDataBundler.getInstance().bundleHTTPData(
|
|
65
|
-
url,
|
|
66
|
-
xhrObj.noibuRequestHeaders,
|
|
67
|
-
rawPayload,
|
|
68
|
-
responseHeaders,
|
|
69
|
-
responseBody,
|
|
70
|
-
method,
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
return [httpEvent, httpData];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
14
|
/**
|
|
77
15
|
* Called in the fetch() wrapper function, this specifically gets called after the promises which
|
|
78
16
|
* return the request and response body complete.
|
|
@@ -83,6 +21,7 @@ function _buildHttpEventDataObjectsForXHR(
|
|
|
83
21
|
* [0] is the response body, [1] is an optional response body.
|
|
84
22
|
* @param {string} url the URL of the request
|
|
85
23
|
* @param {string} method the method used in the fetch call
|
|
24
|
+
* @param {boolean} shouldCollectPayload
|
|
86
25
|
* @returns the return from the bundleHTTPData() call
|
|
87
26
|
*/
|
|
88
27
|
function _handleFetchOnPromiseCompletion(
|
|
@@ -92,7 +31,9 @@ function _handleFetchOnPromiseCompletion(
|
|
|
92
31
|
bodyStrings,
|
|
93
32
|
url,
|
|
94
33
|
method,
|
|
34
|
+
shouldCollectPayload,
|
|
95
35
|
) {
|
|
36
|
+
noibuLog('_handleFetchOnPromiseCompletion', method, url);
|
|
96
37
|
let reqBody; //
|
|
97
38
|
let reqHeaders = new Map();
|
|
98
39
|
let respHeaders = new Map();
|
|
@@ -147,6 +88,7 @@ function _handleFetchOnPromiseCompletion(
|
|
|
147
88
|
respHeaders,
|
|
148
89
|
bodyStrings[0],
|
|
149
90
|
method,
|
|
91
|
+
shouldCollectPayload,
|
|
150
92
|
);
|
|
151
93
|
}
|
|
152
94
|
|
|
@@ -171,6 +113,7 @@ async function _buildHttpEventDataObjectsForFetch(
|
|
|
171
113
|
respTime,
|
|
172
114
|
requestTextPromise,
|
|
173
115
|
) {
|
|
116
|
+
noibuLog('_buildHttpEventDataObjectsForFetch started', method, url);
|
|
174
117
|
const httpEvent = {
|
|
175
118
|
[HTTP_METHOD_ATT_NAME]: method,
|
|
176
119
|
[HTTP_RESP_CODE_ATT_NAME]: response.status,
|
|
@@ -192,15 +135,24 @@ async function _buildHttpEventDataObjectsForFetch(
|
|
|
192
135
|
}
|
|
193
136
|
|
|
194
137
|
let responseText = '';
|
|
195
|
-
|
|
196
|
-
HTTPDataBundler.getInstance().shouldCollectPayloadForURL(url)
|
|
197
|
-
|
|
198
|
-
|
|
138
|
+
const shouldCollectPayload =
|
|
139
|
+
HTTPDataBundler.getInstance().shouldCollectPayloadForURL(url);
|
|
140
|
+
if (shouldCollectPayload && response instanceof Response) {
|
|
141
|
+
noibuLog('_buildHttpEventDataObjectsForFetch collecting response');
|
|
199
142
|
const droppedResponseText =
|
|
200
143
|
HTTPDataBundler.getInstance().dropPayloadIfNecessaryFromHeaders(
|
|
201
144
|
response.headers,
|
|
202
145
|
);
|
|
203
146
|
responseText = droppedResponseText || response.clone().text();
|
|
147
|
+
} else {
|
|
148
|
+
noibuLog(
|
|
149
|
+
'_buildHttpEventDataObjectsForFetch not collecting payload, because of',
|
|
150
|
+
{
|
|
151
|
+
shouldCollectPayloadForURL:
|
|
152
|
+
HTTPDataBundler.getInstance().shouldCollectPayloadForURL(url),
|
|
153
|
+
instanceOfCheck: response instanceof Response,
|
|
154
|
+
},
|
|
155
|
+
);
|
|
204
156
|
}
|
|
205
157
|
const responseTextPromise = Promise.resolve(responseText);
|
|
206
158
|
|
|
@@ -222,224 +174,19 @@ async function _buildHttpEventDataObjectsForFetch(
|
|
|
222
174
|
data,
|
|
223
175
|
url,
|
|
224
176
|
method,
|
|
177
|
+
shouldCollectPayload,
|
|
225
178
|
),
|
|
226
179
|
)
|
|
227
180
|
.then(httpDataReturn => {
|
|
228
181
|
httpData = httpDataReturn;
|
|
229
182
|
});
|
|
230
183
|
}
|
|
184
|
+
noibuLog('_buildHttpEventDataObjectsForFetch ended');
|
|
231
185
|
// This return will await the _handleFetchOnPromiseCompletion() call if necessary,
|
|
232
186
|
// but won't if not. (See if above).
|
|
233
187
|
return [httpEvent, httpData];
|
|
234
188
|
}
|
|
235
189
|
|
|
236
|
-
/**
|
|
237
|
-
* Wraps the send prototype of the xmlhttp object
|
|
238
|
-
* @param {object} proto window.XMLHTTPRequest's prototype
|
|
239
|
-
*/
|
|
240
|
-
function wrapXMLHTTPSend(proto) {
|
|
241
|
-
replace(
|
|
242
|
-
proto,
|
|
243
|
-
'send',
|
|
244
|
-
originalFunction =>
|
|
245
|
-
// We set nbuWrapper to the handler function so if this returns an error
|
|
246
|
-
// the trace message will start with nbuWrapper which would allow us to
|
|
247
|
-
// detect that this is a wrapped function and not a NoibuJS error
|
|
248
|
-
function nbuXMLHTTPSendWrapper(data) {
|
|
249
|
-
// We wrap all of our logic in a try block to guarantee that we will complete the original
|
|
250
|
-
// implementation if we throw an error in our logic.
|
|
251
|
-
try {
|
|
252
|
-
let method;
|
|
253
|
-
if (this.noibuHttpMethod) {
|
|
254
|
-
method = this.noibuHttpMethod;
|
|
255
|
-
} else {
|
|
256
|
-
// This is very hacky but we do not really have an option as
|
|
257
|
-
// we do not have access to either the url nor the method when
|
|
258
|
-
// overiding the send method
|
|
259
|
-
method = data ? 'POST' : 'GET';
|
|
260
|
-
}
|
|
261
|
-
// not using safePerformanceNow as there is a 70% hit with using it.
|
|
262
|
-
// We don't want every request to get impacted by this.
|
|
263
|
-
const rCreatedAt = new Date();
|
|
264
|
-
// on loadend we catch the event and
|
|
265
|
-
// make sure it was correct
|
|
266
|
-
addSafeEventListener(this, 'loadend', async () => {
|
|
267
|
-
const rEndedAt = new Date();
|
|
268
|
-
const respTime = Math.abs(rEndedAt - rCreatedAt);
|
|
269
|
-
// Prefer the URL from the open() call if it is defined.
|
|
270
|
-
const url = this.noibuHttpUrl || this.responseURL;
|
|
271
|
-
const gqlError = await GqlErrorValidator.fromXhr(url, this);
|
|
272
|
-
|
|
273
|
-
const [httpEvent, httpData] = _buildHttpEventDataObjectsForXHR(
|
|
274
|
-
this,
|
|
275
|
-
method,
|
|
276
|
-
url,
|
|
277
|
-
respTime,
|
|
278
|
-
gqlError,
|
|
279
|
-
data,
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
// Save HTTP Info
|
|
283
|
-
let pageVisitEventHTTP = new PageVisitEventHTTP(
|
|
284
|
-
httpEvent,
|
|
285
|
-
httpData,
|
|
286
|
-
);
|
|
287
|
-
pageVisitEventHTTP.saveHTTPEvent();
|
|
288
|
-
|
|
289
|
-
const seq =
|
|
290
|
-
pageVisitEventHTTP.httpData &&
|
|
291
|
-
pageVisitEventHTTP.httpData[PV_SEQ_ATT_NAME];
|
|
292
|
-
if (isHttpCodeFailure(this.status)) {
|
|
293
|
-
saveErrorToPagevisit(XML_HTTP_REQUEST_ERROR_TYPE, this, seq);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (gqlError) {
|
|
297
|
-
gqlError.forEach(error =>
|
|
298
|
-
saveErrorToPagevisit(GQL_ERROR_TYPE, error, seq),
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// helping gc
|
|
303
|
-
pageVisitEventHTTP = null;
|
|
304
|
-
}); // End of loadend callback, return to synchronous code below
|
|
305
|
-
} catch (e) {
|
|
306
|
-
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
307
|
-
`Error in XHR.send() wrapper: ${e}`,
|
|
308
|
-
false,
|
|
309
|
-
SEVERITY.error,
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
return originalFunction.call(this, data);
|
|
313
|
-
},
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Wraps the open prototype of the xmlhttp object
|
|
319
|
-
* @param {object} proto window.XMLHTTPRequest's prototype
|
|
320
|
-
* @param {boolean} shouldHandleLoadend Whether the wrapped XHR.open() function should be
|
|
321
|
-
* responsible for listening to 'loadend' events. (Used in the case where the XHR.send()
|
|
322
|
-
* function on the XHR prototype cannot be modified.)
|
|
323
|
-
*/
|
|
324
|
-
function wrapXMLHTTPOpen(proto, shouldHandleLoadend) {
|
|
325
|
-
replace(
|
|
326
|
-
proto,
|
|
327
|
-
'open',
|
|
328
|
-
originalFunction =>
|
|
329
|
-
// We set nbuWrapper to the handler function so if this returns an error
|
|
330
|
-
// the trace message will start with nbuWrapper which would allow us to
|
|
331
|
-
// detect that this is a wrapped function and not a NoibuJS error
|
|
332
|
-
function nbuXMLHTTPOpenWrapper(
|
|
333
|
-
method,
|
|
334
|
-
url,
|
|
335
|
-
async = true,
|
|
336
|
-
user = null,
|
|
337
|
-
password = null,
|
|
338
|
-
) {
|
|
339
|
-
// We wrap all of our logic in a try block to guarantee that we will complete the original
|
|
340
|
-
// implementation if we throw an error in our logic.
|
|
341
|
-
try {
|
|
342
|
-
// We wrap assignment of the .noibu[...] variables in a try/catch, as we're assigning
|
|
343
|
-
// properties to a built-in object type, and have no guarantee of success.
|
|
344
|
-
try {
|
|
345
|
-
this.noibuHttpMethod = method;
|
|
346
|
-
this.noibuHttpUrl = url;
|
|
347
|
-
} catch (error) {
|
|
348
|
-
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
349
|
-
`Unable to set custom properties on XHR object: ${error}`,
|
|
350
|
-
false,
|
|
351
|
-
SEVERITY.warn,
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (shouldHandleLoadend) {
|
|
356
|
-
// on loadend we catch the event and
|
|
357
|
-
// make sure it was correct
|
|
358
|
-
const rCreatedAt = new Date();
|
|
359
|
-
addSafeEventListener(this, 'loadend', async () => {
|
|
360
|
-
const rEndedAt = new Date();
|
|
361
|
-
const respTime = Math.abs(rEndedAt - rCreatedAt);
|
|
362
|
-
const gqlError = await GqlErrorValidator.fromXhr(url, this);
|
|
363
|
-
|
|
364
|
-
const [httpEvent, httpData] = _buildHttpEventDataObjectsForXHR(
|
|
365
|
-
this,
|
|
366
|
-
method,
|
|
367
|
-
url,
|
|
368
|
-
respTime,
|
|
369
|
-
gqlError,
|
|
370
|
-
);
|
|
371
|
-
|
|
372
|
-
let pageVisitEventHTTP = new PageVisitEventHTTP(
|
|
373
|
-
httpEvent,
|
|
374
|
-
httpData,
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
// Save HTTP Info
|
|
378
|
-
pageVisitEventHTTP.saveHTTPEvent();
|
|
379
|
-
const seq =
|
|
380
|
-
pageVisitEventHTTP.httpData &&
|
|
381
|
-
pageVisitEventHTTP.httpData[PV_SEQ_ATT_NAME];
|
|
382
|
-
if (isHttpCodeFailure(this.status)) {
|
|
383
|
-
saveErrorToPagevisit(XML_HTTP_REQUEST_ERROR_TYPE, this, seq);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (gqlError) {
|
|
387
|
-
gqlError.forEach(error =>
|
|
388
|
-
saveErrorToPagevisit(GQL_ERROR_TYPE, error, seq),
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// helping gc
|
|
393
|
-
pageVisitEventHTTP = null;
|
|
394
|
-
}); // End of loadend callback, return to synchronous code below
|
|
395
|
-
}
|
|
396
|
-
} catch (e) {
|
|
397
|
-
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
398
|
-
`Error in XHR.open() wrapper: ${e}`,
|
|
399
|
-
false,
|
|
400
|
-
SEVERITY.error,
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
return originalFunction.call(this, method, url, async, user, password);
|
|
404
|
-
},
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Replaces the native XMLHTTPRequest.setHeader() function. We use this to build an array of
|
|
410
|
-
* request headers as these are not stored by the XMLHTTPRequest object.
|
|
411
|
-
* @param {object} proto window.XMLHTTPRequest's prototype
|
|
412
|
-
*/
|
|
413
|
-
function wrapXMLHTTPSetRequestHeader(proto) {
|
|
414
|
-
replace(
|
|
415
|
-
proto,
|
|
416
|
-
'setRequestHeader',
|
|
417
|
-
originalFunction =>
|
|
418
|
-
function nbuXMLHTTPSetRequestHeaderWrapper(header, value) {
|
|
419
|
-
// We wrap all of our logic in a try block to guarantee that we will complete the original
|
|
420
|
-
// implementation if we throw an error in our logic.
|
|
421
|
-
try {
|
|
422
|
-
if (
|
|
423
|
-
!this.noibuRequestHeaders ||
|
|
424
|
-
!(this.noibuRequestHeaders instanceof Map)
|
|
425
|
-
) {
|
|
426
|
-
this.noibuRequestHeaders = new Map();
|
|
427
|
-
}
|
|
428
|
-
// Ensure it's a string. This also converts null to "null"
|
|
429
|
-
const stringValue = typeof value === 'string' ? value : String(value);
|
|
430
|
-
this.noibuRequestHeaders.set(header.toLowerCase(), stringValue);
|
|
431
|
-
} catch (error) {
|
|
432
|
-
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
433
|
-
`Error in XHR.setRequestHeader() wrapper: ${error}`,
|
|
434
|
-
false,
|
|
435
|
-
SEVERITY.error,
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
return originalFunction.call(this, header, value);
|
|
439
|
-
},
|
|
440
|
-
);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
190
|
/**
|
|
444
191
|
* Handles fetch failure
|
|
445
192
|
* @param {} err
|
|
@@ -473,12 +220,12 @@ function handleFetchFailure(err, url) {
|
|
|
473
220
|
*/
|
|
474
221
|
function setupGlobalFetchWrapper() {
|
|
475
222
|
const proto = global;
|
|
476
|
-
|
|
477
223
|
// Ensure we're able to replace the fetch() function
|
|
478
224
|
if (!propWriteableOrMadeWriteable(proto, 'fetch')) {
|
|
225
|
+
noibuLog('setupGlobalFetchWrapper fetch is not writable');
|
|
479
226
|
return;
|
|
480
227
|
}
|
|
481
|
-
|
|
228
|
+
noibuLog('setupGlobalFetchWrapper replacing fetch');
|
|
482
229
|
replace(
|
|
483
230
|
proto,
|
|
484
231
|
'fetch',
|
|
@@ -500,6 +247,7 @@ function setupGlobalFetchWrapper() {
|
|
|
500
247
|
// We wrap everything in try/catch to ensure the original function is called even if we throw
|
|
501
248
|
// an unexpected error.
|
|
502
249
|
try {
|
|
250
|
+
noibuLog('nbuGlobalFetchWrapper start call', { resource });
|
|
503
251
|
// There is no valid reason for fetch() to be called without a resource but we
|
|
504
252
|
// see it on a bunch of websites. It seems to work the same as empty string
|
|
505
253
|
if (!resource) {
|
|
@@ -524,11 +272,24 @@ function setupGlobalFetchWrapper() {
|
|
|
524
272
|
resource.clone &&
|
|
525
273
|
resource.text
|
|
526
274
|
) {
|
|
275
|
+
noibuLog('nbuGlobalFetchWrapper collecting request object');
|
|
527
276
|
// We clone the request object, as its body can only be read once per instance.
|
|
528
277
|
// It's important that we do this before the original function is called, as it
|
|
529
278
|
// cannot be cloned afterwards.
|
|
530
279
|
request = resource.clone();
|
|
531
280
|
requestTextPromise = request.text();
|
|
281
|
+
} else {
|
|
282
|
+
noibuLog(
|
|
283
|
+
'nbuGlobalFetchWrapper not collecting request object, because one of these is false',
|
|
284
|
+
{
|
|
285
|
+
shouldCollectPayloadForURL:
|
|
286
|
+
HTTPDataBundler.getInstance().shouldCollectPayloadForURL(
|
|
287
|
+
url,
|
|
288
|
+
),
|
|
289
|
+
clone: !!resource.clone,
|
|
290
|
+
text: !!resource.text,
|
|
291
|
+
},
|
|
292
|
+
);
|
|
532
293
|
}
|
|
533
294
|
}
|
|
534
295
|
} catch (e) {
|
|
@@ -541,6 +302,7 @@ function setupGlobalFetchWrapper() {
|
|
|
541
302
|
|
|
542
303
|
// Break out of the try wrapper momentarily to make the original call
|
|
543
304
|
|
|
305
|
+
noibuLog('nbuGlobalFetchWrapper call original');
|
|
544
306
|
// Can't put this inside finally as const needs to be defined outside it.
|
|
545
307
|
const promiseFromOriginalFetch = originalFunction.call(
|
|
546
308
|
this,
|
|
@@ -552,6 +314,7 @@ function setupGlobalFetchWrapper() {
|
|
|
552
314
|
const rCreatedAt = new Date();
|
|
553
315
|
promiseFromOriginalFetch
|
|
554
316
|
.then(async response => {
|
|
317
|
+
noibuLog('nbuGlobalFetchWrapper got response');
|
|
555
318
|
// We wrap the callback in try in order to only catch real fetch failures in the promise's
|
|
556
319
|
// .catch() call
|
|
557
320
|
try {
|
|
@@ -624,41 +387,15 @@ function setupGlobalFetchWrapper() {
|
|
|
624
387
|
},
|
|
625
388
|
);
|
|
626
389
|
}
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* Setting up the wrapper around send functions to try and
|
|
630
|
-
* catch http errors
|
|
631
|
-
*/
|
|
632
|
-
function setupGlobalXMLHttpWrapper() {
|
|
633
|
-
const XMLHttp = window.XMLHttpRequest;
|
|
634
|
-
// The event target needs to have a prototype and have the open function
|
|
635
|
-
const proto = XMLHttp && XMLHttp.prototype;
|
|
636
|
-
|
|
637
|
-
const canWriteOpen = propWriteableOrMadeWriteable(proto, 'open');
|
|
638
|
-
const canWriteSend = propWriteableOrMadeWriteable(proto, 'send');
|
|
639
|
-
const canWriteSetHeader = propWriteableOrMadeWriteable(
|
|
640
|
-
proto,
|
|
641
|
-
'setRequestHeader',
|
|
642
|
-
);
|
|
643
|
-
|
|
644
|
-
if (canWriteOpen) {
|
|
645
|
-
// If we can't wrap the XHR.send() function, we need the wrapped .open() function to
|
|
646
|
-
// set the listener on 'loadend' events.
|
|
647
|
-
wrapXMLHTTPOpen(proto, !canWriteSend);
|
|
648
|
-
}
|
|
649
|
-
if (canWriteSend) {
|
|
650
|
-
wrapXMLHTTPSend(proto);
|
|
651
|
-
}
|
|
652
|
-
if (canWriteSetHeader) {
|
|
653
|
-
wrapXMLHTTPSetRequestHeader(proto);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
390
|
/**
|
|
657
391
|
* Monitors all requests
|
|
658
392
|
*/
|
|
659
393
|
function monitorRequests() {
|
|
660
|
-
|
|
394
|
+
noibuLog('monitorRequests started');
|
|
395
|
+
/** todo: disabling xmlhttp since it seems to duplicate fetch wrapper, but lacks response body capture */
|
|
396
|
+
// setupGlobalXMLHttpWrapper();
|
|
661
397
|
setupGlobalFetchWrapper();
|
|
398
|
+
noibuLog('monitorRequests ended');
|
|
662
399
|
}
|
|
663
400
|
|
|
664
401
|
export { handleFetchFailure, monitorRequests };
|
package/dist/pageVisit/{pageVisitEventError/pageVisitEventError.js → pageVisitEventError.js}
RENAMED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import { isValidURL,
|
|
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 '
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import StoredMetrics from '../../api/storedMetrics.js';
|
|
1
|
+
import { isValidURL, getJSStack, stringifyJSON, getMaxSubstringAllowed, asString } from '../utils/function.js';
|
|
2
|
+
import { EVENT_ERROR_TYPE, URL_ATT_NAME, BLOCKLISTED_DOMAINS, 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
|
+
import ClientConfig from '../api/clientConfig.js';
|
|
4
|
+
import { InputMonitor } from '../monitors/inputMonitor.js';
|
|
5
|
+
import StoredMetrics from '../api/storedMetrics.js';
|
|
7
6
|
|
|
8
7
|
/** @module PageVisitEventError */
|
|
9
8
|
|
|
9
|
+
/**
|
|
10
|
+
* gets the onURL of a string, defaulting to the location of the webpage
|
|
11
|
+
*/
|
|
12
|
+
function getOnURL(realOnURL) {
|
|
13
|
+
if (realOnURL && realOnURL.trim() !== '' && realOnURL !== 'undefined') {
|
|
14
|
+
return asString(getMaxSubstringAllowed(realOnURL));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return ClientConfig.getInstance().globalUrl;
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
/**
|
|
11
21
|
* getPVErrorFromResponse will create the error information from an
|
|
12
22
|
* http response error type.
|
|
@@ -25,21 +35,10 @@ function getPVErrorFromResponse(errPayload, httpDataSeqNum) {
|
|
|
25
35
|
return errorObject;
|
|
26
36
|
}
|
|
27
37
|
|
|
28
|
-
/**
|
|
29
|
-
* gets the onURL of a string, defaulting to the location of the webpage
|
|
30
|
-
*/
|
|
31
|
-
function getOnURL(realOnURL) {
|
|
32
|
-
if (realOnURL && realOnURL.trim() !== '' && realOnURL !== 'undefined') {
|
|
33
|
-
return asString(getMaxSubstringAllowed(realOnURL));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return ClientConfig.getInstance().globalUrl;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
38
|
/**
|
|
40
39
|
* getPVErrorFromGqlError will create the error information from an
|
|
41
40
|
* GraphQL error payload.
|
|
42
|
-
* @param
|
|
41
|
+
* @param payload
|
|
43
42
|
* @param {} httpDataSeqNum
|
|
44
43
|
*/
|
|
45
44
|
function getPVErrorFromGqlError(payload, httpDataSeqNum) {
|
|
@@ -210,7 +209,7 @@ function createPageVisitEventError(type, errPayload, httpDataSeqNum) {
|
|
|
210
209
|
* determines if an error is a collect error
|
|
211
210
|
* @param {} pvError
|
|
212
211
|
*/
|
|
213
|
-
function
|
|
212
|
+
function isErrorCollectedByNoibu(pvError) {
|
|
214
213
|
// checking if this error originated from the Noibu script
|
|
215
214
|
if (pvError[TYPE_ATT_NAME] === JS_EVENT_TYPE) {
|
|
216
215
|
if (pvError[JS_ERROR_ATT_NAME]) {
|
|
@@ -222,6 +221,7 @@ function isCollectError(pvError) {
|
|
|
222
221
|
|
|
223
222
|
if (
|
|
224
223
|
lowerCapFile.includes('noibu') &&
|
|
224
|
+
!lowerCapFile.includes('examplenoibureactnativeapp') &&
|
|
225
225
|
!lowerCapMethod.includes('nbuwrapper')
|
|
226
226
|
) {
|
|
227
227
|
// if the first file in the stack is Noibu then we do not
|
|
@@ -302,12 +302,12 @@ function saveErrorToPagevisit(type, payload, httpDataSeqNum) {
|
|
|
302
302
|
const urlOb = new URL(currErrURL);
|
|
303
303
|
|
|
304
304
|
// if the domain is blacklisted we drop the error
|
|
305
|
-
if (urlOb.hostname in
|
|
305
|
+
if (urlOb.hostname in BLOCKLISTED_DOMAINS) {
|
|
306
306
|
return;
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
if (
|
|
310
|
+
if (isErrorCollectedByNoibu(pvError)) {
|
|
311
311
|
return;
|
|
312
312
|
}
|
|
313
313
|
|
|
@@ -317,4 +317,4 @@ function saveErrorToPagevisit(type, payload, httpDataSeqNum) {
|
|
|
317
317
|
InputMonitor.getInstance().addEvent(pvError, ERROR_EVENT_TYPE);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
export { getOnURL,
|
|
320
|
+
export { getOnURL, isErrorCollectedByNoibu, saveErrorToPagevisit };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { getMaxSubstringAllowed, asString } from '
|
|
2
|
-
import { timestampWrapper } from '
|
|
3
|
-
import { InputMonitor } from '
|
|
4
|
-
import { HTTP_METHOD_ATT_NAME, MAX_HTTP_DATA_EVENT_COUNT, PV_SEQ_ATT_NAME, HTTP_DATA_METROPLEX_TYPE, HTTP_EVENT_TYPE, PAGE_VISIT_HTTP_DATA_ATT_NAME } from '
|
|
5
|
-
import { PageVisit } from '
|
|
6
|
-
import StoredMetrics from '
|
|
7
|
-
import MetroplexSocket from '
|
|
1
|
+
import { getMaxSubstringAllowed, asString } from '../utils/function.js';
|
|
2
|
+
import { timestampWrapper } from '../utils/date.js';
|
|
3
|
+
import { InputMonitor } from '../monitors/inputMonitor.js';
|
|
4
|
+
import { HTTP_METHOD_ATT_NAME, MAX_HTTP_DATA_EVENT_COUNT, PV_SEQ_ATT_NAME, HTTP_DATA_METROPLEX_TYPE, HTTP_EVENT_TYPE, PAGE_VISIT_HTTP_DATA_ATT_NAME } from '../constants.js';
|
|
5
|
+
import { PageVisit } from './pageVisit.js';
|
|
6
|
+
import StoredMetrics from '../api/storedMetrics.js';
|
|
7
|
+
import MetroplexSocket from '../api/metroplexSocket.js';
|
|
8
|
+
import { noibuLog } from '../utils/log.js';
|
|
8
9
|
|
|
9
10
|
/** @module PageVisitEventHTTP */
|
|
10
11
|
|
|
@@ -49,12 +50,14 @@ class PageVisitEventHTTP {
|
|
|
49
50
|
|
|
50
51
|
/** Saves the HTTP event to the pageVisit Queue */
|
|
51
52
|
saveHTTPEvent() {
|
|
53
|
+
noibuLog('saveHTTPEvent');
|
|
52
54
|
// we do not store http events that have empty urls
|
|
53
55
|
if (
|
|
54
56
|
!this.httpEvent ||
|
|
55
57
|
!this.httpEvent.url ||
|
|
56
58
|
this.httpEvent.url.trim() === ''
|
|
57
59
|
) {
|
|
60
|
+
noibuLog('saveHTTPEvent dropped due to empty url');
|
|
58
61
|
return;
|
|
59
62
|
}
|
|
60
63
|
// we register an http event
|
|
@@ -62,6 +65,7 @@ class PageVisitEventHTTP {
|
|
|
62
65
|
|
|
63
66
|
// send http data down to metroplex
|
|
64
67
|
if (this.httpData) {
|
|
68
|
+
noibuLog('saveHTTPEvent got httpData, working');
|
|
65
69
|
// add the sequence number to both events
|
|
66
70
|
const sequenceNumber = StoredMetrics.getInstance().httpSequenceNumber;
|
|
67
71
|
// restrict total number of events collected per page visit to ensure we don't
|
|
@@ -79,6 +83,9 @@ class PageVisitEventHTTP {
|
|
|
79
83
|
metroplexMsg,
|
|
80
84
|
);
|
|
81
85
|
} else {
|
|
86
|
+
noibuLog(
|
|
87
|
+
'saveHTTPEvent have collected more than the max number of http requests for this page visit, so increment the over request limit count',
|
|
88
|
+
);
|
|
82
89
|
// have collected more than the max number of http requests for this
|
|
83
90
|
// page visit, so increment the over request limit count
|
|
84
91
|
StoredMetrics.getInstance().addHttpDataOverLimit();
|
|
@@ -86,6 +93,7 @@ class PageVisitEventHTTP {
|
|
|
86
93
|
}
|
|
87
94
|
// if this was an error, send immediately so we don't lose it
|
|
88
95
|
if (isHttpCodeFailure(this.httpEvent.code)) {
|
|
96
|
+
noibuLog('saveHTTPEvent is an error, sending immediately');
|
|
89
97
|
PageVisit.getInstance().addPageVisitEvent(
|
|
90
98
|
{
|
|
91
99
|
event: this.httpEvent,
|
package/dist/types/globals.d.ts
CHANGED
|
@@ -14,19 +14,29 @@ declare global {
|
|
|
14
14
|
const MAX_METROPLEX_RECONNECTION_NUMBER: number;
|
|
15
15
|
const METROPLEX_CONSECUTIVE_CONNECTION_DELAY: number;
|
|
16
16
|
const SCRIPT_ID: string;
|
|
17
|
+
const ENABLE_LOGGING: string;
|
|
17
18
|
type Window = {
|
|
18
19
|
noibuJSLoaded?: boolean;
|
|
19
20
|
};
|
|
21
|
+
type PromiseRejectionTrackerOptions = Partial<{
|
|
22
|
+
allRejections: boolean;
|
|
23
|
+
whitelist: (typeof Error)[];
|
|
24
|
+
onUnhandled: (id: number, reason: any) => void;
|
|
25
|
+
onHandled: (id: number, reason: any) => void;
|
|
26
|
+
}>;
|
|
20
27
|
type global = {
|
|
21
28
|
window: Window;
|
|
22
29
|
console: Console;
|
|
23
30
|
noibuJSLoaded?: boolean;
|
|
24
31
|
ErrorUtils: any;
|
|
25
32
|
HermesInternal: null | {
|
|
26
|
-
enablePromiseRejectionTracker?:
|
|
27
|
-
setPromiseRejectionTrackingHook?:
|
|
33
|
+
enablePromiseRejectionTracker?: (options: PromiseRejectionTrackerOptions) => void;
|
|
34
|
+
setPromiseRejectionTrackingHook?: (enable: (options: PromiseRejectionTrackerOptions) => void) => void;
|
|
28
35
|
};
|
|
29
36
|
};
|
|
37
|
+
interface PromiseConstructor {
|
|
38
|
+
_m: <T>(promise: Promise<T>, error: any) => void;
|
|
39
|
+
}
|
|
30
40
|
const global: global;
|
|
31
41
|
const globalThis: global;
|
|
32
42
|
const window: Window & global;
|