noibu-react-native 0.2.3 → 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/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 +11 -11
- package/dist/monitors/{appNavigationMonitor.js → AppNavigationMonitor.js} +10 -19
- 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/sessionRecorder.js +1 -1
- package/dist/src/api/clientConfig.d.ts +1 -1
- 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/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/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/Monitor.d.ts +11 -0
- package/dist/types/Monitor.js +19 -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 +4 -3
- 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
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { __awaiter } from 'tslib';
|
|
2
|
-
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, CONTENT_TYPE, CONTENT_LENGTH, BLOCKED_HTTP_HEADER_KEYS, PII_REDACTION_REPLACEMENT_STRING, MAX_HTTP_DATA_PAYLOAD_LENGTH, MAX_SUCCESS_HTTP_DATA_PAYLOAD_LENGTH } from '
|
|
3
|
-
import ClientConfig from '
|
|
4
|
-
import StoredMetrics from '
|
|
5
|
-
import { safeFromEntries, safeEntries } from '
|
|
6
|
-
import { safeTrim, stringifyJSON, isString } from '
|
|
7
|
-
import { removePII } from '
|
|
2
|
+
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, CONTENT_TYPE, CONTENT_LENGTH, BLOCKED_HTTP_HEADER_KEYS, PII_REDACTION_REPLACEMENT_STRING, MAX_HTTP_DATA_PAYLOAD_LENGTH, MAX_SUCCESS_HTTP_DATA_PAYLOAD_LENGTH } from '../../constants.js';
|
|
3
|
+
import ClientConfig from '../../api/clientConfig.js';
|
|
4
|
+
import StoredMetrics from '../../api/storedMetrics.js';
|
|
5
|
+
import { safeFromEntries, safeEntries } from '../../utils/object.js';
|
|
6
|
+
import { safeTrim, stringifyJSON, isString } from '../../utils/function.js';
|
|
7
|
+
import { removePII } from '../../utils/piiRedactor.js';
|
|
8
|
+
import { Singleton } from '../../types/Monitor.js';
|
|
8
9
|
|
|
9
10
|
/** Bundles HTTP payloads and headers */
|
|
10
|
-
class HTTPDataBundler {
|
|
11
|
+
class HTTPDataBundler extends Singleton {
|
|
11
12
|
/**
|
|
12
13
|
* Creates an instance of the ClickMonitor instance
|
|
13
14
|
*/
|
|
14
15
|
constructor() {
|
|
16
|
+
super();
|
|
15
17
|
// compile regex only once
|
|
16
18
|
this.contentTypeReadableRegex = new RegExp(HUMAN_READABLE_CONTENT_TYPE_REGEX, 'i');
|
|
17
19
|
// pull out the domain hostname
|
|
@@ -43,15 +45,6 @@ class HTTPDataBundler {
|
|
|
43
45
|
// TODO: disabled for beta. NOI-4253
|
|
44
46
|
// this.uniqueRequests = new Set();
|
|
45
47
|
}
|
|
46
|
-
/** gets the singleton instance
|
|
47
|
-
* @returns {HTTPDataBundler}
|
|
48
|
-
* */
|
|
49
|
-
static getInstance() {
|
|
50
|
-
if (!this.instance) {
|
|
51
|
-
this.instance = new HTTPDataBundler();
|
|
52
|
-
}
|
|
53
|
-
return this.instance;
|
|
54
|
-
}
|
|
55
48
|
/**
|
|
56
49
|
* Builds the HTTP payload allowed regexes for full and relative URLs
|
|
57
50
|
* @param allowedURLs A list of allowed URLs
|
|
@@ -77,13 +70,12 @@ class HTTPDataBundler {
|
|
|
77
70
|
}
|
|
78
71
|
/**
|
|
79
72
|
* Takes an iterator and returns a map of strings representing headers.
|
|
80
|
-
*
|
|
81
|
-
*
|
|
73
|
+
* param {object} headersIterable any iterable object
|
|
74
|
+
* returns a map of strings (as expected by metroplex) representing HTTP
|
|
82
75
|
* request or response headers
|
|
83
76
|
*/
|
|
84
77
|
static headersMapFromIterable(headersIterable) {
|
|
85
78
|
const headerMap = new Map();
|
|
86
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
87
79
|
for (const header of headersIterable) {
|
|
88
80
|
// Ensure we're working with strings
|
|
89
81
|
// This will also automatically convert null to "null"
|
|
@@ -153,65 +145,11 @@ class HTTPDataBundler {
|
|
|
153
145
|
return null;
|
|
154
146
|
});
|
|
155
147
|
}
|
|
156
|
-
/**
|
|
157
|
-
* Takes a URL and returns true if it is determined to be on the same domain as the URL
|
|
158
|
-
* the script is running on. Ignores protocol and path, and allows the URL to be a subdomain.
|
|
159
|
-
* @param {string} requestURL the URL of a request to compare to the script website's URL
|
|
160
|
-
* @param {bool} isHostnameCheck the URL is a domain hostname, could be a super or sub domain
|
|
161
|
-
*/
|
|
162
|
-
isURLSameDomain(requestURL, isHostnameCheck = false) {
|
|
163
|
-
if (typeof requestURL !== 'string' ||
|
|
164
|
-
!this.initialURLPartsReversed ||
|
|
165
|
-
this.initialURLPartsReversed.length < 1) {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
let requestHostname = isHostnameCheck ? requestURL : ''; // has to be declared outside of `try`
|
|
169
|
-
// Use URL constructor to extract hostname
|
|
170
|
-
if (!isHostnameCheck) {
|
|
171
|
-
try {
|
|
172
|
-
let requestURLWithProtocol = requestURL;
|
|
173
|
-
if (requestURL.startsWith('//')) {
|
|
174
|
-
// eslint-disable-next-line prefer-template
|
|
175
|
-
requestURLWithProtocol = 'https:' + requestURL;
|
|
176
|
-
}
|
|
177
|
-
requestHostname = new URL(requestURLWithProtocol).hostname;
|
|
178
|
-
}
|
|
179
|
-
catch (e) {
|
|
180
|
-
// Return false if URL() is unable to parse the request URL
|
|
181
|
-
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(`Unable to determine hostname for request URL: ${e}`, false, SEVERITY.warn);
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const requestParts = requestHostname.split('.');
|
|
186
|
-
// Remove first subdomain from either URL if it is www., www1., etc.
|
|
187
|
-
if (requestParts.length < 1)
|
|
188
|
-
return false;
|
|
189
|
-
/* eslint-disable no-unused-expressions */
|
|
190
|
-
DEFAULT_WEBSITE_SUBDOMAIN_PATTERN.test(requestParts[0]) &&
|
|
191
|
-
requestParts.shift();
|
|
192
|
-
/* eslint-enable no-unused-expressions */
|
|
193
|
-
// Reverse order array
|
|
194
|
-
requestParts.reverse();
|
|
195
|
-
if (!isHostnameCheck &&
|
|
196
|
-
requestParts.length < this.initialURLPartsReversed.length)
|
|
197
|
-
return false;
|
|
198
|
-
// Return true if for every part in the website part, the same part exists
|
|
199
|
-
// in the request part, or if alsoCheckSuperDomain is true.
|
|
200
|
-
if (isHostnameCheck) {
|
|
201
|
-
const isSubDomain = requestParts.length >= this.initialURLPartsReversed.length &&
|
|
202
|
-
this.initialURLPartsReversed.every((part, i) => part === requestParts[i]);
|
|
203
|
-
const isSuperDomain = requestParts.length <= this.initialURLPartsReversed.length &&
|
|
204
|
-
requestParts.every((part, i) => part === this.initialURLPartsReversed[i]);
|
|
205
|
-
return isSubDomain || isSuperDomain;
|
|
206
|
-
}
|
|
207
|
-
// Default return false if alsoCheckSuperDomain is not true
|
|
208
|
-
return this.initialURLPartsReversed.every((part, i) => part === requestParts[i]);
|
|
209
|
-
}
|
|
210
148
|
/**
|
|
211
149
|
* Builds an HTTP Data bundle
|
|
212
150
|
*/
|
|
213
151
|
bundleHTTPData(url, requestHeaders, rawRequestPayload, responseHeaders, rawResponsePayload, method, isError) {
|
|
214
|
-
if (!this.isValidRequest(
|
|
152
|
+
if (!this.isValidRequest(method)) {
|
|
215
153
|
return null;
|
|
216
154
|
}
|
|
217
155
|
// stringify payload if correct type and not too large
|
|
@@ -253,24 +191,9 @@ class HTTPDataBundler {
|
|
|
253
191
|
/**
|
|
254
192
|
* Validates a request based on the URL and method. When enabled, will handle
|
|
255
193
|
* de-duping the requests
|
|
256
|
-
* @param {string} url
|
|
257
|
-
* @param {string} method
|
|
258
|
-
* @returns boolean indicating whether the validation passed
|
|
259
194
|
*/
|
|
260
|
-
isValidRequest(
|
|
261
|
-
|
|
262
|
-
return false;
|
|
263
|
-
if (!method || typeof method !== 'string') {
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
// TODO: Check below disabled for beta. NOI-4253
|
|
267
|
-
// only bundle and send once per request/method/response code combination
|
|
268
|
-
// const urlWithoutParams = url.split('?')[0];
|
|
269
|
-
// const requestSignature = `${urlWithoutParams}-${method}-${responseCode}`;
|
|
270
|
-
// if (this.uniqueRequests.has(requestSignature)) {
|
|
271
|
-
// return false;
|
|
272
|
-
// }
|
|
273
|
-
return true;
|
|
195
|
+
isValidRequest(method) {
|
|
196
|
+
return (this.httpDataCollectionEnabled && method && typeof method === 'string');
|
|
274
197
|
}
|
|
275
198
|
/**
|
|
276
199
|
* Checks two things: that the URL is either on the same domain (or an address relative to the
|
|
@@ -288,15 +211,15 @@ class HTTPDataBundler {
|
|
|
288
211
|
// URL is absolute; either "http://example.com" or "//example.com"
|
|
289
212
|
if (HTTPDataBundler.isAbsoluteURL(url)) {
|
|
290
213
|
// Only capture requests on the same domain or in the allowed list
|
|
291
|
-
if (!this.
|
|
214
|
+
if (!this.shouldCollectPayloadForURL(url)) {
|
|
292
215
|
return false;
|
|
216
|
+
}
|
|
293
217
|
} // end `if` (if URL is relative, it is on the same domain.)
|
|
294
218
|
return true;
|
|
295
219
|
}
|
|
296
220
|
/**
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
* @returns boolean indicating whether the URL passed is either absolute or relative
|
|
221
|
+
* Determines if the URL is absolute or relative
|
|
222
|
+
* returns boolean indicating whether the URL passed is either absolute or relative
|
|
300
223
|
*/
|
|
301
224
|
static isAbsoluteURL(url) {
|
|
302
225
|
if (!url || typeof url !== 'string') {
|
|
@@ -306,22 +229,20 @@ class HTTPDataBundler {
|
|
|
306
229
|
}
|
|
307
230
|
/**
|
|
308
231
|
* Checks whether HTTP payloads can be collected on this URL
|
|
309
|
-
*
|
|
232
|
+
* returns boolean indicating whether HTTP payloads can be collected on this URL
|
|
310
233
|
*/
|
|
311
234
|
shouldCollectPayloadForURL(url) {
|
|
235
|
+
var _a, _b;
|
|
312
236
|
if (!url || typeof url !== 'string') {
|
|
313
237
|
return false;
|
|
314
238
|
}
|
|
315
239
|
// check if in the full URL allowed list
|
|
316
|
-
const isAllowedAsAbsolute =
|
|
317
|
-
|
|
318
|
-
const isRelative = !HTTPDataBundler.isAbsoluteURL(url) || this.isURLSameDomain(url);
|
|
240
|
+
const isAllowedAsAbsolute = (_a = this.httpDataAllowedAbsoluteRegex) === null || _a === void 0 ? void 0 : _a.test(url.toLowerCase());
|
|
241
|
+
const isRelative = !HTTPDataBundler.isAbsoluteURL(url);
|
|
319
242
|
// check if in the relative URL allowed list or if it is on the same domain
|
|
320
243
|
const isAllowedAsRelative =
|
|
321
244
|
// if this is a relative URL (isURLSameDomain will fail) OR a full URL on domain
|
|
322
|
-
isRelative &&
|
|
323
|
-
!!this.httpDataAllowedRelativeRegex &&
|
|
324
|
-
this.httpDataAllowedRelativeRegex.test(url.toLowerCase());
|
|
245
|
+
isRelative && ((_b = this.httpDataAllowedRelativeRegex) === null || _b === void 0 ? void 0 : _b.test(url.toLowerCase()));
|
|
325
246
|
return isAllowedAsAbsolute || isAllowedAsRelative;
|
|
326
247
|
}
|
|
327
248
|
/**
|
|
@@ -1,65 +1,54 @@
|
|
|
1
1
|
import { PageVisit } from './pageVisit.js';
|
|
2
|
-
import {
|
|
2
|
+
import { APP_NAVIGATION_EVENT_TYPE, PAGE_EVENT_TYPE, MAX_TIME_FOR_UNSENT_DATA_MILLIS, ERROR_EVENT_TYPE, HTTP_EVENT_TYPE, KEYBOARD_EVENT_TYPE, SEVERITY } from '../constants.js';
|
|
3
3
|
import { timestampWrapper } from '../utils/date.js';
|
|
4
4
|
import { addSafeEventListener } from '../utils/eventlistener.js';
|
|
5
5
|
import ClientConfig from '../api/clientConfig.js';
|
|
6
|
+
import { Singleton } from '../types/Monitor.js';
|
|
6
7
|
|
|
7
8
|
/** @module EventDebouncer */
|
|
8
9
|
/**
|
|
9
10
|
* Singleton class responsible for debouncing all events
|
|
10
11
|
* that are registered
|
|
11
12
|
*/
|
|
12
|
-
class EventDebouncer {
|
|
13
|
+
class EventDebouncer extends Singleton {
|
|
13
14
|
/**
|
|
14
15
|
* Creates an instance of EventDebouncer
|
|
15
16
|
*/
|
|
16
17
|
constructor() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
/** will debounce all events that are of this type by the debounce period
|
|
51
|
-
*/
|
|
52
|
-
registerInputType(type, debouncePeriod, eventName = type) {
|
|
53
|
-
if (type in this.eventsToDebounce) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
// registering this event type as a debouncable
|
|
57
|
-
this.eventsToDebounce[type] = {
|
|
58
|
-
timeout: null,
|
|
59
|
-
events: [],
|
|
60
|
-
debouncePeriod,
|
|
61
|
-
eventName,
|
|
18
|
+
super();
|
|
19
|
+
this.eventsToDebounce = {
|
|
20
|
+
[APP_NAVIGATION_EVENT_TYPE]: {
|
|
21
|
+
timeout: null,
|
|
22
|
+
events: [],
|
|
23
|
+
debouncePeriod: 0,
|
|
24
|
+
eventName: APP_NAVIGATION_EVENT_TYPE,
|
|
25
|
+
},
|
|
26
|
+
[PAGE_EVENT_TYPE]: {
|
|
27
|
+
timeout: null,
|
|
28
|
+
events: [],
|
|
29
|
+
debouncePeriod: MAX_TIME_FOR_UNSENT_DATA_MILLIS,
|
|
30
|
+
eventName: PAGE_EVENT_TYPE,
|
|
31
|
+
},
|
|
32
|
+
[ERROR_EVENT_TYPE]: {
|
|
33
|
+
timeout: null,
|
|
34
|
+
events: [],
|
|
35
|
+
debouncePeriod: MAX_TIME_FOR_UNSENT_DATA_MILLIS,
|
|
36
|
+
eventName: ERROR_EVENT_TYPE,
|
|
37
|
+
},
|
|
38
|
+
[HTTP_EVENT_TYPE]: {
|
|
39
|
+
timeout: null,
|
|
40
|
+
events: [],
|
|
41
|
+
debouncePeriod: MAX_TIME_FOR_UNSENT_DATA_MILLIS,
|
|
42
|
+
eventName: HTTP_EVENT_TYPE,
|
|
43
|
+
},
|
|
44
|
+
[KEYBOARD_EVENT_TYPE]: {
|
|
45
|
+
timeout: null,
|
|
46
|
+
events: [],
|
|
47
|
+
debouncePeriod: MAX_TIME_FOR_UNSENT_DATA_MILLIS,
|
|
48
|
+
eventName: KEYBOARD_EVENT_TYPE,
|
|
49
|
+
},
|
|
62
50
|
};
|
|
51
|
+
this._setupUnloadHandler();
|
|
63
52
|
}
|
|
64
53
|
/**
|
|
65
54
|
* Creates an event object with the event and the time it was added then pushes
|
|
@@ -1,8 +1,8 @@
|
|
|
1
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, GQL_EVENT_TYPE, GQL_ERROR_ATT_NAME
|
|
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
3
|
import ClientConfig from '../api/clientConfig.js';
|
|
4
|
-
import { InputMonitor } from '../monitors/inputMonitor.js';
|
|
5
4
|
import StoredMetrics from '../api/storedMetrics.js';
|
|
5
|
+
import { EventDebouncer } from './EventDebouncer.js';
|
|
6
6
|
|
|
7
7
|
/** @module PageVisitEventError */
|
|
8
8
|
|
|
@@ -314,7 +314,7 @@ function saveErrorToPagevisit(type, payload, httpDataSeqNum) {
|
|
|
314
314
|
// we register an error event
|
|
315
315
|
StoredMetrics.getInstance().addError();
|
|
316
316
|
// debounce event
|
|
317
|
-
|
|
317
|
+
EventDebouncer.getInstance().addEvent(pvError, ERROR_EVENT_TYPE);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
export { getOnURL, isErrorCollectedByNoibu, saveErrorToPagevisit };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { timestampWrapper } from '../utils/date.js';
|
|
2
|
-
import { HTTP_RESP_CODE_ATT_NAME, HTTP_RESP_TIME_ATT_NAME, HTTP_METHOD_ATT_NAME, URL_ATT_NAME, PV_SEQ_ATT_NAME, HTTP_DATA_METROPLEX_TYPE, HTTP_EVENT_TYPE, MAX_HTTP_DATA_EVENT_COUNT, MAX_HTTP_DATA_IF_ERROR_EVENT_COUNT
|
|
2
|
+
import { HTTP_RESP_CODE_ATT_NAME, HTTP_RESP_TIME_ATT_NAME, HTTP_METHOD_ATT_NAME, URL_ATT_NAME, PV_SEQ_ATT_NAME, PAGE_VISIT_HTTP_DATA_ATT_NAME, HTTP_DATA_METROPLEX_TYPE, HTTP_EVENT_TYPE, MAX_HTTP_DATA_EVENT_COUNT, MAX_HTTP_DATA_IF_ERROR_EVENT_COUNT } from '../constants.js';
|
|
3
3
|
import { PageVisit } from './pageVisit.js';
|
|
4
4
|
import StoredMetrics from '../api/storedMetrics.js';
|
|
5
5
|
import MetroplexSocket from '../api/metroplexSocket.js';
|
|
6
6
|
import { getMaxSubstringAllowed, asString, safeTrim } from '../utils/function.js';
|
|
7
|
-
import { EventDebouncer } from './
|
|
7
|
+
import { EventDebouncer } from './EventDebouncer.js';
|
|
8
8
|
|
|
9
9
|
/** @module PageVisitEventHTTP */
|
|
10
10
|
/**
|
|
@@ -54,8 +54,9 @@ class PageVisitEventHTTP {
|
|
|
54
54
|
this.httpEvent[PV_SEQ_ATT_NAME] = sequenceNumber;
|
|
55
55
|
// increment the count
|
|
56
56
|
StoredMetrics.getInstance().addHttpData();
|
|
57
|
-
const metroplexMsg = {
|
|
58
|
-
|
|
57
|
+
const metroplexMsg = {
|
|
58
|
+
[PAGE_VISIT_HTTP_DATA_ATT_NAME]: this.httpData,
|
|
59
|
+
};
|
|
59
60
|
MetroplexSocket.getInstance().sendMessage(HTTP_DATA_METROPLEX_TYPE, metroplexMsg);
|
|
60
61
|
}
|
|
61
62
|
else {
|
|
@@ -4,7 +4,7 @@ import { LogLevel, initialize, subscribeToNativeEvent } from './nativeSessionRec
|
|
|
4
4
|
import StoredMetrics from '../api/storedMetrics.js';
|
|
5
5
|
import ClientConfig from '../api/clientConfig.js';
|
|
6
6
|
import { stringifyJSON } from '../utils/function.js';
|
|
7
|
-
import {
|
|
7
|
+
import { MAX_TIME_FOR_RECORDER_USER_EVENTS, SEVERITY, MAX_RECORDER_EVENT_BUFFER, MAX_TIME_FOR_UNSENT_DATA_MILLIS, VIDEO_FRAG_ATT_NAME, PV_SEQ_ATT_NAME, LENGTH_ATT_NAME, CSS_URLS_ATT_NAME, VIDEO_METROPLEX_TYPE, PAGE_VISIT_VID_FRAG_ATT_NAME, POST_METRICS_EVENT_NAME } from '../constants.js';
|
|
8
8
|
import MetroplexSocket from '../api/metroplexSocket.js';
|
|
9
9
|
import { addSafeEventListener } from '../utils/eventlistener.js';
|
|
10
10
|
import { noibuLog } from '../utils/log.js';
|
|
@@ -43,7 +43,7 @@ export default class ClientConfig {
|
|
|
43
43
|
/** Updates the config object to store the given last active time */
|
|
44
44
|
updateLastActiveTime(lastActiveTime: Date): Promise<void>;
|
|
45
45
|
/** Gets the current page visit sequence number that should be used */
|
|
46
|
-
getPageVisitSeq(): Promise<number
|
|
46
|
+
getPageVisitSeq(): Promise<number>;
|
|
47
47
|
/**
|
|
48
48
|
* Returns the client config object from storage or generates a new one
|
|
49
49
|
* What is stored in storage will look like this
|
|
@@ -1,23 +1,13 @@
|
|
|
1
|
+
import { Singleton } from '../../types/Monitor';
|
|
1
2
|
/**
|
|
2
3
|
* HelpCode class is responsible for help code feature related functionality
|
|
3
4
|
*/
|
|
4
|
-
export default class HelpCode {
|
|
5
|
+
export default class HelpCode extends Singleton {
|
|
6
|
+
private requestContext;
|
|
5
7
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @returns {HelpCode}
|
|
8
|
+
* Constructs instance and sets up event listeners
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
-
requestContext: {
|
|
11
|
-
resolve: null;
|
|
12
|
-
reject: null;
|
|
13
|
-
promise: null;
|
|
14
|
-
} | null;
|
|
15
|
-
/**
|
|
16
|
-
* Handles the received help code event.
|
|
17
|
-
* @param {CustomEvent<string>} event - The event object with string detail property.
|
|
18
|
-
* @returns {void}
|
|
19
|
-
*/
|
|
20
|
-
receiveHelpCode(event: CustomEvent<string>): void;
|
|
10
|
+
constructor();
|
|
21
11
|
/**
|
|
22
12
|
* Requests a help code and returns a Promise that resolves when the help code is obtained
|
|
23
13
|
* or rejects if the noibu connection is unavailable.
|
|
@@ -25,5 +15,9 @@ export default class HelpCode {
|
|
|
25
15
|
* @returns {Promise<string>} Promise object representing the help code request.
|
|
26
16
|
* @throws {string} Throws an error if the noibu connection is unavailable.
|
|
27
17
|
*/
|
|
28
|
-
requestHelpCode(): Promise<
|
|
18
|
+
requestHelpCode(): Promise<unknown>;
|
|
19
|
+
/**
|
|
20
|
+
* Handles the received help code event.
|
|
21
|
+
*/
|
|
22
|
+
receiveHelpCode(event: CustomEvent<string>): void;
|
|
29
23
|
}
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PAGE_VISIT_PART_ATT_NAME, PAGE_VISIT_VID_FRAG_ATT_NAME } from '../constants';
|
|
2
|
+
import { Singleton } from '../../types/Monitor';
|
|
3
|
+
import { CompletePageVisit, InboundMessageType, OutboundMessageType, OutboundMessageTypeMap, PageVisitFrag, PageVisitInfo, SlidingMessage, VideoFrag } from '../../types/Metroplex.types';
|
|
4
|
+
import { PVEventMessage } from '../../types/PageVisitEvents.types';
|
|
5
|
+
/**
|
|
6
|
+
* Implements rolling window of specified size,
|
|
7
|
+
* but only makes a cut once array length exceeds 150%.
|
|
8
|
+
* During downsize deletes oldest (lowest indexes) elements.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createSlidingArrayOfSize<T>(size: number, arraySource?: T[], downsizeThreshold?: number, downsizeFactor?: number): T[];
|
|
2
11
|
/** Manages the socket to Metroplex */
|
|
3
|
-
export default class MetroplexSocket {
|
|
4
|
-
|
|
5
|
-
ackedOnce: any;
|
|
6
|
-
connectionCount: any;
|
|
7
|
-
connectionPromise: Promise<any>;
|
|
8
|
-
connectionURL: any;
|
|
9
|
-
currentConnectionAttempts: any;
|
|
10
|
-
forceClosed: any;
|
|
11
|
-
helpCodeCb: null | ((...args: any[]) => any);
|
|
12
|
-
initialReferingURL: any;
|
|
13
|
-
static instance: MetroplexSocket;
|
|
14
|
-
instanceId: any;
|
|
15
|
-
isRetryLoopDisabled: any;
|
|
16
|
-
latestReceivedSeqNumStoredTime: any;
|
|
17
|
-
latestReceivedSeqNumber: any;
|
|
18
|
-
messageSequenceNum: any;
|
|
19
|
-
metroRetryFrequencyMS: any;
|
|
20
|
-
metroplexTypeLock: Record<any, any>;
|
|
21
|
-
pageVisitInfoSent: any;
|
|
22
|
-
postURL: any;
|
|
23
|
-
previousMessageType: any;
|
|
24
|
-
retryMessageQueue: any[];
|
|
25
|
-
retryMetroplexInterval: null | number;
|
|
26
|
-
scriptInstanceId: any;
|
|
27
|
-
sessionLength: any;
|
|
28
|
-
sessionStartTime: any;
|
|
29
|
-
sessionTimestamp: any;
|
|
12
|
+
export default class MetroplexSocket extends Singleton {
|
|
13
|
+
forceClosed: boolean;
|
|
30
14
|
socket: WebSocket | null;
|
|
31
|
-
socketInstanceId: string |
|
|
15
|
+
socketInstanceId: string | null;
|
|
16
|
+
previousMessageType: string;
|
|
17
|
+
currentConnectionAttempts: number;
|
|
18
|
+
connectionCount: number;
|
|
19
|
+
sessionStartTime: any;
|
|
20
|
+
connectionPromise: Promise<void> | null;
|
|
21
|
+
pageVisitInfoSent: boolean;
|
|
22
|
+
connectionURL: string;
|
|
23
|
+
postURL: string;
|
|
24
|
+
messageSequenceNum: number;
|
|
25
|
+
latestReceivedSeqNumber: number;
|
|
26
|
+
isRetryLoopDisabled: boolean;
|
|
27
|
+
/** messages that need to be resent to metroplex since they are lacking confirmation */
|
|
28
|
+
retryMessageQueue: SlidingMessage<keyof OutboundMessageTypeMap>[];
|
|
29
|
+
metroplexTypeLock: Record<OutboundMessageType, boolean>;
|
|
30
|
+
initialURL: string;
|
|
31
|
+
initialReferringURL: string;
|
|
32
|
+
sessionTimestamp: Date;
|
|
33
|
+
latestReceivedSeqNumStoredTime: Date;
|
|
34
|
+
instanceId: string;
|
|
35
|
+
scriptInstanceId?: string;
|
|
36
|
+
sessionLength: number;
|
|
32
37
|
socketCloseCodes: string[];
|
|
33
38
|
socketOpens: string[];
|
|
39
|
+
ackedOnce: boolean;
|
|
40
|
+
metroRetryFrequencyMS: 30000;
|
|
41
|
+
retryMetroplexInterval: ReturnType<typeof setTimeout> | null;
|
|
42
|
+
private helpCodeCb;
|
|
34
43
|
/**
|
|
35
44
|
* Creates an instance of metroplex
|
|
36
45
|
* id of script, to make sure only a single socket is open
|
|
37
46
|
*/
|
|
38
|
-
constructor(scriptInstanceId?: string
|
|
39
|
-
|
|
40
|
-
* gets the singleton instance
|
|
41
|
-
* @returns {MetroplexSocket}
|
|
42
|
-
*/
|
|
43
|
-
static getInstance(scriptInstanceId?: string | number[]): MetroplexSocket;
|
|
47
|
+
constructor(scriptInstanceId?: string);
|
|
48
|
+
private static readonly typeToPayloadPropMap;
|
|
44
49
|
/**
|
|
45
50
|
* Adds the seq num field to the given payload depending on whether its
|
|
46
51
|
* a page visit part or video frag
|
|
47
52
|
*/
|
|
48
|
-
_addSeqNumToPayload(type:
|
|
49
|
-
/**
|
|
50
|
-
* sets the seq num in the payload for the given key and increments the
|
|
51
|
-
* global seq number
|
|
52
|
-
*/
|
|
53
|
-
_setSeqNumInPayloadAndIncrementSeqNum(payloadKey: string, payload: any): void;
|
|
53
|
+
_addSeqNumToPayload<T>(type: OutboundMessageType, payload: T): T;
|
|
54
54
|
/** requests help code and saves a callback to be called on response */
|
|
55
|
-
requestHelpCode(cb:
|
|
55
|
+
requestHelpCode(cb: typeof this.helpCodeCb): Promise<boolean>;
|
|
56
56
|
/**
|
|
57
57
|
* Immediately sends a message to Metroplex over the web socket
|
|
58
58
|
* Queues the message if the connection isn't open yet.
|
|
59
59
|
* returns true if message was sent succefully, false otherwise
|
|
60
60
|
*/
|
|
61
|
-
sendMessage(type:
|
|
61
|
+
sendMessage<T extends OutboundMessageType>(type: T, payload: OutboundMessageTypeMap[T]): Promise<boolean>;
|
|
62
62
|
/** Updates the latest pv message sent timestamp if events contain any user steps
|
|
63
63
|
*/
|
|
64
|
-
_updateLatestPvTimestamp(events:
|
|
64
|
+
_updateLatestPvTimestamp(events: PVEventMessage[]): Promise<void>;
|
|
65
65
|
/** returns true if the socket is either connecting or connected to metroplex */
|
|
66
66
|
isConnected(): boolean;
|
|
67
67
|
/** returns true if we are connecting to the socket */
|
|
@@ -75,25 +75,23 @@ export default class MetroplexSocket {
|
|
|
75
75
|
* connectSocket will establish a websocket connection to the metroplex
|
|
76
76
|
* service
|
|
77
77
|
*/
|
|
78
|
-
connectSocket(): Promise<
|
|
78
|
+
connectSocket(): Promise<void>;
|
|
79
79
|
/** Calculates and sets the end_at field of the payload
|
|
80
80
|
* @param {} payload
|
|
81
81
|
* @param {} isPageVisit
|
|
82
82
|
*/
|
|
83
|
-
addEndTimeToPayload(payload:
|
|
84
|
-
[END_AT_ATT_NAME]: string;
|
|
85
|
-
};
|
|
83
|
+
addEndTimeToPayload<T extends VideoFrag | PageVisitFrag>(payload: T, isPageVisit: boolean): T;
|
|
86
84
|
/** open handler for socket */
|
|
87
85
|
_onSocketOpen(): Promise<void>;
|
|
88
86
|
/** message handler for socket
|
|
89
87
|
* @param {} event
|
|
90
88
|
*/
|
|
91
|
-
_onSocketMessage(event:
|
|
89
|
+
_onSocketMessage(event: InboundMessageType): Promise<void>;
|
|
92
90
|
/**
|
|
93
91
|
* Returns true if the message's payload has the payload type given and has a sequence
|
|
94
92
|
* number higher than seqNum
|
|
95
93
|
*/
|
|
96
|
-
_messagePayloadHasLargerSeqNum(message:
|
|
94
|
+
_messagePayloadHasLargerSeqNum(message: SlidingMessage, payloadType: typeof PAGE_VISIT_PART_ATT_NAME | typeof PAGE_VISIT_VID_FRAG_ATT_NAME, seqNum: number): boolean;
|
|
97
95
|
/**
|
|
98
96
|
* removes messages from the retry queue that are smaller than the
|
|
99
97
|
* latest stored message in metroplex
|
|
@@ -118,39 +116,22 @@ export default class MetroplexSocket {
|
|
|
118
116
|
/**
|
|
119
117
|
* will send a message to metroplex via a post request that will outlive the current page
|
|
120
118
|
*/
|
|
121
|
-
postMessage(msg:
|
|
119
|
+
postMessage(msg: CompletePageVisit): Promise<void>;
|
|
122
120
|
/**
|
|
123
121
|
* Stringifies the payload into JSON, sends it to the back end if the session
|
|
124
122
|
* is active and returns true. If inactive the session and socket are closed
|
|
125
123
|
* and this method returns false.
|
|
126
124
|
*/
|
|
127
|
-
_sendSocketMessage(payload:
|
|
125
|
+
_sendSocketMessage<T extends ValueOf<OutboundMessageTypeMap>>(payload: T): Promise<void>;
|
|
128
126
|
/**
|
|
129
127
|
* Closes the socket connection if the session is inactive. Returns true if the
|
|
130
128
|
* session is inactive
|
|
131
129
|
*/
|
|
132
130
|
closeIfInactive(): Promise<boolean>;
|
|
133
131
|
/** will get page information, calling this will increase the connection count */
|
|
134
|
-
getPageInformation(): Promise<
|
|
135
|
-
br_id: string;
|
|
136
|
-
pv_id: string;
|
|
137
|
-
v: 5;
|
|
138
|
-
seq: number | null;
|
|
139
|
-
on_url: any;
|
|
140
|
-
ref_url: any;
|
|
141
|
-
start_at: any;
|
|
142
|
-
conc: any;
|
|
143
|
-
cv: 2;
|
|
144
|
-
last: boolean;
|
|
145
|
-
script_id: string;
|
|
146
|
-
script_inst_id: any;
|
|
147
|
-
mp_sock_inst_id: any;
|
|
148
|
-
sock_inst_id: string | number[] | null;
|
|
149
|
-
video_recorder: string;
|
|
150
|
-
}>;
|
|
132
|
+
getPageInformation(): Promise<PageVisitInfo>;
|
|
151
133
|
/**
|
|
152
134
|
* Try to parse help code response and fire custom event
|
|
153
|
-
* @param {String} response
|
|
154
135
|
*/
|
|
155
|
-
_tryProcessHelpCodeResponse(response:
|
|
136
|
+
_tryProcessHelpCodeResponse(response: unknown): boolean;
|
|
156
137
|
}
|
|
@@ -1,39 +1,30 @@
|
|
|
1
|
+
import { Singleton } from '../../types/Monitor';
|
|
2
|
+
import { CompletePageVisit, PageVisitFrag, PageVisitInfo, SlidingMessage } from '../../types/Metroplex.types';
|
|
1
3
|
/**
|
|
2
4
|
* This class holds the final page visit. It flushes it to storage and then
|
|
3
5
|
* finally sends it to Metroplex via the post route when the next page is loaded
|
|
4
6
|
*/
|
|
5
|
-
export default class StoredPageVisit {
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
flushedStorage: boolean;
|
|
7
|
+
export default class StoredPageVisit extends Singleton {
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new instance of StoredPageVisit
|
|
10
|
+
*/
|
|
11
|
+
constructor();
|
|
11
12
|
/**
|
|
12
13
|
* Check if the events contain a click or location change. If they do then write the retry
|
|
13
14
|
* queue (which contains the metroplex msg sent above) to storage
|
|
14
|
-
* @param {} retryMessageQueue
|
|
15
|
-
* @param {} pvInfo
|
|
16
15
|
*/
|
|
17
|
-
checkAndStoreRetryQueue(retryMessageQueue:
|
|
16
|
+
checkAndStoreRetryQueue(retryMessageQueue: SlidingMessage[], pvInfo: PageVisitInfo): void;
|
|
18
17
|
/** Writes the page visit frags in the retry queue to storage
|
|
19
|
-
* @param {} retryMessageQueue
|
|
20
|
-
* @param {} pvInfo
|
|
21
18
|
*/
|
|
22
|
-
writePageVisitsFromRetryQueue(retryMessageQueue:
|
|
19
|
+
writePageVisitsFromRetryQueue(retryMessageQueue: SlidingMessage[], pvInfo: PageVisitInfo): void;
|
|
23
20
|
/** Write the given page visit frags to storage
|
|
24
|
-
* @param {} pageVisitFrags
|
|
25
|
-
* @param {} pvInfo
|
|
26
21
|
*/
|
|
27
|
-
_writePageVisitFrags(pageVisitFrags:
|
|
22
|
+
_writePageVisitFrags(pageVisitFrags: PageVisitFrag[], pvInfo: PageVisitInfo): Promise<void>;
|
|
28
23
|
/**
|
|
29
24
|
* Read the stored page visit from storage, create a complete page visit object
|
|
30
25
|
* and then post that to Metroplex
|
|
31
26
|
*/
|
|
32
|
-
_getPostData(): Promise<
|
|
33
|
-
pvi: any;
|
|
34
|
-
pvp: never[];
|
|
35
|
-
pvvf: never[];
|
|
36
|
-
} | null>;
|
|
27
|
+
_getPostData(): Promise<CompletePageVisit>;
|
|
37
28
|
/**
|
|
38
29
|
* Creates and tries to resolve a promise that posts the previous page visit
|
|
39
30
|
* to Metroplex from storage
|
|
@@ -45,5 +36,5 @@ export default class StoredPageVisit {
|
|
|
45
36
|
*/
|
|
46
37
|
_updateStorageFlushed(): void;
|
|
47
38
|
/** Returns a promise that resolves to post the page visit in storage to Metroplex */
|
|
48
|
-
_getPostPageVisitPromise(): Promise<
|
|
39
|
+
_getPostPageVisitPromise(): Promise<void>;
|
|
49
40
|
}
|