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.
Files changed (66) hide show
  1. package/dist/api/helpCode.js +61 -87
  2. package/dist/api/metroplexSocket.js +72 -65
  3. package/dist/api/storedPageVisit.js +150 -208
  4. package/dist/constants.js +3 -7
  5. package/dist/entry/init.js +11 -11
  6. package/dist/monitors/{appNavigationMonitor.js → AppNavigationMonitor.js} +10 -19
  7. package/dist/monitors/ClickMonitor.js +198 -0
  8. package/dist/monitors/ErrorMonitor.js +206 -0
  9. package/dist/monitors/KeyboardInputMonitor.js +60 -0
  10. package/dist/monitors/PageMonitor.js +98 -0
  11. package/dist/monitors/RequestMonitor.js +390 -0
  12. package/dist/monitors/http-tools/GqlErrorValidator.js +259 -0
  13. package/dist/monitors/{httpDataBundler.js → http-tools/HTTPDataBundler.js} +23 -102
  14. package/dist/pageVisit/{eventDebouncer.js → EventDebouncer.js} +36 -47
  15. package/dist/pageVisit/pageVisitEventError.js +3 -3
  16. package/dist/pageVisit/pageVisitEventHTTP.js +5 -4
  17. package/dist/sessionRecorder/sessionRecorder.js +1 -1
  18. package/dist/src/api/clientConfig.d.ts +1 -1
  19. package/dist/src/api/helpCode.d.ts +10 -16
  20. package/dist/src/api/metroplexSocket.d.ts +52 -71
  21. package/dist/src/api/storedPageVisit.d.ts +12 -21
  22. package/dist/src/constants.d.ts +1 -0
  23. package/dist/src/monitors/AppNavigationMonitor.d.ts +18 -0
  24. package/dist/src/monitors/ClickMonitor.d.ts +31 -0
  25. package/dist/src/monitors/ErrorMonitor.d.ts +63 -0
  26. package/dist/src/monitors/{keyboardInputMonitor.d.ts → KeyboardInputMonitor.d.ts} +7 -4
  27. package/dist/src/monitors/{pageMonitor.d.ts → PageMonitor.d.ts} +6 -8
  28. package/dist/src/monitors/RequestMonitor.d.ts +94 -0
  29. package/dist/src/monitors/http-tools/GqlErrorValidator.d.ts +59 -0
  30. package/dist/src/monitors/{httpDataBundler.d.ts → http-tools/HTTPDataBundler.d.ts} +13 -28
  31. package/dist/src/monitors/integrations/react-native-navigation-integration.d.ts +3 -2
  32. package/dist/src/pageVisit/{eventDebouncer.d.ts → EventDebouncer.d.ts} +3 -10
  33. package/dist/src/pageVisit/pageVisit.d.ts +1 -1
  34. package/dist/src/pageVisit/pageVisitEventHTTP.d.ts +3 -3
  35. package/dist/src/storage/rnStorageProvider.d.ts +1 -1
  36. package/dist/src/storage/storage.d.ts +1 -1
  37. package/dist/src/storage/storageProvider.d.ts +2 -2
  38. package/dist/src/utils/function.d.ts +4 -5
  39. package/dist/src/utils/object.d.ts +3 -5
  40. package/dist/src/utils/polyfills.d.ts +1 -4
  41. package/dist/types/Metroplex.types.d.ts +73 -0
  42. package/dist/types/Monitor.d.ts +11 -0
  43. package/dist/types/Monitor.js +19 -0
  44. package/dist/types/PageVisit.types.d.ts +2 -145
  45. package/dist/types/PageVisitErrors.types.d.ts +114 -0
  46. package/dist/types/PageVisitEvents.types.d.ts +91 -0
  47. package/dist/types/Storage.d.ts +1 -1
  48. package/dist/types/StoredPageVisit.types.d.ts +4 -45
  49. package/dist/utils/function.js +0 -1
  50. package/dist/utils/object.js +1 -0
  51. package/package.json +4 -3
  52. package/dist/monitors/clickMonitor.js +0 -258
  53. package/dist/monitors/errorMonitor.js +0 -202
  54. package/dist/monitors/gqlErrorValidator.js +0 -306
  55. package/dist/monitors/inputMonitor.js +0 -138
  56. package/dist/monitors/keyboardInputMonitor.js +0 -66
  57. package/dist/monitors/pageMonitor.js +0 -122
  58. package/dist/monitors/requestMonitor.js +0 -386
  59. package/dist/src/monitors/appNavigationMonitor.d.ts +0 -22
  60. package/dist/src/monitors/clickMonitor.d.ts +0 -44
  61. package/dist/src/monitors/errorMonitor.d.ts +0 -28
  62. package/dist/src/monitors/gqlErrorValidator.d.ts +0 -82
  63. package/dist/src/monitors/inputMonitor.d.ts +0 -34
  64. package/dist/src/monitors/requestMonitor.d.ts +0 -10
  65. package/dist/types/RRWeb.d.ts +0 -48
  66. package/dist/types/ReactNative.d.ts +0 -4
@@ -1,306 +0,0 @@
1
- import { CONTENT_TYPE, SEVERITY } from '../constants.js';
2
- import { isInstanceOf, getMaxSubstringAllowed } from '../utils/function.js';
3
- import ClientConfig from '../api/clientConfig.js';
4
-
5
- const MESSAGE_ATT_NAME = 'message';
6
- const EXTENSIONS_ATT_NAME = 'extensions';
7
- const LOCATIONS_ATT_NAME = 'locations';
8
- const PATH_ATT_NAME = 'path';
9
- const LINE_ATT_NAME = 'line';
10
- const COLUMN_ATT_NAME = 'column';
11
- const MESSAGE_MAX_LENGTH = 1000;
12
-
13
- /* eslint-disable no-restricted-syntax */
14
- /* eslint-disable no-param-reassign */
15
-
16
- /**
17
- * Try detecting GraphQL errors from http response
18
- */
19
- class GqlErrorValidator {
20
- /**
21
- * Retrieves GQL error object based on fetch request/response
22
- * @param {String} url
23
- * @param {{}} [options]
24
- * @param {Request} [request]
25
- * @param {Response} [response] - cloned() from original response
26
- */
27
- static async fromFetch(url, options, request, response) {
28
- try {
29
- const isResponseValid = isInstanceOf(response, Response) && response.ok;
30
- if (!isResponseValid) {
31
- return null;
32
- }
33
- const contentType = this._getContentTypeFromFetchArguments(
34
- options,
35
- request,
36
- );
37
- if (this._shouldHandleRequest(url, contentType)) {
38
- const data = await response.json();
39
- return this._validate(data, []);
40
- }
41
- } catch (e) {
42
- // can't read the response if request is aborted
43
- // so just ignore it
44
- if (!this._isRequestAborted(options, request)) {
45
- this._postError(e);
46
- }
47
- }
48
-
49
- return null;
50
- }
51
-
52
- /**
53
- * Retrieves GQL error object based on XHR object
54
- * @param {String} url
55
- * @param {XMLHttpRequest} xhr
56
- */
57
- static async fromXhr(url, xhr) {
58
- try {
59
- const isResponseValid =
60
- isInstanceOf(xhr, XMLHttpRequest) &&
61
- xhr.status >= 200 &&
62
- xhr.status <= 299;
63
- if (!isResponseValid) {
64
- return null;
65
- }
66
- let contentType = null;
67
- if (xhr.noibuRequestHeaders) {
68
- contentType = xhr.noibuRequestHeaders.get(CONTENT_TYPE);
69
- }
70
- if (this._shouldHandleRequest(url, contentType)) {
71
- let data = null;
72
- if (xhr.responseType === 'blob') {
73
- if (xhr.response.text) {
74
- const content = await xhr.response.text();
75
- data = this._parseJsonSafely(content);
76
- }
77
- } else if (xhr.responseType === 'json') {
78
- data = xhr.response;
79
- } else {
80
- const content = xhr.responseText;
81
- data = this._parseJsonSafely(content);
82
- }
83
- if (data) {
84
- return this._validate(data, []);
85
- }
86
- }
87
- } catch (e) {
88
- this._postError(e);
89
- }
90
-
91
- return null;
92
- }
93
-
94
- /**
95
- * Try safely parse a string and return null if fails
96
- * @param {String} content
97
- */
98
- static _parseJsonSafely(content) {
99
- try {
100
- return JSON.parse(content);
101
- } catch (e) {
102
- return null;
103
- }
104
- }
105
-
106
- /**
107
- * Try to get content type for fetch arguments
108
- * @param {{}} options
109
- * @param {Request} [request]
110
- */
111
- static _getContentTypeFromFetchArguments(options, request) {
112
- let headers = null;
113
- if (isInstanceOf(request, Request)) {
114
- headers = request.headers;
115
- } else if (options && options.headers) {
116
- headers = new Headers(options.headers);
117
- }
118
- let contentType = null;
119
- if (headers) {
120
- contentType = headers.get(CONTENT_TYPE);
121
- }
122
- return contentType;
123
- }
124
-
125
- /**
126
- * Checks if request is aborted
127
- * If it has been aborder we are not able to consume the response
128
- * @param {{}} options
129
- * @param {Request} request
130
- */
131
- static _isRequestAborted(options, request) {
132
- if (isInstanceOf(request, Request)) {
133
- return request.signal && request.signal.aborted;
134
- }
135
- if (options && isInstanceOf(options.signal, AbortSignal)) {
136
- return options.signal.aborted;
137
- }
138
- return false;
139
- }
140
-
141
- /**
142
- * Determines if request should be processed
143
- * @param {String|URL} url
144
- * @param {String} contentType
145
- */
146
- static _shouldHandleRequest(url, contentType) {
147
- if (contentType) {
148
- contentType = contentType.toLowerCase();
149
- }
150
- let isGqlUrl = false;
151
- if (url) {
152
- if (isInstanceOf(url, URL)) {
153
- url = url.toString();
154
- }
155
- isGqlUrl = url.toLowerCase().includes('graphql');
156
- }
157
- return (
158
- (contentType === 'application/json' && isGqlUrl) ||
159
- contentType === 'application/graphql'
160
- );
161
- }
162
-
163
- /**
164
- * Sanitizes payload object
165
- * @param {any} data
166
- * @param {Array<String>} validationIssues
167
- */
168
- static _validate(data, validationIssues) {
169
- let errors = null;
170
-
171
- if (data && Array.isArray(data.errors)) {
172
- errors = data.errors;
173
-
174
- for (const error of errors) {
175
- const properties = Object.keys(error);
176
- for (const property of properties) {
177
- switch (property) {
178
- case MESSAGE_ATT_NAME:
179
- this._validateMessage(error);
180
- break;
181
- case LOCATIONS_ATT_NAME:
182
- this._validateLocations(error, validationIssues);
183
- break;
184
- case PATH_ATT_NAME:
185
- this._validatePath(error, validationIssues);
186
- break;
187
- case EXTENSIONS_ATT_NAME:
188
- this._validateExtensions(error);
189
- break;
190
- default:
191
- delete error[property];
192
- validationIssues.push(`unexpected error.${property}`);
193
- break;
194
- }
195
- }
196
- }
197
-
198
- if (validationIssues.length > 0) {
199
- this._postValidationIssues(validationIssues);
200
- }
201
- }
202
-
203
- return errors;
204
- }
205
-
206
- /**
207
- * Sanitizes message object
208
- * @param {any} error
209
- */
210
- static _validateMessage(error) {
211
- error[MESSAGE_ATT_NAME] = getMaxSubstringAllowed(
212
- error[MESSAGE_ATT_NAME],
213
- MESSAGE_MAX_LENGTH,
214
- );
215
- }
216
-
217
- /**
218
- * Sanitizes extensions object
219
- * @param {any} error
220
- * @param {Array<String>} validationIssues
221
- */
222
- static _validateExtensions(error) {
223
- const json = JSON.stringify(error[EXTENSIONS_ATT_NAME]);
224
- error[EXTENSIONS_ATT_NAME] = getMaxSubstringAllowed(
225
- json,
226
- MESSAGE_MAX_LENGTH,
227
- );
228
- }
229
-
230
- /**
231
- * Sanitizes locations object
232
- * @param {any} error
233
- * @param {Array<String>} validationIssues
234
- */
235
- static _validateLocations(error, validationIssues) {
236
- const locations = error[LOCATIONS_ATT_NAME];
237
- if (Array.isArray(locations)) {
238
- for (const location of locations) {
239
- const properties = Object.keys(location);
240
- for (const property of properties) {
241
- switch (property) {
242
- case LINE_ATT_NAME:
243
- case COLUMN_ATT_NAME:
244
- if (!Number.isSafeInteger(location[property])) {
245
- const value = location[property];
246
- location[property] = 0;
247
- validationIssues.push(
248
- `unexpected ${property} value '${value}'`,
249
- );
250
- }
251
- break;
252
- default:
253
- delete location[property];
254
- validationIssues.push(`unexpected error.location.${property}`);
255
- break;
256
- }
257
- }
258
- }
259
- } else {
260
- delete error[LOCATIONS_ATT_NAME];
261
- validationIssues.push(`unexpected error.locations`);
262
- }
263
- }
264
-
265
- /**
266
- * Sanitizes path object
267
- * @param {any} error
268
- * @param {Array<String>} validationIssues
269
- */
270
- static _validatePath(error, validationIssues) {
271
- const path = error[PATH_ATT_NAME];
272
- if (Array.isArray(path)) {
273
- error[PATH_ATT_NAME] = error[PATH_ATT_NAME].map(x => x.toString());
274
- } else {
275
- delete error[PATH_ATT_NAME];
276
- validationIssues.push(`unexpected error.path`);
277
- }
278
- }
279
-
280
- /**
281
- * Posts error
282
- * @param {String} message
283
- */
284
- static _postError(message) {
285
- ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
286
- `GQL parse error: ${message}`,
287
- false,
288
- SEVERITY.error,
289
- );
290
- }
291
-
292
- /**
293
- * Posts issue found during object sanitization
294
- * @param {Array<String>} validationIssues
295
- */
296
- static _postValidationIssues(validationIssues) {
297
- const message = validationIssues.join(',');
298
- ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
299
- `GQL error validation warning: ${message}`,
300
- false,
301
- SEVERITY.error,
302
- );
303
- }
304
- }
305
-
306
- export { GqlErrorValidator as default };
@@ -1,138 +0,0 @@
1
- import { PageVisit } from '../pageVisit/pageVisit.js';
2
- import { APP_NAVIGATION_EVENT_TYPE, PAGE_EVENT_TYPE, MAX_TIME_FOR_UNSENT_DATA_MILLIS, ERROR_EVENT_TYPE, HTTP_EVENT_TYPE, KEYBOARD_EVENT_TYPE, USERSTEP_EVENT_TYPE } from '../constants.js';
3
- import { timestampWrapper } from '../utils/date.js';
4
- import { addSafeEventListener } from '../utils/eventlistener.js';
5
- import { noibuLog } from '../utils/log.js';
6
-
7
- /** @module InputMonitor */
8
-
9
- /**
10
- * Singleton class to manage the client configuration
11
- * this class will be responsible for debouncing all events
12
- * that are registered
13
- */
14
- class InputMonitor {
15
- /**
16
- * Creates an instance of InputMonitor
17
- */
18
- constructor() {
19
- // events are stored in queues that are debounced seperatly
20
- // each object in this map is
21
- // type: {
22
- // timeout: timeout,
23
- // events: [...],
24
- // debouncePeriod: period at which we debounce events,
25
- // eventName: name of the event, could be different than type
26
- // (clicks and keyboards are usersteps)
27
- // }
28
- this.eventsToDebounce = {};
29
-
30
- // setting up debouncers for all events
31
- // we send clicks directly to the socket so they aren't registered here
32
- // we dont wait to send location changes, they happen at most once per second.
33
- this.registerInputType(APP_NAVIGATION_EVENT_TYPE, 0);
34
- this.registerInputType(PAGE_EVENT_TYPE, MAX_TIME_FOR_UNSENT_DATA_MILLIS);
35
- this.registerInputType(ERROR_EVENT_TYPE, MAX_TIME_FOR_UNSENT_DATA_MILLIS);
36
- this.registerInputType(HTTP_EVENT_TYPE, MAX_TIME_FOR_UNSENT_DATA_MILLIS);
37
- this.registerInputType(
38
- KEYBOARD_EVENT_TYPE,
39
- MAX_TIME_FOR_UNSENT_DATA_MILLIS,
40
- USERSTEP_EVENT_TYPE,
41
- );
42
-
43
- this._setupUnloadHandler();
44
- }
45
-
46
- /**
47
- * gets the instance of InputMonitor
48
- * @returns {InputMonitor}
49
- */
50
- static getInstance() {
51
- if (!this.instance) {
52
- this.instance = new InputMonitor();
53
- }
54
-
55
- return this.instance;
56
- }
57
-
58
- /** will debounce all events that are of this type by the debounce period
59
- * @param {} type
60
- * @param {} debouncePeriod
61
- * @param {} eventName=type
62
- */
63
- registerInputType(type, debouncePeriod, eventName = type) {
64
- if (type in this.eventsToDebounce) {
65
- return;
66
- }
67
-
68
- // registering this event type as a debouncable
69
- this.eventsToDebounce[type] = {
70
- timeout: null,
71
- events: [],
72
- debouncePeriod,
73
- eventName,
74
- };
75
- }
76
-
77
- /**
78
- * Creates an event object with the event and the time it was added then pushes
79
- * that event object to the queue of events waiting to be debounced.
80
- * @param {} event
81
- * @param {} type
82
- */
83
- addEvent(event, type) {
84
- noibuLog('addEvent', {
85
- event,
86
- type,
87
- });
88
- if (!(type in this.eventsToDebounce)) {
89
- throw new Error(`Type: ${type} is not in eventsToDebounce`);
90
- }
91
-
92
- this.eventsToDebounce[type].events.push({
93
- event,
94
- occurredAt: new Date(timestampWrapper(Date.now())).toISOString(),
95
- });
96
-
97
- this._debouncePvEvents(type);
98
- }
99
-
100
- /**
101
- * Adds the events from the object to the page visit and sets up a timer
102
- * to send the events if no more are received without the timeout
103
- * @param {} type
104
- */
105
- _debouncePvEvents(type) {
106
- /**
107
- * Debounce function to be executed once the debounce period is completed
108
- */
109
- const later = () => {
110
- this.eventsToDebounce[type].timeout = null;
111
- PageVisit.getInstance().addPageVisitEvents(
112
- this.eventsToDebounce[type].events,
113
- this.eventsToDebounce[type].eventName,
114
- );
115
- this.eventsToDebounce[type].events = [];
116
- };
117
-
118
- clearTimeout(this.eventsToDebounce[type].timeout);
119
- this.eventsToDebounce[type].timeout = setTimeout(
120
- later,
121
- this.eventsToDebounce[type].debouncePeriod,
122
- );
123
- }
124
-
125
- /** Sets up the page hide handler to try to push remaining events in the queues */
126
- _setupUnloadHandler() {
127
- addSafeEventListener(window, 'pagehide', () => {
128
- Object.values(this.eventsToDebounce).forEach(eventObject => {
129
- PageVisit.getInstance().addPageVisitEvents(
130
- eventObject.events,
131
- eventObject.eventName,
132
- );
133
- });
134
- });
135
- }
136
- }
137
-
138
- export { InputMonitor };
@@ -1,66 +0,0 @@
1
- import { TextInput } from 'react-native';
2
- import { updatePayload } from '../pageVisit/userStep.js';
3
- import { InputMonitor } from './inputMonitor.js';
4
- import { SOURCE_ATT_NAME, TEXT_ATT_NAME, TAGNAME_ATT_NAME, TYPE_ATT_NAME, KEYBOARD_EVENT_TYPE } from '../constants.js';
5
-
6
- /** @module KeyboardInputMonitor */
7
-
8
- /**
9
- * KeyboardInputMonitor is a listener class that attaches a
10
- * keyboard input listener of the document object.
11
- */
12
- class KeyboardInputMonitor {
13
- /**
14
- * Begins the monitoring process
15
- * we currently only monitor two input locations: textarea and input
16
- */
17
- monitor() {
18
- const handler = this._handle.bind(this);
19
-
20
- if (!TextInput.originalRender) {
21
- TextInput.originalRender = TextInput.render;
22
- TextInput.render = function (props, ...args) {
23
- const onChange = event => {
24
- const currentText = event.nativeEvent.text;
25
- handler(event.target.viewConfig.uiViewClassName, props);
26
- if (props.onChange) props.onChange(event);
27
- if (props.onChangeText) props.onChangeText(currentText);
28
- };
29
- const newProps = {
30
- ...props,
31
- onChange,
32
- };
33
- return TextInput.originalRender(newProps, ...args);
34
- };
35
- }
36
- }
37
-
38
- /**
39
- * Validates an event
40
- * @param uiViewClassName
41
- * @param props
42
- */
43
- _handle(uiViewClassName, props) {
44
- if (!/(text|input)/i.test(uiViewClassName)) {
45
- return;
46
- }
47
-
48
- const name = props.placeholder || props.testID;
49
-
50
- if (!name) {
51
- return;
52
- }
53
-
54
- InputMonitor.getInstance().addEvent(
55
- updatePayload({
56
- [SOURCE_ATT_NAME]: '',
57
- [TEXT_ATT_NAME]: name,
58
- [TAGNAME_ATT_NAME]: uiViewClassName.toLowerCase(),
59
- [TYPE_ATT_NAME]: KEYBOARD_EVENT_TYPE,
60
- }),
61
- KEYBOARD_EVENT_TYPE,
62
- );
63
- }
64
- }
65
-
66
- export { KeyboardInputMonitor };
@@ -1,122 +0,0 @@
1
- import { PAGE_EVENTS_WINDOW, PAGE_EVENTS_DOCUMENT, PAGE_EVENT_TYPE } from '../constants.js';
2
- import { InputMonitor } from './inputMonitor.js';
3
- import { addSafeEventListener } from '../utils/eventlistener.js';
4
-
5
- /** @module PageMonitor */
6
-
7
- /** Monitors the page events which we capture and later process */
8
- class PageMonitor {
9
- /**
10
- * gets the singleton instance
11
- * @returns {PageMonitor}
12
- */
13
- static getInstance() {
14
- if (!this.instance) {
15
- this.instance = new PageMonitor();
16
- }
17
-
18
- return this.instance;
19
- }
20
-
21
- /** Starts monitoring page events on the document */
22
- monitor() {
23
- PAGE_EVENTS_WINDOW.forEach(pageEvent => {
24
- addSafeEventListener(
25
- window,
26
- pageEvent,
27
- this._onPageEventHandle.bind(this),
28
- true,
29
- );
30
- });
31
- if (global.document) {
32
- PAGE_EVENTS_DOCUMENT.forEach(pageEvent => {
33
- addSafeEventListener(
34
- document,
35
- pageEvent,
36
- this._onPageEventHandle.bind(this),
37
- true,
38
- );
39
- });
40
- }
41
- }
42
-
43
- /**
44
- * Handles a single page event
45
- * @param {} event
46
- */
47
- _onPageEventHandle(event) {
48
- if (!event || !event.type) {
49
- return;
50
- }
51
-
52
- const payload = {
53
- type: event.type,
54
- };
55
-
56
- // Add data if applicable for the event type
57
- switch (event.type) {
58
- case 'visibilitychange':
59
- payload.data = `state: ${this.getDocumentState()}`;
60
- break;
61
- case 'readystatechange':
62
- payload.data = `state: ${document.readyState}`;
63
- break;
64
- case 'pagehide':
65
- case 'pageshow':
66
- case 'load':
67
- if (event.persisted) {
68
- payload.data = `persisted: ${event.persisted}`;
69
- }
70
- break;
71
- case 'storage':
72
- if (event.key) {
73
- payload.data = `key: ${event.key}`;
74
- }
75
- break;
76
- case 'message':
77
- case 'messageerror':
78
- if (event.data && event.origin) {
79
- payload.data = `origin: ${event.origin} size: ${this.getSizeInBytes(
80
- event.data,
81
- )}`;
82
- }
83
- break;
84
- case 'hashchange':
85
- if (event.newURL) {
86
- payload.data = `newURL: ${event.newURL}`;
87
- }
88
- break;
89
- // do nothing
90
- }
91
-
92
- // storing the page event in the page visit queue
93
- InputMonitor.getInstance().addEvent(payload, PAGE_EVENT_TYPE);
94
- }
95
-
96
- /** Returns document state */
97
- getDocumentState() {
98
- if (document.visibilityState === 'hidden') {
99
- return 'hidden';
100
- }
101
- if (document.hasFocus()) {
102
- return 'active';
103
- }
104
- return 'passive';
105
- }
106
-
107
- /**
108
- * Returns object size in bytes
109
- * @param {} obj
110
- */
111
- getSizeInBytes(obj) {
112
- let str = obj;
113
- if (typeof obj !== 'string') {
114
- // Else, make obj into a string
115
- str = JSON.stringify(obj);
116
- }
117
- // 2B per character in the string
118
- return str.length * 2;
119
- }
120
- }
121
-
122
- export { PageMonitor };