noibu-react-native 0.0.1

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 (40) hide show
  1. package/README.md +155 -0
  2. package/dist/api/clientConfig.js +416 -0
  3. package/dist/api/helpCode.js +106 -0
  4. package/dist/api/inputManager.js +233 -0
  5. package/dist/api/metroplexSocket.js +882 -0
  6. package/dist/api/storedMetrics.js +201 -0
  7. package/dist/api/storedPageVisit.js +235 -0
  8. package/dist/const_matchers.js +260 -0
  9. package/dist/constants.d.ts +264 -0
  10. package/dist/constants.js +528 -0
  11. package/dist/entry/index.d.ts +8 -0
  12. package/dist/entry/index.js +15 -0
  13. package/dist/entry/init.js +91 -0
  14. package/dist/monitors/clickMonitor.js +284 -0
  15. package/dist/monitors/elementMonitor.js +174 -0
  16. package/dist/monitors/errorMonitor.js +295 -0
  17. package/dist/monitors/gqlErrorValidator.js +306 -0
  18. package/dist/monitors/httpDataBundler.js +665 -0
  19. package/dist/monitors/inputMonitor.js +130 -0
  20. package/dist/monitors/keyboardInputMonitor.js +67 -0
  21. package/dist/monitors/locationChangeMonitor.js +30 -0
  22. package/dist/monitors/pageMonitor.js +119 -0
  23. package/dist/monitors/requestMonitor.js +679 -0
  24. package/dist/pageVisit/pageVisit.js +172 -0
  25. package/dist/pageVisit/pageVisitEventError/pageVisitEventError.js +313 -0
  26. package/dist/pageVisit/pageVisitEventHTTP/pageVisitEventHTTP.js +115 -0
  27. package/dist/pageVisit/userStep/userStep.js +20 -0
  28. package/dist/react/ErrorBoundary.d.ts +72 -0
  29. package/dist/react/ErrorBoundary.js +102 -0
  30. package/dist/storage/localStorageProvider.js +23 -0
  31. package/dist/storage/rnStorageProvider.js +62 -0
  32. package/dist/storage/sessionStorageProvider.js +23 -0
  33. package/dist/storage/storage.js +119 -0
  34. package/dist/storage/storageProvider.js +83 -0
  35. package/dist/utils/date.js +62 -0
  36. package/dist/utils/eventlistener.js +67 -0
  37. package/dist/utils/function.js +398 -0
  38. package/dist/utils/object.js +144 -0
  39. package/dist/utils/performance.js +21 -0
  40. package/package.json +57 -0
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # Noibu React-Native public SDK
2
+
3
+ Noibu's React Native SDK allows customers to easily track checkout errors in their Android and iOS apps written in React Native.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [Configuration](#configuration)
10
+ - [API Reference](#api-reference)
11
+ - [Contributing](#contributing)
12
+ - [License](#license)
13
+
14
+ ## Installation
15
+
16
+ ### Requirements
17
+
18
+ For alpha version there are no strong limitations, except for:
19
+
20
+ - react-native version should be >=0.63.0
21
+
22
+ ### Installing the SDK
23
+
24
+ Install using favourite node package manager (npm, yarn, etc.):
25
+
26
+ ```bash
27
+ npm install noibu-react-native --save
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ Wrap your root App component into SDK ErrorBoundary:
33
+
34
+ ```jsx
35
+ import React from 'react';
36
+ import { View, Text } from 'react-native';
37
+ import { ErrorBoundary } from 'noibu-react-native';
38
+
39
+ export default function App() {
40
+ return (
41
+ <ErrorBoundary
42
+ fallback={() => (
43
+ <View>
44
+ <Text>Oh no!</Text>
45
+ </View>
46
+ )}
47
+ >
48
+ <View>
49
+ <Text>Hello world!</Text>
50
+ </View>
51
+ </ErrorBoundary>
52
+ );
53
+ }
54
+ ```
55
+
56
+ That's it! First time the module is imported, it runs an init and starts listening to errors.
57
+
58
+ ## Configuration
59
+
60
+ ErrorBoundary component has a few useful properties described here https://help.noibu.com/hc/en-us/articles/9562254753677-Noibu-React-SDK under the section _ErrorBoundary Class -> Props_.
61
+
62
+ ## API Reference
63
+
64
+ Apart from exporting ErrorBoundary component, noibu-react-native module has NoibuJS object export with useful methods.
65
+
66
+ ### `NoibuJS`
67
+
68
+ #### `requestHelpCode(alert?: boolean): Promise<string>`
69
+
70
+ Requests a help code from the HelpCode instance. To read more about help codes, refer to the page: https://help.noibu.com/hc/en-us/articles/14051818012813-How-to-Find-a-Session-with-Help-Code
71
+
72
+ - `@returns {Promise<string>}` - A promise that resolves with the requested help code.
73
+
74
+ ```jsx
75
+ import { NoibuJS } from 'noibu-react-native';
76
+ import { useCallback, useState } from 'react';
77
+ import { Alert, Text, Pressable, View} from 'react-native';
78
+
79
+ const AlertHelpCode = () => {
80
+ const triggerHelpCodeAlert = useCallback(async () => {
81
+ const response = await NoibuJS.requestHelpCode();
82
+ if (response) {
83
+ Alert.alert('Help Code delivered:', response);
84
+ }
85
+ }, []);
86
+ return (
87
+ <View>
88
+ <Pressable onPress={triggerHelpCodeAlert}>
89
+ <View>
90
+ <Text>Tap to view Help Code</Text>
91
+ </View>
92
+ </Pressable>
93
+ </View>
94
+ );
95
+ };
96
+ ```
97
+
98
+ #### `addCustomAttribute(name: string, value: string) => string`
99
+
100
+ Adds a custom attribute to the session.
101
+
102
+ - `@param {string} name` - Name of a custom attribute.
103
+ - `@param {any} value` - It's value, should be a JSON.stringify-able type.
104
+ - `@returns {string}` - A success message, or validation failure cause.
105
+
106
+ ```js
107
+ import { NoibuJS } from 'noibu-react-native';
108
+
109
+ NoibuJS.addCustomAttribute('myNameIs', 'Jeff'); // SUCCESS
110
+ NoibuJS.addCustomAttribute('veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong', 'Jeff'); // NAME_TOO_LONG
111
+ ```
112
+
113
+ #### `addError(customError: Error) => string`
114
+
115
+ Adds a custom Error to the session.
116
+
117
+ - `@param {{ message: string, stack: string }} customError` - an Error-like object to be reported with the session.
118
+ - `@returns {string}` - A success message, or validation failure cause.
119
+
120
+ ```js
121
+ import { NoibuJS } from 'noibu-react-native';
122
+
123
+ NoibuJS.addError(new Error('My Error'));
124
+ ```
125
+
126
+ #### `addJsSdkError(customError: Error, errorSource: string) => string`
127
+
128
+ Adds an error from a JS SDK to the session, this method is used by ErrorBoundary internally. Similar to addError(), but additionally allows to set a cause.
129
+
130
+ - `@param {{ message: string, stack: string }} error` - an Error-like object to be reported with the session.
131
+ - `@param {string} errorSource` - source of an error.
132
+ - `@returns {string}` - A success message, or validation failure cause.
133
+
134
+ ```js
135
+ import { NoibuJS } from 'noibu-react-native';
136
+
137
+ NoibuJS.addJsSdkError(new Error('My Error'), 'myModule.js');
138
+ ```
139
+
140
+ ## Publishing
141
+
142
+ Ci has a job to publish a new version of the SDK, but has to be manually triggered.
143
+ Docker image used for publishing is noibujssdkci:0.2
144
+
145
+ ## Contributing
146
+
147
+ You can contribute by checking out the project page and open issues. https://linear.app/noibu/project/react-native-sdk-5ccd19a3343a
148
+
149
+ ## License
150
+
151
+ Copyright 2023 Noibu.com
152
+
153
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
154
+
155
+ THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,416 @@
1
+ import uuid from 'react-native-uuid';
2
+ import { MAX_METROPLEX_SOCKET_INNACTIVE_TIME, DISABLED_STATUS_KEY, CLIENT_UNLOCK_TIME_KEY, SEVERITY_WARN, LAST_ACTIVE_TIME_KEY, CURRENT_PAGE_VISIT_COUNT_KEY, NOIBU_BROWSER_ID_KYWRD, BROWSER_ID_KEY, PV_SEQ_NUM_RESET_TIME_MINUTES, SEVERITY_ERROR, JS_ENV, PAGE_VISIT_ID_KEY, MAX_PAGEVISIT_VISITED, CLIENT_LOCK_TIME_MINUTES, GET_SCRIPT_ID, MAX_COLLECT_ERROR_LOG } from '../constants.js';
3
+ import { stringifyJSON, getUserAgent, asString, makeRequest } from '../utils/function.js';
4
+ import Storage from '../storage/storage.js';
5
+
6
+ /** @module ClientConfig */
7
+
8
+ /**
9
+ * Singleton class to manage the client configuration
10
+ * this class will be responsible for controlling the disabled
11
+ * status of the client script as well as managing all storage
12
+ * storing and retrieval.
13
+ */
14
+ class ClientConfig {
15
+ /**
16
+ * Creates a ClientConfig singleton instance
17
+ * @param {} noibuErrorURL
18
+ */
19
+ constructor(noibuErrorURL) {
20
+ // sets up this.browserId, this.disabledStatus
21
+ this.pageVisitId = uuid.v4();
22
+ // variables stored in storage
23
+ this.isClientDisabled = null;
24
+ this.browserId = null;
25
+ this.pageVisitSeq = null;
26
+ // This variable tracks the last time the user was active in this session.
27
+ // It is also written to storage. Initialized to now so the session can be
28
+ // timed out even if a PV is never sent.
29
+ this.lastActiveTime = new Date();
30
+ // error URL to send Noibu errors to
31
+ this.noibuErrorURL = noibuErrorURL;
32
+ // sets up this.browserId, this.isClientDisabled, this.pageVisitSeq
33
+ this._setupStorageVars();
34
+ // error sent to backend counter
35
+ this.cltErrorPostCounter = 0;
36
+ // variables for checking if the socket is inactive
37
+ // used a class variables in order to be changed in testing
38
+ this.maxSocketInactiveTime = MAX_METROPLEX_SOCKET_INNACTIVE_TIME;
39
+ }
40
+
41
+ /** Configures the singleton instance */
42
+ static configureInstance(noibuErrorURL) {
43
+ if (!this.instance) {
44
+ // Set this.noibuErrorURL preemptively in case ClientConfig isn't able to be
45
+ // configured properly and throws an error.
46
+ // This will ensure we get the expected error POST request at the correct URL.
47
+ this.noibuErrorURL = noibuErrorURL;
48
+ this.instance = new ClientConfig(noibuErrorURL);
49
+ this.instance.noibuErrorURL = noibuErrorURL;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * gets the singleton instance
55
+ * @returns {ClientConfig}
56
+ */
57
+ static getInstance() {
58
+ if (!this.instance) {
59
+ throw new Error('ClientConfig was not configured');
60
+ }
61
+
62
+ return this.instance;
63
+ }
64
+
65
+ /** lockClient will disable the client script for a single pagevisit for
66
+ * duration given in minuntes */
67
+ lockClient(duration, msg) {
68
+ const expiryTime = new Date();
69
+ expiryTime.setMinutes(expiryTime.getMinutes() + duration);
70
+
71
+ const noibuLSObject = this._getClientState();
72
+ noibuLSObject[DISABLED_STATUS_KEY] = true;
73
+ noibuLSObject[CLIENT_UNLOCK_TIME_KEY] = expiryTime;
74
+ this._storeBrowserData(noibuLSObject);
75
+ this.postNoibuErrorAndOptionallyDisableClient(msg, true, SEVERITY_WARN);
76
+ }
77
+
78
+ /** Locks the client until the next page loads */
79
+ lockClientUntilNextPage(msg) {
80
+ this.postNoibuErrorAndOptionallyDisableClient(msg, true, SEVERITY_WARN);
81
+ }
82
+
83
+ /** Updates the config object to store the given last active time */
84
+ updateLastActiveTime(lastActiveTime) {
85
+ this.lastActiveTime = lastActiveTime;
86
+ const newConfigData = this._getLsObject();
87
+ newConfigData[LAST_ACTIVE_TIME_KEY] = lastActiveTime;
88
+ this._storeBrowserData(newConfigData);
89
+ }
90
+
91
+ /** Gets the current page visit sequence number that should be used */
92
+ getPageVisitSeq() {
93
+ if (this._pageVisitSeqNeedsReset()) {
94
+ // Reset the page visit sequence number to zero and store the next seq number in storage
95
+ this.pageVisitSeq = 0;
96
+ const newConfigData = this._getLsObject();
97
+ newConfigData[CURRENT_PAGE_VISIT_COUNT_KEY] = this.pageVisitSeq + 1;
98
+ // Update the last active time since we are actively requesting the seq
99
+ newConfigData[LAST_ACTIVE_TIME_KEY] = new Date();
100
+ this._storeBrowserData(newConfigData);
101
+ }
102
+
103
+ return this.pageVisitSeq;
104
+ }
105
+
106
+ /**
107
+ * Returns the client config object from storage or generates a new one
108
+ * What is stored in storage will look like this
109
+ * {
110
+ * BrowserId: UUIDV4
111
+ * ExpiryTime: DATE OBJ
112
+ * DisabledStatus: BOOL
113
+ * CurrentPageVisitCount: INT
114
+ * ClientUnlockTime: DATE OBJ
115
+ * LastActiveTime: DATE OBJ
116
+ * }
117
+ */
118
+ _getLsObject() {
119
+ const storage = Storage.getInstance();
120
+ const storedConfig = storage.load(NOIBU_BROWSER_ID_KYWRD);
121
+
122
+ // first time browsing since noibu was installed
123
+ if (!storedConfig) {
124
+ return this._generateAndStoreData();
125
+ }
126
+
127
+ let parsedConfig = {};
128
+
129
+ try {
130
+ parsedConfig = JSON.parse(storedConfig);
131
+ } catch (e) {
132
+ return this._generateAndStoreData();
133
+ }
134
+
135
+ // checking if it's a valid object. The CLIENT_UNLOCK_TIME_KEY doesn't have to exist
136
+ // since it's not written to the storage object when set to null.
137
+ if (
138
+ parsedConfig[BROWSER_ID_KEY] == null ||
139
+ parsedConfig[DISABLED_STATUS_KEY] == null ||
140
+ parsedConfig[CURRENT_PAGE_VISIT_COUNT_KEY] == null ||
141
+ parsedConfig[LAST_ACTIVE_TIME_KEY] == null
142
+ ) {
143
+ return this._generateAndStoreData();
144
+ }
145
+ return parsedConfig;
146
+ }
147
+
148
+ /**
149
+ * Check if we have surpased the last active time and the page visit seq number needs resetting
150
+ */
151
+ _pageVisitSeqNeedsReset() {
152
+ const noibuLSObject = this._getClientState();
153
+ const someTimeAgo = new Date();
154
+ someTimeAgo.setMinutes(
155
+ someTimeAgo.getMinutes() - PV_SEQ_NUM_RESET_TIME_MINUTES,
156
+ );
157
+ return new Date(noibuLSObject[LAST_ACTIVE_TIME_KEY]) < someTimeAgo;
158
+ }
159
+
160
+ /**
161
+ * _setupStorageVars will set all class variables that depend
162
+ * on the storage's value.
163
+ */
164
+ _setupStorageVars() {
165
+ const storage = Storage.getInstance();
166
+ if (!storage.isAvailable()) {
167
+ this.postNoibuErrorAndOptionallyDisableClient(
168
+ `Storage is unavailable, disabling client. ${storage.getDiagnoseInfo()}`,
169
+ true,
170
+ SEVERITY_ERROR,
171
+ );
172
+ return;
173
+ }
174
+
175
+ // getting the current content of the storage
176
+ const noibuLSObject = this._getClientState();
177
+
178
+ // Check if we have surpased the last active time and reset the sequence number if so
179
+ if (this._pageVisitSeqNeedsReset()) {
180
+ noibuLSObject[CURRENT_PAGE_VISIT_COUNT_KEY] = 0;
181
+ }
182
+
183
+ this.browserId = noibuLSObject[BROWSER_ID_KEY];
184
+ this.pageVisitSeq = noibuLSObject[CURRENT_PAGE_VISIT_COUNT_KEY];
185
+ this.isClientDisabled = noibuLSObject[DISABLED_STATUS_KEY];
186
+
187
+ // If the client has been disabled just return.
188
+ // Calling _getClientState() above performs the disabled expirey check
189
+ if (this.isClientDisabled) return;
190
+
191
+ // Update the LS object values before storing it for the next page visit
192
+ noibuLSObject[CURRENT_PAGE_VISIT_COUNT_KEY] += 1;
193
+ noibuLSObject[LAST_ACTIVE_TIME_KEY] = new Date();
194
+
195
+ // Expose page visit ID in storage for use by Trailbreaker video tests
196
+ // This will be done for the testvideo and lambdavideo bundles used by Trailbreaker
197
+ if (JS_ENV().includes('video')) {
198
+ noibuLSObject[PAGE_VISIT_ID_KEY] = this.pageVisitId;
199
+ }
200
+
201
+ // if we have reached the max page visits for a browser id then
202
+ // we disabled collect for 45 minutes
203
+ if (noibuLSObject[CURRENT_PAGE_VISIT_COUNT_KEY] >= MAX_PAGEVISIT_VISITED) {
204
+ // if we bust the max pagevisit visited limit we lock the client for 45 minutes
205
+ // since its probably a bot.
206
+ const expiryTime = new Date();
207
+ expiryTime.setMinutes(expiryTime.getMinutes() + CLIENT_LOCK_TIME_MINUTES);
208
+ // setting the lock time
209
+ noibuLSObject[CLIENT_UNLOCK_TIME_KEY] = expiryTime;
210
+ noibuLSObject[DISABLED_STATUS_KEY] = true;
211
+ this.postNoibuErrorAndOptionallyDisableClient(
212
+ `Hit max page visits, disabling client for ${CLIENT_LOCK_TIME_MINUTES}mins`,
213
+ true,
214
+ SEVERITY_ERROR,
215
+ );
216
+ }
217
+
218
+ // we now check if we successfully saved the data
219
+ const savedData = this._storeBrowserData(noibuLSObject);
220
+
221
+ // if the browser is null, we cannot access the storage or an
222
+ // error happened, thus we disable collect.
223
+ if (savedData[BROWSER_ID_KEY] === null) {
224
+ // we do not set a lock expiry date here since we cannot store to storage
225
+ this.postNoibuErrorAndOptionallyDisableClient(
226
+ `Null browser in storage, disabling client`,
227
+ true,
228
+ SEVERITY_ERROR,
229
+ );
230
+ this.browserId = null;
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Function will get the Noibu Storage Object
236
+ * 1. Generate a brand new one
237
+ * Get it from storage if the expiry date is not in the past
238
+ * Generate a brand new one if the expiry date is in the past
239
+ */
240
+ _getClientState() {
241
+ const newConfigData = this._getLsObject();
242
+
243
+ // if the lock expired, we remove the lock period and enable the client
244
+ if (
245
+ newConfigData[CLIENT_UNLOCK_TIME_KEY] &&
246
+ new Date(newConfigData[CLIENT_UNLOCK_TIME_KEY]) <= new Date()
247
+ ) {
248
+ newConfigData[CLIENT_UNLOCK_TIME_KEY] = null;
249
+ newConfigData[DISABLED_STATUS_KEY] = false;
250
+ this._storeBrowserData(newConfigData);
251
+ }
252
+ // return the stored browserId
253
+ return newConfigData;
254
+ }
255
+
256
+ /**
257
+ * _generateAndStoreData generates brand new data and then proceeds to store
258
+ * it.
259
+ */
260
+ _generateAndStoreData() {
261
+ const data = this._storeBrowserData(this._generateNewBrowserData());
262
+ return data;
263
+ }
264
+
265
+ /**
266
+ * _generateNewBrowserData will create new data to be stored in storage
267
+ * and persisted throughout a session
268
+ */
269
+ _generateNewBrowserData() {
270
+ const noibuBrowserData = {
271
+ [DISABLED_STATUS_KEY]: false,
272
+ [BROWSER_ID_KEY]: uuid.v4(),
273
+ [CURRENT_PAGE_VISIT_COUNT_KEY]: 0,
274
+ [CLIENT_UNLOCK_TIME_KEY]: null,
275
+ [LAST_ACTIVE_TIME_KEY]: new Date(),
276
+ };
277
+
278
+ // Expose page visit ID in storage for use by Trailbreaker video tests
279
+ // This will be done for the testvideo and lambdavideo bundles used by Trailbreaker
280
+ if (JS_ENV().includes('video')) {
281
+ noibuBrowserData[PAGE_VISIT_ID_KEY] = this.pageVisitId;
282
+ }
283
+
284
+ return noibuBrowserData;
285
+ }
286
+
287
+ /**
288
+ * _storeBrowserData will store the passed object in storage.
289
+ * @param {} data the data to be stored
290
+ */
291
+ _storeBrowserData(data) {
292
+ const storage = Storage.getInstance();
293
+ try {
294
+ storage.save(NOIBU_BROWSER_ID_KYWRD, stringifyJSON(data));
295
+ return data;
296
+ } catch (e) {
297
+ this.postNoibuErrorAndOptionallyDisableClient(
298
+ `Error writing browser data to storage, disabling client: ${e.message}, ` +
299
+ `${storage.getDiagnoseInfo()}`,
300
+ true,
301
+ SEVERITY_ERROR,
302
+ );
303
+ // sending empty fields if we encountered errors while storing in the LS
304
+ const invalidData = {
305
+ [DISABLED_STATUS_KEY]: true,
306
+ [BROWSER_ID_KEY]: null,
307
+ [CURRENT_PAGE_VISIT_COUNT_KEY]: 0,
308
+ };
309
+ return invalidData;
310
+ }
311
+ }
312
+
313
+ /**
314
+ * postNoibuErrorAndOptionallyDisableClient will post errors that were thrown by collect
315
+ * and disable the client if required
316
+ * severity expects one of the SEVERITY_x level constants, or else error will be used
317
+ * @param {} error
318
+ * @param {} disableClient
319
+ * @param {} severity
320
+ * @param {} keepAlive=false
321
+ */
322
+ async postNoibuErrorAndOptionallyDisableClient(
323
+ error,
324
+ disableClient,
325
+ severity,
326
+ keepAlive = false,
327
+ ) {
328
+ if (this.isClientDisabled) {
329
+ return;
330
+ }
331
+ if (disableClient) {
332
+ this.isClientDisabled = true;
333
+ }
334
+ if (severity === SEVERITY_WARN) {
335
+ // don't log warning messages by default, as a cost savings
336
+ return;
337
+ }
338
+
339
+ let errMsg = `Noibu Browser ID(${
340
+ this.browserId ? this.browserId : ''
341
+ }), PV ID ${
342
+ this.pageVisitId
343
+ }, Script ID ${GET_SCRIPT_ID()}, and User Agent ${await getUserAgent()} error: ${asString(
344
+ error,
345
+ )}`;
346
+
347
+ // if the page visits sends more errors than the
348
+ // allowed threshold we disable the client
349
+ if (this.cltErrorPostCounter >= MAX_COLLECT_ERROR_LOG) {
350
+ // we disable the client for 10 minute if we sent too many logs
351
+ // we manually lock the client to not cause an infinite loop
352
+ const expiryTime = new Date();
353
+ expiryTime.setMinutes(expiryTime.getMinutes() + 10); // 10 minutes lock
354
+
355
+ const noibuLSObject = this._getClientState();
356
+ noibuLSObject[DISABLED_STATUS_KEY] = true;
357
+ noibuLSObject[CLIENT_UNLOCK_TIME_KEY] = expiryTime;
358
+ this._storeBrowserData(noibuLSObject);
359
+ this.isClientDisabled = true;
360
+ // end of lock
361
+ // overriding the message to be an alert that we are shutting collect off.
362
+ errMsg =
363
+ 'Shutting collect off, we reached the ' +
364
+ 'maximum limit of collect errors sent.';
365
+ }
366
+
367
+ const errorContent = {
368
+ url: (global.location && global.location.href) || '',
369
+ err_msg: errMsg,
370
+ sev: severity,
371
+ };
372
+ const headers = {
373
+ 'content-type': 'application/json',
374
+ };
375
+
376
+ if (!keepAlive) {
377
+ makeRequest(
378
+ 'POST',
379
+ this.noibuErrorURL,
380
+ errorContent,
381
+ headers,
382
+ 2000,
383
+ false,
384
+ ).catch(() => {
385
+ // we do nothing and let this error silently fail
386
+ });
387
+ } else {
388
+ fetch(this.noibuErrorURL, {
389
+ method: 'POST',
390
+ headers,
391
+ body: stringifyJSON(errorContent),
392
+ // keep alive outlives the current page, its the same as beacon
393
+ keepalive: true,
394
+ });
395
+ }
396
+
397
+ // only increment if this was an actual error, not a warning or otherwise
398
+ if (severity === SEVERITY_ERROR) {
399
+ this.cltErrorPostCounter += 1;
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Returns true if the page visit is considered to be inactive
405
+ */
406
+ isInactive() {
407
+ const someTimeAgo = new Date();
408
+ someTimeAgo.setSeconds(
409
+ someTimeAgo.getSeconds() - this.maxSocketInactiveTime,
410
+ );
411
+
412
+ return this.lastActiveTime < someTimeAgo;
413
+ }
414
+ }
415
+
416
+ export { ClientConfig as default };
@@ -0,0 +1,106 @@
1
+ import MetroplexSocket from './metroplexSocket.js';
2
+ import { SEVERITY_ERROR, WORK_REQUEST_ATT_NAME, HELP_CODE_ATT_NAME } from '../constants.js';
3
+ import ClientConfig from './clientConfig.js';
4
+
5
+ /**
6
+ * HelpCode class is responsible for help code feature related functionality
7
+ */
8
+ class HelpCode {
9
+ /**
10
+ * Singleton instance
11
+ * @returns {HelpCode}
12
+ */
13
+ static getInstance() {
14
+ if (!this.instance) {
15
+ this.instance = new HelpCode();
16
+ }
17
+
18
+ return this.instance;
19
+ }
20
+
21
+ /**
22
+ * Constructs instance and sets up event listeners
23
+ */
24
+ constructor() {
25
+ this.requestContext = null;
26
+ }
27
+
28
+ /**
29
+ * Requests a help code and returns a Promise that resolves when the help code is obtained
30
+ * or rejects if the noibu connection is unavailable.
31
+
32
+ * @returns {Promise<string>} Promise object representing the help code request.
33
+ * @throws {string} Throws an error if the noibu connection is unavailable.
34
+ */
35
+ requestHelpCode() {
36
+ if (this.requestContext != null) {
37
+ return this.requestContext.promise;
38
+ }
39
+
40
+ const context = {
41
+ resolve: null,
42
+ reject: null,
43
+ promise: null,
44
+ };
45
+
46
+ context.promise = new Promise((resolve, reject) => {
47
+ context.resolve = resolve;
48
+ context.reject = reject;
49
+ });
50
+
51
+ this.requestContext = context;
52
+
53
+ const result = this._sendRequest();
54
+
55
+ if (result === false) {
56
+ this.requestContext = null;
57
+ return Promise.reject(new Error('noibu connection is unavailable'));
58
+ }
59
+
60
+ return this.requestContext.promise;
61
+ }
62
+
63
+ /**
64
+ * Handles the received help code event.
65
+ * @param {CustomEvent<string>} event - The event object with string detail property.
66
+ * @returns {void}
67
+ */
68
+ receiveHelpCode(event) {
69
+ if (this.requestContext === null) {
70
+ const { success, data } = event.detail;
71
+
72
+ if (!success) {
73
+ const message = `Noibu help code is not available due to ${data}`;
74
+
75
+ ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
76
+ message,
77
+ false,
78
+ SEVERITY_ERROR,
79
+ );
80
+ }
81
+ return;
82
+ }
83
+
84
+ const context = this.requestContext;
85
+ this.requestContext = null;
86
+
87
+ const { success, data } = event.detail;
88
+
89
+ if (success) {
90
+ context.resolve(data);
91
+ } else {
92
+ context.reject(new Error(data));
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Sends metroplex request
98
+ */
99
+ _sendRequest() {
100
+ return MetroplexSocket.getInstance().sendMessage(WORK_REQUEST_ATT_NAME, {
101
+ [WORK_REQUEST_ATT_NAME]: HELP_CODE_ATT_NAME,
102
+ });
103
+ }
104
+ }
105
+
106
+ export { HelpCode as default };