noibu-react-native 0.0.3 → 0.0.6

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.
@@ -1,39 +1,31 @@
1
1
  import DeviceInfo from 'react-native-device-info';
2
2
  import { getLocales } from 'react-native-localize';
3
3
  import { parseStack } from './stacktrace-parser.js';
4
- import { MAX_BEACON_PAYLOAD_SIZE, MAX_STRING_LENGTH, REQUIRED_DATA_PROCESSING_URLS, HTTP_DATA_COLLECTION_FLAG_NAME, HTTP_DATA_PAYLOAD_URL_REGEXES_FLAG_NAME, WIN_BLOCKED_SELECTOR_ATT_NAME, PII_EMAIL_PATTERN, PII_REDACTION_REPLACEMENT_STRING, PII_DIGIT_PATTERN, JS_STACK_FRAMES_ATT_NAME, JS_STACK_MESSAGE_ATT_NAME, JS_STACK_FILE_ATT_NAME, JS_STACK_LINE_ATT_NAME, JS_STACK_COL_ATT_NAME, MAX_FRAMES_IN_ARRAY, NOIBUJS_CONFIG, JS_STACK_METHOD_ATT_NAME, DEFAULT_STACK_FRAME_FIELD_VALUE } from '../constants.js';
4
+ import { MAX_BEACON_PAYLOAD_SIZE, MAX_STRING_LENGTH, REQUIRED_DATA_PROCESSING_URLS, PII_EMAIL_PATTERN, PII_REDACTION_REPLACEMENT_STRING, PII_DIGIT_PATTERN, DEFAULT_STACK_FRAME_FIELD_VALUE } from '../constants.js';
5
5
  import ClientConfig from '../api/clientConfig.js';
6
6
 
7
7
  /** @module Functions */
8
-
9
8
  /**
10
9
  * Returns a stack trace frame with default filed values
11
10
  */
12
11
  const getDefaultFrame = () => ({
13
- [JS_STACK_LINE_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
14
- [JS_STACK_METHOD_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
15
- [JS_STACK_FILE_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
12
+ line: DEFAULT_STACK_FRAME_FIELD_VALUE,
13
+ mname: DEFAULT_STACK_FRAME_FIELD_VALUE,
14
+ file: DEFAULT_STACK_FRAME_FIELD_VALUE,
16
15
  });
17
-
18
16
  /**
19
- *
20
17
  * returns a string that satisfies a max length
21
18
  * stringToVerify: string that needs to be verified
22
19
  * length :optional, max length that stringToVerify can be
23
- * @param stringToVerify
24
- * @param length
25
20
  */
26
- function getMaxSubstringAllowed(
27
- stringToVerify,
28
- length = MAX_STRING_LENGTH,
29
- ) {
30
- if (!stringToVerify) {
31
- return stringToVerify;
32
- }
33
- if (stringToVerify.length < length) return stringToVerify;
34
- return stringToVerify.substring(0, length);
21
+ function getMaxSubstringAllowed(stringToVerify, length = MAX_STRING_LENGTH) {
22
+ if (!stringToVerify) {
23
+ return stringToVerify;
24
+ }
25
+ if (stringToVerify.length < length)
26
+ return stringToVerify;
27
+ return stringToVerify.substring(0, length);
35
28
  }
36
-
37
29
  /**
38
30
  *
39
31
  * todo implement navigation
@@ -41,138 +33,98 @@ function getMaxSubstringAllowed(
41
33
  * @returns {string}
42
34
  */
43
35
  function getProperGlobalUrl() {
44
- const globalUrl = new URL('https://localhost');
45
- globalUrl.hostname = ClientConfig.getInstance().customerDomain;
46
- return getMaxSubstringAllowed(globalUrl.toString());
36
+ const globalUrl = new URL('https://localhost');
37
+ globalUrl.hostname = ClientConfig.getInstance().customerDomain;
38
+ return getMaxSubstringAllowed(globalUrl.toString());
47
39
  }
48
-
49
40
  /**
50
41
  * Processes the raw stack frames and creates a readable stack in a safe manner
51
42
  * @param {StackFrame[]} rawFrames
52
43
  */
53
44
  function processFrames(rawFrames) {
54
- return rawFrames.map(frame => {
55
- const processedFrame = getDefaultFrame();
56
-
57
- if (frame[JS_STACK_LINE_ATT_NAME]) {
58
- if (Number.isInteger(frame[JS_STACK_LINE_ATT_NAME])) {
59
- processedFrame[JS_STACK_LINE_ATT_NAME] = String(
60
- frame[JS_STACK_LINE_ATT_NAME],
61
- );
62
- }
63
- }
64
-
65
- if (
66
- frame[JS_STACK_METHOD_ATT_NAME] &&
67
- frame[JS_STACK_METHOD_ATT_NAME] !== '<unknown>'
68
- ) {
69
- processedFrame[JS_STACK_METHOD_ATT_NAME] = String(
70
- frame[JS_STACK_METHOD_ATT_NAME],
71
- );
72
- }
73
-
74
- if (
75
- frame[JS_STACK_FILE_ATT_NAME] &&
76
- frame[JS_STACK_FILE_ATT_NAME] !== '<unknown>'
77
- ) {
78
- processedFrame[JS_STACK_FILE_ATT_NAME] = String(
79
- frame[JS_STACK_FILE_ATT_NAME],
80
- );
81
- }
82
-
83
- if (frame[JS_STACK_COL_ATT_NAME]) {
84
- if (Number.isInteger(frame[JS_STACK_COL_ATT_NAME])) {
85
- processedFrame[JS_STACK_COL_ATT_NAME] = frame[JS_STACK_COL_ATT_NAME];
86
- }
87
- }
88
-
89
- return processedFrame;
90
- });
45
+ return rawFrames.map(frame => {
46
+ const processedFrame = getDefaultFrame();
47
+ if (frame.line) {
48
+ if (Number.isInteger(frame.line)) {
49
+ processedFrame.line = String(frame.line);
50
+ }
51
+ }
52
+ if (frame.mname && frame.mname !== '<unknown>') {
53
+ processedFrame.mname = String(frame.mname);
54
+ }
55
+ if (frame.file && frame.file !== '<unknown>') {
56
+ processedFrame.file = String(frame.file);
57
+ }
58
+ if (frame.column) {
59
+ if (Number.isInteger(frame.column)) {
60
+ processedFrame.column = frame.column;
61
+ }
62
+ }
63
+ return processedFrame;
64
+ });
91
65
  }
92
-
93
66
  /**
94
67
  * Retrieves the javascript stack and message from an error event object
95
68
  * @param errObj error to extract stack from
96
69
  */
97
70
  function getJSStack(errObj) {
98
- let frames = [
99
- {
100
- [JS_STACK_LINE_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
101
- [JS_STACK_METHOD_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
102
- [JS_STACK_FILE_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
103
- },
104
- ];
105
-
106
- // if the errObj type is not an object or null
107
- // return a default frame
108
- if (typeof errObj !== 'object' || !errObj) {
109
- return {
110
- [JS_STACK_FRAMES_ATT_NAME]: frames,
111
- [JS_STACK_MESSAGE_ATT_NAME]: '',
112
- };
113
- }
114
-
115
- // If there is no stack field present then try to read the single file/line/column values
116
- if (!errObj.stack) {
117
- if (errObj.fileName && typeof errObj.fileName === 'string') {
118
- frames[0][JS_STACK_FILE_ATT_NAME] = errObj.fileName;
71
+ let frames = [
72
+ {
73
+ line: DEFAULT_STACK_FRAME_FIELD_VALUE,
74
+ mname: DEFAULT_STACK_FRAME_FIELD_VALUE,
75
+ file: DEFAULT_STACK_FRAME_FIELD_VALUE,
76
+ },
77
+ ];
78
+ // if the errObj type is not an object or null
79
+ // return a default frame
80
+ if (typeof errObj !== 'object' || !errObj) {
81
+ return {
82
+ frames,
83
+ msg: '',
84
+ };
119
85
  }
120
- if (errObj.lineNumber) {
121
- frames[0][JS_STACK_LINE_ATT_NAME] = String(errObj.lineNumber);
122
- }
123
- if (errObj.columnNumber && Number.isInteger(errObj.columnNumber)) {
124
- frames[0][JS_STACK_COL_ATT_NAME] = errObj.columnNumber;
125
- }
126
- } else {
127
86
  frames = processFrames(parseStack(errObj.stack));
128
- }
129
-
130
- if (frames.length >= MAX_FRAMES_IN_ARRAY) {
131
- frames = frames.slice(0, MAX_FRAMES_IN_ARRAY);
132
- }
133
-
134
- const msg = errObj.message ? getMaxSubstringAllowed(errObj.message) : '';
135
- return {
136
- [JS_STACK_FRAMES_ATT_NAME]: frames,
137
- [JS_STACK_MESSAGE_ATT_NAME]: msg,
138
- };
87
+ const msg = errObj.message ? getMaxSubstringAllowed(errObj.message) : '';
88
+ return {
89
+ frames,
90
+ msg,
91
+ };
139
92
  }
140
-
141
93
  /**
142
- * @param {string} possiblyStacktrace
143
- * @returns {boolean}
94
+ * Checks if possiblyStacktrace has any stack frames present
144
95
  */
145
96
  function isStackTrace(possiblyStacktrace) {
146
- try {
147
- return parseStack(possiblyStacktrace).length > 0;
148
- } catch (e) {
149
- return false;
150
- }
97
+ try {
98
+ // todo disable temporary
99
+ // return parseStack(possiblyStacktrace).length > 0;
100
+ return false;
101
+ }
102
+ catch (e) {
103
+ return false;
104
+ }
151
105
  }
152
-
153
106
  /**
154
- * safely stringifies an object
155
- * @param jsonObject
107
+ * Stringify function that is aware of inner circular references and handles them by replacing with {} (empty object)
108
+ * taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value
156
109
  */
157
110
  function stringifyJSON(jsonObject) {
158
- // prototype.js version < 1.7 defines a toJSON on the prototype of
159
- // arrays. This is wrong and they have fixed it since, but some old websites
160
- // still use that lib and it messes up our the expected serilization on metroplex's
161
- // side.
162
- if (Array.prototype.toJSON) {
163
- // this piece of code is safe since js guarentees that only one thing is running
164
- // at once, therefore the prototype will be changed only while this function is executing
165
- const oldToJSON = Array.prototype.toJSON;
166
- delete Array.prototype.toJSON;
167
- const jsonString = JSON.stringify(jsonObject);
168
- // we want to re-enable the existing prototype behaviour
169
- // eslint-disable-next-line no-extend-native
170
- Array.prototype.toJSON = oldToJSON;
171
- return jsonString;
172
- }
173
- return JSON.stringify(jsonObject);
111
+ const ancestors = [];
112
+ return JSON.stringify(jsonObject, function (k, v) {
113
+ if (typeof v !== 'object' || v === null) {
114
+ return v;
115
+ }
116
+ // `this` is the object that value is contained in,
117
+ // i.e., its direct parent.
118
+ while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) {
119
+ ancestors.pop();
120
+ }
121
+ if (ancestors.includes(v)) {
122
+ return {};
123
+ }
124
+ ancestors.push(v);
125
+ return v;
126
+ });
174
127
  }
175
-
176
128
  /**
177
129
  * Wrapper for a request, since we have to do some special handling
178
130
  * @param method
@@ -182,244 +134,188 @@ function stringifyJSON(jsonObject) {
182
134
  * @param timeout
183
135
  * @param sendAndForget
184
136
  */
185
- async function makeRequest(
186
- method,
187
- url,
188
- data,
189
- headers,
190
- timeout,
191
- sendAndForget,
192
- ) {
193
- const ua = Object.keys(headers).findLast(
194
- k => k.toLowerCase() === 'user-agent',
195
- );
196
- const headersWithUa = { ...headers };
197
- if (!headers[ua]) {
198
- headersWithUa['User-Agent'] = await getUserAgent();
199
- }
200
-
201
- // a send-and-forget request is made by using the beacon API (fetch + keepalive)
202
- if (sendAndForget) {
203
- const stringData = stringifyJSON(data);
204
- const currentPayloadSize = new Blob([stringData]).size;
205
- // if we have a large object or fetch is not available, we skip sending the message
206
- if (!global.fetch || currentPayloadSize > MAX_BEACON_PAYLOAD_SIZE) {
207
- return new Promise(resolve => {
208
- resolve();
209
- });
137
+ async function makeRequest(method, url, data, headers, timeout, sendAndForget) {
138
+ const ua = Object.keys(headers).findLast(k => k.toLowerCase() === 'user-agent');
139
+ const headersWithUa = { ...headers };
140
+ if (!headers[ua]) {
141
+ headersWithUa['User-Agent'] = await getUserAgent();
210
142
  }
211
-
212
- return fetch(url, {
213
- method: 'POST',
214
- headers: headersWithUa,
215
- body: stringifyJSON(data),
216
- // keep alive outlives the current page, its the same as beacon
217
- keepalive: true,
218
- });
219
- }
220
-
221
- return new Promise((resolve, reject) => {
222
- const xhr = new global.XMLHttpRequest();
223
- xhr.open(method, url);
224
- xhr.timeout = timeout;
225
- Object.keys(headersWithUa).forEach(header => {
226
- xhr.setRequestHeader(header, headers[header]);
227
- });
228
-
229
- xhr.onload = () => {
230
- if (xhr.status >= 200 && xhr.status < 300) {
231
- resolve(xhr.response);
232
- } else {
233
- reject(new Error(`Custom Request failed: ${xhr.statusText}`));
234
- }
235
- };
236
- xhr.onerror = () => {
237
- reject(new Error(`Custom Request failed: ${xhr.statusText}`));
238
- };
239
- if (data) {
240
- xhr.send(stringifyJSON(data));
241
- } else {
242
- xhr.send();
143
+ // a send-and-forget request is made by using the beacon API (fetch + keepalive)
144
+ if (sendAndForget) {
145
+ const stringData = stringifyJSON(data);
146
+ const currentPayloadSize = new Blob([stringData]).size;
147
+ // if we have a large object or fetch is not available, we skip sending the message
148
+ if (currentPayloadSize > MAX_BEACON_PAYLOAD_SIZE) {
149
+ return Promise.resolve();
150
+ }
151
+ return fetch(url, {
152
+ method: 'POST',
153
+ headers: headersWithUa,
154
+ body: stringifyJSON(data),
155
+ // keep alive outlives the current page, its the same as beacon
156
+ keepalive: true,
157
+ });
243
158
  }
244
- });
245
- }
246
-
247
- /** determines if we have received the block selector */
248
- function checkBlockedSelectorExistence() {
249
- const noibuConfig = NOIBUJS_CONFIG();
250
- return (
251
- noibuConfig[WIN_BLOCKED_SELECTOR_ATT_NAME] &&
252
- Array.isArray(noibuConfig[WIN_BLOCKED_SELECTOR_ATT_NAME])
253
- );
159
+ return new Promise((resolve, reject) => {
160
+ const xhr = new XMLHttpRequest();
161
+ xhr.open(method, url);
162
+ xhr.timeout = timeout;
163
+ Object.keys(headersWithUa).forEach(header => {
164
+ xhr.setRequestHeader(header, headers[header]);
165
+ });
166
+ xhr.onload = () => {
167
+ if (xhr.status >= 200 && xhr.status < 300) {
168
+ resolve(xhr.response);
169
+ }
170
+ else {
171
+ reject(new Error(`Custom Request failed: ${xhr.statusText}`));
172
+ }
173
+ };
174
+ xhr.onerror = () => {
175
+ reject(new Error(`Custom Request failed: ${xhr.statusText}`));
176
+ };
177
+ if (data) {
178
+ xhr.send(stringifyJSON(data));
179
+ }
180
+ else {
181
+ xhr.send();
182
+ }
183
+ });
254
184
  }
255
-
256
185
  /** checks if http data collection is enabled */
257
186
  function checkHttpDataCollectionEnabled() {
258
- const noibuConfig = NOIBUJS_CONFIG();
259
- // Just a boolean, so safe to return truthiness. If undefined, will return false.
260
- return !!noibuConfig[HTTP_DATA_COLLECTION_FLAG_NAME];
261
- }
262
-
263
- /** determines if we have received the http allowed urls */
264
- function checkHttpPayloadAllowedURLsExistence() {
265
- const noibuConfig = NOIBUJS_CONFIG();
266
- return (
267
- noibuConfig[HTTP_DATA_PAYLOAD_URL_REGEXES_FLAG_NAME] &&
268
- Array.isArray(noibuConfig[HTTP_DATA_PAYLOAD_URL_REGEXES_FLAG_NAME])
269
- );
187
+ const noibuConfig = ClientConfig.getInstance();
188
+ // Just a boolean, so safe to return truthiness. If undefined, will return false.
189
+ return !!noibuConfig.enableHttpDataCollection;
270
190
  }
271
-
272
191
  /** gets http data payload allowed URLs */
273
192
  function getHttpPayloadAllowedURLs() {
274
- const noibuConfig = NOIBUJS_CONFIG();
275
- // return the allowed list or an empty list
276
- if (checkHttpPayloadAllowedURLsExistence()) {
277
- return noibuConfig[HTTP_DATA_PAYLOAD_URL_REGEXES_FLAG_NAME];
278
- }
279
- return [];
193
+ const noibuConfig = ClientConfig.getInstance();
194
+ // return the allowed list or an empty list
195
+ if (noibuConfig.listOfUrlsToCollectHttpDataFrom &&
196
+ Array.isArray(noibuConfig.listOfUrlsToCollectHttpDataFrom)) {
197
+ return noibuConfig.listOfUrlsToCollectHttpDataFrom;
198
+ }
199
+ return [];
280
200
  }
281
201
  /**
282
- * Gets all the blocked css classes to prevent those elements from being recorded
202
+ * Gets selectors to prevent those elements from being recorded
283
203
  */
284
- function getBlockedCSSForCurrentDomain() {
285
- const noibuConfig = NOIBUJS_CONFIG();
286
- const blockedCSS = ['noibu-blocked'];
287
- if (checkBlockedSelectorExistence()) {
288
- noibuConfig[WIN_BLOCKED_SELECTOR_ATT_NAME].forEach(sel => {
289
- // this means that we are looking at a class
290
- if (sel.startsWith('.') && sel.length > 0) {
291
- // selecting everything after and including the element at index 1
292
- blockedCSS.push(sel.substring(1));
293
- }
294
- });
295
- }
296
- return blockedCSS;
204
+ function getBlockedElements() {
205
+ const selectors = ClientConfig.getInstance().blockedElements;
206
+ const blockedElements = ['noibu-blocked'];
207
+ if (selectors && Array.isArray(selectors)) {
208
+ blockedElements.push(...selectors);
209
+ }
210
+ return blockedElements;
297
211
  }
298
212
  /**
299
213
  * makes sure the url sent is a valid URL
300
- * @param {} string url to be validated
301
214
  */
302
- function isValidURL(string) {
303
- try {
304
- const _ = new URL(string);
305
- return true;
306
- } catch (_) {
307
- return false;
308
- }
215
+ function isValidURL(str) {
216
+ try {
217
+ return !!new URL(str);
218
+ }
219
+ catch (_) {
220
+ return false;
221
+ }
309
222
  }
310
-
311
223
  let userAgentCache = '';
312
-
313
224
  /**
314
225
  * Because of the nature of user agent in react native, we have to make this async.
315
226
  * But I promise, this is really fast, since we memoize the result for the whole session :)
316
227
  * @returns {Promise<string>}
317
228
  */
318
229
  async function getUserAgent() {
319
- if (userAgentCache) {
230
+ if (userAgentCache) {
231
+ return userAgentCache;
232
+ }
233
+ userAgentCache = await DeviceInfo.getUserAgent();
320
234
  return userAgentCache;
321
- }
322
- userAgentCache = await DeviceInfo.getUserAgent();
323
- return userAgentCache;
324
235
  }
325
-
326
236
  /**
327
237
  * isInvalidURLConfig will verify that Collect is being initializes with
328
238
  * the correct env vars.
329
- * @param {Noibu.UrlConfig} urlConfig
330
239
  */
331
- function isInvalidURLConfig(urlConfig) {
332
- for (let i = 0; i < REQUIRED_DATA_PROCESSING_URLS.length; i += 1) {
333
- const u = REQUIRED_DATA_PROCESSING_URLS[i];
334
- if (!urlConfig[u]) {
335
- return true;
240
+ function isInvalidURLConfig(urls) {
241
+ for (let i = 0; i < REQUIRED_DATA_PROCESSING_URLS.length; i += 1) {
242
+ const u = REQUIRED_DATA_PROCESSING_URLS[i];
243
+ if (!urls[u]) {
244
+ return true;
245
+ }
336
246
  }
337
- }
338
-
339
- return false;
247
+ return false;
340
248
  }
341
-
342
249
  /**
343
250
  * isNoibuJSAlreadyLoaded will verify if there are already other
344
251
  * copies of NoibuJS runnung
345
252
  */
346
253
  function isNoibuJSAlreadyLoaded() {
347
- // check if the global variable exists and its value
348
- const loaded = window.noibuJSLoaded !== undefined;
349
- // set the variable so future copies of the script
350
- // will know this instance is running
351
- window.noibuJSLoaded = true;
352
- return loaded;
254
+ // check if the global variable exists and its value
255
+ const loaded = window.noibuJSLoaded !== undefined;
256
+ // set the variable so future copies of the script
257
+ // will know this instance is running
258
+ window.noibuJSLoaded = true;
259
+ return loaded;
353
260
  }
354
-
355
- /** asString will create a string out of anything passed to it.
356
- * @param {} obj
261
+ /**
262
+ * asString will create a string out of anything passed to it.
357
263
  */
358
264
  function asString(obj) {
359
- if (!obj) {
360
- return '';
361
- }
362
- // we've seen the url be an object in some cases
363
- // we would still like to send those to our backend in the case
364
- // that this is an issue and needs to be queried
365
- if (typeof obj === 'object') {
366
- return stringifyJSON(obj);
367
- }
368
-
369
- return String(obj);
265
+ if (!obj) {
266
+ return '';
267
+ }
268
+ // we've seen the url be an object in some cases
269
+ // we would still like to send those to our backend in the case
270
+ // that this is an issue and needs to be queried
271
+ if (typeof obj === 'object') {
272
+ return stringifyJSON(obj);
273
+ }
274
+ return String(obj);
370
275
  }
371
-
372
- /** masks textual content if it ressembles something sensitive
373
- * @param {} text
276
+ /**
277
+ * masks textual content if it ressembles something sensitive
374
278
  */
375
279
  function maskTextInput(text) {
376
- // if it has an email or digit(s), we mask the text
377
- return text
378
- .replace(PII_EMAIL_PATTERN, PII_REDACTION_REPLACEMENT_STRING)
379
- .replace(PII_DIGIT_PATTERN, '*');
280
+ // if it has an email or digit(s), we mask the text
281
+ return text
282
+ .replace(PII_EMAIL_PATTERN, PII_REDACTION_REPLACEMENT_STRING)
283
+ .replace(PII_DIGIT_PATTERN, '*');
380
284
  }
381
-
382
- /** gets the onURL of a string, defaulting to the location of the webpage
383
- * @param {} realOnURL
285
+ /**
286
+ * gets the onURL of a string, defaulting to the location of the webpage
384
287
  */
385
288
  function getOnURL(realOnURL) {
386
- let onURL = getProperGlobalUrl();
387
-
388
- if (realOnURL && realOnURL.trim() !== '' && realOnURL !== 'undefined') {
389
- onURL = asString(getMaxSubstringAllowed(realOnURL));
390
- }
391
-
392
- return onURL;
289
+ let onURL = getProperGlobalUrl();
290
+ if (realOnURL && realOnURL.trim() !== '' && realOnURL !== 'undefined') {
291
+ onURL = asString(getMaxSubstringAllowed(realOnURL));
292
+ }
293
+ return onURL;
393
294
  }
394
-
395
295
  /** gets the user language from the browser */
396
296
  function getUserLanguage() {
397
- const locales = getLocales();
398
- if (!locales.length) {
399
- return null;
400
- }
401
- const lang = locales[0].languageCode;
402
-
403
- if (lang === '' || !lang) {
404
- return null;
405
- }
406
-
407
- return lang.toLowerCase();
297
+ const locales = getLocales();
298
+ if (!locales.length) {
299
+ return null;
300
+ }
301
+ const lang = locales[0].languageCode;
302
+ if (lang === '' || !lang) {
303
+ return null;
304
+ }
305
+ return lang.toLowerCase();
408
306
  }
409
-
410
307
  /**
411
308
  * Checks if the provided object is an instance of the specified type.
412
309
  * It's safer than `instanceof` operator as it handles cases
413
310
  * where type is not actually a type but an object.
414
- * @param {any} instance
415
- * @param {any} type
416
311
  */
417
312
  function isInstanceOf(instance, type) {
418
- try {
419
- return typeof type === 'function' && instance instanceof type;
420
- } catch (e) {
421
- return false;
422
- }
313
+ try {
314
+ return typeof type === 'function' && instance instanceof type;
315
+ }
316
+ catch (e) {
317
+ return false;
318
+ }
423
319
  }
424
320
 
425
- export { asString, checkHttpDataCollectionEnabled, getBlockedCSSForCurrentDomain, getHttpPayloadAllowedURLs, getJSStack, getMaxSubstringAllowed, getOnURL, getProperGlobalUrl, getUserAgent, getUserLanguage, isInstanceOf, isInvalidURLConfig, isNoibuJSAlreadyLoaded, isStackTrace, isValidURL, makeRequest, maskTextInput, processFrames, stringifyJSON };
321
+ export { asString, checkHttpDataCollectionEnabled, getBlockedElements, getHttpPayloadAllowedURLs, getJSStack, getMaxSubstringAllowed, getOnURL, getProperGlobalUrl, getUserAgent, getUserLanguage, isInstanceOf, isInvalidURLConfig, isNoibuJSAlreadyLoaded, isStackTrace, isValidURL, makeRequest, maskTextInput, processFrames, stringifyJSON };
@@ -1,9 +1,7 @@
1
- import { JS_STACK_COL_ATT_NAME, JS_STACK_FILE_ATT_NAME, JS_STACK_LINE_ATT_NAME, JS_STACK_METHOD_ATT_NAME } from '../constants';
2
- declare interface StackFrame {
3
- [JS_STACK_FILE_ATT_NAME]: string;
4
- [JS_STACK_LINE_ATT_NAME]?: number;
5
- [JS_STACK_COL_ATT_NAME]?: number;
6
- [JS_STACK_METHOD_ATT_NAME]: string;
1
+ export declare interface RawStackFrame {
2
+ file: string;
3
+ line?: number;
4
+ column?: number;
5
+ mname: string;
7
6
  }
8
- export declare function parseStack(stackString: string): StackFrame[];
9
- export {};
7
+ export declare function parseStack(stackString: string): RawStackFrame[];