noibu-react-native 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/api/clientConfig.js +20 -27
  2. package/dist/api/helpCode.js +61 -87
  3. package/dist/api/metroplexSocket.js +72 -65
  4. package/dist/api/storedPageVisit.js +150 -208
  5. package/dist/constants.js +3 -7
  6. package/dist/entry/init.js +13 -15
  7. package/dist/monitors/{appNavigationMonitor.js → AppNavigationMonitor.js} +10 -19
  8. package/dist/monitors/BaseMonitor.js +23 -0
  9. package/dist/monitors/ClickMonitor.js +198 -0
  10. package/dist/monitors/ErrorMonitor.js +206 -0
  11. package/dist/monitors/KeyboardInputMonitor.js +60 -0
  12. package/dist/monitors/PageMonitor.js +98 -0
  13. package/dist/monitors/RequestMonitor.js +390 -0
  14. package/dist/monitors/http-tools/GqlErrorValidator.js +259 -0
  15. package/dist/monitors/{httpDataBundler.js → http-tools/HTTPDataBundler.js} +23 -102
  16. package/dist/pageVisit/{eventDebouncer.js → EventDebouncer.js} +36 -47
  17. package/dist/pageVisit/pageVisitEventError.js +3 -3
  18. package/dist/pageVisit/pageVisitEventHTTP.js +5 -4
  19. package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +22 -5
  20. package/dist/sessionRecorder/sessionRecorder.js +5 -2
  21. package/dist/src/api/clientConfig.d.ts +8 -13
  22. package/dist/src/api/clientConfig.test.d.ts +1 -0
  23. package/dist/src/api/helpCode.d.ts +10 -16
  24. package/dist/src/api/metroplexSocket.d.ts +52 -71
  25. package/dist/src/api/storedPageVisit.d.ts +12 -21
  26. package/dist/src/constants.d.ts +1 -0
  27. package/dist/src/monitors/AppNavigationMonitor.d.ts +18 -0
  28. package/dist/src/monitors/BaseMonitor.d.ts +13 -0
  29. package/dist/src/monitors/BaseMonitor.test.d.ts +1 -0
  30. package/dist/src/monitors/ClickMonitor.d.ts +31 -0
  31. package/dist/src/monitors/ErrorMonitor.d.ts +63 -0
  32. package/dist/src/monitors/{keyboardInputMonitor.d.ts → KeyboardInputMonitor.d.ts} +7 -4
  33. package/dist/src/monitors/{pageMonitor.d.ts → PageMonitor.d.ts} +6 -8
  34. package/dist/src/monitors/RequestMonitor.d.ts +94 -0
  35. package/dist/src/monitors/http-tools/GqlErrorValidator.d.ts +59 -0
  36. package/dist/src/monitors/{httpDataBundler.d.ts → http-tools/HTTPDataBundler.d.ts} +13 -28
  37. package/dist/src/monitors/integrations/react-native-navigation-integration.d.ts +3 -2
  38. package/dist/src/pageVisit/{eventDebouncer.d.ts → EventDebouncer.d.ts} +3 -10
  39. package/dist/src/pageVisit/pageVisit.d.ts +1 -1
  40. package/dist/src/pageVisit/pageVisitEventHTTP.d.ts +3 -3
  41. package/dist/src/sessionRecorder/nativeSessionRecorderSubscription.d.ts +15 -0
  42. package/dist/src/storage/rnStorageProvider.d.ts +1 -1
  43. package/dist/src/storage/storage.d.ts +1 -1
  44. package/dist/src/storage/storageProvider.d.ts +2 -2
  45. package/dist/src/utils/function.d.ts +4 -5
  46. package/dist/src/utils/object.d.ts +3 -5
  47. package/dist/src/utils/polyfills.d.ts +1 -4
  48. package/dist/types/Metroplex.types.d.ts +73 -0
  49. package/dist/types/PageVisit.types.d.ts +2 -145
  50. package/dist/types/PageVisitErrors.types.d.ts +114 -0
  51. package/dist/types/PageVisitEvents.types.d.ts +91 -0
  52. package/dist/types/Storage.d.ts +1 -1
  53. package/dist/types/StoredPageVisit.types.d.ts +4 -45
  54. package/dist/utils/function.js +0 -1
  55. package/dist/utils/object.js +1 -0
  56. package/package.json +11 -7
  57. package/dist/monitors/clickMonitor.js +0 -258
  58. package/dist/monitors/errorMonitor.js +0 -202
  59. package/dist/monitors/gqlErrorValidator.js +0 -306
  60. package/dist/monitors/inputMonitor.js +0 -138
  61. package/dist/monitors/keyboardInputMonitor.js +0 -66
  62. package/dist/monitors/pageMonitor.js +0 -122
  63. package/dist/monitors/requestMonitor.js +0 -386
  64. package/dist/src/monitors/appNavigationMonitor.d.ts +0 -22
  65. package/dist/src/monitors/clickMonitor.d.ts +0 -44
  66. package/dist/src/monitors/errorMonitor.d.ts +0 -28
  67. package/dist/src/monitors/gqlErrorValidator.d.ts +0 -82
  68. package/dist/src/monitors/inputMonitor.d.ts +0 -34
  69. package/dist/src/monitors/requestMonitor.d.ts +0 -10
  70. package/dist/types/RRWeb.d.ts +0 -48
  71. package/dist/types/ReactNative.d.ts +0 -4
@@ -5,6 +5,7 @@ import { stringifyJSON, getMaxSubstringAllowed, getUserAgent, makeRequest } from
5
5
  import Storage from '../storage/storage.js';
6
6
  import { noibuLog } from '../utils/log.js';
7
7
  import { unwrapNoibuWrapped } from '../utils/object.js';
8
+ import { Singleton } from '../monitors/BaseMonitor.js';
8
9
 
9
10
  /**
10
11
  * Singleton class to manage the client configuration
@@ -12,54 +13,46 @@ import { unwrapNoibuWrapped } from '../utils/object.js';
12
13
  * status of the client script as well as managing all storage
13
14
  * storing and retrieval.
14
15
  */
15
- class ClientConfig {
16
+ class ClientConfig extends Singleton {
16
17
  /**
17
18
  * Creates a ClientConfig singleton instance
18
19
  */
19
20
  constructor(noibuErrorURL, customerConfig) {
20
- this.locationBreadcrumbs = [];
21
- // sets up this.browserId, this.disabledStatus
21
+ super();
22
22
  this.pageVisitId = uuid.v4();
23
- // variables stored in storage
24
- this.isClientDisabled = false;
25
23
  this.browserId = '';
26
24
  this.pageVisitSeq = null;
27
25
  // This variable tracks the last time the user was active in this session.
28
26
  // It is also written to storage. Initialized to now so the session can be
29
27
  // timed out even if a PV is never sent.
30
28
  this.lastActiveTime = new Date();
31
- // error URL to send Noibu errors to
32
- this.noibuErrorURL = noibuErrorURL;
33
29
  // error sent to backend counter
34
30
  this.cltErrorPostCounter = 0;
35
31
  // variables for checking if the socket is inactive
36
32
  // used a class variables in order to be changed in testing
37
33
  this.maxSocketInactiveTime = MAX_METROPLEX_SOCKET_INNACTIVE_TIME;
34
+ this.locationBreadcrumbs = [];
35
+ this.isClientDisabled = false;
36
+ this.noibuErrorURL = noibuErrorURL;
38
37
  this.customerDomain = customerConfig.domain;
39
38
  this.listOfUrlsToCollectHttpDataFrom =
40
39
  customerConfig.listOfUrlsToCollectHttpDataFrom;
41
40
  this.enableHttpDataCollection = customerConfig.enableHttpDataCollection;
42
41
  this.blockedElements = customerConfig.blockedElements;
43
- }
44
- /** Configures the singleton instance */
45
- static configureInstance(_a) {
46
- return __awaiter(this, arguments, void 0, function* ({ noibuErrorURL, customerConfig, }) {
47
- if (!this.instance) {
48
- ClientConfig.noibuErrorURL = noibuErrorURL;
49
- this.instance = new ClientConfig(noibuErrorURL, customerConfig);
50
- // sets up this.browserId, this.isClientDisabled, this.pageVisitSeq
51
- yield this.instance._setupStorageVars();
52
- }
53
- });
42
+ if (!noibuErrorURL || !this.hasAllNecessaryArgs()) {
43
+ const reason = Object.assign({ msg: 'ClientConfig was not properly configured' }, customerConfig);
44
+ void this.postNoibuErrorAndOptionallyDisableClient(reason, true, SEVERITY.error);
45
+ this.configurationPromise = Promise.reject(reason);
46
+ return;
47
+ }
48
+ // sets up this.browserId, this.isClientDisabled, this.pageVisitSeq
49
+ this.configurationPromise = this._setupStorageVars();
54
50
  }
55
51
  /**
56
- * gets the singleton instance
52
+ * Convenience method to check correct setup
57
53
  */
58
- static getInstance() {
59
- if (!this.instance) {
60
- throw new Error('ClientConfig was not configured');
61
- }
62
- return this.instance;
54
+ hasAllNecessaryArgs() {
55
+ return (!!this.customerDomain && this.globalUrl.includes(this.customerDomain));
63
56
  }
64
57
  /** lockClient will disable the client script for a single pagevisit for
65
58
  * duration given in minuntes */
@@ -285,7 +278,7 @@ class ClientConfig {
285
278
  /** gets current global url */
286
279
  get globalUrl() {
287
280
  const globalUrl = new URL('https://localhost');
288
- globalUrl.hostname = ClientConfig.getInstance().customerDomain;
281
+ globalUrl.hostname = this.customerDomain;
289
282
  if (this.locationBreadcrumbs.length) {
290
283
  globalUrl.pathname = this.locationBreadcrumbs.join('/');
291
284
  }
@@ -296,8 +289,8 @@ class ClientConfig {
296
289
  * and disable the client if required
297
290
  * severity expects one of the SEVERITY_x level constants, or else error will be used
298
291
  */
299
- postNoibuErrorAndOptionallyDisableClient(errorMsg_1, disableClient_1, severity_1) {
300
- return __awaiter(this, arguments, void 0, function* (errorMsg, disableClient, severity, keepAlive = false) {
292
+ postNoibuErrorAndOptionallyDisableClient(errorMsg_1) {
293
+ return __awaiter(this, arguments, void 0, function* (errorMsg, disableClient = false, severity = SEVERITY.error, keepAlive = false) {
301
294
  noibuLog('postNoibuErrorAndOptionallyDisableClient', {
302
295
  errorMsg,
303
296
  disableClient,
@@ -1,100 +1,74 @@
1
+ import { __awaiter } from 'tslib';
1
2
  import MetroplexSocket from './metroplexSocket.js';
2
3
  import { SEVERITY } from '../constants.js';
3
4
  import ClientConfig from './clientConfig.js';
5
+ import { Singleton } from '../monitors/BaseMonitor.js';
4
6
 
5
7
  /**
6
8
  * HelpCode class is responsible for help code feature related functionality
7
9
  */
8
- class HelpCode {
9
- /**
10
- * Singleton instance
11
- * @returns {HelpCode}
12
- */
13
- static getInstance() {
14
- if (!this.instance) {
15
- this.instance = new HelpCode();
10
+ class HelpCode extends Singleton {
11
+ /**
12
+ * Constructs instance and sets up event listeners
13
+ */
14
+ constructor() {
15
+ super();
16
+ this.requestContext = null;
17
+ this.receiveHelpCode = this.receiveHelpCode.bind(this);
16
18
  }
17
-
18
- return this.instance;
19
- }
20
-
21
- /**
22
- * Constructs instance and sets up event listeners
23
- */
24
- constructor() {
25
- this.requestContext = null;
26
- this.receiveHelpCode = this.receiveHelpCode.bind(this);
27
- }
28
-
29
- /**
30
- * Requests a help code and returns a Promise that resolves when the help code is obtained
31
- * or rejects if the noibu connection is unavailable.
32
-
33
- * @returns {Promise<string>} Promise object representing the help code request.
34
- * @throws {string} Throws an error if the noibu connection is unavailable.
35
- */
36
- async requestHelpCode() {
37
- if (this.requestContext !== null) {
38
- return this.requestContext.promise;
39
- }
40
-
41
- const context = {
42
- resolve: null,
43
- reject: null,
44
- promise: null,
45
- };
46
-
47
- context.promise = new Promise((resolve, reject) => {
48
- context.resolve = resolve;
49
- context.reject = reject;
50
- });
51
-
52
- this.requestContext = context;
53
-
54
- const result = await MetroplexSocket.getInstance().requestHelpCode(
55
- this.receiveHelpCode,
56
- );
57
-
58
- if (result === false) {
59
- this.requestContext = null;
60
- return Promise.reject(new Error('noibu connection is unavailable'));
19
+ /**
20
+ * Requests a help code and returns a Promise that resolves when the help code is obtained
21
+ * or rejects if the noibu connection is unavailable.
22
+
23
+ * @returns {Promise<string>} Promise object representing the help code request.
24
+ * @throws {string} Throws an error if the noibu connection is unavailable.
25
+ */
26
+ requestHelpCode() {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ if (this.requestContext !== null) {
29
+ return this.requestContext.promise;
30
+ }
31
+ const context = {
32
+ resolve: null,
33
+ reject: null,
34
+ promise: null,
35
+ };
36
+ context.promise = new Promise((resolve, reject) => {
37
+ context.resolve = resolve;
38
+ context.reject = reject;
39
+ });
40
+ this.requestContext = context;
41
+ const result = yield MetroplexSocket.getInstance().requestHelpCode(this.receiveHelpCode);
42
+ if (!result) {
43
+ this.requestContext = null;
44
+ return Promise.reject(new Error('noibu connection is unavailable'));
45
+ }
46
+ return this.requestContext.promise;
47
+ });
61
48
  }
62
-
63
- return this.requestContext.promise;
64
- }
65
-
66
- /**
67
- * Handles the received help code event.
68
- * @param {CustomEvent<string>} event - The event object with string detail property.
69
- * @returns {void}
70
- */
71
- receiveHelpCode(event) {
72
- if (this.requestContext === null) {
73
- const { success, data } = event.detail;
74
-
75
- if (!success) {
76
- const message = `Noibu help code is not available due to ${data}`;
77
-
78
- ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
79
- message,
80
- false,
81
- SEVERITY.error,
82
- );
83
- }
84
- return;
85
- }
86
-
87
- const context = this.requestContext;
88
- this.requestContext = null;
89
-
90
- const { success, data } = event.detail;
91
-
92
- if (success) {
93
- context.resolve(data);
94
- } else {
95
- context.reject(new Error(data));
49
+ /**
50
+ * Handles the received help code event.
51
+ */
52
+ receiveHelpCode(event) {
53
+ var _a, _b;
54
+ if (this.requestContext === null) {
55
+ const { success, data } = event.detail;
56
+ if (!success) {
57
+ const message = `Noibu help code is not available due to ${data}`;
58
+ ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(message, false, SEVERITY.error);
59
+ }
60
+ return;
61
+ }
62
+ const context = this.requestContext;
63
+ this.requestContext = null;
64
+ const { success, data } = event.detail;
65
+ if (success) {
66
+ (_a = context.resolve) === null || _a === void 0 ? void 0 : _a.call(context, data);
67
+ }
68
+ else {
69
+ (_b = context.reject) === null || _b === void 0 ? void 0 : _b.call(context, new Error(data));
70
+ }
96
71
  }
97
- }
98
72
  }
99
73
 
100
74
  export { HelpCode as default };
@@ -2,7 +2,7 @@ import { __awaiter } from 'tslib';
2
2
  import uuid from 'react-native-uuid';
3
3
  import { getUserAgent, stringifyJSON, getVideoRecorderType } from '../utils/function.js';
4
4
  import { addSafeEventListener } from '../utils/eventlistener.js';
5
- import { GET_METROPLEX_BASE_SOCKET_URL, METROPLEX_FRAG_ROUTE, GET_METROPLEX_POST_URL, METROPLEX_RETRY_FREQUENCY, META_DATA_METROPLEX_TYPE, PAGE_VISIT_META_DATA_ATT_NAME, HTTP_DATA_METROPLEX_TYPE, PAGE_VISIT_HTTP_DATA_ATT_NAME, VIDEO_METROPLEX_TYPE, PAGE_VISIT_VID_FRAG_ATT_NAME, PV_METROPLEX_TYPE, PAGE_VISIT_PART_ATT_NAME, SEQ_NUM_ATT_NAME, WORK_REQUEST_ATT_NAME, HELP_CODE_ATT_NAME, PV_EVENTS_ATT_NAME, TYPE_ATT_NAME, USERSTEP_EVENT_TYPE, GET_MAX_METROPLEX_RECONNECTION_NUMBER, MAX_METROPLEX_CONNECTION_COUNT, GET_METROPLEX_CONSECUTIVE_CONNECTION_DELAY, END_AT_ATT_NAME, SEVERITY, OK_SOCKET_MESSAGE, CLOSE_CONNECTION_FORCEFULLY, BLOCK_SOCKET_MESSAGE, STOP_STORING_PV_SOCKET_MESSAGE, STOP_STORING_VID_SOCKET_MESSAGE, MAX_BEACON_PAYLOAD_SIZE, PAGE_VISIT_INFORMATION_ATT_NAME, VIDEO_PART_COUNT_ATT_NAME, IS_LAST_ATT_NAME, BROWSER_ID_ATT_NAME, PV_ID_ATT_NAME, VER_ATT_NAME, CURRENT_PV_VERSION, PV_SEQ_ATT_NAME, ON_URL_ATT_NAME, REF_URL_ATT_NAME, STARTED_AT_ATT_NAME, CONN_COUNT_ATT_NAME, COLLECT_VER_ATT_NAME, CURRENT_NOIBUJS_VERSION, SCRIPT_ID_ATT_NAME, GET_SCRIPT_ID, SCRIPT_INSTANCE_ID_ATT_NAME, METROPLEX_SOCKET_INSTANCE_ID_ATT_NAME, SOCKET_INSTANCE_ID_ATT_NAME, VIDEO_RECORDER_ATT_NAME } from '../constants.js';
5
+ import { GET_METROPLEX_BASE_SOCKET_URL, METROPLEX_FRAG_ROUTE, GET_METROPLEX_POST_URL, METROPLEX_RETRY_FREQUENCY, SEQ_NUM_ATT_NAME, WORK_REQUEST_ATT_NAME, HELP_CODE_ATT_NAME, PV_METROPLEX_TYPE, PAGE_VISIT_PART_ATT_NAME, PV_EVENTS_ATT_NAME, TYPE_ATT_NAME, USERSTEP_EVENT_TYPE, GET_MAX_METROPLEX_RECONNECTION_NUMBER, MAX_METROPLEX_CONNECTION_COUNT, GET_METROPLEX_CONSECUTIVE_CONNECTION_DELAY, END_AT_ATT_NAME, SEVERITY, OK_SOCKET_MESSAGE, CLOSE_CONNECTION_FORCEFULLY, BLOCK_SOCKET_MESSAGE, STOP_STORING_PV_SOCKET_MESSAGE, STOP_STORING_VID_SOCKET_MESSAGE, VIDEO_METROPLEX_TYPE, PAGE_VISIT_VID_FRAG_ATT_NAME, MAX_BEACON_PAYLOAD_SIZE, PAGE_VISIT_INFORMATION_ATT_NAME, PAGE_VISIT_HTTP_DATA_ATT_NAME, VIDEO_PART_COUNT_ATT_NAME, IS_LAST_ATT_NAME, HTTP_DATA_METROPLEX_TYPE, BROWSER_ID_ATT_NAME, PV_ID_ATT_NAME, VER_ATT_NAME, CURRENT_PV_VERSION, PV_SEQ_ATT_NAME, ON_URL_ATT_NAME, REF_URL_ATT_NAME, STARTED_AT_ATT_NAME, CONN_COUNT_ATT_NAME, COLLECT_VER_ATT_NAME, CURRENT_NOIBUJS_VERSION, SCRIPT_ID_ATT_NAME, GET_SCRIPT_ID, SCRIPT_INSTANCE_ID_ATT_NAME, METROPLEX_SOCKET_INSTANCE_ID_ATT_NAME, SOCKET_INSTANCE_ID_ATT_NAME, VIDEO_RECORDER_ATT_NAME, META_DATA_METROPLEX_TYPE, PAGE_VISIT_META_DATA_ATT_NAME, MAX_RETRY_MSG_Q_SIZE } from '../constants.js';
6
6
  import ClientConfig from './clientConfig.js';
7
7
  import StoredMetrics from './storedMetrics.js';
8
8
  import StoredPageVisit from './storedPageVisit.js';
@@ -10,15 +10,39 @@ import { safePerformanceNow } from '../utils/performance.js';
10
10
  import { isDateOverwritten } from '../utils/date.js';
11
11
  import { unwrapNoibuWrapped } from '../utils/object.js';
12
12
  import { noibuLog } from '../utils/log.js';
13
+ import { Singleton } from '../monitors/BaseMonitor.js';
13
14
 
15
+ /**
16
+ * Implements rolling window of specified size,
17
+ * but only makes a cut once array length exceeds 150%.
18
+ * During downsize deletes oldest (lowest indexes) elements.
19
+ */
20
+ function createSlidingArrayOfSize(size, arraySource = [], downsizeThreshold = 1.5, downsizeFactor = 2) {
21
+ return new Proxy(arraySource, {
22
+ /**
23
+ * override push to implement sliding window
24
+ */
25
+ get(target, p) {
26
+ if (p === 'push') {
27
+ return (...args) => {
28
+ target.push(...args);
29
+ if (target.length >= downsizeThreshold * size) {
30
+ target.splice(0, size / downsizeFactor);
31
+ }
32
+ };
33
+ }
34
+ return target[p];
35
+ },
36
+ });
37
+ }
14
38
  /** Manages the socket to Metroplex */
15
- class MetroplexSocket {
39
+ class MetroplexSocket extends Singleton {
16
40
  /**
17
41
  * Creates an instance of metroplex
18
42
  * id of script, to make sure only a single socket is open
19
43
  */
20
44
  constructor(scriptInstanceId) {
21
- const socketBase = GET_METROPLEX_BASE_SOCKET_URL();
45
+ super();
22
46
  // this flag is set to true when we manually need to close the
23
47
  // socket. It happens currently during the pagehide event and
24
48
  // if we havent sent a message to our backend in some time.
@@ -33,12 +57,13 @@ class MetroplexSocket {
33
57
  this.currentConnectionAttempts = 0;
34
58
  // how many successful connections to metroplex
35
59
  this.connectionCount = 0;
36
- // sessiont start time, used to calculate accurate end time
60
+ // session start time, used to calculate accurate end time
37
61
  this.sessionStartTime = safePerformanceNow();
62
+ this.connectionPromise = null;
38
63
  // Whether or not we have sent the page visit information after connecting the socket
39
64
  this.pageVisitInfoSent = false;
40
65
  // socket connection url
41
- this.connectionURL = `${socketBase}/${METROPLEX_FRAG_ROUTE}`;
66
+ this.connectionURL = `${GET_METROPLEX_BASE_SOCKET_URL()}/${METROPLEX_FRAG_ROUTE}`;
42
67
  // post endpoint for the same events we would send to the socket
43
68
  this.postURL = GET_METROPLEX_POST_URL();
44
69
  // sequence number of the message sent to metroplex
@@ -47,25 +72,20 @@ class MetroplexSocket {
47
72
  this.latestReceivedSeqNumber = -1;
48
73
  // set to true only when we start running behind on messages in metroplex
49
74
  this.isRetryLoopDisabled = false;
50
- // messages that need to be resent to metroplex since they are lacking confirmation
51
- this.retryMessageQueue = [];
75
+ /** messages that need to be resent to metroplex since they are lacking confirmation */
76
+ this.retryMessageQueue = createSlidingArrayOfSize(MAX_RETRY_MSG_Q_SIZE);
52
77
  // this map will hold all types that noibujs should not send to metroplex
53
78
  this.metroplexTypeLock = {};
54
79
  // setting initial URL at the start in order to guarentee that the
55
80
  // current page visit has the real initial onURL. Fragments and SPA's
56
81
  // can change the URL without reloading the page.
57
- this._initialURL = ClientConfig.getInstance().globalUrl;
58
- noibuLog('metroplex constructor', {
59
- initialURL: this._initialURL,
60
- });
61
- this.initialReferingURL = '';
82
+ this.initialURL = ClientConfig.getInstance().globalUrl;
83
+ this.initialReferringURL = '';
62
84
  this.sessionTimestamp = new Date();
63
85
  // latest time that we received a confirmation message from metroplex
64
86
  this.latestReceivedSeqNumStoredTime = new Date();
65
87
  // unique instance id of Metroplex Socket
66
88
  this.instanceId = uuid.v4();
67
- // unique script instance id
68
- this.scriptInstanceId = scriptInstanceId;
69
89
  // length of the session based on page visit events
70
90
  this.sessionLength = 0;
71
91
  // track socket closure codes for debug
@@ -76,51 +96,30 @@ class MetroplexSocket {
76
96
  this.ackedOnce = false;
77
97
  // retry frequency in ms
78
98
  this.metroRetryFrequencyMS = METROPLEX_RETRY_FREQUENCY;
79
- this.helpCodeCb = null;
80
99
  this.retryMetroplexInterval = null;
100
+ this.helpCodeCb = null;
101
+ noibuLog('Metroplex constructor', {
102
+ initialURL: this.initialURL,
103
+ scriptInstanceId,
104
+ });
105
+ this.scriptInstanceId = scriptInstanceId;
81
106
  // Connect the WS
82
107
  this.connectionPromise = this.connectSocket();
83
108
  // Set up the offload events immediately
84
109
  this._setupOffloadEvents();
85
110
  }
86
- /**
87
- * gets the singleton instance
88
- * @returns {MetroplexSocket}
89
- */
90
- static getInstance(scriptInstanceId) {
91
- if (!this.instance) {
92
- this.instance = new MetroplexSocket(scriptInstanceId);
93
- }
94
- return this.instance;
95
- }
96
111
  /**
97
112
  * Adds the seq num field to the given payload depending on whether its
98
113
  * a page visit part or video frag
99
114
  */
100
115
  _addSeqNumToPayload(type, payload) {
101
- switch (type) {
102
- case PV_METROPLEX_TYPE:
103
- this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_PART_ATT_NAME, payload);
104
- break;
105
- case VIDEO_METROPLEX_TYPE:
106
- this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_VID_FRAG_ATT_NAME, payload);
107
- break;
108
- case HTTP_DATA_METROPLEX_TYPE:
109
- this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_HTTP_DATA_ATT_NAME, payload);
110
- break;
111
- case META_DATA_METROPLEX_TYPE:
112
- this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_META_DATA_ATT_NAME, payload);
113
- break;
116
+ const newPayload = Object.assign({}, payload);
117
+ const key = MetroplexSocket.typeToPayloadPropMap[type];
118
+ if (key) {
119
+ newPayload[key][SEQ_NUM_ATT_NAME] = this.messageSequenceNum;
120
+ this.messageSequenceNum += 1;
114
121
  }
115
- }
116
- /**
117
- * sets the seq num in the payload for the given key and increments the
118
- * global seq number
119
- */
120
- _setSeqNumInPayloadAndIncrementSeqNum(payloadKey, payload) {
121
- // eslint-disable-next-line no-param-reassign
122
- payload[payloadKey][SEQ_NUM_ATT_NAME] = this.messageSequenceNum;
123
- this.messageSequenceNum += 1;
122
+ return newPayload;
124
123
  }
125
124
  /** requests help code and saves a callback to be called on response */
126
125
  requestHelpCode(cb) {
@@ -136,7 +135,7 @@ class MetroplexSocket {
136
135
  */
137
136
  sendMessage(type, payload) {
138
137
  return __awaiter(this, void 0, void 0, function* () {
139
- noibuLog('sendMessage start', { type, payload: JSON.stringify(payload) });
138
+ noibuLog('sendMessage start', { type });
140
139
  // if we have a lock on this specific type, we dont send it
141
140
  if (type in this.metroplexTypeLock ||
142
141
  ClientConfig.getInstance().isClientDisabled) {
@@ -146,19 +145,18 @@ class MetroplexSocket {
146
145
  });
147
146
  return false;
148
147
  }
149
- const payloadData = payload;
150
148
  if (type !== WORK_REQUEST_ATT_NAME) {
151
149
  // Increasing the message sequence number for every message we send to metroplex
152
150
  // and move the data to the retry queue.
153
- this._addSeqNumToPayload(type, payloadData);
151
+ this._addSeqNumToPayload(type, payload);
154
152
  // push the message to the retry queue immediately in case socket isnt connected
155
- this.retryMessageQueue.push({ payload: payloadData, type });
153
+ this.retryMessageQueue.push({ payload, type });
156
154
  StoredPageVisit.getInstance().checkAndStoreRetryQueue(this.retryMessageQueue, yield this.getPageInformation());
157
155
  }
158
156
  // send the socket message if we are connected and have sent page visit info
159
157
  if (this.isConnected() && this.pageVisitInfoSent) {
160
158
  // sending the data to metroplex
161
- yield this._sendSocketMessage(payloadData);
159
+ yield this._sendSocketMessage(payload);
162
160
  }
163
161
  this.previousMessageType = type;
164
162
  // Only update the last message send if its a page visit with user action
@@ -256,7 +254,7 @@ class MetroplexSocket {
256
254
  // if we tried reconnecting too many times we abandon
257
255
  if (this.connectionCount >= MAX_METROPLEX_CONNECTION_COUNT) {
258
256
  // if we tried beyond the threshold, we block ourselves a short
259
- // while while fix the issue
257
+ // while fix the issue
260
258
  yield ClientConfig.getInstance().lockClientUntilNextPage('Too many connections, locking until next page');
261
259
  return;
262
260
  }
@@ -355,6 +353,9 @@ class MetroplexSocket {
355
353
  // we do nothing on an OK
356
354
  break;
357
355
  default:
356
+ if (!event.data) {
357
+ break;
358
+ }
358
359
  // we now need to check if we receive text that contains
359
360
  // the text SEQ_NUM. if that is true, then its a seq num
360
361
  // and we need to clear out the retry queue.
@@ -409,8 +410,9 @@ class MetroplexSocket {
409
410
  */
410
411
  _clearRetryQueue(seqNum) {
411
412
  this.latestReceivedSeqNumStoredTime = new Date();
412
- this.retryMessageQueue = this.retryMessageQueue.filter(message => this._messagePayloadHasLargerSeqNum(message, PAGE_VISIT_PART_ATT_NAME, seqNum) ||
413
+ const newQueue = this.retryMessageQueue.filter(message => this._messagePayloadHasLargerSeqNum(message, PAGE_VISIT_PART_ATT_NAME, seqNum) ||
413
414
  this._messagePayloadHasLargerSeqNum(message, PAGE_VISIT_VID_FRAG_ATT_NAME, seqNum));
415
+ this.retryMessageQueue = createSlidingArrayOfSize(MAX_RETRY_MSG_Q_SIZE, newQueue);
414
416
  }
415
417
  /** will resend everything that is in the retry queue */
416
418
  _sendUnconfirmedMessages(socketWasAlreadyOpen) {
@@ -622,7 +624,9 @@ class MetroplexSocket {
622
624
  return;
623
625
  }
624
626
  const payloadJson = stringifyJSON(payload);
625
- noibuLog(`_sendSocketMessage sending: ${payloadJson}`);
627
+ noibuLog(`_sendSocketMessage sending: ${PAGE_VISIT_VID_FRAG_ATT_NAME in payload
628
+ ? PAGE_VISIT_VID_FRAG_ATT_NAME
629
+ : payloadJson}`);
626
630
  if (this.socket) {
627
631
  this.socket.send(payloadJson);
628
632
  }
@@ -642,12 +646,10 @@ class MetroplexSocket {
642
646
  // the next page is loaded.
643
647
  yield ClientConfig.getInstance().lockClientUntilNextPage('Session is inactive, locking until next page');
644
648
  this.close();
645
- Promise.all([
646
- // post metrics now so we don't include events after going inactive
647
- StoredMetrics.getInstance().postMetrics(),
648
- // post retry queue now so we don't include event after going inactive
649
- this.postFullPageVisit(MAX_BEACON_PAYLOAD_SIZE),
650
- ]);
649
+ // post metrics now so we don't include events after going inactive
650
+ StoredMetrics.getInstance().postMetrics();
651
+ // post retry queue now so we don't include event after going inactive
652
+ this.postFullPageVisit(MAX_BEACON_PAYLOAD_SIZE);
651
653
  }
652
654
  return inactive;
653
655
  });
@@ -660,8 +662,8 @@ class MetroplexSocket {
660
662
  [PV_ID_ATT_NAME]: ClientConfig.getInstance().pageVisitId,
661
663
  [VER_ATT_NAME]: CURRENT_PV_VERSION,
662
664
  [PV_SEQ_ATT_NAME]: yield ClientConfig.getInstance().getPageVisitSeq(),
663
- [ON_URL_ATT_NAME]: this._initialURL,
664
- [REF_URL_ATT_NAME]: this.initialReferingURL,
665
+ [ON_URL_ATT_NAME]: this.initialURL,
666
+ [REF_URL_ATT_NAME]: this.initialReferringURL,
665
667
  [STARTED_AT_ATT_NAME]: this.sessionTimestamp.toISOString(),
666
668
  [CONN_COUNT_ATT_NAME]: this.connectionCount,
667
669
  [COLLECT_VER_ATT_NAME]: CURRENT_NOIBUJS_VERSION,
@@ -676,7 +678,6 @@ class MetroplexSocket {
676
678
  }
677
679
  /**
678
680
  * Try to parse help code response and fire custom event
679
- * @param {String} response
680
681
  */
681
682
  _tryProcessHelpCodeResponse(response) {
682
683
  const prefix = `${HELP_CODE_ATT_NAME}:`;
@@ -691,5 +692,11 @@ class MetroplexSocket {
691
692
  return true;
692
693
  }
693
694
  }
695
+ MetroplexSocket.typeToPayloadPropMap = {
696
+ [HTTP_DATA_METROPLEX_TYPE]: PAGE_VISIT_HTTP_DATA_ATT_NAME,
697
+ [META_DATA_METROPLEX_TYPE]: PAGE_VISIT_META_DATA_ATT_NAME,
698
+ [PV_METROPLEX_TYPE]: PAGE_VISIT_PART_ATT_NAME,
699
+ [VIDEO_METROPLEX_TYPE]: PAGE_VISIT_VID_FRAG_ATT_NAME,
700
+ };
694
701
 
695
- export { MetroplexSocket as default };
702
+ export { createSlidingArrayOfSize, MetroplexSocket as default };